child-create: Maintain traffic selectors during rekeying/reauthentication
If we don't do this, narrowed SAs would default to the wide configured traffic selectors and the peer won't know if/how to narrow.
This commit is contained in:
@@ -117,6 +117,16 @@ struct private_child_create_t {
|
|||||||
*/
|
*/
|
||||||
traffic_selector_t *packet_tsr;
|
traffic_selector_t *packet_tsr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local traffic selectors as configured or previously negotiated
|
||||||
|
*/
|
||||||
|
traffic_selector_list_t *my_ts;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remote traffic selectors as configured or previously negotiated
|
||||||
|
*/
|
||||||
|
traffic_selector_list_t *other_ts;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Key exchanges to perform
|
* Key exchanges to perform
|
||||||
*/
|
*/
|
||||||
@@ -256,6 +266,15 @@ static void schedule_delayed_retry(private_child_create_t *this)
|
|||||||
task->use_if_ids(task, this->child.if_id_in, this->child.if_id_out);
|
task->use_if_ids(task, this->child.if_id_in, this->child.if_id_out);
|
||||||
task->use_label(task, this->child.label);
|
task->use_label(task, this->child.label);
|
||||||
|
|
||||||
|
/* clone these directly as we don't have a public method */
|
||||||
|
if (this->my_ts && this->other_ts)
|
||||||
|
{
|
||||||
|
private_child_create_t *priv = (private_child_create_t*)task;
|
||||||
|
|
||||||
|
priv->my_ts = this->my_ts->clone(this->my_ts);
|
||||||
|
priv->other_ts = this->other_ts->clone(this->other_ts);
|
||||||
|
}
|
||||||
|
|
||||||
DBG1(DBG_IKE, "creating CHILD_SA failed, trying again in %d seconds",
|
DBG1(DBG_IKE, "creating CHILD_SA failed, trying again in %d seconds",
|
||||||
retry);
|
retry);
|
||||||
this->ike_sa->queue_task_delayed(this->ike_sa, (task_t*)task, retry);
|
this->ike_sa->queue_task_delayed(this->ike_sa, (task_t*)task, retry);
|
||||||
@@ -452,15 +471,37 @@ static linked_list_t* get_transport_nat_ts(private_child_create_t *this,
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure we have traffic selector lists when not recreating an SA.
|
||||||
|
*/
|
||||||
|
static void ensure_ts_lists(private_child_create_t *this)
|
||||||
|
{
|
||||||
|
linked_list_t *ts;
|
||||||
|
|
||||||
|
if (!this->my_ts)
|
||||||
|
{
|
||||||
|
ts = this->config->get_traffic_selectors(this->config, TRUE, NULL);
|
||||||
|
this->my_ts = traffic_selector_list_create_from_list(ts);
|
||||||
|
}
|
||||||
|
if (!this->other_ts)
|
||||||
|
{
|
||||||
|
ts = this->config->get_traffic_selectors(this->config, FALSE, NULL);
|
||||||
|
this->other_ts = traffic_selector_list_create_from_list(ts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Narrow received traffic selectors with configuration
|
* Narrow received traffic selectors with configuration
|
||||||
*/
|
*/
|
||||||
static linked_list_t* narrow_ts(private_child_create_t *this, bool local,
|
static linked_list_t* narrow_ts(private_child_create_t *this, bool local,
|
||||||
linked_list_t *in)
|
linked_list_t *in)
|
||||||
{
|
{
|
||||||
linked_list_t *hosts, *nat, *ts;
|
traffic_selector_list_t *ts;
|
||||||
|
linked_list_t *hosts, *nat, *result;
|
||||||
ike_condition_t cond;
|
ike_condition_t cond;
|
||||||
|
|
||||||
|
ensure_ts_lists(this);
|
||||||
|
ts = local ? this->my_ts : this->other_ts;
|
||||||
cond = local ? COND_NAT_HERE : COND_NAT_THERE;
|
cond = local ? COND_NAT_HERE : COND_NAT_THERE;
|
||||||
hosts = ike_sa_get_dynamic_hosts(this->ike_sa, local);
|
hosts = ike_sa_get_dynamic_hosts(this->ike_sa, local);
|
||||||
|
|
||||||
@@ -468,19 +509,16 @@ static linked_list_t* narrow_ts(private_child_create_t *this, bool local,
|
|||||||
this->ike_sa->has_condition(this->ike_sa, cond))
|
this->ike_sa->has_condition(this->ike_sa, cond))
|
||||||
{
|
{
|
||||||
nat = get_transport_nat_ts(this, local, in);
|
nat = get_transport_nat_ts(this, local, in);
|
||||||
ts = this->config->select_traffic_selectors(this->config, local, nat,
|
result = child_cfg_select_ts(this->config, local, ts, nat, hosts);
|
||||||
hosts);
|
|
||||||
nat->destroy_offset(nat, offsetof(traffic_selector_t, destroy));
|
nat->destroy_offset(nat, offsetof(traffic_selector_t, destroy));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ts = this->config->select_traffic_selectors(this->config, local, in,
|
result = child_cfg_select_ts(this->config, local, ts, in, hosts);
|
||||||
hosts);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hosts->destroy(hosts);
|
hosts->destroy(hosts);
|
||||||
|
return result;
|
||||||
return ts;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1435,13 +1473,66 @@ METHOD(task_t, build_i_multi_ke, status_t,
|
|||||||
return NEED_MORE;
|
return NEED_MORE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare proposed traffic selectors as initiator.
|
||||||
|
*/
|
||||||
|
static void prepare_proposed_ts(private_child_create_t *this)
|
||||||
|
{
|
||||||
|
enumerator_t *enumerator;
|
||||||
|
peer_cfg_t *peer_cfg;
|
||||||
|
linked_list_t *list;
|
||||||
|
host_t *vip;
|
||||||
|
|
||||||
|
ensure_ts_lists(this);
|
||||||
|
|
||||||
|
list = linked_list_create();
|
||||||
|
if (!this->rekey)
|
||||||
|
{
|
||||||
|
/* check if we want a virtual IP */
|
||||||
|
peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
|
||||||
|
enumerator = peer_cfg->create_virtual_ip_enumerator(peer_cfg);
|
||||||
|
while (enumerator->enumerate(enumerator, &vip))
|
||||||
|
{
|
||||||
|
/* propose a 0.0.0.0/0 or ::/0 subnet when we use a virtual IP */
|
||||||
|
vip = host_create_any(vip->get_family(vip));
|
||||||
|
list->insert_last(list, vip);
|
||||||
|
}
|
||||||
|
enumerator->destroy(enumerator);
|
||||||
|
}
|
||||||
|
if (list->get_count(list))
|
||||||
|
{
|
||||||
|
this->tsi = child_cfg_select_ts(this->config, TRUE, this->my_ts, NULL,
|
||||||
|
list);
|
||||||
|
list->destroy_offset(list, offsetof(host_t, destroy));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
list->destroy(list);
|
||||||
|
list = ike_sa_get_dynamic_hosts(this->ike_sa, TRUE);
|
||||||
|
this->tsi = child_cfg_select_ts(this->config, TRUE, this->my_ts, NULL,
|
||||||
|
list);
|
||||||
|
list->destroy(list);
|
||||||
|
}
|
||||||
|
list = ike_sa_get_dynamic_hosts(this->ike_sa, FALSE);
|
||||||
|
this->tsr = child_cfg_select_ts(this->config, FALSE, this->other_ts, NULL,
|
||||||
|
list);
|
||||||
|
list->destroy(list);
|
||||||
|
|
||||||
|
if (this->packet_tsi)
|
||||||
|
{
|
||||||
|
this->tsi->insert_first(this->tsi,
|
||||||
|
this->packet_tsi->clone(this->packet_tsi));
|
||||||
|
}
|
||||||
|
if (this->packet_tsr)
|
||||||
|
{
|
||||||
|
this->tsr->insert_first(this->tsr,
|
||||||
|
this->packet_tsr->clone(this->packet_tsr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
METHOD(task_t, build_i, status_t,
|
METHOD(task_t, build_i, status_t,
|
||||||
private_child_create_t *this, message_t *message)
|
private_child_create_t *this, message_t *message)
|
||||||
{
|
{
|
||||||
enumerator_t *enumerator;
|
|
||||||
host_t *vip;
|
|
||||||
peer_cfg_t *peer_cfg;
|
|
||||||
linked_list_t *list;
|
|
||||||
bool no_ke = TRUE;
|
bool no_ke = TRUE;
|
||||||
|
|
||||||
switch (message->get_exchange_type(message))
|
switch (message->get_exchange_type(message))
|
||||||
@@ -1477,49 +1568,7 @@ METHOD(task_t, build_i, status_t,
|
|||||||
return NEED_MORE;
|
return NEED_MORE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check if we want a virtual IP, but don't have one */
|
prepare_proposed_ts(this);
|
||||||
list = linked_list_create();
|
|
||||||
peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
|
|
||||||
if (!this->rekey)
|
|
||||||
{
|
|
||||||
enumerator = peer_cfg->create_virtual_ip_enumerator(peer_cfg);
|
|
||||||
while (enumerator->enumerate(enumerator, &vip))
|
|
||||||
{
|
|
||||||
/* propose a 0.0.0.0/0 or ::/0 subnet when we use virtual ip */
|
|
||||||
vip = host_create_any(vip->get_family(vip));
|
|
||||||
list->insert_last(list, vip);
|
|
||||||
}
|
|
||||||
enumerator->destroy(enumerator);
|
|
||||||
}
|
|
||||||
if (list->get_count(list))
|
|
||||||
{
|
|
||||||
this->tsi = this->config->select_traffic_selectors(this->config, TRUE,
|
|
||||||
NULL, list);
|
|
||||||
list->destroy_offset(list, offsetof(host_t, destroy));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{ /* no virtual IPs configured */
|
|
||||||
list->destroy(list);
|
|
||||||
list = ike_sa_get_dynamic_hosts(this->ike_sa, TRUE);
|
|
||||||
this->tsi = this->config->select_traffic_selectors(this->config, TRUE,
|
|
||||||
NULL, list);
|
|
||||||
list->destroy(list);
|
|
||||||
}
|
|
||||||
list = ike_sa_get_dynamic_hosts(this->ike_sa, FALSE);
|
|
||||||
this->tsr = this->config->select_traffic_selectors(this->config, FALSE,
|
|
||||||
NULL, list);
|
|
||||||
list->destroy(list);
|
|
||||||
|
|
||||||
if (this->packet_tsi)
|
|
||||||
{
|
|
||||||
this->tsi->insert_first(this->tsi,
|
|
||||||
this->packet_tsi->clone(this->packet_tsi));
|
|
||||||
}
|
|
||||||
if (this->packet_tsr)
|
|
||||||
{
|
|
||||||
this->tsr->insert_first(this->tsr,
|
|
||||||
this->packet_tsr->clone(this->packet_tsr));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!generic_label_only(this) && !this->child.label)
|
if (!generic_label_only(this) && !this->child.label)
|
||||||
{ /* in the simple label mode we propose the configured label as we
|
{ /* in the simple label mode we propose the configured label as we
|
||||||
@@ -2544,6 +2593,61 @@ METHOD(child_create_t, use_label, void,
|
|||||||
this->child.label = label ? label->clone(label) : NULL;
|
this->child.label = label ? label->clone(label) : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare traffic selectors for reuse when recreating a CHILD_SA.
|
||||||
|
*/
|
||||||
|
static void reuse_ts(private_child_create_t *this, bool local, child_sa_t *old,
|
||||||
|
traffic_selector_list_t **target)
|
||||||
|
{
|
||||||
|
enumerator_t *old_ts, *hosts_enum;
|
||||||
|
linked_list_t *hosts, *list;
|
||||||
|
traffic_selector_t *ts, *new_ts;
|
||||||
|
host_t *host;
|
||||||
|
|
||||||
|
old_ts = old->create_ts_enumerator(old, local);
|
||||||
|
if (this->rekey)
|
||||||
|
{
|
||||||
|
/* when rekeying, we just reuse the previous TS. this is also the only
|
||||||
|
* way a responder reuses TS */
|
||||||
|
*target = traffic_selector_list_create_from_enumerator(old_ts);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* when recreating/reauthenticating, we check whether the dynamic IPs of
|
||||||
|
* the IKE_SA (as copied from the old SA) match the TS and replace
|
||||||
|
* them with dynamic TS (reusing protocol/ports in case of narrowing) so
|
||||||
|
* they get updated to possibly new IPs when the TS are prepared later */
|
||||||
|
list = linked_list_create();
|
||||||
|
hosts = ike_sa_get_dynamic_hosts(this->ike_sa, local);
|
||||||
|
hosts_enum = hosts->create_enumerator(hosts);
|
||||||
|
while (old_ts->enumerate(old_ts, &ts))
|
||||||
|
{
|
||||||
|
new_ts = NULL;
|
||||||
|
while (hosts_enum->enumerate(hosts_enum, &host))
|
||||||
|
{
|
||||||
|
if (ts->is_host(ts, host))
|
||||||
|
{
|
||||||
|
new_ts = traffic_selector_create_dynamic(ts->get_protocol(ts),
|
||||||
|
ts->get_from_port(ts),
|
||||||
|
ts->get_to_port(ts));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hosts->reset_enumerator(hosts, hosts_enum);
|
||||||
|
|
||||||
|
if (!new_ts)
|
||||||
|
{
|
||||||
|
new_ts = ts->clone(ts);
|
||||||
|
}
|
||||||
|
list->insert_last(list, new_ts);
|
||||||
|
}
|
||||||
|
hosts_enum->destroy(hosts_enum);
|
||||||
|
hosts->destroy(hosts);
|
||||||
|
old_ts->destroy(old_ts);
|
||||||
|
|
||||||
|
*target = traffic_selector_list_create_from_list(list);
|
||||||
|
}
|
||||||
|
|
||||||
METHOD(child_create_t, recreate_sa, void,
|
METHOD(child_create_t, recreate_sa, void,
|
||||||
private_child_create_t *this, child_sa_t *old)
|
private_child_create_t *this, child_sa_t *old)
|
||||||
{
|
{
|
||||||
@@ -2560,6 +2664,10 @@ METHOD(child_create_t, recreate_sa, void,
|
|||||||
this->ke_method = ke_method;
|
this->ke_method = ke_method;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* use previously negotiated traffic selectors */
|
||||||
|
reuse_ts(this, TRUE, old, &this->my_ts);
|
||||||
|
reuse_ts(this, FALSE, old, &this->other_ts);
|
||||||
}
|
}
|
||||||
|
|
||||||
METHOD(child_create_t, get_child, child_sa_t*,
|
METHOD(child_create_t, get_child, child_sa_t*,
|
||||||
@@ -2619,32 +2727,17 @@ METHOD(task_t, migrate, void,
|
|||||||
chunk_free(&this->my_nonce);
|
chunk_free(&this->my_nonce);
|
||||||
chunk_free(&this->other_nonce);
|
chunk_free(&this->other_nonce);
|
||||||
chunk_free(&this->link);
|
chunk_free(&this->link);
|
||||||
if (this->tsr)
|
DESTROY_OFFSET_IF(this->tsr, offsetof(traffic_selector_t, destroy));
|
||||||
{
|
DESTROY_OFFSET_IF(this->tsi, offsetof(traffic_selector_t, destroy));
|
||||||
this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy));
|
DESTROY_OFFSET_IF(this->labels_i, offsetof(sec_label_t, destroy));
|
||||||
}
|
DESTROY_OFFSET_IF(this->labels_r, offsetof(sec_label_t, destroy));
|
||||||
if (this->tsi)
|
|
||||||
{
|
|
||||||
this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
|
|
||||||
}
|
|
||||||
if (this->labels_i)
|
|
||||||
{
|
|
||||||
this->labels_i->destroy_offset(this->labels_i, offsetof(sec_label_t, destroy));
|
|
||||||
}
|
|
||||||
if (this->labels_r)
|
|
||||||
{
|
|
||||||
this->labels_r->destroy_offset(this->labels_r, offsetof(sec_label_t, destroy));
|
|
||||||
}
|
|
||||||
DESTROY_IF(this->child_sa);
|
DESTROY_IF(this->child_sa);
|
||||||
DESTROY_IF(this->proposal);
|
DESTROY_IF(this->proposal);
|
||||||
DESTROY_IF(this->nonceg);
|
DESTROY_IF(this->nonceg);
|
||||||
DESTROY_IF(this->ke);
|
DESTROY_IF(this->ke);
|
||||||
this->ke_failed = FALSE;
|
this->ke_failed = FALSE;
|
||||||
clear_key_exchanges(this);
|
clear_key_exchanges(this);
|
||||||
if (this->proposals)
|
DESTROY_OFFSET_IF(this->proposals, offsetof(proposal_t, destroy));
|
||||||
{
|
|
||||||
this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy));
|
|
||||||
}
|
|
||||||
if (!this->rekey && !this->retry)
|
if (!this->rekey && !this->retry)
|
||||||
{
|
{
|
||||||
this->ke_method = KE_NONE;
|
this->ke_method = KE_NONE;
|
||||||
@@ -2675,22 +2768,10 @@ METHOD(task_t, destroy, void,
|
|||||||
chunk_free(&this->my_nonce);
|
chunk_free(&this->my_nonce);
|
||||||
chunk_free(&this->other_nonce);
|
chunk_free(&this->other_nonce);
|
||||||
chunk_free(&this->link);
|
chunk_free(&this->link);
|
||||||
if (this->tsr)
|
DESTROY_OFFSET_IF(this->tsr, offsetof(traffic_selector_t, destroy));
|
||||||
{
|
DESTROY_OFFSET_IF(this->tsi, offsetof(traffic_selector_t, destroy));
|
||||||
this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy));
|
DESTROY_OFFSET_IF(this->labels_i, offsetof(sec_label_t, destroy));
|
||||||
}
|
DESTROY_OFFSET_IF(this->labels_r, offsetof(sec_label_t, destroy));
|
||||||
if (this->tsi)
|
|
||||||
{
|
|
||||||
this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
|
|
||||||
}
|
|
||||||
if (this->labels_i)
|
|
||||||
{
|
|
||||||
this->labels_i->destroy_offset(this->labels_i, offsetof(sec_label_t, destroy));
|
|
||||||
}
|
|
||||||
if (this->labels_r)
|
|
||||||
{
|
|
||||||
this->labels_r->destroy_offset(this->labels_r, offsetof(sec_label_t, destroy));
|
|
||||||
}
|
|
||||||
if (!this->established)
|
if (!this->established)
|
||||||
{
|
{
|
||||||
DESTROY_IF(this->child_sa);
|
DESTROY_IF(this->child_sa);
|
||||||
@@ -2701,13 +2782,12 @@ METHOD(task_t, destroy, void,
|
|||||||
}
|
}
|
||||||
DESTROY_IF(this->packet_tsi);
|
DESTROY_IF(this->packet_tsi);
|
||||||
DESTROY_IF(this->packet_tsr);
|
DESTROY_IF(this->packet_tsr);
|
||||||
|
DESTROY_IF(this->my_ts);
|
||||||
|
DESTROY_IF(this->other_ts);
|
||||||
DESTROY_IF(this->proposal);
|
DESTROY_IF(this->proposal);
|
||||||
DESTROY_IF(this->ke);
|
DESTROY_IF(this->ke);
|
||||||
clear_key_exchanges(this);
|
clear_key_exchanges(this);
|
||||||
if (this->proposals)
|
DESTROY_OFFSET_IF(this->proposals, offsetof(proposal_t, destroy));
|
||||||
{
|
|
||||||
this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy));
|
|
||||||
}
|
|
||||||
DESTROY_IF(this->config);
|
DESTROY_IF(this->config);
|
||||||
DESTROY_IF(this->nonceg);
|
DESTROY_IF(this->nonceg);
|
||||||
DESTROY_IF(this->child.label);
|
DESTROY_IF(this->child.label);
|
||||||
|
|||||||
Reference in New Issue
Block a user