0432ee573e2584ebd879efd57dd897b0ebe1b5c2
[strongswan.git] / src / libcharon / plugins / stroke / stroke_ca.c
1 /*
2 * Copyright (C) 2008-2015 Tobias Brunner
3 * Copyright (C) 2008 Martin Willi
4 * HSR Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #include "stroke_ca.h"
18 #include "stroke_cred.h"
19
20 #include <threading/rwlock.h>
21 #include <collections/linked_list.h>
22 #include <crypto/hashers/hasher.h>
23
24 #include <daemon.h>
25
26 typedef struct private_stroke_ca_t private_stroke_ca_t;
27 typedef struct ca_section_t ca_section_t;
28 typedef struct ca_cert_t ca_cert_t;
29
30 /**
31 * Provided by stroke_cred.c
32 */
33 certificate_t *stroke_load_ca_cert(char *filename);
34
35 /**
36 * private data of stroke_ca
37 */
38 struct private_stroke_ca_t {
39
40 /**
41 * public functions
42 */
43 stroke_ca_t public;
44
45 /**
46 * read-write lock to lists
47 */
48 rwlock_t *lock;
49
50 /**
51 * list of CA sections and their certificates (ca_section_t)
52 */
53 linked_list_t *sections;
54
55 /**
56 * list of all loaded CA certificates (ca_cert_t)
57 */
58 linked_list_t *certs;
59 };
60
61
62 /**
63 * loaded ipsec.conf CA sections
64 */
65 struct ca_section_t {
66
67 /**
68 * name of the CA section
69 */
70 char *name;
71
72 /**
73 * path/name of the certificate
74 */
75 char *path;
76
77 /**
78 * reference to cert
79 */
80 certificate_t *cert;
81
82 /**
83 * CRL URIs
84 */
85 linked_list_t *crl;
86
87 /**
88 * OCSP URIs
89 */
90 linked_list_t *ocsp;
91
92 /**
93 * Hashes of certificates issued by this CA
94 */
95 linked_list_t *hashes;
96
97 /**
98 * Base URI used for certificates from this CA
99 */
100 char *certuribase;
101 };
102
103 /**
104 * loaded CA certificate
105 */
106 struct ca_cert_t {
107
108 /**
109 * reference to cert
110 */
111 certificate_t *cert;
112
113 /**
114 * The number of CA sections referring to this certificate
115 */
116 u_int count;
117
118 /**
119 * TRUE if this certificate was automatically loaded
120 */
121 bool automatic;
122 };
123
124 /**
125 * create a new CA section
126 */
127 static ca_section_t *ca_section_create(char *name, char *path)
128 {
129 ca_section_t *ca = malloc_thing(ca_section_t);
130
131 ca->name = strdup(name);
132 ca->path = strdup(path);
133 ca->crl = linked_list_create();
134 ca->ocsp = linked_list_create();
135 ca->hashes = linked_list_create();
136 ca->certuribase = NULL;
137 return ca;
138 }
139
140 /**
141 * destroy a ca section entry
142 */
143 static void ca_section_destroy(ca_section_t *this)
144 {
145 this->crl->destroy_function(this->crl, free);
146 this->ocsp->destroy_function(this->ocsp, free);
147 this->hashes->destroy_offset(this->hashes, offsetof(identification_t, destroy));
148 this->cert->destroy(this->cert);
149 free(this->certuribase);
150 free(this->path);
151 free(this->name);
152 free(this);
153 }
154
155 /**
156 * Destroy a ca cert entry
157 */
158 static void ca_cert_destroy(ca_cert_t *this)
159 {
160 this->cert->destroy(this->cert);
161 free(this);
162 }
163
164 /**
165 * Data for the certificate enumerator
166 */
167 typedef struct {
168 private_stroke_ca_t *this;
169 certificate_type_t cert;
170 key_type_t key;
171 identification_t *id;
172 } cert_data_t;
173
174 CALLBACK(cert_data_destroy, void,
175 cert_data_t *data)
176 {
177 data->this->lock->unlock(data->this->lock);
178 free(data);
179 }
180
181 CALLBACK(certs_filter, bool,
182 cert_data_t *data, enumerator_t *orig, va_list args)
183 {
184 ca_cert_t *cacert;
185 public_key_t *public;
186 certificate_t **out;
187
188 VA_ARGS_VGET(args, out);
189
190 while (orig->enumerate(orig, &cacert))
191 {
192 certificate_t *cert = cacert->cert;
193
194 if (data->cert != CERT_ANY && data->cert != cert->get_type(cert))
195 {
196 continue;
197 }
198 public = cert->get_public_key(cert);
199 if (public)
200 {
201 if (data->key == KEY_ANY || data->key == public->get_type(public))
202 {
203 if (data->id && public->has_fingerprint(public,
204 data->id->get_encoding(data->id)))
205 {
206 public->destroy(public);
207 *out = cert;
208 return TRUE;
209 }
210 }
211 public->destroy(public);
212 }
213 else if (data->key != KEY_ANY)
214 {
215 continue;
216 }
217 if (!data->id || cert->has_subject(cert, data->id))
218 {
219 *out = cert;
220 return TRUE;
221 }
222 }
223 return FALSE;
224 }
225
226 METHOD(credential_set_t, create_cert_enumerator, enumerator_t*,
227 private_stroke_ca_t *this, certificate_type_t cert, key_type_t key,
228 identification_t *id, bool trusted)
229 {
230 enumerator_t *enumerator;
231 cert_data_t *data;
232
233 INIT(data,
234 .this = this,
235 .cert = cert,
236 .key = key,
237 .id = id,
238 );
239
240 this->lock->read_lock(this->lock);
241 enumerator = this->certs->create_enumerator(this->certs);
242 return enumerator_create_filter(enumerator, certs_filter, data,
243 cert_data_destroy);
244 }
245
246 /**
247 * data to pass to create_inner_cdp
248 */
249 typedef struct {
250 private_stroke_ca_t *this;
251 certificate_type_t type;
252 identification_t *id;
253 } cdp_data_t;
254
255 /**
256 * destroy cdp enumerator data and unlock list
257 */
258 static void cdp_data_destroy(cdp_data_t *data)
259 {
260 data->this->lock->unlock(data->this->lock);
261 free(data);
262 }
263
264 /**
265 * inner enumerator constructor for CDP URIs
266 */
267 static enumerator_t *create_inner_cdp(ca_section_t *section, cdp_data_t *data)
268 {
269 public_key_t *public;
270 enumerator_t *enumerator = NULL;
271 linked_list_t *list;
272
273 if (data->type == CERT_X509_OCSP_RESPONSE)
274 {
275 list = section->ocsp;
276 }
277 else
278 {
279 list = section->crl;
280 }
281
282 public = section->cert->get_public_key(section->cert);
283 if (public)
284 {
285 if (!data->id)
286 {
287 enumerator = list->create_enumerator(list);
288 }
289 else
290 {
291 if (public->has_fingerprint(public, data->id->get_encoding(data->id)))
292 {
293 enumerator = list->create_enumerator(list);
294 }
295 }
296 public->destroy(public);
297 }
298 return enumerator;
299 }
300
301 /**
302 * inner enumerator constructor for "Hash and URL"
303 */
304 static enumerator_t *create_inner_cdp_hashandurl(ca_section_t *section, cdp_data_t *data)
305 {
306 enumerator_t *enumerator = NULL, *hash_enum;
307 identification_t *current;
308
309 if (!data->id || !section->certuribase)
310 {
311 return NULL;
312 }
313
314 hash_enum = section->hashes->create_enumerator(section->hashes);
315 while (hash_enum->enumerate(hash_enum, &current))
316 {
317 if (current->matches(current, data->id))
318 {
319 char *url, *hash;
320
321 url = malloc(strlen(section->certuribase) + 40 + 1);
322 strcpy(url, section->certuribase);
323 hash = chunk_to_hex(current->get_encoding(current), NULL, FALSE).ptr;
324 strncat(url, hash, 40);
325 free(hash);
326
327 enumerator = enumerator_create_single(url, free);
328 break;
329 }
330 }
331 hash_enum->destroy(hash_enum);
332 return enumerator;
333 }
334
335 METHOD(credential_set_t, create_cdp_enumerator, enumerator_t*,
336 private_stroke_ca_t *this, certificate_type_t type, identification_t *id)
337 {
338 cdp_data_t *data;
339
340 switch (type)
341 { /* we serve CRLs, OCSP responders and URLs for "Hash and URL" */
342 case CERT_X509:
343 case CERT_X509_CRL:
344 case CERT_X509_OCSP_RESPONSE:
345 case CERT_ANY:
346 break;
347 default:
348 return NULL;
349 }
350 data = malloc_thing(cdp_data_t);
351 data->this = this;
352 data->type = type;
353 data->id = id;
354
355 this->lock->read_lock(this->lock);
356 return enumerator_create_nested(this->sections->create_enumerator(this->sections),
357 (type == CERT_X509) ? (void*)create_inner_cdp_hashandurl : (void*)create_inner_cdp,
358 data, (void*)cdp_data_destroy);
359 }
360
361 CALLBACK(match_cert, bool,
362 ca_cert_t *item, va_list args)
363 {
364 certificate_t *cert;
365
366 VA_ARGS_VGET(args, cert);
367 return cert->equals(cert, item->cert);
368 }
369
370 /**
371 * Match automatically added certificates and remove/destroy them if they are
372 * not referenced by CA sections.
373 */
374 static bool remove_auto_certs(ca_cert_t *item, void *not_used)
375 {
376 if (item->automatic)
377 {
378 item->automatic = FALSE;
379 if (!item->count)
380 {
381 ca_cert_destroy(item);
382 return TRUE;
383 }
384 }
385 return FALSE;
386 }
387
388 /**
389 * Find the given certificate that was referenced by a section and remove it
390 * unless it was also loaded automatically or is used by other CA sections.
391 */
392 static bool remove_cert(ca_cert_t *item, certificate_t *cert)
393 {
394 if (item->count && cert->equals(cert, item->cert))
395 {
396 if (--item->count == 0 && !item->automatic)
397 {
398 ca_cert_destroy(item);
399 return TRUE;
400 }
401 }
402 return FALSE;
403 }
404
405 /**
406 * Adds a certificate to the certificate store
407 */
408 static certificate_t *add_cert_internal(private_stroke_ca_t *this,
409 certificate_t *cert, bool automatic)
410 {
411 ca_cert_t *found;
412
413 if (this->certs->find_first(this->certs, match_cert, (void**)&found, cert))
414 {
415 cert->destroy(cert);
416 cert = found->cert->get_ref(found->cert);
417 }
418 else
419 {
420 INIT(found,
421 .cert = cert->get_ref(cert)
422 );
423 this->certs->insert_first(this->certs, found);
424 }
425 if (automatic)
426 {
427 found->automatic = TRUE;
428 }
429 else
430 {
431 found->count++;
432 }
433 return cert;
434 }
435
436 METHOD(stroke_ca_t, add, void,
437 private_stroke_ca_t *this, stroke_msg_t *msg)
438 {
439 certificate_t *cert;
440 ca_section_t *ca;
441
442 if (msg->add_ca.cacert == NULL)
443 {
444 DBG1(DBG_CFG, "missing cacert parameter");
445 return;
446 }
447 cert = stroke_load_ca_cert(msg->add_ca.cacert);
448 if (cert)
449 {
450 ca = ca_section_create(msg->add_ca.name, msg->add_ca.cacert);
451 if (msg->add_ca.crluri)
452 {
453 ca->crl->insert_last(ca->crl, strdup(msg->add_ca.crluri));
454 }
455 if (msg->add_ca.crluri2)
456 {
457 ca->crl->insert_last(ca->crl, strdup(msg->add_ca.crluri2));
458 }
459 if (msg->add_ca.ocspuri)
460 {
461 ca->ocsp->insert_last(ca->ocsp, strdup(msg->add_ca.ocspuri));
462 }
463 if (msg->add_ca.ocspuri2)
464 {
465 ca->ocsp->insert_last(ca->ocsp, strdup(msg->add_ca.ocspuri2));
466 }
467 if (msg->add_ca.certuribase)
468 {
469 ca->certuribase = strdup(msg->add_ca.certuribase);
470 }
471 this->lock->write_lock(this->lock);
472 ca->cert = add_cert_internal(this, cert, FALSE);
473 this->sections->insert_last(this->sections, ca);
474 this->lock->unlock(this->lock);
475 DBG1(DBG_CFG, "added ca '%s'", msg->add_ca.name);
476 }
477 }
478
479 METHOD(stroke_ca_t, del, void,
480 private_stroke_ca_t *this, stroke_msg_t *msg)
481 {
482 enumerator_t *enumerator;
483 ca_section_t *ca = NULL;
484
485 this->lock->write_lock(this->lock);
486 enumerator = this->sections->create_enumerator(this->sections);
487 while (enumerator->enumerate(enumerator, &ca))
488 {
489 if (streq(ca->name, msg->del_ca.name))
490 {
491 this->sections->remove_at(this->sections, enumerator);
492 break;
493 }
494 ca = NULL;
495 }
496 enumerator->destroy(enumerator);
497 if (ca)
498 {
499 this->certs->remove(this->certs, ca->cert, (void*)remove_cert);
500 }
501 this->lock->unlock(this->lock);
502 if (!ca)
503 {
504 DBG1(DBG_CFG, "no ca named '%s' found\n", msg->del_ca.name);
505 return;
506 }
507 ca_section_destroy(ca);
508
509 lib->credmgr->flush_cache(lib->credmgr, CERT_ANY);
510 }
511
512 METHOD(stroke_ca_t, get_cert_ref, certificate_t*,
513 private_stroke_ca_t *this, certificate_t *cert)
514 {
515 ca_cert_t *found;
516
517 this->lock->read_lock(this->lock);
518 if (this->certs->find_first(this->certs, match_cert, (void**)&found, cert))
519 {
520 cert->destroy(cert);
521 cert = found->cert->get_ref(found->cert);
522 }
523 this->lock->unlock(this->lock);
524 return cert;
525 }
526
527 METHOD(stroke_ca_t, reload_certs, void,
528 private_stroke_ca_t *this)
529 {
530 enumerator_t *enumerator;
531 certificate_t *cert;
532 ca_section_t *ca;
533 certificate_type_t type = CERT_X509;
534
535 /* holding the write lock while loading/parsing certificates is not optimal,
536 * however, there usually are not that many ca sections configured */
537 this->lock->write_lock(this->lock);
538 if (this->sections->get_count(this->sections))
539 {
540 DBG1(DBG_CFG, "rereading ca certificates in ca sections");
541 }
542 enumerator = this->sections->create_enumerator(this->sections);
543 while (enumerator->enumerate(enumerator, &ca))
544 {
545 cert = stroke_load_ca_cert(ca->path);
546 if (cert)
547 {
548 if (cert->equals(cert, ca->cert))
549 {
550 cert->destroy(cert);
551 }
552 else
553 {
554 this->certs->remove(this->certs, ca->cert, (void*)remove_cert);
555 ca->cert->destroy(ca->cert);
556 ca->cert = add_cert_internal(this, cert, FALSE);
557 }
558 }
559 else
560 {
561 DBG1(DBG_CFG, "failed to reload certificate '%s', removing ca '%s'",
562 ca->path, ca->name);
563 this->sections->remove_at(this->sections, enumerator);
564 this->certs->remove(this->certs, ca->cert, (void*)remove_cert);
565 ca_section_destroy(ca);
566 type = CERT_ANY;
567 }
568 }
569 enumerator->destroy(enumerator);
570 this->lock->unlock(this->lock);
571 lib->credmgr->flush_cache(lib->credmgr, type);
572 }
573
574 METHOD(stroke_ca_t, replace_certs, void,
575 private_stroke_ca_t *this, mem_cred_t *certs)
576 {
577 enumerator_t *enumerator;
578 certificate_t *cert;
579
580 enumerator = certs->set.create_cert_enumerator(&certs->set, CERT_X509,
581 KEY_ANY, NULL, TRUE);
582 this->lock->write_lock(this->lock);
583 this->certs->remove(this->certs, NULL, (void*)remove_auto_certs);
584 while (enumerator->enumerate(enumerator, &cert))
585 {
586 cert = add_cert_internal(this, cert->get_ref(cert), TRUE);
587 cert->destroy(cert);
588 }
589 this->lock->unlock(this->lock);
590 enumerator->destroy(enumerator);
591 lib->credmgr->flush_cache(lib->credmgr, CERT_X509);
592 }
593 /**
594 * list crl or ocsp URIs
595 */
596 static void list_uris(linked_list_t *list, char *label, FILE *out)
597 {
598 bool first = TRUE;
599 char *uri;
600 enumerator_t *enumerator;
601
602 enumerator = list->create_enumerator(list);
603 while (enumerator->enumerate(enumerator, (void**)&uri))
604 {
605 if (first)
606 {
607 fprintf(out, "%s", label);
608 first = FALSE;
609 }
610 else
611 {
612 fprintf(out, " ");
613 }
614 fprintf(out, "'%s'\n", uri);
615 }
616 enumerator->destroy(enumerator);
617 }
618
619 METHOD(stroke_ca_t, check_for_hash_and_url, void,
620 private_stroke_ca_t *this, certificate_t* cert)
621 {
622 ca_section_t *section;
623 enumerator_t *enumerator;
624
625 hasher_t *hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
626 if (hasher == NULL)
627 {
628 DBG1(DBG_IKE, "unable to use hash-and-url: sha1 not supported");
629 return;
630 }
631
632 this->lock->write_lock(this->lock);
633 enumerator = this->sections->create_enumerator(this->sections);
634 while (enumerator->enumerate(enumerator, (void**)&section))
635 {
636 if (section->certuribase && cert->issued_by(cert, section->cert, NULL))
637 {
638 chunk_t hash, encoded;
639
640 if (cert->get_encoding(cert, CERT_ASN1_DER, &encoded))
641 {
642 if (hasher->allocate_hash(hasher, encoded, &hash))
643 {
644 section->hashes->insert_last(section->hashes,
645 identification_create_from_encoding(ID_KEY_ID, hash));
646 chunk_free(&hash);
647 }
648 chunk_free(&encoded);
649 }
650 break;
651 }
652 }
653 enumerator->destroy(enumerator);
654 this->lock->unlock(this->lock);
655
656 hasher->destroy(hasher);
657 }
658
659 METHOD(stroke_ca_t, list, void,
660 private_stroke_ca_t *this, stroke_msg_t *msg, FILE *out)
661 {
662 bool first = TRUE;
663 ca_section_t *section;
664 enumerator_t *enumerator;
665
666 this->lock->read_lock(this->lock);
667 enumerator = this->sections->create_enumerator(this->sections);
668 while (enumerator->enumerate(enumerator, (void**)&section))
669 {
670 certificate_t *cert = section->cert;
671 public_key_t *public = cert->get_public_key(cert);
672 chunk_t chunk;
673
674 if (first)
675 {
676 fprintf(out, "\n");
677 fprintf(out, "List of CA Information Sections:\n");
678 first = FALSE;
679 }
680 fprintf(out, "\n");
681 fprintf(out, " authname: \"%Y\"\n", cert->get_subject(cert));
682
683 /* list authkey and keyid */
684 if (public)
685 {
686 if (public->get_fingerprint(public, KEYID_PUBKEY_SHA1, &chunk))
687 {
688 fprintf(out, " authkey: %#B\n", &chunk);
689 }
690 if (public->get_fingerprint(public, KEYID_PUBKEY_INFO_SHA1, &chunk))
691 {
692 fprintf(out, " keyid: %#B\n", &chunk);
693 }
694 public->destroy(public);
695 }
696 list_uris(section->crl, " crluris: ", out);
697 list_uris(section->ocsp, " ocspuris: ", out);
698 if (section->certuribase)
699 {
700 fprintf(out, " certuribase: '%s'\n", section->certuribase);
701 }
702 }
703 enumerator->destroy(enumerator);
704 this->lock->unlock(this->lock);
705 }
706
707 METHOD(stroke_ca_t, destroy, void,
708 private_stroke_ca_t *this)
709 {
710 this->sections->destroy_function(this->sections, (void*)ca_section_destroy);
711 this->certs->destroy_function(this->certs, (void*)ca_cert_destroy);
712 this->lock->destroy(this->lock);
713 free(this);
714 }
715
716 /*
717 * see header file
718 */
719 stroke_ca_t *stroke_ca_create()
720 {
721 private_stroke_ca_t *this;
722
723 INIT(this,
724 .public = {
725 .set = {
726 .create_private_enumerator = (void*)return_null,
727 .create_cert_enumerator = _create_cert_enumerator,
728 .create_shared_enumerator = (void*)return_null,
729 .create_cdp_enumerator = _create_cdp_enumerator,
730 .cache_cert = (void*)nop,
731 },
732 .add = _add,
733 .del = _del,
734 .list = _list,
735 .get_cert_ref = _get_cert_ref,
736 .reload_certs = _reload_certs,
737 .replace_certs = _replace_certs,
738 .check_for_hash_and_url = _check_for_hash_and_url,
739 .destroy = _destroy,
740 },
741 .sections = linked_list_create(),
742 .certs = linked_list_create(),
743 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
744 );
745
746 return &this->public;
747 }