Line data Source code
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 :
36 : typedef enum { USE_PEM, USE_DER } outformat;
37 :
38 : struct ks_file {
39 : hx509_certs certs;
40 : char *fn;
41 : outformat format;
42 : };
43 :
44 : /*
45 : *
46 : */
47 :
48 : static int
49 188 : 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)
54 : {
55 : hx509_cert cert;
56 : int ret;
57 :
58 188 : ret = hx509_cert_init_data(context, data, len, &cert);
59 188 : if (ret)
60 0 : return ret;
61 :
62 188 : ret = _hx509_collector_certs_add(context, c, cert);
63 188 : hx509_cert_free(cert);
64 188 : return ret;
65 : }
66 :
67 : static int
68 0 : try_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 0 : keylen = EVP_CIPHER_key_length(c);
84 :
85 0 : key = malloc(keylen);
86 0 : if (key == NULL) {
87 0 : hx509_clear_error_string(context);
88 0 : return ENOMEM;
89 : }
90 :
91 0 : ret = EVP_BytesToKey(c, EVP_md5(), ivdata,
92 : password, passwordlen,
93 : 1, key, NULL);
94 0 : if (ret <= 0) {
95 0 : hx509_set_error_string(context, 0, HX509_CRYPTO_INTERNAL_ERROR,
96 : "Failed to do string2key for private key");
97 0 : return HX509_CRYPTO_INTERNAL_ERROR;
98 : }
99 :
100 0 : clear.data = malloc(len);
101 0 : if (clear.data == NULL) {
102 0 : hx509_set_error_string(context, 0, ENOMEM,
103 : "Out of memory to decrypt for private key");
104 0 : ret = ENOMEM;
105 0 : goto out;
106 : }
107 0 : clear.length = len;
108 :
109 : {
110 : EVP_CIPHER_CTX ctx;
111 0 : EVP_CIPHER_CTX_init(&ctx);
112 0 : EVP_CipherInit_ex(&ctx, c, NULL, key, ivdata, 0);
113 0 : EVP_Cipher(&ctx, clear.data, cipher, len);
114 0 : EVP_CIPHER_CTX_cleanup(&ctx);
115 : }
116 :
117 0 : ret = _hx509_collector_private_key_add(context,
118 : collector,
119 : alg,
120 : NULL,
121 : &clear,
122 : NULL);
123 :
124 0 : memset(clear.data, 0, clear.length);
125 0 : free(clear.data);
126 0 : out:
127 0 : memset(key, 0, keylen);
128 0 : free(key);
129 0 : return ret;
130 : }
131 :
132 : static int
133 0 : parse_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 0 : ret = decode_PKCS8PrivateKeyInfo(data, length, &ki, NULL);
145 0 : if (ret)
146 0 : return ret;
147 :
148 0 : keydata.data = rk_UNCONST(data);
149 0 : keydata.length = length;
150 :
151 0 : ret = _hx509_collector_private_key_add(context,
152 : c,
153 : &ki.privateKeyAlgorithm,
154 : NULL,
155 : &ki.privateKey,
156 : &keydata);
157 0 : free_PKCS8PrivateKeyInfo(&ki);
158 0 : return ret;
159 : }
160 :
161 : static int
162 94 : parse_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 94 : int ret = 0;
169 : const char *enc;
170 :
171 94 : enc = hx509_pem_find_header(headers, "Proc-Type");
172 94 : 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 0 : int decrypted = 0;
181 : size_t i;
182 :
183 0 : lock = _hx509_collector_get_lock(c);
184 0 : if (lock == NULL) {
185 0 : hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP,
186 : "Failed to get password for "
187 : "password protected file %s", fn);
188 0 : return HX509_ALG_NOT_SUPP;
189 : }
190 :
191 0 : if (strcmp(enc, "4,ENCRYPTED") != 0) {
192 0 : hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
193 : "Private key encrypted in unknown method %s "
194 : "in file",
195 : enc, fn);
196 0 : hx509_clear_error_string(context);
197 0 : return HX509_PARSING_KEY_FAILED;
198 : }
199 :
200 0 : dek = hx509_pem_find_header(headers, "DEK-Info");
201 0 : if (dek == NULL) {
202 0 : hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
203 : "Encrypted private key missing DEK-Info");
204 0 : return HX509_PARSING_KEY_FAILED;
205 : }
206 :
207 0 : type = strdup(dek);
208 0 : if (type == NULL) {
209 0 : hx509_clear_error_string(context);
210 0 : return ENOMEM;
211 : }
212 :
213 0 : iv = strchr(type, ',');
214 0 : if (iv == NULL) {
215 0 : free(type);
216 0 : hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
217 : "IV missing");
218 0 : return HX509_PARSING_KEY_FAILED;
219 : }
220 :
221 0 : *iv++ = '\0';
222 :
223 0 : size = strlen(iv);
224 0 : ivdata = malloc(size);
225 0 : if (ivdata == NULL) {
226 0 : hx509_clear_error_string(context);
227 0 : free(type);
228 0 : return ENOMEM;
229 : }
230 :
231 0 : cipher = EVP_get_cipherbyname(type);
232 0 : if (cipher == NULL) {
233 0 : free(ivdata);
234 0 : hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP,
235 : "Private key encrypted with "
236 : "unsupported cipher: %s",
237 : type);
238 0 : free(type);
239 0 : return HX509_ALG_NOT_SUPP;
240 : }
241 :
242 : #define PKCS5_SALT_LEN 8
243 :
244 0 : ssize = hex_decode(iv, ivdata, size);
245 0 : free(type);
246 0 : type = NULL;
247 0 : iv = NULL;
248 :
249 0 : if (ssize < 0 || ssize < PKCS5_SALT_LEN || ssize < EVP_CIPHER_iv_length(cipher)) {
250 0 : free(ivdata);
251 0 : hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
252 : "Salt have wrong length in "
253 : "private key file");
254 0 : return HX509_PARSING_KEY_FAILED;
255 : }
256 :
257 0 : pw = _hx509_lock_get_passwords(lock);
258 0 : if (pw != NULL) {
259 : const void *password;
260 : size_t passwordlen;
261 :
262 0 : for (i = 0; i < pw->len; i++) {
263 0 : password = pw->val[i];
264 0 : passwordlen = strlen(password);
265 :
266 0 : ret = try_decrypt(context, c, ai, cipher, ivdata,
267 : password, passwordlen, data, len);
268 0 : if (ret == 0) {
269 0 : decrypted = 1;
270 0 : break;
271 : }
272 : }
273 : }
274 0 : if (!decrypted) {
275 : hx509_prompt prompt;
276 : char password[128];
277 :
278 0 : memset(&prompt, 0, sizeof(prompt));
279 :
280 0 : prompt.prompt = "Password for keyfile: ";
281 0 : prompt.type = HX509_PROMPT_TYPE_PASSWORD;
282 0 : prompt.reply.data = password;
283 0 : prompt.reply.length = sizeof(password);
284 :
285 0 : ret = hx509_lock_prompt(lock, &prompt);
286 0 : if (ret == 0)
287 0 : ret = try_decrypt(context, c, ai, cipher, ivdata, password,
288 : strlen(password), data, len);
289 : /* XXX add password to lock password collection ? */
290 0 : memset(password, 0, sizeof(password));
291 : }
292 0 : free(ivdata);
293 :
294 : } else {
295 : heim_octet_string keydata;
296 :
297 94 : keydata.data = rk_UNCONST(data);
298 94 : keydata.length = len;
299 :
300 94 : ret = _hx509_collector_private_key_add(context, c, ai, NULL,
301 : &keydata, NULL);
302 : }
303 :
304 86 : return ret;
305 : }
306 :
307 :
308 : struct pem_formats {
309 : const char *name;
310 : int (*func)(hx509_context, const char *, struct hx509_collector *,
311 : const hx509_pem_header *, const void *, size_t,
312 : const AlgorithmIdentifier *);
313 : const AlgorithmIdentifier *(*ai)(void);
314 : } formats[] = {
315 : { "CERTIFICATE", parse_certificate, NULL },
316 : { "PRIVATE KEY", parse_pkcs8_private_key, NULL },
317 : { "RSA PRIVATE KEY", parse_pem_private_key, hx509_signature_rsa },
318 : { "EC PRIVATE KEY", parse_pem_private_key, hx509_signature_ecPublicKey }
319 : };
320 :
321 :
322 : struct pem_ctx {
323 : int flags;
324 : struct hx509_collector *c;
325 : };
326 :
327 : static int
328 282 : pem_func(hx509_context context, const char *type,
329 : const hx509_pem_header *header,
330 : const void *data, size_t len, void *ctx)
331 : {
332 282 : struct pem_ctx *pem_ctx = (struct pem_ctx*)ctx;
333 282 : int ret = 0;
334 : size_t j;
335 :
336 470 : for (j = 0; j < sizeof(formats)/sizeof(formats[0]); j++) {
337 470 : const char *q = formats[j].name;
338 470 : if (strcasecmp(type, q) == 0) {
339 282 : const AlgorithmIdentifier *ai = NULL;
340 282 : if (formats[j].ai != NULL)
341 94 : ai = (*formats[j].ai)();
342 :
343 282 : ret = (*formats[j].func)(context, NULL, pem_ctx->c,
344 : header, data, len, ai);
345 282 : if (ret && (pem_ctx->flags & HX509_CERTS_UNPROTECT_ALL)) {
346 0 : hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
347 : "Failed parseing PEM format %s", type);
348 0 : return ret;
349 : }
350 258 : break;
351 : }
352 : }
353 282 : if (j == sizeof(formats)/sizeof(formats[0])) {
354 0 : ret = HX509_UNSUPPORTED_OPERATION;
355 0 : hx509_set_error_string(context, 0, ret,
356 : "Found no matching PEM format for %s", type);
357 0 : return ret;
358 : }
359 258 : return 0;
360 : }
361 :
362 : /*
363 : *
364 : */
365 :
366 : static int
367 226 : file_init_common(hx509_context context,
368 : hx509_certs certs, void **data, int flags,
369 : const char *residue, hx509_lock lock, outformat format)
370 : {
371 : char *p, *pnext;
372 226 : struct ks_file *ksf = NULL;
373 226 : hx509_private_key *keys = NULL;
374 : int ret;
375 : struct pem_ctx pem_ctx;
376 :
377 226 : pem_ctx.flags = flags;
378 226 : pem_ctx.c = NULL;
379 :
380 226 : *data = NULL;
381 :
382 226 : if (lock == NULL)
383 104 : lock = _hx509_empty_lock;
384 :
385 226 : ksf = calloc(1, sizeof(*ksf));
386 226 : if (ksf == NULL) {
387 0 : hx509_clear_error_string(context);
388 0 : return ENOMEM;
389 : }
390 226 : ksf->format = format;
391 :
392 226 : ksf->fn = strdup(residue);
393 226 : if (ksf->fn == NULL) {
394 0 : hx509_clear_error_string(context);
395 0 : ret = ENOMEM;
396 0 : goto out;
397 : }
398 :
399 : /*
400 : * XXX this is broken, the function should parse the file before
401 : * overwriting it
402 : */
403 :
404 226 : if (flags & HX509_CERTS_CREATE) {
405 0 : ret = hx509_certs_init(context, "MEMORY:ks-file-create",
406 : 0, lock, &ksf->certs);
407 0 : if (ret)
408 0 : goto out;
409 0 : *data = ksf;
410 0 : return 0;
411 : }
412 :
413 226 : ret = _hx509_collector_alloc(context, lock, &pem_ctx.c);
414 226 : if (ret)
415 0 : goto out;
416 :
417 524 : for (p = ksf->fn; p != NULL; p = pnext) {
418 : FILE *f;
419 :
420 320 : pnext = strchr(p, ',');
421 320 : if (pnext)
422 132 : *pnext++ = '\0';
423 :
424 :
425 320 : if ((f = fopen(p, "r")) == NULL) {
426 38 : ret = ENOENT;
427 38 : hx509_set_error_string(context, 0, ret,
428 : "Failed to open PEM file \"%s\": %s",
429 38 : p, strerror(errno));
430 38 : goto out;
431 : }
432 282 : rk_cloexec_file(f);
433 :
434 282 : ret = hx509_pem_read(context, f, pem_func, &pem_ctx);
435 282 : fclose(f);
436 282 : if (ret != 0 && ret != HX509_PARSING_KEY_FAILED)
437 : goto out;
438 282 : else if (ret == HX509_PARSING_KEY_FAILED) {
439 : size_t length;
440 : void *ptr;
441 : size_t i;
442 :
443 0 : ret = rk_undumpdata(p, &ptr, &length);
444 0 : if (ret) {
445 0 : hx509_clear_error_string(context);
446 0 : goto out;
447 : }
448 :
449 0 : for (i = 0; i < sizeof(formats)/sizeof(formats[0]); i++) {
450 0 : const AlgorithmIdentifier *ai = NULL;
451 0 : if (formats[i].ai != NULL)
452 0 : ai = (*formats[i].ai)();
453 :
454 0 : ret = (*formats[i].func)(context, p, pem_ctx.c, NULL, ptr, length, ai);
455 0 : if (ret == 0)
456 0 : break;
457 : }
458 0 : rk_xfree(ptr);
459 0 : if (ret) {
460 0 : hx509_clear_error_string(context);
461 0 : goto out;
462 : }
463 : }
464 : }
465 :
466 188 : ret = _hx509_collector_collect_certs(context, pem_ctx.c, &ksf->certs);
467 188 : if (ret)
468 0 : goto out;
469 :
470 188 : ret = _hx509_collector_collect_private_keys(context, pem_ctx.c, &keys);
471 188 : if (ret == 0) {
472 : int i;
473 :
474 266 : for (i = 0; keys[i]; i++)
475 94 : _hx509_certs_keys_add(context, ksf->certs, keys[i]);
476 188 : _hx509_certs_keys_free(context, keys);
477 : }
478 :
479 226 : out:
480 226 : if (ret == 0)
481 188 : *data = ksf;
482 : else {
483 38 : if (ksf->fn)
484 38 : free(ksf->fn);
485 38 : free(ksf);
486 : }
487 226 : if (pem_ctx.c)
488 226 : _hx509_collector_free(pem_ctx.c);
489 :
490 210 : return ret;
491 : }
492 :
493 : static int
494 226 : file_init_pem(hx509_context context,
495 : hx509_certs certs, void **data, int flags,
496 : const char *residue, hx509_lock lock)
497 : {
498 226 : return file_init_common(context, certs, data, flags, residue, lock, USE_PEM);
499 : }
500 :
501 : static int
502 0 : file_init_der(hx509_context context,
503 : hx509_certs certs, void **data, int flags,
504 : const char *residue, hx509_lock lock)
505 : {
506 0 : return file_init_common(context, certs, data, flags, residue, lock, USE_DER);
507 : }
508 :
509 : static int
510 80 : file_free(hx509_certs certs, void *data)
511 : {
512 80 : struct ks_file *ksf = data;
513 80 : hx509_certs_free(&ksf->certs);
514 80 : free(ksf->fn);
515 80 : free(ksf);
516 80 : return 0;
517 : }
518 :
519 : struct store_ctx {
520 : FILE *f;
521 : outformat format;
522 : };
523 :
524 : static int
525 0 : store_func(hx509_context context, void *ctx, hx509_cert c)
526 : {
527 0 : struct store_ctx *sc = ctx;
528 : heim_octet_string data;
529 : int ret;
530 :
531 0 : ret = hx509_cert_binary(context, c, &data);
532 0 : if (ret)
533 0 : return ret;
534 :
535 0 : switch (sc->format) {
536 0 : case USE_DER:
537 0 : fwrite(data.data, data.length, 1, sc->f);
538 0 : free(data.data);
539 0 : break;
540 0 : case USE_PEM:
541 0 : hx509_pem_write(context, "CERTIFICATE", NULL, sc->f,
542 0 : data.data, data.length);
543 0 : free(data.data);
544 0 : if (_hx509_cert_private_key_exportable(c)) {
545 0 : hx509_private_key key = _hx509_cert_private_key(c);
546 0 : ret = _hx509_private_key_export(context, key,
547 : HX509_KEY_FORMAT_DER, &data);
548 0 : if (ret)
549 0 : break;
550 0 : hx509_pem_write(context, _hx509_private_pem_name(key), NULL, sc->f,
551 0 : data.data, data.length);
552 0 : free(data.data);
553 : }
554 0 : break;
555 : }
556 :
557 0 : return 0;
558 : }
559 :
560 : static int
561 0 : file_store(hx509_context context,
562 : hx509_certs certs, void *data, int flags, hx509_lock lock)
563 : {
564 0 : struct ks_file *ksf = data;
565 : struct store_ctx sc;
566 : int ret;
567 :
568 0 : sc.f = fopen(ksf->fn, "w");
569 0 : if (sc.f == NULL) {
570 0 : hx509_set_error_string(context, 0, ENOENT,
571 : "Failed to open file %s for writing");
572 0 : return ENOENT;
573 : }
574 0 : rk_cloexec_file(sc.f);
575 0 : sc.format = ksf->format;
576 :
577 0 : ret = hx509_certs_iter_f(context, ksf->certs, store_func, &sc);
578 0 : fclose(sc.f);
579 0 : return ret;
580 : }
581 :
582 : static int
583 0 : file_add(hx509_context context, hx509_certs certs, void *data, hx509_cert c)
584 : {
585 0 : struct ks_file *ksf = data;
586 0 : return hx509_certs_add(context, ksf->certs, c);
587 : }
588 :
589 : static int
590 366 : file_iter_start(hx509_context context,
591 : hx509_certs certs, void *data, void **cursor)
592 : {
593 366 : struct ks_file *ksf = data;
594 366 : return hx509_certs_start_seq(context, ksf->certs, cursor);
595 : }
596 :
597 : static int
598 570 : file_iter(hx509_context context,
599 : hx509_certs certs, void *data, void *iter, hx509_cert *cert)
600 : {
601 570 : struct ks_file *ksf = data;
602 570 : return hx509_certs_next_cert(context, ksf->certs, iter, cert);
603 : }
604 :
605 : static int
606 366 : file_iter_end(hx509_context context,
607 : hx509_certs certs,
608 : void *data,
609 : void *cursor)
610 : {
611 366 : struct ks_file *ksf = data;
612 366 : return hx509_certs_end_seq(context, ksf->certs, cursor);
613 : }
614 :
615 : static int
616 0 : file_getkeys(hx509_context context,
617 : hx509_certs certs,
618 : void *data,
619 : hx509_private_key **keys)
620 : {
621 0 : struct ks_file *ksf = data;
622 0 : return _hx509_certs_keys_get(context, ksf->certs, keys);
623 : }
624 :
625 : static int
626 0 : file_addkey(hx509_context context,
627 : hx509_certs certs,
628 : void *data,
629 : hx509_private_key key)
630 : {
631 0 : struct ks_file *ksf = data;
632 0 : return _hx509_certs_keys_add(context, ksf->certs, key);
633 : }
634 :
635 : static struct hx509_keyset_ops keyset_file = {
636 : "FILE",
637 : 0,
638 : file_init_pem,
639 : file_store,
640 : file_free,
641 : file_add,
642 : NULL,
643 : file_iter_start,
644 : file_iter,
645 : file_iter_end,
646 : NULL,
647 : file_getkeys,
648 : file_addkey
649 : };
650 :
651 : static struct hx509_keyset_ops keyset_pemfile = {
652 : "PEM-FILE",
653 : 0,
654 : file_init_pem,
655 : file_store,
656 : file_free,
657 : file_add,
658 : NULL,
659 : file_iter_start,
660 : file_iter,
661 : file_iter_end,
662 : NULL,
663 : file_getkeys,
664 : file_addkey
665 : };
666 :
667 : static struct hx509_keyset_ops keyset_derfile = {
668 : "DER-FILE",
669 : 0,
670 : file_init_der,
671 : file_store,
672 : file_free,
673 : file_add,
674 : NULL,
675 : file_iter_start,
676 : file_iter,
677 : file_iter_end,
678 : NULL,
679 : file_getkeys,
680 : file_addkey
681 : };
682 :
683 :
684 : void
685 908739 : _hx509_ks_file_register(hx509_context context)
686 : {
687 908739 : _hx509_ks_register(context, &keyset_file);
688 908739 : _hx509_ks_register(context, &keyset_pemfile);
689 908739 : _hx509_ks_register(context, &keyset_derfile);
690 908739 : }
|