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