diff options
Diffstat (limited to 'src/libcharon/sa')
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, ¤t, NULL); + child_sa->get_usestats(child_sa, inbound, ¤t, 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) |