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