summaryrefslogtreecommitdiff
path: root/src/libcharon/sa
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcharon/sa')
-rw-r--r--src/libcharon/sa/child_sa.c19
-rw-r--r--src/libcharon/sa/ike_sa.c30
-rw-r--r--src/libcharon/sa/ike_sa_manager.c58
-rw-r--r--src/libcharon/sa/ikev1/keymat_v1.c8
-rw-r--r--src/libcharon/sa/ikev1/task_manager_v1.c54
-rw-r--r--src/libcharon/sa/ikev1/tasks/aggressive_mode.c67
-rw-r--r--src/libcharon/sa/ikev1/tasks/main_mode.c66
-rw-r--r--src/libcharon/sa/ikev1/tasks/mode_config.c269
-rw-r--r--src/libcharon/sa/ikev1/tasks/mode_config.h3
-rw-r--r--src/libcharon/sa/ikev1/tasks/quick_delete.c2
-rw-r--r--src/libcharon/sa/ikev1/tasks/quick_mode.c91
-rw-r--r--src/libcharon/sa/ikev1/tasks/xauth.c2
-rw-r--r--src/libcharon/sa/ikev2/task_manager_v2.c20
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_create.c18
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_delete.c7
-rw-r--r--src/libcharon/sa/keymat.c1
-rw-r--r--src/libcharon/sa/trap_manager.c42
-rw-r--r--src/libcharon/sa/xauth/xauth_manager.c17
-rw-r--r--src/libcharon/sa/xauth/xauth_manager.h6
-rw-r--r--src/libcharon/sa/xauth/xauth_method.h4
20 files changed, 606 insertions, 178 deletions
diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c
index 46e4b6f7b..9c3876a94 100644
--- a/src/libcharon/sa/child_sa.c
+++ b/src/libcharon/sa/child_sa.c
@@ -167,12 +167,12 @@ struct private_child_sa_t {
/**
* time of last use in seconds (inbound)
*/
- u_int32_t my_usetime;
+ time_t my_usetime;
/**
* time of last use in seconds (outbound)
*/
- u_int32_t other_usetime;
+ time_t other_usetime;
/**
* last number of inbound bytes
@@ -429,7 +429,7 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound)
{
status_t status = FAILED;
u_int64_t bytes, packets;
- u_int32_t time;
+ time_t time;
if (inbound)
{
@@ -489,12 +489,12 @@ static bool update_usetime(private_child_sa_t *this, bool inbound)
{
enumerator_t *enumerator;
traffic_selector_t *my_ts, *other_ts;
- u_int32_t last_use = 0;
+ time_t last_use = 0;
enumerator = create_policy_enumerator(this);
while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
{
- u_int32_t in, out, fwd;
+ time_t in, out, fwd;
if (inbound)
{
@@ -594,6 +594,9 @@ METHOD(child_sa_t, alloc_spi, u_int32_t,
proto_ike2ip(protocol), this->reqid,
&this->my_spi) == SUCCESS)
{
+ /* if we allocate a SPI, but then are unable to establish the SA, we
+ * need to know the protocol family to delete the partial SA */
+ this->protocol = protocol;
return this->my_spi;
}
return 0;
@@ -1039,12 +1042,6 @@ METHOD(child_sa_t, destroy, void,
/* delete SAs in the kernel, if they are set up */
if (this->my_spi)
{
- /* if CHILD was not established, use PROTO_ESP used during alloc_spi().
- * TODO: For AH support, we have to store protocol specific SPI.s */
- if (this->protocol == PROTO_NONE)
- {
- this->protocol = PROTO_ESP;
- }
hydra->kernel_interface->del_sa(hydra->kernel_interface,
this->other_addr, this->my_addr, this->my_spi,
proto_ike2ip(this->protocol), this->my_cpi,
diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c
index 2f4e1123c..028208782 100644
--- a/src/libcharon/sa/ike_sa.c
+++ b/src/libcharon/sa/ike_sa.c
@@ -1077,7 +1077,7 @@ METHOD(ike_sa_t, initiate_mediated, status_t,
static void resolve_hosts(private_ike_sa_t *this)
{
host_t *host;
- int family = 0;
+ int family = AF_UNSPEC;
switch (charon->socket->supported_families(charon->socket))
{
@@ -1099,12 +1099,7 @@ static void resolve_hosts(private_ike_sa_t *this)
}
else
{
- char *other_addr;
- u_int16_t other_port;
-
- other_addr = this->ike_cfg->get_other_addr(this->ike_cfg, NULL);
- other_port = this->ike_cfg->get_other_port(this->ike_cfg);
- host = host_create_from_dns(other_addr, family, other_port);
+ host = this->ike_cfg->resolve_other(this->ike_cfg, family);
}
if (host)
{
@@ -1118,17 +1113,12 @@ static void resolve_hosts(private_ike_sa_t *this)
}
else
{
- char *my_addr;
- u_int16_t my_port;
-
/* use same address family as for other */
if (!this->other_host->is_anyaddr(this->other_host))
{
family = this->other_host->get_family(this->other_host);
}
- my_addr = this->ike_cfg->get_my_addr(this->ike_cfg, NULL);
- my_port = this->ike_cfg->get_my_port(this->ike_cfg);
- host = host_create_from_dns(my_addr, family, my_port);
+ host = this->ike_cfg->resolve_me(this->ike_cfg, family);
if (host && host->is_anyaddr(host) &&
!this->other_host->is_anyaddr(this->other_host))
@@ -1142,7 +1132,7 @@ static void resolve_hosts(private_ike_sa_t *this)
}
else
{ /* fallback to address family specific %any(6), if configured */
- host = host_create_from_dns(my_addr, family, my_port);
+ host = this->ike_cfg->resolve_me(this->ike_cfg, family);
}
}
}
@@ -1172,8 +1162,14 @@ METHOD(ike_sa_t, initiate, status_t,
#endif /* ME */
)
{
- char *addr = this->ike_cfg->get_other_addr(this->ike_cfg, NULL);
- bool is_anyaddr = streq(addr, "%any") || streq(addr, "%any6");
+ bool is_anyaddr;
+ host_t *host;
+ char *addr;
+
+ addr = this->ike_cfg->get_my_addr(this->ike_cfg);
+ host = this->ike_cfg->resolve_other(this->ike_cfg, AF_UNSPEC);
+ is_anyaddr = host && host->is_anyaddr(host);
+ DESTROY_IF(host);
if (is_anyaddr || !this->retry_initiate_interval)
{
@@ -1659,6 +1655,8 @@ METHOD(ike_sa_t, reestablish, status_t,
new->set_other_host(new, host->clone(host));
host = this->my_host;
new->set_my_host(new, host->clone(host));
+ /* resolve hosts but use the old addresses above as fallback */
+ resolve_hosts((private_ike_sa_t*)new);
/* if we already have a virtual IP, we reuse it */
enumerator = array_create_enumerator(this->my_vips);
while (enumerator->enumerate(enumerator, &host))
diff --git a/src/libcharon/sa/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c
index 4fbc4da8e..5768803aa 100644
--- a/src/libcharon/sa/ike_sa_manager.c
+++ b/src/libcharon/sa/ike_sa_manager.c
@@ -28,6 +28,7 @@
#include <threading/rwlock.h>
#include <collections/linked_list.h>
#include <crypto/hashers/hasher.h>
+#include <processing/jobs/delete_ike_sa_job.h>
/* the default size of the hash table (MUST be a power of 2) */
#define DEFAULT_HASHTABLE_SIZE 1
@@ -1764,6 +1765,40 @@ static void adopt_children(ike_sa_t *old, ike_sa_t *new)
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));
+}
+
+/**
+ * Delete an existing IKE_SA due to a unique replace policy
+ */
+static status_t enforce_replace(private_ike_sa_manager_t *this,
+ ike_sa_t *duplicate, ike_sa_t *new,
+ identification_t *other, host_t *host)
+{
+ charon->bus->alert(charon->bus, ALERT_UNIQUE_REPLACE);
+
+ if (is_ikev1_reauth(duplicate, host))
+ {
+ /* looks like a reauthentication attempt */
+ adopt_children(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. */
+ lib->scheduler->schedule_job(lib->scheduler, (job_t*)
+ delete_ike_sa_job_create(duplicate->get_id(duplicate), TRUE), 10);
+ return SUCCESS;
+ }
+ DBG1(DBG_IKE, "deleting duplicate IKE_SA for peer '%Y' due to "
+ "uniqueness policy", other);
+ return duplicate->delete(duplicate);
+}
+
METHOD(ike_sa_manager_t, check_uniqueness, bool,
private_ike_sa_manager_t *this, ike_sa_t *ike_sa, bool force_replace)
{
@@ -1815,20 +1850,17 @@ METHOD(ike_sa_manager_t, check_uniqueness, bool,
switch (policy)
{
case UNIQUE_REPLACE:
- charon->bus->alert(charon->bus, ALERT_UNIQUE_REPLACE);
- if (duplicate->get_version(duplicate) == IKEV1)
- {
- adopt_children(duplicate, ike_sa);
- }
- DBG1(DBG_IKE, "deleting duplicate IKE_SA for peer "
- "'%Y' due to uniqueness policy", other);
- status = duplicate->delete(duplicate);
+ status = enforce_replace(this, duplicate, ike_sa,
+ other, other_host);
break;
case UNIQUE_KEEP:
- cancel = TRUE;
- /* we keep the first IKE_SA and delete all
- * other duplicates that might exist */
- policy = UNIQUE_REPLACE;
+ if (!is_ikev1_reauth(duplicate, other_host))
+ {
+ cancel = TRUE;
+ /* we keep the first IKE_SA and delete all
+ * other duplicates that might exist */
+ policy = UNIQUE_REPLACE;
+ }
break;
default:
break;
@@ -2101,7 +2133,7 @@ ike_sa_manager_t *ike_sa_manager_create()
},
);
- this->hasher = lib->crypto->create_hasher(lib->crypto, HASH_PREFERRED);
+ this->hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1);
if (this->hasher == NULL)
{
DBG1(DBG_MGR, "manager initialization failed, no hasher supported");
diff --git a/src/libcharon/sa/ikev1/keymat_v1.c b/src/libcharon/sa/ikev1/keymat_v1.c
index 39e4cad20..bf1b0046c 100644
--- a/src/libcharon/sa/ikev1/keymat_v1.c
+++ b/src/libcharon/sa/ikev1/keymat_v1.c
@@ -196,6 +196,13 @@ METHOD(aead_t, get_iv_size, size_t,
return 0;
}
+METHOD(aead_t, get_iv_gen, iv_gen_t*,
+ private_aead_t *this)
+{
+ /* IVs are retrieved via keymat_v1.get_iv() */
+ return NULL;
+}
+
METHOD(aead_t, get_key_size, size_t,
private_aead_t *this)
{
@@ -304,6 +311,7 @@ static aead_t *create_aead(proposal_t *proposal, prf_t *prf, chunk_t skeyid_e)
.get_block_size = _get_block_size,
.get_icv_size = _get_icv_size,
.get_iv_size = _get_iv_size,
+ .get_iv_gen = _get_iv_gen,
.get_key_size = _get_key_size,
.set_key = _set_key,
.destroy = _aead_destroy,
diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c
index 857cb027e..597416e36 100644
--- a/src/libcharon/sa/ikev1/task_manager_v1.c
+++ b/src/libcharon/sa/ikev1/task_manager_v1.c
@@ -413,7 +413,6 @@ static bool send_packet(private_task_manager_t *this, bool request,
{
bool use_frags = FALSE;
ike_cfg_t *ike_cfg;
- host_t *src, *dst;
chunk_t data;
ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
@@ -438,15 +437,18 @@ static bool send_packet(private_task_manager_t *this, bool request,
fragment_payload_t *fragment;
u_int8_t num, count;
size_t len, frag_size;
- bool nat;
-
- /* reduce size due to non-ESP marker */
- nat = this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY);
- frag_size = this->frag.size - (nat ? 4 : 0);
+ host_t *src, *dst;
src = packet->get_source(packet);
dst = packet->get_destination(packet);
- count = (data.len / (frag_size + 1)) + 1;
+
+ frag_size = this->frag.size;
+ if (dst->get_port(dst) != IKEV2_UDP_PORT &&
+ src->get_port(src) != IKEV2_UDP_PORT)
+ { /* reduce size due to non-ESP marker */
+ frag_size -= 4;
+ }
+ count = data.len / frag_size + (data.len % frag_size ? 1 : 0);
DBG1(DBG_IKE, "sending IKE message with length of %zu bytes in "
"%hhu fragments", data.len, count);
@@ -537,23 +539,40 @@ static bool mode_config_expected(private_task_manager_t *this)
enumerator_t *enumerator;
peer_cfg_t *peer_cfg;
char *pool;
+ bool local;
host_t *host;
peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
if (peer_cfg)
{
- enumerator = peer_cfg->create_pool_enumerator(peer_cfg);
- if (!enumerator->enumerate(enumerator, &pool))
- { /* no pool configured */
+ if (peer_cfg->use_pull_mode(peer_cfg))
+ {
+ enumerator = peer_cfg->create_pool_enumerator(peer_cfg);
+ if (!enumerator->enumerate(enumerator, &pool))
+ { /* no pool configured */
+ enumerator->destroy(enumerator);
+ return FALSE;
+ }
enumerator->destroy(enumerator);
- return FALSE;
+
+ local = FALSE;
}
- enumerator->destroy(enumerator);
+ else
+ {
+ enumerator = peer_cfg->create_virtual_ip_enumerator(peer_cfg);
+ if (!enumerator->enumerate(enumerator, &host))
+ { /* not requesting a vip */
+ enumerator->destroy(enumerator);
+ return FALSE;
+ }
+ enumerator->destroy(enumerator);
+ local = TRUE;
+ }
enumerator = this->ike_sa->create_virtual_ip_enumerator(this->ike_sa,
- FALSE);
+ local);
if (!enumerator->enumerate(enumerator, &host))
- { /* have a pool, but no VIP assigned yet */
+ { /* expecting a VIP exchange, but no VIP assigned yet */
enumerator->destroy(enumerator);
return TRUE;
}
@@ -1085,7 +1104,8 @@ static status_t process_request(private_task_manager_t *this,
case TRANSACTION:
if (this->ike_sa->get_state(this->ike_sa) != IKE_CONNECTING)
{
- task = (task_t *)mode_config_create(this->ike_sa, FALSE);
+ task = (task_t *)mode_config_create(this->ike_sa,
+ FALSE, TRUE);
}
else
{
@@ -1253,7 +1273,7 @@ static status_t handle_fragment(private_task_manager_t *this, message_t *msg)
return FAILED;
}
- if (this->frag.id != payload->get_id(payload))
+ if (!this->frag.list || this->frag.id != payload->get_id(payload))
{
clear_fragments(this, payload->get_id(payload));
this->frag.list = linked_list_create();
@@ -1765,7 +1785,7 @@ static bool have_equal_ts(child_sa_t *child1, child_sa_t *child2, bool local)
{
equal = ts1->equals(ts1, ts2);
}
- e1->destroy(e1);
+ e2->destroy(e2);
e1->destroy(e1);
return equal;
diff --git a/src/libcharon/sa/ikev1/tasks/aggressive_mode.c b/src/libcharon/sa/ikev1/tasks/aggressive_mode.c
index 6b00706bf..46cbb879b 100644
--- a/src/libcharon/sa/ikev1/tasks/aggressive_mode.c
+++ b/src/libcharon/sa/ikev1/tasks/aggressive_mode.c
@@ -196,6 +196,17 @@ static status_t send_delete(private_aggressive_mode_t *this)
return ALREADY_DONE;
}
+/**
+ * Schedule a timeout for the IKE_SA should it not establish
+ */
+static void schedule_timeout(ike_sa_t *ike_sa)
+{
+ job_t *job;
+
+ job = (job_t*)delete_ike_sa_job_create(ike_sa->get_id(ike_sa), FALSE);
+ lib->scheduler->schedule_job(lib->scheduler, job, HALF_OPEN_IKE_SA_TIMEOUT);
+}
+
METHOD(task_t, build_i, status_t,
private_aggressive_mode_t *this, message_t *message)
{
@@ -300,20 +311,15 @@ METHOD(task_t, build_i, status_t,
case AUTH_XAUTH_INIT_PSK:
case AUTH_XAUTH_INIT_RSA:
case AUTH_HYBRID_INIT_RSA:
- { /* wait for XAUTH request, since this may never come,
- * we queue a timeout */
- job_t *job = (job_t*)delete_ike_sa_job_create(
- this->ike_sa->get_id(this->ike_sa), FALSE);
- lib->scheduler->schedule_job(lib->scheduler, job,
- HALF_OPEN_IKE_SA_TIMEOUT);
+ /* wait for XAUTH request */
+ schedule_timeout(this->ike_sa);
break;
- }
case AUTH_XAUTH_RESP_PSK:
case AUTH_XAUTH_RESP_RSA:
case AUTH_HYBRID_RESP_RSA:
this->ike_sa->queue_task(this->ike_sa,
(task_t*)xauth_create(this->ike_sa, TRUE));
- return SUCCESS;
+ break;
default:
if (charon->ike_sa_manager->check_uniqueness(
charon->ike_sa_manager, this->ike_sa, FALSE))
@@ -328,10 +334,30 @@ METHOD(task_t, build_i, status_t,
}
break;
}
+ /* check for and prepare mode config push/pull */
if (this->ph1->has_virtual_ip(this->ph1, this->peer_cfg))
{
- this->ike_sa->queue_task(this->ike_sa,
- (task_t*)mode_config_create(this->ike_sa, TRUE));
+ if (this->peer_cfg->use_pull_mode(this->peer_cfg))
+ {
+ this->ike_sa->queue_task(this->ike_sa,
+ (task_t*)mode_config_create(this->ike_sa, TRUE, TRUE));
+ }
+ else
+ {
+ schedule_timeout(this->ike_sa);
+ }
+ }
+ else if (this->ph1->has_pool(this->ph1, this->peer_cfg))
+ {
+ if (this->peer_cfg->use_pull_mode(this->peer_cfg))
+ {
+ schedule_timeout(this->ike_sa);
+ }
+ else
+ {
+ this->ike_sa->queue_task(this->ike_sa,
+ (task_t*)mode_config_create(this->ike_sa, TRUE, FALSE));
+ }
}
return SUCCESS;
}
@@ -482,7 +508,7 @@ METHOD(task_t, process_r, status_t,
case AUTH_HYBRID_INIT_RSA:
this->ike_sa->queue_task(this->ike_sa,
(task_t*)xauth_create(this->ike_sa, TRUE));
- return SUCCESS;
+ break;
case AUTH_XAUTH_RESP_PSK:
case AUTH_XAUTH_RESP_RSA:
case AUTH_HYBRID_RESP_RSA:
@@ -505,11 +531,22 @@ METHOD(task_t, process_r, status_t,
this->ike_sa->get_id(this->ike_sa)));
break;
}
- if (!this->ph1->has_pool(this->ph1, this->peer_cfg) &&
- this->ph1->has_virtual_ip(this->ph1, this->peer_cfg))
+ /* check for and prepare mode config push/pull */
+ if (this->ph1->has_virtual_ip(this->ph1, this->peer_cfg))
{
- this->ike_sa->queue_task(this->ike_sa,
- (task_t*)mode_config_create(this->ike_sa, TRUE));
+ if (this->peer_cfg->use_pull_mode(this->peer_cfg))
+ {
+ this->ike_sa->queue_task(this->ike_sa,
+ (task_t*)mode_config_create(this->ike_sa, TRUE, TRUE));
+ }
+ }
+ else if (this->ph1->has_pool(this->ph1, this->peer_cfg))
+ {
+ if (!this->peer_cfg->use_pull_mode(this->peer_cfg))
+ {
+ this->ike_sa->queue_task(this->ike_sa,
+ (task_t*)mode_config_create(this->ike_sa, TRUE, FALSE));
+ }
}
return SUCCESS;
}
diff --git a/src/libcharon/sa/ikev1/tasks/main_mode.c b/src/libcharon/sa/ikev1/tasks/main_mode.c
index 441bd7a78..81638169a 100644
--- a/src/libcharon/sa/ikev1/tasks/main_mode.c
+++ b/src/libcharon/sa/ikev1/tasks/main_mode.c
@@ -504,7 +504,7 @@ METHOD(task_t, build_r, status_t,
case AUTH_HYBRID_INIT_RSA:
this->ike_sa->queue_task(this->ike_sa,
(task_t*)xauth_create(this->ike_sa, TRUE));
- return SUCCESS;
+ break;
case AUTH_XAUTH_RESP_PSK:
case AUTH_XAUTH_RESP_RSA:
case AUTH_HYBRID_RESP_RSA:
@@ -527,11 +527,21 @@ METHOD(task_t, build_r, status_t,
this->ike_sa->get_id(this->ike_sa)));
break;
}
- if (!this->ph1->has_pool(this->ph1, this->peer_cfg) &&
- this->ph1->has_virtual_ip(this->ph1, this->peer_cfg))
+ if (this->ph1->has_virtual_ip(this->ph1, this->peer_cfg))
+ {
+ if (this->peer_cfg->use_pull_mode(this->peer_cfg))
+ {
+ this->ike_sa->queue_task(this->ike_sa,
+ (task_t*)mode_config_create(this->ike_sa, TRUE, TRUE));
+ }
+ }
+ else if (this->ph1->has_pool(this->ph1, this->peer_cfg))
{
- this->ike_sa->queue_task(this->ike_sa,
- (task_t*)mode_config_create(this->ike_sa, TRUE));
+ if (!this->peer_cfg->use_pull_mode(this->peer_cfg))
+ {
+ this->ike_sa->queue_task(this->ike_sa,
+ (task_t*)mode_config_create(this->ike_sa, TRUE, FALSE));
+ }
}
return SUCCESS;
}
@@ -540,6 +550,17 @@ METHOD(task_t, build_r, status_t,
}
}
+/**
+ * Schedule a timeout for the IKE_SA should it not establish
+ */
+static void schedule_timeout(ike_sa_t *ike_sa)
+{
+ job_t *job;
+
+ job = (job_t*)delete_ike_sa_job_create(ike_sa->get_id(ike_sa), FALSE);
+ lib->scheduler->schedule_job(lib->scheduler, job, HALF_OPEN_IKE_SA_TIMEOUT);
+}
+
METHOD(task_t, process_i, status_t,
private_main_mode_t *this, message_t *message)
{
@@ -639,20 +660,15 @@ METHOD(task_t, process_i, status_t,
case AUTH_XAUTH_INIT_PSK:
case AUTH_XAUTH_INIT_RSA:
case AUTH_HYBRID_INIT_RSA:
- { /* wait for XAUTH request, since this may never come,
- * we queue a timeout */
- job_t *job = (job_t*)delete_ike_sa_job_create(
- this->ike_sa->get_id(this->ike_sa), FALSE);
- lib->scheduler->schedule_job(lib->scheduler, job,
- HALF_OPEN_IKE_SA_TIMEOUT);
+ /* wait for XAUTH request */
+ schedule_timeout(this->ike_sa);
break;
- }
case AUTH_XAUTH_RESP_PSK:
case AUTH_XAUTH_RESP_RSA:
case AUTH_HYBRID_RESP_RSA:
this->ike_sa->queue_task(this->ike_sa,
(task_t*)xauth_create(this->ike_sa, TRUE));
- return SUCCESS;
+ break;
default:
if (charon->ike_sa_manager->check_uniqueness(
charon->ike_sa_manager, this->ike_sa, FALSE))
@@ -667,10 +683,30 @@ METHOD(task_t, process_i, status_t,
}
break;
}
+ /* check for and prepare mode config push/pull */
if (this->ph1->has_virtual_ip(this->ph1, this->peer_cfg))
{
- this->ike_sa->queue_task(this->ike_sa,
- (task_t*)mode_config_create(this->ike_sa, TRUE));
+ if (this->peer_cfg->use_pull_mode(this->peer_cfg))
+ {
+ this->ike_sa->queue_task(this->ike_sa,
+ (task_t*)mode_config_create(this->ike_sa, TRUE, TRUE));
+ }
+ else
+ {
+ schedule_timeout(this->ike_sa);
+ }
+ }
+ else if (this->ph1->has_pool(this->ph1, this->peer_cfg))
+ {
+ if (this->peer_cfg->use_pull_mode(this->peer_cfg))
+ {
+ schedule_timeout(this->ike_sa);
+ }
+ else
+ {
+ this->ike_sa->queue_task(this->ike_sa,
+ (task_t*)mode_config_create(this->ike_sa, TRUE, FALSE));
+ }
}
return SUCCESS;
}
diff --git a/src/libcharon/sa/ikev1/tasks/mode_config.c b/src/libcharon/sa/ikev1/tasks/mode_config.c
index ce897727a..17fe02538 100644
--- a/src/libcharon/sa/ikev1/tasks/mode_config.c
+++ b/src/libcharon/sa/ikev1/tasks/mode_config.c
@@ -42,14 +42,19 @@ struct private_mode_config_t {
bool initiator;
/**
+ * Use pull (CFG_REQUEST/RESPONSE) or push (CFG_SET/ACK)?
+ */
+ bool pull;
+
+ /**
* Received list of virtual IPs, host_t*
*/
linked_list_t *vips;
/**
- * list of attributes requested and its handler, entry_t
+ * Requested/received list of attributes, entry_t
*/
- linked_list_t *requested;
+ linked_list_t *attributes;
/**
* Identifier to include in response
@@ -58,12 +63,12 @@ struct private_mode_config_t {
};
/**
- * Entry for a requested attribute and the requesting handler
+ * Entry for a attribute and associated handler
*/
typedef struct {
- /** attribute requested */
+ /** attribute type */
configuration_attribute_type_t type;
- /** handler requesting this attribute */
+ /** handler for this attribute */
attribute_handler_t *handler;
} entry_t;
@@ -117,13 +122,13 @@ static void handle_attribute(private_mode_config_t *this,
entry_t *entry;
/* find the handler which requested this attribute */
- enumerator = this->requested->create_enumerator(this->requested);
+ enumerator = this->attributes->create_enumerator(this->attributes);
while (enumerator->enumerate(enumerator, &entry))
{
if (entry->type == ca->get_type(ca))
{
handler = entry->handler;
- this->requested->remove_at(this->requested, enumerator);
+ this->attributes->remove_at(this->attributes, enumerator);
free(entry);
break;
}
@@ -180,7 +185,7 @@ static void process_attribute(private_mode_config_t *this,
}
default:
{
- if (this->initiator)
+ if (this->initiator == this->pull)
{
handle_attribute(this, ca);
}
@@ -189,6 +194,24 @@ static void process_attribute(private_mode_config_t *this,
}
/**
+ * Check if config allows push mode when acting as task responder
+ */
+static bool accept_push(private_mode_config_t *this)
+{
+ enumerator_t *enumerator;
+ peer_cfg_t *config;
+ bool vip;
+ host_t *host;
+
+ config = this->ike_sa->get_peer_cfg(this->ike_sa);
+ enumerator = config->create_virtual_ip_enumerator(config);
+ vip = enumerator->enumerate(enumerator, &host);
+ enumerator->destroy(enumerator);
+
+ return vip && !config->use_pull_mode(config);
+}
+
+/**
* Scan for configuration payloads and attributes
*/
static void process_payloads(private_mode_config_t *this, message_t *message)
@@ -206,6 +229,15 @@ static void process_payloads(private_mode_config_t *this, message_t *message)
switch (cp->get_type(cp))
{
+ case CFG_SET:
+ /* when acting as a responder, we detect the mode using
+ * the type of configuration payload. But we should double
+ * check the peer is allowed to use push mode on us. */
+ if (!this->initiator && accept_push(this))
+ {
+ this->pull = FALSE;
+ }
+ /* FALL */
case CFG_REQUEST:
this->identifier = cp->get_identifier(cp);
/* FALL */
@@ -219,6 +251,8 @@ static void process_payloads(private_mode_config_t *this, message_t *message)
}
attributes->destroy(attributes);
break;
+ case CFG_ACK:
+ break;
default:
DBG1(DBG_IKE, "ignoring %N config payload",
config_type_names, cp->get_type(cp));
@@ -229,8 +263,29 @@ static void process_payloads(private_mode_config_t *this, message_t *message)
enumerator->destroy(enumerator);
}
-METHOD(task_t, build_i, status_t,
- private_mode_config_t *this, message_t *message)
+/**
+ * Add an attribute to a configuration payload, and store it in task
+ */
+static void add_attribute(private_mode_config_t *this, cp_payload_t *cp,
+ configuration_attribute_type_t type, chunk_t data,
+ attribute_handler_t *handler)
+{
+ entry_t *entry;
+
+ cp->add_attribute(cp,
+ configuration_attribute_create_chunk(CONFIGURATION_ATTRIBUTE_V1,
+ type, data));
+ INIT(entry,
+ .type = type,
+ .handler = handler,
+ );
+ this->attributes->insert_last(this->attributes, entry);
+}
+
+/**
+ * Build a CFG_REQUEST as initiator
+ */
+static status_t build_request(private_mode_config_t *this, message_t *message)
{
cp_payload_t *cp;
enumerator_t *enumerator;
@@ -279,18 +334,7 @@ METHOD(task_t, build_i, status_t,
this->ike_sa->get_other_id(this->ike_sa), vips);
while (enumerator->enumerate(enumerator, &handler, &type, &data))
{
- entry_t *entry;
-
- DBG2(DBG_IKE, "building %N attribute",
- configuration_attribute_type_names, type);
- cp->add_attribute(cp,
- configuration_attribute_create_chunk(CONFIGURATION_ATTRIBUTE_V1,
- type, data));
- INIT(entry,
- .type = type,
- .handler = handler,
- );
- this->requested->insert_last(this->requested, entry);
+ add_attribute(this, cp, type, data, handler);
}
enumerator->destroy(enumerator);
@@ -301,15 +345,121 @@ METHOD(task_t, build_i, status_t,
return NEED_MORE;
}
+/**
+ * Build a CFG_SET as initiator
+ */
+static status_t build_set(private_mode_config_t *this, message_t *message)
+{
+ enumerator_t *enumerator;
+ configuration_attribute_type_t type;
+ chunk_t value;
+ cp_payload_t *cp;
+ peer_cfg_t *config;
+ identification_t *id;
+ linked_list_t *pools;
+ host_t *any4, *any6, *found;
+ char *name;
+
+ cp = cp_payload_create_type(CONFIGURATION_V1, CFG_SET);
+
+ 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);
+
+ 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))
+ {
+ 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)
+ {
+ 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);
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ any4->destroy(any4);
+ any6->destroy(any6);
+
+ /* 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);
+ while (enumerator->enumerate(enumerator, &type, &value))
+ {
+ add_attribute(this, cp, type, value, NULL);
+ }
+ enumerator->destroy(enumerator);
+ pools->destroy(pools);
+
+ message->add_payload(message, (payload_t*)cp);
+
+ return SUCCESS;
+}
+
+METHOD(task_t, build_i, status_t,
+ private_mode_config_t *this, message_t *message)
+{
+ if (this->pull)
+ {
+ return build_request(this, message);
+ }
+ return build_set(this, message);
+}
+
+/**
+ * Store received virtual IPs to the IKE_SA, install them
+ */
+static void install_vips(private_mode_config_t *this)
+{
+ enumerator_t *enumerator;
+ host_t *host;
+
+ this->ike_sa->clear_virtual_ips(this->ike_sa, TRUE);
+
+ enumerator = this->vips->create_enumerator(this->vips);
+ while (enumerator->enumerate(enumerator, &host))
+ {
+ if (!host->is_anyaddr(host))
+ {
+ this->ike_sa->add_virtual_ip(this->ike_sa, TRUE, host);
+ }
+ }
+ enumerator->destroy(enumerator);
+}
+
METHOD(task_t, process_r, status_t,
private_mode_config_t *this, message_t *message)
{
process_payloads(this, message);
+
+ if (!this->pull)
+ {
+ install_vips(this);
+ }
return NEED_MORE;
}
-METHOD(task_t, build_r, status_t,
- private_mode_config_t *this, message_t *message)
+/**
+ * Build CFG_REPLY message after receiving CFG_REQUEST
+ */
+static status_t build_reply(private_mode_config_t *this, message_t *message)
{
enumerator_t *enumerator;
configuration_attribute_type_t type;
@@ -360,8 +510,6 @@ METHOD(task_t, build_r, status_t,
hydra->attributes, pools, id, vips);
while (enumerator->enumerate(enumerator, &type, &value))
{
- DBG2(DBG_IKE, "building %N attribute",
- configuration_attribute_type_names, type);
cp->add_attribute(cp,
configuration_attribute_create_chunk(CONFIGURATION_ATTRIBUTE_V1,
type, value));
@@ -376,26 +524,72 @@ METHOD(task_t, build_r, status_t,
return SUCCESS;
}
-METHOD(task_t, process_i, status_t,
- private_mode_config_t *this, message_t *message)
+/**
+ * Build CFG_ACK for a received CFG_SET
+ */
+static status_t build_ack(private_mode_config_t *this, message_t *message)
{
+ cp_payload_t *cp;
enumerator_t *enumerator;
host_t *host;
+ configuration_attribute_type_t type;
+ entry_t *entry;
- process_payloads(this, message);
+ cp = cp_payload_create_type(CONFIGURATION_V1, CFG_ACK);
- this->ike_sa->clear_virtual_ips(this->ike_sa, TRUE);
+ /* return empty attributes for installed IPs */
enumerator = this->vips->create_enumerator(this->vips);
while (enumerator->enumerate(enumerator, &host))
{
- if (!host->is_anyaddr(host))
+ type = INTERNAL_IP6_ADDRESS;
+ if (host->get_family(host) == AF_INET6)
{
- this->ike_sa->add_virtual_ip(this->ike_sa, TRUE, host);
+ type = INTERNAL_IP6_ADDRESS;
}
+ else
+ {
+ type = INTERNAL_IP4_ADDRESS;
+ }
+ cp->add_attribute(cp, configuration_attribute_create_chunk(
+ CONFIGURATION_ATTRIBUTE_V1, type, chunk_empty));
}
enumerator->destroy(enumerator);
+ enumerator = this->attributes->create_enumerator(this->attributes);
+ while (enumerator->enumerate(enumerator, &entry))
+ {
+ cp->add_attribute(cp,
+ configuration_attribute_create_chunk(CONFIGURATION_ATTRIBUTE_V1,
+ entry->type, chunk_empty));
+ }
+ enumerator->destroy(enumerator);
+
+ cp->set_identifier(cp, this->identifier);
+ message->add_payload(message, (payload_t*)cp);
+
+ return SUCCESS;
+}
+
+METHOD(task_t, build_r, status_t,
+ private_mode_config_t *this, message_t *message)
+{
+ if (this->pull)
+ {
+ return build_reply(this, message);
+ }
+ return build_ack(this, message);
+}
+
+METHOD(task_t, process_i, status_t,
+ private_mode_config_t *this, message_t *message)
+{
+ process_payloads(this, message);
+
+ if (this->pull)
+ {
+ install_vips(this);
+ }
return SUCCESS;
}
@@ -411,22 +605,22 @@ METHOD(task_t, migrate, void,
this->ike_sa = ike_sa;
this->vips->destroy_offset(this->vips, offsetof(host_t, destroy));
this->vips = linked_list_create();
- this->requested->destroy_function(this->requested, free);
- this->requested = linked_list_create();
+ this->attributes->destroy_function(this->attributes, free);
+ this->attributes = linked_list_create();
}
METHOD(task_t, destroy, void,
private_mode_config_t *this)
{
this->vips->destroy_offset(this->vips, offsetof(host_t, destroy));
- this->requested->destroy_function(this->requested, free);
+ this->attributes->destroy_function(this->attributes, free);
free(this);
}
/*
* Described in header.
*/
-mode_config_t *mode_config_create(ike_sa_t *ike_sa, bool initiator)
+mode_config_t *mode_config_create(ike_sa_t *ike_sa, bool initiator, bool pull)
{
private_mode_config_t *this;
@@ -439,8 +633,9 @@ mode_config_t *mode_config_create(ike_sa_t *ike_sa, bool initiator)
},
},
.initiator = initiator,
+ .pull = initiator ? pull : TRUE,
.ike_sa = ike_sa,
- .requested = linked_list_create(),
+ .attributes = linked_list_create(),
.vips = linked_list_create(),
);
diff --git a/src/libcharon/sa/ikev1/tasks/mode_config.h b/src/libcharon/sa/ikev1/tasks/mode_config.h
index 462bee374..c2da7a086 100644
--- a/src/libcharon/sa/ikev1/tasks/mode_config.h
+++ b/src/libcharon/sa/ikev1/tasks/mode_config.h
@@ -43,8 +43,9 @@ struct mode_config_t {
*
* @param ike_sa IKE_SA this task works for
* @param initiator TRUE for initiator
+ * @param pull TRUE to pull, FALSE to push (applies if initiator only)
* @return mode_config task to handle by the task_manager
*/
-mode_config_t *mode_config_create(ike_sa_t *ike_sa, bool initiator);
+mode_config_t *mode_config_create(ike_sa_t *ike_sa, bool initiator, bool pull);
#endif /** MODE_CONFIG_H_ @}*/
diff --git a/src/libcharon/sa/ikev1/tasks/quick_delete.c b/src/libcharon/sa/ikev1/tasks/quick_delete.c
index 1a2cdb777..605c10cea 100644
--- a/src/libcharon/sa/ikev1/tasks/quick_delete.c
+++ b/src/libcharon/sa/ikev1/tasks/quick_delete.c
@@ -177,7 +177,7 @@ METHOD(task_t, build_i, status_t,
DBG1(DBG_IKE, "sending DELETE for %N CHILD_SA with SPI %.8x",
protocol_id_names, this->protocol, ntohl(this->spi));
- delete_payload = delete_payload_create(DELETE_V1, PROTO_ESP);
+ delete_payload = delete_payload_create(DELETE_V1, this->protocol);
delete_payload->add_spi(delete_payload, this->spi);
message->add_payload(message, &delete_payload->payload_interface);
diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c
index 6271e5b05..12ee594b9 100644
--- a/src/libcharon/sa/ikev1/tasks/quick_mode.c
+++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c
@@ -165,6 +165,11 @@ struct private_quick_mode_t {
*/
ipsec_mode_t mode;
+ /*
+ * SA protocol (ESP|AH) negotiated
+ */
+ protocol_id_t proto;
+
/**
* Use UDP encapsulation
*/
@@ -722,7 +727,7 @@ static status_t send_notify(private_quick_mode_t *this, notify_type_t type)
notify_payload_t *notify;
notify = notify_payload_create_from_protocol_and_type(NOTIFY_V1,
- PROTO_ESP, type);
+ this->proto, type);
notify->set_spi(notify, this->spi_i);
this->ike_sa->queue_task(this->ike_sa,
@@ -733,6 +738,38 @@ static status_t send_notify(private_quick_mode_t *this, notify_type_t type)
return ALREADY_DONE;
}
+/**
+ * Prepare a list of proposals from child_config containing only the specified
+ * DH group, unless it is set to MODP_NONE.
+ */
+static linked_list_t *get_proposals(private_quick_mode_t *this,
+ diffie_hellman_group_t group)
+{
+ linked_list_t *list;
+ proposal_t *proposal;
+ enumerator_t *enumerator;
+
+ list = this->config->get_proposals(this->config, FALSE);
+ enumerator = list->create_enumerator(list);
+ while (enumerator->enumerate(enumerator, &proposal))
+ {
+ if (group != MODP_NONE)
+ {
+ if (!proposal->has_dh_group(proposal, group))
+ {
+ list->remove_at(list, enumerator);
+ proposal->destroy(proposal);
+ continue;
+ }
+ proposal->strip_dh(proposal, group);
+ }
+ proposal->set_spi(proposal, this->spi_i);
+ }
+ enumerator->destroy(enumerator);
+
+ return list;
+}
+
METHOD(task_t, build_i, status_t,
private_quick_mode_t *this, message_t *message)
{
@@ -740,7 +777,6 @@ METHOD(task_t, build_i, status_t,
{
case QM_INIT:
{
- enumerator_t *enumerator;
sa_payload_t *sa_payload;
linked_list_t *list, *tsi, *tsr;
proposal_t *proposal;
@@ -770,42 +806,55 @@ METHOD(task_t, build_i, status_t,
}
}
- this->spi_i = this->child_sa->alloc_spi(this->child_sa, PROTO_ESP);
+ list = this->config->get_proposals(this->config, MODP_NONE);
+ if (list->get_first(list, (void**)&proposal) == SUCCESS)
+ {
+ this->proto = proposal->get_protocol(proposal);
+ }
+ list->destroy_offset(list, offsetof(proposal_t, destroy));
+ this->spi_i = this->child_sa->alloc_spi(this->child_sa, this->proto);
if (!this->spi_i)
{
DBG1(DBG_IKE, "allocating SPI from kernel failed");
return FAILED;
}
+
group = this->config->get_dh_group(this->config);
if (group != MODP_NONE)
{
+ proposal_t *proposal;
+ u_int16_t preferred_group;
+
+ proposal = this->ike_sa->get_proposal(this->ike_sa);
+ proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP,
+ &preferred_group, NULL);
+ /* try the negotiated DH group from IKE_SA */
+ list = get_proposals(this, preferred_group);
+ if (list->get_count(list))
+ {
+ group = preferred_group;
+ }
+ else
+ {
+ /* fall back to the first configured DH group */
+ list->destroy(list);
+ list = get_proposals(this, group);
+ }
+
this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat,
group);
if (!this->dh)
{
DBG1(DBG_IKE, "configured DH group %N not supported",
diffie_hellman_group_names, group);
+ list->destroy_offset(list, offsetof(proposal_t, destroy));
return FAILED;
}
}
-
- list = this->config->get_proposals(this->config, FALSE);
- enumerator = list->create_enumerator(list);
- while (enumerator->enumerate(enumerator, &proposal))
+ else
{
- if (group != MODP_NONE)
- {
- if (!proposal->has_dh_group(proposal, group))
- {
- list->remove_at(list, enumerator);
- proposal->destroy(proposal);
- continue;
- }
- proposal->strip_dh(proposal, group);
- }
- proposal->set_spi(proposal, this->spi_i);
+ list = get_proposals(this, MODP_NONE);
}
- enumerator->destroy(enumerator);
get_lifetimes(this);
encap = get_encap(this->ike_sa, this->udp);
@@ -1103,7 +1152,8 @@ METHOD(task_t, build_r, status_t,
sa_payload_t *sa_payload;
encap_t encap;
- this->spi_r = this->child_sa->alloc_spi(this->child_sa, PROTO_ESP);
+ this->proto = this->proposal->get_protocol(this->proposal);
+ this->spi_r = this->child_sa->alloc_spi(this->child_sa, this->proto);
if (!this->spi_r)
{
DBG1(DBG_IKE, "allocating SPI from kernel failed");
@@ -1311,6 +1361,7 @@ quick_mode_t *quick_mode_create(ike_sa_t *ike_sa, child_cfg_t *config,
.state = QM_INIT,
.tsi = tsi ? tsi->clone(tsi) : NULL,
.tsr = tsr ? tsr->clone(tsr) : NULL,
+ .proto = PROTO_ESP,
);
if (config)
diff --git a/src/libcharon/sa/ikev1/tasks/xauth.c b/src/libcharon/sa/ikev1/tasks/xauth.c
index 31114e592..f5555ecd2 100644
--- a/src/libcharon/sa/ikev1/tasks/xauth.c
+++ b/src/libcharon/sa/ikev1/tasks/xauth.c
@@ -127,7 +127,7 @@ static xauth_method_t *load_method(private_xauth_t* this)
{
if (name)
{
- DBG1(DBG_CFG, "no XAuth method found named '%s'", name);
+ DBG1(DBG_CFG, "no XAuth method found for '%s'", name);
}
else
{
diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c
index a6af744fc..8e6da1609 100644
--- a/src/libcharon/sa/ikev2/task_manager_v2.c
+++ b/src/libcharon/sa/ikev2/task_manager_v2.c
@@ -1145,14 +1145,9 @@ METHOD(task_manager_t, process_message, status_t,
return FAILED;
}
}
- if (this->ike_sa->get_state(this->ike_sa) == IKE_CREATED ||
- this->ike_sa->get_state(this->ike_sa) == IKE_CONNECTING ||
- msg->get_exchange_type(msg) != IKE_SA_INIT)
- { /* only do host updates based on verified messages */
- if (!this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE))
- { /* with MOBIKE, we do no implicit updates */
- this->ike_sa->update_hosts(this->ike_sa, me, other, mid == 1);
- }
+ if (!this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE))
+ { /* with MOBIKE, we do no implicit updates */
+ this->ike_sa->update_hosts(this->ike_sa, me, other, mid == 1);
}
charon->bus->message(charon->bus, msg, TRUE, TRUE);
if (msg->get_exchange_type(msg) == EXCHANGE_TYPE_UNDEFINED)
@@ -1198,10 +1193,13 @@ METHOD(task_manager_t, process_message, status_t,
if (this->ike_sa->get_state(this->ike_sa) == IKE_CREATED ||
this->ike_sa->get_state(this->ike_sa) == IKE_CONNECTING ||
msg->get_exchange_type(msg) != IKE_SA_INIT)
- { /* only do host updates based on verified messages */
+ { /* only do updates based on verified messages (or initial ones) */
if (!this->ike_sa->supports_extension(this->ike_sa, EXT_MOBIKE))
- { /* with MOBIKE, we do no implicit updates */
- this->ike_sa->update_hosts(this->ike_sa, me, other, FALSE);
+ { /* with MOBIKE, we do no implicit updates. we force an
+ * update of the local address on IKE_SA_INIT, but never
+ * for the remote address */
+ this->ike_sa->update_hosts(this->ike_sa, me, NULL, mid == 0);
+ this->ike_sa->update_hosts(this->ike_sa, NULL, other, FALSE);
}
}
charon->bus->message(charon->bus, msg, TRUE, TRUE);
diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c
index 8ae36af84..7cfa537a9 100644
--- a/src/libcharon/sa/ikev2/tasks/child_create.c
+++ b/src/libcharon/sa/ikev2/tasks/child_create.c
@@ -244,9 +244,23 @@ static bool allocate_spi(private_child_create_t *this)
{
enumerator_t *enumerator;
proposal_t *proposal;
+ protocol_id_t proto = PROTO_ESP;
- /* TODO: allocate additional SPI for AH if we have such proposals */
- this->my_spi = this->child_sa->alloc_spi(this->child_sa, PROTO_ESP);
+ if (this->initiator)
+ {
+ /* we just get a SPI for the first protocol. TODO: If we ever support
+ * proposal lists with mixed protocols, we'd need multiple SPIs */
+ if (this->proposals->get_first(this->proposals,
+ (void**)&proposal) == SUCCESS)
+ {
+ proto = proposal->get_protocol(proposal);
+ }
+ }
+ else
+ {
+ proto = this->proposal->get_protocol(this->proposal);
+ }
+ this->my_spi = this->child_sa->alloc_spi(this->child_sa, proto);
if (this->my_spi)
{
if (this->initiator)
diff --git a/src/libcharon/sa/ikev2/tasks/child_delete.c b/src/libcharon/sa/ikev2/tasks/child_delete.c
index eaaca2039..e898efc88 100644
--- a/src/libcharon/sa/ikev2/tasks/child_delete.c
+++ b/src/libcharon/sa/ikev2/tasks/child_delete.c
@@ -198,7 +198,7 @@ static status_t destroy_and_reestablish(private_child_delete_t *this)
child_sa_t *child_sa;
child_cfg_t *child_cfg;
protocol_id_t protocol;
- u_int32_t spi;
+ u_int32_t spi, reqid;
action_t action;
status_t status = SUCCESS;
@@ -211,6 +211,7 @@ static status_t destroy_and_reestablish(private_child_delete_t *this)
charon->bus->child_updown(charon->bus, child_sa, FALSE);
}
spi = child_sa->get_spi(child_sa, TRUE);
+ reqid = child_sa->get_reqid(child_sa);
protocol = child_sa->get_protocol(child_sa);
child_cfg = child_sa->get_config(child_sa);
child_cfg->get_ref(child_cfg);
@@ -223,12 +224,12 @@ static status_t destroy_and_reestablish(private_child_delete_t *this)
case ACTION_RESTART:
child_cfg->get_ref(child_cfg);
status = this->ike_sa->initiate(this->ike_sa, child_cfg,
- child_sa->get_reqid(child_sa), NULL, NULL);
+ reqid, NULL, NULL);
break;
case ACTION_ROUTE:
charon->traps->install(charon->traps,
this->ike_sa->get_peer_cfg(this->ike_sa), child_cfg,
- child_sa->get_reqid(child_sa));
+ reqid);
break;
default:
break;
diff --git a/src/libcharon/sa/keymat.c b/src/libcharon/sa/keymat.c
index 26c305f77..d1f6a1bdc 100644
--- a/src/libcharon/sa/keymat.c
+++ b/src/libcharon/sa/keymat.c
@@ -93,6 +93,7 @@ int keymat_get_keylen_integ(integrity_algorithm_t alg)
{AUTH_HMAC_SHA2_384_192, 384},
{AUTH_HMAC_SHA2_512_256, 512},
{AUTH_AES_XCBC_96, 128},
+ {AUTH_AES_CMAC_96, 128},
};
int i;
diff --git a/src/libcharon/sa/trap_manager.c b/src/libcharon/sa/trap_manager.c
index 37426fc47..1f66d6ceb 100644
--- a/src/libcharon/sa/trap_manager.c
+++ b/src/libcharon/sa/trap_manager.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011-2012 Tobias Brunner
+ * Copyright (C) 2011-2013 Tobias Brunner
* Copyright (C) 2009 Martin Willi
* Hochschule fuer Technik Rapperswil
*
@@ -19,6 +19,7 @@
#include <hydra.h>
#include <daemon.h>
#include <threading/rwlock.h>
+#include <threading/thread_value.h>
#include <collections/linked_list.h>
@@ -62,6 +63,11 @@ struct private_trap_manager_t {
rwlock_t *lock;
/**
+ * track if the current thread is installing a trap policy
+ */
+ thread_value_t *installing;
+
+ /**
* listener to track acquiring IKE_SAs
*/
trap_listener_t listener;
@@ -102,19 +108,20 @@ METHOD(trap_manager_t, install, u_int32_t,
linked_list_t *my_ts, *other_ts, *list;
enumerator_t *enumerator;
status_t status;
+ linked_list_t *proposals;
+ proposal_t *proposal;
+ protocol_id_t proto = PROTO_ESP;
/* try to resolve addresses */
ike_cfg = peer->get_ike_cfg(peer);
- other = host_create_from_dns(ike_cfg->get_other_addr(ike_cfg, NULL),
- 0, ike_cfg->get_other_port(ike_cfg));
+ other = ike_cfg->resolve_other(ike_cfg, AF_UNSPEC);
if (!other || other->is_anyaddr(other))
{
DESTROY_IF(other);
DBG1(DBG_CFG, "installing trap failed, remote address unknown");
return 0;
}
- me = host_create_from_dns(ike_cfg->get_my_addr(ike_cfg, NULL),
- other->get_family(other), ike_cfg->get_my_port(ike_cfg));
+ me = ike_cfg->resolve_me(ike_cfg, other->get_family(other));
if (!me || me->is_anyaddr(me))
{
DESTROY_IF(me);
@@ -130,6 +137,7 @@ METHOD(trap_manager_t, install, u_int32_t,
}
this->lock->write_lock(this->lock);
+ this->installing->set(this->installing, this);
enumerator = this->traps->create_enumerator(this->traps);
while (enumerator->enumerate(enumerator, &entry))
{
@@ -142,7 +150,6 @@ METHOD(trap_manager_t, install, u_int32_t,
}
}
enumerator->destroy(enumerator);
- this->lock->unlock(this->lock);
if (found)
{ /* config might have changed so update everything */
@@ -162,10 +169,15 @@ METHOD(trap_manager_t, install, u_int32_t,
other_ts = child->get_traffic_selectors(child, FALSE, NULL, list);
list->destroy_offset(list, offsetof(host_t, destroy));
- /* while we don't know the finally negotiated protocol (ESP|AH), we
- * could iterate all proposals for a best guess (TODO). But as we
- * support ESP only for now, we set it here. */
- child_sa->set_protocol(child_sa, PROTO_ESP);
+ /* We don't know the finally negotiated protocol (ESP|AH), we install
+ * the SA with the protocol of the first proposal */
+ proposals = child->get_proposals(child, TRUE);
+ if (proposals->get_first(proposals, (void**)&proposal) == SUCCESS)
+ {
+ proto = proposal->get_protocol(proposal);
+ }
+ proposals->destroy_offset(proposals, offsetof(proposal_t, destroy));
+ child_sa->set_protocol(child_sa, proto);
child_sa->set_mode(child_sa, child->get_mode(child));
status = child_sa->add_policies(child_sa, my_ts, other_ts);
my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy));
@@ -182,11 +194,11 @@ METHOD(trap_manager_t, install, u_int32_t,
.child_sa = child_sa,
.peer_cfg = peer->get_ref(peer),
);
- this->lock->write_lock(this->lock);
this->traps->insert_last(this->traps, entry);
- this->lock->unlock(this->lock);
reqid = child_sa->get_reqid(child_sa);
}
+ this->installing->set(this->installing, NULL);
+ this->lock->unlock(this->lock);
if (status != SUCCESS)
{
@@ -263,6 +275,10 @@ METHOD(trap_manager_t, find_reqid, u_int32_t,
entry_t *entry;
u_int32_t reqid = 0;
+ if (this->installing->get(this->installing))
+ { /* current thread holds the lock */
+ return reqid;
+ }
this->lock->read_lock(this->lock);
enumerator = this->traps->create_enumerator(this->traps);
while (enumerator->enumerate(enumerator, &entry))
@@ -429,6 +445,7 @@ METHOD(trap_manager_t, destroy, void,
{
charon->bus->remove_listener(charon->bus, &this->listener.listener);
this->traps->destroy_function(this->traps, (void*)destroy_entry);
+ this->installing->destroy(this->installing);
this->lock->destroy(this->lock);
free(this);
}
@@ -459,6 +476,7 @@ trap_manager_t *trap_manager_create(void)
},
.traps = linked_list_create(),
.lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
+ .installing = thread_value_create(NULL),
);
charon->bus->add_listener(charon->bus, &this->listener.listener);
diff --git a/src/libcharon/sa/xauth/xauth_manager.c b/src/libcharon/sa/xauth/xauth_manager.c
index 5709dc652..17eecc2c9 100644
--- a/src/libcharon/sa/xauth/xauth_manager.c
+++ b/src/libcharon/sa/xauth/xauth_manager.c
@@ -107,6 +107,17 @@ METHOD(xauth_manager_t, create_instance, xauth_method_t*,
enumerator_t *enumerator;
xauth_entry_t *entry;
xauth_method_t *method = NULL;
+ char *profile = NULL;
+
+ if (name)
+ {
+ profile = strchr(name, ':');
+ if (profile)
+ {
+ name = strndup(name, profile - name);
+ profile++;
+ }
+ }
this->lock->read_lock(this->lock);
enumerator = this->methods->create_enumerator(this->methods);
@@ -118,7 +129,7 @@ METHOD(xauth_manager_t, create_instance, xauth_method_t*,
}
if (role == entry->role && (!name || streq(name, entry->name)))
{
- method = entry->constructor(server, peer);
+ method = entry->constructor(server, peer, profile);
if (method)
{
break;
@@ -127,6 +138,10 @@ METHOD(xauth_manager_t, create_instance, xauth_method_t*,
}
enumerator->destroy(enumerator);
this->lock->unlock(this->lock);
+ if (profile)
+ {
+ free(name);
+ }
return method;
}
diff --git a/src/libcharon/sa/xauth/xauth_manager.h b/src/libcharon/sa/xauth/xauth_manager.h
index 929d5de8f..65b3c58a3 100644
--- a/src/libcharon/sa/xauth/xauth_manager.h
+++ b/src/libcharon/sa/xauth/xauth_manager.h
@@ -55,7 +55,11 @@ struct xauth_manager_t {
/**
* Create a new XAuth method instance.
*
- * @param name backend name, as it was registered with
+ * The name may contain an option string, separated by a colon. This option
+ * string gets passed to the XAuth constructor to specify the behavior
+ * of the XAuth method.
+ *
+ * @param name backend name, with optional config string
* @param role XAUTH_SERVER or XAUTH_PEER
* @param server identity of the server
* @param peer identity of the peer (client)
diff --git a/src/libcharon/sa/xauth/xauth_method.h b/src/libcharon/sa/xauth/xauth_method.h
index 9f6067dbf..701b4dc77 100644
--- a/src/libcharon/sa/xauth/xauth_method.h
+++ b/src/libcharon/sa/xauth/xauth_method.h
@@ -104,10 +104,12 @@ struct xauth_method_t {
*
* @param server ID of the server to use for credential lookup
* @param peer ID of the peer to use for credential lookup
+ * @param profile configuration string to pass to XAuth method, or NULL
* @return implementation of the eap_method_t interface
*/
typedef xauth_method_t *(*xauth_constructor_t)(identification_t *server,
- identification_t *peer);
+ identification_t *peer,
+ char *profile);
/**
* Helper function to (un-)register XAuth methods from plugin features.