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