This commit is contained in:
2018-05-24 00:50:03 +09:00
parent 568c1175f2
commit ee7a19ebef
19 changed files with 418 additions and 219 deletions
+113 -21
View File
@@ -297,48 +297,140 @@ int NAME_CONSTRAINTS_check(X509 *x, NAME_CONSTRAINTS *nc)
}
static int cn2dnsid(ASN1_STRING *cn, unsigned char **dnsid, size_t *idlen)
{
int utf8_length;
unsigned char *utf8_value;
int i;
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)) {
OPENSSL_free(utf8_value);
return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
}
/*
* 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.
*/
int NAME_CONSTRAINTS_check_CN(X509 *x, NAME_CONSTRAINTS *nc)
{
int r, i;
X509_NAME *nm;
X509_NAME *nm = X509_get_subject_name(x);
ASN1_STRING stmp;
GENERAL_NAME gntmp;
stmp.flags = 0;
stmp.type = V_ASN1_IA5STRING;
gntmp.type = GEN_DNS;
gntmp.d.dNSName = &stmp;
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);
cn = 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;
if ((r = cn2dnsid(cn, &idval, &idlen)) != X509_V_OK)
return r;
if (idlen == 0)
continue;
stmp.length = hlen;
stmp.data = h;
r = nc_match(&gntmp, nc);
OPENSSL_free(h);
if (r != X509_V_OK)
return r;
}
stmp.length = idlen;
stmp.data = idval;
r = nc_match(&gntmp, nc);
OPENSSL_free(idval);
if (r != X509_V_OK)
return r;
}
return X509_V_OK;
}