From c0467cf387548dc98c0254f63553d862b35a84e5 Mon Sep 17 00:00:00 2001 From: Ronny Chevalier Date: Wed, 12 Feb 2014 01:29:54 +0100 Subject: syscallfilter: port to libseccomp --- src/core/build.h | 8 +++- src/core/dbus-execute.c | 8 +--- src/core/execute.c | 88 ++++++++++++++++++++------------------------ src/core/execute.h | 12 +++++- src/core/load-fragment.c | 96 +++++++++++++++++++++++++++++++----------------- 5 files changed, 122 insertions(+), 90 deletions(-) (limited to 'src/core') diff --git a/src/core/build.h b/src/core/build.h index 4513a0bad7..f04f03f60d 100644 --- a/src/core/build.h +++ b/src/core/build.h @@ -81,4 +81,10 @@ #define _XZ_FEATURE_ "-XZ" #endif -#define SYSTEMD_FEATURES _PAM_FEATURE_ " " _LIBWRAP_FEATURE_ " " _AUDIT_FEATURE_ " " _SELINUX_FEATURE_ " " _IMA_FEATURE_ " " _SYSVINIT_FEATURE_ " " _LIBCRYPTSETUP_FEATURE_ " " _GCRYPT_FEATURE_ " " _ACL_FEATURE_ " " _XZ_FEATURE_ +#ifdef HAVE_SECCOMP +#define _SECCOMP_FEATURE_ "+SECCOMP" +#else +#define _SECCOMP_FEATURE_ "-SECCOMP" +#endif + +#define SYSTEMD_FEATURES _PAM_FEATURE_ " " _LIBWRAP_FEATURE_ " " _AUDIT_FEATURE_ " " _SELINUX_FEATURE_ " " _IMA_FEATURE_ " " _SYSVINIT_FEATURE_ " " _LIBCRYPTSETUP_FEATURE_ " " _GCRYPT_FEATURE_ " " _ACL_FEATURE_ " " _XZ_FEATURE_ _SECCOMP_FEATURE_ diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index db16990233..2ed7a3c73e 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -25,7 +25,6 @@ #include "missing.h" #include "ioprio.h" #include "strv.h" -#include "syscall-list.h" #include "fileio.h" #include "execute.h" #include "dbus-execute.h" @@ -354,10 +353,7 @@ static int property_get_syscall_filter( assert(reply); assert(c); - if (c->syscall_filter) - return sd_bus_message_append_array(reply, 'u', c->syscall_filter, (syscall_max() + 31) >> 4); - else - return sd_bus_message_append_array(reply, 'u', NULL, 0); + return sd_bus_message_append(reply, "s", c->syscall_filter_string); } const sd_bus_vtable bus_exec_vtable[] = { @@ -422,7 +418,7 @@ 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", "au", property_get_syscall_filter, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("SystemCallFilter", "s", property_get_syscall_filter, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_VTABLE_END }; diff --git a/src/core/execute.c b/src/core/execute.c index b941a024de..d2e5b740b7 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -38,9 +38,13 @@ #include #include #include -#include #include #include +#ifdef HAVE_SECCOMP +#include + +#include "set.h" +#endif #undef basename #ifdef HAVE_PAM @@ -67,7 +71,6 @@ #include "utmp-wtmp.h" #include "def.h" #include "path-util.h" -#include "syscall-list.h" #include "env-util.h" #include "fileio.h" #include "unit.h" @@ -933,57 +936,32 @@ static void rename_process_from_path(const char *path) { rename_process(process_name); } -static int apply_seccomp(uint32_t *syscall_filter) { - static const struct sock_filter header[] = { - VALIDATE_ARCHITECTURE, - EXAMINE_SYSCALL - }; - static const struct sock_filter footer[] = { - _KILL_PROCESS - }; - - int i; - unsigned n; - struct sock_filter *f; - struct sock_fprog prog = {}; - - assert(syscall_filter); +#ifdef HAVE_SECCOMP +static int apply_seccomp(ExecContext *c) { + uint32_t action = SCMP_ACT_ALLOW; + Iterator i; + void *id; - /* First: count the syscalls to check for */ - for (i = 0, n = 0; i < syscall_max(); i++) - if (syscall_filter[i >> 4] & (1 << (i & 31))) - n++; - - /* Second: build the filter program from a header the syscall - * matches and the footer */ - f = alloca(sizeof(struct sock_filter) * (ELEMENTSOF(header) + 2*n + ELEMENTSOF(footer))); - memcpy(f, header, sizeof(header)); - - for (i = 0, n = 0; i < syscall_max(); i++) - if (syscall_filter[i >> 4] & (1 << (i & 31))) { - struct sock_filter item[] = { - BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, INDEX_TO_SYSCALL(i), 0, 1), - BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW) - }; + assert(c); - assert_cc(ELEMENTSOF(item) == 2); + c->syscall_filter = seccomp_init(c->syscall_filter_default_action); + if (!c->syscall_filter) + return -1; - f[ELEMENTSOF(header) + 2*n] = item[0]; - f[ELEMENTSOF(header) + 2*n+1] = item[1]; + if (c->syscall_filter_default_action == SCMP_ACT_ALLOW) + action = SCMP_ACT_KILL; - n++; + SET_FOREACH(id, c->filtered_syscalls, i) { + int r = seccomp_rule_add(c->syscall_filter, action, PTR_TO_INT(id) - 1, 0); + if (r < 0) { + log_error("Failed to add syscall filter"); + return r; } + } - memcpy(f + (ELEMENTSOF(header) + 2*n), footer, sizeof(footer)); - - /* Third: install the filter */ - prog.len = ELEMENTSOF(header) + ELEMENTSOF(footer) + 2*n; - prog.filter = f; - if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) < 0) - return -errno; - - return 0; + return seccomp_load(c->syscall_filter); } +#endif static void do_idle_pipe_dance(int idle_pipe[4]) { assert(idle_pipe); @@ -1562,13 +1540,15 @@ int exec_spawn(ExecCommand *command, goto fail_child; } - if (context->syscall_filter) { - err = apply_seccomp(context->syscall_filter); +#ifdef HAVE_SECCOMP + if (context->filtered_syscalls) { + err = apply_seccomp(context); if (err < 0) { r = EXIT_SECCOMP; goto fail_child; } } +#endif #ifdef HAVE_SELINUX if (context->selinux_context && use_selinux()) { bool ignore; @@ -1751,6 +1731,18 @@ void exec_context_done(ExecContext *c) { free(c->syscall_filter); c->syscall_filter = NULL; + + free(c->syscall_filter_string); + c->syscall_filter_string = NULL; + +#ifdef HAVE_SECCOMP + if (c->syscall_filter) { + seccomp_release(c->syscall_filter); + c->syscall_filter = NULL; + } + set_free(c->filtered_syscalls); + c->filtered_syscalls = NULL; +#endif } void exec_command_done(ExecCommand *c) { diff --git a/src/core/execute.h b/src/core/execute.h index be811a97c1..b2d70d7d86 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -33,6 +33,11 @@ typedef struct ExecRuntime ExecRuntime; #include #include #include +#ifdef HAVE_SECCOMP +#include + +#include "set.h" +#endif #include "list.h" #include "util.h" @@ -162,7 +167,12 @@ struct ExecContext { * don't enter a trigger loop. */ bool same_pgrp; - uint32_t *syscall_filter; +#ifdef HAVE_SECCOMP + scmp_filter_ctx syscall_filter; + Set *filtered_syscalls; + uint32_t syscall_filter_default_action; +#endif + char *syscall_filter_string; bool oom_score_adjust_set:1; bool nice_set:1; diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 7a2d32ddbd..06ff18b573 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -33,6 +33,11 @@ #include #include #include +#ifdef HAVE_SECCOMP +#include + +#include "set.h" +#endif #include "sd-messages.h" #include "unit.h" @@ -47,13 +52,12 @@ #include "unit-printf.h" #include "utf8.h" #include "path-util.h" -#include "syscall-list.h" #include "env-util.h" #include "cgroup.h" #include "bus-util.h" #include "bus-error.h" -#ifndef HAVE_SYSV_COMPAT +#if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) int config_parse_warn_compat(const char *unit, const char *filename, unsigned line, @@ -1916,16 +1920,7 @@ int config_parse_documentation(const char *unit, return r; } -static void syscall_set(uint32_t *p, int nr) { - nr = SYSCALL_TO_INDEX(nr); - p[nr >> 4] |= 1 << (nr & 31); -} - -static void syscall_unset(uint32_t *p, int nr) { - nr = SYSCALL_TO_INDEX(nr); - p[nr >> 4] &= ~(1 << (nr & 31)); -} - +#ifdef HAVE_SECCOMP int config_parse_syscall_filter(const char *unit, const char *filename, unsigned line, @@ -1936,13 +1931,23 @@ int config_parse_syscall_filter(const char *unit, const char *rvalue, void *data, void *userdata) { - ExecContext *c = data; Unit *u = userdata; bool invert = false; char *w; size_t l; char *state; + _cleanup_strv_free_ char **syscalls = strv_new(NULL, NULL); + _cleanup_free_ char *sorted_syscalls = NULL; + uint32_t action = SCMP_ACT_ALLOW; + Iterator i; + void *e; + static char const *default_syscalls[] = {"execve", + "exit", + "exit_group", + "rt_sigreturn", + "sigreturn", + NULL}; assert(filename); assert(lvalue); @@ -1951,34 +1956,37 @@ int config_parse_syscall_filter(const char *unit, if (isempty(rvalue)) { /* Empty assignment resets the list */ - free(c->syscall_filter); - c->syscall_filter = NULL; + set_free(c->filtered_syscalls); + c->filtered_syscalls= NULL; + free(c->syscall_filter_string); + c->syscall_filter_string = NULL; return 0; } if (rvalue[0] == '~') { invert = true; + action = SCMP_ACT_KILL; rvalue++; } - if (!c->syscall_filter) { - size_t n; + if (!c->filtered_syscalls) { + c->filtered_syscalls = set_new(trivial_hash_func, trivial_compare_func); + if (invert) + c->syscall_filter_default_action = SCMP_ACT_ALLOW; + else { + char const **syscall; - n = (syscall_max() + 31) >> 4; - c->syscall_filter = new(uint32_t, n); - if (!c->syscall_filter) - return log_oom(); + c->syscall_filter_default_action = SCMP_ACT_KILL; - memset(c->syscall_filter, invert ? 0xFF : 0, n * sizeof(uint32_t)); + /* accept default syscalls if we are on a whitelist */ + STRV_FOREACH(syscall, default_syscalls) { + int id = seccomp_syscall_resolve_name(*syscall); + if (id < 0) + continue; - /* Add these by default */ - syscall_set(c->syscall_filter, __NR_execve); - syscall_set(c->syscall_filter, __NR_rt_sigreturn); -#ifdef __NR_sigreturn - syscall_set(c->syscall_filter, __NR_sigreturn); -#endif - syscall_set(c->syscall_filter, __NR_exit_group); - syscall_set(c->syscall_filter, __NR_exit); + set_replace(c->filtered_syscalls, INT_TO_PTR(id + 1)); + } + } } FOREACH_WORD_QUOTED(w, l, rvalue, state) { @@ -1989,23 +1997,39 @@ int config_parse_syscall_filter(const char *unit, if (!t) return log_oom(); - id = syscall_from_name(t); + id = seccomp_syscall_resolve_name(t); if (id < 0) { log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse syscall, ignoring: %s", t); continue; } - if (invert) - syscall_unset(c->syscall_filter, id); + /* If we previously wanted to forbid a syscall + * and now we want to allow it, then remove it from the list + * libseccomp will also return -EPERM if we try to add + * a rule with the same action as the default + */ + if (action == c->syscall_filter_default_action) + set_remove(c->filtered_syscalls, INT_TO_PTR(id + 1)); else - syscall_set(c->syscall_filter, id); + set_replace(c->filtered_syscalls, INT_TO_PTR(id + 1)); + } + + SET_FOREACH(e, c->filtered_syscalls, i) { + char *name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(e) - 1); + strv_push(&syscalls, name); } + sorted_syscalls = strv_join(strv_sort(syscalls), " "); + if (invert) + c->syscall_filter_string = strv_join(STRV_MAKE("~", sorted_syscalls, NULL), ""); + else + c->syscall_filter_string = strdup(sorted_syscalls); c->no_new_privileges = true; return 0; } +#endif int config_parse_unit_slice( const char *unit, @@ -2778,7 +2802,11 @@ void unit_dump_config_items(FILE *f) { { config_parse_set_status, "STATUS" }, { config_parse_service_sockets, "SOCKETS" }, { config_parse_environ, "ENVIRON" }, +#ifdef HAVE_SECCOMP { config_parse_syscall_filter, "SYSCALL" }, +#else + { config_parse_warn_compat, "NOTSUPPORTED" }, +#endif { config_parse_cpu_shares, "SHARES" }, { config_parse_memory_limit, "LIMIT" }, { config_parse_device_allow, "DEVICE" }, -- cgit v1.2.3-54-g00ecf