core: add SystemCallArchitectures= unit setting to allow disabling of non-native

architecture support for system calls

Also, turn system call filter bus properties into complex types instead
of concatenated strings.
This commit is contained in:
Lennart Poettering 2014-02-13 00:24:00 +01:00
parent 351a19b17d
commit 57183d117a
11 changed files with 348 additions and 18 deletions

View file

@ -770,6 +770,12 @@ nodist_libsystemd_shared_la_SOURCES = \
src/shared/errno-from-name.h \
src/shared/errno-to-name.h
if HAVE_SECCOMP
libsystemd_shared_la_SOURCES += \
src/shared/seccomp-util.h \
src/shared/seccomp-util.c
endif
# ------------------------------------------------------------------------------
noinst_LTLIBRARIES += \
libsystemd-units.la

View file

@ -1050,6 +1050,14 @@
<function>write</function> will be
removed from the set).
</para></listitem>
<para>Note that setting
<varname>SystemCallFilter=</varname>
implies a
<varname>SystemCallArchitectures=</varname>
setting of <literal>native</literal>
(see below), unless that option is
configured otherwise.</para>
</varlistentry>
<varlistentry>
@ -1072,6 +1080,48 @@
is triggered.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>SystemCallArchitectures=</varname></term>
<listitem><para>Takes a space
separated list of architecture
identifiers to include in the system
call filter. The known architecture
identifiers are
<literal>x86</literal>,
<literal>x86-64</literal>,
<literal>x32</literal>,
<literal>arm</literal> as well as the
special identifier
<literal>native</literal>. Only system
calls of the specified architectures
will be permitted to processes of this
unit. This is an effective way to
disable compatibility with non-native
architectures for processes, for
example to prohibit execution of 32bit
x86 binaries on 64bit x86-64
systems. The special
<literal>native</literal> identifier
implicitly maps to the native
architecture of the system (or more
strictly: to the architecture the
system manager is compiled for). Note
that setting this option to a
non-empty list implies that
<literal>native</literal> is included
too. By default this option is set to
the empty list, i.e. no architecture
system call filtering is applied. Note
that configuring a system call filter
with
<varname>SystemCallFilter=</varname>
(above) implies a
<literal>native</literal> architecture
list, unless configured
otherwise.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>

View file

@ -21,6 +21,10 @@
#include <sys/prctl.h>
#ifdef HAVE_SECCOMP
#include <seccomp.h>
#endif
#include "bus-util.h"
#include "missing.h"
#include "ioprio.h"
@ -31,6 +35,10 @@
#include "capability.h"
#include "env-util.h"
#ifdef HAVE_SECCOMP
#include "seccomp-util.h"
#endif
BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_exec_output, exec_output, ExecOutput);
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_input, exec_input, ExecInput);
@ -349,17 +357,25 @@ static int property_get_syscall_filter(
ExecContext *c = userdata;
_cleanup_strv_free_ char **l = NULL;
_cleanup_free_ char *t = NULL;
int r;
#ifdef HAVE_SECCOMP
Iterator i;
void *id;
int r;
#endif
assert(bus);
assert(reply);
assert(c);
r = sd_bus_message_open_container(reply, 'r', "bas");
if (r < 0)
return r;
r = sd_bus_message_append(reply, "b", c->syscall_whitelist);
if (r < 0)
return r;
#ifdef HAVE_SECCOMP
SET_FOREACH(id, c->syscall_filter, i) {
char *name;
@ -378,22 +394,56 @@ static int property_get_syscall_filter(
strv_sort(l);
t = strv_join(l, " ");
if (!t)
return -ENOMEM;
r = sd_bus_message_append_strv(reply, l);
if (r < 0)
return r;
if (!c->syscall_whitelist) {
char *d;
return sd_bus_message_close_container(reply);
}
d = strappend("~", t);
if (!d)
static int property_get_syscall_archs(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
ExecContext *c = userdata;
_cleanup_strv_free_ char **l = NULL;
int r;
#ifdef HAVE_SECCOMP
Iterator i;
void *id;
#endif
assert(bus);
assert(reply);
assert(c);
#ifdef HAVE_SECCOMP
SET_FOREACH(id, c->syscall_archs, i) {
const char *name;
name = seccomp_arch_to_string(PTR_TO_UINT32(id) - 1);
if (!name)
continue;
r = strv_extend(&l, name);
if (r < 0)
return -ENOMEM;
free(t);
t = d;
}
#endif
return sd_bus_message_append(reply, "s", t);
strv_sort(l);
r = sd_bus_message_append_strv(reply, l);
if (r < 0)
return r;
return 0;
}
static int property_get_syscall_errno(
@ -476,7 +526,8 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("SELinuxContext", "s", NULL, offsetof(ExecContext, selinux_context), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("IgnoreSIGPIPE", "b", bus_property_get_bool, offsetof(ExecContext, ignore_sigpipe), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("NoNewPrivileges", "b", bus_property_get_bool, offsetof(ExecContext, no_new_privileges), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SystemCallFilter", "s", property_get_syscall_filter, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SystemCallFilter", "(bas)", property_get_syscall_filter, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SystemCallArchitectures", "as", property_get_syscall_archs, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SystemCallErrorNumber", "i", property_get_syscall_errno, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_VTABLE_END
};

View file

@ -77,6 +77,10 @@
#include "selinux-util.h"
#include "errno-list.h"
#ifdef HAVE_SECCOMP
#include "seccomp-util.h"
#endif
#define IDLE_TIMEOUT_USEC (5*USEC_PER_SEC)
#define IDLE_TIMEOUT2_USEC (1*USEC_PER_SEC)
@ -953,8 +957,17 @@ static int apply_seccomp(ExecContext *c) {
if (!seccomp)
return -ENOMEM;
action = c->syscall_whitelist ? SCMP_ACT_ALLOW : negative_action;
SET_FOREACH(id, c->syscall_archs, i) {
r = seccomp_arch_add(seccomp, PTR_TO_UINT32(id) - 1);
if (r == -EEXIST)
continue;
if (r < 0) {
seccomp_release(seccomp);
return r;
}
}
action = c->syscall_whitelist ? SCMP_ACT_ALLOW : negative_action;
SET_FOREACH(id, c->syscall_filter, i) {
r = seccomp_rule_add(seccomp, action, PTR_TO_INT(id) - 1, 0);
if (r < 0) {
@ -1548,7 +1561,7 @@ int exec_spawn(ExecCommand *command,
}
#ifdef HAVE_SECCOMP
if (context->syscall_filter) {
if (context->syscall_filter || context->syscall_archs) {
err = apply_seccomp(context);
if (err < 0) {
r = EXIT_SECCOMP;
@ -1740,6 +1753,9 @@ void exec_context_done(ExecContext *c) {
#ifdef HAVE_SECCOMP
set_free(c->syscall_filter);
c->syscall_filter = NULL;
set_free(c->syscall_archs);
c->syscall_archs = NULL;
#endif
}
@ -2122,7 +2138,7 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
#endif
fprintf(f,
"%sSystemCallFilter: \n",
"%sSystemCallFilter: ",
prefix);
if (!c->syscall_whitelist)
@ -2137,7 +2153,7 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
else
fputc(' ', f);
name = seccomp_syscall_resolve_num_arch(PTR_TO_INT(id)-1, SCMP_ARCH_NATIVE);
name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
fputs(strna(name), f);
}
#endif
@ -2145,6 +2161,23 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
fputc('\n', f);
}
if (c->syscall_archs) {
#ifdef HAVE_SECCOMP
Iterator j;
void *id;
#endif
fprintf(f,
"%sSystemCallArchitectures:",
prefix);
#ifdef HAVE_SECCOMP
SET_FOREACH(id, c->syscall_archs, j)
fprintf(f, " %s", strna(seccomp_arch_to_string(PTR_TO_UINT32(id) - 1)));
#endif
fputc('\n', f);
}
if (c->syscall_errno != 0)
fprintf(f,
"%sSystemCallErrorNumber: %s\n",

View file

@ -168,6 +168,7 @@ struct ExecContext {
bool same_pgrp;
Set *syscall_filter;
Set *syscall_archs;
int syscall_errno;
bool syscall_whitelist:1;

View file

@ -51,8 +51,10 @@ $1.TimerSlackNSec, config_parse_nsec, 0,
$1.NoNewPrivileges, config_parse_bool, 0, offsetof($1, exec_context.no_new_privileges)
m4_ifdef(`HAVE_SECCOMP',
`$1.SystemCallFilter, config_parse_syscall_filter, 0, offsetof($1, exec_context)
$1.SystemCallArchitectures, config_parse_syscall_archs, 0, offsetof($1, exec_context)
$1.SystemCallErrorNumber, config_parse_syscall_errno, 0, offsetof($1, exec_context)',
`$1.SystemCallFilter, config_parse_warn_compat, 0, 0
$1.SystemCallArchitectures, config_parse_warn_compat, 0, 0
$1.SystemCallErrorNumber, config_parse_warn_compat, 0, 0')
$1.LimitCPU, config_parse_limit, RLIMIT_CPU, offsetof($1, exec_context.rlimit)
$1.LimitFSIZE, config_parse_limit, RLIMIT_FSIZE, offsetof($1, exec_context.rlimit)

View file

@ -57,6 +57,10 @@
#include "bus-error.h"
#include "errno-list.h"
#ifdef HAVE_SECCOMP
#include "seccomp-util.h"
#endif
#if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP)
int config_parse_warn_compat(
const char *unit,
@ -2029,6 +2033,57 @@ int config_parse_syscall_filter(
return 0;
}
int config_parse_syscall_archs(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
ExecContext *c = data;
char *w, *state;
size_t l;
int r;
if (isempty(rvalue)) {
set_free(c->syscall_archs);
c->syscall_archs = NULL;
return 0;
}
r = set_ensure_allocated(&c->syscall_archs, trivial_hash_func, trivial_compare_func);
if (r < 0)
return log_oom();
FOREACH_WORD_QUOTED(w, l, rvalue, state) {
_cleanup_free_ char *t = NULL;
uint32_t a;
t = strndup(w, l);
if (!t)
return log_oom();
r = seccomp_arch_from_string(t, &a);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse system call architecture, ignoring: %s", t);
continue;
}
r = set_put(c->syscall_archs, UINT32_TO_PTR(a + 1));
if (r == -EEXIST)
continue;
if (r < 0)
return log_oom();
}
return 0;
}
int config_parse_syscall_errno(
const char *unit,
const char *filename,

View file

@ -74,6 +74,7 @@ int config_parse_notify_access(const char *unit, const char *filename, unsigned
int config_parse_start_limit_action(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_unit_requires_mounts_for(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_syscall_filter(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_syscall_archs(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_syscall_errno(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_environ(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_unit_slice(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);

63
src/shared/seccomp-util.c Normal file
View file

@ -0,0 +1,63 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2014 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <seccomp.h>
#include "util.h"
#include "seccomp-util.h"
const char* seccomp_arch_to_string(uint32_t c) {
if (c == SCMP_ARCH_NATIVE)
return "native";
if (c == SCMP_ARCH_X86)
return "x86";
if (c == SCMP_ARCH_X86_64)
return "x86-64";
if (c == SCMP_ARCH_X32)
return "x32";
if (c == SCMP_ARCH_ARM)
return "arm";
return NULL;
}
int seccomp_arch_from_string(const char *n, uint32_t *ret) {
if (!n)
return -EINVAL;
assert(ret);
if (streq(n, "native"))
*ret = SCMP_ARCH_NATIVE;
else if (streq(n, "x86"))
*ret = SCMP_ARCH_X86;
else if (streq(n, "x86-64"))
*ret = SCMP_ARCH_X86_64;
else if (streq(n, "x32"))
*ret = SCMP_ARCH_X32;
else if (streq(n, "arm"))
*ret = SCMP_ARCH_ARM;
else
return -EINVAL;
return 0;
}

26
src/shared/seccomp-util.h Normal file
View file

@ -0,0 +1,26 @@
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
#pragma once
/***
This file is part of systemd.
Copyright 2014 Lennart Poettering
systemd is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
const char* seccomp_arch_to_string(uint32_t c);
int seccomp_arch_from_string(const char *n, uint32_t *ret);

View file

@ -3404,6 +3404,48 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte
if (arg_all || !isempty(a) || !isempty(b))
printf("%s=%s \"%s\"\n", name, strempty(a), strempty(b));
return 0;
} else if (streq_ptr(name, "SystemCallFilter")) {
_cleanup_strv_free_ char **l = NULL;
int whitelist;
r = sd_bus_message_enter_container(m, 'r', "bas");
if (r < 0)
return bus_log_parse_error(r);
r = sd_bus_message_read(m, "b", &whitelist);
if (r < 0)
return bus_log_parse_error(r);
r = sd_bus_message_read_strv(m, &l);
if (r < 0)
return bus_log_parse_error(r);
r = sd_bus_message_exit_container(m);
if (r < 0)
return bus_log_parse_error(r);
if (arg_all || whitelist || !strv_isempty(l)) {
bool first = true;
char **i;
fputs(name, stdout);
fputc('=', stdout);
if (!whitelist)
fputc('~', stdout);
STRV_FOREACH(i, l) {
if (first)
first = false;
else
fputc(' ', stdout);
fputs(*i, stdout);
}
fputc('\n', stdout);
}
return 0;
}