|  | /* | 
|  | * X.509v3 certificate parsing and processing (RFC 3280 profile) | 
|  | * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi> | 
|  | * | 
|  | * This software may be distributed under the terms of the BSD license. | 
|  | * See README for more details. | 
|  | */ | 
|  |  | 
|  | #include "includes.h" | 
|  |  | 
|  | #include "common.h" | 
|  | #include "crypto/crypto.h" | 
|  | #include "asn1.h" | 
|  | #include "x509v3.h" | 
|  |  | 
|  |  | 
|  | void x509_free_name(struct x509_name *name) | 
|  | { | 
|  | size_t i; | 
|  |  | 
|  | for (i = 0; i < name->num_attr; i++) { | 
|  | os_free(name->attr[i].value); | 
|  | name->attr[i].value = NULL; | 
|  | name->attr[i].type = X509_NAME_ATTR_NOT_USED; | 
|  | } | 
|  | name->num_attr = 0; | 
|  | os_free(name->email); | 
|  | name->email = NULL; | 
|  |  | 
|  | os_free(name->alt_email); | 
|  | os_free(name->dns); | 
|  | os_free(name->uri); | 
|  | os_free(name->ip); | 
|  | name->alt_email = name->dns = name->uri = NULL; | 
|  | name->ip = NULL; | 
|  | name->ip_len = 0; | 
|  | os_memset(&name->rid, 0, sizeof(name->rid)); | 
|  | } | 
|  |  | 
|  |  | 
|  | /** | 
|  | * x509_certificate_free - Free an X.509 certificate | 
|  | * @cert: Certificate to be freed | 
|  | */ | 
|  | void x509_certificate_free(struct x509_certificate *cert) | 
|  | { | 
|  | if (cert == NULL) | 
|  | return; | 
|  | if (cert->next) { | 
|  | wpa_printf(MSG_DEBUG, "X509: x509_certificate_free: cer=%p " | 
|  | "was still on a list (next=%p)\n", | 
|  | cert, cert->next); | 
|  | } | 
|  | x509_free_name(&cert->issuer); | 
|  | x509_free_name(&cert->subject); | 
|  | os_free(cert->public_key); | 
|  | os_free(cert->sign_value); | 
|  | os_free(cert->subject_dn); | 
|  | os_free(cert); | 
|  | } | 
|  |  | 
|  |  | 
|  | /** | 
|  | * x509_certificate_free - Free an X.509 certificate chain | 
|  | * @cert: Pointer to the first certificate in the chain | 
|  | */ | 
|  | void x509_certificate_chain_free(struct x509_certificate *cert) | 
|  | { | 
|  | struct x509_certificate *next; | 
|  |  | 
|  | while (cert) { | 
|  | next = cert->next; | 
|  | cert->next = NULL; | 
|  | x509_certificate_free(cert); | 
|  | cert = next; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_whitespace(char c) | 
|  | { | 
|  | return c == ' ' || c == '\t'; | 
|  | } | 
|  |  | 
|  |  | 
|  | static void x509_str_strip_whitespace(char *a) | 
|  | { | 
|  | char *ipos, *opos; | 
|  | int remove_whitespace = 1; | 
|  |  | 
|  | ipos = opos = a; | 
|  |  | 
|  | while (*ipos) { | 
|  | if (remove_whitespace && x509_whitespace(*ipos)) | 
|  | ipos++; | 
|  | else { | 
|  | remove_whitespace = x509_whitespace(*ipos); | 
|  | *opos++ = *ipos++; | 
|  | } | 
|  | } | 
|  |  | 
|  | *opos-- = '\0'; | 
|  | if (opos > a && x509_whitespace(*opos)) | 
|  | *opos = '\0'; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_str_compare(const char *a, const char *b) | 
|  | { | 
|  | char *aa, *bb; | 
|  | int ret; | 
|  |  | 
|  | if (!a && b) | 
|  | return -1; | 
|  | if (a && !b) | 
|  | return 1; | 
|  | if (!a && !b) | 
|  | return 0; | 
|  |  | 
|  | aa = os_strdup(a); | 
|  | bb = os_strdup(b); | 
|  |  | 
|  | if (aa == NULL || bb == NULL) { | 
|  | os_free(aa); | 
|  | os_free(bb); | 
|  | return os_strcasecmp(a, b); | 
|  | } | 
|  |  | 
|  | x509_str_strip_whitespace(aa); | 
|  | x509_str_strip_whitespace(bb); | 
|  |  | 
|  | ret = os_strcasecmp(aa, bb); | 
|  |  | 
|  | os_free(aa); | 
|  | os_free(bb); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  |  | 
|  | /** | 
|  | * x509_name_compare - Compare X.509 certificate names | 
|  | * @a: Certificate name | 
|  | * @b: Certificate name | 
|  | * Returns: <0, 0, or >0 based on whether a is less than, equal to, or | 
|  | * greater than b | 
|  | */ | 
|  | int x509_name_compare(struct x509_name *a, struct x509_name *b) | 
|  | { | 
|  | int res; | 
|  | size_t i; | 
|  |  | 
|  | if (!a && b) | 
|  | return -1; | 
|  | if (a && !b) | 
|  | return 1; | 
|  | if (!a && !b) | 
|  | return 0; | 
|  | if (a->num_attr < b->num_attr) | 
|  | return -1; | 
|  | if (a->num_attr > b->num_attr) | 
|  | return 1; | 
|  |  | 
|  | for (i = 0; i < a->num_attr; i++) { | 
|  | if (a->attr[i].type < b->attr[i].type) | 
|  | return -1; | 
|  | if (a->attr[i].type > b->attr[i].type) | 
|  | return -1; | 
|  | res = x509_str_compare(a->attr[i].value, b->attr[i].value); | 
|  | if (res) | 
|  | return res; | 
|  | } | 
|  | res = x509_str_compare(a->email, b->email); | 
|  | if (res) | 
|  | return res; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | int x509_parse_algorithm_identifier(const u8 *buf, size_t len, | 
|  | struct x509_algorithm_identifier *id, | 
|  | const u8 **next) | 
|  | { | 
|  | struct asn1_hdr hdr; | 
|  | const u8 *pos, *end; | 
|  |  | 
|  | /* | 
|  | * AlgorithmIdentifier ::= SEQUENCE { | 
|  | *     algorithm            OBJECT IDENTIFIER, | 
|  | *     parameters           ANY DEFINED BY algorithm OPTIONAL | 
|  | * } | 
|  | */ | 
|  |  | 
|  | if (asn1_get_next(buf, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { | 
|  | asn1_unexpected(&hdr, | 
|  | "X509: Expected SEQUENCE (AlgorithmIdentifier)"); | 
|  | return -1; | 
|  | } | 
|  | if (hdr.length > buf + len - hdr.payload) | 
|  | return -1; | 
|  | pos = hdr.payload; | 
|  | end = pos + hdr.length; | 
|  |  | 
|  | *next = end; | 
|  |  | 
|  | if (asn1_get_oid(pos, end - pos, &id->oid, &pos)) | 
|  | return -1; | 
|  |  | 
|  | /* TODO: optional parameters */ | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_parse_public_key(const u8 *buf, size_t len, | 
|  | struct x509_certificate *cert, | 
|  | const u8 **next) | 
|  | { | 
|  | struct asn1_hdr hdr; | 
|  | const u8 *pos, *end; | 
|  |  | 
|  | /* | 
|  | * SubjectPublicKeyInfo ::= SEQUENCE { | 
|  | *     algorithm            AlgorithmIdentifier, | 
|  | *     subjectPublicKey     BIT STRING | 
|  | * } | 
|  | */ | 
|  |  | 
|  | pos = buf; | 
|  | end = buf + len; | 
|  |  | 
|  | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | 
|  | !asn1_is_sequence(&hdr)) { | 
|  | asn1_unexpected(&hdr, | 
|  | "X509: Expected SEQUENCE (SubjectPublicKeyInfo)"); | 
|  | return -1; | 
|  | } | 
|  | pos = hdr.payload; | 
|  |  | 
|  | if (hdr.length > end - pos) | 
|  | return -1; | 
|  | end = pos + hdr.length; | 
|  | *next = end; | 
|  |  | 
|  | if (x509_parse_algorithm_identifier(pos, end - pos, | 
|  | &cert->public_key_alg, &pos)) | 
|  | return -1; | 
|  |  | 
|  | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | 
|  | !asn1_is_bitstring(&hdr)) { | 
|  | asn1_unexpected(&hdr, | 
|  | "X509: Expected BITSTRING (subjectPublicKey)"); | 
|  | return -1; | 
|  | } | 
|  | if (hdr.length < 1) | 
|  | return -1; | 
|  | pos = hdr.payload; | 
|  | if (*pos) { | 
|  | wpa_printf(MSG_DEBUG, | 
|  | "X509: BITSTRING (subjectPublicKey) - %d unused bits", | 
|  | *pos); | 
|  | /* | 
|  | * TODO: should this be rejected? X.509 certificates are | 
|  | * unlikely to use such a construction. Now we would end up | 
|  | * including the extra bits in the buffer which may also be | 
|  | * ok. | 
|  | */ | 
|  | } | 
|  | os_free(cert->public_key); | 
|  | cert->public_key = os_memdup(pos + 1, hdr.length - 1); | 
|  | if (cert->public_key == NULL) { | 
|  | wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for " | 
|  | "public key"); | 
|  | return -1; | 
|  | } | 
|  | cert->public_key_len = hdr.length - 1; | 
|  | wpa_hexdump(MSG_MSGDUMP, "X509: subjectPublicKey", | 
|  | cert->public_key, cert->public_key_len); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, | 
|  | const u8 **next) | 
|  | { | 
|  | struct asn1_hdr hdr; | 
|  | const u8 *pos, *end, *set_pos, *set_end, *seq_pos, *seq_end; | 
|  | struct asn1_oid oid; | 
|  | char *val; | 
|  |  | 
|  | /* | 
|  | * Name ::= CHOICE { RDNSequence } | 
|  | * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName | 
|  | * RelativeDistinguishedName ::= SET OF AttributeTypeAndValue | 
|  | * AttributeTypeAndValue ::= SEQUENCE { | 
|  | *     type     AttributeType, | 
|  | *     value    AttributeValue | 
|  | * } | 
|  | * AttributeType ::= OBJECT IDENTIFIER | 
|  | * AttributeValue ::= ANY DEFINED BY AttributeType | 
|  | */ | 
|  |  | 
|  | if (asn1_get_next(buf, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { | 
|  | asn1_unexpected(&hdr, | 
|  | "X509: Expected SEQUENCE (Name / RDNSequencer)"); | 
|  | return -1; | 
|  | } | 
|  | pos = hdr.payload; | 
|  |  | 
|  | if (hdr.length > buf + len - pos) | 
|  | return -1; | 
|  |  | 
|  | end = *next = pos + hdr.length; | 
|  |  | 
|  | while (pos < end) { | 
|  | enum x509_name_attr_type type; | 
|  |  | 
|  | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | 
|  | !asn1_is_set(&hdr)) { | 
|  | asn1_unexpected(&hdr, | 
|  | "X509: Expected SET (RelativeDistinguishedName)"); | 
|  | x509_free_name(name); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | set_pos = hdr.payload; | 
|  | pos = set_end = hdr.payload + hdr.length; | 
|  |  | 
|  | if (asn1_get_next(set_pos, set_end - set_pos, &hdr) < 0 || | 
|  | !asn1_is_sequence(&hdr)) { | 
|  | asn1_unexpected(&hdr, | 
|  | "X509: Expected SEQUENCE (AttributeTypeAndValue)"); | 
|  | x509_free_name(name); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | seq_pos = hdr.payload; | 
|  | seq_end = hdr.payload + hdr.length; | 
|  |  | 
|  | if (asn1_get_oid(seq_pos, seq_end - seq_pos, &oid, &seq_pos)) { | 
|  | x509_free_name(name); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (asn1_get_next(seq_pos, seq_end - seq_pos, &hdr) < 0 || | 
|  | hdr.class != ASN1_CLASS_UNIVERSAL) { | 
|  | wpa_printf(MSG_DEBUG, "X509: Failed to parse " | 
|  | "AttributeValue"); | 
|  | x509_free_name(name); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (!asn1_is_string_type(&hdr)) { | 
|  | wpa_printf(MSG_DEBUG, | 
|  | "X509: Ignore non-string type attribute (tag 0x%x)", | 
|  | hdr.tag); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /* RFC 3280: | 
|  | * MUST: country, organization, organizational-unit, | 
|  | * distinguished name qualifier, state or province name, | 
|  | * common name, serial number. | 
|  | * SHOULD: locality, title, surname, given name, initials, | 
|  | * pseudonym, generation qualifier. | 
|  | * MUST: domainComponent (RFC 2247). | 
|  | */ | 
|  | type = X509_NAME_ATTR_NOT_USED; | 
|  | if (oid.len == 4 && | 
|  | oid.oid[0] == 2 && oid.oid[1] == 5 && oid.oid[2] == 4) { | 
|  | /* id-at ::= 2.5.4 */ | 
|  | switch (oid.oid[3]) { | 
|  | case 3: | 
|  | /* commonName */ | 
|  | type = X509_NAME_ATTR_CN; | 
|  | break; | 
|  | case 6: | 
|  | /*  countryName */ | 
|  | type = X509_NAME_ATTR_C; | 
|  | break; | 
|  | case 7: | 
|  | /* localityName */ | 
|  | type = X509_NAME_ATTR_L; | 
|  | break; | 
|  | case 8: | 
|  | /* stateOrProvinceName */ | 
|  | type = X509_NAME_ATTR_ST; | 
|  | break; | 
|  | case 10: | 
|  | /* organizationName */ | 
|  | type = X509_NAME_ATTR_O; | 
|  | break; | 
|  | case 11: | 
|  | /* organizationalUnitName */ | 
|  | type = X509_NAME_ATTR_OU; | 
|  | break; | 
|  | } | 
|  | } else if (oid.len == 7 && | 
|  | oid.oid[0] == 1 && oid.oid[1] == 2 && | 
|  | oid.oid[2] == 840 && oid.oid[3] == 113549 && | 
|  | oid.oid[4] == 1 && oid.oid[5] == 9 && | 
|  | oid.oid[6] == 1) { | 
|  | /* 1.2.840.113549.1.9.1 - e-mailAddress */ | 
|  | 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 && | 
|  | oid.oid[0] == 0 && oid.oid[1] == 9 && | 
|  | oid.oid[2] == 2342 && oid.oid[3] == 19200300 && | 
|  | oid.oid[4] == 100 && oid.oid[5] == 1 && | 
|  | oid.oid[6] == 25) { | 
|  | /* 0.9.2342.19200300.100.1.25 - domainComponent */ | 
|  | type = X509_NAME_ATTR_DC; | 
|  | } | 
|  |  | 
|  | if (type == X509_NAME_ATTR_NOT_USED) { | 
|  | wpa_hexdump(MSG_DEBUG, "X509: Unrecognized OID", | 
|  | (u8 *) oid.oid, | 
|  | oid.len * sizeof(oid.oid[0])); | 
|  | wpa_hexdump_ascii(MSG_MSGDUMP, "X509: Attribute Data", | 
|  | hdr.payload, hdr.length); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (name->num_attr == X509_MAX_NAME_ATTRIBUTES) { | 
|  | wpa_printf(MSG_INFO, "X509: Too many Name attributes"); | 
|  | x509_free_name(name); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | val = dup_binstr(hdr.payload, hdr.length); | 
|  | if (val == NULL) { | 
|  | x509_free_name(name); | 
|  | return -1; | 
|  | } | 
|  | if (os_strlen(val) != hdr.length) { | 
|  | wpa_printf(MSG_INFO, "X509: Reject certificate with " | 
|  | "embedded NUL byte in a string (%s[NUL])", | 
|  | val); | 
|  | os_free(val); | 
|  | x509_free_name(name); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | name->attr[name->num_attr].type = type; | 
|  | name->attr[name->num_attr].value = val; | 
|  | name->num_attr++; | 
|  | } | 
|  |  | 
|  | 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 | 
|  | * @name: Name to convert | 
|  | * @buf: Buffer for the string | 
|  | * @len: Maximum buffer length | 
|  | */ | 
|  | void x509_name_string(struct x509_name *name, char *buf, size_t len) | 
|  | { | 
|  | char *pos, *end; | 
|  | int ret; | 
|  | size_t i; | 
|  |  | 
|  | if (len == 0) | 
|  | return; | 
|  |  | 
|  | pos = buf; | 
|  | end = buf + len; | 
|  |  | 
|  | for (i = 0; i < name->num_attr; i++) { | 
|  | ret = os_snprintf(pos, end - pos, "%s=%s, ", | 
|  | x509_name_attr_str(name->attr[i].type), | 
|  | name->attr[i].value); | 
|  | if (os_snprintf_error(end - pos, ret)) | 
|  | goto done; | 
|  | pos += ret; | 
|  | } | 
|  |  | 
|  | if (pos > buf + 1 && pos[-1] == ' ' && pos[-2] == ',') { | 
|  | pos--; | 
|  | *pos = '\0'; | 
|  | pos--; | 
|  | *pos = '\0'; | 
|  | } | 
|  |  | 
|  | if (name->email) { | 
|  | ret = os_snprintf(pos, end - pos, "/emailAddress=%s", | 
|  | name->email); | 
|  | if (os_snprintf_error(end - pos, ret)) | 
|  | goto done; | 
|  | pos += ret; | 
|  | } | 
|  |  | 
|  | done: | 
|  | if (pos < end) | 
|  | *pos = '\0'; | 
|  | end[-1] = '\0'; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int parse_uint2(const char *pos, size_t len) | 
|  | { | 
|  | char buf[3]; | 
|  | int ret; | 
|  |  | 
|  | if (len < 2) | 
|  | return -1; | 
|  | buf[0] = pos[0]; | 
|  | buf[1] = pos[1]; | 
|  | buf[2] = 0x00; | 
|  | if (sscanf(buf, "%2d", &ret) != 1) | 
|  | return -1; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int parse_uint4(const char *pos, size_t len) | 
|  | { | 
|  | char buf[5]; | 
|  | int ret; | 
|  |  | 
|  | if (len < 4) | 
|  | return -1; | 
|  | buf[0] = pos[0]; | 
|  | buf[1] = pos[1]; | 
|  | buf[2] = pos[2]; | 
|  | buf[3] = pos[3]; | 
|  | buf[4] = 0x00; | 
|  | if (sscanf(buf, "%4d", &ret) != 1) | 
|  | return -1; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  |  | 
|  | int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag, os_time_t *val) | 
|  | { | 
|  | const char *pos, *end; | 
|  | int year, month, day, hour, min, sec; | 
|  |  | 
|  | /* | 
|  | * Time ::= CHOICE { | 
|  | *     utcTime        UTCTime, | 
|  | *     generalTime    GeneralizedTime | 
|  | * } | 
|  | * | 
|  | * UTCTime: YYMMDDHHMMSSZ | 
|  | * GeneralizedTime: YYYYMMDDHHMMSSZ | 
|  | */ | 
|  |  | 
|  | pos = (const char *) buf; | 
|  | end = pos + len; | 
|  |  | 
|  | switch (asn1_tag) { | 
|  | case ASN1_TAG_UTCTIME: | 
|  | if (len != 13 || buf[12] != 'Z') { | 
|  | wpa_hexdump_ascii(MSG_DEBUG, "X509: Unrecognized " | 
|  | "UTCTime format", buf, len); | 
|  | return -1; | 
|  | } | 
|  | year = parse_uint2(pos, end - pos); | 
|  | if (year < 0) { | 
|  | wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse " | 
|  | "UTCTime year", buf, len); | 
|  | return -1; | 
|  | } | 
|  | if (year < 50) | 
|  | year += 2000; | 
|  | else | 
|  | year += 1900; | 
|  | pos += 2; | 
|  | break; | 
|  | case ASN1_TAG_GENERALIZEDTIME: | 
|  | if (len != 15 || buf[14] != 'Z') { | 
|  | wpa_hexdump_ascii(MSG_DEBUG, "X509: Unrecognized " | 
|  | "GeneralizedTime format", buf, len); | 
|  | return -1; | 
|  | } | 
|  | year = parse_uint4(pos, end - pos); | 
|  | if (year < 0) { | 
|  | wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse " | 
|  | "GeneralizedTime year", buf, len); | 
|  | return -1; | 
|  | } | 
|  | pos += 4; | 
|  | break; | 
|  | default: | 
|  | wpa_printf(MSG_DEBUG, "X509: Expected UTCTime or " | 
|  | "GeneralizedTime - found tag 0x%x", asn1_tag); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | month = parse_uint2(pos, end - pos); | 
|  | if (month < 0) { | 
|  | wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " | 
|  | "(month)", buf, len); | 
|  | return -1; | 
|  | } | 
|  | pos += 2; | 
|  |  | 
|  | day = parse_uint2(pos, end - pos); | 
|  | if (day < 0) { | 
|  | wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " | 
|  | "(day)", buf, len); | 
|  | return -1; | 
|  | } | 
|  | pos += 2; | 
|  |  | 
|  | hour = parse_uint2(pos, end - pos); | 
|  | if (hour < 0) { | 
|  | wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " | 
|  | "(hour)", buf, len); | 
|  | return -1; | 
|  | } | 
|  | pos += 2; | 
|  |  | 
|  | min = parse_uint2(pos, end - pos); | 
|  | if (min < 0) { | 
|  | wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " | 
|  | "(min)", buf, len); | 
|  | return -1; | 
|  | } | 
|  | pos += 2; | 
|  |  | 
|  | sec = parse_uint2(pos, end - pos); | 
|  | if (sec < 0) { | 
|  | wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " | 
|  | "(sec)", buf, len); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (os_mktime(year, month, day, hour, min, sec, val) < 0) { | 
|  | wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to convert Time", | 
|  | buf, len); | 
|  | if (year < 1970) { | 
|  | /* | 
|  | * At least some test certificates have been configured | 
|  | * to use dates prior to 1970. Set the date to | 
|  | * beginning of 1970 to handle these case. | 
|  | */ | 
|  | wpa_printf(MSG_DEBUG, "X509: Year=%d before epoch - " | 
|  | "assume epoch as the time", year); | 
|  | *val = 0; | 
|  | return 0; | 
|  | } | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_parse_validity(const u8 *buf, size_t len, | 
|  | struct x509_certificate *cert, const u8 **next) | 
|  | { | 
|  | struct asn1_hdr hdr; | 
|  | const u8 *pos; | 
|  | size_t plen; | 
|  |  | 
|  | /* | 
|  | * Validity ::= SEQUENCE { | 
|  | *     notBefore      Time, | 
|  | *     notAfter       Time | 
|  | * } | 
|  | * | 
|  | * RFC 3280, 4.1.2.5: | 
|  | * CAs conforming to this profile MUST always encode certificate | 
|  | * validity dates through the year 2049 as UTCTime; certificate | 
|  | * validity dates in 2050 or later MUST be encoded as GeneralizedTime. | 
|  | */ | 
|  |  | 
|  | if (asn1_get_next(buf, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { | 
|  | asn1_unexpected(&hdr, "X509: Expected SEQUENCE (Validity)"); | 
|  | return -1; | 
|  | } | 
|  | pos = hdr.payload; | 
|  | plen = hdr.length; | 
|  |  | 
|  | if (plen > (size_t) (buf + len - pos)) | 
|  | return -1; | 
|  |  | 
|  | *next = pos + plen; | 
|  |  | 
|  | if (asn1_get_next(pos, plen, &hdr) < 0 || | 
|  | (!asn1_is_utctime(&hdr) && !asn1_is_generalizedtime(&hdr)) || | 
|  | x509_parse_time(hdr.payload, hdr.length, hdr.tag, | 
|  | &cert->not_before) < 0) { | 
|  | wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse notBefore " | 
|  | "Time", hdr.payload, hdr.length); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | pos = hdr.payload + hdr.length; | 
|  | plen = *next - pos; | 
|  |  | 
|  | if (asn1_get_next(pos, plen, &hdr) < 0 || | 
|  | (!asn1_is_utctime(&hdr) && !asn1_is_generalizedtime(&hdr)) || | 
|  | x509_parse_time(hdr.payload, hdr.length, hdr.tag, | 
|  | &cert->not_after) < 0) { | 
|  | wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse notAfter " | 
|  | "Time", hdr.payload, hdr.length); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | wpa_printf(MSG_MSGDUMP, "X509: Validity: notBefore: %lu notAfter: %lu", | 
|  | (unsigned long) cert->not_before, | 
|  | (unsigned long) cert->not_after); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_id_ce_oid(struct asn1_oid *oid) | 
|  | { | 
|  | /* id-ce arc from X.509 for standard X.509v3 extensions */ | 
|  | return oid->len >= 4 && | 
|  | oid->oid[0] == 2 /* joint-iso-ccitt */ && | 
|  | oid->oid[1] == 5 /* ds */ && | 
|  | oid->oid[2] == 29 /* id-ce */; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_any_ext_key_usage_oid(struct asn1_oid *oid) | 
|  | { | 
|  | return oid->len == 6 && | 
|  | x509_id_ce_oid(oid) && | 
|  | oid->oid[3] == 37 /* extKeyUsage */ && | 
|  | oid->oid[4] == 0 /* anyExtendedKeyUsage */; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_parse_ext_key_usage(struct x509_certificate *cert, | 
|  | const u8 *pos, size_t len) | 
|  | { | 
|  | struct asn1_hdr hdr; | 
|  |  | 
|  | /* | 
|  | * KeyUsage ::= BIT STRING { | 
|  | *     digitalSignature        (0), | 
|  | *     nonRepudiation          (1), | 
|  | *     keyEncipherment         (2), | 
|  | *     dataEncipherment        (3), | 
|  | *     keyAgreement            (4), | 
|  | *     keyCertSign             (5), | 
|  | *     cRLSign                 (6), | 
|  | *     encipherOnly            (7), | 
|  | *     decipherOnly            (8) } | 
|  | */ | 
|  |  | 
|  | if (asn1_get_next(pos, len, &hdr) < 0 || !asn1_is_bitstring(&hdr) || | 
|  | hdr.length < 1) { | 
|  | asn1_unexpected(&hdr, "X509: Expected BIT STRING in KeyUsage"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | cert->extensions_present |= X509_EXT_KEY_USAGE; | 
|  | cert->key_usage = asn1_bit_string_to_long(hdr.payload, hdr.length); | 
|  |  | 
|  | wpa_printf(MSG_DEBUG, "X509: KeyUsage 0x%lx", cert->key_usage); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_parse_ext_basic_constraints(struct x509_certificate *cert, | 
|  | const u8 *pos, size_t len) | 
|  | { | 
|  | struct asn1_hdr hdr; | 
|  | unsigned long value; | 
|  | size_t left; | 
|  | const u8 *end_seq; | 
|  |  | 
|  | /* | 
|  | * BasicConstraints ::= SEQUENCE { | 
|  | * cA                      BOOLEAN DEFAULT FALSE, | 
|  | * pathLenConstraint       INTEGER (0..MAX) OPTIONAL } | 
|  | */ | 
|  |  | 
|  | if (asn1_get_next(pos, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { | 
|  | asn1_unexpected(&hdr, | 
|  | "X509: Expected SEQUENCE in BasicConstraints"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | cert->extensions_present |= X509_EXT_BASIC_CONSTRAINTS; | 
|  |  | 
|  | if (hdr.length == 0) | 
|  | return 0; | 
|  |  | 
|  | end_seq = hdr.payload + hdr.length; | 
|  | if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0) { | 
|  | wpa_printf(MSG_DEBUG, "X509: Failed to parse " | 
|  | "BasicConstraints"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (asn1_is_boolean(&hdr)) { | 
|  | cert->ca = hdr.payload[0]; | 
|  |  | 
|  | pos = hdr.payload + hdr.length; | 
|  | if (pos >= end_seq) { | 
|  | /* No optional pathLenConstraint */ | 
|  | wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d", | 
|  | cert->ca); | 
|  | return 0; | 
|  | } | 
|  | if (asn1_get_next(pos, end_seq - pos, &hdr) < 0) { | 
|  | wpa_printf(MSG_DEBUG, "X509: Failed to parse " | 
|  | "BasicConstraints"); | 
|  | return -1; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!asn1_is_integer(&hdr)) { | 
|  | asn1_unexpected(&hdr, | 
|  | "X509: Expected INTEGER in BasicConstraints"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | pos = hdr.payload; | 
|  | left = hdr.length; | 
|  | value = 0; | 
|  | while (left) { | 
|  | value <<= 8; | 
|  | value |= *pos++; | 
|  | left--; | 
|  | } | 
|  |  | 
|  | cert->path_len_constraint = value; | 
|  | cert->extensions_present |= X509_EXT_PATH_LEN_CONSTRAINT; | 
|  |  | 
|  | wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d " | 
|  | "pathLenConstraint=%lu", | 
|  | cert->ca, cert->path_len_constraint); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_parse_alt_name_rfc8222(struct x509_name *name, | 
|  | const u8 *pos, size_t len) | 
|  | { | 
|  | /* rfc822Name IA5String */ | 
|  | wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - rfc822Name", pos, len); | 
|  | os_free(name->alt_email); | 
|  | name->alt_email = os_zalloc(len + 1); | 
|  | if (name->alt_email == NULL) | 
|  | return -1; | 
|  | os_memcpy(name->alt_email, pos, len); | 
|  | if (os_strlen(name->alt_email) != len) { | 
|  | wpa_printf(MSG_INFO, "X509: Reject certificate with " | 
|  | "embedded NUL byte in rfc822Name (%s[NUL])", | 
|  | name->alt_email); | 
|  | os_free(name->alt_email); | 
|  | name->alt_email = NULL; | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_parse_alt_name_dns(struct x509_name *name, | 
|  | const u8 *pos, size_t len) | 
|  | { | 
|  | /* dNSName IA5String */ | 
|  | wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - dNSName", pos, len); | 
|  | os_free(name->dns); | 
|  | name->dns = os_zalloc(len + 1); | 
|  | if (name->dns == NULL) | 
|  | return -1; | 
|  | os_memcpy(name->dns, pos, len); | 
|  | if (os_strlen(name->dns) != len) { | 
|  | wpa_printf(MSG_INFO, "X509: Reject certificate with " | 
|  | "embedded NUL byte in dNSName (%s[NUL])", | 
|  | name->dns); | 
|  | os_free(name->dns); | 
|  | name->dns = NULL; | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_parse_alt_name_uri(struct x509_name *name, | 
|  | const u8 *pos, size_t len) | 
|  | { | 
|  | /* uniformResourceIdentifier IA5String */ | 
|  | wpa_hexdump_ascii(MSG_MSGDUMP, | 
|  | "X509: altName - uniformResourceIdentifier", | 
|  | pos, len); | 
|  | os_free(name->uri); | 
|  | name->uri = os_zalloc(len + 1); | 
|  | if (name->uri == NULL) | 
|  | return -1; | 
|  | os_memcpy(name->uri, pos, len); | 
|  | if (os_strlen(name->uri) != len) { | 
|  | wpa_printf(MSG_INFO, "X509: Reject certificate with " | 
|  | "embedded NUL byte in uniformResourceIdentifier " | 
|  | "(%s[NUL])", name->uri); | 
|  | os_free(name->uri); | 
|  | name->uri = NULL; | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_parse_alt_name_ip(struct x509_name *name, | 
|  | const u8 *pos, size_t len) | 
|  | { | 
|  | /* iPAddress OCTET STRING */ | 
|  | wpa_hexdump(MSG_MSGDUMP, "X509: altName - iPAddress", pos, len); | 
|  | os_free(name->ip); | 
|  | name->ip = os_memdup(pos, len); | 
|  | if (name->ip == NULL) | 
|  | return -1; | 
|  | name->ip_len = len; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_parse_alt_name_rid(struct x509_name *name, | 
|  | const u8 *pos, size_t len) | 
|  | { | 
|  | char buf[80]; | 
|  |  | 
|  | /* registeredID OBJECT IDENTIFIER */ | 
|  | if (asn1_parse_oid(pos, len, &name->rid) < 0) | 
|  | return -1; | 
|  |  | 
|  | asn1_oid_to_str(&name->rid, buf, sizeof(buf)); | 
|  | wpa_printf(MSG_MSGDUMP, "X509: altName - registeredID: %s", buf); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_parse_ext_alt_name(struct x509_name *name, | 
|  | const u8 *pos, size_t len) | 
|  | { | 
|  | struct asn1_hdr hdr; | 
|  | const u8 *p, *end; | 
|  |  | 
|  | /* | 
|  | * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName | 
|  | * | 
|  | * GeneralName ::= CHOICE { | 
|  | *     otherName                       [0]     OtherName, | 
|  | *     rfc822Name                      [1]     IA5String, | 
|  | *     dNSName                         [2]     IA5String, | 
|  | *     x400Address                     [3]     ORAddress, | 
|  | *     directoryName                   [4]     Name, | 
|  | *     ediPartyName                    [5]     EDIPartyName, | 
|  | *     uniformResourceIdentifier       [6]     IA5String, | 
|  | *     iPAddress                       [7]     OCTET STRING, | 
|  | *     registeredID                    [8]     OBJECT IDENTIFIER } | 
|  | * | 
|  | * OtherName ::= SEQUENCE { | 
|  | *     type-id    OBJECT IDENTIFIER, | 
|  | *     value      [0] EXPLICIT ANY DEFINED BY type-id } | 
|  | * | 
|  | * EDIPartyName ::= SEQUENCE { | 
|  | *     nameAssigner            [0]     DirectoryString OPTIONAL, | 
|  | *     partyName               [1]     DirectoryString } | 
|  | */ | 
|  |  | 
|  | for (p = pos, end = pos + len; p < end; p = hdr.payload + hdr.length) { | 
|  | int res; | 
|  |  | 
|  | if (asn1_get_next(p, end - p, &hdr) < 0) { | 
|  | wpa_printf(MSG_DEBUG, "X509: Failed to parse " | 
|  | "SubjectAltName item"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) | 
|  | continue; | 
|  |  | 
|  | switch (hdr.tag) { | 
|  | case 1: | 
|  | res = x509_parse_alt_name_rfc8222(name, hdr.payload, | 
|  | hdr.length); | 
|  | break; | 
|  | case 2: | 
|  | res = x509_parse_alt_name_dns(name, hdr.payload, | 
|  | hdr.length); | 
|  | break; | 
|  | case 6: | 
|  | res = x509_parse_alt_name_uri(name, hdr.payload, | 
|  | hdr.length); | 
|  | break; | 
|  | case 7: | 
|  | res = x509_parse_alt_name_ip(name, hdr.payload, | 
|  | hdr.length); | 
|  | break; | 
|  | case 8: | 
|  | res = x509_parse_alt_name_rid(name, hdr.payload, | 
|  | hdr.length); | 
|  | break; | 
|  | case 0: /* TODO: otherName */ | 
|  | case 3: /* TODO: x500Address */ | 
|  | case 4: /* TODO: directoryName */ | 
|  | case 5: /* TODO: ediPartyName */ | 
|  | default: | 
|  | res = 0; | 
|  | break; | 
|  | } | 
|  | if (res < 0) | 
|  | return res; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_parse_ext_subject_alt_name(struct x509_certificate *cert, | 
|  | const u8 *pos, size_t len) | 
|  | { | 
|  | struct asn1_hdr hdr; | 
|  |  | 
|  | /* SubjectAltName ::= GeneralNames */ | 
|  |  | 
|  | if (asn1_get_next(pos, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { | 
|  | asn1_unexpected(&hdr, | 
|  | "X509: Expected SEQUENCE in SubjectAltName"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | wpa_printf(MSG_DEBUG, "X509: SubjectAltName"); | 
|  | cert->extensions_present |= X509_EXT_SUBJECT_ALT_NAME; | 
|  |  | 
|  | if (hdr.length == 0) | 
|  | return 0; | 
|  |  | 
|  | return x509_parse_ext_alt_name(&cert->subject, hdr.payload, | 
|  | hdr.length); | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_parse_ext_issuer_alt_name(struct x509_certificate *cert, | 
|  | const u8 *pos, size_t len) | 
|  | { | 
|  | struct asn1_hdr hdr; | 
|  |  | 
|  | /* IssuerAltName ::= GeneralNames */ | 
|  |  | 
|  | if (asn1_get_next(pos, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { | 
|  | asn1_unexpected(&hdr, | 
|  | "X509: Expected SEQUENCE in IssuerAltName"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | wpa_printf(MSG_DEBUG, "X509: IssuerAltName"); | 
|  | cert->extensions_present |= X509_EXT_ISSUER_ALT_NAME; | 
|  |  | 
|  | if (hdr.length == 0) | 
|  | return 0; | 
|  |  | 
|  | return x509_parse_ext_alt_name(&cert->issuer, hdr.payload, | 
|  | hdr.length); | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_id_cert_policy_any_oid(struct asn1_oid *oid) | 
|  | { | 
|  | return oid->len == 5 && | 
|  | oid->oid[0] == 2 /* iso/itu-t */ && | 
|  | oid->oid[1] == 5 /* X.500 Directory Services */ && | 
|  | oid->oid[2] == 29 /* id-ce */ && | 
|  | oid->oid[3] == 32 /* id-ce-certificate-policies */ && | 
|  | oid->oid[4] == 0 /* anyPolicy */; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_id_wfa_oid(struct asn1_oid *oid) | 
|  | { | 
|  | return oid->len >= 7 && | 
|  | oid->oid[0] == 1 /* iso */ && | 
|  | oid->oid[1] == 3 /* identified-organization */ && | 
|  | oid->oid[2] == 6 /* dod */ && | 
|  | oid->oid[3] == 1 /* internet */ && | 
|  | oid->oid[4] == 4 /* private */ && | 
|  | oid->oid[5] == 1 /* enterprise */ && | 
|  | oid->oid[6] == 40808 /* WFA */; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_id_wfa_tod_oid(struct asn1_oid *oid) | 
|  | { | 
|  | return oid->len >= 9 && | 
|  | x509_id_wfa_oid(oid) && | 
|  | oid->oid[7] == 1 && | 
|  | oid->oid[8] == 3; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_id_wfa_tod_strict_oid(struct asn1_oid *oid) | 
|  | { | 
|  | return oid->len == 10 && | 
|  | x509_id_wfa_tod_oid(oid) && | 
|  | oid->oid[9] == 1; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_id_wfa_tod_tofu_oid(struct asn1_oid *oid) | 
|  | { | 
|  | return oid->len == 10 && | 
|  | x509_id_wfa_tod_oid(oid) && | 
|  | oid->oid[9] == 2; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_parse_ext_certificate_policies(struct x509_certificate *cert, | 
|  | const u8 *pos, size_t len) | 
|  | { | 
|  | struct asn1_hdr hdr; | 
|  | const u8 *end; | 
|  |  | 
|  | /* | 
|  | * certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation | 
|  | * | 
|  | * PolicyInformation ::= SEQUENCE { | 
|  | *      policyIdentifier   CertPolicyId, | 
|  | *      policyQualifiers   SEQUENCE SIZE (1..MAX) OF | 
|  | *                              PolicyQualifierInfo OPTIONAL } | 
|  | * | 
|  | * CertPolicyId ::= OBJECT IDENTIFIER | 
|  | */ | 
|  |  | 
|  | if (asn1_get_next(pos, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { | 
|  | asn1_unexpected(&hdr, | 
|  | "X509: Expected SEQUENCE (certificatePolicies)"); | 
|  | return -1; | 
|  | } | 
|  | if (hdr.length > pos + len - hdr.payload) | 
|  | return -1; | 
|  | pos = hdr.payload; | 
|  | end = pos + hdr.length; | 
|  |  | 
|  | wpa_hexdump(MSG_MSGDUMP, "X509: certificatePolicies", pos, end - pos); | 
|  |  | 
|  | while (pos < end) { | 
|  | const u8 *pol_end; | 
|  | struct asn1_oid oid; | 
|  | char buf[80]; | 
|  |  | 
|  | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | 
|  | !asn1_is_sequence(&hdr)) { | 
|  | asn1_unexpected(&hdr, | 
|  | "X509: Expected SEQUENCE (PolicyInformation)"); | 
|  | return -1; | 
|  | } | 
|  | if (hdr.length > end - hdr.payload) | 
|  | return -1; | 
|  | pos = hdr.payload; | 
|  | pol_end = pos + hdr.length; | 
|  | wpa_hexdump(MSG_MSGDUMP, "X509: PolicyInformation", | 
|  | pos, pol_end - pos); | 
|  |  | 
|  | if (asn1_get_oid(pos, pol_end - pos, &oid, &pos)) | 
|  | return -1; | 
|  | if (x509_id_cert_policy_any_oid(&oid)) { | 
|  | os_strlcpy(buf, "anyPolicy-STRICT", sizeof(buf)); | 
|  | cert->certificate_policy |= | 
|  | X509_EXT_CERT_POLICY_ANY; | 
|  | } else if (x509_id_wfa_tod_strict_oid(&oid)) { | 
|  | os_strlcpy(buf, "TOD-STRICT", sizeof(buf)); | 
|  | cert->certificate_policy |= | 
|  | X509_EXT_CERT_POLICY_TOD_STRICT; | 
|  | } else if (x509_id_wfa_tod_tofu_oid(&oid)) { | 
|  | os_strlcpy(buf, "TOD-TOFU", sizeof(buf)); | 
|  | cert->certificate_policy |= | 
|  | X509_EXT_CERT_POLICY_TOD_TOFU; | 
|  | } else { | 
|  | asn1_oid_to_str(&oid, buf, sizeof(buf)); | 
|  | } | 
|  | wpa_printf(MSG_DEBUG, "policyIdentifier: %s", buf); | 
|  |  | 
|  | pos = pol_end; | 
|  | } | 
|  |  | 
|  | cert->extensions_present |= X509_EXT_CERTIFICATE_POLICY; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_id_pkix_oid(struct asn1_oid *oid) | 
|  | { | 
|  | return oid->len >= 7 && | 
|  | oid->oid[0] == 1 /* iso */ && | 
|  | oid->oid[1] == 3 /* identified-organization */ && | 
|  | oid->oid[2] == 6 /* dod */ && | 
|  | oid->oid[3] == 1 /* internet */ && | 
|  | oid->oid[4] == 5 /* security */ && | 
|  | oid->oid[5] == 5 /* mechanisms */ && | 
|  | oid->oid[6] == 7 /* id-pkix */; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_id_kp_oid(struct asn1_oid *oid) | 
|  | { | 
|  | /* id-kp */ | 
|  | return oid->len >= 8 && | 
|  | x509_id_pkix_oid(oid) && | 
|  | oid->oid[7] == 3 /* id-kp */; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_id_kp_server_auth_oid(struct asn1_oid *oid) | 
|  | { | 
|  | /* id-kp */ | 
|  | return oid->len == 9 && | 
|  | x509_id_kp_oid(oid) && | 
|  | oid->oid[8] == 1 /* id-kp-serverAuth */; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_id_kp_client_auth_oid(struct asn1_oid *oid) | 
|  | { | 
|  | /* id-kp */ | 
|  | return oid->len == 9 && | 
|  | x509_id_kp_oid(oid) && | 
|  | oid->oid[8] == 2 /* id-kp-clientAuth */; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_id_kp_ocsp_oid(struct asn1_oid *oid) | 
|  | { | 
|  | /* id-kp */ | 
|  | return oid->len == 9 && | 
|  | x509_id_kp_oid(oid) && | 
|  | oid->oid[8] == 9 /* id-kp-OCSPSigning */; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_parse_ext_ext_key_usage(struct x509_certificate *cert, | 
|  | const u8 *pos, size_t len) | 
|  | { | 
|  | struct asn1_hdr hdr; | 
|  | const u8 *end; | 
|  | struct asn1_oid oid; | 
|  |  | 
|  | /* | 
|  | * ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId | 
|  | * | 
|  | * KeyPurposeId ::= OBJECT IDENTIFIER | 
|  | */ | 
|  |  | 
|  | if (asn1_get_next(pos, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { | 
|  | asn1_unexpected(&hdr, | 
|  | "X509: Expected SEQUENCE (ExtKeyUsageSyntax)"); | 
|  | return -1; | 
|  | } | 
|  | if (hdr.length > pos + len - hdr.payload) | 
|  | return -1; | 
|  | pos = hdr.payload; | 
|  | end = pos + hdr.length; | 
|  |  | 
|  | wpa_hexdump(MSG_MSGDUMP, "X509: ExtKeyUsageSyntax", pos, end - pos); | 
|  |  | 
|  | while (pos < end) { | 
|  | char buf[80]; | 
|  |  | 
|  | if (asn1_get_oid(pos, end - pos, &oid, &pos)) | 
|  | return -1; | 
|  | if (x509_any_ext_key_usage_oid(&oid)) { | 
|  | os_strlcpy(buf, "anyExtendedKeyUsage", sizeof(buf)); | 
|  | cert->ext_key_usage |= X509_EXT_KEY_USAGE_ANY; | 
|  | } else if (x509_id_kp_server_auth_oid(&oid)) { | 
|  | os_strlcpy(buf, "id-kp-serverAuth", sizeof(buf)); | 
|  | cert->ext_key_usage |= X509_EXT_KEY_USAGE_SERVER_AUTH; | 
|  | } else if (x509_id_kp_client_auth_oid(&oid)) { | 
|  | os_strlcpy(buf, "id-kp-clientAuth", sizeof(buf)); | 
|  | cert->ext_key_usage |= X509_EXT_KEY_USAGE_CLIENT_AUTH; | 
|  | } else if (x509_id_kp_ocsp_oid(&oid)) { | 
|  | os_strlcpy(buf, "id-kp-OCSPSigning", sizeof(buf)); | 
|  | cert->ext_key_usage |= X509_EXT_KEY_USAGE_OCSP; | 
|  | } else { | 
|  | asn1_oid_to_str(&oid, buf, sizeof(buf)); | 
|  | } | 
|  | wpa_printf(MSG_DEBUG, "ExtKeyUsage KeyPurposeId: %s", buf); | 
|  | } | 
|  |  | 
|  | cert->extensions_present |= X509_EXT_EXT_KEY_USAGE; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_parse_extension_data(struct x509_certificate *cert, | 
|  | struct asn1_oid *oid, | 
|  | const u8 *pos, size_t len) | 
|  | { | 
|  | if (!x509_id_ce_oid(oid)) | 
|  | return 1; | 
|  |  | 
|  | /* TODO: add other extensions required by RFC 3280, Ch 4.2: | 
|  | * name constraints (section 4.2.1.11) | 
|  | * policy constraints (section 4.2.1.12) | 
|  | * inhibit any-policy (section 4.2.1.15) | 
|  | */ | 
|  | switch (oid->oid[3]) { | 
|  | case 15: /* id-ce-keyUsage */ | 
|  | return x509_parse_ext_key_usage(cert, pos, len); | 
|  | case 17: /* id-ce-subjectAltName */ | 
|  | return x509_parse_ext_subject_alt_name(cert, pos, len); | 
|  | case 18: /* id-ce-issuerAltName */ | 
|  | return x509_parse_ext_issuer_alt_name(cert, pos, len); | 
|  | case 19: /* id-ce-basicConstraints */ | 
|  | return x509_parse_ext_basic_constraints(cert, pos, len); | 
|  | case 32: /* id-ce-certificatePolicies */ | 
|  | return x509_parse_ext_certificate_policies(cert, pos, len); | 
|  | case 37: /* id-ce-extKeyUsage */ | 
|  | return x509_parse_ext_ext_key_usage(cert, pos, len); | 
|  | default: | 
|  | return 1; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_parse_extension(struct x509_certificate *cert, | 
|  | const u8 *pos, size_t len, const u8 **next) | 
|  | { | 
|  | const u8 *end; | 
|  | struct asn1_hdr hdr; | 
|  | struct asn1_oid oid; | 
|  | int critical_ext = 0, res; | 
|  | char buf[80]; | 
|  |  | 
|  | /* | 
|  | * Extension  ::=  SEQUENCE  { | 
|  | *     extnID      OBJECT IDENTIFIER, | 
|  | *     critical    BOOLEAN DEFAULT FALSE, | 
|  | *     extnValue   OCTET STRING | 
|  | * } | 
|  | */ | 
|  |  | 
|  | if (asn1_get_next(pos, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { | 
|  | asn1_unexpected(&hdr, "X509: Expected SEQUENCE in Extensions"); | 
|  | return -1; | 
|  | } | 
|  | pos = hdr.payload; | 
|  | *next = end = pos + hdr.length; | 
|  |  | 
|  | if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0) { | 
|  | wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 data for " | 
|  | "Extension (expected OID)"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | 
|  | (!asn1_is_boolean(&hdr) && !asn1_is_octetstring(&hdr))) { | 
|  | asn1_unexpected(&hdr, | 
|  | "X509: Expected BOOLEAN or OCTETSTRING in Extensions"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (hdr.tag == ASN1_TAG_BOOLEAN) { | 
|  | critical_ext = hdr.payload[0]; | 
|  | pos = hdr.payload; | 
|  | /* | 
|  | * Number of CA certificates seem to be using Private class in | 
|  | * one of the X.509v3 extensions, so let's accept that instead | 
|  | * of rejecting the certificate. asn1_is_octetstring() covers | 
|  | * the more common case. | 
|  | */ | 
|  | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | 
|  | (!asn1_is_octetstring(&hdr) && | 
|  | !(hdr.class == ASN1_CLASS_PRIVATE && | 
|  | hdr.tag == ASN1_TAG_OCTETSTRING))) { | 
|  | asn1_unexpected(&hdr, | 
|  | "X509: Expected OCTETSTRING in Extensions"); | 
|  | return -1; | 
|  | } | 
|  | } | 
|  |  | 
|  | asn1_oid_to_str(&oid, buf, sizeof(buf)); | 
|  | wpa_printf(MSG_DEBUG, "X509: Extension: extnID=%s critical=%d", | 
|  | buf, critical_ext); | 
|  | wpa_hexdump(MSG_MSGDUMP, "X509: extnValue", hdr.payload, hdr.length); | 
|  |  | 
|  | res = x509_parse_extension_data(cert, &oid, hdr.payload, hdr.length); | 
|  | if (res < 0) | 
|  | return res; | 
|  | if (res == 1 && critical_ext) { | 
|  | wpa_printf(MSG_INFO, "X509: Unknown critical extension %s", | 
|  | buf); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_parse_extensions(struct x509_certificate *cert, | 
|  | const u8 *pos, size_t len) | 
|  | { | 
|  | const u8 *end; | 
|  | struct asn1_hdr hdr; | 
|  |  | 
|  | /* Extensions  ::=  SEQUENCE SIZE (1..MAX) OF Extension */ | 
|  |  | 
|  | if (asn1_get_next(pos, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { | 
|  | asn1_unexpected(&hdr, "X509: Expected SEQUENCE for Extensions"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | pos = hdr.payload; | 
|  | end = pos + hdr.length; | 
|  |  | 
|  | while (pos < end) { | 
|  | if (x509_parse_extension(cert, pos, end - pos, &pos) | 
|  | < 0) | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_parse_tbs_certificate(const u8 *buf, size_t len, | 
|  | struct x509_certificate *cert, | 
|  | const u8 **next) | 
|  | { | 
|  | struct asn1_hdr hdr; | 
|  | const u8 *pos, *end; | 
|  | size_t left; | 
|  | char sbuf[128]; | 
|  | unsigned long value; | 
|  | const u8 *subject_dn; | 
|  |  | 
|  | /* tbsCertificate TBSCertificate ::= SEQUENCE */ | 
|  | if (asn1_get_next(buf, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { | 
|  | asn1_unexpected(&hdr, | 
|  | "X509: tbsCertificate did not start with a valid SEQUENCE"); | 
|  | return -1; | 
|  | } | 
|  | pos = hdr.payload; | 
|  | end = *next = pos + hdr.length; | 
|  |  | 
|  | /* | 
|  | * version [0]  EXPLICIT Version DEFAULT v1 | 
|  | * Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  } | 
|  | */ | 
|  | if (asn1_get_next(pos, end - pos, &hdr) < 0) | 
|  | return -1; | 
|  | pos = hdr.payload; | 
|  |  | 
|  | if (asn1_is_cs_tag(&hdr, 0) && hdr.constructed) { | 
|  | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | 
|  | !asn1_is_integer(&hdr)) { | 
|  | asn1_unexpected(&hdr, | 
|  | "X509: No INTEGER tag found for version field"); | 
|  | return -1; | 
|  | } | 
|  | if (hdr.length != 1) { | 
|  | wpa_printf(MSG_DEBUG, "X509: Unexpected version field " | 
|  | "length %u (expected 1)", hdr.length); | 
|  | return -1; | 
|  | } | 
|  | pos = hdr.payload; | 
|  | left = hdr.length; | 
|  | value = 0; | 
|  | while (left) { | 
|  | value <<= 8; | 
|  | value |= *pos++; | 
|  | left--; | 
|  | } | 
|  |  | 
|  | cert->version = value; | 
|  | if (cert->version != X509_CERT_V1 && | 
|  | cert->version != X509_CERT_V2 && | 
|  | cert->version != X509_CERT_V3) { | 
|  | wpa_printf(MSG_DEBUG, "X509: Unsupported version %d", | 
|  | cert->version + 1); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (asn1_get_next(pos, end - pos, &hdr) < 0) | 
|  | return -1; | 
|  | } else | 
|  | cert->version = X509_CERT_V1; | 
|  | wpa_printf(MSG_MSGDUMP, "X509: Version X.509v%d", cert->version + 1); | 
|  |  | 
|  | /* serialNumber CertificateSerialNumber ::= INTEGER */ | 
|  | if (!asn1_is_integer(&hdr) || | 
|  | hdr.length < 1 || hdr.length > X509_MAX_SERIAL_NUM_LEN) { | 
|  | asn1_unexpected(&hdr, | 
|  | "X509: No INTEGER tag found for serialNumber"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | pos = hdr.payload + hdr.length; | 
|  | while (hdr.length > 0 && hdr.payload[0] == 0) { | 
|  | hdr.payload++; | 
|  | hdr.length--; | 
|  | } | 
|  | os_memcpy(cert->serial_number, hdr.payload, hdr.length); | 
|  | cert->serial_number_len = hdr.length; | 
|  | wpa_hexdump(MSG_MSGDUMP, "X509: serialNumber", cert->serial_number, | 
|  | cert->serial_number_len); | 
|  |  | 
|  | /* signature AlgorithmIdentifier */ | 
|  | if (x509_parse_algorithm_identifier(pos, end - pos, &cert->signature, | 
|  | &pos)) | 
|  | return -1; | 
|  |  | 
|  | /* issuer Name */ | 
|  | if (x509_parse_name(pos, end - pos, &cert->issuer, &pos)) | 
|  | return -1; | 
|  | x509_name_string(&cert->issuer, sbuf, sizeof(sbuf)); | 
|  | wpa_printf(MSG_MSGDUMP, "X509: issuer %s", sbuf); | 
|  |  | 
|  | /* validity Validity */ | 
|  | if (x509_parse_validity(pos, end - pos, cert, &pos)) | 
|  | return -1; | 
|  |  | 
|  | /* subject Name */ | 
|  | subject_dn = pos; | 
|  | if (x509_parse_name(pos, end - pos, &cert->subject, &pos)) | 
|  | return -1; | 
|  | cert->subject_dn = os_malloc(pos - subject_dn); | 
|  | if (!cert->subject_dn) | 
|  | return -1; | 
|  | cert->subject_dn_len = pos - subject_dn; | 
|  | os_memcpy(cert->subject_dn, subject_dn, cert->subject_dn_len); | 
|  | x509_name_string(&cert->subject, sbuf, sizeof(sbuf)); | 
|  | wpa_printf(MSG_MSGDUMP, "X509: subject %s", sbuf); | 
|  |  | 
|  | /* subjectPublicKeyInfo SubjectPublicKeyInfo */ | 
|  | if (x509_parse_public_key(pos, end - pos, cert, &pos)) | 
|  | return -1; | 
|  |  | 
|  | if (pos == end) | 
|  | return 0; | 
|  |  | 
|  | if (cert->version == X509_CERT_V1) | 
|  | return 0; | 
|  |  | 
|  | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | 
|  | hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { | 
|  | asn1_unexpected(&hdr, | 
|  | "X509: Expected Context-Specific tag to parse optional tbsCertificate field(s)"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (hdr.tag == 1) { | 
|  | /* issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL */ | 
|  | wpa_printf(MSG_DEBUG, "X509: issuerUniqueID"); | 
|  | /* TODO: parse UniqueIdentifier ::= BIT STRING */ | 
|  |  | 
|  | pos = hdr.payload + hdr.length; | 
|  | if (pos == end) | 
|  | return 0; | 
|  |  | 
|  | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | 
|  | hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { | 
|  | asn1_unexpected(&hdr, | 
|  | "X509: Expected Context-Specific tag to parse optional tbsCertificate field(s)"); | 
|  | return -1; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (hdr.tag == 2) { | 
|  | /* subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL */ | 
|  | wpa_printf(MSG_DEBUG, "X509: subjectUniqueID"); | 
|  | /* TODO: parse UniqueIdentifier ::= BIT STRING */ | 
|  |  | 
|  | pos = hdr.payload + hdr.length; | 
|  | if (pos == end) | 
|  | return 0; | 
|  |  | 
|  | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | 
|  | hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { | 
|  | asn1_unexpected(&hdr, | 
|  | "X509: Expected Context-Specific tag to parse optional tbsCertificate field(s)"); | 
|  | return -1; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (hdr.tag != 3) { | 
|  | wpa_printf(MSG_DEBUG, | 
|  | "X509: Ignored unexpected Context-Specific constructed %d tag %d in optional tbsCertificate fields", | 
|  | hdr.constructed, hdr.tag); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* extensions      [3]  EXPLICIT Extensions OPTIONAL */ | 
|  |  | 
|  | if (cert->version != X509_CERT_V3) { | 
|  | wpa_printf(MSG_DEBUG, "X509: X.509%d certificate and " | 
|  | "Extensions data which are only allowed for " | 
|  | "version 3", cert->version + 1); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (x509_parse_extensions(cert, hdr.payload, hdr.length) < 0) | 
|  | return -1; | 
|  |  | 
|  | pos = hdr.payload + hdr.length; | 
|  | if (pos < end) { | 
|  | wpa_hexdump(MSG_DEBUG, | 
|  | "X509: Ignored extra tbsCertificate data", | 
|  | pos, end - pos); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_rsadsi_oid(struct asn1_oid *oid) | 
|  | { | 
|  | return oid->len >= 4 && | 
|  | oid->oid[0] == 1 /* iso */ && | 
|  | oid->oid[1] == 2 /* member-body */ && | 
|  | oid->oid[2] == 840 /* us */ && | 
|  | oid->oid[3] == 113549 /* rsadsi */; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_pkcs_oid(struct asn1_oid *oid) | 
|  | { | 
|  | return oid->len >= 5 && | 
|  | x509_rsadsi_oid(oid) && | 
|  | oid->oid[4] == 1 /* pkcs */; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_digest_oid(struct asn1_oid *oid) | 
|  | { | 
|  | return oid->len >= 5 && | 
|  | x509_rsadsi_oid(oid) && | 
|  | oid->oid[4] == 2 /* digestAlgorithm */; | 
|  | } | 
|  |  | 
|  |  | 
|  | int x509_sha1_oid(struct asn1_oid *oid) | 
|  | { | 
|  | return oid->len == 6 && | 
|  | oid->oid[0] == 1 /* iso */ && | 
|  | oid->oid[1] == 3 /* identified-organization */ && | 
|  | oid->oid[2] == 14 /* oiw */ && | 
|  | oid->oid[3] == 3 /* secsig */ && | 
|  | oid->oid[4] == 2 /* algorithms */ && | 
|  | oid->oid[5] == 26 /* id-sha1 */; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_sha2_oid(struct asn1_oid *oid) | 
|  | { | 
|  | return oid->len == 9 && | 
|  | oid->oid[0] == 2 /* joint-iso-itu-t */ && | 
|  | oid->oid[1] == 16 /* country */ && | 
|  | oid->oid[2] == 840 /* us */ && | 
|  | oid->oid[3] == 1 /* organization */ && | 
|  | oid->oid[4] == 101 /* gov */ && | 
|  | oid->oid[5] == 3 /* csor */ && | 
|  | oid->oid[6] == 4 /* nistAlgorithm */ && | 
|  | oid->oid[7] == 2 /* hashAlgs */; | 
|  | } | 
|  |  | 
|  |  | 
|  | int x509_sha256_oid(struct asn1_oid *oid) | 
|  | { | 
|  | return x509_sha2_oid(oid) && | 
|  | oid->oid[8] == 1 /* sha256 */; | 
|  | } | 
|  |  | 
|  |  | 
|  | int x509_sha384_oid(struct asn1_oid *oid) | 
|  | { | 
|  | return x509_sha2_oid(oid) && | 
|  | oid->oid[8] == 2 /* sha384 */; | 
|  | } | 
|  |  | 
|  |  | 
|  | int x509_sha512_oid(struct asn1_oid *oid) | 
|  | { | 
|  | return x509_sha2_oid(oid) && | 
|  | oid->oid[8] == 3 /* sha512 */; | 
|  | } | 
|  |  | 
|  |  | 
|  | /** | 
|  | * x509_certificate_parse - Parse a X.509 certificate in DER format | 
|  | * @buf: Pointer to the X.509 certificate in DER format | 
|  | * @len: Buffer length | 
|  | * Returns: Pointer to the parsed certificate or %NULL on failure | 
|  | * | 
|  | * Caller is responsible for freeing the returned certificate by calling | 
|  | * x509_certificate_free(). | 
|  | */ | 
|  | struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len) | 
|  | { | 
|  | struct asn1_hdr hdr; | 
|  | const u8 *pos, *end, *hash_start; | 
|  | struct x509_certificate *cert; | 
|  |  | 
|  | cert = os_zalloc(sizeof(*cert) + len); | 
|  | if (cert == NULL) | 
|  | return NULL; | 
|  | os_memcpy(cert + 1, buf, len); | 
|  | cert->cert_start = (u8 *) (cert + 1); | 
|  | cert->cert_len = len; | 
|  |  | 
|  | pos = buf; | 
|  | end = buf + len; | 
|  |  | 
|  | /* RFC 3280 - X.509 v3 certificate / ASN.1 DER */ | 
|  |  | 
|  | /* Certificate ::= SEQUENCE */ | 
|  | if (asn1_get_next(pos, len, &hdr) < 0 || !asn1_is_sequence(&hdr)) { | 
|  | asn1_unexpected(&hdr, | 
|  | "X509: Certificate did not start with a valid SEQUENCE"); | 
|  | x509_certificate_free(cert); | 
|  | return NULL; | 
|  | } | 
|  | pos = hdr.payload; | 
|  |  | 
|  | if (hdr.length > end - pos) { | 
|  | x509_certificate_free(cert); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | if (hdr.length < end - pos) { | 
|  | wpa_hexdump(MSG_MSGDUMP, "X509: Ignoring extra data after DER " | 
|  | "encoded certificate", | 
|  | pos + hdr.length, end - (pos + hdr.length)); | 
|  | end = pos + hdr.length; | 
|  | } | 
|  |  | 
|  | hash_start = pos; | 
|  | cert->tbs_cert_start = cert->cert_start + (hash_start - buf); | 
|  | if (x509_parse_tbs_certificate(pos, end - pos, cert, &pos)) { | 
|  | x509_certificate_free(cert); | 
|  | return NULL; | 
|  | } | 
|  | cert->tbs_cert_len = pos - hash_start; | 
|  |  | 
|  | /* signatureAlgorithm AlgorithmIdentifier */ | 
|  | if (x509_parse_algorithm_identifier(pos, end - pos, | 
|  | &cert->signature_alg, &pos)) { | 
|  | x509_certificate_free(cert); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /* signatureValue BIT STRING */ | 
|  | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | 
|  | !asn1_is_bitstring(&hdr)) { | 
|  | asn1_unexpected(&hdr, | 
|  | "X509: Expected BITSTRING (signatureValue)"); | 
|  | x509_certificate_free(cert); | 
|  | return NULL; | 
|  | } | 
|  | if (hdr.length < 1) { | 
|  | x509_certificate_free(cert); | 
|  | return NULL; | 
|  | } | 
|  | pos = hdr.payload; | 
|  | if (*pos) { | 
|  | wpa_printf(MSG_DEBUG, | 
|  | "X509: BITSTRING (signatureValue) - %d unused bits", | 
|  | *pos); | 
|  | /* PKCS #1 v1.5 10.2.1: | 
|  | * It is an error if the length in bits of the signature S is | 
|  | * not a multiple of eight. | 
|  | */ | 
|  | x509_certificate_free(cert); | 
|  | return NULL; | 
|  | } | 
|  | os_free(cert->sign_value); | 
|  | cert->sign_value = os_memdup(pos + 1, hdr.length - 1); | 
|  | if (cert->sign_value == NULL) { | 
|  | wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for " | 
|  | "signatureValue"); | 
|  | x509_certificate_free(cert); | 
|  | return NULL; | 
|  | } | 
|  | cert->sign_value_len = hdr.length - 1; | 
|  | wpa_hexdump(MSG_MSGDUMP, "X509: signature", | 
|  | cert->sign_value, cert->sign_value_len); | 
|  |  | 
|  | return cert; | 
|  | } | 
|  |  | 
|  |  | 
|  | /** | 
|  | * x509_certificate_check_signature - Verify certificate signature | 
|  | * @issuer: Issuer certificate | 
|  | * @cert: Certificate to be verified | 
|  | * Returns: 0 if cert has a valid signature that was signed by the issuer, | 
|  | * -1 if not | 
|  | */ | 
|  | int x509_certificate_check_signature(struct x509_certificate *issuer, | 
|  | struct x509_certificate *cert) | 
|  | { | 
|  | return x509_check_signature(issuer, &cert->signature, | 
|  | cert->sign_value, cert->sign_value_len, | 
|  | cert->tbs_cert_start, cert->tbs_cert_len); | 
|  | } | 
|  |  | 
|  |  | 
|  | int x509_check_signature(struct x509_certificate *issuer, | 
|  | struct x509_algorithm_identifier *signature, | 
|  | const u8 *sign_value, size_t sign_value_len, | 
|  | const u8 *signed_data, size_t signed_data_len) | 
|  | { | 
|  | struct crypto_public_key *pk; | 
|  | u8 *data; | 
|  | const u8 *pos, *end, *next, *da_end; | 
|  | size_t data_len; | 
|  | struct asn1_hdr hdr; | 
|  | struct asn1_oid oid; | 
|  | u8 hash[64]; | 
|  | size_t hash_len; | 
|  | const u8 *addr[1] = { signed_data }; | 
|  | size_t len[1] = { signed_data_len }; | 
|  |  | 
|  | if (!x509_pkcs_oid(&signature->oid) || | 
|  | signature->oid.len != 7 || | 
|  | signature->oid.oid[5] != 1 /* pkcs-1 */) { | 
|  | wpa_printf(MSG_DEBUG, "X509: Unrecognized signature " | 
|  | "algorithm"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | pk = crypto_public_key_import(issuer->public_key, | 
|  | issuer->public_key_len); | 
|  | if (pk == NULL) | 
|  | return -1; | 
|  |  | 
|  | data_len = sign_value_len; | 
|  | data = os_malloc(data_len); | 
|  | if (data == NULL) { | 
|  | crypto_public_key_free(pk); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (crypto_public_key_decrypt_pkcs1(pk, sign_value, | 
|  | sign_value_len, data, | 
|  | &data_len) < 0) { | 
|  | wpa_printf(MSG_DEBUG, "X509: Failed to decrypt signature"); | 
|  | crypto_public_key_free(pk); | 
|  | os_free(data); | 
|  | return -1; | 
|  | } | 
|  | crypto_public_key_free(pk); | 
|  |  | 
|  | wpa_hexdump(MSG_MSGDUMP, "X509: Signature data D", data, data_len); | 
|  |  | 
|  | /* | 
|  | * PKCS #1 v1.5, 10.1.2: | 
|  | * | 
|  | * DigestInfo ::= SEQUENCE { | 
|  | *     digestAlgorithm DigestAlgorithmIdentifier, | 
|  | *     digest Digest | 
|  | * } | 
|  | * | 
|  | * DigestAlgorithmIdentifier ::= AlgorithmIdentifier | 
|  | * | 
|  | * Digest ::= OCTET STRING | 
|  | * | 
|  | */ | 
|  | if (asn1_get_next(data, data_len, &hdr) < 0 || | 
|  | !asn1_is_sequence(&hdr)) { | 
|  | asn1_unexpected(&hdr, "X509: Expected SEQUENCE (DigestInfo)"); | 
|  | os_free(data); | 
|  | return -1; | 
|  | } | 
|  | wpa_hexdump(MSG_MSGDUMP, "X509: DigestInfo", hdr.payload, hdr.length); | 
|  |  | 
|  | pos = hdr.payload; | 
|  | end = pos + hdr.length; | 
|  |  | 
|  | /* | 
|  | * X.509: | 
|  | * AlgorithmIdentifier ::= SEQUENCE { | 
|  | *     algorithm            OBJECT IDENTIFIER, | 
|  | *     parameters           ANY DEFINED BY algorithm OPTIONAL | 
|  | * } | 
|  | */ | 
|  |  | 
|  | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | 
|  | !asn1_is_sequence(&hdr)) { | 
|  | asn1_unexpected(&hdr, | 
|  | "X509: Expected SEQUENCE (AlgorithmIdentifier)"); | 
|  | os_free(data); | 
|  | return -1; | 
|  | } | 
|  | wpa_hexdump(MSG_MSGDUMP, "X509: DigestAlgorithmIdentifier", | 
|  | hdr.payload, hdr.length); | 
|  | da_end = hdr.payload + hdr.length; | 
|  |  | 
|  | if (asn1_get_oid(hdr.payload, hdr.length, &oid, &next)) { | 
|  | wpa_printf(MSG_DEBUG, "X509: Failed to parse digestAlgorithm"); | 
|  | os_free(data); | 
|  | return -1; | 
|  | } | 
|  | wpa_hexdump(MSG_MSGDUMP, "X509: Digest algorithm parameters", | 
|  | next, da_end - next); | 
|  |  | 
|  | /* | 
|  | * RFC 5754: The correct encoding for the SHA2 algorithms would be to | 
|  | * omit the parameters, but there are implementation that encode these | 
|  | * as a NULL element. Allow these two cases and reject anything else. | 
|  | */ | 
|  | if (da_end > next && | 
|  | (asn1_get_next(next, da_end - next, &hdr) < 0 || | 
|  | !asn1_is_null(&hdr) || | 
|  | hdr.payload + hdr.length != da_end)) { | 
|  | wpa_printf(MSG_DEBUG, | 
|  | "X509: Unexpected digest algorithm parameters"); | 
|  | os_free(data); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (x509_sha1_oid(&oid)) { | 
|  | if (signature->oid.oid[6] != 5 /* sha-1WithRSAEncryption */) { | 
|  | wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA1 " | 
|  | "does not match with certificate " | 
|  | "signatureAlgorithm (%lu)", | 
|  | signature->oid.oid[6]); | 
|  | os_free(data); | 
|  | return -1; | 
|  | } | 
|  | goto skip_digest_oid; | 
|  | } | 
|  |  | 
|  | if (x509_sha256_oid(&oid)) { | 
|  | if (signature->oid.oid[6] != | 
|  | 11 /* sha2561WithRSAEncryption */) { | 
|  | wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA256 " | 
|  | "does not match with certificate " | 
|  | "signatureAlgorithm (%lu)", | 
|  | signature->oid.oid[6]); | 
|  | os_free(data); | 
|  | return -1; | 
|  | } | 
|  | goto skip_digest_oid; | 
|  | } | 
|  |  | 
|  | if (x509_sha384_oid(&oid)) { | 
|  | if (signature->oid.oid[6] != 12 /* sha384WithRSAEncryption */) { | 
|  | wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA384 " | 
|  | "does not match with certificate " | 
|  | "signatureAlgorithm (%lu)", | 
|  | signature->oid.oid[6]); | 
|  | os_free(data); | 
|  | return -1; | 
|  | } | 
|  | goto skip_digest_oid; | 
|  | } | 
|  |  | 
|  | if (x509_sha512_oid(&oid)) { | 
|  | if (signature->oid.oid[6] != 13 /* sha512WithRSAEncryption */) { | 
|  | wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA512 " | 
|  | "does not match with certificate " | 
|  | "signatureAlgorithm (%lu)", | 
|  | signature->oid.oid[6]); | 
|  | os_free(data); | 
|  | return -1; | 
|  | } | 
|  | goto skip_digest_oid; | 
|  | } | 
|  |  | 
|  | if (!x509_digest_oid(&oid)) { | 
|  | wpa_printf(MSG_DEBUG, "X509: Unrecognized digestAlgorithm"); | 
|  | os_free(data); | 
|  | return -1; | 
|  | } | 
|  | switch (oid.oid[5]) { | 
|  | case 5: /* md5 */ | 
|  | if (signature->oid.oid[6] != 4 /* md5WithRSAEncryption */) { | 
|  | wpa_printf(MSG_DEBUG, "X509: digestAlgorithm MD5 does " | 
|  | "not match with certificate " | 
|  | "signatureAlgorithm (%lu)", | 
|  | signature->oid.oid[6]); | 
|  | os_free(data); | 
|  | return -1; | 
|  | } | 
|  | break; | 
|  | case 2: /* md2 */ | 
|  | case 4: /* md4 */ | 
|  | default: | 
|  | wpa_printf(MSG_DEBUG, "X509: Unsupported digestAlgorithm " | 
|  | "(%lu)", oid.oid[5]); | 
|  | os_free(data); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | skip_digest_oid: | 
|  | /* Digest ::= OCTET STRING */ | 
|  | pos = da_end; | 
|  |  | 
|  | if (asn1_get_next(pos, end - pos, &hdr) < 0 || | 
|  | !asn1_is_octetstring(&hdr)) { | 
|  | asn1_unexpected(&hdr, "X509: Expected OCTETSTRING (Digest)"); | 
|  | os_free(data); | 
|  | return -1; | 
|  | } | 
|  | wpa_hexdump(MSG_MSGDUMP, "X509: Decrypted Digest", | 
|  | hdr.payload, hdr.length); | 
|  |  | 
|  | switch (signature->oid.oid[6]) { | 
|  | case 4: /* md5WithRSAEncryption */ | 
|  | md5_vector(1, addr, len, hash); | 
|  | hash_len = 16; | 
|  | wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (MD5)", | 
|  | hash, hash_len); | 
|  | break; | 
|  | case 5: /* sha-1WithRSAEncryption */ | 
|  | sha1_vector(1, addr, len, hash); | 
|  | hash_len = 20; | 
|  | wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA1)", | 
|  | hash, hash_len); | 
|  | break; | 
|  | case 11: /* sha256WithRSAEncryption */ | 
|  | sha256_vector(1, addr, len, hash); | 
|  | hash_len = 32; | 
|  | wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA256)", | 
|  | hash, hash_len); | 
|  | break; | 
|  | case 12: /* sha384WithRSAEncryption */ | 
|  | sha384_vector(1, addr, len, hash); | 
|  | hash_len = 48; | 
|  | wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA384)", | 
|  | hash, hash_len); | 
|  | break; | 
|  | case 13: /* sha512WithRSAEncryption */ | 
|  | sha512_vector(1, addr, len, hash); | 
|  | hash_len = 64; | 
|  | wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA512)", | 
|  | hash, hash_len); | 
|  | break; | 
|  | case 2: /* md2WithRSAEncryption */ | 
|  | default: | 
|  | wpa_printf(MSG_INFO, "X509: Unsupported certificate signature " | 
|  | "algorithm (%lu)", signature->oid.oid[6]); | 
|  | os_free(data); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (hdr.length != hash_len || | 
|  | os_memcmp_const(hdr.payload, hash, hdr.length) != 0) { | 
|  | wpa_printf(MSG_INFO, "X509: Certificate Digest does not match " | 
|  | "with calculated tbsCertificate hash"); | 
|  | os_free(data); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (hdr.payload + hdr.length < data + data_len) { | 
|  | wpa_hexdump(MSG_INFO, | 
|  | "X509: Extra data after certificate signature hash", | 
|  | hdr.payload + hdr.length, | 
|  | data + data_len - hdr.payload - hdr.length); | 
|  | os_free(data); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | os_free(data); | 
|  |  | 
|  | wpa_printf(MSG_DEBUG, "X509: Certificate Digest matches with " | 
|  | "calculated tbsCertificate hash"); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int x509_valid_issuer(const struct x509_certificate *cert) | 
|  | { | 
|  | if ((cert->extensions_present & X509_EXT_BASIC_CONSTRAINTS) && | 
|  | !cert->ca) { | 
|  | wpa_printf(MSG_DEBUG, "X509: Non-CA certificate used as an " | 
|  | "issuer"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (cert->version == X509_CERT_V3 && | 
|  | !(cert->extensions_present & X509_EXT_BASIC_CONSTRAINTS)) { | 
|  | wpa_printf(MSG_DEBUG, "X509: v3 CA certificate did not " | 
|  | "include BasicConstraints extension"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if ((cert->extensions_present & X509_EXT_KEY_USAGE) && | 
|  | !(cert->key_usage & X509_KEY_USAGE_KEY_CERT_SIGN)) { | 
|  | wpa_printf(MSG_DEBUG, "X509: Issuer certificate did not have " | 
|  | "keyCertSign bit in Key Usage"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | /** | 
|  | * x509_certificate_chain_validate - Validate X.509 certificate chain | 
|  | * @trusted: List of trusted certificates | 
|  | * @chain: Certificate chain to be validated (first chain must be issued by | 
|  | * signed by the second certificate in the chain and so on) | 
|  | * @reason: Buffer for returning failure reason (X509_VALIDATE_*) | 
|  | * Returns: 0 if chain is valid, -1 if not | 
|  | */ | 
|  | int x509_certificate_chain_validate(struct x509_certificate *trusted, | 
|  | struct x509_certificate *chain, | 
|  | int *reason, int disable_time_checks) | 
|  | { | 
|  | long unsigned idx; | 
|  | int chain_trusted = 0; | 
|  | struct x509_certificate *cert, *trust; | 
|  | char buf[128]; | 
|  | struct os_time now; | 
|  |  | 
|  | *reason = X509_VALIDATE_OK; | 
|  |  | 
|  | wpa_printf(MSG_DEBUG, "X509: Validate certificate chain"); | 
|  | os_get_time(&now); | 
|  |  | 
|  | for (cert = chain, idx = 0; cert; cert = cert->next, idx++) { | 
|  | cert->issuer_trusted = 0; | 
|  | x509_name_string(&cert->subject, buf, sizeof(buf)); | 
|  | wpa_printf(MSG_DEBUG, "X509: %lu: %s", idx, buf); | 
|  |  | 
|  | if (chain_trusted) | 
|  | continue; | 
|  |  | 
|  | if (!disable_time_checks && | 
|  | ((unsigned long) now.sec < | 
|  | (unsigned long) cert->not_before || | 
|  | (unsigned long) now.sec > | 
|  | (unsigned long) cert->not_after)) { | 
|  | wpa_printf(MSG_INFO, "X509: Certificate not valid " | 
|  | "(now=%lu not_before=%lu not_after=%lu)", | 
|  | now.sec, cert->not_before, cert->not_after); | 
|  | *reason = X509_VALIDATE_CERTIFICATE_EXPIRED; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (cert->next) { | 
|  | if (x509_name_compare(&cert->issuer, | 
|  | &cert->next->subject) != 0) { | 
|  | wpa_printf(MSG_DEBUG, "X509: Certificate " | 
|  | "chain issuer name mismatch"); | 
|  | x509_name_string(&cert->issuer, buf, | 
|  | sizeof(buf)); | 
|  | wpa_printf(MSG_DEBUG, "X509: cert issuer: %s", | 
|  | buf); | 
|  | x509_name_string(&cert->next->subject, buf, | 
|  | sizeof(buf)); | 
|  | wpa_printf(MSG_DEBUG, "X509: next cert " | 
|  | "subject: %s", buf); | 
|  | *reason = X509_VALIDATE_CERTIFICATE_UNKNOWN; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (x509_valid_issuer(cert->next) < 0) { | 
|  | *reason = X509_VALIDATE_BAD_CERTIFICATE; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if ((cert->next->extensions_present & | 
|  | X509_EXT_PATH_LEN_CONSTRAINT) && | 
|  | idx > cert->next->path_len_constraint) { | 
|  | wpa_printf(MSG_DEBUG, "X509: pathLenConstraint" | 
|  | " not met (idx=%lu issuer " | 
|  | "pathLenConstraint=%lu)", idx, | 
|  | cert->next->path_len_constraint); | 
|  | *reason = X509_VALIDATE_BAD_CERTIFICATE; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (x509_certificate_check_signature(cert->next, cert) | 
|  | < 0) { | 
|  | wpa_printf(MSG_DEBUG, "X509: Invalid " | 
|  | "certificate signature within " | 
|  | "chain"); | 
|  | *reason = X509_VALIDATE_BAD_CERTIFICATE; | 
|  | return -1; | 
|  | } | 
|  | } | 
|  |  | 
|  | for (trust = trusted; trust; trust = trust->next) { | 
|  | if (x509_name_compare(&cert->issuer, &trust->subject) | 
|  | == 0) | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (trust) { | 
|  | wpa_printf(MSG_DEBUG, "X509: Found issuer from the " | 
|  | "list of trusted certificates"); | 
|  | if (x509_valid_issuer(trust) < 0) { | 
|  | *reason = X509_VALIDATE_BAD_CERTIFICATE; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (x509_certificate_check_signature(trust, cert) < 0) | 
|  | { | 
|  | wpa_printf(MSG_DEBUG, "X509: Invalid " | 
|  | "certificate signature"); | 
|  | *reason = X509_VALIDATE_BAD_CERTIFICATE; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | wpa_printf(MSG_DEBUG, "X509: Trusted certificate " | 
|  | "found to complete the chain"); | 
|  | cert->issuer_trusted = 1; | 
|  | chain_trusted = 1; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!chain_trusted) { | 
|  | wpa_printf(MSG_DEBUG, "X509: Did not find any of the issuers " | 
|  | "from the list of trusted certificates"); | 
|  | if (trusted) { | 
|  | *reason = X509_VALIDATE_UNKNOWN_CA; | 
|  | return -1; | 
|  | } | 
|  | wpa_printf(MSG_DEBUG, "X509: Certificate chain validation " | 
|  | "disabled - ignore unknown CA issue"); | 
|  | } | 
|  |  | 
|  | wpa_printf(MSG_DEBUG, "X509: Certificate chain valid"); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  |  | 
|  | /** | 
|  | * x509_certificate_get_subject - Get a certificate based on Subject name | 
|  | * @chain: Certificate chain to search through | 
|  | * @name: Subject name to search for | 
|  | * Returns: Pointer to the certificate with the given Subject name or | 
|  | * %NULL on failure | 
|  | */ | 
|  | struct x509_certificate * | 
|  | x509_certificate_get_subject(struct x509_certificate *chain, | 
|  | struct x509_name *name) | 
|  | { | 
|  | struct x509_certificate *cert; | 
|  |  | 
|  | for (cert = chain; cert; cert = cert->next) { | 
|  | if (x509_name_compare(&cert->subject, name) == 0) | 
|  | return cert; | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  |  | 
|  | /** | 
|  | * x509_certificate_self_signed - Is the certificate self-signed? | 
|  | * @cert: Certificate | 
|  | * Returns: 1 if certificate is self-signed, 0 if not | 
|  | */ | 
|  | int x509_certificate_self_signed(struct x509_certificate *cert) | 
|  | { | 
|  | return x509_name_compare(&cert->issuer, &cert->subject) == 0; | 
|  | } |