From dbb3f7f9215730f753e6448d1ba4ef55d3c45dd0 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 20 Apr 2015 14:05:16 +0200 Subject: [PATCH 01/36] redirect-provider: Add interface to redirect clients during initial messages This will allow e.g. plugins to decide whether a connecting client is redirected to a different gateway using RFC 5685. --- src/libcharon/Android.mk | 1 + src/libcharon/Makefile.am | 1 + src/libcharon/sa/redirect_provider.h | 59 ++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 src/libcharon/sa/redirect_provider.h diff --git a/src/libcharon/Android.mk b/src/libcharon/Android.mk index 844bbfd8e..ca9466692 100644 --- a/src/libcharon/Android.mk +++ b/src/libcharon/Android.mk @@ -84,6 +84,7 @@ sa/child_sa_manager.c sa/child_sa_manager.h \ sa/task_manager.h sa/task_manager.c \ sa/shunt_manager.c sa/shunt_manager.h \ sa/trap_manager.c sa/trap_manager.h \ +sa/redirect_provider.h \ sa/task.c sa/task.h libcharon_la_SOURCES += \ diff --git a/src/libcharon/Makefile.am b/src/libcharon/Makefile.am index 4de8faab9..2edf92a7a 100644 --- a/src/libcharon/Makefile.am +++ b/src/libcharon/Makefile.am @@ -83,6 +83,7 @@ sa/child_sa_manager.c sa/child_sa_manager.h \ sa/task_manager.h sa/task_manager.c \ sa/shunt_manager.c sa/shunt_manager.h \ sa/trap_manager.c sa/trap_manager.h \ +sa/redirect_provider.h \ sa/task.c sa/task.h if USE_IKEV2 diff --git a/src/libcharon/sa/redirect_provider.h b/src/libcharon/sa/redirect_provider.h new file mode 100644 index 000000000..ef2288ffc --- /dev/null +++ b/src/libcharon/sa/redirect_provider.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2015 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 . + * + * 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 redirect_provider redirect_provider + * @{ @ingroup sa + */ + +#ifndef REDIRECT_PROVIDER_H_ +#define REDIRECT_PROVIDER_H_ + +typedef struct redirect_provider_t redirect_provider_t; + +#include +#include + +/** + * Interface that allows implementations to decide whether a client is + * redirected during IKE_SA_INIT or IKE_AUTH using RFC 5685. + */ +struct redirect_provider_t { + + /** + * Decide whether a client is redirect directly upon receipt of the + * IKE_SA_INIT message. + * + * @param ike_sa IKE_SA for which this is called + * @param gateway[out] new IKE gateway (IP or FQDN) + * @return TRUE if client should be redirected, FALSE otherwise + */ + bool (*redirect_on_init)(redirect_provider_t *this, ike_sa_t *ike_sa, + identification_t **gateway); + + /** + * Decide whether a client is redirect after the IKE_AUTH has been + * handled. This is called after the client is authenticated and when the + * server authenticates itself. + * + * @param ike_sa IKE_SA for which this is called + * @param gateway[out] new IKE gateway (IP or FQDN) + * @return TRUE if client should be redirected, FALSE otherwise + */ + bool (*redirect_on_auth)(redirect_provider_t *this, ike_sa_t *ike_sa, + identification_t **gateway); +}; + +#endif /** REDIRECT_PROVIDER_H_ @}*/ From 4a6e054122fb2c7047e1186e72e14d8e40586d9e Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 20 Apr 2015 14:38:17 +0200 Subject: [PATCH 02/36] redirect-manager: Add manager for redirect providers --- src/libcharon/Android.mk | 2 +- src/libcharon/Makefile.am | 2 +- src/libcharon/sa/redirect_manager.c | 131 ++++++++++++++++++++++++++++ src/libcharon/sa/redirect_manager.h | 90 +++++++++++++++++++ 4 files changed, 223 insertions(+), 2 deletions(-) create mode 100644 src/libcharon/sa/redirect_manager.c create mode 100644 src/libcharon/sa/redirect_manager.h diff --git a/src/libcharon/Android.mk b/src/libcharon/Android.mk index ca9466692..6fe682e49 100644 --- a/src/libcharon/Android.mk +++ b/src/libcharon/Android.mk @@ -84,7 +84,7 @@ sa/child_sa_manager.c sa/child_sa_manager.h \ sa/task_manager.h sa/task_manager.c \ sa/shunt_manager.c sa/shunt_manager.h \ sa/trap_manager.c sa/trap_manager.h \ -sa/redirect_provider.h \ +sa/redirect_provider.h sa/redirect_manager.c sa/redirect_manager.h \ sa/task.c sa/task.h libcharon_la_SOURCES += \ diff --git a/src/libcharon/Makefile.am b/src/libcharon/Makefile.am index 2edf92a7a..34587677f 100644 --- a/src/libcharon/Makefile.am +++ b/src/libcharon/Makefile.am @@ -83,7 +83,7 @@ sa/child_sa_manager.c sa/child_sa_manager.h \ sa/task_manager.h sa/task_manager.c \ sa/shunt_manager.c sa/shunt_manager.h \ sa/trap_manager.c sa/trap_manager.h \ -sa/redirect_provider.h \ +sa/redirect_provider.h sa/redirect_manager.c sa/redirect_manager.h \ sa/task.c sa/task.h if USE_IKEV2 diff --git a/src/libcharon/sa/redirect_manager.c b/src/libcharon/sa/redirect_manager.c new file mode 100644 index 000000000..dfc71e106 --- /dev/null +++ b/src/libcharon/sa/redirect_manager.c @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2015 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 . + * + * 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 "redirect_manager.h" + +#include +#include + +typedef struct private_redirect_manager_t private_redirect_manager_t; + +/** + * Private data + */ +struct private_redirect_manager_t { + + /** + * Public interface + */ + redirect_manager_t public; + + /** + * Registered providers + */ + linked_list_t *providers; + + /** + * Lock to access list of providers + */ + rwlock_t *lock; +}; + +METHOD(redirect_manager_t, add_provider, void, + private_redirect_manager_t *this, redirect_provider_t *provider) +{ + this->lock->write_lock(this->lock); + this->providers->insert_last(this->providers, provider); + this->lock->unlock(this->lock); +} + +METHOD(redirect_manager_t, remove_provider, void, + private_redirect_manager_t *this, redirect_provider_t *provider) +{ + this->lock->write_lock(this->lock); + this->providers->remove(this->providers, provider, NULL); + this->lock->unlock(this->lock); +} + +/** + * Determine whether a client should be redirected using the callback with the + * given offset into the redirect_provider_t interface. + */ +static bool should_redirect(private_redirect_manager_t *this, ike_sa_t *ike_sa, + identification_t **gateway, size_t offset) +{ + enumerator_t *enumerator; + void *provider; + bool redirect = FALSE; + + this->lock->read_lock(this->lock); + enumerator = this->providers->create_enumerator(this->providers); + while (enumerator->enumerate(enumerator, &provider)) + { + bool (**method)(void*,ike_sa_t*,identification_t**) = provider + offset; + if (*method && (*method)(provider, ike_sa, gateway)) + { + redirect = TRUE; + break; + } + } + enumerator->destroy(enumerator); + this->lock->unlock(this->lock); + return redirect; +} + +METHOD(redirect_manager_t, redirect_on_init, bool, + private_redirect_manager_t *this, ike_sa_t *ike_sa, + identification_t **gateway) +{ + return should_redirect(this, ike_sa, gateway, + offsetof(redirect_provider_t, redirect_on_init)); +} + +METHOD(redirect_manager_t, redirect_on_auth, bool, + private_redirect_manager_t *this, ike_sa_t *ike_sa, + identification_t **gateway) +{ + return should_redirect(this, ike_sa, gateway, + offsetof(redirect_provider_t, redirect_on_auth)); +} + +METHOD(redirect_manager_t, destroy, void, + private_redirect_manager_t *this) +{ + this->providers->destroy(this->providers); + this->lock->destroy(this->lock); + free(this); +} + +/* + * Described in header + */ +redirect_manager_t *redirect_manager_create() +{ + private_redirect_manager_t *this; + + INIT(this, + .public = { + .add_provider = _add_provider, + .remove_provider = _remove_provider, + .redirect_on_init = _redirect_on_init, + .redirect_on_auth = _redirect_on_auth, + .destroy = _destroy, + }, + .providers = linked_list_create(), + .lock = rwlock_create(RWLOCK_TYPE_DEFAULT), + ); + + return &this->public; +} diff --git a/src/libcharon/sa/redirect_manager.h b/src/libcharon/sa/redirect_manager.h new file mode 100644 index 000000000..a74cfa3b4 --- /dev/null +++ b/src/libcharon/sa/redirect_manager.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2015 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 . + * + * 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 redirect_manager redirect_manager + * @{ @ingroup sa + */ + +#ifndef REDIRECT_MANAGER_H_ +#define REDIRECT_MANAGER_H_ + +typedef struct redirect_manager_t redirect_manager_t; + +#include + +/** + * Manages redirect providers. + */ +struct redirect_manager_t { + + /** + * Add a redirect provider. + * + * All registered providers are queried until one of them decides to + * redirect a client. + * + * A provider may be called concurrently for different IKE_SAs. + * + * @param provider provider to register + */ + void (*add_provider)(redirect_manager_t *this, + redirect_provider_t *provider); + + /** + * Remove a redirect provider. + * + * @param provider provider to unregister + */ + void (*remove_provider)(redirect_manager_t *this, + redirect_provider_t *provider); + + /** + * Determine whether a client should be redirected upon receipt of the + * IKE_SA_INIT message. + * + * @param ike_sa IKE_SA for which this is called + * @param gateway[out] new IKE gateway (IP or FQDN) + * @return TRUE if client should be redirected, FALSE otherwise + */ + bool (*redirect_on_init)(redirect_manager_t *this, ike_sa_t *ike_sa, + identification_t **gateway); + + /** + * Determine whether a client should be redirected after the IKE_AUTH has + * been handled. Should be called after the client is authenticated and + * when the server authenticates itself. + * + * @param ike_sa IKE_SA for which this is called + * @param gateway[out] new IKE gateway (IP or FQDN) + * @return TRUE if client should be redirected, FALSE otherwise + */ + bool (*redirect_on_auth)(redirect_manager_t *this, ike_sa_t *ike_sa, + identification_t **gateway); + + /** + * Destroy this instance. + */ + void (*destroy)(redirect_manager_t *this); +}; + +/** + * Create a redirect manager instance. + * + * @return manager instance + */ +redirect_manager_t *redirect_manager_create(); + +#endif /** REDIRECT_MANAGER_H_ @}*/ From 32ba44424d65227039d236775eb21268a31371b9 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 20 Apr 2015 14:41:09 +0200 Subject: [PATCH 03/36] daemon: Create global redirect manager instance --- src/libcharon/daemon.c | 2 ++ src/libcharon/daemon.h | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/src/libcharon/daemon.c b/src/libcharon/daemon.c index 799c3f6dc..cef8b8992 100644 --- a/src/libcharon/daemon.c +++ b/src/libcharon/daemon.c @@ -680,6 +680,7 @@ static void destroy(private_daemon_t *this) DESTROY_IF(this->kernel_handler); DESTROY_IF(this->public.traps); DESTROY_IF(this->public.shunts); + DESTROY_IF(this->public.redirect); DESTROY_IF(this->public.controller); DESTROY_IF(this->public.eap); DESTROY_IF(this->public.xauth); @@ -872,6 +873,7 @@ private_daemon_t *daemon_create() this->public.socket = socket_manager_create(); this->public.traps = trap_manager_create(); this->public.shunts = shunt_manager_create(); + this->public.redirect = redirect_manager_create(); this->kernel_handler = kernel_handler_create(); return this; diff --git a/src/libcharon/daemon.h b/src/libcharon/daemon.h index 654e22a07..48b9c7ec3 100644 --- a/src/libcharon/daemon.h +++ b/src/libcharon/daemon.h @@ -190,6 +190,7 @@ typedef struct daemon_t daemon_t; #include #include #include +#include #include #include #include @@ -264,6 +265,11 @@ struct daemon_t { */ shunt_manager_t *shunts; + /** + * Manager for IKE redirect providers + */ + redirect_manager_t *redirect; + /** * Manager for the different configuration backends. */ From c6aa749c28d9e73657befecb5f9d2017edcc6976 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 20 Apr 2015 14:56:21 +0200 Subject: [PATCH 04/36] ike-sa: Add new extension for IKEv2 redirection (RFC 5685) --- src/libcharon/sa/ike_sa.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h index e15ac2e38..3cefb4d99 100644 --- a/src/libcharon/sa/ike_sa.h +++ b/src/libcharon/sa/ike_sa.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2014 Tobias Brunner + * Copyright (C) 2006-2015 Tobias Brunner * Copyright (C) 2006 Daniel Roethlisberger * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2005 Jan Hutter @@ -136,6 +136,11 @@ enum ike_extension_t { * Signature Authentication, RFC 7427 */ EXT_SIGNATURE_AUTH = (1<<12), + + /** + * IKEv2 Redirect Mechanism, RFC 5685 + */ + EXT_IKE_REDIRECTION = (1<<13), }; /** From 099c0b12b60223416433d96d63017815d1983317 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 20 Apr 2015 14:59:08 +0200 Subject: [PATCH 05/36] ike-init: Enable redirection extension if client sends REDIRECT_SUPPORTED notify --- src/libcharon/sa/ikev2/tasks/ike_init.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libcharon/sa/ikev2/tasks/ike_init.c b/src/libcharon/sa/ikev2/tasks/ike_init.c index 1ff643d62..8c628646e 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_init.c +++ b/src/libcharon/sa/ikev2/tasks/ike_init.c @@ -391,6 +391,10 @@ static void process_payloads(private_ike_init_t *this, message_t *message) handle_supported_hash_algorithms(this, notify); } break; + case REDIRECT_SUPPORTED: + this->ike_sa->enable_extension(this->ike_sa, + EXT_IKE_REDIRECTION); + break; default: /* other notifies are handled elsewhere */ break; From 10009b29541eaae1cf0898c2e372c90d923be0fe Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 20 Apr 2015 15:16:10 +0200 Subject: [PATCH 06/36] ike-init: Send REDIRECT_SUPPORTED as initiator --- src/libcharon/sa/ikev2/tasks/ike_init.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libcharon/sa/ikev2/tasks/ike_init.c b/src/libcharon/sa/ikev2/tasks/ike_init.c index 8c628646e..0f9c45a7f 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_init.c +++ b/src/libcharon/sa/ikev2/tasks/ike_init.c @@ -324,6 +324,11 @@ static bool build_payloads(private_ike_init_t *this, message_t *message) send_supported_hash_algorithms(this, message); } } + /* notify other peer if we support redirection */ + if (!this->old_sa && this->initiator) + { + message->add_notify(message, FALSE, REDIRECT_SUPPORTED, chunk_empty); + } return TRUE; } From fa5cfbdcbfdda3417a6d963c7d69c41746240dc4 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 22 Apr 2015 14:19:54 +0200 Subject: [PATCH 07/36] redirect-manager: Verify type of returned gateway ID --- src/libcharon/sa/redirect_manager.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/libcharon/sa/redirect_manager.c b/src/libcharon/sa/redirect_manager.c index dfc71e106..e37c26f0d 100644 --- a/src/libcharon/sa/redirect_manager.c +++ b/src/libcharon/sa/redirect_manager.c @@ -75,7 +75,18 @@ static bool should_redirect(private_redirect_manager_t *this, ike_sa_t *ike_sa, bool (**method)(void*,ike_sa_t*,identification_t**) = provider + offset; if (*method && (*method)(provider, ike_sa, gateway)) { - redirect = TRUE; + switch (*gateway ? (*gateway)->get_type(*gateway) : 0) + { + case ID_IPV4_ADDR: + case ID_IPV6_ADDR: + case ID_FQDN: + redirect = TRUE; + break; + default: + DBG1(DBG_CFG, "redirect provider returned invalid gateway"); + DESTROY_IF(*gateway); + continue; + } break; } } From 2beb26b9489b0d94d2c1be3022099aa8bd02735c Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 22 Apr 2015 14:34:53 +0200 Subject: [PATCH 08/36] redirect-manager: Add helper function to create and parse REDIRECT notify data The same encoding is also used for the REDIRECT_FROM notifies. --- src/libcharon/sa/redirect_manager.c | 154 ++++++++++++++++++++++++++-- src/libcharon/sa/redirect_manager.h | 19 ++++ 2 files changed, 162 insertions(+), 11 deletions(-) diff --git a/src/libcharon/sa/redirect_manager.c b/src/libcharon/sa/redirect_manager.c index e37c26f0d..ff92ac29f 100644 --- a/src/libcharon/sa/redirect_manager.c +++ b/src/libcharon/sa/redirect_manager.c @@ -17,6 +17,8 @@ #include #include +#include +#include typedef struct private_redirect_manager_t private_redirect_manager_t; @@ -41,6 +43,57 @@ struct private_redirect_manager_t { rwlock_t *lock; }; + +/** + * Gateway identify types + * + * The encoding is the same as that for corresponding ID payloads. + */ +typedef enum { + /** IPv4 address of the VPN gateway */ + GATEWAY_ID_TYPE_IPV4 = 1, + /** IPv6 address of the VPN gateway */ + GATEWAY_ID_TYPE_IPV6 = 2, + /** FQDN of the VPN gateway */ + GATEWAY_ID_TYPE_FQDN = 3, +} gateway_id_type_t; + +/** + * Mapping of gateway identity types to identity types + */ +static id_type_t gateway_to_id_type(gateway_id_type_t type) +{ + switch (type) + { + case GATEWAY_ID_TYPE_IPV4: + return ID_IPV4_ADDR; + case GATEWAY_ID_TYPE_IPV6: + return ID_IPV6_ADDR; + case GATEWAY_ID_TYPE_FQDN: + return ID_FQDN; + default: + return 0; + } +} + +/** + * Mapping of identity types to gateway identity types + */ +static gateway_id_type_t id_type_to_gateway(id_type_t type) +{ + switch (type) + { + case ID_IPV4_ADDR: + return GATEWAY_ID_TYPE_IPV4; + case ID_IPV6_ADDR: + return GATEWAY_ID_TYPE_IPV6; + case ID_FQDN: + return GATEWAY_ID_TYPE_FQDN; + default: + return 0; + } +} + METHOD(redirect_manager_t, add_provider, void, private_redirect_manager_t *this, redirect_provider_t *provider) { @@ -75,19 +128,16 @@ static bool should_redirect(private_redirect_manager_t *this, ike_sa_t *ike_sa, bool (**method)(void*,ike_sa_t*,identification_t**) = provider + offset; if (*method && (*method)(provider, ike_sa, gateway)) { - switch (*gateway ? (*gateway)->get_type(*gateway) : 0) + if (*gateway && id_type_to_gateway((*gateway)->get_type(*gateway))) { - case ID_IPV4_ADDR: - case ID_IPV6_ADDR: - case ID_FQDN: - redirect = TRUE; - break; - default: - DBG1(DBG_CFG, "redirect provider returned invalid gateway"); - DESTROY_IF(*gateway); - continue; + redirect = TRUE; + break; + } + else + { + DBG1(DBG_CFG, "redirect provider returned invalid gateway ID"); + DESTROY_IF(*gateway); } - break; } } enumerator->destroy(enumerator); @@ -140,3 +190,85 @@ redirect_manager_t *redirect_manager_create() return &this->public; } + +/* + * Encoding of a REDIRECT or REDIRECTED_FROM notify + * + 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Next Payload |C| RESERVED | Payload Length | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |Protocol ID(=0)| SPI Size (=0) | Notify Message Type | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | GW Ident Type | GW Ident Len | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ~ + ~ New Responder GW Identity ~ + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | + ~ Nonce Data ~ + | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +*/ + +/* + * Described in header + */ +chunk_t redirect_data_create(identification_t *gw, chunk_t nonce) +{ + gateway_id_type_t type; + bio_writer_t *writer; + chunk_t data; + + type = id_type_to_gateway(gw->get_type(gw)); + if (!type) + { + return chunk_empty; + } + + writer = bio_writer_create(0); + writer->write_uint8(writer, type); + writer->write_data8(writer, gw->get_encoding(gw)); + if (nonce.ptr) + { + writer->write_data(writer, nonce); + } + + data = writer->extract_buf(writer); + writer->destroy(writer); + return data; +} + +/* + * Described in header + */ +identification_t *redirect_data_parse(chunk_t data, chunk_t *nonce) +{ + bio_reader_t *reader; + id_type_t id_type; + chunk_t gateway; + u_int8_t type; + + reader = bio_reader_create(data); + if (!reader->read_uint8(reader, &type) || + !reader->read_data8(reader, &gateway)) + { + DBG1(DBG_ENC, "invalid REDIRECT notify data"); + reader->destroy(reader); + return NULL; + } + id_type = gateway_to_id_type(type); + if (!id_type) + { + DBG1(DBG_ENC, "invalid gateway ID type (%d) in REDIRECT notify", type); + reader->destroy(reader); + return NULL; + } + if (nonce) + { + *nonce = chunk_clone(reader->peek(reader)); + } + reader->destroy(reader); + return identification_create_from_encoding(id_type, gateway); +} diff --git a/src/libcharon/sa/redirect_manager.h b/src/libcharon/sa/redirect_manager.h index a74cfa3b4..45a727c29 100644 --- a/src/libcharon/sa/redirect_manager.h +++ b/src/libcharon/sa/redirect_manager.h @@ -87,4 +87,23 @@ struct redirect_manager_t { */ redirect_manager_t *redirect_manager_create(); +/** + * Create notification data of a REDIRECT or REDIRECT_FROM payload using the + * given gateway identity and optional nonce (only used during IKE_SA_INIT). + * + * @param gw gateway identity (IP or FQDN), gets cloned + * @param nonce nonce value, or chunk_empty, gets cloned + * @return notify data, chunk_empty if ID type is not supported + */ +chunk_t redirect_data_create(identification_t *gw, chunk_t nonce); + +/** + * Parse notification data of a REDIRECT or REDIRECTED_FROM notify payload. + * + * @param data notification data to parse + * @param nonce[out] nonce data (allocated), if any was provided + * @return gateway identity, NULL if data is invalid + */ +identification_t *redirect_data_parse(chunk_t data, chunk_t *nonce); + #endif /** REDIRECT_MANAGER_H_ @}*/ From dd2b335b795b96c73561a44f0dfb106ca9189971 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Wed, 22 Apr 2015 17:02:23 +0200 Subject: [PATCH 09/36] ike-init: Send REDIRECT notify during IKE_SA_INIT if requested by providers --- src/libcharon/sa/ikev2/tasks/ike_init.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/libcharon/sa/ikev2/tasks/ike_init.c b/src/libcharon/sa/ikev2/tasks/ike_init.c index 0f9c45a7f..87761ad59 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_init.c +++ b/src/libcharon/sa/ikev2/tasks/ike_init.c @@ -559,6 +559,8 @@ static bool derive_keys(private_ike_init_t *this, METHOD(task_t, build_r, status_t, private_ike_init_t *this, message_t *message) { + identification_t *gateway; + /* check if we have everything we need */ if (this->proposal == NULL || this->other_nonce.len == 0 || this->my_nonce.len == 0) @@ -569,6 +571,21 @@ METHOD(task_t, build_r, status_t, } this->ike_sa->set_proposal(this->ike_sa, this->proposal); + /* check if we'd have to redirect the client */ + if (this->ike_sa->supports_extension(this->ike_sa, EXT_IKE_REDIRECTION) && + charon->redirect->redirect_on_init(charon->redirect, this->ike_sa, + &gateway)) + { + chunk_t data; + + DBG1(DBG_IKE, "redirecting peer to %Y", gateway); + data = redirect_data_create(gateway, this->other_nonce); + message->add_notify(message, TRUE, REDIRECT, data); + gateway->destroy(gateway); + chunk_free(&data); + return FAILED; + } + if (this->dh == NULL || !this->proposal->has_dh_group(this->proposal, this->dh_group)) { From c126ddd048ab41d8d71f9284c4ac5c75c53f9779 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 23 Apr 2015 11:50:31 +0200 Subject: [PATCH 10/36] ikev2: Handle REDIRECT notifies during IKE_SA_INIT --- src/libcharon/sa/ike_sa.c | 32 +++++++++++++++++++++++++ src/libcharon/sa/ike_sa.h | 10 ++++++++ src/libcharon/sa/ikev2/tasks/ike_init.c | 22 +++++++++++++++++ 3 files changed, 64 insertions(+) diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index 48a4b274a..864e8c055 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -1951,6 +1951,37 @@ METHOD(ike_sa_t, reestablish, status_t, return status; } +METHOD(ike_sa_t, handle_redirect, bool, + private_ike_sa_t *this, identification_t *gateway) +{ + char gw[BUF_LEN]; + host_t *other; + + DBG1(DBG_IKE, "redirected to %Y", gateway); + + snprintf(gw, sizeof(gw), "%Y", gateway); + gw[sizeof(gw)-1] = '\0'; + other = host_create_from_dns(gw, AF_UNSPEC, IKEV2_UDP_PORT); + if (!other) + { + DBG1(DBG_IKE, "unable to resolve gateway ID '%Y', redirect failed", + gateway); + return FALSE; + } + switch (this->state) + { + case IKE_CONNECTING: + reset(this); + set_other_host(this, other); + return TRUE; + default: + DBG1(DBG_IKE, "unable to handle redirect for IKE_SA in state %N", + ike_sa_state_names, this->state); + other->destroy(other); + return FALSE; + } +} + METHOD(ike_sa_t, retransmit, status_t, private_ike_sa_t *this, u_int32_t message_id) { @@ -2543,6 +2574,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator, .destroy = _destroy, .send_dpd = _send_dpd, .send_keepalive = _send_keepalive, + .handle_redirect = _handle_redirect, .get_keymat = _get_keymat, .add_child_sa = _add_child_sa, .get_child_sa = _get_child_sa, diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h index 3cefb4d99..384912d00 100644 --- a/src/libcharon/sa/ike_sa.h +++ b/src/libcharon/sa/ike_sa.h @@ -847,6 +847,16 @@ struct ike_sa_t { */ void (*send_keepalive) (ike_sa_t *this, bool scheduled); + /** + * Handle a redirect request. + * + * The behavior is different depending on the state of the IKE_SA. + * + * @param gateway gateway ID (IP or FQDN) of the target + * @return FALSE if redirect not possible, TRUE otherwise + */ + bool (*handle_redirect)(ike_sa_t *this, identification_t *gateway); + /** * Get the keying material of this IKE_SA. * diff --git a/src/libcharon/sa/ikev2/tasks/ike_init.c b/src/libcharon/sa/ikev2/tasks/ike_init.c index 87761ad59..71bd82cf1 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_init.c +++ b/src/libcharon/sa/ikev2/tasks/ike_init.c @@ -704,6 +704,28 @@ METHOD(task_t, process_i, status_t, this->retry++; return NEED_MORE; } + case REDIRECT: + { + identification_t *gateway; + chunk_t data, nonce = chunk_empty; + status_t status = FAILED; + + data = notify->get_notification_data(notify); + gateway = redirect_data_parse(data, &nonce); + enumerator->destroy(enumerator); + if (!gateway || !chunk_equals(nonce, this->my_nonce)) + { + DBG1(DBG_IKE, "received invalid REDIRECT notify"); + } + else if (this->ike_sa->handle_redirect(this->ike_sa, + gateway)) + { + status = NEED_MORE; + } + DESTROY_IF(gateway); + chunk_free(&nonce); + return status; + } default: { if (type <= 16383) From 489d154e63c14cb79f3e6a757ab4ea9256c4aab8 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 20 Apr 2015 17:36:45 +0200 Subject: [PATCH 11/36] ikev2: Add option to disable following redirects as client --- conf/options/charon.opt | 3 +++ src/libcharon/sa/ike_sa.c | 12 ++++++++++++ src/libcharon/sa/ikev2/tasks/ike_init.c | 9 ++++++++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/conf/options/charon.opt b/conf/options/charon.opt index 382003644..86279ec83 100644 --- a/conf/options/charon.opt +++ b/conf/options/charon.opt @@ -97,6 +97,9 @@ charon.flush_auth_cfg = no this might conflict with plugins that later need access to e.g. the used certificates. +charon.follow_redirects = yes + Whether to follow IKEv2 redirects (RFC 5685). + charon.fragment_size = 0 Maximum size (complete IP datagram size in bytes) of a sent IKE fragment when using proprietary IKEv1 or standardized IKEv2 fragmentation (0 for diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index 864e8c055..cb941afb0 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -282,6 +282,11 @@ struct private_ike_sa_t { * Maximum length of a single fragment, 0 for address-specific defaults */ size_t fragment_size; + + /** + * Whether to follow IKEv2 redirects + */ + bool follow_redirects; }; /** @@ -1958,6 +1963,11 @@ METHOD(ike_sa_t, handle_redirect, bool, host_t *other; DBG1(DBG_IKE, "redirected to %Y", gateway); + if (!this->follow_redirects) + { + DBG1(DBG_IKE, "server sent REDIRECT even though we disabled it"); + return FALSE; + } snprintf(gw, sizeof(gw), "%Y", gateway); gw[sizeof(gw)-1] = '\0'; @@ -2640,6 +2650,8 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator, "%s.flush_auth_cfg", FALSE, lib->ns), .fragment_size = lib->settings->get_int(lib->settings, "%s.fragment_size", 0, lib->ns), + .follow_redirects = lib->settings->get_bool(lib->settings, + "%s.follow_redirects", TRUE, lib->ns), ); if (version == IKEV2) diff --git a/src/libcharon/sa/ikev2/tasks/ike_init.c b/src/libcharon/sa/ikev2/tasks/ike_init.c index 71bd82cf1..42be11896 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_init.c +++ b/src/libcharon/sa/ikev2/tasks/ike_init.c @@ -118,6 +118,11 @@ struct private_ike_init_t { * Whether to use Signature Authentication as per RFC 7427 */ bool signature_authentication; + + /** + * Whether to follow IKEv2 redirects as per RFC 5685 + */ + bool follow_redirects; }; /** @@ -325,7 +330,7 @@ static bool build_payloads(private_ike_init_t *this, message_t *message) } } /* notify other peer if we support redirection */ - if (!this->old_sa && this->initiator) + if (!this->old_sa && this->initiator && this->follow_redirects) { message->add_notify(message, FALSE, REDIRECT_SUPPORTED, chunk_empty); } @@ -850,6 +855,8 @@ ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa) .old_sa = old_sa, .signature_authentication = lib->settings->get_bool(lib->settings, "%s.signature_authentication", TRUE, lib->ns), + .follow_redirects = lib->settings->get_bool(lib->settings, + "%s.follow_redirects", TRUE, lib->ns), ); this->nonceg = this->keymat->keymat.create_nonce_gen(&this->keymat->keymat); From e4af6e6b7ad4e62a761dc500cb4eca48ede5d790 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 23 Apr 2015 12:16:21 +0200 Subject: [PATCH 12/36] ike-sa: Keep track of the address of the gateway that redirected us --- src/libcharon/sa/ike_sa.c | 21 ++++++++++++++++++++- src/libcharon/sa/ike_sa.h | 7 +++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index cb941afb0..79960fcb2 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -287,6 +287,11 @@ struct private_ike_sa_t { * Whether to follow IKEv2 redirects */ bool follow_redirects; + + /** + * Original gateway address from which we got redirected + */ + host_t *redirected_from; }; /** @@ -391,6 +396,12 @@ METHOD(ike_sa_t, set_other_host, void, this->other_host = other; } +METHOD(ike_sa_t, get_redirected_from, host_t*, + private_ike_sa_t *this) +{ + return this->redirected_from; +} + METHOD(ike_sa_t, get_peer_cfg, peer_cfg_t*, private_ike_sa_t *this) { @@ -748,6 +759,8 @@ METHOD(ike_sa_t, set_state, void, { keepalives = TRUE; } + DESTROY_IF(this->redirected_from); + this->redirected_from = NULL; } break; } @@ -1960,7 +1973,7 @@ METHOD(ike_sa_t, handle_redirect, bool, private_ike_sa_t *this, identification_t *gateway) { char gw[BUF_LEN]; - host_t *other; + host_t *other, *from; DBG1(DBG_IKE, "redirected to %Y", gateway); if (!this->follow_redirects) @@ -1978,16 +1991,20 @@ METHOD(ike_sa_t, handle_redirect, bool, gateway); return FALSE; } + from = this->other_host->clone(this->other_host); switch (this->state) { case IKE_CONNECTING: reset(this); set_other_host(this, other); + DESTROY_IF(this->redirected_from); + this->redirected_from = from; return TRUE; default: DBG1(DBG_IKE, "unable to handle redirect for IKE_SA in state %N", ike_sa_state_names, this->state); other->destroy(other); + from->destroy(from); return FALSE; } } @@ -2505,6 +2522,7 @@ METHOD(ike_sa_t, destroy, void, DESTROY_IF(this->other_id); DESTROY_IF(this->local_host); DESTROY_IF(this->remote_host); + DESTROY_IF(this->redirected_from); DESTROY_IF(this->ike_cfg); DESTROY_IF(this->peer_cfg); @@ -2585,6 +2603,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator, .send_dpd = _send_dpd, .send_keepalive = _send_keepalive, .handle_redirect = _handle_redirect, + .get_redirected_from = _get_redirected_from, .get_keymat = _get_keymat, .add_child_sa = _add_child_sa, .get_child_sa = _get_child_sa, diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h index 384912d00..54c0a2ebf 100644 --- a/src/libcharon/sa/ike_sa.h +++ b/src/libcharon/sa/ike_sa.h @@ -857,6 +857,13 @@ struct ike_sa_t { */ bool (*handle_redirect)(ike_sa_t *this, identification_t *gateway); + /** + * Get the address of the gateway that redirected us. + * + * @return original gateway address + */ + host_t *(*get_redirected_from)(ike_sa_t *this); + /** * Get the keying material of this IKE_SA. * From 6cde9875e123206d949225eac14e28c79ed12de9 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 23 Apr 2015 12:23:46 +0200 Subject: [PATCH 13/36] ike-init: Send REDIRECTED_FROM instead of REDIRECT_SUPPORTED if appropriate --- src/libcharon/sa/ikev2/tasks/ike_init.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/libcharon/sa/ikev2/tasks/ike_init.c b/src/libcharon/sa/ikev2/tasks/ike_init.c index 42be11896..09dc903f2 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_init.c +++ b/src/libcharon/sa/ikev2/tasks/ike_init.c @@ -332,7 +332,25 @@ static bool build_payloads(private_ike_init_t *this, message_t *message) /* notify other peer if we support redirection */ if (!this->old_sa && this->initiator && this->follow_redirects) { - message->add_notify(message, FALSE, REDIRECT_SUPPORTED, chunk_empty); + identification_t *gateway; + host_t *from; + chunk_t data; + + from = this->ike_sa->get_redirected_from(this->ike_sa); + if (from) + { + gateway = identification_create_from_sockaddr( + from->get_sockaddr(from)); + data = redirect_data_create(gateway, chunk_empty); + message->add_notify(message, FALSE, REDIRECTED_FROM, data); + chunk_free(&data); + gateway->destroy(gateway); + } + else + { + message->add_notify(message, FALSE, REDIRECT_SUPPORTED, + chunk_empty); + } } return TRUE; } From 3d074bce0036b03e700bb35f128b1682ad082036 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 23 Apr 2015 12:29:03 +0200 Subject: [PATCH 14/36] ike-init: Handle REDIRECTED_FROM similar to REDIRECT_SUPPORTED as server --- src/libcharon/sa/ikev2/tasks/ike_init.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/libcharon/sa/ikev2/tasks/ike_init.c b/src/libcharon/sa/ikev2/tasks/ike_init.c index 09dc903f2..b5a58dfd7 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_init.c +++ b/src/libcharon/sa/ikev2/tasks/ike_init.c @@ -419,6 +419,23 @@ static void process_payloads(private_ike_init_t *this, message_t *message) handle_supported_hash_algorithms(this, notify); } break; + case REDIRECTED_FROM: + { + identification_t *gateway; + chunk_t data; + + data = notify->get_notification_data(notify); + gateway = redirect_data_parse(data, NULL); + if (!gateway) + { + DBG1(DBG_IKE, "received invalid REDIRECTED_FROM " + "notify, ignored"); + break; + } + DBG1(DBG_IKE, "client got redirected from %Y", gateway); + gateway->destroy(gateway); + /* fall-through */ + } case REDIRECT_SUPPORTED: this->ike_sa->enable_extension(this->ike_sa, EXT_IKE_REDIRECTION); From d68c05d2691258b5ed685a447c95b6def2087aff Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 23 Apr 2015 16:31:39 +0200 Subject: [PATCH 15/36] ike-sa: Add a condition to mark redirected IKE_SAs --- src/libcharon/sa/ike_sa.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h index 54c0a2ebf..32e9d65d5 100644 --- a/src/libcharon/sa/ike_sa.h +++ b/src/libcharon/sa/ike_sa.h @@ -202,6 +202,11 @@ enum ike_condition_t { * This IKE_SA is currently being reauthenticated */ COND_REAUTHENTICATING = (1<<10), + + /** + * This IKE_SA has been redirected + */ + COND_REDIRECTED = (1<<11), }; /** From b6fcb917626474885da4a07bd8ae98a5111dc3cf Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 23 Apr 2015 16:36:49 +0200 Subject: [PATCH 16/36] child-create: Don't create CHILD_SA if the IKE_SA got redirected in IKE_AUTH --- src/libcharon/sa/ikev2/tasks/child_create.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libcharon/sa/ikev2/tasks/child_create.c b/src/libcharon/sa/ikev2/tasks/child_create.c index 740d09778..3d4ded944 100644 --- a/src/libcharon/sa/ikev2/tasks/child_create.c +++ b/src/libcharon/sa/ikev2/tasks/child_create.c @@ -1220,6 +1220,10 @@ METHOD(task_t, build_r, status_t, { /* wait until all authentication round completed */ return NEED_MORE; } + if (this->ike_sa->has_condition(this->ike_sa, COND_REDIRECTED)) + { /* no CHILD_SA is created for redirected SAs */ + return SUCCESS; + } ike_auth = TRUE; default: break; From fdc4b82728c3d2eeeeb092cc95fb1d6bc8706c30 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 28 Apr 2015 18:15:35 +0200 Subject: [PATCH 17/36] ike-config: Do not assign attributes for redirected IKE_SAs --- src/libcharon/sa/ikev2/tasks/ike_config.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libcharon/sa/ikev2/tasks/ike_config.c b/src/libcharon/sa/ikev2/tasks/ike_config.c index 646f20c61..6c42b81a6 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_config.c +++ b/src/libcharon/sa/ikev2/tasks/ike_config.c @@ -333,6 +333,11 @@ METHOD(task_t, build_r, status_t, linked_list_t *vips, *pools; host_t *requested; + if (this->ike_sa->has_condition(this->ike_sa, COND_REDIRECTED)) + { /* don't assign attributes for redirected SAs */ + return SUCCESS; + } + id = this->ike_sa->get_other_eap_id(this->ike_sa); config = this->ike_sa->get_peer_cfg(this->ike_sa); vips = linked_list_create(); From 19233ef9808bf1cae48964d560bd8ac56d9f5cda Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 23 Apr 2015 16:43:35 +0200 Subject: [PATCH 18/36] ike-auth: Send REDIRECT notify during IKE_AUTH if requested by providers To prevent the creation of the CHILD_SA we set a condition on the IKE_SA. We also schedule a delete job in case the client does not terminate the IKE_SA (which is a SHOULD in RFC 5685). --- src/libcharon/sa/ikev2/tasks/ike_auth.c | 78 ++++++++++++++++--------- 1 file changed, 51 insertions(+), 27 deletions(-) diff --git a/src/libcharon/sa/ikev2/tasks/ike_auth.c b/src/libcharon/sa/ikev2/tasks/ike_auth.c index 2554496c1..ddb92f155 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_auth.c +++ b/src/libcharon/sa/ikev2/tasks/ike_auth.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Tobias Brunner + * Copyright (C) 2012-2015 Tobias Brunner * Copyright (C) 2005-2009 Martin Willi * Copyright (C) 2005 Jan Hutter * Hochschule fuer Technik Rapperswil @@ -25,6 +25,7 @@ #include #include #include +#include typedef struct private_ike_auth_t private_ike_auth_t; @@ -685,6 +686,7 @@ METHOD(task_t, process_r, status_t, METHOD(task_t, build_r, status_t, private_ike_auth_t *this, message_t *message) { + identification_t *gateway; auth_cfg_t *cfg; if (message->get_exchange_type(message) == IKE_SA_INIT) @@ -817,34 +819,56 @@ METHOD(task_t, build_r, status_t, { this->do_another_auth = FALSE; } - if (!this->do_another_auth && !this->expect_another_auth) + if (this->do_another_auth || this->expect_another_auth) { - if (charon->ike_sa_manager->check_uniqueness(charon->ike_sa_manager, - this->ike_sa, FALSE)) - { - DBG1(DBG_IKE, "cancelling IKE_SA setup due to uniqueness policy"); - charon->bus->alert(charon->bus, ALERT_UNIQUE_KEEP); - message->add_notify(message, TRUE, AUTHENTICATION_FAILED, - chunk_empty); - return FAILED; - } - if (!charon->bus->authorize(charon->bus, TRUE)) - { - DBG1(DBG_IKE, "final authorization hook forbids IKE_SA, cancelling"); - goto peer_auth_failed; - } - DBG0(DBG_IKE, "IKE_SA %s[%d] established between %H[%Y]...%H[%Y]", - this->ike_sa->get_name(this->ike_sa), - this->ike_sa->get_unique_id(this->ike_sa), - this->ike_sa->get_my_host(this->ike_sa), - this->ike_sa->get_my_id(this->ike_sa), - this->ike_sa->get_other_host(this->ike_sa), - this->ike_sa->get_other_id(this->ike_sa)); - this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); - charon->bus->ike_updown(charon->bus, this->ike_sa, TRUE); - return SUCCESS; + return NEED_MORE; } - return NEED_MORE; + + if (charon->ike_sa_manager->check_uniqueness(charon->ike_sa_manager, + this->ike_sa, FALSE)) + { + DBG1(DBG_IKE, "cancelling IKE_SA setup due to uniqueness policy"); + charon->bus->alert(charon->bus, ALERT_UNIQUE_KEEP); + message->add_notify(message, TRUE, AUTHENTICATION_FAILED, + chunk_empty); + return FAILED; + } + if (!charon->bus->authorize(charon->bus, TRUE)) + { + DBG1(DBG_IKE, "final authorization hook forbids IKE_SA, cancelling"); + goto peer_auth_failed; + } + if (this->ike_sa->supports_extension(this->ike_sa, EXT_IKE_REDIRECTION) && + charon->redirect->redirect_on_auth(charon->redirect, this->ike_sa, + &gateway)) + { + delete_ike_sa_job_t *job; + chunk_t data; + + DBG1(DBG_IKE, "redirecting peer to %Y", gateway); + data = redirect_data_create(gateway, chunk_empty); + message->add_notify(message, FALSE, REDIRECT, data); + gateway->destroy(gateway); + chunk_free(&data); + /* we use this condition to prevent the CHILD_SA from getting created */ + this->ike_sa->set_condition(this->ike_sa, COND_REDIRECTED, TRUE); + /* if the peer does not delete the SA we do so after a while */ + job = delete_ike_sa_job_create(this->ike_sa->get_id(this->ike_sa), TRUE); + lib->scheduler->schedule_job(lib->scheduler, (job_t*)job, + lib->settings->get_int(lib->settings, + "%s.half_open_timeout", HALF_OPEN_IKE_SA_TIMEOUT, + lib->ns)); + } + DBG0(DBG_IKE, "IKE_SA %s[%d] established between %H[%Y]...%H[%Y]", + this->ike_sa->get_name(this->ike_sa), + this->ike_sa->get_unique_id(this->ike_sa), + this->ike_sa->get_my_host(this->ike_sa), + this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_other_host(this->ike_sa), + this->ike_sa->get_other_id(this->ike_sa)); + this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); + charon->bus->ike_updown(charon->bus, this->ike_sa, TRUE); + return SUCCESS; peer_auth_failed: message->add_notify(message, TRUE, AUTHENTICATION_FAILED, chunk_empty); From f20e00fe545d9939de095979870eebc6987ea6ad Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 24 Apr 2015 16:57:43 +0200 Subject: [PATCH 19/36] ike-sa: Handle redirect requests for established SAs as reestablishment We handle this similar to how we do reestablishing IKE_SAs with all CHILD_SAs, which also includes the one actively queued during IKE_AUTH. To delete the old SA we use the recently added ike_reauth_complete task. --- src/libcharon/sa/ike_sa.c | 258 ++++++++++++++++++++++++++------------ 1 file changed, 175 insertions(+), 83 deletions(-) diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index 79960fcb2..f0c38efa8 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -56,6 +56,7 @@ #include #include #include +#include #ifdef ME #include @@ -1768,6 +1769,86 @@ static bool is_child_queued(private_ike_sa_t *this, task_queue_t queue) return found; } +/** + * Reestablish CHILD_SAs and migrate queued tasks. + * + * If force is true all SAs are restarted, otherwise their close/dpd_action + * is followed. + */ +static status_t reestablish_children(private_ike_sa_t *this, ike_sa_t *new, + bool force) +{ + enumerator_t *enumerator; + child_sa_t *child_sa; + child_cfg_t *child_cfg; + action_t action; + status_t status = FAILED; + + /* handle existing CHILD_SAs */ + enumerator = create_child_sa_enumerator(this); + while (enumerator->enumerate(enumerator, (void**)&child_sa)) + { + if (force) + { + switch (child_sa->get_state(child_sa)) + { + case CHILD_ROUTED: + { /* move routed child directly */ + remove_child_sa(this, enumerator); + new->add_child_sa(new, child_sa); + action = ACTION_NONE; + break; + } + default: + { /* initiate/queue all other CHILD_SAs */ + action = ACTION_RESTART; + break; + } + } + } + else + { /* only restart CHILD_SAs that are configured accordingly */ + if (this->state == IKE_DELETING) + { + action = child_sa->get_close_action(child_sa); + } + else + { + action = child_sa->get_dpd_action(child_sa); + } + } + switch (action) + { + case ACTION_RESTART: + child_cfg = child_sa->get_config(child_sa); + DBG1(DBG_IKE, "restarting CHILD_SA %s", + child_cfg->get_name(child_cfg)); + child_cfg->get_ref(child_cfg); + status = new->initiate(new, child_cfg, + child_sa->get_reqid(child_sa), NULL, NULL); + break; + default: + continue; + } + if (status == DESTROY_ME) + { + break; + } + } + enumerator->destroy(enumerator); + /* adopt any active or queued CHILD-creating tasks */ + if (status != DESTROY_ME) + { + task_manager_t *other_tasks = ((private_ike_sa_t*)new)->task_manager; + other_tasks->adopt_child_tasks(other_tasks, this->task_manager); + if (new->get_state(new) == IKE_CREATED) + { + status = new->initiate(new, NULL, 0, NULL, NULL); + } + } + return status; +} + METHOD(ike_sa_t, reestablish, status_t, private_ike_sa_t *this) { @@ -1776,7 +1857,6 @@ METHOD(ike_sa_t, reestablish, status_t, action_t action; enumerator_t *enumerator; child_sa_t *child_sa; - child_cfg_t *child_cfg; bool restart = FALSE; status_t status = FAILED; @@ -1887,68 +1967,8 @@ METHOD(ike_sa_t, reestablish, status_t, else #endif /* ME */ { - /* handle existing CHILD_SAs */ - enumerator = create_child_sa_enumerator(this); - while (enumerator->enumerate(enumerator, (void**)&child_sa)) - { - if (has_condition(this, COND_REAUTHENTICATING)) - { - switch (child_sa->get_state(child_sa)) - { - case CHILD_ROUTED: - { /* move routed child directly */ - remove_child_sa(this, enumerator); - new->add_child_sa(new, child_sa); - action = ACTION_NONE; - break; - } - default: - { /* initiate/queue all other CHILD_SAs */ - action = ACTION_RESTART; - break; - } - } - } - else - { /* only restart CHILD_SAs that are configured accordingly */ - if (this->state == IKE_DELETING) - { - action = child_sa->get_close_action(child_sa); - } - else - { - action = child_sa->get_dpd_action(child_sa); - } - } - switch (action) - { - case ACTION_RESTART: - child_cfg = child_sa->get_config(child_sa); - DBG1(DBG_IKE, "restarting CHILD_SA %s", - child_cfg->get_name(child_cfg)); - child_cfg->get_ref(child_cfg); - status = new->initiate(new, child_cfg, - child_sa->get_reqid(child_sa), NULL, NULL); - break; - default: - continue; - } - if (status == DESTROY_ME) - { - break; - } - } - enumerator->destroy(enumerator); - /* adopt any active or queued CHILD-creating tasks */ - if (status != DESTROY_ME) - { - task_manager_t *other_tasks = ((private_ike_sa_t*)new)->task_manager; - other_tasks->adopt_child_tasks(other_tasks, this->task_manager); - if (new->get_state(new) == IKE_CREATED) - { - status = new->initiate(new, NULL, 0, NULL, NULL); - } - } + status = reestablish_children(this, new, + has_condition(this, COND_REAUTHENTICATING)); } if (status == DESTROY_ME) @@ -1969,12 +1989,98 @@ METHOD(ike_sa_t, reestablish, status_t, return status; } +/** + * Resolve the given gateway ID + */ +static host_t *resolve_gateway_id(identification_t *gateway) +{ + char gw[BUF_LEN]; + host_t *addr; + + snprintf(gw, sizeof(gw), "%Y", gateway); + gw[sizeof(gw)-1] = '\0'; + addr = host_create_from_dns(gw, AF_UNSPEC, IKEV2_UDP_PORT); + if (!addr) + { + DBG1(DBG_IKE, "unable to resolve gateway ID '%Y', redirect failed", + gateway); + } + return addr; +} + +/** + * Redirect the current SA to the given target host + */ +static bool redirect_established(private_ike_sa_t *this, identification_t *to) +{ + private_ike_sa_t *new_priv; + ike_sa_t *new; + host_t *other; + + new = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager, + this->version, TRUE); + if (!new) + { + return FALSE; + } + new_priv = (private_ike_sa_t*)new; + new->set_peer_cfg(new, this->peer_cfg); + new_priv->redirected_from = this->other_host->clone(this->other_host); + charon->bus->ike_reestablish_pre(charon->bus, &this->public, new); + other = resolve_gateway_id(to); + if (other) + { + set_my_host(new_priv, this->my_host->clone(this->my_host)); + /* this allows us to force the remote address while we still properly + * resolve the local address */ + new_priv->remote_host = other; + resolve_hosts(new_priv); + if (reestablish_children(this, new, TRUE) != DESTROY_ME) + { +#ifdef USE_IKEV2 + new->queue_task(new, (task_t*)ike_reauth_complete_create(new, + this->ike_sa_id)); +#endif + charon->bus->ike_reestablish_post(charon->bus, &this->public, new, + TRUE); + charon->ike_sa_manager->checkin(charon->ike_sa_manager, new); + charon->bus->set_sa(charon->bus, &this->public); + return TRUE; + } + } + charon->bus->ike_reestablish_post(charon->bus, &this->public, new, + FALSE); + charon->ike_sa_manager->checkin_and_destroy(charon->ike_sa_manager, new); + charon->bus->set_sa(charon->bus, &this->public); + return FALSE; +} + +/** + * Redirect the current connecting SA to the given target host + */ +static bool redirect_connecting(private_ike_sa_t *this, identification_t *to) +{ + host_t *other; + + other = resolve_gateway_id(to); + if (!other) + { + return FALSE; + } + reset(this); + DESTROY_IF(this->redirected_from); + this->redirected_from = this->other_host->clone(this->other_host); + DESTROY_IF(this->remote_host); + /* this allows us to force the remote address while we still properly + * resolve the local address */ + this->remote_host = other; + resolve_hosts(this); + return TRUE; +} + METHOD(ike_sa_t, handle_redirect, bool, private_ike_sa_t *this, identification_t *gateway) { - char gw[BUF_LEN]; - host_t *other, *from; - DBG1(DBG_IKE, "redirected to %Y", gateway); if (!this->follow_redirects) { @@ -1982,29 +2088,15 @@ METHOD(ike_sa_t, handle_redirect, bool, return FALSE; } - snprintf(gw, sizeof(gw), "%Y", gateway); - gw[sizeof(gw)-1] = '\0'; - other = host_create_from_dns(gw, AF_UNSPEC, IKEV2_UDP_PORT); - if (!other) - { - DBG1(DBG_IKE, "unable to resolve gateway ID '%Y', redirect failed", - gateway); - return FALSE; - } - from = this->other_host->clone(this->other_host); switch (this->state) { case IKE_CONNECTING: - reset(this); - set_other_host(this, other); - DESTROY_IF(this->redirected_from); - this->redirected_from = from; - return TRUE; + return redirect_connecting(this, gateway); + case IKE_ESTABLISHED: + return redirect_established(this, gateway); default: DBG1(DBG_IKE, "unable to handle redirect for IKE_SA in state %N", ike_sa_state_names, this->state); - other->destroy(other); - from->destroy(from); return FALSE; } } From f5a9025ce9101574e511bc7bd0cc2a2f003f5c2f Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Fri, 24 Apr 2015 16:59:44 +0200 Subject: [PATCH 20/36] ike-auth: Handle REDIRECT notifies during IKE_AUTH --- src/libcharon/sa/ikev2/tasks/ike_auth.c | 66 ++++++++++++++++--------- 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/src/libcharon/sa/ikev2/tasks/ike_auth.c b/src/libcharon/sa/ikev2/tasks/ike_auth.c index ddb92f155..79a436fbf 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_auth.c +++ b/src/libcharon/sa/ikev2/tasks/ike_auth.c @@ -118,6 +118,11 @@ struct private_ike_auth_t { * Is EAP acceptable, did we strictly authenticate peer? */ bool eap_acceptable; + + /** + * Gateway ID if redirected + */ + identification_t *redirect_to; }; /** @@ -988,6 +993,15 @@ METHOD(task_t, process_i, status_t, case ME_ENDPOINT: /* handled in ike_me task */ break; + case REDIRECT: + DESTROY_IF(this->redirect_to); + this->redirect_to = redirect_data_parse( + notify->get_notification_data(notify), NULL); + if (!this->redirect_to) + { + DBG1(DBG_IKE, "received invalid REDIRECT notify"); + } + break; default: { if (type <= 16383) @@ -1118,30 +1132,35 @@ METHOD(task_t, process_i, status_t, { this->expect_another_auth = FALSE; } - if (!this->expect_another_auth && !this->do_another_auth && !this->my_auth) + if (this->expect_another_auth || this->do_another_auth || this->my_auth) { - if (!update_cfg_candidates(this, TRUE)) - { - goto peer_auth_failed; - } - if (!charon->bus->authorize(charon->bus, TRUE)) - { - DBG1(DBG_IKE, "final authorization hook forbids IKE_SA, " - "cancelling"); - goto peer_auth_failed; - } - DBG0(DBG_IKE, "IKE_SA %s[%d] established between %H[%Y]...%H[%Y]", - this->ike_sa->get_name(this->ike_sa), - this->ike_sa->get_unique_id(this->ike_sa), - this->ike_sa->get_my_host(this->ike_sa), - this->ike_sa->get_my_id(this->ike_sa), - this->ike_sa->get_other_host(this->ike_sa), - this->ike_sa->get_other_id(this->ike_sa)); - this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); - charon->bus->ike_updown(charon->bus, this->ike_sa, TRUE); - return SUCCESS; + return NEED_MORE; } - return NEED_MORE; + if (!update_cfg_candidates(this, TRUE)) + { + goto peer_auth_failed; + } + if (!charon->bus->authorize(charon->bus, TRUE)) + { + DBG1(DBG_IKE, "final authorization hook forbids IKE_SA, " + "cancelling"); + goto peer_auth_failed; + } + DBG0(DBG_IKE, "IKE_SA %s[%d] established between %H[%Y]...%H[%Y]", + this->ike_sa->get_name(this->ike_sa), + this->ike_sa->get_unique_id(this->ike_sa), + this->ike_sa->get_my_host(this->ike_sa), + this->ike_sa->get_my_id(this->ike_sa), + this->ike_sa->get_other_host(this->ike_sa), + this->ike_sa->get_other_id(this->ike_sa)); + this->ike_sa->set_state(this->ike_sa, IKE_ESTABLISHED); + charon->bus->ike_updown(charon->bus, this->ike_sa, TRUE); + + if (this->redirect_to) + { + this->ike_sa->handle_redirect(this->ike_sa, this->redirect_to); + } + return SUCCESS; peer_auth_failed: charon->bus->alert(charon->bus, ALERT_PEER_AUTH_FAILED); @@ -1165,6 +1184,7 @@ METHOD(task_t, migrate, void, DESTROY_IF(this->peer_cfg); DESTROY_IF(this->my_auth); DESTROY_IF(this->other_auth); + DESTROY_IF(this->redirect_to); this->candidates->destroy_offset(this->candidates, offsetof(peer_cfg_t, destroy)); this->my_packet = NULL; @@ -1173,6 +1193,7 @@ METHOD(task_t, migrate, void, this->peer_cfg = NULL; this->my_auth = NULL; this->other_auth = NULL; + this->redirect_to = NULL; this->do_another_auth = TRUE; this->expect_another_auth = TRUE; this->authentication_failed = FALSE; @@ -1189,6 +1210,7 @@ METHOD(task_t, destroy, void, DESTROY_IF(this->my_auth); DESTROY_IF(this->other_auth); DESTROY_IF(this->peer_cfg); + DESTROY_IF(this->redirect_to); this->candidates->destroy_offset(this->candidates, offsetof(peer_cfg_t, destroy)); free(this); } From 0840385b275bd4f46ad528922e1a75a559cba842 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 28 Apr 2015 14:10:43 +0200 Subject: [PATCH 21/36] ike-redirect: Add task to redirect active IKE_SAs --- src/libcharon/Android.mk | 1 + src/libcharon/Makefile.am | 1 + src/libcharon/sa/ikev2/task_manager_v2.c | 11 ++ src/libcharon/sa/ikev2/tasks/ike_redirect.c | 150 ++++++++++++++++++++ src/libcharon/sa/ikev2/tasks/ike_redirect.h | 54 +++++++ src/libcharon/sa/task.c | 1 + src/libcharon/sa/task.h | 2 + 7 files changed, 220 insertions(+) create mode 100644 src/libcharon/sa/ikev2/tasks/ike_redirect.c create mode 100644 src/libcharon/sa/ikev2/tasks/ike_redirect.h diff --git a/src/libcharon/Android.mk b/src/libcharon/Android.mk index 6fe682e49..cd78e28be 100644 --- a/src/libcharon/Android.mk +++ b/src/libcharon/Android.mk @@ -108,6 +108,7 @@ sa/ikev2/tasks/ike_mobike.c sa/ikev2/tasks/ike_mobike.h \ sa/ikev2/tasks/ike_rekey.c sa/ikev2/tasks/ike_rekey.h \ sa/ikev2/tasks/ike_reauth.c sa/ikev2/tasks/ike_reauth.h \ sa/ikev2/tasks/ike_reauth_complete.c sa/ikev2/tasks/ike_reauth_complete.h \ +sa/ikev2/tasks/ike_redirect.c sa/ikev2/tasks/ike_redirect.h \ sa/ikev2/tasks/ike_auth_lifetime.c sa/ikev2/tasks/ike_auth_lifetime.h \ sa/ikev2/tasks/ike_vendor.c sa/ikev2/tasks/ike_vendor.h diff --git a/src/libcharon/Makefile.am b/src/libcharon/Makefile.am index 34587677f..71ceed379 100644 --- a/src/libcharon/Makefile.am +++ b/src/libcharon/Makefile.am @@ -108,6 +108,7 @@ sa/ikev2/tasks/ike_mobike.c sa/ikev2/tasks/ike_mobike.h \ sa/ikev2/tasks/ike_rekey.c sa/ikev2/tasks/ike_rekey.h \ sa/ikev2/tasks/ike_reauth.c sa/ikev2/tasks/ike_reauth.h \ sa/ikev2/tasks/ike_reauth_complete.c sa/ikev2/tasks/ike_reauth_complete.h \ +sa/ikev2/tasks/ike_redirect.c sa/ikev2/tasks/ike_redirect.h \ sa/ikev2/tasks/ike_auth_lifetime.c sa/ikev2/tasks/ike_auth_lifetime.h \ sa/ikev2/tasks/ike_vendor.c sa/ikev2/tasks/ike_vendor.h endif diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c index 4676867df..feddada4d 100644 --- a/src/libcharon/sa/ikev2/task_manager_v2.c +++ b/src/libcharon/sa/ikev2/task_manager_v2.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -474,6 +475,11 @@ METHOD(task_manager_t, initiate, status_t, exchange = INFORMATIONAL; break; } + if (activate_task(this, TASK_IKE_REDIRECT)) + { + exchange = INFORMATIONAL; + break; + } if (activate_task(this, TASK_CHILD_DELETE)) { exchange = INFORMATIONAL; @@ -992,6 +998,11 @@ static status_t process_request(private_task_manager_t *this, * invokes all the required hooks. */ task = (task_t*)ike_delete_create( this->ike_sa, FALSE); + break; + case REDIRECT: + task = (task_t*)ike_redirect_create( + this->ike_sa, NULL); + break; default: break; } diff --git a/src/libcharon/sa/ikev2/tasks/ike_redirect.c b/src/libcharon/sa/ikev2/tasks/ike_redirect.c new file mode 100644 index 000000000..f82c80f71 --- /dev/null +++ b/src/libcharon/sa/ikev2/tasks/ike_redirect.c @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2015 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 . + * + * 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_redirect.h" + +#include +#include + +typedef struct private_ike_redirect_t private_ike_redirect_t; + +/** + * Private members + */ +struct private_ike_redirect_t { + + /** + * Public interface + */ + ike_redirect_t public; + + /** + * Assigned IKE_SA + */ + ike_sa_t *ike_sa; + + /** + * Gateway ID to redirect to + */ + identification_t *gateway; +}; + +METHOD(task_t, build_i, status_t, + private_ike_redirect_t *this, message_t *message) +{ + chunk_t data; + + DBG1(DBG_IKE, "redirecting peer to %Y", this->gateway); + data = redirect_data_create(this->gateway, chunk_empty); + message->add_notify(message, FALSE, REDIRECT, data); + chunk_free(&data); + this->ike_sa->set_condition(this->ike_sa, COND_REDIRECTED, TRUE); + return NEED_MORE; +} + +METHOD(task_t, process_r, status_t, + private_ike_redirect_t *this, message_t *message) +{ + notify_payload_t *notify; + identification_t *to; + + notify = message->get_notify(message, REDIRECT); + if (!notify) + { + return SUCCESS; + } + + to = redirect_data_parse(notify->get_notification_data(notify), NULL); + if (!to) + { + DBG1(DBG_IKE, "received invalid REDIRECT notify"); + } + else + { + this->ike_sa->handle_redirect(this->ike_sa, to); + to->destroy(to); + } + return SUCCESS; +} + +METHOD(task_t, build_r, status_t, + private_ike_redirect_t *this, message_t *message) +{ + /* not called because SUCCESS is returned above */ + return SUCCESS; +} + +METHOD(task_t, process_i, status_t, + private_ike_redirect_t *this, message_t *message) +{ + delete_ike_sa_job_t *job; + + /* if the peer does not delete the SA we do so after a while */ + job = delete_ike_sa_job_create(this->ike_sa->get_id(this->ike_sa), TRUE); + lib->scheduler->schedule_job(lib->scheduler, (job_t*)job, + lib->settings->get_int(lib->settings, + "%s.half_open_timeout", HALF_OPEN_IKE_SA_TIMEOUT, + lib->ns)); + return SUCCESS; +} + +METHOD(task_t, get_type, task_type_t, + private_ike_redirect_t *this) +{ + return TASK_IKE_REDIRECT; +} + +METHOD(task_t, migrate, void, + private_ike_redirect_t *this, ike_sa_t *ike_sa) +{ + this->ike_sa = ike_sa; +} + +METHOD(task_t, destroy, void, + private_ike_redirect_t *this) +{ + DESTROY_IF(this->gateway); + free(this); +} + +/* + * Described in header. + */ +ike_redirect_t *ike_redirect_create(ike_sa_t *ike_sa, identification_t *to) +{ + private_ike_redirect_t *this; + + INIT(this, + .public = { + .task = { + .get_type = _get_type, + .build = _build_r, + .process = _process_r, + .migrate = _migrate, + .destroy = _destroy, + }, + }, + .ike_sa = ike_sa, + ); + + if (to) + { + this->gateway = to->clone(to); + this->public.task.build = _build_i; + this->public.task.process = _process_i; + } + + return &this->public; +} diff --git a/src/libcharon/sa/ikev2/tasks/ike_redirect.h b/src/libcharon/sa/ikev2/tasks/ike_redirect.h new file mode 100644 index 000000000..afa00ce5d --- /dev/null +++ b/src/libcharon/sa/ikev2/tasks/ike_redirect.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2015 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 . + * + * 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_redirect ike_redirect + * @{ @ingroup tasks_v2 + */ + +#ifndef IKE_REDIRECT_H_ +#define IKE_REDIRECT_H_ + +typedef struct ike_redirect_t ike_redirect_t; + +#include +#include +#include + +/** + * Task that handles redirection requests for established SAs. + */ +struct ike_redirect_t { + + /** + * Implements the task_t interface + */ + task_t task; +}; + +/** + * Create a new ike_redirect_t task. + * + * As initiator (i.e. original responder) pass the ID of the target gateway, + * as responder (i.e. original initiator) this argument is NULL. + * + * @param ike_sa IKE_SA this task works for + * @param to gateway ID (gets cloned), or NULL as responder + * @return task instance + */ +ike_redirect_t *ike_redirect_create(ike_sa_t *ike_sa, + identification_t *to); + +#endif /** IKE_REDIRECT_H_ @}*/ diff --git a/src/libcharon/sa/task.c b/src/libcharon/sa/task.c index b35b58185..4bd2ba221 100644 --- a/src/libcharon/sa/task.c +++ b/src/libcharon/sa/task.c @@ -28,6 +28,7 @@ ENUM(task_type_names, TASK_IKE_INIT, TASK_ISAKMP_CERT_POST, "IKE_REKEY", "IKE_REAUTH", "IKE_REAUTH_COMPLETE", + "IKE_REDIRECT", "IKE_DELETE", "IKE_DPD", "IKE_VENDOR", diff --git a/src/libcharon/sa/task.h b/src/libcharon/sa/task.h index 7bd3da1fe..ae4260762 100644 --- a/src/libcharon/sa/task.h +++ b/src/libcharon/sa/task.h @@ -57,6 +57,8 @@ enum task_type_t { TASK_IKE_REAUTH, /** completion task for make-before-break IKE_SA re-authentication */ TASK_IKE_REAUTH_COMPLETE, + /** redirect an active IKE_SA */ + TASK_IKE_REDIRECT, /** delete an IKE_SA */ TASK_IKE_DELETE, /** liveness check */ From 71c707058862bb7c46633b425c3746c0325f3cb9 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 28 Apr 2015 14:50:11 +0200 Subject: [PATCH 22/36] ike-sa: Add redirect() method to actively redirect an IKE_SA --- src/libcharon/sa/ike_sa.c | 41 +++++++++++++++++++++++++++++++++++++++ src/libcharon/sa/ike_sa.h | 9 +++++++++ 2 files changed, 50 insertions(+) diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index f0c38efa8..6884bf249 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -57,6 +57,7 @@ #include #include #include +#include #ifdef ME #include @@ -2101,6 +2102,45 @@ METHOD(ike_sa_t, handle_redirect, bool, } } +METHOD(ike_sa_t, redirect, status_t, + private_ike_sa_t *this, identification_t *gateway) +{ + switch (this->state) + { + case IKE_CONNECTING: + case IKE_ESTABLISHED: + case IKE_REKEYING: + if (has_condition(this, COND_REDIRECTED)) + { /* IKE_SA already got redirected */ + return SUCCESS; + } + if (has_condition(this, COND_ORIGINAL_INITIATOR)) + { + DBG1(DBG_IKE, "unable to redirect IKE_SA as initiator"); + return FAILED; + } + if (this->version == IKEV1) + { + DBG1(DBG_IKE, "unable to redirect IKEv1 SA"); + return FAILED; + } + if (!supports_extension(this, EXT_IKE_REDIRECTION)) + { + DBG1(DBG_IKE, "client does not support IKE redirection"); + return FAILED; + } +#ifdef USE_IKEV2 + this->task_manager->queue_task(this->task_manager, + (task_t*)ike_redirect_create(&this->public, gateway)); +#endif + return this->task_manager->initiate(this->task_manager); + default: + DBG1(DBG_IKE, "unable to redirect IKE_SA in state %N", + ike_sa_state_names, this->state); + return INVALID_STATE; + } +} + METHOD(ike_sa_t, retransmit, status_t, private_ike_sa_t *this, u_int32_t message_id) { @@ -2694,6 +2734,7 @@ ike_sa_t * ike_sa_create(ike_sa_id_t *ike_sa_id, bool initiator, .destroy = _destroy, .send_dpd = _send_dpd, .send_keepalive = _send_keepalive, + .redirect = _redirect, .handle_redirect = _handle_redirect, .get_redirected_from = _get_redirected_from, .get_keymat = _get_keymat, diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h index 32e9d65d5..08c19971f 100644 --- a/src/libcharon/sa/ike_sa.h +++ b/src/libcharon/sa/ike_sa.h @@ -852,6 +852,15 @@ struct ike_sa_t { */ void (*send_keepalive) (ike_sa_t *this, bool scheduled); + /** + * Redirect an active IKE_SA. + * + * @param gateway gateway ID (IP or FQDN) of the target + * @return state, including DESTROY_ME, if this IKE_SA MUST be + * destroyed + */ + status_t (*redirect)(ike_sa_t *this, identification_t *gateway); + /** * Handle a redirect request. * From 0d424d2107218a0fb03b05ca6981caa3d3be0721 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 28 Apr 2015 17:29:42 +0200 Subject: [PATCH 23/36] redirect-job: Add job to redirect an active IKE_SA --- src/libcharon/Android.mk | 1 + src/libcharon/Makefile.am | 1 + src/libcharon/processing/jobs/redirect_job.c | 106 +++++++++++++++++++ src/libcharon/processing/jobs/redirect_job.h | 51 +++++++++ 4 files changed, 159 insertions(+) create mode 100644 src/libcharon/processing/jobs/redirect_job.c create mode 100644 src/libcharon/processing/jobs/redirect_job.h diff --git a/src/libcharon/Android.mk b/src/libcharon/Android.mk index cd78e28be..0b09d100d 100644 --- a/src/libcharon/Android.mk +++ b/src/libcharon/Android.mk @@ -59,6 +59,7 @@ processing/jobs/delete_child_sa_job.c processing/jobs/delete_child_sa_job.h \ processing/jobs/delete_ike_sa_job.c processing/jobs/delete_ike_sa_job.h \ processing/jobs/migrate_job.c processing/jobs/migrate_job.h \ processing/jobs/process_message_job.c processing/jobs/process_message_job.h \ +processing/jobs/redirect_job.c processing/jobs/redirect_job.h \ processing/jobs/rekey_child_sa_job.c processing/jobs/rekey_child_sa_job.h \ processing/jobs/rekey_ike_sa_job.c processing/jobs/rekey_ike_sa_job.h \ processing/jobs/retransmit_job.c processing/jobs/retransmit_job.h \ diff --git a/src/libcharon/Makefile.am b/src/libcharon/Makefile.am index 71ceed379..062b96aff 100644 --- a/src/libcharon/Makefile.am +++ b/src/libcharon/Makefile.am @@ -58,6 +58,7 @@ processing/jobs/delete_child_sa_job.c processing/jobs/delete_child_sa_job.h \ processing/jobs/delete_ike_sa_job.c processing/jobs/delete_ike_sa_job.h \ processing/jobs/migrate_job.c processing/jobs/migrate_job.h \ processing/jobs/process_message_job.c processing/jobs/process_message_job.h \ +processing/jobs/redirect_job.c processing/jobs/redirect_job.h \ processing/jobs/rekey_child_sa_job.c processing/jobs/rekey_child_sa_job.h \ processing/jobs/rekey_ike_sa_job.c processing/jobs/rekey_ike_sa_job.h \ processing/jobs/retransmit_job.c processing/jobs/retransmit_job.h \ diff --git a/src/libcharon/processing/jobs/redirect_job.c b/src/libcharon/processing/jobs/redirect_job.c new file mode 100644 index 000000000..e1af662c9 --- /dev/null +++ b/src/libcharon/processing/jobs/redirect_job.c @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2015 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 . + * + * 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 + +#include "redirect_job.h" + +typedef struct private_redirect_job_t private_redirect_job_t; + +/** + * Private data + */ +struct private_redirect_job_t { + + /** + * Public interface + */ + redirect_job_t public; + + /** + * ID of the IKE_SA to redirect + */ + ike_sa_id_t *ike_sa_id; + + /** + * Target gateway identity + */ + identification_t *gateway; +}; + + +METHOD(job_t, destroy, void, + private_redirect_job_t *this) +{ + this->ike_sa_id->destroy(this->ike_sa_id); + this->gateway->destroy(this->gateway); + free(this); +} + +METHOD(job_t, execute, job_requeue_t, + private_redirect_job_t *this) +{ + ike_sa_t *ike_sa; + + ike_sa = charon->ike_sa_manager->checkout(charon->ike_sa_manager, + this->ike_sa_id); + if (ike_sa) + { + if (ike_sa->get_state(ike_sa) == IKE_PASSIVE) + { + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + return JOB_REQUEUE_NONE; + } + if (ike_sa->redirect(ike_sa, this->gateway) == DESTROY_ME) + { + charon->ike_sa_manager->checkin_and_destroy( + charon->ike_sa_manager, ike_sa); + } + else + { + charon->ike_sa_manager->checkin(charon->ike_sa_manager, ike_sa); + } + } + return JOB_REQUEUE_NONE; +} + +METHOD(job_t, get_priority, job_priority_t, + private_redirect_job_t *this) +{ + return JOB_PRIO_MEDIUM; +} + +/* + * Described in header + */ +redirect_job_t *redirect_job_create(ike_sa_id_t *ike_sa_id, + identification_t *gateway) +{ + private_redirect_job_t *this; + + INIT(this, + .public = { + .job_interface = { + .execute = _execute, + .get_priority = _get_priority, + .destroy = _destroy, + }, + }, + .ike_sa_id = ike_sa_id->clone(ike_sa_id), + .gateway = gateway->clone(gateway), + ); + + return &(this->public); +} diff --git a/src/libcharon/processing/jobs/redirect_job.h b/src/libcharon/processing/jobs/redirect_job.h new file mode 100644 index 000000000..fe4b34ee9 --- /dev/null +++ b/src/libcharon/processing/jobs/redirect_job.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2015 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 . + * + * 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 redirect_job redirect_job + * @{ @ingroup cjobs + */ + +#ifndef REDIRECT_JOB_H_ +#define REDIRECT_JOB_H_ + +typedef struct redirect_job_t redirect_job_t; + +#include +#include +#include + +/** + * Job used to redirect an IKE_SA. + */ +struct redirect_job_t { + + /** + * The job_t interface. + */ + job_t job_interface; +}; + +/** + * Creates a job to redirect an IKE_SA. + * + * @param ike_sa_id id of the IKE_SA to redirect (cloned) + * @param gateway gateway identity (IP or FQDN) of target (cloned) + * @return created redirect_job_t object + */ +redirect_job_t *redirect_job_create(ike_sa_id_t *ike_sa_id, + identification_t *gateway); + +#endif /** REDIRECT_JOB_H_ @}*/ From 43b46b26ea1eac87e6e4189c5ef3b92ccb885c66 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 28 Apr 2015 17:45:52 +0200 Subject: [PATCH 24/36] vici: Add redirect command This allows redirecting IKE_SAs by multiple different selectors, if none are given all SAs are redirected. --- src/libcharon/plugins/vici/README.md | 15 +++ .../perl/Vici-Session/lib/Vici/Session.pm | 4 + .../plugins/vici/python/vici/session.py | 8 ++ src/libcharon/plugins/vici/ruby/lib/vici.rb | 6 + src/libcharon/plugins/vici/vici_control.c | 117 ++++++++++++++++++ 5 files changed, 150 insertions(+) diff --git a/src/libcharon/plugins/vici/README.md b/src/libcharon/plugins/vici/README.md index 773ef1aa5..d08488310 100644 --- a/src/libcharon/plugins/vici/README.md +++ b/src/libcharon/plugins/vici/README.md @@ -289,6 +289,21 @@ Terminates an SA while streaming _control-log_ events. The default timeout of 0 waits indefinitely for a result, and a timeout value of -1 returns a result immediately. +### redirect() ### + +Redirect a client-initiated IKE_SA to another gateway. Only for IKEv2 and if +supported by the peer. + + { + ike = + ike-id = + peer-ip = + peer-id = + } => { + success = + errmsg = + } + ### install() ### Install a trap, drop or bypass policy defined by a CHILD_SA config. diff --git a/src/libcharon/plugins/vici/perl/Vici-Session/lib/Vici/Session.pm b/src/libcharon/plugins/vici/perl/Vici-Session/lib/Vici/Session.pm index 5252296cf..78197136a 100644 --- a/src/libcharon/plugins/vici/perl/Vici-Session/lib/Vici/Session.pm +++ b/src/libcharon/plugins/vici/perl/Vici-Session/lib/Vici/Session.pm @@ -36,6 +36,10 @@ sub terminate { return request_vars_res('terminate', @_); } +sub redirect { + return request_vars_res('redirect', @_); +} + sub install { return request_vars_res('install', @_); } diff --git a/src/libcharon/plugins/vici/python/vici/session.py b/src/libcharon/plugins/vici/python/vici/session.py index 283e3d13d..66de8590a 100644 --- a/src/libcharon/plugins/vici/python/vici/session.py +++ b/src/libcharon/plugins/vici/python/vici/session.py @@ -53,6 +53,14 @@ class Session(object): """ return self.handler.streamed_request("terminate", "control-log", sa) + def redirect(self, sa): + """Redirect an IKE_SA. + + :param sa: the SA to redirect + :type sa: dict + """ + self.handler.request("redirect", sa) + def install(self, policy): """Install a trap, drop or bypass policy defined by a CHILD_SA config. diff --git a/src/libcharon/plugins/vici/ruby/lib/vici.rb b/src/libcharon/plugins/vici/ruby/lib/vici.rb index f8169add0..018f50766 100644 --- a/src/libcharon/plugins/vici/ruby/lib/vici.rb +++ b/src/libcharon/plugins/vici/ruby/lib/vici.rb @@ -504,6 +504,12 @@ module Vici "control-log", &block)) end + ## + # Redirect an IKE_SA. + def redirect(options) + check_success(@transp.request("redirect", Message.new(options))) + end + ## # Install a shunt/route policy. def install(policy) diff --git a/src/libcharon/plugins/vici/vici_control.c b/src/libcharon/plugins/vici/vici_control.c index 87794d24d..7bcab0e86 100644 --- a/src/libcharon/plugins/vici/vici_control.c +++ b/src/libcharon/plugins/vici/vici_control.c @@ -1,4 +1,7 @@ /* + * Copyright (C) 2015 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * * Copyright (C) 2014 Martin Willi * Copyright (C) 2014 revosec AG * @@ -20,6 +23,7 @@ #include #include +#include typedef struct private_vici_control_t private_vici_control_t; @@ -356,6 +360,118 @@ CALLBACK(terminate, vici_message_t*, return builder->finalize(builder); } +CALLBACK(redirect, vici_message_t*, + private_vici_control_t *this, char *name, u_int id, vici_message_t *request) +{ + enumerator_t *sas; + char *ike, *peer_ip, *peer_id, *gw, *errmsg = NULL; + u_int ike_id, current, found = 0; + identification_t *gateway, *identity = NULL; + host_t *address = NULL; + ike_sa_t *ike_sa; + vici_builder_t *builder; + + ike = request->get_str(request, NULL, "ike"); + ike_id = request->get_int(request, 0, "ike-id"); + peer_ip = request->get_str(request, NULL, "peer-ip"); + peer_id = request->get_str(request, NULL, "peer-id"); + gw = request->get_str(request, NULL, "gateway"); + + if (!gw || !(gateway = identification_create_from_string(gw))) + { + return send_reply(this, "missing target gateway"); + } + switch (gateway->get_type(gateway)) + { + case ID_IPV4_ADDR: + case ID_IPV6_ADDR: + case ID_FQDN: + break; + default: + return send_reply(this, "unsupported gateway identity"); + } + if (peer_ip) + { + address = host_create_from_string(peer_ip, 0); + if (!address) + { + return send_reply(this, "invalid peer IP selector"); + } + DBG1(DBG_CFG, "vici redirect IKE_SAs with src %H to %Y", address, + gateway); + } + if (peer_id) + { + identity = identification_create_from_string(peer_id); + if (!identity) + { + DESTROY_IF(address); + return send_reply(this, "invalid peer identity selector"); + } + DBG1(DBG_CFG, "vici redirect IKE_SAs with ID '%Y' to %Y", identity, + gateway); + } + if (ike_id) + { + DBG1(DBG_CFG, "vici redirect IKE_SA #%d to %Y", ike_id, gateway); + } + if (ike) + { + DBG1(DBG_CFG, "vici redirect IKE_SA '%s' to %Y", ike, gateway); + } + if (!peer_ip && !peer_id && !ike && !ike_id) + { + DBG1(DBG_CFG, "vici redirect all IKE_SAs to %Y", gateway); + } + + sas = charon->controller->create_ike_sa_enumerator(charon->controller, TRUE); + while (sas->enumerate(sas, &ike_sa)) + { + if (ike_sa->get_version(ike_sa) != IKEV2) + { + continue; + } + current = ike_sa->get_unique_id(ike_sa); + if (ike_id && ike_id != current) + { + continue; + } + if (ike && !streq(ike, ike_sa->get_name(ike_sa))) + { + continue; + } + if (address && + !address->ip_equals(address, ike_sa->get_other_host(ike_sa))) + { + continue; + } + if (identity && + !identity->equals(identity, ike_sa->get_other_eap_id(ike_sa))) + { + continue; + } + lib->processor->queue_job(lib->processor, + (job_t*)redirect_job_create(ike_sa->get_id(ike_sa), gateway)); + found++; + } + sas->destroy(sas); + + builder = vici_builder_create(); + if (!found) + { + errmsg = "no matching SAs to redirect found"; + } + builder->add_kv(builder, "success", errmsg ? "no" : "yes"); + if (errmsg) + { + builder->add_kv(builder, "errmsg", "%s", errmsg); + } + gateway->destroy(gateway); + DESTROY_IF(identity); + DESTROY_IF(address); + return builder->finalize(builder); +} + /** * Find reqid of an existing CHILD_SA */ @@ -498,6 +614,7 @@ static void manage_commands(private_vici_control_t *this, bool reg) { manage_command(this, "initiate", initiate, reg); manage_command(this, "terminate", terminate, reg); + manage_command(this, "redirect", redirect, reg); manage_command(this, "install", install, reg); manage_command(this, "uninstall", uninstall, reg); manage_command(this, "reload-settings", reload_settings, reg); From e92364db66bb8f09888252fb609b35fd8bf94fc8 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 28 Apr 2015 18:00:01 +0200 Subject: [PATCH 25/36] swanctl: Add --redirect command --- src/swanctl/Makefile.am | 1 + src/swanctl/command.h | 2 +- src/swanctl/commands/redirect.c | 132 ++++++++++++++++++++++++++++++++ src/swanctl/swanctl.8.in | 4 + 4 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 src/swanctl/commands/redirect.c diff --git a/src/swanctl/Makefile.am b/src/swanctl/Makefile.am index 5b6b8e4be..fb027149a 100644 --- a/src/swanctl/Makefile.am +++ b/src/swanctl/Makefile.am @@ -4,6 +4,7 @@ swanctl_SOURCES = \ command.c command.h \ commands/initiate.c \ commands/terminate.c \ + commands/redirect.c \ commands/install.c \ commands/list_sas.c \ commands/list_pols.c \ diff --git a/src/swanctl/command.h b/src/swanctl/command.h index 7eb11a68d..8d0a2e6b9 100644 --- a/src/swanctl/command.h +++ b/src/swanctl/command.h @@ -27,7 +27,7 @@ /** * Maximum number of commands (+1). */ -#define MAX_COMMANDS 22 +#define MAX_COMMANDS 23 /** * Maximum number of options in a command (+3) diff --git a/src/swanctl/commands/redirect.c b/src/swanctl/commands/redirect.c new file mode 100644 index 000000000..0afe96a1b --- /dev/null +++ b/src/swanctl/commands/redirect.c @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2015 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 . + * + * 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 "command.h" + +#include + +static int redirect(vici_conn_t *conn) +{ + vici_req_t *req; + vici_res_t *res; + command_format_options_t format = COMMAND_FORMAT_NONE; + char *arg, *peer_ip = NULL, *peer_id = NULL, *ike = NULL, *gateway = NULL; + int ret = 0, ike_id = 0; + + while (TRUE) + { + switch (command_getopt(&arg)) + { + case 'h': + return command_usage(NULL); + case 'P': + format |= COMMAND_FORMAT_PRETTY; + /* fall through to raw */ + case 'r': + format |= COMMAND_FORMAT_RAW; + continue; + case 'i': + ike = arg; + continue; + case 'I': + ike_id = atoi(arg); + continue; + case 'p': + peer_ip = arg; + continue; + case 'd': + peer_id = arg; + continue; + case 'g': + gateway = arg; + continue; + case EOF: + break; + default: + return command_usage("invalid --redirect option"); + } + break; + } + req = vici_begin("redirect"); + if (ike) + { + vici_add_key_valuef(req, "ike", "%s", ike); + } + if (ike_id) + { + vici_add_key_valuef(req, "ike-id", "%d", ike_id); + } + if (peer_ip) + { + vici_add_key_valuef(req, "peer-ip", "%s", peer_ip); + } + if (peer_id) + { + vici_add_key_valuef(req, "peer-id", "%s", peer_id); + } + if (gateway) + { + vici_add_key_valuef(req, "gateway", "%s", gateway); + } + res = vici_submit(req, conn); + if (!res) + { + ret = errno; + fprintf(stderr, "redirect request failed: %s\n", strerror(errno)); + return ret; + } + if (format & COMMAND_FORMAT_RAW) + { + vici_dump(res, "redirect reply", format & COMMAND_FORMAT_PRETTY, + stdout); + } + else + { + if (streq(vici_find_str(res, "no", "success"), "yes")) + { + printf("redirect completed successfully\n"); + } + else + { + fprintf(stderr, "redirect failed: %s\n", + vici_find_str(res, "", "errmsg")); + ret = 1; + } + } + vici_free_res(res); + return ret; +} + +/** + * Register the command. + */ +static void __attribute__ ((constructor))reg() +{ + command_register((command_t) { + redirect, 'd', "redirect", "redirect an IKE_SA", + {"--ike | --ike-id | --peer-ip ", + "--peer-id | --gateway [--raw|--pretty]"}, + { + {"help", 'h', 0, "show usage information"}, + {"ike", 'i', 1, "redirect by IKE_SA name"}, + {"ike-id", 'I', 1, "redirect by IKE_SA unique identifier"}, + {"peer-ip", 'p', 1, "redirect by client IP"}, + {"peer-id", 'd', 1, "redirect by IKE_SA name"}, + {"gateway", 'g', 1, "target gateway (IP or FQDN)"}, + {"raw", 'r', 0, "dump raw response message"}, + {"pretty", 'P', 0, "dump raw response message in pretty print"}, + } + }); +} diff --git a/src/swanctl/swanctl.8.in b/src/swanctl/swanctl.8.in index 4b49d3098..a3074601e 100644 --- a/src/swanctl/swanctl.8.in +++ b/src/swanctl/swanctl.8.in @@ -41,6 +41,10 @@ initiate a connection \-\-terminate\fR terminate a connection .TP +.B "\-d, \-\-redirect" +\-\-redirect\fR +redirect an IKE_SA +.TP .B "\-p, \-\-install" install a trap or shunt policy .TP From bef4518de7a5aed45bbb182abb169c16c63274dc Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 28 Apr 2015 18:33:31 +0200 Subject: [PATCH 26/36] vici: Match identity with wildcards against remote ID in redirect command --- src/libcharon/plugins/vici/README.md | 3 ++- src/libcharon/plugins/vici/vici_control.c | 11 +++++++---- src/swanctl/commands/redirect.c | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/libcharon/plugins/vici/README.md b/src/libcharon/plugins/vici/README.md index d08488310..54291b8bb 100644 --- a/src/libcharon/plugins/vici/README.md +++ b/src/libcharon/plugins/vici/README.md @@ -298,7 +298,8 @@ supported by the peer. ike = ike-id = peer-ip = - peer-id = + peer-id = } => { success = errmsg = diff --git a/src/libcharon/plugins/vici/vici_control.c b/src/libcharon/plugins/vici/vici_control.c index 7bcab0e86..a63caf0e1 100644 --- a/src/libcharon/plugins/vici/vici_control.c +++ b/src/libcharon/plugins/vici/vici_control.c @@ -366,7 +366,7 @@ CALLBACK(redirect, vici_message_t*, enumerator_t *sas; char *ike, *peer_ip, *peer_id, *gw, *errmsg = NULL; u_int ike_id, current, found = 0; - identification_t *gateway, *identity = NULL; + identification_t *gateway, *identity = NULL, *other_id; host_t *address = NULL; ike_sa_t *ike_sa; vici_builder_t *builder; @@ -445,10 +445,13 @@ CALLBACK(redirect, vici_message_t*, { continue; } - if (identity && - !identity->equals(identity, ike_sa->get_other_eap_id(ike_sa))) + if (identity) { - continue; + other_id = ike_sa->get_other_eap_id(ike_sa); + if (!other_id->matches(other_id, identity)) + { + continue; + } } lib->processor->queue_job(lib->processor, (job_t*)redirect_job_create(ike_sa->get_id(ike_sa), gateway)); diff --git a/src/swanctl/commands/redirect.c b/src/swanctl/commands/redirect.c index 0afe96a1b..295689bd3 100644 --- a/src/swanctl/commands/redirect.c +++ b/src/swanctl/commands/redirect.c @@ -117,7 +117,7 @@ static void __attribute__ ((constructor))reg() command_register((command_t) { redirect, 'd', "redirect", "redirect an IKE_SA", {"--ike | --ike-id | --peer-ip ", - "--peer-id | --gateway [--raw|--pretty]"}, + "--peer-id | --gateway [--raw|--pretty]"}, { {"help", 'h', 0, "show usage information"}, {"ike", 'i', 1, "redirect by IKE_SA name"}, From 27074f31557f15efc1dfaf1c1e02e4e66c8d63d0 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 30 Apr 2015 10:56:27 +0200 Subject: [PATCH 27/36] vici: Match subnets and ranges against peer IP in redirect command --- src/libcharon/plugins/vici/README.md | 3 +- src/libcharon/plugins/vici/vici_control.c | 51 ++++++++++++++++++----- src/swanctl/commands/redirect.c | 2 +- 3 files changed, 43 insertions(+), 13 deletions(-) diff --git a/src/libcharon/plugins/vici/README.md b/src/libcharon/plugins/vici/README.md index 54291b8bb..52929bd74 100644 --- a/src/libcharon/plugins/vici/README.md +++ b/src/libcharon/plugins/vici/README.md @@ -297,7 +297,8 @@ supported by the peer. { ike = ike-id = - peer-ip = + peer-ip = peer-id = } => { diff --git a/src/libcharon/plugins/vici/vici_control.c b/src/libcharon/plugins/vici/vici_control.c index a63caf0e1..d619a8028 100644 --- a/src/libcharon/plugins/vici/vici_control.c +++ b/src/libcharon/plugins/vici/vici_control.c @@ -360,6 +360,36 @@ CALLBACK(terminate, vici_message_t*, return builder->finalize(builder); } +/** + * Parse a peer-ip specified, which can be a subnet in CIDR notation, a range + * or a single IP address. + */ +static traffic_selector_t *parse_peer_ip(char *ip) +{ + traffic_selector_t *ts; + host_t *from, *to; + ts_type_t type; + + if (host_create_from_range(ip, &from, &to)) + { + if (to->get_family(to) == AF_INET) + { + type = TS_IPV4_ADDR_RANGE; + } + else + { + type = TS_IPV6_ADDR_RANGE; + } + ts = traffic_selector_create_from_bytes(0, type, + from->get_address(from), 0, + to->get_address(to), 0xFFFF); + from->destroy(from); + to->destroy(to); + return ts; + } + return traffic_selector_create_from_cidr(ip, 0, 0, 0xFFFF); +} + CALLBACK(redirect, vici_message_t*, private_vici_control_t *this, char *name, u_int id, vici_message_t *request) { @@ -367,7 +397,7 @@ CALLBACK(redirect, vici_message_t*, char *ike, *peer_ip, *peer_id, *gw, *errmsg = NULL; u_int ike_id, current, found = 0; identification_t *gateway, *identity = NULL, *other_id; - host_t *address = NULL; + traffic_selector_t *ts = NULL; ike_sa_t *ike_sa; vici_builder_t *builder; @@ -392,12 +422,12 @@ CALLBACK(redirect, vici_message_t*, } if (peer_ip) { - address = host_create_from_string(peer_ip, 0); - if (!address) + ts = parse_peer_ip(peer_ip); + if (!ts) { return send_reply(this, "invalid peer IP selector"); } - DBG1(DBG_CFG, "vici redirect IKE_SAs with src %H to %Y", address, + DBG1(DBG_CFG, "vici redirect IKE_SAs with src %R to %Y", ts, gateway); } if (peer_id) @@ -405,7 +435,7 @@ CALLBACK(redirect, vici_message_t*, identity = identification_create_from_string(peer_id); if (!identity) { - DESTROY_IF(address); + DESTROY_IF(ts); return send_reply(this, "invalid peer identity selector"); } DBG1(DBG_CFG, "vici redirect IKE_SAs with ID '%Y' to %Y", identity, @@ -413,15 +443,15 @@ CALLBACK(redirect, vici_message_t*, } if (ike_id) { - DBG1(DBG_CFG, "vici redirect IKE_SA #%d to %Y", ike_id, gateway); + DBG1(DBG_CFG, "vici redirect IKE_SA #%d to '%Y'", ike_id, gateway); } if (ike) { - DBG1(DBG_CFG, "vici redirect IKE_SA '%s' to %Y", ike, gateway); + DBG1(DBG_CFG, "vici redirect IKE_SA '%s' to '%Y'", ike, gateway); } if (!peer_ip && !peer_id && !ike && !ike_id) { - DBG1(DBG_CFG, "vici redirect all IKE_SAs to %Y", gateway); + DBG1(DBG_CFG, "vici redirect all IKE_SAs to '%Y'", gateway); } sas = charon->controller->create_ike_sa_enumerator(charon->controller, TRUE); @@ -440,8 +470,7 @@ CALLBACK(redirect, vici_message_t*, { continue; } - if (address && - !address->ip_equals(address, ike_sa->get_other_host(ike_sa))) + if (ts && !ts->includes(ts, ike_sa->get_other_host(ike_sa))) { continue; } @@ -471,7 +500,7 @@ CALLBACK(redirect, vici_message_t*, } gateway->destroy(gateway); DESTROY_IF(identity); - DESTROY_IF(address); + DESTROY_IF(ts); return builder->finalize(builder); } diff --git a/src/swanctl/commands/redirect.c b/src/swanctl/commands/redirect.c index 295689bd3..6edb936e6 100644 --- a/src/swanctl/commands/redirect.c +++ b/src/swanctl/commands/redirect.c @@ -116,7 +116,7 @@ static void __attribute__ ((constructor))reg() { command_register((command_t) { redirect, 'd', "redirect", "redirect an IKE_SA", - {"--ike | --ike-id | --peer-ip ", + {"--ike | --ike-id | --peer-ip ", "--peer-id | --gateway [--raw|--pretty]"}, { {"help", 'h', 0, "show usage information"}, From c13eb73719958e004cf8ae362f3801a3f3f243b0 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 12 May 2015 17:49:46 +0200 Subject: [PATCH 28/36] vici: Don't redirect all SAs if no selectors are given This avoid confusion and redirecting all SAs can now easily be done explicitly (e.g. peer_ip=0.0.0.0/0). --- src/libcharon/plugins/vici/vici_control.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcharon/plugins/vici/vici_control.c b/src/libcharon/plugins/vici/vici_control.c index d619a8028..c526d2fda 100644 --- a/src/libcharon/plugins/vici/vici_control.c +++ b/src/libcharon/plugins/vici/vici_control.c @@ -451,7 +451,7 @@ CALLBACK(redirect, vici_message_t*, } if (!peer_ip && !peer_id && !ike && !ike_id) { - DBG1(DBG_CFG, "vici redirect all IKE_SAs to '%Y'", gateway); + return send_reply(this, "missing redirect selector"); } sas = charon->controller->create_ike_sa_enumerator(charon->controller, TRUE); From 7505fb8d457983892b3cba049a4b6a8bae78b49d Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 30 Apr 2015 12:26:41 +0200 Subject: [PATCH 29/36] ike-sa: Reauthenticate to the same addresses we currently use If the SA got redirected this would otherwise cause a reauthentication with the original gateway. Reestablishing the SA to the original gateway, if e.g. the new gateway is not reachable makes sense though. --- src/libcharon/sa/ike_sa.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index 6884bf249..f5245417e 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -1950,8 +1950,11 @@ METHOD(ike_sa_t, reestablish, status_t, host = this->my_host; new->set_my_host(new, host->clone(host)); charon->bus->ike_reestablish_pre(charon->bus, &this->public, new); - /* resolve hosts but use the old addresses above as fallback */ - resolve_hosts((private_ike_sa_t*)new); + if (!has_condition(this, COND_REAUTHENTICATING)) + { /* reauthenticate to the same addresses, but resolve hosts if + * reestablishing (old addresses serve as fallback) */ + resolve_hosts((private_ike_sa_t*)new); + } /* if we already have a virtual IP, we reuse it */ enumerator = array_create_enumerator(this->my_vips); while (enumerator->enumerate(enumerator, &host)) From c6ebd0332ec01ab50b4046874d111c942fc59b55 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 30 Apr 2015 12:44:56 +0200 Subject: [PATCH 30/36] ike-sa: Add limit for the number of redirects within a defined time period --- src/libcharon/sa/ike_sa.c | 44 +++++++++++++++++++++++++++++++++++++++ src/libcharon/sa/ike_sa.h | 10 +++++++++ 2 files changed, 54 insertions(+) diff --git a/src/libcharon/sa/ike_sa.c b/src/libcharon/sa/ike_sa.c index f5245417e..b07ff0e74 100644 --- a/src/libcharon/sa/ike_sa.c +++ b/src/libcharon/sa/ike_sa.c @@ -294,6 +294,11 @@ struct private_ike_sa_t { * Original gateway address from which we got redirected */ host_t *redirected_from; + + /** + * Timestamps of redirect attempts to handle loops + */ + array_t *redirected_at; }; /** @@ -2020,6 +2025,7 @@ static bool redirect_established(private_ike_sa_t *this, identification_t *to) private_ike_sa_t *new_priv; ike_sa_t *new; host_t *other; + time_t redirect; new = charon->ike_sa_manager->checkout_new(charon->ike_sa_manager, this->version, TRUE); @@ -2039,6 +2045,11 @@ static bool redirect_established(private_ike_sa_t *this, identification_t *to) * resolve the local address */ new_priv->remote_host = other; resolve_hosts(new_priv); + new_priv->redirected_at = array_create(sizeof(time_t), MAX_REDIRECTS); + while (array_remove(this->redirected_at, ARRAY_HEAD, &redirect)) + { + array_insert(new_priv->redirected_at, ARRAY_TAIL, &redirect); + } if (reestablish_children(this, new, TRUE) != DESTROY_ME) { #ifdef USE_IKEV2 @@ -2082,6 +2093,32 @@ static bool redirect_connecting(private_ike_sa_t *this, identification_t *to) return TRUE; } +/** + * Check if the current redirect exceeds the limits for redirects + */ +static bool redirect_count_exceeded(private_ike_sa_t *this) +{ + time_t now, redirect; + + now = time_monotonic(NULL); + /* remove entries outside the defined period */ + while (array_get(this->redirected_at, ARRAY_HEAD, &redirect) && + now - redirect >= REDIRECT_LOOP_DETECT_PERIOD) + { + array_remove(this->redirected_at, ARRAY_HEAD, NULL); + } + if (array_count(this->redirected_at) < MAX_REDIRECTS) + { + if (!this->redirected_at) + { + this->redirected_at = array_create(sizeof(time_t), MAX_REDIRECTS); + } + array_insert(this->redirected_at, ARRAY_TAIL, &now); + return FALSE; + } + return TRUE; +} + METHOD(ike_sa_t, handle_redirect, bool, private_ike_sa_t *this, identification_t *gateway) { @@ -2091,6 +2128,12 @@ METHOD(ike_sa_t, handle_redirect, bool, DBG1(DBG_IKE, "server sent REDIRECT even though we disabled it"); return FALSE; } + if (redirect_count_exceeded(this)) + { + DBG1(DBG_IKE, "only %d redirects are allowed within %d seconds", + MAX_REDIRECTS, REDIRECT_LOOP_DETECT_PERIOD); + return FALSE; + } switch (this->state) { @@ -2658,6 +2701,7 @@ METHOD(ike_sa_t, destroy, void, DESTROY_IF(this->local_host); DESTROY_IF(this->remote_host); DESTROY_IF(this->redirected_from); + array_destroy(this->redirected_at); DESTROY_IF(this->ike_cfg); DESTROY_IF(this->peer_cfg); diff --git a/src/libcharon/sa/ike_sa.h b/src/libcharon/sa/ike_sa.h index 08c19971f..158a690df 100644 --- a/src/libcharon/sa/ike_sa.h +++ b/src/libcharon/sa/ike_sa.h @@ -65,6 +65,16 @@ typedef struct ike_sa_t ike_sa_t; */ #define RETRY_JITTER 20 +/** + * Number of redirects allowed within REDIRECT_LOOP_DETECT_PERIOD. + */ +#define MAX_REDIRECTS 5 + +/** + * Time period in seconds in which at most MAX_REDIRECTS are allowed. + */ +#define REDIRECT_LOOP_DETECT_PERIOD 300 + /** * Extensions (or optional features) the peer supports */ From 9282bc39a7bbd0e6a00e24539b1d21565646bb97 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 30 Apr 2015 12:57:19 +0200 Subject: [PATCH 31/36] ike-init: Ignore notifies related to redirects during rekeying Also don't query redirect providers in this case. --- src/libcharon/sa/ikev2/tasks/ike_init.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/libcharon/sa/ikev2/tasks/ike_init.c b/src/libcharon/sa/ikev2/tasks/ike_init.c index b5a58dfd7..572c997cb 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_init.c +++ b/src/libcharon/sa/ikev2/tasks/ike_init.c @@ -437,8 +437,11 @@ static void process_payloads(private_ike_init_t *this, message_t *message) /* fall-through */ } case REDIRECT_SUPPORTED: - this->ike_sa->enable_extension(this->ike_sa, - EXT_IKE_REDIRECTION); + if (!this->old_sa) + { + this->ike_sa->enable_extension(this->ike_sa, + EXT_IKE_REDIRECTION); + } break; default: /* other notifies are handled elsewhere */ @@ -612,7 +615,8 @@ METHOD(task_t, build_r, status_t, this->ike_sa->set_proposal(this->ike_sa, this->proposal); /* check if we'd have to redirect the client */ - if (this->ike_sa->supports_extension(this->ike_sa, EXT_IKE_REDIRECTION) && + if (!this->old_sa && + this->ike_sa->supports_extension(this->ike_sa, EXT_IKE_REDIRECTION) && charon->redirect->redirect_on_init(charon->redirect, this->ike_sa, &gateway)) { @@ -750,6 +754,12 @@ METHOD(task_t, process_i, status_t, chunk_t data, nonce = chunk_empty; status_t status = FAILED; + if (this->old_sa) + { + DBG1(DBG_IKE, "received REDIRECT notify during rekeying" + ", ignored"); + break; + } data = notify->get_notification_data(notify); gateway = redirect_data_parse(data, &nonce); enumerator->destroy(enumerator); From f80e910cceed2a80b73cecd6142afc0aa00c0888 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 12 May 2015 16:53:04 +0200 Subject: [PATCH 32/36] testing: Add ikev2/redirect-active scenario --- .../ikev2/redirect-active/description.txt | 8 +++++ .../tests/ikev2/redirect-active/evaltest.dat | 28 +++++++++++++++ .../hosts/alice/etc/ipsec.conf | 19 +++++++++++ .../alice/etc/ipsec.d/certs/marsCert.pem | 25 ++++++++++++++ .../alice/etc/ipsec.d/private/marsKey.pem | 27 +++++++++++++++ .../hosts/alice/etc/ipsec.secrets | 3 ++ .../hosts/alice/etc/iptables.rules | 34 +++++++++++++++++++ .../hosts/alice/etc/strongswan.conf | 6 ++++ .../hosts/carol/etc/ipsec.conf | 19 +++++++++++ .../hosts/carol/etc/strongswan.conf | 5 +++ .../redirect-active/hosts/dave/etc/ipsec.conf | 19 +++++++++++ .../hosts/dave/etc/strongswan.conf | 6 ++++ .../redirect-active/hosts/moon/etc/ipsec.conf | 18 ++++++++++ .../hosts/moon/etc/ipsec.d/certs/marsCert.pem | 25 ++++++++++++++ .../moon/etc/ipsec.d/private/marsKey.pem | 27 +++++++++++++++ .../hosts/moon/etc/ipsec.secrets | 3 ++ .../hosts/moon/etc/strongswan.conf | 6 ++++ .../tests/ikev2/redirect-active/posttest.dat | 10 ++++++ .../tests/ikev2/redirect-active/pretest.dat | 13 +++++++ testing/tests/ikev2/redirect-active/test.conf | 21 ++++++++++++ 20 files changed, 322 insertions(+) create mode 100644 testing/tests/ikev2/redirect-active/description.txt create mode 100644 testing/tests/ikev2/redirect-active/evaltest.dat create mode 100644 testing/tests/ikev2/redirect-active/hosts/alice/etc/ipsec.conf create mode 100644 testing/tests/ikev2/redirect-active/hosts/alice/etc/ipsec.d/certs/marsCert.pem create mode 100644 testing/tests/ikev2/redirect-active/hosts/alice/etc/ipsec.d/private/marsKey.pem create mode 100644 testing/tests/ikev2/redirect-active/hosts/alice/etc/ipsec.secrets create mode 100644 testing/tests/ikev2/redirect-active/hosts/alice/etc/iptables.rules create mode 100644 testing/tests/ikev2/redirect-active/hosts/alice/etc/strongswan.conf create mode 100644 testing/tests/ikev2/redirect-active/hosts/carol/etc/ipsec.conf create mode 100644 testing/tests/ikev2/redirect-active/hosts/carol/etc/strongswan.conf create mode 100644 testing/tests/ikev2/redirect-active/hosts/dave/etc/ipsec.conf create mode 100644 testing/tests/ikev2/redirect-active/hosts/dave/etc/strongswan.conf create mode 100644 testing/tests/ikev2/redirect-active/hosts/moon/etc/ipsec.conf create mode 100644 testing/tests/ikev2/redirect-active/hosts/moon/etc/ipsec.d/certs/marsCert.pem create mode 100644 testing/tests/ikev2/redirect-active/hosts/moon/etc/ipsec.d/private/marsKey.pem create mode 100644 testing/tests/ikev2/redirect-active/hosts/moon/etc/ipsec.secrets create mode 100644 testing/tests/ikev2/redirect-active/hosts/moon/etc/strongswan.conf create mode 100644 testing/tests/ikev2/redirect-active/posttest.dat create mode 100644 testing/tests/ikev2/redirect-active/pretest.dat create mode 100644 testing/tests/ikev2/redirect-active/test.conf diff --git a/testing/tests/ikev2/redirect-active/description.txt b/testing/tests/ikev2/redirect-active/description.txt new file mode 100644 index 000000000..942c66ebf --- /dev/null +++ b/testing/tests/ikev2/redirect-active/description.txt @@ -0,0 +1,8 @@ +The roadwarriors carol and dave set up a connection each +to gateway alice, which identifies itself as mars. +Host venus in the subnet behind this gateway uses gateway moon as +default gateway, so it is not fully reachable by the two roadwarriors. +Then both SAs are actively redirected with the swanctl --redirect +command to gateway moon (also identified as mars) using the +mechanism described in RFC 5685 enabling the roadwarriors to +ping venus. diff --git a/testing/tests/ikev2/redirect-active/evaltest.dat b/testing/tests/ikev2/redirect-active/evaltest.dat new file mode 100644 index 000000000..6d84173cd --- /dev/null +++ b/testing/tests/ikev2/redirect-active/evaltest.dat @@ -0,0 +1,28 @@ +alice::ipsec status 2> /dev/null::rw\[1].*ESTABLISHED.*mars.strongswan.org.*carol@strongswan.org::YES +alice::ipsec status 2> /dev/null::rw\[2].*ESTABLISHED.*mars.strongswan.org.*dave@strongswan.org::YES +carol::ipsec status 2> /dev/null::home.*ESTABLISHED.*carol@strongswan.org.*mars.strongswan.org::YES +dave:: ipsec status 2> /dev/null::home.*ESTABLISHED.*dave@strongswan.org.*mars.strongswan.org::YES +carol::ping -c 1 -W 1 PH_IP_VENUS::64 bytes from PH_IP_VENUS: icmp_req=1::NO +dave:: ping -c 1 -W 1 PH_IP_VENUS::64 bytes from PH_IP_VENUS: icmp_req=1::NO +alice::swanctl --redirect --peer-ip PH_IP_CAROL --gateway 192.168.0.1::redirect completed successfully::YES +alice::swanctl --redirect --peer-id dave@strongswan.org --gateway moon.strongswan.org::redirect completed successfully::YES +carol::sleep 1::No output expected::NO +carol::cat /var/log/daemon.log::redirected to 192.168.0.1::YES +dave::cat /var/log/daemon.log::redirected to moon.strongswan.org::YES +moon::cat /var/log/daemon.log::client got redirected from 192.168.0.5::YES +moon::ipsec status 2> /dev/null::rw\[1].*ESTABLISHED.*mars.strongswan.org.*carol@strongswan.org::YES +moon::ipsec status 2> /dev/null::rw\[2].*ESTABLISHED.*mars.strongswan.org.*dave@strongswan.org::YES +carol::ping -c 1 PH_IP_VENUS::64 bytes from PH_IP_VENUS: icmp_req=1::YES +dave:: ping -c 1 PH_IP_VENUS::64 bytes from PH_IP_VENUS: icmp_req=1::YES +carol::tcpdump::IP carol.strongswan.org > mars.strongswan.org: ESP::YES +carol::tcpdump::IP mars.strongswan.org > carol.strongswan.org: ESP::NO +carol::tcpdump::IP carol.strongswan.org > moon.strongswan.org: ESP::YES +carol::tcpdump::IP moon.strongswan.org > carol.strongswan.org: ESP::YES +dave::tcpdump::IP dave.strongswan.org > mars.strongswan.org: ESP::YES +dave::tcpdump::IP mars.strongswan.org > dave.strongswan.org: ESP::NO +dave::tcpdump::IP dave.strongswan.org > moon.strongswan.org: ESP::YES +dave::tcpdump::IP moon.strongswan.org > dave.strongswan.org: ESP::YES +venus::tcpdump::IP carol.strongswan.org > venus.strongswan.org: ICMP echo request::YES +venus::tcpdump::IP venus.strongswan.org > carol.strongswan.org: ICMP echo reply::YES +venus::tcpdump::IP dave.strongswan.org > venus.strongswan.org: ICMP echo request::YES +venus::tcpdump::IP venus.strongswan.org > dave.strongswan.org: ICMP echo reply::YES diff --git a/testing/tests/ikev2/redirect-active/hosts/alice/etc/ipsec.conf b/testing/tests/ikev2/redirect-active/hosts/alice/etc/ipsec.conf new file mode 100644 index 000000000..6a810552b --- /dev/null +++ b/testing/tests/ikev2/redirect-active/hosts/alice/etc/ipsec.conf @@ -0,0 +1,19 @@ +# /etc/ipsec.conf - strongSwan IPsec configuration file + +config setup + +conn %default + ikelifetime=60m + keylife=20m + rekeymargin=3m + keyingtries=1 + +conn rw + left=192.168.0.5 + leftcert=marsCert.pem + leftid=mars.strongswan.org + leftsubnet=10.1.0.0/16 + leftfirewall=yes + right=%any + keyexchange=ikev2 + auto=add diff --git a/testing/tests/ikev2/redirect-active/hosts/alice/etc/ipsec.d/certs/marsCert.pem b/testing/tests/ikev2/redirect-active/hosts/alice/etc/ipsec.d/certs/marsCert.pem new file mode 100644 index 000000000..cf0977489 --- /dev/null +++ b/testing/tests/ikev2/redirect-active/hosts/alice/etc/ipsec.d/certs/marsCert.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEQDCCAyigAwIBAgIBNDANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJDSDEZ +MBcGA1UEChMQTGludXggc3Ryb25nU3dhbjEbMBkGA1UEAxMSc3Ryb25nU3dhbiBS +b290IENBMB4XDTE1MTEyNjA4MjQ1M1oXDTE5MDkwNjA4MjQ1M1owZDELMAkGA1UE +BhMCQ0gxGTAXBgNVBAoTEExpbnV4IHN0cm9uZ1N3YW4xHDAaBgNVBAsTE1ZpcnR1 +YWwgVlBOIEdhdGV3YXkxHDAaBgNVBAMTE21hcnMuc3Ryb25nc3dhbi5vcmcwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCxTvcinIErfzu8YZKl+a2MNTVY ++qjG83OkweWjWed/egAugxRjTyWeKjrSNf6DWpXC0pNhzja+Sj0njz4Kq8VczkFB +/yYEkFKpdpFQHXvbQo5h7wn1i3Omhvou5x0rRfddRpLGl7Z/Xg8gnHsZMHHN60hI +qiLZdRSu5QF25WNcy9tO58gz8Hvo5jsk8YDu6eGYolaygQo3cqxx/r8yWzehju0F +oWm4sLOrq+OTegeCQYI8PsOALAwLWgWdGfMPMT0ntq9tD97AcUD0EilvcXVdmwUW +44/hM9BZ0JS7N7Wv6fgIChC7xhZj1yo6vY+FDsmNMhz7vdeulKx2dqRJkn0XAgMB +AAGjggEaMIIBFjAJBgNVHRMEAjAAMAsGA1UdDwQEAwIDqDAdBgNVHQ4EFgQUvo44 +06FQwLI8P6VqNehTNHa2LNMwbQYDVR0jBGYwZIAUXafdcAZRMn7ntm2zteXgYOou +Te+hSaRHMEUxCzAJBgNVBAYTAkNIMRkwFwYDVQQKExBMaW51eCBzdHJvbmdTd2Fu +MRswGQYDVQQDExJzdHJvbmdTd2FuIFJvb3QgQ0GCAQAwHgYDVR0RBBcwFYITbWFy +cy5zdHJvbmdzd2FuLm9yZzATBgNVHSUEDDAKBggrBgEFBQcDATA5BgNVHR8EMjAw +MC6gLKAqhihodHRwOi8vY3JsLnN0cm9uZ3N3YW4ub3JnL3N0cm9uZ3N3YW4uY3Js +MA0GCSqGSIb3DQEBCwUAA4IBAQCWvHc0v5BhxsLRxPvcOu1bVoeexzA/5nftfRly +PumU2hYFQ8p7soJiT/jYPmnfNvl6halnmS0FrqLdTMNfXj/6WCZ3w4Xnokc5PYW4 +oL1VqdEgR7jyQjifZuzRkuPw+yAFoT+HbMttLdRMVTq9NXlPXtGaJTWbx5Z/gGP3 +1Ry47WhvzJyRSysPh/+9WJFOwS6nf6DFbNuyVtNXQRzXjpsGefZgN9ysntNRFMMZ +V0j+zFIkLBLh2WBkJsuLQM9EFAlZrpQZ7iwHuz7gU0jATxN6Qxnn9AemNPl4k8T3 +igPXeVhDbZFeTc+u/Z7X5i1HXhks2QGz/zGee6no54XOnBWB +-----END CERTIFICATE----- diff --git a/testing/tests/ikev2/redirect-active/hosts/alice/etc/ipsec.d/private/marsKey.pem b/testing/tests/ikev2/redirect-active/hosts/alice/etc/ipsec.d/private/marsKey.pem new file mode 100644 index 000000000..14f9440ee --- /dev/null +++ b/testing/tests/ikev2/redirect-active/hosts/alice/etc/ipsec.d/private/marsKey.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAsU73IpyBK387vGGSpfmtjDU1WPqoxvNzpMHlo1nnf3oALoMU +Y08lnio60jX+g1qVwtKTYc42vko9J48+CqvFXM5BQf8mBJBSqXaRUB1720KOYe8J +9Ytzpob6LucdK0X3XUaSxpe2f14PIJx7GTBxzetISKoi2XUUruUBduVjXMvbTufI +M/B76OY7JPGA7unhmKJWsoEKN3Kscf6/Mls3oY7tBaFpuLCzq6vjk3oHgkGCPD7D +gCwMC1oFnRnzDzE9J7avbQ/ewHFA9BIpb3F1XZsFFuOP4TPQWdCUuze1r+n4CAoQ +u8YWY9cqOr2PhQ7JjTIc+73XrpSsdnakSZJ9FwIDAQABAoIBAQCe+TYMClamMUXV ++YYK2noIJAzY4qVA/DbLmi3S8GDnovNWwKAfCm+oCCUFJPQ1wZpVO4h5QPNEWjvK +pFv0JV6nfyZzjdkAboYu+uf+9PB//oS6uiAr0AS5YnweYYl9qxXGw+gFtzEhsavO +iX61bwIapsIApMrqPld1GE7f03vrTjyqYeJC+V5WEVZe1fkZtKFFJiHN1boPwoWt +VRQ4UGlq9eto5pddmMcPl/uR9/TItdMC8AZ7fcRYP9RdYFEp8jS+tx3CDNs0XrlF +qKlroDSdwBt475Ud5irtNwjGoohl3xCHvEne9l+fjlmQFynfRNeOb9rgVIsT3OoO +MFSeW9HBAoGBAOiAjYCWoQHSlbi3Nkd5fsfTd4iZOkeeZi+2PJOhDByH1Hsq87CP +KoYejjYUQ6QvJY2WDJCU8qXxPJkDuI6XZ6QbTDPad2xXDkA4pxuPi/c+Hb/eqLQ+ +24ebIzauC5iLdddp22oQXQOngKwUMihYggKs9cIpem8G99bcMfq1OeaDAoGBAMM6 +Z790eySPDIaKZZ/KVOu4hJe1s5vIJqN9xtV0o55PTR2wFCgwCogiXQchyhF8Qo6q +BEpcKpY1bAivmL8T2PBsLUuKC4LAr8uzW8rVFbG3HfC62RoaVOZWmH+yBABi6eej +Lh8KNhal+HzVOlMsw78FAkhq9aubWBywxfULFyrdAoGAPmnsEn3dIPkf+iMsrgk+ +cHQjHYdMrtclxV8daeAMuUf5UUsZeeck2jYvpPLRqayCXzzNiK6DaOqWgtkN3TFl +wBcX4L+f2XSRPco3id2CDFAfeldSYHiTRBbwYAhe0od1jes73IAiCA4aK5eMU2pX +FFLL8mBiHpX3SDV6MTwfG/MCgYEApgLXUC3LvVXLL+kl65ARUHUSgzNmrNSxTptY +5hCORcNnbxRYv6fCfOrDspngC6hfrJMUnOrs59g7qtMvD2TgN+m1RqP3j9Msagp1 +xvAVx45QbnFkxdaYVVpTyVpmHrH99xSC1rBHOFEgEX78XtgVCC1JOK2DhM7PSW5k +lzA8Hh0CgYBVXNnQsy4x+9D09zeFP2TKXsk7N154yUP6oxXu6OjZC+cfxHOGyi/g +PVe8ukBIknZkFRz+ZXdfZzXXuJslENOVeFEInqVAu2W55KeCPc/3SU6hB2lj6MBl +WIZ7yrUmtdalU3cPkFnHkQTL2glYAzjB2z22AxyXNzy37ZhtFpovtg== +-----END RSA PRIVATE KEY----- diff --git a/testing/tests/ikev2/redirect-active/hosts/alice/etc/ipsec.secrets b/testing/tests/ikev2/redirect-active/hosts/alice/etc/ipsec.secrets new file mode 100644 index 000000000..d65b96e34 --- /dev/null +++ b/testing/tests/ikev2/redirect-active/hosts/alice/etc/ipsec.secrets @@ -0,0 +1,3 @@ +# /etc/ipsec.secrets - strongSwan IPsec secrets file + +: RSA marsKey.pem diff --git a/testing/tests/ikev2/redirect-active/hosts/alice/etc/iptables.rules b/testing/tests/ikev2/redirect-active/hosts/alice/etc/iptables.rules new file mode 100644 index 000000000..33c22b909 --- /dev/null +++ b/testing/tests/ikev2/redirect-active/hosts/alice/etc/iptables.rules @@ -0,0 +1,34 @@ +*filter + +# default policy is DROP +-P INPUT DROP +-P OUTPUT DROP +-P FORWARD DROP + +# forward ESP-tunneled traffic +-A FORWARD -i eth1 -m policy --dir in --pol ipsec --proto esp -s PH_IP_CAROL -j ACCEPT +-A FORWARD -i eth1 -m policy --dir in --pol ipsec --proto esp -s PH_IP_DAVE -j ACCEPT +-A FORWARD -o eth1 -m policy --dir out --pol ipsec --proto esp -j ACCEPT + +# allow esp +-A INPUT -p 50 -j ACCEPT +-A OUTPUT -p 50 -d PH_IP_CAROL -j ACCEPT +-A OUTPUT -p 50 -d PH_IP_DAVE -j ACCEPT + +# allow IKE +-A INPUT -i eth1 -p udp --sport 500 --dport 500 -j ACCEPT +-A OUTPUT -o eth1 -p udp --dport 500 --sport 500 -j ACCEPT + +# allow MobIKE +-A INPUT -i eth1 -p udp --sport 4500 --dport 4500 -j ACCEPT +-A OUTPUT -o eth1 -p udp --dport 4500 --sport 4500 -j ACCEPT + +# allow crl fetch from winnetou +-A INPUT -i eth1 -p tcp --sport 80 -s PH_IP_WINNETOU -j ACCEPT +-A OUTPUT -o eth1 -p tcp --dport 80 -d PH_IP_WINNETOU -j ACCEPT + +# allow ssh +-A INPUT -p tcp --dport 22 -j ACCEPT +-A OUTPUT -p tcp --sport 22 -j ACCEPT + +COMMIT diff --git a/testing/tests/ikev2/redirect-active/hosts/alice/etc/strongswan.conf b/testing/tests/ikev2/redirect-active/hosts/alice/etc/strongswan.conf new file mode 100644 index 000000000..6f7f4c4cb --- /dev/null +++ b/testing/tests/ikev2/redirect-active/hosts/alice/etc/strongswan.conf @@ -0,0 +1,6 @@ +# /etc/strongswan.conf - strongSwan configuration file + +charon { + load = aes des sha1 sha2 md5 pem pkcs1 gmp random nonce x509 curl revocation hmac stroke kernel-netlink socket-default updown vici +} + diff --git a/testing/tests/ikev2/redirect-active/hosts/carol/etc/ipsec.conf b/testing/tests/ikev2/redirect-active/hosts/carol/etc/ipsec.conf new file mode 100644 index 000000000..a5c2cc5fc --- /dev/null +++ b/testing/tests/ikev2/redirect-active/hosts/carol/etc/ipsec.conf @@ -0,0 +1,19 @@ +# /etc/ipsec.conf - strongSwan IPsec configuration file + +config setup + charondebug="knl 2" +conn %default + ikelifetime=60m + keylife=20m + rekeymargin=3m + keyingtries=1 + +conn home + leftcert=carolCert.pem + leftid=carol@strongswan.org + leftfirewall=yes + right=192.168.0.5 + rightid=mars.strongswan.org + rightsubnet=10.1.0.0/16 + keyexchange=ikev2 + auto=add diff --git a/testing/tests/ikev2/redirect-active/hosts/carol/etc/strongswan.conf b/testing/tests/ikev2/redirect-active/hosts/carol/etc/strongswan.conf new file mode 100644 index 000000000..e58af9efd --- /dev/null +++ b/testing/tests/ikev2/redirect-active/hosts/carol/etc/strongswan.conf @@ -0,0 +1,5 @@ +# /etc/strongswan.conf - strongSwan configuration file + +charon { + load = aes des sha1 sha2 md5 pem pkcs1 gmp random nonce x509 curl revocation hmac xcbc ctr ccm gcm stroke kernel-netlink socket-default updown +} diff --git a/testing/tests/ikev2/redirect-active/hosts/dave/etc/ipsec.conf b/testing/tests/ikev2/redirect-active/hosts/dave/etc/ipsec.conf new file mode 100644 index 000000000..642811d10 --- /dev/null +++ b/testing/tests/ikev2/redirect-active/hosts/dave/etc/ipsec.conf @@ -0,0 +1,19 @@ +# /etc/ipsec.conf - strongSwan IPsec configuration file + +config setup + +conn %default + ikelifetime=60m + keylife=20m + rekeymargin=3m + keyingtries=1 + +conn home + leftcert=daveCert.pem + leftid=dave@strongswan.org + leftfirewall=yes + right=192.168.0.5 + rightid=mars.strongswan.org + rightsubnet=10.1.0.0/16 + keyexchange=ikev2 + auto=add diff --git a/testing/tests/ikev2/redirect-active/hosts/dave/etc/strongswan.conf b/testing/tests/ikev2/redirect-active/hosts/dave/etc/strongswan.conf new file mode 100644 index 000000000..ecbad665c --- /dev/null +++ b/testing/tests/ikev2/redirect-active/hosts/dave/etc/strongswan.conf @@ -0,0 +1,6 @@ +# /etc/strongswan.conf - strongSwan configuration file + +charon { + load = aes des sha1 sha2 md5 pem pkcs1 gmp random nonce x509 curl revocation hmac xcbc ctr ccm gcm stroke kernel-netlink socket-default updown +} + diff --git a/testing/tests/ikev2/redirect-active/hosts/moon/etc/ipsec.conf b/testing/tests/ikev2/redirect-active/hosts/moon/etc/ipsec.conf new file mode 100644 index 000000000..fa901cbdf --- /dev/null +++ b/testing/tests/ikev2/redirect-active/hosts/moon/etc/ipsec.conf @@ -0,0 +1,18 @@ +# /etc/ipsec.conf - strongSwan IPsec configuration file + +config setup + +conn %default + ikelifetime=60m + keylife=20m + rekeymargin=3m + keyingtries=1 + +conn rw + leftcert=marsCert.pem + leftid=mars.strongswan.org + leftsubnet=10.1.0.0/16 + leftfirewall=yes + right=%any + keyexchange=ikev2 + auto=add diff --git a/testing/tests/ikev2/redirect-active/hosts/moon/etc/ipsec.d/certs/marsCert.pem b/testing/tests/ikev2/redirect-active/hosts/moon/etc/ipsec.d/certs/marsCert.pem new file mode 100644 index 000000000..cf0977489 --- /dev/null +++ b/testing/tests/ikev2/redirect-active/hosts/moon/etc/ipsec.d/certs/marsCert.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEQDCCAyigAwIBAgIBNDANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJDSDEZ +MBcGA1UEChMQTGludXggc3Ryb25nU3dhbjEbMBkGA1UEAxMSc3Ryb25nU3dhbiBS +b290IENBMB4XDTE1MTEyNjA4MjQ1M1oXDTE5MDkwNjA4MjQ1M1owZDELMAkGA1UE +BhMCQ0gxGTAXBgNVBAoTEExpbnV4IHN0cm9uZ1N3YW4xHDAaBgNVBAsTE1ZpcnR1 +YWwgVlBOIEdhdGV3YXkxHDAaBgNVBAMTE21hcnMuc3Ryb25nc3dhbi5vcmcwggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCxTvcinIErfzu8YZKl+a2MNTVY ++qjG83OkweWjWed/egAugxRjTyWeKjrSNf6DWpXC0pNhzja+Sj0njz4Kq8VczkFB +/yYEkFKpdpFQHXvbQo5h7wn1i3Omhvou5x0rRfddRpLGl7Z/Xg8gnHsZMHHN60hI +qiLZdRSu5QF25WNcy9tO58gz8Hvo5jsk8YDu6eGYolaygQo3cqxx/r8yWzehju0F +oWm4sLOrq+OTegeCQYI8PsOALAwLWgWdGfMPMT0ntq9tD97AcUD0EilvcXVdmwUW +44/hM9BZ0JS7N7Wv6fgIChC7xhZj1yo6vY+FDsmNMhz7vdeulKx2dqRJkn0XAgMB +AAGjggEaMIIBFjAJBgNVHRMEAjAAMAsGA1UdDwQEAwIDqDAdBgNVHQ4EFgQUvo44 +06FQwLI8P6VqNehTNHa2LNMwbQYDVR0jBGYwZIAUXafdcAZRMn7ntm2zteXgYOou +Te+hSaRHMEUxCzAJBgNVBAYTAkNIMRkwFwYDVQQKExBMaW51eCBzdHJvbmdTd2Fu +MRswGQYDVQQDExJzdHJvbmdTd2FuIFJvb3QgQ0GCAQAwHgYDVR0RBBcwFYITbWFy +cy5zdHJvbmdzd2FuLm9yZzATBgNVHSUEDDAKBggrBgEFBQcDATA5BgNVHR8EMjAw +MC6gLKAqhihodHRwOi8vY3JsLnN0cm9uZ3N3YW4ub3JnL3N0cm9uZ3N3YW4uY3Js +MA0GCSqGSIb3DQEBCwUAA4IBAQCWvHc0v5BhxsLRxPvcOu1bVoeexzA/5nftfRly +PumU2hYFQ8p7soJiT/jYPmnfNvl6halnmS0FrqLdTMNfXj/6WCZ3w4Xnokc5PYW4 +oL1VqdEgR7jyQjifZuzRkuPw+yAFoT+HbMttLdRMVTq9NXlPXtGaJTWbx5Z/gGP3 +1Ry47WhvzJyRSysPh/+9WJFOwS6nf6DFbNuyVtNXQRzXjpsGefZgN9ysntNRFMMZ +V0j+zFIkLBLh2WBkJsuLQM9EFAlZrpQZ7iwHuz7gU0jATxN6Qxnn9AemNPl4k8T3 +igPXeVhDbZFeTc+u/Z7X5i1HXhks2QGz/zGee6no54XOnBWB +-----END CERTIFICATE----- diff --git a/testing/tests/ikev2/redirect-active/hosts/moon/etc/ipsec.d/private/marsKey.pem b/testing/tests/ikev2/redirect-active/hosts/moon/etc/ipsec.d/private/marsKey.pem new file mode 100644 index 000000000..14f9440ee --- /dev/null +++ b/testing/tests/ikev2/redirect-active/hosts/moon/etc/ipsec.d/private/marsKey.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAsU73IpyBK387vGGSpfmtjDU1WPqoxvNzpMHlo1nnf3oALoMU +Y08lnio60jX+g1qVwtKTYc42vko9J48+CqvFXM5BQf8mBJBSqXaRUB1720KOYe8J +9Ytzpob6LucdK0X3XUaSxpe2f14PIJx7GTBxzetISKoi2XUUruUBduVjXMvbTufI +M/B76OY7JPGA7unhmKJWsoEKN3Kscf6/Mls3oY7tBaFpuLCzq6vjk3oHgkGCPD7D +gCwMC1oFnRnzDzE9J7avbQ/ewHFA9BIpb3F1XZsFFuOP4TPQWdCUuze1r+n4CAoQ +u8YWY9cqOr2PhQ7JjTIc+73XrpSsdnakSZJ9FwIDAQABAoIBAQCe+TYMClamMUXV ++YYK2noIJAzY4qVA/DbLmi3S8GDnovNWwKAfCm+oCCUFJPQ1wZpVO4h5QPNEWjvK +pFv0JV6nfyZzjdkAboYu+uf+9PB//oS6uiAr0AS5YnweYYl9qxXGw+gFtzEhsavO +iX61bwIapsIApMrqPld1GE7f03vrTjyqYeJC+V5WEVZe1fkZtKFFJiHN1boPwoWt +VRQ4UGlq9eto5pddmMcPl/uR9/TItdMC8AZ7fcRYP9RdYFEp8jS+tx3CDNs0XrlF +qKlroDSdwBt475Ud5irtNwjGoohl3xCHvEne9l+fjlmQFynfRNeOb9rgVIsT3OoO +MFSeW9HBAoGBAOiAjYCWoQHSlbi3Nkd5fsfTd4iZOkeeZi+2PJOhDByH1Hsq87CP +KoYejjYUQ6QvJY2WDJCU8qXxPJkDuI6XZ6QbTDPad2xXDkA4pxuPi/c+Hb/eqLQ+ +24ebIzauC5iLdddp22oQXQOngKwUMihYggKs9cIpem8G99bcMfq1OeaDAoGBAMM6 +Z790eySPDIaKZZ/KVOu4hJe1s5vIJqN9xtV0o55PTR2wFCgwCogiXQchyhF8Qo6q +BEpcKpY1bAivmL8T2PBsLUuKC4LAr8uzW8rVFbG3HfC62RoaVOZWmH+yBABi6eej +Lh8KNhal+HzVOlMsw78FAkhq9aubWBywxfULFyrdAoGAPmnsEn3dIPkf+iMsrgk+ +cHQjHYdMrtclxV8daeAMuUf5UUsZeeck2jYvpPLRqayCXzzNiK6DaOqWgtkN3TFl +wBcX4L+f2XSRPco3id2CDFAfeldSYHiTRBbwYAhe0od1jes73IAiCA4aK5eMU2pX +FFLL8mBiHpX3SDV6MTwfG/MCgYEApgLXUC3LvVXLL+kl65ARUHUSgzNmrNSxTptY +5hCORcNnbxRYv6fCfOrDspngC6hfrJMUnOrs59g7qtMvD2TgN+m1RqP3j9Msagp1 +xvAVx45QbnFkxdaYVVpTyVpmHrH99xSC1rBHOFEgEX78XtgVCC1JOK2DhM7PSW5k +lzA8Hh0CgYBVXNnQsy4x+9D09zeFP2TKXsk7N154yUP6oxXu6OjZC+cfxHOGyi/g +PVe8ukBIknZkFRz+ZXdfZzXXuJslENOVeFEInqVAu2W55KeCPc/3SU6hB2lj6MBl +WIZ7yrUmtdalU3cPkFnHkQTL2glYAzjB2z22AxyXNzy37ZhtFpovtg== +-----END RSA PRIVATE KEY----- diff --git a/testing/tests/ikev2/redirect-active/hosts/moon/etc/ipsec.secrets b/testing/tests/ikev2/redirect-active/hosts/moon/etc/ipsec.secrets new file mode 100644 index 000000000..d65b96e34 --- /dev/null +++ b/testing/tests/ikev2/redirect-active/hosts/moon/etc/ipsec.secrets @@ -0,0 +1,3 @@ +# /etc/ipsec.secrets - strongSwan IPsec secrets file + +: RSA marsKey.pem diff --git a/testing/tests/ikev2/redirect-active/hosts/moon/etc/strongswan.conf b/testing/tests/ikev2/redirect-active/hosts/moon/etc/strongswan.conf new file mode 100644 index 000000000..7c415b1ee --- /dev/null +++ b/testing/tests/ikev2/redirect-active/hosts/moon/etc/strongswan.conf @@ -0,0 +1,6 @@ +# /etc/strongswan.conf - strongSwan configuration file + +charon { + load = aes des sha1 sha2 md5 pem pkcs1 gmp random nonce x509 curl revocation hmac stroke kernel-netlink socket-default updown +} + diff --git a/testing/tests/ikev2/redirect-active/posttest.dat b/testing/tests/ikev2/redirect-active/posttest.dat new file mode 100644 index 000000000..7e436a683 --- /dev/null +++ b/testing/tests/ikev2/redirect-active/posttest.dat @@ -0,0 +1,10 @@ +carol::ipsec stop +dave::ipsec stop +moon::ipsec stop +alice::ipsec stop +moon::iptables-restore < /etc/iptables.flush +alice::iptables-restore < /etc/iptables.flush +carol::iptables-restore < /etc/iptables.flush +dave::iptables-restore < /etc/iptables.flush +alice::ip addr del 192.168.0.5/24 dev eth1 +alice::ifdown eth1 diff --git a/testing/tests/ikev2/redirect-active/pretest.dat b/testing/tests/ikev2/redirect-active/pretest.dat new file mode 100644 index 000000000..3f7ac1dcf --- /dev/null +++ b/testing/tests/ikev2/redirect-active/pretest.dat @@ -0,0 +1,13 @@ +alice::ifup eth1 +alice::ip addr add 192.168.0.5/24 dev eth1 +moon::iptables-restore < /etc/iptables.rules +alice::iptables-restore < /etc/iptables.rules +carol::iptables-restore < /etc/iptables.rules +dave::iptables-restore < /etc/iptables.rules +moon::ipsec start +alice::ipsec start +carol::ipsec start +dave::ipsec start +carol::sleep 1 +carol::ipsec up home +dave::ipsec up home diff --git a/testing/tests/ikev2/redirect-active/test.conf b/testing/tests/ikev2/redirect-active/test.conf new file mode 100644 index 000000000..8056d9ce4 --- /dev/null +++ b/testing/tests/ikev2/redirect-active/test.conf @@ -0,0 +1,21 @@ +#!/bin/bash +# +# This configuration file provides information on the +# guest instances used for this test + +# All guest instances that are required for this test +# +VIRTHOSTS="alice venus moon carol winnetou dave" + +# Corresponding block diagram +# +DIAGRAM="a-v-m-c-w-d.png" + +# Guest instances on which tcpdump is to be started +# +TCPDUMPHOSTS="venus carol dave" + +# Guest instances on which IPsec is started +# Used for IPsec logging purposes +# +IPSECHOSTS="alice moon carol dave" From b4968a952e0e393ae412c75de4f4bf52719024de Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 21 May 2015 14:38:02 +0200 Subject: [PATCH 33/36] task: Add optional pre_process() method This will eventually allow tasks to pre-process and verify received messages. --- src/libcharon/sa/task.h | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/libcharon/sa/task.h b/src/libcharon/sa/task.h index ae4260762..b2e9d8886 100644 --- a/src/libcharon/sa/task.h +++ b/src/libcharon/sa/task.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Tobias Brunner + * Copyright (C) 2007-2015 Tobias Brunner * Copyright (C) 2006 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -155,6 +155,18 @@ struct task_t { */ status_t (*process) (task_t *this, message_t *message); + /** + * Verify a message before processing it (optional to implement by tasks). + * + * @param message message to verify + * @return + * - FAILED if verification is not successful, the + * message will be silently discarded + * - DESTROY_ME if IKE_SA has to be destroyed + * - SUCCESS if verification is successful + */ + status_t (*pre_process) (task_t *this, message_t *message); + /** * Get the type of the task implementation. */ From 4b83619310047f5de426217ed32ca3dbe2bb338e Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 21 May 2015 14:45:52 +0200 Subject: [PATCH 34/36] ikev2: Allow tasks to verify response messages before processing them --- src/libcharon/sa/ikev2/task_manager_v2.c | 28 +++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c index feddada4d..1b37b453d 100644 --- a/src/libcharon/sa/ikev2/task_manager_v2.c +++ b/src/libcharon/sa/ikev2/task_manager_v2.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007-2014 Tobias Brunner + * Copyright (C) 2007-2015 Tobias Brunner * Copyright (C) 2007-2010 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -662,6 +662,32 @@ static status_t process_response(private_task_manager_t *this, return DESTROY_ME; } + enumerator = array_create_enumerator(this->active_tasks); + while (enumerator->enumerate(enumerator, &task)) + { + if (!task->pre_process) + { + continue; + } + switch (task->pre_process(task, message)) + { + case SUCCESS: + break; + case FAILED: + default: + /* just ignore the message */ + DBG1(DBG_IKE, "ignore invalid %N response", + exchange_type_names, message->get_exchange_type(message)); + enumerator->destroy(enumerator); + return SUCCESS; + case DESTROY_ME: + /* critical failure, destroy IKE_SA */ + enumerator->destroy(enumerator); + return DESTROY_ME; + } + } + enumerator->destroy(enumerator); + /* catch if we get resetted while processing */ this->reset = FALSE; enumerator = array_create_enumerator(this->active_tasks); From fb7cc16d67e867191335bf05ed5c9fc7e3599a14 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 21 May 2015 14:48:35 +0200 Subject: [PATCH 35/36] ikev2: Allow tasks to verify request messages before processing them --- src/libcharon/sa/ikev2/task_manager_v2.c | 51 ++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/src/libcharon/sa/ikev2/task_manager_v2.c b/src/libcharon/sa/ikev2/task_manager_v2.c index 1b37b453d..8ed86302b 100644 --- a/src/libcharon/sa/ikev2/task_manager_v2.c +++ b/src/libcharon/sa/ikev2/task_manager_v2.c @@ -1078,6 +1078,44 @@ static status_t process_request(private_task_manager_t *this, } } + enumerator = array_create_enumerator(this->passive_tasks); + while (enumerator->enumerate(enumerator, &task)) + { + if (!task->pre_process) + { + continue; + } + switch (task->pre_process(task, message)) + { + case SUCCESS: + break; + case FAILED: + default: + /* just ignore the message */ + DBG1(DBG_IKE, "ignore invalid %N request", + exchange_type_names, message->get_exchange_type(message)); + enumerator->destroy(enumerator); + switch (message->get_exchange_type(message)) + { + case IKE_SA_INIT: + /* no point in keeping the SA when it was created with + * an invalid IKE_SA_INIT message */ + return DESTROY_ME; + default: + /* remove tasks we queued for this request */ + flush_queue(this, TASK_QUEUE_PASSIVE); + /* fall-through */ + case IKE_AUTH: + return NEED_MORE; + } + case DESTROY_ME: + /* critical failure, destroy IKE_SA */ + enumerator->destroy(enumerator); + return DESTROY_ME; + } + } + enumerator->destroy(enumerator); + /* let the tasks process the message */ enumerator = array_create_enumerator(this->passive_tasks); while (enumerator->enumerate(enumerator, (void*)&task)) @@ -1368,12 +1406,17 @@ METHOD(task_manager_t, process_message, status_t, { /* ignore messages altered to EXCHANGE_TYPE_UNDEFINED */ return SUCCESS; } - if (process_request(this, msg) != SUCCESS) + switch (process_request(this, msg)) { - flush(this); - return DESTROY_ME; + case SUCCESS: + this->responding.mid++; + break; + case NEED_MORE: + break; + default: + flush(this); + return DESTROY_ME; } - this->responding.mid++; } else if ((mid == this->responding.mid - 1) && array_count(this->responding.packets)) From 47701e1178a91da363a61bd0478932352bfde2af Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Thu, 21 May 2015 14:56:01 +0200 Subject: [PATCH 36/36] ike-init: Verify REDIRECT notify before processing IKE_SA_INIT message An attacker could blindly send a message with invalid nonce data (or none at all) to DoS an initiator if we just destroy the SA. To prevent this we ignore the message and wait for the one by the correct responder. --- src/libcharon/sa/ikev2/tasks/ike_init.c | 58 ++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/src/libcharon/sa/ikev2/tasks/ike_init.c b/src/libcharon/sa/ikev2/tasks/ike_init.c index 572c997cb..5cfb51807 100644 --- a/src/libcharon/sa/ikev2/tasks/ike_init.c +++ b/src/libcharon/sa/ikev2/tasks/ike_init.c @@ -693,6 +693,54 @@ static void raise_alerts(private_ike_init_t *this, notify_type_t type) } } +METHOD(task_t, pre_process_i, status_t, + private_ike_init_t *this, message_t *message) +{ + enumerator_t *enumerator; + payload_t *payload; + + /* check for erroneous notifies */ + enumerator = message->create_payload_enumerator(message); + while (enumerator->enumerate(enumerator, &payload)) + { + if (payload->get_type(payload) == PLV2_NOTIFY) + { + notify_payload_t *notify = (notify_payload_t*)payload; + notify_type_t type = notify->get_notify_type(notify); + + switch (type) + { + case REDIRECT: + { + identification_t *gateway; + chunk_t data, nonce = chunk_empty; + status_t status = SUCCESS; + + if (this->old_sa) + { + break; + } + data = notify->get_notification_data(notify); + gateway = redirect_data_parse(data, &nonce); + if (!gateway || !chunk_equals(nonce, this->my_nonce)) + { + DBG1(DBG_IKE, "received invalid REDIRECT notify"); + status = FAILED; + } + DESTROY_IF(gateway); + chunk_free(&nonce); + enumerator->destroy(enumerator); + return status; + } + default: + break; + } + } + } + enumerator->destroy(enumerator); + return SUCCESS; +} + METHOD(task_t, process_i, status_t, private_ike_init_t *this, message_t *message) { @@ -762,18 +810,13 @@ METHOD(task_t, process_i, status_t, } data = notify->get_notification_data(notify); gateway = redirect_data_parse(data, &nonce); - enumerator->destroy(enumerator); - if (!gateway || !chunk_equals(nonce, this->my_nonce)) - { - DBG1(DBG_IKE, "received invalid REDIRECT notify"); - } - else if (this->ike_sa->handle_redirect(this->ike_sa, - gateway)) + if (this->ike_sa->handle_redirect(this->ike_sa, gateway)) { status = NEED_MORE; } DESTROY_IF(gateway); chunk_free(&nonce); + enumerator->destroy(enumerator); return status; } default: @@ -909,6 +952,7 @@ ike_init_t *ike_init_create(ike_sa_t *ike_sa, bool initiator, ike_sa_t *old_sa) { this->public.task.build = _build_i; this->public.task.process = _process_i; + this->public.task.pre_process = _pre_process_i; } else {