xref: /openbsd/regress/lib/libcrypto/x509/verify.c (revision 6822f9c8)
1*6822f9c8Santon /* $OpenBSD: verify.c,v 1.12 2024/08/23 12:56:26 anton Exp $ */
2fbd64e3cSjsing /*
3fbd64e3cSjsing  * Copyright (c) 2020 Joel Sing <jsing@openbsd.org>
4b68b63e3Sbeck  * Copyright (c) 2020-2021 Bob Beck <beck@openbsd.org>
5fbd64e3cSjsing  *
6fbd64e3cSjsing  * Permission to use, copy, modify, and distribute this software for any
7fbd64e3cSjsing  * purpose with or without fee is hereby granted, provided that the above
8fbd64e3cSjsing  * copyright notice and this permission notice appear in all copies.
9fbd64e3cSjsing  *
10fbd64e3cSjsing  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11fbd64e3cSjsing  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12fbd64e3cSjsing  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13fbd64e3cSjsing  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14fbd64e3cSjsing  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15fbd64e3cSjsing  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16fbd64e3cSjsing  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17fbd64e3cSjsing  */
18fbd64e3cSjsing 
19fbd64e3cSjsing #include <err.h>
20fbd64e3cSjsing #include <string.h>
21fbd64e3cSjsing 
22fbd64e3cSjsing #include <openssl/bio.h>
237cbb8da0Sjsing #include <openssl/crypto.h>
24fbd64e3cSjsing #include <openssl/err.h>
25fbd64e3cSjsing #include <openssl/pem.h>
26fbd64e3cSjsing #include <openssl/x509.h>
27fbd64e3cSjsing #include <openssl/x509v3.h>
28f45080b3Stb 
29f45080b3Stb #include "x509_verify.h"
3023258cfeSbeck 
3123258cfeSbeck #define MODE_MODERN_VFY		0
32b68b63e3Sbeck #define MODE_MODERN_VFY_DIR	1
33b68b63e3Sbeck #define MODE_LEGACY_VFY		2
34b68b63e3Sbeck #define MODE_VERIFY		3
35fbd64e3cSjsing 
36fbd64e3cSjsing static int verbose = 1;
37fbd64e3cSjsing 
38fbd64e3cSjsing static int
passwd_cb(char * buf,int size,int rwflag,void * u)39fbd64e3cSjsing passwd_cb(char *buf, int size, int rwflag, void *u)
40fbd64e3cSjsing {
41fbd64e3cSjsing 	memset(buf, 0, size);
42fbd64e3cSjsing 	return (0);
43fbd64e3cSjsing }
44fbd64e3cSjsing 
45fbd64e3cSjsing static int
certs_from_file(const char * filename,STACK_OF (X509)** certs)46fbd64e3cSjsing certs_from_file(const char *filename, STACK_OF(X509) **certs)
47fbd64e3cSjsing {
48fbd64e3cSjsing 	STACK_OF(X509_INFO) *xis = NULL;
49fbd64e3cSjsing 	STACK_OF(X509) *xs = NULL;
50fbd64e3cSjsing 	BIO *bio = NULL;
51fbd64e3cSjsing 	X509 *x;
52fbd64e3cSjsing 	int i;
53fbd64e3cSjsing 
54fbd64e3cSjsing 	if ((xs = sk_X509_new_null()) == NULL)
55fbd64e3cSjsing 		errx(1, "failed to create X509 stack");
56fbd64e3cSjsing 	if ((bio = BIO_new_file(filename, "r")) == NULL) {
57fbd64e3cSjsing 		ERR_print_errors_fp(stderr);
58fbd64e3cSjsing 		errx(1, "failed to create bio");
59fbd64e3cSjsing 	}
60fbd64e3cSjsing 	if ((xis = PEM_X509_INFO_read_bio(bio, NULL, passwd_cb, NULL)) == NULL)
61fbd64e3cSjsing 		errx(1, "failed to read PEM");
62fbd64e3cSjsing 
63fbd64e3cSjsing 	for (i = 0; i < sk_X509_INFO_num(xis); i++) {
64fbd64e3cSjsing 		if ((x = sk_X509_INFO_value(xis, i)->x509) == NULL)
65fbd64e3cSjsing 			continue;
66fbd64e3cSjsing 		if (!sk_X509_push(xs, x))
67fbd64e3cSjsing 			errx(1, "failed to push X509");
68fbd64e3cSjsing 		X509_up_ref(x);
69fbd64e3cSjsing 	}
70fbd64e3cSjsing 
71fbd64e3cSjsing 	*certs = xs;
72fbd64e3cSjsing 	xs = NULL;
73fbd64e3cSjsing 
74fbd64e3cSjsing 	sk_X509_INFO_pop_free(xis, X509_INFO_free);
75fbd64e3cSjsing 	sk_X509_pop_free(xs, X509_free);
76fbd64e3cSjsing 	BIO_free(bio);
77fbd64e3cSjsing 
78fbd64e3cSjsing 	return 1;
79fbd64e3cSjsing }
80fbd64e3cSjsing 
81fbd64e3cSjsing static int
verify_cert_cb(int ok,X509_STORE_CTX * xsc)82fbd64e3cSjsing verify_cert_cb(int ok, X509_STORE_CTX *xsc)
83fbd64e3cSjsing {
84fbd64e3cSjsing 	X509 *current_cert;
85fbd64e3cSjsing 	int verify_err;
86fbd64e3cSjsing 
87fbd64e3cSjsing 	current_cert = X509_STORE_CTX_get_current_cert(xsc);
88fbd64e3cSjsing 	if (current_cert != NULL) {
89fbd64e3cSjsing 		X509_NAME_print_ex_fp(stderr,
90fbd64e3cSjsing 		    X509_get_subject_name(current_cert), 0,
91fbd64e3cSjsing 		    XN_FLAG_ONELINE);
92fbd64e3cSjsing 		fprintf(stderr, "\n");
93fbd64e3cSjsing 	}
94fbd64e3cSjsing 
95fbd64e3cSjsing 	verify_err = X509_STORE_CTX_get_error(xsc);
96fbd64e3cSjsing 	if (verify_err != X509_V_OK) {
97fbd64e3cSjsing 		fprintf(stderr, "verify error at depth %d: %s\n",
98fbd64e3cSjsing 		    X509_STORE_CTX_get_error_depth(xsc),
99fbd64e3cSjsing 		    X509_verify_cert_error_string(verify_err));
100fbd64e3cSjsing 	}
101fbd64e3cSjsing 
102fbd64e3cSjsing 	return ok;
103fbd64e3cSjsing }
104fbd64e3cSjsing 
105fbd64e3cSjsing static void
verify_cert(const char * roots_dir,const char * roots_file,const char * bundle_file,int * chains,int * error,int * error_depth,int mode)106b68b63e3Sbeck verify_cert(const char *roots_dir, const char *roots_file,
1077cbb8da0Sjsing     const char *bundle_file, int *chains, int *error, int *error_depth,
1087cbb8da0Sjsing     int mode)
109fbd64e3cSjsing {
110fbd64e3cSjsing 	STACK_OF(X509) *roots = NULL, *bundle = NULL;
111fbd64e3cSjsing 	X509_STORE_CTX *xsc = NULL;
112b68b63e3Sbeck 	X509_STORE *store = NULL;
113fbd64e3cSjsing 	X509 *leaf = NULL;
1147cbb8da0Sjsing 	int use_dir;
1157cbb8da0Sjsing 	int ret;
116fbd64e3cSjsing 
117fbd64e3cSjsing 	*chains = 0;
1187cbb8da0Sjsing 	*error = 0;
1197cbb8da0Sjsing 	*error_depth = 0;
1207cbb8da0Sjsing 
121b68b63e3Sbeck 	use_dir = (mode == MODE_MODERN_VFY_DIR);
122fbd64e3cSjsing 
123b68b63e3Sbeck 	if (!use_dir && !certs_from_file(roots_file, &roots))
124fbd64e3cSjsing 		errx(1, "failed to load roots from '%s'", roots_file);
125fbd64e3cSjsing 	if (!certs_from_file(bundle_file, &bundle))
126fbd64e3cSjsing 		errx(1, "failed to load bundle from '%s'", bundle_file);
127fbd64e3cSjsing 	if (sk_X509_num(bundle) < 1)
128fbd64e3cSjsing 		errx(1, "not enough certs in bundle");
129fbd64e3cSjsing 	leaf = sk_X509_shift(bundle);
130fbd64e3cSjsing 
131fbd64e3cSjsing 	if ((xsc = X509_STORE_CTX_new()) == NULL)
132fbd64e3cSjsing 		errx(1, "X509_STORE_CTX");
133b68b63e3Sbeck 	if (use_dir && (store = X509_STORE_new()) == NULL)
134b68b63e3Sbeck 		errx(1, "X509_STORE");
135b68b63e3Sbeck 	if (!X509_STORE_CTX_init(xsc, store, leaf, bundle)) {
136fbd64e3cSjsing 		ERR_print_errors_fp(stderr);
137fbd64e3cSjsing 		errx(1, "failed to init store context");
138fbd64e3cSjsing 	}
139b68b63e3Sbeck 	if (use_dir) {
140b68b63e3Sbeck 		if (!X509_STORE_load_locations(store, NULL, roots_dir))
141b68b63e3Sbeck 			errx(1, "failed to set by_dir directory of %s", roots_dir);
142b68b63e3Sbeck 	}
143920585faStb 	if (mode == MODE_LEGACY_VFY)
144920585faStb 		X509_STORE_CTX_set_flags(xsc, X509_V_FLAG_LEGACY_VERIFY);
145920585faStb 	else
146920585faStb 		X509_VERIFY_PARAM_clear_flags(X509_STORE_CTX_get0_param(xsc),
147920585faStb 		    X509_V_FLAG_LEGACY_VERIFY);
14823258cfeSbeck 
149fbd64e3cSjsing 	if (verbose)
150fbd64e3cSjsing 		X509_STORE_CTX_set_verify_cb(xsc, verify_cert_cb);
151b68b63e3Sbeck 	if (!use_dir)
152fbd64e3cSjsing 		X509_STORE_CTX_set0_trusted_stack(xsc, roots);
1537cbb8da0Sjsing 
1547cbb8da0Sjsing 	ret = X509_verify_cert(xsc);
1557cbb8da0Sjsing 
1567cbb8da0Sjsing 	*error = X509_STORE_CTX_get_error(xsc);
1577cbb8da0Sjsing 	*error_depth = X509_STORE_CTX_get_error_depth(xsc);
1587cbb8da0Sjsing 
1597cbb8da0Sjsing 	if (ret == 1) {
160fbd64e3cSjsing 		*chains = 1; /* XXX */
161fbd64e3cSjsing 		goto done;
162fbd64e3cSjsing 	}
163fbd64e3cSjsing 
1647cbb8da0Sjsing 	if (*error == 0)
165*6822f9c8Santon 		errx(1, "Error unset on failure!");
16628dcb2c6Sbeck 
167fbd64e3cSjsing 	fprintf(stderr, "failed to verify at %d: %s\n",
1687cbb8da0Sjsing 	    *error_depth, X509_verify_cert_error_string(*error));
169fbd64e3cSjsing 
170fbd64e3cSjsing  done:
171fbd64e3cSjsing 	sk_X509_pop_free(roots, X509_free);
172fbd64e3cSjsing 	sk_X509_pop_free(bundle, X509_free);
173b68b63e3Sbeck 	X509_STORE_free(store);
174fbd64e3cSjsing 	X509_STORE_CTX_free(xsc);
175fbd64e3cSjsing 	X509_free(leaf);
176fbd64e3cSjsing }
177fbd64e3cSjsing 
17823258cfeSbeck static void
verify_cert_new(const char * roots_file,const char * bundle_file,int * chains)17923258cfeSbeck verify_cert_new(const char *roots_file, const char *bundle_file, int *chains)
18023258cfeSbeck {
18123258cfeSbeck 	STACK_OF(X509) *roots = NULL, *bundle = NULL;
18223258cfeSbeck 	X509_STORE_CTX *xsc = NULL;
18323258cfeSbeck 	X509 *leaf = NULL;
18423258cfeSbeck 	struct x509_verify_ctx *ctx;
18523258cfeSbeck 
18623258cfeSbeck 	*chains = 0;
18723258cfeSbeck 
18823258cfeSbeck 	if (!certs_from_file(roots_file, &roots))
18923258cfeSbeck 		errx(1, "failed to load roots from '%s'", roots_file);
19023258cfeSbeck 	if (!certs_from_file(bundle_file, &bundle))
19123258cfeSbeck 		errx(1, "failed to load bundle from '%s'", bundle_file);
19223258cfeSbeck 	if (sk_X509_num(bundle) < 1)
19323258cfeSbeck 		errx(1, "not enough certs in bundle");
19423258cfeSbeck 	leaf = sk_X509_shift(bundle);
19523258cfeSbeck 
19623258cfeSbeck         if ((xsc = X509_STORE_CTX_new()) == NULL)
19723258cfeSbeck 		errx(1, "X509_STORE_CTX");
19823258cfeSbeck 	if (!X509_STORE_CTX_init(xsc, NULL, leaf, bundle)) {
19923258cfeSbeck 		ERR_print_errors_fp(stderr);
20023258cfeSbeck 		errx(1, "failed to init store context");
20123258cfeSbeck 	}
20223258cfeSbeck 	if (verbose)
20323258cfeSbeck 		X509_STORE_CTX_set_verify_cb(xsc, verify_cert_cb);
20423258cfeSbeck 
20523258cfeSbeck 	if ((ctx = x509_verify_ctx_new(roots)) == NULL)
20623258cfeSbeck 		errx(1, "failed to create ctx");
20723258cfeSbeck 	if (!x509_verify_ctx_set_intermediates(ctx, bundle))
20823258cfeSbeck 		errx(1, "failed to set intermediates");
20923258cfeSbeck 
21023258cfeSbeck 	if ((*chains = x509_verify(ctx, leaf, NULL)) == 0) {
21123258cfeSbeck 		fprintf(stderr, "failed to verify at %lu: %s\n",
21223258cfeSbeck 		    x509_verify_ctx_error_depth(ctx),
21323258cfeSbeck 		    x509_verify_ctx_error_string(ctx));
21423258cfeSbeck 	} else {
2154820661fStb 		int c;
2164820661fStb 
2174820661fStb 		for (c = 0; verbose && c < *chains; c++) {
2184820661fStb 			STACK_OF(X509) *chain;
2194820661fStb 			int i;
2204820661fStb 
22123258cfeSbeck 			fprintf(stderr, "Chain %d\n--------\n", c);
2224820661fStb 			chain = x509_verify_ctx_chain(ctx, c);
2234820661fStb 			for (i = 0; i < sk_X509_num(chain); i++) {
22423258cfeSbeck 				X509 *cert = sk_X509_value(chain, i);
22523258cfeSbeck 				X509_NAME_print_ex_fp(stderr,
22623258cfeSbeck 				    X509_get_subject_name(cert), 0,
22723258cfeSbeck 				    XN_FLAG_ONELINE);
22823258cfeSbeck 				fprintf(stderr, "\n");
22923258cfeSbeck 			}
23023258cfeSbeck 		}
23123258cfeSbeck 	}
23223258cfeSbeck 	sk_X509_pop_free(roots, X509_free);
23323258cfeSbeck 	sk_X509_pop_free(bundle, X509_free);
23423258cfeSbeck 	X509_free(leaf);
235047399e3Stb 	X509_STORE_CTX_free(xsc);
236047399e3Stb 	x509_verify_ctx_free(ctx);
23723258cfeSbeck }
23823258cfeSbeck 
2397cbb8da0Sjsing struct verify_cert_test {
2407cbb8da0Sjsing 	const char *id;
2417cbb8da0Sjsing 	int want_chains;
2427cbb8da0Sjsing 	int want_error;
2437cbb8da0Sjsing 	int want_error_depth;
2447cbb8da0Sjsing 	int want_legacy_error;
2457cbb8da0Sjsing 	int want_legacy_error_depth;
2467cbb8da0Sjsing 	int failing;
2477cbb8da0Sjsing };
2487cbb8da0Sjsing 
249fbd64e3cSjsing struct verify_cert_test verify_cert_tests[] = {
250fbd64e3cSjsing 	{
251fbd64e3cSjsing 		.id = "1a",
252fbd64e3cSjsing 		.want_chains = 1,
253fbd64e3cSjsing 	},
254fbd64e3cSjsing 	{
255fbd64e3cSjsing 		.id = "2a",
256fbd64e3cSjsing 		.want_chains = 1,
257fbd64e3cSjsing 	},
258fbd64e3cSjsing 	{
259fbd64e3cSjsing 		.id = "2b",
260fbd64e3cSjsing 		.want_chains = 0,
2617cbb8da0Sjsing 		.want_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
2627cbb8da0Sjsing 		.want_error_depth = 0,
2637cbb8da0Sjsing 		.want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
2647cbb8da0Sjsing 		.want_legacy_error_depth = 0,
265fbd64e3cSjsing 	},
266fbd64e3cSjsing 	{
2676f4c0c98Sbeck 		.id = "2c",
2686f4c0c98Sbeck 		.want_chains = 1,
2696f4c0c98Sbeck 	},
2706f4c0c98Sbeck 	{
271fbd64e3cSjsing 		.id = "3a",
272fbd64e3cSjsing 		.want_chains = 1,
273fbd64e3cSjsing 	},
274fbd64e3cSjsing 	{
275fbd64e3cSjsing 		.id = "3b",
276fbd64e3cSjsing 		.want_chains = 0,
2777cbb8da0Sjsing 		.want_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
2787cbb8da0Sjsing 		.want_error_depth = 2,
2797cbb8da0Sjsing 		.want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
2807cbb8da0Sjsing 		.want_legacy_error_depth = 2,
281fbd64e3cSjsing 	},
282fbd64e3cSjsing 	{
283fbd64e3cSjsing 		.id = "3c",
284fbd64e3cSjsing 		.want_chains = 0,
2857cbb8da0Sjsing 		.want_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
2867cbb8da0Sjsing 		.want_error_depth = 1,
2877cbb8da0Sjsing 		.want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
2887cbb8da0Sjsing 		.want_legacy_error_depth = 1,
289fbd64e3cSjsing 	},
290fbd64e3cSjsing 	{
291fbd64e3cSjsing 		.id = "3d",
292fbd64e3cSjsing 		.want_chains = 0,
2937cbb8da0Sjsing 		.want_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
2947cbb8da0Sjsing 		.want_error_depth = 0,
2957cbb8da0Sjsing 		.want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
2967cbb8da0Sjsing 		.want_legacy_error_depth = 0,
297fbd64e3cSjsing 	},
298fbd64e3cSjsing 	{
299fbd64e3cSjsing 		.id = "3e",
300fbd64e3cSjsing 		.want_chains = 1,
301fbd64e3cSjsing 	},
302fbd64e3cSjsing 	{
303fbd64e3cSjsing 		.id = "4a",
304fbd64e3cSjsing 		.want_chains = 2,
305fbd64e3cSjsing 	},
306fbd64e3cSjsing 	{
307fbd64e3cSjsing 		.id = "4b",
308fbd64e3cSjsing 		.want_chains = 1,
309fbd64e3cSjsing 	},
310fbd64e3cSjsing 	{
311fbd64e3cSjsing 		.id = "4c",
312fbd64e3cSjsing 		.want_chains = 1,
3137cbb8da0Sjsing 		.want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
3147cbb8da0Sjsing 		.want_legacy_error_depth = 1,
315fbd64e3cSjsing 		.failing = 1,
316fbd64e3cSjsing 	},
317fbd64e3cSjsing 	{
318fbd64e3cSjsing 		.id = "4d",
319fbd64e3cSjsing 		.want_chains = 1,
320fbd64e3cSjsing 	},
321fbd64e3cSjsing 	{
322fbd64e3cSjsing 		.id = "4e",
323fbd64e3cSjsing 		.want_chains = 1,
324fbd64e3cSjsing 	},
325fbd64e3cSjsing 	{
326fbd64e3cSjsing 		.id = "4f",
327fbd64e3cSjsing 		.want_chains = 2,
328fbd64e3cSjsing 	},
329fbd64e3cSjsing 	{
330fbd64e3cSjsing 		.id = "4g",
331fbd64e3cSjsing 		.want_chains = 1,
3327cbb8da0Sjsing 		.want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
3337cbb8da0Sjsing 		.want_legacy_error_depth = 1,
334fbd64e3cSjsing 		.failing = 1,
335fbd64e3cSjsing 	},
336fbd64e3cSjsing 	{
337fbd64e3cSjsing 		.id = "4h",
338fbd64e3cSjsing 		.want_chains = 1,
339fbd64e3cSjsing 	},
340fbd64e3cSjsing 	{
341fbd64e3cSjsing 		.id = "5a",
342fbd64e3cSjsing 		.want_chains = 2,
343fbd64e3cSjsing 	},
344fbd64e3cSjsing 	{
345fbd64e3cSjsing 		.id = "5b",
346fbd64e3cSjsing 		.want_chains = 1,
3477cbb8da0Sjsing 		.want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
3487cbb8da0Sjsing 		.want_legacy_error_depth = 2,
349fbd64e3cSjsing 		.failing = 1,
350fbd64e3cSjsing 	},
351fbd64e3cSjsing 	{
352fbd64e3cSjsing 		.id = "5c",
353fbd64e3cSjsing 		.want_chains = 1,
354fbd64e3cSjsing 	},
355fbd64e3cSjsing 	{
356fbd64e3cSjsing 		.id = "5d",
357fbd64e3cSjsing 		.want_chains = 1,
358fbd64e3cSjsing 	},
359fbd64e3cSjsing 	{
360fbd64e3cSjsing 		.id = "5e",
361fbd64e3cSjsing 		.want_chains = 1,
3627cbb8da0Sjsing 		.want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
3637cbb8da0Sjsing 		.want_legacy_error_depth = 1,
364fbd64e3cSjsing 		.failing = 1,
365fbd64e3cSjsing 	},
366fbd64e3cSjsing 	{
367fbd64e3cSjsing 		.id = "5f",
368fbd64e3cSjsing 		.want_chains = 1,
369fbd64e3cSjsing 	},
370fbd64e3cSjsing 	{
371fbd64e3cSjsing 		.id = "5g",
372fbd64e3cSjsing 		.want_chains = 2,
373fbd64e3cSjsing 	},
374fbd64e3cSjsing 	{
375fbd64e3cSjsing 		.id = "5h",
376fbd64e3cSjsing 		.want_chains = 1,
377fbd64e3cSjsing 	},
378fbd64e3cSjsing 	{
379fbd64e3cSjsing 		.id = "5i",
380fbd64e3cSjsing 		.want_chains = 1,
3817cbb8da0Sjsing 		.want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
3827cbb8da0Sjsing 		.want_legacy_error_depth = 1,
383fbd64e3cSjsing 		.failing = 1,
384fbd64e3cSjsing 	},
385fbd64e3cSjsing 	{
386fbd64e3cSjsing 		.id = "6a",
387fbd64e3cSjsing 		.want_chains = 1,
388fbd64e3cSjsing 	},
389fbd64e3cSjsing 	{
390fbd64e3cSjsing 		.id = "6b",
391fbd64e3cSjsing 		.want_chains = 1,
3927cbb8da0Sjsing 		.want_error = X509_V_ERR_CERT_HAS_EXPIRED,
3937cbb8da0Sjsing 		.want_error_depth = 0,
3947cbb8da0Sjsing 		.want_legacy_error = X509_V_ERR_CERT_HAS_EXPIRED,
3957cbb8da0Sjsing 		.want_legacy_error_depth = 2,
396fbd64e3cSjsing 		.failing = 1,
397fbd64e3cSjsing 	},
398fbd64e3cSjsing 	{
399fbd64e3cSjsing 		.id = "7a",
400fbd64e3cSjsing 		.want_chains = 1,
4017cbb8da0Sjsing 		.want_error = X509_V_ERR_CERT_HAS_EXPIRED,
4027cbb8da0Sjsing 		.want_error_depth = 0,
4037cbb8da0Sjsing 		.want_legacy_error = X509_V_ERR_CERT_HAS_EXPIRED,
4047cbb8da0Sjsing 		.want_legacy_error_depth = 3,
405fbd64e3cSjsing 		.failing = 1,
406fbd64e3cSjsing 	},
407fbd64e3cSjsing 	{
408fbd64e3cSjsing 		.id = "7b",
409fbd64e3cSjsing 		.want_chains = 1,
410fbd64e3cSjsing 	},
411fbd64e3cSjsing 	{
412fbd64e3cSjsing 		.id = "8a",
413fbd64e3cSjsing 		.want_chains = 0,
4147cbb8da0Sjsing 		.want_error = X509_V_ERR_CERT_HAS_EXPIRED,
4157cbb8da0Sjsing 		.want_error_depth = 0,
4167cbb8da0Sjsing 		.want_legacy_error = X509_V_ERR_CERT_HAS_EXPIRED,
4177cbb8da0Sjsing 		.want_legacy_error_depth = 0,
418fbd64e3cSjsing 	},
419fbd64e3cSjsing 	{
420fbd64e3cSjsing 		.id = "9a",
421fbd64e3cSjsing 		.want_chains = 0,
4227cbb8da0Sjsing 		.want_error = X509_V_ERR_CERT_HAS_EXPIRED,
4237cbb8da0Sjsing 		.want_error_depth = 1,
4247cbb8da0Sjsing 		.want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
4257cbb8da0Sjsing 		.want_legacy_error_depth = 0,
426fbd64e3cSjsing 	},
427fbd64e3cSjsing 	{
428fbd64e3cSjsing 		.id = "10a",
429fbd64e3cSjsing 		.want_chains = 1,
4307cbb8da0Sjsing 		.want_error = X509_V_ERR_CERT_HAS_EXPIRED,
4317cbb8da0Sjsing 		.want_error_depth = 0,
432fbd64e3cSjsing 	},
433fbd64e3cSjsing 	{
434fbd64e3cSjsing 		.id = "10b",
435fbd64e3cSjsing 		.want_chains = 1,
436fbd64e3cSjsing 	},
437fbd64e3cSjsing 	{
438fbd64e3cSjsing 		.id = "11a",
439fbd64e3cSjsing 		.want_chains = 1,
4407cbb8da0Sjsing 		.want_error = X509_V_ERR_CERT_HAS_EXPIRED,
4417cbb8da0Sjsing 		.want_error_depth = 0,
4427cbb8da0Sjsing 		.want_legacy_error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
4437cbb8da0Sjsing 		.want_legacy_error_depth = 1,
444fbd64e3cSjsing 		.failing = 1,
445fbd64e3cSjsing 	},
446fbd64e3cSjsing 	{
447fbd64e3cSjsing 		.id = "11b",
448fbd64e3cSjsing 		.want_chains = 1,
449fbd64e3cSjsing 	},
450fbd64e3cSjsing 	{
451fbd64e3cSjsing 		.id = "12a",
452fbd64e3cSjsing 		.want_chains = 1,
453fbd64e3cSjsing 	},
454fbd64e3cSjsing 	{
455fbd64e3cSjsing 		.id = "13a",
456fbd64e3cSjsing 		.want_chains = 1,
4577cbb8da0Sjsing 		.want_error = X509_V_ERR_CERT_HAS_EXPIRED,
4587cbb8da0Sjsing 		.want_error_depth = 0,
4597cbb8da0Sjsing 		.want_legacy_error = X509_V_ERR_CERT_HAS_EXPIRED,
4607cbb8da0Sjsing 		.want_legacy_error_depth = 2,
46107499e19Sjsing 		.failing = 1,
462fbd64e3cSjsing 	},
463fbd64e3cSjsing };
464fbd64e3cSjsing 
465fbd64e3cSjsing #define N_VERIFY_CERT_TESTS \
466fbd64e3cSjsing     (sizeof(verify_cert_tests) / sizeof(*verify_cert_tests))
467fbd64e3cSjsing 
468fbd64e3cSjsing static int
verify_cert_test(const char * certs_path,int mode)46923258cfeSbeck verify_cert_test(const char *certs_path, int mode)
470fbd64e3cSjsing {
471b68b63e3Sbeck 	char *roots_file, *bundle_file, *roots_dir;
472fbd64e3cSjsing 	struct verify_cert_test *vct;
4737cbb8da0Sjsing 	int chains, error, error_depth;
474fbd64e3cSjsing 	int failed = 0;
475fbd64e3cSjsing 	size_t i;
476fbd64e3cSjsing 
477fbd64e3cSjsing 	for (i = 0; i < N_VERIFY_CERT_TESTS; i++) {
478fbd64e3cSjsing 		vct = &verify_cert_tests[i];
479fbd64e3cSjsing 
480fbd64e3cSjsing 		if (asprintf(&roots_file, "%s/%s/roots.pem", certs_path,
481fbd64e3cSjsing 		    vct->id) == -1)
482fbd64e3cSjsing 			errx(1, "asprintf");
483fbd64e3cSjsing 		if (asprintf(&bundle_file, "%s/%s/bundle.pem", certs_path,
484fbd64e3cSjsing 		    vct->id) == -1)
485fbd64e3cSjsing 			errx(1, "asprintf");
486b68b63e3Sbeck 		if (asprintf(&roots_dir, "./%s/roots", vct->id) == -1)
487b68b63e3Sbeck 			errx(1, "asprintf");
488fbd64e3cSjsing 
4897cbb8da0Sjsing 		error = 0;
4907cbb8da0Sjsing 		error_depth = 0;
4917cbb8da0Sjsing 
492fbd64e3cSjsing 		fprintf(stderr, "== Test %zu (%s)\n", i, vct->id);
49323258cfeSbeck 		if (mode == MODE_VERIFY)
49423258cfeSbeck 			verify_cert_new(roots_file, bundle_file, &chains);
49523258cfeSbeck 		else
4967cbb8da0Sjsing 			verify_cert(roots_dir, roots_file, bundle_file, &chains,
4977cbb8da0Sjsing 			    &error, &error_depth, mode);
4987cbb8da0Sjsing 
499b68b63e3Sbeck 		if ((mode == MODE_VERIFY && chains == vct->want_chains) ||
50023258cfeSbeck 		    (chains == 0 && vct->want_chains == 0) ||
501fbd64e3cSjsing 		    (chains == 1 && vct->want_chains > 0)) {
502fbd64e3cSjsing 			fprintf(stderr, "INFO: Succeeded with %d chains%s\n",
50323258cfeSbeck 			    chains, vct->failing ? " (legacy failure)" : "");
50423258cfeSbeck 			if (mode == MODE_LEGACY_VFY && vct->failing)
505fbd64e3cSjsing 				failed |= 1;
506fbd64e3cSjsing 		} else {
507fbd64e3cSjsing 			fprintf(stderr, "FAIL: Failed with %d chains%s\n",
50823258cfeSbeck 			    chains, vct->failing ? " (legacy failure)" : "");
509fbd64e3cSjsing 			if (!vct->failing)
510fbd64e3cSjsing 				failed |= 1;
511fbd64e3cSjsing 		}
5127cbb8da0Sjsing 
5137cbb8da0Sjsing 		if (mode == MODE_LEGACY_VFY) {
5147cbb8da0Sjsing 			if (error != vct->want_legacy_error) {
5157cbb8da0Sjsing 				fprintf(stderr, "FAIL: Got legacy error %d, "
5167cbb8da0Sjsing 				    "want %d\n", error, vct->want_legacy_error);
5177cbb8da0Sjsing 				failed |= 1;
5187cbb8da0Sjsing 			}
5197cbb8da0Sjsing 			if (error_depth != vct->want_legacy_error_depth) {
5207cbb8da0Sjsing 				fprintf(stderr, "FAIL: Got legacy error depth "
5217cbb8da0Sjsing 				    "%d, want %d\n", error_depth,
5227cbb8da0Sjsing 				    vct->want_legacy_error_depth);
5237cbb8da0Sjsing 				failed |= 1;
5247cbb8da0Sjsing 			}
5257cbb8da0Sjsing 		} else if (mode == MODE_MODERN_VFY || mode == MODE_MODERN_VFY_DIR) {
5267cbb8da0Sjsing 			if (error != vct->want_error) {
5277cbb8da0Sjsing 				fprintf(stderr, "FAIL: Got error %d, want %d\n",
5287cbb8da0Sjsing 				    error, vct->want_error);
5297cbb8da0Sjsing 				failed |= 1;
5307cbb8da0Sjsing 			}
5317cbb8da0Sjsing 			if (error_depth != vct->want_error_depth) {
5327cbb8da0Sjsing 				fprintf(stderr, "FAIL: Got error depth %d, want"
5337cbb8da0Sjsing 				    " %d\n", error_depth, vct->want_error_depth);
5347cbb8da0Sjsing 				failed |= 1;
5357cbb8da0Sjsing 			}
5367cbb8da0Sjsing 		}
5377cbb8da0Sjsing 
538fbd64e3cSjsing 		fprintf(stderr, "\n");
539fbd64e3cSjsing 
540fbd64e3cSjsing 		free(roots_file);
541fbd64e3cSjsing 		free(bundle_file);
542b68b63e3Sbeck 		free(roots_dir);
543fbd64e3cSjsing 	}
544fbd64e3cSjsing 
545fbd64e3cSjsing 	return failed;
546fbd64e3cSjsing }
547fbd64e3cSjsing 
548fbd64e3cSjsing int
main(int argc,char ** argv)549fbd64e3cSjsing main(int argc, char **argv)
550fbd64e3cSjsing {
551fbd64e3cSjsing 	int failed = 0;
552fbd64e3cSjsing 
553fbd64e3cSjsing 	if (argc != 2) {
554fbd64e3cSjsing 		fprintf(stderr, "usage: %s <certs_path>\n", argv[0]);
555fbd64e3cSjsing 		exit(1);
556fbd64e3cSjsing 	}
557fbd64e3cSjsing 
55823258cfeSbeck 	fprintf(stderr, "\n\nTesting legacy x509_vfy\n");
55923258cfeSbeck 	failed |= verify_cert_test(argv[1], MODE_LEGACY_VFY);
56023258cfeSbeck 	fprintf(stderr, "\n\nTesting modern x509_vfy\n");
56123258cfeSbeck 	failed |= verify_cert_test(argv[1], MODE_MODERN_VFY);
562b68b63e3Sbeck 	fprintf(stderr, "\n\nTesting modern x509_vfy by_dir\n");
563b68b63e3Sbeck 	failed |= verify_cert_test(argv[1], MODE_MODERN_VFY_DIR);
56423258cfeSbeck 	fprintf(stderr, "\n\nTesting x509_verify\n");
56523258cfeSbeck 	failed |= verify_cert_test(argv[1], MODE_VERIFY);
566fbd64e3cSjsing 
567fbd64e3cSjsing 	return (failed);
568fbd64e3cSjsing }
569