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