Upgrade to thud
[AGL/meta-agl.git] / meta-security / recipes-core / dbus-cynara / dbus-cynara / 0002-Disable-message-dispatching-when-send-rule-result-is.patch
1 From 9bea6ec0497391b6af41daca18d7868af2656cef Mon Sep 17 00:00:00 2001
2 From: Jacek Bukarewicz <j.bukarewicz@samsung.com>
3 Date: Fri, 28 Nov 2014 12:07:39 +0100
4 Subject: [PATCH 2/5] Disable message dispatching when send rule result is not
5  known
6 MIME-Version: 1.0
7 Content-Type: text/plain; charset=UTF-8
8 Content-Transfer-Encoding: 8bit
9
10 When unicast message is sent to addressed recipient and policy result
11 is not available message dispatch from the sender is disabled.
12 This also means that any further messages from the given connection are
13 put into the incoming queue without being processed. If response is received
14 message dispatching is resumed. This time answer is attached to the message
15 which is now processed synchronously.
16 Receive rule result unavailability is not yet handled - such messages are
17 rejected. Also, if message is sent to non-addressed recipient and policy result
18 is unknown, message is silently dropped.
19
20 Change-Id: I57eccbf973525fd51369c7d4e58908292f44da80
21
22 Cherry-picked from b1b87ad9f20b2052c28431b48e81073078a745ce
23 by Jose Bollo.
24
25 Updated for dbus 1.12.10 by Scott Murray.
26
27 Signed-off-by: José Bollo <jose.bollo@iot.bzh>
28 Signed-off-by: Scott Murray <scott.murray@konsulko.com>
29
30 diff --git a/bus/activation.c b/bus/activation.c
31 index 451179d..5f02153 100644
32 --- a/bus/activation.c
33 +++ b/bus/activation.c
34 @@ -32,6 +32,7 @@
35  #include "services.h"
36  #include "test.h"
37  #include "utils.h"
38 +#include <dbus/dbus-connection-internal.h>
39  #include <dbus/dbus-internals.h>
40  #include <dbus/dbus-hash.h>
41  #include <dbus/dbus-list.h>
42 @@ -94,6 +95,8 @@ struct BusPendingActivationEntry
43    DBusConnection *connection;
44  
45    dbus_bool_t auto_activation;
46 +
47 +  dbus_bool_t is_put_back;
48  };
49  
50  typedef struct
51 @@ -1241,20 +1244,23 @@ bus_activation_send_pending_auto_activation_messages (BusActivation  *activation
52        BusPendingActivationEntry *entry = link->data;
53        DBusList *next = _dbus_list_get_next_link (&pending_activation->entries, link);
54  
55 -      if (entry->auto_activation && (entry->connection == NULL || dbus_connection_get_is_connected (entry->connection)))
56 +      if (entry->auto_activation && !entry->is_put_back &&
57 +          (entry->connection == NULL || dbus_connection_get_is_connected (entry->connection)))
58          {
59            DBusConnection *addressed_recipient;
60            DBusError error;
61 +          BusResult res;
62  
63            dbus_error_init (&error);
64  
65            addressed_recipient = bus_service_get_primary_owners_connection (service);
66  
67            /* Resume dispatching where we left off in bus_dispatch() */
68 -          if (!bus_dispatch_matches (transaction,
69 -                                     entry->connection,
70 -                                     addressed_recipient,
71 -                                     entry->activation_message, &error))
72 +          res = bus_dispatch_matches (transaction,
73 +                                      entry->connection,
74 +                                      addressed_recipient,
75 +                                      entry->activation_message, &error);
76 +          if (res == BUS_RESULT_FALSE)
77              {
78                /* If permission is denied, we just want to return the error
79                 * to the original method invoker; in particular, we don't
80 @@ -1266,9 +1272,40 @@ bus_activation_send_pending_auto_activation_messages (BusActivation  *activation
81                    bus_connection_send_oom_error (entry->connection,
82                                                   entry->activation_message);
83                  }
84 +            }
85 +          else if (res == BUS_RESULT_LATER)
86 +            {
87 +              DBusList *putback_message_link = link;
88 +              DBusMessage *last_inserted_message = NULL;
89 +
90 +              /* NULL entry->connection implies sending pending ActivationRequest message to systemd */
91 +              if (entry->connection == NULL)
92 +                {
93 +                  _dbus_assert_not_reached ("bus_dispatch_matches returned BUS_RESULT_LATER unexpectedly when sender is NULL");
94 +                  link = next;
95 +                  continue;
96 +                }
97  
98 -              link = next;
99 -              continue;
100 +              /**
101 +               * Getting here means that policy check result is not yet available and dispatching
102 +               * messages from entry->connection has been disabled.
103 +               * Let's put back all messages for the given connection in the incoming queue and mark
104 +               * this entry as put back so they are not handled twice.
105 +               */
106 +              while (putback_message_link != NULL)
107 +                {
108 +                  BusPendingActivationEntry *putback_message = putback_message_link->data;
109 +                  if (putback_message->connection == entry->connection)
110 +                    {
111 +                      if (!_dbus_connection_putback_message (putback_message->connection, last_inserted_message,
112 +                            putback_message->activation_message, &error))
113 +                        goto error;
114 +                      last_inserted_message = putback_message->activation_message;
115 +                      putback_message->is_put_back = TRUE;
116 +                    }
117 +
118 +                  putback_message_link = _dbus_list_get_next_link(&pending_activation->entries, putback_message_link);
119 +                }
120              }
121          }
122  
123 @@ -1286,6 +1323,19 @@ bus_activation_send_pending_auto_activation_messages (BusActivation  *activation
124    return TRUE;
125  
126   error:
127 +  /* remove all messages that have been put to connections' incoming queues */
128 +  link = _dbus_list_get_first_link (&pending_activation->entries);
129 +  while (link != NULL)
130 +    {
131 +      BusPendingActivationEntry *entry = link->data;
132 +      if (entry->is_put_back)
133 +        {
134 +          _dbus_connection_remove_message(entry->connection, entry->activation_message);
135 +          entry->is_put_back = FALSE;
136 +        }
137 +      link = _dbus_list_get_next_link(&pending_activation->entries, link);
138 +    }
139 +
140    return FALSE;
141  }
142  
143 @@ -2078,6 +2128,7 @@ bus_activation_activate_service (BusActivation  *activation,
144  
145            if (service != NULL)
146              {
147 +              BusResult res;
148                bus_context_log (activation->context,
149                                 DBUS_SYSTEM_LOG_INFO, "Activating via systemd: service name='%s' unit='%s' requested by '%s' (%s)",
150                                 service_name,
151 @@ -2085,8 +2136,17 @@ bus_activation_activate_service (BusActivation  *activation,
152                                 bus_connection_get_name (connection),
153                                 bus_connection_get_loginfo (connection));
154                /* Wonderful, systemd is connected, let's just send the msg */
155 -              retval = bus_dispatch_matches (activation_transaction, NULL,
156 -                                             systemd, message, error);
157 +              res = bus_dispatch_matches (activation_transaction, NULL,
158 +                                          systemd, message, error);
159 +
160 +              if (res == BUS_RESULT_TRUE)
161 +                retval = TRUE;
162 +              else
163 +                {
164 +                  retval = FALSE;
165 +                  if (res == BUS_RESULT_LATER)
166 +                    _dbus_verbose("Unexpectedly need time to check message from bus driver to systemd - dropping the message.\n");
167 +                }
168              }
169            else
170              {
171 diff --git a/bus/check.c b/bus/check.c
172 index 5b72d31..4b8a699 100644
173 --- a/bus/check.c
174 +++ b/bus/check.c
175 @@ -55,6 +55,8 @@ typedef struct BusDeferredMessage
176    BusCheckResponseFunc response_callback;
177  } BusDeferredMessage;
178  
179 +static dbus_int32_t deferred_message_data_slot = -1;
180 +
181  BusCheck *
182  bus_check_new (BusContext *context, DBusError *error)
183  {
184 @@ -67,11 +69,19 @@ bus_check_new (BusContext *context, DBusError *error)
185        return NULL;
186      }
187  
188 +  if (!dbus_message_allocate_data_slot(&deferred_message_data_slot))
189 +    {
190 +      dbus_free(check);
191 +      BUS_SET_OOM(error);
192 +      return NULL;
193 +    }
194 +
195    check->refcount = 1;
196    check->context = context;
197    check->cynara = bus_cynara_new(check, error);
198    if (dbus_error_is_set(error))
199      {
200 +      dbus_message_free_data_slot(&deferred_message_data_slot);
201        dbus_free(check);
202        return NULL;
203      }
204 @@ -98,6 +108,7 @@ bus_check_unref (BusCheck *check)
205    if (check->refcount == 0)
206      {
207        bus_cynara_unref(check->cynara);
208 +      dbus_message_free_data_slot(&deferred_message_data_slot);
209        dbus_free(check);
210      }
211  }
212 @@ -114,6 +125,45 @@ bus_check_get_cynara (BusCheck *check)
213    return check->cynara;
214  }
215  
216 +static void
217 +bus_check_enable_dispatch_callback (BusDeferredMessage *deferred_message,
218 +                                    BusResult result)
219 +{
220 +  _dbus_verbose("bus_check_enable_dispatch_callback called deferred_message=%p\n", deferred_message);
221 +
222 +  deferred_message->response = result;
223 +  _dbus_connection_enable_dispatch(deferred_message->sender);
224 +}
225 +
226 +static void
227 +deferred_message_free_function(void *data)
228 +{
229 +  BusDeferredMessage *deferred_message = (BusDeferredMessage *)data;
230 +  bus_deferred_message_unref(deferred_message);
231 +}
232 +
233 +void
234 +bus_deferred_message_disable_sender (BusDeferredMessage *deferred_message)
235 +{
236 +  _dbus_assert(deferred_message != NULL);
237 +  _dbus_assert(deferred_message->sender != NULL);
238 +
239 +  if (dbus_message_get_data(deferred_message->message, deferred_message_data_slot) == NULL)
240 +    {
241 +      if (dbus_message_set_data(deferred_message->message, deferred_message_data_slot, deferred_message,
242 +          deferred_message_free_function))
243 +        bus_deferred_message_ref(deferred_message);
244 +    }
245 +
246 +  _dbus_connection_disable_dispatch(deferred_message->sender);
247 +  deferred_message->response_callback = bus_check_enable_dispatch_callback;
248 +}
249 +
250 +#ifdef DBUS_ENABLE_EMBEDDED_TESTS
251 +BusResult (*bus_check_test_override) (DBusConnection *connection,
252 +                                        const char *privilege);
253 +#endif
254 +
255  BusResult
256  bus_check_privilege (BusCheck *check,
257                       DBusMessage *message,
258 @@ -124,6 +174,7 @@ bus_check_privilege (BusCheck *check,
259                       BusDeferredMessageStatus check_type,
260                       BusDeferredMessage **deferred_message)
261  {
262 +  BusDeferredMessage *previous_deferred_message;
263    BusResult result = BUS_RESULT_FALSE;
264  #ifdef DBUS_ENABLE_CYNARA
265    BusCynara *cynara;
266 @@ -137,16 +188,54 @@ bus_check_privilege (BusCheck *check,
267        return BUS_RESULT_FALSE;
268      }
269  
270 -  /* ask policy checkers */
271 -#ifdef DBUS_ENABLE_CYNARA
272 -  cynara = bus_check_get_cynara(check);
273 -  result = bus_cynara_check_privilege(cynara, message, sender, addressed_recipient,
274 -      proposed_recipient, privilege, check_type, deferred_message);
275 +#ifdef DBUS_ENABLE_EMBEDDED_TESTS
276 +  if (bus_check_test_override)
277 +    return bus_check_test_override (connection, privilege);
278  #endif
279  
280 -  if (result == BUS_RESULT_LATER && deferred_message != NULL)
281 +  previous_deferred_message = dbus_message_get_data(message, deferred_message_data_slot);
282 +  /* check if message blocked at sender's queue is being processed */
283 +  if (previous_deferred_message != NULL)
284 +    {
285 +      if ((check_type & BUS_DEFERRED_MESSAGE_CHECK_SEND) &&
286 +          !(previous_deferred_message->status & BUS_DEFERRED_MESSAGE_CHECK_SEND))
287 +        {
288 +          /**
289 +           * Message has been deferred due to receive or own rule which means that sending this message
290 +           * is allowed - it must have been checked previously.
291 +           * This might happen when client calls RequestName method which depending on security
292 +           * policy might result in both "can_send" and "can_own" Cynara checks.
293 +           */
294 +          result = BUS_RESULT_TRUE;
295 +        }
296 +      else
297 +        {
298 +          result = previous_deferred_message->response;
299 +          if (result == BUS_RESULT_LATER)
300 +            {
301 +              /* result is still not known - reuse deferred message object */
302 +              if (deferred_message != NULL)
303 +                *deferred_message = previous_deferred_message;
304 +            }
305 +          else
306 +            {
307 +              /* result is available - we can remove deferred message from the processed message */
308 +              dbus_message_set_data(message, deferred_message_data_slot, NULL, NULL);
309 +            }
310 +        }
311 +    }
312 +  else
313      {
314 -      (*deferred_message)->status |= check_type;
315 +      /* ask policy checkers */
316 +#ifdef DBUS_ENABLE_CYNARA
317 +      cynara = bus_check_get_cynara(check);
318 +      result = bus_cynara_check_privilege(cynara, message, sender, addressed_recipient,
319 +          proposed_recipient, privilege, check_type, deferred_message);
320 +#endif
321 +      if (result == BUS_RESULT_LATER && deferred_message != NULL)
322 +        {
323 +          (*deferred_message)->status |= check_type;
324 +        }
325      }
326    return result;
327  }
328 @@ -206,6 +295,12 @@ bus_deferred_message_unref (BusDeferredMessage *deferred_message)
329       }
330  }
331  
332 +BusDeferredMessageStatus
333 +bus_deferred_message_get_status (BusDeferredMessage *deferred_message)
334 +{
335 +  return deferred_message->status;
336 +}
337 +
338  void
339  bus_deferred_message_response_received (BusDeferredMessage *deferred_message,
340                                          BusResult result)
341 diff --git a/bus/check.h b/bus/check.h
342 index c3fcaf9..d177549 100644
343 --- a/bus/check.h
344 +++ b/bus/check.h
345 @@ -55,6 +55,7 @@ BusResult   bus_check_privilege   (BusCheck *check,
346                                     BusDeferredMessageStatus check_type,
347                                     BusDeferredMessage **deferred_message);
348  
349 +
350  BusDeferredMessage *bus_deferred_message_new                (DBusMessage *message,
351                                                               DBusConnection *sender,
352                                                               DBusConnection *addressed_recipient,
353 @@ -65,4 +66,13 @@ BusDeferredMessage *bus_deferred_message_ref                (BusDeferredMessage
354  void                bus_deferred_message_unref              (BusDeferredMessage *deferred_message);
355  void                bus_deferred_message_response_received  (BusDeferredMessage *deferred_message,
356                                                               BusResult result);
357 +void                bus_deferred_message_disable_sender     (BusDeferredMessage *deferred_message);
358 +
359 +BusDeferredMessageStatus  bus_deferred_message_get_status   (BusDeferredMessage *deferred_message);
360 +
361 +#ifdef DBUS_ENABLE_EMBEDDED_TESTS
362 +extern BusResult (*bus_check_test_override) (DBusConnection *connection,
363 +                                               const char *privilege);
364 +#endif
365 +
366  #endif /* BUS_CHECK_H */
367 diff --git a/bus/cynara.c b/bus/cynara.c
368 index 57a4c45..77aed62 100644
369 --- a/bus/cynara.c
370 +++ b/bus/cynara.c
371 @@ -36,7 +36,6 @@
372  #include <cynara-client-async.h>
373  #endif
374  
375 -
376  #ifdef DBUS_ENABLE_CYNARA
377  typedef struct BusCynara
378  {
379 diff --git a/bus/dispatch.c b/bus/dispatch.c
380 index 7e51bc1..0250b53 100644
381 --- a/bus/dispatch.c
382 +++ b/bus/dispatch.c
383 @@ -35,6 +35,7 @@
384  #include "signals.h"
385  #include "test.h"
386  #include <dbus/dbus-internals.h>
387 +#include <dbus/dbus-connection-internal.h>
388  #include <dbus/dbus-misc.h>
389  #include <string.h>
390  
391 @@ -122,7 +123,7 @@ send_one_message (DBusConnection *connection,
392    return TRUE;
393  }
394  
395 -dbus_bool_t
396 +BusResult
397  bus_dispatch_matches (BusTransaction *transaction,
398                        DBusConnection *sender,
399                        DBusConnection *addressed_recipient,
400 @@ -158,13 +159,29 @@ bus_dispatch_matches (BusTransaction *transaction,
401                                                 message, NULL, error,
402                                                 &deferred_message);
403        if (res == BUS_RESULT_FALSE)
404 -        return FALSE;
405 +        return BUS_RESULT_FALSE;
406        else if (res == BUS_RESULT_LATER)
407          {
408 -          dbus_set_error (error,
409 -                          DBUS_ERROR_ACCESS_DENIED,
410 -                          "Rejecting message because time is needed to check security policy");
411 -          return FALSE;
412 +          BusDeferredMessageStatus status;
413 +          status = bus_deferred_message_get_status(deferred_message);
414 +
415 +          if (status & BUS_DEFERRED_MESSAGE_CHECK_SEND)
416 +            {
417 +              /* send rule result not available - disable dispatching messages from the sender */
418 +              bus_deferred_message_disable_sender(deferred_message);
419 +              return BUS_RESULT_LATER;
420 +            }
421 +          else if (status & BUS_DEFERRED_MESSAGE_CHECK_RECEIVE)
422 +            {
423 +              dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
424 +                              "Rejecting message because time is needed to check security policy");
425 +              return BUS_RESULT_FALSE;
426 +            }
427 +          else
428 +            {
429 +              _dbus_verbose("deferred message has no status field set to send or receive unexpectedly\n");
430 +              return BUS_RESULT_FALSE;
431 +            }
432          }
433  
434        if (dbus_message_contains_unix_fds (message) &&
435 @@ -175,14 +192,14 @@ bus_dispatch_matches (BusTransaction *transaction,
436                            DBUS_ERROR_NOT_SUPPORTED,
437                            "Tried to send message with Unix file descriptors"
438                            "to a client that doesn't support that.");
439 -          return FALSE;
440 -      }
441 +          return BUS_RESULT_FALSE;
442 +        }
443  
444        /* Dispatch the message */
445        if (!bus_transaction_send (transaction, addressed_recipient, message))
446          {
447            BUS_SET_OOM (error);
448 -          return FALSE;
449 +          return BUS_RESULT_FALSE;
450          }
451      }
452  
453 @@ -197,7 +214,7 @@ bus_dispatch_matches (BusTransaction *transaction,
454                                        &recipients))
455      {
456        BUS_SET_OOM (error);
457 -      return FALSE;
458 +      return BUS_RESULT_FALSE;
459      }
460  
461    link = _dbus_list_get_first_link (&recipients);
462 @@ -219,10 +236,10 @@ bus_dispatch_matches (BusTransaction *transaction,
463    if (dbus_error_is_set (&tmp_error))
464      {
465        dbus_move_error (&tmp_error, error);
466 -      return FALSE;
467 +      return BUS_RESULT_FALSE;
468      }
469    else
470 -    return TRUE;
471 +    return BUS_RESULT_TRUE;
472  }
473  
474  static DBusHandlerResult
475 @@ -410,10 +427,12 @@ bus_dispatch (DBusConnection *connection,
476          }
477        else if (res == BUS_RESULT_LATER)
478          {
479 -          dbus_set_error (&error,
480 -                          DBUS_ERROR_ACCESS_DENIED,
481 -                          "Rejecting message because time is needed to check security policy");
482 -          _dbus_verbose ("Security policy needs time to check policy. Dropping message\n");
483 +          /* Disable dispatching messages from the sender,
484 +           * roll back and dispatch the message once the policy result is available */
485 +          bus_deferred_message_disable_sender(deferred_message);
486 +          bus_transaction_cancel_and_free (transaction);
487 +          transaction = NULL;
488 +          result = DBUS_HANDLER_RESULT_LATER;
489            goto out;
490          }
491  
492 @@ -515,8 +534,14 @@ bus_dispatch (DBusConnection *connection,
493     * addressed_recipient == NULL), and match it against other connections'
494     * match rules.
495     */
496 -  if (!bus_dispatch_matches (transaction, connection, addressed_recipient, message, &error))
497 -    goto out;
498 +  if (BUS_RESULT_LATER == bus_dispatch_matches (transaction, connection, addressed_recipient,
499 +                                                message, &error))
500 +    {
501 +      /* Roll back and dispatch the message once the policy result is available */
502 +      bus_transaction_cancel_and_free (transaction);
503 +      transaction = NULL;
504 +      result = DBUS_HANDLER_RESULT_LATER;
505 +    }
506  
507   out:
508    if (dbus_error_is_set (&error))
509 @@ -5061,9 +5086,132 @@ bus_dispatch_test_conf_fail (const DBusString *test_data_dir,
510  }
511  #endif
512  
513 +typedef struct {
514 +  DBusTimeout *timeout;
515 +  DBusConnection *connection;
516 +  dbus_bool_t timedout;
517 +  int check_counter;
518 +} BusTestCheckData;
519 +
520 +static BusTestCheckData *cdata;
521 +
522 +static dbus_bool_t
523 +bus_dispatch_test_check_timeout (void *data)
524 +{
525 +  _dbus_verbose ("timeout triggered - pretend that privilege check result is available\n");
526 +
527 +  /* should only happen once during the test */
528 +  _dbus_assert (!cdata->timedout);
529 +  cdata->timedout = TRUE;
530 +  _dbus_connection_enable_dispatch (cdata->connection);
531 +
532 +  /* don't call this again */
533 +  _dbus_loop_remove_timeout (bus_connection_get_loop (cdata->connection),
534 +                             cdata->timeout);
535 +  dbus_connection_unref (cdata->connection);
536 +  cdata->connection = NULL;
537 +  return TRUE;
538 +}
539 +
540 +static BusResult
541 +bus_dispatch_test_check_override (DBusConnection *connection,
542 +                                  const char *privilege)
543 +{
544 +  _dbus_verbose ("overriding privilege check %s #%d\n", privilege, cdata->check_counter);
545 +  cdata->check_counter++;
546 +  if (!cdata->timedout)
547 +    {
548 +      dbus_bool_t added;
549 +
550 +      /* Should be the first privilege check for the "Echo" method. */
551 +      _dbus_assert (cdata->check_counter == 1);
552 +      cdata->timeout = _dbus_timeout_new (1, bus_dispatch_test_check_timeout,
553 +                                          NULL, NULL);
554 +      _dbus_assert (cdata->timeout);
555 +      added = _dbus_loop_add_timeout (bus_connection_get_loop (connection),
556 +                                      cdata->timeout);
557 +      _dbus_assert (added);
558 +      cdata->connection = connection;
559 +      dbus_connection_ref (connection);
560 +      _dbus_connection_disable_dispatch (connection);
561 +      return BUS_RESULT_LATER;
562 +    }
563 +  else
564 +    {
565 +      /* Should only be checked one more time, and this time succeeds. */
566 +      _dbus_assert (cdata->check_counter == 2);
567 +      return BUS_RESULT_TRUE;
568 +    }
569 +}
570 +
571 +static dbus_bool_t
572 +bus_dispatch_test_check (const DBusString *test_data_dir)
573 +{
574 +  const char *filename = "valid-config-files/debug-check-some.conf";
575 +  BusContext *context;
576 +  DBusConnection *foo;
577 +  DBusError error;
578 +  dbus_bool_t result = TRUE;
579 +  BusTestCheckData data;
580 +
581 +  /* save the config name for the activation helper */
582 +  if (!setenv_TEST_LAUNCH_HELPER_CONFIG (test_data_dir, filename))
583 +    _dbus_assert_not_reached ("no memory setting TEST_LAUNCH_HELPER_CONFIG");
584 +
585 +  dbus_error_init (&error);
586 +
587 +  context = bus_context_new_test (test_data_dir, filename);
588 +  if (context == NULL)
589 +    return FALSE;
590 +
591 +  foo = dbus_connection_open_private (TEST_DEBUG_PIPE, &error);
592 +  if (foo == NULL)
593 +    _dbus_assert_not_reached ("could not alloc connection");
594 +
595 +  if (!bus_setup_debug_client (foo))
596 +    _dbus_assert_not_reached ("could not set up connection");
597 +
598 +  spin_connection_until_authenticated (context, foo);
599 +
600 +  if (!check_hello_message (context, foo))
601 +    _dbus_assert_not_reached ("hello message failed");
602 +
603 +  if (!check_double_hello_message (context, foo))
604 +    _dbus_assert_not_reached ("double hello message failed");
605 +
606 +  if (!check_add_match (context, foo, ""))
607 +    _dbus_assert_not_reached ("AddMatch message failed");
608 +
609 +  /*
610 +   * Cause bus_check_send_privilege() to return BUS_RESULT_LATER in the
611 +   * first call, then BUS_RESULT_TRUE.
612 +   */
613 +  cdata = &data;
614 +  memset (cdata, 0, sizeof(*cdata));
615 +  bus_check_test_override = bus_dispatch_test_check_override;
616 +
617 +  result = check_existent_service_auto_start (context, foo);
618 +
619 +  _dbus_assert (cdata->check_counter == 2);
620 +  _dbus_assert (cdata->timedout);
621 +  _dbus_assert (cdata->timeout);
622 +  _dbus_assert (!cdata->connection);
623 +  _dbus_timeout_unref (cdata->timeout);
624 +
625 +  kill_client_connection_unchecked (foo);
626 +
627 +  bus_context_unref (context);
628 +
629 +  return result;
630 +}
631 +
632  dbus_bool_t
633  bus_dispatch_test (const DBusString *test_data_dir)
634  {
635 +  _dbus_verbose ("<check> tests\n");
636 +  if (!bus_dispatch_test_check (test_data_dir))
637 +    return FALSE;
638 +
639    /* run normal activation tests */
640    _dbus_verbose ("Normal activation tests\n");
641    if (!bus_dispatch_test_conf (test_data_dir,
642 diff --git a/bus/dispatch.h b/bus/dispatch.h
643 index fb5ba7a..afba6a2 100644
644 --- a/bus/dispatch.h
645 +++ b/bus/dispatch.h
646 @@ -29,7 +29,7 @@
647  
648  dbus_bool_t bus_dispatch_add_connection    (DBusConnection *connection);
649  void        bus_dispatch_remove_connection (DBusConnection *connection);
650 -dbus_bool_t bus_dispatch_matches           (BusTransaction *transaction,
651 +BusResult   bus_dispatch_matches           (BusTransaction *transaction,
652                                              DBusConnection *sender,
653                                              DBusConnection *recipient,
654                                              DBusMessage    *message,
655 diff --git a/bus/driver.c b/bus/driver.c
656 index cd0a714..f414f64 100644
657 --- a/bus/driver.c
658 +++ b/bus/driver.c
659 @@ -218,6 +218,7 @@ bus_driver_send_service_owner_changed (const char     *service_name,
660  {
661    DBusMessage *message;
662    dbus_bool_t retval;
663 +  BusResult res;
664    const char *null_service;
665  
666    _DBUS_ASSERT_ERROR_IS_CLEAR (error);
667 @@ -253,7 +254,16 @@ bus_driver_send_service_owner_changed (const char     *service_name,
668    if (!bus_transaction_capture (transaction, NULL, NULL, message))
669      goto oom;
670  
671 -  retval = bus_dispatch_matches (transaction, NULL, NULL, message, error);
672 +  res = bus_dispatch_matches (transaction, NULL, NULL, message, error);
673 +  if (res == BUS_RESULT_TRUE)
674 +    retval = TRUE;
675 +  else
676 +    {
677 +      retval = FALSE;
678 +      if (res == BUS_RESULT_LATER)
679 +        /* should never happen */
680 +        _dbus_assert_not_reached ("bus_dispatch_matches returned BUS_RESULT_LATER unexpectedly");
681 +    }
682    dbus_message_unref (message);
683  
684    return retval;
685 diff --git a/dbus/dbus-connection-internal.h b/dbus/dbus-connection-internal.h
686 index 4835732..94b1c95 100644
687 --- a/dbus/dbus-connection-internal.h
688 +++ b/dbus/dbus-connection-internal.h
689 @@ -118,6 +118,21 @@ DBUS_PRIVATE_EXPORT
690  dbus_bool_t       _dbus_connection_get_linux_security_label       (DBusConnection  *connection,
691                                                                     char           **label_p);
692  
693 +DBUS_PRIVATE_EXPORT
694 +void              _dbus_connection_enable_dispatch                (DBusConnection *connection);
695 +DBUS_PRIVATE_EXPORT
696 +void              _dbus_connection_disable_dispatch               (DBusConnection *connection);
697 +
698 +DBUS_PRIVATE_EXPORT
699 +dbus_bool_t       _dbus_connection_putback_message                (DBusConnection *connection,
700 +                                                                   DBusMessage    *after_message,
701 +                                                                   DBusMessage    *message,
702 +                                                                   DBusError      *error);
703 +
704 +DBUS_PRIVATE_EXPORT
705 +dbus_bool_t       _dbus_connection_remove_message                 (DBusConnection *connection,
706 +                                                                   DBusMessage    *message);
707 +
708  /* if DBUS_ENABLE_STATS */
709  DBUS_PRIVATE_EXPORT
710  void _dbus_connection_get_stats (DBusConnection *connection,
711 diff --git a/dbus/dbus-connection.c b/dbus/dbus-connection.c
712 index c525b6d..f1b0ea0 100644
713 --- a/dbus/dbus-connection.c
714 +++ b/dbus/dbus-connection.c
715 @@ -311,7 +311,8 @@ struct DBusConnection
716     */
717    dbus_bool_t dispatch_acquired; /**< Someone has dispatch path (can drain incoming queue) */
718    dbus_bool_t io_path_acquired;  /**< Someone has transport io path (can use the transport to read/write messages) */
719 -  
720 +
721 +  unsigned int dispatch_disabled : 1;  /**< if true, then dispatching incoming messages is stopped until enabled again */
722    unsigned int shareable : 1; /**< #TRUE if libdbus owns a reference to the connection and can return it from dbus_connection_open() more than once */
723    
724    unsigned int exit_on_disconnect : 1; /**< If #TRUE, exit after handling disconnect signal */
725 @@ -439,6 +440,39 @@ _dbus_connection_wakeup_mainloop (DBusConnection *connection)
726      (*connection->wakeup_main_function) (connection->wakeup_main_data);
727  }
728  
729 +static void
730 +_dbus_connection_set_dispatch(DBusConnection *connection,
731 +                              dbus_bool_t disabled)
732 +{
733 +  CONNECTION_LOCK (connection);
734 +  if (connection->dispatch_disabled != disabled)
735 +    {
736 +      DBusDispatchStatus status;
737 +
738 +      connection->dispatch_disabled = disabled;
739 +      status = _dbus_connection_get_dispatch_status_unlocked (connection);
740 +      _dbus_connection_update_dispatch_status_and_unlock (connection, status);
741 +    }
742 +  else
743 +    {
744 +      CONNECTION_UNLOCK (connection);
745 +    }
746 +}
747 +
748 +
749 +void
750 +_dbus_connection_enable_dispatch (DBusConnection *connection)
751 +{
752 +  _dbus_connection_set_dispatch (connection, FALSE);
753 +}
754 +
755 +void
756 + _dbus_connection_disable_dispatch (DBusConnection *connection)
757 +{
758 +  _dbus_connection_set_dispatch (connection, TRUE);
759 +}
760 +
761 +
762  #ifdef DBUS_ENABLE_EMBEDDED_TESTS
763  /**
764   * Gets the locks so we can examine them
765 @@ -4069,6 +4103,82 @@ _dbus_connection_putback_message_link_unlocked (DBusConnection *connection,
766        "_dbus_connection_putback_message_link_unlocked");
767  }
768  
769 +dbus_bool_t
770 +_dbus_connection_putback_message (DBusConnection *connection,
771 +                                  DBusMessage    *after_message,
772 +                                  DBusMessage    *message,
773 +                                  DBusError      *error)
774 +{
775 +  DBusDispatchStatus status;
776 +  DBusList *message_link = _dbus_list_alloc_link (message);
777 +  DBusList *after_link;
778 +  if (message_link == NULL)
779 +    {
780 +      _DBUS_SET_OOM (error);
781 +      return FALSE;
782 +    }
783 +  dbus_message_ref (message);
784 +
785 +  CONNECTION_LOCK (connection);
786 +  _dbus_connection_acquire_dispatch (connection);
787 +  HAVE_LOCK_CHECK (connection);
788 +
789 +  after_link = _dbus_list_find_first(&connection->incoming_messages, after_message);
790 +  _dbus_list_insert_after_link (&connection->incoming_messages, after_link, message_link);
791 +  connection->n_incoming += 1;
792 +
793 +  _dbus_verbose ("Message %p (%s %s %s '%s') put back into queue %p, %d incoming\n",
794 +                 message_link->data,
795 +                 dbus_message_type_to_string (dbus_message_get_type (message_link->data)),
796 +                 dbus_message_get_interface (message_link->data) ?
797 +                 dbus_message_get_interface (message_link->data) :
798 +                 "no interface",
799 +                 dbus_message_get_member (message_link->data) ?
800 +                 dbus_message_get_member (message_link->data) :
801 +                 "no member",
802 +                 dbus_message_get_signature (message_link->data),
803 +                 connection, connection->n_incoming);
804 +
805 +  _dbus_message_trace_ref (message_link->data, -1, -1,
806 +      "_dbus_connection_putback_message");
807 +
808 +  _dbus_connection_release_dispatch (connection);
809 +
810 +  status = _dbus_connection_get_dispatch_status_unlocked (connection);
811 +  _dbus_connection_update_dispatch_status_and_unlock (connection, status);
812 +
813 +  return TRUE;
814 +}
815 +
816 +dbus_bool_t
817 +_dbus_connection_remove_message (DBusConnection *connection,
818 +                                 DBusMessage *message)
819 +{
820 +  DBusDispatchStatus status;
821 +  dbus_bool_t removed;
822 +
823 +  CONNECTION_LOCK (connection);
824 +  _dbus_connection_acquire_dispatch (connection);
825 +  HAVE_LOCK_CHECK (connection);
826 +
827 +  removed = _dbus_list_remove(&connection->incoming_messages, message);
828 +
829 +  if (removed)
830 +    {
831 +      connection->n_incoming -= 1;
832 +      dbus_message_unref(message);
833 +      _dbus_verbose ("Message %p removed from incoming queue\n", message);
834 +    }
835 +  else
836 +      _dbus_verbose ("Message %p not found in the incoming queue\n", message);
837 +
838 +  _dbus_connection_release_dispatch (connection);
839 +
840 +  status = _dbus_connection_get_dispatch_status_unlocked (connection);
841 +  _dbus_connection_update_dispatch_status_and_unlock (connection, status);
842 +  return removed;
843 +}
844 +
845  /**
846   * Returns the first-received message from the incoming message queue,
847   * removing it from the queue. The caller owns a reference to the
848 @@ -4252,8 +4362,9 @@ static DBusDispatchStatus
849  _dbus_connection_get_dispatch_status_unlocked (DBusConnection *connection)
850  {
851    HAVE_LOCK_CHECK (connection);
852 -  
853 -  if (connection->n_incoming > 0)
854 +  if (connection->dispatch_disabled && dbus_connection_get_is_connected(connection))
855 +    return DBUS_DISPATCH_COMPLETE;
856 +  else if (connection->n_incoming > 0)
857      return DBUS_DISPATCH_DATA_REMAINS;
858    else if (!_dbus_transport_queue_messages (connection->transport))
859      return DBUS_DISPATCH_NEED_MEMORY;
860 @@ -4716,6 +4827,8 @@ dbus_connection_dispatch (DBusConnection *connection)
861    
862    CONNECTION_LOCK (connection);
863  
864 +  if (result == DBUS_HANDLER_RESULT_LATER)
865 +      goto out;
866    if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
867      {
868        _dbus_verbose ("No memory\n");
869 @@ -4838,9 +4951,11 @@ dbus_connection_dispatch (DBusConnection *connection)
870                   connection);
871    
872   out:
873 -  if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
874 +  if (result == DBUS_HANDLER_RESULT_LATER ||
875 +      result == DBUS_HANDLER_RESULT_NEED_MEMORY)
876      {
877 -      _dbus_verbose ("out of memory\n");
878 +      if (result == DBUS_HANDLER_RESULT_NEED_MEMORY)
879 +        _dbus_verbose ("out of memory\n");
880        
881        /* Put message back, and we'll start over.
882         * Yes this means handlers must be idempotent if they
883 diff --git a/dbus/dbus-list.c b/dbus/dbus-list.c
884 index 8e713c0..32ea871 100644
885 --- a/dbus/dbus-list.c
886 +++ b/dbus/dbus-list.c
887 @@ -458,6 +458,35 @@ _dbus_list_remove_last (DBusList **list,
888      return FALSE;
889  }
890  
891 +/**
892 + * Finds a value in the list. Returns the first link
893 + * with value equal to the given data pointer.
894 + * This is a linear-time operation.
895 + * Returns #NULL if no value found that matches.
896 + *
897 + * @param list address of the list head.
898 + * @param data the value to find.
899 + * @returns the link if found
900 + */
901 +DBusList*
902 +_dbus_list_find_first (DBusList **list,
903 +                       void      *data)
904 +{
905 +  DBusList *link;
906 +
907 +  link = _dbus_list_get_first_link (list);
908 +
909 +  while (link != NULL)
910 +    {
911 +      if (link->data == data)
912 +        return link;
913 +
914 +      link = _dbus_list_get_next_link (list, link);
915 +    }
916 +
917 +  return NULL;
918 +}
919 +
920  /**
921   * Finds a value in the list. Returns the last link
922   * with value equal to the given data pointer.
923 diff --git a/dbus/dbus-list.h b/dbus/dbus-list.h
924 index 9350a0d..fee9f1b 100644
925 --- a/dbus/dbus-list.h
926 +++ b/dbus/dbus-list.h
927 @@ -68,6 +68,9 @@ DBUS_PRIVATE_EXPORT
928  void        _dbus_list_remove_link        (DBusList **list,
929                                             DBusList  *link);
930  DBUS_PRIVATE_EXPORT
931 +DBusList*   _dbus_list_find_first         (DBusList **list,
932 +                                           void      *data);
933 +DBUS_PRIVATE_EXPORT
934  DBusList*   _dbus_list_find_last          (DBusList **list,
935                                             void      *data);
936  DBUS_PRIVATE_EXPORT
937 diff --git a/dbus/dbus-shared.h b/dbus/dbus-shared.h
938 index 7ab9103..e5bfbed 100644
939 --- a/dbus/dbus-shared.h
940 +++ b/dbus/dbus-shared.h
941 @@ -67,7 +67,8 @@ typedef enum
942  {
943    DBUS_HANDLER_RESULT_HANDLED,         /**< Message has had its effect - no need to run more handlers. */ 
944    DBUS_HANDLER_RESULT_NOT_YET_HANDLED, /**< Message has not had any effect - see if other handlers want it. */
945 -  DBUS_HANDLER_RESULT_NEED_MEMORY      /**< Need more memory in order to return #DBUS_HANDLER_RESULT_HANDLED or #DBUS_HANDLER_RESULT_NOT_YET_HANDLED. Please try again later with more memory. */
946 +  DBUS_HANDLER_RESULT_NEED_MEMORY,     /**< Need more memory in order to return #DBUS_HANDLER_RESULT_HANDLED or #DBUS_HANDLER_RESULT_NOT_YET_HANDLED. Please try again later with more memory. */
947 +  DBUS_HANDLER_RESULT_LATER            /**< Message dispatch deferred due to pending policy check */
948  } DBusHandlerResult;
949  
950  /* Bus names */