summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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;