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:
parent
150c430fd4
commit
005bfaf118
@ -1888,7 +1888,8 @@ RestrictNamespaces=~cgroup net</programlisting>
|
||||
<constant>EACCES</constant> or <constant>EUCLEAN</constant> (see <citerefentry
|
||||
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
|
||||
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,
|
||||
but without the <constant>CAP_SYS_ADMIN</constant> capability (e.g. setting
|
||||
<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,
|
||||
instead of terminating the process immediately. See <citerefentry
|
||||
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
|
||||
process will be terminated immediately when the filter is triggered.</para></listitem>
|
||||
full list of error codes. When this setting is not used, or when the empty string or the special
|
||||
setting <literal>kill</literal> is assigned, the process will be terminated immediately when the
|
||||
filter is triggered.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
|
@ -16,6 +16,9 @@
|
||||
#include "missing_network.h"
|
||||
#include "parse-util.h"
|
||||
#include "process-util.h"
|
||||
#if HAVE_SECCOMP
|
||||
#include "seccomp-util.h"
|
||||
#endif
|
||||
#include "stat-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
@ -314,6 +317,7 @@ int parse_errno(const char *t) {
|
||||
return e;
|
||||
}
|
||||
|
||||
#if HAVE_SECCOMP
|
||||
int parse_syscall_and_errno(const char *in, char **name, int *error) {
|
||||
_cleanup_free_ char *n = NULL;
|
||||
char *p;
|
||||
@ -332,7 +336,7 @@ int parse_syscall_and_errno(const char *in, char **name, int *error) {
|
||||
|
||||
p = strchr(in, ':');
|
||||
if (p) {
|
||||
e = parse_errno(p + 1);
|
||||
e = seccomp_parse_errno_or_action(p + 1);
|
||||
if (e < 0)
|
||||
return e;
|
||||
|
||||
@ -351,6 +355,7 @@ int parse_syscall_and_errno(const char *in, char **name, int *error) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const char *mangle_base(const char *s, unsigned *base) {
|
||||
const char *k;
|
||||
|
@ -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_range(const char *t, unsigned *lower, unsigned *upper);
|
||||
int parse_errno(const char *t);
|
||||
#if HAVE_SECCOMP
|
||||
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_LEADING_ZERO (1U << 29)
|
||||
|
@ -387,7 +387,7 @@ static int property_get_syscall_filter(
|
||||
continue;
|
||||
|
||||
if (num >= 0) {
|
||||
e = errno_to_name(num);
|
||||
e = seccomp_errno_or_action_to_string(num);
|
||||
if (e) {
|
||||
s = strjoin(name, ":", e);
|
||||
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_IS_VALID(log_level, "i", int32_t, int, "%" PRIi32, log_level_is_valid);
|
||||
#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
|
||||
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);
|
||||
|
@ -1465,7 +1465,7 @@ static int apply_syscall_filter(const Unit* u, const ExecContext *c, bool needs_
|
||||
if (skip_seccomp_unavailable(u, "SystemCallFilter="))
|
||||
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) {
|
||||
default_action = negative_action;
|
||||
@ -4675,6 +4675,9 @@ void exec_context_init(ExecContext *c) {
|
||||
assert_cc(NAMESPACE_FLAGS_INITIAL != NAMESPACE_FLAGS_ALL);
|
||||
c->restrict_namespaces = NAMESPACE_FLAGS_INITIAL;
|
||||
c->log_level_max = -1;
|
||||
#if HAVE_SECCOMP
|
||||
c->syscall_errno = SECCOMP_ERROR_NUMBER_KILL;
|
||||
#endif
|
||||
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);
|
||||
|
||||
if (num >= 0) {
|
||||
errno_name = errno_to_name(num);
|
||||
errno_name = seccomp_errno_or_action_to_string(num);
|
||||
if (errno_name)
|
||||
fprintf(f, ":%s", errno_name);
|
||||
else
|
||||
@ -5517,15 +5520,20 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
|
||||
prefix, c->network_namespace_path);
|
||||
|
||||
if (c->syscall_errno > 0) {
|
||||
#if HAVE_SECCOMP
|
||||
const char *errno_name;
|
||||
#endif
|
||||
|
||||
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)
|
||||
fprintf(f, "%s\n", errno_name);
|
||||
fputs(errno_name, f);
|
||||
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++) {
|
||||
|
@ -3264,9 +3264,9 @@ int config_parse_syscall_errno(
|
||||
assert(lvalue);
|
||||
assert(rvalue);
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
if (isempty(rvalue) || streq(rvalue, "kill")) {
|
||||
/* Empty assignment resets to KILL */
|
||||
c->syscall_errno = 0;
|
||||
c->syscall_errno = SECCOMP_ERROR_NUMBER_KILL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,9 @@
|
||||
#include "path-util.h"
|
||||
#include "process-util.h"
|
||||
#include "rlimit-util.h"
|
||||
#if HAVE_SECCOMP
|
||||
#include "seccomp-util.h"
|
||||
#endif
|
||||
#include "securebits-util.h"
|
||||
#include "signal-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", log_facility_unshifted_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", secure_bits_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);
|
||||
|
||||
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"))
|
||||
return bus_append_ioprio_class_from_string(m, field, eq);
|
||||
|
@ -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 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);
|
||||
|
||||
r = seccomp_rule_add_exact(seccomp, a, id, 0);
|
||||
|
@ -5,7 +5,10 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "errno-list.h"
|
||||
#include "parse-util.h"
|
||||
#include "set.h"
|
||||
#include "string-util.h"
|
||||
|
||||
const char* seccomp_arch_to_string(uint32_t c);
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -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-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-override-error-action.service", SIGSYS, CLD_KILLED);
|
||||
test(__func__, m, "exec-systemcallfilter-override-error-action2.service", errno_from_name("EILSEQ"), CLD_EXITED);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,9 @@
|
||||
#include "log.h"
|
||||
#include "parse-util.h"
|
||||
#include "string-util.h"
|
||||
#if HAVE_SECCOMP
|
||||
#include "seccomp-util.h"
|
||||
#endif
|
||||
|
||||
static void test_parse_boolean(void) {
|
||||
assert_se(parse_boolean("1") == 1);
|
||||
@ -852,6 +855,7 @@ static void test_parse_errno(void) {
|
||||
}
|
||||
|
||||
static void test_parse_syscall_and_errno(void) {
|
||||
#if HAVE_SECCOMP
|
||||
_cleanup_free_ char *n = NULL;
|
||||
int e;
|
||||
|
||||
@ -882,11 +886,16 @@ static void test_parse_syscall_and_errno(void) {
|
||||
assert_se(e == 255);
|
||||
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. */
|
||||
assert_se(parse_syscall_and_errno("", &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:-3", &n, &e) == -ERANGE);
|
||||
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:EINVALaaa", &n, &e) == -EINVAL);
|
||||
assert_se(parse_syscall_and_errno("hoge:", &n, &e) == -EINVAL);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void test_parse_mtu(void) {
|
||||
|
@ -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
|
@ -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
|
Loading…
Reference in New Issue
Block a user