summaryrefslogtreecommitdiff
path: root/src/regress/lib/libcrypto/x509/callback.c
diff options
context:
space:
mode:
authorbeck <>2021-09-01 08:12:15 +0000
committerbeck <>2021-09-01 08:12:15 +0000
commit414ce9bc403d88f0560f6570625d3116e7d18e5b (patch)
tree672b8d50480a1180e4323a8905815a195ea03bfb /src/regress/lib/libcrypto/x509/callback.c
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.
Diffstat (limited to 'src/regress/lib/libcrypto/x509/callback.c')
-rw-r--r--src/regress/lib/libcrypto/x509/callback.c431
1 files changed, 431 insertions, 0 deletions
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}