Moved CRL/OCSP checking to a dedicated plugin called revocation
[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 <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 send = request->get_encoding(request);
62 request->destroy(request);
63
64 DBG1(DBG_CFG, " requesting ocsp status from '%s' ...", url);
65 if (lib->fetcher->fetch(lib->fetcher, url, &receive,
66 FETCH_REQUEST_DATA, send,
67 FETCH_REQUEST_TYPE, "application/ocsp-request",
68 FETCH_END) != SUCCESS)
69 {
70 DBG1(DBG_CFG, "ocsp request to %s failed", url);
71 chunk_free(&send);
72 return NULL;
73 }
74 chunk_free(&send);
75
76 response = lib->creds->create(lib->creds,
77 CRED_CERTIFICATE, CERT_X509_OCSP_RESPONSE,
78 BUILD_BLOB_ASN1_DER, receive, BUILD_END);
79 chunk_free(&receive);
80 if (!response)
81 {
82 DBG1(DBG_CFG, "parsing ocsp response failed");
83 return NULL;
84 }
85 return response;
86 }
87
88 /**
89 * check the signature of an OCSP response
90 */
91 static bool verify_ocsp(ocsp_response_t *response)
92 {
93 certificate_t *issuer, *subject;
94 identification_t *responder;
95 ocsp_response_wrapper_t *wrapper;
96 enumerator_t *enumerator;
97 bool verified = FALSE;
98
99 wrapper = ocsp_response_wrapper_create((ocsp_response_t*)response);
100 lib->credmgr->add_local_set(lib->credmgr, &wrapper->set);
101
102 subject = &response->certificate;
103 responder = subject->get_issuer(subject);
104 enumerator = lib->credmgr->create_trusted_enumerator(lib->credmgr,
105 KEY_ANY, responder, FALSE);
106 while (enumerator->enumerate(enumerator, &issuer, NULL))
107 {
108 if (lib->credmgr->issued_by(lib->credmgr, subject, issuer))
109 {
110 DBG1(DBG_CFG, " ocsp response correctly signed by \"%Y\"",
111 issuer->get_subject(issuer));
112 verified = TRUE;
113 break;
114 }
115 }
116 enumerator->destroy(enumerator);
117
118 lib->credmgr->remove_local_set(lib->credmgr, &wrapper->set);
119 wrapper->destroy(wrapper);
120 return verified;
121 }
122
123 /**
124 * Get the better of two OCSP responses, and check for usable OCSP info
125 */
126 static certificate_t *get_better_ocsp(certificate_t *cand, certificate_t *best,
127 x509_t *subject, x509_t *issuer, cert_validation_t *valid, bool cache)
128 {
129 ocsp_response_t *response;
130 time_t revocation, this_update, next_update, valid_until;
131 crl_reason_t reason;
132 bool revoked = FALSE;
133
134 response = (ocsp_response_t*)cand;
135
136 /* check ocsp signature */
137 if (!verify_ocsp(response))
138 {
139 DBG1(DBG_CFG, "ocsp response verification failed");
140 cand->destroy(cand);
141 return best;
142 }
143 /* check if response contains our certificate */
144 switch (response->get_status(response, subject, issuer, &revocation, &reason,
145 &this_update, &next_update))
146 {
147 case VALIDATION_REVOKED:
148 /* subject has been revoked by a valid OCSP response */
149 DBG1(DBG_CFG, "certificate was revoked on %T, reason: %N",
150 &revocation, TRUE, crl_reason_names, reason);
151 revoked = TRUE;
152 break;
153 case VALIDATION_GOOD:
154 /* results in either good or stale */
155 break;
156 default:
157 case VALIDATION_FAILED:
158 /* candidate unusable, does not contain our cert */
159 DBG1(DBG_CFG, " ocsp response contains no status on our certificate");
160 cand->destroy(cand);
161 return best;
162 }
163
164 /* select the better of the two responses */
165 if (best == NULL || certificate_is_newer(cand, best))
166 {
167 DESTROY_IF(best);
168 best = cand;
169 if (best->get_validity(best, NULL, NULL, &valid_until))
170 {
171 DBG1(DBG_CFG, " ocsp response is valid: until %T",
172 &valid_until, FALSE);
173 *valid = VALIDATION_GOOD;
174 if (cache)
175 { /* cache non-stale only, stale certs get refetched */
176 lib->credmgr->cache_cert(lib->credmgr, best);
177 }
178 }
179 else
180 {
181 DBG1(DBG_CFG, " ocsp response is stale: since %T",
182 &valid_until, FALSE);
183 *valid = VALIDATION_STALE;
184 }
185 }
186 else
187 {
188 *valid = VALIDATION_STALE;
189 cand->destroy(cand);
190 }
191 if (revoked)
192 { /* revoked always counts, even if stale */
193 *valid = VALIDATION_REVOKED;
194 }
195 return best;
196 }
197
198 /**
199 * validate a x509 certificate using OCSP
200 */
201 static cert_validation_t check_ocsp(x509_t *subject, x509_t *issuer,
202 auth_cfg_t *auth)
203 {
204 enumerator_t *enumerator;
205 cert_validation_t valid = VALIDATION_SKIPPED;
206 certificate_t *best = NULL, *current;
207 identification_t *keyid = NULL;
208 public_key_t *public;
209 chunk_t chunk;
210 char *uri = NULL;
211
212 /** lookup cache for valid OCSP responses */
213 enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr,
214 CERT_X509_OCSP_RESPONSE, KEY_ANY, NULL, FALSE);
215 while (enumerator->enumerate(enumerator, &current))
216 {
217 current->get_ref(current);
218 best = get_better_ocsp(current, best, subject, issuer, &valid, FALSE);
219 if (best && valid != VALIDATION_STALE)
220 {
221 DBG1(DBG_CFG, " using cached ocsp response");
222 break;
223 }
224 }
225 enumerator->destroy(enumerator);
226
227 /* derive the authorityKeyIdentifier from the issuer's public key */
228 current = &issuer->interface;
229 public = current->get_public_key(current);
230 if (public && public->get_fingerprint(public, KEY_ID_PUBKEY_SHA1, &chunk))
231 {
232 keyid = identification_create_from_encoding(ID_KEY_ID, chunk);
233 }
234 /** fetch from configured OCSP responder URLs */
235 if (keyid && valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
236 {
237 enumerator = lib->credmgr->create_cdp_enumerator(lib->credmgr,
238 CERT_X509_OCSP_RESPONSE, keyid);
239 while (enumerator->enumerate(enumerator, &uri))
240 {
241 current = fetch_ocsp(uri, &subject->interface, &issuer->interface);
242 if (current)
243 {
244 best = get_better_ocsp(current, best, subject, issuer,
245 &valid, TRUE);
246 if (best && valid != VALIDATION_STALE)
247 {
248 break;
249 }
250 }
251 }
252 enumerator->destroy(enumerator);
253 }
254 DESTROY_IF(public);
255 DESTROY_IF(keyid);
256
257 /* fallback to URL fetching from subject certificate's URIs */
258 if (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
259 {
260 enumerator = subject->create_ocsp_uri_enumerator(subject);
261 while (enumerator->enumerate(enumerator, &uri))
262 {
263 current = fetch_ocsp(uri, &subject->interface, &issuer->interface);
264 if (current)
265 {
266 best = get_better_ocsp(current, best, subject, issuer,
267 &valid, TRUE);
268 if (best && valid != VALIDATION_STALE)
269 {
270 break;
271 }
272 }
273 }
274 enumerator->destroy(enumerator);
275 }
276 /* an uri was found, but no result. switch validation state to failed */
277 if (valid == VALIDATION_SKIPPED && uri)
278 {
279 valid = VALIDATION_FAILED;
280 }
281 if (auth)
282 {
283 auth->add(auth, AUTH_RULE_OCSP_VALIDATION, valid);
284 if (valid == VALIDATION_GOOD)
285 { /* successful OCSP check fulfills also CRL constraint */
286 auth->add(auth, AUTH_RULE_CRL_VALIDATION, VALIDATION_GOOD);
287 }
288 }
289 DESTROY_IF(best);
290 return valid;
291 }
292
293 /**
294 * fetch a CRL from an URL
295 */
296 static certificate_t* fetch_crl(char *url)
297 {
298 certificate_t *crl;
299 chunk_t chunk;
300
301 DBG1(DBG_CFG, " fetching crl from '%s' ...", url);
302 if (lib->fetcher->fetch(lib->fetcher, url, &chunk, FETCH_END) != SUCCESS)
303 {
304 DBG1(DBG_CFG, "crl fetching failed");
305 return NULL;
306 }
307 crl = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509_CRL,
308 BUILD_BLOB_ASN1_DER, chunk, BUILD_END);
309 chunk_free(&chunk);
310 if (!crl)
311 {
312 DBG1(DBG_CFG, "crl fetched successfully but parsing failed");
313 return NULL;
314 }
315 return crl;
316 }
317
318 /**
319 * check the signature of an CRL
320 */
321 static bool verify_crl(certificate_t *crl)
322 {
323 certificate_t *issuer;
324 enumerator_t *enumerator;
325 bool verified = FALSE;
326
327 enumerator = lib->credmgr->create_trusted_enumerator(lib->credmgr,
328 KEY_ANY, crl->get_issuer(crl), FALSE);
329 while (enumerator->enumerate(enumerator, &issuer, NULL))
330 {
331 if (lib->credmgr->issued_by(lib->credmgr, crl, issuer))
332 {
333 DBG1(DBG_CFG, " crl correctly signed by \"%Y\"",
334 issuer->get_subject(issuer));
335 verified = TRUE;
336 break;
337 }
338 }
339 enumerator->destroy(enumerator);
340
341 return verified;
342 }
343
344 /**
345 * Get the better of two CRLs, and check for usable CRL info
346 */
347 static certificate_t *get_better_crl(certificate_t *cand, certificate_t *best,
348 x509_t *subject, x509_t *issuer, cert_validation_t *valid, bool cache)
349 {
350 enumerator_t *enumerator;
351 time_t revocation, valid_until;
352 crl_reason_t reason;
353 chunk_t serial;
354 crl_t *crl;
355
356 /* check CRL signature */
357 if (!verify_crl(cand))
358 {
359 DBG1(DBG_CFG, "crl response verification failed");
360 cand->destroy(cand);
361 return best;
362 }
363
364 crl = (crl_t*)cand;
365 enumerator = crl->create_enumerator(crl);
366 while (enumerator->enumerate(enumerator, &serial, &revocation, &reason))
367 {
368 if (chunk_equals(serial, subject->get_serial(subject)))
369 {
370 DBG1(DBG_CFG, "certificate was revoked on %T, reason: %N",
371 &revocation, TRUE, crl_reason_names, reason);
372 *valid = VALIDATION_REVOKED;
373 enumerator->destroy(enumerator);
374 DESTROY_IF(best);
375 return cand;
376 }
377 }
378 enumerator->destroy(enumerator);
379
380 /* select the better of the two CRLs */
381 if (best == NULL || crl_is_newer(crl, (crl_t*)best))
382 {
383 DESTROY_IF(best);
384 best = cand;
385 if (best->get_validity(best, NULL, NULL, &valid_until))
386 {
387 DBG1(DBG_CFG, " crl is valid: until %T", &valid_until, FALSE);
388 *valid = VALIDATION_GOOD;
389 if (cache)
390 { /* we cache non-stale crls only, as a stale crls are refetched */
391 lib->credmgr->cache_cert(lib->credmgr, best);
392 }
393 }
394 else
395 {
396 DBG1(DBG_CFG, " crl is stale: since %T", &valid_until, FALSE);
397 *valid = VALIDATION_STALE;
398 }
399 }
400 else
401 {
402 *valid = VALIDATION_STALE;
403 cand->destroy(cand);
404 }
405 return best;
406 }
407
408 /**
409 * validate a x509 certificate using CRL
410 */
411 static cert_validation_t check_crl(x509_t *subject, x509_t *issuer,
412 auth_cfg_t *auth)
413 {
414 cert_validation_t valid = VALIDATION_SKIPPED;
415 identification_t *keyid = NULL;
416 certificate_t *best = NULL;
417 certificate_t *current;
418 public_key_t *public;
419 enumerator_t *enumerator;
420 chunk_t chunk;
421 char *uri = NULL;
422
423 /* derive the authorityKeyIdentifier from the issuer's public key */
424 current = &issuer->interface;
425 public = current->get_public_key(current);
426 if (public && public->get_fingerprint(public, KEY_ID_PUBKEY_SHA1, &chunk))
427 {
428 keyid = identification_create_from_encoding(ID_KEY_ID, chunk);
429
430 /* find a cached crl by authorityKeyIdentifier */
431 enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr,
432 CERT_X509_CRL, KEY_ANY, keyid, FALSE);
433 while (enumerator->enumerate(enumerator, &current))
434 {
435 current->get_ref(current);
436 best = get_better_crl(current, best, subject, issuer,
437 &valid, FALSE);
438 if (best && valid != VALIDATION_STALE)
439 {
440 DBG1(DBG_CFG, " using cached crl");
441 break;
442 }
443 }
444 enumerator->destroy(enumerator);
445
446 /* fallback to fetching crls from credential sets cdps */
447 if (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
448 {
449 enumerator = lib->credmgr->create_cdp_enumerator(lib->credmgr,
450 CERT_X509_CRL, keyid);
451 while (enumerator->enumerate(enumerator, &uri))
452 {
453 current = fetch_crl(uri);
454 if (current)
455 {
456 best = get_better_crl(current, best, subject, issuer,
457 &valid, TRUE);
458 if (best && valid != VALIDATION_STALE)
459 {
460 break;
461 }
462 }
463 }
464 enumerator->destroy(enumerator);
465 }
466 keyid->destroy(keyid);
467 }
468 DESTROY_IF(public);
469
470 /* fallback to fetching crls from cdps from subject's certificate */
471 if (valid != VALIDATION_GOOD && valid != VALIDATION_REVOKED)
472 {
473 enumerator = subject->create_crl_uri_enumerator(subject);
474
475 while (enumerator->enumerate(enumerator, &uri))
476 {
477 current = fetch_crl(uri);
478 if (current)
479 {
480 best = get_better_crl(current, best, subject, issuer,
481 &valid, TRUE);
482 if (best && valid != VALIDATION_STALE)
483 {
484 break;
485 }
486 }
487 }
488 enumerator->destroy(enumerator);
489 }
490
491 /* an uri was found, but no result. switch validation state to failed */
492 if (valid == VALIDATION_SKIPPED && uri)
493 {
494 valid = VALIDATION_FAILED;
495 }
496 if (auth)
497 {
498 if (valid == VALIDATION_SKIPPED)
499 { /* if we skipped CRL validation, we use the result of OCSP for
500 * constraint checking */
501 auth->add(auth, AUTH_RULE_CRL_VALIDATION,
502 auth->get(auth, AUTH_RULE_OCSP_VALIDATION));
503 }
504 else
505 {
506 auth->add(auth, AUTH_RULE_CRL_VALIDATION, valid);
507 }
508 }
509 DESTROY_IF(best);
510 return valid;
511 }
512
513 METHOD(cert_validator_t, validate, bool,
514 private_revocation_validator_t *this, certificate_t *subject,
515 certificate_t *issuer, bool online, int pathlen, auth_cfg_t *auth)
516 {
517 if (subject->get_type(subject) == CERT_X509 &&
518 issuer->get_type(issuer) == CERT_X509 &&
519 online)
520 {
521 DBG1(DBG_CFG, "checking certificate status of \"%Y\"",
522 subject->get_subject(subject));
523 switch (check_ocsp((x509_t*)subject, (x509_t*)issuer, auth))
524 {
525 case VALIDATION_GOOD:
526 DBG1(DBG_CFG, "certificate status is good");
527 return TRUE;
528 case VALIDATION_REVOKED:
529 /* has already been logged */
530 return FALSE;
531 case VALIDATION_SKIPPED:
532 DBG2(DBG_CFG, "ocsp check skipped, no ocsp found");
533 break;
534 case VALIDATION_STALE:
535 DBG1(DBG_CFG, "ocsp information stale, fallback to crl");
536 break;
537 case VALIDATION_FAILED:
538 DBG1(DBG_CFG, "ocsp check failed, fallback to crl");
539 break;
540 }
541 switch (check_crl((x509_t*)subject, (x509_t*)issuer, auth))
542 {
543 case VALIDATION_GOOD:
544 DBG1(DBG_CFG, "certificate status is good");
545 return TRUE;
546 case VALIDATION_REVOKED:
547 /* has already been logged */
548 return FALSE;
549 case VALIDATION_FAILED:
550 case VALIDATION_SKIPPED:
551 DBG1(DBG_CFG, "certificate status is not available");
552 break;
553 case VALIDATION_STALE:
554 DBG1(DBG_CFG, "certificate status is unknown, crl is stale");
555 break;
556 }
557 }
558 return TRUE;
559 }
560
561 METHOD(revocation_validator_t, destroy, void,
562 private_revocation_validator_t *this)
563 {
564 free(this);
565 }
566
567 /**
568 * See header
569 */
570 revocation_validator_t *revocation_validator_create()
571 {
572 private_revocation_validator_t *this;
573
574 INIT(this,
575 .public = {
576 .validator.validate = _validate,
577 .destroy = _destroy,
578 },
579 );
580
581 return &this->public;
582 }