s4:heimdal: import lorikeet-heimdal-202201172009 (commit 5a0b45cd723628b3690ea848548b...
[samba.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 #ifndef WIN32
36 #include <libgen.h>
37 #endif
38
39 typedef enum { USE_PEM, USE_DER } outformat;
40
41 struct ks_file {
42     hx509_certs certs;
43     char *fn;
44     outformat format;
45 };
46
47 /*
48  *
49  */
50
51 static int
52 parse_certificate(hx509_context context, const char *fn, int flags,
53                   struct hx509_collector *c,
54                   const hx509_pem_header *headers,
55                   const void *data, size_t len,
56                   const AlgorithmIdentifier *ai)
57 {
58     heim_error_t error = NULL;
59     hx509_cert cert;
60     int ret;
61
62     cert = hx509_cert_init_data(context, data, len, &error);
63     if (cert == NULL) {
64         ret = heim_error_get_code(error);
65         heim_release(error);
66         return ret;
67     }
68
69     ret = _hx509_collector_certs_add(context, c, cert);
70     hx509_cert_free(cert);
71     return ret;
72 }
73
74 static int
75 try_decrypt(hx509_context context,
76             struct hx509_collector *collector,
77             int flags,
78             const AlgorithmIdentifier *alg,
79             const EVP_CIPHER *c,
80             const void *ivdata,
81             const void *password,
82             size_t passwordlen,
83             const void *cipher,
84             size_t len)
85 {
86     heim_octet_string clear;
87     size_t keylen;
88     void *key;
89     int ret;
90
91     keylen = EVP_CIPHER_key_length(c);
92
93     key = malloc(keylen);
94     if (key == NULL) {
95         hx509_clear_error_string(context);
96         return ENOMEM;
97     }
98
99     ret = EVP_BytesToKey(c, EVP_md5(), ivdata,
100                          password, passwordlen,
101                          1, key, NULL);
102     if (ret <= 0) {
103         ret = HX509_CRYPTO_INTERNAL_ERROR;
104         hx509_set_error_string(context, 0, ret,
105                                "Failed to do string2key for private key");
106         goto out;
107     }
108
109     clear.data = malloc(len);
110     if (clear.data == NULL) {
111         hx509_set_error_string(context, 0, ENOMEM,
112                                "Out of memory to decrypt for private key");
113         ret = ENOMEM;
114         goto out;
115     }
116     clear.length = len;
117
118     {
119         EVP_CIPHER_CTX ctx;
120         EVP_CIPHER_CTX_init(&ctx);
121         EVP_CipherInit_ex(&ctx, c, NULL, key, ivdata, 0);
122         EVP_Cipher(&ctx, clear.data, cipher, len);
123         EVP_CIPHER_CTX_cleanup(&ctx);
124     }
125
126     if (!(flags & HX509_CERTS_NO_PRIVATE_KEYS))
127         ret = _hx509_collector_private_key_add(context, collector, alg, NULL,
128                                                &clear, NULL);
129
130     memset_s(clear.data, clear.length, 0, clear.length);
131     free(clear.data);
132 out:
133     memset_s(key, keylen, 0, keylen);
134     free(key);
135     return ret;
136 }
137
138 static int
139 parse_pkcs8_private_key(hx509_context context, const char *fn, int flags,
140                         struct hx509_collector *c,
141                         const hx509_pem_header *headers,
142                         const void *data, size_t length,
143                         const AlgorithmIdentifier *ai)
144 {
145     PKCS8PrivateKeyInfo ki;
146     heim_octet_string keydata;
147     int ret;
148
149     ret = decode_PKCS8PrivateKeyInfo(data, length, &ki, NULL);
150     if (ret)
151         return ret;
152
153     if (!(flags & HX509_CERTS_NO_PRIVATE_KEYS)) {
154         keydata.data = rk_UNCONST(data);
155         keydata.length = length;
156         ret = _hx509_collector_private_key_add(context,
157                                                c,
158                                                &ki.privateKeyAlgorithm,
159                                                NULL,
160                                                &ki.privateKey,
161                                                &keydata);
162     }
163     free_PKCS8PrivateKeyInfo(&ki);
164     return ret;
165 }
166
167 static int
168 parse_pem_private_key(hx509_context context, const char *fn, int flags,
169                       struct hx509_collector *c,
170                       const hx509_pem_header *headers,
171                       const void *data, size_t len,
172                       const AlgorithmIdentifier *ai)
173 {
174     int ret = 0;
175     const char *enc;
176
177     enc = hx509_pem_find_header(headers, "Proc-Type");
178     if (enc) {
179         const char *dek;
180         char *type, *iv;
181         ssize_t ssize, size;
182         void *ivdata;
183         const EVP_CIPHER *cipher;
184         const struct _hx509_password *pw;
185         hx509_lock lock;
186         int decrypted = 0;
187         size_t i;
188
189         lock = _hx509_collector_get_lock(c);
190         if (lock == NULL) {
191             hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP,
192                                    "Failed to get password for "
193                                    "password protected file %s", fn);
194             return HX509_ALG_NOT_SUPP;
195         }
196
197         if (strcmp(enc, "4,ENCRYPTED") != 0) {
198             hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
199                                    "Private key encrypted in unknown method %s "
200                                    "in file",
201                                    enc, fn);
202             hx509_clear_error_string(context);
203             return HX509_PARSING_KEY_FAILED;
204         }
205
206         dek = hx509_pem_find_header(headers, "DEK-Info");
207         if (dek == NULL) {
208             hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
209                                    "Encrypted private key missing DEK-Info");
210             return HX509_PARSING_KEY_FAILED;
211         }
212
213         type = strdup(dek);
214         if (type == NULL) {
215             hx509_clear_error_string(context);
216             return ENOMEM;
217         }
218
219         iv = strchr(type, ',');
220         if (iv == NULL) {
221             free(type);
222             hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
223                                    "IV missing");
224             return HX509_PARSING_KEY_FAILED;
225         }
226
227         *iv++ = '\0';
228
229         size = strlen(iv);
230         ivdata = malloc(size);
231         if (ivdata == NULL) {
232             hx509_clear_error_string(context);
233             free(type);
234             return ENOMEM;
235         }
236
237         cipher = EVP_get_cipherbyname(type);
238         if (cipher == NULL) {
239             free(ivdata);
240             hx509_set_error_string(context, 0, HX509_ALG_NOT_SUPP,
241                                    "Private key encrypted with "
242                                    "unsupported cipher: %s",
243                                    type);
244             free(type);
245             return HX509_ALG_NOT_SUPP;
246         }
247
248 #define PKCS5_SALT_LEN 8
249
250         ssize = hex_decode(iv, ivdata, size);
251         free(type);
252         type = NULL;
253         iv = NULL;
254
255         if (ssize < 0 || ssize < PKCS5_SALT_LEN || ssize < EVP_CIPHER_iv_length(cipher)) {
256             free(ivdata);
257             hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
258                                    "Salt have wrong length in "
259                                    "private key file");
260             return HX509_PARSING_KEY_FAILED;
261         }
262
263         pw = _hx509_lock_get_passwords(lock);
264         if (pw != NULL) {
265             const void *password;
266             size_t passwordlen;
267
268             for (i = 0; i < pw->len; i++) {
269                 password = pw->val[i];
270                 passwordlen = strlen(password);
271
272                 ret = try_decrypt(context, c, flags, ai, cipher, ivdata,
273                                   password, passwordlen, data, len);
274                 if (ret == 0) {
275                     decrypted = 1;
276                     break;
277                 }
278             }
279         }
280         if (!decrypted) {
281             hx509_prompt prompt;
282             char password[128];
283
284             memset(&prompt, 0, sizeof(prompt));
285
286             prompt.prompt = "Password for keyfile: ";
287             prompt.type = HX509_PROMPT_TYPE_PASSWORD;
288             prompt.reply.data = password;
289             prompt.reply.length = sizeof(password);
290
291             ret = hx509_lock_prompt(lock, &prompt);
292             if (ret == 0)
293                 ret = try_decrypt(context, c, flags, ai, cipher, ivdata,
294                                   password, strlen(password), data, len);
295             /* XXX add password to lock password collection ? */
296             memset_s(password, sizeof(password), 0, sizeof(password));
297         }
298         free(ivdata);
299
300     } else if (!(flags & HX509_CERTS_NO_PRIVATE_KEYS)) {
301         heim_octet_string keydata;
302
303         keydata.data = rk_UNCONST(data);
304         keydata.length = len;
305
306         ret = _hx509_collector_private_key_add(context, c, ai, NULL,
307                                                &keydata, NULL);
308     }
309
310     return ret;
311 }
312
313
314 struct pem_formats {
315     const char *name;
316     int (*func)(hx509_context, const char *, int, struct hx509_collector *,
317                 const hx509_pem_header *, const void *, size_t,
318                 const AlgorithmIdentifier *);
319     const AlgorithmIdentifier *(*ai)(void);
320 } formats[] = {
321     { "CERTIFICATE", parse_certificate, NULL },
322     { "PRIVATE KEY", parse_pkcs8_private_key, NULL },
323     { "RSA PRIVATE KEY", parse_pem_private_key, hx509_signature_rsa },
324 #ifdef HAVE_HCRYPTO_W_OPENSSL
325     { "EC PRIVATE KEY", parse_pem_private_key, hx509_signature_ecPublicKey }
326 #endif
327 };
328
329
330 struct pem_ctx {
331     int flags;
332     struct hx509_collector *c;
333 };
334
335 static int
336 pem_func(hx509_context context, const char *type,
337          const hx509_pem_header *header,
338          const void *data, size_t len, void *ctx)
339 {
340     struct pem_ctx *pem_ctx = (struct pem_ctx*)ctx;
341     int ret = 0;
342     size_t j;
343
344     for (j = 0; j < sizeof(formats)/sizeof(formats[0]); j++) {
345         const char *q = formats[j].name;
346         if (strcasecmp(type, q) == 0) {
347             const AlgorithmIdentifier *ai = NULL;
348
349             if (formats[j].ai != NULL)
350                 ai = (*formats[j].ai)();
351
352             ret = (*formats[j].func)(context, NULL, pem_ctx->flags, pem_ctx->c,
353                                      header, data, len, ai);
354             if (ret && (pem_ctx->flags & HX509_CERTS_UNPROTECT_ALL)) {
355                 hx509_set_error_string(context, HX509_ERROR_APPEND, ret,
356                                        "Failed parseing PEM format %s", type);
357                 return ret;
358             }
359             break;
360         }
361     }
362     if (j == sizeof(formats)/sizeof(formats[0])) {
363         ret = HX509_UNSUPPORTED_OPERATION;
364         hx509_set_error_string(context, 0, ret,
365                                "Found no matching PEM format for %s", type);
366         return ret;
367     }
368     return 0;
369 }
370
371 /*
372  *
373  */
374
375 static int
376 file_init_common(hx509_context context,
377                  hx509_certs certs, void **data, int flags,
378                  const char *residue, hx509_lock lock, outformat format)
379 {
380     char *p, *pnext;
381     struct ks_file *ksf = NULL;
382     hx509_private_key *keys = NULL;
383     int ret;
384     struct pem_ctx pem_ctx;
385
386     pem_ctx.flags = flags;
387     pem_ctx.c = NULL;
388
389     if (residue == NULL || residue[0] == '\0') {
390         hx509_set_error_string(context, 0, EINVAL,
391                                "PEM file name not specified");
392         return EINVAL;
393     }
394
395     *data = NULL;
396
397     if (lock == NULL)
398         lock = _hx509_empty_lock;
399
400     ksf = calloc(1, sizeof(*ksf));
401     if (ksf == NULL) {
402         hx509_clear_error_string(context);
403         return ENOMEM;
404     }
405     ksf->format = format;
406
407     ksf->fn = strdup(residue);
408     if (ksf->fn == NULL) {
409         hx509_clear_error_string(context);
410         ret = ENOMEM;
411         goto out;
412     }
413
414     /*
415      * XXX this is broken, the function should parse the file before
416      * overwriting it
417      */
418
419     if (flags & HX509_CERTS_CREATE) {
420         /*
421          * Note that the file creation is deferred until file_store() is
422          * called.
423          */
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
471                 if (formats[i].ai != NULL)
472                     ai = (*formats[i].ai)();
473
474                 ret = (*formats[i].func)(context, p, pem_ctx.flags, pem_ctx.c,
475                                          NULL, ptr, length, ai);
476                 if (ret == 0)
477                     break;
478             }
479             rk_xfree(ptr);
480             if (ret) {
481                 hx509_clear_error_string(context);
482                 goto out;
483             }
484         }
485     }
486
487     ret = _hx509_collector_collect_certs(context, pem_ctx.c, &ksf->certs);
488     if (ret)
489         goto out;
490
491     ret = _hx509_collector_collect_private_keys(context, pem_ctx.c, &keys);
492     if (ret == 0) {
493         int i;
494
495         for (i = 0; keys[i]; i++)
496             _hx509_certs_keys_add(context, ksf->certs, keys[i]);
497         _hx509_certs_keys_free(context, keys);
498     }
499
500 out:
501     if (ret == 0)
502         *data = ksf;
503     else {
504         if (ksf->fn)
505             free(ksf->fn);
506         free(ksf);
507     }
508     if (pem_ctx.c)
509         _hx509_collector_free(pem_ctx.c);
510
511     return ret;
512 }
513
514 static int
515 file_init_pem(hx509_context context,
516               hx509_certs certs, void **data, int flags,
517               const char *residue, hx509_lock lock)
518 {
519     return file_init_common(context, certs, data, flags, residue, lock, USE_PEM);
520 }
521
522 static int
523 file_init_der(hx509_context context,
524               hx509_certs certs, void **data, int flags,
525               const char *residue, hx509_lock lock)
526 {
527     return file_init_common(context, certs, data, flags, residue, lock, USE_DER);
528 }
529
530 static int
531 file_free(hx509_certs certs, void *data)
532 {
533     struct ks_file *ksf = data;
534     hx509_certs_free(&ksf->certs);
535     free(ksf->fn);
536     free(ksf);
537     return 0;
538 }
539
540 struct store_ctx {
541     FILE *f;
542     outformat format;
543     int store_flags;
544 };
545
546 static int HX509_LIB_CALL
547 store_func(hx509_context context, void *ctx, hx509_cert c)
548 {
549     struct store_ctx *sc = ctx;
550     heim_octet_string data;
551     int ret;
552
553     if (hx509_cert_have_private_key_only(c)) {
554         data.length = 0;
555         data.data = NULL;
556     } else {
557         ret = hx509_cert_binary(context, c, &data);
558         if (ret)
559             return ret;
560     }
561
562     switch (sc->format) {
563     case USE_DER:
564         /* Can't store both.  Well, we could, but nothing will support it */
565         if (data.data) {
566             fwrite(data.data, data.length, 1, sc->f);
567             free(data.data);
568         } else if (_hx509_cert_private_key_exportable(c) &&
569                    !(sc->store_flags & HX509_CERTS_STORE_NO_PRIVATE_KEYS)) {
570             hx509_private_key key = _hx509_cert_private_key(c);
571
572             ret = _hx509_private_key_export(context, key,
573                                             HX509_KEY_FORMAT_DER, &data);
574             fwrite(data.data, data.length, 1, sc->f);
575             free(data.data);
576         }
577         break;
578     case USE_PEM:
579         if (_hx509_cert_private_key_exportable(c) &&
580             !(sc->store_flags & HX509_CERTS_STORE_NO_PRIVATE_KEYS)) {
581             heim_octet_string priv_key;
582             hx509_private_key key = _hx509_cert_private_key(c);
583
584             ret = _hx509_private_key_export(context, key,
585                                             HX509_KEY_FORMAT_DER, &priv_key);
586             if (ret) {
587                 free(data.data);
588                 break;
589             }
590             hx509_pem_write(context, _hx509_private_pem_name(key), NULL, sc->f,
591                             priv_key.data, priv_key.length);
592             free(priv_key.data);
593         }
594         if (data.data) {
595             hx509_pem_write(context, "CERTIFICATE", NULL, sc->f,
596                             data.data, data.length);
597             free(data.data);
598         }
599         break;
600     }
601
602     return 0;
603 }
604
605 static int
606 mk_temp(const char *fn, char **tfn)
607 {
608     char *ds;
609     int ret = -1;
610
611 #ifdef WIN32
612     char buf[PATH_MAX];
613     char *p;
614
615     *tfn = NULL;
616
617     if ((ds = _fullpath(buf, fn, sizeof(buf))) == NULL) {
618         errno = errno ? errno : ENAMETOOLONG;
619         return -1;
620     }
621
622     if ((p = strrchr(ds, '\\')) == NULL) {
623         ret = asprintf(tfn, ".%s-XXXXXX", ds); /* XXX can't happen */
624     } else {
625         *(p++) = '\0';
626         ret = asprintf(tfn, "%s/.%s-XXXXXX", ds, p);
627     }
628 #else
629     *tfn = NULL;
630     if ((ds = strdup(fn)))
631         ret = asprintf(tfn, "%s/.%s-XXXXXX", dirname(ds), basename(ds));
632     free(ds);
633 #endif
634
635     /*
636      * Using mkostemp() risks leaving garbage files lying around.  To do better
637      * without resorting to file locks (which have their own problems) we need
638      * O_TMPFILE and linkat(2), which only Linux has.
639      */
640     return  (ret == -1 || *tfn == NULL) ? -1 : mkostemp(*tfn, O_CLOEXEC);
641 }
642
643 static int
644 file_store(hx509_context context,
645            hx509_certs certs, void *data, int flags, hx509_lock lock)
646 {
647     struct ks_file *ksf = data;
648     struct store_ctx sc;
649     char *tfn;
650     int ret;
651     int fd;
652
653     sc.f = NULL;
654     fd = mk_temp(ksf->fn, &tfn);
655     if (fd > -1)
656         sc.f = fdopen(fd, "w");
657     if (sc.f == NULL) {
658         hx509_set_error_string(context, 0, ret = errno,
659                                "Failed to open file %s for writing", ksf->fn);
660         if (fd > -1)
661             (void) close(fd);
662         return ret;
663     }
664     rk_cloexec_file(sc.f);
665     sc.store_flags = flags;
666     sc.format = ksf->format;
667
668     ret = hx509_certs_iter_f(context, ksf->certs, store_func, &sc);
669     if (ret == 0)
670         ret = fclose(sc.f);
671     else
672         (void) fclose(sc.f);
673     if (ret)
674         (void) unlink(tfn);
675     else
676         (void) rename(tfn, ksf->fn);
677     free(tfn);
678     return ret;
679 }
680
681 static int
682 file_add(hx509_context context, hx509_certs certs, void *data, hx509_cert c)
683 {
684     struct ks_file *ksf = data;
685     return hx509_certs_add(context, ksf->certs, c);
686 }
687
688 static int
689 file_iter_start(hx509_context context,
690                 hx509_certs certs, void *data, void **cursor)
691 {
692     struct ks_file *ksf = data;
693     return hx509_certs_start_seq(context, ksf->certs, cursor);
694 }
695
696 static int
697 file_iter(hx509_context context,
698           hx509_certs certs, void *data, void *iter, hx509_cert *cert)
699 {
700     struct ks_file *ksf = data;
701     return hx509_certs_next_cert(context, ksf->certs, iter, cert);
702 }
703
704 static int
705 file_iter_end(hx509_context context,
706               hx509_certs certs,
707               void *data,
708               void *cursor)
709 {
710     struct ks_file *ksf = data;
711     return hx509_certs_end_seq(context, ksf->certs, cursor);
712 }
713
714 static int
715 file_getkeys(hx509_context context,
716              hx509_certs certs,
717              void *data,
718              hx509_private_key **keys)
719 {
720     struct ks_file *ksf = data;
721     return _hx509_certs_keys_get(context, ksf->certs, keys);
722 }
723
724 static int
725 file_addkey(hx509_context context,
726              hx509_certs certs,
727              void *data,
728              hx509_private_key key)
729 {
730     struct ks_file *ksf = data;
731     return _hx509_certs_keys_add(context, ksf->certs, key);
732 }
733
734 static int
735 file_destroy(hx509_context context,
736              hx509_certs certs,
737              void *data)
738 {
739     struct ks_file *ksf = data;
740     return _hx509_erase_file(context, ksf->fn);
741 }
742
743 static struct hx509_keyset_ops keyset_file = {
744     "FILE",
745     0,
746     file_init_pem,
747     file_store,
748     file_free,
749     file_add,
750     NULL,
751     file_iter_start,
752     file_iter,
753     file_iter_end,
754     NULL,
755     file_getkeys,
756     file_addkey,
757     file_destroy
758 };
759
760 static struct hx509_keyset_ops keyset_pemfile = {
761     "PEM-FILE",
762     0,
763     file_init_pem,
764     file_store,
765     file_free,
766     file_add,
767     NULL,
768     file_iter_start,
769     file_iter,
770     file_iter_end,
771     NULL,
772     file_getkeys,
773     file_addkey,
774     file_destroy
775 };
776
777 static struct hx509_keyset_ops keyset_derfile = {
778     "DER-FILE",
779     0,
780     file_init_der,
781     file_store,
782     file_free,
783     file_add,
784     NULL,
785     file_iter_start,
786     file_iter,
787     file_iter_end,
788     NULL,
789     file_getkeys,
790     file_addkey,
791     file_destroy
792 };
793
794
795 HX509_LIB_FUNCTION void HX509_LIB_CALL
796 _hx509_ks_file_register(hx509_context context)
797 {
798     _hx509_ks_register(context, &keyset_file);
799     _hx509_ks_register(context, &keyset_pemfile);
800     _hx509_ks_register(context, &keyset_derfile);
801 }