diff options
author | Tobias Brunner <tobias@strongswan.org> | 2011-11-30 17:09:42 +0100 |
---|---|---|
committer | Tobias Brunner <tobias@strongswan.org> | 2012-03-20 17:31:10 +0100 |
commit | 1cc4ec46cfd9cf2209d6411d3605ea6835883c8a (patch) | |
tree | a5b99c443127e95943546fd066188dc11f82a923 | |
parent | 61e2a1ad8a6a3ea2d4a4c6f5298d685b9dcde60c (diff) | |
download | strongswan-1cc4ec46cfd9cf2209d6411d3605ea6835883c8a.tar.bz2 strongswan-1cc4ec46cfd9cf2209d6411d3605ea6835883c8a.tar.xz |
Task added for IKEv1 NAT detection.
There is already support for both Main and Aggressive Mode.
-rw-r--r-- | src/libcharon/Makefile.am | 6 | ||||
-rw-r--r-- | src/libcharon/sa/ike_sa.c | 3 | ||||
-rw-r--r-- | src/libcharon/sa/task_manager_v1.c | 5 | ||||
-rw-r--r-- | src/libcharon/sa/tasks/ike_natd_v1.c | 440 | ||||
-rw-r--r-- | src/libcharon/sa/tasks/ike_natd_v1.h | 50 | ||||
-rw-r--r-- | src/libcharon/sa/tasks/task.c | 2 | ||||
-rw-r--r-- | src/libcharon/sa/tasks/task.h | 4 |
7 files changed, 506 insertions, 4 deletions
diff --git a/src/libcharon/Makefile.am b/src/libcharon/Makefile.am index 538774928..aab293dd2 100644 --- a/src/libcharon/Makefile.am +++ b/src/libcharon/Makefile.am @@ -85,6 +85,7 @@ sa/tasks/ike_delete.c sa/tasks/ike_delete.h \ sa/tasks/ike_dpd.c sa/tasks/ike_dpd.h \ sa/tasks/ike_init.c sa/tasks/ike_init.h \ sa/tasks/ike_natd.c sa/tasks/ike_natd.h \ +sa/tasks/ike_natd_v1.c sa/tasks/ike_natd_v1.h \ sa/tasks/ike_mobike.c sa/tasks/ike_mobike.h \ sa/tasks/ike_rekey.c sa/tasks/ike_rekey.h \ sa/tasks/ike_reauth.c sa/tasks/ike_reauth.h \ @@ -93,8 +94,9 @@ sa/tasks/ike_vendor.c sa/tasks/ike_vendor.h \ sa/tasks/ike_vendor_v1.c sa/tasks/ike_vendor_v1.h \ sa/tasks/main_mode.c sa/tasks/main_mode.h \ sa/tasks/quick_mode.c sa/tasks/quick_mode.h \ -sa/tasks/task.c sa/tasks/task.h \ -sa/tasks/xauth_request.c sa/tasks/xauth_request.h +sa/tasks/xauth_request.c sa/tasks/xauth_request.h \ +sa/tasks/task.c sa/tasks/task.h + daemon.lo : $(top_builddir)/config.status diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index 0990111bf..5a21a8870 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -46,6 +46,7 @@ #include <sa/tasks/child_rekey.h> #include <sa/tasks/main_mode.h> #include <sa/tasks/quick_mode.h> +#include <sa/tasks/ike_natd_v1.h> #include <sa/tasks/ike_vendor_v1.h> #include <sa/tasks/xauth_request.h> #include <processing/jobs/retransmit_job.h> @@ -1123,6 +1124,8 @@ METHOD(ike_sa_t, initiate, status_t, this->task_manager->queue_task(this->task_manager, task); task = (task_t*)main_mode_create(&this->public, TRUE); this->task_manager->queue_task(this->task_manager, task); + task = (task_t*)ike_natd_v1_create(&this->public, TRUE); + this->task_manager->queue_task(this->task_manager, task); } else { diff --git a/src/libcharon/sa/task_manager_v1.c b/src/libcharon/sa/task_manager_v1.c index 9008b607a..18decb678 100644 --- a/src/libcharon/sa/task_manager_v1.c +++ b/src/libcharon/sa/task_manager_v1.c @@ -19,10 +19,10 @@ #include <math.h> #include <daemon.h> -#include <sa/tasks/ike_vendor.h> #include <sa/tasks/main_mode.h> #include <sa/tasks/quick_mode.h> #include <sa/tasks/xauth_request.h> +#include <sa/tasks/ike_natd_v1.h> #include <sa/tasks/ike_vendor_v1.h> #include <processing/jobs/retransmit_job.h> #include <processing/jobs/delete_ike_sa_job.h> @@ -267,6 +267,7 @@ METHOD(task_manager_t, initiate, status_t, if (activate_task(this, TASK_MAIN_MODE)) { exchange = ID_PROT; + activate_task(this, TASK_IKE_NATD_V1); } break; case IKE_CONNECTING: @@ -497,6 +498,8 @@ static status_t process_request(private_task_manager_t *this, this->passive_tasks->insert_last(this->passive_tasks, task); task = (task_t *)main_mode_create(this->ike_sa, FALSE); this->passive_tasks->insert_last(this->passive_tasks, task); + task = (task_t *)ike_natd_v1_create(this->ike_sa, FALSE); + this->passive_tasks->insert_last(this->passive_tasks, task); break; case AGGRESSIVE: /* TODO-IKEv1: agressive mode */ diff --git a/src/libcharon/sa/tasks/ike_natd_v1.c b/src/libcharon/sa/tasks/ike_natd_v1.c new file mode 100644 index 000000000..d7f957d50 --- /dev/null +++ b/src/libcharon/sa/tasks/ike_natd_v1.c @@ -0,0 +1,440 @@ +/* + * Copyright (C) 2006-2011 Tobias Brunner, + * Copyright (C) 2006-2007 Martin Willi + * Copyright (C) 2006 Daniel Roethlisberger + * 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. + */ + +#include "ike_natd_v1.h" + +#include <string.h> + +#include <hydra.h> +#include <daemon.h> +#include <sa/keymat_v1.h> +#include <config/peer_cfg.h> +#include <crypto/hashers/hasher.h> +#include <encoding/payloads/hash_payload.h> + +typedef struct private_ike_natd_v1_t private_ike_natd_v1_t; + +/** + * Private members of a ike_natt_t task. + */ +struct private_ike_natd_v1_t { + + /** + * Public interface. + */ + ike_natd_v1_t public; + + /** + * Assigned IKE_SA. + */ + ike_sa_t *ike_sa; + + /** + * Are we the initiator? + */ + bool initiator; + + /** + * Keymat derivation (from SA) + */ + keymat_v1_t *keymat; + + /** + * Did we process any NAT detection payloads for a source address? + */ + bool src_seen; + + /** + * Did we process any NAT detection payloads for a destination address? + */ + bool dst_seen; + + /** + * Have we found a matching source address NAT hash? + */ + bool src_matched; + + /** + * Have we found a matching destination address NAT hash? + */ + bool dst_matched; +}; + +/** + * Build NAT detection hash for a host. + */ +static chunk_t generate_natd_hash(private_ike_natd_v1_t *this, + ike_sa_id_t *ike_sa_id, host_t *host) +{ + hasher_t *hasher; + chunk_t natd_chunk, natd_hash; + u_int64_t spi_i, spi_r; + u_int16_t port; + + hasher = this->keymat->get_hasher(this->keymat); + if (!hasher) + { + DBG1(DBG_IKE, "no hasher available to build NAT-D payload"); + return chunk_empty; + } + + spi_i = ike_sa_id->get_initiator_spi(ike_sa_id); + spi_r = ike_sa_id->get_responder_spi(ike_sa_id); + port = htons(host->get_port(host)); + + /* natd_hash = HASH(CKY-I | CKY-R | IP | Port) */ + natd_chunk = chunk_cata("cccc", chunk_from_thing(spi_i), + chunk_from_thing(spi_r), host->get_address(host), + chunk_from_thing(port)); + hasher->allocate_hash(hasher, natd_chunk, &natd_hash); + DBG3(DBG_IKE, "natd_chunk %B", &natd_chunk); + DBG3(DBG_IKE, "natd_hash %B", &natd_hash); + + return natd_hash; +} + +/** + * Build a faked NAT-D payload to enforce UDP encapsulation. + */ +static chunk_t generate_natd_hash_faked(private_ike_natd_v1_t *this) +{ + hasher_t *hasher; + chunk_t chunk; + rng_t *rng; + + hasher = this->keymat->get_hasher(this->keymat); + if (!hasher) + { + DBG1(DBG_IKE, "no hasher available to build NAT-D payload"); + return chunk_empty; + } + rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); + if (!rng) + { + DBG1(DBG_IKE, "unable to get random bytes for NAT-D fake"); + return chunk_empty; + } + rng->allocate_bytes(rng, hasher->get_hash_size(hasher), &chunk); + rng->destroy(rng); + return chunk; +} + +/** + * Build a NAT-D payload. + */ +static hash_payload_t *build_natd_payload(private_ike_natd_v1_t *this, bool src, + host_t *host) +{ + hash_payload_t *payload; + ike_cfg_t *config; + chunk_t hash; + + config = this->ike_sa->get_ike_cfg(this->ike_sa); + if (src && config->force_encap(config)) + { + hash = generate_natd_hash_faked(this); + } + else + { + ike_sa_id_t *ike_sa_id = this->ike_sa->get_id(this->ike_sa); + hash = generate_natd_hash(this, ike_sa_id, host); + } + payload = hash_payload_create(NAT_D_V1); + payload->set_hash(payload, hash); + chunk_free(&hash); + return payload; +} + +/** + * Add NAT-D payloads to the message. + */ +static void add_natd_payloads(private_ike_natd_v1_t *this, message_t *message) +{ + hash_payload_t *payload; + host_t *host; + + /* destination has to be added first */ + host = message->get_destination(message); + payload = build_natd_payload(this, FALSE, host); + message->add_payload(message, (payload_t*)payload); + + /* source is added second, compared with IKEv2 we always know the source, + * as these payloads are added in the second Phase 1 exchange or the + * response to the first */ + host = message->get_source(message); + payload = build_natd_payload(this, TRUE, host); + message->add_payload(message, (payload_t*)payload); +} + +/** + * Read NAT-D payloads from message and evaluate them. + */ +static void process_payloads(private_ike_natd_v1_t *this, message_t *message) +{ + enumerator_t *enumerator; + payload_t *payload; + hash_payload_t *hash_payload; + chunk_t hash, src_hash, dst_hash; + ike_sa_id_t *ike_sa_id; + host_t *me, *other; + ike_cfg_t *config; + + /* precompute hashes for incoming NAT-D comparison */ + ike_sa_id = message->get_ike_sa_id(message); + me = message->get_destination(message); + other = message->get_source(message); + dst_hash = generate_natd_hash(this, ike_sa_id, me); + src_hash = generate_natd_hash(this, ike_sa_id, other); + + DBG3(DBG_IKE, "precalculated src_hash %B", &src_hash); + DBG3(DBG_IKE, "precalculated dst_hash %B", &dst_hash); + + enumerator = message->create_payload_enumerator(message); + while (enumerator->enumerate(enumerator, &payload)) + { + if (payload->get_type(payload) != NAT_D_V1) + { + continue; + } + hash_payload = (hash_payload_t*)payload; + if (!this->dst_seen) + { /* the first NAT-D payload contains the destination hash */ + this->dst_seen = TRUE; + hash = hash_payload->get_hash(hash_payload); + DBG3(DBG_IKE, "received dst_hash %B", &hash); + if (chunk_equals(hash, dst_hash)) + { + this->dst_matched = TRUE; + } + continue; + } + /* the other NAT-D payloads contain source hashes */ + this->src_seen = TRUE; + if (!this->src_matched) + { + hash = hash_payload->get_hash(hash_payload); + DBG3(DBG_IKE, "received src_hash %B", &hash); + if (chunk_equals(hash, src_hash)) + { + this->src_matched = TRUE; + } + } + } + enumerator->destroy(enumerator); + + chunk_free(&src_hash); + chunk_free(&dst_hash); + + if (this->src_seen && this->dst_seen) + { + this->ike_sa->set_condition(this->ike_sa, COND_NAT_HERE, + !this->dst_matched); + this->ike_sa->set_condition(this->ike_sa, COND_NAT_THERE, + !this->src_matched); + config = this->ike_sa->get_ike_cfg(this->ike_sa); + if (this->dst_matched && this->src_matched && + config->force_encap(config)) + { + this->ike_sa->set_condition(this->ike_sa, COND_NAT_FAKE, TRUE); + } + } +} + +METHOD(task_t, build_i, status_t, + private_ike_natd_v1_t *this, message_t *message) +{ + status_t result = NEED_MORE; + + switch (message->get_exchange_type(message)) + { + case AGGRESSIVE: + { /* add NAT-D payloads to the second request, already processed + * those by the responder contained in the first response */ + result = SUCCESS; + /* fall */ + } + case ID_PROT: + { /* add NAT-D payloads to the second request, need to process + * those by the responder contained in the second response */ + if (message->get_payload(message, SECURITY_ASSOCIATION_V1)) + { /* wait for the second exchange */ + return NEED_MORE; + } + add_natd_payloads(this, message); + return result; + } + default: + break; + } + return SUCCESS; +} + +METHOD(task_t, process_i, status_t, + private_ike_natd_v1_t *this, message_t *message) +{ + status_t result = NEED_MORE; + + if (!this->ike_sa->supports_extension(this->ike_sa, EXT_NATT)) + { /* we didn't receive VIDs inidcating support for NAT-T */ + return SUCCESS; + } + + switch (message->get_exchange_type(message)) + { + case ID_PROT: + { /* process NAT-D payloads in the second response, added them in the + * second request already, so we're done afterwards */ + if (message->get_payload(message, SECURITY_ASSOCIATION_V1)) + { /* wait for the second exchange */ + return NEED_MORE; + } + result = SUCCESS; + /* fall */ + } + case AGGRESSIVE: + { /* process NAT-D payloads in the first response, add them in the + * following second request */ + process_payloads(this, message); + + if (this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY)) + { + this->ike_sa->float_ports(this->ike_sa); + } + return result; + } + default: + break; + } + return SUCCESS; +} + +METHOD(task_t, process_r, status_t, + private_ike_natd_v1_t *this, message_t *message) +{ + status_t result = NEED_MORE; + + if (!this->ike_sa->supports_extension(this->ike_sa, EXT_NATT)) + { /* we didn't receive VIDs indicating NAT-T support */ + return SUCCESS; + } + + switch (message->get_exchange_type(message)) + { + case AGGRESSIVE: + { /* proccess NAT-D payloads in the second request, already added ours + * in the first response */ + result = SUCCESS; + /* fall */ + } + case ID_PROT: + { /* process NAT-D payloads in the second request, need to add ours + * to the second response */ + if (message->get_payload(message, SECURITY_ASSOCIATION_V1)) + { /* wait for the second exchange */ + return NEED_MORE; + } + process_payloads(this, message); + return result; + } + default: + break; + } + return SUCCESS; +} + +METHOD(task_t, build_r, status_t, + private_ike_natd_v1_t *this, message_t *message) +{ + switch (message->get_exchange_type(message)) + { + case ID_PROT: + { /* add NAT-D payloads to second response, already processed those + * contained in the second request */ + if (message->get_payload(message, SECURITY_ASSOCIATION_V1)) + { /* wait for the second exchange */ + return NEED_MORE; + } + add_natd_payloads(this, message); + return SUCCESS; + } + case AGGRESSIVE: + { /* add NAT-D payloads to the first response, process those contained + * in the following second request */ + add_natd_payloads(this, message); + return NEED_MORE; + } + default: + break; + } + return SUCCESS; +} + +METHOD(task_t, get_type, task_type_t, + private_ike_natd_v1_t *this) +{ + return TASK_IKE_NATD_V1; +} + +METHOD(task_t, migrate, void, + private_ike_natd_v1_t *this, ike_sa_t *ike_sa) +{ + this->ike_sa = ike_sa; + this->src_seen = FALSE; + this->dst_seen = FALSE; + this->src_matched = FALSE; + this->dst_matched = FALSE; +} + +METHOD(task_t, destroy, void, + private_ike_natd_v1_t *this) +{ + free(this); +} + +/* + * Described in header. + */ +ike_natd_v1_t *ike_natd_v1_create(ike_sa_t *ike_sa, bool initiator) +{ + private_ike_natd_v1_t *this; + + INIT(this, + .public = { + .task = { + .get_type = _get_type, + .migrate = _migrate, + .destroy = _destroy, + }, + }, + .ike_sa = ike_sa, + .keymat = (keymat_v1_t*)ike_sa->get_keymat(ike_sa), + .initiator = initiator, + ); + + if (initiator) + { + this->public.task.build = _build_i; + this->public.task.process = _process_i; + } + else + { + this->public.task.build = _build_r; + this->public.task.process = _process_r; + } + + return &this->public; +} diff --git a/src/libcharon/sa/tasks/ike_natd_v1.h b/src/libcharon/sa/tasks/ike_natd_v1.h new file mode 100644 index 000000000..a59054fb0 --- /dev/null +++ b/src/libcharon/sa/tasks/ike_natd_v1.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2011 Tobias Brunner + * 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 ike_natd_v1 ike_natd_v1 + * @{ @ingroup tasks + */ + +#ifndef IKE_NATD_V1_H_ +#define IKE_NATD_V1_H_ + +typedef struct ike_natd_v1_t ike_natd_v1_t; + +#include <library.h> +#include <sa/ike_sa.h> +#include <sa/tasks/task.h> + +/** + * Task of type ike_natd_v1, detects NAT situation in IKEv1 Phase 1. + */ +struct ike_natd_v1_t { + + /** + * Implements the task_t interface + */ + task_t task; +}; + +/** + * Create a new ike_natd_v1 task. + * + * @param ike_sa IKE_SA this task works for + * @param initiator TRUE if task is the original initiator + * @return ike_natd_v1 task to handle by the task_manager + */ +ike_natd_v1_t *ike_natd_v1_create(ike_sa_t *ike_sa, bool initiator); + +#endif /** IKE_NATD_V1_H_ @}*/ diff --git a/src/libcharon/sa/tasks/task.c b/src/libcharon/sa/tasks/task.c index d0eed77e3..2f12ed574 100644 --- a/src/libcharon/sa/tasks/task.c +++ b/src/libcharon/sa/tasks/task.c @@ -38,6 +38,7 @@ ENUM(task_type_names, TASK_IKE_INIT, TASK_XAUTH_REQUEST, "MAIN_MODE", "QUICK_MODE", "VENDOR_V1", + "IKE_NATD_V1", "XAUTH_REQUEST", ); #else @@ -61,6 +62,7 @@ ENUM(task_type_names, TASK_IKE_INIT, TASK_XAUTH_REQUEST, "MAIN_MODE", "QUICK_MODE", "VENDOR_V1", + "IKE_NATD_V1", "XAUTH_REQUEST", ); #endif /* ME */ diff --git a/src/libcharon/sa/tasks/task.h b/src/libcharon/sa/tasks/task.h index e76ffcd8c..e4edb78e0 100644 --- a/src/libcharon/sa/tasks/task.h +++ b/src/libcharon/sa/tasks/task.h @@ -75,6 +75,8 @@ enum task_type_t { TASK_QUICK_MODE, /** IKEv1 vendor ID payload handling */ TASK_VENDOR_V1, + /** IKEv1 NAT detection */ + TASK_IKE_NATD_V1, /** Request the user/pass with XAUTH */ TASK_XAUTH_REQUEST, }; @@ -122,7 +124,7 @@ struct task_t { * * @param message message to read payloads from * @return - * - FAILED if a critical error occurred + * - FAILED if a critical error occurred * - DESTROY_ME if IKE_SA has been properly deleted * - NEED_MORE if another call to build/process needed * - SUCCESS if task completed |