summaryrefslogtreecommitdiffstats
path: root/meta-security/recipes-core/dbus/dbus-cynara/0005-Disable-message-dispatching-when-send-rule-result-is.patch
diff options
context:
space:
mode:
authorJosé Bollo <jose.bollo@iot.bzh>2018-01-24 11:38:43 +0100
committerJosé Bollo <jose.bollo@iot.bzh>2018-02-13 11:02:00 +0100
commitf70d712e4f505f5c5b50ae17f4f023d20a667568 (patch)
tree57b0aaa702651012e1adfc07f9b6b6c580506f66 /meta-security/recipes-core/dbus/dbus-cynara/0005-Disable-message-dispatching-when-send-rule-result-is.patch
parent3f962c7d202055777dd0238f12dbcf70f09ac07d (diff)
Integrate parts of meta-intel-iot-security
Adds the recipes of the sub layers - meta-security-framework - meta-security-smack Change-Id: I618608008a3b3d1d34adb6e38048110f13ac0643 Signed-off-by: José Bollo <jose.bollo@iot.bzh>
Diffstat (limited to 'meta-security/recipes-core/dbus/dbus-cynara/0005-Disable-message-dispatching-when-send-rule-result-is.patch')
-rw-r--r--meta-security/recipes-core/dbus/dbus-cynara/0005-Disable-message-dispatching-when-send-rule-result-is.patch941
1 files changed, 941 insertions, 0 deletions
diff --git a/meta-security/recipes-core/dbus/dbus-cynara/0005-Disable-message-dispatching-when-send-rule-result-is.patch b/meta-security/recipes-core/dbus/dbus-cynara/0005-Disable-message-dispatching-when-send-rule-result-is.patch
new file mode 100644
index 000000000..ca787149e
--- /dev/null
+++ b/meta-security/recipes-core/dbus/dbus-cynara/0005-Disable-message-dispatching-when-send-rule-result-is.patch
@@ -0,0 +1,941 @@
+From b1b87ad9f20b2052c28431b48e81073078a745ce Mon Sep 17 00:00:00 2001
+From: Jacek Bukarewicz <j.bukarewicz@samsung.com>
+Date: Fri, 28 Nov 2014 12:07:39 +0100
+Subject: [PATCH 5/8] Disable message dispatching when send rule result is not
+ known
+
+When unicast message is sent to addressed recipient and policy result
+is not available message dispatch from the sender is disabled.
+This also means that any further messages from the given connection are
+put into the incoming queue without being processed. If response is received
+message dispatching is resumed. This time answer is attached to the message
+which is now processed synchronously.
+Receive rule result unavailability is not yet handled - such messages are
+rejected. Also, if message is sent to non-addressed recipient and policy result
+is unknown, message is silently dropped.
+
+Change-Id: I57eccbf973525fd51369c7d4e58908292f44da80
+---
+ bus/activation.c | 79 +++++++++++++++--
+ bus/check.c | 109 ++++++++++++++++++++++--
+ bus/check.h | 10 +++
+ bus/cynara.c | 1 -
+ bus/dispatch.c | 183 ++++++++++++++++++++++++++++++++++++----
+ bus/dispatch.h | 2 +-
+ bus/driver.c | 13 ++-
+ dbus/dbus-connection-internal.h | 9 ++
+ dbus/dbus-connection.c | 125 +++++++++++++++++++++++++--
+ dbus/dbus-list.c | 29 +++++++
+ dbus/dbus-list.h | 2 +
+ dbus/dbus-shared.h | 3 +-
+ 12 files changed, 522 insertions(+), 43 deletions(-)
+
+diff --git a/bus/activation.c b/bus/activation.c
+index ecd19bb..8c43941 100644
+--- a/bus/activation.c
++++ b/bus/activation.c
+@@ -31,6 +31,7 @@
+ #include "services.h"
+ #include "test.h"
+ #include "utils.h"
++#include <dbus/dbus-connection-internal.h>
+ #include <dbus/dbus-internals.h>
+ #include <dbus/dbus-hash.h>
+ #include <dbus/dbus-list.h>
+@@ -91,6 +92,8 @@ struct BusPendingActivationEntry
+ DBusConnection *connection;
+
+ dbus_bool_t auto_activation;
++
++ dbus_bool_t is_put_back;
+ };
+
+ typedef struct
+@@ -1180,20 +1183,23 @@ bus_activation_send_pending_auto_activation_messages (BusActivation *activation
+ BusPendingActivationEntry *entry = link->data;
+ DBusList *next = _dbus_list_get_next_link (&pending_activation->entries, link);
+
+- if (entry->auto_activation && (entry->connection == NULL || dbus_connection_get_is_connected (entry->connection)))
++ if (entry->auto_activation && !entry->is_put_back &&
++ (entry->connection == NULL || dbus_connection_get_is_connected (entry->connection)))
+ {
+ DBusConnection *addressed_recipient;
+ DBusError error;
++ BusResult res;
+
+ dbus_error_init (&error);
+
+ addressed_recipient = bus_service_get_primary_owners_connection (service);
+
+ /* Resume dispatching where we left off in bus_dispatch() */
+- if (!bus_dispatch_matches (transaction,
+- entry->connection,
+- addressed_recipient,
+- entry->activation_message, &error))
++ res = bus_dispatch_matches (transaction,
++ entry->connection,
++ addressed_recipient,
++ entry->activation_message, &error);
++ if (res == BUS_RESULT_FALSE)
+ {
+ /* If permission is denied, we just want to return the error
+ * to the original method invoker; in particular, we don't
+@@ -1205,9 +1211,40 @@ bus_activation_send_pending_auto_activation_messages (BusActivation *activation
+ bus_connection_send_oom_error (entry->connection,
+ entry->activation_message);
+ }
++ }
++ else if (res == BUS_RESULT_LATER)
++ {
++ DBusList *putback_message_link = link;
++ DBusMessage *last_inserted_message = NULL;
++
++ /* NULL entry->connection implies sending pending ActivationRequest message to systemd */
++ if (entry->connection == NULL)
++ {
++ _dbus_assert_not_reached ("bus_dispatch_matches returned BUS_RESULT_LATER unexpectedly when sender is NULL");
++ link = next;
++ continue;
++ }
+
+- link = next;
+- continue;
++ /**
++ * Getting here means that policy check result is not yet available and dispatching
++ * messages from entry->connection has been disabled.
++ * Let's put back all messages for the given connection in the incoming queue and mark
++ * this entry as put back so they are not handled twice.
++ */
++ while (putback_message_link != NULL)
++ {
++ BusPendingActivationEntry *putback_message = putback_message_link->data;
++ if (putback_message->connection == entry->connection)
++ {
++ if (!_dbus_connection_putback_message (putback_message->connection, last_inserted_message,
++ putback_message->activation_message, &error))
++ goto error;
++ last_inserted_message = putback_message->activation_message;
++ putback_message->is_put_back = TRUE;
++ }
++
++ putback_message_link = _dbus_list_get_next_link(&pending_activation->entries, putback_message_link);
++ }
+ }
+ }
+
+@@ -1225,6 +1262,19 @@ bus_activation_send_pending_auto_activation_messages (BusActivation *activation
+ return TRUE;
+
+ error:
++ /* remove all messages that have been put to connections' incoming queues */
++ link = _dbus_list_get_first_link (&pending_activation->entries);
++ while (link != NULL)
++ {
++ BusPendingActivationEntry *entry = link->data;
++ if (entry->is_put_back)
++ {
++ _dbus_connection_remove_message(entry->connection, entry->activation_message);
++ entry->is_put_back = FALSE;
++ }
++ link = _dbus_list_get_next_link(&pending_activation->entries, link);
++ }
++
+ return FALSE;
+ }
+
+@@ -2009,13 +2059,24 @@ bus_activation_activate_service (BusActivation *activation,
+
+ if (service != NULL)
+ {
++ BusResult res;
+ bus_context_log (activation->context,
+ DBUS_SYSTEM_LOG_INFO, "Activating via systemd: service name='%s' unit='%s'",
+ service_name,
+ entry->systemd_service);
+ /* Wonderful, systemd is connected, let's just send the msg */
+- retval = bus_dispatch_matches (activation_transaction, NULL, bus_service_get_primary_owners_connection (service),
+- message, error);
++ res = bus_dispatch_matches (activation_transaction, NULL, bus_service_get_primary_owners_connection (service),
++ message, error);
++
++ if (res == BUS_RESULT_TRUE)
++ retval = TRUE;
++ else if (res == BUS_RESULT_FALSE)
++ retval = FALSE;
++ else if (res == BUS_RESULT_LATER)
++ {
++ _dbus_verbose("Unexpectedly need time to check message from bus driver to systemd - dropping the message.\n");
++ retval = FALSE;
++ }
+ }
+ else
+ {
+diff --git a/bus/check.c b/bus/check.c
+index d2f418a..cd6a74b 100644
+--- a/bus/check.c
++++ b/bus/check.c
+@@ -55,6 +55,8 @@ typedef struct BusDeferredMessage
+ BusCheckResponseFunc response_callback;
+ } BusDeferredMessage;
+
++static dbus_int32_t deferred_message_data_slot = -1;
++
+ BusCheck *
+ bus_check_new (BusContext *context, DBusError *error)
+ {
+@@ -67,11 +69,19 @@ bus_check_new (BusContext *context, DBusError *error)
+ return NULL;
+ }
+
++ if (!dbus_message_allocate_data_slot(&deferred_message_data_slot))
++ {
++ dbus_free(check);
++ BUS_SET_OOM(error);
++ return NULL;
++ }
++
+ check->refcount = 1;
+ check->context = context;
+ check->cynara = bus_cynara_new(check, error);
+ if (dbus_error_is_set(error))
+ {
++ dbus_message_free_data_slot(&deferred_message_data_slot);
+ dbus_free(check);
+ return NULL;
+ }
+@@ -98,6 +108,7 @@ bus_check_unref (BusCheck *check)
+ if (check->refcount == 0)
+ {
+ bus_cynara_unref(check->cynara);
++ dbus_message_free_data_slot(&deferred_message_data_slot);
+ dbus_free(check);
+ }
+ }
+@@ -114,6 +125,45 @@ bus_check_get_cynara (BusCheck *check)
+ return check->cynara;
+ }
+
++static void
++bus_check_enable_dispatch_callback (BusDeferredMessage *deferred_message,
++ BusResult result)
++{
++ _dbus_verbose("bus_check_enable_dispatch_callback called deferred_message=%p\n", deferred_message);
++
++ deferred_message->response = result;
++ _dbus_connection_enable_dispatch(deferred_message->sender);
++}
++
++static void
++deferred_message_free_function(void *data)
++{
++ BusDeferredMessage *deferred_message = (BusDeferredMessage *)data;
++ bus_deferred_message_unref(deferred_message);
++}
++
++void
++bus_deferred_message_disable_sender (BusDeferredMessage *deferred_message)
++{
++ _dbus_assert(deferred_message != NULL);
++ _dbus_assert(deferred_message->sender != NULL);
++
++ if (dbus_message_get_data(deferred_message->message, deferred_message_data_slot) == NULL)
++ {
++ if (dbus_message_set_data(deferred_message->message, deferred_message_data_slot, deferred_message,
++ deferred_message_free_function))
++ bus_deferred_message_ref(deferred_message);
++ }
++
++ _dbus_connection_disable_dispatch(deferred_message->sender);
++ deferred_message->response_callback = bus_check_enable_dispatch_callback;
++}
++
++#ifdef DBUS_ENABLE_EMBEDDED_TESTS
++dbus_bool_t (*bus_check_test_override) (DBusConnection *connection,
++ const char *privilege);
++#endif
++
+ BusResult
+ bus_check_privilege (BusCheck *check,
+ DBusMessage *message,
+@@ -124,6 +174,7 @@ bus_check_privilege (BusCheck *check,
+ BusDeferredMessageStatus check_type,
+ BusDeferredMessage **deferred_message)
+ {
++ BusDeferredMessage *previous_deferred_message;
+ BusResult result = BUS_RESULT_FALSE;
+ BusCynara *cynara;
+ DBusConnection *connection;
+@@ -135,16 +186,54 @@ bus_check_privilege (BusCheck *check,
+ return BUS_RESULT_FALSE;
+ }
+
+- /* ask policy checkers */
+-#ifdef DBUS_ENABLE_CYNARA
+- cynara = bus_check_get_cynara(check);
+- result = bus_cynara_check_privilege(cynara, message, sender, addressed_recipient,
+- proposed_recipient, privilege, check_type, deferred_message);
++#ifdef DBUS_ENABLE_EMBEDDED_TESTS
++ if (bus_check_test_override)
++ return bus_check_test_override (connection, privilege);
+ #endif
+
+- if (result == BUS_RESULT_LATER && deferred_message != NULL)
++ previous_deferred_message = dbus_message_get_data(message, deferred_message_data_slot);
++ /* check if message blocked at sender's queue is being processed */
++ if (previous_deferred_message != NULL)
++ {
++ if ((check_type & BUS_DEFERRED_MESSAGE_CHECK_SEND) &&
++ !(previous_deferred_message->status & BUS_DEFERRED_MESSAGE_CHECK_SEND))
++ {
++ /**
++ * Message has been deferred due to receive or own rule which means that sending this message
++ * is allowed - it must have been checked previously.
++ * This might happen when client calls RequestName method which depending on security
++ * policy might result in both "can_send" and "can_own" Cynara checks.
++ */
++ result = BUS_RESULT_TRUE;
++ }
++ else
++ {
++ result = previous_deferred_message->response;
++ if (result == BUS_RESULT_LATER)
++ {
++ /* result is still not known - reuse deferred message object */
++ if (deferred_message != NULL)
++ *deferred_message = previous_deferred_message;
++ }
++ else
++ {
++ /* result is available - we can remove deferred message from the processed message */
++ dbus_message_set_data(message, deferred_message_data_slot, NULL, NULL);
++ }
++ }
++ }
++ else
+ {
+- (*deferred_message)->status |= check_type;
++ /* ask policy checkers */
++#ifdef DBUS_ENABLE_CYNARA
++ cynara = bus_check_get_cynara(check);
++ result = bus_cynara_check_privilege(cynara, message, sender, addressed_recipient,
++ proposed_recipient, privilege, check_type, deferred_message);
++#endif
++ if (result == BUS_RESULT_LATER && deferred_message != NULL)
++ {
++ (*deferred_message)->status |= check_type;
++ }
+ }
+ return result;
+ }
+@@ -204,6 +293,12 @@ bus_deferred_message_unref (BusDeferredMessage *deferred_message)
+ }
+ }
+
++BusDeferredMessageStatus
++bus_deferred_message_get_status (BusDeferredMessage *deferred_message)
++{
++ return deferred_message->status;
++}
++
+ void
+ bus_deferred_message_response_received (BusDeferredMessage *deferred_message,
+ BusResult result)
+diff --git a/bus/check.h b/bus/check.h
+index c3fcaf9..f381789 100644
+--- a/bus/check.h
++++ b/bus/check.h
+@@ -55,6 +55,7 @@ BusResult bus_check_privilege (BusCheck *check,
+ BusDeferredMessageStatus check_type,
+ BusDeferredMessage **deferred_message);
+
++
+ BusDeferredMessage *bus_deferred_message_new (DBusMessage *message,
+ DBusConnection *sender,
+ DBusConnection *addressed_recipient,
+@@ -65,4 +66,13 @@ BusDeferredMessage *bus_deferred_message_ref (BusDeferredMessage
+ void bus_deferred_message_unref (BusDeferredMessage *deferred_message);
+ void bus_deferred_message_response_received (BusDeferredMessage *deferred_message,
+ BusResult result);
++void bus_deferred_message_disable_sender (BusDeferredMessage *deferred_message);
++
++BusDeferredMessageStatus bus_deferred_message_get_status (BusDeferredMessage *deferred_message);
++
++#ifdef DBUS_ENABLE_EMBEDDED_TESTS
++extern dbus_bool_t (*bus_check_test_override) (DBusConnection *connection,
++ const char *privilege);
++#endif
++
+ #endif /* BUS_CHECK_H */
+diff --git a/bus/cynara.c b/bus/cynara.c
+index 57a4c45..77aed62 100644
+--- a/bus/cynara.c
++++ b/bus/cynara.c
+@@ -36,7 +36,6 @@
+ #include <cynara-client-async.h>
+ #endif
+
+-
+ #ifdef DBUS_ENABLE_CYNARA
+ typedef struct BusCynara
+ {
+diff --git a/bus/dispatch.c b/bus/dispatch.c
+index ce4076d..6b0eadc 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,
+@@ -117,13 +117,29 @@ bus_dispatch_matches (BusTransaction *transaction,
+ message, error,
+ &deferred_message);
+ if (res == BUS_RESULT_FALSE)
+- return FALSE;
++ return BUS_RESULT_FALSE;
+ else if (res == BUS_RESULT_LATER)
+ {
+- dbus_set_error (error,
+- DBUS_ERROR_ACCESS_DENIED,
+- "Rejecting message because time is needed to check security policy");
+- return 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) &&
+@@ -134,14 +150,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;
+ }
+ }
+
+@@ -156,7 +172,7 @@ bus_dispatch_matches (BusTransaction *transaction,
+ &recipients))
+ {
+ BUS_SET_OOM (error);
+- return FALSE;
++ return BUS_RESULT_FALSE;
+ }
+
+ link = _dbus_list_get_first_link (&recipients);
+@@ -178,10 +194,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 +314,12 @@ bus_dispatch (DBusConnection *connection,
+ }
+ else if (res == 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 +384,14 @@ 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;
++ if (BUS_RESULT_LATER == bus_dispatch_matches (transaction, connection, addressed_recipient,
++ message, &error))
++ {
++ /* 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;
++ }
+
+ out:
+ if (dbus_error_is_set (&error))
+@@ -4714,9 +4738,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,
+diff --git a/bus/dispatch.h b/bus/dispatch.h
+index fb5ba7a..afba6a2 100644
+--- a/bus/dispatch.h
++++ b/bus/dispatch.h
+@@ -29,7 +29,7 @@
+
+ dbus_bool_t bus_dispatch_add_connection (DBusConnection *connection);
+ void bus_dispatch_remove_connection (DBusConnection *connection);
+-dbus_bool_t bus_dispatch_matches (BusTransaction *transaction,
++BusResult bus_dispatch_matches (BusTransaction *transaction,
+ DBusConnection *sender,
+ DBusConnection *recipient,
+ DBusMessage *message,
+diff --git a/bus/driver.c b/bus/driver.c
+index 11706f8..4dbce3d 100644
+--- a/bus/driver.c
++++ b/bus/driver.c
+@@ -97,6 +97,7 @@ bus_driver_send_service_owner_changed (const char *service_name,
+ {
+ DBusMessage *message;
+ dbus_bool_t retval;
++ BusResult res;
+ const char *null_service;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+@@ -129,7 +130,17 @@ bus_driver_send_service_owner_changed (const char *service_name,
+
+ _dbus_assert (dbus_message_has_signature (message, "sss"));
+
+- retval = bus_dispatch_matches (transaction, NULL, NULL, message, error);
++ res = bus_dispatch_matches (transaction, NULL, NULL, message, error);
++ if (res == BUS_RESULT_TRUE)
++ retval = TRUE;
++ else if (res == BUS_RESULT_FALSE)
++ retval = FALSE;
++ else if (res == BUS_RESULT_LATER)
++ {
++ /* should never happen */
++ _dbus_assert_not_reached ("bus_dispatch_matches returned BUS_RESULT_LATER unexpectedly");
++ retval = FALSE;
++ }
+ dbus_message_unref (message);
+
+ return retval;
+diff --git a/dbus/dbus-connection-internal.h b/dbus/dbus-connection-internal.h
+index 64ef336..4fcd118 100644
+--- a/dbus/dbus-connection-internal.h
++++ b/dbus/dbus-connection-internal.h
+@@ -109,6 +109,15 @@ void _dbus_connection_set_pending_fds_function (DBusConnectio
+
+ dbus_bool_t _dbus_connection_get_linux_security_label (DBusConnection *connection,
+ char **label_p);
++void _dbus_connection_enable_dispatch (DBusConnection *connection);
++void _dbus_connection_disable_dispatch (DBusConnection *connection);
++dbus_bool_t _dbus_connection_putback_message (DBusConnection *connection,
++ DBusMessage *after_message,
++ DBusMessage *message,
++ DBusError *error);
++
++dbus_bool_t _dbus_connection_remove_message (DBusConnection *connection,
++ DBusMessage *message);
+
+ /* if DBUS_ENABLE_STATS */
+ void _dbus_connection_get_stats (DBusConnection *connection,
+diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c
+index 8952b75..5d8d943 100644
+--- a/dbus/dbus-connection.c
++++ b/dbus/dbus-connection.c
+@@ -311,7 +311,8 @@ struct DBusConnection
+ */
+ dbus_bool_t dispatch_acquired; /**< Someone has dispatch path (can drain incoming queue) */
+ dbus_bool_t io_path_acquired; /**< Someone has transport io path (can use the transport to read/write messages) */
+-
++
++ unsigned int dispatch_disabled : 1; /**< if true, then dispatching incoming messages is stopped until enabled again */
+ unsigned int shareable : 1; /**< #TRUE if libdbus owns a reference to the connection and can return it from dbus_connection_open() more than once */
+
+ unsigned int exit_on_disconnect : 1; /**< If #TRUE, exit after handling disconnect signal */
+@@ -439,6 +440,39 @@ _dbus_connection_wakeup_mainloop (DBusConnection *connection)
+ (*connection->wakeup_main_function) (connection->wakeup_main_data);
+ }
+
++static void
++_dbus_connection_set_dispatch(DBusConnection *connection,
++ dbus_bool_t disabled)
++{
++ CONNECTION_LOCK (connection);
++ if (connection->dispatch_disabled != disabled)
++ {
++ DBusDispatchStatus status;
++
++ connection->dispatch_disabled = disabled;
++ status = _dbus_connection_get_dispatch_status_unlocked (connection);
++ _dbus_connection_update_dispatch_status_and_unlock (connection, status);
++ }
++ else
++ {
++ CONNECTION_UNLOCK (connection);
++ }
++}
++
++
++void
++_dbus_connection_enable_dispatch (DBusConnection *connection)
++{
++ _dbus_connection_set_dispatch (connection, FALSE);
++}
++
++void
++ _dbus_connection_disable_dispatch (DBusConnection *connection)
++{
++ _dbus_connection_set_dispatch (connection, TRUE);
++}
++
++
+ #ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ /**
+ * Gets the locks so we can examine them
+@@ -4068,6 +4102,82 @@ _dbus_connection_putback_message_link_unlocked (DBusConnection *connection,
+ "_dbus_connection_putback_message_link_unlocked");
+ }
+
++dbus_bool_t
++_dbus_connection_putback_message (DBusConnection *connection,
++ DBusMessage *after_message,
++ DBusMessage *message,
++ DBusError *error)
++{
++ DBusDispatchStatus status;
++ DBusList *message_link = _dbus_list_alloc_link (message);
++ DBusList *after_link;
++ if (message_link == NULL)
++ {
++ _DBUS_SET_OOM (error);
++ return FALSE;
++ }
++ dbus_message_ref (message);
++
++ CONNECTION_LOCK (connection);
++ _dbus_connection_acquire_dispatch (connection);
++ HAVE_LOCK_CHECK (connection);
++
++ after_link = _dbus_list_find_first(&connection->incoming_messages, after_message);
++ _dbus_list_insert_after_link (&connection->incoming_messages, after_link, message_link);
++ connection->n_incoming += 1;
++
++ _dbus_verbose ("Message %p (%s %s %s '%s') put back into queue %p, %d incoming\n",
++ message_link->data,
++ dbus_message_type_to_string (dbus_message_get_type (message_link->data)),
++ dbus_message_get_interface (message_link->data) ?
++ dbus_message_get_interface (message_link->data) :
++ "no interface",
++ dbus_message_get_member (message_link->data) ?
++ dbus_message_get_member (message_link->data) :
++ "no member",
++ dbus_message_get_signature (message_link->data),
++ connection, connection->n_incoming);
++
++ _dbus_message_trace_ref (message_link->data, -1, -1,
++ "_dbus_connection_putback_message");
++
++ _dbus_connection_release_dispatch (connection);
++
++ status = _dbus_connection_get_dispatch_status_unlocked (connection);
++ _dbus_connection_update_dispatch_status_and_unlock (connection, status);
++
++ return TRUE;
++}
++
++dbus_bool_t
++_dbus_connection_remove_message (DBusConnection *connection,
++ DBusMessage *message)
++{
++ DBusDispatchStatus status;
++ dbus_bool_t removed;
++
++ CONNECTION_LOCK (connection);
++ _dbus_connection_acquire_dispatch (connection);
++ HAVE_LOCK_CHECK (connection);
++
++ removed = _dbus_list_remove(&connection->incoming_messages, message);
++
++ if (removed)
++ {
++ connection->n_incoming -= 1;
++ dbus_message_unref(message);
++ _dbus_verbose ("Message %p removed from incoming queue\n", message);
++ }
++ else
++ _dbus_verbose ("Message %p not found in the incoming queue\n", message);
++
++ _dbus_connection_release_dispatch (connection);
++
++ status = _dbus_connection_get_dispatch_status_unlocked (connection);
++ _dbus_connection_update_dispatch_status_and_unlock (connection, status);
++ return removed;
++}
++
+ /**
+ * Returns the first-received message from the incoming message queue,
+ * removing it from the queue. The caller owns a reference to the
+@@ -4251,8 +4361,9 @@ static DBusDispatchStatus
+ _dbus_connection_get_dispatch_status_unlocked (DBusConnection *connection)
+ {
+ HAVE_LOCK_CHECK (connection);
+-
+- if (connection->n_incoming > 0)
++ if (connection->dispatch_disabled && dbus_connection_get_is_connected(connection))
++ return DBUS_DISPATCH_COMPLETE;
++ else if (connection->n_incoming > 0)
+ return DBUS_DISPATCH_DATA_REMAINS;
+ else if (!_dbus_transport_queue_messages (connection->transport))
+ return DBUS_DISPATCH_NEED_MEMORY;
+@@ -4689,6 +4800,8 @@ dbus_connection_dispatch (DBusConnection *connection)
+
+ CONNECTION_LOCK (connection);
+
++ if (result == DBUS_HANDLER_RESULT_LATER)
++ goto out;
+ if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
+ {
+ _dbus_verbose ("No memory\n");
+@@ -4811,9 +4924,11 @@ dbus_connection_dispatch (DBusConnection *connection)
+ connection);
+
+ out:
+- if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
++ if (result == DBUS_HANDLER_RESULT_LATER ||
++ result == DBUS_HANDLER_RESULT_NEED_MEMORY)
+ {
+- _dbus_verbose ("out of memory\n");
++ if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
++ _dbus_verbose ("out of memory\n");
+
+ /* Put message back, and we'll start over.
+ * Yes this means handlers must be idempotent if they
+diff --git a/dbus/dbus-list.c b/dbus/dbus-list.c
+index c4c1856..f84918b 100644
+--- a/dbus/dbus-list.c
++++ b/dbus/dbus-list.c
+@@ -459,6 +459,35 @@ _dbus_list_remove_last (DBusList **list,
+ }
+
+ /**
++ * Finds a value in the list. Returns the first link
++ * with value equal to the given data pointer.
++ * This is a linear-time operation.
++ * Returns #NULL if no value found that matches.
++ *
++ * @param list address of the list head.
++ * @param data the value to find.
++ * @returns the link if found
++ */
++DBusList*
++_dbus_list_find_first (DBusList **list,
++ void *data)
++{
++ DBusList *link;
++
++ link = _dbus_list_get_first_link (list);
++
++ while (link != NULL)
++ {
++ if (link->data == data)
++ return link;
++
++ link = _dbus_list_get_next_link (list, link);
++ }
++
++ return NULL;
++}
++
++/**
+ * Finds a value in the list. Returns the last link
+ * with value equal to the given data pointer.
+ * This is a linear-time operation.
+diff --git a/dbus/dbus-list.h b/dbus/dbus-list.h
+index 910d738..abe8331 100644
+--- a/dbus/dbus-list.h
++++ b/dbus/dbus-list.h
+@@ -59,6 +59,8 @@ dbus_bool_t _dbus_list_remove_last (DBusList **list,
+ void *data);
+ void _dbus_list_remove_link (DBusList **list,
+ DBusList *link);
++DBusList* _dbus_list_find_first (DBusList **list,
++ void *data);
+ DBusList* _dbus_list_find_last (DBusList **list,
+ void *data);
+ void _dbus_list_clear (DBusList **list);
+diff --git a/dbus/dbus-shared.h b/dbus/dbus-shared.h
+index 6a57670..5371d88 100644
+--- a/dbus/dbus-shared.h
++++ b/dbus/dbus-shared.h
+@@ -67,7 +67,8 @@ typedef enum
+ {
+ DBUS_HANDLER_RESULT_HANDLED, /**< Message has had its effect - no need to run more handlers. */
+ DBUS_HANDLER_RESULT_NOT_YET_HANDLED, /**< Message has not had any effect - see if other handlers want it. */
+- DBUS_HANDLER_RESULT_NEED_MEMORY /**< Need more memory in order to return #DBUS_HANDLER_RESULT_HANDLED or #DBUS_HANDLER_RESULT_NOT_YET_HANDLED. Please try again later with more memory. */
++ DBUS_HANDLER_RESULT_NEED_MEMORY, /**< Need more memory in order to return #DBUS_HANDLER_RESULT_HANDLED or #DBUS_HANDLER_RESULT_NOT_YET_HANDLED. Please try again later with more memory. */
++ DBUS_HANDLER_RESULT_LATER /**< Message dispatch deferred due to pending policy check */
+ } DBusHandlerResult;
+
+ /* Bus names */
+--
+2.1.4
+