aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorViktor Dukhovni <openssl-users@dukhovni.org>2016-01-28 03:01:45 -0500
committerViktor Dukhovni <openssl-users@dukhovni.org>2016-01-31 21:23:23 -0500
commit0daccd4dc1f1ac62181738a91714f35472e50f3c (patch)
tree5b7c2b6c5db0c2caf223ea978db03559b5eb90f8
parent1b4cf96f9b82ec3b06e7902bb21620a09cadd94e (diff)
downloadopenssl-0daccd4dc1f1ac62181738a91714f35472e50f3c.tar.gz
Check chain extensions also for trusted certificates
This includes basic constraints, key usages, issuer EKUs and auxiliary trust OIDs (given a trust suitably related to the intended purpose). Added tests and updated documentation. Reviewed-by: Dr. Stephen Henson <steve@openssl.org>
-rw-r--r--apps/opt.c13
-rw-r--r--crypto/x509/x509_trs.c19
-rw-r--r--crypto/x509/x509_vfy.c118
-rw-r--r--crypto/x509/x509_vpm.c6
-rw-r--r--doc/apps/verify.pod26
-rw-r--r--doc/apps/x509.pod9
-rw-r--r--doc/crypto/X509_VERIFY_PARAM_set_flags.pod17
-rw-r--r--include/openssl/x509.h2
-rw-r--r--test/certs/root+anyEKU.pem18
-rw-r--r--test/certs/root-anyEKU.pem18
-rw-r--r--test/certs/root2+clientAuth.pem19
-rw-r--r--test/certs/root2+serverAuth.pem19
-rw-r--r--test/certs/root2-serverAuth.pem19
-rwxr-xr-xtest/certs/setup.sh12
-rw-r--r--test/recipes/25-test_verify.t98
15 files changed, 315 insertions, 98 deletions
diff --git a/apps/opt.c b/apps/opt.c
index 14e05de023..2fbc9fe8b2 100644
--- a/apps/opt.c
+++ b/apps/opt.c
@@ -496,14 +496,25 @@ int opt_verify(int opt, X509_VERIFY_PARAM *vpm)
X509_VERIFY_PARAM_add0_policy(vpm, otmp);
break;
case OPT_V_PURPOSE:
+ /* purpose name -> purpose index */
i = X509_PURPOSE_get_by_sname(opt_arg());
if (i < 0) {
BIO_printf(bio_err, "%s: Invalid purpose %s\n", prog, opt_arg());
return 0;
}
+
+ /* purpose index -> purpose object */
xptmp = X509_PURPOSE_get0(i);
+
+ /* purpose object -> purpose value */
i = X509_PURPOSE_get_id(xptmp);
- X509_VERIFY_PARAM_set_purpose(vpm, i);
+
+ if (!X509_VERIFY_PARAM_set_purpose(vpm, i)) {
+ BIO_printf(bio_err,
+ "%s: Internal error setting purpose %s\n",
+ prog, opt_arg());
+ return 0;
+ }
break;
case OPT_V_VERIFY_NAME:
vtmp = X509_VERIFY_PARAM_lookup(opt_arg());
diff --git a/crypto/x509/x509_trs.c b/crypto/x509/x509_trs.c
index 7392c55953..c81c725ea1 100644
--- a/crypto/x509/x509_trs.c
+++ b/crypto/x509/x509_trs.c
@@ -276,7 +276,7 @@ static int trust_1oidany(X509_TRUST *trust, X509 *x, int flags)
static int trust_1oid(X509_TRUST *trust, X509 *x, int flags)
{
- if (x->aux)
+ if (x->aux && (x->aux->trust || x->aux->reject))
return obj_trust(trust->arg1, x, flags);
return X509_TRUST_UNTRUSTED;
}
@@ -293,23 +293,26 @@ static int trust_compat(X509_TRUST *trust, X509 *x, int flags)
static int obj_trust(int id, X509 *x, int flags)
{
- ASN1_OBJECT *obj;
+ X509_CERT_AUX *ax = x->aux;
int i;
- X509_CERT_AUX *ax;
- ax = x->aux;
+
if (!ax)
return X509_TRUST_UNTRUSTED;
if (ax->reject) {
for (i = 0; i < sk_ASN1_OBJECT_num(ax->reject); i++) {
- obj = sk_ASN1_OBJECT_value(ax->reject, i);
- if (OBJ_obj2nid(obj) == id)
+ ASN1_OBJECT *obj = sk_ASN1_OBJECT_value(ax->reject, i);
+ int nid = OBJ_obj2nid(obj);
+
+ if (nid == id || nid == NID_anyExtendedKeyUsage)
return X509_TRUST_REJECTED;
}
}
if (ax->trust) {
for (i = 0; i < sk_ASN1_OBJECT_num(ax->trust); i++) {
- obj = sk_ASN1_OBJECT_value(ax->trust, i);
- if (OBJ_obj2nid(obj) == id)
+ ASN1_OBJECT *obj = sk_ASN1_OBJECT_value(ax->trust, i);
+ int nid = OBJ_obj2nid(obj);
+
+ if (nid == id || nid == NID_anyExtendedKeyUsage)
return X509_TRUST_TRUSTED;
}
/*
diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c
index 39d37b99a7..14d6a8d74e 100644
--- a/crypto/x509/x509_vfy.c
+++ b/crypto/x509/x509_vfy.c
@@ -363,17 +363,60 @@ static STACK_OF(X509) *lookup_certs_sk(X509_STORE_CTX *ctx, X509_NAME *nm)
}
/*
+ * Check EE or CA certificate purpose. For trusted certificates explicit local
+ * auxiliary trust can be used to override EKU-restrictions.
+ */
+static int check_purpose(X509_STORE_CTX *ctx, X509 *x, int purpose, int depth,
+ int must_be_ca)
+{
+ int pu_ok = X509_check_purpose(x, purpose, must_be_ca > 0);
+ int tr_ok = X509_TRUST_UNTRUSTED;
+
+ /*
+ * For trusted certificates we want to see whether any auxiliary trust
+ * settings override the purpose constraints we failed to meet above.
+ *
+ * This is complicated by the fact that the trust ordinals in
+ * ctx->param->trust are entirely independent of the purpose ordinals in
+ * ctx->param->purpose!
+ *
+ * What connects them is their mutual initialization via calls from
+ * X509_STORE_CTX_set_default() into X509_VERIFY_PARAM_lookup() which sets
+ * related values of both param->trust and param->purpose. It is however
+ * typically possible to infer associated trust values from a purpose value
+ * via the X509_PURPOSE API.
+ *
+ * Therefore, we can only check for trust overrides when the purpose we're
+ * checking is the same as ctx->param->purpose and ctx->param->trust is
+ * also set, or can be inferred from the purpose.
+ */
+ if (depth >= ctx->num_untrusted && purpose == ctx->param->purpose)
+ tr_ok = X509_check_trust(x, ctx->param->trust, X509_TRUST_NO_SS_COMPAT);
+
+ if (tr_ok != X509_TRUST_REJECTED &&
+ (pu_ok == 1 ||
+ (pu_ok != 0 && (ctx->param->flags & X509_V_FLAG_X509_STRICT) == 0)))
+ return 1;
+
+ ctx->error = X509_V_ERR_INVALID_PURPOSE;
+ ctx->error_depth = depth;
+ ctx->current_cert = x;
+ return ctx->verify_cb(0, ctx);
+}
+
+/*
* Check a certificate chains extensions for consistency with the supplied
* purpose
*/
static int check_chain_extensions(X509_STORE_CTX *ctx)
{
- int i, ok = 0, must_be_ca, plen = 0;
+ int i, must_be_ca, plen = 0;
X509 *x;
int proxy_path_length = 0;
int purpose;
int allow_proxy_certs;
+ int num = sk_X509_num(ctx->chain);
/*-
* must_be_ca can have 1 of 3 values:
@@ -402,8 +445,7 @@ static int check_chain_extensions(X509_STORE_CTX *ctx)
purpose = ctx->param->purpose;
}
- /* Check all untrusted certificates */
- for (i = 0; i == 0 || i < ctx->num_untrusted; i++) {
+ for (i = 0; i < num; i++) {
int ret;
x = sk_X509_value(ctx->chain, i);
if (!(ctx->param->flags & X509_V_FLAG_IGNORE_CRITICAL)
@@ -411,17 +453,15 @@ static int check_chain_extensions(X509_STORE_CTX *ctx)
ctx->error = X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION;
ctx->error_depth = i;
ctx->current_cert = x;
- ok = ctx->verify_cb(0, ctx);
- if (!ok)
- goto end;
+ if (!ctx->verify_cb(0, ctx))
+ return 0;
}
if (!allow_proxy_certs && (x->ex_flags & EXFLAG_PROXY)) {
ctx->error = X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED;
ctx->error_depth = i;
ctx->current_cert = x;
- ok = ctx->verify_cb(0, ctx);
- if (!ok)
- goto end;
+ if (!ctx->verify_cb(0, ctx))
+ return 0;
}
ret = X509_check_ca(x);
switch (must_be_ca) {
@@ -453,22 +493,12 @@ static int check_chain_extensions(X509_STORE_CTX *ctx)
if (ret == 0) {
ctx->error_depth = i;
ctx->current_cert = x;
- ok = ctx->verify_cb(0, ctx);
- if (!ok)
- goto end;
+ if (! ctx->verify_cb(0, ctx))
+ return 0;
}
- if (ctx->param->purpose > 0) {
- ret = X509_check_purpose(x, purpose, must_be_ca > 0);
- if ((ret == 0)
- || ((ctx->param->flags & X509_V_FLAG_X509_STRICT)
- && (ret != 1))) {
- ctx->error = X509_V_ERR_INVALID_PURPOSE;
- ctx->error_depth = i;
- ctx->current_cert = x;
- ok = ctx->verify_cb(0, ctx);
- if (!ok)
- goto end;
- }
+ if (purpose > 0) {
+ if (!check_purpose(ctx, x, purpose, i, must_be_ca))
+ return 0;
}
/* Check pathlen if not self issued */
if ((i > 1) && !(x->ex_flags & EXFLAG_SI)
@@ -477,9 +507,8 @@ static int check_chain_extensions(X509_STORE_CTX *ctx)
ctx->error = X509_V_ERR_PATH_LENGTH_EXCEEDED;
ctx->error_depth = i;
ctx->current_cert = x;
- ok = ctx->verify_cb(0, ctx);
- if (!ok)
- goto end;
+ if (!ctx->verify_cb(0, ctx))
+ return 0;
}
/* Increment path length if not self issued */
if (!(x->ex_flags & EXFLAG_SI))
@@ -494,18 +523,15 @@ static int check_chain_extensions(X509_STORE_CTX *ctx)
ctx->error = X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED;
ctx->error_depth = i;
ctx->current_cert = x;
- ok = ctx->verify_cb(0, ctx);
- if (!ok)
- goto end;
+ if (!ctx->verify_cb(0, ctx))
+ return 0;
}
proxy_path_length++;
must_be_ca = 0;
} else
must_be_ca = 1;
}
- ok = 1;
- end:
- return ok;
+ return 1;
}
static int check_name_constraints(X509_STORE_CTX *ctx)
@@ -2016,11 +2042,20 @@ void X509_STORE_CTX_set0_crls(X509_STORE_CTX *ctx, STACK_OF(X509_CRL) *sk)
int X509_STORE_CTX_set_purpose(X509_STORE_CTX *ctx, int purpose)
{
+ /*
+ * XXX: Why isn't this function always used to set the associated trust?
+ * Should there even be a VPM->trust field at all? Or should the trust
+ * always be inferred from the purpose by X509_STORE_CTX_init().
+ */
return X509_STORE_CTX_purpose_inherit(ctx, 0, purpose, 0);
}
int X509_STORE_CTX_set_trust(X509_STORE_CTX *ctx, int trust)
{
+ /*
+ * XXX: See above, this function would only be needed when the default
+ * trust for the purpose needs an override in a corner case.
+ */
return X509_STORE_CTX_purpose_inherit(ctx, 0, 0, trust);
}
@@ -2054,6 +2089,11 @@ int X509_STORE_CTX_purpose_inherit(X509_STORE_CTX *ctx, int def_purpose,
ptmp = X509_PURPOSE_get0(idx);
if (ptmp->trust == X509_TRUST_DEFAULT) {
idx = X509_PURPOSE_get_by_id(def_purpose);
+ /*
+ * XXX: In the two callers above def_purpose is always 0, which is
+ * not a known value, so idx will always be -1. How is the
+ * X509_TRUST_DEFAULT case actually supposed to be handled?
+ */
if (idx == -1) {
X509err(X509_F_X509_STORE_CTX_PURPOSE_INHERIT,
X509_R_UNKNOWN_PURPOSE_ID);
@@ -2211,6 +2251,18 @@ int X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store, X509 *x509,
goto err;
}
+ /*
+ * XXX: For now, continue to inherit trust from VPM, but infer from the
+ * purpose if this still yields the default value.
+ */
+ if (ctx->param->trust == X509_TRUST_DEFAULT) {
+ int idx = X509_PURPOSE_get_by_id(ctx->param->purpose);
+ X509_PURPOSE *xp = X509_PURPOSE_get0(idx);
+
+ if (xp != NULL)
+ ctx->param->trust = X509_PURPOSE_get_trust(xp);
+ }
+
if (CRYPTO_new_ex_data(CRYPTO_EX_INDEX_X509_STORE_CTX, ctx,
&ctx->ex_data))
return 1;
diff --git a/crypto/x509/x509_vpm.c b/crypto/x509/x509_vpm.c
index 295ce885a1..41b0fde4a5 100644
--- a/crypto/x509/x509_vpm.c
+++ b/crypto/x509/x509_vpm.c
@@ -133,7 +133,7 @@ static void x509_verify_param_zero(X509_VERIFY_PARAM *param)
return;
param->name = NULL;
param->purpose = 0;
- param->trust = 0;
+ param->trust = X509_TRUST_DEFAULT;
/*
* param->inh_flags = X509_VP_FLAG_DEFAULT;
*/
@@ -243,7 +243,7 @@ int X509_VERIFY_PARAM_inherit(X509_VERIFY_PARAM *dest,
to_overwrite = 0;
x509_verify_param_copy(purpose, 0);
- x509_verify_param_copy(trust, 0);
+ x509_verify_param_copy(trust, X509_TRUST_DEFAULT);
x509_verify_param_copy(depth, -1);
/* If overwrite or check time not set, copy across */
@@ -511,7 +511,7 @@ static const X509_VERIFY_PARAM default_table[] = {
"default", /* X509 default parameters */
0, /* Check time */
0, /* internal flags */
- 0, /* flags */
+ X509_V_FLAG_TRUSTED_FIRST, /* flags */
0, /* purpose */
0, /* trust */
100, /* depth */
diff --git a/doc/apps/verify.pod b/doc/apps/verify.pod
index e15a5de968..6d54592687 100644
--- a/doc/apps/verify.pod
+++ b/doc/apps/verify.pod
@@ -198,14 +198,16 @@ When constructing the certificate chain, use the trusted certificates specified
via B<-CAfile>, B<-CApath> or B<-trusted> before any certificates specified via
B<-untrusted>.
This can be useful in environments with Bridge or Cross-Certified CAs.
+As of OpenSSL 1.1.0 this option is on by default and cannot be disabled.
=item B<-no_alt_chains>
-When building a certificate chain, if the first certificate chain found is not
-trusted, then OpenSSL will continue to check to see if an alternative chain can
-be found that is trusted. With this option that behaviour is suppressed so that
-only the first chain found is ever used. Using this option will force the
-behaviour to match that of OpenSSL versions prior to 1.1.0.
+By default, unless B<-trusted_first> is specified, when building a certificate
+chain, if the first certificate chain found is not trusted, then OpenSSL will
+attempt to replace untrusted issuer certificates with certificates from the
+trust store to see if an alternative chain can be found that is trusted.
+As of OpenSSL 1.1.0, with B<-trusted_first> always on, this option has no
+effect.
=item B<-untrusted file>
@@ -264,13 +266,17 @@ the subject certificate.
Use default verification policies like trust model and required certificate
policies identified by B<name>.
+The trust model determines which auxiliary trust or reject OIDs are applicable
+to verifying the given certificate chain.
+See the B<-addtrust> and B<-addreject> options of the L<x509(1)> command-line
+utility.
Supported policy names include: B<default>, B<pkcs7>, B<smime_sign>,
B<ssl_client>, B<ssl_server>.
-This checks not only the purpose of the leaf certificate, but also the
-trust settings of the trusted CAs.
-When in doubt, use this option rather than B<-purpose>.
-The B<-verify_name> option more closely matches how certificates are checked in
-e.g. SSL and S/MIME.
+These mimics the combinations of purpose and trust settings used in SSL, CMS
+and S/MIME.
+As of OpenSSL 1.1.0, the trust model is inferred from the purpose when not
+specified, so the B<-verify_name> options are functionally equivalent to the
+corresponding B<-purpose> settings.
=item B<-x509_strict>
diff --git a/doc/apps/x509.pod b/doc/apps/x509.pod
index 1c98e9decf..637eedc947 100644
--- a/doc/apps/x509.pod
+++ b/doc/apps/x509.pod
@@ -289,9 +289,12 @@ clears all the prohibited or rejected uses of the certificate.
=item B<-addtrust arg>
-adds a trusted certificate use. Any object name can be used here
-but currently only B<clientAuth> (SSL client use), B<serverAuth>
-(SSL server use) and B<emailProtection> (S/MIME email) are used.
+adds a trusted certificate use.
+Any object name can be used here but currently only B<clientAuth> (SSL client
+use), B<serverAuth> (SSL server use), B<emailProtection> (S/MIME email) and
+B<anyExtendedKeyUsage> are used.
+As of OpenSSL 1.1.0, the last of these blocks all purposes when rejected or
+enables all purposes when trusted.
Other OpenSSL applications may define additional uses.
=item B<-addreject arg>
diff --git a/doc/crypto/X509_VERIFY_PARAM_set_flags.pod b/doc/crypto/X509_VERIFY_PARAM_set_flags.pod
index a2219d2aae..53a063a48c 100644
--- a/doc/crypto/X509_VERIFY_PARAM_set_flags.pod
+++ b/doc/crypto/X509_VERIFY_PARAM_set_flags.pod
@@ -197,11 +197,20 @@ verification. If this flag is set then additional status codes will be sent
to the verification callback and it B<must> be prepared to handle such cases
without assuming they are hard errors.
+If B<X509_V_FLAG_TRUSTED_FIRST> is set, when constructing the certificate chain,
+L<X509_verify_cert(3)> will search the trust store for issuer certificates before
+searching the provided untrusted certificates.
+As of OpenSSL 1.1.0 this option is on by default and cannot be disabled.
+
The B<X509_V_FLAG_NO_ALT_CHAINS> flag suppresses checking for alternative
-chains. By default, when building a certificate chain, if the first certificate
-chain found is not trusted, then OpenSSL will continue to check to see if an
-alternative chain can be found that is trusted. With this flag set the behaviour
-will match that of OpenSSL versions prior to 1.1.0.
+chains.
+By default, unless B<X509_V_FLAG_TRUSTED_FIRST> is set, when building a
+certificate chain, if the first certificate chain found is not trusted, then
+OpenSSL will attempt to replace untrusted certificates supplied by the peer
+with certificates from the trust store to see if an alternative chain can be
+found that is trusted.
+As of OpenSSL 1.1.0, with B<X509_V_FLAG_TRUSTED_FIRST> always set, this option
+has no effect.
The B<X509_V_FLAG_NO_CHECK_TIME> flag suppresses checking the validity period
of certificates and CRLs against the current time. If X509_VERIFY_PARAM_set_time()
diff --git a/include/openssl/x509.h b/include/openssl/x509.h
index 31f784dccc..3a1c5e2f56 100644
--- a/include/openssl/x509.h
+++ b/include/openssl/x509.h
@@ -183,7 +183,7 @@ DEFINE_STACK_OF(X509_TRUST)
/* standard trust ids */
-# define X509_TRUST_DEFAULT -1/* Only valid in purpose settings */
+# define X509_TRUST_DEFAULT 0 /* Only valid in purpose settings */
# define X509_TRUST_COMPAT 1
# define X509_TRUST_SSL_CLIENT 2
diff --git a/test/certs/root+anyEKU.pem b/test/certs/root+anyEKU.pem
new file mode 100644
index 0000000000..97e0732189
--- /dev/null
+++ b/test/certs/root+anyEKU.pem
@@ -0,0 +1,18 @@
+-----BEGIN TRUSTED CERTIFICATE-----
+MIIC8TCCAdmgAwIBAgIBATANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdSb290
+IENBMCAXDTE2MDExNTA4MTk0OVoYDzIxMTYwMTE2MDgxOTQ5WjASMRAwDgYDVQQD
+DAdSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4eYA9Qa8
+oEY4eQ8/HnEZE20C3yubdmv8rLAh7daRCEI7pWM17FJboKJKxdYAlAOXWj25ZyjS
+feMhXKTtxjyNjoTRnVTDPdl0opZ2Z3H5xhpQd7P9eO5b4OOMiSPCmiLsPtQ3ngfN
+wCtVERc6NEIcaQ06GLDtFZRexv2eh8Yc55QaksBfBcFzQ+UD3gmRySTO2I6Lfi7g
+MUjRhipqVSZ66As2Tpex4KTJ2lxpSwOACFaDox+yKrjBTP7FsU3UwAGq7b7OJb3u
+aa32B81uK6GJVPVo65gJ7clgZsszYkoDsGjWDqtfwTVVfv1G7rrr3Laio+2Ff3ff
+tWgiQ35mJCOvxQIDAQABo1AwTjAdBgNVHQ4EFgQUjvUlrx6ba4Q9fICayVOcTXL3
+o1IwHwYDVR0jBBgwFoAUjvUlrx6ba4Q9fICayVOcTXL3o1IwDAYDVR0TBAUwAwEB
+/zANBgkqhkiG9w0BAQsFAAOCAQEAyRRJx27WYOogPXZpPfAMt8ptapr/ugLWGLlw
+bzKySoyLpoV2/YNAvTAGB90iFq6x/ujjrK41/ES0p3v38/Qfuxo24gcZgc/oYLV2
+UqR+uGCx68p2OWLYctBsARtYWOEgPhHFb9aVxcOQKyZHtivDX0wLGX+nqZoHX9IY
+mc0sbpRBRMzxRsChbzD5re9kZ5NrgkjA6DJ7jYh2GitOM6oIU3Dd9+pk3bCEkFUg
+Ry9qN/k+AyeqH1Qcb5LU+MTmlw8bmyzmMOBZgdegtO4HshcBMO054KSB3WSfBPDO
+bEhZ0vm/lw63TGi88yIMtlkmcU2g0RKpeQI96G6QeqHyKF3p8DAIMAYGBFUdJQA=
+-----END TRUSTED CERTIFICATE-----
diff --git a/test/certs/root-anyEKU.pem b/test/certs/root-anyEKU.pem
new file mode 100644
index 0000000000..712b1f572a
--- /dev/null
+++ b/test/certs/root-anyEKU.pem
@@ -0,0 +1,18 @@
+-----BEGIN TRUSTED CERTIFICATE-----
+MIIC8TCCAdmgAwIBAgIBATANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdSb290
+IENBMCAXDTE2MDExNTA4MTk0OVoYDzIxMTYwMTE2MDgxOTQ5WjASMRAwDgYDVQQD
+DAdSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4eYA9Qa8
+oEY4eQ8/HnEZE20C3yubdmv8rLAh7daRCEI7pWM17FJboKJKxdYAlAOXWj25ZyjS
+feMhXKTtxjyNjoTRnVTDPdl0opZ2Z3H5xhpQd7P9eO5b4OOMiSPCmiLsPtQ3ngfN
+wCtVERc6NEIcaQ06GLDtFZRexv2eh8Yc55QaksBfBcFzQ+UD3gmRySTO2I6Lfi7g
+MUjRhipqVSZ66As2Tpex4KTJ2lxpSwOACFaDox+yKrjBTP7FsU3UwAGq7b7OJb3u
+aa32B81uK6GJVPVo65gJ7clgZsszYkoDsGjWDqtfwTVVfv1G7rrr3Laio+2Ff3ff
+tWgiQ35mJCOvxQIDAQABo1AwTjAdBgNVHQ4EFgQUjvUlrx6ba4Q9fICayVOcTXL3
+o1IwHwYDVR0jBBgwFoAUjvUlrx6ba4Q9fICayVOcTXL3o1IwDAYDVR0TBAUwAwEB
+/zANBgkqhkiG9w0BAQsFAAOCAQEAyRRJx27WYOogPXZpPfAMt8ptapr/ugLWGLlw
+bzKySoyLpoV2/YNAvTAGB90iFq6x/ujjrK41/ES0p3v38/Qfuxo24gcZgc/oYLV2
+UqR+uGCx68p2OWLYctBsARtYWOEgPhHFb9aVxcOQKyZHtivDX0wLGX+nqZoHX9IY
+mc0sbpRBRMzxRsChbzD5re9kZ5NrgkjA6DJ7jYh2GitOM6oIU3Dd9+pk3bCEkFUg
+Ry9qN/k+AyeqH1Qcb5LU+MTmlw8bmyzmMOBZgdegtO4HshcBMO054KSB3WSfBPDO
+bEhZ0vm/lw63TGi88yIMtlkmcU2g0RKpeQI96G6QeqHyKF3p8DAIoAYGBFUdJQA=
+-----END TRUSTED CERTIFICATE-----
diff --git a/test/certs/root2+clientAuth.pem b/test/certs/root2+clientAuth.pem
new file mode 100644
index 0000000000..41355b040e
--- /dev/null
+++ b/test/certs/root2+clientAuth.pem
@@ -0,0 +1,19 @@
+-----BEGIN TRUSTED CERTIFICATE-----
+MIIC8TCCAdmgAwIBAgIBATANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdSb290
+IENBMCAXDTE2MDExNTA4MTk0OVoYDzIxMTYwMTE2MDgxOTQ5WjASMRAwDgYDVQQD
+DAdSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyB6dJAD5
+wbStQf4HE0EhldtDShNVQ/jhDu6s2Ka30FdP4ml1+c2Py7ODUSjSCegXaBIOXCA+
+R0zaBAJ3ZeqXx3UrE9PiXaHRGZcoPtX4mK9IOHhIdxwPUa6ceSOJn4cHY+p0cFLp
+/5bnUErp4IqbL1bMd4v8fFxJ0ZDGJahfLiurnYUyalaNCHK+hK2+RaeRgPlsXfiU
+/vwhhjFhdhixbPm8l+S+2xNySV1JAAzrUvEDdNZ0iBvuVcS2mlhSKTht5Zeg+0C6
+7kYYqxM9CVZCwcV/aSUImwjeFsNMJsl/nFyEacu6vXz0rjvLwPzTAeVYZy592Gwv
+akWOtiDdap7WJQIDAQABo1AwTjAdBgNVHQ4EFgQUnM5mQjCrHAgmX3MZbd8Pp65Y
+Uh4wHwYDVR0jBBgwFoAUnM5mQjCrHAgmX3MZbd8Pp65YUh4wDAYDVR0TBAUwAwEB
+/zANBgkqhkiG9w0BAQsFAAOCAQEADkH6+rUX2QD5TMBn8x4PR9mTQsxhD2k8K2bv
+NpbsWX0ta2pDPhiBpIbrTrTmw656MMRkwMLYIAX7BFhyjO9gO0nVXfU1SSTDsso+
+qu/K1t2US/rLeJQn8gYiTw6AqmvxHOndLaZQrYef4rUzsYnahNzxcoS1FMVxoJFM
+o+1Wo0BFBlASv5Az0iFfjd1Uy3+AHB41+2vczNIWSki3mg4hzus2PSS4AA9IYeh+
+zU/HJMddnVedLKNstTAfR85ftACtsP6JhBqCBqC4mCVsN2ZlgucETbsOMyWYB4+y
+9b6JIYDA1wxNVBXwN+D4MyALxjmjwcTsL6pXgoVc0JEJWVqQ1zAMMAoGCCsGAQUF
+BwMC
+-----END TRUSTED CERTIFICATE-----
diff --git a/test/certs/root2+serverAuth.pem b/test/certs/root2+serverAuth.pem
new file mode 100644
index 0000000000..52053f1bf6
--- /dev/null
+++ b/test/certs/root2+serverAuth.pem
@@ -0,0 +1,19 @@
+-----BEGIN TRUSTED CERTIFICATE-----
+MIIC8TCCAdmgAwIBAgIBATANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdSb290
+IENBMCAXDTE2MDExNTA4MTk0OVoYDzIxMTYwMTE2MDgxOTQ5WjASMRAwDgYDVQQD
+DAdSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyB6dJAD5
+wbStQf4HE0EhldtDShNVQ/jhDu6s2Ka30FdP4ml1+c2Py7ODUSjSCegXaBIOXCA+
+R0zaBAJ3ZeqXx3UrE9PiXaHRGZcoPtX4mK9IOHhIdxwPUa6ceSOJn4cHY+p0cFLp
+/5bnUErp4IqbL1bMd4v8fFxJ0ZDGJahfLiurnYUyalaNCHK+hK2+RaeRgPlsXfiU
+/vwhhjFhdhixbPm8l+S+2xNySV1JAAzrUvEDdNZ0iBvuVcS2mlhSKTht5Zeg+0C6
+7kYYqxM9CVZCwcV/aSUImwjeFsNMJsl/nFyEacu6vXz0rjvLwPzTAeVYZy592Gwv
+akWOtiDdap7WJQIDAQABo1AwTjAdBgNVHQ4EFgQUnM5mQjCrHAgmX3MZbd8Pp65Y
+Uh4wHwYDVR0jBBgwFoAUnM5mQjCrHAgmX3MZbd8Pp65YUh4wDAYDVR0TBAUwAwEB
+/zANBgkqhkiG9w0BAQsFAAOCAQEADkH6+rUX2QD5TMBn8x4PR9mTQsxhD2k8K2bv
+NpbsWX0ta2pDPhiBpIbrTrTmw656MMRkwMLYIAX7BFhyjO9gO0nVXfU1SSTDsso+
+qu/K1t2US/rLeJQn8gYiTw6AqmvxHOndLaZQrYef4rUzsYnahNzxcoS1FMVxoJFM
+o+1Wo0BFBlASv5Az0iFfjd1Uy3+AHB41+2vczNIWSki3mg4hzus2PSS4AA9IYeh+
+zU/HJMddnVedLKNstTAfR85ftACtsP6JhBqCBqC4mCVsN2ZlgucETbsOMyWYB4+y
+9b6JIYDA1wxNVBXwN+D4MyALxjmjwcTsL6pXgoVc0JEJWVqQ1zAMMAoGCCsGAQUF
+BwMB
+-----END TRUSTED CERTIFICATE-----
diff --git a/test/certs/root2-serverAuth.pem b/test/certs/root2-serverAuth.pem
new file mode 100644
index 0000000000..dae848a1a9
--- /dev/null
+++ b/test/certs/root2-serverAuth.pem
@@ -0,0 +1,19 @@
+-----BEGIN TRUSTED CERTIFICATE-----
+MIIC8TCCAdmgAwIBAgIBATANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdSb290
+IENBMCAXDTE2MDExNTA4MTk0OVoYDzIxMTYwMTE2MDgxOTQ5WjASMRAwDgYDVQQD
+DAdSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyB6dJAD5
+wbStQf4HE0EhldtDShNVQ/jhDu6s2Ka30FdP4ml1+c2Py7ODUSjSCegXaBIOXCA+
+R0zaBAJ3ZeqXx3UrE9PiXaHRGZcoPtX4mK9IOHhIdxwPUa6ceSOJn4cHY+p0cFLp
+/5bnUErp4IqbL1bMd4v8fFxJ0ZDGJahfLiurnYUyalaNCHK+hK2+RaeRgPlsXfiU
+/vwhhjFhdhixbPm8l+S+2xNySV1JAAzrUvEDdNZ0iBvuVcS2mlhSKTht5Zeg+0C6
+7kYYqxM9CVZCwcV/aSUImwjeFsNMJsl/nFyEacu6vXz0rjvLwPzTAeVYZy592Gwv
+akWOtiDdap7WJQIDAQABo1AwTjAdBgNVHQ4EFgQUnM5mQjCrHAgmX3MZbd8Pp65Y
+Uh4wHwYDVR0jBBgwFoAUnM5mQjCrHAgmX3MZbd8Pp65YUh4wDAYDVR0TBAUwAwEB
+/zANBgkqhkiG9w0BAQsFAAOCAQEADkH6+rUX2QD5TMBn8x4PR9mTQsxhD2k8K2bv
+NpbsWX0ta2pDPhiBpIbrTrTmw656MMRkwMLYIAX7BFhyjO9gO0nVXfU1SSTDsso+
+qu/K1t2US/rLeJQn8gYiTw6AqmvxHOndLaZQrYef4rUzsYnahNzxcoS1FMVxoJFM
+o+1Wo0BFBlASv5Az0iFfjd1Uy3+AHB41+2vczNIWSki3mg4hzus2PSS4AA9IYeh+
+zU/HJMddnVedLKNstTAfR85ftACtsP6JhBqCBqC4mCVsN2ZlgucETbsOMyWYB4+y
+9b6JIYDA1wxNVBXwN+D4MyALxjmjwcTsL6pXgoVc0JEJWVqQ1zAMoAoGCCsGAQUF
+BwMB
+-----END TRUSTED CERTIFICATE-----
diff --git a/test/certs/setup.sh b/test/certs/setup.sh
index b50f7e3015..795ff4aa0f 100755
--- a/test/certs/setup.sh
+++ b/test/certs/setup.sh
@@ -2,7 +2,7 @@
# Primary root: root-cert
# root certs variants: CA:false, key2, DN2
-# trust variants: +serverAuth -serverAuth +clientAuth
+# trust variants: +serverAuth -serverAuth +clientAuth +anyEKU -anyEKU
#
./mkcert.sh genroot "Root CA" root-key root-cert
./mkcert.sh genss "Root CA" root-key root-nonca
@@ -15,6 +15,16 @@ openssl x509 -in root-cert.pem -trustout \
-addreject serverAuth -out root-serverAuth.pem
openssl x509 -in root-cert.pem -trustout \
-addtrust clientAuth -out root+clientAuth.pem
+openssl x509 -in root-cert.pem -trustout \
+ -addreject anyExtendedKeyUsage -out root-anyEKU.pem
+openssl x509 -in root-cert.pem -trustout \
+ -addtrust anyExtendedKeyUsage -out root+anyEKU.pem
+openssl x509 -in root-cert2.pem -trustout \
+ -addtrust serverAuth -out root2+serverAuth.pem
+openssl x509 -in root-cert2.pem -trustout \
+ -addreject serverAuth -out root2-serverAuth.pem
+openssl x509 -in root-cert2.pem -trustout \
+ -addtrust clientAuth -out root2+clientAuth.pem
# Primary intermediate ca: ca-cert
# ca variants: CA:false, key2, DN2, issuer2, expired
diff --git a/test/recipes/25-test_verify.t b/test/recipes/25-test_verify.t
index 56d83077d4..93d993dcdc 100644
--- a/test/recipes/25-test_verify.t
+++ b/test/recipes/25-test_verify.t
@@ -9,80 +9,110 @@ use OpenSSL::Test qw/:DEFAULT top_file/;
setup("test_verify");
sub verify {
- my ($cert, $vname, $trusted, $untrusted, @opts) = @_;
- my @args = qw(openssl verify -verify_name);
+ my ($cert, $purpose, $trusted, $untrusted, @opts) = @_;
+ my @args = qw(openssl verify -purpose);
my @path = qw(test certs);
- push(@args, "$vname", @opts);
+ push(@args, "$purpose", @opts);
for (@$trusted) { push(@args, "-trusted", top_file(@path, "$_.pem")) }
for (@$untrusted) { push(@args, "-untrusted", top_file(@path, "$_.pem")) }
push(@args, top_file(@path, "$cert.pem"));
run(app([@args]));
}
-plan tests => 29;
+plan tests => 38;
# Canonical success
-ok(verify("ee-cert", "ssl_server", ["root-cert"], ["ca-cert"]),
+ok(verify("ee-cert", "sslserver", ["root-cert"], ["ca-cert"]),
"verify valid chain");
# Root CA variants
-ok(verify("ee-cert", "ssl_server", [qw(root-nonca)], [qw(ca-cert)]),
- "Trusted certs not subject to CA:true checks");
-ok(!verify("ee-cert", "ssl_server", [qw(root-cert2)], [qw(ca-cert)]),
+ok(!verify("ee-cert", "sslserver", [qw(root-nonca)], [qw(ca-cert)]),
+ "Trusted CA certs now subject to CA:true checks");
+ok(!verify("ee-cert", "sslserver", [qw(root-cert2)], [qw(ca-cert)]),
"fail wrong root key");
-ok(!verify("ee-cert", "ssl_server", [qw(root-name2)], [qw(ca-cert)]),
+ok(!verify("ee-cert", "sslserver", [qw(root-name2)], [qw(ca-cert)]),
"fail wrong root DN");
-ok(verify("ee-cert", "ssl_server", [qw(root+serverAuth)], [qw(ca-cert)]),
+ok(verify("ee-cert", "sslserver", [qw(root+serverAuth)], [qw(ca-cert)]),
"accept right EKU");
-ok(!verify("ee-cert", "ssl_server", [qw(root-serverAuth)], [qw(ca-cert)]),
+ok(verify("ee-cert", "sslserver", [qw(root+anyEKU)], [qw(ca-cert)]),
+ "accept anyEKU");
+ok(!verify("ee-cert", "sslserver", [qw(root-serverAuth)], [qw(ca-cert)]),
"fail rejected EKU");
-ok(!verify("ee-cert", "ssl_server", [qw(root+clientAuth)], [qw(ca-cert)]),
+ok(!verify("ee-cert", "sslserver", [qw(root-anyEKU)], [qw(ca-cert)]),
+ "fail rejected anyEKU");
+ok(!verify("ee-cert", "sslserver", [qw(root+clientAuth)], [qw(ca-cert)]),
"fail wrong EKU");
+# Check that trusted-first is on by setting up paths to different roots
+# depending on whether the intermediate is the trusted or untrusted one.
+#
+ok(verify("ee-cert", "sslserver", [qw(root-serverAuth root-cert2 ca-root2)],
+ [qw(ca-cert)]),
+ "verify trusted-first path");
+ok(verify("ee-cert", "sslserver", [qw(root-cert root2+serverAuth ca-root2)],
+ [qw(ca-cert)]),
+ "verify trusted-first path right EKU");
+ok(!verify("ee-cert", "sslserver", [qw(root-cert root2-serverAuth ca-root2)],
+ [qw(ca-cert)]),
+ "fail trusted-first path rejected EKU");
+ok(!verify("ee-cert", "sslserver", [qw(root-cert root2+clientAuth ca-root2)],
+ [qw(ca-cert)]),
+ "fail trusted-first path wrong EKU");
+
# CA variants
-ok(!verify("ee-cert", "ssl_server", [qw(root-cert)], [qw(ca-nonca)]),
+ok(!verify("ee-cert", "sslserver", [qw(root-cert)], [qw(ca-nonca)]),
"fail non-CA");
-ok(!verify("ee-cert", "ssl_server", [qw(root-cert)], [qw(ca-cert2)]),
+ok(!verify("ee-cert", "sslserver", [qw(root-cert)], [qw(ca-cert2)]),
"fail wrong CA key");
-ok(!verify("ee-cert", "ssl_server", [qw(root-cert)], [qw(ca-name2)]),
+ok(!verify("ee-cert", "sslserver", [qw(root-cert)], [qw(ca-name2)]),
"fail wrong CA DN");
-ok(!verify("ee-cert", "ssl_server", [qw(root-cert)], [qw(ca-root2)]),
+ok(!verify("ee-cert", "sslserver", [qw(root-cert)], [qw(ca-root2)]),
"fail wrong CA issuer");
-ok(!verify("ee-cert", "ssl_server", [], [qw(ca-cert)], "-partial_chain"),
+ok(!verify("ee-cert", "sslserver", [], [qw(ca-cert)], "-partial_chain"),
"fail untrusted partial");
-ok(!verify("ee-cert", "ssl_server", [], [qw(ca+serverAuth)], "-partial_chain"),
+ok(!verify("ee-cert", "sslserver", [], [qw(ca+serverAuth)], "-partial_chain"),
"fail untrusted EKU partial");
-ok(verify("ee-cert", "ssl_server", [qw(ca+serverAuth)], [], "-partial_chain"),
+ok(verify("ee-cert", "sslserver", [qw(ca+serverAuth)], [], "-partial_chain"),
"accept trusted EKU partial");
-ok(!verify("ee-cert", "ssl_server", [qw(ca-serverAuth)], [], "-partial_chain"),
+ok(!verify("ee-cert", "sslserver", [qw(ca-serverAuth)], [], "-partial_chain"),
"fail rejected EKU partial");
-ok(!verify("ee-cert", "ssl_server", [qw(ca+clientAuth)], [], "-partial_chain"),
+ok(!verify("ee-cert", "sslserver", [qw(ca+clientAuth)], [], "-partial_chain"),
"fail wrong EKU partial");
+# We now test auxiliary trust even for intermediate trusted certs without
+# -partial_chain. Note that "-trusted_first" is now always on and cannot
+# be disabled.
+ok(verify("ee-cert", "sslserver", [qw(root-cert ca+serverAuth)], [qw(ca-cert)]),
+ "accept trusted EKU");
+ok(!verify("ee-cert", "sslserver", [qw(root-cert ca-serverAuth)], [qw(ca-cert)]),
+ "fail rejected EKU");
+ok(!verify("ee-cert", "sslserver", [qw(root-cert ca+clientAuth)], [qw(ca-cert)]),
+ "fail wrong EKU");
+
# EE variants
-ok(verify("ee-client", "ssl_client", [qw(root-cert)], [qw(ca-cert)]),
+ok(verify("ee-client", "sslclient", [qw(root-cert)], [qw(ca-cert)]),
"accept client cert");
-ok(!verify("ee-client", "ssl_server", [qw(root-cert)], [qw(ca-cert)]),
+ok(!verify("ee-client", "sslserver", [qw(root-cert)], [qw(ca-cert)]),
"fail wrong leaf purpose");
-ok(!verify("ee-cert", "ssl_client", [qw(root-cert)], [qw(ca-cert)]),
+ok(!verify("ee-cert", "sslclient", [qw(root-cert)], [qw(ca-cert)]),
"fail wrong leaf purpose");
-ok(!verify("ee-cert2", "ssl_server", [qw(root-cert)], [qw(ca-cert)]),
+ok(!verify("ee-cert2", "sslserver", [qw(root-cert)], [qw(ca-cert)]),
"fail wrong CA key");
-ok(!verify("ee-name2", "ssl_server", [qw(root-cert)], [qw(ca-cert)]),
+ok(!verify("ee-name2", "sslserver", [qw(root-cert)], [qw(ca-cert)]),
"fail wrong CA name");
-ok(!verify("ee-expired", "ssl_server", [qw(root-cert)], [qw(ca-cert)]),
+ok(!verify("ee-expired", "sslserver", [qw(root-cert)], [qw(ca-cert)]),
"fail expired leaf");
-ok(verify("ee-cert", "ssl_server", [qw(ee-cert)], [], "-partial_chain"),
+ok(verify("ee-cert", "sslserver", [qw(ee-cert)], [], "-partial_chain"),
"accept last-resort direct leaf match");
-ok(verify("ee-client", "ssl_client", [qw(ee-client)], [], "-partial_chain"),
+ok(verify("ee-client", "sslclient", [qw(ee-client)], [], "-partial_chain"),
"accept last-resort direct leaf match");
-ok(!verify("ee-cert", "ssl_server", [qw(ee-client)], [], "-partial_chain"),
+ok(!verify("ee-cert", "sslserver", [qw(ee-client)], [], "-partial_chain"),
"fail last-resort direct leaf non-match");
-ok(verify("ee-cert", "ssl_server", [qw(ee+serverAuth)], [], "-partial_chain"),
+ok(verify("ee-cert", "sslserver", [qw(ee+serverAuth)], [], "-partial_chain"),
"accept direct match with trusted EKU");
-ok(!verify("ee-cert", "ssl_server", [qw(ee-serverAuth)], [], "-partial_chain"),
+ok(!verify("ee-cert", "sslserver", [qw(ee-serverAuth)], [], "-partial_chain"),
"reject direct match with rejected EKU");
-ok(verify("ee-client", "ssl_client", [qw(ee+clientAuth)], [], "-partial_chain"),
+ok(verify("ee-client", "sslclient", [qw(ee+clientAuth)], [], "-partial_chain"),
"accept direct match with trusted EKU");
-ok(!verify("ee-client", "ssl_client", [qw(ee-clientAuth)], [], "-partial_chain"),
+ok(!verify("ee-client", "sslclient", [qw(ee-clientAuth)], [], "-partial_chain"),
"reject direct match with rejected EKU");