summaryrefslogtreecommitdiff
path: root/src/libcharon/sa
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcharon/sa')
-rw-r--r--src/libcharon/sa/child_sa.c25
-rw-r--r--src/libcharon/sa/child_sa.h3
-rw-r--r--src/libcharon/sa/eap/eap_inner_method.h57
-rw-r--r--src/libcharon/sa/ike_sa.c56
-rw-r--r--src/libcharon/sa/ike_sa_manager.c76
-rw-r--r--src/libcharon/sa/ikev1/keymat_v1.c1
-rw-r--r--src/libcharon/sa/ikev1/task_manager_v1.c44
-rw-r--r--src/libcharon/sa/ikev1/tasks/aggressive_mode.c9
-rw-r--r--src/libcharon/sa/ikev1/tasks/main_mode.c9
-rw-r--r--src/libcharon/sa/ikev1/tasks/quick_delete.c4
-rw-r--r--src/libcharon/sa/ikev1/tasks/quick_mode.c60
-rw-r--r--src/libcharon/sa/ikev1/tasks/xauth.c66
-rw-r--r--src/libcharon/sa/ikev2/authenticators/eap_authenticator.c10
-rw-r--r--src/libcharon/sa/ikev2/task_manager_v2.c17
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_create.c9
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_delete.c4
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_rekey.c37
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_auth.c68
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_dpd.c10
-rw-r--r--src/libcharon/sa/xauth/xauth_manager.c7
20 files changed, 405 insertions, 167 deletions
diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c
index f02d836cf..463ad2e22 100644
--- a/src/libcharon/sa/child_sa.c
+++ b/src/libcharon/sa/child_sa.c
@@ -182,6 +182,16 @@ struct private_child_sa_t {
* last number of outbound bytes
*/
u_int64_t other_usebytes;
+
+ /**
+ * last number of inbound packets
+ */
+ u_int64_t my_usepackets;
+
+ /**
+ * last number of outbound bytes
+ */
+ u_int64_t other_usepackets;
};
/**
@@ -413,7 +423,7 @@ METHOD(child_sa_t, create_policy_enumerator, enumerator_t*,
static status_t update_usebytes(private_child_sa_t *this, bool inbound)
{
status_t status = FAILED;
- u_int64_t bytes;
+ u_int64_t bytes, packets;
if (inbound)
{
@@ -422,12 +432,13 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound)
status = hydra->kernel_interface->query_sa(hydra->kernel_interface,
this->other_addr, this->my_addr, this->my_spi,
proto_ike2ip(this->protocol), this->mark_in,
- &bytes);
+ &bytes, &packets);
if (status == SUCCESS)
{
if (bytes > this->my_usebytes)
{
this->my_usebytes = bytes;
+ this->my_usepackets = packets;
return SUCCESS;
}
return FAILED;
@@ -441,12 +452,13 @@ static status_t update_usebytes(private_child_sa_t *this, bool inbound)
status = hydra->kernel_interface->query_sa(hydra->kernel_interface,
this->my_addr, this->other_addr, this->other_spi,
proto_ike2ip(this->protocol), this->mark_out,
- &bytes);
+ &bytes, &packets);
if (status == SUCCESS)
{
if (bytes > this->other_usebytes)
{
this->other_usebytes = bytes;
+ this->other_usepackets = packets;
return SUCCESS;
}
return FAILED;
@@ -512,7 +524,8 @@ static void update_usetime(private_child_sa_t *this, bool inbound)
}
METHOD(child_sa_t, get_usestats, void,
- private_child_sa_t *this, bool inbound, time_t *time, u_int64_t *bytes)
+ private_child_sa_t *this, bool inbound,
+ time_t *time, u_int64_t *bytes, u_int64_t *packets)
{
if (update_usebytes(this, inbound) != FAILED)
{
@@ -529,6 +542,10 @@ METHOD(child_sa_t, get_usestats, void,
{
*bytes = inbound ? this->my_usebytes : this->other_usebytes;
}
+ if (packets)
+ {
+ *packets = inbound ? this->my_usepackets : this->other_usepackets;
+ }
}
METHOD(child_sa_t, get_mark, mark_t,
diff --git a/src/libcharon/sa/child_sa.h b/src/libcharon/sa/child_sa.h
index dae3f2c18..44511edf8 100644
--- a/src/libcharon/sa/child_sa.h
+++ b/src/libcharon/sa/child_sa.h
@@ -270,9 +270,10 @@ struct child_sa_t {
* @param inbound TRUE for inbound traffic, FALSE for outbound
* @param[out] time time of last use in seconds (NULL to ignore)
* @param[out] bytes number of processed bytes (NULL to ignore)
+ * @param[out] packets number of processed packets (NULL to ignore)
*/
void (*get_usestats)(child_sa_t *this, bool inbound, time_t *time,
- u_int64_t *bytes);
+ u_int64_t *bytes, u_int64_t *packets);
/**
* Get the mark used with this CHILD_SA.
diff --git a/src/libcharon/sa/eap/eap_inner_method.h b/src/libcharon/sa/eap/eap_inner_method.h
new file mode 100644
index 000000000..500852965
--- /dev/null
+++ b/src/libcharon/sa/eap/eap_inner_method.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2013 Andreas Steffen
+ * HSR 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.
+ */
+
+/**
+ * @defgroup eap_inner_method eap_inner_method
+ * @{ @ingroup eap
+ */
+
+#ifndef EAP_INNER_METHOD_H_
+#define EAP_INNER_METHOD_H_
+
+typedef struct eap_inner_method_t eap_inner_method_t;
+
+#include <library.h>
+
+#include "eap_method.h"
+
+/**
+ * Interface of a weak inner EAP method like EAP-TNC or PT-EAP
+ * that must be encapsulated in a strong TLS-based EAP method
+ */
+struct eap_inner_method_t {
+
+ /*
+ * Public EAP method interface
+ */
+ eap_method_t eap_method;
+
+ /*
+ * Get type of outer EAP authentication method
+ *
+ * @return outer EAP authentication type
+ */
+ eap_type_t (*get_auth_type)(eap_inner_method_t *this);
+
+ /*
+ * Set type of outer EAP Client/Server authentication
+ *
+ * @param type outer EAP authentication type
+ */
+ void (*set_auth_type)(eap_inner_method_t *this, eap_type_t type);
+
+};
+
+#endif /** EAP_INNER_METHOD_H_ @}*/
diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c
index 4029db11d..8c4dabd81 100644
--- a/src/libcharon/sa/ike_sa.c
+++ b/src/libcharon/sa/ike_sa.c
@@ -285,7 +285,7 @@ static time_t get_use_time(private_ike_sa_t* this, bool inbound)
enumerator = this->child_sas->create_enumerator(this->child_sas);
while (enumerator->enumerate(enumerator, &child_sa))
{
- child_sa->get_usestats(child_sa, inbound, &current, NULL);
+ child_sa->get_usestats(child_sa, inbound, &current, NULL, NULL);
use_time = max(use_time, current);
}
enumerator->destroy(enumerator);
@@ -900,7 +900,7 @@ METHOD(ike_sa_t, update_hosts, void,
else
{
/* update our address in any case */
- if (!me->equals(me, this->my_host))
+ if (force && !me->equals(me, this->my_host))
{
set_my_host(this, me->clone(me));
update = TRUE;
@@ -909,7 +909,8 @@ METHOD(ike_sa_t, update_hosts, void,
if (!other->equals(other, this->other_host))
{
/* update others address if we are NOT NATed */
- if (force || !has_condition(this, COND_NAT_HERE))
+ if ((has_condition(this, COND_NAT_THERE) &&
+ !has_condition(this, COND_NAT_HERE)) || force )
{
set_other_host(this, other->clone(other));
update = TRUE;
@@ -939,14 +940,38 @@ METHOD(ike_sa_t, update_hosts, void,
}
}
+/**
+ * Set configured DSCP value on packet
+ */
+static void set_dscp(private_ike_sa_t *this, packet_t *packet)
+{
+ ike_cfg_t *ike_cfg;
+
+ /* prefer IKE config on peer_cfg, as its selection is more accurate
+ * then the initial IKE config */
+ if (this->peer_cfg)
+ {
+ ike_cfg = this->peer_cfg->get_ike_cfg(this->peer_cfg);
+ }
+ else
+ {
+ ike_cfg = this->ike_cfg;
+ }
+ if (ike_cfg)
+ {
+ packet->set_dscp(packet, ike_cfg->get_dscp(ike_cfg));
+ }
+}
+
METHOD(ike_sa_t, generate_message, status_t,
private_ike_sa_t *this, message_t *message, packet_t **packet)
{
status_t status;
if (message->is_encoded(message))
- { /* already done */
+ { /* already encoded in task, but set DSCP value */
*packet = message->get_packet(message);
+ set_dscp(this, *packet);
return SUCCESS;
}
this->stats[STAT_OUTBOUND] = time_monotonic(NULL);
@@ -955,6 +980,7 @@ METHOD(ike_sa_t, generate_message, status_t,
status = message->generate(message, this->keymat, packet);
if (status == SUCCESS)
{
+ set_dscp(this, *packet);
charon->bus->message(charon->bus, message, FALSE, FALSE);
}
return status;
@@ -1225,24 +1251,6 @@ METHOD(ike_sa_t, process_message, status_t,
{ /* do not handle messages in passive state */
return FAILED;
}
- switch (message->get_exchange_type(message))
- {
- case ID_PROT:
- case AGGRESSIVE:
- case IKE_SA_INIT:
- case IKE_AUTH:
- if (this->state != IKE_CREATED &&
- this->state != IKE_CONNECTING &&
- message->get_first_payload_type(message) != FRAGMENT_V1)
- {
- DBG1(DBG_IKE, "ignoring %N in established IKE_SA state",
- exchange_type_names, message->get_exchange_type(message));
- return FAILED;
- }
- break;
- default:
- break;
- }
if (message->get_major_version(message) != this->version)
{
DBG1(DBG_IKE, "ignoring %N IKEv%u exchange on %N SA",
@@ -1437,6 +1445,10 @@ METHOD(ike_sa_t, delete_, status_t,
}
/* FALL */
case IKE_ESTABLISHED:
+ if (time_monotonic(NULL) >= this->stats[STAT_DELETE])
+ { /* IKE_SA hard lifetime hit */
+ charon->bus->alert(charon->bus, ALERT_IKE_SA_EXPIRED);
+ }
this->task_manager->queue_ike_delete(this->task_manager);
return this->task_manager->initiate(this->task_manager);
case IKE_CREATED:
diff --git a/src/libcharon/sa/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c
index 2ac8c3123..4fbc4da8e 100644
--- a/src/libcharon/sa/ike_sa_manager.c
+++ b/src/libcharon/sa/ike_sa_manager.c
@@ -108,9 +108,9 @@ struct entry_t {
identification_t *other_id;
/**
- * message ID currently processing, if any
+ * message ID or hash of currently processing message, -1 if none
*/
- u_int32_t message_id;
+ u_int32_t processing;
};
/**
@@ -135,23 +135,12 @@ static status_t entry_destroy(entry_t *this)
*/
static entry_t *entry_create()
{
- entry_t *this = malloc_thing(entry_t);
-
- this->waiting_threads = 0;
- this->condvar = condvar_create(CONDVAR_TYPE_DEFAULT);
-
- /* we set checkout flag when we really give it out */
- this->checked_out = FALSE;
- this->driveout_new_threads = FALSE;
- this->driveout_waiting_threads = FALSE;
- this->message_id = -1;
- this->init_hash = chunk_empty;
- this->other = NULL;
- this->half_open = FALSE;
- this->my_id = NULL;
- this->other_id = NULL;
- this->ike_sa_id = NULL;
- this->ike_sa = NULL;
+ entry_t *this;
+
+ INIT(this,
+ .condvar = condvar_create(CONDVAR_TYPE_DEFAULT),
+ .processing = -1,
+ );
return this;
}
@@ -1171,6 +1160,20 @@ METHOD(ike_sa_manager_t, checkout_new, ike_sa_t*,
return ike_sa;
}
+/**
+ * Get the message ID or message hash to detect early retransmissions
+ */
+static u_int32_t get_message_id_or_hash(message_t *message)
+{
+ /* Use the message ID, or the message hash in IKEv1 Main/Aggressive mode */
+ if (message->get_major_version(message) == IKEV1_MAJOR_VERSION &&
+ message->get_message_id(message) == 0)
+ {
+ return chunk_hash(message->get_packet_data(message));
+ }
+ return message->get_message_id(message);
+}
+
METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*,
private_ike_sa_manager_t* this, message_t *message)
{
@@ -1246,7 +1249,7 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*,
entry->checked_out = TRUE;
unlock_single_segment(this, segment);
- entry->message_id = message->get_message_id(message);
+ entry->processing = get_message_id_or_hash(message);
entry->init_hash = hash;
DBG2(DBG_MGR, "created IKE_SA %s[%u]",
@@ -1290,12 +1293,11 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*,
if (get_entry_by_id(this, id, &entry, &segment) == SUCCESS)
{
- /* only check out in IKEv2 if we are not already processing it */
- if (message->get_request(message) &&
- message->get_message_id(message) == entry->message_id)
+ /* only check out if we are not already processing it. */
+ if (entry->processing == get_message_id_or_hash(message))
{
DBG1(DBG_MGR, "ignoring request with ID %u, already processing",
- entry->message_id);
+ entry->processing);
}
else if (wait_for_entry(this, entry, segment))
{
@@ -1305,7 +1307,7 @@ METHOD(ike_sa_manager_t, checkout_by_message, ike_sa_t*,
entry->checked_out = TRUE;
if (message->get_first_payload_type(message) != FRAGMENT_V1)
{
- entry->message_id = message->get_message_id(message);
+ entry->processing = get_message_id_or_hash(message);
}
if (ike_id->get_responder_spi(ike_id) == 0)
{
@@ -1564,7 +1566,7 @@ METHOD(ike_sa_manager_t, checkin, void,
entry->ike_sa_id->replace_values(entry->ike_sa_id, ike_sa->get_id(ike_sa));
/* signal waiting threads */
entry->checked_out = FALSE;
- entry->message_id = -1;
+ entry->processing = -1;
/* check if this SA is half-open */
if (entry->half_open && ike_sa->get_state(ike_sa) != IKE_CONNECTING)
{
@@ -1745,6 +1747,23 @@ METHOD(ike_sa_manager_t, create_id_enumerator, enumerator_t*,
(void*)id_enumerator_cleanup, ids);
}
+/**
+ * Move all CHILD_SAs from old to new
+ */
+static void adopt_children(ike_sa_t *old, ike_sa_t *new)
+{
+ enumerator_t *enumerator;
+ child_sa_t *child_sa;
+
+ enumerator = old->create_child_sa_enumerator(old);
+ while (enumerator->enumerate(enumerator, &child_sa))
+ {
+ old->remove_child_sa(old, enumerator);
+ new->add_child_sa(new, child_sa);
+ }
+ enumerator->destroy(enumerator);
+}
+
METHOD(ike_sa_manager_t, check_uniqueness, bool,
private_ike_sa_manager_t *this, ike_sa_t *ike_sa, bool force_replace)
{
@@ -1782,6 +1801,7 @@ METHOD(ike_sa_manager_t, check_uniqueness, bool,
{
DBG1(DBG_IKE, "destroying duplicate IKE_SA for peer '%Y', "
"received INITIAL_CONTACT", other);
+ charon->bus->ike_updown(charon->bus, duplicate, FALSE);
checkin_and_destroy(this, duplicate);
continue;
}
@@ -1796,6 +1816,10 @@ METHOD(ike_sa_manager_t, check_uniqueness, bool,
{
case UNIQUE_REPLACE:
charon->bus->alert(charon->bus, ALERT_UNIQUE_REPLACE);
+ if (duplicate->get_version(duplicate) == IKEV1)
+ {
+ adopt_children(duplicate, ike_sa);
+ }
DBG1(DBG_IKE, "deleting duplicate IKE_SA for peer "
"'%Y' due to uniqueness policy", other);
status = duplicate->delete(duplicate);
diff --git a/src/libcharon/sa/ikev1/keymat_v1.c b/src/libcharon/sa/ikev1/keymat_v1.c
index eb642109b..39e4cad20 100644
--- a/src/libcharon/sa/ikev1/keymat_v1.c
+++ b/src/libcharon/sa/ikev1/keymat_v1.c
@@ -431,6 +431,7 @@ METHOD(keymat_v1_t, derive_ike_keys, bool,
{
case AUTH_PSK:
case AUTH_XAUTH_INIT_PSK:
+ case AUTH_XAUTH_RESP_PSK:
{ /* SKEYID = prf(pre-shared-key, Ni_b | Nr_b) */
chunk_t psk;
if (!shared_key)
diff --git a/src/libcharon/sa/ikev1/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c
index 8a4761d5c..709033cb5 100644
--- a/src/libcharon/sa/ikev1/task_manager_v1.c
+++ b/src/libcharon/sa/ikev1/task_manager_v1.c
@@ -411,7 +411,7 @@ static bool send_fragment(private_task_manager_t *this, bool request,
static bool send_packet(private_task_manager_t *this, bool request,
packet_t *packet)
{
- fragmentation_t fragmentation = FRAGMENTATION_NO;
+ bool use_frags = FALSE;
ike_cfg_t *ike_cfg;
host_t *src, *dst;
chunk_t data;
@@ -419,12 +419,21 @@ static bool send_packet(private_task_manager_t *this, bool request,
ike_cfg = this->ike_sa->get_ike_cfg(this->ike_sa);
if (ike_cfg)
{
- fragmentation = ike_cfg->fragmentation(ike_cfg);
+ switch (ike_cfg->fragmentation(ike_cfg))
+ {
+ case FRAGMENTATION_FORCE:
+ use_frags = TRUE;
+ break;
+ case FRAGMENTATION_YES:
+ use_frags = this->ike_sa->supports_extension(this->ike_sa,
+ EXT_IKE_FRAGMENTATION);
+ break;
+ default:
+ break;
+ }
}
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)))
+ if (data.len > this->frag.size && use_frags)
{
fragment_payload_t *fragment;
u_int8_t num, count;
@@ -1163,6 +1172,15 @@ static status_t process_response(private_task_manager_t *this,
if (message->get_exchange_type(message) != this->initiating.type)
{
+ /* Windows server sends a fourth quick mode message having an initial
+ * contact notify. Ignore this message for compatibility. */
+ if (this->initiating.type == EXCHANGE_TYPE_UNDEFINED &&
+ message->get_exchange_type(message) == QUICK_MODE &&
+ message->get_notify(message, INITIAL_CONTACT))
+ {
+ DBG1(DBG_IKE, "ignoring fourth Quick Mode message");
+ return SUCCESS;
+ }
DBG1(DBG_IKE, "received %N response, but expected %N",
exchange_type_names, message->get_exchange_type(message),
exchange_type_names, this->initiating.type);
@@ -1471,6 +1489,21 @@ METHOD(task_manager_t, process_message, status_t,
charon->bus->alert(charon->bus, ALERT_RETRANSMIT_RECEIVE, msg);
return SUCCESS;
}
+
+ /* reject Main/Aggressive Modes once established */
+ if (msg->get_exchange_type(msg) == ID_PROT ||
+ msg->get_exchange_type(msg) == AGGRESSIVE)
+ {
+ if (this->ike_sa->get_state(this->ike_sa) != IKE_CREATED &&
+ this->ike_sa->get_state(this->ike_sa) != IKE_CONNECTING &&
+ msg->get_first_payload_type(msg) != FRAGMENT_V1)
+ {
+ DBG1(DBG_IKE, "ignoring %N in established IKE_SA state",
+ exchange_type_names, msg->get_exchange_type(msg));
+ return FAILED;
+ }
+ }
+
if (msg->get_exchange_type(msg) == TRANSACTION &&
this->active_tasks->get_count(this->active_tasks))
{ /* main mode not yet complete, queue XAuth/Mode config tasks */
@@ -2030,4 +2063,3 @@ task_manager_v1_t *task_manager_v1_create(ike_sa_t *ike_sa)
return &this->public;
}
-
diff --git a/src/libcharon/sa/ikev1/tasks/aggressive_mode.c b/src/libcharon/sa/ikev1/tasks/aggressive_mode.c
index 7336d5d64..6b00706bf 100644
--- a/src/libcharon/sa/ikev1/tasks/aggressive_mode.c
+++ b/src/libcharon/sa/ikev1/tasks/aggressive_mode.c
@@ -30,6 +30,7 @@
#include <sa/ikev1/tasks/informational.h>
#include <sa/ikev1/tasks/isakmp_delete.h>
#include <processing/jobs/adopt_children_job.h>
+#include <processing/jobs/delete_ike_sa_job.h>
typedef struct private_aggressive_mode_t private_aggressive_mode_t;
@@ -299,8 +300,14 @@ METHOD(task_t, build_i, status_t,
case AUTH_XAUTH_INIT_PSK:
case AUTH_XAUTH_INIT_RSA:
case AUTH_HYBRID_INIT_RSA:
- /* wait for XAUTH request */
+ { /* wait for XAUTH request, since this may never come,
+ * we queue a timeout */
+ job_t *job = (job_t*)delete_ike_sa_job_create(
+ this->ike_sa->get_id(this->ike_sa), FALSE);
+ lib->scheduler->schedule_job(lib->scheduler, job,
+ HALF_OPEN_IKE_SA_TIMEOUT);
break;
+ }
case AUTH_XAUTH_RESP_PSK:
case AUTH_XAUTH_RESP_RSA:
case AUTH_HYBRID_RESP_RSA:
diff --git a/src/libcharon/sa/ikev1/tasks/main_mode.c b/src/libcharon/sa/ikev1/tasks/main_mode.c
index bc9d4bbc3..441bd7a78 100644
--- a/src/libcharon/sa/ikev1/tasks/main_mode.c
+++ b/src/libcharon/sa/ikev1/tasks/main_mode.c
@@ -30,6 +30,7 @@
#include <sa/ikev1/tasks/informational.h>
#include <sa/ikev1/tasks/isakmp_delete.h>
#include <processing/jobs/adopt_children_job.h>
+#include <processing/jobs/delete_ike_sa_job.h>
typedef struct private_main_mode_t private_main_mode_t;
@@ -638,8 +639,14 @@ METHOD(task_t, process_i, status_t,
case AUTH_XAUTH_INIT_PSK:
case AUTH_XAUTH_INIT_RSA:
case AUTH_HYBRID_INIT_RSA:
- /* wait for XAUTH request */
+ { /* wait for XAUTH request, since this may never come,
+ * we queue a timeout */
+ job_t *job = (job_t*)delete_ike_sa_job_create(
+ this->ike_sa->get_id(this->ike_sa), FALSE);
+ lib->scheduler->schedule_job(lib->scheduler, job,
+ HALF_OPEN_IKE_SA_TIMEOUT);
break;
+ }
case AUTH_XAUTH_RESP_PSK:
case AUTH_XAUTH_RESP_RSA:
case AUTH_HYBRID_RESP_RSA:
diff --git a/src/libcharon/sa/ikev1/tasks/quick_delete.c b/src/libcharon/sa/ikev1/tasks/quick_delete.c
index db48bc58e..e9f06cbe3 100644
--- a/src/libcharon/sa/ikev1/tasks/quick_delete.c
+++ b/src/libcharon/sa/ikev1/tasks/quick_delete.c
@@ -97,8 +97,8 @@ static bool delete_child(private_quick_delete_t *this,
}
else
{
- child_sa->get_usestats(child_sa, TRUE, NULL, &bytes_in);
- child_sa->get_usestats(child_sa, FALSE, NULL, &bytes_out);
+ child_sa->get_usestats(child_sa, TRUE, NULL, &bytes_in, NULL);
+ child_sa->get_usestats(child_sa, FALSE, NULL, &bytes_out, NULL);
DBG0(DBG_IKE, "closing CHILD_SA %s{%d} with SPIs "
"%.8x_i (%llu bytes) %.8x_o (%llu bytes) and TS %#R=== %#R",
diff --git a/src/libcharon/sa/ikev1/tasks/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c
index 1eae6aa93..7a0fb5788 100644
--- a/src/libcharon/sa/ikev1/tasks/quick_mode.c
+++ b/src/libcharon/sa/ikev1/tasks/quick_mode.c
@@ -576,12 +576,12 @@ static bool get_ts(private_quick_mode_t *this, message_t *message)
if (!tsi)
{
tsi = traffic_selector_create_from_subnet(hsi->clone(hsi),
- hsi->get_family(hsi) == AF_INET ? 32 : 128, 0, 0);
+ 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);
+ hsr->get_family(hsr) == AF_INET ? 32 : 128, 0, 0, 65535);
}
if (this->mode == MODE_TRANSPORT && this->udp &&
(!tsi->is_host(tsi, hsi) || !tsr->is_host(tsr, hsr)))
@@ -594,20 +594,27 @@ static bool get_ts(private_quick_mode_t *this, message_t *message)
if (this->initiator)
{
+ traffic_selector_t *tsisub, *tsrsub;
+
/* check if peer selection is valid */
- if (!tsr->is_contained_in(tsr, this->tsr) ||
- !tsi->is_contained_in(tsi, this->tsi))
+ 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 = tsi;
- this->tsr = tsr;
+ this->tsi = tsisub;
+ this->tsr = tsrsub;
}
else
{
@@ -914,30 +921,37 @@ static void check_for_rekeyed_child(private_quick_mode_t *this)
enumerator_t *enumerator, *policies;
traffic_selector_t *local, *remote;
child_sa_t *child_sa;
+ proposal_t *proposal;
+ char *name;
+ name = this->config->get_name(this->config);
enumerator = this->ike_sa->create_child_sa_enumerator(this->ike_sa);
while (this->reqid == 0 && enumerator->enumerate(enumerator, &child_sa))
{
- if (child_sa->get_state(child_sa) == CHILD_INSTALLED &&
- streq(child_sa->get_name(child_sa),
- this->config->get_name(this->config)))
+ if (streq(child_sa->get_name(child_sa), name))
{
- policies = child_sa->create_policy_enumerator(child_sa);
- if (policies->enumerate(policies, &local, &remote))
+ proposal = child_sa->get_proposal(child_sa);
+ switch (child_sa->get_state(child_sa))
{
- if (local->equals(local, this->tsr) &&
- remote->equals(remote, this->tsi) &&
- this->proposal->equals(this->proposal,
- child_sa->get_proposal(child_sa)))
- {
- this->reqid = child_sa->get_reqid(child_sa);
- this->rekey = child_sa->get_spi(child_sa, TRUE);
- child_sa->set_state(child_sa, CHILD_REKEYING);
- DBG1(DBG_IKE, "detected rekeying of CHILD_SA %s{%u}",
- child_sa->get_name(child_sa), this->reqid);
- }
+ case CHILD_INSTALLED:
+ case CHILD_REKEYING:
+ policies = child_sa->create_policy_enumerator(child_sa);
+ if (policies->enumerate(policies, &local, &remote) &&
+ local->equals(local, this->tsr) &&
+ remote->equals(remote, this->tsi) &&
+ this->proposal->equals(this->proposal, proposal))
+ {
+ this->reqid = child_sa->get_reqid(child_sa);
+ this->rekey = child_sa->get_spi(child_sa, TRUE);
+ child_sa->set_state(child_sa, CHILD_REKEYING);
+ DBG1(DBG_IKE, "detected rekeying of CHILD_SA %s{%u}",
+ child_sa->get_name(child_sa), this->reqid);
+ }
+ policies->destroy(policies);
+ break;
+ default:
+ break;
}
- policies->destroy(policies);
}
}
enumerator->destroy(enumerator);
diff --git a/src/libcharon/sa/ikev1/tasks/xauth.c b/src/libcharon/sa/ikev1/tasks/xauth.c
index 10bea5636..31114e592 100644
--- a/src/libcharon/sa/ikev1/tasks/xauth.c
+++ b/src/libcharon/sa/ikev1/tasks/xauth.c
@@ -286,21 +286,55 @@ METHOD(task_t, build_i_status, status_t,
return NEED_MORE;
}
+METHOD(task_t, process_i_status, status_t,
+ private_xauth_t *this, message_t *message)
+{
+ cp_payload_t *cp;
+
+ cp = (cp_payload_t*)message->get_payload(message, CONFIGURATION_V1);
+ if (!cp || cp->get_type(cp) != CFG_ACK)
+ {
+ DBG1(DBG_IKE, "received invalid XAUTH status response");
+ return FAILED;
+ }
+ if (this->status != XAUTH_OK)
+ {
+ DBG1(DBG_IKE, "destroying IKE_SA after failed XAuth authentication");
+ return FAILED;
+ }
+ if (!establish(this))
+ {
+ return FAILED;
+ }
+ this->ike_sa->set_condition(this->ike_sa, COND_XAUTH_AUTHENTICATED, TRUE);
+ lib->processor->queue_job(lib->processor, (job_t*)
+ adopt_children_job_create(this->ike_sa->get_id(this->ike_sa)));
+ return SUCCESS;
+}
+
METHOD(task_t, build_i, status_t,
private_xauth_t *this, message_t *message)
{
if (!this->xauth)
{
- cp_payload_t *cp;
+ cp_payload_t *cp = NULL;
this->xauth = load_method(this);
if (!this->xauth)
{
return FAILED;
}
- if (this->xauth->initiate(this->xauth, &cp) != NEED_MORE)
+ switch (this->xauth->initiate(this->xauth, &cp))
{
- return FAILED;
+ case NEED_MORE:
+ break;
+ case SUCCESS:
+ DESTROY_IF(cp);
+ this->status = XAUTH_OK;
+ this->public.task.process = _process_i_status;
+ return build_i_status(this, message);
+ default:
+ return FAILED;
}
message->add_payload(message, (payload_t *)cp);
return NEED_MORE;
@@ -411,32 +445,6 @@ METHOD(task_t, build_r, status_t,
return NEED_MORE;
}
-METHOD(task_t, process_i_status, status_t,
- private_xauth_t *this, message_t *message)
-{
- cp_payload_t *cp;
-
- cp = (cp_payload_t*)message->get_payload(message, CONFIGURATION_V1);
- if (!cp || cp->get_type(cp) != CFG_ACK)
- {
- DBG1(DBG_IKE, "received invalid XAUTH status response");
- return FAILED;
- }
- if (this->status != XAUTH_OK)
- {
- DBG1(DBG_IKE, "destroying IKE_SA after failed XAuth authentication");
- return FAILED;
- }
- if (!establish(this))
- {
- return FAILED;
- }
- this->ike_sa->set_condition(this->ike_sa, COND_XAUTH_AUTHENTICATED, TRUE);
- lib->processor->queue_job(lib->processor, (job_t*)
- adopt_children_job_create(this->ike_sa->get_id(this->ike_sa)));
- return SUCCESS;
-}
-
METHOD(task_t, process_i, status_t,
private_xauth_t *this, message_t *message)
{
diff --git a/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c b/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c
index aa0644033..b8359cc88 100644
--- a/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c
+++ b/src/libcharon/sa/ikev2/authenticators/eap_authenticator.c
@@ -667,6 +667,16 @@ METHOD(authenticator_t, build_client, status_t,
METHOD(authenticator_t, is_mutual, bool,
private_eap_authenticator_t *this)
{
+ if (this->method)
+ {
+ u_int32_t vendor;
+
+ if (this->method->get_type(this->method, &vendor) != EAP_IDENTITY ||
+ vendor != 0)
+ {
+ return this->method->is_mutual(this->method);
+ }
+ }
/* we don't know yet, but insist on it after EAP is complete */
this->require_mutual = TRUE;
return TRUE;
diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c
index ea0117c54..5298abf79 100644
--- a/src/libcharon/sa/ikev2/task_manager_v2.c
+++ b/src/libcharon/sa/ikev2/task_manager_v2.c
@@ -475,6 +475,7 @@ METHOD(task_manager_t, initiate, status_t,
break;
case FAILED:
default:
+ this->initiating.type = EXCHANGE_TYPE_UNDEFINED;
if (this->ike_sa->get_state(this->ike_sa) != IKE_CONNECTING)
{
charon->bus->ike_updown(charon->bus, this->ike_sa, FALSE);
@@ -1123,6 +1124,18 @@ METHOD(task_manager_t, process_message, status_t,
{
if (mid == this->responding.mid)
{
+ /* reject initial messages once established */
+ if (msg->get_exchange_type(msg) == IKE_SA_INIT ||
+ msg->get_exchange_type(msg) == IKE_AUTH)
+ {
+ if (this->ike_sa->get_state(this->ike_sa) != IKE_CREATED &&
+ this->ike_sa->get_state(this->ike_sa) != IKE_CONNECTING)
+ {
+ DBG1(DBG_IKE, "ignoring %N in established IKE_SA state",
+ exchange_type_names, msg->get_exchange_type(msg));
+ return FAILED;
+ }
+ }
if (this->ike_sa->get_state(this->ike_sa) == IKE_CREATED ||
this->ike_sa->get_state(this->ike_sa) == IKE_CONNECTING ||
msg->get_exchange_type(msg) != IKE_SA_INIT)
@@ -1163,6 +1176,10 @@ METHOD(task_manager_t, process_message, status_t,
{
DBG1(DBG_IKE, "received message ID %d, expected %d. Ignored",
mid, this->responding.mid);
+ if (msg->get_exchange_type(msg) == IKE_SA_INIT)
+ { /* clean up IKE_SA state if IKE_SA_INIT has invalid msg ID */
+ return DESTROY_ME;
+ }
}
}
else
diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c
index eb3972c29..32c0e8c4a 100644
--- a/src/libcharon/sa/ikev2/tasks/child_create.c
+++ b/src/libcharon/sa/ikev2/tasks/child_create.c
@@ -18,6 +18,7 @@
#include "child_create.h"
#include <daemon.h>
+#include <hydra.h>
#include <sa/ikev2/keymat_v2.h>
#include <crypto/diffie_hellman.h>
#include <credentials/certificates/x509.h>
@@ -615,6 +616,7 @@ static void build_payloads(private_child_create_t *this, message_t *message)
nonce_payload_t *nonce_payload;
ke_payload_t *ke_payload;
ts_payload_t *ts_payload;
+ kernel_feature_t features;
/* add SA payload */
if (this->initiator)
@@ -661,6 +663,13 @@ static void build_payloads(private_child_create_t *this, message_t *message)
default:
break;
}
+
+ features = hydra->kernel_interface->get_features(hydra->kernel_interface);
+ if (!(features & KERNEL_ESP_V3_TFC))
+ {
+ message->add_notify(message, FALSE, ESP_TFC_PADDING_NOT_SUPPORTED,
+ chunk_empty);
+ }
}
/**
diff --git a/src/libcharon/sa/ikev2/tasks/child_delete.c b/src/libcharon/sa/ikev2/tasks/child_delete.c
index 644af782c..8652942ad 100644
--- a/src/libcharon/sa/ikev2/tasks/child_delete.c
+++ b/src/libcharon/sa/ikev2/tasks/child_delete.c
@@ -264,8 +264,8 @@ static void log_children(private_child_delete_t *this)
}
else
{
- child_sa->get_usestats(child_sa, TRUE, NULL, &bytes_in);
- child_sa->get_usestats(child_sa, FALSE, NULL, &bytes_out);
+ child_sa->get_usestats(child_sa, TRUE, NULL, &bytes_in, NULL);
+ child_sa->get_usestats(child_sa, FALSE, NULL, &bytes_out, NULL);
DBG0(DBG_IKE, "closing CHILD_SA %s{%d} with SPIs %.8x_i "
"(%llu bytes) %.8x_o (%llu bytes) and TS %#R=== %#R",
diff --git a/src/libcharon/sa/ikev2/tasks/child_rekey.c b/src/libcharon/sa/ikev2/tasks/child_rekey.c
index f8c2ed141..262cb10e0 100644
--- a/src/libcharon/sa/ikev2/tasks/child_rekey.c
+++ b/src/libcharon/sa/ikev2/tasks/child_rekey.c
@@ -87,6 +87,24 @@ struct private_child_rekey_t {
};
/**
+ * Schedule a retry if rekeying temporary failed
+ */
+static void schedule_delayed_rekey(private_child_rekey_t *this)
+{
+ u_int32_t retry;
+ job_t *job;
+
+ retry = RETRY_INTERVAL - (random() % RETRY_JITTER);
+ job = (job_t*)rekey_child_sa_job_create(
+ this->child_sa->get_reqid(this->child_sa),
+ this->child_sa->get_protocol(this->child_sa),
+ this->child_sa->get_spi(this->child_sa, TRUE));
+ DBG1(DBG_IKE, "CHILD_SA rekeying failed, trying again in %d seconds", retry);
+ this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
+ lib->scheduler->schedule_job(lib->scheduler, job, retry);
+}
+
+/**
* Implementation of task_t.build for initiator, after rekeying
*/
static status_t build_i_delete(private_child_rekey_t *this, message_t *message)
@@ -166,8 +184,13 @@ METHOD(task_t, build_i, status_t,
}
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);
+ if (this->child_create->task.build(&this->child_create->task,
+ message) != NEED_MORE)
+ {
+ schedule_delayed_rekey(this);
+ return FAILED;
+ }
this->child_sa->set_state(this->child_sa, CHILD_REKEYING);
return NEED_MORE;
@@ -316,17 +339,7 @@ METHOD(task_t, process_i, status_t,
if (!(this->collision &&
this->collision->get_type(this->collision) == TASK_CHILD_DELETE))
{
- job_t *job;
- u_int32_t retry = RETRY_INTERVAL - (random() % RETRY_JITTER);
-
- job = (job_t*)rekey_child_sa_job_create(
- this->child_sa->get_reqid(this->child_sa),
- this->child_sa->get_protocol(this->child_sa),
- this->child_sa->get_spi(this->child_sa, TRUE));
- DBG1(DBG_IKE, "CHILD_SA rekeying failed, "
- "trying again in %d seconds", retry);
- this->child_sa->set_state(this->child_sa, CHILD_INSTALLED);
- lib->scheduler->schedule_job(lib->scheduler, job, retry);
+ schedule_delayed_rekey(this);
}
return SUCCESS;
}
diff --git a/src/libcharon/sa/ikev2/tasks/ike_auth.c b/src/libcharon/sa/ikev2/tasks/ike_auth.c
index 70efcd7af..942f97cf5 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_auth.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_auth.c
@@ -223,6 +223,18 @@ static auth_cfg_t *get_auth_cfg(private_ike_auth_t *this, bool local)
}
/**
+ * Move the currently active auth config to the auth configs completed
+ */
+static void apply_auth_cfg(private_ike_auth_t *this, bool local)
+{
+ auth_cfg_t *cfg;
+
+ cfg = auth_cfg_create();
+ cfg->merge(cfg, this->ike_sa->get_auth_cfg(this->ike_sa, local), local);
+ this->ike_sa->add_auth_cfg(this->ike_sa, local, cfg);
+}
+
+/**
* Check if we have should initiate another authentication round
*/
static bool do_another_auth(private_ike_auth_t *this)
@@ -307,7 +319,7 @@ static bool update_cfg_candidates(private_ike_auth_t *this, bool strict)
{
if (this->peer_cfg)
{
- bool complies = TRUE;
+ char *comply_error = NULL;
enumerator_t *e1, *e2, *tmp;
auth_cfg_t *c1, *c2;
@@ -324,22 +336,30 @@ static bool update_cfg_candidates(private_ike_auth_t *this, bool strict)
while (e1->enumerate(e1, &c1))
{
/* check if done authentications comply to configured ones */
- if ((!e2->enumerate(e2, &c2)) ||
- (!strict && !c1->complies(c1, c2, TRUE)) ||
- (strict && !c2->complies(c2, c1, TRUE)))
+ if (!e2->enumerate(e2, &c2))
+ {
+ comply_error = "insufficient authentication rounds";
+ break;
+ }
+ if (!strict && !c1->complies(c1, c2, TRUE))
{
- complies = FALSE;
+ comply_error = "non-matching authentication done";
+ break;
+ }
+ if (strict && !c2->complies(c2, c1, TRUE))
+ {
+ comply_error = "constraint checking failed";
break;
}
}
e1->destroy(e1);
e2->destroy(e2);
- if (complies)
+ if (!comply_error)
{
break;
}
- DBG1(DBG_CFG, "selected peer config '%s' inacceptable",
- this->peer_cfg->get_name(this->peer_cfg));
+ DBG1(DBG_CFG, "selected peer config '%s' inacceptable: %s",
+ this->peer_cfg->get_name(this->peer_cfg), comply_error);
this->peer_cfg->destroy(this->peer_cfg);
}
if (this->candidates->remove_first(this->candidates,
@@ -464,10 +484,7 @@ METHOD(task_t, build_i, status_t,
switch (this->my_auth->build(this->my_auth, message))
{
case SUCCESS:
- /* authentication step complete, reset authenticator */
- cfg = auth_cfg_create();
- cfg->merge(cfg, this->ike_sa->get_auth_cfg(this->ike_sa, TRUE), TRUE);
- this->ike_sa->add_auth_cfg(this->ike_sa, TRUE, cfg);
+ apply_auth_cfg(this, TRUE);
this->my_auth->destroy(this->my_auth);
this->my_auth = NULL;
break;
@@ -640,10 +657,7 @@ METHOD(task_t, process_r, status_t,
return NEED_MORE;
}
- /* store authentication information */
- cfg = auth_cfg_create();
- cfg->merge(cfg, this->ike_sa->get_auth_cfg(this->ike_sa, FALSE), FALSE);
- this->ike_sa->add_auth_cfg(this->ike_sa, FALSE, cfg);
+ apply_auth_cfg(this, FALSE);
if (!update_cfg_candidates(this, FALSE))
{
@@ -778,10 +792,7 @@ METHOD(task_t, build_r, status_t,
switch (this->my_auth->build(this->my_auth, message))
{
case SUCCESS:
- cfg = auth_cfg_create();
- cfg->merge(cfg, this->ike_sa->get_auth_cfg(this->ike_sa, TRUE),
- TRUE);
- this->ike_sa->add_auth_cfg(this->ike_sa, TRUE, cfg);
+ apply_auth_cfg(this, TRUE);
this->my_auth->destroy(this->my_auth);
this->my_auth = NULL;
break;
@@ -969,10 +980,10 @@ METHOD(task_t, process_i, status_t,
goto peer_auth_failed;
}
- /* store authentication information, reset authenticator */
- cfg = auth_cfg_create();
- cfg->merge(cfg, this->ike_sa->get_auth_cfg(this->ike_sa, FALSE), FALSE);
- this->ike_sa->add_auth_cfg(this->ike_sa, FALSE, cfg);
+ if (!mutual_eap)
+ {
+ apply_auth_cfg(this, FALSE);
+ }
}
if (this->my_auth)
@@ -980,10 +991,11 @@ METHOD(task_t, process_i, status_t,
switch (this->my_auth->process(this->my_auth, message))
{
case SUCCESS:
- cfg = auth_cfg_create();
- cfg->merge(cfg, this->ike_sa->get_auth_cfg(this->ike_sa, TRUE),
- TRUE);
- this->ike_sa->add_auth_cfg(this->ike_sa, TRUE, cfg);
+ apply_auth_cfg(this, TRUE);
+ if (this->my_auth->is_mutual(this->my_auth))
+ {
+ apply_auth_cfg(this, FALSE);
+ }
this->my_auth->destroy(this->my_auth);
this->my_auth = NULL;
this->do_another_auth = do_another_auth(this);
diff --git a/src/libcharon/sa/ikev2/tasks/ike_dpd.c b/src/libcharon/sa/ikev2/tasks/ike_dpd.c
index 28ccc2efe..7a33f7938 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_dpd.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_dpd.c
@@ -37,12 +37,6 @@ METHOD(task_t, return_need_more, status_t,
return NEED_MORE;
}
-METHOD(task_t, return_success, status_t,
- private_ike_dpd_t *this, message_t *message)
-{
- return SUCCESS;
-}
-
METHOD(task_t, get_type, task_type_t,
private_ike_dpd_t *this)
{
@@ -82,11 +76,11 @@ ike_dpd_t *ike_dpd_create(bool initiator)
if (initiator)
{
this->public.task.build = _return_need_more;
- this->public.task.process = _return_success;
+ this->public.task.process = (void*)return_success;
}
else
{
- this->public.task.build = _return_success;
+ this->public.task.build = (void*)return_success;
this->public.task.process = _return_need_more;
}
diff --git a/src/libcharon/sa/xauth/xauth_manager.c b/src/libcharon/sa/xauth/xauth_manager.c
index f0602a673..5709dc652 100644
--- a/src/libcharon/sa/xauth/xauth_manager.c
+++ b/src/libcharon/sa/xauth/xauth_manager.c
@@ -112,8 +112,11 @@ METHOD(xauth_manager_t, create_instance, xauth_method_t*,
enumerator = this->methods->create_enumerator(this->methods);
while (enumerator->enumerate(enumerator, &entry))
{
- if (role == entry->role &&
- (!name || streq(name, entry->name)))
+ if (!name && streq(entry->name, "noauth"))
+ { /* xauth-noauth has to be configured explicitly */
+ continue;
+ }
+ if (role == entry->role && (!name || streq(name, entry->name)))
{
method = entry->constructor(server, peer);
if (method)