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 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 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 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 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 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 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