summaryrefslogtreecommitdiff
path: root/src/libcharon/sa
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcharon/sa')
-rw-r--r--src/libcharon/sa/authenticator.c9
-rw-r--r--src/libcharon/sa/authenticator.h10
-rw-r--r--src/libcharon/sa/child_sa.c147
-rw-r--r--src/libcharon/sa/child_sa.h25
-rw-r--r--src/libcharon/sa/child_sa_manager.c333
-rw-r--r--src/libcharon/sa/child_sa_manager.h89
-rw-r--r--src/libcharon/sa/eap/eap_method.h12
-rw-r--r--src/libcharon/sa/ike_sa.c82
-rw-r--r--src/libcharon/sa/ike_sa.h10
-rw-r--r--src/libcharon/sa/ike_sa_manager.c95
-rw-r--r--src/libcharon/sa/ike_sa_manager.h10
-rw-r--r--src/libcharon/sa/ikev1/authenticators/psk_v1_authenticator.c10
-rw-r--r--src/libcharon/sa/ikev1/authenticators/pubkey_v1_authenticator.c11
-rw-r--r--src/libcharon/sa/ikev1/keymat_v1.c9
-rw-r--r--src/libcharon/sa/ikev1/phase1.c14
-rw-r--r--src/libcharon/sa/ikev1/task_manager_v1.c5
-rw-r--r--src/libcharon/sa/ikev1/tasks/isakmp_delete.c39
-rw-r--r--src/libcharon/sa/ikev1/tasks/main_mode.c39
-rw-r--r--src/libcharon/sa/ikev1/tasks/mode_config.c137
-rw-r--r--src/libcharon/sa/ikev1/tasks/quick_delete.c6
-rw-r--r--src/libcharon/sa/ikev1/tasks/quick_mode.c76
-rw-r--r--src/libcharon/sa/ikev1/tasks/quick_mode.h8
-rw-r--r--src/libcharon/sa/ikev2/authenticators/eap_authenticator.c7
-rw-r--r--src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c314
-rw-r--r--src/libcharon/sa/ikev2/keymat_v2.c35
-rw-r--r--src/libcharon/sa/ikev2/keymat_v2.h18
-rw-r--r--src/libcharon/sa/ikev2/task_manager_v2.c113
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_create.c84
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_create.h8
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_delete.c4
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_rekey.c10
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_cert_pre.c4
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_config.c19
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_init.c206
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_mobike.c27
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_reauth.h2
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_reauth_complete.c102
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_reauth_complete.h56
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_rekey.c36
-rw-r--r--src/libcharon/sa/task.c1
-rw-r--r--src/libcharon/sa/task.h6
-rw-r--r--src/libcharon/sa/trap_manager.c11
42 files changed, 1940 insertions, 299 deletions
diff --git a/src/libcharon/sa/authenticator.c b/src/libcharon/sa/authenticator.c
index 8571274ac..6c3681a2d 100644
--- a/src/libcharon/sa/authenticator.c
+++ b/src/libcharon/sa/authenticator.c
@@ -31,12 +31,14 @@ ENUM_BEGIN(auth_method_names, AUTH_RSA, AUTH_DSS,
"RSA signature",
"pre-shared key",
"DSS signature");
-ENUM_NEXT(auth_method_names, AUTH_ECDSA_256, AUTH_GSPM, AUTH_DSS,
+ENUM_NEXT(auth_method_names, AUTH_ECDSA_256, AUTH_DS, AUTH_DSS,
"ECDSA-256 signature",
"ECDSA-384 signature",
"ECDSA-521 signature",
- "secure password method");
-ENUM_NEXT(auth_method_names, AUTH_XAUTH_INIT_PSK, AUTH_HYBRID_RESP_RSA, AUTH_GSPM,
+ "secure password method",
+ "NULL authentication",
+ "digital signature");
+ENUM_NEXT(auth_method_names, AUTH_XAUTH_INIT_PSK, AUTH_HYBRID_RESP_RSA, AUTH_DS,
"XAuthInitPSK",
"XAuthRespPSK",
"XAuthInitRSA",
@@ -99,6 +101,7 @@ authenticator_t *authenticator_create_verifier(
case AUTH_ECDSA_256:
case AUTH_ECDSA_384:
case AUTH_ECDSA_521:
+ case AUTH_DS:
return (authenticator_t*)pubkey_authenticator_create_verifier(ike_sa,
sent_nonce, received_init, reserved);
case AUTH_PSK:
diff --git a/src/libcharon/sa/authenticator.h b/src/libcharon/sa/authenticator.h
index 914f42d9d..97c042e71 100644
--- a/src/libcharon/sa/authenticator.h
+++ b/src/libcharon/sa/authenticator.h
@@ -80,6 +80,16 @@ enum auth_method_t {
AUTH_GSPM = 12,
/**
+ * NULL Authentication Method as specified in draft-ietf-ipsecme-ikev2-null-auth
+ */
+ AUTH_NULL = 13,
+
+ /**
+ * Digital Signature as specified in RFC 7427
+ */
+ AUTH_DS = 14,
+
+ /**
* IKEv1 initiator XAUTH with PSK, outside of IANA range
*/
AUTH_XAUTH_INIT_PSK = 256,
diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c
index a96ab4e90..e0db2e655 100644
--- a/src/libcharon/sa/child_sa.c
+++ b/src/libcharon/sa/child_sa.c
@@ -34,6 +34,8 @@ ENUM(child_sa_state_names, CHILD_CREATED, CHILD_DESTROYING,
"INSTALLED",
"UPDATING",
"REKEYING",
+ "REKEYED",
+ "RETRYING",
"DELETING",
"DESTROYING",
);
@@ -100,6 +102,16 @@ struct private_child_sa_t {
u_int32_t reqid;
/**
+ * Did we allocate/confirm and must release the reqid?
+ */
+ bool reqid_allocated;
+
+ /*
+ * Unique CHILD_SA identifier
+ */
+ u_int32_t unique_id;
+
+ /**
* inbound mark used for this child_sa
*/
mark_t mark_in;
@@ -228,6 +240,12 @@ METHOD(child_sa_t, get_reqid, u_int32_t,
return this->reqid;
}
+METHOD(child_sa_t, get_unique_id, u_int32_t,
+ private_child_sa_t *this)
+{
+ return this->unique_id;
+}
+
METHOD(child_sa_t, get_config, child_cfg_t*,
private_child_sa_t *this)
{
@@ -602,7 +620,7 @@ METHOD(child_sa_t, alloc_spi, u_int32_t,
{
if (hydra->kernel_interface->get_spi(hydra->kernel_interface,
this->other_addr, this->my_addr,
- proto_ike2ip(protocol), this->reqid,
+ proto_ike2ip(protocol),
&this->my_spi) == SUCCESS)
{
/* if we allocate a SPI, but then are unable to establish the SA, we
@@ -618,7 +636,7 @@ METHOD(child_sa_t, alloc_cpi, u_int16_t,
{
if (hydra->kernel_interface->get_cpi(hydra->kernel_interface,
this->other_addr, this->my_addr,
- this->reqid, &this->my_cpi) == SUCCESS)
+ &this->my_cpi) == SUCCESS)
{
return this->my_cpi;
}
@@ -632,7 +650,7 @@ METHOD(child_sa_t, install, status_t,
{
u_int16_t enc_alg = ENCR_UNDEFINED, int_alg = AUTH_UNDEFINED, size;
u_int16_t esn = NO_EXT_SEQ_NUMBERS;
- traffic_selector_t *src_ts = NULL, *dst_ts = NULL;
+ linked_list_t *src_ts = NULL, *dst_ts = NULL;
time_t now;
lifetime_cfg_t *lifetime;
u_int32_t tfc = 0;
@@ -680,6 +698,18 @@ METHOD(child_sa_t, install, status_t,
this->proposal->get_algorithm(this->proposal, EXTENDED_SEQUENCE_NUMBERS,
&esn, NULL);
+ if (!this->reqid_allocated)
+ {
+ status = hydra->kernel_interface->alloc_reqid(hydra->kernel_interface,
+ my_ts, other_ts, this->mark_in, this->mark_out,
+ &this->reqid);
+ if (status != SUCCESS)
+ {
+ return status;
+ }
+ this->reqid_allocated = TRUE;
+ }
+
lifetime = this->config->get_lifetime(this->config);
now = time_monotonic(NULL);
@@ -704,18 +734,16 @@ METHOD(child_sa_t, install, status_t,
lifetime->time.rekey = 0;
}
- /* BEET requires the bound address from the traffic selectors.
- * TODO: We add just the first traffic selector for now, as the
- * kernel accepts a single TS per SA only */
+ /* BEET requires the bound address from the traffic selectors */
if (inbound)
{
- my_ts->get_first(my_ts, (void**)&dst_ts);
- other_ts->get_first(other_ts, (void**)&src_ts);
+ dst_ts = my_ts;
+ src_ts = other_ts;
}
else
{
- my_ts->get_first(my_ts, (void**)&src_ts);
- other_ts->get_first(other_ts, (void**)&dst_ts);
+ src_ts = my_ts;
+ dst_ts = other_ts;
}
status = hydra->kernel_interface->add_sa(hydra->kernel_interface,
@@ -723,7 +751,7 @@ METHOD(child_sa_t, install, status_t,
inbound ? this->mark_in : this->mark_out, tfc,
lifetime, enc_alg, encr, int_alg, integ, this->mode,
this->ipcomp, cpi, this->config->get_replay_window(this->config),
- initiator, this->encap, esn, update, src_ts, dst_ts);
+ initiator, this->encap, esn, inbound, update, src_ts, dst_ts);
free(lifetime);
@@ -798,6 +826,19 @@ METHOD(child_sa_t, add_policies, status_t,
traffic_selector_t *my_ts, *other_ts;
status_t status = SUCCESS;
+ if (!this->reqid_allocated)
+ {
+ /* trap policy, get or confirm reqid */
+ status = hydra->kernel_interface->alloc_reqid(
+ hydra->kernel_interface, my_ts_list, other_ts_list,
+ this->mark_in, this->mark_out, &this->reqid);
+ if (status != SUCCESS)
+ {
+ return status;
+ }
+ this->reqid_allocated = TRUE;
+ }
+
/* apply traffic selectors */
enumerator = my_ts_list->create_enumerator(my_ts_list);
while (enumerator->enumerate(enumerator, &my_ts))
@@ -805,12 +846,15 @@ METHOD(child_sa_t, add_policies, status_t,
array_insert(this->my_ts, ARRAY_TAIL, my_ts->clone(my_ts));
}
enumerator->destroy(enumerator);
+ array_sort(this->my_ts, (void*)traffic_selector_cmp, NULL);
+
enumerator = other_ts_list->create_enumerator(other_ts_list);
while (enumerator->enumerate(enumerator, &other_ts))
{
array_insert(this->other_ts, ARRAY_TAIL, other_ts->clone(other_ts));
}
enumerator->destroy(enumerator);
+ array_sort(this->other_ts, (void*)traffic_selector_cmp, NULL);
if (this->config->install_policy(this->config))
{
@@ -1071,6 +1115,22 @@ METHOD(child_sa_t, destroy, void,
set_state(this, CHILD_DESTROYING);
+ if (this->config->install_policy(this->config))
+ {
+ /* delete all policies in the kernel */
+ enumerator = create_policy_enumerator(this);
+ while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
+ {
+ del_policies_internal(this, my_ts, other_ts, priority);
+ if (priority == POLICY_PRIORITY_DEFAULT && require_policy_update())
+ {
+ del_policies_internal(this, my_ts, other_ts,
+ POLICY_PRIORITY_FALLBACK);
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+
/* delete SAs in the kernel, if they are set up */
if (this->my_spi)
{
@@ -1087,20 +1147,13 @@ METHOD(child_sa_t, destroy, void,
this->mark_out);
}
- if (this->config->install_policy(this->config))
+ if (this->reqid_allocated)
{
- /* delete all policies in the kernel */
- enumerator = create_policy_enumerator(this);
- while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
+ if (hydra->kernel_interface->release_reqid(hydra->kernel_interface,
+ this->reqid, this->mark_in, this->mark_out) != SUCCESS)
{
- del_policies_internal(this, my_ts, other_ts, priority);
- if (priority == POLICY_PRIORITY_DEFAULT && require_policy_update())
- {
- del_policies_internal(this, my_ts, other_ts,
- POLICY_PRIORITY_FALLBACK);
- }
+ DBG1(DBG_CHD, "releasing reqid %u failed", this->reqid);
}
- enumerator->destroy(enumerator);
}
array_destroy_offset(this->my_ts, offsetof(traffic_selector_t, destroy));
@@ -1151,15 +1204,17 @@ static host_t* get_proxy_addr(child_cfg_t *config, host_t *ike, bool local)
* Described in header.
*/
child_sa_t * child_sa_create(host_t *me, host_t* other,
- child_cfg_t *config, u_int32_t rekey, bool encap)
+ child_cfg_t *config, u_int32_t rekey, bool encap,
+ u_int mark_in, u_int mark_out)
{
- static refcount_t reqid = 0;
private_child_sa_t *this;
+ static refcount_t unique_id = 0, unique_mark = 0, mark;
INIT(this,
.public = {
.get_name = _get_name,
.get_reqid = _get_reqid,
+ .get_unique_id = _get_unique_id,
.get_config = _get_config,
.get_state = _get_state,
.set_state = _set_state,
@@ -1201,6 +1256,7 @@ child_sa_t * child_sa_create(host_t *me, host_t* other,
.close_action = config->get_close_action(config),
.dpd_action = config->get_dpd_action(config),
.reqid = config->get_reqid(config),
+ .unique_id = ref_get(&unique_id),
.mark_in = config->get_mark(config, TRUE),
.mark_out = config->get_mark(config, FALSE),
.install_time = time_monotonic(NULL),
@@ -1209,9 +1265,37 @@ child_sa_t * child_sa_create(host_t *me, host_t* other,
this->config = config;
config->get_ref(config);
+ if (mark_in)
+ {
+ this->mark_in.value = mark_in;
+ }
+ if (mark_out)
+ {
+ this->mark_out.value = mark_out;
+ }
+ if (this->mark_in.value == MARK_UNIQUE ||
+ this->mark_out.value == MARK_UNIQUE)
+ {
+ mark = ref_get(&unique_mark);
+ if (this->mark_in.value == MARK_UNIQUE)
+ {
+ this->mark_in.value = mark;
+ }
+ if (this->mark_out.value == MARK_UNIQUE)
+ {
+ this->mark_out.value = mark;
+ }
+ }
+
if (!this->reqid)
{
- /* reuse old reqid if we are rekeying an existing CHILD_SA */
+ /* reuse old reqid if we are rekeying an existing CHILD_SA. While the
+ * reqid cache would find the same reqid for our selectors, this does
+ * not work in a special case: If an SA is triggered by a trap policy,
+ * but the negotiated SA gets narrowed, we still must reuse the same
+ * reqid to successfully "trigger" the SA on the kernel level. Rekeying
+ * such an SA requires an explicit reqid, as the cache currently knows
+ * the original selectors only for that reqid. */
if (rekey)
{
this->reqid = rekey;
@@ -1219,22 +1303,9 @@ child_sa_t * child_sa_create(host_t *me, host_t* other,
else
{
this->reqid = charon->traps->find_reqid(charon->traps, config);
- if (!this->reqid)
- {
- this->reqid = ref_get(&reqid);
- }
}
}
- if (this->mark_in.value == MARK_REQID)
- {
- this->mark_in.value = this->reqid;
- }
- if (this->mark_out.value == MARK_REQID)
- {
- this->mark_out.value = this->reqid;
- }
-
/* MIPv6 proxy transport mode sets SA endpoints to TS hosts */
if (config->get_mode(config) == MODE_TRANSPORT &&
config->use_proxy_mode(config))
diff --git a/src/libcharon/sa/child_sa.h b/src/libcharon/sa/child_sa.h
index a0c6c357f..debe8eb2c 100644
--- a/src/libcharon/sa/child_sa.h
+++ b/src/libcharon/sa/child_sa.h
@@ -68,6 +68,16 @@ enum child_sa_state_t {
CHILD_REKEYING,
/**
+ * CHILD_SA that was rekeyed, but stays installed
+ */
+ CHILD_REKEYED,
+
+ /**
+ * CHILD_SA negotiation failed, but gets retried
+ */
+ CHILD_RETRYING,
+
+ /**
* CHILD_SA in progress of delete
*/
CHILD_DELETING,
@@ -121,6 +131,16 @@ struct child_sa_t {
u_int32_t (*get_reqid)(child_sa_t *this);
/**
+ * Get the unique numerical identifier for this CHILD_SA.
+ *
+ * While the same reqid might be shared between multiple SAs, the unique_id
+ * is truly unique for all CHILD_SA instances.
+ *
+ * @return unique CHILD_SA identifier
+ */
+ u_int32_t (*get_unique_id)(child_sa_t *this);
+
+ /**
* Get the config used to set up this child sa.
*
* @return child_cfg
@@ -379,9 +399,12 @@ struct child_sa_t {
* @param config config to use for this CHILD_SA
* @param reqid reqid of old CHILD_SA when rekeying, 0 otherwise
* @param encap TRUE to enable UDP encapsulation (NAT traversal)
+ * @param mark_in explicit inbound mark value to use, 0 for config
+ * @param mark_out explicit outbound mark value to use, 0 for config
* @return child_sa_t object
*/
child_sa_t * child_sa_create(host_t *me, host_t *other, child_cfg_t *config,
- u_int32_t reqid, bool encap);
+ u_int32_t reqid, bool encap,
+ u_int mark_in, u_int mark_out);
#endif /** CHILD_SA_H_ @}*/
diff --git a/src/libcharon/sa/child_sa_manager.c b/src/libcharon/sa/child_sa_manager.c
new file mode 100644
index 000000000..071a119da
--- /dev/null
+++ b/src/libcharon/sa/child_sa_manager.c
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2014 Martin Willi
+ * Copyright (C) 2014 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "child_sa_manager.h"
+
+#include <daemon.h>
+#include <threading/mutex.h>
+#include <collections/hashtable.h>
+
+typedef struct private_child_sa_manager_t private_child_sa_manager_t;
+
+/**
+ * Private data of an child_sa_manager_t object.
+ */
+struct private_child_sa_manager_t {
+
+ /**
+ * Public child_sa_manager_t interface.
+ */
+ child_sa_manager_t public;
+
+ /**
+ * CHILD_SAs by inbound SPI/dst, child_entry_t => child_entry_t
+ */
+ hashtable_t *in;
+
+ /**
+ * CHILD_SAs by outbound SPI/dst, child_entry_t => child_entry_t
+ */
+ hashtable_t *out;
+
+ /**
+ * CHILD_SAs by unique ID, child_entry_t => child_entry_t
+ */
+ hashtable_t *ids;
+
+ /**
+ * Mutex to access any hashtable
+ */
+ mutex_t *mutex;
+};
+
+/**
+ * Hashtable entry for a known CHILD_SA
+ */
+typedef struct {
+ /** the associated IKE_SA */
+ ike_sa_id_t *ike_id;
+ /** unique CHILD_SA identifier */
+ u_int32_t unique_id;
+ /** inbound SPI */
+ u_int32_t spi_in;
+ /** outbound SPI */
+ u_int32_t spi_out;
+ /** inbound host address */
+ host_t *host_in;
+ /** outbound host address and port */
+ host_t *host_out;
+ /** IPsec protocol, AH|ESP */
+ protocol_id_t proto;
+} child_entry_t;
+
+/**
+ * Destroy a CHILD_SA entry
+ */
+static void child_entry_destroy(child_entry_t *entry)
+{
+ entry->ike_id->destroy(entry->ike_id);
+ entry->host_in->destroy(entry->host_in);
+ entry->host_out->destroy(entry->host_out);
+ free(entry);
+}
+
+/**
+ * Hashtable hash function for inbound SAs
+ */
+static u_int hash_in(child_entry_t *entry)
+{
+ return chunk_hash_inc(chunk_from_thing(entry->spi_in),
+ chunk_hash_inc(entry->host_in->get_address(entry->host_in),
+ chunk_hash(chunk_from_thing(entry->proto))));
+}
+
+/**
+ * Hashtable equals function for inbound SAs
+ */
+static bool equals_in(child_entry_t *a, child_entry_t *b)
+{
+ return a->spi_in == b->spi_in &&
+ a->proto == b->proto &&
+ a->host_in->ip_equals(a->host_in, b->host_in);
+}
+
+/**
+ * Hashtable hash function for outbound SAs
+ */
+static u_int hash_out(child_entry_t *entry)
+{
+ return chunk_hash_inc(chunk_from_thing(entry->spi_out),
+ chunk_hash_inc(entry->host_out->get_address(entry->host_out),
+ chunk_hash(chunk_from_thing(entry->proto))));
+}
+
+/**
+ * Hashtable equals function for outbound SAs
+ */
+static bool equals_out(child_entry_t *a, child_entry_t *b)
+{
+ return a->spi_out == b->spi_out &&
+ a->proto == b->proto &&
+ a->host_out->ip_equals(a->host_out, b->host_out);
+}
+
+/**
+ * Hashtable hash function for SAs by unique ID
+ */
+static u_int hash_id(child_entry_t *entry)
+{
+ return chunk_hash(chunk_from_thing(entry->unique_id));
+}
+
+/**
+ * Hashtable equals function for SAs by unique ID
+ */
+static bool equals_id(child_entry_t *a, child_entry_t *b)
+{
+ return a->unique_id == b->unique_id;
+}
+
+METHOD(child_sa_manager_t, add, void,
+ private_child_sa_manager_t *this, child_sa_t *child_sa, ike_sa_t *ike_sa)
+{
+ child_entry_t *entry;
+ host_t *in, *out;
+ ike_sa_id_t *id;
+
+ id = ike_sa->get_id(ike_sa);
+ in = ike_sa->get_my_host(ike_sa);
+ out = ike_sa->get_other_host(ike_sa);
+
+ INIT(entry,
+ .ike_id = id->clone(id),
+ .unique_id = child_sa->get_unique_id(child_sa),
+ .proto = child_sa->get_protocol(child_sa),
+ .spi_in = child_sa->get_spi(child_sa, TRUE),
+ .spi_out = child_sa->get_spi(child_sa, FALSE),
+ .host_in = in->clone(in),
+ .host_out = out->clone(out),
+ );
+
+ this->mutex->lock(this->mutex);
+ if (!this->in->get(this->in, entry) &&
+ !this->out->get(this->out, entry))
+ {
+ this->in->put(this->in, entry, entry);
+ this->out->put(this->out, entry, entry);
+ entry = this->ids->put(this->ids, entry, entry);
+ }
+ this->mutex->unlock(this->mutex);
+
+ if (entry)
+ {
+ child_entry_destroy(entry);
+ }
+}
+
+METHOD(child_sa_manager_t, remove_, void,
+ private_child_sa_manager_t *this, child_sa_t *child_sa)
+{
+ child_entry_t *entry, key = {
+ .unique_id = child_sa->get_unique_id(child_sa),
+ };
+
+ this->mutex->lock(this->mutex);
+ entry = this->ids->remove(this->ids, &key);
+ if (entry)
+ {
+ this->in->remove(this->in, entry);
+ this->out->remove(this->out, entry);
+ }
+ this->mutex->unlock(this->mutex);
+
+ if (entry)
+ {
+ child_entry_destroy(entry);
+ }
+}
+
+/**
+ * Check out an IKE_SA for a given CHILD_SA
+ */
+static ike_sa_t *checkout_ikesa(private_child_sa_manager_t *this,
+ ike_sa_id_t *id, u_int32_t unique_id, child_sa_t **child_sa)
+{
+ enumerator_t *enumerator;
+ child_sa_t *current;
+ ike_sa_t *ike_sa;
+ bool found = FALSE;
+
+ ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, id);
+ id->destroy(id);
+ if (ike_sa)
+ {
+ enumerator = ike_sa->create_child_sa_enumerator(ike_sa);
+ while (enumerator->enumerate(enumerator, &current))
+ {
+ found = current->get_unique_id(current) == unique_id;
+ if (found)
+ {
+ if (child_sa)
+ {
+ *child_sa = current;
+ }
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ if (found)
+ {
+ return ike_sa;
+ }
+ charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa);
+ }
+ return NULL;
+}
+
+METHOD(child_sa_manager_t, checkout_by_id, ike_sa_t*,
+ private_child_sa_manager_t *this, u_int32_t unique_id,
+ child_sa_t **child_sa)
+{
+ ike_sa_id_t *id;
+ child_entry_t *entry, key = {
+ .unique_id = unique_id,
+ };
+
+ this->mutex->lock(this->mutex);
+ entry = this->ids->get(this->ids, &key);
+ if (entry)
+ {
+ id = entry->ike_id->clone(entry->ike_id);
+ }
+ this->mutex->unlock(this->mutex);
+
+ if (entry)
+ {
+ return checkout_ikesa(this, id, unique_id, child_sa);
+ }
+ return NULL;
+}
+
+METHOD(child_sa_manager_t, checkout, ike_sa_t*,
+ private_child_sa_manager_t *this, protocol_id_t protocol, u_int32_t spi,
+ host_t *dst, child_sa_t **child_sa)
+{
+ ike_sa_id_t *id;
+ u_int32_t unique_id;
+ child_entry_t *entry, key = {
+ .spi_in = spi,
+ .spi_out = spi,
+ .host_in = dst,
+ .host_out = dst,
+ .proto = protocol,
+ };
+
+ this->mutex->lock(this->mutex);
+ entry = this->in->get(this->in, &key);
+ if (!entry)
+ {
+ entry = this->out->get(this->out, &key);
+ }
+ if (entry)
+ {
+ unique_id = entry->unique_id;
+ id = entry->ike_id->clone(entry->ike_id);
+ }
+ this->mutex->unlock(this->mutex);
+
+ if (entry)
+ {
+ return checkout_ikesa(this, id, unique_id, child_sa);
+ }
+ return NULL;
+}
+
+METHOD(child_sa_manager_t, destroy, void,
+ private_child_sa_manager_t *this)
+{
+ this->in->destroy(this->in);
+ this->out->destroy(this->out);
+ this->ids->destroy(this->ids);
+ this->mutex->destroy(this->mutex);
+ free(this);
+}
+
+/**
+ * See header
+ */
+child_sa_manager_t *child_sa_manager_create()
+{
+ private_child_sa_manager_t *this;
+
+ INIT(this,
+ .public = {
+ .add = _add,
+ .remove = _remove_,
+ .checkout = _checkout,
+ .checkout_by_id = _checkout_by_id,
+ .destroy = _destroy,
+ },
+ .in = hashtable_create((hashtable_hash_t)hash_in,
+ (hashtable_equals_t)equals_in, 8),
+ .out = hashtable_create((hashtable_hash_t)hash_out,
+ (hashtable_equals_t)equals_out, 8),
+ .ids = hashtable_create((hashtable_hash_t)hash_id,
+ (hashtable_equals_t)equals_id, 8),
+ .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
+ );
+
+ return &this->public;
+}
diff --git a/src/libcharon/sa/child_sa_manager.h b/src/libcharon/sa/child_sa_manager.h
new file mode 100644
index 000000000..4d57528e8
--- /dev/null
+++ b/src/libcharon/sa/child_sa_manager.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2014 Martin Willi
+ * Copyright (C) 2014 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup child_sa_manager child_sa_manager
+ * @{ @ingroup sa
+ */
+
+#ifndef CHILD_SA_MANAGER_H_
+#define CHILD_SA_MANAGER_H_
+
+#include <sa/ike_sa.h>
+#include <sa/child_sa.h>
+
+typedef struct child_sa_manager_t child_sa_manager_t;
+
+/**
+ * Handle CHILD_SA to IKE_SA relations
+ */
+struct child_sa_manager_t {
+
+ /**
+ * Register a CHILD_SA/IKE_SA relation.
+ *
+ * @param child_sa CHILD_SA to register
+ * @param ike_sa IKE_SA owning the CHILD_SA
+ */
+ void (*add)(child_sa_manager_t *this, child_sa_t *child_sa, ike_sa_t *ike_sa);
+
+ /**
+ * Unregister a CHILD_SA/IKE_SA relation.
+ *
+ * @param child_sa CHILD_SA to unregister
+ */
+ void (*remove)(child_sa_manager_t *this, child_sa_t *child_sa);
+
+ /**
+ * Find a CHILD_SA and check out the associated IKE_SA by SPI.
+ *
+ * On success, the returned IKE_SA must be checked in after use to
+ * the IKE_SA manager.
+ *
+ * @param protocol IPsec protocol, AH|ESP
+ * @param spi SPI of CHILD_SA to check out
+ * @param dst SA destination host related to SPI
+ * @param child_sa returns CHILD_SA managed by IKE_SA
+ * @return IKE_SA, NULL if not found
+ */
+ ike_sa_t *(*checkout)(child_sa_manager_t *this,
+ protocol_id_t protocol, u_int32_t spi, host_t *dst,
+ child_sa_t **child_sa);
+
+ /**
+ * Find a CHILD_SA and check out the associated IKE_SA by unique_id.
+ *
+ * On success, the returned IKE_SA must be checked in after use to
+ * the IKE_SA manager.
+ *
+ * @param unique_id unique ID of CHILD_SA to check out
+ * @param child_sa returns CHILD_SA managed by IKE_SA
+ * @return IKE_SA, NULL if not found
+ */
+ ike_sa_t *(*checkout_by_id)(child_sa_manager_t *this, u_int32_t unique_id,
+ child_sa_t **child_sa);
+
+ /**
+ * Destroy a child_sa_manager_t.
+ */
+ void (*destroy)(child_sa_manager_t *this);
+};
+
+/**
+ * Create a child_sa_manager instance.
+ */
+child_sa_manager_t *child_sa_manager_create();
+
+#endif /** CHILD_SA_MANAGER_H_ @}*/
diff --git a/src/libcharon/sa/eap/eap_method.h b/src/libcharon/sa/eap/eap_method.h
index 6242a5a6e..689c0f990 100644
--- a/src/libcharon/sa/eap/eap_method.h
+++ b/src/libcharon/sa/eap/eap_method.h
@@ -137,6 +137,18 @@ struct eap_method_t {
void (*set_identifier) (eap_method_t *this, u_int8_t identifier);
/**
+ * Get authentication details performed by this EAP method.
+ *
+ * After EAP completion, the auth data contains additional information
+ * of the authentication process, used certificates etc.
+ * This method is optional to implement, but if it is, it must return
+ * a valid auth_cfg.
+ *
+ * @return auth method, internal data
+ */
+ auth_cfg_t* (*get_auth)(eap_method_t *this);
+
+ /**
* Destroys a eap_method_t object.
*/
void (*destroy) (eap_method_t *this);
diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c
index d92b9df8e..3aafa4c13 100644
--- a/src/libcharon/sa/ike_sa.c
+++ b/src/libcharon/sa/ike_sa.c
@@ -932,6 +932,7 @@ METHOD(ike_sa_t, update_hosts, void,
/* update our address in any case */
if (force && !me->equals(me, this->my_host))
{
+ charon->bus->ike_update(charon->bus, &this->public, TRUE, me);
set_my_host(this, me->clone(me));
update = TRUE;
}
@@ -945,6 +946,7 @@ METHOD(ike_sa_t, update_hosts, void,
(!has_condition(this, COND_NAT_HERE) ||
!has_condition(this, COND_ORIGINAL_INITIATOR)))
{
+ charon->bus->ike_update(charon->bus, &this->public, FALSE, other);
set_other_host(this, other->clone(other));
update = TRUE;
}
@@ -964,6 +966,10 @@ METHOD(ike_sa_t, update_hosts, void,
enumerator = array_create_enumerator(this->child_sas);
while (enumerator->enumerate(enumerator, &child_sa))
{
+ charon->child_sa_manager->remove(charon->child_sa_manager, child_sa);
+ charon->child_sa_manager->add(charon->child_sa_manager,
+ child_sa, &this->public);
+
if (child_sa->update(child_sa, this->my_host, this->other_host,
vips, has_condition(this, COND_NAT_ANY)) == NOT_SUPPORTED)
{
@@ -971,6 +977,7 @@ METHOD(ike_sa_t, update_hosts, void,
child_sa->get_protocol(child_sa),
child_sa->get_spi(child_sa, TRUE));
}
+
}
enumerator->destroy(enumerator);
@@ -1444,6 +1451,8 @@ METHOD(ike_sa_t, add_child_sa, void,
private_ike_sa_t *this, child_sa_t *child_sa)
{
array_insert_create(&this->child_sas, ARRAY_TAIL, child_sa);
+ charon->child_sa_manager->add(charon->child_sa_manager,
+ child_sa, &this->public);
}
METHOD(ike_sa_t, get_child_sa, child_sa_t*,
@@ -1471,16 +1480,58 @@ METHOD(ike_sa_t, get_child_count, int,
return array_count(this->child_sas);
}
+/**
+ * Private data of a create_child_sa_enumerator()
+ */
+typedef struct {
+ /** implements enumerator */
+ enumerator_t public;
+ /** inner array enumerator */
+ enumerator_t *inner;
+ /** current item */
+ child_sa_t *current;
+} child_enumerator_t;
+
+METHOD(enumerator_t, child_enumerate, bool,
+ child_enumerator_t *this, child_sa_t **child_sa)
+{
+ if (this->inner->enumerate(this->inner, &this->current))
+ {
+ *child_sa = this->current;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+METHOD(enumerator_t, child_enumerator_destroy, void,
+ child_enumerator_t *this)
+{
+ this->inner->destroy(this->inner);
+ free(this);
+}
+
METHOD(ike_sa_t, create_child_sa_enumerator, enumerator_t*,
private_ike_sa_t *this)
{
- return array_create_enumerator(this->child_sas);
+ child_enumerator_t *enumerator;
+
+ INIT(enumerator,
+ .public = {
+ .enumerate = (void*)_child_enumerate,
+ .destroy = _child_enumerator_destroy,
+ },
+ .inner = array_create_enumerator(this->child_sas),
+ );
+ return &enumerator->public;
}
METHOD(ike_sa_t, remove_child_sa, void,
private_ike_sa_t *this, enumerator_t *enumerator)
{
- array_remove_at(this->child_sas, enumerator);
+ child_enumerator_t *ce = (child_enumerator_t*)enumerator;
+
+ charon->child_sa_manager->remove(charon->child_sa_manager, ce->current);
+ array_remove_at(this->child_sas, ce->inner);
}
METHOD(ike_sa_t, rekey_child_sa, status_t,
@@ -1513,13 +1564,13 @@ METHOD(ike_sa_t, destroy_child_sa, status_t,
child_sa_t *child_sa;
status_t status = NOT_FOUND;
- enumerator = array_create_enumerator(this->child_sas);
+ enumerator = create_child_sa_enumerator(this);
while (enumerator->enumerate(enumerator, (void**)&child_sa))
{
if (child_sa->get_protocol(child_sa) == protocol &&
child_sa->get_spi(child_sa, TRUE) == spi)
{
- array_remove_at(this->child_sas, enumerator);
+ remove_child_sa(this, enumerator);
child_sa->destroy(child_sa);
status = SUCCESS;
break;
@@ -1771,7 +1822,7 @@ METHOD(ike_sa_t, reestablish, status_t,
#endif /* ME */
{
/* handle existing CHILD_SAs */
- enumerator = array_create_enumerator(this->child_sas);
+ enumerator = create_child_sa_enumerator(this);
while (enumerator->enumerate(enumerator, (void**)&child_sa))
{
if (has_condition(this, COND_REAUTHENTICATING))
@@ -1780,7 +1831,7 @@ METHOD(ike_sa_t, reestablish, status_t,
{
case CHILD_ROUTED:
{ /* move routed child directly */
- array_remove_at(this->child_sas, enumerator);
+ remove_child_sa(this, enumerator);
new->add_child_sa(new, child_sa);
action = ACTION_NONE;
break;
@@ -2209,6 +2260,12 @@ METHOD(ike_sa_t, inherit_post, void,
array_insert_create(&this->other_vips, ARRAY_TAIL, vip);
}
+ /* MOBIKE additional addresses */
+ while (array_remove(other->peer_addresses, ARRAY_HEAD, &vip))
+ {
+ array_insert_create(&this->peer_addresses, ARRAY_TAIL, vip);
+ }
+
/* authentication information */
enumerator = array_create_enumerator(other->my_auths);
while (enumerator->enumerate(enumerator, &cfg))
@@ -2251,7 +2308,8 @@ METHOD(ike_sa_t, inherit_post, void,
/* adopt all children */
while (array_remove(other->child_sas, ARRAY_HEAD, &child_sa))
{
- array_insert_create(&this->child_sas, ARRAY_TAIL, child_sa);
+ charon->child_sa_manager->remove(charon->child_sa_manager, child_sa);
+ add_child_sa(this, child_sa);
}
/* move pending tasks to the new IKE_SA */
@@ -2296,8 +2354,8 @@ METHOD(ike_sa_t, destroy, void,
{
if (entry.handler)
{
- hydra->attributes->release(hydra->attributes, entry.handler,
- this->other_id, entry.type, entry.data);
+ charon->attributes->release(charon->attributes, entry.handler,
+ &this->public, entry.type, entry.data);
}
free(entry.data.ptr);
}
@@ -2305,6 +2363,7 @@ METHOD(ike_sa_t, destroy, void,
* routes that the CHILD_SA tries to uninstall. */
while (array_remove(this->child_sas, ARRAY_TAIL, &child_sa))
{
+ charon->child_sa_manager->remove(charon->child_sa_manager, child_sa);
child_sa->destroy(child_sa);
}
while (array_remove(this->my_vips, ARRAY_TAIL, &vip))
@@ -2321,12 +2380,11 @@ METHOD(ike_sa_t, destroy, void,
if (this->peer_cfg)
{
linked_list_t *pools;
- identification_t *id;
- id = get_other_eap_id(this);
pools = linked_list_create_from_enumerator(
this->peer_cfg->create_pool_enumerator(this->peer_cfg));
- hydra->attributes->release_address(hydra->attributes, pools, vip, id);
+ charon->attributes->release_address(charon->attributes,
+ pools, vip, &this->public);
pools->destroy(pools);
}
vip->destroy(vip);
diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h
index c72d87367..9dbc805c9 100644
--- a/src/libcharon/sa/ike_sa.h
+++ b/src/libcharon/sa/ike_sa.h
@@ -131,6 +131,11 @@ enum ike_extension_t {
* peer supports proprietary IKEv1 or standardized IKEv2 fragmentation
*/
EXT_IKE_FRAGMENTATION = (1<<11),
+
+ /**
+ * Signature Authentication, RFC 7427
+ */
+ EXT_SIGNATURE_AUTH = (1<<12),
};
/**
@@ -936,8 +941,9 @@ struct ike_sa_t {
/**
* Reauthenticate the IKE_SA.
*
- * Create a completely new IKE_SA with authentication, recreates all children
- * within the IKE_SA, closes this IKE_SA.
+ * Triggers a new IKE_SA that replaces this one. IKEv1 implicitly inherits
+ * all Quick Modes, while IKEv2 recreates all active and queued CHILD_SAs
+ * in the new IKE_SA.
*
* @return DESTROY_ME to destroy the IKE_SA
*/
diff --git a/src/libcharon/sa/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c
index bdabc59b5..13fc74ff7 100644
--- a/src/libcharon/sa/ike_sa_manager.c
+++ b/src/libcharon/sa/ike_sa_manager.c
@@ -1184,7 +1184,8 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*,
DBG2(DBG_MGR, "checkout IKE_SA by message");
- if (id->get_responder_spi(id) == 0)
+ if (id->get_responder_spi(id) == 0 &&
+ message->get_message_id(message) == 0)
{
if (message->get_major_version(message) == IKEV2_MAJOR_VERSION)
{
@@ -1383,54 +1384,35 @@ METHOD(ike_sa_manager_t, checkout_by_config, ike_sa_t*,
}
METHOD(ike_sa_manager_t, checkout_by_id, ike_sa_t*,
- private_ike_sa_manager_t *this, u_int32_t id, bool child)
+ private_ike_sa_manager_t *this, u_int32_t id)
{
- enumerator_t *enumerator, *children;
+ enumerator_t *enumerator;
entry_t *entry;
ike_sa_t *ike_sa = NULL;
- child_sa_t *child_sa;
u_int segment;
- DBG2(DBG_MGR, "checkout IKE_SA by ID");
+ DBG2(DBG_MGR, "checkout IKE_SA by ID %u", id);
enumerator = create_table_enumerator(this);
while (enumerator->enumerate(enumerator, &entry, &segment))
{
if (wait_for_entry(this, entry, segment))
{
- /* look for a child with such a reqid ... */
- if (child)
- {
- children = entry->ike_sa->create_child_sa_enumerator(entry->ike_sa);
- while (children->enumerate(children, (void**)&child_sa))
- {
- if (child_sa->get_reqid(child_sa) == id)
- {
- ike_sa = entry->ike_sa;
- break;
- }
- }
- children->destroy(children);
- }
- else /* ... or for a IKE_SA with such a unique id */
- {
- if (entry->ike_sa->get_unique_id(entry->ike_sa) == id)
- {
- ike_sa = entry->ike_sa;
- }
- }
- /* got one, return */
- if (ike_sa)
+ if (entry->ike_sa->get_unique_id(entry->ike_sa) == id)
{
+ ike_sa = entry->ike_sa;
entry->checked_out = TRUE;
- DBG2(DBG_MGR, "IKE_SA %s[%u] successfully checked out",
- ike_sa->get_name(ike_sa), ike_sa->get_unique_id(ike_sa));
break;
}
}
}
enumerator->destroy(enumerator);
+ if (ike_sa)
+ {
+ DBG2(DBG_MGR, "IKE_SA %s[%u] successfully checked out",
+ ike_sa->get_name(ike_sa), ike_sa->get_unique_id(ike_sa));
+ }
charon->bus->set_sa(charon->bus, ike_sa);
return ike_sa;
}
@@ -1746,29 +1728,45 @@ METHOD(ike_sa_manager_t, create_id_enumerator, enumerator_t*,
}
/**
- * Move all CHILD_SAs from old to new
+ * Move all CHILD_SAs and virtual IPs from old to new
*/
-static void adopt_children(ike_sa_t *old, ike_sa_t *new)
+static void adopt_children_and_vips(ike_sa_t *old, ike_sa_t *new)
{
enumerator_t *enumerator;
child_sa_t *child_sa;
+ host_t *vip;
+ int chcount = 0, vipcount = 0;
+
enumerator = old->create_child_sa_enumerator(old);
while (enumerator->enumerate(enumerator, &child_sa))
{
old->remove_child_sa(old, enumerator);
new->add_child_sa(new, child_sa);
+ chcount++;
}
enumerator->destroy(enumerator);
-}
-/**
- * Check if the replaced IKE_SA might get reauthenticated from host
- */
-static bool is_ikev1_reauth(ike_sa_t *duplicate, host_t *host)
-{
- return duplicate->get_version(duplicate) == IKEV1 &&
- host->equals(host, duplicate->get_other_host(duplicate));
+ enumerator = old->create_virtual_ip_enumerator(old, FALSE);
+ while (enumerator->enumerate(enumerator, &vip))
+ {
+ new->add_virtual_ip(new, FALSE, vip);
+ vipcount++;
+ }
+ enumerator->destroy(enumerator);
+ /* this does not release the addresses, which is good, but it does trigger
+ * an assign_vips(FALSE) event... */
+ old->clear_virtual_ips(old, FALSE);
+ /* ...trigger the analogous event on the new SA */
+ charon->bus->set_sa(charon->bus, new);
+ charon->bus->assign_vips(charon->bus, new, TRUE);
+ charon->bus->set_sa(charon->bus, old);
+
+ if (chcount || vipcount)
+ {
+ DBG1(DBG_IKE, "detected reauth of existing IKE_SA, adopting %d "
+ "children and %d virtual IPs", chcount, vipcount);
+ }
}
/**
@@ -1780,13 +1778,20 @@ static status_t enforce_replace(private_ike_sa_manager_t *this,
{
charon->bus->alert(charon->bus, ALERT_UNIQUE_REPLACE);
- if (is_ikev1_reauth(duplicate, host))
+ if (host->equals(host, duplicate->get_other_host(duplicate)))
{
/* looks like a reauthentication attempt */
- adopt_children(duplicate, new);
+ if (!new->has_condition(new, COND_INIT_CONTACT_SEEN) &&
+ new->get_version(new) == IKEV1)
+ {
+ /* IKEv1 implicitly takes over children, IKEv2 recreates them
+ * explicitly. */
+ adopt_children_and_vips(duplicate, new);
+ }
/* For IKEv1 we have to delay the delete for the old IKE_SA. Some
* peers need to complete the new SA first, otherwise the quick modes
- * might get lost. */
+ * might get lost. For IKEv2 we do the same, as we want overlapping
+ * CHILD_SAs to keep connectivity up. */
lib->scheduler->schedule_job(lib->scheduler, (job_t*)
delete_ike_sa_job_create(duplicate->get_id(duplicate), TRUE), 10);
return SUCCESS;
@@ -1851,7 +1856,9 @@ METHOD(ike_sa_manager_t, check_uniqueness, bool,
other, other_host);
break;
case UNIQUE_KEEP:
- if (!is_ikev1_reauth(duplicate, other_host))
+ /* potential reauthentication? */
+ if (!other_host->equals(other_host,
+ duplicate->get_other_host(duplicate)))
{
cancel = TRUE;
/* we keep the first IKE_SA and delete all
diff --git a/src/libcharon/sa/ike_sa_manager.h b/src/libcharon/sa/ike_sa_manager.h
index a68ae7763..f259d8e56 100644
--- a/src/libcharon/sa/ike_sa_manager.h
+++ b/src/libcharon/sa/ike_sa_manager.h
@@ -129,19 +129,15 @@ struct ike_sa_manager_t {
/**
* Check out an IKE_SA a unique ID.
*
- * Every IKE_SA and every CHILD_SA is uniquely identified by an ID.
- * These checkout function uses, depending
- * on the child parameter, the unique ID of the IKE_SA or the reqid
- * of one of a IKE_SAs CHILD_SA.
+ * Every IKE_SA is uniquely identified by a numerical ID. This checkout
+ * function uses the unique ID of the IKE_SA to check it out.
*
* @param id unique ID of the object
- * @param child TRUE to use CHILD, FALSE to use IKE_SA
* @return
* - checked out IKE_SA, if found
* - NULL, if not found
*/
- ike_sa_t* (*checkout_by_id) (ike_sa_manager_t* this, u_int32_t id,
- bool child);
+ ike_sa_t* (*checkout_by_id) (ike_sa_manager_t* this, u_int32_t id);
/**
* Check out an IKE_SA by the policy/connection name.
diff --git a/src/libcharon/sa/ikev1/authenticators/psk_v1_authenticator.c b/src/libcharon/sa/ikev1/authenticators/psk_v1_authenticator.c
index aa966cd5f..bb187f07c 100644
--- a/src/libcharon/sa/ikev1/authenticators/psk_v1_authenticator.c
+++ b/src/libcharon/sa/ikev1/authenticators/psk_v1_authenticator.c
@@ -74,7 +74,10 @@ METHOD(authenticator_t, build, status_t,
keymat_v1_t *keymat;
chunk_t hash, dh;
- this->dh->get_my_public_value(this->dh, &dh);
+ if (!this->dh->get_my_public_value(this->dh, &dh))
+ {
+ return FAILED;
+ }
keymat = (keymat_v1_t*)this->ike_sa->get_keymat(this->ike_sa);
if (!keymat->get_hash(keymat, this->initiator, dh, this->dh_value,
this->ike_sa->get_id(this->ike_sa), this->sa_payload,
@@ -108,7 +111,10 @@ METHOD(authenticator_t, process, status_t,
return FAILED;
}
- this->dh->get_my_public_value(this->dh, &dh);
+ if (!this->dh->get_my_public_value(this->dh, &dh))
+ {
+ return FAILED;
+ }
keymat = (keymat_v1_t*)this->ike_sa->get_keymat(this->ike_sa);
if (!keymat->get_hash(keymat, !this->initiator, this->dh_value, dh,
this->ike_sa->get_id(this->ike_sa), this->sa_payload,
diff --git a/src/libcharon/sa/ikev1/authenticators/pubkey_v1_authenticator.c b/src/libcharon/sa/ikev1/authenticators/pubkey_v1_authenticator.c
index bfe5ff449..52228ef2e 100644
--- a/src/libcharon/sa/ikev1/authenticators/pubkey_v1_authenticator.c
+++ b/src/libcharon/sa/ikev1/authenticators/pubkey_v1_authenticator.c
@@ -94,7 +94,11 @@ METHOD(authenticator_t, build, status_t,
return NOT_FOUND;
}
- this->dh->get_my_public_value(this->dh, &dh);
+ if (!this->dh->get_my_public_value(this->dh, &dh))
+ {
+ private->destroy(private);
+ return FAILED;
+ }
keymat = (keymat_v1_t*)this->ike_sa->get_keymat(this->ike_sa);
if (!keymat->get_hash(keymat, this->initiator, dh, this->dh_value,
this->ike_sa->get_id(this->ike_sa), this->sa_payload,
@@ -152,7 +156,10 @@ METHOD(authenticator_t, process, status_t,
}
id = this->ike_sa->get_other_id(this->ike_sa);
- this->dh->get_my_public_value(this->dh, &dh);
+ if (!this->dh->get_my_public_value(this->dh, &dh))
+ {
+ return FAILED;
+ }
keymat = (keymat_v1_t*)this->ike_sa->get_keymat(this->ike_sa);
if (!keymat->get_hash(keymat, !this->initiator, this->dh_value, dh,
this->ike_sa->get_id(this->ike_sa), this->sa_payload,
diff --git a/src/libcharon/sa/ikev1/keymat_v1.c b/src/libcharon/sa/ikev1/keymat_v1.c
index 619d197bd..f5a91dbeb 100644
--- a/src/libcharon/sa/ikev1/keymat_v1.c
+++ b/src/libcharon/sa/ikev1/keymat_v1.c
@@ -425,7 +425,7 @@ METHOD(keymat_v1_t, derive_ike_keys, bool,
return FALSE;
}
- if (dh->get_shared_secret(dh, &g_xy) != SUCCESS)
+ if (!dh->get_shared_secret(dh, &g_xy))
{
return FALSE;
}
@@ -560,7 +560,10 @@ METHOD(keymat_v1_t, derive_ike_keys, bool,
return FALSE;
}
- dh->get_my_public_value(dh, &dh_me);
+ if (!dh->get_my_public_value(dh, &dh_me))
+ {
+ return FALSE;
+ }
g_xi = this->initiator ? dh_me : dh_other;
g_xr = this->initiator ? dh_other : dh_me;
@@ -661,7 +664,7 @@ METHOD(keymat_v1_t, derive_child_keys, bool,
protocol = proposal->get_protocol(proposal);
if (dh)
{
- if (dh->get_shared_secret(dh, &secret) != SUCCESS)
+ if (!dh->get_shared_secret(dh, &secret))
{
return FALSE;
}
diff --git a/src/libcharon/sa/ikev1/phase1.c b/src/libcharon/sa/ikev1/phase1.c
index d01a831f8..c968b2a9c 100644
--- a/src/libcharon/sa/ikev1/phase1.c
+++ b/src/libcharon/sa/ikev1/phase1.c
@@ -694,7 +694,13 @@ METHOD(phase1_t, add_nonce_ke, bool,
nonce_gen_t *nonceg;
chunk_t nonce;
- ke_payload = ke_payload_create_from_diffie_hellman(PLV1_KEY_EXCHANGE, this->dh);
+ ke_payload = ke_payload_create_from_diffie_hellman(PLV1_KEY_EXCHANGE,
+ this->dh);
+ if (!ke_payload)
+ {
+ DBG1(DBG_IKE, "creating KE payload failed");
+ return FALSE;
+ }
message->add_payload(message, &ke_payload->payload_interface);
nonceg = this->keymat->keymat.create_nonce_gen(&this->keymat->keymat);
@@ -739,7 +745,11 @@ METHOD(phase1_t, get_nonce_ke, bool,
return FALSE;
}
this->dh_value = chunk_clone(ke_payload->get_key_exchange_data(ke_payload));
- this->dh->set_other_public_value(this->dh, this->dh_value);
+ if (!this->dh->set_other_public_value(this->dh, this->dh_value))
+ {
+ DBG1(DBG_IKE, "unable to apply received KE value");
+ return FALSE;
+ }
nonce_payload = (nonce_payload_t*)message->get_payload(message, PLV1_NONCE);
if (!nonce_payload)
diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c
index 0f8e8bc6d..cb22bf606 100644
--- a/src/libcharon/sa/ikev1/task_manager_v1.c
+++ b/src/libcharon/sa/ikev1/task_manager_v1.c
@@ -1596,7 +1596,8 @@ static bool is_redundant(private_task_manager_t *this, child_sa_t *child_sa)
child_sa->get_lifetime(child_sa, FALSE))
{
DBG1(DBG_IKE, "deleting redundant CHILD_SA %s{%d}",
- child_sa->get_name(child_sa), child_sa->get_reqid(child_sa));
+ child_sa->get_name(child_sa),
+ child_sa->get_unique_id(child_sa));
redundant = TRUE;
break;
}
@@ -1647,6 +1648,8 @@ METHOD(task_manager_t, queue_child_rekey, void,
task = quick_mode_create(this->ike_sa, cfg->get_ref(cfg),
get_first_ts(child_sa, TRUE), get_first_ts(child_sa, FALSE));
task->use_reqid(task, child_sa->get_reqid(child_sa));
+ task->use_marks(task, child_sa->get_mark(child_sa, TRUE).value,
+ child_sa->get_mark(child_sa, FALSE).value);
task->rekey(task, child_sa->get_spi(child_sa, TRUE));
queue_task(this, &task->task);
diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_delete.c b/src/libcharon/sa/ikev1/tasks/isakmp_delete.c
index bea0428c4..a56805afb 100644
--- a/src/libcharon/sa/ikev1/tasks/isakmp_delete.c
+++ b/src/libcharon/sa/ikev1/tasks/isakmp_delete.c
@@ -1,4 +1,7 @@
/*
+ * Copyright (C) 2015 Tobias Brunner
+ * Hochschule fuer Technik Rapperswil
+ *
* Copyright (C) 2011 Martin Willi
* Copyright (C) 2011 revosec AG
*
@@ -74,6 +77,42 @@ METHOD(task_t, process_i, status_t,
METHOD(task_t, process_r, status_t,
private_isakmp_delete_t *this, message_t *message)
{
+ enumerator_t *payloads;
+ payload_t *payload;
+ delete_payload_t *delete_payload;
+ ike_sa_id_t *id;
+ u_int64_t spi_i, spi_r;
+ bool found = FALSE;
+
+ /* some peers send DELETE payloads for other IKE_SAs, e.g. those for expired
+ * ones after a rekeyeing, make sure the SPIs match */
+ id = this->ike_sa->get_id(this->ike_sa);
+ payloads = message->create_payload_enumerator(message);
+ while (payloads->enumerate(payloads, &payload))
+ {
+ if (payload->get_type(payload) == PLV1_DELETE)
+ {
+ delete_payload = (delete_payload_t*)payload;
+ if (!delete_payload->get_ike_spi(delete_payload, &spi_i, &spi_r))
+ {
+ continue;
+ }
+ if (id->get_initiator_spi(id) == spi_i &&
+ id->get_responder_spi(id) == spi_r)
+ {
+ found = TRUE;
+ break;
+ }
+ }
+ }
+ payloads->destroy(payloads);
+
+ if (!found)
+ {
+ DBG1(DBG_IKE, "received DELETE for different IKE_SA, ignored");
+ return SUCCESS;
+ }
+
DBG1(DBG_IKE, "received DELETE for IKE_SA %s[%d]",
this->ike_sa->get_name(this->ike_sa),
this->ike_sa->get_unique_id(this->ike_sa));
diff --git a/src/libcharon/sa/ikev1/tasks/main_mode.c b/src/libcharon/sa/ikev1/tasks/main_mode.c
index 2fb4c6935..3ea4a2a85 100644
--- a/src/libcharon/sa/ikev1/tasks/main_mode.c
+++ b/src/libcharon/sa/ikev1/tasks/main_mode.c
@@ -205,6 +205,43 @@ static status_t send_delete(private_main_mode_t *this)
return ALREADY_DONE;
}
+/**
+ * Add an INITIAL_CONTACT notify if first contact with peer
+ */
+static void add_initial_contact(private_main_mode_t *this, message_t *message,
+ identification_t *idi)
+{
+ identification_t *idr;
+ host_t *host;
+ notify_payload_t *notify;
+ ike_sa_id_t *ike_sa_id;
+ u_int64_t spi_i, spi_r;
+ chunk_t spi;
+
+ idr = this->ph1->get_id(this->ph1, this->peer_cfg, FALSE);
+ if (idr && !idr->contains_wildcards(idr))
+ {
+ if (this->peer_cfg->get_unique_policy(this->peer_cfg) != UNIQUE_NO &&
+ this->peer_cfg->get_unique_policy(this->peer_cfg) != UNIQUE_NEVER)
+ {
+ host = this->ike_sa->get_other_host(this->ike_sa);
+ if (!charon->ike_sa_manager->has_contact(charon->ike_sa_manager,
+ idi, idr, host->get_family(host)))
+ {
+ notify = notify_payload_create_from_protocol_and_type(
+ PLV1_NOTIFY, PROTO_IKE, INITIAL_CONTACT_IKEV1);
+ ike_sa_id = this->ike_sa->get_id(this->ike_sa);
+ spi_i = ike_sa_id->get_initiator_spi(ike_sa_id);
+ spi_r = ike_sa_id->get_responder_spi(ike_sa_id);
+ spi = chunk_cata("cc", chunk_from_thing(spi_i),
+ chunk_from_thing(spi_r));
+ notify->set_spi_data(notify, spi);
+ message->add_payload(message, (payload_t*)notify);
+ }
+ }
+ }
+}
+
METHOD(task_t, build_i, status_t,
private_main_mode_t *this, message_t *message)
{
@@ -311,6 +348,8 @@ METHOD(task_t, build_i, status_t,
return send_notify(this, AUTHENTICATION_FAILED);
}
+ add_initial_contact(this, message, id);
+
this->state = MM_AUTH;
return NEED_MORE;
}
diff --git a/src/libcharon/sa/ikev1/tasks/mode_config.c b/src/libcharon/sa/ikev1/tasks/mode_config.c
index 94026b9af..d0994a961 100644
--- a/src/libcharon/sa/ikev1/tasks/mode_config.c
+++ b/src/libcharon/sa/ikev1/tasks/mode_config.c
@@ -16,7 +16,6 @@
#include "mode_config.h"
#include <daemon.h>
-#include <hydra.h>
#include <encoding/payloads/cp_payload.h>
typedef struct private_mode_config_t private_mode_config_t;
@@ -136,9 +135,8 @@ static void handle_attribute(private_mode_config_t *this,
enumerator->destroy(enumerator);
/* and pass it to the handle function */
- handler = hydra->attributes->handle(hydra->attributes,
- this->ike_sa->get_other_id(this->ike_sa), handler,
- ca->get_type(ca), ca->get_chunk(ca));
+ handler = charon->attributes->handle(charon->attributes,
+ this->ike_sa, handler, ca->get_type(ca), ca->get_chunk(ca));
this->ike_sa->add_configuration_attribute(this->ike_sa,
handler, ca->get_type(ca), ca->get_chunk(ca));
}
@@ -326,9 +324,8 @@ static status_t build_request(private_mode_config_t *this, message_t *message)
enumerator->destroy(enumerator);
}
- enumerator = hydra->attributes->create_initiator_enumerator(
- hydra->attributes,
- this->ike_sa->get_other_id(this->ike_sa), vips);
+ enumerator = charon->attributes->create_initiator_enumerator(
+ charon->attributes, this->ike_sa, vips);
while (enumerator->enumerate(enumerator, &handler, &type, &data))
{
add_attribute(this, cp, type, data, handler);
@@ -353,7 +350,7 @@ static status_t build_set(private_mode_config_t *this, message_t *message)
cp_payload_t *cp;
peer_cfg_t *config;
identification_t *id;
- linked_list_t *pools;
+ linked_list_t *pools, *migrated, *vips;
host_t *any4, *any6, *found;
char *name;
@@ -361,45 +358,62 @@ static status_t build_set(private_mode_config_t *this, message_t *message)
id = this->ike_sa->get_other_eap_id(this->ike_sa);
config = this->ike_sa->get_peer_cfg(this->ike_sa);
- any4 = host_create_any(AF_INET);
- any6 = host_create_any(AF_INET6);
+ /* if we migrated virtual IPs during reauthentication, reassign them */
+ migrated = linked_list_create_from_enumerator(
+ this->ike_sa->create_virtual_ip_enumerator(this->ike_sa,
+ FALSE));
+ vips = migrated->clone_offset(migrated, offsetof(host_t, clone));
+ migrated->destroy(migrated);
this->ike_sa->clear_virtual_ips(this->ike_sa, FALSE);
/* in push mode, we ask each configured pool for an address */
- enumerator = config->create_pool_enumerator(config);
- while (enumerator->enumerate(enumerator, &name))
+ if (!vips->get_count(vips))
{
- pools = linked_list_create_with_items(name, NULL);
- /* try IPv4, then IPv6 */
- found = hydra->attributes->acquire_address(hydra->attributes,
- pools, id, any4);
- if (!found)
- {
- found = hydra->attributes->acquire_address(hydra->attributes,
- pools, id, any6);
- }
- pools->destroy(pools);
- if (found)
+ any4 = host_create_any(AF_INET);
+ any6 = host_create_any(AF_INET6);
+ enumerator = config->create_pool_enumerator(config);
+ while (enumerator->enumerate(enumerator, &name))
{
- DBG1(DBG_IKE, "assigning virtual IP %H to peer '%Y'", found, id);
- this->ike_sa->add_virtual_ip(this->ike_sa, FALSE, found);
- cp->add_attribute(cp, build_vip(found));
- this->vips->insert_last(this->vips, found);
+ pools = linked_list_create_with_items(name, NULL);
+ /* try IPv4, then IPv6 */
+ found = charon->attributes->acquire_address(charon->attributes,
+ pools, this->ike_sa, any4);
+ if (!found)
+ {
+ found = charon->attributes->acquire_address(charon->attributes,
+ pools, this->ike_sa, any6);
+ }
+ pools->destroy(pools);
+ if (found)
+ {
+ vips->insert_last(vips, found);
+ }
}
+ enumerator->destroy(enumerator);
+ any4->destroy(any4);
+ any6->destroy(any6);
}
- enumerator->destroy(enumerator);
- any4->destroy(any4);
- any6->destroy(any6);
+ enumerator = vips->create_enumerator(vips);
+ while (enumerator->enumerate(enumerator, &found))
+ {
+ DBG1(DBG_IKE, "assigning virtual IP %H to peer '%Y'", found, id);
+ this->ike_sa->add_virtual_ip(this->ike_sa, FALSE, found);
+ cp->add_attribute(cp, build_vip(found));
+ this->vips->insert_last(this->vips, found);
+ vips->remove_at(vips, enumerator);
+ }
+ enumerator->destroy(enumerator);
+ vips->destroy(vips);
charon->bus->assign_vips(charon->bus, this->ike_sa, TRUE);
/* query registered providers for additional attributes to include */
pools = linked_list_create_from_enumerator(
config->create_pool_enumerator(config));
- enumerator = hydra->attributes->create_responder_enumerator(
- hydra->attributes, pools, id, this->vips);
+ enumerator = charon->attributes->create_responder_enumerator(
+ charon->attributes, pools, this->ike_sa, this->vips);
while (enumerator->enumerate(enumerator, &type, &value))
{
add_attribute(this, cp, type, value, NULL);
@@ -458,6 +472,28 @@ METHOD(task_t, process_r, status_t,
}
/**
+ * Assign a migrated virtual IP
+ */
+static host_t *assign_migrated_vip(linked_list_t *migrated, host_t *requested)
+{
+ enumerator_t *enumerator;
+ host_t *found = NULL, *vip;
+
+ enumerator = migrated->create_enumerator(migrated);
+ while (enumerator->enumerate(enumerator, &vip))
+ {
+ if (vip->ip_equals(vip, requested))
+ {
+ migrated->remove_at(migrated, enumerator);
+ found = vip;
+ break;
+ }
+ }
+ enumerator->destroy(enumerator);
+ return found;
+}
+
+/**
* Build CFG_REPLY message after receiving CFG_REQUEST
*/
static status_t build_reply(private_mode_config_t *this, message_t *message)
@@ -468,29 +504,35 @@ static status_t build_reply(private_mode_config_t *this, message_t *message)
cp_payload_t *cp;
peer_cfg_t *config;
identification_t *id;
- linked_list_t *vips, *pools;
- host_t *requested;
+ linked_list_t *vips, *pools, *migrated;
+ host_t *requested, *found;
cp = cp_payload_create_type(PLV1_CONFIGURATION, CFG_REPLY);
id = this->ike_sa->get_other_eap_id(this->ike_sa);
config = this->ike_sa->get_peer_cfg(this->ike_sa);
- vips = linked_list_create();
pools = linked_list_create_from_enumerator(
config->create_pool_enumerator(config));
-
+ /* if we migrated virtual IPs during reauthentication, reassign them */
+ vips = linked_list_create_from_enumerator(
+ this->ike_sa->create_virtual_ip_enumerator(this->ike_sa,
+ FALSE));
+ migrated = vips->clone_offset(vips, offsetof(host_t, clone));
+ vips->destroy(vips);
this->ike_sa->clear_virtual_ips(this->ike_sa, FALSE);
+ vips = linked_list_create();
enumerator = this->vips->create_enumerator(this->vips);
while (enumerator->enumerate(enumerator, &requested))
{
- host_t *found = NULL;
-
- /* query all pools until we get an address */
DBG1(DBG_IKE, "peer requested virtual IP %H", requested);
- found = hydra->attributes->acquire_address(hydra->attributes,
- pools, id, requested);
+ found = assign_migrated_vip(migrated, requested);
+ if (!found)
+ {
+ found = charon->attributes->acquire_address(charon->attributes,
+ pools, this->ike_sa, requested);
+ }
if (found)
{
DBG1(DBG_IKE, "assigning virtual IP %H to peer '%Y'", found, id);
@@ -509,8 +551,8 @@ static status_t build_reply(private_mode_config_t *this, message_t *message)
charon->bus->assign_vips(charon->bus, this->ike_sa, TRUE);
/* query registered providers for additional attributes to include */
- enumerator = hydra->attributes->create_responder_enumerator(
- hydra->attributes, pools, id, vips);
+ enumerator = charon->attributes->create_responder_enumerator(
+ charon->attributes, pools, this->ike_sa, vips);
while (enumerator->enumerate(enumerator, &type, &value))
{
cp->add_attribute(cp,
@@ -518,6 +560,15 @@ static status_t build_reply(private_mode_config_t *this, message_t *message)
type, value));
}
enumerator->destroy(enumerator);
+ /* if a client did not re-request all adresses, release them */
+ enumerator = migrated->create_enumerator(migrated);
+ while (enumerator->enumerate(enumerator, &found))
+ {
+ charon->attributes->release_address(charon->attributes,
+ pools, found, this->ike_sa);
+ }
+ enumerator->destroy(enumerator);
+ migrated->destroy_offset(migrated, offsetof(host_t, destroy));
vips->destroy_offset(vips, offsetof(host_t, destroy));
pools->destroy(pools);
diff --git a/src/libcharon/sa/ikev1/tasks/quick_delete.c b/src/libcharon/sa/ikev1/tasks/quick_delete.c
index 499081caa..1b95a8b11 100644
--- a/src/libcharon/sa/ikev1/tasks/quick_delete.c
+++ b/src/libcharon/sa/ikev1/tasks/quick_delete.c
@@ -105,7 +105,7 @@ static bool delete_child(private_quick_delete_t *this, protocol_id_t protocol,
this->spi = spi = child_sa->get_spi(child_sa, TRUE);
}
- rekeyed = child_sa->get_state(child_sa) == CHILD_REKEYING;
+ rekeyed = child_sa->get_state(child_sa) == CHILD_REKEYED;
child_sa->set_state(child_sa, CHILD_DELETING);
my_ts = linked_list_create_from_enumerator(
@@ -116,7 +116,7 @@ static bool delete_child(private_quick_delete_t *this, protocol_id_t protocol,
{
DBG0(DBG_IKE, "closing expired CHILD_SA %s{%d} "
"with SPIs %.8x_i %.8x_o and TS %#R=== %#R",
- child_sa->get_name(child_sa), child_sa->get_reqid(child_sa),
+ 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);
}
@@ -127,7 +127,7 @@ static bool delete_child(private_quick_delete_t *this, protocol_id_t protocol,
DBG0(DBG_IKE, "closing CHILD_SA %s{%d} with SPIs "
"%.8x_i (%llu bytes) %.8x_o (%llu bytes) and TS %#R=== %#R",
- child_sa->get_name(child_sa), child_sa->get_reqid(child_sa),
+ child_sa->get_name(child_sa), child_sa->get_unique_id(child_sa),
ntohl(child_sa->get_spi(child_sa, TRUE)), bytes_in,
ntohl(child_sa->get_spi(child_sa, FALSE)), bytes_out,
my_ts, other_ts);
diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c
index 1133aab65..96edfd8d8 100644
--- a/src/libcharon/sa/ikev1/tasks/quick_mode.c
+++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c
@@ -156,6 +156,16 @@ struct private_quick_mode_t {
u_int32_t reqid;
/**
+ * Explicit inbound mark value to use, if any
+ */
+ u_int mark_in;
+
+ /**
+ * Explicit inbound mark value to use, if any
+ */
+ u_int mark_out;
+
+ /**
* SPI of SA we rekey
*/
u_int32_t rekey;
@@ -196,8 +206,8 @@ static void schedule_inactivity_timeout(private_quick_mode_t *this)
close_ike = lib->settings->get_bool(lib->settings,
"%s.inactivity_close_ike", FALSE, lib->ns);
lib->scheduler->schedule_job(lib->scheduler, (job_t*)
- inactivity_job_create(this->child_sa->get_reqid(this->child_sa),
- timeout, close_ike), timeout);
+ inactivity_job_create(this->child_sa->get_unique_id(this->child_sa),
+ timeout, close_ike), timeout);
}
}
@@ -375,7 +385,7 @@ static bool install(private_quick_mode_t *this)
DBG0(DBG_IKE, "CHILD_SA %s{%d} established "
"with SPIs %.8x_i %.8x_o and TS %#R=== %#R",
this->child_sa->get_name(this->child_sa),
- this->child_sa->get_reqid(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);
@@ -391,15 +401,14 @@ static bool install(private_quick_mode_t *this)
if (old)
{
charon->bus->child_rekey(charon->bus, old, this->child_sa);
+ /* rekeyed CHILD_SAs stay installed until they expire */
+ old->set_state(old, CHILD_REKEYED);
}
else
{
charon->bus->child_updown(charon->bus, this->child_sa, TRUE);
}
- if (!this->rekey)
- {
- schedule_inactivity_timeout(this);
- }
+ schedule_inactivity_timeout(this);
this->child_sa = NULL;
return TRUE;
}
@@ -456,12 +465,19 @@ static bool get_nonce(private_quick_mode_t *this, chunk_t *nonce,
/**
* Add KE payload to message
*/
-static void add_ke(private_quick_mode_t *this, message_t *message)
+static bool add_ke(private_quick_mode_t *this, message_t *message)
{
ke_payload_t *ke_payload;
- ke_payload = ke_payload_create_from_diffie_hellman(PLV1_KEY_EXCHANGE, this->dh);
+ ke_payload = ke_payload_create_from_diffie_hellman(PLV1_KEY_EXCHANGE,
+ this->dh);
+ if (!ke_payload)
+ {
+ DBG1(DBG_IKE, "creating KE payload failed");
+ return FALSE;
+ }
message->add_payload(message, &ke_payload->payload_interface);
+ return TRUE;
}
/**
@@ -477,8 +493,12 @@ static bool get_ke(private_quick_mode_t *this, message_t *message)
DBG1(DBG_IKE, "KE payload missing");
return FALSE;
}
- this->dh->set_other_public_value(this->dh,
- ke_payload->get_key_exchange_data(ke_payload));
+ if (!this->dh->set_other_public_value(this->dh,
+ ke_payload->get_key_exchange_data(ke_payload)))
+ {
+ DBG1(DBG_IKE, "unable to apply received KE value");
+ return FALSE;
+ }
return TRUE;
}
@@ -788,7 +808,8 @@ METHOD(task_t, build_i, status_t,
this->child_sa = child_sa_create(
this->ike_sa->get_my_host(this->ike_sa),
this->ike_sa->get_other_host(this->ike_sa),
- this->config, this->reqid, this->udp);
+ this->config, this->reqid, this->udp,
+ this->mark_in, this->mark_out);
if (this->udp && this->mode == MODE_TRANSPORT)
{
@@ -870,7 +891,10 @@ METHOD(task_t, build_i, status_t,
}
if (group != MODP_NONE)
{
- add_ke(this, message);
+ if (!add_ke(this, message))
+ {
+ return FAILED;
+ }
}
if (!this->tsi)
{
@@ -964,6 +988,7 @@ static void check_for_rekeyed_child(private_quick_mode_t *this)
{
case CHILD_INSTALLED:
case CHILD_REKEYING:
+ case CHILD_REKEYED:
policies = child_sa->create_policy_enumerator(child_sa);
if (policies->enumerate(policies, &local, &remote) &&
local->equals(local, this->tsr) &&
@@ -972,9 +997,14 @@ static void check_for_rekeyed_child(private_quick_mode_t *this)
{
this->reqid = child_sa->get_reqid(child_sa);
this->rekey = child_sa->get_spi(child_sa, TRUE);
+ this->mark_in = child_sa->get_mark(child_sa,
+ TRUE).value;
+ this->mark_out = child_sa->get_mark(child_sa,
+ FALSE).value;
child_sa->set_state(child_sa, CHILD_REKEYING);
DBG1(DBG_IKE, "detected rekeying of CHILD_SA %s{%u}",
- child_sa->get_name(child_sa), this->reqid);
+ child_sa->get_name(child_sa),
+ child_sa->get_unique_id(child_sa));
}
policies->destroy(policies);
break;
@@ -1097,7 +1127,8 @@ METHOD(task_t, process_r, status_t,
this->child_sa = child_sa_create(
this->ike_sa->get_my_host(this->ike_sa),
this->ike_sa->get_other_host(this->ike_sa),
- this->config, this->reqid, this->udp);
+ this->config, this->reqid, this->udp,
+ this->mark_in, this->mark_out);
tsi = linked_list_create_with_items(this->tsi, NULL);
tsr = linked_list_create_with_items(this->tsr, NULL);
@@ -1202,7 +1233,10 @@ METHOD(task_t, build_r, status_t,
}
if (this->dh)
{
- add_ke(this, message);
+ if (!add_ke(this, message))
+ {
+ return FAILED;
+ }
}
add_ts(this, message);
@@ -1307,6 +1341,13 @@ METHOD(quick_mode_t, use_reqid, void,
this->reqid = reqid;
}
+METHOD(quick_mode_t, use_marks, void,
+ private_quick_mode_t *this, u_int in, u_int out)
+{
+ this->mark_in = in;
+ this->mark_out = out;
+}
+
METHOD(quick_mode_t, rekey, void,
private_quick_mode_t *this, u_int32_t spi)
{
@@ -1334,6 +1375,8 @@ METHOD(task_t, migrate, void,
this->dh = NULL;
this->spi_i = 0;
this->spi_r = 0;
+ this->mark_in = 0;
+ this->mark_out = 0;
if (!this->initiator)
{
@@ -1372,6 +1415,7 @@ quick_mode_t *quick_mode_create(ike_sa_t *ike_sa, child_cfg_t *config,
.destroy = _destroy,
},
.use_reqid = _use_reqid,
+ .use_marks = _use_marks,
.rekey = _rekey,
},
.ike_sa = ike_sa,
diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.h b/src/libcharon/sa/ikev1/tasks/quick_mode.h
index 0b80cb836..ee9b64d13 100644
--- a/src/libcharon/sa/ikev1/tasks/quick_mode.h
+++ b/src/libcharon/sa/ikev1/tasks/quick_mode.h
@@ -45,6 +45,14 @@ struct quick_mode_t {
void (*use_reqid)(quick_mode_t *this, u_int32_t reqid);
/**
+ * Use specific mark values, overriding configuration.
+ *
+ * @param in inbound mark value
+ * @param out outbound mark value
+ */
+ void (*use_marks)(quick_mode_t *this, u_int in, u_int out);
+
+ /**
* Set the SPI of the old SA, if rekeying.
*
* @param spi spi of SA to rekey
diff --git a/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c b/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c
index eed6d1996..ebef31930 100644
--- a/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c
+++ b/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c
@@ -522,6 +522,13 @@ METHOD(authenticator_t, process_server, status_t,
{
return FAILED;
}
+ if (this->method->get_auth)
+ {
+ auth_cfg_t *auth;
+
+ auth = this->ike_sa->get_auth_cfg(this->ike_sa, FALSE);
+ auth->merge(auth, this->method->get_auth(this->method), FALSE);
+ }
return NEED_MORE;
}
diff --git a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c
index 6fb14bc06..151b49718 100644
--- a/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c
+++ b/src/libcharon/sa/ikev2/authenticators/pubkey_authenticator.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 Tobias Brunner
+ * Copyright (C) 2008-2015 Tobias Brunner
* Copyright (C) 2005-2009 Martin Willi
* Copyright (C) 2005 Jan Hutter
* Hochschule fuer Technik Rapperswil
@@ -20,6 +20,9 @@
#include <daemon.h>
#include <encoding/payloads/auth_payload.h>
#include <sa/ikev2/keymat_v2.h>
+#include <asn1/asn1.h>
+#include <asn1/oid.h>
+#include <collections/array.h>
typedef struct private_pubkey_authenticator_t private_pubkey_authenticator_t;
@@ -52,83 +55,303 @@ struct private_pubkey_authenticator_t {
* Reserved bytes of ID payload
*/
char reserved[3];
+
+ /**
+ * Whether to store signature schemes on remote auth configs.
+ */
+ bool store_signature_scheme;
};
-METHOD(authenticator_t, build, status_t,
- private_pubkey_authenticator_t *this, message_t *message)
+/**
+ * Parse authentication data used for Signature Authentication as per RFC 7427
+ */
+static bool parse_signature_auth_data(chunk_t *auth_data, key_type_t *key_type,
+ signature_scheme_t *scheme)
{
- chunk_t octets = chunk_empty, auth_data;
- status_t status = FAILED;
- private_key_t *private;
- identification_t *id;
- auth_cfg_t *auth;
- auth_payload_t *auth_payload;
- auth_method_t auth_method;
+ u_int8_t len;
+ int oid;
+
+ if (!auth_data->len)
+ {
+ return FALSE;
+ }
+ len = auth_data->ptr[0];
+ *auth_data = chunk_skip(*auth_data, 1);
+ /* we currently don't support schemes that require parameters */
+ oid = asn1_parse_algorithmIdentifier(*auth_data, 1, NULL);
+ *scheme = signature_scheme_from_oid(oid);
+ if (*scheme == SIGN_UNKNOWN)
+ {
+ return FALSE;
+ }
+ *key_type = key_type_from_signature_scheme(*scheme);
+ *auth_data = chunk_skip(*auth_data, len);
+ return TRUE;
+}
+
+/**
+ * Build authentication data used for Signature Authentication as per RFC 7427
+ */
+static bool build_signature_auth_data(chunk_t *auth_data,
+ signature_scheme_t scheme)
+{
+ chunk_t data;
+ u_int8_t len;
+ int oid;
+
+ oid = signature_scheme_to_oid(scheme);
+ if (oid == OID_UNKNOWN)
+ {
+ return FALSE;
+ }
+ data = asn1_algorithmIdentifier(oid);
+ len = data.len;
+ *auth_data = chunk_cat("cmm", chunk_from_thing(len), data, *auth_data);
+ return TRUE;
+}
+
+/**
+ * Selects possible signature schemes based on our configuration, the other
+ * peer's capabilities and the private key
+ */
+static array_t *select_signature_schemes(keymat_v2_t *keymat,
+ auth_cfg_t *auth, private_key_t *private)
+{
+ enumerator_t *enumerator;
signature_scheme_t scheme;
+ uintptr_t config;
+ auth_rule_t rule;
+ key_type_t key_type;
+ bool have_config = FALSE;
+ array_t *selected;
+
+ selected = array_create(sizeof(signature_scheme_t), 0);
+ key_type = private->get_type(private);
+ enumerator = auth->create_enumerator(auth);
+ while (enumerator->enumerate(enumerator, &rule, &config))
+ {
+ if (rule != AUTH_RULE_SIGNATURE_SCHEME)
+ {
+ continue;
+ }
+ have_config = TRUE;
+ if (key_type == key_type_from_signature_scheme(config) &&
+ keymat->hash_algorithm_supported(keymat,
+ hasher_from_signature_scheme(config)))
+ {
+ scheme = config;
+ array_insert(selected, ARRAY_TAIL, &scheme);
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ if (!have_config)
+ {
+ /* if no specific configuration, find schemes appropriate for the key
+ * and supported by the other peer */
+ enumerator = signature_schemes_for_key(key_type,
+ private->get_keysize(private));
+ while (enumerator->enumerate(enumerator, &scheme))
+ {
+ if (keymat->hash_algorithm_supported(keymat,
+ hasher_from_signature_scheme(scheme)))
+ {
+ array_insert(selected, ARRAY_TAIL, &scheme);
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ /* for RSA we tried at least SHA-512, also try other schemes down to
+ * what we'd use with classic authentication */
+ if (key_type == KEY_RSA)
+ {
+ signature_scheme_t schemes[] = {
+ SIGN_RSA_EMSA_PKCS1_SHA384,
+ SIGN_RSA_EMSA_PKCS1_SHA256,
+ SIGN_RSA_EMSA_PKCS1_SHA1,
+ }, contained;
+ bool found;
+ int i, j;
+
+ for (i = 0; i < countof(schemes); i++)
+ {
+ scheme = schemes[i];
+ found = FALSE;
+ for (j = 0; j < array_count(selected); j++)
+ {
+ array_get(selected, j, &contained);
+ if (scheme == contained)
+ {
+ found = TRUE;
+ break;
+ }
+ }
+ if (!found && keymat->hash_algorithm_supported(keymat,
+ hasher_from_signature_scheme(scheme)))
+ {
+ array_insert(selected, ARRAY_TAIL, &scheme);
+ }
+ }
+ }
+ }
+ return selected;
+}
+
+/**
+ * Create a signature using RFC 7427 signature authentication
+ */
+static status_t sign_signature_auth(private_pubkey_authenticator_t *this,
+ auth_cfg_t *auth, private_key_t *private,
+ identification_t *id, chunk_t *auth_data)
+{
+ enumerator_t *enumerator;
keymat_v2_t *keymat;
+ signature_scheme_t scheme = SIGN_UNKNOWN, *schemep;
+ array_t *schemes;
+ chunk_t octets = chunk_empty;
+ status_t status = FAILED;
- id = this->ike_sa->get_my_id(this->ike_sa);
- auth = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE);
- private = lib->credmgr->get_private(lib->credmgr, KEY_ANY, id, auth);
- if (private == NULL)
+ keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa);
+ schemes = select_signature_schemes(keymat, auth, private);
+ if (!array_count(schemes))
{
- DBG1(DBG_IKE, "no private key found for '%Y'", id);
- return NOT_FOUND;
+ DBG1(DBG_IKE, "no common hash algorithm found to create signature "
+ "with %N key", key_type_names, private->get_type(private));
+ array_destroy(schemes);
+ return FAILED;
}
+ if (keymat->get_auth_octets(keymat, FALSE, this->ike_sa_init,
+ this->nonce, id, this->reserved, &octets))
+ {
+ enumerator = array_create_enumerator(schemes);
+ while (enumerator->enumerate(enumerator, &schemep))
+ {
+ scheme = *schemep;
+ if (private->sign(private, scheme, octets, auth_data) &&
+ build_signature_auth_data(auth_data, scheme))
+ {
+ status = SUCCESS;
+ break;
+ }
+ else
+ {
+ DBG2(DBG_IKE, "unable to create %N signature for %N key",
+ signature_scheme_names, scheme, key_type_names,
+ private->get_type(private));
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+ DBG1(DBG_IKE, "authentication of '%Y' (myself) with %N %s", id,
+ signature_scheme_names, scheme,
+ status == SUCCESS ? "successful" : "failed");
+ array_destroy(schemes);
+ chunk_free(&octets);
+ return status;
+}
+
+/**
+ * Create a classic IKEv2 signature
+ */
+static status_t sign_classic(private_pubkey_authenticator_t *this,
+ auth_cfg_t *auth, private_key_t *private,
+ identification_t *id, auth_method_t *auth_method,
+ chunk_t *auth_data)
+{
+ signature_scheme_t scheme;
+ keymat_v2_t *keymat;
+ chunk_t octets = chunk_empty;
+ status_t status = FAILED;
+
switch (private->get_type(private))
{
case KEY_RSA:
- /* we currently use always SHA1 for signatures,
- * TODO: support other hashes depending on configuration/auth */
scheme = SIGN_RSA_EMSA_PKCS1_SHA1;
- auth_method = AUTH_RSA;
+ *auth_method = AUTH_RSA;
break;
case KEY_ECDSA:
- /* we try to deduct the signature scheme from the keysize */
+ /* deduct the signature scheme from the keysize */
switch (private->get_keysize(private))
{
case 256:
scheme = SIGN_ECDSA_256;
- auth_method = AUTH_ECDSA_256;
+ *auth_method = AUTH_ECDSA_256;
break;
case 384:
scheme = SIGN_ECDSA_384;
- auth_method = AUTH_ECDSA_384;
+ *auth_method = AUTH_ECDSA_384;
break;
case 521:
scheme = SIGN_ECDSA_521;
- auth_method = AUTH_ECDSA_521;
+ *auth_method = AUTH_ECDSA_521;
break;
default:
DBG1(DBG_IKE, "%d bit ECDSA private key size not supported",
- private->get_keysize(private));
- return status;
+ private->get_keysize(private));
+ return FAILED;
}
break;
default:
DBG1(DBG_IKE, "private key of type %N not supported",
- key_type_names, private->get_type(private));
- return status;
+ key_type_names, private->get_type(private));
+ return FAILED;
}
+
keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa);
if (keymat->get_auth_octets(keymat, FALSE, this->ike_sa_init,
this->nonce, id, this->reserved, &octets) &&
- private->sign(private, scheme, octets, &auth_data))
+ private->sign(private, scheme, octets, auth_data))
{
- auth_payload = auth_payload_create();
- auth_payload->set_auth_method(auth_payload, auth_method);
- auth_payload->set_data(auth_payload, auth_data);
- chunk_free(&auth_data);
- message->add_payload(message, (payload_t*)auth_payload);
status = SUCCESS;
}
DBG1(DBG_IKE, "authentication of '%Y' (myself) with %N %s", id,
- auth_method_names, auth_method,
- (status == SUCCESS)? "successful":"failed");
+ auth_method_names, *auth_method,
+ status == SUCCESS ? "successful" : "failed");
chunk_free(&octets);
+ return status;
+}
+
+METHOD(authenticator_t, build, status_t,
+ private_pubkey_authenticator_t *this, message_t *message)
+{
+ private_key_t *private;
+ identification_t *id;
+ auth_cfg_t *auth;
+ chunk_t auth_data;
+ status_t status;
+ auth_payload_t *auth_payload;
+ auth_method_t auth_method;
+
+ id = this->ike_sa->get_my_id(this->ike_sa);
+ auth = this->ike_sa->get_auth_cfg(this->ike_sa, TRUE);
+ private = lib->credmgr->get_private(lib->credmgr, KEY_ANY, id, auth);
+ if (!private)
+ {
+ DBG1(DBG_IKE, "no private key found for '%Y'", id);
+ return NOT_FOUND;
+ }
+
+ if (this->ike_sa->supports_extension(this->ike_sa, EXT_SIGNATURE_AUTH))
+ {
+ auth_method = AUTH_DS;
+ status = sign_signature_auth(this, auth, private, id, &auth_data);
+ }
+ else
+ {
+ status = sign_classic(this, auth, private, id, &auth_method,
+ &auth_data);
+ }
private->destroy(private);
+ if (status == SUCCESS)
+ {
+ auth_payload = auth_payload_create();
+ auth_payload->set_auth_method(auth_payload, auth_method);
+ auth_payload->set_data(auth_payload, auth_data);
+ chunk_free(&auth_data);
+ message->add_payload(message, (payload_t*)auth_payload);
+ }
return status;
}
@@ -153,11 +376,10 @@ METHOD(authenticator_t, process, status_t,
return FAILED;
}
auth_method = auth_payload->get_auth_method(auth_payload);
+ auth_data = auth_payload->get_data(auth_payload);
switch (auth_method)
{
case AUTH_RSA:
- /* We currently accept SHA1 signatures only
- * TODO: allow other hash algorithms and note it in "auth" */
key_type = KEY_RSA;
scheme = SIGN_RSA_EMSA_PKCS1_SHA1;
break;
@@ -170,10 +392,15 @@ METHOD(authenticator_t, process, status_t,
case AUTH_ECDSA_521:
scheme = SIGN_ECDSA_521;
break;
+ case AUTH_DS:
+ if (parse_signature_auth_data(&auth_data, &key_type, &scheme))
+ {
+ break;
+ }
+ /* fall-through */
default:
return INVALID_ARG;
}
- auth_data = auth_payload->get_data(auth_payload);
id = this->ike_sa->get_other_id(this->ike_sa);
keymat = (keymat_v2_t*)this->ike_sa->get_keymat(this->ike_sa);
if (!keymat->get_auth_octets(keymat, TRUE, this->ike_sa_init,
@@ -188,11 +415,16 @@ METHOD(authenticator_t, process, status_t,
{
if (public->verify(public, scheme, octets, auth_data))
{
- DBG1(DBG_IKE, "authentication of '%Y' with %N successful",
- id, auth_method_names, auth_method);
+ DBG1(DBG_IKE, "authentication of '%Y' with %N successful", id,
+ auth_method == AUTH_DS ? signature_scheme_names : auth_method_names,
+ auth_method == AUTH_DS ? scheme : auth_method);
status = SUCCESS;
auth->merge(auth, current_auth, FALSE);
auth->add(auth, AUTH_RULE_AUTH_CLASS, AUTH_CLASS_PUBKEY);
+ if (this->store_signature_scheme)
+ {
+ auth->add(auth, AUTH_RULE_SIGNATURE_SCHEME, (uintptr_t)scheme);
+ }
break;
}
else
@@ -265,6 +497,8 @@ pubkey_authenticator_t *pubkey_authenticator_create_verifier(ike_sa_t *ike_sa,
.ike_sa = ike_sa,
.ike_sa_init = received_init,
.nonce = sent_nonce,
+ .store_signature_scheme = lib->settings->get_bool(lib->settings,
+ "%s.signature_authentication_constraints", TRUE, lib->ns),
);
memcpy(this->reserved, reserved, sizeof(this->reserved));
diff --git a/src/libcharon/sa/ikev2/keymat_v2.c b/src/libcharon/sa/ikev2/keymat_v2.c
index 88ad14faf..f70f5cfed 100644
--- a/src/libcharon/sa/ikev2/keymat_v2.c
+++ b/src/libcharon/sa/ikev2/keymat_v2.c
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2015 Tobias Brunner
* Copyright (C) 2008 Martin Willi
* Hochschule fuer Technik Rapperswil
*
@@ -17,6 +18,7 @@
#include <daemon.h>
#include <crypto/prf_plus.h>
+#include <crypto/hashers/hash_algorithm_set.h>
typedef struct private_keymat_v2_t private_keymat_v2_t;
@@ -69,6 +71,11 @@ struct private_keymat_v2_t {
* Key to verify incoming authentication data (SKp)
*/
chunk_t skp_verify;
+
+ /**
+ * Set of hash algorithms supported by peer for signature authentication
+ */
+ hash_algorithm_set_t *hash_algorithms;
};
METHOD(keymat_t, get_version, ike_version_t,
@@ -293,7 +300,7 @@ METHOD(keymat_v2_t, derive_ike_keys, bool,
spi_i = chunk_alloca(sizeof(u_int64_t));
spi_r = chunk_alloca(sizeof(u_int64_t));
- if (dh->get_shared_secret(dh, &secret) != SUCCESS)
+ if (!dh->get_shared_secret(dh, &secret))
{
return FALSE;
}
@@ -547,7 +554,7 @@ METHOD(keymat_v2_t, derive_child_keys, bool,
if (dh)
{
- if (dh->get_shared_secret(dh, &secret) != SUCCESS)
+ if (!dh->get_shared_secret(dh, &secret))
{
return FALSE;
}
@@ -676,6 +683,26 @@ METHOD(keymat_v2_t, get_psk_sig, bool,
return TRUE;
}
+METHOD(keymat_v2_t, hash_algorithm_supported, bool,
+ private_keymat_v2_t *this, hash_algorithm_t hash)
+{
+ if (!this->hash_algorithms)
+ {
+ return FALSE;
+ }
+ return this->hash_algorithms->contains(this->hash_algorithms, hash);
+}
+
+METHOD(keymat_v2_t, add_hash_algorithm, void,
+ private_keymat_v2_t *this, hash_algorithm_t hash)
+{
+ if (!this->hash_algorithms)
+ {
+ this->hash_algorithms = hash_algorithm_set_create();
+ }
+ this->hash_algorithms->add(this->hash_algorithms, hash);
+}
+
METHOD(keymat_t, destroy, void,
private_keymat_v2_t *this)
{
@@ -685,6 +712,7 @@ METHOD(keymat_t, destroy, void,
chunk_clear(&this->skd);
chunk_clear(&this->skp_verify);
chunk_clear(&this->skp_build);
+ DESTROY_IF(this->hash_algorithms);
free(this);
}
@@ -709,6 +737,9 @@ keymat_v2_t *keymat_v2_create(bool initiator)
.get_skd = _get_skd,
.get_auth_octets = _get_auth_octets,
.get_psk_sig = _get_psk_sig,
+ .add_hash_algorithm = _add_hash_algorithm,
+ .hash_algorithm_supported = _hash_algorithm_supported,
+
},
.initiator = initiator,
.prf_alg = PRF_UNDEFINED,
diff --git a/src/libcharon/sa/ikev2/keymat_v2.h b/src/libcharon/sa/ikev2/keymat_v2.h
index 04432f05b..927b62b03 100644
--- a/src/libcharon/sa/ikev2/keymat_v2.h
+++ b/src/libcharon/sa/ikev2/keymat_v2.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 Tobias Brunner
+ * Copyright (C) 2011-2015 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
@@ -124,6 +124,22 @@ struct keymat_v2_t {
bool (*get_psk_sig)(keymat_v2_t *this, bool verify, chunk_t ike_sa_init,
chunk_t nonce, chunk_t secret,
identification_t *id, char reserved[3], chunk_t *sig);
+
+ /**
+ * Add a hash algorithm supported by the peer for signature authentication.
+ *
+ * @param hash hash algorithm
+ */
+ void (*add_hash_algorithm)(keymat_v2_t *this, hash_algorithm_t hash);
+
+ /**
+ * Check if a given hash algorithm is supported by the peer for signature
+ * authentication.
+ *
+ * @param hash hash algorithm
+ * @return TRUE if supported, FALSE otherwise
+ */
+ bool (*hash_algorithm_supported)(keymat_v2_t *this, hash_algorithm_t hash);
};
/**
diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c
index eb7df3516..298167703 100644
--- a/src/libcharon/sa/ikev2/task_manager_v2.c
+++ b/src/libcharon/sa/ikev2/task_manager_v2.c
@@ -29,6 +29,7 @@
#include <sa/ikev2/tasks/ike_cert_post.h>
#include <sa/ikev2/tasks/ike_rekey.h>
#include <sa/ikev2/tasks/ike_reauth.h>
+#include <sa/ikev2/tasks/ike_reauth_complete.h>
#include <sa/ikev2/tasks/ike_delete.h>
#include <sa/ikev2/tasks/ike_config.h>
#include <sa/ikev2/tasks/ike_dpd.h>
@@ -171,6 +172,11 @@ struct private_task_manager_t {
* Base to calculate retransmission timeout
*/
double retransmit_base;
+
+ /**
+ * Use make-before-break instead of break-before-make reauth?
+ */
+ bool make_before_break;
};
/**
@@ -510,6 +516,11 @@ METHOD(task_manager_t, initiate, status_t,
break;
}
#endif /* ME */
+ if (activate_task(this, TASK_IKE_REAUTH_COMPLETE))
+ {
+ exchange = INFORMATIONAL;
+ break;
+ }
case IKE_REKEYING:
if (activate_task(this, TASK_IKE_DELETE))
{
@@ -604,6 +615,11 @@ METHOD(task_manager_t, initiate, status_t,
/* update exchange type if a task changed it */
this->initiating.type = message->get_exchange_type(message);
+ if (this->initiating.type == EXCHANGE_TYPE_UNDEFINED)
+ {
+ message->destroy(message);
+ return SUCCESS;
+ }
if (!generate_message(this, message, &this->initiating.packets))
{
@@ -1170,7 +1186,7 @@ static status_t parse_message(private_task_manager_t *this, message_t *msg)
{
unknown = (unknown_payload_t*)payload;
type = payload->get_type(payload);
- if (!payload_is_known(type) &&
+ if (!payload_is_known(type, msg->get_major_version(msg)) &&
unknown->is_critical(unknown))
{
DBG1(DBG_ENC, "payload type %N is not supported, "
@@ -1288,17 +1304,16 @@ METHOD(task_manager_t, process_message, status_t,
{
if (mid == this->responding.mid)
{
- /* reject initial messages once established */
- if (msg->get_exchange_type(msg) == IKE_SA_INIT ||
- msg->get_exchange_type(msg) == IKE_AUTH)
+ /* reject initial messages if not received in specific states */
+ if ((msg->get_exchange_type(msg) == IKE_SA_INIT &&
+ this->ike_sa->get_state(this->ike_sa) != IKE_CREATED) ||
+ (msg->get_exchange_type(msg) == IKE_AUTH &&
+ this->ike_sa->get_state(this->ike_sa) != IKE_CONNECTING))
{
- if (this->ike_sa->get_state(this->ike_sa) != IKE_CREATED &&
- this->ike_sa->get_state(this->ike_sa) != IKE_CONNECTING)
- {
- DBG1(DBG_IKE, "ignoring %N in established IKE_SA state",
- exchange_type_names, msg->get_exchange_type(msg));
- return FAILED;
- }
+ DBG1(DBG_IKE, "ignoring %N in IKE_SA state %N",
+ exchange_type_names, msg->get_exchange_type(msg),
+ ike_sa_state_names, this->ike_sa->get_state(this->ike_sa));
+ return FAILED;
}
if (!this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE))
{ /* with MOBIKE, we do no implicit updates */
@@ -1339,10 +1354,6 @@ METHOD(task_manager_t, process_message, status_t,
{
DBG1(DBG_IKE, "received message ID %d, expected %d. Ignored",
mid, this->responding.mid);
- if (msg->get_exchange_type(msg) == IKE_SA_INIT)
- { /* clean up IKE_SA state if IKE_SA_INIT has invalid msg ID */
- return DESTROY_ME;
- }
}
}
else
@@ -1505,9 +1516,79 @@ METHOD(task_manager_t, queue_ike_rekey, void,
queue_task(this, (task_t*)ike_rekey_create(this->ike_sa, TRUE));
}
+/**
+ * Start reauthentication using make-before-break
+ */
+static void trigger_mbb_reauth(private_task_manager_t *this)
+{
+ enumerator_t *enumerator;
+ child_sa_t *child_sa;
+ child_cfg_t *cfg;
+ ike_sa_t *new;
+ host_t *host;
+ task_t *task;
+
+ new = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager,
+ this->ike_sa->get_version(this->ike_sa), TRUE);
+ if (!new)
+ { /* shouldn't happen */
+ return;
+ }
+
+ new->set_peer_cfg(new, this->ike_sa->get_peer_cfg(this->ike_sa));
+ host = this->ike_sa->get_other_host(this->ike_sa);
+ new->set_other_host(new, host->clone(host));
+ host = this->ike_sa->get_my_host(this->ike_sa);
+ new->set_my_host(new, host->clone(host));
+ enumerator = this->ike_sa->create_virtual_ip_enumerator(this->ike_sa, TRUE);
+ while (enumerator->enumerate(enumerator, &host))
+ {
+ new->add_virtual_ip(new, TRUE, host);
+ }
+ enumerator->destroy(enumerator);
+
+ enumerator = this->ike_sa->create_child_sa_enumerator(this->ike_sa);
+ while (enumerator->enumerate(enumerator, &child_sa))
+ {
+ cfg = child_sa->get_config(child_sa);
+ new->queue_task(new, &child_create_create(new, cfg->get_ref(cfg),
+ FALSE, NULL, NULL)->task);
+ }
+ enumerator->destroy(enumerator);
+
+ enumerator = array_create_enumerator(this->queued_tasks);
+ while (enumerator->enumerate(enumerator, &task))
+ {
+ if (task->get_type(task) == TASK_CHILD_CREATE)
+ {
+ task->migrate(task, new);
+ new->queue_task(new, task);
+ array_remove_at(this->queued_tasks, enumerator);
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ if (new->initiate(new, NULL, 0, NULL, NULL) != DESTROY_ME)
+ {
+ new->queue_task(new, (task_t*)ike_reauth_complete_create(new,
+ this->ike_sa->get_id(this->ike_sa)));
+ charon->ike_sa_manager->checkin(charon->ike_sa_manager, new);
+ }
+ else
+ {
+ charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, new);
+ DBG1(DBG_IKE, "reauthenticating IKE_SA failed");
+ }
+ charon->bus->set_sa(charon->bus, this->ike_sa);
+}
+
METHOD(task_manager_t, queue_ike_reauth, void,
private_task_manager_t *this)
{
+ if (this->make_before_break)
+ {
+ return trigger_mbb_reauth(this);
+ }
queue_task(this, (task_t*)ike_reauth_create(this->ike_sa));
}
@@ -1773,6 +1854,8 @@ task_manager_v2_t *task_manager_v2_create(ike_sa_t *ike_sa)
"%s.retransmit_timeout", RETRANSMIT_TIMEOUT, lib->ns),
.retransmit_base = lib->settings->get_double(lib->settings,
"%s.retransmit_base", RETRANSMIT_BASE, lib->ns),
+ .make_before_break = lib->settings->get_bool(lib->settings,
+ "%s.make_before_break", FALSE, lib->ns),
);
return &this->public;
diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c
index a1f01c276..6d9132a68 100644
--- a/src/libcharon/sa/ikev2/tasks/child_create.c
+++ b/src/libcharon/sa/ikev2/tasks/child_create.c
@@ -105,6 +105,11 @@ struct private_child_create_t {
diffie_hellman_t *dh;
/**
+ * Applying DH public value failed?
+ */
+ bool dh_failed;
+
+ /**
* group used for DH exchange
*/
diffie_hellman_group_t dh_group;
@@ -160,6 +165,16 @@ struct private_child_create_t {
u_int32_t reqid;
/**
+ * Explicit inbound mark value
+ */
+ u_int mark_in;
+
+ /**
+ * Explicit outbound mark value
+ */
+ u_int mark_out;
+
+ /**
* CHILD_SA which gets established
*/
child_sa_t *child_sa;
@@ -286,7 +301,7 @@ static bool allocate_spi(private_child_create_t *this)
*/
static void schedule_inactivity_timeout(private_child_create_t *this)
{
- u_int32_t timeout;
+ u_int32_t timeout, id;
bool close_ike;
timeout = this->config->get_inactivity(this->config);
@@ -294,9 +309,9 @@ static void schedule_inactivity_timeout(private_child_create_t *this)
{
close_ike = lib->settings->get_bool(lib->settings,
"%s.inactivity_close_ike", FALSE, lib->ns);
+ id = this->child_sa->get_unique_id(this->child_sa);
lib->scheduler->schedule_job(lib->scheduler, (job_t*)
- inactivity_job_create(this->child_sa->get_reqid(this->child_sa),
- timeout, close_ike), timeout);
+ inactivity_job_create(id, timeout, close_ike), timeout);
}
}
@@ -683,10 +698,7 @@ static status_t select_and_install(private_child_create_t *this,
this->ike_sa->add_child_sa(this->ike_sa, this->child_sa);
this->established = TRUE;
- if (!this->rekey)
- { /* a rekeyed SA uses the same reqid, no need for a new job */
- schedule_inactivity_timeout(this);
- }
+ schedule_inactivity_timeout(this);
my_ts = linked_list_create_from_enumerator(
this->child_sa->create_ts_enumerator(this->child_sa, TRUE));
@@ -696,7 +708,7 @@ static status_t select_and_install(private_child_create_t *this,
DBG0(DBG_IKE, "CHILD_SA %s{%d} established "
"with SPIs %.8x_i %.8x_o and TS %#R=== %#R",
this->child_sa->get_name(this->child_sa),
- this->child_sa->get_reqid(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);
@@ -709,7 +721,7 @@ static status_t select_and_install(private_child_create_t *this,
/**
* build the payloads for the message
*/
-static void build_payloads(private_child_create_t *this, message_t *message)
+static bool build_payloads(private_child_create_t *this, message_t *message)
{
sa_payload_t *sa_payload;
nonce_payload_t *nonce_payload;
@@ -741,6 +753,11 @@ static void build_payloads(private_child_create_t *this, message_t *message)
{
ke_payload = ke_payload_create_from_diffie_hellman(PLV2_KEY_EXCHANGE,
this->dh);
+ if (!ke_payload)
+ {
+ DBG1(DBG_IKE, "creating KE payload failed");
+ return FALSE;
+ }
message->add_payload(message, (payload_t*)ke_payload);
}
@@ -769,6 +786,7 @@ static void build_payloads(private_child_create_t *this, message_t *message)
message->add_notify(message, FALSE, ESP_TFC_PADDING_NOT_SUPPORTED,
chunk_empty);
}
+ return TRUE;
}
/**
@@ -880,7 +898,7 @@ static void process_payloads(private_child_create_t *this, message_t *message)
}
if (this->dh)
{
- this->dh->set_other_public_value(this->dh,
+ this->dh_failed = !this->dh->set_other_public_value(this->dh,
ke_payload->get_key_exchange_data(ke_payload));
}
break;
@@ -996,7 +1014,8 @@ METHOD(task_t, build_i, status_t,
this->child_sa = child_sa_create(this->ike_sa->get_my_host(this->ike_sa),
this->ike_sa->get_other_host(this->ike_sa), this->config, this->reqid,
- this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY));
+ this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY),
+ this->mark_in, this->mark_out);
if (!allocate_spi(this))
{
@@ -1027,7 +1046,10 @@ METHOD(task_t, build_i, status_t,
NARROW_INITIATOR_PRE_AUTH, this->tsi, this->tsr);
}
- build_payloads(this, message);
+ if (!build_payloads(this, message))
+ {
+ return FAILED;
+ }
this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy));
@@ -1168,12 +1190,19 @@ METHOD(task_t, build_r, status_t,
case IKE_SA_INIT:
return get_nonce(message, &this->my_nonce);
case CREATE_CHILD_SA:
- if (generate_nonce(this) != SUCCESS)
+ if (generate_nonce(this) != SUCCESS )
{
message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN,
chunk_empty);
return SUCCESS;
}
+ if (this->dh_failed)
+ {
+ DBG1(DBG_IKE, "applying DH public value failed");
+ message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN,
+ chunk_empty);
+ return SUCCESS;
+ }
no_dh = FALSE;
break;
case IKE_AUTH:
@@ -1241,7 +1270,8 @@ METHOD(task_t, build_r, status_t,
this->child_sa = child_sa_create(this->ike_sa->get_my_host(this->ike_sa),
this->ike_sa->get_other_host(this->ike_sa), this->config, this->reqid,
- this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY));
+ this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY),
+ this->mark_in, this->mark_out);
if (this->ipcomp_received != IPCOMP_NONE)
{
@@ -1279,7 +1309,12 @@ METHOD(task_t, build_r, status_t,
return SUCCESS;
}
- build_payloads(this, message);
+ if (!build_payloads(this, message))
+ {
+ message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, chunk_empty);
+ handle_child_sa_failure(this, message);
+ return SUCCESS;
+ }
if (!this->rekey)
{ /* invoke the child_up() hook if we are not rekeying */
@@ -1408,6 +1443,7 @@ METHOD(task_t, process_i, status_t,
this->dh_group, diffie_hellman_group_names, group);
this->retry = TRUE;
this->dh_group = group;
+ this->child_sa->set_state(this->child_sa, CHILD_RETRYING);
this->public.task.migrate(&this->public.task, this->ike_sa);
enumerator->destroy(enumerator);
return NEED_MORE;
@@ -1456,6 +1492,13 @@ METHOD(task_t, process_i, status_t,
return delete_failed_sa(this);
}
+ if (this->dh_failed)
+ {
+ DBG1(DBG_IKE, "applying DH public value failed");
+ handle_child_sa_failure(this, message);
+ return delete_failed_sa(this);
+ }
+
if (select_and_install(this, no_dh, ike_auth) == SUCCESS)
{
if (!this->rekey)
@@ -1477,6 +1520,13 @@ METHOD(child_create_t, use_reqid, void,
this->reqid = reqid;
}
+METHOD(child_create_t, use_marks, void,
+ private_child_create_t *this, u_int in, u_int out)
+{
+ this->mark_in = in;
+ this->mark_out = out;
+}
+
METHOD(child_create_t, get_child, child_sa_t*,
private_child_create_t *this)
{
@@ -1526,6 +1576,7 @@ METHOD(task_t, migrate, void,
DESTROY_IF(this->child_sa);
DESTROY_IF(this->proposal);
DESTROY_IF(this->dh);
+ this->dh_failed = FALSE;
if (this->proposals)
{
this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy));
@@ -1544,6 +1595,8 @@ METHOD(task_t, migrate, void,
this->ipcomp_received = IPCOMP_NONE;
this->other_cpi = 0;
this->reqid = 0;
+ this->mark_in = 0;
+ this->mark_out = 0;
this->established = FALSE;
}
@@ -1592,6 +1645,7 @@ child_create_t *child_create_create(ike_sa_t *ike_sa,
.set_config = _set_config,
.get_lower_nonce = _get_lower_nonce,
.use_reqid = _use_reqid,
+ .use_marks = _use_marks,
.task = {
.get_type = _get_type,
.migrate = _migrate,
diff --git a/src/libcharon/sa/ikev2/tasks/child_create.h b/src/libcharon/sa/ikev2/tasks/child_create.h
index d29ba3d98..46d9403ee 100644
--- a/src/libcharon/sa/ikev2/tasks/child_create.h
+++ b/src/libcharon/sa/ikev2/tasks/child_create.h
@@ -52,6 +52,14 @@ struct child_create_t {
void (*use_reqid) (child_create_t *this, u_int32_t reqid);
/**
+ * Use specific mark values to override configuration.
+ *
+ * @param in inbound mark value
+ * @param out outbound mark value
+ */
+ void (*use_marks)(child_create_t *this, u_int in, u_int out);
+
+ /**
* Get the lower of the two nonces, used for rekey collisions.
*
* @return lower nonce
diff --git a/src/libcharon/sa/ikev2/tasks/child_delete.c b/src/libcharon/sa/ikev2/tasks/child_delete.c
index 2b1697423..f0b11e291 100644
--- a/src/libcharon/sa/ikev2/tasks/child_delete.c
+++ b/src/libcharon/sa/ikev2/tasks/child_delete.c
@@ -267,7 +267,7 @@ static void log_children(private_child_delete_t *this)
{
DBG0(DBG_IKE, "closing expired CHILD_SA %s{%d} "
"with SPIs %.8x_i %.8x_o and TS %#R=== %#R",
- child_sa->get_name(child_sa), child_sa->get_reqid(child_sa),
+ 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);
}
@@ -278,7 +278,7 @@ static void log_children(private_child_delete_t *this)
DBG0(DBG_IKE, "closing CHILD_SA %s{%d} with SPIs %.8x_i "
"(%llu bytes) %.8x_o (%llu bytes) and TS %#R=== %#R",
- child_sa->get_name(child_sa), child_sa->get_reqid(child_sa),
+ child_sa->get_name(child_sa), child_sa->get_unique_id(child_sa),
ntohl(child_sa->get_spi(child_sa, TRUE)), bytes_in,
ntohl(child_sa->get_spi(child_sa, FALSE)), bytes_out,
my_ts, other_ts);
diff --git a/src/libcharon/sa/ikev2/tasks/child_rekey.c b/src/libcharon/sa/ikev2/tasks/child_rekey.c
index db872827d..c806e19ca 100644
--- a/src/libcharon/sa/ikev2/tasks/child_rekey.c
+++ b/src/libcharon/sa/ikev2/tasks/child_rekey.c
@@ -96,9 +96,9 @@ static void schedule_delayed_rekey(private_child_rekey_t *this)
retry = RETRY_INTERVAL - (random() % RETRY_JITTER);
job = (job_t*)rekey_child_sa_job_create(
- this->child_sa->get_reqid(this->child_sa),
this->child_sa->get_protocol(this->child_sa),
- this->child_sa->get_spi(this->child_sa, TRUE));
+ this->child_sa->get_spi(this->child_sa, TRUE),
+ this->ike_sa->get_my_host(this->ike_sa));
DBG1(DBG_IKE, "CHILD_SA rekeying failed, trying again in %d seconds", retry);
this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
lib->scheduler->schedule_job(lib->scheduler, job, retry);
@@ -184,6 +184,9 @@ METHOD(task_t, build_i, status_t,
}
reqid = this->child_sa->get_reqid(this->child_sa);
this->child_create->use_reqid(this->child_create, reqid);
+ this->child_create->use_marks(this->child_create,
+ this->child_sa->get_mark(this->child_sa, TRUE).value,
+ this->child_sa->get_mark(this->child_sa, FALSE).value);
if (this->child_create->task.build(&this->child_create->task,
message) != NEED_MORE)
@@ -224,6 +227,9 @@ METHOD(task_t, build_r, status_t,
/* let the CHILD_CREATE task build the response */
reqid = this->child_sa->get_reqid(this->child_sa);
this->child_create->use_reqid(this->child_create, reqid);
+ this->child_create->use_marks(this->child_create,
+ this->child_sa->get_mark(this->child_sa, TRUE).value,
+ this->child_sa->get_mark(this->child_sa, FALSE).value);
config = this->child_sa->get_config(this->child_sa);
this->child_create->set_config(this->child_create, config->get_ref(config));
this->child_create->task.build(&this->child_create->task, message);
diff --git a/src/libcharon/sa/ikev2/tasks/ike_cert_pre.c b/src/libcharon/sa/ikev2/tasks/ike_cert_pre.c
index 0dac975e7..ca17494de 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_cert_pre.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_cert_pre.c
@@ -229,12 +229,12 @@ static void process_x509(cert_payload_t *payload, auth_cfg_t *auth,
return;
}
url = strdup(url);
- if (first)
+ if (*first)
{ /* first URL is for an end entity certificate */
DBG1(DBG_IKE, "received hash-and-url for end entity cert \"%s\"",
url);
auth->add(auth, AUTH_HELPER_SUBJECT_HASH_URL, url);
- first = FALSE;
+ *first = FALSE;
}
else
{
diff --git a/src/libcharon/sa/ikev2/tasks/ike_config.c b/src/libcharon/sa/ikev2/tasks/ike_config.c
index da06e2a36..646f20c61 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_config.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_config.c
@@ -17,7 +17,6 @@
#include "ike_config.h"
#include <daemon.h>
-#include <hydra.h>
#include <encoding/payloads/cp_payload.h>
typedef struct private_ike_config_t private_ike_config_t;
@@ -127,9 +126,8 @@ static void handle_attribute(private_ike_config_t *this,
enumerator->destroy(enumerator);
/* and pass it to the handle function */
- handler = hydra->attributes->handle(hydra->attributes,
- this->ike_sa->get_other_id(this->ike_sa), handler,
- ca->get_type(ca), ca->get_chunk(ca));
+ handler = charon->attributes->handle(charon->attributes,
+ this->ike_sa, handler, ca->get_type(ca), ca->get_chunk(ca));
this->ike_sa->add_configuration_attribute(this->ike_sa,
handler, ca->get_type(ca), ca->get_chunk(ca));
}
@@ -274,9 +272,8 @@ METHOD(task_t, build_i, status_t,
enumerator->destroy(enumerator);
}
- enumerator = hydra->attributes->create_initiator_enumerator(
- hydra->attributes,
- this->ike_sa->get_other_id(this->ike_sa), vips);
+ enumerator = charon->attributes->create_initiator_enumerator(
+ charon->attributes, this->ike_sa, vips);
while (enumerator->enumerate(enumerator, &handler, &type, &data))
{
configuration_attribute_t *ca;
@@ -352,8 +349,8 @@ METHOD(task_t, build_r, status_t,
/* query all pools until we get an address */
DBG1(DBG_IKE, "peer requested virtual IP %H", requested);
- found = hydra->attributes->acquire_address(hydra->attributes,
- pools, id, requested);
+ found = charon->attributes->acquire_address(charon->attributes,
+ pools, this->ike_sa, requested);
if (found)
{
DBG1(DBG_IKE, "assigning virtual IP %H to peer '%Y'", found, id);
@@ -398,8 +395,8 @@ METHOD(task_t, build_r, status_t,
}
/* query registered providers for additional attributes to include */
- enumerator = hydra->attributes->create_responder_enumerator(
- hydra->attributes, pools, id, vips);
+ enumerator = charon->attributes->create_responder_enumerator(
+ charon->attributes, pools, this->ike_sa, vips);
while (enumerator->enumerate(enumerator, &type, &value))
{
if (!cp)
diff --git a/src/libcharon/sa/ikev2/tasks/ike_init.c b/src/libcharon/sa/ikev2/tasks/ike_init.c
index 71c5f22fa..0d5700ef2 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_init.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_init.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2009 Tobias Brunner
+ * Copyright (C) 2008-2015 Tobias Brunner
* Copyright (C) 2005-2008 Martin Willi
* Copyright (C) 2005 Jan Hutter
* Hochschule fuer Technik Rapperswil
@@ -20,8 +20,11 @@
#include <string.h>
#include <daemon.h>
+#include <bio/bio_reader.h>
+#include <bio/bio_writer.h>
#include <sa/ikev2/keymat_v2.h>
#include <crypto/diffie_hellman.h>
+#include <crypto/hashers/hash_algorithm_set.h>
#include <encoding/payloads/sa_payload.h>
#include <encoding/payloads/ke_payload.h>
#include <encoding/payloads/nonce_payload.h>
@@ -67,6 +70,11 @@ struct private_ike_init_t {
diffie_hellman_t *dh;
/**
+ * Applying DH public value failed?
+ */
+ bool dh_failed;
+
+ /**
* Keymat derivation (from IKE_SA)
*/
keymat_v2_t *keymat;
@@ -100,12 +108,114 @@ struct private_ike_init_t {
* retries done so far after failure (cookie or bad dh group)
*/
u_int retry;
+
+ /**
+ * Whether to use Signature Authentication as per RFC 7427
+ */
+ bool signature_authentication;
};
/**
+ * Notify the peer about the hash algorithms we support or expect,
+ * as per RFC 7427
+ */
+static void send_supported_hash_algorithms(private_ike_init_t *this,
+ message_t *message)
+{
+ hash_algorithm_set_t *algos;
+ enumerator_t *enumerator, *rounds;
+ bio_writer_t *writer;
+ hash_algorithm_t hash;
+ peer_cfg_t *peer;
+ auth_cfg_t *auth;
+ auth_rule_t rule;
+ uintptr_t config;
+ char *plugin_name;
+
+ algos = hash_algorithm_set_create();
+ peer = this->ike_sa->get_peer_cfg(this->ike_sa);
+ if (peer)
+ {
+ rounds = peer->create_auth_cfg_enumerator(peer, FALSE);
+ while (rounds->enumerate(rounds, &auth))
+ {
+ enumerator = auth->create_enumerator(auth);
+ while (enumerator->enumerate(enumerator, &rule, &config))
+ {
+ if (rule == AUTH_RULE_SIGNATURE_SCHEME)
+ {
+ hash = hasher_from_signature_scheme(config);
+ if (hasher_algorithm_for_ikev2(hash))
+ {
+ algos->add(algos, hash);
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+ rounds->destroy(rounds);
+ }
+
+ if (!algos->count(algos))
+ {
+ enumerator = lib->crypto->create_hasher_enumerator(lib->crypto);
+ while (enumerator->enumerate(enumerator, &hash, &plugin_name))
+ {
+ if (hasher_algorithm_for_ikev2(hash))
+ {
+ algos->add(algos, hash);
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
+
+ if (algos->count(algos))
+ {
+ writer = bio_writer_create(0);
+ enumerator = algos->create_enumerator(algos);
+ while (enumerator->enumerate(enumerator, &hash))
+ {
+ writer->write_uint16(writer, hash);
+ }
+ enumerator->destroy(enumerator);
+ message->add_notify(message, FALSE, SIGNATURE_HASH_ALGORITHMS,
+ writer->get_buf(writer));
+ writer->destroy(writer);
+ }
+ algos->destroy(algos);
+}
+
+/**
+ * Store algorithms supported by other peer
+ */
+static void handle_supported_hash_algorithms(private_ike_init_t *this,
+ notify_payload_t *notify)
+{
+ bio_reader_t *reader;
+ u_int16_t algo;
+ bool added = FALSE;
+
+ reader = bio_reader_create(notify->get_notification_data(notify));
+ while (reader->remaining(reader) >= 2 && reader->read_uint16(reader, &algo))
+ {
+ if (hasher_algorithm_for_ikev2(algo))
+ {
+ this->keymat->add_hash_algorithm(this->keymat, algo);
+ added = TRUE;
+ }
+ }
+ reader->destroy(reader);
+
+ if (added)
+ {
+ this->ike_sa->enable_extension(this->ike_sa, EXT_SIGNATURE_AUTH);
+ }
+}
+
+/**
* build the payloads for the message
*/
-static void build_payloads(private_ike_init_t *this, message_t *message)
+static bool build_payloads(private_ike_init_t *this, message_t *message)
{
sa_payload_t *sa_payload;
ke_payload_t *ke_payload;
@@ -149,7 +259,13 @@ static void build_payloads(private_ike_init_t *this, message_t *message)
nonce_payload = nonce_payload_create(PLV2_NONCE);
nonce_payload->set_nonce(nonce_payload, this->my_nonce);
- ke_payload = ke_payload_create_from_diffie_hellman(PLV2_KEY_EXCHANGE, this->dh);
+ ke_payload = ke_payload_create_from_diffie_hellman(PLV2_KEY_EXCHANGE,
+ this->dh);
+ if (!ke_payload)
+ {
+ DBG1(DBG_IKE, "creating KE payload failed");
+ return FALSE;
+ }
if (this->old_sa)
{ /* payload order differs if we are rekeying */
@@ -174,6 +290,17 @@ static void build_payloads(private_ike_init_t *this, message_t *message)
chunk_empty);
}
}
+ /* submit supported hash algorithms for signature authentication */
+ if (!this->old_sa && this->signature_authentication)
+ {
+ if (this->initiator ||
+ this->ike_sa->supports_extension(this->ike_sa,
+ EXT_SIGNATURE_AUTH))
+ {
+ send_supported_hash_algorithms(this, message);
+ }
+ }
+ return TRUE;
}
/**
@@ -183,6 +310,7 @@ static void process_payloads(private_ike_init_t *this, message_t *message)
{
enumerator_t *enumerator;
payload_t *payload;
+ ke_payload_t *ke_payload = NULL;
enumerator = message->create_payload_enumerator(message);
while (enumerator->enumerate(enumerator, &payload))
@@ -211,19 +339,9 @@ static void process_payloads(private_ike_init_t *this, message_t *message)
}
case PLV2_KEY_EXCHANGE:
{
- ke_payload_t *ke_payload = (ke_payload_t*)payload;
+ ke_payload = (ke_payload_t*)payload;
this->dh_group = ke_payload->get_dh_group_number(ke_payload);
- if (!this->initiator)
- {
- this->dh = this->keymat->keymat.create_dh(
- &this->keymat->keymat, this->dh_group);
- }
- if (this->dh)
- {
- this->dh->set_other_public_value(this->dh,
- ke_payload->get_key_exchange_data(ke_payload));
- }
break;
}
case PLV2_NONCE:
@@ -237,17 +355,44 @@ static void process_payloads(private_ike_init_t *this, message_t *message)
{
notify_payload_t *notify = (notify_payload_t*)payload;
- if (notify->get_notify_type(notify) == FRAGMENTATION_SUPPORTED)
+ switch (notify->get_notify_type(notify))
{
- this->ike_sa->enable_extension(this->ike_sa,
- EXT_IKE_FRAGMENTATION);
+ case FRAGMENTATION_SUPPORTED:
+ this->ike_sa->enable_extension(this->ike_sa,
+ EXT_IKE_FRAGMENTATION);
+ break;
+ case SIGNATURE_HASH_ALGORITHMS:
+ if (this->signature_authentication)
+ {
+ handle_supported_hash_algorithms(this, notify);
+ }
+ break;
+ default:
+ /* other notifies are handled elsewhere */
+ break;
}
+
}
default:
break;
}
}
enumerator->destroy(enumerator);
+
+ if (ke_payload && this->proposal &&
+ this->proposal->has_dh_group(this->proposal, this->dh_group))
+ {
+ if (!this->initiator)
+ {
+ this->dh = this->keymat->keymat.create_dh(
+ &this->keymat->keymat, this->dh_group);
+ }
+ if (this->dh)
+ {
+ this->dh_failed = !this->dh->set_other_public_value(this->dh,
+ ke_payload->get_key_exchange_data(ke_payload));
+ }
+ }
}
METHOD(task_t, build_i, status_t,
@@ -305,7 +450,10 @@ METHOD(task_t, build_i, status_t,
message->add_notify(message, FALSE, COOKIE, this->cookie);
}
- build_payloads(this, message);
+ if (!build_payloads(this, message))
+ {
+ return FAILED;
+ }
#ifdef ME
{
@@ -433,13 +581,24 @@ METHOD(task_t, build_r, status_t,
return FAILED;
}
+ if (this->dh_failed)
+ {
+ DBG1(DBG_IKE, "applying DH public value failed");
+ message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
+ return FAILED;
+ }
+
if (!derive_keys(this, this->other_nonce, this->my_nonce))
{
DBG1(DBG_IKE, "key derivation failed");
message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
return FAILED;
}
- build_payloads(this, message);
+ if (!build_payloads(this, message))
+ {
+ message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
+ return FAILED;
+ }
return SUCCESS;
}
@@ -554,6 +713,12 @@ METHOD(task_t, process_i, status_t,
return FAILED;
}
+ if (this->dh_failed)
+ {
+ DBG1(DBG_IKE, "applying DH public value failed");
+ return FAILED;
+ }
+
if (!derive_keys(this, this->my_nonce, this->other_nonce))
{
DBG1(DBG_IKE, "key derivation failed");
@@ -577,6 +742,7 @@ METHOD(task_t, migrate, void,
this->ike_sa = ike_sa;
this->keymat = (keymat_v2_t*)ike_sa->get_keymat(ike_sa);
this->proposal = NULL;
+ this->dh_failed = FALSE;
if (this->dh && this->dh->get_dh_group(this->dh) != this->dh_group)
{ /* reset DH value only if group changed (INVALID_KE_PAYLOAD) */
this->dh->destroy(this->dh);
@@ -631,6 +797,8 @@ ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa)
.dh_group = MODP_NONE,
.keymat = (keymat_v2_t*)ike_sa->get_keymat(ike_sa),
.old_sa = old_sa,
+ .signature_authentication = lib->settings->get_bool(lib->settings,
+ "%s.signature_authentication", TRUE, lib->ns),
);
if (initiator)
diff --git a/src/libcharon/sa/ikev2/tasks/ike_mobike.c b/src/libcharon/sa/ikev2/tasks/ike_mobike.c
index d91fa5862..6295d7960 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_mobike.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_mobike.c
@@ -256,6 +256,7 @@ static void update_children(private_ike_mobike_t *this)
enumerator_t *enumerator;
child_sa_t *child_sa;
linked_list_t *vips;
+ status_t status;
host_t *host;
vips = linked_list_create();
@@ -270,15 +271,25 @@ static void update_children(private_ike_mobike_t *this)
enumerator = this->ike_sa->create_child_sa_enumerator(this->ike_sa);
while (enumerator->enumerate(enumerator, (void**)&child_sa))
{
- if (child_sa->update(child_sa,
- this->ike_sa->get_my_host(this->ike_sa),
- this->ike_sa->get_other_host(this->ike_sa), vips,
- this->ike_sa->has_condition(this->ike_sa,
- COND_NAT_ANY)) == NOT_SUPPORTED)
+ status = child_sa->update(child_sa,
+ this->ike_sa->get_my_host(this->ike_sa),
+ this->ike_sa->get_other_host(this->ike_sa), vips,
+ this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY));
+ switch (status)
{
- this->ike_sa->rekey_child_sa(this->ike_sa,
- child_sa->get_protocol(child_sa),
- child_sa->get_spi(child_sa, TRUE));
+ case NOT_SUPPORTED:
+ this->ike_sa->rekey_child_sa(this->ike_sa,
+ child_sa->get_protocol(child_sa),
+ child_sa->get_spi(child_sa, TRUE));
+ break;
+ case SUCCESS:
+ charon->child_sa_manager->remove(charon->child_sa_manager,
+ child_sa);
+ charon->child_sa_manager->add(charon->child_sa_manager,
+ child_sa, this->ike_sa);
+ break;
+ default:
+ break;
}
}
enumerator->destroy(enumerator);
diff --git a/src/libcharon/sa/ikev2/tasks/ike_reauth.h b/src/libcharon/sa/ikev2/tasks/ike_reauth.h
index 781b463a7..e2e48f0d4 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_reauth.h
+++ b/src/libcharon/sa/ikev2/tasks/ike_reauth.h
@@ -29,6 +29,8 @@ typedef struct ike_reauth_t ike_reauth_t;
/**
* Task of type ike_reauth, reestablishes an IKE_SA.
+ *
+ * This task implements break-before-make reauthentication.
*/
struct ike_reauth_t {
diff --git a/src/libcharon/sa/ikev2/tasks/ike_reauth_complete.c b/src/libcharon/sa/ikev2/tasks/ike_reauth_complete.c
new file mode 100644
index 000000000..a01489c03
--- /dev/null
+++ b/src/libcharon/sa/ikev2/tasks/ike_reauth_complete.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2014 Martin Willi
+ * Copyright (C) 2014 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "ike_reauth_complete.h"
+
+#include <daemon.h>
+#include <processing/jobs/delete_ike_sa_job.h>
+
+
+typedef struct private_ike_reauth_complete_t private_ike_reauth_complete_t;
+
+/**
+ * Private members of a ike_reauth_complete_t task.
+ */
+struct private_ike_reauth_complete_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ ike_reauth_complete_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Reauthenticated IKE_SA identifier
+ */
+ ike_sa_id_t *id;
+};
+
+METHOD(task_t, build_i, status_t,
+ private_ike_reauth_complete_t *this, message_t *message)
+{
+ message->set_exchange_type(message, EXCHANGE_TYPE_UNDEFINED);
+ lib->processor->queue_job(lib->processor,
+ (job_t*)delete_ike_sa_job_create(this->id, TRUE));
+ return SUCCESS;
+}
+
+METHOD(task_t, process_i, status_t,
+ private_ike_reauth_complete_t *this, message_t *message)
+{
+ return DESTROY_ME;
+}
+
+METHOD(task_t, get_type, task_type_t,
+ private_ike_reauth_complete_t *this)
+{
+ return TASK_IKE_REAUTH_COMPLETE;
+}
+
+METHOD(task_t, migrate, void,
+ private_ike_reauth_complete_t *this, ike_sa_t *ike_sa)
+{
+ this->ike_sa = ike_sa;
+}
+
+METHOD(task_t, destroy, void,
+ private_ike_reauth_complete_t *this)
+{
+ this->id->destroy(this->id);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_reauth_complete_t *ike_reauth_complete_create(ike_sa_t *ike_sa,
+ ike_sa_id_t *id)
+{
+ private_ike_reauth_complete_t *this;
+
+ INIT(this,
+ .public = {
+ .task = {
+ .get_type = _get_type,
+ .migrate = _migrate,
+ .build = _build_i,
+ .process = _process_i,
+ .destroy = _destroy,
+ },
+ },
+ .ike_sa = ike_sa,
+ .id = id->clone(id),
+ );
+
+ return &this->public;
+}
diff --git a/src/libcharon/sa/ikev2/tasks/ike_reauth_complete.h b/src/libcharon/sa/ikev2/tasks/ike_reauth_complete.h
new file mode 100644
index 000000000..cc3d3b713
--- /dev/null
+++ b/src/libcharon/sa/ikev2/tasks/ike_reauth_complete.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 Martin Willi
+ * Copyright (C) 2014 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup ike_reauth_complete ike_reauth_complete
+ * @{ @ingroup tasks_v2
+ */
+
+#ifndef IKE_REAUTH_COMPLETE_H_
+#define IKE_REAUTH_COMPLETE_H_
+
+typedef struct ike_reauth_complete_t ike_reauth_complete_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/task.h>
+
+/**
+ * Task of type IKE_REAUTH_COMPLETE, removes reauthenticated SA after reauth.
+ *
+ * This task completes make-before-break reauthentication by deleting the
+ * old, reauthenticated IKE_SA after the new one established.
+ */
+struct ike_reauth_complete_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * Create a new ike_reauth_complete task.
+ *
+ * This task is initiator only.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param id old, reauthenticated IKE_SA
+ * @return ike_reauth_complete task to handle by the task_manager
+ */
+ike_reauth_complete_t *ike_reauth_complete_create(ike_sa_t *ike_sa,
+ ike_sa_id_t *id);
+
+#endif /** IKE_REAUTH_COMPLETE_H_ @}*/
diff --git a/src/libcharon/sa/ikev2/tasks/ike_rekey.c b/src/libcharon/sa/ikev2/tasks/ike_rekey.c
index 444ac6ade..1855517ce 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_rekey.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_rekey.c
@@ -22,6 +22,7 @@
#include <sa/ikev2/tasks/ike_delete.h>
#include <processing/jobs/delete_ike_sa_job.h>
#include <processing/jobs/rekey_ike_sa_job.h>
+#include <processing/jobs/initiate_tasks_job.h>
typedef struct private_ike_rekey_t private_ike_rekey_t;
@@ -68,12 +69,33 @@ struct private_ike_rekey_t {
};
/**
+ * Check if an IKE_SA has any queued tasks, return initiation job
+ */
+static job_t* check_queued_tasks(ike_sa_t *ike_sa)
+{
+ enumerator_t *enumerator;
+ task_t *task;
+ job_t *job = NULL;
+
+ enumerator = ike_sa->create_task_enumerator(ike_sa, TASK_QUEUE_QUEUED);
+ if (enumerator->enumerate(enumerator, &task))
+ {
+ job = (job_t*)initiate_tasks_job_create(ike_sa->get_id(ike_sa));
+ }
+ enumerator->destroy(enumerator);
+
+ return job;
+}
+
+/**
* Establish the new replacement IKE_SA
*/
static void establish_new(private_ike_rekey_t *this)
{
if (this->new_sa)
{
+ job_t *job;
+
this->new_sa->set_state(this->new_sa, IKE_ESTABLISHED);
DBG0(DBG_IKE, "IKE_SA %s[%d] rekeyed between %H[%Y]...%H[%Y]",
this->new_sa->get_name(this->new_sa),
@@ -85,7 +107,14 @@ static void establish_new(private_ike_rekey_t *this)
this->new_sa->inherit_post(this->new_sa, this->ike_sa);
charon->bus->ike_rekey(charon->bus, this->ike_sa, this->new_sa);
+ job = check_queued_tasks(this->new_sa);
+ /* don't queue job before checkin(), as the IKE_SA is not yet
+ * registered at the manager */
charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa);
+ if (job)
+ {
+ lib->processor->queue_job(lib->processor, job);
+ }
this->new_sa = NULL;
/* set threads active IKE_SA after checkin */
charon->bus->set_sa(charon->bus, this->ike_sa);
@@ -163,6 +192,7 @@ METHOD(task_t, process_r, status_t,
{
case CHILD_CREATED:
case CHILD_REKEYING:
+ case CHILD_RETRYING:
case CHILD_DELETING:
/* we do not allow rekeying while we have children in-progress */
DBG1(DBG_IKE, "peer initiated rekeying, but a child is half-open");
@@ -209,6 +239,12 @@ METHOD(task_t, build_r, status_t,
this->public.task.build = _build_r_delete;
this->public.task.process = _process_r_delete;
+ /* the peer does have to delete the IKE_SA. If it does not, we get a
+ * unusable IKE_SA in REKEYING state without a replacement. We consider
+ * this a timeout condition by the peer, and trigger a delete actively. */
+ lib->scheduler->schedule_job(lib->scheduler, (job_t*)
+ delete_ike_sa_job_create(this->ike_sa->get_id(this->ike_sa), TRUE), 90);
+
return NEED_MORE;
}
diff --git a/src/libcharon/sa/task.c b/src/libcharon/sa/task.c
index 4336b23ff..b35b58185 100644
--- a/src/libcharon/sa/task.c
+++ b/src/libcharon/sa/task.c
@@ -27,6 +27,7 @@ ENUM(task_type_names, TASK_IKE_INIT, TASK_ISAKMP_CERT_POST,
"IKE_CONFIG",
"IKE_REKEY",
"IKE_REAUTH",
+ "IKE_REAUTH_COMPLETE",
"IKE_DELETE",
"IKE_DPD",
"IKE_VENDOR",
diff --git a/src/libcharon/sa/task.h b/src/libcharon/sa/task.h
index f2c4299cc..7bd3da1fe 100644
--- a/src/libcharon/sa/task.h
+++ b/src/libcharon/sa/task.h
@@ -22,6 +22,8 @@
#ifndef TASK_H_
#define TASK_H_
+#include <utils/utils.h>
+
typedef enum task_type_t task_type_t;
typedef struct task_t task_t;
@@ -51,8 +53,10 @@ enum task_type_t {
TASK_IKE_CONFIG,
/** rekey an IKE_SA */
TASK_IKE_REKEY,
- /** reestablish a complete IKE_SA */
+ /** reestablish a complete IKE_SA, break-before-make */
TASK_IKE_REAUTH,
+ /** completion task for make-before-break IKE_SA re-authentication */
+ TASK_IKE_REAUTH_COMPLETE,
/** delete an IKE_SA */
TASK_IKE_DELETE,
/** liveness check */
diff --git a/src/libcharon/sa/trap_manager.c b/src/libcharon/sa/trap_manager.c
index 7e55d6b0f..d6ff3c8c5 100644
--- a/src/libcharon/sa/trap_manager.c
+++ b/src/libcharon/sa/trap_manager.c
@@ -65,6 +65,11 @@ struct private_trap_manager_t {
* listener to track acquiring IKE_SAs
*/
trap_listener_t listener;
+
+ /**
+ * Whether to ignore traffic selectors from acquires
+ */
+ bool ignore_acquire_ts;
};
/**
@@ -171,7 +176,7 @@ METHOD(trap_manager_t, install, u_int32_t,
this->lock->unlock(this->lock);
/* create and route CHILD_SA */
- child_sa = child_sa_create(me, other, child, reqid, FALSE);
+ child_sa = child_sa_create(me, other, child, reqid, FALSE, 0, 0);
list = linked_list_create_with_items(me, NULL);
my_ts = child->get_traffic_selectors(child, TRUE, NULL, list);
@@ -353,7 +358,7 @@ METHOD(trap_manager_t, acquire, void,
{
ike_sa->set_peer_cfg(ike_sa, peer);
}
- if (ike_sa->get_version(ike_sa) == IKEV1)
+ if (this->ignore_acquire_ts || ike_sa->get_version(ike_sa) == IKEV1)
{ /* in IKEv1, don't prepend the acquiring packet TS, as we only
* have a single TS that we can establish in a Quick Mode. */
src = dst = NULL;
@@ -484,6 +489,8 @@ trap_manager_t *trap_manager_create(void)
},
.traps = linked_list_create(),
.lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
+ .ignore_acquire_ts = lib->settings->get_bool(lib->settings,
+ "%s.ignore_acquire_ts", FALSE, lib->ns),
);
charon->bus->add_listener(charon->bus, &this->listener.listener);