summaryrefslogtreecommitdiffstats
path: root/meta-app-framework/recipes-core/dbus-cynagora/dbus-cynagora/0003-Handle-unavailability-of-policy-results-for-broadcas.patch
diff options
context:
space:
mode:
authorJan-Simon Moeller <jsmoeller@linuxfoundation.org>2020-12-08 11:12:45 +0100
committerJan-Simon Moeller <jsmoeller@linuxfoundation.org>2020-12-17 13:59:52 +0000
commit1c3c06842ac1b9c089d0a08e91c60f44e4844fac (patch)
tree21e97368be8f78a3e76b66dfda24c1d5e774519f /meta-app-framework/recipes-core/dbus-cynagora/dbus-cynagora/0003-Handle-unavailability-of-policy-results-for-broadcas.patch
parentc1e048fc05542d859115990312e0753ce2dea72e (diff)
SPEC-3723: restructure meta-agl
Goal is to reach a minimal meta-agl-core as base for IVI and IC work at the same time. Trim dependencies and move most 'demo' related recipes to meta-agl-demo. v2: changed to bbapend + .inc , added description v3: testbuild of all images v4: restore -test packagegroup and -qa images, compare manifests and adapt packagegroups. v5: rebased v6: merged meta-agl-distro into meta-agl-core, due to dependency on meta-oe, moved -test packagegroup and -qa images to own layer meta-agl-core-test v7: Fixed comments from Paul Barker v8: Update the markdown files v9: restore wayland/weston/agl-compositor recipes/appends, reworked to move app f/w specific changes to bbappends in meta-app-framework and only demo specific weston-init changes to meta-agl-demo v10: fix s/agldemo/aglcore/ missed in weston-init.bbappend Description: This patch is part 1 out of 2 large patches that implement the layer rework discussed during the previous workshop. Essentially meta-agl-core is the small but versatile new core layer of AGL serving as basis for the work done by the IC and IVI EGs. All demo related work is moved to meta-agl-demo in the 2nd patchset. This should be applied together as atomic change. The resulting meta-agl/* follows these guidelines: - only bsp adaptations in meta-agl-bsp - remove the agl-profile-* layers for simplicity -- the packagegroup-agl(-profile)-graphical and so on have been kept in meta-agl-demo - meta-agl-profile-core is now meta-agl-core - meta-agl-core does pass yocto-check-layer -- therefore use the bbappend + conditional + .inc file construct found in meta-virtualization - meta-agl/meta-security has been merged into meta-agl/meta-app-framework - meta-netboot does pass yocto-check-layer - meta-pipewire does pass yocto-check-layer Migration: All packagegroups are preserved but they're now enabled by 'agl-demo'. Bug-AGL: SPEC-3723 Signed-off-by: Jan-Simon Moeller <jsmoeller@linuxfoundation.org> Signed-off-by: Scott Murray <scott.murray@konsulko.com> Change-Id: Ia6c6e5e6ce2b4ffa69ea94959cdc57c310ba7c53 Reviewed-on: https://gerrit.automotivelinux.org/gerrit/c/AGL/meta-agl/+/25769
Diffstat (limited to 'meta-app-framework/recipes-core/dbus-cynagora/dbus-cynagora/0003-Handle-unavailability-of-policy-results-for-broadcas.patch')
-rw-r--r--meta-app-framework/recipes-core/dbus-cynagora/dbus-cynagora/0003-Handle-unavailability-of-policy-results-for-broadcas.patch1095
1 files changed, 1095 insertions, 0 deletions
diff --git a/meta-app-framework/recipes-core/dbus-cynagora/dbus-cynagora/0003-Handle-unavailability-of-policy-results-for-broadcas.patch b/meta-app-framework/recipes-core/dbus-cynagora/dbus-cynagora/0003-Handle-unavailability-of-policy-results-for-broadcas.patch
new file mode 100644
index 000000000..7d89a7496
--- /dev/null
+++ b/meta-app-framework/recipes-core/dbus-cynagora/dbus-cynagora/0003-Handle-unavailability-of-policy-results-for-broadcas.patch
@@ -0,0 +1,1095 @@
+From 9d39aa9dd55680529d721a0389ce9ef579bb669a 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/8] 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.
+
+Cherry picked from 1e231194610892dd4360224998d91336097b05a1 by Jose Bollo
+
+Updated for dbus 1.10.20 by Scott Murray and José Bollo
+
+Signed-off-by: José Bollo <jose.bollo@iot.bzh>
+Signed-off-by: Scott Murray <scott.murray@konsulko.com>
+---
+ bus/activation.c | 4 +-
+ bus/bus.c | 50 ++++++--
+ bus/bus.h | 19 +++
+ bus/check.c | 307 +++++++++++++++++++++++++++++++++++++++++++++++
+ bus/check.h | 25 ++++
+ bus/connection.c | 168 ++++++++++++++++++++++++--
+ bus/connection.h | 19 ++-
+ bus/dispatch.c | 115 +++++++++++++++---
+ bus/dispatch.h | 11 +-
+ bus/driver.c | 2 +-
+ bus/policy.c | 6 +
+ 11 files changed, 683 insertions(+), 43 deletions(-)
+
+diff --git a/bus/activation.c b/bus/activation.c
+index 8301b59..d4b597c 100644
+--- a/bus/activation.c
++++ b/bus/activation.c
+@@ -1259,7 +1259,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
+@@ -2140,7 +2140,7 @@ bus_activation_activate_service (BusActivation *activation,
+ bus_connection_get_loginfo (connection));
+ /* Wonderful, systemd is connected, let's just send the msg */
+ res = bus_dispatch_matches (activation_transaction, NULL,
+- systemd, message, error);
++ systemd, message, NULL, error);
+
+ if (res == BUS_RESULT_TRUE)
+ retval = TRUE;
+diff --git a/bus/bus.c b/bus/bus.c
+index 6fc45d0..0aa700b 100644
+--- a/bus/bus.c
++++ b/bus/bus.c
+@@ -1800,17 +1800,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
+@@ -1869,3 +1861,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 82c32c8..1b08f7c 100644
+--- a/bus/bus.h
++++ b/bus/bus.h
+@@ -164,4 +164,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 4b8a699..f3d283f 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, 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 d177549..9c13c18 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 b348d42..ee93384 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 */
+@@ -2307,7 +2312,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;
+ }
+
+@@ -2361,6 +2366,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
+@@ -2401,7 +2407,7 @@ bus_transaction_send_from_driver (BusTransaction *transaction,
+ res = bus_context_check_security_policy (bus_transaction_get_context (transaction),
+ transaction,
+ NULL, connection, connection, message, NULL,
+- &error, NULL);
++ &error, &deferred_message);
+ if (res == BUS_RESULT_FALSE)
+ {
+ if (!bus_transaction_capture_error_reply (transaction, connection,
+@@ -2419,18 +2425,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;
+@@ -2456,7 +2464,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)
+ {
+@@ -2708,6 +2737,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 71078ea..97dae96 100644
+--- a/bus/connection.h
++++ b/bus/connection.h
+@@ -85,6 +85,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);
+@@ -137,7 +153,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,
+ DBusConnection *addressed_recipient,
+diff --git a/bus/dispatch.c b/bus/dispatch.c
+index 50a22a3..7d30ce4 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>
+@@ -77,7 +78,7 @@ send_one_message (DBusConnection *connection,
+ NULL,
+ &stack_error,
+ &deferred_message);
+- if (result != BUS_RESULT_TRUE)
++ if (result == BUS_RESULT_FALSE)
+ {
+ if (!bus_transaction_capture_error_reply (transaction, sender,
+ &stack_error, message))
+@@ -112,9 +113,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;
+@@ -124,11 +135,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;
+@@ -137,7 +149,6 @@ bus_dispatch_matches (BusTransaction *transaction,
+ DBusList *link;
+ BusContext *context;
+ BusDeferredMessage *deferred_message;
+- BusResult res;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+@@ -153,16 +164,80 @@ bus_dispatch_matches (BusTransaction *transaction,
+ /* First, send the message to the addressed_recipient, if there is one. */
+ if (addressed_recipient != NULL)
+ {
+- res = bus_context_check_security_policy (context, transaction,
++ 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, NULL, error,
+ &deferred_message);
+- if (res == BUS_RESULT_FALSE)
++
++ 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)
+@@ -173,13 +248,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;
+ }
+ }
+@@ -196,7 +276,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;
+@@ -534,7 +615,7 @@ bus_dispatch (DBusConnection *connection,
+ * match rules.
+ */
+ if (BUS_RESULT_LATER == bus_dispatch_matches (transaction, connection, addressed_recipient,
+- message, &error))
++ message, NULL, &error))
+ {
+ /* Roll back and dispatch the message once the policy result is available */
+ bus_transaction_cancel_and_free (transaction);
+diff --git a/bus/dispatch.h b/bus/dispatch.h
+index afba6a2..f6102e8 100644
+--- a/bus/dispatch.h
++++ b/bus/dispatch.h
+@@ -29,10 +29,11 @@
+
+ dbus_bool_t bus_dispatch_add_connection (DBusConnection *connection);
+ void bus_dispatch_remove_connection (DBusConnection *connection);
+-BusResult bus_dispatch_matches (BusTransaction *transaction,
+- DBusConnection *sender,
+- DBusConnection *recipient,
+- DBusMessage *message,
+- DBusError *error);
++BusResult bus_dispatch_matches (BusTransaction *transaction,
++ DBusConnection *sender,
++ DBusConnection *recipient,
++ DBusMessage *message,
++ BusDeferredMessage *dispatched_deferred_message,
++ DBusError *error);
+
+ #endif /* BUS_DISPATCH_H */
+diff --git a/bus/driver.c b/bus/driver.c
+index f414f64..d89a658 100644
+--- a/bus/driver.c
++++ b/bus/driver.c
+@@ -254,7 +254,7 @@ bus_driver_send_service_owner_changed (const char *service_name,
+ if (!bus_transaction_capture (transaction, NULL, 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 7de92c6..483cc97 100644
+--- a/bus/policy.c
++++ b/bus/policy.c
+@@ -1122,6 +1122,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;
+@@ -1372,6 +1375,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.21.1
+