xref: /openbsd-src/regress/lib/libcrypto/x509/callbackfailures.c (revision 6822f9c8033774f6aab00bcb43e8718e7034e67d)
1*6822f9c8Santon /* $OpenBSD: callbackfailures.c,v 1.3 2024/08/23 12:56:26 anton Exp $ */
2d8913d6aSbeck /*
3d8913d6aSbeck  * Copyright (c) 2020 Joel Sing <jsing@openbsd.org>
4d8913d6aSbeck  * Copyright (c) 2020-2021 Bob Beck <beck@openbsd.org>
5d8913d6aSbeck  *
6d8913d6aSbeck  * Permission to use, copy, modify, and distribute this software for any
7d8913d6aSbeck  * purpose with or without fee is hereby granted, provided that the above
8d8913d6aSbeck  * copyright notice and this permission notice appear in all copies.
9d8913d6aSbeck  *
10d8913d6aSbeck  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11d8913d6aSbeck  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12d8913d6aSbeck  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13d8913d6aSbeck  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14d8913d6aSbeck  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15d8913d6aSbeck  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16d8913d6aSbeck  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17d8913d6aSbeck  */
18d8913d6aSbeck 
19d8913d6aSbeck #include <err.h>
20d8913d6aSbeck #include <string.h>
21d8913d6aSbeck 
22d8913d6aSbeck #include <openssl/bio.h>
23d8913d6aSbeck #include <openssl/err.h>
24d8913d6aSbeck #include <openssl/pem.h>
25d8913d6aSbeck #include <openssl/x509.h>
26d8913d6aSbeck #include <openssl/x509v3.h>
27f45080b3Stb 
28f45080b3Stb #include "x509_verify.h"
29d8913d6aSbeck 
30d8913d6aSbeck #define MODE_MODERN_VFY		0
31d8913d6aSbeck #define MODE_MODERN_VFY_DIR	1
32d8913d6aSbeck #define MODE_LEGACY_VFY		2
33d8913d6aSbeck #define MODE_VERIFY		3
34d8913d6aSbeck 
35d8913d6aSbeck static int verbose = 1;
36d8913d6aSbeck 
37d8913d6aSbeck static int expected_depth;
38d8913d6aSbeck static int expected_error;
39d8913d6aSbeck static int seen_depth;
40d8913d6aSbeck static int seen_error;
41d8913d6aSbeck 
42d8913d6aSbeck static int
43d8913d6aSbeck passwd_cb(char *buf, int size, int rwflag, void *u)
44d8913d6aSbeck {
45d8913d6aSbeck 	memset(buf, 0, size);
46d8913d6aSbeck 	return (0);
47d8913d6aSbeck }
48d8913d6aSbeck 
49d8913d6aSbeck static int
50d8913d6aSbeck certs_from_file(const char *filename, STACK_OF(X509) **certs, int clear)
51d8913d6aSbeck {
52d8913d6aSbeck 	STACK_OF(X509_INFO) *xis = NULL;
53d8913d6aSbeck 	STACK_OF(X509) *xs = NULL;
54d8913d6aSbeck 	BIO *bio = NULL;
55d8913d6aSbeck 	X509 *x;
56d8913d6aSbeck 	int i;
57d8913d6aSbeck 
58d8913d6aSbeck 	if (clear) {
59d8913d6aSbeck 		if ((xs = sk_X509_new_null()) == NULL)
60d8913d6aSbeck 			errx(1, "failed to create X509 stack");
61d8913d6aSbeck 	} else
62d8913d6aSbeck 		xs = *certs;
63d8913d6aSbeck 	if ((bio = BIO_new_file(filename, "r")) == NULL) {
64d8913d6aSbeck 		ERR_print_errors_fp(stderr);
65d8913d6aSbeck 		errx(1, "failed to create bio");
66d8913d6aSbeck 	}
67d8913d6aSbeck 	if ((xis = PEM_X509_INFO_read_bio(bio, NULL, passwd_cb, NULL)) == NULL)
68d8913d6aSbeck 		errx(1, "failed to read PEM");
69d8913d6aSbeck 
70d8913d6aSbeck 	for (i = 0; i < sk_X509_INFO_num(xis); i++) {
71d8913d6aSbeck 		if ((x = sk_X509_INFO_value(xis, i)->x509) == NULL)
72d8913d6aSbeck 			continue;
73d8913d6aSbeck 		if (!sk_X509_push(xs, x))
74d8913d6aSbeck 			errx(1, "failed to push X509");
75d8913d6aSbeck 		X509_up_ref(x);
76d8913d6aSbeck 	}
77d8913d6aSbeck 
78d8913d6aSbeck 	*certs = xs;
79d8913d6aSbeck 	xs = NULL;
80d8913d6aSbeck 
81d8913d6aSbeck 	sk_X509_INFO_pop_free(xis, X509_INFO_free);
82d8913d6aSbeck 	sk_X509_pop_free(xs, X509_free);
83d8913d6aSbeck 	BIO_free(bio);
84d8913d6aSbeck 
85d8913d6aSbeck 	return 1;
86d8913d6aSbeck }
87d8913d6aSbeck 
88d8913d6aSbeck static int
89d8913d6aSbeck verify_cert_cb(int ok, X509_STORE_CTX *xsc)
90d8913d6aSbeck {
91d8913d6aSbeck 	X509 *current_cert;
92d8913d6aSbeck 	int verify_err;
93d8913d6aSbeck 
94d8913d6aSbeck 	current_cert = X509_STORE_CTX_get_current_cert(xsc);
95d8913d6aSbeck 	if (current_cert != NULL) {
96d8913d6aSbeck 		X509_NAME_print_ex_fp(stderr,
97d8913d6aSbeck 		    X509_get_subject_name(current_cert), 0,
98d8913d6aSbeck 		    XN_FLAG_ONELINE);
99d8913d6aSbeck 		fprintf(stderr, "\n");
100d8913d6aSbeck 	}
101d8913d6aSbeck 
102d8913d6aSbeck 	verify_err = X509_STORE_CTX_get_error(xsc);
103d8913d6aSbeck 	if (verify_err != X509_V_OK) {
104d8913d6aSbeck 		seen_depth = X509_STORE_CTX_get_error_depth(xsc);
105d8913d6aSbeck 		seen_error = verify_err;
106d8913d6aSbeck 		fprintf(stderr, "verify error at depth %d: %s\n",
107d8913d6aSbeck 		    X509_STORE_CTX_get_error_depth(xsc),
108d8913d6aSbeck 		    X509_verify_cert_error_string(verify_err));
109d8913d6aSbeck 	}
110d8913d6aSbeck 
111d8913d6aSbeck 	fprintf(stderr, "chain of length %d\n", sk_X509_num (X509_STORE_CTX_get0_chain (xsc)));
112d8913d6aSbeck 
113d8913d6aSbeck 	return ok;
114d8913d6aSbeck }
115d8913d6aSbeck 
116d8913d6aSbeck static void
117d8913d6aSbeck verify_cert(const char *roots_dir, const char *roots_file,
118d8913d6aSbeck     const char *bundle_file, const char*bundle_file2, int *chains, int mode)
119d8913d6aSbeck {
120d8913d6aSbeck 	STACK_OF(X509) *roots = NULL, *bundle = NULL;
121d8913d6aSbeck 	X509_STORE_CTX *xsc = NULL;
122d8913d6aSbeck 	X509_STORE *store = NULL;
123d8913d6aSbeck 	int verify_err, use_dir;
124d8913d6aSbeck 	X509 *leaf = NULL;
125d8913d6aSbeck 
126d8913d6aSbeck 	*chains = 0;
127d8913d6aSbeck 	use_dir = (mode == MODE_MODERN_VFY_DIR);
128d8913d6aSbeck 
129d8913d6aSbeck 	if (!use_dir && !certs_from_file(roots_file, &roots, 1))
130d8913d6aSbeck 		errx(1, "failed to load roots from '%s'", roots_file);
131d8913d6aSbeck 	if (!certs_from_file(bundle_file, &bundle, 1))
132d8913d6aSbeck 		errx(1, "failed to load bundle from '%s'", bundle_file);
133d8913d6aSbeck 	if (!certs_from_file(bundle_file, &bundle, 0))
134d8913d6aSbeck 		errx(1, "failed to load bundle from '%s'", bundle_file2);
135d8913d6aSbeck 	if (sk_X509_num(bundle) < 1)
136d8913d6aSbeck 		errx(1, "not enough certs in bundle");
137d8913d6aSbeck 	leaf = sk_X509_shift(bundle);
138d8913d6aSbeck 
139d8913d6aSbeck 	if ((xsc = X509_STORE_CTX_new()) == NULL)
140d8913d6aSbeck 		errx(1, "X509_STORE_CTX");
141d8913d6aSbeck 	if (use_dir && (store = X509_STORE_new()) == NULL)
142d8913d6aSbeck 		errx(1, "X509_STORE");
143d8913d6aSbeck 	if (!X509_STORE_CTX_init(xsc, store, leaf, bundle)) {
144d8913d6aSbeck 		ERR_print_errors_fp(stderr);
145d8913d6aSbeck 		errx(1, "failed to init store context");
146d8913d6aSbeck 	}
147d8913d6aSbeck 
148d8913d6aSbeck 	if (use_dir) {
149d8913d6aSbeck 		if (!X509_STORE_load_locations(store, NULL, roots_dir))
150d8913d6aSbeck 			errx(1, "failed to set by_dir directory of %s", roots_dir);
151d8913d6aSbeck 	}
152d8913d6aSbeck 	if (mode == MODE_LEGACY_VFY)
153d8913d6aSbeck 		X509_STORE_CTX_set_flags(xsc, X509_V_FLAG_LEGACY_VERIFY);
154d8913d6aSbeck 	else
155d8913d6aSbeck 		X509_VERIFY_PARAM_clear_flags(X509_STORE_CTX_get0_param(xsc),
156d8913d6aSbeck 		    X509_V_FLAG_LEGACY_VERIFY);
157d8913d6aSbeck 
158d8913d6aSbeck 	if (verbose)
159d8913d6aSbeck 		X509_STORE_CTX_set_verify_cb(xsc, verify_cert_cb);
160d8913d6aSbeck 	if (!use_dir)
161d8913d6aSbeck 		X509_STORE_CTX_set0_trusted_stack(xsc, roots);
162d8913d6aSbeck 
163d8913d6aSbeck 	if (X509_verify_cert(xsc) == 1) {
164d8913d6aSbeck 		*chains = 1; /* XXX */
165d8913d6aSbeck 		goto done;
166d8913d6aSbeck 	}
167d8913d6aSbeck 
168d8913d6aSbeck 	verify_err = X509_STORE_CTX_get_error(xsc);
169d8913d6aSbeck 	if (verify_err == 0)
170*6822f9c8Santon 		errx(1, "Error unset on failure!");
171d8913d6aSbeck 
172d8913d6aSbeck 	fprintf(stderr, "failed to verify at %d: %s\n",
173d8913d6aSbeck 	    X509_STORE_CTX_get_error_depth(xsc),
174d8913d6aSbeck 	    X509_verify_cert_error_string(verify_err));
175d8913d6aSbeck 
176d8913d6aSbeck  done:
177d8913d6aSbeck 	sk_X509_pop_free(roots, X509_free);
178d8913d6aSbeck 	sk_X509_pop_free(bundle, X509_free);
179d8913d6aSbeck 	X509_STORE_free(store);
180d8913d6aSbeck 	X509_STORE_CTX_free(xsc);
181d8913d6aSbeck 	X509_free(leaf);
182d8913d6aSbeck }
183d8913d6aSbeck 
184d8913d6aSbeck struct verify_cert_test {
185d8913d6aSbeck 	const char *id;
186d8913d6aSbeck 	int want_chains;
187d8913d6aSbeck 	int failing;
188d8913d6aSbeck 	int depth;
189d8913d6aSbeck 	int error;
190d8913d6aSbeck };
191d8913d6aSbeck 
192d8913d6aSbeck struct verify_cert_test verify_cert_tests[] = {
193d8913d6aSbeck 	{
194d8913d6aSbeck 		.id = "1a",
195d8913d6aSbeck 		.want_chains = 0,
196d8913d6aSbeck 		.depth = 0,
197d8913d6aSbeck 		.error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
198d8913d6aSbeck 	},
199d8913d6aSbeck 	{
200d8913d6aSbeck 		.id = "2a",
201d8913d6aSbeck 		.want_chains = 0,
202d8913d6aSbeck 		.depth = 1,
203d8913d6aSbeck 		.error = X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
204d8913d6aSbeck 	},
205d8913d6aSbeck 	{
206d8913d6aSbeck 		.id = "2c",
207d8913d6aSbeck 		.want_chains = 0,
208d8913d6aSbeck 		.depth = 2,
209d8913d6aSbeck 		.error = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN,
210d8913d6aSbeck 	},
211d8913d6aSbeck };
212d8913d6aSbeck 
213d8913d6aSbeck #define N_VERIFY_CERT_TESTS \
214d8913d6aSbeck     (sizeof(verify_cert_tests) / sizeof(*verify_cert_tests))
215d8913d6aSbeck 
216d8913d6aSbeck static int
217d8913d6aSbeck verify_cert_test(const char *certs_path, int mode)
218d8913d6aSbeck {
219d8913d6aSbeck 	char *roots_file, *bundle_file, *bundle_file2, *roots_dir;
220d8913d6aSbeck 	struct verify_cert_test *vct;
221d8913d6aSbeck 	int failed = 0;
222d8913d6aSbeck 	int chains;
223d8913d6aSbeck 	size_t i;
224d8913d6aSbeck 
225d8913d6aSbeck 	for (i = 0; i < N_VERIFY_CERT_TESTS; i++) {
226d8913d6aSbeck 		vct = &verify_cert_tests[i];
227d8913d6aSbeck 
228d8913d6aSbeck 		if (asprintf(&roots_file, "/etc/ssl/cert.pem") == -1)
229d8913d6aSbeck 			errx(1, "asprintf");
230d8913d6aSbeck 		if (asprintf(&bundle_file, "%s/%s/bundle.pem", certs_path,
231d8913d6aSbeck 		    vct->id) == -1)
232d8913d6aSbeck 			errx(1, "asprintf");
233d8913d6aSbeck 		if (asprintf(&bundle_file2, "%s/%s/roots.pem", certs_path,
234d8913d6aSbeck 		    vct->id) == -1)
235d8913d6aSbeck 			errx(1, "asprintf");
236d8913d6aSbeck 		if (asprintf(&roots_dir, "./%s/roots", vct->id) == -1)
237d8913d6aSbeck 			errx(1, "asprintf");
238d8913d6aSbeck 
239d8913d6aSbeck 		fprintf(stderr, "== Test %zu (%s)\n", i, vct->id);
240d8913d6aSbeck 		fprintf(stderr, "== depth %d\n", vct->depth);
241d8913d6aSbeck 		fprintf(stderr, "== error %d\n", vct->error);
242d8913d6aSbeck 		expected_depth = vct->depth;
243d8913d6aSbeck 		expected_error = vct->error;
244d8913d6aSbeck 		verify_cert(roots_dir, roots_file, bundle_file, bundle_file2, &chains, mode);
245d8913d6aSbeck 		if (chains == 0 && vct->want_chains == 0) {
246d8913d6aSbeck 			if (seen_error != expected_error) {
247d8913d6aSbeck 				fprintf(stderr, "FAIL: expected error %d, got %d\n",
248d8913d6aSbeck 				    seen_error, expected_error);
249d8913d6aSbeck 				failed |= 1;
250d8913d6aSbeck 			}
251d8913d6aSbeck 			if (seen_depth != expected_depth) {
252d8913d6aSbeck 				fprintf(stderr, "FAIL: expected depth %d, got %d\n",
253d8913d6aSbeck 				    seen_depth, expected_depth);
254d8913d6aSbeck 				failed |= 1;
255d8913d6aSbeck 			}
256d8913d6aSbeck 		}
257d8913d6aSbeck 
258d8913d6aSbeck 		if ((mode == MODE_VERIFY && chains == vct->want_chains) ||
259d8913d6aSbeck 		    (chains == 0 && vct->want_chains == 0) ||
260d8913d6aSbeck 		    (chains == 1 && vct->want_chains > 0)) {
261d8913d6aSbeck 			fprintf(stderr, "INFO: Succeeded with %d chains%s\n",
262d8913d6aSbeck 			    chains, vct->failing ? " (legacy failure)" : "");
263d8913d6aSbeck 			if (mode == MODE_LEGACY_VFY && vct->failing)
264d8913d6aSbeck 				failed |= 1;
265d8913d6aSbeck 		} else {
266d8913d6aSbeck 			fprintf(stderr, "FAIL: Failed with %d chains%s\n",
267d8913d6aSbeck 			    chains, vct->failing ? " (legacy failure)" : "");
268d8913d6aSbeck 			if (!vct->failing)
269d8913d6aSbeck 				failed |= 1;
270d8913d6aSbeck 		}
271d8913d6aSbeck 		fprintf(stderr, "\n");
272d8913d6aSbeck 
273d8913d6aSbeck 		free(roots_file);
274d8913d6aSbeck 		free(bundle_file);
275d8913d6aSbeck 		free(bundle_file2);
276d8913d6aSbeck 		free(roots_dir);
277d8913d6aSbeck 	}
278d8913d6aSbeck 
279d8913d6aSbeck 	return failed;
280d8913d6aSbeck }
281d8913d6aSbeck 
282d8913d6aSbeck int
283d8913d6aSbeck main(int argc, char **argv)
284d8913d6aSbeck {
285d8913d6aSbeck 	int failed = 0;
286d8913d6aSbeck 
287d8913d6aSbeck 	if (argc != 2) {
288d8913d6aSbeck 		fprintf(stderr, "usage: %s <certs_path>\n", argv[0]);
289d8913d6aSbeck 		exit(1);
290d8913d6aSbeck 	}
291d8913d6aSbeck 
292d8913d6aSbeck 	fprintf(stderr, "\n\nTesting legacy x509_vfy\n");
293d8913d6aSbeck 	failed |= verify_cert_test(argv[1], MODE_LEGACY_VFY);
294d8913d6aSbeck 	fprintf(stderr, "\n\nTesting modern x509_vfy\n");
295d8913d6aSbeck 	failed |= verify_cert_test(argv[1], MODE_MODERN_VFY);
296d8913d6aSbeck 
297d8913d6aSbeck 	return (failed);
298d8913d6aSbeck }
299