From 6a49ced2dd3a1e3b161c677deb72d98674a9069d Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 26 Jan 2015 15:47:59 +0000 Subject: Add infrastructure to run bits of tests under an alternative uid Bug: https://bugs.freedesktop.org/show_bug.cgi?id=88810 Reviewed-by: Philip Withnall --- test/dbus-daemon-eavesdrop.c | 2 +- test/dbus-daemon.c | 1 + test/test-utils-glib.c | 119 +++++++++++++++++++++++++++++++++++++++---- test/test-utils-glib.h | 31 +++++++++++ 4 files changed, 142 insertions(+), 11 deletions(-) (limited to 'test') diff --git a/test/dbus-daemon-eavesdrop.c b/test/dbus-daemon-eavesdrop.c index 79c1e903..686ccb35 100644 --- a/test/dbus-daemon-eavesdrop.c +++ b/test/dbus-daemon-eavesdrop.c @@ -283,7 +283,7 @@ setup (Fixture *f, f->ge = NULL; dbus_error_init (&f->e); - address = test_get_dbus_daemon (NULL, &f->daemon_pid); + address = test_get_dbus_daemon (NULL, TEST_USER_ME, &f->daemon_pid); f->sender = test_connect_to_bus (f->ctx, address); dbus_bus_request_name (f->sender, SENDER_NAME, DBUS_NAME_FLAG_DO_NOT_QUEUE, diff --git a/test/dbus-daemon.c b/test/dbus-daemon.c index 02684646..02590904 100644 --- a/test/dbus-daemon.c +++ b/test/dbus-daemon.c @@ -124,6 +124,7 @@ setup (Fixture *f, dbus_error_init (&f->e); address = test_get_dbus_daemon (config ? config->config_file : NULL, + TEST_USER_ME, &f->daemon_pid); if (address == NULL) diff --git a/test/test-utils-glib.c b/test/test-utils-glib.c index ce312f54..24f0ee46 100644 --- a/test/test-utils-glib.c +++ b/test/test-utils-glib.c @@ -33,9 +33,11 @@ # include # include #else +# include # include # include # include +# include #endif #include @@ -53,9 +55,41 @@ _test_assert_no_error (const DBusError *e, file, line, e->name, e->message); } +#ifdef DBUS_UNIX +static void +child_setup (gpointer user_data) +{ + const struct passwd *pwd = user_data; + uid_t uid = geteuid (); + + if (pwd == NULL || (pwd->pw_uid == uid && getuid () == uid)) + return; + + if (uid != 0) + g_error ("not currently euid 0: %lu", (unsigned long) uid); + + if (setuid (pwd->pw_uid) != 0) + g_error ("could not setuid (%lu): %s", + (unsigned long) pwd->pw_uid, g_strerror (errno)); + + uid = getuid (); + + if (uid != pwd->pw_uid) + g_error ("after successful setuid (%lu) my uid is %ld", + (unsigned long) pwd->pw_uid, (unsigned long) uid); + + uid = geteuid (); + + if (uid != pwd->pw_uid) + g_error ("after successful setuid (%lu) my euid is %ld", + (unsigned long) pwd->pw_uid, (unsigned long) uid); +} +#endif + static gchar * spawn_dbus_daemon (const gchar *binary, const gchar *configuration, + TestUser user, GPid *daemon_pid) { GError *error = NULL; @@ -68,13 +102,74 @@ spawn_dbus_daemon (const gchar *binary, "--print-address=1", /* stdout */ NULL }; +#ifdef DBUS_UNIX + const struct passwd *pwd = NULL; +#endif + + if (user == TEST_USER_ME) + { +#ifdef DBUS_UNIX + if (getuid () == 0) + { + g_message ("SKIP: this test is not designed to run as root"); + return NULL; + } +#endif + } + else + { +#ifdef DBUS_UNIX + if (getuid () != 0) + { + g_message ("SKIP: cannot use alternative uid when not uid 0"); + return NULL; + } + + switch (user) + { + case TEST_USER_ROOT: + break; + + case TEST_USER_MESSAGEBUS: + pwd = getpwnam (DBUS_USER); + + if (pwd == NULL) + { + g_message ("SKIP: user '%s' does not exist", DBUS_USER); + return NULL; + } + + break; + + case TEST_USER_OTHER: + pwd = getpwnam (DBUS_TEST_USER); + + if (pwd == NULL) + { + g_message ("SKIP: user '%s' does not exist", DBUS_TEST_USER); + return NULL; + } + + break; + + default: + g_assert_not_reached (); + } +#else + g_message ("SKIP: cannot use alternative uid on Windows"); + return NULL; +#endif + } g_spawn_async_with_pipes (NULL, /* working directory */ (gchar **) argv, /* g_s_a_w_p() is not const-correct :-( */ NULL, /* envp */ G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH, - NULL, /* child_setup */ - NULL, /* user data */ +#ifdef DBUS_UNIX + child_setup, (gpointer) pwd, +#else + NULL, NULL, +#endif daemon_pid, NULL, /* child's stdin = /dev/null */ &address_fd, @@ -118,6 +213,7 @@ spawn_dbus_daemon (const gchar *binary, gchar * test_get_dbus_daemon (const gchar *config_file, + TestUser user, GPid *daemon_pid) { gchar *dbus_daemon; @@ -126,12 +222,6 @@ test_get_dbus_daemon (const gchar *config_file, if (config_file != NULL) { - if (g_getenv ("DBUS_TEST_DAEMON_ADDRESS") != NULL) - { - g_message ("SKIP: cannot use DBUS_TEST_DAEMON_ADDRESS for " - "unusally-configured dbus-daemon"); - return NULL; - } if (g_getenv ("DBUS_TEST_DATA") == NULL) { @@ -167,11 +257,20 @@ test_get_dbus_daemon (const gchar *config_file, if (g_getenv ("DBUS_TEST_DAEMON_ADDRESS") != NULL) { - address = g_strdup (g_getenv ("DBUS_TEST_DAEMON_ADDRESS")); + if (config_file != NULL || user != TEST_USER_ME) + { + g_message ("SKIP: cannot use DBUS_TEST_DAEMON_ADDRESS for " + "unusally-configured dbus-daemon"); + address = NULL; + } + else + { + address = g_strdup (g_getenv ("DBUS_TEST_DAEMON_ADDRESS")); + } } else { - address = spawn_dbus_daemon (dbus_daemon, arg, daemon_pid); + address = spawn_dbus_daemon (dbus_daemon, arg, user, daemon_pid); } g_free (dbus_daemon); diff --git a/test/test-utils-glib.h b/test/test-utils-glib.h index b233384f..c672fe50 100644 --- a/test/test-utils-glib.h +++ b/test/test-utils-glib.h @@ -33,12 +33,43 @@ #include "test-utils.h" +/* + * Multi-user support for regression tests run with root privileges in + * a continuous integration system. + * + * A developer would normally run the tests as their own uid. Tests run + * as TEST_USER_ME are run, and the others are skipped. + * + * In a CI system that has access to root privileges, most tests should still + * be run as an arbitrary non-root user, as above. + * + * Certain tests can usefully be run again, as root. When this is done, + * tests using TEST_USER_ME will be skipped, and tests using TEST_USER_ROOT, + * TEST_USER_MESSAGEBUS and/or TEST_USER_OTHER can exercise situations + * that only arise when there's more than one uid. + */ +typedef enum { + /* Whatever non-root user happens to be running the regression test; + * such tests also work on Windows */ + TEST_USER_ME, + /* Must be uid 0 on Unix; the test is skipped on Windows */ + TEST_USER_ROOT, + /* The user who would normally run the system bus. This is the DBUS_USER + * from configure.ac, usually 'messagebus' but perhaps 'dbus' or + * '_dbus'. */ + TEST_USER_MESSAGEBUS, + /* An unprivileged user who is neither root nor DBUS_USER. + * This is DBUS_TEST_USER from configure.ac, usually 'nobody'. */ + TEST_USER_OTHER +} TestUser; + #define test_assert_no_error(e) _test_assert_no_error (e, __FILE__, __LINE__) void _test_assert_no_error (const DBusError *e, const char *file, int line); gchar *test_get_dbus_daemon (const gchar *config_file, + TestUser user, GPid *daemon_pid); DBusConnection *test_connect_to_bus (TestMainContext *ctx, -- cgit v1.2.1