diff options
author | Lennart Poettering <lennart@poettering.net> | 2014-02-12 18:28:21 +0100 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2014-02-12 18:30:36 +0100 |
commit | 17df7223be064b1542dbe868e3b35cca977ee639 (patch) | |
tree | 8c88ea1827e95cb5a0c639b17a30b4295b924f79 /src/core | |
parent | c0467cf387548dc98c0254f63553d862b35a84e5 (diff) |
core: rework syscall filter
- Allow configuration of an errno error to return from blacklisted
syscalls, instead of immediately terminating a process.
- Fix parsing logic when libseccomp support is turned off
- Only keep the actual syscall set in the ExecContext, and generate the
string version only on demand.
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/dbus-execute.c | 57 | ||||
-rw-r--r-- | src/core/execute.c | 86 | ||||
-rw-r--r-- | src/core/execute.h | 9 | ||||
-rw-r--r-- | src/core/load-fragment-gperf.gperf.m4 | 6 | ||||
-rw-r--r-- | src/core/load-fragment.c | 182 | ||||
-rw-r--r-- | src/core/load-fragment.h | 1 |
6 files changed, 231 insertions, 110 deletions
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index 2ed7a3c73e..34d8eedc07 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -348,12 +348,66 @@ static int property_get_syscall_filter( sd_bus_error *error) { ExecContext *c = userdata; + _cleanup_strv_free_ char **l = NULL; + _cleanup_free_ char *t = NULL; + Iterator i; + void *id; + int r; + + assert(bus); + assert(reply); + assert(c); + + SET_FOREACH(id, c->syscall_filter, i) { + char *name; + + name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1); + if (!name) + continue; + + r = strv_push(&l, name); + if (r < 0) { + free(name); + return -ENOMEM; + } + } + + strv_sort(l); + + t = strv_join(l, " "); + if (!t) + return -ENOMEM; + + if (!c->syscall_whitelist) { + char *d; + + d = strappend("~", t); + if (!d) + return -ENOMEM; + + free(t); + t = d; + } + + return sd_bus_message_append(reply, "s", t); +} + +static int property_get_syscall_errno( + 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; assert(bus); assert(reply); assert(c); - return sd_bus_message_append(reply, "s", c->syscall_filter_string); + return sd_bus_message_append(reply, "i", (int32_t) c->syscall_errno); } const sd_bus_vtable bus_exec_vtable[] = { @@ -419,6 +473,7 @@ const sd_bus_vtable bus_exec_vtable[] = { 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("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 d2e5b740b7..1184777295 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -40,11 +40,6 @@ #include <sys/poll.h> #include <glob.h> #include <libgen.h> -#ifdef HAVE_SECCOMP -#include <seccomp.h> - -#include "set.h" -#endif #undef basename #ifdef HAVE_PAM @@ -55,6 +50,10 @@ #include <selinux/selinux.h> #endif +#ifdef HAVE_SECCOMP +#include <seccomp.h> +#endif + #include "execute.h" #include "strv.h" #include "macro.h" @@ -76,6 +75,7 @@ #include "unit.h" #include "async.h" #include "selinux-util.h" +#include "errno-list.h" #define IDLE_TIMEOUT_USEC (5*USEC_PER_SEC) #define IDLE_TIMEOUT2_USEC (1*USEC_PER_SEC) @@ -937,29 +937,36 @@ static void rename_process_from_path(const char *path) { } #ifdef HAVE_SECCOMP + static int apply_seccomp(ExecContext *c) { - uint32_t action = SCMP_ACT_ALLOW; + uint32_t negative_action, action; + scmp_filter_ctx *seccomp; Iterator i; void *id; + int r; assert(c); - c->syscall_filter = seccomp_init(c->syscall_filter_default_action); - if (!c->syscall_filter) - return -1; + negative_action = c->syscall_errno == 0 ? SCMP_ACT_KILL : SCMP_ACT_ERRNO(c->syscall_errno); + + seccomp = seccomp_init(c->syscall_whitelist ? negative_action : SCMP_ACT_ALLOW); + if (!seccomp) + return -ENOMEM; - if (c->syscall_filter_default_action == SCMP_ACT_ALLOW) - action = SCMP_ACT_KILL; + action = c->syscall_whitelist ? SCMP_ACT_ALLOW : negative_action; - SET_FOREACH(id, c->filtered_syscalls, i) { - int r = seccomp_rule_add(c->syscall_filter, action, PTR_TO_INT(id) - 1, 0); + SET_FOREACH(id, c->syscall_filter, i) { + r = seccomp_rule_add(seccomp, action, PTR_TO_INT(id) - 1, 0); if (r < 0) { - log_error("Failed to add syscall filter"); + seccomp_release(seccomp); return r; } } - return seccomp_load(c->syscall_filter); + r = seccomp_load(seccomp); + seccomp_release(seccomp); + + return r; } #endif @@ -1541,7 +1548,7 @@ int exec_spawn(ExecCommand *command, } #ifdef HAVE_SECCOMP - if (context->filtered_syscalls) { + if (context->syscall_filter) { err = apply_seccomp(context); if (err < 0) { r = EXIT_SECCOMP; @@ -1549,6 +1556,7 @@ int exec_spawn(ExecCommand *command, } } #endif + #ifdef HAVE_SELINUX if (context->selinux_context && use_selinux()) { bool ignore; @@ -1729,19 +1737,9 @@ void exec_context_done(ExecContext *c) { free(c->selinux_context); c->selinux_context = NULL; - 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; + set_free(c->syscall_filter); + c->syscall_filter = NULL; #endif } @@ -2115,6 +2113,38 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { fprintf(f, "%sSELinuxContext: %s\n", prefix, c->selinux_context); + + if (c->syscall_filter) { + Iterator j; + void *id; + bool first = true; + + fprintf(f, + "%sSystemCallFilter: \n", + prefix); + + if (!c->syscall_whitelist) + fputc('~', f); + + SET_FOREACH(id, c->syscall_filter, j) { + _cleanup_free_ char *name = NULL; + + if (first) + first = false; + else + fputc(' ', f); + + name = seccomp_syscall_resolve_num_arch(PTR_TO_INT(id)-1, SCMP_ARCH_NATIVE); + fputs(strna(name), f); + } + + fputc('\n', f); + } + + if (c->syscall_errno != 0) + fprintf(f, + "%sSystemCallErrorNumber: %s\n", + prefix, strna(errno_to_name(c->syscall_errno))); } void exec_status_start(ExecStatus *s, pid_t pid) { diff --git a/src/core/execute.h b/src/core/execute.h index b2d70d7d86..baf430a047 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -167,12 +167,9 @@ struct ExecContext { * don't enter a trigger loop. */ bool same_pgrp; -#ifdef HAVE_SECCOMP - scmp_filter_ctx syscall_filter; - Set *filtered_syscalls; - uint32_t syscall_filter_default_action; -#endif - char *syscall_filter_string; + Set *syscall_filter; + int syscall_errno; + bool syscall_whitelist:1; bool oom_score_adjust_set:1; bool nice_set:1; diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index 7d405788d7..16c9e25785 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -49,7 +49,11 @@ $1.SecureBits, config_parse_exec_secure_bits, 0, $1.CapabilityBoundingSet, config_parse_bounding_set, 0, offsetof($1, exec_context.capability_bounding_set_drop) $1.TimerSlackNSec, config_parse_nsec, 0, offsetof($1, exec_context.timer_slack_nsec) $1.NoNewPrivileges, config_parse_bool, 0, offsetof($1, exec_context.no_new_privileges) -$1.SystemCallFilter, config_parse_syscall_filter, 0, offsetof($1, exec_context) +m4_ifdef(`HAVE_SECCOMP', +`$1.SystemCallFilter, config_parse_syscall_filter, 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.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) $1.LimitDATA, config_parse_limit, RLIMIT_DATA, offsetof($1, exec_context.rlimit) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 06ff18b573..1b5856e273 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -33,10 +33,9 @@ #include <sys/stat.h> #include <sys/time.h> #include <sys/resource.h> + #ifdef HAVE_SECCOMP #include <seccomp.h> - -#include "set.h" #endif #include "sd-messages.h" @@ -56,18 +55,20 @@ #include "cgroup.h" #include "bus-util.h" #include "bus-error.h" +#include "errno-list.h" #if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) -int config_parse_warn_compat(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_warn_compat( + 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) { log_syntax(unit, LOG_DEBUG, filename, line, EINVAL, "Support for option %s= has been disabled at compile time and is ignored", @@ -1921,33 +1922,31 @@ int config_parse_documentation(const char *unit, } #ifdef HAVE_SECCOMP -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_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) { + + static const char default_syscalls[] = + "execve\0" + "exit\0" + "exit_group\0" + "rt_sigreturn\0" + "sigreturn\0"; + ExecContext *c = data; Unit *u = userdata; bool invert = false; - char *w; + char *w, *state; 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}; + int r; assert(filename); assert(lvalue); @@ -1956,42 +1955,51 @@ int config_parse_syscall_filter(const char *unit, if (isempty(rvalue)) { /* Empty assignment resets the list */ - set_free(c->filtered_syscalls); - c->filtered_syscalls= NULL; - free(c->syscall_filter_string); - c->syscall_filter_string = NULL; + set_free(c->syscall_filter); + c->syscall_filter = NULL; + c->syscall_whitelist = false; return 0; } if (rvalue[0] == '~') { invert = true; - action = SCMP_ACT_KILL; rvalue++; } - if (!c->filtered_syscalls) { - c->filtered_syscalls = set_new(trivial_hash_func, trivial_compare_func); + if (!c->syscall_filter) { + c->syscall_filter = set_new(trivial_hash_func, trivial_compare_func); + if (!c->syscall_filter) + return log_oom(); + if (invert) - c->syscall_filter_default_action = SCMP_ACT_ALLOW; + /* Allow everything but the ones listed */ + c->syscall_whitelist = false; else { - char const **syscall; + const char *i; + + /* Allow nothing but the ones listed */ + c->syscall_whitelist = true; - c->syscall_filter_default_action = SCMP_ACT_KILL; + /* Accept default syscalls if we are on a whitelist */ + NULSTR_FOREACH(i, default_syscalls) { + int id; - /* accept default syscalls if we are on a whitelist */ - STRV_FOREACH(syscall, default_syscalls) { - int id = seccomp_syscall_resolve_name(*syscall); + id = seccomp_syscall_resolve_name(i); if (id < 0) continue; - set_replace(c->filtered_syscalls, INT_TO_PTR(id + 1)); + r = set_put(c->syscall_filter, INT_TO_PTR(id + 1)); + if (r == -EEXIST) + continue; + if (r < 0) + return log_oom(); } } } FOREACH_WORD_QUOTED(w, l, rvalue, state) { - int id; _cleanup_free_ char *t = NULL; + int id; t = strndup(w, l); if (!t) @@ -1999,34 +2007,60 @@ int config_parse_syscall_filter(const char *unit, id = seccomp_syscall_resolve_name(t); if (id < 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Failed to parse syscall, ignoring: %s", t); + log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse system call, ignoring: %s", t); continue; } - /* 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 we previously wanted to forbid a syscall and now + * we want to allow it, then remove it from the list */ - if (action == c->syscall_filter_default_action) - set_remove(c->filtered_syscalls, INT_TO_PTR(id + 1)); - else - set_replace(c->filtered_syscalls, INT_TO_PTR(id + 1)); + if (!invert == c->syscall_whitelist) { + r = set_put(c->syscall_filter, INT_TO_PTR(id + 1)); + if (r == -EEXIST) + continue; + if (r < 0) + return log_oom(); + } else + set_remove(c->syscall_filter, 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); + c->no_new_privileges = true; + + return 0; +} + +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) { + + ExecContext *c = data; + int e; + + assert(filename); + assert(lvalue); + assert(rvalue); + + if (isempty(rvalue)) { + /* Empty assignment resets to KILL */ + c->syscall_errno = 0; + return 0; } - 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; + e = errno_from_name(rvalue); + if (e < 0) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Failed to parse error number, ignoring: %s", rvalue); + return 0; + } + c->syscall_errno = e; return 0; } #endif @@ -2742,6 +2776,9 @@ void unit_dump_config_items(FILE *f) { const ConfigParserCallback callback; const char *rvalue; } table[] = { +#if !defined(HAVE_SYSV_COMPAT) || !defined(HAVE_SECCOMP) + { config_parse_warn_compat, "NOTSUPPORTED" }, +#endif { config_parse_int, "INTEGER" }, { config_parse_unsigned, "UNSIGNED" }, { config_parse_bytes_size, "SIZE" }, @@ -2773,8 +2810,6 @@ void unit_dump_config_items(FILE *f) { { config_parse_service_restart, "SERVICERESTART" }, #ifdef HAVE_SYSV_COMPAT { config_parse_sysv_priority, "SYSVPRIORITY" }, -#else - { config_parse_warn_compat, "NOTSUPPORTED" }, #endif { config_parse_kill_mode, "KILLMODE" }, { config_parse_kill_signal, "SIGNAL" }, @@ -2803,9 +2838,8 @@ void unit_dump_config_items(FILE *f) { { config_parse_service_sockets, "SOCKETS" }, { config_parse_environ, "ENVIRON" }, #ifdef HAVE_SECCOMP - { config_parse_syscall_filter, "SYSCALL" }, -#else - { config_parse_warn_compat, "NOTSUPPORTED" }, + { config_parse_syscall_filter, "SYSCALLS" }, + { config_parse_syscall_errno, "ERRNO" }, #endif { config_parse_cpu_shares, "SHARES" }, { config_parse_memory_limit, "LIMIT" }, diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index dcd09ad1f0..4cefa76157 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_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); int config_parse_cpu_shares(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); |