aboutsummaryrefslogtreecommitdiffstats
path: root/src/libcharon/sa
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcharon/sa')
-rw-r--r--src/libcharon/sa/ike_sa.c23
-rw-r--r--src/libcharon/sa/ike_sa_manager.c76
-rw-r--r--src/libcharon/sa/ikev1/task_manager_v1.c16
-rw-r--r--src/libcharon/sa/ikev1/tasks/quick_mode.c19
-rw-r--r--src/libcharon/sa/ikev2/authenticators/eap_authenticator.c10
-rw-r--r--src/libcharon/sa/ikev2/task_manager_v2.c12
-rw-r--r--src/libcharon/sa/ikev2/tasks/child_create.c9
-rw-r--r--src/libcharon/sa/ikev2/tasks/ike_auth.c68
8 files changed, 152 insertions, 81 deletions
diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c
index 258e562d4..0157599c1 100644
--- a/src/libcharon/sa/ike_sa.c
+++ b/src/libcharon/sa/ike_sa.c
@@ -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;
@@ -1250,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",
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/task_manager_v1.c b/src/libcharon/sa/ikev1/task_manager_v1.c
index 8a4761d5c..7a84d1cef 100644
--- a/src/libcharon/sa/ikev1/task_manager_v1.c
+++ b/src/libcharon/sa/ikev1/task_manager_v1.c
@@ -1471,6 +1471,21 @@ METHOD(task_manager_t, process_message, status_t,
charon->bus->alert(charon->bus, ALERT_RETRANSMIT_RECEIVE, msg);
return SUCCESS;
}
+
+ /* reject Main/Agressive 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 +2045,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/quick_mode.c b/src/libcharon/sa/ikev1/tasks/quick_mode.c
index 1eae6aa93..afdff8cf9 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
{
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..29d8d830e 100644
--- a/src/libcharon/sa/ikev2/task_manager_v2.c
+++ b/src/libcharon/sa/ikev2/task_manager_v2.c
@@ -1123,6 +1123,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)
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/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);