Alternative to mem_cred_t.add_cert added, which returns the certificate.
[strongswan.git] / src / libstrongswan / credentials / sets / mem_cred.c
1 /*
2 * Copyright (C) 2010 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 <utils/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 /**
62 * Data for the certificate enumerator
63 */
64 typedef struct {
65 rwlock_t *lock;
66 certificate_type_t cert;
67 key_type_t key;
68 identification_t *id;
69 } cert_data_t;
70
71 /**
72 * destroy cert_data
73 */
74 static void cert_data_destroy(cert_data_t *data)
75 {
76 data->lock->unlock(data->lock);
77 free(data);
78 }
79
80 /**
81 * filter function for certs enumerator
82 */
83 static bool certs_filter(cert_data_t *data, certificate_t **in, certificate_t **out)
84 {
85 public_key_t *public;
86 certificate_t *cert = *in;
87
88 if (data->cert == CERT_ANY || data->cert == cert->get_type(cert))
89 {
90 public = cert->get_public_key(cert);
91 if (public)
92 {
93 if (data->key == KEY_ANY || data->key == public->get_type(public))
94 {
95 if (data->id && public->has_fingerprint(public,
96 data->id->get_encoding(data->id)))
97 {
98 public->destroy(public);
99 *out = *in;
100 return TRUE;
101 }
102 }
103 public->destroy(public);
104 }
105 else if (data->key != KEY_ANY)
106 {
107 return FALSE;
108 }
109 if (data->id == NULL || cert->has_subject(cert, data->id))
110 {
111 *out = *in;
112 return TRUE;
113 }
114 }
115 return FALSE;
116 }
117
118 METHOD(credential_set_t, create_cert_enumerator, enumerator_t*,
119 private_mem_cred_t *this, certificate_type_t cert, key_type_t key,
120 identification_t *id, bool trusted)
121 {
122 cert_data_t *data;
123 enumerator_t *enumerator;
124
125 INIT(data,
126 .lock = this->lock,
127 .cert = cert,
128 .key = key,
129 .id = id,
130 );
131 this->lock->read_lock(this->lock);
132 if (trusted)
133 {
134 enumerator = this->trusted->create_enumerator(this->trusted);
135 }
136 else
137 {
138 enumerator = this->untrusted->create_enumerator(this->untrusted);
139 }
140 return enumerator_create_filter(enumerator, (void*)certs_filter, data,
141 (void*)cert_data_destroy);
142 }
143
144 static bool certificate_equals(certificate_t *item, certificate_t *cert)
145 {
146 return item->equals(item, cert);
147 }
148
149 /**
150 * Add a certificate the the cache. Returns a reference to "cert" or a
151 * previously cached certificate that equals "cert".
152 */
153 static certificate_t *add_cert_internal(private_mem_cred_t *this, bool trusted,
154 certificate_t *cert)
155 {
156 certificate_t *cached;
157 this->lock->write_lock(this->lock);
158 if (this->untrusted->find_last(this->untrusted,
159 (linked_list_match_t)certificate_equals,
160 (void**)&cached, cert) == SUCCESS)
161 {
162 cert->destroy(cert);
163 cert = cached->get_ref(cached);
164 }
165 else
166 {
167 if (trusted)
168 {
169 this->trusted->insert_last(this->trusted, cert->get_ref(cert));
170 }
171 this->untrusted->insert_last(this->untrusted, cert->get_ref(cert));
172 }
173 this->lock->unlock(this->lock);
174 return cert;
175 }
176
177 METHOD(mem_cred_t, add_cert, void,
178 private_mem_cred_t *this, bool trusted, certificate_t *cert)
179 {
180 certificate_t *cached = add_cert_internal(this, trusted, cert);
181 cached->destroy(cached);
182 }
183
184 METHOD(mem_cred_t, add_cert_ref, certificate_t*,
185 private_mem_cred_t *this, bool trusted, certificate_t *cert)
186 {
187 return add_cert_internal(this, trusted, cert);
188 }
189
190 /**
191 * Data for key enumerator
192 */
193 typedef struct {
194 rwlock_t *lock;
195 key_type_t type;
196 identification_t *id;
197 } key_data_t;
198
199 /**
200 * Destroy key enumerator data
201 */
202 static void key_data_destroy(key_data_t *data)
203 {
204 data->lock->unlock(data->lock);
205 free(data);
206 }
207
208 /**
209 * filter function for private key enumerator
210 */
211 static bool key_filter(key_data_t *data, private_key_t **in, private_key_t **out)
212 {
213 private_key_t *key;
214
215 key = *in;
216 if (data->type == KEY_ANY || data->type == key->get_type(key))
217 {
218 if (data->id == NULL ||
219 key->has_fingerprint(key, data->id->get_encoding(data->id)))
220 {
221 *out = key;
222 return TRUE;
223 }
224 }
225 return FALSE;
226 }
227
228 METHOD(credential_set_t, create_private_enumerator, enumerator_t*,
229 private_mem_cred_t *this, key_type_t type, identification_t *id)
230 {
231 key_data_t *data;
232
233 INIT(data,
234 .lock = this->lock,
235 .type = type,
236 .id = id,
237 );
238 this->lock->read_lock(this->lock);
239 return enumerator_create_filter(this->keys->create_enumerator(this->keys),
240 (void*)key_filter, data, (void*)key_data_destroy);
241 }
242
243 METHOD(mem_cred_t, add_key, void,
244 private_mem_cred_t *this, private_key_t *key)
245 {
246 this->lock->write_lock(this->lock);
247 this->keys->insert_last(this->keys, key);
248 this->lock->unlock(this->lock);
249 }
250
251 /**
252 * Shared key entry
253 */
254 typedef struct {
255 /* shared key */
256 shared_key_t *shared;
257 /* list of owners, identification_t */
258 linked_list_t *owners;
259 } shared_entry_t;
260
261 /**
262 * Clean up a shared entry
263 */
264 static void shared_entry_destroy(shared_entry_t *entry)
265 {
266 entry->owners->destroy_offset(entry->owners,
267 offsetof(identification_t, destroy));
268 entry->shared->destroy(entry->shared);
269 free(entry);
270 }
271
272 /**
273 * Data for the shared_key enumerator
274 */
275 typedef struct {
276 rwlock_t *lock;
277 identification_t *me;
278 identification_t *other;
279 shared_key_type_t type;
280 } shared_data_t;
281
282 /**
283 * free shared key enumerator data and unlock list
284 */
285 static void shared_data_destroy(shared_data_t *data)
286 {
287 data->lock->unlock(data->lock);
288 free(data);
289 }
290
291 /**
292 * Get the best match of an owner in an entry.
293 */
294 static id_match_t has_owner(shared_entry_t *entry, identification_t *owner)
295 {
296 enumerator_t *enumerator;
297 id_match_t match, best = ID_MATCH_NONE;
298 identification_t *current;
299
300 enumerator = entry->owners->create_enumerator(entry->owners);
301 while (enumerator->enumerate(enumerator, &current))
302 {
303 match = owner->matches(owner, current);
304 if (match > best)
305 {
306 best = match;
307 }
308 }
309 enumerator->destroy(enumerator);
310 return best;
311 }
312
313 /**
314 * enumerator filter function for shared entries
315 */
316 static bool shared_filter(shared_data_t *data,
317 shared_entry_t **in, shared_key_t **out,
318 void **unused1, id_match_t *me,
319 void **unused2, id_match_t *other)
320 {
321 id_match_t my_match = ID_MATCH_NONE, other_match = ID_MATCH_NONE;
322 shared_entry_t *entry = *in;
323
324 if (data->type != SHARED_ANY &&
325 entry->shared->get_type(entry->shared) != data->type)
326 {
327 return FALSE;
328 }
329 if (data->me)
330 {
331 my_match = has_owner(entry, data->me);
332 }
333 if (data->other)
334 {
335 other_match = has_owner(entry, data->other);
336 }
337 if ((data->me || data->other) && (!my_match && !other_match))
338 {
339 return FALSE;
340 }
341 *out = entry->shared;
342 if (me)
343 {
344 *me = my_match;
345 }
346 if (other)
347 {
348 *other = other_match;
349 }
350 return TRUE;
351 }
352
353 METHOD(credential_set_t, create_shared_enumerator, enumerator_t*,
354 private_mem_cred_t *this, shared_key_type_t type,
355 identification_t *me, identification_t *other)
356 {
357 shared_data_t *data;
358
359 INIT(data,
360 .lock = this->lock,
361 .me = me,
362 .other = other,
363 .type = type,
364 );
365 data->lock->read_lock(data->lock);
366 return enumerator_create_filter(
367 this->shared->create_enumerator(this->shared),
368 (void*)shared_filter, data, (void*)shared_data_destroy);
369 }
370
371 METHOD(mem_cred_t, add_shared_list, void,
372 private_mem_cred_t *this, shared_key_t *shared, linked_list_t* owners)
373 {
374 shared_entry_t *entry;
375
376 INIT(entry,
377 .shared = shared,
378 .owners = owners,
379 );
380
381 this->lock->write_lock(this->lock);
382 this->shared->insert_last(this->shared, entry);
383 this->lock->unlock(this->lock);
384 }
385
386 METHOD(mem_cred_t, add_shared, void,
387 private_mem_cred_t *this, shared_key_t *shared, ...)
388 {
389 identification_t *id;
390 linked_list_t *owners = linked_list_create();
391 va_list args;
392
393 va_start(args, shared);
394 do
395 {
396 id = va_arg(args, identification_t*);
397 if (id)
398 {
399 owners->insert_last(owners, id);
400 }
401 }
402 while (id);
403 va_end(args);
404
405 add_shared_list(this, shared, owners);
406 }
407
408 METHOD(mem_cred_t, clear_, void,
409 private_mem_cred_t *this)
410 {
411 this->lock->write_lock(this->lock);
412 this->trusted->destroy_offset(this->trusted,
413 offsetof(certificate_t, destroy));
414 this->untrusted->destroy_offset(this->untrusted,
415 offsetof(certificate_t, destroy));
416 this->keys->destroy_offset(this->keys, offsetof(private_key_t, destroy));
417 this->shared->destroy_function(this->shared, (void*)shared_entry_destroy);
418 this->trusted = linked_list_create();
419 this->untrusted = linked_list_create();
420 this->keys = linked_list_create();
421 this->shared = linked_list_create();
422 this->lock->unlock(this->lock);
423 }
424
425 METHOD(mem_cred_t, destroy, void,
426 private_mem_cred_t *this)
427 {
428 clear_(this);
429 this->trusted->destroy(this->trusted);
430 this->untrusted->destroy(this->untrusted);
431 this->keys->destroy(this->keys);
432 this->shared->destroy(this->shared);
433 this->lock->destroy(this->lock);
434 free(this);
435 }
436
437 /**
438 * See header
439 */
440 mem_cred_t *mem_cred_create()
441 {
442 private_mem_cred_t *this;
443
444 INIT(this,
445 .public = {
446 .set = {
447 .create_shared_enumerator = _create_shared_enumerator,
448 .create_private_enumerator = _create_private_enumerator,
449 .create_cert_enumerator = _create_cert_enumerator,
450 .create_cdp_enumerator = (void*)return_null,
451 .cache_cert = (void*)nop,
452 },
453 .add_cert = _add_cert,
454 .add_cert_ref = _add_cert_ref,
455 .add_key = _add_key,
456 .add_shared = _add_shared,
457 .add_shared_list = _add_shared_list,
458 .clear = _clear_,
459 .destroy = _destroy,
460 },
461 .trusted = linked_list_create(),
462 .untrusted = linked_list_create(),
463 .keys = linked_list_create(),
464 .shared = linked_list_create(),
465 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
466 );
467
468 return &this->public;
469 }