From: Tobias Brunner <[email protected]> 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 <[email protected]> --- 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 <[email protected]> + * + * 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 _______________________________________________ Dev mailing list [email protected] https://lists.strongswan.org/mailman/listinfo/dev
