revocation: Don't merge auth config of CLR/OCSP trustchain validation
[strongswan.git] / src / libstrongswan / plugins / revocation / revocation_validator.c
1 /*
2 * Copyright (C) 2010 Martin Willi
3 * Copyright (C) 2010 revosec AG
4 * Copyright (C) 2009 Andreas Steffen
5 * Hochschule fuer Technik Rapperswil
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
16 */
17
18 #include "revocation_validator.h"
19
20 #include <utils/debug.h>
21 #include <credentials/certificates/x509.h>
22 #include <credentials/certificates/crl.h>
23 #include <credentials/certificates/ocsp_request.h>
24 #include <credentials/certificates/ocsp_response.h>
25 #include <credentials/sets/ocsp_response_wrapper.h>
26 #include <selectors/traffic_selector.h>
27
28 typedef struct private_revocation_validator_t private_revocation_validator_t;
29
30 /**
31 * Private data of an revocation_validator_t object.
32 */
33 struct private_revocation_validator_t {
34
35 /**
36 * Public revocation_validator_t interface.
37 */
38 revocation_validator_t public;
39 };
40
41 /**
42 * Do an OCSP request
43 */
44 static certificate_t *fetch_ocsp(char *url, certificate_t *subject,
45 certificate_t *issuer)
46 {
47 certificate_t *request, *response;
48 chunk_t send, receive;
49
50 /* TODO: requestor name, signature */
51 request = lib->creds->create(lib->creds,
52 CRED_CERTIFICATE, CERT_X509_OCSP_REQUEST,
53 BUILD_CA_CERT, issuer,
54 BUILD_CERT, subject, BUILD_END);
55 if (!request)
56 {
57 DBG1(DBG_CFG, "generating ocsp request failed");
58 return NULL;
59 }
60
61 if (!request->get_encoding(request, CERT_ASN1_DER, &send))
62 {
63 DBG1(DBG_CFG, "encoding ocsp request failed");
64 request->destroy(request);
65 return NULL;
66 }
67 request->destroy(request);
68
69 DBG1(DBG_CFG, " requesting ocsp status from '%s' ...", url);
70 if (lib->fetcher->fetch(lib->fetcher, url, &receive,
71 FETCH_REQUEST_DATA, send,
72 FETCH_REQUEST_TYPE, "application/ocsp-request",
73 FETCH_END) != SUCCESS)
74 {
75 DBG1(DBG_CFG, "ocsp request to %s failed", url);
76 chunk_free(&send);
77 return NULL;
78 }
79 chunk_free(&send);
80
81 response = lib->creds->create(lib->creds,
82 CRED_CERTIFICATE, CERT_X509_OCSP_RESPONSE,
83 BUILD_BLOB_ASN1_DER, receive, BUILD_END);
84 chunk_free(&receive);
85 if (!response)
86 {
87 DBG1(DBG_CFG, "parsing ocsp response failed");
88 return NULL;
89 }
90 return response;
91 }
92
93 /**
94 * check the signature of an OCSP response
95 */
96 static bool verify_ocsp(ocsp_response_t *response)
97 {
98 certificate_t *issuer, *subject;
99 identification_t *responder;
100 ocsp_response_wrapper_t *wrapper;
101 enumerator_t *enumerator;
102 bool verified = FALSE;
103
104 wrapper = ocsp_response_wrapper_create((ocsp_response_t*)response);
105 lib->credmgr->add_local_set(lib->credmgr, &wrapper->set, FALSE);
106
107 subject = &response->certificate;
108 responder = subject->get_issuer(subject);
109 enumerator = lib->credmgr->create_trusted_enumerator(lib->credmgr,
110 KEY_ANY, responder, FALSE);
111 while (enumerator->enumerate(enumerator, &issuer, NULL))
112 {
113 if (lib->credmgr->issued_by(lib->credmgr, subject, issuer, NULL))
114 {
115 DBG1(DBG_CFG, " ocsp response correctly signed by \"%Y\"",
116 issuer->get_subject(issuer));
117 verified = TRUE;
118 break;
119 }
120 }
121 enumerator->destroy(enumerator);
122
123 lib->credmgr->remove_local_set(lib->credmgr, &wrapper->set);
124 wrapper->destroy(wrapper);
125 return verified;
126 }
127
128 /**
129 * Get the better of two OCSP responses, and check for usable OCSP info
130 */
131 static certificate_t *get_better_ocsp(certificate_t *cand, certificate_t *best,
132 x509_t *subject, x509_t *issuer,
133 cert_validation_t *valid, bool cache)
134 {
135 ocsp_response_t *response;
136 time_t revocation, this_update, next_update, valid_until;
137 crl_reason_t reason;
138 bool revoked = FALSE;
139
140 response = (ocsp_response_t*)cand;
141
142 /* check ocsp signature */
143 if (!verify_ocsp(response))
144 {
145 DBG1(DBG_CFG, "ocsp response verification failed");
146 cand->destroy(cand);
147 return best;
148 }
149 /* check if response contains our certificate */
150 switch (response->get_status(response, subject, issuer, &revocation, &reason,
151 &this_update, &next_update))
152 {
153 case VALIDATION_REVOKED:
154 /* subject has been revoked by a valid OCSP response */
155 DBG1(DBG_CFG, "certificate was revoked on %T, reason: %N",
156 &revocation, TRUE, crl_reason_names, reason);
157 revoked = TRUE;
158 break;
159 case VALIDATION_GOOD:
160 /* results in either good or stale */
161 break;
162 default:
163 case VALIDATION_FAILED:
164 /* candidate unusable, does not contain our cert */
165 DBG1(DBG_CFG, " ocsp response contains no status on our certificate");
166 cand->destroy(cand);
167 return best;
168 }
169
170 /* select the better of the two responses */
171 if (best == NULL || certificate_is_newer(cand, best))
172 {
173 DESTROY_IF(best);
174 best = cand;
175 if (best->get_validity(best, NULL, NULL, &valid_until))
176 {
177 DBG1(DBG_CFG, " ocsp response is valid: until %T",
178 &valid_until, FALSE);
179 *valid = VALIDATION_GOOD;
180 if (cache)
181 { /* cache non-stale only, stale certs get refetched */
182 lib->credmgr->cache_cert(lib->credmgr, best);
183 }
184 }
185 else
186 {
187 DBG1(DBG_CFG, " ocsp response is stale: since %T",
188 &valid_until, FALSE);
189 *valid = VALIDATION_STALE;
190 }
191 }
192 else
193 {
194 *valid = VALIDATION_STALE;
195 cand->destroy(cand);
196 }
197 if (revoked)
198 { /* revoked always counts, even if stale */
199 *valid = VALIDATION_REVOKED;
200 }
201 return best;
202 }
203
204 /**
205 * validate a x509 certificate using OCSP
206 */
207 static cert_validation_t check_ocsp(x509_t *subject, x509_t *issuer,
208 auth_cfg_t *auth)
209 {
210 enumerator_t *enumerator;
211 cert_validation_t valid = VALIDATION_SKIPPED;
212 certificate_t *best = NULL, *current;
213 identification_t *keyid = NULL;
214 public_key_t *public;
215 chunk_t chunk;
216 char *uri = NULL;
217
218 /** lookup cache for valid OCSP responses */
219 enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr,
220 CERT_X509_OCSP_RESPONSE, KEY_ANY, NULL, FALSE);
221 while (enumerator->enumerate(enumerator, &current))
222 {
223 current->get_ref(current);
224 best = get_better_ocsp(current, best, subject, issuer, &valid, FALSE);
225 if (best && valid != VALIDATION_STALE)
226 {
227 DBG1(DBG_CFG, " using cached ocsp response");
228 break;
229 }
230 }
231 enumerator->destroy(enumerator);
232
233 /* derive the authorityKeyIdentifier from the issuer's public key */
234 current = &issuer->interface;
235 public = current->get_public_key(current);
236 if (public && public->get_fingerprint(public, KEYID_PUBKEY_SHA1, &chunk))
237 {
238 keyid = identification_create_from_encoding(ID_KEY_ID, chunk);
239 }
240 /** fetch from configured OCSP responder URLs */
241 if (keyid && valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
242 {
243 enumerator = lib->credmgr->create_cdp_enumerator(lib->credmgr,
244 CERT_X509_OCSP_RESPONSE, keyid);
245 while (enumerator->enumerate(enumerator, &uri))
246 {
247 current = fetch_ocsp(uri, &subject->interface, &issuer->interface);
248 if (current)
249 {
250 best = get_better_ocsp(current, best, subject, issuer,
251 &valid, TRUE);
252 if (best && valid != VALIDATION_STALE)
253 {
254 break;
255 }
256 }
257 }
258 enumerator->destroy(enumerator);
259 }
260 DESTROY_IF(public);
261 DESTROY_IF(keyid);
262
263 /* fallback to URL fetching from subject certificate's URIs */
264 if (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
265 {
266 enumerator = subject->create_ocsp_uri_enumerator(subject);
267 while (enumerator->enumerate(enumerator, &uri))
268 {
269 current = fetch_ocsp(uri, &subject->interface, &issuer->interface);
270 if (current)
271 {
272 best = get_better_ocsp(current, best, subject, issuer,
273 &valid, TRUE);
274 if (best && valid != VALIDATION_STALE)
275 {
276 break;
277 }
278 }
279 }
280 enumerator->destroy(enumerator);
281 }
282 /* an uri was found, but no result. switch validation state to failed */
283 if (valid == VALIDATION_SKIPPED && uri)
284 {
285 valid = VALIDATION_FAILED;
286 }
287 if (auth)
288 {
289 auth->add(auth, AUTH_RULE_OCSP_VALIDATION, valid);
290 if (valid == VALIDATION_GOOD)
291 { /* successful OCSP check fulfills also CRL constraint */
292 auth->add(auth, AUTH_RULE_CRL_VALIDATION, VALIDATION_GOOD);
293 }
294 }
295 DESTROY_IF(best);
296 return valid;
297 }
298
299 /**
300 * fetch a CRL from an URL
301 */
302 static certificate_t* fetch_crl(char *url)
303 {
304 certificate_t *crl;
305 chunk_t chunk;
306
307 DBG1(DBG_CFG, " fetching crl from '%s' ...", url);
308 if (lib->fetcher->fetch(lib->fetcher, url, &chunk, FETCH_END) != SUCCESS)
309 {
310 DBG1(DBG_CFG, "crl fetching failed");
311 return NULL;
312 }
313 crl = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509_CRL,
314 BUILD_BLOB_ASN1_DER, chunk, BUILD_END);
315 chunk_free(&chunk);
316 if (!crl)
317 {
318 DBG1(DBG_CFG, "crl fetched successfully but parsing failed");
319 return NULL;
320 }
321 return crl;
322 }
323
324 /**
325 * check the signature of an CRL
326 */
327 static bool verify_crl(certificate_t *crl)
328 {
329 certificate_t *issuer;
330 enumerator_t *enumerator;
331 bool verified = FALSE;
332
333 enumerator = lib->credmgr->create_trusted_enumerator(lib->credmgr,
334 KEY_ANY, crl->get_issuer(crl), FALSE);
335 while (enumerator->enumerate(enumerator, &issuer, NULL))
336 {
337 if (lib->credmgr->issued_by(lib->credmgr, crl, issuer, NULL))
338 {
339 DBG1(DBG_CFG, " crl correctly signed by \"%Y\"",
340 issuer->get_subject(issuer));
341 verified = TRUE;
342 break;
343 }
344 }
345 enumerator->destroy(enumerator);
346
347 return verified;
348 }
349
350 /**
351 * Get the better of two CRLs, and check for usable CRL info
352 */
353 static certificate_t *get_better_crl(certificate_t *cand, certificate_t *best,
354 x509_t *subject, cert_validation_t *valid,
355 bool cache, crl_t *base)
356 {
357 enumerator_t *enumerator;
358 time_t revocation, valid_until;
359 crl_reason_t reason;
360 chunk_t serial;
361 crl_t *crl = (crl_t*)cand;
362
363 if (base)
364 {
365 if (!crl->is_delta_crl(crl, &serial) ||
366 !chunk_equals(serial, base->get_serial(base)))
367 {
368 cand->destroy(cand);
369 return best;
370 }
371 }
372 else
373 {
374 if (crl->is_delta_crl(crl, NULL))
375 {
376 cand->destroy(cand);
377 return best;
378 }
379 }
380
381 /* check CRL signature */
382 if (!verify_crl(cand))
383 {
384 DBG1(DBG_CFG, "crl response verification failed");
385 cand->destroy(cand);
386 return best;
387 }
388
389 enumerator = crl->create_enumerator(crl);
390 while (enumerator->enumerate(enumerator, &serial, &revocation, &reason))
391 {
392 if (chunk_equals(serial, subject->get_serial(subject)))
393 {
394 DBG1(DBG_CFG, "certificate was revoked on %T, reason: %N",
395 &revocation, TRUE, crl_reason_names, reason);
396 if (reason != CRL_REASON_CERTIFICATE_HOLD)
397 {
398 *valid = VALIDATION_REVOKED;
399 }
400 else
401 {
402 /* if the cert is on hold, a newer CRL might not contain it */
403 *valid = VALIDATION_ON_HOLD;
404 }
405 enumerator->destroy(enumerator);
406 DESTROY_IF(best);
407 return cand;
408 }
409 }
410 enumerator->destroy(enumerator);
411
412 /* select the better of the two CRLs */
413 if (best == NULL || crl_is_newer(crl, (crl_t*)best))
414 {
415 DESTROY_IF(best);
416 best = cand;
417 if (best->get_validity(best, NULL, NULL, &valid_until))
418 {
419 DBG1(DBG_CFG, " crl is valid: until %T", &valid_until, FALSE);
420 *valid = VALIDATION_GOOD;
421 if (cache)
422 { /* we cache non-stale crls only, as a stale crls are refetched */
423 lib->credmgr->cache_cert(lib->credmgr, best);
424 }
425 }
426 else
427 {
428 DBG1(DBG_CFG, " crl is stale: since %T", &valid_until, FALSE);
429 *valid = VALIDATION_STALE;
430 }
431 }
432 else
433 {
434 *valid = VALIDATION_STALE;
435 cand->destroy(cand);
436 }
437 return best;
438 }
439
440 /**
441 * Find or fetch a certificate for a given crlIssuer
442 */
443 static cert_validation_t find_crl(x509_t *subject, identification_t *issuer,
444 crl_t *base, certificate_t **best,
445 bool *uri_found)
446 {
447 cert_validation_t valid = VALIDATION_SKIPPED;
448 enumerator_t *enumerator;
449 certificate_t *current;
450 char *uri;
451
452 /* find a cached (delta) crl */
453 enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr,
454 CERT_X509_CRL, KEY_ANY, issuer, FALSE);
455 while (enumerator->enumerate(enumerator, &current))
456 {
457 current->get_ref(current);
458 *best = get_better_crl(current, *best, subject, &valid, FALSE, base);
459 if (*best && valid != VALIDATION_STALE)
460 {
461 DBG1(DBG_CFG, " using cached crl");
462 break;
463 }
464 }
465 enumerator->destroy(enumerator);
466
467 /* fallback to fetching crls from credential sets cdps */
468 if (!base && valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
469 {
470 enumerator = lib->credmgr->create_cdp_enumerator(lib->credmgr,
471 CERT_X509_CRL, issuer);
472 while (enumerator->enumerate(enumerator, &uri))
473 {
474 *uri_found = TRUE;
475 current = fetch_crl(uri);
476 if (current)
477 {
478 if (!current->has_issuer(current, issuer))
479 {
480 DBG1(DBG_CFG, "issuer of fetched CRL '%Y' does not match CRL "
481 "issuer '%Y'", current->get_issuer(current), issuer);
482 current->destroy(current);
483 continue;
484 }
485 *best = get_better_crl(current, *best, subject,
486 &valid, TRUE, base);
487 if (*best && valid != VALIDATION_STALE)
488 {
489 break;
490 }
491 }
492 }
493 enumerator->destroy(enumerator);
494 }
495 return valid;
496 }
497
498 /**
499 * Look for a delta CRL for a given base CRL
500 */
501 static cert_validation_t check_delta_crl(x509_t *subject, x509_t *issuer,
502 crl_t *base, cert_validation_t base_valid)
503 {
504 cert_validation_t valid = VALIDATION_SKIPPED;
505 certificate_t *best = NULL, *current;
506 enumerator_t *enumerator;
507 identification_t *id;
508 x509_cdp_t *cdp;
509 chunk_t chunk;
510 bool uri;
511
512 /* find cached delta CRL via subjectKeyIdentifier */
513 chunk = issuer->get_subjectKeyIdentifier(issuer);
514 if (chunk.len)
515 {
516 id = identification_create_from_encoding(ID_KEY_ID, chunk);
517 valid = find_crl(subject, id, base, &best, &uri);
518 id->destroy(id);
519 }
520
521 /* find delta CRL by CRLIssuer */
522 enumerator = subject->create_crl_uri_enumerator(subject);
523 while (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED &&
524 enumerator->enumerate(enumerator, &cdp))
525 {
526 if (cdp->issuer)
527 {
528 valid = find_crl(subject, cdp->issuer, base, &best, &uri);
529 }
530 }
531 enumerator->destroy(enumerator);
532
533 /* fetch from URIs found in Freshest CRL extension */
534 enumerator = base->create_delta_crl_uri_enumerator(base);
535 while (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED &&
536 enumerator->enumerate(enumerator, &cdp))
537 {
538 current = fetch_crl(cdp->uri);
539 if (current)
540 {
541 if (cdp->issuer && !current->has_issuer(current, cdp->issuer))
542 {
543 DBG1(DBG_CFG, "issuer of fetched delta CRL '%Y' does not match "
544 "certificates CRL issuer '%Y'",
545 current->get_issuer(current), cdp->issuer);
546 current->destroy(current);
547 continue;
548 }
549 best = get_better_crl(current, best, subject, &valid, TRUE, base);
550 if (best && valid != VALIDATION_STALE)
551 {
552 break;
553 }
554 }
555 }
556 enumerator->destroy(enumerator);
557
558 if (best)
559 {
560 best->destroy(best);
561 return valid;
562 }
563 return base_valid;
564 }
565
566 /**
567 * validate a x509 certificate using CRL
568 */
569 static cert_validation_t check_crl(x509_t *subject, x509_t *issuer,
570 auth_cfg_t *auth)
571 {
572 cert_validation_t valid = VALIDATION_SKIPPED;
573 certificate_t *best = NULL;
574 identification_t *id;
575 x509_cdp_t *cdp;
576 bool uri_found = FALSE;
577 certificate_t *current;
578 enumerator_t *enumerator;
579 chunk_t chunk;
580
581 /* use issuers subjectKeyIdentifier to find a cached CRL / fetch from CDP */
582 chunk = issuer->get_subjectKeyIdentifier(issuer);
583 if (chunk.len)
584 {
585 id = identification_create_from_encoding(ID_KEY_ID, chunk);
586 valid = find_crl(subject, id, NULL, &best, &uri_found);
587 id->destroy(id);
588 }
589
590 /* find a cached CRL or fetch via configured CDP via CRLIssuer */
591 enumerator = subject->create_crl_uri_enumerator(subject);
592 while (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED &&
593 enumerator->enumerate(enumerator, &cdp))
594 {
595 if (cdp->issuer)
596 {
597 valid = find_crl(subject, cdp->issuer, NULL, &best, &uri_found);
598 }
599 }
600 enumerator->destroy(enumerator);
601
602 /* fallback to fetching CRLs from CDPs found in subjects certificate */
603 if (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
604 {
605 enumerator = subject->create_crl_uri_enumerator(subject);
606 while (enumerator->enumerate(enumerator, &cdp))
607 {
608 uri_found = TRUE;
609 current = fetch_crl(cdp->uri);
610 if (current)
611 {
612 if (cdp->issuer && !current->has_issuer(current, cdp->issuer))
613 {
614 DBG1(DBG_CFG, "issuer of fetched CRL '%Y' does not match "
615 "certificates CRL issuer '%Y'",
616 current->get_issuer(current), cdp->issuer);
617 current->destroy(current);
618 continue;
619 }
620 best = get_better_crl(current, best, subject, &valid,
621 TRUE, NULL);
622 if (best && valid != VALIDATION_STALE)
623 {
624 break;
625 }
626 }
627 }
628 enumerator->destroy(enumerator);
629 }
630
631 /* look for delta CRLs */
632 if (best && (valid == VALIDATION_GOOD || valid == VALIDATION_STALE))
633 {
634 valid = check_delta_crl(subject, issuer, (crl_t*)best, valid);
635 }
636
637 /* an uri was found, but no result. switch validation state to failed */
638 if (valid == VALIDATION_SKIPPED && uri_found)
639 {
640 valid = VALIDATION_FAILED;
641 }
642 if (auth)
643 {
644 if (valid == VALIDATION_SKIPPED)
645 { /* if we skipped CRL validation, we use the result of OCSP for
646 * constraint checking */
647 auth->add(auth, AUTH_RULE_CRL_VALIDATION,
648 auth->get(auth, AUTH_RULE_OCSP_VALIDATION));
649 }
650 else
651 {
652 auth->add(auth, AUTH_RULE_CRL_VALIDATION, valid);
653 }
654 }
655 DESTROY_IF(best);
656 return valid;
657 }
658
659 METHOD(cert_validator_t, validate, bool,
660 private_revocation_validator_t *this, certificate_t *subject,
661 certificate_t *issuer, bool online, u_int pathlen, bool anchor,
662 auth_cfg_t *auth)
663 {
664 if (subject->get_type(subject) == CERT_X509 &&
665 issuer->get_type(issuer) == CERT_X509 &&
666 online)
667 {
668 DBG1(DBG_CFG, "checking certificate status of \"%Y\"",
669 subject->get_subject(subject));
670 switch (check_ocsp((x509_t*)subject, (x509_t*)issuer,
671 pathlen ? NULL : auth))
672 {
673 case VALIDATION_GOOD:
674 DBG1(DBG_CFG, "certificate status is good");
675 return TRUE;
676 case VALIDATION_REVOKED:
677 case VALIDATION_ON_HOLD:
678 /* has already been logged */
679 lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_REVOKED,
680 subject);
681 return FALSE;
682 case VALIDATION_SKIPPED:
683 DBG2(DBG_CFG, "ocsp check skipped, no ocsp found");
684 break;
685 case VALIDATION_STALE:
686 DBG1(DBG_CFG, "ocsp information stale, fallback to crl");
687 break;
688 case VALIDATION_FAILED:
689 DBG1(DBG_CFG, "ocsp check failed, fallback to crl");
690 break;
691 }
692 switch (check_crl((x509_t*)subject, (x509_t*)issuer,
693 pathlen ? NULL : auth))
694 {
695 case VALIDATION_GOOD:
696 DBG1(DBG_CFG, "certificate status is good");
697 return TRUE;
698 case VALIDATION_REVOKED:
699 case VALIDATION_ON_HOLD:
700 /* has already been logged */
701 lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_REVOKED,
702 subject);
703 return FALSE;
704 case VALIDATION_FAILED:
705 case VALIDATION_SKIPPED:
706 DBG1(DBG_CFG, "certificate status is not available");
707 break;
708 case VALIDATION_STALE:
709 DBG1(DBG_CFG, "certificate status is unknown, crl is stale");
710 break;
711 }
712 lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_VALIDATION_FAILED,
713 subject);
714 }
715 return TRUE;
716 }
717
718 METHOD(revocation_validator_t, destroy, void,
719 private_revocation_validator_t *this)
720 {
721 free(this);
722 }
723
724 /**
725 * See header
726 */
727 revocation_validator_t *revocation_validator_create()
728 {
729 private_revocation_validator_t *this;
730
731 INIT(this,
732 .public = {
733 .validator.validate = _validate,
734 .destroy = _destroy,
735 },
736 );
737
738 return &this->public;
739 }