diff --git a/man/run0.xml b/man/run0.xml index 5ad91240d5..ee2074b4ab 100644 --- a/man/run0.xml +++ b/man/run0.xml @@ -139,8 +139,8 @@ Switches to the specified user/group. If not specified defaults to - root, unless is used (see below), in which case this - defaults to the invoking user. + root, unless or are used (see + below), in which case this defaults to the invoking user. @@ -290,6 +290,17 @@ + + + + If specified, run0 will elevate the privileges of the selected user (using + ) or the current user if no user is explicitly selected. Currently this means + we give the user all available capabilities, but other privileges may be granted in the future as + well when using this option. + + + + diff --git a/shell-completion/bash/run0 b/shell-completion/bash/run0 index 0850b6ef96..1bba3b8145 100644 --- a/shell-completion/bash/run0 +++ b/shell-completion/bash/run0 @@ -37,7 +37,7 @@ _run0() { --machine --unit --property --description --slice -u --user -g --group --nice -D --chdir --setenv --background ) - local OPTS="${opts_with_values[*]} -h --help -V --version --no-ask-password --slice-inherit" + local OPTS="${opts_with_values[*]} -h --help -V --version --no-ask-password --slice-inherit --empower" local i for (( i=1; i <= COMP_CWORD; i++ )); do diff --git a/shell-completion/zsh/_run0 b/shell-completion/zsh/_run0 index 5998f41016..dc95486bef 100644 --- a/shell-completion/zsh/_run0 +++ b/shell-completion/zsh/_run0 @@ -52,6 +52,7 @@ local -a args=( '--machine=[Execute the operation on a local container]:machine:_sd_machines' {-h,--help}'[Show the help text and exit]' '--version[Print a short version string and exit]' + '--empower[Give privileges to selected or current user]' ) _arguments -S $args '*:: :{_normal -p $service}' diff --git a/src/run/run.c b/src/run/run.c index 1c01d4485b..b5030b9cb7 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -25,6 +25,7 @@ #include "bus-util.h" #include "bus-wait-for-jobs.h" #include "calendarspec.h" +#include "capability-util.h" #include "capsule-util.h" #include "chase.h" #include "env-util.h" @@ -117,6 +118,7 @@ static char *arg_shell_prompt_prefix = NULL; static int arg_lightweight = -1; static char *arg_area = NULL; static bool arg_via_shell = false; +static bool arg_empower = false; STATIC_DESTRUCTOR_REGISTER(arg_description, freep); STATIC_DESTRUCTOR_REGISTER(arg_environment, strv_freep); @@ -244,6 +246,7 @@ static int help_sudo_mode(void) { " --lightweight=BOOLEAN Control whether to register a session with service manager\n" " or without\n" " --area=AREA Home area to log into\n" + " --empower Give privileges to selected or current user\n" "\nSee the %s for details.\n", program_invocation_short_name, ansi_highlight(), @@ -253,11 +256,15 @@ static int help_sudo_mode(void) { return 0; } +static bool become_root(void) { + return !arg_exec_user || STR_IN_SET(arg_exec_user, "root", "0"); +} + static bool privileged_execution(void) { if (arg_runtime_scope != RUNTIME_SCOPE_SYSTEM) return false; - return !arg_exec_user || STR_IN_SET(arg_exec_user, "root", "0"); + return become_root() || arg_empower; } static int add_timer_property(const char *name, const char *val) { @@ -859,6 +866,7 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) { ARG_LIGHTWEIGHT, ARG_AREA, ARG_VIA_SHELL, + ARG_EMPOWER, }; /* If invoked as "run0" binary, let's expose a more sudo-like interface. We add various extensions @@ -888,6 +896,7 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) { { "shell-prompt-prefix", required_argument, NULL, ARG_SHELL_PROMPT_PREFIX }, { "lightweight", required_argument, NULL, ARG_LIGHTWEIGHT }, { "area", required_argument, NULL, ARG_AREA }, + { "empower", no_argument, NULL, ARG_EMPOWER }, {}, }; @@ -1027,6 +1036,10 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) { arg_via_shell = true; break; + case ARG_EMPOWER: + arg_empower = true; + break; + case '?': return -EINVAL; @@ -1034,9 +1047,13 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) { assert_not_reached(); } - if (!arg_exec_user && arg_area) { + if (!arg_exec_user && (arg_area || arg_empower)) { /* If the user specifies --area= but not --user= then consider this an area switch request, - * and default to logging into our own account */ + * and default to logging into our own account. + * + * If the user specifies --empower but not --user= then consider this a request to empower + * the current user. */ + arg_exec_user = getusername_malloc(); if (!arg_exec_user) return log_oom(); @@ -1211,8 +1228,8 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) { if (arg_lightweight >= 0) { const char *class = - arg_lightweight ? (arg_stdio == ARG_STDIO_PTY ? (privileged_execution() ? "user-early-light" : "user-light") : "background-light") : - (arg_stdio == ARG_STDIO_PTY ? (privileged_execution() ? "user-early" : "user") : "background"); + arg_lightweight ? (arg_stdio == ARG_STDIO_PTY ? (become_root() ? "user-early-light" : "user-light") : "background-light") : + (arg_stdio == ARG_STDIO_PTY ? (become_root() ? "user-early" : "user") : "background"); log_debug("Setting XDG_SESSION_CLASS to '%s'.", class); @@ -1371,6 +1388,12 @@ static int transient_service_set_properties(sd_bus_message *m, const char *pty_p return bus_log_create_error(r); } + if (arg_empower) { + r = sd_bus_message_append(m, "(sv)", "AmbientCapabilities", "t", CAP_MASK_ALL); + if (r < 0) + return bus_log_create_error(r); + } + if (arg_nice_set) { r = sd_bus_message_append(m, "(sv)", "Nice", "i", arg_nice); if (r < 0) diff --git a/test/units/TEST-74-AUX-UTILS.run.sh b/test/units/TEST-74-AUX-UTILS.run.sh index c7ca8ce615..f0799f6ca6 100755 --- a/test/units/TEST-74-AUX-UTILS.run.sh +++ b/test/units/TEST-74-AUX-UTILS.run.sh @@ -297,6 +297,14 @@ if [[ -e /usr/lib/pam.d/systemd-run0 ]] || [[ -e /etc/pam.d/systemd-run0 ]]; the # Validate when we invoke run0 without a tty, that depending on --pty it either allocates a tty or not assert_neq "$(run0 --pty tty < /dev/null)" "not a tty" assert_eq "$(run0 --pipe tty < /dev/null)" "not a tty" + + # Validate that --empower gives all capabilities to a non-root user. + caps="$(run0 -u testuser --empower systemd-analyze capability --mask "$(grep CapEff /proc/self/status | cut -d':' -f2)" --json=pretty | jq -r length)" + assert_neq "$caps" "0" + + run0 -u testuser --empower touch /run/empower + assert_eq "$(stat -c "%U" /run/empower)" testuser + rm /run/empower fi # Tests whether intermediate disconnects corrupt us (modified testcase from https://github.com/systemd/systemd/issues/27204)