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