Commit 6a49ced2 authored by Simon McVittie's avatar Simon McVittie

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
parent 1e21cc86
......@@ -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>],[User for running the DBUS daemon (messagebus)]))
AC_ARG_WITH([test_user],
[AS_HELP_STRING([--with-test-user=<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)
......
......@@ -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,
......
......@@ -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)
......
......@@ -33,9 +33,11 @@
# include <io.h>
# include <windows.h>
#else
# include <errno.h>
# include <signal.h>
# include <unistd.h>
# include <sys/types.h>
# include <pwd.h>
#endif
#include <glib.h>
......@@ -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);
......
......@@ -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,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment