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