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