summaryrefslogtreecommitdiff
path: root/lib/libcrypto/x509/x509_verify.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libcrypto/x509/x509_verify.c')
-rw-r--r--lib/libcrypto/x509/x509_verify.c146
1 files changed, 131 insertions, 15 deletions
diff --git a/lib/libcrypto/x509/x509_verify.c b/lib/libcrypto/x509/x509_verify.c
index 57c52aa2402..21b391c76c4 100644
--- a/lib/libcrypto/x509/x509_verify.c
+++ b/lib/libcrypto/x509/x509_verify.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: x509_verify.c,v 1.37 2021/04/28 17:53:34 tb Exp $ */
+/* $OpenBSD: x509_verify.c,v 1.38 2021/07/10 15:52:59 beck Exp $ */
/*
* Copyright (c) 2020-2021 Bob Beck <beck@openbsd.org>
*
@@ -33,7 +33,7 @@
static int x509_verify_cert_valid(struct x509_verify_ctx *ctx, X509 *cert,
struct x509_verify_chain *current_chain);
static void x509_verify_build_chains(struct x509_verify_ctx *ctx, X509 *cert,
- struct x509_verify_chain *current_chain);
+ struct x509_verify_chain *current_chain, int full_chain);
static int x509_verify_cert_error(struct x509_verify_ctx *ctx, X509 *cert,
size_t depth, int error, int ok);
static void x509_verify_chain_free(struct x509_verify_chain *chain);
@@ -166,6 +166,9 @@ x509_verify_ctx_reset(struct x509_verify_ctx *ctx)
for (i = 0; i < ctx->chains_count; i++)
x509_verify_chain_free(ctx->chains[i]);
+ sk_X509_pop_free(ctx->saved_error_chain, X509_free);
+ ctx->saved_error = 0;
+ ctx->saved_error_depth = 0;
ctx->error = 0;
ctx->error_depth = 0;
ctx->chains_count = 0;
@@ -183,14 +186,42 @@ x509_verify_ctx_clear(struct x509_verify_ctx *ctx)
}
static int
-x509_verify_ctx_cert_is_root(struct x509_verify_ctx *ctx, X509 *cert)
+x509_verify_cert_cache_extensions(X509 *cert) {
+ if (!(cert->ex_flags & EXFLAG_SET)) {
+ CRYPTO_w_lock(CRYPTO_LOCK_X509);
+ x509v3_cache_extensions(cert);
+ CRYPTO_w_unlock(CRYPTO_LOCK_X509);
+ }
+ if (cert->ex_flags & EXFLAG_INVALID)
+ return 0;
+ return (cert->ex_flags & EXFLAG_SET);
+}
+
+static int
+x509_verify_cert_self_signed(X509 *cert)
+{
+ return (cert->ex_flags & EXFLAG_SS) ? 1 : 0;
+}
+
+static int
+x509_verify_ctx_cert_is_root(struct x509_verify_ctx *ctx, X509 *cert,
+ int full_chain)
{
int i;
+ if (!x509_verify_cert_cache_extensions(cert))
+ return 0;
+
for (i = 0; i < sk_X509_num(ctx->roots); i++) {
if (X509_cmp(sk_X509_value(ctx->roots, i), cert) == 0)
- return 1;
+ return !full_chain ||
+ x509_verify_cert_self_signed(cert);
}
+ /*
+ * XXX what if this is a by_dir thing? this currently isn't
+ * handled so this case is a bit messed up for loonix with
+ * by directory trust bundles...
+ */
return 0;
}
@@ -236,6 +267,46 @@ x509_verify_ctx_set_xsc_chain(struct x509_verify_ctx *ctx,
return 1;
}
+
+/*
+ * Save the error state and unvalidated chain off of the xsc for
+ * later.
+ */
+static int
+x509_verify_ctx_save_xsc_error(struct x509_verify_ctx *ctx)
+{
+ if (ctx->xsc != NULL && ctx->xsc->chain != NULL) {
+ sk_X509_pop_free(ctx->saved_error_chain, X509_free);
+ ctx->saved_error_chain = X509_chain_up_ref(ctx->xsc->chain);
+ if (ctx->saved_error_chain == NULL)
+ return x509_verify_cert_error(ctx, NULL, 0,
+ X509_V_ERR_OUT_OF_MEM, 0);
+ ctx->saved_error = ctx->xsc->error;
+ ctx->saved_error_depth = ctx->xsc->error_depth;
+ }
+ return 1;
+}
+
+/*
+ * Restore the saved error state and unvalidated chain to the xsc
+ * if we do not have a validated chain.
+ */
+static int
+x509_verify_ctx_restore_xsc_error(struct x509_verify_ctx *ctx)
+{
+ if (ctx->xsc != NULL && ctx->chains_count == 0 &&
+ ctx->saved_error_chain != NULL) {
+ sk_X509_pop_free(ctx->xsc->chain, X509_free);
+ ctx->xsc->chain = X509_chain_up_ref(ctx->saved_error_chain);
+ if (ctx->xsc->chain == NULL)
+ return x509_verify_cert_error(ctx, NULL, 0,
+ X509_V_ERR_OUT_OF_MEM, 0);
+ ctx->xsc->error = ctx->saved_error;
+ ctx->xsc->error_depth = ctx->saved_error_depth;
+ }
+ return 1;
+}
+
/* Add a validated chain to our list of valid chains */
static int
x509_verify_ctx_add_chain(struct x509_verify_ctx *ctx,
@@ -331,6 +402,8 @@ static int
x509_verify_potential_parent(struct x509_verify_ctx *ctx, X509 *parent,
X509 *child)
{
+ if (!x509_verify_cert_cache_extensions(parent))
+ return 0;
if (ctx->xsc != NULL)
return (ctx->xsc->check_issued(ctx->xsc, child, parent));
@@ -378,7 +451,7 @@ x509_verify_parent_signature(X509 *parent, X509 *child,
static int
x509_verify_consider_candidate(struct x509_verify_ctx *ctx, X509 *cert,
unsigned char *cert_md, int is_root_cert, X509 *candidate,
- struct x509_verify_chain *current_chain)
+ struct x509_verify_chain *current_chain, int full_chain)
{
int depth = sk_X509_num(current_chain->certs);
struct x509_verify_chain *new_chain;
@@ -446,7 +519,7 @@ x509_verify_consider_candidate(struct x509_verify_ctx *ctx, X509 *cert,
}
}
- x509_verify_build_chains(ctx, candidate, new_chain);
+ x509_verify_build_chains(ctx, candidate, new_chain, full_chain);
done:
x509_verify_chain_free(new_chain);
@@ -470,11 +543,11 @@ x509_verify_cert_error(struct x509_verify_ctx *ctx, X509 *cert, size_t depth,
static void
x509_verify_build_chains(struct x509_verify_ctx *ctx, X509 *cert,
- struct x509_verify_chain *current_chain)
+ struct x509_verify_chain *current_chain, int full_chain)
{
unsigned char cert_md[EVP_MAX_MD_SIZE] = { 0 };
X509 *candidate;
- int i, depth, count, ret;
+ int i, depth, count, ret, is_root;
/*
* If we are finding chains with an xsc, just stop after we have
@@ -519,8 +592,11 @@ x509_verify_build_chains(struct x509_verify_ctx *ctx, X509 *cert,
for (i = 0; i < sk_X509_num(ctx->roots); i++) {
candidate = sk_X509_value(ctx->roots, i);
if (x509_verify_potential_parent(ctx, candidate, cert)) {
+ is_root = !full_chain ||
+ x509_verify_cert_self_signed(candidate);
x509_verify_consider_candidate(ctx, cert,
- cert_md, 1, candidate, current_chain);
+ cert_md, is_root, candidate, current_chain,
+ full_chain);
}
}
/* Check for legacy mode roots */
@@ -532,8 +608,11 @@ x509_verify_build_chains(struct x509_verify_ctx *ctx, X509 *cert,
}
if (ret > 0) {
if (x509_verify_potential_parent(ctx, candidate, cert)) {
+ is_root = !full_chain ||
+ x509_verify_cert_self_signed(candidate);
x509_verify_consider_candidate(ctx, cert,
- cert_md, 1, candidate, current_chain);
+ cert_md, is_root, candidate, current_chain,
+ full_chain);
}
X509_free(candidate);
}
@@ -545,7 +624,8 @@ x509_verify_build_chains(struct x509_verify_ctx *ctx, X509 *cert,
candidate = sk_X509_value(ctx->intermediates, i);
if (x509_verify_potential_parent(ctx, candidate, cert)) {
x509_verify_consider_candidate(ctx, cert,
- cert_md, 0, candidate, current_chain);
+ cert_md, 0, candidate, current_chain,
+ full_chain);
}
}
}
@@ -973,6 +1053,7 @@ size_t
x509_verify(struct x509_verify_ctx *ctx, X509 *leaf, char *name)
{
struct x509_verify_chain *current_chain;
+ int retry_chain_build, full_chain = 0;
if (ctx->roots == NULL || ctx->max_depth == 0) {
ctx->error = X509_V_ERR_INVALID_CALL;
@@ -986,6 +1067,10 @@ x509_verify(struct x509_verify_ctx *ctx, X509 *leaf, char *name)
}
leaf = ctx->xsc->cert;
+ /* XXX */
+ full_chain = 1;
+ if (ctx->xsc->param->flags & X509_V_FLAG_PARTIAL_CHAIN)
+ full_chain = 0;
/*
* XXX
* The legacy code expects the top level cert to be
@@ -1024,14 +1109,45 @@ x509_verify(struct x509_verify_ctx *ctx, X509 *leaf, char *name)
x509_verify_chain_free(current_chain);
goto err;
}
- if (x509_verify_ctx_cert_is_root(ctx, leaf))
- x509_verify_ctx_add_chain(ctx, current_chain);
- else
- x509_verify_build_chains(ctx, leaf, current_chain);
+ do {
+ retry_chain_build = 0;
+ if (x509_verify_ctx_cert_is_root(ctx, leaf, full_chain))
+ x509_verify_ctx_add_chain(ctx, current_chain);
+ else {
+ x509_verify_build_chains(ctx, leaf, current_chain,
+ full_chain);
+ if (full_chain && ctx->chains_count == 0) {
+ /*
+ * Save the error state from the xsc
+ * at this point to put back on the
+ * xsc in case we do not find a chain
+ * that is trusted but not a full
+ * chain to a self signed root. This
+ * is because the unvalidated chain is
+ * used by the autochain batshittery
+ * on failure and will be needed for
+ * that.
+ */
+ if (!x509_verify_ctx_save_xsc_error(ctx)) {
+ x509_verify_chain_free(current_chain);
+ goto err;
+ }
+ full_chain = 0;
+ retry_chain_build = 1;
+ }
+ }
+ } while (retry_chain_build);
x509_verify_chain_free(current_chain);
/*
+ * Bring back the failure case we wanted to the xsc if
+ * we saved one.
+ */
+ if (!x509_verify_ctx_restore_xsc_error(ctx))
+ goto err;
+
+ /*
* Safety net:
* We could not find a validated chain, and for some reason do not
* have an error set.