hx509: check Name RDN attribute size bounds

This commit is contained in:
Nicolas Williams
2019-10-03 17:26:54 -05:00
parent cb2db14ed1
commit 038ed5ec31
2 changed files with 130 additions and 40 deletions

View File

@@ -582,4 +582,44 @@ id-ms-client-authentication OBJECT IDENTIFIER ::=
-- DER:1e:20:00:44:00:6f:00:6d:00:61:00:69:00:6e:00:43:00:6f:00:6e:00:74:00:72:00:6f:00:6c:00:6c:00:65:00:72
-- Upper bounds:
ub-name INTEGER ::= 32768
ub-common-name INTEGER ::= 64
ub-locality-name INTEGER ::= 128
ub-state-name INTEGER ::= 128
ub-organization-name INTEGER ::= 64
ub-organizational-unit-name INTEGER ::= 64
ub-title INTEGER ::= 64
ub-serial-number INTEGER ::= 64
ub-match INTEGER ::= 128
ub-emailaddress-length INTEGER ::= 255
ub-common-name-length INTEGER ::= 64
ub-country-name-alpha-length INTEGER ::= 2
ub-country-name-numeric-length INTEGER ::= 3
ub-domain-defined-attributes INTEGER ::= 4
ub-domain-defined-attribute-type-length INTEGER ::= 8
ub-domain-defined-attribute-value-length INTEGER ::= 128
ub-domain-name-length INTEGER ::= 16
ub-extension-attributes INTEGER ::= 256
ub-e163-4-number-length INTEGER ::= 15
ub-e163-4-sub-address-length INTEGER ::= 40
ub-generation-qualifier-length INTEGER ::= 3
ub-given-name-length INTEGER ::= 16
ub-initials-length INTEGER ::= 5
ub-integer-options INTEGER ::= 256
ub-numeric-user-id-length INTEGER ::= 32
ub-organization-name-length INTEGER ::= 64
ub-organizational-unit-name-length INTEGER ::= 32
ub-organizational-units INTEGER ::= 4
ub-pds-name-length INTEGER ::= 16
ub-pds-parameter-length INTEGER ::= 30
ub-pds-physical-address-lines INTEGER ::= 6
ub-postal-code-length INTEGER ::= 16
ub-pseudonym INTEGER ::= 128
ub-surname-length INTEGER ::= 40
ub-terminal-id-length INTEGER ::= 24
ub-unformatted-address-length INTEGER ::= 180
ub-x121-address-length INTEGER ::= 16
END

View File

@@ -65,18 +65,27 @@ static const struct {
const char *n;
const heim_oid *o;
wind_profile_flags flags;
/*
* RFC52380 imposes maximum lengths for some strings in Names. These are
* ASN.1 size limits. We should implement these in our copy of the PKIX
* ASN.1 module. For now we treat them as maximum byte counts rather than
* maximum character counts, and we encode and enforce them here.
*
* 0 -> no max
*/
size_t max_bytes;
} no[] = {
{ "C", &asn1_oid_id_at_countryName, 0 },
{ "CN", &asn1_oid_id_at_commonName, 0 },
{ "DC", &asn1_oid_id_domainComponent, 0 },
{ "L", &asn1_oid_id_at_localityName, 0 },
{ "O", &asn1_oid_id_at_organizationName, 0 },
{ "OU", &asn1_oid_id_at_organizationalUnitName, 0 },
{ "S", &asn1_oid_id_at_stateOrProvinceName, 0 },
{ "STREET", &asn1_oid_id_at_streetAddress, 0 },
{ "UID", &asn1_oid_id_Userid, 0 },
{ "emailAddress", &asn1_oid_id_pkcs9_emailAddress, 0 },
{ "serialNumber", &asn1_oid_id_at_serialNumber, 0 }
{ "C", &asn1_oid_id_at_countryName, 0, 2 },
{ "CN", &asn1_oid_id_at_commonName, 0, ub_common_name },
{ "DC", &asn1_oid_id_domainComponent, 0, 63 }, /* DNS label */
{ "L", &asn1_oid_id_at_localityName, 0, ub_locality_name },
{ "O", &asn1_oid_id_at_organizationName, 0, ub_organization_name },
{ "OU", &asn1_oid_id_at_organizationalUnitName, 0, ub_organizational_unit_name },
{ "S", &asn1_oid_id_at_stateOrProvinceName, 0, ub_state_name },
{ "STREET", &asn1_oid_id_at_streetAddress, 0, 0 }, /* ENOTSUP */
{ "UID", &asn1_oid_id_Userid, 0, ub_numeric_user_id_length },
{ "emailAddress", &asn1_oid_id_pkcs9_emailAddress, 0, ub_emailaddress_length },
{ "serialNumber", &asn1_oid_id_at_serialNumber, 0, ub_serial_number }
};
static char *
@@ -156,6 +165,18 @@ oidtostring(const heim_oid *type)
return s;
}
static size_t
oidtomaxlen(const heim_oid *type)
{
size_t i;
for (i = 0; i < sizeof(no)/sizeof(no[0]); i++) {
if (der_heim_oid_cmp(no[i].o, type) == 0)
return no[i].max_bytes;
}
return 0;
}
static int
stringtooid(const char *name, size_t len, heim_oid *oid)
{
@@ -531,42 +552,56 @@ _hx509_name_modify(hx509_context context,
const heim_oid *oid,
const char *str)
{
RelativeDistinguishedName *rdn;
RelativeDistinguishedName rdn;
size_t max_len = oidtomaxlen(oid);
int ret;
void *ptr;
ptr = realloc(name->u.rdnSequence.val,
sizeof(name->u.rdnSequence.val[0]) *
(name->u.rdnSequence.len + 1));
if (ptr == NULL) {
/*
* Check string length upper bounds.
*
* Because we don't have these bounds in our copy of the PKIX ASN.1 module,
* and because we might like to catch these early anyways, we enforce them
* here.
*/
if (max_len && strlen(str) > max_len) {
const char *a = oidtostring(oid);
ret = HX509_PARSING_NAME_FAILED;
hx509_set_error_string(context, 0, ret, "RDN attribute %s value too "
"long (max %llu): %s", a ? a : "<unknown>",
max_len, str);
return ret;
}
memset(&rdn, 0, sizeof(rdn));
if ((rdn.val = malloc(sizeof(rdn.val[0]))) == NULL) {
hx509_set_error_string(context, 0, ENOMEM, "Out of memory");
return ENOMEM;
}
name->u.rdnSequence.val = ptr;
if (append) {
rdn = &name->u.rdnSequence.val[name->u.rdnSequence.len];
} else {
memmove(&name->u.rdnSequence.val[1],
&name->u.rdnSequence.val[0],
name->u.rdnSequence.len *
sizeof(name->u.rdnSequence.val[0]));
rdn = &name->u.rdnSequence.val[0];
rdn.len = 1;
rdn.val[0].value.element = choice_DirectoryString_utf8String;
if ((rdn.val[0].value.u.utf8String = strdup(str)) == NULL ||
(ret = der_copy_oid(oid, &rdn.val[0].type))) {
hx509_set_error_string(context, 0, ENOMEM, "Out of memory");
free(rdn.val[0].value.u.utf8String);
free(rdn.val);
return ENOMEM;
}
rdn->val = malloc(sizeof(rdn->val[0]));
if (rdn->val == NULL)
return ENOMEM;
rdn->len = 1;
ret = der_copy_oid(oid, &rdn->val[0].type);
if (ret)
return ret;
rdn->val[0].value.element = choice_DirectoryString_utf8String;
rdn->val[0].value.u.utf8String = strdup(str);
if (rdn->val[0].value.u.utf8String == NULL)
return ENOMEM;
name->u.rdnSequence.len += 1;
/* Append RDN. If the caller wanted to prepend instead, we'll rotate. */
ret = add_RDNSequence(&name->u.rdnSequence, &rdn);
free_RelativeDistinguishedName(&rdn);
if (ret || append || name->u.rdnSequence.len < 2)
return ret;
/* Rotate */
rdn = name->u.rdnSequence.val[name->u.rdnSequence.len - 1];
memmove(&name->u.rdnSequence.val[1],
&name->u.rdnSequence.val[0],
(name->u.rdnSequence.len - 1) *
sizeof(name->u.rdnSequence.val[0]));
name->u.rdnSequence.val[0] = rdn;
return 0;
}
@@ -746,6 +781,7 @@ hx509_name_expand(hx509_context context,
{
Name *n = &name->der_name;
size_t i, j;
int bounds_check = 1;
if (env == NULL)
return 0;
@@ -768,6 +804,7 @@ hx509_name_expand(hx509_context context,
free normalized utf8 string
*/
DirectoryString *ds = &n->u.rdnSequence.val[i].val[j].value;
heim_oid *type = &n->u.rdnSequence.val[i].val[j].type;
char *p, *p2;
struct rk_strpool *strpool = NULL;
@@ -822,15 +859,28 @@ hx509_name_expand(hx509_context context,
}
}
if (strpool) {
size_t max_bytes;
free(ds->u.utf8String);
ds->u.utf8String = rk_strpoolcollect(strpool);
if (ds->u.utf8String == NULL) {
hx509_set_error_string(context, 0, ENOMEM, "out of memory");
return ENOMEM;
}
/* Check upper bounds! */
if ((max_bytes = oidtomaxlen(type)) &&
strlen(ds->u.utf8String) > max_bytes)
bounds_check = 0;
}
}
}
if (!bounds_check) {
hx509_set_error_string(context, 0, HX509_PARSING_NAME_FAILED,
"some expanded RDNs are too long");
return HX509_PARSING_NAME_FAILED;
}
return 0;
}