aboutsummaryrefslogtreecommitdiffstats
path: root/src/charon/sa
diff options
context:
space:
mode:
Diffstat (limited to 'src/charon/sa')
-rw-r--r--src/charon/sa/child_sa.c147
-rw-r--r--src/charon/sa/child_sa.h49
-rw-r--r--src/charon/sa/ike_sa.c1546
-rw-r--r--src/charon/sa/ike_sa.h229
-rw-r--r--src/charon/sa/ike_sa_manager.c475
-rw-r--r--src/charon/sa/ike_sa_manager.h86
-rw-r--r--src/charon/sa/task_manager.c780
-rw-r--r--src/charon/sa/task_manager.h144
-rw-r--r--src/charon/sa/tasks/child_create.c718
-rw-r--r--src/charon/sa/tasks/child_create.h72
-rw-r--r--src/charon/sa/tasks/child_delete.c264
-rw-r--r--src/charon/sa/tasks/child_delete.h58
-rw-r--r--src/charon/sa/tasks/child_rekey.c353
-rw-r--r--src/charon/sa/tasks/child_rekey.h58
-rw-r--r--src/charon/sa/tasks/ike_auth.c513
-rw-r--r--src/charon/sa/tasks/ike_auth.h60
-rw-r--r--src/charon/sa/tasks/ike_cert.c361
-rw-r--r--src/charon/sa/tasks/ike_cert.h61
-rw-r--r--src/charon/sa/tasks/ike_config.c421
-rw-r--r--src/charon/sa/tasks/ike_config.h59
-rw-r--r--src/charon/sa/tasks/ike_delete.c169
-rw-r--r--src/charon/sa/tasks/ike_delete.h57
-rw-r--r--src/charon/sa/tasks/ike_dpd.c106
-rw-r--r--src/charon/sa/tasks/ike_dpd.h58
-rw-r--r--src/charon/sa/tasks/ike_init.c536
-rw-r--r--src/charon/sa/tasks/ike_init.h60
-rw-r--r--src/charon/sa/tasks/ike_natd.c378
-rw-r--r--src/charon/sa/tasks/ike_natd.h57
-rw-r--r--src/charon/sa/tasks/ike_rekey.c232
-rw-r--r--src/charon/sa/tasks/ike_rekey.h57
-rw-r--r--src/charon/sa/tasks/task.c38
-rw-r--r--src/charon/sa/tasks/task.h151
-rw-r--r--src/charon/sa/transactions/create_child_sa.c1121
-rw-r--r--src/charon/sa/transactions/create_child_sa.h123
-rw-r--r--src/charon/sa/transactions/dead_peer_detection.c187
-rw-r--r--src/charon/sa/transactions/dead_peer_detection.h58
-rw-r--r--src/charon/sa/transactions/delete_child_sa.c356
-rw-r--r--src/charon/sa/transactions/delete_child_sa.h68
-rw-r--r--src/charon/sa/transactions/delete_ike_sa.c284
-rw-r--r--src/charon/sa/transactions/delete_ike_sa.h81
-rw-r--r--src/charon/sa/transactions/ike_auth.c1562
-rw-r--r--src/charon/sa/transactions/ike_auth.h110
-rw-r--r--src/charon/sa/transactions/ike_sa_init.c1121
-rw-r--r--src/charon/sa/transactions/ike_sa_init.h97
-rw-r--r--src/charon/sa/transactions/rekey_ike_sa.c889
-rw-r--r--src/charon/sa/transactions/rekey_ike_sa.h81
-rw-r--r--src/charon/sa/transactions/transaction.c167
-rw-r--r--src/charon/sa/transactions/transaction.h180
48 files changed, 6937 insertions, 7901 deletions
diff --git a/src/charon/sa/child_sa.c b/src/charon/sa/child_sa.c
index 117471472..fa895eb7e 100644
--- a/src/charon/sa/child_sa.c
+++ b/src/charon/sa/child_sa.c
@@ -6,8 +6,8 @@
*/
/*
+ * Copyright (C) 2005-2007 Martin Willi
* Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger
- * Copyright (C) 2005-2006 Martin Willi
* Copyright (C) 2005 Jan Hutter
* Hochschule fuer Technik Rapperswil
*
@@ -69,11 +69,6 @@ struct private_child_sa_t {
*/
child_sa_t public;
- /**
- * Name of the policy used by this CHILD_SA
- */
- char *name;
-
struct {
/** address of peer */
host_t *addr;
@@ -134,34 +129,9 @@ struct private_child_sa_t {
time_t install_time;
/**
- * Lifetime before rekeying
- */
- u_int32_t soft_lifetime;
-
- /**
- * Lifetime before delete
- */
- u_int32_t hard_lifetime;
-
- /**
* state of the CHILD_SA
*/
child_sa_state_t state;
-
- /**
- * transaction which is rekeying this CHILD_SA
- */
- transaction_t *rekeying_transaction;
-
- /**
- * Updown script
- */
- char *script;
-
- /**
- * Allow host access
- */
- bool hostaccess;
/**
* Specifies if NAT traversal is used
@@ -172,6 +142,11 @@ struct private_child_sa_t {
* mode this SA uses, tunnel/transport
*/
mode_t mode;
+
+ /**
+ * policy used to create this child
+ */
+ policy_t *policy;
};
/**
@@ -179,22 +154,7 @@ struct private_child_sa_t {
*/
static char *get_name(private_child_sa_t *this)
{
- return this->name;
-}
-
-/**
- * Implementation of child_sa_t.set_name.
- */
-static void set_name(private_child_sa_t *this, char* name)
-{
- char buffer[64];
-
- if (snprintf(buffer, sizeof(buffer), "%s[%d]",
- name, this->reqid - REQID_START) > 0)
- {
- free(this->name);
- this->name = strdup(buffer);
- }
+ return this->policy->get_name(this->policy);;
}
/**
@@ -234,14 +194,25 @@ static child_sa_state_t get_state(private_child_sa_t *this)
}
/**
+ * Implements child_sa_t.get_policy
+ */
+static policy_t* get_policy(private_child_sa_t *this)
+{
+ return this->policy;
+}
+
+/**
* Run the up/down script
*/
static void updown(private_child_sa_t *this, bool up)
{
sa_policy_t *policy;
iterator_t *iterator;
+ char *script;
- if (this->script == NULL)
+ script = this->policy->get_updown(this->policy);
+
+ if (script == NULL)
{
return;
}
@@ -307,7 +278,7 @@ static void updown(private_child_sa_t *this, bool up)
policy->my_ts->is_host(policy->my_ts,
this->me.addr) ? "-host" : "-client",
this->me.addr->get_family(this->me.addr) == AF_INET ? "" : "-ipv6",
- this->name,
+ this->policy->get_name(this->policy),
ifname,
this->reqid,
this->me.addr,
@@ -322,8 +293,8 @@ static void updown(private_child_sa_t *this, bool up)
other_client, other_client_mask,
policy->other_ts->get_from_port(policy->other_ts),
policy->other_ts->get_protocol(policy->other_ts),
- this->hostaccess? "PLUTO_HOST_ACCESS='1' " : "",
- this->script);
+ this->policy->get_hostaccess(this->policy) ?
+ "PLUTO_HOST_ACCESS='1' " : "", script);
free(ifname);
free(my_client);
free(other_client);
@@ -332,7 +303,7 @@ static void updown(private_child_sa_t *this, bool up)
if (shell == NULL)
{
- DBG1(DBG_CHD, "could not execute updown script '%s'", this->script);
+ DBG1(DBG_CHD, "could not execute updown script '%s'", script);
return;
}
@@ -447,7 +418,7 @@ static status_t alloc(private_child_sa_t *this, linked_list_t *proposals)
static status_t install(private_child_sa_t *this, proposal_t *proposal,
mode_t mode, prf_plus_t *prf_plus, bool mine)
{
- u_int32_t spi;
+ u_int32_t spi, soft, hard;;
algorithm_t *enc_algo, *int_algo;
algorithm_t enc_algo_none = {ENCR_UNDEFINED, 0};
algorithm_t int_algo_none = {AUTH_UNDEFINED, 0};
@@ -532,16 +503,15 @@ static status_t install(private_child_sa_t *this, proposal_t *proposal,
natt = NULL;
}
+ soft = this->policy->get_soft_lifetime(this->policy);
+ hard = this->policy->get_hard_lifetime(this->policy);
/* send SA down to the kernel */
DBG2(DBG_CHD, " SPI 0x%.8x, src %H dst %H", ntohl(spi), src, dst);
status = charon->kernel_interface->add_sa(charon->kernel_interface,
- src, dst,
- spi, this->protocol,
- this->reqid,
- mine ? this->soft_lifetime : 0,
- this->hard_lifetime,
- enc_algo, int_algo,
+ src, dst, spi, this->protocol,
+ this->reqid, mine ? soft : 0,
+ hard, enc_algo, int_algo,
prf_plus, natt, mode, mine);
this->encryption = *enc_algo;
@@ -705,22 +675,6 @@ static linked_list_t *get_other_traffic_selectors(private_child_sa_t *this)
}
/**
- * Implementation of child_sa_t.set_rekeying_transaction.
- */
-static void set_rekeying_transaction(private_child_sa_t *this, transaction_t *transaction)
-{
- this->rekeying_transaction = transaction;
-}
-
-/**
- * Implementation of child_sa_t.get_rekeying_transaction.
- */
-static transaction_t* get_rekeying_transaction(private_child_sa_t *this)
-{
- return this->rekeying_transaction;
-}
-
-/**
* Implementation of child_sa_t.get_use_time
*/
static status_t get_use_time(private_child_sa_t *this, bool inbound, time_t *use_time)
@@ -769,7 +723,7 @@ static int print(FILE *stream, const struct printf_info *info,
private_child_sa_t *this = *((private_child_sa_t**)(args[0]));
iterator_t *iterator;
sa_policy_t *policy;
- u_int32_t now, rekeying;
+ u_int32_t now, rekeying, soft;
u_int32_t use, use_in, use_fwd;
status_t status;
size_t written = 0;
@@ -781,8 +735,9 @@ static int print(FILE *stream, const struct printf_info *info,
now = (u_int32_t)time(NULL);
- written += fprintf(stream, "%12s: %N, reqid: %d, %N", this->name,
- child_sa_state_names, this->state, this->reqid,
+ written += fprintf(stream, "%12s{%d}: %N, %N",
+ this->policy->get_name(this->policy), this->reqid,
+ child_sa_state_names, this->state,
mode_names, this->mode);
if (this->state == CHILD_INSTALLED)
@@ -793,7 +748,9 @@ static int print(FILE *stream, const struct printf_info *info,
if (info->alt)
{
- written += fprintf(stream, "\n%12s: ", this->name);
+ written += fprintf(stream, "\n%12s{%d}: ",
+ this->policy->get_name(this->policy),
+ this->reqid);
if (this->protocol == PROTO_ESP)
{
@@ -815,10 +772,11 @@ static int print(FILE *stream, const struct printf_info *info,
}
written += fprintf(stream, ", rekeying ");
+ soft = this->policy->get_soft_lifetime(this->policy);
/* calculate rekey times */
- if (this->soft_lifetime)
+ if (soft)
{
- rekeying = this->soft_lifetime - (now - this->install_time);
+ rekeying = soft - (now - this->install_time);
written += fprintf(stream, "in %ds", rekeying);
}
else
@@ -830,8 +788,9 @@ static int print(FILE *stream, const struct printf_info *info,
iterator = this->policies->create_iterator(this->policies, TRUE);
while (iterator->iterate(iterator, (void**)&policy))
{
- written += fprintf(stream, "\n%12s: %R===%R, last use: ",
- this->name, policy->my_ts, policy->other_ts);
+ written += fprintf(stream, "\n%12s{%d}: %R===%R, last use: ",
+ this->policy->get_name(this->policy), this->reqid,
+ policy->my_ts, policy->other_ts);
/* query time of last policy use */
@@ -1074,25 +1033,22 @@ static void destroy(private_child_sa_t *this)
this->other.addr->destroy(this->other.addr);
this->me.id->destroy(this->me.id);
this->other.id->destroy(this->other.id);
- free(this->name);
- free(this->script);
+ this->policy->destroy(this->policy);
free(this);
}
/*
* Described in header.
*/
-child_sa_t * child_sa_create(u_int32_t rekey, host_t *me, host_t* other,
+child_sa_t * child_sa_create(host_t *me, host_t* other,
identification_t *my_id, identification_t *other_id,
- u_int32_t soft_lifetime, u_int32_t hard_lifetime,
- char *script, bool hostaccess, bool use_natt)
+ policy_t *policy, u_int32_t rekey, bool use_natt)
{
- static u_int32_t reqid = REQID_START;
+ static u_int32_t reqid = 0;
private_child_sa_t *this = malloc_thing(private_child_sa_t);
/* public functions */
this->public.get_name = (char*(*)(child_sa_t*))get_name;
- this->public.set_name = (void(*)(child_sa_t*,char*))set_name;
this->public.get_reqid = (u_int32_t(*)(child_sa_t*))get_reqid;
this->public.get_spi = (u_int32_t(*)(child_sa_t*, bool))get_spi;
this->public.get_protocol = (protocol_id_t(*)(child_sa_t*))get_protocol;
@@ -1104,14 +1060,12 @@ child_sa_t * child_sa_create(u_int32_t rekey, host_t *me, host_t* other,
this->public.get_my_traffic_selectors = (linked_list_t*(*)(child_sa_t*))get_my_traffic_selectors;
this->public.get_other_traffic_selectors = (linked_list_t*(*)(child_sa_t*))get_other_traffic_selectors;
this->public.get_use_time = (status_t (*)(child_sa_t*,bool,time_t*))get_use_time;
- this->public.set_rekeying_transaction = (void (*)(child_sa_t*,transaction_t*))set_rekeying_transaction;
- this->public.get_rekeying_transaction = (transaction_t* (*)(child_sa_t*))get_rekeying_transaction;
this->public.set_state = (void(*)(child_sa_t*,child_sa_state_t))set_state;
this->public.get_state = (child_sa_state_t(*)(child_sa_t*))get_state;
+ this->public.get_policy = (policy_t*(*)(child_sa_t*))get_policy;
this->public.destroy = (void(*)(child_sa_t*))destroy;
/* private data */
- this->name = strdup("(uninitialized)");
this->me.addr = me->clone(me);
this->other.addr = other->clone(other);
this->me.id = my_id->clone(my_id);
@@ -1120,11 +1074,7 @@ child_sa_t * child_sa_create(u_int32_t rekey, host_t *me, host_t* other,
this->other.spi = 0;
this->alloc_ah_spi = 0;
this->alloc_esp_spi = 0;
- this->script = script ? strdup(script) : NULL;
- this->hostaccess = hostaccess;
this->use_natt = use_natt;
- this->soft_lifetime = soft_lifetime;
- this->hard_lifetime = hard_lifetime;
this->state = CHILD_CREATED;
/* reuse old reqid if we are rekeying an existing CHILD_SA */
this->reqid = rekey ? rekey : ++reqid;
@@ -1137,7 +1087,8 @@ child_sa_t * child_sa_create(u_int32_t rekey, host_t *me, host_t* other,
this->other_ts = linked_list_create();
this->protocol = PROTO_NONE;
this->mode = MODE_TUNNEL;
- this->rekeying_transaction = NULL;
+ this->policy = policy;
+ policy->get_ref(policy);
return &this->public;
}
diff --git a/src/charon/sa/child_sa.h b/src/charon/sa/child_sa.h
index 06362f35e..bd0e032da 100644
--- a/src/charon/sa/child_sa.h
+++ b/src/charon/sa/child_sa.h
@@ -6,8 +6,8 @@
*/
/*
+ * Copyright (C) 2006-2007 Martin Willi
* Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger
- * Copyright (C) 2006 Martin Willi
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
@@ -32,7 +32,7 @@ typedef struct child_sa_t child_sa_t;
#include <crypto/prf_plus.h>
#include <encoding/payloads/proposal_substructure.h>
#include <config/proposal.h>
-#include <sa/transactions/transaction.h>
+#include <config/policies/policy.h>
/**
* Where we should start with reqid enumeration
@@ -109,18 +109,10 @@ struct child_sa_t {
char* (*get_name) (child_sa_t *this);
/**
- * @brief Set the name of the policy this IKE_SA uses.
- *
- * @param this calling object
- * @param name name, gets cloned
- */
- void (*set_name) (child_sa_t *this, char* name);
-
- /**
- * @brief Get the unique reqid of the CHILD SA.
+ * @brief Get the reqid of the CHILD SA.
*
- * Every CHILD_SA has a unique reqid, which is also
- * stored down in the kernel.
+ * Every CHILD_SA has a reqid. The kernel uses this ID to
+ * identify it.
*
* @param this calling object
* @return reqid of the CHILD SA
@@ -259,23 +251,12 @@ struct child_sa_t {
void (*set_state) (child_sa_t *this, child_sa_state_t state);
/**
- * @brief Set the transaction which rekeys this CHILD_SA.
- *
- * Since either end may initiate CHILD_SA rekeying, we must detect
- * such situations to handle them cleanly. A rekeying transaction
- * registers itself to the CHILD_SA, and checks later if another
- * transaction is in progress of a rekey.
+ * @brief Get the policy used to set up this child sa.
*
* @param this calling object
- */
- void (*set_rekeying_transaction) (child_sa_t *this, transaction_t *transaction);
-
- /**
- * @brief Get the transaction which rekeys this CHILD_SA.
- *
- * @param this calling object
- */
- transaction_t* (*get_rekeying_transaction) (child_sa_t *this);
+ * @return policy
+ */
+ policy_t* (*get_policy) (child_sa_t *this);
/**
* @brief Destroys a child_sa.
@@ -288,23 +269,19 @@ struct child_sa_t {
/**
* @brief Constructor to create a new child_sa_t.
*
- * @param rekey_reqid reqid of old CHILD_SA when rekeying, 0 otherwise
* @param me own address
* @param other remote address
* @param my_id id of own peer
* @param other_id id of remote peer
- * @param soft_lifetime time before rekeying
- * @param hard_lifteime time before delete
- * @param script updown script to use when calling child_sa_t.script()
- * @param hostaccess allow host access (needed by updown script)
+ * @param policy policy this CHILD_SA instantiates
+ * @param reqid reqid of old CHILD_SA when rekeying, 0 otherwise
* @param use_natt TRUE if NAT traversal is used
* @return child_sa_t object
*
* @ingroup sa
*/
-child_sa_t * child_sa_create(u_int32_t rekey_reqid, host_t *me, host_t *other,
+child_sa_t * child_sa_create(host_t *me, host_t *other,
identification_t *my_id, identification_t* other_id,
- u_int32_t soft_lifetime, u_int32_t hard_lifetime,
- char *script, bool hostaccess, bool use_natt);
+ policy_t *policy, u_int32_t reqid, bool use_natt);
#endif /*CHILD_SA_H_*/
diff --git a/src/charon/sa/ike_sa.c b/src/charon/sa/ike_sa.c
index e5a77edad..91ee4be0b 100644
--- a/src/charon/sa/ike_sa.c
+++ b/src/charon/sa/ike_sa.c
@@ -25,12 +25,14 @@
#include <sys/time.h>
#include <string.h>
#include <printf.h>
+#include <sys/stat.h>
#include "ike_sa.h"
#include <library.h>
#include <daemon.h>
#include <utils/linked_list.h>
+#include <utils/lexparser.h>
#include <crypto/diffie_hellman.h>
#include <crypto/prf_plus.h>
#include <crypto/crypters/crypter.h>
@@ -42,14 +44,19 @@
#include <encoding/payloads/transform_substructure.h>
#include <encoding/payloads/transform_attribute.h>
#include <encoding/payloads/ts_payload.h>
-#include <sa/transactions/transaction.h>
-#include <sa/transactions/ike_sa_init.h>
-#include <sa/transactions/delete_ike_sa.h>
-#include <sa/transactions/create_child_sa.h>
-#include <sa/transactions/delete_child_sa.h>
-#include <sa/transactions/dead_peer_detection.h>
-#include <sa/transactions/rekey_ike_sa.h>
-#include <queues/jobs/retransmit_request_job.h>
+#include <sa/task_manager.h>
+#include <sa/tasks/ike_init.h>
+#include <sa/tasks/ike_natd.h>
+#include <sa/tasks/ike_auth.h>
+#include <sa/tasks/ike_config.h>
+#include <sa/tasks/ike_cert.h>
+#include <sa/tasks/ike_rekey.h>
+#include <sa/tasks/ike_delete.h>
+#include <sa/tasks/ike_dpd.h>
+#include <sa/tasks/child_create.h>
+#include <sa/tasks/child_delete.h>
+#include <sa/tasks/child_rekey.h>
+#include <queues/jobs/retransmit_job.h>
#include <queues/jobs/delete_ike_sa_job.h>
#include <queues/jobs/send_dpd_job.h>
#include <queues/jobs/send_keepalive_job.h>
@@ -57,6 +64,11 @@
#include <queues/jobs/route_job.h>
#include <queues/jobs/initiate_job.h>
+
+#ifndef RESOLV_CONF
+#define RESOLV_CONF "/etc/resolv.conf"
+#endif
+
ENUM(ike_sa_state_names, IKE_CREATED, IKE_DELETING,
"CREATED",
"CONNECTING",
@@ -83,14 +95,29 @@ struct private_ike_sa_t {
ike_sa_id_t *ike_sa_id;
/**
+ * unique numerical ID for this IKE_SA.
+ */
+ u_int32_t unique_id;
+
+ /**
* Current state of the IKE_SA
*/
- ike_sa_state_t state;
+ ike_sa_state_t state;
+
+ /**
+ * connection used to establish this IKE_SA.
+ */
+ connection_t *connection;
+
+ /**
+ * Peer and authentication information to establish IKE_SA.
+ */
+ policy_t *policy;
/**
- * Name of the connection used by this IKE_SA
+ * Juggles tasks to process messages
*/
- char *name;
+ task_manager_t *task_manager;
/**
* Address of local host
@@ -158,11 +185,6 @@ struct private_ike_sa_t {
prf_t *auth_verify;
/**
- * NAT hasher.
- */
- hasher_t *nat_hasher;
-
- /**
* NAT status of local host.
*/
bool nat_here;
@@ -173,14 +195,19 @@ struct private_ike_sa_t {
bool nat_there;
/**
- * message ID for next outgoung request
+ * Virtual IP on local host, if any
*/
- u_int32_t message_id_out;
-
+ host_t *my_virtual_ip;
+
/**
- * will the IKE_SA be fully reauthenticated or rekeyed only?
+ * Virtual IP on remote host, if any
*/
- bool reauth;
+ host_t *other_virtual_ip;
+
+ /**
+ * List of DNS servers installed by us
+ */
+ linked_list_t *dns_servers;
/**
* Timestamps for this IKE_SA
@@ -197,51 +224,12 @@ struct private_ike_sa_t {
/** when IKE_SA gets deleted */
u_int32_t delete;
} time;
-
- /**
- * interval to send DPD liveness check
- */
- time_t dpd_delay;
-
- /**
- * number of retransmit sequences to go through before giving up (keyingtries)
- */
- u_int32_t retrans_sequences;
-
- /**
- * List of queued transactions to process
- */
- linked_list_t *transaction_queue;
-
- /**
- * Transaction currently initiated
- * (only one supported yet, window size = 1)
- */
- transaction_t *transaction_out;
-
- /**
- * last transaction initiated by peer processed.
- * (only one supported yet, window size = 1)
- * Stored for retransmission.
- */
- transaction_t *transaction_in;
-
- /**
- * Next incoming transaction expected. Used to
- * do multi transaction operations.
- */
- transaction_t *transaction_in_next;
-
- /**
- * Transaction which rekeys this IKE_SA, used do detect simultaneus rekeying
- */
- transaction_t *rekeying_transaction;
};
/**
* get the time of the latest traffic processed by the kernel
*/
-static time_t get_kernel_time(private_ike_sa_t* this, bool inbound)
+static time_t get_use_time(private_ike_sa_t* this, bool inbound)
{
iterator_t *iterator;
child_sa_t *child_sa;
@@ -257,49 +245,68 @@ static time_t get_kernel_time(private_ike_sa_t* this, bool inbound)
}
iterator->destroy(iterator);
- return latest;
+ if (inbound)
+ {
+ return max(this->time.inbound, latest);
+ }
+ else
+ {
+ return max(this->time.outbound, latest);
+ }
}
/**
- * get the time of the latest received traffice
+ * Implementation of ike_sa_t.get_unique_id
*/
-static time_t get_time_inbound(private_ike_sa_t *this)
+static u_int32_t get_unique_id(private_ike_sa_t *this)
{
- return max(this->time.inbound, get_kernel_time(this, TRUE));
+ return this->unique_id;
}
/**
- * get the time of the latest sent traffic
+ * Implementation of ike_sa_t.get_name.
*/
-static time_t get_time_outbound(private_ike_sa_t *this)
+static char *get_name(private_ike_sa_t *this)
{
- return max(this->time.outbound, get_kernel_time(this, FALSE));
+ if (this->connection)
+ {
+ return this->connection->get_name(this->connection);
+ }
+ return "(unnamed)";
}
/**
- * Implementation of ike_sa_t.get_name.
+ * Implementation of ike_sa_t.get_connection
*/
-static char *get_name(private_ike_sa_t *this)
+static connection_t* get_connection(private_ike_sa_t *this)
{
- return this->name;
+ return this->connection;
}
/**
- * Implementation of ike_sa_t.set_name.
+ * Implementation of ike_sa_t.set_connection
*/
-static void set_name(private_ike_sa_t *this, char* name)
+static void set_connection(private_ike_sa_t *this, connection_t *connection)
{
- free(this->name);
- this->name = strdup(name);
+ this->connection = connection;
+ connection->get_ref(connection);
}
/**
- * Implementation of ike_sa_t.apply_connection.
+ * Implementation of ike_sa_t.get_policy
*/
-static void apply_connection(private_ike_sa_t *this, connection_t *connection)
+static policy_t *get_policy(private_ike_sa_t *this)
{
- this->dpd_delay = connection->get_dpd_delay(connection);
- this->retrans_sequences = connection->get_retrans_seq(connection);
+ return this->policy;
+}
+
+/**
+ * Implementation of ike_sa_t.set_policy
+ */
+static void set_policy(private_ike_sa_t *this, policy_t *policy)
+{
+ policy->get_ref(policy);
+ this->policy = policy;
}
/**
@@ -341,35 +348,6 @@ static void set_other_host(private_ike_sa_t *this, host_t *other)
*/
static void update_hosts(private_ike_sa_t *this, host_t *me, host_t *other)
{
- /*
- * Quoting RFC 4306:
- *
- * 2.11. Address and Port Agility
- *
- * IKE runs over UDP ports 500 and 4500, and implicitly sets up ESP and
- * AH associations for the same IP addresses it runs over. The IP
- * addresses and ports in the outer header are, however, not themselves
- * cryptographically protected, and IKE is designed to work even through
- * Network Address Translation (NAT) boxes. An implementation MUST
- * accept incoming requests even if the source port is not 500 or 4500,
- * and MUST respond to the address and port from which the request was
- * received. It MUST specify the address and port at which the request
- * was received as the source address and port in the response. IKE
- * functions identically over IPv4 or IPv6.
- *
- * [...]
- *
- * There are cases where a NAT box decides to remove mappings that
- * are still alive (for example, the keepalive interval is too long,
- * or the NAT box is rebooted). To recover in these cases, hosts
- * that are not behind a NAT SHOULD send all packets (including
- * retransmission packets) to the IP address and port from the last
- * valid authenticated packet from the other end (i.e., dynamically
- * update the address). A host behind a NAT SHOULD NOT do this
- * because it opens a DoS attack possibility. Any authenticated IKE
- * packet or any authenticated UDP-encapsulated ESP packet can be
- * used to detect that the IP address or the port has changed.
- */
iterator_t *iterator = NULL;
child_sa_t *child_sa = NULL;
host_diff_t my_diff, other_diff;
@@ -425,399 +403,149 @@ static void update_hosts(private_ike_sa_t *this, host_t *me, host_t *other)
{
child_sa->update_hosts(child_sa, this->my_host, this->other_host,
my_diff, other_diff);
- /* TODO: what to do if update fails? Delete CHILD_SA? */
}
iterator->destroy(iterator);
}
/**
- * called when the peer is not responding anymore
+ * Implementation of ike_sa_t.retransmit.
*/
-static void dpd_detected(private_ike_sa_t *this)
+static status_t retransmit(private_ike_sa_t *this, u_int32_t message_id)
{
- connection_t *connection = NULL;
- policy_t *policy;
- linked_list_t *my_ts, *other_ts;
- child_sa_t* child_sa;
- dpd_action_t action;
- job_t *job;
-
- DBG2(DBG_IKE, "dead peer detected, handling CHILD_SAs dpd action");
-
- /* check for childrens with dpdaction = hold */
- while(this->child_sas->remove_first(this->child_sas,
- (void**)&child_sa) == SUCCESS)
+ this->time.outbound = time(NULL);
+ if (this->task_manager->retransmit(this->task_manager, message_id) != SUCCESS)
{
- /* get the policy which belongs to this CHILD */
- my_ts = child_sa->get_my_traffic_selectors(child_sa);
- other_ts = child_sa->get_other_traffic_selectors(child_sa);
- policy = charon->policies->get_policy(charon->policies,
- this->my_id, this->other_id,
- my_ts, other_ts,
- this->my_host, this->other_host);
- if (policy == NULL)
- {
- DBG1(DBG_IKE, "no policy for CHILD to handle DPD");
- continue;
- }
+ connection_t *connection = NULL;
+ policy_t *policy;
+ linked_list_t *my_ts, *other_ts;
+ child_sa_t* child_sa;
+ dpd_action_t action;
+ job_t *job;
- action = policy->get_dpd_action(policy);
- /* get a connection for further actions */
- if (connection == NULL &&
- (action == DPD_ROUTE || action == DPD_RESTART))
+ DBG2(DBG_IKE, "dead peer detected, handling CHILD_SAs dpd action");
+
+ /* check for childrens with dpdaction = hold */
+ while(this->child_sas->remove_first(this->child_sas,
+ (void**)&child_sa) == SUCCESS)
{
- connection = charon->connections->get_connection_by_hosts(
- charon->connections,
- this->my_host, this->other_host);
- if (connection == NULL)
+ /* get the policy which belongs to this CHILD */
+ my_ts = child_sa->get_my_traffic_selectors(child_sa);
+ other_ts = child_sa->get_other_traffic_selectors(child_sa);
+ policy = charon->policies->get_policy(charon->policies,
+ this->my_id, this->other_id,
+ my_ts, other_ts,
+ this->my_host, this->other_host);
+ if (policy == NULL)
{
- SIG(IKE_UP_FAILED, "no connection found to handle DPD");
- break;
+ DBG1(DBG_IKE, "no policy for CHILD to handle DPD");
+ continue;
+ }
+
+ action = policy->get_dpd_action(policy);
+ /* get a connection for further actions */
+ if (connection == NULL &&
+ (action == DPD_ROUTE || action == DPD_RESTART))
+ {
+ connection = charon->connections->get_connection_by_hosts(
+ charon->connections,
+ this->my_host, this->other_host);
+ if (connection == NULL)
+ {
+ SIG(IKE_UP_FAILED, "no connection found to handle DPD");
+ break;
+ }
}
+
+ DBG1(DBG_IKE, "dpd action for %s is %N",
+ policy->get_name(policy), dpd_action_names, action);
+
+ switch (action)
+ {
+ case DPD_ROUTE:
+ connection->get_ref(connection);
+ job = (job_t*)route_job_create(connection, policy, TRUE);
+ charon->job_queue->add(charon->job_queue, job);
+ break;
+ case DPD_RESTART:
+ connection->get_ref(connection);
+ job = (job_t*)initiate_job_create(connection, policy);
+ charon->job_queue->add(charon->job_queue, job);
+ break;
+ default:
+ policy->destroy(policy);
+ break;
+ }
+ child_sa->destroy(child_sa);
}
- DBG1(DBG_IKE, "dpd action for %s is %N",
- policy->get_name(policy), dpd_action_names, action);
-
- switch (action)
+ /* send a proper signal to brief interested bus listeners */
+ switch (this->state)
{
- case DPD_ROUTE:
- connection->get_ref(connection);
- job = (job_t*)route_job_create(connection, policy, TRUE);
- charon->job_queue->add(charon->job_queue, job);
+ case IKE_CONNECTING:
+ SIG(IKE_UP_FAILED, "establishing IKE_SA failed, peer not responding");
break;
- case DPD_RESTART:
- connection->get_ref(connection);
- job = (job_t*)initiate_job_create(connection, NULL, policy);
- charon->job_queue->add(charon->job_queue, job);
+ case IKE_REKEYING:
+ SIG(IKE_REKEY_FAILED, "rekeying IKE_SA failed, peer not responding");
+ break;
+ case IKE_DELETING:
+ SIG(IKE_DOWN_FAILED, "proper IKE_SA delete failed, peer not responding");
break;
default:
- policy->destroy(policy);
break;
}
- child_sa->destroy(child_sa);
- }
-
- /* send a proper signal to brief interested bus listeners */
- switch (this->state)
- {
- case IKE_CONNECTING:
- SIG(IKE_UP_FAILED, "establishing IKE_SA failed, peer not responding");
- break;
- case IKE_REKEYING:
- SIG(IKE_REKEY_FAILED, "rekeying IKE_SA failed, peer not responding");
- break;
- case IKE_DELETING:
- SIG(IKE_DOWN_FAILED, "proper IKE_SA delete failed, peer not responding");
- break;
- default:
- break;
- }
-
- DESTROY_IF(connection);
-}
-
-/**
- * send a request and schedule retransmission
- */
-static status_t transmit_request(private_ike_sa_t *this)
-{
- message_t *request;
- packet_t *packet;
- status_t status;
- retransmit_request_job_t *job;
- u_int32_t transmitted;
- u_int32_t timeout;
- transaction_t *transaction = this->transaction_out;
- u_int32_t message_id;
-
- transmitted = transaction->requested(transaction);
- timeout = charon->configuration->get_retransmit_timeout(charon->configuration,
- transmitted,
- this->retrans_sequences);
- if (timeout == 0)
- {
- DBG1(DBG_IKE, "giving up after %d retransmits, deleting IKE_SA",
- transmitted - 1);
- dpd_detected(this);
+
+ DESTROY_IF(connection);
return DESTROY_ME;
}
-
- status = transaction->get_request(transaction, &request);
- if (status != SUCCESS)
- {
- /* generating request failed */
- return status;
- }
- message_id = transaction->get_message_id(transaction);
- /* if we retransmit, the request is already generated */
- if (transmitted == 0)
- {
- status = request->generate(request, this->crypter_out, this->signer_out, &packet);
- if (status != SUCCESS)
- {
- DBG1(DBG_IKE, "request generation failed. transaction discarded");
- return FAILED;
- }
- }
- else
- {
- DBG1(DBG_IKE, "sending retransmit %d for %N request with messageID %d",
- transmitted, exchange_type_names, request->get_exchange_type(request),
- message_id);
- packet = request->get_packet(request);
- }
- /* finally send */
- charon->send_queue->add(charon->send_queue, packet);
- this->time.outbound = time(NULL);
-
- /* schedule retransmission job */
- job = retransmit_request_job_create(message_id, this->ike_sa_id);
- charon->event_queue->add_relative(charon->event_queue, (job_t*)job, timeout);
return SUCCESS;
}
/**
- * Implementation of ike_sa.retransmit_request.
- */
-static status_t retransmit_request(private_ike_sa_t *this, u_int32_t message_id)
-{
- if (this->transaction_out == NULL ||
- this->transaction_out->get_message_id(this->transaction_out) != message_id)
- {
- /* no retransmit necessary, transaction did already complete */
- return SUCCESS;
- }
- return transmit_request(this);
-}
-
-/**
- * Check for transactions in the queue and initiate the first transaction found.
+ * Implementation of ike_sa_t.generate
*/
-static status_t process_transaction_queue(private_ike_sa_t *this)
+static status_t generate_message(private_ike_sa_t *this, message_t *message,
+ packet_t **packet)
{
- if (this->transaction_out)
- {
- /* already a transaction in progress */
- return SUCCESS;
- }
-
- while (TRUE)
- {
- if (this->transaction_queue->remove_first(this->transaction_queue,
- (void**)&this->transaction_out) != SUCCESS)
- {
- /* transaction queue empty */
- return SUCCESS;
- }
- switch (transmit_request(this))
- {
- case SUCCESS:
- return SUCCESS;
- case DESTROY_ME:
- /* critical, IKE_SA unusable, destroy immediately */
- return DESTROY_ME;
- default:
- /* discard transaction, process next one */
- this->transaction_out->destroy(this->transaction_out);
- this->transaction_out = NULL;
- /* handle next transaction */
- continue;
- }
- }
-}
-
-/**
- * Queue a new transaction and execute the next outstanding transaction
- */
-static status_t queue_transaction(private_ike_sa_t *this, transaction_t *transaction, bool prefer)
-{
- /* inject next transaction */
- if (transaction)
- {
- if (prefer)
- {
- this->transaction_queue->insert_first(this->transaction_queue, transaction);
- }
- else
- {
- this->transaction_queue->insert_last(this->transaction_queue, transaction);
- }
- }
- /* process a transaction */
- return process_transaction_queue(this);
-}
-
-/**
- * process an incoming request.
- */
-static status_t process_request(private_ike_sa_t *this, message_t *request)
-{
- transaction_t *last, *current = NULL;
- message_t *response;
- packet_t *packet;
- u_int32_t request_mid;
- status_t status;
-
- request_mid = request->get_message_id(request);
- last = this->transaction_in;
-
- /* check if message ID is correct */
- if (last)
- {
- u_int32_t last_mid = last->get_message_id(last);
-
- if (last_mid == request_mid)
- {
- /* retransmit detected */
- DBG1(DBG_IKE, "received retransmitted request for message "
- "ID %d, retransmitting response", request_mid);
- last->get_response(last, request, &response, &this->transaction_in_next);
- packet = response->get_packet(response);
- charon->send_queue->add(charon->send_queue, packet);
- this->time.outbound = time(NULL);
- return SUCCESS;
- }
-
- if (last_mid > request_mid)
- {
- /* something seriously wrong here, message id may not decrease */
- DBG1(DBG_IKE, "received request with message ID %d, "
- "excepted %d, ingored", request_mid, last_mid + 1);
- return FAILED;
- }
- /* we allow jumps in message IDs, as long as they are incremental */
- if (last_mid + 1 < request_mid)
- {
- DBG1(DBG_IKE, "received request with message ID %d, excepted %d",
- request_mid, last_mid + 1);
- }
- }
- else
- {
- if (request_mid != 0)
- {
- /* warn, but allow it */
- DBG1(DBG_IKE, "first received request has message ID %d, "
- "excepted 0", request_mid);
- }
- }
-
- /* check if we already have a pre-created transaction for this request */
- if (this->transaction_in_next)
- {
- current = this->transaction_in_next;
- this->transaction_in_next = NULL;
- }
- else
- {
- current = transaction_create(&this->public, request);
- if (current == NULL)
- {
- DBG1(DBG_IKE, "no idea how to handle received message (exchange"
- " type %d), ignored", request->get_exchange_type(request));
- return FAILED;
- }
- }
-
- /* send message. get_request() always gives a valid response */
- status = current->get_response(current, request, &response, &this->transaction_in_next);
- if (response->generate(response, this->crypter_out, this->signer_out, &packet) != SUCCESS)
- {
- DBG1(DBG_IKE, "response generation failed, discarding transaction");
- current->destroy(current);
- return FAILED;
- }
-
- charon->send_queue->add(charon->send_queue, packet);
this->time.outbound = time(NULL);
- /* act depending on transaction result */
- switch (status)
- {
- case DESTROY_ME:
- /* transactions says we should destroy the IKE_SA, so do it */
- current->destroy(current);
- return DESTROY_ME;
- default:
- /* store for retransmission, destroy old transaction */
- this->transaction_in = current;
- if (last)
- {
- last->destroy(last);
- }
- return SUCCESS;
- }
-}
-
-/**
- * process an incoming response
- */
-static status_t process_response(private_ike_sa_t *this, message_t *response)
-{
- transaction_t *current, *new = NULL;
-
- current = this->transaction_out;
- /* check if message ID is that of our currently active transaction */
- if (current == NULL ||
- current->get_message_id(current) != response->get_message_id(response))
- {
- DBG1(DBG_IKE, "received response with message ID %d "
- "not requested, ignored", response->get_message_id(response));
- return FAILED;
- }
-
- switch (current->conclude(current, response, &new))
- {
- case DESTROY_ME:
- /* state requested to destroy IKE_SA */
- return DESTROY_ME;
- default:
- /* discard transaction, process next one */
- break;
- }
- /* transaction comleted, remove */
- current->destroy(current);
- this->transaction_out = NULL;
-
- /* queue new transaction */
- return queue_transaction(this, new, TRUE);
+ message->set_ike_sa_id(message, this->ike_sa_id);
+ message->set_destination(message, this->other_host->clone(this->other_host));
+ message->set_source(message, this->my_host->clone(this->my_host));
+ return message->generate(message, this->crypter_out, this->signer_out, packet);
}
/**
* send a notify back to the sender
*/
-static void send_notify_response(private_ike_sa_t *this,
- message_t *request,
+static void send_notify_response(private_ike_sa_t *this, message_t *request,
notify_type_t type)
{
- notify_payload_t *notify;
message_t *response;
- host_t *src, *dst;
packet_t *packet;
response = message_create();
- dst = request->get_source(request);
- src = request->get_destination(request);
- response->set_source(response, src->clone(src));
- response->set_destination(response, dst->clone(dst));
response->set_exchange_type(response, request->get_exchange_type(request));
response->set_request(response, FALSE);
response->set_message_id(response, request->get_message_id(request));
- response->set_ike_sa_id(response, this->ike_sa_id);
- notify = notify_payload_create_from_protocol_and_type(PROTO_NONE, type);
- response->add_payload(response, (payload_t *)notify);
- if (response->generate(response, this->crypter_out, this->signer_out, &packet) != SUCCESS)
+ response->add_notify(response, FALSE, type, chunk_empty);
+ if (this->my_host->is_anyaddr(this->my_host))
{
- response->destroy(response);
- return;
+ this->my_host->destroy(this->my_host);
+ this->my_host = request->get_destination(request);
+ this->my_host = this->my_host->clone(this->my_host);
+ }
+ if (this->other_host->is_anyaddr(this->other_host))
+ {
+ this->other_host->destroy(this->other_host);
+ this->other_host = request->get_source(request);
+ this->other_host = this->other_host->clone(this->other_host);
+ }
+ if (generate_message(this, response, &packet) == SUCCESS)
+ {
+ charon->send_queue->add(charon->send_queue, packet);
}
- charon->send_queue->add(charon->send_queue, packet);
- this->time.outbound = time(NULL);
response->destroy(response);
- return;
}
-
/**
* Implementation of ike_sa_t.process_message.
*/
@@ -875,27 +603,66 @@ static status_t process_message(private_ike_sa_t *this, message_t *message)
exchange_type_names, message->get_exchange_type(message),
message->get_request(message) ? "request" : "response",
message->get_message_id(message));
+ return status;
}
else
{
+ host_t *me, *other;
+
+ me = message->get_destination(message);
+ other = message->get_source(message);
+
+ /* if this IKE_SA is virgin, we check for a connection */
+ if (this->connection == NULL)
+ {
+ this->connection = charon->connections->get_connection_by_hosts(
+ charon->connections, me, other);
+ if (this->connection == NULL)
+ {
+ /* no connection found for these hosts, destroy */
+ send_notify_response(this, message, NO_PROPOSAL_CHOSEN);
+ return DESTROY_ME;
+ }
+ }
+
/* check if message is trustworthy, and update connection information */
if (this->state == IKE_CREATED ||
message->get_exchange_type(message) != IKE_SA_INIT)
{
- update_hosts(this, message->get_destination(message),
- message->get_source(message));
+ update_hosts(this, me, other);
this->time.inbound = time(NULL);
}
- if (is_request)
- {
- status = process_request(this, message);
- }
- else
- {
- status = process_response(this, message);
- }
+ return this->task_manager->process_message(this->task_manager, message);
+ }
+}
+
+/**
+ * apply the connection/policy information to this IKE_SA
+ */
+static void apply_config(private_ike_sa_t *this,
+ connection_t *connection, policy_t *policy)
+{
+ host_t *me, *other;
+ identification_t *my_id, *other_id;
+
+ if (this->connection == NULL && this->policy == NULL)
+ {
+ this->connection = connection;
+ connection->get_ref(connection);
+ this->policy = policy;
+ policy->get_ref(policy);
+
+ me = connection->get_my_host(connection);
+ other = connection->get_other_host(connection);
+ my_id = policy->get_my_id(policy);
+ other_id = policy->get_other_id(policy);
+ set_my_host(this, me->clone(me));
+ set_other_host(this, other->clone(other));
+ DESTROY_IF(this->my_id);
+ DESTROY_IF(this->other_id);
+ this->my_id = my_id->clone(my_id);
+ this->other_id = other_id->clone(other_id);
}
- return status;
}
/**
@@ -904,77 +671,29 @@ static status_t process_message(private_ike_sa_t *this, message_t *message)
static status_t initiate(private_ike_sa_t *this,
connection_t *connection, policy_t *policy)
{
- switch (this->state)
+ task_t *task;
+
+ if (this->state == IKE_CREATED)
{
- case IKE_CREATED:
- {
- /* in state CREATED, we must do the ike_sa_init
- * and ike_auth transactions. Along with these,
- * a CHILD_SA with the supplied policy is set up.
- */
- ike_sa_init_t *ike_sa_init;
-
- DBG2(DBG_IKE, "initiating new IKE_SA for CHILD_SA");
- if (this->my_host->is_anyaddr(this->my_host))
- {
- this->my_host->destroy(this->my_host);
- this->my_host = connection->get_my_host(connection);
- this->my_host = this->my_host->clone(this->my_host);
- }
- if (this->other_host->is_anyaddr(this->other_host))
- {
- this->other_host->destroy(this->other_host);
- this->other_host = connection->get_other_host(connection);
- this->other_host = this->other_host->clone(this->other_host);
- }
- if (this->other_host->is_anyaddr(this->other_host))
- {
- SIG(IKE_UP_START, "establishing new IKE_SA for CHILD_SA");
- SIG(IKE_UP_FAILED, "can not initiate a connection to %%any, aborting");
- policy->destroy(policy);
- connection->destroy(connection);
- return DESTROY_ME;
- }
-
- this->retrans_sequences = connection->get_retrans_seq(connection);
- this->dpd_delay = connection->get_dpd_delay(connection);
-
- this->message_id_out = 1;
- ike_sa_init = ike_sa_init_create(&this->public);
- ike_sa_init->set_config(ike_sa_init, connection, policy);
- return queue_transaction(this, (transaction_t*)ike_sa_init, TRUE);
- }
- case IKE_DELETING:
- case IKE_REKEYING:
- {
- /* if we are in DELETING/REKEYING, we deny set up of a policy.
- * TODO: would it make sense to queue the transaction and adopt
- * all transactions to the new IKE_SA? */
- SIG(IKE_UP_START, "creating CHILD_SA in existing IKE_SA");
- SIG(IKE_UP_FAILED, "creating CHILD_SA discarded, as IKE_SA is in state %N",
- ike_sa_state_names, this->state);
- policy->destroy(policy);
- connection->destroy(connection);
- return FAILED;
- }
- case IKE_CONNECTING:
- case IKE_ESTABLISHED:
- {
- /* if we are ESTABLISHED or CONNECTING, we queue the
- * transaction to create the CHILD_SA. It gets processed
- * when the IKE_SA is ready to do so. We don't need the
- * connection, as the IKE_SA is already established/establishing.
- */
- create_child_sa_t *create_child;
-
- DBG1(DBG_IKE, "creating CHILD_SA in existing IKE_SA");
- connection->destroy(connection);
- create_child = create_child_sa_create(&this->public);
- create_child->set_policy(create_child, policy);
- return queue_transaction(this, (transaction_t*)create_child, FALSE);
- }
- }
- return FAILED;
+ /* if we aren't established/establishing, do so */
+ apply_config(this, connection, policy);
+
+ task = (task_t*)ike_init_create(&this->public, TRUE, NULL);
+ this->task_manager->queue_task(this->task_manager, task);
+ task = (task_t*)ike_natd_create(&this->public, TRUE);
+ this->task_manager->queue_task(this->task_manager, task);
+ task = (task_t*)ike_cert_create(&this->public, TRUE);
+ this->task_manager->queue_task(this->task_manager, task);
+ task = (task_t*)ike_config_create(&this->public, policy);
+ this->task_manager->queue_task(this->task_manager, task);
+ task = (task_t*)ike_auth_create(&this->public, TRUE);
+ this->task_manager->queue_task(this->task_manager, task);
+ }
+
+ task = (task_t*)child_create_create(&this->public, policy);
+ this->task_manager->queue_task(this->task_manager, task);
+
+ return this->task_manager->initiate(this->task_manager);
}
/**
@@ -982,11 +701,11 @@ static status_t initiate(private_ike_sa_t *this,
*/
static status_t acquire(private_ike_sa_t *this, u_int32_t reqid)
{
- connection_t *connection;
policy_t *policy;
iterator_t *iterator;
child_sa_t *current, *child_sa = NULL;
- linked_list_t *my_ts, *other_ts;
+ task_t *task;
+ child_create_t *child_create;
if (this->state == IKE_DELETING)
{
@@ -1014,66 +733,28 @@ static status_t acquire(private_ike_sa_t *this, u_int32_t reqid)
"CHILD_SA not found", reqid);
return FAILED;
}
- my_ts = child_sa->get_my_traffic_selectors(child_sa);
- other_ts = child_sa->get_other_traffic_selectors(child_sa);
- policy = charon->policies->get_policy(charon->policies,
- this->my_id, this->other_id,
- my_ts, other_ts,
- this->my_host, this->other_host);
- if (policy == NULL)
- {
- SIG(CHILD_UP_START, "acquiring CHILD_SA with reqid %d", reqid);
- SIG(CHILD_UP_FAILED, "acquiring CHILD_SA (reqid %d) failed: "
- "no policy found", reqid);
- return FAILED;
- }
+ policy = child_sa->get_policy(child_sa);
- switch (this->state)
+ if (this->state == IKE_CREATED)
{
- case IKE_CREATED:
- {
- ike_sa_init_t *ike_sa_init;
-
- connection = charon->connections->get_connection_by_hosts(
- charon->connections, this->my_host, this->other_host);
-
- if (connection == NULL)
- {
- SIG(CHILD_UP_START, "acquiring CHILD_SA with reqid %d", reqid);
- SIG(CHILD_UP_FAILED, "acquiring CHILD_SA (reqid %d) failed: "
- "no connection found to establsih IKE_SA", reqid);
- policy->destroy(policy);
- return FAILED;
- }
-
- DBG1(DBG_IKE, "establishing IKE_SA to acquire CHILD_SA "
- "with reqid %d", reqid);
-
- this->message_id_out = 1;
- ike_sa_init = ike_sa_init_create(&this->public);
- ike_sa_init->set_config(ike_sa_init, connection, policy);
- /* reuse existing reqid */
- ike_sa_init->set_reqid(ike_sa_init, reqid);
- return queue_transaction(this, (transaction_t*)ike_sa_init, TRUE);
- }
- case IKE_CONNECTING:
- case IKE_ESTABLISHED:
- {
- create_child_sa_t *create_child;
-
- DBG1(DBG_CHD, "acquiring CHILD_SA with reqid %d", reqid);
-
- create_child = create_child_sa_create(&this->public);
- create_child->set_policy(create_child, policy);
- /* reuse existing reqid */
- create_child->set_reqid(create_child, reqid);
- return queue_transaction(this, (transaction_t*)create_child, FALSE);
- }
- default:
- break;
+ task = (task_t*)ike_init_create(&this->public, TRUE, NULL);
+ this->task_manager->queue_task(this->task_manager, task);
+ task = (task_t*)ike_natd_create(&this->public, TRUE);
+ this->task_manager->queue_task(this->task_manager, task);
+ task = (task_t*)ike_cert_create(&this->public, TRUE);
+ this->task_manager->queue_task(this->task_manager, task);
+ task = (task_t*)ike_config_create(&this->public, policy);
+ this->task_manager->queue_task(this->task_manager, task);
+ task = (task_t*)ike_auth_create(&this->public, TRUE);
+ this->task_manager->queue_task(this->task_manager, task);
}
- return FAILED;
+
+ child_create = child_create_create(&this->public, policy);
+ child_create->use_reqid(child_create, reqid);
+ this->task_manager->queue_task(this->task_manager, (task_t*)child_create);
+
+ return this->task_manager->initiate(this->task_manager);
}
/**
@@ -1148,55 +829,25 @@ static status_t route(private_ike_sa_t *this, connection_t *connection, policy_t
switch (this->state)
{
+ case IKE_DELETING:
+ case IKE_REKEYING:
+ SIG(CHILD_ROUTE_FAILED,
+ "unable to route CHILD_SA, as its IKE_SA gets deleted");
+ return FAILED;
case IKE_CREATED:
- case IKE_CONNECTING:
- /* we update IKE_SA information as good as possible,
- * this allows us to set up the SA later when an acquire comes in. */
- if (this->my_id->get_type(this->my_id) == ID_ANY)
- {
- this->my_id->destroy(this->my_id);
- this->my_id = policy->get_my_id(policy);
- this->my_id = this->my_id->clone(this->my_id);
- }
- if (this->other_id->get_type(this->other_id) == ID_ANY)
- {
- this->other_id->destroy(this->other_id);
- this->other_id = policy->get_other_id(policy);
- this->other_id = this->other_id->clone(this->other_id);
- }
- if (this->my_host->is_anyaddr(this->my_host))
- {
- this->my_host->destroy(this->my_host);
- this->my_host = connection->get_my_host(connection);
- this->my_host = this->my_host->clone(this->my_host);
- }
- if (this->other_host->is_anyaddr(this->other_host))
- {
- this->other_host->destroy(this->other_host);
- this->other_host = connection->get_other_host(connection);
- this->other_host = this->other_host->clone(this->other_host);
- }
- set_name(this, connection->get_name(connection));
- this->retrans_sequences = connection->get_retrans_seq(connection);
- this->dpd_delay = connection->get_dpd_delay(connection);
+ /* apply connection information, we need it to acquire */
+ apply_config(this, connection, policy);
break;
+ case IKE_CONNECTING:
case IKE_ESTABLISHED:
- case IKE_REKEYING:
- /* nothing to do. We allow it for rekeying, as it will be
- * adopted by the new IKE_SA */
+ default:
break;
- case IKE_DELETING:
- /* TODO: hanlde this case, create a new IKE_SA and route CHILD_SA */
- SIG(CHILD_ROUTE_FAILED, "unable to route CHILD_SA, as its IKE_SA gets deleted");
- return FAILED;
}
- child_sa = child_sa_create(0, this->my_host, this->other_host,
- this->my_id, this->other_id,
- 0, 0,
- NULL, policy->get_hostaccess(policy),
- FALSE);
- child_sa->set_name(child_sa, policy->get_name(policy));
+ /* install kernel policies */
+ child_sa = child_sa_create(this->my_host, this->other_host,
+ this->my_id, this->other_id, policy, FALSE, 0);
+
my_ts = policy->get_my_traffic_selectors(policy, this->my_host);
other_ts = policy->get_other_traffic_selectors(policy, this->other_host);
status = child_sa->add_policies(child_sa, my_ts, other_ts,
@@ -1269,40 +920,45 @@ static status_t unroute(private_ike_sa_t *this, policy_t *policy)
static status_t send_dpd(private_ike_sa_t *this)
{
send_dpd_job_t *job;
- time_t diff;
+ time_t diff, delay;
+
+ delay = this->connection->get_dpd_delay(this->connection);
- if (this->dpd_delay == 0)
+ if (delay == 0)
{
/* DPD disabled */
return SUCCESS;
}
- if (this->transaction_out)
+ if (this->task_manager->busy(this->task_manager))
{
- /* there is a transaction in progress. Come back later */
+ /* an exchange is in the air, no need to start a DPD check */
diff = 0;
}
else
{
/* check if there was any inbound traffic */
time_t last_in, now;
- last_in = get_time_inbound(this);
+ last_in = get_use_time(this, TRUE);
now = time(NULL);
diff = now - last_in;
- if (diff >= this->dpd_delay)
+ if (diff >= delay)
{
/* to long ago, initiate dead peer detection */
- dead_peer_detection_t *dpd;
- DBG1(DBG_IKE, "sending DPD request");
- dpd = dead_peer_detection_create(&this->public);
- queue_transaction(this, (transaction_t*)dpd, FALSE);
+ task_t *task;
+
+ task = (task_t*)ike_dpd_create(TRUE);
diff = 0;
+ DBG1(DBG_IKE, "sending DPD request");
+
+ this->task_manager->queue_task(this->task_manager, task);
+ this->task_manager->initiate(this->task_manager);
}
}
/* recheck in "interval" seconds */
job = send_dpd_job_create(this->ike_sa_id);
charon->event_queue->add_relative(charon->event_queue, (job_t*)job,
- (this->dpd_delay - diff) * 1000);
+ (delay - diff) * 1000);
return SUCCESS;
}
@@ -1314,7 +970,7 @@ static void send_keepalive(private_ike_sa_t *this)
send_keepalive_job_t *job;
time_t last_out, now, diff, interval;
- last_out = get_time_outbound(this);
+ last_out = get_use_time(this, FALSE);
now = time(NULL);
diff = now - last_out;
@@ -1360,9 +1016,37 @@ static void set_state(private_ike_sa_t *this, ike_sa_state_t state)
if (state == IKE_ESTABLISHED)
{
- this->time.established = time(NULL);
+ job_t *job;
+ u_int32_t now = time(NULL);
+ u_int32_t soft, hard;
+ bool reauth;
+
+ this->time.established = now;
/* start DPD checks */
send_dpd(this);
+
+ /* schedule rekeying/reauthentication */
+ soft = this->connection->get_soft_lifetime(this->connection);
+ hard = this->connection->get_hard_lifetime(this->connection);
+ reauth = this->connection->get_reauth(this->connection);
+ DBG1(DBG_IKE, "scheduling %s in %ds, maximum lifetime %ds",
+ reauth ? "reauthentication": "rekeying", soft, hard);
+
+ if (soft)
+ {
+ this->time.rekey = now + soft;
+ job = (job_t*)rekey_ike_sa_job_create(this->ike_sa_id, reauth);
+ charon->event_queue->add_relative(charon->event_queue, job,
+ soft * 1000);
+ }
+
+ if (hard)
+ {
+ this->time.delete = now + hard;
+ job = (job_t*)delete_ike_sa_job_create(this->ike_sa_id, TRUE);
+ charon->event_queue->add_relative(charon->event_queue, job,
+ hard * 1000);
+ }
}
this->state = state;
@@ -1446,12 +1130,12 @@ static void set_other_id(private_ike_sa_t *this, identification_t *other)
* Implementation of ike_sa_t.derive_keys.
*/
static status_t derive_keys(private_ike_sa_t *this,
- proposal_t *proposal, diffie_hellman_t *dh,
+ proposal_t *proposal, chunk_t secret,
chunk_t nonce_i, chunk_t nonce_r,
bool initiator, prf_t *child_prf, prf_t *old_prf)
{
prf_plus_t *prf_plus;
- chunk_t skeyseed, secret, key, nonces, prf_plus_seed;
+ chunk_t skeyseed, key, nonces, prf_plus_seed;
algorithm_t *algo;
size_t key_size;
crypter_t *crypter_i, *crypter_r;
@@ -1475,7 +1159,6 @@ static status_t derive_keys(private_ike_sa_t *this,
return FAILED;
}
- dh->get_shared_secret(dh, &secret);
DBG4(DBG_IKE, "shared Diffie Hellman secret %B", &secret);
nonces = chunk_cat("cc", nonce_i, nonce_r);
*((u_int64_t*)spi_i.ptr) = this->ike_sa_id->get_initiator_spi(this->ike_sa_id);
@@ -1640,28 +1323,6 @@ static void add_child_sa(private_ike_sa_t *this, child_sa_t *child_sa)
}
/**
- * Implementation of ike_sa_t.has_child_sa.
- */
-static bool has_child_sa(private_ike_sa_t *this, u_int32_t reqid)
-{
- iterator_t *iterator;
- child_sa_t *current;
- bool found = FALSE;
-
- iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
- while (iterator->iterate(iterator, (void**)&current))
- {
- if (current->get_reqid(current) == reqid)
- {
- found = TRUE;
- break;
- }
- }
- iterator->destroy(iterator);
- return found;
-}
-
-/**
* Implementation of ike_sa_t.get_child_sa.
*/
static child_sa_t* get_child_sa(private_ike_sa_t *this, protocol_id_t protocol,
@@ -1672,7 +1333,7 @@ static child_sa_t* get_child_sa(private_ike_sa_t *this, protocol_id_t protocol,
iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
while (iterator->iterate(iterator, (void**)&current))
- {;
+ {
if (current->get_spi(current, inbound) == spi &&
current->get_protocol(current) == protocol)
{
@@ -1696,18 +1357,17 @@ static iterator_t* create_child_sa_iterator(private_ike_sa_t *this)
*/
static status_t rekey_child_sa(private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi)
{
- create_child_sa_t *rekey;
child_sa_t *child_sa;
+ child_rekey_t *child_rekey;
child_sa = get_child_sa(this, protocol, spi, TRUE);
- if (child_sa == NULL)
+ if (child_sa)
{
- return NOT_FOUND;
+ child_rekey = child_rekey_create(&this->public, child_sa);
+ this->task_manager->queue_task(this->task_manager, &child_rekey->task);
+ return this->task_manager->initiate(this->task_manager);
}
-
- rekey = create_child_sa_create(&this->public);
- rekey->rekeys_child(rekey, child_sa);
- return queue_transaction(this, (transaction_t*)rekey, FALSE);
+ return FAILED;
}
/**
@@ -1715,24 +1375,24 @@ static status_t rekey_child_sa(private_ike_sa_t *this, protocol_id_t protocol, u
*/
static status_t delete_child_sa(private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi)
{
- delete_child_sa_t *del;
child_sa_t *child_sa;
+ child_delete_t *child_delete;
child_sa = get_child_sa(this, protocol, spi, TRUE);
- if (child_sa == NULL)
+ if (child_sa)
{
- return NOT_FOUND;
+ child_delete = child_delete_create(&this->public, child_sa);
+ this->task_manager->queue_task(this->task_manager, &child_delete->task);
+ return this->task_manager->initiate(this->task_manager);
}
-
- del = delete_child_sa_create(&this->public);
- del->set_child_sa(del, child_sa);
- return queue_transaction(this, (transaction_t*)del, FALSE);
+ return FAILED;
}
/**
* Implementation of ike_sa_t.destroy_child_sa.
*/
-static status_t destroy_child_sa(private_ike_sa_t *this, protocol_id_t protocol, u_int32_t spi)
+static status_t destroy_child_sa(private_ike_sa_t *this, protocol_id_t protocol,
+ u_int32_t spi)
{
iterator_t *iterator;
child_sa_t *child_sa;
@@ -1755,69 +1415,27 @@ static status_t destroy_child_sa(private_ike_sa_t *this, protocol_id_t protocol,
}
/**
- * Implementation of ike_sa_t.set_lifetimes.
- */
-static void set_lifetimes(private_ike_sa_t *this, bool reauth,
- u_int32_t soft_lifetime, u_int32_t hard_lifetime)
-{
- job_t *job;
- u_int32_t now = time(NULL);
-
- this->reauth = reauth;
-
- if (soft_lifetime)
- {
- this->time.rekey = now + soft_lifetime;
- job = (job_t*)rekey_ike_sa_job_create(this->ike_sa_id, reauth);
- charon->event_queue->add_relative(charon->event_queue, job,
- soft_lifetime * 1000);
- }
-
- if (hard_lifetime)
- {
- this->time.delete = now + hard_lifetime;
- job = (job_t*)delete_ike_sa_job_create(this->ike_sa_id, TRUE);
- charon->event_queue->add_relative(charon->event_queue, job,
- hard_lifetime * 1000);
- }
-}
-
-/**
* Implementation of public_ike_sa_t.delete.
*/
static status_t delete_(private_ike_sa_t *this)
{
+ ike_delete_t *ike_delete;
+
switch (this->state)
{
- case IKE_CONNECTING:
- {
- /* this may happen if a half open IKE_SA gets closed after a
- * timeout. We signal here UP_FAILED to complete the SIG schema */
- SIG(IKE_UP_FAILED, "half open IKE_SA deleted after timeout");
- return DESTROY_ME;
- }
case IKE_ESTABLISHED:
- {
- delete_ike_sa_t *delete_ike_sa;
- if (this->transaction_out)
- {
- /* already a transaction in progress. As this may hang
- * around a while, we don't inform the other peer. */
- return DESTROY_ME;
- }
- delete_ike_sa = delete_ike_sa_create(&this->public);
- return queue_transaction(this, (transaction_t*)delete_ike_sa, FALSE);
- }
- case IKE_CREATED:
- case IKE_DELETING:
+ DBG1(DBG_IKE, "deleting IKE_SA");
+ /* do not log when rekeyed */
+ case IKE_REKEYING:
+ ike_delete = ike_delete_create(&this->public, TRUE);
+ this->task_manager->queue_task(this->task_manager, &ike_delete->task);
+ return this->task_manager->initiate(this->task_manager);
default:
- {
- SIG(IKE_DOWN_START, "closing IKE_SA");
- SIG(IKE_DOWN_SUCCESS, "IKE_SA closed between %H[%D]...%H[%D]",
- this->my_host, this->my_id, this->other_host, this->other_id);
- return DESTROY_ME;
- }
+ DBG1(DBG_IKE, "destroying IKE_SA in state %N without notification",
+ ike_sa_state_names, this->state);
+ break;
}
+ return DESTROY_ME;
}
/**
@@ -1825,161 +1443,373 @@ static status_t delete_(private_ike_sa_t *this)
*/
static status_t rekey(private_ike_sa_t *this)
{
- rekey_ike_sa_t *rekey_ike_sa;
-
- DBG1(DBG_IKE, "rekeying IKE_SA between %H[%D]..%H[%D]",
- this->my_host, this->my_id, this->other_host, this->other_id);
+ ike_rekey_t *ike_rekey;
- if (this->state != IKE_ESTABLISHED)
- {
- SIG(IKE_REKEY_START, "rekeying IKE_SA");
- SIG(IKE_REKEY_FAILED, "unable to rekey IKE_SA in state %N",
- ike_sa_state_names, this->state);
- return FAILED;
- }
+ ike_rekey = ike_rekey_create(&this->public, TRUE);
- rekey_ike_sa = rekey_ike_sa_create(&this->public);
- return queue_transaction(this, (transaction_t*)rekey_ike_sa, FALSE);
+ this->task_manager->queue_task(this->task_manager, &ike_rekey->task);
+ return this->task_manager->initiate(this->task_manager);
}
/**
- * Implementation of ike_sa_t.reauth.
+ * Implementation of ike_sa_t.reestablish
*/
-static status_t reauth(private_ike_sa_t *this)
+static void reestablish(private_ike_sa_t *this)
{
- connection_t *connection;
- child_sa_t *child_sa;
+ ike_sa_id_t *other_id;
+ private_ike_sa_t *other;
iterator_t *iterator;
+ child_sa_t *child_sa;
+ policy_t *policy;
+ task_t *task;
+ job_t *job;
- DBG1(DBG_IKE, "reauthenticating IKE_SA between %H[%D]..%H[%D]",
- this->my_host, this->my_id, this->other_host, this->other_id);
+ other_id = ike_sa_id_create(0, 0, TRUE);
+ other = (private_ike_sa_t*)charon->ike_sa_manager->checkout(
+ charon->ike_sa_manager, other_id);
+ other_id->destroy(other_id);
- /* get a connection to initiate */
- connection = charon->connections->get_connection_by_hosts(charon->connections,
- this->my_host, this->other_host);
- if (connection == NULL)
+ apply_config(other, this->connection, this->policy);
+
+ if (this->state == IKE_ESTABLISHED)
{
- DBG1(DBG_IKE, "no connection found to reauthenticate");
- return FAILED;
+ task = (task_t*)ike_init_create(&other->public, TRUE, NULL);
+ other->task_manager->queue_task(other->task_manager, task);
+ task = (task_t*)ike_natd_create(&other->public, TRUE);
+ other->task_manager->queue_task(other->task_manager, task);
+ task = (task_t*)ike_cert_create(&other->public, TRUE);
+ other->task_manager->queue_task(other->task_manager, task);
+ task = (task_t*)ike_config_create(&other->public, other->policy);
+ other->task_manager->queue_task(other->task_manager, task);
+ task = (task_t*)ike_auth_create(&other->public, TRUE);
+ other->task_manager->queue_task(other->task_manager, task);
}
- /* queue CREATE_CHILD_SA transactions to set up all CHILD_SAs */
+ other->task_manager->adopt_tasks(other->task_manager, this->task_manager);
+
+ /* Create task for established children, adopt routed children directly */
iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
- while (iterator->iterate(iterator, (void**)&child_sa))
+ while(iterator->iterate(iterator, (void**)&child_sa))
{
- job_t *job;
- policy_t *policy;
- linked_list_t *my_ts, *other_ts;
- host_t *other;
-
- my_ts = child_sa->get_my_traffic_selectors(child_sa);
- other_ts = child_sa->get_other_traffic_selectors(child_sa);
- policy = charon->policies->get_policy(charon->policies,
- this->my_id, this->other_id, my_ts, other_ts,
- this->my_host, this->other_host);
- if (policy == NULL)
+ switch (child_sa->get_state(child_sa))
{
- DBG1(DBG_IKE, "policy not found to recreate CHILD_SA, skipped");
- continue;
+ case CHILD_ROUTED:
+ {
+ iterator->remove(iterator);
+ other->child_sas->insert_first(other->child_sas, child_sa);
+ break;
+ }
+ default:
+ {
+ policy = child_sa->get_policy(child_sa);
+ task = (task_t*)child_create_create(&other->public, policy);
+ other->task_manager->queue_task(other->task_manager, task);
+ break;
+ }
}
- connection->get_ref(connection);
- other = this->other_host->clone(this->other_host);
- job = (job_t*)initiate_job_create(connection, other, policy);
- charon->job_queue->add(charon->job_queue, job);
}
iterator->destroy(iterator);
- connection->destroy(connection);
- /* delete the old IKE_SA
- * TODO: we should delay the delete to avoid connectivity gaps?! */
- return delete_(this);
+ other->task_manager->initiate(other->task_manager);
+
+ charon->ike_sa_manager->checkin(charon->ike_sa_manager, &other->public);
+
+ job = (job_t*)delete_ike_sa_job_create(this->ike_sa_id, TRUE);
+ charon->job_queue->add(charon->job_queue, job);
}
/**
- * Implementation of ike_sa_t.get_rekeying_transaction.
+ * Implementation of ike_sa_t.inherit.
*/
-static transaction_t* get_rekeying_transaction(private_ike_sa_t *this)
+static void inherit(private_ike_sa_t *this, private_ike_sa_t *other)
{
- return this->rekeying_transaction;
+ child_sa_t *child_sa;
+ host_t *ip;
+
+ /* apply hosts and ids */
+ this->my_host->destroy(this->my_host);
+ this->other_host->destroy(this->other_host);
+ this->my_id->destroy(this->my_id);
+ this->other_id->destroy(this->other_id);
+ this->my_host = other->my_host->clone(other->my_host);
+ this->other_host = other->other_host->clone(other->other_host);
+ this->my_id = other->my_id->clone(other->my_id);
+ this->other_id = other->other_id->clone(other->other_id);
+
+ /* apply virtual assigned IPs... */
+ if (other->my_virtual_ip)
+ {
+ this->my_virtual_ip = other->my_virtual_ip;
+ other->my_virtual_ip = NULL;
+ }
+ if (other->other_virtual_ip)
+ {
+ this->other_virtual_ip = other->other_virtual_ip;
+ other->other_virtual_ip = NULL;
+ }
+
+ /* ... and DNS servers */
+ while (other->dns_servers->remove_last(other->dns_servers,
+ (void**)&ip) == SUCCESS)
+ {
+ this->dns_servers->insert_first(this->dns_servers, ip);
+ }
+
+ /* adopt all children */
+ while (other->child_sas->remove_last(other->child_sas,
+ (void**)&child_sa) == SUCCESS)
+ {
+ this->child_sas->insert_first(this->child_sas, (void*)child_sa);
+ }
}
/**
- * Implementation of ike_sa_t.set_rekeying_transaction.
+ * Implementation of ike_sa_t.is_natt_enabled.
*/
-static void set_rekeying_transaction(private_ike_sa_t *this, transaction_t *rekey)
+static bool is_natt_enabled(private_ike_sa_t *this)
{
- this->rekeying_transaction = rekey;
+ return this->nat_here || this->nat_there;
}
/**
- * Implementation of ike_sa_t.adopt_children.
+ * Implementation of ike_sa_t.enable_natt.
*/
-static void adopt_children(private_ike_sa_t *this, private_ike_sa_t *other)
+static void enable_natt(private_ike_sa_t *this, bool local)
{
- child_sa_t *child_sa;
-
- while (other->child_sas->remove_last(other->child_sas,
- (void**)&child_sa) == SUCCESS)
+ if (local)
{
- this->child_sas->insert_first(this->child_sas, (void*)child_sa);
+ DBG1(DBG_IKE, "local host is behind NAT, scheduling keep alives");
+ this->nat_here = TRUE;
+ send_keepalive(this);
+ }
+ else
+ {
+ DBG1(DBG_IKE, "remote host is behind NAT");
+ this->nat_there = TRUE;
}
}
/**
- * Implementation of ike_sa_t.get_next_message_id.
+ * Implementation of ike_sa_t.reset
*/
-static u_int32_t get_next_message_id (private_ike_sa_t *this)
+static void reset(private_ike_sa_t *this)
{
- return this->message_id_out++;
+ /* the responder ID is reset, as peer may choose another one */
+ if (this->ike_sa_id->is_initiator(this->ike_sa_id))
+ {
+ this->ike_sa_id->set_responder_spi(this->ike_sa_id, 0);
+ }
+
+ set_state(this, IKE_CREATED);
+
+ this->task_manager->reset(this->task_manager);
}
/**
- * Implementation of ike_sa_t.is_natt_enabled.
+ * Implementation of ike_sa_t.set_virtual_ip
*/
-static bool is_natt_enabled(private_ike_sa_t *this)
+static void set_virtual_ip(private_ike_sa_t *this, bool local, host_t *ip)
{
- return this->nat_here || this->nat_there;
+ if (local)
+ {
+ DBG1(DBG_IKE, "installing new virtual IP %H", ip);
+ if (this->my_virtual_ip)
+ {
+ DBG1(DBG_IKE, "removing old virtual IP %H", this->my_virtual_ip);
+ charon->kernel_interface->del_ip(charon->kernel_interface,
+ this->my_virtual_ip,
+ this->other_host);
+ this->my_virtual_ip->destroy(this->my_virtual_ip);
+ }
+ if (charon->kernel_interface->add_ip(charon->kernel_interface, ip,
+ this->other_host) == SUCCESS)
+ {
+ this->my_virtual_ip = ip->clone(ip);
+ }
+ else
+ {
+ DBG1(DBG_IKE, "installing virtual IP %H failed", ip);
+ this->my_virtual_ip = NULL;
+ }
+ }
+ else
+ {
+ DESTROY_IF(this->other_virtual_ip);
+ this->other_virtual_ip = ip->clone(ip);
+ }
}
/**
- * Implementation of ike_sa_t.enable_natt.
+ * Implementation of ike_sa_t.get_virtual_ip
*/
-static void enable_natt(private_ike_sa_t *this, bool local)
+static host_t* get_virtual_ip(private_ike_sa_t *this, bool local)
{
if (local)
{
- DBG1(DBG_IKE, "local host is behind NAT, using NAT-T, "
- "scheduled keep alives");
- this->nat_here = TRUE;
- send_keepalive(this);
+ return this->my_virtual_ip;
}
else
{
- DBG1(DBG_IKE, "remote host is behind NAT, using NAT-T");
- this->nat_there = TRUE;
+ return this->other_virtual_ip;
}
}
/**
+ * Implementation of ike_sa_t.remove_dns_server
+ */
+static void remove_dns_servers(private_ike_sa_t *this)
+{
+ FILE *file;
+ struct stat stats;
+ chunk_t contents, line, orig_line, token;
+ char string[INET6_ADDRSTRLEN];
+ host_t *ip;
+ iterator_t *iterator;
+
+ if (this->dns_servers->get_count(this->dns_servers) == 0)
+ {
+ /* don't touch anything if we have no nameservers installed */
+ return;
+ }
+
+ file = fopen(RESOLV_CONF, "r");
+ if (file == NULL || stat(RESOLV_CONF, &stats) != 0)
+ {
+ DBG1(DBG_IKE, "unable to open DNS configuration file %s: %m", RESOLV_CONF);
+ return;
+ }
+
+ contents = chunk_alloca((size_t)stats.st_size);
+
+ if (fread(contents.ptr, 1, contents.len, file) != contents.len)
+ {
+ DBG1(DBG_IKE, "unable to read DNS configuration file: %m");
+ fclose(file);
+ return;
+ }
+
+ fclose(file);
+ file = fopen(RESOLV_CONF, "w");
+ if (file == NULL)
+ {
+ DBG1(DBG_IKE, "unable to open DNS configuration file %s: %m", RESOLV_CONF);
+ return;
+ }
+
+ iterator = this->dns_servers->create_iterator(this->dns_servers, TRUE);
+ while (fetchline(&contents, &line))
+ {
+ bool found = FALSE;
+ orig_line = line;
+ if (extract_token(&token, ' ', &line) &&
+ strncasecmp(token.ptr, "nameserver", token.len) == 0)
+ {
+ if (!extract_token(&token, ' ', &line))
+ {
+ token = line;
+ }
+ iterator->reset(iterator);
+ while (iterator->iterate(iterator, (void**)&ip))
+ {
+ snprintf(string, sizeof(string), "%H", ip);
+ if (strlen(string) == token.len &&
+ strncmp(token.ptr, string, token.len) == 0)
+ {
+ iterator->remove(iterator);
+ ip->destroy(ip);
+ found = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (!found)
+ {
+ /* write line untouched back to file */
+ fwrite(orig_line.ptr, orig_line.len, 1, file);
+ fprintf(file, "\n");
+ }
+ }
+ iterator->destroy(iterator);
+ fclose(file);
+}
+
+/**
+ * Implementation of ike_sa_t.add_dns_server
+ */
+static void add_dns_server(private_ike_sa_t *this, host_t *dns)
+{
+ FILE *file;
+ struct stat stats;
+ chunk_t contents;
+
+ DBG1(DBG_IKE, "installing DNS server %H", dns);
+
+ file = fopen(RESOLV_CONF, "a+");
+ if (file == NULL || stat(RESOLV_CONF, &stats) != 0)
+ {
+ DBG1(DBG_IKE, "unable to open DNS configuration file %s: %m", RESOLV_CONF);
+ return;
+ }
+
+ contents = chunk_alloca(stats.st_size);
+
+ if (fread(contents.ptr, 1, contents.len, file) != contents.len)
+ {
+ DBG1(DBG_IKE, "unable to read DNS configuration file: %m");
+ fclose(file);
+ return;
+ }
+
+ fclose(file);
+ file = fopen(RESOLV_CONF, "w");
+ if (file == NULL)
+ {
+ DBG1(DBG_IKE, "unable to open DNS configuration file %s: %m", RESOLV_CONF);
+ return;
+ }
+
+ if (fprintf(file, "nameserver %H # added by strongSwan, assigned by %D\n",
+ dns, this->other_id) < 0)
+ {
+ DBG1(DBG_IKE, "unable to write DNS configuration: %m");
+ }
+ else
+ {
+ this->dns_servers->insert_last(this->dns_servers, dns->clone(dns));
+ }
+ fwrite(contents.ptr, contents.len, 1, file);
+
+ fclose(file);
+}
+
+/**
* output handler in printf()
*/
static int print(FILE *stream, const struct printf_info *info,
const void *const *args)
{
int written = 0;
+ bool reauth = FALSE;
private_ike_sa_t *this = *((private_ike_sa_t**)(args[0]));
+ if (this->connection)
+ {
+ reauth = this->connection->get_reauth(this->connection);
+ }
+
if (this == NULL)
{
return fprintf(stream, "(null)");
}
- written = fprintf(stream, "%12s: %N, %H[%D]...%H[%D]",
- this->name, ike_sa_state_names, this->state,
- this->my_host, this->my_id, this->other_host, this->other_id);
- written += fprintf(stream, "\n%12s: IKE SPIs: %J, %s in %ds",
- this->name, this->ike_sa_id,
- this->reauth? "reauthentication":"rekeying",
+ written = fprintf(stream, "%12s[%d]: %N, %H[%D]...%H[%D]", get_name(this),
+ this->unique_id, ike_sa_state_names, this->state,
+ this->my_host, this->my_id, this->other_host,
+ this->other_id);
+ written += fprintf(stream, "\n%12s[%d]: IKE SPIs: %J, %s in %ds",
+ get_name(this), this->unique_id, this->ike_sa_id,
+ this->connection && reauth? "reauthentication":"rekeying",
this->time.rekey - time(NULL));
if (info->alt)
@@ -2003,11 +1833,7 @@ static void __attribute__ ((constructor))print_register()
static void destroy(private_ike_sa_t *this)
{
this->child_sas->destroy_offset(this->child_sas, offsetof(child_sa_t, destroy));
- this->transaction_queue->destroy_offset(this->transaction_queue, offsetof(transaction_t, destroy));
- DESTROY_IF(this->transaction_in);
- DESTROY_IF(this->transaction_in_next);
- DESTROY_IF(this->transaction_out);
DESTROY_IF(this->crypter_in);
DESTROY_IF(this->crypter_out);
DESTROY_IF(this->signer_in);
@@ -2017,13 +1843,27 @@ static void destroy(private_ike_sa_t *this)
DESTROY_IF(this->auth_verify);
DESTROY_IF(this->auth_build);
+ if (this->my_virtual_ip)
+ {
+ charon->kernel_interface->del_ip(charon->kernel_interface,
+ this->my_virtual_ip, this->other_host);
+ this->my_virtual_ip->destroy(this->my_virtual_ip);
+ }
+ DESTROY_IF(this->other_virtual_ip);
+
+ remove_dns_servers(this);
+ this->dns_servers->destroy_offset(this->dns_servers, offsetof(host_t, destroy));
+
DESTROY_IF(this->my_host);
DESTROY_IF(this->other_host);
DESTROY_IF(this->my_id);
DESTROY_IF(this->other_id);
- free(this->name);
+ DESTROY_IF(this->connection);
+ DESTROY_IF(this->policy);
+
this->ike_sa_id->destroy(this->ike_sa_id);
+ this->task_manager->destroy(this->task_manager);
free(this);
}
@@ -2033,17 +1873,21 @@ static void destroy(private_ike_sa_t *this)
ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id)
{
private_ike_sa_t *this = malloc_thing(private_ike_sa_t);
+ static u_int32_t unique_id = 0;
/* Public functions */
this->public.get_state = (ike_sa_state_t(*)(ike_sa_t*)) get_state;
this->public.set_state = (void(*)(ike_sa_t*,ike_sa_state_t)) set_state;
this->public.get_name = (char*(*)(ike_sa_t*))get_name;
- this->public.set_name = (void(*)(ike_sa_t*,char*))set_name;
this->public.process_message = (status_t(*)(ike_sa_t*, message_t*)) process_message;
this->public.initiate = (status_t(*)(ike_sa_t*,connection_t*,policy_t*)) initiate;
this->public.route = (status_t(*)(ike_sa_t*,connection_t*,policy_t*)) route;
this->public.unroute = (status_t(*)(ike_sa_t*,policy_t*)) unroute;
this->public.acquire = (status_t(*)(ike_sa_t*,u_int32_t)) acquire;
+ this->public.get_connection = (connection_t*(*)(ike_sa_t*))get_connection;
+ this->public.set_connection = (void(*)(ike_sa_t*,connection_t*))set_connection;
+ this->public.get_policy = (policy_t*(*)(ike_sa_t*))get_policy;
+ this->public.set_policy = (void(*)(ike_sa_t*,policy_t*))set_policy;
this->public.get_id = (ike_sa_id_t*(*)(ike_sa_t*)) get_id;
this->public.get_my_host = (host_t*(*)(ike_sa_t*)) get_my_host;
this->public.set_my_host = (void(*)(ike_sa_t*,host_t*)) set_my_host;
@@ -2053,8 +1897,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id)
this->public.set_my_id = (void(*)(ike_sa_t*,identification_t*)) set_my_id;
this->public.get_other_id = (identification_t*(*)(ike_sa_t*)) get_other_id;
this->public.set_other_id = (void(*)(ike_sa_t*,identification_t*)) set_other_id;
- this->public.get_next_message_id = (u_int32_t(*)(ike_sa_t*)) get_next_message_id;
- this->public.retransmit_request = (status_t (*) (ike_sa_t *, u_int32_t)) retransmit_request;
+ this->public.retransmit = (status_t (*) (ike_sa_t *, u_int32_t)) retransmit;
this->public.delete = (status_t(*)(ike_sa_t*))delete_;
this->public.destroy = (void(*)(ike_sa_t*))destroy;
this->public.send_dpd = (status_t (*)(ike_sa_t*)) send_dpd;
@@ -2063,9 +1906,8 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id)
this->public.get_child_prf = (prf_t *(*) (ike_sa_t *)) get_child_prf;
this->public.get_auth_verify = (prf_t *(*) (ike_sa_t *)) get_auth_verify;
this->public.get_auth_build = (prf_t *(*) (ike_sa_t *)) get_auth_build;
- this->public.derive_keys = (status_t (*) (ike_sa_t *,proposal_t*,diffie_hellman_t*,chunk_t,chunk_t,bool,prf_t*,prf_t*)) derive_keys;
+ this->public.derive_keys = (status_t (*) (ike_sa_t *,proposal_t*,chunk_t,chunk_t,chunk_t,bool,prf_t*,prf_t*)) derive_keys;
this->public.add_child_sa = (void (*) (ike_sa_t*,child_sa_t*)) add_child_sa;
- this->public.has_child_sa = (bool(*)(ike_sa_t*,u_int32_t)) has_child_sa;
this->public.get_child_sa = (child_sa_t* (*)(ike_sa_t*,protocol_id_t,u_int32_t,bool)) get_child_sa;
this->public.create_child_sa_iterator = (iterator_t* (*)(ike_sa_t*)) create_child_sa_iterator;
this->public.rekey_child_sa = (status_t(*)(ike_sa_t*,protocol_id_t,u_int32_t)) rekey_child_sa;
@@ -2073,20 +1915,21 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id)
this->public.destroy_child_sa = (status_t (*)(ike_sa_t*,protocol_id_t,u_int32_t))destroy_child_sa;
this->public.enable_natt = (void(*)(ike_sa_t*, bool)) enable_natt;
this->public.is_natt_enabled = (bool(*)(ike_sa_t*)) is_natt_enabled;
- this->public.set_lifetimes = (void(*)(ike_sa_t*,bool,u_int32_t,u_int32_t))set_lifetimes;
- this->public.apply_connection = (void(*)(ike_sa_t*,connection_t*))apply_connection;
this->public.rekey = (status_t(*)(ike_sa_t*))rekey;
- this->public.reauth = (status_t(*)(ike_sa_t*))reauth;
- this->public.get_rekeying_transaction = (transaction_t*(*)(ike_sa_t*))get_rekeying_transaction;
- this->public.set_rekeying_transaction = (void(*)(ike_sa_t*,transaction_t*))set_rekeying_transaction;
- this->public.adopt_children = (void(*)(ike_sa_t*,ike_sa_t*))adopt_children;
+ this->public.reestablish = (void(*)(ike_sa_t*))reestablish;
+ this->public.inherit = (void(*)(ike_sa_t*,ike_sa_t*))inherit;
+ this->public.generate_message = (status_t(*)(ike_sa_t*,message_t*,packet_t**))generate_message;
+ this->public.reset = (void(*)(ike_sa_t*))reset;
+ this->public.get_unique_id = (u_int32_t(*)(ike_sa_t*))get_unique_id;
+ this->public.set_virtual_ip = (void(*)(ike_sa_t*,bool,host_t*))set_virtual_ip;
+ this->public.get_virtual_ip = (host_t*(*)(ike_sa_t*,bool))get_virtual_ip;
+ this->public.add_dns_server = (void(*)(ike_sa_t*,host_t*))add_dns_server;
/* initialize private fields */
this->ike_sa_id = ike_sa_id->clone(ike_sa_id);
- this->name = strdup("(uninitialized)");
this->child_sas = linked_list_create();
- this->my_host = host_create_from_string("0.0.0.0", 0);
- this->other_host = host_create_from_string("0.0.0.0", 0);
+ this->my_host = host_create_any(AF_INET);
+ this->other_host = host_create_any(AF_INET);
this->my_id = identification_create_from_encoding(ID_ANY, chunk_empty);
this->other_id = identification_create_from_encoding(ID_ANY, chunk_empty);
this->crypter_in = NULL;
@@ -2099,21 +1942,18 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id)
this->child_prf = NULL;
this->nat_here = FALSE;
this->nat_there = FALSE;
- this->transaction_queue = linked_list_create();
- this->transaction_in = NULL;
- this->transaction_in_next = NULL;
- this->transaction_out = NULL;
- this->rekeying_transaction = NULL;
this->state = IKE_CREATED;
- this->message_id_out = 0;
- /* set to NOW, as when we rekey an existing IKE_SA no message is exchanged
- * and inbound therefore uninitialized */
this->time.inbound = this->time.outbound = time(NULL);
this->time.established = 0;
this->time.rekey = 0;
this->time.delete = 0;
- this->dpd_delay = 0;
- this->retrans_sequences = 0;
+ this->connection = NULL;
+ this->policy = NULL;
+ this->task_manager = task_manager_create(&this->public);
+ this->unique_id = ++unique_id;
+ this->my_virtual_ip = NULL;
+ this->other_virtual_ip = NULL;
+ this->dns_servers = linked_list_create();
return &this->public;
}
diff --git a/src/charon/sa/ike_sa.h b/src/charon/sa/ike_sa.h
index 433e37292..2ba9313ab 100644
--- a/src/charon/sa/ike_sa.h
+++ b/src/charon/sa/ike_sa.h
@@ -33,7 +33,7 @@ typedef struct ike_sa_t ike_sa_t;
#include <encoding/payloads/proposal_substructure.h>
#include <sa/ike_sa_id.h>
#include <sa/child_sa.h>
-#include <sa/transactions/transaction.h>
+#include <sa/tasks/task.h>
#include <config/configuration.h>
#include <utils/randomizer.h>
#include <crypto/prfs/prf.h>
@@ -121,7 +121,7 @@ extern enum_name_t *ike_sa_state_names;
* An IKE_SA contains crypto information related to a connection
* with a peer. It contains multiple IPsec CHILD_SA, for which
* it is responsible. All traffic is handled by an IKE_SA, using
- * transactions.
+ * the task manager and its tasks.
*
* @b Constructors:
* - ike_sa_create()
@@ -141,6 +141,14 @@ struct ike_sa_t {
ike_sa_id_t* (*get_id) (ike_sa_t *this);
/**
+ * @brief Get the numerical ID uniquely defining this IKE_SA.
+ *
+ * @param this calling object
+ * @return unique ID
+ */
+ u_int32_t (*get_unique_id) (ike_sa_t *this);
+
+ /**
* @brief Get the state of the IKE_SA.
*
* @param this calling object
@@ -165,14 +173,6 @@ struct ike_sa_t {
char* (*get_name) (ike_sa_t *this);
/**
- * @brief Set the name of the connection this IKE_SA uses.
- *
- * @param this calling object
- * @param name name, gets cloned
- */
- void (*set_name) (ike_sa_t *this, char* name);
-
- /**
* @brief Get the own host address.
*
* @param this calling object
@@ -235,6 +235,38 @@ struct ike_sa_t {
* @param other identification
*/
void (*set_other_id) (ike_sa_t *this, identification_t *other);
+
+ /**
+ * @brief Get the connection used by this IKE_SA.
+ *
+ * @param this calling object
+ * @return connection
+ */
+ connection_t* (*get_connection) (ike_sa_t *this);
+
+ /**
+ * @brief Set the connection to use with this IKE_SA.
+ *
+ * @param this calling object
+ * @param connection connection to use
+ */
+ void (*set_connection) (ike_sa_t *this, connection_t* connection);
+
+ /**
+ * @brief Get the policy used by this IKE_SA.
+ *
+ * @param this calling object
+ * @return policy
+ */
+ policy_t* (*get_policy) (ike_sa_t *this);
+
+ /**
+ * @brief Set the policy to use with this IKE_SA.
+ *
+ * @param this calling object
+ * @param policy policy to use
+ */
+ void (*set_policy) (ike_sa_t *this, policy_t *policy);
/**
* @brief Initiate a new connection.
@@ -309,17 +341,6 @@ struct ike_sa_t {
status_t (*delete) (ike_sa_t *this);
/**
- * @brief Retransmits a request.
- *
- * @param this calling object
- * @param message_id ID of the request to retransmit
- * @return
- * - SUCCESS
- * - NOT_FOUND if request doesn't have to be retransmited
- */
- status_t (*retransmit_request) (ike_sa_t *this, u_int32_t message_id);
-
- /**
* @brief Processes a incoming IKEv2-Message.
*
* Message processing may fail. If a critical failure occurs,
@@ -327,7 +348,7 @@ struct ike_sa_t {
* destroy the IKE_SA immediatly, as it is unusable.
*
* @param this calling object
- * @param[in] message message to process
+ * @param message message to process
* @return
* - SUCCESS
* - FAILED
@@ -336,44 +357,33 @@ struct ike_sa_t {
status_t (*process_message) (ike_sa_t *this, message_t *message);
/**
- * @brief Get the next message ID for a request.
- *
+ * @brief Generate a IKE message to send it to the peer.
+ *
+ * This method generates all payloads in the message and encrypts/signs
+ * the packet.
+ *
* @param this calling object
- * @return the next message id
+ * @param message message to generate
+ * @param packet generated output packet
+ * @return
+ * - SUCCESS
+ * - FAILED
+ * - DESTROY_ME if this IKE_SA MUST be deleted
*/
- u_int32_t (*get_next_message_id) (ike_sa_t *this);
+ status_t (*generate_message) (ike_sa_t *this, message_t *message,
+ packet_t **packet);
/**
- * @brief Check if NAT traversal is enabled for this IKE_SA.
- *
- * @param this calling object
- * @return TRUE if NAT traversal enabled
- */
- bool (*is_natt_enabled) (ike_sa_t *this);
-
- /**
- * @brief Enable NAT detection for this IKE_SA.
- *
- * If a Network address translation is detected with
- * NAT_DETECTION notifys, a SA must switch to ports
- * 4500. To enable this behavior, call enable_natt().
- * It is relevant which peer is NATted, this is specified
- * with the "local" parameter. Call it twice when both
- * are NATted.
- *
- * @param this calling object
- * @param local TRUE, if we are NATted, FALSE if other
- */
- void (*enable_natt) (ike_sa_t *this, bool local);
-
- /**
- * @brief Apply connection parameters for this IKE_SA.
+ * @brief Retransmits a request.
*
- * @param this calling object
- * @param connection connection definition
+ * @param this calling object
+ * @param message_id ID of the request to retransmit
+ * @return
+ * - SUCCESS
+ * - NOT_FOUND if request doesn't have to be retransmited
*/
- void (*apply_connection) (ike_sa_t *this, connection_t *connection);
-
+ status_t (*retransmit) (ike_sa_t *this, u_int32_t message_id);
+
/**
* @brief Sends a DPD request to the peer.
*
@@ -399,6 +409,29 @@ struct ike_sa_t {
* @param this calling object
*/
void (*send_keepalive) (ike_sa_t *this);
+
+ /**
+ * @brief Check if NAT traversal is enabled for this IKE_SA.
+ *
+ * @param this calling object
+ * @return TRUE if NAT traversal enabled
+ */
+ bool (*is_natt_enabled) (ike_sa_t *this);
+
+ /**
+ * @brief Enable NAT detection for this IKE_SA.
+ *
+ * If a Network address translation is detected with
+ * NAT_DETECTION notifys, a SA must switch to ports
+ * 4500. To enable this behavior, call enable_natt().
+ * It is relevant which peer is NATted, this is specified
+ * with the "local" parameter. Call it twice when both
+ * are NATted.
+ *
+ * @param this calling object
+ * @param local TRUE, if we are NATted, FALSE if other
+ */
+ void (*enable_natt) (ike_sa_t *this, bool local);
/**
* @brief Derive all keys and create the transforms for IKE communication.
@@ -411,15 +444,14 @@ struct ike_sa_t {
*
* @param this calling object
* @param proposal proposal which contains algorithms to use
- * @param dh diffie hellman object with shared secret
+ * @param secret secret derived from DH exchange, gets freed
* @param nonce_i initiators nonce
* @param nonce_r responders nonce
* @param initiator TRUE if initiator, FALSE otherwise
* @param child_prf PRF with SK_d key when rekeying, NULL otherwise
* @param old_prf general purpose PRF of old SA when rekeying
*/
- status_t (*derive_keys)(ike_sa_t *this, proposal_t* proposal,
- diffie_hellman_t *dh,
+ status_t (*derive_keys)(ike_sa_t *this, proposal_t* proposal, chunk_t secret,
chunk_t nonce_i, chunk_t nonce_r,
bool initiator, prf_t *child_prf, prf_t *old_prf);
@@ -464,15 +496,6 @@ struct ike_sa_t {
void (*add_child_sa) (ike_sa_t *this, child_sa_t *child_sa);
/**
- * @brief Check if an IKE_SA has one or more CHILD_SAs with a given reqid.
- *
- * @param this calling object
- * @param reqid reqid of the CHILD
- * @return TRUE if it has such a CHILD, FALSE if not
- */
- bool (*has_child_sa) (ike_sa_t *this, u_int32_t reqid);
-
- /**
* @brief Get a CHILD_SA identified by protocol and SPI.
*
* @param this calling object
@@ -537,22 +560,6 @@ struct ike_sa_t {
status_t (*destroy_child_sa) (ike_sa_t *this, protocol_id_t protocol, u_int32_t spi);
/**
- * @brief Set lifetimes of an IKE_SA.
- *
- * Two lifetimes are specified. The soft_lifetime says, when rekeying should
- * be initiated. The hard_lifetime says, when the IKE_SA has been expired
- * and must be deleted. Normally, hard_lifetime > soft_lifetime, and
- * hard_lifetime is only reached when rekeying at soft_lifetime fails.
- *
- * @param this calling object
- * @param reauth use full reauthentication instead of rekeying.
- * @param soft_lifetime soft_lifetime
- * @param hard_lifetime hard_lifetime
- */
- void (*set_lifetimes) (ike_sa_t *this, bool reauth,
- u_int32_t soft_lifetime, u_int32_t hard_lifetime);
-
- /**
* @brief Rekey the IKE_SA.
*
* Sets up a new IKE_SA, moves all CHILDs to it and deletes this IKE_SA.
@@ -563,42 +570,60 @@ struct ike_sa_t {
status_t (*rekey) (ike_sa_t *this);
/**
- * @brief Reauthentication the IKE_SA.
+ * @brief Restablish the IKE_SA.
*
* Create a completely new IKE_SA with authentication, recreates all children
- * within the IKE_SA and shuts the old SA down.
+ * within the IKE_SA, but lets the old IKE_SA untouched.
*
* @param this calling object
- * @return - SUCCESS, if IKE_SA rekeying initiated
*/
- status_t (*reauth) (ike_sa_t *this);
-
+ void (*reestablish) (ike_sa_t *this);
+
/**
- * @brief Get the transaction which rekeys this IKE_SA.
+ * @brief Set the virtual IP to use for this IKE_SA and its children.
+ *
+ * The virtual IP is assigned per IKE_SA, not per CHILD_SA. It has the same
+ * lifetime as the IKE_SA.
*
* @param this calling object
- * @return rekey_ike_sa_t transaction or NULL
*/
- transaction_t* (*get_rekeying_transaction) (ike_sa_t *this);
-
+ void (*set_virtual_ip) (ike_sa_t *this, bool local, host_t *ip);
+
/**
- * @brief Set the transaction which rekeys this IKE_SA.
+ * @brief Get the virtual IP configured.
*
* @param this calling object
- * @param rekey rekey_ike_sa_t transaction or NULL
+ * @param local TRUE to get local virtual IP, FALSE for remote
*/
- void (*set_rekeying_transaction) (ike_sa_t *this, transaction_t *rekey);
-
+ host_t* (*get_virtual_ip) (ike_sa_t *this, bool local);
+
+ /**
+ * @brief Add a DNS server to the system.
+ *
+ * An IRAS may send a DNS server. To use it, it is installed on the
+ * system. The DNS entry has a lifetime until the IKE_SA gets closed.
+ *
+ * @param this calling object
+ * @param dns DNS server to install on the system
+ */
+ void (*add_dns_server) (ike_sa_t *this, host_t *dns);
+
/**
- * @brief Move all children from other IKE_SA to this IKE_SA.
+ * @brief Inherit all attributes of other to this after rekeying.
*
- * After rekeying completes, all children are switched over to the
- * newly created IKE_SA.
+ * When rekeying is completed, all CHILD_SAs, the virtual IP and all
+ * outstanding tasks are moved from other to this.
+ *
+ * @param this calling object
+ */
+ void (*inherit) (ike_sa_t *this, ike_sa_t *other);
+
+ /**
+ * @brief Reset the IKE_SA, useable when initiating fails
*
- * @param this stepfather
- * @param other deceased (rekeyed) IKE_SA
+ * @param this calling object
*/
- void (*adopt_children) (ike_sa_t *this, ike_sa_t *other);
+ void (*reset) (ike_sa_t *this);
/**
* @brief Destroys a ike_sa_t object.
@@ -611,8 +636,6 @@ struct ike_sa_t {
/**
* @brief Creates an ike_sa_t object with a specific ID.
*
- * The ID gets cloned internally.
- *
* @param ike_sa_id ike_sa_id_t object to associate with new IKE_SA
* @return ike_sa_t object
*
diff --git a/src/charon/sa/ike_sa_manager.c b/src/charon/sa/ike_sa_manager.c
index 31972e0bc..6e9d867fd 100644
--- a/src/charon/sa/ike_sa_manager.c
+++ b/src/charon/sa/ike_sa_manager.c
@@ -297,13 +297,134 @@ static u_int64_t get_next_spi(private_ike_sa_manager_t *this)
}
/**
+ * Implementation of of ike_sa_manager.checkout.
+ */
+static ike_sa_t* checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id)
+{
+ bool responder_spi_set;
+ bool initiator_spi_set;
+ bool original_initiator;
+ ike_sa_t *ike_sa = NULL;
+
+ DBG2(DBG_MGR, "checkout IKE_SA: %J", ike_sa_id);
+
+ DBG2(DBG_MGR, "%d IKE_SAs in manager",
+ this->ike_sa_list->get_count(this->ike_sa_list));
+
+ /* each access is locked */
+ pthread_mutex_lock(&(this->mutex));
+
+ responder_spi_set = ike_sa_id->get_responder_spi(ike_sa_id);
+ initiator_spi_set = ike_sa_id->get_initiator_spi(ike_sa_id);
+ original_initiator = ike_sa_id->is_initiator(ike_sa_id);
+
+ if ((initiator_spi_set && responder_spi_set) ||
+ ((initiator_spi_set && !responder_spi_set) && (original_initiator)))
+ {
+ /* we SHOULD have an IKE_SA for these SPIs in the list,
+ * if not, we can't handle the request...
+ */
+ entry_t *entry;
+ /* look for the entry */
+ if (get_entry_by_id(this, ike_sa_id, &entry) == SUCCESS)
+ {
+ if (wait_for_entry(this, entry))
+ {
+ DBG2(DBG_MGR, "IKE_SA successfully checked out");
+ /* ok, this IKE_SA is finally ours */
+ entry->checked_out = TRUE;
+ ike_sa = entry->ike_sa;
+ /* update responder SPI when it's not set */
+ if (entry->ike_sa_id->get_responder_spi(entry->ike_sa_id) == 0)
+ {
+ ike_sa_id_t *ike_sa_ike_sa_id = ike_sa->get_id(ike_sa);
+ u_int64_t spi = ike_sa_id->get_responder_spi(ike_sa_id);
+
+ ike_sa_ike_sa_id->set_responder_spi(ike_sa_ike_sa_id, spi);
+ entry->ike_sa_id->set_responder_spi(entry->ike_sa_id, spi);
+ }
+ }
+ else
+ {
+ DBG2(DBG_MGR, "IKE_SA found, but not allowed to check it out");
+ }
+ }
+ else
+ {
+ DBG2(DBG_MGR, "IKE_SA not stored in list");
+ /* looks like there is no such IKE_SA, better luck next time... */
+ }
+ }
+ else if ((initiator_spi_set && !responder_spi_set) && (!original_initiator))
+ {
+ /* an IKE_SA_INIT from an another endpoint,
+ * he is the initiator.
+ * For simplicity, we do NOT check for retransmitted
+ * IKE_SA_INIT-Requests here, so EVERY single IKE_SA_INIT-
+ * Request (even a retransmitted one) will result in a
+ * IKE_SA. This could be improved...
+ */
+ u_int64_t responder_spi;
+ entry_t *new_entry;
+
+ /* set SPIs, we are the responder */
+ responder_spi = get_next_spi(this);
+
+ /* we also set arguments spi, so its still valid */
+ ike_sa_id->set_responder_spi(ike_sa_id, responder_spi);
+
+ /* create entry */
+ new_entry = entry_create(ike_sa_id);
+
+ this->ike_sa_list->insert_last(this->ike_sa_list, new_entry);
+
+ /* check ike_sa out */
+ DBG2(DBG_MGR, "IKE_SA added to list of known IKE_SAs");
+ new_entry->checked_out = TRUE;
+ ike_sa = new_entry->ike_sa;
+ }
+ else if (!initiator_spi_set && !responder_spi_set)
+ {
+ /* checkout of a new and unused IKE_SA, used for rekeying */
+ entry_t *new_entry;
+
+ if (original_initiator)
+ {
+ ike_sa_id->set_initiator_spi(ike_sa_id, get_next_spi(this));
+ }
+ else
+ {
+ ike_sa_id->set_responder_spi(ike_sa_id, get_next_spi(this));
+ }
+ /* create entry */
+ new_entry = entry_create(ike_sa_id);
+ DBG2(DBG_MGR, "created IKE_SA: %J", ike_sa_id);
+
+ this->ike_sa_list->insert_last(this->ike_sa_list, new_entry);
+
+ /* check ike_sa out */
+ new_entry->checked_out = TRUE;
+ ike_sa = new_entry->ike_sa;
+ }
+ else
+ {
+ /* responder set, initiator not: here is something seriously wrong! */
+ DBG2(DBG_MGR, "invalid IKE_SA SPIs");
+ }
+
+ pthread_mutex_unlock(&(this->mutex));
+
+ charon->bus->set_sa(charon->bus, ike_sa);
+ return ike_sa;
+}
+
+/**
* Implementation of of ike_sa_manager.checkout_by_id.
*/
-static ike_sa_t* checkout_by_id(private_ike_sa_manager_t *this,
- host_t *my_host,
- host_t *other_host,
- identification_t *my_id,
- identification_t *other_id)
+static ike_sa_t* checkout_by_peer(private_ike_sa_manager_t *this,
+ host_t *my_host, host_t *other_host,
+ identification_t *my_id,
+ identification_t *other_id)
{
iterator_t *iterator;
entry_t *entry;
@@ -387,105 +508,53 @@ static ike_sa_t* checkout_by_id(private_ike_sa_manager_t *this,
}
/**
- * Implementation of of ike_sa_manager.checkout.
+ * Implementation of of ike_sa_manager.checkout_by_id.
*/
-static ike_sa_t* checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id)
+static ike_sa_t* checkout_by_id(private_ike_sa_manager_t *this, u_int32_t id,
+ bool child)
{
- bool responder_spi_set;
- bool initiator_spi_set;
- bool original_initiator;
+ iterator_t *iterator, *children;
+ entry_t *entry;
ike_sa_t *ike_sa = NULL;
+ child_sa_t *child_sa;
- DBG2(DBG_MGR, "checkout IKE_SA: %J", ike_sa_id);
-
- DBG2(DBG_MGR, "%d IKE_SAs in manager",
- this->ike_sa_list->get_count(this->ike_sa_list));
-
- /* each access is locked */
pthread_mutex_lock(&(this->mutex));
- responder_spi_set = ike_sa_id->get_responder_spi(ike_sa_id);
- initiator_spi_set = ike_sa_id->get_initiator_spi(ike_sa_id);
- original_initiator = ike_sa_id->is_initiator(ike_sa_id);
-
- if ((initiator_spi_set && responder_spi_set) ||
- ((initiator_spi_set && !responder_spi_set) && (original_initiator)))
+ iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE);
+ while (iterator->iterate(iterator, (void**)&entry))
{
- /* we SHOULD have an IKE_SA for these SPIs in the list,
- * if not, we can't handle the request...
- */
- entry_t *entry;
- /* look for the entry */
- if (get_entry_by_id(this, ike_sa_id, &entry) == SUCCESS)
+ if (wait_for_entry(this, entry))
{
- if (wait_for_entry(this, entry))
+ /* look for a child with such a reqid ... */
+ if (child)
{
- DBG2(DBG_MGR, "IKE_SA successfully checked out");
- /* ok, this IKE_SA is finally ours */
- entry->checked_out = TRUE;
- ike_sa = entry->ike_sa;
+ children = entry->ike_sa->create_child_sa_iterator(entry->ike_sa);
+ while (children->iterate(children, (void**)&child_sa))
+ {
+ if (child_sa->get_reqid(child_sa) == id)
+ {
+ ike_sa = entry->ike_sa;
+ break;
+ }
+ }
+ children->destroy(children);
}
- else
+ else /* ... or for a IKE_SA with such a unique id */
{
- DBG2(DBG_MGR, "IKE_SA found, but not allowed to check it out");
+ if (entry->ike_sa->get_unique_id(entry->ike_sa) == id)
+ {
+ ike_sa = entry->ike_sa;
+ }
+ }
+ /* got one, return */
+ if (ike_sa)
+ {
+ entry->checked_out = TRUE;
+ break;
}
- }
- else
- {
- DBG2(DBG_MGR, "IKE_SA not stored in list");
- /* looks like there is no such IKE_SA, better luck next time... */
}
}
- else if ((initiator_spi_set && !responder_spi_set) && (!original_initiator))
- {
- /* an IKE_SA_INIT from an another endpoint,
- * he is the initiator.
- * For simplicity, we do NOT check for retransmitted
- * IKE_SA_INIT-Requests here, so EVERY single IKE_SA_INIT-
- * Request (even a retransmitted one) will result in a
- * IKE_SA. This could be improved...
- */
- u_int64_t responder_spi;
- entry_t *new_entry;
-
- /* set SPIs, we are the responder */
- responder_spi = get_next_spi(this);
-
- /* we also set arguments spi, so its still valid */
- ike_sa_id->set_responder_spi(ike_sa_id, responder_spi);
-
- /* create entry */
- new_entry = entry_create(ike_sa_id);
-
- this->ike_sa_list->insert_last(this->ike_sa_list, new_entry);
-
- /* check ike_sa out */
- DBG2(DBG_MGR, "IKE_SA added to list of known IKE_SAs");
- new_entry->checked_out = TRUE;
- ike_sa = new_entry->ike_sa;
- }
- else if (!initiator_spi_set && !responder_spi_set && original_initiator)
- {
- /* checkout of a new and unused IKE_SA, used for rekeying */
- entry_t *new_entry;
-
- ike_sa_id->set_initiator_spi(ike_sa_id, get_next_spi(this));
- /* create entry */
- new_entry = entry_create(ike_sa_id);
- DBG2(DBG_MGR, "created IKE_SA: %J", ike_sa_id);
-
- this->ike_sa_list->insert_last(this->ike_sa_list, new_entry);
-
- /* check ike_sa out */
- new_entry->checked_out = TRUE;
- ike_sa = new_entry->ike_sa;
- }
- else
- {
- /* responder set, initiator not: here is something seriously wrong! */
- DBG2(DBG_MGR, "invalid IKE_SA SPIs");
- }
-
+ iterator->destroy(iterator);
pthread_mutex_unlock(&(this->mutex));
charon->bus->set_sa(charon->bus, ike_sa);
@@ -493,14 +562,15 @@ static ike_sa_t* checkout(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id
}
/**
- * Implementation of of ike_sa_manager.checkout_by_child.
+ * Implementation of of ike_sa_manager.checkout_by_name.
*/
-static ike_sa_t* checkout_by_child(private_ike_sa_manager_t *this,
- u_int32_t reqid)
+static ike_sa_t* checkout_by_name(private_ike_sa_manager_t *this, char *name,
+ bool child)
{
- iterator_t *iterator;
+ iterator_t *iterator, *children;
entry_t *entry;
ike_sa_t *ike_sa = NULL;
+ child_sa_t *child_sa;
pthread_mutex_lock(&(this->mutex));
@@ -509,12 +579,31 @@ static ike_sa_t* checkout_by_child(private_ike_sa_manager_t *this,
{
if (wait_for_entry(this, entry))
{
- /* ok, access is exclusive for us, check for child */
- if (entry->ike_sa->has_child_sa(entry->ike_sa, reqid))
+ /* look for a child with such a policy name ... */
+ if (child)
+ {
+ children = entry->ike_sa->create_child_sa_iterator(entry->ike_sa);
+ while (children->iterate(children, (void**)&child_sa))
+ {
+ if (streq(child_sa->get_name(child_sa), name))
+ {
+ ike_sa = entry->ike_sa;
+ break;
+ }
+ }
+ children->destroy(children);
+ }
+ else /* ... or for a IKE_SA with such a connection name */
+ {
+ if (streq(entry->ike_sa->get_name(entry->ike_sa), name))
+ {
+ ike_sa = entry->ike_sa;
+ }
+ }
+ /* got one, return */
+ if (ike_sa)
{
- /* match */
entry->checked_out = TRUE;
- ike_sa = entry->ike_sa;
break;
}
}
@@ -529,9 +618,16 @@ static ike_sa_t* checkout_by_child(private_ike_sa_manager_t *this,
/**
* Iterator hook for iterate, gets ike_sas instead of entries
*/
-static void* iterator_hook(void *value)
+static bool iterator_hook(private_ike_sa_manager_t* this, entry_t *in,
+ ike_sa_t **out)
{
- return ((entry_t*)value)->ike_sa;
+ /* check out entry */
+ if (wait_for_entry(this, in))
+ {
+ *out = in->ike_sa;
+ return TRUE;
+ }
+ return FALSE;
}
/**
@@ -540,9 +636,9 @@ static void* iterator_hook(void *value)
static iterator_t *create_iterator(private_ike_sa_manager_t* this)
{
iterator_t *iterator = this->ike_sa_list->create_iterator_locked(
- this->ike_sa_list, &this->mutex);
+ this->ike_sa_list, &this->mutex);
/* register hook to iterator over ike_sas, not entries */
- iterator->set_iterator_hook(iterator, iterator_hook);
+ iterator->set_iterator_hook(iterator, (iterator_hook_t*)iterator_hook, this);
return iterator;
}
@@ -634,170 +730,6 @@ static status_t checkin_and_destroy(private_ike_sa_manager_t *this, ike_sa_t *ik
}
/**
- * Implementation of ike_sa_manager_t.delete.
- */
-static status_t delete_(private_ike_sa_manager_t *this, ike_sa_id_t *ike_sa_id)
-{
- /* deletion is a bit complex, we must garant that no thread is waiting for
- * this SA.
- * We take this SA from the list, and start signaling while threads
- * are in the condvar.
- */
- entry_t *entry;
- status_t retval;
-
- DBG2(DBG_MGR, "delete IKE_SA: %J", ike_sa_id);
-
- pthread_mutex_lock(&(this->mutex));
-
- if (get_entry_by_id(this, ike_sa_id, &entry) == SUCCESS)
- {
- /* we try a delete. If it succeeds, our job is done here. The
- * other peer will reply, and the IKE SA gets the finally deleted...
- */
- if (entry->ike_sa->delete(entry->ike_sa) == SUCCESS)
- {
- DBG2(DBG_MGR, "initiated delete for IKE_SA");
- }
- /* but if the IKE SA is not in a state where the deletion is
- * negotiated with the other peer, we can destroy the IKE SA on our own.
- */
- else
- {
-
- }
- retval = SUCCESS;
- }
- else
- {
- DBG2(DBG_MGR, "tried to delete nonexisting IKE_SA");
- retval = NOT_FOUND;
- }
-
- pthread_mutex_unlock(&(this->mutex));
- return retval;
-}
-
-/**
- * Implementation of ike_sa_manager_t.delete_by_name.
- */
-static status_t delete_by_name(private_ike_sa_manager_t *this, char *name)
-{
- iterator_t *iterator;
- iterator_t *child_iter;
- entry_t *entry;
- size_t name_len = strlen(name);
-
- pthread_mutex_lock(&(this->mutex));
-
- iterator = this->ike_sa_list->create_iterator(this->ike_sa_list, TRUE);
- while (iterator->iterate(iterator, (void**)&entry))
- {
- if (wait_for_entry(this, entry))
- {
- /* delete ike_sa if:
- * name{x} matches completely
- * name{} matches by name
- * name matches by name
- */
- bool del = FALSE;
- char *ike_name;
- char *child_name;
- child_sa_t *child_sa;
-
- ike_name = entry->ike_sa->get_name(entry->ike_sa);
- /* check if "name{x}" matches completely */
- if (strcmp(name, ike_name) == 0)
- {
- del = TRUE;
- }
- /* check if name is in form of "name{}" and matches to ike_name */
- else if (name_len > 1 &&
- name[name_len - 2] == '{' && name[name_len - 1] == '}' &&
- strlen(ike_name) > name_len &&
- ike_name[name_len - 2] == '{' &&
- strncmp(name, ike_name, name_len - 2) == 0)
- {
- del = TRUE;
- }
- /* finally, check if name is "name" and matches ike_name */
- else if (name_len == strchr(ike_name, '{') - ike_name &&
- strncmp(name, ike_name, name_len) == 0)
- {
- del = TRUE;
- }
-
- if (del)
- {
- if (entry->ike_sa->delete(entry->ike_sa) == DESTROY_ME)
- {
- delete_entry(this, entry);
- iterator->reset(iterator);
- }
- /* no need to check children, as we delete all */
- continue;
- }
-
- /* and now the same game for all children. delete child_sa if:
- * name[x] matches completely
- * name[] matches by name
- * name matches by name
- */
- child_iter = entry->ike_sa->create_child_sa_iterator(entry->ike_sa);
- while (child_iter->iterate(child_iter, (void**)&child_sa))
- {
- /* skip ROUTED children, they have their "unroute" command */
- if (child_sa->get_state(child_sa) == CHILD_ROUTED)
- {
- continue;
- }
-
- child_name = child_sa->get_name(child_sa);
- del = FALSE;
- /* check if "name[x]" matches completely */
- if (strcmp(name, child_name) == 0)
- {
- del = TRUE;
- }
- /* check if name is in form of "name[]" and matches to child_name */
- else if (name_len > 1 &&
- name[name_len - 2] == '[' && name[name_len - 1] == ']' &&
- strlen(child_name) > name_len &&
- child_name[name_len - 2] == '[' &&
- strncmp(name, child_name, name_len - 2) == 0)
- {
- del = TRUE;
- }
- /* finally, check if name is "name" and matches child_name */
- else if (name_len == strchr(child_name, '[') - child_name &&
- strncmp(name, child_name, name_len) == 0)
- {
- del = TRUE;
- }
- if (del)
- {
- if (entry->ike_sa->delete_child_sa(entry->ike_sa,
- child_sa->get_protocol(child_sa),
- child_sa->get_spi(child_sa, TRUE)) == DESTROY_ME)
- {
- /* when a fatal error occurs, we are responsible to
- * remove the IKE_SA */
- delete_entry(this, entry);
- iterator->reset(iterator);
- break;
- }
- }
- }
- child_iter->destroy(child_iter);
- }
- }
- iterator->destroy(iterator);
- pthread_mutex_unlock(&(this->mutex));
-
- return SUCCESS;
-}
-
-/**
* Implementation of ike_sa_manager_t.destroy.
*/
static void destroy(private_ike_sa_manager_t *this)
@@ -859,19 +791,18 @@ ike_sa_manager_t *ike_sa_manager_create()
/* assign public functions */
this->public.destroy = (void(*)(ike_sa_manager_t*))destroy;
- this->public.checkout_by_id = (ike_sa_t*(*)(ike_sa_manager_t*,host_t*,host_t*,identification_t*,identification_t*))checkout_by_id;
this->public.checkout = (ike_sa_t*(*)(ike_sa_manager_t*, ike_sa_id_t*))checkout;
- this->public.checkout_by_child = (ike_sa_t*(*)(ike_sa_manager_t*,u_int32_t))checkout_by_child;
+ this->public.checkout_by_peer = (ike_sa_t*(*)(ike_sa_manager_t*,host_t*,host_t*,identification_t*,identification_t*))checkout_by_peer;
+ this->public.checkout_by_id = (ike_sa_t*(*)(ike_sa_manager_t*,u_int32_t,bool))checkout_by_id;
+ this->public.checkout_by_name = (ike_sa_t*(*)(ike_sa_manager_t*,char*,bool))checkout_by_name;
this->public.create_iterator = (iterator_t*(*)(ike_sa_manager_t*))create_iterator;
this->public.checkin = (status_t(*)(ike_sa_manager_t*,ike_sa_t*))checkin;
- this->public.delete = (status_t(*)(ike_sa_manager_t*,ike_sa_id_t*))delete_;
- this->public.delete_by_name = (status_t(*)(ike_sa_manager_t*,char*))delete_by_name;
this->public.checkin_and_destroy = (status_t(*)(ike_sa_manager_t*,ike_sa_t*))checkin_and_destroy;
/* initialize private variables */
this->ike_sa_list = linked_list_create();
- pthread_mutex_init(&(this->mutex), NULL);
+ pthread_mutex_init(&this->mutex, NULL);
this->randomizer = randomizer_create();
- return (ike_sa_manager_t*)this;
+ return &this->public;
}
diff --git a/src/charon/sa/ike_sa_manager.h b/src/charon/sa/ike_sa_manager.h
index 671062c4e..ecd3db156 100644
--- a/src/charon/sa/ike_sa_manager.h
+++ b/src/charon/sa/ike_sa_manager.h
@@ -59,7 +59,7 @@ struct ike_sa_manager_t {
* result in a deadlock!
*
* @param this the manager object
- * @param[in/out] ike_sa_id the SA identifier, will be updated
+ * @param ike_sa_id the SA identifier, will be updated
* @returns
* - checked out IKE_SA if found
* - NULL, if no such IKE_SA available
@@ -82,25 +82,44 @@ struct ike_sa_manager_t {
* @param other_id ID used by remote
* @return checked out/created IKE_SA
*/
- ike_sa_t* (*checkout_by_id) (ike_sa_manager_t* this,
- host_t *my_host, host_t* other_host,
- identification_t *my_id,
- identification_t *other_id);
+ ike_sa_t* (*checkout_by_peer) (ike_sa_manager_t* this,
+ host_t *my_host, host_t* other_host,
+ identification_t *my_id,
+ identification_t *other_id);
/**
- * @brief Check out an IKE_SA by protocol and SPI of one of its CHILD_SA.
+ * @brief Check out an IKE_SA a unique ID.
*
- * The kernel sends us expire messages for IPsec SAs. To fullfill
- * this request, we must check out the IKE SA which contains the
- * CHILD_SA the kernel wants to modify.
+ * 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.
*
* @param this the manager object
- * @param reqid reqid of the CHILD_SA
+ * @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_child) (ike_sa_manager_t* this, u_int32_t reqid);
+ ike_sa_t* (*checkout_by_id) (ike_sa_manager_t* this, u_int32_t id,
+ bool child);
+
+ /**
+ * @brief Check out an IKE_SA by the policy/connection name.
+ *
+ * Check out the IKE_SA by the connections name or by a CHILD_SAs policy
+ * name.
+ *
+ * @param this the manager object
+ * @param name name of the connection/policy
+ * @param child TRUE to use policy name, FALSE to use conn name
+ * @return
+ * - checked out IKE_SA, if found
+ * - NULL, if not found
+ */
+ ike_sa_t* (*checkout_by_name) (ike_sa_manager_t* this, char *name,
+ bool child);
/**
* @brief Create an iterator over all stored IKE_SAs.
@@ -121,8 +140,8 @@ struct ike_sa_manager_t {
* The SA must be checked out again!
*
* @param this the manager object
- * @param[in/out] ike_sa_id the SA identifier, will be updated
- * @param[out] ike_sa checked out SA
+ * @param ike_sa_id the SA identifier, will be updated
+ * @param ike_sa checked out SA
* @returns
* - SUCCESS if checked in
* - NOT_FOUND when not found (shouldn't happen!)
@@ -130,47 +149,6 @@ struct ike_sa_manager_t {
status_t (*checkin) (ike_sa_manager_t* this, ike_sa_t *ike_sa);
/**
- * @brief Delete a SA, which was not checked out.
- *
- * If the state allows it, the IKE SA is destroyed immediately. If it is
- * in the state ESTABLSIHED, a delete message
- * is sent to the remote peer, which has to be acknowledged.
- *
- * @warning do not use this when the SA is already checked out, this will
- * deadlock!
- *
- * @param this the manager object
- * @param[in/out] ike_sa_id the SA identifier
- * @returns
- * - SUCCESS if found
- * - NOT_FOUND when no such SA is available
- */
- status_t (*delete) (ike_sa_manager_t* this, ike_sa_id_t *ike_sa_id);
-
- /**
- * @brief Delete a SA identified by its name, which was not checked out.
- *
- * Using delete_by_name allows the delete of IKE_SAs and CHILD_SAs.
- * The supplied name may have one of the following format:
- *
- * name{x} => delete IKE_SA with "name" and unique id "x"
- * name{} => delete all IKE_SAs with "name"
- * name[x] => delete CHILD_SA with "name" and unique id "x"
- * name[] => delete all CHILD_SAs with "name"
- * name => delete all CHILD_SAs or IKE_SAs with "name"
- *
- * @warning do not use this when the SA is already checked out, this will
- * deadlock!
- *
- * @param this the manager object
- * @param name name in one of the format described above
- * @returns
- * - SUCCESS if found
- * - NOT_FOUND when no such SA is available
- */
- status_t (*delete_by_name) (ike_sa_manager_t* this, char *name);
-
- /**
* @brief Destroy a checked out SA.
*
* The IKE SA is destroyed without notification of the remote peer.
diff --git a/src/charon/sa/task_manager.c b/src/charon/sa/task_manager.c
new file mode 100644
index 000000000..061b0239a
--- /dev/null
+++ b/src/charon/sa/task_manager.c
@@ -0,0 +1,780 @@
+/**
+ * @file task_manager.c
+ *
+ * @brief Implementation of task_manager_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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 "task_manager.h"
+
+#include <daemon.h>
+#include <sa/tasks/ike_init.h>
+#include <sa/tasks/ike_natd.h>
+#include <sa/tasks/ike_auth.h>
+#include <sa/tasks/ike_cert.h>
+#include <sa/tasks/ike_rekey.h>
+#include <sa/tasks/ike_delete.h>
+#include <sa/tasks/ike_config.h>
+#include <sa/tasks/ike_dpd.h>
+#include <sa/tasks/child_create.h>
+#include <sa/tasks/child_rekey.h>
+#include <sa/tasks/child_delete.h>
+#include <encoding/payloads/delete_payload.h>
+#include <queues/jobs/retransmit_job.h>
+
+typedef struct exchange_t exchange_t;
+
+/**
+ * An exchange in the air, used do detect and handle retransmission
+ */
+struct exchange_t {
+
+ /**
+ * Message ID used for this transaction
+ */
+ u_int32_t mid;
+
+ /**
+ * generated packet for retransmission
+ */
+ packet_t *packet;
+};
+
+typedef struct private_task_manager_t private_task_manager_t;
+
+/**
+ * private data of the task manager
+ */
+struct private_task_manager_t {
+
+ /**
+ * public functions
+ */
+ task_manager_t public;
+
+ /**
+ * associated IKE_SA we are serving
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Exchange we are currently handling as responder
+ */
+ struct {
+ /**
+ * Message ID of the exchange
+ */
+ u_int32_t mid;
+
+ /**
+ * packet for retransmission
+ */
+ packet_t *packet;
+
+ } responding;
+
+ /**
+ * Exchange we are currently handling as initiator
+ */
+ struct {
+ /**
+ * Message ID of the exchange
+ */
+ u_int32_t mid;
+
+ /**
+ * how many times we have retransmitted so far
+ */
+ u_int retransmitted;
+
+ /**
+ * packet for retransmission
+ */
+ packet_t *packet;
+
+ /**
+ * type of the initated exchange
+ */
+ exchange_type_t type;
+
+ } initiating;
+
+ /**
+ * List of queued tasks not yet in action
+ */
+ linked_list_t *queued_tasks;
+
+ /**
+ * List of active tasks, initiated by ourselve
+ */
+ linked_list_t *active_tasks;
+
+ /**
+ * List of tasks initiated by peer
+ */
+ linked_list_t *passive_tasks;
+
+ /**
+ * ike_sa_init message we sent, stored here for later authentication
+ */
+ packet_t *ike_sa_init;
+};
+
+/**
+ * move a task of a specific type from the queue to the active list
+ */
+static bool activate_task(private_task_manager_t *this, task_type_t type)
+{
+ iterator_t *iterator;
+ task_t *task;
+ bool found = FALSE;
+
+ iterator = this->queued_tasks->create_iterator(this->queued_tasks, TRUE);
+ while (iterator->iterate(iterator, (void**)&task))
+ {
+ if (task->get_type(task) == type)
+ {
+ DBG2(DBG_IKE, " activating %N task", task_type_names, type);
+ iterator->remove(iterator);
+ this->active_tasks->insert_last(this->active_tasks, task);
+ found = TRUE;
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+ return found;
+}
+
+/**
+ * Implementation of task_manager_t.retransmit
+ */
+static status_t retransmit(private_task_manager_t *this, u_int32_t message_id)
+{
+ if (message_id == this->initiating.mid)
+ {
+ u_int32_t timeout;
+ job_t *job;
+
+ timeout = charon->configuration->get_retransmit_timeout(
+ charon->configuration, this->initiating.retransmitted);
+ if (timeout == 0)
+ {
+ DBG1(DBG_IKE, "giving up after %d retransmits",
+ this->initiating.retransmitted - 1);
+ return DESTROY_ME;
+ }
+
+ if (this->initiating.retransmitted)
+ {
+ DBG1(DBG_IKE, "retransmit %d of request with message ID %d",
+ this->initiating.retransmitted, message_id);
+ }
+ this->initiating.retransmitted++;
+
+ charon->send_queue->add(charon->send_queue,
+ this->initiating.packet->clone(this->initiating.packet));
+ job = (job_t*)retransmit_job_create(this->initiating.mid,
+ this->ike_sa->get_id(this->ike_sa));
+ charon->event_queue->add_relative(charon->event_queue, job, timeout);
+ }
+ return SUCCESS;
+}
+
+/**
+ * build a request using the active task list
+ * Implementation of task_manager_t.initiate
+ */
+static status_t build_request(private_task_manager_t *this)
+{
+ iterator_t *iterator;
+ task_t *task;
+ message_t *message;
+ status_t status;
+ exchange_type_t exchange = 0;
+
+ if (this->active_tasks->get_count(this->active_tasks) == 0)
+ {
+ DBG2(DBG_IKE, "activating new tasks");
+ switch (this->ike_sa->get_state(this->ike_sa))
+ {
+ case IKE_CREATED:
+ if (activate_task(this, IKE_INIT))
+ {
+ exchange = IKE_SA_INIT;
+ activate_task(this, IKE_NATD);
+ activate_task(this, IKE_CERT);
+ activate_task(this, IKE_AUTHENTICATE);
+ activate_task(this, IKE_CONFIG);
+ activate_task(this, CHILD_CREATE);
+ }
+ break;
+ case IKE_ESTABLISHED:
+ if (activate_task(this, CHILD_CREATE))
+ {
+ exchange = CREATE_CHILD_SA;
+ activate_task(this, IKE_CONFIG);
+ break;
+ }
+ if (activate_task(this, CHILD_DELETE))
+ {
+ exchange = INFORMATIONAL;
+ break;
+ }
+ if (activate_task(this, CHILD_REKEY))
+ {
+ exchange = CREATE_CHILD_SA;
+ break;
+ }
+ if (activate_task(this, IKE_DELETE))
+ {
+ exchange = INFORMATIONAL;
+ break;
+ }
+ if (activate_task(this, IKE_REKEY))
+ {
+ exchange = CREATE_CHILD_SA;
+ break;
+ }
+ if (activate_task(this, IKE_DEADPEER))
+ {
+ exchange = INFORMATIONAL;
+ break;
+ }
+ case IKE_REKEYING:
+ if (activate_task(this, IKE_DELETE))
+ {
+ exchange = INFORMATIONAL;
+ break;
+ }
+ case IKE_DELETING:
+ default:
+ break;
+ }
+ }
+ else
+ {
+ DBG2(DBG_IKE, "reinitiating already active tasks");
+ iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE);
+ while (iterator->iterate(iterator, (void**)&task))
+ {
+ DBG2(DBG_IKE, " %N task", task_type_names, task->get_type(task));
+ switch (task->get_type(task))
+ {
+ case IKE_INIT:
+ exchange = IKE_SA_INIT;
+ break;
+ case IKE_AUTHENTICATE:
+ exchange = IKE_AUTH;
+ break;
+ default:
+ continue;
+ }
+ break;
+ }
+ iterator->destroy(iterator);
+ }
+
+ if (exchange == 0)
+ {
+ DBG2(DBG_IKE, "nothing to initiate");
+ /* nothing to do yet... */
+ return SUCCESS;
+ }
+
+ message = message_create();
+ message->set_message_id(message, this->initiating.mid);
+ message->set_exchange_type(message, exchange);
+ this->initiating.type = exchange;
+ this->initiating.retransmitted = 0;
+
+ iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE);
+ while (iterator->iterate(iterator, (void*)&task))
+ {
+ switch (task->build(task, message))
+ {
+ case SUCCESS:
+ /* task completed, remove it */
+ iterator->remove(iterator);
+ task->destroy(task);
+ break;
+ case NEED_MORE:
+ /* processed, but task needs another exchange */
+ break;
+ case FAILED:
+ default:
+ /* critical failure, destroy IKE_SA */
+ iterator->destroy(iterator);
+ message->destroy(message);
+ return DESTROY_ME;
+ }
+ }
+ iterator->destroy(iterator);
+
+ DESTROY_IF(this->initiating.packet);
+ status = this->ike_sa->generate_message(this->ike_sa, message,
+ &this->initiating.packet);
+ message->destroy(message);
+ if (status != SUCCESS)
+ {
+ /* message generation failed. There is nothing more to do than to
+ * close the SA */
+ return DESTROY_ME;
+ }
+
+ return retransmit(this, this->initiating.mid);
+}
+
+/**
+ * handle an incoming response message
+ */
+static status_t process_response(private_task_manager_t *this,
+ message_t *message)
+{
+ iterator_t *iterator;
+ task_t *task;
+
+ if (message->get_exchange_type(message) != this->initiating.type)
+ {
+ DBG1(DBG_IKE, "received %N response, but expected %N",
+ exchange_type_names, message->get_exchange_type(message),
+ exchange_type_names, this->initiating.type);
+ return DESTROY_ME;
+ }
+
+ iterator = this->active_tasks->create_iterator(this->active_tasks, TRUE);
+ while (iterator->iterate(iterator, (void*)&task))
+ {
+ switch (task->process(task, message))
+ {
+ case SUCCESS:
+ /* task completed, remove it */
+ iterator->remove(iterator);
+ task->destroy(task);
+ break;
+ case NEED_MORE:
+ /* processed, but task needs another exchange */
+ break;
+ case FAILED:
+ default:
+ /* critical failure, destroy IKE_SA */
+ iterator->destroy(iterator);
+ return DESTROY_ME;
+ }
+ }
+ iterator->destroy(iterator);
+
+ this->initiating.mid++;
+
+ return build_request(this);
+}
+
+/**
+ * build a response depending on the "passive" task list
+ */
+static status_t build_response(private_task_manager_t *this,
+ exchange_type_t exchange)
+{
+ iterator_t *iterator;
+ task_t *task;
+ message_t *message;
+ bool delete = FALSE;
+ status_t status;
+
+ message = message_create();
+ message->set_exchange_type(message, exchange);
+ message->set_message_id(message, this->responding.mid);
+ message->set_request(message, FALSE);
+
+ iterator = this->passive_tasks->create_iterator(this->passive_tasks, TRUE);
+ while (iterator->iterate(iterator, (void*)&task))
+ {
+ switch (task->build(task, message))
+ {
+ case SUCCESS:
+ /* task completed, remove it */
+ iterator->remove(iterator);
+ task->destroy(task);
+ break;
+ case NEED_MORE:
+ /* processed, but task needs another exchange */
+ break;
+ case FAILED:
+ default:
+ /* destroy IKE_SA, but SEND response first */
+ delete = TRUE;
+ break;
+ }
+ if (delete)
+ {
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+
+ /* message complete, send it */
+ DESTROY_IF(this->responding.packet);
+ status = this->ike_sa->generate_message(this->ike_sa, message,
+ &this->responding.packet);
+ message->destroy(message);
+ if (status != SUCCESS)
+ {
+ return DESTROY_ME;
+ }
+
+ charon->send_queue->add(charon->send_queue,
+ this->responding.packet->clone(this->responding.packet));
+ if (delete)
+ {
+ return DESTROY_ME;
+ }
+ return SUCCESS;
+}
+
+/**
+ * handle an incoming request message
+ */
+static status_t process_request(private_task_manager_t *this,
+ message_t *message)
+{
+ iterator_t *iterator;
+ task_t *task = NULL;
+ exchange_type_t exchange;
+ payload_t *payload;
+ notify_payload_t *notify;
+
+ exchange = message->get_exchange_type(message);
+
+ /* create tasks depending on request type */
+ switch (exchange)
+ {
+ case IKE_SA_INIT:
+ {
+ task = (task_t*)ike_init_create(this->ike_sa, FALSE, NULL);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ task = (task_t*)ike_natd_create(this->ike_sa, FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ task = (task_t*)ike_cert_create(this->ike_sa, FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ task = (task_t*)ike_config_create(this->ike_sa, NULL);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ task = (task_t*)ike_auth_create(this->ike_sa, FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ task = (task_t*)child_create_create(this->ike_sa, NULL);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ break;
+ }
+ case CREATE_CHILD_SA:
+ {
+ bool notify_found = FALSE, ts_found = FALSE;
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ switch (payload->get_type(payload))
+ {
+ case NOTIFY:
+ {
+ /* if we find a rekey notify, its CHILD_SA rekeying */
+ notify = (notify_payload_t*)payload;
+ if (notify->get_notify_type(notify) == REKEY_SA &&
+ (notify->get_protocol_id(notify) == PROTO_AH ||
+ notify->get_protocol_id(notify) == PROTO_ESP))
+ {
+ notify_found = TRUE;
+ }
+ break;
+ }
+ case TRAFFIC_SELECTOR_INITIATOR:
+ case TRAFFIC_SELECTOR_RESPONDER:
+ {
+ /* if we don't find a TS, its IKE rekeying */
+ ts_found = TRUE;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+
+ if (ts_found)
+ {
+ if (notify_found)
+ {
+ task = (task_t*)child_rekey_create(this->ike_sa, NULL);
+ }
+ else
+ {
+ task = (task_t*)child_create_create(this->ike_sa, NULL);
+ }
+ }
+ else
+ {
+ task = (task_t*)ike_rekey_create(this->ike_sa, FALSE);
+ }
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+
+ break;
+ }
+ case INFORMATIONAL:
+ {
+ delete_payload_t *delete;
+
+ delete = (delete_payload_t*)message->get_payload(message, DELETE);
+ if (delete)
+ {
+ if (delete->get_protocol_id(delete) == PROTO_IKE)
+ {
+ task = (task_t*)ike_delete_create(this->ike_sa, FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ }
+ else
+ {
+ task = (task_t*)child_delete_create(this->ike_sa, NULL);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ }
+ }
+ else
+ {
+ task = (task_t*)ike_dpd_create(FALSE);
+ this->passive_tasks->insert_last(this->passive_tasks, task);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ /* let the tasks process the message */
+ iterator = this->passive_tasks->create_iterator(this->passive_tasks, TRUE);
+ while (iterator->iterate(iterator, (void*)&task))
+ {
+ switch (task->process(task, message))
+ {
+ case SUCCESS:
+ /* task completed, remove it */
+ iterator->remove(iterator);
+ task->destroy(task);
+ break;
+ case NEED_MORE:
+ /* processed, but task needs at least another call to build() */
+ break;
+ case FAILED:
+ default:
+ /* critical failure, destroy IKE_SA */
+ iterator->destroy(iterator);
+ return DESTROY_ME;
+ }
+ }
+ iterator->destroy(iterator);
+
+ return build_response(this, exchange);
+}
+
+/**
+ * Implementation of task_manager_t.process_message
+ */
+static status_t process_message(private_task_manager_t *this, message_t *msg)
+{
+ u_int32_t mid = msg->get_message_id(msg);
+
+ if (msg->get_request(msg))
+ {
+ if (mid == this->responding.mid)
+ {
+ if (process_request(this, msg) != SUCCESS)
+ {
+ return DESTROY_ME;
+ }
+ this->responding.mid++;
+ }
+ else if ((mid == this->responding.mid - 1) && this->responding.packet)
+ {
+ DBG1(DBG_IKE, "received retransmit of request with ID %d, "
+ "retransmitting response", mid);
+ charon->send_queue->add(charon->send_queue,
+ this->responding.packet->clone(
+ this->responding.packet));
+ }
+ else
+ {
+ DBG1(DBG_IKE, "received message ID %d, excepted %d. Ignored",
+ mid, this->responding.mid);
+ }
+ }
+ else
+ {
+ if (mid == this->initiating.mid)
+ {
+ if (process_response(this, msg) != SUCCESS)
+ {
+ return DESTROY_ME;
+ }
+ }
+ else
+ {
+ DBG1(DBG_IKE, "received message ID %d, excepted %d. Ignored",
+ mid, this->initiating.mid);
+ return SUCCESS;
+ }
+ }
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_manager_t.queue_task
+ */
+static void queue_task(private_task_manager_t *this, task_t *task)
+{
+ DBG2(DBG_IKE, "queueing %N task", task_type_names, task->get_type(task));
+ this->queued_tasks->insert_last(this->queued_tasks, task);
+}
+
+/**
+ * Implementation of task_manager_t.adopt_tasks
+ */
+static void adopt_tasks(private_task_manager_t *this, private_task_manager_t *other)
+{
+ task_t *task;
+
+ /* move queued tasks from other to this */
+ while (other->queued_tasks->remove_last(other->queued_tasks,
+ (void**)&task) == SUCCESS)
+ {
+ task->migrate(task, this->ike_sa);
+ this->queued_tasks->insert_first(this->queued_tasks, task);
+ }
+
+ /* reset active tasks and move them to others queued tasks */
+ while (other->active_tasks->remove_last(other->active_tasks,
+ (void**)&task) == SUCCESS)
+ {
+ task->migrate(task, this->ike_sa);
+ this->queued_tasks->insert_first(this->queued_tasks, task);
+ }
+}
+
+/**
+ * Implementation of task_manager_t.busy
+ */
+static bool busy(private_task_manager_t *this)
+{
+ return (this->active_tasks->get_count(this->active_tasks) > 0);
+}
+
+/**
+ * Implementation of task_manager_t.reset
+ */
+static void reset(private_task_manager_t *this)
+{
+ task_t *task;
+
+ /* reset message counters and retransmit packets */
+ DESTROY_IF(this->responding.packet);
+ DESTROY_IF(this->initiating.packet);
+ DESTROY_IF(this->ike_sa_init);
+ this->responding.packet = NULL;
+ this->initiating.packet = NULL;
+ this->ike_sa_init = NULL;
+ this->responding.mid = 0;
+ this->initiating.mid = -1;
+
+ /* reset active tasks */
+ while (this->active_tasks->remove_last(this->active_tasks,
+ (void**)&task) == SUCCESS)
+ {
+ task->migrate(task, this->ike_sa);
+ this->queued_tasks->insert_first(this->queued_tasks, task);
+ }
+}
+
+/**
+ * Implementation of task_manager_t.destroy
+ */
+static void destroy(private_task_manager_t *this)
+{
+ task_t *task;
+
+ this->queued_tasks->destroy_offset(this->queued_tasks,
+ offsetof(task_t, destroy));
+ this->passive_tasks->destroy_offset(this->passive_tasks,
+ offsetof(task_t, destroy));
+
+ /* emmit outstanding signals for tasks */
+ while (this->active_tasks->remove_last(this->active_tasks,
+ (void**)&task) == SUCCESS)
+ {
+ switch (task->get_type(task))
+ {
+ case IKE_AUTH:
+ SIG(IKE_UP_FAILED, "establishing IKE_SA failed");
+ break;
+ case IKE_DELETE:
+ SIG(IKE_DOWN_FAILED, "deleteing IKE_SA properly failed");
+ break;
+ case IKE_REKEY:
+ SIG(IKE_REKEY_FAILED, "rekeying IKE_SA failed");
+ break;
+ case CHILD_CREATE:
+ SIG(CHILD_UP_FAILED, "establishing CHILD_SA failed");
+ break;
+ case CHILD_DELETE:
+ SIG(CHILD_DOWN_FAILED, "deleting CHILD_SA failed");
+ break;
+ case CHILD_REKEY:
+ SIG(IKE_REKEY_FAILED, "rekeying CHILD_SA failed");
+ break;
+ default:
+ break;
+ }
+ task->destroy(task);
+ }
+ this->active_tasks->destroy(this->active_tasks);
+ DESTROY_IF(this->responding.packet);
+ DESTROY_IF(this->initiating.packet);
+ DESTROY_IF(this->ike_sa_init);
+ free(this);
+}
+
+/*
+ * see header file
+ */
+task_manager_t *task_manager_create(ike_sa_t *ike_sa)
+{
+ private_task_manager_t *this = malloc_thing(private_task_manager_t);
+
+ this->public.process_message = (status_t(*)(task_manager_t*,message_t*))process_message;
+ this->public.queue_task = (void(*)(task_manager_t*,task_t*))queue_task;
+ this->public.initiate = (status_t(*)(task_manager_t*))build_request;
+ this->public.retransmit = (status_t(*)(task_manager_t*,u_int32_t))retransmit;
+ this->public.reset = (void(*)(task_manager_t*))reset;
+ this->public.adopt_tasks = (void(*)(task_manager_t*,task_manager_t*))adopt_tasks;
+ this->public.busy = (bool(*)(task_manager_t*))busy;
+ this->public.destroy = (void(*)(task_manager_t*))destroy;
+
+ this->ike_sa = ike_sa;
+ this->responding.packet = NULL;
+ this->initiating.packet = NULL;
+ this->responding.mid = 0;
+ this->initiating.mid = 0;
+ this->queued_tasks = linked_list_create();
+ this->active_tasks = linked_list_create();
+ this->passive_tasks = linked_list_create();
+ this->ike_sa_init = NULL;
+
+ return &this->public;
+}
diff --git a/src/charon/sa/task_manager.h b/src/charon/sa/task_manager.h
new file mode 100644
index 000000000..c766d4a65
--- /dev/null
+++ b/src/charon/sa/task_manager.h
@@ -0,0 +1,144 @@
+/**
+ * @file task_manager.h
+ *
+ * @brief Interface of task_manager_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2006 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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.
+ */
+
+#ifndef TASK_MANAGER_H_
+#define TASK_MANAGER_H_
+
+typedef struct task_manager_t task_manager_t;
+
+#include <library.h>
+#include <encoding/message.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+
+/**
+ * @brief The task manager, juggles task and handles message exchanges.
+ *
+ * On incoming requests, the task manager creates new tasks on demand and
+ * juggles the request through all available tasks. Each task inspects the
+ * request and adds payloads as necessary to the response.
+ * On outgoing requests, the task manager delivers the request through the tasks
+ * to build it, the response gets processed by each task to complete.
+ * The task manager has an internal Queue to store task which should get
+ * completed.
+ * For the initial IKE_SA setup, several tasks are queued: One for the
+ * unauthenticated IKE_SA setup, one for authentication, one for CHILD_SA setup
+ * and maybe one for virtual IP assignement.
+ *
+ * @b Constructors:
+ * - task_manager_create()
+ *
+ * @ingroup sa
+ */
+struct task_manager_t {
+
+ /**
+ * @brief Process an incoming message.
+ *
+ * @param this calling object
+ * @param message message to add payloads to
+ * @return
+ * - DESTROY_ME if IKE_SA must be closed
+ * - SUCCESS otherwise
+ */
+ status_t (*process_message) (task_manager_t *this, message_t *message);
+
+ /**
+ * @brief Initiate an exchange with the currently queued tasks.
+ *
+ * @param this calling object
+ */
+ status_t (*initiate) (task_manager_t *this);
+
+ /**
+ * @brief Queue a task in the manager.
+ *
+ * @param this calling object
+ * @param task task to queue
+ */
+ void (*queue_task) (task_manager_t *this, task_t *task);
+
+ /**
+ * @brief Retransmit a request if it hasn't been acknowledged yet.
+ *
+ * A return value of INVALID_STATE means that the message was already
+ * acknowledged and has not to be retransmitted. A return value of SUCCESS
+ * means retransmission was required and the message has been resent.
+ *
+ * @param this calling object
+ * @param message_id ID of the message to retransmit
+ * @return
+ * - INVALID_STATE if retransmission not required
+ * - SUCCESS if retransmission sent
+ */
+ status_t (*retransmit) (task_manager_t *this, u_int32_t message_id);
+
+ /**
+ * @brief Migrate all tasks from other to this.
+ *
+ * To rekey or reestablish an IKE_SA completely, all queued or active
+ * tasks should get migrated to the new IKE_SA.
+ *
+ * @param this manager which gets all tasks
+ * @param other manager which gives away its tasks
+ */
+ void (*adopt_tasks) (task_manager_t *this, task_manager_t *other);
+
+ /**
+ * @brief Reset message ID counters of the task manager.
+ *
+ * The IKEv2 protocol requires to restart exchanges with message IDs
+ * reset to zero (INVALID_KE_PAYLOAD, COOKIES, ...). The reset() method
+ * resets the message IDs and resets all active tasks using the migrate()
+ * method.
+ *
+ * @param this calling object
+ * @param other manager which gives away its tasks
+ */
+ void (*reset) (task_manager_t *this);
+
+ /**
+ * @brief Check if we are currently waiting for a reply.
+ *
+ * @param this calling object
+ * @return TRUE if we are waiting, FALSE otherwise
+ */
+ bool (*busy) (task_manager_t *this);
+
+ /**
+ * @brief Destroy the task_manager_t.
+ *
+ * @param this calling object
+ */
+ void (*destroy) (task_manager_t *this);
+};
+
+/**
+ * @brief Create an instance of the task manager.
+ *
+ * @param ike_sa IKE_SA to manage.
+ *
+ * @ingroup sa
+ */
+task_manager_t *task_manager_create(ike_sa_t *ike_sa);
+
+#endif /* TASK_MANAGER_H_ */
diff --git a/src/charon/sa/tasks/child_create.c b/src/charon/sa/tasks/child_create.c
new file mode 100644
index 000000000..165baa4be
--- /dev/null
+++ b/src/charon/sa/tasks/child_create.c
@@ -0,0 +1,718 @@
+/**
+ * @file child_create.c
+ *
+ * @brief Implementation of the child_create task.
+ *
+ */
+
+/*
+ * Copyright (C) 2005-2007 Martin Willi
+ * Copyright (C) 2005 Jan Hutter
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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_create.h"
+
+#include <daemon.h>
+#include <crypto/diffie_hellman.h>
+#include <encoding/payloads/sa_payload.h>
+#include <encoding/payloads/ts_payload.h>
+#include <encoding/payloads/nonce_payload.h>
+#include <encoding/payloads/notify_payload.h>
+
+
+typedef struct private_child_create_t private_child_create_t;
+
+/**
+ * Private members of a child_create_t task.
+ */
+struct private_child_create_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ child_create_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * nonce chosen by us
+ */
+ chunk_t my_nonce;
+
+ /**
+ * nonce chosen by peer
+ */
+ chunk_t other_nonce;
+
+ /**
+ * policy to create the CHILD_SA from
+ */
+ policy_t *policy;
+
+ /**
+ * list of proposal candidates
+ */
+ linked_list_t *proposals;
+
+ /**
+ * selected proposal to use for CHILD_SA
+ */
+ proposal_t *proposal;
+
+ /**
+ * traffic selectors for initiators side
+ */
+ linked_list_t *tsi;
+
+ /**
+ * traffic selectors for responders side
+ */
+ linked_list_t *tsr;
+
+ /**
+ * mode the new CHILD_SA uses (transport/tunnel/beet)
+ */
+ mode_t mode;
+
+ /**
+ * reqid to use if we are rekeying
+ */
+ u_int32_t reqid;
+
+ /**
+ * CHILD_SA which gets established
+ */
+ child_sa_t *child_sa;
+};
+
+/**
+ * get the nonce from a message
+ */
+static status_t get_nonce(message_t *message, chunk_t *nonce)
+{
+ nonce_payload_t *payload;
+
+ payload = (nonce_payload_t*)message->get_payload(message, NONCE);
+ if (payload == NULL)
+ {
+ return FAILED;
+ }
+ *nonce = payload->get_nonce(payload);
+ return NEED_MORE;
+}
+
+/**
+ * generate a new nonce to include in a CREATE_CHILD_SA message
+ */
+static status_t generate_nonce(chunk_t *nonce)
+{
+ status_t status;
+ randomizer_t *randomizer = randomizer_create();
+
+ status = randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_SIZE,
+ nonce);
+ randomizer->destroy(randomizer);
+ if (status != SUCCESS)
+ {
+ DBG1(DBG_IKE, "error generating random nonce value");
+ return FAILED;
+ }
+ return SUCCESS;
+}
+
+/**
+ * Check a list of traffic selectors if any selector belongs to host
+ */
+static bool ts_list_is_host(linked_list_t *list, host_t *host)
+{
+ traffic_selector_t *ts;
+ bool is_host = TRUE;
+ iterator_t *iterator = list->create_iterator(list, TRUE);
+
+ while (is_host && iterator->iterate(iterator, (void**)&ts))
+ {
+ is_host = is_host && ts->is_host(ts, host);
+ }
+ iterator->destroy(iterator);
+ return is_host;
+}
+
+/**
+ * Install a CHILD_SA for usage
+ */
+static status_t select_and_install(private_child_create_t *this)
+{
+ prf_plus_t *prf_plus;
+ status_t status;
+ chunk_t nonce_i, nonce_r, seed;
+ linked_list_t *my_ts, *other_ts;
+ host_t *me, *other, *other_vip, *my_vip;
+
+ if (this->proposals == NULL || this->tsi == NULL || this->tsr == NULL)
+ {
+ SIG(CHILD_UP_FAILED, "SA/TS payloads missing in message");
+ return FAILED;
+ }
+
+ if (this->initiator)
+ {
+ nonce_i = this->my_nonce;
+ nonce_r = this->other_nonce;
+ my_ts = this->tsi;
+ other_ts = this->tsr;
+ }
+ else
+ {
+ nonce_r = this->my_nonce;
+ nonce_i = this->other_nonce;
+ my_ts = this->tsr;
+ other_ts = this->tsi;
+ }
+
+ me = this->ike_sa->get_my_host(this->ike_sa);
+ other = this->ike_sa->get_other_host(this->ike_sa);
+ my_vip = this->ike_sa->get_virtual_ip(this->ike_sa, TRUE);
+ other_vip = this->ike_sa->get_virtual_ip(this->ike_sa, FALSE);
+
+ this->proposal = this->policy->select_proposal(this->policy, this->proposals);
+
+ if (this->initiator && my_vip)
+ { /* if we have a virtual IP, shorten our TS to the minimum */
+ my_ts = this->policy->select_my_traffic_selectors(this->policy, my_ts,
+ my_vip);
+ }
+ else
+ { /* shorten in the host2host case only */
+ my_ts = this->policy->select_my_traffic_selectors(this->policy,
+ my_ts, me);
+ }
+ if (other_vip)
+ { /* if other has a virtual IP, shorten it's traffic selectors to it */
+ other_ts = this->policy->select_other_traffic_selectors(this->policy,
+ other_ts, other_vip);
+ }
+ else
+ { /* use his host for the host2host case */
+ other_ts = this->policy->select_other_traffic_selectors(this->policy,
+ other_ts, other);
+ }
+ this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy));
+ this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
+ if (this->initiator)
+ {
+ this->tsi = my_ts;
+ this->tsr = other_ts;
+ }
+ else
+ {
+ this->tsr = my_ts;
+ this->tsi = other_ts;
+ }
+
+ if (this->proposal == NULL ||
+ this->tsi->get_count(this->tsi) == 0 ||
+ this->tsr->get_count(this->tsr) == 0)
+ {
+ SIG(CHILD_UP_FAILED, "no acceptable proposal found");
+ return FAILED;
+ }
+
+ if (!this->initiator)
+ {
+ /* check if requested mode is acceptable, downgrade if required */
+ switch (this->mode)
+ {
+ case MODE_TRANSPORT:
+ if (!ts_list_is_host(this->tsi, other) ||
+ !ts_list_is_host(this->tsr, me))
+ {
+ this->mode = MODE_TUNNEL;
+ DBG1(DBG_IKE, "not using tranport mode, not host-to-host");
+ }
+ else if (this->ike_sa->is_natt_enabled(this->ike_sa))
+ {
+ this->mode = MODE_TUNNEL;
+ DBG1(DBG_IKE, "not using tranport mode, connection NATed");
+ }
+ break;
+ case MODE_BEET:
+ if (!ts_list_is_host(this->tsi, NULL) ||
+ !ts_list_is_host(this->tsr, NULL))
+ {
+ this->mode = MODE_TUNNEL;
+ DBG1(DBG_IKE, "not using BEET mode, not host-to-host");
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ seed = chunk_cata("cc", nonce_i, nonce_r);
+ prf_plus = prf_plus_create(this->ike_sa->get_child_prf(this->ike_sa), seed);
+
+ if (this->initiator)
+ {
+ status = this->child_sa->update(this->child_sa, this->proposal,
+ this->mode, prf_plus);
+ }
+ else
+ {
+ status = this->child_sa->add(this->child_sa, this->proposal,
+ this->mode, prf_plus);
+ }
+ prf_plus->destroy(prf_plus);
+
+ if (status != SUCCESS)
+ {
+ SIG(CHILD_UP_FAILED, "unable to install IPsec SA (SAD) in kernel");
+ return status;
+ }
+
+ status = this->child_sa->add_policies(this->child_sa, my_ts, other_ts,
+ this->mode);
+
+ if (status != SUCCESS)
+ {
+ SIG(CHILD_UP_FAILED, "unable to install IPsec policies (SPD) in kernel");
+ return status;
+ }
+ /* add to IKE_SA, and remove from task */
+ this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
+ this->ike_sa->add_child_sa(this->ike_sa, this->child_sa);
+ this->child_sa = NULL;
+ return SUCCESS;
+}
+
+/**
+ * build the payloads for the message
+ */
+static void build_payloads(private_child_create_t *this, message_t *message)
+{
+ sa_payload_t *sa_payload;
+ ts_payload_t *ts_payload;
+ nonce_payload_t *nonce_payload;
+
+ /* add SA payload */
+ if (this->initiator)
+ {
+ sa_payload = sa_payload_create_from_proposal_list(this->proposals);
+ }
+ else
+ {
+ sa_payload = sa_payload_create_from_proposal(this->proposal);
+ }
+ message->add_payload(message, (payload_t*)sa_payload);
+
+ /* add TSi/TSr payloads */
+ ts_payload = ts_payload_create_from_traffic_selectors(TRUE, this->tsi);
+ message->add_payload(message, (payload_t*)ts_payload);
+ ts_payload = ts_payload_create_from_traffic_selectors(FALSE, this->tsr);
+ message->add_payload(message, (payload_t*)ts_payload);
+
+ /* add nonce payload if not in IKE_AUTH */
+ if (message->get_exchange_type(message) == CREATE_CHILD_SA)
+ {
+ nonce_payload = nonce_payload_create();
+ nonce_payload->set_nonce(nonce_payload, this->my_nonce);
+ message->add_payload(message, (payload_t*)nonce_payload);
+ }
+
+ /* add a notify if we are not in tunnel mode */
+ switch (this->mode)
+ {
+ case MODE_TRANSPORT:
+ message->add_notify(message, FALSE, USE_TRANSPORT_MODE, chunk_empty);
+ break;
+ case MODE_BEET:
+ message->add_notify(message, FALSE, USE_BEET_MODE, chunk_empty);
+ break;
+ default:
+ break;
+ }
+}
+
+/**
+ * Read payloads from message
+ */
+static void process_payloads(private_child_create_t *this, message_t *message)
+{
+ iterator_t *iterator;
+ payload_t *payload;
+ sa_payload_t *sa_payload;
+ ts_payload_t *ts_payload;
+ notify_payload_t *notify_payload;
+
+ /* defaults to TUNNEL mode */
+ this->mode = MODE_TUNNEL;
+
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ switch (payload->get_type(payload))
+ {
+ case SECURITY_ASSOCIATION:
+ sa_payload = (sa_payload_t*)payload;
+ this->proposals = sa_payload->get_proposals(sa_payload);
+ break;
+ case TRAFFIC_SELECTOR_INITIATOR:
+ ts_payload = (ts_payload_t*)payload;
+ this->tsi = ts_payload->get_traffic_selectors(ts_payload);
+ break;
+ case TRAFFIC_SELECTOR_RESPONDER:
+ ts_payload = (ts_payload_t*)payload;
+ this->tsr = ts_payload->get_traffic_selectors(ts_payload);
+ break;
+ case NOTIFY:
+ notify_payload = (notify_payload_t*)payload;
+ switch (notify_payload ->get_notify_type(notify_payload ))
+ {
+ case USE_TRANSPORT_MODE:
+ this->mode = MODE_TRANSPORT;
+ break;
+ case USE_BEET_MODE:
+ this->mode = MODE_BEET;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+}
+
+/**
+ * Implementation of task_t.build for initiator
+ */
+static status_t build_i(private_child_create_t *this, message_t *message)
+{
+ host_t *me, *other, *vip;
+
+ switch (message->get_exchange_type(message))
+ {
+ case IKE_SA_INIT:
+ return get_nonce(message, &this->my_nonce);
+ case CREATE_CHILD_SA:
+ if (generate_nonce(&this->my_nonce) != SUCCESS)
+ {
+ message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, chunk_empty);
+ return SUCCESS;
+ }
+ break;
+ default:
+ break;
+ }
+
+ SIG(CHILD_UP_START, "establishing CHILD_SA");
+
+ me = this->ike_sa->get_my_host(this->ike_sa);
+ other = this->ike_sa->get_other_host(this->ike_sa);
+ vip = this->policy->get_virtual_ip(this->policy, NULL);
+
+ if (vip)
+ { /* propose a 0.0.0.0/0 subnet when we use virtual ip */
+ this->tsi = this->policy->get_my_traffic_selectors(this->policy, NULL);
+ vip->destroy(vip);
+ }
+ else
+ { /* but shorten a 0.0.0.0/0 subnet to the actual address if host2host */
+ this->tsi = this->policy->get_my_traffic_selectors(this->policy, me);
+ }
+ this->tsr = this->policy->get_other_traffic_selectors(this->policy, other);
+ this->proposals = this->policy->get_proposals(this->policy);
+ this->mode = this->policy->get_mode(this->policy);
+
+ this->child_sa = child_sa_create(me, other,
+ this->ike_sa->get_my_id(this->ike_sa),
+ this->ike_sa->get_other_id(this->ike_sa),
+ this->policy, this->reqid,
+ this->ike_sa->is_natt_enabled(this->ike_sa));
+
+ if (this->child_sa->alloc(this->child_sa, this->proposals) != SUCCESS)
+ {
+ SIG(CHILD_UP_FAILED, "unable to allocate SPIs from kernel");
+ return FAILED;
+ }
+
+ build_payloads(this, message);
+
+ this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
+ this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy));
+ this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy));
+ this->tsi = NULL;
+ this->tsr = NULL;
+ this->proposals = NULL;
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_r(private_child_create_t *this, message_t *message)
+{
+ switch (message->get_exchange_type(message))
+ {
+ case IKE_SA_INIT:
+ return get_nonce(message, &this->other_nonce);
+ case CREATE_CHILD_SA:
+ get_nonce(message, &this->other_nonce);
+ break;
+ default:
+ break;
+ }
+
+ process_payloads(this, message);
+
+ if (this->tsi == NULL || this->tsr == NULL)
+ {
+ DBG1(DBG_IKE, "TS payload missing in message");
+ return NEED_MORE;
+ }
+
+ this->policy = charon->policies->get_policy(charon->policies,
+ this->ike_sa->get_my_id(this->ike_sa),
+ this->ike_sa->get_other_id(this->ike_sa),
+ this->tsr, this->tsi,
+ this->ike_sa->get_my_host(this->ike_sa),
+ this->ike_sa->get_other_host(this->ike_sa));
+
+ if (this->policy && this->ike_sa->get_policy(this->ike_sa) == NULL)
+ {
+ this->ike_sa->set_policy(this->ike_sa, this->policy);
+ }
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for responder
+ */
+static status_t build_r(private_child_create_t *this, message_t *message)
+{
+ switch (message->get_exchange_type(message))
+ {
+ case IKE_SA_INIT:
+ return get_nonce(message, &this->my_nonce);
+ case CREATE_CHILD_SA:
+ if (generate_nonce(&this->my_nonce) != SUCCESS)
+ {
+ message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, chunk_empty);
+ return SUCCESS;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (this->policy == NULL)
+ {
+ SIG(CHILD_UP_FAILED, "received traffic selectors inacceptable");
+ message->add_notify(message, FALSE, TS_UNACCEPTABLE, chunk_empty);
+ return SUCCESS;
+ }
+
+ 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->ike_sa->get_my_id(this->ike_sa),
+ this->ike_sa->get_other_id(this->ike_sa),
+ this->policy, this->reqid,
+ this->ike_sa->is_natt_enabled(this->ike_sa));
+
+ if (select_and_install(this) != SUCCESS)
+ {
+ message->add_notify(message, FALSE, NO_PROPOSAL_CHOSEN, chunk_empty);
+ return SUCCESS;
+ }
+
+ build_payloads(this, message);
+
+ SIG(CHILD_UP_SUCCESS, "established CHILD_SA successfully");
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_i(private_child_create_t *this, message_t *message)
+{
+ iterator_t *iterator;
+ payload_t *payload;
+ status_t status;
+
+ switch (message->get_exchange_type(message))
+ {
+ case IKE_SA_INIT:
+ return get_nonce(message, &this->other_nonce);
+ case CREATE_CHILD_SA:
+ get_nonce(message, &this->other_nonce);
+ break;
+ default:
+ break;
+ }
+
+ /* check for erronous notifies */
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ if (payload->get_type(payload) == NOTIFY)
+ {
+ notify_payload_t *notify = (notify_payload_t*)payload;
+ notify_type_t type = notify->get_notify_type(notify);
+
+ if (type < 16383)
+ {
+ SIG(CHILD_UP_FAILED, "received %N notify error",
+ notify_type_names, type);
+ iterator->destroy(iterator);
+ /* an error in CHILD_SA creation is not critical */
+ return SUCCESS;
+ }
+ }
+ }
+ iterator->destroy(iterator);
+
+ process_payloads(this, message);
+
+ status = select_and_install(this);
+
+ SIG(CHILD_UP_SUCCESS, "established CHILD_SA successfully");
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_child_create_t *this)
+{
+ return CHILD_CREATE;
+}
+
+/**
+ * Implementation of child_create_t.use_reqid
+ */
+static void use_reqid(private_child_create_t *this, u_int32_t reqid)
+{
+ this->reqid = reqid;
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_child_create_t *this, ike_sa_t *ike_sa)
+{
+ chunk_free(&this->my_nonce);
+ chunk_free(&this->other_nonce);
+ if (this->tsi)
+ {
+ this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy));
+ }
+ if (this->tsr)
+ {
+ this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
+ }
+ DESTROY_IF(this->child_sa);
+ DESTROY_IF(this->proposal);
+ if (this->proposals)
+ {
+ this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy));
+ }
+
+ this->ike_sa = ike_sa;
+ this->proposals = NULL;
+ this->tsi = NULL;
+ this->tsr = NULL;
+ this->child_sa = NULL;
+ this->mode = MODE_TUNNEL;
+ this->reqid = 0;
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_child_create_t *this)
+{
+ chunk_free(&this->my_nonce);
+ chunk_free(&this->other_nonce);
+ if (this->tsi)
+ {
+ this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy));
+ }
+ if (this->tsr)
+ {
+ this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
+ }
+ DESTROY_IF(this->child_sa);
+ DESTROY_IF(this->proposal);
+ if (this->proposals)
+ {
+ this->proposals->destroy_offset(this->proposals, offsetof(proposal_t, destroy));
+ }
+
+ DESTROY_IF(this->policy);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+child_create_t *child_create_create(ike_sa_t *ike_sa, policy_t *policy)
+{
+ private_child_create_t *this = malloc_thing(private_child_create_t);
+
+ this->public.use_reqid = (void(*)(child_create_t*,u_int32_t))use_reqid;
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+ if (policy)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
+ this->initiator = TRUE;
+ policy->get_ref(policy);
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
+ this->initiator = FALSE;
+ }
+
+ this->ike_sa = ike_sa;
+ this->policy = policy;
+ this->my_nonce = chunk_empty;
+ this->other_nonce = chunk_empty;
+ this->proposals = NULL;
+ this->proposal = NULL;
+ this->tsi = NULL;
+ this->tsr = NULL;
+ this->child_sa = NULL;
+ this->mode = MODE_TUNNEL;
+ this->reqid = 0;
+
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/child_create.h b/src/charon/sa/tasks/child_create.h
new file mode 100644
index 000000000..1644865b9
--- /dev/null
+++ b/src/charon/sa/tasks/child_create.h
@@ -0,0 +1,72 @@
+/**
+ * @file child_create.h
+ *
+ * @brief Interface child_create_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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.
+ */
+
+#ifndef CHILD_CREATE_H_
+#define CHILD_CREATE_H_
+
+typedef struct child_create_t child_create_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+#include <config/policies/policy.h>
+
+/**
+ * @brief Task of type CHILD_CREATE, established a new CHILD_SA.
+ *
+ * This task may be included in the IKE_AUTH message or in a separate
+ * CREATE_CHILD_SA exchange.
+ *
+ * @b Constructors:
+ * - child_create_create()
+ *
+ * @ingroup tasks
+ */
+struct child_create_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+
+ /**
+ * @brief Use a specific reqid for the CHILD_SA.
+ *
+ * When this task is used for rekeying, the same reqid is used
+ * for the new CHILD_SA.
+ *
+ * @param this calling object
+ * @param reqid reqid to use
+ */
+ void (*use_reqid) (child_create_t *this, u_int32_t reqid);
+};
+
+/**
+ * @brief Create a new child_create task.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param policy policy if task initiator, NULL if responder
+ * @return child_create task to handle by the task_manager
+ */
+child_create_t *child_create_create(ike_sa_t *ike_sa, policy_t *policy);
+
+#endif /* CHILD_CREATE_H_ */
diff --git a/src/charon/sa/tasks/child_delete.c b/src/charon/sa/tasks/child_delete.c
new file mode 100644
index 000000000..875f14acc
--- /dev/null
+++ b/src/charon/sa/tasks/child_delete.c
@@ -0,0 +1,264 @@
+/**
+ * @file child_delete.c
+ *
+ * @brief Implementation of the child_delete task.
+ *
+ */
+
+/*
+ * Copyright (C) 2006-2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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_delete.h"
+
+#include <daemon.h>
+#include <encoding/payloads/delete_payload.h>
+
+
+typedef struct private_child_delete_t private_child_delete_t;
+
+/**
+ * Private members of a child_delete_t task.
+ */
+struct private_child_delete_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ child_delete_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * CHILD_SAs which get deleted
+ */
+ linked_list_t *child_sas;
+};
+
+/**
+ * build the delete payloads from the listed child_sas
+ */
+static void build_payloads(private_child_delete_t *this, message_t *message)
+{
+ iterator_t *iterator;
+ delete_payload_t *ah = NULL, *esp = NULL;
+ u_int32_t spi;
+ child_sa_t *child_sa;
+
+ iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
+ while (iterator->iterate(iterator, (void**)&child_sa))
+ {
+ spi = child_sa->get_spi(child_sa, TRUE);
+ switch (child_sa->get_protocol(child_sa))
+ {
+ case PROTO_ESP:
+ if (esp == NULL)
+ {
+ esp = delete_payload_create(PROTO_ESP);
+ message->add_payload(message, (payload_t*)esp);
+ }
+ esp->add_spi(esp, spi);
+ break;
+ case PROTO_AH:
+ if (ah == NULL)
+ {
+ ah = delete_payload_create(PROTO_AH);
+ message->add_payload(message, (payload_t*)ah);
+ }
+ ah->add_spi(ah, spi);
+ break;
+ default:
+ break;
+ }
+ child_sa->set_state(child_sa, CHILD_DELETING);
+ }
+ iterator->destroy(iterator);
+}
+
+/**
+ * read in payloads and find the children to delete
+ */
+static void process_payloads(private_child_delete_t *this, message_t *message)
+{
+ iterator_t *payloads, *spis;
+ payload_t *payload;
+ delete_payload_t *delete_payload;
+ u_int32_t spi;
+ protocol_id_t protocol;
+ child_sa_t *child_sa;
+
+ payloads = message->get_payload_iterator(message);
+ while (payloads->iterate(payloads, (void**)&payload))
+ {
+ if (payload->get_type(payload) == DELETE)
+ {
+ delete_payload = (delete_payload_t*)payload;
+ protocol = delete_payload->get_protocol_id(delete_payload);
+ if (protocol != PROTO_ESP && protocol != PROTO_AH)
+ {
+ continue;
+ }
+ spis = delete_payload->create_spi_iterator(delete_payload);
+ while (spis->iterate(spis, (void**)&spi))
+ {
+ child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol,
+ spi, FALSE);
+ if (child_sa == NULL)
+ {
+ DBG1(DBG_IKE, "received DELETE for %N CHILD_SA with SPI 0x%x, "
+ "but no such SA", protocol_id_names, protocol, ntohl(spi));
+ break;
+ }
+
+ if (child_sa->get_state(child_sa) == CHILD_REKEYING)
+ {
+ /* TODO: handle rekeying */
+ }
+
+ this->child_sas->insert_last(this->child_sas, child_sa);
+ }
+ spis->destroy(spis);
+ }
+ }
+ payloads->destroy(payloads);
+}
+
+/**
+ * destroy the children listed in this->child_sas
+ */
+static void destroy_children(private_child_delete_t *this)
+{
+ iterator_t *iterator;
+ child_sa_t *child_sa;
+ protocol_id_t protocol;
+ u_int32_t spi;
+
+ iterator = this->child_sas->create_iterator(this->child_sas, TRUE);
+ while (iterator->iterate(iterator, (void**)&child_sa))
+ {
+ /* TODO: can we do this more cleanly? */
+ spi = child_sa->get_spi(child_sa, TRUE);
+ protocol = child_sa->get_protocol(child_sa);
+ this->ike_sa->destroy_child_sa(this->ike_sa, protocol, spi);
+ }
+ iterator->destroy(iterator);
+}
+
+/**
+ * Implementation of task_t.build for initiator
+ */
+static status_t build_i(private_child_delete_t *this, message_t *message)
+{
+ build_payloads(this, message);
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_i(private_child_delete_t *this, message_t *message)
+{
+ /* flush the list before adding new SAs */
+ this->child_sas->destroy(this->child_sas);
+ this->child_sas = linked_list_create();
+
+ process_payloads(this, message);
+ destroy_children(this);
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_r(private_child_delete_t *this, message_t *message)
+{
+ process_payloads(this, message);
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for responder
+ */
+static status_t build_r(private_child_delete_t *this, message_t *message)
+{
+ build_payloads(this, message);
+ destroy_children(this);
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_child_delete_t *this)
+{
+ return CHILD_DELETE;
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_child_delete_t *this, ike_sa_t *ike_sa)
+{
+ this->ike_sa = ike_sa;
+
+ this->child_sas->destroy(this->child_sas);
+ this->child_sas = linked_list_create();
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_child_delete_t *this)
+{
+ this->child_sas->destroy(this->child_sas);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+child_delete_t *child_delete_create(ike_sa_t *ike_sa, child_sa_t *child_sa)
+{
+ private_child_delete_t *this = malloc_thing(private_child_delete_t);
+
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+
+ this->ike_sa = ike_sa;
+ this->child_sas = linked_list_create();
+
+ if (child_sa != NULL)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
+ this->initiator = TRUE;
+ this->child_sas->insert_last(this->child_sas, child_sa);
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
+ this->initiator = FALSE;
+ }
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/child_delete.h b/src/charon/sa/tasks/child_delete.h
new file mode 100644
index 000000000..327a2ec15
--- /dev/null
+++ b/src/charon/sa/tasks/child_delete.h
@@ -0,0 +1,58 @@
+/**
+ * @file child_delete.h
+ *
+ * @brief Interface child_delete_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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.
+ */
+
+#ifndef CHILD_DELETE_H_
+#define CHILD_DELETE_H_
+
+typedef struct child_delete_t child_delete_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+#include <sa/child_sa.h>
+
+/**
+ * @brief Task of type child_delete, delete a CHILD_SA.
+ *
+ * @b Constructors:
+ * - child_delete_create()
+ *
+ * @ingroup tasks
+ */
+struct child_delete_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * @brief Create a new child_delete task.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param child_sa CHILD_SA to delete, or NULL as responder
+ * @return child_delete task to handle by the task_manager
+ */
+child_delete_t *child_delete_create(ike_sa_t *ike_sa, child_sa_t *child_sa);
+
+#endif /* CHILD_DELETE_H_ */
diff --git a/src/charon/sa/tasks/child_rekey.c b/src/charon/sa/tasks/child_rekey.c
new file mode 100644
index 000000000..41533b68c
--- /dev/null
+++ b/src/charon/sa/tasks/child_rekey.c
@@ -0,0 +1,353 @@
+/**
+ * @file child_rekey.c
+ *
+ * @brief Implementation of the child_rekey task.
+ *
+ */
+
+/*
+ * Copyright (C) 2005-2007 Martin Willi
+ * Copyright (C) 2005 Jan Hutter
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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_rekey.h"
+
+#include <daemon.h>
+#include <crypto/diffie_hellman.h>
+#include <encoding/payloads/notify_payload.h>
+#include <encoding/payloads/nonce_payload.h>
+#include <sa/tasks/child_create.h>
+
+
+typedef struct private_child_rekey_t private_child_rekey_t;
+
+/**
+ * Private members of a child_rekey_t task.
+ */
+struct private_child_rekey_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ child_rekey_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * the CHILD_CREATE task which is reused to simplify rekeying
+ */
+ child_create_t *child_create;
+
+ /**
+ * CHILD_SA which gets rekeyed
+ */
+ child_sa_t *child_sa;
+
+ /**
+ * redundandt CHILD_SA created simultaneously
+ */
+ child_sa_t *simultaneous;
+
+ /**
+ * the lowest nonce compared so far
+ */
+ chunk_t nonce;
+
+ /**
+ * TRUE if we have the lower nonce
+ */
+ bool winner;
+};
+
+/**
+ * get the nonce from a message, return TRUE if it was lower than this->nonce
+ */
+static bool get_nonce(private_child_rekey_t *this, message_t *message)
+{
+ nonce_payload_t *payload;
+ chunk_t nonce;
+
+ payload = (nonce_payload_t*)message->get_payload(message, NONCE);
+ if (payload == NULL)
+ {
+ return FALSE;
+ }
+ nonce = payload->get_nonce(payload);
+
+ if (this->nonce.ptr && memcmp(nonce.ptr, this->nonce.ptr,
+ min(nonce.len, this->nonce.len)) > 0)
+ {
+ chunk_free(&nonce);
+ return FALSE;
+ }
+
+ chunk_free(&this->nonce);
+ this->nonce = nonce;
+ return TRUE;
+}
+
+/**
+ * find a child using the REKEY_SA notify
+ */
+static void find_child(private_child_rekey_t *this, message_t *message)
+{
+ iterator_t *iterator;
+ payload_t *payload;
+
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ notify_payload_t *notify;
+ u_int32_t spi;
+ protocol_id_t protocol;
+
+ if (payload->get_type(payload) != NOTIFY)
+ {
+ continue;
+ }
+
+ notify = (notify_payload_t*)payload;
+ protocol = notify->get_protocol_id(notify);
+ spi = notify->get_spi(notify);
+
+ if (protocol != PROTO_ESP && protocol != PROTO_AH)
+ {
+ continue;
+ }
+ this->child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol,
+ spi, FALSE);
+ break;
+
+ }
+ iterator->destroy(iterator);
+}
+
+#if 0
+/**
+ * handle a detected simultaneous rekeying situation as responder
+ */
+static void simultaneous_r(private_child_rekey_t *this, message_t *message)
+{
+ private_child_rekey_t *other = NULL;
+ task_t *task;
+ iterator_t *iterator;
+
+ this->ike_sa->create_task_iterator(this->ike_sa);
+ while (iterator->iterate(iterator, (void**)&task))
+ {
+ if (task->get_type(task) == CHILD_REKEY)
+ {
+ other = (private_child_rekey_t*)task;
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+
+ if (other)
+ {
+ other->simultaneous = this->child_create->get_child(this->child_create);
+
+ if (!get_nonce(other, message))
+ {
+ /* this wins the race, other lost */
+ other->winner = FALSE;
+ }
+ }
+}
+
+/**
+ * was there a simultaneous rekeying, did we win the nonce compare?
+ */
+static bool simultaneous_i(private_child_rekey_t *this, message_t *message)
+{
+ if (this->winner || get_nonce(this, message))
+ {
+ /* we have the lower nonce and win */
+ return TRUE;
+ }
+ return FALSE;
+}
+#endif
+
+/**
+ * Implementation of task_t.build for initiator
+ */
+static status_t build_i(private_child_rekey_t *this, message_t *message)
+{
+ notify_payload_t *notify;
+ protocol_id_t protocol;
+ u_int32_t spi, reqid;
+
+ /* our CHILD_CREATE task does the hard work for us... */
+ reqid = this->child_sa->get_reqid(this->child_sa);
+ this->child_create->use_reqid(this->child_create, reqid);
+ this->child_create->task.build(&this->child_create->task, message);
+ get_nonce(this, message);
+
+ /* ... we just need the rekey notify */
+ protocol = this->child_sa->get_protocol(this->child_sa);
+ spi = this->child_sa->get_spi(this->child_sa, TRUE);
+ notify = notify_payload_create_from_protocol_and_type(protocol, REKEY_SA);
+ notify->set_spi(notify, spi);
+ message->add_payload(message, (payload_t*)notify);
+
+ this->child_sa->set_state(this->child_sa, CHILD_REKEYING);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_r(private_child_rekey_t *this, message_t *message)
+{
+ /* let the CHILD_CREATE task process the message */
+ this->child_create->task.process(&this->child_create->task, message);
+ get_nonce(this, message);
+
+ find_child(this, message);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for responder
+ */
+static status_t build_r(private_child_rekey_t *this, message_t *message)
+{
+ u_int32_t reqid;
+
+ if (this->child_sa == NULL ||
+ this->child_sa->get_state(this->child_sa) == CHILD_DELETING)
+ {
+ message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
+ return SUCCESS;
+ }
+
+ /* 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->task.build(&this->child_create->task, message);
+ get_nonce(this, message);
+
+ if (this->child_sa->get_state(this->child_sa) == CHILD_REKEYING)
+ {
+ /* simultaneous_detected(this); */
+ }
+
+ this->child_sa->set_state(this->child_sa, CHILD_REKEYING);
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_i(private_child_rekey_t *this, message_t *message)
+{
+ protocol_id_t protocol;
+ u_int32_t spi;
+
+ this->child_create->task.process(&this->child_create->task, message);
+
+ /*if (!simultaneous_won(this, message))
+ {
+ * delete the redundant CHILD_SA, instead of the rekeyed *
+ this->child_sa = this->create_child->get_child(this->create_child);
+ }*/
+ spi = this->child_sa->get_spi(this->child_sa, TRUE);
+ protocol = this->child_sa->get_protocol(this->child_sa);
+
+ /* TODO: don't delete when rekeying failed */
+ if (this->ike_sa->delete_child_sa(this->ike_sa, protocol, spi) != SUCCESS)
+ {
+ return FAILED;
+ }
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_child_rekey_t *this)
+{
+ return CHILD_REKEY;
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_child_rekey_t *this, ike_sa_t *ike_sa)
+{
+ this->child_create->task.migrate(&this->child_create->task, ike_sa);
+ chunk_free(&this->nonce);
+
+ this->ike_sa = ike_sa;
+ this->winner = TRUE;
+ this->simultaneous = NULL;
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_child_rekey_t *this)
+{
+ this->child_create->task.destroy(&this->child_create->task);
+ chunk_free(&this->nonce);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, child_sa_t *child_sa)
+{
+ private_child_rekey_t *this = malloc_thing(private_child_rekey_t);
+ policy_t *policy;
+
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+ if (child_sa != NULL)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
+ this->initiator = TRUE;
+ policy = child_sa->get_policy(child_sa);
+ this->child_create = child_create_create(ike_sa, policy);
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
+ this->initiator = FALSE;
+ this->child_create = child_create_create(ike_sa, NULL);
+ }
+
+ this->ike_sa = ike_sa;
+ this->child_sa = child_sa;
+ this->nonce = chunk_empty;
+ this->winner = TRUE;
+ this->simultaneous = NULL;
+
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/child_rekey.h b/src/charon/sa/tasks/child_rekey.h
new file mode 100644
index 000000000..77297b13d
--- /dev/null
+++ b/src/charon/sa/tasks/child_rekey.h
@@ -0,0 +1,58 @@
+/**
+ * @file child_rekey.h
+ *
+ * @brief Interface child_rekey_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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.
+ */
+
+#ifndef CHILD_REKEY_H_
+#define CHILD_REKEY_H_
+
+typedef struct child_rekey_t child_rekey_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/child_sa.h>
+#include <sa/tasks/task.h>
+
+/**
+ * @brief Task of type CHILD_REKEY, rekey an established CHILD_SA.
+ *
+ * @b Constructors:
+ * - child_rekey_create()
+ *
+ * @ingroup tasks
+ */
+struct child_rekey_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * @brief Create a new CHILD_REKEY task.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param child_sa child_sa to rekey, NULL if responder
+ * @return child_rekey task to handle by the task_manager
+ */
+child_rekey_t *child_rekey_create(ike_sa_t *ike_sa, child_sa_t *child_sa);
+
+#endif /* CHILD_REKEY_H_ */
diff --git a/src/charon/sa/tasks/ike_auth.c b/src/charon/sa/tasks/ike_auth.c
new file mode 100644
index 000000000..2e54b7797
--- /dev/null
+++ b/src/charon/sa/tasks/ike_auth.c
@@ -0,0 +1,513 @@
+/**
+ * @file ike_auth.c
+ *
+ * @brief Implementation of the ike_auth task.
+ *
+ */
+
+/*
+ * Copyright (C) 2005-2007 Martin Willi
+ * Copyright (C) 2005 Jan Hutter
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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_auth.h"
+
+#include <string.h>
+
+#include <daemon.h>
+#include <crypto/diffie_hellman.h>
+#include <encoding/payloads/id_payload.h>
+#include <encoding/payloads/auth_payload.h>
+#include <encoding/payloads/nonce_payload.h>
+
+
+typedef struct private_ike_auth_t private_ike_auth_t;
+
+/**
+ * Private members of a ike_auth_t task.
+ */
+struct private_ike_auth_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ ike_auth_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * Nonce chosen by us in ike_init
+ */
+ chunk_t my_nonce;
+
+ /**
+ * Nonce chosen by peer in ike_init
+ */
+ chunk_t other_nonce;
+
+ /**
+ * IKE_SA_INIT message sent by us
+ */
+ packet_t *my_packet;
+
+ /**
+ * IKE_SA_INIT message sent by peer
+ */
+ packet_t *other_packet;
+
+ /**
+ * authenticator to authenticate us
+ */
+ authenticator_t *my_auth;
+
+ /**
+ * authenticator to authenticate peer
+ */
+ authenticator_t *other_auth;
+
+ /**
+ * has the peer been authenticated successfully?
+ */
+ bool peer_authenticated;
+};
+
+/**
+ * build the payloads for the message
+ */
+static status_t build_payloads(private_ike_auth_t *this, message_t *message)
+{
+ authenticator_t *auth;
+ auth_payload_t *auth_payload;
+ id_payload_t *id_payload;
+ chunk_t ike_sa_init;
+ identification_t *me, *other;
+ policy_t *policy;
+ auth_method_t method = AUTH_RSA;
+ status_t status;
+
+ /* add own ID payload */
+ me = this->ike_sa->get_my_id(this->ike_sa);
+ other = this->ike_sa->get_other_id(this->ike_sa);
+
+ id_payload = id_payload_create_from_identification(this->initiator, me);
+ message->add_payload(message, (payload_t*)id_payload);
+
+ /* as initiator, include other ID if it does not contain wildcards */
+ if (this->initiator && !other->contains_wildcards(other))
+ {
+ id_payload = id_payload_create_from_identification(FALSE, other);
+ message->add_payload(message, (payload_t*)id_payload);
+ }
+
+ /* create own authenticator and add auth payload */
+ policy = this->ike_sa->get_policy(this->ike_sa);
+ if (policy)
+ {
+ method = policy->get_auth_method(policy);
+ }
+ auth = authenticator_create(this->ike_sa, method);
+ if (auth == NULL)
+ {
+ SIG(IKE_UP_FAILED, "configured authentication method %N not supported",
+ auth_method_names, method);
+ return FAILED;
+ }
+
+ ike_sa_init = this->my_packet->get_data(this->my_packet);
+ status = auth->build(auth, ike_sa_init, this->other_nonce, &auth_payload);
+ auth->destroy(auth);
+ if (status != SUCCESS)
+ {
+ SIG(IKE_UP_FAILED, "generating authentication data failed");
+ return FAILED;
+ }
+ message->add_payload(message, (payload_t*)auth_payload);
+
+ return SUCCESS;
+}
+
+/**
+ * process payloads from message
+ */
+static void process_payloads(private_ike_auth_t *this, message_t *message)
+{
+ iterator_t *iterator;
+ payload_t *payload;
+ payload_type_t type;
+ identification_t *idi = NULL, *idr = NULL;
+ auth_payload_t *auth_payload = NULL;
+ authenticator_t *auth;
+ auth_method_t auth_method;
+ status_t status;
+
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ type = payload->get_type(payload);
+ switch (type)
+ {
+ case ID_INITIATOR:
+ {
+ id_payload_t *id_payload = (id_payload_t*)payload;
+ idi = id_payload->get_identification(id_payload);
+ break;
+ }
+ case ID_RESPONDER:
+ {
+ id_payload_t *id_payload = (id_payload_t*)payload;
+ idr = id_payload->get_identification(id_payload);
+ break;
+ }
+ case AUTHENTICATION:
+ {
+ auth_payload = (auth_payload_t*)payload;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+
+ /* apply IDs */
+ if ((this->initiator && idr == NULL) || (!this->initiator && idi == NULL))
+ {
+ SIG(IKE_UP_FAILED, "ID payload missing in message");
+ DESTROY_IF(idr); DESTROY_IF(idi);
+ return;
+ }
+
+ if (this->initiator)
+ {
+ identification_t *other_id = this->ike_sa->get_other_id(this->ike_sa);
+ if (!idr->matches(idr, other_id, NULL))
+ {
+ SIG(IKE_UP_FAILED, "received inacceptable id %D, %D required", idr,
+ this->ike_sa->get_other_id(this->ike_sa));
+ DESTROY_IF(idi); DESTROY_IF(idr);
+ return;
+ }
+ this->ike_sa->set_other_id(this->ike_sa, idr);
+ }
+ else
+ {
+ identification_t *my_id = this->ike_sa->get_other_id(this->ike_sa);
+ if (idr)
+ {
+ if (!idr->matches(idr, my_id, NULL))
+ {
+ SIG(IKE_UP_FAILED, "received inacceptable id %D, %D required",
+ idr, this->ike_sa->get_other_id(this->ike_sa));
+ DESTROY_IF(idi); DESTROY_IF(idr);
+ return;
+ }
+ this->ike_sa->set_my_id(this->ike_sa, idr);
+ }
+ else
+ {
+ if (my_id->contains_wildcards(my_id))
+ {
+ SIG(IKE_UP_FAILED, "own ID (%D) not defined after exchange",
+ my_id);
+ DESTROY_IF(idi);
+ return;
+ }
+ }
+ this->ike_sa->set_other_id(this->ike_sa, idi);
+ }
+
+ /* verify auth payload */
+ if (auth_payload == NULL)
+ {
+ SIG(IKE_UP_FAILED, "AUTH payload missing in message");
+ return;
+ }
+
+ auth_method = auth_payload->get_auth_method(auth_payload);
+ auth = authenticator_create(this->ike_sa, auth_method);
+ if (auth == NULL)
+ {
+ SIG(IKE_UP_FAILED, "authentication method %N used by %D not "
+ "supported", auth_method_names, auth_method,
+ this->ike_sa->get_other_id(this->ike_sa));
+ return;
+ }
+ status = auth->verify(auth, this->other_packet->get_data(this->other_packet),
+ this->my_nonce, auth_payload);
+ auth->destroy(auth);
+ if (status != SUCCESS)
+ {
+ SIG(IKE_UP_FAILED, "authentication of %D using %N failed",
+ this->ike_sa->get_other_id(this->ike_sa),
+ auth_method_names, auth_method);
+ return;
+ }
+ this->peer_authenticated = TRUE;
+}
+
+/**
+ * collect the needed information in the IKE_SA_INIT exchange from our message
+ */
+static status_t collect_my_init_data(private_ike_auth_t *this, message_t *message)
+{
+ nonce_payload_t *nonce;
+
+ /* get the nonce that was generated in ike_init */
+ nonce = (nonce_payload_t*)message->get_payload(message, NONCE);
+ if (nonce == NULL)
+ {
+ return FAILED;
+ }
+ this->my_nonce = nonce->get_nonce(nonce);
+
+ /* pre-generate the message, so we can store it for us */
+ if (this->ike_sa->generate_message(this->ike_sa, message,
+ &this->my_packet) != SUCCESS)
+ {
+ return FAILED;
+ }
+ return NEED_MORE;
+}
+
+/**
+ * collect the needed information in the IKE_SA_INIT exchange from others message
+ */
+static status_t collect_other_init_data(private_ike_auth_t *this, message_t *message)
+{
+ /* we collect the needed information in the IKE_SA_INIT exchange */
+ nonce_payload_t *nonce;
+
+ /* get the nonce that was generated in ike_init */
+ nonce = (nonce_payload_t*)message->get_payload(message, NONCE);
+ if (nonce == NULL)
+ {
+ return FAILED;
+ }
+ this->other_nonce = nonce->get_nonce(nonce);
+
+ /* pre-generate the message, so we can store it for us */
+ this->other_packet = message->get_packet(message);
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for initiator
+ */
+static status_t build_i(private_ike_auth_t *this, message_t *message)
+{
+ if (message->get_exchange_type(message) == IKE_SA_INIT)
+ {
+ return collect_my_init_data(this, message);
+ }
+
+ if (build_payloads(this, message) == SUCCESS)
+ {
+ return NEED_MORE;
+ }
+ return FAILED;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_r(private_ike_auth_t *this, message_t *message)
+{
+ if (message->get_exchange_type(message) == IKE_SA_INIT)
+ {
+ return collect_other_init_data(this, message);
+ }
+
+ process_payloads(this, message);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for responder
+ */
+static status_t build_r(private_ike_auth_t *this, message_t *message)
+{
+ if (message->get_exchange_type(message) == IKE_SA_INIT)
+ {
+ return collect_my_init_data(this, message);
+ }
+
+ if (!this->peer_authenticated)
+ {
+ message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty);
+ return FAILED;
+ }
+
+ if (build_payloads(this, message) == SUCCESS)
+ {
+ this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
+ SIG(IKE_UP_SUCCESS, "IKE_SA established between %D[%H]...[%H]%D",
+ this->ike_sa->get_my_id(this->ike_sa),
+ this->ike_sa->get_my_host(this->ike_sa),
+ this->ike_sa->get_other_host(this->ike_sa),
+ this->ike_sa->get_other_id(this->ike_sa));
+ return SUCCESS;
+ }
+ return FAILED;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_i(private_ike_auth_t *this, message_t *message)
+{
+ iterator_t *iterator;
+ payload_t *payload;
+
+ if (message->get_exchange_type(message) == IKE_SA_INIT)
+ {
+ return collect_other_init_data(this, message);
+ }
+
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ if (payload->get_type(payload) == NOTIFY)
+ {
+ notify_payload_t *notify = (notify_payload_t*)payload;
+ notify_type_t type = notify->get_notify_type(notify);
+
+ switch (type)
+ {
+ case NO_PROPOSAL_CHOSEN:
+ case SINGLE_PAIR_REQUIRED:
+ case NO_ADDITIONAL_SAS:
+ case INTERNAL_ADDRESS_FAILURE:
+ case FAILED_CP_REQUIRED:
+ case TS_UNACCEPTABLE:
+ case INVALID_SELECTORS:
+ /* these are errors, but are not critical as only the
+ * CHILD_SA won't get build, but IKE_SA establishes anyway */
+ DBG1(DBG_IKE, "received %N notify, no CHILD_SA built",
+ notify_type_names, type);
+ iterator->destroy(iterator);
+ return SUCCESS;
+ default:
+ {
+ if (type < 16383)
+ {
+ DBG1(DBG_IKE, "received %N notify error",
+ notify_type_names, type);
+ iterator->destroy(iterator);
+ return FAILED;
+ }
+ }
+ }
+ }
+ }
+ iterator->destroy(iterator);
+
+ process_payloads(this, message);
+
+ if (this->peer_authenticated)
+ {
+ this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
+ SIG(IKE_UP_SUCCESS, "IKE_SA established between %D[%H]...[%H]%D",
+ this->ike_sa->get_my_id(this->ike_sa),
+ this->ike_sa->get_my_host(this->ike_sa),
+ this->ike_sa->get_other_host(this->ike_sa),
+ this->ike_sa->get_other_id(this->ike_sa));
+ return SUCCESS;
+ }
+ return FAILED;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_ike_auth_t *this)
+{
+ return IKE_AUTHENTICATE;
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_ike_auth_t *this, ike_sa_t *ike_sa)
+{
+ chunk_free(&this->my_nonce);
+ chunk_free(&this->other_nonce);
+ DESTROY_IF(this->my_packet);
+ DESTROY_IF(this->other_packet);
+ DESTROY_IF(this->my_auth);
+ DESTROY_IF(this->other_auth);
+ this->my_packet = NULL;
+ this->other_packet = NULL;
+ this->my_auth = NULL;
+ this->other_auth = NULL;
+ this->peer_authenticated = FALSE;
+ this->ike_sa = ike_sa;
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_ike_auth_t *this)
+{
+ chunk_free(&this->my_nonce);
+ chunk_free(&this->other_nonce);
+ DESTROY_IF(this->my_packet);
+ DESTROY_IF(this->other_packet);
+ DESTROY_IF(this->my_auth);
+ DESTROY_IF(this->other_auth);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_auth_t *ike_auth_create(ike_sa_t *ike_sa, bool initiator)
+{
+ private_ike_auth_t *this = malloc_thing(private_ike_auth_t);
+
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+
+ if (initiator)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
+ }
+
+ this->ike_sa = ike_sa;
+ this->initiator = initiator;
+ this->my_nonce = chunk_empty;
+ this->other_nonce = chunk_empty;
+ this->my_packet = NULL;
+ this->other_packet = NULL;
+ this->my_auth = NULL;
+ this->other_auth = NULL;
+ this->peer_authenticated = FALSE;
+
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/ike_auth.h b/src/charon/sa/tasks/ike_auth.h
new file mode 100644
index 000000000..e59e6811a
--- /dev/null
+++ b/src/charon/sa/tasks/ike_auth.h
@@ -0,0 +1,60 @@
+/**
+ * @file ike_auth.h
+ *
+ * @brief Interface ike_auth_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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.
+ */
+
+#ifndef IKE_AUTH_H_
+#define IKE_AUTH_H_
+
+typedef struct ike_auth_t ike_auth_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+
+/**
+ * @brief Task of type ike_auth, authenticates an IKE_SA authenticators.
+ *
+ * The ike_auth task authenticates the IKE_SA using the IKE_AUTH
+ * exchange.
+ *
+ * @b Constructors:
+ * - ike_auth_create()
+ *
+ * @ingroup tasks
+ */
+struct ike_auth_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * @brief Create a new task of type IKE_AUTHENTICATE.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param initiator TRUE if thask is the initator of an exchange
+ * @return ike_auth task to handle by the task_manager
+ */
+ike_auth_t *ike_auth_create(ike_sa_t *ike_sa, bool initiator);
+
+#endif /* IKE_AUTH_H_ */
diff --git a/src/charon/sa/tasks/ike_cert.c b/src/charon/sa/tasks/ike_cert.c
new file mode 100644
index 000000000..3cddc0073
--- /dev/null
+++ b/src/charon/sa/tasks/ike_cert.c
@@ -0,0 +1,361 @@
+/**
+ * @file ike_cert.c
+ *
+ * @brief Implementation of the ike_cert task.
+ *
+ */
+
+/*
+ * Copyright (C) 2006-2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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_cert.h"
+
+#include <daemon.h>
+#include <sa/ike_sa.h>
+#include <crypto/hashers/hasher.h>
+#include <encoding/payloads/cert_payload.h>
+#include <encoding/payloads/certreq_payload.h>
+
+
+typedef struct private_ike_cert_t private_ike_cert_t;
+
+/**
+ * Private members of a ike_cert_t task.
+ */
+struct private_ike_cert_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ ike_cert_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * list of CA cert hashes requested, items point to 20 byte chunk
+ */
+ linked_list_t *cas;
+};
+
+/**
+ * read certificate requests
+ */
+static void process_certreqs(private_ike_cert_t *this, message_t *message)
+{
+ iterator_t *iterator;
+ payload_t *payload;
+
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ if (payload->get_type(payload) == CERTIFICATE_REQUEST)
+ {
+ certreq_payload_t *certreq = (certreq_payload_t*)payload;
+ cert_encoding_t encoding;
+ chunk_t keyids, keyid;
+
+ encoding = certreq->get_cert_encoding(certreq);
+ if (encoding != CERT_X509_SIGNATURE)
+ {
+ DBG1(DBG_IKE, "certreq payload %N not supported, ignored",
+ cert_encoding_names, encoding);
+ continue;
+ }
+
+ keyids = certreq->get_data(certreq);
+
+ while (keyids.len >= HASH_SIZE_SHA1)
+ {
+ keyid = chunk_create(keyids.ptr, HASH_SIZE_SHA1);
+ keyid = chunk_clone(keyid);
+ this->cas->insert_last(this->cas, keyid.ptr);
+ keyids = chunk_skip(keyids, HASH_SIZE_SHA1);
+ }
+ }
+ }
+ iterator->destroy(iterator);
+}
+
+/**
+ * import certificates
+ */
+static void process_certs(private_ike_cert_t *this, message_t *message)
+{
+ iterator_t *iterator;
+ payload_t *payload;
+
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ if (payload->get_type(payload) == CERTIFICATE)
+ {
+ cert_encoding_t encoding;
+ x509_t *cert;
+ chunk_t cert_data;
+ bool found;
+ cert_payload_t *cert_payload = (cert_payload_t*)payload;
+
+ encoding = cert_payload->get_cert_encoding(cert_payload);
+ if (encoding != CERT_X509_SIGNATURE)
+ {
+ DBG1(DBG_IKE, "certificate payload %N not supported, ignored",
+ cert_encoding_names, encoding);
+ continue;
+ }
+
+ cert_data = cert_payload->get_data_clone(cert_payload);
+ cert = x509_create_from_chunk(cert_data);
+ if (cert)
+ {
+ if (charon->credentials->verify(charon->credentials,
+ cert, &found))
+ {
+ DBG2(DBG_IKE, "received end entity certificate is trusted, "
+ "added to store");
+ if (!found)
+ {
+ charon->credentials->add_end_certificate(
+ charon->credentials, cert);
+ }
+ else
+ {
+ cert->destroy(cert);
+ }
+ }
+ else
+ {
+ DBG1(DBG_IKE, "received end entity certificate is not "
+ "trusted, discarded");
+ cert->destroy(cert);
+ }
+ }
+ else
+ {
+ DBG1(DBG_IKE, "parsing of received certificate failed, discarded");
+ chunk_free(&cert_data);
+ }
+ }
+ }
+ iterator->destroy(iterator);
+}
+
+/**
+ * build certificate requests
+ */
+static void build_certreqs(private_ike_cert_t *this, message_t *message)
+{
+ connection_t *connection;
+ policy_t *policy;
+ identification_t *ca;
+ certreq_payload_t *certreq;
+
+ connection = this->ike_sa->get_connection(this->ike_sa);
+
+ if (connection->get_certreq_policy(connection) != CERT_NEVER_SEND)
+ {
+ policy = this->ike_sa->get_policy(this->ike_sa);
+
+ if (policy)
+ {
+ ca = policy->get_other_ca(policy);
+
+ if (ca && ca->get_type(ca) != ID_ANY)
+ {
+ certreq = certreq_payload_create_from_cacert(ca);
+ }
+ else
+ {
+ certreq = certreq_payload_create_from_cacerts();
+ }
+ }
+ else
+ {
+ certreq = certreq_payload_create_from_cacerts();
+ }
+
+ if (certreq)
+ {
+ message->add_payload(message, (payload_t*)certreq);
+ }
+ }
+}
+
+/**
+ * add certificates to message
+ */
+static void build_certs(private_ike_cert_t *this, message_t *message)
+{
+ policy_t *policy;
+ connection_t *connection;
+ x509_t *cert;
+ cert_payload_t *payload;
+
+ policy = this->ike_sa->get_policy(this->ike_sa);
+ connection = this->ike_sa->get_connection(this->ike_sa);
+
+ if (policy && policy->get_auth_method(policy) == AUTH_RSA)
+ {
+ switch (connection->get_cert_policy(connection))
+ {
+ case CERT_NEVER_SEND:
+ break;
+ case CERT_SEND_IF_ASKED:
+ if (this->cas->get_count(this->cas) == 0)
+ {
+ break;
+ }
+ /* FALL */
+ case CERT_ALWAYS_SEND:
+ {
+ /* TODO: respect CA cert request */
+ cert = charon->credentials->get_certificate(charon->credentials,
+ policy->get_my_id(policy));
+ if (cert)
+ {
+ payload = cert_payload_create_from_x509(cert);
+ message->add_payload(message, (payload_t*)payload);
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t build_i(private_ike_cert_t *this, message_t *message)
+{
+ if (message->get_exchange_type(message) == IKE_SA_INIT)
+ {
+ return NEED_MORE;
+ }
+
+ build_certreqs(this, message);
+ build_certs(this, message);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for responder
+ */
+static status_t process_r(private_ike_cert_t *this, message_t *message)
+{
+ if (message->get_exchange_type(message) == IKE_SA_INIT)
+ {
+ return NEED_MORE;
+ }
+
+ process_certreqs(this, message);
+ process_certs(this, message);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for responder
+ */
+static status_t build_r(private_ike_cert_t *this, message_t *message)
+{
+ if (message->get_exchange_type(message) == IKE_SA_INIT)
+ {
+ build_certreqs(this, message);
+ return NEED_MORE;
+ }
+
+ build_certs(this, message);
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_i(private_ike_cert_t *this, message_t *message)
+{
+ if (message->get_exchange_type(message) == IKE_SA_INIT)
+ {
+ process_certreqs(this, message);
+ return NEED_MORE;
+ }
+
+ process_certs(this, message);
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_ike_cert_t *this)
+{
+ return IKE_CERT;
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_ike_cert_t *this, ike_sa_t *ike_sa)
+{
+ this->ike_sa = ike_sa;
+
+ this->cas->destroy_function(this->cas, free);
+ this->cas = linked_list_create();
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_ike_cert_t *this)
+{
+ this->cas->destroy_function(this->cas, free);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_cert_t *ike_cert_create(ike_sa_t *ike_sa, bool initiator)
+{
+ private_ike_cert_t *this = malloc_thing(private_ike_cert_t);
+
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+
+ if (initiator)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
+ }
+
+ this->ike_sa = ike_sa;
+ this->initiator = initiator;
+ this->cas = linked_list_create();
+
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/ike_cert.h b/src/charon/sa/tasks/ike_cert.h
new file mode 100644
index 000000000..ba0283953
--- /dev/null
+++ b/src/charon/sa/tasks/ike_cert.h
@@ -0,0 +1,61 @@
+/**
+ * @file ike_cert.h
+ *
+ * @brief Interface ike_cert_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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.
+ */
+
+#ifndef IKE_CERT_H_
+#define IKE_CERT_H_
+
+typedef struct ike_cert_t ike_cert_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+
+/**
+ * @brief Task of type ike_cert, exchanges certificates and
+ * certificate requests.
+ *
+ * @b Constructors:
+ * - ike_cert_create()
+ *
+ * @ingroup tasks
+ */
+struct ike_cert_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * @brief Create a new ike_cert task.
+ *
+ * The initiator parameter means the original initiator, not the initiator
+ * of the certificate request.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param initiator TRUE if thask is the original initator
+ * @return ike_cert task to handle by the task_manager
+ */
+ike_cert_t *ike_cert_create(ike_sa_t *ike_sa, bool initiator);
+
+#endif /* IKE_CERT_H_ */
diff --git a/src/charon/sa/tasks/ike_config.c b/src/charon/sa/tasks/ike_config.c
new file mode 100644
index 000000000..b6e883b48
--- /dev/null
+++ b/src/charon/sa/tasks/ike_config.c
@@ -0,0 +1,421 @@
+/**
+ * @file ike_config.c
+ *
+ * @brief Implementation of the ike_config task.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Copyright (C) 2006-2007 Fabian Hartmann, Noah Heusser
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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_config.h"
+
+#include <daemon.h>
+#include <encoding/payloads/cp_payload.h>
+
+typedef struct private_ike_config_t private_ike_config_t;
+
+/**
+ * Private members of a ike_config_t task.
+ */
+struct private_ike_config_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ ike_config_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * associated policy with virtual IP configuration
+ */
+ policy_t *policy;
+
+ /**
+ * virtual ip
+ */
+ host_t *virtual_ip;
+
+ /**
+ * list of DNS servers
+ */
+ linked_list_t *dns;
+};
+
+/**
+ * build configuration payloads and attributes
+ */
+static void build_payloads(private_ike_config_t *this, message_t *message,
+ config_type_t type)
+{
+ cp_payload_t *cp;
+ configuration_attribute_t *ca;
+ chunk_t chunk, prefix;
+
+ if (!this->virtual_ip)
+ {
+ return;
+ }
+
+ cp = cp_payload_create();
+ cp->set_config_type(cp, type);
+
+ ca = configuration_attribute_create();
+
+ if (this->virtual_ip->get_family(this->virtual_ip) == AF_INET)
+ {
+ ca->set_type(ca, INTERNAL_IP4_ADDRESS);
+ if (this->virtual_ip->is_anyaddr(this->virtual_ip))
+ {
+ chunk = chunk_empty;
+ }
+ else
+ {
+ chunk = this->virtual_ip->get_address(this->virtual_ip);
+ }
+ }
+ else
+ {
+ ca->set_type(ca, INTERNAL_IP6_ADDRESS);
+ if (this->virtual_ip->is_anyaddr(this->virtual_ip))
+ {
+ chunk = chunk_empty;
+ }
+ else
+ {
+ prefix = chunk_alloca(1);
+ *prefix.ptr = 64;
+ chunk = this->virtual_ip->get_address(this->virtual_ip);
+ chunk = chunk_cata("cc", chunk, prefix);
+ }
+ }
+ ca->set_value(ca, chunk);
+ cp->add_configuration_attribute(cp, ca);
+
+ /* we currently always add a DNS request if we request an IP */
+ if (this->initiator)
+ {
+ ca = configuration_attribute_create();
+ if (this->virtual_ip->get_family(this->virtual_ip) == AF_INET)
+ {
+ ca->set_type(ca, INTERNAL_IP4_DNS);
+ }
+ else
+ {
+ ca->set_type(ca, INTERNAL_IP6_DNS);
+ }
+ cp->add_configuration_attribute(cp, ca);
+ }
+ else
+ {
+ host_t *ip;
+ iterator_t *iterator = this->dns->create_iterator(this->dns, TRUE);
+ while (iterator->iterate(iterator, (void**)&ip))
+ {
+ ca = configuration_attribute_create();
+ if (ip->get_family(ip) == AF_INET)
+ {
+ ca->set_type(ca, INTERNAL_IP4_DNS);
+ }
+ else
+ {
+ ca->set_type(ca, INTERNAL_IP6_DNS);
+ }
+ chunk = ip->get_address(ip);
+ ca->set_value(ca, chunk);
+ cp->add_configuration_attribute(cp, ca);
+ }
+ iterator->destroy(iterator);
+ }
+ message->add_payload(message, (payload_t*)cp);
+}
+
+/**
+ * process a single configuration attribute
+ */
+static void process_attribute(private_ike_config_t *this,
+ configuration_attribute_t *ca)
+{
+ host_t *ip;
+ chunk_t addr;
+ int family = AF_INET6;
+
+ switch (ca->get_type(ca))
+ {
+ case INTERNAL_IP4_ADDRESS:
+ family = AF_INET;
+ /* fall */
+ case INTERNAL_IP6_ADDRESS:
+ {
+ addr = ca->get_value(ca);
+ if (addr.len == 0)
+ {
+ ip = host_create_any(family);
+ }
+ else
+ {
+ /* skip prefix byte in IPv6 payload*/
+ if (family == AF_INET6)
+ {
+ addr.len--;
+ }
+ ip = host_create_from_chunk(family, addr, 0);
+ }
+ if (ip && !this->virtual_ip)
+ {
+ this->virtual_ip = ip;
+ }
+ break;
+ }
+ case INTERNAL_IP4_DNS:
+ family = AF_INET;
+ /* fall */
+ case INTERNAL_IP6_DNS:
+ {
+ addr = ca->get_value(ca);
+ if (addr.len == 0)
+ {
+ ip = host_create_any(family);
+ }
+ else
+ {
+ ip = host_create_from_chunk(family, addr, 0);
+ }
+ if (ip)
+ {
+ this->dns->insert_last(this->dns, ip);
+ }
+ break;
+ }
+ case INTERNAL_IP4_NBNS:
+ case INTERNAL_IP6_NBNS:
+ /* TODO */
+ default:
+ DBG1(DBG_IKE, "ignoring %N config attribute",
+ configuration_attribute_type_names,
+ ca->get_type(ca));
+ break;
+ }
+}
+
+/**
+ * Scan for configuration payloads and attributes
+ */
+static void process_payloads(private_ike_config_t *this, message_t *message)
+{
+ iterator_t *iterator, *attributes;
+ payload_t *payload;
+
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ if (payload->get_type(payload) == CONFIGURATION)
+ {
+ cp_payload_t *cp = (cp_payload_t*)payload;
+ configuration_attribute_t *ca;
+ switch (cp->get_config_type(cp))
+ {
+ case CFG_REQUEST:
+ case CFG_REPLY:
+ {
+ attributes = cp->create_attribute_iterator(cp);
+ while (attributes->iterate(attributes, (void**)&ca))
+ {
+ process_attribute(this, ca);
+ }
+ attributes->destroy(attributes);
+ break;
+ }
+ default:
+ DBG1(DBG_IKE, "ignoring %N config payload",
+ config_type_names, cp->get_config_type(cp));
+ break;
+ }
+ }
+ }
+ iterator->destroy(iterator);
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t build_i(private_ike_config_t *this, message_t *message)
+{
+ if (message->get_exchange_type(message) != IKE_SA_INIT)
+ {
+ this->virtual_ip = this->policy->get_virtual_ip(this->policy, NULL);
+
+ build_payloads(this, message, CFG_REQUEST);
+ }
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for responder
+ */
+static status_t process_r(private_ike_config_t *this, message_t *message)
+{
+ if (message->get_exchange_type(message) != IKE_SA_INIT)
+ {
+ process_payloads(this, message);
+ }
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for responder
+ */
+static status_t build_r(private_ike_config_t *this, message_t *message)
+{
+ if (message->get_exchange_type(message) != IKE_SA_INIT)
+ {
+ this->policy = this->ike_sa->get_policy(this->ike_sa);
+
+ if (this->policy && this->virtual_ip)
+ {
+ host_t *ip;
+
+ DBG1(DBG_IKE, "peer requested virtual IP %H", this->virtual_ip);
+ ip = this->policy->get_virtual_ip(this->policy, this->virtual_ip);
+ if (ip == NULL)
+ {
+ DBG1(DBG_IKE, "not assigning a virtual IP to peer");
+ return SUCCESS;
+ }
+ DBG1(DBG_IKE, "assigning virtual IP %H to peer", ip);
+ this->ike_sa->set_virtual_ip(this->ike_sa, FALSE, ip);
+
+ this->virtual_ip->destroy(this->virtual_ip);
+ this->virtual_ip = ip;
+
+ /* DNS is for testing only */
+ if (this->dns->remove_last(this->dns, (void**)&ip) == SUCCESS)
+ {
+ ip->destroy(ip);
+ ip = host_create_from_string("10.3.0.1", 0);
+ this->dns->insert_last(this->dns, ip);
+ ip = host_create_from_string("10.3.0.2", 0);
+ this->dns->insert_last(this->dns, ip);
+ }
+
+ build_payloads(this, message, CFG_REPLY);
+ }
+ return SUCCESS;
+ }
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_i(private_ike_config_t *this, message_t *message)
+{
+ if (message->get_exchange_type(message) != IKE_SA_INIT)
+ {
+ host_t *ip;
+
+ DESTROY_IF(this->virtual_ip);
+ this->virtual_ip = NULL;
+
+ process_payloads(this, message);
+
+ if (this->virtual_ip)
+ {
+ this->ike_sa->set_virtual_ip(this->ike_sa, TRUE, this->virtual_ip);
+
+ while (this->dns->remove_last(this->dns, (void**)&ip) == SUCCESS)
+ {
+ this->ike_sa->add_dns_server(this->ike_sa, ip);
+ ip->destroy(ip);
+ }
+ }
+ return SUCCESS;
+ }
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_ike_config_t *this)
+{
+ return IKE_CONFIG;
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_ike_config_t *this, ike_sa_t *ike_sa)
+{
+ DESTROY_IF(this->virtual_ip);
+ this->dns->destroy_offset(this->dns, offsetof(host_t, destroy));
+
+ this->ike_sa = ike_sa;
+ this->virtual_ip = NULL;
+ this->dns = linked_list_create();
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_ike_config_t *this)
+{
+ DESTROY_IF(this->virtual_ip);
+ this->dns->destroy_offset(this->dns, offsetof(host_t, destroy));
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_config_t *ike_config_create(ike_sa_t *ike_sa, policy_t *policy)
+{
+ private_ike_config_t *this = malloc_thing(private_ike_config_t);
+
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+
+ if (policy)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
+ this->initiator = TRUE;
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
+ this->initiator = FALSE;
+ }
+
+ this->ike_sa = ike_sa;
+ this->policy = policy;
+ this->virtual_ip = NULL;
+ this->dns = linked_list_create();
+
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/ike_config.h b/src/charon/sa/tasks/ike_config.h
new file mode 100644
index 000000000..0c9b961b4
--- /dev/null
+++ b/src/charon/sa/tasks/ike_config.h
@@ -0,0 +1,59 @@
+/**
+ * @file ike_config.h
+ *
+ * @brief Interface ike_config_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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.
+ */
+
+#ifndef IKE_CONFIG_H_
+#define IKE_CONFIG_H_
+
+typedef struct ike_config_t ike_config_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+#include <config/policies/policy.h>
+
+/**
+ * @brief Task of type IKE_CONFIG, sets up a virtual IP and other
+ * configurations for an IKE_SA.
+ *
+ * @b Constructors:
+ * - ike_config_create()
+ *
+ * @ingroup tasks
+ */
+struct ike_config_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * @brief Create a new ike_config task.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param policy policy for the initiator, NULL for the responder
+ * @return ike_config task to handle by the task_manager
+ */
+ike_config_t *ike_config_create(ike_sa_t *ike_sa, policy_t *policy);
+
+#endif /* IKE_CONFIG_H_ */
diff --git a/src/charon/sa/tasks/ike_delete.c b/src/charon/sa/tasks/ike_delete.c
new file mode 100644
index 000000000..b9efc4ea9
--- /dev/null
+++ b/src/charon/sa/tasks/ike_delete.c
@@ -0,0 +1,169 @@
+/**
+ * @file ike_delete.c
+ *
+ * @brief Implementation of the ike_delete task.
+ *
+ */
+
+/*
+ * Copyright (C) 2006-2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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_delete.h"
+
+#include <daemon.h>
+#include <encoding/payloads/delete_payload.h>
+
+
+typedef struct private_ike_delete_t private_ike_delete_t;
+
+/**
+ * Private members of a ike_delete_t task.
+ */
+struct private_ike_delete_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ ike_delete_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * are we responding to a delete, but have initated our own?
+ */
+ bool simultaneous;
+};
+
+/**
+ * Implementation of task_t.build for initiator
+ */
+static status_t build_i(private_ike_delete_t *this, message_t *message)
+{
+ delete_payload_t *delete_payload;
+
+ delete_payload = delete_payload_create(PROTO_IKE);
+ message->add_payload(message, (payload_t*)delete_payload);
+
+ this->ike_sa->set_state(this->ike_sa, IKE_DELETING);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_i(private_ike_delete_t *this, message_t *message)
+{
+ /* completed, delete IKE_SA by returning FAILED */
+ return FAILED;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_r(private_ike_delete_t *this, message_t *message)
+{
+ /* we don't even scan the payloads, as the message wouldn't have
+ * come so far without being correct */
+ switch (this->ike_sa->get_state(this->ike_sa))
+ {
+ case IKE_DELETING:
+ this->simultaneous = TRUE;
+ break;
+ case IKE_ESTABLISHED:
+ DBG1(DBG_IKE, "deleting IKE_SA on request");
+ /* warn only if we are established */
+ default:
+ this->ike_sa->set_state(this->ike_sa, IKE_DELETING);
+ break;
+ }
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for responder
+ */
+static status_t build_r(private_ike_delete_t *this, message_t *message)
+{
+ if (this->simultaneous)
+ {
+ /* wait for peers response for our delete request */
+ return SUCCESS;
+ }
+ /* completed, delete IKE_SA by returning FAILED */
+ return FAILED;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_ike_delete_t *this)
+{
+ return IKE_DELETE;
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_ike_delete_t *this, ike_sa_t *ike_sa)
+{
+ this->ike_sa = ike_sa;
+ this->simultaneous = FALSE;
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_ike_delete_t *this)
+{
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_delete_t *ike_delete_create(ike_sa_t *ike_sa, bool initiator)
+{
+ private_ike_delete_t *this = malloc_thing(private_ike_delete_t);
+
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+
+ if (initiator)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
+ }
+
+ this->ike_sa = ike_sa;
+ this->initiator = initiator;
+ this->simultaneous = FALSE;
+
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/ike_delete.h b/src/charon/sa/tasks/ike_delete.h
new file mode 100644
index 000000000..e8ec5ebbe
--- /dev/null
+++ b/src/charon/sa/tasks/ike_delete.h
@@ -0,0 +1,57 @@
+/**
+ * @file ike_delete.h
+ *
+ * @brief Interface ike_delete_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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.
+ */
+
+#ifndef IKE_DELETE_H_
+#define IKE_DELETE_H_
+
+typedef struct ike_delete_t ike_delete_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+
+/**
+ * @brief Task of type ike_delete, delete an IKE_SA.
+ *
+ * @b Constructors:
+ * - ike_delete_create()
+ *
+ * @ingroup tasks
+ */
+struct ike_delete_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * @brief Create a new ike_delete task.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param initiator TRUE if we initiate the delete
+ * @return ike_delete task to handle by the task_manager
+ */
+ike_delete_t *ike_delete_create(ike_sa_t *ike_sa, bool initiator);
+
+#endif /* IKE_DELETE_H_ */
diff --git a/src/charon/sa/tasks/ike_dpd.c b/src/charon/sa/tasks/ike_dpd.c
new file mode 100644
index 000000000..1cb05c45c
--- /dev/null
+++ b/src/charon/sa/tasks/ike_dpd.c
@@ -0,0 +1,106 @@
+/**
+ * @file ike_dpd.c
+ *
+ * @brief Implementation of the ike_dpd task.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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_dpd.h"
+
+#include <daemon.h>
+
+
+typedef struct private_ike_dpd_t private_ike_dpd_t;
+
+/**
+ * Private members of a ike_dpd_t task.
+ */
+struct private_ike_dpd_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ ike_dpd_t public;
+};
+
+/**
+ * Implementation of task_t.build for initiator
+ * Implementation of task_t.process for responder
+ */
+static status_t return_need_more(private_ike_dpd_t *this, message_t *message)
+{
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ * Implementation of task_t.build for responder
+ */
+static status_t return_success(private_ike_dpd_t *this, message_t *message)
+{
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_ike_dpd_t *this)
+{
+ return IKE_DEADPEER;
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_ike_dpd_t *this, ike_sa_t *ike_sa)
+{
+
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_ike_dpd_t *this)
+{
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_dpd_t *ike_dpd_create(bool initiator)
+{
+ private_ike_dpd_t *this = malloc_thing(private_ike_dpd_t);
+
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+
+ if (initiator)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))return_need_more;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))return_success;
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))return_success;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))return_need_more;
+ }
+
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/ike_dpd.h b/src/charon/sa/tasks/ike_dpd.h
new file mode 100644
index 000000000..531b0502d
--- /dev/null
+++ b/src/charon/sa/tasks/ike_dpd.h
@@ -0,0 +1,58 @@
+/**
+ * @file ike_dpd.h
+ *
+ * @brief Interface ike_dpd_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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.
+ */
+
+#ifndef IKE_DPD_H_
+#define IKE_DPD_H_
+
+typedef struct ike_dpd_t ike_dpd_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+
+/**
+ * @brief Task of type ike_dpd, detects dead peers.
+ *
+ * The DPD task actually does nothing, as a DPD has no associated payloads.
+ *
+ * @b Constructors:
+ * - ike_dpd_create()
+ *
+ * @ingroup tasks
+ */
+struct ike_dpd_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * @brief Create a new ike_dpd task.
+ *
+ * @param initiator TRUE if thask is the original initator
+ * @return ike_dpd task to handle by the task_manager
+ */
+ike_dpd_t *ike_dpd_create(bool initiator);
+
+#endif /* IKE_DPD_H_ */
diff --git a/src/charon/sa/tasks/ike_init.c b/src/charon/sa/tasks/ike_init.c
new file mode 100644
index 000000000..9149aab91
--- /dev/null
+++ b/src/charon/sa/tasks/ike_init.c
@@ -0,0 +1,536 @@
+/**
+ * @file ike_init.c
+ *
+ * @brief Implementation of the ike_init task.
+ *
+ */
+
+/*
+ * Copyright (C) 2005-2007 Martin Willi
+ * Copyright (C) 2005 Jan Hutter
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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_init.h"
+
+#include <string.h>
+
+#include <daemon.h>
+#include <crypto/diffie_hellman.h>
+#include <encoding/payloads/sa_payload.h>
+#include <encoding/payloads/ke_payload.h>
+#include <encoding/payloads/nonce_payload.h>
+
+
+typedef struct private_ike_init_t private_ike_init_t;
+
+/**
+ * Private members of a ike_init_t task.
+ */
+struct private_ike_init_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ ike_init_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * Connection established by this IKE_SA
+ */
+ connection_t *connection;
+
+ /**
+ * diffie hellman group to use
+ */
+ diffie_hellman_group_t dh_group;
+
+ /**
+ * Diffie hellman object used to generate public DH value.
+ */
+ diffie_hellman_t *diffie_hellman;
+
+ /**
+ * nonce chosen by us
+ */
+ chunk_t my_nonce;
+
+ /**
+ * nonce chosen by peer
+ */
+ chunk_t other_nonce;
+
+ /**
+ * Negotiated proposal used for IKE_SA
+ */
+ proposal_t *proposal;
+
+ /**
+ * Old IKE_SA which gets rekeyed
+ */
+ ike_sa_t *old_sa;
+};
+
+/**
+ * build the payloads for the message
+ */
+static void build_payloads(private_ike_init_t *this, message_t *message)
+{
+ sa_payload_t *sa_payload;
+ ke_payload_t *ke_payload;
+ nonce_payload_t *nonce_payload;
+ linked_list_t *proposal_list;
+ ike_sa_id_t *id;
+ proposal_t *proposal;
+ iterator_t *iterator;
+
+ id = this->ike_sa->get_id(this->ike_sa);
+
+ this->connection = this->ike_sa->get_connection(this->ike_sa);
+
+ if (this->initiator)
+ {
+ proposal_list = this->connection->get_proposals(this->connection);
+ if (this->old_sa)
+ {
+ /* include SPI of new IKE_SA when we are rekeying */
+ iterator = proposal_list->create_iterator(proposal_list, TRUE);
+ while (iterator->iterate(iterator, (void**)&proposal))
+ {
+ proposal->set_spi(proposal, id->get_initiator_spi(id));
+ }
+ iterator->destroy(iterator);
+ }
+
+ sa_payload = sa_payload_create_from_proposal_list(proposal_list);
+ proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
+ }
+ else
+ {
+ if (this->old_sa)
+ {
+ /* include SPI of new IKE_SA when we are rekeying */
+ this->proposal->set_spi(this->proposal, id->get_responder_spi(id));
+ }
+ sa_payload = sa_payload_create_from_proposal(this->proposal);
+ }
+ message->add_payload(message, (payload_t*)sa_payload);
+
+ ke_payload = ke_payload_create_from_diffie_hellman(this->diffie_hellman);
+ message->add_payload(message, (payload_t*)ke_payload);
+
+ nonce_payload = nonce_payload_create();
+ nonce_payload->set_nonce(nonce_payload, this->my_nonce);
+
+ message->add_payload(message, (payload_t*)nonce_payload);
+}
+
+/**
+ * Read payloads from message
+ */
+static void process_payloads(private_ike_init_t *this, message_t *message)
+{
+ iterator_t *iterator;
+ payload_t *payload;
+
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ switch (payload->get_type(payload))
+ {
+ case SECURITY_ASSOCIATION:
+ {
+ sa_payload_t *sa_payload = (sa_payload_t*)payload;
+ linked_list_t *proposal_list;
+
+ proposal_list = sa_payload->get_proposals(sa_payload);
+ this->proposal = this->connection->select_proposal(
+ this->connection, proposal_list);
+ proposal_list->destroy_offset(proposal_list,
+ offsetof(proposal_t, destroy));
+ break;
+ }
+ case KEY_EXCHANGE:
+ {
+ ke_payload_t *ke_payload = (ke_payload_t*)payload;
+ diffie_hellman_group_t dh_group;
+ chunk_t key_data;
+
+ dh_group = ke_payload->get_dh_group_number(ke_payload);
+
+ if (this->initiator)
+ {
+ if (dh_group != this->dh_group)
+ {
+ DBG1(DBG_IKE, "received a DH group not requested (%N)",
+ diffie_hellman_group_names, dh_group);
+ break;
+ }
+ }
+ else
+ {
+ this->dh_group = dh_group;
+ if (!this->connection->check_dh_group(this->connection,
+ dh_group))
+ {
+ break;
+ }
+ this->diffie_hellman = diffie_hellman_create(dh_group);
+ }
+ if (this->diffie_hellman)
+ {
+ key_data = ke_payload->get_key_exchange_data(ke_payload);
+ this->diffie_hellman->set_other_public_value(this->diffie_hellman, key_data);
+ }
+ break;
+ }
+ case NONCE:
+ {
+ nonce_payload_t *nonce_payload = (nonce_payload_t*)payload;
+ this->other_nonce = nonce_payload->get_nonce(nonce_payload);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t build_i(private_ike_init_t *this, message_t *message)
+{
+ randomizer_t *randomizer;
+ status_t status;
+
+ this->connection = this->ike_sa->get_connection(this->ike_sa);
+ SIG(IKE_UP_START, "initiating IKE_SA to %H",
+ this->connection->get_other_host(this->connection));
+ this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING);
+
+ /* if the DH group is set via use_dh_group(), we already have a DH object */
+ if (!this->diffie_hellman)
+ {
+ this->dh_group = this->connection->get_dh_group(this->connection);
+ this->diffie_hellman = diffie_hellman_create(this->dh_group);
+ if (this->diffie_hellman == NULL)
+ {
+ SIG(IKE_UP_FAILED, "configured DH group %N not supported",
+ diffie_hellman_group_names, this->dh_group);
+ return FAILED;
+ }
+ }
+
+ randomizer = randomizer_create();
+ status = randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_SIZE,
+ &this->my_nonce);
+ randomizer->destroy(randomizer);
+ if (status != SUCCESS)
+ {
+ SIG(IKE_UP_FAILED, "error generating random nonce value");
+ return FAILED;
+ }
+
+ build_payloads(this, message);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_r(private_ike_init_t *this, message_t *message)
+{
+ randomizer_t *randomizer;
+
+ this->connection = this->ike_sa->get_connection(this->ike_sa);
+ SIG(IKE_UP_FAILED, "%H is initiating an IKE_SA",
+ message->get_source(message));
+ this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING);
+
+ randomizer = randomizer_create();
+ if (randomizer->allocate_pseudo_random_bytes(randomizer, NONCE_SIZE,
+ &this->my_nonce) != SUCCESS)
+ {
+ DBG1(DBG_IKE, "error generating random nonce value");
+ }
+ randomizer->destroy(randomizer);
+
+ process_payloads(this, message);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for responder
+ */
+static status_t build_r(private_ike_init_t *this, message_t *message)
+{
+ chunk_t secret;
+ status_t status;
+
+ /* check if we have everything we need */
+ if (this->proposal == NULL ||
+ this->other_nonce.len == 0 || this->my_nonce.len == 0)
+ {
+ SIG(IKE_UP_FAILED, "received proposals inacceptable");
+ message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
+ return FAILED;
+ }
+
+ if (this->diffie_hellman == NULL ||
+ this->diffie_hellman->get_shared_secret(this->diffie_hellman,
+ &secret) != SUCCESS)
+ {
+ chunk_t chunk;
+ u_int16_t dh_enc;
+
+ SIG(IKE_UP_FAILED, "received inacceptable DH group (%N)",
+ diffie_hellman_group_names, this->dh_group);
+ this->dh_group = this->connection->get_dh_group(this->connection);
+ dh_enc = htons(this->dh_group);
+ chunk.ptr = (u_int8_t*)&dh_enc;
+ chunk.len = sizeof(dh_enc);
+ message->add_notify(message, TRUE, INVALID_KE_PAYLOAD, chunk);
+ DBG1(DBG_IKE, "requesting DH group %N",
+ diffie_hellman_group_names, this->dh_group);
+ return FAILED;
+ }
+
+
+ if (this->old_sa)
+ {
+ ike_sa_id_t *id;
+ prf_t *prf, *child_prf;
+
+ /* Apply SPI if we are rekeying */
+ id = this->ike_sa->get_id(this->ike_sa);
+ id->set_initiator_spi(id, this->proposal->get_spi(this->proposal));
+
+ /* setup crypto keys for the rekeyed SA */
+ prf = this->old_sa->get_prf(this->old_sa);
+ child_prf = this->old_sa->get_child_prf(this->old_sa);
+ status = this->ike_sa->derive_keys(this->ike_sa, this->proposal, secret,
+ this->other_nonce, this->my_nonce,
+ FALSE, child_prf, prf);
+ }
+ else
+ {
+ /* setup crypto keys */
+ status = this->ike_sa->derive_keys(this->ike_sa, this->proposal, secret,
+ this->other_nonce, this->my_nonce,
+ FALSE, NULL, NULL);
+ }
+ if (status != SUCCESS)
+ {
+ SIG(IKE_UP_FAILED, "key derivation failed");
+ message->add_notify(message, TRUE, NO_PROPOSAL_CHOSEN, chunk_empty);
+ return FAILED;
+ }
+
+ build_payloads(this, message);
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_i(private_ike_init_t *this, message_t *message)
+{
+ chunk_t secret;
+ status_t status;
+ iterator_t *iterator;
+ payload_t *payload;
+
+ /* check for erronous notifies */
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ if (payload->get_type(payload) == NOTIFY)
+ {
+ notify_payload_t *notify = (notify_payload_t*)payload;
+ notify_type_t type = notify->get_notify_type(notify);
+
+ switch (type)
+ {
+ case INVALID_KE_PAYLOAD:
+ {
+ chunk_t data;
+ diffie_hellman_group_t old_dh_group;
+
+ old_dh_group = this->dh_group;
+ data = notify->get_notification_data(notify);
+ this->dh_group = ntohs(*((u_int16_t*)data.ptr));
+
+ DBG1(DBG_IKE, "peer didn't accept DH group %N, it requested"
+ " %N", diffie_hellman_group_names, old_dh_group,
+ diffie_hellman_group_names, this->dh_group);
+ if (!this->connection->check_dh_group(this->connection,
+ this->dh_group))
+ {
+ SIG(IKE_UP_FAILED, "requested DH group %N not "
+ "acceptable, giving up", diffie_hellman_group_names,
+ this->dh_group);
+ iterator->destroy(iterator);
+ return FAILED;
+ }
+
+ this->ike_sa->reset(this->ike_sa);
+
+ iterator->destroy(iterator);
+ return NEED_MORE;
+ }
+ default:
+ {
+ if (type < 16383)
+ {
+ SIG(IKE_UP_FAILED, "received %N notify error",
+ notify_type_names, type);
+ iterator->destroy(iterator);
+ return FAILED;
+ }
+ }
+ }
+ }
+ }
+ iterator->destroy(iterator);
+
+ process_payloads(this, message);
+
+ /* check if we have everything */
+ if (this->proposal == NULL ||
+ this->other_nonce.len == 0 || this->my_nonce.len == 0)
+ {
+ SIG(IKE_UP_FAILED, "peers proposal selection invalid");
+ return FAILED;
+ }
+
+ if (this->diffie_hellman == NULL ||
+ this->diffie_hellman->get_shared_secret(this->diffie_hellman,
+ &secret) != SUCCESS)
+ {
+ SIG(IKE_UP_FAILED, "peers DH group selection invalid");
+ return FAILED;
+ }
+
+ /* Apply SPI if we are rekeying */
+ if (this->old_sa)
+ {
+ ike_sa_id_t *id;
+ prf_t *prf, *child_prf;
+
+ id = this->ike_sa->get_id(this->ike_sa);
+ id->set_responder_spi(id, this->proposal->get_spi(this->proposal));
+
+ /* setup crypto keys for the rekeyed SA */
+ prf = this->old_sa->get_prf(this->old_sa);
+ child_prf = this->old_sa->get_child_prf(this->old_sa);
+ status = this->ike_sa->derive_keys(this->ike_sa, this->proposal, secret,
+ this->my_nonce, this->other_nonce,
+ TRUE, child_prf, prf);
+ }
+ else
+ {
+ /* setup crypto keys for a new SA */
+ status = this->ike_sa->derive_keys(this->ike_sa, this->proposal, secret,
+ this->my_nonce, this->other_nonce,
+ TRUE, NULL, NULL);
+ }
+ if (status != SUCCESS)
+ {
+ SIG(IKE_UP_FAILED, "key derivation failed");
+ return FAILED;
+ }
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_ike_init_t *this)
+{
+ return IKE_INIT;
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_ike_init_t *this, ike_sa_t *ike_sa)
+{
+ DESTROY_IF(this->proposal);
+ DESTROY_IF(this->diffie_hellman);
+ chunk_free(&this->my_nonce);
+ chunk_free(&this->other_nonce);
+
+ this->ike_sa = ike_sa;
+ this->proposal = NULL;
+ this->diffie_hellman = diffie_hellman_create(this->dh_group);
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_ike_init_t *this)
+{
+ DESTROY_IF(this->proposal);
+ DESTROY_IF(this->diffie_hellman);
+ chunk_free(&this->my_nonce);
+ chunk_free(&this->other_nonce);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa)
+{
+ private_ike_init_t *this = malloc_thing(private_ike_init_t);
+
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+ if (initiator)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
+ }
+
+ this->ike_sa = ike_sa;
+ this->initiator = initiator;
+ this->dh_group = MODP_NONE;
+ this->diffie_hellman = NULL;
+ this->my_nonce = chunk_empty;
+ this->other_nonce = chunk_empty;
+ this->proposal = NULL;
+ this->connection = NULL;
+ this->old_sa = old_sa;
+
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/ike_init.h b/src/charon/sa/tasks/ike_init.h
new file mode 100644
index 000000000..290cc46e8
--- /dev/null
+++ b/src/charon/sa/tasks/ike_init.h
@@ -0,0 +1,60 @@
+/**
+ * @file ike_init.h
+ *
+ * @brief Interface ike_init_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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.
+ */
+
+#ifndef IKE_INIT_H_
+#define IKE_INIT_H_
+
+typedef struct ike_init_t ike_init_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+
+/**
+ * @brief Task of type IKE_INIT, creates an IKE_SA without authentication.
+ *
+ * The authentication of is handle in the ike_auth and/or ike_auth_eap task.
+ *
+ * @b Constructors:
+ * - ike_init_create()
+ *
+ * @ingroup tasks
+ */
+struct ike_init_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * @brief Create a new IKE_INIT task.
+ *
+ * @param ike_sa IKE_SA this task works for (new one when rekeying)
+ * @param initiator TRUE if thask is the original initator
+ * @param old_sa old IKE_SA when we are rekeying
+ * @return ike_init task to handle by the task_manager
+ */
+ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa);
+
+#endif /* IKE_INIT_H_ */
diff --git a/src/charon/sa/tasks/ike_natd.c b/src/charon/sa/tasks/ike_natd.c
new file mode 100644
index 000000000..a25b6e0d7
--- /dev/null
+++ b/src/charon/sa/tasks/ike_natd.c
@@ -0,0 +1,378 @@
+/**
+ * @file ike_natd.c
+ *
+ * @brief Implementation of the ike_natd task.
+ *
+ */
+
+/*
+ * Copyright (C) 2006-2007 Martin Willi
+ * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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_natd.h"
+
+#include <string.h>
+
+#include <daemon.h>
+#include <crypto/hashers/hasher.h>
+#include <encoding/payloads/notify_payload.h>
+
+
+typedef struct private_ike_natd_t private_ike_natd_t;
+
+/**
+ * Private members of a ike_natd_t task.
+ */
+struct private_ike_natd_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ ike_natd_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * Hasher used to build NAT detection hashes
+ */
+ hasher_t *hasher;
+
+ /**
+ * Did we process any NAT detection notifys for a source address?
+ */
+ bool src_seen;
+
+ /**
+ * Did we process any NAT detection notifys for a destination address?
+ */
+ bool dst_seen;
+
+ /**
+ * Have we found a matching source address NAT hash?
+ */
+ bool src_matched;
+
+ /**
+ * Have we found a matching destination address NAT hash?
+ */
+ bool dst_matched;
+};
+
+
+/**
+ * Build NAT detection hash for a host
+ */
+static chunk_t generate_natd_hash(private_ike_natd_t *this,
+ ike_sa_id_t *ike_sa_id, host_t *host)
+{
+ chunk_t natd_chunk, spi_i_chunk, spi_r_chunk, addr_chunk, port_chunk;
+ chunk_t natd_hash;
+ u_int64_t spi_i, spi_r;
+ u_int16_t port;
+
+ /* prepare all requred chunks */
+ spi_i = ike_sa_id->get_initiator_spi(ike_sa_id);
+ spi_r = ike_sa_id->get_responder_spi(ike_sa_id);
+ spi_i_chunk.ptr = (void*)&spi_i;
+ spi_i_chunk.len = sizeof(spi_i);
+ spi_r_chunk.ptr = (void*)&spi_r;
+ spi_r_chunk.len = sizeof(spi_r);
+ port = htons(host->get_port(host));
+ port_chunk.ptr = (void*)&port;
+ port_chunk.len = sizeof(port);
+ addr_chunk = host->get_address(host);
+ DBG2(DBG_IKE, "using SPI %J", ike_sa_id);
+
+ /* natd_hash = SHA1( spi_i | spi_r | address | port ) */
+ natd_chunk = chunk_cat("cccc", spi_i_chunk, spi_r_chunk, addr_chunk, port_chunk);
+ this->hasher->allocate_hash(this->hasher, natd_chunk, &natd_hash);
+ DBG3(DBG_IKE, "natd_chunk %B", &natd_chunk);
+ DBG3(DBG_IKE, "natd_hash %B", &natd_hash);
+
+ chunk_free(&natd_chunk);
+ return natd_hash;
+}
+
+/**
+ * Build a NAT detection notify payload.
+ */
+static notify_payload_t *build_natd_payload(private_ike_natd_t *this,
+ notify_type_t type, host_t *host)
+{
+ chunk_t hash;
+ notify_payload_t *notify;
+ ike_sa_id_t *ike_sa_id;
+
+ ike_sa_id = this->ike_sa->get_id(this->ike_sa);
+ notify = notify_payload_create();
+ notify->set_notify_type(notify, type);
+ hash = generate_natd_hash(this, ike_sa_id, host);
+ notify->set_notification_data(notify, hash);
+ chunk_free(&hash);
+
+ return notify;
+}
+
+/**
+ * read notifys from message and evaluate them
+ */
+static void process_payloads(private_ike_natd_t *this, message_t *message)
+{
+ iterator_t *iterator;
+ payload_t *payload;
+ notify_payload_t *notify;
+ chunk_t hash, src_hash, dst_hash;
+ ike_sa_id_t *ike_sa_id;
+ host_t *me, *other;
+
+ /* Precompute NAT-D hashes for incoming NAT notify comparison */
+ ike_sa_id = message->get_ike_sa_id(message);
+ me = this->ike_sa->get_my_host(this->ike_sa);
+ other = this->ike_sa->get_other_host(this->ike_sa);
+ dst_hash = generate_natd_hash(this, ike_sa_id, me);
+ src_hash = generate_natd_hash(this, ike_sa_id, other);
+
+ DBG2(DBG_IKE, "precalculated src_hash %B", &src_hash);
+ DBG2(DBG_IKE, "precalculated dst_hash %B", &dst_hash);
+
+ iterator = message->get_payload_iterator(message);
+ while (iterator->iterate(iterator, (void**)&payload))
+ {
+ if (payload->get_type(payload) != NOTIFY)
+ {
+ continue;
+ }
+ notify = (notify_payload_t*)payload;
+ switch (notify->get_notify_type(notify))
+ {
+ case NAT_DETECTION_DESTINATION_IP:
+ {
+ this->dst_seen = TRUE;
+ if (!this->dst_matched)
+ {
+ hash = notify->get_notification_data(notify);
+ DBG2(DBG_IKE, "received dst_hash %B", &hash);
+ if (chunk_equals(hash, dst_hash))
+ {
+ this->dst_matched = TRUE;
+ }
+ }
+ break;
+ }
+ case NAT_DETECTION_SOURCE_IP:
+ {
+ this->src_seen = TRUE;
+ if (!this->src_matched)
+ {
+ hash = notify->get_notification_data(notify);
+ DBG2(DBG_IKE, "received src_hash %B", &hash);
+ if (chunk_equals(hash, src_hash))
+ {
+ this->src_matched = TRUE;
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ iterator->destroy(iterator);
+
+ chunk_free(&src_hash);
+ chunk_free(&dst_hash);
+
+ if (this->src_seen && this->dst_seen)
+ {
+ if (!this->dst_matched)
+ {
+ this->ike_sa->enable_natt(this->ike_sa, TRUE);
+ }
+ if (!this->src_matched)
+ {
+ this->ike_sa->enable_natt(this->ike_sa, FALSE);
+ }
+ }
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_i(private_ike_natd_t *this, message_t *message)
+{
+ process_payloads(this, message);
+
+ if (this->ike_sa->is_natt_enabled(this->ike_sa))
+ {
+ host_t *me, *other;
+
+ me = this->ike_sa->get_my_host(this->ike_sa);
+ me->set_port(me, IKEV2_NATT_PORT);
+ other = this->ike_sa->get_other_host(this->ike_sa);
+ other->set_port(other, IKEV2_NATT_PORT);
+ }
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t build_i(private_ike_natd_t *this, message_t *message)
+{
+ notify_payload_t *notify;
+ linked_list_t *list;
+ host_t *host;
+
+ /* include one notify if our address is defined, all addresses otherwise */
+ host = this->ike_sa->get_my_host(this->ike_sa);
+ if (host->is_anyaddr(host))
+ {
+ /* TODO: we could get the src address from netlink!? */
+ list = charon->socket->create_local_address_list(charon->socket);
+ while (list->remove_first(list, (void**)&host) == SUCCESS)
+ {
+ notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host);
+ host->destroy(host);
+ message->add_payload(message, (payload_t*)notify);
+ }
+ list->destroy(list);
+ }
+ else
+ {
+ notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host);
+ message->add_payload(message, (payload_t*)notify);
+ }
+
+ host = this->ike_sa->get_other_host(this->ike_sa);
+ notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, host);
+ message->add_payload(message, (payload_t*)notify);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for responder
+ */
+static status_t build_r(private_ike_natd_t *this, message_t *message)
+{
+ notify_payload_t *notify;
+ host_t *me, *other;
+ iterator_t *iterator;
+ u_int count;
+
+ /* when only one payload is in the message, an error occured.
+ * TODO: find a better hack */
+ iterator = message->get_payload_iterator(message);
+ count = iterator->get_count(iterator);
+ iterator->destroy(iterator);
+ if (count < 3)
+ {
+ return NEED_MORE;
+ }
+
+ if (this->src_seen && this->dst_seen)
+ {
+ /* initiator seems to support NAT detection, add response */
+ me = this->ike_sa->get_my_host(this->ike_sa);
+ notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, me);
+ message->add_payload(message, (payload_t*)notify);
+
+ other = this->ike_sa->get_other_host(this->ike_sa);
+ notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, other);
+ message->add_payload(message, (payload_t*)notify);
+ }
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.process for responder
+ */
+static status_t process_r(private_ike_natd_t *this, message_t *message)
+{
+ process_payloads(this, message);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_ike_natd_t *this)
+{
+ return IKE_NATD;
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_ike_natd_t *this, ike_sa_t *ike_sa)
+{
+ this->ike_sa = ike_sa;
+ this->src_seen = FALSE;
+ this->dst_seen = FALSE;
+ this->src_matched = FALSE;
+ this->dst_matched = FALSE;
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_ike_natd_t *this)
+{
+ this->hasher->destroy(this->hasher);
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_natd_t *ike_natd_create(ike_sa_t *ike_sa, bool initiator)
+{
+ private_ike_natd_t *this = malloc_thing(private_ike_natd_t);
+
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+
+ if (initiator)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
+ }
+
+ this->ike_sa = ike_sa;
+ this->initiator = initiator;
+ this->hasher = hasher_create(HASH_SHA1);
+ this->src_seen = FALSE;
+ this->dst_seen = FALSE;
+ this->src_matched = FALSE;
+ this->dst_matched = FALSE;
+
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/ike_natd.h b/src/charon/sa/tasks/ike_natd.h
new file mode 100644
index 000000000..8d0cb58b4
--- /dev/null
+++ b/src/charon/sa/tasks/ike_natd.h
@@ -0,0 +1,57 @@
+/**
+ * @file ike_natd.h
+ *
+ * @brief Interface ike_natd_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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.
+ */
+
+#ifndef IKE_NATD_H_
+#define IKE_NATD_H_
+
+typedef struct ike_natd_t ike_natd_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+
+/**
+ * @brief Task of type ike_natd, detects NAT situation in IKE_SA_INIT exchange.
+ *
+ * @b Constructors:
+ * - ike_natd_create()
+ *
+ * @ingroup tasks
+ */
+struct ike_natd_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * @brief Create a new ike_natd task.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param initiator TRUE if thask is the original initator
+ * @return ike_natd task to handle by the task_manager
+ */
+ike_natd_t *ike_natd_create(ike_sa_t *ike_sa, bool initiator);
+
+#endif /* IKE_NATD_H_ */
diff --git a/src/charon/sa/tasks/ike_rekey.c b/src/charon/sa/tasks/ike_rekey.c
new file mode 100644
index 000000000..bbbd310bc
--- /dev/null
+++ b/src/charon/sa/tasks/ike_rekey.c
@@ -0,0 +1,232 @@
+/**
+ * @file ike_rekey.c
+ *
+ * @brief Implementation of the ike_rekey task.
+ *
+ */
+
+/*
+ * Copyright (C) 2005-2007 Martin Willi
+ * Copyright (C) 2005 Jan Hutter
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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_rekey.h"
+
+#include <daemon.h>
+#include <crypto/diffie_hellman.h>
+#include <encoding/payloads/notify_payload.h>
+#include <encoding/payloads/nonce_payload.h>
+#include <sa/tasks/ike_init.h>
+#include <queues/jobs/delete_ike_sa_job.h>
+
+
+typedef struct private_ike_rekey_t private_ike_rekey_t;
+
+/**
+ * Private members of a ike_rekey_t task.
+ */
+struct private_ike_rekey_t {
+
+ /**
+ * Public methods and task_t interface.
+ */
+ ike_rekey_t public;
+
+ /**
+ * Assigned IKE_SA.
+ */
+ ike_sa_t *ike_sa;
+
+ /**
+ * New IKE_SA which replaces the current one
+ */
+ ike_sa_t *new_sa;
+
+ /**
+ * Are we the initiator?
+ */
+ bool initiator;
+
+ /**
+ * the IKE_INIT task which is reused to simplify rekeying
+ */
+ ike_init_t *ike_init;
+};
+
+/**
+ * Implementation of task_t.build for initiator
+ */
+static status_t build_i(private_ike_rekey_t *this, message_t *message)
+{
+ connection_t *connection;
+ policy_t *policy;
+ ike_sa_id_t *id;
+
+ id = ike_sa_id_create(0, 0, TRUE);
+ this->new_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, id);
+ id->destroy(id);
+
+ connection = this->ike_sa->get_connection(this->ike_sa);
+ policy = this->ike_sa->get_policy(this->ike_sa);
+ this->new_sa->set_connection(this->new_sa, connection);
+ this->new_sa->set_policy(this->new_sa, policy);
+
+ this->ike_init = ike_init_create(this->new_sa, TRUE, this->ike_sa);
+ this->ike_init->task.build(&this->ike_init->task, message);
+
+ this->ike_sa->set_state(this->ike_sa, IKE_REKEYING);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_r(private_ike_rekey_t *this, message_t *message)
+{
+ connection_t *connection;
+ policy_t *policy;
+ ike_sa_id_t *id;
+
+ id = ike_sa_id_create(0, 0, FALSE);
+ this->new_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, id);
+ id->destroy(id);
+
+ connection = this->ike_sa->get_connection(this->ike_sa);
+ policy = this->ike_sa->get_policy(this->ike_sa);
+ this->new_sa->set_connection(this->new_sa, connection);
+ this->new_sa->set_policy(this->new_sa, policy);
+
+ this->ike_init = ike_init_create(this->new_sa, FALSE, this->ike_sa);
+ this->ike_init->task.process(&this->ike_init->task, message);
+
+ return NEED_MORE;
+}
+
+/**
+ * Implementation of task_t.build for responder
+ */
+static status_t build_r(private_ike_rekey_t *this, message_t *message)
+{
+ if (this->ike_init->task.build(&this->ike_init->task, message) == FAILED)
+ {
+ return SUCCESS;
+ }
+
+ this->ike_sa->set_state(this->ike_sa, IKE_REKEYING);
+ this->new_sa->inherit(this->new_sa, this->ike_sa);
+ this->new_sa->set_state(this->new_sa, IKE_ESTABLISHED);
+ charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa);
+ this->new_sa = NULL;
+
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.process for initiator
+ */
+static status_t process_i(private_ike_rekey_t *this, message_t *message)
+{
+ job_t *job;
+
+ if (this->ike_init->task.process(&this->ike_init->task, message) == FAILED)
+ {
+ return SUCCESS;
+ }
+
+ this->new_sa->set_state(this->new_sa, IKE_ESTABLISHED);
+ this->new_sa->inherit(this->new_sa, this->ike_sa);
+ charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa);
+ this->new_sa = NULL;
+
+ job = (job_t*)delete_ike_sa_job_create(this->ike_sa->get_id(this->ike_sa),
+ TRUE);
+
+ charon->job_queue->add(charon->job_queue, job);
+ return SUCCESS;
+}
+
+/**
+ * Implementation of task_t.get_type
+ */
+static task_type_t get_type(private_ike_rekey_t *this)
+{
+ return IKE_REKEY;
+}
+
+/**
+ * Implementation of task_t.migrate
+ */
+static void migrate(private_ike_rekey_t *this, ike_sa_t *ike_sa)
+{
+ if (this->ike_init)
+ {
+ this->ike_init->task.destroy(&this->ike_init->task);
+ }
+ if (this->new_sa)
+ {
+ charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
+ this->new_sa);
+ }
+
+ this->ike_sa = ike_sa;
+ this->new_sa = NULL;
+ this->ike_init = NULL;
+}
+
+/**
+ * Implementation of task_t.destroy
+ */
+static void destroy(private_ike_rekey_t *this)
+{
+ if (this->ike_init)
+ {
+ this->ike_init->task.destroy(&this->ike_init->task);
+ }
+ if (this->new_sa)
+ {
+ charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
+ this->new_sa);
+ }
+ free(this);
+}
+
+/*
+ * Described in header.
+ */
+ike_rekey_t *ike_rekey_create(ike_sa_t *ike_sa, bool initiator)
+{
+ private_ike_rekey_t *this = malloc_thing(private_ike_rekey_t);
+
+ this->public.task.get_type = (task_type_t(*)(task_t*))get_type;
+ this->public.task.migrate = (void(*)(task_t*,ike_sa_t*))migrate;
+ this->public.task.destroy = (void(*)(task_t*))destroy;
+ if (initiator)
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_i;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_i;
+ }
+ else
+ {
+ this->public.task.build = (status_t(*)(task_t*,message_t*))build_r;
+ this->public.task.process = (status_t(*)(task_t*,message_t*))process_r;
+ }
+
+ this->ike_sa = ike_sa;
+ this->new_sa = NULL;
+ this->ike_init = NULL;
+ this->initiator = initiator;
+
+ return &this->public;
+}
diff --git a/src/charon/sa/tasks/ike_rekey.h b/src/charon/sa/tasks/ike_rekey.h
new file mode 100644
index 000000000..f1caf5758
--- /dev/null
+++ b/src/charon/sa/tasks/ike_rekey.h
@@ -0,0 +1,57 @@
+/**
+ * @file ike_rekey.h
+ *
+ * @brief Interface ike_rekey_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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.
+ */
+
+#ifndef IKE_REKEY_H_
+#define IKE_REKEY_H_
+
+typedef struct ike_rekey_t ike_rekey_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <sa/tasks/task.h>
+
+/**
+ * @brief Task of type IKE_REKEY, rekey an established IKE_SA.
+ *
+ * @b Constructors:
+ * - ike_rekey_create()
+ *
+ * @ingroup tasks
+ */
+struct ike_rekey_t {
+
+ /**
+ * Implements the task_t interface
+ */
+ task_t task;
+};
+
+/**
+ * @brief Create a new IKE_REKEY task.
+ *
+ * @param ike_sa IKE_SA this task works for
+ * @param initiator TRUE for initiator, FALSE for responder
+ * @return IKE_REKEY task to handle by the task_manager
+ */
+ike_rekey_t *ike_rekey_create(ike_sa_t *ike_sa, bool initiator);
+
+#endif /* IKE_REKEY_H_ */
diff --git a/src/charon/sa/tasks/task.c b/src/charon/sa/tasks/task.c
new file mode 100644
index 000000000..68d8ebf0c
--- /dev/null
+++ b/src/charon/sa/tasks/task.c
@@ -0,0 +1,38 @@
+/**
+ * @file task.c
+ *
+ * @brief Enum values for task types
+ *
+ */
+
+/*
+ * Copyright (C) 2007 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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 "task.h"
+
+ENUM(task_type_names, IKE_INIT, CHILD_REKEY,
+ "IKE_INIT",
+ "IKE_NATD",
+ "IKE_AUTHENTICATE",
+ "IKE_CERT",
+ "IKE_CONFIG",
+ "IKE_DPD",
+ "IKE_REKEY",
+ "IKE_DELETE",
+ "IKE_DEADPEER",
+ "CHILD_CREATE",
+ "CHILD_DELETE",
+ "CHILD_REKEY",
+);
diff --git a/src/charon/sa/tasks/task.h b/src/charon/sa/tasks/task.h
new file mode 100644
index 000000000..128d7db4a
--- /dev/null
+++ b/src/charon/sa/tasks/task.h
@@ -0,0 +1,151 @@
+/**
+ * @file task.h
+ *
+ * @brief Interface task_t.
+ *
+ */
+
+/*
+ * Copyright (C) 2006 Martin Willi
+ * Hochschule fuer Technik Rapperswil
+ *
+ * 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.
+ */
+
+#ifndef TASK_H_
+#define TASK_H_
+
+typedef enum task_type_t task_type_t;
+typedef struct task_t task_t;
+
+#include <library.h>
+#include <sa/ike_sa.h>
+#include <encoding/message.h>
+
+/**
+ * @brief Different kinds of tasks.
+ *
+ * @ingroup tasks
+ */
+enum task_type_t {
+ /** establish an unauthenticated IKE_SA */
+ IKE_INIT,
+ /** detect NAT situation */
+ IKE_NATD,
+ /** authenticate the initiated IKE_SA */
+ IKE_AUTHENTICATE,
+ /** exchange certificates and requests */
+ IKE_CERT,
+ /** Configuration payloads, virtual IP and such */
+ IKE_CONFIG,
+ /** DPD detection */
+ IKE_DEADPEER,
+ /** rekey an IKE_SA */
+ IKE_REKEY,
+ /** delete an IKE_SA */
+ IKE_DELETE,
+ /** liveness check */
+ IKE_DPD,
+ /** establish a CHILD_SA within an IKE_SA */
+ CHILD_CREATE,
+ /** delete an established CHILD_SA */
+ CHILD_DELETE,
+ /** rekey an CHILD_SA */
+ CHILD_REKEY,
+};
+
+/**
+ * enum names for task_type_t.
+ */
+extern enum_name_t *task_type_names;
+
+/**
+ * @brief Interface for a task, an operation handled within exchanges.
+ *
+ * A task is an elemantary operation. It may be handled by a single or by
+ * multiple exchanges. An exchange may even complete multiple tasks.
+ * A task has a build() and an process() operation. The build() operation
+ * creates payloads and adds it to the message. The process() operation
+ * inspects a message and handles its payloads. An initiator of an exchange
+ * first calls build() to build the request, and processes the response message
+ * with the process() method.
+ * A responder does the opposite; it calls process() first to handle an incoming
+ * request and secondly calls build() to build an appropriate response.
+ * Both methods return either SUCCESS, NEED_MORE or FAILED. A SUCCESS indicates
+ * that the task completed, even when the task completed unsuccesfully. The
+ * manager then removes the task from the list. A NEED_MORE is returned when
+ * the task needs further build()/process() calls to complete, the manager
+ * leaves the taks in the queue. A returned FAILED indicates a critical failure.
+ * The manager closes the IKE_SA whenever a task returns FAILED.
+ *
+ * @b Constructors:
+ * - None, use implementations specific constructors
+ *
+ * @ingroup tasks
+ */
+struct task_t {
+
+ /**
+ * @brief Build a request or response message for this task.
+ *
+ * @param this calling object
+ * @param message message to add payloads to
+ * @return
+ * - FAILED if a critical error occured
+ * - NEED_MORE if another call to build/process needed
+ * - SUCCESS if task completed
+ */
+ status_t (*build) (task_t *this, message_t *message);
+
+ /**
+ * @brief Process a request or response message for this task.
+ *
+ * @param this calling object
+ * @param message message to read payloads from
+ * @return
+ * - FAILED if a critical error occured
+ * - NEED_MORE if another call to build/process needed
+ * - SUCCESS if task completed
+ */
+ status_t (*process) (task_t *this, message_t *message);
+
+ /**
+ * @brief Get the type of the task implementation.
+ *
+ * @param this calling object
+ */
+ task_type_t (*get_type) (task_t *this);
+
+ /**
+ * @brief Migrate a task to a new IKE_SA.
+ *
+ * After migrating a task, it goes back to a state where it can be
+ * used again to initate an exchange. This is useful when a task
+ * has to get migrated to a new IKE_SA.
+ * A special usage is when a INVALID_KE_PAYLOAD is received. A call
+ * to reset resets the task, but uses another DH group for the next
+ * try.
+ * The ike_sa is the new IKE_SA this task belongs to and operates on.
+ *
+ * @param this calling object
+ * @param ike_sa new IKE_SA this task works for
+ */
+ void (*migrate) (task_t *this, ike_sa_t *ike_sa);
+
+ /**
+ * @brief Destroys a task_t object.
+ *
+ * @param this calling object
+ */
+ void (*destroy) (task_t *this);
+};
+
+#endif /* TASK_H_ */
diff --git a/src/charon/sa/transactions/create_child_sa.c b/src/charon/sa/transactions/create_child_sa.c
deleted file mode 100644
index 67e4d782b..000000000
--- a/src/charon/sa/transactions/create_child_sa.c
+++ /dev/null
@@ -1,1121 +0,0 @@
-/**
- * @file create_child_sa.c
- *
- * @brief Implementation of create_child_sa_t transaction.
- *
- */
-
-/*
- * Copyright (C) 2006 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * 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 "create_child_sa.h"
-
-#include <string.h>
-
-#include <daemon.h>
-#include <encoding/payloads/sa_payload.h>
-#include <encoding/payloads/nonce_payload.h>
-#include <encoding/payloads/ts_payload.h>
-#include <sa/transactions/delete_child_sa.h>
-#include <utils/randomizer.h>
-
-
-typedef struct private_create_child_sa_t private_create_child_sa_t;
-
-/**
- * Private members of a create_child_sa_t object..
- */
-struct private_create_child_sa_t {
-
- /**
- * Public methods and transaction_t interface.
- */
- create_child_sa_t public;
-
- /**
- * Assigned IKE_SA.
- */
- ike_sa_t *ike_sa;
-
- /**
- * Message sent by our peer, if already generated
- */
- message_t *message;
-
- /**
- * Message ID this transaction uses
- */
- u_int32_t message_id;
-
- /**
- * Times we did send the request
- */
- u_int32_t requested;
-
- /**
- * initiators inbound SPI of the CHILD_SA which gets rekeyed
- */
- u_int32_t rekey_spi;
-
- /**
- * reqid to use for new CHILD_SA
- */
- u_int32_t reqid;
-
- /**
- * policy definition used
- */
- policy_t *policy;
-
- /**
- * Negotiated proposal used for CHILD_SA
- */
- proposal_t *proposal;
-
- /**
- * initiator chosen nonce
- */
- chunk_t nonce_i;
-
- /**
- * responder chosen nonce
- */
- chunk_t nonce_r;
-
- /**
- * lower of the nonces of a simultaneus rekeying request
- */
- chunk_t nonce_s;
-
- /**
- * Negotiated traffic selectors for initiator
- */
- linked_list_t *tsi;
-
- /**
- * Negotiated traffic selectors for responder
- */
- linked_list_t *tsr;
-
- /**
- * CHILD_SA created by this transaction
- */
- child_sa_t *child_sa;
-
- /**
- * CHILD_SA rekeyed if we are rekeying
- */
- child_sa_t *rekeyed_sa;
-
- /**
- * mode of the CHILD_SA to create: transport/tunnel
- */
- mode_t mode;
-
- /**
- * Have we lost the simultaneous rekeying nonce compare?
- */
- bool lost;
-
- /**
- * source of randomness
- */
- randomizer_t *randomizer;
-
- /**
- * signal to emit when transaction fails. As this transaction is used
- * for CHILD_SA creation AND rekeying, we must emit different signals.
- */
- signal_t failsig;
-};
-
-/**
- * Implementation of transaction_t.get_message_id.
- */
-static u_int32_t get_message_id(private_create_child_sa_t *this)
-{
- return this->message_id;
-}
-
-/**
- * Implementation of transaction_t.requested.
- */
-static u_int32_t requested(private_create_child_sa_t *this)
-{
- return this->requested++;
-}
-
-/**
- * Implementation of create_child_sa_t.set_policy.
- */
-static void set_policy(private_create_child_sa_t *this, policy_t *policy)
-{
- this->policy = policy;
-}
-
-/**
- * Implementation of create_child_sa_t.set_reqid.
- */
-static void set_reqid(private_create_child_sa_t *this, u_int32_t reqid)
-{
- this->reqid = reqid;
-}
-
-/**
- * Implementation of create_child_sa_t.rekeys_child.
- */
-static void rekeys_child(private_create_child_sa_t *this, child_sa_t *child_sa)
-{
- this->rekeyed_sa = child_sa;
- this->failsig = CHILD_REKEY_FAILED;
-}
-
-/**
- * Implementation of create_child_sa_t.cancel.
- */
-static void cancel(private_create_child_sa_t *this)
-{
- this->rekeyed_sa = NULL;
- this->lost = TRUE;
-}
-
-/**
- * Build a notify message.
- */
-static void build_notify(notify_type_t type, chunk_t data, message_t *message, bool flush_message)
-{
- notify_payload_t *notify;
-
- if (flush_message)
- {
- payload_t *payload;
- iterator_t *iterator = message->get_payload_iterator(message);
- while (iterator->iterate(iterator, (void**)&payload))
- {
- payload->destroy(payload);
- iterator->remove(iterator);
- }
- iterator->destroy(iterator);
- }
-
- notify = notify_payload_create();
- notify->set_notify_type(notify, type);
- notify->set_notification_data(notify, data);
- message->add_payload(message, (payload_t*)notify);
-}
-
-/**
- * Implementation of transaction_t.get_request.
- */
-static status_t get_request(private_create_child_sa_t *this, message_t **result)
-{
- message_t *request;
- host_t *me, *other;
- identification_t *my_id, *other_id;
-
- /* check if we already have built a message (retransmission) */
- if (this->message)
- {
- *result = this->message;
- return SUCCESS;
- }
-
- /* check if we are not already rekeying */
- if (this->rekeyed_sa)
- {
- SIG(CHILD_REKEY_START, "rekeying CHILD_SA");
-
- switch (this->rekeyed_sa->get_state(this->rekeyed_sa))
- {
- case CHILD_REKEYING:
- SIG(CHILD_REKEY_FAILED,
- "rekeying a CHILD_SA which is already rekeying, aborted");
- return FAILED;
- case CHILD_DELETING:
- SIG(CHILD_REKEY_FAILED,
- "rekeying a CHILD_SA which is deleting, aborted");
- return FAILED;
- default:
- break;
- }
- this->rekeyed_sa->set_state(this->rekeyed_sa, CHILD_REKEYING);
- }
- else
- {
- SIG(CHILD_UP_START, "creating CHILD_SA");
- }
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
- my_id = this->ike_sa->get_my_id(this->ike_sa);
- other_id = this->ike_sa->get_other_id(this->ike_sa);
-
- /* build the request */
- request = message_create();
- request->set_source(request, me->clone(me));
- request->set_destination(request, other->clone(other));
- request->set_exchange_type(request, CREATE_CHILD_SA);
- request->set_request(request, TRUE);
- request->set_ike_sa_id(request, this->ike_sa->get_id(this->ike_sa));
- *result = request;
- this->message = request;
-
- { /* build SA payload */
- sa_payload_t *sa_payload;
- linked_list_t *proposals;
- bool use_natt;
-
- /* get a policy, if we are rekeying */
- if (this->rekeyed_sa)
- {
- linked_list_t *my_ts, *other_ts;
- identification_t *my_id, *other_id;
-
- my_ts = this->rekeyed_sa->get_my_traffic_selectors(this->rekeyed_sa);
- other_ts = this->rekeyed_sa->get_other_traffic_selectors(this->rekeyed_sa);
- my_id = this->ike_sa->get_my_id(this->ike_sa);
- other_id = this->ike_sa->get_other_id(this->ike_sa);
-
- this->policy = charon->policies->get_policy(charon->policies,
- my_id, other_id,
- my_ts, other_ts,
- me, other);
-
- this->reqid = this->rekeyed_sa->get_reqid(this->rekeyed_sa);
-
- if (this->policy == NULL)
- {
- SIG(IKE_REKEY_FAILED, "no policy found to rekey "
- "CHILD_SA with reqid %d", this->reqid);
- return FAILED;
- }
- }
-
- proposals = this->policy->get_proposals(this->policy);
- use_natt = this->ike_sa->is_natt_enabled(this->ike_sa);
- this->child_sa = child_sa_create(this->reqid, me, other, my_id, other_id,
- this->policy->get_soft_lifetime(this->policy),
- this->policy->get_hard_lifetime(this->policy),
- this->policy->get_updown(this->policy),
- this->policy->get_hostaccess(this->policy),
- use_natt);
- this->child_sa->set_name(this->child_sa, this->policy->get_name(this->policy));
- if (this->child_sa->alloc(this->child_sa, proposals) != SUCCESS)
- {
- SIG(this->failsig, "could not install CHILD_SA, CHILD_SA creation failed");
- return FAILED;
- }
- sa_payload = sa_payload_create_from_proposal_list(proposals);
- proposals->destroy_offset(proposals, offsetof(proposal_t, destroy));
- request->add_payload(request, (payload_t*)sa_payload);
- }
-
- /* notify for transport/BEET mode, we propose it
- * independent of the traffic selectors */
- switch (this->policy->get_mode(this->policy))
- {
- case MODE_TUNNEL:
- /* is the default */
- break;
- case MODE_TRANSPORT:
- if (this->ike_sa->is_natt_enabled(this->ike_sa))
- {
- DBG1(DBG_IKE, "not using tranport mode, as connection NATed");
- }
- else
- {
- build_notify(USE_TRANSPORT_MODE, chunk_empty, request, FALSE);
- }
- break;
- case MODE_BEET:
- build_notify(USE_BEET_MODE, chunk_empty, request, FALSE);
- break;
- }
-
- { /* build the NONCE payload for us (initiator) */
- nonce_payload_t *nonce_payload;
-
- if (this->randomizer->allocate_pseudo_random_bytes(this->randomizer,
- NONCE_SIZE, &this->nonce_i) != SUCCESS)
- {
- SIG(this->failsig, "could not create nonce, CHILD_SA creation failed");
- return FAILED;
- }
- nonce_payload = nonce_payload_create();
- nonce_payload->set_nonce(nonce_payload, this->nonce_i);
- request->add_payload(request, (payload_t*)nonce_payload);
- }
-
- { /* build TSi payload */
- linked_list_t *ts_list;
- ts_payload_t *ts_payload;
-
- ts_list = this->policy->get_my_traffic_selectors(this->policy, me);
- ts_payload = ts_payload_create_from_traffic_selectors(TRUE, ts_list);
- ts_list->destroy_offset(ts_list, offsetof(traffic_selector_t, destroy));
- request->add_payload(request, (payload_t*)ts_payload);
- }
-
- { /* build TSr payload */
- linked_list_t *ts_list;
- ts_payload_t *ts_payload;
-
- ts_list = this->policy->get_other_traffic_selectors(this->policy, other);
- ts_payload = ts_payload_create_from_traffic_selectors(FALSE, ts_list);
- ts_list->destroy_offset(ts_list, offsetof(traffic_selector_t, destroy));
- request->add_payload(request, (payload_t*)ts_payload);
- }
-
- if (this->rekeyed_sa)
- { /* add REKEY_SA notify if we are rekeying */
- notify_payload_t *notify;
- protocol_id_t protocol;
-
- protocol = this->rekeyed_sa->get_protocol(this->rekeyed_sa);
- notify = notify_payload_create_from_protocol_and_type(protocol, REKEY_SA);
- notify->set_spi(notify, this->rekeyed_sa->get_spi(this->rekeyed_sa, TRUE));
- request->add_payload(request, (payload_t*)notify);
-
- /* register us as rekeying to detect multiple rekeying */
- this->rekeyed_sa->set_rekeying_transaction(this->rekeyed_sa,
- &this->public.transaction);
- }
-
- this->message_id = this->ike_sa->get_next_message_id(this->ike_sa);
- request->set_message_id(request, this->message_id);
-
- return SUCCESS;
-}
-
-/**
- * Handle all kind of notifys
- */
-static status_t process_notifys(private_create_child_sa_t *this, notify_payload_t *notify_payload)
-{
- notify_type_t notify_type = notify_payload->get_notify_type(notify_payload);
-
- DBG2(DBG_IKE, "process notify type %N", notify_type_names, notify_type);
-
- switch (notify_type)
- {
- case SINGLE_PAIR_REQUIRED:
- {
- SIG(this->failsig, "received a SINGLE_PAIR_REQUIRED notify");
- return FAILED;
- }
- case TS_UNACCEPTABLE:
- {
- SIG(this->failsig, "received TS_UNACCEPTABLE notify");
- return FAILED;
- }
- case NO_PROPOSAL_CHOSEN:
- {
- SIG(this->failsig, "received NO_PROPOSAL_CHOSEN notify");
- return FAILED;
- }
- case USE_TRANSPORT_MODE:
- {
- this->mode = MODE_TRANSPORT;
- return SUCCESS;
- }
- case USE_BEET_MODE:
- {
- this->mode = MODE_BEET;
- return SUCCESS;
- }
- case REKEY_SA:
- {
- u_int32_t spi;
- protocol_id_t protocol;
-
- protocol = notify_payload->get_protocol_id(notify_payload);
- switch (protocol)
- {
- case PROTO_AH:
- case PROTO_ESP:
- spi = notify_payload->get_spi(notify_payload);
- this->rekeyed_sa = this->ike_sa->get_child_sa(this->ike_sa,
- protocol, spi,
- FALSE);
- this->failsig = CHILD_REKEY_FAILED;
- break;
- default:
- break;
- }
- return SUCCESS;
- }
- default:
- {
- if (notify_type < 16383)
- {
- SIG(this->failsig, "received %N notify error, CHILD_SA "
- "creation failed", notify_type_names, notify_type);
- return FAILED;
- }
- else
- {
- DBG1(DBG_IKE, "received %N notify, ignored",
- notify_type_names, notify_type);
- return SUCCESS;
- }
- }
- }
-}
-
-/**
- * Check a list of traffic selectors if any selector belongs to host
- */
-static bool ts_list_is_host(linked_list_t *list, host_t *host)
-{
- traffic_selector_t *ts;
- bool is_host = TRUE;
- iterator_t *iterator = list->create_iterator(list, TRUE);
-
- while (is_host && iterator->iterate(iterator, (void**)&ts))
- {
- is_host = is_host && ts->is_host(ts, host);
- }
- iterator->destroy(iterator);
- return is_host;
-}
-
-/**
- * Install a CHILD_SA for usage
- */
-static status_t install_child_sa(private_create_child_sa_t *this, bool initiator)
-{
- prf_plus_t *prf_plus;
- chunk_t seed;
- status_t status;
-
- seed = chunk_alloc(this->nonce_i.len + this->nonce_r.len);
- memcpy(seed.ptr, this->nonce_i.ptr, this->nonce_i.len);
- memcpy(seed.ptr + this->nonce_i.len, this->nonce_r.ptr, this->nonce_r.len);
- prf_plus = prf_plus_create(this->ike_sa->get_child_prf(this->ike_sa), seed);
- chunk_free(&seed);
-
- if (initiator)
- {
- status = this->child_sa->update(this->child_sa, this->proposal,
- this->mode, prf_plus);
- }
- else
- {
- status = this->child_sa->add(this->child_sa, this->proposal,
- this->mode, prf_plus);
- }
- prf_plus->destroy(prf_plus);
- if (status != SUCCESS)
- {
- return DESTROY_ME;
- }
- if (initiator)
- {
- status = this->child_sa->add_policies(this->child_sa, this->tsi,
- this->tsr, this->mode);
- }
- else
- {
- status = this->child_sa->add_policies(this->child_sa, this->tsr,
- this->tsi, this->mode);
- }
- if (status != SUCCESS)
- {
- return DESTROY_ME;
- }
-
- /* add to IKE_SA, and remove from transaction */
- this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
- this->ike_sa->add_child_sa(this->ike_sa, this->child_sa);
- this->child_sa = NULL;
- return SUCCESS;
-}
-
-/**
- * Implementation of transaction_t.get_response.
- */
-static status_t get_response(private_create_child_sa_t *this, message_t *request,
- message_t **result, transaction_t **next)
-{
- host_t *me, *other;
- identification_t *my_id, *other_id;
- message_t *response;
- status_t status;
- iterator_t *payloads;
- payload_t *payload;
- sa_payload_t *sa_request = NULL;
- nonce_payload_t *nonce_request = NULL;
- ts_payload_t *tsi_request = NULL;
- ts_payload_t *tsr_request = NULL;
- nonce_payload_t *nonce_response;
-
- /* check if we already have built a response (retransmission) */
- if (this->message)
- {
- *result = this->message;
- return SUCCESS;
- }
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
- my_id = this->ike_sa->get_my_id(this->ike_sa);
- other_id = this->ike_sa->get_other_id(this->ike_sa);
- this->message_id = request->get_message_id(request);
-
- /* set up response */
- response = message_create();
- response->set_source(response, me->clone(me));
- response->set_destination(response, other->clone(other));
- response->set_exchange_type(response, CREATE_CHILD_SA);
- response->set_request(response, FALSE);
- response->set_message_id(response, this->message_id);
- response->set_ike_sa_id(response, this->ike_sa->get_id(this->ike_sa));
- this->message = response;
- *result = response;
-
- /* check message type */
- if (request->get_exchange_type(request) != CREATE_CHILD_SA)
- {
- DBG1(DBG_IKE, "CREATE_CHILD_SA response of invalid type, aborted");
- return FAILED;
- }
-
- /* we do not allow the creation of new CHILDren/rekeying when IKE_SA is
- * rekeying */
- if (this->ike_sa->get_state(this->ike_sa) == IKE_REKEYING ||
- this->ike_sa->get_state(this->ike_sa) == IKE_DELETING)
- {
- build_notify(NO_ADDITIONAL_SAS, chunk_empty, response, TRUE);
- DBG1(DBG_IKE, "unable to create new CHILD_SAs, as rekeying in progress");
- return FAILED;
- }
-
- /* Iterate over all payloads. */
- payloads = request->get_payload_iterator(request);
- while (payloads->iterate(payloads, (void**)&payload))
- {
- switch (payload->get_type(payload))
- {
- case SECURITY_ASSOCIATION:
- sa_request = (sa_payload_t*)payload;
- break;
- case NONCE:
- nonce_request = (nonce_payload_t*)payload;
- break;
- case TRAFFIC_SELECTOR_INITIATOR:
- tsi_request = (ts_payload_t*)payload;
- break;
- case TRAFFIC_SELECTOR_RESPONDER:
- tsr_request = (ts_payload_t*)payload;
- break;
- case KEY_EXCHANGE:
- {
- u_int8_t dh_buffer[] = {0x00, 0x00}; /* MODP_NONE */
- chunk_t group = chunk_from_buf(dh_buffer);
- build_notify(INVALID_KE_PAYLOAD, group, response, TRUE);
- DBG1(DBG_IKE, "CREATE_CHILD_SA used PFS, sending INVALID_KE_PAYLOAD");
- return FAILED;
- }
- case NOTIFY:
- {
- status = process_notifys(this, (notify_payload_t*)payload);
- if (status != SUCCESS)
- {
- payloads->destroy(payloads);
- return status;
- }
- break;
- }
- default:
- {
- DBG1(DBG_IKE, "ignoring %N payload",
- payload_type_names, payload->get_type(payload));
- break;
- }
- }
- }
- payloads->destroy(payloads);
-
- /* after processing the notify payloads, we know if this transaction is
- * for rekeying or for a new CHILD_SA. We can emit the signals now. */
- if (this->rekeyed_sa)
- {
- SIG(CHILD_REKEY_START, "rekeying CHILD_SA");
- }
- else
- {
- SIG(CHILD_UP_START, "creating CHILD_SA");
- }
-
- /* check if we have all payloads */
- if (!(sa_request && nonce_request && tsi_request && tsr_request))
- {
- build_notify(INVALID_SYNTAX, chunk_empty, response, TRUE);
- SIG(this->failsig, "request message incomplete, no CHILD_SA created");
- return FAILED;
- }
-
- { /* process nonce payload */
- this->nonce_i = nonce_request->get_nonce(nonce_request);
- if (this->randomizer->allocate_pseudo_random_bytes(this->randomizer,
- NONCE_SIZE, &this->nonce_r) != SUCCESS)
- {
- build_notify(NO_PROPOSAL_CHOSEN, chunk_empty, response, TRUE);
- SIG(this->failsig, "nonce generation failed, no CHILD_SA created");
- return FAILED;
- }
- nonce_response = nonce_payload_create();
- nonce_response->set_nonce(nonce_response, this->nonce_r);
- }
-
- { /* get a policy and process traffic selectors */
- identification_t *my_id, *other_id;
- linked_list_t *my_ts, *other_ts;
-
- my_id = this->ike_sa->get_my_id(this->ike_sa);
- other_id = this->ike_sa->get_other_id(this->ike_sa);
-
- my_ts = tsr_request->get_traffic_selectors(tsr_request);
- other_ts = tsi_request->get_traffic_selectors(tsi_request);
-
- this->policy = charon->policies->get_policy(charon->policies,
- my_id, other_id,
- my_ts, other_ts,
- me, other);
- if (this->policy)
- {
- this->tsr = this->policy->select_my_traffic_selectors(this->policy, my_ts, me);
- this->tsi = this->policy->select_other_traffic_selectors(this->policy, other_ts, other);
- }
- my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy));
- other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy));
-
- if (this->policy == NULL)
- {
- SIG(this->failsig, "no acceptable policy found, sending TS_UNACCEPTABLE notify");
- build_notify(TS_UNACCEPTABLE, chunk_empty, response, TRUE);
- return FAILED;
- }
- }
-
- { /* process SA payload */
- linked_list_t *proposal_list;
- sa_payload_t *sa_response;
- ts_payload_t *ts_response;
- bool use_natt;
- u_int32_t soft_lifetime, hard_lifetime;
-
- sa_response = sa_payload_create();
- /* get proposals from request, and select one with ours */
- proposal_list = sa_request->get_proposals(sa_request);
- DBG2(DBG_IKE, "selecting proposals:");
- this->proposal = this->policy->select_proposal(this->policy, proposal_list);
- proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
-
- /* do we have a proposal? */
- if (this->proposal == NULL)
- {
- SIG(this->failsig, "CHILD_SA proposals unacceptable, sending NO_PROPOSAL_CHOSEN notify");
- build_notify(NO_PROPOSAL_CHOSEN, chunk_empty, response, TRUE);
- return FAILED;
- }
- /* do we have traffic selectors? */
- else if (this->tsi->get_count(this->tsi) == 0 || this->tsr->get_count(this->tsr) == 0)
- {
- SIG(this->failsig, "CHILD_SA traffic selectors unacceptable, sending TS_UNACCEPTABLE notify");
- build_notify(TS_UNACCEPTABLE, chunk_empty, response, TRUE);
- return FAILED;
- }
- else
- { /* create child sa */
- if (this->rekeyed_sa)
- {
- this->reqid = this->rekeyed_sa->get_reqid(this->rekeyed_sa);
- }
- soft_lifetime = this->policy->get_soft_lifetime(this->policy);
- hard_lifetime = this->policy->get_hard_lifetime(this->policy);
- use_natt = this->ike_sa->is_natt_enabled(this->ike_sa);
- this->child_sa = child_sa_create(this->reqid, me, other, my_id, other_id,
- soft_lifetime, hard_lifetime,
- this->policy->get_updown(this->policy),
- this->policy->get_hostaccess(this->policy),
- use_natt);
- this->child_sa->set_name(this->child_sa, this->policy->get_name(this->policy));
-
- /* check mode, and include notify into reply */
- switch (this->mode)
- {
- case MODE_TUNNEL:
- /* is the default */
- break;
- case MODE_TRANSPORT:
- if (!ts_list_is_host(this->tsi, other) ||
- !ts_list_is_host(this->tsr, me))
- {
- this->mode = MODE_TUNNEL;
- DBG1(DBG_IKE, "not using tranport mode, not host-to-host");
- }
- else if (this->ike_sa->is_natt_enabled(this->ike_sa))
- {
- this->mode = MODE_TUNNEL;
- DBG1(DBG_IKE, "not using tranport mode, as connection NATed");
- }
- else
- {
- build_notify(USE_TRANSPORT_MODE, chunk_empty, response, FALSE);
- }
- break;
- case MODE_BEET:
- if (!ts_list_is_host(this->tsi, NULL) ||
- !ts_list_is_host(this->tsr, NULL))
- {
- this->mode = MODE_TUNNEL;
- DBG1(DBG_IKE, "not using BEET mode, not host-to-host");
- }
- else
- {
- build_notify(USE_BEET_MODE, chunk_empty, response, FALSE);
- }
- break;
- }
-
- if (install_child_sa(this, FALSE) != SUCCESS)
- {
- SIG(this->failsig, "installing CHILD_SA failed, sending NO_PROPOSAL_CHOSEN notify");
- build_notify(NO_PROPOSAL_CHOSEN, chunk_empty, response, TRUE);
- return FAILED;
- }
- /* add proposal to sa payload */
- sa_response->add_proposal(sa_response, this->proposal);
- }
- response->add_payload(response, (payload_t*)sa_response);
-
- /* add nonce/ts payload after sa payload */
- response->add_payload(response, (payload_t *)nonce_response);
- ts_response = ts_payload_create_from_traffic_selectors(TRUE, this->tsi);
- response->add_payload(response, (payload_t*)ts_response);
- ts_response = ts_payload_create_from_traffic_selectors(FALSE, this->tsr);
- response->add_payload(response, (payload_t*)ts_response);
- }
- /* CHILD_SA successfully created. If another transaction is already rekeying
- * this SA, our lower nonce must be registered for a later nonce compare. */
- if (this->rekeyed_sa)
- {
- private_create_child_sa_t *other;
-
- other = (private_create_child_sa_t*)
- this->rekeyed_sa->get_rekeying_transaction(this->rekeyed_sa);
- if (other)
- {
- /* store our lower nonce in the simultaneus transaction, it
- * will later compare it against its nonces when it calls conclude().
- */
- if (memcmp(this->nonce_i.ptr, this->nonce_r.ptr,
- min(this->nonce_i.len, this->nonce_r.len)) < 0)
- {
- other->nonce_s = chunk_clone(this->nonce_i);
- }
- else
- {
- other->nonce_s = chunk_clone(this->nonce_r);
- }
- }
- else
- {
- /* we only signal when no other transaction is rekeying */
- SIG(CHILD_REKEY_SUCCESS, "CHILD_SA rekeyed");
- }
- this->rekeyed_sa->set_state(this->rekeyed_sa, CHILD_REKEYING);
- }
- else
- {
- SIG(CHILD_UP_SUCCESS, "CHILD_SA '%s' created",
- this->policy->get_name(this->policy));
- }
- return SUCCESS;
-}
-
-/**
- * Implementation of transaction_t.conclude
- */
-static status_t conclude(private_create_child_sa_t *this, message_t *response,
- transaction_t **next)
-{
- iterator_t *payloads;
- payload_t *payload;
- host_t *me, *other;
- sa_payload_t *sa_payload = NULL;
- nonce_payload_t *nonce_payload = NULL;
- ts_payload_t *tsi_payload = NULL;
- ts_payload_t *tsr_payload = NULL;
- status_t status;
- child_sa_t *new_child = NULL;
- delete_child_sa_t *delete_child_sa;
-
- /* check message type */
- if (response->get_exchange_type(response) != CREATE_CHILD_SA)
- {
- SIG(this->failsig, "CREATE_CHILD_SA response of invalid type, aborting");
- return FAILED;
- }
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
-
- /* Iterate over all payloads to collect them */
- payloads = response->get_payload_iterator(response);
- while (payloads->iterate(payloads, (void**)&payload))
- {
- switch (payload->get_type(payload))
- {
- case SECURITY_ASSOCIATION:
- sa_payload = (sa_payload_t*)payload;
- break;
- case NONCE:
- nonce_payload = (nonce_payload_t*)payload;
- break;
- case TRAFFIC_SELECTOR_INITIATOR:
- tsi_payload = (ts_payload_t*)payload;
- break;
- case TRAFFIC_SELECTOR_RESPONDER:
- tsr_payload = (ts_payload_t*)payload;
- break;
- case NOTIFY:
- {
- status = process_notifys(this, (notify_payload_t*)payload);
- if (status != SUCCESS)
- {
- payloads->destroy(payloads);
- return status;
- }
- break;
- }
- default:
- {
- DBG1(DBG_IKE, "ignoring %N payload",
- payload_type_names, payload->get_type(payload));
- break;
- }
- }
- }
- payloads->destroy(payloads);
-
- if (!(sa_payload && nonce_payload && tsi_payload && tsr_payload))
- {
- SIG(this->failsig, "response message incomplete, no CHILD_SA built");
- return FAILED;
- }
-
- { /* process NONCE payload */
- this->nonce_r = nonce_payload->get_nonce(nonce_payload);
- }
-
- { /* process traffic selectors for us */
- linked_list_t *ts_received = tsi_payload->get_traffic_selectors(tsi_payload);
- this->tsi = this->policy->select_my_traffic_selectors(this->policy, ts_received, me);
- ts_received->destroy_offset(ts_received, offsetof(traffic_selector_t, destroy));
- }
-
- { /* process traffic selectors for other */
- linked_list_t *ts_received = tsr_payload->get_traffic_selectors(tsr_payload);
- this->tsr = this->policy->select_other_traffic_selectors(this->policy, ts_received, other);
- ts_received->destroy_offset(ts_received, offsetof(traffic_selector_t, destroy));
- }
-
- { /* process sa payload */
- linked_list_t *proposal_list;
-
- proposal_list = sa_payload->get_proposals(sa_payload);
- /* we have to re-check here if other's selection is valid */
- this->proposal = this->policy->select_proposal(this->policy, proposal_list);
- proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
-
- /* everything fine to create CHILD? */
- if (this->proposal == NULL ||
- this->tsi->get_count(this->tsi) == 0 ||
- this->tsr->get_count(this->tsr) == 0)
- {
- SIG(this->failsig, "CHILD_SA negotiation failed, no CHILD_SA built");
- return FAILED;
- }
-
- /* check mode if it is acceptable */
- switch (this->mode)
- {
- case MODE_TUNNEL:
- /* is the default */
- break;
- case MODE_TRANSPORT:
- /* TODO: we should close the CHILD_SA if negotiated
- * mode is not acceptable for us */
- if (!ts_list_is_host(this->tsi, me) ||
- !ts_list_is_host(this->tsr, other))
- {
- this->mode = MODE_TUNNEL;
- DBG1(DBG_IKE, "not using tranport mode, not host-to-host");
- }
- else if (this->ike_sa->is_natt_enabled(this->ike_sa))
- {
- this->mode = MODE_TUNNEL;
- DBG1(DBG_IKE, "not using tranport mode, as connection NATed");
- }
- break;
- case MODE_BEET:
- if (!ts_list_is_host(this->tsi, NULL) ||
- !ts_list_is_host(this->tsr, NULL))
- {
- this->mode = MODE_TUNNEL;
- DBG1(DBG_IKE, "not using BEET mode, not host-to-host");
- }
- break;
- }
-
- new_child = this->child_sa;
- if (install_child_sa(this, TRUE) != SUCCESS)
- {
- SIG(this->failsig, "installing CHILD_SA failed, no CHILD_SA built");
- return FAILED;
- }
- }
- /* CHILD_SA successfully created. If the other peer initiated rekeying
- * in the meantime, we detect this by comparing the rekeying_transaction
- * of the SA. If it changed, we are not alone. Then we must compare the nonces.
- * If no simultaneous rekeying is going on, we just initiate the delete of
- * the superseded SA. */
- if (this->rekeyed_sa)
- {
- /* rekeying finished, update SA status */
- this->rekeyed_sa->set_rekeying_transaction(this->rekeyed_sa, NULL);
-
- if (this->nonce_s.ptr)
- { /* simlutaneous rekeying is going on, not so good */
- chunk_t this_lowest;
-
- /* first get our lowest nonce */
- if (memcmp(this->nonce_i.ptr, this->nonce_r.ptr,
- min(this->nonce_i.len, this->nonce_r.len)) < 0)
- {
- this_lowest = this->nonce_i;
- }
- else
- {
- this_lowest = this->nonce_r;
- }
- /* then compare against other lowest nonce */
- if (memcmp(this_lowest.ptr, this->nonce_s.ptr,
- min(this_lowest.len, this->nonce_s.len)) < 0)
- {
- DBG1(DBG_IKE, "detected simultaneous CHILD_SA rekeying, deleting ours");
- this->lost = TRUE;
- }
- else
- {
- DBG1(DBG_IKE, "detected simultaneous CHILD_SA rekeying, but ours is preferred");
- }
- }
- /* delete the old SA if we have won the rekeying nonce compare*/
- if (!this->lost)
- {
- delete_child_sa = delete_child_sa_create(this->ike_sa);
- delete_child_sa->set_child_sa(delete_child_sa, this->rekeyed_sa);
- *next = (transaction_t*)delete_child_sa;
- }
- /* we send a rekey SUCCESS signal in any case. If the other transaction
- * detected our transaction, it did not send a signal. We do it for it. */
- SIG(CHILD_REKEY_SUCCESS, "CHILD_SA rekeyed");
- }
- else
- {
- SIG(CHILD_UP_SUCCESS, "CHILD_SA '%s' created",
- this->policy->get_name(this->policy));
- }
- if (this->lost)
- {
- /* we have lost simlutaneous rekeying, delete the CHILD_SA we just have created */
- delete_child_sa = delete_child_sa_create(this->ike_sa);
- delete_child_sa->set_child_sa(delete_child_sa, new_child);
- *next = (transaction_t*)delete_child_sa;
- }
- return SUCCESS;
-}
-
-/**
- * implements transaction_t.destroy
- */
-static void destroy(private_create_child_sa_t *this)
-{
- DESTROY_IF(this->message);
- DESTROY_IF(this->proposal);
- DESTROY_IF(this->child_sa);
- DESTROY_IF(this->policy);
- if (this->tsi)
- {
- this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
- }
- if (this->tsr)
- {
- this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy));
- }
- chunk_free(&this->nonce_i);
- chunk_free(&this->nonce_r);
- chunk_free(&this->nonce_s);
- this->randomizer->destroy(this->randomizer);
- free(this);
-}
-
-/*
- * Described in header.
- */
-create_child_sa_t *create_child_sa_create(ike_sa_t *ike_sa)
-{
- private_create_child_sa_t *this = malloc_thing(private_create_child_sa_t);
-
- /* transaction interface functions */
- this->public.transaction.get_request = (status_t(*)(transaction_t*,message_t**))get_request;
- this->public.transaction.get_response = (status_t(*)(transaction_t*,message_t*,message_t**,transaction_t**))get_response;
- this->public.transaction.conclude = (status_t(*)(transaction_t*,message_t*,transaction_t**))conclude;
- this->public.transaction.get_message_id = (u_int32_t(*)(transaction_t*))get_message_id;
- this->public.transaction.requested = (u_int32_t(*)(transaction_t*))requested;
- this->public.transaction.destroy = (void(*)(transaction_t*))destroy;
-
- /* public functions */
- this->public.set_policy = (void(*)(create_child_sa_t*,policy_t*))set_policy;
- this->public.set_reqid = (void(*)(create_child_sa_t*,u_int32_t))set_reqid;
- this->public.rekeys_child = (void(*)(create_child_sa_t*,child_sa_t*))rekeys_child;
- this->public.cancel = (void(*)(create_child_sa_t*))cancel;
-
- /* private data */
- this->ike_sa = ike_sa;
- this->message_id = 0;
- this->message = NULL;
- this->requested = 0;
- this->rekey_spi = 0;
- this->reqid = 0;
- this->nonce_i = chunk_empty;
- this->nonce_r = chunk_empty;
- this->nonce_s = chunk_empty;
- this->child_sa = NULL;
- this->rekeyed_sa = NULL;
- this->lost = FALSE;
- this->proposal = NULL;
- this->policy = NULL;
- this->tsi = NULL;
- this->tsr = NULL;
- this->mode = MODE_TUNNEL;
- this->randomizer = randomizer_create();
- this->failsig = CHILD_UP_FAILED;
-
- return &this->public;
-}
diff --git a/src/charon/sa/transactions/create_child_sa.h b/src/charon/sa/transactions/create_child_sa.h
deleted file mode 100644
index 8ce72e11d..000000000
--- a/src/charon/sa/transactions/create_child_sa.h
+++ /dev/null
@@ -1,123 +0,0 @@
-/**
- * @file create_child_sa.h
- *
- * @brief Interface of transaction create_child_sa.
- *
- */
-
-/*
- * Copyright (C) 2006 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * 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.
- */
-
-#ifndef CREATE_CHILD_SA_H_
-#define CREATE_CHILD_SA_H_
-
-typedef struct create_child_sa_t create_child_sa_t;
-
-#include <sa/transactions/transaction.h>
-#include <sa/ike_sa.h>
-#include <sa/child_sa.h>
-
-/**
- * @brief A transaction to create a new or rekey an existing CHILD_SA.
- *
- * If the CHILD_SA is intended to create a new CHILD_SA, set the policy
- * with set_policy(). If it is intended to rekey an existing CHILD_SA,
- * set the appropriate CHILD_SA with rekeys_child().
- *
- * Rekeying of an CHILD_SA works the same way as creating a new one,
- * but includes an additional REKEY_SA notify and deletes the old
- * one (in a separate transaction).
- *
- * __________ _________
- * Cyq \/ Czq
- * __________/\_________
- * detect __________ _________ detect
- * Czp \/ Czp
- * compare nonces, won __________/\_________ compare nonces, lost
- *
- * delete old __________
- * Dxq \__________
- * __________
- * __________/ Dxp
- * __________ delete created
- * __________/ Dzq
- * __________
- * Dzp \__________
- *
- *
- * @b Constructors:
- * - create_child_sa_create()
- * - transaction_create() with the appropriate message
- *
- * @ingroup transactions
- */
-struct create_child_sa_t {
-
- /**
- * The transaction_t interface.
- */
- transaction_t transaction;
-
- /**
- * @brief Set the policy to use for creating a new CHILD_SA.
- *
- * @param this calling object
- * @param policy policy for CHILD_SA
- */
- void (*set_policy) (create_child_sa_t* this, policy_t *policy);
-
- /**
- * @brief Set the reqid used for CHILD_SA setup.
- *
- * If we acquire, we must use the same reqid as the
- * installed policy.
- *
- * @param this calling object
- * @param reqid reqid to use for the CHILD_SA
- */
- void (*set_reqid) (create_child_sa_t* this, u_int32_t reqid);
-
- /**
- * @brief Set the CHILD_SA which gets rekeyed by the new one.
- *
- * @param this calling object
- * @param child_sa CHILD_SA to rekey
- */
- void (*rekeys_child) (create_child_sa_t* this, child_sa_t *child_sa);
-
- /**
- * @brief Cancel the request.
- *
- * Cancelling the request will set a flag in the transaction. When
- * the response for the transaction is received, the created CHILD_SA
- * gets deleted afterwards.
- *
- * @param this calling object
- * @param child_sa CHILD_SA to rekey
- */
- void (*cancel) (create_child_sa_t* this);
-};
-
-/**
- * @brief Create a new transaction which creates/rekeys CHILD_SAs.
- *
- * @param ike_sa assigned IKE_SA
- * @return created create_child_sa transaction
- *
- * @ingroup transactions
- */
-create_child_sa_t *create_child_sa_create(ike_sa_t *ike_sa);
-
-#endif /* CREATE_CHILD_SA_H_ */
diff --git a/src/charon/sa/transactions/dead_peer_detection.c b/src/charon/sa/transactions/dead_peer_detection.c
deleted file mode 100644
index 390ce3401..000000000
--- a/src/charon/sa/transactions/dead_peer_detection.c
+++ /dev/null
@@ -1,187 +0,0 @@
-/**
- * @file dead_peer_detection.c
- *
- * @brief Implementation of the dead_peer_detection transaction.
- *
- */
-
-/*
- * Copyright (C) 2006 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * 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 "dead_peer_detection.h"
-
-#include <daemon.h>
-
-
-typedef struct private_dead_peer_detection_t private_dead_peer_detection_t;
-
-/**
- * Private members of a dead_peer_detection_t object..
- */
-struct private_dead_peer_detection_t {
-
- /**
- * Public methods and transaction_t interface.
- */
- dead_peer_detection_t public;
-
- /**
- * Assigned IKE_SA.
- */
- ike_sa_t *ike_sa;
-
- /**
- * Message sent by our peer, if already generated
- */
- message_t *message;
-
- /**
- * Message ID this transaction uses
- */
- u_int32_t message_id;
-
- /**
- * Times we did send the request
- */
- u_int32_t requested;
-};
-
-/**
- * Implementation of transaction_t.get_message_id.
- */
-static u_int32_t get_message_id(private_dead_peer_detection_t *this)
-{
- return this->message_id;
-}
-
-/**
- * Implementation of transaction_t.requested.
- */
-static u_int32_t requested(private_dead_peer_detection_t *this)
-{
- return this->requested++;
-}
-
-/**
- * Implementation of transaction_t.get_request.
- */
-static status_t get_request(private_dead_peer_detection_t *this, message_t **result)
-{
- message_t *request;
- host_t *me, *other;
-
- /* check if we already have built a message (retransmission) */
- if (this->message)
- {
- *result = this->message;
- return SUCCESS;
- }
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
-
- /* build the request */
- request = message_create();
- request->set_source(request, me->clone(me));
- request->set_destination(request, other->clone(other));
- request->set_exchange_type(request, INFORMATIONAL);
- request->set_request(request, TRUE);
- this->message_id = this->ike_sa->get_next_message_id(this->ike_sa);
- request->set_message_id(request, this->message_id);
- request->set_ike_sa_id(request, this->ike_sa->get_id(this->ike_sa));
- /* apply for caller */
- *result = request;
- /* store for retransmission */
- this->message = request;
-
- return SUCCESS;
-}
-
-/**
- * Implementation of transaction_t.get_response.
- */
-static status_t get_response(private_dead_peer_detection_t *this, message_t *request,
- message_t **result, transaction_t **next)
-{
- host_t *me, *other;
- message_t *response;
-
- /* check if we already have built a response (retransmission) */
- if (this->message)
- {
- *result = this->message;
- return SUCCESS;
- }
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
- this->message_id = request->get_message_id(request);
-
- /* set up response */
- response = message_create();
- response->set_source(response, me->clone(me));
- response->set_destination(response, other->clone(other));
- response->set_exchange_type(response, INFORMATIONAL);
- response->set_request(response, FALSE);
- response->set_message_id(response, this->message_id);
- response->set_ike_sa_id(response, this->ike_sa->get_id(this->ike_sa));
- this->message = response;
- *result = response;
-
- return SUCCESS;
-}
-
-
-/**
- * Implementation of transaction_t.conclude
- */
-static status_t conclude(private_dead_peer_detection_t *this, message_t *response,
- transaction_t **transaction)
-{
- return SUCCESS;
-}
-
-/**
- * implements transaction_t.destroy
- */
-static void destroy(private_dead_peer_detection_t *this)
-{
- DESTROY_IF(this->message);
- free(this);
-}
-
-/*
- * Described in header.
- */
-dead_peer_detection_t *dead_peer_detection_create(ike_sa_t *ike_sa)
-{
- private_dead_peer_detection_t *this = malloc_thing(private_dead_peer_detection_t);
-
- /* transaction interface functions */
- this->public.transaction.get_request = (status_t(*)(transaction_t*,message_t**))get_request;
- this->public.transaction.get_response = (status_t(*)(transaction_t*,message_t*,message_t**,transaction_t**))get_response;
- this->public.transaction.conclude = (status_t(*)(transaction_t*,message_t*,transaction_t**))conclude;
- this->public.transaction.get_message_id = (u_int32_t(*)(transaction_t*))get_message_id;
- this->public.transaction.requested = (u_int32_t(*)(transaction_t*))requested;
- this->public.transaction.destroy = (void(*)(transaction_t*))destroy;
-
- /* private data */
- this->ike_sa = ike_sa;
- this->message_id = 0;
- this->message = NULL;
- this->requested = 0;
-
- return &this->public;
-}
diff --git a/src/charon/sa/transactions/dead_peer_detection.h b/src/charon/sa/transactions/dead_peer_detection.h
deleted file mode 100644
index 78d7b9b4f..000000000
--- a/src/charon/sa/transactions/dead_peer_detection.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/**
- * @file dead_peer_detection.h
- *
- * @brief Interface of transaction dead_peer_detection.
- *
- */
-
-/*
- * Copyright (C) 2006 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * 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.
- */
-
-
-#ifndef DEAD_PEER_DETECTION_H_
-#define DEAD_PEER_DETECTION_H_
-
-typedef struct dead_peer_detection_t dead_peer_detection_t;
-
-#include <sa/ike_sa.h>
-#include <sa/transactions/transaction.h>
-
-/**
- * @brief A transaction used to detect dead peers.
- *
- * In IKEv2, dead peer detection is done using empty
- * informational messages. These must be acknowledged.
- *
- * @ingroup transactions
- */
-struct dead_peer_detection_t {
-
- /**
- * The transaction_t interface.
- */
- transaction_t transaction;
-};
-
-/**
- * @brief Create a new transaction which detects dead peers.
- *
- * @param ike_sa assigned IKE_SA
- * @return created dead_peer_detection transaction
- *
- * @ingroup transactions
- */
-dead_peer_detection_t *dead_peer_detection_create(ike_sa_t *ike_sa);
-
-#endif /* DEAD_PEER_DETECTION_H_ */
diff --git a/src/charon/sa/transactions/delete_child_sa.c b/src/charon/sa/transactions/delete_child_sa.c
deleted file mode 100644
index 7ec332004..000000000
--- a/src/charon/sa/transactions/delete_child_sa.c
+++ /dev/null
@@ -1,356 +0,0 @@
-/**
- * @file delete_child_sa.c
- *
- * @brief Implementation of the delete_child_sa transaction.
- *
- */
-
-/*
- * Copyright (C) 2006 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * 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 "delete_child_sa.h"
-
-#include <daemon.h>
-#include <encoding/payloads/delete_payload.h>
-#include <sa/transactions/create_child_sa.h>
-
-
-typedef struct private_delete_child_sa_t private_delete_child_sa_t;
-
-/**
- * Private members of a delete_child_sa_t object..
- */
-struct private_delete_child_sa_t {
-
- /**
- * Public methods and transaction_t interface.
- */
- delete_child_sa_t public;
-
- /**
- * Assigned IKE_SA.
- */
- ike_sa_t *ike_sa;
-
- /**
- * Message sent by our peer, if already generated
- */
- message_t *message;
-
- /**
- * Message ID this transaction uses
- */
- u_int32_t message_id;
-
- /**
- * Times we did send the request
- */
- u_int32_t requested;
-
- /**
- * CHILD SA to delete
- */
- child_sa_t *child_sa;
-};
-
-/**
- * Implementation of transaction_t.get_message_id.
- */
-static u_int32_t get_message_id(private_delete_child_sa_t *this)
-{
- return this->message_id;
-}
-
-/**
- * Implementation of transaction_t.requested.
- */
-static u_int32_t requested(private_delete_child_sa_t *this)
-{
- return this->requested++;
-}
-
-/**
- * Implementation of delete_child_sa_t.set_child_sa.
- */
-static void set_child_sa(private_delete_child_sa_t *this, child_sa_t *child_sa)
-{
- this->child_sa = child_sa;
-}
-
-/**
- * Implementation of transaction_t.get_request.
- */
-static status_t get_request(private_delete_child_sa_t *this, message_t **result)
-{
- message_t *request;
- host_t *me, *other;
-
- /* check if we already have built a message (retransmission) */
- if (this->message)
- {
- *result = this->message;
- return SUCCESS;
- }
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
-
- /* build the request */
- request = message_create();
- request->set_source(request, me->clone(me));
- request->set_destination(request, other->clone(other));
- request->set_exchange_type(request, INFORMATIONAL);
- request->set_request(request, TRUE);
- this->message_id = this->ike_sa->get_next_message_id(this->ike_sa);
- request->set_message_id(request, this->message_id);
- request->set_ike_sa_id(request, this->ike_sa->get_id(this->ike_sa));
- *result = request;
- this->message = request;
-
- { /* add delete payload */
- delete_payload_t *delete_payload;
- protocol_id_t protocol;
- u_int32_t spi;
-
- protocol = this->child_sa->get_protocol(this->child_sa);
- spi = this->child_sa->get_spi(this->child_sa, TRUE);
- delete_payload = delete_payload_create(protocol);
-
- DBG1(DBG_IKE, "created DELETE payload for %N CHILD_SA with SPI 0x%x",
- protocol_id_names, protocol, htonl(spi));
- delete_payload->add_spi(delete_payload, spi);
- request->add_payload(request, (payload_t*)delete_payload);
- }
-
- this->child_sa->set_state(this->child_sa, CHILD_DELETING);
-
- return SUCCESS;
-}
-
-/**
- * process a delete payload
- */
-static status_t process_delete(private_delete_child_sa_t *this, delete_payload_t *delete_request, message_t *response)
-{
- protocol_id_t protocol;
- u_int32_t spi;
- iterator_t *iterator;
- delete_payload_t *delete_response = NULL;
-
- /* get requested CHILD */
- protocol = delete_request->get_protocol_id(delete_request);
- if (protocol != PROTO_ESP && protocol != PROTO_AH)
- {
- DBG1(DBG_IKE, "CHILD_SA delete response contained unexpected protocol");
- return FAILED;
- }
-
- /* prepare response payload */
- if (response)
- {
- delete_response = delete_payload_create(protocol);
- response->add_payload(response, (payload_t*)delete_response);
- }
-
- iterator = delete_request->create_spi_iterator(delete_request);
- while (iterator->iterate(iterator, (void**)&spi))
- {
- child_sa_t *child_sa;
-
- child_sa = this->ike_sa->get_child_sa(this->ike_sa, protocol, spi, FALSE);
-
- if (child_sa != NULL)
- {
- create_child_sa_t *rekey;
-
- child_sa->set_state(child_sa, CHILD_DELETING);
-
- DBG1(DBG_IKE, "received DELETE for %N CHILD_SA with SPI 0x%x, deleting",
- protocol_id_names, protocol, ntohl(spi));
-
- rekey = (create_child_sa_t*)child_sa->get_rekeying_transaction(child_sa);
- if (rekey)
- {
- /* we have received a delete for an SA which we are still rekeying.
- * this means we have lost the nonce comparison, and the rekeying
- * will fail. We set a flag in the transaction for this special case.
- */
- rekey->cancel(rekey);
- }
- /* delete it, with inbound spi */
- spi = child_sa->get_spi(child_sa, TRUE);
- this->ike_sa->destroy_child_sa(this->ike_sa, protocol, spi);
- /* add delete response to message, if we are responding */
- if (response)
- {
- delete_response->add_spi(delete_response, spi);
- }
- }
- else
- {
- DBG1(DBG_IKE, "received DELETE for %N CHILD_SA with SPI 0x%x, but no such SA",
- protocol_id_names, protocol, ntohl(spi));
- }
- }
- iterator->destroy(iterator);
- return SUCCESS;
-}
-
-/**
- * Implementation of transaction_t.get_response.
- */
-static status_t get_response(private_delete_child_sa_t *this, message_t *request,
- message_t **result, transaction_t **next)
-{
- host_t *me, *other;
- message_t *response;
- iterator_t *payloads;
- payload_t *payload;
-
- /* check if we already have built a response (retransmission) */
- if (this->message)
- {
- *result = this->message;
- return SUCCESS;
- }
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
- this->message_id = request->get_message_id(request);
-
- /* set up response */
- response = message_create();
- response->set_source(response, me->clone(me));
- response->set_destination(response, other->clone(other));
- response->set_exchange_type(response, INFORMATIONAL);
- response->set_request(response, FALSE);
- response->set_message_id(response, this->message_id);
- response->set_ike_sa_id(response, this->ike_sa->get_id(this->ike_sa));
- this->message = response;
- *result = response;
-
- if (request->get_exchange_type(request) != INFORMATIONAL)
- {
- DBG1(DBG_IKE, "INFORMATIONAL response of invalid type, aborting");
- return FAILED;
- }
-
- /* we can't handle a delete for a CHILD when we are rekeying. There
- * is no proper solution for this. We send a empty informational response,
- * as described in ikev2-clarifications draft */
- if (this->ike_sa->get_state(this->ike_sa) == IKE_REKEYING ||
- this->ike_sa->get_state(this->ike_sa) == IKE_DELETING)
- {
- DBG1(DBG_IKE, "unable to delete CHILD_SA, as rekeying in progress");
- return FAILED;
- }
-
- /* iterate over all payloads */
- payloads = request->get_payload_iterator(request);
- while (payloads->iterate(payloads, (void**)&payload))
- {
- switch (payload->get_type(payload))
- {
- case DELETE:
- {
- process_delete(this, (delete_payload_t*)payload, response);
- break;
- }
- default:
- {
- DBG2(DBG_IKE, "ignoring payload %N",
- payload_type_names, payload->get_type(payload));
- break;
- }
- }
- }
- payloads->destroy(payloads);
- return SUCCESS;
-}
-
-/**
- * Implementation of transaction_t.conclude
- */
-static status_t conclude(private_delete_child_sa_t *this, message_t *response,
- transaction_t **transaction)
-{
- iterator_t *payloads;
- payload_t *payload;
-
- /* check message type */
- if (response->get_exchange_type(response) != INFORMATIONAL)
- {
- DBG1(DBG_IKE, "INFORMATIONAL response of invalid type, aborting");
- return FAILED;
- }
-
- /* iterate over all payloads */
- payloads = response->get_payload_iterator(response);
- while (payloads->iterate(payloads, (void**)&payload))
- {
- switch (payload->get_type(payload))
- {
- case DELETE:
- {
- process_delete(this, (delete_payload_t*)payload, NULL);
- break;
- }
- default:
- {
- DBG1(DBG_IKE, "ignoring payload %N",
- payload_type_names, payload->get_type(payload));
- break;
- }
- }
- }
- payloads->destroy(payloads);
- return SUCCESS;
-}
-
-/**
- * implements transaction_t.destroy
- */
-static void destroy(private_delete_child_sa_t *this)
-{
- DESTROY_IF(this->message);
- free(this);
-}
-
-/*
- * Described in header.
- */
-delete_child_sa_t *delete_child_sa_create(ike_sa_t *ike_sa)
-{
- private_delete_child_sa_t *this = malloc_thing(private_delete_child_sa_t);
-
- /* transaction interface functions */
- this->public.transaction.get_request = (status_t(*)(transaction_t*,message_t**))get_request;
- this->public.transaction.get_response = (status_t(*)(transaction_t*,message_t*,message_t**,transaction_t**))get_response;
- this->public.transaction.conclude = (status_t(*)(transaction_t*,message_t*,transaction_t**))conclude;
- this->public.transaction.get_message_id = (u_int32_t(*)(transaction_t*))get_message_id;
- this->public.transaction.requested = (u_int32_t(*)(transaction_t*))requested;
- this->public.transaction.destroy = (void(*)(transaction_t*))destroy;
-
- /* publics */
- this->public.set_child_sa = (void(*)(delete_child_sa_t*,child_sa_t*))set_child_sa;
-
- /* private data */
- this->ike_sa = ike_sa;
- this->message_id = 0;
- this->message = NULL;
- this->requested = 0;
-
- return &this->public;
-}
diff --git a/src/charon/sa/transactions/delete_child_sa.h b/src/charon/sa/transactions/delete_child_sa.h
deleted file mode 100644
index b4cd8ea7a..000000000
--- a/src/charon/sa/transactions/delete_child_sa.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/**
- * @file delete_child_sa.h
- *
- * @brief Interface of transaction delete_child_sa.
- *
- */
-
-/*
- * Copyright (C) 2006 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * 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.
- */
-
-
-#ifndef DELETE_CHILD_SA_H_
-#define DELETE_CHILD_SA_H_
-
-typedef struct delete_child_sa_t delete_child_sa_t;
-
-#include <sa/ike_sa.h>
-#include <sa/transactions/transaction.h>
-
-
-/**
- * @brief A transaction used to delete a CHILD_SA.
- *
- * @b Constructors:
- * - delete_child_sa_create()
- * - transaction_create() with the appropriate message
- *
- * @ingroup transactions
- */
-struct delete_child_sa_t {
-
- /**
- * The transaction_t interface.
- */
- transaction_t transaction;
-
- /**
- * @brief Set the CHILD_SA to delete.
- *
- * @param this calling object
- * @param child_sa CHILD_SA to rekey
- */
- void (*set_child_sa) (delete_child_sa_t* this, child_sa_t *child_sa);
-};
-
-/**
- * @brief Create a new transaction which deletes a CHILD_SA.
- *
- * @param ike_sa assigned IKE_SA
- * @return created delete_child_sa transaction
- *
- * @ingroup transactions
- */
-delete_child_sa_t *delete_child_sa_create(ike_sa_t *ike_sa);
-
-#endif /* DELETE_CHILD_SA_H_ */
diff --git a/src/charon/sa/transactions/delete_ike_sa.c b/src/charon/sa/transactions/delete_ike_sa.c
deleted file mode 100644
index 26817bb6a..000000000
--- a/src/charon/sa/transactions/delete_ike_sa.c
+++ /dev/null
@@ -1,284 +0,0 @@
-/**
- * @file delete_ike_sa.c
- *
- * @brief Implementation of the delete_ike_sa transaction.
- *
- */
-
-/*
- * Copyright (C) 2006 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * 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 "delete_ike_sa.h"
-
-#include <daemon.h>
-#include <encoding/payloads/delete_payload.h>
-
-typedef struct private_delete_ike_sa_t private_delete_ike_sa_t;
-
-/**
- * Private members of a delete_ike_sa_t object..
- */
-struct private_delete_ike_sa_t {
-
- /**
- * Public methods and transaction_t interface.
- */
- delete_ike_sa_t public;
-
- /**
- * Assigned IKE_SA.
- */
- ike_sa_t *ike_sa;
-
- /**
- * Message sent by our peer, if already generated
- */
- message_t *message;
-
- /**
- * Message ID this transaction uses
- */
- u_int32_t message_id;
-
- /**
- * Times we did send the request
- */
- u_int32_t requested;
-
- /**
- * is the IKE_SA redundant and gets deleted without further notification?
- */
- bool redundant;
-};
-
-/**
- * Implementation of transaction_t.get_message_id.
- */
-static u_int32_t get_message_id(private_delete_ike_sa_t *this)
-{
- return this->message_id;
-}
-
-/**
- * Implementation of transaction_t.requested.
- */
-static u_int32_t requested(private_delete_ike_sa_t *this)
-{
- return this->requested++;
-}
-
-/**
- * Implementation of transaction_t.get_request.
- */
-static status_t get_request(private_delete_ike_sa_t *this, message_t **result)
-{
- message_t *request;
- host_t *me, *other;
- delete_payload_t *delete_payload;
-
- /* check if we already have built a message (retransmission) */
- if (this->message)
- {
- *result = this->message;
- return SUCCESS;
- }
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
- this->redundant = this->ike_sa->get_state(this->ike_sa) == IKE_REKEYING;
-
- if (!this->redundant)
- {
- SIG(IKE_DOWN_START, "deleting IKE_SA");
- }
-
- /* build the request */
- request = message_create();
- request->set_source(request, me->clone(me));
- request->set_destination(request, other->clone(other));
- request->set_exchange_type(request, INFORMATIONAL);
- request->set_request(request, TRUE);
- this->message_id = this->ike_sa->get_next_message_id(this->ike_sa);
- request->set_message_id(request, this->message_id);
- request->set_ike_sa_id(request, this->ike_sa->get_id(this->ike_sa));
- /* apply for caller */
- *result = request;
- /* store for retransmission */
- this->message = request;
-
- delete_payload = delete_payload_create(PROTO_IKE);
- request->add_payload(request, (payload_t*)delete_payload);
-
- /* transit to state SA_DELETING */
- this->ike_sa->set_state(this->ike_sa, IKE_DELETING);
-
- return SUCCESS;
-}
-
-/**
- * Implementation of transaction_t.get_response.
- */
-static status_t get_response(private_delete_ike_sa_t *this, message_t *request,
- message_t **result, transaction_t **next)
-{
- host_t *me, *other;
- message_t *response;
- iterator_t *payloads;
- payload_t *payload;
- delete_payload_t *delete_request = NULL;
-
- /* check if we already have built a response (retransmission)
- * this only happens in special simultanous transaction cases,
- * as we delete the IKE_SA after the response is sent. */
- if (this->message)
- {
- *result = this->message;
- return SUCCESS;
- }
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
- this->message_id = request->get_message_id(request);
- this->redundant = this->ike_sa->get_state(this->ike_sa) == IKE_REKEYING;
-
- if (!this->redundant)
- {
- SIG(IKE_DOWN_START, "deleting IKE_SA");
- }
-
- /* set up response */
- response = message_create();
- response->set_source(response, me->clone(me));
- response->set_destination(response, other->clone(other));
- response->set_exchange_type(response, INFORMATIONAL);
- response->set_request(response, FALSE);
- response->set_message_id(response, this->message_id);
- response->set_ike_sa_id(response, this->ike_sa->get_id(this->ike_sa));
- this->message = response;
- *result = response;
-
- /* check message type */
- if (request->get_exchange_type(request) != INFORMATIONAL)
- {
- if (!this->redundant)
- {
- SIG(IKE_DOWN_FAILED, "INFORMATIONAL response of invalid type, deleting IKE_SA");
- }
- return DESTROY_ME;
- }
-
- /* iterate over all payloads */
- payloads = request->get_payload_iterator(request);
- while (payloads->iterate(payloads, (void**)&payload))
- {
- switch (payload->get_type(payload))
- {
- case DELETE:
- {
- delete_request = (delete_payload_t *)payload;
- break;
- }
- default:
- {
- DBG1(DBG_IKE, "ignoring payload %N",
- payload_type_names, payload->get_type(payload));
- break;
- }
- }
- }
- payloads->destroy(payloads);
-
- if (delete_request &&
- delete_request->get_protocol_id(delete_request) == PROTO_IKE)
- {
- DBG1(DBG_IKE, "DELETE request for IKE_SA received, deleting IKE_SA");
- }
- else
- {
- /* should not happen, as we preparsed this at transaction construction */
- DBG1(DBG_IKE, "received a weird DELETE request for IKE_SA, deleting anyway");
- }
- if (this->ike_sa->get_state(this->ike_sa) == IKE_DELETING)
- {
- /* if we are already deleting an IKE_SA, we do not destroy. We wait
- * until we get the response for our initiated delete. */
- return SUCCESS;
- }
- this->ike_sa->set_state(this->ike_sa, IKE_DELETING);
- if (!this->redundant)
- {
- SIG(IKE_DOWN_SUCCESS, "IKE_SA deleted on request");
- }
- return DESTROY_ME;
-}
-
-
-/**
- * Implementation of transaction_t.conclude
- */
-static status_t conclude(private_delete_ike_sa_t *this, message_t *response,
- transaction_t **transaction)
-{
- /* check message type */
- if (response->get_exchange_type(response) != INFORMATIONAL)
- {
- if (!this->redundant)
- {
- SIG(IKE_DOWN_FAILED, "INFORMATIONAL response of invalid type, deleting IKE_SA");
- }
- return DESTROY_ME;
- }
- /* this is only an acknowledge. We can't do anything here, but delete
- * the IKE_SA. */
- if (!this->redundant)
- {
- SIG(IKE_DOWN_SUCCESS, "IKE_SA deleted");
- }
- return DESTROY_ME;
-}
-
-/**
- * implements transaction_t.destroy
- */
-static void destroy(private_delete_ike_sa_t *this)
-{
- DESTROY_IF(this->message);
- free(this);
-}
-
-/*
- * Described in header.
- */
-delete_ike_sa_t *delete_ike_sa_create(ike_sa_t *ike_sa)
-{
- private_delete_ike_sa_t *this = malloc_thing(private_delete_ike_sa_t);
-
- /* transaction interface functions */
- this->public.transaction.get_request = (status_t(*)(transaction_t*,message_t**))get_request;
- this->public.transaction.get_response = (status_t(*)(transaction_t*,message_t*,message_t**,transaction_t**))get_response;
- this->public.transaction.conclude = (status_t(*)(transaction_t*,message_t*,transaction_t**))conclude;
- this->public.transaction.get_message_id = (u_int32_t(*)(transaction_t*))get_message_id;
- this->public.transaction.requested = (u_int32_t(*)(transaction_t*))requested;
- this->public.transaction.destroy = (void(*)(transaction_t*))destroy;
-
- /* private data */
- this->ike_sa = ike_sa;
- this->message_id = 0;
- this->message = NULL;
- this->requested = 0;
- this->redundant = FALSE;
-
- return &this->public;
-}
diff --git a/src/charon/sa/transactions/delete_ike_sa.h b/src/charon/sa/transactions/delete_ike_sa.h
deleted file mode 100644
index 139e65ebb..000000000
--- a/src/charon/sa/transactions/delete_ike_sa.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/**
- * @file delete_ike_sa.h
- *
- * @brief Interface of transaction delete_ike_sa.
- *
- */
-
-/*
- * Copyright (C) 2006 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * 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.
- */
-
-
-#ifndef DELETE_IKE_SA_H_
-#define DELETE_IKE_SA_H_
-
-typedef struct delete_ike_sa_t delete_ike_sa_t;
-
-#include <sa/ike_sa.h>
-#include <sa/transactions/transaction.h>
-
-/**
- * @brief A transaction used to delete the IKE_SA.
- *
- * Notation as follows:
- * Mx{D} means: Message, with message ID "x", containing a Delete payload
- *
- * The clarifcation Document says in 5.8, that a IKE_SA delete should not
- * be acknowledged with the same delete. This only makes sense for CHILD_SAs,
- * as they are paired. IKE_SAs are not, there is only one for both ends.
- *
- * Normal case:
- * ----------------
- * Mx{D} -->
- * <-- Mx{}
- * Delete request is sent, and we wait for the acknowledge.
- *
- * Special case 1:
- * ---------------
- * Mx{D} -->
- * <-- My{D}
- * My{} -->
- * <-- Mx{}
- * Both initate a delete at the same time. We ack the delete, but wait for
- * our delete to be acknowledged.
- *
- * @b Constructors:
- * - delete_ike_sa_create()
- * - transaction_create() with the appropriate message
- *
- * @ingroup transactions
- */
-struct delete_ike_sa_t {
-
- /**
- * The transaction_t interface.
- */
- transaction_t transaction;
-};
-
-/**
- * @brief Create a new transaction which deletes the IKE_SA.
- *
- * @param ike_sa assigned IKE_SA
- * @return created delete_ike_sa transaction
- *
- * @ingroup transactions
- */
-delete_ike_sa_t *delete_ike_sa_create(ike_sa_t *ike_sa);
-
-#endif /* DELETE_IKE_SA_H_ */
diff --git a/src/charon/sa/transactions/ike_auth.c b/src/charon/sa/transactions/ike_auth.c
deleted file mode 100644
index bf7fd6d12..000000000
--- a/src/charon/sa/transactions/ike_auth.c
+++ /dev/null
@@ -1,1562 +0,0 @@
-/**
- * @file ike_auth.c
- *
- * @brief Implementation of ike_auth_t transaction.
- *
- */
-
-/*
- * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger
- * Copyright (C) 2005-2006 Martin Willi
- * Copyright (C) 2005 Jan Hutter
- * Hochschule fuer Technik Rapperswil
- *
- * 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_auth.h"
-
-#include <string.h>
-
-#include <daemon.h>
-#include <encoding/payloads/sa_payload.h>
-#include <encoding/payloads/id_payload.h>
-#include <encoding/payloads/cert_payload.h>
-#include <encoding/payloads/certreq_payload.h>
-#include <encoding/payloads/auth_payload.h>
-#include <encoding/payloads/ts_payload.h>
-#include <sa/authenticators/authenticator.h>
-#include <sa/authenticators/eap_authenticator.h>
-#include <sa/child_sa.h>
-
-
-typedef struct private_ike_auth_t private_ike_auth_t;
-
-/**
- * Private members of a ike_auth_t object..
- */
-struct private_ike_auth_t {
-
- /**
- * Public methods and transaction_t interface.
- */
- ike_auth_t public;
-
- /**
- * Assigned IKE_SA.
- */
- ike_sa_t *ike_sa;
-
- /**
- * Message sent by our peer, if already generated
- */
- message_t *message;
-
- /**
- * Message ID this transaction uses
- */
- u_int32_t message_id;
-
- /**
- * Times we did send the request
- */
- u_int32_t requested;
-
- /**
- * initiator chosen nonce
- */
- chunk_t nonce_i;
-
- /**
- * responder chosen nonce
- */
- chunk_t nonce_r;
-
- /**
- * encoded request message of ike_sa_init transaction
- */
- chunk_t init_request;
-
- /**
- * encoded response message of ike_sa_init transaction
- */
- chunk_t init_response;
-
- /**
- * connection definition used for IKE_SA setup
- */
- connection_t *connection;
-
- /**
- * policy definition used CHILD_SA creation
- */
- policy_t *policy;
-
- /**
- * Negotiated proposal used for CHILD_SA
- */
- proposal_t *proposal;
-
- /**
- * Negotiated traffic selectors for initiator
- */
- linked_list_t *tsi;
-
- /**
- * Negotiated traffic selectors for responder
- */
- linked_list_t *tsr;
-
- /**
- * CHILD_SA created along with IKE_AUTH
- */
- child_sa_t *child_sa;
-
- /**
- * did other peer create a CHILD_SA?
- */
- bool build_child;
-
- /**
- * reqid to use for CHILD_SA setup
- */
- u_int32_t reqid;
-
- /**
- * List of CA certificates the other peer trusts
- */
- linked_list_t *cacerts;
-
- /**
- * EAP uses this authentication, which is passed along multiple ike_auths
- */
- eap_authenticator_t *eap_auth;
-
- /**
- * if the client receives a EAP request, it is stored here for later use
- */
- eap_payload_t *eap_next;
-
- /**
- * set to TRUE if authentication should be done with EAP only
- */
- bool eap_only;
-
- /**
- * has the other peer been authenticated yet?
- */
- bool peer_authenticated;
-
- /**
- * mode the CHILD_SA uses: tranport, tunnel, BEET
- */
- mode_t mode;
-};
-
-/**
- * Implementation of transaction_t.get_message_id.
- */
-static u_int32_t get_message_id(private_ike_auth_t *this)
-{
- return this->message_id;
-}
-
-/**
- * Implementation of transaction_t.requested.
- */
-static u_int32_t requested(private_ike_auth_t *this)
-{
- return this->requested++;
-}
-
-/**
- * Implementation of transaction_t.set_config.
- */
-static void set_config(private_ike_auth_t *this,
- connection_t *connection, policy_t *policy)
-{
- this->connection = connection;
- this->policy = policy;
-}
-
-/**
- * Implementation of transaction_t.set_reqid.
- */
-static void set_reqid(private_ike_auth_t *this, u_int32_t reqid)
-{
- this->reqid = reqid;
-}
-
-/**
- * Implementation of transaction_t.set_nonces.
- */
-static void set_nonces(private_ike_auth_t *this, chunk_t nonce_i, chunk_t nonce_r)
-{
- this->nonce_i = nonce_i;
- this->nonce_r = nonce_r;
-}
-
-/**
- * Implementation of transaction_t.set_init_messages.
- */
-static void set_init_messages(private_ike_auth_t *this, chunk_t init_request, chunk_t init_response)
-{
- this->init_request = init_request;
- this->init_response = init_response;
-}
-
-/**
- * Build a notify message.
- */
-static void build_notify(notify_type_t type, message_t *message, bool flush_message)
-{
- notify_payload_t *notify;
-
- if (flush_message)
- {
- payload_t *payload;
- iterator_t *iterator = message->get_payload_iterator(message);
- while (iterator->iterate(iterator, (void**)&payload))
- {
- payload->destroy(payload);
- iterator->remove(iterator);
- }
- iterator->destroy(iterator);
- }
-
- notify = notify_payload_create();
- notify->set_notify_type(notify, type);
- message->add_payload(message, (payload_t*)notify);
-}
-
-/**
- * Implementation of transaction_t.get_request.
- */
-static status_t get_request(private_ike_auth_t *this, message_t **result)
-{
- message_t *request;
- host_t *me, *other;
- identification_t *my_id, *other_id;
- id_payload_t *my_id_payload;
-
- /* check if we already have built a message (retransmission) */
- if (this->message)
- {
- *result = this->message;
- return SUCCESS;
- }
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
- my_id = this->policy->get_my_id(this->policy);
- other_id = this->policy->get_other_id(this->policy);
-
- /* build the request */
- request = message_create();
- request->set_source(request, me->clone(me));
- request->set_destination(request, other->clone(other));
- request->set_exchange_type(request, IKE_AUTH);
- request->set_request(request, TRUE);
- request->set_ike_sa_id(request, this->ike_sa->get_id(this->ike_sa));
- /* apply for caller */
- *result = request;
- /* store for retransmission */
- this->message = request;
-
- this->message_id = this->ike_sa->get_next_message_id(this->ike_sa);
- request->set_message_id(request, this->message_id);
-
- if (this->eap_auth)
- {
- /* we already sent ID, SA, TS in an earlier ike_auth, we now
- * continiue EAP processing */
- if (this->eap_next)
- {
- /* if we have another outstanding EAP response, send it */
- request->add_payload(request, (payload_t*)this->eap_next);
- this->eap_next = NULL;
- return SUCCESS;
- }
- else
- {
- /* if not, we have received an EAP_SUCCESS, send AUTH payload.
- * we only send our data if:
- * a) The peer has been authenticated using RSA/PSK, or
- * b) The EAP method is mutual and gives us enough security
- */
- if (this->eap_auth->is_mutual(this->eap_auth) ||
- this->peer_authenticated)
- {
- auth_payload_t *auth_payload;
- authenticator_t *authenticator = (authenticator_t*)this->eap_auth;
-
- if (authenticator->build(authenticator, this->init_request,
- this->nonce_r, &auth_payload) != SUCCESS)
- {
- SIG(IKE_UP_FAILED,
- "EAP authentication data generation failed, deleting IKE_SA");
- SIG(CHILD_UP_FAILED,
- "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
- request->add_payload(request, (payload_t*)auth_payload);
- return SUCCESS;
- }
- else
- {
- SIG(IKE_UP_FAILED,
- "peer didn't send authentication data, deleting IKE_SA");
- SIG(CHILD_UP_FAILED,
- "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
- }
- }
- /* otherwise we do a normal ike_auth request... */
-
- { /* build ID payload */
- my_id_payload = id_payload_create_from_identification(TRUE, my_id);
- request->add_payload(request, (payload_t*)my_id_payload);
- }
-
- /* build certificate request payload */
- if (this->connection->get_certreq_policy(this->connection) != CERT_NEVER_SEND)
- {
- certreq_payload_t *certreq_payload;
- identification_t *other_ca = this->policy->get_other_ca(this->policy);
-
- if (other_ca)
- {
- if (other_ca->get_type(other_ca) == ID_ANY)
- {
- certreq_payload = certreq_payload_create_from_cacerts();
- }
- else
- {
- certreq_payload = certreq_payload_create_from_cacert(other_ca);
- }
- if (certreq_payload != NULL)
- {
- request->add_payload(request, (payload_t*)certreq_payload);
- }
- }
- }
-
- /* build certificate payload. TODO: Handle certreq from init_ike_sa. */
- if (this->policy->get_auth_method(this->policy) == AUTH_RSA &&
- this->connection->get_cert_policy(this->connection) != CERT_NEVER_SEND)
- {
- cert_payload_t *cert_payload;
-
- x509_t *cert = charon->credentials->get_certificate(charon->credentials, my_id);
-
- if (cert)
- {
- cert_payload = cert_payload_create_from_x509(cert);
- request->add_payload(request, (payload_t*)cert_payload);
- }
- else
- {
- DBG1(DBG_IKE, "could not find my certificate, certificate payload omitted");
- }
- }
-
- { /* build IDr payload, if other_id defined */
- id_payload_t *id_payload;
- if (!other_id->contains_wildcards(other_id))
- {
- id_payload = id_payload_create_from_identification(FALSE, other_id);
- request->add_payload(request, (payload_t*)id_payload);
- }
- }
-
- if (this->policy->get_auth_method(this->policy) != AUTH_EAP)
- { /* build auth payload */
- authenticator_t *authenticator;
- auth_payload_t *auth_payload;
- auth_method_t auth_method;
- status_t status;
-
- auth_method = this->policy->get_auth_method(this->policy);
- authenticator = authenticator_create(this->ike_sa, auth_method);
- if (authenticator == NULL)
- {
- SIG(IKE_UP_FAILED, "auth method %N not supported, deleting IKE_SA",
- auth_method_names, auth_method);
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
- status = authenticator->build(authenticator, this->init_request,
- this->nonce_r, &auth_payload);
- authenticator->destroy(authenticator);
- if (status != SUCCESS)
- {
- SIG(IKE_UP_FAILED, "could not generate AUTH data, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
- request->add_payload(request, (payload_t*)auth_payload);
- }
- else
- {
- this->eap_auth = eap_authenticator_create(this->ike_sa);
- /* include notify that we support EAP only authentication */
- build_notify(EAP_ONLY_AUTHENTICATION, request, FALSE);
- }
-
- { /* build SA payload for CHILD_SA */
- linked_list_t *proposal_list;
- sa_payload_t *sa_payload;
- u_int32_t soft_lifetime, hard_lifetime;
- bool enable_natt;
-
- proposal_list = this->policy->get_proposals(this->policy);
- soft_lifetime = this->policy->get_soft_lifetime(this->policy);
- hard_lifetime = this->policy->get_hard_lifetime(this->policy);
- enable_natt = this->ike_sa->is_natt_enabled(this->ike_sa);
- this->child_sa = child_sa_create(this->reqid, me, other, my_id, other_id,
- soft_lifetime, hard_lifetime,
- this->policy->get_updown(this->policy),
- this->policy->get_hostaccess(this->policy),
- enable_natt);
- this->child_sa->set_name(this->child_sa, this->policy->get_name(this->policy));
- if (this->child_sa->alloc(this->child_sa, proposal_list) != SUCCESS)
- {
- SIG(IKE_UP_FAILED, "could not install CHILD_SA, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
- sa_payload = sa_payload_create_from_proposal_list(proposal_list);
- proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
- request->add_payload(request, (payload_t*)sa_payload);
- }
-
- /* notify for transport/BEET mode, we propose it
- * independent of the traffic selectors */
- switch (this->policy->get_mode(this->policy))
- {
- case MODE_TUNNEL:
- /* is the default */
- break;
- case MODE_TRANSPORT:
- if (this->ike_sa->is_natt_enabled(this->ike_sa))
- {
- DBG1(DBG_IKE, "not using tranport mode, as connection NATed");
- }
- else
- {
- build_notify(USE_TRANSPORT_MODE, request, FALSE);
- }
- break;
- case MODE_BEET:
- build_notify(USE_BEET_MODE, request, FALSE);
- break;
- }
-
- { /* build TSi payload */
- linked_list_t *ts_list;
- ts_payload_t *ts_payload;
-
- ts_list = this->policy->get_my_traffic_selectors(this->policy, me);
- ts_payload = ts_payload_create_from_traffic_selectors(TRUE, ts_list);
- ts_list->destroy_offset(ts_list, offsetof(traffic_selector_t, destroy));
-
- request->add_payload(request, (payload_t*)ts_payload);
- }
-
- { /* build TSr payload */
- linked_list_t *ts_list;
- ts_payload_t *ts_payload;
-
- ts_list = this->policy->get_other_traffic_selectors(this->policy, other);
- ts_payload = ts_payload_create_from_traffic_selectors(FALSE, ts_list);
- ts_list->destroy_offset(ts_list, offsetof(traffic_selector_t, destroy));
-
- request->add_payload(request, (payload_t*)ts_payload);
- }
-
- return SUCCESS;
-}
-
-/**
- * Handle all kind of notifies
- */
-static status_t process_notifies(private_ike_auth_t *this, notify_payload_t *notify_payload)
-{
- notify_type_t notify_type = notify_payload->get_notify_type(notify_payload);
-
- DBG2(DBG_IKE, "process notify type %N", notify_type_names, notify_type);
-
- switch (notify_type)
- {
- /* these notifies are not critical. no child_sa is built, but IKE stays alive */
- case SINGLE_PAIR_REQUIRED:
- {
- SIG(CHILD_UP_FAILED, "received a SINGLE_PAIR_REQUIRED notify");
- this->build_child = FALSE;
- return SUCCESS;
- }
- case TS_UNACCEPTABLE:
- {
- SIG(CHILD_UP_FAILED, "received TS_UNACCEPTABLE notify");
- this->build_child = FALSE;
- return SUCCESS;
- }
- case NO_PROPOSAL_CHOSEN:
- {
- SIG(CHILD_UP_FAILED, "received NO_PROPOSAL_CHOSEN notify");
- this->build_child = FALSE;
- return SUCCESS;
- }
- case EAP_ONLY_AUTHENTICATION:
- {
- DBG1(DBG_IKE, "peer requested EAP_ONLY_AUTHENTICATION");
- this->eap_only = TRUE;
- return SUCCESS;
- }
- case USE_TRANSPORT_MODE:
- {
- this->mode = MODE_TRANSPORT;
- return SUCCESS;
- }
- case USE_BEET_MODE:
- {
- this->mode = MODE_BEET;
- return SUCCESS;
- }
- default:
- {
- if (notify_type < 16383)
- {
- SIG(IKE_UP_FAILED, "received %N notify error, deleting IKE_SA",
- notify_type_names, notify_type);
- return DESTROY_ME;
- }
- else
- {
- DBG1(DBG_IKE, "received %N notify, ignored",
- notify_type_names, notify_type);
- return SUCCESS;
- }
- }
- }
-}
-
-/**
- * Import certificate requests from a certreq payload
- */
-static void process_certificate_request(private_ike_auth_t *this,
- certreq_payload_t *certreq_payload)
-{
- chunk_t keyids;
- cert_encoding_t encoding = certreq_payload->get_cert_encoding(certreq_payload);
-
- if (encoding != CERT_X509_SIGNATURE)
- {
- DBG1(DBG_IKE, "certreq payload %N not supported, ignored",
- cert_encoding_names, encoding);
- return;
- }
-
- keyids = certreq_payload->get_data(certreq_payload);
- while (keyids.len >= HASH_SIZE_SHA1)
- {
- chunk_t keyid = { keyids.ptr, HASH_SIZE_SHA1};
- x509_t *cacert = charon->credentials->get_ca_certificate_by_keyid(
- charon->credentials, keyid);
- if (cacert)
- {
- DBG2(DBG_IKE, "request for certificate issued by ca '%D'",
- cacert->get_subject(cacert));
- this->cacerts->insert_last(this->cacerts, cacert);
- }
- else
- {
- DBG2(DBG_IKE, "request for certificate issued by unknown ca");
- }
- DBG2(DBG_IKE, " with keyid %#B", &keyid);
-
- keyids.ptr += HASH_SIZE_SHA1;
- keyids.len -= HASH_SIZE_SHA1;
- }
-}
-
-/**
- * Import a certificate from a cert payload
- */
-static void import_certificate(cert_payload_t *cert_payload)
-{
- bool found;
- x509_t *cert;
- cert_encoding_t encoding;
-
- encoding = cert_payload->get_cert_encoding(cert_payload);
- if (encoding != CERT_X509_SIGNATURE)
- {
- DBG1(DBG_IKE, "certificate payload %N not supported, ignored",
- cert_encoding_names, encoding);
- return;
- }
- cert = x509_create_from_chunk(cert_payload->get_data_clone(cert_payload));
- if (cert)
- {
- if (charon->credentials->verify(charon->credentials, cert, &found))
- {
- DBG2(DBG_IKE, "received end entity certificate is trusted, added to store");
- if (!found)
- {
- charon->credentials->add_end_certificate(charon->credentials, cert);
- }
- else
- {
- cert->destroy(cert);
- }
- }
- else
- {
- DBG1(DBG_IKE, "received end entity certificate is not trusted, discarded");
- cert->destroy(cert);
- }
- }
- else
- {
- DBG1(DBG_IKE, "parsing of received certificate failed, discarded");
- }
-}
-
-/**
- * Check a list of traffic selectors if any selector belongs to host
- */
-static bool ts_list_is_host(linked_list_t *list, host_t *host)
-{
- traffic_selector_t *ts;
- bool is_host = TRUE;
- iterator_t *iterator = list->create_iterator(list, TRUE);
-
- while (is_host && iterator->iterate(iterator, (void**)&ts))
- {
- is_host = is_host && ts->is_host(ts, host);
- }
- iterator->destroy(iterator);
- return is_host;
-}
-
-/**
- * Install a CHILD_SA for usage
- */
-static status_t install_child_sa(private_ike_auth_t *this, bool initiator)
-{
- prf_plus_t *prf_plus;
- chunk_t seed;
- status_t status;
-
- seed = chunk_alloc(this->nonce_i.len + this->nonce_r.len);
- memcpy(seed.ptr, this->nonce_i.ptr, this->nonce_i.len);
- memcpy(seed.ptr + this->nonce_i.len, this->nonce_r.ptr, this->nonce_r.len);
- prf_plus = prf_plus_create(this->ike_sa->get_child_prf(this->ike_sa), seed);
- chunk_free(&seed);
-
- if (initiator)
- {
- status = this->child_sa->update(this->child_sa, this->proposal,
- this->mode, prf_plus);
- }
- else
- {
- status = this->child_sa->add(this->child_sa, this->proposal,
- this->mode, prf_plus);
- }
- prf_plus->destroy(prf_plus);
- if (status != SUCCESS)
- {
- return DESTROY_ME;
- }
- if (initiator)
- {
- status = this->child_sa->add_policies(this->child_sa, this->tsi,
- this->tsr, this->mode);
- }
- else
- {
- status = this->child_sa->add_policies(this->child_sa, this->tsr,
- this->tsi, this->mode);
- }
- if (status != SUCCESS)
- {
- return DESTROY_ME;
- }
-
- /* add to IKE_SA, and remove from transaction */
- this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
- this->ike_sa->add_child_sa(this->ike_sa, this->child_sa);
- this->child_sa = NULL;
- return SUCCESS;
-}
-
-/**
- * create a CHILD SA, install it, and build response message
- */
-static void setup_child_sa(private_ike_auth_t *this, message_t *response)
-{
- bool use_natt;
- u_int32_t soft_lifetime, hard_lifetime;
- host_t *me, *other;
- identification_t *my_id, *other_id;
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
- my_id = this->ike_sa->get_my_id(this->ike_sa);
- other_id = this->ike_sa->get_other_id(this->ike_sa);
-
- soft_lifetime = this->policy->get_soft_lifetime(this->policy);
- hard_lifetime = this->policy->get_hard_lifetime(this->policy);
- use_natt = this->ike_sa->is_natt_enabled(this->ike_sa);
- this->child_sa = child_sa_create(this->reqid, me, other, my_id, other_id,
- soft_lifetime, hard_lifetime,
- this->policy->get_updown(this->policy),
- this->policy->get_hostaccess(this->policy),
- use_natt);
- this->child_sa->set_name(this->child_sa, this->policy->get_name(this->policy));
- /* check mode, and include notify into reply */
- switch (this->mode)
- {
- case MODE_TUNNEL:
- /* is the default */
- break;
- case MODE_TRANSPORT:
- if (!ts_list_is_host(this->tsi, other) ||
- !ts_list_is_host(this->tsr, me))
- {
- this->mode = MODE_TUNNEL;
- DBG1(DBG_IKE, "not using tranport mode, not host-to-host");
- }
- else if (this->ike_sa->is_natt_enabled(this->ike_sa))
- {
- this->mode = MODE_TUNNEL;
- DBG1(DBG_IKE, "not using tranport mode, as connection NATed");
- }
- else
- {
- build_notify(USE_TRANSPORT_MODE, response, FALSE);
- }
- break;
- case MODE_BEET:
- if (!ts_list_is_host(this->tsi, NULL) ||
- !ts_list_is_host(this->tsr, NULL))
- {
- this->mode = MODE_TUNNEL;
- DBG1(DBG_IKE, "not using BEET mode, not host-to-host");
- }
- else
- {
- build_notify(USE_BEET_MODE, response, FALSE);
- }
- break;
- }
-
- if (install_child_sa(this, FALSE) != SUCCESS)
- {
- SIG(CHILD_UP_FAILED, "installing CHILD_SA %s failed, no CHILD_SA created",
- this->policy->get_name(this->policy));
- DBG1(DBG_IKE, "adding NO_PROPOSAL_CHOSEN notify to response");
- build_notify(NO_PROPOSAL_CHOSEN, response, FALSE);
- }
- else
- {
- /* build SA and TS payloads */
- SIG(CHILD_UP_SUCCESS, "CHILD_SA created");
- ts_payload_t *ts_response;
- sa_payload_t *sa_response = sa_payload_create();
- sa_response->add_proposal(sa_response, this->proposal);
- response->add_payload(response, (payload_t*)sa_response);
- ts_response = ts_payload_create_from_traffic_selectors(TRUE, this->tsi);
- response->add_payload(response, (payload_t*)ts_response);
- ts_response = ts_payload_create_from_traffic_selectors(FALSE, this->tsr);
- response->add_payload(response, (payload_t*)ts_response);
- }
-}
-
-/**
- * clone the transaction to requeue it for EAP handling
- */
-static private_ike_auth_t *clone_for_eap(private_ike_auth_t *this)
-{
- private_ike_auth_t *clone;
- clone = (private_ike_auth_t*)ike_auth_create(this->ike_sa);
-
- clone->tsi = this->tsi; this->tsi = NULL;
- clone->tsr = this->tsr; this->tsr = NULL;
- clone->eap_auth = this->eap_auth; this->eap_auth = NULL;
- clone->eap_next = this->eap_next; this->eap_next = NULL;
- clone->build_child = this->build_child;
- clone->nonce_i = this->nonce_i; this->nonce_i = chunk_empty;
- clone->nonce_r = this->nonce_r; this->nonce_r = chunk_empty;
- clone->child_sa = this->child_sa; this->child_sa = NULL;
- clone->proposal = this->proposal; this->proposal = NULL;
- clone->peer_authenticated = this->peer_authenticated;
- clone->connection = this->connection; this->connection = NULL;
- clone->policy = this->policy; this->policy = NULL;
-
- return clone;
-}
-
-/**
- * Implementation of transaction_t.get_response.
- */
-static status_t get_response(private_ike_auth_t *this, message_t *request,
- message_t **result, transaction_t **next)
-{
- host_t *me, *other;
- identification_t *my_id, *other_id;
- message_t *response;
- status_t status;
- iterator_t *payloads;
- payload_t *payload;
- id_payload_t *idi_request = NULL;
- id_payload_t *idr_request = NULL;
- auth_payload_t *auth_request = NULL;
- certreq_payload_t *certreq_request = NULL;
- cert_payload_t *cert_request = NULL;
- sa_payload_t *sa_request = NULL;
- ts_payload_t *tsi_request = NULL;
- ts_payload_t *tsr_request = NULL;
- eap_payload_t *eap_request = NULL;
- id_payload_t *idr_response;
-
- /* check if we already have built a response (retransmission) */
- if (this->message)
- {
- *result = this->message;
- return SUCCESS;
- }
-
- SIG(CHILD_UP_START, "setting up CHILD_SA along with IKE_AUTH");
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
- my_id = this->ike_sa->get_my_id(this->ike_sa);
- other_id = this->ike_sa->get_other_id(this->ike_sa);
- this->message_id = request->get_message_id(request);
-
- /* set up response */
- response = message_create();
- response->set_source(response, me->clone(me));
- response->set_destination(response, other->clone(other));
- response->set_exchange_type(response, IKE_AUTH);
- response->set_request(response, FALSE);
- response->set_message_id(response, this->message_id);
- response->set_ike_sa_id(response, this->ike_sa->get_id(this->ike_sa));
- this->message = response;
- *result = response;
-
- /* check message type */
- if (request->get_exchange_type(request) != IKE_AUTH)
- {
- SIG(IKE_UP_FAILED, "IKE_AUTH response of invalid type, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
-
- /* Iterate over all payloads. */
- payloads = request->get_payload_iterator(request);
- while (payloads->iterate(payloads, (void**)&payload))
- {
- switch (payload->get_type(payload))
- {
- case ID_INITIATOR:
- idi_request = (id_payload_t*)payload;
- break;
- case ID_RESPONDER:
- idr_request = (id_payload_t*)payload;
- break;
- case AUTHENTICATION:
- auth_request = (auth_payload_t*)payload;
- break;
- case CERTIFICATE_REQUEST:
- certreq_request = (certreq_payload_t*)payload;
- process_certificate_request(this, certreq_request);
- break;
- case CERTIFICATE:
- cert_request = (cert_payload_t*)payload;
- break;
- case SECURITY_ASSOCIATION:
- sa_request = (sa_payload_t*)payload;
- break;
- case TRAFFIC_SELECTOR_INITIATOR:
- tsi_request = (ts_payload_t*)payload;
- break;
- case TRAFFIC_SELECTOR_RESPONDER:
- tsr_request = (ts_payload_t*)payload;
- break;
- case EXTENSIBLE_AUTHENTICATION:
- eap_request = (eap_payload_t*)payload;
- break;
- case NOTIFY:
- {
- status = process_notifies(this, (notify_payload_t*)payload);
- if (status == FAILED)
- {
- payloads->destroy(payloads);
- /* we return SUCCESS, returned FAILED means do next transaction */
- return SUCCESS;
- }
- if (status == DESTROY_ME)
- {
- payloads->destroy(payloads);
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
- break;
- }
- default:
- {
- DBG1(DBG_IKE, "ignoring %N payload",
- payload_type_names, payload->get_type(payload));
- break;
- }
- }
- }
- payloads->destroy(payloads);
-
- /* if message contains an EAP payload, we process it */
- if (eap_request && this->eap_auth)
- {
- eap_payload_t *eap_response;
- private_ike_auth_t *next_auth;
-
- status = this->eap_auth->process(this->eap_auth, eap_request, &eap_response);
- response->add_payload(response, (payload_t*)eap_response);
-
- if (status == FAILED)
- {
- /* shut down if EAP message is EAP_FAILURE */
- return DESTROY_ME;
- }
-
- next_auth = clone_for_eap(this);
- next_auth->message_id = this->message_id + 1;
- *next = (transaction_t*)next_auth;
- return SUCCESS;
- }
-
- /* if we do EAP authentication and a AUTH payload comes in, verify it */
- if (auth_request && this->eap_auth)
- {
- auth_payload_t *auth_response;
- authenticator_t *authenticator = (authenticator_t*)this->eap_auth;
-
- if (authenticator->verify(authenticator, this->init_request,
- this->nonce_r, auth_request) != SUCCESS)
- {
- SIG(IKE_UP_FAILED, "authentication with EAP failed, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- build_notify(AUTHENTICATION_FAILED, response, TRUE);
- return DESTROY_ME;
- }
-
- if (authenticator->build(authenticator, this->init_response,
- this->nonce_i, &auth_response) != SUCCESS)
- {
- SIG(IKE_UP_FAILED, "EAP authentication data generation failed, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- build_notify(AUTHENTICATION_FAILED, response, TRUE);
- return DESTROY_ME;
- }
- response->add_payload(response, (payload_t*)auth_response);
-
- SIG(IKE_UP_SUCCESS, "IKE_SA '%s' established between %H[%D]...%H[%D]",
- this->ike_sa->get_name(this->ike_sa), me, my_id, other, other_id);
- this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
-
- setup_child_sa(this, response);
-
- return SUCCESS;
- }
-
- /* check if we have all payloads (AUTH is not checked, not required with EAP) */
- if (!(idi_request && sa_request && tsi_request && tsr_request))
- {
- build_notify(INVALID_SYNTAX, response, TRUE);
- SIG(IKE_UP_FAILED, "request message incomplete, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
-
- { /* process ID payload */
- other_id = idi_request->get_identification(idi_request);
- if (idr_request)
- {
- my_id = idr_request->get_identification(idr_request);
- }
- else
- {
- my_id = identification_create_from_encoding(ID_ANY, chunk_empty);
- }
- }
-
- { /* get a policy and process traffic selectors */
- linked_list_t *my_ts, *other_ts;
-
- my_ts = tsr_request->get_traffic_selectors(tsr_request);
- other_ts = tsi_request->get_traffic_selectors(tsi_request);
-
- this->policy = charon->policies->get_policy(charon->policies,
- my_id, other_id,
- my_ts, other_ts,
- me, other);
-
- if (this->policy)
- {
- this->tsr = this->policy->select_my_traffic_selectors(this->policy, my_ts, me);
- this->tsi = this->policy->select_other_traffic_selectors(this->policy, other_ts, other);
- }
- my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy));
- other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy));
-
- /* TODO: We should check somehow if we have a policy, but with other
- * traffic selectors. Then we would create a IKE_SA without a CHILD_SA. */
- if (this->policy == NULL)
- {
- SIG(IKE_UP_FAILED, "no acceptable policy for IDs %D - %D found, "
- "deleting IKE_SA", my_id, other_id);
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- my_id->destroy(my_id);
- other_id->destroy(other_id);
- build_notify(AUTHENTICATION_FAILED, response, TRUE);
- return DESTROY_ME;
- }
- my_id->destroy(my_id);
-
- /* get my id from policy, which must contain a fully qualified valid id */
- my_id = this->policy->get_my_id(this->policy);
- this->ike_sa->set_my_id(this->ike_sa, my_id->clone(my_id));
- this->ike_sa->set_other_id(this->ike_sa, other_id);
-
- idr_response = id_payload_create_from_identification(FALSE, my_id);
- response->add_payload(response, (payload_t*)idr_response);
- }
-
- if (this->policy->get_auth_method(this->policy) == AUTH_RSA &&
- this->connection->get_cert_policy(this->connection) != CERT_NEVER_SEND)
- { /* build certificate payload */
- x509_t *cert;
- cert_payload_t *cert_payload;
-
- cert = charon->credentials->get_certificate(charon->credentials, my_id);
- if (cert)
- {
- cert_payload = cert_payload_create_from_x509(cert);
- response->add_payload(response, (payload_t *)cert_payload);
- }
- else
- {
- DBG1(DBG_IKE, "could not find my certificate, cert payload omitted");
- }
- }
-
- if (cert_request)
- { /* process certificate payload */
- import_certificate(cert_request);
- }
-
- { /* process SA payload */
- linked_list_t *proposal_list;
-
- /* get proposals from request, and select one with ours */
- proposal_list = sa_request->get_proposals(sa_request);
- DBG2(DBG_IKE, "selecting proposals:");
- this->proposal = this->policy->select_proposal(this->policy, proposal_list);
- proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
-
- /* do we have a proposal? */
- if (this->proposal == NULL)
- {
- SIG(CHILD_UP_FAILED, "CHILD_SA proposals unacceptable, no CHILD_SA created");
- DBG1(DBG_IKE, "adding NO_PROPOSAL_CHOSEN notify to response");
- build_notify(NO_PROPOSAL_CHOSEN, response, FALSE);
- this->build_child = FALSE;
- }
- /* do we have traffic selectors? */
- else if (this->tsi->get_count(this->tsi) == 0 || this->tsr->get_count(this->tsr) == 0)
- {
- SIG(CHILD_UP_FAILED, "CHILD_SA traffic selectors unacceptable, no CHILD_SA created");
- DBG1(DBG_IKE, "adding TS_UNACCEPTABLE notify to response");
- build_notify(TS_UNACCEPTABLE, response, FALSE);
- this->build_child = FALSE;
- }
- }
-
- if (!this->eap_only || this->policy->get_auth_method(this->policy) != AUTH_EAP)
- { /* build response AUTH payload when not using a mutual EAP authentication */
- auth_payload_t *auth_response;
- authenticator_t *authenticator;
- auth_method_t auth_method;
- status_t status;
-
- auth_method = this->policy->get_auth_method(this->policy);
- if (auth_method == AUTH_EAP)
- {
- SIG(IKE_UP_FAILED,
- "peer does not support EAP only authentication, deleting IKE_SA");
- SIG(CHILD_UP_FAILED,
- "initiating CHILD_SA failed, unable to create IKE_SA");
- build_notify(AUTHENTICATION_FAILED, response, TRUE);
- return DESTROY_ME;
- }
-
- authenticator = authenticator_create(this->ike_sa, auth_method);
- if (authenticator == NULL)
- {
- SIG(IKE_UP_FAILED, "auth method %N not supported, deleting IKE_SA",
- auth_method_names, auth_method);
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- build_notify(AUTHENTICATION_FAILED, response, TRUE);
- return DESTROY_ME;
- }
- status = authenticator->build(authenticator, this->init_response,
- this->nonce_i, &auth_response);
- authenticator->destroy(authenticator);
- if (status != SUCCESS)
- {
- SIG(IKE_UP_FAILED, "authentication data generation failed, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- build_notify(AUTHENTICATION_FAILED, response, TRUE);
- return DESTROY_ME;
- }
- response->add_payload(response, (payload_t*)auth_response);
- }
-
- if (auth_request)
- { /* process auth payload, if not using EAP */
- authenticator_t *authenticator;
- auth_method_t auth_method;
- status_t status;
-
- auth_method = auth_request->get_auth_method(auth_request);
- authenticator = authenticator_create(this->ike_sa, auth_method);
- if (authenticator == NULL)
- {
- SIG(IKE_UP_FAILED, "auth method %N not supported, deleting IKE_SA",
- auth_method_names, auth_method);
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
- status = authenticator->verify(authenticator, this->init_request,
- this->nonce_r, auth_request);
- authenticator->destroy(authenticator);
- if (status != SUCCESS)
- {
- SIG(IKE_UP_FAILED, "authentication failed, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- build_notify(AUTHENTICATION_FAILED, response, TRUE);
- return DESTROY_ME;
- }
-
- SIG(IKE_UP_SUCCESS, "IKE_SA '%s' established between %H[%D]...%H[%D]",
- this->ike_sa->get_name(this->ike_sa), me, my_id, other, other_id);
- this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
-
- /* set up CHILD_SA if negotiation succeded */
- if (this->build_child)
- {
- setup_child_sa(this, response);
- }
-
- this->peer_authenticated = TRUE;
- }
- else
- {
- /* if no AUTH payload was included, we start with an EAP exchange.
- * eap_response is a request in the EAP meaning, but is
- * contained in a IKEv2 response */
- eap_payload_t *eap_response;
- private_ike_auth_t *next_auth;
- eap_type_t eap_type;
-
- eap_type = this->policy->get_eap_type(this->policy);
- this->eap_auth = eap_authenticator_create(this->ike_sa);
- status = this->eap_auth->initiate(this->eap_auth, eap_type, &eap_response);
- response->add_payload(response, (payload_t*)eap_response);
-
- if (status == FAILED)
- {
- /* EAP initiaton failed, we send the EAP_FAILURE message and quit */
- return DESTROY_ME;
- }
- /* we send an EAP request. to handle the reply, we reschedule
- * this transaction, as it knows how to handle the reply */
- next_auth = clone_for_eap(this);
- next_auth->message_id = this->message_id + 1;
- *next = (transaction_t*)next_auth;
- }
- return SUCCESS;
-}
-
-/**
- * Implementation of transaction_t.conclude
- */
-static status_t conclude(private_ike_auth_t *this, message_t *response,
- transaction_t **next)
-{
- iterator_t *payloads;
- payload_t *payload;
- host_t *me, *other;
- identification_t *other_id, *my_id;
- ts_payload_t *tsi_payload = NULL;
- ts_payload_t *tsr_payload = NULL;
- id_payload_t *idr_payload = NULL;
- cert_payload_t *cert_payload = NULL;
- auth_payload_t *auth_payload = NULL;
- sa_payload_t *sa_payload = NULL;
- eap_payload_t *eap_payload = NULL;
- status_t status;
-
- /* check message type */
- if (response->get_exchange_type(response) != IKE_AUTH)
- {
- SIG(IKE_UP_FAILED, "IKE_AUTH response of invalid type, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
- my_id = this->ike_sa->get_my_id(this->ike_sa);
- other_id = this->ike_sa->get_other_id(this->ike_sa);
-
- /* Iterate over all payloads to collect them */
- payloads = response->get_payload_iterator(response);
- while (payloads->iterate(payloads, (void**)&payload))
- {
- switch (payload->get_type(payload))
- {
- case ID_RESPONDER:
- idr_payload = (id_payload_t*)payload;
- break;
- case AUTHENTICATION:
- auth_payload = (auth_payload_t*)payload;
- break;
- case CERTIFICATE:
- cert_payload = (cert_payload_t*)payload;
- break;
- case SECURITY_ASSOCIATION:
- sa_payload = (sa_payload_t*)payload;
- break;
- case TRAFFIC_SELECTOR_INITIATOR:
- tsi_payload = (ts_payload_t*)payload;
- break;
- case TRAFFIC_SELECTOR_RESPONDER:
- tsr_payload = (ts_payload_t*)payload;
- break;
- case EXTENSIBLE_AUTHENTICATION:
- eap_payload = (eap_payload_t*)payload;
- break;
- case NOTIFY:
- {
- status = process_notifies(this, (notify_payload_t*)payload);
- if (status == FAILED)
- {
- payloads->destroy(payloads);
- /* we return SUCCESS, as transaction completet */
- return SUCCESS;
- }
- if (status == DESTROY_ME)
- {
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- payloads->destroy(payloads);
- return status;
- }
- break;
- }
- default:
- {
- DBG1(DBG_IKE, "ignoring payload %N",
- payload_type_names, payload->get_type(payload));
- break;
- }
- }
- }
- payloads->destroy(payloads);
-
- if (idr_payload)
- { /* process idr payload */
- identification_t *configured_other_id;
- int wildcards;
-
- other_id = idr_payload->get_identification(idr_payload);
- configured_other_id = this->policy->get_other_id(this->policy);
-
- if (!other_id->matches(other_id, configured_other_id, &wildcards))
- {
- other_id->destroy(other_id);
- SIG(IKE_UP_FAILED, "other peer uses unacceptable ID (%D, excepted "
- "%D), deleting IKE_SA", other_id, configured_other_id);
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
- /* update other ID. It was already set, but may contain wildcards */
- this->ike_sa->set_other_id(this->ike_sa, other_id);
- }
-
- if (cert_payload)
- { /* process cert payload */
- import_certificate(cert_payload);
- }
-
- if (auth_payload && idr_payload)
- { /* authenticate peer */
- authenticator_t *authenticator;
- auth_method_t auth_method;
- status_t status;
-
- my_id = this->policy->get_my_id(this->policy);
- auth_method = auth_payload->get_auth_method(auth_payload);
- authenticator = authenticator_create(this->ike_sa, auth_method);
- if (authenticator == NULL)
- {
- SIG(IKE_UP_FAILED, "auth method %N not supported, deleting IKE_SA",
- auth_method_names, auth_method);
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
- status = authenticator->verify(authenticator, this->init_response,
- this->nonce_i, auth_payload);
- authenticator->destroy(authenticator);
- if (status != SUCCESS)
- {
- SIG(IKE_UP_FAILED, "authentication of '%D' with %N failed, "
- "deleting IKE_SA", other_id, auth_method_names, auth_method);
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
- this->peer_authenticated = TRUE;
- }
-
- if (eap_payload && this->eap_auth)
- {
- switch (this->eap_auth->process(this->eap_auth, eap_payload, &this->eap_next))
- {
- case SUCCESS:
- {
- /* EAP message was EAP_SUCCESS, send AUTH in next transaction */
- DBG2(DBG_IKE, "EAP authentication exchanges completed successful");
- this->eap_next = NULL;
- /* fall through */
- }
- case NEED_MORE:
- {
- /* EAP message was a EAP_REQUEST, handle it in next transaction */
- private_ike_auth_t *next_auth = clone_for_eap(this);
- next_auth->message_id = this->message_id + 1;
- *next = (transaction_t*)next_auth;
- return SUCCESS;
- }
- case FAILED:
- default:
- {
- /* EAP message was EAP_FAILURE */
- SIG(IKE_UP_FAILED, "EAP authentication failed, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
- }
- }
-
- if (!(auth_payload && sa_payload && tsi_payload && tsr_payload))
- {
- SIG(IKE_UP_FAILED, "response message incomplete, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
-
- /* if we do EAP authentication and a AUTH payload comes in, verify it */
- if (this->eap_auth &&
- (this->eap_auth->is_mutual(this->eap_auth) || this->peer_authenticated))
- {
- authenticator_t *authenticator = (authenticator_t*)this->eap_auth;
-
- if (authenticator->verify(authenticator, this->init_response,
- this->nonce_i, auth_payload) != SUCCESS)
- {
- SIG(IKE_UP_FAILED, "authentication with EAP failed, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
- this->peer_authenticated = TRUE;
- }
-
- if (!this->peer_authenticated)
- {
- SIG(IKE_UP_FAILED, "server didn't send authentication data, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
-
- SIG(IKE_UP_SUCCESS, "IKE_SA '%s' established between %H[%D]...%H[%D]",
- this->ike_sa->get_name(this->ike_sa), me, my_id, other, other_id);
- this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED);
-
- { /* process traffic selectors for us */
- linked_list_t *ts_received = tsi_payload->get_traffic_selectors(tsi_payload);
- this->tsi = this->policy->select_my_traffic_selectors(this->policy, ts_received, me);
- ts_received->destroy_offset(ts_received, offsetof(traffic_selector_t, destroy));
- }
-
- { /* process traffic selectors for other */
- linked_list_t *ts_received = tsr_payload->get_traffic_selectors(tsr_payload);
- this->tsr = this->policy->select_other_traffic_selectors(this->policy, ts_received, other);
- ts_received->destroy_offset(ts_received, offsetof(traffic_selector_t, destroy));
- }
-
- { /* process sa payload */
- linked_list_t *proposal_list;
-
- proposal_list = sa_payload->get_proposals(sa_payload);
- /* we have to re-check here if other's selection is valid */
- this->proposal = this->policy->select_proposal(this->policy, proposal_list);
- proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
-
- /* everything fine to create CHILD? */
- if (this->proposal == NULL ||
- this->tsi->get_count(this->tsi) == 0 ||
- this->tsr->get_count(this->tsr) == 0 ||
- !this->build_child)
- {
- SIG(CHILD_UP_FAILED, "CHILD_SA negotiation failed, no CHILD_SA built");
- }
- else
- {
- /* check mode if it is acceptable */
- switch (this->mode)
- {
- case MODE_TUNNEL:
- /* is the default */
- break;
- case MODE_TRANSPORT:
- /* TODO: we should close the CHILD_SA if negotiated
- * mode is not acceptable for us */
- if (!ts_list_is_host(this->tsi, me) ||
- !ts_list_is_host(this->tsr, other))
- {
- this->mode = MODE_TUNNEL;
- DBG1(DBG_IKE, "not using tranport mode, not host-to-host");
- }
- else if (this->ike_sa->is_natt_enabled(this->ike_sa))
- {
- this->mode = MODE_TUNNEL;
- DBG1(DBG_IKE, "not using tranport mode, as connection NATed");
- }
- break;
- case MODE_BEET:
- if (!ts_list_is_host(this->tsi, NULL) ||
- !ts_list_is_host(this->tsr, NULL))
- {
- this->mode = MODE_TUNNEL;
- DBG1(DBG_IKE, "not using BEET mode, not host-to-host");
- }
- break;
- }
-
- if (install_child_sa(this, TRUE) != SUCCESS)
- {
- SIG(CHILD_UP_FAILED, "installing CHILD_SA '%s' failed, no CHILD_SA built",
- this->policy->get_name(this->policy));
- /* TODO: we should send a DELETE for that CHILD to stay
- * synchronous with the peer */
- }
- else
- {
- SIG(CHILD_UP_SUCCESS, "CHILD_SA '%s' created",
- this->policy->get_name(this->policy));
- }
- }
- }
- return SUCCESS;
-}
-
-/**
- * implements transaction_t.destroy
- */
-static void destroy(private_ike_auth_t *this)
-{
- DESTROY_IF(this->message);
- DESTROY_IF(this->proposal);
- DESTROY_IF(this->child_sa);
- DESTROY_IF(this->policy);
- DESTROY_IF(this->connection);
- DESTROY_IF(this->cacerts);
- if (this->eap_auth)
- {
- this->eap_auth->authenticator_interface.destroy(&this->eap_auth->authenticator_interface);
- }
- DESTROY_IF(this->eap_next);
- if (this->tsi)
- {
- this->tsi->destroy_offset(this->tsi, offsetof(traffic_selector_t, destroy));
- }
- if (this->tsr)
- {
- this->tsr->destroy_offset(this->tsr, offsetof(traffic_selector_t, destroy));
- }
- chunk_free(&this->nonce_i);
- chunk_free(&this->nonce_r);
- chunk_free(&this->init_request);
- chunk_free(&this->init_response);
- free(this);
-}
-
-/*
- * Described in header.
- */
-ike_auth_t *ike_auth_create(ike_sa_t *ike_sa)
-{
- private_ike_auth_t *this = malloc_thing(private_ike_auth_t);
-
- /* transaction interface functions */
- this->public.transaction.get_request = (status_t(*)(transaction_t*,message_t**))get_request;
- this->public.transaction.get_response = (status_t(*)(transaction_t*,message_t*,message_t**,transaction_t**))get_response;
- this->public.transaction.conclude = (status_t(*)(transaction_t*,message_t*,transaction_t**))conclude;
- this->public.transaction.get_message_id = (u_int32_t(*)(transaction_t*))get_message_id;
- this->public.transaction.requested = (u_int32_t(*)(transaction_t*))requested;
- this->public.transaction.destroy = (void(*)(transaction_t*))destroy;
-
- /* public functions */
- this->public.set_config = (void(*)(ike_auth_t*,connection_t*,policy_t*))set_config;
- this->public.set_reqid = (void(*)(ike_auth_t*,u_int32_t))set_reqid;
- this->public.set_nonces = (void(*)(ike_auth_t*,chunk_t,chunk_t))set_nonces;
- this->public.set_init_messages = (void(*)(ike_auth_t*,chunk_t,chunk_t))set_init_messages;
-
- /* private data */
- this->ike_sa = ike_sa;
- this->message_id = 0;
- this->message = NULL;
- this->requested = 0;
- this->nonce_i = chunk_empty;
- this->nonce_r = chunk_empty;
- this->init_request = chunk_empty;
- this->init_response = chunk_empty;
- this->child_sa = NULL;
- this->proposal = NULL;
- this->policy = NULL;
- this->connection = NULL;
- this->tsi = NULL;
- this->tsr = NULL;
- this->build_child = TRUE;
- this->eap_auth = NULL;
- this->eap_next = NULL;
- this->eap_only = FALSE;
- this->peer_authenticated = FALSE;
- this->reqid = 0;
- this->cacerts = linked_list_create();
- this->mode = MODE_TUNNEL;
-
- return &this->public;
-}
diff --git a/src/charon/sa/transactions/ike_auth.h b/src/charon/sa/transactions/ike_auth.h
deleted file mode 100644
index 490359eda..000000000
--- a/src/charon/sa/transactions/ike_auth.h
+++ /dev/null
@@ -1,110 +0,0 @@
-/**
- * @file ike_auth.h
- *
- * @brief Interface of transaction ike_auth.
- *
- */
-
-/*
- * Copyright (C) 2006 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * 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.
- */
-
-
-#ifndef IKE_AUTH_H_
-#define IKE_AUTH_H_
-
-typedef struct ike_auth_t ike_auth_t;
-
-#include <sa/ike_sa.h>
-#include <sa/transactions/transaction.h>
-
-/**
- * @brief A transaction for the second message exchange to authenticate an IKE_SA.
- *
- * The second transaction is encrypted and authenticates the peers. It also
- * sets up a first CHILD_SA.
- *
- * @b Constructors:
- * - ike_auth_create()
- * - transaction_create() with the appropriate message
- *
- * @ingroup transactions
- */
-struct ike_auth_t {
-
- /**
- * The transaction_t interface.
- */
- transaction_t transaction;
-
- /**
- * @brief Set the config used for the ike_auth exchange.
- *
- * The connection definition is used to complete IKE_SA setup, the
- * policy defines the CHILD_SA which is created along with the ike_auth
- * exchange.
- *
- * @param this calling object
- * @param connection connection definition
- * @param policy policy definition
- */
- void (*set_config) (ike_auth_t* this,
- connection_t *connection, policy_t *policy);
-
- /**
- * @brief Set the reqid used for CHILD_SA setup.
- *
- * The first two message exchanges may set up an associated
- * CHILD_SA. If we acquire, we must use the same reqid as the
- * installed policy.
- *
- * @param this calling object
- * @param reqid reqid to use for the CHILD_SA
- */
- void (*set_reqid) (ike_auth_t* this, u_int32_t reqid);
-
- /**
- * @brief Set the nonces used in the previous ike_sa_init transaction.
- *
- * The nonces are used to create the authentication data.
- *
- * @param this calling object
- * @param nonce_i initiator chosen nonce
- * @param nonce_r responder chosen nonce
- */
- void (*set_nonces) (ike_auth_t* this, chunk_t nonce_i, chunk_t nonce_r);
-
- /**
- * @brief Set the messages used in the previous ike_sa_init transaction.
- *
- * The messages are used to create the authentication data.
- *
- * @param this calling object
- * @param request encoded request message as a chunk
- * @param response encoded response message as a chunk
- */
- void (*set_init_messages) (ike_auth_t* this, chunk_t request, chunk_t response);
-};
-
-/**
- * @brief Create a new transaction which processes IKE_AUTH exchanges.
- *
- * @param ike_sa assigned IKE_SA
- * @return created ike_auth transaction
- *
- * @ingroup transactions
- */
-ike_auth_t *ike_auth_create(ike_sa_t *ike_sa);
-
-#endif /* IKE_AUTH_H_ */
diff --git a/src/charon/sa/transactions/ike_sa_init.c b/src/charon/sa/transactions/ike_sa_init.c
deleted file mode 100644
index b5728a986..000000000
--- a/src/charon/sa/transactions/ike_sa_init.c
+++ /dev/null
@@ -1,1121 +0,0 @@
-/**
- * @file ike_sa_init.c
- *
- * @brief Implementation of ike_sa_init_t transaction.
- *
- */
-
-/*
- * Copyright (C) 2006 Tobias Brunner, Daniel Roethlisberger
- * Copyright (C) 2005-2006 Martin Willi
- * Copyright (C) 2005 Jan Hutter
- * Hochschule fuer Technik Rapperswil
- *
- * 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_sa_init.h"
-
-#include <string.h>
-
-#include <daemon.h>
-#include <crypto/diffie_hellman.h>
-#include <crypto/hashers/hasher.h>
-#include <encoding/payloads/sa_payload.h>
-#include <encoding/payloads/ke_payload.h>
-#include <encoding/payloads/nonce_payload.h>
-#include <sa/transactions/ike_auth.h>
-#include <queues/jobs/delete_ike_sa_job.h>
-#include <queues/jobs/rekey_ike_sa_job.h>
-
-
-typedef struct private_ike_sa_init_t private_ike_sa_init_t;
-
-/**
- * Private members of a ike_sa_init_t object..
- */
-struct private_ike_sa_init_t {
-
- /**
- * Public methods and transaction_t interface.
- */
- ike_sa_init_t public;
-
- /**
- * Assigned IKE_SA.
- */
- ike_sa_t *ike_sa;
-
- /**
- * Message sent by our peer, if already generated
- */
- message_t *message;
-
- /**
- * Message ID this transaction uses
- */
- u_int32_t message_id;
-
- /**
- * Times we did send the request
- */
- u_int32_t requested;
-
- /**
- * Next transaction followed to this one. May be IKE_AUTH,
- * or a IKE_SA_INIT retry
- */
- transaction_t **next;
-
- /**
- * Diffie hellman object used to generate public DH value.
- */
- diffie_hellman_t *diffie_hellman;
-
- /**
- * initiator chosen nonce
- */
- chunk_t nonce_i;
-
- /**
- * responder chosen nonce
- */
- chunk_t nonce_r;
-
- /**
- * connection definition used for initiation
- */
- connection_t *connection;
-
- /**
- * policy definition forwarded to ike_auth transaction
- */
- policy_t *policy;
-
- /**
- * Negotiated proposal used for IKE_SA
- */
- proposal_t *proposal;
-
- /**
- * Reqid to pass to IKE_AUTH, used for created CHILD_SA
- */
- u_int32_t reqid;
-
- /**
- * Unique ID for to enumerate all IKE_SAs in its name
- */
- u_int32_t unique_id;
-
- /**
- * Randomizer to generate nonces
- */
- randomizer_t *randomizer;
-
- /**
- * Hasher used to build NAT detection hashes
- */
- hasher_t *nat_hasher;
-
- /**
- * Precomputed NAT hash for source address
- */
- chunk_t natd_src_hash;
-
- /**
- * Precomputed NAT hash for destination address
- */
- chunk_t natd_dst_hash;
-
- /**
- * Did we process any NAT detection notifys for a source address?
- */
- bool natd_src_seen;
-
- /**
- * Did we process any NAT detection notifys for a destination address?
- */
- bool natd_dst_seen;
-
- /**
- * Have we found a matching source address NAT hash?
- */
- bool natd_src_matched;
-
- /**
- * Have we found a matching destination address NAT hash?
- */
- bool natd_dst_matched;
-};
-
-/**
- * Implementation of ike_sa_init_t.use_dh_group.
- */
-static bool use_dh_group(private_ike_sa_init_t *this, diffie_hellman_group_t dh_group)
-{
- if (this->connection->check_dh_group(this->connection, dh_group))
- {
- this->diffie_hellman = diffie_hellman_create(dh_group);
- if (this->diffie_hellman)
- {
- return TRUE;
- }
- }
- return FALSE;
-}
-
-/**
- * Implementation of ike_sa_init_t.set_config.
- */
-static void set_config(private_ike_sa_init_t *this,
- connection_t *connection, policy_t *policy)
-{
- this->connection = connection;
- this->policy = policy;
-}
-
-/**
- * Implementation of ike_sa_init_t.set_reqid.
- */
-static void set_reqid(private_ike_sa_init_t *this, u_int32_t reqid)
-{
- this->reqid = reqid;
-}
-
-/**
- * Implementation of transaction_t.get_message_id.
- */
-static u_int32_t get_message_id(private_ike_sa_init_t *this)
-{
- return this->message_id;
-}
-
-/**
- * Implementation of transaction_t.requested.
- */
-static u_int32_t requested(private_ike_sa_init_t *this)
-{
- return this->requested++;
-}
-
-/**
- * Build NAT detection hash for a host
- */
-static chunk_t generate_natd_hash(private_ike_sa_init_t *this,
- ike_sa_id_t * ike_sa_id, host_t *host)
-{
- chunk_t natd_chunk, spi_i_chunk, spi_r_chunk, addr_chunk, port_chunk;
- chunk_t natd_hash;
- u_int64_t spi_i, spi_r;
- u_int16_t port;
-
- /* prepare all requred chunks */
- spi_i = ike_sa_id->get_initiator_spi(ike_sa_id);
- spi_r = ike_sa_id->get_responder_spi(ike_sa_id);
- spi_i_chunk.ptr = (void*)&spi_i;
- spi_i_chunk.len = sizeof(spi_i);
- spi_r_chunk.ptr = (void*)&spi_r;
- spi_r_chunk.len = sizeof(spi_r);
- port = htons(host->get_port(host));
- port_chunk.ptr = (void*)&port;
- port_chunk.len = sizeof(port);
- addr_chunk = host->get_address(host);
-
- /* natd_hash = SHA1( spi_i | spi_r | address | port ) */
- natd_chunk = chunk_cat("cccc", spi_i_chunk, spi_r_chunk, addr_chunk, port_chunk);
- this->nat_hasher->allocate_hash(this->nat_hasher, natd_chunk, &natd_hash);
- DBG3(DBG_IKE, "natd_chunk %B", &natd_chunk);
- DBG3(DBG_IKE, "natd_hash %B", &natd_hash);
-
- chunk_free(&natd_chunk);
- return natd_hash;
-}
-
-/**
- * Build a NAT detection notify payload.
- */
-static notify_payload_t *build_natd_payload(private_ike_sa_init_t *this,
- notify_type_t type, host_t *host)
-{
- chunk_t hash;
- notify_payload_t *notify;
- ike_sa_id_t *ike_sa_id;
-
- ike_sa_id = this->ike_sa->get_id(this->ike_sa);
- notify = notify_payload_create();
- notify->set_notify_type(notify, type);
- hash = generate_natd_hash(this, ike_sa_id, host);
- notify->set_notification_data(notify, hash);
- chunk_free(&hash);
-
- return notify;
-}
-
-/**
- * Implementation of transaction_t.get_request.
- */
-static status_t get_request(private_ike_sa_init_t *this, message_t **result)
-{
- message_t *request;
- host_t *me, *other;
- identification_t *my_id, *other_id;
- char name[64];
-
- /* check if we already have built a message (retransmission) */
- if (this->message)
- {
- *result = this->message;
- return SUCCESS;
- }
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
-
- /* we already set up the IDs. Mine is already fully qualified, other
- * will be updated in the ike_auth transaction */
- my_id = this->policy->get_my_id(this->policy);
- other_id = this->policy->get_other_id(this->policy);
- this->ike_sa->set_my_id(this->ike_sa, my_id->clone(my_id));
- this->ike_sa->set_other_id(this->ike_sa, other_id->clone(other_id));
- if (snprintf(name, sizeof(name), "%s{%d}",
- this->connection->get_name(this->connection),
- this->unique_id) > 0)
- {
- this->ike_sa->set_name(this->ike_sa, name);
- }
-
- /* setting up a IKE_SA implicitly requires setup of a CHILD_SA */
- SIG(IKE_UP_START, "initiating IKE_SA '%s' between %H[%D]...%H[%D]",
- this->connection->get_name(this->connection), me, my_id, other, other_id);
- SIG(CHILD_UP_START, "establishing CHILD_SA '%s' along with IKE_SA",
- this->policy->get_name(this->policy));
-
- /* build the request */
- request = message_create();
- request->set_source(request, me->clone(me));
- request->set_destination(request, other->clone(other));
- request->set_exchange_type(request, IKE_SA_INIT);
- request->set_request(request, TRUE);
- request->set_message_id(request, this->message_id);
- request->set_ike_sa_id(request, this->ike_sa->get_id(this->ike_sa));
- /* apply for caller */
- *result = request;
- /* store for retransmission */
- this->message = request;
-
- /* if the DH group is set via use_dh_group(), we already have a DH object */
- if (!this->diffie_hellman)
- {
- diffie_hellman_group_t dh_group;
-
- dh_group = this->connection->get_dh_group(this->connection);
- this->diffie_hellman = diffie_hellman_create(dh_group);
- if (this->diffie_hellman == NULL)
- {
- SIG(IKE_UP_FAILED, "DH group %N not supported, aborting",
- diffie_hellman_group_names, dh_group);
- SIG(CHILD_UP_FAILED,
- "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
- }
-
- { /* build the SA payload from proposals */
- sa_payload_t *sa_payload;
- linked_list_t *proposal_list;
-
- proposal_list = this->connection->get_proposals(this->connection);
- sa_payload = sa_payload_create_from_proposal_list(proposal_list);
- proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
-
- request->add_payload(request, (payload_t*)sa_payload);
- }
-
- { /* build the KE payload from the DH object */
- ke_payload_t *ke_payload;
-
- ke_payload = ke_payload_create_from_diffie_hellman(this->diffie_hellman);
-
- request->add_payload(request, (payload_t*)ke_payload);
- }
-
- { /* build the NONCE payload for us (initiator) */
- nonce_payload_t *nonce_payload;
-
- if (this->randomizer->allocate_pseudo_random_bytes(this->randomizer,
- NONCE_SIZE, &this->nonce_i) != SUCCESS)
- {
- SIG(IKE_UP_FAILED, "could not generate nonce, aborting");
- SIG(CHILD_UP_FAILED,
- "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
- nonce_payload = nonce_payload_create();
- nonce_payload->set_nonce(nonce_payload, this->nonce_i);
-
- request->add_payload(request, (payload_t*)nonce_payload);
- }
-
- { /* build NAT_DETECTION notifys */
- notify_payload_t *notify;
- linked_list_t *list;
- host_t *host;
-
- /* N(NAT_DETECTION_SOURCE_IP)+
- * we include only one notify if our address is defined, but all
- * possible if not */
- host = this->ike_sa->get_my_host(this->ike_sa);
- if (host->is_anyaddr(host))
- {
- /* TODO: we could get the src address from netlink */
- list = charon->socket->create_local_address_list(charon->socket);
- while (list->remove_first(list, (void**)&host) == SUCCESS)
- {
- notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host);
- host->destroy(host);
- request->add_payload(request, (payload_t*)notify);
- }
- list->destroy(list);
- }
- else
- {
- notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host);
- request->add_payload(request, (payload_t*)notify);
- }
-
- /* N(NAT_DETECTION_DESTINATION_IP) */
- notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, other);
- request->add_payload(request, (payload_t*)notify);
- }
-
- this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING);
- return SUCCESS;
-}
-
-/**
- * Handle all kind of notifys
- */
-static status_t process_notifys(private_ike_sa_init_t *this, notify_payload_t *notify_payload)
-{
- chunk_t notification_data;
- notify_type_t notify_type = notify_payload->get_notify_type(notify_payload);
-
- DBG2(DBG_IKE, "process notify type %N", notify_type_names, notify_type);
-
- switch (notify_type)
- {
- case NO_PROPOSAL_CHOSEN:
- {
- SIG(IKE_UP_FAILED,
- "received a NO_PROPOSAL_CHOSEN notify, deleting IKE_SA");
- return DESTROY_ME;
- }
- case INVALID_MAJOR_VERSION:
- {
- SIG(IKE_UP_FAILED,
- "received a INVALID_MAJOR_VERSION notify, deleting IKE_SA");
- return DESTROY_ME;
- }
- case INVALID_KE_PAYLOAD:
- {
- chunk_t notify_data;
- diffie_hellman_group_t dh_group, old_dh_group;
- ike_sa_init_t *retry;
-
- old_dh_group = this->connection->get_dh_group(this->connection);
- notify_data = notify_payload->get_notification_data(notify_payload);
- dh_group = ntohs(*((u_int16_t*)notify_data.ptr));
-
- DBG1(DBG_IKE, "peer didn't accept DH group %N, it requested %N",
- diffie_hellman_group_names, old_dh_group,
- diffie_hellman_group_names, dh_group);
- if (!this->connection->check_dh_group(this->connection, dh_group))
- {
- SIG(IKE_UP_FAILED, "DH group %N not acceptable, aborting",
- diffie_hellman_group_names, dh_group);
- return DESTROY_ME;
- }
- retry = ike_sa_init_create(this->ike_sa);
- retry->set_config(retry, this->connection, this->policy);
- this->connection = NULL;
- this->policy = NULL;
- retry->use_dh_group(retry, dh_group);
- *this->next = (transaction_t*)retry;
- return FAILED;
- }
- case NAT_DETECTION_DESTINATION_IP:
- {
- this->natd_dst_seen = TRUE;
- if (this->natd_dst_matched)
- {
- return SUCCESS;
- }
- notification_data = notify_payload->get_notification_data(notify_payload);
- if (chunk_equals(notification_data, this->natd_dst_hash))
- {
- this->natd_dst_matched = TRUE;
- DBG2(DBG_IKE, "NAT-D dst hash match");
- }
- else
- {
- DBG2(DBG_IKE, "NAT-D dst hash mismatch");
- }
- return SUCCESS;
- }
- case NAT_DETECTION_SOURCE_IP:
- {
- this->natd_src_seen = TRUE;;
- if (this->natd_src_matched)
- {
- return SUCCESS;
- }
- notification_data = notify_payload->get_notification_data(notify_payload);
- if (chunk_equals(notification_data, this->natd_src_hash))
- {
- this->natd_src_matched = TRUE;
- DBG2(DBG_IKE, "NAT-D src hash match");
- }
- else
- {
- DBG2(DBG_IKE, "NAT-D src hash mismatch");
- }
- return SUCCESS;
- }
- default:
- {
- if (notify_type < 16383)
- {
- SIG(IKE_UP_FAILED, "received %N notify error, deleting IKE_SA",
- notify_type_names, notify_type);
- return DESTROY_ME;
- }
- else
- {
- DBG1(DBG_IKE, "received %N notify, ignored",
- notify_type_names, notify_type);
- return SUCCESS;
- }
- }
- }
-}
-
-/**
- * Implementation of transaction_t.get_response.
- */
-static status_t get_response(private_ike_sa_init_t *this,
- message_t *request, message_t **result,
- transaction_t **next)
-{
- host_t *me, *other;
- message_t *response;
- status_t status;
- iterator_t *payloads;
- payload_t *payload;
- sa_payload_t *sa_request = NULL;
- ke_payload_t *ke_request = NULL;
- nonce_payload_t *nonce_request = NULL;
- ike_sa_id_t *ike_sa_id;
- u_int32_t timeout;
- char name[64];
-
- /* check if we already have built a response (retransmission) */
- if (this->message)
- {
- *result = this->message;
- return SUCCESS;
- }
-
- me = request->get_destination(request);
- other = request->get_source(request);
- this->message_id = request->get_message_id(request);
-
- SIG(IKE_UP_START, "establishing IKE_SA between %H...%H", me, other);
-
- /* set up response */
- response = message_create();
- response->set_source(response, me->clone(me));
- response->set_destination(response, other->clone(other));
- response->set_exchange_type(response, IKE_SA_INIT);
- response->set_request(response, FALSE);
- response->set_message_id(response, this->message_id);
- response->set_ike_sa_id(response, this->ike_sa->get_id(this->ike_sa));
- this->message = response;
- *result = response;
-
- /* check message type */
- if (request->get_exchange_type(request) != IKE_SA_INIT)
- {
- SIG(IKE_UP_FAILED, "IKE_SA_INIT request of invalid type, deleting IKE_SA");
- return DESTROY_ME;
- }
-
- /* this is the first message to process, find a connection for IKE_SA */
- this->connection = charon->connections->get_connection_by_hosts(
- charon->connections, me, other);
- if (this->connection == NULL)
- {
- notify_payload_t *notify = notify_payload_create();
- notify->set_notify_type(notify, NO_PROPOSAL_CHOSEN);
- response->add_payload(response, (payload_t*)notify);
-
- SIG(IKE_UP_FAILED, "no connection for hosts %H...%H found, "
- "deleting IKE_SA", me, other);
- return DESTROY_ME;
- }
-
- if (snprintf(name, sizeof(name), "%s{%d}",
- this->connection->get_name(this->connection),
- this->unique_id) > 0)
- {
- this->ike_sa->set_name(this->ike_sa, name);
- }
- this->ike_sa->apply_connection(this->ike_sa, this->connection);
-
- /* Precompute NAT-D hashes for incoming NAT notify comparison */
- ike_sa_id = request->get_ike_sa_id(request);
- this->natd_dst_hash = generate_natd_hash(this, ike_sa_id, me);
- this->natd_src_hash = generate_natd_hash(this, ike_sa_id, other);
-
- /* Iterate over all payloads. */
- payloads = request->get_payload_iterator(request);
- while (payloads->iterate(payloads, (void**)&payload))
- {
- switch (payload->get_type(payload))
- {
- case SECURITY_ASSOCIATION:
- sa_request = (sa_payload_t*)payload;
- break;
- case KEY_EXCHANGE:
- ke_request = (ke_payload_t*)payload;
- break;
- case NONCE:
- nonce_request = (nonce_payload_t*)payload;
- break;
- case NOTIFY:
- {
- status = process_notifys(this, (notify_payload_t*)payload);
- if (status == FAILED)
- {
- payloads->destroy(payloads);
- /* we return SUCCESS, returned FAILED means do next transaction */
- return SUCCESS;
- }
- if (status == DESTROY_ME)
- {
- payloads->destroy(payloads);
- return DESTROY_ME;
- }
- break;
- }
- default:
- {
- DBG2(DBG_IKE, "ignoring %N payload",
- payload_type_names, payload->get_type(payload));
- break;
- }
- }
- }
- payloads->destroy(payloads);
-
- /* check if we have all payloads */
- if (!(sa_request && ke_request && nonce_request))
- {
- notify_payload_t *notify = notify_payload_create();
- notify->set_notify_type(notify, INVALID_SYNTAX);
- response->add_payload(response, (payload_t*)notify);
- SIG(IKE_UP_FAILED, "received request message incomplete, deleting IKE_SA");
- return DESTROY_ME;
- }
-
- { /* process SA payload:
- * -------------------
- * - extract proposals
- * - select our most preferred proposal found in extracted
- * - if no matches, return NO_PROPOSAL_CHOSEN
- * - add sa payload with selected proposal
- */
- sa_payload_t* sa_response;
- linked_list_t *proposal_list;
-
- proposal_list = sa_request->get_proposals(sa_request);
- this->proposal = this->connection->select_proposal(this->connection, proposal_list);
- proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
- if (this->proposal == NULL)
- {
- notify_payload_t *notify = notify_payload_create();
- notify->set_notify_type(notify, NO_PROPOSAL_CHOSEN);
- response->add_payload(response, (payload_t*)notify);
- SIG(IKE_UP_FAILED, "request did not contain any acceptable "
- "proposals, deleting IKE_SA");
- return DESTROY_ME;
- }
- sa_response = sa_payload_create_from_proposal(this->proposal);
- response->add_payload(response, (payload_t *)sa_response);
- }
-
- { /* process KE payload:
- * --------------------
- * - check if used group match the selected proposal
- * - if not, stop with INVALID_KE_PAYLOAD
- * - apply others public value to complete diffie hellman exchange
- * - add our public value to response
- */
- diffie_hellman_group_t used_group;
- ke_payload_t *ke_response;
-
- used_group = ke_request->get_dh_group_number(ke_request);
-
- if (!this->connection->check_dh_group(this->connection, used_group) ||
- (this->diffie_hellman = diffie_hellman_create(used_group)) == NULL)
- {
- u_int16_t notify_group;
- chunk_t notify_chunk;
- notify_payload_t *notify;
- iterator_t *iterator;
- payload_t *payload;
-
- notify_group = this->connection->get_dh_group(this->connection);
- SIG(IKE_UP_FAILED, "request used inacceptable DH group %N, sending "
- "INVALID_KE_PAYLOAD with %N, deleting IKE_SA",
- diffie_hellman_group_names, used_group,
- diffie_hellman_group_names, notify_group);
-
- /* remove already added payloads */
- iterator = response->get_payload_iterator(response);
- while (iterator->iterate(iterator, (void**)&payload))
- {
- iterator->remove(iterator);
- payload->destroy(payload);
- }
- iterator->destroy(iterator);
-
- notify_group = htons(notify_group);
- notify_chunk.ptr = (u_int8_t*)&notify_group;
- notify_chunk.len = sizeof(notify_group);
- notify = notify_payload_create();
- notify->set_notify_type(notify, INVALID_KE_PAYLOAD);
- notify->set_notification_data(notify, notify_chunk);
- response->add_payload(response, (payload_t*)notify);
- return DESTROY_ME;
- }
- this->diffie_hellman->set_other_public_value(this->diffie_hellman,
- ke_request->get_key_exchange_data(ke_request));
-
- /* build response */
- ke_response = ke_payload_create_from_diffie_hellman(this->diffie_hellman);
- response->add_payload(response, (payload_t*)ke_response);
- }
-
- { /* process nonce payload:
- * ----------------------
- * - get nonce from payload
- * - generate own nonce and add to reply
- */
- nonce_payload_t *nonce_response;
-
- this->nonce_i = nonce_request->get_nonce(nonce_request);
-
- /* build response nonce */
- if (this->randomizer->allocate_pseudo_random_bytes(this->randomizer,
- NONCE_SIZE, &this->nonce_r) != SUCCESS)
- {
- notify_payload_t *notify = notify_payload_create();
- notify->set_notify_type(notify, NO_PROPOSAL_CHOSEN);
- response->add_payload(response, (payload_t*)notify);
- SIG(IKE_UP_FAILED, "could not create nonce, deleting IKE_SA");
- return DESTROY_ME;
- }
- nonce_response = nonce_payload_create();
- nonce_response->set_nonce(nonce_response, this->nonce_r);
- response->add_payload(response, (payload_t *)nonce_response);
- }
-
- { /* processs NATT stuff:
- * --------------------
- * - check if we or other is behind NAT
- * - enable NATT if so
- * - build NAT detection notifys for reply
- */
- notify_payload_t *notify;
-
- if ((!this->natd_src_seen && this->natd_dst_seen) ||
- (this->natd_src_seen && !this->natd_dst_seen))
- {
- notify = notify_payload_create();
- notify->set_notify_type(notify, INVALID_SYNTAX);
- response->add_payload(response, (payload_t*)notify);
- SIG(IKE_UP_FAILED, "request contained invalid number of NAT-D"
- "payloads, deleting IKE_SA");
- return DESTROY_ME;
- }
- if (this->natd_dst_seen && !this->natd_dst_matched)
- {
- this->ike_sa->enable_natt(this->ike_sa, TRUE);
- }
- if (this->natd_src_seen && !this->natd_src_matched)
- {
- this->ike_sa->enable_natt(this->ike_sa, FALSE);
- }
- /* build response NAT DETECTION notifys, if remote supports it */
- if (this->natd_src_seen || this->natd_dst_seen)
- {
- /* N(NAT_DETECTION_SOURCE_IP) */
- notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, me);
- response->add_payload(response, (payload_t*)notify);
-
- /* N(NAT_DETECTION_DESTINATION_IP) */
- notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, other);
- response->add_payload(response, (payload_t*)notify);
- }
- }
-
- /* derive all the keys used in the IKE_SA */
- if (this->ike_sa->derive_keys(this->ike_sa, this->proposal,
- this->diffie_hellman,
- this->nonce_i, this->nonce_r,
- FALSE, NULL, NULL) != SUCCESS)
- {
- notify_payload_t *notify = notify_payload_create();
- notify->set_notify_type(notify, NO_PROPOSAL_CHOSEN);
- response->add_payload(response, (payload_t*)notify);
- SIG(IKE_UP_FAILED, "error creating transforms from proposal, deleting IKE_SA");
- return DESTROY_ME;
- }
-
- this->ike_sa->set_lifetimes(this->ike_sa,
- this->connection->get_reauth(this->connection),
- this->connection->get_soft_lifetime(this->connection),
- this->connection->get_hard_lifetime(this->connection));
-
- { /* create ike_auth transaction, which will store informations for us */
- packet_t *response_packet;
- chunk_t request_chunk, response_chunk;
- ike_auth_t *ike_auth;
-
- /* we normally do not generate the message. But we need the generated message
- * for authentication in the next state, so we do it here. This is not problematic,
- * as we don't use a crypter/signer in ike_sa_init... */
- if (response->generate(response, NULL, NULL, &response_packet) != SUCCESS)
- {
- SIG(IKE_UP_FAILED, "error in response generation, deleting IKE_SA");
- return DESTROY_ME;
- }
- response_packet->destroy(response_packet);
- request_chunk = request->get_packet_data(request);
- response_chunk = response->get_packet_data(response);
-
- /* create next transaction, for which we except a message */
- ike_auth = ike_auth_create(this->ike_sa);
- ike_auth->set_config(ike_auth, this->connection, this->policy);
- ike_auth->set_reqid(ike_auth, this->reqid);
- this->connection = NULL;
- this->policy = NULL;
- ike_auth->set_nonces(ike_auth,
- chunk_clone(this->nonce_i),
- chunk_clone(this->nonce_r));
- ike_auth->set_init_messages(ike_auth, request_chunk, response_chunk);
- *next = (transaction_t*)ike_auth;
- }
-
- /* everything went fine. Now we set a timeout to destroy half initiated IKE_SAs */
- timeout = charon->configuration->get_half_open_ike_sa_timeout(charon->configuration);
- if (timeout)
- {
- job_t *job = (job_t*)delete_ike_sa_job_create(
- this->ike_sa->get_id(this->ike_sa), FALSE);
- charon->event_queue->add_relative(charon->event_queue, job, timeout);
- }
- /* set new state */
- this->ike_sa->set_state(this->ike_sa, IKE_CONNECTING);
-
- return SUCCESS;
-}
-
-
-/**
- * Implementation of transaction_t.conclude
- */
-static status_t conclude(private_ike_sa_init_t *this, message_t *response,
- transaction_t **next)
-{
- u_int64_t responder_spi;
- ike_sa_id_t *ike_sa_id;
- iterator_t *payloads;
- payload_t *payload;
- host_t *me, *other;
- sa_payload_t *sa_payload = NULL;
- ke_payload_t *ke_payload = NULL;
- nonce_payload_t *nonce_payload = NULL;
- status_t status;
-
- /* check message type */
- if (response->get_exchange_type(response) != IKE_SA_INIT)
- {
- SIG(IKE_UP_FAILED, "IKE_SA_INIT response of invalid type, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
-
- /* allow setting of next transaction in other functions */
- this->next = next;
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
-
- /* check if SPI has been updated, but apply only if all goes ok later */
- responder_spi = response->get_responder_spi(response);
- if (responder_spi == 0)
- {
- SIG(IKE_UP_FAILED, "response contained a SPI of zero, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
-
- /* Precompute NAT-D hashes for later comparison */
- ike_sa_id = response->get_ike_sa_id(response);
- this->natd_src_hash = generate_natd_hash(this, ike_sa_id, other);
- this->natd_dst_hash = generate_natd_hash(this, ike_sa_id, me);
-
- /* Iterate over all payloads to collect them */
- payloads = response->get_payload_iterator(response);
- while (payloads->iterate(payloads, (void**)&payload))
- {
- switch (payload->get_type(payload))
- {
- case SECURITY_ASSOCIATION:
- {
- sa_payload = (sa_payload_t*)payload;
- break;
- }
- case KEY_EXCHANGE:
- {
- ke_payload = (ke_payload_t*)payload;
- break;
- }
- case NONCE:
- {
- nonce_payload = (nonce_payload_t*)payload;
- break;
- }
- case NOTIFY:
- {
- status = process_notifys(this, (notify_payload_t*)payload);
- if (status == FAILED)
- {
- payloads->destroy(payloads);
- /* we return SUCCESS, returned FAILED means do next transaction */
- return SUCCESS;
- }
- if (status == DESTROY_ME)
- {
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- payloads->destroy(payloads);
- return status;
- }
- break;
- }
- default:
- {
- DBG1(DBG_IKE, "ignoring payload %N",
- payload_type_names, payload->get_type(payload));
- break;
- }
- }
- }
- payloads->destroy(payloads);
-
- if (!(nonce_payload && sa_payload && ke_payload))
- {
- SIG(IKE_UP_FAILED, "response message incomplete, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
-
- { /* process SA payload:
- * -------------------
- * - get proposals from it
- * - check if peer selected a proposal
- * - verify it's selection againts our set
- */
- linked_list_t *proposal_list;
-
- /* get the list of selected proposals, the peer has to select only one proposal */
- proposal_list = sa_payload->get_proposals (sa_payload);
- if (proposal_list->get_count(proposal_list) != 1)
- {
- SIG(IKE_UP_FAILED, "response did not contain a single proposal, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
- return DESTROY_ME;
- }
-
- /* we have to re-check if the others selection is valid */
- this->proposal = this->connection->select_proposal(this->connection, proposal_list);
- proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
-
- if (this->proposal == NULL)
- {
- SIG(IKE_UP_FAILED, "peer selected a proposal we did not offer, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
- }
-
- { /* process KE payload:
- * -------------------
- * - extract others public value
- * - complete diffie-hellman exchange
- */
- this->diffie_hellman->set_other_public_value(this->diffie_hellman,
- ke_payload->get_key_exchange_data(ke_payload));
- }
-
- { /* process NONCE payload:
- * ----------------------
- * - extract nonce used for key derivation */
- this->nonce_r = nonce_payload->get_nonce(nonce_payload);
- }
-
- { /* process NATT stuff:
- * -------------------
- * - check if we or other is NATted
- * - switch to port 4500 if so
- */
- if ((!this->natd_dst_seen && this->natd_src_seen) ||
- (this->natd_dst_seen && !this->natd_src_seen))
- {
- SIG(IKE_UP_FAILED, "request contained invalid number of NAT-D payloads, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
- if (this->natd_src_seen && !this->natd_src_matched)
- {
- this->ike_sa->enable_natt(this->ike_sa, FALSE);
- }
- if (this->natd_dst_seen && !this->natd_dst_matched)
- {
- this->ike_sa->enable_natt(this->ike_sa, TRUE);
- }
- if (this->ike_sa->is_natt_enabled(this->ike_sa))
- {
- me = this->ike_sa->get_my_host(this->ike_sa);
- me->set_port(me, IKEV2_NATT_PORT);
- other = this->ike_sa->get_other_host(this->ike_sa);
- other->set_port(other, IKEV2_NATT_PORT);
-
- DBG2(DBG_IKE, "switching to port %d", IKEV2_NATT_PORT);
- }
- }
-
- /* because we are original initiator we have to update the responder SPI to the new one */
- ike_sa_id = this->ike_sa->get_id(this->ike_sa);
- ike_sa_id->set_responder_spi(ike_sa_id, responder_spi);
-
- /* derive all the keys used in the IKE_SA */
- if (this->ike_sa->derive_keys(this->ike_sa, this->proposal,
- this->diffie_hellman,
- this->nonce_i, this->nonce_r,
- TRUE, NULL, NULL) != SUCCESS)
- {
- SIG(IKE_UP_FAILED, "error creating transforms from proposal, deleting IKE_SA");
- SIG(CHILD_UP_FAILED, "initiating CHILD_SA failed, unable to create IKE_SA");
- return DESTROY_ME;
- }
-
- this->ike_sa->set_lifetimes(this->ike_sa,
- this->connection->get_reauth(this->connection),
- this->connection->get_soft_lifetime(this->connection),
- this->connection->get_hard_lifetime(this->connection));
-
- { /* create ike_auth transaction, which will continue IKE_SA setup */
- chunk_t request_chunk, response_chunk;
- ike_auth_t *ike_auth;
-
- request_chunk = this->message->get_packet_data(this->message);
- response_chunk = response->get_packet_data(response);
-
- /* create next transaction, for which we except a message */
- ike_auth = ike_auth_create(this->ike_sa);
- ike_auth->set_config(ike_auth, this->connection, this->policy);
- ike_auth->set_reqid(ike_auth, this->reqid);
- this->connection = NULL;
- this->policy = NULL;
- ike_auth->set_nonces(ike_auth,
- chunk_clone(this->nonce_i),
- chunk_clone(this->nonce_r));
- ike_auth->set_init_messages(ike_auth, request_chunk, response_chunk);
- *next = (transaction_t*)ike_auth;
- }
-
- return SUCCESS;
-}
-
-static void destroy(private_ike_sa_init_t *this)
-{
- DESTROY_IF(this->message);
- DESTROY_IF(this->diffie_hellman);
- DESTROY_IF(this->proposal);
- DESTROY_IF(this->connection);
- DESTROY_IF(this->policy);
- chunk_free(&this->nonce_i);
- chunk_free(&this->nonce_r);
- this->randomizer->destroy(this->randomizer);
- this->nat_hasher->destroy(this->nat_hasher);
- chunk_free(&this->natd_src_hash);
- chunk_free(&this->natd_dst_hash);
- free(this);
-}
-
-/*
- * Described in header.
- */
-ike_sa_init_t *ike_sa_init_create(ike_sa_t *ike_sa)
-{
- static u_int unique_id = 0;
- private_ike_sa_init_t *this = malloc_thing(private_ike_sa_init_t);
-
- /* transaction interface functions */
- this->public.transaction.get_request = (status_t(*)(transaction_t*,message_t**))get_request;
- this->public.transaction.get_response = (status_t(*)(transaction_t*,message_t*,message_t**,transaction_t**))get_response;
- this->public.transaction.conclude = (status_t(*)(transaction_t*,message_t*,transaction_t**))conclude;
- this->public.transaction.get_message_id = (u_int32_t(*)(transaction_t*))get_message_id;
- this->public.transaction.requested = (u_int32_t(*)(transaction_t*))requested;
- this->public.transaction.destroy = (void(*)(transaction_t*))destroy;
-
- /* public functions */
- this->public.set_config = (void(*)(ike_sa_init_t*,connection_t*,policy_t*))set_config;
- this->public.set_reqid = (void(*)(ike_sa_init_t*,u_int32_t))set_reqid;
- this->public.use_dh_group = (bool(*)(ike_sa_init_t*,diffie_hellman_group_t))use_dh_group;
-
- /* private data */
- this->ike_sa = ike_sa;
- this->message_id = 0;
- this->message = NULL;
- this->requested = 0;
- this->diffie_hellman = NULL;
- this->nonce_i = chunk_empty;
- this->nonce_r = chunk_empty;
- this->connection = NULL;
- this->policy = NULL;
- this->proposal = NULL;
- this->unique_id = ++unique_id;
- this->reqid = 0;
- this->randomizer = randomizer_create();
- this->nat_hasher = hasher_create(HASH_SHA1);
- this->natd_src_hash = chunk_empty;
- this->natd_dst_hash = chunk_empty;
- this->natd_src_seen = FALSE;
- this->natd_dst_seen = FALSE;
- this->natd_src_matched = FALSE;
- this->natd_dst_matched = FALSE;
-
- return &this->public;
-}
diff --git a/src/charon/sa/transactions/ike_sa_init.h b/src/charon/sa/transactions/ike_sa_init.h
deleted file mode 100644
index ab820fea5..000000000
--- a/src/charon/sa/transactions/ike_sa_init.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/**
- * @file ike_sa_init.h
- *
- * @brief Interface of transaction ike_sa_init.
- *
- */
-
-/*
- * Copyright (C) 2006 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * 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.
- */
-
-
-#ifndef IKE_SA_INIT_H_
-#define IKE_SA_INIT_H_
-
-typedef struct ike_sa_init_t ike_sa_init_t;
-
-#include <sa/ike_sa.h>
-#include <sa/transactions/transaction.h>
-
-/**
- * @brief A transaction for the first message exchange to set up an IKE_SA.
- *
- * @b Constructors:
- * - ike_sa_init_create()
- * - transaction_create() with the appropriate message
- *
- * @ingroup transactions
- */
-struct ike_sa_init_t {
-
- /**
- * The transaction_t interface.
- */
- transaction_t transaction;
-
- /**
- * @brief Set connection & policy to use for initiation.
- *
- * The policy is not used directly, but forwarded to the
- * ike_auth transaction.
- *
- * @param this calling object
- * @param connection connection to use for initiation
- * @param policy policy used in ike_auth transaction
- */
- void (*set_config) (ike_sa_init_t* this,
- connection_t *connection, policy_t *policy);
-
- /**
- * @brief Set the reqid used for CHILD_SA setup.
- *
- * The first two message exchanges may set up an associated
- * CHILD_SA. If we acquire, we must use the same reqid as the
- * installed policy. This requid is passed to the ike_auth
- * transaction which creates the CHILD_AS.
- *
- * @param this calling object
- * @param reqid reqid to use for the CHILD_SA
- */
- void (*set_reqid) (ike_sa_init_t* this, u_int32_t reqid);
-
- /**
- * @brief Set the Diffie Hellman group to use for initiating.
- *
- * If a first exchange fails with a INVALID_KE_PAYLOAD, the second
- * try uses the DH group proposed by the responder.
- *
- * @param this calling object
- * @param dh_group diffie hellman group to use
- * @return FALSE, if DH group not allowed/supported
- */
- bool (*use_dh_group) (ike_sa_init_t* this, diffie_hellman_group_t dh_group);
-};
-
-/**
- * @brief Create a new transaction which processes IKE_SA_INIT exchanges.
- *
- * @param ike_sa assigned IKE_SA
- * @return created ike_sa_init transaction
- *
- * @ingroup transactions
- */
-ike_sa_init_t *ike_sa_init_create(ike_sa_t *ike_sa);
-
-#endif /* IKE_SA_INIT_H_ */
diff --git a/src/charon/sa/transactions/rekey_ike_sa.c b/src/charon/sa/transactions/rekey_ike_sa.c
deleted file mode 100644
index 23e7e5638..000000000
--- a/src/charon/sa/transactions/rekey_ike_sa.c
+++ /dev/null
@@ -1,889 +0,0 @@
-/**
- * @file rekey_ike_sa.c
- *
- * @brief Implementation of rekey_ike_sa_t transaction.
- *
- */
-
-/*
- * Copyright (C) 2006 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * 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 "rekey_ike_sa.h"
-
-#include <string.h>
-
-#include <daemon.h>
-#include <encoding/payloads/sa_payload.h>
-#include <encoding/payloads/nonce_payload.h>
-#include <encoding/payloads/ke_payload.h>
-#include <sa/transactions/delete_ike_sa.h>
-#include <utils/randomizer.h>
-
-
-typedef struct private_rekey_ike_sa_t private_rekey_ike_sa_t;
-
-/**
- * Private members of a rekey_ike_sa_t object..
- */
-struct private_rekey_ike_sa_t {
-
- /**
- * Public methods and transaction_t interface.
- */
- rekey_ike_sa_t public;
-
- /**
- * Assigned IKE_SA.
- */
- ike_sa_t *ike_sa;
-
- /**
- * Message sent by our peer, if already generated
- */
- message_t *message;
-
- /**
- * Message ID this transaction uses
- */
- u_int32_t message_id;
-
- /**
- * Times we did send the request
- */
- u_int32_t requested;
-
- /**
- * IKE_SA we set up, replaces ike_sa
- */
- ike_sa_t *new_sa;
-
- /**
- * Connection used to replace IKE_SA
- */
- connection_t *connection;
-
- /**
- * initiator chosen nonce
- */
- chunk_t nonce_i;
-
- /**
- * responder chosen nonce
- */
- chunk_t nonce_r;
-
- /**
- * lower of the nonces of a simultaneus rekeying request
- */
- chunk_t nonce_s;
-
- /**
- * Diffie hellman to generate new shared secret
- */
- diffie_hellman_t *diffie_hellman;
-
- /**
- * negotiated proposal to use
- */
- proposal_t *proposal;
-
- /**
- * Have we lost the simultaneous rekeying nonce compare?
- */
- bool lost;
-
- /**
- * source of randomness for nonces
- */
- randomizer_t *randomizer;
-
- /**
- * next transaction processed by the IKE_SA
- */
- transaction_t **next;
-};
-
-/**
- * Implementation of transaction_t.get_message_id.
- */
-static u_int32_t get_message_id(private_rekey_ike_sa_t *this)
-{
- return this->message_id;
-}
-
-/**
- * Implementation of transaction_t.requested.
- */
-static u_int32_t requested(private_rekey_ike_sa_t *this)
-{
- return this->requested++;
-}
-
-
-/**
- * Implementation of rekey_ike_sa_t.use_dh_group.
- */
-static void use_dh_group(private_rekey_ike_sa_t *this, diffie_hellman_group_t dh_group)
-{
- this->diffie_hellman = diffie_hellman_create(dh_group);
-}
-
-/**
- * Implementation of rekey_ike_sa_t.cancel.
- */
-static void cancel(private_rekey_ike_sa_t *this)
-{
- this->lost = TRUE;
-}
-
-/**
- * Implementation of transaction_t.get_request.
- */
-static status_t get_request(private_rekey_ike_sa_t *this, message_t **result)
-{
- message_t *request;
- host_t *me, *other;
-
- /* check if we already have built a message (retransmission) */
- if (this->message)
- {
- *result = this->message;
- return SUCCESS;
- }
-
- /* check for correct state, except when retrying with another dh group */
- if (this->ike_sa->get_state(this->ike_sa) != IKE_ESTABLISHED &&
- !this->diffie_hellman)
- {
- DBG1(DBG_IKE, "tried to rekey in state %N, aborted",
- ike_sa_state_names, this->ike_sa->get_state(this->ike_sa));
- return FAILED;
- }
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
-
- /* build the request */
- request = message_create();
- request->set_source(request, me->clone(me));
- request->set_destination(request, other->clone(other));
- request->set_exchange_type(request, CREATE_CHILD_SA);
- request->set_request(request, TRUE);
- request->set_ike_sa_id(request, this->ike_sa->get_id(this->ike_sa));
- *result = request;
- this->message = request;
-
- { /* build SA payload */
- sa_payload_t *sa_payload;
- linked_list_t *proposals;
- ike_sa_id_t *ike_sa_id;
- iterator_t *iterator;
- proposal_t *proposal;
- u_int64_t spi;
-
- /* get a connection to replace current IKE_SA */
- this->connection = charon->connections->get_connection_by_name(
- charon->connections,
- this->ike_sa->get_name(this->ike_sa));
- /* if connection lookup by name fails, try it with the hosts */
- if (this->connection == NULL)
- {
- this->connection = charon->connections->get_connection_by_hosts(
- charon->connections,
- me, other);
- if (this->connection == NULL)
- {
- DBG1(DBG_IKE, "no connection found to rekey IKE_SA");
- return FAILED;
- }
- }
-
- /* create a new SA */
- ike_sa_id = ike_sa_id_create(0, 0, TRUE);
- this->new_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager,
- ike_sa_id);
- spi = ike_sa_id->get_initiator_spi(ike_sa_id);
- ike_sa_id->destroy(ike_sa_id);
-
- proposals = this->connection->get_proposals(this->connection);
- iterator = proposals->create_iterator(proposals, TRUE);
- while (iterator->iterate(iterator, (void**)&proposal))
- {
- proposal->set_spi(proposal, spi);
- }
- iterator->destroy(iterator);
-
- sa_payload = sa_payload_create_from_proposal_list(proposals);
- proposals->destroy_offset(proposals, offsetof(proposal_t, destroy));
- request->add_payload(request, (payload_t*)sa_payload);
- }
-
- { /* build the NONCE payload for us (initiator) */
- nonce_payload_t *nonce_payload;
-
- if (this->randomizer->allocate_pseudo_random_bytes(this->randomizer,
- NONCE_SIZE, &this->nonce_i) != SUCCESS)
- {
- return FAILED;
- }
- nonce_payload = nonce_payload_create();
- nonce_payload->set_nonce(nonce_payload, this->nonce_i);
- request->add_payload(request, (payload_t*)nonce_payload);
- }
-
- /* if the DH group is set via use_dh_group(), we already have a DH object */
- if (!this->diffie_hellman)
- {
- diffie_hellman_group_t dh_group;
-
- dh_group = this->connection->get_dh_group(this->connection);
- this->diffie_hellman = diffie_hellman_create(dh_group);
- if (this->diffie_hellman == NULL)
- {
- DBG1(DBG_IKE, "DH group %N not supported, aborting",
- diffie_hellman_group_names, dh_group);
- return FAILED;
- }
- }
-
- { /* build the KE payload from the DH object */
- ke_payload_t *ke_payload;
-
- ke_payload = ke_payload_create_from_diffie_hellman(this->diffie_hellman);
- request->add_payload(request, (payload_t*)ke_payload);
- }
-
- this->message_id = this->ike_sa->get_next_message_id(this->ike_sa);
- request->set_message_id(request, this->message_id);
-
- /* register us as rekeying to detect multiple rekeying */
- this->ike_sa->set_state(this->ike_sa, IKE_REKEYING);
- this->ike_sa->set_rekeying_transaction(this->ike_sa, &this->public.transaction);
-
- return SUCCESS;
-}
-
-/**
- * Handle all kind of notifys
- */
-static status_t process_notifys(private_rekey_ike_sa_t *this, notify_payload_t *notify_payload)
-{
- notify_type_t notify_type = notify_payload->get_notify_type(notify_payload);
-
- DBG2(DBG_IKE,"process notify type %N", notify_type_names, notify_type);
-
- switch (notify_type)
- {
- case NO_PROPOSAL_CHOSEN:
- {
- DBG1(DBG_IKE, "received a NO_PROPOSAL_CHOSEN notify, IKE_SA rekeying failed");
- return FAILED;
- }
- case INVALID_KE_PAYLOAD:
- {
- chunk_t notify_data;
- diffie_hellman_group_t dh_group, old_dh_group;
- rekey_ike_sa_t *retry;
-
- old_dh_group = this->connection->get_dh_group(this->connection);
- notify_data = notify_payload->get_notification_data(notify_payload);
- dh_group = ntohs(*((u_int16_t*)notify_data.ptr));
-
- DBG1(DBG_IKE, "peer didn't accept DH group %N, it requested %N",
- diffie_hellman_group_names, old_dh_group,
- diffie_hellman_group_names, dh_group);
- if (!this->connection->check_dh_group(this->connection, dh_group))
- {
- DBG1(DBG_IKE, "requested DH group not acceptable, IKE_SA rekeying failed");
- return FAILED;
- }
- retry = rekey_ike_sa_create(this->ike_sa);
- retry->use_dh_group(retry, dh_group);
- *this->next = (transaction_t*)retry;
- return FAILED;
- }
- default:
- {
- if (notify_type < 16383)
- {
- DBG1(DBG_IKE, "received %N notify error, IKE_SA rekeying failed",
- notify_type_names, notify_type);
- return FAILED;
- }
- else
- {
- DBG1(DBG_IKE, "received %N notify, ignored",
- notify_type_names, notify_type);
- return SUCCESS;
- }
- }
- }
-}
-
-/**
- * Switch to the newly created IKE_SA
- */
-static status_t switchto_new_sa(private_rekey_ike_sa_t* this, bool initiator)
-{
- identification_t *my_id, *other_id;
- host_t *my_host, *other_host;
- char *name;
-
- my_id = this->ike_sa->get_my_id(this->ike_sa);
- other_id = this->ike_sa->get_other_id(this->ike_sa);
- my_host = this->ike_sa->get_my_host(this->ike_sa);
- other_host = this->ike_sa->get_other_host(this->ike_sa);
- name = this->ike_sa->get_name(this->ike_sa);
-
- this->new_sa->set_my_id(this->new_sa, my_id->clone(my_id));
- this->new_sa->set_other_id(this->new_sa, other_id->clone(other_id));
- this->new_sa->set_my_host(this->new_sa, my_host->clone(my_host));
- this->new_sa->set_other_host(this->new_sa, other_host->clone(other_host));
- this->new_sa->set_name(this->new_sa, name);
-
- if (this->new_sa->derive_keys(this->new_sa, this->proposal,
- this->diffie_hellman,
- this->nonce_i, this->nonce_r, initiator,
- this->ike_sa->get_child_prf(this->ike_sa),
- this->ike_sa->get_prf(this->ike_sa)
- ) != SUCCESS)
- {
- return FAILED;
- }
-
- this->new_sa->apply_connection(this->new_sa, this->connection);
- this->new_sa->set_state(this->new_sa, IKE_ESTABLISHED);
- this->new_sa->set_lifetimes(this->new_sa,
- this->connection->get_reauth(this->connection),
- this->connection->get_soft_lifetime(this->connection),
- this->connection->get_hard_lifetime(this->connection));
- return SUCCESS;
-}
-
-/**
- * Build a notify message.
- */
-static void build_notify(notify_type_t type, chunk_t data, message_t *message, bool flush_message)
-{
- notify_payload_t *notify;
-
- if (flush_message)
- {
- payload_t *payload;
- iterator_t *iterator = message->get_payload_iterator(message);
- while (iterator->iterate(iterator, (void**)&payload))
- {
- payload->destroy(payload);
- iterator->remove(iterator);
- }
- iterator->destroy(iterator);
- }
-
- notify = notify_payload_create();
- notify->set_notify_type(notify, type);
- notify->set_notification_data(notify, data);
- message->add_payload(message, (payload_t*)notify);
-}
-
-/**
- * Implementation of transaction_t.get_response.
- */
-static status_t get_response(private_rekey_ike_sa_t *this, message_t *request,
- message_t **result, transaction_t **next)
-{
- host_t *me, *other;
- message_t *response;
- status_t status;
- iterator_t *payloads, *iterator;
- payload_t *payload;
- child_sa_t *child_sa;
- sa_payload_t *sa_request = NULL;
- nonce_payload_t *nonce_request = NULL;
- ke_payload_t *ke_request = NULL;
- nonce_payload_t *nonce_response;
-
- /* check if we already have built a response (retransmission) */
- if (this->message)
- {
- *result = this->message;
- return SUCCESS;
- }
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
- this->message_id = request->get_message_id(request);
-
- /* set up response */
- response = message_create();
- response->set_source(response, me->clone(me));
- response->set_destination(response, other->clone(other));
- response->set_exchange_type(response, CREATE_CHILD_SA);
- response->set_request(response, FALSE);
- response->set_message_id(response, this->message_id);
- response->set_ike_sa_id(response, this->ike_sa->get_id(this->ike_sa));
- this->message = response;
- *result = response;
-
- /* check message type */
- if (request->get_exchange_type(request) != CREATE_CHILD_SA)
- {
- DBG1(DBG_IKE, "CREATE_CHILD_SA response of invalid type, aborted");
- return FAILED;
- }
-
- /* if we already initiate a delete, we do not allow rekeying */
- if (this->ike_sa->get_state(this->ike_sa) == IKE_DELETING)
- {
- build_notify(NO_PROPOSAL_CHOSEN, chunk_empty, response, TRUE);
- DBG1(DBG_IKE, "unable to rekey, as delete in progress. Sending NO_PROPOSAL_CHOSEN");
- return FAILED;
- }
-
- /* if we have a CHILD which is "half-open", we do not allow rekeying */
- iterator = this->ike_sa->create_child_sa_iterator(this->ike_sa);
- while (iterator->iterate(iterator, (void**)&child_sa))
- {
- child_sa_state_t state = child_sa->get_state(child_sa);
- if (state == CHILD_CREATED ||
- state == CHILD_REKEYING ||
- state == CHILD_DELETING)
- {
- build_notify(NO_PROPOSAL_CHOSEN, chunk_empty, response, TRUE);
- DBG1(DBG_IKE, "unable to rekey, one CHILD_SA is half open. Sending NO_PROPOSAL_CHOSEN");
- iterator->destroy(iterator);
- return FAILED;
- }
- }
- iterator->destroy(iterator);
-
- /* apply for notify processing */
- this->next = next;
-
-
- /* get a connection to replace current IKE_SA */
- this->connection = charon->connections->get_connection_by_name(
- charon->connections, this->ike_sa->get_name(this->ike_sa));
- /* if connection lookup by name fails, try it with the hosts */
- if (this->connection == NULL)
- {
- this->connection = charon->connections->get_connection_by_hosts(
- charon->connections, me, other);
- if (this->connection == NULL)
- {
- DBG1(DBG_IKE, "no connection found to rekey IKE_SA, sending NO_RROPOSAL_CHOSEN");
- build_notify(NO_PROPOSAL_CHOSEN, chunk_empty, response, TRUE);
- return FAILED;
- }
- }
-
- /* Iterate over all payloads. */
- payloads = request->get_payload_iterator(request);
- while (payloads->iterate(payloads, (void**)&payload))
- {
- switch (payload->get_type(payload))
- {
- case SECURITY_ASSOCIATION:
- sa_request = (sa_payload_t*)payload;
- break;
- case NONCE:
- nonce_request = (nonce_payload_t*)payload;
- break;
- case KEY_EXCHANGE:
- {
- ke_request = (ke_payload_t*)payload;
- break;
- }
- case NOTIFY:
- {
- status = process_notifys(this, (notify_payload_t*)payload);
- if (status != SUCCESS)
- {
- payloads->destroy(payloads);
- return status;
- }
- break;
- }
- default:
- {
- DBG1(DBG_IKE, "ignoring %N payload",
- payload_type_names, payload->get_type(payload));
- break;
- }
- }
- }
- payloads->destroy(payloads);
-
- /* check if we have all payloads */
- if (!(sa_request && nonce_request && ke_request))
- {
- build_notify(INVALID_SYNTAX, chunk_empty, response, TRUE);
- DBG1(DBG_IKE, "request message incomplete, IKE_SA rekeying failed");
- return FAILED;
- }
-
- { /* process nonce payload */
- this->nonce_i = nonce_request->get_nonce(nonce_request);
- if (this->randomizer->allocate_pseudo_random_bytes(this->randomizer,
- NONCE_SIZE, &this->nonce_r) != SUCCESS)
- {
- build_notify(NO_PROPOSAL_CHOSEN, chunk_empty, response, TRUE);
- return FAILED;
- }
- nonce_response = nonce_payload_create();
- nonce_response->set_nonce(nonce_response, this->nonce_r);
- }
-
- { /* process SA payload */
- linked_list_t *proposal_list;
- sa_payload_t *sa_response;
- u_int64_t spi;
- ike_sa_id_t *ike_sa_id;
-
- sa_response = sa_payload_create();
- /* get proposals from request, and select one with ours */
- proposal_list = sa_request->get_proposals(sa_request);
- DBG2(DBG_IKE, "selecting proposals:");
- this->proposal = this->connection->select_proposal(this->connection, proposal_list);
- proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
-
- /* do we have a proposal? */
- if (this->proposal == NULL)
- {
- DBG1(DBG_IKE, "no proposals acceptable to rekey IKE_SA, sending NO_PROPOSAL_CHOSEN");
- build_notify(NO_PROPOSAL_CHOSEN, chunk_empty, response, TRUE);
- return FAILED;
- }
-
- /* create IKE_SA with new SPIs */
- spi = this->proposal->get_spi(this->proposal);
- ike_sa_id = ike_sa_id_create(spi, 0, FALSE);
- this->new_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager,
- ike_sa_id);
- spi = ike_sa_id->get_responder_spi(ike_sa_id);
- ike_sa_id->destroy(ike_sa_id);
- this->proposal->set_spi(this->proposal, spi);
-
- sa_response->add_proposal(sa_response, this->proposal);
- response->add_payload(response, (payload_t*)sa_response);
- /* add nonce after sa payload */
- response->add_payload(response, (payload_t *)nonce_response);
- }
-
- { /* process KE payload */
- diffie_hellman_group_t used_group;
- ke_payload_t *ke_response;
-
- used_group = ke_request->get_dh_group_number(ke_request);
-
- if (!this->connection->check_dh_group(this->connection, used_group) ||
- (this->diffie_hellman = diffie_hellman_create(used_group)) == NULL)
- {
- u_int16_t notify_group;
- chunk_t notify_chunk;
-
- notify_group = this->connection->get_dh_group(this->connection);
- DBG1(DBG_IKE, "request used inacceptable DH group %N, sending "
- "INVALID_KE_PAYLOAD with %N",
- diffie_hellman_group_names, used_group,
- diffie_hellman_group_names, notify_group);
-
- notify_group = htons(notify_group);
- notify_chunk.ptr = (u_int8_t*)&notify_group;
- notify_chunk.len = sizeof(notify_group);
- build_notify(INVALID_KE_PAYLOAD, notify_chunk, response, TRUE);
- return FAILED;
- }
- this->diffie_hellman->set_other_public_value(this->diffie_hellman,
- ke_request->get_key_exchange_data(ke_request));
-
- /* build response */
- ke_response = ke_payload_create_from_diffie_hellman(this->diffie_hellman);
- response->add_payload(response, (payload_t*)ke_response);
- }
-
- status = switchto_new_sa(this, FALSE);
- if (status != SUCCESS)
- {
- return status;
- }
-
- /* IKE_SA successfully created. If another transaction is already rekeying
- * this SA, our lower nonce must be registered for a later nonce compare. */
- {
- private_rekey_ike_sa_t *other;
-
- other = (private_rekey_ike_sa_t*)
- this->ike_sa->get_rekeying_transaction(this->ike_sa);
- if (other)
- {
- /* store our lower nonce in the simultaneus transaction, we
- * will later compare it against his nonces when we calls conclude().
- * We do not adopt childrens yet, as we don't know if we'll win
- * the race...
- */
- if (memcmp(this->nonce_i.ptr, this->nonce_r.ptr,
- min(this->nonce_i.len, this->nonce_r.len)) < 0)
- {
- other->nonce_s = chunk_clone(this->nonce_i);
- }
- else
- {
- other->nonce_s = chunk_clone(this->nonce_r);
- }
- /* overwrite "other" in IKE_SA, allows "other" to access "this" */
- this->ike_sa->set_rekeying_transaction(this->ike_sa, &this->public.transaction);
- }
- else
- {
- /* if we have no simultaneus transaction, we can safely adopt
- * all children and complete. */
- this->new_sa->adopt_children(this->new_sa, this->ike_sa);
- charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa);
- this->new_sa = NULL;
- }
- this->ike_sa->set_state(this->ike_sa, IKE_REKEYING);
- }
-
- return SUCCESS;
-}
-
-/**
- * Implementation of transaction_t.conclude
- */
-static status_t conclude(private_rekey_ike_sa_t *this, message_t *response,
- transaction_t **next)
-{
- iterator_t *payloads;
- payload_t *payload;
- host_t *me, *other;
- sa_payload_t *sa_payload = NULL;
- nonce_payload_t *nonce_payload = NULL;
- ke_payload_t *ke_payload = NULL;
- private_rekey_ike_sa_t *other_trans;
- status_t status;
-
- /* check message type */
- if (response->get_exchange_type(response) != CREATE_CHILD_SA)
- {
- DBG1(DBG_IKE, "CREATE_CHILD_SA response of invalid type, aborting");
- return FAILED;
- }
-
- me = this->ike_sa->get_my_host(this->ike_sa);
- other = this->ike_sa->get_other_host(this->ike_sa);
-
- /* apply for notify processing */
- this->next = next;
-
- /* Iterate over all payloads to collect them */
- payloads = response->get_payload_iterator(response);
- while (payloads->iterate(payloads, (void**)&payload))
- {
- switch (payload->get_type(payload))
- {
- case SECURITY_ASSOCIATION:
- sa_payload = (sa_payload_t*)payload;
- break;
- case NONCE:
- nonce_payload = (nonce_payload_t*)payload;
- break;
- case KEY_EXCHANGE:
- ke_payload = (ke_payload_t*)payload;
- break;
- case NOTIFY:
- {
- status = process_notifys(this, (notify_payload_t*)payload);
- if (status != SUCCESS)
- {
- payloads->destroy(payloads);
- return status;
- }
- break;
- }
- default:
- {
- DBG1(DBG_IKE, "ignoring %N payload",
- payload_type_names, payload->get_type(payload));
- break;
- }
- }
- }
- payloads->destroy(payloads);
-
- if (!(sa_payload && nonce_payload && ke_payload))
- {
- DBG1(DBG_IKE, "response message incomplete, rekeying IKE_SA failed");
- return FAILED;
- }
-
- { /* process NONCE payload */
- this->nonce_r = nonce_payload->get_nonce(nonce_payload);
- }
-
- { /* process SA payload */
- linked_list_t *proposal_list;
- ike_sa_id_t *ike_sa_id;
- u_int64_t spi;
-
- proposal_list = sa_payload->get_proposals(sa_payload);
- /* we have to re-check here if other's selection is valid */
- this->proposal = this->connection->select_proposal(this->connection, proposal_list);
- proposal_list->destroy_offset(proposal_list, offsetof(proposal_t, destroy));
-
- if (this->proposal == NULL)
- {
- DBG1(DBG_IKE, "no proposal selected, rekeying IKE_SA failed");
- return FAILED;
- }
- spi = this->proposal->get_spi(this->proposal);
- ike_sa_id = this->new_sa->get_id(this->new_sa);
- ike_sa_id->set_responder_spi(ike_sa_id, spi);
- }
-
- { /* process KE payload */
- this->diffie_hellman->set_other_public_value(this->diffie_hellman,
- ke_payload->get_key_exchange_data(ke_payload));
- }
-
- if (switchto_new_sa(this, TRUE) != SUCCESS)
- {
- /* this should not happen. But if, we destroy the new SAs */
- this->new_sa->set_state(this->new_sa, IKE_REKEYING);
- *next = (transaction_t*)delete_ike_sa_create(this->new_sa);
- return FAILED;
- }
-
- /* IKE_SA successfully created. If the other peer initiated rekeying
- * in the meantime, we detect this by comparing the rekeying_transaction
- * of the SA. If it changed, we are not alone. Then we must compare the nonces.
- * If no simultaneous rekeying is going on, we just initiate the delete of
- * the superseded SA. */
- other_trans = (private_rekey_ike_sa_t*)
- this->ike_sa->get_rekeying_transaction(this->ike_sa);
- this->ike_sa->set_rekeying_transaction(this->ike_sa, NULL);
-
- if (this->nonce_s.ptr)
- { /* simlutaneous rekeying is going on, not so good */
- chunk_t this_lowest;
-
- /* first get our lowest nonce */
- if (memcmp(this->nonce_i.ptr, this->nonce_r.ptr,
- min(this->nonce_i.len, this->nonce_r.len)) < 0)
- {
- this_lowest = this->nonce_i;
- }
- else
- {
- this_lowest = this->nonce_r;
- }
- /* then compare against other lowest nonce */
- if (memcmp(this_lowest.ptr, this->nonce_s.ptr,
- min(this_lowest.len, this->nonce_s.len)) < 0)
- {
- DBG1(DBG_IKE, "detected simultaneous IKE_SA rekeying, deleting ours");
- this->lost = TRUE;
- }
- else
- {
- DBG1(DBG_IKE, "detected simultaneous IKE_SA rekeying, but ours is preferred");
- }
- if (this->lost)
- {
- /* the other has won, he gets our children */
- other_trans->new_sa->adopt_children(other_trans->new_sa, this->ike_sa);
- /* we have lost simlutaneous rekeying, delete the SA we just have created */
- this->new_sa->set_state(this->new_sa, IKE_REKEYING);
- *next = (transaction_t*)delete_ike_sa_create(this->new_sa);
- }
- else
- {
- other_trans->new_sa->set_state(other_trans->new_sa, IKE_REKEYING);
- }
- /* other trans' SA is still not checked in, so do it now. It's SA will get
- * deleted by remote peer. */
- charon->ike_sa_manager->checkin(charon->ike_sa_manager, other_trans->new_sa);
- other_trans->new_sa = NULL;
- }
-
- if (!this->lost)
- {
- /* we have won. delete old IKE_SA, and migrate all children */
- this->new_sa->adopt_children(this->new_sa, this->ike_sa);
- *next = (transaction_t*)delete_ike_sa_create(this->ike_sa);
- }
-
- charon->ike_sa_manager->checkin(charon->ike_sa_manager, this->new_sa);
- this->new_sa = NULL;
-
- return SUCCESS;
-}
-
-/**
- * implements transaction_t.destroy
- */
-static void destroy(private_rekey_ike_sa_t *this)
-{
- if (this->new_sa)
- {
- charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager,
- this->new_sa);
- }
- DESTROY_IF(this->message);
- DESTROY_IF(this->connection);
- DESTROY_IF(this->diffie_hellman);
- DESTROY_IF(this->proposal);
- chunk_free(&this->nonce_i);
- chunk_free(&this->nonce_r);
- chunk_free(&this->nonce_s);
- this->randomizer->destroy(this->randomizer);
- free(this);
-}
-
-/*
- * Described in header.
- */
-rekey_ike_sa_t *rekey_ike_sa_create(ike_sa_t *ike_sa)
-{
- private_rekey_ike_sa_t *this = malloc_thing(private_rekey_ike_sa_t);
-
- /* transaction interface functions */
- this->public.transaction.get_request = (status_t(*)(transaction_t*,message_t**))get_request;
- this->public.transaction.get_response = (status_t(*)(transaction_t*,message_t*,message_t**,transaction_t**))get_response;
- this->public.transaction.conclude = (status_t(*)(transaction_t*,message_t*,transaction_t**))conclude;
- this->public.transaction.get_message_id = (u_int32_t(*)(transaction_t*))get_message_id;
- this->public.transaction.requested = (u_int32_t(*)(transaction_t*))requested;
- this->public.transaction.destroy = (void(*)(transaction_t*))destroy;
-
- /* public functions */
- this->public.use_dh_group = (void(*)(rekey_ike_sa_t*,diffie_hellman_group_t))use_dh_group;
- this->public.cancel = (void(*)(rekey_ike_sa_t*))cancel;
-
- /* private data */
- this->ike_sa = ike_sa;
- this->message_id = 0;
- this->message = NULL;
- this->requested = 0;
- this->nonce_i = chunk_empty;
- this->nonce_r = chunk_empty;
- this->nonce_s = chunk_empty;
- this->new_sa = NULL;
- this->lost = FALSE;
- this->connection = NULL;
- this->randomizer = randomizer_create();
- this->diffie_hellman = NULL;
- this->proposal = NULL;
-
- return &this->public;
-}
diff --git a/src/charon/sa/transactions/rekey_ike_sa.h b/src/charon/sa/transactions/rekey_ike_sa.h
deleted file mode 100644
index dbb65237e..000000000
--- a/src/charon/sa/transactions/rekey_ike_sa.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/**
- * @file rekey_ike_sa.h
- *
- * @brief Interface of transaction rekey_ike_sa.
- *
- */
-
-/*
- * Copyright (C) 2006 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * 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.
- */
-
-#ifndef REKEY_IKE_SA_H
-#define REKEY_IKE_SA_H
-
-typedef struct rekey_ike_sa_t rekey_ike_sa_t;
-
-#include <sa/ike_sa.h>
-#include <sa/transactions/transaction.h>
-#include <crypto/diffie_hellman.h>
-
-
-/**
- * @brief A transaction to rekey an established IKE_SA
- *
- * @b Constructors:
- * - rekey_ike_sa_create()
- * - transaction_create() with the appropriate message
- *
- * @ingroup transactions
- */
-struct rekey_ike_sa_t {
-
- /**
- * The transaction_t interface.
- */
- transaction_t transaction;
-
- /**
- * @brief Set the Diffie Hellman group to use for initiating.
- *
- * If a first exchange fails with a INVALID_KE_PAYLOAD, the second
- * try uses the DH group proposed by the responder.
- *
- * @param this calling object
- * @param dh_group diffie hellman group to use
- */
- void (*use_dh_group) (rekey_ike_sa_t* this, diffie_hellman_group_t dh_group);
-
- /**
- * @brief Cancel the request.
- *
- * Cancelling the request will set a flag in the transaction.
- *
- * @param this calling object
- * @param child_sa CHILD_SA to rekey
- */
- void (*cancel) (rekey_ike_sa_t* this);
-};
-
-/**
- * @brief Create a new transaction to rekey an existing IKE_SA.
- *
- * @param ike_sa existing IKE_SA
- * @return created rekey_ike_sa transaction
- *
- * @ingroup transactions
- */
-rekey_ike_sa_t *rekey_ike_sa_create(ike_sa_t *ike_sa);
-
-#endif /* REKEY_IKE_SA_H */
diff --git a/src/charon/sa/transactions/transaction.c b/src/charon/sa/transactions/transaction.c
deleted file mode 100644
index d4da1cdcb..000000000
--- a/src/charon/sa/transactions/transaction.c
+++ /dev/null
@@ -1,167 +0,0 @@
-/**
- * @file transaction.c
- *
- * @brief Generic contstructor for the different transaction types.
- *
- */
-
-/*
- * Copyright (C) 2006 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * 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 "transaction.h"
-
-#include <sa/child_sa.h>
-#include <sa/transactions/ike_sa_init.h>
-#include <sa/transactions/ike_auth.h>
-#include <sa/transactions/delete_ike_sa.h>
-#include <sa/transactions/create_child_sa.h>
-#include <sa/transactions/delete_child_sa.h>
-#include <sa/transactions/dead_peer_detection.h>
-#include <sa/transactions/rekey_ike_sa.h>
-#include <encoding/payloads/ts_payload.h>
-#include <encoding/payloads/sa_payload.h>
-#include <encoding/payloads/nonce_payload.h>
-#include <encoding/payloads/notify_payload.h>
-#include <encoding/payloads/delete_payload.h>
-
-/*
- * see header file
- */
-transaction_t *transaction_create(ike_sa_t *ike_sa, message_t *request)
-{
- iterator_t *iterator;
- payload_t *current;
- transaction_t *transaction = NULL;
-
- if (!request->get_request(request))
- {
- return NULL;
- }
-
- switch (request->get_exchange_type(request))
- {
- case IKE_SA_INIT:
- {
- if (ike_sa->get_state(ike_sa) == IKE_CREATED)
- {
- transaction = (transaction_t*)ike_sa_init_create(ike_sa);
- }
- break;
- }
- case IKE_AUTH:
- {
- /* IKE_AUTH is always created in IKE_SA_INIT, it never should
- * appear alone */
- break;
- }
- case CREATE_CHILD_SA:
- {
- if (ike_sa->get_state(ike_sa) < IKE_ESTABLISHED)
- {
- break;
- }
- /* check protocol of SA payload */
- iterator = request->get_payload_iterator(request);
- while (iterator->iterate(iterator, (void**)&current))
- {
- if (current->get_type(current) == SECURITY_ASSOCIATION)
- {
- iterator_t *prop_iter;
- proposal_substructure_t *prop_struct;
- sa_payload_t *sa_payload = (sa_payload_t*)current;
-
- prop_iter = sa_payload->create_proposal_substructure_iterator(sa_payload, TRUE);
- if (prop_iter->iterate(prop_iter, (void**)&prop_struct))
- {
- switch (prop_struct->get_protocol_id(prop_struct))
- {
- case PROTO_IKE:
- transaction = (transaction_t*)
- rekey_ike_sa_create(ike_sa);
- break;
- case PROTO_AH:
- case PROTO_ESP:
- transaction = (transaction_t*)
- create_child_sa_create(ike_sa);
- break;
- default:
- break;
- }
- }
- prop_iter->destroy(prop_iter);
- }
- if (transaction)
- {
- break;
- }
- }
- iterator->destroy(iterator);
- break;
- }
- case INFORMATIONAL:
- {
- if (ike_sa->get_state(ike_sa) < IKE_ESTABLISHED)
- {
- break;
- }
- u_int payload_count = 0;
- iterator = request->get_payload_iterator(request);
- while (iterator->iterate(iterator, (void**)&current))
- {
- payload_count++;
- switch (current->get_type(current))
- {
- case DELETE:
- {
- delete_payload_t *delete_payload = (delete_payload_t*)current;
- switch (delete_payload->get_protocol_id(delete_payload))
- {
- case PROTO_IKE:
- transaction = (transaction_t*)
- delete_ike_sa_create(ike_sa);
- break;
- case PROTO_AH:
- case PROTO_ESP:
- transaction = (transaction_t*)
- delete_child_sa_create(ike_sa);
- break;
- default:
- break;
- }
- break;
- }
- default:
- break;
- }
- if (transaction)
- {
- break;
- }
- }
- iterator->destroy(iterator);
- /* empty informationals are used for dead peer detection in
- * IKEv2. We use a special transaction for it. */
- if (payload_count == 0)
- {
- transaction = (transaction_t*)
- dead_peer_detection_create(ike_sa);
- }
- break;
- }
- default:
- break;
- }
- return transaction;
-}
diff --git a/src/charon/sa/transactions/transaction.h b/src/charon/sa/transactions/transaction.h
deleted file mode 100644
index 4a401e12b..000000000
--- a/src/charon/sa/transactions/transaction.h
+++ /dev/null
@@ -1,180 +0,0 @@
-/**
- * @file transaction.h
- *
- * @brief Interface transaction_t.
- *
- */
-
-/*
- * Copyright (C) 2006 Martin Willi
- * Hochschule fuer Technik Rapperswil
- *
- * 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.
- */
-
-#ifndef TRANSACTION_H_
-#define TRANSACTION_H_
-
-typedef struct transaction_t transaction_t;
-
-#include <library.h>
-#include <encoding/message.h>
-#include <sa/ike_sa.h>
-
-/**
- * @brief This interface represents a transaction an established IKE_SA can do.
- *
- * To every transaction, a message ID is associated. IKEv2 uses strict message
- * IDs, which are equal for a request/response pair in a transaction.
- * An initiator of a transaction does the following:
- * - create the transaction using a specific constructor
- * - call request() to get the message for initiaton
- * - call conclude() to process received reply
- * The other peer does the following:
- * - create a transanction using the generic transaction constructor
- * - call respond() to get a reply to send
- *
- * The responder must not destroy the transaction, until the
- * initiator initiates another transaction (or a number of transactions
- * > window size). This allows us to redo a transaction in case of a
- * message loss. The initiator can destroy the the transaction once
- * the conclude() function is called.
- *
- * @b Constructors:
- * - transaction_create()
- * - ike_sa_init_create()
- * - ike_auth_create()
- *
- * @ingroup transactions
- */
-struct transaction_t {
-
- /**
- * @brief Get the request to use for initiating the transaction.
- *
- * A transaction creates a request only once. The request is stored
- * internally and may be queried multiple times for retransmission.
- * The transaction is not responsible for generating/encrypting the
- * message, this is the job of the caller. But it MAY be already
- * generated when calling get_request() the second time.
- *
- * @param this calling object
- * @param[out] request resultin request
- * @return
- * - FAILED if transaction failed
- * - DESTROY_ME if transaction failed and IKE SA
- * must be deleted
- * - SUCCESS
- */
- status_t (*get_request) (transaction_t *this, message_t **request);
-
- /**
- * @brief Build the response for a received request.
- *
- * A transaction creates a response only once for a unique request.
- * This allows the use of get_response multiple times for retransmission
- * purposes.
- * The transaction is not responsible for generating/encrypting the
- * response, nor is it responsible for decrypting/parsing the request.
- * This is the job of the caller. But the response MAY be already
- * generated when calling get_request() the second time.
- * The initiator waits for a response, so we send one in every case. This
- * means response points always to a valid message. This message
- * may not be modified or destroyed, it gets destroyed along with the
- * transaction.
- * The get_response() function may return a next transaction. This allows
- * passing of informations from one transaction to a next one.
- *
- * @param this calling object
- * @param request received request
- * @param[out] response resulting response
- * @param[out] next transaction expected as next, or NULL
- * @return
- * - FAILED if transaction failed
- * - DESTROY_ME if transaction failed and IKE SA
- * must be deleted
- * - SUCCESS
- */
- status_t (*get_response) (transaction_t *this, message_t *request,
- message_t **response, transaction_t **next);
-
- /**
- * @brief Conclude an initiated transaction with a received response.
- *
- * The response must be decrypted and parsed. The conclude function
- * may return a new transaction. This transaction has to be executed
- * next to complete a multi-exchange scenario. It allows a clean
- * transaction mechanism, as the transaction knows best whats to do
- * after it completes. It must only be executed if conclude returns
- * SUCCESS.
- *
- * @param this calling object
- * @param response received response
- * @param[out] next transaction to execute as next, or NULL
- * @return
- * - FAILED if transaction failed
- * - DESTROY_ME if transaction failed and IKE SA
- * must be deleted
- * - SUCCESS
- */
- status_t (*conclude) (transaction_t *this, message_t *response,
- transaction_t **next);
-
- /**
- * @brief Get the message ID associated with this transaction.
- *
- * Every transaction consists of a message pair with the same
- * message ID. This ID can be queried with get_message_id().
- *
- * @param this calling object
- * @return message id
- */
- u_int32_t (*get_message_id) (transaction_t *this);
-
- /**
- * @brief Times we already sent the request (retransmitted).
- *
- * The transaction stores an internal counter to see how
- * many times we sent the request. This counter is incremented
- * each time after a call to requested().
- *
- * @param this calling object
- * @return message id
- */
- u_int32_t (*requested) (transaction_t *this);
-
- /**
- * @brief Destroys a transaction_t object.
- *
- * @param this calling object
- */
- void (*destroy) (transaction_t *this);
-};
-
-/**
- * @brief Create a transaction instance based on a received request.
- *
- * Incoming requests are handled by a transaction. But as we don't
- * know what kind of transaction we use for a specific request, we use
- * a generic constructor. This constructor decides which instance will
- * handle the transaction, and creates it.
- *
- * @param ike_sa ike_sa associated with this transaction
- * @param request received request
- * @return
- * - created transaction, or
- * - NULL no transaction needed
- *
- * @ingroup transactions
- */
-transaction_t *transaction_create(ike_sa_t *ike_sa, message_t* request);
-
-#endif /* TRANSACTION_H_ */