1
0
mirror of https://github.com/systemd/systemd.git synced 2024-12-26 03:22:00 +03:00

exec: Add kill action to system call filters

Define explicit action "kill" for SystemCallErrorNumber=.

In addition to errno code, allow specifying "kill" as action for
SystemCallFilter=.

---
v7: seccomp_parse_errno_or_action() returns -EINVAL if !HAVE_SECCOMP
v6: use streq_ptr(), let errno_to_name() handle bad values, kill processes,
 init syscall_errno
v5: actually use seccomp_errno_or_action_to_string(), don't fail bus unit
parsing without seccomp
v4: fix build without seccomp
v3: drop log action
v2: action -> number
This commit is contained in:
Topi Miettinen 2020-08-05 16:31:26 +03:00
parent 150c430fd4
commit 005bfaf118
13 changed files with 95 additions and 17 deletions

View File

@ -1888,7 +1888,8 @@ RestrictNamespaces=~cgroup net</programlisting>
<constant>EACCES</constant> or <constant>EUCLEAN</constant> (see <citerefentry <constant>EACCES</constant> or <constant>EUCLEAN</constant> (see <citerefentry
project='man-pages'><refentrytitle>errno</refentrytitle><manvolnum>3</manvolnum></citerefentry> for a project='man-pages'><refentrytitle>errno</refentrytitle><manvolnum>3</manvolnum></citerefentry> for a
full list). This value will be returned when a deny-listed system call is triggered, instead of full list). This value will be returned when a deny-listed system call is triggered, instead of
terminating the processes immediately. This value takes precedence over the one given in terminating the processes immediately. Special setting <literal>kill</literal> can be used to
explicitly specify killing. This value takes precedence over the one given in
<varname>SystemCallErrorNumber=</varname>, see below. If running in user mode, or in system mode, <varname>SystemCallErrorNumber=</varname>, see below. If running in user mode, or in system mode,
but without the <constant>CAP_SYS_ADMIN</constant> capability (e.g. setting but without the <constant>CAP_SYS_ADMIN</constant> capability (e.g. setting
<varname>User=nobody</varname>), <varname>NoNewPrivileges=yes</varname> is implied. This feature <varname>User=nobody</varname>), <varname>NoNewPrivileges=yes</varname> is implied. This feature
@ -2098,8 +2099,9 @@ SystemCallErrorNumber=EPERM</programlisting>
return when the system call filter configured with <varname>SystemCallFilter=</varname> is triggered, return when the system call filter configured with <varname>SystemCallFilter=</varname> is triggered,
instead of terminating the process immediately. See <citerefentry instead of terminating the process immediately. See <citerefentry
project='man-pages'><refentrytitle>errno</refentrytitle><manvolnum>3</manvolnum></citerefentry> for a project='man-pages'><refentrytitle>errno</refentrytitle><manvolnum>3</manvolnum></citerefentry> for a
full list of error codes. When this setting is not used, or when the empty string is assigned, the full list of error codes. When this setting is not used, or when the empty string or the special
process will be terminated immediately when the filter is triggered.</para></listitem> setting <literal>kill</literal> is assigned, the process will be terminated immediately when the
filter is triggered.</para></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>

View File

@ -16,6 +16,9 @@
#include "missing_network.h" #include "missing_network.h"
#include "parse-util.h" #include "parse-util.h"
#include "process-util.h" #include "process-util.h"
#if HAVE_SECCOMP
#include "seccomp-util.h"
#endif
#include "stat-util.h" #include "stat-util.h"
#include "string-util.h" #include "string-util.h"
#include "strv.h" #include "strv.h"
@ -314,6 +317,7 @@ int parse_errno(const char *t) {
return e; return e;
} }
#if HAVE_SECCOMP
int parse_syscall_and_errno(const char *in, char **name, int *error) { int parse_syscall_and_errno(const char *in, char **name, int *error) {
_cleanup_free_ char *n = NULL; _cleanup_free_ char *n = NULL;
char *p; char *p;
@ -332,7 +336,7 @@ int parse_syscall_and_errno(const char *in, char **name, int *error) {
p = strchr(in, ':'); p = strchr(in, ':');
if (p) { if (p) {
e = parse_errno(p + 1); e = seccomp_parse_errno_or_action(p + 1);
if (e < 0) if (e < 0)
return e; return e;
@ -351,6 +355,7 @@ int parse_syscall_and_errno(const char *in, char **name, int *error) {
return 0; return 0;
} }
#endif
static const char *mangle_base(const char *s, unsigned *base) { static const char *mangle_base(const char *s, unsigned *base) {
const char *k; const char *k;

View File

@ -19,7 +19,9 @@ int parse_mtu(int family, const char *s, uint32_t *ret);
int parse_size(const char *t, uint64_t base, uint64_t *size); int parse_size(const char *t, uint64_t base, uint64_t *size);
int parse_range(const char *t, unsigned *lower, unsigned *upper); int parse_range(const char *t, unsigned *lower, unsigned *upper);
int parse_errno(const char *t); int parse_errno(const char *t);
#if HAVE_SECCOMP
int parse_syscall_and_errno(const char *in, char **name, int *error); int parse_syscall_and_errno(const char *in, char **name, int *error);
#endif
#define SAFE_ATO_REFUSE_PLUS_MINUS (1U << 30) #define SAFE_ATO_REFUSE_PLUS_MINUS (1U << 30)
#define SAFE_ATO_REFUSE_LEADING_ZERO (1U << 29) #define SAFE_ATO_REFUSE_LEADING_ZERO (1U << 29)

View File

@ -387,7 +387,7 @@ static int property_get_syscall_filter(
continue; continue;
if (num >= 0) { if (num >= 0) {
e = errno_to_name(num); e = seccomp_errno_or_action_to_string(num);
if (e) { if (e) {
s = strjoin(name, ":", e); s = strjoin(name, ":", e);
if (!s) if (!s)
@ -1424,7 +1424,7 @@ static const char* mount_propagation_flags_to_string_with_check(unsigned long n)
static BUS_DEFINE_SET_TRANSIENT(nsec, "t", uint64_t, nsec_t, NSEC_FMT); static BUS_DEFINE_SET_TRANSIENT(nsec, "t", uint64_t, nsec_t, NSEC_FMT);
static BUS_DEFINE_SET_TRANSIENT_IS_VALID(log_level, "i", int32_t, int, "%" PRIi32, log_level_is_valid); static BUS_DEFINE_SET_TRANSIENT_IS_VALID(log_level, "i", int32_t, int, "%" PRIi32, log_level_is_valid);
#if HAVE_SECCOMP #if HAVE_SECCOMP
static BUS_DEFINE_SET_TRANSIENT_IS_VALID(errno, "i", int32_t, int, "%" PRIi32, errno_is_valid); static BUS_DEFINE_SET_TRANSIENT_IS_VALID(errno, "i", int32_t, int, "%" PRIi32, seccomp_errno_or_action_is_valid);
#endif #endif
static BUS_DEFINE_SET_TRANSIENT_PARSE(std_input, ExecInput, exec_input_from_string); static BUS_DEFINE_SET_TRANSIENT_PARSE(std_input, ExecInput, exec_input_from_string);
static BUS_DEFINE_SET_TRANSIENT_PARSE(std_output, ExecOutput, exec_output_from_string); static BUS_DEFINE_SET_TRANSIENT_PARSE(std_output, ExecOutput, exec_output_from_string);

View File

@ -1465,7 +1465,7 @@ static int apply_syscall_filter(const Unit* u, const ExecContext *c, bool needs_
if (skip_seccomp_unavailable(u, "SystemCallFilter=")) if (skip_seccomp_unavailable(u, "SystemCallFilter="))
return 0; return 0;
negative_action = c->syscall_errno == 0 ? scmp_act_kill_process() : SCMP_ACT_ERRNO(c->syscall_errno); negative_action = c->syscall_errno == SECCOMP_ERROR_NUMBER_KILL ? scmp_act_kill_process() : SCMP_ACT_ERRNO(c->syscall_errno);
if (c->syscall_allow_list) { if (c->syscall_allow_list) {
default_action = negative_action; default_action = negative_action;
@ -4675,6 +4675,9 @@ void exec_context_init(ExecContext *c) {
assert_cc(NAMESPACE_FLAGS_INITIAL != NAMESPACE_FLAGS_ALL); assert_cc(NAMESPACE_FLAGS_INITIAL != NAMESPACE_FLAGS_ALL);
c->restrict_namespaces = NAMESPACE_FLAGS_INITIAL; c->restrict_namespaces = NAMESPACE_FLAGS_INITIAL;
c->log_level_max = -1; c->log_level_max = -1;
#if HAVE_SECCOMP
c->syscall_errno = SECCOMP_ERROR_NUMBER_KILL;
#endif
numa_policy_reset(&c->numa_policy); numa_policy_reset(&c->numa_policy);
} }
@ -5474,7 +5477,7 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
fputs(strna(name), f); fputs(strna(name), f);
if (num >= 0) { if (num >= 0) {
errno_name = errno_to_name(num); errno_name = seccomp_errno_or_action_to_string(num);
if (errno_name) if (errno_name)
fprintf(f, ":%s", errno_name); fprintf(f, ":%s", errno_name);
else else
@ -5517,15 +5520,20 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
prefix, c->network_namespace_path); prefix, c->network_namespace_path);
if (c->syscall_errno > 0) { if (c->syscall_errno > 0) {
#if HAVE_SECCOMP
const char *errno_name; const char *errno_name;
#endif
fprintf(f, "%sSystemCallErrorNumber: ", prefix); fprintf(f, "%sSystemCallErrorNumber: ", prefix);
errno_name = errno_to_name(c->syscall_errno); #if HAVE_SECCOMP
errno_name = seccomp_errno_or_action_to_string(c->syscall_errno);
if (errno_name) if (errno_name)
fprintf(f, "%s\n", errno_name); fputs(errno_name, f);
else else
fprintf(f, "%d\n", c->syscall_errno); fprintf(f, "%d", c->syscall_errno);
#endif
fputc('\n', f);
} }
for (size_t i = 0; i < c->n_mount_images; i++) { for (size_t i = 0; i < c->n_mount_images; i++) {

View File

@ -3264,9 +3264,9 @@ int config_parse_syscall_errno(
assert(lvalue); assert(lvalue);
assert(rvalue); assert(rvalue);
if (isempty(rvalue)) { if (isempty(rvalue) || streq(rvalue, "kill")) {
/* Empty assignment resets to KILL */ /* Empty assignment resets to KILL */
c->syscall_errno = 0; c->syscall_errno = SECCOMP_ERROR_NUMBER_KILL;
return 0; return 0;
} }

View File

@ -30,6 +30,9 @@
#include "path-util.h" #include "path-util.h"
#include "process-util.h" #include "process-util.h"
#include "rlimit-util.h" #include "rlimit-util.h"
#if HAVE_SECCOMP
#include "seccomp-util.h"
#endif
#include "securebits-util.h" #include "securebits-util.h"
#include "signal-util.h" #include "signal-util.h"
#include "socket-util.h" #include "socket-util.h"
@ -107,7 +110,10 @@ DEFINE_BUS_APPEND_PARSE("i", ioprio_class_from_string);
DEFINE_BUS_APPEND_PARSE("i", ip_tos_from_string); DEFINE_BUS_APPEND_PARSE("i", ip_tos_from_string);
DEFINE_BUS_APPEND_PARSE("i", log_facility_unshifted_from_string); DEFINE_BUS_APPEND_PARSE("i", log_facility_unshifted_from_string);
DEFINE_BUS_APPEND_PARSE("i", log_level_from_string); DEFINE_BUS_APPEND_PARSE("i", log_level_from_string);
DEFINE_BUS_APPEND_PARSE("i", parse_errno); #if !HAVE_SECCOMP
static inline int seccomp_parse_errno_or_action(const char *eq) { return -EINVAL; }
#endif
DEFINE_BUS_APPEND_PARSE("i", seccomp_parse_errno_or_action);
DEFINE_BUS_APPEND_PARSE("i", sched_policy_from_string); DEFINE_BUS_APPEND_PARSE("i", sched_policy_from_string);
DEFINE_BUS_APPEND_PARSE("i", secure_bits_from_string); DEFINE_BUS_APPEND_PARSE("i", secure_bits_from_string);
DEFINE_BUS_APPEND_PARSE("i", signal_from_string); DEFINE_BUS_APPEND_PARSE("i", signal_from_string);
@ -927,7 +933,7 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
return bus_append_parse_nice(m, field, eq); return bus_append_parse_nice(m, field, eq);
if (streq(field, "SystemCallErrorNumber")) if (streq(field, "SystemCallErrorNumber"))
return bus_append_parse_errno(m, field, eq); return bus_append_seccomp_parse_errno_or_action(m, field, eq);
if (streq(field, "IOSchedulingClass")) if (streq(field, "IOSchedulingClass"))
return bus_append_ioprio_class_from_string(m, field, eq); return bus_append_ioprio_class_from_string(m, field, eq);

View File

@ -1071,7 +1071,9 @@ int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap* set, u
int id = PTR_TO_INT(syscall_id) - 1; int id = PTR_TO_INT(syscall_id) - 1;
int error = PTR_TO_INT(val); int error = PTR_TO_INT(val);
if (action != SCMP_ACT_ALLOW && error >= 0) if (error == SECCOMP_ERROR_NUMBER_KILL)
a = scmp_act_kill_process();
else if (action != SCMP_ACT_ALLOW && error >= 0)
a = SCMP_ACT_ERRNO(error); a = SCMP_ACT_ERRNO(error);
r = seccomp_rule_add_exact(seccomp, a, id, 0); r = seccomp_rule_add_exact(seccomp, a, id, 0);

View File

@ -5,7 +5,10 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include "errno-list.h"
#include "parse-util.h"
#include "set.h" #include "set.h"
#include "string-util.h"
const char* seccomp_arch_to_string(uint32_t c); const char* seccomp_arch_to_string(uint32_t c);
int seccomp_arch_from_string(const char *n, uint32_t *ret); int seccomp_arch_from_string(const char *n, uint32_t *ret);
@ -115,3 +118,25 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(scmp_filter_ctx, seccomp_release);
int parse_syscall_archs(char **l, Set **ret_archs); int parse_syscall_archs(char **l, Set **ret_archs);
uint32_t scmp_act_kill_process(void); uint32_t scmp_act_kill_process(void);
/* This is a special value to be used where syscall filters otherwise expect errno numbers, will be
replaced with real seccomp action. */
enum {
SECCOMP_ERROR_NUMBER_KILL = INT_MAX - 1,
};
static inline bool seccomp_errno_or_action_is_valid(int n) {
return n == SECCOMP_ERROR_NUMBER_KILL || errno_is_valid(n);
}
static inline int seccomp_parse_errno_or_action(const char *p) {
if (streq_ptr(p, "kill"))
return SECCOMP_ERROR_NUMBER_KILL;
return parse_errno(p);
}
static inline const char *seccomp_errno_or_action_to_string(int num) {
if (num == SECCOMP_ERROR_NUMBER_KILL)
return "kill";
return errno_to_name(num);
}

View File

@ -434,6 +434,8 @@ static void test_exec_systemcallfilter(Manager *m) {
test(__func__, m, "exec-systemcallfilter-with-errno-name.service", errno_from_name("EILSEQ"), CLD_EXITED); test(__func__, m, "exec-systemcallfilter-with-errno-name.service", errno_from_name("EILSEQ"), CLD_EXITED);
test(__func__, m, "exec-systemcallfilter-with-errno-number.service", 255, CLD_EXITED); test(__func__, m, "exec-systemcallfilter-with-errno-number.service", 255, CLD_EXITED);
test(__func__, m, "exec-systemcallfilter-with-errno-multi.service", errno_from_name("EILSEQ"), CLD_EXITED); test(__func__, m, "exec-systemcallfilter-with-errno-multi.service", errno_from_name("EILSEQ"), CLD_EXITED);
test(__func__, m, "exec-systemcallfilter-override-error-action.service", SIGSYS, CLD_KILLED);
test(__func__, m, "exec-systemcallfilter-override-error-action2.service", errno_from_name("EILSEQ"), CLD_EXITED);
#endif #endif
} }

View File

@ -10,6 +10,9 @@
#include "log.h" #include "log.h"
#include "parse-util.h" #include "parse-util.h"
#include "string-util.h" #include "string-util.h"
#if HAVE_SECCOMP
#include "seccomp-util.h"
#endif
static void test_parse_boolean(void) { static void test_parse_boolean(void) {
assert_se(parse_boolean("1") == 1); assert_se(parse_boolean("1") == 1);
@ -852,6 +855,7 @@ static void test_parse_errno(void) {
} }
static void test_parse_syscall_and_errno(void) { static void test_parse_syscall_and_errno(void) {
#if HAVE_SECCOMP
_cleanup_free_ char *n = NULL; _cleanup_free_ char *n = NULL;
int e; int e;
@ -882,11 +886,16 @@ static void test_parse_syscall_and_errno(void) {
assert_se(e == 255); assert_se(e == 255);
n = mfree(n); n = mfree(n);
assert_se(parse_syscall_and_errno("hoge:kill", &n, &e) >= 0);
assert_se(streq(n, "hoge"));
assert_se(e == SECCOMP_ERROR_NUMBER_KILL);
n = mfree(n);
/* The function checks the syscall name is empty or not. */ /* The function checks the syscall name is empty or not. */
assert_se(parse_syscall_and_errno("", &n, &e) == -EINVAL); assert_se(parse_syscall_and_errno("", &n, &e) == -EINVAL);
assert_se(parse_syscall_and_errno(":255", &n, &e) == -EINVAL); assert_se(parse_syscall_and_errno(":255", &n, &e) == -EINVAL);
/* errno must be a valid errno name or number between 0 and ERRNO_MAX == 4095 */ /* errno must be a valid errno name or number between 0 and ERRNO_MAX == 4095, or "kill" */
assert_se(parse_syscall_and_errno("hoge:4096", &n, &e) == -ERANGE); assert_se(parse_syscall_and_errno("hoge:4096", &n, &e) == -ERANGE);
assert_se(parse_syscall_and_errno("hoge:-3", &n, &e) == -ERANGE); assert_se(parse_syscall_and_errno("hoge:-3", &n, &e) == -ERANGE);
assert_se(parse_syscall_and_errno("hoge:12.3", &n, &e) == -EINVAL); assert_se(parse_syscall_and_errno("hoge:12.3", &n, &e) == -EINVAL);
@ -896,6 +905,7 @@ static void test_parse_syscall_and_errno(void) {
assert_se(parse_syscall_and_errno("hoge:-EINVAL", &n, &e) == -EINVAL); assert_se(parse_syscall_and_errno("hoge:-EINVAL", &n, &e) == -EINVAL);
assert_se(parse_syscall_and_errno("hoge:EINVALaaa", &n, &e) == -EINVAL); assert_se(parse_syscall_and_errno("hoge:EINVALaaa", &n, &e) == -EINVAL);
assert_se(parse_syscall_and_errno("hoge:", &n, &e) == -EINVAL); assert_se(parse_syscall_and_errno("hoge:", &n, &e) == -EINVAL);
#endif
} }
static void test_parse_mtu(void) { static void test_parse_mtu(void) {

View File

@ -0,0 +1,8 @@
[Unit]
Description=Test for SystemCallFilter with specific kill action overriding default errno action
[Service]
ExecStart=/usr/bin/python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
Type=oneshot
SystemCallFilter=~uname:kill
SystemCallErrorNumber=EILSEQ

View File

@ -0,0 +1,8 @@
[Unit]
Description=Test for SystemCallFilter with specific errno action overriding default kill action
[Service]
ExecStart=/usr/bin/python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)'
Type=oneshot
SystemCallFilter=~uname:EILSEQ
SystemCallErrorNumber=kill