diff --git a/crypto/x509/v3_purp.c b/crypto/x509/v3_purp.c index 1c70d60ca0..4a2b549199 100644 --- a/crypto/x509/v3_purp.c +++ b/crypto/x509/v3_purp.c @@ -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; diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c index 1f17c71bc1..3bd23d131c 100644 --- a/crypto/x509/x509_vfy.c +++ b/crypto/x509/x509_vfy.c @@ -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; diff --git a/doc/man1/openssl.pod b/doc/man1/openssl.pod index dbab509be4..f075e2170b 100644 --- a/doc/man1/openssl.pod +++ b/doc/man1/openssl.pod @@ -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> diff --git a/doc/man3/X509_VERIFY_PARAM_set_flags.pod b/doc/man3/X509_VERIFY_PARAM_set_flags.pod index 72da4cb143..4f067c877c 100644 --- a/doc/man3/X509_VERIFY_PARAM_set_flags.pod +++ b/doc/man3/X509_VERIFY_PARAM_set_flags.pod @@ -283,13 +283,15 @@ they are enabled. If B is set delta CRLs (if present) are used to determine certificate status. If not set deltas are ignored. -B 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 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 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 is set, which is always the case since OpenSSL 1.1.0, construction of the certificate chain