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