dbus-cynara: Upgrade to 1.10.20
[AGL/meta-agl.git] / meta-security / recipes-core / dbus-cynara / dbus / 0003-Handle-unavailability-of-policy-results-for-broadcas.patch
diff --git a/meta-security/recipes-core/dbus-cynara/dbus/0003-Handle-unavailability-of-policy-results-for-broadcas.patch b/meta-security/recipes-core/dbus-cynara/dbus/0003-Handle-unavailability-of-policy-results-for-broadcas.patch
new file mode 100644 (file)
index 0000000..b797064
--- /dev/null
@@ -0,0 +1,1090 @@
+From 8c5fd05f7b2f14ac0f4423cae300f60c6bb51c74 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 3/5] Handle unavailability of policy results for broadcasts
+ and receive rules
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+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
+
+Cherry picked from 1e231194610892dd4360224998d91336097b05a1 by Jose Bollo
+
+Signed-off-by: José Bollo <jose.bollo@iot.bzh>
+---
+ bus/activation.c |   4 +-
+ bus/bus.c        |  50 +++++++--
+ bus/bus.h        |  19 ++++
+ bus/check.c      | 307 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ bus/check.h      |  25 +++++
+ bus/connection.c | 169 ++++++++++++++++++++++++++++--
+ bus/connection.h |  19 +++-
+ bus/dispatch.c   | 121 ++++++++++++++++++----
+ bus/dispatch.h   |  11 +-
+ bus/driver.c     |   2 +-
+ bus/policy.c     |   6 ++
+ 11 files changed, 686 insertions(+), 47 deletions(-)
+
+diff --git a/bus/activation.c b/bus/activation.c
+index 343d3f22..11bd8386 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
+@@ -2085,7 +2085,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 c4008505..911e2340 100644
+--- a/bus/bus.c
++++ b/bus/bus.c
+@@ -1796,17 +1796,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
+@@ -1861,3 +1853,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 dab7791f..445165c9 100644
+--- a/bus/bus.h
++++ b/bus/bus.h
+@@ -158,4 +158,23 @@ BusResult         bus_context_check_security_policy              (BusContext
+                                                                   BusDeferredMessage **deferred_message);
+ void              bus_context_check_all_watches                  (BusContext       *context);
++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 4b8a6994..b8833349 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;
+@@ -135,6 +138,89 @@ bus_check_enable_dispatch_callback (BusDeferredMessage *deferred_message,
+   _dbus_connection_enable_dispatch(deferred_message->sender);
+ }
++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)
+ {
+@@ -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
+ BusResult (*bus_check_test_override) (DBusConnection *connection,
+                                         const char *privilege);
+@@ -259,6 +359,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;
+@@ -295,12 +398,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)
+@@ -310,3 +616,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 d1775497..9c13c184 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 BusResult (*bus_check_test_override) (DBusConnection *connection,
+                                                const char *privilege);
+diff --git a/bus/connection.c b/bus/connection.c
+index eea50ecd..1c0bdffb 100644
+--- a/bus/connection.c
++++ b/bus/connection.c
+@@ -31,11 +31,13 @@
+ #include "expirelist.h"
+ #include "selinux.h"
+ #include "apparmor.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-internals.h>
++#include <dbus/dbus-message-internal.h>
+ #ifdef DBUS_ENABLE_CYNARA
+ #include <stdlib.h>
+ #include <cynara-session.h>
+@@ -102,6 +104,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;
+@@ -268,6 +271,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 */
+@@ -2264,7 +2269,7 @@ bus_transaction_capture (BusTransaction *transaction,
+     {
+       DBusConnection *recipient = link->data;
+-      if (!bus_transaction_send (transaction, recipient, message))
++      if (!bus_transaction_send (transaction, recipient, message, FALSE))
+         goto out;
+     }
+@@ -2317,6 +2322,7 @@ bus_transaction_send_from_driver (BusTransaction *transaction,
+ {
+   DBusError error = DBUS_ERROR_INIT;
+   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
+@@ -2357,7 +2363,8 @@ bus_transaction_send_from_driver (BusTransaction *transaction,
+   res = bus_context_check_security_policy (bus_transaction_get_context (transaction),
+                                            transaction,
+                                            NULL, connection, connection, message, &error,
+-                                           NULL);
++                                           &deferred_message);
++
+   if (res == BUS_RESULT_FALSE)
+     {
+       if (!bus_transaction_capture_error_reply (transaction, &error, message))
+@@ -2374,18 +2381,20 @@ bus_transaction_send_from_driver (BusTransaction *transaction,
+     }
+   else if (res == BUS_RESULT_LATER)
+     {
+-      _dbus_verbose ("Cannot delay sending message from bus driver, dropping it\n");
+       dbus_error_free (&error);
+-      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;
+@@ -2411,7 +2420,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)
+     {
+@@ -2663,6 +2693,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 a6e5dfde..46e883e6 100644
+--- a/bus/connection.h
++++ b/bus/connection.h
+@@ -83,6 +83,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);
+@@ -135,7 +151,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_capture          (BusTransaction               *transaction,
+                                                   DBusConnection               *connection,
+                                                   DBusMessage                  *message);
+diff --git a/bus/dispatch.c b/bus/dispatch.c
+index 7353501b..e32c9263 100644
+--- a/bus/dispatch.c
++++ b/bus/dispatch.c
+@@ -33,6 +33,7 @@
+ #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>
+@@ -76,7 +77,7 @@ send_one_message (DBusConnection *connection,
+                                           message,
+                                           &stack_error,
+                                           &deferred_message);
+-  if (result != BUS_RESULT_TRUE)
++  if (result == BUS_RESULT_FALSE)
+     {
+       if (!bus_transaction_capture_error_reply (transaction, &stack_error,
+                                                 message))
+@@ -111,9 +112,19 @@ send_one_message (DBusConnection *connection,
+       return TRUE; /* don't send it but don't return an error either */
+     }
++  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;
+@@ -123,11 +134,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;
+@@ -151,17 +163,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)
+@@ -172,13 +245,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;
+             }
+         }
+@@ -195,7 +273,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;
+@@ -495,7 +574,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 afba6a24..f6102e80 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 a5823d4d..5acdd62a 100644
+--- a/bus/driver.c
++++ b/bus/driver.c
+@@ -261,7 +261,7 @@ bus_driver_send_service_owner_changed (const char     *service_name,
+   if (!bus_transaction_capture (transaction, NULL, message))
+     goto oom;
+-  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
+diff --git a/bus/policy.c b/bus/policy.c
+index bcade176..47bd1a24 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.14.3
+