Commit 0daccd4d authored by Viktor Dukhovni's avatar Viktor Dukhovni
Browse files

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: default avatarDr. Stephen Henson <steve@openssl.org>
parent 1b4cf96f
Loading
Loading
Loading
Loading
+12 −1
Original line number Diff line number Diff line
@@ -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());
+11 −8
Original line number Diff line number Diff line
@@ -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;
        }
        /*
+85 −33
Original line number Diff line number Diff line
@@ -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->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 (! ctx->verify_cb(0, ctx))
                return 0;
        }
        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;
+3 −3
Original line number Diff line number Diff line
@@ -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 */
+16 −10
Original line number Diff line number Diff line
@@ -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>

Loading