This makes possible to spawn service instances triggered by socket with MLS/MCS SELinux labels which are created based on information provided by connected peer.
Implementation of label_get_child_label derived from xinetd. Reviewed-by: Paul Moore <pmo...@redhat.com> --- Changes in v2: * make possible to use both SELinuxContextViaNet and SELinuxContext at the same time * provide better explanation how is resulting SELinux context figured out * fix some of the issues pointed out by Lennart Future work: * find out nicer way how to pass information to the triggered service unit that SELinuxContextViaNet is enabled on the socket man/systemd.socket.xml | 23 +++++++++++ src/core/execute.c | 35 ++++++++++++---- src/core/execute.h | 1 + src/core/load-fragment-gperf.gperf.m4 | 3 ++ src/core/socket.c | 22 ++++++++-- src/core/socket.h | 2 + src/shared/label.c | 77 +++++++++++++++++++++++++++++++++++ src/shared/label.h | 1 + 8 files changed, 154 insertions(+), 10 deletions(-) diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml index 238029a..0ff4ef4 100644 --- a/man/systemd.socket.xml +++ b/man/systemd.socket.xml @@ -676,6 +676,29 @@ </varlistentry> <varlistentry> + <term><varname>SELinuxContextViaNet=</varname></term> + <listitem><para>Takes a boolean + value. Controls whether systemd attempts to figure out + SELinux label used for instantiated service from + information handed by peer over the + network. From information provided by + peer we actually use only security level. + Other parts of resulting SELinux context + originate from either the target binary + effectively triggered by socket unit or + it is the value of + <varname>SELinuxContext</varname> + option. Configuration option has effect + only on sockets with + <varname>Accept</varname> mode set to + <literal>yes</literal>. Also note that + this option is usefull only when MLS/MCS + SELinux policy is deployed. Defaults to + <literal>false</literal>. + </para></listitem> + </varlistentry> + + <varlistentry> <term><varname>PipeSize=</varname></term> <listitem><para>Takes a size in bytes. Controls the pipe buffer size diff --git a/src/core/execute.c b/src/core/execute.c index d8452a6..e006fdb 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -83,6 +83,7 @@ #include "af-list.h" #include "mkdir.h" #include "apparmor-util.h" +#include "label.h" #ifdef HAVE_SECCOMP #include "seccomp-util.h" @@ -1722,11 +1723,29 @@ int exec_spawn(ExecCommand *command, #endif #ifdef HAVE_SELINUX - if (context->selinux_context && use_selinux()) { - err = setexeccon(context->selinux_context); - if (err < 0 && !context->selinux_context_ignore) { - r = EXIT_SELINUX_CONTEXT; - goto fail_child; + if (use_selinux()) { + if (context->selinux_context) { + err = setexeccon(context->selinux_context); + if (err < 0 && !context->selinux_context_ignore) { + r = EXIT_SELINUX_CONTEXT; + goto fail_child; + } + } + + if (context->selinux_context_via_net && socket_fd >= 0) { + _cleanup_free_ char *label = NULL; + + err = label_get_child_label(socket_fd, command->path, &label); + if (err < 0) { + r = EXIT_SELINUX_CONTEXT; + goto fail_child; + } + + err = setexeccon(label); + if (err < 0) { + r = EXIT_SELINUX_CONTEXT; + goto fail_child; + } } } #endif @@ -2112,7 +2131,8 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { "%sPrivateDevices: %s\n" "%sProtectHome: %s\n" "%sProtectSystem: %s\n" - "%sIgnoreSIGPIPE: %s\n", + "%sIgnoreSIGPIPE: %s\n" + "%sSELinuxContextViaNet: %s\n", prefix, c->umask, prefix, c->working_directory ? c->working_directory : "/", prefix, c->root_directory ? c->root_directory : "/", @@ -2122,7 +2142,8 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { prefix, yes_no(c->private_devices), prefix, protect_home_to_string(c->protect_home), prefix, protect_system_to_string(c->protect_system), - prefix, yes_no(c->ignore_sigpipe)); + prefix, yes_no(c->ignore_sigpipe), + prefix, yes_no(c->selinux_context_via_net)); STRV_FOREACH(e, c->environment) fprintf(f, "%sEnvironment: %s\n", prefix, *e); diff --git a/src/core/execute.h b/src/core/execute.h index 9d05d3a..29eddbf 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -136,6 +136,7 @@ struct ExecContext { bool selinux_context_ignore; char *selinux_context; + bool selinux_context_via_net; bool apparmor_profile_ignore; char *apparmor_profile; diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 24aa80d..a0a1939 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -262,6 +262,9 @@ Socket.SmackLabelIPOut, config_parse_string, 0, `Socket.SmackLabel, config_parse_warn_compat, 0, 0 Socket.SmackLabelIPIn, config_parse_warn_compat, 0, 0 Socket.SmackLabelIPOut, config_parse_warn_compat, 0, 0') +m4_ifdef(`HAVE_SELINUX', +`Socket.SELinuxContextViaNet, config_parse_bool, 0, offsetof(Socket, selinux_context_via_net)', +`Socket.SELinuxContextViaNet, config_parse_warn_compat, 0, 0') EXEC_CONTEXT_CONFIG_ITEMS(Socket)m4_dnl CGROUP_CONTEXT_CONFIG_ITEMS(Socket)m4_dnl KILL_CONTEXT_CONFIG_ITEMS(Socket)m4_dnl diff --git a/src/core/socket.c b/src/core/socket.c index 1189f45..80a5d6e 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -31,6 +31,10 @@ #include <mqueue.h> #include <sys/xattr.h> +#ifdef HAVE_SELINUX +#include <selinux/selinux.h> +#endif + #include "sd-event.h" #include "log.h" #include "load-dropin.h" @@ -488,7 +492,8 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { "%sPassCredentials: %s\n" "%sPassSecurity: %s\n" "%sTCPCongestion: %s\n" - "%sRemoveOnStop: %s\n", + "%sRemoveOnStop: %s\n" + "%sSELinuxContextViaNet: %s\n", prefix, socket_state_to_string(s->state), prefix, socket_result_to_string(s->result), prefix, socket_address_bind_ipv6_only_to_string(s->bind_ipv6_only), @@ -503,7 +508,8 @@ static void socket_dump(Unit *u, FILE *f, const char *prefix) { prefix, yes_no(s->pass_cred), prefix, yes_no(s->pass_sec), prefix, strna(s->tcp_congestion), - prefix, yes_no(s->remove_on_stop)); + prefix, yes_no(s->remove_on_stop), + prefix, yes_no(s->selinux_context_via_net)); if (s->control_pid > 0) fprintf(f, @@ -1127,7 +1133,14 @@ static int socket_open_fds(Socket *s) { continue; if (p->type == SOCKET_SOCKET) { - +#ifdef HAVE_SELINUX + if (!know_label && s->selinux_context_via_net) { + r = getcon(&label); + if (r < 0) + return r; + know_label = true; + } +#endif if (!know_label) { r = socket_instantiate_service(s); @@ -1826,6 +1839,9 @@ static void socket_enter_running(Socket *s, int cfd) { cfd = -1; s->n_connections ++; + if (s->selinux_context_via_net) + service->exec_context.selinux_context_via_net = true; + r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, true, &error, NULL); if (r < 0) goto fail; diff --git a/src/core/socket.h b/src/core/socket.h index eede705..b52b844 100644 --- a/src/core/socket.h +++ b/src/core/socket.h @@ -165,6 +165,8 @@ struct Socket { char *smack_ip_in; char *smack_ip_out; + bool selinux_context_via_net; + char *user, *group; }; diff --git a/src/shared/label.c b/src/shared/label.c index 25a8b36..100c2f0 100644 --- a/src/shared/label.c +++ b/src/shared/label.c @@ -31,6 +31,7 @@ #ifdef HAVE_SELINUX #include <selinux/selinux.h> #include <selinux/label.h> +#include <selinux/context.h> #endif #include "label.h" @@ -243,6 +244,82 @@ fail: return r; } +int label_get_child_label(int socket_fd, const char *exe, char **label) { + int r = 0; + +#ifdef HAVE_SELINUX + + security_context_t mycon = NULL, peercon = NULL, fcon = NULL, ret = NULL; + security_class_t sclass; + context_t pcon = NULL, bcon = NULL; + const char *range = NULL; + + assert(socket_fd >= 0); + assert(exe); + assert(label); + + r = getcon(&mycon); + if (r < 0) + goto out; + + r = getpeercon(socket_fd, &peercon); + if (r < 0) + goto out; + + r = getexeccon(&fcon); + if (r < 0) + goto out; + + if (!fcon) { + /* If there is no context set for next exec let's use context + of target executable */ + r = getfilecon(exe, &fcon); + if (r < 0) + goto out; + } + + bcon = context_new(mycon); + if (!bcon) + goto out; + + pcon = context_new(peercon); + if (!pcon) + goto out; + + range = context_range_get(pcon); + if (!range) + goto out; + + r = context_range_set(bcon, range); + if (r) + goto out; + + freecon(mycon); + mycon = context_str(bcon); + if (!mycon) + goto out; + + sclass = string_to_security_class("process"); + r = security_compute_create(mycon, fcon, sclass, &ret); + if (r < 0) + goto out; + + *label = ret; + +out: + if (r && security_getenforce() == 1) + r = -errno; + + freecon(mycon); + freecon(peercon); + freecon(fcon); + context_free(pcon); + context_free(bcon); + +#endif + return r; +} + int label_context_set(const char *path, mode_t mode) { int r = 0; diff --git a/src/shared/label.h b/src/shared/label.h index 7294820..4163f7f 100644 --- a/src/shared/label.h +++ b/src/shared/label.h @@ -39,6 +39,7 @@ void label_context_clear(void); void label_free(const char *label); int label_get_create_label_from_exe(const char *exe, char **label); +int label_get_child_label(int socket_fd, const char *exec, char **label); int label_mkdir(const char *path, mode_t mode); -- 2.0.1 _______________________________________________ systemd-devel mailing list systemd-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/systemd-devel