1*6822f9c8Santon /* $OpenBSD: policy.c,v 1.13 2024/08/23 12:56:26 anton Exp $ */ 211e92b75Sbeck /* 311e92b75Sbeck * Copyright (c) 2020 Joel Sing <jsing@openbsd.org> 4b4d45f74Sbeck * Copyright (c) 2020-2023 Bob Beck <beck@openbsd.org> 511e92b75Sbeck * 611e92b75Sbeck * Permission to use, copy, modify, and distribute this software for any 711e92b75Sbeck * purpose with or without fee is hereby granted, provided that the above 811e92b75Sbeck * copyright notice and this permission notice appear in all copies. 911e92b75Sbeck * 1011e92b75Sbeck * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1111e92b75Sbeck * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1211e92b75Sbeck * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1311e92b75Sbeck * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1411e92b75Sbeck * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1511e92b75Sbeck * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1611e92b75Sbeck * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1711e92b75Sbeck */ 1811e92b75Sbeck 1911e92b75Sbeck #include <err.h> 2011e92b75Sbeck #include <string.h> 2111e92b75Sbeck 2211e92b75Sbeck #include <openssl/bio.h> 2311e92b75Sbeck #include <openssl/crypto.h> 2411e92b75Sbeck #include <openssl/err.h> 2511e92b75Sbeck #include <openssl/pem.h> 2611e92b75Sbeck #include <openssl/x509.h> 2711e92b75Sbeck #include <openssl/x509v3.h> 2811e92b75Sbeck 2911e92b75Sbeck #include "x509_verify.h" 3011e92b75Sbeck 3111e92b75Sbeck #define MODE_MODERN_VFY 0 3211e92b75Sbeck #define MODE_MODERN_VFY_DIR 1 3311e92b75Sbeck #define MODE_LEGACY_VFY 2 3411e92b75Sbeck 3511e92b75Sbeck static int verbose = 1; 3611e92b75Sbeck 3711e92b75Sbeck #define OID1 "1.2.840.113554.4.1.72585.2.1" 3811e92b75Sbeck #define OID2 "1.2.840.113554.4.1.72585.2.2" 3911e92b75Sbeck #define OID3 "1.2.840.113554.4.1.72585.2.3" 4011e92b75Sbeck #define OID4 "1.2.840.113554.4.1.72585.2.4" 4111e92b75Sbeck #define OID5 "1.2.840.113554.4.1.72585.2.5" 4211e92b75Sbeck 4311e92b75Sbeck #ifndef CERTSDIR 4411e92b75Sbeck #define CERTSDIR "." 4511e92b75Sbeck #endif 4611e92b75Sbeck 4711e92b75Sbeck static int 4811e92b75Sbeck passwd_cb(char *buf, int size, int rwflag, void *u) 4911e92b75Sbeck { 5011e92b75Sbeck memset(buf, 0, size); 5111e92b75Sbeck return (0); 5211e92b75Sbeck } 5311e92b75Sbeck 5411e92b75Sbeck static int 5511e92b75Sbeck certs_from_file(const char *filename, STACK_OF(X509) **certs) 5611e92b75Sbeck { 5711e92b75Sbeck STACK_OF(X509_INFO) *xis = NULL; 5811e92b75Sbeck STACK_OF(X509) *xs = NULL; 5911e92b75Sbeck BIO *bio = NULL; 6011e92b75Sbeck X509 *x; 6111e92b75Sbeck int i; 6211e92b75Sbeck 6311e92b75Sbeck if (*certs == NULL) { 6411e92b75Sbeck if ((xs = sk_X509_new_null()) == NULL) 6511e92b75Sbeck errx(1, "failed to create X509 stack"); 6611e92b75Sbeck } else { 6711e92b75Sbeck xs = *certs; 6811e92b75Sbeck } 6911e92b75Sbeck if ((bio = BIO_new_file(filename, "r")) == NULL) { 7011e92b75Sbeck ERR_print_errors_fp(stderr); 7111e92b75Sbeck errx(1, "failed to create bio"); 7211e92b75Sbeck } 7311e92b75Sbeck if ((xis = PEM_X509_INFO_read_bio(bio, NULL, passwd_cb, NULL)) == NULL) 7411e92b75Sbeck errx(1, "failed to read PEM"); 7511e92b75Sbeck 7611e92b75Sbeck for (i = 0; i < sk_X509_INFO_num(xis); i++) { 7711e92b75Sbeck if ((x = sk_X509_INFO_value(xis, i)->x509) == NULL) 7811e92b75Sbeck continue; 7911e92b75Sbeck if (!sk_X509_push(xs, x)) 8011e92b75Sbeck errx(1, "failed to push X509"); 8111e92b75Sbeck X509_up_ref(x); 8211e92b75Sbeck } 8311e92b75Sbeck 8411e92b75Sbeck *certs = xs; 8511e92b75Sbeck xs = NULL; 8611e92b75Sbeck 8711e92b75Sbeck sk_X509_INFO_pop_free(xis, X509_INFO_free); 8811e92b75Sbeck sk_X509_pop_free(xs, X509_free); 8911e92b75Sbeck BIO_free(bio); 9011e92b75Sbeck 9111e92b75Sbeck return 1; 9211e92b75Sbeck } 9311e92b75Sbeck 9411e92b75Sbeck static int 9511e92b75Sbeck verify_cert_cb(int ok, X509_STORE_CTX *xsc) 9611e92b75Sbeck { 9711e92b75Sbeck X509 *current_cert; 9811e92b75Sbeck int verify_err; 9911e92b75Sbeck 10011e92b75Sbeck current_cert = X509_STORE_CTX_get_current_cert(xsc); 10111e92b75Sbeck if (current_cert != NULL) { 10211e92b75Sbeck X509_NAME_print_ex_fp(stderr, 10311e92b75Sbeck X509_get_subject_name(current_cert), 0, 10411e92b75Sbeck XN_FLAG_ONELINE); 10511e92b75Sbeck fprintf(stderr, "\n"); 10611e92b75Sbeck } 10711e92b75Sbeck 10811e92b75Sbeck verify_err = X509_STORE_CTX_get_error(xsc); 10911e92b75Sbeck if (verify_err != X509_V_OK) { 11011e92b75Sbeck fprintf(stderr, "verify error at depth %d: %s\n", 11111e92b75Sbeck X509_STORE_CTX_get_error_depth(xsc), 11211e92b75Sbeck X509_verify_cert_error_string(verify_err)); 11311e92b75Sbeck } 11411e92b75Sbeck 11511e92b75Sbeck return ok; 11611e92b75Sbeck } 11711e92b75Sbeck 11811e92b75Sbeck static void 11911e92b75Sbeck verify_cert(const char *roots_file, const char *intermediate_file, 12011e92b75Sbeck const char *leaf_file, int *chains, int *error, int *error_depth, 12187535099Sbeck int mode, ASN1_OBJECT *policy_oid, ASN1_OBJECT *policy_oid2, 12287535099Sbeck int verify_flags) 12311e92b75Sbeck { 12411e92b75Sbeck STACK_OF(X509) *roots = NULL, *bundle = NULL; 12511e92b75Sbeck X509_STORE_CTX *xsc = NULL; 12611e92b75Sbeck X509_STORE *store = NULL; 12711e92b75Sbeck X509 *leaf = NULL; 128630f168fStb int flags, ret; 12911e92b75Sbeck 13011e92b75Sbeck *chains = 0; 13111e92b75Sbeck *error = 0; 13211e92b75Sbeck *error_depth = 0; 13311e92b75Sbeck 13411e92b75Sbeck if (!certs_from_file(roots_file, &roots)) 13511e92b75Sbeck errx(1, "failed to load roots from '%s'", roots_file); 13611e92b75Sbeck if (!certs_from_file(leaf_file, &bundle)) 13711e92b75Sbeck errx(1, "failed to load leaf from '%s'", leaf_file); 13811e92b75Sbeck if (intermediate_file != NULL && !certs_from_file(intermediate_file, 13911e92b75Sbeck &bundle)) 14011e92b75Sbeck errx(1, "failed to load intermediate from '%s'", 14111e92b75Sbeck intermediate_file); 14211e92b75Sbeck if (sk_X509_num(bundle) < 1) 14311e92b75Sbeck errx(1, "not enough certs in bundle"); 14411e92b75Sbeck leaf = sk_X509_shift(bundle); 14511e92b75Sbeck 14611e92b75Sbeck if ((xsc = X509_STORE_CTX_new()) == NULL) 14711e92b75Sbeck errx(1, "X509_STORE_CTX"); 14811e92b75Sbeck if (!X509_STORE_CTX_init(xsc, store, leaf, bundle)) { 14911e92b75Sbeck ERR_print_errors_fp(stderr); 15011e92b75Sbeck errx(1, "failed to init store context"); 15111e92b75Sbeck } 15211e92b75Sbeck 153630f168fStb flags = X509_V_FLAG_POLICY_CHECK; 15487535099Sbeck flags |= verify_flags; 15511e92b75Sbeck if (mode == MODE_LEGACY_VFY) 15611e92b75Sbeck flags |= X509_V_FLAG_LEGACY_VERIFY; 15711e92b75Sbeck X509_STORE_CTX_set_flags(xsc, flags); 15811e92b75Sbeck 15911e92b75Sbeck if (verbose) 16011e92b75Sbeck X509_STORE_CTX_set_verify_cb(xsc, verify_cert_cb); 16111e92b75Sbeck X509_STORE_CTX_set0_trusted_stack(xsc, roots); 16211e92b75Sbeck 16311e92b75Sbeck if (policy_oid != NULL) { 16411e92b75Sbeck X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(xsc); 16511e92b75Sbeck ASN1_OBJECT *copy = OBJ_dup(policy_oid); 16611e92b75Sbeck X509_VERIFY_PARAM_add0_policy(param, copy); 16711e92b75Sbeck } 16811e92b75Sbeck if (policy_oid2 != NULL) { 16911e92b75Sbeck X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(xsc); 17011e92b75Sbeck ASN1_OBJECT *copy = OBJ_dup(policy_oid2); 17111e92b75Sbeck X509_VERIFY_PARAM_add0_policy(param, copy); 17211e92b75Sbeck } 17311e92b75Sbeck 17411e92b75Sbeck ret = X509_verify_cert(xsc); 17511e92b75Sbeck 17611e92b75Sbeck *error = X509_STORE_CTX_get_error(xsc); 17711e92b75Sbeck *error_depth = X509_STORE_CTX_get_error_depth(xsc); 17811e92b75Sbeck 17911e92b75Sbeck if (ret == 1) { 18011e92b75Sbeck *chains = 1; /* XXX */ 18111e92b75Sbeck goto done; 18211e92b75Sbeck } 18311e92b75Sbeck 18411e92b75Sbeck if (*error == 0) 185*6822f9c8Santon errx(1, "Error unset on failure!"); 18611e92b75Sbeck 18711e92b75Sbeck fprintf(stderr, "failed to verify at %d: %s\n", 18811e92b75Sbeck *error_depth, X509_verify_cert_error_string(*error)); 18911e92b75Sbeck 19011e92b75Sbeck done: 19111e92b75Sbeck sk_X509_pop_free(roots, X509_free); 19211e92b75Sbeck sk_X509_pop_free(bundle, X509_free); 19311e92b75Sbeck X509_STORE_free(store); 19411e92b75Sbeck X509_STORE_CTX_free(xsc); 19511e92b75Sbeck X509_free(leaf); 19611e92b75Sbeck } 19711e92b75Sbeck 19811e92b75Sbeck struct verify_cert_test { 19911e92b75Sbeck const char *id; 20011e92b75Sbeck const char *root_file; 20111e92b75Sbeck const char *intermediate_file; 20211e92b75Sbeck const char *leaf_file; 20311e92b75Sbeck const char *policy_oid_to_check; 20411e92b75Sbeck const char *policy_oid_to_check2; 20511e92b75Sbeck int want_chains; 20611e92b75Sbeck int want_error; 20711e92b75Sbeck int want_error_depth; 20811e92b75Sbeck int want_legacy_error; 20911e92b75Sbeck int want_legacy_error_depth; 21011e92b75Sbeck int failing; 21187535099Sbeck int verify_flags; 21211e92b75Sbeck }; 21311e92b75Sbeck 21411e92b75Sbeck struct verify_cert_test verify_cert_tests[] = { 215b4d45f74Sbeck /* 216b4d45f74Sbeck * Comments here are from boringssl/crypto/x509/x509_test.cc 217b4d45f74Sbeck * certs were generated by 218b4d45f74Sbeck * boringssl/crypto/x509/test/make_policy_certs.go 219b4d45f74Sbeck */ 220b4d45f74Sbeck 221b4d45f74Sbeck /* The chain is good for |oid1| and |oid2|, but not |oid3|. */ 22211e92b75Sbeck { 22311e92b75Sbeck .id = "nothing in 1 and 2", 22411e92b75Sbeck .root_file = CERTSDIR "/" "policy_root.pem", 22511e92b75Sbeck .intermediate_file = CERTSDIR "/" "policy_intermediate.pem", 22611e92b75Sbeck .leaf_file = CERTSDIR "/" "policy_leaf.pem", 22711e92b75Sbeck .want_chains = 1, 22887535099Sbeck .verify_flags = X509_V_FLAG_EXPLICIT_POLICY, 22911e92b75Sbeck }, 23011e92b75Sbeck { 23111e92b75Sbeck .id = "1, in 1 and 2", 23211e92b75Sbeck .root_file = CERTSDIR "/" "policy_root.pem", 23311e92b75Sbeck .intermediate_file = CERTSDIR "/" "policy_intermediate.pem", 23411e92b75Sbeck .leaf_file = CERTSDIR "/" "policy_leaf.pem", 23511e92b75Sbeck .policy_oid_to_check = OID1, 23611e92b75Sbeck .want_chains = 1, 23787535099Sbeck .verify_flags = X509_V_FLAG_EXPLICIT_POLICY, 23811e92b75Sbeck }, 23911e92b75Sbeck { 24011e92b75Sbeck .id = "2, in 1 and 2", 24111e92b75Sbeck .root_file = CERTSDIR "/" "policy_root.pem", 24211e92b75Sbeck .intermediate_file = CERTSDIR "/" "policy_intermediate.pem", 24311e92b75Sbeck .leaf_file = CERTSDIR "/" "policy_leaf.pem", 24411e92b75Sbeck .policy_oid_to_check = OID2, 24511e92b75Sbeck .want_chains = 1, 24687535099Sbeck .verify_flags = X509_V_FLAG_EXPLICIT_POLICY, 24711e92b75Sbeck }, 24811e92b75Sbeck { 24911e92b75Sbeck .id = "3, in 1 and 2", 25011e92b75Sbeck .root_file = CERTSDIR "/" "policy_root.pem", 25111e92b75Sbeck .intermediate_file = CERTSDIR "/" "policy_intermediate.pem", 25211e92b75Sbeck .leaf_file = CERTSDIR "/" "policy_leaf.pem", 25358ea120dSbeck .policy_oid_to_check = OID3, 25411e92b75Sbeck .want_chains = 0, 25587535099Sbeck .verify_flags = X509_V_FLAG_EXPLICIT_POLICY, 25658ea120dSbeck .want_error = X509_V_ERR_NO_EXPLICIT_POLICY, 25758ea120dSbeck .want_error_depth = 0, 25858ea120dSbeck .want_legacy_error = X509_V_ERR_NO_EXPLICIT_POLICY, 25958ea120dSbeck .want_legacy_error_depth = 0, 26011e92b75Sbeck }, 26111e92b75Sbeck { 26211e92b75Sbeck .id = "1 and 2, in 1 and 2", 26311e92b75Sbeck .root_file = CERTSDIR "/" "policy_root.pem", 26411e92b75Sbeck .intermediate_file = CERTSDIR "/" "policy_intermediate.pem", 26511e92b75Sbeck .leaf_file = CERTSDIR "/" "policy_leaf.pem", 26611e92b75Sbeck .policy_oid_to_check = OID1, 26711e92b75Sbeck .policy_oid_to_check2 = OID2, 26811e92b75Sbeck .want_chains = 1, 26987535099Sbeck .verify_flags = X509_V_FLAG_EXPLICIT_POLICY, 27011e92b75Sbeck }, 27111e92b75Sbeck { 27211e92b75Sbeck .id = "1 and 3, in 1 and 2", 27311e92b75Sbeck .root_file = CERTSDIR "/" "policy_root.pem", 27411e92b75Sbeck .intermediate_file = CERTSDIR "/" "policy_intermediate.pem", 27511e92b75Sbeck .leaf_file = CERTSDIR "/" "policy_leaf.pem", 27611e92b75Sbeck .policy_oid_to_check = OID1, 27711e92b75Sbeck .policy_oid_to_check2 = OID3, 27811e92b75Sbeck .want_chains = 1, 27911e92b75Sbeck }, 280b4d45f74Sbeck /* The policy extension cannot be parsed. */ 28111e92b75Sbeck { 2821f6bba93Stb .id = "1 in invalid intermediate policy", 28311e92b75Sbeck .root_file = CERTSDIR "/" "policy_root.pem", 28411e92b75Sbeck .intermediate_file = CERTSDIR "/" "policy_intermediate_invalid.pem", 28511e92b75Sbeck .leaf_file = CERTSDIR "/" "policy_leaf.pem", 28611e92b75Sbeck .policy_oid_to_check = OID1, 28711e92b75Sbeck .want_chains = 0, 28887535099Sbeck .verify_flags = X509_V_FLAG_EXPLICIT_POLICY, 28958ea120dSbeck .want_error = X509_V_ERR_INVALID_POLICY_EXTENSION, 29058ea120dSbeck .want_error_depth = 0, 29158ea120dSbeck .want_legacy_error = X509_V_ERR_INVALID_POLICY_EXTENSION, 29258ea120dSbeck .want_legacy_error_depth = 0, 29311e92b75Sbeck }, 29411e92b75Sbeck { 29511e92b75Sbeck .id = "invalid intermediate", 29611e92b75Sbeck .root_file = CERTSDIR "/" "policy_root.pem", 29711e92b75Sbeck .intermediate_file = CERTSDIR "/" "policy_intermediate_invalid.pem", 29811e92b75Sbeck .leaf_file = CERTSDIR "/" "policy_leaf.pem", 29911e92b75Sbeck .want_chains = 0, 30087535099Sbeck .verify_flags = X509_V_FLAG_EXPLICIT_POLICY, 30158ea120dSbeck .want_error = X509_V_ERR_INVALID_POLICY_EXTENSION, 30258ea120dSbeck .want_error_depth = 0, 30358ea120dSbeck .want_legacy_error = X509_V_ERR_INVALID_POLICY_EXTENSION, 30458ea120dSbeck .want_legacy_error_depth = 0, 30511e92b75Sbeck }, 30611e92b75Sbeck { 30711e92b75Sbeck .id = "1 in invalid policy in leaf", 30811e92b75Sbeck .root_file = CERTSDIR "/" "policy_root.pem", 30911e92b75Sbeck .intermediate_file = CERTSDIR "/" "policy_intermediate.pem", 31011e92b75Sbeck .leaf_file = CERTSDIR "/" "policy_leaf_invalid.pem", 31111e92b75Sbeck .policy_oid_to_check = OID1, 31211e92b75Sbeck .want_chains = 0, 31387535099Sbeck .verify_flags = X509_V_FLAG_EXPLICIT_POLICY, 31458ea120dSbeck .want_error = X509_V_ERR_INVALID_POLICY_EXTENSION, 31558ea120dSbeck .want_error_depth = 0, 31658ea120dSbeck .want_legacy_error = X509_V_ERR_INVALID_POLICY_EXTENSION, 31758ea120dSbeck .want_legacy_error_depth = 0, 31811e92b75Sbeck }, 31911e92b75Sbeck { 32011e92b75Sbeck .id = "invalid leaf", 32111e92b75Sbeck .root_file = CERTSDIR "/" "policy_root.pem", 32211e92b75Sbeck .intermediate_file = CERTSDIR "/" "policy_intermediate.pem", 32311e92b75Sbeck .leaf_file = CERTSDIR "/" "policy_leaf_invalid.pem", 32411e92b75Sbeck .want_chains = 0, 32587535099Sbeck .verify_flags = X509_V_FLAG_EXPLICIT_POLICY, 32658ea120dSbeck .want_error = X509_V_ERR_INVALID_POLICY_EXTENSION, 32758ea120dSbeck .want_error_depth = 0, 32858ea120dSbeck .want_legacy_error = X509_V_ERR_INVALID_POLICY_EXTENSION, 32958ea120dSbeck .want_legacy_error_depth = 0, 33011e92b75Sbeck }, 3317d883af9Sbeck { 3327d883af9Sbeck .id = "invalid leaf without explicit policy", 3337d883af9Sbeck .root_file = CERTSDIR "/" "policy_root.pem", 3347d883af9Sbeck .intermediate_file = CERTSDIR "/" "policy_intermediate.pem", 3357d883af9Sbeck .leaf_file = CERTSDIR "/" "policy_leaf_invalid.pem", 3367d883af9Sbeck .want_chains = 0, 3377d883af9Sbeck .want_error = X509_V_ERR_INVALID_POLICY_EXTENSION, 3387d883af9Sbeck .want_error_depth = 0, 3397d883af9Sbeck .want_legacy_error = X509_V_ERR_INVALID_POLICY_EXTENSION, 3407d883af9Sbeck .want_legacy_error_depth = 0, 3417d883af9Sbeck }, 342b4d45f74Sbeck /* There is a duplicate policy in the leaf policy extension. */ 34311e92b75Sbeck { 34411e92b75Sbeck .id = "1 in duplicate policy extension in leaf", 34511e92b75Sbeck .root_file = CERTSDIR "/" "policy_root.pem", 34611e92b75Sbeck .intermediate_file = CERTSDIR "/" "policy_intermediate.pem", 34711e92b75Sbeck .leaf_file = CERTSDIR "/" "policy_leaf_duplicate.pem", 34811e92b75Sbeck .policy_oid_to_check = OID1, 34911e92b75Sbeck .want_chains = 0, 35087535099Sbeck .verify_flags = X509_V_FLAG_EXPLICIT_POLICY, 35158ea120dSbeck .want_error = X509_V_ERR_INVALID_POLICY_EXTENSION, 35258ea120dSbeck .want_error_depth = 0, 35358ea120dSbeck .want_legacy_error = X509_V_ERR_INVALID_POLICY_EXTENSION, 35458ea120dSbeck .want_legacy_error_depth = 0, 35511e92b75Sbeck }, 356b4d45f74Sbeck /* There is a duplicate policy in the intermediate policy extension. */ 35711e92b75Sbeck { 35811e92b75Sbeck .id = "1 in duplicate policy extension in intermediate", 35911e92b75Sbeck .root_file = CERTSDIR "/" "policy_root.pem", 36011e92b75Sbeck .intermediate_file = CERTSDIR "/" "policy_intermediate_duplicate.pem", 36111e92b75Sbeck .leaf_file = CERTSDIR "/" "policy_leaf.pem", 36211e92b75Sbeck .policy_oid_to_check = OID1, 36311e92b75Sbeck .want_chains = 0, 36487535099Sbeck .verify_flags = X509_V_FLAG_EXPLICIT_POLICY, 36558ea120dSbeck .want_error = X509_V_ERR_INVALID_POLICY_EXTENSION, 36658ea120dSbeck .want_error_depth = 0, 36758ea120dSbeck .want_legacy_error = X509_V_ERR_INVALID_POLICY_EXTENSION, 36858ea120dSbeck .want_legacy_error_depth = 0, 36911e92b75Sbeck }, 370b4d45f74Sbeck /* 371b4d45f74Sbeck * Without |X509_V_FLAG_EXPLICIT_POLICY|, the policy tree is built and 372b4d45f74Sbeck * intersected with user-specified policies, but it is not required to result 373b4d45f74Sbeck * in any valid policies. 374b4d45f74Sbeck */ 37587535099Sbeck { 37687535099Sbeck .id = "nothing with explicit_policy unset", 37787535099Sbeck .root_file = CERTSDIR "/" "policy_root.pem", 37887535099Sbeck .intermediate_file = CERTSDIR "/" "policy_intermediate.pem", 37987535099Sbeck .leaf_file = CERTSDIR "/" "policy_leaf.pem", 38087535099Sbeck .want_chains = 1, 38187535099Sbeck }, 38287535099Sbeck { 38387535099Sbeck .id = "oid3 with explicit_policy unset", 38487535099Sbeck .root_file = CERTSDIR "/" "policy_root.pem", 38587535099Sbeck .intermediate_file = CERTSDIR "/" "policy_intermediate.pem", 38687535099Sbeck .leaf_file = CERTSDIR "/" "policy_leaf.pem", 38787535099Sbeck .policy_oid_to_check = OID3, 38887535099Sbeck .want_chains = 1, 38987535099Sbeck }, 390b4d45f74Sbeck /* However, a CA with policy constraints can require an explicit policy. */ 39187535099Sbeck { 39287535099Sbeck .id = "oid1 with explicit_policy unset, intermediate requiring policy", 39387535099Sbeck .root_file = CERTSDIR "/" "policy_root.pem", 39487535099Sbeck .intermediate_file = CERTSDIR "/" "policy_intermediate_require.pem", 39587535099Sbeck .leaf_file = CERTSDIR "/" "policy_leaf.pem", 39687535099Sbeck .policy_oid_to_check = OID1, 39787535099Sbeck .want_chains = 1, 39887535099Sbeck }, 39987535099Sbeck { 40087535099Sbeck .id = "oid3 with explicit_policy unset, intermediate requiring policy", 40187535099Sbeck .root_file = CERTSDIR "/" "policy_root.pem", 40287535099Sbeck .intermediate_file = CERTSDIR "/" "policy_intermediate_require.pem", 40387535099Sbeck .leaf_file = CERTSDIR "/" "policy_leaf.pem", 40487535099Sbeck .policy_oid_to_check = OID3, 40587535099Sbeck .want_chains = 0, 40687535099Sbeck .want_error = X509_V_ERR_NO_EXPLICIT_POLICY, 40787535099Sbeck .want_error_depth = 0, 40887535099Sbeck .want_legacy_error = X509_V_ERR_NO_EXPLICIT_POLICY, 40987535099Sbeck .want_legacy_error_depth = 0, 41087535099Sbeck }, 411b4d45f74Sbeck /* 412b4d45f74Sbeck * requireExplicitPolicy applies even if the application does not configure a 413b4d45f74Sbeck * user-initial-policy-set. If the validation results in no policies, the 414b4d45f74Sbeck * chain is invalid. 415b4d45f74Sbeck */ 41687535099Sbeck { 41787535099Sbeck .id = "nothing explict_policy unset, with intermediate requiring policy", 41887535099Sbeck .root_file = CERTSDIR "/" "policy_root.pem", 41987535099Sbeck .intermediate_file = CERTSDIR "/" "policy_intermediate_require.pem", 42087535099Sbeck .leaf_file = CERTSDIR "/" "policy_leaf_none.pem", 42187535099Sbeck .want_chains = 0, 42287535099Sbeck .want_error = X509_V_ERR_NO_EXPLICIT_POLICY, 42387535099Sbeck .want_error_depth = 0, 42487535099Sbeck .want_legacy_error = X509_V_ERR_NO_EXPLICIT_POLICY, 42587535099Sbeck .want_legacy_error_depth = 0, 42687535099Sbeck }, 427b4d45f74Sbeck /* A leaf can also set requireExplicitPolicy but should work with none */ 42887535099Sbeck { 42987535099Sbeck .id = "nothing explicit_policy unset, with leaf requiring policy", 43087535099Sbeck .root_file = CERTSDIR "/" "policy_root.pem", 43187535099Sbeck .intermediate_file = CERTSDIR "/" "policy_intermediate.pem", 43287535099Sbeck .leaf_file = CERTSDIR "/" "policy_leaf_require.pem", 43387535099Sbeck .want_chains = 1, 43487535099Sbeck }, 435b4d45f74Sbeck /* A leaf can also set requireExplicitPolicy but should fail with policy */ 43687535099Sbeck { 43787535099Sbeck .id = "oid3, explicit policy unset, with leaf requiring policy", 43887535099Sbeck .root_file = CERTSDIR "/" "policy_root.pem", 43987535099Sbeck .intermediate_file = CERTSDIR "/" "policy_intermediate.pem", 44087535099Sbeck .leaf_file = CERTSDIR "/" "policy_leaf_require.pem", 44187535099Sbeck .policy_oid_to_check = OID3, 44287535099Sbeck .want_chains = 0, 44387535099Sbeck .want_error = X509_V_ERR_NO_EXPLICIT_POLICY, 44487535099Sbeck .want_error_depth = 0, 44587535099Sbeck .want_legacy_error = X509_V_ERR_NO_EXPLICIT_POLICY, 44687535099Sbeck .want_legacy_error_depth = 0, 44787535099Sbeck }, 448b4d45f74Sbeck /* 449b4d45f74Sbeck * requireExplicitPolicy is a count of certificates to skip. If the value is 450b4d45f74Sbeck * not zero by the end of the chain, it doesn't count. 451b4d45f74Sbeck */ 45287535099Sbeck { 45387535099Sbeck .id = "oid3, with intermediate requiring explicit depth 1", 45487535099Sbeck .root_file = CERTSDIR "/" "policy_root.pem", 45587535099Sbeck .intermediate_file = CERTSDIR "/" "policy_intermediate_require1.pem", 45687535099Sbeck .leaf_file = CERTSDIR "/" "policy_leaf.pem", 45787535099Sbeck .policy_oid_to_check = OID3, 45887535099Sbeck .verify_flags = X509_V_FLAG_EXPLICIT_POLICY, 45987535099Sbeck .want_chains = 0, 46087535099Sbeck .want_error = X509_V_ERR_NO_EXPLICIT_POLICY, 46187535099Sbeck .want_error_depth = 0, 46287535099Sbeck .want_legacy_error = X509_V_ERR_NO_EXPLICIT_POLICY, 46387535099Sbeck .want_legacy_error_depth = 0, 46487535099Sbeck }, 46587535099Sbeck { 46687535099Sbeck .id = "oid3, with intermediate requiring explicit depth 2", 46787535099Sbeck .root_file = CERTSDIR "/" "policy_root.pem", 46887535099Sbeck .intermediate_file = CERTSDIR "/" "policy_intermediate_require2.pem", 46987535099Sbeck .leaf_file = CERTSDIR "/" "policy_leaf.pem", 47087535099Sbeck .policy_oid_to_check = OID3, 47187535099Sbeck .want_chains = 1, 47287535099Sbeck }, 47387535099Sbeck { 47487535099Sbeck .id = "oid3, with leaf requiring explicit depth 1", 47587535099Sbeck .root_file = CERTSDIR "/" "policy_root.pem", 47687535099Sbeck .intermediate_file = CERTSDIR "/" "policy_intermediate.pem", 47787535099Sbeck .leaf_file = CERTSDIR "/" "policy_leaf_require1.pem", 47887535099Sbeck .policy_oid_to_check = OID3, 47987535099Sbeck .want_chains = 1, 48087535099Sbeck }, 481b4d45f74Sbeck /* 482b4d45f74Sbeck * If multiple certificates specify the constraint, the more constrained value 483b4d45f74Sbeck * wins. 484b4d45f74Sbeck */ 48587535099Sbeck { 48687535099Sbeck .id = "oid3, with leaf and intermediate requiring explicit depth 1", 48787535099Sbeck .root_file = CERTSDIR "/" "policy_root.pem", 48887535099Sbeck .intermediate_file = CERTSDIR "/" "policy_intermediate_require1.pem", 48987535099Sbeck .leaf_file = CERTSDIR "/" "policy_leaf_require1.pem", 49087535099Sbeck .policy_oid_to_check = OID3, 49187535099Sbeck .verify_flags = X509_V_FLAG_EXPLICIT_POLICY, 49287535099Sbeck .want_chains = 0, 49387535099Sbeck .want_error = X509_V_ERR_NO_EXPLICIT_POLICY, 49487535099Sbeck .want_error_depth = 0, 49587535099Sbeck .want_legacy_error = X509_V_ERR_NO_EXPLICIT_POLICY, 49687535099Sbeck .want_legacy_error_depth = 0, 49787535099Sbeck }, 49887535099Sbeck { 49987535099Sbeck .id = "oid3, with leaf requiring explicit depth 1 and intermediate depth 2", 50087535099Sbeck .root_file = CERTSDIR "/" "policy_root.pem", 50187535099Sbeck .intermediate_file = CERTSDIR "/" "policy_intermediate_require2.pem", 50287535099Sbeck .leaf_file = CERTSDIR "/" "policy_leaf_require.pem", 50387535099Sbeck .policy_oid_to_check = OID3, 50487535099Sbeck .verify_flags = X509_V_FLAG_EXPLICIT_POLICY, 50587535099Sbeck .want_chains = 0, 50687535099Sbeck .want_error = X509_V_ERR_NO_EXPLICIT_POLICY, 50787535099Sbeck .want_error_depth = 0, 50887535099Sbeck .want_legacy_error = X509_V_ERR_NO_EXPLICIT_POLICY, 50987535099Sbeck .want_legacy_error_depth = 0, 51087535099Sbeck }, 511b4d45f74Sbeck /* 512b4d45f74Sbeck * An intermediate that requires an explicit policy, but then specifies no 513b4d45f74Sbeck * policies should fail verification as a result. 514b4d45f74Sbeck */ 51587535099Sbeck { 51687535099Sbeck .id = "oid1 with explicit_policy unset, intermediate requiring policy but specifying none", 51787535099Sbeck .root_file = CERTSDIR "/" "policy_root.pem", 51887535099Sbeck .intermediate_file = CERTSDIR "/" "policy_intermediate_require_no_policies.pem", 51987535099Sbeck .leaf_file = CERTSDIR "/" "policy_leaf.pem", 52087535099Sbeck .policy_oid_to_check = OID3, 52187535099Sbeck .want_chains = 0, 52287535099Sbeck .want_error = X509_V_ERR_NO_EXPLICIT_POLICY, 52387535099Sbeck .want_error_depth = 0, 52487535099Sbeck .want_legacy_error = X509_V_ERR_NO_EXPLICIT_POLICY, 52587535099Sbeck .want_legacy_error_depth = 0, 52687535099Sbeck }, 527b4d45f74Sbeck /* 528b4d45f74Sbeck * A constrained intermediate's policy extension has a duplicate policy, which 529b4d45f74Sbeck * is invalid. Historically this, and the above case, leaked memory. 530b4d45f74Sbeck */ 53187535099Sbeck { 53287535099Sbeck .id = "oid1 with explicit_policy unset, intermediate requiring policy but has duplicate", 53387535099Sbeck .root_file = CERTSDIR "/" "policy_root.pem", 53487535099Sbeck .intermediate_file = CERTSDIR "/" "policy_intermediate_require_duplicate.pem", 53587535099Sbeck .leaf_file = CERTSDIR "/" "policy_leaf.pem", 53687535099Sbeck .policy_oid_to_check = OID3, 53787535099Sbeck .want_chains = 0, 53887535099Sbeck .want_error = X509_V_ERR_INVALID_POLICY_EXTENSION, 53987535099Sbeck .want_error_depth = 0, 54087535099Sbeck .want_legacy_error = X509_V_ERR_INVALID_POLICY_EXTENSION, 54187535099Sbeck .want_legacy_error_depth = 0, 54287535099Sbeck }, 543b4d45f74Sbeck /* 544b4d45f74Sbeck * The leaf asserts anyPolicy, but the intermediate does not. The resulting 545b4d45f74Sbeck * valid policies are the intersection.(and vice versa) 546b4d45f74Sbeck */ 54787535099Sbeck { 54887535099Sbeck .id = "oid1, with explicit_policy set, with leaf asserting any", 54987535099Sbeck .root_file = CERTSDIR "/" "policy_root.pem", 55087535099Sbeck .intermediate_file = CERTSDIR "/" "policy_intermediate.pem", 55187535099Sbeck .leaf_file = CERTSDIR "/" "policy_leaf_any.pem", 55287535099Sbeck .policy_oid_to_check = OID1, 55387535099Sbeck .verify_flags = X509_V_FLAG_EXPLICIT_POLICY, 55487535099Sbeck .want_chains = 1, 55587535099Sbeck }, 55687535099Sbeck { 55787535099Sbeck .id = "oid3, with explicit_policy set, with leaf asserting any", 55887535099Sbeck .root_file = CERTSDIR "/" "policy_root.pem", 55987535099Sbeck .intermediate_file = CERTSDIR "/" "policy_intermediate.pem", 56087535099Sbeck .leaf_file = CERTSDIR "/" "policy_leaf_any.pem", 5615898fc2bSbeck .policy_oid_to_check = OID3, 56287535099Sbeck .verify_flags = X509_V_FLAG_EXPLICIT_POLICY, 5635898fc2bSbeck .want_chains = 0, 5645898fc2bSbeck .want_error = X509_V_ERR_NO_EXPLICIT_POLICY, 5655898fc2bSbeck .want_error_depth = 0, 5665898fc2bSbeck .want_legacy_error = X509_V_ERR_NO_EXPLICIT_POLICY, 5675898fc2bSbeck .want_legacy_error_depth = 0, 56887535099Sbeck }, 569b4d45f74Sbeck /* Both assert anyPolicy. All policies are valid. */ 57087535099Sbeck { 57187535099Sbeck .id = "oid1, with explicit_policy set, with leaf and intermediate asserting any", 57287535099Sbeck .root_file = CERTSDIR "/" "policy_root.pem", 57387535099Sbeck .intermediate_file = CERTSDIR "/" "policy_intermediate_any.pem", 57487535099Sbeck .leaf_file = CERTSDIR "/" "policy_leaf_any.pem", 57587535099Sbeck .policy_oid_to_check = OID1, 57687535099Sbeck .verify_flags = X509_V_FLAG_EXPLICIT_POLICY, 57787535099Sbeck .want_chains = 1, 57887535099Sbeck }, 57987535099Sbeck { 58087535099Sbeck .id = "oid3, with explicit_policy set, with leaf and intermediate asserting any", 58187535099Sbeck .root_file = CERTSDIR "/" "policy_root.pem", 58287535099Sbeck .intermediate_file = CERTSDIR "/" "policy_intermediate_any.pem", 58387535099Sbeck .leaf_file = CERTSDIR "/" "policy_leaf_any.pem", 58487535099Sbeck .policy_oid_to_check = OID1, 58587535099Sbeck .verify_flags = X509_V_FLAG_EXPLICIT_POLICY, 58687535099Sbeck .want_chains = 1, 58787535099Sbeck }, 588b4d45f74Sbeck /* 589b4d45f74Sbeck * BoringSSL tests just a trust anchor but behaves differently in this corner case. 590b4d45f74Sbeck * than libressl for reasons that have nothing to do with policy (because parital 591b4d45f74Sbeck * chains and legacy verifier horror) 592b4d45f74Sbeck */ 59311e92b75Sbeck }; 59411e92b75Sbeck 59511e92b75Sbeck #define N_VERIFY_CERT_TESTS \ 59611e92b75Sbeck (sizeof(verify_cert_tests) / sizeof(*verify_cert_tests)) 59711e92b75Sbeck 59811e92b75Sbeck static int 59911e92b75Sbeck verify_cert_test(int mode) 60011e92b75Sbeck { 601630f168fStb ASN1_OBJECT *policy_oid, *policy_oid2; 60211e92b75Sbeck struct verify_cert_test *vct; 60311e92b75Sbeck int chains, error, error_depth; 60411e92b75Sbeck int failed = 0; 60511e92b75Sbeck size_t i; 60611e92b75Sbeck 60711e92b75Sbeck for (i = 0; i < N_VERIFY_CERT_TESTS; i++) { 60811e92b75Sbeck vct = &verify_cert_tests[i]; 609630f168fStb policy_oid = vct->policy_oid_to_check ? 61011e92b75Sbeck OBJ_txt2obj(vct->policy_oid_to_check, 1) : NULL; 611630f168fStb policy_oid2 = vct->policy_oid_to_check2 ? 61211e92b75Sbeck OBJ_txt2obj(vct->policy_oid_to_check2, 1) : NULL; 61311e92b75Sbeck 61411e92b75Sbeck error = 0; 61511e92b75Sbeck error_depth = 0; 61611e92b75Sbeck 61711e92b75Sbeck fprintf(stderr, "== Test %zu (%s)\n", i, vct->id); 61811e92b75Sbeck verify_cert(vct->root_file, vct->intermediate_file, 61911e92b75Sbeck vct->leaf_file, &chains, &error, &error_depth, 62087535099Sbeck mode, policy_oid, policy_oid2, vct->verify_flags); 62111e92b75Sbeck 622dfcebad6Sbeck if ((chains == 0 && vct->want_chains == 0) || 62311e92b75Sbeck (chains == 1 && vct->want_chains > 0)) { 62411e92b75Sbeck fprintf(stderr, "INFO: Succeeded with %d chains%s\n", 62511e92b75Sbeck chains, vct->failing ? " (legacy failure)" : ""); 62611e92b75Sbeck if (mode == MODE_LEGACY_VFY && vct->failing) 62711e92b75Sbeck failed |= 1; 62811e92b75Sbeck } else { 62911e92b75Sbeck fprintf(stderr, "FAIL: Failed with %d chains%s\n", 63011e92b75Sbeck chains, vct->failing ? " (legacy failure)" : ""); 63111e92b75Sbeck if (!vct->failing) 63211e92b75Sbeck failed |= 1; 63311e92b75Sbeck } 63411e92b75Sbeck 63511e92b75Sbeck if (mode == MODE_LEGACY_VFY) { 63611e92b75Sbeck if (error != vct->want_legacy_error) { 63711e92b75Sbeck fprintf(stderr, "FAIL: Got legacy error %d, " 63811e92b75Sbeck "want %d\n", error, vct->want_legacy_error); 63911e92b75Sbeck failed |= 1; 64011e92b75Sbeck } 64111e92b75Sbeck if (error_depth != vct->want_legacy_error_depth) { 64211e92b75Sbeck fprintf(stderr, "FAIL: Got legacy error depth " 64311e92b75Sbeck "%d, want %d\n", error_depth, 64411e92b75Sbeck vct->want_legacy_error_depth); 64511e92b75Sbeck failed |= 1; 64611e92b75Sbeck } 64711e92b75Sbeck } 64811e92b75Sbeck fprintf(stderr, "\n"); 64911e92b75Sbeck ASN1_OBJECT_free(policy_oid); 65011e92b75Sbeck ASN1_OBJECT_free(policy_oid2); 65111e92b75Sbeck } 65211e92b75Sbeck return failed; 65311e92b75Sbeck } 65411e92b75Sbeck 65511e92b75Sbeck int 65611e92b75Sbeck main(int argc, char **argv) 65711e92b75Sbeck { 65811e92b75Sbeck int failed = 0; 65911e92b75Sbeck 66011e92b75Sbeck fprintf(stderr, "\n\nTesting legacy x509_vfy\n"); 66111e92b75Sbeck failed |= verify_cert_test(MODE_LEGACY_VFY); 66211e92b75Sbeck fprintf(stderr, "\n\nTesting modern x509_vfy\n"); 66311e92b75Sbeck failed |= verify_cert_test(MODE_MODERN_VFY); 664dfcebad6Sbeck /* New verifier does not do policy goop at the moment */ 66511e92b75Sbeck 66611e92b75Sbeck return (failed); 66711e92b75Sbeck } 668