Handle receive rule result unavailability and message broadcasts
authorJacek Bukarewicz <j.bukarewicz@samsung.com>
Fri, 28 Nov 2014 11:39:33 +0000 (12:39 +0100)
committerAdrian Szyndela <adrian.s@samsung.com>
Fri, 19 Feb 2016 10:53:57 +0000 (11:53 +0100)
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
bus/bus.c
bus/bus.h
bus/check.c
bus/check.h
bus/connection.c
bus/connection.h
bus/dispatch.c
bus/dispatch.h
bus/driver.c
bus/policy.c

index 59d3bd4..6378af9 100644 (file)
@@ -1197,7 +1197,7 @@ bus_activation_send_pending_auto_activation_messages (BusActivation  *activation
           switch (bus_dispatch_matches (transaction,
                                         entry->connection,
                                         addressed_recipient,
-                                        entry->activation_message, error))
+                                        entry->activation_message, NULL, &error))
             {
             case BUS_RESULT_TRUE:
               break;
@@ -1240,7 +1240,7 @@ bus_activation_send_pending_auto_activation_messages (BusActivation  *activation
                     if (putback_message->connection == entry->connection)
                       {
                         if (!_dbus_connection_putback_message (putback_message->connection, last_inserted_message,
-                              putback_message->activation_message, error))
+                              putback_message->activation_message, &error))
                           goto error;
                         last_inserted_message = putback_message->activation_message;
                         putback_message->is_put_back = TRUE;
@@ -2076,7 +2076,7 @@ bus_activation_activate_service (BusActivation  *activation,
               /* Wonderful, systemd is connected, let's just send the msg */
               switch (bus_dispatch_matches (activation_transaction, NULL,
                                             bus_service_get_primary_owners_connection (service),
-                                            message, error))
+                                            message, NULL, error))
                 {
                 case BUS_RESULT_TRUE:
                   retval = TRUE;
index f97dda3..144c0df 100644 (file)
--- a/bus/bus.c
+++ b/bus/bus.c
@@ -1788,17 +1788,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
@@ -1853,3 +1845,42 @@ 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;
+}
index 4e1ef6b..95ca9a8 100644 (file)
--- a/bus/bus.h
+++ b/bus/bus.h
@@ -161,4 +161,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 */
index a5dbaff..baff981 100644 (file)
@@ -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;
@@ -122,6 +125,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;
+}
+
 void
 bus_deferred_message_disable_sender (BusDeferredMessage *deferred_message)
 {
@@ -132,6 +218,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);
@@ -196,6 +296,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;
@@ -232,12 +335,219 @@ 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;
+      switch (result)
+        {
+        case 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;
+          break;
+        case BUS_RESULT_FALSE:
+          break;
+        case 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)
@@ -247,3 +557,4 @@ bus_deferred_message_response_received (BusDeferredMessage *deferred_message,
       deferred_message->response_callback(deferred_message, result);
     }
 }
+
index f381789..3c6b2a1 100644 (file)
@@ -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);
index 418a688..f9fa9aa 100644 (file)
 #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 */
@@ -2238,7 +2243,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;
     }
 
@@ -2290,6 +2295,7 @@ bus_transaction_send_from_driver (BusTransaction *transaction,
                                   DBusMessage    *message)
 {
   DBusError error = DBUS_ERROR_INIT;
+  BusDeferredMessage *deferred_message;
 
   /* We have to set the sender to the driver, and have
    * to check security policy since it was not done in
@@ -2330,7 +2336,7 @@ bus_transaction_send_from_driver (BusTransaction *transaction,
   switch (bus_context_check_security_policy (bus_transaction_get_context (transaction),
                                              transaction,
                                              NULL, connection, connection, message, &error,
-                                             NULL))
+                                             &deferred_message))
     {
     case BUS_RESULT_TRUE:
       break;
@@ -2348,18 +2354,19 @@ bus_transaction_send_from_driver (BusTransaction *transaction,
       return TRUE;
       break;
     case BUS_RESULT_LATER:
-      _dbus_verbose ("Cannot delay sending message from bus driver, dropping it\n");
-      return TRUE;
-      break;
+      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;
@@ -2385,7 +2392,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)
     {
@@ -2637,6 +2665,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)
 {
index a6e5dfd..46e883e 100644 (file)
@@ -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);
index 90cc30b..d42b1c0 100644 (file)
 #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>
 
@@ -71,7 +73,7 @@ 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)
     {
       if (!bus_transaction_capture_error_reply (transaction, &stack_error,
                                                 message))
@@ -106,9 +108,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;
@@ -118,11 +130,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;
@@ -146,39 +159,107 @@ bus_dispatch_matches (BusTransaction *transaction,
   /* First, send the message to the addressed_recipient, if there is one. */
   if (addressed_recipient != NULL)
     {
-      switch (bus_context_check_security_policy (context, transaction,
-                                                 sender, addressed_recipient,
-                                                 addressed_recipient,
-                                                 message, error,
-                                                 &deferred_message))
+      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))
         {
-          case BUS_RESULT_TRUE:
-            break;
-          case BUS_RESULT_FALSE:
-            return BUS_RESULT_FALSE;
-          case BUS_RESULT_LATER:
+          deferred_message = bus_deferred_message_new(message, sender,
+              addressed_recipient, addressed_recipient, BUS_RESULT_LATER);
+
+          if (deferred_message == NULL)
             {
-              BusDeferredMessageStatus status;
-              status = bus_deferred_message_get_status(deferred_message);
+              BUS_SET_OOM(error);
+              return BUS_RESULT_FALSE;
+            }
 
-              if (status & BUS_DEFERRED_MESSAGE_CHECK_SEND)
-                {
-                  /* send rule result not available - disable dispatching messages from the sender */
-                  bus_deferred_message_disable_sender(deferred_message);
-                  return BUS_RESULT_LATER;
-                }
-              else if (status & BUS_DEFERRED_MESSAGE_CHECK_RECEIVE)
-                {
-                   dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
-                              "Rejecting message because time is needed to check security policy");
-                   return BUS_RESULT_FALSE;
-                }
-              else
-                {
-                  _dbus_verbose("deferred message has no status field set to send or receive unexpectedly\n");
-                  return BUS_RESULT_FALSE;
-                }
+          if (!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);
+
+      switch (result)
+        {
+        case BUS_RESULT_TRUE:
+          break;
+        case BUS_RESULT_FALSE:
+          return BUS_RESULT_FALSE;
+        case 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)
+              {
+                /* send rule result not available - disable dispatching messages from the sender */
+                bus_deferred_message_disable_sender(deferred_message);
+                return BUS_RESULT_LATER;
+              }
+            else if (status & BUS_DEFERRED_MESSAGE_CHECK_RECEIVE)
+              {
+                /* 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 unexpectedly\n");
+                return BUS_RESULT_FALSE;
             }
+          }
         }
 
       if (dbus_message_contains_unix_fds (message) &&
@@ -193,7 +274,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;
@@ -492,7 +574,7 @@ bus_dispatch (DBusConnection *connection,
    * addressed_recipient == NULL), and match it against other connections'
    * match rules.
    */
-  switch (bus_dispatch_matches (transaction, connection, addressed_recipient, message, &error))
+  switch (bus_dispatch_matches (transaction, connection, addressed_recipient, message, NULL, &error))
     {
     case BUS_RESULT_TRUE:
     case BUS_RESULT_FALSE:
index afba6a2..f6102e8 100644 (file)
 
 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 */
index e92d77b..240f5ac 100644 (file)
@@ -261,7 +261,7 @@ bus_driver_send_service_owner_changed (const char     *service_name,
   if (!bus_transaction_capture (transaction, NULL, message))
     goto oom;
 
-  switch (bus_dispatch_matches (transaction, NULL, NULL, message, error))
+  switch (bus_dispatch_matches (transaction, NULL, NULL, message, NULL, error))
     {
     case BUS_RESULT_TRUE:
       retval = TRUE;
index 825d754..914b588 100644 (file)
@@ -1196,6 +1196,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;
@@ -1430,6 +1433,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;