From 0daccd4dc1f1ac62181738a91714f35472e50f3c Mon Sep 17 00:00:00 2001 From: Viktor Dukhovni Date: Thu, 28 Jan 2016 03:01:45 -0500 Subject: [PATCH] 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 --- apps/opt.c | 13 ++- crypto/x509/x509_trs.c | 19 ++-- crypto/x509/x509_vfy.c | 118 +++++++++++++++------ crypto/x509/x509_vpm.c | 6 +- doc/apps/verify.pod | 26 +++-- doc/apps/x509.pod | 9 +- doc/crypto/X509_VERIFY_PARAM_set_flags.pod | 17 ++- include/openssl/x509.h | 2 +- test/certs/root+anyEKU.pem | 18 ++++ test/certs/root-anyEKU.pem | 18 ++++ test/certs/root2+clientAuth.pem | 19 ++++ test/certs/root2+serverAuth.pem | 19 ++++ test/certs/root2-serverAuth.pem | 19 ++++ test/certs/setup.sh | 12 ++- test/recipes/25-test_verify.t | 98 +++++++++++------ 15 files changed, 315 insertions(+), 98 deletions(-) create mode 100644 test/certs/root+anyEKU.pem create mode 100644 test/certs/root-anyEKU.pem create mode 100644 test/certs/root2+clientAuth.pem create mode 100644 test/certs/root2+serverAuth.pem create mode 100644 test/certs/root2-serverAuth.pem 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 @@ -362,6 +362,48 @@ static STACK_OF(X509) *lookup_certs_sk(X509_STORE_CTX *ctx, X509_NAME *nm) return sk; } +/* + * 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 @@ -369,11 +411,12 @@ static STACK_OF(X509) *lookup_certs_sk(X509_STORE_CTX *ctx, X509_NAME *nm) 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. +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 command-line +utility. Supported policy names include: B, B, B, B, B. -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 (SSL client use), B -(SSL server use) and B (S/MIME email) are used. +adds a trusted certificate use. +Any object name can be used here but currently only B (SSL client +use), B (SSL server use), B (S/MIME email) and +B 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 be prepared to handle such cases without assuming they are hard errors. +If B is set, when constructing the certificate chain, +L 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 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 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 always set, this option +has no effect. The B 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"); -- 2.34.1