/src/strongswan/src/libcharon/config/backend_manager.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 2018 Tobias Brunner |
3 | | * Copyright (C) 2007-2009 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 "backend_manager.h" |
19 | | |
20 | | #include <sys/types.h> |
21 | | |
22 | | #include <daemon.h> |
23 | | #include <collections/linked_list.h> |
24 | | #include <threading/rwlock.h> |
25 | | |
26 | | |
27 | | typedef struct private_backend_manager_t private_backend_manager_t; |
28 | | |
29 | | /** |
30 | | * Private data of an backend_manager_t object. |
31 | | */ |
32 | | struct private_backend_manager_t { |
33 | | |
34 | | /** |
35 | | * Public part of backend_manager_t object. |
36 | | */ |
37 | | backend_manager_t public; |
38 | | |
39 | | /** |
40 | | * list of registered backends |
41 | | */ |
42 | | linked_list_t *backends; |
43 | | |
44 | | /** |
45 | | * rwlock for backends |
46 | | */ |
47 | | rwlock_t *lock; |
48 | | }; |
49 | | |
50 | | /** |
51 | | * match of an ike_cfg |
52 | | */ |
53 | | typedef enum ike_cfg_match_t { |
54 | | /* doesn't match at all */ |
55 | | MATCH_NONE = 0x00, |
56 | | /* match for a %any host. For both hosts, hence skip 0x02 */ |
57 | | MATCH_ANY = 0x01, |
58 | | /* IKE version matches exactly (config is not for any version) */ |
59 | | MATCH_VERSION = 0x04, |
60 | | /* local identity matches */ |
61 | | MATCH_ME = 0x08, |
62 | | /* remote identity matches */ |
63 | | MATCH_OTHER = 0x10, |
64 | | } ike_cfg_match_t; |
65 | | |
66 | | /** |
67 | | * data to pass nested IKE enumerator |
68 | | */ |
69 | | typedef struct { |
70 | | private_backend_manager_t *this; |
71 | | host_t *me; |
72 | | host_t *other; |
73 | | } ike_data_t; |
74 | | |
75 | | /** |
76 | | * inner enumerator constructor for IKE cfgs |
77 | | */ |
78 | | static enumerator_t *ike_enum_create(backend_t *backend, ike_data_t *data) |
79 | 0 | { |
80 | 0 | return backend->create_ike_cfg_enumerator(backend, data->me, data->other); |
81 | 0 | } |
82 | | |
83 | | /** |
84 | | * get a match of a candidate ike_cfg for two hosts |
85 | | */ |
86 | | static ike_cfg_match_t get_ike_match(ike_cfg_t *cand, host_t *me, host_t *other, |
87 | | ike_version_t version) |
88 | 0 | { |
89 | 0 | ike_cfg_match_t match = MATCH_NONE; |
90 | 0 | int quality; |
91 | |
|
92 | 0 | if (cand->get_version(cand) != IKE_ANY && |
93 | 0 | version != cand->get_version(cand)) |
94 | 0 | { |
95 | 0 | return MATCH_NONE; |
96 | 0 | } |
97 | | |
98 | 0 | if (me) |
99 | 0 | { |
100 | 0 | quality = cand->match_me(cand, me); |
101 | 0 | if (!quality) |
102 | 0 | { |
103 | 0 | return MATCH_NONE; |
104 | 0 | } |
105 | 0 | match += quality * MATCH_ME; |
106 | 0 | } |
107 | 0 | else |
108 | 0 | { |
109 | 0 | match += MATCH_ANY; |
110 | 0 | } |
111 | | |
112 | 0 | if (other) |
113 | 0 | { |
114 | 0 | quality = cand->match_other(cand, other); |
115 | 0 | if (!quality) |
116 | 0 | { |
117 | 0 | return MATCH_NONE; |
118 | 0 | } |
119 | 0 | match += quality * MATCH_OTHER; |
120 | 0 | } |
121 | 0 | else |
122 | 0 | { |
123 | 0 | match += MATCH_ANY; |
124 | 0 | } |
125 | | |
126 | 0 | if (match != MATCH_NONE && |
127 | 0 | cand->get_version(cand) != IKE_ANY) |
128 | 0 | { /* if we have a match, improve it if candidate version specified */ |
129 | 0 | match += MATCH_VERSION; |
130 | 0 | } |
131 | 0 | return match; |
132 | 0 | } |
133 | | |
134 | | /** |
135 | | * list element to help sorting |
136 | | */ |
137 | | typedef struct { |
138 | | ike_cfg_match_t match; |
139 | | ike_cfg_t *cfg; |
140 | | } ike_match_entry_t; |
141 | | |
142 | | CALLBACK(ike_enum_filter, bool, |
143 | | linked_list_t *configs, enumerator_t *orig, va_list args) |
144 | 0 | { |
145 | 0 | ike_match_entry_t *entry; |
146 | 0 | ike_cfg_t **out; |
147 | |
|
148 | 0 | VA_ARGS_VGET(args, out); |
149 | |
|
150 | 0 | if (orig->enumerate(orig, &entry)) |
151 | 0 | { |
152 | 0 | *out = entry->cfg; |
153 | 0 | return TRUE; |
154 | 0 | } |
155 | 0 | return FALSE; |
156 | 0 | } |
157 | | |
158 | | CALLBACK(ike_match_entry_list_destroy, void, |
159 | | linked_list_t *configs) |
160 | 0 | { |
161 | 0 | ike_match_entry_t *entry; |
162 | |
|
163 | 0 | while (configs->remove_last(configs, (void**)&entry) == SUCCESS) |
164 | 0 | { |
165 | 0 | entry->cfg->destroy(entry->cfg); |
166 | 0 | free(entry); |
167 | 0 | } |
168 | 0 | configs->destroy(configs); |
169 | 0 | } |
170 | | |
171 | | /** |
172 | | * Insert entry into match-sorted list |
173 | | */ |
174 | | static void insert_sorted_ike(ike_match_entry_t *entry, linked_list_t *list) |
175 | 0 | { |
176 | 0 | enumerator_t *enumerator; |
177 | 0 | ike_match_entry_t *current; |
178 | |
|
179 | 0 | enumerator = list->create_enumerator(list); |
180 | 0 | while (enumerator->enumerate(enumerator, ¤t)) |
181 | 0 | { |
182 | 0 | if (entry->match > current->match) |
183 | 0 | { |
184 | 0 | break; |
185 | 0 | } |
186 | 0 | } |
187 | 0 | list->insert_before(list, enumerator, entry); |
188 | 0 | enumerator->destroy(enumerator); |
189 | 0 | } |
190 | | |
191 | | /** |
192 | | * Create a sorted list of all matching IKE configs |
193 | | */ |
194 | | static linked_list_t *get_matching_ike_cfgs(private_backend_manager_t *this, |
195 | | host_t *me, host_t *other, |
196 | | ike_version_t version) |
197 | 0 | { |
198 | 0 | ike_cfg_t *current; |
199 | 0 | enumerator_t *enumerator; |
200 | 0 | ike_data_t *data; |
201 | 0 | linked_list_t *configs; |
202 | 0 | ike_cfg_match_t match; |
203 | 0 | ike_match_entry_t *entry; |
204 | |
|
205 | 0 | INIT(data, |
206 | 0 | .this = this, |
207 | 0 | .me = me, |
208 | 0 | .other = other, |
209 | 0 | ); |
210 | |
|
211 | 0 | configs = linked_list_create(); |
212 | |
|
213 | 0 | this->lock->read_lock(this->lock); |
214 | 0 | enumerator = enumerator_create_nested( |
215 | 0 | this->backends->create_enumerator(this->backends), |
216 | 0 | (void*)ike_enum_create, data, (void*)free); |
217 | |
|
218 | 0 | while (enumerator->enumerate(enumerator, ¤t)) |
219 | 0 | { |
220 | | #if DEBUG_LEVEL >= 2 |
221 | | char *my_addr = current->get_my_addr(current); |
222 | | char *other_addr = current->get_other_addr(current); |
223 | | #endif |
224 | 0 | match = get_ike_match(current, me, other, version); |
225 | 0 | DBG3(DBG_CFG, "ike config match: %d (%s...%s %N)", match, my_addr, |
226 | 0 | other_addr, ike_version_names, current->get_version(current)); |
227 | |
|
228 | 0 | if (match) |
229 | 0 | { |
230 | 0 | DBG2(DBG_CFG, " candidate: %s...%s, prio %d", |
231 | 0 | my_addr, other_addr, match); |
232 | |
|
233 | 0 | INIT(entry, |
234 | 0 | .match = match, |
235 | 0 | .cfg = current->get_ref(current), |
236 | 0 | ); |
237 | 0 | insert_sorted_ike(entry, configs); |
238 | 0 | } |
239 | 0 | } |
240 | 0 | enumerator->destroy(enumerator); |
241 | 0 | this->lock->unlock(this->lock); |
242 | |
|
243 | 0 | return configs; |
244 | 0 | } |
245 | | |
246 | | METHOD(backend_manager_t, get_ike_cfg, ike_cfg_t*, |
247 | | private_backend_manager_t *this, host_t *me, host_t *other, |
248 | | ike_version_t version) |
249 | 0 | { |
250 | 0 | linked_list_t *configs; |
251 | 0 | ike_match_entry_t *entry; |
252 | 0 | ike_cfg_t *found = NULL; |
253 | |
|
254 | 0 | DBG2(DBG_CFG, "looking for an %N config for %H...%H", ike_version_names, |
255 | 0 | version, me, other); |
256 | |
|
257 | 0 | configs = get_matching_ike_cfgs(this, me, other, version); |
258 | 0 | if (configs->get_first(configs, (void**)&entry) == SUCCESS) |
259 | 0 | { |
260 | 0 | found = entry->cfg->get_ref(entry->cfg); |
261 | 0 | DBG2(DBG_CFG, "found matching ike config: %s...%s with prio %d", |
262 | 0 | found->get_my_addr(found), found->get_other_addr(found), |
263 | 0 | entry->match); |
264 | 0 | } |
265 | 0 | ike_match_entry_list_destroy(configs); |
266 | |
|
267 | 0 | return found; |
268 | 0 | } |
269 | | |
270 | | METHOD(backend_manager_t, create_ike_cfg_enumerator, enumerator_t*, |
271 | | private_backend_manager_t *this, host_t *me, host_t *other, |
272 | | ike_version_t version) |
273 | 0 | { |
274 | 0 | linked_list_t *configs; |
275 | |
|
276 | 0 | DBG2(DBG_CFG, "looking for %N configs for %H...%H", ike_version_names, |
277 | 0 | version, me, other); |
278 | |
|
279 | 0 | configs = get_matching_ike_cfgs(this, me, other, version); |
280 | |
|
281 | 0 | return enumerator_create_filter(configs->create_enumerator(configs), |
282 | 0 | ike_enum_filter, configs, |
283 | 0 | ike_match_entry_list_destroy); |
284 | 0 | } |
285 | | |
286 | | /** |
287 | | * Get the best ID match in one of the configs auth_cfg |
288 | | */ |
289 | | static id_match_t get_peer_match(identification_t *id, |
290 | | peer_cfg_t *cfg, bool local) |
291 | 0 | { |
292 | 0 | enumerator_t *enumerator; |
293 | 0 | auth_cfg_t *auth; |
294 | 0 | identification_t *candidate; |
295 | 0 | id_match_t match = ID_MATCH_NONE; |
296 | 0 | char *where DBG_UNUSED = local ? "local" : "remote"; |
297 | |
|
298 | 0 | if (!id) |
299 | 0 | { |
300 | 0 | DBG3(DBG_CFG, " %s id match: %d (%N)", |
301 | 0 | where, ID_MATCH_ANY, id_type_names, ID_ANY); |
302 | 0 | return ID_MATCH_ANY; |
303 | 0 | } |
304 | | |
305 | | /* compare first auth config only */ |
306 | 0 | enumerator = cfg->create_auth_cfg_enumerator(cfg, local); |
307 | 0 | if (enumerator->enumerate(enumerator, &auth)) |
308 | 0 | { |
309 | 0 | candidate = auth->get(auth, AUTH_RULE_IDENTITY); |
310 | 0 | if (candidate) |
311 | 0 | { |
312 | 0 | match = id->matches(id, candidate); |
313 | | /* match vice-versa, as the proposed IDr might be ANY */ |
314 | 0 | if (!match) |
315 | 0 | { |
316 | 0 | match = candidate->matches(candidate, id); |
317 | 0 | } |
318 | 0 | } |
319 | 0 | else |
320 | 0 | { |
321 | 0 | match = ID_MATCH_ANY; |
322 | 0 | } |
323 | 0 | } |
324 | 0 | enumerator->destroy(enumerator); |
325 | |
|
326 | | #if DEBUG_LEVEL >= 3 |
327 | | chunk_t data = id->get_encoding(id); |
328 | | DBG3(DBG_CFG, " %s id match: %d (%N: %#B)", |
329 | | where, match, id_type_names, id->get_type(id), &data); |
330 | | #endif |
331 | 0 | return match; |
332 | 0 | } |
333 | | |
334 | | /** |
335 | | * data to pass nested peer enumerator |
336 | | */ |
337 | | typedef struct { |
338 | | rwlock_t *lock; |
339 | | identification_t *me; |
340 | | identification_t *other; |
341 | | } peer_data_t; |
342 | | |
343 | | /** |
344 | | * list element to help sorting |
345 | | */ |
346 | | typedef struct { |
347 | | id_match_t match_peer; |
348 | | ike_cfg_match_t match_ike; |
349 | | peer_cfg_t *cfg; |
350 | | } match_entry_t; |
351 | | |
352 | | /** |
353 | | * inner enumerator constructor for peer cfgs |
354 | | */ |
355 | | static enumerator_t *peer_enum_create(backend_t *backend, peer_data_t *data) |
356 | 0 | { |
357 | 0 | return backend->create_peer_cfg_enumerator(backend, data->me, data->other); |
358 | 0 | } |
359 | | |
360 | | /** |
361 | | * unlock/cleanup peer enumerator |
362 | | */ |
363 | | static void peer_enum_destroy(peer_data_t *data) |
364 | 0 | { |
365 | 0 | data->lock->unlock(data->lock); |
366 | 0 | free(data); |
367 | 0 | } |
368 | | |
369 | | CALLBACK(peer_enum_filter, bool, |
370 | | linked_list_t *configs, enumerator_t *orig, va_list args) |
371 | 0 | { |
372 | 0 | match_entry_t *entry; |
373 | 0 | peer_cfg_t **out; |
374 | |
|
375 | 0 | VA_ARGS_VGET(args, out); |
376 | |
|
377 | 0 | if (orig->enumerate(orig, &entry)) |
378 | 0 | { |
379 | 0 | *out = entry->cfg; |
380 | 0 | return TRUE; |
381 | 0 | } |
382 | 0 | return FALSE; |
383 | 0 | } |
384 | | |
385 | | CALLBACK(peer_enum_filter_destroy, void, |
386 | | linked_list_t *configs) |
387 | 0 | { |
388 | 0 | match_entry_t *entry; |
389 | |
|
390 | 0 | while (configs->remove_last(configs, (void**)&entry) == SUCCESS) |
391 | 0 | { |
392 | 0 | entry->cfg->destroy(entry->cfg); |
393 | 0 | free(entry); |
394 | 0 | } |
395 | 0 | configs->destroy(configs); |
396 | 0 | } |
397 | | |
398 | | /** |
399 | | * Insert entry into match-sorted list |
400 | | */ |
401 | | static void insert_sorted(match_entry_t *entry, linked_list_t *list) |
402 | 0 | { |
403 | 0 | enumerator_t *enumerator; |
404 | 0 | match_entry_t *current; |
405 | |
|
406 | 0 | enumerator = list->create_enumerator(list); |
407 | 0 | while (enumerator->enumerate(enumerator, ¤t)) |
408 | 0 | { |
409 | 0 | if ((entry->match_ike > current->match_ike && |
410 | 0 | entry->match_peer >= current->match_peer) || |
411 | 0 | (entry->match_ike >= current->match_ike && |
412 | 0 | entry->match_peer > current->match_peer)) |
413 | 0 | { |
414 | 0 | break; |
415 | 0 | } |
416 | 0 | } |
417 | 0 | list->insert_before(list, enumerator, entry); |
418 | 0 | enumerator->destroy(enumerator); |
419 | 0 | } |
420 | | |
421 | | METHOD(backend_manager_t, create_peer_cfg_enumerator, enumerator_t*, |
422 | | private_backend_manager_t *this, host_t *me, host_t *other, |
423 | | identification_t *my_id, identification_t *other_id, ike_version_t version) |
424 | 0 | { |
425 | 0 | enumerator_t *enumerator; |
426 | 0 | peer_data_t *data; |
427 | 0 | peer_cfg_t *cfg; |
428 | 0 | linked_list_t *configs; |
429 | |
|
430 | 0 | INIT(data, |
431 | 0 | .lock = this->lock, |
432 | 0 | .me = my_id, |
433 | 0 | .other = other_id, |
434 | 0 | ); |
435 | | |
436 | | /* create a sorted list with all matches */ |
437 | 0 | this->lock->read_lock(this->lock); |
438 | 0 | enumerator = enumerator_create_nested( |
439 | 0 | this->backends->create_enumerator(this->backends), |
440 | 0 | (void*)peer_enum_create, data, (void*)peer_enum_destroy); |
441 | |
|
442 | 0 | if (!me && !other && !my_id && !other_id) |
443 | 0 | { /* shortcut if we are doing a "listall" */ |
444 | 0 | return enumerator; |
445 | 0 | } |
446 | | |
447 | 0 | configs = linked_list_create(); |
448 | 0 | while (enumerator->enumerate(enumerator, &cfg)) |
449 | 0 | { |
450 | 0 | ike_cfg_t *ike_cfg = cfg->get_ike_cfg(cfg); |
451 | 0 | ike_cfg_match_t match_ike; |
452 | 0 | id_match_t match_peer_me, match_peer_other; |
453 | 0 | match_entry_t *entry; |
454 | |
|
455 | 0 | match_ike = get_ike_match(ike_cfg, me, other, version); |
456 | 0 | DBG3(DBG_CFG, "peer config \"%s\", ike match: %d (%s...%s %N)", |
457 | 0 | cfg->get_name(cfg), match_ike, ike_cfg->get_my_addr(ike_cfg), |
458 | 0 | ike_cfg->get_other_addr(ike_cfg), ike_version_names, |
459 | 0 | ike_cfg->get_version(ike_cfg)); |
460 | |
|
461 | 0 | if (!match_ike) |
462 | 0 | { |
463 | 0 | continue; |
464 | 0 | } |
465 | | |
466 | 0 | match_peer_me = get_peer_match(my_id, cfg, TRUE); |
467 | 0 | if (!match_peer_me) |
468 | 0 | { |
469 | 0 | continue; |
470 | 0 | } |
471 | 0 | match_peer_other = get_peer_match(other_id, cfg, FALSE); |
472 | |
|
473 | 0 | if (match_peer_other) |
474 | 0 | { |
475 | 0 | DBG2(DBG_CFG, " candidate \"%s\", match: %d/%d/%d (me/other/ike)", |
476 | 0 | cfg->get_name(cfg), match_peer_me, match_peer_other, match_ike); |
477 | 0 | INIT(entry, |
478 | 0 | .match_peer = match_peer_me + match_peer_other, |
479 | 0 | .match_ike = match_ike, |
480 | 0 | .cfg = cfg->get_ref(cfg), |
481 | 0 | ); |
482 | 0 | insert_sorted(entry, configs); |
483 | 0 | } |
484 | 0 | } |
485 | 0 | enumerator->destroy(enumerator); |
486 | |
|
487 | 0 | return enumerator_create_filter(configs->create_enumerator(configs), |
488 | 0 | peer_enum_filter, configs, |
489 | 0 | peer_enum_filter_destroy); |
490 | 0 | } |
491 | | |
492 | | METHOD(backend_manager_t, get_peer_cfg_by_name, peer_cfg_t*, |
493 | | private_backend_manager_t *this, char *name) |
494 | 0 | { |
495 | 0 | backend_t *backend; |
496 | 0 | peer_cfg_t *config = NULL; |
497 | 0 | enumerator_t *enumerator; |
498 | |
|
499 | 0 | this->lock->read_lock(this->lock); |
500 | 0 | enumerator = this->backends->create_enumerator(this->backends); |
501 | 0 | while (config == NULL && enumerator->enumerate(enumerator, (void**)&backend)) |
502 | 0 | { |
503 | 0 | config = backend->get_peer_cfg_by_name(backend, name); |
504 | 0 | } |
505 | 0 | enumerator->destroy(enumerator); |
506 | 0 | this->lock->unlock(this->lock); |
507 | 0 | return config; |
508 | 0 | } |
509 | | |
510 | | METHOD(backend_manager_t, remove_backend, void, |
511 | | private_backend_manager_t *this, backend_t *backend) |
512 | 0 | { |
513 | 0 | this->lock->write_lock(this->lock); |
514 | 0 | this->backends->remove(this->backends, backend, NULL); |
515 | 0 | this->lock->unlock(this->lock); |
516 | 0 | } |
517 | | |
518 | | METHOD(backend_manager_t, add_backend, void, |
519 | | private_backend_manager_t *this, backend_t *backend) |
520 | 0 | { |
521 | 0 | this->lock->write_lock(this->lock); |
522 | 0 | this->backends->insert_last(this->backends, backend); |
523 | 0 | this->lock->unlock(this->lock); |
524 | 0 | } |
525 | | |
526 | | METHOD(backend_manager_t, destroy, void, |
527 | | private_backend_manager_t *this) |
528 | 0 | { |
529 | 0 | this->backends->destroy(this->backends); |
530 | 0 | this->lock->destroy(this->lock); |
531 | 0 | free(this); |
532 | 0 | } |
533 | | |
534 | | /* |
535 | | * Described in header |
536 | | */ |
537 | | backend_manager_t *backend_manager_create() |
538 | 2 | { |
539 | 2 | private_backend_manager_t *this; |
540 | | |
541 | 2 | INIT(this, |
542 | 2 | .public = { |
543 | 2 | .get_ike_cfg = _get_ike_cfg, |
544 | 2 | .create_ike_cfg_enumerator = _create_ike_cfg_enumerator, |
545 | 2 | .get_peer_cfg_by_name = _get_peer_cfg_by_name, |
546 | 2 | .create_peer_cfg_enumerator = _create_peer_cfg_enumerator, |
547 | 2 | .add_backend = _add_backend, |
548 | 2 | .remove_backend = _remove_backend, |
549 | 2 | .destroy = _destroy, |
550 | 2 | }, |
551 | 2 | .backends = linked_list_create(), |
552 | 2 | .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), |
553 | 2 | ); |
554 | | |
555 | 2 | return &this->public; |
556 | 2 | } |