summaryrefslogtreecommitdiff
path: root/bus/dispatch.c
diff options
context:
space:
mode:
Diffstat (limited to 'bus/dispatch.c')
-rw-r--r--bus/dispatch.c187
1 files changed, 170 insertions, 17 deletions
diff --git a/bus/dispatch.c b/bus/dispatch.c
index 556715d5..bb81cc09 100644
--- a/bus/dispatch.c
+++ b/bus/dispatch.c
@@ -81,7 +81,7 @@ send_one_message (DBusConnection *connection,
return TRUE;
}
-dbus_bool_t
+BusResult
bus_dispatch_matches (BusTransaction *transaction,
DBusConnection *sender,
DBusConnection *addressed_recipient,
@@ -121,10 +121,28 @@ bus_dispatch_matches (BusTransaction *transaction,
case BUS_RESULT_FALSE:
return BUS_RESULT_FALSE;
case BUS_RESULT_LATER:
- dbus_set_error (error,
- DBUS_ERROR_ACCESS_DENIED,
- "Rejecting message because time is needed to check security policy");
- return BUS_RESULT_FALSE;
+ {
+ BusDeferredMessageStatus status;
+ status = bus_deferred_message_get_status(deferred_message);
+
+ if (status & BUS_DEFERRED_MESSAGE_CHECK_SEND)
+ {
+ /* send rule result not available - disable dispatching messages from the sender */
+ bus_deferred_message_disable_sender(deferred_message);
+ return BUS_RESULT_LATER;
+ }
+ else if (status & BUS_DEFERRED_MESSAGE_CHECK_RECEIVE)
+ {
+ dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
+ "Rejecting message because time is needed to check security policy");
+ return BUS_RESULT_FALSE;
+ }
+ else
+ {
+ _dbus_verbose("deferred message has no status field set to send or receive unexpectedly\n");
+ return BUS_RESULT_FALSE;
+ }
+ }
}
if (dbus_message_contains_unix_fds (message) &&
@@ -135,14 +153,14 @@ bus_dispatch_matches (BusTransaction *transaction,
DBUS_ERROR_NOT_SUPPORTED,
"Tried to send message with Unix file descriptors"
"to a client that doesn't support that.");
- return FALSE;
- }
+ return BUS_RESULT_FALSE;
+ }
/* Dispatch the message */
if (!bus_transaction_send (transaction, addressed_recipient, message))
{
BUS_SET_OOM (error);
- return FALSE;
+ return BUS_RESULT_FALSE;
}
}
@@ -157,7 +175,7 @@ bus_dispatch_matches (BusTransaction *transaction,
&recipients))
{
BUS_SET_OOM (error);
- return FALSE;
+ return BUS_RESULT_FALSE;
}
link = _dbus_list_get_first_link (&recipients);
@@ -179,10 +197,10 @@ bus_dispatch_matches (BusTransaction *transaction,
if (dbus_error_is_set (&tmp_error))
{
dbus_move_error (&tmp_error, error);
- return FALSE;
+ return BUS_RESULT_FALSE;
}
else
- return TRUE;
+ return BUS_RESULT_TRUE;
}
static DBusHandlerResult
@@ -298,10 +316,12 @@ bus_dispatch (DBusConnection *connection,
_dbus_verbose ("Security policy rejected message\n");
goto out;
case BUS_RESULT_LATER:
- dbus_set_error (&error,
- DBUS_ERROR_ACCESS_DENIED,
- "Rejecting message because time is needed to check security policy");
- _dbus_verbose ("Security policy needs time to check policy. Dropping message\n");
+ /* Disable dispatching messages from the sender,
+ * roll back and dispatch the message once the policy result is available */
+ bus_deferred_message_disable_sender(deferred_message);
+ bus_transaction_cancel_and_free (transaction);
+ transaction = NULL;
+ result = DBUS_HANDLER_RESULT_LATER;
goto out;
}
@@ -366,8 +386,18 @@ bus_dispatch (DBusConnection *connection,
* addressed_recipient == NULL), and match it against other connections'
* match rules.
*/
- if (!bus_dispatch_matches (transaction, connection, addressed_recipient, message, &error))
- goto out;
+ switch (bus_dispatch_matches (transaction, connection, addressed_recipient, message, &error))
+ {
+ case BUS_RESULT_TRUE:
+ case BUS_RESULT_FALSE:
+ break;
+ case BUS_RESULT_LATER:
+ /* Roll back and dispatch the message once the policy result is available */
+ bus_transaction_cancel_and_free (transaction);
+ transaction = NULL;
+ result = DBUS_HANDLER_RESULT_LATER;
+ break;
+ }
out:
if (dbus_error_is_set (&error))
@@ -4714,9 +4744,132 @@ bus_dispatch_test_conf_fail (const DBusString *test_data_dir,
return TRUE;
}
+typedef struct {
+ DBusTimeout *timeout;
+ DBusConnection *connection;
+ dbus_bool_t timedout;
+ int check_counter;
+} BusTestCheckData;
+
+static BusTestCheckData *cdata;
+
+static dbus_bool_t
+bus_dispatch_test_check_timeout (void *data)
+{
+ _dbus_verbose ("timeout triggered - pretend that privilege check result is available\n");
+
+ /* should only happen once during the test */
+ _dbus_assert (!cdata->timedout);
+ cdata->timedout = TRUE;
+ _dbus_connection_enable_dispatch (cdata->connection);
+
+ /* don't call this again */
+ _dbus_loop_remove_timeout (bus_connection_get_loop (cdata->connection),
+ cdata->timeout);
+ dbus_connection_unref (cdata->connection);
+ cdata->connection = NULL;
+ return TRUE;
+}
+
+static dbus_bool_t
+bus_dispatch_test_check_override (DBusConnection *connection,
+ const char *privilege)
+{
+ _dbus_verbose ("overriding privilege check %s #%d\n", privilege, cdata->check_counter);
+ cdata->check_counter++;
+ if (!cdata->timedout)
+ {
+ dbus_bool_t added;
+
+ /* Should be the first privilege check for the "Echo" method. */
+ _dbus_assert (cdata->check_counter == 1);
+ cdata->timeout = _dbus_timeout_new (1, bus_dispatch_test_check_timeout,
+ NULL, NULL);
+ _dbus_assert (cdata->timeout);
+ added = _dbus_loop_add_timeout (bus_connection_get_loop (connection),
+ cdata->timeout);
+ _dbus_assert (added);
+ cdata->connection = connection;
+ dbus_connection_ref (connection);
+ _dbus_connection_disable_dispatch (connection);
+ return BUS_RESULT_LATER;
+ }
+ else
+ {
+ /* Should only be checked one more time, and this time succeeds. */
+ _dbus_assert (cdata->check_counter == 2);
+ return BUS_RESULT_TRUE;
+ }
+}
+
+static dbus_bool_t
+bus_dispatch_test_check (const DBusString *test_data_dir)
+{
+ const char *filename = "valid-config-files/debug-check-some.conf";
+ BusContext *context;
+ DBusConnection *foo;
+ DBusError error;
+ dbus_bool_t result = TRUE;
+ BusTestCheckData data;
+
+ /* save the config name for the activation helper */
+ if (!setenv_TEST_LAUNCH_HELPER_CONFIG (test_data_dir, filename))
+ _dbus_assert_not_reached ("no memory setting TEST_LAUNCH_HELPER_CONFIG");
+
+ dbus_error_init (&error);
+
+ context = bus_context_new_test (test_data_dir, filename);
+ if (context == NULL)
+ return FALSE;
+
+ foo = dbus_connection_open_private (TEST_DEBUG_PIPE, &error);
+ if (foo == NULL)
+ _dbus_assert_not_reached ("could not alloc connection");
+
+ if (!bus_setup_debug_client (foo))
+ _dbus_assert_not_reached ("could not set up connection");
+
+ spin_connection_until_authenticated (context, foo);
+
+ if (!check_hello_message (context, foo))
+ _dbus_assert_not_reached ("hello message failed");
+
+ if (!check_double_hello_message (context, foo))
+ _dbus_assert_not_reached ("double hello message failed");
+
+ if (!check_add_match_all (context, foo))
+ _dbus_assert_not_reached ("AddMatch message failed");
+
+ /*
+ * Cause bus_check_send_privilege() to return BUS_RESULT_LATER in the
+ * first call, then BUS_RESULT_TRUE.
+ */
+ cdata = &data;
+ memset (cdata, 0, sizeof(*cdata));
+ bus_check_test_override = bus_dispatch_test_check_override;
+
+ result = check_existent_service_auto_start (context, foo);
+
+ _dbus_assert (cdata->check_counter == 2);
+ _dbus_assert (cdata->timedout);
+ _dbus_assert (cdata->timeout);
+ _dbus_assert (!cdata->connection);
+ _dbus_timeout_unref (cdata->timeout);
+
+ kill_client_connection_unchecked (foo);
+
+ bus_context_unref (context);
+
+ return result;
+}
+
dbus_bool_t
bus_dispatch_test (const DBusString *test_data_dir)
{
+ _dbus_verbose ("<check> tests\n");
+ if (!bus_dispatch_test_check (test_data_dir))
+ return FALSE;
+
/* run normal activation tests */
_dbus_verbose ("Normal activation tests\n");
if (!bus_dispatch_test_conf (test_data_dir,