summaryrefslogtreecommitdiff
path: root/src/libcharon/sa/ikev2/tasks
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcharon/sa/ikev2/tasks')
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_create.c96
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_delete.c229
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_rekey.c16
3 files changed, 244 insertions, 97 deletions
diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c
index 71cb6b8ea..896cabb2b 100644
--- a/src/libcharon/sa/ikev2/tasks/child_create.c
+++ b/src/libcharon/sa/ikev2/tasks/child_create.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2016 Tobias Brunner
+ * Copyright (C) 2008-2017 Tobias Brunner
* Copyright (C) 2005-2008 Martin Willi
* Copyright (C) 2005 Jan Hutter
* HSR Hochschule fuer Technik Rapperswil
@@ -602,7 +602,7 @@ static status_t select_and_install(private_child_create_t *this,
switch (this->mode)
{
case MODE_TRANSPORT:
- if (!this->config->use_proxy_mode(this->config) &&
+ if (!this->config->has_option(this->config, OPT_PROXY_MODE) &&
(!ts_list_is_host(this->tsi, other) ||
!ts_list_is_host(this->tsr, me))
)
@@ -630,6 +630,32 @@ static status_t select_and_install(private_child_create_t *this,
default:
break;
}
+ /* use a copy of the traffic selectors, as the POST hook should not
+ * change payloads */
+ my_ts = this->tsr->clone_offset(this->tsr,
+ offsetof(traffic_selector_t, clone));
+ other_ts = this->tsi->clone_offset(this->tsi,
+ offsetof(traffic_selector_t, clone));
+ charon->bus->narrow(charon->bus, this->child_sa,
+ NARROW_RESPONDER_POST, my_ts, other_ts);
+
+ if (my_ts->get_count(my_ts) == 0 || other_ts->get_count(other_ts) == 0)
+ {
+ my_ts->destroy_offset(my_ts,
+ offsetof(traffic_selector_t, destroy));
+ other_ts->destroy_offset(other_ts,
+ offsetof(traffic_selector_t, destroy));
+ return NOT_FOUND;
+ }
+ }
+
+ this->child_sa->set_policies(this->child_sa, my_ts, other_ts);
+ if (!this->initiator)
+ {
+ my_ts->destroy_offset(my_ts,
+ offsetof(traffic_selector_t, destroy));
+ other_ts->destroy_offset(other_ts,
+ offsetof(traffic_selector_t, destroy));
}
this->child_sa->set_state(this->child_sa, CHILD_INSTALLING);
@@ -651,19 +677,30 @@ 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, my_ts, other_ts);
+ 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, my_ts, other_ts);
+ FALSE, this->tfcv3);
}
- else
+ else if (!this->rekey)
{
status_i = this->child_sa->install(this->child_sa, encr_i, integ_i,
this->my_spi, this->my_cpi, this->initiator,
- TRUE, this->tfcv3, my_ts, other_ts);
+ TRUE, this->tfcv3);
status_o = this->child_sa->install(this->child_sa, encr_r, integ_r,
this->other_spi, this->other_cpi, this->initiator,
- FALSE, this->tfcv3, my_ts, other_ts);
+ 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;
}
}
@@ -679,36 +716,8 @@ static status_t select_and_install(private_child_create_t *this,
}
else
{
- if (this->initiator)
- {
- status = this->child_sa->add_policies(this->child_sa,
- my_ts, other_ts);
- }
- else
- {
- /* use a copy of the traffic selectors, as the POST hook should not
- * change payloads */
- my_ts = this->tsr->clone_offset(this->tsr,
- offsetof(traffic_selector_t, clone));
- other_ts = this->tsi->clone_offset(this->tsi,
- offsetof(traffic_selector_t, clone));
- charon->bus->narrow(charon->bus, this->child_sa,
- NARROW_RESPONDER_POST, my_ts, other_ts);
- if (my_ts->get_count(my_ts) == 0 ||
- other_ts->get_count(other_ts) == 0)
- {
- status = FAILED;
- }
- else
- {
- status = this->child_sa->add_policies(this->child_sa,
- my_ts, other_ts);
- }
- my_ts->destroy_offset(my_ts,
- offsetof(traffic_selector_t, destroy));
- other_ts->destroy_offset(other_ts,
- offsetof(traffic_selector_t, destroy));
- }
+ status = this->child_sa->install_policies(this->child_sa);
+
if (status != SUCCESS)
{
DBG1(DBG_IKE, "unable to install IPsec policies (SPD) in kernel");
@@ -736,7 +745,6 @@ 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);
- /* 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);
this->established = TRUE;
@@ -748,16 +756,17 @@ static status_t select_and_install(private_child_create_t *this,
other_ts = linked_list_create_from_enumerator(
this->child_sa->create_ts_enumerator(this->child_sa, FALSE));
- DBG0(DBG_IKE, "CHILD_SA %s{%d} established "
+ DBG0(DBG_IKE, "%sCHILD_SA %s{%d} established "
"with SPIs %.8x_i %.8x_o and TS %#R === %#R",
+ this->rekey && !this->initiator ? "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)),
- ntohl(this->child_sa->get_spi(this->child_sa, FALSE)), my_ts, other_ts);
+ ntohl(this->child_sa->get_spi(this->child_sa, FALSE)),
+ my_ts, other_ts);
my_ts->destroy(my_ts);
other_ts->destroy(other_ts);
-
return SUCCESS;
}
@@ -1073,7 +1082,7 @@ METHOD(task_t, build_i, status_t,
this->dh_group);
}
- if (this->config->use_ipcomp(this->config))
+ if (this->config->has_option(this->config, OPT_IPCOMP))
{
/* IPCOMP_DEFLATE is the only transform we support at the moment */
add_ipcomp_notify(this, message, IPCOMP_DEFLATE);
@@ -1327,7 +1336,7 @@ METHOD(task_t, build_r, status_t,
if (this->ipcomp_received != IPCOMP_NONE)
{
- if (this->config->use_ipcomp(this->config))
+ if (this->config->has_option(this->config, OPT_IPCOMP))
{
add_ipcomp_notify(this, message, this->ipcomp_received);
}
@@ -1690,7 +1699,6 @@ METHOD(task_t, destroy, void,
{
this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy));
}
-
DESTROY_IF(this->config);
DESTROY_IF(this->nonceg);
free(this);
diff --git a/src/libcharon/sa/ikev2/tasks/child_delete.c b/src/libcharon/sa/ikev2/tasks/child_delete.c
index 6fa8836ac..626796383 100644
--- a/src/libcharon/sa/ikev2/tasks/child_delete.c
+++ b/src/libcharon/sa/ikev2/tasks/child_delete.c
@@ -18,9 +18,14 @@
#include <daemon.h>
#include <encoding/payloads/delete_payload.h>
+#include <processing/jobs/delete_child_sa_job.h>
#include <sa/ikev2/tasks/child_create.h>
#include <sa/ikev2/tasks/child_rekey.h>
+#ifndef DELETE_REKEYED_DELAY
+#define DELETE_REKEYED_DELAY 5
+#endif
+
typedef struct private_child_delete_t private_child_delete_t;
/**
@@ -39,67 +44,80 @@ struct private_child_delete_t {
ike_sa_t *ike_sa;
/**
- * Are we the initiator?
+ * Whether we are the initiator of the exchange
*/
bool initiator;
/**
- * Protocol of CHILD_SA to delete
+ * Protocol of CHILD_SA to delete (as initiator)
*/
protocol_id_t protocol;
/**
- * Inbound SPI of CHILD_SA to delete
+ * Inbound SPI of CHILD_SA to delete (as initiator)
*/
uint32_t spi;
/**
- * whether to enforce delete action policy
- */
- bool check_delete_action;
-
- /**
- * is this delete exchange following a rekey?
- */
- bool rekeyed;
-
- /**
- * CHILD_SA already expired?
+ * CHILD_SA already expired (as initiator)
*/
bool expired;
/**
- * CHILD_SAs which get deleted
+ * CHILD_SAs which get deleted, entry_t*
*/
linked_list_t *child_sas;
};
/**
+ * Information about a deleted CHILD_SA
+ */
+typedef struct {
+ /** Deleted CHILD_SA */
+ child_sa_t *child_sa;
+ /** Whether the CHILD_SA was rekeyed */
+ bool rekeyed;
+ /** Whether to enforce any delete action policy */
+ bool check_delete_action;
+} entry_t;
+
+CALLBACK(match_child, bool,
+ entry_t *entry, va_list args)
+{
+ child_sa_t *child_sa;
+
+ VA_ARGS_VGET(args, child_sa);
+ return entry->child_sa == child_sa;
+}
+
+/**
* build the delete payloads from the listed child_sas
*/
static void build_payloads(private_child_delete_t *this, message_t *message)
{
delete_payload_t *ah = NULL, *esp = NULL;
enumerator_t *enumerator;
- child_sa_t *child_sa;
+ entry_t *entry;
+ protocol_id_t protocol;
+ uint32_t spi;
enumerator = this->child_sas->create_enumerator(this->child_sas);
- while (enumerator->enumerate(enumerator, (void**)&child_sa))
+ while (enumerator->enumerate(enumerator, (void**)&entry))
{
- protocol_id_t protocol = child_sa->get_protocol(child_sa);
- uint32_t spi = child_sa->get_spi(child_sa, TRUE);
+ protocol = entry->child_sa->get_protocol(entry->child_sa);
+ spi = entry->child_sa->get_spi(entry->child_sa, TRUE);
switch (protocol)
{
case PROTO_ESP:
- if (esp == NULL)
+ if (!esp)
{
esp = delete_payload_create(PLV2_DELETE, PROTO_ESP);
message->add_payload(message, (payload_t*)esp);
}
esp->add_spi(esp, spi);
DBG1(DBG_IKE, "sending DELETE for %N CHILD_SA with SPI %.8x",
- protocol_id_names, protocol, ntohl(spi));
+ protocol_id_names, protocol, ntohl(spi));
break;
case PROTO_AH:
if (ah == NULL)
@@ -109,12 +127,12 @@ static void build_payloads(private_child_delete_t *this, message_t *message)
}
ah->add_spi(ah, spi);
DBG1(DBG_IKE, "sending DELETE for %N CHILD_SA with SPI %.8x",
- protocol_id_names, protocol, ntohl(spi));
+ protocol_id_names, protocol, ntohl(spi));
break;
default:
break;
}
- child_sa->set_state(child_sa, CHILD_DELETING);
+ entry->child_sa->set_state(entry->child_sa, CHILD_DELETING);
}
enumerator->destroy(enumerator);
}
@@ -147,6 +165,57 @@ static bool is_redundant(private_child_delete_t *this, child_sa_t *child)
}
/**
+ * Install the outbound CHILD_SA with the given SPI
+ */
+static void install_outbound(private_child_delete_t *this,
+ protocol_id_t protocol, uint32_t spi)
+{
+ child_sa_t *child_sa;
+ linked_list_t *my_ts, *other_ts;
+ status_t status;
+
+ child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol,
+ spi, FALSE);
+ if (!child_sa)
+ {
+ DBG1(DBG_IKE, "CHILD_SA not found after rekeying");
+ return;
+ }
+ if (this->initiator && is_redundant(this, child_sa))
+ { /* if we won the rekey collision we don't want to install the
+ * redundant SA created by the peer */
+ return;
+ }
+
+ status = child_sa->install_outbound(child_sa);
+ if (status != SUCCESS)
+ {
+ DBG1(DBG_IKE, "unable to install outbound IPsec SA (SAD) in kernel");
+ charon->bus->alert(charon->bus, ALERT_INSTALL_CHILD_SA_FAILED,
+ child_sa);
+ /* 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));
+ other_ts = linked_list_create_from_enumerator(
+ child_sa->create_ts_enumerator(child_sa, FALSE));
+
+ DBG0(DBG_IKE, "outbound CHILD_SA %s{%d} established "
+ "with SPIs %.8x_i %.8x_o and TS %#R === %#R",
+ child_sa->get_name(child_sa),
+ child_sa->get_unique_id(child_sa),
+ ntohl(child_sa->get_spi(child_sa, TRUE)),
+ ntohl(child_sa->get_spi(child_sa, FALSE)),
+ my_ts, other_ts);
+
+ my_ts->destroy(my_ts);
+ other_ts->destroy(other_ts);
+}
+
+/**
* read in payloads and find the children to delete
*/
static void process_payloads(private_child_delete_t *this, message_t *message)
@@ -157,6 +226,7 @@ static void process_payloads(private_child_delete_t *this, message_t *message)
uint32_t spi;
protocol_id_t protocol;
child_sa_t *child_sa;
+ entry_t *entry;
payloads = message->create_payload_enumerator(message);
while (payloads->enumerate(payloads, &payload))
@@ -174,27 +244,37 @@ static void process_payloads(private_child_delete_t *this, message_t *message)
{
child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol,
spi, FALSE);
- if (child_sa == NULL)
+ if (!child_sa)
{
- DBG1(DBG_IKE, "received DELETE for %N CHILD_SA with SPI %.8x, "
- "but no such SA", protocol_id_names, protocol, ntohl(spi));
+ DBG1(DBG_IKE, "received DELETE for unknown %N CHILD_SA with"
+ " SPI %.8x", protocol_id_names, protocol, ntohl(spi));
continue;
}
DBG1(DBG_IKE, "received DELETE for %N CHILD_SA with SPI %.8x",
protocol_id_names, protocol, ntohl(spi));
+ if (this->child_sas->find_first(this->child_sas, match_child,
+ NULL, child_sa))
+ {
+ continue;
+ }
+ INIT(entry,
+ .child_sa = child_sa
+ );
switch (child_sa->get_state(child_sa))
{
case CHILD_REKEYED:
- this->rekeyed = TRUE;
+ entry->rekeyed = TRUE;
break;
case CHILD_DELETING:
- /* we don't send back a delete if we initiated ourself */
+ /* we don't send back a delete if we already initiated
+ * a delete ourself */
if (!this->initiator)
{
+ free(entry);
continue;
}
- /* fall through */
+ break;
case CHILD_REKEYING:
/* we reply as usual, rekeying will fail */
case CHILD_INSTALLED:
@@ -202,22 +282,18 @@ static void process_payloads(private_child_delete_t *this, message_t *message)
{
if (is_redundant(this, child_sa))
{
- this->rekeyed = TRUE;
+ entry->rekeyed = TRUE;
}
else
{
- this->check_delete_action = TRUE;
+ entry->check_delete_action = TRUE;
}
}
break;
default:
break;
}
- if (this->child_sas->find_first(this->child_sas, NULL,
- (void**)&child_sa) != SUCCESS)
- {
- this->child_sas->insert_last(this->child_sas, child_sa);
- }
+ this->child_sas->insert_last(this->child_sas, entry);
}
spis->destroy(spis);
}
@@ -231,29 +307,64 @@ static void process_payloads(private_child_delete_t *this, message_t *message)
static status_t destroy_and_reestablish(private_child_delete_t *this)
{
enumerator_t *enumerator;
+ entry_t *entry;
child_sa_t *child_sa;
child_cfg_t *child_cfg;
protocol_id_t protocol;
- uint32_t spi, reqid;
+ uint32_t spi, reqid, rekey_spi;
action_t action;
status_t status = SUCCESS;
+ time_t now, expire;
+ u_int delay;
+
+ now = time_monotonic(NULL);
+ delay = lib->settings->get_int(lib->settings, "%s.delete_rekeyed_delay",
+ DELETE_REKEYED_DELAY, lib->ns);
enumerator = this->child_sas->create_enumerator(this->child_sas);
- while (enumerator->enumerate(enumerator, (void**)&child_sa))
+ while (enumerator->enumerate(enumerator, (void**)&entry))
{
+ child_sa = entry->child_sa;
/* signal child down event if we weren't rekeying */
- if (!this->rekeyed)
+ protocol = child_sa->get_protocol(child_sa);
+ if (!entry->rekeyed)
{
charon->bus->child_updown(charon->bus, child_sa, FALSE);
}
+ else
+ {
+ rekey_spi = child_sa->get_rekey_spi(child_sa);
+ if (rekey_spi)
+ {
+ install_outbound(this, protocol, rekey_spi);
+ }
+ /* for rekeyed CHILD_SAs we uninstall the outbound SA but don't
+ * immediately destroy it, by default, so we can process delayed
+ * packets */
+ child_sa->remove_outbound(child_sa);
+ expire = child_sa->get_lifetime(child_sa, TRUE);
+ if (delay && (!expire || ((now + delay) < expire)))
+ {
+ lib->scheduler->schedule_job(lib->scheduler,
+ (job_t*)delete_child_sa_job_create_id(
+ child_sa->get_unique_id(child_sa)), delay);
+ continue;
+ }
+ else if (expire)
+ { /* let it expire naturally */
+ continue;
+ }
+ /* no delay and no lifetime, destroy it immediately */
+ }
spi = child_sa->get_spi(child_sa, TRUE);
reqid = child_sa->get_reqid(child_sa);
- protocol = child_sa->get_protocol(child_sa);
child_cfg = child_sa->get_config(child_sa);
child_cfg->get_ref(child_cfg);
action = child_sa->get_close_action(child_sa);
+
this->ike_sa->destroy_child_sa(this->ike_sa, protocol, spi);
- if (this->check_delete_action)
+
+ if (entry->check_delete_action)
{ /* enforce child_cfg policy if deleted passively */
switch (action)
{
@@ -288,12 +399,14 @@ static void log_children(private_child_delete_t *this)
{
linked_list_t *my_ts, *other_ts;
enumerator_t *enumerator;
+ entry_t *entry;
child_sa_t *child_sa;
uint64_t bytes_in, bytes_out;
enumerator = this->child_sas->create_enumerator(this->child_sas);
- while (enumerator->enumerate(enumerator, (void**)&child_sa))
+ while (enumerator->enumerate(enumerator, (void**)&entry))
{
+ child_sa = entry->child_sa;
my_ts = linked_list_create_from_enumerator(
child_sa->create_ts_enumerator(child_sa, TRUE));
other_ts = linked_list_create_from_enumerator(
@@ -328,6 +441,7 @@ METHOD(task_t, build_i, status_t,
private_child_delete_t *this, message_t *message)
{
child_sa_t *child_sa;
+ entry_t *entry;
child_sa = this->ike_sa->get_child_sa(this->ike_sa, this->protocol,
this->spi, TRUE);
@@ -342,15 +456,24 @@ METHOD(task_t, build_i, status_t,
/* we work only with the inbound SPI */
this->spi = child_sa->get_spi(child_sa, TRUE);
}
- this->child_sas->insert_last(this->child_sas, child_sa);
- if (child_sa->get_state(child_sa) == CHILD_REKEYED)
- {
- this->rekeyed = TRUE;
+
+ if (child_sa->get_state(child_sa) == CHILD_DELETING)
+ { /* DELETEs for this CHILD_SA were already exchanged, but it was not yet
+ * destroyed to allow delayed packets to get processed */
+ this->ike_sa->destroy_child_sa(this->ike_sa, this->protocol, this->spi);
+ message->set_exchange_type(message, EXCHANGE_TYPE_UNDEFINED);
+ return SUCCESS;
}
+
+ INIT(entry,
+ .child_sa = child_sa,
+ .rekeyed = child_sa->get_state(child_sa) == CHILD_REKEYED,
+ );
+ this->child_sas->insert_last(this->child_sas, entry);
log_children(this);
build_payloads(this, message);
- if (!this->rekeyed && this->expired)
+ if (!entry->rekeyed && this->expired)
{
child_cfg_t *child_cfg;
@@ -397,24 +520,28 @@ METHOD(child_delete_t , get_child, child_sa_t*,
private_child_delete_t *this)
{
child_sa_t *child_sa = NULL;
- this->child_sas->get_first(this->child_sas, (void**)&child_sa);
+ entry_t *entry;
+
+ if (this->child_sas->get_first(this->child_sas, (void**)&entry) == SUCCESS)
+ {
+ child_sa = entry->child_sa;
+ }
return child_sa;
}
METHOD(task_t, migrate, void,
private_child_delete_t *this, ike_sa_t *ike_sa)
{
- this->check_delete_action = FALSE;
this->ike_sa = ike_sa;
- this->child_sas->destroy(this->child_sas);
+ this->child_sas->destroy_function(this->child_sas, free);
this->child_sas = linked_list_create();
}
METHOD(task_t, destroy, void,
private_child_delete_t *this)
{
- this->child_sas->destroy(this->child_sas);
+ this->child_sas->destroy_function(this->child_sas, free);
free(this);
}
diff --git a/src/libcharon/sa/ikev2/tasks/child_rekey.c b/src/libcharon/sa/ikev2/tasks/child_rekey.c
index c04ec141f..761c860e7 100644
--- a/src/libcharon/sa/ikev2/tasks/child_rekey.c
+++ b/src/libcharon/sa/ikev2/tasks/child_rekey.c
@@ -132,6 +132,7 @@ static void find_child(private_child_rekey_t *this, message_t *message)
notify_payload_t *notify;
protocol_id_t protocol;
uint32_t spi;
+ child_sa_t *child_sa;
notify = message->get_notify(message, REKEY_SA);
if (notify)
@@ -141,8 +142,15 @@ static void find_child(private_child_rekey_t *this, message_t *message)
if (protocol == PROTO_ESP || protocol == PROTO_AH)
{
- this->child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol,
- spi, FALSE);
+ child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol,
+ spi, FALSE);
+ if (child_sa &&
+ child_sa->get_state(child_sa) == CHILD_DELETING &&
+ child_sa->get_outbound_state(child_sa) == CHILD_OUTBOUND_NONE)
+ { /* ignore rekeyed CHILD_SAs we keep around */
+ return;
+ }
+ this->child_sa = child_sa;
}
}
}
@@ -227,6 +235,7 @@ METHOD(task_t, build_r, status_t,
child_cfg_t *config;
uint32_t reqid;
child_sa_state_t state;
+ child_sa_t *child_sa;
if (!this->child_sa)
{
@@ -260,7 +269,10 @@ METHOD(task_t, build_r, status_t,
return SUCCESS;
}
+ child_sa = this->child_create->get_child(this->child_create);
this->child_sa->set_state(this->child_sa, CHILD_REKEYED);
+ this->child_sa->set_rekey_spi(this->child_sa,
+ child_sa->get_spi(child_sa, FALSE));
/* invoke rekey hook */
charon->bus->child_rekey(charon->bus, this->child_sa,