test: Skip tests that involve switching uid if unable to do so

In a Linux user namespace, it is possible that we are uid 0 but are
unable to switch to some other uid like DBUS_USER or DBUS_TEST_USER,
because the other uid is not "mapped" in the user namespace, resulting
in setuid() or setresuid() failing with EINVAL "Invalid argument".
For example, it's easy for this to happen when running under the
bubblewrap tool.

Try to drop privileges in a child process, and skip the test if we
are unable to do so.

Resolves: dbus#407
Signed-off-by: Simon McVittie <smcv@collabora.com>
This commit is contained in:
Simon McVittie 2022-07-18 11:14:08 +01:00
parent a615db944c
commit 8b08dd3264

View file

@ -40,6 +40,7 @@
# include <unistd.h>
# include <sys/socket.h>
# include <sys/types.h>
# include <sys/wait.h>
# include <pwd.h>
#endif
@ -66,6 +67,61 @@ _test_assert_no_error (const DBusError *e,
}
#ifdef DBUS_UNIX
static gboolean
can_become_user_or_skip (uid_t uid)
{
gchar *message;
pid_t child_pid;
pid_t pid;
int wstatus;
/* We can't switch to the uid without affecting the whole process,
* which we don't necessarily want to do, so try it in a child process. */
child_pid = fork ();
if (child_pid < 0)
g_error ("fork: %s", g_strerror (errno));
if (child_pid == 0)
{
/* Child process: try to become uid, exit 0 on success, exit with
* status = errno on failure */
if (setuid (uid) != 0)
{
/* make sure we report failure even if errno is wrong */
if (errno == 0)
errno = ENODATA;
_exit (errno);
}
/* success */
_exit (0);
}
/* Parent process: wait for child and report result */
pid = waitpid (child_pid, &wstatus, 0);
g_assert_cmpuint (child_pid, ==, pid);
if (WIFEXITED (wstatus) && WEXITSTATUS (wstatus) == 0)
return TRUE;
if (WIFEXITED (wstatus))
message = g_strdup_printf ("unable to become uid %lu: %s",
(unsigned long) uid,
g_strerror (WEXITSTATUS (wstatus)));
else
message = g_strdup_printf ("unable to become uid %lu: unknown wait status %d",
(unsigned long) uid,
wstatus);
g_test_skip (message);
g_free (message);
return FALSE;
}
static void
child_setup (gpointer user_data)
{
@ -141,6 +197,9 @@ spawn_dbus_daemon (const gchar *binary,
return NULL;
}
if (!can_become_user_or_skip (pwd->pw_uid))
return NULL;
if (user == TEST_USER_ROOT_DROP_TO_MESSAGEBUS)
{
/* Let the dbus-daemon start as root and drop privileges
@ -163,6 +222,9 @@ spawn_dbus_daemon (const gchar *binary,
return NULL;
}
if (!can_become_user_or_skip (pwd->pw_uid))
return NULL;
break;
case TEST_USER_ME: