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
|
-- 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
|
||||||
|
126
lib/hx509/name.c
126
lib/hx509/name.c
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user