]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
trap-manager: Add facility to install externally managed trap policies
authorTobias Brunner <tobias@strongswan.org>
Tue, 1 Feb 2022 19:17:50 +0000 (20:17 +0100)
committerTobias Brunner <tobias@strongswan.org>
Thu, 14 Apr 2022 16:42:01 +0000 (18:42 +0200)
This allows managing trap policies outside of the trap manager.

We'll use this to create trap policies with generic labels if trap policies
can't (or won't) be used (e.g. as responder for roadwarriors).

src/libcharon/sa/trap_manager.c
src/libcharon/sa/trap_manager.h

index b00f7edc977a8f92b3bdf22150b69cb81716c864..7b6e2abdf6fcc1b5d207ad4eb5d5292aa7b56b3d 100644 (file)
@@ -106,6 +106,8 @@ typedef struct {
        child_sa_t *child_sa;
        /** TRUE in case of wildcard Transport Mode SA */
        bool wildcard;
+       /** TRUE for CHILD_SAs that are externally managed */
+       bool external;
 } entry_t;
 
 /**
@@ -125,7 +127,10 @@ typedef struct {
  */
 static void destroy_entry(entry_t *this)
 {
-       this->child_sa->destroy(this->child_sa);
+       if (!this->external)
+       {
+               this->child_sa->destroy(this->child_sa);
+       }
        this->peer_cfg->destroy(this->peer_cfg);
        free(this->name);
        free(this);
@@ -183,6 +188,41 @@ static bool dynamic_remote_ts(child_cfg_t *child)
        return found;
 }
 
+/**
+ * Install the given trap
+ */
+static status_t install_trap(child_sa_t *child_sa, linked_list_t *local,
+                                                        linked_list_t *remote)
+{
+       linked_list_t *my_ts, *other_ts, *proposals;
+       proposal_t *proposal;
+       child_cfg_t *child;
+       protocol_id_t proto = PROTO_ESP;
+
+       child = child_sa->get_config(child_sa);
+
+       my_ts = child->get_traffic_selectors(child, TRUE, NULL, local, FALSE);
+       other_ts = child->get_traffic_selectors(child, FALSE, NULL, remote, FALSE);
+
+       /* we don't know the finally negotiated protocol (ESP|AH), we install
+        * the SA with the protocol of the first proposal */
+       proposals = child->get_proposals(child, TRUE);
+       if (proposals->get_first(proposals, (void**)&proposal) == SUCCESS)
+       {
+               proto = proposal->get_protocol(proposal);
+       }
+       proposals->destroy_offset(proposals, offsetof(proposal_t, destroy));
+
+       child_sa->set_protocol(child_sa, proto);
+       child_sa->set_mode(child_sa, child->get_mode(child));
+
+       child_sa->set_policies(child_sa, my_ts, other_ts);
+       my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy));
+       other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy));
+
+       return child_sa->install_policies(child_sa);
+}
+
 METHOD(trap_manager_t, install, bool,
        private_trap_manager_t *this, peer_cfg_t *peer, child_cfg_t *child)
 {
@@ -190,12 +230,9 @@ METHOD(trap_manager_t, install, bool,
        ike_cfg_t *ike_cfg;
        child_sa_t *child_sa;
        host_t *me, *other;
-       linked_list_t *my_ts, *other_ts, *list;
+       linked_list_t *local, *remote;
        enumerator_t *enumerator;
        status_t status;
-       linked_list_t *proposals;
-       proposal_t *proposal;
-       protocol_id_t proto = PROTO_ESP;
        bool result = FALSE, wildcard = FALSE;
 
        /* try to resolve addresses */
@@ -254,13 +291,14 @@ METHOD(trap_manager_t, install, bool,
        enumerator = this->traps->create_enumerator(this->traps);
        while (enumerator->enumerate(enumerator, &entry))
        {
-               if (streq(entry->name, child->get_name(child)) &&
+               if (!entry->external &&
+                       streq(entry->name, child->get_name(child)) &&
                        streq(entry->peer_cfg->get_name(entry->peer_cfg),
                                  peer->get_name(peer)))
                {
                        found = entry;
                        if (entry->child_sa)
-                       {       /* replace it with an updated version, if already installed */
+                       {       /* replace it with an updated version if already installed */
                                this->traps->remove_at(this->traps, enumerator);
                        }
                        break;
@@ -302,28 +340,13 @@ METHOD(trap_manager_t, install, bool,
        };
        child_sa = child_sa_create(me, other, child, &child_data);
 
-       list = linked_list_create_with_items(me, NULL);
-       my_ts = child->get_traffic_selectors(child, TRUE, NULL, list, FALSE);
-       list->destroy_offset(list, offsetof(host_t, destroy));
+       local = linked_list_create_with_items(me, NULL);
+       remote = linked_list_create_with_items(other, NULL);
 
-       list = linked_list_create_with_items(other, NULL);
-       other_ts = child->get_traffic_selectors(child, FALSE, NULL, list, FALSE);
-       list->destroy_offset(list, offsetof(host_t, destroy));
+       status = install_trap(child_sa, local, remote);
 
-       /* We don't know the finally negotiated protocol (ESP|AH), we install
-        * the SA with the protocol of the first proposal */
-       proposals = child->get_proposals(child, TRUE);
-       if (proposals->get_first(proposals, (void**)&proposal) == SUCCESS)
-       {
-               proto = proposal->get_protocol(proposal);
-       }
-       proposals->destroy_offset(proposals, offsetof(proposal_t, destroy));
-       child_sa->set_protocol(child_sa, proto);
-       child_sa->set_mode(child_sa, child->get_mode(child));
-       child_sa->set_policies(child_sa, my_ts, other_ts);
-       status = child_sa->install_policies(child_sa);
-       my_ts->destroy_offset(my_ts, offsetof(traffic_selector_t, destroy));
-       other_ts->destroy_offset(other_ts, offsetof(traffic_selector_t, destroy));
+       local->destroy_offset(local, offsetof(host_t, destroy));
+       remote->destroy_offset(remote, offsetof(host_t, destroy));
        if (status != SUCCESS)
        {
                DBG1(DBG_CFG, "installing trap failed");
@@ -366,8 +389,71 @@ METHOD(trap_manager_t, uninstall, bool,
        enumerator = this->traps->create_enumerator(this->traps);
        while (enumerator->enumerate(enumerator, &entry))
        {
-               if (streq(entry->name, child) &&
-                  (!peer || streq(peer, entry->peer_cfg->get_name(entry->peer_cfg))))
+               if (!entry->external &&
+                       streq(entry->name, child) &&
+                   (!peer || streq(peer, entry->peer_cfg->get_name(entry->peer_cfg))))
+               {
+                       this->traps->remove_at(this->traps, enumerator);
+                       found = entry;
+                       break;
+               }
+       }
+       enumerator->destroy(enumerator);
+       this->lock->unlock(this->lock);
+
+       if (!found)
+       {
+               return FALSE;
+       }
+       destroy_entry(found);
+       return TRUE;
+}
+
+METHOD(trap_manager_t, install_external, bool,
+       private_trap_manager_t *this, peer_cfg_t *peer, child_sa_t *child,
+       linked_list_t *local, linked_list_t *remote)
+{
+       entry_t *entry;
+
+       this->lock->write_lock(this->lock);
+       if (this->installing == INSTALL_DISABLED)
+       {       /* flush() has been called */
+               this->lock->unlock(this->lock);
+               return FALSE;
+       }
+
+       INIT(entry,
+               .name = strdup(child->get_name(child)),
+               .peer_cfg = peer->get_ref(peer),
+               .child_sa = child,
+               .external = TRUE,
+       );
+       this->traps->insert_first(this->traps, entry);
+       this->lock->unlock(this->lock);
+
+       if (install_trap(child, local, remote) != SUCCESS)
+       {
+               DBG1(DBG_CFG, "installing trap failed");
+               this->lock->write_lock(this->lock);
+               this->traps->remove(this->traps, entry, NULL);
+               this->lock->unlock(this->lock);
+               destroy_entry(entry);
+               return FALSE;
+       }
+       return TRUE;
+}
+
+METHOD(trap_manager_t, remove_external, bool,
+       private_trap_manager_t *this, child_sa_t *child)
+{
+       enumerator_t *enumerator;
+       entry_t *entry, *found = NULL;
+
+       this->lock->write_lock(this->lock);
+       enumerator = this->traps->create_enumerator(this->traps);
+       while (enumerator->enumerate(enumerator, &entry))
+       {
+               if (entry->external && entry->child_sa == child)
                {
                        this->traps->remove_at(this->traps, enumerator);
                        found = entry;
@@ -396,8 +482,9 @@ CALLBACK(trap_filter, bool,
 
        while (orig->enumerate(orig, &entry))
        {
-               if (!entry->child_sa)
-               {       /* skip entries that are currently being installed */
+               if (!entry->child_sa || entry->external)
+               {       /* skip entries that are currently being installed or are managed
+                        * externally */
                        continue;
                }
                if (peer_cfg)
@@ -678,6 +765,8 @@ trap_manager_t *trap_manager_create(void)
                .public = {
                        .install = _install,
                        .uninstall = _uninstall,
+                       .install_external = _install_external,
+                       .remove_external = _remove_external,
                        .create_enumerator = _create_enumerator,
                        .acquire = _acquire,
                        .flush = _flush,
index d9b4906f742326c8fb051b1b06bccafa0b230c1b..60dbc2f1253ecdf5c2374ab1acb2240d1a775b35 100644 (file)
@@ -25,6 +25,7 @@
 #include <library.h>
 #include <collections/enumerator.h>
 #include <config/peer_cfg.h>
+#include <sa/child_sa.h>
 
 typedef struct trap_manager_t trap_manager_t;
 
@@ -55,7 +56,31 @@ struct trap_manager_t {
        bool (*uninstall)(trap_manager_t *this, char *peer, char *child);
 
        /**
-        * Create an enumerator over all installed traps.
+        * Install and register an externally managed trap policy using the two
+        * lists of local and remote addresses when deriving traffic selectors.
+        *
+        * @param peer          peer configuration to register
+        * @param child         CHILD_SA to install and register
+        * @param local         list of local addresses (virtual or physical)
+        * @param remote        list of remote addresses (virtual or physical)
+        * @return                      TRUE if successfully installed and registered
+        */
+       bool (*install_external)(trap_manager_t *this, peer_cfg_t *peer,
+                                                        child_sa_t *child, linked_list_t *local,
+                                                        linked_list_t *remote);
+
+       /**
+        * Remove and uninstall a previously registered externally managed trap
+        * policy.
+        *
+        * @param child         CHILD_SA to remove
+        * @return                      TRUE if successfully removed
+        */
+       bool (*remove_external)(trap_manager_t *this, child_sa_t *child);
+
+       /**
+        * Create an enumerator over all installed traps (does not include
+        * externally managed trap policies).
         *
         * @return                      enumerator over (peer_cfg_t, child_sa_t)
         */