summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGES8
-rw-r--r--crypto/x509/x509_cmp.c2
-rw-r--r--crypto/x509/x509_vfy.c51
-rw-r--r--doc/man1/verify.pod12
-rw-r--r--doc/man3/X509_STORE_set_verify_cb_func.pod4
-rw-r--r--test/certs/root-expired.pem18
-rwxr-xr-xtest/certs/setup.sh5
-rw-r--r--test/recipes/25-test_verify.t6
8 files changed, 71 insertions, 35 deletions
diff --git a/CHANGES b/CHANGES
index b927cf1361..044108d036 100644
--- a/CHANGES
+++ b/CHANGES
@@ -19,6 +19,10 @@
pass an EVP_PKEY instead.
[Matt Caswell]
+ *) In 1.1.1h, an expired trusted (root) certificate was not anymore rejected
+ when validating a certificate path. This check is restored in 1.1.1i.
+ [David von Oheimb]
+
Changes between 1.1.1g and 1.1.1h [22 Sep 2020]
*) Certificates with explicit curve parameters are now disallowed in
@@ -44,6 +48,10 @@
on renegotiation.
[Tomas Mraz]
+ *) Accidentally, an expired trusted (root) certificate is not anymore rejected
+ when validating a certificate path.
+ [David von Oheimb]
+
*) The Oracle Developer Studio compiler will start reporting deprecated APIs
*) Add support for Apple Silicon M1 Macs with the darwin64-arm64-cc target.
diff --git a/crypto/x509/x509_cmp.c b/crypto/x509/x509_cmp.c
index d1600e1e8d..ad620af0af 100644
--- a/crypto/x509/x509_cmp.c
+++ b/crypto/x509/x509_cmp.c
@@ -135,6 +135,8 @@ int X509_cmp(const X509 *a, const X509 *b)
{
int rv;
+ if (a == b) /* for efficiency */
+ return 0;
/* ensure hash is valid */
if (X509_check_purpose((X509 *)a, -1, 0) != 1)
return -2;
diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c
index ffa8d637ff..730a0160ff 100644
--- a/crypto/x509/x509_vfy.c
+++ b/crypto/x509/x509_vfy.c
@@ -312,8 +312,20 @@ int X509_verify_cert(X509_STORE_CTX *ctx)
return ret;
}
+static int sk_X509_contains(STACK_OF(X509) *sk, X509 *cert)
+{
+ int i, n = sk_X509_num(sk);
+
+ for (i = 0; i < n; i++)
+ if (X509_cmp(sk_X509_value(sk, i), cert) == 0)
+ return 1;
+ return 0;
+}
+
/*
- * Given a STACK_OF(X509) find the issuer of cert (if any)
+ * Find in given STACK_OF(X509) sk a non-expired issuer cert (if any) of given cert x.
+ * The issuer must not be the same as x and must not yet be in ctx->chain, where the
+ * exceptional case x is self-issued and ctx->chain has just one element is allowed.
*/
static X509 *find_issuer(X509_STORE_CTX *ctx, STACK_OF(X509) *sk, X509 *x)
{
@@ -322,7 +334,13 @@ static X509 *find_issuer(X509_STORE_CTX *ctx, STACK_OF(X509) *sk, X509 *x)
for (i = 0; i < sk_X509_num(sk); i++) {
issuer = sk_X509_value(sk, i);
- if (ctx->check_issued(ctx, x, issuer)) {
+ /*
+ * Below check 'issuer != x' is an optimization and safety precaution:
+ * Candidate issuer cert cannot be the same as the subject cert 'x'.
+ */
+ if (issuer != x && ctx->check_issued(ctx, x, issuer)
+ && (((x->ex_flags & EXFLAG_SI) != 0 && sk_X509_num(ctx->chain) == 1)
+ || !sk_X509_contains(ctx->chain, issuer))) {
rv = issuer;
if (x509_check_cert_time(ctx, rv, -1))
break;
@@ -331,30 +349,13 @@ static X509 *find_issuer(X509_STORE_CTX *ctx, STACK_OF(X509) *sk, X509 *x)
return rv;
}
-/*
- * Check that the given certificate 'x' is issued by the certificate 'issuer'
- * and the issuer is not yet in ctx->chain, where the exceptional case
- * that 'x' is self-issued and ctx->chain has just one element is allowed.
- */
+/* Check that the given certificate 'x' is issued by the certificate 'issuer' */
static int check_issued(X509_STORE_CTX *ctx, X509 *x, X509 *issuer)
{
- if (x509_likely_issued(issuer, x) != X509_V_OK)
- return 0;
- if ((x->ex_flags & EXFLAG_SI) == 0 || sk_X509_num(ctx->chain) != 1) {
- int i;
- X509 *ch;
-
- for (i = 0; i < sk_X509_num(ctx->chain); i++) {
- ch = sk_X509_value(ctx->chain, i);
- if (ch == issuer || X509_cmp(ch, issuer) == 0)
- return 0;
- }
- }
- return 1;
+ return x509_likely_issued(issuer, x) == X509_V_OK;
}
/* Alternative lookup method: look from a STACK stored in other_ctx */
-
static int get_issuer_sk(X509 **issuer, X509_STORE_CTX *ctx, X509 *x)
{
*issuer = find_issuer(ctx, ctx->other_ctx, x);
@@ -1740,7 +1741,7 @@ 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))
@@ -1748,7 +1749,7 @@ static int internal_verify(X509_STORE_CTX *ctx)
else {
if (ctx->param->flags & X509_V_FLAG_PARTIAL_CHAIN) {
xs = xi;
- goto check_cert;
+ goto check_cert_time;
}
if (n <= 0) {
if (!verify_cb_cert(ctx, xi, 0,
@@ -1756,7 +1757,7 @@ static int internal_verify(X509_STORE_CTX *ctx)
return 0;
xs = xi;
- goto check_cert;
+ goto check_cert_time;
}
n--;
@@ -1817,7 +1818,7 @@ static int internal_verify(X509_STORE_CTX *ctx)
}
}
- check_cert:
+ check_cert_time: /* in addition to RFC 5280, do also for trusted (root) cert */
/* Calls verify callback as needed */
if (!x509_check_cert_time(ctx, xs, n))
return 0;
diff --git a/doc/man1/verify.pod b/doc/man1/verify.pod
index 71288be40d..da2b702482 100644
--- a/doc/man1/verify.pod
+++ b/doc/man1/verify.pod
@@ -382,10 +382,14 @@ should be trusted for the supplied purpose.
For compatibility with previous versions of OpenSSL, a certificate with no
trust settings is considered to be valid for all purposes.
-The final operation is to check the validity of the certificate chain. The validity
-period is checked against the current system time and the notBefore and notAfter
-dates in the certificate. The certificate signatures are also checked at this
-point.
+The final operation is to check the validity of the certificate chain.
+For each element in the chain, including the root CA certificate,
+the validity period as specified by the C<notBefore> and C<notAfter> fields
+is checked against the current system time.
+The B<-attime> flag may be used to use a reference time other than "now."
+The certificate signature is checked as well
+(except for the signature of the typically self-signed root CA certificate,
+which is verified only if the B<-check_ss_sig> option is given).
If all operations complete successfully then certificate is considered valid. If
any operation fails then the certificate is not valid.
diff --git a/doc/man3/X509_STORE_set_verify_cb_func.pod b/doc/man3/X509_STORE_set_verify_cb_func.pod
index 526790938a..6d7098250d 100644
--- a/doc/man3/X509_STORE_set_verify_cb_func.pod
+++ b/doc/man3/X509_STORE_set_verify_cb_func.pod
@@ -137,9 +137,7 @@ I<If no function to get the issuer is provided, the internal default
function will be used instead.>
X509_STORE_set_check_issued() sets the function to check that a given
-certificate B<x> is issued by the issuer certificate B<issuer> and
-the issuer is not yet in the chain contained in <ctx>, where the exceptional
-case that B<x> is self-issued and ctx->chain has just one element is allowed.
+certificate B<x> is issued by the issuer certificate B<issuer>.
This function must return 0 on failure (among others if B<x> hasn't
been issued with B<issuer>) and 1 on success.
I<If no function to get the issuer is provided, the internal default
diff --git a/test/certs/root-expired.pem b/test/certs/root-expired.pem
new file mode 100644
index 0000000000..eb5b697ed2
--- /dev/null
+++ b/test/certs/root-expired.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC8jCCAdqgAwIBAgIBATANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdSb290
+IENBMB4XDTIwMTIwMjE0MTYwOVoXDTIwMTIwMTE0MTYwOVowEjEQMA4GA1UEAwwH
+Um9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOHmAPUGvKBG
+OHkPPx5xGRNtAt8rm3Zr/KywIe3WkQhCO6VjNexSW6CiSsXWAJQDl1o9uWco0n3j
+IVyk7cY8jY6E0Z1Uwz3ZdKKWdmdx+cYaUHez/XjuW+DjjIkjwpoi7D7UN54HzcAr
+VREXOjRCHGkNOhiw7RWUXsb9nofGHOeUGpLAXwXBc0PlA94JkckkztiOi34u4DFI
+0YYqalUmeugLNk6XseCkydpcaUsDgAhWg6Mfsiq4wUz+xbFN1MABqu2+ziW97mmt
+9gfNbiuhiVT1aOuYCe3JYGbLM2JKA7Bo1g6rX8E1VX79Ru6669y2oqPthX9337Vo
+IkN+ZiQjr8UCAwEAAaNTMFEwHQYDVR0OBBYEFI71Ja8em2uEPXyAmslTnE1y96NS
+MB8GA1UdIwQYMBaAFI71Ja8em2uEPXyAmslTnE1y96NSMA8GA1UdEwEB/wQFMAMB
+Af8wDQYJKoZIhvcNAQELBQADggEBAH1uqov7eXVT6GbhJ7foASTQpIaVi4GXIfbS
+bYKCb0erWkLfW7EKalOTBp5TjWONSM4mX2OlZag7yq1P1YwMaBA51OkH0Ojic9fX
+majK2S/ZyFI6NLoPqN0Uw/K1HHU0DXpK/mf3YdFOEZMf9LVlXR0O6og19HxBmNnN
+LhTOQ29IGqNzayHGBi4U8LG+UAe5sxlC+gnnQEPGMrOS1XElybtHIxnqk2LJDvXj
+2Dj12TCISD9bQ53oRkudTvTPyvxK6OsnFC/wTBmHk03yxnZdQEKyj9guahiRb+hj
+sz4mDWWMmelcr6veEfzzlUZK7aoIrpJmgukhv/Qafwczo38J5U0=
+-----END CERTIFICATE-----
diff --git a/test/certs/setup.sh b/test/certs/setup.sh
index 2bb01fa13e..04591bcc05 100755
--- a/test/certs/setup.sh
+++ b/test/certs/setup.sh
@@ -1,10 +1,11 @@
-#! /bin/sh
+#! /bin/bash
# Primary root: root-cert
# root cert variants: CA:false, key2, DN2
# trust variants: +serverAuth -serverAuth +clientAuth -clientAuth +anyEKU -anyEKU
#
./mkcert.sh genroot "Root CA" root-key root-cert
+DAYS=-1 ./mkcert.sh genroot "Root CA" root-key root-expired
./mkcert.sh genss "Root CA" root-key root-nonca
./mkcert.sh genroot "Root CA" root-key2 root-cert2
./mkcert.sh genroot "Root Cert 2" root-key root-name2
@@ -168,7 +169,7 @@ openssl x509 -in sca-cert.pem -trustout \
./mkcert.sh genee server.example ee-key ee-name2 ca-key ca-name2
./mkcert.sh genee -p clientAuth server.example ee-key ee-client ca-key ca-cert
./mkcert.sh genee server.example ee-key ee-pathlen ca-key ca-cert \
- -extfile <(echo "basicConstraints=CA:FALSE,pathlen:0")
+ -extfile <(echo "basicConstraints=CA:FALSE,pathlen:0") # bash needed here
#
openssl x509 -in ee-cert.pem -trustout \
-addtrust serverAuth -out ee+serverAuth.pem
diff --git a/test/recipes/25-test_verify.t b/test/recipes/25-test_verify.t
index 9b8648e670..1336b8a726 100644
--- a/test/recipes/25-test_verify.t
+++ b/test/recipes/25-test_verify.t
@@ -27,7 +27,7 @@ sub verify {
run(app([@args]));
}
-plan tests => 143;
+plan tests => 145;
# Canonical success
ok(verify("ee-cert", "sslserver", ["root-cert"], ["ca-cert"]),
@@ -132,6 +132,10 @@ ok(!verify("ee-cert", "sslserver", [], [qw(ca-cert)], "-partial_chain"),
"fail untrusted partial chain");
ok(verify("ee-cert", "sslserver", [qw(ca-cert)], [], "-partial_chain"),
"accept trusted partial chain");
+ok(!verify("ee-cert", "sslserver", [qw(ca-expired)], [], "-partial_chain"),
+ "reject expired trusted partial chain"); # this check is beyond RFC 5280
+ok(!verify("ee-cert", "sslserver", [qw(root-expired)], [qw(ca-cert)]),
+ "reject expired trusted root"); # this check is beyond RFC 5280
ok(verify("ee-cert", "sslserver", [qw(sca-cert)], [], "-partial_chain"),
"accept partial chain with server purpose");
ok(!verify("ee-cert", "sslserver", [qw(cca-cert)], [], "-partial_chain"),