mem-cred: Add a method to unify certificate references, without adding it
[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, get_cert_ref, certificate_t*,
196 private_mem_cred_t *this, certificate_t *cert)
197 {
198 certificate_t *cached;
199
200 this->lock->write_lock(this->lock);
201 if (this->untrusted->find_first(this->untrusted,
202 (linked_list_match_t)certificate_equals,
203 (void**)&cached, cert) == SUCCESS)
204 {
205 cert->destroy(cert);
206 cert = cached->get_ref(cached);
207 }
208 this->lock->unlock(this->lock);
209
210 return cert;
211 }
212
213 METHOD(mem_cred_t, add_crl, bool,
214 private_mem_cred_t *this, crl_t *crl)
215 {
216 certificate_t *current, *cert = &crl->certificate;
217 enumerator_t *enumerator;
218 bool new = TRUE;
219
220 this->lock->write_lock(this->lock);
221 enumerator = this->untrusted->create_enumerator(this->untrusted);
222 while (enumerator->enumerate(enumerator, (void**)&current))
223 {
224 if (current->get_type(current) == CERT_X509_CRL)
225 {
226 bool found = FALSE;
227 crl_t *crl_c = (crl_t*)current;
228 chunk_t authkey = crl->get_authKeyIdentifier(crl);
229 chunk_t authkey_c = crl_c->get_authKeyIdentifier(crl_c);
230
231 /* compare authorityKeyIdentifiers if available */
232 if (chunk_equals(authkey, authkey_c))
233 {
234 found = TRUE;
235 }
236 else
237 {
238 identification_t *issuer = cert->get_issuer(cert);
239 identification_t *issuer_c = current->get_issuer(current);
240
241 /* otherwise compare issuer distinguished names */
242 if (issuer->equals(issuer, issuer_c))
243 {
244 found = TRUE;
245 }
246 }
247 if (found)
248 {
249 new = crl_is_newer(crl, crl_c);
250 if (new)
251 {
252 this->untrusted->remove_at(this->untrusted, enumerator);
253 }
254 else
255 {
256 cert->destroy(cert);
257 }
258 break;
259 }
260 }
261 }
262 enumerator->destroy(enumerator);
263
264 if (new)
265 {
266 this->untrusted->insert_first(this->untrusted, cert);
267 }
268 this->lock->unlock(this->lock);
269 return new;
270 }
271
272 /**
273 * Data for key enumerator
274 */
275 typedef struct {
276 rwlock_t *lock;
277 key_type_t type;
278 identification_t *id;
279 } key_data_t;
280
281 /**
282 * Destroy key enumerator data
283 */
284 static void key_data_destroy(key_data_t *data)
285 {
286 data->lock->unlock(data->lock);
287 free(data);
288 }
289
290 /**
291 * filter function for private key enumerator
292 */
293 static bool key_filter(key_data_t *data, private_key_t **in, private_key_t **out)
294 {
295 private_key_t *key;
296
297 key = *in;
298 if (data->type == KEY_ANY || data->type == key->get_type(key))
299 {
300 if (data->id == NULL ||
301 key->has_fingerprint(key, data->id->get_encoding(data->id)))
302 {
303 *out = key;
304 return TRUE;
305 }
306 }
307 return FALSE;
308 }
309
310 METHOD(credential_set_t, create_private_enumerator, enumerator_t*,
311 private_mem_cred_t *this, key_type_t type, identification_t *id)
312 {
313 key_data_t *data;
314
315 INIT(data,
316 .lock = this->lock,
317 .type = type,
318 .id = id,
319 );
320 this->lock->read_lock(this->lock);
321 return enumerator_create_filter(this->keys->create_enumerator(this->keys),
322 (void*)key_filter, data, (void*)key_data_destroy);
323 }
324
325 METHOD(mem_cred_t, add_key, void,
326 private_mem_cred_t *this, private_key_t *key)
327 {
328 enumerator_t *enumerator;
329 private_key_t *current;
330
331 this->lock->write_lock(this->lock);
332
333 enumerator = this->keys->create_enumerator(this->keys);
334 while (enumerator->enumerate(enumerator, &current))
335 {
336 if (current->equals(current, key))
337 {
338 this->keys->remove_at(this->keys, enumerator);
339 current->destroy(current);
340 break;
341 }
342 }
343 enumerator->destroy(enumerator);
344
345 this->keys->insert_first(this->keys, key);
346
347 this->lock->unlock(this->lock);
348 }
349
350 /**
351 * Shared key entry
352 */
353 typedef struct {
354 /* shared key */
355 shared_key_t *shared;
356 /* list of owners, identification_t */
357 linked_list_t *owners;
358 } shared_entry_t;
359
360 /**
361 * Clean up a shared entry
362 */
363 static void shared_entry_destroy(shared_entry_t *entry)
364 {
365 entry->owners->destroy_offset(entry->owners,
366 offsetof(identification_t, destroy));
367 entry->shared->destroy(entry->shared);
368 free(entry);
369 }
370
371 /**
372 * Check if two shared key entries equal
373 */
374 static bool shared_entry_equals(shared_entry_t *a, shared_entry_t *b)
375 {
376 enumerator_t *e1, *e2;
377 identification_t *id1, *id2;
378 bool equals = TRUE;
379
380 if (a->shared->get_type(a->shared) != b->shared->get_type(b->shared))
381 {
382 return FALSE;
383 }
384 if (!chunk_equals(a->shared->get_key(a->shared),
385 b->shared->get_key(b->shared)))
386 {
387 return FALSE;
388 }
389 if (a->owners->get_count(a->owners) != b->owners->get_count(b->owners))
390 {
391 return FALSE;
392 }
393 e1 = a->owners->create_enumerator(a->owners);
394 e2 = b->owners->create_enumerator(b->owners);
395 while (e1->enumerate(e1, &id1) && e2->enumerate(e2, &id2))
396 {
397 if (!id1->equals(id1, id2))
398 {
399 equals = FALSE;
400 break;
401 }
402 }
403 e1->destroy(e1);
404 e2->destroy(e2);
405
406 return equals;
407 }
408
409 /**
410 * Data for the shared_key enumerator
411 */
412 typedef struct {
413 rwlock_t *lock;
414 identification_t *me;
415 identification_t *other;
416 shared_key_type_t type;
417 } shared_data_t;
418
419 /**
420 * free shared key enumerator data and unlock list
421 */
422 static void shared_data_destroy(shared_data_t *data)
423 {
424 data->lock->unlock(data->lock);
425 free(data);
426 }
427
428 /**
429 * Get the best match of an owner in an entry.
430 */
431 static id_match_t has_owner(shared_entry_t *entry, identification_t *owner)
432 {
433 enumerator_t *enumerator;
434 id_match_t match, best = ID_MATCH_NONE;
435 identification_t *current;
436
437 enumerator = entry->owners->create_enumerator(entry->owners);
438 while (enumerator->enumerate(enumerator, &current))
439 {
440 match = owner->matches(owner, current);
441 if (match > best)
442 {
443 best = match;
444 }
445 }
446 enumerator->destroy(enumerator);
447 return best;
448 }
449
450 /**
451 * enumerator filter function for shared entries
452 */
453 static bool shared_filter(shared_data_t *data,
454 shared_entry_t **in, shared_key_t **out,
455 void **unused1, id_match_t *me,
456 void **unused2, id_match_t *other)
457 {
458 id_match_t my_match = ID_MATCH_NONE, other_match = ID_MATCH_NONE;
459 shared_entry_t *entry = *in;
460
461 if (data->type != SHARED_ANY &&
462 entry->shared->get_type(entry->shared) != data->type)
463 {
464 return FALSE;
465 }
466 if (data->me)
467 {
468 my_match = has_owner(entry, data->me);
469 }
470 if (data->other)
471 {
472 other_match = has_owner(entry, data->other);
473 }
474 if ((data->me || data->other) && (!my_match && !other_match))
475 {
476 return FALSE;
477 }
478 *out = entry->shared;
479 if (me)
480 {
481 *me = my_match;
482 }
483 if (other)
484 {
485 *other = other_match;
486 }
487 return TRUE;
488 }
489
490 METHOD(credential_set_t, create_shared_enumerator, enumerator_t*,
491 private_mem_cred_t *this, shared_key_type_t type,
492 identification_t *me, identification_t *other)
493 {
494 shared_data_t *data;
495
496 INIT(data,
497 .lock = this->lock,
498 .me = me,
499 .other = other,
500 .type = type,
501 );
502 data->lock->read_lock(data->lock);
503 return enumerator_create_filter(
504 this->shared->create_enumerator(this->shared),
505 (void*)shared_filter, data, (void*)shared_data_destroy);
506 }
507
508 METHOD(mem_cred_t, add_shared_list, void,
509 private_mem_cred_t *this, shared_key_t *shared, linked_list_t* owners)
510 {
511 shared_entry_t *current, *new;
512 enumerator_t *enumerator;
513
514 INIT(new,
515 .shared = shared,
516 .owners = owners,
517 );
518
519 this->lock->write_lock(this->lock);
520
521 enumerator = this->shared->create_enumerator(this->shared);
522 while (enumerator->enumerate(enumerator, &current))
523 {
524 if (shared_entry_equals(current, new))
525 {
526 this->shared->remove_at(this->shared, enumerator);
527 shared_entry_destroy(current);
528 break;
529 }
530 }
531 enumerator->destroy(enumerator);
532
533 this->shared->insert_first(this->shared, new);
534
535 this->lock->unlock(this->lock);
536 }
537
538 METHOD(mem_cred_t, add_shared, void,
539 private_mem_cred_t *this, shared_key_t *shared, ...)
540 {
541 identification_t *id;
542 linked_list_t *owners = linked_list_create();
543 va_list args;
544
545 va_start(args, shared);
546 do
547 {
548 id = va_arg(args, identification_t*);
549 if (id)
550 {
551 owners->insert_first(owners, id);
552 }
553 }
554 while (id);
555 va_end(args);
556
557 add_shared_list(this, shared, owners);
558 }
559
560 /**
561 * Certificate distribution point
562 */
563 typedef struct {
564 certificate_type_t type;
565 identification_t *id;
566 char *uri;
567 } cdp_t;
568
569 /**
570 * Destroy a CDP entry
571 */
572 static void cdp_destroy(cdp_t *this)
573 {
574 this->id->destroy(this->id);
575 free(this->uri);
576 free(this);
577 }
578
579 METHOD(mem_cred_t, add_cdp, void,
580 private_mem_cred_t *this, certificate_type_t type,
581 identification_t *id, char *uri)
582 {
583 cdp_t *cdp;
584
585 INIT(cdp,
586 .type = type,
587 .id = id->clone(id),
588 .uri = strdup(uri),
589 );
590 this->lock->write_lock(this->lock);
591 this->cdps->insert_last(this->cdps, cdp);
592 this->lock->unlock(this->lock);
593 }
594
595 /**
596 * CDP enumerator data
597 */
598 typedef struct {
599 certificate_type_t type;
600 identification_t *id;
601 rwlock_t *lock;
602 } cdp_data_t;
603
604 /**
605 * Clean up CDP enumerator data
606 */
607 static void cdp_data_destroy(cdp_data_t *data)
608 {
609 data->lock->unlock(data->lock);
610 free(data);
611 }
612
613 /**
614 * CDP enumerator filter
615 */
616 static bool cdp_filter(cdp_data_t *data, cdp_t **cdp, char **uri)
617 {
618 if (data->type != CERT_ANY && data->type != (*cdp)->type)
619 {
620 return FALSE;
621 }
622 if (data->id && !(*cdp)->id->matches((*cdp)->id, data->id))
623 {
624 return FALSE;
625 }
626 *uri = (*cdp)->uri;
627 return TRUE;
628 }
629
630 METHOD(credential_set_t, create_cdp_enumerator, enumerator_t*,
631 private_mem_cred_t *this, certificate_type_t type, identification_t *id)
632 {
633 cdp_data_t *data;
634
635 INIT(data,
636 .type = type,
637 .id = id,
638 .lock = this->lock,
639 );
640 this->lock->read_lock(this->lock);
641 return enumerator_create_filter(this->cdps->create_enumerator(this->cdps),
642 (void*)cdp_filter, data, (void*)cdp_data_destroy);
643
644 }
645
646 static void reset_secrets(private_mem_cred_t *this)
647 {
648 this->keys->destroy_offset(this->keys, offsetof(private_key_t, destroy));
649 this->shared->destroy_function(this->shared, (void*)shared_entry_destroy);
650 this->keys = linked_list_create();
651 this->shared = linked_list_create();
652 }
653
654 METHOD(mem_cred_t, replace_secrets, void,
655 private_mem_cred_t *this, mem_cred_t *other_set, bool clone)
656 {
657 private_mem_cred_t *other = (private_mem_cred_t*)other_set;
658 enumerator_t *enumerator;
659 shared_entry_t *entry, *new_entry;
660 private_key_t *key;
661
662 this->lock->write_lock(this->lock);
663
664 reset_secrets(this);
665
666 if (clone)
667 {
668 enumerator = other->keys->create_enumerator(other->keys);
669 while (enumerator->enumerate(enumerator, &key))
670 {
671 this->keys->insert_last(this->keys, key->get_ref(key));
672 }
673 enumerator->destroy(enumerator);
674 enumerator = other->shared->create_enumerator(other->shared);
675 while (enumerator->enumerate(enumerator, &entry))
676 {
677 INIT(new_entry,
678 .shared = entry->shared->get_ref(entry->shared),
679 .owners = entry->owners->clone_offset(entry->owners,
680 offsetof(identification_t, clone)),
681 );
682 this->shared->insert_last(this->shared, new_entry);
683 }
684 enumerator->destroy(enumerator);
685 }
686 else
687 {
688 while (other->keys->remove_first(other->keys, (void**)&key) == SUCCESS)
689 {
690 this->keys->insert_last(this->keys, key);
691 }
692 while (other->shared->remove_first(other->shared,
693 (void**)&entry) == SUCCESS)
694 {
695 this->shared->insert_last(this->shared, entry);
696 }
697 }
698 this->lock->unlock(this->lock);
699 }
700
701 METHOD(mem_cred_t, clear_secrets, void,
702 private_mem_cred_t *this)
703 {
704 this->lock->write_lock(this->lock);
705 reset_secrets(this);
706 this->lock->unlock(this->lock);
707 }
708
709 METHOD(mem_cred_t, clear_, void,
710 private_mem_cred_t *this)
711 {
712 this->lock->write_lock(this->lock);
713 this->trusted->destroy_offset(this->trusted,
714 offsetof(certificate_t, destroy));
715 this->untrusted->destroy_offset(this->untrusted,
716 offsetof(certificate_t, destroy));
717 this->cdps->destroy_function(this->cdps, (void*)cdp_destroy);
718 this->trusted = linked_list_create();
719 this->untrusted = linked_list_create();
720 this->cdps = linked_list_create();
721 this->lock->unlock(this->lock);
722
723 clear_secrets(this);
724 }
725
726 METHOD(mem_cred_t, destroy, void,
727 private_mem_cred_t *this)
728 {
729 clear_(this);
730 this->trusted->destroy(this->trusted);
731 this->untrusted->destroy(this->untrusted);
732 this->keys->destroy(this->keys);
733 this->shared->destroy(this->shared);
734 this->cdps->destroy(this->cdps);
735 this->lock->destroy(this->lock);
736 free(this);
737 }
738
739 /**
740 * See header
741 */
742 mem_cred_t *mem_cred_create()
743 {
744 private_mem_cred_t *this;
745
746 INIT(this,
747 .public = {
748 .set = {
749 .create_shared_enumerator = _create_shared_enumerator,
750 .create_private_enumerator = _create_private_enumerator,
751 .create_cert_enumerator = _create_cert_enumerator,
752 .create_cdp_enumerator = _create_cdp_enumerator,
753 .cache_cert = (void*)nop,
754 },
755 .add_cert = _add_cert,
756 .add_cert_ref = _add_cert_ref,
757 .get_cert_ref = _get_cert_ref,
758 .add_crl = _add_crl,
759 .add_key = _add_key,
760 .add_shared = _add_shared,
761 .add_shared_list = _add_shared_list,
762 .add_cdp = _add_cdp,
763 .replace_secrets = _replace_secrets,
764 .clear = _clear_,
765 .clear_secrets = _clear_secrets,
766 .destroy = _destroy,
767 },
768 .trusted = linked_list_create(),
769 .untrusted = linked_list_create(),
770 .keys = linked_list_create(),
771 .shared = linked_list_create(),
772 .cdps = linked_list_create(),
773 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
774 );
775
776 return &this->public;
777 }