[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 Apr 29 17:13:22 CEST 2015
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 a002231..d4ccab8 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>
@@ -92,6 +114,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;
/**
@@ -102,6 +126,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;
/**
@@ -120,6 +148,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);
}
@@ -131,6 +161,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)
@@ -145,29 +184,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);
@@ -202,6 +252,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 */
@@ -350,6 +401,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);
@@ -366,30 +419,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);
@@ -398,7 +492,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)
@@ -427,6 +524,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, 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,
@@ -443,9 +580,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.3.6
More information about the Dev
mailing list