[strongSwan-dev] [PATCH 7/8] trap-manager: Enable auto=route with right=%any for transport mode connections

Timo Teräs timo.teras at iki.fi
Wed Aug 27 15:05:23 CEST 2014


From: Tobias Brunner <tobias at strongswan.org>

Timo: Rebased on top of git master and my charon source/remote override work,
and added also tracking of source address.

Signed-off-by: Timo Teräs <timo.teras at iki.fi>
---
 src/libcharon/sa/trap_manager.c | 199 ++++++++++++++++++++++++++++++++++------
 1 file changed, 172 insertions(+), 27 deletions(-)

diff --git a/src/libcharon/sa/trap_manager.c b/src/libcharon/sa/trap_manager.c
index e70f400..22774b2 100644
--- a/src/libcharon/sa/trap_manager.c
+++ b/src/libcharon/sa/trap_manager.c
@@ -14,6 +14,28 @@
  * for more details.
  */
 
+/*
+ * Copyright (C) 2014 Timo Teräs <timo.teras at iki.fi>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
 #include "trap_manager.h"
 
 #include <hydra.h>
@@ -87,6 +109,8 @@ typedef struct {
 	peer_cfg_t *peer_cfg;
 	/** ref to instantiated CHILD_SA (i.e the trap policy) */
 	child_sa_t *child_sa;
+	/** TRUE in case of wildcard Transport Mode SA */
+	bool wildcard;
 } entry_t;
 
 /**
@@ -97,6 +121,10 @@ typedef struct {
 	ike_sa_t *ike_sa;
 	/** reqid of pending trap policy */
 	u_int32_t reqid;
+	/** source address (wildcard case) */
+	host_t *src;
+	/** destination address (wildcard case) */
+	host_t *dst;
 } acquire_t;
 
 /**
@@ -115,6 +143,8 @@ static void destroy_entry(entry_t *this)
  */
 static void destroy_acquire(acquire_t *this)
 {
+	DESTROY_IF(this->src);
+	DESTROY_IF(this->dst);
 	free(this);
 }
 
@@ -126,6 +156,15 @@ static bool acquire_by_reqid(acquire_t *this, u_int32_t *reqid)
 	return this->reqid == *reqid;
 }
 
+/**
+ * match an acquire entry by destination address
+ */
+static bool acquire_by_acquire(acquire_t *this, acquire_t *that)
+{
+	return this->src && this->src->ip_equals(this->src, that->src) &&
+		   this->dst && this->dst->ip_equals(this->dst, that->dst);
+}
+
 METHOD(trap_manager_t, install, u_int32_t,
 	private_trap_manager_t *this, peer_cfg_t *peer, child_cfg_t *child,
 	u_int32_t reqid)
@@ -140,29 +179,40 @@ METHOD(trap_manager_t, install, u_int32_t,
 	linked_list_t *proposals;
 	proposal_t *proposal;
 	protocol_id_t proto = PROTO_ESP;
+	bool wildcard = FALSE;
 
 	/* try to resolve addresses */
 	ike_cfg = peer->get_ike_cfg(peer);
 	other = ike_cfg->resolve_other(ike_cfg, AF_UNSPEC);
-	if (!other || other->is_anyaddr(other))
+	if (other && other->is_anyaddr(other) &&
+		child->get_mode(child) == MODE_TRANSPORT)
+	{
+		/* allow wildcard for Transport Mode SAs */
+		me = host_create_any(other->get_family(other));
+		wildcard = TRUE;
+	}
+	else if (!other || other->is_anyaddr(other))
 	{
 		DESTROY_IF(other);
 		DBG1(DBG_CFG, "installing trap failed, remote address unknown");
 		return 0;
 	}
-	me = ike_cfg->resolve_me(ike_cfg, other->get_family(other));
-	if (!me || me->is_anyaddr(me))
+	else
 	{
-		DESTROY_IF(me);
-		me = hydra->kernel_interface->get_source_addr(
-									hydra->kernel_interface, other, NULL);
-		if (!me)
+		me = ike_cfg->resolve_me(ike_cfg, other->get_family(other));
+		if (!me || me->is_anyaddr(me))
 		{
-			DBG1(DBG_CFG, "installing trap failed, local address unknown");
-			other->destroy(other);
-			return 0;
+			DESTROY_IF(me);
+			me = hydra->kernel_interface->get_source_addr(
+										hydra->kernel_interface, other, NULL);
+			if (!me)
+			{
+				DBG1(DBG_CFG, "installing trap failed, local address unknown");
+				other->destroy(other);
+				return 0;
+			}
+			me->set_port(me, ike_cfg->get_my_port(ike_cfg));
 		}
-		me->set_port(me, ike_cfg->get_my_port(ike_cfg));
 	}
 
 	this->lock->write_lock(this->lock);
@@ -197,6 +247,7 @@ METHOD(trap_manager_t, install, u_int32_t,
 	INIT(entry,
 		.name = strdup(child->get_name(child)),
 		.peer_cfg = peer->get_ref(peer),
+		.wildcard = wildcard,
 	);
 	this->traps->insert_first(this->traps, entry);
 	/* don't hold lock while creating CHILD_SA and installing policies */
@@ -345,6 +396,8 @@ METHOD(trap_manager_t, acquire, void,
 	peer_cfg_t *peer;
 	child_cfg_t *child;
 	ike_sa_t *ike_sa;
+	host_t *src_host, *dst_host;
+	bool wildcard, ignore = FALSE;
 
 	this->lock->read_lock(this->lock);
 	enumerator = this->traps->create_enumerator(this->traps);
@@ -361,30 +414,71 @@ METHOD(trap_manager_t, acquire, void,
 
 	if (!found)
 	{
-		DBG1(DBG_CFG, "trap not found, unable to acquire reqid %d",reqid);
+		DBG1(DBG_CFG, "trap not found, unable to acquire reqid %d", reqid);
 		this->lock->unlock(this->lock);
 		return;
 	}
+	reqid = found->child_sa->get_reqid(found->child_sa);
+	wildcard = found->wildcard;
 
 	this->mutex->lock(this->mutex);
-	reqid = found->child_sa->get_reqid(found->child_sa);
-	if (this->acquires->find_first(this->acquires, (void*)acquire_by_reqid,
-								  (void**)&acquire, &reqid) == SUCCESS)
-	{
-		DBG1(DBG_CFG, "ignoring acquire, connection attempt pending");
-		this->mutex->unlock(this->mutex);
-		this->lock->unlock(this->lock);
-		return;
+	if (wildcard)
+	{	/* for wildcard acquires we check that we don't have a pending acquire
+		* with the same peer */
+		u_int8_t mask;
+
+		src->to_subnet(src, &src_host, &mask);
+		dst->to_subnet(dst, &dst_host, &mask);
+
+		acquire_t match = {
+			.src = src_host,
+			.dst = dst_host,
+		};
+
+		if (this->acquires->find_first(this->acquires, (void*)acquire_by_acquire,
+										(void**)&acquire, &match) == SUCCESS)
+		{
+			src_host->destroy(src_host);
+			dst_host->destroy(dst_host);
+			ignore = TRUE;
+		}
+		else
+		{
+			INIT(acquire,
+				.src = src_host->clone(src_host),
+				.dst = dst_host->clone(dst_host),
+				/* store the original reqid to remove the temporary SA later */
+				.reqid = reqid,
+			);
+			this->acquires->insert_last(this->acquires, acquire);
+			/* we have to allocate a new reqid for each instance */
+			reqid = 0;
+		}
 	}
 	else
 	{
-		INIT(acquire,
-			.reqid = reqid,
-		);
-		this->acquires->insert_last(this->acquires, acquire);
+		if (this->acquires->find_first(this->acquires, (void*)acquire_by_reqid,
+										(void**)&acquire, &reqid) == SUCCESS)
+		{
+			ignore = TRUE;
+		}
+		else
+		{
+			INIT(acquire,
+				.reqid = reqid,
+			);
+			this->acquires->insert_last(this->acquires, acquire);
+		}
 	}
 	this->mutex->unlock(this->mutex);
 
+	if (ignore)
+	{
+		DBG1(DBG_CFG, "ignoring acquire, connection attempt pending");
+		this->lock->unlock(this->lock);
+		return;
+	}
+
 	peer = found->peer_cfg->get_ref(found->peer_cfg);
 	child = found->child_sa->get_config(found->child_sa);
 	child = child->get_ref(child);
@@ -393,7 +487,10 @@ METHOD(trap_manager_t, acquire, void,
 
 	ike_sa = charon->ike_sa_manager->checkout_by_config(
 											charon->ike_sa_manager, peer,
-											NULL, NULL);
+											src_host, dst_host);
+	DESTROY_IF(src_host);
+	DESTROY_IF(dst_host);
+
 	if (ike_sa)
 	{
 		if (ike_sa->get_peer_cfg(ike_sa) == NULL)
@@ -422,6 +519,46 @@ METHOD(trap_manager_t, acquire, void,
 }
 
 /**
+ * If right=%any is used every SA gets its own reqid different from the reqid
+ * of the trap policy.  Before such an SA is installed, that is, whenever
+ * traffic matches the trap policy, the kernel will allocate a temporary SA to
+ * block the traffic and keep track of acquires.  This function deletes such a
+ * temporary SA after the final SA got installed by pretending that an SA was
+ * established for the trap policy (with its reqid) and then deleting that SA
+ * immediately afterwards (the temporary SA can't be deleted directly as it has
+ * no SPI)
+ */
+static void delete_temporary_sa(ike_sa_t *ike_sa, child_sa_t *child_sa,
+								acquire_t *acquire)
+{
+	lifetime_cfg_t lifetime = { .time = { .life = 0 } };
+	mark_t mark = { .value = 0 };
+	host_t *me, *other;
+	u_int32_t spi;
+	rng_t *rng;
+
+	rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK);
+	if (!rng || !rng->get_bytes(rng, sizeof(spi), (u_int8_t*)&spi))
+	{
+		DBG1(DBG_CFG, "failed to allocate random SPI to delete temporary SA");
+		DESTROY_IF(rng);
+		return;
+	}
+	rng->destroy(rng);
+
+	other = ike_sa->get_other_host(ike_sa);
+	me = ike_sa->get_my_host(ike_sa);
+
+	hydra->kernel_interface->add_sa(hydra->kernel_interface, me, other, spi,
+							IPPROTO_ESP, acquire->reqid, mark, 0, &lifetime,
+							ENCR_NULL, chunk_empty, AUTH_UNDEFINED, chunk_empty,
+							MODE_TRANSPORT, IPCOMP_NONE, 0, 0, TRUE, FALSE, FALSE,
+							FALSE, NULL, NULL);
+	hydra->kernel_interface->del_sa(hydra->kernel_interface, me, other, spi,
+									IPPROTO_ESP, 0, mark);
+}
+
+/**
  * Complete the acquire, if successful or failed
  */
 static void complete(private_trap_manager_t *this, ike_sa_t *ike_sa,
@@ -438,9 +575,17 @@ static void complete(private_trap_manager_t *this, ike_sa_t *ike_sa,
 		{
 			continue;
 		}
-		if (child_sa && child_sa->get_reqid(child_sa) != acquire->reqid)
+		if (child_sa)
 		{
-			continue;
+			if (acquire->dst)
+			{	/* since every wildcard acquire results in a separate IKE_SA
+				 * there is no need to compare the destination address */
+				delete_temporary_sa(ike_sa, child_sa, acquire);
+			}
+			else if (child_sa->get_reqid(child_sa) != acquire->reqid)
+			{
+				continue;
+			}
 		}
 		this->acquires->remove_at(this->acquires, enumerator);
 		destroy_acquire(acquire);
-- 
2.1.0



More information about the Dev mailing list