dbus-cynara: Upgrade to 1.10.20
[AGL/meta-agl.git] / meta-security / recipes-core / dbus-cynara / dbus-cynara / 0005-Disable-message-dispatching-when-send-rule-result-is.patch
diff --git a/meta-security/recipes-core/dbus-cynara/dbus-cynara/0005-Disable-message-dispatching-when-send-rule-result-is.patch b/meta-security/recipes-core/dbus-cynara/dbus-cynara/0005-Disable-message-dispatching-when-send-rule-result-is.patch
new file mode 100644 (file)
index 0000000..ca78714
--- /dev/null
@@ -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
+