@@ -97,6 +97,10 @@ config HAVE_LIBSYSTEMD
bool
option env="HAVE_LIBSYSTEMD"
+config HAVE_LIBSELINUX
+ bool
+ option env="HAVE_LIBSELINUX"
+
config HAVE_LIBCRYPTO
bool
option env="HAVE_LIBCRYPTO"
@@ -161,6 +165,19 @@ config SYSTEMD
comment "systemd support needs libsystemd"
depends on !HAVE_LIBSYSTEMD && HAVE_LINUX
+config SELINUX
+ bool "enable SELinux subprocess contexts"
+ depends on HAVE_LINUX && HAVE_LIBSELINUX
+ default n
+ help
+ Allow Suricatta, web-server and downloaders to use
+ contexts specified by selinux-context in their configuration
+ sections.
+ Requires libselinux and a policy permitting the transitions.
+
+comment "SELinux support needs libselinux"
+ depends on !HAVE_LIBSELINUX && HAVE_LINUX
+
config DEFAULT_CONFIG_FILE
string "File with default settings"
default "/etc/swupdate.cfg"
@@ -90,6 +90,14 @@ export HAVE_LIBSYSTEMD = n
endif
endif
+ifeq ($(HAVE_LIBSELINUX),)
+ifeq ($(HAVE_LINUX),y)
+export HAVE_LIBSELINUX = y
+else
+export HAVE_LIBSELINUX = n
+endif
+endif
+
ifeq ($(HAVE_LIBCRYPTO),)
export HAVE_LIBCRYPTO = y
endif
@@ -151,6 +151,10 @@ endif
# libconfig is always compiled
LDLIBS += config
+ifeq ($(CONFIG_SELINUX),y)
+LDLIBS += selinux
+endif
+
# libarchive
ifeq ($(CONFIG_ARCHIVE),y)
LDLIBS += archive
@@ -26,6 +26,10 @@
#include <parselib.h>
#include <swupdate_settings.h>
+#ifdef CONFIG_SELINUX
+#include <selinux/selinux.h>
+#endif
+
#ifndef WAIT_ANY
#define WAIT_ANY (-1)
#endif
@@ -125,6 +129,7 @@ void wait_threads_ready(void)
*/
static int spawn_process(struct swupdate_task *task,
uid_t run_as_userid, gid_t run_as_groupid,
+ const char *selinux_context,
const char *cfgname,
int ac, char **av,
swupdate_process start,
@@ -177,6 +182,27 @@ static int spawn_process(struct swupdate_task *task,
}
}
+ /* Apply the requested SELinux context before starting the child process */
+ if (selinux_context && selinux_context[0]) {
+#ifdef CONFIG_SELINUX
+ if (is_selinux_enabled() <= 0) {
+ WARN("Ignoring SELinux context for %s: SELinux is not enabled",
+ task->name);
+ } else if (security_check_context(selinux_context) < 0) {
+ ERROR("Cannot validate SELinux context %s for %s: %s",
+ selinux_context, task->name, strerror(errno));
+ return -1;
+ } else if (setcon(selinux_context) < 0) {
+ ERROR("Cannot set SELinux context %s for %s: %s",
+ selinux_context, task->name, strerror(errno));
+ return -1;
+ }
+#else
+ WARN("Ignoring SELinux context for %s: without SELinux support",
+ task->name);
+#endif
+ }
+
/* Save new pid */
pid = getpid();
@@ -209,6 +235,7 @@ static int spawn_process(struct swupdate_task *task,
static void start_swupdate_subprocess(sourcetype type, const char *name,
uid_t run_as_userid, gid_t run_as_groupid,
+ const char *selinux_context,
const char* cfgfile,
int argc, char **argv,
swupdate_process start,
@@ -216,7 +243,8 @@ static void start_swupdate_subprocess(sourcetype type, const char *name,
{
procs[nprocs].name = name;
procs[nprocs].type = type;
- if (spawn_process(&procs[nprocs], run_as_userid, run_as_groupid, cfgfile, argc, argv, start, cmdline) < 0) {
+ if (spawn_process(&procs[nprocs], run_as_userid, run_as_groupid,
+ selinux_context, cfgfile, argc, argv, start, cmdline) < 0) {
ERROR("Spawning %s failed, exiting process...", name);
exit(1);
}
@@ -232,17 +260,20 @@ void start_subprocess_from_file(sourcetype type, const char *name,
int argc, char **argv,
const char *cmdline)
{
- start_swupdate_subprocess(type, name, run_as_userid, run_as_groupid, cfgfile, argc, argv, NULL, cmdline);
+ start_swupdate_subprocess(type, name, run_as_userid, run_as_groupid,
+ NULL, cfgfile, argc, argv, NULL, cmdline);
}
void start_subprocess(sourcetype type, const char *name,
uid_t run_as_userid, gid_t run_as_groupid,
+ const char *selinux_context,
const char *cfgfile,
int argc, char **argv,
swupdate_process start)
{
- start_swupdate_subprocess(type, name, run_as_userid, run_as_groupid, cfgfile, argc, argv, start, NULL);
+ start_swupdate_subprocess(type, name, run_as_userid, run_as_groupid,
+ selinux_context, cfgfile, argc, argv, start, NULL);
}
/*
@@ -1146,8 +1146,11 @@ int main(int argc, char **argv)
if (opt_w) {
uid_t uid;
gid_t gid;
+ const char *context;
+ if (read_settings_selinux_context(&handle, "webserver", &context))
+ exit(EXIT_FAILURE);
read_settings_user_id(&handle, "webserver", &uid, &gid);
- start_subprocess(SOURCE_WEBSERVER, "webserver", uid, gid,
+ start_subprocess(SOURCE_WEBSERVER, "webserver", uid, gid, context,
cfgfname, ac, av,
start_webserver);
freeargs(av);
@@ -1158,8 +1161,11 @@ int main(int argc, char **argv)
if (opt_u) {
uid_t uid;
gid_t gid;
+ const char *context;
+ if (read_settings_selinux_context(&handle, "suricatta", &context))
+ exit(EXIT_FAILURE);
read_settings_user_id(&handle, "suricatta", &uid, &gid);
- start_subprocess(SOURCE_SURICATTA, "suricatta", uid, gid,
+ start_subprocess(SOURCE_SURICATTA, "suricatta", uid, gid, context,
cfgfname, argcount,
argvalues, start_suricatta);
@@ -1171,8 +1177,11 @@ int main(int argc, char **argv)
if (opt_d) {
uid_t uid;
gid_t gid;
+ const char *context;
+ if (read_settings_selinux_context(&handle, "download", &context))
+ exit(EXIT_FAILURE);
read_settings_user_id(&handle, "download", &uid, &gid);
- start_subprocess(SOURCE_DOWNLOADER, "download", uid, gid,
+ start_subprocess(SOURCE_DOWNLOADER, "download", uid, gid, context,
cfgfname, dwlac,
dwlav, start_download_server);
freeargs(dwlav);
@@ -1182,8 +1191,11 @@ int main(int argc, char **argv)
{
uid_t uid;
gid_t gid;
+ const char *context;
+ if (read_settings_selinux_context(&handle, "download", &context))
+ exit(EXIT_FAILURE);
read_settings_user_id(&handle, "download", &uid, &gid);
- start_subprocess(SOURCE_CHUNKS_DOWNLOADER, "chunks_downloader", uid, gid,
+ start_subprocess(SOURCE_CHUNKS_DOWNLOADER, "chunks_downloader", uid, gid, context,
cfgfname, 0, NULL,
start_delta_downloader);
}
@@ -147,6 +147,32 @@ int read_settings_user_id(swupdate_cfg_handle *handle, const char *module, uid_t
}
+int read_settings_selinux_context(swupdate_cfg_handle *handle, const char *module,
+ const char **context)
+{
+ config_setting_t *elem, *setting;
+
+ *context = NULL;
+ elem = find_settings_node(&handle->cfg, module);
+ if (!elem)
+ return 0;
+
+ setting = config_setting_get_member(elem, "selinux-context");
+ if (!setting)
+ return 0;
+
+ if (config_setting_type(setting) != CONFIG_TYPE_STRING) {
+ ERROR("%s.selinux-context must be a string", module);
+ return -EINVAL;
+ }
+
+ *context = config_setting_get_string(setting);
+ if (!(*context)[0])
+ *context = NULL;
+
+ return 0;
+}
+
/*
* Callback to be used to put a section of configuration
* into a dictionary
@@ -236,6 +236,32 @@ To configure the options:
make menuconfig
+SELinux subprocess contexts
+---------------------------
+
+Enable ``CONFIG_SELINUX`` (requires ``libselinux``) and set contexts
+in ``swupdate.cfg`` for Suricatta, the web server and downloaders::
+
+ suricatta : {
+ selinux-context = "system_u:system_r:swupdate_suricatta_t:s0";
+ };
+
+ webserver : {
+ selinux-context = "system_u:system_r:swupdate_webserver_t:s0";
+ };
+
+ download : {
+ selinux-context = "system_u:system_r:swupdate_download_t:s0";
+ };
+
+Both the downloader and the delta chunks downloader use ``download``.
+Omitted or empty settings preserve the inherited context.
+If built without SELinux support or SELinux is not enabled at runtime, a
+requested context is ignored with a warning. Otherwise, a context that cannot
+be validated or applied prevents the child from starting.
+
+Replace these example contexts with ones defined by your SELinux policy.
+
Building
--------
@@ -155,13 +155,15 @@ logcolors : {
# retries : integer
# Number of retries (0=forever)
# userid : integer
-# userID for Webserver process
+# userID for downloader process
# groupid : integer
-# groupId for Webserver process
+# groupId for downloader process
+# selinux-context : string
+# SELinux context for both regular and delta chunks downloaders
# user : string
-# user name for Webserver process, only if userid is 0 or unset
+# user name for downloader process, only if userid is 0 or unset
# group : string
-# group name for Webserver process, only if groupid is 0 or unset
+# group name for downloader process, only if groupid is 0 or unset
# timeout : integer
# it is the number of seconds that can be accepted without
# receiving any packets. If it elapses, the connection is
@@ -184,6 +186,7 @@ download :
url = "http://example.com/software.swu";
userid = 1000;
groupid = 1000;
+ # selinux-context = "system_u:system_r:swupdate_download_t:s0";
};
#
@@ -229,9 +232,11 @@ identify : (
# number of retries to connect to server
# retrywait : integer
# userid : integer
-# userID for Webserver process
+# userID for Suricatta process
# groupid : integer
-# groupId for Webserver process
+# groupId for Suricatta process
+# selinux-context : string
+# SELinux context for Suricatta process
# user : string
# user name for Webserver process, only if userid is 0 or unset
# group : string
@@ -287,6 +292,7 @@ suricatta :
loglevel = 10;
userid = 1000;
groupid = 1000;
+ # selinux-context = "system_u:system_r:swupdate_suricatta_t:s0";
enable = true;
initial-report-resend-period = 30;
connection-timeout = 10;
@@ -373,6 +379,8 @@ gservice :
# userID for Webserver process
# groupid : integer
# groupId for Webserver process
+# selinux-context : string
+# SELinux context for Webserver process
# listening_ports : integer
# Webserver listening port
# ssl_certificate : string
@@ -405,6 +413,7 @@ webserver :
document_root = "./www";
userid = 1000;
groupid = 1000;
+ # selinux-context = "system_u:system_r:swupdate_webserver_t:s0";
timeout = 20;
};
@@ -35,6 +35,7 @@ typedef server_op_res_t(*server_ipc_fn)(int fd);
void start_subprocess(sourcetype type, const char *name,
uid_t run_as_userid, gid_t run_as_groupid,
+ const char *selinux_context,
const char *cfgfile,
int argc, char **argv,
swupdate_process start);
@@ -23,4 +23,6 @@ int swupdate_cfg_read_file(swupdate_cfg_handle *handle, const char *filename);
void swupdate_cfg_destroy(swupdate_cfg_handle *handle);
int read_module_settings(swupdate_cfg_handle *handle, const char *module, settings_callback fcn, void *data);
int read_settings_user_id(swupdate_cfg_handle *handle, const char *module, uid_t *userid, gid_t *groupid);
+int read_settings_selinux_context(swupdate_cfg_handle *handle, const char *module,
+ const char **context);
int settings_into_dict(void *settings, void *data);
Add CONFIG_SELINUX and selinux-context settings for Suricatta, the web server and downloaders. Startup will fail if a context cannot be set. Signed-off-by: Ayoub Zaki <ayoub.zaki@embetrix.com> --- Kconfig | 17 +++++++++++++ Makefile.deps | 8 +++++++ Makefile.flags | 4 ++++ core/pctl.c | 37 ++++++++++++++++++++++++++--- core/swupdate.c | 20 ++++++++++++---- corelib/swupdate_settings.c | 26 ++++++++++++++++++++ doc/source/swupdate.rst | 26 ++++++++++++++++++++ examples/configuration/swupdate.cfg | 21 +++++++++++----- include/pctl.h | 1 + include/swupdate_settings.h | 2 ++ 10 files changed, 149 insertions(+), 13 deletions(-)