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