]> git.ipfire.org Git - thirdparty/dbus.git/commitdiff
test: Skip tests that involve switching uid if unable to do so
authorSimon McVittie <smcv@collabora.com>
Mon, 18 Jul 2022 10:14:08 +0000 (11:14 +0100)
committerSimon McVittie <smcv@collabora.com>
Mon, 18 Jul 2022 10:31:40 +0000 (10:31 +0000)
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>
test/test-utils-glib.c

index 30b9836c520a5acac37e939572b117686b3edd5c..bcd83d111f59282d2c36606b0497b363bab8353f 100644 (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: