support of crlcheckinterval=0 to disable IKEv2 CRL fetching
[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 "certinfo.h"
33 #include "ocsp.h"
34
35 #include <library.h>
36 #include <debug.h>
37 #include <utils/linked_list.h>
38 #include <utils/identification.h>
39 #include <utils/fetcher.h>
40
41 typedef struct private_ca_info_t private_ca_info_t;
42
43 /**
44 * Private data of a ca_info_t object.
45 */
46 struct private_ca_info_t {
47 /**
48 * Public interface for this ca info record
49 */
50 ca_info_t public;
51
52 /**
53 * Name of the ca info record
54 */
55 char *name;
56
57 /**
58 * Time when ca info record was installed
59 */
60 time_t installed;
61
62 /**
63 * Distinguished Name of the CA
64 */
65 x509_t *cacert;
66
67 /**
68 * List of crl URIs
69 */
70 linked_list_t *crluris;
71
72 /**
73 * List of ocsp URIs
74 */
75 linked_list_t *ocspuris;
76
77 /**
78 * CRL issued by this ca
79 */
80 crl_t *crl;
81
82 /**
83 * List of certificate info records
84 */
85 linked_list_t *certinfos;
86
87 /**
88 * mutex controls access to the elements:
89 * name, crluris, ocspuris, crl, and certinfos
90 */
91 pthread_mutex_t mutex;
92 };
93
94 /**
95 * static value set by ca_info_set_crl()
96 */
97 static crl_check_interval = 0;
98
99 /**
100 * Implements ca_info_t.equals
101 */
102 static bool equals(const private_ca_info_t *this, const private_ca_info_t *that)
103 {
104 return chunk_equals(this->cacert->get_keyid(this->cacert),
105 that->cacert->get_keyid(that->cacert));
106 }
107
108 /**
109 * Implements ca_info_t.equals_name_release_info
110 */
111 static bool equals_name_release_info(private_ca_info_t *this, const char *name)
112 {
113 bool found;
114
115 pthread_mutex_lock(&(this->mutex));
116 found = this->name != NULL && streq(this->name, name);
117
118 if (found)
119 {
120 this->crluris->destroy_offset(this->crluris,
121 offsetof(identification_t, destroy));
122 this->crluris = linked_list_create();
123
124 this->ocspuris->destroy_offset(this->ocspuris,
125 offsetof(identification_t, destroy));
126 this->ocspuris = linked_list_create();
127
128 free(this->name);
129 this->name = NULL;
130 }
131
132 pthread_mutex_unlock(&(this->mutex));
133 return found;
134 }
135
136 /**
137 * Implements ca_info_t.is_crl_issuer
138 */
139 static bool is_cert_issuer(private_ca_info_t *this, const x509_t *cert)
140 {
141 return cert->is_issuer(cert, this->cacert);
142 }
143
144 /**
145 * Implements ca_info_t.is_crl_issuer
146 */
147 static bool is_crl_issuer(private_ca_info_t *this, const crl_t *crl)
148 {
149 return crl->is_issuer(crl, this->cacert);
150 }
151
152 /**
153 * Implements ca_info_t.has_crl
154 */
155 static bool has_crl(private_ca_info_t *this)
156 {
157 bool found;
158
159 pthread_mutex_lock(&(this->mutex));
160 found = this->crl != NULL;
161 pthread_mutex_unlock(&(this->mutex));
162
163 return found;
164 }
165
166 /**
167 * Implements ca_info_t.has_certinfos
168 */
169 static bool has_certinfos(private_ca_info_t *this)
170 {
171 bool found;
172
173 pthread_mutex_lock(&(this->mutex));
174 found = this->certinfos->get_count(this->certinfos) > 0;
175 pthread_mutex_unlock(&(this->mutex));
176
177 return found;
178 }
179
180 /**
181 * Implements ca_info_t.add_crl
182 */
183 static void add_crl(private_ca_info_t *this, crl_t *crl)
184 {
185 pthread_mutex_lock(&(this->mutex));
186
187 if (this->crl)
188 {
189 if (crl->is_newer(crl, this->crl))
190 {
191 this->crl->destroy(this->crl);
192 this->crl = crl;
193 DBG1(" thisUpdate is newer - existing crl replaced");
194 }
195 else
196 {
197 crl->destroy(crl);
198 DBG1(" thisUpdate is not newer - existing crl retained");
199 }
200 }
201 else
202 {
203 this->crl = crl;
204 DBG2(" crl added");
205 }
206
207 pthread_mutex_unlock(&(this->mutex));
208 }
209
210 /**
211 * Implements ca_info_t.list_crl
212 */
213 static void list_crl(private_ca_info_t *this, FILE *out, bool utc)
214 {
215 pthread_mutex_lock(&(this->mutex));
216
217 fprintf(out, "%#U\n", this->crl, utc);
218
219 pthread_mutex_unlock(&(this->mutex));
220 }
221
222 /**
223 * Implements ca_info_t.list_certinfos
224 */
225 static void list_certinfos(private_ca_info_t *this, FILE *out, bool utc)
226 {
227 pthread_mutex_lock(&(this->mutex));
228
229 fprintf(out," authname: '%D'\n", this->cacert->get_subject(this->cacert));
230 {
231 chunk_t authkey = this->cacert->get_subjectKeyID(this->cacert);
232
233 fprintf(out," authkey: %#B\n", &authkey);
234 }
235 {
236 iterator_t *iterator = this->certinfos->create_iterator(this->certinfos, TRUE);
237 certinfo_t *certinfo;
238
239 while (iterator->iterate(iterator, (void**)&certinfo))
240 {
241 fprintf(out, "%#Y\n", certinfo, utc);
242 }
243 iterator->destroy(iterator);
244 }
245
246 pthread_mutex_unlock(&(this->mutex));
247 }
248
249 /**
250 * Find an exact copy of an identification in a linked list
251 */
252 static identification_t* find_identification(linked_list_t *list, identification_t *id)
253 {
254 identification_t *found_id = NULL, *current_id;
255
256 iterator_t *iterator = list->create_iterator(list, TRUE);
257
258 while (iterator->iterate(iterator, (void**)&current_id))
259 {
260 if (id->equals(id, current_id))
261 {
262 found_id = current_id;
263 break;
264 }
265 }
266 iterator->destroy(iterator);
267
268 return found_id;
269 }
270
271 /**
272 * Add a unique identification to a linked list
273 */
274 static identification_t *add_identification(linked_list_t *list, identification_t *id)
275 {
276 identification_t *found_id = find_identification(list, id);
277
278 if (found_id)
279 {
280 id->destroy(id);
281 return found_id;
282 }
283 else
284 {
285 list->insert_last(list, (void*)id);
286 return id;
287 }
288 }
289
290 /**
291 * Implements ca_info_t.add_crluri
292 */
293 static void add_crluri(private_ca_info_t *this, chunk_t uri)
294 {
295 if (uri.len < 6 ||
296 (strncasecmp(uri.ptr, "http", 4) != 0 &&
297 strncasecmp(uri.ptr, "ldap", 4) != 0 &&
298 strncasecmp(uri.ptr, "file", 4) != 0 &&
299 strncasecmp(uri.ptr, "ftp", 3) != 0))
300 {
301 DBG1(" invalid crl uri '%#B'", uri);
302 return;
303 }
304 else
305 {
306 identification_t *crluri = identification_create_from_encoding(ID_DER_ASN1_GN_URI, uri);
307
308 pthread_mutex_lock(&(this->mutex));
309 add_identification(this->crluris, crluri);
310 pthread_mutex_unlock(&(this->mutex));
311 }
312 }
313
314 /**
315 * Implements ca_info_t.add_ocspuri
316 */
317 static void add_ocspuri(private_ca_info_t *this, chunk_t uri)
318 {
319 if (uri.len < 7 || strncasecmp(uri.ptr, "http", 4) != 0)
320 {
321 DBG1(" invalid ocsp uri '%.*s'", uri.len, uri.ptr);
322 return;
323 }
324 else
325 {
326 identification_t *ocspuri = identification_create_from_encoding(ID_DER_ASN1_GN_URI, uri);
327
328 pthread_mutex_lock(&(this->mutex));
329 add_identification(this->ocspuris, ocspuri);
330 pthread_mutex_unlock(&(this->mutex));
331 }
332 }
333
334 /**
335 * Implements ca_info_t.add_info.
336 */
337 void add_info (private_ca_info_t *this, const private_ca_info_t *that)
338 {
339 pthread_mutex_lock(&(this->mutex));
340
341 if (this->name == NULL && that->name != NULL)
342 {
343 this->name = strdup(that->name);
344 }
345
346 pthread_mutex_unlock(&(this->mutex));
347
348 {
349 identification_t *uri;
350
351 iterator_t *iterator = that->crluris->create_iterator(that->crluris, TRUE);
352
353 while (iterator->iterate(iterator, (void**)&uri))
354 {
355 add_crluri(this, uri->get_encoding(uri));
356 }
357 iterator->destroy(iterator);
358 }
359
360 {
361 identification_t *uri;
362
363 iterator_t *iterator = that->ocspuris->create_iterator(that->ocspuris, TRUE);
364
365 while (iterator->iterate(iterator, (void**)&uri))
366 {
367 add_ocspuri(this, uri->get_encoding(uri));
368 }
369 iterator->destroy(iterator);
370 }
371 }
372
373 /**
374 * Implements ca_info_t.get_certificate.
375 */
376 static x509_t* get_certificate(private_ca_info_t* this)
377 {
378 return this->cacert;
379 }
380
381 /**
382 * Implements ca_info_t.verify_by_crl.
383 */
384 static cert_status_t verify_by_crl(private_ca_info_t* this,
385 certinfo_t *certinfo)
386 {
387 rsa_public_key_t *issuer_public_key = this->cacert->get_public_key(this->cacert);
388 bool stale;
389
390 pthread_mutex_lock(&(this->mutex));
391 if (this->crl == NULL)
392 {
393 stale = TRUE;
394 DBG1("no crl is locally available");
395 }
396 else
397 {
398 stale = !this->crl->is_valid(this->crl);
399 DBG1("crl is %s", stale? "stale":"valid");
400 }
401
402 if (stale && crl_check_interval > 0)
403 {
404 iterator_t *iterator = this->crluris->create_iterator(this->crluris, TRUE);
405 identification_t *uri;
406
407 while (iterator->iterate(iterator, (void**)&uri))
408 {
409 fetcher_t *fetcher;
410 char uri_string[BUF_LEN];
411 chunk_t uri_chunk = uri->get_encoding(uri);
412 chunk_t response_chunk;
413
414 snprintf(uri_string, BUF_LEN, "%.*s", uri_chunk.len, uri_chunk.ptr);
415 fetcher = fetcher_create(uri_string);
416
417 response_chunk = fetcher->get(fetcher);
418 fetcher->destroy(fetcher);
419 if (response_chunk.ptr != NULL)
420 {
421 crl_t *crl = crl_create_from_chunk(response_chunk);
422
423 if (crl == NULL)
424 {
425 free(response_chunk.ptr);
426 continue;
427 }
428 if (!is_crl_issuer(this, crl))
429 {
430 DBG1(" fetched crl has wrong issuer");
431 crl->destroy(crl);
432 continue;
433 }
434 if (!crl->verify(crl, issuer_public_key))
435 {
436 DBG1("fetched crl signature is invalid");
437 crl->destroy(crl);
438 continue;
439 }
440 DBG2("fetched crl signature is valid");
441
442 if (this->crl == NULL)
443 {
444 this->crl = crl;
445 }
446 else if (crl->is_newer(crl, this->crl))
447 {
448 this->crl->destroy(this->crl);
449 this->crl = crl;
450 DBG1(" thisUpdate is newer - existing crl replaced");
451 if (this->crl->is_valid(this->crl))
452 {
453 /* we found a valid crl and exit the fetch loop */
454 break;
455 }
456 else
457 {
458 DBG1("fetched crl is stale");
459 }
460 }
461 else
462 {
463 crl->destroy(crl);
464 DBG1("thisUpdate is not newer - existing crl retained");
465 }
466 }
467 }
468 iterator->destroy(iterator);
469 }
470
471 if (this->crl)
472 {
473 if (!this->crl->verify(this->crl, issuer_public_key))
474 {
475 DBG1("crl signature is invalid");
476 goto ret;
477 }
478 DBG2("crl signature is valid");
479
480 this->crl->get_status(this->crl, certinfo);
481 }
482
483 ret:
484 pthread_mutex_unlock(&(this->mutex));
485 return certinfo->get_status(certinfo);
486 }
487
488 /**
489 * Implements ca_info_t.verify_by_ocsp.
490 */
491 static cert_status_t verify_by_ocsp(private_ca_info_t* this,
492 certinfo_t *certinfo,
493 credential_store_t *credentials)
494 {
495 bool stale;
496 iterator_t *iterator;
497 certinfo_t *cached_certinfo = NULL;
498 int comparison = 1;
499
500 pthread_mutex_lock(&(this->mutex));
501
502 /* do we support OCSP at all? */
503 if (this->ocspuris->get_count(this->ocspuris) == 0)
504 {
505 goto ret;
506 }
507
508 iterator = this->certinfos->create_iterator(this->certinfos, TRUE);
509
510 /* find the list insertion point in alphabetical order */
511 while(iterator->iterate(iterator, (void**)&cached_certinfo))
512 {
513 comparison = certinfo->compare_serialNumber(certinfo, cached_certinfo);
514
515 if (comparison <= 0)
516 {
517 break;
518 }
519 }
520
521 /* do we have a valid certinfo_t for this serial number in our cache? */
522 if (comparison == 0)
523 {
524 stale = cached_certinfo->get_nextUpdate(cached_certinfo) < time(NULL);
525 DBG1("ocsp status in cache is %s", stale ? "stale":"fresh");
526 }
527 else
528 {
529 stale = TRUE;
530 DBG1("ocsp status is not in cache");
531 }
532
533 if (stale)
534 {
535 ocsp_t *ocsp;
536
537 ocsp = ocsp_create(this->cacert, this->ocspuris);
538 ocsp->fetch(ocsp, certinfo, credentials);
539 if (certinfo->get_status(certinfo) != CERT_UNDEFINED)
540 {
541 if (comparison != 0)
542 {
543 cached_certinfo = certinfo_create(certinfo->get_serialNumber(certinfo));
544
545 if (comparison > 0)
546 {
547 iterator->insert_after(iterator, (void *)cached_certinfo);
548 }
549 else
550 {
551 iterator->insert_before(iterator, (void *)cached_certinfo);
552 }
553 }
554 cached_certinfo->update(cached_certinfo, certinfo);
555 }
556 ocsp->destroy(ocsp);
557 }
558 else
559 {
560 certinfo->update(certinfo, cached_certinfo);
561 }
562
563 iterator->destroy(iterator);
564
565 ret:
566 pthread_mutex_unlock(&(this->mutex));
567 return certinfo->get_status(certinfo);
568 }
569
570 /**
571 * Implements ca_info_t.purge_ocsp
572 */
573 static void purge_ocsp(private_ca_info_t *this)
574 {
575 pthread_mutex_lock(&(this->mutex));
576
577 this->certinfos->destroy_offset(this->certinfos,
578 offsetof(certinfo_t, destroy));
579 this->certinfos = linked_list_create();
580
581 pthread_mutex_unlock(&(this->mutex));
582 }
583
584 /**
585 * Implements ca_info_t.destroy
586 */
587 static void destroy(private_ca_info_t *this)
588 {
589 this->crluris->destroy_offset(this->crluris,
590 offsetof(identification_t, destroy));
591 this->ocspuris->destroy_offset(this->ocspuris,
592 offsetof(identification_t, destroy));
593 this->certinfos->destroy_offset(this->certinfos,
594 offsetof(certinfo_t, destroy));
595 DESTROY_IF(this->crl);
596 free(this->name);
597 free(this);
598 }
599
600 /**
601 * output handler in printf()
602 */
603 static int print(FILE *stream, const struct printf_info *info,
604 const void *const *args)
605 {
606 private_ca_info_t *this = *((private_ca_info_t**)(args[0]));
607 bool utc = TRUE;
608 int written = 0;
609 const x509_t *cacert;
610
611 if (info->alt)
612 {
613 utc = *((bool*)args[1]);
614 }
615 if (this == NULL)
616 {
617 return fprintf(stream, "(null)");
618 }
619
620 pthread_mutex_lock(&(this->mutex));
621 written += fprintf(stream, "%#T", &this->installed, utc);
622
623 if (this->name)
624 {
625 written += fprintf(stream, ", \"%s\"\n", this->name);
626 }
627 else
628 {
629 written += fprintf(stream, "\n");
630 }
631
632 cacert = this->cacert;
633 written += fprintf(stream, " authname: '%D'\n", cacert->get_subject(cacert));
634 {
635 chunk_t authkey = cacert->get_subjectKeyID(cacert);
636
637 written += fprintf(stream, " authkey: %#B\n", &authkey);
638 }
639 {
640 chunk_t keyid = cacert->get_keyid(cacert);
641
642 written += fprintf(stream, " keyid: %#B\n", &keyid);
643 }
644 {
645 identification_t *crluri;
646 iterator_t *iterator = this->crluris->create_iterator(this->crluris, TRUE);
647 bool first = TRUE;
648
649 while (iterator->iterate(iterator, (void**)&crluri))
650 {
651 written += fprintf(stream, " %s '%D'\n",
652 first? "crluris:":" ", crluri);
653 first = FALSE;
654 }
655 iterator->destroy(iterator);
656 }
657 {
658 identification_t *ocspuri;
659 iterator_t *iterator = this->ocspuris->create_iterator(this->ocspuris, TRUE);
660 bool first = TRUE;
661
662 while (iterator->iterate(iterator, (void**)&ocspuri))
663 {
664 written += fprintf(stream, " %s '%D'\n",
665 first? "ocspuris:":" ", ocspuri);
666 first = FALSE;
667 }
668 iterator->destroy(iterator);
669 }
670 pthread_mutex_unlock(&(this->mutex));
671 return written;
672 }
673
674 /**
675 * register printf() handlers
676 */
677 static void __attribute__ ((constructor))print_register()
678 {
679 register_printf_function(PRINTF_CAINFO, print, arginfo_ptr_alt_ptr_int);
680 }
681
682 /*
683 * Described in header.
684 */
685 void ca_info_set_crlcheckinterval(u_int interval)
686 {
687 crl_check_interval = interval;
688 }
689
690 /*
691 * Described in header.
692 */
693 ca_info_t *ca_info_create(const char *name, x509_t *cacert)
694 {
695 private_ca_info_t *this = malloc_thing(private_ca_info_t);
696
697 /* initialize */
698 this->installed = time(NULL);
699 this->name = (name == NULL)? NULL:strdup(name);
700 this->cacert = cacert;
701 this->crluris = linked_list_create();
702 this->ocspuris = linked_list_create();
703 this->certinfos = linked_list_create();
704 this->crl = NULL;
705
706 /* initialize the mutex */
707 pthread_mutex_init(&(this->mutex), NULL);
708
709 /* public functions */
710 this->public.equals = (bool (*) (const ca_info_t*,const ca_info_t*))equals;
711 this->public.equals_name_release_info = (bool (*) (ca_info_t*,const char*))equals_name_release_info;
712 this->public.is_cert_issuer = (bool (*) (ca_info_t*,const x509_t*))is_cert_issuer;
713 this->public.is_crl_issuer = (bool (*) (ca_info_t*,const crl_t*))is_crl_issuer;
714 this->public.add_info = (void (*) (ca_info_t*,const ca_info_t*))add_info;
715 this->public.add_crl = (void (*) (ca_info_t*,crl_t*))add_crl;
716 this->public.has_crl = (bool (*) (ca_info_t*))has_crl;
717 this->public.has_certinfos = (bool (*) (ca_info_t*))has_certinfos;
718 this->public.list_crl = (void (*) (ca_info_t*,FILE*,bool))list_crl;
719 this->public.list_certinfos = (void (*) (ca_info_t*,FILE*,bool))list_certinfos;
720 this->public.add_crluri = (void (*) (ca_info_t*,chunk_t))add_crluri;
721 this->public.add_ocspuri = (void (*) (ca_info_t*,chunk_t))add_ocspuri;
722 this->public.get_certificate = (x509_t* (*) (ca_info_t*))get_certificate;
723 this->public.verify_by_crl = (cert_status_t (*) (ca_info_t*,certinfo_t*))verify_by_crl;
724 this->public.verify_by_ocsp = (cert_status_t (*) (ca_info_t*,certinfo_t*,credential_store_t*))verify_by_ocsp;
725 this->public.purge_ocsp = (void (*) (ca_info_t*))purge_ocsp;
726 this->public.destroy = (void (*) (ca_info_t*))destroy;
727
728 return &this->public;
729 }