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