merged tasking branch into trunk
This commit is contained in:
+49
-98
@@ -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;
|
||||
@@ -133,35 +128,10 @@ 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);;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -233,6 +193,14 @@ static child_sa_state_t get_state(private_child_sa_t *this)
|
||||
return this->state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements child_sa_t.get_policy
|
||||
*/
|
||||
static policy_t* get_policy(private_child_sa_t *this)
|
||||
{
|
||||
return this->policy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the up/down script
|
||||
*/
|
||||
@@ -240,8 +208,11 @@ 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;
|
||||
@@ -704,22 +674,6 @@ static linked_list_t *get_other_traffic_selectors(private_child_sa_t *this)
|
||||
return this->other_ts;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+13
-36
@@ -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_*/
|
||||
|
||||
+700
-860
File diff suppressed because it is too large
Load Diff
+126
-103
@@ -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()
|
||||
@@ -140,6 +140,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.
|
||||
*
|
||||
@@ -164,14 +172,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.
|
||||
*
|
||||
@@ -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.
|
||||
@@ -308,17 +340,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.
|
||||
*
|
||||
@@ -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);
|
||||
|
||||
@@ -463,15 +495,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.
|
||||
*
|
||||
@@ -536,22 +559,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.
|
||||
*
|
||||
@@ -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 Move all children from other IKE_SA to this IKE_SA.
|
||||
* @brief Add a DNS server to the system.
|
||||
*
|
||||
* After rekeying completes, all children are switched over to the
|
||||
* newly created IKE_SA.
|
||||
* 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 stepfather
|
||||
* @param other deceased (rekeyed) IKE_SA
|
||||
* @param this calling object
|
||||
* @param dns DNS server to install on the system
|
||||
*/
|
||||
void (*adopt_children) (ike_sa_t *this, ike_sa_t *other);
|
||||
void (*add_dns_server) (ike_sa_t *this, host_t *dns);
|
||||
|
||||
/**
|
||||
* @brief Inherit all attributes of other to this after rekeying.
|
||||
*
|
||||
* 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 calling object
|
||||
*/
|
||||
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
|
||||
*
|
||||
|
||||
+224
-293
@@ -296,14 +296,135 @@ static u_int64_t get_next_spi(private_ike_sa_manager_t *this)
|
||||
return spi;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,120 +508,15 @@ 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;
|
||||
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;
|
||||
}
|
||||
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 && 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");
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&(this->mutex));
|
||||
|
||||
charon->bus->set_sa(charon->bus, ike_sa);
|
||||
return ike_sa;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of of ike_sa_manager.checkout_by_child.
|
||||
*/
|
||||
static ike_sa_t* checkout_by_child(private_ike_sa_manager_t *this,
|
||||
u_int32_t reqid)
|
||||
{
|
||||
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 +525,85 @@ 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 reqid ... */
|
||||
if (child)
|
||||
{
|
||||
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 /* ... or for a IKE_SA with such a unique id */
|
||||
{
|
||||
if (entry->ike_sa->get_unique_id(entry->ike_sa) == id)
|
||||
{
|
||||
ike_sa = entry->ike_sa;
|
||||
}
|
||||
}
|
||||
/* got one, return */
|
||||
if (ike_sa)
|
||||
{
|
||||
entry->checked_out = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
iterator->destroy(iterator);
|
||||
pthread_mutex_unlock(&(this->mutex));
|
||||
|
||||
charon->bus->set_sa(charon->bus, ike_sa);
|
||||
return ike_sa;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of of ike_sa_manager.checkout_by_name.
|
||||
*/
|
||||
static ike_sa_t* checkout_by_name(private_ike_sa_manager_t *this, char *name,
|
||||
bool child)
|
||||
{
|
||||
iterator_t *iterator, *children;
|
||||
entry_t *entry;
|
||||
ike_sa_t *ike_sa = NULL;
|
||||
child_sa_t *child_sa;
|
||||
|
||||
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))
|
||||
{
|
||||
/* 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;
|
||||
}
|
||||
|
||||
@@ -633,170 +729,6 @@ static status_t checkin_and_destroy(private_ike_sa_manager_t *this, ike_sa_t *ik
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,55 +140,14 @@ 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!)
|
||||
*/
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -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",
|
||||
);
|
||||
@@ -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_ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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_ */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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_ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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_ */
|
||||
@@ -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*)¬ify_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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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**)¤t))
|
||||
{
|
||||
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**)¤t))
|
||||
{
|
||||
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;
|
||||
}
|
||||
@@ -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_ */
|
||||
Reference in New Issue
Block a user