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