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

Reply via email to