trap-manager: Don't require that remote is resolvable during installation
[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 /**
162 * Check if any remote TS are dynamic
163 */
164 static bool dynamic_remote_ts(child_cfg_t *child)
165 {
166 enumerator_t *enumerator;
167 linked_list_t *other_ts;
168 traffic_selector_t *ts;
169 bool found = FALSE;
170
171 other_ts = child->get_traffic_selectors(child, FALSE, NULL, NULL);
172 enumerator = other_ts->create_enumerator(other_ts);
173 while (enumerator->enumerate(enumerator, &ts))
174 {
175 if (ts->is_dynamic(ts))
176 {
177 found = TRUE;
178 break;
179 }
180 }
181 enumerator->destroy(enumerator);
182 other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy));
183 return found;
184 }
185
186 METHOD(trap_manager_t, install, uint32_t,
187 private_trap_manager_t *this, peer_cfg_t *peer, child_cfg_t *child,
188 uint32_t reqid)
189 {
190 entry_t *entry, *found = NULL;
191 ike_cfg_t *ike_cfg;
192 child_sa_t *child_sa;
193 host_t *me, *other;
194 linked_list_t *my_ts, *other_ts, *list;
195 enumerator_t *enumerator;
196 status_t status;
197 linked_list_t *proposals;
198 proposal_t *proposal;
199 protocol_id_t proto = PROTO_ESP;
200 bool wildcard = FALSE;
201
202 /* try to resolve addresses */
203 ike_cfg = peer->get_ike_cfg(peer);
204 other = ike_cfg->resolve_other(ike_cfg, AF_UNSPEC);
205 if (other && other->is_anyaddr(other) &&
206 child->get_mode(child) == MODE_TRANSPORT)
207 {
208 /* allow wildcard for Transport Mode SAs */
209 me = host_create_any(other->get_family(other));
210 wildcard = TRUE;
211 }
212 else if (other && other->is_anyaddr(other))
213 {
214 other->destroy(other);
215 DBG1(DBG_CFG, "installing trap failed, remote address unknown");
216 return 0;
217 }
218 else
219 { /* depending on the traffic selectors we don't really need a remote
220 * host yet, but we might fail later if no IP can be resolved */
221 if (!other && dynamic_remote_ts(child))
222 { /* with dynamic TS we do need a host, otherwise 0.0.0.0/0 is used,
223 * which is probably not what users expect*/
224 DBG1(DBG_CFG, "installing trap failed, remote address unknown with "
225 "dynamic traffic selector");
226 return 0;
227 }
228 me = ike_cfg->resolve_me(ike_cfg, other ? other->get_family(other)
229 : AF_UNSPEC);
230 if (!other)
231 {
232 other = host_create_any(me ? me->get_family(me) : AF_INET);
233 }
234 other->set_port(other, ike_cfg->get_other_port(ike_cfg));
235 if ((!me || me->is_anyaddr(me)) && !other->is_anyaddr(other))
236 {
237 DESTROY_IF(me);
238 me = charon->kernel->get_source_addr(charon->kernel, other, NULL);
239 }
240 if (!me)
241 {
242 me = host_create_any(other->get_family(other));
243 }
244 me->set_port(me, ike_cfg->get_my_port(ike_cfg));
245 }
246
247 this->lock->write_lock(this->lock);
248 if (this->installing == INSTALL_DISABLED)
249 { /* flush() has been called */
250 this->lock->unlock(this->lock);
251 other->destroy(other);
252 me->destroy(me);
253 return 0;
254 }
255 enumerator = this->traps->create_enumerator(this->traps);
256 while (enumerator->enumerate(enumerator, &entry))
257 {
258 if (streq(entry->name, child->get_name(child)))
259 {
260 found = entry;
261 if (entry->child_sa)
262 { /* replace it with an updated version, if already installed */
263 this->traps->remove_at(this->traps, enumerator);
264 }
265 break;
266 }
267 }
268 enumerator->destroy(enumerator);
269
270 if (found)
271 {
272 if (!found->child_sa)
273 {
274 DBG1(DBG_CFG, "CHILD_SA '%s' is already being routed", found->name);
275 this->lock->unlock(this->lock);
276 other->destroy(other);
277 me->destroy(me);
278 return 0;
279 }
280 /* config might have changed so update everything */
281 DBG1(DBG_CFG, "updating already routed CHILD_SA '%s'", found->name);
282 reqid = found->child_sa->get_reqid(found->child_sa);
283 }
284
285 INIT(entry,
286 .name = strdup(child->get_name(child)),
287 .peer_cfg = peer->get_ref(peer),
288 .wildcard = wildcard,
289 );
290 this->traps->insert_first(this->traps, entry);
291 this->installing++;
292 /* don't hold lock while creating CHILD_SA and installing policies */
293 this->lock->unlock(this->lock);
294
295 /* create and route CHILD_SA */
296 child_sa = child_sa_create(me, other, child, reqid, FALSE, 0, 0);
297
298 list = linked_list_create_with_items(me, NULL);
299 my_ts = child->get_traffic_selectors(child, TRUE, NULL, list);
300 list->destroy_offset(list, offsetof(host_t, destroy));
301
302 list = linked_list_create_with_items(other, NULL);
303 other_ts = child->get_traffic_selectors(child, FALSE, NULL, list);
304 list->destroy_offset(list, offsetof(host_t, destroy));
305
306 /* We don't know the finally negotiated protocol (ESP|AH), we install
307 * the SA with the protocol of the first proposal */
308 proposals = child->get_proposals(child, TRUE);
309 if (proposals->get_first(proposals, (void**)&proposal) == SUCCESS)
310 {
311 proto = proposal->get_protocol(proposal);
312 }
313 proposals->destroy_offset(proposals, offsetof(proposal_t, destroy));
314 child_sa->set_protocol(child_sa, proto);
315 child_sa->set_mode(child_sa, child->get_mode(child));
316 child_sa->set_policies(child_sa, my_ts, other_ts);
317 status = child_sa->install_policies(child_sa);
318 my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy));
319 other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy));
320 if (status != SUCCESS)
321 {
322 DBG1(DBG_CFG, "installing trap failed");
323 this->lock->write_lock(this->lock);
324 this->traps->remove(this->traps, entry, NULL);
325 this->lock->unlock(this->lock);
326 entry->child_sa = child_sa;
327 destroy_entry(entry);
328 reqid = 0;
329 }
330 else
331 {
332 reqid = child_sa->get_reqid(child_sa);
333 this->lock->write_lock(this->lock);
334 entry->child_sa = child_sa;
335 this->lock->unlock(this->lock);
336 }
337 if (found)
338 {
339 destroy_entry(found);
340 }
341 this->lock->write_lock(this->lock);
342 /* do this at the end, so entries created temporarily are also destroyed */
343 this->installing--;
344 this->condvar->signal(this->condvar);
345 this->lock->unlock(this->lock);
346 return reqid;
347 }
348
349 METHOD(trap_manager_t, uninstall, bool,
350 private_trap_manager_t *this, uint32_t reqid)
351 {
352 enumerator_t *enumerator;
353 entry_t *entry, *found = NULL;
354
355 this->lock->write_lock(this->lock);
356 enumerator = this->traps->create_enumerator(this->traps);
357 while (enumerator->enumerate(enumerator, &entry))
358 {
359 if (entry->child_sa &&
360 entry->child_sa->get_reqid(entry->child_sa) == reqid)
361 {
362 this->traps->remove_at(this->traps, enumerator);
363 found = entry;
364 break;
365 }
366 }
367 enumerator->destroy(enumerator);
368 this->lock->unlock(this->lock);
369
370 if (!found)
371 {
372 DBG1(DBG_CFG, "trap %d not found to uninstall", reqid);
373 return FALSE;
374 }
375 destroy_entry(found);
376 return TRUE;
377 }
378
379 CALLBACK(trap_filter, bool,
380 rwlock_t *lock, enumerator_t *orig, va_list args)
381 {
382 entry_t *entry;
383 peer_cfg_t **peer_cfg;
384 child_sa_t **child_sa;
385
386 VA_ARGS_VGET(args, peer_cfg, child_sa);
387
388 while (orig->enumerate(orig, &entry))
389 {
390 if (!entry->child_sa)
391 { /* skip entries that are currently being installed */
392 continue;
393 }
394 if (peer_cfg)
395 {
396 *peer_cfg = entry->peer_cfg;
397 }
398 if (child_sa)
399 {
400 *child_sa = entry->child_sa;
401 }
402 return TRUE;
403 }
404 return FALSE;
405 }
406
407 METHOD(trap_manager_t, create_enumerator, enumerator_t*,
408 private_trap_manager_t *this)
409 {
410 this->lock->read_lock(this->lock);
411 return enumerator_create_filter(this->traps->create_enumerator(this->traps),
412 trap_filter, this->lock,
413 (void*)this->lock->unlock);
414 }
415
416 METHOD(trap_manager_t, find_reqid, uint32_t,
417 private_trap_manager_t *this, child_cfg_t *child)
418 {
419 enumerator_t *enumerator;
420 entry_t *entry;
421 uint32_t reqid = 0;
422
423 this->lock->read_lock(this->lock);
424 enumerator = this->traps->create_enumerator(this->traps);
425 while (enumerator->enumerate(enumerator, &entry))
426 {
427 if (streq(entry->name, child->get_name(child)))
428 {
429 if (entry->child_sa)
430 {
431 reqid = entry->child_sa->get_reqid(entry->child_sa);
432 }
433 break;
434 }
435 }
436 enumerator->destroy(enumerator);
437 this->lock->unlock(this->lock);
438 return reqid;
439 }
440
441 METHOD(trap_manager_t, acquire, void,
442 private_trap_manager_t *this, uint32_t reqid,
443 traffic_selector_t *src, traffic_selector_t *dst)
444 {
445 enumerator_t *enumerator;
446 entry_t *entry, *found = NULL;
447 acquire_t *acquire;
448 peer_cfg_t *peer;
449 child_cfg_t *child;
450 ike_sa_t *ike_sa;
451 host_t *host;
452 bool wildcard, ignore = FALSE;
453
454 this->lock->read_lock(this->lock);
455 enumerator = this->traps->create_enumerator(this->traps);
456 while (enumerator->enumerate(enumerator, &entry))
457 {
458 if (entry->child_sa &&
459 entry->child_sa->get_reqid(entry->child_sa) == reqid)
460 {
461 found = entry;
462 break;
463 }
464 }
465 enumerator->destroy(enumerator);
466
467 if (!found)
468 {
469 DBG1(DBG_CFG, "trap not found, unable to acquire reqid %d", reqid);
470 this->lock->unlock(this->lock);
471 return;
472 }
473 reqid = found->child_sa->get_reqid(found->child_sa);
474 wildcard = found->wildcard;
475
476 this->mutex->lock(this->mutex);
477 if (wildcard)
478 { /* for wildcard acquires we check that we don't have a pending acquire
479 * with the same peer */
480 uint8_t mask;
481
482 dst->to_subnet(dst, &host, &mask);
483 if (this->acquires->find_first(this->acquires, acquire_by_dst,
484 (void**)&acquire, host))
485 {
486 host->destroy(host);
487 ignore = TRUE;
488 }
489 else
490 {
491 INIT(acquire,
492 .dst = host,
493 .reqid = reqid,
494 );
495 this->acquires->insert_last(this->acquires, acquire);
496 }
497 }
498 else
499 {
500 if (this->acquires->find_first(this->acquires, acquire_by_reqid,
501 (void**)&acquire, reqid))
502 {
503 ignore = TRUE;
504 }
505 else
506 {
507 INIT(acquire,
508 .reqid = reqid,
509 );
510 this->acquires->insert_last(this->acquires, acquire);
511 }
512 }
513 this->mutex->unlock(this->mutex);
514 if (ignore)
515 {
516 DBG1(DBG_CFG, "ignoring acquire, connection attempt pending");
517 this->lock->unlock(this->lock);
518 return;
519 }
520 peer = found->peer_cfg->get_ref(found->peer_cfg);
521 child = found->child_sa->get_config(found->child_sa);
522 child = child->get_ref(child);
523 /* don't hold the lock while checking out the IKE_SA */
524 this->lock->unlock(this->lock);
525
526 if (wildcard)
527 { /* the peer config would match IKE_SAs with other peers */
528 ike_sa = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager,
529 peer->get_ike_version(peer), TRUE);
530 if (ike_sa)
531 {
532 ike_cfg_t *ike_cfg;
533 uint16_t port;
534 uint8_t mask;
535
536 ike_sa->set_peer_cfg(ike_sa, peer);
537 ike_cfg = ike_sa->get_ike_cfg(ike_sa);
538
539 port = ike_cfg->get_other_port(ike_cfg);
540 dst->to_subnet(dst, &host, &mask);
541 host->set_port(host, port);
542 ike_sa->set_other_host(ike_sa, host);
543
544 port = ike_cfg->get_my_port(ike_cfg);
545 src->to_subnet(src, &host, &mask);
546 host->set_port(host, port);
547 ike_sa->set_my_host(ike_sa, host);
548
549 charon->bus->set_sa(charon->bus, ike_sa);
550 }
551 }
552 else
553 {
554 ike_sa = charon->ike_sa_manager->checkout_by_config(
555 charon->ike_sa_manager, peer);
556 }
557 if (ike_sa)
558 {
559 if (ike_sa->get_peer_cfg(ike_sa) == NULL)
560 {
561 ike_sa->set_peer_cfg(ike_sa, peer);
562 }
563 if (this->ignore_acquire_ts || ike_sa->get_version(ike_sa) == IKEV1)
564 { /* in IKEv1, don't prepend the acquiring packet TS, as we only
565 * have a single TS that we can establish in a Quick Mode. */
566 src = dst = NULL;
567 }
568
569 this->mutex->lock(this->mutex);
570 acquire->ike_sa = ike_sa;
571 this->mutex->unlock(this->mutex);
572
573 if (ike_sa->initiate(ike_sa, child, reqid, src, dst) != DESTROY_ME)
574 {
575 charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
576 }
577 else
578 {
579 charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
580 ike_sa);
581 }
582 }
583 else
584 {
585 this->mutex->lock(this->mutex);
586 this->acquires->remove(this->acquires, acquire, NULL);
587 this->mutex->unlock(this->mutex);
588 destroy_acquire(acquire);
589 child->destroy(child);
590 }
591 peer->destroy(peer);
592 }
593
594 /**
595 * Complete the acquire, if successful or failed
596 */
597 static void complete(private_trap_manager_t *this, ike_sa_t *ike_sa,
598 child_sa_t *child_sa)
599 {
600 enumerator_t *enumerator;
601 acquire_t *acquire;
602
603 this->mutex->lock(this->mutex);
604 enumerator = this->acquires->create_enumerator(this->acquires);
605 while (enumerator->enumerate(enumerator, &acquire))
606 {
607 if (!acquire->ike_sa || acquire->ike_sa != ike_sa)
608 {
609 continue;
610 }
611 if (child_sa)
612 {
613 if (acquire->dst)
614 {
615 /* since every wildcard acquire results in a separate IKE_SA
616 * there is no need to compare the destination address */
617 }
618 else if (child_sa->get_reqid(child_sa) != acquire->reqid)
619 {
620 continue;
621 }
622 }
623 this->acquires->remove_at(this->acquires, enumerator);
624 destroy_acquire(acquire);
625 }
626 enumerator->destroy(enumerator);
627 this->mutex->unlock(this->mutex);
628 }
629
630 METHOD(listener_t, ike_state_change, bool,
631 trap_listener_t *listener, ike_sa_t *ike_sa, ike_sa_state_t state)
632 {
633 switch (state)
634 {
635 case IKE_DESTROYING:
636 complete(listener->traps, ike_sa, NULL);
637 return TRUE;
638 default:
639 return TRUE;
640 }
641 }
642
643 METHOD(listener_t, child_state_change, bool,
644 trap_listener_t *listener, ike_sa_t *ike_sa, child_sa_t *child_sa,
645 child_sa_state_t state)
646 {
647 switch (state)
648 {
649 case CHILD_INSTALLED:
650 case CHILD_DESTROYING:
651 complete(listener->traps, ike_sa, child_sa);
652 return TRUE;
653 default:
654 return TRUE;
655 }
656 }
657
658 METHOD(trap_manager_t, flush, void,
659 private_trap_manager_t *this)
660 {
661 this->lock->write_lock(this->lock);
662 while (this->installing)
663 {
664 this->condvar->wait(this->condvar, this->lock);
665 }
666 this->traps->destroy_function(this->traps, (void*)destroy_entry);
667 this->traps = linked_list_create();
668 this->installing = INSTALL_DISABLED;
669 this->lock->unlock(this->lock);
670 }
671
672 METHOD(trap_manager_t, destroy, void,
673 private_trap_manager_t *this)
674 {
675 charon->bus->remove_listener(charon->bus, &this->listener.listener);
676 this->traps->destroy_function(this->traps, (void*)destroy_entry);
677 this->acquires->destroy_function(this->acquires, (void*)destroy_acquire);
678 this->condvar->destroy(this->condvar);
679 this->mutex->destroy(this->mutex);
680 this->lock->destroy(this->lock);
681 free(this);
682 }
683
684 /**
685 * See header
686 */
687 trap_manager_t *trap_manager_create(void)
688 {
689 private_trap_manager_t *this;
690
691 INIT(this,
692 .public = {
693 .install = _install,
694 .uninstall = _uninstall,
695 .create_enumerator = _create_enumerator,
696 .find_reqid = _find_reqid,
697 .acquire = _acquire,
698 .flush = _flush,
699 .destroy = _destroy,
700 },
701 .listener = {
702 .traps = this,
703 .listener = {
704 .ike_state_change = _ike_state_change,
705 .child_state_change = _child_state_change,
706 },
707 },
708 .traps = linked_list_create(),
709 .acquires = linked_list_create(),
710 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
711 .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
712 .condvar = rwlock_condvar_create(),
713 .ignore_acquire_ts = lib->settings->get_bool(lib->settings,
714 "%s.ignore_acquire_ts", FALSE, lib->ns),
715 );
716 charon->bus->add_listener(charon->bus, &this->listener.listener);
717
718 return &this->public;
719 }