Commit d02d80b2 authored by Viktor Dukhovni's avatar Viktor Dukhovni
Browse files

Limit scope of CN name constraints



Don't apply DNS name constraints to the subject CN when there's a
least one DNS-ID subjectAlternativeName.

Don't apply DNS name constraints to subject CN's that are sufficiently
unlike DNS names.  Checked name must have at least two labels, with
all labels non-empty, no trailing '.' and all hyphens must be
internal in each label.  In addition to the usual LDH characters,
we also allow "_", since some sites use these for hostnames despite
all the standards.

Reviewed-by: default avatarMatt Caswell <matt@openssl.org>
Reviewed-by: default avatarTim Hudson <tjh@openssl.org>
parent de9f5b35
Loading
Loading
Loading
Loading
+0 −50
Original line number Diff line number Diff line
@@ -624,53 +624,3 @@ int ASN1_STRING_to_UTF8(unsigned char **out, const ASN1_STRING *in)
    *out = stmp.data;
    return stmp.length;
}

/* Return 1 if host is a valid hostname and 0 otherwise */
int asn1_valid_host(const ASN1_STRING *host)
{
    int hostlen = host->length;
    const unsigned char *hostptr = host->data;
    int type = host->type;
    int i;
    signed char width = -1;
    unsigned short chflags = 0, prevchflags;

    if (type > 0 && type < 31)
        width = tag2nbyte[type];
    if (width == -1 || hostlen == 0)
        return 0;
    /* Treat UTF8String as width 1 as any MSB set is invalid */
    if (width == 0)
        width = 1;
    for (i = 0 ; i < hostlen; i+= width) {
        prevchflags = chflags;
        /* Value must be <= 0x7F: check upper bytes are all zeroes */
        if (width == 4) {
            if (*hostptr++ != 0 || *hostptr++ != 0 || *hostptr++ != 0)
                return 0;
        } else if (width == 2) {
            if (*hostptr++ != 0)
                return 0;
        }
        if (*hostptr > 0x7f)
            return 0;
        chflags = char_type[*hostptr++];
        if (!(chflags & (CHARTYPE_HOST_ANY | CHARTYPE_HOST_WILD))) {
            /* Nothing else allowed at start or end of string */
            if (i == 0 || i == hostlen - 1)
                return 0;
            /* Otherwise invalid if not dot or hyphen */
            if (!(chflags & (CHARTYPE_HOST_DOT | CHARTYPE_HOST_HYPHEN)))
                return 0;
            /*
             * If previous is dot or hyphen then illegal unless both
             * are hyphens: as .- -. .. are all illegal
             */
            if (prevchflags & (CHARTYPE_HOST_DOT | CHARTYPE_HOST_HYPHEN)
                && ((prevchflags & CHARTYPE_HOST_DOT)
                    || (chflags & CHARTYPE_HOST_DOT)))
                return 0;
        }
    }
    return 1;
}
+0 −1
Original line number Diff line number Diff line
@@ -107,5 +107,4 @@ struct asn1_pctx_st {
    unsigned long str_flags;
} /* ASN1_PCTX */ ;

int asn1_valid_host(const ASN1_STRING *host);
int asn1_d2i_read_bio(BIO *in, BUF_MEM **pb);
+125 −18
Original line number Diff line number Diff line
@@ -297,49 +297,156 @@ int NAME_CONSTRAINTS_check(X509 *x, NAME_CONSTRAINTS *nc)

}

static int cn2dnsid(ASN1_STRING *cn, unsigned char **dnsid, size_t *idlen)
{
    int utf8_length;    /* Return type of ASN1_STRING_to_UTF8 */
    int i;
    unsigned char *utf8_value;
    int isdnsname = 0;

    /* Don't leave outputs uninitialized */
    *dnsid = NULL;
    *idlen = 0;

    /*-
     * Per RFC 6125, DNS-IDs representing internationalized domain names appear
     * in certificates in A-label encoded form:
     *
     *   https://tools.ietf.org/html/rfc6125#section-6.4.2
     *
     * The same applies to CNs which are intended to represent DNS names.
     * However, while in the SAN DNS-IDs are IA5Strings, as CNs they may be
     * needlessly encoded in 16-bit Unicode.  We perform a conversion to UTF-8
     * to ensure that we get an ASCII representation of any CNs that are
     * representable as ASCII, but just not encoded as ASCII.  The UTF-8 form
     * may contain some non-ASCII octets, and that's fine, such CNs are not
     * valid legacy DNS names.
     *
     * Note, 'int' is the return type of ASN1_STRING_to_UTF8() so that's what
     * we must use for 'utf8_length'.
     */
    if ((utf8_length = ASN1_STRING_to_UTF8(&utf8_value, cn)) < 0)
        return X509_V_ERR_OUT_OF_MEM;

    /*
     * Some certificates have had names that include a *trailing* NUL byte.
     * Remove these harmless NUL characters. They would otherwise yield false
     * alarms with the following embedded NUL check.
     */
    while (utf8_length > 0 && utf8_value[utf8_length - 1] == '\0')
        --utf8_length;

    /* Reject *embedded* NULs */
    if ((size_t)utf8_length != strlen((char *)utf8_value))
        return X509_V_ERR_UNSPECIFIED;

    /*
     * XXX: Deviation from strict DNS name syntax, also check names with '_'
     * Check DNS name syntax, any '-' or '.' must be internal,
     * and on either side of each '.' we can't have a '-' or '.'.
     *
     * If the name has just one label, we don't consider it a DNS name.  This
     * means that "CN=sometld" cannot be precluded by DNS name constraints, but
     * that is not a problem.
     */
    for (i = 0; i < utf8_length; ++i) {
        unsigned char c = utf8_value[i];

        if ((c >= 'a' && c <= 'z')
            || (c >= 'A' && c <= 'Z')
            || (c >= '0' && c <= '9')
            || c == '_')
            continue;

        /* Dot and hyphen cannot be first or last. */
        if (i > 0 && i < utf8_length - 1) {
            if (c == '-')
                continue;
            /*
             * Next to a dot the preceding and following characters must not be
             * another dot or a hyphen.  Otherwise, record that the name is
             * plausible, since it has two or more labels.
             */
            if (c == '.'
                && utf8_value[i + 1] != '.'
                && utf8_value[i - 1] != '-'
                && utf8_value[i + 1] != '-') {
                isdnsname = 1;
                continue;
            }
        }
        isdnsname = 0;
        break;
    }

    if (isdnsname) {
        *dnsid = utf8_value;
        *idlen = (size_t)utf8_length;
        return X509_V_OK;
    }
    OPENSSL_free(utf8_value);
    return X509_V_OK;
}

/*
 * Check CN against DNS-ID name constraints, provided no DNS-ID
 * subjectAlternativeName values are present in the certificate.
 */
int NAME_CONSTRAINTS_check_CN(X509 *x, NAME_CONSTRAINTS *nc)
{
    int r, i;
    GENERAL_NAMES *gens = NULL;
    X509_NAME *nm;

    ASN1_STRING stmp;
    GENERAL_NAME gntmp;

    stmp.flags = 0;
    stmp.type = V_ASN1_IA5STRING;
    gntmp.type = GEN_DNS;
    gntmp.d.dNSName = &stmp;

    gens = X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL);
    if (gens != NULL) {
        for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) {
            GENERAL_NAME *gen = sk_GENERAL_NAME_value(gens, i);

            if (gen->type == GEN_DNS) {
                GENERAL_NAMES_free(gens);
                return X509_V_OK;
            }
        }
        GENERAL_NAMES_free(gens);
    }

    nm = X509_get_subject_name(x);

    /* Process any commonName attributes in subject name */

    for (i = -1;;) {
        X509_NAME_ENTRY *ne;
        ASN1_STRING *hn;
        ASN1_STRING *cn;
        unsigned char *idval;
        size_t idlen;

        i = X509_NAME_get_index_by_NID(nm, NID_commonName, i);
        if (i == -1)
            break;
        ne = X509_NAME_get_entry(nm, i);
        hn = X509_NAME_ENTRY_get_data(ne);
        /* Only process attributes that look like host names */
        if (asn1_valid_host(hn)) {
            unsigned char *h;
            int hlen = ASN1_STRING_to_UTF8(&h, hn);
            if (hlen <= 0)
                return X509_V_ERR_OUT_OF_MEM;
        cn = X509_NAME_ENTRY_get_data(ne);

            stmp.length = hlen;
            stmp.data = h;
        /* Only process attributes that look like host names */
        if ((r = cn2dnsid(cn, &idval, &idlen)) != X509_V_OK)
            return r;
        if (idlen == 0)
            continue;

        stmp.length = idlen;
        stmp.data = idval;
        r = nc_match(&gntmp, nc);

            OPENSSL_free(h);

        OPENSSL_free(idval);
        if (r != X509_V_OK)
            return r;
    }
    }
    return X509_V_OK;
}

+19 −20
Original line number Diff line number Diff line
-----BEGIN CERTIFICATE-----
MIIDlTCCAn2gAwIBAgIBAjANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDDAxUZXN0
IE5DIENBIDEwIBcNMTYwNzA5MTQ0ODExWhgPMjExNjA3MTAxNDQ4MTFaMGgxIzAh
BgNVBAoMGkdvb2QgTkMgVGVzdCBDZXJ0aWZpY2F0ZSAxMRUwEwYDVQQDDAx3d3cu
Z29vZC5vcmcxEzARBgNVBAMMCkpvZSBCbG9nZ3MxFTATBgNVBAMMDGFueS5nb29k
LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALAv1X8S8uUpnjTa
3bv7m1jJbbX7bC9w7k4TfxiU5XL/m3EhN//EUBJSoamy6vFC6oy/6jA8XmptlVrY
Sp3ZKFdjdZh+CyYZKcrv4JReF2lfRIINn6d6EgcAobGTNwdcv67xuNtMi0meAvmK
gLjOa/IhCHNC+l8vNDJx/a+7mxH+yNxPL6lC/kJMja6oaYndx74WJpPC22LJ/cCp
xspKKsoPYYjk0BX9RvbKO8s4b86Wjzzntht+NpQ4LLh9XwPZog11qGE4UIrsV8XA
YxJrMGQNZd69cnCOz8vnOVCszFOa4qVvXeAGr0iFlZAXbQJevpiiXaXHMEt8C1qH
xpcW8DcCAwEAAaOBmDCBlTAdBgNVHQ4EFgQUw8nB25NP0gUaFCrOwAO5KzllnREw
HwYDVR0jBBgwFoAUCNGb+ebVZHCg8Wsanu1S2t31UEMwCQYDVR0TBAIwADBIBgNV
HREEQTA/ggx3d3cuZ29vZC5vcmeCDGFueS5nb29kLmNvbYENZ29vZEBnb29kLm9y
Z4EMYW55QGdvb2QuY29thwTAqAABMA0GCSqGSIb3DQEBCwUAA4IBAQBUnDMrg1py
8/iYXzs11Qbw7bBhc/HQDpu5QVgriaX2zDUpTLSEUV7qZFSHmwWm91ILw2VA1Xni
ua2sF19o/tJT0ZHpapkfqGpfsym2H04NDMKy0l0fSZhlCB5Kv5wpiFt9hBUrxS/2
Dd6Kg+Ka02nD5QBXSAk/xz0FmgezzGGCLjg85/Sfe9Y7tNhQXh3HuGXuJizYccdQ
Fh1IAFYW3DZoDKS7dDTCltvDEma/2IE684+CRJiA6PH9rYfJ1CCUfAMpyA85CxKT
P68GDKI++WoUgM8LDfxS0KOL7A9cqcpM2L27hjyEgnqIBPHFfm9fxztBotuCTl5L
vRlTFVjv65nn
MIIDgTCCAmmgAwIBAgIBAjANBgkqhkiG9w0BAQsFADAXMRUwEwYDVQQDDAxUZXN0
IE5DIENBIDEwIBcNMTgwNTE2MDIzODEzWhgPMjExODA1MTcwMjM4MTNaMFQxIzAh
BgNVBAoMGkdvb2QgTkMgVGVzdCBDZXJ0aWZpY2F0ZSAxMRgwFgYDVQQDDA93d3cu
ZXhhbXBsZS5uZXQxEzARBgNVBAMMCkpvZSBCbG9nZ3MwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQDTqvf6j+WxCtn4RU8/6uXXgCTcksv6NDXCZ9JAz4Vv
cQbJfhFbDWpGZQZDOCqwtj+7CSVIraxItHzPlrt36cevsoPmpuqGbHrUaOLneme2
x81SXUq0z/DmDvwxVENmRj1u7iCt3sL7awcid4SiotLOY2F1jBazmqprqKZBUiyQ
XqpSp+9uSav77ydwDXCrQozBdns1YRshgU9omQrTcIqHCj1f9Lo+A2y4+TZYZkvS
DuUZiTfPTPouR6sopM8JLyAZc+TvFFncEg24N+zz3O3jwH82BZEjzavw92J9npJB
UXvKb8O9z7UA65WYuL2he7kSQCsPNLoRWZnVpchwr3VHAgMBAAGjgZgwgZUwHQYD
VR0OBBYEFHvLhGWckFjVXdDI3ds9Wti6zgXAMB8GA1UdIwQYMBaAFAjRm/nm1WRw
oPFrGp7tUtrd9VBDMAkGA1UdEwQCMAAwSAYDVR0RBEEwP4IMd3d3Lmdvb2Qub3Jn
ggxhbnkuZ29vZC5jb22BDWdvb2RAZ29vZC5vcmeBDGFueUBnb29kLmNvbYcEwKgA
ATANBgkqhkiG9w0BAQsFAAOCAQEATVcTyrAxsehdQNrkL6kquXxWlyegJcxvVxUe
hfh9+Lw4620b2S1/l2YxFM3peLAsRgJOznmJOeG18+y7/kx/3UNqYGY7e8iJQ3Gl
JwDIJp5JCaUOlodjhMJtRc7jn9RcsL97oizXdcryyWT0vSlM9Pie9NtHG5iq5X4+
oL3X8+OG25MOkF2h3YVCEG3vDu7quyTlHc2ebwpdLZRndcOewO2Cap1ettyWXUPP
Mha6wyJE8LJhrGmrI8Lw+i7gGscP0xYZn3yCLk5BtOabn4dvCiDmb+TPruKQQARw
BG45LEZzGxz+Ad3xRdZyVi1I67v9YShoYTCpMTSxJaR0erH74g==
-----END CERTIFICATE-----
+26 −26
Original line number Diff line number Diff line
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCwL9V/EvLlKZ40
2t27+5tYyW21+2wvcO5OE38YlOVy/5txITf/xFASUqGpsurxQuqMv+owPF5qbZVa
2Eqd2ShXY3WYfgsmGSnK7+CUXhdpX0SCDZ+nehIHAKGxkzcHXL+u8bjbTItJngL5
ioC4zmvyIQhzQvpfLzQycf2vu5sR/sjcTy+pQv5CTI2uqGmJ3ce+FiaTwttiyf3A
qcbKSirKD2GI5NAV/Ub2yjvLOG/Olo8857YbfjaUOCy4fV8D2aINdahhOFCK7FfF
wGMSazBkDWXevXJwjs/L5zlQrMxTmuKlb13gBq9IhZWQF20CXr6Yol2lxzBLfAta
h8aXFvA3AgMBAAECggEAa073DcqQvhq3DSIw4wm/+DfW5nwXzF1QB6XAR0yI453j
IuhEnzcGPeKuLBmZFxDWoptRG8fpCZFs4kPSTomxFGizewlp6O5ykfPAKR2VzMwF
geCiWPL0f+dWlD1Byu4moXsASDE6tL/UuAAvnl+7R2HvL6SfsdGiTQc4qAvvyukM
szks+MePHSlXmL5Eld7HfKgpvxY1SbYOQU0aPXAQAnLaOT931q+tgZMG6nBWN+pu
w5bgKCA26BMAAaUAdIIDEa9fjzkpXjElCT4qhJYVKQn9Pb7aSc4jihSpCknqbb9c
55nW5PWMZJyCbCOUG/SVTblXV+NmhdtwrgUbHImXIQKBgQDcb/7vp+rq06uNx3b4
AjTZdzCVbHM8gp7b1GkGD0SncrzX6RxPSzNn7d4AUKY065bwa89A+TRwV8DSo7G8
hxjzdU/FKCg8ce0eqoCtWjIT2r+rV2P9dFhfRT5jdOwHrym8LeSGzANjIBNV7FOf
FIRkQ1BVD0QSPla+26ASqsw60wKBgQDMnEzChQWgAsBelALmGaj/wDdWDUXK8xRg
s7dG1Sx41SLk39SAjCUYXPyy8IHBitJtPZNDp23tR4/m8Ui1pB2T0EnlzBsuzrZ/
0aCbJnQ08FXE8iVajrgce4ZCdT8vkeH8EVhqDpJIlAhoKy3HaoAr4o2/uRoGDpHZ
iAbDLTEOjQKBgFrp4dXLhkqFNArMShetKUjLLIFj8f7xzDzT1ODH6UO6QYI2xRM6
65+gbd/pYzMOOvk7LYYZgXQX7RGyq3oaqcK3Dkg88KNFRUtRfLKCMYcYv9YVu8pr
cosQTtPMBBCDQI44yziA6aC3OOJGDpLcbmG/lWEPY762cSZUBCfOw147AoGAd8S+
AdcPtdwmcrY9BCfdDuea/JoEUon7UaehDqtVvt0z8bk7kIt4Y0x69ttleL8j8aHr
g9yLsisDhvGR2BFa5t0zhHn3J20E0skINAlMWHieHAyJ5PpJtxJvQpOTCutf1sbo
dBxXcHiGe0NbJrGmmQmiY6mcHBOHOEgxfSoE3zkCgYAc+ozIr3xmUcooUeA7uqpd
LvGGqHThGrtXVFIErOIcajC9bHEeZw4Do/oT5L7Wr7pOZ20VUmuRvwytd7IYYTVV
g+nIyKaMttEaCzHEsO0CQUHexOkJbL4rpc3HiK5hIhL8Yo2L/obQgCxYmvyChpo3
sXJAoFllBNfAK3aanFOR1Q==
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDTqvf6j+WxCtn4
RU8/6uXXgCTcksv6NDXCZ9JAz4VvcQbJfhFbDWpGZQZDOCqwtj+7CSVIraxItHzP
lrt36cevsoPmpuqGbHrUaOLneme2x81SXUq0z/DmDvwxVENmRj1u7iCt3sL7awci
d4SiotLOY2F1jBazmqprqKZBUiyQXqpSp+9uSav77ydwDXCrQozBdns1YRshgU9o
mQrTcIqHCj1f9Lo+A2y4+TZYZkvSDuUZiTfPTPouR6sopM8JLyAZc+TvFFncEg24
N+zz3O3jwH82BZEjzavw92J9npJBUXvKb8O9z7UA65WYuL2he7kSQCsPNLoRWZnV
pchwr3VHAgMBAAECggEACPTB+1sdV+lioaulF8pDoWOtq5uWf+a3o5sq/U0Kk1WP
+PSZnWWq6oGZyzxUKhf8CFjxt+qJUKY6Zbo2AnPk3B1MkXTclYV/iP9LIoo+WzCH
EoYaBB6MTd+ycg/jri8oqEnxHgo/681yhtXRyePj0ZHI7OVZjI3tyhJfvoHQmuci
u6qYYUP0GWuyM+kHS11vn6Q1U8nOZWvXpEDXDDdJ7+2QRuv01AXcjFxpbFzkMn2W
JkhKkCTIQpUU66VMRHwNexi+TR2rRESq0G+fa+6gaVFVIs0vBukq48IeC5W21j1L
zyftHxci67FlYC9iaiUxDVt3KB+lcukx6Cz5mjtzqQKBgQD/GrAtFfjiXKj9O5ld
K7dnnBHE8fzyWQWyOfwpVjNAC1J7tgwFvDpBpTHOwS5JnCwMWWM3rkBPRhCusmrF
AtfE8b643G+cJbTgDuEhGh11QR0p9VWMVFQL9kZxx12PegDtFBfzcfcI3XQwKVKL
ZbQn4ibW3BKSt9+Nh3APa0s5iwKBgQDUaTxZBajTdzoDd6Pg3warL5BhsxWr2tUQ
qf+iVoba2Y9NTBdxBht2whSaYweU9kxmeNZvnCu95B8HeRGE69Dxb7IWwpsaxoaf
ND0NcCF7aPZgx7hvhbHF7duzt3nuv+q5sOuuyHPzm+nF2snAuY3Zg+Bpv3nlYekf
18aXZdwStQKBgEpF8e9ei1UUl1sLZC6dUMvIw9+sePHye1cVzNYYM9m8sio0qbFt
ySRdvW+uDRT/dE+wItQOVsj95FOIvM9ZcYr0u4vFGnXDALOPgXqKyPLfn2cc9+hg
kQvei0oLOrFQWz6rcAHAN6WMHIz9KvxNAzPtg1NhRcMT5/Gj8jt7CK7bAoGAIeKz
7OO5Phr8F0eDzkDmGHMbDmr6XxMnAGSOUoCJPOqOMN+dsbsusHBfxw1bTUlJgONw
GhgI5l85EAEhaVoRWCLgfz8GbWwUV9uGjdlAjiZ9f4z9AFWMua2rae0wN4VIVd1C
i/yQeuF5lsXDf8paNcQTDeus74oCHcFXfhmS1S0CgYB2q8E+H0kFHbUxkIZYwhsM
r0lTecn+kVsyPPje2UlzfTwvcC9dFIC4ppCdJGUJAwi/PJnr6xNyOH6I1pjUA8ER
Aofm4Oj2DwX8W+81oO71/RXSfEFUjdOw0H6iRDyvWa1gqftj2/aWjV7Ifdo49thx
EzX/9GdsRInifN6FfOfo/A==
-----END PRIVATE KEY-----
Loading