Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package xdg-dbus-proxy for openSUSE:Factory checked in at 2026-08-22 21:34:26 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/xdg-dbus-proxy (Old) and /work/SRC/openSUSE:Factory/.xdg-dbus-proxy.new.1258 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "xdg-dbus-proxy" Sat Aug 22 21:34:26 2026 rev:9 rq:1372904 version:0.1.8 Changes: -------- --- /work/SRC/openSUSE:Factory/xdg-dbus-proxy/xdg-dbus-proxy.changes 2026-04-26 21:12:42.204829108 +0200 +++ /work/SRC/openSUSE:Factory/.xdg-dbus-proxy.new.1258/xdg-dbus-proxy.changes 2026-08-22 21:34:54.754564082 +0200 @@ -1,0 +2,15 @@ +Thu Aug 20 08:25:27 UTC 2026 - Dominique Leuenberger <[email protected]> + +- Update to version 0.1.8: + + Fix broadcast messages bypassing path/interface/member checks + + Improvements to the existing testing infrastructure + + Add tests for owning names, issuing method calls, receiving + messages + +------------------------------------------------------------------- +Mon Aug 17 10:11:41 UTC 2026 - Zoltan Balogh <[email protected]> + +- Add xdg-dbus-proxy-tests subpackage with installed + tests for gnome-desktop-testing-runner + +------------------------------------------------------------------- @@ -78 +92,0 @@ - Old: ---- xdg-dbus-proxy-0.1.7.tar.xz New: ---- xdg-dbus-proxy-0.1.8.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ xdg-dbus-proxy.spec ++++++ --- /var/tmp/diff_new_pack.rAltVY/_old 2026-08-22 21:34:56.258617712 +0200 +++ /var/tmp/diff_new_pack.rAltVY/_new 2026-08-22 21:34:56.263617891 +0200 @@ -1,7 +1,7 @@ # # spec file for package xdg-dbus-proxy # -# Copyright (c) 2024 SUSE LLC +# Copyright (c) 2026 SUSE LLC and contributors # Copyright (c) 2019 Bjørn Lie, Bryne, Norway. # # All modifications and additions to the file contributed by third parties @@ -18,7 +18,7 @@ Name: xdg-dbus-proxy -Version: 0.1.7 +Version: 0.1.8 Release: 0 Summary: Filtering proxy for D-Bus connections License: LGPL-2.1-or-later @@ -49,6 +49,7 @@ %build %meson \ + -Dinstalled_tests=true \ %{nil} %meson_build @@ -64,3 +65,18 @@ %{_bindir}/xdg-dbus-proxy %{_mandir}/man1/xdg-dbus-proxy.1%{ext_man} +%package tests +Summary: Installed tests for %{name} +Requires: %{name} = %{version} +Requires: dbus-1 +Requires: gnome-desktop-testing + +%description tests +Installed tests for xdg-dbus-proxy, compatible with gnome-desktop-testing-runner. + +%files tests +%dir %{_libexecdir}/installed-tests +%{_libexecdir}/installed-tests/%{name}/ +%dir %{_datadir}/installed-tests +%{_datadir}/installed-tests/%{name}/ + ++++++ _scmsync.obsinfo ++++++ --- /var/tmp/diff_new_pack.rAltVY/_old 2026-08-22 21:34:56.363621457 +0200 +++ /var/tmp/diff_new_pack.rAltVY/_new 2026-08-22 21:34:56.367621599 +0200 @@ -1,6 +1,6 @@ -mtime: 1776410437 -commit: 700fea6c729eaf8f4c6224bd1794cc39755db804dff571768942871bda8c3abd +mtime: 1787214355 +commit: 3ce2c8449f3e5125299f57cce14b06e0b1ff1ab847c7748a3a3c082758f1e11d url: https://src.opensuse.org/GNOME/xdg-dbus-proxy -revision: 700fea6c729eaf8f4c6224bd1794cc39755db804dff571768942871bda8c3abd +revision: 3ce2c8449f3e5125299f57cce14b06e0b1ff1ab847c7748a3a3c082758f1e11d projectscmsync: https://src.opensuse.org/GNOME/_ObsPrj ++++++ build.specials.obscpio ++++++ ++++++ build.specials.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/.gitignore new/.gitignore --- old/.gitignore 1970-01-01 01:00:00.000000000 +0100 +++ new/.gitignore 2026-08-20 10:25:55.000000000 +0200 @@ -0,0 +1,4 @@ +*.obscpio +*.osc +_build.* +.pbuild ++++++ xdg-dbus-proxy-0.1.7.tar.xz -> xdg-dbus-proxy-0.1.8.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xdg-dbus-proxy-0.1.7/NEWS new/xdg-dbus-proxy-0.1.8/NEWS --- old/xdg-dbus-proxy-0.1.7/NEWS 2026-04-07 18:51:52.000000000 +0200 +++ new/xdg-dbus-proxy-0.1.8/NEWS 2026-08-11 14:05:09.000000000 +0200 @@ -1,3 +1,12 @@ +Changes in 0.1.8 +================ + +Released 2026-08-11 + + * Fix broadcast messages bypassing path/interface/member checks + * Improvements to the existing testing infrastructure + * Add tests for owning names, issuing method calls, receiving messages + Changes in 0.1.7 ================ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xdg-dbus-proxy-0.1.7/flatpak-proxy.c new/xdg-dbus-proxy-0.1.8/flatpak-proxy.c --- old/xdg-dbus-proxy-0.1.7/flatpak-proxy.c 2026-04-07 18:51:52.000000000 +0200 +++ new/xdg-dbus-proxy-0.1.8/flatpak-proxy.c 2026-08-11 14:05:09.000000000 +0200 @@ -2808,11 +2808,11 @@ policy = flatpak_proxy_client_get_max_policy_and_matched (client, header->sender, &filters); if (policy == FLATPAK_POLICY_OWN || - policy == FLATPAK_POLICY_TALK || - any_filter_matches (filters, FILTER_TYPE_BROADCAST, - header->path, - header->interface, - header->member)) + (policy == FLATPAK_POLICY_TALK && + any_filter_matches (filters, FILTER_TYPE_BROADCAST, + header->path, + header->interface, + header->member))) filtered = FALSE; if (filtered) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xdg-dbus-proxy-0.1.7/meson.build new/xdg-dbus-proxy-0.1.8/meson.build --- old/xdg-dbus-proxy-0.1.7/meson.build 2026-04-07 18:51:52.000000000 +0200 +++ new/xdg-dbus-proxy-0.1.8/meson.build 2026-08-11 14:05:09.000000000 +0200 @@ -1,7 +1,7 @@ project( 'xdg-dbus-proxy', 'c', - version : '0.1.7', + version : '0.1.8', meson_version : '>=0.49.0', default_options : [ 'warning_level=2', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/xdg-dbus-proxy-0.1.7/tests/test-proxy.c new/xdg-dbus-proxy-0.1.8/tests/test-proxy.c --- old/xdg-dbus-proxy-0.1.7/tests/test-proxy.c 2026-04-07 18:51:52.000000000 +0200 +++ new/xdg-dbus-proxy-0.1.8/tests/test-proxy.c 2026-08-11 14:05:09.000000000 +0200 @@ -33,11 +33,93 @@ #define DBUS_SERVICE_DBUS "org.freedesktop.DBus" #define DBUS_PATH_DBUS "/org/freedesktop/DBus" #define DBUS_INTERFACE_DBUS "org.freedesktop.DBus" +#define DBUS_INTERFACE_PEER "org.freedesktop.DBus.Peer" + +#define DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER 1 +#define DBUS_REQUEST_NAME_REPLY_IN_QUEUE 2 +#define DBUS_REQUEST_NAME_REPLY_EXISTS 3 +#define DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER 4 + +#define CANNOT_ACCESS_NAME "com.example.CannotAccess" +#define CAN_SEE_NAME "com.example.CanSee" +#define CAN_TALK_NAME "com.example.CanTalk" +#define CAN_OWN_NAME "com.example.CanOwn" +#define CAN_CALL_ANYTHING_NAME "com.example.CanCallAny" +#define CAN_CALL_SOME_NAME "com.example.CanCallSome" +#define CAN_RECEIVE_ANYTHING_NAME "com.example.CanReceiveAny" +#define CAN_RECEIVE_SOME_NAME "com.example.CanReceiveSome" + +#define EXAMPLE_IFACE "net.example.AnyInterface" +#define EXAMPLE_METHOD "Echo" +#define EXAMPLE_SIGNAL "Shouted" +#define EXAMPLE_PATH "/path" +#define CAN_CALL_SOME_IFACE "org.example.CanCallThis" +#define CAN_CALL_SOME_METHOD "OnlyThisMethod" +#define CAN_CALL_SOME_PATH "/only/this/path" +#define CAN_RECEIVE_SOME_IFACE "org.example.CanReceiveThis" +#define CAN_RECEIVE_SOME_SIGNAL "JustThisSignal" +#define CAN_RECEIVE_SOME_PATH "/just/this/path" + +static void +ready_cb (GObject *source_object, + GAsyncResult *result, + void *user_data) +{ + GAsyncResult **result_p = user_data; + + g_assert_nonnull (result_p); + g_assert_null (*result_p); + *result_p = g_object_ref (result); +} + +typedef struct +{ + GDBusConnection *conn; + const char *label; + const char *unique_name; + guint filter; + int n_method_calls; /* atomic */ + int n_unicast_signals; /* atomic */ + int n_broadcasts; /* atomic */ +} Connection; + +static void +connection_clear (Connection *self) +{ + if (self->conn != NULL) + { + g_autoptr(GError) error = NULL; + + if (self->filter != 0) + { + g_dbus_connection_remove_filter (self->conn, self->filter); + self->filter = 0; + } + + g_dbus_connection_close_sync (self->conn, NULL, &error); + + if (error != NULL) + g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED); + } + + self->unique_name = NULL; + g_clear_object (&self->conn); +} typedef struct { - GDBusConnection *proxied_conn; + Connection proxied; + Connection cannot_access_conn; + Connection can_see_conn; + Connection can_talk_conn; + Connection can_own_conn; + Connection can_call_anything_conn; + Connection can_call_some_conn; + Connection can_receive_anything_conn; + Connection can_receive_some_conn; + GHashTable *connections_by_name; GSubprocess *dbus_daemon; + GSubprocess *monitor; GSubprocess *proxy; gchar *dbus_address; gchar *temp_directory; @@ -52,6 +134,140 @@ int dummy; } Config; +static GDBusMessage * +conn_filter_cb (GDBusConnection *conn, + GDBusMessage *message, + gboolean incoming, + void *user_data) +{ + Connection *conn_info = user_data; + + g_assert (conn == conn_info->conn); + + if (incoming) + { + GDBusMessageType type = g_dbus_message_get_message_type (message); + const char *sender = g_dbus_message_get_sender (message); + const char *dest = g_dbus_message_get_destination (message); + const char *path = g_dbus_message_get_path (message); + const char *iface = g_dbus_message_get_interface (message); + const char *member = g_dbus_message_get_member (message); + + g_test_message ("%s got message type %u, from=%s, to=%s, path=%s, %s.%s", + conn_info->label, + type, + sender ?: "(message bus)", + dest ?: "(broadcast)", + path ?: "(none)", + iface ?: "(none)", + member ?: "(none)"); + + switch (type) + { + case G_DBUS_MESSAGE_TYPE_METHOD_CALL: + g_test_message ("%s got method call", conn_info->label); + g_atomic_int_inc (&conn_info->n_method_calls); + break; + + case G_DBUS_MESSAGE_TYPE_SIGNAL: + if (sender == NULL) + { + g_test_message ("%s got signal from message bus, ignoring", conn_info->label); + } + else if (dest != NULL && dest[0] != '\0') + { + g_test_message ("%s got unicast signal", conn_info->label); + g_atomic_int_inc (&conn_info->n_unicast_signals); + } + else + { + g_test_message ("%s got broadcast signal", conn_info->label); + g_atomic_int_inc (&conn_info->n_broadcasts); + } + + break; + + case G_DBUS_MESSAGE_TYPE_METHOD_RETURN: + g_test_message ("%s got method reply", conn_info->label); + break; + + case G_DBUS_MESSAGE_TYPE_ERROR: + g_test_message ("%s got error reply", conn_info->label); + break; + + case G_DBUS_MESSAGE_TYPE_INVALID: + default: + g_assert_not_reached (); + } + } + + return message; +} + +/* + * Open a direct connection to the bus + */ +static void +fixture_connect (Fixture *f, + Connection *conn, + const char *name) +{ + g_autoptr(GError) error = NULL; + + g_return_if_fail (conn != NULL); + g_return_if_fail (conn->conn == NULL); + + if (name != NULL) + conn->label = name; + else + conn->label = "(unnamed connection)"; + + conn->conn = g_dbus_connection_new_for_address_sync (f->dbus_address, + (G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT + | G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION), + NULL, /* observer */ + NULL, /* cancellable */ + &error); + g_assert_no_error (error); + g_assert_nonnull (conn->conn); + conn->unique_name = g_dbus_connection_get_unique_name (conn->conn); + + if (name != NULL) + { + g_autoptr(GVariant) tuple = NULL; + guint32 result = 0; + + tuple = g_dbus_connection_call_sync (conn->conn, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "RequestName", + g_variant_new ("(su)", + name, + (G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT + | G_BUS_NAME_OWNER_FLAGS_REPLACE + | G_BUS_NAME_OWNER_FLAGS_DO_NOT_QUEUE)), + G_VARIANT_TYPE ("(u)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, /* cancellable */ + &error); + g_assert_no_error (error); + g_assert_nonnull (tuple); + g_variant_get (tuple, "(u)", &result); + g_assert_cmpuint (result, ==, DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER); + + g_hash_table_replace (f->connections_by_name, + g_strdup (name), + conn); + } + + conn->filter = g_dbus_connection_add_filter (conn->conn, + conn_filter_cb, + conn, + NULL); +} + static void setup (Fixture *f, gconstpointer context G_GNUC_UNUSED) @@ -102,6 +318,18 @@ *newline = '\0'; f->dbus_address = g_strdup (address_buffer); + if (g_getenv ("TEST_DBUS_MONITOR") != NULL) + { + g_autoptr(GSubprocessLauncher) monitor_launcher = NULL; + + monitor_launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_NONE); + g_subprocess_launcher_take_stdout_fd (monitor_launcher, dup (STDERR_FILENO)); + f->monitor = g_subprocess_launcher_spawn (monitor_launcher, NULL, + "dbus-monitor", + "--address", f->dbus_address, + NULL); + } + f->proxy_path = g_getenv ("DBUS_PROXY"); if (f->proxy_path == NULL) @@ -112,6 +340,20 @@ f->proxy_socket = g_build_filename (f->temp_directory, "proxy", NULL); escaped = g_dbus_address_escape_value (f->proxy_socket); f->proxy_address = g_strdup_printf ("unix:path=%s", escaped); + + f->connections_by_name = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + NULL); + + fixture_connect (f, &f->cannot_access_conn, CANNOT_ACCESS_NAME); + fixture_connect (f, &f->can_see_conn, CAN_SEE_NAME); + fixture_connect (f, &f->can_talk_conn, CAN_TALK_NAME); + fixture_connect (f, &f->can_own_conn, CAN_OWN_NAME); + fixture_connect (f, &f->can_call_anything_conn, CAN_CALL_ANYTHING_NAME); + fixture_connect (f, &f->can_call_some_conn, CAN_CALL_SOME_NAME); + fixture_connect (f, &f->can_receive_anything_conn, CAN_RECEIVE_ANYTHING_NAME); + fixture_connect (f, &f->can_receive_some_conn, CAN_RECEIVE_SOME_NAME); } enum @@ -122,27 +364,25 @@ }; static void -test_basics (Fixture *f, - gconstpointer context G_GNUC_UNUSED) +fixture_start_proxy (Fixture *f) { g_autoptr(GSubprocessLauncher) launcher = NULL; g_autoptr(GError) error = NULL; g_autoptr(GVariant) tuple = NULL; - g_auto(GStrv) strv = NULL; - const char *proxied_name; int sync_pipe[PIPE_FDS]; char buf; ssize_t bytes_read; - gsize i; - gboolean found; - - alarm (30); +#if GLIB_CHECK_VERSION (2, 78, 0) + g_unix_open_pipe (sync_pipe, O_CLOEXEC, &error); +#else g_unix_open_pipe (sync_pipe, FD_CLOEXEC, &error); +#endif g_assert_no_error (error); f->sync_pipe = sync_pipe[READ_END]; - launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE); + launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_NONE); + g_subprocess_launcher_take_fd (launcher, dup (STDERR_FILENO), STDOUT_FILENO); g_subprocess_launcher_take_fd (launcher, sync_pipe[WRITE_END], 3); sync_pipe[WRITE_END] = -1; @@ -151,6 +391,15 @@ "--fd=3", f->dbus_address, f->proxy_socket, + "--filter", + "--log", + "--see=" CAN_SEE_NAME, + "--talk=" CAN_TALK_NAME, + "--own=" CAN_OWN_NAME, + "--call=" CAN_CALL_ANYTHING_NAME "=*", + "--call=" CAN_CALL_SOME_NAME "=" CAN_CALL_SOME_IFACE "." CAN_CALL_SOME_METHOD "@" CAN_CALL_SOME_PATH, + "--broadcast=" CAN_RECEIVE_ANYTHING_NAME "=*", + "--broadcast=" CAN_RECEIVE_SOME_NAME "=" CAN_RECEIVE_SOME_IFACE "." CAN_RECEIVE_SOME_SIGNAL "@" CAN_RECEIVE_SOME_PATH, NULL); g_assert_no_error (error); g_assert_nonnull (f->proxy); @@ -159,15 +408,49 @@ bytes_read = read (sync_pipe[READ_END], &buf, 1); g_assert_cmpint (bytes_read, ==, 1); - f->proxied_conn = g_dbus_connection_new_for_address_sync (f->proxy_address, - G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT - | G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION, - NULL, NULL, &error); + f->proxied.label = "Sandboxed connection"; + f->proxied.conn = g_dbus_connection_new_for_address_sync (f->proxy_address, + (G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT + | G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION), + NULL, /* observer */ + NULL, /* cancellable */ + &error); + g_assert_no_error (error); + g_assert_nonnull (f->proxied.conn); + f->proxied.unique_name = g_dbus_connection_get_unique_name (f->proxied.conn); + tuple = g_dbus_connection_call_sync (f->proxied.conn, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "AddMatch", + g_variant_new ("(s)", ""), + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, /* cancellable */ + &error); g_assert_no_error (error); - g_assert_nonnull (f->proxied_conn); - proxied_name = g_dbus_connection_get_unique_name (f->proxied_conn); + f->proxied.filter = g_dbus_connection_add_filter (f->proxied.conn, + conn_filter_cb, + &f->proxied, + NULL); +} - tuple = g_dbus_connection_call_sync (f->proxied_conn, DBUS_SERVICE_DBUS, +static void +test_basics (Fixture *f, + gconstpointer context G_GNUC_UNUSED) +{ + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) tuple = NULL; + g_auto(GStrv) strv = NULL; + gsize i; + gboolean found; + + alarm (30); + + fixture_start_proxy (f); + + tuple = g_dbus_connection_call_sync (f->proxied.conn, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "ListNames", NULL, G_VARIANT_TYPE ("(as)"), @@ -184,19 +467,376 @@ { g_test_message ("ListNames(): %s", strv[i]); - if (g_strcmp0 (strv[i], proxied_name) == 0) + if (g_strcmp0 (strv[i], f->proxied.unique_name) == 0) found = TRUE; } g_assert_true (found); } +typedef struct +{ + const char *name; + const char *path; + const char *iface; + const char *method; + gboolean can_call; + gboolean can_see; +} CallTest; + +static const CallTest call_tests[] = +{ + { CANNOT_ACCESS_NAME, EXAMPLE_PATH, EXAMPLE_IFACE, EXAMPLE_METHOD, + .can_call = FALSE, .can_see = FALSE }, + { CAN_SEE_NAME, EXAMPLE_PATH, EXAMPLE_IFACE, EXAMPLE_METHOD, + .can_call = FALSE, .can_see = TRUE }, + { CAN_TALK_NAME, EXAMPLE_PATH, EXAMPLE_IFACE, EXAMPLE_METHOD, + .can_call = TRUE, .can_see = TRUE }, + { CAN_OWN_NAME, EXAMPLE_PATH, EXAMPLE_IFACE, EXAMPLE_METHOD, + .can_call = TRUE, .can_see = TRUE }, + { CAN_RECEIVE_ANYTHING_NAME, EXAMPLE_PATH, EXAMPLE_IFACE, EXAMPLE_METHOD, + .can_call = FALSE, .can_see = TRUE }, + { CAN_RECEIVE_SOME_NAME, EXAMPLE_PATH, EXAMPLE_IFACE, EXAMPLE_METHOD, + .can_call = FALSE, .can_see = TRUE }, + + { CAN_CALL_ANYTHING_NAME, EXAMPLE_PATH, EXAMPLE_IFACE, EXAMPLE_METHOD, + .can_call = TRUE, .can_see = TRUE }, + + { CAN_CALL_SOME_NAME, EXAMPLE_PATH, EXAMPLE_IFACE, EXAMPLE_METHOD, + .can_call = FALSE, .can_see = TRUE }, + { CAN_CALL_SOME_NAME, CAN_CALL_SOME_PATH, CAN_CALL_SOME_IFACE, CAN_CALL_SOME_METHOD, + .can_call = TRUE, .can_see = TRUE }, + { CAN_CALL_SOME_NAME, EXAMPLE_PATH, CAN_CALL_SOME_IFACE, CAN_CALL_SOME_METHOD, + .can_call = FALSE, .can_see = TRUE }, + { CAN_CALL_SOME_NAME, CAN_CALL_SOME_PATH, EXAMPLE_IFACE, CAN_CALL_SOME_METHOD, + .can_call = FALSE, .can_see = TRUE }, + { CAN_CALL_SOME_NAME, CAN_CALL_SOME_PATH, CAN_CALL_SOME_IFACE, EXAMPLE_METHOD, + .can_call = FALSE, .can_see = TRUE }, +}; + +static void +test_call (Fixture *f, + gconstpointer context G_GNUC_UNUSED) +{ + alarm (30); + fixture_start_proxy (f); + + for (size_t i = 0; i < G_N_ELEMENTS (call_tests); i++) + { + const CallTest *t = &call_tests[i]; + g_autoptr(GAsyncResult) result = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) tuple = NULL; + const Connection *dest; + int n_calls_before; + + g_test_message ("#%zu: sandboxed connection %s be allowed to call %s:%s.%s on %s", + i, + t->can_call ? "should" : "should not", + t->path, + t->iface, + t->method, + t->name); + + dest = g_hash_table_lookup (f->connections_by_name, t->name); + g_assert_nonnull (dest); + n_calls_before = g_atomic_int_get (&dest->n_method_calls); + + g_dbus_connection_call (f->proxied.conn, + t->name, + t->path, + t->iface, + t->method, + NULL, + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, /* cancellable */ + ready_cb, + &result); + + while (result == NULL) + g_main_context_iteration (NULL, TRUE); + + tuple = g_dbus_connection_call_finish (f->proxied.conn, result, &error); + g_assert_nonnull (error); + g_assert_null (tuple); + g_test_message ("-> %s", error->message); + + if (t->can_call) + { + /* If the method call was allowed, we just return an error, + * because for simplicity we didn't actually implement any + * method calls. */ + g_assert_cmpstr (g_quark_to_string (error->domain), + ==, g_quark_to_string (G_DBUS_ERROR)); + + switch (error->code) + { + case G_DBUS_ERROR_UNKNOWN_METHOD: + case G_DBUS_ERROR_UNKNOWN_INTERFACE: + case G_DBUS_ERROR_UNKNOWN_OBJECT: + /* OK */ + break; + + default: + g_assert_not_reached (); + } + + g_assert_cmpint (g_atomic_int_get (&dest->n_method_calls), ==, n_calls_before + 1); + } + else if (t->can_see) + { + g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED); + g_assert_cmpint (g_atomic_int_get (&dest->n_method_calls), ==, n_calls_before); + } + else + { + g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN); + g_assert_cmpint (g_atomic_int_get (&dest->n_method_calls), ==, n_calls_before); + } + } +} + +typedef struct +{ + const char *name; + gboolean can_own; + gboolean can_see; +} OwnTest; + +static const OwnTest own_tests[] = +{ + { CANNOT_ACCESS_NAME, .can_own = FALSE, .can_see = FALSE }, + { CAN_SEE_NAME, .can_own = FALSE, .can_see = TRUE }, + { CAN_TALK_NAME, .can_own = FALSE, .can_see = TRUE }, + { CAN_OWN_NAME, .can_own = TRUE, .can_see = TRUE }, + { CAN_CALL_ANYTHING_NAME, .can_own = FALSE, .can_see = TRUE }, + { CAN_CALL_SOME_NAME, .can_own = FALSE, .can_see = TRUE }, + { CAN_RECEIVE_ANYTHING_NAME, .can_own = FALSE, .can_see = TRUE }, + { CAN_RECEIVE_SOME_NAME, .can_own = FALSE, .can_see = TRUE }, +}; + +static void +test_own (Fixture *f, + gconstpointer context G_GNUC_UNUSED) +{ + alarm (30); + fixture_start_proxy (f); + + for (size_t i = 0; i < G_N_ELEMENTS (own_tests); i++) + { + const OwnTest *t = &own_tests[i]; + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) tuple = NULL; + const char *owner = NULL; + + g_test_message ("#%zu: sandboxed connection %s be allowed to own %s", + i, t->can_own ? "should" : "should not", t->name); + + tuple = g_dbus_connection_call_sync (f->proxied.conn, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "RequestName", + g_variant_new ("(su)", + t->name, + (G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT + | G_BUS_NAME_OWNER_FLAGS_REPLACE + | G_BUS_NAME_OWNER_FLAGS_DO_NOT_QUEUE)), + G_VARIANT_TYPE ("(u)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, /* cancellable */ + &error); + + if (tuple != NULL) + g_test_message ("-> Was allowed"); + else + g_test_message ("-> Was not allowed: %s", error->message); + + if (t->can_own) + { + guint32 result = 0; + + g_assert_no_error (error); + g_assert_nonnull (tuple); + g_variant_get (tuple, "(u)", &result); + g_assert_cmpuint (result, ==, DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER); + } + else if (t->can_see) + { + g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED); + } + else + { + g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN); + } + + g_clear_error (&error); + g_clear_pointer (&tuple, g_variant_unref); + + /* Use a different connection to check who actually owns the name */ + tuple = g_dbus_connection_call_sync (f->cannot_access_conn.conn, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "GetNameOwner", + g_variant_new ("(s)", t->name), + G_VARIANT_TYPE ("(s)"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, /* cancellable */ + NULL); + + if (tuple != NULL) + g_variant_get (tuple, "(&s)", &owner); + else + owner = ""; + + if (t->can_own) + g_assert_cmpstr (owner, ==, f->proxied.unique_name); + else + g_assert_cmpstr (owner, !=, f->proxied.unique_name); + } +} + +typedef struct +{ + const char *name; + const char *path; + const char *iface; + const char *member; + gboolean can_receive_broadcast; +} ReceiveTest; + +static const ReceiveTest receive_tests[] = +{ + { CANNOT_ACCESS_NAME, EXAMPLE_PATH, EXAMPLE_IFACE, EXAMPLE_SIGNAL, FALSE }, + { CAN_SEE_NAME, EXAMPLE_PATH, EXAMPLE_IFACE, EXAMPLE_SIGNAL, FALSE }, + { CAN_TALK_NAME, EXAMPLE_PATH, EXAMPLE_IFACE, EXAMPLE_SIGNAL, TRUE }, + { CAN_OWN_NAME, EXAMPLE_PATH, EXAMPLE_IFACE, EXAMPLE_SIGNAL, TRUE }, + + /* Before GHSA-r7hp-698j-2h6c was fixed, both of these would receive the + * broadcast, but that was unintended */ + { CAN_CALL_ANYTHING_NAME, EXAMPLE_PATH, EXAMPLE_IFACE, EXAMPLE_SIGNAL, FALSE }, + { CAN_CALL_SOME_NAME, EXAMPLE_PATH, EXAMPLE_IFACE, EXAMPLE_SIGNAL, FALSE }, + + { CAN_RECEIVE_ANYTHING_NAME, EXAMPLE_PATH, EXAMPLE_IFACE, EXAMPLE_SIGNAL, TRUE }, + + { CAN_RECEIVE_SOME_NAME, CAN_RECEIVE_SOME_PATH, CAN_RECEIVE_SOME_IFACE, CAN_RECEIVE_SOME_SIGNAL, TRUE }, + + /* Before GHSA-r7hp-698j-2h6c was fixed, all of these would receive the broadcast, + * but that was unintended */ + { CAN_RECEIVE_SOME_NAME, EXAMPLE_PATH, CAN_RECEIVE_SOME_IFACE, CAN_RECEIVE_SOME_SIGNAL, FALSE }, + { CAN_RECEIVE_SOME_NAME, CAN_RECEIVE_SOME_PATH, EXAMPLE_IFACE, CAN_RECEIVE_SOME_SIGNAL, FALSE }, + { CAN_RECEIVE_SOME_NAME, CAN_RECEIVE_SOME_PATH, CAN_RECEIVE_SOME_IFACE, EXAMPLE_SIGNAL, FALSE }, + { CAN_RECEIVE_SOME_NAME, EXAMPLE_PATH, EXAMPLE_IFACE, EXAMPLE_SIGNAL, FALSE }, +}; + +static void +test_receive (Fixture *f, + gconstpointer context G_GNUC_UNUSED) +{ + alarm (30); + fixture_start_proxy (f); + + for (size_t i = 0; i < G_N_ELEMENTS (receive_tests); i++) + { + g_autoptr(GAsyncResult) result = NULL; + g_autoptr(GVariant) tuple = NULL; + g_autoptr(GError) error = NULL; + const Connection *sender; + const ReceiveTest *t = &receive_tests[i]; + int n_unicasts_before; + int n_broadcasts_before; + int n_calls_before; + + g_test_message ("#%zu: sandboxed connection %s be allowed to receive broadcast from %s:%s.%s on %s", + i, + t->can_receive_broadcast ? "should" : "should not", + t->path, + t->iface, + t->member, + t->name); + + sender = g_hash_table_lookup (f->connections_by_name, t->name); + g_assert_nonnull (sender); + + n_calls_before = g_atomic_int_get (&f->proxied.n_method_calls); + n_unicasts_before = g_atomic_int_get (&f->proxied.n_unicast_signals); + n_broadcasts_before = g_atomic_int_get (&f->proxied.n_broadcasts); + + /* The sandboxed recipient is only sometimes allowed to receive + * broadcasts. We do this first, because D-Bus preserves message + * order, therefore by the time we have received the unicast signal + * and/or the method call, it's guaranteed that this broadcast + * has been processed (and received, or not, as appropriate). */ + g_dbus_connection_emit_signal (sender->conn, + NULL, + t->path, + t->iface, + t->member, + NULL, + &error); + g_assert_no_error (error); + + /* The sandboxed recipient is always allowed to receive + * unicast signals. */ + g_dbus_connection_emit_signal (sender->conn, + f->proxied.unique_name, + t->path, + t->iface, + t->member, + NULL, + &error); + g_assert_no_error (error); + + /* The sandboxed recipient is always allowed to receive + * method calls. */ + g_dbus_connection_call (sender->conn, + f->proxied.unique_name, + "/", + DBUS_INTERFACE_PEER, + "Ping", + NULL, + G_VARIANT_TYPE ("()"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, /* cancellable */ + ready_cb, + &result); + + while (result == NULL) + g_main_context_iteration (NULL, TRUE); + + tuple = g_dbus_connection_call_finish (sender->conn, result, &error); + g_assert_no_error (error); + g_assert_nonnull (tuple); + + g_assert_cmpint (g_atomic_int_get (&f->proxied.n_method_calls), ==, n_calls_before + 1); + g_assert_cmpint (g_atomic_int_get (&f->proxied.n_unicast_signals), ==, n_unicasts_before + 1); + + if (t->can_receive_broadcast) + g_assert_cmpint (g_atomic_int_get (&f->proxied.n_broadcasts), ==, n_broadcasts_before + 1); + else + g_assert_cmpint (g_atomic_int_get (&f->proxied.n_broadcasts), ==, n_broadcasts_before); + } +} + static void teardown (Fixture *f, gconstpointer context G_GNUC_UNUSED) { g_autoptr(GError) error = NULL; + if (f->monitor != NULL) + { + g_subprocess_send_signal (f->monitor, SIGTERM); + g_subprocess_wait (f->monitor, NULL, &error); + g_assert_no_error (error); + } + if (f->dbus_daemon != NULL) { g_subprocess_send_signal (f->dbus_daemon, SIGTERM); @@ -218,16 +858,7 @@ g_assert_no_error (error); } - if (f->proxied_conn != NULL) - { - g_dbus_connection_close_sync (f->proxied_conn, NULL, &error); - - if (error != NULL) - { - g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED); - g_clear_error (&error); - } - } + connection_clear (&f->proxied); if (f->proxy_socket != NULL) { @@ -245,7 +876,8 @@ g_free (f->temp_directory); } - g_clear_object (&f->proxied_conn); + g_clear_pointer (&f->connections_by_name, g_hash_table_unref); + g_clear_object (&f->monitor); g_clear_object (&f->dbus_daemon); g_clear_object (&f->proxy); g_free (f->dbus_address); @@ -260,6 +892,9 @@ g_test_init (&argc, &argv, NULL); g_test_add ("/basics", Fixture, NULL, setup, test_basics, teardown); + g_test_add ("/call", Fixture, NULL, setup, test_call, teardown); + g_test_add ("/own", Fixture, NULL, setup, test_own, teardown); + g_test_add ("/receive", Fixture, NULL, setup, test_receive, teardown); return g_test_run (); }
