diff options
-rw-r--r-- | Makefile.am | 6 | ||||
-rw-r--r-- | man/systemd.exec.xml | 50 | ||||
-rw-r--r-- | src/core/dbus-execute.c | 79 | ||||
-rw-r--r-- | src/core/execute.c | 41 | ||||
-rw-r--r-- | src/core/execute.h | 1 | ||||
-rw-r--r-- | src/core/load-fragment-gperf.gperf.m4 | 2 | ||||
-rw-r--r-- | src/core/load-fragment.c | 55 | ||||
-rw-r--r-- | src/core/load-fragment.h | 1 | ||||
-rw-r--r-- | src/shared/seccomp-util.c | 63 | ||||
-rw-r--r-- | src/shared/seccomp-util.h | 26 | ||||
-rw-r--r-- | src/systemctl/systemctl.c | 42 |
11 files changed, 348 insertions, 18 deletions
diff --git a/Makefile.am b/Makefile.am index 8c309dabed..713a3aad93 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 86ad7e223d..01356e4c45 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -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> diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index 435c3d505f..a62f517dc6 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -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) - return -ENOMEM; +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) { - free(t); - t = d; + 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; } +#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 }; diff --git a/src/core/execute.c b/src/core/execute.c index 4033470e5b..06ddd5c91c 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -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", diff --git a/src/core/execute.h b/src/core/execute.h index baf430a047..06b6b3fb2d 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -168,6 +168,7 @@ struct ExecContext { bool same_pgrp; Set *syscall_filter; + Set *syscall_archs; int syscall_errno; bool syscall_whitelist:1; diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 16c9e25785..fa559e58f9 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -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) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 1b5856e273..ec04ad28ba 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -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, diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index 4cefa76157..23e9d1131b 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -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); diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c new file mode 100644 index 0000000000..ee39cc7c1d --- /dev/null +++ b/src/shared/seccomp-util.c @@ -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; +} diff --git a/src/shared/seccomp-util.h b/src/shared/seccomp-util.h new file mode 100644 index 0000000000..6b63902f5d --- /dev/null +++ b/src/shared/seccomp-util.h @@ -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); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index eb2d3f14fe..edd4daaa24 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -3405,6 +3405,48 @@ static int print_property(const char *name, sd_bus_message *m, const char *conte 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; } break; |