In certain situations the server provided certificate chain may no longer be
authorMatt Caswell <matt@openssl.org>
Tue, 27 Jan 2015 10:03:29 +0000 (10:03 +0000)
committerMatt Caswell <matt@openssl.org>
Wed, 25 Feb 2015 09:14:13 +0000 (09:14 +0000)
valid. However the issuer of the leaf, or some intermediate cert is in fact
in the trust store.

When building a trust chain if the first attempt fails, then try to see if
alternate chains could be constructed that are trusted.

RT3637
RT3621

Reviewed-by: Dr. Stephen Henson <steve@openssl.org>
crypto/x509/x509_vfy.c

index cc9665ea6528bed327da72bb860671b5f19126b9..b986d4c6430a86eebbfc1982bf3cb4314a0eb405 100644 (file)
@@ -182,11 +182,11 @@ static X509 *lookup_cert_match(X509_STORE_CTX *ctx, X509 *x)
 
 int X509_verify_cert(X509_STORE_CTX *ctx)
 {
-    X509 *x, *xtmp, *chain_ss = NULL;
+    X509 *x, *xtmp, *xtmp2, *chain_ss = NULL;
     int bad_chain = 0;
     X509_VERIFY_PARAM *param = ctx->param;
     int depth, i, ok = 0;
-    int num;
+    int num, j, retry;
     int (*cb) (int xok, X509_STORE_CTX *xctx);
     STACK_OF(X509) *sktmp = NULL;
     if (ctx->cert == NULL) {
@@ -271,91 +271,126 @@ int X509_verify_cert(X509_STORE_CTX *ctx)
         break;
     }
 
+    /* Remember how many untrusted certs we have */
+    j = num;
     /*
      * at this point, chain should contain a list of untrusted certificates.
      * We now need to add at least one trusted one, if possible, otherwise we
      * complain.
      */
 
-    /*
-     * Examine last certificate in chain and see if it is self signed.
-     */
-
-    i = sk_X509_num(ctx->chain);
-    x = sk_X509_value(ctx->chain, i - 1);
-    if (cert_self_signed(x)) {
-        /* we have a self signed certificate */
-        if (sk_X509_num(ctx->chain) == 1) {
-            /*
-             * We have a single self signed certificate: see if we can find
-             * it in the store. We must have an exact match to avoid possible
-             * impersonation.
-             */
-            ok = ctx->get_issuer(&xtmp, ctx, x);
-            if ((ok <= 0) || X509_cmp(x, xtmp)) {
-                ctx->error = X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT;
-                ctx->current_cert = x;
-                ctx->error_depth = i - 1;
-                if (ok == 1)
-                    X509_free(xtmp);
-                bad_chain = 1;
-                ok = cb(0, ctx);
-                if (!ok)
-                    goto end;
+    do {
+        /*
+         * Examine last certificate in chain and see if it is self signed.
+         */
+        i = sk_X509_num(ctx->chain);
+        x = sk_X509_value(ctx->chain, i - 1);
+        if (cert_self_signed(x)) {
+            /* we have a self signed certificate */
+            if (sk_X509_num(ctx->chain) == 1) {
+                /*
+                 * We have a single self signed certificate: see if we can
+                 * find it in the store. We must have an exact match to avoid
+                 * possible impersonation.
+                 */
+                ok = ctx->get_issuer(&xtmp, ctx, x);
+                if ((ok <= 0) || X509_cmp(x, xtmp)) {
+                    ctx->error = X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT;
+                    ctx->current_cert = x;
+                    ctx->error_depth = i - 1;
+                    if (ok == 1)
+                        X509_free(xtmp);
+                    bad_chain = 1;
+                    ok = cb(0, ctx);
+                    if (!ok)
+                        goto end;
+                } else {
+                    /*
+                     * We have a match: replace certificate with store
+                     * version so we get any trust settings.
+                     */
+                    X509_free(x);
+                    x = xtmp;
+                    (void)sk_X509_set(ctx->chain, i - 1, x);
+                    ctx->last_untrusted = 0;
+                }
             } else {
                 /*
-                 * We have a match: replace certificate with store version so
-                 * we get any trust settings.
+                 * extract and save self signed certificate for later use
                  */
-                X509_free(x);
-                x = xtmp;
-                (void)sk_X509_set(ctx->chain, i - 1, x);
-                ctx->last_untrusted = 0;
+                chain_ss = sk_X509_pop(ctx->chain);
+                ctx->last_untrusted--;
+                num--;
+                j--;
+                x = sk_X509_value(ctx->chain, num - 1);
             }
-        } else {
-            /*
-             * extract and save self signed certificate for later use
-             */
-            chain_ss = sk_X509_pop(ctx->chain);
-            ctx->last_untrusted--;
-            num--;
-            x = sk_X509_value(ctx->chain, num - 1);
         }
-    }
-
-    /* We now lookup certs from the certificate store */
-    for (;;) {
-        /* If we have enough, we break */
-        if (depth < num)
-            break;
+        /* We now lookup certs from the certificate store */
+        for (;;) {
+            /* If we have enough, we break */
+            if (depth < num)
+                break;
+            /* If we are self signed, we break */
+            if (cert_self_signed(x))
+                break;
+            ok = ctx->get_issuer(&xtmp, ctx, x);
 
-        /* If we are self signed, we break */
-        if (cert_self_signed(x))
-            break;
+            if (ok < 0)
+                return ok;
+            if (ok == 0)
+                break;
+            x = xtmp;
+            if (!sk_X509_push(ctx->chain, x)) {
+                X509_free(xtmp);
+                X509err(X509_F_X509_VERIFY_CERT, ERR_R_MALLOC_FAILURE);
+                return 0;
+            }
+            num++;
+        }
 
-        ok = ctx->get_issuer(&xtmp, ctx, x);
+        /* we now have our chain, lets check it... */
+        i = check_trust(ctx);
 
-        if (ok < 0)
-            return ok;
-        if (ok == 0)
-            break;
+        /* If explicitly rejected error */
+        if (i == X509_TRUST_REJECTED)
+            goto end;
+        /*
+         * If it's not explicitly trusted then check if there is an alternative
+         * chain that could be used. We only do this if we haven't already
+         * checked via TRUSTED_FIRST
+         */
+        retry = 0;
+        if (i != X509_TRUST_TRUSTED
+            && !(ctx->param->flags & X509_V_FLAG_TRUSTED_FIRST)) {
+            while (j-- > 1) {
+                xtmp2 = sk_X509_value(ctx->chain, j - 1);
+                ok = ctx->get_issuer(&xtmp, ctx, xtmp2);
+                if (ok < 0)
+                    goto end;
+                /* Check if we found an alternate chain */
+                if (ok > 0) {
+                    /*
+                     * Free up the found cert we'll add it again later
+                     */
+                    X509_free(xtmp);
 
-        x = xtmp;
-        if (!sk_X509_push(ctx->chain, x)) {
-            X509_free(xtmp);
-            X509err(X509_F_X509_VERIFY_CERT, ERR_R_MALLOC_FAILURE);
-            return 0;
+                    /*
+                     * Dump all the certs above this point - we've found an
+                     * alternate chain
+                     */
+                    while (num > j) {
+                        xtmp = sk_X509_pop(ctx->chain);
+                        X509_free(xtmp);
+                        num--;
+                        ctx->last_untrusted--;
+                    }
+                    retry = 1;
+                    break;
+                }
+            }
         }
-        num++;
-    }
+    } while (retry);
 
-    /* we now have our chain, lets check it... */
-
-    i = check_trust(ctx);
-
-    /* If explicitly rejected error */
-    if (i == X509_TRUST_REJECTED)
-        goto end;
     /*
      * If not explicitly trusted then indicate error unless it's a single
      * self signed certificate in which case we've indicated an error already