Commit 604ba265 authored by Matt Caswell's avatar Matt Caswell
Browse files

Fix SSL_check_chain()



The function SSL_check_chain() can be used by applications to check that
a cert and chain is compatible with the negotiated parameters. This could
be useful (for example) from the certificate callback. Unfortunately this
function was applying TLSv1.2 sig algs rules and did not work correctly if
TLSv1.3 was negotiated.

We refactor tls_choose_sigalg to split it up and create a new function
find_sig_alg which can (optionally) take a certificate and key as
parameters and find an appropriate sig alg if one exists. If the cert and
key are not supplied then we try to find a cert and key from the ones we
have available that matches the shared sig algs.

Reviewed-by: default avatarTomas Mraz <tmraz@fedoraproject.org>
(Merged from https://github.com/openssl/openssl/pull/9443)
parent cd5e2b0a
Loading
Loading
Loading
Loading
+140 −63
Original line number Original line Diff line number Diff line
@@ -21,6 +21,8 @@
#include "ssl_locl.h"
#include "ssl_locl.h"
#include <openssl/ct.h>
#include <openssl/ct.h>


static const SIGALG_LOOKUP *find_sig_alg(SSL *s, X509 *x, EVP_PKEY *pkey);

SSL3_ENC_METHOD const TLSv1_enc_data = {
SSL3_ENC_METHOD const TLSv1_enc_data = {
    tls1_enc,
    tls1_enc,
    tls1_mac,
    tls1_mac,
@@ -2072,16 +2074,34 @@ int tls1_set_sigalgs(CERT *c, const int *psig_nids, size_t salglen, int client)


static int tls1_check_sig_alg(SSL *s, X509 *x, int default_nid)
static int tls1_check_sig_alg(SSL *s, X509 *x, int default_nid)
{
{
    int sig_nid;
    int sig_nid, use_pc_sigalgs = 0;
    size_t i;
    size_t i;
    const SIGALG_LOOKUP *sigalg;
    size_t sigalgslen;
    if (default_nid == -1)
    if (default_nid == -1)
        return 1;
        return 1;
    sig_nid = X509_get_signature_nid(x);
    sig_nid = X509_get_signature_nid(x);
    if (default_nid)
    if (default_nid)
        return sig_nid == default_nid ? 1 : 0;
        return sig_nid == default_nid ? 1 : 0;
    for (i = 0; i < s->shared_sigalgslen; i++)

        if (sig_nid == s->shared_sigalgs[i]->sigandhash)
    if (SSL_IS_TLS13(s) && s->s3->tmp.peer_cert_sigalgs != NULL) {
        /*
         * If we're in TLSv1.3 then we only get here if we're checking the
         * chain. If the peer has specified peer_cert_sigalgs then we use them
         * otherwise we default to normal sigalgs.
         */
        sigalgslen = s->s3->tmp.peer_cert_sigalgslen;
        use_pc_sigalgs = 1;
    } else {
        sigalgslen = s->shared_sigalgslen;
    }
    for (i = 0; i < sigalgslen; i++) {
        sigalg = use_pc_sigalgs
                 ? tls1_lookup_sigalg(s->s3->tmp.peer_cert_sigalgs[i])
                 : s->shared_sigalgs[i];
        if (sig_nid == sigalg->sigandhash)
            return 1;
            return 1;
    }
    return 0;
    return 0;
}
}


@@ -2238,7 +2258,14 @@ int tls1_check_chain(SSL *s, X509 *x, EVP_PKEY *pk, STACK_OF(X509) *chain,
            }
            }
        }
        }
        /* Check signature algorithm of each cert in chain */
        /* Check signature algorithm of each cert in chain */
        if (!tls1_check_sig_alg(s, x, default_nid)) {
        if (SSL_IS_TLS13(s)) {
            /*
             * We only get here if the application has called SSL_check_chain(),
             * so check_flags is always set.
             */
            if (find_sig_alg(s, x, pk) != NULL)
                rv |= CERT_PKEY_EE_SIGNATURE;
        } else if (!tls1_check_sig_alg(s, x, default_nid)) {
            if (!check_flags)
            if (!check_flags)
                goto end;
                goto end;
        } else
        } else
@@ -2526,43 +2553,36 @@ static int tls12_get_cert_sigalg_idx(const SSL *s, const SIGALG_LOOKUP *lu)
}
}


/*
/*
 * Returns true if |s| has a usable certificate configured for use
 * Checks the given cert against signature_algorithm_cert restrictions sent by
 * with signature scheme |sig|.
 * the peer (if any) as well as whether the hash from the sigalg is usable with
 * "Usable" includes a check for presence as well as applying
 * the key.
 * the signature_algorithm_cert restrictions sent by the peer (if any).
 * Returns true if the cert is usable and false otherwise.
 * Returns false if no usable certificate is found.
 */
 */
static int has_usable_cert(SSL *s, const SIGALG_LOOKUP *sig, int idx)
static int check_cert_usable(SSL *s, const SIGALG_LOOKUP *sig, X509 *x,
                             EVP_PKEY *pkey)
{
{
    const SIGALG_LOOKUP *lu;
    const SIGALG_LOOKUP *lu;
    int mdnid, pknid, default_mdnid;
    int mdnid, pknid, default_mdnid;
    int mandatory_md = 0;
    int mandatory_md = 0;
    size_t i;
    size_t i;


    /* TLS 1.2 callers can override lu->sig_idx, but not TLS 1.3 callers. */
    if (idx == -1)
        idx = sig->sig_idx;
    if (!ssl_has_cert(s, idx))
        return 0;
    /* If the EVP_PKEY reports a mandatory digest, allow nothing else. */
    /* If the EVP_PKEY reports a mandatory digest, allow nothing else. */
    ERR_set_mark();
    ERR_set_mark();
    switch (EVP_PKEY_get_default_digest_nid(s->cert->pkeys[idx].privatekey,
    switch (EVP_PKEY_get_default_digest_nid(pkey, &default_mdnid)) {
                                            &default_mdnid)) {
    case 2:
    case 2:
        mandatory_md = 1;
        mandatory_md = 1;
        break;
        break;
    case 1:
    case 1:
        break;
    default: /* If it didn't report a mandatory NID, for whatever reasons,
    default: /* If it didn't report a mandatory NID, for whatever reasons,
              * just clear the error and allow all hashes to be used. */
              * just clear the error and allow all hashes to be used. */
        ERR_pop_to_mark();
        break;
    }
    }
    ERR_pop_to_mark();
    if (s->s3->tmp.peer_cert_sigalgs != NULL) {
    if (s->s3->tmp.peer_cert_sigalgs != NULL) {
        for (i = 0; i < s->s3->tmp.peer_cert_sigalgslen; i++) {
        for (i = 0; i < s->s3->tmp.peer_cert_sigalgslen; i++) {
            lu = tls1_lookup_sigalg(s->s3->tmp.peer_cert_sigalgs[i]);
            lu = tls1_lookup_sigalg(s->s3->tmp.peer_cert_sigalgs[i]);
            if (lu == NULL
            if (lu == NULL
                || !X509_get_signature_info(s->cert->pkeys[idx].x509, &mdnid,
                || !X509_get_signature_info(x, &mdnid, &pknid, NULL, NULL)
                                            &pknid, NULL, NULL)
                || (mandatory_md && mdnid != default_mdnid))
                || (mandatory_md && mdnid != default_mdnid))
                continue;
                continue;
            /*
            /*
@@ -2580,34 +2600,60 @@ static int has_usable_cert(SSL *s, const SIGALG_LOOKUP *sig, int idx)
}
}


/*
/*
 * Choose an appropriate signature algorithm based on available certificates
 * Returns true if |s| has a usable certificate configured for use
 * Sets chosen certificate and signature algorithm.
 * with signature scheme |sig|.
 *
 * "Usable" includes a check for presence as well as applying
 * For servers if we fail to find a required certificate it is a fatal error,
 * the signature_algorithm_cert restrictions sent by the peer (if any).
 * an appropriate error code is set and a TLS alert is sent.
 * Returns false if no usable certificate is found.
 *
 * For clients fatalerrs is set to 0. If a certificate is not suitable it is not
 * a fatal error: we will either try another certificate or not present one
 * to the server. In this case no error is set.
 */
 */
int tls_choose_sigalg(SSL *s, int fatalerrs)
static int has_usable_cert(SSL *s, const SIGALG_LOOKUP *sig, int idx)
{
{
    const SIGALG_LOOKUP *lu = NULL;
    /* TLS 1.2 callers can override sig->sig_idx, but not TLS 1.3 callers. */
    int sig_idx = -1;
    if (idx == -1)
        idx = sig->sig_idx;
    if (!ssl_has_cert(s, idx))
        return 0;


    s->s3->tmp.cert = NULL;
    return check_cert_usable(s, sig, s->cert->pkeys[idx].x509,
    s->s3->tmp.sigalg = NULL;
                             s->cert->pkeys[idx].privatekey);
}


    if (SSL_IS_TLS13(s)) {
/*
 * Returns true if the supplied cert |x| and key |pkey| is usable with the
 * specified signature scheme |sig|, or false otherwise.
 */
static int is_cert_usable(SSL *s, const SIGALG_LOOKUP *sig, X509 *x,
                          EVP_PKEY *pkey)
{
    size_t idx;

    if (ssl_cert_lookup_by_pkey(pkey, &idx) == NULL)
        return 0;

    /* Check the key is consistent with the sig alg */
    if ((int)idx != sig->sig_idx)
        return 0;

    return check_cert_usable(s, sig, x, pkey);
}

/*
 * Find a signature scheme that works with the supplied certificate |x| and key
 * |pkey|. |x| and |pkey| may be NULL in which case we additionally look at our
 * available certs/keys to find one that works.
 */
static const SIGALG_LOOKUP *find_sig_alg(SSL *s, X509 *x, EVP_PKEY *pkey)
{
    const SIGALG_LOOKUP *lu = NULL;
    size_t i;
    size_t i;
#ifndef OPENSSL_NO_EC
#ifndef OPENSSL_NO_EC
    int curve = -1;
    int curve = -1;
#endif
#endif
    EVP_PKEY *tmppkey;


        /* Look for a certificate matching shared sigalgs */
    /* Look for a shared sigalgs matching possible certificates */
    for (i = 0; i < s->shared_sigalgslen; i++) {
    for (i = 0; i < s->shared_sigalgslen; i++) {
        lu = s->shared_sigalgs[i];
        lu = s->shared_sigalgs[i];
            sig_idx = -1;


        /* Skip SHA1, SHA224, DSA and RSA if not PSS */
        /* Skip SHA1, SHA224, DSA and RSA if not PSS */
        if (lu->hash == NID_sha1
        if (lu->hash == NID_sha1
@@ -2616,13 +2662,19 @@ int tls_choose_sigalg(SSL *s, int fatalerrs)
            || lu->sig == EVP_PKEY_RSA)
            || lu->sig == EVP_PKEY_RSA)
            continue;
            continue;
        /* Check that we have a cert, and signature_algorithms_cert */
        /* Check that we have a cert, and signature_algorithms_cert */
            if (!tls1_lookup_md(lu, NULL) || !has_usable_cert(s, lu, -1))
        if (!tls1_lookup_md(lu, NULL))
            continue;
        if ((pkey == NULL && !has_usable_cert(s, lu, -1))
                || (pkey != NULL && !is_cert_usable(s, lu, x, pkey)))
            continue;
            continue;

        tmppkey = (pkey != NULL) ? pkey
                                 : s->cert->pkeys[lu->sig_idx].privatekey;

        if (lu->sig == EVP_PKEY_EC) {
        if (lu->sig == EVP_PKEY_EC) {
#ifndef OPENSSL_NO_EC
#ifndef OPENSSL_NO_EC
            if (curve == -1) {
            if (curve == -1) {
                    EC_KEY *ec = EVP_PKEY_get0_EC_KEY(s->cert->pkeys[SSL_PKEY_ECC].privatekey);
                EC_KEY *ec = EVP_PKEY_get0_EC_KEY(tmppkey);

                curve = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec));
                curve = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec));
            }
            }
            if (lu->curve != NID_undef && curve != lu->curve)
            if (lu->curve != NID_undef && curve != lu->curve)
@@ -2632,15 +2684,40 @@ int tls_choose_sigalg(SSL *s, int fatalerrs)
#endif
#endif
        } else if (lu->sig == EVP_PKEY_RSA_PSS) {
        } else if (lu->sig == EVP_PKEY_RSA_PSS) {
            /* validate that key is large enough for the signature algorithm */
            /* validate that key is large enough for the signature algorithm */
                EVP_PKEY *pkey;
            if (!rsa_pss_check_min_key_size(EVP_PKEY_get0(tmppkey), lu))

                pkey = s->cert->pkeys[lu->sig_idx].privatekey;
                if (!rsa_pss_check_min_key_size(EVP_PKEY_get0(pkey), lu))
                continue;
                continue;
        }
        }
        break;
        break;
    }
    }
        if (i == s->shared_sigalgslen) {

    if (i == s->shared_sigalgslen)
        return NULL;

    return lu;
}

/*
 * Choose an appropriate signature algorithm based on available certificates
 * Sets chosen certificate and signature algorithm.
 *
 * For servers if we fail to find a required certificate it is a fatal error,
 * an appropriate error code is set and a TLS alert is sent.
 *
 * For clients fatalerrs is set to 0. If a certificate is not suitable it is not
 * a fatal error: we will either try another certificate or not present one
 * to the server. In this case no error is set.
 */
int tls_choose_sigalg(SSL *s, int fatalerrs)
{
    const SIGALG_LOOKUP *lu = NULL;
    int sig_idx = -1;

    s->s3->tmp.cert = NULL;
    s->s3->tmp.sigalg = NULL;

    if (SSL_IS_TLS13(s)) {
        lu = find_sig_alg(s, NULL, NULL);
        if (lu == NULL) {
            if (!fatalerrs)
            if (!fatalerrs)
                return 1;
                return 1;
            SSLfatal(s, SSL_AD_HANDSHAKE_FAILURE, SSL_F_TLS_CHOOSE_SIGALG,
            SSLfatal(s, SSL_AD_HANDSHAKE_FAILURE, SSL_F_TLS_CHOOSE_SIGALG,