/src/strongswan/src/libstrongswan/credentials/credential_manager.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2015-2023 Tobias Brunner |
3 | | * Copyright (C) 2007 Martin Willi |
4 | | * |
5 | | * Copyright (C) secunet Security Networks 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 "credential_manager.h" |
19 | | |
20 | | #include <library.h> |
21 | | #include <utils/debug.h> |
22 | | #include <threading/thread_value.h> |
23 | | #include <threading/mutex.h> |
24 | | #include <threading/rwlock.h> |
25 | | #include <collections/linked_list.h> |
26 | | #include <credentials/sets/cert_cache.h> |
27 | | #include <credentials/sets/auth_cfg_wrapper.h> |
28 | | #include <credentials/certificates/x509.h> |
29 | | |
30 | | /** |
31 | | * Maximum length of a certificate trust chain |
32 | | */ |
33 | 0 | #define MAX_TRUST_PATH_LEN 7 |
34 | | |
35 | | typedef struct private_credential_manager_t private_credential_manager_t; |
36 | | |
37 | | /** |
38 | | * private data of credential_manager |
39 | | */ |
40 | | struct private_credential_manager_t { |
41 | | |
42 | | /** |
43 | | * public functions |
44 | | */ |
45 | | credential_manager_t public; |
46 | | |
47 | | /** |
48 | | * list of credential sets |
49 | | */ |
50 | | linked_list_t *sets; |
51 | | |
52 | | /** |
53 | | * thread local set of credentials, linked_list_t with credential_set_t's |
54 | | */ |
55 | | thread_value_t *local_sets; |
56 | | |
57 | | /** |
58 | | * Exclusive local sets, linked_list_t with credential_set_t |
59 | | */ |
60 | | thread_value_t *exclusive_local_sets; |
61 | | |
62 | | /** |
63 | | * trust relationship and certificate cache |
64 | | */ |
65 | | cert_cache_t *cache; |
66 | | |
67 | | /** |
68 | | * certificates queued for persistent caching |
69 | | */ |
70 | | linked_list_t *cache_queue; |
71 | | |
72 | | /** |
73 | | * list of certificate validators, cert_validator_t |
74 | | */ |
75 | | linked_list_t *validators; |
76 | | |
77 | | /** |
78 | | * read-write lock to sets list |
79 | | */ |
80 | | rwlock_t *lock; |
81 | | |
82 | | /** |
83 | | * mutex for cache queue |
84 | | */ |
85 | | mutex_t *queue_mutex; |
86 | | |
87 | | /** |
88 | | * Registered hook to call on validation errors |
89 | | */ |
90 | | credential_hook_t hook; |
91 | | |
92 | | /** |
93 | | * Registered data to pass to hook |
94 | | */ |
95 | | void *hook_data; |
96 | | |
97 | | /** |
98 | | * Whether to reject pre-trusted end-entity certificates |
99 | | */ |
100 | | bool reject_pretrusted; |
101 | | }; |
102 | | |
103 | | /** data to pass to create_private_enumerator */ |
104 | | typedef struct { |
105 | | private_credential_manager_t *this; |
106 | | key_type_t type; |
107 | | identification_t* keyid; |
108 | | } private_data_t; |
109 | | |
110 | | /** data to pass to create_cert_enumerator */ |
111 | | typedef struct { |
112 | | private_credential_manager_t *this; |
113 | | certificate_type_t cert; |
114 | | key_type_t key; |
115 | | identification_t *id; |
116 | | bool trusted; |
117 | | } cert_data_t; |
118 | | |
119 | | /** data to pass to create_cdp_enumerator */ |
120 | | typedef struct { |
121 | | private_credential_manager_t *this; |
122 | | certificate_type_t type; |
123 | | identification_t *id; |
124 | | } cdp_data_t; |
125 | | |
126 | | /** data to pass to create_shared_enumerator */ |
127 | | typedef struct { |
128 | | private_credential_manager_t *this; |
129 | | shared_key_type_t type; |
130 | | identification_t *me; |
131 | | identification_t *other; |
132 | | } shared_data_t; |
133 | | |
134 | | /** enumerator over local and global sets */ |
135 | | typedef struct { |
136 | | /** implements enumerator_t */ |
137 | | enumerator_t public; |
138 | | /** enumerator over global sets */ |
139 | | enumerator_t *global; |
140 | | /** enumerator over local sets */ |
141 | | enumerator_t *local; |
142 | | /** enumerator over exclusive local sets */ |
143 | | enumerator_t *exclusive; |
144 | | } sets_enumerator_t; |
145 | | |
146 | | METHOD(credential_manager_t, set_hook, void, |
147 | | private_credential_manager_t *this, credential_hook_t hook, void *data) |
148 | 0 | { |
149 | 0 | this->hook = hook; |
150 | 0 | this->hook_data = data; |
151 | 0 | } |
152 | | |
153 | | METHOD(credential_manager_t, call_hook, void, |
154 | | private_credential_manager_t *this, credential_hook_type_t type, |
155 | | certificate_t *cert) |
156 | 0 | { |
157 | 0 | if (this->hook) |
158 | 0 | { |
159 | 0 | this->hook(this->hook_data, type, cert); |
160 | 0 | } |
161 | 0 | } |
162 | | |
163 | | METHOD(enumerator_t, sets_enumerate, bool, |
164 | | sets_enumerator_t *this, va_list args) |
165 | 2 | { |
166 | 2 | credential_set_t **set; |
167 | | |
168 | 2 | VA_ARGS_VGET(args, set); |
169 | | |
170 | 2 | if (this->exclusive) |
171 | 0 | { |
172 | 0 | if (this->exclusive->enumerate(this->exclusive, set)) |
173 | 0 | { /* only enumerate last added */ |
174 | 0 | this->exclusive->destroy(this->exclusive); |
175 | 0 | this->exclusive = NULL; |
176 | 0 | return TRUE; |
177 | 0 | } |
178 | 0 | } |
179 | 2 | if (this->local) |
180 | 0 | { |
181 | 0 | if (this->local->enumerate(this->local, set)) |
182 | 0 | { |
183 | 0 | return TRUE; |
184 | 0 | } |
185 | | /* end of local sets, look for global */ |
186 | 0 | this->local->destroy(this->local); |
187 | 0 | this->local = NULL; |
188 | 0 | } |
189 | 2 | if (this->global) |
190 | 2 | { |
191 | 2 | return this->global->enumerate(this->global, set); |
192 | 2 | } |
193 | 0 | return FALSE; |
194 | 2 | } |
195 | | |
196 | | METHOD(enumerator_t, sets_destroy, void, |
197 | | sets_enumerator_t *this) |
198 | 1 | { |
199 | 1 | DESTROY_IF(this->global); |
200 | 1 | DESTROY_IF(this->local); |
201 | 1 | DESTROY_IF(this->exclusive); |
202 | 1 | free(this); |
203 | 1 | } |
204 | | |
205 | | /** |
206 | | * create an enumerator over both, global and local sets |
207 | | */ |
208 | | static enumerator_t *create_sets_enumerator(private_credential_manager_t *this) |
209 | 1 | { |
210 | 1 | sets_enumerator_t *enumerator; |
211 | 1 | linked_list_t *list; |
212 | | |
213 | 1 | INIT(enumerator, |
214 | 1 | .public = { |
215 | 1 | .enumerate = enumerator_enumerate_default, |
216 | 1 | .venumerate = _sets_enumerate, |
217 | 1 | .destroy = _sets_destroy, |
218 | 1 | }, |
219 | 1 | ); |
220 | | |
221 | 1 | list = this->exclusive_local_sets->get(this->exclusive_local_sets); |
222 | 1 | if (list && list->get_count(list)) |
223 | 0 | { |
224 | 0 | enumerator->exclusive = list->create_enumerator(list); |
225 | 0 | } |
226 | 1 | else |
227 | 1 | { |
228 | 1 | enumerator->global = this->sets->create_enumerator(this->sets); |
229 | 1 | list = this->local_sets->get(this->local_sets); |
230 | 1 | if (list) |
231 | 0 | { |
232 | 0 | enumerator->local = list->create_enumerator(list); |
233 | 0 | } |
234 | 1 | } |
235 | 1 | return &enumerator->public; |
236 | 1 | } |
237 | | |
238 | | /** |
239 | | * cleanup function for cert data |
240 | | */ |
241 | | static void destroy_cert_data(cert_data_t *data) |
242 | 0 | { |
243 | 0 | data->this->lock->unlock(data->this->lock); |
244 | 0 | free(data); |
245 | 0 | } |
246 | | |
247 | | /** |
248 | | * enumerator constructor for certificates |
249 | | */ |
250 | | static enumerator_t *create_cert(credential_set_t *set, cert_data_t *data) |
251 | 0 | { |
252 | 0 | return set->create_cert_enumerator(set, data->cert, data->key, |
253 | 0 | data->id, data->trusted); |
254 | 0 | } |
255 | | |
256 | | METHOD(credential_manager_t, create_cert_enumerator, enumerator_t*, |
257 | | private_credential_manager_t *this, certificate_type_t certificate, |
258 | | key_type_t key, identification_t *id, bool trusted) |
259 | 0 | { |
260 | 0 | cert_data_t *data = malloc_thing(cert_data_t); |
261 | 0 | data->this = this; |
262 | 0 | data->cert = certificate; |
263 | 0 | data->key = key; |
264 | 0 | data->id = id; |
265 | 0 | data->trusted = trusted; |
266 | |
|
267 | 0 | this->lock->read_lock(this->lock); |
268 | 0 | return enumerator_create_nested(create_sets_enumerator(this), |
269 | 0 | (void*)create_cert, data, |
270 | 0 | (void*)destroy_cert_data); |
271 | 0 | } |
272 | | |
273 | | METHOD(credential_manager_t, get_cert, certificate_t*, |
274 | | private_credential_manager_t *this, certificate_type_t cert, key_type_t key, |
275 | | identification_t *id, bool trusted) |
276 | 0 | { |
277 | 0 | certificate_t *current, *found = NULL; |
278 | 0 | enumerator_t *enumerator; |
279 | |
|
280 | 0 | enumerator = create_cert_enumerator(this, cert, key, id, trusted); |
281 | 0 | if (enumerator->enumerate(enumerator, ¤t)) |
282 | 0 | { |
283 | | /* TODO: best match? order by keyid, subject, subjectAltName */ |
284 | 0 | found = current->get_ref(current); |
285 | 0 | } |
286 | 0 | enumerator->destroy(enumerator); |
287 | 0 | return found; |
288 | 0 | } |
289 | | |
290 | | |
291 | | /** |
292 | | * cleanup function for cdp data |
293 | | */ |
294 | | static void destroy_cdp_data(cdp_data_t *data) |
295 | 0 | { |
296 | 0 | data->this->lock->unlock(data->this->lock); |
297 | 0 | free(data); |
298 | 0 | } |
299 | | |
300 | | /** |
301 | | * enumerator constructor for CDPs |
302 | | */ |
303 | | static enumerator_t *create_cdp(credential_set_t *set, cdp_data_t *data) |
304 | 0 | { |
305 | 0 | return set->create_cdp_enumerator(set, data->type, data->id); |
306 | 0 | } |
307 | | |
308 | | METHOD(credential_manager_t, create_cdp_enumerator, enumerator_t*, |
309 | | private_credential_manager_t *this, certificate_type_t type, |
310 | | identification_t *id) |
311 | 0 | { |
312 | 0 | cdp_data_t *data; |
313 | |
|
314 | 0 | INIT(data, |
315 | 0 | .this = this, |
316 | 0 | .type = type, |
317 | 0 | .id = id, |
318 | 0 | ); |
319 | 0 | this->lock->read_lock(this->lock); |
320 | 0 | return enumerator_create_nested(create_sets_enumerator(this), |
321 | 0 | (void*)create_cdp, data, |
322 | 0 | (void*)destroy_cdp_data); |
323 | 0 | } |
324 | | |
325 | | /** |
326 | | * cleanup function for private data |
327 | | */ |
328 | | static void destroy_private_data(private_data_t *data) |
329 | 0 | { |
330 | 0 | data->this->lock->unlock(data->this->lock); |
331 | 0 | free(data); |
332 | 0 | } |
333 | | |
334 | | /** |
335 | | * enumerator constructor for private keys |
336 | | */ |
337 | | static enumerator_t *create_private(credential_set_t *set, private_data_t *data) |
338 | 0 | { |
339 | 0 | return set->create_private_enumerator(set, data->type, data->keyid); |
340 | 0 | } |
341 | | |
342 | | /** |
343 | | * Create an enumerator over private keys |
344 | | */ |
345 | | static enumerator_t *create_private_enumerator( |
346 | | private_credential_manager_t *this, key_type_t key, identification_t *keyid) |
347 | 0 | { |
348 | 0 | private_data_t *data; |
349 | |
|
350 | 0 | INIT(data, |
351 | 0 | .this = this, |
352 | 0 | .type = key, |
353 | 0 | .keyid = keyid, |
354 | 0 | ); |
355 | 0 | this->lock->read_lock(this->lock); |
356 | 0 | return enumerator_create_nested(create_sets_enumerator(this), |
357 | 0 | (void*)create_private, data, |
358 | 0 | (void*)destroy_private_data); |
359 | 0 | } |
360 | | |
361 | | /** |
362 | | * Look up a private key by its key identifier |
363 | | */ |
364 | | static private_key_t* get_private_by_keyid(private_credential_manager_t *this, |
365 | | key_type_t key, identification_t *keyid) |
366 | 0 | { |
367 | 0 | private_key_t *found = NULL; |
368 | 0 | enumerator_t *enumerator; |
369 | |
|
370 | 0 | enumerator = create_private_enumerator(this, key, keyid); |
371 | 0 | if (enumerator->enumerate(enumerator, &found)) |
372 | 0 | { |
373 | 0 | found->get_ref(found); |
374 | 0 | } |
375 | 0 | enumerator->destroy(enumerator); |
376 | 0 | return found; |
377 | 0 | } |
378 | | |
379 | | /** |
380 | | * cleanup function for shared data |
381 | | */ |
382 | | static void destroy_shared_data(shared_data_t *data) |
383 | 1 | { |
384 | 1 | data->this->lock->unlock(data->this->lock); |
385 | 1 | free(data); |
386 | 1 | } |
387 | | |
388 | | /** |
389 | | * enumerator constructor for shared keys |
390 | | */ |
391 | | static enumerator_t *create_shared(credential_set_t *set, shared_data_t *data) |
392 | 1 | { |
393 | 1 | return set->create_shared_enumerator(set, data->type, data->me, data->other); |
394 | 1 | } |
395 | | |
396 | | METHOD(credential_manager_t, create_shared_enumerator, enumerator_t*, |
397 | | private_credential_manager_t *this, shared_key_type_t type, |
398 | | identification_t *me, identification_t *other) |
399 | 1 | { |
400 | 1 | shared_data_t *data; |
401 | | |
402 | 1 | INIT(data, |
403 | 1 | .this = this, |
404 | 1 | .type = type, |
405 | 1 | .me = me, |
406 | 1 | .other = other, |
407 | 1 | ); |
408 | 1 | this->lock->read_lock(this->lock); |
409 | 1 | return enumerator_create_nested(create_sets_enumerator(this), |
410 | 1 | (void*)create_shared, data, |
411 | 1 | (void*)destroy_shared_data); |
412 | 1 | } |
413 | | |
414 | | METHOD(credential_manager_t, get_shared, shared_key_t*, |
415 | | private_credential_manager_t *this, shared_key_type_t type, |
416 | | identification_t *me, identification_t *other) |
417 | 0 | { |
418 | 0 | shared_key_t *current, *found = NULL; |
419 | 0 | id_match_t best_me = ID_MATCH_NONE, best_other = ID_MATCH_NONE; |
420 | 0 | id_match_t match_me, match_other; |
421 | 0 | enumerator_t *enumerator; |
422 | |
|
423 | 0 | enumerator = create_shared_enumerator(this, type, me, other); |
424 | 0 | while (enumerator->enumerate(enumerator, ¤t, &match_me, &match_other)) |
425 | 0 | { |
426 | 0 | if (match_other > best_other || |
427 | 0 | (match_other == best_other && match_me > best_me)) |
428 | 0 | { |
429 | 0 | DESTROY_IF(found); |
430 | 0 | found = current->get_ref(current); |
431 | 0 | best_me = match_me; |
432 | 0 | best_other = match_other; |
433 | 0 | } |
434 | 0 | if (best_me == ID_MATCH_PERFECT && best_other == ID_MATCH_PERFECT) |
435 | 0 | { |
436 | 0 | break; |
437 | 0 | } |
438 | 0 | } |
439 | 0 | enumerator->destroy(enumerator); |
440 | 0 | return found; |
441 | 0 | } |
442 | | |
443 | | METHOD(credential_manager_t, add_local_set, void, |
444 | | private_credential_manager_t *this, credential_set_t *set, bool exclusive) |
445 | 0 | { |
446 | 0 | linked_list_t *sets; |
447 | 0 | thread_value_t *tv; |
448 | |
|
449 | 0 | if (exclusive) |
450 | 0 | { |
451 | 0 | tv = this->exclusive_local_sets; |
452 | 0 | } |
453 | 0 | else |
454 | 0 | { |
455 | 0 | tv = this->local_sets; |
456 | 0 | } |
457 | 0 | sets = tv->get(tv); |
458 | 0 | if (!sets) |
459 | 0 | { |
460 | 0 | sets = linked_list_create(); |
461 | 0 | tv->set(tv, sets); |
462 | 0 | } |
463 | 0 | if (exclusive) |
464 | 0 | { |
465 | 0 | sets->insert_first(sets, set); |
466 | 0 | } |
467 | 0 | else |
468 | 0 | { |
469 | 0 | sets->insert_last(sets, set); |
470 | 0 | } |
471 | 0 | } |
472 | | |
473 | | METHOD(credential_manager_t, remove_local_set, void, |
474 | | private_credential_manager_t *this, credential_set_t *set) |
475 | 0 | { |
476 | 0 | linked_list_t *sets; |
477 | 0 | thread_value_t *tv; |
478 | |
|
479 | 0 | tv = this->local_sets; |
480 | 0 | sets = tv->get(tv); |
481 | 0 | if (sets && sets->remove(sets, set, NULL) && sets->get_count(sets) == 0) |
482 | 0 | { |
483 | 0 | tv->set(tv, NULL); |
484 | 0 | sets->destroy(sets); |
485 | 0 | } |
486 | 0 | tv = this->exclusive_local_sets; |
487 | 0 | sets = tv->get(tv); |
488 | 0 | if (sets && sets->remove(sets, set, NULL) && sets->get_count(sets) == 0) |
489 | 0 | { |
490 | 0 | tv->set(tv, NULL); |
491 | 0 | sets->destroy(sets); |
492 | 0 | } |
493 | 0 | } |
494 | | |
495 | | METHOD(credential_manager_t, issued_by, bool, |
496 | | private_credential_manager_t *this, certificate_t *subject, |
497 | | certificate_t *issuer, signature_params_t **scheme) |
498 | 0 | { |
499 | 0 | if (this->cache) |
500 | 0 | { |
501 | 0 | return this->cache->issued_by(this->cache, subject, issuer, scheme); |
502 | 0 | } |
503 | 0 | return subject->issued_by(subject, issuer, scheme); |
504 | 0 | } |
505 | | |
506 | | METHOD(credential_manager_t, cache_cert, void, |
507 | | private_credential_manager_t *this, certificate_t *cert) |
508 | 0 | { |
509 | 0 | credential_set_t *set; |
510 | 0 | enumerator_t *enumerator; |
511 | |
|
512 | 0 | if (this->lock->try_write_lock(this->lock)) |
513 | 0 | { |
514 | 0 | enumerator = this->sets->create_enumerator(this->sets); |
515 | 0 | while (enumerator->enumerate(enumerator, &set)) |
516 | 0 | { |
517 | 0 | set->cache_cert(set, cert); |
518 | 0 | } |
519 | 0 | enumerator->destroy(enumerator); |
520 | 0 | this->lock->unlock(this->lock); |
521 | 0 | } |
522 | 0 | else |
523 | 0 | { /* we can't cache now as other threads are active, queue for later */ |
524 | 0 | this->queue_mutex->lock(this->queue_mutex); |
525 | 0 | this->cache_queue->insert_last(this->cache_queue, cert->get_ref(cert)); |
526 | 0 | this->queue_mutex->unlock(this->queue_mutex); |
527 | 0 | } |
528 | 0 | } |
529 | | |
530 | | /** |
531 | | * Try to cache certificates queued for caching |
532 | | */ |
533 | | static void cache_queue(private_credential_manager_t *this) |
534 | 3.51k | { |
535 | 3.51k | credential_set_t *set; |
536 | 3.51k | certificate_t *cert; |
537 | 3.51k | enumerator_t *enumerator; |
538 | | |
539 | 3.51k | this->queue_mutex->lock(this->queue_mutex); |
540 | 3.51k | if (this->cache_queue->get_count(this->cache_queue) > 0 && |
541 | 3.51k | this->lock->try_write_lock(this->lock)) |
542 | 0 | { |
543 | 0 | while (this->cache_queue->remove_last(this->cache_queue, |
544 | 0 | (void**)&cert) == SUCCESS) |
545 | 0 | { |
546 | 0 | enumerator = this->sets->create_enumerator(this->sets); |
547 | 0 | while (enumerator->enumerate(enumerator, &set)) |
548 | 0 | { |
549 | 0 | set->cache_cert(set, cert); |
550 | 0 | } |
551 | 0 | enumerator->destroy(enumerator); |
552 | 0 | cert->destroy(cert); |
553 | 0 | } |
554 | 0 | this->lock->unlock(this->lock); |
555 | 0 | } |
556 | 3.51k | this->queue_mutex->unlock(this->queue_mutex); |
557 | 3.51k | } |
558 | | |
559 | | /** |
560 | | * Use validators to check the lifetime of certificates |
561 | | */ |
562 | | static bool check_lifetime(private_credential_manager_t *this, |
563 | | certificate_t *cert, char *label, |
564 | | int pathlen, bool anchor, auth_cfg_t *auth) |
565 | 0 | { |
566 | 0 | time_t not_before, not_after; |
567 | 0 | cert_validator_t *validator; |
568 | 0 | enumerator_t *enumerator; |
569 | 0 | status_t status = NEED_MORE; |
570 | |
|
571 | 0 | enumerator = this->validators->create_enumerator(this->validators); |
572 | 0 | while (enumerator->enumerate(enumerator, &validator)) |
573 | 0 | { |
574 | 0 | if (!validator->check_lifetime) |
575 | 0 | { |
576 | 0 | continue; |
577 | 0 | } |
578 | 0 | status = validator->check_lifetime(validator, cert, |
579 | 0 | pathlen, anchor, auth); |
580 | 0 | if (status != NEED_MORE) |
581 | 0 | { |
582 | 0 | break; |
583 | 0 | } |
584 | 0 | } |
585 | 0 | enumerator->destroy(enumerator); |
586 | |
|
587 | 0 | switch (status) |
588 | 0 | { |
589 | 0 | case NEED_MORE: |
590 | 0 | if (!cert->get_validity(cert, NULL, ¬_before, ¬_after)) |
591 | 0 | { |
592 | 0 | DBG1(DBG_CFG, "%s certificate invalid (valid from %T to %T)", |
593 | 0 | label, ¬_before, FALSE, ¬_after, FALSE); |
594 | 0 | break; |
595 | 0 | } |
596 | 0 | return TRUE; |
597 | 0 | case SUCCESS: |
598 | 0 | return TRUE; |
599 | 0 | case FAILED: |
600 | 0 | default: |
601 | 0 | break; |
602 | 0 | } |
603 | 0 | call_hook(this, CRED_HOOK_EXPIRED, cert); |
604 | 0 | return FALSE; |
605 | 0 | } |
606 | | |
607 | | /** |
608 | | * Check a certificate's lifetime and consult plugins |
609 | | */ |
610 | | static bool check_certificate(private_credential_manager_t *this, |
611 | | certificate_t *subject, certificate_t *issuer, |
612 | | int pathlen, bool anchor, auth_cfg_t *auth) |
613 | 0 | { |
614 | 0 | cert_validator_t *validator; |
615 | 0 | enumerator_t *enumerator; |
616 | |
|
617 | 0 | if (!check_lifetime(this, subject, "subject", pathlen, FALSE, auth) || |
618 | 0 | !check_lifetime(this, issuer, "issuer", pathlen + 1, anchor, auth)) |
619 | 0 | { |
620 | 0 | return FALSE; |
621 | 0 | } |
622 | | |
623 | 0 | enumerator = this->validators->create_enumerator(this->validators); |
624 | 0 | while (enumerator->enumerate(enumerator, &validator)) |
625 | 0 | { |
626 | 0 | if (validator->validate && |
627 | 0 | !validator->validate(validator, subject, issuer, |
628 | 0 | pathlen, anchor, auth)) |
629 | 0 | { |
630 | 0 | enumerator->destroy(enumerator); |
631 | 0 | return FALSE; |
632 | 0 | } |
633 | 0 | } |
634 | 0 | enumerator->destroy(enumerator); |
635 | 0 | return TRUE; |
636 | 0 | } |
637 | | |
638 | | /** |
639 | | * Do online revocation checking |
640 | | */ |
641 | | static bool check_certificate_online(private_credential_manager_t *this, |
642 | | certificate_t *subject, certificate_t *issuer, |
643 | | int pathlen, bool anchor, auth_cfg_t *auth) |
644 | 0 | { |
645 | 0 | cert_validator_t *validator; |
646 | 0 | enumerator_t *enumerator; |
647 | |
|
648 | 0 | enumerator = this->validators->create_enumerator(this->validators); |
649 | 0 | while (enumerator->enumerate(enumerator, &validator)) |
650 | 0 | { |
651 | 0 | if (validator->validate_online && |
652 | 0 | !validator->validate_online(validator, subject, issuer, |
653 | 0 | pathlen, anchor, auth)) |
654 | 0 | { |
655 | 0 | enumerator->destroy(enumerator); |
656 | 0 | return FALSE; |
657 | 0 | } |
658 | 0 | } |
659 | 0 | enumerator->destroy(enumerator); |
660 | 0 | return TRUE; |
661 | 0 | } |
662 | | |
663 | | /** |
664 | | * Get a trusted certificate from a credential set |
665 | | */ |
666 | | static certificate_t *get_pretrusted_cert(private_credential_manager_t *this, |
667 | | key_type_t type, identification_t *id) |
668 | 0 | { |
669 | 0 | certificate_t *subject; |
670 | 0 | public_key_t *public; |
671 | |
|
672 | 0 | subject = get_cert(this, CERT_ANY, type, id, TRUE); |
673 | 0 | if (!subject) |
674 | 0 | { |
675 | 0 | return NULL; |
676 | 0 | } |
677 | 0 | public = subject->get_public_key(subject); |
678 | 0 | if (!public) |
679 | 0 | { |
680 | 0 | subject->destroy(subject); |
681 | 0 | return NULL; |
682 | 0 | } |
683 | 0 | public->destroy(public); |
684 | 0 | return subject; |
685 | 0 | } |
686 | | |
687 | | /** |
688 | | * Get the issuing certificate of a subject certificate |
689 | | */ |
690 | | static certificate_t *get_issuer_cert(private_credential_manager_t *this, |
691 | | certificate_t *subject, bool trusted, |
692 | | signature_params_t **scheme) |
693 | 0 | { |
694 | 0 | enumerator_t *enumerator; |
695 | 0 | certificate_t *issuer = NULL, *candidate; |
696 | |
|
697 | 0 | enumerator = create_cert_enumerator(this, subject->get_type(subject), KEY_ANY, |
698 | 0 | subject->get_issuer(subject), trusted); |
699 | 0 | while (enumerator->enumerate(enumerator, &candidate)) |
700 | 0 | { |
701 | 0 | if (issued_by(this, subject, candidate, scheme)) |
702 | 0 | { |
703 | 0 | issuer = candidate->get_ref(candidate); |
704 | 0 | break; |
705 | 0 | } |
706 | 0 | } |
707 | 0 | enumerator->destroy(enumerator); |
708 | 0 | return issuer; |
709 | 0 | } |
710 | | |
711 | | /** |
712 | | * Get the strength of certificate, add it to auth |
713 | | */ |
714 | | static void get_key_strength(certificate_t *cert, auth_cfg_t *auth) |
715 | 0 | { |
716 | 0 | uintptr_t strength; |
717 | 0 | public_key_t *key; |
718 | 0 | key_type_t type; |
719 | |
|
720 | 0 | key = cert->get_public_key(cert); |
721 | 0 | if (key) |
722 | 0 | { |
723 | 0 | type = key->get_type(key); |
724 | 0 | strength = key->get_keysize(key); |
725 | 0 | DBG2(DBG_CFG, " certificate \"%Y\" key: %d bit %N", |
726 | 0 | cert->get_subject(cert), strength, key_type_names, type); |
727 | 0 | switch (type) |
728 | 0 | { |
729 | 0 | case KEY_RSA: |
730 | 0 | auth->add(auth, AUTH_RULE_RSA_STRENGTH, strength); |
731 | 0 | break; |
732 | 0 | case KEY_ECDSA: |
733 | 0 | auth->add(auth, AUTH_RULE_ECDSA_STRENGTH, strength); |
734 | 0 | break; |
735 | 0 | case KEY_BLISS: |
736 | 0 | auth->add(auth, AUTH_RULE_BLISS_STRENGTH, strength); |
737 | 0 | break; |
738 | 0 | default: |
739 | 0 | break; |
740 | 0 | } |
741 | 0 | key->destroy(key); |
742 | 0 | } |
743 | 0 | } |
744 | | |
745 | | /** |
746 | | * try to verify the trust chain of subject, return TRUE if trusted |
747 | | */ |
748 | | static bool verify_trust_chain(private_credential_manager_t *this, |
749 | | certificate_t *subject, auth_cfg_t *result, |
750 | | bool trusted, bool online) |
751 | 0 | { |
752 | 0 | certificate_t *current, *issuer; |
753 | 0 | auth_cfg_t *auth; |
754 | 0 | signature_params_t *scheme; |
755 | 0 | int pathlen; |
756 | 0 | bool is_anchor = FALSE; |
757 | |
|
758 | 0 | auth = auth_cfg_create(); |
759 | 0 | get_key_strength(subject, auth); |
760 | 0 | current = subject->get_ref(subject); |
761 | 0 | auth->add(auth, AUTH_RULE_SUBJECT_CERT, current->get_ref(current)); |
762 | |
|
763 | 0 | for (pathlen = 0; pathlen <= MAX_TRUST_PATH_LEN; pathlen++) |
764 | 0 | { |
765 | 0 | issuer = get_issuer_cert(this, current, TRUE, &scheme); |
766 | 0 | if (issuer) |
767 | 0 | { |
768 | | /* accept only self-signed CAs as trust anchor */ |
769 | 0 | if (issued_by(this, issuer, issuer, NULL)) |
770 | 0 | { |
771 | 0 | auth->add(auth, AUTH_RULE_CA_CERT, issuer->get_ref(issuer)); |
772 | 0 | DBG1(DBG_CFG, " using trusted ca certificate \"%Y\"", |
773 | 0 | issuer->get_subject(issuer)); |
774 | 0 | trusted = is_anchor = TRUE; |
775 | 0 | } |
776 | 0 | else |
777 | 0 | { |
778 | 0 | auth->add(auth, AUTH_RULE_IM_CERT, issuer->get_ref(issuer)); |
779 | 0 | DBG1(DBG_CFG, " using trusted intermediate ca certificate " |
780 | 0 | "\"%Y\"", issuer->get_subject(issuer)); |
781 | 0 | } |
782 | 0 | auth->add(auth, AUTH_RULE_SIGNATURE_SCHEME, scheme); |
783 | 0 | } |
784 | 0 | else |
785 | 0 | { |
786 | 0 | issuer = get_issuer_cert(this, current, FALSE, &scheme); |
787 | 0 | if (issuer) |
788 | 0 | { |
789 | 0 | if (current->equals(current, issuer)) |
790 | 0 | { |
791 | 0 | DBG1(DBG_CFG, " self-signed certificate \"%Y\" is not " |
792 | 0 | "trusted", current->get_subject(current)); |
793 | 0 | issuer->destroy(issuer); |
794 | 0 | call_hook(this, CRED_HOOK_UNTRUSTED_ROOT, current); |
795 | 0 | break; |
796 | 0 | } |
797 | 0 | auth->add(auth, AUTH_RULE_IM_CERT, issuer->get_ref(issuer)); |
798 | 0 | DBG1(DBG_CFG, " using untrusted intermediate certificate " |
799 | 0 | "\"%Y\"", issuer->get_subject(issuer)); |
800 | 0 | auth->add(auth, AUTH_RULE_SIGNATURE_SCHEME, scheme); |
801 | 0 | } |
802 | 0 | else |
803 | 0 | { |
804 | 0 | DBG1(DBG_CFG, "no issuer certificate found for \"%Y\"", |
805 | 0 | current->get_subject(current)); |
806 | 0 | DBG1(DBG_CFG, " issuer is \"%Y\"", |
807 | 0 | current->get_issuer(current)); |
808 | 0 | call_hook(this, CRED_HOOK_NO_ISSUER, current); |
809 | 0 | if (trusted) |
810 | 0 | { |
811 | 0 | DBG1(DBG_CFG, " reached end of incomplete trust chain for " |
812 | 0 | "trusted certificate \"%Y\"", |
813 | 0 | subject->get_subject(subject)); |
814 | 0 | } |
815 | 0 | break; |
816 | 0 | } |
817 | 0 | } |
818 | 0 | if (!check_certificate(this, current, issuer, pathlen, is_anchor, auth)) |
819 | 0 | { |
820 | 0 | trusted = FALSE; |
821 | 0 | issuer->destroy(issuer); |
822 | 0 | break; |
823 | 0 | } |
824 | 0 | if (issuer) |
825 | 0 | { |
826 | 0 | get_key_strength(issuer, auth); |
827 | 0 | } |
828 | 0 | current->destroy(current); |
829 | 0 | current = issuer; |
830 | 0 | if (is_anchor) |
831 | 0 | { |
832 | 0 | DBG1(DBG_CFG, " reached self-signed root ca with a " |
833 | 0 | "path length of %d", pathlen); |
834 | 0 | break; |
835 | 0 | } |
836 | 0 | } |
837 | 0 | current->destroy(current); |
838 | 0 | if (pathlen > MAX_TRUST_PATH_LEN) |
839 | 0 | { |
840 | 0 | DBG1(DBG_CFG, "maximum path length of %d exceeded", MAX_TRUST_PATH_LEN); |
841 | 0 | call_hook(this, CRED_HOOK_EXCEEDED_PATH_LEN, subject); |
842 | 0 | } |
843 | 0 | else if (trusted && online) |
844 | 0 | { |
845 | 0 | enumerator_t *enumerator; |
846 | 0 | auth_rule_t rule; |
847 | | |
848 | | /* do online revocation checks after basic validation of the chain */ |
849 | 0 | pathlen = 0; |
850 | 0 | current = subject; |
851 | 0 | enumerator = auth->create_enumerator(auth); |
852 | 0 | while (enumerator->enumerate(enumerator, &rule, &issuer)) |
853 | 0 | { |
854 | 0 | if (rule == AUTH_RULE_CA_CERT || rule == AUTH_RULE_IM_CERT) |
855 | 0 | { |
856 | 0 | if (!check_certificate_online(this, current, issuer, pathlen++, |
857 | 0 | rule == AUTH_RULE_CA_CERT, auth)) |
858 | 0 | { |
859 | 0 | trusted = FALSE; |
860 | 0 | break; |
861 | 0 | } |
862 | 0 | else if (rule == AUTH_RULE_CA_CERT) |
863 | 0 | { |
864 | 0 | break; |
865 | 0 | } |
866 | 0 | current = issuer; |
867 | 0 | } |
868 | 0 | } |
869 | 0 | enumerator->destroy(enumerator); |
870 | 0 | } |
871 | 0 | if (trusted) |
872 | 0 | { |
873 | 0 | result->merge(result, auth, FALSE); |
874 | 0 | } |
875 | 0 | auth->destroy(auth); |
876 | 0 | return trusted; |
877 | 0 | } |
878 | | |
879 | | CALLBACK(cert_equals, bool, |
880 | | certificate_t *a, va_list args) |
881 | 0 | { |
882 | 0 | certificate_t *b; |
883 | |
|
884 | 0 | VA_ARGS_VGET(args, b); |
885 | 0 | return a->equals(a, b); |
886 | 0 | } |
887 | | |
888 | | /** |
889 | | * enumerator for trusted certificates |
890 | | */ |
891 | | typedef struct { |
892 | | /** implements enumerator_t interface */ |
893 | | enumerator_t public; |
894 | | /** enumerator over candidate peer certificates */ |
895 | | enumerator_t *candidates; |
896 | | /** reference to the credential_manager */ |
897 | | private_credential_manager_t *this; |
898 | | /** type of the requested key */ |
899 | | key_type_t type; |
900 | | /** identity the requested key belongs to */ |
901 | | identification_t *id; |
902 | | /** TRUE to do CRL/OCSP checking */ |
903 | | bool online; |
904 | | /** pretrusted certificate we have served at first invocation */ |
905 | | certificate_t *pretrusted; |
906 | | /** currently enumerating auth config */ |
907 | | auth_cfg_t *auth; |
908 | | /** list of failed candidates */ |
909 | | linked_list_t *failed; |
910 | | } trusted_enumerator_t; |
911 | | |
912 | | METHOD(enumerator_t, trusted_enumerate, bool, |
913 | | trusted_enumerator_t *this, va_list args) |
914 | 0 | { |
915 | 0 | certificate_t *current, **cert; |
916 | 0 | auth_cfg_t **auth; |
917 | |
|
918 | 0 | VA_ARGS_VGET(args, cert, auth); |
919 | |
|
920 | 0 | DESTROY_IF(this->auth); |
921 | 0 | this->auth = auth_cfg_create(); |
922 | |
|
923 | 0 | if (!this->candidates) |
924 | 0 | { |
925 | | /* first invocation, build enumerator for next one */ |
926 | 0 | this->candidates = create_cert_enumerator(this->this, CERT_ANY, |
927 | 0 | this->type, this->id, FALSE); |
928 | | /* check if we have a trusted certificate for that peer */ |
929 | 0 | this->pretrusted = get_pretrusted_cert(this->this, this->type, this->id); |
930 | 0 | if (this->pretrusted) |
931 | 0 | { |
932 | 0 | if (this->this->reject_pretrusted) |
933 | 0 | { |
934 | 0 | DBG1(DBG_CFG, " rejecting trusted certificate \"%Y\"", |
935 | 0 | this->pretrusted->get_subject(this->pretrusted)); |
936 | 0 | return FALSE; |
937 | 0 | } |
938 | 0 | DBG1(DBG_CFG, " using trusted certificate \"%Y\"", |
939 | 0 | this->pretrusted->get_subject(this->pretrusted)); |
940 | | /* if we find a trusted self signed certificate, we just accept it. |
941 | | * However, in order to fulfill authorization rules, we try to build |
942 | | * the trust chain if it is not self signed */ |
943 | 0 | if (issued_by(this->this, this->pretrusted, this->pretrusted, NULL) || |
944 | 0 | verify_trust_chain(this->this, this->pretrusted, this->auth, |
945 | 0 | TRUE, this->online)) |
946 | 0 | { |
947 | 0 | *cert = this->pretrusted; |
948 | 0 | if (!this->auth->get(this->auth, AUTH_RULE_SUBJECT_CERT)) |
949 | 0 | { /* add cert to auth info, if not returned by trustchain */ |
950 | 0 | this->auth->add(this->auth, AUTH_RULE_SUBJECT_CERT, |
951 | 0 | this->pretrusted->get_ref(this->pretrusted)); |
952 | 0 | } |
953 | 0 | if (auth) |
954 | 0 | { |
955 | 0 | *auth = this->auth; |
956 | 0 | } |
957 | 0 | return TRUE; |
958 | 0 | } |
959 | 0 | } |
960 | 0 | } |
961 | | /* try to verify the trust chain for each certificate found */ |
962 | 0 | while (this->candidates->enumerate(this->candidates, ¤t)) |
963 | 0 | { |
964 | 0 | if (this->pretrusted && |
965 | 0 | this->pretrusted->equals(this->pretrusted, current)) |
966 | 0 | { /* skip pretrusted certificate we already served */ |
967 | 0 | continue; |
968 | 0 | } |
969 | | |
970 | 0 | if (this->failed->find_first(this->failed, cert_equals, NULL, current)) |
971 | 0 | { /* check each candidate only once */ |
972 | 0 | continue; |
973 | 0 | } |
974 | | |
975 | 0 | DBG1(DBG_CFG, " using certificate \"%Y\"", |
976 | 0 | current->get_subject(current)); |
977 | 0 | if (verify_trust_chain(this->this, current, this->auth, FALSE, |
978 | 0 | this->online)) |
979 | 0 | { |
980 | 0 | *cert = current; |
981 | 0 | if (auth) |
982 | 0 | { |
983 | 0 | *auth = this->auth; |
984 | 0 | } |
985 | 0 | return TRUE; |
986 | 0 | } |
987 | 0 | this->failed->insert_last(this->failed, current->get_ref(current)); |
988 | 0 | } |
989 | 0 | return FALSE; |
990 | 0 | } |
991 | | |
992 | | METHOD(enumerator_t, trusted_destroy, void, |
993 | | trusted_enumerator_t *this) |
994 | 0 | { |
995 | 0 | DESTROY_IF(this->pretrusted); |
996 | 0 | DESTROY_IF(this->auth); |
997 | 0 | DESTROY_IF(this->candidates); |
998 | 0 | this->failed->destroy_offset(this->failed, offsetof(certificate_t, destroy)); |
999 | | /* check for delayed certificate cache queue */ |
1000 | 0 | cache_queue(this->this); |
1001 | 0 | free(this); |
1002 | 0 | } |
1003 | | |
1004 | | METHOD(credential_manager_t, create_trusted_enumerator, enumerator_t*, |
1005 | | private_credential_manager_t *this, key_type_t type, |
1006 | | identification_t *id, bool online) |
1007 | 0 | { |
1008 | 0 | trusted_enumerator_t *enumerator; |
1009 | |
|
1010 | 0 | INIT(enumerator, |
1011 | 0 | .public = { |
1012 | 0 | .enumerate = enumerator_enumerate_default, |
1013 | 0 | .venumerate = _trusted_enumerate, |
1014 | 0 | .destroy = _trusted_destroy, |
1015 | 0 | }, |
1016 | 0 | .this = this, |
1017 | 0 | .type = type, |
1018 | 0 | .id = id, |
1019 | 0 | .online = online, |
1020 | 0 | .failed = linked_list_create(), |
1021 | 0 | ); |
1022 | 0 | return &enumerator->public; |
1023 | 0 | } |
1024 | | |
1025 | | /** |
1026 | | * enumerator for public keys |
1027 | | */ |
1028 | | typedef struct { |
1029 | | /** implements enumerator_t interface */ |
1030 | | enumerator_t public; |
1031 | | /** enumerator over candidate peer certificates */ |
1032 | | enumerator_t *inner; |
1033 | | /** reference to the credential_manager */ |
1034 | | private_credential_manager_t *this; |
1035 | | /** currently enumerating key */ |
1036 | | public_key_t *current; |
1037 | | /** credset wrapper around auth config */ |
1038 | | auth_cfg_wrapper_t *wrapper; |
1039 | | } public_enumerator_t; |
1040 | | |
1041 | | METHOD(enumerator_t, public_enumerate, bool, |
1042 | | public_enumerator_t *this, va_list args) |
1043 | 0 | { |
1044 | 0 | certificate_t *cert; |
1045 | 0 | public_key_t **key; |
1046 | 0 | auth_cfg_t **auth; |
1047 | |
|
1048 | 0 | VA_ARGS_VGET(args, key, auth); |
1049 | |
|
1050 | 0 | while (this->inner->enumerate(this->inner, &cert, auth)) |
1051 | 0 | { |
1052 | 0 | DESTROY_IF(this->current); |
1053 | 0 | this->current = cert->get_public_key(cert); |
1054 | 0 | if (this->current) |
1055 | 0 | { |
1056 | 0 | *key = this->current; |
1057 | 0 | return TRUE; |
1058 | 0 | } |
1059 | 0 | } |
1060 | 0 | return FALSE; |
1061 | 0 | } |
1062 | | |
1063 | | METHOD(enumerator_t, public_destroy, void, |
1064 | | public_enumerator_t *this) |
1065 | 0 | { |
1066 | 0 | DESTROY_IF(this->current); |
1067 | 0 | this->inner->destroy(this->inner); |
1068 | 0 | if (this->wrapper) |
1069 | 0 | { |
1070 | 0 | remove_local_set(this->this, &this->wrapper->set); |
1071 | 0 | this->wrapper->destroy(this->wrapper); |
1072 | 0 | } |
1073 | 0 | this->this->lock->unlock(this->this->lock); |
1074 | | /* check for delayed certificate cache queue */ |
1075 | 0 | cache_queue(this->this); |
1076 | 0 | free(this); |
1077 | 0 | } |
1078 | | |
1079 | | METHOD(credential_manager_t, create_public_enumerator, enumerator_t*, |
1080 | | private_credential_manager_t *this, key_type_t type, identification_t *id, |
1081 | | auth_cfg_t *auth, bool online) |
1082 | 0 | { |
1083 | 0 | public_enumerator_t *enumerator; |
1084 | |
|
1085 | 0 | INIT(enumerator, |
1086 | 0 | .public = { |
1087 | 0 | .enumerate = enumerator_enumerate_default, |
1088 | 0 | .venumerate = _public_enumerate, |
1089 | 0 | .destroy = _public_destroy, |
1090 | 0 | }, |
1091 | 0 | .inner = create_trusted_enumerator(this, type, id, online), |
1092 | 0 | .this = this, |
1093 | 0 | ); |
1094 | 0 | if (auth) |
1095 | 0 | { |
1096 | 0 | enumerator->wrapper = auth_cfg_wrapper_create(auth); |
1097 | 0 | add_local_set(this, &enumerator->wrapper->set, FALSE); |
1098 | 0 | } |
1099 | 0 | this->lock->read_lock(this->lock); |
1100 | 0 | return &enumerator->public; |
1101 | 0 | } |
1102 | | |
1103 | | /** |
1104 | | * Check if a helper contains a certificate as trust anchor |
1105 | | */ |
1106 | | static bool auth_contains_cacert(auth_cfg_t *auth, certificate_t *cert) |
1107 | 0 | { |
1108 | 0 | enumerator_t *enumerator; |
1109 | 0 | identification_t *value; |
1110 | 0 | auth_rule_t type; |
1111 | 0 | bool found = FALSE; |
1112 | |
|
1113 | 0 | enumerator = auth->create_enumerator(auth); |
1114 | 0 | while (enumerator->enumerate(enumerator, &type, &value)) |
1115 | 0 | { |
1116 | 0 | if (type == AUTH_RULE_CA_CERT && |
1117 | 0 | cert->equals(cert, (certificate_t*)value)) |
1118 | 0 | { |
1119 | 0 | found = TRUE; |
1120 | 0 | break; |
1121 | 0 | } |
1122 | 0 | } |
1123 | 0 | enumerator->destroy(enumerator); |
1124 | 0 | return found; |
1125 | 0 | } |
1126 | | |
1127 | | /** |
1128 | | * Build a trust chain for subject, optionally only up to one of the CA |
1129 | | * certificates in auth. Returns whether one of the anchors was found. |
1130 | | */ |
1131 | | static auth_cfg_t *build_trustchain(private_credential_manager_t *this, |
1132 | | certificate_t *subject, auth_cfg_t *auth, |
1133 | | bool *found_anchor) |
1134 | 0 | { |
1135 | 0 | certificate_t *issuer, *current; |
1136 | 0 | auth_cfg_t *trustchain; |
1137 | 0 | int pathlen = 0; |
1138 | |
|
1139 | 0 | *found_anchor = FALSE; |
1140 | |
|
1141 | 0 | trustchain = auth_cfg_create(); |
1142 | | /* immediately return for self-signed certificates */ |
1143 | 0 | if (issued_by(this, subject, subject, NULL)) |
1144 | 0 | { |
1145 | 0 | return trustchain; |
1146 | 0 | } |
1147 | 0 | current = subject->get_ref(subject); |
1148 | 0 | for (pathlen = 0; pathlen <= MAX_TRUST_PATH_LEN; pathlen++) |
1149 | 0 | { |
1150 | 0 | issuer = get_issuer_cert(this, current, FALSE, NULL); |
1151 | 0 | if (!issuer) |
1152 | 0 | { /* return the incomplete trust chain */ |
1153 | 0 | break; |
1154 | 0 | } |
1155 | 0 | if (auth_contains_cacert(auth, issuer)) |
1156 | 0 | { /* stop if we find one of the anchors */ |
1157 | 0 | trustchain->add(trustchain, AUTH_RULE_CA_CERT, issuer); |
1158 | 0 | *found_anchor = TRUE; |
1159 | 0 | break; |
1160 | 0 | } |
1161 | 0 | if (issued_by(this, issuer, issuer, NULL)) |
1162 | 0 | { /* trust chain is complete */ |
1163 | 0 | trustchain->add(trustchain, AUTH_RULE_CA_CERT, issuer); |
1164 | 0 | break; |
1165 | 0 | } |
1166 | 0 | trustchain->add(trustchain, AUTH_RULE_IM_CERT, issuer); |
1167 | 0 | current->destroy(current); |
1168 | 0 | current = issuer->get_ref(issuer); |
1169 | 0 | } |
1170 | 0 | current->destroy(current); |
1171 | 0 | if (pathlen > MAX_TRUST_PATH_LEN) |
1172 | 0 | { |
1173 | 0 | trustchain->destroy(trustchain); |
1174 | 0 | return NULL; |
1175 | 0 | } |
1176 | 0 | return trustchain; |
1177 | 0 | } |
1178 | | |
1179 | | /** |
1180 | | * find a private key of a given certificate |
1181 | | */ |
1182 | | static private_key_t *get_private_by_cert(private_credential_manager_t *this, |
1183 | | certificate_t *cert, key_type_t type) |
1184 | 0 | { |
1185 | 0 | private_key_t *private = NULL; |
1186 | 0 | identification_t *keyid; |
1187 | 0 | chunk_t chunk; |
1188 | 0 | public_key_t *public; |
1189 | |
|
1190 | 0 | public = cert->get_public_key(cert); |
1191 | 0 | if (public) |
1192 | 0 | { |
1193 | 0 | if (public->get_fingerprint(public, KEYID_PUBKEY_SHA1, &chunk)) |
1194 | 0 | { |
1195 | 0 | keyid = identification_create_from_encoding(ID_KEY_ID, chunk); |
1196 | 0 | private = get_private_by_keyid(this, type, keyid); |
1197 | 0 | keyid->destroy(keyid); |
1198 | 0 | } |
1199 | 0 | public->destroy(public); |
1200 | 0 | } |
1201 | 0 | return private; |
1202 | 0 | } |
1203 | | |
1204 | | /** |
1205 | | * Move the actually used certificate to front, so it gets returned with get() |
1206 | | */ |
1207 | | static void prefer_cert(auth_cfg_t *auth, certificate_t *cert) |
1208 | 0 | { |
1209 | 0 | enumerator_t *enumerator; |
1210 | 0 | auth_rule_t rule; |
1211 | 0 | certificate_t *current; |
1212 | |
|
1213 | 0 | enumerator = auth->create_enumerator(auth); |
1214 | 0 | while (enumerator->enumerate(enumerator, &rule, ¤t)) |
1215 | 0 | { |
1216 | 0 | if (rule == AUTH_RULE_SUBJECT_CERT) |
1217 | 0 | { |
1218 | 0 | current->get_ref(current); |
1219 | 0 | auth->replace(auth, enumerator, AUTH_RULE_SUBJECT_CERT, cert); |
1220 | 0 | cert = current; |
1221 | 0 | } |
1222 | 0 | } |
1223 | 0 | enumerator->destroy(enumerator); |
1224 | 0 | auth->add(auth, AUTH_RULE_SUBJECT_CERT, cert); |
1225 | 0 | } |
1226 | | |
1227 | | METHOD(credential_manager_t, get_private, private_key_t*, |
1228 | | private_credential_manager_t *this, key_type_t type, identification_t *id, |
1229 | | auth_cfg_t *auth) |
1230 | 0 | { |
1231 | 0 | enumerator_t *enumerator; |
1232 | 0 | certificate_t *cert; |
1233 | 0 | private_key_t *private = NULL, *first_private = NULL; |
1234 | 0 | auth_cfg_t *trustchain, *first_trustchain = NULL; |
1235 | 0 | auth_rule_t rule; |
1236 | 0 | bool has_anchor, found_anchor; |
1237 | | |
1238 | | /* check if this is a lookup by key ID, and do it if so */ |
1239 | 0 | if (id && id->get_type(id) == ID_KEY_ID) |
1240 | 0 | { |
1241 | 0 | private = get_private_by_keyid(this, type, id); |
1242 | 0 | if (private) |
1243 | 0 | { |
1244 | 0 | return private; |
1245 | 0 | } |
1246 | 0 | } |
1247 | | |
1248 | 0 | if (auth) |
1249 | 0 | { |
1250 | 0 | has_anchor = auth->get(auth, AUTH_RULE_CA_CERT) != NULL; |
1251 | | |
1252 | | /* try to find a trust chain with one of the configured subject certs, |
1253 | | * prefer one with any given anchor */ |
1254 | 0 | enumerator = auth->create_enumerator(auth); |
1255 | 0 | while (enumerator->enumerate(enumerator, &rule, &cert)) |
1256 | 0 | { |
1257 | 0 | if (rule == AUTH_RULE_SUBJECT_CERT) |
1258 | 0 | { |
1259 | 0 | private = get_private_by_cert(this, cert, type); |
1260 | 0 | if (private) |
1261 | 0 | { |
1262 | 0 | trustchain = build_trustchain(this, cert, auth, &found_anchor); |
1263 | 0 | if (trustchain) |
1264 | 0 | { |
1265 | 0 | if (!has_anchor || found_anchor) |
1266 | 0 | { |
1267 | 0 | auth->merge(auth, trustchain, FALSE); |
1268 | 0 | prefer_cert(auth, cert->get_ref(cert)); |
1269 | 0 | trustchain->destroy(trustchain); |
1270 | 0 | break; |
1271 | 0 | } |
1272 | 0 | else if (!first_private) |
1273 | 0 | { |
1274 | 0 | first_private = private; |
1275 | 0 | first_trustchain = trustchain; |
1276 | 0 | private = NULL; |
1277 | 0 | continue; |
1278 | 0 | } |
1279 | 0 | trustchain->destroy(trustchain); |
1280 | 0 | } |
1281 | 0 | private->destroy(private); |
1282 | 0 | private = NULL; |
1283 | 0 | } |
1284 | 0 | } |
1285 | 0 | } |
1286 | 0 | enumerator->destroy(enumerator); |
1287 | | |
1288 | | /* if no certificates are configured, try to find one based on the |
1289 | | * identity, preferably with any of the given anchors */ |
1290 | 0 | if (!private && !first_private) |
1291 | 0 | { |
1292 | 0 | enumerator = create_cert_enumerator(this, CERT_ANY, type, id, FALSE); |
1293 | 0 | while (enumerator->enumerate(enumerator, &cert)) |
1294 | 0 | { |
1295 | 0 | private = get_private_by_cert(this, cert, type); |
1296 | 0 | if (private) |
1297 | 0 | { |
1298 | 0 | trustchain = build_trustchain(this, cert, auth, &found_anchor); |
1299 | 0 | if (trustchain) |
1300 | 0 | { |
1301 | 0 | if (!has_anchor || found_anchor) |
1302 | 0 | { |
1303 | 0 | auth->merge(auth, trustchain, FALSE); |
1304 | 0 | prefer_cert(auth, cert->get_ref(cert)); |
1305 | 0 | trustchain->destroy(trustchain); |
1306 | 0 | break; |
1307 | 0 | } |
1308 | 0 | else if (!first_private) |
1309 | 0 | { |
1310 | | /* add this certificate, if we end up choosing a |
1311 | | * different one, it gets replaced above */ |
1312 | 0 | auth->add(auth, AUTH_RULE_SUBJECT_CERT, |
1313 | 0 | cert->get_ref(cert)); |
1314 | 0 | first_private = private; |
1315 | 0 | first_trustchain = trustchain; |
1316 | 0 | private = NULL; |
1317 | 0 | continue; |
1318 | 0 | } |
1319 | 0 | trustchain->destroy(trustchain); |
1320 | 0 | } |
1321 | 0 | private->destroy(private); |
1322 | 0 | private = NULL; |
1323 | 0 | } |
1324 | 0 | } |
1325 | 0 | enumerator->destroy(enumerator); |
1326 | 0 | } |
1327 | | |
1328 | | /* fall back to the first configured or found private key */ |
1329 | 0 | if (!private && first_private) |
1330 | 0 | { |
1331 | 0 | auth->merge(auth, first_trustchain, FALSE); |
1332 | 0 | private = first_private->get_ref(first_private); |
1333 | 0 | } |
1334 | 0 | DESTROY_IF(first_private); |
1335 | 0 | DESTROY_IF(first_trustchain); |
1336 | 0 | } |
1337 | 0 | else |
1338 | 0 | { |
1339 | | /* if we have no config, use the first usable cert with the given |
1340 | | * identity */ |
1341 | 0 | enumerator = create_cert_enumerator(this, CERT_ANY, type, id, FALSE); |
1342 | 0 | while (enumerator->enumerate(enumerator, &cert)) |
1343 | 0 | { |
1344 | 0 | private = get_private_by_cert(this, cert, type); |
1345 | 0 | if (private) |
1346 | 0 | { |
1347 | 0 | break; |
1348 | 0 | } |
1349 | 0 | } |
1350 | 0 | enumerator->destroy(enumerator); |
1351 | 0 | } |
1352 | 0 | return private; |
1353 | 0 | } |
1354 | | |
1355 | | METHOD(credential_manager_t, flush_cache, void, |
1356 | | private_credential_manager_t *this, certificate_type_t type) |
1357 | 3.51k | { |
1358 | 3.51k | if (this->cache) |
1359 | 3.51k | { |
1360 | 3.51k | this->cache->flush(this->cache, type); |
1361 | 3.51k | } |
1362 | 3.51k | } |
1363 | | |
1364 | | METHOD(credential_manager_t, add_set, void, |
1365 | | private_credential_manager_t *this, credential_set_t *set) |
1366 | 0 | { |
1367 | 0 | this->lock->write_lock(this->lock); |
1368 | 0 | this->sets->insert_last(this->sets, set); |
1369 | 0 | this->lock->unlock(this->lock); |
1370 | 0 | } |
1371 | | |
1372 | | METHOD(credential_manager_t, remove_set, void, |
1373 | | private_credential_manager_t *this, credential_set_t *set) |
1374 | 0 | { |
1375 | 0 | this->lock->write_lock(this->lock); |
1376 | 0 | this->sets->remove(this->sets, set, NULL); |
1377 | 0 | this->lock->unlock(this->lock); |
1378 | 0 | } |
1379 | | |
1380 | | METHOD(credential_manager_t, add_validator, void, |
1381 | | private_credential_manager_t *this, cert_validator_t *vdtr) |
1382 | 0 | { |
1383 | 0 | this->lock->write_lock(this->lock); |
1384 | 0 | this->validators->insert_last(this->validators, vdtr); |
1385 | 0 | this->lock->unlock(this->lock); |
1386 | 0 | } |
1387 | | |
1388 | | METHOD(credential_manager_t, remove_validator, void, |
1389 | | private_credential_manager_t *this, cert_validator_t *vdtr) |
1390 | 0 | { |
1391 | 0 | this->lock->write_lock(this->lock); |
1392 | 0 | this->validators->remove(this->validators, vdtr, NULL); |
1393 | 0 | this->lock->unlock(this->lock); |
1394 | 0 | } |
1395 | | |
1396 | | METHOD(credential_manager_t, destroy, void, |
1397 | | private_credential_manager_t *this) |
1398 | 3.51k | { |
1399 | 3.51k | cache_queue(this); |
1400 | 3.51k | this->cache_queue->destroy(this->cache_queue); |
1401 | 3.51k | if (this->cache) |
1402 | 3.51k | { |
1403 | 3.51k | this->sets->remove(this->sets, this->cache, NULL); |
1404 | 3.51k | this->cache->destroy(this->cache); |
1405 | 3.51k | } |
1406 | 3.51k | this->sets->destroy(this->sets); |
1407 | 3.51k | this->local_sets->destroy(this->local_sets); |
1408 | 3.51k | this->exclusive_local_sets->destroy(this->exclusive_local_sets); |
1409 | 3.51k | this->validators->destroy(this->validators); |
1410 | 3.51k | this->lock->destroy(this->lock); |
1411 | 3.51k | this->queue_mutex->destroy(this->queue_mutex); |
1412 | 3.51k | free(this); |
1413 | 3.51k | } |
1414 | | |
1415 | | /* |
1416 | | * see header file |
1417 | | */ |
1418 | | credential_manager_t *credential_manager_create() |
1419 | 3.51k | { |
1420 | 3.51k | private_credential_manager_t *this; |
1421 | | |
1422 | 3.51k | INIT(this, |
1423 | 3.51k | .public = { |
1424 | 3.51k | .create_cert_enumerator = _create_cert_enumerator, |
1425 | 3.51k | .create_shared_enumerator = _create_shared_enumerator, |
1426 | 3.51k | .create_cdp_enumerator = _create_cdp_enumerator, |
1427 | 3.51k | .get_cert = _get_cert, |
1428 | 3.51k | .get_shared = _get_shared, |
1429 | 3.51k | .get_private = _get_private, |
1430 | 3.51k | .create_trusted_enumerator = _create_trusted_enumerator, |
1431 | 3.51k | .create_public_enumerator = _create_public_enumerator, |
1432 | 3.51k | .flush_cache = _flush_cache, |
1433 | 3.51k | .cache_cert = _cache_cert, |
1434 | 3.51k | .issued_by = _issued_by, |
1435 | 3.51k | .add_set = _add_set, |
1436 | 3.51k | .remove_set = _remove_set, |
1437 | 3.51k | .add_local_set = _add_local_set, |
1438 | 3.51k | .remove_local_set = _remove_local_set, |
1439 | 3.51k | .add_validator = _add_validator, |
1440 | 3.51k | .remove_validator = _remove_validator, |
1441 | 3.51k | .set_hook = _set_hook, |
1442 | 3.51k | .call_hook = _call_hook, |
1443 | 3.51k | .destroy = _destroy, |
1444 | 3.51k | }, |
1445 | 3.51k | .sets = linked_list_create(), |
1446 | 3.51k | .validators = linked_list_create(), |
1447 | 3.51k | .cache_queue = linked_list_create(), |
1448 | 3.51k | .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), |
1449 | 3.51k | .queue_mutex = mutex_create(MUTEX_TYPE_DEFAULT), |
1450 | 3.51k | .reject_pretrusted = lib->settings->get_bool(lib->settings, |
1451 | 3.51k | "%s.reject_trusted_end_entity", FALSE, lib->ns), |
1452 | 3.51k | ); |
1453 | | |
1454 | 3.51k | this->local_sets = thread_value_create((thread_cleanup_t)this->sets->destroy); |
1455 | 3.51k | this->exclusive_local_sets = thread_value_create((thread_cleanup_t)this->sets->destroy); |
1456 | 3.51k | if (lib->settings->get_bool(lib->settings, "%s.cert_cache", TRUE, lib->ns)) |
1457 | 3.51k | { |
1458 | 3.51k | this->cache = cert_cache_create(); |
1459 | 3.51k | this->sets->insert_first(this->sets, this->cache); |
1460 | 3.51k | } |
1461 | | |
1462 | 3.51k | return &this->public; |
1463 | 3.51k | } |