strongswan/src/libcharon/sa/ikev1/tasks/quick_mode.c

1547 lines
39 KiB
C
Raw Normal View History

2011-11-21 10:20:34 +00:00
/*
* Copyright (C) 2012-2019 Tobias Brunner
* HSR Hochschule fuer Technik Rapperswil
*
2011-11-21 10:20:34 +00:00
* Copyright (C) 2011 Martin Willi
* Copyright (C) 2011 revosec AG
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
/*
* Copyright (C) 2012 Volker Rümelin
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
2011-11-21 10:20:34 +00:00
#include "quick_mode.h"
#include <string.h>
#include <daemon.h>
#include <sa/ikev1/keymat_v1.h>
#include <encoding/payloads/sa_payload.h>
#include <encoding/payloads/nonce_payload.h>
#include <encoding/payloads/ke_payload.h>
#include <encoding/payloads/id_payload.h>
#include <encoding/payloads/payload.h>
#include <sa/ikev1/tasks/informational.h>
#include <sa/ikev1/tasks/quick_delete.h>
#include <processing/jobs/inactivity_job.h>
2011-11-21 10:20:34 +00:00
typedef struct private_quick_mode_t private_quick_mode_t;
/**
* Private members of a quick_mode_t task.
*/
struct private_quick_mode_t {
/**
* Public methods and task_t interface.
*/
quick_mode_t public;
/**
* Assigned IKE_SA.
*/
ike_sa_t *ike_sa;
/**
* TRUE if we are initiating quick mode
*/
bool initiator;
2011-11-21 10:20:34 +00:00
/**
* Traffic selector of initiator
*/
traffic_selector_t *tsi;
/**
* Traffic selector of responder
*/
traffic_selector_t *tsr;
/**
* Initiators nonce
*/
chunk_t nonce_i;
/**
* Responder nonce
*/
chunk_t nonce_r;
/**
* Initiators ESP SPI
*/
2016-03-22 12:22:01 +00:00
uint32_t spi_i;
/**
* Responder ESP SPI
*/
2016-03-22 12:22:01 +00:00
uint32_t spi_r;
/**
* Initiators IPComp CPI
*/
2016-03-22 12:22:01 +00:00
uint16_t cpi_i;
/**
* Responders IPComp CPI
*/
2016-03-22 12:22:01 +00:00
uint16_t cpi_r;
2011-11-21 10:20:34 +00:00
/**
* selected CHILD_SA proposal
*/
proposal_t *proposal;
/**
* Config of CHILD_SA to establish
*/
child_cfg_t *config;
/**
* CHILD_SA we are about to establish
*/
child_sa_t *child_sa;
/**
* IKEv1 keymat
*/
keymat_v1_t *keymat;
/**
* DH exchange, when PFS is in use
*/
diffie_hellman_t *dh;
/**
* Negotiated lifetime of new SA
*/
2016-03-22 12:22:01 +00:00
uint32_t lifetime;
/**
* Negotiated lifebytes of new SA
*/
2016-03-22 12:22:01 +00:00
uint64_t lifebytes;
/**
* Data collected to create the CHILD_SA
*/
child_sa_create_t child;
/**
* SPI of SA we rekey
*/
2016-03-22 12:22:01 +00:00
uint32_t rekey;
/**
* Delete old child after successful rekey
*/
bool delete;
/**
* Negotiated mode, tunnel or transport
*/
ipsec_mode_t mode;
/*
* SA protocol (ESP|AH) negotiated
*/
protocol_id_t proto;
/**
* Message ID of handled quick mode exchange
*/
2016-03-22 12:22:01 +00:00
uint32_t mid;
2011-11-21 10:20:34 +00:00
/** states of quick mode */
enum {
QM_INIT,
QM_NEGOTIATED,
2011-11-21 10:20:34 +00:00
} state;
};
/**
* Schedule inactivity timeout for CHILD_SA with reqid, if enabled
*/
static void schedule_inactivity_timeout(private_quick_mode_t *this)
{
2016-03-22 12:22:01 +00:00
uint32_t timeout;
bool close_ike;
timeout = this->config->get_inactivity(this->config);
if (timeout)
{
close_ike = lib->settings->get_bool(lib->settings,
"%s.inactivity_close_ike", FALSE, lib->ns);
lib->scheduler->schedule_job(lib->scheduler, (job_t*)
inactivity_job_create(this->child_sa->get_unique_id(this->child_sa),
timeout, close_ike), timeout);
}
}
/**
* Check if we have a an address pool configured
*/
static bool have_pool(ike_sa_t *ike_sa)
{
enumerator_t *enumerator;
peer_cfg_t *peer_cfg;
char *pool;
bool found = FALSE;
peer_cfg = ike_sa->get_peer_cfg(ike_sa);
if (peer_cfg)
{
enumerator = peer_cfg->create_pool_enumerator(peer_cfg);
if (enumerator->enumerate(enumerator, &pool))
{
found = TRUE;
}
enumerator->destroy(enumerator);
}
return found;
}
/**
* Get hosts to use for dynamic traffic selectors
*/
static linked_list_t *get_dynamic_hosts(ike_sa_t *ike_sa, bool local)
{
enumerator_t *enumerator;
linked_list_t *list;
host_t *host;
list = linked_list_create();
enumerator = ike_sa->create_virtual_ip_enumerator(ike_sa, local);
while (enumerator->enumerate(enumerator, &host))
{
list->insert_last(list, host);
}
enumerator->destroy(enumerator);
if (list->get_count(list) == 0)
{ /* no virtual IPs assigned */
if (local)
{
host = ike_sa->get_my_host(ike_sa);
list->insert_last(list, host);
}
else if (!have_pool(ike_sa))
{ /* use host only if we don't have a pool configured */
host = ike_sa->get_other_host(ike_sa);
list->insert_last(list, host);
}
}
return list;
}
/**
* Install negotiated CHILD_SA
*/
static bool install(private_quick_mode_t *this)
{
status_t status, status_i, status_o;
chunk_t encr_i, encr_r, integ_i, integ_r;
linked_list_t *tsi, *tsr, *my_ts, *other_ts;
child_sa_t *old = NULL;
this->child_sa->set_proposal(this->child_sa, this->proposal);
this->child_sa->set_state(this->child_sa, CHILD_INSTALLING);
this->child_sa->set_mode(this->child_sa, this->mode);
if (this->cpi_i && this->cpi_r)
{ /* DEFLATE is the only transform we currently support */
this->child_sa->set_ipcomp(this->child_sa, IPCOMP_DEFLATE);
}
else
{
this->cpi_i = this->cpi_r = 0;
}
this->child_sa->set_protocol(this->child_sa,
this->proposal->get_protocol(this->proposal));
status_i = status_o = FAILED;
encr_i = encr_r = integ_i = integ_r = chunk_empty;
tsi = linked_list_create_with_items(this->tsi->clone(this->tsi), NULL);
tsr = linked_list_create_with_items(this->tsr->clone(this->tsr), NULL);
if (this->initiator)
{
charon->bus->narrow(charon->bus, this->child_sa,
NARROW_INITIATOR_POST_AUTH, tsi, tsr);
}
else
{
charon->bus->narrow(charon->bus, this->child_sa,
NARROW_RESPONDER_POST, tsr, tsi);
}
if (tsi->get_count(tsi) == 0 || tsr->get_count(tsr) == 0)
{
tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy));
tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy));
DBG1(DBG_IKE, "no acceptable traffic selectors found");
return FALSE;
}
if (this->initiator)
{
this->child_sa->set_policies(this->child_sa, tsi, tsr);
}
else
{
this->child_sa->set_policies(this->child_sa, tsr, tsi);
}
tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy));
tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy));
if (this->keymat->derive_child_keys(this->keymat, this->proposal, this->dh,
this->spi_i, this->spi_r, this->nonce_i, this->nonce_r,
&encr_i, &integ_i, &encr_r, &integ_r))
{
if (this->initiator)
{
kernel-interface: add an exchange initiator parameter to add_sa() This new flag gives the kernel-interface a hint how it should priorize the use of newly installed SAs during rekeying. Consider the following rekey procedure in IKEv2: Initiator --- Responder I1 -------CREATE-------> R1 I2 <------CREATE-------- -------DELETE-------> R2 I3 <------DELETE-------- SAs are always handled as pairs, the following happens at the SA level: * Initiator starts the exchange at I1 * Responder installs new SA pair at R1 * Initiator installs new SA pair at I2 * Responder removes old SA pair at R2 * Initiator removes old SA pair at I3 This makes sure SAs get installed/removed overlapping during rekeying. However, to avoid any packet loss, it is crucial that the new outbound SA gets activated at the correct position: * as exchange initiator, in I2 * as exchange responder, in R2 This should guarantee that we don't use the new outbound SA before the peer could install its corresponding inbound SA. The new parameter allows the kernel backend to install the new SA with appropriate priorities, i.e. it should: * as exchange inititator, have the new outbound SA installed with higher priority than the old SA * as exchange responder, have the new outbound SA installed with lower priority than the old SA While we could split up the SA installation at the responder, this approach has another advantage: it allows the kernel backend to switch SAs based on other criteria, for example when receiving traffic on the new inbound SA.
2013-05-08 08:31:06 +00:00
status_i = this->child_sa->install(this->child_sa,
encr_r, integ_r, this->spi_i, this->cpi_i,
this->initiator, TRUE, FALSE);
kernel-interface: add an exchange initiator parameter to add_sa() This new flag gives the kernel-interface a hint how it should priorize the use of newly installed SAs during rekeying. Consider the following rekey procedure in IKEv2: Initiator --- Responder I1 -------CREATE-------> R1 I2 <------CREATE-------- -------DELETE-------> R2 I3 <------DELETE-------- SAs are always handled as pairs, the following happens at the SA level: * Initiator starts the exchange at I1 * Responder installs new SA pair at R1 * Initiator installs new SA pair at I2 * Responder removes old SA pair at R2 * Initiator removes old SA pair at I3 This makes sure SAs get installed/removed overlapping during rekeying. However, to avoid any packet loss, it is crucial that the new outbound SA gets activated at the correct position: * as exchange initiator, in I2 * as exchange responder, in R2 This should guarantee that we don't use the new outbound SA before the peer could install its corresponding inbound SA. The new parameter allows the kernel backend to install the new SA with appropriate priorities, i.e. it should: * as exchange inititator, have the new outbound SA installed with higher priority than the old SA * as exchange responder, have the new outbound SA installed with lower priority than the old SA While we could split up the SA installation at the responder, this approach has another advantage: it allows the kernel backend to switch SAs based on other criteria, for example when receiving traffic on the new inbound SA.
2013-05-08 08:31:06 +00:00
status_o = this->child_sa->install(this->child_sa,
encr_i, integ_i, this->spi_r, this->cpi_r,
this->initiator, FALSE, FALSE);
}
else
{
kernel-interface: add an exchange initiator parameter to add_sa() This new flag gives the kernel-interface a hint how it should priorize the use of newly installed SAs during rekeying. Consider the following rekey procedure in IKEv2: Initiator --- Responder I1 -------CREATE-------> R1 I2 <------CREATE-------- -------DELETE-------> R2 I3 <------DELETE-------- SAs are always handled as pairs, the following happens at the SA level: * Initiator starts the exchange at I1 * Responder installs new SA pair at R1 * Initiator installs new SA pair at I2 * Responder removes old SA pair at R2 * Initiator removes old SA pair at I3 This makes sure SAs get installed/removed overlapping during rekeying. However, to avoid any packet loss, it is crucial that the new outbound SA gets activated at the correct position: * as exchange initiator, in I2 * as exchange responder, in R2 This should guarantee that we don't use the new outbound SA before the peer could install its corresponding inbound SA. The new parameter allows the kernel backend to install the new SA with appropriate priorities, i.e. it should: * as exchange inititator, have the new outbound SA installed with higher priority than the old SA * as exchange responder, have the new outbound SA installed with lower priority than the old SA While we could split up the SA installation at the responder, this approach has another advantage: it allows the kernel backend to switch SAs based on other criteria, for example when receiving traffic on the new inbound SA.
2013-05-08 08:31:06 +00:00
status_i = this->child_sa->install(this->child_sa,
encr_i, integ_i, this->spi_r, this->cpi_r,
this->initiator, TRUE, FALSE);
kernel-interface: add an exchange initiator parameter to add_sa() This new flag gives the kernel-interface a hint how it should priorize the use of newly installed SAs during rekeying. Consider the following rekey procedure in IKEv2: Initiator --- Responder I1 -------CREATE-------> R1 I2 <------CREATE-------- -------DELETE-------> R2 I3 <------DELETE-------- SAs are always handled as pairs, the following happens at the SA level: * Initiator starts the exchange at I1 * Responder installs new SA pair at R1 * Initiator installs new SA pair at I2 * Responder removes old SA pair at R2 * Initiator removes old SA pair at I3 This makes sure SAs get installed/removed overlapping during rekeying. However, to avoid any packet loss, it is crucial that the new outbound SA gets activated at the correct position: * as exchange initiator, in I2 * as exchange responder, in R2 This should guarantee that we don't use the new outbound SA before the peer could install its corresponding inbound SA. The new parameter allows the kernel backend to install the new SA with appropriate priorities, i.e. it should: * as exchange inititator, have the new outbound SA installed with higher priority than the old SA * as exchange responder, have the new outbound SA installed with lower priority than the old SA While we could split up the SA installation at the responder, this approach has another advantage: it allows the kernel backend to switch SAs based on other criteria, for example when receiving traffic on the new inbound SA.
2013-05-08 08:31:06 +00:00
status_o = this->child_sa->install(this->child_sa,
encr_r, integ_r, this->spi_i, this->cpi_i,
this->initiator, FALSE, FALSE);
}
}
if (status_i != SUCCESS || status_o != SUCCESS)
{
DBG1(DBG_IKE, "unable to install %s%s%sIPsec SA (SAD) in kernel",
(status_i != SUCCESS) ? "inbound " : "",
(status_i != SUCCESS && status_o != SUCCESS) ? "and ": "",
(status_o != SUCCESS) ? "outbound " : "");
status = FAILED;
}
else
{
status = this->child_sa->install_policies(this->child_sa);
if (status != SUCCESS)
{
DBG1(DBG_IKE, "unable to install IPsec policies (SPD) in kernel");
}
else
{
charon->bus->child_derived_keys(charon->bus, this->child_sa,
this->initiator, encr_i, encr_r,
integ_i, integ_r);
}
}
chunk_clear(&integ_i);
chunk_clear(&integ_r);
chunk_clear(&encr_i);
chunk_clear(&encr_r);
if (status != SUCCESS)
{
return FALSE;
}
charon->bus->child_keys(charon->bus, this->child_sa, this->initiator,
this->dh, this->nonce_i, this->nonce_r);
my_ts = linked_list_create_from_enumerator(
this->child_sa->create_ts_enumerator(this->child_sa, TRUE));
other_ts = linked_list_create_from_enumerator(
this->child_sa->create_ts_enumerator(this->child_sa, FALSE));
DBG0(DBG_IKE, "CHILD_SA %s{%d} established "
"with SPIs %.8x_i %.8x_o and TS %#R === %#R",
this->child_sa->get_name(this->child_sa),
this->child_sa->get_unique_id(this->child_sa),
ntohl(this->child_sa->get_spi(this->child_sa, TRUE)),
ntohl(this->child_sa->get_spi(this->child_sa, FALSE)), my_ts, other_ts);
my_ts->destroy(my_ts);
other_ts->destroy(other_ts);
this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
this->ike_sa->add_child_sa(this->ike_sa, this->child_sa);
if (this->rekey)
{
old = this->ike_sa->get_child_sa(this->ike_sa,
this->proposal->get_protocol(this->proposal),
this->rekey, TRUE);
}
if (old)
{
charon->bus->child_rekey(charon->bus, old, this->child_sa);
/* rekeyed CHILD_SAs stay installed until they expire or are deleted
* by the other peer */
old->set_state(old, CHILD_REKEYED);
/* as initiator we delete the CHILD_SA if configured to do so */
if (this->initiator && this->delete)
{
this->ike_sa->queue_task(this->ike_sa,
(task_t*)quick_delete_create(this->ike_sa,
this->proposal->get_protocol(this->proposal),
this->rekey, TRUE, FALSE));
}
}
else
{
charon->bus->child_updown(charon->bus, this->child_sa, TRUE);
}
schedule_inactivity_timeout(this);
this->child_sa = NULL;
return TRUE;
}
/**
* Generate and add NONCE
*/
static bool add_nonce(private_quick_mode_t *this, chunk_t *nonce,
message_t *message)
{
nonce_payload_t *nonce_payload;
nonce_gen_t *nonceg;
nonceg = this->keymat->keymat.create_nonce_gen(&this->keymat->keymat);
if (!nonceg)
{
DBG1(DBG_IKE, "no nonce generator found to create nonce");
return FALSE;
}
if (!nonceg->allocate_nonce(nonceg, NONCE_SIZE, nonce))
{
DBG1(DBG_IKE, "nonce allocation failed");
nonceg->destroy(nonceg);
return FALSE;
}
nonceg->destroy(nonceg);
nonce_payload = nonce_payload_create(PLV1_NONCE);
nonce_payload->set_nonce(nonce_payload, *nonce);
message->add_payload(message, &nonce_payload->payload_interface);
return TRUE;
}
/**
* Extract nonce from NONCE payload
*/
static bool get_nonce(private_quick_mode_t *this, chunk_t *nonce,
message_t *message)
{
nonce_payload_t *nonce_payload;
nonce_payload = (nonce_payload_t*)message->get_payload(message, PLV1_NONCE);
if (!nonce_payload)
{
DBG1(DBG_IKE, "NONCE payload missing in message");
return FALSE;
}
*nonce = nonce_payload->get_nonce(nonce_payload);
return TRUE;
}
/**
* Add KE payload to message
*/
static bool add_ke(private_quick_mode_t *this, message_t *message)
{
ke_payload_t *ke_payload;
ke_payload = ke_payload_create_from_diffie_hellman(PLV1_KEY_EXCHANGE,
this->dh);
if (!ke_payload)
{
DBG1(DBG_IKE, "creating KE payload failed");
return FALSE;
}
message->add_payload(message, &ke_payload->payload_interface);
return TRUE;
}
/**
* Get DH value from a KE payload
*/
static bool get_ke(private_quick_mode_t *this, message_t *message)
{
ke_payload_t *ke_payload;
ke_payload = (ke_payload_t*)message->get_payload(message, PLV1_KEY_EXCHANGE);
if (!ke_payload)
{
DBG1(DBG_IKE, "KE payload missing");
return FALSE;
}
if (!this->dh->set_other_public_value(this->dh,
ke_payload->get_key_exchange_data(ke_payload)))
{
DBG1(DBG_IKE, "unable to apply received KE value");
return FALSE;
}
return TRUE;
}
/**
* Select a traffic selector from configuration
*/
static traffic_selector_t* select_ts(private_quick_mode_t *this, bool local,
linked_list_t *supplied)
{
traffic_selector_t *ts;
linked_list_t *list, *hosts;
hosts = get_dynamic_hosts(this->ike_sa, local);
list = this->config->get_traffic_selectors(this->config,
local, supplied, hosts, TRUE);
hosts->destroy(hosts);
if (list->get_first(list, (void**)&ts) == SUCCESS)
{
ts = ts->clone(ts);
}
else
{
DBG1(DBG_IKE, "%s traffic selector missing in configuration",
local ? "local" : "remote");
ts = NULL;
}
list->destroy_offset(list, offsetof(traffic_selector_t, destroy));
return ts;
}
/**
* Add selected traffic selectors to message
*/
static void add_ts(private_quick_mode_t *this, message_t *message)
{
id_payload_t *id_payload;
id_payload = id_payload_create_from_ts(this->tsi);
message->add_payload(message, &id_payload->payload_interface);
id_payload = id_payload_create_from_ts(this->tsr);
message->add_payload(message, &id_payload->payload_interface);
}
/**
* Get traffic selectors from received message
*/
static bool get_ts(private_quick_mode_t *this, message_t *message)
{
traffic_selector_t *tsi = NULL, *tsr = NULL;
enumerator_t *enumerator;
id_payload_t *id_payload;
payload_t *payload;
host_t *hsi, *hsr;
bool first = TRUE;
enumerator = message->create_payload_enumerator(message);
while (enumerator->enumerate(enumerator, &payload))
{
if (payload->get_type(payload) == PLV1_ID)
{
id_payload = (id_payload_t*)payload;
if (first)
{
tsi = id_payload->get_ts(id_payload);
first = FALSE;
}
else
{
tsr = id_payload->get_ts(id_payload);
break;
}
}
}
enumerator->destroy(enumerator);
/* create host2host selectors if ID payloads missing */
if (this->initiator)
{
hsi = this->ike_sa->get_my_host(this->ike_sa);
hsr = this->ike_sa->get_other_host(this->ike_sa);
}
else
{
hsr = this->ike_sa->get_my_host(this->ike_sa);
hsi = this->ike_sa->get_other_host(this->ike_sa);
}
if (!tsi)
{
tsi = traffic_selector_create_from_subnet(hsi->clone(hsi),
hsi->get_family(hsi) == AF_INET ? 32 : 128, 0, 0, 65535);
}
if (!tsr)
{
tsr = traffic_selector_create_from_subnet(hsr->clone(hsr),
hsr->get_family(hsr) == AF_INET ? 32 : 128, 0, 0, 65535);
}
if (this->mode == MODE_TRANSPORT && this->child.encap &&
(!tsi->is_host(tsi, hsi) || !tsr->is_host(tsr, hsr)))
{ /* change TS in case of a NAT in transport mode */
DBG2(DBG_IKE, "changing received traffic selectors %R=== %R due to NAT",
tsi, tsr);
tsi->set_address(tsi, hsi);
tsr->set_address(tsr, hsr);
}
if (this->initiator)
{
traffic_selector_t *tsisub, *tsrsub;
/* check if peer selection is valid */
tsisub = this->tsi->get_subset(this->tsi, tsi);
tsrsub = this->tsr->get_subset(this->tsr, tsr);
if (!tsisub || !tsrsub)
{
DBG1(DBG_IKE, "peer selected invalid traffic selectors: "
"%R for %R, %R for %R", tsi, this->tsi, tsr, this->tsr);
DESTROY_IF(tsisub);
DESTROY_IF(tsrsub);
tsi->destroy(tsi);
tsr->destroy(tsr);
return FALSE;
}
tsi->destroy(tsi);
tsr->destroy(tsr);
this->tsi->destroy(this->tsi);
this->tsr->destroy(this->tsr);
this->tsi = tsisub;
this->tsr = tsrsub;
}
else
{
this->tsi = tsi;
this->tsr = tsr;
}
return TRUE;
}
/**
* Get encap
*/
static encap_t get_encap(ike_sa_t* ike_sa, bool udp)
{
if (!udp)
{
return ENCAP_NONE;
}
if (ike_sa->supports_extension(ike_sa, EXT_NATT_DRAFT_02_03))
{
return ENCAP_UDP_DRAFT_00_03;
}
return ENCAP_UDP;
}
/**
* Get NAT-OA payload type (RFC 3947 or RFC 3947 drafts).
*/
static payload_type_t get_nat_oa_payload_type(ike_sa_t *ike_sa)
{
if (ike_sa->supports_extension(ike_sa, EXT_NATT_DRAFT_02_03))
{
return PLV1_NAT_OA_DRAFT_00_03;
}
return PLV1_NAT_OA;
}
/**
* Add NAT-OA payloads
*/
static void add_nat_oa_payloads(private_quick_mode_t *this, message_t *message)
{
identification_t *id;
id_payload_t *nat_oa;
host_t *init, *resp;
payload_type_t nat_oa_payload_type;
if (this->initiator)
{
init = message->get_source(message);
resp = message->get_destination(message);
}
else
{
init = message->get_destination(message);
resp = message->get_source(message);
}
nat_oa_payload_type = get_nat_oa_payload_type(this->ike_sa);
/* first NAT-OA is the initiator's address */
id = identification_create_from_sockaddr(init->get_sockaddr(init));
nat_oa = id_payload_create_from_identification(nat_oa_payload_type, id);
message->add_payload(message, (payload_t*)nat_oa);
id->destroy(id);
/* second NAT-OA is that of the responder */
id = identification_create_from_sockaddr(resp->get_sockaddr(resp));
nat_oa = id_payload_create_from_identification(nat_oa_payload_type, id);
message->add_payload(message, (payload_t*)nat_oa);
id->destroy(id);
}
/**
* Look up lifetimes
*/
static void get_lifetimes(private_quick_mode_t *this)
{
lifetime_cfg_t *lft;
lft = this->config->get_lifetime(this->config, TRUE);
if (lft->time.life)
{
this->lifetime = lft->time.life;
}
if (lft->bytes.life)
{
this->lifebytes = lft->bytes.life;
}
free(lft);
}
/**
* Check and apply lifetimes
*/
static void apply_lifetimes(private_quick_mode_t *this, sa_payload_t *sa_payload)
{
2016-03-22 12:22:01 +00:00
uint32_t lifetime;
uint64_t lifebytes;
lifetime = sa_payload->get_lifetime(sa_payload, this->proposal);
lifebytes = sa_payload->get_lifebytes(sa_payload, this->proposal);
if (this->lifetime != lifetime)
{
DBG1(DBG_IKE, "received %us lifetime, configured %us",
lifetime, this->lifetime);
this->lifetime = lifetime;
}
if (this->lifebytes != lifebytes)
{
DBG1(DBG_IKE, "received %llu lifebytes, configured %llu",
lifebytes, this->lifebytes);
this->lifebytes = lifebytes;
}
}
/**
* Set the task ready to build notify error message
*/
static status_t send_notify(private_quick_mode_t *this, notify_type_t type)
{
notify_payload_t *notify;
notify = notify_payload_create_from_protocol_and_type(PLV1_NOTIFY,
this->proto, type);
notify->set_spi(notify, this->spi_i);
this->ike_sa->queue_task(this->ike_sa,
(task_t*)informational_create(this->ike_sa, notify));
/* cancel all active/passive tasks in favor of informational */
this->ike_sa->flush_queue(this->ike_sa,
this->initiator ? TASK_QUEUE_ACTIVE : TASK_QUEUE_PASSIVE);
return ALREADY_DONE;
}
/**
* Prepare a list of proposals from child_config containing only the specified
* DH group, unless it is set to MODP_NONE.
*/
static linked_list_t *get_proposals(private_quick_mode_t *this,
diffie_hellman_group_t group)
{
linked_list_t *list;
proposal_t *proposal;
enumerator_t *enumerator;
list = this->config->get_proposals(this->config, FALSE);
enumerator = list->create_enumerator(list);
while (enumerator->enumerate(enumerator, &proposal))
{
if (group != MODP_NONE)
{
if (!proposal->has_dh_group(proposal, group))
{
list->remove_at(list, enumerator);
proposal->destroy(proposal);
continue;
}
proposal->promote_dh_group(proposal, group);
}
proposal->set_spi(proposal, this->spi_i);
}
enumerator->destroy(enumerator);
return list;
}
2011-11-21 10:20:34 +00:00
METHOD(task_t, build_i, status_t,
private_quick_mode_t *this, message_t *message)
{
switch (this->state)
{
case QM_INIT:
{
sa_payload_t *sa_payload;
linked_list_t *list, *tsi, *tsr;
proposal_t *proposal;
diffie_hellman_group_t group;
encap_t encap;
this->mode = this->config->get_mode(this->config);
this->child.if_id_in_def = this->ike_sa->get_if_id(this->ike_sa,
TRUE);
this->child.if_id_out_def = this->ike_sa->get_if_id(this->ike_sa,
FALSE);
this->child.encap = this->ike_sa->has_condition(this->ike_sa,
COND_NAT_ANY);
this->child_sa = child_sa_create(
this->ike_sa->get_my_host(this->ike_sa),
this->ike_sa->get_other_host(this->ike_sa),
this->config, &this->child);
if (this->child.encap && this->mode == MODE_TRANSPORT)
{
/* TODO-IKEv1: disable NAT-T for TRANSPORT mode by default? */
add_nat_oa_payloads(this, message);
}
if (this->config->has_option(this->config, OPT_IPCOMP))
{
this->cpi_i = this->child_sa->alloc_cpi(this->child_sa);
if (!this->cpi_i)
{
DBG1(DBG_IKE, "unable to allocate a CPI from kernel, "
"IPComp disabled");
}
}
list = this->config->get_proposals(this->config, FALSE);
if (list->get_first(list, (void**)&proposal) == SUCCESS)
{
this->proto = proposal->get_protocol(proposal);
}
list->destroy_offset(list, offsetof(proposal_t, destroy));
this->spi_i = this->child_sa->alloc_spi(this->child_sa, this->proto);
if (!this->spi_i)
{
DBG1(DBG_IKE, "allocating SPI from kernel failed");
return FAILED;
}
group = this->config->get_dh_group(this->config);
if (group != MODP_NONE)
{
proposal_t *proposal;
2016-03-22 12:22:01 +00:00
uint16_t preferred_group;
proposal = this->ike_sa->get_proposal(this->ike_sa);
proposal->get_algorithm(proposal, DIFFIE_HELLMAN_GROUP,
&preferred_group, NULL);
/* try the negotiated DH group from IKE_SA */
list = get_proposals(this, preferred_group);
if (list->get_count(list))
{
group = preferred_group;
}
else
{
/* fall back to the first configured DH group */
list->destroy(list);
list = get_proposals(this, group);
}
this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat,
group);
if (!this->dh)
{
DBG1(DBG_IKE, "configured DH group %N not supported",
diffie_hellman_group_names, group);
list->destroy_offset(list, offsetof(proposal_t, destroy));
return FAILED;
}
}
else
{
list = get_proposals(this, MODP_NONE);
}
get_lifetimes(this);
encap = get_encap(this->ike_sa, this->child.encap);
sa_payload = sa_payload_create_from_proposals_v1(list,
this->lifetime, this->lifebytes, AUTH_NONE,
this->mode, encap, this->cpi_i);
list->destroy_offset(list, offsetof(proposal_t, destroy));
message->add_payload(message, &sa_payload->payload_interface);
if (!add_nonce(this, &this->nonce_i, message))
{
return FAILED;
}
if (group != MODP_NONE)
{
if (!add_ke(this, message))
{
return FAILED;
}
}
if (!this->tsi)
{
this->tsi = select_ts(this, TRUE, NULL);
}
if (!this->tsr)
{
this->tsr = select_ts(this, FALSE, NULL);
}
tsi = linked_list_create_with_items(this->tsi, NULL);
tsr = linked_list_create_with_items(this->tsr, NULL);
this->tsi = this->tsr = NULL;
charon->bus->narrow(charon->bus, this->child_sa,
NARROW_INITIATOR_PRE_AUTH, tsi, tsr);
tsi->remove_first(tsi, (void**)&this->tsi);
tsr->remove_first(tsr, (void**)&this->tsr);
tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy));
tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy));
if (!this->tsi || !this->tsr)
{
return FAILED;
}
add_ts(this, message);
return NEED_MORE;
}
case QM_NEGOTIATED:
{
return SUCCESS;
}
default:
return FAILED;
}
2011-11-21 10:20:34 +00:00
}
/**
* Check for notify errors, return TRUE if error found
*/
static bool has_notify_errors(private_quick_mode_t *this, message_t *message)
{
enumerator_t *enumerator;
payload_t *payload;
bool err = FALSE;
enumerator = message->create_payload_enumerator(message);
while (enumerator->enumerate(enumerator, &payload))
{
if (payload->get_type(payload) == PLV1_NOTIFY)
{
notify_payload_t *notify;
notify_type_t type;
notify = (notify_payload_t*)payload;
type = notify->get_notify_type(notify);
if (type < 16384)
{
DBG1(DBG_IKE, "received %N error notify",
notify_type_names, type);
err = TRUE;
}
else
{
DBG1(DBG_IKE, "received %N notify", notify_type_names, type);
}
}
}
enumerator->destroy(enumerator);
return err;
}
/**
* Check if this is a rekey for an existing CHILD_SA, reuse reqid if so
*/
static void check_for_rekeyed_child(private_quick_mode_t *this, bool responder)
{
enumerator_t *enumerator, *policies;
traffic_selector_t *local, *remote, *my_ts, *other_ts;
child_sa_t *child_sa;
proposal_t *proposal;
char *name;
if (responder)
{
my_ts = this->tsr;
other_ts = this->tsi;
}
else
{
my_ts = this->tsi;
other_ts = this->tsr;
}
name = this->config->get_name(this->config);
enumerator = this->ike_sa->create_child_sa_enumerator(this->ike_sa);
while (!this->child.reqid && enumerator->enumerate(enumerator, &child_sa))
{
if (streq(child_sa->get_name(child_sa), name))
{
proposal = child_sa->get_proposal(child_sa);
switch (child_sa->get_state(child_sa))
{
case CHILD_INSTALLED:
case CHILD_REKEYING:
policies = child_sa->create_policy_enumerator(child_sa);
if (policies->enumerate(policies, &local, &remote) &&
local->equals(local, my_ts) &&
remote->equals(remote, other_ts) &&
this->proposal->equals(this->proposal, proposal))
{
this->rekey = child_sa->get_spi(child_sa, TRUE);
this->child.reqid = child_sa->get_reqid(child_sa);
this->child.mark_in = child_sa->get_mark(child_sa,
TRUE).value;
this->child.mark_out = child_sa->get_mark(child_sa,
FALSE).value;
this->child.if_id_in = child_sa->get_if_id(child_sa,
TRUE);
this->child.if_id_out = child_sa->get_if_id(child_sa,
FALSE);
child_sa->set_state(child_sa, CHILD_REKEYING);
DBG1(DBG_IKE, "detected rekeying of CHILD_SA %s{%u}",
child_sa->get_name(child_sa),
child_sa->get_unique_id(child_sa));
}
policies->destroy(policies);
break;
case CHILD_REKEYED:
default:
break;
}
}
}
enumerator->destroy(enumerator);
}
2011-11-21 10:20:34 +00:00
METHOD(task_t, process_r, status_t,
private_quick_mode_t *this, message_t *message)
{
if (this->mid && this->mid != message->get_message_id(message))
{ /* not responsible for this quick mode exchange */
return INVALID_ARG;
}
switch (this->state)
{
case QM_INIT:
{
sa_payload_t *sa_payload;
linked_list_t *tsi, *tsr, *hostsi, *hostsr, *list = NULL;
peer_cfg_t *peer_cfg;
2016-03-22 12:22:01 +00:00
uint16_t group;
proposal_selection_flag_t flags = 0;
sa_payload = (sa_payload_t*)message->get_payload(message,
PLV1_SECURITY_ASSOCIATION);
if (!sa_payload)
{
DBG1(DBG_IKE, "sa payload missing");
return send_notify(this, INVALID_PAYLOAD_TYPE);
}
this->mode = sa_payload->get_encap_mode(sa_payload,
&this->child.encap);
if (!get_ts(this, message))
{
return FAILED;
}
peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
tsi = linked_list_create_with_items(this->tsi, NULL);
tsr = linked_list_create_with_items(this->tsr, NULL);
this->tsi = this->tsr = NULL;
hostsi = get_dynamic_hosts(this->ike_sa, FALSE);
hostsr = get_dynamic_hosts(this->ike_sa, TRUE);
this->config = peer_cfg->select_child_cfg(peer_cfg, tsr, tsi,
hostsr, hostsi);
hostsi->destroy(hostsi);
hostsr->destroy(hostsr);
if (this->config)
{
this->tsi = select_ts(this, FALSE, tsi);
this->tsr = select_ts(this, TRUE, tsr);
}
if (!this->config || !this->tsi || !this->tsr ||
this->mode != this->config->get_mode(this->config))
{
DBG1(DBG_IKE, "no matching CHILD_SA config found for "
"%#R === %#R", tsi, tsr);
tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy));
tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy));
return send_notify(this, INVALID_ID_INFORMATION);
}
tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy));
tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy));
if (this->config->has_option(this->config, OPT_IPCOMP))
{
list = sa_payload->get_ipcomp_proposals(sa_payload,
&this->cpi_i);
if (!list->get_count(list))
{
DBG1(DBG_IKE, "expected IPComp proposal but peer did "
"not send one, IPComp disabled");
this->cpi_i = 0;
}
}
if (!list || !list->get_count(list))
{
DESTROY_IF(list);
list = sa_payload->get_proposals(sa_payload);
}
if (!this->ike_sa->supports_extension(this->ike_sa, EXT_STRONGSWAN)
&& !lib->settings->get_bool(lib->settings,
"%s.accept_private_algs", FALSE, lib->ns))
{
flags |= PROPOSAL_SKIP_PRIVATE;
}
if (!lib->settings->get_bool(lib->settings,
"%s.prefer_configured_proposals", TRUE, lib->ns))
{
flags |= PROPOSAL_PREFER_SUPPLIED;
}
this->proposal = this->config->select_proposal(this->config, list,
flags);
list->destroy_offset(list, offsetof(proposal_t, destroy));
if (!this->proposal)
{
DBG1(DBG_IKE, "no matching proposal found, sending %N",
notify_type_names, NO_PROPOSAL_CHOSEN);
return send_notify(this, NO_PROPOSAL_CHOSEN);
}
this->spi_i = this->proposal->get_spi(this->proposal);
get_lifetimes(this);
apply_lifetimes(this, sa_payload);
if (!get_nonce(this, &this->nonce_i, message))
{
return send_notify(this, INVALID_PAYLOAD_TYPE);
}
if (this->proposal->get_algorithm(this->proposal,
DIFFIE_HELLMAN_GROUP, &group, NULL))
{
this->dh = this->keymat->keymat.create_dh(&this->keymat->keymat,
group);
if (!this->dh)
{
DBG1(DBG_IKE, "negotiated DH group %N not supported",
diffie_hellman_group_names, group);
return send_notify(this, INVALID_KEY_INFORMATION);
}
if (!get_ke(this, message))
{
return send_notify(this, INVALID_PAYLOAD_TYPE);
}
}
check_for_rekeyed_child(this, TRUE);
this->child.if_id_in_def = this->ike_sa->get_if_id(this->ike_sa,
TRUE);
this->child.if_id_out_def = this->ike_sa->get_if_id(this->ike_sa,
FALSE);
this->child_sa = child_sa_create(
this->ike_sa->get_my_host(this->ike_sa),
this->ike_sa->get_other_host(this->ike_sa),
this->config, &this->child);
tsi = linked_list_create_with_items(this->tsi, NULL);
tsr = linked_list_create_with_items(this->tsr, NULL);
this->tsi = this->tsr = NULL;
charon->bus->narrow(charon->bus, this->child_sa,
NARROW_RESPONDER, tsr, tsi);
if (tsi->remove_first(tsi, (void**)&this->tsi) != SUCCESS ||
tsr->remove_first(tsr, (void**)&this->tsr) != SUCCESS)
{
tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy));
tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy));
return send_notify(this, INVALID_ID_INFORMATION);
}
tsi->destroy_offset(tsi, offsetof(traffic_selector_t, destroy));
tsr->destroy_offset(tsr, offsetof(traffic_selector_t, destroy));
return NEED_MORE;
}
case QM_NEGOTIATED:
{
if (has_notify_errors(this, message))
{
return SUCCESS;
}
if (message->get_exchange_type(message) == INFORMATIONAL_V1)
{
if (message->get_payload(message, PLV1_DELETE))
{
/* If the DELETE for a Quick Mode follows immediately
* after rekeying, we might receive it before the
* third completing Quick Mode message. Ignore it, as
* it gets handled by a separately queued delete task. */
return NEED_MORE;
}
return SUCCESS;
}
if (!this->rekey)
{
/* do another check in case SAs were created since we handled
* the QM request, this is consistent with the rekey check
* before installation on the initiator */
check_for_rekeyed_child(this, TRUE);
if (this->rekey)
{
this->child_sa->destroy(this->child_sa);
this->child_sa = child_sa_create(
this->ike_sa->get_my_host(this->ike_sa),
this->ike_sa->get_other_host(this->ike_sa),
this->config, &this->child);
}
}
if (!install(this))
{
ike_sa_t *ike_sa = this->ike_sa;
task_t *task;
task = (task_t*)quick_delete_create(this->ike_sa,
this->proposal->get_protocol(this->proposal),
this->spi_i, TRUE, TRUE);
/* flush_queue() destroys the current task */
ike_sa->flush_queue(ike_sa, TASK_QUEUE_PASSIVE);
ike_sa->queue_task(ike_sa, task);
return ALREADY_DONE;
}
return SUCCESS;
}
default:
return FAILED;
}
2011-11-21 10:20:34 +00:00
}
METHOD(task_t, build_r, status_t,
private_quick_mode_t *this, message_t *message)
{
if (this->mid && this->mid != message->get_message_id(message))
{ /* not responsible for this quick mode exchange */
return INVALID_ARG;
}
switch (this->state)
{
case QM_INIT:
{
sa_payload_t *sa_payload;
encap_t encap;
this->proto = this->proposal->get_protocol(this->proposal);
this->spi_r = this->child_sa->alloc_spi(this->child_sa, this->proto);
if (!this->spi_r)
{
DBG1(DBG_IKE, "allocating SPI from kernel failed");
return send_notify(this, NO_PROPOSAL_CHOSEN);
}
this->proposal->set_spi(this->proposal, this->spi_r);
if (this->cpi_i)
{
this->cpi_r = this->child_sa->alloc_cpi(this->child_sa);
if (!this->cpi_r)
{
DBG1(DBG_IKE, "unable to allocate a CPI from "
"kernel, IPComp disabled");
return send_notify(this, NO_PROPOSAL_CHOSEN);
}
}
if (this->child.encap && this->mode == MODE_TRANSPORT)
{
/* TODO-IKEv1: disable NAT-T for TRANSPORT mode by default? */
add_nat_oa_payloads(this, message);
}
encap = get_encap(this->ike_sa, this->child.encap);
sa_payload = sa_payload_create_from_proposal_v1(this->proposal,
this->lifetime, this->lifebytes, AUTH_NONE,
this->mode, encap, this->cpi_r);
message->add_payload(message, &sa_payload->payload_interface);
if (!add_nonce(this, &this->nonce_r, message))
{
return FAILED;
}
if (this->dh)
{
if (!add_ke(this, message))
{
return FAILED;
}
}
add_ts(this, message);
this->state = QM_NEGOTIATED;
this->mid = message->get_message_id(message);
return NEED_MORE;
}
case QM_NEGOTIATED:
if (message->get_exchange_type(message) == INFORMATIONAL_V1)
{
/* skip INFORMATIONAL response if we received a INFORMATIONAL
* delete, see process_r() */
return ALREADY_DONE;
}
/* fall */
default:
return FAILED;
}
2011-11-21 10:20:34 +00:00
}
METHOD(task_t, process_i, status_t,
private_quick_mode_t *this, message_t *message)
{
switch (this->state)
{
case QM_INIT:
{
sa_payload_t *sa_payload;
linked_list_t *list = NULL;
proposal_selection_flag_t flags = 0;
sa_payload = (sa_payload_t*)message->get_payload(message,
PLV1_SECURITY_ASSOCIATION);
if (!sa_payload)
{
DBG1(DBG_IKE, "sa payload missing");
return send_notify(this, NO_PROPOSAL_CHOSEN);
}
if (this->cpi_i)
{
list = sa_payload->get_ipcomp_proposals(sa_payload,
&this->cpi_r);
if (!list->get_count(list))
{
DBG1(DBG_IKE, "peer did not accept our IPComp proposal, "
"IPComp disabled");
this->cpi_i = 0;
}
}
if (!list || !list->get_count(list))
{
DESTROY_IF(list);
list = sa_payload->get_proposals(sa_payload);
}
if (!this->ike_sa->supports_extension(this->ike_sa, EXT_STRONGSWAN)
&& !lib->settings->get_bool(lib->settings,
"%s.accept_private_algs", FALSE, lib->ns))
{
flags |= PROPOSAL_SKIP_PRIVATE;
}
this->proposal = this->config->select_proposal(this->config, list,
flags);
list->destroy_offset(list, offsetof(proposal_t, destroy));
if (!this->proposal)
{
DBG1(DBG_IKE, "no matching proposal found");
return send_notify(this, NO_PROPOSAL_CHOSEN);
}
this->spi_r = this->proposal->get_spi(this->proposal);
apply_lifetimes(this, sa_payload);
if (!get_nonce(this, &this->nonce_r, message))
{
return send_notify(this, INVALID_PAYLOAD_TYPE);
}
if (this->dh && !get_ke(this, message))
{
return send_notify(this, INVALID_KEY_INFORMATION);
}
if (!get_ts(this, message))
{
return send_notify(this, INVALID_PAYLOAD_TYPE);
}
check_for_rekeyed_child(this, FALSE);
if (!install(this))
{
return send_notify(this, NO_PROPOSAL_CHOSEN);
}
this->state = QM_NEGOTIATED;
return NEED_MORE;
}
default:
return FAILED;
}
2011-11-21 10:20:34 +00:00
}
METHOD(task_t, get_type, task_type_t,
private_quick_mode_t *this)
{
return TASK_QUICK_MODE;
}
2016-03-22 12:22:01 +00:00
METHOD(quick_mode_t, get_mid, uint32_t,
private_quick_mode_t *this)
{
return this->mid;
}
METHOD(quick_mode_t, use_reqid, void,
2016-03-22 12:22:01 +00:00
private_quick_mode_t *this, uint32_t reqid)
{
this->child.reqid = reqid;
}
METHOD(quick_mode_t, use_marks, void,
private_quick_mode_t *this, uint32_t in, uint32_t out)
{
this->child.mark_in = in;
this->child.mark_out = out;
}
METHOD(quick_mode_t, use_if_ids, void,
private_quick_mode_t *this, uint32_t in, uint32_t out)
{
this->child.if_id_in = in;
this->child.if_id_out = out;
}
METHOD(quick_mode_t, rekey, void,
2016-03-22 12:22:01 +00:00
private_quick_mode_t *this, uint32_t spi)
{
this->rekey = spi;
}
2011-11-21 10:20:34 +00:00
METHOD(task_t, migrate, void,
private_quick_mode_t *this, ike_sa_t *ike_sa)
{
chunk_free(&this->nonce_i);
chunk_free(&this->nonce_r);
DESTROY_IF(this->tsi);
DESTROY_IF(this->tsr);
DESTROY_IF(this->proposal);
DESTROY_IF(this->child_sa);
DESTROY_IF(this->dh);
2011-11-21 10:20:34 +00:00
this->ike_sa = ike_sa;
this->keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa);
this->state = QM_INIT;
this->mid = 0;
this->tsi = NULL;
this->tsr = NULL;
this->proposal = NULL;
this->child_sa = NULL;
this->dh = NULL;
this->spi_i = 0;
this->spi_r = 0;
this->child = (child_sa_create_t){};
if (!this->initiator)
{
DESTROY_IF(this->config);
this->config = NULL;
}
2011-11-21 10:20:34 +00:00
}
METHOD(task_t, destroy, void,
private_quick_mode_t *this)
{
chunk_free(&this->nonce_i);
chunk_free(&this->nonce_r);
DESTROY_IF(this->tsi);
DESTROY_IF(this->tsr);
DESTROY_IF(this->proposal);
DESTROY_IF(this->child_sa);
DESTROY_IF(this->config);
DESTROY_IF(this->dh);
2011-11-21 10:20:34 +00:00
free(this);
}
/*
* Described in header.
*/
quick_mode_t *quick_mode_create(ike_sa_t *ike_sa, child_cfg_t *config,
traffic_selector_t *tsi, traffic_selector_t *tsr)
{
private_quick_mode_t *this;
INIT(this,
.public = {
.task = {
.get_type = _get_type,
.migrate = _migrate,
.destroy = _destroy,
},
.get_mid = _get_mid,
.use_reqid = _use_reqid,
.use_marks = _use_marks,
.use_if_ids = _use_if_ids,
.rekey = _rekey,
2011-11-21 10:20:34 +00:00
},
.ike_sa = ike_sa,
.initiator = config != NULL,
2011-11-21 10:20:34 +00:00
.config = config,
.keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa),
2011-11-21 10:20:34 +00:00
.state = QM_INIT,
.tsi = tsi ? tsi->clone(tsi) : NULL,
.tsr = tsr ? tsr->clone(tsr) : NULL,
.proto = PROTO_ESP,
.delete = lib->settings->get_bool(lib->settings,
"%s.delete_rekeyed", FALSE, lib->ns),
2011-11-21 10:20:34 +00:00
);
if (config)
{
this->public.task.build = _build_i;
this->public.task.process = _process_i;
}
else
{
this->public.task.build = _build_r;
this->public.task.process = _process_r;
}
return &this->public;
}