mirror of
https://github.com/systemd/systemd.git
synced 2024-11-01 00:51:24 +03:00
Merge pull request #7178 from yuwata/rfe-7169-v2
core: add support to specify errno in SystemCallFilter=
This commit is contained in:
commit
cb6d2872ba
@ -1438,7 +1438,12 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
|
||||
executed by the unit processes except for the listed ones will result in immediate process termination with the
|
||||
<constant>SIGSYS</constant> signal (whitelisting). If the first character of the list is <literal>~</literal>,
|
||||
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 <constant>CAP_SYS_ADMIN</constant>
|
||||
(blacklisting). Blacklisted system calls and system call groups may optionally be suffixed with a colon
|
||||
(<literal>:</literal>) and <literal>errno</literal> error number (between 0 and 4095) or errno name such as
|
||||
<constant>EPERM</constant>, <constant>EACCES</constant> or <constant>EUCLEAN</constant>. 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 <varname>SystemCallErrorNumber=</varname>.
|
||||
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 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 <function>execve</function>,
|
||||
@ -1617,15 +1622,11 @@ CapabilityBoundingSet=~CAP_B CAP_C</programlisting>
|
||||
<varlistentry>
|
||||
<term><varname>SystemCallErrorNumber=</varname></term>
|
||||
|
||||
<listitem><para>Takes an <literal>errno</literal> error number
|
||||
name to return when the system call filter configured with
|
||||
<varname>SystemCallFilter=</varname> is triggered, instead of
|
||||
terminating the process immediately. Takes an error name such
|
||||
as <constant>EPERM</constant>, <constant>EACCES</constant> or
|
||||
<constant>EUCLEAN</constant>. 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>
|
||||
<listitem><para>Takes an <literal>errno</literal> error number (between 1 and 4095) or errno name such as
|
||||
<constant>EPERM</constant>, <constant>EACCES</constant> or <constant>EUCLEAN</constant>, to return when the
|
||||
system call filter configured with <varname>SystemCallFilter=</varname> 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.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -19,7 +19,11 @@
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#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
|
||||
}
|
||||
|
||||
|
@ -21,8 +21,11 @@
|
||||
#include <locale.h>
|
||||
#include <math.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
Loading…
Reference in New Issue
Block a user