1/* 2 * Copyright (c) 2005 - 2007 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#include "hx_locl.h" 35 36typedef enum { USE_PEM, USE_DER } outformat; 37 38struct ks_file { 39 hx509_certs certs; 40 char *fn; 41 outformat format; 42}; 43 44/* 45 * 46 */ 47 48static int 49parse_certificate(hx509_context context, const char *fn, 50 struct hx509_collector *c, 51 const hx509_pem_header *headers, 52 const void *data, size_t len, 53 const AlgorithmIdentifier *ai) 54{ 55 hx509_cert cert; 56 int ret; 57 58 ret = hx509_cert_init_data(context, data, len, &cert); 59 if (ret) 60 return ret; 61 62 ret = _hx509_collector_certs_add(context, c, cert); 63 hx509_cert_free(cert); 64 return ret; 65} 66 67static int 68try_decrypt(hx509_context context, 69 struct hx509_collector *collector, 70 const AlgorithmIdentifier *alg, 71 const EVP_CIPHER *c, 72 const void *ivdata, 73 const void *password, 74 size_t passwordlen, 75 const void *cipher, 76 size_t len) 77{ 78 heim_octet_string clear; 79 size_t keylen; 80 void *key; 81 int ret; 82 83 keylen = EVP_CIPHER_key_length(c); 84 85 key = malloc(keylen); 86 if (key == NULL) { 87 hx509_clear_error_string(context); 88 return ENOMEM; 89 } 90 91 ret = EVP_BytesToKey(c, EVP_md5(), ivdata, 92 password, passwordlen, 93 1, key, NULL); 94 if (ret <= 0) { 95 hx509_set_error_string(context, 0, HX509_CRYPTO_INTERNAL_ERROR, 96 "Failed to do string2key for private key"); 97 return HX509_CRYPTO_INTERNAL_ERROR; 98 } 99 100 clear.data = malloc(len); 101 if (clear.data == NULL) { 102 hx509_set_error_string(context, 0, ENOMEM, 103 "Out of memory to decrypt for private key"); 104 ret = ENOMEM; 105 goto out; 106 } 107 clear.length = len; 108 109 { 110 EVP_CIPHER_CTX ctx; 111 EVP_CIPHER_CTX_init(&ctx); 112 EVP_CipherInit_ex(&ctx, c, NULL, key, ivdata, 0); 113 EVP_Cipher(&ctx, clear.data, cipher, len); 114 EVP_CIPHER_CTX_cleanup(&ctx); 115 } 116 117 ret = _hx509_collector_private_key_add(context, 118 collector, 119 alg, 120 NULL, 121 &clear, 122 NULL); 123 124 memset(clear.data, 0, clear.length); 125 free(clear.data); 126out: 127 memset(key, 0, keylen); 128 free(key); 129 return ret; 130} 131 132static int 133parse_pkcs8_private_key(hx509_context context, const char *fn, 134 struct hx509_collector *c, 135 const hx509_pem_header *headers, 136 const void *data, size_t length, 137 const AlgorithmIdentifier *ai) 138{ 139 PKCS8PrivateKeyInfo ki; 140 heim_octet_string keydata; 141 142 int ret; 143 144 ret = decode_PKCS8PrivateKeyInfo(data, length, &ki, NULL); 145 if (ret) 146 return ret; 147 148 keydata.data = rk_UNCONST(data); 149 keydata.length = length; 150 151 ret = _hx509_collector_private_key_add(context, 152 c, 153 &ki.privateKeyAlgorithm, 154 NULL, 155 &ki.privateKey, 156 &keydata); 157 free_PKCS8PrivateKeyInfo(&ki); 158 return ret; 159} 160 161static int 162parse_pem_private_key(hx509_context context, const char *fn, 163 struct hx509_collector *c, 164 const hx509_pem_header *headers, 165 const void *data, size_t len, 166 const AlgorithmIdentifier *ai) 167{ 168 int ret = 0; 169 const char *enc; 170 171 enc = hx509_pem_find_header(headers, "Proc-Type"); 172 if (enc) { 173 const char *dek; 174 char *type, *iv; 175 ssize_t ssize, size; 176 void *ivdata; 177 const EVP_CIPHER *cipher; 178 const struct _hx509_password *pw; 179 hx509_lock lock; 180 int i, decrypted = 0; 181 182 lock = _hx509_collector_get_lock(c); 183 if (lock == NULL) { 184 hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP, 185 "Failed to get password for " 186 "password protected file %s", fn); 187 return HX509_ALG_NOT_SUPP; 188 } 189 190 if (strcmp(enc, "4,ENCRYPTED") != 0) { 191 hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, 192 "Private key encrypted in unknown method %s " 193 "in file", 194 enc, fn); 195 hx509_clear_error_string(context); 196 return HX509_PARSING_KEY_FAILED; 197 } 198 199 dek = hx509_pem_find_header(headers, "DEK-Info"); 200 if (dek == NULL) { 201 hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, 202 "Encrypted private key missing DEK-Info"); 203 return HX509_PARSING_KEY_FAILED; 204 } 205 206 type = strdup(dek); 207 if (type == NULL) { 208 hx509_clear_error_string(context); 209 return ENOMEM; 210 } 211 212 iv = strchr(type, ','); 213 if (iv == NULL) { 214 free(type); 215 hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, 216 "IV missing"); 217 return HX509_PARSING_KEY_FAILED; 218 } 219 220 *iv++ = '\0'; 221 222 size = strlen(iv); 223 ivdata = malloc(size); 224 if (ivdata == NULL) { 225 hx509_clear_error_string(context); 226 free(type); 227 return ENOMEM; 228 } 229 230 cipher = EVP_get_cipherbyname(type); 231 if (cipher == NULL) { 232 free(ivdata); 233 hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP, 234 "Private key encrypted with " 235 "unsupported cipher: %s", 236 type); 237 free(type); 238 return HX509_ALG_NOT_SUPP; 239 } 240 241#define PKCS5_SALT_LEN 8 242 243 ssize = hex_decode(iv, ivdata, size); 244 free(type); 245 type = NULL; 246 iv = NULL; 247 248 if (ssize < 0 || ssize < PKCS5_SALT_LEN || ssize < EVP_CIPHER_iv_length(cipher)) { 249 free(ivdata); 250 hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED, 251 "Salt have wrong length in " 252 "private key file"); 253 return HX509_PARSING_KEY_FAILED; 254 } 255 256 pw = _hx509_lock_get_passwords(lock); 257 if (pw != NULL) { 258 const void *password; 259 size_t passwordlen; 260 261 for (i = 0; i < pw->len; i++) { 262 password = pw->val[i]; 263 passwordlen = strlen(password); 264 265 ret = try_decrypt(context, c, ai, cipher, ivdata, 266 password, passwordlen, data, len); 267 if (ret == 0) { 268 decrypted = 1; 269 break; 270 } 271 } 272 } 273 if (!decrypted) { 274 hx509_prompt prompt; 275 char password[128]; 276 277 memset(&prompt, 0, sizeof(prompt)); 278 279 prompt.prompt = "Password for keyfile: "; 280 prompt.type = HX509_PROMPT_TYPE_PASSWORD; 281 prompt.reply.data = password; 282 prompt.reply.length = sizeof(password); 283 284 ret = hx509_lock_prompt(lock, &prompt); 285 if (ret == 0) 286 ret = try_decrypt(context, c, ai, cipher, ivdata, password, 287 strlen(password), data, len); 288 /* XXX add password to lock password collection ? */ 289 memset(password, 0, sizeof(password)); 290 } 291 free(ivdata); 292 293 } else { 294 heim_octet_string keydata; 295 296 keydata.data = rk_UNCONST(data); 297 keydata.length = len; 298 299 ret = _hx509_collector_private_key_add(context, c, ai, NULL, 300 &keydata, NULL); 301 } 302 303 return ret; 304} 305 306 307struct pem_formats { 308 const char *name; 309 int (*func)(hx509_context, const char *, struct hx509_collector *, 310 const hx509_pem_header *, const void *, size_t, 311 const AlgorithmIdentifier *); 312 const AlgorithmIdentifier *(*ai)(void); 313} formats[] = { 314 { "CERTIFICATE", parse_certificate, NULL }, 315 { "PRIVATE KEY", parse_pkcs8_private_key, NULL }, 316 { "RSA PRIVATE KEY", parse_pem_private_key, hx509_signature_rsa }, 317 { "EC PRIVATE KEY", parse_pem_private_key, hx509_signature_ecPublicKey } 318}; 319 320 321struct pem_ctx { 322 int flags; 323 struct hx509_collector *c; 324}; 325 326static int 327pem_func(hx509_context context, const char *type, 328 const hx509_pem_header *header, 329 const void *data, size_t len, void *ctx) 330{ 331 struct pem_ctx *pem_ctx = (struct pem_ctx*)ctx; 332 int ret = 0, j; 333 334 for (j = 0; j < sizeof(formats)/sizeof(formats[0]); j++) { 335 const char *q = formats[j].name; 336 if (strcasecmp(type, q) == 0) { 337 const AlgorithmIdentifier *ai = NULL; 338 if (formats[j].ai != NULL) 339 ai = (*formats[j].ai)(); 340 341 ret = (*formats[j].func)(context, NULL, pem_ctx->c, 342 header, data, len, ai); 343 if (ret && (pem_ctx->flags & HX509_CERTS_UNPROTECT_ALL)) { 344 hx509_set_error_string(context, HX509_ERROR_APPEND, ret, 345 "Failed parseing PEM format %s", type); 346 return ret; 347 } 348 break; 349 } 350 } 351 if (j == sizeof(formats)/sizeof(formats[0])) { 352 ret = HX509_UNSUPPORTED_OPERATION; 353 hx509_set_error_string(context, 0, ret, 354 "Found no matching PEM format for %s", type); 355 return ret; 356 } 357 return 0; 358} 359 360/* 361 * 362 */ 363 364static int 365file_init_common(hx509_context context, 366 hx509_certs certs, void **data, int flags, 367 const char *residue, hx509_lock lock, outformat format) 368{ 369 char *p, *pnext; 370 struct ks_file *f = NULL; 371 hx509_private_key *keys = NULL; 372 int ret; 373 struct pem_ctx pem_ctx; 374 375 pem_ctx.flags = flags; 376 pem_ctx.c = NULL; 377 378 *data = NULL; 379 380 if (lock == NULL) 381 lock = _hx509_empty_lock; 382 383 f = calloc(1, sizeof(*f)); 384 if (f == NULL) { 385 hx509_clear_error_string(context); 386 return ENOMEM; 387 } 388 f->format = format; 389 390 f->fn = strdup(residue); 391 if (f->fn == NULL) { 392 hx509_clear_error_string(context); 393 ret = ENOMEM; 394 goto out; 395 } 396 397 /* 398 * XXX this is broken, the function should parse the file before 399 * overwriting it 400 */ 401 402 if (flags & HX509_CERTS_CREATE) { 403 ret = hx509_certs_init(context, "MEMORY:ks-file-create", 404 0, lock, &f->certs); 405 if (ret) 406 goto out; 407 *data = f; 408 return 0; 409 } 410 411 ret = _hx509_collector_alloc(context, lock, &pem_ctx.c); 412 if (ret) 413 goto out; 414 415 for (p = f->fn; p != NULL; p = pnext) { 416 FILE *f; 417 418 pnext = strchr(p, ','); 419 if (pnext) 420 *pnext++ = '\0'; 421 422 423 if ((f = fopen(p, "r")) == NULL) { 424 ret = ENOENT; 425 hx509_set_error_string(context, 0, ret, 426 "Failed to open PEM file \"%s\": %s", 427 p, strerror(errno)); 428 goto out; 429 } 430 rk_cloexec_file(f); 431 432 ret = hx509_pem_read(context, f, pem_func, &pem_ctx); 433 fclose(f); 434 if (ret != 0 && ret != HX509_PARSING_KEY_FAILED) 435 goto out; 436 else if (ret == HX509_PARSING_KEY_FAILED) { 437 size_t length; 438 void *ptr; 439 int i; 440 441 ret = rk_undumpdata(p, &ptr, &length); 442 if (ret) { 443 hx509_clear_error_string(context); 444 goto out; 445 } 446 447 for (i = 0; i < sizeof(formats)/sizeof(formats[0]); i++) { 448 const AlgorithmIdentifier *ai = NULL; 449 if (formats[i].ai != NULL) 450 ai = (*formats[i].ai)(); 451 452 ret = (*formats[i].func)(context, p, pem_ctx.c, NULL, ptr, length, ai); 453 if (ret == 0) 454 break; 455 } 456 rk_xfree(ptr); 457 if (ret) { 458 hx509_clear_error_string(context); 459 goto out; 460 } 461 } 462 } 463 464 ret = _hx509_collector_collect_certs(context, pem_ctx.c, &f->certs); 465 if (ret) 466 goto out; 467 468 ret = _hx509_collector_collect_private_keys(context, pem_ctx.c, &keys); 469 if (ret == 0) { 470 int i; 471 472 for (i = 0; keys[i]; i++) 473 _hx509_certs_keys_add(context, f->certs, keys[i]); 474 _hx509_certs_keys_free(context, keys); 475 } 476 477out: 478 if (ret == 0) 479 *data = f; 480 else { 481 if (f->fn) 482 free(f->fn); 483 free(f); 484 } 485 if (pem_ctx.c) 486 _hx509_collector_free(pem_ctx.c); 487 488 return ret; 489} 490 491static int 492file_init_pem(hx509_context context, 493 hx509_certs certs, void **data, int flags, 494 const char *residue, hx509_lock lock) 495{ 496 return file_init_common(context, certs, data, flags, residue, lock, USE_PEM); 497} 498 499static int 500file_init_der(hx509_context context, 501 hx509_certs certs, void **data, int flags, 502 const char *residue, hx509_lock lock) 503{ 504 return file_init_common(context, certs, data, flags, residue, lock, USE_DER); 505} 506 507static int 508file_free(hx509_certs certs, void *data) 509{ 510 struct ks_file *f = data; 511 hx509_certs_free(&f->certs); 512 free(f->fn); 513 free(f); 514 return 0; 515} 516 517struct store_ctx { 518 FILE *f; 519 outformat format; 520}; 521 522static int 523store_func(hx509_context context, void *ctx, hx509_cert c) 524{ 525 struct store_ctx *sc = ctx; 526 heim_octet_string data; 527 int ret; 528 529 ret = hx509_cert_binary(context, c, &data); 530 if (ret) 531 return ret; 532 533 switch (sc->format) { 534 case USE_DER: 535 fwrite(data.data, data.length, 1, sc->f); 536 free(data.data); 537 break; 538 case USE_PEM: 539 hx509_pem_write(context, "CERTIFICATE", NULL, sc->f, 540 data.data, data.length); 541 free(data.data); 542 if (_hx509_cert_private_key_exportable(c)) { 543 hx509_private_key key = _hx509_cert_private_key(c); 544 ret = _hx509_private_key_export(context, key, &data); 545 if (ret) 546 break; 547 hx509_pem_write(context, _hx509_private_pem_name(key), NULL, sc->f, 548 data.data, data.length); 549 free(data.data); 550 } 551 break; 552 } 553 554 return 0; 555} 556 557static int 558file_store(hx509_context context, 559 hx509_certs certs, void *data, int flags, hx509_lock lock) 560{ 561 struct ks_file *f = data; 562 struct store_ctx sc; 563 int ret; 564 565 sc.f = fopen(f->fn, "w"); 566 if (sc.f == NULL) { 567 hx509_set_error_string(context, 0, ENOENT, 568 "Failed to open file %s for writing"); 569 return ENOENT; 570 } 571 rk_cloexec_file(sc.f); 572 sc.format = f->format; 573 574 ret = hx509_certs_iter(context, f->certs, store_func, &sc); 575 fclose(sc.f); 576 return ret; 577} 578 579static int 580file_add(hx509_context context, hx509_certs certs, void *data, hx509_cert c) 581{ 582 struct ks_file *f = data; 583 return hx509_certs_add(context, f->certs, c); 584} 585 586static int 587file_iter_start(hx509_context context, 588 hx509_certs certs, void *data, void **cursor) 589{ 590 struct ks_file *f = data; 591 return hx509_certs_start_seq(context, f->certs, cursor); 592} 593 594static int 595file_iter(hx509_context context, 596 hx509_certs certs, void *data, void *iter, hx509_cert *cert) 597{ 598 struct ks_file *f = data; 599 return hx509_certs_next_cert(context, f->certs, iter, cert); 600} 601 602static int 603file_iter_end(hx509_context context, 604 hx509_certs certs, 605 void *data, 606 void *cursor) 607{ 608 struct ks_file *f = data; 609 return hx509_certs_end_seq(context, f->certs, cursor); 610} 611 612static int 613file_getkeys(hx509_context context, 614 hx509_certs certs, 615 void *data, 616 hx509_private_key **keys) 617{ 618 struct ks_file *f = data; 619 return _hx509_certs_keys_get(context, f->certs, keys); 620} 621 622static int 623file_addkey(hx509_context context, 624 hx509_certs certs, 625 void *data, 626 hx509_private_key key) 627{ 628 struct ks_file *f = data; 629 return _hx509_certs_keys_add(context, f->certs, key); 630} 631 632static struct hx509_keyset_ops keyset_file = { 633 "FILE", 634 0, 635 file_init_pem, 636 file_store, 637 file_free, 638 file_add, 639 NULL, 640 file_iter_start, 641 file_iter, 642 file_iter_end, 643 NULL, 644 file_getkeys, 645 file_addkey 646}; 647 648static struct hx509_keyset_ops keyset_pemfile = { 649 "PEM-FILE", 650 0, 651 file_init_pem, 652 file_store, 653 file_free, 654 file_add, 655 NULL, 656 file_iter_start, 657 file_iter, 658 file_iter_end, 659 NULL, 660 file_getkeys, 661 file_addkey 662}; 663 664static struct hx509_keyset_ops keyset_derfile = { 665 "DER-FILE", 666 0, 667 file_init_der, 668 file_store, 669 file_free, 670 file_add, 671 NULL, 672 file_iter_start, 673 file_iter, 674 file_iter_end, 675 NULL, 676 file_getkeys, 677 file_addkey 678}; 679 680 681void 682_hx509_ks_file_register(hx509_context context) 683{ 684 _hx509_ks_register(context, &keyset_file); 685 _hx509_ks_register(context, &keyset_pemfile); 686 _hx509_ks_register(context, &keyset_derfile); 687} 688