support: Pick group in support_capture_subprogram_self_sgid if UID == 0

When running as root, it is likely that we can run under any group.
Pick a harmless group from /etc/group in this case.

Reviewed-by: Carlos O'Donell <carlos@redhat.com>
(cherry picked from commit 2f769cec44)
This commit is contained in:
Florian Weimer 2025-05-21 16:47:34 +02:00
parent 61e461ed0c
commit 11e634ccf3
1 changed files with 54 additions and 4 deletions

View File

@ -21,7 +21,11 @@
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <scratch_buffer.h>
#include <stdio_ext.h>
#include <stdlib.h>
#include <string.h>
#include <support/check.h>
#include <support/xunistd.h>
#include <support/xsocket.h>
@ -210,10 +214,48 @@ err:
return status;
}
/* Returns true if a group with NAME has been found, and writes its
GID to *TARGET. */
static bool
find_sgid_group (gid_t *target, const char *name)
{
/* Do not use getgrname_r because it does not work in statically
linked binaries if the system libc is different. */
FILE *fp = fopen ("/etc/group", "rce");
if (fp == NULL)
return false;
__fsetlocking (fp, FSETLOCKING_BYCALLER);
bool ok = false;
struct scratch_buffer buf;
scratch_buffer_init (&buf);
while (true)
{
struct group grp;
struct group *result = NULL;
int status = fgetgrent_r (fp, &grp, buf.data, buf.length, &result);
if (status == 0 && result != NULL)
{
if (strcmp (result->gr_name, name) == 0)
{
*target = result->gr_gid;
ok = true;
break;
}
}
else if (errno != ERANGE)
break;
else if (!scratch_buffer_grow (&buf))
break;
}
scratch_buffer_free (&buf);
fclose (fp);
return ok;
}
int
support_capture_subprogram_self_sgid (const char *child_id)
{
gid_t target = 0;
const int count = 64;
gid_t groups[count];
@ -225,6 +267,7 @@ support_capture_subprogram_self_sgid (const char *child_id)
(intmax_t) getuid ());
gid_t current = getgid ();
gid_t target = current;
for (int i = 0; i < ret; ++i)
{
if (groups[i] != current)
@ -234,9 +277,16 @@ support_capture_subprogram_self_sgid (const char *child_id)
}
}
if (target == 0)
FAIL_UNSUPPORTED("Could not find a suitable GID for user %jd\n",
(intmax_t) getuid ());
if (target == current)
{
/* If running as root, try to find a harmless group for SGID. */
if (getuid () != 0
|| (!find_sgid_group (&target, "nogroup")
&& !find_sgid_group (&target, "bin")
&& !find_sgid_group (&target, "daemon")))
FAIL_UNSUPPORTED("Could not find a suitable GID for user %jd\n",
(intmax_t) getuid ());
}
return copy_and_spawn_sgid (child_id, target);
}