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