run: reset groups before scope uid switch

Reproducer:
  sudo systemd-run --scope --uid=nobody /usr/bin/id

Before, the command ran as nobody but kept the caller supplementary
root group, for example groups=65534(nogroup),0(root). Scope mode
performs the uid/gid switch locally, so initialize the target user groups
before dropping privileges.

Follow-up for 4de33e7f32
This commit is contained in:
dongshengyuan
2026-07-13 14:12:30 +08:00
parent eeb8ef0366
commit df3a08b244
2 changed files with 43 additions and 13 deletions

View File

@@ -2517,6 +2517,17 @@ static int start_transient_service(sd_bus *bus) {
return EXIT_SUCCESS;
}
static int log_scope_group_setup_errno(int r, gid_t gid, const char *message) {
assert(r < 0);
assert(message);
if (!ERRNO_IS_PRIVILEGE(r) || gid != getgid())
return log_error_errno(r, "%s: %m", message);
log_debug_errno(r, "%s, ignoring: %m", message);
return 0;
}
static int start_transient_scope(sd_bus *bus) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *w = NULL;
@@ -2623,26 +2634,22 @@ static int start_transient_scope(sd_bus *bus) {
return log_error_errno(errno, "Failed to set nice level: %m");
}
gid_t gid = GID_INVALID;
if (arg_exec_group) {
gid_t gid;
r = get_group_creds(arg_exec_group, /* flags= */ 0, /* ret_name= */ NULL, &gid);
if (r < 0)
return log_error_errno(r, "Failed to resolve group '%s': %s",
arg_exec_group, STRERROR_GROUP(r));
if (setresgid(gid, gid, gid) < 0)
return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
}
uid_t uid = UID_INVALID;
if (arg_exec_user) {
_cleanup_free_ char *user = NULL, *home = NULL, *shell = NULL;
uid_t uid;
gid_t gid;
gid_t user_gid;
r = get_user_creds(arg_exec_user,
USER_CREDS_CLEAN|USER_CREDS_SUPPRESS_PLACEHOLDER|USER_CREDS_PREFER_NSS,
&user, &uid, &gid, &home, &shell);
&user, &uid, &user_gid, &home, &shell);
if (r < 0)
return log_error_errno(r, "Failed to resolve user '%s': %s",
arg_exec_user, STRERROR_USER(r));
@@ -2669,14 +2676,33 @@ static int start_transient_scope(sd_bus *bus) {
if (r < 0)
return log_oom();
if (!arg_exec_group &&
setresgid(gid, gid, gid) < 0)
return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
if (!gid_is_valid(gid))
gid = user_gid;
if (setresuid(uid, uid, uid) < 0)
return log_error_errno(errno, "Failed to change UID to " UID_FMT ": %m", uid);
r = initgroups_wrapper(arg_exec_user, gid);
if (r < 0) {
r = log_scope_group_setup_errno(
r,
gid,
strjoina("Failed to initialize supplementary groups for user '", arg_exec_user, "'"));
if (r < 0)
return r;
}
} else if (gid_is_valid(gid)) {
r = maybe_setgroups(/* size= */ 0, /* list= */ NULL);
if (r < 0) {
r = log_scope_group_setup_errno(r, gid, "Failed to drop supplementary groups");
if (r < 0)
return r;
}
}
if (gid_is_valid(gid) && setresgid(gid, gid, gid) < 0)
return log_error_errno(errno, "Failed to change GID to " GID_FMT ": %m", gid);
if (uid_is_valid(uid) && setresuid(uid, uid, uid) < 0)
return log_error_errno(errno, "Failed to change UID to " UID_FMT ": %m", uid);
if (arg_working_directory && chdir(arg_working_directory) < 0)
return log_error_errno(errno, "Failed to change directory to '%s': %m", arg_working_directory);

View File

@@ -111,6 +111,10 @@ systemd-run --scope --slice-inherit --slice=foo \
bash -xec '[[ "$(</proc/self/cgroup)" =~ /system\.slice/system-foo\.slice/run-.+\.scope$ ]]'
# We should inherit caller's environment
systemd-run --scope bash -xec '[[ "$PARENT_FOO" == bar ]]'
systemd-run --scope --uid=testuser \
bash -xec '[[ " $(id -G) " != *" 0 "* ]]'
systemd-run --scope --gid=testuser \
bash -xec '[[ " $(id -G) " != *" 0 "* ]]'
systemd-run --scope \
--property=RuntimeMaxSec=10 \
--property=RuntimeMaxSec=infinity \