diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 6843c208ca4..7545c75d770 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -1438,7 +1438,12 @@ CapabilityBoundingSet=~CAP_B CAP_C executed by the unit processes except for the listed ones will result in immediate process termination with the SIGSYS signal (whitelisting). If the first character of the list is ~, the effect is inverted: only the listed system calls will result in immediate process termination - (blacklisting). If running in user mode, or in system mode, but without the CAP_SYS_ADMIN + (blacklisting). Blacklisted system calls and system call groups may optionally be suffixed with a colon + (:) and errno error number (between 0 and 4095) or errno name such as + EPERM, EACCES or EUCLEAN. This value will be + returned when a blacklisted system call is triggered, instead of terminating the processes immediately. + This value takes precedence over the one given in SystemCallErrorNumber=. + If running in user mode, or in system mode, but without the CAP_SYS_ADMIN capability (e.g. setting User=nobody), NoNewPrivileges=yes is implied. This feature makes use of the Secure Computing Mode 2 interfaces of the kernel ('seccomp filtering') and is useful for enforcing a minimal sandboxing environment. Note that the execve, @@ -1617,15 +1622,11 @@ CapabilityBoundingSet=~CAP_B CAP_C SystemCallErrorNumber= - Takes an errno error number - name to return when the system call filter configured with - SystemCallFilter= is triggered, instead of - terminating the process immediately. Takes an error name such - as EPERM, EACCES or - EUCLEAN. When this setting is not used, - or when the empty string is assigned, the process will be - terminated immediately when the filter is - triggered. + Takes an errno error number (between 1 and 4095) or errno name such as + EPERM, EACCES or EUCLEAN, to return when the + system call filter configured with SystemCallFilter= is triggered, instead of terminating + the process immediately. When this setting is not used, or when the empty string is assigned, the process + will be terminated immediately when the filter is triggered. diff --git a/src/basic/errno-list.c b/src/basic/errno-list.c index c6a01eec8ba..f0b74753fea 100644 --- a/src/basic/errno-list.c +++ b/src/basic/errno-list.c @@ -51,7 +51,3 @@ int errno_from_name(const char *name) { assert(sc->id > 0); return sc->id; } - -int errno_max(void) { - return ELEMENTSOF(errno_names); -} diff --git a/src/basic/errno-list.h b/src/basic/errno-list.h index 4eec0cc7869..c8e52053773 100644 --- a/src/basic/errno-list.h +++ b/src/basic/errno-list.h @@ -19,7 +19,11 @@ along with systemd; If not, see . ***/ +/* + * MAX_ERRNO is defined as 4095 in linux/err.h + * We use the same value here. + */ +#define ERRNO_MAX 4095 + const char *errno_to_name(int id); int errno_from_name(const char *name); - -int errno_max(void); diff --git a/src/basic/parse-util.c b/src/basic/parse-util.c index 4ae07b0a8ed..9108820ca8d 100644 --- a/src/basic/parse-util.c +++ b/src/basic/parse-util.c @@ -25,6 +25,7 @@ #include #include "alloc-util.h" +#include "errno-list.h" #include "extract-word.h" #include "macro.h" #include "parse-util.h" @@ -268,6 +269,64 @@ int parse_range(const char *t, unsigned *lower, unsigned *upper) { return 0; } +int parse_errno(const char *t) { + int r, e; + + assert(t); + + r = errno_from_name(t); + if (r > 0) + return r; + + r = safe_atoi(t, &e); + if (r < 0) + return r; + + if (e < 0 || e > ERRNO_MAX) + return -ERANGE; + + return e; +} + +int parse_syscall_and_errno(const char *in, char **name, int *error) { + _cleanup_free_ char *n = NULL; + char *p; + int e = -1; + + assert(in); + assert(name); + assert(error); + + /* + * This parse "syscall:errno" like "uname:EILSEQ", "@sync:255". + * If errno is omitted, then error is set to -1. + * Empty syscall name is not allowed. + * Here, we do not check that the syscall name is valid or not. + */ + + p = strchr(in, ':'); + if (p) { + e = parse_errno(p + 1); + if (e < 0) + return e; + + n = strndup(in, p - in); + } else + n = strdup(in); + + if (!n) + return -ENOMEM; + + if (isempty(n)) + return -EINVAL; + + *error = e; + *name = n; + n = NULL; + + return 0; +} + char *format_bytes(char *buf, size_t l, uint64_t t) { unsigned i; diff --git a/src/basic/parse-util.h b/src/basic/parse-util.h index dc09782ca89..5f1c850855b 100644 --- a/src/basic/parse-util.h +++ b/src/basic/parse-util.h @@ -37,6 +37,8 @@ int parse_ifindex(const char *s, int *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); +int parse_syscall_and_errno(const char *in, char **name, int *error); #define FORMAT_BYTES_MAX 8 char *format_bytes(char *buf, size_t l, uint64_t t); diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index 4dbd6b0f6f9..30861617bd4 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -378,7 +378,7 @@ static int property_get_syscall_filter( #if HAVE_SECCOMP Iterator i; - void *id; + void *id, *val; #endif assert(bus); @@ -394,14 +394,33 @@ static int property_get_syscall_filter( return r; #if HAVE_SECCOMP - SET_FOREACH(id, c->syscall_filter, i) { - char *name; + HASHMAP_FOREACH_KEY(val, id, c->syscall_filter, i) { + _cleanup_free_ char *name = NULL; + const char *e = NULL; + char *s; + int num = PTR_TO_INT(val); name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1); if (!name) continue; - r = strv_consume(&l, name); + if (num >= 0) { + e = errno_to_name(num); + if (e) { + s = strjoin(name, ":", e); + if (!s) + return -ENOMEM; + } else { + r = asprintf(&s, "%s:%d", name, num); + if (r < 0) + return -ENOMEM; + } + } else { + s = name; + name = NULL; + } + + r = strv_consume(&l, s); if (r < 0) return r; } @@ -1210,22 +1229,29 @@ int bus_exec_context_set_transient_property( if (strv_length(l) == 0) { c->syscall_whitelist = false; - c->syscall_filter = set_free(c->syscall_filter); + c->syscall_filter = hashmap_free(c->syscall_filter); } else { char **s; c->syscall_whitelist = whitelist; - r = set_ensure_allocated(&c->syscall_filter, NULL); + r = hashmap_ensure_allocated(&c->syscall_filter, NULL); if (r < 0) return r; STRV_FOREACH(s, l) { - if (**s == '@') { + _cleanup_free_ char *n = NULL; + int e; + + r = parse_syscall_and_errno(*s, &n, &e); + if (r < 0) + return r; + + if (*n == '@') { const SyscallFilterSet *set; const char *i; - set = syscall_filter_set_find(*s); + set = syscall_filter_set_find(n); if (!set) return -EINVAL; @@ -1236,7 +1262,7 @@ int bus_exec_context_set_transient_property( if (id == __NR_SCMP_ERROR) return -EINVAL; - r = set_put(c->syscall_filter, INT_TO_PTR(id + 1)); + r = hashmap_put(c->syscall_filter, INT_TO_PTR(id + 1), INT_TO_PTR(e)); if (r < 0) return r; } @@ -1244,11 +1270,11 @@ int bus_exec_context_set_transient_property( } else { int id; - id = seccomp_syscall_resolve_name(*s); + id = seccomp_syscall_resolve_name(n); if (id == __NR_SCMP_ERROR) return -EINVAL; - r = set_put(c->syscall_filter, INT_TO_PTR(id + 1)); + r = hashmap_put(c->syscall_filter, INT_TO_PTR(id + 1), INT_TO_PTR(e)); if (r < 0) return r; } @@ -1308,20 +1334,18 @@ int bus_exec_context_set_transient_property( } else if (streq(name, "SystemCallErrorNumber")) { int32_t n; - const char *str; r = sd_bus_message_read(message, "i", &n); if (r < 0) return r; - str = errno_to_name(n); - if (!str) + if (n <= 0 || n > ERRNO_MAX) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid SystemCallErrorNumber"); if (mode != UNIT_CHECK) { c->syscall_errno = n; - unit_write_drop_in_private_format(u, mode, name, "SystemCallErrorNumber=%s", str); + unit_write_drop_in_private_format(u, mode, name, "SystemCallErrorNumber=%d", n); } return 1; diff --git a/src/core/execute.c b/src/core/execute.c index 5187ad250fe..9f7181549e7 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -1290,7 +1290,7 @@ static bool context_has_syscall_filters(const ExecContext *c) { assert(c); return c->syscall_whitelist || - !set_isempty(c->syscall_filter); + !hashmap_isempty(c->syscall_filter); } static bool context_has_no_new_privileges(const ExecContext *c) { @@ -3528,7 +3528,7 @@ void exec_context_done(ExecContext *c) { c->apparmor_profile = mfree(c->apparmor_profile); c->smack_process_label = mfree(c->smack_process_label); - c->syscall_filter = set_free(c->syscall_filter); + c->syscall_filter = hashmap_free(c->syscall_filter); c->syscall_archs = set_free(c->syscall_archs); c->address_families = set_free(c->address_families); @@ -4065,7 +4065,7 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { if (c->syscall_filter) { #if HAVE_SECCOMP Iterator j; - void *id; + void *id, *val; bool first = true; #endif @@ -4077,8 +4077,10 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { fputc('~', f); #if HAVE_SECCOMP - SET_FOREACH(id, c->syscall_filter, j) { + HASHMAP_FOREACH_KEY(val, id, c->syscall_filter, j) { _cleanup_free_ char *name = NULL; + const char *errno_name = NULL; + int num = PTR_TO_INT(val); if (first) first = false; @@ -4087,6 +4089,14 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1); fputs(strna(name), f); + + if (num >= 0) { + errno_name = errno_to_name(num); + if (errno_name) + fprintf(f, ":%s", errno_name); + else + fprintf(f, ":%d", num); + } } #endif @@ -4119,10 +4129,17 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { prefix, s); } - if (c->syscall_errno > 0) - fprintf(f, - "%sSystemCallErrorNumber: %s\n", - prefix, strna(errno_to_name(c->syscall_errno))); + if (c->syscall_errno > 0) { + const char *errno_name; + + fprintf(f, "%sSystemCallErrorNumber: ", prefix); + + errno_name = errno_to_name(c->syscall_errno); + if (errno_name) + fprintf(f, "%s\n", errno_name); + else + fprintf(f, "%d\n", c->syscall_errno); + } if (c->apparmor_profile) fprintf(f, diff --git a/src/core/execute.h b/src/core/execute.h index 4d41990b362..23abdd4516a 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -242,7 +242,7 @@ struct ExecContext { unsigned long restrict_namespaces; /* The CLONE_NEWxyz flags permitted to the unit's processes */ - Set *syscall_filter; + Hashmap *syscall_filter; Set *syscall_archs; int syscall_errno; bool syscall_whitelist:1; diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index a4e39a84eab..ede62e7b3d0 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -2632,7 +2632,8 @@ static int syscall_filter_parse_one( ExecContext *c, bool invert, const char *t, - bool warn) { + bool warn, + int errno_num) { int r; if (t[0] == '@') { @@ -2647,7 +2648,7 @@ static int syscall_filter_parse_one( } NULSTR_FOREACH(i, set->value) { - r = syscall_filter_parse_one(unit, filename, line, c, invert, i, false); + r = syscall_filter_parse_one(unit, filename, line, c, invert, i, false, errno_num); if (r < 0) return r; } @@ -2665,13 +2666,13 @@ static int syscall_filter_parse_one( * we want to allow it, then remove it from the list */ if (!invert == c->syscall_whitelist) { - r = set_put(c->syscall_filter, INT_TO_PTR(id + 1)); + r = hashmap_put(c->syscall_filter, INT_TO_PTR(id + 1), INT_TO_PTR(errno_num)); if (r == 0) return 0; if (r < 0) return log_oom(); } else - (void) set_remove(c->syscall_filter, INT_TO_PTR(id + 1)); + (void) hashmap_remove(c->syscall_filter, INT_TO_PTR(id + 1)); } return 0; @@ -2702,7 +2703,7 @@ int config_parse_syscall_filter( if (isempty(rvalue)) { /* Empty assignment resets the list */ - c->syscall_filter = set_free(c->syscall_filter); + c->syscall_filter = hashmap_free(c->syscall_filter); c->syscall_whitelist = false; return 0; } @@ -2713,7 +2714,7 @@ int config_parse_syscall_filter( } if (!c->syscall_filter) { - c->syscall_filter = set_new(NULL); + c->syscall_filter = hashmap_new(NULL); if (!c->syscall_filter) return log_oom(); @@ -2725,7 +2726,7 @@ int config_parse_syscall_filter( c->syscall_whitelist = true; /* Accept default syscalls if we are on a whitelist */ - r = syscall_filter_parse_one(unit, filename, line, c, false, "@default", false); + r = syscall_filter_parse_one(unit, filename, line, c, false, "@default", false, -1); if (r < 0) return r; } @@ -2733,7 +2734,8 @@ int config_parse_syscall_filter( p = rvalue; for (;;) { - _cleanup_free_ char *word = NULL; + _cleanup_free_ char *word = NULL, *name = NULL; + int num; r = extract_first_word(&p, &word, NULL, 0); if (r == 0) @@ -2745,7 +2747,13 @@ int config_parse_syscall_filter( break; } - r = syscall_filter_parse_one(unit, filename, line, c, invert, word, true); + r = parse_syscall_and_errno(word, &name, &num); + if (r < 0) { + log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse syscall:errno, ignoring: %s", word); + continue; + } + + r = syscall_filter_parse_one(unit, filename, line, c, invert, name, true, num); if (r < 0) return r; } @@ -2831,8 +2839,8 @@ int config_parse_syscall_errno( return 0; } - e = errno_from_name(rvalue); - if (e < 0) { + e = parse_errno(rvalue); + if (e <= 0) { log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse error number, ignoring: %s", rvalue); return 0; } diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index b891d246532..57de9975c79 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -690,8 +690,8 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen } else if (streq(field, "SystemCallErrorNumber")) { int n; - n = errno_from_name(eq); - if (n < 0) + n = parse_errno(eq); + if (n <= 0) return log_error_errno(r, "Failed to parse %s value: %s", field, eq); r = sd_bus_message_append(m, "v", "i", (int32_t) n); diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c index f87701675bf..d60ac918b32 100644 --- a/src/shared/seccomp-util.c +++ b/src/shared/seccomp-util.c @@ -900,20 +900,20 @@ int seccomp_load_syscall_filter_set(uint32_t default_action, const SyscallFilter return 0; } -int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Set* set, uint32_t action) { +int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap* set, uint32_t action) { uint32_t arch; int r; /* Similar to seccomp_load_syscall_filter_set(), but takes a raw Set* of syscalls, instead of a * SyscallFilterSet* table. */ - if (set_isempty(set) && default_action == SCMP_ACT_ALLOW) + if (hashmap_isempty(set) && default_action == SCMP_ACT_ALLOW) return 0; SECCOMP_FOREACH_LOCAL_ARCH(arch) { _cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL; Iterator i; - void *id; + void *id, *val; log_debug("Operating on architecture: %s", seccomp_arch_to_string(arch)); @@ -921,8 +921,14 @@ int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Set* set, uint3 if (r < 0) return r; - SET_FOREACH(id, set, i) { - r = seccomp_rule_add_exact(seccomp, action, PTR_TO_INT(id) - 1, 0); + HASHMAP_FOREACH_KEY(val, id, set, i) { + uint32_t a = action; + int e = PTR_TO_INT(val); + + if (action != SCMP_ACT_ALLOW && e >= 0) + a = SCMP_ACT_ERRNO(e); + + r = seccomp_rule_add_exact(seccomp, a, PTR_TO_INT(id) - 1, 0); if (r < 0) { /* If the system call is not known on this architecture, then that's fine, let's ignore it */ _cleanup_free_ char *n = NULL; @@ -1515,7 +1521,7 @@ int parse_syscall_archs(char **l, Set **archs) { return 0; } -int seccomp_filter_set_add(Set *filter, bool add, const SyscallFilterSet *set) { +int seccomp_filter_set_add(Hashmap *filter, bool add, const SyscallFilterSet *set) { const char *i; int r; @@ -1543,11 +1549,11 @@ int seccomp_filter_set_add(Set *filter, bool add, const SyscallFilterSet *set) { } if (add) { - r = set_put(filter, INT_TO_PTR(id + 1)); + r = hashmap_put(filter, INT_TO_PTR(id + 1), INT_TO_PTR(-1)); if (r < 0) return r; } else - (void) set_remove(filter, INT_TO_PTR(id + 1)); + (void) hashmap_remove(filter, INT_TO_PTR(id + 1)); } } diff --git a/src/shared/seccomp-util.h b/src/shared/seccomp-util.h index 6dfa465ef33..a3c360cdb3a 100644 --- a/src/shared/seccomp-util.h +++ b/src/shared/seccomp-util.h @@ -73,12 +73,12 @@ extern const SyscallFilterSet syscall_filter_sets[]; const SyscallFilterSet *syscall_filter_set_find(const char *name); -int seccomp_filter_set_add(Set *s, bool b, const SyscallFilterSet *set); +int seccomp_filter_set_add(Hashmap *s, bool b, const SyscallFilterSet *set); int seccomp_add_syscall_filter_item(scmp_filter_ctx *ctx, const char *name, uint32_t action, char **exclude); int seccomp_load_syscall_filter_set(uint32_t default_action, const SyscallFilterSet *set, uint32_t action); -int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Set* set, uint32_t action); +int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap* set, uint32_t action); int seccomp_restrict_archs(Set *archs); int seccomp_restrict_namespaces(unsigned long retain); diff --git a/src/test/test-execute.c b/src/test/test-execute.c index 0468d11fe89..998724189f7 100644 --- a/src/test/test-execute.c +++ b/src/test/test-execute.c @@ -23,6 +23,7 @@ #include #include +#include "errno-list.h" #include "fileio.h" #include "fs-util.h" #include "macro.h" @@ -261,14 +262,18 @@ static void test_exec_systemcallfilter(Manager *m) { test(m, "exec-systemcallfilter-not-failing2.service", 0, CLD_EXITED); test(m, "exec-systemcallfilter-failing.service", SIGSYS, CLD_KILLED); test(m, "exec-systemcallfilter-failing2.service", SIGSYS, CLD_KILLED); + test(m, "exec-systemcallfilter-with-errno-name.service", errno_from_name("EILSEQ"), CLD_EXITED); + test(m, "exec-systemcallfilter-with-errno-number.service", 255, CLD_EXITED); #endif } static void test_exec_systemcallerrornumber(Manager *m) { #if HAVE_SECCOMP - if (is_seccomp_available()) - test(m, "exec-systemcallerrornumber.service", 1, CLD_EXITED); + if (!is_seccomp_available()) + return; + test(m, "exec-systemcallerrornumber-name.service", errno_from_name("EACCES"), CLD_EXITED); + test(m, "exec-systemcallerrornumber-number.service", 255, CLD_EXITED); #endif } diff --git a/src/test/test-parse-util.c b/src/test/test-parse-util.c index c0abc8130af..4cd82911467 100644 --- a/src/test/test-parse-util.c +++ b/src/test/test-parse-util.c @@ -21,8 +21,11 @@ #include #include +#include "alloc-util.h" +#include "errno-list.h" #include "log.h" #include "parse-util.h" +#include "string-util.h" static void test_parse_boolean(void) { assert_se(parse_boolean("1") == 1); @@ -660,6 +663,74 @@ static void test_parse_dev(void) { assert_se(parse_dev("8:11", &dev) >= 0 && major(dev) == 8 && minor(dev) == 11); } +static void test_parse_errno(void) { + assert_se(parse_errno("EILSEQ") == EILSEQ); + assert_se(parse_errno("EINVAL") == EINVAL); + assert_se(parse_errno("0") == 0); + assert_se(parse_errno("1") == 1); + assert_se(parse_errno("4095") == 4095); + + assert_se(parse_errno("-1") == -ERANGE); + assert_se(parse_errno("-3") == -ERANGE); + assert_se(parse_errno("4096") == -ERANGE); + + assert_se(parse_errno("") == -EINVAL); + assert_se(parse_errno("12.3") == -EINVAL); + assert_se(parse_errno("123junk") == -EINVAL); + assert_se(parse_errno("junk123") == -EINVAL); + assert_se(parse_errno("255EILSEQ") == -EINVAL); + assert_se(parse_errno("EINVAL12") == -EINVAL); + assert_se(parse_errno("-EINVAL") == -EINVAL); + assert_se(parse_errno("EINVALaaa") == -EINVAL); +} + +static void test_parse_syscall_and_errno(void) { + _cleanup_free_ char *n = NULL; + int e; + + assert_se(parse_syscall_and_errno("uname:EILSEQ", &n, &e) >= 0); + assert_se(streq(n, "uname")); + assert_se(e == errno_from_name("EILSEQ") && e >= 0); + n = mfree(n); + + assert_se(parse_syscall_and_errno("uname:EINVAL", &n, &e) >= 0); + assert_se(streq(n, "uname")); + assert_se(e == errno_from_name("EINVAL") && e >= 0); + n = mfree(n); + + assert_se(parse_syscall_and_errno("@sync:4095", &n, &e) >= 0); + assert_se(streq(n, "@sync")); + assert_se(e == 4095); + n = mfree(n); + + /* If errno is omitted, then e is set to -1 */ + assert_se(parse_syscall_and_errno("mount", &n, &e) >= 0); + assert_se(streq(n, "mount")); + assert_se(e == -1); + n = mfree(n); + + /* parse_syscall_and_errno() does not check the syscall name is valid or not. */ + assert_se(parse_syscall_and_errno("hoge:255", &n, &e) >= 0); + assert_se(streq(n, "hoge")); + assert_se(e == 255); + 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 */ + 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); + assert_se(parse_syscall_and_errno("hoge:123junk", &n, &e) == -EINVAL); + assert_se(parse_syscall_and_errno("hoge:junk123", &n, &e) == -EINVAL); + assert_se(parse_syscall_and_errno("hoge:255:EILSEQ", &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:", &n, &e) == -EINVAL); +} + int main(int argc, char *argv[]) { log_parse_environment(); log_open(); @@ -679,6 +750,8 @@ int main(int argc, char *argv[]) { test_parse_percent_unbounded(); test_parse_nice(); test_parse_dev(); + test_parse_errno(); + test_parse_syscall_and_errno(); return 0; } diff --git a/src/test/test-seccomp.c b/src/test/test-seccomp.c index e5f97894b7d..a1d3c6280e1 100644 --- a/src/test/test-seccomp.c +++ b/src/test/test-seccomp.c @@ -519,7 +519,7 @@ static void test_load_syscall_filter_set_raw(void) { assert_se(pid >= 0); if (pid == 0) { - _cleanup_set_free_ Set *s = NULL; + _cleanup_hashmap_free_ Hashmap *s = NULL; assert_se(access("/", F_OK) >= 0); assert_se(poll(NULL, 0, 0) == 0); @@ -528,11 +528,11 @@ static void test_load_syscall_filter_set_raw(void) { assert_se(access("/", F_OK) >= 0); assert_se(poll(NULL, 0, 0) == 0); - assert_se(s = set_new(NULL)); + assert_se(s = hashmap_new(NULL)); #if SCMP_SYS(access) >= 0 - assert_se(set_put(s, UINT32_TO_PTR(__NR_access + 1)) >= 0); + assert_se(hashmap_put(s, UINT32_TO_PTR(__NR_access + 1), INT_TO_PTR(-1)) >= 0); #else - assert_se(set_put(s, UINT32_TO_PTR(__NR_faccessat + 1)) >= 0); + assert_se(hashmap_put(s, UINT32_TO_PTR(__NR_faccessat + 1), INT_TO_PTR(-1)) >= 0); #endif assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EUCLEAN)) >= 0); @@ -542,23 +542,56 @@ static void test_load_syscall_filter_set_raw(void) { assert_se(poll(NULL, 0, 0) == 0); - s = set_free(s); + s = hashmap_free(s); - assert_se(s = set_new(NULL)); -#if SCMP_SYS(poll) >= 0 - assert_se(set_put(s, UINT32_TO_PTR(__NR_poll + 1)) >= 0); + assert_se(s = hashmap_new(NULL)); +#if SCMP_SYS(access) >= 0 + assert_se(hashmap_put(s, UINT32_TO_PTR(__NR_access + 1), INT_TO_PTR(EILSEQ)) >= 0); #else - assert_se(set_put(s, UINT32_TO_PTR(__NR_ppoll + 1)) >= 0); + assert_se(hashmap_put(s, UINT32_TO_PTR(__NR_faccessat + 1), INT_TO_PTR(EILSEQ)) >= 0); +#endif + + assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EUCLEAN)) >= 0); + + assert_se(access("/", F_OK) < 0); + assert_se(errno == EILSEQ); + + assert_se(poll(NULL, 0, 0) == 0); + + s = hashmap_free(s); + + assert_se(s = hashmap_new(NULL)); +#if SCMP_SYS(poll) >= 0 + assert_se(hashmap_put(s, UINT32_TO_PTR(__NR_poll + 1), INT_TO_PTR(-1)) >= 0); +#else + assert_se(hashmap_put(s, UINT32_TO_PTR(__NR_ppoll + 1), INT_TO_PTR(-1)) >= 0); #endif assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EUNATCH)) >= 0); assert_se(access("/", F_OK) < 0); - assert_se(errno == EUCLEAN); + assert_se(errno == EILSEQ); assert_se(poll(NULL, 0, 0) < 0); assert_se(errno == EUNATCH); + s = hashmap_free(s); + + assert_se(s = hashmap_new(NULL)); +#if SCMP_SYS(poll) >= 0 + assert_se(hashmap_put(s, UINT32_TO_PTR(__NR_poll + 1), INT_TO_PTR(EILSEQ)) >= 0); +#else + assert_se(hashmap_put(s, UINT32_TO_PTR(__NR_ppoll + 1), INT_TO_PTR(EILSEQ)) >= 0); +#endif + + assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EUNATCH)) >= 0); + + assert_se(access("/", F_OK) < 0); + assert_se(errno == EILSEQ); + + assert_se(poll(NULL, 0, 0) < 0); + assert_se(errno == EILSEQ); + _exit(EXIT_SUCCESS); } diff --git a/test/meson.build b/test/meson.build index bc37946bab0..69d6c758b0a 100644 --- a/test/meson.build +++ b/test/meson.build @@ -100,13 +100,16 @@ test_data_files = ''' test-execute/exec-supplementarygroups-single-group-user.service test-execute/exec-supplementarygroups-single-group.service test-execute/exec-supplementarygroups.service - test-execute/exec-systemcallerrornumber.service + test-execute/exec-systemcallerrornumber-name.service + test-execute/exec-systemcallerrornumber-number.service test-execute/exec-systemcallfilter-failing.service test-execute/exec-systemcallfilter-failing2.service test-execute/exec-systemcallfilter-not-failing.service test-execute/exec-systemcallfilter-not-failing2.service test-execute/exec-systemcallfilter-system-user-nfsnobody.service test-execute/exec-systemcallfilter-system-user.service + test-execute/exec-systemcallfilter-with-errno-name.service + test-execute/exec-systemcallfilter-with-errno-number.service test-execute/exec-umask-0177.service test-execute/exec-umask-default.service test-execute/exec-unset-environment.service diff --git a/test/test-execute/exec-systemcallerrornumber.service b/test/test-execute/exec-systemcallerrornumber-name.service similarity index 57% rename from test/test-execute/exec-systemcallerrornumber.service rename to test/test-execute/exec-systemcallerrornumber-name.service index ff7da3c1a49..229b862794d 100644 --- a/test/test-execute/exec-systemcallerrornumber.service +++ b/test/test-execute/exec-systemcallerrornumber-name.service @@ -2,7 +2,7 @@ Description=Test for SystemCallErrorNumber [Service] -ExecStart=/bin/sh -x -c 'uname -a' +ExecStart=/bin/python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)' Type=oneshot SystemCallFilter=~uname SystemCallErrorNumber=EACCES diff --git a/test/test-execute/exec-systemcallerrornumber-number.service b/test/test-execute/exec-systemcallerrornumber-number.service new file mode 100644 index 00000000000..2e13f08bf5a --- /dev/null +++ b/test/test-execute/exec-systemcallerrornumber-number.service @@ -0,0 +1,8 @@ +[Unit] +Description=Test for SystemCallErrorNumber + +[Service] +ExecStart=/bin/python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)' +Type=oneshot +SystemCallFilter=~uname +SystemCallErrorNumber=255 diff --git a/test/test-execute/exec-systemcallfilter-with-errno-name.service b/test/test-execute/exec-systemcallfilter-with-errno-name.service new file mode 100644 index 00000000000..b9beb73b7e8 --- /dev/null +++ b/test/test-execute/exec-systemcallfilter-with-errno-name.service @@ -0,0 +1,8 @@ +[Unit] +Description=Test for SystemCallFilter with errno name + +[Service] +ExecStart=/bin/python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)' +Type=oneshot +SystemCallFilter=~uname:EILSEQ +SystemCallErrorNumber=EACCES diff --git a/test/test-execute/exec-systemcallfilter-with-errno-number.service b/test/test-execute/exec-systemcallfilter-with-errno-number.service new file mode 100644 index 00000000000..6e5019d5932 --- /dev/null +++ b/test/test-execute/exec-systemcallfilter-with-errno-number.service @@ -0,0 +1,8 @@ +[Unit] +Description=Test for SystemCallFilter with errno number + +[Service] +ExecStart=/bin/python3 -c 'import os\ntry: os.uname()\nexcept Exception as e: exit(e.errno)' +Type=oneshot +SystemCallFilter=~uname:255 +SystemCallErrorNumber=EACCES