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