summaryrefslogtreecommitdiff
path: root/src/libcharon/sa/ikev1
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcharon/sa/ikev1')
-rw-r--r--src/libcharon/sa/ikev1/keymat_v1.c2
-rw-r--r--src/libcharon/sa/ikev1/phase1.c4
-rw-r--r--src/libcharon/sa/ikev1/task_manager_v1.c355
-rw-r--r--src/libcharon/sa/ikev1/tasks/aggressive_mode.c6
-rw-r--r--src/libcharon/sa/ikev1/tasks/isakmp_cert_pre.c155
-rw-r--r--src/libcharon/sa/ikev1/tasks/isakmp_natd.c41
-rw-r--r--src/libcharon/sa/ikev1/tasks/isakmp_vendor.c174
-rw-r--r--src/libcharon/sa/ikev1/tasks/main_mode.c6
-rw-r--r--src/libcharon/sa/ikev1/tasks/quick_mode.c103
9 files changed, 742 insertions, 104 deletions
diff --git a/src/libcharon/sa/ikev1/keymat_v1.c b/src/libcharon/sa/ikev1/keymat_v1.c
index cff344a34..eb642109b 100644
--- a/src/libcharon/sa/ikev1/keymat_v1.c
+++ b/src/libcharon/sa/ikev1/keymat_v1.c
@@ -18,7 +18,7 @@
#include <daemon.h>
#include <encoding/generator.h>
#include <encoding/payloads/nonce_payload.h>
-#include <utils/linked_list.h>
+#include <collections/linked_list.h>
typedef struct private_keymat_v1_t private_keymat_v1_t;
diff --git a/src/libcharon/sa/ikev1/phase1.c b/src/libcharon/sa/ikev1/phase1.c
index 4096141ec..1189d3c69 100644
--- a/src/libcharon/sa/ikev1/phase1.c
+++ b/src/libcharon/sa/ikev1/phase1.c
@@ -22,7 +22,7 @@
#include <sa/ikev1/keymat_v1.h>
#include <encoding/payloads/ke_payload.h>
#include <encoding/payloads/nonce_payload.h>
-#include <utils/linked_list.h>
+#include <collections/linked_list.h>
typedef struct private_phase1_t private_phase1_t;
@@ -186,7 +186,7 @@ static shared_key_t *lookup_shared_key(private_phase1_t *this,
}
}
enumerator->destroy(enumerator);
- if (!peer_cfg)
+ if (!shared_key)
{
DBG1(DBG_IKE, "no shared key found for %H - %H", me, other);
}
diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c
index fd0ad235a..8a4761d5c 100644
--- a/src/libcharon/sa/ikev1/task_manager_v1.c
+++ b/src/libcharon/sa/ikev1/task_manager_v1.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007-2011 Tobias Brunner
+ * Copyright (C) 2007-2013 Tobias Brunner
* Copyright (C) 2007-2011 Martin Willi
* Hochschule fuer Technik Rapperswil
*
@@ -36,6 +36,10 @@
#include <processing/jobs/retransmit_job.h>
#include <processing/jobs/delete_ike_sa_job.h>
#include <processing/jobs/dpd_timeout_job.h>
+#include <processing/jobs/process_message_job.h>
+
+#include <encoding/payloads/fragment_payload.h>
+#include <bio/bio_writer.h>
/**
* Number of old messages hashes we keep for retransmission.
@@ -47,6 +51,20 @@
#define MAX_OLD_HASHES 2
/**
+ * Maximum packet size for fragmented packets (same as in sockets)
+ */
+#define MAX_PACKET 10000
+
+/**
+ * Maximum size of fragment data when sending packets (currently the same is
+ * used for IPv4 and IPv6, even though the latter has a higher minimum datagram
+ * size). 576 (= min. IPv4) - 20 (= IP header) - 8 (= UDP header) -
+ * - 28 (= IKE header) - 8 (= fragment header) = 512
+ * This is reduced by 4 in case of NAT-T (due to the non-ESP marker).
+ */
+#define MAX_FRAGMENT_SIZE 512
+
+/**
* First sequence number of responding packets.
*
* To distinguish retransmission jobs for initiating and responding packets,
@@ -160,19 +178,65 @@ struct private_task_manager_t {
packet_t *packet;
/**
- * type of the initated exchange
+ * type of the initiated exchange
*/
exchange_type_t type;
} initiating;
/**
+ * Data used to reassemble a fragmented message
+ */
+ struct {
+
+ /**
+ * Fragment ID (currently only one is supported at a time)
+ */
+ u_int16_t id;
+
+ /**
+ * The number of the last fragment (in case we receive the fragments out
+ * of order), since the first starts with 1 this defines the number of
+ * fragments we expect
+ */
+ u_int8_t last;
+
+ /**
+ * List of fragments (fragment_t*)
+ */
+ linked_list_t *list;
+
+ /**
+ * Length of all currently received fragments
+ */
+ size_t len;
+
+ /**
+ * Maximum length of a fragmented packet
+ */
+ size_t max_packet;
+
+ /**
+ * Maximum length of a single fragment (when sending)
+ */
+ size_t size;
+
+ /**
+ * The exchange type we use for fragments. Always the initial type even
+ * for fragmented quick mode or transaction messages (i.e. either
+ * ID_PROT or AGGRESSIVE)
+ */
+ exchange_type_t exchange;
+
+ } frag;
+
+ /**
* List of queued tasks not yet in action
*/
linked_list_t *queued_tasks;
/**
- * List of active tasks, initiated by ourselve
+ * List of active tasks, initiated by ourselves
*/
linked_list_t *active_tasks;
@@ -212,6 +276,34 @@ struct private_task_manager_t {
u_int32_t dpd_recv;
};
+/**
+ * A single fragment within a fragmented message
+ */
+typedef struct {
+
+ /** fragment number */
+ u_int8_t num;
+
+ /** fragment data */
+ chunk_t data;
+
+} fragment_t;
+
+static void fragment_destroy(fragment_t *this)
+{
+ chunk_free(&this->data);
+ free(this);
+}
+
+static void clear_fragments(private_task_manager_t *this, u_int16_t id)
+{
+ DESTROY_FUNCTION_IF(this->frag.list, (void*)fragment_destroy);
+ this->frag.list = NULL;
+ this->frag.last = 0;
+ this->frag.len = 0;
+ this->frag.id = id;
+}
+
METHOD(task_manager_t, flush_queue, void,
private_task_manager_t *this, task_queue_t queue)
{
@@ -283,16 +375,103 @@ static bool activate_task(private_task_manager_t *this, task_type_t type)
}
/**
+ * Send a single fragment with the given data
+ */
+static bool send_fragment(private_task_manager_t *this, bool request,
+ host_t *src, host_t *dst, fragment_payload_t *fragment)
+{
+ message_t *message;
+ packet_t *packet;
+ status_t status;
+
+ message = message_create(IKEV1_MAJOR_VERSION, IKEV1_MINOR_VERSION);
+ /* other implementations seem to just use 0 as message ID, so here we go */
+ message->set_message_id(message, 0);
+ message->set_request(message, request);
+ message->set_source(message, src->clone(src));
+ message->set_destination(message, dst->clone(dst));
+ message->set_exchange_type(message, this->frag.exchange);
+ message->add_payload(message, (payload_t*)fragment);
+
+ status = this->ike_sa->generate_message(this->ike_sa, message, &packet);
+ if (status != SUCCESS)
+ {
+ DBG1(DBG_IKE, "failed to generate IKE fragment");
+ message->destroy(message);
+ return FALSE;
+ }
+ charon->sender->send(charon->sender, packet);
+ message->destroy(message);
+ return TRUE;
+}
+
+/**
+ * Send a packet, if supported and required do so in fragments
+ */
+static bool send_packet(private_task_manager_t *this, bool request,
+ packet_t *packet)
+{
+ fragmentation_t fragmentation = FRAGMENTATION_NO;
+ ike_cfg_t *ike_cfg;
+ host_t *src, *dst;
+ chunk_t data;
+
+ ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
+ if (ike_cfg)
+ {
+ fragmentation = ike_cfg->fragmentation(ike_cfg);
+ }
+ data = packet->get_data(packet);
+ if (data.len > this->frag.size && (fragmentation == FRAGMENTATION_FORCE ||
+ (this->ike_sa->supports_extension(this->ike_sa, EXT_IKE_FRAGMENTATION) &&
+ fragmentation == FRAGMENTATION_YES)))
+ {
+ fragment_payload_t *fragment;
+ u_int8_t num, count;
+ size_t len, frag_size;
+ bool nat;
+
+ /* reduce size due to non-ESP marker */
+ nat = this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY);
+ frag_size = this->frag.size - (nat ? 4 : 0);
+
+ src = packet->get_source(packet);
+ dst = packet->get_destination(packet);
+ count = (data.len / (frag_size + 1)) + 1;
+
+ DBG1(DBG_IKE, "sending IKE message with length of %zu bytes in "
+ "%hhu fragments", data.len, count);
+ for (num = 1; num <= count; num++)
+ {
+ len = min(data.len, frag_size);
+ fragment = fragment_payload_create_from_data(num, num == count,
+ chunk_create(data.ptr, len));
+ if (!send_fragment(this, request, src, dst, fragment))
+ {
+ packet->destroy(packet);
+ return FALSE;
+ }
+ data = chunk_skip(data, len);
+ }
+ packet->destroy(packet);
+ return TRUE;
+ }
+ charon->sender->send(charon->sender, packet);
+ return TRUE;
+}
+
+/**
* Retransmit a packet, either as initiator or as responder
*/
-static status_t retransmit_packet(private_task_manager_t *this, u_int32_t seqnr,
- u_int mid, u_int retransmitted, packet_t *packet)
+static status_t retransmit_packet(private_task_manager_t *this, bool request,
+ u_int32_t seqnr, u_int mid, u_int retransmitted, packet_t *packet)
{
u_int32_t t;
if (retransmitted > this->retransmit_tries)
{
DBG1(DBG_IKE, "giving up after %u retransmits", retransmitted - 1);
+ charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND_TIMEOUT, packet);
return DESTROY_ME;
}
t = (u_int32_t)(this->retransmit_timeout * 1000.0 *
@@ -302,8 +481,12 @@ static status_t retransmit_packet(private_task_manager_t *this, u_int32_t seqnr,
DBG1(DBG_IKE, "sending retransmit %u of %s message ID %u, seq %u",
retransmitted, seqnr < RESPONDING_SEQ ? "request" : "response",
mid, seqnr < RESPONDING_SEQ ? seqnr : seqnr - RESPONDING_SEQ);
+ charon->bus->alert(charon->bus, ALERT_RETRANSMIT_SEND, packet);
+ }
+ if (!send_packet(this, request, packet->clone(packet)))
+ {
+ return DESTROY_ME;
}
- charon->sender->send(charon->sender, packet->clone(packet));
lib->scheduler->schedule_job_ms(lib->scheduler, (job_t*)
retransmit_job_create(seqnr, this->ike_sa->get_id(this->ike_sa)), t);
return NEED_MORE;
@@ -316,7 +499,7 @@ METHOD(task_manager_t, retransmit, status_t,
if (seqnr == this->initiating.seqnr && this->initiating.packet)
{
- status = retransmit_packet(this, seqnr, this->initiating.mid,
+ status = retransmit_packet(this, TRUE, seqnr, this->initiating.mid,
this->initiating.retransmitted, this->initiating.packet);
if (status == NEED_MORE)
{
@@ -326,7 +509,7 @@ METHOD(task_manager_t, retransmit, status_t,
}
if (seqnr == this->responding.seqnr && this->responding.packet)
{
- status = retransmit_packet(this, seqnr, this->responding.mid,
+ status = retransmit_packet(this, FALSE, seqnr, this->responding.mid,
this->responding.retransmitted, this->responding.packet);
if (status == NEED_MORE)
{
@@ -602,12 +785,12 @@ METHOD(task_manager_t, initiate, status_t,
}
if (keep)
{ /* keep the packet for retransmission, the responder might request it */
- charon->sender->send(charon->sender,
+ send_packet(this, TRUE,
this->initiating.packet->clone(this->initiating.packet));
}
else
{
- charon->sender->send(charon->sender, this->initiating.packet);
+ send_packet(this, TRUE, this->initiating.packet);
this->initiating.packet = NULL;
}
message->destroy(message);
@@ -711,8 +894,8 @@ static status_t build_response(private_task_manager_t *this, message_t *request)
{
return retransmit(this, this->responding.seqnr);
}
- charon->sender->send(charon->sender,
- this->responding.packet->clone(this->responding.packet));
+ send_packet(this, FALSE,
+ this->responding.packet->clone(this->responding.packet));
if (delete)
{
return DESTROY_ME;
@@ -767,7 +950,7 @@ static void send_notify(private_task_manager_t *this, message_t *request,
if (this->ike_sa->generate_message(this->ike_sa, response,
&packet) == SUCCESS)
{
- charon->sender->send(charon->sender, packet);
+ send_packet(this, TRUE, packet);
}
response->destroy(response);
}
@@ -866,6 +1049,7 @@ static status_t process_request(private_task_manager_t *this,
this->passive_tasks->insert_last(this->passive_tasks, task);
task = (task_t *)isakmp_natd_create(this->ike_sa, FALSE);
this->passive_tasks->insert_last(this->passive_tasks, task);
+ this->frag.exchange = AGGRESSIVE;
break;
case QUICK_MODE:
if (this->ike_sa->get_state(this->ike_sa) != IKE_ESTABLISHED)
@@ -1036,6 +1220,114 @@ static status_t process_response(private_task_manager_t *this,
return initiate(this);
}
+static status_t handle_fragment(private_task_manager_t *this, message_t *msg)
+{
+ fragment_payload_t *payload;
+ enumerator_t *enumerator;
+ fragment_t *fragment;
+ status_t status = SUCCESS;
+ chunk_t data;
+ u_int8_t num;
+
+ payload = (fragment_payload_t*)msg->get_payload(msg, FRAGMENT_V1);
+ if (!payload)
+ {
+ return FAILED;
+ }
+
+ if (this->frag.id != payload->get_id(payload))
+ {
+ clear_fragments(this, payload->get_id(payload));
+ this->frag.list = linked_list_create();
+ }
+
+ num = payload->get_number(payload);
+ if (!this->frag.last && payload->is_last(payload))
+ {
+ this->frag.last = num;
+ }
+
+ enumerator = this->frag.list->create_enumerator(this->frag.list);
+ while (enumerator->enumerate(enumerator, &fragment))
+ {
+ if (fragment->num == num)
+ { /* ignore a duplicate fragment */
+ DBG1(DBG_IKE, "received duplicate fragment #%hhu", num);
+ enumerator->destroy(enumerator);
+ return NEED_MORE;
+ }
+ if (fragment->num > num)
+ {
+ break;
+ }
+ }
+
+ data = payload->get_data(payload);
+ this->frag.len += data.len;
+ if (this->frag.len > this->frag.max_packet)
+ {
+ DBG1(DBG_IKE, "fragmented IKE message is too large");
+ enumerator->destroy(enumerator);
+ clear_fragments(this, 0);
+ return FAILED;
+ }
+
+ INIT(fragment,
+ .num = num,
+ .data = chunk_clone(data),
+ );
+
+ this->frag.list->insert_before(this->frag.list, enumerator, fragment);
+ enumerator->destroy(enumerator);
+
+ if (this->frag.list->get_count(this->frag.list) == this->frag.last)
+ {
+ message_t *message;
+ packet_t *pkt;
+ host_t *src, *dst;
+ bio_writer_t *writer;
+
+ writer = bio_writer_create(this->frag.len);
+ DBG1(DBG_IKE, "received fragment #%hhu, reassembling fragmented IKE "
+ "message", num);
+ enumerator = this->frag.list->create_enumerator(this->frag.list);
+ while (enumerator->enumerate(enumerator, &fragment))
+ {
+ writer->write_data(writer, fragment->data);
+ }
+ enumerator->destroy(enumerator);
+
+ src = msg->get_source(msg);
+ dst = msg->get_destination(msg);
+ pkt = packet_create_from_data(src->clone(src), dst->clone(dst),
+ writer->extract_buf(writer));
+ writer->destroy(writer);
+
+ message = message_create_from_packet(pkt);
+ if (message->parse_header(message) != SUCCESS)
+ {
+ DBG1(DBG_IKE, "failed to parse header of reassembled IKE message");
+ message->destroy(message);
+ status = FAILED;
+ }
+ else
+ {
+ lib->processor->queue_job(lib->processor,
+ (job_t*)process_message_job_create(message));
+ status = NEED_MORE;
+
+ }
+ clear_fragments(this, 0);
+ }
+ else
+ { /* there are some fragments missing */
+ DBG1(DBG_IKE, "received fragment #%hhu, waiting for complete IKE "
+ "message", num);
+ status = NEED_MORE;
+ }
+ return status;
+}
+
/**
* Parse the given message and verify that it is valid.
*/
@@ -1076,11 +1368,18 @@ static status_t parse_message(private_task_manager_t *this, message_t *msg)
msg->get_request(msg) ? "request" : "response",
msg->get_message_id(msg));
+ charon->bus->alert(charon->bus, ALERT_PARSE_ERROR_BODY, msg, status);
+
if (this->ike_sa->get_state(this->ike_sa) == IKE_CREATED)
{ /* invalid initiation attempt, close SA */
return DESTROY_ME;
}
}
+
+ if (msg->get_first_payload_type(msg) == FRAGMENT_V1)
+ {
+ return handle_fragment(this, msg);
+ }
return status;
}
@@ -1107,8 +1406,8 @@ METHOD(task_manager_t, process_message, status_t,
{
DBG1(DBG_IKE, "received retransmit of response with ID %u, "
"resending last request", mid);
- charon->sender->send(charon->sender,
- this->initiating.packet->clone(this->initiating.packet));
+ send_packet(this, TRUE,
+ this->initiating.packet->clone(this->initiating.packet));
return SUCCESS;
}
DBG1(DBG_IKE, "received retransmit of response with ID %u, "
@@ -1125,6 +1424,10 @@ METHOD(task_manager_t, process_message, status_t,
msg->set_request(msg, FALSE);
charon->bus->message(charon->bus, msg, TRUE, FALSE);
status = parse_message(this, msg);
+ if (status == NEED_MORE)
+ {
+ return SUCCESS;
+ }
if (status != SUCCESS)
{
return status;
@@ -1149,7 +1452,7 @@ METHOD(task_manager_t, process_message, status_t,
{
DBG1(DBG_IKE, "received retransmit of request with ID %u, "
"retransmitting response", mid);
- charon->sender->send(charon->sender,
+ send_packet(this, FALSE,
this->responding.packet->clone(this->responding.packet));
}
else if (this->initiating.packet &&
@@ -1157,7 +1460,7 @@ METHOD(task_manager_t, process_message, status_t,
{
DBG1(DBG_IKE, "received retransmit of DPD request, "
"retransmitting response");
- charon->sender->send(charon->sender,
+ send_packet(this, TRUE,
this->initiating.packet->clone(this->initiating.packet));
}
else
@@ -1165,6 +1468,7 @@ METHOD(task_manager_t, process_message, status_t,
DBG1(DBG_IKE, "received retransmit of request with ID %u, "
"but no response to retransmit", mid);
}
+ charon->bus->alert(charon->bus, ALERT_RETRANSMIT_RECEIVE, msg);
return SUCCESS;
}
if (msg->get_exchange_type(msg) == TRANSACTION &&
@@ -1191,6 +1495,10 @@ METHOD(task_manager_t, process_message, status_t,
msg->set_request(msg, TRUE);
charon->bus->message(charon->bus, msg, TRUE, FALSE);
status = parse_message(this, msg);
+ if (status == NEED_MORE)
+ {
+ return SUCCESS;
+ }
if (status != SUCCESS)
{
return status;
@@ -1202,7 +1510,8 @@ METHOD(task_manager_t, process_message, status_t,
ike_cfg_t *ike_cfg;
job_t *job;
- ike_cfg = charon->backends->get_ike_cfg(charon->backends, me, other);
+ ike_cfg = charon->backends->get_ike_cfg(charon->backends,
+ me, other, IKEV1);
if (ike_cfg == NULL)
{
/* no config found for these hosts, destroy */
@@ -1282,6 +1591,7 @@ METHOD(task_manager_t, queue_ike, void,
{
queue_task(this, (task_t*)aggressive_mode_create(this->ike_sa, TRUE));
}
+ this->frag.exchange = AGGRESSIVE;
}
else
{
@@ -1585,6 +1895,7 @@ METHOD(task_manager_t, reset, void,
this->initiating.seqnr = 0;
this->initiating.retransmitted = 0;
this->initiating.type = EXCHANGE_TYPE_UNDEFINED;
+ clear_fragments(this, 0);
if (initiate != UINT_MAX)
{
this->dpd_send = initiate;
@@ -1635,6 +1946,7 @@ METHOD(task_manager_t, destroy, void,
this->active_tasks->destroy(this->active_tasks);
this->queued_tasks->destroy(this->queued_tasks);
this->passive_tasks->destroy(this->passive_tasks);
+ clear_fragments(this, 0);
DESTROY_IF(this->queued);
DESTROY_IF(this->responding.packet);
@@ -1681,6 +1993,13 @@ task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa)
.responding = {
.seqnr = RESPONDING_SEQ,
},
+ .frag = {
+ .exchange = ID_PROT,
+ .max_packet = lib->settings->get_int(lib->settings,
+ "%s.max_packet", MAX_PACKET, charon->name),
+ .size = lib->settings->get_int(lib->settings,
+ "%s.fragment_size", MAX_FRAGMENT_SIZE, charon->name),
+ },
.ike_sa = ike_sa,
.rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK),
.queued_tasks = linked_list_create(),
diff --git a/src/libcharon/sa/ikev1/tasks/aggressive_mode.c b/src/libcharon/sa/ikev1/tasks/aggressive_mode.c
index 954dea880..7336d5d64 100644
--- a/src/libcharon/sa/ikev1/tasks/aggressive_mode.c
+++ b/src/libcharon/sa/ikev1/tasks/aggressive_mode.c
@@ -235,7 +235,8 @@ METHOD(task_t, build_i, status_t,
this->lifetime += this->peer_cfg->get_over_time(this->peer_cfg);
proposals = this->ike_cfg->get_proposals(this->ike_cfg);
sa_payload = sa_payload_create_from_proposals_v1(proposals,
- this->lifetime, 0, this->method, MODE_NONE, FALSE, 0);
+ this->lifetime, 0, this->method, MODE_NONE,
+ ENCAP_NONE, 0);
proposals->destroy_offset(proposals, offsetof(proposal_t, destroy));
message->add_payload(message, &sa_payload->payload_interface);
@@ -520,7 +521,8 @@ METHOD(task_t, build_r, status_t,
identification_t *id;
sa_payload = sa_payload_create_from_proposal_v1(this->proposal,
- this->lifetime, 0, this->method, MODE_NONE, FALSE, 0);
+ this->lifetime, 0, this->method, MODE_NONE,
+ ENCAP_NONE, 0);
message->add_payload(message, &sa_payload->payload_interface);
if (!this->ph1->add_nonce_ke(this->ph1, message))
diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_cert_pre.c b/src/libcharon/sa/ikev1/tasks/isakmp_cert_pre.c
index d48484f09..43a0aaa36 100644
--- a/src/libcharon/sa/ikev1/tasks/isakmp_cert_pre.c
+++ b/src/libcharon/sa/ikev1/tasks/isakmp_cert_pre.c
@@ -13,6 +13,28 @@
* for more details.
*/
+/*
+ * Copyright (C) 2013 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.
+ */
+
#include "isakmp_cert_pre.h"
#include <daemon.h>
@@ -21,6 +43,7 @@
#include <encoding/payloads/sa_payload.h>
#include <encoding/payloads/certreq_payload.h>
#include <credentials/certificates/x509.h>
+#include <credentials/containers/pkcs7.h>
typedef struct private_isakmp_cert_pre_t private_isakmp_cert_pre_t;
@@ -132,7 +155,106 @@ static void process_certreqs(private_isakmp_cert_pre_t *this, message_t *message
}
/**
- * Import receuved certificates
+ * Process an X509 certificate payload
+ */
+static void process_x509(cert_payload_t *payload, auth_cfg_t *auth, bool *first)
+{
+ certificate_t *cert;
+
+ cert = payload->get_cert(payload);
+ if (cert)
+ {
+ if (*first)
+ { /* the first is an end entity certificate */
+ DBG1(DBG_IKE, "received end entity cert \"%Y\"",
+ cert->get_subject(cert));
+ auth->add(auth, AUTH_HELPER_SUBJECT_CERT, cert);
+ *first = FALSE;
+ }
+ else
+ {
+ DBG1(DBG_IKE, "received issuer cert \"%Y\"",
+ cert->get_subject(cert));
+ auth->add(auth, AUTH_HELPER_IM_CERT, cert);
+ }
+ }
+}
+
+/**
+ * Process a CRL certificate payload
+ */
+static void process_crl(cert_payload_t *payload, auth_cfg_t *auth)
+{
+ certificate_t *cert;
+
+ cert = payload->get_cert(payload);
+ if (cert)
+ {
+ DBG1(DBG_IKE, "received CRL \"%Y\"", cert->get_subject(cert));
+ auth->add(auth, AUTH_HELPER_REVOCATION_CERT, cert);
+ }
+}
+
+/**
+ * Process a PKCS7 certificate payload
+ */
+static void process_pkcs7(cert_payload_t *payload, auth_cfg_t *auth)
+{
+ enumerator_t *enumerator;
+ container_t *container;
+ certificate_t *cert;
+ pkcs7_t *pkcs7;
+
+ container = payload->get_container(payload);
+ if (!container)
+ {
+ return;
+ }
+ switch (container->get_type(container))
+ {
+ case CONTAINER_PKCS7_DATA:
+ case CONTAINER_PKCS7_SIGNED_DATA:
+ case CONTAINER_PKCS7_ENVELOPED_DATA:
+ break;
+ default:
+ container->destroy(container);
+ return;
+ }
+
+ pkcs7 = (pkcs7_t *)container;
+ enumerator = pkcs7->create_cert_enumerator(pkcs7);
+ while (enumerator->enumerate(enumerator, &cert))
+ {
+ if (cert->get_type(cert) == CERT_X509)
+ {
+ x509_t *x509 = (x509_t*)cert;
+
+ if (x509->get_flags(x509) & X509_CA)
+ {
+ DBG1(DBG_IKE, "received issuer cert \"%Y\"",
+ cert->get_subject(cert));
+ auth->add(auth, AUTH_HELPER_IM_CERT, cert->get_ref(cert));
+ }
+ else
+ {
+ DBG1(DBG_IKE, "received end entity cert \"%Y\"",
+ cert->get_subject(cert));
+ auth->add(auth, AUTH_HELPER_SUBJECT_CERT, cert->get_ref(cert));
+ }
+ }
+ else
+ {
+ DBG1(DBG_IKE, "received unsupported cert type %N",
+ certificate_type_names, cert->get_type(cert));
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ container->destroy(container);
+}
+
+/**
+ * Import received certificates
*/
static void process_certs(private_isakmp_cert_pre_t *this, message_t *message)
{
@@ -150,7 +272,6 @@ static void process_certs(private_isakmp_cert_pre_t *this, message_t *message)
{
cert_payload_t *cert_payload;
cert_encoding_t encoding;
- certificate_t *cert;
cert_payload = (cert_payload_t*)payload;
encoding = cert_payload->get_cert_encoding(cert_payload);
@@ -158,36 +279,14 @@ static void process_certs(private_isakmp_cert_pre_t *this, message_t *message)
switch (encoding)
{
case ENC_X509_SIGNATURE:
- {
- cert = cert_payload->get_cert(cert_payload);
- if (cert)
- {
- if (first)
- { /* the first is an end entity certificate */
- DBG1(DBG_IKE, "received end entity cert \"%Y\"",
- cert->get_subject(cert));
- auth->add(auth, AUTH_HELPER_SUBJECT_CERT, cert);
- first = FALSE;
- }
- else
- {
- DBG1(DBG_IKE, "received issuer cert \"%Y\"",
- cert->get_subject(cert));
- auth->add(auth, AUTH_HELPER_IM_CERT, cert);
- }
- }
+ process_x509(cert_payload, auth, &first);
break;
- }
case ENC_CRL:
- cert = cert_payload->get_cert(cert_payload);
- if (cert)
- {
- DBG1(DBG_IKE, "received CRL \"%Y\"",
- cert->get_subject(cert));
- auth->add(auth, AUTH_HELPER_REVOCATION_CERT, cert);
- }
+ process_crl(cert_payload, auth);
break;
case ENC_PKCS7_WRAPPED_X509:
+ process_pkcs7(cert_payload, auth);
+ break;
case ENC_PGP:
case ENC_DNS_SIGNED_KEY:
case ENC_KERBEROS_TOKEN:
diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_natd.c b/src/libcharon/sa/ikev1/tasks/isakmp_natd.c
index 50bf1612d..5a779ff62 100644
--- a/src/libcharon/sa/ikev1/tasks/isakmp_natd.c
+++ b/src/libcharon/sa/ikev1/tasks/isakmp_natd.c
@@ -15,6 +15,28 @@
* 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.
+ */
+
#include "isakmp_natd.h"
#include <string.h>
@@ -75,6 +97,18 @@ struct private_isakmp_natd_t {
};
/**
+ * Get NAT-D payload type (RFC 3947 or RFC 3947 drafts).
+ */
+static payload_type_t get_nat_d_payload_type(ike_sa_t *ike_sa)
+{
+ if (ike_sa->supports_extension(ike_sa, EXT_NATT_DRAFT_02_03))
+ {
+ return NAT_D_DRAFT_00_03_V1;
+ }
+ return NAT_D_V1;
+}
+
+/**
* Build NAT detection hash for a host.
*/
static chunk_t generate_natd_hash(private_isakmp_natd_t *this,
@@ -162,7 +196,7 @@ static hash_payload_t *build_natd_payload(private_isakmp_natd_t *this, bool src,
{
return NULL;
}
- payload = hash_payload_create(NAT_D_V1);
+ payload = hash_payload_create(get_nat_d_payload_type(this->ike_sa));
payload->set_hash(payload, hash);
chunk_free(&hash);
return payload;
@@ -221,7 +255,8 @@ static void process_payloads(private_isakmp_natd_t *this, message_t *message)
enumerator = message->create_payload_enumerator(message);
while (enumerator->enumerate(enumerator, &payload))
{
- if (payload->get_type(payload) != NAT_D_V1)
+ if (payload->get_type(payload) != NAT_D_V1 &&
+ payload->get_type(payload) != NAT_D_DRAFT_00_03_V1)
{
continue;
}
@@ -350,7 +385,7 @@ METHOD(task_t, process_r, status_t,
switch (message->get_exchange_type(message))
{
case AGGRESSIVE:
- { /* proccess NAT-D payloads in the second request, already added ours
+ { /* process NAT-D payloads in the second request, already added ours
* in the first response */
result = SUCCESS;
/* fall */
diff --git a/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c b/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c
index 4fd0ef39b..2ff2b55e9 100644
--- a/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c
+++ b/src/libcharon/sa/ikev1/tasks/isakmp_vendor.c
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2012-2013 Tobias Brunner
* Copyright (C) 2009 Martin Willi
* Hochschule fuer Technik Rapperswil
*
@@ -13,6 +14,28 @@
* 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.
+ */
+
#include "isakmp_vendor.h"
#include <daemon.h>
@@ -39,6 +62,11 @@ struct private_isakmp_vendor_t {
* Are we the inititator of this task
*/
bool initiator;
+
+ /**
+ * Index of best nat traversal VID found
+ */
+ int best_natt_ext;
};
/**
@@ -65,76 +93,132 @@ static struct {
{ "XAuth", EXT_XAUTH, TRUE, 8,
"\x09\x00\x26\x89\xdf\xd6\xb7\x12"},
- /* NAT-Traversal, MD5("RFC 3947") */
- { "NAT-T (RFC 3947)", EXT_NATT, TRUE, 16,
- "\x4a\x13\x1c\x81\x07\x03\x58\x45\x5c\x57\x28\xf2\x0e\x95\x45\x2f"},
-
/* Dead peer detection, RFC 3706 */
{ "DPD", EXT_DPD, TRUE, 16,
"\xaf\xca\xd7\x13\x68\xa1\xf1\xc9\x6b\x86\x96\xfc\x77\x57\x01\x00"},
- { "draft-stenberg-ipsec-nat-traversal-01", 0, FALSE, 16,
- "\x27\xba\xb5\xdc\x01\xea\x07\x60\xea\x4e\x31\x90\xac\x27\xc0\xd0"},
+ { "Cisco Unity", EXT_CISCO_UNITY, FALSE, 16,
+ "\x12\xf5\xf2\x8c\x45\x71\x68\xa9\x70\x2d\x9f\xe2\x74\xcc\x01\x00"},
- { "draft-stenberg-ipsec-nat-traversal-02", 0, FALSE, 16,
- "\x61\x05\xc4\x22\xe7\x68\x47\xe4\x3f\x96\x84\x80\x12\x92\xae\xcd"},
+ /* Proprietary IKE fragmentation extension. Capabilities are handled
+ * specially on receipt of this VID. */
+ { "FRAGMENTATION", EXT_IKE_FRAGMENTATION, FALSE, 20,
+ "\x40\x48\xb7\xd5\x6e\xbc\xe8\x85\x25\xe7\xde\x7f\x00\xd6\xc2\xd3\x80\x00\x00\x00"},
- { "draft-ietf-ipsec-nat-t-ike", 0, FALSE, 16,
- "\x4d\xf3\x79\x28\xe9\xfc\x4f\xd1\xb3\x26\x21\x70\xd5\x15\xc6\x62"},
+}, vendor_natt_ids[] = {
- { "draft-ietf-ipsec-nat-t-ike-00", 0, FALSE, 16,
- "\x44\x85\x15\x2d\x18\xb6\xbb\xcd\x0b\xe8\xa8\x46\x95\x79\xdd\xcc"},
+ /* NAT-Traversal VIDs ordered by preference */
+
+ /* NAT-Traversal, MD5("RFC 3947") */
+ { "NAT-T (RFC 3947)", EXT_NATT, TRUE, 16,
+ "\x4a\x13\x1c\x81\x07\x03\x58\x45\x5c\x57\x28\xf2\x0e\x95\x45\x2f"},
- { "draft-ietf-ipsec-nat-t-ike-02", 0, FALSE, 16,
+ { "draft-ietf-ipsec-nat-t-ike-03", EXT_NATT | EXT_NATT_DRAFT_02_03,
+ FALSE, 16,
+ "\x7d\x94\x19\xa6\x53\x10\xca\x6f\x2c\x17\x9d\x92\x15\x52\x9d\x56"},
+
+ { "draft-ietf-ipsec-nat-t-ike-02", EXT_NATT | EXT_NATT_DRAFT_02_03,
+ FALSE, 16,
"\xcd\x60\x46\x43\x35\xdf\x21\xf8\x7c\xfd\xb2\xfc\x68\xb6\xa4\x48"},
- { "draft-ietf-ipsec-nat-t-ike-02\\n", 0, FALSE, 16,
+ { "draft-ietf-ipsec-nat-t-ike-02\\n", EXT_NATT | EXT_NATT_DRAFT_02_03,
+ TRUE, 16,
"\x90\xcb\x80\x91\x3e\xbb\x69\x6e\x08\x63\x81\xb5\xec\x42\x7b\x1f"},
- { "draft-ietf-ipsec-nat-t-ike-03", 0, FALSE, 16,
- "\x7d\x94\x19\xa6\x53\x10\xca\x6f\x2c\x17\x9d\x92\x15\x52\x9d\x56"},
+ { "draft-ietf-ipsec-nat-t-ike-08", 0, FALSE, 16,
+ "\x8f\x8d\x83\x82\x6d\x24\x6b\x6f\xc7\xa8\xa6\xa4\x28\xc1\x1d\xe8"},
- { "draft-ietf-ipsec-nat-t-ike-04", 0, FALSE, 16,
- "\x99\x09\xb6\x4e\xed\x93\x7c\x65\x73\xde\x52\xac\xe9\x52\xfa\x6b"},
+ { "draft-ietf-ipsec-nat-t-ike-07", 0, FALSE, 16,
+ "\x43\x9b\x59\xf8\xba\x67\x6c\x4c\x77\x37\xae\x22\xea\xb8\xf5\x82"},
+
+ { "draft-ietf-ipsec-nat-t-ike-06", 0, FALSE, 16,
+ "\x4d\x1e\x0e\x13\x6d\xea\xfa\x34\xc4\xf3\xea\x9f\x02\xec\x72\x85"},
{ "draft-ietf-ipsec-nat-t-ike-05", 0, FALSE, 16,
"\x80\xd0\xbb\x3d\xef\x54\x56\x5e\xe8\x46\x45\xd4\xc8\x5c\xe3\xee"},
- { "draft-ietf-ipsec-nat-t-ike-06", 0, FALSE, 16,
- "\x4d\x1e\x0e\x13\x6d\xea\xfa\x34\xc4\xf3\xea\x9f\x02\xec\x72\x85"},
+ { "draft-ietf-ipsec-nat-t-ike-04", 0, FALSE, 16,
+ "\x99\x09\xb6\x4e\xed\x93\x7c\x65\x73\xde\x52\xac\xe9\x52\xfa\x6b"},
- { "draft-ietf-ipsec-nat-t-ike-07", 0, FALSE, 16,
- "\x43\x9b\x59\xf8\xba\x67\x6c\x4c\x77\x37\xae\x22\xea\xb8\xf5\x82"},
+ { "draft-ietf-ipsec-nat-t-ike-00", 0, FALSE, 16,
+ "\x44\x85\x15\x2d\x18\xb6\xbb\xcd\x0b\xe8\xa8\x46\x95\x79\xdd\xcc"},
- { "draft-ietf-ipsec-nat-t-ike-08", 0, FALSE, 16,
- "\x8f\x8d\x83\x82\x6d\x24\x6b\x6f\xc7\xa8\xa6\xa4\x28\xc1\x1d\xe8"},
+ { "draft-ietf-ipsec-nat-t-ike", 0, FALSE, 16,
+ "\x4d\xf3\x79\x28\xe9\xfc\x4f\xd1\xb3\x26\x21\x70\xd5\x15\xc6\x62"},
+
+ { "draft-stenberg-ipsec-nat-traversal-02", 0, FALSE, 16,
+ "\x61\x05\xc4\x22\xe7\x68\x47\xe4\x3f\x96\x84\x80\x12\x92\xae\xcd"},
+
+ { "draft-stenberg-ipsec-nat-traversal-01", 0, FALSE, 16,
+ "\x27\xba\xb5\xdc\x01\xea\x07\x60\xea\x4e\x31\x90\xac\x27\xc0\xd0"},
- { "Cisco Unity", EXT_CISCO_UNITY, FALSE, 16,
- "\x12\xf5\xf2\x8c\x45\x71\x68\xa9\x70\x2d\x9f\xe2\x74\xcc\x01\x00"},
};
+/**
+ * According to racoon 0x80000000 seems to indicate support for fragmentation
+ * of Aggressive and Main mode messages. 0x40000000 seems to indicate support
+ * for fragmentation of base ISAKMP messages (Cisco adds that and thus sends
+ * 0xc0000000)
+ */
+static const u_int32_t fragmentation_ike = 0x80000000;
+
+/**
+ * Check if the given vendor ID indicate support for fragmentation
+ */
+static bool fragmentation_supported(chunk_t data, int i)
+{
+ if (vendor_ids[i].extension == EXT_IKE_FRAGMENTATION &&
+ data.len == 20 && memeq(data.ptr, vendor_ids[i].id, 16))
+ {
+ return untoh32(&data.ptr[16]) & fragmentation_ike;
+ }
+ return FALSE;
+}
+
METHOD(task_t, build, status_t,
private_isakmp_vendor_t *this, message_t *message)
{
vendor_id_payload_t *vid_payload;
- bool strongswan, cisco_unity;
+ bool strongswan, cisco_unity, fragmentation;
+ ike_cfg_t *ike_cfg;
int i;
strongswan = lib->settings->get_bool(lib->settings,
- "%s.send_vendor_id", FALSE, charon->name);
+ "%s.send_vendor_id", FALSE, charon->name);
cisco_unity = lib->settings->get_bool(lib->settings,
- "%s.cisco_unity", FALSE, charon->name);
+ "%s.cisco_unity", FALSE, charon->name);
+ ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
+ fragmentation = ike_cfg->fragmentation(ike_cfg) != FRAGMENTATION_NO;
+ if (!this->initiator && fragmentation)
+ {
+ fragmentation = this->ike_sa->supports_extension(this->ike_sa,
+ EXT_IKE_FRAGMENTATION);
+ }
for (i = 0; i < countof(vendor_ids); i++)
{
if (vendor_ids[i].send ||
(vendor_ids[i].extension == EXT_STRONGSWAN && strongswan) ||
- (vendor_ids[i].extension == EXT_CISCO_UNITY && cisco_unity))
+ (vendor_ids[i].extension == EXT_CISCO_UNITY && cisco_unity) ||
+ (vendor_ids[i].extension == EXT_IKE_FRAGMENTATION && fragmentation))
{
+ DBG2(DBG_IKE, "sending %s vendor ID", vendor_ids[i].desc);
vid_payload = vendor_id_payload_create_data(VENDOR_ID_V1,
chunk_clone(chunk_create(vendor_ids[i].id, vendor_ids[i].len)));
message->add_payload(message, &vid_payload->payload_interface);
}
}
+ for (i = 0; i < countof(vendor_natt_ids); i++)
+ {
+ if ((this->initiator && vendor_natt_ids[i].send) ||
+ this->best_natt_ext == i)
+ {
+ DBG2(DBG_IKE, "sending %s vendor ID", vendor_natt_ids[i].desc);
+ vid_payload = vendor_id_payload_create_data(VENDOR_ID_V1,
+ chunk_clone(chunk_create(vendor_natt_ids[i].id,
+ vendor_natt_ids[i].len)));
+ message->add_payload(message, &vid_payload->payload_interface);
+ }
+ }
return this->initiator ? NEED_MORE : SUCCESS;
}
@@ -160,7 +244,8 @@ METHOD(task_t, process, status_t,
for (i = 0; i < countof(vendor_ids); i++)
{
if (chunk_equals(data, chunk_create(vendor_ids[i].id,
- vendor_ids[i].len)))
+ vendor_ids[i].len)) ||
+ fragmentation_supported(data, i))
{
DBG1(DBG_IKE, "received %s vendor ID", vendor_ids[i].desc);
if (vendor_ids[i].extension)
@@ -169,6 +254,26 @@ METHOD(task_t, process, status_t,
vendor_ids[i].extension);
}
found = TRUE;
+ break;
+ }
+ }
+ if (!found)
+ {
+ for (i = 0; i < countof(vendor_natt_ids); i++)
+ {
+ if (chunk_equals(data, chunk_create(vendor_natt_ids[i].id,
+ vendor_natt_ids[i].len)))
+ {
+ DBG1(DBG_IKE, "received %s vendor ID",
+ vendor_natt_ids[i].desc);
+ if (vendor_natt_ids[i].extension &&
+ (i < this->best_natt_ext || this->best_natt_ext < 0))
+ {
+ this->best_natt_ext = i;
+ }
+ found = TRUE;
+ break;
+ }
}
}
if (!found)
@@ -179,6 +284,12 @@ METHOD(task_t, process, status_t,
}
enumerator->destroy(enumerator);
+ if (this->best_natt_ext >= 0)
+ {
+ this->ike_sa->enable_extension(this->ike_sa,
+ vendor_natt_ids[this->best_natt_ext].extension);
+ }
+
return this->initiator ? SUCCESS : NEED_MORE;
}
@@ -219,6 +330,7 @@ isakmp_vendor_t *isakmp_vendor_create(ike_sa_t *ike_sa, bool initiator)
},
.initiator = initiator,
.ike_sa = ike_sa,
+ .best_natt_ext = -1,
);
return &this->public;
diff --git a/src/libcharon/sa/ikev1/tasks/main_mode.c b/src/libcharon/sa/ikev1/tasks/main_mode.c
index 9ccf9abf5..bc9d4bbc3 100644
--- a/src/libcharon/sa/ikev1/tasks/main_mode.c
+++ b/src/libcharon/sa/ikev1/tasks/main_mode.c
@@ -241,7 +241,8 @@ METHOD(task_t, build_i, status_t,
this->lifetime += this->peer_cfg->get_over_time(this->peer_cfg);
proposals = this->ike_cfg->get_proposals(this->ike_cfg);
sa_payload = sa_payload_create_from_proposals_v1(proposals,
- this->lifetime, 0, this->method, MODE_NONE, FALSE, 0);
+ this->lifetime, 0, this->method, MODE_NONE,
+ ENCAP_NONE, 0);
proposals->destroy_offset(proposals, offsetof(proposal_t, destroy));
message->add_payload(message, &sa_payload->payload_interface);
@@ -455,7 +456,8 @@ METHOD(task_t, build_r, status_t,
sa_payload_t *sa_payload;
sa_payload = sa_payload_create_from_proposal_v1(this->proposal,
- this->lifetime, 0, this->method, MODE_NONE, FALSE, 0);
+ this->lifetime, 0, this->method, MODE_NONE,
+ ENCAP_NONE, 0);
message->add_payload(message, &sa_payload->payload_interface);
return NEED_MORE;
diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c
index 82a7238c3..1eae6aa93 100644
--- a/src/libcharon/sa/ikev1/tasks/quick_mode.c
+++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c
@@ -16,6 +16,28 @@
* 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.
+ */
+
#include "quick_mode.h"
#include <string.h>
@@ -561,7 +583,7 @@ static bool get_ts(private_quick_mode_t *this, message_t *message)
tsr = traffic_selector_create_from_subnet(hsr->clone(hsr),
hsr->get_family(hsr) == AF_INET ? 32 : 128, 0, 0);
}
- if (!this->initiator && this->mode == MODE_TRANSPORT && this->udp &&
+ if (this->mode == MODE_TRANSPORT && this->udp &&
(!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",
@@ -572,11 +594,11 @@ static bool get_ts(private_quick_mode_t *this, message_t *message)
if (this->initiator)
{
- /* check if peer selection valid */
+ /* check if peer selection is valid */
if (!tsr->is_contained_in(tsr, this->tsr) ||
!tsi->is_contained_in(tsi, this->tsi))
{
- DBG1(DBG_IKE, "peer selected invalid traffic selectors: ",
+ DBG1(DBG_IKE, "peer selected invalid traffic selectors: "
"%R for %R, %R for %R", tsi, this->tsi, tsr, this->tsr);
tsi->destroy(tsi);
tsr->destroy(tsr);
@@ -596,6 +618,34 @@ static bool get_ts(private_quick_mode_t *this, message_t *message)
}
/**
+ * 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 NAT_OA_DRAFT_00_03_V1;
+ }
+ return NAT_OA_V1;
+}
+
+/**
* Add NAT-OA payloads
*/
static void add_nat_oa_payloads(private_quick_mode_t *this, message_t *message)
@@ -603,6 +653,7 @@ static void add_nat_oa_payloads(private_quick_mode_t *this, message_t *message)
identification_t *id;
id_payload_t *nat_oa;
host_t *src, *dst;
+ payload_type_t nat_oa_payload_type;
src = message->get_source(message);
dst = message->get_destination(message);
@@ -610,15 +661,17 @@ static void add_nat_oa_payloads(private_quick_mode_t *this, message_t *message)
src = this->initiator ? src : dst;
dst = this->initiator ? dst : src;
+ 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(src->get_sockaddr(src));
- nat_oa = id_payload_create_from_identification(NAT_OA_V1, id);
+ 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(dst->get_sockaddr(dst));
- nat_oa = id_payload_create_from_identification(NAT_OA_V1, id);
+ nat_oa = id_payload_create_from_identification(nat_oa_payload_type, id);
message->add_payload(message, (payload_t*)nat_oa);
id->destroy(id);
}
@@ -697,6 +750,7 @@ METHOD(task_t, build_i, status_t,
linked_list_t *list, *tsi, *tsr;
proposal_t *proposal;
diffie_hellman_group_t group;
+ encap_t encap;
this->udp = this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY);
this->mode = this->config->get_mode(this->config);
@@ -735,19 +789,42 @@ METHOD(task_t, build_i, status_t,
DBG1(DBG_IKE, "allocating SPI from kernel failed");
return FAILED;
}
+ group = this->config->get_dh_group(this->config);
+ if (group != MODP_NONE)
+ {
+ 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);
+ return FAILED;
+ }
+ }
list = this->config->get_proposals(this->config, FALSE);
enumerator = list->create_enumerator(list);
while (enumerator->enumerate(enumerator, &proposal))
{
+ if (group != MODP_NONE)
+ {
+ if (!proposal->has_dh_group(proposal, group))
+ {
+ list->remove_at(list, enumerator);
+ proposal->destroy(proposal);
+ continue;
+ }
+ proposal->strip_dh(proposal, group);
+ }
proposal->set_spi(proposal, this->spi_i);
}
enumerator->destroy(enumerator);
get_lifetimes(this);
+ encap = get_encap(this->ike_sa, this->udp);
sa_payload = sa_payload_create_from_proposals_v1(list,
this->lifetime, this->lifebytes, AUTH_NONE,
- this->mode, this->udp, this->cpi_i);
+ this->mode, encap, this->cpi_i);
list->destroy_offset(list, offsetof(proposal_t, destroy));
message->add_payload(message, &sa_payload->payload_interface);
@@ -755,18 +832,8 @@ METHOD(task_t, build_i, status_t,
{
return FAILED;
}
-
- group = this->config->get_dh_group(this->config);
if (group != MODP_NONE)
{
- 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);
- return FAILED;
- }
add_ke(this, message);
}
if (!this->tsi)
@@ -1048,6 +1115,7 @@ METHOD(task_t, build_r, status_t,
case QM_INIT:
{
sa_payload_t *sa_payload;
+ encap_t encap;
this->spi_r = this->child_sa->alloc_spi(this->child_sa, PROTO_ESP);
if (!this->spi_r)
@@ -1074,9 +1142,10 @@ METHOD(task_t, build_r, status_t,
add_nat_oa_payloads(this, message);
}
+ encap = get_encap(this->ike_sa, this->udp);
sa_payload = sa_payload_create_from_proposal_v1(this->proposal,
this->lifetime, this->lifebytes, AUTH_NONE,
- this->mode, this->udp, this->cpi_r);
+ this->mode, encap, this->cpi_r);
message->add_payload(message, &sa_payload->payload_interface);
if (!add_nonce(this, &this->nonce_r, message))