From dd1f5bd0aa584c5e5e10fddc735155c3501eb21e Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Aug 2017 23:25:11 +0900 Subject: [PATCH 1/6] cap-list: add capability_set_{from_string,to_string_alloc}() --- src/basic/cap-list.c | 65 ++++++++++++++++++++++++++++++++++++++++ src/basic/cap-list.h | 3 ++ src/core/execute.c | 22 +++++--------- src/core/load-fragment.c | 30 +++++-------------- 4 files changed, 83 insertions(+), 37 deletions(-) diff --git a/src/basic/cap-list.c b/src/basic/cap-list.c index d68cc78d05..124641f940 100644 --- a/src/basic/cap-list.c +++ b/src/basic/cap-list.c @@ -20,7 +20,10 @@ #include #include +#include "alloc-util.h" +#include "capability-util.h" #include "cap-list.h" +#include "extract-word.h" #include "macro.h" #include "missing.h" #include "parse-util.h" @@ -64,3 +67,65 @@ int capability_from_name(const char *name) { int capability_list_length(void) { return (int) ELEMENTSOF(capability_names); } + +int capability_set_to_string_alloc(uint64_t set, char **s) { + _cleanup_free_ char *str = NULL; + unsigned long i; + size_t allocated = 0, n = 0; + + assert(s); + + for (i = 0; i < cap_last_cap(); i++) + if (set & (UINT64_C(1) << i)) { + const char *p; + size_t add; + + p = capability_to_name(i); + if (!p) + return -EINVAL; + + add = strlen(p); + + if (!GREEDY_REALLOC0(str, allocated, n + add + 2)) + return -ENOMEM; + + strcpy(mempcpy(str + n, p, add), " "); + n += add + 1; + } + + if (n != 0) + str[n - 1] = '\0'; + + *s = str; + str = NULL; + + return 0; +} + +int capability_set_from_string(const char *s, uint64_t *set) { + uint64_t val = 0; + const char *p; + + assert(set); + + for (p = s;;) { + _cleanup_free_ char *word = NULL; + int r; + + r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); + if (r == -ENOMEM) + return r; + if (r <= 0) + break; + + r = capability_from_name(word); + if (r < 0) + continue; + + val |= ((uint64_t) UINT64_C(1)) << (uint64_t) r; + } + + *set = val; + + return 0; +} diff --git a/src/basic/cap-list.h b/src/basic/cap-list.h index c1f6b94ad3..f9f6b70d80 100644 --- a/src/basic/cap-list.h +++ b/src/basic/cap-list.h @@ -22,3 +22,6 @@ const char *capability_to_name(int id); int capability_from_name(const char *name); int capability_list_length(void); + +int capability_set_to_string_alloc(uint64_t set, char **s); +int capability_set_from_string(const char *s, uint64_t *set); diff --git a/src/core/execute.c b/src/core/execute.c index 3b10fa1365..ca179426d6 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -3610,25 +3610,19 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { (c->secure_bits & 1<capability_bounding_set != CAP_ALL) { - unsigned long l; - fprintf(f, "%sCapabilityBoundingSet:", prefix); + _cleanup_free_ char *str = NULL; - for (l = 0; l <= cap_last_cap(); l++) - if (c->capability_bounding_set & (UINT64_C(1) << l)) - fprintf(f, " %s", strna(capability_to_name(l))); - - fputs("\n", f); + r = capability_set_to_string_alloc(c->capability_bounding_set, &str); + if (r >= 0) + fprintf(f, "%sCapabilityBoundingSet: %s\n", prefix, str); } if (c->capability_ambient_set != 0) { - unsigned long l; - fprintf(f, "%sAmbientCapabilities:", prefix); + _cleanup_free_ char *str = NULL; - for (l = 0; l <= cap_last_cap(); l++) - if (c->capability_ambient_set & (UINT64_C(1) << l)) - fprintf(f, " %s", strna(capability_to_name(l))); - - fputs("\n", f); + r = capability_set_to_string_alloc(c->capability_ambient_set, &str); + if (r >= 0) + fprintf(f, "%sAmbientCapabilities: %s\n", prefix, str); } if (c->user) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index ac946561dc..63144c4efc 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -1157,7 +1157,7 @@ int config_parse_capability_set( uint64_t *capability_set = data; uint64_t sum = 0, initial = 0; bool invert = false; - const char *p; + int r; assert(filename); assert(lvalue); @@ -1173,28 +1173,12 @@ int config_parse_capability_set( initial = CAP_ALL; /* initialized to all bits on */ /* else "AmbientCapabilities" initialized to all bits off */ - p = rvalue; - for (;;) { - _cleanup_free_ char *word = NULL; - int cap, r; - - r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); - if (r == 0) - break; - if (r == -ENOMEM) - return log_oom(); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse word, ignoring: %s", rvalue); - break; - } - - cap = capability_from_name(word); - if (cap < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse capability in bounding/ambient set, ignoring: %s", word); - continue; - } - - sum |= ((uint64_t) UINT64_C(1)) << (uint64_t) cap; + r = capability_set_from_string(rvalue, &sum); + if (r == -ENOMEM) + return log_oom(); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse word: %s", rvalue); + return 0; } sum = invert ? ~sum : sum; From 07d46372fe19e58614906800690c8e0e4a75dc58 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Mon, 7 Aug 2017 23:40:25 +0900 Subject: [PATCH 2/6] securebits-util: add secure_bits_{from_string,to_string_alloc}() --- src/basic/meson.build | 2 + src/basic/securebits-util.c | 84 +++++++++++++++++++++++++++++++++++++ src/basic/securebits-util.h | 28 +++++++++++++ src/core/execute.c | 17 ++++---- src/core/load-fragment.c | 44 ++++++------------- 5 files changed, 134 insertions(+), 41 deletions(-) create mode 100644 src/basic/securebits-util.c create mode 100644 src/basic/securebits-util.h diff --git a/src/basic/meson.build b/src/basic/meson.build index 065f0ac4af..67cc27274d 100644 --- a/src/basic/meson.build +++ b/src/basic/meson.build @@ -139,6 +139,8 @@ basic_sources_plain = files(''' rm-rf.c rm-rf.h securebits.h + securebits-util.c + securebits-util.h selinux-util.c selinux-util.h set.h diff --git a/src/basic/securebits-util.c b/src/basic/securebits-util.c new file mode 100644 index 0000000000..011ec36af4 --- /dev/null +++ b/src/basic/securebits-util.c @@ -0,0 +1,84 @@ +/*** + This file is part of systemd. + + Copyright 2017 Yu Watanabe + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include + +#include "alloc-util.h" +#include "extract-word.h" +#include "securebits.h" +#include "securebits-util.h" +#include "string-util.h" + +int secure_bits_to_string_alloc(int i, char **s) { + _cleanup_free_ char *str = NULL; + size_t len; + int r; + + assert(s); + + r = asprintf(&str, "%s%s%s%s%s%s", + (i & (1 << SECURE_KEEP_CAPS)) ? "keep-caps " : "", + (i & (1 << SECURE_KEEP_CAPS_LOCKED)) ? "keep-caps-locked " : "", + (i & (1 << SECURE_NO_SETUID_FIXUP)) ? "no-setuid-fixup " : "", + (i & (1 << SECURE_NO_SETUID_FIXUP_LOCKED)) ? "no-setuid-fixup-locked " : "", + (i & (1 << SECURE_NOROOT)) ? "noroot " : "", + (i & (1 << SECURE_NOROOT_LOCKED)) ? "noroot-locked " : ""); + if (r < 0) + return -ENOMEM; + + len = strlen(str); + if (len != 0) + str[len - 1] = '\0'; + + *s = str; + str = NULL; + + return 0; +} + +int secure_bits_from_string(const char *s) { + int secure_bits = 0; + const char *p; + int r; + + for (p = s;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); + if (r == -ENOMEM) + return r; + if (r <= 0) + break; + + if (streq(word, "keep-caps")) + secure_bits |= 1 << SECURE_KEEP_CAPS; + else if (streq(word, "keep-caps-locked")) + secure_bits |= 1 << SECURE_KEEP_CAPS_LOCKED; + else if (streq(word, "no-setuid-fixup")) + secure_bits |= 1 << SECURE_NO_SETUID_FIXUP; + else if (streq(word, "no-setuid-fixup-locked")) + secure_bits |= 1 << SECURE_NO_SETUID_FIXUP_LOCKED; + else if (streq(word, "noroot")) + secure_bits |= 1 << SECURE_NOROOT; + else if (streq(word, "noroot-locked")) + secure_bits |= 1 << SECURE_NOROOT_LOCKED; + } + + return secure_bits; +} diff --git a/src/basic/securebits-util.h b/src/basic/securebits-util.h new file mode 100644 index 0000000000..b4d970c366 --- /dev/null +++ b/src/basic/securebits-util.h @@ -0,0 +1,28 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2017 Yu Watanabe + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "securebits.h" + +int secure_bits_to_string_alloc(int i, char **s); +int secure_bits_from_string(const char *s); +static inline bool secure_bits_is_valid(int i) { + return ((SECURE_ALL_BITS | SECURE_ALL_LOCKS) & i) == i; +} diff --git a/src/core/execute.c b/src/core/execute.c index ca179426d6..55f9aab4b2 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -90,6 +90,7 @@ #include "seccomp-util.h" #endif #include "securebits.h" +#include "securebits-util.h" #include "selinux-util.h" #include "signal-util.h" #include "smack-util.h" @@ -3599,15 +3600,13 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { fprintf(f, "%sSyslogLevel: %s\n", prefix, lvl_str); } - if (c->secure_bits) - fprintf(f, "%sSecure Bits:%s%s%s%s%s%s\n", - prefix, - (c->secure_bits & 1<secure_bits & 1<secure_bits & 1<secure_bits & 1<secure_bits & 1<secure_bits & 1<secure_bits) { + _cleanup_free_ char *str = NULL; + + r = secure_bits_to_string_alloc(c->secure_bits, &str); + if (r >= 0) + fprintf(f, "%sSecure Bits: %s\n", prefix, str); + } if (c->capability_bounding_set != CAP_ALL) { _cleanup_free_ char *str = NULL; diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 63144c4efc..d9da1a422e 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -58,6 +58,7 @@ #include "seccomp-util.h" #endif #include "securebits.h" +#include "securebits-util.h" #include "signal-util.h" #include "stat-util.h" #include "string-util.h" @@ -1094,7 +1095,6 @@ int config_parse_exec_secure_bits(const char *unit, void *userdata) { ExecContext *c = data; - const char *p; int r; assert(filename); @@ -1108,38 +1108,18 @@ int config_parse_exec_secure_bits(const char *unit, return 0; } - for (p = rvalue;;) { - _cleanup_free_ char *word = NULL; - - r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); - if (r == 0) - return 0; - if (r == -ENOMEM) - return log_oom(); - if (r < 0) { - log_syntax(unit, LOG_WARNING, filename, line, r, - "Invalid syntax, ignoring: %s", rvalue); - return 0; - } - - if (streq(word, "keep-caps")) - c->secure_bits |= 1<secure_bits |= 1<secure_bits |= 1<secure_bits |= 1<secure_bits |= 1<secure_bits |= 1<secure_bits = r; + + return 0; } int config_parse_capability_set( From 032cf8e4d2619ec6e51cf334fe4cd3e5315b8145 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 2 Aug 2017 13:42:13 +0900 Subject: [PATCH 3/6] cpu-set-util: add parse_cpu_set() --- src/basic/cpu-set-util.c | 46 ++++++++++++++++++++++++++++++++++++++++ src/basic/cpu-set-util.h | 1 + 2 files changed, 47 insertions(+) diff --git a/src/basic/cpu-set-util.c b/src/basic/cpu-set-util.c index 95ed6928ff..aec0edc3dc 100644 --- a/src/basic/cpu-set-util.c +++ b/src/basic/cpu-set-util.c @@ -112,3 +112,49 @@ int parse_cpu_set_and_warn( return (int) ncpus; } + +int parse_cpu_set( + const char *rvalue, + cpu_set_t **cpu_set) { + + _cleanup_cpu_free_ cpu_set_t *c = NULL; + unsigned ncpus = 0; + + assert(rvalue); + + for (;;) { + _cleanup_free_ char *word = NULL; + unsigned cpu, cpu_lower, cpu_upper; + int r; + + r = extract_first_word(&rvalue, &word, WHITESPACE ",", EXTRACT_QUOTES); + if (r == -ENOMEM) + return r; + if (r <= 0) + break; + + if (!c) { + c = cpu_set_malloc(&ncpus); + if (!c) + return -ENOMEM; + } + + r = parse_range(word, &cpu_lower, &cpu_upper); + if (r < 0) + return r; + if (cpu_lower >= ncpus || cpu_upper >= ncpus) + return -EINVAL; + + if (cpu_lower <= cpu_upper) + for (cpu = cpu_lower; cpu <= cpu_upper; cpu++) + CPU_SET_S(cpu, CPU_ALLOC_SIZE(ncpus), c); + } + + /* On success, sets *cpu_set and returns ncpus for the system. */ + if (c) { + *cpu_set = c; + c = NULL; + } + + return (int) ncpus; +} diff --git a/src/basic/cpu-set-util.h b/src/basic/cpu-set-util.h index 6f49d9afb0..9b08ba0a67 100644 --- a/src/basic/cpu-set-util.h +++ b/src/basic/cpu-set-util.h @@ -30,3 +30,4 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(cpu_set_t*, CPU_FREE); cpu_set_t* cpu_set_malloc(unsigned *ncpus); int parse_cpu_set_and_warn(const char *rvalue, cpu_set_t **cpu_set, const char *unit, const char *filename, unsigned line, const char *lvalue); +int parse_cpu_set(const char *rvalue, cpu_set_t **cpu_set); From 29ea9f0f42c20d9cad137ea7f7b0e19b630e5235 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 2 Aug 2017 13:43:44 +0900 Subject: [PATCH 4/6] process-util: add sched_{policy,priority}_is_valid() --- src/basic/process-util.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/basic/process-util.h b/src/basic/process-util.h index b45d60dbd1..913991b3cb 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -20,6 +20,7 @@ ***/ #include +#include #include #include #include @@ -110,6 +111,14 @@ static inline bool nice_is_valid(int n) { return n >= PRIO_MIN && n < PRIO_MAX; } +static inline bool sched_policy_is_valid(int i) { + return IN_SET(i, SCHED_OTHER, SCHED_BATCH, SCHED_IDLE, SCHED_FIFO, SCHED_RR); +} + +static inline bool sched_priority_is_valid(int i) { + return i >= 0 && i <= sched_get_priority_max(SCHED_RR); +} + static inline bool ioprio_class_is_valid(int i) { return IN_SET(i, IOPRIO_CLASS_NONE, IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE); } From b16bd5350fe686a0e8e1ef39cc35c383fb1560a1 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Wed, 2 Aug 2017 13:46:45 +0900 Subject: [PATCH 5/6] seccomp-util: add parse_syscall_archs() --- src/shared/seccomp-util.c | 32 ++++++++++++++++++++++++++++++++ src/shared/seccomp-util.h | 2 ++ 2 files changed, 34 insertions(+) diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c index 5d8a6986b7..147b1b2ab2 100644 --- a/src/shared/seccomp-util.c +++ b/src/shared/seccomp-util.c @@ -30,7 +30,9 @@ #include "macro.h" #include "nsflags.h" #include "seccomp-util.h" +#include "set.h" #include "string-util.h" +#include "strv.h" #include "util.h" #include "errno-list.h" @@ -1313,3 +1315,33 @@ int seccomp_restrict_archs(Set *archs) { return seccomp_load(seccomp); } + +int parse_syscall_archs(char **l, Set **archs) { + _cleanup_set_free_ Set *_archs; + char **s; + int r; + + assert(l); + assert(archs); + + r = set_ensure_allocated(&_archs, NULL); + if (r < 0) + return r; + + STRV_FOREACH(s, l) { + uint32_t a; + + r = seccomp_arch_from_string(*s, &a); + if (r < 0) + return -EINVAL; + + r = set_put(_archs, UINT32_TO_PTR(a + 1)); + if (r < 0) + return -ENOMEM; + } + + *archs = _archs; + _archs = NULL; + + return 0; +} diff --git a/src/shared/seccomp-util.h b/src/shared/seccomp-util.h index 4438e87fa6..596539e8f5 100644 --- a/src/shared/seccomp-util.h +++ b/src/shared/seccomp-util.h @@ -84,3 +84,5 @@ extern const uint32_t seccomp_local_archs[]; (arch) = seccomp_local_archs[++_i]) DEFINE_TRIVIAL_CLEANUP_FUNC(scmp_filter_ctx, seccomp_release); + +int parse_syscall_archs(char **l, Set **archs); From cffaed83e87bde81378b2be74192193850d0e03a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 8 Aug 2017 00:37:02 +0900 Subject: [PATCH 6/6] core: add missing properties in D-Bus API Closes #6466. --- src/core/dbus-execute.c | 459 ++++++++++++++++++++++++++++++++++++- src/shared/bus-unit-util.c | 272 +++++++++++++++++++++- 2 files changed, 717 insertions(+), 14 deletions(-) diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index 12348d3423..bd4e113bec 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -27,8 +27,10 @@ #include "alloc-util.h" #include "bus-util.h" #include "capability-util.h" +#include "cap-list.h" #include "dbus-execute.h" #include "env-util.h" +#include "errno-list.h" #include "execute.h" #include "fd-util.h" #include "fileio.h" @@ -43,6 +45,7 @@ #ifdef HAVE_SECCOMP #include "seccomp-util.h" #endif +#include "securebits-util.h" #include "strv.h" #include "syslog-util.h" #include "unit-printf.h" @@ -1028,6 +1031,41 @@ int bus_exec_context_set_transient_property( } return 1; + + } else if (streq(name, "SupplementaryGroups")) { + _cleanup_strv_free_ char **l = NULL; + char **p; + + r = sd_bus_message_read_strv(message, &l); + if (r < 0) + return r; + + STRV_FOREACH(p, l) { + if (!isempty(*p) && !valid_user_group_name_or_id(*p)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid supplementary group names"); + } + + if (mode != UNIT_CHECK) { + if (strv_length(l) == 0) { + c->supplementary_groups = strv_free(c->supplementary_groups); + unit_write_drop_in_private_format(u, mode, name, "%s=", name); + } else { + _cleanup_free_ char *joined = NULL; + + r = strv_extend_strv(&c->supplementary_groups, l, true); + if (r < 0) + return -ENOMEM; + + joined = strv_join(c->supplementary_groups, " "); + if (!joined) + return -ENOMEM; + + unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, joined); + } + } + + return 1; + } else if (streq(name, "SyslogIdentifier")) { const char *id; @@ -1077,6 +1115,361 @@ int bus_exec_context_set_transient_property( unit_write_drop_in_private_format(u, mode, name, "SyslogFacility=%i", facility); } + return 1; + } else if (streq(name, "SecureBits")) { + int n; + + r = sd_bus_message_read(message, "i", &n); + if (r < 0) + return r; + + if (!secure_bits_is_valid(n)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid secure bits"); + + if (mode != UNIT_CHECK) { + _cleanup_free_ char *str = NULL; + + c->secure_bits = n; + r = secure_bits_to_string_alloc(n, &str); + if (r < 0) + return r; + + unit_write_drop_in_private_format(u, mode, name, "SecureBits=%s", str); + } + + return 1; + } else if (STR_IN_SET(name, "CapabilityBoundingSet", "AmbientCapabilities")) { + uint64_t n; + + r = sd_bus_message_read(message, "t", &n); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + _cleanup_free_ char *str = NULL; + + if (streq(name, "CapabilityBoundingSet")) + c->capability_bounding_set = n; + else /* "AmbientCapabilities" */ + c->capability_ambient_set = n; + + r = capability_set_to_string_alloc(n, &str); + if (r < 0) + return r; + + unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, str); + } + + return 1; + + } else if (streq(name, "Personality")) { + const char *s; + unsigned long p; + + r = sd_bus_message_read(message, "s", &s); + if (r < 0) + return r; + + p = personality_from_string(s); + if (p == PERSONALITY_INVALID) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid personality"); + + if (mode != UNIT_CHECK) { + _cleanup_free_ char *str = NULL; + + c->personality = p; + unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, s); + } + + return 1; + +#ifdef HAVE_SECCOMP + + } else if (streq(name, "SystemCallFilter")) { + int whitelist; + _cleanup_strv_free_ char **l; + + r = sd_bus_message_enter_container(message, 'r', "bas"); + if (r < 0) + return r; + + r = sd_bus_message_read(message, "b", &whitelist); + if (r < 0) + return r; + + r = sd_bus_message_read_strv(message, &l); + if (r < 0) + return r; + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + _cleanup_free_ char *joined = NULL; + + if (strv_length(l) == 0) { + c->syscall_whitelist = false; + c->syscall_filter = set_free(c->syscall_filter); + } else { + char **s; + + c->syscall_whitelist = whitelist; + + r = set_ensure_allocated(&c->syscall_filter, NULL); + if (r < 0) + return r; + + STRV_FOREACH(s, l) { + if (**s == '@') { + const SyscallFilterSet *set; + const char *i; + + set = syscall_filter_set_find(*s); + if (!set) + return -EINVAL; + + NULSTR_FOREACH(i, set->value) { + int id; + + id = seccomp_syscall_resolve_name(i); + if (id == __NR_SCMP_ERROR) + return -EINVAL; + + r = set_put(c->address_families, INT_TO_PTR(id + 1)); + if (r < 0) + return r; + } + + } else { + int id; + + id = seccomp_syscall_resolve_name(*s); + if (id == __NR_SCMP_ERROR) + return -EINVAL; + + r = set_put(c->address_families, INT_TO_PTR(id + 1)); + if (r < 0) + return r; + } + } + } + + joined = strv_join(l, " "); + if (!joined) + return -ENOMEM; + + unit_write_drop_in_private_format(u, mode, name, "SystemCallFilter=%s%s", whitelist ? "" : "~", joined); + } + + return 1; + + } else if (streq(name, "SystemCallArchitectures")) { + _cleanup_strv_free_ char **l = NULL; + + r = sd_bus_message_read_strv(message, &l); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + _cleanup_free_ char *joined = NULL; + + if (strv_length(l) == 0) + c->syscall_archs = set_free(c->syscall_archs); + else { + char **s; + + r = set_ensure_allocated(&c->syscall_archs, NULL); + if (r < 0) + return r; + + STRV_FOREACH(s, l) { + uint32_t a; + + r = seccomp_arch_from_string(*s, &a); + if (r < 0) + return r; + + r = set_put(c->syscall_archs, UINT32_TO_PTR(a + 1)); + if (r < 0) + return r; + } + + } + + joined = strv_join(l, " "); + if (!joined) + return -ENOMEM; + + unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, joined); + } + + return 1; + + } 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) + 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); + } + + return 1; + + } else if (streq(name, "RestrictAddressFamilies")) { + int whitelist; + _cleanup_strv_free_ char **l; + + r = sd_bus_message_enter_container(message, 'r', "bas"); + if (r < 0) + return r; + + r = sd_bus_message_read(message, "b", &whitelist); + if (r < 0) + return r; + + r = sd_bus_message_read_strv(message, &l); + if (r < 0) + return r; + + r = sd_bus_message_exit_container(message); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + _cleanup_free_ char *joined = NULL; + + if (strv_length(l) == 0) { + c->address_families_whitelist = false; + c->address_families = set_free(c->address_families); + } else { + char **s; + + c->address_families_whitelist = whitelist; + + r = set_ensure_allocated(&c->address_families, NULL); + if (r < 0) + return r; + + STRV_FOREACH(s, l) { + int af; + + af = af_from_name(*s); + if (af <= 0) + return -EINVAL; + + r = set_put(c->address_families, INT_TO_PTR(af)); + if (r < 0) + return r; + } + } + + joined = strv_join(l, " "); + if (!joined) + return -ENOMEM; + + unit_write_drop_in_private_format(u, mode, name, "RestrictAddressFamilies=%s%s", whitelist ? "" : "~", joined); + } + + return 1; +#endif + + } else if (streq(name, "CPUSchedulingPolicy")) { + int32_t n; + + r = sd_bus_message_read(message, "i", &n); + if (r < 0) + return r; + + if (!sched_policy_is_valid(n)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid CPU scheduling policy"); + + if (mode != UNIT_CHECK) { + _cleanup_free_ char *str = NULL; + + c->cpu_sched_policy = n; + r = sched_policy_to_string_alloc(n, &str); + if (r < 0) + return r; + + unit_write_drop_in_private_format(u, mode, name, "CPUSchedulingPolicy=%s", str); + } + + return 1; + + } else if (streq(name, "CPUSchedulingPriority")) { + int32_t n; + + r = sd_bus_message_read(message, "i", &n); + if (r < 0) + return r; + + if (!ioprio_priority_is_valid(n)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid CPU scheduling priority"); + + if (mode != UNIT_CHECK) { + c->cpu_sched_priority = n; + unit_write_drop_in_private_format(u, mode, name, "CPUSchedulingPriority=%i", n); + } + + return 1; + + } else if (streq(name, "CPUAffinity")) { + const void *a; + size_t n = 0; + + r = sd_bus_message_read_array(message, 'y', &a, &n); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + if (n == 0) { + c->cpuset = mfree(c->cpuset); + unit_write_drop_in_private_format(u, mode, name, "%s=", name); + } else { + _cleanup_free_ char *str = NULL; + uint8_t *l; + size_t allocated = 0, len = 0, i; + + c->cpuset = (cpu_set_t*) memdup(a, sizeof(cpu_set_t) * n); + if (c->cpuset) + return -ENOMEM; + + l = (uint8_t*) a; + for (i = 0; i < n; i++) { + _cleanup_free_ char *p = NULL; + size_t add; + + r = asprintf(&p, "%hhi", l[i]); + if (r < 0) + return -ENOMEM; + + add = strlen(p); + + if (GREEDY_REALLOC(str, allocated, len + add + 2)) + return -ENOMEM; + + strcpy(mempcpy(str + len, p, add), " "); + len += add + 1; + } + + if (len != 0) + str[len - 1] = '\0'; + + unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, str); + } + } + return 1; } else if (streq(name, "Nice")) { int32_t n; @@ -1297,11 +1690,12 @@ int bus_exec_context_set_transient_property( return 1; } else if (STR_IN_SET(name, - "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", + "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate", "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers", "NoNewPrivileges", "SyslogLevelPrefix", "MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser", "RemoveIPC", "ProtectKernelTunables", - "ProtectKernelModules", "ProtectControlGroups", "MountAPIVFS")) { + "ProtectKernelModules", "ProtectControlGroups", "MountAPIVFS", + "CPUSchedulingResetOnFork", "NonBlocking")) { int b; r = sd_bus_message_read(message, "b", &b); @@ -1315,6 +1709,8 @@ int bus_exec_context_set_transient_property( c->tty_vhangup = b; else if (streq(name, "TTYReset")) c->tty_reset = b; + else if (streq(name, "TTYVTDisallocate")) + c->tty_vt_disallocate = b; else if (streq(name, "PrivateTmp")) c->private_tmp = b; else if (streq(name, "PrivateDevices")) @@ -1343,6 +1739,10 @@ int bus_exec_context_set_transient_property( c->protect_control_groups = b; else if (streq(name, "MountAPIVFS")) c->mount_apivfs = b; + else if (streq(name, "CPUSchedulingResetOnFork")) + c->cpu_sched_reset_on_fork = b; + else if (streq(name, "NonBlocking")) + c->non_blocking = b; unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, yes_no(b)); } @@ -1709,7 +2109,7 @@ int bus_exec_context_set_transient_property( return 1; - } else if (STR_IN_SET(name, "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode", "LogsDirectoryMode", "ConfigurationDirectoryMode")) { + } else if (STR_IN_SET(name, "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode", "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask")) { mode_t m; r = sd_bus_message_read(message, "u", &m); @@ -1719,11 +2119,14 @@ int bus_exec_context_set_transient_property( if (mode != UNIT_CHECK) { ExecDirectoryType i; - for (i = 0; i < _EXEC_DIRECTORY_MAX; i++) - if (startswith(name, exec_directory_type_to_string(i))) { - c->directories[i].mode = m; - break; - } + if (streq(name, "UMask")) + c->umask = m; + else + for (i = 0; i < _EXEC_DIRECTORY_MAX; i++) + if (startswith(name, exec_directory_type_to_string(i))) { + c->directories[i].mode = m; + break; + } unit_write_drop_in_private_format(u, mode, name, "%s=%040o", name, m); } @@ -1777,7 +2180,6 @@ int bus_exec_context_set_transient_property( } else if (streq(name, "SELinuxContext")) { const char *s; - r = sd_bus_message_read(message, "s", &s); if (r < 0) return r; @@ -1792,6 +2194,45 @@ int bus_exec_context_set_transient_property( } return 1; + + } else if (STR_IN_SET(name, "AppArmorProfile", "SmackProcessLabel")) { + int ignore; + const char *s; + + r = sd_bus_message_enter_container(message, 'r', "bs"); + if (r < 0) + return r; + + r = sd_bus_message_read(message, "bs", &ignore, &s); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + char **p; + bool *b; + + if (streq(name, "AppArmorProfile")) { + p = &c->apparmor_profile; + b = &c->apparmor_profile_ignore; + } else { /* "SmackProcessLabel" */ + p = &c->smack_process_label; + b = &c->smack_process_label_ignore; + } + + if (isempty(s)) { + *p = mfree(*p); + *b = false; + } else { + if (free_and_strdup(p, s) < 0) + return -ENOMEM; + *b = ignore; + } + + unit_write_drop_in_private_format(u, mode, name, "%s=%s%s", name, ignore ? "-" : "", strempty(s)); + } + + return 1; + } else if (streq(name, "RestrictNamespaces")) { uint64_t flags; diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 1d77c18edb..1e720004c9 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -21,8 +21,11 @@ #include "bus-internal.h" #include "bus-unit-util.h" #include "bus-util.h" +#include "cap-list.h" #include "cgroup-util.h" +#include "cpu-set-util.h" #include "env-util.h" +#include "errno-list.h" #include "escape.h" #include "hashmap.h" #include "list.h" @@ -33,10 +36,12 @@ #include "path-util.h" #include "process-util.h" #include "rlimit-util.h" +#include "securebits-util.h" #include "signal-util.h" #include "string-util.h" #include "syslog-util.h" #include "terminal-util.h" +#include "user-util.h" #include "utf8.h" #include "util.h" @@ -204,11 +209,12 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen } else if (STR_IN_SET(field, "CPUAccounting", "MemoryAccounting", "IOAccounting", "BlockIOAccounting", "TasksAccounting", "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies", - "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit", + "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate", "RemainAfterExit", "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers", "NoNewPrivileges", "SyslogLevelPrefix", "Delegate", "RemainAfterElapse", "MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser", "RemoveIPC", "ProtectKernelTunables", - "ProtectKernelModules", "ProtectControlGroups", "MountAPIVFS")) { + "ProtectKernelModules", "ProtectControlGroups", "MountAPIVFS", + "CPUSchedulingResetOnFork")) { r = parse_boolean(eq); if (r < 0) @@ -267,10 +273,24 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen "Description", "Slice", "Type", "WorkingDirectory", "RootDirectory", "SyslogIdentifier", "ProtectSystem", "ProtectHome", "SELinuxContext", "Restart", "RootImage", - "NotifyAccess", "RuntimeDirectoryPreserve")) + "NotifyAccess", "RuntimeDirectoryPreserve", "Personality")) r = sd_bus_message_append(m, "v", "s", eq); - else if (streq(field, "SyslogLevel")) { + else if (STR_IN_SET(field, "AppArmorProfile", "SmackProcessLabel")) { + bool ignore; + const char *s; + + if (eq[0] == '-') { + ignore = true; + s = eq + 1; + } else { + ignore = false; + s = eq; + } + + r = sd_bus_message_append(m, "v", "(bs)", ignore, s); + + } else if (streq(field, "SyslogLevel")) { int level; level = log_level_from_string(eq); @@ -292,6 +312,37 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen r = sd_bus_message_append(m, "v", "i", facility); + } else if (streq(field, "SecureBits")) { + + r = secure_bits_from_string(eq); + if (r < 0) { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + r = sd_bus_message_append(m, "v", "i", r); + + } else if (STR_IN_SET(field, "CapabilityBoundingSet", "AmbientCapabilities")) { + uint64_t sum = 0; + bool invert = false; + const char *p; + + p = eq; + if (*p == '~') { + invert = true; + p++; + } + + r = capability_set_from_string(p, &sum); + if (r < 0) { + log_error("Failed to parse %s value %s.", field, eq); + return -EINVAL; + } + + sum = invert ? ~sum : sum; + + r = sd_bus_message_append(m, "v", "t", sum); + } else if (streq(field, "DeviceAllow")) { if (isempty(eq)) @@ -381,6 +432,43 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen r = sd_bus_message_append(m, "v", "a(st)", 1, path, u); } + } else if (streq(field, "CPUSchedulingPolicy")) { + int n; + + n = sched_policy_from_string(eq); + if (n < 0) + return log_error_errno(r, "Failed to parse CPUSchedulingPolicy: %s", eq); + + r = sd_bus_message_append(m, "v", "i", (int32_t) n); + + } else if (streq(field, "CPUSchedulingPriority")) { + int n; + + r = safe_atoi(eq, &n); + if (r < 0) + return log_error_errno(r, "Failed to parse CPUSchedulingPriority: %s", eq); + if (!sched_priority_is_valid(n)) + return log_error_errno(r, "Invalid CPUSchedulingPriority: %s", eq); + + r = sd_bus_message_append(m, "v", "i", (int32_t) n); + + } else if (streq(field, "CPUAffinity")) { + _cleanup_cpu_free_ cpu_set_t *cpuset = NULL; + int ncpus; + + ncpus = parse_cpu_set(eq, &cpuset); + if (ncpus < 0) + return log_error_errno(r, "Failed to parse %s value: %s", field, eq); + + r = sd_bus_message_open_container(m, 'v', "ay"); + if (r < 0) + return bus_log_create_error(r); + + if (cpuset) + sd_bus_message_append_array(m, 'y', cpuset, CPU_ALLOC_SIZE(ncpus)); + + r = sd_bus_message_close_container(m); + } else if (streq(field, "Nice")) { int n; @@ -390,6 +478,142 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen r = sd_bus_message_append(m, "v", "i", (int32_t) n); +#ifdef HAVE_SECCOMP + + } else if (streq(field, "SystemCallFilter")) { + int whitelist; + const char *p; + + r = sd_bus_message_open_container(m, 'v', "bas"); + if (r < 0) + return bus_log_create_error(r); + + p = eq; + if (*p == '~') { + whitelist = 0; + p++; + } else + whitelist = 1; + + r = sd_bus_message_append_basic(m, 'b', &whitelist); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "s"); + if (r < 0) + return bus_log_create_error(r); + + if (whitelist != 0) { + r = sd_bus_message_append_basic(m, 's', "@default"); + if (r < 0) + return bus_log_create_error(r); + } + + for (;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); + if (r < 0) + return log_error_errno(r, "Failed to parse %s value: %s", field, eq); + if (r == 0) + break; + + r = sd_bus_message_append_basic(m, 's', word); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + + } else if (streq(field, "SystemCallArchitectures")) { + const char *p; + + r = sd_bus_message_open_container(m, 'v', "as"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "s"); + if (r < 0) + return bus_log_create_error(r); + + for (p = eq;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); + if (r < 0) + return log_error_errno(r, "Failed to parse %s value: %s", field, eq); + if (r == 0) + break; + + r = sd_bus_message_append_basic(m, 's', word); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + + } else if (streq(field, "SystemCallErrorNumber")) { + int n; + + n = errno_from_name(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); + + } else if (streq(field, "RestrictAddressFamilies")) { + int whitelist; + const char *p; + + r = sd_bus_message_open_container(m, 'v', "bas"); + if (r < 0) + return bus_log_create_error(r); + + p = eq; + if (*p == '~') { + whitelist = 0; + p++; + } else + whitelist = 1; + + r = sd_bus_message_append_basic(m, 'b', &whitelist); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "s"); + if (r < 0) + return bus_log_create_error(r); + + for (;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); + if (r < 0) + return log_error_errno(r, "Failed to parse %s value: %s", field, eq); + if (r == 0) + break; + + r = sd_bus_message_append_basic(m, 's', word); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + +#endif + } else if (streq(field, "FileDescriptorStoreMax")) { unsigned u; @@ -548,7 +772,45 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen r = sd_bus_message_close_container(m); - } else if (STR_IN_SET(field, "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode", "LogsDirectoryMode", "ConfigurationDirectoryMode")) { + } else if (streq(field, "SupplementaryGroups")) { + const char *p; + + r = sd_bus_message_open_container(m, 'v', "as"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(m, 'a', "s"); + if (r < 0) + return bus_log_create_error(r); + + for (p = eq;;) { + _cleanup_free_ char *word = NULL; + + r = extract_first_word(&p, &word, NULL, EXTRACT_QUOTES); + if (r < 0) { + log_error("Failed to parse %s value %s", field, eq); + return -EINVAL; + } + if (r == 0) + break; + + if (!valid_user_group_name_or_id(word)) { + log_error("Failed to parse %s value %s", field, eq); + return -EINVAL; + } + + r = sd_bus_message_append_basic(m, 's', word); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_message_close_container(m); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(m); + + } else if (STR_IN_SET(field, "RuntimeDirectoryMode", "StateDirectoryMode", "CacheDirectoryMode", "LogsDirectoryMode", "ConfigurationDirectoryMode", "UMask")) { mode_t mode; r = parse_mode(eq, &mode);