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 <pmoore@redhat.com>
This commit is contained in:
Michal Sekletar 2014-07-24 10:40:28 +02:00
parent 6c3e68e7c1
commit cf8bd44339
8 changed files with 127 additions and 5 deletions

View File

@ -675,6 +675,17 @@
for details.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>SELinuxLabelViaNet=</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. Configuration option has effect only
on sockets with <literal>Accept=</literal>
mode set to <literal>yes</literal>.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>PipeSize=</varname></term>
<listitem><para>Takes a size in

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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"
"%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;

View File

@ -165,6 +165,8 @@ struct Socket {
char *smack_ip_in;
char *smack_ip_out;
bool selinux_label_via_net;
char *user, *group;
};

View File

@ -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,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;

View File

@ -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);