hx509: check Name RDN attribute size bounds
This commit is contained in:
@@ -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
|
||||
|
130
lib/hx509/name.c
130
lib/hx509/name.c
@@ -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;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user