ignoring unkown crl/ocsp uris
[strongswan.git] / src / libstrongswan / crypto / ca.c
1 /**
2 * @file ca.c
3 *
4 * @brief Implementation of ca_info_t.
5 *
6 */
7
8 /*
9 * Copyright (C) 2007 Andreas Steffen
10 * Hochschule fuer Technik Rapperswil
11 *
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * for more details.
21 */
22
23 #include <sys/stat.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <pthread.h>
28
29 #include "x509.h"
30 #include "crl.h"
31 #include "ca.h"
32 #include "ac.h"
33 #include "certinfo.h"
34 #include "ocsp.h"
35
36 #include <library.h>
37 #include <debug.h>
38 #include <utils/linked_list.h>
39 #include <utils/identification.h>
40 #include <utils/fetcher.h>
41
42 typedef struct private_ca_info_t private_ca_info_t;
43
44 /**
45 * Private data of a ca_info_t object.
46 */
47 struct private_ca_info_t {
48 /**
49 * Public interface for this ca info record
50 */
51 ca_info_t public;
52
53 /**
54 * Name of the ca info record
55 */
56 char *name;
57
58 /**
59 * Time when ca info record was installed
60 */
61 time_t installed;
62
63 /**
64 * Distinguished Name of the CA
65 */
66 x509_t *cacert;
67
68 /**
69 * List of attribute certificates
70 */
71 linked_list_t *attrcerts;
72
73 /**
74 * List of crl URIs
75 */
76 linked_list_t *crluris;
77
78 /**
79 * List of ocsp URIs
80 */
81 linked_list_t *ocspuris;
82
83 /**
84 * CRL issued by this ca
85 */
86 crl_t *crl;
87
88 /**
89 * List of certificate info records
90 */
91 linked_list_t *certinfos;
92
93 /**
94 * mutex controls access to the elements:
95 * name, crluris, ocspuris, crl, and certinfos
96 */
97 pthread_mutex_t mutex;
98 };
99
100 /**
101 * static options set by ca_info_set_options()
102 */
103 static strict_t strict_crl_policy = STRICT_NO;
104 static bool cache_crls = FALSE;
105 static u_int crl_check_interval = 0;
106
107 /**
108 * Implements ca_info_t.equals
109 */
110 static bool equals(const private_ca_info_t *this, const private_ca_info_t *that)
111 {
112 return chunk_equals(this->cacert->get_keyid(this->cacert),
113 that->cacert->get_keyid(that->cacert));
114 }
115
116 /**
117 * Implements ca_info_t.equals_name_release_info
118 */
119 static bool equals_name_release_info(private_ca_info_t *this, const char *name)
120 {
121 bool found;
122
123 pthread_mutex_lock(&(this->mutex));
124 found = this->name != NULL && streq(this->name, name);
125
126 if (found)
127 {
128 this->crluris->destroy_offset(this->crluris,
129 offsetof(identification_t, destroy));
130 this->crluris = linked_list_create();
131
132 this->ocspuris->destroy_offset(this->ocspuris,
133 offsetof(identification_t, destroy));
134 this->ocspuris = linked_list_create();
135
136 free(this->name);
137 this->name = NULL;
138 }
139
140 pthread_mutex_unlock(&(this->mutex));
141 return found;
142 }
143
144 /**
145 * Implements ca_info_t.is_crl_issuer
146 */
147 static bool is_cert_issuer(private_ca_info_t *this, const x509_t *cert)
148 {
149 return cert->is_issuer(cert, this->cacert);
150 }
151
152 /**
153 * Implements ca_info_t.is_crl_issuer
154 */
155 static bool is_crl_issuer(private_ca_info_t *this, const crl_t *crl)
156 {
157 return crl->is_issuer(crl, this->cacert);
158 }
159
160 /**
161 * Implements ca_info_t.is_ca
162 */
163 static bool is_ca(private_ca_info_t *this)
164 {
165 return this->cacert->is_ca(this->cacert);
166 }
167
168 /**
169 * Implements ca_info_t.is_strict
170 */
171 static bool is_strict(private_ca_info_t *this)
172 {
173 bool strict = strict_crl_policy != STRICT_NO;
174
175 if (strict_crl_policy == STRICT_IFURI)
176 {
177 pthread_mutex_lock(&(this->mutex));
178 strict = this->crluris->get_count(this->crluris) > 0 ||
179 this->ocspuris->get_count(this->ocspuris) > 0;
180 pthread_mutex_unlock(&(this->mutex));
181 }
182 return strict;
183 }
184
185 /**
186 * Implements ca_info_t.has_crl
187 */
188 static bool has_crl(private_ca_info_t *this)
189 {
190 bool found;
191
192 pthread_mutex_lock(&(this->mutex));
193 found = this->crl != NULL;
194 pthread_mutex_unlock(&(this->mutex));
195
196 return found;
197 }
198
199 /**
200 * Implements ca_info_t.has_certinfos
201 */
202 static bool has_certinfos(private_ca_info_t *this)
203 {
204 bool found;
205
206 pthread_mutex_lock(&(this->mutex));
207 found = this->certinfos->get_count(this->certinfos) > 0;
208 pthread_mutex_unlock(&(this->mutex));
209
210 return found;
211 }
212
213 /**
214 * Implements ca_info_t.add_crl
215 */
216 static void add_crl(private_ca_info_t *this, crl_t *crl)
217 {
218 pthread_mutex_lock(&(this->mutex));
219
220 if (this->crl)
221 {
222 if (crl->is_newer(crl, this->crl))
223 {
224 this->crl->destroy(this->crl);
225 this->crl = crl;
226 DBG1(" this crl is newer - existing crl replaced");
227 }
228 else
229 {
230 crl->destroy(crl);
231 DBG1(" this crl is not newer - existing crl retained");
232 }
233 }
234 else
235 {
236 this->crl = crl;
237 DBG2(" crl added");
238 }
239
240 pthread_mutex_unlock(&(this->mutex));
241 }
242
243 /**
244 * Implements ca_info_t.list_crl
245 */
246 static void list_crl(private_ca_info_t *this, FILE *out, bool utc)
247 {
248 pthread_mutex_lock(&this->mutex);
249 this->crl->list(this->crl, out, utc);
250 pthread_mutex_unlock(&this->mutex);
251 }
252
253 /**
254 * Implements ca_info_t.list_certinfos
255 */
256 static void list_certinfos(private_ca_info_t *this, FILE *out, bool utc)
257 {
258 iterator_t *iterator;
259 certinfo_t *certinfo;
260 chunk_t authkey;
261
262 pthread_mutex_lock(&this->mutex);
263
264 authkey = this->cacert->get_subjectKeyID(this->cacert);
265 fprintf(out," authname: '%D'\n", this->cacert->get_subject(this->cacert));
266 fprintf(out," authkey: %#B\n", &authkey);
267
268 iterator = this->certinfos->create_iterator(this->certinfos, TRUE);
269 while (iterator->iterate(iterator, (void**)&certinfo))
270 {
271 time_t nextUpdate, thisUpdate, now;
272 chunk_t serial;
273
274 now = time(NULL);
275 nextUpdate = certinfo->get_nextUpdate(certinfo);
276 thisUpdate = certinfo->get_thisUpdate(certinfo);
277 serial = certinfo->get_serialNumber(certinfo);
278
279 fprintf(out, "%#T, until %#T, ", &thisUpdate, utc, &nextUpdate, utc);
280 if (now > nextUpdate)
281 {
282 fprintf(out, "expired (%V ago)\n", &now, &nextUpdate);
283 }
284 else
285 {
286 fprintf(out, "ok (expires in %V)\n", &now, &nextUpdate);
287 }
288 fprintf(out, " serial: %#B, %N\n", &serial,
289 cert_status_names, certinfo->get_status(certinfo));
290 }
291 iterator->destroy(iterator);
292
293 pthread_mutex_unlock(&this->mutex);
294 }
295
296 /**
297 * Find an exact copy of an identification in a linked list
298 */
299 static identification_t* find_identification(linked_list_t *list, identification_t *id)
300 {
301 identification_t *found_id = NULL, *current_id;
302
303 iterator_t *iterator = list->create_iterator(list, TRUE);
304
305 while (iterator->iterate(iterator, (void**)&current_id))
306 {
307 if (id->equals(id, current_id))
308 {
309 found_id = current_id;
310 break;
311 }
312 }
313 iterator->destroy(iterator);
314
315 return found_id;
316 }
317
318 /**
319 * Add a unique identification to a linked list
320 */
321 static identification_t *add_identification(linked_list_t *list, identification_t *id)
322 {
323 identification_t *found_id = find_identification(list, id);
324
325 if (found_id)
326 {
327 id->destroy(id);
328 return found_id;
329 }
330 else
331 {
332 list->insert_last(list, (void*)id);
333 return id;
334 }
335 }
336
337 /**
338 * Implements ca_info_t.add_crluri
339 */
340 static void add_crluri(private_ca_info_t *this, chunk_t uri)
341 {
342 if (uri.len < 6 ||
343 (strncasecmp(uri.ptr, "http", 4) != 0 &&
344 strncasecmp(uri.ptr, "ldap", 4) != 0 &&
345 strncasecmp(uri.ptr, "file", 4) != 0 &&
346 strncasecmp(uri.ptr, "ftp", 3) != 0))
347 {
348 DBG1(" invalid crl uri '%#B'", &uri);
349 return;
350 }
351 else
352 {
353 identification_t *crluri = identification_create_from_encoding(ID_DER_ASN1_GN_URI, uri);
354
355 pthread_mutex_lock(&(this->mutex));
356 add_identification(this->crluris, crluri);
357 pthread_mutex_unlock(&(this->mutex));
358 }
359 }
360
361 /**
362 * Implements ca_info_t.add_ocspuri
363 */
364 static void add_ocspuri(private_ca_info_t *this, chunk_t uri)
365 {
366 if (uri.len < 7 || strncasecmp(uri.ptr, "http", 4) != 0)
367 {
368 DBG1(" invalid ocsp uri '%.*s'", uri.len, uri.ptr);
369 return;
370 }
371 else
372 {
373 identification_t *ocspuri = identification_create_from_encoding(ID_DER_ASN1_GN_URI, uri);
374
375 pthread_mutex_lock(&(this->mutex));
376 add_identification(this->ocspuris, ocspuri);
377 pthread_mutex_unlock(&(this->mutex));
378 }
379 }
380
381 /**
382 * Implements ca_info_t.add_info.
383 */
384 void add_info (private_ca_info_t *this, const private_ca_info_t *that)
385 {
386 pthread_mutex_lock(&(this->mutex));
387
388 if (this->name == NULL && that->name != NULL)
389 {
390 this->name = strdup(that->name);
391 }
392
393 pthread_mutex_unlock(&(this->mutex));
394
395 {
396 identification_t *uri;
397
398 iterator_t *iterator = that->crluris->create_iterator(that->crluris, TRUE);
399
400 while (iterator->iterate(iterator, (void**)&uri))
401 {
402 if (uri->get_type(uri) == ID_DER_ASN1_GN_URI)
403 {
404 add_crluri(this, uri->get_encoding(uri));
405 }
406 }
407 iterator->destroy(iterator);
408 }
409
410 {
411 identification_t *uri;
412
413 iterator_t *iterator = that->ocspuris->create_iterator(that->ocspuris, TRUE);
414
415 while (iterator->iterate(iterator, (void**)&uri))
416 {
417 if (uri->get_type(uri) == ID_DER_ASN1_GN_URI)
418 {
419 add_ocspuri(this, uri->get_encoding(uri));
420 }
421 }
422 iterator->destroy(iterator);
423 }
424 }
425
426 /**
427 * Implements ca_info_t.get_certificate.
428 */
429 static x509_t* get_certificate(private_ca_info_t* this)
430 {
431 return this->cacert;
432 }
433
434 /**
435 * caches a crl by saving it to a given crl directory
436 */
437 void cache_crl(private_ca_info_t* this, const char *crl_dir, crl_t *crl)
438 {
439 char buffer[BUF_LEN];
440 char *path;
441 char *pos = buffer;
442 int len = BUF_LEN;
443 int n;
444
445 chunk_t authKeyID = this->cacert->get_subjectKeyID(this->cacert);
446 chunk_t uri;
447
448 uri.ptr = buffer;
449 uri.len = 7 + strlen(crl_dir) + 1 + 2*authKeyID.len + 4;
450
451 if (uri.len >= BUF_LEN)
452 {
453 DBG1("file uri exceeds buffer length of %d bytes - crl not saved", BUF_LEN);
454 return;
455 }
456
457 /* print the file uri prefix */
458 n = snprintf(pos, len, "file://");
459 pos += n; len -= n;
460
461 /* remember the start of the path string */
462 path = pos;
463
464 /* print the default crl directory path */
465 n = snprintf(pos, len, "%s/", crl_dir);
466 pos += n; len -= n;
467
468 /* create and print a unique crl filename derived from the authKeyID */
469 while (authKeyID.len-- > 0)
470 {
471 n = snprintf(pos, len, "%02x", *authKeyID.ptr++);
472 pos += n; len -= n;
473 }
474
475 /* add the file suffix */
476 n = snprintf(pos, len, ".crl");
477
478 if (crl->write_to_file(crl, path, 0022, TRUE))
479 {
480 identification_t *crluri = identification_create_from_encoding(ID_DER_ASN1_GN_URI, uri);
481
482 add_identification(this->crluris, crluri);
483 }
484 }
485
486 /**
487 * Implements ca_info_t.verify_by_crl.
488 */
489 static cert_status_t verify_by_crl(private_ca_info_t* this, certinfo_t *certinfo,
490 const char *crl_dir)
491 {
492 rsa_public_key_t *issuer_public_key = this->cacert->get_public_key(this->cacert);
493 bool stale;
494
495 pthread_mutex_lock(&(this->mutex));
496 if (this->crl == NULL)
497 {
498 stale = TRUE;
499 DBG1("no crl is locally available");
500 }
501 else
502 {
503 stale = !this->crl->is_valid(this->crl);
504 DBG1("crl is %s", stale? "stale":"valid");
505 }
506
507 if (stale && crl_check_interval > 0)
508 {
509 iterator_t *iterator = this->crluris->create_iterator(this->crluris, TRUE);
510 identification_t *uri;
511
512 while (iterator->iterate(iterator, (void**)&uri))
513 {
514 fetcher_t *fetcher;
515 char uri_string[BUF_LEN];
516 chunk_t uri_chunk = uri->get_encoding(uri);
517 chunk_t response_chunk;
518
519 snprintf(uri_string, BUF_LEN, "%.*s", uri_chunk.len, uri_chunk.ptr);
520 fetcher = fetcher_create(uri_string);
521
522 response_chunk = fetcher->get(fetcher);
523 fetcher->destroy(fetcher);
524 if (response_chunk.ptr != NULL)
525 {
526 crl_t *crl = crl_create_from_chunk(response_chunk);
527
528 if (crl == NULL)
529 {
530 free(response_chunk.ptr);
531 continue;
532 }
533 if (!is_crl_issuer(this, crl))
534 {
535 DBG1(" fetched crl has wrong issuer");
536 crl->destroy(crl);
537 continue;
538 }
539 if (!crl->verify(crl, issuer_public_key))
540 {
541 DBG1("fetched crl signature is invalid");
542 crl->destroy(crl);
543 continue;
544 }
545 DBG2("fetched crl signature is valid");
546
547 if (this->crl == NULL)
548 {
549 this->crl = crl;
550 }
551 else if (crl->is_newer(crl, this->crl))
552 {
553 this->crl->destroy(this->crl);
554 this->crl = crl;
555 DBG1("this crl is newer - existing crl replaced");
556 }
557 else
558 {
559 crl->destroy(crl);
560 DBG1("this crl is not newer - existing crl retained");
561 continue;
562 }
563 if (crl->is_valid(crl))
564 {
565 if (cache_crls && strncasecmp(uri_string, "file", 4) != 0)
566 {
567 cache_crl(this, crl_dir, crl);
568 }
569 /* we found a valid crl and therefore exit the fetch loop */
570 break;
571 }
572 else
573 {
574 DBG1("fetched crl is stale");
575 }
576 }
577 }
578 iterator->destroy(iterator);
579 }
580
581 if (this->crl)
582 {
583 if (!this->crl->verify(this->crl, issuer_public_key))
584 {
585 DBG1("crl signature is invalid");
586 goto ret;
587 }
588 DBG2("crl signature is valid");
589
590 this->crl->get_status(this->crl, certinfo);
591 }
592
593 ret:
594 pthread_mutex_unlock(&(this->mutex));
595 return certinfo->get_status(certinfo);
596 }
597
598 /**
599 * Implements ca_info_t.verify_by_ocsp.
600 */
601 static cert_status_t verify_by_ocsp(private_ca_info_t* this,
602 certinfo_t *certinfo,
603 credential_store_t *credentials)
604 {
605 bool stale;
606 iterator_t *iterator;
607 certinfo_t *cached_certinfo = NULL;
608 int comparison = 1;
609
610 pthread_mutex_lock(&(this->mutex));
611
612 /* do we support OCSP at all? */
613 if (this->ocspuris->get_count(this->ocspuris) == 0)
614 {
615 goto ret;
616 }
617
618 iterator = this->certinfos->create_iterator(this->certinfos, TRUE);
619
620 /* find the list insertion point in alphabetical order */
621 while(iterator->iterate(iterator, (void**)&cached_certinfo))
622 {
623 comparison = certinfo->compare_serialNumber(certinfo, cached_certinfo);
624
625 if (comparison <= 0)
626 {
627 break;
628 }
629 }
630
631 /* do we have a valid certinfo_t for this serial number in our cache? */
632 if (comparison == 0)
633 {
634 stale = cached_certinfo->get_nextUpdate(cached_certinfo) < time(NULL);
635 DBG1("ocsp status in cache is %s", stale ? "stale":"fresh");
636 }
637 else
638 {
639 stale = TRUE;
640 DBG1("ocsp status is not in cache");
641 }
642
643 if (stale)
644 {
645 ocsp_t *ocsp;
646
647 ocsp = ocsp_create(this->cacert, this->ocspuris);
648 ocsp->fetch(ocsp, certinfo, credentials);
649 if (certinfo->get_status(certinfo) != CERT_UNDEFINED)
650 {
651 if (comparison != 0)
652 {
653 cached_certinfo = certinfo_create(certinfo->get_serialNumber(certinfo));
654
655 if (comparison > 0)
656 {
657 iterator->insert_after(iterator, (void *)cached_certinfo);
658 }
659 else
660 {
661 iterator->insert_before(iterator, (void *)cached_certinfo);
662 }
663 }
664 cached_certinfo->update(cached_certinfo, certinfo);
665 }
666 ocsp->destroy(ocsp);
667 }
668 else
669 {
670 certinfo->update(certinfo, cached_certinfo);
671 }
672
673 iterator->destroy(iterator);
674
675 ret:
676 pthread_mutex_unlock(&(this->mutex));
677 return certinfo->get_status(certinfo);
678 }
679
680 /**
681 * Implements ca_info_t.purge_ocsp
682 */
683 static void purge_ocsp(private_ca_info_t *this)
684 {
685 pthread_mutex_lock(&(this->mutex));
686
687 this->certinfos->destroy_offset(this->certinfos,
688 offsetof(certinfo_t, destroy));
689 this->certinfos = linked_list_create();
690
691 pthread_mutex_unlock(&(this->mutex));
692 }
693
694 /**
695 * Implements ca_info_t.destroy
696 */
697 static void destroy(private_ca_info_t *this)
698 {
699 this->attrcerts->destroy_offset(this->attrcerts,
700 offsetof(x509ac_t, destroy));
701 this->crluris->destroy_offset(this->crluris,
702 offsetof(identification_t, destroy));
703 this->ocspuris->destroy_offset(this->ocspuris,
704 offsetof(identification_t, destroy));
705 this->certinfos->destroy_offset(this->certinfos,
706 offsetof(certinfo_t, destroy));
707 DESTROY_IF(this->crl);
708 free(this->name);
709 free(this);
710 }
711
712 /**
713 * list the info of this CA
714 */
715 static void list(private_ca_info_t* this, FILE* out, bool utc)
716 {
717 chunk_t chunk;
718 identification_t *uri;
719 iterator_t *iterator;
720 bool first;
721
722 pthread_mutex_lock(&(this->mutex));
723 fprintf(out, "%#T", &this->installed, utc);
724
725 if (this->name)
726 {
727 fprintf(out, ", \"%s\"\n", this->name);
728 }
729 else
730 {
731 fprintf(out, "\n");
732 }
733
734 fprintf(out, " authname: '%D'\n", this->cacert->get_subject(this->cacert));
735 chunk = this->cacert->get_subjectKeyID(this->cacert);
736 fprintf(out, " authkey: %#B\n", &chunk);
737 chunk = this->cacert->get_keyid(this->cacert);
738 fprintf(out, " keyid: %#B\n", &chunk);
739
740 first = TRUE;
741 iterator = this->crluris->create_iterator(this->crluris, TRUE);
742 while (iterator->iterate(iterator, (void**)&uri))
743 {
744 fprintf(out, " %s '%D'\n", first ? "crluris:":" ", uri);
745 first = FALSE;
746 }
747 iterator->destroy(iterator);
748
749 first = TRUE;
750 iterator = this->ocspuris->create_iterator(this->ocspuris, TRUE);
751 while (iterator->iterate(iterator, (void**)&uri))
752 {
753 fprintf(out, " %s '%D'\n", first ? "ocspuris:":" ", uri);
754 first = FALSE;
755 }
756 iterator->destroy(iterator);
757 pthread_mutex_unlock(&(this->mutex));
758 }
759
760 /*
761 * Described in header.
762 */
763 void ca_info_set_options(strict_t strict, bool cache, u_int interval)
764 {
765 strict_crl_policy = strict;
766 cache_crls = cache;
767 crl_check_interval = interval;
768 }
769
770 /*
771 * Described in header.
772 */
773 ca_info_t *ca_info_create(const char *name, x509_t *cacert)
774 {
775 private_ca_info_t *this = malloc_thing(private_ca_info_t);
776
777 /* initialize */
778 this->installed = time(NULL);
779 this->name = (name == NULL)? NULL:strdup(name);
780 this->cacert = cacert;
781 this->attrcerts = linked_list_create();
782 this->crluris = linked_list_create();
783 this->ocspuris = linked_list_create();
784 this->certinfos = linked_list_create();
785 this->crl = NULL;
786
787 /* initialize the mutex */
788 pthread_mutex_init(&(this->mutex), NULL);
789
790 /* public functions */
791 this->public.equals = (bool (*) (const ca_info_t*,const ca_info_t*))equals;
792 this->public.equals_name_release_info = (bool (*) (ca_info_t*,const char*))equals_name_release_info;
793 this->public.is_cert_issuer = (bool (*) (ca_info_t*,const x509_t*))is_cert_issuer;
794 this->public.is_crl_issuer = (bool (*) (ca_info_t*,const crl_t*))is_crl_issuer;
795 this->public.is_ca = (bool (*) (ca_info_t*))is_ca;
796 this->public.is_strict = (bool (*) (ca_info_t*))is_strict;
797 this->public.add_info = (void (*) (ca_info_t*,const ca_info_t*))add_info;
798 this->public.add_crl = (void (*) (ca_info_t*,crl_t*))add_crl;
799 this->public.has_crl = (bool (*) (ca_info_t*))has_crl;
800 this->public.has_certinfos = (bool (*) (ca_info_t*))has_certinfos;
801 this->public.list = (void (*) (ca_info_t*,FILE*,bool))list;
802 this->public.list_crl = (void (*) (ca_info_t*,FILE*,bool))list_crl;
803 this->public.list_certinfos = (void (*) (ca_info_t*,FILE*,bool))list_certinfos;
804 this->public.add_crluri = (void (*) (ca_info_t*,chunk_t))add_crluri;
805 this->public.add_ocspuri = (void (*) (ca_info_t*,chunk_t))add_ocspuri;
806 this->public.get_certificate = (x509_t* (*) (ca_info_t*))get_certificate;
807 this->public.verify_by_crl = (cert_status_t (*) (ca_info_t*,certinfo_t*, const char*))verify_by_crl;
808 this->public.verify_by_ocsp = (cert_status_t (*) (ca_info_t*,certinfo_t*,credential_store_t*))verify_by_ocsp;
809 this->public.purge_ocsp = (void (*) (ca_info_t*))purge_ocsp;
810 this->public.destroy = (void (*) (ca_info_t*))destroy;
811
812 return &this->public;
813 }