Compare commits

...

5 Commits

Author SHA1 Message Date
Lennart Poettering 1fb1f637ba netlink-socket: ignore ECHRNG/EXFULL errors from recvmsg_safe() if we expect truncation
When we receive a netlink messages from userspace we need to drop it
from the queue. Hence we need call recvmsg() on the socket for it. We do
this with a zero-size socket read() buffer, so that the message would be
truncated when copied to userspace, and we do not have to allocate any
memory for it, but it's still dropped.

This was broken in ad501930d7, which
turned datagram truncation into an error (rightfully so I think – for
the common case). That broke this code here, because here we *expect*
truncation, and need to handle it gracefully.

Hence simply check for the two error codes for truncated payload or
cdata, and eat it up.

(cherry picked from commit 90755dac69)
2025-04-18 08:49:45 +02:00
Mike Yuan b893fb5c41 test: add test case for PAMName= $SHELL acquisition for root
(cherry picked from commit aadb8f9784)
2025-04-18 08:49:45 +02:00
Mike Yuan e558789a22 core/exec-invoke: consult NSS for root user creds if SetLoginEnvironment=/PAMName=
(cherry picked from commit 9c0d8b8c4f)
2025-04-18 08:49:45 +02:00
Mike Yuan a9b4a0f3a5 core/exec-invoke: never override acquired user cred with fallback one
(cherry picked from commit 2fb438cc88)
2025-04-18 08:49:45 +02:00
Mike Yuan fbfeda9ff5 run0: make sure we submit $SHELL to remote
Normally, the service manager sets $SHELL to the target user's
login shell, but run0 always overrides that with either
originating user's shell or value from --setenv=SHELL=. In both cases
$SHELL needs to be sent.

Fixes #35007

(cherry picked from commit ba7fb8cf5f)
2025-04-18 08:49:45 +02:00
4 changed files with 51 additions and 24 deletions

View File

@ -844,6 +844,7 @@ restore_stdio:
static int get_fixed_user( static int get_fixed_user(
const char *user_or_uid, const char *user_or_uid,
bool prefer_nss,
const char **ret_username, const char **ret_username,
uid_t *ret_uid, uid_t *ret_uid,
gid_t *ret_gid, gid_t *ret_gid,
@ -855,7 +856,8 @@ static int get_fixed_user(
assert(user_or_uid); assert(user_or_uid);
assert(ret_username); 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) if (r < 0)
return r; return r;
@ -1855,8 +1857,10 @@ static int build_environment(
* could cause problem for e.g. getty, since login doesn't override $HOME, and $LOGNAME and $SHELL don't * 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 * really make much sense since we're not logged in. Hence we conditionalize the three based on
* SetLoginEnvironment= switch. */ * SetLoginEnvironment= switch. */
if (!c->user && !c->dynamic_user && p->runtime_scope == RUNTIME_SCOPE_SYSTEM) { if (!username && !c->dynamic_user && p->runtime_scope == RUNTIME_SCOPE_SYSTEM) {
r = get_fixed_user("root", &username, NULL, NULL, &home, &shell); assert(!c->user);
r = get_fixed_user("root", /* prefer_nss = */ false, &username, NULL, NULL, &home, &shell);
if (r < 0) if (r < 0)
return log_exec_debug_errno(c, return log_exec_debug_errno(c,
p, p,
@ -4498,7 +4502,14 @@ int exec_invoke(
u = NULL; u = NULL;
if (u) { if (u) {
r = get_fixed_user(u, &username, &uid, &gid, &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, &home, &shell);
if (r < 0) { if (r < 0) {
*exit_status = EXIT_USER; *exit_status = EXIT_USER;
return log_exec_error_errno(context, params, r, "Failed to determine user credentials: %m"); return log_exec_error_errno(context, params, r, "Failed to determine user credentials: %m");

View File

@ -177,11 +177,8 @@ static int socket_recv_message(int fd, void *buf, size_t buf_size, uint32_t *ret
assert(peek || (buf && buf_size > 0)); assert(peek || (buf && buf_size > 0));
n = recvmsg_safe(fd, &msg, peek ? (MSG_PEEK|MSG_TRUNC) : 0); n = recvmsg_safe(fd, &msg, peek ? (MSG_PEEK|MSG_TRUNC) : 0);
if (ERRNO_IS_NEG_TRANSIENT(n)) { if (ERRNO_IS_NEG_TRANSIENT(n))
if (ret_mcast_group) goto transient;
*ret_mcast_group = 0;
return 0;
}
if (n == -ENOBUFS) if (n == -ENOBUFS)
return log_debug_errno(n, "sd-netlink: kernel receive buffer overrun"); return log_debug_errno(n, "sd-netlink: kernel receive buffer overrun");
if (n == -ECHRNG) if (n == -ECHRNG)
@ -196,15 +193,15 @@ static int socket_recv_message(int fd, void *buf, size_t buf_size, uint32_t *ret
log_debug("sd-netlink: ignoring message from PID %"PRIu32, sender.nl.nl_pid); log_debug("sd-netlink: ignoring message from PID %"PRIu32, sender.nl.nl_pid);
if (peek) { if (peek) {
/* drop the message */ /* Drop the message. Note that we ignore ECHRNG/EXFULL errors here, which
* recvmsg_safe() returns in case the payload or cdata is truncated. Here it's quite
* likely it is truncated, because we pass a zero-sized buffer. */
n = recvmsg_safe(fd, &msg, 0); n = recvmsg_safe(fd, &msg, 0);
if (n < 0) if (n < 0 && !IN_SET(n, -ECHRNG, -EXFULL))
return (int) n; return (int) n;
} }
if (ret_mcast_group) goto transient;
*ret_mcast_group = 0;
return 0;
} }
if (ret_mcast_group) { if (ret_mcast_group) {
@ -218,6 +215,12 @@ static int socket_recv_message(int fd, void *buf, size_t buf_size, uint32_t *ret
} }
return (int) n; return (int) n;
transient:
if (ret_mcast_group)
*ret_mcast_group = 0;
return 0;
} }
DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR( DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(

View File

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

View File

@ -238,13 +238,19 @@ if [[ -e /usr/lib/pam.d/systemd-run0 ]] || [[ -e /etc/pam.d/systemd-run0 ]]; the
run0 ls / run0 ls /
assert_eq "$(run0 echo foo)" "foo" assert_eq "$(run0 echo foo)" "foo"
# Check if we set some expected environment variables # Check if we set some expected environment variables
for arg in "" "--user=root" "--user=0" "--user=testuser"; do for tu in "" "root" "0" "testuser"; do
assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_USER')" "$USER" assert_eq "$(run0 ${tu:+"--user=$tu"} bash -c 'echo $SUDO_USER')" "$USER"
assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_UID')" "$(id -u "$USER")" assert_eq "$(run0 ${tu:+"--user=$tu"} bash -c 'echo $SUDO_UID')" "$(id -u "$USER")"
assert_eq "$(run0 ${arg:+"$arg"} bash -c 'echo $SUDO_GID')" "$(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) # 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 done
# Let's chain a couple of run0 calls together, for fun # Let's chain a couple of run0 calls together, for fun
readarray -t cmdline < <(printf "%.0srun0\n" {0..31}) readarray -t cmdline < <(printf "%.0srun0\n" {0..31})