Avoid enumerating certificates with non-matching key type
[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 else
212 {
213 public->destroy(public);
214 continue;
215 }
216 public->destroy(public);
217 }
218 else if (data->key != KEY_ANY)
219 {
220 continue;
221 }
222 if (!data->id || cert->has_subject(cert, data->id))
223 {
224 *out = cert;
225 return TRUE;
226 }
227 }
228 return FALSE;
229 }
230
231 METHOD(credential_set_t, create_cert_enumerator, enumerator_t*,
232 private_stroke_ca_t *this, certificate_type_t cert, key_type_t key,
233 identification_t *id, bool trusted)
234 {
235 enumerator_t *enumerator;
236 cert_data_t *data;
237
238 INIT(data,
239 .this = this,
240 .cert = cert,
241 .key = key,
242 .id = id,
243 );
244
245 this->lock->read_lock(this->lock);
246 enumerator = this->certs->create_enumerator(this->certs);
247 return enumerator_create_filter(enumerator, certs_filter, data,
248 cert_data_destroy);
249 }
250
251 /**
252 * data to pass to create_inner_cdp
253 */
254 typedef struct {
255 private_stroke_ca_t *this;
256 certificate_type_t type;
257 identification_t *id;
258 } cdp_data_t;
259
260 /**
261 * destroy cdp enumerator data and unlock list
262 */
263 static void cdp_data_destroy(cdp_data_t *data)
264 {
265 data->this->lock->unlock(data->this->lock);
266 free(data);
267 }
268
269 /**
270 * inner enumerator constructor for CDP URIs
271 */
272 static enumerator_t *create_inner_cdp(ca_section_t *section, cdp_data_t *data)
273 {
274 public_key_t *public;
275 enumerator_t *enumerator = NULL;
276 linked_list_t *list;
277
278 if (data->type == CERT_X509_OCSP_RESPONSE)
279 {
280 list = section->ocsp;
281 }
282 else
283 {
284 list = section->crl;
285 }
286
287 public = section->cert->get_public_key(section->cert);
288 if (public)
289 {
290 if (!data->id)
291 {
292 enumerator = list->create_enumerator(list);
293 }
294 else
295 {
296 if (public->has_fingerprint(public, data->id->get_encoding(data->id)))
297 {
298 enumerator = list->create_enumerator(list);
299 }
300 }
301 public->destroy(public);
302 }
303 return enumerator;
304 }
305
306 /**
307 * inner enumerator constructor for "Hash and URL"
308 */
309 static enumerator_t *create_inner_cdp_hashandurl(ca_section_t *section, cdp_data_t *data)
310 {
311 enumerator_t *enumerator = NULL, *hash_enum;
312 identification_t *current;
313
314 if (!data->id || !section->certuribase)
315 {
316 return NULL;
317 }
318
319 hash_enum = section->hashes->create_enumerator(section->hashes);
320 while (hash_enum->enumerate(hash_enum, &current))
321 {
322 if (current->matches(current, data->id))
323 {
324 char *url, *hash;
325
326 url = malloc(strlen(section->certuribase) + 40 + 1);
327 strcpy(url, section->certuribase);
328 hash = chunk_to_hex(current->get_encoding(current), NULL, FALSE).ptr;
329 strncat(url, hash, 40);
330 free(hash);
331
332 enumerator = enumerator_create_single(url, free);
333 break;
334 }
335 }
336 hash_enum->destroy(hash_enum);
337 return enumerator;
338 }
339
340 METHOD(credential_set_t, create_cdp_enumerator, enumerator_t*,
341 private_stroke_ca_t *this, certificate_type_t type, identification_t *id)
342 {
343 cdp_data_t *data;
344
345 switch (type)
346 { /* we serve CRLs, OCSP responders and URLs for "Hash and URL" */
347 case CERT_X509:
348 case CERT_X509_CRL:
349 case CERT_X509_OCSP_RESPONSE:
350 case CERT_ANY:
351 break;
352 default:
353 return NULL;
354 }
355 data = malloc_thing(cdp_data_t);
356 data->this = this;
357 data->type = type;
358 data->id = id;
359
360 this->lock->read_lock(this->lock);
361 return enumerator_create_nested(this->sections->create_enumerator(this->sections),
362 (type == CERT_X509) ? (void*)create_inner_cdp_hashandurl : (void*)create_inner_cdp,
363 data, (void*)cdp_data_destroy);
364 }
365
366 CALLBACK(match_cert, bool,
367 ca_cert_t *item, va_list args)
368 {
369 certificate_t *cert;
370
371 VA_ARGS_VGET(args, cert);
372 return cert->equals(cert, item->cert);
373 }
374
375 /**
376 * Match automatically added certificates and remove/destroy them if they are
377 * not referenced by CA sections.
378 */
379 static bool remove_auto_certs(ca_cert_t *item, void *not_used)
380 {
381 if (item->automatic)
382 {
383 item->automatic = FALSE;
384 if (!item->count)
385 {
386 ca_cert_destroy(item);
387 return TRUE;
388 }
389 }
390 return FALSE;
391 }
392
393 /**
394 * Find the given certificate that was referenced by a section and remove it
395 * unless it was also loaded automatically or is used by other CA sections.
396 */
397 static bool remove_cert(ca_cert_t *item, certificate_t *cert)
398 {
399 if (item->count && cert->equals(cert, item->cert))
400 {
401 if (--item->count == 0 && !item->automatic)
402 {
403 ca_cert_destroy(item);
404 return TRUE;
405 }
406 }
407 return FALSE;
408 }
409
410 /**
411 * Adds a certificate to the certificate store
412 */
413 static certificate_t *add_cert_internal(private_stroke_ca_t *this,
414 certificate_t *cert, bool automatic)
415 {
416 ca_cert_t *found;
417
418 if (this->certs->find_first(this->certs, match_cert, (void**)&found, cert))
419 {
420 cert->destroy(cert);
421 cert = found->cert->get_ref(found->cert);
422 }
423 else
424 {
425 INIT(found,
426 .cert = cert->get_ref(cert)
427 );
428 this->certs->insert_first(this->certs, found);
429 }
430 if (automatic)
431 {
432 found->automatic = TRUE;
433 }
434 else
435 {
436 found->count++;
437 }
438 return cert;
439 }
440
441 METHOD(stroke_ca_t, add, void,
442 private_stroke_ca_t *this, stroke_msg_t *msg)
443 {
444 certificate_t *cert;
445 ca_section_t *ca;
446
447 if (msg->add_ca.cacert == NULL)
448 {
449 DBG1(DBG_CFG, "missing cacert parameter");
450 return;
451 }
452 cert = stroke_load_ca_cert(msg->add_ca.cacert);
453 if (cert)
454 {
455 ca = ca_section_create(msg->add_ca.name, msg->add_ca.cacert);
456 if (msg->add_ca.crluri)
457 {
458 ca->crl->insert_last(ca->crl, strdup(msg->add_ca.crluri));
459 }
460 if (msg->add_ca.crluri2)
461 {
462 ca->crl->insert_last(ca->crl, strdup(msg->add_ca.crluri2));
463 }
464 if (msg->add_ca.ocspuri)
465 {
466 ca->ocsp->insert_last(ca->ocsp, strdup(msg->add_ca.ocspuri));
467 }
468 if (msg->add_ca.ocspuri2)
469 {
470 ca->ocsp->insert_last(ca->ocsp, strdup(msg->add_ca.ocspuri2));
471 }
472 if (msg->add_ca.certuribase)
473 {
474 ca->certuribase = strdup(msg->add_ca.certuribase);
475 }
476 this->lock->write_lock(this->lock);
477 ca->cert = add_cert_internal(this, cert, FALSE);
478 this->sections->insert_last(this->sections, ca);
479 this->lock->unlock(this->lock);
480 DBG1(DBG_CFG, "added ca '%s'", msg->add_ca.name);
481 }
482 }
483
484 METHOD(stroke_ca_t, del, void,
485 private_stroke_ca_t *this, stroke_msg_t *msg)
486 {
487 enumerator_t *enumerator;
488 ca_section_t *ca = NULL;
489
490 this->lock->write_lock(this->lock);
491 enumerator = this->sections->create_enumerator(this->sections);
492 while (enumerator->enumerate(enumerator, &ca))
493 {
494 if (streq(ca->name, msg->del_ca.name))
495 {
496 this->sections->remove_at(this->sections, enumerator);
497 break;
498 }
499 ca = NULL;
500 }
501 enumerator->destroy(enumerator);
502 if (ca)
503 {
504 this->certs->remove(this->certs, ca->cert, (void*)remove_cert);
505 }
506 this->lock->unlock(this->lock);
507 if (!ca)
508 {
509 DBG1(DBG_CFG, "no ca named '%s' found\n", msg->del_ca.name);
510 return;
511 }
512 ca_section_destroy(ca);
513
514 lib->credmgr->flush_cache(lib->credmgr, CERT_ANY);
515 }
516
517 METHOD(stroke_ca_t, get_cert_ref, certificate_t*,
518 private_stroke_ca_t *this, certificate_t *cert)
519 {
520 ca_cert_t *found;
521
522 this->lock->read_lock(this->lock);
523 if (this->certs->find_first(this->certs, match_cert, (void**)&found, cert))
524 {
525 cert->destroy(cert);
526 cert = found->cert->get_ref(found->cert);
527 }
528 this->lock->unlock(this->lock);
529 return cert;
530 }
531
532 METHOD(stroke_ca_t, reload_certs, void,
533 private_stroke_ca_t *this)
534 {
535 enumerator_t *enumerator;
536 certificate_t *cert;
537 ca_section_t *ca;
538 certificate_type_t type = CERT_X509;
539
540 /* holding the write lock while loading/parsing certificates is not optimal,
541 * however, there usually are not that many ca sections configured */
542 this->lock->write_lock(this->lock);
543 if (this->sections->get_count(this->sections))
544 {
545 DBG1(DBG_CFG, "rereading ca certificates in ca sections");
546 }
547 enumerator = this->sections->create_enumerator(this->sections);
548 while (enumerator->enumerate(enumerator, &ca))
549 {
550 cert = stroke_load_ca_cert(ca->path);
551 if (cert)
552 {
553 if (cert->equals(cert, ca->cert))
554 {
555 cert->destroy(cert);
556 }
557 else
558 {
559 this->certs->remove(this->certs, ca->cert, (void*)remove_cert);
560 ca->cert->destroy(ca->cert);
561 ca->cert = add_cert_internal(this, cert, FALSE);
562 }
563 }
564 else
565 {
566 DBG1(DBG_CFG, "failed to reload certificate '%s', removing ca '%s'",
567 ca->path, ca->name);
568 this->sections->remove_at(this->sections, enumerator);
569 this->certs->remove(this->certs, ca->cert, (void*)remove_cert);
570 ca_section_destroy(ca);
571 type = CERT_ANY;
572 }
573 }
574 enumerator->destroy(enumerator);
575 this->lock->unlock(this->lock);
576 lib->credmgr->flush_cache(lib->credmgr, type);
577 }
578
579 METHOD(stroke_ca_t, replace_certs, void,
580 private_stroke_ca_t *this, mem_cred_t *certs)
581 {
582 enumerator_t *enumerator;
583 certificate_t *cert;
584
585 enumerator = certs->set.create_cert_enumerator(&certs->set, CERT_X509,
586 KEY_ANY, NULL, TRUE);
587 this->lock->write_lock(this->lock);
588 this->certs->remove(this->certs, NULL, (void*)remove_auto_certs);
589 while (enumerator->enumerate(enumerator, &cert))
590 {
591 cert = add_cert_internal(this, cert->get_ref(cert), TRUE);
592 cert->destroy(cert);
593 }
594 this->lock->unlock(this->lock);
595 enumerator->destroy(enumerator);
596 lib->credmgr->flush_cache(lib->credmgr, CERT_X509);
597 }
598 /**
599 * list crl or ocsp URIs
600 */
601 static void list_uris(linked_list_t *list, char *label, FILE *out)
602 {
603 bool first = TRUE;
604 char *uri;
605 enumerator_t *enumerator;
606
607 enumerator = list->create_enumerator(list);
608 while (enumerator->enumerate(enumerator, (void**)&uri))
609 {
610 if (first)
611 {
612 fprintf(out, "%s", label);
613 first = FALSE;
614 }
615 else
616 {
617 fprintf(out, " ");
618 }
619 fprintf(out, "'%s'\n", uri);
620 }
621 enumerator->destroy(enumerator);
622 }
623
624 METHOD(stroke_ca_t, check_for_hash_and_url, void,
625 private_stroke_ca_t *this, certificate_t* cert)
626 {
627 ca_section_t *section;
628 enumerator_t *enumerator;
629
630 hasher_t *hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
631 if (hasher == NULL)
632 {
633 DBG1(DBG_IKE, "unable to use hash-and-url: sha1 not supported");
634 return;
635 }
636
637 this->lock->write_lock(this->lock);
638 enumerator = this->sections->create_enumerator(this->sections);
639 while (enumerator->enumerate(enumerator, (void**)&section))
640 {
641 if (section->certuribase && cert->issued_by(cert, section->cert, NULL))
642 {
643 chunk_t hash, encoded;
644
645 if (cert->get_encoding(cert, CERT_ASN1_DER, &encoded))
646 {
647 if (hasher->allocate_hash(hasher, encoded, &hash))
648 {
649 section->hashes->insert_last(section->hashes,
650 identification_create_from_encoding(ID_KEY_ID, hash));
651 chunk_free(&hash);
652 }
653 chunk_free(&encoded);
654 }
655 break;
656 }
657 }
658 enumerator->destroy(enumerator);
659 this->lock->unlock(this->lock);
660
661 hasher->destroy(hasher);
662 }
663
664 METHOD(stroke_ca_t, list, void,
665 private_stroke_ca_t *this, stroke_msg_t *msg, FILE *out)
666 {
667 bool first = TRUE;
668 ca_section_t *section;
669 enumerator_t *enumerator;
670
671 this->lock->read_lock(this->lock);
672 enumerator = this->sections->create_enumerator(this->sections);
673 while (enumerator->enumerate(enumerator, (void**)&section))
674 {
675 certificate_t *cert = section->cert;
676 public_key_t *public = cert->get_public_key(cert);
677 chunk_t chunk;
678
679 if (first)
680 {
681 fprintf(out, "\n");
682 fprintf(out, "List of CA Information Sections:\n");
683 first = FALSE;
684 }
685 fprintf(out, "\n");
686 fprintf(out, " authname: \"%Y\"\n", cert->get_subject(cert));
687
688 /* list authkey and keyid */
689 if (public)
690 {
691 if (public->get_fingerprint(public, KEYID_PUBKEY_SHA1, &chunk))
692 {
693 fprintf(out, " authkey: %#B\n", &chunk);
694 }
695 if (public->get_fingerprint(public, KEYID_PUBKEY_INFO_SHA1, &chunk))
696 {
697 fprintf(out, " keyid: %#B\n", &chunk);
698 }
699 public->destroy(public);
700 }
701 list_uris(section->crl, " crluris: ", out);
702 list_uris(section->ocsp, " ocspuris: ", out);
703 if (section->certuribase)
704 {
705 fprintf(out, " certuribase: '%s'\n", section->certuribase);
706 }
707 }
708 enumerator->destroy(enumerator);
709 this->lock->unlock(this->lock);
710 }
711
712 METHOD(stroke_ca_t, destroy, void,
713 private_stroke_ca_t *this)
714 {
715 this->sections->destroy_function(this->sections, (void*)ca_section_destroy);
716 this->certs->destroy_function(this->certs, (void*)ca_cert_destroy);
717 this->lock->destroy(this->lock);
718 free(this);
719 }
720
721 /*
722 * see header file
723 */
724 stroke_ca_t *stroke_ca_create()
725 {
726 private_stroke_ca_t *this;
727
728 INIT(this,
729 .public = {
730 .set = {
731 .create_private_enumerator = (void*)return_null,
732 .create_cert_enumerator = _create_cert_enumerator,
733 .create_shared_enumerator = (void*)return_null,
734 .create_cdp_enumerator = _create_cdp_enumerator,
735 .cache_cert = (void*)nop,
736 },
737 .add = _add,
738 .del = _del,
739 .list = _list,
740 .get_cert_ref = _get_cert_ref,
741 .reload_certs = _reload_certs,
742 .replace_certs = _replace_certs,
743 .check_for_hash_and_url = _check_for_hash_and_url,
744 .destroy = _destroy,
745 },
746 .sections = linked_list_create(),
747 .certs = linked_list_create(),
748 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
749 );
750
751 return &this->public;
752 }