From 44fe8b679c41632b3ec5abf9da2cb4e03e2be382 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Mon, 26 Jan 2015 15:47:59 +0000 Subject: [PATCH 05/12] Add infrastructure to run bits of tests under an alternative uid Bug: https://bugs.freedesktop.org/show_bug.cgi?id=88810 --- configure.ac | 12 ++++- test/dbus-daemon-eavesdrop.c | 2 +- test/dbus-daemon.c | 1 + test/test-utils-glib.c | 119 +++++++++++++++++++++++++++++++++++++++---- test/test-utils-glib.h | 31 +++++++++++ 5 files changed, 153 insertions(+), 12 deletions(-) diff --git a/configure.ac b/configure.ac index b9b3ce3..6deb800 100644 --- a/configure.ac +++ b/configure.ac @@ -168,6 +168,9 @@ AC_ARG_WITH(console-auth-dir, AS_HELP_STRING([--with-console-auth-dir=[dirname]] AC_ARG_WITH(console-owner-file, AS_HELP_STRING([--with-console-owner-file=[filename]],[file whose owner determines current console owner])) AC_ARG_WITH(launchd-agent-dir, AS_HELP_STRING([--with-launchd-agent-dir=[dirname]],[directory to put the launchd agent (default: /Library/LaunchAgents)])) AC_ARG_WITH(dbus_user, AS_HELP_STRING([--with-dbus-user=],[User for running the DBUS daemon (messagebus)])) +AC_ARG_WITH([test_user], + [AS_HELP_STRING([--with-test-user=], + [Unprivileged user for regression tests, other than root and the dbus_user (default: nobody)])]) AC_ARG_WITH(dbus_daemondir, AS_HELP_STRING([--with-dbus-daemondir=[dirname]],[Directory for installing the DBUS daemon])) AC_ARG_ENABLE([embedded-tests], @@ -591,7 +594,7 @@ AC_DEFINE_UNQUOTED([DBUS_USE_SYNC], [$have_sync], [Use the gcc __sync extension] AC_SEARCH_LIBS(socket,[socket network]) AC_CHECK_FUNC(gethostbyname,,[AC_CHECK_LIB(nsl,gethostbyname)]) -AC_CHECK_FUNCS([vsnprintf vasprintf nanosleep usleep setenv clearenv unsetenv socketpair getgrouplist fpathconf setrlimit poll setlocale localeconv strtoll strtoull issetugid getresuid getrlimit]) +AC_CHECK_FUNCS([vsnprintf vasprintf nanosleep usleep setenv clearenv unsetenv socketpair getgrouplist fpathconf setrlimit poll setlocale localeconv strtoll strtoull issetugid getresuid setresuid getrlimit]) AC_CHECK_HEADERS([syslog.h]) if test "x$ac_cv_header_syslog_h" = "xyes"; then @@ -1575,6 +1578,13 @@ fi AC_SUBST(DBUS_USER) AC_DEFINE_UNQUOTED(DBUS_USER,"$DBUS_USER", [User for running the system BUS daemon]) +#### User for regression tests +AS_IF([test -z "$with_test_user"], [with_test_user=nobody]) +DBUS_TEST_USER="$with_test_user" +AC_SUBST([DBUS_TEST_USER]) +AC_DEFINE_UNQUOTED([DBUS_TEST_USER], ["$DBUS_TEST_USER"], + [Unprivileged user used in some regression tests]) + #### Prefix to install into DBUS_PREFIX=$EXPANDED_PREFIX AC_SUBST(DBUS_PREFIX) diff --git a/test/dbus-daemon-eavesdrop.c b/test/dbus-daemon-eavesdrop.c index 79c1e90..686ccb3 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 0268464..0259090 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 ce312f5..24f0ee4 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 b233384..c672fe5 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, -- 2.1.4