Internal TLS: Fix X.509 name handling to use sequency of attributes

There may be more than one attribute of same type (e.g., multiple DC
attributes), so the code needs to be able to handle that. Replace the
fixed structure with an array of attributes.
This commit is contained in:
Jouni Malinen 2010-05-25 20:55:29 +03:00 committed by Jouni Malinen
parent 969b403fa7
commit 32b752ef8f
2 changed files with 110 additions and 95 deletions

View file

@ -22,16 +22,15 @@
static void x509_free_name(struct x509_name *name) static void x509_free_name(struct x509_name *name)
{ {
os_free(name->dc); size_t i;
os_free(name->cn);
os_free(name->c); for (i = 0; i < name->num_attr; i++) {
os_free(name->l); os_free(name->attr[i].value);
os_free(name->st); name->attr[i].value = NULL;
os_free(name->o); name->attr[i].type = X509_NAME_ATTR_NOT_USED;
os_free(name->ou); }
name->num_attr = 0;
os_free(name->email); os_free(name->email);
name->dc = NULL;
name->cn = name->c = name->l = name->st = name->o = name->ou = NULL;
name->email = NULL; name->email = NULL;
os_free(name->alt_email); os_free(name->alt_email);
@ -154,6 +153,7 @@ static int x509_str_compare(const char *a, const char *b)
int x509_name_compare(struct x509_name *a, struct x509_name *b) int x509_name_compare(struct x509_name *a, struct x509_name *b)
{ {
int res; int res;
size_t i;
if (!a && b) if (!a && b)
return -1; return -1;
@ -161,28 +161,20 @@ int x509_name_compare(struct x509_name *a, struct x509_name *b)
return 1; return 1;
if (!a && !b) if (!a && !b)
return 0; return 0;
if (a->num_attr < b->num_attr)
return -1;
if (a->num_attr > b->num_attr)
return 1;
res = x509_str_compare(a->dc, b->dc); for (i = 0; i < a->num_attr; i++) {
if (res) if (a->attr[i].type < b->attr[i].type)
return res; return -1;
res = x509_str_compare(a->cn, b->cn); if (a->attr[i].type > b->attr[i].type)
if (res) return -1;
return res; res = x509_str_compare(a->attr[i].value, b->attr[i].value);
res = x509_str_compare(a->c, b->c);
if (res)
return res;
res = x509_str_compare(a->l, b->l);
if (res)
return res;
res = x509_str_compare(a->st, b->st);
if (res)
return res;
res = x509_str_compare(a->o, b->o);
if (res)
return res;
res = x509_str_compare(a->ou, b->ou);
if (res) if (res)
return res; return res;
}
res = x509_str_compare(a->email, b->email); res = x509_str_compare(a->email, b->email);
if (res) if (res)
return res; return res;
@ -309,7 +301,7 @@ static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name,
struct asn1_hdr hdr; struct asn1_hdr hdr;
const u8 *pos, *end, *set_pos, *set_end, *seq_pos, *seq_end; const u8 *pos, *end, *set_pos, *set_end, *seq_pos, *seq_end;
struct asn1_oid oid; struct asn1_oid oid;
char **fieldp; char *val;
/* /*
* Name ::= CHOICE { RDNSequence } * Name ::= CHOICE { RDNSequence }
@ -339,6 +331,8 @@ static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name,
end = *next = pos + hdr.length; end = *next = pos + hdr.length;
while (pos < end) { while (pos < end) {
enum x509_name_attr_type type;
if (asn1_get_next(pos, end - pos, &hdr) < 0 || if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
hdr.class != ASN1_CLASS_UNIVERSAL || hdr.class != ASN1_CLASS_UNIVERSAL ||
hdr.tag != ASN1_TAG_SET) { hdr.tag != ASN1_TAG_SET) {
@ -386,34 +380,34 @@ static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name,
* pseudonym, generation qualifier. * pseudonym, generation qualifier.
* MUST: domainComponent (RFC 2247). * MUST: domainComponent (RFC 2247).
*/ */
fieldp = NULL; type = X509_NAME_ATTR_NOT_USED;
if (oid.len == 4 && if (oid.len == 4 &&
oid.oid[0] == 2 && oid.oid[1] == 5 && oid.oid[2] == 4) { oid.oid[0] == 2 && oid.oid[1] == 5 && oid.oid[2] == 4) {
/* id-at ::= 2.5.4 */ /* id-at ::= 2.5.4 */
switch (oid.oid[3]) { switch (oid.oid[3]) {
case 3: case 3:
/* commonName */ /* commonName */
fieldp = &name->cn; type = X509_NAME_ATTR_CN;
break; break;
case 6: case 6:
/* countryName */ /* countryName */
fieldp = &name->c; type = X509_NAME_ATTR_C;
break; break;
case 7: case 7:
/* localityName */ /* localityName */
fieldp = &name->l; type = X509_NAME_ATTR_L;
break; break;
case 8: case 8:
/* stateOrProvinceName */ /* stateOrProvinceName */
fieldp = &name->st; type = X509_NAME_ATTR_ST;
break; break;
case 10: case 10:
/* organizationName */ /* organizationName */
fieldp = &name->o; type = X509_NAME_ATTR_O;
break; break;
case 11: case 11:
/* organizationalUnitName */ /* organizationalUnitName */
fieldp = &name->ou; type = X509_NAME_ATTR_OU;
break; break;
} }
} else if (oid.len == 7 && } else if (oid.len == 7 &&
@ -422,17 +416,25 @@ static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name,
oid.oid[4] == 1 && oid.oid[5] == 9 && oid.oid[4] == 1 && oid.oid[5] == 9 &&
oid.oid[6] == 1) { oid.oid[6] == 1) {
/* 1.2.840.113549.1.9.1 - e-mailAddress */ /* 1.2.840.113549.1.9.1 - e-mailAddress */
fieldp = &name->email; os_free(name->email);
name->email = os_malloc(hdr.length + 1);
if (name->email == NULL) {
x509_free_name(name);
return -1;
}
os_memcpy(name->email, hdr.payload, hdr.length);
name->email[hdr.length] = '\0';
continue;
} else if (oid.len == 7 && } else if (oid.len == 7 &&
oid.oid[0] == 0 && oid.oid[1] == 9 && oid.oid[0] == 0 && oid.oid[1] == 9 &&
oid.oid[2] == 2342 && oid.oid[3] == 19200300 && oid.oid[2] == 2342 && oid.oid[3] == 19200300 &&
oid.oid[4] == 100 && oid.oid[5] == 1 && oid.oid[4] == 100 && oid.oid[5] == 1 &&
oid.oid[6] == 25) { oid.oid[6] == 25) {
/* 0.9.2342.19200300.100.1.25 - domainComponent */ /* 0.9.2342.19200300.100.1.25 - domainComponent */
fieldp = &name->dc; type = X509_NAME_ATTR_DC;
} }
if (fieldp == NULL) { if (type == X509_NAME_ATTR_NOT_USED) {
wpa_hexdump(MSG_DEBUG, "X509: Unrecognized OID", wpa_hexdump(MSG_DEBUG, "X509: Unrecognized OID",
(u8 *) oid.oid, (u8 *) oid.oid,
oid.len * sizeof(oid.oid[0])); oid.len * sizeof(oid.oid[0]));
@ -441,27 +443,60 @@ static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name,
continue; continue;
} }
os_free(*fieldp); if (name->num_attr == X509_MAX_NAME_ATTRIBUTES) {
*fieldp = os_malloc(hdr.length + 1); wpa_printf(MSG_INFO, "X509: Too many Name attributes");
if (*fieldp == NULL) {
x509_free_name(name); x509_free_name(name);
return -1; return -1;
} }
os_memcpy(*fieldp, hdr.payload, hdr.length);
(*fieldp)[hdr.length] = '\0'; val = os_malloc(hdr.length + 1);
if (os_strlen(*fieldp) != hdr.length) { if (val == NULL) {
x509_free_name(name);
return -1;
}
os_memcpy(val, hdr.payload, hdr.length);
val[hdr.length] = '\0';
if (os_strlen(val) != hdr.length) {
wpa_printf(MSG_INFO, "X509: Reject certificate with " wpa_printf(MSG_INFO, "X509: Reject certificate with "
"embedded NUL byte in a string (%s[NUL])", "embedded NUL byte in a string (%s[NUL])",
*fieldp); val);
x509_free_name(name); x509_free_name(name);
return -1; return -1;
} }
name->attr[name->num_attr].type = type;
name->attr[name->num_attr].value = val;
name->num_attr++;
} }
return 0; return 0;
} }
static char * x509_name_attr_str(enum x509_name_attr_type type)
{
switch (type) {
case X509_NAME_ATTR_NOT_USED:
return "[N/A]";
case X509_NAME_ATTR_DC:
return "DC";
case X509_NAME_ATTR_CN:
return "CN";
case X509_NAME_ATTR_C:
return "C";
case X509_NAME_ATTR_L:
return "L";
case X509_NAME_ATTR_ST:
return "ST";
case X509_NAME_ATTR_O:
return "O";
case X509_NAME_ATTR_OU:
return "OU";
}
return "?";
}
/** /**
* x509_name_string - Convert an X.509 certificate name into a string * x509_name_string - Convert an X.509 certificate name into a string
* @name: Name to convert * @name: Name to convert
@ -472,6 +507,7 @@ void x509_name_string(struct x509_name *name, char *buf, size_t len)
{ {
char *pos, *end; char *pos, *end;
int ret; int ret;
size_t i;
if (len == 0) if (len == 0)
return; return;
@ -479,52 +515,20 @@ void x509_name_string(struct x509_name *name, char *buf, size_t len)
pos = buf; pos = buf;
end = buf + len; end = buf + len;
if (name->c) { for (i = 0; i < name->num_attr; i++) {
ret = os_snprintf(pos, end - pos, "C=%s, ", name->c); ret = os_snprintf(pos, end - pos, "%s=%s, ",
if (ret < 0 || ret >= end - pos) x509_name_attr_str(name->attr[i].type),
goto done; name->attr[i].value);
pos += ret;
}
if (name->st) {
ret = os_snprintf(pos, end - pos, "ST=%s, ", name->st);
if (ret < 0 || ret >= end - pos)
goto done;
pos += ret;
}
if (name->l) {
ret = os_snprintf(pos, end - pos, "L=%s, ", name->l);
if (ret < 0 || ret >= end - pos)
goto done;
pos += ret;
}
if (name->o) {
ret = os_snprintf(pos, end - pos, "O=%s, ", name->o);
if (ret < 0 || ret >= end - pos)
goto done;
pos += ret;
}
if (name->ou) {
ret = os_snprintf(pos, end - pos, "OU=%s, ", name->ou);
if (ret < 0 || ret >= end - pos)
goto done;
pos += ret;
}
if (name->cn) {
ret = os_snprintf(pos, end - pos, "CN=%s, ", name->cn);
if (ret < 0 || ret >= end - pos)
goto done;
pos += ret;
}
if (name->dc) {
ret = os_snprintf(pos, end - pos, "DC=%s, ", name->dc);
if (ret < 0 || ret >= end - pos) if (ret < 0 || ret >= end - pos)
goto done; goto done;
pos += ret; pos += ret;
} }
if (pos > buf + 1 && pos[-1] == ' ' && pos[-2] == ',') { if (pos > buf + 1 && pos[-1] == ' ' && pos[-2] == ',') {
*pos-- = '\0'; pos--;
*pos-- = '\0'; *pos = '\0';
pos--;
*pos = '\0';
} }
if (name->email) { if (name->email) {

View file

@ -21,14 +21,25 @@ struct x509_algorithm_identifier {
struct asn1_oid oid; struct asn1_oid oid;
}; };
struct x509_name_attr {
enum x509_name_attr_type {
X509_NAME_ATTR_NOT_USED,
X509_NAME_ATTR_DC,
X509_NAME_ATTR_CN,
X509_NAME_ATTR_C,
X509_NAME_ATTR_L,
X509_NAME_ATTR_ST,
X509_NAME_ATTR_O,
X509_NAME_ATTR_OU
} type;
char *value;
};
#define X509_MAX_NAME_ATTRIBUTES 20
struct x509_name { struct x509_name {
char *dc; /* domainComponent */ struct x509_name_attr attr[X509_MAX_NAME_ATTRIBUTES];
char *cn; /* commonName */ size_t num_attr;
char *c; /* countryName */
char *l; /* localityName */
char *st; /* stateOrProvinceName */
char *o; /* organizationName */
char *ou; /* organizationalUnitName */
char *email; /* emailAddress */ char *email; /* emailAddress */
/* from alternative name extension */ /* from alternative name extension */