diff options
Diffstat (limited to 'src/libcharon/sa')
-rw-r--r-- | src/libcharon/sa/child_sa.c | 143 | ||||
-rw-r--r-- | src/libcharon/sa/child_sa.h | 34 | ||||
-rw-r--r-- | src/libcharon/sa/ikev1/task_manager_v1.c | 6 | ||||
-rw-r--r-- | src/libcharon/sa/ikev1/tasks/quick_mode.c | 7 | ||||
-rw-r--r-- | src/libcharon/sa/ikev2/keymat_v2.c | 7 | ||||
-rw-r--r-- | src/libcharon/sa/ikev2/tasks/child_create.c | 81 | ||||
-rw-r--r-- | src/libcharon/sa/ikev2/tasks/child_delete.c | 1 | ||||
-rw-r--r-- | src/libcharon/sa/ikev2/tasks/child_rekey.c | 62 | ||||
-rw-r--r-- | src/libcharon/sa/trap_manager.c | 59 |
9 files changed, 277 insertions, 123 deletions
diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c index 3d9f6133b..4133d9182 100644 --- a/src/libcharon/sa/child_sa.c +++ b/src/libcharon/sa/child_sa.c @@ -40,10 +40,10 @@ ENUM(child_sa_state_names, CHILD_CREATED, CHILD_DESTROYING, "DESTROYING", ); -ENUM(child_sa_outbound_state_names, CHILD_OUTBOUND_NONE, CHILD_OUTBOUND_INSTALLED, - "NONE", +ENUM_FLAGS(child_sa_outbound_state_names, CHILD_OUTBOUND_REGISTERED, CHILD_OUTBOUND_POLICIES, "REGISTERED", - "INSTALLED", + "SA", + "POLICIES", ); typedef struct private_child_sa_t private_child_sa_t; @@ -296,12 +296,15 @@ METHOD(child_sa_t, get_config, child_cfg_t*, METHOD(child_sa_t, set_state, void, private_child_sa_t *this, child_sa_state_t state) { - DBG2(DBG_CHD, "CHILD_SA %s{%d} state change: %N => %N", - get_name(this), this->unique_id, - child_sa_state_names, this->state, - child_sa_state_names, state); - charon->bus->child_state_change(charon->bus, &this->public, state); - this->state = state; + if (this->state != state) + { + DBG2(DBG_CHD, "CHILD_SA %s{%d} state change: %N => %N", + get_name(this), this->unique_id, + child_sa_state_names, this->state, + child_sa_state_names, state); + charon->bus->child_state_change(charon->bus, &this->public, state); + this->state = state; + } } METHOD(child_sa_t, get_state, child_sa_state_t, @@ -547,7 +550,7 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound) } else { - if (this->other_spi && this->outbound_state == CHILD_OUTBOUND_INSTALLED) + if (this->other_spi && (this->outbound_state & CHILD_OUTBOUND_SA)) { kernel_ipsec_sa_id_t id = { .src = this->my_addr, @@ -788,7 +791,7 @@ static status_t install_internal(private_child_sa_t *this, chunk_t encr, { tfc = this->config->get_tfc(this->config); } - this->outbound_state = CHILD_OUTBOUND_INSTALLED; + this->outbound_state |= CHILD_OUTBOUND_SA; } DBG2(DBG_CHD, "adding %s %N SA", inbound ? "inbound" : "outbound", @@ -1188,6 +1191,7 @@ METHOD(child_sa_t, install_policies, status_t, linked_list_t *my_ts_list, *other_ts_list; traffic_selector_t *my_ts, *other_ts; status_t status = SUCCESS; + bool install_outbound = FALSE; if (!this->reqid_allocated && !this->static_reqid) { @@ -1207,12 +1211,17 @@ METHOD(child_sa_t, install_policies, status_t, this->reqid_allocated = TRUE; } + if (!(this->outbound_state & CHILD_OUTBOUND_REGISTERED)) + { + install_outbound = TRUE; + this->outbound_state |= CHILD_OUTBOUND_POLICIES; + } + if (!this->config->has_option(this->config, OPT_NO_POLICIES)) { policy_priority_t priority; ipsec_sa_cfg_t my_sa, other_sa; uint32_t manual_prio; - bool install_outbound; prepare_sa_cfg(this, &my_sa, &other_sa); manual_prio = this->config->get_manual_prio(this->config); @@ -1222,7 +1231,6 @@ METHOD(child_sa_t, install_policies, status_t, this->trap = this->state == CHILD_CREATED; priority = this->trap ? POLICY_PRIORITY_ROUTED : POLICY_PRIORITY_DEFAULT; - install_outbound = this->outbound_state != CHILD_OUTBOUND_REGISTERED; /* enumerate pairs of traffic selectors */ enumerator = create_policy_enumerator(this); @@ -1250,7 +1258,6 @@ METHOD(child_sa_t, install_policies, status_t, this->other_addr, my_ts, other_ts, &my_sa, &other_sa, POLICY_IPSEC, priority, manual_prio); - } if (status != SUCCESS) { @@ -1267,21 +1274,35 @@ METHOD(child_sa_t, install_policies, status_t, return status; } -METHOD(child_sa_t, register_outbound, void, +METHOD(child_sa_t, register_outbound, status_t, private_child_sa_t *this, chunk_t encr, chunk_t integ, uint32_t spi, uint16_t cpi, bool tfcv3) { - DBG2(DBG_CHD, "registering outbound %N SA", protocol_id_names, - this->protocol); - DBG2(DBG_CHD, " SPI 0x%.8x, src %H dst %H", ntohl(spi), this->my_addr, - this->other_addr); - - this->other_spi = spi; - this->other_cpi = cpi; - this->encr_r = chunk_clone(encr); - this->integ_r = chunk_clone(integ); - this->tfcv3 = tfcv3; - this->outbound_state = CHILD_OUTBOUND_REGISTERED; + status_t status; + + /* if the kernel supports installing SPIs with policies we install the + * SA immediately as it will only be used once we update the policies */ + if (charon->kernel->get_features(charon->kernel) & KERNEL_POLICY_SPI) + { + status = install_internal(this, encr, integ, spi, cpi, FALSE, FALSE, + tfcv3); + } + else + { + DBG2(DBG_CHD, "registering outbound %N SA", protocol_id_names, + this->protocol); + DBG2(DBG_CHD, " SPI 0x%.8x, src %H dst %H", ntohl(spi), this->my_addr, + this->other_addr); + + this->other_spi = spi; + this->other_cpi = cpi; + this->encr_r = chunk_clone(encr); + this->integ_r = chunk_clone(integ); + this->tfcv3 = tfcv3; + status = SUCCESS; + } + this->outbound_state |= CHILD_OUTBOUND_REGISTERED; + return status; } METHOD(child_sa_t, install_outbound, status_t, @@ -1289,18 +1310,23 @@ METHOD(child_sa_t, install_outbound, status_t, { enumerator_t *enumerator; traffic_selector_t *my_ts, *other_ts; - status_t status; + status_t status = SUCCESS; - status = install_internal(this, this->encr_r, this->integ_r, - this->other_spi, this->other_cpi, FALSE, FALSE, - this->tfcv3); - chunk_clear(&this->encr_r); - chunk_clear(&this->integ_r); + if (!(this->outbound_state & CHILD_OUTBOUND_SA)) + { + status = install_internal(this, this->encr_r, this->integ_r, + this->other_spi, this->other_cpi, FALSE, + FALSE, this->tfcv3); + chunk_clear(&this->encr_r); + chunk_clear(&this->integ_r); + } + this->outbound_state &= ~CHILD_OUTBOUND_REGISTERED; if (status != SUCCESS) { return status; } - if (!this->config->has_option(this->config, OPT_NO_POLICIES)) + if (!this->config->has_option(this->config, OPT_NO_POLICIES) && + !(this->outbound_state & CHILD_OUTBOUND_POLICIES)) { ipsec_sa_cfg_t my_sa, other_sa; uint32_t manual_prio; @@ -1331,6 +1357,7 @@ METHOD(child_sa_t, install_outbound, status_t, } enumerator->destroy(enumerator); } + this->outbound_state |= CHILD_OUTBOUND_POLICIES; return status; } @@ -1340,20 +1367,19 @@ METHOD(child_sa_t, remove_outbound, void, enumerator_t *enumerator; traffic_selector_t *my_ts, *other_ts; - switch (this->outbound_state) + if (!(this->outbound_state & CHILD_OUTBOUND_SA)) { - case CHILD_OUTBOUND_INSTALLED: - break; - case CHILD_OUTBOUND_REGISTERED: + if (this->outbound_state & CHILD_OUTBOUND_REGISTERED) + { chunk_clear(&this->encr_r); chunk_clear(&this->integ_r); this->outbound_state = CHILD_OUTBOUND_NONE; - /* fall-through */ - case CHILD_OUTBOUND_NONE: - return; + } + return; } - if (!this->config->has_option(this->config, OPT_NO_POLICIES)) + if (!this->config->has_option(this->config, OPT_NO_POLICIES) && + (this->outbound_state & CHILD_OUTBOUND_POLICIES)) { ipsec_sa_cfg_t my_sa, other_sa; uint32_t manual_prio; @@ -1598,8 +1624,8 @@ METHOD(child_sa_t, destroy, void, prepare_sa_cfg(this, &my_sa, &other_sa); manual_prio = this->config->get_manual_prio(this->config); - del_outbound = this->trap || - this->outbound_state == CHILD_OUTBOUND_INSTALLED; + del_outbound = (this->outbound_state & CHILD_OUTBOUND_POLICIES) || + this->trap; /* delete all policies in the kernel */ enumerator = create_policy_enumerator(this); @@ -1640,7 +1666,7 @@ METHOD(child_sa_t, destroy, void, }; charon->kernel->del_sa(charon->kernel, &id, &sa); } - if (this->other_spi && this->outbound_state == CHILD_OUTBOUND_INSTALLED) + if (this->other_spi && (this->outbound_state & CHILD_OUTBOUND_SA)) { kernel_ipsec_sa_id_t id = { .src = this->my_addr, @@ -1719,7 +1745,7 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, { private_child_sa_t *this; static refcount_t unique_id = 0, unique_mark = 0; - refcount_t mark; + refcount_t mark = 0; INIT(this, .public = { @@ -1792,16 +1818,33 @@ child_sa_t * child_sa_create(host_t *me, host_t* other, { this->mark_out.value = mark_out; } - if (this->mark_in.value == MARK_UNIQUE || - this->mark_out.value == MARK_UNIQUE) + + if (MARK_IS_UNIQUE(this->mark_in.value) || + MARK_IS_UNIQUE(this->mark_out.value)) { - mark = ref_get(&unique_mark); - if (this->mark_in.value == MARK_UNIQUE) + bool unique_dir; + + unique_dir = this->mark_in.value == MARK_UNIQUE_DIR || + this->mark_out.value == MARK_UNIQUE_DIR; + + if (!unique_dir) + { + mark = ref_get(&unique_mark); + } + if (MARK_IS_UNIQUE(this->mark_in.value)) { + if (unique_dir) + { + mark = ref_get(&unique_mark); + } this->mark_in.value = mark; } - if (this->mark_out.value == MARK_UNIQUE) + if (MARK_IS_UNIQUE(this->mark_out.value)) { + if (unique_dir) + { + mark = ref_get(&unique_mark); + } this->mark_out.value = mark; } } diff --git a/src/libcharon/sa/child_sa.h b/src/libcharon/sa/child_sa.h index b9a913da1..082404d93 100644 --- a/src/libcharon/sa/child_sa.h +++ b/src/libcharon/sa/child_sa.h @@ -102,17 +102,28 @@ enum child_sa_outbound_state_t { /** * Outbound SA is not installed */ - CHILD_OUTBOUND_NONE, + CHILD_OUTBOUND_NONE = 0, /** - * Data for the outbound SA has been registered, but not installed yet + * Data for the outbound SA has been registered during a rekeying (not set + * once the SA and policies are both installed) */ - CHILD_OUTBOUND_REGISTERED, + CHILD_OUTBOUND_REGISTERED = (1<<0), /** - * The outbound SA is currently installed + * The outbound SA has been installed */ - CHILD_OUTBOUND_INSTALLED, + CHILD_OUTBOUND_SA = (1<<1), + + /** + * The outbound policies have been installed + */ + CHILD_OUTBOUND_POLICIES = (1<<2), + + /** + * The outbound SA and policies are both installed + */ + CHILD_OUTBOUND_INSTALLED = (CHILD_OUTBOUND_SA|CHILD_OUTBOUND_POLICIES), }; /** @@ -400,20 +411,23 @@ struct child_sa_t { * Register data for the installation of an outbound SA as responder during * a rekeying. * - * The SA is not installed until install_outbound() is called. + * If the kernel is able to handle SPIs on policies the SA is installed + * immediately, if not it won't be installed until install_outbound() is + * called. * * @param encr encryption key, if any (cloned) * @param integ integrity key (cloned) * @param spi SPI to use, allocated for inbound * @param cpi CPI to use, allocated for outbound * @param tfcv3 TRUE if peer supports ESPv3 TFC + * @return SUCCESS or FAILED */ - void (*register_outbound)(child_sa_t *this, chunk_t encr, chunk_t integ, - uint32_t spi, uint16_t cpi, bool tfcv3); + status_t (*register_outbound)(child_sa_t *this, chunk_t encr, chunk_t integ, + uint32_t spi, uint16_t cpi, bool tfcv3); /** - * Install the outbound SA and the outbound policies as responder during a - * rekeying. + * Install the outbound policies and, if not already done, the outbound SA + * as responder during a rekeying. * * @return SUCCESS or FAILED */ diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c index 48ec3e7f5..3472d2c35 100644 --- a/src/libcharon/sa/ikev1/task_manager_v1.c +++ b/src/libcharon/sa/ikev1/task_manager_v1.c @@ -1805,8 +1805,12 @@ METHOD(task_manager_t, queue_child_rekey, void, if (is_redundant(this, child_sa)) { child_sa->set_state(child_sa, CHILD_REKEYED); - queue_task(this, (task_t*)quick_delete_create(this->ike_sa, + if (lib->settings->get_bool(lib->settings, "%s.delete_rekeyed", + FALSE, lib->ns)) + { + queue_task(this, (task_t*)quick_delete_create(this->ike_sa, protocol, spi, FALSE, FALSE)); + } } else { diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c index 8be82ebe2..49b476ad8 100644 --- a/src/libcharon/sa/ikev1/tasks/quick_mode.c +++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c @@ -396,10 +396,6 @@ static bool install(private_quick_mode_t *this) charon->bus->child_keys(charon->bus, this->child_sa, this->initiator, this->dh, this->nonce_i, this->nonce_r); - /* add to IKE_SA, and remove from task */ - this->child_sa->set_state(this->child_sa, CHILD_INSTALLED); - this->ike_sa->add_child_sa(this->ike_sa, this->child_sa); - my_ts = linked_list_create_from_enumerator( this->child_sa->create_ts_enumerator(this->child_sa, TRUE)); other_ts = linked_list_create_from_enumerator( @@ -415,6 +411,9 @@ static bool install(private_quick_mode_t *this) my_ts->destroy(my_ts); other_ts->destroy(other_ts); + this->child_sa->set_state(this->child_sa, CHILD_INSTALLED); + this->ike_sa->add_child_sa(this->ike_sa, this->child_sa); + if (this->rekey) { old = this->ike_sa->get_child_sa(this->ike_sa, diff --git a/src/libcharon/sa/ikev2/keymat_v2.c b/src/libcharon/sa/ikev2/keymat_v2.c index 70dacd1dc..0c41c68d0 100644 --- a/src/libcharon/sa/ikev2/keymat_v2.c +++ b/src/libcharon/sa/ikev2/keymat_v2.c @@ -342,10 +342,13 @@ METHOD(keymat_v2_t, derive_ike_keys, bool, * the nonces. */ switch (alg) { + case PRF_AES128_CMAC: + /* while variable keys may be used according to RFC 4615, RFC 7296 + * explicitly limits the key size to 128 bit for this application */ case PRF_AES128_XCBC: - /* while rfc4434 defines variable keys for AES-XCBC, rfc3664 does + /* while RFC 4434 defines variable keys for AES-XCBC, RFC 3664 does * not and therefore fixed key semantics apply to XCBC for key - * derivation. */ + * derivation, which is also reinforced by RFC 7296 */ case PRF_CAMELLIA128_XCBC: /* draft-kanno-ipsecme-camellia-xcbc refers to rfc 4434, we * assume fixed key length. */ diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c index 896cabb2b..cac3bc0a2 100644 --- a/src/libcharon/sa/ikev2/tasks/child_create.c +++ b/src/libcharon/sa/ikev2/tasks/child_create.c @@ -478,6 +478,7 @@ static status_t select_and_install(private_child_create_t *this, bool no_dh, bool ike_auth) { status_t status, status_i, status_o; + child_sa_outbound_state_t out_state; chunk_t nonce_i, nonce_r; chunk_t encr_i = chunk_empty, encr_r = chunk_empty; chunk_t integ_i = chunk_empty, integ_r = chunk_empty; @@ -678,29 +679,42 @@ static status_t select_and_install(private_child_create_t *this, status_i = this->child_sa->install(this->child_sa, encr_r, integ_r, this->my_spi, this->my_cpi, this->initiator, TRUE, this->tfcv3); - status_o = this->child_sa->install(this->child_sa, encr_i, integ_i, - this->other_spi, this->other_cpi, this->initiator, - FALSE, this->tfcv3); } - else if (!this->rekey) + else { status_i = this->child_sa->install(this->child_sa, encr_i, integ_i, this->my_spi, this->my_cpi, this->initiator, TRUE, this->tfcv3); - status_o = this->child_sa->install(this->child_sa, encr_r, integ_r, + } + if (this->rekey) + { /* during rekeyings we install the outbound SA and/or policies + * separately: as responder when we receive the delete for the old + * SA, as initiator pretty much immediately in the ike-rekey task, + * unless there was a rekey collision that we lost */ + if (this->initiator) + { + status_o = this->child_sa->register_outbound(this->child_sa, + encr_i, integ_i, this->other_spi, this->other_cpi, + this->tfcv3); + } + else + { + status_o = this->child_sa->register_outbound(this->child_sa, + encr_r, integ_r, this->other_spi, this->other_cpi, + this->tfcv3); + } + } + else if (this->initiator) + { + status_o = this->child_sa->install(this->child_sa, encr_i, integ_i, this->other_spi, this->other_cpi, this->initiator, FALSE, this->tfcv3); } else - { /* as responder during a rekeying we only install the inbound - * SA now, the outbound SA and policies are installed when we - * receive the delete for the old SA */ - status_i = this->child_sa->install(this->child_sa, encr_i, integ_i, - this->my_spi, this->my_cpi, this->initiator, - TRUE, this->tfcv3); - this->child_sa->register_outbound(this->child_sa, encr_r, integ_r, - this->other_spi, this->other_cpi, this->tfcv3); - status_o = SUCCESS; + { + status_o = this->child_sa->install(this->child_sa, encr_r, integ_r, + this->other_spi, this->other_cpi, this->initiator, + FALSE, this->tfcv3); } } @@ -745,20 +759,15 @@ static status_t select_and_install(private_child_create_t *this, charon->bus->child_keys(charon->bus, this->child_sa, this->initiator, this->dh, nonce_i, nonce_r); - this->child_sa->set_state(this->child_sa, CHILD_INSTALLED); - this->ike_sa->add_child_sa(this->ike_sa, this->child_sa); - this->established = TRUE; - - schedule_inactivity_timeout(this); - my_ts = linked_list_create_from_enumerator( this->child_sa->create_ts_enumerator(this->child_sa, TRUE)); other_ts = linked_list_create_from_enumerator( this->child_sa->create_ts_enumerator(this->child_sa, FALSE)); + out_state = this->child_sa->get_outbound_state(this->child_sa); DBG0(DBG_IKE, "%sCHILD_SA %s{%d} established " "with SPIs %.8x_i %.8x_o and TS %#R === %#R", - this->rekey && !this->initiator ? "inbound " : "", + (out_state == CHILD_OUTBOUND_INSTALLED) ? "" : "inbound ", this->child_sa->get_name(this->child_sa), this->child_sa->get_unique_id(this->child_sa), ntohl(this->child_sa->get_spi(this->child_sa, TRUE)), @@ -767,6 +776,12 @@ static status_t select_and_install(private_child_create_t *this, my_ts->destroy(my_ts); other_ts->destroy(other_ts); + + this->child_sa->set_state(this->child_sa, CHILD_INSTALLED); + this->ike_sa->add_child_sa(this->ike_sa, this->child_sa); + this->established = TRUE; + + schedule_inactivity_timeout(this); return SUCCESS; } @@ -1007,17 +1022,6 @@ METHOD(task_t, build_i, status_t, break; } - if (this->reqid) - { - DBG0(DBG_IKE, "establishing CHILD_SA %s{%d}", - this->config->get_name(this->config), this->reqid); - } - else - { - DBG0(DBG_IKE, "establishing CHILD_SA %s", - this->config->get_name(this->config)); - } - /* check if we want a virtual IP, but don't have one */ list = linked_list_create(); peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa); @@ -1070,6 +1074,19 @@ METHOD(task_t, build_i, status_t, this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY), this->mark_in, this->mark_out); + if (this->reqid) + { + DBG0(DBG_IKE, "establishing CHILD_SA %s{%d} reqid %d", + this->child_sa->get_name(this->child_sa), + this->child_sa->get_unique_id(this->child_sa), this->reqid); + } + else + { + DBG0(DBG_IKE, "establishing CHILD_SA %s{%d}", + this->child_sa->get_name(this->child_sa), + this->child_sa->get_unique_id(this->child_sa)); + } + if (!allocate_spi(this)) { DBG1(DBG_IKE, "unable to allocate SPIs from kernel"); diff --git a/src/libcharon/sa/ikev2/tasks/child_delete.c b/src/libcharon/sa/ikev2/tasks/child_delete.c index 626796383..2217295b6 100644 --- a/src/libcharon/sa/ikev2/tasks/child_delete.c +++ b/src/libcharon/sa/ikev2/tasks/child_delete.c @@ -196,7 +196,6 @@ static void install_outbound(private_child_delete_t *this, /* FIXME: delete the new child_sa? */ return; } - child_sa->set_state(child_sa, CHILD_INSTALLED); my_ts = linked_list_create_from_enumerator( child_sa->create_ts_enumerator(child_sa, TRUE)); diff --git a/src/libcharon/sa/ikev2/tasks/child_rekey.c b/src/libcharon/sa/ikev2/tasks/child_rekey.c index 761c860e7..b67e9b80f 100644 --- a/src/libcharon/sa/ikev2/tasks/child_rekey.c +++ b/src/libcharon/sa/ikev2/tasks/child_rekey.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2016 Tobias Brunner + * Copyright (C) 2009-2017 Tobias Brunner * Copyright (C) 2005-2007 Martin Willi * Copyright (C) 2005 Jan Hutter * HSR Hochschule fuer Technik Rapperswil @@ -283,7 +283,8 @@ METHOD(task_t, build_r, status_t, /** * Handle a rekey collision */ -static child_sa_t *handle_collision(private_child_rekey_t *this) +static child_sa_t *handle_collision(private_child_rekey_t *this, + child_sa_t **to_install) { child_sa_t *to_delete; @@ -302,8 +303,11 @@ static child_sa_t *handle_collision(private_child_rekey_t *this) { child_sa_t *child_sa; - DBG1(DBG_IKE, "CHILD_SA rekey collision won, deleting old child"); + *to_install = this->child_create->get_child(this->child_create); to_delete = this->child_sa; + DBG1(DBG_IKE, "CHILD_SA rekey collision won, deleting old child " + "%s{%d}", to_delete->get_name(to_delete), + to_delete->get_unique_id(to_delete)); /* don't touch child other created, it has already been deleted */ if (!this->other_child_destroyed) { @@ -321,9 +325,10 @@ static child_sa_t *handle_collision(private_child_rekey_t *this) } else { - DBG1(DBG_IKE, "CHILD_SA rekey collision lost, " - "deleting rekeyed child"); to_delete = this->child_create->get_child(this->child_create); + DBG1(DBG_IKE, "CHILD_SA rekey collision lost, deleting redundant " + "child %s{%d}", to_delete->get_name(to_delete), + to_delete->get_unique_id(to_delete)); } } else @@ -334,15 +339,17 @@ static child_sa_t *handle_collision(private_child_rekey_t *this) * the CHILD_SA the other is not deleting. */ if (del->get_child(del) != this->child_sa) { - DBG1(DBG_IKE, "CHILD_SA rekey/delete collision, " - "deleting rekeyed child"); to_delete = this->child_sa; + DBG1(DBG_IKE, "CHILD_SA rekey/delete collision, deleting old child " + "%s{%d}", to_delete->get_name(to_delete), + to_delete->get_unique_id(to_delete)); } else { - DBG1(DBG_IKE, "CHILD_SA rekey/delete collision, " - "deleting redundant child"); to_delete = this->child_create->get_child(this->child_create); + DBG1(DBG_IKE, "CHILD_SA rekey/delete collision, deleting redundant " + "child %s{%d}", to_delete->get_name(to_delete), + to_delete->get_unique_id(to_delete)); } } return to_delete; @@ -353,7 +360,7 @@ METHOD(task_t, process_i, status_t, { protocol_id_t protocol; uint32_t spi; - child_sa_t *to_delete; + child_sa_t *to_delete, *to_install = NULL; if (message->get_notify(message, NO_ADDITIONAL_SAS)) { @@ -415,19 +422,48 @@ METHOD(task_t, process_i, status_t, /* check for rekey collisions */ if (this->collision) { - to_delete = handle_collision(this); + to_delete = handle_collision(this, &to_install); } else { + to_install = this->child_create->get_child(this->child_create); to_delete = this->child_sa; } - + if (to_install) + { + if (to_install->install_outbound(to_install) != SUCCESS) + { + DBG1(DBG_IKE, "unable to install outbound IPsec SA (SAD) in kernel"); + charon->bus->alert(charon->bus, ALERT_INSTALL_CHILD_SA_FAILED, + to_install); + /* FIXME: delete the child_sa? fail the task? */ + } + else + { + linked_list_t *my_ts, *other_ts; + + my_ts = linked_list_create_from_enumerator( + to_install->create_ts_enumerator(to_install, TRUE)); + other_ts = linked_list_create_from_enumerator( + to_install->create_ts_enumerator(to_install, FALSE)); + + DBG0(DBG_IKE, "outbound CHILD_SA %s{%d} established " + "with SPIs %.8x_i %.8x_o and TS %#R === %#R", + to_install->get_name(to_install), + to_install->get_unique_id(to_install), + ntohl(to_install->get_spi(to_install, TRUE)), + ntohl(to_install->get_spi(to_install, FALSE)), + my_ts, other_ts); + + my_ts->destroy(my_ts); + other_ts->destroy(other_ts); + } + } if (to_delete != this->child_create->get_child(this->child_create)) { /* invoke rekey hook if rekeying successful */ charon->bus->child_rekey(charon->bus, this->child_sa, this->child_create->get_child(this->child_create)); } - if (to_delete == NULL) { return SUCCESS; diff --git a/src/libcharon/sa/trap_manager.c b/src/libcharon/sa/trap_manager.c index f9fee5e7e..6436a2549 100644 --- a/src/libcharon/sa/trap_manager.c +++ b/src/libcharon/sa/trap_manager.c @@ -158,6 +158,31 @@ CALLBACK(acquire_by_dst, bool, return this->dst && this->dst->ip_equals(this->dst, dst); } +/** + * Check if any remote TS are dynamic + */ +static bool dynamic_remote_ts(child_cfg_t *child) +{ + enumerator_t *enumerator; + linked_list_t *other_ts; + traffic_selector_t *ts; + bool found = FALSE; + + other_ts = child->get_traffic_selectors(child, FALSE, NULL, NULL); + enumerator = other_ts->create_enumerator(other_ts); + while (enumerator->enumerate(enumerator, &ts)) + { + if (ts->is_dynamic(ts)) + { + found = TRUE; + break; + } + } + enumerator->destroy(enumerator); + other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy)); + return found; +} + METHOD(trap_manager_t, install, uint32_t, private_trap_manager_t *this, peer_cfg_t *peer, child_cfg_t *child, uint32_t reqid) @@ -184,25 +209,39 @@ METHOD(trap_manager_t, install, uint32_t, me = host_create_any(other->get_family(other)); wildcard = TRUE; } - else if (!other || other->is_anyaddr(other)) + else if (other && other->is_anyaddr(other)) { - DESTROY_IF(other); + other->destroy(other); DBG1(DBG_CFG, "installing trap failed, remote address unknown"); return 0; } else - { - me = ike_cfg->resolve_me(ike_cfg, other->get_family(other)); - if (!me || me->is_anyaddr(me)) + { /* depending on the traffic selectors we don't really need a remote + * host yet, but we might fail later if no IP can be resolved */ + if (!other && dynamic_remote_ts(child)) + { /* with dynamic TS we do need a host, otherwise 0.0.0.0/0 is used, + * which is probably not what users expect*/ + DBG1(DBG_CFG, "installing trap failed, remote address unknown with " + "dynamic traffic selector"); + return 0; + } + me = ike_cfg->resolve_me(ike_cfg, other ? other->get_family(other) + : AF_UNSPEC); + if (!other) + { + other = host_create_any(me ? me->get_family(me) : AF_INET); + } + other->set_port(other, ike_cfg->get_other_port(ike_cfg)); + if ((!me || me->is_anyaddr(me)) && !other->is_anyaddr(other)) { DESTROY_IF(me); me = charon->kernel->get_source_addr(charon->kernel, other, NULL); - if (!me) - { - me = host_create_any(other->get_family(other)); - } - me->set_port(me, ike_cfg->get_my_port(ike_cfg)); } + if (!me) + { + me = host_create_any(other->get_family(other)); + } + me->set_port(me, ike_cfg->get_my_port(ike_cfg)); } this->lock->write_lock(this->lock); |