Rename context handle lifetime to endtime
[heimdal.git] / lib / hx509 / ks_file.c
blob3eaf695157b24b49001d1823fde080c170332681
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"
36 typedef enum { USE_PEM, USE_DER } outformat;
38 struct ks_file {
39 hx509_certs certs;
40 char *fn;
41 outformat format;
48 static int
49 parse_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)
55 heim_error_t error = NULL;
56 hx509_cert cert;
57 int ret;
59 cert = hx509_cert_init_data(context, data, len, &error);
60 if (cert == NULL) {
61 ret = heim_error_get_code(error);
62 heim_release(error);
63 return ret;
66 ret = _hx509_collector_certs_add(context, c, cert);
67 hx509_cert_free(cert);
68 return ret;
71 static int
72 try_decrypt(hx509_context context,
73 struct hx509_collector *collector,
74 const AlgorithmIdentifier *alg,
75 const EVP_CIPHER *c,
76 const void *ivdata,
77 const void *password,
78 size_t passwordlen,
79 const void *cipher,
80 size_t len)
82 heim_octet_string clear;
83 size_t keylen;
84 void *key;
85 int ret;
87 keylen = EVP_CIPHER_key_length(c);
89 key = malloc(keylen);
90 if (key == NULL) {
91 hx509_clear_error_string(context);
92 return ENOMEM;
95 ret = EVP_BytesToKey(c, EVP_md5(), ivdata,
96 password, passwordlen,
97 1, key, NULL);
98 if (ret <= 0) {
99 hx509_set_error_string(context, 0, HX509_CRYPTO_INTERNAL_ERROR,
100 "Failed to do string2key for private key");
101 return HX509_CRYPTO_INTERNAL_ERROR;
104 clear.data = malloc(len);
105 if (clear.data == NULL) {
106 hx509_set_error_string(context, 0, ENOMEM,
107 "Out of memory to decrypt for private key");
108 ret = ENOMEM;
109 goto out;
111 clear.length = len;
114 EVP_CIPHER_CTX ctx;
115 EVP_CIPHER_CTX_init(&ctx);
116 EVP_CipherInit_ex(&ctx, c, NULL, key, ivdata, 0);
117 EVP_Cipher(&ctx, clear.data, cipher, len);
118 EVP_CIPHER_CTX_cleanup(&ctx);
121 ret = _hx509_collector_private_key_add(context,
122 collector,
123 alg,
124 NULL,
125 &clear,
126 NULL);
128 memset(clear.data, 0, clear.length);
129 free(clear.data);
130 out:
131 memset(key, 0, keylen);
132 free(key);
133 return ret;
136 static int
137 parse_pkcs8_private_key(hx509_context context, const char *fn,
138 struct hx509_collector *c,
139 const hx509_pem_header *headers,
140 const void *data, size_t length,
141 const AlgorithmIdentifier *ai)
143 PKCS8PrivateKeyInfo ki;
144 heim_octet_string keydata;
146 int ret;
148 ret = decode_PKCS8PrivateKeyInfo(data, length, &ki, NULL);
149 if (ret)
150 return ret;
152 keydata.data = rk_UNCONST(data);
153 keydata.length = length;
155 ret = _hx509_collector_private_key_add(context,
157 &ki.privateKeyAlgorithm,
158 NULL,
159 &ki.privateKey,
160 &keydata);
161 free_PKCS8PrivateKeyInfo(&ki);
162 return ret;
165 static int
166 parse_pem_private_key(hx509_context context, const char *fn,
167 struct hx509_collector *c,
168 const hx509_pem_header *headers,
169 const void *data, size_t len,
170 const AlgorithmIdentifier *ai)
172 int ret = 0;
173 const char *enc;
175 enc = hx509_pem_find_header(headers, "Proc-Type");
176 if (enc) {
177 const char *dek;
178 char *type, *iv;
179 ssize_t ssize, size;
180 void *ivdata;
181 const EVP_CIPHER *cipher;
182 const struct _hx509_password *pw;
183 hx509_lock lock;
184 int decrypted = 0;
185 size_t i;
187 lock = _hx509_collector_get_lock(c);
188 if (lock == NULL) {
189 hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP,
190 "Failed to get password for "
191 "password protected file %s", fn);
192 return HX509_ALG_NOT_SUPP;
195 if (strcmp(enc, "4,ENCRYPTED") != 0) {
196 hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
197 "Private key encrypted in unknown method %s "
198 "in file",
199 enc, fn);
200 hx509_clear_error_string(context);
201 return HX509_PARSING_KEY_FAILED;
204 dek = hx509_pem_find_header(headers, "DEK-Info");
205 if (dek == NULL) {
206 hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
207 "Encrypted private key missing DEK-Info");
208 return HX509_PARSING_KEY_FAILED;
211 type = strdup(dek);
212 if (type == NULL) {
213 hx509_clear_error_string(context);
214 return ENOMEM;
217 iv = strchr(type, ',');
218 if (iv == NULL) {
219 free(type);
220 hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
221 "IV missing");
222 return HX509_PARSING_KEY_FAILED;
225 *iv++ = '\0';
227 size = strlen(iv);
228 ivdata = malloc(size);
229 if (ivdata == NULL) {
230 hx509_clear_error_string(context);
231 free(type);
232 return ENOMEM;
235 cipher = EVP_get_cipherbyname(type);
236 if (cipher == NULL) {
237 free(ivdata);
238 hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP,
239 "Private key encrypted with "
240 "unsupported cipher: %s",
241 type);
242 free(type);
243 return HX509_ALG_NOT_SUPP;
246 #define PKCS5_SALT_LEN 8
248 ssize = hex_decode(iv, ivdata, size);
249 free(type);
250 type = NULL;
251 iv = NULL;
253 if (ssize < 0 || ssize < PKCS5_SALT_LEN || ssize < EVP_CIPHER_iv_length(cipher)) {
254 free(ivdata);
255 hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
256 "Salt have wrong length in "
257 "private key file");
258 return HX509_PARSING_KEY_FAILED;
261 pw = _hx509_lock_get_passwords(lock);
262 if (pw != NULL) {
263 const void *password;
264 size_t passwordlen;
266 for (i = 0; i < pw->len; i++) {
267 password = pw->val[i];
268 passwordlen = strlen(password);
270 ret = try_decrypt(context, c, ai, cipher, ivdata,
271 password, passwordlen, data, len);
272 if (ret == 0) {
273 decrypted = 1;
274 break;
278 if (!decrypted) {
279 hx509_prompt prompt;
280 char password[128];
282 memset(&prompt, 0, sizeof(prompt));
284 prompt.prompt = "Password for keyfile: ";
285 prompt.type = HX509_PROMPT_TYPE_PASSWORD;
286 prompt.reply.data = password;
287 prompt.reply.length = sizeof(password);
289 ret = hx509_lock_prompt(lock, &prompt);
290 if (ret == 0)
291 ret = try_decrypt(context, c, ai, cipher, ivdata, password,
292 strlen(password), data, len);
293 /* XXX add password to lock password collection ? */
294 memset(password, 0, sizeof(password));
296 free(ivdata);
298 } else {
299 heim_octet_string keydata;
301 keydata.data = rk_UNCONST(data);
302 keydata.length = len;
304 ret = _hx509_collector_private_key_add(context, c, ai, NULL,
305 &keydata, NULL);
308 return ret;
312 struct pem_formats {
313 const char *name;
314 int (*func)(hx509_context, const char *, struct hx509_collector *,
315 const hx509_pem_header *, const void *, size_t,
316 const AlgorithmIdentifier *);
317 const AlgorithmIdentifier *(*ai)(void);
318 } formats[] = {
319 { "CERTIFICATE", parse_certificate, NULL },
320 { "PRIVATE KEY", parse_pkcs8_private_key, NULL },
321 { "RSA PRIVATE KEY", parse_pem_private_key, hx509_signature_rsa },
322 { "EC PRIVATE KEY", parse_pem_private_key, hx509_signature_ecPublicKey }
326 struct pem_ctx {
327 int flags;
328 struct hx509_collector *c;
331 static int
332 pem_func(hx509_context context, const char *type,
333 const hx509_pem_header *header,
334 const void *data, size_t len, void *ctx)
336 struct pem_ctx *pem_ctx = (struct pem_ctx*)ctx;
337 int ret = 0;
338 size_t j;
340 for (j = 0; j < sizeof(formats)/sizeof(formats[0]); j++) {
341 const char *q = formats[j].name;
342 if (strcasecmp(type, q) == 0) {
343 const AlgorithmIdentifier *ai = NULL;
344 if (formats[j].ai != NULL)
345 ai = (*formats[j].ai)();
347 ret = (*formats[j].func)(context, NULL, pem_ctx->c,
348 header, data, len, ai);
349 if (ret && (pem_ctx->flags & HX509_CERTS_UNPROTECT_ALL)) {
350 hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
351 "Failed parseing PEM format %s", type);
352 return ret;
354 break;
357 if (j == sizeof(formats)/sizeof(formats[0])) {
358 ret = HX509_UNSUPPORTED_OPERATION;
359 hx509_set_error_string(context, 0, ret,
360 "Found no matching PEM format for %s", type);
361 return ret;
363 return 0;
370 static int
371 file_init_common(hx509_context context,
372 hx509_certs certs, void **data, int flags,
373 const char *residue, hx509_lock lock, outformat format)
375 char *p, *pnext;
376 struct ks_file *ksf = NULL;
377 hx509_private_key *keys = NULL;
378 int ret;
379 struct pem_ctx pem_ctx;
381 pem_ctx.flags = flags;
382 pem_ctx.c = NULL;
384 *data = NULL;
386 if (lock == NULL)
387 lock = _hx509_empty_lock;
389 ksf = calloc(1, sizeof(*ksf));
390 if (ksf == NULL) {
391 hx509_clear_error_string(context);
392 return ENOMEM;
394 ksf->format = format;
396 ksf->fn = strdup(residue);
397 if (ksf->fn == NULL) {
398 hx509_clear_error_string(context);
399 ret = ENOMEM;
400 goto out;
404 * XXX this is broken, the function should parse the file before
405 * overwriting it
408 if (flags & HX509_CERTS_CREATE) {
409 ret = hx509_certs_init(context, "MEMORY:ks-file-create",
410 0, lock, &ksf->certs);
411 if (ret)
412 goto out;
413 *data = ksf;
414 return 0;
417 ret = _hx509_collector_alloc(context, lock, &pem_ctx.c);
418 if (ret)
419 goto out;
421 for (p = ksf->fn; p != NULL; p = pnext) {
422 FILE *f;
424 pnext = strchr(p, ',');
425 if (pnext)
426 *pnext++ = '\0';
429 if ((f = fopen(p, "r")) == NULL) {
430 ret = ENOENT;
431 hx509_set_error_string(context, 0, ret,
432 "Failed to open PEM file \"%s\": %s",
433 p, strerror(errno));
434 goto out;
436 rk_cloexec_file(f);
438 ret = hx509_pem_read(context, f, pem_func, &pem_ctx);
439 fclose(f);
440 if (ret != 0 && ret != HX509_PARSING_KEY_FAILED)
441 goto out;
442 else if (ret == HX509_PARSING_KEY_FAILED) {
443 size_t length;
444 void *ptr;
445 size_t i;
447 ret = rk_undumpdata(p, &ptr, &length);
448 if (ret) {
449 hx509_clear_error_string(context);
450 goto out;
453 for (i = 0; i < sizeof(formats)/sizeof(formats[0]); i++) {
454 const AlgorithmIdentifier *ai = NULL;
455 if (formats[i].ai != NULL)
456 ai = (*formats[i].ai)();
458 ret = (*formats[i].func)(context, p, pem_ctx.c, NULL, ptr, length, ai);
459 if (ret == 0)
460 break;
462 rk_xfree(ptr);
463 if (ret) {
464 hx509_clear_error_string(context);
465 goto out;
470 ret = _hx509_collector_collect_certs(context, pem_ctx.c, &ksf->certs);
471 if (ret)
472 goto out;
474 ret = _hx509_collector_collect_private_keys(context, pem_ctx.c, &keys);
475 if (ret == 0) {
476 int i;
478 for (i = 0; keys[i]; i++)
479 _hx509_certs_keys_add(context, ksf->certs, keys[i]);
480 _hx509_certs_keys_free(context, keys);
483 out:
484 if (ret == 0)
485 *data = ksf;
486 else {
487 if (ksf->fn)
488 free(ksf->fn);
489 free(ksf);
491 if (pem_ctx.c)
492 _hx509_collector_free(pem_ctx.c);
494 return ret;
497 static int
498 file_init_pem(hx509_context context,
499 hx509_certs certs, void **data, int flags,
500 const char *residue, hx509_lock lock)
502 return file_init_common(context, certs, data, flags, residue, lock, USE_PEM);
505 static int
506 file_init_der(hx509_context context,
507 hx509_certs certs, void **data, int flags,
508 const char *residue, hx509_lock lock)
510 return file_init_common(context, certs, data, flags, residue, lock, USE_DER);
513 static int
514 file_free(hx509_certs certs, void *data)
516 struct ks_file *ksf = data;
517 hx509_certs_free(&ksf->certs);
518 free(ksf->fn);
519 free(ksf);
520 return 0;
523 struct store_ctx {
524 FILE *f;
525 outformat format;
528 static int
529 store_func(hx509_context context, void *ctx, hx509_cert c)
531 struct store_ctx *sc = ctx;
532 heim_octet_string data;
533 int ret;
535 ret = hx509_cert_binary(context, c, &data);
536 if (ret)
537 return ret;
539 switch (sc->format) {
540 case USE_DER:
541 fwrite(data.data, data.length, 1, sc->f);
542 free(data.data);
543 break;
544 case USE_PEM:
545 hx509_pem_write(context, "CERTIFICATE", NULL, sc->f,
546 data.data, data.length);
547 free(data.data);
548 if (_hx509_cert_private_key_exportable(c)) {
549 hx509_private_key key = _hx509_cert_private_key(c);
550 ret = _hx509_private_key_export(context, key,
551 HX509_KEY_FORMAT_DER, &data);
552 if (ret)
553 break;
554 hx509_pem_write(context, _hx509_private_pem_name(key), NULL, sc->f,
555 data.data, data.length);
556 free(data.data);
558 break;
561 return 0;
564 static int
565 file_store(hx509_context context,
566 hx509_certs certs, void *data, int flags, hx509_lock lock)
568 struct ks_file *ksf = data;
569 struct store_ctx sc;
570 int ret;
572 sc.f = fopen(ksf->fn, "w");
573 if (sc.f == NULL) {
574 hx509_set_error_string(context, 0, ENOENT,
575 "Failed to open file %s for writing");
576 return ENOENT;
578 rk_cloexec_file(sc.f);
579 sc.format = ksf->format;
581 ret = hx509_certs_iter_f(context, ksf->certs, store_func, &sc);
582 fclose(sc.f);
583 return ret;
586 static int
587 file_add(hx509_context context, hx509_certs certs, void *data, hx509_cert c)
589 struct ks_file *ksf = data;
590 return hx509_certs_add(context, ksf->certs, c);
593 static int
594 file_iter_start(hx509_context context,
595 hx509_certs certs, void *data, void **cursor)
597 struct ks_file *ksf = data;
598 return hx509_certs_start_seq(context, ksf->certs, cursor);
601 static int
602 file_iter(hx509_context context,
603 hx509_certs certs, void *data, void *iter, hx509_cert *cert)
605 struct ks_file *ksf = data;
606 return hx509_certs_next_cert(context, ksf->certs, iter, cert);
609 static int
610 file_iter_end(hx509_context context,
611 hx509_certs certs,
612 void *data,
613 void *cursor)
615 struct ks_file *ksf = data;
616 return hx509_certs_end_seq(context, ksf->certs, cursor);
619 static int
620 file_getkeys(hx509_context context,
621 hx509_certs certs,
622 void *data,
623 hx509_private_key **keys)
625 struct ks_file *ksf = data;
626 return _hx509_certs_keys_get(context, ksf->certs, keys);
629 static int
630 file_addkey(hx509_context context,
631 hx509_certs certs,
632 void *data,
633 hx509_private_key key)
635 struct ks_file *ksf = data;
636 return _hx509_certs_keys_add(context, ksf->certs, key);
639 static struct hx509_keyset_ops keyset_file = {
640 "FILE",
642 file_init_pem,
643 file_store,
644 file_free,
645 file_add,
646 NULL,
647 file_iter_start,
648 file_iter,
649 file_iter_end,
650 NULL,
651 file_getkeys,
652 file_addkey
655 static struct hx509_keyset_ops keyset_pemfile = {
656 "PEM-FILE",
658 file_init_pem,
659 file_store,
660 file_free,
661 file_add,
662 NULL,
663 file_iter_start,
664 file_iter,
665 file_iter_end,
666 NULL,
667 file_getkeys,
668 file_addkey
671 static struct hx509_keyset_ops keyset_derfile = {
672 "DER-FILE",
674 file_init_der,
675 file_store,
676 file_free,
677 file_add,
678 NULL,
679 file_iter_start,
680 file_iter,
681 file_iter_end,
682 NULL,
683 file_getkeys,
684 file_addkey
688 void
689 _hx509_ks_file_register(hx509_context context)
691 _hx509_ks_register(context, &keyset_file);
692 _hx509_ks_register(context, &keyset_pemfile);
693 _hx509_ks_register(context, &keyset_derfile);