diff options
author | Simon McVittie <smcv@collabora.com> | 2022-07-18 11:14:08 +0100 |
---|---|---|
committer | Simon McVittie <smcv@collabora.com> | 2022-07-18 10:31:40 +0000 |
commit | 8b08dd32644cfdf6d6d054bfbcb836f3fefca723 (patch) | |
tree | 12344902c6616bc603253c2268aaaa3d1a10ebf1 | |
parent | a615db944c685b4fdb61c55de71b402fcb801d81 (diff) |
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>
-rw-r--r-- | test/test-utils-glib.c | 62 |
1 files changed, 62 insertions, 0 deletions
diff --git a/test/test-utils-glib.c b/test/test-utils-glib.c index 30b9836c..bcd83d11 100644 --- a/test/test-utils-glib.c +++ b/test/test-utils-glib.c @@ -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: |