From: Tobias Brunner Date: Wed, 2 Feb 2022 14:51:06 +0000 (+0100) Subject: selinux: Add plugin to install trap policies with generic labels X-Git-Tag: 5.9.6rc1~3^2~13 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b00a4e778f9e9afe3d3bfbb1daa3463096d4c50f;p=thirdparty%2Fstrongswan.git selinux: Add plugin to install trap policies with generic labels 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. --- diff --git a/configure.ac b/configure.ac index ac07246125..86e50a3660 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/src/libcharon/Makefile.am b/src/libcharon/Makefile.am index 46346a44d4..fb6de8ebd7 100644 --- a/src/libcharon/Makefile.am +++ b/src/libcharon/Makefile.am @@ -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 index 0000000000..8bf821b5e6 --- /dev/null +++ b/src/libcharon/plugins/selinux/Makefile.am @@ -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 index 0000000000..bce309236c --- /dev/null +++ b/src/libcharon/plugins/selinux/selinux_listener.c @@ -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 +#include +#include + +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 index 0000000000..31deed2148 --- /dev/null +++ b/src/libcharon/plugins/selinux/selinux_listener.h @@ -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 + +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 index 0000000000..80919ea25f --- /dev/null +++ b/src/libcharon/plugins/selinux/selinux_plugin.c @@ -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 + +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 index 0000000000..46d413a9e4 --- /dev/null +++ b/src/libcharon/plugins/selinux/selinux_plugin.h @@ -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 + +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_ @}*/