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.
This commit is contained in:
Tobias Brunner
2022-04-14 18:42:01 +02:00
parent 065685dde7
commit b00a4e778f
7 changed files with 500 additions and 0 deletions
+3
View 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
+7
View 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
+18
View File
@@ -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
@@ -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;
}
@@ -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_ @}*/
@@ -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;
}
@@ -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_ @}*/