diff --git a/lib/asn1/rfc2459.asn1 b/lib/asn1/rfc2459.asn1 index 20c5287c9..05214b866 100644 --- a/lib/asn1/rfc2459.asn1 +++ b/lib/asn1/rfc2459.asn1 @@ -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 diff --git a/lib/hx509/name.c b/lib/hx509/name.c index fef927635..33df8a23b 100644 --- a/lib/hx509/name.c +++ b/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 : "", + 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; }