tlsv1_cred.c revision c5ec7f57ead87efa365800228aa0b09a12d9e6c4
1/* 2 * TLSv1 credentials 3 * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi> 4 * 5 * This software may be distributed under the terms of the BSD license. 6 * See README for more details. 7 */ 8 9#include "includes.h" 10 11#include "common.h" 12#include "base64.h" 13#include "crypto/crypto.h" 14#include "x509v3.h" 15#include "tlsv1_cred.h" 16 17 18struct tlsv1_credentials * tlsv1_cred_alloc(void) 19{ 20 struct tlsv1_credentials *cred; 21 cred = os_zalloc(sizeof(*cred)); 22 return cred; 23} 24 25 26void tlsv1_cred_free(struct tlsv1_credentials *cred) 27{ 28 if (cred == NULL) 29 return; 30 31 x509_certificate_chain_free(cred->trusted_certs); 32 x509_certificate_chain_free(cred->cert); 33 crypto_private_key_free(cred->key); 34 os_free(cred->dh_p); 35 os_free(cred->dh_g); 36 os_free(cred); 37} 38 39 40static int tlsv1_add_cert_der(struct x509_certificate **chain, 41 const u8 *buf, size_t len) 42{ 43 struct x509_certificate *cert, *p; 44 char name[128]; 45 46 cert = x509_certificate_parse(buf, len); 47 if (cert == NULL) { 48 wpa_printf(MSG_INFO, "TLSv1: %s - failed to parse certificate", 49 __func__); 50 return -1; 51 } 52 53 p = *chain; 54 while (p && p->next) 55 p = p->next; 56 if (p && x509_name_compare(&cert->subject, &p->issuer) == 0) { 57 /* 58 * The new certificate is the issuer of the last certificate in 59 * the chain - add the new certificate to the end. 60 */ 61 p->next = cert; 62 } else { 63 /* Add to the beginning of the chain */ 64 cert->next = *chain; 65 *chain = cert; 66 } 67 68 x509_name_string(&cert->subject, name, sizeof(name)); 69 wpa_printf(MSG_DEBUG, "TLSv1: Added certificate: %s", name); 70 71 return 0; 72} 73 74 75static const char *pem_cert_begin = "-----BEGIN CERTIFICATE-----"; 76static const char *pem_cert_end = "-----END CERTIFICATE-----"; 77static const char *pem_key_begin = "-----BEGIN RSA PRIVATE KEY-----"; 78static const char *pem_key_end = "-----END RSA PRIVATE KEY-----"; 79static const char *pem_key2_begin = "-----BEGIN PRIVATE KEY-----"; 80static const char *pem_key2_end = "-----END PRIVATE KEY-----"; 81static const char *pem_key_enc_begin = "-----BEGIN ENCRYPTED PRIVATE KEY-----"; 82static const char *pem_key_enc_end = "-----END ENCRYPTED PRIVATE KEY-----"; 83 84 85static const u8 * search_tag(const char *tag, const u8 *buf, size_t len) 86{ 87 size_t i, plen; 88 89 plen = os_strlen(tag); 90 if (len < plen) 91 return NULL; 92 93 for (i = 0; i < len - plen; i++) { 94 if (os_memcmp(buf + i, tag, plen) == 0) 95 return buf + i; 96 } 97 98 return NULL; 99} 100 101 102static int tlsv1_add_cert(struct x509_certificate **chain, 103 const u8 *buf, size_t len) 104{ 105 const u8 *pos, *end; 106 unsigned char *der; 107 size_t der_len; 108 109 pos = search_tag(pem_cert_begin, buf, len); 110 if (!pos) { 111 wpa_printf(MSG_DEBUG, "TLSv1: No PEM certificate tag found - " 112 "assume DER format"); 113 return tlsv1_add_cert_der(chain, buf, len); 114 } 115 116 wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format certificate into " 117 "DER format"); 118 119 while (pos) { 120 pos += os_strlen(pem_cert_begin); 121 end = search_tag(pem_cert_end, pos, buf + len - pos); 122 if (end == NULL) { 123 wpa_printf(MSG_INFO, "TLSv1: Could not find PEM " 124 "certificate end tag (%s)", pem_cert_end); 125 return -1; 126 } 127 128 der = base64_decode(pos, end - pos, &der_len); 129 if (der == NULL) { 130 wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM " 131 "certificate"); 132 return -1; 133 } 134 135 if (tlsv1_add_cert_der(chain, der, der_len) < 0) { 136 wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM " 137 "certificate after DER conversion"); 138 os_free(der); 139 return -1; 140 } 141 142 os_free(der); 143 144 end += os_strlen(pem_cert_end); 145 pos = search_tag(pem_cert_begin, end, buf + len - end); 146 } 147 148 return 0; 149} 150 151 152static int tlsv1_set_cert_chain(struct x509_certificate **chain, 153 const char *cert, const u8 *cert_blob, 154 size_t cert_blob_len) 155{ 156 if (cert_blob) 157 return tlsv1_add_cert(chain, cert_blob, cert_blob_len); 158 159 if (cert) { 160 u8 *buf; 161 size_t len; 162 int ret; 163 164 buf = (u8 *) os_readfile(cert, &len); 165 if (buf == NULL) { 166 wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'", 167 cert); 168 return -1; 169 } 170 171 ret = tlsv1_add_cert(chain, buf, len); 172 os_free(buf); 173 return ret; 174 } 175 176 return 0; 177} 178 179 180/** 181 * tlsv1_set_ca_cert - Set trusted CA certificate(s) 182 * @cred: TLSv1 credentials from tlsv1_cred_alloc() 183 * @cert: File or reference name for X.509 certificate in PEM or DER format 184 * @cert_blob: cert as inlined data or %NULL if not used 185 * @cert_blob_len: ca_cert_blob length 186 * @path: Path to CA certificates (not yet supported) 187 * Returns: 0 on success, -1 on failure 188 */ 189int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert, 190 const u8 *cert_blob, size_t cert_blob_len, 191 const char *path) 192{ 193 if (tlsv1_set_cert_chain(&cred->trusted_certs, cert, 194 cert_blob, cert_blob_len) < 0) 195 return -1; 196 197 if (path) { 198 /* TODO: add support for reading number of certificate files */ 199 wpa_printf(MSG_INFO, "TLSv1: Use of CA certificate directory " 200 "not yet supported"); 201 return -1; 202 } 203 204 return 0; 205} 206 207 208/** 209 * tlsv1_set_cert - Set certificate 210 * @cred: TLSv1 credentials from tlsv1_cred_alloc() 211 * @cert: File or reference name for X.509 certificate in PEM or DER format 212 * @cert_blob: cert as inlined data or %NULL if not used 213 * @cert_blob_len: cert_blob length 214 * Returns: 0 on success, -1 on failure 215 */ 216int tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert, 217 const u8 *cert_blob, size_t cert_blob_len) 218{ 219 return tlsv1_set_cert_chain(&cred->cert, cert, 220 cert_blob, cert_blob_len); 221} 222 223 224static struct crypto_private_key * tlsv1_set_key_pem(const u8 *key, size_t len) 225{ 226 const u8 *pos, *end; 227 unsigned char *der; 228 size_t der_len; 229 struct crypto_private_key *pkey; 230 231 pos = search_tag(pem_key_begin, key, len); 232 if (!pos) { 233 pos = search_tag(pem_key2_begin, key, len); 234 if (!pos) 235 return NULL; 236 pos += os_strlen(pem_key2_begin); 237 end = search_tag(pem_key2_end, pos, key + len - pos); 238 if (!end) 239 return NULL; 240 } else { 241 const u8 *pos2; 242 pos += os_strlen(pem_key_begin); 243 end = search_tag(pem_key_end, pos, key + len - pos); 244 if (!end) 245 return NULL; 246 pos2 = search_tag("Proc-Type: 4,ENCRYPTED", pos, end - pos); 247 if (pos2) { 248 wpa_printf(MSG_DEBUG, "TLSv1: Unsupported private key " 249 "format (Proc-Type/DEK-Info)"); 250 return NULL; 251 } 252 } 253 254 der = base64_decode(pos, end - pos, &der_len); 255 if (!der) 256 return NULL; 257 pkey = crypto_private_key_import(der, der_len, NULL); 258 os_free(der); 259 return pkey; 260} 261 262 263static struct crypto_private_key * tlsv1_set_key_enc_pem(const u8 *key, 264 size_t len, 265 const char *passwd) 266{ 267 const u8 *pos, *end; 268 unsigned char *der; 269 size_t der_len; 270 struct crypto_private_key *pkey; 271 272 if (passwd == NULL) 273 return NULL; 274 pos = search_tag(pem_key_enc_begin, key, len); 275 if (!pos) 276 return NULL; 277 pos += os_strlen(pem_key_enc_begin); 278 end = search_tag(pem_key_enc_end, pos, key + len - pos); 279 if (!end) 280 return NULL; 281 282 der = base64_decode(pos, end - pos, &der_len); 283 if (!der) 284 return NULL; 285 pkey = crypto_private_key_import(der, der_len, passwd); 286 os_free(der); 287 return pkey; 288} 289 290 291static int tlsv1_set_key(struct tlsv1_credentials *cred, 292 const u8 *key, size_t len, const char *passwd) 293{ 294 cred->key = crypto_private_key_import(key, len, passwd); 295 if (cred->key == NULL) 296 cred->key = tlsv1_set_key_pem(key, len); 297 if (cred->key == NULL) 298 cred->key = tlsv1_set_key_enc_pem(key, len, passwd); 299 if (cred->key == NULL) { 300 wpa_printf(MSG_INFO, "TLSv1: Failed to parse private key"); 301 return -1; 302 } 303 return 0; 304} 305 306 307/** 308 * tlsv1_set_private_key - Set private key 309 * @cred: TLSv1 credentials from tlsv1_cred_alloc() 310 * @private_key: File or reference name for the key in PEM or DER format 311 * @private_key_passwd: Passphrase for decrypted private key, %NULL if no 312 * passphrase is used. 313 * @private_key_blob: private_key as inlined data or %NULL if not used 314 * @private_key_blob_len: private_key_blob length 315 * Returns: 0 on success, -1 on failure 316 */ 317int tlsv1_set_private_key(struct tlsv1_credentials *cred, 318 const char *private_key, 319 const char *private_key_passwd, 320 const u8 *private_key_blob, 321 size_t private_key_blob_len) 322{ 323 crypto_private_key_free(cred->key); 324 cred->key = NULL; 325 326 if (private_key_blob) 327 return tlsv1_set_key(cred, private_key_blob, 328 private_key_blob_len, 329 private_key_passwd); 330 331 if (private_key) { 332 u8 *buf; 333 size_t len; 334 int ret; 335 336 buf = (u8 *) os_readfile(private_key, &len); 337 if (buf == NULL) { 338 wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'", 339 private_key); 340 return -1; 341 } 342 343 ret = tlsv1_set_key(cred, buf, len, private_key_passwd); 344 os_free(buf); 345 return ret; 346 } 347 348 return 0; 349} 350 351 352static int tlsv1_set_dhparams_der(struct tlsv1_credentials *cred, 353 const u8 *dh, size_t len) 354{ 355 struct asn1_hdr hdr; 356 const u8 *pos, *end; 357 358 pos = dh; 359 end = dh + len; 360 361 /* 362 * DHParameter ::= SEQUENCE { 363 * prime INTEGER, -- p 364 * base INTEGER, -- g 365 * privateValueLength INTEGER OPTIONAL } 366 */ 367 368 /* DHParamer ::= SEQUENCE */ 369 if (asn1_get_next(pos, len, &hdr) < 0 || 370 hdr.class != ASN1_CLASS_UNIVERSAL || 371 hdr.tag != ASN1_TAG_SEQUENCE) { 372 wpa_printf(MSG_DEBUG, "DH: DH parameters did not start with a " 373 "valid SEQUENCE - found class %d tag 0x%x", 374 hdr.class, hdr.tag); 375 return -1; 376 } 377 pos = hdr.payload; 378 379 /* prime INTEGER */ 380 if (asn1_get_next(pos, end - pos, &hdr) < 0) 381 return -1; 382 383 if (hdr.class != ASN1_CLASS_UNIVERSAL || 384 hdr.tag != ASN1_TAG_INTEGER) { 385 wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for p; " 386 "class=%d tag=0x%x", hdr.class, hdr.tag); 387 return -1; 388 } 389 390 wpa_hexdump(MSG_MSGDUMP, "DH: prime (p)", hdr.payload, hdr.length); 391 if (hdr.length == 0) 392 return -1; 393 os_free(cred->dh_p); 394 cred->dh_p = os_malloc(hdr.length); 395 if (cred->dh_p == NULL) 396 return -1; 397 os_memcpy(cred->dh_p, hdr.payload, hdr.length); 398 cred->dh_p_len = hdr.length; 399 pos = hdr.payload + hdr.length; 400 401 /* base INTEGER */ 402 if (asn1_get_next(pos, end - pos, &hdr) < 0) 403 return -1; 404 405 if (hdr.class != ASN1_CLASS_UNIVERSAL || 406 hdr.tag != ASN1_TAG_INTEGER) { 407 wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for g; " 408 "class=%d tag=0x%x", hdr.class, hdr.tag); 409 return -1; 410 } 411 412 wpa_hexdump(MSG_MSGDUMP, "DH: base (g)", hdr.payload, hdr.length); 413 if (hdr.length == 0) 414 return -1; 415 os_free(cred->dh_g); 416 cred->dh_g = os_malloc(hdr.length); 417 if (cred->dh_g == NULL) 418 return -1; 419 os_memcpy(cred->dh_g, hdr.payload, hdr.length); 420 cred->dh_g_len = hdr.length; 421 422 return 0; 423} 424 425 426static const char *pem_dhparams_begin = "-----BEGIN DH PARAMETERS-----"; 427static const char *pem_dhparams_end = "-----END DH PARAMETERS-----"; 428 429 430static int tlsv1_set_dhparams_blob(struct tlsv1_credentials *cred, 431 const u8 *buf, size_t len) 432{ 433 const u8 *pos, *end; 434 unsigned char *der; 435 size_t der_len; 436 437 pos = search_tag(pem_dhparams_begin, buf, len); 438 if (!pos) { 439 wpa_printf(MSG_DEBUG, "TLSv1: No PEM dhparams tag found - " 440 "assume DER format"); 441 return tlsv1_set_dhparams_der(cred, buf, len); 442 } 443 444 wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format dhparams into DER " 445 "format"); 446 447 pos += os_strlen(pem_dhparams_begin); 448 end = search_tag(pem_dhparams_end, pos, buf + len - pos); 449 if (end == NULL) { 450 wpa_printf(MSG_INFO, "TLSv1: Could not find PEM dhparams end " 451 "tag (%s)", pem_dhparams_end); 452 return -1; 453 } 454 455 der = base64_decode(pos, end - pos, &der_len); 456 if (der == NULL) { 457 wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM dhparams"); 458 return -1; 459 } 460 461 if (tlsv1_set_dhparams_der(cred, der, der_len) < 0) { 462 wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM dhparams " 463 "DER conversion"); 464 os_free(der); 465 return -1; 466 } 467 468 os_free(der); 469 470 return 0; 471} 472 473 474/** 475 * tlsv1_set_dhparams - Set Diffie-Hellman parameters 476 * @cred: TLSv1 credentials from tlsv1_cred_alloc() 477 * @dh_file: File or reference name for the DH params in PEM or DER format 478 * @dh_blob: DH params as inlined data or %NULL if not used 479 * @dh_blob_len: dh_blob length 480 * Returns: 0 on success, -1 on failure 481 */ 482int tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file, 483 const u8 *dh_blob, size_t dh_blob_len) 484{ 485 if (dh_blob) 486 return tlsv1_set_dhparams_blob(cred, dh_blob, dh_blob_len); 487 488 if (dh_file) { 489 u8 *buf; 490 size_t len; 491 int ret; 492 493 buf = (u8 *) os_readfile(dh_file, &len); 494 if (buf == NULL) { 495 wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'", 496 dh_file); 497 return -1; 498 } 499 500 ret = tlsv1_set_dhparams_blob(cred, buf, len); 501 os_free(buf); 502 return ret; 503 } 504 505 return 0; 506} 507