vici: Fix refcount for CA certificates when reloading authority sections
[strongswan.git] / src / libcharon / plugins / vici / vici_authority.c
1 /*
2 * Copyright (C) 2016-2020 Tobias Brunner
3 * Copyright (C) 2015 Andreas Steffen
4 * HSR Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #define _GNU_SOURCE
18
19 #include "vici_authority.h"
20 #include "vici_builder.h"
21
22 #include <threading/rwlock.h>
23 #include <collections/linked_list.h>
24 #include <credentials/certificates/x509.h>
25 #include <utils/debug.h>
26
27 #include <stdio.h>
28
29 typedef struct private_vici_authority_t private_vici_authority_t;
30
31 /**
32 * Private data of an vici_authority_t object.
33 */
34 struct private_vici_authority_t {
35
36 /**
37 * Public vici_authority_t interface.
38 */
39 vici_authority_t public;
40
41 /**
42 * Dispatcher
43 */
44 vici_dispatcher_t *dispatcher;
45
46 /**
47 * List of certification authorities (authority_t*)
48 */
49 linked_list_t *authorities;
50
51 /**
52 * List of CA certificates (ca_cert_t*)
53 */
54 linked_list_t *certs;
55
56 /**
57 * rwlock to lock access to certification authorities
58 */
59 rwlock_t *lock;
60
61 };
62
63 typedef struct authority_t authority_t;
64
65 /**
66 * loaded certification authorities
67 */
68 struct authority_t {
69
70 /**
71 * Name of the certification authority
72 */
73 char *name;
74
75 /**
76 * Reference to CA certificate
77 */
78 certificate_t *cert;
79
80 /**
81 * CRL URIs
82 */
83 linked_list_t *crl_uris;
84
85 /**
86 * OCSP URIs
87 */
88 linked_list_t *ocsp_uris;
89
90 /**
91 * Base URI used for certificates from this CA
92 */
93 char *cert_uri_base;
94 };
95
96 /**
97 * create a new certification authority
98 */
99 static authority_t *authority_create(char *name)
100 {
101 authority_t *authority;
102
103 INIT(authority,
104 .name = strdup(name),
105 .crl_uris = linked_list_create(),
106 .ocsp_uris = linked_list_create(),
107 );
108
109 return authority;
110 }
111
112 CALLBACK(authority_destroy, void,
113 authority_t *this)
114 {
115 this->crl_uris->destroy_function(this->crl_uris, free);
116 this->ocsp_uris->destroy_function(this->ocsp_uris, free);
117 DESTROY_IF(this->cert);
118 free(this->cert_uri_base);
119 free(this->name);
120 free(this);
121 }
122
123 typedef struct ca_cert_t ca_cert_t;
124
125 /**
126 * Loaded CA certificate.
127 */
128 struct ca_cert_t {
129
130 /**
131 * Reference to certificate.
132 */
133 certificate_t *cert;
134
135 /**
136 * The number of authority sections referring to this certificate.
137 */
138 u_int count;
139
140 /**
141 * TRUE if this certificate was (also) added externally.
142 */
143 bool external;
144 };
145
146 /**
147 * Destroy a CA certificate entry
148 */
149 CALLBACK(ca_cert_destroy, void,
150 ca_cert_t *this)
151 {
152 this->cert->destroy(this->cert);
153 free(this);
154 }
155
156 CALLBACK(match_cert, bool,
157 ca_cert_t *item, va_list args)
158 {
159 certificate_t *cert;
160
161 VA_ARGS_VGET(args, cert);
162 return cert->equals(cert, item->cert);
163 }
164
165 /**
166 * Add a CA certificate to the local store
167 */
168 static certificate_t *add_cert_internal(private_vici_authority_t *this,
169 certificate_t *cert, bool external)
170 {
171 ca_cert_t *found;
172
173 if (this->certs->find_first(this->certs, match_cert, (void**)&found, cert))
174 {
175 cert->destroy(cert);
176 cert = found->cert->get_ref(found->cert);
177 }
178 else
179 {
180 INIT(found,
181 .cert = cert->get_ref(cert)
182 );
183 this->certs->insert_first(this->certs, found);
184 }
185 if (external)
186 {
187 found->external = TRUE;
188 }
189 else
190 {
191 found->count++;
192 }
193 return cert;
194 }
195
196 CALLBACK(remove_external_certs, bool,
197 ca_cert_t *item, void *unused)
198 {
199 if (item->external)
200 {
201 item->external = FALSE;
202
203 if (!item->count)
204 {
205 ca_cert_destroy(item);
206 return TRUE;
207 }
208 }
209 return FALSE;
210 }
211
212 CALLBACK2(remove_cert, bool,
213 ca_cert_t *item, certificate_t *cert)
214 {
215 if (cert == item->cert)
216 {
217 if (--item->count == 0 && !item->external)
218 {
219 ca_cert_destroy(item);
220 return TRUE;
221 }
222 }
223 return FALSE;
224 }
225
226 /**
227 * Create a (error) reply message
228 */
229 static vici_message_t* create_reply(char *fmt, ...)
230 {
231 vici_builder_t *builder;
232 va_list args;
233
234 builder = vici_builder_create();
235 builder->add_kv(builder, "success", fmt ? "no" : "yes");
236 if (fmt)
237 {
238 va_start(args, fmt);
239 builder->vadd_kv(builder, "errmsg", fmt, args);
240 va_end(args);
241 }
242 return builder->finalize(builder);
243 }
244
245 /**
246 * A rule to parse a key/value or list item
247 */
248 typedef struct {
249 /** name of the key/value or list */
250 char *name;
251 /** function to parse value */
252 bool (*parse)(void *out, chunk_t value);
253 /** result, passed to parse() */
254 void *out;
255 } parse_rule_t;
256
257 /**
258 * Parse key/values using a rule-set
259 */
260 static bool parse_rules(parse_rule_t *rules, int count, char *name,
261 chunk_t value, vici_message_t **reply)
262 {
263 int i;
264
265 for (i = 0; i < count; i++)
266 {
267 if (streq(name, rules[i].name))
268 {
269 if (rules[i].parse(rules[i].out, value))
270 {
271 return TRUE;
272 }
273 *reply = create_reply("invalid value for: %s, authority discarded",
274 name);
275 return FALSE;
276 }
277 }
278 *reply = create_reply("unknown option: %s, authority discarded", name);
279 return FALSE;
280 }
281
282 /**
283 * Parse callback data, passed to each callback
284 */
285 typedef struct {
286 private_vici_authority_t *this;
287 vici_message_t *reply;
288 } request_data_t;
289
290 /**
291 * Data associated with an authority load
292 */
293 typedef struct {
294 request_data_t *request;
295 authority_t *authority;
296 char *handle;
297 uint32_t slot;
298 char *module;
299 char *file;
300 } load_data_t;
301
302 /**
303 * Clean up data associated with an authority load
304 */
305 static void free_load_data(load_data_t *data)
306 {
307 if (data->authority)
308 {
309 authority_destroy(data->authority);
310 }
311 free(data->handle);
312 free(data->module);
313 free(data->file);
314 free(data);
315 }
316
317 /**
318 * Parse a string
319 */
320 CALLBACK(parse_string, bool,
321 char **str, chunk_t v)
322 {
323 if (!chunk_printable(v, NULL, ' '))
324 {
325 return FALSE;
326 }
327 *str = strndup(v.ptr, v.len);
328
329 return TRUE;
330 }
331
332 /**
333 * Parse a uint32_t
334 */
335 CALLBACK(parse_uint32, bool,
336 uint32_t *out, chunk_t v)
337 {
338 char buf[16], *end;
339 u_long l;
340
341 if (!vici_stringify(v, buf, sizeof(buf)))
342 {
343 return FALSE;
344 }
345 l = strtoul(buf, &end, 0);
346 if (*end == 0)
347 {
348 *out = l;
349 return TRUE;
350 }
351 return FALSE;
352 }
353
354 /**
355 * Parse list of URIs
356 */
357 CALLBACK(parse_uris, bool,
358 linked_list_t *out, chunk_t v)
359 {
360 char *uri;
361
362 if (!chunk_printable(v, NULL, ' '))
363 {
364 return FALSE;
365 }
366 uri = strndup(v.ptr, v.len);
367 out->insert_last(out, uri);
368
369 return TRUE;
370 }
371
372 /**
373 * Parse a CA certificate
374 */
375 CALLBACK(parse_cacert, bool,
376 certificate_t **cacert, chunk_t v)
377 {
378 certificate_t *cert;
379 x509_t *x509;
380
381 cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509,
382 BUILD_BLOB_PEM, v, BUILD_END);
383 if (!cert)
384 {
385 return create_reply("parsing %N certificate failed",
386 certificate_type_names, CERT_X509);
387 }
388 x509 = (x509_t*)cert;
389
390 if ((x509->get_flags(x509) & X509_CA) != X509_CA)
391 {
392 cert->destroy(cert);
393 return create_reply("certificate without CA flag, rejected");
394 }
395 *cacert = cert;
396
397 return TRUE;
398 }
399
400 CALLBACK(authority_kv, bool,
401 load_data_t *data, vici_message_t *message, char *name, chunk_t value)
402 {
403 parse_rule_t rules[] = {
404 { "cacert", parse_cacert, &data->authority->cert },
405 { "file", parse_string, &data->file },
406 { "handle", parse_string, &data->handle },
407 { "slot", parse_uint32, &data->slot },
408 { "module", parse_string, &data->module },
409 { "cert_uri_base", parse_string, &data->authority->cert_uri_base },
410 };
411
412 return parse_rules(rules, countof(rules), name, value,
413 &data->request->reply);
414 }
415
416 CALLBACK(authority_li, bool,
417 load_data_t *data, vici_message_t *message, char *name, chunk_t value)
418 {
419 parse_rule_t rules[] = {
420 { "crl_uris", parse_uris, data->authority->crl_uris },
421 { "ocsp_uris", parse_uris, data->authority->ocsp_uris },
422 };
423
424 return parse_rules(rules, countof(rules), name, value,
425 &data->request->reply);
426 }
427
428 static void log_authority_data(authority_t *authority)
429 {
430 enumerator_t *enumerator;
431 identification_t *subject;
432 bool first = TRUE;
433 char *uri;
434
435 subject = authority->cert->get_subject(authority->cert);
436 DBG2(DBG_CFG, " cacert = %Y", subject);
437
438 enumerator = authority->crl_uris->create_enumerator(authority->crl_uris);
439 while (enumerator->enumerate(enumerator, &uri))
440 {
441 if (first)
442 {
443 DBG2(DBG_CFG, " crl_uris = %s", uri);
444 first = FALSE;
445 }
446 else
447 {
448 DBG2(DBG_CFG, " %s", uri);
449 }
450 }
451 enumerator->destroy(enumerator);
452
453 first = TRUE;
454 enumerator = authority->ocsp_uris->create_enumerator(authority->ocsp_uris);
455 while (enumerator->enumerate(enumerator, &uri))
456 {
457 if (first)
458 {
459 DBG2(DBG_CFG, " ocsp_uris = %s", uri);
460 first = FALSE;
461 }
462 else
463 {
464 DBG2(DBG_CFG, " %s", uri);
465 }
466 }
467 enumerator->destroy(enumerator);
468
469 if (authority->cert_uri_base)
470 {
471 DBG2(DBG_CFG, " cert_uri_base = %s", authority->cert_uri_base);
472 }
473 }
474
475 CALLBACK(authority_sn, bool,
476 request_data_t *request, vici_message_t *message,
477 vici_parse_context_t *ctx, char *name)
478 {
479 enumerator_t *enumerator;
480 linked_list_t *authorities;
481 authority_t *authority;
482 load_data_t *data;
483 chunk_t handle;
484
485 INIT(data,
486 .request = request,
487 .authority = authority_create(name),
488 .slot = -1,
489 );
490
491 DBG2(DBG_CFG, " authority %s:", name);
492
493 if (!message->parse(message, ctx, NULL, authority_kv, authority_li, data))
494 {
495 free_load_data(data);
496 return FALSE;
497 }
498 if (!data->authority->cert)
499 {
500 if (data->file)
501 {
502 data->authority->cert = lib->creds->create(lib->creds,
503 CRED_CERTIFICATE, CERT_X509,
504 BUILD_FROM_FILE, data->file, BUILD_END);
505 }
506 else if (data->handle)
507 {
508 handle = chunk_from_hex(chunk_from_str(data->handle), NULL);
509 if (data->slot != -1)
510 {
511 data->authority->cert = lib->creds->create(lib->creds,
512 CRED_CERTIFICATE, CERT_X509,
513 BUILD_PKCS11_KEYID, handle,
514 BUILD_PKCS11_SLOT, data->slot,
515 data->module ? BUILD_PKCS11_MODULE : BUILD_END,
516 data->module, BUILD_END);
517 }
518 else
519 {
520 data->authority->cert = lib->creds->create(lib->creds,
521 CRED_CERTIFICATE, CERT_X509,
522 BUILD_PKCS11_KEYID, handle,
523 data->module ? BUILD_PKCS11_MODULE : BUILD_END,
524 data->module, BUILD_END);
525 }
526 chunk_free(&handle);
527 }
528 }
529 if (!data->authority->cert)
530 {
531 request->reply = create_reply("CA certificate missing: %s", name);
532 free_load_data(data);
533 return FALSE;
534 }
535 log_authority_data(data->authority);
536
537 request->this->lock->write_lock(request->this->lock);
538
539 data->authority->cert = add_cert_internal(request->this,
540 data->authority->cert, FALSE);
541
542 authorities = request->this->authorities;
543 enumerator = authorities->create_enumerator(authorities);
544 while (enumerator->enumerate(enumerator, &authority))
545 {
546 if (streq(authority->name, name))
547 {
548 /* remove the old authority definition */
549 authorities->remove_at(authorities, enumerator);
550 request->this->certs->remove(request->this->certs, authority->cert,
551 remove_cert);
552 authority_destroy(authority);
553 break;
554 }
555 }
556 enumerator->destroy(enumerator);
557 authorities->insert_last(authorities, data->authority);
558
559 request->this->lock->unlock(request->this->lock);
560 data->authority = NULL;
561 free_load_data(data);
562
563 return TRUE;
564 }
565
566 CALLBACK(load_authority, vici_message_t*,
567 private_vici_authority_t *this, char *name, u_int id, vici_message_t *message)
568 {
569 request_data_t request = {
570 .this = this,
571 };
572
573 if (!message->parse(message, NULL, authority_sn, NULL, NULL, &request))
574 {
575 if (request.reply)
576 {
577 return request.reply;
578 }
579 return create_reply("parsing request failed");
580 }
581 return create_reply(NULL);
582 }
583
584 CALLBACK(unload_authority, vici_message_t*,
585 private_vici_authority_t *this, char *name, u_int id, vici_message_t *message)
586 {
587 enumerator_t *enumerator;
588 authority_t *authority;
589 char *authority_name;
590 bool found = FALSE;
591
592 authority_name = message->get_str(message, NULL, "name");
593 if (!authority_name)
594 {
595 return create_reply("unload: missing authority name");
596 }
597
598 this->lock->write_lock(this->lock);
599 enumerator = this->authorities->create_enumerator(this->authorities);
600 while (enumerator->enumerate(enumerator, &authority))
601 {
602 if (streq(authority->name, authority_name))
603 {
604 this->authorities->remove_at(this->authorities, enumerator);
605 this->certs->remove(this->certs, authority->cert, remove_cert);
606 authority_destroy(authority);
607 found = TRUE;
608 break;
609 }
610 }
611 enumerator->destroy(enumerator);
612 this->lock->unlock(this->lock);
613
614 if (!found)
615 {
616 return create_reply("unload: authority '%s' not found", authority_name);
617 }
618 lib->credmgr->flush_cache(lib->credmgr, CERT_ANY);
619 return create_reply(NULL);
620 }
621
622 CALLBACK(get_authorities, vici_message_t*,
623 private_vici_authority_t *this, char *name, u_int id,
624 vici_message_t *message)
625 {
626 vici_builder_t *builder;
627 enumerator_t *enumerator;
628 authority_t *authority;
629
630 builder = vici_builder_create();
631 builder->begin_list(builder, "authorities");
632
633 this->lock->read_lock(this->lock);
634 enumerator = this->authorities->create_enumerator(this->authorities);
635 while (enumerator->enumerate(enumerator, &authority))
636 {
637 builder->add_li(builder, "%s", authority->name);
638 }
639 enumerator->destroy(enumerator);
640 this->lock->unlock(this->lock);
641
642 builder->end_list(builder);
643
644 return builder->finalize(builder);
645 }
646
647 CALLBACK(list_authorities, vici_message_t*,
648 private_vici_authority_t *this, char *name, u_int id, vici_message_t *request)
649 {
650 enumerator_t *enumerator, *e;
651 authority_t *authority;
652 vici_builder_t *b;
653 char *str, *uri;
654
655 str = request->get_str(request, NULL, "name");
656
657 this->lock->read_lock(this->lock);
658 enumerator = this->authorities->create_enumerator(this->authorities);
659 while (enumerator->enumerate(enumerator, &authority))
660 {
661 if (str && !streq(str, authority->name))
662 {
663 continue;
664 }
665 b = vici_builder_create();
666
667 /* open authority section */
668 b->begin_section(b, authority->name);
669
670 /* subject DN of cacert */
671 b->add_kv(b, "cacert", "%Y",
672 authority->cert->get_subject(authority->cert));
673
674 /* list of crl_uris */
675 b->begin_list(b, "crl_uris");
676 e = authority->crl_uris->create_enumerator(authority->crl_uris);
677 while (e->enumerate(e, &uri))
678 {
679 b->add_li(b, "%s", uri);
680 }
681 e->destroy(e);
682 b->end_list(b);
683
684 /* list of ocsp_uris */
685 b->begin_list(b, "ocsp_uris");
686 e = authority->ocsp_uris->create_enumerator(authority->ocsp_uris);
687 while (e->enumerate(e, &uri))
688 {
689 b->add_li(b, "%s", uri);
690 }
691 e->destroy(e);
692 b->end_list(b);
693
694 /* cert_uri_base */
695 if (authority->cert_uri_base)
696 {
697 b->add_kv(b, "cert_uri_base", "%s", authority->cert_uri_base);
698 }
699
700 /* close authority and raise event */
701 b->end_section(b);
702 this->dispatcher->raise_event(this->dispatcher, "list-authority", id,
703 b->finalize(b));
704 }
705 enumerator->destroy(enumerator);
706 this->lock->unlock(this->lock);
707
708 b = vici_builder_create();
709 return b->finalize(b);
710 }
711
712 static void manage_command(private_vici_authority_t *this,
713 char *name, vici_command_cb_t cb, bool reg)
714 {
715 this->dispatcher->manage_command(this->dispatcher, name,
716 reg ? cb : NULL, this);
717 }
718
719 /**
720 * (Un-)register dispatcher functions
721 */
722 static void manage_commands(private_vici_authority_t *this, bool reg)
723 {
724 this->dispatcher->manage_event(this->dispatcher, "list-authority", reg);
725
726 manage_command(this, "load-authority", load_authority, reg);
727 manage_command(this, "unload-authority", unload_authority, reg);
728 manage_command(this, "get-authorities", get_authorities, reg);
729 manage_command(this, "list-authorities", list_authorities, reg);
730 }
731
732 /**
733 * Data for the certificate and CDP enumerator
734 */
735 typedef struct {
736 private_vici_authority_t *this;
737 certificate_type_t type;
738 key_type_t key;
739 identification_t *id;
740 } cert_data_t;
741
742 CALLBACK(cert_data_destroy, void,
743 cert_data_t *data)
744 {
745 data->this->lock->unlock(data->this->lock);
746 free(data);
747 }
748
749 CALLBACK(certs_filter, bool,
750 cert_data_t *data, enumerator_t *orig, va_list args)
751 {
752 ca_cert_t *ca;
753 certificate_t **out;
754
755 VA_ARGS_VGET(args, out);
756
757 while (orig->enumerate(orig, &ca))
758 {
759 if (certificate_matches(ca->cert, data->type, data->key, data->id))
760 {
761 *out = ca->cert;
762 return TRUE;
763 }
764 }
765 return FALSE;
766 }
767
768 METHOD(credential_set_t, create_cert_enumerator, enumerator_t*,
769 private_vici_authority_t *this, certificate_type_t cert, key_type_t key,
770 identification_t *id, bool trusted)
771 {
772 enumerator_t *enumerator;
773 cert_data_t *data;
774
775 INIT(data,
776 .this = this,
777 .type = cert,
778 .key = key,
779 .id = id,
780 );
781
782 this->lock->read_lock(this->lock);
783 enumerator = this->certs->create_enumerator(this->certs);
784 return enumerator_create_filter(enumerator, certs_filter, data,
785 cert_data_destroy);
786 }
787
788 CALLBACK(create_inner_cdp, enumerator_t*,
789 authority_t *authority, cert_data_t *data)
790 {
791 public_key_t *public;
792 enumerator_t *enumerator = NULL;
793 linked_list_t *list;
794
795 if (data->type == CERT_X509_OCSP_RESPONSE)
796 {
797 list = authority->ocsp_uris;
798 }
799 else
800 {
801 list = authority->crl_uris;
802 }
803
804 public = authority->cert->get_public_key(authority->cert);
805 if (public)
806 {
807 if (!data->id)
808 {
809 enumerator = list->create_enumerator(list);
810 }
811 else
812 {
813 if (public->has_fingerprint(public,
814 data->id->get_encoding(data->id)))
815 {
816 enumerator = list->create_enumerator(list);
817 }
818 }
819 public->destroy(public);
820 }
821 return enumerator;
822 }
823
824 CALLBACK(create_inner_cdp_hashandurl, enumerator_t*,
825 authority_t *authority, cert_data_t *data)
826 {
827 enumerator_t *enumerator = NULL;
828
829 if (!data->id || !authority->cert_uri_base)
830 {
831 return NULL;
832 }
833
834 if (authority->cert->has_subject(authority->cert,
835 data->id) != ID_MATCH_NONE)
836 {
837 enumerator = enumerator_create_single(strdup(authority->cert_uri_base),
838 free);
839 }
840 return enumerator;
841 }
842
843 METHOD(credential_set_t, create_cdp_enumerator, enumerator_t*,
844 private_vici_authority_t *this, certificate_type_t type,
845 identification_t *id)
846 {
847 cert_data_t *data;
848
849 switch (type)
850 { /* we serve CRLs, OCSP responders and URLs for "Hash and URL" */
851 case CERT_X509:
852 case CERT_X509_CRL:
853 case CERT_X509_OCSP_RESPONSE:
854 case CERT_ANY:
855 break;
856 default:
857 return NULL;
858 }
859
860 INIT(data,
861 .this = this,
862 .type = type,
863 .id = id,
864 );
865
866 this->lock->read_lock(this->lock);
867 return enumerator_create_nested(
868 this->authorities->create_enumerator(this->authorities),
869 (type == CERT_X509) ? (void*)create_inner_cdp_hashandurl :
870 (void*)create_inner_cdp, data, cert_data_destroy);
871 }
872
873 METHOD(vici_authority_t, add_ca_cert, certificate_t*,
874 private_vici_authority_t *this, certificate_t *cert)
875 {
876 this->lock->write_lock(this->lock);
877 cert = add_cert_internal(this, cert, TRUE);
878 this->lock->unlock(this->lock);
879 return cert;
880 }
881
882 METHOD(vici_authority_t, clear_ca_certs, void,
883 private_vici_authority_t *this)
884 {
885 this->lock->write_lock(this->lock);
886 this->certs->remove(this->certs, NULL, remove_external_certs);
887 this->lock->unlock(this->lock);
888 }
889
890 METHOD(vici_authority_t, destroy, void,
891 private_vici_authority_t *this)
892 {
893 manage_commands(this, FALSE);
894
895 this->authorities->destroy_function(this->authorities,
896 (void*)authority_destroy);
897 this->certs->destroy_function(this->certs, ca_cert_destroy);
898 this->lock->destroy(this->lock);
899 free(this);
900 }
901
902 /**
903 * See header
904 */
905 vici_authority_t *vici_authority_create(vici_dispatcher_t *dispatcher)
906 {
907 private_vici_authority_t *this;
908
909 INIT(this,
910 .public = {
911 .set = {
912 .create_private_enumerator = (void*)return_null,
913 .create_cert_enumerator = _create_cert_enumerator,
914 .create_shared_enumerator = (void*)return_null,
915 .create_cdp_enumerator = _create_cdp_enumerator,
916 .cache_cert = (void*)nop,
917 },
918 .add_ca_cert = _add_ca_cert,
919 .clear_ca_certs = _clear_ca_certs,
920 .destroy = _destroy,
921 },
922 .dispatcher = dispatcher,
923 .authorities = linked_list_create(),
924 .certs = linked_list_create(),
925 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
926 );
927
928 manage_commands(this, TRUE);
929
930 return &this->public;
931 }