Integrate parts of meta-intel-iot-security
[AGL/meta-agl.git] / meta-security / recipes-core / dbus / dbus-cynara / 0006-Handle-unavailability-of-policy-results-for-broadcas.patch
diff --git a/meta-security/recipes-core/dbus/dbus-cynara/0006-Handle-unavailability-of-policy-results-for-broadcas.patch b/meta-security/recipes-core/dbus/dbus-cynara/0006-Handle-unavailability-of-policy-results-for-broadcas.patch
new file mode 100644 (file)
index 0000000..66f4e14
--- /dev/null
@@ -0,0 +1,1071 @@
+From 1e231194610892dd4360224998d91336097b05a1 Mon Sep 17 00:00:00 2001
+From: Jacek Bukarewicz <j.bukarewicz@samsung.com>
+Date: Fri, 28 Nov 2014 12:39:33 +0100
+Subject: [PATCH 6/8] Handle unavailability of policy results for broadcasts
+ and receive rules
+
+When message is sent to the addressed recipient and receive rule
+result is unavailable we don't want to block the sender
+as it most likely will be the privileged service, so instead we queue
+it at the recipient. Any further messages sent to it will be queued to
+maintain message order. Once the answer from Cynara arrives messages are
+dispatched from the recipient queue. In such case full dispatch is
+performed - messages are sent to addressed recipient and other
+interested connections.
+Messages sent to non-addressed recipients (eavesdroppers or broadcast
+message recipients) are handled in a similar way. The difference is
+that it is not full dispatch meaning message is sent to a single recipient.
+
+Change-Id: Iecd5395f75a4c7811fa97247a37d8fc4d42e8814
+---
+ bus/activation.c |   4 +-
+ bus/bus.c        |  50 +++++++--
+ bus/bus.h        |  19 ++++
+ bus/check.c      | 307 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ bus/check.h      |  25 +++++
+ bus/connection.c | 166 ++++++++++++++++++++++++++++--
+ bus/connection.h |  19 +++-
+ bus/dispatch.c   | 122 ++++++++++++++++++----
+ bus/dispatch.h   |  11 +-
+ bus/driver.c     |   2 +-
+ bus/policy.c     |   6 ++
+ 11 files changed, 685 insertions(+), 46 deletions(-)
+
+diff --git a/bus/activation.c b/bus/activation.c
+index 8c43941..308bf41 100644
+--- a/bus/activation.c
++++ b/bus/activation.c
+@@ -1198,7 +1198,7 @@ bus_activation_send_pending_auto_activation_messages (BusActivation  *activation
+           res = bus_dispatch_matches (transaction,
+                                       entry->connection,
+                                       addressed_recipient,
+-                                      entry->activation_message, &error);
++                                      entry->activation_message, NULL, &error);
+           if (res == BUS_RESULT_FALSE)
+             {
+               /* If permission is denied, we just want to return the error
+@@ -2066,7 +2066,7 @@ bus_activation_activate_service (BusActivation  *activation,
+                                entry->systemd_service);
+               /* Wonderful, systemd is connected, let's just send the msg */
+               res = bus_dispatch_matches (activation_transaction, NULL, bus_service_get_primary_owners_connection (service),
+-                                            message, error);
++                                          message, NULL, error);
+               if (res == BUS_RESULT_TRUE)
+                 retval = TRUE;
+diff --git a/bus/bus.c b/bus/bus.c
+index ac9ea8d..b478b8e 100644
+--- a/bus/bus.c
++++ b/bus/bus.c
+@@ -1704,17 +1704,9 @@ bus_context_check_security_policy (BusContext          *context,
+     }
+   /* See if limits on size have been exceeded */
+-  if (proposed_recipient &&
+-      ((dbus_connection_get_outgoing_size (proposed_recipient) > context->limits.max_outgoing_bytes) ||
+-       (dbus_connection_get_outgoing_unix_fds (proposed_recipient) > context->limits.max_outgoing_unix_fds)))
+-    {
+-      complain_about_message (context, DBUS_ERROR_LIMITS_EXCEEDED,
+-          "Rejected: destination has a full message queue",
+-          0, message, sender, proposed_recipient, requested_reply, TRUE, NULL,
+-          error);
+-      _dbus_verbose ("security policy disallowing message due to full message queue\n");
++  if (!bus_context_check_recipient_message_limits(context, proposed_recipient, sender, message,
++      requested_reply, error))
+       return BUS_RESULT_FALSE;
+-    }
+   /* Record that we will allow a reply here in the future (don't
+    * bother if the recipient is the bus or this is an eavesdropping
+@@ -1769,3 +1761,41 @@ bus_context_check_all_watches (BusContext *context)
+       _dbus_server_toggle_all_watches (server, enabled);
+     }
+ }
++
++void
++bus_context_complain_about_message (BusContext     *context,
++                                    const char     *error_name,
++                                    const char     *complaint,
++                                    int             matched_rules,
++                                    DBusMessage    *message,
++                                    DBusConnection *sender,
++                                    DBusConnection *proposed_recipient,
++                                    dbus_bool_t     requested_reply,
++                                    dbus_bool_t     log,
++                                    const char     *privilege,
++                                    DBusError      *error)
++{
++  complain_about_message(context, error_name, complaint, matched_rules, message, sender,
++      proposed_recipient, requested_reply, log, privilege, error);
++}
++
++dbus_bool_t bus_context_check_recipient_message_limits (BusContext *context,
++                                                        DBusConnection *recipient,
++                                                        DBusConnection *sender,
++                                                        DBusMessage *message,
++                                                        dbus_bool_t requested_reply,
++                                                        DBusError *error)
++{
++  if (recipient &&
++       ((dbus_connection_get_outgoing_size (recipient) > context->limits.max_outgoing_bytes) ||
++        (dbus_connection_get_outgoing_unix_fds (recipient) > context->limits.max_outgoing_unix_fds)))
++     {
++       complain_about_message (context, DBUS_ERROR_LIMITS_EXCEEDED,
++           "Rejected: destination has a full message queue",
++           0, message, sender, recipient, requested_reply, TRUE, NULL,
++           error);
++       _dbus_verbose ("security policy disallowing message due to full message queue\n");
++       return FALSE;
++     }
++  return TRUE;
++}
+diff --git a/bus/bus.h b/bus/bus.h
+index 78084dd..27a5e49 100644
+--- a/bus/bus.h
++++ b/bus/bus.h
+@@ -148,4 +148,23 @@ BusResult         bus_context_check_security_policy              (BusContext
+                                                                   DBusError           *error,
+                                                                   BusDeferredMessage **deferred_message);
++dbus_bool_t       bus_context_check_recipient_message_limits     (BusContext *context,
++                                                                  DBusConnection *recipient,
++                                                                  DBusConnection *sender,
++                                                                  DBusMessage *message,
++                                                                  dbus_bool_t requested_reply,
++                                                                  DBusError *error);
++void              bus_context_complain_about_message             (BusContext     *context,
++                                                                  const char     *error_name,
++                                                                  const char     *complaint,
++                                                                  int             matched_rules,
++                                                                  DBusMessage    *message,
++                                                                  DBusConnection *sender,
++                                                                  DBusConnection *proposed_recipient,
++                                                                  dbus_bool_t     requested_reply,
++                                                                  dbus_bool_t     log,
++                                                                  const char     *privilege,
++                                                                  DBusError      *error);
++
++
+ #endif /* BUS_BUS_H */
+diff --git a/bus/check.c b/bus/check.c
+index cd6a74b..733763a 100644
+--- a/bus/check.c
++++ b/bus/check.c
+@@ -49,6 +49,9 @@ typedef struct BusDeferredMessage
+   DBusConnection *sender;
+   DBusConnection *proposed_recipient;
+   DBusConnection *addressed_recipient;
++  dbus_bool_t requested_reply;
++  int matched_rules;
++  const char *privilege;
+   dbus_bool_t full_dispatch;
+   BusDeferredMessageStatus status;
+   BusResult response;
+@@ -136,6 +139,89 @@ bus_check_enable_dispatch_callback (BusDeferredMessage *deferred_message,
+ }
+ static void
++bus_check_queued_message_reply_callback (BusDeferredMessage *deferred_message,
++                                         BusResult result)
++{
++  int status;
++
++  _dbus_verbose("bus_check_queued_message_reply_callback called message=%p\n", deferred_message);
++
++  if (!bus_connection_is_active(deferred_message->proposed_recipient))
++    return;
++
++  status = deferred_message->status;
++
++  deferred_message->status = 0; /* mark message as not waiting for response */
++  deferred_message->response = result;
++
++  /*
++   * If send rule allows us to send message we still need to check receive rules.
++   */
++  if ((status & BUS_DEFERRED_MESSAGE_CHECK_SEND) && (result == BUS_RESULT_TRUE))
++    {
++      int toggles;
++      BusContext *context;
++      BusRegistry *registry;
++      BusClientPolicy *recipient_policy;
++      BusDeferredMessage *deferred_message_receive;
++
++      context = bus_connection_get_context(deferred_message->proposed_recipient);
++      registry = bus_context_get_registry(context);
++      recipient_policy = bus_connection_get_policy(deferred_message->proposed_recipient);
++
++      deferred_message->response = bus_client_policy_check_can_receive(recipient_policy, registry,
++          deferred_message->requested_reply, deferred_message->sender,
++          deferred_message->addressed_recipient, deferred_message->proposed_recipient, deferred_message->message,
++          &toggles, NULL, &deferred_message_receive);
++      if (deferred_message->response == BUS_RESULT_LATER)
++        {
++          /* replace deferred message associated with send check with the one associated with
++           * receive check */
++          if (!bus_deferred_message_replace(deferred_message, deferred_message_receive))
++            {
++              /* failed to replace deferred message (due to oom). Set it to rejected */
++              deferred_message->response = BUS_RESULT_FALSE;
++            }
++        }
++    }
++
++  bus_connection_dispatch_deferred(deferred_message->proposed_recipient);
++}
++
++static void
++queue_deferred_message_cancel_transaction_hook (void *data)
++{
++  BusDeferredMessage *deferred_message = (BusDeferredMessage *)data;
++  bus_connection_remove_deferred_message(deferred_message->proposed_recipient, deferred_message);
++}
++
++
++dbus_bool_t
++bus_deferred_message_queue_at_recipient (BusDeferredMessage *deferred_message,
++                                         BusTransaction *transaction,
++                                         dbus_bool_t full_dispatch,
++                                         dbus_bool_t prepend)
++{
++  _dbus_assert(deferred_message != NULL);
++  _dbus_assert(deferred_message->proposed_recipient != NULL);
++
++  if (!bus_connection_queue_deferred_message(deferred_message->proposed_recipient,
++         deferred_message, prepend))
++    return FALSE;
++
++  if (!bus_transaction_add_cancel_hook(transaction, queue_deferred_message_cancel_transaction_hook,
++      deferred_message, NULL))
++    {
++      bus_connection_remove_deferred_message(deferred_message->proposed_recipient, deferred_message);
++      return FALSE;
++    }
++  deferred_message->response_callback = bus_check_queued_message_reply_callback;
++  deferred_message->full_dispatch = full_dispatch;
++
++  return TRUE;
++}
++
++static void
+ deferred_message_free_function(void *data)
+ {
+   BusDeferredMessage *deferred_message = (BusDeferredMessage *)data;
+@@ -159,6 +245,20 @@ bus_deferred_message_disable_sender (BusDeferredMessage *deferred_message)
+   deferred_message->response_callback = bus_check_enable_dispatch_callback;
+ }
++void
++bus_deferred_message_set_policy_check_info (BusDeferredMessage *deferred_message,
++                                            dbus_bool_t requested_reply,
++                                            int matched_rules,
++                                            const char *privilege)
++{
++  _dbus_assert(deferred_message != NULL);
++
++  deferred_message->requested_reply = requested_reply;
++  deferred_message->matched_rules = matched_rules;
++  deferred_message->privilege = privilege;
++}
++
++
+ #ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ dbus_bool_t (*bus_check_test_override) (DBusConnection *connection,
+                                         const char *privilege);
+@@ -257,6 +357,9 @@ BusDeferredMessage *bus_deferred_message_new (DBusMessage *message,
+   deferred_message->addressed_recipient = addressed_recipient != NULL ? dbus_connection_ref(addressed_recipient) : NULL;
+   deferred_message->proposed_recipient = proposed_recipient != NULL ? dbus_connection_ref(proposed_recipient) : NULL;
+   deferred_message->message = dbus_message_ref(message);
++  deferred_message->requested_reply = FALSE;
++  deferred_message->matched_rules = 0;
++  deferred_message->privilege = NULL;
+   deferred_message->response = response;
+   deferred_message->status = 0;
+   deferred_message->full_dispatch = FALSE;
+@@ -293,12 +396,215 @@ bus_deferred_message_unref (BusDeferredMessage *deferred_message)
+      }
+ }
++dbus_bool_t
++bus_deferred_message_check_message_limits (BusDeferredMessage *deferred_message, DBusError *error)
++{
++  BusContext *context = bus_connection_get_context(deferred_message->proposed_recipient);
++
++  return bus_context_check_recipient_message_limits(context, deferred_message->proposed_recipient,
++      deferred_message->sender, deferred_message->message, deferred_message->requested_reply,
++      error);
++}
++
++dbus_bool_t
++bus_deferred_message_expect_method_reply(BusDeferredMessage *deferred_message, BusTransaction *transaction, DBusError *error)
++{
++  int type = dbus_message_get_type(deferred_message->message);
++  if (type == DBUS_MESSAGE_TYPE_METHOD_CALL &&
++        deferred_message->sender &&
++        deferred_message->addressed_recipient &&
++        deferred_message->addressed_recipient == deferred_message->proposed_recipient && /* not eavesdropping */
++        !bus_connections_expect_reply (bus_connection_get_connections (deferred_message->sender),
++                                       transaction,
++                                       deferred_message->sender, deferred_message->addressed_recipient,
++                                       deferred_message->message, error))
++    {
++      _dbus_verbose ("Failed to record reply expectation or problem with the message expecting a reply\n");
++      return FALSE;
++    }
++  return TRUE;
++}
++
++void
++bus_deferred_message_create_error(BusDeferredMessage *deferred_message,
++    const char *error_message, DBusError *error)
++{
++  BusContext *context;
++  _dbus_assert (deferred_message->status == 0 && deferred_message->response == BUS_RESULT_FALSE);
++
++  if (deferred_message->sender == NULL)
++    return; /* error won't be sent to bus driver anyway */
++
++  context = bus_connection_get_context(deferred_message->sender);
++  bus_context_complain_about_message(context, DBUS_ERROR_ACCESS_DENIED, "Rejected message",
++      deferred_message->matched_rules, deferred_message->message, deferred_message->sender,
++      deferred_message->proposed_recipient, deferred_message->requested_reply, FALSE,
++      deferred_message->privilege, error);
++}
++
++BusResult
++bus_deferred_message_dispatch (BusDeferredMessage *deferred_message)
++{
++  BusContext *context = bus_connection_get_context (deferred_message->proposed_recipient);
++  BusTransaction *transaction = bus_transaction_new (context);
++  BusResult result = BUS_RESULT_TRUE;
++  DBusError error;
++
++  if (transaction == NULL)
++    {
++      return BUS_RESULT_FALSE;
++    }
++
++  dbus_error_init(&error);
++
++  if (!deferred_message->full_dispatch)
++    {
++      result = deferred_message->response;
++      if (result == BUS_RESULT_TRUE)
++        {
++          if (!bus_context_check_recipient_message_limits(context, deferred_message->proposed_recipient,
++               deferred_message->sender, deferred_message->message, deferred_message->requested_reply, &error))
++              result = BUS_RESULT_FALSE;
++        }
++      else if (result == BUS_RESULT_LATER)
++        {
++          BusDeferredMessage *deferred_message2;
++          result = bus_context_check_security_policy (context, transaction,
++                                                      deferred_message->sender,
++                                                      deferred_message->addressed_recipient,
++                                                      deferred_message->proposed_recipient,
++                                                      deferred_message->message, NULL,
++                                                      &deferred_message2);
++
++          if (result == BUS_RESULT_LATER)
++            {
++              /* prepend at recipient */
++              if (!bus_deferred_message_queue_at_recipient(deferred_message2, transaction,
++                  FALSE, TRUE))
++                result = BUS_RESULT_FALSE;
++            }
++        }
++
++      /* silently drop messages on access denial */
++      if (result == BUS_RESULT_TRUE)
++        {
++          if (!bus_transaction_send (transaction, deferred_message->proposed_recipient, deferred_message->message, TRUE))
++            result = BUS_RESULT_FALSE;
++        }
++
++      bus_transaction_execute_and_free(transaction);
++
++      goto out;
++    }
++
++  /* do not attempt to send message if sender has disconnected */
++  if (deferred_message->sender != NULL && !bus_connection_is_active(deferred_message->sender))
++    {
++      bus_transaction_cancel_and_free(transaction);
++      result = BUS_RESULT_FALSE;
++      goto out;
++    }
++
++  result = bus_dispatch_matches(transaction, deferred_message->sender,
++      deferred_message->addressed_recipient, deferred_message->message, deferred_message, &error);
++
++  if (result == BUS_RESULT_LATER)
++    {
++      /* Message deferring was already done in bus_dispatch_matches */
++      bus_transaction_cancel_and_free(transaction);
++      goto out;
++    }
++
++  /* this part is a copy & paste from bus_dispatch function. Probably can be moved to a function */
++  if (dbus_error_is_set (&error))
++    {
++      if (!dbus_connection_get_is_connected (deferred_message->sender))
++        {
++          /* If we disconnected it, we won't bother to send it any error
++           * messages.
++           */
++          _dbus_verbose ("Not sending error to connection we disconnected\n");
++        }
++      else if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
++        {
++          bus_connection_send_oom_error (deferred_message->sender, deferred_message->message);
++
++          /* cancel transaction due to OOM */
++          if (transaction != NULL)
++            {
++              bus_transaction_cancel_and_free (transaction);
++              transaction = NULL;
++            }
++        }
++      else
++        {
++          /* Try to send the real error, if no mem to do that, send
++           * the OOM error
++           */
++          _dbus_assert (transaction != NULL);
++          if (!bus_transaction_send_error_reply (transaction, deferred_message->sender,
++                                                 &error, deferred_message->message))
++            {
++              bus_connection_send_oom_error (deferred_message->sender, deferred_message->message);
++
++              /* cancel transaction due to OOM */
++              if (transaction != NULL)
++                {
++                  bus_transaction_cancel_and_free (transaction);
++                  transaction = NULL;
++                }
++            }
++        }
++    }
++
++  if (transaction != NULL)
++    {
++      bus_transaction_execute_and_free (transaction);
++    }
++
++out:
++  dbus_error_free(&error);
++
++  return result;
++}
++
++dbus_bool_t
++bus_deferred_message_replace (BusDeferredMessage *old_message, BusDeferredMessage *new_message)
++{
++  if (bus_connection_replace_deferred_message(old_message->proposed_recipient,
++        old_message, new_message))
++    {
++      new_message->response_callback = old_message->response_callback;
++      new_message->full_dispatch = old_message->full_dispatch;
++      return TRUE;
++    }
++  return FALSE;
++}
++
++dbus_bool_t
++bus_deferred_message_waits_for_check(BusDeferredMessage *deferred_message)
++{
++  return deferred_message->status != 0;
++}
++
++DBusConnection *
++bus_deferred_message_get_recipient(BusDeferredMessage *deferred_message)
++{
++  return deferred_message->proposed_recipient;
++}
++
+ BusDeferredMessageStatus
+ bus_deferred_message_get_status (BusDeferredMessage *deferred_message)
+ {
+   return deferred_message->status;
+ }
++BusResult
++bus_deferred_message_get_response (BusDeferredMessage *deferred_message)
++{
++  return deferred_message->response;
++}
++
+ void
+ bus_deferred_message_response_received (BusDeferredMessage *deferred_message,
+                                         BusResult result)
+@@ -308,3 +614,4 @@ bus_deferred_message_response_received (BusDeferredMessage *deferred_message,
+       deferred_message->response_callback(deferred_message, result);
+     }
+ }
++
+diff --git a/bus/check.h b/bus/check.h
+index f381789..3c6b2a1 100644
+--- a/bus/check.h
++++ b/bus/check.h
+@@ -64,12 +64,37 @@ BusDeferredMessage *bus_deferred_message_new                (DBusMessage *messag
+ BusDeferredMessage *bus_deferred_message_ref                (BusDeferredMessage *deferred_message);
+ void                bus_deferred_message_unref              (BusDeferredMessage *deferred_message);
++BusResult           bus_deferred_message_dispatch           (BusDeferredMessage *deferred_message);
++dbus_bool_t         bus_deferred_message_waits_for_check    (BusDeferredMessage *deferred_message);
++DBusConnection     *bus_deferred_message_get_recipient      (BusDeferredMessage *deferred_message);
+ void                bus_deferred_message_response_received  (BusDeferredMessage *deferred_message,
+                                                              BusResult result);
++dbus_bool_t         bus_deferred_message_queue_at_recipient (BusDeferredMessage *deferred_message,
++                                                             BusTransaction *transaction,
++                                                             dbus_bool_t full_dispatch,
++                                                             dbus_bool_t prepend);
++dbus_bool_t         bus_deferred_message_replace            (BusDeferredMessage *old_message,
++                                                             BusDeferredMessage *new_message);
+ void                bus_deferred_message_disable_sender     (BusDeferredMessage *deferred_message);
++BusResult           bus_deferred_message_get_response       (BusDeferredMessage *deferred_message);
+ BusDeferredMessageStatus  bus_deferred_message_get_status   (BusDeferredMessage *deferred_message);
++
++dbus_bool_t         bus_deferred_message_expect_method_reply (BusDeferredMessage *deferred_message,
++                                                              BusTransaction *transaction,
++                                                              DBusError *error);
++void                bus_deferred_message_create_error        (BusDeferredMessage *deferred_message,
++                                                              const char *error_message,
++                                                              DBusError *error);
++void                bus_deferred_message_set_policy_check_info (BusDeferredMessage *deferred_message,
++                                                                dbus_bool_t requested_reply,
++                                                                int matched_rules,
++                                                                const char *privilege);
++dbus_bool_t         bus_deferred_message_check_message_limits (BusDeferredMessage *deferred_message,
++                                                               DBusError *error);
++
++
+ #ifdef DBUS_ENABLE_EMBEDDED_TESTS
+ extern dbus_bool_t (*bus_check_test_override) (DBusConnection *connection,
+                                                const char *privilege);
+diff --git a/bus/connection.c b/bus/connection.c
+index a6d87e5..d2ebe82 100644
+--- a/bus/connection.c
++++ b/bus/connection.c
+@@ -30,10 +30,12 @@
+ #include "signals.h"
+ #include "expirelist.h"
+ #include "selinux.h"
++#include "check.h"
+ #include <dbus/dbus-list.h>
+ #include <dbus/dbus-hash.h>
+ #include <dbus/dbus-timeout.h>
+ #include <dbus/dbus-connection-internal.h>
++#include <dbus/dbus-message-internal.h>
+ #ifdef DBUS_ENABLE_CYNARA
+ #include <stdlib.h>
+ #include <cynara-session.h>
+@@ -95,6 +97,7 @@ typedef struct
+   DBusMessage *oom_message;
+   DBusPreallocatedSend *oom_preallocated;
+   BusClientPolicy *policy;
++  DBusList *deferred_messages;  /**< Queue of messages deferred due to pending policy check */
+   char *cached_loginfo_string;
+   BusSELinuxID *selinux_id;
+@@ -256,6 +259,8 @@ bus_connection_disconnected (DBusConnection *connection)
+       bus_transaction_execute_and_free (transaction);
+     }
++  bus_connection_clear_deferred_messages(connection);
++
+   bus_dispatch_remove_connection (connection);
+   
+   /* no more watching */
+@@ -2132,6 +2137,7 @@ bus_transaction_send_from_driver (BusTransaction *transaction,
+                                   DBusMessage    *message)
+ {
+   BusResult res;
++  BusDeferredMessage *deferred_message;
+   /* We have to set the sender to the driver, and have
+    * to check security policy since it was not done in
+    * dispatch.c
+@@ -2163,23 +2169,25 @@ bus_transaction_send_from_driver (BusTransaction *transaction,
+   res = bus_context_check_security_policy (bus_transaction_get_context (transaction),
+                                            transaction,
+                                            NULL, connection, connection, message, NULL,
+-                                           NULL);
++                                           &deferred_message);
+   if (res == BUS_RESULT_FALSE)
+     return TRUE;
+   else if (res == BUS_RESULT_LATER)
+     {
+-      _dbus_verbose ("Cannot delay sending message from bus driver, dropping it\n");
+-      return TRUE;
++      if (!bus_deferred_message_queue_at_recipient(deferred_message, transaction, FALSE, FALSE))
++        return FALSE;
++      return TRUE; /* pretend to have sent it */
+     }
+-  return bus_transaction_send (transaction, connection, message);
++  return bus_transaction_send (transaction, connection, message, FALSE);
+ }
+ dbus_bool_t
+ bus_transaction_send (BusTransaction *transaction,
+                       DBusConnection *connection,
+-                      DBusMessage    *message)
++                      DBusMessage    *message,
++                      dbus_bool_t     deferred_dispatch)
+ {
+   MessageToSend *to_send;
+   BusConnectionData *d;
+@@ -2205,7 +2213,28 @@ bus_transaction_send (BusTransaction *transaction,
+   
+   d = BUS_CONNECTION_DATA (connection);
+   _dbus_assert (d != NULL);
+-  
++
++  if (!deferred_dispatch && d->deferred_messages != NULL)
++    {
++      BusDeferredMessage *deferred_message;
++      dbus_bool_t success;
++      /* sender and addressed recipient are not required at this point as we only need to send message
++       * to a single recipient without performing policy check. */
++      deferred_message = bus_deferred_message_new (message,
++                                                   NULL,
++                                                   NULL,
++                                                   connection,
++                                                   BUS_RESULT_TRUE);
++      if (deferred_message == NULL)
++        return FALSE;
++
++      success = bus_deferred_message_queue_at_recipient(deferred_message, transaction,
++          FALSE, FALSE);
++      bus_deferred_message_unref(deferred_message);
++
++      return success;
++    }
++
+   to_send = dbus_new (MessageToSend, 1);
+   if (to_send == NULL)
+     {
+@@ -2457,6 +2486,131 @@ bus_transaction_add_cancel_hook (BusTransaction               *transaction,
+   return TRUE;
+ }
++void
++bus_connection_dispatch_deferred (DBusConnection *connection)
++{
++  BusDeferredMessage *message;
++
++  _dbus_return_if_fail (connection != NULL);
++
++  while ((message = bus_connection_pop_deferred_message(connection)) != NULL)
++    {
++      bus_deferred_message_dispatch(message);
++      bus_deferred_message_unref(message);
++    }
++}
++
++dbus_bool_t
++bus_connection_has_deferred_messages (DBusConnection *connection)
++{
++  BusConnectionData *d = BUS_CONNECTION_DATA(connection);
++  return d->deferred_messages != NULL ? TRUE : FALSE;
++}
++
++dbus_bool_t
++bus_connection_queue_deferred_message (DBusConnection *connection,
++                                       BusDeferredMessage *message,
++                                       dbus_bool_t prepend)
++{
++  BusConnectionData *d = BUS_CONNECTION_DATA(connection);
++  dbus_bool_t success;
++  if (prepend)
++    success = _dbus_list_prepend(&d->deferred_messages, message);
++  else
++    success = _dbus_list_append(&d->deferred_messages, message);
++
++  if (success)
++    {
++      bus_deferred_message_ref(message);
++      return TRUE;
++    }
++
++  return FALSE;
++}
++
++dbus_bool_t
++bus_connection_replace_deferred_message (DBusConnection *connection,
++                                         BusDeferredMessage *oldMessage,
++                                         BusDeferredMessage *newMessage)
++{
++  DBusList *link;
++  BusConnectionData *d = BUS_CONNECTION_DATA(connection);
++
++  link = _dbus_list_find_first(&d->deferred_messages, oldMessage);
++  if (link == NULL)
++    return FALSE;
++
++  if (!_dbus_list_insert_after(&d->deferred_messages, link, newMessage))
++    return FALSE;
++
++  bus_deferred_message_ref(newMessage);
++  _dbus_list_remove_link(&d->deferred_messages, link);
++  bus_deferred_message_unref(oldMessage);
++  return TRUE;
++}
++
++BusDeferredMessage *
++bus_connection_pop_deferred_message (DBusConnection *connection)
++{
++  DBusList *link;
++  BusDeferredMessage *message;
++  BusConnectionData *d = BUS_CONNECTION_DATA(connection);
++
++  link =_dbus_list_get_first_link(&d->deferred_messages);
++  if (link != NULL)
++    {
++      message = link->data;
++      if (!bus_deferred_message_waits_for_check(message))
++        {
++          _dbus_list_remove_link(&d->deferred_messages, link);
++          return message;
++        }
++    }
++
++  return NULL;
++}
++
++dbus_bool_t
++bus_connection_putback_deferred_message (DBusConnection *connection, BusDeferredMessage *message)
++{
++  BusConnectionData *d = BUS_CONNECTION_DATA(connection);
++  if (_dbus_list_prepend(&d->deferred_messages, message))
++    {
++      return TRUE;
++    }
++  return FALSE;
++}
++
++void
++bus_connection_clear_deferred_messages (DBusConnection *connection)
++{
++  BusConnectionData *d = BUS_CONNECTION_DATA(connection);
++  DBusList *link;
++  DBusList *next;
++  BusDeferredMessage *message;
++
++  link =_dbus_list_get_first_link(&d->deferred_messages);
++  while (link != NULL)
++    {
++      next = _dbus_list_get_next_link (&d->deferred_messages, link);
++      message = link->data;
++
++      bus_deferred_message_unref(message);
++      _dbus_list_remove_link(&d->deferred_messages, link);
++
++      link = next;
++    }
++}
++
++void
++bus_connection_remove_deferred_message (DBusConnection *connection,
++                                        BusDeferredMessage *message)
++{
++  BusConnectionData *d = BUS_CONNECTION_DATA(connection);
++  if (_dbus_list_remove(&d->deferred_messages, message))
++    bus_deferred_message_unref(message);
++}
++
+ int
+ bus_connections_get_n_active (BusConnections *connections)
+ {
+diff --git a/bus/connection.h b/bus/connection.h
+index 7433746..8d49b25 100644
+--- a/bus/connection.h
++++ b/bus/connection.h
+@@ -82,6 +82,22 @@ dbus_bool_t bus_connection_preallocate_oom_error (DBusConnection *connection);
+ void        bus_connection_send_oom_error        (DBusConnection *connection,
+                                                   DBusMessage    *in_reply_to);
++dbus_bool_t         bus_connection_has_deferred_messages    (DBusConnection *connection);
++dbus_bool_t         bus_connection_queue_deferred_message   (DBusConnection *connection,
++                                                             BusDeferredMessage *message,
++                                                             dbus_bool_t prepend);
++BusDeferredMessage *bus_connection_pop_deferred_message     (DBusConnection *connection);
++dbus_bool_t         bus_connection_putback_deferred_message (DBusConnection *connection,
++                                                             BusDeferredMessage *message);
++void                bus_connection_remove_deferred_message  (DBusConnection *connection,
++                                                             BusDeferredMessage *message);
++dbus_bool_t         bus_connection_replace_deferred_message (DBusConnection *connection,
++                                                             BusDeferredMessage *oldMessage,
++                                                             BusDeferredMessage *newMessage);
++void                bus_connection_dispatch_deferred        (DBusConnection *connection);
++void                bus_connection_clear_deferred_messages  (DBusConnection *connection);
++
++
+ /* called by signals.c */
+ dbus_bool_t bus_connection_add_match_rule      (DBusConnection *connection,
+                                                 BusMatchRule   *rule);
+@@ -129,7 +145,8 @@ BusTransaction* bus_transaction_new              (BusContext                   *
+ BusContext*     bus_transaction_get_context      (BusTransaction               *transaction);
+ dbus_bool_t     bus_transaction_send             (BusTransaction               *transaction,
+                                                   DBusConnection               *connection,
+-                                                  DBusMessage                  *message);
++                                                  DBusMessage                  *message,
++                                                  dbus_bool_t                   deferred_dispatch);
+ dbus_bool_t     bus_transaction_send_from_driver (BusTransaction               *transaction,
+                                                   DBusConnection               *connection,
+                                                   DBusMessage                  *message);
+diff --git a/bus/dispatch.c b/bus/dispatch.c
+index 6b0eadc..9972e76 100644
+--- a/bus/dispatch.c
++++ b/bus/dispatch.c
+@@ -33,8 +33,10 @@
+ #include "utils.h"
+ #include "bus.h"
+ #include "signals.h"
++#include "dispatch.h"
+ #include "test.h"
+ #include <dbus/dbus-internals.h>
++#include <dbus/dbus-connection-internal.h>
+ #include <dbus/dbus-misc.h>
+ #include <string.h>
+@@ -63,16 +65,26 @@ send_one_message (DBusConnection *connection,
+   result = bus_context_check_security_policy (context, transaction, sender, addressed_recipient,
+       connection, message, NULL, &deferred_message);
+-  if (result != BUS_RESULT_TRUE)
++  if (result == BUS_RESULT_FALSE)
+       return TRUE; /* silently don't send it */
+   if (dbus_message_contains_unix_fds(message) &&
+       !dbus_connection_can_send_type(connection, DBUS_TYPE_UNIX_FD))
+     return TRUE; /* silently don't send it */
++  if (result == BUS_RESULT_LATER)
++    {
++      if (!bus_deferred_message_queue_at_recipient(deferred_message, transaction, FALSE, FALSE))
++        {
++          BUS_SET_OOM (error);
++          return FALSE;
++        }
++      return TRUE; /* pretend to have sent it */
++    }
++
+   if (!bus_transaction_send (transaction,
+                              connection,
+-                             message))
++                             message, FALSE))
+     {
+       BUS_SET_OOM (error);
+       return FALSE;
+@@ -82,11 +94,12 @@ send_one_message (DBusConnection *connection,
+ }
+ BusResult
+-bus_dispatch_matches (BusTransaction *transaction,
+-                      DBusConnection *sender,
+-                      DBusConnection *addressed_recipient,
+-                      DBusMessage    *message,
+-                      DBusError      *error)
++bus_dispatch_matches (BusTransaction     *transaction,
++                      DBusConnection     *sender,
++                      DBusConnection     *addressed_recipient,
++                      DBusMessage        *message,
++                      BusDeferredMessage *dispatched_deferred_message,
++                      DBusError          *error)
+ {
+   DBusError tmp_error;
+   BusConnections *connections;
+@@ -110,17 +123,78 @@ bus_dispatch_matches (BusTransaction *transaction,
+   /* First, send the message to the addressed_recipient, if there is one. */
+   if (addressed_recipient != NULL)
+     {
+-      BusResult res;
+-      res = bus_context_check_security_policy (context, transaction,
+-                                               sender, addressed_recipient,
+-                                               addressed_recipient,
+-                                               message, error,
+-                                               &deferred_message);
+-      if (res == BUS_RESULT_FALSE)
++      BusResult result;
++      /* To maintain message order message needs to be appended at the recipient if there are already
++       *  deferred messages and we are not doing deferred dispatch
++       */
++      if (dispatched_deferred_message == NULL && bus_connection_has_deferred_messages(addressed_recipient))
++        {
++          deferred_message = bus_deferred_message_new(message, sender,
++              addressed_recipient, addressed_recipient, BUS_RESULT_LATER);
++
++          if (deferred_message == NULL)
++            {
++              BUS_SET_OOM(error);
++              return BUS_RESULT_FALSE;
++            }
++
++          if (!bus_deferred_message_queue_at_recipient(deferred_message, transaction, TRUE, FALSE))
++            {
++              bus_deferred_message_unref(deferred_message);
++              BUS_SET_OOM(error);
++              return BUS_RESULT_FALSE;
++            }
++
++          bus_deferred_message_unref(deferred_message);
++          return BUS_RESULT_TRUE; /* pretend to have sent it */
++        }
++
++      if (dispatched_deferred_message != NULL)
++        {
++          result = bus_deferred_message_get_response(dispatched_deferred_message);
++          if (result == BUS_RESULT_TRUE)
++            {
++              /* if we know the result of policy check we still need to check if message limits
++               * are not exceeded. It is also required to add entry in expected replies list if
++               * this is a method call
++               */
++              if (!bus_deferred_message_check_message_limits(dispatched_deferred_message, error))
++                return BUS_RESULT_FALSE;
++
++              if (!bus_deferred_message_expect_method_reply(dispatched_deferred_message, transaction, error))
++                return BUS_RESULT_FALSE;
++            }
++          else if (result == BUS_RESULT_FALSE)
++            {
++              bus_deferred_message_create_error(dispatched_deferred_message, "Rejected message", error);
++              return BUS_RESULT_FALSE;
++            }
++        }
++      else
++        result = BUS_RESULT_LATER;
++
++      if (result == BUS_RESULT_LATER)
++        result = bus_context_check_security_policy(context, transaction,
++            sender, addressed_recipient, addressed_recipient, message, error,
++            &deferred_message);
++
++      if (result == BUS_RESULT_FALSE)
+         return BUS_RESULT_FALSE;
+-      else if (res == BUS_RESULT_LATER)
++      else if (result == BUS_RESULT_LATER)
+         {
+           BusDeferredMessageStatus status;
++
++          if (dispatched_deferred_message != NULL)
++            {
++              /* for deferred dispatch prepend message at the recipient */
++              if (!bus_deferred_message_queue_at_recipient(deferred_message, transaction, TRUE, TRUE))
++                {
++                  BUS_SET_OOM(error);
++                  return BUS_RESULT_FALSE;
++                }
++              return BUS_RESULT_TRUE; /* pretend to have sent it */
++            }
++
+           status = bus_deferred_message_get_status(deferred_message);
+           if (status & BUS_DEFERRED_MESSAGE_CHECK_SEND)
+@@ -131,13 +205,18 @@ bus_dispatch_matches (BusTransaction *transaction,
+             }
+           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;
++              /* receive rule result not available - queue message at the recipient */
++              if (!bus_deferred_message_queue_at_recipient(deferred_message, transaction, TRUE, FALSE))
++                {
++                  BUS_SET_OOM(error);
++                  return BUS_RESULT_FALSE;
++                }
++
++              return BUS_RESULT_TRUE; /* pretend to have sent it */
+             }
+           else
+             {
+-              _dbus_verbose("deferred message has no status field set to send or receive unexpectedly\n");
++              _dbus_verbose("deferred message has no status field set unexpectedly\n");
+               return BUS_RESULT_FALSE;
+             }
+         }
+@@ -154,7 +233,8 @@ bus_dispatch_matches (BusTransaction *transaction,
+         }
+       /* Dispatch the message */
+-      if (!bus_transaction_send (transaction, addressed_recipient, message))
++      if (!bus_transaction_send(transaction, addressed_recipient, message,
++          dispatched_deferred_message != NULL ? TRUE : FALSE))
+         {
+           BUS_SET_OOM (error);
+           return BUS_RESULT_FALSE;
+@@ -385,7 +465,7 @@ bus_dispatch (DBusConnection *connection,
+    * match rules.
+    */
+   if (BUS_RESULT_LATER == bus_dispatch_matches (transaction, connection, addressed_recipient,
+-                                                message, &error))
++                                                message, NULL, &error))
+     {
+       /* Roll back and dispatch the message once the policy result is available */
+       bus_transaction_cancel_and_free (transaction);
+diff --git a/bus/dispatch.h b/bus/dispatch.h
+index afba6a2..f6102e8 100644
+--- a/bus/dispatch.h
++++ b/bus/dispatch.h
+@@ -29,10 +29,11 @@
+ dbus_bool_t bus_dispatch_add_connection    (DBusConnection *connection);
+ void        bus_dispatch_remove_connection (DBusConnection *connection);
+-BusResult   bus_dispatch_matches           (BusTransaction *transaction,
+-                                            DBusConnection *sender,
+-                                            DBusConnection *recipient,
+-                                            DBusMessage    *message,
+-                                            DBusError      *error);
++BusResult   bus_dispatch_matches           (BusTransaction     *transaction,
++                                            DBusConnection     *sender,
++                                            DBusConnection     *recipient,
++                                            DBusMessage        *message,
++                                            BusDeferredMessage *dispatched_deferred_message,
++                                            DBusError           *error);
+ #endif /* BUS_DISPATCH_H */
+diff --git a/bus/driver.c b/bus/driver.c
+index 4dbce3d..2fb1385 100644
+--- a/bus/driver.c
++++ b/bus/driver.c
+@@ -130,7 +130,7 @@ bus_driver_send_service_owner_changed (const char     *service_name,
+   _dbus_assert (dbus_message_has_signature (message, "sss"));
+-  res = bus_dispatch_matches (transaction, NULL, NULL, message, error);
++  res = bus_dispatch_matches (transaction, NULL, NULL, message, NULL, error);
+   if (res == BUS_RESULT_TRUE)
+     retval = TRUE;
+   else if (res == BUS_RESULT_FALSE)
+diff --git a/bus/policy.c b/bus/policy.c
+index ec888df..448147f 100644
+--- a/bus/policy.c
++++ b/bus/policy.c
+@@ -1071,6 +1071,9 @@ bus_client_policy_check_can_send (DBusConnection      *sender,
+       result = bus_check_privilege(check, message, sender, addressed_recipient, receiver,
+           privilege, BUS_DEFERRED_MESSAGE_CHECK_SEND, deferred_message);
++      if (result == BUS_RESULT_LATER && deferred_message != NULL)
++        bus_deferred_message_set_policy_check_info(*deferred_message, requested_reply,
++            *toggles, privilege);
+     }
+   else
+     privilege = NULL;
+@@ -1305,6 +1308,9 @@ bus_client_policy_check_can_receive (BusClientPolicy     *policy,
+       result = bus_check_privilege(check, message, sender, addressed_recipient, proposed_recipient,
+                  privilege, BUS_DEFERRED_MESSAGE_CHECK_RECEIVE, deferred_message);
++      if (result == BUS_RESULT_LATER && deferred_message != NULL)
++        bus_deferred_message_set_policy_check_info(*deferred_message, requested_reply,
++                    *toggles, privilege);
+     }
+   else
+       privilege = NULL;
+-- 
+2.1.4
+