xref: /openbsd-src/regress/lib/libcrypto/x509/verify.c (revision 8550894424f8a4aa4aafb6cd57229dd6ed7cd9dd)
1 /* $OpenBSD: verify.c,v 1.10 2022/10/17 18:36:52 jsing 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/crypto.h>
24 #include <openssl/err.h>
25 #include <openssl/pem.h>
26 #include <openssl/x509.h>
27 #include <openssl/x509v3.h>
28 #include <openssl/x509_verify.h>
29 
30 #define MODE_MODERN_VFY		0
31 #define MODE_MODERN_VFY_DIR	1
32 #define MODE_LEGACY_VFY		2
33 #define MODE_VERIFY		3
34 
35 static int verbose = 1;
36 
37 static int
38 passwd_cb(char *buf, int size, int rwflag, void *u)
39 {
40 	memset(buf, 0, size);
41 	return (0);
42 }
43 
44 static int
45 certs_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 
80 static int
81 verify_cert_cb(int ok, X509_STORE_CTX *xsc)
82 {
83 	X509 *current_cert;
84 	int verify_err;
85 
86 	current_cert = X509_STORE_CTX_get_current_cert(xsc);
87 	if (current_cert != NULL) {
88 		X509_NAME_print_ex_fp(stderr,
89 		    X509_get_subject_name(current_cert), 0,
90 		    XN_FLAG_ONELINE);
91 		fprintf(stderr, "\n");
92 	}
93 
94 	verify_err = X509_STORE_CTX_get_error(xsc);
95 	if (verify_err != X509_V_OK) {
96 		fprintf(stderr, "verify error at depth %d: %s\n",
97 		    X509_STORE_CTX_get_error_depth(xsc),
98 		    X509_verify_cert_error_string(verify_err));
99 	}
100 
101 	return ok;
102 }
103 
104 static void
105 verify_cert(const char *roots_dir, const char *roots_file,
106     const char *bundle_file, int *chains, int *error, int *error_depth,
107     int mode)
108 {
109 	STACK_OF(X509) *roots = NULL, *bundle = NULL;
110 	X509_STORE_CTX *xsc = NULL;
111 	X509_STORE *store = NULL;
112 	X509 *leaf = NULL;
113 	int use_dir;
114 	int ret;
115 
116 	*chains = 0;
117 	*error = 0;
118 	*error_depth = 0;
119 
120 	use_dir = (mode == MODE_MODERN_VFY_DIR);
121 
122 	if (!use_dir && !certs_from_file(roots_file, &roots))
123 		errx(1, "failed to load roots from '%s'", roots_file);
124 	if (!certs_from_file(bundle_file, &bundle))
125 		errx(1, "failed to load bundle from '%s'", bundle_file);
126 	if (sk_X509_num(bundle) < 1)
127 		errx(1, "not enough certs in bundle");
128 	leaf = sk_X509_shift(bundle);
129 
130 	if ((xsc = X509_STORE_CTX_new()) == NULL)
131 		errx(1, "X509_STORE_CTX");
132 	if (use_dir && (store = X509_STORE_new()) == NULL)
133 		errx(1, "X509_STORE");
134 	if (!X509_STORE_CTX_init(xsc, store, leaf, bundle)) {
135 		ERR_print_errors_fp(stderr);
136 		errx(1, "failed to init store context");
137 	}
138 	if (use_dir) {
139 		if (!X509_STORE_load_locations(store, NULL, roots_dir))
140 			errx(1, "failed to set by_dir directory of %s", roots_dir);
141 	}
142 	if (mode == MODE_LEGACY_VFY)
143 		X509_STORE_CTX_set_flags(xsc, X509_V_FLAG_LEGACY_VERIFY);
144 	else
145 		X509_VERIFY_PARAM_clear_flags(X509_STORE_CTX_get0_param(xsc),
146 		    X509_V_FLAG_LEGACY_VERIFY);
147 
148 	if (verbose)
149 		X509_STORE_CTX_set_verify_cb(xsc, verify_cert_cb);
150 	if (!use_dir)
151 		X509_STORE_CTX_set0_trusted_stack(xsc, roots);
152 
153 	ret = X509_verify_cert(xsc);
154 
155 	*error = X509_STORE_CTX_get_error(xsc);
156 	*error_depth = X509_STORE_CTX_get_error_depth(xsc);
157 
158 	if (ret == 1) {
159 		*chains = 1; /* XXX */
160 		goto done;
161 	}
162 
163 	if (*error == 0)
164 		errx(1, "Error unset on failure!\n");
165 
166 	fprintf(stderr, "failed to verify at %d: %s\n",
167 	    *error_depth, X509_verify_cert_error_string(*error));
168 
169  done:
170 	sk_X509_pop_free(roots, X509_free);
171 	sk_X509_pop_free(bundle, X509_free);
172 	X509_STORE_free(store);
173 	X509_STORE_CTX_free(xsc);
174 	X509_free(leaf);
175 }
176 
177 static void
178 verify_cert_new(const char *roots_file, const char *bundle_file, int *chains)
179 {
180 	STACK_OF(X509) *roots = NULL, *bundle = NULL;
181 	X509_STORE_CTX *xsc = NULL;
182 	X509 *leaf = NULL;
183 	struct x509_verify_ctx *ctx;
184 
185 	*chains = 0;
186 
187 	if (!certs_from_file(roots_file, &roots))
188 		errx(1, "failed to load roots from '%s'", roots_file);
189 	if (!certs_from_file(bundle_file, &bundle))
190 		errx(1, "failed to load bundle from '%s'", bundle_file);
191 	if (sk_X509_num(bundle) < 1)
192 		errx(1, "not enough certs in bundle");
193 	leaf = sk_X509_shift(bundle);
194 
195         if ((xsc = X509_STORE_CTX_new()) == NULL)
196 		errx(1, "X509_STORE_CTX");
197 	if (!X509_STORE_CTX_init(xsc, NULL, leaf, bundle)) {
198 		ERR_print_errors_fp(stderr);
199 		errx(1, "failed to init store context");
200 	}
201 	if (verbose)
202 		X509_STORE_CTX_set_verify_cb(xsc, verify_cert_cb);
203 
204 	if ((ctx = x509_verify_ctx_new(roots)) == NULL)
205 		errx(1, "failed to create ctx");
206 	if (!x509_verify_ctx_set_intermediates(ctx, bundle))
207 		errx(1, "failed to set intermediates");
208 
209 	if ((*chains = x509_verify(ctx, leaf, NULL)) == 0) {
210 		fprintf(stderr, "failed to verify at %lu: %s\n",
211 		    x509_verify_ctx_error_depth(ctx),
212 		    x509_verify_ctx_error_string(ctx));
213 	} else {
214 		int c;
215 
216 		for (c = 0; verbose && c < *chains; c++) {
217 			STACK_OF(X509) *chain;
218 			int i;
219 
220 			fprintf(stderr, "Chain %d\n--------\n", c);
221 			chain = x509_verify_ctx_chain(ctx, c);
222 			for (i = 0; i < sk_X509_num(chain); i++) {
223 				X509 *cert = sk_X509_value(chain, i);
224 				X509_NAME_print_ex_fp(stderr,
225 				    X509_get_subject_name(cert), 0,
226 				    XN_FLAG_ONELINE);
227 				fprintf(stderr, "\n");
228 			}
229 		}
230 	}
231 	sk_X509_pop_free(roots, X509_free);
232 	sk_X509_pop_free(bundle, X509_free);
233 	X509_free(leaf);
234 	X509_STORE_CTX_free(xsc);
235 	x509_verify_ctx_free(ctx);
236 }
237 
238 struct verify_cert_test {
239 	const char *id;
240 	int want_chains;
241 	int want_error;
242 	int want_error_depth;
243 	int want_legacy_error;
244 	int want_legacy_error_depth;
245 	int failing;
246 };
247 
248 struct verify_cert_test verify_cert_tests[] = {
249 	{
250 		.id = "1a",
251 		.want_chains = 1,
252 	},
253 	{
254 		.id = "2a",
255 		.want_chains = 1,
256 	},
257 	{
258 		.id = "2b",
259 		.want_chains = 0,
260 		.want_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
261 		.want_error_depth = 0,
262 		.want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
263 		.want_legacy_error_depth = 0,
264 	},
265 	{
266 		.id = "2c",
267 		.want_chains = 1,
268 	},
269 	{
270 		.id = "3a",
271 		.want_chains = 1,
272 	},
273 	{
274 		.id = "3b",
275 		.want_chains = 0,
276 		.want_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
277 		.want_error_depth = 2,
278 		.want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
279 		.want_legacy_error_depth = 2,
280 	},
281 	{
282 		.id = "3c",
283 		.want_chains = 0,
284 		.want_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
285 		.want_error_depth = 1,
286 		.want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
287 		.want_legacy_error_depth = 1,
288 	},
289 	{
290 		.id = "3d",
291 		.want_chains = 0,
292 		.want_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
293 		.want_error_depth = 0,
294 		.want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
295 		.want_legacy_error_depth = 0,
296 	},
297 	{
298 		.id = "3e",
299 		.want_chains = 1,
300 	},
301 	{
302 		.id = "4a",
303 		.want_chains = 2,
304 	},
305 	{
306 		.id = "4b",
307 		.want_chains = 1,
308 	},
309 	{
310 		.id = "4c",
311 		.want_chains = 1,
312 		.want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
313 		.want_legacy_error_depth = 1,
314 		.failing = 1,
315 	},
316 	{
317 		.id = "4d",
318 		.want_chains = 1,
319 	},
320 	{
321 		.id = "4e",
322 		.want_chains = 1,
323 	},
324 	{
325 		.id = "4f",
326 		.want_chains = 2,
327 	},
328 	{
329 		.id = "4g",
330 		.want_chains = 1,
331 		.want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
332 		.want_legacy_error_depth = 1,
333 		.failing = 1,
334 	},
335 	{
336 		.id = "4h",
337 		.want_chains = 1,
338 	},
339 	{
340 		.id = "5a",
341 		.want_chains = 2,
342 	},
343 	{
344 		.id = "5b",
345 		.want_chains = 1,
346 		.want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
347 		.want_legacy_error_depth = 2,
348 		.failing = 1,
349 	},
350 	{
351 		.id = "5c",
352 		.want_chains = 1,
353 	},
354 	{
355 		.id = "5d",
356 		.want_chains = 1,
357 	},
358 	{
359 		.id = "5e",
360 		.want_chains = 1,
361 		.want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
362 		.want_legacy_error_depth = 1,
363 		.failing = 1,
364 	},
365 	{
366 		.id = "5f",
367 		.want_chains = 1,
368 	},
369 	{
370 		.id = "5g",
371 		.want_chains = 2,
372 	},
373 	{
374 		.id = "5h",
375 		.want_chains = 1,
376 	},
377 	{
378 		.id = "5i",
379 		.want_chains = 1,
380 		.want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
381 		.want_legacy_error_depth = 1,
382 		.failing = 1,
383 	},
384 	{
385 		.id = "6a",
386 		.want_chains = 1,
387 	},
388 	{
389 		.id = "6b",
390 		.want_chains = 1,
391 		.want_error = X509_V_ERR_CERT_HAS_EXPIRED,
392 		.want_error_depth = 0,
393 		.want_legacy_error = X509_V_ERR_CERT_HAS_EXPIRED,
394 		.want_legacy_error_depth = 2,
395 		.failing = 1,
396 	},
397 	{
398 		.id = "7a",
399 		.want_chains = 1,
400 		.want_error = X509_V_ERR_CERT_HAS_EXPIRED,
401 		.want_error_depth = 0,
402 		.want_legacy_error = X509_V_ERR_CERT_HAS_EXPIRED,
403 		.want_legacy_error_depth = 3,
404 		.failing = 1,
405 	},
406 	{
407 		.id = "7b",
408 		.want_chains = 1,
409 	},
410 	{
411 		.id = "8a",
412 		.want_chains = 0,
413 		.want_error = X509_V_ERR_CERT_HAS_EXPIRED,
414 		.want_error_depth = 0,
415 		.want_legacy_error = X509_V_ERR_CERT_HAS_EXPIRED,
416 		.want_legacy_error_depth = 0,
417 	},
418 	{
419 		.id = "9a",
420 		.want_chains = 0,
421 		.want_error = X509_V_ERR_CERT_HAS_EXPIRED,
422 		.want_error_depth = 1,
423 		.want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
424 		.want_legacy_error_depth = 0,
425 	},
426 	{
427 		.id = "10a",
428 		.want_chains = 1,
429 		.want_error = X509_V_ERR_CERT_HAS_EXPIRED,
430 		.want_error_depth = 0,
431 	},
432 	{
433 		.id = "10b",
434 		.want_chains = 1,
435 	},
436 	{
437 		.id = "11a",
438 		.want_chains = 1,
439 		.want_error = X509_V_ERR_CERT_HAS_EXPIRED,
440 		.want_error_depth = 0,
441 		.want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
442 		.want_legacy_error_depth = 1,
443 		.failing = 1,
444 	},
445 	{
446 		.id = "11b",
447 		.want_chains = 1,
448 	},
449 	{
450 		.id = "12a",
451 		.want_chains = 1,
452 	},
453 	{
454 		.id = "13a",
455 		.want_chains = 1,
456 		.want_error = X509_V_ERR_CERT_HAS_EXPIRED,
457 		.want_error_depth = 0,
458 		.want_legacy_error = X509_V_ERR_CERT_HAS_EXPIRED,
459 		.want_legacy_error_depth = 2,
460 		.failing = 1,
461 	},
462 };
463 
464 #define N_VERIFY_CERT_TESTS \
465     (sizeof(verify_cert_tests) / sizeof(*verify_cert_tests))
466 
467 static int
468 verify_cert_test(const char *certs_path, int mode)
469 {
470 	char *roots_file, *bundle_file, *roots_dir;
471 	struct verify_cert_test *vct;
472 	int chains, error, error_depth;
473 	int failed = 0;
474 	size_t i;
475 
476 	for (i = 0; i < N_VERIFY_CERT_TESTS; i++) {
477 		vct = &verify_cert_tests[i];
478 
479 		if (asprintf(&roots_file, "%s/%s/roots.pem", certs_path,
480 		    vct->id) == -1)
481 			errx(1, "asprintf");
482 		if (asprintf(&bundle_file, "%s/%s/bundle.pem", certs_path,
483 		    vct->id) == -1)
484 			errx(1, "asprintf");
485 		if (asprintf(&roots_dir, "./%s/roots", vct->id) == -1)
486 			errx(1, "asprintf");
487 
488 		error = 0;
489 		error_depth = 0;
490 
491 		fprintf(stderr, "== Test %zu (%s)\n", i, vct->id);
492 		if (mode == MODE_VERIFY)
493 			verify_cert_new(roots_file, bundle_file, &chains);
494 		else
495 			verify_cert(roots_dir, roots_file, bundle_file, &chains,
496 			    &error, &error_depth, mode);
497 
498 		if ((mode == MODE_VERIFY && chains == vct->want_chains) ||
499 		    (chains == 0 && vct->want_chains == 0) ||
500 		    (chains == 1 && vct->want_chains > 0)) {
501 			fprintf(stderr, "INFO: Succeeded with %d chains%s\n",
502 			    chains, vct->failing ? " (legacy failure)" : "");
503 			if (mode == MODE_LEGACY_VFY && vct->failing)
504 				failed |= 1;
505 		} else {
506 			fprintf(stderr, "FAIL: Failed with %d chains%s\n",
507 			    chains, vct->failing ? " (legacy failure)" : "");
508 			if (!vct->failing)
509 				failed |= 1;
510 		}
511 
512 		if (mode == MODE_LEGACY_VFY) {
513 			if (error != vct->want_legacy_error) {
514 				fprintf(stderr, "FAIL: Got legacy error %d, "
515 				    "want %d\n", error, vct->want_legacy_error);
516 				failed |= 1;
517 			}
518 			if (error_depth != vct->want_legacy_error_depth) {
519 				fprintf(stderr, "FAIL: Got legacy error depth "
520 				    "%d, want %d\n", error_depth,
521 				    vct->want_legacy_error_depth);
522 				failed |= 1;
523 			}
524 		} else if (mode == MODE_MODERN_VFY || mode == MODE_MODERN_VFY_DIR) {
525 			if (error != vct->want_error) {
526 				fprintf(stderr, "FAIL: Got error %d, want %d\n",
527 				    error, vct->want_error);
528 				failed |= 1;
529 			}
530 			if (error_depth != vct->want_error_depth) {
531 				fprintf(stderr, "FAIL: Got error depth %d, want"
532 				    " %d\n", error_depth, vct->want_error_depth);
533 				failed |= 1;
534 			}
535 		}
536 
537 		fprintf(stderr, "\n");
538 
539 		free(roots_file);
540 		free(bundle_file);
541 		free(roots_dir);
542 	}
543 
544 	return failed;
545 }
546 
547 int
548 main(int argc, char **argv)
549 {
550 	int failed = 0;
551 
552 	if (argc != 2) {
553 		fprintf(stderr, "usage: %s <certs_path>\n", argv[0]);
554 		exit(1);
555 	}
556 
557 	fprintf(stderr, "\n\nTesting legacy x509_vfy\n");
558 	failed |= verify_cert_test(argv[1], MODE_LEGACY_VFY);
559 	fprintf(stderr, "\n\nTesting modern x509_vfy\n");
560 	failed |= verify_cert_test(argv[1], MODE_MODERN_VFY);
561 	fprintf(stderr, "\n\nTesting modern x509_vfy by_dir\n");
562 	failed |= verify_cert_test(argv[1], MODE_MODERN_VFY_DIR);
563 	fprintf(stderr, "\n\nTesting x509_verify\n");
564 	failed |= verify_cert_test(argv[1], MODE_VERIFY);
565 
566 	return (failed);
567 }
568