From: Michal Sekletar Date: Thu, 24 Jul 2014 08:40:28 +0000 (+0200) Subject: socket: introduce SELinuxLabelViaNet option X-Git-Tag: v216~26 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=commitdiff_plain;h=cf8bd44339b00330fdbc91041d6731ba8aba9fec;hp=6c3e68e7c1adc6176526e69769bf2eba86cdd257 socket: introduce SELinuxLabelViaNet option 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 --- diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml index 448390583..f376f725c 100644 --- a/man/systemd.socket.xml +++ b/man/systemd.socket.xml @@ -675,6 +675,17 @@ for details. + + SELinuxLabelViaNet= + 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. Configuration option has effect only + on sockets with Accept= + mode set to yes. + + PipeSize= Takes a size in diff --git a/src/core/execute.c b/src/core/execute.c index d8452a666..129791294 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" @@ -1729,6 +1730,22 @@ int exec_spawn(ExecCommand *command, goto fail_child; } } + + if (context->selinux_label_via_net && use_selinux()) { + _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 #ifdef HAVE_APPARMOR @@ -2112,7 +2129,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" + "%sSELinuxLabelViaNet: %s\n", prefix, c->umask, prefix, c->working_directory ? c->working_directory : "/", prefix, c->root_directory ? c->root_directory : "/", @@ -2122,7 +2140,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_label_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 9d05d3a9d..d23a98097 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_label_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 b4e2b2574..d5ff848c3 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.SELinuxLabelViaNet, config_parse_bool, 0, offsetof(Socket, selinux_label_via_net)', +`Socket.SELinuxLabelViaNet, 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 a16b20d73..34ce1b1ff 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -31,6 +31,10 @@ #include #include +#ifdef HAVE_SELINUX +#include +#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" + "%sSELinuxLabelViaNet: %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_label_via_net)); if (s->control_pid > 0) fprintf(f, @@ -1130,7 +1136,14 @@ static int socket_open_fds(Socket *s) { continue; if (p->type == SOCKET_SOCKET) { - +#ifdef HAVE_SELINUX + if (!know_label && s->selinux_label_via_net) { + r = getcon(&label); + if (r < 0) + return r; + know_label = true; + } +#endif if (!know_label) { r = socket_instantiate_service(s); @@ -1829,6 +1842,9 @@ static void socket_enter_running(Socket *s, int cfd) { cfd = -1; s->n_connections ++; + if (s->selinux_label_via_net) + service->exec_context.selinux_label_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 eede70564..ab342c34e 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_label_via_net; + char *user, *group; }; diff --git a/src/shared/label.c b/src/shared/label.c index 25a8b361b..dd89bec6e 100644 --- a/src/shared/label.c +++ b/src/shared/label.c @@ -31,6 +31,7 @@ #ifdef HAVE_SELINUX #include #include +#include #endif #include "label.h" @@ -243,6 +244,74 @@ 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 = 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 72948205f..4163f7f98 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);