1189251Ssam/* 2189251Ssam * TLSv1 credentials 3214734Srpaulo * Copyright (c) 2006-2009, 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" 12189251Ssam#include "base64.h" 13214734Srpaulo#include "crypto/crypto.h" 14189251Ssam#include "x509v3.h" 15189251Ssam#include "tlsv1_cred.h" 16189251Ssam 17189251Ssam 18189251Ssamstruct tlsv1_credentials * tlsv1_cred_alloc(void) 19189251Ssam{ 20189251Ssam struct tlsv1_credentials *cred; 21189251Ssam cred = os_zalloc(sizeof(*cred)); 22189251Ssam return cred; 23189251Ssam} 24189251Ssam 25189251Ssam 26189251Ssamvoid tlsv1_cred_free(struct tlsv1_credentials *cred) 27189251Ssam{ 28189251Ssam if (cred == NULL) 29189251Ssam return; 30189251Ssam 31189251Ssam x509_certificate_chain_free(cred->trusted_certs); 32189251Ssam x509_certificate_chain_free(cred->cert); 33189251Ssam crypto_private_key_free(cred->key); 34189251Ssam os_free(cred->dh_p); 35189251Ssam os_free(cred->dh_g); 36189251Ssam os_free(cred); 37189251Ssam} 38189251Ssam 39189251Ssam 40189251Ssamstatic int tlsv1_add_cert_der(struct x509_certificate **chain, 41189251Ssam const u8 *buf, size_t len) 42189251Ssam{ 43252726Srpaulo struct x509_certificate *cert, *p; 44189251Ssam char name[128]; 45189251Ssam 46189251Ssam cert = x509_certificate_parse(buf, len); 47189251Ssam if (cert == NULL) { 48189251Ssam wpa_printf(MSG_INFO, "TLSv1: %s - failed to parse certificate", 49189251Ssam __func__); 50189251Ssam return -1; 51189251Ssam } 52189251Ssam 53252726Srpaulo p = *chain; 54252726Srpaulo while (p && p->next) 55252726Srpaulo p = p->next; 56252726Srpaulo if (p && x509_name_compare(&cert->subject, &p->issuer) == 0) { 57252726Srpaulo /* 58252726Srpaulo * The new certificate is the issuer of the last certificate in 59252726Srpaulo * the chain - add the new certificate to the end. 60252726Srpaulo */ 61252726Srpaulo p->next = cert; 62252726Srpaulo } else { 63252726Srpaulo /* Add to the beginning of the chain */ 64252726Srpaulo cert->next = *chain; 65252726Srpaulo *chain = cert; 66252726Srpaulo } 67189251Ssam 68189251Ssam x509_name_string(&cert->subject, name, sizeof(name)); 69189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Added certificate: %s", name); 70189251Ssam 71189251Ssam return 0; 72189251Ssam} 73189251Ssam 74189251Ssam 75189251Ssamstatic const char *pem_cert_begin = "-----BEGIN CERTIFICATE-----"; 76189251Ssamstatic const char *pem_cert_end = "-----END CERTIFICATE-----"; 77214734Srpaulostatic const char *pem_key_begin = "-----BEGIN RSA PRIVATE KEY-----"; 78214734Srpaulostatic const char *pem_key_end = "-----END RSA PRIVATE KEY-----"; 79214734Srpaulostatic const char *pem_key2_begin = "-----BEGIN PRIVATE KEY-----"; 80214734Srpaulostatic const char *pem_key2_end = "-----END PRIVATE KEY-----"; 81214734Srpaulostatic const char *pem_key_enc_begin = "-----BEGIN ENCRYPTED PRIVATE KEY-----"; 82214734Srpaulostatic const char *pem_key_enc_end = "-----END ENCRYPTED PRIVATE KEY-----"; 83189251Ssam 84189251Ssam 85189251Ssamstatic const u8 * search_tag(const char *tag, const u8 *buf, size_t len) 86189251Ssam{ 87189251Ssam size_t i, plen; 88189251Ssam 89189251Ssam plen = os_strlen(tag); 90189251Ssam if (len < plen) 91189251Ssam return NULL; 92189251Ssam 93189251Ssam for (i = 0; i < len - plen; i++) { 94189251Ssam if (os_memcmp(buf + i, tag, plen) == 0) 95189251Ssam return buf + i; 96189251Ssam } 97189251Ssam 98189251Ssam return NULL; 99189251Ssam} 100189251Ssam 101189251Ssam 102189251Ssamstatic int tlsv1_add_cert(struct x509_certificate **chain, 103189251Ssam const u8 *buf, size_t len) 104189251Ssam{ 105189251Ssam const u8 *pos, *end; 106189251Ssam unsigned char *der; 107189251Ssam size_t der_len; 108189251Ssam 109189251Ssam pos = search_tag(pem_cert_begin, buf, len); 110189251Ssam if (!pos) { 111189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: No PEM certificate tag found - " 112189251Ssam "assume DER format"); 113189251Ssam return tlsv1_add_cert_der(chain, buf, len); 114189251Ssam } 115189251Ssam 116189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format certificate into " 117189251Ssam "DER format"); 118189251Ssam 119189251Ssam while (pos) { 120189251Ssam pos += os_strlen(pem_cert_begin); 121189251Ssam end = search_tag(pem_cert_end, pos, buf + len - pos); 122189251Ssam if (end == NULL) { 123189251Ssam wpa_printf(MSG_INFO, "TLSv1: Could not find PEM " 124189251Ssam "certificate end tag (%s)", pem_cert_end); 125189251Ssam return -1; 126189251Ssam } 127189251Ssam 128189251Ssam der = base64_decode(pos, end - pos, &der_len); 129189251Ssam if (der == NULL) { 130189251Ssam wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM " 131189251Ssam "certificate"); 132189251Ssam return -1; 133189251Ssam } 134189251Ssam 135189251Ssam if (tlsv1_add_cert_der(chain, der, der_len) < 0) { 136189251Ssam wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM " 137189251Ssam "certificate after DER conversion"); 138189251Ssam os_free(der); 139189251Ssam return -1; 140189251Ssam } 141189251Ssam 142189251Ssam os_free(der); 143189251Ssam 144189251Ssam end += os_strlen(pem_cert_end); 145189251Ssam pos = search_tag(pem_cert_begin, end, buf + len - end); 146189251Ssam } 147189251Ssam 148189251Ssam return 0; 149189251Ssam} 150189251Ssam 151189251Ssam 152189251Ssamstatic int tlsv1_set_cert_chain(struct x509_certificate **chain, 153189251Ssam const char *cert, const u8 *cert_blob, 154189251Ssam size_t cert_blob_len) 155189251Ssam{ 156189251Ssam if (cert_blob) 157189251Ssam return tlsv1_add_cert(chain, cert_blob, cert_blob_len); 158189251Ssam 159189251Ssam if (cert) { 160189251Ssam u8 *buf; 161189251Ssam size_t len; 162189251Ssam int ret; 163189251Ssam 164189251Ssam buf = (u8 *) os_readfile(cert, &len); 165189251Ssam if (buf == NULL) { 166189251Ssam wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'", 167189251Ssam cert); 168189251Ssam return -1; 169189251Ssam } 170189251Ssam 171189251Ssam ret = tlsv1_add_cert(chain, buf, len); 172189251Ssam os_free(buf); 173189251Ssam return ret; 174189251Ssam } 175189251Ssam 176189251Ssam return 0; 177189251Ssam} 178189251Ssam 179189251Ssam 180189251Ssam/** 181189251Ssam * tlsv1_set_ca_cert - Set trusted CA certificate(s) 182189251Ssam * @cred: TLSv1 credentials from tlsv1_cred_alloc() 183189251Ssam * @cert: File or reference name for X.509 certificate in PEM or DER format 184189251Ssam * @cert_blob: cert as inlined data or %NULL if not used 185189251Ssam * @cert_blob_len: ca_cert_blob length 186189251Ssam * @path: Path to CA certificates (not yet supported) 187189251Ssam * Returns: 0 on success, -1 on failure 188189251Ssam */ 189189251Ssamint tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert, 190189251Ssam const u8 *cert_blob, size_t cert_blob_len, 191189251Ssam const char *path) 192189251Ssam{ 193189251Ssam if (tlsv1_set_cert_chain(&cred->trusted_certs, cert, 194189251Ssam cert_blob, cert_blob_len) < 0) 195189251Ssam return -1; 196189251Ssam 197189251Ssam if (path) { 198189251Ssam /* TODO: add support for reading number of certificate files */ 199189251Ssam wpa_printf(MSG_INFO, "TLSv1: Use of CA certificate directory " 200189251Ssam "not yet supported"); 201189251Ssam return -1; 202189251Ssam } 203189251Ssam 204189251Ssam return 0; 205189251Ssam} 206189251Ssam 207189251Ssam 208189251Ssam/** 209189251Ssam * tlsv1_set_cert - Set certificate 210189251Ssam * @cred: TLSv1 credentials from tlsv1_cred_alloc() 211189251Ssam * @cert: File or reference name for X.509 certificate in PEM or DER format 212189251Ssam * @cert_blob: cert as inlined data or %NULL if not used 213189251Ssam * @cert_blob_len: cert_blob length 214189251Ssam * Returns: 0 on success, -1 on failure 215189251Ssam */ 216189251Ssamint tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert, 217189251Ssam const u8 *cert_blob, size_t cert_blob_len) 218189251Ssam{ 219189251Ssam return tlsv1_set_cert_chain(&cred->cert, cert, 220189251Ssam cert_blob, cert_blob_len); 221189251Ssam} 222189251Ssam 223189251Ssam 224214734Srpaulostatic struct crypto_private_key * tlsv1_set_key_pem(const u8 *key, size_t len) 225214734Srpaulo{ 226214734Srpaulo const u8 *pos, *end; 227214734Srpaulo unsigned char *der; 228214734Srpaulo size_t der_len; 229214734Srpaulo struct crypto_private_key *pkey; 230214734Srpaulo 231214734Srpaulo pos = search_tag(pem_key_begin, key, len); 232214734Srpaulo if (!pos) { 233214734Srpaulo pos = search_tag(pem_key2_begin, key, len); 234214734Srpaulo if (!pos) 235214734Srpaulo return NULL; 236214734Srpaulo pos += os_strlen(pem_key2_begin); 237214734Srpaulo end = search_tag(pem_key2_end, pos, key + len - pos); 238214734Srpaulo if (!end) 239214734Srpaulo return NULL; 240214734Srpaulo } else { 241252726Srpaulo const u8 *pos2; 242214734Srpaulo pos += os_strlen(pem_key_begin); 243214734Srpaulo end = search_tag(pem_key_end, pos, key + len - pos); 244214734Srpaulo if (!end) 245214734Srpaulo return NULL; 246252726Srpaulo pos2 = search_tag("Proc-Type: 4,ENCRYPTED", pos, end - pos); 247252726Srpaulo if (pos2) { 248252726Srpaulo wpa_printf(MSG_DEBUG, "TLSv1: Unsupported private key " 249252726Srpaulo "format (Proc-Type/DEK-Info)"); 250252726Srpaulo return NULL; 251252726Srpaulo } 252214734Srpaulo } 253214734Srpaulo 254214734Srpaulo der = base64_decode(pos, end - pos, &der_len); 255214734Srpaulo if (!der) 256214734Srpaulo return NULL; 257214734Srpaulo pkey = crypto_private_key_import(der, der_len, NULL); 258214734Srpaulo os_free(der); 259214734Srpaulo return pkey; 260214734Srpaulo} 261214734Srpaulo 262214734Srpaulo 263214734Srpaulostatic struct crypto_private_key * tlsv1_set_key_enc_pem(const u8 *key, 264214734Srpaulo size_t len, 265214734Srpaulo const char *passwd) 266214734Srpaulo{ 267214734Srpaulo const u8 *pos, *end; 268214734Srpaulo unsigned char *der; 269214734Srpaulo size_t der_len; 270214734Srpaulo struct crypto_private_key *pkey; 271214734Srpaulo 272214734Srpaulo if (passwd == NULL) 273214734Srpaulo return NULL; 274214734Srpaulo pos = search_tag(pem_key_enc_begin, key, len); 275214734Srpaulo if (!pos) 276214734Srpaulo return NULL; 277214734Srpaulo pos += os_strlen(pem_key_enc_begin); 278214734Srpaulo end = search_tag(pem_key_enc_end, pos, key + len - pos); 279214734Srpaulo if (!end) 280214734Srpaulo return NULL; 281214734Srpaulo 282214734Srpaulo der = base64_decode(pos, end - pos, &der_len); 283214734Srpaulo if (!der) 284214734Srpaulo return NULL; 285214734Srpaulo pkey = crypto_private_key_import(der, der_len, passwd); 286214734Srpaulo os_free(der); 287214734Srpaulo return pkey; 288214734Srpaulo} 289214734Srpaulo 290214734Srpaulo 291189251Ssamstatic int tlsv1_set_key(struct tlsv1_credentials *cred, 292214734Srpaulo const u8 *key, size_t len, const char *passwd) 293189251Ssam{ 294214734Srpaulo cred->key = crypto_private_key_import(key, len, passwd); 295214734Srpaulo if (cred->key == NULL) 296214734Srpaulo cred->key = tlsv1_set_key_pem(key, len); 297214734Srpaulo if (cred->key == NULL) 298214734Srpaulo cred->key = tlsv1_set_key_enc_pem(key, len, passwd); 299189251Ssam if (cred->key == NULL) { 300189251Ssam wpa_printf(MSG_INFO, "TLSv1: Failed to parse private key"); 301189251Ssam return -1; 302189251Ssam } 303189251Ssam return 0; 304189251Ssam} 305189251Ssam 306189251Ssam 307189251Ssam/** 308189251Ssam * tlsv1_set_private_key - Set private key 309189251Ssam * @cred: TLSv1 credentials from tlsv1_cred_alloc() 310189251Ssam * @private_key: File or reference name for the key in PEM or DER format 311189251Ssam * @private_key_passwd: Passphrase for decrypted private key, %NULL if no 312189251Ssam * passphrase is used. 313189251Ssam * @private_key_blob: private_key as inlined data or %NULL if not used 314189251Ssam * @private_key_blob_len: private_key_blob length 315189251Ssam * Returns: 0 on success, -1 on failure 316189251Ssam */ 317189251Ssamint tlsv1_set_private_key(struct tlsv1_credentials *cred, 318189251Ssam const char *private_key, 319189251Ssam const char *private_key_passwd, 320189251Ssam const u8 *private_key_blob, 321189251Ssam size_t private_key_blob_len) 322189251Ssam{ 323189251Ssam crypto_private_key_free(cred->key); 324189251Ssam cred->key = NULL; 325189251Ssam 326189251Ssam if (private_key_blob) 327189251Ssam return tlsv1_set_key(cred, private_key_blob, 328214734Srpaulo private_key_blob_len, 329214734Srpaulo private_key_passwd); 330189251Ssam 331189251Ssam if (private_key) { 332189251Ssam u8 *buf; 333189251Ssam size_t len; 334189251Ssam int ret; 335189251Ssam 336189251Ssam buf = (u8 *) os_readfile(private_key, &len); 337189251Ssam if (buf == NULL) { 338189251Ssam wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'", 339189251Ssam private_key); 340189251Ssam return -1; 341189251Ssam } 342189251Ssam 343214734Srpaulo ret = tlsv1_set_key(cred, buf, len, private_key_passwd); 344189251Ssam os_free(buf); 345189251Ssam return ret; 346189251Ssam } 347189251Ssam 348189251Ssam return 0; 349189251Ssam} 350189251Ssam 351189251Ssam 352189251Ssamstatic int tlsv1_set_dhparams_der(struct tlsv1_credentials *cred, 353189251Ssam const u8 *dh, size_t len) 354189251Ssam{ 355189251Ssam struct asn1_hdr hdr; 356189251Ssam const u8 *pos, *end; 357189251Ssam 358189251Ssam pos = dh; 359189251Ssam end = dh + len; 360189251Ssam 361189251Ssam /* 362189251Ssam * DHParameter ::= SEQUENCE { 363189251Ssam * prime INTEGER, -- p 364189251Ssam * base INTEGER, -- g 365189251Ssam * privateValueLength INTEGER OPTIONAL } 366189251Ssam */ 367189251Ssam 368189251Ssam /* DHParamer ::= SEQUENCE */ 369189251Ssam if (asn1_get_next(pos, len, &hdr) < 0 || 370189251Ssam hdr.class != ASN1_CLASS_UNIVERSAL || 371189251Ssam hdr.tag != ASN1_TAG_SEQUENCE) { 372189251Ssam wpa_printf(MSG_DEBUG, "DH: DH parameters did not start with a " 373189251Ssam "valid SEQUENCE - found class %d tag 0x%x", 374189251Ssam hdr.class, hdr.tag); 375189251Ssam return -1; 376189251Ssam } 377189251Ssam pos = hdr.payload; 378189251Ssam 379189251Ssam /* prime INTEGER */ 380189251Ssam if (asn1_get_next(pos, end - pos, &hdr) < 0) 381189251Ssam return -1; 382189251Ssam 383189251Ssam if (hdr.class != ASN1_CLASS_UNIVERSAL || 384189251Ssam hdr.tag != ASN1_TAG_INTEGER) { 385189251Ssam wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for p; " 386189251Ssam "class=%d tag=0x%x", hdr.class, hdr.tag); 387189251Ssam return -1; 388189251Ssam } 389189251Ssam 390189251Ssam wpa_hexdump(MSG_MSGDUMP, "DH: prime (p)", hdr.payload, hdr.length); 391189251Ssam if (hdr.length == 0) 392189251Ssam return -1; 393189251Ssam os_free(cred->dh_p); 394189251Ssam cred->dh_p = os_malloc(hdr.length); 395189251Ssam if (cred->dh_p == NULL) 396189251Ssam return -1; 397189251Ssam os_memcpy(cred->dh_p, hdr.payload, hdr.length); 398189251Ssam cred->dh_p_len = hdr.length; 399189251Ssam pos = hdr.payload + hdr.length; 400189251Ssam 401189251Ssam /* base INTEGER */ 402189251Ssam if (asn1_get_next(pos, end - pos, &hdr) < 0) 403189251Ssam return -1; 404189251Ssam 405189251Ssam if (hdr.class != ASN1_CLASS_UNIVERSAL || 406189251Ssam hdr.tag != ASN1_TAG_INTEGER) { 407189251Ssam wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for g; " 408189251Ssam "class=%d tag=0x%x", hdr.class, hdr.tag); 409189251Ssam return -1; 410189251Ssam } 411189251Ssam 412189251Ssam wpa_hexdump(MSG_MSGDUMP, "DH: base (g)", hdr.payload, hdr.length); 413189251Ssam if (hdr.length == 0) 414189251Ssam return -1; 415189251Ssam os_free(cred->dh_g); 416189251Ssam cred->dh_g = os_malloc(hdr.length); 417189251Ssam if (cred->dh_g == NULL) 418189251Ssam return -1; 419189251Ssam os_memcpy(cred->dh_g, hdr.payload, hdr.length); 420189251Ssam cred->dh_g_len = hdr.length; 421189251Ssam 422189251Ssam return 0; 423189251Ssam} 424189251Ssam 425189251Ssam 426189251Ssamstatic const char *pem_dhparams_begin = "-----BEGIN DH PARAMETERS-----"; 427189251Ssamstatic const char *pem_dhparams_end = "-----END DH PARAMETERS-----"; 428189251Ssam 429189251Ssam 430189251Ssamstatic int tlsv1_set_dhparams_blob(struct tlsv1_credentials *cred, 431189251Ssam const u8 *buf, size_t len) 432189251Ssam{ 433189251Ssam const u8 *pos, *end; 434189251Ssam unsigned char *der; 435189251Ssam size_t der_len; 436189251Ssam 437189251Ssam pos = search_tag(pem_dhparams_begin, buf, len); 438189251Ssam if (!pos) { 439189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: No PEM dhparams tag found - " 440189251Ssam "assume DER format"); 441189251Ssam return tlsv1_set_dhparams_der(cred, buf, len); 442189251Ssam } 443189251Ssam 444189251Ssam wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format dhparams into DER " 445189251Ssam "format"); 446189251Ssam 447189251Ssam pos += os_strlen(pem_dhparams_begin); 448189251Ssam end = search_tag(pem_dhparams_end, pos, buf + len - pos); 449189251Ssam if (end == NULL) { 450189251Ssam wpa_printf(MSG_INFO, "TLSv1: Could not find PEM dhparams end " 451189251Ssam "tag (%s)", pem_dhparams_end); 452189251Ssam return -1; 453189251Ssam } 454189251Ssam 455189251Ssam der = base64_decode(pos, end - pos, &der_len); 456189251Ssam if (der == NULL) { 457189251Ssam wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM dhparams"); 458189251Ssam return -1; 459189251Ssam } 460189251Ssam 461189251Ssam if (tlsv1_set_dhparams_der(cred, der, der_len) < 0) { 462189251Ssam wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM dhparams " 463189251Ssam "DER conversion"); 464189251Ssam os_free(der); 465189251Ssam return -1; 466189251Ssam } 467189251Ssam 468189251Ssam os_free(der); 469189251Ssam 470189251Ssam return 0; 471189251Ssam} 472189251Ssam 473189251Ssam 474189251Ssam/** 475189251Ssam * tlsv1_set_dhparams - Set Diffie-Hellman parameters 476189251Ssam * @cred: TLSv1 credentials from tlsv1_cred_alloc() 477189251Ssam * @dh_file: File or reference name for the DH params in PEM or DER format 478189251Ssam * @dh_blob: DH params as inlined data or %NULL if not used 479189251Ssam * @dh_blob_len: dh_blob length 480189251Ssam * Returns: 0 on success, -1 on failure 481189251Ssam */ 482189251Ssamint tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file, 483189251Ssam const u8 *dh_blob, size_t dh_blob_len) 484189251Ssam{ 485189251Ssam if (dh_blob) 486189251Ssam return tlsv1_set_dhparams_blob(cred, dh_blob, dh_blob_len); 487189251Ssam 488189251Ssam if (dh_file) { 489189251Ssam u8 *buf; 490189251Ssam size_t len; 491189251Ssam int ret; 492189251Ssam 493189251Ssam buf = (u8 *) os_readfile(dh_file, &len); 494189251Ssam if (buf == NULL) { 495189251Ssam wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'", 496189251Ssam dh_file); 497189251Ssam return -1; 498189251Ssam } 499189251Ssam 500189251Ssam ret = tlsv1_set_dhparams_blob(cred, buf, len); 501189251Ssam os_free(buf); 502189251Ssam return ret; 503189251Ssam } 504189251Ssam 505189251Ssam return 0; 506189251Ssam} 507