f9fee5e7ed126e47413778fd73a279353446b790
[strongswan.git] / src / libcharon / sa / trap_manager.c
1 /*
2 * Copyright (C) 2011-2015 Tobias Brunner
3 * Copyright (C) 2009 Martin Willi
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #include "trap_manager.h"
18
19 #include <daemon.h>
20 #include <threading/mutex.h>
21 #include <threading/rwlock.h>
22 #include <threading/rwlock_condvar.h>
23 #include <collections/linked_list.h>
24
25 #define INSTALL_DISABLED ((u_int)~0)
26
27 typedef struct private_trap_manager_t private_trap_manager_t;
28 typedef struct trap_listener_t trap_listener_t;
29
30 /**
31 * listener to track acquires
32 */
33 struct trap_listener_t {
34
35 /**
36 * Implements listener interface
37 */
38 listener_t listener;
39
40 /**
41 * points to trap_manager
42 */
43 private_trap_manager_t *traps;
44 };
45
46 /**
47 * Private data of an trap_manager_t object.
48 */
49 struct private_trap_manager_t {
50
51 /**
52 * Public trap_manager_t interface.
53 */
54 trap_manager_t public;
55
56 /**
57 * Installed traps, as entry_t
58 */
59 linked_list_t *traps;
60
61 /**
62 * read write lock for traps list
63 */
64 rwlock_t *lock;
65
66 /**
67 * listener to track acquiring IKE_SAs
68 */
69 trap_listener_t listener;
70
71 /**
72 * list of acquires we currently handle
73 */
74 linked_list_t *acquires;
75
76 /**
77 * mutex for list of acquires
78 */
79 mutex_t *mutex;
80
81 /**
82 * number of threads currently installing trap policies, or INSTALL_DISABLED
83 */
84 u_int installing;
85
86 /**
87 * condvar to signal trap policy installation
88 */
89 rwlock_condvar_t *condvar;
90
91 /**
92 * Whether to ignore traffic selectors from acquires
93 */
94 bool ignore_acquire_ts;
95 };
96
97 /**
98 * A installed trap entry
99 */
100 typedef struct {
101 /** name of the trapped CHILD_SA */
102 char *name;
103 /** ref to peer_cfg to initiate */
104 peer_cfg_t *peer_cfg;
105 /** ref to instantiated CHILD_SA (i.e the trap policy) */
106 child_sa_t *child_sa;
107 /** TRUE in case of wildcard Transport Mode SA */
108 bool wildcard;
109 } entry_t;
110
111 /**
112 * A handled acquire
113 */
114 typedef struct {
115 /** pending IKE_SA connecting upon acquire */
116 ike_sa_t *ike_sa;
117 /** reqid of pending trap policy */
118 uint32_t reqid;
119 /** destination address (wildcard case) */
120 host_t *dst;
121 } acquire_t;
122
123 /**
124 * actually uninstall and destroy an installed entry
125 */
126 static void destroy_entry(entry_t *this)
127 {
128 this->child_sa->destroy(this->child_sa);
129 this->peer_cfg->destroy(this->peer_cfg);
130 free(this->name);
131 free(this);
132 }
133
134 /**
135 * destroy a cached acquire entry
136 */
137 static void destroy_acquire(acquire_t *this)
138 {
139 DESTROY_IF(this->dst);
140 free(this);
141 }
142
143 CALLBACK(acquire_by_reqid, bool,
144 acquire_t *this, va_list args)
145 {
146 uint32_t reqid;
147
148 VA_ARGS_VGET(args, reqid);
149 return this->reqid == reqid;
150 }
151
152 CALLBACK(acquire_by_dst, bool,
153 acquire_t *this, va_list args)
154 {
155 host_t *dst;
156
157 VA_ARGS_VGET(args, dst);
158 return this->dst && this->dst->ip_equals(this->dst, dst);
159 }
160
161 METHOD(trap_manager_t, install, uint32_t,
162 private_trap_manager_t *this, peer_cfg_t *peer, child_cfg_t *child,
163 uint32_t reqid)
164 {
165 entry_t *entry, *found = NULL;
166 ike_cfg_t *ike_cfg;
167 child_sa_t *child_sa;
168 host_t *me, *other;
169 linked_list_t *my_ts, *other_ts, *list;
170 enumerator_t *enumerator;
171 status_t status;
172 linked_list_t *proposals;
173 proposal_t *proposal;
174 protocol_id_t proto = PROTO_ESP;
175 bool wildcard = FALSE;
176
177 /* try to resolve addresses */
178 ike_cfg = peer->get_ike_cfg(peer);
179 other = ike_cfg->resolve_other(ike_cfg, AF_UNSPEC);
180 if (other && other->is_anyaddr(other) &&
181 child->get_mode(child) == MODE_TRANSPORT)
182 {
183 /* allow wildcard for Transport Mode SAs */
184 me = host_create_any(other->get_family(other));
185 wildcard = TRUE;
186 }
187 else if (!other || other->is_anyaddr(other))
188 {
189 DESTROY_IF(other);
190 DBG1(DBG_CFG, "installing trap failed, remote address unknown");
191 return 0;
192 }
193 else
194 {
195 me = ike_cfg->resolve_me(ike_cfg, other->get_family(other));
196 if (!me || me->is_anyaddr(me))
197 {
198 DESTROY_IF(me);
199 me = charon->kernel->get_source_addr(charon->kernel, other, NULL);
200 if (!me)
201 {
202 me = host_create_any(other->get_family(other));
203 }
204 me->set_port(me, ike_cfg->get_my_port(ike_cfg));
205 }
206 }
207
208 this->lock->write_lock(this->lock);
209 if (this->installing == INSTALL_DISABLED)
210 { /* flush() has been called */
211 this->lock->unlock(this->lock);
212 other->destroy(other);
213 me->destroy(me);
214 return 0;
215 }
216 enumerator = this->traps->create_enumerator(this->traps);
217 while (enumerator->enumerate(enumerator, &entry))
218 {
219 if (streq(entry->name, child->get_name(child)))
220 {
221 found = entry;
222 if (entry->child_sa)
223 { /* replace it with an updated version, if already installed */
224 this->traps->remove_at(this->traps, enumerator);
225 }
226 break;
227 }
228 }
229 enumerator->destroy(enumerator);
230
231 if (found)
232 {
233 if (!found->child_sa)
234 {
235 DBG1(DBG_CFG, "CHILD_SA '%s' is already being routed", found->name);
236 this->lock->unlock(this->lock);
237 other->destroy(other);
238 me->destroy(me);
239 return 0;
240 }
241 /* config might have changed so update everything */
242 DBG1(DBG_CFG, "updating already routed CHILD_SA '%s'", found->name);
243 reqid = found->child_sa->get_reqid(found->child_sa);
244 }
245
246 INIT(entry,
247 .name = strdup(child->get_name(child)),
248 .peer_cfg = peer->get_ref(peer),
249 .wildcard = wildcard,
250 );
251 this->traps->insert_first(this->traps, entry);
252 this->installing++;
253 /* don't hold lock while creating CHILD_SA and installing policies */
254 this->lock->unlock(this->lock);
255
256 /* create and route CHILD_SA */
257 child_sa = child_sa_create(me, other, child, reqid, FALSE, 0, 0);
258
259 list = linked_list_create_with_items(me, NULL);
260 my_ts = child->get_traffic_selectors(child, TRUE, NULL, list);
261 list->destroy_offset(list, offsetof(host_t, destroy));
262
263 list = linked_list_create_with_items(other, NULL);
264 other_ts = child->get_traffic_selectors(child, FALSE, NULL, list);
265 list->destroy_offset(list, offsetof(host_t, destroy));
266
267 /* We don't know the finally negotiated protocol (ESP|AH), we install
268 * the SA with the protocol of the first proposal */
269 proposals = child->get_proposals(child, TRUE);
270 if (proposals->get_first(proposals, (void**)&proposal) == SUCCESS)
271 {
272 proto = proposal->get_protocol(proposal);
273 }
274 proposals->destroy_offset(proposals, offsetof(proposal_t, destroy));
275 child_sa->set_protocol(child_sa, proto);
276 child_sa->set_mode(child_sa, child->get_mode(child));
277 child_sa->set_policies(child_sa, my_ts, other_ts);
278 status = child_sa->install_policies(child_sa);
279 my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy));
280 other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy));
281 if (status != SUCCESS)
282 {
283 DBG1(DBG_CFG, "installing trap failed");
284 this->lock->write_lock(this->lock);
285 this->traps->remove(this->traps, entry, NULL);
286 this->lock->unlock(this->lock);
287 entry->child_sa = child_sa;
288 destroy_entry(entry);
289 reqid = 0;
290 }
291 else
292 {
293 reqid = child_sa->get_reqid(child_sa);
294 this->lock->write_lock(this->lock);
295 entry->child_sa = child_sa;
296 this->lock->unlock(this->lock);
297 }
298 if (found)
299 {
300 destroy_entry(found);
301 }
302 this->lock->write_lock(this->lock);
303 /* do this at the end, so entries created temporarily are also destroyed */
304 this->installing--;
305 this->condvar->signal(this->condvar);
306 this->lock->unlock(this->lock);
307 return reqid;
308 }
309
310 METHOD(trap_manager_t, uninstall, bool,
311 private_trap_manager_t *this, uint32_t reqid)
312 {
313 enumerator_t *enumerator;
314 entry_t *entry, *found = NULL;
315
316 this->lock->write_lock(this->lock);
317 enumerator = this->traps->create_enumerator(this->traps);
318 while (enumerator->enumerate(enumerator, &entry))
319 {
320 if (entry->child_sa &&
321 entry->child_sa->get_reqid(entry->child_sa) == reqid)
322 {
323 this->traps->remove_at(this->traps, enumerator);
324 found = entry;
325 break;
326 }
327 }
328 enumerator->destroy(enumerator);
329 this->lock->unlock(this->lock);
330
331 if (!found)
332 {
333 DBG1(DBG_CFG, "trap %d not found to uninstall", reqid);
334 return FALSE;
335 }
336 destroy_entry(found);
337 return TRUE;
338 }
339
340 CALLBACK(trap_filter, bool,
341 rwlock_t *lock, enumerator_t *orig, va_list args)
342 {
343 entry_t *entry;
344 peer_cfg_t **peer_cfg;
345 child_sa_t **child_sa;
346
347 VA_ARGS_VGET(args, peer_cfg, child_sa);
348
349 while (orig->enumerate(orig, &entry))
350 {
351 if (!entry->child_sa)
352 { /* skip entries that are currently being installed */
353 continue;
354 }
355 if (peer_cfg)
356 {
357 *peer_cfg = entry->peer_cfg;
358 }
359 if (child_sa)
360 {
361 *child_sa = entry->child_sa;
362 }
363 return TRUE;
364 }
365 return FALSE;
366 }
367
368 METHOD(trap_manager_t, create_enumerator, enumerator_t*,
369 private_trap_manager_t *this)
370 {
371 this->lock->read_lock(this->lock);
372 return enumerator_create_filter(this->traps->create_enumerator(this->traps),
373 trap_filter, this->lock,
374 (void*)this->lock->unlock);
375 }
376
377 METHOD(trap_manager_t, find_reqid, uint32_t,
378 private_trap_manager_t *this, child_cfg_t *child)
379 {
380 enumerator_t *enumerator;
381 entry_t *entry;
382 uint32_t reqid = 0;
383
384 this->lock->read_lock(this->lock);
385 enumerator = this->traps->create_enumerator(this->traps);
386 while (enumerator->enumerate(enumerator, &entry))
387 {
388 if (streq(entry->name, child->get_name(child)))
389 {
390 if (entry->child_sa)
391 {
392 reqid = entry->child_sa->get_reqid(entry->child_sa);
393 }
394 break;
395 }
396 }
397 enumerator->destroy(enumerator);
398 this->lock->unlock(this->lock);
399 return reqid;
400 }
401
402 METHOD(trap_manager_t, acquire, void,
403 private_trap_manager_t *this, uint32_t reqid,
404 traffic_selector_t *src, traffic_selector_t *dst)
405 {
406 enumerator_t *enumerator;
407 entry_t *entry, *found = NULL;
408 acquire_t *acquire;
409 peer_cfg_t *peer;
410 child_cfg_t *child;
411 ike_sa_t *ike_sa;
412 host_t *host;
413 bool wildcard, ignore = FALSE;
414
415 this->lock->read_lock(this->lock);
416 enumerator = this->traps->create_enumerator(this->traps);
417 while (enumerator->enumerate(enumerator, &entry))
418 {
419 if (entry->child_sa &&
420 entry->child_sa->get_reqid(entry->child_sa) == reqid)
421 {
422 found = entry;
423 break;
424 }
425 }
426 enumerator->destroy(enumerator);
427
428 if (!found)
429 {
430 DBG1(DBG_CFG, "trap not found, unable to acquire reqid %d", reqid);
431 this->lock->unlock(this->lock);
432 return;
433 }
434 reqid = found->child_sa->get_reqid(found->child_sa);
435 wildcard = found->wildcard;
436
437 this->mutex->lock(this->mutex);
438 if (wildcard)
439 { /* for wildcard acquires we check that we don't have a pending acquire
440 * with the same peer */
441 uint8_t mask;
442
443 dst->to_subnet(dst, &host, &mask);
444 if (this->acquires->find_first(this->acquires, acquire_by_dst,
445 (void**)&acquire, host))
446 {
447 host->destroy(host);
448 ignore = TRUE;
449 }
450 else
451 {
452 INIT(acquire,
453 .dst = host,
454 .reqid = reqid,
455 );
456 this->acquires->insert_last(this->acquires, acquire);
457 }
458 }
459 else
460 {
461 if (this->acquires->find_first(this->acquires, acquire_by_reqid,
462 (void**)&acquire, reqid))
463 {
464 ignore = TRUE;
465 }
466 else
467 {
468 INIT(acquire,
469 .reqid = reqid,
470 );
471 this->acquires->insert_last(this->acquires, acquire);
472 }
473 }
474 this->mutex->unlock(this->mutex);
475 if (ignore)
476 {
477 DBG1(DBG_CFG, "ignoring acquire, connection attempt pending");
478 this->lock->unlock(this->lock);
479 return;
480 }
481 peer = found->peer_cfg->get_ref(found->peer_cfg);
482 child = found->child_sa->get_config(found->child_sa);
483 child = child->get_ref(child);
484 /* don't hold the lock while checking out the IKE_SA */
485 this->lock->unlock(this->lock);
486
487 if (wildcard)
488 { /* the peer config would match IKE_SAs with other peers */
489 ike_sa = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager,
490 peer->get_ike_version(peer), TRUE);
491 if (ike_sa)
492 {
493 ike_cfg_t *ike_cfg;
494 uint16_t port;
495 uint8_t mask;
496
497 ike_sa->set_peer_cfg(ike_sa, peer);
498 ike_cfg = ike_sa->get_ike_cfg(ike_sa);
499
500 port = ike_cfg->get_other_port(ike_cfg);
501 dst->to_subnet(dst, &host, &mask);
502 host->set_port(host, port);
503 ike_sa->set_other_host(ike_sa, host);
504
505 port = ike_cfg->get_my_port(ike_cfg);
506 src->to_subnet(src, &host, &mask);
507 host->set_port(host, port);
508 ike_sa->set_my_host(ike_sa, host);
509
510 charon->bus->set_sa(charon->bus, ike_sa);
511 }
512 }
513 else
514 {
515 ike_sa = charon->ike_sa_manager->checkout_by_config(
516 charon->ike_sa_manager, peer);
517 }
518 if (ike_sa)
519 {
520 if (ike_sa->get_peer_cfg(ike_sa) == NULL)
521 {
522 ike_sa->set_peer_cfg(ike_sa, peer);
523 }
524 if (this->ignore_acquire_ts || ike_sa->get_version(ike_sa) == IKEV1)
525 { /* in IKEv1, don't prepend the acquiring packet TS, as we only
526 * have a single TS that we can establish in a Quick Mode. */
527 src = dst = NULL;
528 }
529
530 this->mutex->lock(this->mutex);
531 acquire->ike_sa = ike_sa;
532 this->mutex->unlock(this->mutex);
533
534 if (ike_sa->initiate(ike_sa, child, reqid, src, dst) != DESTROY_ME)
535 {
536 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
537 }
538 else
539 {
540 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
541 ike_sa);
542 }
543 }
544 else
545 {
546 this->mutex->lock(this->mutex);
547 this->acquires->remove(this->acquires, acquire, NULL);
548 this->mutex->unlock(this->mutex);
549 destroy_acquire(acquire);
550 child->destroy(child);
551 }
552 peer->destroy(peer);
553 }
554
555 /**
556 * Complete the acquire, if successful or failed
557 */
558 static void complete(private_trap_manager_t *this, ike_sa_t *ike_sa,
559 child_sa_t *child_sa)
560 {
561 enumerator_t *enumerator;
562 acquire_t *acquire;
563
564 this->mutex->lock(this->mutex);
565 enumerator = this->acquires->create_enumerator(this->acquires);
566 while (enumerator->enumerate(enumerator, &acquire))
567 {
568 if (!acquire->ike_sa || acquire->ike_sa != ike_sa)
569 {
570 continue;
571 }
572 if (child_sa)
573 {
574 if (acquire->dst)
575 {
576 /* since every wildcard acquire results in a separate IKE_SA
577 * there is no need to compare the destination address */
578 }
579 else if (child_sa->get_reqid(child_sa) != acquire->reqid)
580 {
581 continue;
582 }
583 }
584 this->acquires->remove_at(this->acquires, enumerator);
585 destroy_acquire(acquire);
586 }
587 enumerator->destroy(enumerator);
588 this->mutex->unlock(this->mutex);
589 }
590
591 METHOD(listener_t, ike_state_change, bool,
592 trap_listener_t *listener, ike_sa_t *ike_sa, ike_sa_state_t state)
593 {
594 switch (state)
595 {
596 case IKE_DESTROYING:
597 complete(listener->traps, ike_sa, NULL);
598 return TRUE;
599 default:
600 return TRUE;
601 }
602 }
603
604 METHOD(listener_t, child_state_change, bool,
605 trap_listener_t *listener, ike_sa_t *ike_sa, child_sa_t *child_sa,
606 child_sa_state_t state)
607 {
608 switch (state)
609 {
610 case CHILD_INSTALLED:
611 case CHILD_DESTROYING:
612 complete(listener->traps, ike_sa, child_sa);
613 return TRUE;
614 default:
615 return TRUE;
616 }
617 }
618
619 METHOD(trap_manager_t, flush, void,
620 private_trap_manager_t *this)
621 {
622 this->lock->write_lock(this->lock);
623 while (this->installing)
624 {
625 this->condvar->wait(this->condvar, this->lock);
626 }
627 this->traps->destroy_function(this->traps, (void*)destroy_entry);
628 this->traps = linked_list_create();
629 this->installing = INSTALL_DISABLED;
630 this->lock->unlock(this->lock);
631 }
632
633 METHOD(trap_manager_t, destroy, void,
634 private_trap_manager_t *this)
635 {
636 charon->bus->remove_listener(charon->bus, &this->listener.listener);
637 this->traps->destroy_function(this->traps, (void*)destroy_entry);
638 this->acquires->destroy_function(this->acquires, (void*)destroy_acquire);
639 this->condvar->destroy(this->condvar);
640 this->mutex->destroy(this->mutex);
641 this->lock->destroy(this->lock);
642 free(this);
643 }
644
645 /**
646 * See header
647 */
648 trap_manager_t *trap_manager_create(void)
649 {
650 private_trap_manager_t *this;
651
652 INIT(this,
653 .public = {
654 .install = _install,
655 .uninstall = _uninstall,
656 .create_enumerator = _create_enumerator,
657 .find_reqid = _find_reqid,
658 .acquire = _acquire,
659 .flush = _flush,
660 .destroy = _destroy,
661 },
662 .listener = {
663 .traps = this,
664 .listener = {
665 .ike_state_change = _ike_state_change,
666 .child_state_change = _child_state_change,
667 },
668 },
669 .traps = linked_list_create(),
670 .acquires = linked_list_create(),
671 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
672 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
673 .condvar = rwlock_condvar_create(),
674 .ignore_acquire_ts = lib->settings->get_bool(lib->settings,
675 "%s.ignore_acquire_ts", FALSE, lib->ns),
676 );
677 charon->bus->add_listener(charon->bus, &this->listener.listener);
678
679 return &this->public;
680 }