xref: /openbsd-src/regress/lib/libcrypto/x509/verify.c (revision 4e1ee0786f11cc571bd0be17d38e46f635c719fc)
1 /* $OpenBSD: verify.c,v 1.8 2021/09/30 18:28:38 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/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 
34 static int verbose = 1;
35 
36 static int
37 passwd_cb(char *buf, int size, int rwflag, void *u)
38 {
39 	memset(buf, 0, size);
40 	return (0);
41 }
42 
43 static int
44 certs_from_file(const char *filename, STACK_OF(X509) **certs)
45 {
46 	STACK_OF(X509_INFO) *xis = NULL;
47 	STACK_OF(X509) *xs = NULL;
48 	BIO *bio = NULL;
49 	X509 *x;
50 	int i;
51 
52 	if ((xs = sk_X509_new_null()) == NULL)
53 		errx(1, "failed to create X509 stack");
54 	if ((bio = BIO_new_file(filename, "r")) == NULL) {
55 		ERR_print_errors_fp(stderr);
56 		errx(1, "failed to create bio");
57 	}
58 	if ((xis = PEM_X509_INFO_read_bio(bio, NULL, passwd_cb, NULL)) == NULL)
59 		errx(1, "failed to read PEM");
60 
61 	for (i = 0; i < sk_X509_INFO_num(xis); i++) {
62 		if ((x = sk_X509_INFO_value(xis, i)->x509) == NULL)
63 			continue;
64 		if (!sk_X509_push(xs, x))
65 			errx(1, "failed to push X509");
66 		X509_up_ref(x);
67 	}
68 
69 	*certs = xs;
70 	xs = NULL;
71 
72 	sk_X509_INFO_pop_free(xis, X509_INFO_free);
73 	sk_X509_pop_free(xs, X509_free);
74 	BIO_free(bio);
75 
76 	return 1;
77 }
78 
79 static int
80 verify_cert_cb(int ok, X509_STORE_CTX *xsc)
81 {
82 	X509 *current_cert;
83 	int verify_err;
84 
85 	current_cert = X509_STORE_CTX_get_current_cert(xsc);
86 	if (current_cert != NULL) {
87 		X509_NAME_print_ex_fp(stderr,
88 		    X509_get_subject_name(current_cert), 0,
89 		    XN_FLAG_ONELINE);
90 		fprintf(stderr, "\n");
91 	}
92 
93 	verify_err = X509_STORE_CTX_get_error(xsc);
94 	if (verify_err != X509_V_OK) {
95 		fprintf(stderr, "verify error at depth %d: %s\n",
96 		    X509_STORE_CTX_get_error_depth(xsc),
97 		    X509_verify_cert_error_string(verify_err));
98 	}
99 
100 	return ok;
101 }
102 
103 static void
104 verify_cert(const char *roots_dir, const char *roots_file,
105     const char *bundle_file, int *chains, int mode)
106 {
107 	STACK_OF(X509) *roots = NULL, *bundle = NULL;
108 	X509_STORE_CTX *xsc = NULL;
109 	X509_STORE *store = NULL;
110 	int verify_err, use_dir;
111 	unsigned long flags;
112 	X509 *leaf = NULL;
113 
114 	*chains = 0;
115 	use_dir = (mode == MODE_MODERN_VFY_DIR);
116 
117 	if (!use_dir && !certs_from_file(roots_file, &roots))
118 		errx(1, "failed to load roots from '%s'", roots_file);
119 	if (!certs_from_file(bundle_file, &bundle))
120 		errx(1, "failed to load bundle from '%s'", bundle_file);
121 	if (sk_X509_num(bundle) < 1)
122 		errx(1, "not enough certs in bundle");
123 	leaf = sk_X509_shift(bundle);
124 
125 	if ((xsc = X509_STORE_CTX_new()) == NULL)
126 		errx(1, "X509_STORE_CTX");
127 	if (use_dir && (store = X509_STORE_new()) == NULL)
128 		errx(1, "X509_STORE");
129 	if (!X509_STORE_CTX_init(xsc, store, leaf, bundle)) {
130 		ERR_print_errors_fp(stderr);
131 		errx(1, "failed to init store context");
132 	}
133 	if (use_dir) {
134 		if (!X509_STORE_load_locations(store, NULL, roots_dir))
135 			errx(1, "failed to set by_dir directory of %s", roots_dir);
136 	}
137 	if (mode == MODE_LEGACY_VFY) {
138 		flags = X509_VERIFY_PARAM_get_flags(xsc->param);
139 		flags |= X509_V_FLAG_LEGACY_VERIFY;
140 		X509_VERIFY_PARAM_set_flags(xsc->param, flags);
141 	} else {
142 		flags = X509_VERIFY_PARAM_get_flags(xsc->param);
143 		flags &= ~X509_V_FLAG_LEGACY_VERIFY;
144 		X509_VERIFY_PARAM_set_flags(xsc->param, flags);
145 	}
146 
147 	if (verbose)
148 		X509_STORE_CTX_set_verify_cb(xsc, verify_cert_cb);
149 	if (!use_dir)
150 		X509_STORE_CTX_set0_trusted_stack(xsc, roots);
151 	if (X509_verify_cert(xsc) == 1) {
152 		*chains = 1; /* XXX */
153 		goto done;
154 	}
155 
156 	verify_err = X509_STORE_CTX_get_error(xsc);
157 	if (verify_err == 0)
158 		errx(1, "Error unset on failure!\n");
159 
160 	fprintf(stderr, "failed to verify at %d: %s\n",
161 	    X509_STORE_CTX_get_error_depth(xsc),
162 	    X509_verify_cert_error_string(verify_err));
163 
164  done:
165 	sk_X509_pop_free(roots, X509_free);
166 	sk_X509_pop_free(bundle, X509_free);
167 	X509_STORE_free(store);
168 	X509_STORE_CTX_free(xsc);
169 	X509_free(leaf);
170 }
171 
172 struct verify_cert_test {
173 	const char *id;
174 	int want_chains;
175 	int failing;
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 verify_cert_tests[] = {
240 	{
241 		.id = "1a",
242 		.want_chains = 1,
243 	},
244 	{
245 		.id = "2a",
246 		.want_chains = 1,
247 	},
248 	{
249 		.id = "2b",
250 		.want_chains = 0,
251 	},
252 	{
253 		.id = "2c",
254 		.want_chains = 1,
255 	},
256 	{
257 		.id = "3a",
258 		.want_chains = 1,
259 	},
260 	{
261 		.id = "3b",
262 		.want_chains = 0,
263 	},
264 	{
265 		.id = "3c",
266 		.want_chains = 0,
267 	},
268 	{
269 		.id = "3d",
270 		.want_chains = 0,
271 	},
272 	{
273 		.id = "3e",
274 		.want_chains = 1,
275 	},
276 	{
277 		.id = "4a",
278 		.want_chains = 2,
279 	},
280 	{
281 		.id = "4b",
282 		.want_chains = 1,
283 	},
284 	{
285 		.id = "4c",
286 		.want_chains = 1,
287 		.failing = 1,
288 	},
289 	{
290 		.id = "4d",
291 		.want_chains = 1,
292 	},
293 	{
294 		.id = "4e",
295 		.want_chains = 1,
296 	},
297 	{
298 		.id = "4f",
299 		.want_chains = 2,
300 	},
301 	{
302 		.id = "4g",
303 		.want_chains = 1,
304 		.failing = 1,
305 	},
306 	{
307 		.id = "4h",
308 		.want_chains = 1,
309 	},
310 	{
311 		.id = "5a",
312 		.want_chains = 2,
313 	},
314 	{
315 		.id = "5b",
316 		.want_chains = 1,
317 		.failing = 1,
318 	},
319 	{
320 		.id = "5c",
321 		.want_chains = 1,
322 	},
323 	{
324 		.id = "5d",
325 		.want_chains = 1,
326 	},
327 	{
328 		.id = "5e",
329 		.want_chains = 1,
330 		.failing = 1,
331 	},
332 	{
333 		.id = "5f",
334 		.want_chains = 1,
335 	},
336 	{
337 		.id = "5g",
338 		.want_chains = 2,
339 	},
340 	{
341 		.id = "5h",
342 		.want_chains = 1,
343 	},
344 	{
345 		.id = "5i",
346 		.want_chains = 1,
347 		.failing = 1,
348 	},
349 	{
350 		.id = "6a",
351 		.want_chains = 1,
352 	},
353 	{
354 		.id = "6b",
355 		.want_chains = 1,
356 		.failing = 1,
357 	},
358 	{
359 		.id = "7a",
360 		.want_chains = 1,
361 		.failing = 1,
362 	},
363 	{
364 		.id = "7b",
365 		.want_chains = 1,
366 	},
367 	{
368 		.id = "8a",
369 		.want_chains = 0,
370 	},
371 	{
372 		.id = "9a",
373 		.want_chains = 0,
374 	},
375 	{
376 		.id = "10a",
377 		.want_chains = 1,
378 	},
379 	{
380 		.id = "10b",
381 		.want_chains = 1,
382 	},
383 	{
384 		.id = "11a",
385 		.want_chains = 1,
386 		.failing = 1,
387 	},
388 	{
389 		.id = "11b",
390 		.want_chains = 1,
391 	},
392 	{
393 		.id = "12a",
394 		.want_chains = 1,
395 	},
396 	{
397 		.id = "13a",
398 		.want_chains = 1,
399 		.failing = 1,
400 	},
401 };
402 
403 #define N_VERIFY_CERT_TESTS \
404     (sizeof(verify_cert_tests) / sizeof(*verify_cert_tests))
405 
406 static int
407 verify_cert_test(const char *certs_path, int mode)
408 {
409 	char *roots_file, *bundle_file, *roots_dir;
410 	struct verify_cert_test *vct;
411 	int failed = 0;
412 	int chains;
413 	size_t i;
414 
415 	for (i = 0; i < N_VERIFY_CERT_TESTS; i++) {
416 		vct = &verify_cert_tests[i];
417 
418 		if (asprintf(&roots_file, "%s/%s/roots.pem", certs_path,
419 		    vct->id) == -1)
420 			errx(1, "asprintf");
421 		if (asprintf(&bundle_file, "%s/%s/bundle.pem", certs_path,
422 		    vct->id) == -1)
423 			errx(1, "asprintf");
424 		if (asprintf(&roots_dir, "./%s/roots", vct->id) == -1)
425 			errx(1, "asprintf");
426 
427 		fprintf(stderr, "== Test %zu (%s)\n", i, vct->id);
428 		if (mode == MODE_VERIFY)
429 			verify_cert_new(roots_file, bundle_file, &chains);
430 		else
431 			verify_cert(roots_dir, roots_file, bundle_file, &chains, mode);
432 		if ((mode == MODE_VERIFY && chains == vct->want_chains) ||
433 		    (chains == 0 && vct->want_chains == 0) ||
434 		    (chains == 1 && vct->want_chains > 0)) {
435 			fprintf(stderr, "INFO: Succeeded with %d chains%s\n",
436 			    chains, vct->failing ? " (legacy failure)" : "");
437 			if (mode == MODE_LEGACY_VFY && vct->failing)
438 				failed |= 1;
439 		} else {
440 			fprintf(stderr, "FAIL: Failed with %d chains%s\n",
441 			    chains, vct->failing ? " (legacy failure)" : "");
442 			if (!vct->failing)
443 				failed |= 1;
444 		}
445 		fprintf(stderr, "\n");
446 
447 		free(roots_file);
448 		free(bundle_file);
449 		free(roots_dir);
450 	}
451 
452 	return failed;
453 }
454 
455 int
456 main(int argc, char **argv)
457 {
458 	int failed = 0;
459 
460 	if (argc != 2) {
461 		fprintf(stderr, "usage: %s <certs_path>\n", argv[0]);
462 		exit(1);
463 	}
464 
465 	fprintf(stderr, "\n\nTesting legacy x509_vfy\n");
466 	failed |= verify_cert_test(argv[1], MODE_LEGACY_VFY);
467 	fprintf(stderr, "\n\nTesting modern x509_vfy\n");
468 	failed |= verify_cert_test(argv[1], MODE_MODERN_VFY);
469 	fprintf(stderr, "\n\nTesting modern x509_vfy by_dir\n");
470 	failed |= verify_cert_test(argv[1], MODE_MODERN_VFY_DIR);
471 	fprintf(stderr, "\n\nTesting x509_verify\n");
472 	failed |= verify_cert_test(argv[1], MODE_VERIFY);
473 
474 	return (failed);
475 }
476