summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authortb <>2021-08-30 17:27:45 +0000
committertb <>2021-08-30 17:27:45 +0000
commitff0ebfdd90c1f0edf8cda1269349b659fd4250c8 (patch)
treebcb1cf15ae4740482148cb5a6bc7e7499e46387c /src
parent16d988bc2e4a08ee42706c3f39c9fd70bfa71cd4 (diff)
downloadopenbsd-ff0ebfdd90c1f0edf8cda1269349b659fd4250c8.tar.gz
openbsd-ff0ebfdd90c1f0edf8cda1269349b659fd4250c8.tar.bz2
openbsd-ff0ebfdd90c1f0edf8cda1269349b659fd4250c8.zip
Reimplement part of the openssl/x509 regress tests in C
Instead of using s_client and s_server and complicated shell scripts, we can reuse the framework from the ssl_get_shared_cipher() regress test and inspect the verify return value directly. Discussed with beck jan jsing
Diffstat (limited to 'src')
-rw-r--r--src/regress/lib/libssl/verify/Makefile37
-rw-r--r--src/regress/lib/libssl/verify/create-libressl-test-certs.pl111
-rw-r--r--src/regress/lib/libssl/verify/verify.c373
3 files changed, 521 insertions, 0 deletions
diff --git a/src/regress/lib/libssl/verify/Makefile b/src/regress/lib/libssl/verify/Makefile
new file mode 100644
index 0000000000..515b22e07a
--- /dev/null
+++ b/src/regress/lib/libssl/verify/Makefile
@@ -0,0 +1,37 @@
1# $OpenBSD: Makefile,v 1.1.1.1 2021/08/30 17:27:45 tb Exp $
2
3.if !(make(clean) || make(cleandir) || make(obj))
4. if !exists(/usr/local/libdata/perl5/site_perl/IO/Socket/SSL.pm)
5regress:
6 @echo "missing package p5-IO-Socket-SSL"
7 @echo SKIPPED
8. endif
9.endif
10PROGS += verify
11
12.for p in ${PROGS}
13REGRESS_TARGETS += run-$p
14.endfor
15
16LDADD = -lcrypto -lssl
17DPADD = ${LIBCRYPTO} ${LIBSSL}
18WARNINGS = Yes
19CFLAGS += -DLIBRESSL_INTERNAL -Wundef -Werror
20
21PERL ?= perl
22
23REGRESS_SETUP_ONCE += create-libressl-test-certs
24create-libressl-test-certs: create-libressl-test-certs.pl
25 ${PERL} ${.CURDIR}/$@.pl
26
27
28CLEANFILES += *.pem *.key
29
30.for p in ${PROGS}
31run-$p: $p
32 ./$p
33
34.PHONY: run-$p
35.endfor
36
37.include <bsd.regress.mk>
diff --git a/src/regress/lib/libssl/verify/create-libressl-test-certs.pl b/src/regress/lib/libssl/verify/create-libressl-test-certs.pl
new file mode 100644
index 0000000000..fdb718aadc
--- /dev/null
+++ b/src/regress/lib/libssl/verify/create-libressl-test-certs.pl
@@ -0,0 +1,111 @@
1#!/usr/bin/perl
2
3# Copyright (c) 2021 Steffen Ullrich <sullr@cpan.org>
4# Public Domain
5
6use strict;
7use warnings;
8use IO::Socket::SSL::Utils;
9
10# primitive CA - ROOT
11my @ca = cert(
12 CA => 1,
13 subject => { CN => 'ROOT' }
14);
15out('caR.pem', pem(crt => $ca[0]));
16out('caR.key', pem(key => $ca[1]));
17
18# server certificate where SAN contains in-label wildcards, which a
19# client MAY choose to accept as per RFC 6125 section 6.4.3.
20my @leafcert = cert(
21 issuer => \@ca,
22 purpose => 'server',
23 subject => { CN => 'server.local' },
24 subjectAltNames => [
25 [ DNS => 'bar.server.local' ],
26 [ DNS => 'www*.server.local'],
27 [ DNS => '*.www.server.local'],
28 [ DNS => 'foo.server.local' ],
29 [ DNS => 'server.local' ],
30 ]
31);
32out('server-unusual-wildcard.pem', pem(@leafcert));
33
34@leafcert = cert(
35 issuer => \@ca,
36 purpose => 'server',
37 subject => { CN => 'server.local' },
38 subjectAltNames => [
39 [ DNS => 'bar.server.local' ],
40 [ DNS => '*.www.server.local'],
41 [ DNS => 'foo.server.local' ],
42 [ DNS => 'server.local' ],
43 ]
44);
45out('server-common-wildcard.pem', pem(@leafcert));
46
47# alternative CA - OLD_ROOT
48my @caO = cert(
49 CA => 1,
50 subject => { CN => 'OLD_ROOT' }
51);
52out('caO.pem', pem(crt => $caO[0]));
53out('caO.key', pem(key => $caO[1]));
54
55# alternative ROOT CA, signed by OLD_ROOT, same key as other ROOT CA
56my @caX = cert(
57 issuer => \@caO,
58 CA => 1,
59 subject => { CN => 'ROOT' },
60 key => $ca[1],
61);
62out('caX.pem', pem(crt => $caX[0]));
63out('caX.key', pem(key => $caX[1]));
64
65# subCA below ROOT
66my @subcaR = cert(
67 issuer => \@ca,
68 CA => 1,
69 subject => { CN => 'SubCA.of.ROOT' }
70);
71out('subcaR.pem', pem(crt => $subcaR[0]));
72out('subcaR.key', pem(key => $subcaR[1]));
73out('chainSX.pem', pem($subcaR[0]), pem($caX[0]));
74
75@leafcert = cert(
76 issuer => \@subcaR,
77 purpose => 'server',
78 subject => { CN => 'server.subca.local' },
79 subjectAltNames => [
80 [ DNS => 'server.subca.local' ],
81 ]
82);
83out('server-subca.pem', pem(@leafcert));
84out('server-subca-chainSX.pem', pem(@leafcert, $subcaR[0], $caX[0]));
85out('server-subca-chainS.pem', pem(@leafcert, $subcaR[0]));
86
87
88sub cert { CERT_create(not_after => 10*365*86400+time(), @_) }
89sub pem {
90 my @default = qw(crt key);
91 my %m = (key => \&PEM_key2string, crt => \&PEM_cert2string);
92 my $result = '';
93 while (my $f = shift(@_)) {
94 my $v;
95 if ($f =~m{^(key|crt)$}) {
96 $v = shift(@_);
97 } else {
98 $v = $f;
99 $f = shift(@default) || 'crt';
100 }
101 $f = $m{$f} || die "wrong key $f";
102 $result .= $f->($v);
103 }
104 return $result;
105}
106
107sub out {
108 my $file = shift;
109 open(my $fh,'>',"$file") or die "failed to create $file: $!";
110 print $fh @_
111}
diff --git a/src/regress/lib/libssl/verify/verify.c b/src/regress/lib/libssl/verify/verify.c
new file mode 100644
index 0000000000..8784396a79
--- /dev/null
+++ b/src/regress/lib/libssl/verify/verify.c
@@ -0,0 +1,373 @@
1/* $OpenBSD: verify.c,v 1.1.1.1 2021/08/30 17:27:45 tb Exp $ */
2/*
3 * Copyright (c) 2021 Theo Buehler <tb@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18/* Based on https://github.com/noxxi/libressl-tests */
19
20#include <stdint.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24
25#include <openssl/bio.h>
26#include <openssl/crypto.h>
27#include <openssl/err.h>
28#include <openssl/x509_vfy.h>
29#include <openssl/ssl.h>
30
31struct peer_config {
32 const char *name;
33 int server;
34 const char *cert;
35 const char *key;
36 const char *ca_file;
37};
38
39struct ssl_wildcard_test_data {
40 const char *description;
41 struct peer_config client_config;
42 struct peer_config server_config;
43 long verify_result;
44};
45
46static const struct ssl_wildcard_test_data ssl_wildcard_tests[] = {
47 {
48 .description = "unusual wildcard cert, no CA given to client",
49 .client_config = {
50 .name = "client",
51 .server = 0,
52 .cert = NULL,
53 .ca_file = NULL,
54 },
55 .server_config = {
56 .name = "server",
57 .server = 1,
58 .cert = "server-unusual-wildcard.pem",
59 .key = "server-unusual-wildcard.pem",
60 },
61 /* OpenSSL returns X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE */
62 .verify_result = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
63 },
64
65 {
66 .description = "unusual wildcard cert, CA given to client",
67 .client_config = {
68 .name = "client",
69 .server = 0,
70 .cert = NULL,
71 .ca_file = "caR.pem",
72 },
73 .server_config = {
74 .name = "server",
75 .server = 1,
76 .cert = "server-unusual-wildcard.pem",
77 .key = "server-unusual-wildcard.pem",
78 },
79 .verify_result = X509_V_OK,
80 },
81
82 {
83 .description = "common wildcard cert, no CA given to client",
84 .client_config = {
85 .name = "client",
86 .server = 0,
87 .cert = NULL,
88 .ca_file = NULL,
89 },
90 .server_config = {
91 .name = "server",
92 .server = 1,
93 .cert = "server-common-wildcard.pem",
94 .key = "server-common-wildcard.pem",
95 },
96 /* OpenSSL returns X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE */
97 .verify_result = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
98 },
99
100 {
101 .description = "common wildcard cert, CA given to client",
102 .client_config = {
103 .name = "client",
104 .server = 0,
105 .cert = NULL,
106 .ca_file = "caR.pem",
107 },
108 .server_config = {
109 .name = "server",
110 .server = 1,
111 .cert = "server-common-wildcard.pem",
112 .key = "server-common-wildcard.pem",
113 },
114 .verify_result = X509_V_OK,
115 },
116
117 {
118 .description = "server sends all chain certificates",
119 .client_config = {
120 .name = "client",
121 .server = 0,
122 .cert = NULL,
123 .ca_file = "caR.pem",
124 },
125 .server_config = {
126 .name = "server",
127 .server = 1,
128 .cert = "server-subca-chainS.pem",
129 .key = "server-subca-chainS.pem",
130 .ca_file = "subcaR.pem"
131 },
132 .verify_result = X509_V_OK,
133 },
134};
135
136static const size_t N_SSL_WILDCARD_TESTS =
137 sizeof(ssl_wildcard_tests) / sizeof(ssl_wildcard_tests[0]);
138
139static SSL_CTX *
140peer_config_to_ssl_ctx(const struct peer_config *config)
141{
142 SSL_CTX *ctx;
143
144 if ((ctx = SSL_CTX_new(TLS_method())) == NULL) {
145 fprintf(stderr, "SSL_CTX_new(%s) failed\n", config->name);
146 goto err;
147 }
148
149 if (config->server) {
150 if (!SSL_CTX_use_certificate_file(ctx, config->cert,
151 SSL_FILETYPE_PEM)) {
152 fprintf(stderr, "use_certificate_file(%s) failed\n",
153 config->name);
154 goto err;
155 }
156 if (config->key != NULL && !SSL_CTX_use_PrivateKey_file(ctx,
157 config->key, SSL_FILETYPE_PEM)) {
158 fprintf(stderr, "use_PrivateKey_file(%s) failed\n",
159 config->name);
160 goto err;
161 }
162 }
163
164 if (config->ca_file != NULL) {
165 if (!SSL_CTX_load_verify_locations(ctx, config->ca_file, NULL)) {
166 fprintf(stderr, "load_verify_locations(%s) failed\n",
167 config->name);
168 goto err;
169 }
170 }
171
172 return ctx;
173
174 err:
175 SSL_CTX_free(ctx);
176 return NULL;
177}
178
179/* Connect client and server via a pair of "nonblocking" memory BIOs. */
180static int
181connect_peers(SSL *client_ssl, SSL *server_ssl, const char *description)
182{
183 BIO *client_wbio = NULL, *server_wbio = NULL;
184 int ret = 0;
185
186 if ((client_wbio = BIO_new(BIO_s_mem())) == NULL) {
187 fprintf(stderr, "%s: failed to create client BIO\n",
188 description);
189 goto err;
190 }
191 if ((server_wbio = BIO_new(BIO_s_mem())) == NULL) {
192 fprintf(stderr, "%s: failed to create server BIO\n",
193 description);
194 goto err;
195 }
196 if (BIO_set_mem_eof_return(client_wbio, -1) <= 0) {
197 fprintf(stderr, "%s: failed to set client eof return\n",
198 description);
199 goto err;
200 }
201 if (BIO_set_mem_eof_return(server_wbio, -1) <= 0) {
202 fprintf(stderr, "%s: failed to set server eof return\n",
203 description);
204 goto err;
205 }
206
207 /* Avoid double free. SSL_set_bio() takes ownership of the BIOs. */
208 BIO_up_ref(client_wbio);
209 BIO_up_ref(server_wbio);
210
211 SSL_set_bio(client_ssl, server_wbio, client_wbio);
212 SSL_set_bio(server_ssl, client_wbio, server_wbio);
213 client_wbio = NULL;
214 server_wbio = NULL;
215
216 ret = 1;
217
218 err:
219 BIO_free(client_wbio);
220 BIO_free(server_wbio);
221
222 return ret;
223}
224
225static int
226push_data_to_peer(SSL *ssl, int *ret, int (*func)(SSL *), const char *func_name,
227 const char *description)
228{
229 int ssl_err = 0;
230
231 if (*ret == 1)
232 return 1;
233
234 /*
235 * Do SSL_connect/SSL_accept/SSL_shutdown once and loop while hitting
236 * WANT_WRITE. If done or on WANT_READ hand off to peer.
237 */
238
239 do {
240 if ((*ret = func(ssl)) <= 0)
241 ssl_err = SSL_get_error(ssl, *ret);
242 } while (*ret <= 0 && ssl_err == SSL_ERROR_WANT_WRITE);
243
244 /* Ignore erroneous error - see SSL_shutdown(3)... */
245 if (func == SSL_shutdown && ssl_err == SSL_ERROR_SYSCALL)
246 return 1;
247
248 if (*ret <= 0 && ssl_err != SSL_ERROR_WANT_READ) {
249 fprintf(stderr, "%s: %s failed\n", description, func_name);
250 ERR_print_errors_fp(stderr);
251 return 0;
252 }
253
254 return 1;
255}
256
257/*
258 * Alternate between loops of SSL_connect() and SSL_accept() as long as only
259 * WANT_READ and WANT_WRITE situations are encountered. A function is repeated
260 * until WANT_READ is returned or it succeeds, then it's the other function's
261 * turn to make progress. Succeeds if SSL_connect() and SSL_accept() return 1.
262 */
263static int
264handshake(SSL *client_ssl, SSL *server_ssl, const char *description)
265{
266 int loops = 0, client_ret = 0, server_ret = 0;
267
268 while (loops++ < 10 && (client_ret <= 0 || server_ret <= 0)) {
269 if (!push_data_to_peer(client_ssl, &client_ret, SSL_connect,
270 "SSL_connect", description))
271 return 0;
272
273 if (!push_data_to_peer(server_ssl, &server_ret, SSL_accept,
274 "SSL_accept", description))
275 return 0;
276 }
277
278 if (client_ret != 1 || server_ret != 1) {
279 fprintf(stderr, "%s: failed\n", __func__);
280 return 0;
281 }
282
283 return 1;
284}
285
286static int
287shutdown_peers(SSL *client_ssl, SSL *server_ssl, const char *description)
288{
289 int loops = 0, client_ret = 0, server_ret = 0;
290
291 while (loops++ < 10 && (client_ret <= 0 || server_ret <= 0)) {
292 if (!push_data_to_peer(client_ssl, &client_ret, SSL_shutdown,
293 "client shutdown", description))
294 return 0;
295
296 if (!push_data_to_peer(server_ssl, &server_ret, SSL_shutdown,
297 "server shutdown", description))
298 return 0;
299 }
300
301 if (client_ret != 1 || server_ret != 1) {
302 fprintf(stderr, "%s: failed\n", __func__);
303 return 0;
304 }
305
306 return 1;
307}
308
309static int
310test_ssl_wildcards(const struct ssl_wildcard_test_data *test)
311{
312 SSL_CTX *client_ctx = NULL, *server_ctx = NULL;
313 SSL *client_ssl = NULL, *server_ssl = NULL;
314 long verify_result;
315 int failed = 1;
316
317 if ((client_ctx = peer_config_to_ssl_ctx(&test->client_config)) == NULL)
318 goto err;
319 if ((server_ctx = peer_config_to_ssl_ctx(&test->server_config)) == NULL)
320 goto err;
321
322 if ((client_ssl = SSL_new(client_ctx)) == NULL) {
323 fprintf(stderr, "%s: failed to create client SSL\n",
324 test->description);
325 goto err;
326 }
327 if ((server_ssl = SSL_new(server_ctx)) == NULL) {
328 fprintf(stderr, "%s: failed to create server SSL\n",
329 test->description);
330 goto err;
331 }
332
333 if (!connect_peers(client_ssl, server_ssl, test->description))
334 goto err;
335
336 if (!handshake(client_ssl, server_ssl, test->description))
337 goto err;
338
339 verify_result = SSL_get_verify_result(client_ssl);
340
341 if (test->verify_result == verify_result) {
342 failed = 0;
343 fprintf(stderr, "%s: ok\n", test->description);
344 } else
345 fprintf(stderr, "%s: verify_result: want %ld, got %ld\n",
346 test->description, test->verify_result, verify_result);
347
348 if (!shutdown_peers(client_ssl, server_ssl, test->description))
349 goto err;
350
351 err:
352 SSL_CTX_free(client_ctx);
353 SSL_CTX_free(server_ctx);
354 SSL_free(client_ssl);
355 SSL_free(server_ssl);
356
357 return failed;
358}
359
360int
361main(int argc, char **argv)
362{
363 size_t i;
364 int failed = 0;
365
366 for (i = 0; i < N_SSL_WILDCARD_TESTS; i++)
367 failed |= test_ssl_wildcards(&ssl_wildcard_tests[i]);
368
369 if (failed == 0)
370 printf("PASS %s\n", __FILE__);
371
372 return failed;
373}