core,run0: fixlets/tweaks around $SHELL handling (#37052)

Fixes #35007
This commit is contained in:
Yu Watanabe
2025-04-10 04:29:30 +09:00
committed by GitHub
3 changed files with 38 additions and 14 deletions

View File

@@ -854,6 +854,7 @@ restore_stdio:
static int get_fixed_user(
const char *user_or_uid,
bool prefer_nss,
const char **ret_username,
uid_t *ret_uid,
gid_t *ret_gid,
@@ -865,7 +866,8 @@ static int get_fixed_user(
assert(user_or_uid);
assert(ret_username);
r = get_user_creds(&user_or_uid, ret_uid, ret_gid, ret_home, ret_shell, USER_CREDS_CLEAN);
r = get_user_creds(&user_or_uid, ret_uid, ret_gid, ret_home, ret_shell,
USER_CREDS_CLEAN|(prefer_nss ? USER_CREDS_PREFER_NSS : 0));
if (r < 0)
return r;
@@ -1973,8 +1975,10 @@ static int build_environment(
* could cause problem for e.g. getty, since login doesn't override $HOME, and $LOGNAME and $SHELL don't
* really make much sense since we're not logged in. Hence we conditionalize the three based on
* SetLoginEnvironment= switch. */
if (!c->user && !c->dynamic_user && p->runtime_scope == RUNTIME_SCOPE_SYSTEM) {
r = get_fixed_user("root", &username, NULL, NULL, &home, &shell);
if (!username && !c->dynamic_user && p->runtime_scope == RUNTIME_SCOPE_SYSTEM) {
assert(!c->user);
r = get_fixed_user("root", /* prefer_nss = */ false, &username, NULL, NULL, &home, &shell);
if (r < 0)
return log_exec_debug_errno(c,
p,
@@ -4904,7 +4908,14 @@ int exec_invoke(
u = NULL;
if (u) {
r = get_fixed_user(u, &username, &uid, &gid, &pwent_home, &shell);
/* We can't use nss unconditionally for root without risking deadlocks if some IPC services
* will be started by pid1 and are ordered after us. But if SetLoginEnvironment= is
* enabled *explicitly* (i.e. no exec_context_get_set_login_environment() here),
* or PAM shall be invoked, let's consult NSS even for root, so that the user
* gets accurate $SHELL in session(-like) contexts. */
r = get_fixed_user(u,
/* prefer_nss = */ context->set_login_environment > 0 || context->pam_name,
&username, &uid, &gid, &pwent_home, &shell);
if (r < 0) {
*exit_status = EXIT_USER;
return log_exec_error_errno(context, params, r, "Failed to determine user credentials: %m");

View File

@@ -1029,18 +1029,25 @@ static int parse_argv_sudo_mode(int argc, char *argv[]) {
const char *e;
e = strv_env_get(arg_environment, "SHELL");
if (e)
if (e) {
arg_exec_path = strdup(e);
else {
if (!arg_exec_path)
return log_oom();
} else {
if (arg_transport == BUS_TRANSPORT_LOCAL) {
r = get_shell(&arg_exec_path);
if (r < 0)
return log_error_errno(r, "Failed to determine shell: %m");
} else
} else {
arg_exec_path = strdup("/bin/sh");
if (!arg_exec_path)
return log_oom();
}
r = strv_env_assign(&arg_environment, "SHELL", arg_exec_path);
if (r < 0)
return log_error_errno(r, "Failed to set $SHELL environment variable: %m");
}
if (!arg_exec_path)
return log_oom();
l = make_login_shell_cmdline(arg_exec_path);
}

View File

@@ -248,13 +248,19 @@ if [[ -e /usr/lib/pam.d/systemd-run0 ]] || [[ -e /etc/pam.d/systemd-run0 ]]; the
run0 ls /
assert_eq "$(run0 echo foo)" "foo"
# Check if we set some expected environment variables
for arg in "" "--user=root" "--user=0" "--user=testuser"; do
assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_USER')" "$USER"
assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_UID')" "$(id -u "$USER")"
assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_GID')" "$(id -u "$USER")"
for tu in "" "root" "0" "testuser"; do
assert_eq "$(run0 ${tu:+"--user=$tu"} bash -c 'echo $SUDO_USER')" "$USER"
assert_eq "$(run0 ${tu:+"--user=$tu"} bash -c 'echo $SUDO_UID')" "$(id -u "$USER")"
assert_eq "$(run0 ${tu:+"--user=$tu"} bash -c 'echo $SUDO_GID')" "$(id -u "$USER")"
# Validate that we actually went properly through PAM (XDG_SESSION_TYPE is set by pam_systemd)
assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $XDG_SESSION_TYPE')" "unspecified"
assert_eq "$(run0 ${tu:+"--user=$tu"} bash -c 'echo $XDG_SESSION_TYPE')" "unspecified"
if [[ -n "$tu" ]]; then
# Validate that $SHELL is set to login shell of target user when cmdline is supplied (not invoking shell)
TARGET_LOGIN_SHELL="$(getent passwd "$tu" | cut -d: -f7)"
assert_eq "$(run0 --user="$tu" printenv SHELL)" "$TARGET_LOGIN_SHELL"
fi
done
# Let's chain a couple of run0 calls together, for fun
readarray -t cmdline < <(printf "%.0srun0\n" {0..31})