| /* Integration tests for the dbus-daemon's uid-based hardening |
| * |
| * Author: Simon McVittie <simon.mcvittie@collabora.co.uk> |
| * Copyright © 2010-2011 Nokia Corporation |
| * Copyright © 2015 Collabora Ltd. |
| * |
| * Permission is hereby granted, free of charge, to any person |
| * obtaining a copy of this software and associated documentation files |
| * (the "Software"), to deal in the Software without restriction, |
| * including without limitation the rights to use, copy, modify, merge, |
| * publish, distribute, sublicense, and/or sell copies of the Software, |
| * and to permit persons to whom the Software is furnished to do so, |
| * subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be |
| * included in all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
| * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
| * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| * SOFTWARE. |
| */ |
| |
| #include <config.h> |
| |
| #include "test-utils-glib.h" |
| |
| typedef struct { |
| gboolean skip; |
| |
| TestMainContext *ctx; |
| |
| DBusError e; |
| GError *ge; |
| |
| GPid daemon_pid; |
| |
| DBusConnection *conn; |
| } Fixture; |
| |
| typedef struct { |
| const char *config_file; |
| TestUser user; |
| gboolean expect_success; |
| } Config; |
| |
| static void |
| setup (Fixture *f, |
| gconstpointer context) |
| { |
| const Config *config = context; |
| gchar *address; |
| |
| f->ctx = test_main_context_get (); |
| f->ge = NULL; |
| dbus_error_init (&f->e); |
| |
| address = test_get_dbus_daemon (config ? config->config_file : NULL, |
| TEST_USER_MESSAGEBUS, NULL, |
| &f->daemon_pid); |
| |
| if (address == NULL) |
| { |
| f->skip = TRUE; |
| return; |
| } |
| |
| f->conn = test_connect_to_bus_as_user (f->ctx, address, |
| config ? config->user : TEST_USER_ME); |
| |
| if (f->conn == NULL) |
| f->skip = TRUE; |
| |
| g_free (address); |
| } |
| |
| static void |
| test_uae (Fixture *f, |
| gconstpointer context) |
| { |
| const Config *config = context; |
| DBusMessage *m; |
| DBusPendingCall *pc; |
| DBusMessageIter args_iter; |
| DBusMessageIter arr_iter; |
| |
| if (f->skip) |
| return; |
| |
| m = dbus_message_new_method_call (DBUS_SERVICE_DBUS, |
| DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "UpdateActivationEnvironment"); |
| |
| if (m == NULL) |
| g_error ("OOM"); |
| |
| dbus_message_iter_init_append (m, &args_iter); |
| |
| /* Append an empty a{ss} (string => string dictionary). */ |
| if (!dbus_message_iter_open_container (&args_iter, DBUS_TYPE_ARRAY, |
| "{ss}", &arr_iter) || |
| !dbus_message_iter_close_container (&args_iter, &arr_iter)) |
| g_error ("OOM"); |
| |
| if (!dbus_connection_send_with_reply (f->conn, m, &pc, |
| DBUS_TIMEOUT_USE_DEFAULT) || |
| pc == NULL) |
| g_error ("OOM"); |
| |
| dbus_message_unref (m); |
| m = NULL; |
| |
| if (dbus_pending_call_get_completed (pc)) |
| test_pending_call_store_reply (pc, &m); |
| else if (!dbus_pending_call_set_notify (pc, test_pending_call_store_reply, |
| &m, NULL)) |
| g_error ("OOM"); |
| |
| while (m == NULL) |
| test_main_context_iterate (f->ctx, TRUE); |
| |
| if (config->expect_success) |
| { |
| /* it succeeds */ |
| g_assert_cmpint (dbus_message_get_type (m), ==, |
| DBUS_MESSAGE_TYPE_METHOD_RETURN); |
| } |
| else |
| { |
| /* it fails, yielding an error message with one string argument */ |
| g_assert_cmpint (dbus_message_get_type (m), ==, DBUS_MESSAGE_TYPE_ERROR); |
| g_assert_cmpstr (dbus_message_get_error_name (m), ==, |
| DBUS_ERROR_ACCESS_DENIED); |
| g_assert_cmpstr (dbus_message_get_signature (m), ==, "s"); |
| } |
| |
| dbus_message_unref (m); |
| } |
| |
| static void |
| test_monitor (Fixture *f, |
| gconstpointer context) |
| { |
| const Config *config = context; |
| DBusMessage *m; |
| DBusPendingCall *pc; |
| DBusMessageIter args_iter; |
| DBusMessageIter arr_iter; |
| dbus_uint32_t no_flags = 0; |
| |
| if (f->skip) |
| return; |
| |
| m = dbus_message_new_method_call (DBUS_SERVICE_DBUS, |
| DBUS_PATH_DBUS, DBUS_INTERFACE_MONITORING, "BecomeMonitor"); |
| |
| if (m == NULL) |
| g_error ("OOM"); |
| |
| dbus_message_iter_init_append (m, &args_iter); |
| |
| /* Append an empty as (string array). */ |
| if (!dbus_message_iter_open_container (&args_iter, DBUS_TYPE_ARRAY, |
| "s", &arr_iter) || |
| !dbus_message_iter_close_container (&args_iter, &arr_iter) || |
| !dbus_message_iter_append_basic (&args_iter, |
| DBUS_TYPE_UINT32, &no_flags)) |
| g_error ("OOM"); |
| |
| if (!dbus_connection_send_with_reply (f->conn, m, &pc, |
| DBUS_TIMEOUT_USE_DEFAULT) || |
| pc == NULL) |
| g_error ("OOM"); |
| |
| dbus_message_unref (m); |
| m = NULL; |
| |
| if (dbus_pending_call_get_completed (pc)) |
| test_pending_call_store_reply (pc, &m); |
| else if (!dbus_pending_call_set_notify (pc, test_pending_call_store_reply, |
| &m, NULL)) |
| g_error ("OOM"); |
| |
| while (m == NULL) |
| test_main_context_iterate (f->ctx, TRUE); |
| |
| if (config->expect_success) |
| { |
| /* it succeeds */ |
| g_assert_cmpint (dbus_message_get_type (m), ==, |
| DBUS_MESSAGE_TYPE_METHOD_RETURN); |
| } |
| else |
| { |
| /* it fails, yielding an error message with one string argument */ |
| g_assert_cmpint (dbus_message_get_type (m), ==, DBUS_MESSAGE_TYPE_ERROR); |
| g_assert_cmpstr (dbus_message_get_error_name (m), ==, |
| DBUS_ERROR_ACCESS_DENIED); |
| g_assert_cmpstr (dbus_message_get_signature (m), ==, "s"); |
| } |
| |
| dbus_message_unref (m); |
| } |
| |
| static void |
| teardown (Fixture *f, |
| gconstpointer context G_GNUC_UNUSED) |
| { |
| dbus_error_free (&f->e); |
| g_clear_error (&f->ge); |
| |
| if (f->conn != NULL) |
| { |
| dbus_connection_close (f->conn); |
| dbus_connection_unref (f->conn); |
| f->conn = NULL; |
| } |
| |
| if (f->daemon_pid != 0) |
| { |
| test_kill_pid (f->daemon_pid); |
| g_spawn_close_pid (f->daemon_pid); |
| f->daemon_pid = 0; |
| } |
| |
| test_main_context_unref (f->ctx); |
| } |
| |
| static Config root_ok_config = { |
| "valid-config-files/multi-user.conf", |
| TEST_USER_ROOT, |
| TRUE |
| }; |
| |
| static Config messagebus_ok_config = { |
| "valid-config-files/multi-user.conf", |
| TEST_USER_MESSAGEBUS, |
| TRUE |
| }; |
| |
| static Config other_fail_config = { |
| "valid-config-files/multi-user.conf", |
| TEST_USER_OTHER, |
| FALSE |
| }; |
| |
| int |
| main (int argc, |
| char **argv) |
| { |
| test_init (&argc, &argv); |
| |
| /* UpdateActivationEnvironment used to be allowed by dbus-daemon for root |
| * and messagebus but not for other users (although system.conf forbids it |
| * for everyone, and it's useless). It is now hard-coded to fail on a |
| * system bus for everyone, so don't assert that root and messagebus |
| * may call it; continue to assert that it is denied for unprivileged |
| * users though. */ |
| g_test_add ("/uid-permissions/uae/other", Fixture, &other_fail_config, |
| setup, test_uae, teardown); |
| |
| /* BecomeMonitor has the behaviour that UAE used to have. */ |
| g_test_add ("/uid-permissions/monitor/root", Fixture, &root_ok_config, |
| setup, test_monitor, teardown); |
| g_test_add ("/uid-permissions/monitor/messagebus", Fixture, &messagebus_ok_config, |
| setup, test_monitor, teardown); |
| g_test_add ("/uid-permissions/monitor/other", Fixture, &other_fail_config, |
| setup, test_monitor, teardown); |
| |
| return g_test_run (); |
| } |