From: Tobias Brunner Date: Fri, 24 Apr 2015 14:57:43 +0000 (+0200) Subject: ike-sa: Handle redirect requests for established SAs as reestablishment X-Git-Tag: 5.4.0dr8~12^2~17 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f20e00fe545d9939de095979870eebc6987ea6ad;p=thirdparty%2Fstrongswan.git ike-sa: Handle redirect requests for established SAs as reestablishment We handle this similar to how we do reestablishing IKE_SAs with all CHILD_SAs, which also includes the one actively queued during IKE_AUTH. To delete the old SA we use the recently added ike_reauth_complete task. --- diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index 79960fcb27..f0c38efa8b 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -56,6 +56,7 @@ #include #include #include +#include #ifdef ME #include @@ -1768,6 +1769,86 @@ static bool is_child_queued(private_ike_sa_t *this, task_queue_t queue) return found; } +/** + * Reestablish CHILD_SAs and migrate queued tasks. + * + * If force is true all SAs are restarted, otherwise their close/dpd_action + * is followed. + */ +static status_t reestablish_children(private_ike_sa_t *this, ike_sa_t *new, + bool force) +{ + enumerator_t *enumerator; + child_sa_t *child_sa; + child_cfg_t *child_cfg; + action_t action; + status_t status = FAILED; + + /* handle existing CHILD_SAs */ + enumerator = create_child_sa_enumerator(this); + while (enumerator->enumerate(enumerator, (void**)&child_sa)) + { + if (force) + { + switch (child_sa->get_state(child_sa)) + { + case CHILD_ROUTED: + { /* move routed child directly */ + remove_child_sa(this, enumerator); + new->add_child_sa(new, child_sa); + action = ACTION_NONE; + break; + } + default: + { /* initiate/queue all other CHILD_SAs */ + action = ACTION_RESTART; + break; + } + } + } + else + { /* only restart CHILD_SAs that are configured accordingly */ + if (this->state == IKE_DELETING) + { + action = child_sa->get_close_action(child_sa); + } + else + { + action = child_sa->get_dpd_action(child_sa); + } + } + switch (action) + { + case ACTION_RESTART: + child_cfg = child_sa->get_config(child_sa); + DBG1(DBG_IKE, "restarting CHILD_SA %s", + child_cfg->get_name(child_cfg)); + child_cfg->get_ref(child_cfg); + status = new->initiate(new, child_cfg, + child_sa->get_reqid(child_sa), NULL, NULL); + break; + default: + continue; + } + if (status == DESTROY_ME) + { + break; + } + } + enumerator->destroy(enumerator); + /* adopt any active or queued CHILD-creating tasks */ + if (status != DESTROY_ME) + { + task_manager_t *other_tasks = ((private_ike_sa_t*)new)->task_manager; + other_tasks->adopt_child_tasks(other_tasks, this->task_manager); + if (new->get_state(new) == IKE_CREATED) + { + status = new->initiate(new, NULL, 0, NULL, NULL); + } + } + return status; +} + METHOD(ike_sa_t, reestablish, status_t, private_ike_sa_t *this) { @@ -1776,7 +1857,6 @@ METHOD(ike_sa_t, reestablish, status_t, action_t action; enumerator_t *enumerator; child_sa_t *child_sa; - child_cfg_t *child_cfg; bool restart = FALSE; status_t status = FAILED; @@ -1887,68 +1967,8 @@ METHOD(ike_sa_t, reestablish, status_t, else #endif /* ME */ { - /* handle existing CHILD_SAs */ - enumerator = create_child_sa_enumerator(this); - while (enumerator->enumerate(enumerator, (void**)&child_sa)) - { - if (has_condition(this, COND_REAUTHENTICATING)) - { - switch (child_sa->get_state(child_sa)) - { - case CHILD_ROUTED: - { /* move routed child directly */ - remove_child_sa(this, enumerator); - new->add_child_sa(new, child_sa); - action = ACTION_NONE; - break; - } - default: - { /* initiate/queue all other CHILD_SAs */ - action = ACTION_RESTART; - break; - } - } - } - else - { /* only restart CHILD_SAs that are configured accordingly */ - if (this->state == IKE_DELETING) - { - action = child_sa->get_close_action(child_sa); - } - else - { - action = child_sa->get_dpd_action(child_sa); - } - } - switch (action) - { - case ACTION_RESTART: - child_cfg = child_sa->get_config(child_sa); - DBG1(DBG_IKE, "restarting CHILD_SA %s", - child_cfg->get_name(child_cfg)); - child_cfg->get_ref(child_cfg); - status = new->initiate(new, child_cfg, - child_sa->get_reqid(child_sa), NULL, NULL); - break; - default: - continue; - } - if (status == DESTROY_ME) - { - break; - } - } - enumerator->destroy(enumerator); - /* adopt any active or queued CHILD-creating tasks */ - if (status != DESTROY_ME) - { - task_manager_t *other_tasks = ((private_ike_sa_t*)new)->task_manager; - other_tasks->adopt_child_tasks(other_tasks, this->task_manager); - if (new->get_state(new) == IKE_CREATED) - { - status = new->initiate(new, NULL, 0, NULL, NULL); - } - } + status = reestablish_children(this, new, + has_condition(this, COND_REAUTHENTICATING)); } if (status == DESTROY_ME) @@ -1969,42 +1989,114 @@ METHOD(ike_sa_t, reestablish, status_t, return status; } -METHOD(ike_sa_t, handle_redirect, bool, - private_ike_sa_t *this, identification_t *gateway) +/** + * Resolve the given gateway ID + */ +static host_t *resolve_gateway_id(identification_t *gateway) { char gw[BUF_LEN]; - host_t *other, *from; + host_t *addr; - DBG1(DBG_IKE, "redirected to %Y", gateway); - if (!this->follow_redirects) + snprintf(gw, sizeof(gw), "%Y", gateway); + gw[sizeof(gw)-1] = '\0'; + addr = host_create_from_dns(gw, AF_UNSPEC, IKEV2_UDP_PORT); + if (!addr) + { + DBG1(DBG_IKE, "unable to resolve gateway ID '%Y', redirect failed", + gateway); + } + return addr; +} + +/** + * Redirect the current SA to the given target host + */ +static bool redirect_established(private_ike_sa_t *this, identification_t *to) +{ + private_ike_sa_t *new_priv; + ike_sa_t *new; + host_t *other; + + new = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager, + this->version, TRUE); + if (!new) { - DBG1(DBG_IKE, "server sent REDIRECT even though we disabled it"); return FALSE; } + new_priv = (private_ike_sa_t*)new; + new->set_peer_cfg(new, this->peer_cfg); + new_priv->redirected_from = this->other_host->clone(this->other_host); + charon->bus->ike_reestablish_pre(charon->bus, &this->public, new); + other = resolve_gateway_id(to); + if (other) + { + set_my_host(new_priv, this->my_host->clone(this->my_host)); + /* this allows us to force the remote address while we still properly + * resolve the local address */ + new_priv->remote_host = other; + resolve_hosts(new_priv); + if (reestablish_children(this, new, TRUE) != DESTROY_ME) + { +#ifdef USE_IKEV2 + new->queue_task(new, (task_t*)ike_reauth_complete_create(new, + this->ike_sa_id)); +#endif + charon->bus->ike_reestablish_post(charon->bus, &this->public, new, + TRUE); + charon->ike_sa_manager->checkin(charon->ike_sa_manager, new); + charon->bus->set_sa(charon->bus, &this->public); + return TRUE; + } + } + charon->bus->ike_reestablish_post(charon->bus, &this->public, new, + FALSE); + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, new); + charon->bus->set_sa(charon->bus, &this->public); + return FALSE; +} - snprintf(gw, sizeof(gw), "%Y", gateway); - gw[sizeof(gw)-1] = '\0'; - other = host_create_from_dns(gw, AF_UNSPEC, IKEV2_UDP_PORT); +/** + * Redirect the current connecting SA to the given target host + */ +static bool redirect_connecting(private_ike_sa_t *this, identification_t *to) +{ + host_t *other; + + other = resolve_gateway_id(to); if (!other) { - DBG1(DBG_IKE, "unable to resolve gateway ID '%Y', redirect failed", - gateway); return FALSE; } - from = this->other_host->clone(this->other_host); + reset(this); + DESTROY_IF(this->redirected_from); + this->redirected_from = this->other_host->clone(this->other_host); + DESTROY_IF(this->remote_host); + /* this allows us to force the remote address while we still properly + * resolve the local address */ + this->remote_host = other; + resolve_hosts(this); + return TRUE; +} + +METHOD(ike_sa_t, handle_redirect, bool, + private_ike_sa_t *this, identification_t *gateway) +{ + DBG1(DBG_IKE, "redirected to %Y", gateway); + if (!this->follow_redirects) + { + DBG1(DBG_IKE, "server sent REDIRECT even though we disabled it"); + return FALSE; + } + switch (this->state) { case IKE_CONNECTING: - reset(this); - set_other_host(this, other); - DESTROY_IF(this->redirected_from); - this->redirected_from = from; - return TRUE; + return redirect_connecting(this, gateway); + case IKE_ESTABLISHED: + return redirect_established(this, gateway); default: DBG1(DBG_IKE, "unable to handle redirect for IKE_SA in state %N", ike_sa_state_names, this->state); - other->destroy(other); - from->destroy(from); return FALSE; } }