From b718b86e1b8477f58461f3c456c944abb1428c0f Mon Sep 17 00:00:00 2001 From: Mike Yuan Date: Mon, 18 Nov 2024 19:41:07 +0100 Subject: [PATCH] core/exec-invoke: suppress placeholder home only in build_environment() Currently, get_fixed_user() employs USER_CREDS_SUPPRESS_PLACEHOLDER, meaning home path is set to NULL if it's empty or root. However, the path is also used for applying WorkingDirectory=~, and we'd spuriously use the invoking user's home as fallback even if User= is changed in that case. Let's instead delegate such suppression to build_environment(), so that home is proper initialized for usage at other steps. shell doesn't actually suffer from such problem, but it's changed too for consistency. Alternative to #34789 --- src/core/exec-invoke.c | 16 ++++++++-------- test/units/TEST-07-PID1.working-directory.sh | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+), 8 deletions(-) create mode 100755 test/units/TEST-07-PID1.working-directory.sh diff --git a/src/core/exec-invoke.c b/src/core/exec-invoke.c index 31493049a17..9d636f55295 100644 --- a/src/core/exec-invoke.c +++ b/src/core/exec-invoke.c @@ -855,10 +855,7 @@ static int get_fixed_user( assert(user_or_uid); assert(ret_username); - /* Note that we don't set $HOME or $SHELL if they are not particularly enlightening anyway - * (i.e. are "/" or "/bin/nologin"). */ - - r = get_user_creds(&user_or_uid, ret_uid, ret_gid, ret_home, ret_shell, USER_CREDS_CLEAN|USER_CREDS_SUPPRESS_PLACEHOLDER); + r = get_user_creds(&user_or_uid, ret_uid, ret_gid, ret_home, ret_shell, USER_CREDS_CLEAN); if (r < 0) return r; @@ -1883,7 +1880,10 @@ static int build_environment( } } - if (home && set_user_login_env) { + /* Note that we don't set $HOME or $SHELL if they are not particularly enlightening anyway + * (i.e. are "/" or "/bin/nologin"). */ + + if (home && set_user_login_env && !empty_or_root(home)) { x = strjoin("HOME=", home); if (!x) return -ENOMEM; @@ -1892,7 +1892,7 @@ static int build_environment( our_env[n_env++] = x; } - if (shell && set_user_login_env) { + if (shell && set_user_login_env && !shell_is_placeholder(shell)) { x = strjoin("SHELL=", shell); if (!x) return -ENOMEM; @@ -3775,7 +3775,7 @@ static int acquire_home(const ExecContext *c, const char **home, char **ret_buf) if (!c->working_directory_home) return 0; - if (c->dynamic_user) + if (c->dynamic_user || (c->user && is_this_me(c->user) <= 0)) return -EADDRNOTAVAIL; r = get_home_dir(ret_buf); @@ -4533,7 +4533,7 @@ int exec_invoke( r = acquire_home(context, &home, &home_buffer); if (r < 0) { *exit_status = EXIT_CHDIR; - return log_exec_error_errno(context, params, r, "Failed to determine $HOME for user: %m"); + return log_exec_error_errno(context, params, r, "Failed to determine $HOME for the invoking user: %m"); } /* If a socket is connected to STDIN/STDOUT/STDERR, we must drop O_NONBLOCK */ diff --git a/test/units/TEST-07-PID1.working-directory.sh b/test/units/TEST-07-PID1.working-directory.sh new file mode 100755 index 00000000000..1cff3e0602c --- /dev/null +++ b/test/units/TEST-07-PID1.working-directory.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: LGPL-2.1-or-later + +set -eux +set -o pipefail + +# shellcheck source=test/units/util.sh +. "$(dirname "$0")"/util.sh + +(! systemd-run --wait -p DynamicUser=yes \ + -p EnvironmentFile=-/usr/lib/systemd/systemd-asan-env \ + -p WorkingDirectory='~' true) + +assert_eq "$(systemd-run --pipe --uid=root -p WorkingDirectory='~' pwd)" "/root" +assert_eq "$(systemd-run --pipe --uid=nobody -p WorkingDirectory='~' pwd)" "/" +assert_eq "$(systemd-run --pipe --uid=testuser -p WorkingDirectory='~' pwd)" "/home/testuser" + +(! systemd-run --wait -p DynamicUser=yes -p User=testuser \ + -p EnvironmentFile=-/usr/lib/systemd/systemd-asan-env \ + -p WorkingDirectory='~' true) -- 2.47.3