xref: /openbsd/regress/lib/libcrypto/x509/verify.c (revision 73471bf0)
1 /* $OpenBSD: verify.c,v 1.9 2021/10/31 08:27:15 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/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 	X509 *leaf = NULL;
112 
113 	*chains = 0;
114 	use_dir = (mode == MODE_MODERN_VFY_DIR);
115 
116 	if (!use_dir && !certs_from_file(roots_file, &roots))
117 		errx(1, "failed to load roots from '%s'", roots_file);
118 	if (!certs_from_file(bundle_file, &bundle))
119 		errx(1, "failed to load bundle from '%s'", bundle_file);
120 	if (sk_X509_num(bundle) < 1)
121 		errx(1, "not enough certs in bundle");
122 	leaf = sk_X509_shift(bundle);
123 
124 	if ((xsc = X509_STORE_CTX_new()) == NULL)
125 		errx(1, "X509_STORE_CTX");
126 	if (use_dir && (store = X509_STORE_new()) == NULL)
127 		errx(1, "X509_STORE");
128 	if (!X509_STORE_CTX_init(xsc, store, leaf, bundle)) {
129 		ERR_print_errors_fp(stderr);
130 		errx(1, "failed to init store context");
131 	}
132 	if (use_dir) {
133 		if (!X509_STORE_load_locations(store, NULL, roots_dir))
134 			errx(1, "failed to set by_dir directory of %s", roots_dir);
135 	}
136 	if (mode == MODE_LEGACY_VFY)
137 		X509_STORE_CTX_set_flags(xsc, X509_V_FLAG_LEGACY_VERIFY);
138 	else
139 		X509_VERIFY_PARAM_clear_flags(X509_STORE_CTX_get0_param(xsc),
140 		    X509_V_FLAG_LEGACY_VERIFY);
141 
142 	if (verbose)
143 		X509_STORE_CTX_set_verify_cb(xsc, verify_cert_cb);
144 	if (!use_dir)
145 		X509_STORE_CTX_set0_trusted_stack(xsc, roots);
146 	if (X509_verify_cert(xsc) == 1) {
147 		*chains = 1; /* XXX */
148 		goto done;
149 	}
150 
151 	verify_err = X509_STORE_CTX_get_error(xsc);
152 	if (verify_err == 0)
153 		errx(1, "Error unset on failure!\n");
154 
155 	fprintf(stderr, "failed to verify at %d: %s\n",
156 	    X509_STORE_CTX_get_error_depth(xsc),
157 	    X509_verify_cert_error_string(verify_err));
158 
159  done:
160 	sk_X509_pop_free(roots, X509_free);
161 	sk_X509_pop_free(bundle, X509_free);
162 	X509_STORE_free(store);
163 	X509_STORE_CTX_free(xsc);
164 	X509_free(leaf);
165 }
166 
167 struct verify_cert_test {
168 	const char *id;
169 	int want_chains;
170 	int failing;
171 };
172 
173 static void
174 verify_cert_new(const char *roots_file, const char *bundle_file, int *chains)
175 {
176 	STACK_OF(X509) *roots = NULL, *bundle = NULL;
177 	X509_STORE_CTX *xsc = NULL;
178 	X509 *leaf = NULL;
179 	struct x509_verify_ctx *ctx;
180 
181 	*chains = 0;
182 
183 	if (!certs_from_file(roots_file, &roots))
184 		errx(1, "failed to load roots from '%s'", roots_file);
185 	if (!certs_from_file(bundle_file, &bundle))
186 		errx(1, "failed to load bundle from '%s'", bundle_file);
187 	if (sk_X509_num(bundle) < 1)
188 		errx(1, "not enough certs in bundle");
189 	leaf = sk_X509_shift(bundle);
190 
191         if ((xsc = X509_STORE_CTX_new()) == NULL)
192 		errx(1, "X509_STORE_CTX");
193 	if (!X509_STORE_CTX_init(xsc, NULL, leaf, bundle)) {
194 		ERR_print_errors_fp(stderr);
195 		errx(1, "failed to init store context");
196 	}
197 	if (verbose)
198 		X509_STORE_CTX_set_verify_cb(xsc, verify_cert_cb);
199 
200 	if ((ctx = x509_verify_ctx_new(roots)) == NULL)
201 		errx(1, "failed to create ctx");
202 	if (!x509_verify_ctx_set_intermediates(ctx, bundle))
203 		errx(1, "failed to set intermediates");
204 
205 	if ((*chains = x509_verify(ctx, leaf, NULL)) == 0) {
206 		fprintf(stderr, "failed to verify at %lu: %s\n",
207 		    x509_verify_ctx_error_depth(ctx),
208 		    x509_verify_ctx_error_string(ctx));
209 	} else {
210 		int c;
211 
212 		for (c = 0; verbose && c < *chains; c++) {
213 			STACK_OF(X509) *chain;
214 			int i;
215 
216 			fprintf(stderr, "Chain %d\n--------\n", c);
217 			chain = x509_verify_ctx_chain(ctx, c);
218 			for (i = 0; i < sk_X509_num(chain); i++) {
219 				X509 *cert = sk_X509_value(chain, i);
220 				X509_NAME_print_ex_fp(stderr,
221 				    X509_get_subject_name(cert), 0,
222 				    XN_FLAG_ONELINE);
223 				fprintf(stderr, "\n");
224 			}
225 		}
226 	}
227 	sk_X509_pop_free(roots, X509_free);
228 	sk_X509_pop_free(bundle, X509_free);
229 	X509_free(leaf);
230 	X509_STORE_CTX_free(xsc);
231 	x509_verify_ctx_free(ctx);
232 }
233 
234 struct verify_cert_test verify_cert_tests[] = {
235 	{
236 		.id = "1a",
237 		.want_chains = 1,
238 	},
239 	{
240 		.id = "2a",
241 		.want_chains = 1,
242 	},
243 	{
244 		.id = "2b",
245 		.want_chains = 0,
246 	},
247 	{
248 		.id = "2c",
249 		.want_chains = 1,
250 	},
251 	{
252 		.id = "3a",
253 		.want_chains = 1,
254 	},
255 	{
256 		.id = "3b",
257 		.want_chains = 0,
258 	},
259 	{
260 		.id = "3c",
261 		.want_chains = 0,
262 	},
263 	{
264 		.id = "3d",
265 		.want_chains = 0,
266 	},
267 	{
268 		.id = "3e",
269 		.want_chains = 1,
270 	},
271 	{
272 		.id = "4a",
273 		.want_chains = 2,
274 	},
275 	{
276 		.id = "4b",
277 		.want_chains = 1,
278 	},
279 	{
280 		.id = "4c",
281 		.want_chains = 1,
282 		.failing = 1,
283 	},
284 	{
285 		.id = "4d",
286 		.want_chains = 1,
287 	},
288 	{
289 		.id = "4e",
290 		.want_chains = 1,
291 	},
292 	{
293 		.id = "4f",
294 		.want_chains = 2,
295 	},
296 	{
297 		.id = "4g",
298 		.want_chains = 1,
299 		.failing = 1,
300 	},
301 	{
302 		.id = "4h",
303 		.want_chains = 1,
304 	},
305 	{
306 		.id = "5a",
307 		.want_chains = 2,
308 	},
309 	{
310 		.id = "5b",
311 		.want_chains = 1,
312 		.failing = 1,
313 	},
314 	{
315 		.id = "5c",
316 		.want_chains = 1,
317 	},
318 	{
319 		.id = "5d",
320 		.want_chains = 1,
321 	},
322 	{
323 		.id = "5e",
324 		.want_chains = 1,
325 		.failing = 1,
326 	},
327 	{
328 		.id = "5f",
329 		.want_chains = 1,
330 	},
331 	{
332 		.id = "5g",
333 		.want_chains = 2,
334 	},
335 	{
336 		.id = "5h",
337 		.want_chains = 1,
338 	},
339 	{
340 		.id = "5i",
341 		.want_chains = 1,
342 		.failing = 1,
343 	},
344 	{
345 		.id = "6a",
346 		.want_chains = 1,
347 	},
348 	{
349 		.id = "6b",
350 		.want_chains = 1,
351 		.failing = 1,
352 	},
353 	{
354 		.id = "7a",
355 		.want_chains = 1,
356 		.failing = 1,
357 	},
358 	{
359 		.id = "7b",
360 		.want_chains = 1,
361 	},
362 	{
363 		.id = "8a",
364 		.want_chains = 0,
365 	},
366 	{
367 		.id = "9a",
368 		.want_chains = 0,
369 	},
370 	{
371 		.id = "10a",
372 		.want_chains = 1,
373 	},
374 	{
375 		.id = "10b",
376 		.want_chains = 1,
377 	},
378 	{
379 		.id = "11a",
380 		.want_chains = 1,
381 		.failing = 1,
382 	},
383 	{
384 		.id = "11b",
385 		.want_chains = 1,
386 	},
387 	{
388 		.id = "12a",
389 		.want_chains = 1,
390 	},
391 	{
392 		.id = "13a",
393 		.want_chains = 1,
394 		.failing = 1,
395 	},
396 };
397 
398 #define N_VERIFY_CERT_TESTS \
399     (sizeof(verify_cert_tests) / sizeof(*verify_cert_tests))
400 
401 static int
402 verify_cert_test(const char *certs_path, int mode)
403 {
404 	char *roots_file, *bundle_file, *roots_dir;
405 	struct verify_cert_test *vct;
406 	int failed = 0;
407 	int chains;
408 	size_t i;
409 
410 	for (i = 0; i < N_VERIFY_CERT_TESTS; i++) {
411 		vct = &verify_cert_tests[i];
412 
413 		if (asprintf(&roots_file, "%s/%s/roots.pem", certs_path,
414 		    vct->id) == -1)
415 			errx(1, "asprintf");
416 		if (asprintf(&bundle_file, "%s/%s/bundle.pem", certs_path,
417 		    vct->id) == -1)
418 			errx(1, "asprintf");
419 		if (asprintf(&roots_dir, "./%s/roots", vct->id) == -1)
420 			errx(1, "asprintf");
421 
422 		fprintf(stderr, "== Test %zu (%s)\n", i, vct->id);
423 		if (mode == MODE_VERIFY)
424 			verify_cert_new(roots_file, bundle_file, &chains);
425 		else
426 			verify_cert(roots_dir, roots_file, bundle_file, &chains, mode);
427 		if ((mode == MODE_VERIFY && chains == vct->want_chains) ||
428 		    (chains == 0 && vct->want_chains == 0) ||
429 		    (chains == 1 && vct->want_chains > 0)) {
430 			fprintf(stderr, "INFO: Succeeded with %d chains%s\n",
431 			    chains, vct->failing ? " (legacy failure)" : "");
432 			if (mode == MODE_LEGACY_VFY && vct->failing)
433 				failed |= 1;
434 		} else {
435 			fprintf(stderr, "FAIL: Failed with %d chains%s\n",
436 			    chains, vct->failing ? " (legacy failure)" : "");
437 			if (!vct->failing)
438 				failed |= 1;
439 		}
440 		fprintf(stderr, "\n");
441 
442 		free(roots_file);
443 		free(bundle_file);
444 		free(roots_dir);
445 	}
446 
447 	return failed;
448 }
449 
450 int
451 main(int argc, char **argv)
452 {
453 	int failed = 0;
454 
455 	if (argc != 2) {
456 		fprintf(stderr, "usage: %s <certs_path>\n", argv[0]);
457 		exit(1);
458 	}
459 
460 	fprintf(stderr, "\n\nTesting legacy x509_vfy\n");
461 	failed |= verify_cert_test(argv[1], MODE_LEGACY_VFY);
462 	fprintf(stderr, "\n\nTesting modern x509_vfy\n");
463 	failed |= verify_cert_test(argv[1], MODE_MODERN_VFY);
464 	fprintf(stderr, "\n\nTesting modern x509_vfy by_dir\n");
465 	failed |= verify_cert_test(argv[1], MODE_MODERN_VFY_DIR);
466 	fprintf(stderr, "\n\nTesting x509_verify\n");
467 	failed |= verify_cert_test(argv[1], MODE_VERIFY);
468 
469 	return (failed);
470 }
471