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>,
#if HAVE_SECCOMP
Iterator i;
- void *id;
+ void *id, *val;
#endif
assert(bus);
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;
}
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;
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;
}
} 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;
}
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) {
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);
if (c->syscall_filter) {
#if HAVE_SECCOMP
Iterator j;
- void *id;
+ void *id, *val;
bool first = true;
#endif
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;
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
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;
ExecContext *c,
bool invert,
const char *t,
- bool warn) {
+ bool warn,
+ int errno_num) {
int r;
if (t[0] == '@') {
}
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;
}
* 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;
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;
}
}
if (!c->syscall_filter) {
- c->syscall_filter = set_new(NULL);
+ c->syscall_filter = hashmap_new(NULL);
if (!c->syscall_filter)
return log_oom();
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;
}
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)
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;
}
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));
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;
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;
}
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));
}
}
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);