]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
run: reset groups before scope uid switch
authordongshengyuan <545258830@qq.com>
Mon, 13 Jul 2026 06:12:30 +0000 (14:12 +0800)
committerdongshengyuan <545258830@qq.com>
Tue, 14 Jul 2026 04:03:22 +0000 (12:03 +0800)
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 4de33e7f3238a6fe616e61139ab87e221572e5e5

src/run/run.c
test/units/TEST-74-AUX-UTILS.run.sh

index 2fffa66301e01600fd0797e88587d76b04b36a62..5d95ee8a22a6bac11ac3ea98d788e46caee8fd36 100644 (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);
 
index 5a67ef3f10ae2fbcf1a3c47adb70524bc96d3abd..acd7bc5b6c914d590f965db3de612f5f694b2ab3 100755 (executable)
@@ -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 \