summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbeck <>2021-09-01 08:12:15 +0000
committerbeck <>2021-09-01 08:12:15 +0000
commit414ce9bc403d88f0560f6570625d3116e7d18e5b (patch)
tree672b8d50480a1180e4323a8905815a195ea03bfb
parentaa22a52eaacf467f4410ed6bfb0eb8d7fc2ac99a (diff)
downloadopenbsd-414ce9bc403d88f0560f6570625d3116e7d18e5b.tar.gz
openbsd-414ce9bc403d88f0560f6570625d3116e7d18e5b.tar.bz2
openbsd-414ce9bc403d88f0560f6570625d3116e7d18e5b.zip
Add a regression test to verify that we call the callback in the same
order on success for both the legacy and the new verifier, This avoids problems as seen in perl's regression tests for some of the crazy things net:ssleay does. This is currently marked as expected to fail, it will be expected to succeed after a forthcoming commit from me.
-rw-r--r--src/regress/lib/libcrypto/x509/Makefile19
-rw-r--r--src/regress/lib/libcrypto/x509/callback.c431
-rw-r--r--src/regress/lib/libcrypto/x509/callback.pl105
3 files changed, 551 insertions, 4 deletions
diff --git a/src/regress/lib/libcrypto/x509/Makefile b/src/regress/lib/libcrypto/x509/Makefile
index f394a93655..b05bf0bc66 100644
--- a/src/regress/lib/libcrypto/x509/Makefile
+++ b/src/regress/lib/libcrypto/x509/Makefile
@@ -1,6 +1,6 @@
1# $OpenBSD: Makefile,v 1.6 2021/08/28 15:20:19 tb Exp $ 1# $OpenBSD: Makefile,v 1.7 2021/09/01 08:12:15 beck Exp $
2 2
3PROGS = constraints verify x509attribute x509name 3PROGS = constraints verify x509attribute x509name callback
4LDADD= -Wl,-Bstatic -lcrypto -Wl,-Bdynamic 4LDADD= -Wl,-Bstatic -lcrypto -Wl,-Bdynamic
5DPADD= ${LIBCRYPTO} 5DPADD= ${LIBCRYPTO}
6WARNINGS= Yes 6WARNINGS= Yes
@@ -8,8 +8,15 @@ CFLAGS+= -DLIBRESSL_INTERNAL -Wall -Werror -I$(BSDSRCDIR)/lib/libcrypto/x509
8 8
9SUBDIR += bettertls 9SUBDIR += bettertls
10 10
11REGRESS_TARGETS=regress-constraints regress-verify regress-x509attribute regress-x509name 11REGRESS_TARGETS += regress-verify
12CLEANFILES+= x509name.result 12REGRESS_TARGETS += regress-constraints
13REGRESS_TARGETS += regress-x509attribute
14REGRESS_TARGETS += regress-x509name
15REGRESS_TARGETS += regress-callback
16
17REGRESS_EXPECTED_FAILURES += regress-callback
18
19CLEANFILES+= x509name.result callbackout
13 20
14.if make(clean) || make(cleandir) 21.if make(clean) || make(cleandir)
15. if ${.OBJDIR} != ${.CURDIR} 22. if ${.OBJDIR} != ${.CURDIR}
@@ -32,4 +39,8 @@ regress-x509name: x509name
32 ./x509name > x509name.result 39 ./x509name > x509name.result
33 diff -u ${.CURDIR}/x509name.expected x509name.result 40 diff -u ${.CURDIR}/x509name.expected x509name.result
34 41
42regress-callback: callback
43 ./callback ${.CURDIR}/../certs
44 perl ${.CURDIR}/callback.pl callback.out
45
35.include <bsd.regress.mk> 46.include <bsd.regress.mk>
diff --git a/src/regress/lib/libcrypto/x509/callback.c b/src/regress/lib/libcrypto/x509/callback.c
new file mode 100644
index 0000000000..f7cb546d5c
--- /dev/null
+++ b/src/regress/lib/libcrypto/x509/callback.c
@@ -0,0 +1,431 @@
1/* $OpenBSD: callback.c,v 1.1 2021/09/01 08:12:15 beck Exp $ */
2/*
3 * Copyright (c) 2020 Joel Sing <jsing@openbsd.org>
4 * Copyright (c) 2020-2021 Bob Beck <beck@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <err.h>
20#include <string.h>
21
22#include <openssl/bio.h>
23#include <openssl/err.h>
24#include <openssl/pem.h>
25#include <openssl/x509.h>
26#include <openssl/x509v3.h>
27#include <openssl/x509_verify.h>
28
29#define MODE_MODERN_VFY 0
30#define MODE_MODERN_VFY_DIR 1
31#define MODE_LEGACY_VFY 2
32#define MODE_VERIFY 3
33
34static int verbose = 1;
35FILE *output;
36
37static int
38passwd_cb(char *buf, int size, int rwflag, void *u)
39{
40 memset(buf, 0, size);
41 return (0);
42}
43
44static int
45certs_from_file(const char *filename, STACK_OF(X509) **certs)
46{
47 STACK_OF(X509_INFO) *xis = NULL;
48 STACK_OF(X509) *xs = NULL;
49 BIO *bio = NULL;
50 X509 *x;
51 int i;
52
53 if ((xs = sk_X509_new_null()) == NULL)
54 errx(1, "failed to create X509 stack");
55 if ((bio = BIO_new_file(filename, "r")) == NULL) {
56 ERR_print_errors_fp(stderr);
57 errx(1, "failed to create bio");
58 }
59 if ((xis = PEM_X509_INFO_read_bio(bio, NULL, passwd_cb, NULL)) == NULL)
60 errx(1, "failed to read PEM");
61
62 for (i = 0; i < sk_X509_INFO_num(xis); i++) {
63 if ((x = sk_X509_INFO_value(xis, i)->x509) == NULL)
64 continue;
65 if (!sk_X509_push(xs, x))
66 errx(1, "failed to push X509");
67 X509_up_ref(x);
68 }
69
70 *certs = xs;
71 xs = NULL;
72
73 sk_X509_INFO_pop_free(xis, X509_INFO_free);
74 sk_X509_pop_free(xs, X509_free);
75 BIO_free(bio);
76
77 return 1;
78}
79
80static int
81verify_cert_cb(int ok, X509_STORE_CTX *xsc)
82{
83 X509 *current_cert, *issuer_cert;
84 int verify_err, verify_depth;
85
86 current_cert = X509_STORE_CTX_get_current_cert(xsc);
87 issuer_cert = X509_STORE_CTX_get0_current_issuer(xsc);
88 verify_depth = X509_STORE_CTX_get_error_depth(xsc);
89 verify_err = X509_STORE_CTX_get_error(xsc);
90 fprintf(output, "depth %d error %d", verify_depth, verify_err);
91 fprintf(output, " cert: ");
92 if (current_cert != NULL) {
93 X509_NAME_print_ex_fp(output,
94 X509_get_subject_name(current_cert), 0,
95 XN_FLAG_ONELINE);
96 } else
97 fprintf(output, "NULL");
98 fprintf(output, " issuer: ");
99 if (issuer_cert != NULL) {
100 X509_NAME_print_ex_fp(output,
101 X509_get_subject_name(issuer_cert), 0,
102 XN_FLAG_ONELINE);
103 } else
104 fprintf(output, "NULL");
105 fprintf(output, "\n");
106
107 return ok;
108}
109
110static void
111verify_cert(const char *roots_dir, const char *roots_file,
112 const char *bundle_file, int *chains, int mode)
113{
114 STACK_OF(X509) *roots = NULL, *bundle = NULL;
115 X509_STORE_CTX *xsc = NULL;
116 X509_STORE *store = NULL;
117 int verify_err, use_dir;
118 unsigned long flags;
119 X509 *leaf = NULL;
120
121 *chains = 0;
122 use_dir = (mode == MODE_MODERN_VFY_DIR);
123
124 if (!use_dir && !certs_from_file(roots_file, &roots))
125 errx(1, "failed to load roots from '%s'", roots_file);
126 if (!certs_from_file(bundle_file, &bundle))
127 errx(1, "failed to load bundle from '%s'", bundle_file);
128 if (sk_X509_num(bundle) < 1)
129 errx(1, "not enough certs in bundle");
130 leaf = sk_X509_shift(bundle);
131
132 if ((xsc = X509_STORE_CTX_new()) == NULL)
133 errx(1, "X509_STORE_CTX");
134 if (use_dir && (store = X509_STORE_new()) == NULL)
135 errx(1, "X509_STORE");
136 if (!X509_STORE_CTX_init(xsc, store, leaf, bundle)) {
137 ERR_print_errors_fp(stderr);
138 errx(1, "failed to init store context");
139 }
140 if (use_dir) {
141 if (!X509_STORE_load_locations(store, NULL, roots_dir))
142 errx(1, "failed to set by_dir directory of %s", roots_dir);
143 }
144 if (mode == MODE_LEGACY_VFY) {
145 flags = X509_VERIFY_PARAM_get_flags(xsc->param);
146 flags |= X509_V_FLAG_LEGACY_VERIFY;
147 X509_VERIFY_PARAM_set_flags(xsc->param, flags);
148 } else {
149 flags = X509_VERIFY_PARAM_get_flags(xsc->param);
150 flags &= ~X509_V_FLAG_LEGACY_VERIFY;
151 X509_VERIFY_PARAM_set_flags(xsc->param, flags);
152 }
153
154 if (verbose)
155 X509_STORE_CTX_set_verify_cb(xsc, verify_cert_cb);
156 if (!use_dir)
157 X509_STORE_CTX_set0_trusted_stack(xsc, roots);
158 if (X509_verify_cert(xsc) == 1) {
159 *chains = 1; /* XXX */
160 goto done;
161 }
162
163 verify_err = X509_STORE_CTX_get_error(xsc);
164 if (verify_err == 0)
165 errx(1, "Error unset on failure!\n");
166
167 fprintf(stderr, "failed to verify at %d: %s\n",
168 X509_STORE_CTX_get_error_depth(xsc),
169 X509_verify_cert_error_string(verify_err));
170
171 done:
172 sk_X509_pop_free(roots, X509_free);
173 sk_X509_pop_free(bundle, X509_free);
174 X509_STORE_free(store);
175 X509_STORE_CTX_free(xsc);
176 X509_free(leaf);
177}
178
179struct verify_cert_test {
180 const char *id;
181 int want_chains;
182 int failing;
183};
184
185struct verify_cert_test verify_cert_tests[] = {
186 {
187 .id = "1a",
188 .want_chains = 1,
189 },
190 {
191 .id = "2a",
192 .want_chains = 1,
193 },
194 {
195 .id = "2b",
196 .want_chains = 0,
197 },
198 {
199 .id = "2c",
200 .want_chains = 1,
201 },
202 {
203 .id = "3a",
204 .want_chains = 1,
205 },
206 {
207 .id = "3b",
208 .want_chains = 0,
209 },
210 {
211 .id = "3c",
212 .want_chains = 0,
213 },
214 {
215 .id = "3d",
216 .want_chains = 0,
217 },
218 {
219 .id = "3e",
220 .want_chains = 1,
221 },
222 {
223 .id = "4a",
224 .want_chains = 2,
225 },
226 {
227 .id = "4b",
228 .want_chains = 1,
229 },
230 {
231 .id = "4c",
232 .want_chains = 1,
233 .failing = 1,
234 },
235 {
236 .id = "4d",
237 .want_chains = 1,
238 },
239 {
240 .id = "4e",
241 .want_chains = 1,
242 },
243 {
244 .id = "4f",
245 .want_chains = 2,
246 },
247 {
248 .id = "4g",
249 .want_chains = 1,
250 .failing = 1,
251 },
252 {
253 .id = "4h",
254 .want_chains = 1,
255 },
256 {
257 .id = "5a",
258 .want_chains = 2,
259 },
260 {
261 .id = "5b",
262 .want_chains = 1,
263 .failing = 1,
264 },
265 {
266 .id = "5c",
267 .want_chains = 1,
268 },
269 {
270 .id = "5d",
271 .want_chains = 1,
272 },
273 {
274 .id = "5e",
275 .want_chains = 1,
276 .failing = 1,
277 },
278 {
279 .id = "5f",
280 .want_chains = 1,
281 },
282 {
283 .id = "5g",
284 .want_chains = 2,
285 },
286 {
287 .id = "5h",
288 .want_chains = 1,
289 },
290 {
291 .id = "5i",
292 .want_chains = 1,
293 .failing = 1,
294 },
295 {
296 .id = "6a",
297 .want_chains = 1,
298 },
299 {
300 .id = "6b",
301 .want_chains = 1,
302 .failing = 1,
303 },
304 {
305 .id = "7a",
306 .want_chains = 1,
307 .failing = 1,
308 },
309 {
310 .id = "7b",
311 .want_chains = 1,
312 },
313 {
314 .id = "8a",
315 .want_chains = 0,
316 },
317 {
318 .id = "9a",
319 .want_chains = 0,
320 },
321 {
322 .id = "10a",
323 .want_chains = 1,
324 },
325 {
326 .id = "10b",
327 .want_chains = 1,
328 },
329 {
330 .id = "11a",
331 .want_chains = 1,
332 .failing = 1,
333 },
334 {
335 .id = "11b",
336 .want_chains = 1,
337 },
338 {
339 .id = "12a",
340 .want_chains = 1,
341 },
342 {
343 .id = "13a",
344 .want_chains = 1,
345 },
346};
347
348#define N_VERIFY_CERT_TESTS \
349 (sizeof(verify_cert_tests) / sizeof(*verify_cert_tests))
350
351static int
352verify_cert_test(const char *certs_path, int mode)
353{
354 char *roots_file, *bundle_file, *roots_dir;
355 struct verify_cert_test *vct;
356 int failed = 0;
357 int chains;
358 size_t i;
359
360 for (i = 0; i < N_VERIFY_CERT_TESTS; i++) {
361 vct = &verify_cert_tests[i];
362
363 if (asprintf(&roots_file, "%s/%s/roots.pem", certs_path,
364 vct->id) == -1)
365 errx(1, "asprintf");
366 if (asprintf(&bundle_file, "%s/%s/bundle.pem", certs_path,
367 vct->id) == -1)
368 errx(1, "asprintf");
369 if (asprintf(&roots_dir, "./%s/roots", vct->id) == -1)
370 errx(1, "asprintf");
371
372 fprintf(output, "== Test %zu (%s)\n", i, vct->id);
373 fprintf(output, "== Legacy:\n");
374 mode = MODE_LEGACY_VFY;
375 verify_cert(roots_dir, roots_file, bundle_file, &chains, mode);
376 if ((mode == MODE_VERIFY && chains == vct->want_chains) ||
377 (chains == 0 && vct->want_chains == 0) ||
378 (chains == 1 && vct->want_chains > 0)) {
379 fprintf(output, "INFO: Succeeded with %d chains%s\n",
380 chains, vct->failing ? " (legacy failure)" : "");
381 if (mode == MODE_LEGACY_VFY && vct->failing)
382 failed |= 1;
383 } else {
384 fprintf(output, "FAIL: Failed with %d chains%s\n",
385 chains, vct->failing ? " (legacy failure)" : "");
386 if (!vct->failing)
387 failed |= 1;
388 }
389 fprintf(output, "\n");
390 fprintf(output, "== Modern:\n");
391 mode = MODE_MODERN_VFY;
392 verify_cert(roots_dir, roots_file, bundle_file, &chains, mode);
393 if ((mode == MODE_VERIFY && chains == vct->want_chains) ||
394 (chains == 0 && vct->want_chains == 0) ||
395 (chains == 1 && vct->want_chains > 0)) {
396 fprintf(output, "INFO: Succeeded with %d chains%s\n",
397 chains, vct->failing ? " (legacy failure)" : "");
398 if (mode == MODE_LEGACY_VFY && vct->failing)
399 failed |= 1;
400 } else {
401 fprintf(output, "FAIL: Failed with %d chains%s\n",
402 chains, vct->failing ? " (legacy failure)" : "");
403 if (!vct->failing)
404 failed |= 1;
405 }
406 fprintf(output, "\n");
407
408 free(roots_file);
409 free(bundle_file);
410 free(roots_dir);
411 }
412
413 return failed;
414}
415
416int
417main(int argc, char **argv)
418{
419 int failed = 0;
420
421 if (argc != 2) {
422 fprintf(stderr, "usage: %s <certs_path>\n", argv[0]);
423 exit(1);
424 }
425
426 output = fopen("callback.out", "w+");
427
428 fprintf(stderr, "\n\nTesting legacy and modern X509_vfy\n");
429 failed |= verify_cert_test(argv[1], MODE_LEGACY_VFY);
430 return (failed);
431}
diff --git a/src/regress/lib/libcrypto/x509/callback.pl b/src/regress/lib/libcrypto/x509/callback.pl
new file mode 100644
index 0000000000..410dfc1a18
--- /dev/null
+++ b/src/regress/lib/libcrypto/x509/callback.pl
@@ -0,0 +1,105 @@
1#!/usr/bin/perl
2#
3# Copyright (c) 2021 Bob Beck <beck@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# Validate that we call out with the same chain on the callback with the legacy
19# verifier as we do with the new verifier in compatibility mode. We ignore cases
20# where one of the tests does not succesd to find a chain, as the error paths
21# may be different. It's also ok for the new verifier to have signalled an
22# error before finding a chain since it may try something and then back up.
23
24my $Test;
25my $State = "Read";
26my @Legacy;
27my @Modern;
28my $Mfail;
29my @Lfail;
30my $Failures = "";
31
32while (<>) {
33 chomp;
34 print "$_\n";
35 if ($State eq "Read") {
36 if (/^== Test/) {
37 $Test = $_;
38 @Legacy = ();
39 @Modern = ();
40 $Mfail = 0;
41 $Lfail = 0;
42 $State = "Read";
43 next;
44 }
45 if (/^== Legacy/) {
46 $State = "Legacy";
47 next;
48 }
49 if (/^== Modern/) {
50 $State = "Modern";
51 next;
52 }
53 }
54 if ($State eq "Legacy") {
55 if (/^INFO/) {
56 $State = "Read";
57 next;
58 }
59 if (/^FAIL/) {
60 $Lfail = 1;
61 $State = "Read";
62 next;
63 }
64 push @Legacy, ($_);
65 }
66 if ($State eq "Modern") {
67 if (/^INFO/) {
68 $State = "Process";
69 next;
70 }
71 if (/^FAIL/) {
72 $Mfail = 1;
73 $State = "Process";
74 next;
75 }
76 push @Modern, ($_);
77 }
78 if ($State eq "Process") {
79 my $mlen = scalar(@Modern);
80 my $llen = scalar(@Legacy);
81 print "$Test has $llen legacy lines and $mlen modern lines\n";
82 while ($mlen > 0 && $llen > 0) {
83 my $lline = $Legacy[$llen - 1];
84 my $mline = $Modern[$mlen - 1];
85
86 if (!@Mfail && !$Lfail && $mline =~ /error 0 cert/ && $lline =~ /error 0 cert/) {
87 if ($lline ne $mline) {
88 print "MISMATCH: $lline VS $mline\n";
89 $Failures .= "$Test ";
90 }
91 }
92 $mlen--;
93 $llen--;
94 }
95 $State = "Read";
96 next;
97 }
98}
99print "=============Test Summary============\n";
100if ($Failures ne "") {
101 print "FAIL: Mismatech on $Failures\n";
102 exit 1;
103}
104print "Success: No Mismatches\n";
105exit 0;