1189251Ssam/* 2189251Ssam * X.509v3 certificate parsing and processing (RFC 3280 profile) 3252726Srpaulo * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi> 4189251Ssam * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7189251Ssam */ 8189251Ssam 9189251Ssam#include "includes.h" 10189251Ssam 11189251Ssam#include "common.h" 12214734Srpaulo#include "crypto/crypto.h" 13189251Ssam#include "asn1.h" 14189251Ssam#include "x509v3.h" 15189251Ssam 16189251Ssam 17189251Ssamstatic void x509_free_name(struct x509_name *name) 18189251Ssam{ 19214734Srpaulo size_t i; 20214734Srpaulo 21214734Srpaulo for (i = 0; i < name->num_attr; i++) { 22214734Srpaulo os_free(name->attr[i].value); 23214734Srpaulo name->attr[i].value = NULL; 24214734Srpaulo name->attr[i].type = X509_NAME_ATTR_NOT_USED; 25214734Srpaulo } 26214734Srpaulo name->num_attr = 0; 27189251Ssam os_free(name->email); 28189251Ssam name->email = NULL; 29214734Srpaulo 30214734Srpaulo os_free(name->alt_email); 31214734Srpaulo os_free(name->dns); 32214734Srpaulo os_free(name->uri); 33214734Srpaulo os_free(name->ip); 34214734Srpaulo name->alt_email = name->dns = name->uri = NULL; 35214734Srpaulo name->ip = NULL; 36214734Srpaulo name->ip_len = 0; 37214734Srpaulo os_memset(&name->rid, 0, sizeof(name->rid)); 38189251Ssam} 39189251Ssam 40189251Ssam 41189251Ssam/** 42189251Ssam * x509_certificate_free - Free an X.509 certificate 43189251Ssam * @cert: Certificate to be freed 44189251Ssam */ 45189251Ssamvoid x509_certificate_free(struct x509_certificate *cert) 46189251Ssam{ 47189251Ssam if (cert == NULL) 48189251Ssam return; 49189251Ssam if (cert->next) { 50189251Ssam wpa_printf(MSG_DEBUG, "X509: x509_certificate_free: cer=%p " 51189251Ssam "was still on a list (next=%p)\n", 52189251Ssam cert, cert->next); 53189251Ssam } 54189251Ssam x509_free_name(&cert->issuer); 55189251Ssam x509_free_name(&cert->subject); 56189251Ssam os_free(cert->public_key); 57189251Ssam os_free(cert->sign_value); 58189251Ssam os_free(cert); 59189251Ssam} 60189251Ssam 61189251Ssam 62189251Ssam/** 63189251Ssam * x509_certificate_free - Free an X.509 certificate chain 64189251Ssam * @cert: Pointer to the first certificate in the chain 65189251Ssam */ 66189251Ssamvoid x509_certificate_chain_free(struct x509_certificate *cert) 67189251Ssam{ 68189251Ssam struct x509_certificate *next; 69189251Ssam 70189251Ssam while (cert) { 71189251Ssam next = cert->next; 72189251Ssam cert->next = NULL; 73189251Ssam x509_certificate_free(cert); 74189251Ssam cert = next; 75189251Ssam } 76189251Ssam} 77189251Ssam 78189251Ssam 79189251Ssamstatic int x509_whitespace(char c) 80189251Ssam{ 81189251Ssam return c == ' ' || c == '\t'; 82189251Ssam} 83189251Ssam 84189251Ssam 85189251Ssamstatic void x509_str_strip_whitespace(char *a) 86189251Ssam{ 87189251Ssam char *ipos, *opos; 88189251Ssam int remove_whitespace = 1; 89189251Ssam 90189251Ssam ipos = opos = a; 91189251Ssam 92189251Ssam while (*ipos) { 93189251Ssam if (remove_whitespace && x509_whitespace(*ipos)) 94189251Ssam ipos++; 95189251Ssam else { 96189251Ssam remove_whitespace = x509_whitespace(*ipos); 97189251Ssam *opos++ = *ipos++; 98189251Ssam } 99189251Ssam } 100189251Ssam 101189251Ssam *opos-- = '\0'; 102189251Ssam if (opos > a && x509_whitespace(*opos)) 103189251Ssam *opos = '\0'; 104189251Ssam} 105189251Ssam 106189251Ssam 107189251Ssamstatic int x509_str_compare(const char *a, const char *b) 108189251Ssam{ 109189251Ssam char *aa, *bb; 110189251Ssam int ret; 111189251Ssam 112189251Ssam if (!a && b) 113189251Ssam return -1; 114189251Ssam if (a && !b) 115189251Ssam return 1; 116189251Ssam if (!a && !b) 117189251Ssam return 0; 118189251Ssam 119189251Ssam aa = os_strdup(a); 120189251Ssam bb = os_strdup(b); 121189251Ssam 122189251Ssam if (aa == NULL || bb == NULL) { 123189251Ssam os_free(aa); 124189251Ssam os_free(bb); 125189251Ssam return os_strcasecmp(a, b); 126189251Ssam } 127189251Ssam 128189251Ssam x509_str_strip_whitespace(aa); 129189251Ssam x509_str_strip_whitespace(bb); 130189251Ssam 131189251Ssam ret = os_strcasecmp(aa, bb); 132189251Ssam 133189251Ssam os_free(aa); 134189251Ssam os_free(bb); 135189251Ssam 136189251Ssam return ret; 137189251Ssam} 138189251Ssam 139189251Ssam 140189251Ssam/** 141189251Ssam * x509_name_compare - Compare X.509 certificate names 142189251Ssam * @a: Certificate name 143189251Ssam * @b: Certificate name 144189251Ssam * Returns: <0, 0, or >0 based on whether a is less than, equal to, or 145189251Ssam * greater than b 146189251Ssam */ 147189251Ssamint x509_name_compare(struct x509_name *a, struct x509_name *b) 148189251Ssam{ 149189251Ssam int res; 150214734Srpaulo size_t i; 151189251Ssam 152189251Ssam if (!a && b) 153189251Ssam return -1; 154189251Ssam if (a && !b) 155189251Ssam return 1; 156189251Ssam if (!a && !b) 157189251Ssam return 0; 158214734Srpaulo if (a->num_attr < b->num_attr) 159214734Srpaulo return -1; 160214734Srpaulo if (a->num_attr > b->num_attr) 161214734Srpaulo return 1; 162189251Ssam 163214734Srpaulo for (i = 0; i < a->num_attr; i++) { 164214734Srpaulo if (a->attr[i].type < b->attr[i].type) 165214734Srpaulo return -1; 166214734Srpaulo if (a->attr[i].type > b->attr[i].type) 167214734Srpaulo return -1; 168214734Srpaulo res = x509_str_compare(a->attr[i].value, b->attr[i].value); 169214734Srpaulo if (res) 170214734Srpaulo return res; 171214734Srpaulo } 172189251Ssam res = x509_str_compare(a->email, b->email); 173189251Ssam if (res) 174189251Ssam return res; 175189251Ssam 176189251Ssam return 0; 177189251Ssam} 178189251Ssam 179189251Ssam 180189251Ssamstatic int x509_parse_algorithm_identifier( 181189251Ssam const u8 *buf, size_t len, 182189251Ssam struct x509_algorithm_identifier *id, const u8 **next) 183189251Ssam{ 184189251Ssam struct asn1_hdr hdr; 185189251Ssam const u8 *pos, *end; 186189251Ssam 187189251Ssam /* 188189251Ssam * AlgorithmIdentifier ::= SEQUENCE { 189189251Ssam * algorithm OBJECT IDENTIFIER, 190189251Ssam * parameters ANY DEFINED BY algorithm OPTIONAL 191189251Ssam * } 192189251Ssam */ 193189251Ssam 194189251Ssam if (asn1_get_next(buf, len, &hdr) < 0 || 195189251Ssam hdr.class != ASN1_CLASS_UNIVERSAL || 196189251Ssam hdr.tag != ASN1_TAG_SEQUENCE) { 197189251Ssam wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " 198189251Ssam "(AlgorithmIdentifier) - found class %d tag 0x%x", 199189251Ssam hdr.class, hdr.tag); 200189251Ssam return -1; 201189251Ssam } 202189251Ssam pos = hdr.payload; 203189251Ssam end = pos + hdr.length; 204189251Ssam 205189251Ssam if (end > buf + len) 206189251Ssam return -1; 207189251Ssam 208189251Ssam *next = end; 209189251Ssam 210189251Ssam if (asn1_get_oid(pos, end - pos, &id->oid, &pos)) 211189251Ssam return -1; 212189251Ssam 213189251Ssam /* TODO: optional parameters */ 214189251Ssam 215189251Ssam return 0; 216189251Ssam} 217189251Ssam 218189251Ssam 219189251Ssamstatic int x509_parse_public_key(const u8 *buf, size_t len, 220189251Ssam struct x509_certificate *cert, 221189251Ssam const u8 **next) 222189251Ssam{ 223189251Ssam struct asn1_hdr hdr; 224189251Ssam const u8 *pos, *end; 225189251Ssam 226189251Ssam /* 227189251Ssam * SubjectPublicKeyInfo ::= SEQUENCE { 228189251Ssam * algorithm AlgorithmIdentifier, 229189251Ssam * subjectPublicKey BIT STRING 230189251Ssam * } 231189251Ssam */ 232189251Ssam 233189251Ssam pos = buf; 234189251Ssam end = buf + len; 235189251Ssam 236189251Ssam if (asn1_get_next(pos, end - pos, &hdr) < 0 || 237189251Ssam hdr.class != ASN1_CLASS_UNIVERSAL || 238189251Ssam hdr.tag != ASN1_TAG_SEQUENCE) { 239189251Ssam wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " 240189251Ssam "(SubjectPublicKeyInfo) - found class %d tag 0x%x", 241189251Ssam hdr.class, hdr.tag); 242189251Ssam return -1; 243189251Ssam } 244189251Ssam pos = hdr.payload; 245189251Ssam 246189251Ssam if (pos + hdr.length > end) 247189251Ssam return -1; 248189251Ssam end = pos + hdr.length; 249189251Ssam *next = end; 250189251Ssam 251189251Ssam if (x509_parse_algorithm_identifier(pos, end - pos, 252189251Ssam &cert->public_key_alg, &pos)) 253189251Ssam return -1; 254189251Ssam 255189251Ssam if (asn1_get_next(pos, end - pos, &hdr) < 0 || 256189251Ssam hdr.class != ASN1_CLASS_UNIVERSAL || 257189251Ssam hdr.tag != ASN1_TAG_BITSTRING) { 258189251Ssam wpa_printf(MSG_DEBUG, "X509: Expected BITSTRING " 259189251Ssam "(subjectPublicKey) - found class %d tag 0x%x", 260189251Ssam hdr.class, hdr.tag); 261189251Ssam return -1; 262189251Ssam } 263189251Ssam if (hdr.length < 1) 264189251Ssam return -1; 265189251Ssam pos = hdr.payload; 266189251Ssam if (*pos) { 267189251Ssam wpa_printf(MSG_DEBUG, "X509: BITSTRING - %d unused bits", 268189251Ssam *pos); 269189251Ssam /* 270189251Ssam * TODO: should this be rejected? X.509 certificates are 271189251Ssam * unlikely to use such a construction. Now we would end up 272189251Ssam * including the extra bits in the buffer which may also be 273189251Ssam * ok. 274189251Ssam */ 275189251Ssam } 276189251Ssam os_free(cert->public_key); 277189251Ssam cert->public_key = os_malloc(hdr.length - 1); 278189251Ssam if (cert->public_key == NULL) { 279189251Ssam wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for " 280189251Ssam "public key"); 281189251Ssam return -1; 282189251Ssam } 283189251Ssam os_memcpy(cert->public_key, pos + 1, hdr.length - 1); 284189251Ssam cert->public_key_len = hdr.length - 1; 285189251Ssam wpa_hexdump(MSG_MSGDUMP, "X509: subjectPublicKey", 286189251Ssam cert->public_key, cert->public_key_len); 287189251Ssam 288189251Ssam return 0; 289189251Ssam} 290189251Ssam 291189251Ssam 292189251Ssamstatic int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, 293189251Ssam const u8 **next) 294189251Ssam{ 295189251Ssam struct asn1_hdr hdr; 296189251Ssam const u8 *pos, *end, *set_pos, *set_end, *seq_pos, *seq_end; 297189251Ssam struct asn1_oid oid; 298214734Srpaulo char *val; 299189251Ssam 300189251Ssam /* 301189251Ssam * Name ::= CHOICE { RDNSequence } 302189251Ssam * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName 303189251Ssam * RelativeDistinguishedName ::= SET OF AttributeTypeAndValue 304189251Ssam * AttributeTypeAndValue ::= SEQUENCE { 305189251Ssam * type AttributeType, 306189251Ssam * value AttributeValue 307189251Ssam * } 308189251Ssam * AttributeType ::= OBJECT IDENTIFIER 309189251Ssam * AttributeValue ::= ANY DEFINED BY AttributeType 310189251Ssam */ 311189251Ssam 312189251Ssam if (asn1_get_next(buf, len, &hdr) < 0 || 313189251Ssam hdr.class != ASN1_CLASS_UNIVERSAL || 314189251Ssam hdr.tag != ASN1_TAG_SEQUENCE) { 315189251Ssam wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " 316189251Ssam "(Name / RDNSequencer) - found class %d tag 0x%x", 317189251Ssam hdr.class, hdr.tag); 318189251Ssam return -1; 319189251Ssam } 320189251Ssam pos = hdr.payload; 321189251Ssam 322189251Ssam if (pos + hdr.length > buf + len) 323189251Ssam return -1; 324189251Ssam 325189251Ssam end = *next = pos + hdr.length; 326189251Ssam 327189251Ssam while (pos < end) { 328214734Srpaulo enum x509_name_attr_type type; 329214734Srpaulo 330189251Ssam if (asn1_get_next(pos, end - pos, &hdr) < 0 || 331189251Ssam hdr.class != ASN1_CLASS_UNIVERSAL || 332189251Ssam hdr.tag != ASN1_TAG_SET) { 333189251Ssam wpa_printf(MSG_DEBUG, "X509: Expected SET " 334189251Ssam "(RelativeDistinguishedName) - found class " 335189251Ssam "%d tag 0x%x", hdr.class, hdr.tag); 336189251Ssam x509_free_name(name); 337189251Ssam return -1; 338189251Ssam } 339189251Ssam 340189251Ssam set_pos = hdr.payload; 341189251Ssam pos = set_end = hdr.payload + hdr.length; 342189251Ssam 343189251Ssam if (asn1_get_next(set_pos, set_end - set_pos, &hdr) < 0 || 344189251Ssam hdr.class != ASN1_CLASS_UNIVERSAL || 345189251Ssam hdr.tag != ASN1_TAG_SEQUENCE) { 346189251Ssam wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " 347189251Ssam "(AttributeTypeAndValue) - found class %d " 348189251Ssam "tag 0x%x", hdr.class, hdr.tag); 349189251Ssam x509_free_name(name); 350189251Ssam return -1; 351189251Ssam } 352189251Ssam 353189251Ssam seq_pos = hdr.payload; 354189251Ssam seq_end = hdr.payload + hdr.length; 355189251Ssam 356189251Ssam if (asn1_get_oid(seq_pos, seq_end - seq_pos, &oid, &seq_pos)) { 357189251Ssam x509_free_name(name); 358189251Ssam return -1; 359189251Ssam } 360189251Ssam 361189251Ssam if (asn1_get_next(seq_pos, seq_end - seq_pos, &hdr) < 0 || 362189251Ssam hdr.class != ASN1_CLASS_UNIVERSAL) { 363189251Ssam wpa_printf(MSG_DEBUG, "X509: Failed to parse " 364189251Ssam "AttributeValue"); 365189251Ssam x509_free_name(name); 366189251Ssam return -1; 367189251Ssam } 368189251Ssam 369189251Ssam /* RFC 3280: 370189251Ssam * MUST: country, organization, organizational-unit, 371189251Ssam * distinguished name qualifier, state or province name, 372189251Ssam * common name, serial number. 373189251Ssam * SHOULD: locality, title, surname, given name, initials, 374189251Ssam * pseudonym, generation qualifier. 375189251Ssam * MUST: domainComponent (RFC 2247). 376189251Ssam */ 377214734Srpaulo type = X509_NAME_ATTR_NOT_USED; 378189251Ssam if (oid.len == 4 && 379189251Ssam oid.oid[0] == 2 && oid.oid[1] == 5 && oid.oid[2] == 4) { 380189251Ssam /* id-at ::= 2.5.4 */ 381189251Ssam switch (oid.oid[3]) { 382189251Ssam case 3: 383189251Ssam /* commonName */ 384214734Srpaulo type = X509_NAME_ATTR_CN; 385189251Ssam break; 386189251Ssam case 6: 387189251Ssam /* countryName */ 388214734Srpaulo type = X509_NAME_ATTR_C; 389189251Ssam break; 390189251Ssam case 7: 391189251Ssam /* localityName */ 392214734Srpaulo type = X509_NAME_ATTR_L; 393189251Ssam break; 394189251Ssam case 8: 395189251Ssam /* stateOrProvinceName */ 396214734Srpaulo type = X509_NAME_ATTR_ST; 397189251Ssam break; 398189251Ssam case 10: 399189251Ssam /* organizationName */ 400214734Srpaulo type = X509_NAME_ATTR_O; 401189251Ssam break; 402189251Ssam case 11: 403189251Ssam /* organizationalUnitName */ 404214734Srpaulo type = X509_NAME_ATTR_OU; 405189251Ssam break; 406189251Ssam } 407189251Ssam } else if (oid.len == 7 && 408189251Ssam oid.oid[0] == 1 && oid.oid[1] == 2 && 409189251Ssam oid.oid[2] == 840 && oid.oid[3] == 113549 && 410189251Ssam oid.oid[4] == 1 && oid.oid[5] == 9 && 411189251Ssam oid.oid[6] == 1) { 412189251Ssam /* 1.2.840.113549.1.9.1 - e-mailAddress */ 413214734Srpaulo os_free(name->email); 414214734Srpaulo name->email = os_malloc(hdr.length + 1); 415214734Srpaulo if (name->email == NULL) { 416214734Srpaulo x509_free_name(name); 417214734Srpaulo return -1; 418214734Srpaulo } 419214734Srpaulo os_memcpy(name->email, hdr.payload, hdr.length); 420214734Srpaulo name->email[hdr.length] = '\0'; 421214734Srpaulo continue; 422214734Srpaulo } else if (oid.len == 7 && 423214734Srpaulo oid.oid[0] == 0 && oid.oid[1] == 9 && 424214734Srpaulo oid.oid[2] == 2342 && oid.oid[3] == 19200300 && 425214734Srpaulo oid.oid[4] == 100 && oid.oid[5] == 1 && 426214734Srpaulo oid.oid[6] == 25) { 427214734Srpaulo /* 0.9.2342.19200300.100.1.25 - domainComponent */ 428214734Srpaulo type = X509_NAME_ATTR_DC; 429189251Ssam } 430189251Ssam 431214734Srpaulo if (type == X509_NAME_ATTR_NOT_USED) { 432189251Ssam wpa_hexdump(MSG_DEBUG, "X509: Unrecognized OID", 433189251Ssam (u8 *) oid.oid, 434189251Ssam oid.len * sizeof(oid.oid[0])); 435189251Ssam wpa_hexdump_ascii(MSG_MSGDUMP, "X509: Attribute Data", 436189251Ssam hdr.payload, hdr.length); 437189251Ssam continue; 438189251Ssam } 439189251Ssam 440214734Srpaulo if (name->num_attr == X509_MAX_NAME_ATTRIBUTES) { 441214734Srpaulo wpa_printf(MSG_INFO, "X509: Too many Name attributes"); 442189251Ssam x509_free_name(name); 443189251Ssam return -1; 444189251Ssam } 445214734Srpaulo 446214734Srpaulo val = os_malloc(hdr.length + 1); 447214734Srpaulo if (val == NULL) { 448214734Srpaulo x509_free_name(name); 449214734Srpaulo return -1; 450214734Srpaulo } 451214734Srpaulo os_memcpy(val, hdr.payload, hdr.length); 452214734Srpaulo val[hdr.length] = '\0'; 453214734Srpaulo if (os_strlen(val) != hdr.length) { 454214734Srpaulo wpa_printf(MSG_INFO, "X509: Reject certificate with " 455214734Srpaulo "embedded NUL byte in a string (%s[NUL])", 456214734Srpaulo val); 457214734Srpaulo x509_free_name(name); 458214734Srpaulo return -1; 459214734Srpaulo } 460214734Srpaulo 461214734Srpaulo name->attr[name->num_attr].type = type; 462214734Srpaulo name->attr[name->num_attr].value = val; 463214734Srpaulo name->num_attr++; 464189251Ssam } 465189251Ssam 466189251Ssam return 0; 467189251Ssam} 468189251Ssam 469189251Ssam 470214734Srpaulostatic char * x509_name_attr_str(enum x509_name_attr_type type) 471214734Srpaulo{ 472214734Srpaulo switch (type) { 473214734Srpaulo case X509_NAME_ATTR_NOT_USED: 474214734Srpaulo return "[N/A]"; 475214734Srpaulo case X509_NAME_ATTR_DC: 476214734Srpaulo return "DC"; 477214734Srpaulo case X509_NAME_ATTR_CN: 478214734Srpaulo return "CN"; 479214734Srpaulo case X509_NAME_ATTR_C: 480214734Srpaulo return "C"; 481214734Srpaulo case X509_NAME_ATTR_L: 482214734Srpaulo return "L"; 483214734Srpaulo case X509_NAME_ATTR_ST: 484214734Srpaulo return "ST"; 485214734Srpaulo case X509_NAME_ATTR_O: 486214734Srpaulo return "O"; 487214734Srpaulo case X509_NAME_ATTR_OU: 488214734Srpaulo return "OU"; 489214734Srpaulo } 490214734Srpaulo return "?"; 491214734Srpaulo} 492214734Srpaulo 493214734Srpaulo 494189251Ssam/** 495189251Ssam * x509_name_string - Convert an X.509 certificate name into a string 496189251Ssam * @name: Name to convert 497189251Ssam * @buf: Buffer for the string 498189251Ssam * @len: Maximum buffer length 499189251Ssam */ 500189251Ssamvoid x509_name_string(struct x509_name *name, char *buf, size_t len) 501189251Ssam{ 502189251Ssam char *pos, *end; 503189251Ssam int ret; 504214734Srpaulo size_t i; 505189251Ssam 506189251Ssam if (len == 0) 507189251Ssam return; 508189251Ssam 509189251Ssam pos = buf; 510189251Ssam end = buf + len; 511189251Ssam 512214734Srpaulo for (i = 0; i < name->num_attr; i++) { 513214734Srpaulo ret = os_snprintf(pos, end - pos, "%s=%s, ", 514214734Srpaulo x509_name_attr_str(name->attr[i].type), 515214734Srpaulo name->attr[i].value); 516189251Ssam if (ret < 0 || ret >= end - pos) 517189251Ssam goto done; 518189251Ssam pos += ret; 519189251Ssam } 520189251Ssam 521189251Ssam if (pos > buf + 1 && pos[-1] == ' ' && pos[-2] == ',') { 522214734Srpaulo pos--; 523214734Srpaulo *pos = '\0'; 524214734Srpaulo pos--; 525214734Srpaulo *pos = '\0'; 526189251Ssam } 527189251Ssam 528189251Ssam if (name->email) { 529189251Ssam ret = os_snprintf(pos, end - pos, "/emailAddress=%s", 530189251Ssam name->email); 531189251Ssam if (ret < 0 || ret >= end - pos) 532189251Ssam goto done; 533189251Ssam pos += ret; 534189251Ssam } 535189251Ssam 536189251Ssamdone: 537189251Ssam end[-1] = '\0'; 538189251Ssam} 539189251Ssam 540189251Ssam 541189251Ssamstatic int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag, 542189251Ssam os_time_t *val) 543189251Ssam{ 544189251Ssam const char *pos; 545189251Ssam int year, month, day, hour, min, sec; 546189251Ssam 547189251Ssam /* 548189251Ssam * Time ::= CHOICE { 549189251Ssam * utcTime UTCTime, 550189251Ssam * generalTime GeneralizedTime 551189251Ssam * } 552189251Ssam * 553189251Ssam * UTCTime: YYMMDDHHMMSSZ 554189251Ssam * GeneralizedTime: YYYYMMDDHHMMSSZ 555189251Ssam */ 556189251Ssam 557189251Ssam pos = (const char *) buf; 558189251Ssam 559189251Ssam switch (asn1_tag) { 560189251Ssam case ASN1_TAG_UTCTIME: 561189251Ssam if (len != 13 || buf[12] != 'Z') { 562189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "X509: Unrecognized " 563189251Ssam "UTCTime format", buf, len); 564189251Ssam return -1; 565189251Ssam } 566189251Ssam if (sscanf(pos, "%02d", &year) != 1) { 567189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse " 568189251Ssam "UTCTime year", buf, len); 569189251Ssam return -1; 570189251Ssam } 571189251Ssam if (year < 50) 572189251Ssam year += 2000; 573189251Ssam else 574189251Ssam year += 1900; 575189251Ssam pos += 2; 576189251Ssam break; 577189251Ssam case ASN1_TAG_GENERALIZEDTIME: 578189251Ssam if (len != 15 || buf[14] != 'Z') { 579189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "X509: Unrecognized " 580189251Ssam "GeneralizedTime format", buf, len); 581189251Ssam return -1; 582189251Ssam } 583189251Ssam if (sscanf(pos, "%04d", &year) != 1) { 584189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse " 585189251Ssam "GeneralizedTime year", buf, len); 586189251Ssam return -1; 587189251Ssam } 588189251Ssam pos += 4; 589189251Ssam break; 590189251Ssam default: 591189251Ssam wpa_printf(MSG_DEBUG, "X509: Expected UTCTime or " 592189251Ssam "GeneralizedTime - found tag 0x%x", asn1_tag); 593189251Ssam return -1; 594189251Ssam } 595189251Ssam 596189251Ssam if (sscanf(pos, "%02d", &month) != 1) { 597189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " 598189251Ssam "(month)", buf, len); 599189251Ssam return -1; 600189251Ssam } 601189251Ssam pos += 2; 602189251Ssam 603189251Ssam if (sscanf(pos, "%02d", &day) != 1) { 604189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " 605189251Ssam "(day)", buf, len); 606189251Ssam return -1; 607189251Ssam } 608189251Ssam pos += 2; 609189251Ssam 610189251Ssam if (sscanf(pos, "%02d", &hour) != 1) { 611189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " 612189251Ssam "(hour)", buf, len); 613189251Ssam return -1; 614189251Ssam } 615189251Ssam pos += 2; 616189251Ssam 617189251Ssam if (sscanf(pos, "%02d", &min) != 1) { 618189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " 619189251Ssam "(min)", buf, len); 620189251Ssam return -1; 621189251Ssam } 622189251Ssam pos += 2; 623189251Ssam 624189251Ssam if (sscanf(pos, "%02d", &sec) != 1) { 625189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time " 626189251Ssam "(sec)", buf, len); 627189251Ssam return -1; 628189251Ssam } 629189251Ssam 630189251Ssam if (os_mktime(year, month, day, hour, min, sec, val) < 0) { 631189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to convert Time", 632189251Ssam buf, len); 633189251Ssam if (year < 1970) { 634189251Ssam /* 635189251Ssam * At least some test certificates have been configured 636189251Ssam * to use dates prior to 1970. Set the date to 637189251Ssam * beginning of 1970 to handle these case. 638189251Ssam */ 639189251Ssam wpa_printf(MSG_DEBUG, "X509: Year=%d before epoch - " 640189251Ssam "assume epoch as the time", year); 641189251Ssam *val = 0; 642189251Ssam return 0; 643189251Ssam } 644189251Ssam return -1; 645189251Ssam } 646189251Ssam 647189251Ssam return 0; 648189251Ssam} 649189251Ssam 650189251Ssam 651189251Ssamstatic int x509_parse_validity(const u8 *buf, size_t len, 652189251Ssam struct x509_certificate *cert, const u8 **next) 653189251Ssam{ 654189251Ssam struct asn1_hdr hdr; 655189251Ssam const u8 *pos; 656189251Ssam size_t plen; 657189251Ssam 658189251Ssam /* 659189251Ssam * Validity ::= SEQUENCE { 660189251Ssam * notBefore Time, 661189251Ssam * notAfter Time 662189251Ssam * } 663189251Ssam * 664189251Ssam * RFC 3280, 4.1.2.5: 665189251Ssam * CAs conforming to this profile MUST always encode certificate 666189251Ssam * validity dates through the year 2049 as UTCTime; certificate 667189251Ssam * validity dates in 2050 or later MUST be encoded as GeneralizedTime. 668189251Ssam */ 669189251Ssam 670189251Ssam if (asn1_get_next(buf, len, &hdr) < 0 || 671189251Ssam hdr.class != ASN1_CLASS_UNIVERSAL || 672189251Ssam hdr.tag != ASN1_TAG_SEQUENCE) { 673189251Ssam wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " 674189251Ssam "(Validity) - found class %d tag 0x%x", 675189251Ssam hdr.class, hdr.tag); 676189251Ssam return -1; 677189251Ssam } 678189251Ssam pos = hdr.payload; 679189251Ssam plen = hdr.length; 680189251Ssam 681189251Ssam if (pos + plen > buf + len) 682189251Ssam return -1; 683189251Ssam 684189251Ssam *next = pos + plen; 685189251Ssam 686189251Ssam if (asn1_get_next(pos, plen, &hdr) < 0 || 687189251Ssam hdr.class != ASN1_CLASS_UNIVERSAL || 688189251Ssam x509_parse_time(hdr.payload, hdr.length, hdr.tag, 689189251Ssam &cert->not_before) < 0) { 690189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse notBefore " 691189251Ssam "Time", hdr.payload, hdr.length); 692189251Ssam return -1; 693189251Ssam } 694189251Ssam 695189251Ssam pos = hdr.payload + hdr.length; 696189251Ssam plen = *next - pos; 697189251Ssam 698189251Ssam if (asn1_get_next(pos, plen, &hdr) < 0 || 699189251Ssam hdr.class != ASN1_CLASS_UNIVERSAL || 700189251Ssam x509_parse_time(hdr.payload, hdr.length, hdr.tag, 701189251Ssam &cert->not_after) < 0) { 702189251Ssam wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse notAfter " 703189251Ssam "Time", hdr.payload, hdr.length); 704189251Ssam return -1; 705189251Ssam } 706189251Ssam 707189251Ssam wpa_printf(MSG_MSGDUMP, "X509: Validity: notBefore: %lu notAfter: %lu", 708189251Ssam (unsigned long) cert->not_before, 709189251Ssam (unsigned long) cert->not_after); 710189251Ssam 711189251Ssam return 0; 712189251Ssam} 713189251Ssam 714189251Ssam 715189251Ssamstatic int x509_id_ce_oid(struct asn1_oid *oid) 716189251Ssam{ 717189251Ssam /* id-ce arc from X.509 for standard X.509v3 extensions */ 718189251Ssam return oid->len >= 4 && 719189251Ssam oid->oid[0] == 2 /* joint-iso-ccitt */ && 720189251Ssam oid->oid[1] == 5 /* ds */ && 721189251Ssam oid->oid[2] == 29 /* id-ce */; 722189251Ssam} 723189251Ssam 724189251Ssam 725189251Ssamstatic int x509_parse_ext_key_usage(struct x509_certificate *cert, 726189251Ssam const u8 *pos, size_t len) 727189251Ssam{ 728189251Ssam struct asn1_hdr hdr; 729189251Ssam 730189251Ssam /* 731189251Ssam * KeyUsage ::= BIT STRING { 732189251Ssam * digitalSignature (0), 733189251Ssam * nonRepudiation (1), 734189251Ssam * keyEncipherment (2), 735189251Ssam * dataEncipherment (3), 736189251Ssam * keyAgreement (4), 737189251Ssam * keyCertSign (5), 738189251Ssam * cRLSign (6), 739189251Ssam * encipherOnly (7), 740189251Ssam * decipherOnly (8) } 741189251Ssam */ 742189251Ssam 743189251Ssam if (asn1_get_next(pos, len, &hdr) < 0 || 744189251Ssam hdr.class != ASN1_CLASS_UNIVERSAL || 745189251Ssam hdr.tag != ASN1_TAG_BITSTRING || 746189251Ssam hdr.length < 1) { 747189251Ssam wpa_printf(MSG_DEBUG, "X509: Expected BIT STRING in " 748189251Ssam "KeyUsage; found %d tag 0x%x len %d", 749189251Ssam hdr.class, hdr.tag, hdr.length); 750189251Ssam return -1; 751189251Ssam } 752189251Ssam 753189251Ssam cert->extensions_present |= X509_EXT_KEY_USAGE; 754189251Ssam cert->key_usage = asn1_bit_string_to_long(hdr.payload, hdr.length); 755189251Ssam 756189251Ssam wpa_printf(MSG_DEBUG, "X509: KeyUsage 0x%lx", cert->key_usage); 757189251Ssam 758189251Ssam return 0; 759189251Ssam} 760189251Ssam 761189251Ssam 762189251Ssamstatic int x509_parse_ext_basic_constraints(struct x509_certificate *cert, 763189251Ssam const u8 *pos, size_t len) 764189251Ssam{ 765189251Ssam struct asn1_hdr hdr; 766189251Ssam unsigned long value; 767189251Ssam size_t left; 768189251Ssam 769189251Ssam /* 770189251Ssam * BasicConstraints ::= SEQUENCE { 771189251Ssam * cA BOOLEAN DEFAULT FALSE, 772189251Ssam * pathLenConstraint INTEGER (0..MAX) OPTIONAL } 773189251Ssam */ 774189251Ssam 775189251Ssam if (asn1_get_next(pos, len, &hdr) < 0 || 776189251Ssam hdr.class != ASN1_CLASS_UNIVERSAL || 777189251Ssam hdr.tag != ASN1_TAG_SEQUENCE) { 778189251Ssam wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in " 779189251Ssam "BasicConstraints; found %d tag 0x%x", 780189251Ssam hdr.class, hdr.tag); 781189251Ssam return -1; 782189251Ssam } 783189251Ssam 784189251Ssam cert->extensions_present |= X509_EXT_BASIC_CONSTRAINTS; 785189251Ssam 786189251Ssam if (hdr.length == 0) 787189251Ssam return 0; 788189251Ssam 789189251Ssam if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 || 790189251Ssam hdr.class != ASN1_CLASS_UNIVERSAL) { 791189251Ssam wpa_printf(MSG_DEBUG, "X509: Failed to parse " 792189251Ssam "BasicConstraints"); 793189251Ssam return -1; 794189251Ssam } 795189251Ssam 796189251Ssam if (hdr.tag == ASN1_TAG_BOOLEAN) { 797189251Ssam if (hdr.length != 1) { 798189251Ssam wpa_printf(MSG_DEBUG, "X509: Unexpected " 799189251Ssam "Boolean length (%u) in BasicConstraints", 800189251Ssam hdr.length); 801189251Ssam return -1; 802189251Ssam } 803189251Ssam cert->ca = hdr.payload[0]; 804189251Ssam 805189251Ssam if (hdr.payload + hdr.length == pos + len) { 806189251Ssam wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d", 807189251Ssam cert->ca); 808189251Ssam return 0; 809189251Ssam } 810189251Ssam 811189251Ssam if (asn1_get_next(hdr.payload + hdr.length, len - hdr.length, 812189251Ssam &hdr) < 0 || 813189251Ssam hdr.class != ASN1_CLASS_UNIVERSAL) { 814189251Ssam wpa_printf(MSG_DEBUG, "X509: Failed to parse " 815189251Ssam "BasicConstraints"); 816189251Ssam return -1; 817189251Ssam } 818189251Ssam } 819189251Ssam 820189251Ssam if (hdr.tag != ASN1_TAG_INTEGER) { 821189251Ssam wpa_printf(MSG_DEBUG, "X509: Expected INTEGER in " 822189251Ssam "BasicConstraints; found class %d tag 0x%x", 823189251Ssam hdr.class, hdr.tag); 824189251Ssam return -1; 825189251Ssam } 826189251Ssam 827189251Ssam pos = hdr.payload; 828189251Ssam left = hdr.length; 829189251Ssam value = 0; 830189251Ssam while (left) { 831189251Ssam value <<= 8; 832189251Ssam value |= *pos++; 833189251Ssam left--; 834189251Ssam } 835189251Ssam 836189251Ssam cert->path_len_constraint = value; 837189251Ssam cert->extensions_present |= X509_EXT_PATH_LEN_CONSTRAINT; 838189251Ssam 839189251Ssam wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d " 840189251Ssam "pathLenConstraint=%lu", 841189251Ssam cert->ca, cert->path_len_constraint); 842189251Ssam 843189251Ssam return 0; 844189251Ssam} 845189251Ssam 846189251Ssam 847214734Srpaulostatic int x509_parse_alt_name_rfc8222(struct x509_name *name, 848214734Srpaulo const u8 *pos, size_t len) 849214734Srpaulo{ 850214734Srpaulo /* rfc822Name IA5String */ 851214734Srpaulo wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - rfc822Name", pos, len); 852214734Srpaulo os_free(name->alt_email); 853214734Srpaulo name->alt_email = os_zalloc(len + 1); 854214734Srpaulo if (name->alt_email == NULL) 855214734Srpaulo return -1; 856214734Srpaulo os_memcpy(name->alt_email, pos, len); 857214734Srpaulo if (os_strlen(name->alt_email) != len) { 858214734Srpaulo wpa_printf(MSG_INFO, "X509: Reject certificate with " 859214734Srpaulo "embedded NUL byte in rfc822Name (%s[NUL])", 860214734Srpaulo name->alt_email); 861214734Srpaulo os_free(name->alt_email); 862214734Srpaulo name->alt_email = NULL; 863214734Srpaulo return -1; 864214734Srpaulo } 865214734Srpaulo return 0; 866214734Srpaulo} 867214734Srpaulo 868214734Srpaulo 869214734Srpaulostatic int x509_parse_alt_name_dns(struct x509_name *name, 870214734Srpaulo const u8 *pos, size_t len) 871214734Srpaulo{ 872214734Srpaulo /* dNSName IA5String */ 873214734Srpaulo wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - dNSName", pos, len); 874214734Srpaulo os_free(name->dns); 875214734Srpaulo name->dns = os_zalloc(len + 1); 876214734Srpaulo if (name->dns == NULL) 877214734Srpaulo return -1; 878214734Srpaulo os_memcpy(name->dns, pos, len); 879214734Srpaulo if (os_strlen(name->dns) != len) { 880214734Srpaulo wpa_printf(MSG_INFO, "X509: Reject certificate with " 881214734Srpaulo "embedded NUL byte in dNSName (%s[NUL])", 882214734Srpaulo name->dns); 883214734Srpaulo os_free(name->dns); 884214734Srpaulo name->dns = NULL; 885214734Srpaulo return -1; 886214734Srpaulo } 887214734Srpaulo return 0; 888214734Srpaulo} 889214734Srpaulo 890214734Srpaulo 891214734Srpaulostatic int x509_parse_alt_name_uri(struct x509_name *name, 892214734Srpaulo const u8 *pos, size_t len) 893214734Srpaulo{ 894214734Srpaulo /* uniformResourceIdentifier IA5String */ 895214734Srpaulo wpa_hexdump_ascii(MSG_MSGDUMP, 896214734Srpaulo "X509: altName - uniformResourceIdentifier", 897214734Srpaulo pos, len); 898214734Srpaulo os_free(name->uri); 899214734Srpaulo name->uri = os_zalloc(len + 1); 900214734Srpaulo if (name->uri == NULL) 901214734Srpaulo return -1; 902214734Srpaulo os_memcpy(name->uri, pos, len); 903214734Srpaulo if (os_strlen(name->uri) != len) { 904214734Srpaulo wpa_printf(MSG_INFO, "X509: Reject certificate with " 905214734Srpaulo "embedded NUL byte in uniformResourceIdentifier " 906214734Srpaulo "(%s[NUL])", name->uri); 907214734Srpaulo os_free(name->uri); 908214734Srpaulo name->uri = NULL; 909214734Srpaulo return -1; 910214734Srpaulo } 911214734Srpaulo return 0; 912214734Srpaulo} 913214734Srpaulo 914214734Srpaulo 915214734Srpaulostatic int x509_parse_alt_name_ip(struct x509_name *name, 916214734Srpaulo const u8 *pos, size_t len) 917214734Srpaulo{ 918214734Srpaulo /* iPAddress OCTET STRING */ 919214734Srpaulo wpa_hexdump(MSG_MSGDUMP, "X509: altName - iPAddress", pos, len); 920214734Srpaulo os_free(name->ip); 921214734Srpaulo name->ip = os_malloc(len); 922214734Srpaulo if (name->ip == NULL) 923214734Srpaulo return -1; 924214734Srpaulo os_memcpy(name->ip, pos, len); 925214734Srpaulo name->ip_len = len; 926214734Srpaulo return 0; 927214734Srpaulo} 928214734Srpaulo 929214734Srpaulo 930214734Srpaulostatic int x509_parse_alt_name_rid(struct x509_name *name, 931214734Srpaulo const u8 *pos, size_t len) 932214734Srpaulo{ 933214734Srpaulo char buf[80]; 934214734Srpaulo 935214734Srpaulo /* registeredID OBJECT IDENTIFIER */ 936214734Srpaulo if (asn1_parse_oid(pos, len, &name->rid) < 0) 937214734Srpaulo return -1; 938214734Srpaulo 939214734Srpaulo asn1_oid_to_str(&name->rid, buf, sizeof(buf)); 940214734Srpaulo wpa_printf(MSG_MSGDUMP, "X509: altName - registeredID: %s", buf); 941214734Srpaulo 942214734Srpaulo return 0; 943214734Srpaulo} 944214734Srpaulo 945214734Srpaulo 946214734Srpaulostatic int x509_parse_ext_alt_name(struct x509_name *name, 947214734Srpaulo const u8 *pos, size_t len) 948214734Srpaulo{ 949214734Srpaulo struct asn1_hdr hdr; 950214734Srpaulo const u8 *p, *end; 951214734Srpaulo 952214734Srpaulo /* 953214734Srpaulo * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName 954214734Srpaulo * 955214734Srpaulo * GeneralName ::= CHOICE { 956214734Srpaulo * otherName [0] OtherName, 957214734Srpaulo * rfc822Name [1] IA5String, 958214734Srpaulo * dNSName [2] IA5String, 959214734Srpaulo * x400Address [3] ORAddress, 960214734Srpaulo * directoryName [4] Name, 961214734Srpaulo * ediPartyName [5] EDIPartyName, 962214734Srpaulo * uniformResourceIdentifier [6] IA5String, 963214734Srpaulo * iPAddress [7] OCTET STRING, 964214734Srpaulo * registeredID [8] OBJECT IDENTIFIER } 965214734Srpaulo * 966214734Srpaulo * OtherName ::= SEQUENCE { 967214734Srpaulo * type-id OBJECT IDENTIFIER, 968214734Srpaulo * value [0] EXPLICIT ANY DEFINED BY type-id } 969214734Srpaulo * 970214734Srpaulo * EDIPartyName ::= SEQUENCE { 971214734Srpaulo * nameAssigner [0] DirectoryString OPTIONAL, 972214734Srpaulo * partyName [1] DirectoryString } 973214734Srpaulo */ 974214734Srpaulo 975214734Srpaulo for (p = pos, end = pos + len; p < end; p = hdr.payload + hdr.length) { 976214734Srpaulo int res; 977214734Srpaulo 978214734Srpaulo if (asn1_get_next(p, end - p, &hdr) < 0) { 979214734Srpaulo wpa_printf(MSG_DEBUG, "X509: Failed to parse " 980214734Srpaulo "SubjectAltName item"); 981214734Srpaulo return -1; 982214734Srpaulo } 983214734Srpaulo 984214734Srpaulo if (hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) 985214734Srpaulo continue; 986214734Srpaulo 987214734Srpaulo switch (hdr.tag) { 988214734Srpaulo case 1: 989214734Srpaulo res = x509_parse_alt_name_rfc8222(name, hdr.payload, 990214734Srpaulo hdr.length); 991214734Srpaulo break; 992214734Srpaulo case 2: 993214734Srpaulo res = x509_parse_alt_name_dns(name, hdr.payload, 994214734Srpaulo hdr.length); 995214734Srpaulo break; 996214734Srpaulo case 6: 997214734Srpaulo res = x509_parse_alt_name_uri(name, hdr.payload, 998214734Srpaulo hdr.length); 999214734Srpaulo break; 1000214734Srpaulo case 7: 1001214734Srpaulo res = x509_parse_alt_name_ip(name, hdr.payload, 1002214734Srpaulo hdr.length); 1003214734Srpaulo break; 1004214734Srpaulo case 8: 1005214734Srpaulo res = x509_parse_alt_name_rid(name, hdr.payload, 1006214734Srpaulo hdr.length); 1007214734Srpaulo break; 1008214734Srpaulo case 0: /* TODO: otherName */ 1009214734Srpaulo case 3: /* TODO: x500Address */ 1010214734Srpaulo case 4: /* TODO: directoryName */ 1011214734Srpaulo case 5: /* TODO: ediPartyName */ 1012214734Srpaulo default: 1013214734Srpaulo res = 0; 1014214734Srpaulo break; 1015214734Srpaulo } 1016214734Srpaulo if (res < 0) 1017214734Srpaulo return res; 1018214734Srpaulo } 1019214734Srpaulo 1020214734Srpaulo return 0; 1021214734Srpaulo} 1022214734Srpaulo 1023214734Srpaulo 1024214734Srpaulostatic int x509_parse_ext_subject_alt_name(struct x509_certificate *cert, 1025214734Srpaulo const u8 *pos, size_t len) 1026214734Srpaulo{ 1027214734Srpaulo struct asn1_hdr hdr; 1028214734Srpaulo 1029214734Srpaulo /* SubjectAltName ::= GeneralNames */ 1030214734Srpaulo 1031214734Srpaulo if (asn1_get_next(pos, len, &hdr) < 0 || 1032214734Srpaulo hdr.class != ASN1_CLASS_UNIVERSAL || 1033214734Srpaulo hdr.tag != ASN1_TAG_SEQUENCE) { 1034214734Srpaulo wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in " 1035214734Srpaulo "SubjectAltName; found %d tag 0x%x", 1036214734Srpaulo hdr.class, hdr.tag); 1037214734Srpaulo return -1; 1038214734Srpaulo } 1039214734Srpaulo 1040214734Srpaulo wpa_printf(MSG_DEBUG, "X509: SubjectAltName"); 1041214734Srpaulo cert->extensions_present |= X509_EXT_SUBJECT_ALT_NAME; 1042214734Srpaulo 1043214734Srpaulo if (hdr.length == 0) 1044214734Srpaulo return 0; 1045214734Srpaulo 1046214734Srpaulo return x509_parse_ext_alt_name(&cert->subject, hdr.payload, 1047214734Srpaulo hdr.length); 1048214734Srpaulo} 1049214734Srpaulo 1050214734Srpaulo 1051214734Srpaulostatic int x509_parse_ext_issuer_alt_name(struct x509_certificate *cert, 1052214734Srpaulo const u8 *pos, size_t len) 1053214734Srpaulo{ 1054214734Srpaulo struct asn1_hdr hdr; 1055214734Srpaulo 1056214734Srpaulo /* IssuerAltName ::= GeneralNames */ 1057214734Srpaulo 1058214734Srpaulo if (asn1_get_next(pos, len, &hdr) < 0 || 1059214734Srpaulo hdr.class != ASN1_CLASS_UNIVERSAL || 1060214734Srpaulo hdr.tag != ASN1_TAG_SEQUENCE) { 1061214734Srpaulo wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in " 1062214734Srpaulo "IssuerAltName; found %d tag 0x%x", 1063214734Srpaulo hdr.class, hdr.tag); 1064214734Srpaulo return -1; 1065214734Srpaulo } 1066214734Srpaulo 1067214734Srpaulo wpa_printf(MSG_DEBUG, "X509: IssuerAltName"); 1068214734Srpaulo cert->extensions_present |= X509_EXT_ISSUER_ALT_NAME; 1069214734Srpaulo 1070214734Srpaulo if (hdr.length == 0) 1071214734Srpaulo return 0; 1072214734Srpaulo 1073214734Srpaulo return x509_parse_ext_alt_name(&cert->issuer, hdr.payload, 1074214734Srpaulo hdr.length); 1075214734Srpaulo} 1076214734Srpaulo 1077214734Srpaulo 1078189251Ssamstatic int x509_parse_extension_data(struct x509_certificate *cert, 1079189251Ssam struct asn1_oid *oid, 1080189251Ssam const u8 *pos, size_t len) 1081189251Ssam{ 1082189251Ssam if (!x509_id_ce_oid(oid)) 1083189251Ssam return 1; 1084189251Ssam 1085189251Ssam /* TODO: add other extensions required by RFC 3280, Ch 4.2: 1086189251Ssam * certificate policies (section 4.2.1.5) 1087189251Ssam * name constraints (section 4.2.1.11) 1088189251Ssam * policy constraints (section 4.2.1.12) 1089189251Ssam * extended key usage (section 4.2.1.13) 1090189251Ssam * inhibit any-policy (section 4.2.1.15) 1091189251Ssam */ 1092189251Ssam switch (oid->oid[3]) { 1093189251Ssam case 15: /* id-ce-keyUsage */ 1094189251Ssam return x509_parse_ext_key_usage(cert, pos, len); 1095214734Srpaulo case 17: /* id-ce-subjectAltName */ 1096214734Srpaulo return x509_parse_ext_subject_alt_name(cert, pos, len); 1097214734Srpaulo case 18: /* id-ce-issuerAltName */ 1098214734Srpaulo return x509_parse_ext_issuer_alt_name(cert, pos, len); 1099189251Ssam case 19: /* id-ce-basicConstraints */ 1100189251Ssam return x509_parse_ext_basic_constraints(cert, pos, len); 1101189251Ssam default: 1102189251Ssam return 1; 1103189251Ssam } 1104189251Ssam} 1105189251Ssam 1106189251Ssam 1107189251Ssamstatic int x509_parse_extension(struct x509_certificate *cert, 1108189251Ssam const u8 *pos, size_t len, const u8 **next) 1109189251Ssam{ 1110189251Ssam const u8 *end; 1111189251Ssam struct asn1_hdr hdr; 1112189251Ssam struct asn1_oid oid; 1113189251Ssam int critical_ext = 0, res; 1114189251Ssam char buf[80]; 1115189251Ssam 1116189251Ssam /* 1117189251Ssam * Extension ::= SEQUENCE { 1118189251Ssam * extnID OBJECT IDENTIFIER, 1119189251Ssam * critical BOOLEAN DEFAULT FALSE, 1120189251Ssam * extnValue OCTET STRING 1121189251Ssam * } 1122189251Ssam */ 1123189251Ssam 1124189251Ssam if (asn1_get_next(pos, len, &hdr) < 0 || 1125189251Ssam hdr.class != ASN1_CLASS_UNIVERSAL || 1126189251Ssam hdr.tag != ASN1_TAG_SEQUENCE) { 1127189251Ssam wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header in " 1128189251Ssam "Extensions: class %d tag 0x%x; expected SEQUENCE", 1129189251Ssam hdr.class, hdr.tag); 1130189251Ssam return -1; 1131189251Ssam } 1132189251Ssam pos = hdr.payload; 1133189251Ssam *next = end = pos + hdr.length; 1134189251Ssam 1135189251Ssam if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0) { 1136189251Ssam wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 data for " 1137189251Ssam "Extension (expected OID)"); 1138189251Ssam return -1; 1139189251Ssam } 1140189251Ssam 1141189251Ssam if (asn1_get_next(pos, end - pos, &hdr) < 0 || 1142189251Ssam hdr.class != ASN1_CLASS_UNIVERSAL || 1143189251Ssam (hdr.tag != ASN1_TAG_BOOLEAN && 1144189251Ssam hdr.tag != ASN1_TAG_OCTETSTRING)) { 1145189251Ssam wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header in " 1146189251Ssam "Extensions: class %d tag 0x%x; expected BOOLEAN " 1147189251Ssam "or OCTET STRING", hdr.class, hdr.tag); 1148189251Ssam return -1; 1149189251Ssam } 1150189251Ssam 1151189251Ssam if (hdr.tag == ASN1_TAG_BOOLEAN) { 1152189251Ssam if (hdr.length != 1) { 1153189251Ssam wpa_printf(MSG_DEBUG, "X509: Unexpected " 1154189251Ssam "Boolean length (%u)", hdr.length); 1155189251Ssam return -1; 1156189251Ssam } 1157189251Ssam critical_ext = hdr.payload[0]; 1158189251Ssam pos = hdr.payload; 1159189251Ssam if (asn1_get_next(pos, end - pos, &hdr) < 0 || 1160189251Ssam (hdr.class != ASN1_CLASS_UNIVERSAL && 1161189251Ssam hdr.class != ASN1_CLASS_PRIVATE) || 1162189251Ssam hdr.tag != ASN1_TAG_OCTETSTRING) { 1163189251Ssam wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header " 1164189251Ssam "in Extensions: class %d tag 0x%x; " 1165189251Ssam "expected OCTET STRING", 1166189251Ssam hdr.class, hdr.tag); 1167189251Ssam return -1; 1168189251Ssam } 1169189251Ssam } 1170189251Ssam 1171189251Ssam asn1_oid_to_str(&oid, buf, sizeof(buf)); 1172189251Ssam wpa_printf(MSG_DEBUG, "X509: Extension: extnID=%s critical=%d", 1173189251Ssam buf, critical_ext); 1174189251Ssam wpa_hexdump(MSG_MSGDUMP, "X509: extnValue", hdr.payload, hdr.length); 1175189251Ssam 1176189251Ssam res = x509_parse_extension_data(cert, &oid, hdr.payload, hdr.length); 1177189251Ssam if (res < 0) 1178189251Ssam return res; 1179189251Ssam if (res == 1 && critical_ext) { 1180189251Ssam wpa_printf(MSG_INFO, "X509: Unknown critical extension %s", 1181189251Ssam buf); 1182189251Ssam return -1; 1183189251Ssam } 1184189251Ssam 1185189251Ssam return 0; 1186189251Ssam} 1187189251Ssam 1188189251Ssam 1189189251Ssamstatic int x509_parse_extensions(struct x509_certificate *cert, 1190189251Ssam const u8 *pos, size_t len) 1191189251Ssam{ 1192189251Ssam const u8 *end; 1193189251Ssam struct asn1_hdr hdr; 1194189251Ssam 1195189251Ssam /* Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension */ 1196189251Ssam 1197189251Ssam if (asn1_get_next(pos, len, &hdr) < 0 || 1198189251Ssam hdr.class != ASN1_CLASS_UNIVERSAL || 1199189251Ssam hdr.tag != ASN1_TAG_SEQUENCE) { 1200189251Ssam wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 data " 1201189251Ssam "for Extensions: class %d tag 0x%x; " 1202189251Ssam "expected SEQUENCE", hdr.class, hdr.tag); 1203189251Ssam return -1; 1204189251Ssam } 1205189251Ssam 1206189251Ssam pos = hdr.payload; 1207189251Ssam end = pos + hdr.length; 1208189251Ssam 1209189251Ssam while (pos < end) { 1210189251Ssam if (x509_parse_extension(cert, pos, end - pos, &pos) 1211189251Ssam < 0) 1212189251Ssam return -1; 1213189251Ssam } 1214189251Ssam 1215189251Ssam return 0; 1216189251Ssam} 1217189251Ssam 1218189251Ssam 1219189251Ssamstatic int x509_parse_tbs_certificate(const u8 *buf, size_t len, 1220189251Ssam struct x509_certificate *cert, 1221189251Ssam const u8 **next) 1222189251Ssam{ 1223189251Ssam struct asn1_hdr hdr; 1224189251Ssam const u8 *pos, *end; 1225189251Ssam size_t left; 1226189251Ssam char sbuf[128]; 1227189251Ssam unsigned long value; 1228189251Ssam 1229189251Ssam /* tbsCertificate TBSCertificate ::= SEQUENCE */ 1230189251Ssam if (asn1_get_next(buf, len, &hdr) < 0 || 1231189251Ssam hdr.class != ASN1_CLASS_UNIVERSAL || 1232189251Ssam hdr.tag != ASN1_TAG_SEQUENCE) { 1233189251Ssam wpa_printf(MSG_DEBUG, "X509: tbsCertificate did not start " 1234189251Ssam "with a valid SEQUENCE - found class %d tag 0x%x", 1235189251Ssam hdr.class, hdr.tag); 1236189251Ssam return -1; 1237189251Ssam } 1238189251Ssam pos = hdr.payload; 1239189251Ssam end = *next = pos + hdr.length; 1240189251Ssam 1241189251Ssam /* 1242189251Ssam * version [0] EXPLICIT Version DEFAULT v1 1243189251Ssam * Version ::= INTEGER { v1(0), v2(1), v3(2) } 1244189251Ssam */ 1245189251Ssam if (asn1_get_next(pos, end - pos, &hdr) < 0) 1246189251Ssam return -1; 1247189251Ssam pos = hdr.payload; 1248189251Ssam 1249189251Ssam if (hdr.class == ASN1_CLASS_CONTEXT_SPECIFIC) { 1250189251Ssam if (asn1_get_next(pos, end - pos, &hdr) < 0) 1251189251Ssam return -1; 1252189251Ssam 1253189251Ssam if (hdr.class != ASN1_CLASS_UNIVERSAL || 1254189251Ssam hdr.tag != ASN1_TAG_INTEGER) { 1255189251Ssam wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for " 1256189251Ssam "version field - found class %d tag 0x%x", 1257189251Ssam hdr.class, hdr.tag); 1258189251Ssam return -1; 1259189251Ssam } 1260189251Ssam if (hdr.length != 1) { 1261189251Ssam wpa_printf(MSG_DEBUG, "X509: Unexpected version field " 1262189251Ssam "length %u (expected 1)", hdr.length); 1263189251Ssam return -1; 1264189251Ssam } 1265189251Ssam pos = hdr.payload; 1266189251Ssam left = hdr.length; 1267189251Ssam value = 0; 1268189251Ssam while (left) { 1269189251Ssam value <<= 8; 1270189251Ssam value |= *pos++; 1271189251Ssam left--; 1272189251Ssam } 1273189251Ssam 1274189251Ssam cert->version = value; 1275189251Ssam if (cert->version != X509_CERT_V1 && 1276189251Ssam cert->version != X509_CERT_V2 && 1277189251Ssam cert->version != X509_CERT_V3) { 1278189251Ssam wpa_printf(MSG_DEBUG, "X509: Unsupported version %d", 1279189251Ssam cert->version + 1); 1280189251Ssam return -1; 1281189251Ssam } 1282189251Ssam 1283189251Ssam if (asn1_get_next(pos, end - pos, &hdr) < 0) 1284189251Ssam return -1; 1285189251Ssam } else 1286189251Ssam cert->version = X509_CERT_V1; 1287189251Ssam wpa_printf(MSG_MSGDUMP, "X509: Version X.509v%d", cert->version + 1); 1288189251Ssam 1289189251Ssam /* serialNumber CertificateSerialNumber ::= INTEGER */ 1290189251Ssam if (hdr.class != ASN1_CLASS_UNIVERSAL || 1291189251Ssam hdr.tag != ASN1_TAG_INTEGER) { 1292189251Ssam wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for " 1293189251Ssam "serialNumber; class=%d tag=0x%x", 1294189251Ssam hdr.class, hdr.tag); 1295189251Ssam return -1; 1296189251Ssam } 1297189251Ssam 1298189251Ssam pos = hdr.payload; 1299189251Ssam left = hdr.length; 1300189251Ssam while (left) { 1301189251Ssam cert->serial_number <<= 8; 1302189251Ssam cert->serial_number |= *pos++; 1303189251Ssam left--; 1304189251Ssam } 1305189251Ssam wpa_printf(MSG_MSGDUMP, "X509: serialNumber %lu", cert->serial_number); 1306189251Ssam 1307189251Ssam /* signature AlgorithmIdentifier */ 1308189251Ssam if (x509_parse_algorithm_identifier(pos, end - pos, &cert->signature, 1309189251Ssam &pos)) 1310189251Ssam return -1; 1311189251Ssam 1312189251Ssam /* issuer Name */ 1313189251Ssam if (x509_parse_name(pos, end - pos, &cert->issuer, &pos)) 1314189251Ssam return -1; 1315189251Ssam x509_name_string(&cert->issuer, sbuf, sizeof(sbuf)); 1316189251Ssam wpa_printf(MSG_MSGDUMP, "X509: issuer %s", sbuf); 1317189251Ssam 1318189251Ssam /* validity Validity */ 1319189251Ssam if (x509_parse_validity(pos, end - pos, cert, &pos)) 1320189251Ssam return -1; 1321189251Ssam 1322189251Ssam /* subject Name */ 1323189251Ssam if (x509_parse_name(pos, end - pos, &cert->subject, &pos)) 1324189251Ssam return -1; 1325189251Ssam x509_name_string(&cert->subject, sbuf, sizeof(sbuf)); 1326189251Ssam wpa_printf(MSG_MSGDUMP, "X509: subject %s", sbuf); 1327189251Ssam 1328189251Ssam /* subjectPublicKeyInfo SubjectPublicKeyInfo */ 1329189251Ssam if (x509_parse_public_key(pos, end - pos, cert, &pos)) 1330189251Ssam return -1; 1331189251Ssam 1332189251Ssam if (pos == end) 1333189251Ssam return 0; 1334189251Ssam 1335189251Ssam if (cert->version == X509_CERT_V1) 1336189251Ssam return 0; 1337189251Ssam 1338189251Ssam if (asn1_get_next(pos, end - pos, &hdr) < 0 || 1339189251Ssam hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { 1340189251Ssam wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific" 1341189251Ssam " tag to parse optional tbsCertificate " 1342189251Ssam "field(s); parsed class %d tag 0x%x", 1343189251Ssam hdr.class, hdr.tag); 1344189251Ssam return -1; 1345189251Ssam } 1346189251Ssam 1347189251Ssam if (hdr.tag == 1) { 1348189251Ssam /* issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL */ 1349189251Ssam wpa_printf(MSG_DEBUG, "X509: issuerUniqueID"); 1350189251Ssam /* TODO: parse UniqueIdentifier ::= BIT STRING */ 1351189251Ssam 1352189251Ssam if (hdr.payload + hdr.length == end) 1353189251Ssam return 0; 1354189251Ssam 1355189251Ssam if (asn1_get_next(pos, end - pos, &hdr) < 0 || 1356189251Ssam hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { 1357189251Ssam wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific" 1358189251Ssam " tag to parse optional tbsCertificate " 1359189251Ssam "field(s); parsed class %d tag 0x%x", 1360189251Ssam hdr.class, hdr.tag); 1361189251Ssam return -1; 1362189251Ssam } 1363189251Ssam } 1364189251Ssam 1365189251Ssam if (hdr.tag == 2) { 1366189251Ssam /* subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL */ 1367189251Ssam wpa_printf(MSG_DEBUG, "X509: subjectUniqueID"); 1368189251Ssam /* TODO: parse UniqueIdentifier ::= BIT STRING */ 1369189251Ssam 1370189251Ssam if (hdr.payload + hdr.length == end) 1371189251Ssam return 0; 1372189251Ssam 1373189251Ssam if (asn1_get_next(pos, end - pos, &hdr) < 0 || 1374189251Ssam hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) { 1375189251Ssam wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific" 1376189251Ssam " tag to parse optional tbsCertificate " 1377189251Ssam "field(s); parsed class %d tag 0x%x", 1378189251Ssam hdr.class, hdr.tag); 1379189251Ssam return -1; 1380189251Ssam } 1381189251Ssam } 1382189251Ssam 1383189251Ssam if (hdr.tag != 3) { 1384189251Ssam wpa_printf(MSG_DEBUG, "X509: Ignored unexpected " 1385189251Ssam "Context-Specific tag %d in optional " 1386189251Ssam "tbsCertificate fields", hdr.tag); 1387189251Ssam return 0; 1388189251Ssam } 1389189251Ssam 1390189251Ssam /* extensions [3] EXPLICIT Extensions OPTIONAL */ 1391189251Ssam 1392189251Ssam if (cert->version != X509_CERT_V3) { 1393189251Ssam wpa_printf(MSG_DEBUG, "X509: X.509%d certificate and " 1394189251Ssam "Extensions data which are only allowed for " 1395189251Ssam "version 3", cert->version + 1); 1396189251Ssam return -1; 1397189251Ssam } 1398189251Ssam 1399189251Ssam if (x509_parse_extensions(cert, hdr.payload, hdr.length) < 0) 1400189251Ssam return -1; 1401189251Ssam 1402189251Ssam pos = hdr.payload + hdr.length; 1403189251Ssam if (pos < end) { 1404189251Ssam wpa_hexdump(MSG_DEBUG, 1405189251Ssam "X509: Ignored extra tbsCertificate data", 1406189251Ssam pos, end - pos); 1407189251Ssam } 1408189251Ssam 1409189251Ssam return 0; 1410189251Ssam} 1411189251Ssam 1412189251Ssam 1413189251Ssamstatic int x509_rsadsi_oid(struct asn1_oid *oid) 1414189251Ssam{ 1415189251Ssam return oid->len >= 4 && 1416189251Ssam oid->oid[0] == 1 /* iso */ && 1417189251Ssam oid->oid[1] == 2 /* member-body */ && 1418189251Ssam oid->oid[2] == 840 /* us */ && 1419189251Ssam oid->oid[3] == 113549 /* rsadsi */; 1420189251Ssam} 1421189251Ssam 1422189251Ssam 1423189251Ssamstatic int x509_pkcs_oid(struct asn1_oid *oid) 1424189251Ssam{ 1425189251Ssam return oid->len >= 5 && 1426189251Ssam x509_rsadsi_oid(oid) && 1427189251Ssam oid->oid[4] == 1 /* pkcs */; 1428189251Ssam} 1429189251Ssam 1430189251Ssam 1431189251Ssamstatic int x509_digest_oid(struct asn1_oid *oid) 1432189251Ssam{ 1433189251Ssam return oid->len >= 5 && 1434189251Ssam x509_rsadsi_oid(oid) && 1435189251Ssam oid->oid[4] == 2 /* digestAlgorithm */; 1436189251Ssam} 1437189251Ssam 1438189251Ssam 1439189251Ssamstatic int x509_sha1_oid(struct asn1_oid *oid) 1440189251Ssam{ 1441189251Ssam return oid->len == 6 && 1442189251Ssam oid->oid[0] == 1 /* iso */ && 1443189251Ssam oid->oid[1] == 3 /* identified-organization */ && 1444189251Ssam oid->oid[2] == 14 /* oiw */ && 1445189251Ssam oid->oid[3] == 3 /* secsig */ && 1446189251Ssam oid->oid[4] == 2 /* algorithms */ && 1447189251Ssam oid->oid[5] == 26 /* id-sha1 */; 1448189251Ssam} 1449189251Ssam 1450189251Ssam 1451189251Ssamstatic int x509_sha256_oid(struct asn1_oid *oid) 1452189251Ssam{ 1453189251Ssam return oid->len == 9 && 1454189251Ssam oid->oid[0] == 2 /* joint-iso-itu-t */ && 1455189251Ssam oid->oid[1] == 16 /* country */ && 1456189251Ssam oid->oid[2] == 840 /* us */ && 1457189251Ssam oid->oid[3] == 1 /* organization */ && 1458189251Ssam oid->oid[4] == 101 /* gov */ && 1459189251Ssam oid->oid[5] == 3 /* csor */ && 1460189251Ssam oid->oid[6] == 4 /* nistAlgorithm */ && 1461189251Ssam oid->oid[7] == 2 /* hashAlgs */ && 1462189251Ssam oid->oid[8] == 1 /* sha256 */; 1463189251Ssam} 1464189251Ssam 1465189251Ssam 1466189251Ssam/** 1467189251Ssam * x509_certificate_parse - Parse a X.509 certificate in DER format 1468189251Ssam * @buf: Pointer to the X.509 certificate in DER format 1469189251Ssam * @len: Buffer length 1470189251Ssam * Returns: Pointer to the parsed certificate or %NULL on failure 1471189251Ssam * 1472189251Ssam * Caller is responsible for freeing the returned certificate by calling 1473189251Ssam * x509_certificate_free(). 1474189251Ssam */ 1475189251Ssamstruct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len) 1476189251Ssam{ 1477189251Ssam struct asn1_hdr hdr; 1478189251Ssam const u8 *pos, *end, *hash_start; 1479189251Ssam struct x509_certificate *cert; 1480189251Ssam 1481189251Ssam cert = os_zalloc(sizeof(*cert) + len); 1482189251Ssam if (cert == NULL) 1483189251Ssam return NULL; 1484189251Ssam os_memcpy(cert + 1, buf, len); 1485189251Ssam cert->cert_start = (u8 *) (cert + 1); 1486189251Ssam cert->cert_len = len; 1487189251Ssam 1488189251Ssam pos = buf; 1489189251Ssam end = buf + len; 1490189251Ssam 1491189251Ssam /* RFC 3280 - X.509 v3 certificate / ASN.1 DER */ 1492189251Ssam 1493189251Ssam /* Certificate ::= SEQUENCE */ 1494189251Ssam if (asn1_get_next(pos, len, &hdr) < 0 || 1495189251Ssam hdr.class != ASN1_CLASS_UNIVERSAL || 1496189251Ssam hdr.tag != ASN1_TAG_SEQUENCE) { 1497189251Ssam wpa_printf(MSG_DEBUG, "X509: Certificate did not start with " 1498189251Ssam "a valid SEQUENCE - found class %d tag 0x%x", 1499189251Ssam hdr.class, hdr.tag); 1500189251Ssam x509_certificate_free(cert); 1501189251Ssam return NULL; 1502189251Ssam } 1503189251Ssam pos = hdr.payload; 1504189251Ssam 1505189251Ssam if (pos + hdr.length > end) { 1506189251Ssam x509_certificate_free(cert); 1507189251Ssam return NULL; 1508189251Ssam } 1509189251Ssam 1510189251Ssam if (pos + hdr.length < end) { 1511189251Ssam wpa_hexdump(MSG_MSGDUMP, "X509: Ignoring extra data after DER " 1512189251Ssam "encoded certificate", 1513189251Ssam pos + hdr.length, end - pos + hdr.length); 1514189251Ssam end = pos + hdr.length; 1515189251Ssam } 1516189251Ssam 1517189251Ssam hash_start = pos; 1518189251Ssam cert->tbs_cert_start = cert->cert_start + (hash_start - buf); 1519189251Ssam if (x509_parse_tbs_certificate(pos, end - pos, cert, &pos)) { 1520189251Ssam x509_certificate_free(cert); 1521189251Ssam return NULL; 1522189251Ssam } 1523189251Ssam cert->tbs_cert_len = pos - hash_start; 1524189251Ssam 1525189251Ssam /* signatureAlgorithm AlgorithmIdentifier */ 1526189251Ssam if (x509_parse_algorithm_identifier(pos, end - pos, 1527189251Ssam &cert->signature_alg, &pos)) { 1528189251Ssam x509_certificate_free(cert); 1529189251Ssam return NULL; 1530189251Ssam } 1531189251Ssam 1532189251Ssam /* signatureValue BIT STRING */ 1533189251Ssam if (asn1_get_next(pos, end - pos, &hdr) < 0 || 1534189251Ssam hdr.class != ASN1_CLASS_UNIVERSAL || 1535189251Ssam hdr.tag != ASN1_TAG_BITSTRING) { 1536189251Ssam wpa_printf(MSG_DEBUG, "X509: Expected BITSTRING " 1537189251Ssam "(signatureValue) - found class %d tag 0x%x", 1538189251Ssam hdr.class, hdr.tag); 1539189251Ssam x509_certificate_free(cert); 1540189251Ssam return NULL; 1541189251Ssam } 1542189251Ssam if (hdr.length < 1) { 1543189251Ssam x509_certificate_free(cert); 1544189251Ssam return NULL; 1545189251Ssam } 1546189251Ssam pos = hdr.payload; 1547189251Ssam if (*pos) { 1548189251Ssam wpa_printf(MSG_DEBUG, "X509: BITSTRING - %d unused bits", 1549189251Ssam *pos); 1550189251Ssam /* PKCS #1 v1.5 10.2.1: 1551189251Ssam * It is an error if the length in bits of the signature S is 1552189251Ssam * not a multiple of eight. 1553189251Ssam */ 1554189251Ssam x509_certificate_free(cert); 1555189251Ssam return NULL; 1556189251Ssam } 1557189251Ssam os_free(cert->sign_value); 1558189251Ssam cert->sign_value = os_malloc(hdr.length - 1); 1559189251Ssam if (cert->sign_value == NULL) { 1560189251Ssam wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for " 1561189251Ssam "signatureValue"); 1562189251Ssam x509_certificate_free(cert); 1563189251Ssam return NULL; 1564189251Ssam } 1565189251Ssam os_memcpy(cert->sign_value, pos + 1, hdr.length - 1); 1566189251Ssam cert->sign_value_len = hdr.length - 1; 1567189251Ssam wpa_hexdump(MSG_MSGDUMP, "X509: signature", 1568189251Ssam cert->sign_value, cert->sign_value_len); 1569189251Ssam 1570189251Ssam return cert; 1571189251Ssam} 1572189251Ssam 1573189251Ssam 1574189251Ssam/** 1575189251Ssam * x509_certificate_check_signature - Verify certificate signature 1576189251Ssam * @issuer: Issuer certificate 1577189251Ssam * @cert: Certificate to be verified 1578189251Ssam * Returns: 0 if cert has a valid signature that was signed by the issuer, 1579189251Ssam * -1 if not 1580189251Ssam */ 1581189251Ssamint x509_certificate_check_signature(struct x509_certificate *issuer, 1582189251Ssam struct x509_certificate *cert) 1583189251Ssam{ 1584189251Ssam struct crypto_public_key *pk; 1585189251Ssam u8 *data; 1586189251Ssam const u8 *pos, *end, *next, *da_end; 1587189251Ssam size_t data_len; 1588189251Ssam struct asn1_hdr hdr; 1589189251Ssam struct asn1_oid oid; 1590189251Ssam u8 hash[32]; 1591189251Ssam size_t hash_len; 1592189251Ssam 1593189251Ssam if (!x509_pkcs_oid(&cert->signature.oid) || 1594189251Ssam cert->signature.oid.len != 7 || 1595189251Ssam cert->signature.oid.oid[5] != 1 /* pkcs-1 */) { 1596189251Ssam wpa_printf(MSG_DEBUG, "X509: Unrecognized signature " 1597189251Ssam "algorithm"); 1598189251Ssam return -1; 1599189251Ssam } 1600189251Ssam 1601189251Ssam pk = crypto_public_key_import(issuer->public_key, 1602189251Ssam issuer->public_key_len); 1603189251Ssam if (pk == NULL) 1604189251Ssam return -1; 1605189251Ssam 1606189251Ssam data_len = cert->sign_value_len; 1607189251Ssam data = os_malloc(data_len); 1608189251Ssam if (data == NULL) { 1609189251Ssam crypto_public_key_free(pk); 1610189251Ssam return -1; 1611189251Ssam } 1612189251Ssam 1613189251Ssam if (crypto_public_key_decrypt_pkcs1(pk, cert->sign_value, 1614189251Ssam cert->sign_value_len, data, 1615189251Ssam &data_len) < 0) { 1616189251Ssam wpa_printf(MSG_DEBUG, "X509: Failed to decrypt signature"); 1617189251Ssam crypto_public_key_free(pk); 1618189251Ssam os_free(data); 1619189251Ssam return -1; 1620189251Ssam } 1621189251Ssam crypto_public_key_free(pk); 1622189251Ssam 1623189251Ssam wpa_hexdump(MSG_MSGDUMP, "X509: Signature data D", data, data_len); 1624189251Ssam 1625189251Ssam /* 1626189251Ssam * PKCS #1 v1.5, 10.1.2: 1627189251Ssam * 1628189251Ssam * DigestInfo ::= SEQUENCE { 1629189251Ssam * digestAlgorithm DigestAlgorithmIdentifier, 1630189251Ssam * digest Digest 1631189251Ssam * } 1632189251Ssam * 1633189251Ssam * DigestAlgorithmIdentifier ::= AlgorithmIdentifier 1634189251Ssam * 1635189251Ssam * Digest ::= OCTET STRING 1636189251Ssam * 1637189251Ssam */ 1638189251Ssam if (asn1_get_next(data, data_len, &hdr) < 0 || 1639189251Ssam hdr.class != ASN1_CLASS_UNIVERSAL || 1640189251Ssam hdr.tag != ASN1_TAG_SEQUENCE) { 1641189251Ssam wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " 1642189251Ssam "(DigestInfo) - found class %d tag 0x%x", 1643189251Ssam hdr.class, hdr.tag); 1644189251Ssam os_free(data); 1645189251Ssam return -1; 1646189251Ssam } 1647189251Ssam 1648189251Ssam pos = hdr.payload; 1649189251Ssam end = pos + hdr.length; 1650189251Ssam 1651189251Ssam /* 1652189251Ssam * X.509: 1653189251Ssam * AlgorithmIdentifier ::= SEQUENCE { 1654189251Ssam * algorithm OBJECT IDENTIFIER, 1655189251Ssam * parameters ANY DEFINED BY algorithm OPTIONAL 1656189251Ssam * } 1657189251Ssam */ 1658189251Ssam 1659189251Ssam if (asn1_get_next(pos, end - pos, &hdr) < 0 || 1660189251Ssam hdr.class != ASN1_CLASS_UNIVERSAL || 1661189251Ssam hdr.tag != ASN1_TAG_SEQUENCE) { 1662189251Ssam wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE " 1663189251Ssam "(AlgorithmIdentifier) - found class %d tag 0x%x", 1664189251Ssam hdr.class, hdr.tag); 1665189251Ssam os_free(data); 1666189251Ssam return -1; 1667189251Ssam } 1668189251Ssam da_end = hdr.payload + hdr.length; 1669189251Ssam 1670189251Ssam if (asn1_get_oid(hdr.payload, hdr.length, &oid, &next)) { 1671189251Ssam wpa_printf(MSG_DEBUG, "X509: Failed to parse digestAlgorithm"); 1672189251Ssam os_free(data); 1673189251Ssam return -1; 1674189251Ssam } 1675189251Ssam 1676189251Ssam if (x509_sha1_oid(&oid)) { 1677189251Ssam if (cert->signature.oid.oid[6] != 1678189251Ssam 5 /* sha-1WithRSAEncryption */) { 1679189251Ssam wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA1 " 1680189251Ssam "does not match with certificate " 1681189251Ssam "signatureAlgorithm (%lu)", 1682189251Ssam cert->signature.oid.oid[6]); 1683189251Ssam os_free(data); 1684189251Ssam return -1; 1685189251Ssam } 1686189251Ssam goto skip_digest_oid; 1687189251Ssam } 1688189251Ssam 1689189251Ssam if (x509_sha256_oid(&oid)) { 1690189251Ssam if (cert->signature.oid.oid[6] != 1691189251Ssam 11 /* sha2561WithRSAEncryption */) { 1692189251Ssam wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA256 " 1693189251Ssam "does not match with certificate " 1694189251Ssam "signatureAlgorithm (%lu)", 1695189251Ssam cert->signature.oid.oid[6]); 1696189251Ssam os_free(data); 1697189251Ssam return -1; 1698189251Ssam } 1699189251Ssam goto skip_digest_oid; 1700189251Ssam } 1701189251Ssam 1702189251Ssam if (!x509_digest_oid(&oid)) { 1703189251Ssam wpa_printf(MSG_DEBUG, "X509: Unrecognized digestAlgorithm"); 1704189251Ssam os_free(data); 1705189251Ssam return -1; 1706189251Ssam } 1707189251Ssam switch (oid.oid[5]) { 1708189251Ssam case 5: /* md5 */ 1709189251Ssam if (cert->signature.oid.oid[6] != 4 /* md5WithRSAEncryption */) 1710189251Ssam { 1711189251Ssam wpa_printf(MSG_DEBUG, "X509: digestAlgorithm MD5 does " 1712189251Ssam "not match with certificate " 1713189251Ssam "signatureAlgorithm (%lu)", 1714189251Ssam cert->signature.oid.oid[6]); 1715189251Ssam os_free(data); 1716189251Ssam return -1; 1717189251Ssam } 1718189251Ssam break; 1719189251Ssam case 2: /* md2 */ 1720189251Ssam case 4: /* md4 */ 1721189251Ssam default: 1722189251Ssam wpa_printf(MSG_DEBUG, "X509: Unsupported digestAlgorithm " 1723189251Ssam "(%lu)", oid.oid[5]); 1724189251Ssam os_free(data); 1725189251Ssam return -1; 1726189251Ssam } 1727189251Ssam 1728189251Ssamskip_digest_oid: 1729189251Ssam /* Digest ::= OCTET STRING */ 1730189251Ssam pos = da_end; 1731189251Ssam end = data + data_len; 1732189251Ssam 1733189251Ssam if (asn1_get_next(pos, end - pos, &hdr) < 0 || 1734189251Ssam hdr.class != ASN1_CLASS_UNIVERSAL || 1735189251Ssam hdr.tag != ASN1_TAG_OCTETSTRING) { 1736189251Ssam wpa_printf(MSG_DEBUG, "X509: Expected OCTETSTRING " 1737189251Ssam "(Digest) - found class %d tag 0x%x", 1738189251Ssam hdr.class, hdr.tag); 1739189251Ssam os_free(data); 1740189251Ssam return -1; 1741189251Ssam } 1742189251Ssam wpa_hexdump(MSG_MSGDUMP, "X509: Decrypted Digest", 1743189251Ssam hdr.payload, hdr.length); 1744189251Ssam 1745189251Ssam switch (cert->signature.oid.oid[6]) { 1746189251Ssam case 4: /* md5WithRSAEncryption */ 1747189251Ssam md5_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, 1748189251Ssam hash); 1749189251Ssam hash_len = 16; 1750189251Ssam wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (MD5)", 1751189251Ssam hash, hash_len); 1752189251Ssam break; 1753189251Ssam case 5: /* sha-1WithRSAEncryption */ 1754189251Ssam sha1_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, 1755189251Ssam hash); 1756189251Ssam hash_len = 20; 1757189251Ssam wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA1)", 1758189251Ssam hash, hash_len); 1759189251Ssam break; 1760189251Ssam case 11: /* sha256WithRSAEncryption */ 1761189251Ssam sha256_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, 1762189251Ssam hash); 1763189251Ssam hash_len = 32; 1764189251Ssam wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA256)", 1765189251Ssam hash, hash_len); 1766189251Ssam break; 1767189251Ssam case 2: /* md2WithRSAEncryption */ 1768189251Ssam case 12: /* sha384WithRSAEncryption */ 1769189251Ssam case 13: /* sha512WithRSAEncryption */ 1770189251Ssam default: 1771189251Ssam wpa_printf(MSG_INFO, "X509: Unsupported certificate signature " 1772189251Ssam "algorithm (%lu)", cert->signature.oid.oid[6]); 1773189251Ssam os_free(data); 1774189251Ssam return -1; 1775189251Ssam } 1776189251Ssam 1777189251Ssam if (hdr.length != hash_len || 1778189251Ssam os_memcmp(hdr.payload, hash, hdr.length) != 0) { 1779189251Ssam wpa_printf(MSG_INFO, "X509: Certificate Digest does not match " 1780189251Ssam "with calculated tbsCertificate hash"); 1781189251Ssam os_free(data); 1782189251Ssam return -1; 1783189251Ssam } 1784189251Ssam 1785189251Ssam os_free(data); 1786189251Ssam 1787189251Ssam wpa_printf(MSG_DEBUG, "X509: Certificate Digest matches with " 1788189251Ssam "calculated tbsCertificate hash"); 1789189251Ssam 1790189251Ssam return 0; 1791189251Ssam} 1792189251Ssam 1793189251Ssam 1794189251Ssamstatic int x509_valid_issuer(const struct x509_certificate *cert) 1795189251Ssam{ 1796189251Ssam if ((cert->extensions_present & X509_EXT_BASIC_CONSTRAINTS) && 1797189251Ssam !cert->ca) { 1798189251Ssam wpa_printf(MSG_DEBUG, "X509: Non-CA certificate used as an " 1799189251Ssam "issuer"); 1800189251Ssam return -1; 1801189251Ssam } 1802189251Ssam 1803189251Ssam if (cert->version == X509_CERT_V3 && 1804189251Ssam !(cert->extensions_present & X509_EXT_BASIC_CONSTRAINTS)) { 1805189251Ssam wpa_printf(MSG_DEBUG, "X509: v3 CA certificate did not " 1806189251Ssam "include BasicConstraints extension"); 1807189251Ssam return -1; 1808189251Ssam } 1809189251Ssam 1810189251Ssam if ((cert->extensions_present & X509_EXT_KEY_USAGE) && 1811189251Ssam !(cert->key_usage & X509_KEY_USAGE_KEY_CERT_SIGN)) { 1812189251Ssam wpa_printf(MSG_DEBUG, "X509: Issuer certificate did not have " 1813189251Ssam "keyCertSign bit in Key Usage"); 1814189251Ssam return -1; 1815189251Ssam } 1816189251Ssam 1817189251Ssam return 0; 1818189251Ssam} 1819189251Ssam 1820189251Ssam 1821189251Ssam/** 1822189251Ssam * x509_certificate_chain_validate - Validate X.509 certificate chain 1823189251Ssam * @trusted: List of trusted certificates 1824189251Ssam * @chain: Certificate chain to be validated (first chain must be issued by 1825189251Ssam * signed by the second certificate in the chain and so on) 1826189251Ssam * @reason: Buffer for returning failure reason (X509_VALIDATE_*) 1827189251Ssam * Returns: 0 if chain is valid, -1 if not 1828189251Ssam */ 1829189251Ssamint x509_certificate_chain_validate(struct x509_certificate *trusted, 1830189251Ssam struct x509_certificate *chain, 1831252726Srpaulo int *reason, int disable_time_checks) 1832189251Ssam{ 1833189251Ssam long unsigned idx; 1834189251Ssam int chain_trusted = 0; 1835189251Ssam struct x509_certificate *cert, *trust; 1836189251Ssam char buf[128]; 1837189251Ssam struct os_time now; 1838189251Ssam 1839189251Ssam *reason = X509_VALIDATE_OK; 1840189251Ssam 1841189251Ssam wpa_printf(MSG_DEBUG, "X509: Validate certificate chain"); 1842189251Ssam os_get_time(&now); 1843189251Ssam 1844189251Ssam for (cert = chain, idx = 0; cert; cert = cert->next, idx++) { 1845189251Ssam x509_name_string(&cert->subject, buf, sizeof(buf)); 1846189251Ssam wpa_printf(MSG_DEBUG, "X509: %lu: %s", idx, buf); 1847189251Ssam 1848189251Ssam if (chain_trusted) 1849189251Ssam continue; 1850189251Ssam 1851252726Srpaulo if (!disable_time_checks && 1852252726Srpaulo ((unsigned long) now.sec < 1853252726Srpaulo (unsigned long) cert->not_before || 1854252726Srpaulo (unsigned long) now.sec > 1855252726Srpaulo (unsigned long) cert->not_after)) { 1856189251Ssam wpa_printf(MSG_INFO, "X509: Certificate not valid " 1857189251Ssam "(now=%lu not_before=%lu not_after=%lu)", 1858189251Ssam now.sec, cert->not_before, cert->not_after); 1859189251Ssam *reason = X509_VALIDATE_CERTIFICATE_EXPIRED; 1860189251Ssam return -1; 1861189251Ssam } 1862189251Ssam 1863189251Ssam if (cert->next) { 1864189251Ssam if (x509_name_compare(&cert->issuer, 1865189251Ssam &cert->next->subject) != 0) { 1866189251Ssam wpa_printf(MSG_DEBUG, "X509: Certificate " 1867189251Ssam "chain issuer name mismatch"); 1868189251Ssam x509_name_string(&cert->issuer, buf, 1869189251Ssam sizeof(buf)); 1870189251Ssam wpa_printf(MSG_DEBUG, "X509: cert issuer: %s", 1871189251Ssam buf); 1872189251Ssam x509_name_string(&cert->next->subject, buf, 1873189251Ssam sizeof(buf)); 1874189251Ssam wpa_printf(MSG_DEBUG, "X509: next cert " 1875189251Ssam "subject: %s", buf); 1876189251Ssam *reason = X509_VALIDATE_CERTIFICATE_UNKNOWN; 1877189251Ssam return -1; 1878189251Ssam } 1879189251Ssam 1880189251Ssam if (x509_valid_issuer(cert->next) < 0) { 1881189251Ssam *reason = X509_VALIDATE_BAD_CERTIFICATE; 1882189251Ssam return -1; 1883189251Ssam } 1884189251Ssam 1885189251Ssam if ((cert->next->extensions_present & 1886189251Ssam X509_EXT_PATH_LEN_CONSTRAINT) && 1887189251Ssam idx > cert->next->path_len_constraint) { 1888189251Ssam wpa_printf(MSG_DEBUG, "X509: pathLenConstraint" 1889189251Ssam " not met (idx=%lu issuer " 1890189251Ssam "pathLenConstraint=%lu)", idx, 1891189251Ssam cert->next->path_len_constraint); 1892189251Ssam *reason = X509_VALIDATE_BAD_CERTIFICATE; 1893189251Ssam return -1; 1894189251Ssam } 1895189251Ssam 1896189251Ssam if (x509_certificate_check_signature(cert->next, cert) 1897189251Ssam < 0) { 1898189251Ssam wpa_printf(MSG_DEBUG, "X509: Invalid " 1899189251Ssam "certificate signature within " 1900189251Ssam "chain"); 1901189251Ssam *reason = X509_VALIDATE_BAD_CERTIFICATE; 1902189251Ssam return -1; 1903189251Ssam } 1904189251Ssam } 1905189251Ssam 1906189251Ssam for (trust = trusted; trust; trust = trust->next) { 1907189251Ssam if (x509_name_compare(&cert->issuer, &trust->subject) 1908189251Ssam == 0) 1909189251Ssam break; 1910189251Ssam } 1911189251Ssam 1912189251Ssam if (trust) { 1913189251Ssam wpa_printf(MSG_DEBUG, "X509: Found issuer from the " 1914189251Ssam "list of trusted certificates"); 1915189251Ssam if (x509_valid_issuer(trust) < 0) { 1916189251Ssam *reason = X509_VALIDATE_BAD_CERTIFICATE; 1917189251Ssam return -1; 1918189251Ssam } 1919189251Ssam 1920189251Ssam if (x509_certificate_check_signature(trust, cert) < 0) 1921189251Ssam { 1922189251Ssam wpa_printf(MSG_DEBUG, "X509: Invalid " 1923189251Ssam "certificate signature"); 1924189251Ssam *reason = X509_VALIDATE_BAD_CERTIFICATE; 1925189251Ssam return -1; 1926189251Ssam } 1927189251Ssam 1928189251Ssam wpa_printf(MSG_DEBUG, "X509: Trusted certificate " 1929189251Ssam "found to complete the chain"); 1930189251Ssam chain_trusted = 1; 1931189251Ssam } 1932189251Ssam } 1933189251Ssam 1934189251Ssam if (!chain_trusted) { 1935189251Ssam wpa_printf(MSG_DEBUG, "X509: Did not find any of the issuers " 1936189251Ssam "from the list of trusted certificates"); 1937189251Ssam if (trusted) { 1938189251Ssam *reason = X509_VALIDATE_UNKNOWN_CA; 1939189251Ssam return -1; 1940189251Ssam } 1941189251Ssam wpa_printf(MSG_DEBUG, "X509: Certificate chain validation " 1942189251Ssam "disabled - ignore unknown CA issue"); 1943189251Ssam } 1944189251Ssam 1945189251Ssam wpa_printf(MSG_DEBUG, "X509: Certificate chain valid"); 1946189251Ssam 1947189251Ssam return 0; 1948189251Ssam} 1949189251Ssam 1950189251Ssam 1951189251Ssam/** 1952189251Ssam * x509_certificate_get_subject - Get a certificate based on Subject name 1953189251Ssam * @chain: Certificate chain to search through 1954189251Ssam * @name: Subject name to search for 1955189251Ssam * Returns: Pointer to the certificate with the given Subject name or 1956189251Ssam * %NULL on failure 1957189251Ssam */ 1958189251Ssamstruct x509_certificate * 1959189251Ssamx509_certificate_get_subject(struct x509_certificate *chain, 1960189251Ssam struct x509_name *name) 1961189251Ssam{ 1962189251Ssam struct x509_certificate *cert; 1963189251Ssam 1964189251Ssam for (cert = chain; cert; cert = cert->next) { 1965189251Ssam if (x509_name_compare(&cert->subject, name) == 0) 1966189251Ssam return cert; 1967189251Ssam } 1968189251Ssam return NULL; 1969189251Ssam} 1970189251Ssam 1971189251Ssam 1972189251Ssam/** 1973189251Ssam * x509_certificate_self_signed - Is the certificate self-signed? 1974189251Ssam * @cert: Certificate 1975189251Ssam * Returns: 1 if certificate is self-signed, 0 if not 1976189251Ssam */ 1977189251Ssamint x509_certificate_self_signed(struct x509_certificate *cert) 1978189251Ssam{ 1979189251Ssam return x509_name_compare(&cert->issuer, &cert->subject) == 0; 1980189251Ssam} 1981