chiark / gitweb /
core: add SystemCallArchitectures= unit setting to allow disabling of non-native
authorLennart Poettering <lennart@poettering.net>
Wed, 12 Feb 2014 23:24:00 +0000 (00:24 +0100)
committerLennart Poettering <lennart@poettering.net>
Wed, 12 Feb 2014 23:24:00 +0000 (00:24 +0100)
architecture support for system calls

Also, turn system call filter bus properties into complex types instead
of concatenated strings.

Makefile.am
man/systemd.exec.xml
src/core/dbus-execute.c
src/core/execute.c
src/core/execute.h
src/core/load-fragment-gperf.gperf.m4
src/core/load-fragment.c
src/core/load-fragment.h
src/shared/seccomp-util.c [new file with mode: 0644]
src/shared/seccomp-util.h [new file with mode: 0644]
src/systemctl/systemctl.c

index 8c309da..713a3aa 100644 (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
index 86ad7e2..01356e4 100644 (file)
                                 <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>
                                 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>
 
index 435c3d5..a62f517 100644 (file)
 
 #include <sys/prctl.h>
 
+#ifdef HAVE_SECCOMP
+#include <seccomp.h>
+#endif
+
 #include "bus-util.h"
 #include "missing.h"
 #include "ioprio.h"
 #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
 };
index 4033470..06ddd5c 100644 (file)
 #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",
index baf430a..06b6b3f 100644 (file)
@@ -168,6 +168,7 @@ struct ExecContext {
         bool same_pgrp;
 
         Set *syscall_filter;
+        Set *syscall_archs;
         int syscall_errno;
         bool syscall_whitelist:1;
 
index 16c9e25..fa559e5 100644 (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)
index 1b5856e..ec04ad2 100644 (file)
 #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,
index 4cefa76..23e9d11 100644 (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);
diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c
new file mode 100644 (file)
index 0000000..ee39cc7
--- /dev/null
@@ -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 (file)
index 0000000..6b63902
--- /dev/null
@@ -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);
index eb2d3f1..edd4daa 100644 (file)
@@ -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;