]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
selinux: Add plugin to install trap policies with generic labels
authorTobias Brunner <tobias@strongswan.org>
Wed, 2 Feb 2022 14:51:06 +0000 (15:51 +0100)
committerTobias Brunner <tobias@strongswan.org>
Thu, 14 Apr 2022 16:42:01 +0000 (18:42 +0200)
After establishing an IKE_SA, we check if any of its child configs
define generic SELinux labels and install trap policies for them if
necessary narrowed to the current (virtual) IPs.

configure.ac
src/libcharon/Makefile.am
src/libcharon/plugins/selinux/Makefile.am [new file with mode: 0644]
src/libcharon/plugins/selinux/selinux_listener.c [new file with mode: 0644]
src/libcharon/plugins/selinux/selinux_listener.h [new file with mode: 0644]
src/libcharon/plugins/selinux/selinux_plugin.c [new file with mode: 0644]
src/libcharon/plugins/selinux/selinux_plugin.h [new file with mode: 0644]

index ac0724612588e74ce1a8ed2556a106a1662d4cee..86e50a366059d2178661f05cae1babe5964cb9c2 100644 (file)
@@ -1524,6 +1524,7 @@ ADD_PLUGIN([kernel-iph],           [c charon])
 ADD_PLUGIN([kernel-pfkey],         [c charon starter nm cmd])
 ADD_PLUGIN([kernel-pfroute],       [c charon starter nm cmd])
 ADD_PLUGIN([kernel-netlink],       [c charon starter nm cmd])
+ADD_PLUGIN([selinux],              [c charon starter nm cmd])
 ADD_PLUGIN([resolve],              [c charon cmd])
 ADD_PLUGIN([save-keys],            [c])
 ADD_PLUGIN([socket-default],       [c charon nm cmd])
@@ -1770,6 +1771,7 @@ AM_CONDITIONAL(USE_RESOLVE, test x$resolve = xtrue)
 AM_CONDITIONAL(USE_ATTR, test x$attr = xtrue)
 AM_CONDITIONAL(USE_ATTR_SQL, test x$attr_sql = xtrue)
 AM_CONDITIONAL(USE_COUNTERS, test x$counters = xtrue)
+AM_CONDITIONAL(USE_SELINUX, test x$selinux = xtrue)
 
 #  other options
 # ---------------
@@ -2073,6 +2075,7 @@ AC_CONFIG_FILES([
        src/libcharon/plugins/resolve/Makefile
        src/libcharon/plugins/attr/Makefile
        src/libcharon/plugins/attr_sql/Makefile
+       src/libcharon/plugins/selinux/Makefile
        src/libcharon/tests/Makefile
        src/libtpmtss/Makefile
        src/libtpmtss/plugins/tpm/Makefile
index 46346a44d4a49ddc54424a66ef4e278ac174bc6a..fb6de8ebd763eca619403a3ff577389e45b5cd5a 100644 (file)
@@ -726,6 +726,13 @@ if MONOLITHIC
 endif
 endif
 
+if USE_SELINUX
+  SUBDIRS += plugins/selinux
+if MONOLITHIC
+  libcharon_la_LIBADD += plugins/selinux/libstrongswan-selinux.la
+endif
+endif
+
 if USE_ATTR_SQL
   SUBDIRS += plugins/attr_sql
 if MONOLITHIC
diff --git a/src/libcharon/plugins/selinux/Makefile.am b/src/libcharon/plugins/selinux/Makefile.am
new file mode 100644 (file)
index 0000000..8bf821b
--- /dev/null
@@ -0,0 +1,18 @@
+AM_CPPFLAGS = \
+       -I$(top_srcdir)/src/libstrongswan \
+       -I$(top_srcdir)/src/libcharon
+
+AM_CFLAGS = \
+       $(PLUGIN_CFLAGS)
+
+if MONOLITHIC
+noinst_LTLIBRARIES = libstrongswan-selinux.la
+else
+plugin_LTLIBRARIES = libstrongswan-selinux.la
+endif
+
+libstrongswan_selinux_la_SOURCES = \
+       selinux_plugin.h selinux_plugin.c \
+       selinux_listener.h selinux_listener.c
+
+libstrongswan_selinux_la_LDFLAGS = -module -avoid-version
diff --git a/src/libcharon/plugins/selinux/selinux_listener.c b/src/libcharon/plugins/selinux/selinux_listener.c
new file mode 100644 (file)
index 0000000..bce3092
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2022 Tobias Brunner, codelabs GmbH
+ *
+ * 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 "selinux_listener.h"
+
+#include <daemon.h>
+#include <collections/array.h>
+#include <collections/hashtable.h>
+
+typedef struct private_selinux_listener_t private_selinux_listener_t;
+
+/**
+ * Private data.
+ */
+struct private_selinux_listener_t {
+
+       /**
+        * Public interface.
+        */
+       selinux_listener_t public;
+
+       /**
+        * IKE_SAs with attached trap policies, ike_sa_id_t => entry_t.
+        */
+       hashtable_t *sas;
+};
+
+/**
+ * Entry to keep track of trap policies.
+ */
+typedef struct {
+
+       /**
+        * IKE_SA ID.
+        */
+       ike_sa_id_t *id;
+
+       /**
+        * Installed trap policies.
+        */
+       array_t *traps;
+
+} entry_t;
+
+/**
+ * Destroy the given entry.
+ */
+static void destroy_entry(entry_t *entry)
+{
+       entry->id->destroy(entry->id);
+       array_destroy(entry->traps);
+       free(entry);
+}
+
+/**
+ * Hashtable hash function
+ */
+static u_int hash(const void *key)
+{
+       ike_sa_id_t *id = (ike_sa_id_t*)key;
+       uint64_t spi_i = id->get_initiator_spi(id),
+                        spi_r = id->get_responder_spi(id);
+       return chunk_hash_inc(chunk_from_thing(spi_i),
+                                                 chunk_hash(chunk_from_thing(spi_r)));
+}
+
+/**
+ * Hashtable equals function
+ */
+static bool equals(const void *a_pub, const void *b)
+{
+       ike_sa_id_t *a = (ike_sa_id_t*)a_pub;
+       return a->equals(a, (ike_sa_id_t*)b);
+}
+
+/**
+ * Install a trap policy for the generic SELinux label.
+ */
+static bool install_generic_trap(ike_sa_t *ike_sa, child_sa_t *child_sa)
+{
+       linked_list_t *local, *remote;
+       sec_label_t *label;
+       bool success;
+
+       label = child_sa->get_label(child_sa);
+       DBG1(DBG_IKE, "installing trap %s{%d} with generic security label '%s'",
+                child_sa->get_name(child_sa), child_sa->get_unique_id(child_sa),
+                label->get_string(label));
+
+       local = ike_sa_get_dynamic_hosts(ike_sa, TRUE);
+       remote = ike_sa_get_dynamic_hosts(ike_sa, FALSE);
+       success = charon->traps->install_external(charon->traps,
+                                                                                         ike_sa->get_peer_cfg(ike_sa),
+                                                                                         child_sa, local, remote);
+       local->destroy(local);
+       remote->destroy(remote);
+       return success;
+}
+
+METHOD(listener_t, ike_updown, bool,
+       private_selinux_listener_t *this, ike_sa_t *ike_sa, bool up)
+{
+       enumerator_t *enumerator;
+       peer_cfg_t *peer_cfg;
+       child_cfg_t *child_cfg;
+       child_sa_t *child_sa;
+       entry_t *entry;
+
+       if (up)
+       {
+               child_sa_create_t child = {
+                       .if_id_in_def = ike_sa->get_if_id(ike_sa, TRUE),
+                       .if_id_out_def = ike_sa->get_if_id(ike_sa, FALSE),
+               };
+
+               INIT(entry,
+                       .id = ike_sa->get_id(ike_sa),
+               );
+               entry->id = entry->id->clone(entry->id);
+
+               peer_cfg = ike_sa->get_peer_cfg(ike_sa);
+               enumerator = peer_cfg->create_child_cfg_enumerator(peer_cfg);
+               while (enumerator->enumerate(enumerator, &child_cfg))
+               {
+                       if (child_cfg->get_label(child_cfg) &&
+                               child_cfg->get_label_mode(child_cfg) == SEC_LABEL_MODE_SELINUX)
+                       {
+                               child_sa = child_sa_create(ike_sa->get_my_host(ike_sa),
+                                                                                  ike_sa->get_other_host(ike_sa),
+                                                                                  child_cfg, &child);
+                               if (install_generic_trap(ike_sa, child_sa))
+                               {
+                                       array_insert_create(&entry->traps, ARRAY_TAIL, child_sa);
+                               }
+                               else
+                               {
+                                       child_sa->destroy(child_sa);
+                               }
+                       }
+               }
+               enumerator->destroy(enumerator);
+
+               if (array_count(entry->traps))
+               {
+                       this->sas->put(this->sas, entry->id, entry);
+               }
+               else
+               {
+                       destroy_entry(entry);
+               }
+       }
+       else
+       {
+               entry = this->sas->remove(this->sas, ike_sa->get_id(ike_sa));
+               if (entry)
+               {
+                       while (array_remove(entry->traps, ARRAY_TAIL, &child_sa))
+                       {
+                               sec_label_t *label = child_sa->get_label(child_sa);
+
+                               DBG1(DBG_IKE, "uninstalling trap %s{%d} with generic security "
+                                        "label '%s'", child_sa->get_name(child_sa),
+                                        child_sa->get_unique_id(child_sa),
+                                        label->get_string(label));
+                               charon->traps->remove_external(charon->traps, child_sa);
+                               child_sa->destroy(child_sa);
+                       }
+                       destroy_entry(entry);
+               }
+       }
+       return TRUE;
+}
+
+METHOD(listener_t, ike_rekey, bool,
+       private_selinux_listener_t *this, ike_sa_t *old, ike_sa_t *new)
+{
+       entry_t *entry;
+
+       entry = this->sas->remove(this->sas, old->get_id(old));
+       if (entry)
+       {
+               entry->id->destroy(entry->id);
+               entry->id = new->get_id(new);
+               entry->id = entry->id->clone(entry->id);
+               this->sas->put(this->sas, entry->id, entry);
+       }
+       return TRUE;
+}
+
+METHOD(listener_t, ike_update, bool,
+       private_selinux_listener_t *this, ike_sa_t *ike_sa,
+       host_t *local, host_t *remote)
+{
+       entry_t *entry;
+       child_sa_t *child_sa;
+       linked_list_t *vips;
+       int i;
+
+       entry = this->sas->get(this->sas, ike_sa->get_id(ike_sa));
+       if (entry)
+       {
+               vips = linked_list_create_from_enumerator(
+                                               ike_sa->create_virtual_ip_enumerator(ike_sa, local));
+               for (i = 0; i < array_count(entry->traps); i++)
+               {
+                       array_get(entry->traps, i, &child_sa);
+                       child_sa->update(child_sa, local, remote, vips,
+                                                        ike_sa->has_condition(ike_sa, COND_NAT_ANY));
+               }
+               vips->destroy(vips);
+       }
+       return TRUE;
+}
+
+METHOD(selinux_listener_t, destroy, void,
+       private_selinux_listener_t *this)
+{
+       this->sas->destroy(this->sas);
+       free(this);
+}
+
+/*
+ * Described in header
+ */
+selinux_listener_t *selinux_listener_create()
+{
+       private_selinux_listener_t *this;
+
+       INIT(this,
+               .public = {
+                       .listener = {
+                               .ike_updown = _ike_updown,
+                               .ike_rekey = _ike_rekey,
+                               .ike_update = _ike_update,
+                       },
+                       .destroy = _destroy,
+               },
+               .sas = hashtable_create(hash, equals, 32),
+       );
+
+       return &this->public;
+}
diff --git a/src/libcharon/plugins/selinux/selinux_listener.h b/src/libcharon/plugins/selinux/selinux_listener.h
new file mode 100644 (file)
index 0000000..31deed2
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 Tobias Brunner, codelabs GmbH
+ *
+ * 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.
+ */
+
+/**
+ * @defgroup selinux_listener selinux_listener
+ * @{ @ingroup selinux
+ */
+
+#ifndef SELINUX_LISTENER_H_
+#define SELINUX_LISTENER_H_
+
+#include <bus/listeners/listener.h>
+
+typedef struct selinux_listener_t selinux_listener_t;
+
+/**
+ * Listener to manage trap policies for generic SELinux labels.
+ */
+struct selinux_listener_t {
+
+       /**
+        * Implements listener_t interface.
+        */
+       listener_t listener;
+
+       /**
+        * Destroy a selinux_listener_t.
+        */
+       void (*destroy)(selinux_listener_t *this);
+};
+
+/**
+ * Create a listener instance.
+ */
+selinux_listener_t *selinux_listener_create();
+
+#endif /** SELINUX_LISTENER_H_ @}*/
diff --git a/src/libcharon/plugins/selinux/selinux_plugin.c b/src/libcharon/plugins/selinux/selinux_plugin.c
new file mode 100644 (file)
index 0000000..80919ea
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2022 Tobias Brunner, codelabs GmbH
+ *
+ * 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 "selinux_plugin.h"
+#include "selinux_listener.h"
+
+#include <daemon.h>
+
+typedef struct private_selinux_plugin_t private_selinux_plugin_t;
+
+/**
+ * Private data
+ */
+struct private_selinux_plugin_t {
+
+       /**
+        * Public interface
+        */
+       selinux_plugin_t public;
+
+       /**
+        * Listener
+        */
+       selinux_listener_t *listener;
+};
+
+METHOD(plugin_t, get_name, char*,
+       private_selinux_plugin_t *this)
+{
+       return "selinux";
+}
+
+/**
+ * Register handler
+ */
+static bool plugin_cb(private_selinux_plugin_t *this,
+                                         plugin_feature_t *feature, bool reg, void *cb_data)
+{
+       if (reg)
+       {
+               charon->bus->add_listener(charon->bus, &this->listener->listener);
+       }
+       else
+       {
+               charon->bus->remove_listener(charon->bus, &this->listener->listener);
+       }
+       return TRUE;
+}
+
+METHOD(plugin_t, get_features, int,
+       private_selinux_plugin_t *this, plugin_feature_t *features[])
+{
+       static plugin_feature_t f[] = {
+               PLUGIN_CALLBACK((plugin_feature_callback_t)plugin_cb, NULL),
+                       PLUGIN_PROVIDE(CUSTOM, "selinux"),
+       };
+       *features = f;
+       return countof(f);
+}
+
+METHOD(plugin_t, destroy, void,
+       private_selinux_plugin_t *this)
+{
+       this->listener->destroy(this->listener);
+       free(this);
+}
+
+/*
+ * Described in header
+ */
+plugin_t *selinux_plugin_create()
+{
+       private_selinux_plugin_t *this;
+
+       INIT(this,
+               .public = {
+                       .plugin = {
+                               .get_name = _get_name,
+                               .get_features = _get_features,
+                               .destroy = _destroy,
+                       },
+               },
+               .listener = selinux_listener_create(),
+       );
+
+       return &this->public.plugin;
+}
diff --git a/src/libcharon/plugins/selinux/selinux_plugin.h b/src/libcharon/plugins/selinux/selinux_plugin.h
new file mode 100644 (file)
index 0000000..46d413a
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 Tobias Brunner, codelabs GmbH
+ *
+ * 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.
+ */
+
+/**
+ * @defgroup selinux selinux
+ * @ingroup cplugins
+ *
+ * @defgroup selinux_plugin selinux_plugin
+ * @{ @ingroup selinux
+ */
+
+#ifndef SELINUX_PLUGIN_H_
+#define SELINUX_PLUGIN_H_
+
+#include <plugins/plugin.h>
+
+typedef struct selinux_plugin_t selinux_plugin_t;
+
+/**
+ * Plugin managing trap policies with generic SELinux labels.
+ */
+struct selinux_plugin_t {
+
+       /**
+        * Implements plugin interface
+        */
+       plugin_t plugin;
+};
+
+#endif /** SELINUX_PLUGIN_H_ @}*/