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