implementation of strictcrlpolicy=ifuri
[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_strict
162 */
163 static bool is_strict(private_ca_info_t *this)
164 {
165 bool strict = strict_crl_policy != STRICT_NO;
166
167 if (strict_crl_policy == STRICT_IFURI)
168 {
169 pthread_mutex_lock(&(this->mutex));
170 strict = this->crluris->get_count(this->crluris) > 0 ||
171 this->ocspuris->get_count(this->ocspuris) > 0;
172 pthread_mutex_unlock(&(this->mutex));
173 }
174 return strict;
175 }
176
177 /**
178 * Implements ca_info_t.has_crl
179 */
180 static bool has_crl(private_ca_info_t *this)
181 {
182 bool found;
183
184 pthread_mutex_lock(&(this->mutex));
185 found = this->crl != NULL;
186 pthread_mutex_unlock(&(this->mutex));
187
188 return found;
189 }
190
191 /**
192 * Implements ca_info_t.has_certinfos
193 */
194 static bool has_certinfos(private_ca_info_t *this)
195 {
196 bool found;
197
198 pthread_mutex_lock(&(this->mutex));
199 found = this->certinfos->get_count(this->certinfos) > 0;
200 pthread_mutex_unlock(&(this->mutex));
201
202 return found;
203 }
204
205 /**
206 * Implements ca_info_t.add_crl
207 */
208 static void add_crl(private_ca_info_t *this, crl_t *crl)
209 {
210 pthread_mutex_lock(&(this->mutex));
211
212 if (this->crl)
213 {
214 if (crl->is_newer(crl, this->crl))
215 {
216 this->crl->destroy(this->crl);
217 this->crl = crl;
218 DBG1(" this crl is newer - existing crl replaced");
219 }
220 else
221 {
222 crl->destroy(crl);
223 DBG1(" this crl is not newer - existing crl retained");
224 }
225 }
226 else
227 {
228 this->crl = crl;
229 DBG2(" crl added");
230 }
231
232 pthread_mutex_unlock(&(this->mutex));
233 }
234
235 /**
236 * Implements ca_info_t.list_crl
237 */
238 static void list_crl(private_ca_info_t *this, FILE *out, bool utc)
239 {
240 pthread_mutex_lock(&this->mutex);
241 this->crl->list(this->crl, out, utc);
242 pthread_mutex_unlock(&this->mutex);
243 }
244
245 /**
246 * Implements ca_info_t.list_certinfos
247 */
248 static void list_certinfos(private_ca_info_t *this, FILE *out, bool utc)
249 {
250 iterator_t *iterator;
251 certinfo_t *certinfo;
252 chunk_t authkey;
253
254 pthread_mutex_lock(&this->mutex);
255
256 authkey = this->cacert->get_subjectKeyID(this->cacert);
257 fprintf(out," authname: '%D'\n", this->cacert->get_subject(this->cacert));
258 fprintf(out," authkey: %#B\n", &authkey);
259
260 iterator = this->certinfos->create_iterator(this->certinfos, TRUE);
261 while (iterator->iterate(iterator, (void**)&certinfo))
262 {
263 time_t nextUpdate, thisUpdate, now;
264 chunk_t serial;
265
266 now = time(NULL);
267 nextUpdate = certinfo->get_nextUpdate(certinfo);
268 thisUpdate = certinfo->get_thisUpdate(certinfo);
269 serial = certinfo->get_serialNumber(certinfo);
270
271 fprintf(out, "%#T, until %#T, ", &thisUpdate, utc, &nextUpdate, utc);
272 if (now > nextUpdate)
273 {
274 fprintf(out, "expired (%V ago)\n", &now, &nextUpdate);
275 }
276 else
277 {
278 fprintf(out, "ok (expires in %V)\n", &now, &nextUpdate);
279 }
280 fprintf(out, " serial: %#B, %N\n", &serial,
281 cert_status_names, certinfo->get_status(certinfo));
282 }
283 iterator->destroy(iterator);
284
285 pthread_mutex_unlock(&this->mutex);
286 }
287
288 /**
289 * Find an exact copy of an identification in a linked list
290 */
291 static identification_t* find_identification(linked_list_t *list, identification_t *id)
292 {
293 identification_t *found_id = NULL, *current_id;
294
295 iterator_t *iterator = list->create_iterator(list, TRUE);
296
297 while (iterator->iterate(iterator, (void**)&current_id))
298 {
299 if (id->equals(id, current_id))
300 {
301 found_id = current_id;
302 break;
303 }
304 }
305 iterator->destroy(iterator);
306
307 return found_id;
308 }
309
310 /**
311 * Add a unique identification to a linked list
312 */
313 static identification_t *add_identification(linked_list_t *list, identification_t *id)
314 {
315 identification_t *found_id = find_identification(list, id);
316
317 if (found_id)
318 {
319 id->destroy(id);
320 return found_id;
321 }
322 else
323 {
324 list->insert_last(list, (void*)id);
325 return id;
326 }
327 }
328
329 /**
330 * Implements ca_info_t.add_crluri
331 */
332 static void add_crluri(private_ca_info_t *this, chunk_t uri)
333 {
334 if (uri.len < 6 ||
335 (strncasecmp(uri.ptr, "http", 4) != 0 &&
336 strncasecmp(uri.ptr, "ldap", 4) != 0 &&
337 strncasecmp(uri.ptr, "file", 4) != 0 &&
338 strncasecmp(uri.ptr, "ftp", 3) != 0))
339 {
340 DBG1(" invalid crl uri '%#B'", uri);
341 return;
342 }
343 else
344 {
345 identification_t *crluri = identification_create_from_encoding(ID_DER_ASN1_GN_URI, uri);
346
347 pthread_mutex_lock(&(this->mutex));
348 add_identification(this->crluris, crluri);
349 pthread_mutex_unlock(&(this->mutex));
350 }
351 }
352
353 /**
354 * Implements ca_info_t.add_ocspuri
355 */
356 static void add_ocspuri(private_ca_info_t *this, chunk_t uri)
357 {
358 if (uri.len < 7 || strncasecmp(uri.ptr, "http", 4) != 0)
359 {
360 DBG1(" invalid ocsp uri '%.*s'", uri.len, uri.ptr);
361 return;
362 }
363 else
364 {
365 identification_t *ocspuri = identification_create_from_encoding(ID_DER_ASN1_GN_URI, uri);
366
367 pthread_mutex_lock(&(this->mutex));
368 add_identification(this->ocspuris, ocspuri);
369 pthread_mutex_unlock(&(this->mutex));
370 }
371 }
372
373 /**
374 * Implements ca_info_t.add_info.
375 */
376 void add_info (private_ca_info_t *this, const private_ca_info_t *that)
377 {
378 pthread_mutex_lock(&(this->mutex));
379
380 if (this->name == NULL && that->name != NULL)
381 {
382 this->name = strdup(that->name);
383 }
384
385 pthread_mutex_unlock(&(this->mutex));
386
387 {
388 identification_t *uri;
389
390 iterator_t *iterator = that->crluris->create_iterator(that->crluris, TRUE);
391
392 while (iterator->iterate(iterator, (void**)&uri))
393 {
394 add_crluri(this, uri->get_encoding(uri));
395 }
396 iterator->destroy(iterator);
397 }
398
399 {
400 identification_t *uri;
401
402 iterator_t *iterator = that->ocspuris->create_iterator(that->ocspuris, TRUE);
403
404 while (iterator->iterate(iterator, (void**)&uri))
405 {
406 add_ocspuri(this, uri->get_encoding(uri));
407 }
408 iterator->destroy(iterator);
409 }
410 }
411
412 /**
413 * Implements ca_info_t.get_certificate.
414 */
415 static x509_t* get_certificate(private_ca_info_t* this)
416 {
417 return this->cacert;
418 }
419
420 /**
421 * caches a crl by saving it to a given crl directory
422 */
423 void cache_crl(private_ca_info_t* this, const char *crl_dir, crl_t *crl)
424 {
425 char buffer[BUF_LEN];
426 char *path;
427 char *pos = buffer;
428 int len = BUF_LEN;
429 int n;
430
431 chunk_t authKeyID = this->cacert->get_subjectKeyID(this->cacert);
432 chunk_t uri;
433
434 uri.ptr = buffer;
435 uri.len = 7 + strlen(crl_dir) + 1 + 2*authKeyID.len + 4;
436
437 if (uri.len >= BUF_LEN)
438 {
439 DBG1("file uri exceeds buffer length of %d bytes - crl not saved", BUF_LEN);
440 return;
441 }
442
443 /* print the file uri prefix */
444 n = snprintf(pos, len, "file://");
445 pos += n; len -= n;
446
447 /* remember the start of the path string */
448 path = pos;
449
450 /* print the default crl directory path */
451 n = snprintf(pos, len, "%s/", crl_dir);
452 pos += n; len -= n;
453
454 /* create and print a unique crl filename derived from the authKeyID */
455 while (authKeyID.len-- > 0)
456 {
457 n = snprintf(pos, len, "%02x", *authKeyID.ptr++);
458 pos += n; len -= n;
459 }
460
461 /* add the file suffix */
462 n = snprintf(pos, len, ".crl");
463
464 if (crl->write_to_file(crl, path, 0022, TRUE))
465 {
466 identification_t *crluri = identification_create_from_encoding(ID_DER_ASN1_GN_URI, uri);
467
468 add_identification(this->crluris, crluri);
469 }
470 }
471
472 /**
473 * Implements ca_info_t.verify_by_crl.
474 */
475 static cert_status_t verify_by_crl(private_ca_info_t* this, certinfo_t *certinfo,
476 const char *crl_dir)
477 {
478 rsa_public_key_t *issuer_public_key = this->cacert->get_public_key(this->cacert);
479 bool stale;
480
481 pthread_mutex_lock(&(this->mutex));
482 if (this->crl == NULL)
483 {
484 stale = TRUE;
485 DBG1("no crl is locally available");
486 }
487 else
488 {
489 stale = !this->crl->is_valid(this->crl);
490 DBG1("crl is %s", stale? "stale":"valid");
491 }
492
493 if (stale && crl_check_interval > 0)
494 {
495 iterator_t *iterator = this->crluris->create_iterator(this->crluris, TRUE);
496 identification_t *uri;
497
498 while (iterator->iterate(iterator, (void**)&uri))
499 {
500 fetcher_t *fetcher;
501 char uri_string[BUF_LEN];
502 chunk_t uri_chunk = uri->get_encoding(uri);
503 chunk_t response_chunk;
504
505 snprintf(uri_string, BUF_LEN, "%.*s", uri_chunk.len, uri_chunk.ptr);
506 fetcher = fetcher_create(uri_string);
507
508 response_chunk = fetcher->get(fetcher);
509 fetcher->destroy(fetcher);
510 if (response_chunk.ptr != NULL)
511 {
512 crl_t *crl = crl_create_from_chunk(response_chunk);
513
514 if (crl == NULL)
515 {
516 free(response_chunk.ptr);
517 continue;
518 }
519 if (!is_crl_issuer(this, crl))
520 {
521 DBG1(" fetched crl has wrong issuer");
522 crl->destroy(crl);
523 continue;
524 }
525 if (!crl->verify(crl, issuer_public_key))
526 {
527 DBG1("fetched crl signature is invalid");
528 crl->destroy(crl);
529 continue;
530 }
531 DBG2("fetched crl signature is valid");
532
533 if (this->crl == NULL)
534 {
535 this->crl = crl;
536 }
537 else if (crl->is_newer(crl, this->crl))
538 {
539 this->crl->destroy(this->crl);
540 this->crl = crl;
541 DBG1("this crl is newer - existing crl replaced");
542 }
543 else
544 {
545 crl->destroy(crl);
546 DBG1("this crl is not newer - existing crl retained");
547 continue;
548 }
549 if (crl->is_valid(crl))
550 {
551 if (cache_crls && strncasecmp(uri_string, "file", 4) != 0)
552 {
553 cache_crl(this, crl_dir, crl);
554 }
555 /* we found a valid crl and therefore exit the fetch loop */
556 break;
557 }
558 else
559 {
560 DBG1("fetched crl is stale");
561 }
562 }
563 }
564 iterator->destroy(iterator);
565 }
566
567 if (this->crl)
568 {
569 if (!this->crl->verify(this->crl, issuer_public_key))
570 {
571 DBG1("crl signature is invalid");
572 goto ret;
573 }
574 DBG2("crl signature is valid");
575
576 this->crl->get_status(this->crl, certinfo);
577 }
578
579 ret:
580 pthread_mutex_unlock(&(this->mutex));
581 return certinfo->get_status(certinfo);
582 }
583
584 /**
585 * Implements ca_info_t.verify_by_ocsp.
586 */
587 static cert_status_t verify_by_ocsp(private_ca_info_t* this,
588 certinfo_t *certinfo,
589 credential_store_t *credentials)
590 {
591 bool stale;
592 iterator_t *iterator;
593 certinfo_t *cached_certinfo = NULL;
594 int comparison = 1;
595
596 pthread_mutex_lock(&(this->mutex));
597
598 /* do we support OCSP at all? */
599 if (this->ocspuris->get_count(this->ocspuris) == 0)
600 {
601 goto ret;
602 }
603
604 iterator = this->certinfos->create_iterator(this->certinfos, TRUE);
605
606 /* find the list insertion point in alphabetical order */
607 while(iterator->iterate(iterator, (void**)&cached_certinfo))
608 {
609 comparison = certinfo->compare_serialNumber(certinfo, cached_certinfo);
610
611 if (comparison <= 0)
612 {
613 break;
614 }
615 }
616
617 /* do we have a valid certinfo_t for this serial number in our cache? */
618 if (comparison == 0)
619 {
620 stale = cached_certinfo->get_nextUpdate(cached_certinfo) < time(NULL);
621 DBG1("ocsp status in cache is %s", stale ? "stale":"fresh");
622 }
623 else
624 {
625 stale = TRUE;
626 DBG1("ocsp status is not in cache");
627 }
628
629 if (stale)
630 {
631 ocsp_t *ocsp;
632
633 ocsp = ocsp_create(this->cacert, this->ocspuris);
634 ocsp->fetch(ocsp, certinfo, credentials);
635 if (certinfo->get_status(certinfo) != CERT_UNDEFINED)
636 {
637 if (comparison != 0)
638 {
639 cached_certinfo = certinfo_create(certinfo->get_serialNumber(certinfo));
640
641 if (comparison > 0)
642 {
643 iterator->insert_after(iterator, (void *)cached_certinfo);
644 }
645 else
646 {
647 iterator->insert_before(iterator, (void *)cached_certinfo);
648 }
649 }
650 cached_certinfo->update(cached_certinfo, certinfo);
651 }
652 ocsp->destroy(ocsp);
653 }
654 else
655 {
656 certinfo->update(certinfo, cached_certinfo);
657 }
658
659 iterator->destroy(iterator);
660
661 ret:
662 pthread_mutex_unlock(&(this->mutex));
663 return certinfo->get_status(certinfo);
664 }
665
666 /**
667 * Implements ca_info_t.purge_ocsp
668 */
669 static void purge_ocsp(private_ca_info_t *this)
670 {
671 pthread_mutex_lock(&(this->mutex));
672
673 this->certinfos->destroy_offset(this->certinfos,
674 offsetof(certinfo_t, destroy));
675 this->certinfos = linked_list_create();
676
677 pthread_mutex_unlock(&(this->mutex));
678 }
679
680 /**
681 * Implements ca_info_t.destroy
682 */
683 static void destroy(private_ca_info_t *this)
684 {
685 this->attrcerts->destroy_offset(this->attrcerts,
686 offsetof(x509ac_t, destroy));
687 this->crluris->destroy_offset(this->crluris,
688 offsetof(identification_t, destroy));
689 this->ocspuris->destroy_offset(this->ocspuris,
690 offsetof(identification_t, destroy));
691 this->certinfos->destroy_offset(this->certinfos,
692 offsetof(certinfo_t, destroy));
693 DESTROY_IF(this->crl);
694 free(this->name);
695 free(this);
696 }
697
698 /**
699 * list the info of this CA
700 */
701 static void list(private_ca_info_t* this, FILE* out, bool utc)
702 {
703 chunk_t chunk;
704 identification_t *uri;
705 iterator_t *iterator;
706 bool first;
707
708 pthread_mutex_lock(&(this->mutex));
709 fprintf(out, "%#T", &this->installed, utc);
710
711 if (this->name)
712 {
713 fprintf(out, ", \"%s\"\n", this->name);
714 }
715 else
716 {
717 fprintf(out, "\n");
718 }
719
720 fprintf(out, " authname: '%D'\n", this->cacert->get_subject(this->cacert));
721 chunk = this->cacert->get_subjectKeyID(this->cacert);
722 fprintf(out, " authkey: %#B\n", &chunk);
723 chunk = this->cacert->get_keyid(this->cacert);
724 fprintf(out, " keyid: %#B\n", &chunk);
725
726 first = TRUE;
727 iterator = this->crluris->create_iterator(this->crluris, TRUE);
728 while (iterator->iterate(iterator, (void**)&uri))
729 {
730 fprintf(out, " %s '%D'\n", first ? "crluris:":" ", uri);
731 first = FALSE;
732 }
733 iterator->destroy(iterator);
734
735 first = TRUE;
736 iterator = this->ocspuris->create_iterator(this->ocspuris, TRUE);
737 while (iterator->iterate(iterator, (void**)&uri))
738 {
739 fprintf(out, " %s '%D'\n", first ? "ocspuris:":" ", uri);
740 first = FALSE;
741 }
742 iterator->destroy(iterator);
743 pthread_mutex_unlock(&(this->mutex));
744 }
745
746 /*
747 * Described in header.
748 */
749 void ca_info_set_options(strict_t strict, bool cache, u_int interval)
750 {
751 strict_crl_policy = strict;
752 cache_crls = cache;
753 crl_check_interval = interval;
754 }
755
756 /*
757 * Described in header.
758 */
759 ca_info_t *ca_info_create(const char *name, x509_t *cacert)
760 {
761 private_ca_info_t *this = malloc_thing(private_ca_info_t);
762
763 /* initialize */
764 this->installed = time(NULL);
765 this->name = (name == NULL)? NULL:strdup(name);
766 this->cacert = cacert;
767 this->attrcerts = linked_list_create();
768 this->crluris = linked_list_create();
769 this->ocspuris = linked_list_create();
770 this->certinfos = linked_list_create();
771 this->crl = NULL;
772
773 /* initialize the mutex */
774 pthread_mutex_init(&(this->mutex), NULL);
775
776 /* public functions */
777 this->public.equals = (bool (*) (const ca_info_t*,const ca_info_t*))equals;
778 this->public.equals_name_release_info = (bool (*) (ca_info_t*,const char*))equals_name_release_info;
779 this->public.is_cert_issuer = (bool (*) (ca_info_t*,const x509_t*))is_cert_issuer;
780 this->public.is_crl_issuer = (bool (*) (ca_info_t*,const crl_t*))is_crl_issuer;
781 this->public.is_strict = (bool (*) (ca_info_t*))is_strict;
782 this->public.add_info = (void (*) (ca_info_t*,const ca_info_t*))add_info;
783 this->public.add_crl = (void (*) (ca_info_t*,crl_t*))add_crl;
784 this->public.has_crl = (bool (*) (ca_info_t*))has_crl;
785 this->public.has_certinfos = (bool (*) (ca_info_t*))has_certinfos;
786 this->public.list = (void (*) (ca_info_t*,FILE*,bool))list;
787 this->public.list_crl = (void (*) (ca_info_t*,FILE*,bool))list_crl;
788 this->public.list_certinfos = (void (*) (ca_info_t*,FILE*,bool))list_certinfos;
789 this->public.add_crluri = (void (*) (ca_info_t*,chunk_t))add_crluri;
790 this->public.add_ocspuri = (void (*) (ca_info_t*,chunk_t))add_ocspuri;
791 this->public.get_certificate = (x509_t* (*) (ca_info_t*))get_certificate;
792 this->public.verify_by_crl = (cert_status_t (*) (ca_info_t*,certinfo_t*, const char*))verify_by_crl;
793 this->public.verify_by_ocsp = (cert_status_t (*) (ca_info_t*,certinfo_t*,credential_store_t*))verify_by_ocsp;
794 this->public.purge_ocsp = (void (*) (ca_info_t*))purge_ocsp;
795 this->public.destroy = (void (*) (ca_info_t*))destroy;
796
797 return &this->public;
798 }