08f6ce5c77c01f9cfd7dda4eab3861675ae201f7
[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 authority_destroy(authority);
551 break;
552 }
553 }
554 enumerator->destroy(enumerator);
555 authorities->insert_last(authorities, data->authority);
556
557 request->this->lock->unlock(request->this->lock);
558 data->authority = NULL;
559 free_load_data(data);
560
561 return TRUE;
562 }
563
564 CALLBACK(load_authority, vici_message_t*,
565 private_vici_authority_t *this, char *name, u_int id, vici_message_t *message)
566 {
567 request_data_t request = {
568 .this = this,
569 };
570
571 if (!message->parse(message, NULL, authority_sn, NULL, NULL, &request))
572 {
573 if (request.reply)
574 {
575 return request.reply;
576 }
577 return create_reply("parsing request failed");
578 }
579 return create_reply(NULL);
580 }
581
582 CALLBACK(unload_authority, vici_message_t*,
583 private_vici_authority_t *this, char *name, u_int id, vici_message_t *message)
584 {
585 enumerator_t *enumerator;
586 authority_t *authority;
587 char *authority_name;
588 bool found = FALSE;
589
590 authority_name = message->get_str(message, NULL, "name");
591 if (!authority_name)
592 {
593 return create_reply("unload: missing authority name");
594 }
595
596 this->lock->write_lock(this->lock);
597 enumerator = this->authorities->create_enumerator(this->authorities);
598 while (enumerator->enumerate(enumerator, &authority))
599 {
600 if (streq(authority->name, authority_name))
601 {
602 this->authorities->remove_at(this->authorities, enumerator);
603 this->certs->remove(this->certs, authority->cert, remove_cert);
604 authority_destroy(authority);
605 found = TRUE;
606 break;
607 }
608 }
609 enumerator->destroy(enumerator);
610 this->lock->unlock(this->lock);
611
612 if (!found)
613 {
614 return create_reply("unload: authority '%s' not found", authority_name);
615 }
616 lib->credmgr->flush_cache(lib->credmgr, CERT_ANY);
617 return create_reply(NULL);
618 }
619
620 CALLBACK(get_authorities, vici_message_t*,
621 private_vici_authority_t *this, char *name, u_int id,
622 vici_message_t *message)
623 {
624 vici_builder_t *builder;
625 enumerator_t *enumerator;
626 authority_t *authority;
627
628 builder = vici_builder_create();
629 builder->begin_list(builder, "authorities");
630
631 this->lock->read_lock(this->lock);
632 enumerator = this->authorities->create_enumerator(this->authorities);
633 while (enumerator->enumerate(enumerator, &authority))
634 {
635 builder->add_li(builder, "%s", authority->name);
636 }
637 enumerator->destroy(enumerator);
638 this->lock->unlock(this->lock);
639
640 builder->end_list(builder);
641
642 return builder->finalize(builder);
643 }
644
645 CALLBACK(list_authorities, vici_message_t*,
646 private_vici_authority_t *this, char *name, u_int id, vici_message_t *request)
647 {
648 enumerator_t *enumerator, *e;
649 authority_t *authority;
650 vici_builder_t *b;
651 char *str, *uri;
652
653 str = request->get_str(request, NULL, "name");
654
655 this->lock->read_lock(this->lock);
656 enumerator = this->authorities->create_enumerator(this->authorities);
657 while (enumerator->enumerate(enumerator, &authority))
658 {
659 if (str && !streq(str, authority->name))
660 {
661 continue;
662 }
663 b = vici_builder_create();
664
665 /* open authority section */
666 b->begin_section(b, authority->name);
667
668 /* subject DN of cacert */
669 b->add_kv(b, "cacert", "%Y",
670 authority->cert->get_subject(authority->cert));
671
672 /* list of crl_uris */
673 b->begin_list(b, "crl_uris");
674 e = authority->crl_uris->create_enumerator(authority->crl_uris);
675 while (e->enumerate(e, &uri))
676 {
677 b->add_li(b, "%s", uri);
678 }
679 e->destroy(e);
680 b->end_list(b);
681
682 /* list of ocsp_uris */
683 b->begin_list(b, "ocsp_uris");
684 e = authority->ocsp_uris->create_enumerator(authority->ocsp_uris);
685 while (e->enumerate(e, &uri))
686 {
687 b->add_li(b, "%s", uri);
688 }
689 e->destroy(e);
690 b->end_list(b);
691
692 /* cert_uri_base */
693 if (authority->cert_uri_base)
694 {
695 b->add_kv(b, "cert_uri_base", "%s", authority->cert_uri_base);
696 }
697
698 /* close authority and raise event */
699 b->end_section(b);
700 this->dispatcher->raise_event(this->dispatcher, "list-authority", id,
701 b->finalize(b));
702 }
703 enumerator->destroy(enumerator);
704 this->lock->unlock(this->lock);
705
706 b = vici_builder_create();
707 return b->finalize(b);
708 }
709
710 static void manage_command(private_vici_authority_t *this,
711 char *name, vici_command_cb_t cb, bool reg)
712 {
713 this->dispatcher->manage_command(this->dispatcher, name,
714 reg ? cb : NULL, this);
715 }
716
717 /**
718 * (Un-)register dispatcher functions
719 */
720 static void manage_commands(private_vici_authority_t *this, bool reg)
721 {
722 this->dispatcher->manage_event(this->dispatcher, "list-authority", reg);
723
724 manage_command(this, "load-authority", load_authority, reg);
725 manage_command(this, "unload-authority", unload_authority, reg);
726 manage_command(this, "get-authorities", get_authorities, reg);
727 manage_command(this, "list-authorities", list_authorities, reg);
728 }
729
730 /**
731 * Data for the certificate and CDP enumerator
732 */
733 typedef struct {
734 private_vici_authority_t *this;
735 certificate_type_t type;
736 key_type_t key;
737 identification_t *id;
738 } cert_data_t;
739
740 CALLBACK(cert_data_destroy, void,
741 cert_data_t *data)
742 {
743 data->this->lock->unlock(data->this->lock);
744 free(data);
745 }
746
747 CALLBACK(certs_filter, bool,
748 cert_data_t *data, enumerator_t *orig, va_list args)
749 {
750 ca_cert_t *ca;
751 certificate_t **out;
752
753 VA_ARGS_VGET(args, out);
754
755 while (orig->enumerate(orig, &ca))
756 {
757 if (certificate_matches(ca->cert, data->type, data->key, data->id))
758 {
759 *out = ca->cert;
760 return TRUE;
761 }
762 }
763 return FALSE;
764 }
765
766 METHOD(credential_set_t, create_cert_enumerator, enumerator_t*,
767 private_vici_authority_t *this, certificate_type_t cert, key_type_t key,
768 identification_t *id, bool trusted)
769 {
770 enumerator_t *enumerator;
771 cert_data_t *data;
772
773 INIT(data,
774 .this = this,
775 .type = cert,
776 .key = key,
777 .id = id,
778 );
779
780 this->lock->read_lock(this->lock);
781 enumerator = this->certs->create_enumerator(this->certs);
782 return enumerator_create_filter(enumerator, certs_filter, data,
783 cert_data_destroy);
784 }
785
786 CALLBACK(create_inner_cdp, enumerator_t*,
787 authority_t *authority, cert_data_t *data)
788 {
789 public_key_t *public;
790 enumerator_t *enumerator = NULL;
791 linked_list_t *list;
792
793 if (data->type == CERT_X509_OCSP_RESPONSE)
794 {
795 list = authority->ocsp_uris;
796 }
797 else
798 {
799 list = authority->crl_uris;
800 }
801
802 public = authority->cert->get_public_key(authority->cert);
803 if (public)
804 {
805 if (!data->id)
806 {
807 enumerator = list->create_enumerator(list);
808 }
809 else
810 {
811 if (public->has_fingerprint(public,
812 data->id->get_encoding(data->id)))
813 {
814 enumerator = list->create_enumerator(list);
815 }
816 }
817 public->destroy(public);
818 }
819 return enumerator;
820 }
821
822 CALLBACK(create_inner_cdp_hashandurl, enumerator_t*,
823 authority_t *authority, cert_data_t *data)
824 {
825 enumerator_t *enumerator = NULL;
826
827 if (!data->id || !authority->cert_uri_base)
828 {
829 return NULL;
830 }
831
832 if (authority->cert->has_subject(authority->cert,
833 data->id) != ID_MATCH_NONE)
834 {
835 enumerator = enumerator_create_single(strdup(authority->cert_uri_base),
836 free);
837 }
838 return enumerator;
839 }
840
841 METHOD(credential_set_t, create_cdp_enumerator, enumerator_t*,
842 private_vici_authority_t *this, certificate_type_t type,
843 identification_t *id)
844 {
845 cert_data_t *data;
846
847 switch (type)
848 { /* we serve CRLs, OCSP responders and URLs for "Hash and URL" */
849 case CERT_X509:
850 case CERT_X509_CRL:
851 case CERT_X509_OCSP_RESPONSE:
852 case CERT_ANY:
853 break;
854 default:
855 return NULL;
856 }
857
858 INIT(data,
859 .this = this,
860 .type = type,
861 .id = id,
862 );
863
864 this->lock->read_lock(this->lock);
865 return enumerator_create_nested(
866 this->authorities->create_enumerator(this->authorities),
867 (type == CERT_X509) ? (void*)create_inner_cdp_hashandurl :
868 (void*)create_inner_cdp, data, cert_data_destroy);
869 }
870
871 METHOD(vici_authority_t, add_ca_cert, certificate_t*,
872 private_vici_authority_t *this, certificate_t *cert)
873 {
874 this->lock->write_lock(this->lock);
875 cert = add_cert_internal(this, cert, TRUE);
876 this->lock->unlock(this->lock);
877 return cert;
878 }
879
880 METHOD(vici_authority_t, clear_ca_certs, void,
881 private_vici_authority_t *this)
882 {
883 this->lock->write_lock(this->lock);
884 this->certs->remove(this->certs, NULL, remove_external_certs);
885 this->lock->unlock(this->lock);
886 }
887
888 METHOD(vici_authority_t, destroy, void,
889 private_vici_authority_t *this)
890 {
891 manage_commands(this, FALSE);
892
893 this->authorities->destroy_function(this->authorities,
894 (void*)authority_destroy);
895 this->certs->destroy_function(this->certs, ca_cert_destroy);
896 this->lock->destroy(this->lock);
897 free(this);
898 }
899
900 /**
901 * See header
902 */
903 vici_authority_t *vici_authority_create(vici_dispatcher_t *dispatcher)
904 {
905 private_vici_authority_t *this;
906
907 INIT(this,
908 .public = {
909 .set = {
910 .create_private_enumerator = (void*)return_null,
911 .create_cert_enumerator = _create_cert_enumerator,
912 .create_shared_enumerator = (void*)return_null,
913 .create_cdp_enumerator = _create_cdp_enumerator,
914 .cache_cert = (void*)nop,
915 },
916 .add_ca_cert = _add_ca_cert,
917 .clear_ca_certs = _clear_ca_certs,
918 .destroy = _destroy,
919 },
920 .dispatcher = dispatcher,
921 .authorities = linked_list_create(),
922 .certs = linked_list_create(),
923 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
924 );
925
926 manage_commands(this, TRUE);
927
928 return &this->public;
929 }