1 /* $OpenBSD: policy.c,v 1.13 2024/08/23 12:56:26 anton Exp $ */
2 /*
3  * Copyright (c) 2020 Joel Sing <jsing@openbsd.org>
4  * Copyright (c) 2020-2023 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 
35 static int verbose = 1;
36 
37 #define OID1 "1.2.840.113554.4.1.72585.2.1"
38 #define OID2 "1.2.840.113554.4.1.72585.2.2"
39 #define OID3 "1.2.840.113554.4.1.72585.2.3"
40 #define OID4 "1.2.840.113554.4.1.72585.2.4"
41 #define OID5 "1.2.840.113554.4.1.72585.2.5"
42 
43 #ifndef CERTSDIR
44 #define CERTSDIR "."
45 #endif
46 
47 static int
passwd_cb(char * buf,int size,int rwflag,void * u)48 passwd_cb(char *buf, int size, int rwflag, void *u)
49 {
50 	memset(buf, 0, size);
51 	return (0);
52 }
53 
54 static int
certs_from_file(const char * filename,STACK_OF (X509)** certs)55 certs_from_file(const char *filename, STACK_OF(X509) **certs)
56 {
57 	STACK_OF(X509_INFO) *xis = NULL;
58 	STACK_OF(X509) *xs = NULL;
59 	BIO *bio = NULL;
60 	X509 *x;
61 	int i;
62 
63 	if (*certs == NULL) {
64 		if ((xs = sk_X509_new_null()) == NULL)
65 			errx(1, "failed to create X509 stack");
66 	} else {
67 		xs = *certs;
68 	}
69 	if ((bio = BIO_new_file(filename, "r")) == NULL) {
70 		ERR_print_errors_fp(stderr);
71 		errx(1, "failed to create bio");
72 	}
73 	if ((xis = PEM_X509_INFO_read_bio(bio, NULL, passwd_cb, NULL)) == NULL)
74 		errx(1, "failed to read PEM");
75 
76 	for (i = 0; i < sk_X509_INFO_num(xis); i++) {
77 		if ((x = sk_X509_INFO_value(xis, i)->x509) == NULL)
78 			continue;
79 		if (!sk_X509_push(xs, x))
80 			errx(1, "failed to push X509");
81 		X509_up_ref(x);
82 	}
83 
84 	*certs = xs;
85 	xs = NULL;
86 
87 	sk_X509_INFO_pop_free(xis, X509_INFO_free);
88 	sk_X509_pop_free(xs, X509_free);
89 	BIO_free(bio);
90 
91 	return 1;
92 }
93 
94 static int
verify_cert_cb(int ok,X509_STORE_CTX * xsc)95 verify_cert_cb(int ok, X509_STORE_CTX *xsc)
96 {
97 	X509 *current_cert;
98 	int verify_err;
99 
100 	current_cert = X509_STORE_CTX_get_current_cert(xsc);
101 	if (current_cert != NULL) {
102 		X509_NAME_print_ex_fp(stderr,
103 		    X509_get_subject_name(current_cert), 0,
104 		    XN_FLAG_ONELINE);
105 		fprintf(stderr, "\n");
106 	}
107 
108 	verify_err = X509_STORE_CTX_get_error(xsc);
109 	if (verify_err != X509_V_OK) {
110 		fprintf(stderr, "verify error at depth %d: %s\n",
111 		    X509_STORE_CTX_get_error_depth(xsc),
112 		    X509_verify_cert_error_string(verify_err));
113 	}
114 
115 	return ok;
116 }
117 
118 static void
verify_cert(const char * roots_file,const char * intermediate_file,const char * leaf_file,int * chains,int * error,int * error_depth,int mode,ASN1_OBJECT * policy_oid,ASN1_OBJECT * policy_oid2,int verify_flags)119 verify_cert(const char *roots_file, const char *intermediate_file,
120     const char *leaf_file, int *chains, int *error, int *error_depth,
121     int mode, ASN1_OBJECT *policy_oid, ASN1_OBJECT *policy_oid2,
122     int verify_flags)
123 {
124 	STACK_OF(X509) *roots = NULL, *bundle = NULL;
125 	X509_STORE_CTX *xsc = NULL;
126 	X509_STORE *store = NULL;
127 	X509 *leaf = NULL;
128 	int flags, ret;
129 
130 	*chains = 0;
131 	*error = 0;
132 	*error_depth = 0;
133 
134 	if (!certs_from_file(roots_file, &roots))
135 		errx(1, "failed to load roots from '%s'", roots_file);
136 	if (!certs_from_file(leaf_file, &bundle))
137 		errx(1, "failed to load leaf from '%s'", leaf_file);
138 	if (intermediate_file != NULL && !certs_from_file(intermediate_file,
139 	    &bundle))
140 		errx(1, "failed to load intermediate from '%s'",
141 		    intermediate_file);
142 	if (sk_X509_num(bundle) < 1)
143 		errx(1, "not enough certs in bundle");
144 	leaf = sk_X509_shift(bundle);
145 
146 	if ((xsc = X509_STORE_CTX_new()) == NULL)
147 		errx(1, "X509_STORE_CTX");
148 	if (!X509_STORE_CTX_init(xsc, store, leaf, bundle)) {
149 		ERR_print_errors_fp(stderr);
150 		errx(1, "failed to init store context");
151 	}
152 
153 	flags = X509_V_FLAG_POLICY_CHECK;
154 	flags |= verify_flags;
155 	if (mode == MODE_LEGACY_VFY)
156 		flags |= X509_V_FLAG_LEGACY_VERIFY;
157 	X509_STORE_CTX_set_flags(xsc, flags);
158 
159 	if (verbose)
160 		X509_STORE_CTX_set_verify_cb(xsc, verify_cert_cb);
161 	X509_STORE_CTX_set0_trusted_stack(xsc, roots);
162 
163 	if (policy_oid != NULL) {
164 		X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(xsc);
165 		ASN1_OBJECT *copy = OBJ_dup(policy_oid);
166 		X509_VERIFY_PARAM_add0_policy(param, copy);
167 	}
168 	if (policy_oid2 != NULL) {
169 		X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(xsc);
170 		ASN1_OBJECT *copy = OBJ_dup(policy_oid2);
171 		X509_VERIFY_PARAM_add0_policy(param, copy);
172 	}
173 
174 	ret = X509_verify_cert(xsc);
175 
176 	*error = X509_STORE_CTX_get_error(xsc);
177 	*error_depth = X509_STORE_CTX_get_error_depth(xsc);
178 
179 	if (ret == 1) {
180 		*chains = 1; /* XXX */
181 		goto done;
182 	}
183 
184 	if (*error == 0)
185 		errx(1, "Error unset on failure!");
186 
187 	fprintf(stderr, "failed to verify at %d: %s\n",
188 	    *error_depth, X509_verify_cert_error_string(*error));
189 
190  done:
191 	sk_X509_pop_free(roots, X509_free);
192 	sk_X509_pop_free(bundle, X509_free);
193 	X509_STORE_free(store);
194 	X509_STORE_CTX_free(xsc);
195 	X509_free(leaf);
196 }
197 
198 struct verify_cert_test {
199 	const char *id;
200 	const char *root_file;
201 	const char *intermediate_file;
202 	const char *leaf_file;
203 	const char *policy_oid_to_check;
204 	const char *policy_oid_to_check2;
205 	int want_chains;
206 	int want_error;
207 	int want_error_depth;
208 	int want_legacy_error;
209 	int want_legacy_error_depth;
210 	int failing;
211 	int verify_flags;
212 };
213 
214 struct verify_cert_test verify_cert_tests[] = {
215 	/*
216 	 * Comments here are from boringssl/crypto/x509/x509_test.cc
217 	 * certs were generated by
218 	 * boringssl/crypto/x509/test/make_policy_certs.go
219 	 */
220 
221 	/* The chain is good for |oid1| and |oid2|, but not |oid3|. */
222 	{
223 		.id = "nothing  in 1 and 2",
224 		.root_file = CERTSDIR "/" "policy_root.pem",
225 		.intermediate_file = CERTSDIR "/" "policy_intermediate.pem",
226 		.leaf_file = CERTSDIR "/" "policy_leaf.pem",
227 		.want_chains = 1,
228 		.verify_flags = X509_V_FLAG_EXPLICIT_POLICY,
229 	},
230 	{
231 		.id = "1, in 1 and 2",
232 		.root_file = CERTSDIR "/" "policy_root.pem",
233 		.intermediate_file = CERTSDIR "/" "policy_intermediate.pem",
234 		.leaf_file = CERTSDIR "/" "policy_leaf.pem",
235 		.policy_oid_to_check = OID1,
236 		.want_chains = 1,
237 		.verify_flags = X509_V_FLAG_EXPLICIT_POLICY,
238 	},
239 	{
240 		.id = "2, in 1 and 2",
241 		.root_file = CERTSDIR "/" "policy_root.pem",
242 		.intermediate_file = CERTSDIR "/" "policy_intermediate.pem",
243 		.leaf_file = CERTSDIR "/" "policy_leaf.pem",
244 		.policy_oid_to_check = OID2,
245 		.want_chains = 1,
246 		.verify_flags = X509_V_FLAG_EXPLICIT_POLICY,
247 	},
248 	{
249 		.id = "3, in 1 and 2",
250 		.root_file = CERTSDIR "/" "policy_root.pem",
251 		.intermediate_file = CERTSDIR "/" "policy_intermediate.pem",
252 		.leaf_file = CERTSDIR "/" "policy_leaf.pem",
253 		.policy_oid_to_check = OID3,
254 		.want_chains = 0,
255 		.verify_flags = X509_V_FLAG_EXPLICIT_POLICY,
256 		.want_error = X509_V_ERR_NO_EXPLICIT_POLICY,
257 		.want_error_depth = 0,
258 		.want_legacy_error = X509_V_ERR_NO_EXPLICIT_POLICY,
259 		.want_legacy_error_depth = 0,
260 	},
261 	{
262 		.id = "1 and 2, in 1 and 2",
263 		.root_file = CERTSDIR "/" "policy_root.pem",
264 		.intermediate_file = CERTSDIR "/" "policy_intermediate.pem",
265 		.leaf_file = CERTSDIR "/" "policy_leaf.pem",
266 		.policy_oid_to_check = OID1,
267 		.policy_oid_to_check2 = OID2,
268 		.want_chains = 1,
269 		.verify_flags = X509_V_FLAG_EXPLICIT_POLICY,
270 	},
271 	{
272 		.id = "1 and 3, in 1 and 2",
273 		.root_file = CERTSDIR "/" "policy_root.pem",
274 		.intermediate_file = CERTSDIR "/" "policy_intermediate.pem",
275 		.leaf_file = CERTSDIR "/" "policy_leaf.pem",
276 		.policy_oid_to_check = OID1,
277 		.policy_oid_to_check2 = OID3,
278 		.want_chains = 1,
279 	},
280 	/*  The policy extension cannot be parsed. */
281 	{
282 		.id = "1 in invalid intermediate policy",
283 		.root_file = CERTSDIR "/" "policy_root.pem",
284 		.intermediate_file = CERTSDIR "/" "policy_intermediate_invalid.pem",
285 		.leaf_file = CERTSDIR "/" "policy_leaf.pem",
286 		.policy_oid_to_check = OID1,
287 		.want_chains = 0,
288 		.verify_flags = X509_V_FLAG_EXPLICIT_POLICY,
289 		.want_error = X509_V_ERR_INVALID_POLICY_EXTENSION,
290 		.want_error_depth = 0,
291 		.want_legacy_error = X509_V_ERR_INVALID_POLICY_EXTENSION,
292 		.want_legacy_error_depth = 0,
293 	},
294 	{
295 		.id = "invalid intermediate",
296 		.root_file = CERTSDIR "/" "policy_root.pem",
297 		.intermediate_file = CERTSDIR "/" "policy_intermediate_invalid.pem",
298 		.leaf_file = CERTSDIR "/" "policy_leaf.pem",
299 		.want_chains = 0,
300 		.verify_flags = X509_V_FLAG_EXPLICIT_POLICY,
301 		.want_error = X509_V_ERR_INVALID_POLICY_EXTENSION,
302 		.want_error_depth = 0,
303 		.want_legacy_error = X509_V_ERR_INVALID_POLICY_EXTENSION,
304 		.want_legacy_error_depth = 0,
305 	},
306 	{
307 		.id = "1 in invalid policy in leaf",
308 		.root_file = CERTSDIR "/" "policy_root.pem",
309 		.intermediate_file = CERTSDIR "/" "policy_intermediate.pem",
310 		.leaf_file = CERTSDIR "/" "policy_leaf_invalid.pem",
311 		.policy_oid_to_check = OID1,
312 		.want_chains = 0,
313 		.verify_flags = X509_V_FLAG_EXPLICIT_POLICY,
314 		.want_error = X509_V_ERR_INVALID_POLICY_EXTENSION,
315 		.want_error_depth = 0,
316 		.want_legacy_error = X509_V_ERR_INVALID_POLICY_EXTENSION,
317 		.want_legacy_error_depth = 0,
318 	},
319 	{
320 		.id = "invalid leaf",
321 		.root_file = CERTSDIR "/" "policy_root.pem",
322 		.intermediate_file = CERTSDIR "/" "policy_intermediate.pem",
323 		.leaf_file = CERTSDIR "/" "policy_leaf_invalid.pem",
324 		.want_chains = 0,
325 		.verify_flags = X509_V_FLAG_EXPLICIT_POLICY,
326 		.want_error = X509_V_ERR_INVALID_POLICY_EXTENSION,
327 		.want_error_depth = 0,
328 		.want_legacy_error = X509_V_ERR_INVALID_POLICY_EXTENSION,
329 		.want_legacy_error_depth = 0,
330 	},
331 	{
332 		.id = "invalid leaf without explicit policy",
333 		.root_file = CERTSDIR "/" "policy_root.pem",
334 		.intermediate_file = CERTSDIR "/" "policy_intermediate.pem",
335 		.leaf_file = CERTSDIR "/" "policy_leaf_invalid.pem",
336 		.want_chains = 0,
337 		.want_error = X509_V_ERR_INVALID_POLICY_EXTENSION,
338 		.want_error_depth = 0,
339 		.want_legacy_error = X509_V_ERR_INVALID_POLICY_EXTENSION,
340 		.want_legacy_error_depth = 0,
341 	},
342 	/*  There is a duplicate policy in the leaf policy extension. */
343 	{
344 		.id = "1 in duplicate policy extension in leaf",
345 		.root_file = CERTSDIR "/" "policy_root.pem",
346 		.intermediate_file = CERTSDIR "/" "policy_intermediate.pem",
347 		.leaf_file = CERTSDIR "/" "policy_leaf_duplicate.pem",
348 		.policy_oid_to_check = OID1,
349 		.want_chains = 0,
350 		.verify_flags = X509_V_FLAG_EXPLICIT_POLICY,
351 		.want_error = X509_V_ERR_INVALID_POLICY_EXTENSION,
352 		.want_error_depth = 0,
353 		.want_legacy_error = X509_V_ERR_INVALID_POLICY_EXTENSION,
354 		.want_legacy_error_depth = 0,
355 	},
356 	/*  There is a duplicate policy in the intermediate policy extension. */
357 	{
358 		.id = "1 in duplicate policy extension in intermediate",
359 		.root_file = CERTSDIR "/" "policy_root.pem",
360 		.intermediate_file = CERTSDIR "/" "policy_intermediate_duplicate.pem",
361 		.leaf_file = CERTSDIR "/" "policy_leaf.pem",
362 		.policy_oid_to_check = OID1,
363 		.want_chains = 0,
364 		.verify_flags = X509_V_FLAG_EXPLICIT_POLICY,
365 		.want_error = X509_V_ERR_INVALID_POLICY_EXTENSION,
366 		.want_error_depth = 0,
367 		.want_legacy_error = X509_V_ERR_INVALID_POLICY_EXTENSION,
368 		.want_legacy_error_depth = 0,
369 	},
370 	/*
371 	 * Without |X509_V_FLAG_EXPLICIT_POLICY|, the policy tree is built and
372 	 * intersected with user-specified policies, but it is not required to result
373 	 * in any valid policies.
374 	 */
375 	{
376 		.id = "nothing with explicit_policy unset",
377 		.root_file = CERTSDIR "/" "policy_root.pem",
378 		.intermediate_file = CERTSDIR "/" "policy_intermediate.pem",
379 		.leaf_file = CERTSDIR "/" "policy_leaf.pem",
380 		.want_chains = 1,
381 	},
382 	{
383 		.id = "oid3 with explicit_policy unset",
384 		.root_file = CERTSDIR "/" "policy_root.pem",
385 		.intermediate_file = CERTSDIR "/" "policy_intermediate.pem",
386 		.leaf_file = CERTSDIR "/" "policy_leaf.pem",
387 		.policy_oid_to_check = OID3,
388 		.want_chains = 1,
389 	},
390 	/*  However, a CA with policy constraints can require an explicit policy. */
391 	{
392 		.id = "oid1 with explicit_policy unset, intermediate requiring policy",
393 		.root_file = CERTSDIR "/" "policy_root.pem",
394 		.intermediate_file = CERTSDIR "/" "policy_intermediate_require.pem",
395 		.leaf_file = CERTSDIR "/" "policy_leaf.pem",
396 		.policy_oid_to_check = OID1,
397 		.want_chains = 1,
398 	},
399 	{
400 		.id = "oid3 with explicit_policy unset, intermediate requiring policy",
401 		.root_file = CERTSDIR "/" "policy_root.pem",
402 		.intermediate_file = CERTSDIR "/" "policy_intermediate_require.pem",
403 		.leaf_file = CERTSDIR "/" "policy_leaf.pem",
404 		.policy_oid_to_check = OID3,
405 		.want_chains = 0,
406 		.want_error = X509_V_ERR_NO_EXPLICIT_POLICY,
407 		.want_error_depth = 0,
408 		.want_legacy_error = X509_V_ERR_NO_EXPLICIT_POLICY,
409 		.want_legacy_error_depth = 0,
410 	},
411 	/*
412 	 * requireExplicitPolicy applies even if the application does not configure a
413 	 * user-initial-policy-set. If the validation results in no policies, the
414 	 * chain is invalid.
415 	 */
416 	{
417 		.id = "nothing explict_policy unset, with intermediate requiring policy",
418 		.root_file = CERTSDIR "/" "policy_root.pem",
419 		.intermediate_file = CERTSDIR "/" "policy_intermediate_require.pem",
420 		.leaf_file = CERTSDIR "/" "policy_leaf_none.pem",
421 		.want_chains = 0,
422 		.want_error = X509_V_ERR_NO_EXPLICIT_POLICY,
423 		.want_error_depth = 0,
424 		.want_legacy_error = X509_V_ERR_NO_EXPLICIT_POLICY,
425 		.want_legacy_error_depth = 0,
426 	},
427 	/* A leaf can also set requireExplicitPolicy but should work with none */
428 	{
429 		.id = "nothing explicit_policy unset, with leaf requiring policy",
430 		.root_file = CERTSDIR "/" "policy_root.pem",
431 		.intermediate_file = CERTSDIR "/" "policy_intermediate.pem",
432 		.leaf_file = CERTSDIR "/" "policy_leaf_require.pem",
433 		.want_chains = 1,
434 	},
435 	/* A leaf can also set requireExplicitPolicy but should fail with policy */
436 	{
437 		.id = "oid3, explicit policy unset,  with leaf requiring policy",
438 		.root_file = CERTSDIR "/" "policy_root.pem",
439 		.intermediate_file = CERTSDIR "/" "policy_intermediate.pem",
440 		.leaf_file = CERTSDIR "/" "policy_leaf_require.pem",
441 		.policy_oid_to_check = OID3,
442 		.want_chains = 0,
443 		.want_error = X509_V_ERR_NO_EXPLICIT_POLICY,
444 		.want_error_depth = 0,
445 		.want_legacy_error = X509_V_ERR_NO_EXPLICIT_POLICY,
446 		.want_legacy_error_depth = 0,
447 	},
448 	/*
449 	 * requireExplicitPolicy is a count of certificates to skip. If the value is
450 	 * not zero by the end of the chain, it doesn't count.
451 	 */
452 	{
453 		.id = "oid3, with intermediate requiring explicit depth 1",
454 		.root_file = CERTSDIR "/" "policy_root.pem",
455 		.intermediate_file = CERTSDIR "/" "policy_intermediate_require1.pem",
456 		.leaf_file = CERTSDIR "/" "policy_leaf.pem",
457 		.policy_oid_to_check = OID3,
458 		.verify_flags = X509_V_FLAG_EXPLICIT_POLICY,
459 		.want_chains = 0,
460 		.want_error = X509_V_ERR_NO_EXPLICIT_POLICY,
461 		.want_error_depth = 0,
462 		.want_legacy_error = X509_V_ERR_NO_EXPLICIT_POLICY,
463 		.want_legacy_error_depth = 0,
464 	},
465 	{
466 		.id = "oid3, with intermediate requiring explicit depth 2",
467 		.root_file = CERTSDIR "/" "policy_root.pem",
468 		.intermediate_file = CERTSDIR "/" "policy_intermediate_require2.pem",
469 		.leaf_file = CERTSDIR "/" "policy_leaf.pem",
470 		.policy_oid_to_check = OID3,
471 		.want_chains = 1,
472 	},
473 	{
474 		.id = "oid3, with leaf requiring explicit depth 1",
475 		.root_file = CERTSDIR "/" "policy_root.pem",
476 		.intermediate_file = CERTSDIR "/" "policy_intermediate.pem",
477 		.leaf_file = CERTSDIR "/" "policy_leaf_require1.pem",
478 		.policy_oid_to_check = OID3,
479 		.want_chains = 1,
480 	},
481 	/*
482 	 * If multiple certificates specify the constraint, the more constrained value
483 	 * wins.
484 	 */
485 	{
486 		.id = "oid3, with leaf and intermediate requiring explicit depth 1",
487 		.root_file = CERTSDIR "/" "policy_root.pem",
488 		.intermediate_file = CERTSDIR "/" "policy_intermediate_require1.pem",
489 		.leaf_file = CERTSDIR "/" "policy_leaf_require1.pem",
490 		.policy_oid_to_check = OID3,
491 		.verify_flags = X509_V_FLAG_EXPLICIT_POLICY,
492 		.want_chains = 0,
493 		.want_error = X509_V_ERR_NO_EXPLICIT_POLICY,
494 		.want_error_depth = 0,
495 		.want_legacy_error = X509_V_ERR_NO_EXPLICIT_POLICY,
496 		.want_legacy_error_depth = 0,
497 	},
498 	{
499 		.id = "oid3, with leaf requiring explicit depth 1 and intermediate depth 2",
500 		.root_file = CERTSDIR "/" "policy_root.pem",
501 		.intermediate_file = CERTSDIR "/" "policy_intermediate_require2.pem",
502 		.leaf_file = CERTSDIR "/" "policy_leaf_require.pem",
503 		.policy_oid_to_check = OID3,
504 		.verify_flags = X509_V_FLAG_EXPLICIT_POLICY,
505 		.want_chains = 0,
506 		.want_error = X509_V_ERR_NO_EXPLICIT_POLICY,
507 		.want_error_depth = 0,
508 		.want_legacy_error = X509_V_ERR_NO_EXPLICIT_POLICY,
509 		.want_legacy_error_depth = 0,
510 	},
511 	/*
512 	 * An intermediate that requires an explicit policy, but then specifies no
513 	 * policies should fail verification as a result.
514 	 */
515 	{
516 		.id = "oid1 with explicit_policy unset, intermediate requiring policy but specifying none",
517 		.root_file = CERTSDIR "/" "policy_root.pem",
518 		.intermediate_file = CERTSDIR "/" "policy_intermediate_require_no_policies.pem",
519 		.leaf_file = CERTSDIR "/" "policy_leaf.pem",
520 		.policy_oid_to_check = OID3,
521 		.want_chains = 0,
522 		.want_error = X509_V_ERR_NO_EXPLICIT_POLICY,
523 		.want_error_depth = 0,
524 		.want_legacy_error = X509_V_ERR_NO_EXPLICIT_POLICY,
525 		.want_legacy_error_depth = 0,
526 	},
527 	/*
528 	 * A constrained intermediate's policy extension has a duplicate policy, which
529 	 * is invalid. Historically this, and the above case, leaked memory.
530 	 */
531 	{
532 		.id = "oid1 with explicit_policy unset, intermediate requiring policy but has duplicate",
533 		.root_file = CERTSDIR "/" "policy_root.pem",
534 		.intermediate_file = CERTSDIR "/" "policy_intermediate_require_duplicate.pem",
535 		.leaf_file = CERTSDIR "/" "policy_leaf.pem",
536 		.policy_oid_to_check = OID3,
537 		.want_chains = 0,
538 		.want_error = X509_V_ERR_INVALID_POLICY_EXTENSION,
539 		.want_error_depth = 0,
540 		.want_legacy_error = X509_V_ERR_INVALID_POLICY_EXTENSION,
541 		.want_legacy_error_depth = 0,
542 	},
543 	/*
544 	 * The leaf asserts anyPolicy, but the intermediate does not. The resulting
545 	 * valid policies are the intersection.(and vice versa)
546 	 */
547 	{
548 		.id = "oid1, with explicit_policy set, with leaf asserting any",
549 		.root_file = CERTSDIR "/" "policy_root.pem",
550 		.intermediate_file = CERTSDIR "/" "policy_intermediate.pem",
551 		.leaf_file = CERTSDIR "/" "policy_leaf_any.pem",
552 		.policy_oid_to_check = OID1,
553 		.verify_flags = X509_V_FLAG_EXPLICIT_POLICY,
554 		.want_chains = 1,
555 	},
556 	{
557 		.id = "oid3, with explicit_policy set, with leaf asserting any",
558 		.root_file = CERTSDIR "/" "policy_root.pem",
559 		.intermediate_file = CERTSDIR "/" "policy_intermediate.pem",
560 		.leaf_file = CERTSDIR "/" "policy_leaf_any.pem",
561 		.policy_oid_to_check = OID3,
562 		.verify_flags = X509_V_FLAG_EXPLICIT_POLICY,
563 		.want_chains = 0,
564 		.want_error = X509_V_ERR_NO_EXPLICIT_POLICY,
565 		.want_error_depth = 0,
566 		.want_legacy_error = X509_V_ERR_NO_EXPLICIT_POLICY,
567 		.want_legacy_error_depth = 0,
568 	},
569 	/* Both assert anyPolicy. All policies are valid. */
570 	{
571 		.id = "oid1, with explicit_policy set, with leaf and intermediate asserting any",
572 		.root_file = CERTSDIR "/" "policy_root.pem",
573 		.intermediate_file = CERTSDIR "/" "policy_intermediate_any.pem",
574 		.leaf_file = CERTSDIR "/" "policy_leaf_any.pem",
575 		.policy_oid_to_check = OID1,
576 		.verify_flags = X509_V_FLAG_EXPLICIT_POLICY,
577 		.want_chains = 1,
578 	},
579 	{
580 		.id = "oid3, with explicit_policy set, with leaf and intermediate asserting any",
581 		.root_file = CERTSDIR "/" "policy_root.pem",
582 		.intermediate_file = CERTSDIR "/" "policy_intermediate_any.pem",
583 		.leaf_file = CERTSDIR "/" "policy_leaf_any.pem",
584 		.policy_oid_to_check = OID1,
585 		.verify_flags = X509_V_FLAG_EXPLICIT_POLICY,
586 		.want_chains = 1,
587 	},
588 	/*
589 	 * BoringSSL tests just a trust anchor but behaves differently in this corner case.
590 	 * than libressl for reasons that have nothing to do with policy (because parital
591 	 * chains and legacy verifier horror)
592 	 */
593 };
594 
595 #define N_VERIFY_CERT_TESTS \
596     (sizeof(verify_cert_tests) / sizeof(*verify_cert_tests))
597 
598 static int
verify_cert_test(int mode)599 verify_cert_test(int mode)
600 {
601 	ASN1_OBJECT *policy_oid, *policy_oid2;
602 	struct verify_cert_test *vct;
603 	int chains, error, error_depth;
604 	int failed = 0;
605 	size_t i;
606 
607 	for (i = 0; i < N_VERIFY_CERT_TESTS; i++) {
608 		vct = &verify_cert_tests[i];
609 		policy_oid = vct->policy_oid_to_check ?
610 		    OBJ_txt2obj(vct->policy_oid_to_check, 1) : NULL;
611 		policy_oid2 = vct->policy_oid_to_check2 ?
612 		    OBJ_txt2obj(vct->policy_oid_to_check2, 1) : NULL;
613 
614 		error = 0;
615 		error_depth = 0;
616 
617 		fprintf(stderr, "== Test %zu (%s)\n", i, vct->id);
618 		verify_cert(vct->root_file, vct->intermediate_file,
619 		    vct->leaf_file, &chains, &error, &error_depth,
620 		    mode, policy_oid, policy_oid2, vct->verify_flags);
621 
622 		if ((chains == 0 && vct->want_chains == 0) ||
623 		    (chains == 1 && vct->want_chains > 0)) {
624 			fprintf(stderr, "INFO: Succeeded with %d chains%s\n",
625 			    chains, vct->failing ? " (legacy failure)" : "");
626 			if (mode == MODE_LEGACY_VFY && vct->failing)
627 				failed |= 1;
628 		} else {
629 			fprintf(stderr, "FAIL: Failed with %d chains%s\n",
630 			    chains, vct->failing ? " (legacy failure)" : "");
631 			if (!vct->failing)
632 				failed |= 1;
633 		}
634 
635 		if (mode == MODE_LEGACY_VFY) {
636 			if (error != vct->want_legacy_error) {
637 				fprintf(stderr, "FAIL: Got legacy error %d, "
638 				    "want %d\n", error, vct->want_legacy_error);
639 				failed |= 1;
640 			}
641 			if (error_depth != vct->want_legacy_error_depth) {
642 				fprintf(stderr, "FAIL: Got legacy error depth "
643 				    "%d, want %d\n", error_depth,
644 				    vct->want_legacy_error_depth);
645 				failed |= 1;
646 			}
647 		}
648 		fprintf(stderr, "\n");
649 		ASN1_OBJECT_free(policy_oid);
650 		ASN1_OBJECT_free(policy_oid2);
651 	}
652 	return failed;
653 }
654 
655 int
main(int argc,char ** argv)656 main(int argc, char **argv)
657 {
658 	int failed = 0;
659 
660 	fprintf(stderr, "\n\nTesting legacy x509_vfy\n");
661 	failed |= verify_cert_test(MODE_LEGACY_VFY);
662 	fprintf(stderr, "\n\nTesting modern x509_vfy\n");
663 	failed |= verify_cert_test(MODE_MODERN_VFY);
664 	/* New verifier does not do policy goop at the moment */
665 
666 	return (failed);
667 }
668