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 -- 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 END

View File

@@ -65,18 +65,27 @@ static const struct {
const char *n; const char *n;
const heim_oid *o; const heim_oid *o;
wind_profile_flags flags; 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[] = { } no[] = {
{ "C", &asn1_oid_id_at_countryName, 0 }, { "C", &asn1_oid_id_at_countryName, 0, 2 },
{ "CN", &asn1_oid_id_at_commonName, 0 }, { "CN", &asn1_oid_id_at_commonName, 0, ub_common_name },
{ "DC", &asn1_oid_id_domainComponent, 0 }, { "DC", &asn1_oid_id_domainComponent, 0, 63 }, /* DNS label */
{ "L", &asn1_oid_id_at_localityName, 0 }, { "L", &asn1_oid_id_at_localityName, 0, ub_locality_name },
{ "O", &asn1_oid_id_at_organizationName, 0 }, { "O", &asn1_oid_id_at_organizationName, 0, ub_organization_name },
{ "OU", &asn1_oid_id_at_organizationalUnitName, 0 }, { "OU", &asn1_oid_id_at_organizationalUnitName, 0, ub_organizational_unit_name },
{ "S", &asn1_oid_id_at_stateOrProvinceName, 0 }, { "S", &asn1_oid_id_at_stateOrProvinceName, 0, ub_state_name },
{ "STREET", &asn1_oid_id_at_streetAddress, 0 }, { "STREET", &asn1_oid_id_at_streetAddress, 0, 0 }, /* ENOTSUP */
{ "UID", &asn1_oid_id_Userid, 0 }, { "UID", &asn1_oid_id_Userid, 0, ub_numeric_user_id_length },
{ "emailAddress", &asn1_oid_id_pkcs9_emailAddress, 0 }, { "emailAddress", &asn1_oid_id_pkcs9_emailAddress, 0, ub_emailaddress_length },
{ "serialNumber", &asn1_oid_id_at_serialNumber, 0 } { "serialNumber", &asn1_oid_id_at_serialNumber, 0, ub_serial_number }
}; };
static char * static char *
@@ -156,6 +165,18 @@ oidtostring(const heim_oid *type)
return s; 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 static int
stringtooid(const char *name, size_t len, heim_oid *oid) 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 heim_oid *oid,
const char *str) const char *str)
{ {
RelativeDistinguishedName *rdn; RelativeDistinguishedName rdn;
size_t max_len = oidtomaxlen(oid);
int ret; int ret;
void *ptr;
ptr = realloc(name->u.rdnSequence.val, /*
sizeof(name->u.rdnSequence.val[0]) * * Check string length upper bounds.
(name->u.rdnSequence.len + 1)); *
if (ptr == NULL) { * 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"); hx509_set_error_string(context, 0, ENOMEM, "Out of memory");
return ENOMEM; return ENOMEM;
} }
name->u.rdnSequence.val = ptr; 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;
}
if (append) { /* Append RDN. If the caller wanted to prepend instead, we'll rotate. */
rdn = &name->u.rdnSequence.val[name->u.rdnSequence.len]; ret = add_RDNSequence(&name->u.rdnSequence, &rdn);
} else { 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], memmove(&name->u.rdnSequence.val[1],
&name->u.rdnSequence.val[0], &name->u.rdnSequence.val[0],
name->u.rdnSequence.len * (name->u.rdnSequence.len - 1) *
sizeof(name->u.rdnSequence.val[0])); sizeof(name->u.rdnSequence.val[0]));
name->u.rdnSequence.val[0] = rdn;
rdn = &name->u.rdnSequence.val[0];
}
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;
return 0; return 0;
} }
@@ -746,6 +781,7 @@ hx509_name_expand(hx509_context context,
{ {
Name *n = &name->der_name; Name *n = &name->der_name;
size_t i, j; size_t i, j;
int bounds_check = 1;
if (env == NULL) if (env == NULL)
return 0; return 0;
@@ -768,6 +804,7 @@ hx509_name_expand(hx509_context context,
free normalized utf8 string free normalized utf8 string
*/ */
DirectoryString *ds = &n->u.rdnSequence.val[i].val[j].value; DirectoryString *ds = &n->u.rdnSequence.val[i].val[j].value;
heim_oid *type = &n->u.rdnSequence.val[i].val[j].type;
char *p, *p2; char *p, *p2;
struct rk_strpool *strpool = NULL; struct rk_strpool *strpool = NULL;
@@ -822,15 +859,28 @@ hx509_name_expand(hx509_context context,
} }
} }
if (strpool) { if (strpool) {
size_t max_bytes;
free(ds->u.utf8String); free(ds->u.utf8String);
ds->u.utf8String = rk_strpoolcollect(strpool); ds->u.utf8String = rk_strpoolcollect(strpool);
if (ds->u.utf8String == NULL) { if (ds->u.utf8String == NULL) {
hx509_set_error_string(context, 0, ENOMEM, "out of memory"); hx509_set_error_string(context, 0, ENOMEM, "out of memory");
return ENOMEM; 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; return 0;
} }