]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
x509_vfy.c: Improve key usage checks in internal_verify() of cert chains
authorDr. David von Oheimb <David.von.Oheimb@siemens.com>
Fri, 3 Jul 2020 19:19:55 +0000 (21:19 +0200)
committerDr. David von Oheimb <David.von.Oheimb@siemens.com>
Thu, 16 Jul 2020 13:48:53 +0000 (15:48 +0200)
If a presumably self-signed cert is last in chain we verify its signature
only if X509_V_FLAG_CHECK_SS_SIGNATURE is set. Upon this request we do the
signature verification, but not in case it is a (non-conforming) self-issued
CA certificate with a key usage extension that does not include keyCertSign.

Make clear when we must verify the signature of a certificate
and when we must adhere to key usage restrictions of the 'issuing' cert.
Add some comments for making internal_verify() easier to understand.
Update the documentation of X509_V_FLAG_CHECK_SS_SIGNATURE accordingly.

Reviewed-by: Tomas Mraz <tmraz@fedoraproject.org>
(Merged from https://github.com/openssl/openssl/pull/12375)

crypto/x509/v3_purp.c
crypto/x509/x509_vfy.c
doc/man1/openssl.pod
doc/man3/X509_VERIFY_PARAM_set_flags.pod

index 1c70d60ca0ef94d4c66fa03f7dee57c8e5628a69..4a2b549199847014b7bf9b5cc43f41ca79c0ebbb 100644 (file)
@@ -533,6 +533,7 @@ int X509v3_cache_extensions(X509 *x, OPENSSL_CTX *libctx, const char *propq)
                 /* .. and the signature alg matches the PUBKEY alg: */
                 && check_sig_alg_match(X509_get0_pubkey(x), x) == X509_V_OK)
             x->ex_flags |= EXFLAG_SS; /* indicate self-signed */
+        /* This is very related to x509_likely_issued(x, x) == X509_V_OK */
     }
 
     /* Handle subject alternative names and various other extensions */
@@ -865,6 +866,7 @@ int x509_likely_issued(X509 *issuer, X509 *subject,
                       X509_get_issuer_name(subject)) != 0)
         return X509_V_ERR_SUBJECT_ISSUER_MISMATCH;
 
+    /* set issuer->skid and subject->akid */
     if (!X509v3_cache_extensions(issuer, libctx, propq)
             || !X509v3_cache_extensions(subject, libctx, propq))
         return X509_V_ERR_UNSPECIFIED;
index 1f17c71bc1aa86d76722fccff0336fe8df5a4fd8..3bd23d131c660d009c0c8aa1999ecbe49054666d 100644 (file)
@@ -1746,6 +1746,7 @@ int x509_check_cert_time(X509_STORE_CTX *ctx, X509 *x, int depth)
     return 1;
 }
 
+/* verify the issuer signatures and cert times of ctx->chain */
 static int internal_verify(X509_STORE_CTX *ctx)
 {
     int n = sk_X509_num(ctx->chain) - 1;
@@ -1760,15 +1761,15 @@ static int internal_verify(X509_STORE_CTX *ctx)
     if (ctx->bare_ta_signed) {
         xs = xi;
         xi = NULL;
-        goto check_cert;
+        goto check_cert_time;
     }
 
-    if (ctx->check_issued(ctx, xi, xi)) /* the last cert appears self-signed */
-        xs = xi;
+    if (ctx->check_issued(ctx, xi, xi))
+        xs = xi; /* the typical case: last cert in the chain is self-issued */
     else {
         if (ctx->param->flags & X509_V_FLAG_PARTIAL_CHAIN) {
             xs = xi;
-            goto check_cert;
+            goto check_cert_time;
         }
         if (n <= 0)
             return verify_cb_cert(ctx, xi, 0,
@@ -1784,31 +1785,54 @@ static int internal_verify(X509_STORE_CTX *ctx)
      */
     while (n >= 0) {
         /*
+         * For each iteration of this loop:
+         * n is the subject depth
+         * xs is the subject cert, for which the signature is to be checked
+         * xi is the supposed issuer cert containing the public key to use
+         * Initially xs == xi if the last cert in the chain is self-issued.
+         *
          * Skip signature check for self-signed certificates unless explicitly
          * asked for because it does not add any security and just wastes time.
-         * If the issuer's public key is not available or its key usage does
-         * not support issuing the subject cert, report the issuer certificate
-         * and its depth (rather than the depth of the subject).
          */
-        if (xs != xi || (ctx->param->flags & X509_V_FLAG_CHECK_SS_SIGNATURE)) {
+        if (xs != xi || ((ctx->param->flags & X509_V_FLAG_CHECK_SS_SIGNATURE)
+                         && (xi->ex_flags & EXFLAG_SS) != 0)) {
             EVP_PKEY *pkey;
-            int issuer_depth = n + (xi == xs ? 0 : 1);
-            int ret = x509_signing_allowed(xi, xs);
+            /*
+             * If the issuer's public key is not available or its key usage
+             * does not support issuing the subject cert, report the issuer
+             * cert and its depth (rather than n, the depth of the subject).
+             */
+            int issuer_depth = n + (xs == xi ? 0 : 1);
+            /*
+             * According to https://tools.ietf.org/html/rfc5280#section-6.1.4
+             * step (n) we must check any given key usage extension in a CA cert
+             * when preparing the verification of a certificate issued by it.
+             * According to https://tools.ietf.org/html/rfc5280#section-4.2.1.3
+             * we must not verify a certifiate signature if the key usage of the
+             * CA certificate that issued the certificate prohibits signing.
+             * In case the 'issuing' certificate is the last in the chain and is
+             * not a CA certificate but a 'self-issued' end-entity cert (i.e.,
+             * xs == xi && !(xi->ex_flags & EXFLAG_CA)) RFC 5280 does not apply
+             * (see https://tools.ietf.org/html/rfc6818#section-2) and thus
+             * we are free to ignore any key usage restrictions on such certs.
+             */
+            int ret = xs == xi && (xi->ex_flags & EXFLAG_CA) == 0
+                ? X509_V_OK : x509_signing_allowed(xi, xs);
 
             if (ret != X509_V_OK && !verify_cb_cert(ctx, xi, issuer_depth, ret))
                 return 0;
             if ((pkey = X509_get0_pubkey(xi)) == NULL) {
-                if (!verify_cb_cert(ctx, xi, issuer_depth,
-                                    X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY))
+                ret = X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY;
+                if (!verify_cb_cert(ctx, xi, issuer_depth, ret))
                     return 0;
             } else if (X509_verify_ex(xs, pkey, ctx->libctx, ctx->propq) <= 0) {
-                if (!verify_cb_cert(ctx, xs, n,
-                                    X509_V_ERR_CERT_SIGNATURE_FAILURE))
+                ret = X509_V_ERR_CERT_SIGNATURE_FAILURE;
+                if (!verify_cb_cert(ctx, xs, n, ret))
                     return 0;
             }
         }
 
- check_cert:
+ check_cert_time:
         /* Calls verify callback as needed */
         if (!x509_check_cert_time(ctx, xs, n))
             return 0;
index dbab509be42ec4c26673d834920cf88eb755929b..f075e2170be9362321cee56e2410d6431e2b3fba 100644 (file)
@@ -967,10 +967,11 @@ This certificate may be self-issued or belong to an intermediate CA.
 
 =item B<-check_ss_sig>
 
-Verify the signature on the last certificate in a chain
-even when it is a self-signed (root CA) certificate.
-By default in this case the check is disabled
-because it does not add any security.
+Verify the signature of
+the last certificate in a chain if the certificate is supposedly self-signed.
+This is prohibited and will result in an error if it is a non-conforming CA
+certificate with key usage restrictions not including the keyCertSign bit.
+This verification is disabled by default because it doesn't add any security.
 
 =item B<-allow_proxy_certs>
 
index 72da4cb14360dc952c1b80caa2726e75d6f2fbef..4f067c877c530e1a3dac06c7ec12bdeae316bce3 100644 (file)
@@ -283,13 +283,15 @@ they are enabled.
 If B<X509_V_FLAG_USE_DELTAS> is set delta CRLs (if present) are used to
 determine certificate status. If not set deltas are ignored.
 
-B<X509_V_FLAG_CHECK_SS_SIGNATURE> requires verifying the signature of the last
-certificate in a chain even when it is a self-signed (root CA) certificate.
-In this case the check is disabled by default because it does not
+B<X509_V_FLAG_CHECK_SS_SIGNATURE> requests checking the signature of
+the last certificate in a chain if the certificate is supposedly self-signed.
+This is prohibited and will result in an error if it is a non-conforming CA
+certificate with key usage restrictions not including the I<keyCertSign> bit.
+By default this check is disabled because it doesn't
 add any additional security but in some cases applications might want to
-check the signature anyway. A side effect of not checking the root CA
-signature is that disabled or unsupported message digests on the root CA
-are not treated as fatal errors.
+check the signature anyway. A side effect of not checking the self-signature
+of such a certificate is that disabled or unsupported message digests used for
+the signature are not treated as fatal errors.
 
 When B<X509_V_FLAG_TRUSTED_FIRST> is set, which is always the case since
 OpenSSL 1.1.0, construction of the certificate chain