xref: /openbsd-src/regress/lib/libcrypto/x509/policy/policy.c (revision 6822f9c8033774f6aab00bcb43e8718e7034e67d)
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