2 * Copyright (C) 2010 Martin Willi
3 * Copyright (C) 2010 revosec AG
4 * Copyright (C) 2009 Andreas Steffen
5 * Hochschule fuer Technik Rapperswil
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>.
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
18 #include "revocation_validator.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>
28 typedef struct private_revocation_validator_t private_revocation_validator_t
;
31 * Private data of an revocation_validator_t object.
33 struct private_revocation_validator_t
{
36 * Public revocation_validator_t interface.
38 revocation_validator_t
public;
44 static certificate_t
*fetch_ocsp(char *url
, certificate_t
*subject
,
45 certificate_t
*issuer
)
47 certificate_t
*request
, *response
;
48 chunk_t send
, receive
;
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
);
57 DBG1(DBG_CFG
, "generating ocsp request failed");
61 if (!request
->get_encoding(request
, CERT_ASN1_DER
, &send
))
63 DBG1(DBG_CFG
, "encoding ocsp request failed");
64 request
->destroy(request
);
67 request
->destroy(request
);
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
)
75 DBG1(DBG_CFG
, "ocsp request to %s failed", url
);
81 response
= lib
->creds
->create(lib
->creds
,
82 CRED_CERTIFICATE
, CERT_X509_OCSP_RESPONSE
,
83 BUILD_BLOB_ASN1_DER
, receive
, BUILD_END
);
87 DBG1(DBG_CFG
, "parsing ocsp response failed");
94 * check the signature of an OCSP response
96 static bool verify_ocsp(ocsp_response_t
*response
)
98 certificate_t
*issuer
, *subject
;
99 identification_t
*responder
;
100 ocsp_response_wrapper_t
*wrapper
;
101 enumerator_t
*enumerator
;
102 bool verified
= FALSE
;
104 wrapper
= ocsp_response_wrapper_create((ocsp_response_t
*)response
);
105 lib
->credmgr
->add_local_set(lib
->credmgr
, &wrapper
->set
);
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
))
113 if (lib
->credmgr
->issued_by(lib
->credmgr
, subject
, issuer
))
115 DBG1(DBG_CFG
, " ocsp response correctly signed by \"%Y\"",
116 issuer
->get_subject(issuer
));
121 enumerator
->destroy(enumerator
);
123 lib
->credmgr
->remove_local_set(lib
->credmgr
, &wrapper
->set
);
124 wrapper
->destroy(wrapper
);
129 * Get the better of two OCSP responses, and check for usable OCSP info
131 static certificate_t
*get_better_ocsp(certificate_t
*cand
, certificate_t
*best
,
132 x509_t
*subject
, x509_t
*issuer
, cert_validation_t
*valid
, bool cache
)
134 ocsp_response_t
*response
;
135 time_t revocation
, this_update
, next_update
, valid_until
;
137 bool revoked
= FALSE
;
139 response
= (ocsp_response_t
*)cand
;
141 /* check ocsp signature */
142 if (!verify_ocsp(response
))
144 DBG1(DBG_CFG
, "ocsp response verification failed");
148 /* check if response contains our certificate */
149 switch (response
->get_status(response
, subject
, issuer
, &revocation
, &reason
,
150 &this_update
, &next_update
))
152 case VALIDATION_REVOKED
:
153 /* subject has been revoked by a valid OCSP response */
154 DBG1(DBG_CFG
, "certificate was revoked on %T, reason: %N",
155 &revocation
, TRUE
, crl_reason_names
, reason
);
158 case VALIDATION_GOOD
:
159 /* results in either good or stale */
162 case VALIDATION_FAILED
:
163 /* candidate unusable, does not contain our cert */
164 DBG1(DBG_CFG
, " ocsp response contains no status on our certificate");
169 /* select the better of the two responses */
170 if (best
== NULL
|| certificate_is_newer(cand
, best
))
174 if (best
->get_validity(best
, NULL
, NULL
, &valid_until
))
176 DBG1(DBG_CFG
, " ocsp response is valid: until %T",
177 &valid_until
, FALSE
);
178 *valid
= VALIDATION_GOOD
;
180 { /* cache non-stale only, stale certs get refetched */
181 lib
->credmgr
->cache_cert(lib
->credmgr
, best
);
186 DBG1(DBG_CFG
, " ocsp response is stale: since %T",
187 &valid_until
, FALSE
);
188 *valid
= VALIDATION_STALE
;
193 *valid
= VALIDATION_STALE
;
197 { /* revoked always counts, even if stale */
198 *valid
= VALIDATION_REVOKED
;
204 * validate a x509 certificate using OCSP
206 static cert_validation_t
check_ocsp(x509_t
*subject
, x509_t
*issuer
,
209 enumerator_t
*enumerator
;
210 cert_validation_t valid
= VALIDATION_SKIPPED
;
211 certificate_t
*best
= NULL
, *current
;
212 identification_t
*keyid
= NULL
;
213 public_key_t
*public;
217 /** lookup cache for valid OCSP responses */
218 enumerator
= lib
->credmgr
->create_cert_enumerator(lib
->credmgr
,
219 CERT_X509_OCSP_RESPONSE
, KEY_ANY
, NULL
, FALSE
);
220 while (enumerator
->enumerate(enumerator
, ¤t
))
222 current
->get_ref(current
);
223 best
= get_better_ocsp(current
, best
, subject
, issuer
, &valid
, FALSE
);
224 if (best
&& valid
!= VALIDATION_STALE
)
226 DBG1(DBG_CFG
, " using cached ocsp response");
230 enumerator
->destroy(enumerator
);
232 /* derive the authorityKeyIdentifier from the issuer's public key */
233 current
= &issuer
->interface
;
234 public = current
->get_public_key(current
);
235 if (public && public->get_fingerprint(public, KEYID_PUBKEY_SHA1
, &chunk
))
237 keyid
= identification_create_from_encoding(ID_KEY_ID
, chunk
);
239 /** fetch from configured OCSP responder URLs */
240 if (keyid
&& valid
!= VALIDATION_GOOD
&& valid
!= VALIDATION_REVOKED
)
242 enumerator
= lib
->credmgr
->create_cdp_enumerator(lib
->credmgr
,
243 CERT_X509_OCSP_RESPONSE
, keyid
);
244 while (enumerator
->enumerate(enumerator
, &uri
))
246 current
= fetch_ocsp(uri
, &subject
->interface
, &issuer
->interface
);
249 best
= get_better_ocsp(current
, best
, subject
, issuer
,
251 if (best
&& valid
!= VALIDATION_STALE
)
257 enumerator
->destroy(enumerator
);
262 /* fallback to URL fetching from subject certificate's URIs */
263 if (valid
!= VALIDATION_GOOD
&& valid
!= VALIDATION_REVOKED
)
265 enumerator
= subject
->create_ocsp_uri_enumerator(subject
);
266 while (enumerator
->enumerate(enumerator
, &uri
))
268 current
= fetch_ocsp(uri
, &subject
->interface
, &issuer
->interface
);
271 best
= get_better_ocsp(current
, best
, subject
, issuer
,
273 if (best
&& valid
!= VALIDATION_STALE
)
279 enumerator
->destroy(enumerator
);
281 /* an uri was found, but no result. switch validation state to failed */
282 if (valid
== VALIDATION_SKIPPED
&& uri
)
284 valid
= VALIDATION_FAILED
;
288 auth
->add(auth
, AUTH_RULE_OCSP_VALIDATION
, valid
);
289 if (valid
== VALIDATION_GOOD
)
290 { /* successful OCSP check fulfills also CRL constraint */
291 auth
->add(auth
, AUTH_RULE_CRL_VALIDATION
, VALIDATION_GOOD
);
299 * fetch a CRL from an URL
301 static certificate_t
* fetch_crl(char *url
)
306 DBG1(DBG_CFG
, " fetching crl from '%s' ...", url
);
307 if (lib
->fetcher
->fetch(lib
->fetcher
, url
, &chunk
, FETCH_END
) != SUCCESS
)
309 DBG1(DBG_CFG
, "crl fetching failed");
312 crl
= lib
->creds
->create(lib
->creds
, CRED_CERTIFICATE
, CERT_X509_CRL
,
313 BUILD_BLOB_ASN1_DER
, chunk
, BUILD_END
);
317 DBG1(DBG_CFG
, "crl fetched successfully but parsing failed");
324 * check the signature of an CRL
326 static bool verify_crl(certificate_t
*crl
)
328 certificate_t
*issuer
;
329 enumerator_t
*enumerator
;
330 bool verified
= FALSE
;
332 enumerator
= lib
->credmgr
->create_trusted_enumerator(lib
->credmgr
,
333 KEY_ANY
, crl
->get_issuer(crl
), FALSE
);
334 while (enumerator
->enumerate(enumerator
, &issuer
, NULL
))
336 if (lib
->credmgr
->issued_by(lib
->credmgr
, crl
, issuer
))
338 DBG1(DBG_CFG
, " crl correctly signed by \"%Y\"",
339 issuer
->get_subject(issuer
));
344 enumerator
->destroy(enumerator
);
350 * Get the better of two CRLs, and check for usable CRL info
352 static certificate_t
*get_better_crl(certificate_t
*cand
, certificate_t
*best
,
353 x509_t
*subject
, cert_validation_t
*valid
, bool cache
)
355 enumerator_t
*enumerator
;
356 time_t revocation
, valid_until
;
361 /* check CRL signature */
362 if (!verify_crl(cand
))
364 DBG1(DBG_CFG
, "crl response verification failed");
370 enumerator
= crl
->create_enumerator(crl
);
371 while (enumerator
->enumerate(enumerator
, &serial
, &revocation
, &reason
))
373 if (chunk_equals(serial
, subject
->get_serial(subject
)))
375 DBG1(DBG_CFG
, "certificate was revoked on %T, reason: %N",
376 &revocation
, TRUE
, crl_reason_names
, reason
);
377 *valid
= VALIDATION_REVOKED
;
378 enumerator
->destroy(enumerator
);
383 enumerator
->destroy(enumerator
);
385 /* select the better of the two CRLs */
386 if (best
== NULL
|| crl_is_newer(crl
, (crl_t
*)best
))
390 if (best
->get_validity(best
, NULL
, NULL
, &valid_until
))
392 DBG1(DBG_CFG
, " crl is valid: until %T", &valid_until
, FALSE
);
393 *valid
= VALIDATION_GOOD
;
395 { /* we cache non-stale crls only, as a stale crls are refetched */
396 lib
->credmgr
->cache_cert(lib
->credmgr
, best
);
401 DBG1(DBG_CFG
, " crl is stale: since %T", &valid_until
, FALSE
);
402 *valid
= VALIDATION_STALE
;
407 *valid
= VALIDATION_STALE
;
414 * Find or fetch a certificate for a given crlIssuer
416 static cert_validation_t
find_crl(x509_t
*subject
, identification_t
*issuer
,
417 certificate_t
**best
, bool *uri_found
)
419 cert_validation_t valid
= VALIDATION_SKIPPED
;
420 enumerator_t
*enumerator
;
421 certificate_t
*current
;
424 /* find a cached crl */
425 enumerator
= lib
->credmgr
->create_cert_enumerator(lib
->credmgr
,
426 CERT_X509_CRL
, KEY_ANY
, issuer
, FALSE
);
427 while (enumerator
->enumerate(enumerator
, ¤t
))
429 current
->get_ref(current
);
430 *best
= get_better_crl(current
, *best
, subject
, &valid
, FALSE
);
431 if (*best
&& valid
!= VALIDATION_STALE
)
433 DBG1(DBG_CFG
, " using cached crl");
437 enumerator
->destroy(enumerator
);
439 /* fallback to fetching crls from credential sets cdps */
440 if (valid
!= VALIDATION_GOOD
&& valid
!= VALIDATION_REVOKED
)
442 enumerator
= lib
->credmgr
->create_cdp_enumerator(lib
->credmgr
,
443 CERT_X509_CRL
, issuer
);
444 while (enumerator
->enumerate(enumerator
, &uri
))
447 current
= fetch_crl(uri
);
448 if (!current
->has_issuer(current
, issuer
))
450 DBG1(DBG_CFG
, "issuer of fetched CRL '%Y' does not match CRL "
451 "issuer '%Y'", current
->get_issuer(current
), issuer
);
452 current
->destroy(current
);
457 *best
= get_better_crl(current
, *best
, subject
, &valid
, TRUE
);
458 if (*best
&& valid
!= VALIDATION_STALE
)
464 enumerator
->destroy(enumerator
);
470 * validate a x509 certificate using CRL
472 static cert_validation_t
check_crl(x509_t
*subject
, x509_t
*issuer
,
475 cert_validation_t valid
= VALIDATION_SKIPPED
;
476 identification_t
*id
;
477 certificate_t
*best
= NULL
;
478 bool uri_found
= FALSE
;
479 certificate_t
*current
;
480 enumerator_t
*enumerator
;
484 /* use issuers subjectKeyIdentifier to find a cached CRL / fetch from CDP */
485 chunk
= issuer
->get_subjectKeyIdentifier(issuer
);
488 id
= identification_create_from_encoding(ID_KEY_ID
, chunk
);
489 valid
= find_crl(subject
, id
, &best
, &uri_found
);
493 /* find a cached CRL or fetch via configured CDP via CRLIssuer */
494 enumerator
= subject
->create_crl_uri_enumerator(subject
);
495 while (valid
!= VALIDATION_GOOD
&& valid
!= VALIDATION_REVOKED
&&
496 enumerator
->enumerate(enumerator
, &uri
, &id
))
500 valid
= find_crl(subject
, id
, &best
, &uri_found
);
503 enumerator
->destroy(enumerator
);
505 /* fallback to fetching CRLs from CDPs found in subjects certificate */
506 if (valid
!= VALIDATION_GOOD
&& valid
!= VALIDATION_REVOKED
)
508 enumerator
= subject
->create_crl_uri_enumerator(subject
);
509 while (enumerator
->enumerate(enumerator
, &uri
, &id
))
512 current
= fetch_crl(uri
);
515 if (id
&& !current
->has_issuer(current
, id
))
517 DBG1(DBG_CFG
, "issuer of fetched CRL '%Y' does not match "
518 "certificates CRL issuer '%Y'",
519 current
->get_issuer(current
), id
);
520 current
->destroy(current
);
523 best
= get_better_crl(current
, best
, subject
, &valid
, TRUE
);
524 if (best
&& valid
!= VALIDATION_STALE
)
530 enumerator
->destroy(enumerator
);
533 /* an uri was found, but no result. switch validation state to failed */
534 if (valid
== VALIDATION_SKIPPED
&& uri_found
)
536 valid
= VALIDATION_FAILED
;
540 if (valid
== VALIDATION_SKIPPED
)
541 { /* if we skipped CRL validation, we use the result of OCSP for
542 * constraint checking */
543 auth
->add(auth
, AUTH_RULE_CRL_VALIDATION
,
544 auth
->get(auth
, AUTH_RULE_OCSP_VALIDATION
));
548 auth
->add(auth
, AUTH_RULE_CRL_VALIDATION
, valid
);
555 METHOD(cert_validator_t
, validate
, bool,
556 private_revocation_validator_t
*this, certificate_t
*subject
,
557 certificate_t
*issuer
, bool online
, int pathlen
, auth_cfg_t
*auth
)
559 if (subject
->get_type(subject
) == CERT_X509
&&
560 issuer
->get_type(issuer
) == CERT_X509
&&
563 DBG1(DBG_CFG
, "checking certificate status of \"%Y\"",
564 subject
->get_subject(subject
));
565 switch (check_ocsp((x509_t
*)subject
, (x509_t
*)issuer
, auth
))
567 case VALIDATION_GOOD
:
568 DBG1(DBG_CFG
, "certificate status is good");
570 case VALIDATION_REVOKED
:
571 /* has already been logged */
573 case VALIDATION_SKIPPED
:
574 DBG2(DBG_CFG
, "ocsp check skipped, no ocsp found");
576 case VALIDATION_STALE
:
577 DBG1(DBG_CFG
, "ocsp information stale, fallback to crl");
579 case VALIDATION_FAILED
:
580 DBG1(DBG_CFG
, "ocsp check failed, fallback to crl");
583 switch (check_crl((x509_t
*)subject
, (x509_t
*)issuer
, auth
))
585 case VALIDATION_GOOD
:
586 DBG1(DBG_CFG
, "certificate status is good");
588 case VALIDATION_REVOKED
:
589 /* has already been logged */
591 case VALIDATION_FAILED
:
592 case VALIDATION_SKIPPED
:
593 DBG1(DBG_CFG
, "certificate status is not available");
595 case VALIDATION_STALE
:
596 DBG1(DBG_CFG
, "certificate status is unknown, crl is stale");
603 METHOD(revocation_validator_t
, destroy
, void,
604 private_revocation_validator_t
*this)
612 revocation_validator_t
*revocation_validator_create()
614 private_revocation_validator_t
*this;
618 .validator
.validate
= _validate
,
623 return &this->public;