Rewrite X.509 policy tree logic.
This reimplements policy handling using a similar DAG structure as in
https://chromium-review.googlesource.com/c/chromium/src/+/4111415. The
main difference is that, being C, we don't have std::set or std::map
easily available. But the algorithm can be implemented purely with
sorted lists, while remaining subquadratic.
This implementation relies on two assumptions:
1. We do not return the policy tree. This was removed in
https://boringssl-review.googlesource.com/c/boringssl/+/53327
2. We do not return the final set of certificate policies. I.e.,
certificate policy checking is only used for evaluating policy
constraints and X509_V_FLAG_EXPLICIT_POLICY.
The second assumption is not very important. It mostly simplifies
has_explicit_policy slightly.
In addition, this new implementation removes the per-certificate policy
cache. Instead, we just process the policy extensions anew on
certificate verification. This avoids a mess of threading complexity,
including a race condition in the old logic. See
https://boringssl-review.googlesource.com/c/boringssl/+/55762 for a
description of the race condition.
Change-Id: Ifba9037588ecff5eb6ed3c34c8bd7611f60013a6
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/56036
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Bob Beck <bbe@google.com>
diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c
index ce110e6..7638d0d 100644
--- a/crypto/x509/x509_vfy.c
+++ b/crypto/x509/x509_vfy.c
@@ -1689,39 +1689,19 @@
}
static int check_policy(X509_STORE_CTX *ctx) {
- int ret;
if (ctx->parent) {
return 1;
}
- // TODO(davidben): Historically, outputs of the |X509_policy_check| were saved
- // on |ctx| and accessible via the public API. This has since been removed, so
- // remove the fields from |X509_STORE_CTX|.
- ret = X509_policy_check(&ctx->tree, &ctx->explicit_policy, ctx->chain,
- ctx->param->policies, ctx->param->flags);
- if (ret == 0) {
- OPENSSL_PUT_ERROR(X509, ERR_R_MALLOC_FAILURE);
- ctx->error = X509_V_ERR_OUT_OF_MEM;
- return 0;
- }
- // Invalid or inconsistent extensions
- if (ret == -1) {
- // Locate certificates with bad extensions and notify callback.
- for (size_t i = 0; i < sk_X509_num(ctx->chain); i++) {
- X509 *x = sk_X509_value(ctx->chain, i);
- if (!(x->ex_flags & EXFLAG_INVALID_POLICY)) {
- continue;
- }
- ctx->current_cert = x;
- ctx->error = X509_V_ERR_INVALID_POLICY_EXTENSION;
- if (!ctx->verify_cb(0, ctx)) {
- return 0;
- }
+
+ X509 *current_cert = NULL;
+ int ret = X509_policy_check(ctx->chain, ctx->param->policies,
+ ctx->param->flags, ¤t_cert);
+ if (ret != X509_V_OK) {
+ ctx->current_cert = current_cert;
+ ctx->error = ret;
+ if (ret == X509_V_ERR_OUT_OF_MEM) {
+ return 0;
}
- return 1;
- }
- if (ret == -2) {
- ctx->current_cert = NULL;
- ctx->error = X509_V_ERR_NO_EXPLICIT_POLICY;
return ctx->verify_cb(0, ctx);
}
@@ -2311,10 +2291,6 @@
}
ctx->param = NULL;
}
- if (ctx->tree != NULL) {
- X509_policy_tree_free(ctx->tree);
- ctx->tree = NULL;
- }
if (ctx->chain != NULL) {
sk_X509_pop_free(ctx->chain, X509_free);
ctx->chain = NULL;