d8f568d36d9166468a82cee4e2d136cdc63f5e4e
[strongswan.git] / src / libstrongswan / credentials / sets / mem_cred.c
1 /*
2 * Copyright (C) 2010-2013 Tobias Brunner
3 * Hochschule fuer Technik Rapperwsil
4 * Copyright (C) 2010 Martin Willi
5 * Copyright (C) 2010 revosec AG
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 * for more details.
16 */
17
18 #include "mem_cred.h"
19
20 #include <threading/rwlock.h>
21 #include <collections/linked_list.h>
22
23 typedef struct private_mem_cred_t private_mem_cred_t;
24
25 /**
26 * Private data of an mem_cred_t object.
27 */
28 struct private_mem_cred_t {
29
30 /**
31 * Public mem_cred_t interface.
32 */
33 mem_cred_t public;
34
35 /**
36 * Lock for this set
37 */
38 rwlock_t *lock;
39
40 /**
41 * List of trusted certificates, certificate_t
42 */
43 linked_list_t *trusted;
44
45 /**
46 * List of trusted and untrusted certificates, certificate_t
47 */
48 linked_list_t *untrusted;
49
50 /**
51 * List of private keys, private_key_t
52 */
53 linked_list_t *keys;
54
55 /**
56 * List of shared keys, as shared_entry_t
57 */
58 linked_list_t *shared;
59
60 /**
61 * List of CDPs, as cdp_t
62 */
63 linked_list_t *cdps;
64 };
65
66 /**
67 * Data for the certificate enumerator
68 */
69 typedef struct {
70 rwlock_t *lock;
71 certificate_type_t cert;
72 key_type_t key;
73 identification_t *id;
74 } cert_data_t;
75
76 /**
77 * destroy cert_data
78 */
79 static void cert_data_destroy(cert_data_t *data)
80 {
81 data->lock->unlock(data->lock);
82 free(data);
83 }
84
85 /**
86 * filter function for certs enumerator
87 */
88 static bool certs_filter(cert_data_t *data, certificate_t **in, certificate_t **out)
89 {
90 public_key_t *public;
91 certificate_t *cert = *in;
92
93 if (data->cert == CERT_ANY || data->cert == cert->get_type(cert))
94 {
95 public = cert->get_public_key(cert);
96 if (public)
97 {
98 if (data->key == KEY_ANY || data->key == public->get_type(public))
99 {
100 if (data->id && public->has_fingerprint(public,
101 data->id->get_encoding(data->id)))
102 {
103 public->destroy(public);
104 *out = *in;
105 return TRUE;
106 }
107 }
108 public->destroy(public);
109 }
110 else if (data->key != KEY_ANY)
111 {
112 return FALSE;
113 }
114 if (data->id == NULL || cert->has_subject(cert, data->id))
115 {
116 *out = *in;
117 return TRUE;
118 }
119 }
120 return FALSE;
121 }
122
123 METHOD(credential_set_t, create_cert_enumerator, enumerator_t*,
124 private_mem_cred_t *this, certificate_type_t cert, key_type_t key,
125 identification_t *id, bool trusted)
126 {
127 cert_data_t *data;
128 enumerator_t *enumerator;
129
130 INIT(data,
131 .lock = this->lock,
132 .cert = cert,
133 .key = key,
134 .id = id,
135 );
136 this->lock->read_lock(this->lock);
137 if (trusted)
138 {
139 enumerator = this->trusted->create_enumerator(this->trusted);
140 }
141 else
142 {
143 enumerator = this->untrusted->create_enumerator(this->untrusted);
144 }
145 return enumerator_create_filter(enumerator, (void*)certs_filter, data,
146 (void*)cert_data_destroy);
147 }
148
149 static bool certificate_equals(certificate_t *item, certificate_t *cert)
150 {
151 return item->equals(item, cert);
152 }
153
154 /**
155 * Add a certificate the the cache. Returns a reference to "cert" or a
156 * previously cached certificate that equals "cert".
157 */
158 static certificate_t *add_cert_internal(private_mem_cred_t *this, bool trusted,
159 certificate_t *cert)
160 {
161 certificate_t *cached;
162 this->lock->write_lock(this->lock);
163 if (this->untrusted->find_first(this->untrusted,
164 (linked_list_match_t)certificate_equals,
165 (void**)&cached, cert) == SUCCESS)
166 {
167 cert->destroy(cert);
168 cert = cached->get_ref(cached);
169 }
170 else
171 {
172 if (trusted)
173 {
174 this->trusted->insert_first(this->trusted, cert->get_ref(cert));
175 }
176 this->untrusted->insert_first(this->untrusted, cert->get_ref(cert));
177 }
178 this->lock->unlock(this->lock);
179 return cert;
180 }
181
182 METHOD(mem_cred_t, add_cert, void,
183 private_mem_cred_t *this, bool trusted, certificate_t *cert)
184 {
185 certificate_t *cached = add_cert_internal(this, trusted, cert);
186 cached->destroy(cached);
187 }
188
189 METHOD(mem_cred_t, add_cert_ref, certificate_t*,
190 private_mem_cred_t *this, bool trusted, certificate_t *cert)
191 {
192 return add_cert_internal(this, trusted, cert);
193 }
194
195 METHOD(mem_cred_t, add_crl, bool,
196 private_mem_cred_t *this, crl_t *crl)
197 {
198 certificate_t *current, *cert = &crl->certificate;
199 enumerator_t *enumerator;
200 bool new = TRUE;
201
202 this->lock->write_lock(this->lock);
203 enumerator = this->untrusted->create_enumerator(this->untrusted);
204 while (enumerator->enumerate(enumerator, (void**)&current))
205 {
206 if (current->get_type(current) == CERT_X509_CRL)
207 {
208 bool found = FALSE;
209 crl_t *crl_c = (crl_t*)current;
210 chunk_t authkey = crl->get_authKeyIdentifier(crl);
211 chunk_t authkey_c = crl_c->get_authKeyIdentifier(crl_c);
212
213 /* compare authorityKeyIdentifiers if available */
214 if (chunk_equals(authkey, authkey_c))
215 {
216 found = TRUE;
217 }
218 else
219 {
220 identification_t *issuer = cert->get_issuer(cert);
221 identification_t *issuer_c = current->get_issuer(current);
222
223 /* otherwise compare issuer distinguished names */
224 if (issuer->equals(issuer, issuer_c))
225 {
226 found = TRUE;
227 }
228 }
229 if (found)
230 {
231 new = crl_is_newer(crl, crl_c);
232 if (new)
233 {
234 this->untrusted->remove_at(this->untrusted, enumerator);
235 }
236 else
237 {
238 cert->destroy(cert);
239 }
240 break;
241 }
242 }
243 }
244 enumerator->destroy(enumerator);
245
246 if (new)
247 {
248 this->untrusted->insert_first(this->untrusted, cert);
249 }
250 this->lock->unlock(this->lock);
251 return new;
252 }
253
254 /**
255 * Data for key enumerator
256 */
257 typedef struct {
258 rwlock_t *lock;
259 key_type_t type;
260 identification_t *id;
261 } key_data_t;
262
263 /**
264 * Destroy key enumerator data
265 */
266 static void key_data_destroy(key_data_t *data)
267 {
268 data->lock->unlock(data->lock);
269 free(data);
270 }
271
272 /**
273 * filter function for private key enumerator
274 */
275 static bool key_filter(key_data_t *data, private_key_t **in, private_key_t **out)
276 {
277 private_key_t *key;
278
279 key = *in;
280 if (data->type == KEY_ANY || data->type == key->get_type(key))
281 {
282 if (data->id == NULL ||
283 key->has_fingerprint(key, data->id->get_encoding(data->id)))
284 {
285 *out = key;
286 return TRUE;
287 }
288 }
289 return FALSE;
290 }
291
292 METHOD(credential_set_t, create_private_enumerator, enumerator_t*,
293 private_mem_cred_t *this, key_type_t type, identification_t *id)
294 {
295 key_data_t *data;
296
297 INIT(data,
298 .lock = this->lock,
299 .type = type,
300 .id = id,
301 );
302 this->lock->read_lock(this->lock);
303 return enumerator_create_filter(this->keys->create_enumerator(this->keys),
304 (void*)key_filter, data, (void*)key_data_destroy);
305 }
306
307 METHOD(mem_cred_t, add_key, void,
308 private_mem_cred_t *this, private_key_t *key)
309 {
310 enumerator_t *enumerator;
311 private_key_t *current;
312
313 this->lock->write_lock(this->lock);
314
315 enumerator = this->keys->create_enumerator(this->keys);
316 while (enumerator->enumerate(enumerator, &current))
317 {
318 if (current->equals(current, key))
319 {
320 this->keys->remove_at(this->keys, enumerator);
321 current->destroy(current);
322 break;
323 }
324 }
325 enumerator->destroy(enumerator);
326
327 this->keys->insert_first(this->keys, key);
328
329 this->lock->unlock(this->lock);
330 }
331
332 /**
333 * Shared key entry
334 */
335 typedef struct {
336 /* shared key */
337 shared_key_t *shared;
338 /* list of owners, identification_t */
339 linked_list_t *owners;
340 } shared_entry_t;
341
342 /**
343 * Clean up a shared entry
344 */
345 static void shared_entry_destroy(shared_entry_t *entry)
346 {
347 entry->owners->destroy_offset(entry->owners,
348 offsetof(identification_t, destroy));
349 entry->shared->destroy(entry->shared);
350 free(entry);
351 }
352
353 /**
354 * Check if two shared key entries equal
355 */
356 static bool shared_entry_equals(shared_entry_t *a, shared_entry_t *b)
357 {
358 enumerator_t *e1, *e2;
359 identification_t *id1, *id2;
360 bool equals = TRUE;
361
362 if (a->shared->get_type(a->shared) != b->shared->get_type(b->shared))
363 {
364 return FALSE;
365 }
366 if (!chunk_equals(a->shared->get_key(a->shared),
367 b->shared->get_key(b->shared)))
368 {
369 return FALSE;
370 }
371 if (a->owners->get_count(a->owners) != b->owners->get_count(b->owners))
372 {
373 return FALSE;
374 }
375 e1 = a->owners->create_enumerator(a->owners);
376 e2 = b->owners->create_enumerator(b->owners);
377 while (e1->enumerate(e1, &id1) && e2->enumerate(e2, &id2))
378 {
379 if (!id1->equals(id1, id2))
380 {
381 equals = FALSE;
382 break;
383 }
384 }
385 e1->destroy(e1);
386 e2->destroy(e2);
387
388 return equals;
389 }
390
391 /**
392 * Data for the shared_key enumerator
393 */
394 typedef struct {
395 rwlock_t *lock;
396 identification_t *me;
397 identification_t *other;
398 shared_key_type_t type;
399 } shared_data_t;
400
401 /**
402 * free shared key enumerator data and unlock list
403 */
404 static void shared_data_destroy(shared_data_t *data)
405 {
406 data->lock->unlock(data->lock);
407 free(data);
408 }
409
410 /**
411 * Get the best match of an owner in an entry.
412 */
413 static id_match_t has_owner(shared_entry_t *entry, identification_t *owner)
414 {
415 enumerator_t *enumerator;
416 id_match_t match, best = ID_MATCH_NONE;
417 identification_t *current;
418
419 enumerator = entry->owners->create_enumerator(entry->owners);
420 while (enumerator->enumerate(enumerator, &current))
421 {
422 match = owner->matches(owner, current);
423 if (match > best)
424 {
425 best = match;
426 }
427 }
428 enumerator->destroy(enumerator);
429 return best;
430 }
431
432 /**
433 * enumerator filter function for shared entries
434 */
435 static bool shared_filter(shared_data_t *data,
436 shared_entry_t **in, shared_key_t **out,
437 void **unused1, id_match_t *me,
438 void **unused2, id_match_t *other)
439 {
440 id_match_t my_match = ID_MATCH_NONE, other_match = ID_MATCH_NONE;
441 shared_entry_t *entry = *in;
442
443 if (data->type != SHARED_ANY &&
444 entry->shared->get_type(entry->shared) != data->type)
445 {
446 return FALSE;
447 }
448 if (data->me)
449 {
450 my_match = has_owner(entry, data->me);
451 }
452 if (data->other)
453 {
454 other_match = has_owner(entry, data->other);
455 }
456 if ((data->me || data->other) && (!my_match && !other_match))
457 {
458 return FALSE;
459 }
460 *out = entry->shared;
461 if (me)
462 {
463 *me = my_match;
464 }
465 if (other)
466 {
467 *other = other_match;
468 }
469 return TRUE;
470 }
471
472 METHOD(credential_set_t, create_shared_enumerator, enumerator_t*,
473 private_mem_cred_t *this, shared_key_type_t type,
474 identification_t *me, identification_t *other)
475 {
476 shared_data_t *data;
477
478 INIT(data,
479 .lock = this->lock,
480 .me = me,
481 .other = other,
482 .type = type,
483 );
484 data->lock->read_lock(data->lock);
485 return enumerator_create_filter(
486 this->shared->create_enumerator(this->shared),
487 (void*)shared_filter, data, (void*)shared_data_destroy);
488 }
489
490 METHOD(mem_cred_t, add_shared_list, void,
491 private_mem_cred_t *this, shared_key_t *shared, linked_list_t* owners)
492 {
493 shared_entry_t *current, *new;
494 enumerator_t *enumerator;
495
496 INIT(new,
497 .shared = shared,
498 .owners = owners,
499 );
500
501 this->lock->write_lock(this->lock);
502
503 enumerator = this->shared->create_enumerator(this->shared);
504 while (enumerator->enumerate(enumerator, &current))
505 {
506 if (shared_entry_equals(current, new))
507 {
508 this->shared->remove_at(this->shared, enumerator);
509 shared_entry_destroy(current);
510 break;
511 }
512 }
513 enumerator->destroy(enumerator);
514
515 this->shared->insert_first(this->shared, new);
516
517 this->lock->unlock(this->lock);
518 }
519
520 METHOD(mem_cred_t, add_shared, void,
521 private_mem_cred_t *this, shared_key_t *shared, ...)
522 {
523 identification_t *id;
524 linked_list_t *owners = linked_list_create();
525 va_list args;
526
527 va_start(args, shared);
528 do
529 {
530 id = va_arg(args, identification_t*);
531 if (id)
532 {
533 owners->insert_first(owners, id);
534 }
535 }
536 while (id);
537 va_end(args);
538
539 add_shared_list(this, shared, owners);
540 }
541
542 /**
543 * Certificate distribution point
544 */
545 typedef struct {
546 certificate_type_t type;
547 identification_t *id;
548 char *uri;
549 } cdp_t;
550
551 /**
552 * Destroy a CDP entry
553 */
554 static void cdp_destroy(cdp_t *this)
555 {
556 this->id->destroy(this->id);
557 free(this->uri);
558 free(this);
559 }
560
561 METHOD(mem_cred_t, add_cdp, void,
562 private_mem_cred_t *this, certificate_type_t type,
563 identification_t *id, char *uri)
564 {
565 cdp_t *cdp;
566
567 INIT(cdp,
568 .type = type,
569 .id = id->clone(id),
570 .uri = strdup(uri),
571 );
572 this->lock->write_lock(this->lock);
573 this->cdps->insert_last(this->cdps, cdp);
574 this->lock->unlock(this->lock);
575 }
576
577 /**
578 * CDP enumerator data
579 */
580 typedef struct {
581 certificate_type_t type;
582 identification_t *id;
583 rwlock_t *lock;
584 } cdp_data_t;
585
586 /**
587 * Clean up CDP enumerator data
588 */
589 static void cdp_data_destroy(cdp_data_t *data)
590 {
591 data->lock->unlock(data->lock);
592 free(data);
593 }
594
595 /**
596 * CDP enumerator filter
597 */
598 static bool cdp_filter(cdp_data_t *data, cdp_t **cdp, char **uri)
599 {
600 if (data->type != CERT_ANY && data->type != (*cdp)->type)
601 {
602 return FALSE;
603 }
604 if (data->id && !(*cdp)->id->matches((*cdp)->id, data->id))
605 {
606 return FALSE;
607 }
608 *uri = (*cdp)->uri;
609 return TRUE;
610 }
611
612 METHOD(credential_set_t, create_cdp_enumerator, enumerator_t*,
613 private_mem_cred_t *this, certificate_type_t type, identification_t *id)
614 {
615 cdp_data_t *data;
616
617 INIT(data,
618 .type = type,
619 .id = id,
620 .lock = this->lock,
621 );
622 this->lock->read_lock(this->lock);
623 return enumerator_create_filter(this->cdps->create_enumerator(this->cdps),
624 (void*)cdp_filter, data, (void*)cdp_data_destroy);
625
626 }
627
628 static void reset_secrets(private_mem_cred_t *this)
629 {
630 this->keys->destroy_offset(this->keys, offsetof(private_key_t, destroy));
631 this->shared->destroy_function(this->shared, (void*)shared_entry_destroy);
632 this->keys = linked_list_create();
633 this->shared = linked_list_create();
634 }
635
636 METHOD(mem_cred_t, replace_secrets, void,
637 private_mem_cred_t *this, mem_cred_t *other_set, bool clone)
638 {
639 private_mem_cred_t *other = (private_mem_cred_t*)other_set;
640 enumerator_t *enumerator;
641 shared_entry_t *entry, *new_entry;
642 private_key_t *key;
643
644 this->lock->write_lock(this->lock);
645
646 reset_secrets(this);
647
648 if (clone)
649 {
650 enumerator = other->keys->create_enumerator(other->keys);
651 while (enumerator->enumerate(enumerator, &key))
652 {
653 this->keys->insert_last(this->keys, key->get_ref(key));
654 }
655 enumerator->destroy(enumerator);
656 enumerator = other->shared->create_enumerator(other->shared);
657 while (enumerator->enumerate(enumerator, &entry))
658 {
659 INIT(new_entry,
660 .shared = entry->shared->get_ref(entry->shared),
661 .owners = entry->owners->clone_offset(entry->owners,
662 offsetof(identification_t, clone)),
663 );
664 this->shared->insert_last(this->shared, new_entry);
665 }
666 enumerator->destroy(enumerator);
667 }
668 else
669 {
670 while (other->keys->remove_first(other->keys, (void**)&key) == SUCCESS)
671 {
672 this->keys->insert_last(this->keys, key);
673 }
674 while (other->shared->remove_first(other->shared,
675 (void**)&entry) == SUCCESS)
676 {
677 this->shared->insert_last(this->shared, entry);
678 }
679 }
680 this->lock->unlock(this->lock);
681 }
682
683 METHOD(mem_cred_t, clear_secrets, void,
684 private_mem_cred_t *this)
685 {
686 this->lock->write_lock(this->lock);
687 reset_secrets(this);
688 this->lock->unlock(this->lock);
689 }
690
691 METHOD(mem_cred_t, clear_, void,
692 private_mem_cred_t *this)
693 {
694 this->lock->write_lock(this->lock);
695 this->trusted->destroy_offset(this->trusted,
696 offsetof(certificate_t, destroy));
697 this->untrusted->destroy_offset(this->untrusted,
698 offsetof(certificate_t, destroy));
699 this->cdps->destroy_function(this->cdps, (void*)cdp_destroy);
700 this->trusted = linked_list_create();
701 this->untrusted = linked_list_create();
702 this->cdps = linked_list_create();
703 this->lock->unlock(this->lock);
704
705 clear_secrets(this);
706 }
707
708 METHOD(mem_cred_t, destroy, void,
709 private_mem_cred_t *this)
710 {
711 clear_(this);
712 this->trusted->destroy(this->trusted);
713 this->untrusted->destroy(this->untrusted);
714 this->keys->destroy(this->keys);
715 this->shared->destroy(this->shared);
716 this->cdps->destroy(this->cdps);
717 this->lock->destroy(this->lock);
718 free(this);
719 }
720
721 /**
722 * See header
723 */
724 mem_cred_t *mem_cred_create()
725 {
726 private_mem_cred_t *this;
727
728 INIT(this,
729 .public = {
730 .set = {
731 .create_shared_enumerator = _create_shared_enumerator,
732 .create_private_enumerator = _create_private_enumerator,
733 .create_cert_enumerator = _create_cert_enumerator,
734 .create_cdp_enumerator = _create_cdp_enumerator,
735 .cache_cert = (void*)nop,
736 },
737 .add_cert = _add_cert,
738 .add_cert_ref = _add_cert_ref,
739 .add_crl = _add_crl,
740 .add_key = _add_key,
741 .add_shared = _add_shared,
742 .add_shared_list = _add_shared_list,
743 .add_cdp = _add_cdp,
744 .replace_secrets = _replace_secrets,
745 .clear = _clear_,
746 .clear_secrets = _clear_secrets,
747 .destroy = _destroy,
748 },
749 .trusted = linked_list_create(),
750 .untrusted = linked_list_create(),
751 .keys = linked_list_create(),
752 .shared = linked_list_create(),
753 .cdps = linked_list_create(),
754 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
755 );
756
757 return &this->public;
758 }