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