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