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