kafs: Fix a warning
[heimdal.git] / lib / hx509 / ks_file.c
blob880668b4561603a6a103c1c448c298fa57333278
1 /*
2 * Copyright (c) 2005 - 2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
34 #include "hx_locl.h"
35 #ifndef WIN32
36 #include <libgen.h>
37 #endif
39 typedef enum { USE_PEM, USE_DER } outformat;
41 struct ks_file {
42 hx509_certs certs;
43 char *fn;
44 outformat format;
51 static int
52 parse_certificate(hx509_context context, const char *fn, int flags,
53 struct hx509_collector *c,
54 const hx509_pem_header *headers,
55 const void *data, size_t len,
56 const AlgorithmIdentifier *ai)
58 heim_error_t error = NULL;
59 hx509_cert cert;
60 int ret;
62 cert = hx509_cert_init_data(context, data, len, &error);
63 if (cert == NULL) {
64 ret = heim_error_get_code(error);
65 heim_release(error);
66 return ret;
69 ret = _hx509_collector_certs_add(context, c, cert);
70 hx509_cert_free(cert);
71 return ret;
74 static int
75 try_decrypt(hx509_context context,
76 struct hx509_collector *collector,
77 int flags,
78 const AlgorithmIdentifier *alg,
79 const EVP_CIPHER *c,
80 const void *ivdata,
81 const void *password,
82 size_t passwordlen,
83 const void *cipher,
84 size_t len)
86 heim_octet_string clear;
87 size_t keylen;
88 void *key;
89 int ret;
91 keylen = EVP_CIPHER_key_length(c);
93 key = malloc(keylen);
94 if (key == NULL) {
95 hx509_clear_error_string(context);
96 return ENOMEM;
99 ret = EVP_BytesToKey(c, EVP_md5(), ivdata,
100 password, passwordlen,
101 1, key, NULL);
102 if (ret <= 0) {
103 ret = HX509_CRYPTO_INTERNAL_ERROR;
104 hx509_set_error_string(context, 0, ret,
105 "Failed to do string2key for private key");
106 goto out;
109 clear.data = malloc(len);
110 if (clear.data == NULL) {
111 hx509_set_error_string(context, 0, ENOMEM,
112 "Out of memory to decrypt for private key");
113 ret = ENOMEM;
114 goto out;
116 clear.length = len;
119 EVP_CIPHER_CTX ctx;
120 EVP_CIPHER_CTX_init(&ctx);
121 EVP_CipherInit_ex(&ctx, c, NULL, key, ivdata, 0);
122 EVP_Cipher(&ctx, clear.data, cipher, len);
123 EVP_CIPHER_CTX_cleanup(&ctx);
126 if (!(flags & HX509_CERTS_NO_PRIVATE_KEYS))
127 ret = _hx509_collector_private_key_add(context, collector, alg, NULL,
128 &clear, NULL);
130 memset_s(clear.data, clear.length, 0, clear.length);
131 free(clear.data);
132 out:
133 memset_s(key, keylen, 0, keylen);
134 free(key);
135 return ret;
138 static int
139 parse_pkcs8_private_key(hx509_context context, const char *fn, int flags,
140 struct hx509_collector *c,
141 const hx509_pem_header *headers,
142 const void *data, size_t length,
143 const AlgorithmIdentifier *ai)
145 PKCS8PrivateKeyInfo ki;
146 heim_octet_string keydata;
147 int ret;
149 ret = decode_PKCS8PrivateKeyInfo(data, length, &ki, NULL);
150 if (ret)
151 return ret;
153 if (!(flags & HX509_CERTS_NO_PRIVATE_KEYS)) {
154 keydata.data = rk_UNCONST(data);
155 keydata.length = length;
156 ret = _hx509_collector_private_key_add(context,
158 &ki.privateKeyAlgorithm,
159 NULL,
160 &ki.privateKey,
161 &keydata);
163 free_PKCS8PrivateKeyInfo(&ki);
164 return ret;
167 static int
168 parse_pem_private_key(hx509_context context, const char *fn, int flags,
169 struct hx509_collector *c,
170 const hx509_pem_header *headers,
171 const void *data, size_t len,
172 const AlgorithmIdentifier *ai)
174 int ret = 0;
175 const char *enc;
177 enc = hx509_pem_find_header(headers, "Proc-Type");
178 if (enc) {
179 const char *dek;
180 char *type, *iv;
181 ssize_t ssize, size;
182 void *ivdata;
183 const EVP_CIPHER *cipher;
184 const struct _hx509_password *pw;
185 hx509_lock lock;
186 int decrypted = 0;
187 size_t i;
189 lock = _hx509_collector_get_lock(c);
190 if (lock == NULL) {
191 hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP,
192 "Failed to get password for "
193 "password protected file %s", fn);
194 return HX509_ALG_NOT_SUPP;
197 if (strcmp(enc, "4,ENCRYPTED") != 0) {
198 hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
199 "Private key encrypted in unknown method %s "
200 "in file",
201 enc, fn);
202 hx509_clear_error_string(context);
203 return HX509_PARSING_KEY_FAILED;
206 dek = hx509_pem_find_header(headers, "DEK-Info");
207 if (dek == NULL) {
208 hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
209 "Encrypted private key missing DEK-Info");
210 return HX509_PARSING_KEY_FAILED;
213 type = strdup(dek);
214 if (type == NULL) {
215 hx509_clear_error_string(context);
216 return ENOMEM;
219 iv = strchr(type, ',');
220 if (iv == NULL) {
221 free(type);
222 hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
223 "IV missing");
224 return HX509_PARSING_KEY_FAILED;
227 *iv++ = '\0';
229 size = strlen(iv);
230 ivdata = malloc(size);
231 if (ivdata == NULL) {
232 hx509_clear_error_string(context);
233 free(type);
234 return ENOMEM;
237 cipher = EVP_get_cipherbyname(type);
238 if (cipher == NULL) {
239 free(ivdata);
240 hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP,
241 "Private key encrypted with "
242 "unsupported cipher: %s",
243 type);
244 free(type);
245 return HX509_ALG_NOT_SUPP;
248 #define PKCS5_SALT_LEN 8
250 ssize = hex_decode(iv, ivdata, size);
251 free(type);
252 type = NULL;
253 iv = NULL;
255 if (ssize < 0 || ssize < PKCS5_SALT_LEN || ssize < EVP_CIPHER_iv_length(cipher)) {
256 free(ivdata);
257 hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
258 "Salt have wrong length in "
259 "private key file");
260 return HX509_PARSING_KEY_FAILED;
263 pw = _hx509_lock_get_passwords(lock);
264 if (pw != NULL) {
265 const void *password;
266 size_t passwordlen;
268 for (i = 0; i < pw->len; i++) {
269 password = pw->val[i];
270 passwordlen = strlen(password);
272 ret = try_decrypt(context, c, flags, ai, cipher, ivdata,
273 password, passwordlen, data, len);
274 if (ret == 0) {
275 decrypted = 1;
276 break;
280 if (!decrypted) {
281 hx509_prompt prompt;
282 char password[128];
284 memset(&prompt, 0, sizeof(prompt));
286 prompt.prompt = "Password for keyfile: ";
287 prompt.type = HX509_PROMPT_TYPE_PASSWORD;
288 prompt.reply.data = password;
289 prompt.reply.length = sizeof(password);
291 ret = hx509_lock_prompt(lock, &prompt);
292 if (ret == 0)
293 ret = try_decrypt(context, c, flags, ai, cipher, ivdata,
294 password, strlen(password), data, len);
295 /* XXX add password to lock password collection ? */
296 memset_s(password, sizeof(password), 0, sizeof(password));
298 free(ivdata);
300 } else if (!(flags & HX509_CERTS_NO_PRIVATE_KEYS)) {
301 heim_octet_string keydata;
303 keydata.data = rk_UNCONST(data);
304 keydata.length = len;
306 ret = _hx509_collector_private_key_add(context, c, ai, NULL,
307 &keydata, NULL);
310 return ret;
314 struct pem_formats {
315 const char *name;
316 int (*func)(hx509_context, const char *, int, struct hx509_collector *,
317 const hx509_pem_header *, const void *, size_t,
318 const AlgorithmIdentifier *);
319 const AlgorithmIdentifier *(*ai)(void);
320 } formats[] = {
321 { "CERTIFICATE", parse_certificate, NULL },
322 { "PRIVATE KEY", parse_pkcs8_private_key, NULL },
323 { "RSA PRIVATE KEY", parse_pem_private_key, hx509_signature_rsa },
324 #ifdef HAVE_HCRYPTO_W_OPENSSL
325 { "EC PRIVATE KEY", parse_pem_private_key, hx509_signature_ecPublicKey }
326 #endif
330 struct pem_ctx {
331 int flags;
332 struct hx509_collector *c;
335 static int
336 pem_func(hx509_context context, const char *type,
337 const hx509_pem_header *header,
338 const void *data, size_t len, void *ctx)
340 struct pem_ctx *pem_ctx = (struct pem_ctx*)ctx;
341 int ret = 0;
342 size_t j;
344 for (j = 0; j < sizeof(formats)/sizeof(formats[0]); j++) {
345 const char *q = formats[j].name;
346 if (strcasecmp(type, q) == 0) {
347 const AlgorithmIdentifier *ai = NULL;
349 if (formats[j].ai != NULL)
350 ai = (*formats[j].ai)();
352 ret = (*formats[j].func)(context, NULL, pem_ctx->flags, pem_ctx->c,
353 header, data, len, ai);
354 if (ret && (pem_ctx->flags & HX509_CERTS_UNPROTECT_ALL)) {
355 hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
356 "Failed parseing PEM format %s", type);
357 return ret;
359 break;
362 if (j == sizeof(formats)/sizeof(formats[0])) {
363 ret = HX509_UNSUPPORTED_OPERATION;
364 hx509_set_error_string(context, 0, ret,
365 "Found no matching PEM format for %s", type);
366 return ret;
368 return 0;
375 static int
376 file_init_common(hx509_context context,
377 hx509_certs certs, void **data, int flags,
378 const char *residue, hx509_lock lock, outformat format)
380 char *p, *pnext;
381 struct ks_file *ksf = NULL;
382 hx509_private_key *keys = NULL;
383 int ret;
384 struct pem_ctx pem_ctx;
386 pem_ctx.flags = flags;
387 pem_ctx.c = NULL;
389 if (residue == NULL || residue[0] == '\0') {
390 hx509_set_error_string(context, 0, EINVAL,
391 "PEM file name not specified");
392 return EINVAL;
395 *data = NULL;
397 if (lock == NULL)
398 lock = _hx509_empty_lock;
400 ksf = calloc(1, sizeof(*ksf));
401 if (ksf == NULL) {
402 hx509_clear_error_string(context);
403 return ENOMEM;
405 ksf->format = format;
407 ksf->fn = strdup(residue);
408 if (ksf->fn == NULL) {
409 hx509_clear_error_string(context);
410 ret = ENOMEM;
411 goto out;
415 * XXX this is broken, the function should parse the file before
416 * overwriting it
419 if (flags & HX509_CERTS_CREATE) {
421 * Note that the file creation is deferred until file_store() is
422 * called.
424 ret = hx509_certs_init(context, "MEMORY:ks-file-create",
425 0, lock, &ksf->certs);
426 if (ret)
427 goto out;
428 *data = ksf;
429 return 0;
432 ret = _hx509_collector_alloc(context, lock, &pem_ctx.c);
433 if (ret)
434 goto out;
436 for (p = ksf->fn; p != NULL; p = pnext) {
437 FILE *f;
439 pnext = strchr(p, ',');
440 if (pnext)
441 *pnext++ = '\0';
444 if ((f = fopen(p, "r")) == NULL) {
445 ret = ENOENT;
446 hx509_set_error_string(context, 0, ret,
447 "Failed to open PEM file \"%s\": %s",
448 p, strerror(errno));
449 goto out;
451 rk_cloexec_file(f);
453 ret = hx509_pem_read(context, f, pem_func, &pem_ctx);
454 fclose(f);
455 if (ret != 0 && ret != HX509_PARSING_KEY_FAILED)
456 goto out;
457 else if (ret == HX509_PARSING_KEY_FAILED) {
458 size_t length;
459 void *ptr;
460 size_t i;
462 ret = rk_undumpdata(p, &ptr, &length);
463 if (ret) {
464 hx509_clear_error_string(context);
465 goto out;
468 for (i = 0; i < sizeof(formats)/sizeof(formats[0]); i++) {
469 const AlgorithmIdentifier *ai = NULL;
471 if (formats[i].ai != NULL)
472 ai = (*formats[i].ai)();
474 ret = (*formats[i].func)(context, p, pem_ctx.flags, pem_ctx.c,
475 NULL, ptr, length, ai);
476 if (ret == 0)
477 break;
479 rk_xfree(ptr);
480 if (ret) {
481 hx509_clear_error_string(context);
482 goto out;
487 ret = _hx509_collector_collect_certs(context, pem_ctx.c, &ksf->certs);
488 if (ret)
489 goto out;
491 ret = _hx509_collector_collect_private_keys(context, pem_ctx.c, &keys);
492 if (ret == 0) {
493 int i;
495 for (i = 0; keys[i]; i++)
496 _hx509_certs_keys_add(context, ksf->certs, keys[i]);
497 _hx509_certs_keys_free(context, keys);
500 out:
501 if (ret == 0)
502 *data = ksf;
503 else {
504 if (ksf->fn)
505 free(ksf->fn);
506 free(ksf);
508 if (pem_ctx.c)
509 _hx509_collector_free(pem_ctx.c);
511 return ret;
514 static int
515 file_init_pem(hx509_context context,
516 hx509_certs certs, void **data, int flags,
517 const char *residue, hx509_lock lock)
519 return file_init_common(context, certs, data, flags, residue, lock, USE_PEM);
522 static int
523 file_init_der(hx509_context context,
524 hx509_certs certs, void **data, int flags,
525 const char *residue, hx509_lock lock)
527 return file_init_common(context, certs, data, flags, residue, lock, USE_DER);
530 static int
531 file_free(hx509_certs certs, void *data)
533 struct ks_file *ksf = data;
534 hx509_certs_free(&ksf->certs);
535 free(ksf->fn);
536 free(ksf);
537 return 0;
540 struct store_ctx {
541 FILE *f;
542 outformat format;
543 int store_flags;
546 static int HX509_LIB_CALL
547 store_func(hx509_context context, void *ctx, hx509_cert c)
549 struct store_ctx *sc = ctx;
550 heim_octet_string data;
551 int ret = 0;
553 if (hx509_cert_have_private_key_only(c)) {
554 data.length = 0;
555 data.data = NULL;
556 } else {
557 ret = hx509_cert_binary(context, c, &data);
558 if (ret)
559 return ret;
562 switch (sc->format) {
563 case USE_DER:
564 /* Can't store both. Well, we could, but nothing will support it */
565 if (data.data) {
566 fwrite(data.data, data.length, 1, sc->f);
567 } else if (_hx509_cert_private_key_exportable(c) &&
568 !(sc->store_flags & HX509_CERTS_STORE_NO_PRIVATE_KEYS)) {
569 hx509_private_key key = _hx509_cert_private_key(c);
571 free(data.data);
572 data.length = 0;
573 data.data = NULL;
574 ret = _hx509_private_key_export(context, key,
575 HX509_KEY_FORMAT_DER, &data);
576 if (ret == 0 && data.length)
577 fwrite(data.data, data.length, 1, sc->f);
579 break;
580 case USE_PEM:
581 if (_hx509_cert_private_key_exportable(c) &&
582 !(sc->store_flags & HX509_CERTS_STORE_NO_PRIVATE_KEYS)) {
583 heim_octet_string priv_key;
584 hx509_private_key key = _hx509_cert_private_key(c);
586 ret = _hx509_private_key_export(context, key,
587 HX509_KEY_FORMAT_DER, &priv_key);
588 if (ret == 0)
589 ret = hx509_pem_write(context, _hx509_private_pem_name(key), NULL,
590 sc->f, priv_key.data, priv_key.length);
591 free(priv_key.data);
593 if (ret == 0 && data.data) {
594 ret = hx509_pem_write(context, "CERTIFICATE", NULL, sc->f,
595 data.data, data.length);
597 break;
600 free(data.data);
601 return ret;
604 static int
605 mk_temp(const char *fn, char **tfn)
607 char *ds;
608 int ret = -1;
610 #ifdef WIN32
611 char buf[PATH_MAX];
612 char *p;
614 *tfn = NULL;
616 if ((ds = _fullpath(buf, fn, sizeof(buf))) == NULL) {
617 errno = errno ? errno : ENAMETOOLONG;
618 return -1;
621 if ((p = strrchr(ds, '\\')) == NULL) {
622 ret = asprintf(tfn, ".%s-XXXXXX", ds); /* XXX can't happen */
623 } else {
624 *(p++) = '\0';
625 ret = asprintf(tfn, "%s/.%s-XXXXXX", ds, p);
627 #else
628 *tfn = NULL;
629 if ((ds = strdup(fn)))
630 ret = asprintf(tfn, "%s/.%s-XXXXXX", dirname(ds), basename(ds));
631 free(ds);
632 #endif
635 * Using mkostemp() risks leaving garbage files lying around. To do better
636 * without resorting to file locks (which have their own problems) we need
637 * O_TMPFILE and linkat(2), which only Linux has.
639 return (ret == -1 || *tfn == NULL) ? -1 : mkostemp(*tfn, O_CLOEXEC);
642 static int
643 file_store(hx509_context context,
644 hx509_certs certs, void *data, int flags, hx509_lock lock)
646 struct ks_file *ksf = data;
647 struct store_ctx sc;
648 char *tfn;
649 int ret;
650 int fd;
652 sc.f = NULL;
653 fd = mk_temp(ksf->fn, &tfn);
654 if (fd > -1)
655 sc.f = fdopen(fd, "w");
656 if (sc.f == NULL) {
657 hx509_set_error_string(context, 0, ret = errno,
658 "Failed to open file %s for writing", ksf->fn);
659 if (fd > -1)
660 (void) close(fd);
661 return ret;
663 rk_cloexec_file(sc.f);
664 sc.store_flags = flags;
665 sc.format = ksf->format;
667 ret = hx509_certs_iter_f(context, ksf->certs, store_func, &sc);
668 if (ret == 0)
669 ret = fclose(sc.f);
670 else
671 (void) fclose(sc.f);
672 if (ret)
673 (void) unlink(tfn);
674 else
675 (void) rename(tfn, ksf->fn);
676 free(tfn);
677 return ret;
680 static int
681 file_add(hx509_context context, hx509_certs certs, void *data, hx509_cert c)
683 struct ks_file *ksf = data;
684 return hx509_certs_add(context, ksf->certs, c);
687 static int
688 file_iter_start(hx509_context context,
689 hx509_certs certs, void *data, void **cursor)
691 struct ks_file *ksf = data;
692 return hx509_certs_start_seq(context, ksf->certs, cursor);
695 static int
696 file_iter(hx509_context context,
697 hx509_certs certs, void *data, void *iter, hx509_cert *cert)
699 struct ks_file *ksf = data;
700 return hx509_certs_next_cert(context, ksf->certs, iter, cert);
703 static int
704 file_iter_end(hx509_context context,
705 hx509_certs certs,
706 void *data,
707 void *cursor)
709 struct ks_file *ksf = data;
710 return hx509_certs_end_seq(context, ksf->certs, cursor);
713 static int
714 file_getkeys(hx509_context context,
715 hx509_certs certs,
716 void *data,
717 hx509_private_key **keys)
719 struct ks_file *ksf = data;
720 return _hx509_certs_keys_get(context, ksf->certs, keys);
723 static int
724 file_addkey(hx509_context context,
725 hx509_certs certs,
726 void *data,
727 hx509_private_key key)
729 struct ks_file *ksf = data;
730 return _hx509_certs_keys_add(context, ksf->certs, key);
733 static int
734 file_destroy(hx509_context context,
735 hx509_certs certs,
736 void *data)
738 struct ks_file *ksf = data;
739 return _hx509_erase_file(context, ksf->fn);
742 static struct hx509_keyset_ops keyset_file = {
743 "FILE",
745 file_init_pem,
746 file_store,
747 file_free,
748 file_add,
749 NULL,
750 file_iter_start,
751 file_iter,
752 file_iter_end,
753 NULL,
754 file_getkeys,
755 file_addkey,
756 file_destroy
759 static struct hx509_keyset_ops keyset_pemfile = {
760 "PEM-FILE",
762 file_init_pem,
763 file_store,
764 file_free,
765 file_add,
766 NULL,
767 file_iter_start,
768 file_iter,
769 file_iter_end,
770 NULL,
771 file_getkeys,
772 file_addkey,
773 file_destroy
776 static struct hx509_keyset_ops keyset_derfile = {
777 "DER-FILE",
779 file_init_der,
780 file_store,
781 file_free,
782 file_add,
783 NULL,
784 file_iter_start,
785 file_iter,
786 file_iter_end,
787 NULL,
788 file_getkeys,
789 file_addkey,
790 file_destroy
794 HX509_LIB_FUNCTION void HX509_LIB_CALL
795 _hx509_ks_file_register(hx509_context context)
797 _hx509_ks_register(context, &keyset_file);
798 _hx509_ks_register(context, &keyset_pemfile);
799 _hx509_ks_register(context, &keyset_derfile);