6a7e8a39d985516b319225e63b351b316f1cb012
[AGL/meta-agl.git] / meta-security / recipes-core / dbus-cynara / dbus-cynara / 0001-Integration-of-Cynara-asynchronous-security-checks.patch
1 From 8f69153081140fa4c347ab1729c348ec77b309ec Mon Sep 17 00:00:00 2001
2 From: Jacek Bukarewicz <j.bukarewicz@samsung.com>
3 Date: Thu, 27 Nov 2014 18:11:05 +0100
4 Subject: [PATCH 1/5] Integration of Cynara asynchronous security checks
5 MIME-Version: 1.0
6 Content-Type: text/plain; charset=UTF-8
7 Content-Transfer-Encoding: 8bit
8
9 This commit introduces basic framework for asynchronous policy
10 checks and Cynara integration code. Functions for checking security
11 policy can now return third value - BUS_RESULT_LATER denoting check
12 result unavailability. Whenever policy checker cannot decide on the
13 result of the check it is supposed to allocate DeferredMessage structure
14 that will be passed to the upper layers which can decide what should be
15 done in such situation.
16 Proper handling of such case will be implemented in subsequent commits.
17 Currently such return value results in message denial.
18
19 Cherry picked from 4dcfb02f17247ff9de966b62182cd2e08f301238
20 by José Bollo.
21
22 Change-Id: I9bcbce34577e5dc2a3cecf6233a0a2b0e43e1108
23 Signed-off-by: José Bollo <jose.bollo@iot.bzh>
24 ---
25  bus/Makefile.am                                    |   6 +
26  bus/bus.c                                          | 136 +++++---
27  bus/bus.h                                          |  32 +-
28  bus/check.c                                        | 217 ++++++++++++
29  bus/check.h                                        |  68 ++++
30  bus/config-parser-common.c                         |   6 +
31  bus/config-parser-common.h                         |   1 +
32  bus/config-parser.c                                |  71 +++-
33  bus/connection.c                                   |  56 ++-
34  bus/connection.h                                   |   4 +
35  bus/cynara.c                                       | 374 +++++++++++++++++++++
36  bus/cynara.h                                       |  37 ++
37  bus/dispatch.c                                     |  44 ++-
38  bus/policy.c                                       | 193 +++++++----
39  bus/policy.h                                       |  51 ++-
40  configure.ac                                       |  12 +
41  test/Makefile.am                                   |   1 +
42  test/data/invalid-config-files/badcheck-1.conf     |   9 +
43  test/data/invalid-config-files/badcheck-2.conf     |   9 +
44  test/data/valid-config-files/check-1.conf          |   9 +
45  .../valid-config-files/debug-check-some.conf.in    |  18 +
46  tools/dbus-send.c                                  |   2 +-
47  22 files changed, 1193 insertions(+), 163 deletions(-)
48  create mode 100644 bus/check.c
49  create mode 100644 bus/check.h
50  create mode 100644 bus/cynara.c
51  create mode 100644 bus/cynara.h
52  create mode 100644 test/data/invalid-config-files/badcheck-1.conf
53  create mode 100644 test/data/invalid-config-files/badcheck-2.conf
54  create mode 100644 test/data/valid-config-files/check-1.conf
55  create mode 100644 test/data/valid-config-files/debug-check-some.conf.in
56
57 diff --git a/bus/Makefile.am b/bus/Makefile.am
58 index 33af09b0..3f57cc48 100644
59 --- a/bus/Makefile.am
60 +++ b/bus/Makefile.am
61 @@ -9,6 +9,7 @@ DBUS_BUS_LIBS = \
62         $(THREAD_LIBS) \
63         $(ADT_LIBS) \
64         $(NETWORK_libs) \
65 +       $(CYNARA_LIBS) \
66         $(NULL)
67  
68  DBUS_LAUNCHER_LIBS = \
69 @@ -24,6 +25,7 @@ AM_CPPFLAGS = \
70         $(APPARMOR_CFLAGS) \
71         -DDBUS_SYSTEM_CONFIG_FILE=\""$(dbusdatadir)/system.conf"\" \
72         -DDBUS_COMPILATION \
73 +       $(CYNARA_CFLAGS) \
74         $(NULL)
75  
76  # if assertions are enabled, improve backtraces
77 @@ -82,12 +84,16 @@ BUS_SOURCES=                                        \
78         audit.h                                 \
79         bus.c                                   \
80         bus.h                                   \
81 +       check.c                                 \
82 +       check.h                                 \
83         config-parser.c                         \
84         config-parser.h                         \
85         config-parser-common.c                  \
86         config-parser-common.h                  \
87         connection.c                            \
88         connection.h                            \
89 +       cynara.c                                \
90 +       cynara.h                                \
91         desktop-file.c                          \
92         desktop-file.h                          \
93         $(DIR_WATCH_SOURCE)                     \
94 diff --git a/bus/bus.c b/bus/bus.c
95 index fd4ab9e4..c4008505 100644
96 --- a/bus/bus.c
97 +++ b/bus/bus.c
98 @@ -37,6 +37,7 @@
99  #include "apparmor.h"
100  #include "audit.h"
101  #include "dir-watch.h"
102 +#include "check.h"
103  #include <dbus/dbus-list.h>
104  #include <dbus/dbus-hash.h>
105  #include <dbus/dbus-credentials.h>
106 @@ -65,6 +66,7 @@ struct BusContext
107    BusRegistry *registry;
108    BusPolicy *policy;
109    BusMatchmaker *matchmaker;
110 +  BusCheck *check;
111    BusLimits limits;
112    DBusRLimit *initial_fd_limit;
113    unsigned int fork : 1;
114 @@ -988,6 +990,10 @@ bus_context_new (const DBusString *config_file,
115        parser = NULL;
116      }
117  
118 +  context->check = bus_check_new(context, error);
119 +  if (context->check == NULL)
120 +      goto failed;
121 +
122    dbus_server_free_data_slot (&server_data_slot);
123  
124    return context;
125 @@ -1112,6 +1118,12 @@ bus_context_unref (BusContext *context)
126  
127        bus_context_shutdown (context);
128  
129 +      if (context->check)
130 +        {
131 +          bus_check_unref(context->check);
132 +          context->check = NULL;
133 +        }
134 +
135        if (context->connections)
136          {
137            bus_connections_unref (context->connections);
138 @@ -1241,6 +1253,12 @@ bus_context_get_loop (BusContext *context)
139    return context->loop;
140  }
141  
142 +BusCheck*
143 +bus_context_get_check (BusContext *context)
144 +{
145 +  return context->check;
146 +}
147 +
148  dbus_bool_t
149  bus_context_allow_unix_user (BusContext   *context,
150                               unsigned long uid)
151 @@ -1456,6 +1474,7 @@ complain_about_message (BusContext     *context,
152                          DBusConnection *proposed_recipient,
153                          dbus_bool_t     requested_reply,
154                          dbus_bool_t     log,
155 +                        const char     *privilege,
156                          DBusError      *error)
157  {
158    DBusError stack_error = DBUS_ERROR_INIT;
159 @@ -1485,7 +1504,8 @@ complain_about_message (BusContext     *context,
160    dbus_set_error (&stack_error, error_name,
161        "%s, %d matched rules; type=\"%s\", sender=\"%s\" (%s) "
162        "interface=\"%s\" member=\"%s\" error name=\"%s\" "
163 -      "requested_reply=\"%d\" destination=\"%s\" (%s)",
164 +      "requested_reply=\"%d\" destination=\"%s\" (%s) "
165 +      "privilege=\"%s\"",
166        complaint,
167        matched_rules,
168        dbus_message_type_to_string (dbus_message_get_type (message)),
169 @@ -1496,7 +1516,8 @@ complain_about_message (BusContext     *context,
170        nonnull (dbus_message_get_error_name (message), "(unset)"),
171        requested_reply,
172        nonnull (dbus_message_get_destination (message), DBUS_SERVICE_DBUS),
173 -      proposed_recipient_loginfo);
174 +      proposed_recipient_loginfo,
175 +      nonnull (privilege, "(n/a)"));
176  
177    /* If we hit OOM while setting the error, this will syslog "out of memory"
178     * which is itself an indication that something is seriously wrong */
179 @@ -1520,14 +1541,15 @@ complain_about_message (BusContext     *context,
180   * NULL for addressed_recipient may mean the bus driver, or may mean
181   * no destination was specified in the message (e.g. a signal).
182   */
183 -dbus_bool_t
184 -bus_context_check_security_policy (BusContext     *context,
185 -                                   BusTransaction *transaction,
186 -                                   DBusConnection *sender,
187 -                                   DBusConnection *addressed_recipient,
188 -                                   DBusConnection *proposed_recipient,
189 -                                   DBusMessage    *message,
190 -                                   DBusError      *error)
191 +BusResult
192 +bus_context_check_security_policy (BusContext          *context,
193 +                                   BusTransaction      *transaction,
194 +                                   DBusConnection      *sender,
195 +                                   DBusConnection      *addressed_recipient,
196 +                                   DBusConnection      *proposed_recipient,
197 +                                   DBusMessage         *message,
198 +                                   DBusError           *error,
199 +                                   BusDeferredMessage **deferred_message)
200  {
201    const char *src, *dest;
202    BusClientPolicy *sender_policy;
203 @@ -1536,6 +1558,7 @@ bus_context_check_security_policy (BusContext     *context,
204    dbus_bool_t log;
205    int type;
206    dbus_bool_t requested_reply;
207 +  const char *privilege;
208  
209    type = dbus_message_get_type (message);
210    src = dbus_message_get_sender (message);
211 @@ -1564,7 +1587,7 @@ bus_context_check_security_policy (BusContext     *context,
212        dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
213                        "Message bus will not accept messages of unknown type\n");
214  
215 -      return FALSE;
216 +      return BUS_RESULT_FALSE;
217      }
218  
219    requested_reply = FALSE;
220 @@ -1594,7 +1617,7 @@ bus_context_check_security_policy (BusContext     *context,
221                    if (dbus_error_is_set (&error2))
222                      {
223                        dbus_move_error (&error2, error);
224 -                      return FALSE;
225 +                      return BUS_RESULT_FALSE;
226                      }
227                  }
228              }
229 @@ -1621,11 +1644,11 @@ bus_context_check_security_policy (BusContext     *context,
230                complain_about_message (context, DBUS_ERROR_ACCESS_DENIED,
231                    "An SELinux policy prevents this sender from sending this "
232                    "message to this recipient",
233 -                  0, message, sender, proposed_recipient, FALSE, FALSE, error);
234 +                  0, message, sender, proposed_recipient, FALSE, FALSE, NULL, error);
235                _dbus_verbose ("SELinux security check denying send to service\n");
236              }
237  
238 -          return FALSE;
239 +          return BUS_RESULT_FALSE;
240          }
241  
242        /* next verify AppArmor access controls.  If allowed then
243 @@ -1642,7 +1665,7 @@ bus_context_check_security_policy (BusContext     *context,
244                                       dest ? dest : DBUS_SERVICE_DBUS,
245                                       src ? src : DBUS_SERVICE_DBUS,
246                                       error))
247 -        return FALSE;
248 +        return BUS_RESULT_FALSE;
249  
250        if (!bus_connection_is_active (sender))
251          {
252 @@ -1656,7 +1679,7 @@ bus_context_check_security_policy (BusContext     *context,
253              {
254                _dbus_verbose ("security check allowing %s message\n",
255                               "Hello");
256 -              return TRUE;
257 +              return BUS_RESULT_TRUE;
258              }
259            else
260              {
261 @@ -1667,7 +1690,7 @@ bus_context_check_security_policy (BusContext     *context,
262                                "Client tried to send a message other than %s without being registered",
263                                "Hello");
264  
265 -              return FALSE;
266 +              return BUS_RESULT_FALSE;
267              }
268          }
269      }
270 @@ -1716,20 +1739,29 @@ bus_context_check_security_policy (BusContext     *context,
271                  (proposed_recipient == NULL && recipient_policy == NULL));
272  
273    log = FALSE;
274 -  if (sender_policy &&
275 -      !bus_client_policy_check_can_send (sender_policy,
276 -                                         context->registry,
277 -                                         requested_reply,
278 -                                         proposed_recipient,
279 -                                         message, &toggles, &log))
280 -    {
281 -      complain_about_message (context, DBUS_ERROR_ACCESS_DENIED,
282 -          "Rejected send message", toggles,
283 -          message, sender, proposed_recipient, requested_reply,
284 -          (addressed_recipient == proposed_recipient), error);
285 -      _dbus_verbose ("security policy disallowing message due to sender policy\n");
286 -      return FALSE;
287 -    }
288 +  if (sender_policy)
289 +    {
290 +      BusResult res = bus_client_policy_check_can_send (sender,
291 +                                                        sender_policy,
292 +                                                        context->registry,
293 +                                                        requested_reply,
294 +                                                        addressed_recipient,
295 +                                                        proposed_recipient,
296 +                                                        message, &toggles, &log, &privilege,
297 +                                                        deferred_message);
298 +      if (res == BUS_RESULT_FALSE)
299 +        {
300 +          complain_about_message (context, DBUS_ERROR_ACCESS_DENIED,
301 +                                  "Rejected send message", toggles,
302 +                                  message, sender, proposed_recipient, requested_reply,
303 +                                  (addressed_recipient == proposed_recipient), privilege,
304 +                                  error);
305 +          _dbus_verbose ("security policy disallowing message due to sender policy\n");
306 +          return BUS_RESULT_FALSE;
307 +        }
308 +      else if (res == BUS_RESULT_LATER)
309 +        return BUS_RESULT_LATER;
310 +  }
311  
312    if (log)
313      {
314 @@ -1738,23 +1770,29 @@ bus_context_check_security_policy (BusContext     *context,
315        complain_about_message (context, DBUS_ERROR_ACCESS_DENIED,
316            "Would reject message", toggles,
317            message, sender, proposed_recipient, requested_reply,
318 -          TRUE, NULL);
319 +          TRUE, privilege, NULL);
320      }
321  
322 -  if (recipient_policy &&
323 -      !bus_client_policy_check_can_receive (recipient_policy,
324 -                                            context->registry,
325 -                                            requested_reply,
326 -                                            sender,
327 -                                            addressed_recipient, proposed_recipient,
328 -                                            message, &toggles))
329 +  if (recipient_policy)
330      {
331 -      complain_about_message (context, DBUS_ERROR_ACCESS_DENIED,
332 -          "Rejected receive message", toggles,
333 -          message, sender, proposed_recipient, requested_reply,
334 -          (addressed_recipient == proposed_recipient), error);
335 -      _dbus_verbose ("security policy disallowing message due to recipient policy\n");
336 -      return FALSE;
337 +      BusResult res;
338 +      res = bus_client_policy_check_can_receive (recipient_policy,
339 +                                                 context->registry,
340 +                                                 requested_reply,
341 +                                                 sender,
342 +                                                 addressed_recipient, proposed_recipient,
343 +                                                 message, &toggles, &privilege, deferred_message);
344 +      if (res == BUS_RESULT_FALSE)
345 +        {
346 +          complain_about_message(context, DBUS_ERROR_ACCESS_DENIED, "Rejected receive message",
347 +              toggles, message, sender, proposed_recipient, requested_reply,
348 +            (addressed_recipient == proposed_recipient), privilege, error);
349 +          _dbus_verbose(
350 +            "security policy disallowing message due to recipient policy\n");
351 +          return BUS_RESULT_FALSE;
352 +        }
353 +      else if (res == BUS_RESULT_LATER)
354 +        return BUS_RESULT_LATER;
355      }
356  
357    /* See if limits on size have been exceeded */
358 @@ -1764,10 +1802,10 @@ bus_context_check_security_policy (BusContext     *context,
359      {
360        complain_about_message (context, DBUS_ERROR_LIMITS_EXCEEDED,
361            "Rejected: destination has a full message queue",
362 -          0, message, sender, proposed_recipient, requested_reply, TRUE,
363 +          0, message, sender, proposed_recipient, requested_reply, TRUE, NULL,
364            error);
365        _dbus_verbose ("security policy disallowing message due to full message queue\n");
366 -      return FALSE;
367 +      return BUS_RESULT_FALSE;
368      }
369  
370    /* Record that we will allow a reply here in the future (don't
371 @@ -1784,11 +1822,11 @@ bus_context_check_security_policy (BusContext     *context,
372                                       message, error))
373      {
374        _dbus_verbose ("Failed to record reply expectation or problem with the message expecting a reply\n");
375 -      return FALSE;
376 +      return BUS_RESULT_FALSE;
377      }
378  
379    _dbus_verbose ("security policy allowing message\n");
380 -  return TRUE;
381 +  return BUS_RESULT_TRUE;
382  }
383  
384  void
385 diff --git a/bus/bus.h b/bus/bus.h
386 index 3fab59ff..dab7791f 100644
387 --- a/bus/bus.h
388 +++ b/bus/bus.h
389 @@ -44,6 +44,22 @@ typedef struct BusOwner              BusOwner;
390  typedef struct BusTransaction   BusTransaction;
391  typedef struct BusMatchmaker    BusMatchmaker;
392  typedef struct BusMatchRule     BusMatchRule;
393 +typedef struct BusCheck         BusCheck;
394 +typedef struct BusDeferredMessage BusDeferredMessage;
395 +typedef struct BusCynara        BusCynara;
396 +
397 +/**
398 + * BusResult is defined as a pointer to a dummy structure to allow detection of type mismatches.
399 + * The disadvantage of such solution is that now BusResult variables cannot be used in switch
400 + * statement.
401 + * Additionally, BUS_RESULT_TRUE is defined as 0 instead of 1 to help detect type mismatches
402 + * at runtime.
403 + */
404 +typedef const struct BusResultStruct { int dummy; } *BusResult;
405 +
406 +static const BusResult BUS_RESULT_TRUE  = (BusResult)0x0;
407 +static const BusResult BUS_RESULT_FALSE = (BusResult)0x1;
408 +static const BusResult BUS_RESULT_LATER = (BusResult)0x2;
409  
410  typedef struct
411  {
412 @@ -97,6 +113,7 @@ BusConnections*   bus_context_get_connections                    (BusContext
413  BusActivation*    bus_context_get_activation                     (BusContext       *context);
414  BusMatchmaker*    bus_context_get_matchmaker                     (BusContext       *context);
415  DBusLoop*         bus_context_get_loop                           (BusContext       *context);
416 +BusCheck *        bus_context_get_check                          (BusContext       *context);
417  dbus_bool_t       bus_context_allow_unix_user                    (BusContext       *context,
418                                                                    unsigned long     uid);
419  dbus_bool_t       bus_context_allow_windows_user                 (BusContext       *context,
420 @@ -131,13 +148,14 @@ void              bus_context_log_and_set_error                  (BusContext
421                                                                    const char       *name,
422                                                                    const char       *msg,
423                                                                    ...) _DBUS_GNUC_PRINTF (5, 6);
424 -dbus_bool_t       bus_context_check_security_policy              (BusContext       *context,
425 -                                                                  BusTransaction   *transaction,
426 -                                                                  DBusConnection   *sender,
427 -                                                                  DBusConnection   *addressed_recipient,
428 -                                                                  DBusConnection   *proposed_recipient,
429 -                                                                  DBusMessage      *message,
430 -                                                                  DBusError        *error);
431 +BusResult         bus_context_check_security_policy              (BusContext          *context,
432 +                                                                  BusTransaction      *transaction,
433 +                                                                  DBusConnection      *sender,
434 +                                                                  DBusConnection      *addressed_recipient,
435 +                                                                  DBusConnection      *proposed_recipient,
436 +                                                                  DBusMessage         *message,
437 +                                                                  DBusError           *error,
438 +                                                                  BusDeferredMessage **deferred_message);
439  void              bus_context_check_all_watches                  (BusContext       *context);
440  
441  #endif /* BUS_BUS_H */
442 diff --git a/bus/check.c b/bus/check.c
443 new file mode 100644
444 index 00000000..5b72d31c
445 --- /dev/null
446 +++ b/bus/check.c
447 @@ -0,0 +1,217 @@
448 +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
449 +/* check.c  Bus security policy runtime check
450 + *
451 + * Copyright (C) 2014  Intel, Inc.
452 + * Copyright (c) 2014  Samsung Electronics, Ltd.
453 + *
454 + * Licensed under the Academic Free License version 2.1
455 + *
456 + * This program is free software; you can redistribute it and/or modify
457 + * it under the terms of the GNU General Public License as published by
458 + * the Free Software Foundation; either version 2 of the License, or
459 + * (at your option) any later version.
460 + *
461 + * This program is distributed in the hope that it will be useful,
462 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
463 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
464 + * GNU General Public License for more details.
465 + *
466 + * You should have received a copy of the GNU General Public License
467 + * along with this program; if not, write to the Free Software
468 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
469 + *
470 + */
471 +
472 +#include <config.h>
473 +#include "check.h"
474 +#include "connection.h"
475 +#include "dispatch.h"
476 +#include "cynara.h"
477 +#include "utils.h"
478 +#include <dbus/dbus-connection-internal.h>
479 +#include <dbus/dbus-message-internal.h>
480 +#include <dbus/dbus-internals.h>
481 +
482 +
483 +typedef struct BusCheck
484 +{
485 +  int refcount;
486 +
487 +  BusContext *context;
488 +  BusCynara *cynara;
489 +} BusCheck;
490 +
491 +typedef struct BusDeferredMessage
492 +{
493 +  int refcount;
494 +
495 +  DBusMessage *message;
496 +  DBusConnection *sender;
497 +  DBusConnection *proposed_recipient;
498 +  DBusConnection *addressed_recipient;
499 +  dbus_bool_t full_dispatch;
500 +  BusDeferredMessageStatus status;
501 +  BusResult response;
502 +  BusCheckResponseFunc response_callback;
503 +} BusDeferredMessage;
504 +
505 +BusCheck *
506 +bus_check_new (BusContext *context, DBusError *error)
507 +{
508 +  BusCheck *check;
509 +
510 +  check = dbus_new(BusCheck, 1);
511 +  if (check == NULL)
512 +    {
513 +      BUS_SET_OOM(error);
514 +      return NULL;
515 +    }
516 +
517 +  check->refcount = 1;
518 +  check->context = context;
519 +  check->cynara = bus_cynara_new(check, error);
520 +  if (dbus_error_is_set(error))
521 +    {
522 +      dbus_free(check);
523 +      return NULL;
524 +    }
525 +
526 +  return check;
527 +}
528 +
529 +BusCheck *
530 +bus_check_ref (BusCheck *check)
531 +{
532 +  _dbus_assert (check->refcount > 0);
533 +  check->refcount += 1;
534 +
535 +  return check;
536 +}
537 +
538 +void
539 +bus_check_unref (BusCheck *check)
540 +{
541 +  _dbus_assert (check->refcount > 0);
542 +
543 +  check->refcount -= 1;
544 +
545 +  if (check->refcount == 0)
546 +    {
547 +      bus_cynara_unref(check->cynara);
548 +      dbus_free(check);
549 +    }
550 +}
551 +
552 +BusContext *
553 +bus_check_get_context (BusCheck *check)
554 +{
555 +  return check->context;
556 +}
557 +
558 +BusCynara *
559 +bus_check_get_cynara (BusCheck *check)
560 +{
561 +  return check->cynara;
562 +}
563 +
564 +BusResult
565 +bus_check_privilege (BusCheck *check,
566 +                     DBusMessage *message,
567 +                     DBusConnection *sender,
568 +                     DBusConnection *addressed_recipient,
569 +                     DBusConnection *proposed_recipient,
570 +                     const char *privilege,
571 +                     BusDeferredMessageStatus check_type,
572 +                     BusDeferredMessage **deferred_message)
573 +{
574 +  BusResult result = BUS_RESULT_FALSE;
575 +#ifdef DBUS_ENABLE_CYNARA
576 +  BusCynara *cynara;
577 +#endif
578 +  DBusConnection *connection;
579 +
580 +  connection = check_type == BUS_DEFERRED_MESSAGE_CHECK_RECEIVE ? proposed_recipient : sender;
581 +
582 +  if (!dbus_connection_get_is_connected(connection))
583 +    {
584 +      return BUS_RESULT_FALSE;
585 +    }
586 +
587 +  /* ask policy checkers */
588 +#ifdef DBUS_ENABLE_CYNARA
589 +  cynara = bus_check_get_cynara(check);
590 +  result = bus_cynara_check_privilege(cynara, message, sender, addressed_recipient,
591 +      proposed_recipient, privilege, check_type, deferred_message);
592 +#endif
593 +
594 +  if (result == BUS_RESULT_LATER && deferred_message != NULL)
595 +    {
596 +      (*deferred_message)->status |= check_type;
597 +    }
598 +  return result;
599 +}
600 +
601 +BusDeferredMessage *bus_deferred_message_new (DBusMessage *message,
602 +                                              DBusConnection *sender,
603 +                                              DBusConnection *addressed_recipient,
604 +                                              DBusConnection *proposed_recipient,
605 +                                              BusResult response)
606 +{
607 +  BusDeferredMessage *deferred_message;
608 +
609 +  deferred_message = dbus_new(BusDeferredMessage, 1);
610 +  if (deferred_message == NULL)
611 +    {
612 +      return NULL;
613 +    }
614 +
615 +  deferred_message->refcount = 1;
616 +  deferred_message->sender = sender != NULL ? dbus_connection_ref(sender) : NULL;
617 +  deferred_message->addressed_recipient = addressed_recipient != NULL ? dbus_connection_ref(addressed_recipient) : NULL;
618 +  deferred_message->proposed_recipient = proposed_recipient != NULL ? dbus_connection_ref(proposed_recipient) : NULL;
619 +  deferred_message->message = dbus_message_ref(message);
620 +  deferred_message->response = response;
621 +  deferred_message->status = 0;
622 +  deferred_message->full_dispatch = FALSE;
623 +  deferred_message->response_callback = NULL;
624 +
625 +  return deferred_message;
626 +}
627 +
628 +BusDeferredMessage *
629 +bus_deferred_message_ref (BusDeferredMessage *deferred_message)
630 +{
631 +  _dbus_assert (deferred_message->refcount > 0);
632 +  deferred_message->refcount += 1;
633 +  return deferred_message;
634 +}
635 +
636 +void
637 +bus_deferred_message_unref (BusDeferredMessage *deferred_message)
638 +{
639 +  _dbus_assert (deferred_message->refcount > 0);
640 +
641 +  deferred_message->refcount -= 1;
642 +
643 +   if (deferred_message->refcount == 0)
644 +     {
645 +       dbus_message_unref(deferred_message->message);
646 +       if (deferred_message->sender != NULL)
647 +           dbus_connection_unref(deferred_message->sender);
648 +       if (deferred_message->addressed_recipient != NULL)
649 +           dbus_connection_unref(deferred_message->addressed_recipient);
650 +       if (deferred_message->proposed_recipient != NULL)
651 +           dbus_connection_unref(deferred_message->proposed_recipient);
652 +       dbus_free(deferred_message);
653 +     }
654 +}
655 +
656 +void
657 +bus_deferred_message_response_received (BusDeferredMessage *deferred_message,
658 +                                        BusResult result)
659 +{
660 +  if (deferred_message->response_callback != NULL)
661 +    {
662 +      deferred_message->response_callback(deferred_message, result);
663 +    }
664 +}
665 diff --git a/bus/check.h b/bus/check.h
666 new file mode 100644
667 index 00000000..c3fcaf90
668 --- /dev/null
669 +++ b/bus/check.h
670 @@ -0,0 +1,68 @@
671 +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
672 +/* check.h  Bus security policy runtime check
673 + *
674 + * Copyright (C) 2014  Intel, Inc.
675 + * Copyright (c) 2014  Samsung Electronics, Ltd.
676 + *
677 + * Licensed under the Academic Free License version 2.1
678 + *
679 + * This program is free software; you can redistribute it and/or modify
680 + * it under the terms of the GNU General Public License as published by
681 + * the Free Software Foundation; either version 2 of the License, or
682 + * (at your option) any later version.
683 + *
684 + * This program is distributed in the hope that it will be useful,
685 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
686 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
687 + * GNU General Public License for more details.
688 + *
689 + * You should have received a copy of the GNU General Public License
690 + * along with this program; if not, write to the Free Software
691 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
692 + *
693 + */
694 +
695 +#ifndef BUS_CHECK_H
696 +#define BUS_CHECK_H
697 +
698 +#include "bus.h"
699 +#include "policy.h"
700 +
701 +
702 +typedef void (*BusCheckResponseFunc) (BusDeferredMessage *message,
703 +                                      BusResult result);
704 +
705 +typedef enum {
706 +  BUS_DEFERRED_MESSAGE_CHECK_SEND      = 1 << 0,
707 +  BUS_DEFERRED_MESSAGE_CHECK_RECEIVE   = 1 << 1,
708 +  BUS_DEFERRED_MESSAGE_CHECK_OWN       = 1 << 2,
709 +} BusDeferredMessageStatus;
710 +
711 +
712 +BusCheck   *bus_check_new         (BusContext *context,
713 +                                   DBusError *error);
714 +BusCheck   *bus_check_ref         (BusCheck *check);
715 +void        bus_check_unref       (BusCheck *check);
716 +
717 +BusContext *bus_check_get_context (BusCheck *check);
718 +BusCynara  *bus_check_get_cynara  (BusCheck *check);
719 +BusResult   bus_check_privilege   (BusCheck *check,
720 +                                   DBusMessage *message,
721 +                                   DBusConnection *sender,
722 +                                   DBusConnection *addressed_recipient,
723 +                                   DBusConnection *proposed_recipient,
724 +                                   const char *privilege,
725 +                                   BusDeferredMessageStatus check_type,
726 +                                   BusDeferredMessage **deferred_message);
727 +
728 +BusDeferredMessage *bus_deferred_message_new                (DBusMessage *message,
729 +                                                             DBusConnection *sender,
730 +                                                             DBusConnection *addressed_recipient,
731 +                                                             DBusConnection *proposed_recipient,
732 +                                                             BusResult response);
733 +
734 +BusDeferredMessage *bus_deferred_message_ref                (BusDeferredMessage *deferred_message);
735 +void                bus_deferred_message_unref              (BusDeferredMessage *deferred_message);
736 +void                bus_deferred_message_response_received  (BusDeferredMessage *deferred_message,
737 +                                                             BusResult result);
738 +#endif /* BUS_CHECK_H */
739 diff --git a/bus/config-parser-common.c b/bus/config-parser-common.c
740 index 5db6b289..ea25f5e6 100644
741 --- a/bus/config-parser-common.c
742 +++ b/bus/config-parser-common.c
743 @@ -75,6 +75,10 @@ bus_config_parser_element_name_to_type (const char *name)
744      {
745        return ELEMENT_DENY;
746      }
747 +  else if (strcmp (name, "check") == 0)
748 +    {
749 +      return ELEMENT_CHECK;
750 +    }
751    else if (strcmp (name, "servicehelper") == 0)
752      {
753        return ELEMENT_SERVICEHELPER;
754 @@ -159,6 +163,8 @@ bus_config_parser_element_type_to_name (ElementType type)
755        return "allow";
756      case ELEMENT_DENY:
757        return "deny";
758 +    case ELEMENT_CHECK:
759 +      return "check";
760      case ELEMENT_FORK:
761        return "fork";
762      case ELEMENT_PIDFILE:
763 diff --git a/bus/config-parser-common.h b/bus/config-parser-common.h
764 index 382a0141..9e026d10 100644
765 --- a/bus/config-parser-common.h
766 +++ b/bus/config-parser-common.h
767 @@ -36,6 +36,7 @@ typedef enum
768    ELEMENT_LIMIT,
769    ELEMENT_ALLOW,
770    ELEMENT_DENY,
771 +  ELEMENT_CHECK,
772    ELEMENT_FORK,
773    ELEMENT_PIDFILE,
774    ELEMENT_SERVICEDIR,
775 diff --git a/bus/config-parser.c b/bus/config-parser.c
776 index d9f6042c..a8c4ca5d 100644
777 --- a/bus/config-parser.c
778 +++ b/bus/config-parser.c
779 @@ -1172,7 +1172,7 @@ append_rule_from_element (BusConfigParser   *parser,
780                            const char        *element_name,
781                            const char       **attribute_names,
782                            const char       **attribute_values,
783 -                          dbus_bool_t        allow,
784 +                          BusPolicyRuleAccess access,
785                            DBusError         *error)
786  {
787    const char *log;
788 @@ -1195,6 +1195,7 @@ append_rule_from_element (BusConfigParser   *parser,
789    const char *own_prefix;
790    const char *user;
791    const char *group;
792 +  const char *privilege;
793  
794    BusPolicyRule *rule;
795    
796 @@ -1222,6 +1223,7 @@ append_rule_from_element (BusConfigParser   *parser,
797                            "user", &user,
798                            "group", &group,
799                            "log", &log,
800 +                          "privilege", &privilege,
801                            NULL))
802      return FALSE;
803  
804 @@ -1230,6 +1232,7 @@ append_rule_from_element (BusConfigParser   *parser,
805          receive_interface || receive_member || receive_error || receive_sender ||
806          receive_type || receive_path || eavesdrop ||
807          send_requested_reply || receive_requested_reply ||
808 +        privilege ||
809          own || own_prefix || user || group))
810      {
811        dbus_set_error (error, DBUS_ERROR_FAILED,
812 @@ -1246,7 +1249,30 @@ append_rule_from_element (BusConfigParser   *parser,
813                        element_name);
814        return FALSE;
815      }
816 -  
817 +
818 +  if (access == BUS_POLICY_RULE_ACCESS_CHECK)
819 +    {
820 +      if (privilege == NULL || !*privilege)
821 +        {
822 +          dbus_set_error (error, DBUS_ERROR_FAILED,
823 +                          "On element <%s>, you must specify the privilege to be checked.",
824 +                          element_name);
825 +          return FALSE;
826 +        }
827 +    }
828 +  else
829 +    {
830 +      if (privilege != NULL && *privilege)
831 +        {
832 +          dbus_set_error (error, DBUS_ERROR_FAILED,
833 +                          "On element <%s>, privilege %s is used outside of a check rule.",
834 +                          element_name, privilege);
835 +          return FALSE;
836 +        }
837 +      else
838 +        privilege = NULL; /* replace (potentially) empty string with NULL pointer, it wouldn't be used anyway */
839 +    }
840 +
841    /* Allowed combinations of elements are:
842     *
843     *   base, must be all send or all receive:
844 @@ -1420,7 +1446,7 @@ append_rule_from_element (BusConfigParser   *parser,
845            return FALSE;
846          }
847        
848 -      rule = bus_policy_rule_new (BUS_POLICY_RULE_SEND, allow); 
849 +      rule = bus_policy_rule_new (BUS_POLICY_RULE_SEND, access);
850        if (rule == NULL)
851          goto nomem;
852        
853 @@ -1502,7 +1528,7 @@ append_rule_from_element (BusConfigParser   *parser,
854            return FALSE;
855          }
856        
857 -      rule = bus_policy_rule_new (BUS_POLICY_RULE_RECEIVE, allow); 
858 +      rule = bus_policy_rule_new (BUS_POLICY_RULE_RECEIVE, access);
859        if (rule == NULL)
860          goto nomem;
861  
862 @@ -1532,7 +1558,7 @@ append_rule_from_element (BusConfigParser   *parser,
863      }
864    else if (own || own_prefix)
865      {
866 -      rule = bus_policy_rule_new (BUS_POLICY_RULE_OWN, allow); 
867 +      rule = bus_policy_rule_new (BUS_POLICY_RULE_OWN, access);
868        if (rule == NULL)
869          goto nomem;
870  
871 @@ -1558,7 +1584,7 @@ append_rule_from_element (BusConfigParser   *parser,
872      {      
873        if (IS_WILDCARD (user))
874          {
875 -          rule = bus_policy_rule_new (BUS_POLICY_RULE_USER, allow); 
876 +          rule = bus_policy_rule_new (BUS_POLICY_RULE_USER, access);
877            if (rule == NULL)
878              goto nomem;
879  
880 @@ -1573,7 +1599,7 @@ append_rule_from_element (BusConfigParser   *parser,
881        
882            if (_dbus_parse_unix_user_from_config (&username, &uid))
883              {
884 -              rule = bus_policy_rule_new (BUS_POLICY_RULE_USER, allow); 
885 +              rule = bus_policy_rule_new (BUS_POLICY_RULE_USER, access);
886                if (rule == NULL)
887                  goto nomem;
888  
889 @@ -1590,7 +1616,7 @@ append_rule_from_element (BusConfigParser   *parser,
890      {
891        if (IS_WILDCARD (group))
892          {
893 -          rule = bus_policy_rule_new (BUS_POLICY_RULE_GROUP, allow); 
894 +          rule = bus_policy_rule_new (BUS_POLICY_RULE_GROUP, access);
895            if (rule == NULL)
896              goto nomem;
897  
898 @@ -1605,7 +1631,7 @@ append_rule_from_element (BusConfigParser   *parser,
899            
900            if (_dbus_parse_unix_group_from_config (&groupname, &gid))
901              {
902 -              rule = bus_policy_rule_new (BUS_POLICY_RULE_GROUP, allow); 
903 +              rule = bus_policy_rule_new (BUS_POLICY_RULE_GROUP, access);
904                if (rule == NULL)
905                  goto nomem;
906  
907 @@ -1629,6 +1655,10 @@ append_rule_from_element (BusConfigParser   *parser,
908        _dbus_assert (pe != NULL);
909        _dbus_assert (pe->type == ELEMENT_POLICY);
910  
911 +      rule->privilege = _dbus_strdup (privilege);
912 +      if (privilege && !rule->privilege)
913 +        goto nomem;
914 +
915        switch (pe->d.policy.type)
916          {
917          case POLICY_IGNORED:
918 @@ -1703,7 +1733,7 @@ start_policy_child (BusConfigParser   *parser,
919      {
920        if (!append_rule_from_element (parser, element_name,
921                                       attribute_names, attribute_values,
922 -                                     TRUE, error))
923 +                                     BUS_POLICY_RULE_ACCESS_ALLOW, error))
924          return FALSE;
925        
926        if (push_element (parser, ELEMENT_ALLOW) == NULL)
927 @@ -1718,7 +1748,7 @@ start_policy_child (BusConfigParser   *parser,
928      {
929        if (!append_rule_from_element (parser, element_name,
930                                       attribute_names, attribute_values,
931 -                                     FALSE, error))
932 +                                     BUS_POLICY_RULE_ACCESS_DENY, error))
933          return FALSE;
934        
935        if (push_element (parser, ELEMENT_DENY) == NULL)
936 @@ -1727,6 +1757,21 @@ start_policy_child (BusConfigParser   *parser,
937            return FALSE;
938          }
939        
940 +      return TRUE;
941 +    }
942 +  else if (strcmp (element_name, "check") == 0)
943 +    {
944 +      if (!append_rule_from_element (parser, element_name,
945 +                                     attribute_names, attribute_values,
946 +                                     BUS_POLICY_RULE_ACCESS_CHECK, error))
947 +        return FALSE;
948 +
949 +      if (push_element (parser, ELEMENT_CHECK) == NULL)
950 +        {
951 +          BUS_SET_OOM (error);
952 +          return FALSE;
953 +        }
954 +
955        return TRUE;
956      }
957    else
958 @@ -2088,6 +2133,7 @@ bus_config_parser_end_element (BusConfigParser   *parser,
959      case ELEMENT_POLICY:
960      case ELEMENT_ALLOW:
961      case ELEMENT_DENY:
962 +    case ELEMENT_CHECK:
963      case ELEMENT_FORK:
964      case ELEMENT_SYSLOG:
965      case ELEMENT_KEEP_UMASK:
966 @@ -2397,6 +2443,7 @@ bus_config_parser_content (BusConfigParser   *parser,
967      case ELEMENT_POLICY:
968      case ELEMENT_ALLOW:
969      case ELEMENT_DENY:
970 +    case ELEMENT_CHECK:
971      case ELEMENT_FORK:
972      case ELEMENT_SYSLOG:
973      case ELEMENT_KEEP_UMASK:
974 @@ -2862,6 +2909,8 @@ do_load (const DBusString *full_path,
975    dbus_error_init (&error);
976  
977    parser = bus_config_load (full_path, TRUE, NULL, &error);
978 +  if (dbus_error_is_set (&error))
979 +    _dbus_verbose ("Failed to load file: %s\n", error.message);
980    if (parser == NULL)
981      {
982        _DBUS_ASSERT_ERROR_IS_SET (&error);
983 diff --git a/bus/connection.c b/bus/connection.c
984 index 02d6c220..eea50ecd 100644
985 --- a/bus/connection.c
986 +++ b/bus/connection.c
987 @@ -36,6 +36,10 @@
988  #include <dbus/dbus-timeout.h>
989  #include <dbus/dbus-connection-internal.h>
990  #include <dbus/dbus-internals.h>
991 +#ifdef DBUS_ENABLE_CYNARA
992 +#include <stdlib.h>
993 +#include <cynara-session.h>
994 +#endif
995  
996  /* Trim executed commands to this length; we want to keep logs readable */
997  #define MAX_LOG_COMMAND_LEN 50
998 @@ -116,6 +120,9 @@ typedef struct
999  
1000    /** non-NULL if and only if this is a monitor */
1001    DBusList *link_in_monitors;
1002 +#ifdef DBUS_ENABLE_CYNARA
1003 +  char *cynara_session_id;
1004 +#endif
1005  } BusConnectionData;
1006  
1007  static dbus_bool_t bus_pending_reply_expired (BusExpireList *list,
1008 @@ -129,8 +136,8 @@ static dbus_bool_t expire_incomplete_timeout (void *data);
1009  
1010  #define BUS_CONNECTION_DATA(connection) (dbus_connection_get_data ((connection), connection_data_slot))
1011  
1012 -static DBusLoop*
1013 -connection_get_loop (DBusConnection *connection)
1014 +DBusLoop*
1015 +bus_connection_get_loop (DBusConnection *connection)
1016  {
1017    BusConnectionData *d;
1018  
1019 @@ -354,7 +361,7 @@ add_connection_watch (DBusWatch      *watch,
1020  {
1021    DBusConnection *connection = data;
1022  
1023 -  return _dbus_loop_add_watch (connection_get_loop (connection), watch);
1024 +  return _dbus_loop_add_watch (bus_connection_get_loop (connection), watch);
1025  }
1026  
1027  static void
1028 @@ -363,7 +370,7 @@ remove_connection_watch (DBusWatch      *watch,
1029  {
1030    DBusConnection *connection = data;
1031    
1032 -  _dbus_loop_remove_watch (connection_get_loop (connection), watch);
1033 +  _dbus_loop_remove_watch (bus_connection_get_loop (connection), watch);
1034  }
1035  
1036  static void
1037 @@ -372,7 +379,7 @@ toggle_connection_watch (DBusWatch      *watch,
1038  {
1039    DBusConnection *connection = data;
1040  
1041 -  _dbus_loop_toggle_watch (connection_get_loop (connection), watch);
1042 +  _dbus_loop_toggle_watch (bus_connection_get_loop (connection), watch);
1043  }
1044  
1045  static dbus_bool_t
1046 @@ -381,7 +388,7 @@ add_connection_timeout (DBusTimeout    *timeout,
1047  {
1048    DBusConnection *connection = data;
1049    
1050 -  return _dbus_loop_add_timeout (connection_get_loop (connection), timeout);
1051 +  return _dbus_loop_add_timeout (bus_connection_get_loop (connection), timeout);
1052  }
1053  
1054  static void
1055 @@ -390,7 +397,7 @@ remove_connection_timeout (DBusTimeout    *timeout,
1056  {
1057    DBusConnection *connection = data;
1058    
1059 -  _dbus_loop_remove_timeout (connection_get_loop (connection), timeout);
1060 +  _dbus_loop_remove_timeout (bus_connection_get_loop (connection), timeout);
1061  }
1062  
1063  static void
1064 @@ -451,6 +458,10 @@ free_connection_data (void *data)
1065    
1066    dbus_free (d->name);
1067    
1068 +#ifdef DBUS_ENABLE_CYNARA
1069 +  free (d->cynara_session_id);
1070 +#endif
1071 +
1072    dbus_free (d);
1073  }
1074  
1075 @@ -1063,6 +1074,22 @@ bus_connection_get_policy (DBusConnection *connection)
1076    return d->policy;
1077  }
1078  
1079 +#ifdef DBUS_ENABLE_CYNARA
1080 +const char *bus_connection_get_cynara_session_id (DBusConnection *connection)
1081 +{
1082 +  BusConnectionData *d = BUS_CONNECTION_DATA (connection);
1083 +  _dbus_assert (d != NULL);
1084 +
1085 +  if (d->cynara_session_id == NULL)
1086 +    {
1087 +      unsigned long pid;
1088 +      if (dbus_connection_get_unix_process_id(connection, &pid))
1089 +        d->cynara_session_id = cynara_session_from_pid(pid);
1090 +    }
1091 +  return d->cynara_session_id;
1092 +}
1093 +#endif
1094 +
1095  static dbus_bool_t
1096  foreach_active (BusConnections               *connections,
1097                  BusConnectionForeachFunction  function,
1098 @@ -2289,6 +2316,7 @@ bus_transaction_send_from_driver (BusTransaction *transaction,
1099                                    DBusMessage    *message)
1100  {
1101    DBusError error = DBUS_ERROR_INIT;
1102 +  BusResult res;
1103  
1104    /* We have to set the sender to the driver, and have
1105     * to check security policy since it was not done in
1106 @@ -2326,9 +2354,11 @@ bus_transaction_send_from_driver (BusTransaction *transaction,
1107     * if we're actively capturing messages, it's nice to log that we
1108     * tried to send it and did not allow ourselves to do so.
1109     */
1110 -  if (!bus_context_check_security_policy (bus_transaction_get_context (transaction),
1111 -                                          transaction,
1112 -                                          NULL, connection, connection, message, &error))
1113 +  res = bus_context_check_security_policy (bus_transaction_get_context (transaction),
1114 +                                           transaction,
1115 +                                           NULL, connection, connection, message, &error,
1116 +                                           NULL);
1117 +  if (res == BUS_RESULT_FALSE)
1118      {
1119        if (!bus_transaction_capture_error_reply (transaction, &error, message))
1120          {
1121 @@ -2342,6 +2372,12 @@ bus_transaction_send_from_driver (BusTransaction *transaction,
1122        dbus_error_free (&error);
1123        return TRUE;
1124      }
1125 +  else if (res == BUS_RESULT_LATER)
1126 +    {
1127 +      _dbus_verbose ("Cannot delay sending message from bus driver, dropping it\n");
1128 +      dbus_error_free (&error);
1129 +      return TRUE;
1130 +    }
1131  
1132    return bus_transaction_send (transaction, connection, message);
1133  }
1134 diff --git a/bus/connection.h b/bus/connection.h
1135 index 8c68d0a0..a6e5dfde 100644
1136 --- a/bus/connection.h
1137 +++ b/bus/connection.h
1138 @@ -31,6 +31,7 @@
1139  typedef dbus_bool_t (* BusConnectionForeachFunction) (DBusConnection *connection, 
1140                                                        void           *data);
1141  
1142 +DBusLoop*       bus_connection_get_loop           (DBusConnection *connection);
1143  
1144  BusConnections* bus_connections_new               (BusContext                   *context);
1145  BusConnections* bus_connections_ref               (BusConnections               *connections);
1146 @@ -122,6 +123,9 @@ dbus_bool_t bus_connection_be_monitor (DBusConnection  *connection,
1147                                         BusTransaction  *transaction,
1148                                         DBusList       **rules,
1149                                         DBusError       *error);
1150 +#ifdef DBUS_ENABLE_CYNARA
1151 +const char *bus_connection_get_cynara_session_id (DBusConnection *connection);
1152 +#endif
1153  
1154  /* transaction API so we can send or not send a block of messages as a whole */
1155  
1156 diff --git a/bus/cynara.c b/bus/cynara.c
1157 new file mode 100644
1158 index 00000000..57a4c45c
1159 --- /dev/null
1160 +++ b/bus/cynara.c
1161 @@ -0,0 +1,374 @@
1162 +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
1163 +/* cynara.c  Cynara runtime privilege checking
1164 + *
1165 + * Copyright (c) 2014 Samsung Electronics, Ltd.
1166 + *
1167 + * Licensed under the Academic Free License version 2.1
1168 + *
1169 + * This program is free software; you can redistribute it and/or modify
1170 + * it under the terms of the GNU General Public License as published by
1171 + * the Free Software Foundation; either version 2 of the License, or
1172 + * (at your option) any later version.
1173 + *
1174 + * This program is distributed in the hope that it will be useful,
1175 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1176 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1177 + * GNU General Public License for more details.
1178 + *
1179 + * You should have received a copy of the GNU General Public License
1180 + * along with this program; if not, write to the Free Software
1181 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
1182 + *
1183 + */
1184 +
1185 +#include <config.h>
1186 +#include "cynara.h"
1187 +#include "check.h"
1188 +#include "utils.h"
1189 +
1190 +#include <stdio.h>
1191 +
1192 +#include <dbus/dbus.h>
1193 +#include <dbus/dbus-watch.h>
1194 +#include <dbus/dbus-connection-internal.h>
1195 +#include <bus/connection.h>
1196 +#ifdef DBUS_ENABLE_CYNARA
1197 +#include <cynara-client-async.h>
1198 +#endif
1199 +
1200 +
1201 +#ifdef DBUS_ENABLE_CYNARA
1202 +typedef struct BusCynara
1203 +{
1204 +  int refcount;
1205 +
1206 +  BusContext   *context;
1207 +  BusCheck     *check;
1208 +  cynara_async *cynara;
1209 +  DBusWatch    *cynara_watch;
1210 +} BusCynara;
1211 +
1212 +#define USE_CYNARA_CACHE 1
1213 +#ifdef USE_CYNARA_CACHE
1214 +#define CYNARA_CACHE_SIZE 1000
1215 +#endif
1216 +
1217 +static dbus_bool_t bus_cynara_watch_callback(DBusWatch *watch,
1218 +                                             unsigned int flags,
1219 +                                             void *data);
1220 +
1221 +static void status_callback(int old_fd,
1222 +                            int new_fd,
1223 +                            cynara_async_status status,
1224 +                            void *user_status_data);
1225 +static void bus_cynara_check_response_callback (cynara_check_id check_id,
1226 +                                                cynara_async_call_cause cause,
1227 +                                                int response,
1228 +                                                void *user_response_data);
1229 +#endif
1230 +
1231 +
1232 +BusCynara *
1233 +bus_cynara_new(BusCheck *check, DBusError *error)
1234 +{
1235 +#ifdef DBUS_ENABLE_CYNARA
1236 +  BusContext *context;
1237 +  BusCynara *cynara;
1238 +  cynara_async_configuration *conf = NULL;
1239 +  int ret;
1240 +
1241 +  cynara = dbus_new(BusCynara, 1);
1242 +  if (cynara == NULL)
1243 +    {
1244 +      BUS_SET_OOM(error);
1245 +      return NULL;
1246 +    }
1247 +
1248 +  context = bus_check_get_context(check);
1249 +
1250 +  cynara->refcount = 1;
1251 +  cynara->check = check;
1252 +  cynara->context = context;
1253 +  cynara->cynara_watch = NULL;
1254 +
1255 +  ret = cynara_async_configuration_create(&conf);
1256 +  if (ret != CYNARA_API_SUCCESS)
1257 +    {
1258 +      dbus_set_error (error, DBUS_ERROR_FAILED, "Failed to create Cynara configuration");
1259 +      goto out;
1260 +    }
1261 +
1262 +#ifdef CYNARA_CACHE_SIZE
1263 +  ret = cynara_async_configuration_set_cache_size(conf, CYNARA_CACHE_SIZE);
1264 +  if (ret != CYNARA_API_SUCCESS)
1265 +    {
1266 +      dbus_set_error (error, DBUS_ERROR_FAILED, "Failed to Cynara cache size");
1267 +      goto out;
1268 +    }
1269 +#endif
1270 +
1271 +  ret = cynara_async_initialize(&cynara->cynara, conf, &status_callback, cynara);
1272 +  if (ret != CYNARA_API_SUCCESS)
1273 +    {
1274 +      dbus_set_error (error, DBUS_ERROR_FAILED, "Failed to initialize Cynara client");
1275 +      goto out;
1276 +    }
1277 +
1278 +out:
1279 +  cynara_async_configuration_destroy(conf);
1280 +  if (ret != CYNARA_API_SUCCESS)
1281 +    {
1282 +      dbus_free(cynara);
1283 +      return NULL;
1284 +    }
1285 +
1286 +  return cynara;
1287 +#else
1288 +  return NULL;
1289 +#endif
1290 +}
1291 +
1292 +BusCynara *
1293 +bus_cynara_ref (BusCynara *cynara)
1294 +{
1295 +#ifdef DBUS_ENABLE_CYNARA
1296 +  _dbus_assert (cynara->refcount > 0);
1297 +  cynara->refcount += 1;
1298 +
1299 +  return cynara;
1300 +#else
1301 +  return NULL;
1302 +#endif
1303 +}
1304 +
1305 +void
1306 +bus_cynara_unref (BusCynara *cynara)
1307 +{
1308 +#ifdef DBUS_ENABLE_CYNARA
1309 +  _dbus_assert (cynara->refcount > 0);
1310 +
1311 +  cynara->refcount -= 1;
1312 +
1313 +  if (cynara->refcount == 0)
1314 +    {
1315 +      cynara_async_finish(cynara->cynara);
1316 +      dbus_free(cynara);
1317 +    }
1318 +#endif
1319 +}
1320 +
1321 +BusResult
1322 +bus_cynara_check_privilege (BusCynara *cynara,
1323 +                            DBusMessage *message,
1324 +                            DBusConnection *sender,
1325 +                            DBusConnection *addressed_recipient,
1326 +                            DBusConnection *proposed_recipient,
1327 +                            const char *privilege,
1328 +                            BusDeferredMessageStatus check_type,
1329 +                            BusDeferredMessage **deferred_message_param)
1330 +{
1331 +#ifdef DBUS_ENABLE_CYNARA
1332 +  int result;
1333 +  unsigned long uid;
1334 +  char *label;
1335 +  const char *session_id;
1336 +  char user[32];
1337 +  cynara_check_id check_id;
1338 +  DBusConnection *connection = check_type == BUS_DEFERRED_MESSAGE_CHECK_RECEIVE ? proposed_recipient : sender;
1339 +  BusDeferredMessage *deferred_message;
1340 +  BusResult ret;
1341 +
1342 +  _dbus_assert(connection != NULL);
1343 +
1344 +  if (dbus_connection_get_unix_user(connection, &uid) == FALSE)
1345 +      return BUS_RESULT_FALSE;
1346 +
1347 +  if (_dbus_connection_get_linux_security_label(connection, &label) == FALSE || label == NULL)
1348 +    {
1349 +      _dbus_warn("Failed to obtain security label for connection\n");
1350 +      return BUS_RESULT_FALSE;
1351 +    }
1352 +
1353 +  session_id = bus_connection_get_cynara_session_id (connection);
1354 +  if (session_id == NULL)
1355 +    {
1356 +      ret = BUS_RESULT_FALSE;
1357 +      goto out;
1358 +    }
1359 +
1360 +  snprintf(user, sizeof(user), "%lu", uid);
1361 +
1362 +#if USE_CYNARA_CACHE
1363 +  result = cynara_async_check_cache(cynara->cynara, label, session_id, user, privilege);
1364 +#else
1365 +  result = CYNARA_API_CACHE_MISS;
1366 +#endif
1367 +
1368 +  switch (result)
1369 +  {
1370 +  case CYNARA_API_ACCESS_ALLOWED:
1371 +    _dbus_verbose("Cynara: got ALLOWED answer from cache (client=%s session_id=%s user=%s privilege=%s)\n",
1372 +               label, session_id, user, privilege);
1373 +    ret = BUS_RESULT_TRUE;
1374 +    break;
1375 +
1376 +  case CYNARA_API_ACCESS_DENIED:
1377 +    _dbus_verbose("Cynara: got DENIED answer from cache (client=%s session_id=%s user=%s privilege=%s)\n",
1378 +               label, session_id, user, privilege);
1379 +    ret = BUS_RESULT_FALSE;
1380 +    break;
1381 +
1382 +  case CYNARA_API_CACHE_MISS:
1383 +     deferred_message = bus_deferred_message_new(message, sender, addressed_recipient,
1384 +         proposed_recipient, BUS_RESULT_LATER);
1385 +     if (deferred_message == NULL)
1386 +       {
1387 +         _dbus_verbose("Failed to allocate memory for deferred message\n");
1388 +         ret = BUS_RESULT_FALSE;
1389 +         goto out;
1390 +       }
1391 +
1392 +    /* callback is supposed to unref deferred_message*/
1393 +    result = cynara_async_create_request(cynara->cynara, label, session_id, user, privilege, &check_id,
1394 +        &bus_cynara_check_response_callback, deferred_message);
1395 +    if (result == CYNARA_API_SUCCESS)
1396 +      {
1397 +        _dbus_verbose("Created Cynara request: client=%s session_id=%s user=%s privilege=%s check_id=%u "
1398 +            "deferred_message=%p\n", label, session_id, user, privilege, (unsigned int)check_id, deferred_message);
1399 +        if (deferred_message_param != NULL)
1400 +          *deferred_message_param = deferred_message;
1401 +        ret = BUS_RESULT_LATER;
1402 +      }
1403 +    else
1404 +      {
1405 +        _dbus_verbose("Error on cynara request create: %i\n", result);
1406 +        bus_deferred_message_unref(deferred_message);
1407 +        ret = BUS_RESULT_FALSE;
1408 +      }
1409 +    break;
1410 +  default:
1411 +    _dbus_verbose("Error when accessing Cynara cache: %i\n", result);
1412 +    ret = BUS_RESULT_FALSE;
1413 +  }
1414 +out:
1415 +  dbus_free(label);
1416 +  return ret;
1417 +
1418 +#else
1419 +  return BUS_RESULT_FALSE;
1420 +#endif
1421 +}
1422 +
1423 +
1424 +
1425 +#ifdef DBUS_ENABLE_CYNARA
1426 +static void
1427 +status_callback(int old_fd, int new_fd, cynara_async_status status,
1428 +                void *user_status_data)
1429 +{
1430 +  BusCynara *cynara = (BusCynara *)user_status_data;
1431 +  DBusLoop *loop = bus_context_get_loop(cynara->context);
1432 +
1433 +  if (cynara->cynara_watch != NULL)
1434 +    {
1435 +      _dbus_loop_remove_watch(loop, cynara->cynara_watch);
1436 +      _dbus_watch_invalidate(cynara->cynara_watch);
1437 +      _dbus_watch_unref(cynara->cynara_watch);
1438 +      cynara->cynara_watch = NULL;
1439 +    }
1440 +
1441 +  if (new_fd != -1)
1442 +    {
1443 +      unsigned int flags;
1444 +      DBusWatch *watch;
1445 +
1446 +      switch (status)
1447 +      {
1448 +      case CYNARA_STATUS_FOR_READ:
1449 +        flags = DBUS_WATCH_READABLE;
1450 +        break;
1451 +      case CYNARA_STATUS_FOR_RW:
1452 +        flags = DBUS_WATCH_READABLE | DBUS_WATCH_WRITABLE;
1453 +        break;
1454 +      default:
1455 +        /* Cynara passed unknown status - warn and add RW watch */
1456 +        _dbus_verbose("Cynara passed unknown status value: 0x%08X\n", (unsigned int)status);
1457 +        flags = DBUS_WATCH_READABLE | DBUS_WATCH_WRITABLE;
1458 +        break;
1459 +      }
1460 +
1461 +      watch = _dbus_watch_new(new_fd, flags, TRUE, &bus_cynara_watch_callback, cynara, NULL);
1462 +      if (watch != NULL)
1463 +        {
1464 +          if (_dbus_loop_add_watch(loop, watch) == TRUE)
1465 +            {
1466 +              cynara->cynara_watch = watch;
1467 +              return;
1468 +            }
1469 +
1470 +          _dbus_watch_invalidate(watch);
1471 +          _dbus_watch_unref(watch);
1472 +        }
1473 +
1474 +      /* It seems like not much can be done at this point. Cynara events won't be processed
1475 +       * until next Cynara function call triggering status callback */
1476 +      _dbus_verbose("Failed to add dbus watch\n");
1477 +    }
1478 +}
1479 +
1480 +static dbus_bool_t
1481 +bus_cynara_watch_callback(DBusWatch    *watch,
1482 +                          unsigned int  flags,
1483 +                          void         *data)
1484 +{
1485 +  BusCynara *cynara = (BusCynara *)data;
1486 +  int result = cynara_async_process(cynara->cynara);
1487 +  if (result != CYNARA_API_SUCCESS)
1488 +      _dbus_verbose("cynara_async_process returned %d\n", result);
1489 +
1490 +  return result != CYNARA_API_OUT_OF_MEMORY ? TRUE : FALSE;
1491 +}
1492 +
1493 +static inline const char *
1494 +call_cause_to_string(cynara_async_call_cause cause)
1495 +{
1496 +  switch (cause)
1497 +  {
1498 +  case CYNARA_CALL_CAUSE_ANSWER:
1499 +    return "ANSWER";
1500 +  case CYNARA_CALL_CAUSE_CANCEL:
1501 +    return "CANCEL";
1502 +  case CYNARA_CALL_CAUSE_FINISH:
1503 +    return "FINSIH";
1504 +  case CYNARA_CALL_CAUSE_SERVICE_NOT_AVAILABLE:
1505 +    return "SERVICE NOT AVAILABLE";
1506 +  default:
1507 +    return "INVALID";
1508 +  }
1509 +}
1510 +
1511 +static void
1512 +bus_cynara_check_response_callback (cynara_check_id check_id,
1513 +                                    cynara_async_call_cause cause,
1514 +                                    int response,
1515 +                                    void *user_response_data)
1516 +{
1517 +  BusDeferredMessage *deferred_message = user_response_data;
1518 +  BusResult result;
1519 +
1520 +  _dbus_verbose("Cynara callback: check_id=%u, cause=%s response=%i response_data=%p\n",
1521 +      (unsigned int)check_id, call_cause_to_string(cause), response, user_response_data);
1522 +
1523 +  if (deferred_message == NULL)
1524 +    return;
1525 +
1526 +  if (cause == CYNARA_CALL_CAUSE_ANSWER && response == CYNARA_API_ACCESS_ALLOWED)
1527 +    result = BUS_RESULT_TRUE;
1528 +  else
1529 +    result = BUS_RESULT_FALSE;
1530 +
1531 +  bus_deferred_message_response_received(deferred_message, result);
1532 +  bus_deferred_message_unref(deferred_message);
1533 +}
1534 +
1535 +#endif /* DBUS_ENABLE_CYNARA */
1536 diff --git a/bus/cynara.h b/bus/cynara.h
1537 new file mode 100644
1538 index 00000000..c4728bb7
1539 --- /dev/null
1540 +++ b/bus/cynara.h
1541 @@ -0,0 +1,37 @@
1542 +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
1543 +/* cynara.h  Cynara runtime privilege checking
1544 + *
1545 + * Copyright (c) 2014 Samsung Electronics, Ltd.
1546 + *
1547 + * Licensed under the Academic Free License version 2.1
1548 + *
1549 + * This program is free software; you can redistribute it and/or modify
1550 + * it under the terms of the GNU General Public License as published by
1551 + * the Free Software Foundation; either version 2 of the License, or
1552 + * (at your option) any later version.
1553 + *
1554 + * This program is distributed in the hope that it will be useful,
1555 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1556 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1557 + * GNU General Public License for more details.
1558 + *
1559 + * You should have received a copy of the GNU General Public License
1560 + * along with this program; if not, write to the Free Software
1561 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
1562 + *
1563 + */
1564 +
1565 +#include "bus.h"
1566 +#include "check.h"
1567 +
1568 +BusCynara *bus_cynara_new             (BusCheck *check, DBusError *error);
1569 +BusCynara *bus_cynara_ref             (BusCynara *cynara);
1570 +void       bus_cynara_unref           (BusCynara *cynara);
1571 +BusResult  bus_cynara_check_privilege (BusCynara *cynara,
1572 +                                       DBusMessage *message,
1573 +                                       DBusConnection *sender,
1574 +                                       DBusConnection *addressed_recipient,
1575 +                                       DBusConnection *proposed_recipient,
1576 +                                       const char *privilege,
1577 +                                       BusDeferredMessageStatus check_type,
1578 +                                       BusDeferredMessage **deferred_message);
1579 diff --git a/bus/dispatch.c b/bus/dispatch.c
1580 index edfa1b44..05be3bdf 100644
1581 --- a/bus/dispatch.c
1582 +++ b/bus/dispatch.c
1583 @@ -25,6 +25,7 @@
1584  
1585  #include <config.h>
1586  #include "dispatch.h"
1587 +#include "check.h"
1588  #include "connection.h"
1589  #include "driver.h"
1590  #include "services.h"
1591 @@ -64,13 +65,17 @@ send_one_message (DBusConnection *connection,
1592                    DBusError      *error)
1593  {
1594    DBusError stack_error = DBUS_ERROR_INIT;
1595 +  BusDeferredMessage *deferred_message;
1596 +  BusResult result;
1597  
1598 -  if (!bus_context_check_security_policy (context, transaction,
1599 +  result = bus_context_check_security_policy (context, transaction,
1600                                            sender,
1601                                            addressed_recipient,
1602                                            connection,
1603                                            message,
1604 -                                          &stack_error))
1605 +                                          &stack_error,
1606 +                                          &deferred_message);
1607 +  if (result != BUS_RESULT_TRUE)
1608      {
1609        if (!bus_transaction_capture_error_reply (transaction, &stack_error,
1610                                                  message))
1611 @@ -129,6 +134,7 @@ bus_dispatch_matches (BusTransaction *transaction,
1612    BusMatchmaker *matchmaker;
1613    DBusList *link;
1614    BusContext *context;
1615 +  BusDeferredMessage *deferred_message;
1616  
1617    _DBUS_ASSERT_ERROR_IS_CLEAR (error);
1618  
1619 @@ -144,11 +150,21 @@ bus_dispatch_matches (BusTransaction *transaction,
1620    /* First, send the message to the addressed_recipient, if there is one. */
1621    if (addressed_recipient != NULL)
1622      {
1623 -      if (!bus_context_check_security_policy (context, transaction,
1624 -                                              sender, addressed_recipient,
1625 -                                              addressed_recipient,
1626 -                                              message, error))
1627 +      BusResult res;
1628 +      res = bus_context_check_security_policy (context, transaction,
1629 +                                               sender, addressed_recipient,
1630 +                                               addressed_recipient,
1631 +                                               message, error,
1632 +                                               &deferred_message);
1633 +      if (res == BUS_RESULT_FALSE)
1634          return FALSE;
1635 +      else if (res == BUS_RESULT_LATER)
1636 +        {
1637 +          dbus_set_error (error,
1638 +                          DBUS_ERROR_ACCESS_DENIED,
1639 +                          "Rejecting message because time is needed to check security policy");
1640 +          return FALSE;
1641 +        }
1642  
1643        if (dbus_message_contains_unix_fds (message) &&
1644            !dbus_connection_can_send_type (addressed_recipient,
1645 @@ -379,12 +395,24 @@ bus_dispatch (DBusConnection *connection,
1646    if (service_name &&
1647        strcmp (service_name, DBUS_SERVICE_DBUS) == 0) /* to bus driver */
1648      {
1649 -      if (!bus_context_check_security_policy (context, transaction,
1650 -                                              connection, NULL, NULL, message, &error))
1651 +      BusDeferredMessage *deferred_message;
1652 +      BusResult res;
1653 +      res = bus_context_check_security_policy (context, transaction,
1654 +                                               connection, NULL, NULL, message, &error,
1655 +                                               &deferred_message);
1656 +      if (res == BUS_RESULT_FALSE)
1657          {
1658            _dbus_verbose ("Security policy rejected message\n");
1659            goto out;
1660          }
1661 +      else if (res == BUS_RESULT_LATER)
1662 +        {
1663 +          dbus_set_error (&error,
1664 +                          DBUS_ERROR_ACCESS_DENIED,
1665 +                          "Rejecting message because time is needed to check security policy");
1666 +          _dbus_verbose ("Security policy needs time to check policy. Dropping message\n");
1667 +          goto out;
1668 +        }
1669  
1670        _dbus_verbose ("Giving message to %s\n", DBUS_SERVICE_DBUS);
1671        if (!bus_driver_handle_message (connection, transaction, message, &error))
1672 diff --git a/bus/policy.c b/bus/policy.c
1673 index 082f3853..bcade176 100644
1674 --- a/bus/policy.c
1675 +++ b/bus/policy.c
1676 @@ -22,6 +22,7 @@
1677   */
1678  
1679  #include <config.h>
1680 +#include "check.h"
1681  #include "policy.h"
1682  #include "services.h"
1683  #include "test.h"
1684 @@ -32,7 +33,7 @@
1685  
1686  BusPolicyRule*
1687  bus_policy_rule_new (BusPolicyRuleType type,
1688 -                     dbus_bool_t       allow)
1689 +                     BusPolicyRuleAccess access)
1690  {
1691    BusPolicyRule *rule;
1692  
1693 @@ -42,7 +43,7 @@ bus_policy_rule_new (BusPolicyRuleType type,
1694  
1695    rule->type = type;
1696    rule->refcount = 1;
1697 -  rule->allow = allow;
1698 +  rule->access = access;
1699  
1700    switch (rule->type)
1701      {
1702 @@ -54,18 +55,19 @@ bus_policy_rule_new (BusPolicyRuleType type,
1703        break;
1704      case BUS_POLICY_RULE_SEND:
1705        rule->d.send.message_type = DBUS_MESSAGE_TYPE_INVALID;
1706 -
1707        /* allow rules default to TRUE (only requested replies allowed)
1708 +       * check rules default to TRUE (only requested replies are checked)
1709         * deny rules default to FALSE (only unrequested replies denied)
1710         */
1711 -      rule->d.send.requested_reply = rule->allow;
1712 +      rule->d.send.requested_reply = rule->access != BUS_POLICY_RULE_ACCESS_DENY;
1713        break;
1714      case BUS_POLICY_RULE_RECEIVE:
1715        rule->d.receive.message_type = DBUS_MESSAGE_TYPE_INVALID;
1716        /* allow rules default to TRUE (only requested replies allowed)
1717 +       * check rules default to TRUE (only requested replies are checked)
1718         * deny rules default to FALSE (only unrequested replies denied)
1719         */
1720 -      rule->d.receive.requested_reply = rule->allow;
1721 +      rule->d.receive.requested_reply = rule->access != BUS_POLICY_RULE_ACCESS_DENY;
1722        break;
1723      case BUS_POLICY_RULE_OWN:
1724        break;
1725 @@ -117,7 +119,8 @@ bus_policy_rule_unref (BusPolicyRule *rule)
1726          case BUS_POLICY_RULE_GROUP:
1727            break;
1728          }
1729 -      
1730 +
1731 +      dbus_free (rule->privilege);
1732        dbus_free (rule);
1733      }
1734  }
1735 @@ -427,7 +430,10 @@ list_allows_user (dbus_bool_t           def,
1736        else
1737          continue;
1738  
1739 -      allowed = rule->allow;
1740 +      /* We don't intend to support <check user="..." /> and <check group="..." />
1741 +         rules. They are treated like deny.
1742 +      */
1743 +      allowed = rule->access == BUS_POLICY_RULE_ACCESS_ALLOW;
1744      }
1745    
1746    return allowed;
1747 @@ -862,18 +868,23 @@ bus_client_policy_append_rule (BusClientPolicy *policy,
1748    return TRUE;
1749  }
1750  
1751 -dbus_bool_t
1752 -bus_client_policy_check_can_send (BusClientPolicy *policy,
1753 -                                  BusRegistry     *registry,
1754 -                                  dbus_bool_t      requested_reply,
1755 -                                  DBusConnection  *receiver,
1756 -                                  DBusMessage     *message,
1757 -                                  dbus_int32_t    *toggles,
1758 -                                  dbus_bool_t     *log)
1759 +BusResult
1760 +bus_client_policy_check_can_send (DBusConnection      *sender,
1761 +                                  BusClientPolicy     *policy,
1762 +                                  BusRegistry         *registry,
1763 +                                  dbus_bool_t          requested_reply,
1764 +                                  DBusConnection      *addressed_recipient,
1765 +                                  DBusConnection      *receiver,
1766 +                                  DBusMessage         *message,
1767 +                                  dbus_int32_t        *toggles,
1768 +                                  dbus_bool_t         *log,
1769 +                                  const char         **privilege_param,
1770 +                                  BusDeferredMessage **deferred_message)
1771  {
1772    DBusList *link;
1773 -  dbus_bool_t allowed;
1774 -  
1775 +  BusResult result;
1776 +  const char *privilege;
1777 +
1778    /* policy->rules is in the order the rules appeared
1779     * in the config file, i.e. last rule that applies wins
1780     */
1781 @@ -881,7 +892,7 @@ bus_client_policy_check_can_send (BusClientPolicy *policy,
1782    _dbus_verbose ("  (policy) checking send rules\n");
1783    *toggles = 0;
1784    
1785 -  allowed = FALSE;
1786 +  result = BUS_RESULT_FALSE;
1787    link = _dbus_list_get_first_link (&policy->rules);
1788    while (link != NULL)
1789      {
1790 @@ -912,13 +923,14 @@ bus_client_policy_check_can_send (BusClientPolicy *policy,
1791        /* If it's a reply, the requested_reply flag kicks in */
1792        if (dbus_message_get_reply_serial (message) != 0)
1793          {
1794 -          /* for allow, requested_reply=true means the rule applies
1795 -           * only when reply was requested. requested_reply=false means
1796 -           * always allow.
1797 +          /* for allow or check requested_reply=true means the rule applies
1798 +           * only when reply was requested. requested_reply=false means the
1799 +           * rule always applies
1800             */
1801 -          if (!requested_reply && rule->allow && rule->d.send.requested_reply && !rule->d.send.eavesdrop)
1802 +          if (!requested_reply && rule->access != BUS_POLICY_RULE_ACCESS_DENY && rule->d.send.requested_reply && !rule->d.send.eavesdrop)
1803              {
1804 -              _dbus_verbose ("  (policy) skipping allow rule since it only applies to requested replies and does not allow eavesdropping\n");
1805 +              _dbus_verbose ("  (policy) skipping %s rule since it only applies to requested replies and does not allow eavesdropping\n",
1806 +                  rule->access == BUS_POLICY_RULE_ACCESS_ALLOW ? "allow" : "check");
1807                continue;
1808              }
1809  
1810 @@ -926,7 +938,7 @@ bus_client_policy_check_can_send (BusClientPolicy *policy,
1811             * when the reply was not requested. requested_reply=true means the
1812             * rule always applies.
1813             */
1814 -          if (requested_reply && !rule->allow && !rule->d.send.requested_reply)
1815 +          if (requested_reply && rule->access == BUS_POLICY_RULE_ACCESS_DENY && !rule->d.send.requested_reply)
1816              {
1817                _dbus_verbose ("  (policy) skipping deny rule since it only applies to unrequested replies\n");
1818                continue;
1819 @@ -949,13 +961,15 @@ bus_client_policy_check_can_send (BusClientPolicy *policy,
1820            /* The interface is optional in messages. For allow rules, if the message
1821             * has no interface we want to skip the rule (and thus not allow);
1822             * for deny rules, if the message has no interface we want to use the
1823 -           * rule (and thus deny).
1824 +           * rule (and thus deny). Check rules are meant to be used like allow
1825 +           * rules (they can grant access, but not remove it), so we treat it like
1826 +           * allow here.
1827             */
1828            dbus_bool_t no_interface;
1829  
1830            no_interface = dbus_message_get_interface (message) == NULL;
1831            
1832 -          if ((no_interface && rule->allow) ||
1833 +          if ((no_interface && rule->access != BUS_POLICY_RULE_ACCESS_DENY) ||
1834                (!no_interface && 
1835                 strcmp (dbus_message_get_interface (message),
1836                         rule->d.send.interface) != 0))
1837 @@ -1029,33 +1043,63 @@ bus_client_policy_check_can_send (BusClientPolicy *policy,
1838          }
1839  
1840        /* Use this rule */
1841 -      allowed = rule->allow;
1842 +      switch (rule->access)
1843 +        {
1844 +        case BUS_POLICY_RULE_ACCESS_ALLOW:
1845 +          result = BUS_RESULT_TRUE;
1846 +          break;
1847 +        case BUS_POLICY_RULE_ACCESS_DENY:
1848 +          result = BUS_RESULT_FALSE;
1849 +          break;
1850 +        case BUS_POLICY_RULE_ACCESS_CHECK:
1851 +          result = BUS_RESULT_LATER;
1852 +          privilege = rule->privilege;
1853 +          break;
1854 +        }
1855 +
1856        *log = rule->d.send.log;
1857        (*toggles)++;
1858  
1859 -      _dbus_verbose ("  (policy) used rule, allow now = %d\n",
1860 -                     allowed);
1861 +      _dbus_verbose ("  (policy) used rule, result now = %d\n",
1862 +                     (int)(intptr_t)result);
1863      }
1864  
1865 -  return allowed;
1866 +  if (result == BUS_RESULT_LATER)
1867 +    {
1868 +      BusContext *context = bus_connection_get_context(sender);
1869 +      BusCheck *check = bus_context_get_check(context);
1870 +
1871 +      result = bus_check_privilege(check, message, sender, addressed_recipient, receiver,
1872 +          privilege, BUS_DEFERRED_MESSAGE_CHECK_SEND, deferred_message);
1873 +    }
1874 +  else
1875 +    privilege = NULL;
1876 +
1877 +  if (privilege_param != NULL)
1878 +    *privilege_param = privilege;
1879 +
1880 +  return result;
1881  }
1882  
1883  /* See docs on what the args mean on bus_context_check_security_policy()
1884   * comment
1885   */
1886 -dbus_bool_t
1887 -bus_client_policy_check_can_receive (BusClientPolicy *policy,
1888 -                                     BusRegistry     *registry,
1889 -                                     dbus_bool_t      requested_reply,
1890 -                                     DBusConnection  *sender,
1891 -                                     DBusConnection  *addressed_recipient,
1892 -                                     DBusConnection  *proposed_recipient,
1893 -                                     DBusMessage     *message,
1894 -                                     dbus_int32_t    *toggles)
1895 +BusResult
1896 +bus_client_policy_check_can_receive (BusClientPolicy     *policy,
1897 +                                     BusRegistry         *registry,
1898 +                                     dbus_bool_t          requested_reply,
1899 +                                     DBusConnection      *sender,
1900 +                                     DBusConnection      *addressed_recipient,
1901 +                                     DBusConnection      *proposed_recipient,
1902 +                                     DBusMessage         *message,
1903 +                                     dbus_int32_t        *toggles,
1904 +                                     const char         **privilege_param,
1905 +                                     BusDeferredMessage **deferred_message)
1906  {
1907    DBusList *link;
1908 -  dbus_bool_t allowed;
1909    dbus_bool_t eavesdropping;
1910 +  BusResult result;
1911 +  const char *privilege;
1912  
1913    eavesdropping =
1914      addressed_recipient != proposed_recipient &&
1915 @@ -1068,7 +1112,7 @@ bus_client_policy_check_can_receive (BusClientPolicy *policy,
1916    _dbus_verbose ("  (policy) checking receive rules, eavesdropping = %d\n", eavesdropping);
1917    *toggles = 0;
1918    
1919 -  allowed = FALSE;
1920 +  result = BUS_RESULT_FALSE;
1921    link = _dbus_list_get_first_link (&policy->rules);
1922    while (link != NULL)
1923      {
1924 @@ -1091,19 +1135,21 @@ bus_client_policy_check_can_receive (BusClientPolicy *policy,
1925              }
1926          }
1927  
1928 -      /* for allow, eavesdrop=false means the rule doesn't apply when
1929 -       * eavesdropping. eavesdrop=true means always allow.
1930 +
1931 +      /* for allow or check, eavesdrop=false means the rule doesn't apply when
1932 +       * eavesdropping. eavesdrop=true means the rule always applies
1933         */
1934 -      if (eavesdropping && rule->allow && !rule->d.receive.eavesdrop)
1935 +      if (eavesdropping && rule->access != BUS_POLICY_RULE_ACCESS_DENY && !rule->d.receive.eavesdrop)
1936          {
1937 -          _dbus_verbose ("  (policy) skipping allow rule since it doesn't apply to eavesdropping\n");
1938 +          _dbus_verbose ("  (policy) skipping %s rule since it doesn't apply to eavesdropping\n",
1939 +              rule->access == BUS_POLICY_RULE_ACCESS_ALLOW ? "allow" : "check");
1940            continue;
1941          }
1942  
1943        /* for deny, eavesdrop=true means the rule applies only when
1944         * eavesdropping; eavesdrop=false means always deny.
1945         */
1946 -      if (!eavesdropping && !rule->allow && rule->d.receive.eavesdrop)
1947 +      if (!eavesdropping && rule->access == BUS_POLICY_RULE_ACCESS_DENY && rule->d.receive.eavesdrop)
1948          {
1949            _dbus_verbose ("  (policy) skipping deny rule since it only applies to eavesdropping\n");
1950            continue;
1951 @@ -1112,13 +1158,14 @@ bus_client_policy_check_can_receive (BusClientPolicy *policy,
1952        /* If it's a reply, the requested_reply flag kicks in */
1953        if (dbus_message_get_reply_serial (message) != 0)
1954          {
1955 -          /* for allow, requested_reply=true means the rule applies
1956 -           * only when reply was requested. requested_reply=false means
1957 -           * always allow.
1958 +          /* for allow or check requested_reply=true means the rule applies
1959 +           * only when reply was requested. requested_reply=false means the
1960 +           * rule always applies
1961             */
1962 -          if (!requested_reply && rule->allow && rule->d.receive.requested_reply && !rule->d.receive.eavesdrop)
1963 +          if (!requested_reply && rule->access != BUS_POLICY_RULE_ACCESS_DENY && rule->d.send.requested_reply && !rule->d.send.eavesdrop)
1964              {
1965 -              _dbus_verbose ("  (policy) skipping allow rule since it only applies to requested replies and does not allow eavesdropping\n");
1966 +              _dbus_verbose ("  (policy) skipping %s rule since it only applies to requested replies and does not allow eavesdropping\n",
1967 +                  rule->access == BUS_POLICY_RULE_ACCESS_DENY ? "allow" : "deny");
1968                continue;
1969              }
1970  
1971 @@ -1126,7 +1173,7 @@ bus_client_policy_check_can_receive (BusClientPolicy *policy,
1972             * when the reply was not requested. requested_reply=true means the
1973             * rule always applies.
1974             */
1975 -          if (requested_reply && !rule->allow && !rule->d.receive.requested_reply)
1976 +          if (requested_reply && rule->access == BUS_POLICY_RULE_ACCESS_DENY && !rule->d.receive.requested_reply)
1977              {
1978                _dbus_verbose ("  (policy) skipping deny rule since it only applies to unrequested replies\n");
1979                continue;
1980 @@ -1149,13 +1196,13 @@ bus_client_policy_check_can_receive (BusClientPolicy *policy,
1981            /* The interface is optional in messages. For allow rules, if the message
1982             * has no interface we want to skip the rule (and thus not allow);
1983             * for deny rules, if the message has no interface we want to use the
1984 -           * rule (and thus deny).
1985 +           * rule (and thus deny). Check rules are treated like allow rules.
1986             */
1987            dbus_bool_t no_interface;
1988  
1989            no_interface = dbus_message_get_interface (message) == NULL;
1990            
1991 -          if ((no_interface && rule->allow) ||
1992 +          if ((no_interface && rule->access != BUS_POLICY_RULE_ACCESS_DENY) ||
1993                (!no_interface &&
1994                 strcmp (dbus_message_get_interface (message),
1995                         rule->d.receive.interface) != 0))
1996 @@ -1230,14 +1277,42 @@ bus_client_policy_check_can_receive (BusClientPolicy *policy,
1997          }
1998        
1999        /* Use this rule */
2000 -      allowed = rule->allow;
2001 +      switch (rule->access)
2002 +      {
2003 +        case BUS_POLICY_RULE_ACCESS_ALLOW:
2004 +          result = BUS_RESULT_TRUE;
2005 +          break;
2006 +        case BUS_POLICY_RULE_ACCESS_DENY:
2007 +          result = BUS_RESULT_FALSE;
2008 +          break;
2009 +        case BUS_POLICY_RULE_ACCESS_CHECK:
2010 +          result = BUS_RESULT_LATER;
2011 +          privilege = rule->privilege;
2012 +          break;
2013 +      }
2014 +
2015        (*toggles)++;
2016  
2017 -      _dbus_verbose ("  (policy) used rule, allow now = %d\n",
2018 -                     allowed);
2019 +      _dbus_verbose ("  (policy) used rule, result now = %d\n",
2020 +                     (int)(intptr_t)result);
2021      }
2022  
2023 -  return allowed;
2024 +
2025 +  if (result == BUS_RESULT_LATER)
2026 +    {
2027 +      BusContext *context = bus_connection_get_context(proposed_recipient);
2028 +      BusCheck *check = bus_context_get_check(context);
2029 +
2030 +      result = bus_check_privilege(check, message, sender, addressed_recipient, proposed_recipient,
2031 +                 privilege, BUS_DEFERRED_MESSAGE_CHECK_RECEIVE, deferred_message);
2032 +    }
2033 +  else
2034 +      privilege = NULL;
2035 +
2036 +  if (privilege_param != NULL)
2037 +     *privilege_param = privilege;
2038 +
2039 +  return result;
2040  }
2041  
2042  
2043 @@ -1289,7 +1364,7 @@ bus_rules_check_can_own (DBusList *rules,
2044          }
2045  
2046        /* Use this rule */
2047 -      allowed = rule->allow;
2048 +      allowed = rule->access == BUS_POLICY_RULE_ACCESS_ALLOW;
2049      }
2050  
2051    return allowed;
2052 diff --git a/bus/policy.h b/bus/policy.h
2053 index d1d3e72b..e9f193af 100644
2054 --- a/bus/policy.h
2055 +++ b/bus/policy.h
2056 @@ -39,6 +39,14 @@ typedef enum
2057    BUS_POLICY_RULE_GROUP
2058  } BusPolicyRuleType;
2059  
2060 +typedef enum
2061 +{
2062 +  BUS_POLICY_RULE_ACCESS_DENY,
2063 +  BUS_POLICY_RULE_ACCESS_ALLOW,
2064 +  /** runtime check resulting in allow or deny */
2065 +  BUS_POLICY_RULE_ACCESS_CHECK
2066 +} BusPolicyRuleAccess;
2067 +
2068  /** determines whether the rule affects a connection, or some global item */
2069  #define BUS_POLICY_RULE_IS_PER_CLIENT(rule) (!((rule)->type == BUS_POLICY_RULE_USER || \
2070                                                 (rule)->type == BUS_POLICY_RULE_GROUP))
2071 @@ -49,8 +57,9 @@ struct BusPolicyRule
2072    
2073    BusPolicyRuleType type;
2074  
2075 -  unsigned int allow : 1; /**< #TRUE if this allows, #FALSE if it denies */
2076 -  
2077 +  unsigned int access : 2; /**< BusPolicyRuleAccess */
2078 +  char *privilege; /**< for BUS_POLICY_RULE_ACCESS_CHECK */
2079 +
2080    union
2081    {
2082      struct
2083 @@ -106,7 +115,7 @@ struct BusPolicyRule
2084  };
2085  
2086  BusPolicyRule* bus_policy_rule_new   (BusPolicyRuleType type,
2087 -                                      dbus_bool_t       allow);
2088 +                                      BusPolicyRuleAccess access);
2089  BusPolicyRule* bus_policy_rule_ref   (BusPolicyRule    *rule);
2090  void           bus_policy_rule_unref (BusPolicyRule    *rule);
2091  
2092 @@ -140,21 +149,27 @@ dbus_bool_t      bus_policy_merge                 (BusPolicy        *policy,
2093  BusClientPolicy* bus_client_policy_new               (void);
2094  BusClientPolicy* bus_client_policy_ref               (BusClientPolicy  *policy);
2095  void             bus_client_policy_unref             (BusClientPolicy  *policy);
2096 -dbus_bool_t      bus_client_policy_check_can_send    (BusClientPolicy  *policy,
2097 -                                                      BusRegistry      *registry,
2098 -                                                      dbus_bool_t       requested_reply,
2099 -                                                      DBusConnection   *receiver,
2100 -                                                      DBusMessage      *message,
2101 -                                                      dbus_int32_t     *toggles,
2102 -                                                      dbus_bool_t      *log);
2103 -dbus_bool_t      bus_client_policy_check_can_receive (BusClientPolicy  *policy,
2104 -                                                      BusRegistry      *registry,
2105 -                                                      dbus_bool_t       requested_reply,
2106 -                                                      DBusConnection   *sender,
2107 -                                                      DBusConnection   *addressed_recipient,
2108 -                                                      DBusConnection   *proposed_recipient,
2109 -                                                      DBusMessage      *message,
2110 -                                                      dbus_int32_t     *toggles);
2111 +BusResult        bus_client_policy_check_can_send    (DBusConnection      *sender,
2112 +                                                      BusClientPolicy     *policy,
2113 +                                                      BusRegistry         *registry,
2114 +                                                      dbus_bool_t          requested_reply,
2115 +                                                      DBusConnection      *addressed_recipient,
2116 +                                                      DBusConnection      *receiver,
2117 +                                                      DBusMessage         *message,
2118 +                                                      dbus_int32_t        *toggles,
2119 +                                                      dbus_bool_t         *log,
2120 +                                                      const char         **privilege_param,
2121 +                                                      BusDeferredMessage **deferred_message);
2122 +BusResult        bus_client_policy_check_can_receive (BusClientPolicy     *policy,
2123 +                                                      BusRegistry         *registry,
2124 +                                                      dbus_bool_t          requested_reply,
2125 +                                                      DBusConnection      *sender,
2126 +                                                      DBusConnection      *addressed_recipient,
2127 +                                                      DBusConnection      *proposed_recipient,
2128 +                                                      DBusMessage         *message,
2129 +                                                      dbus_int32_t        *toggles,
2130 +                                                      const char         **privilege_param,
2131 +                                                      BusDeferredMessage **deferred_message);
2132  dbus_bool_t      bus_client_policy_check_can_own     (BusClientPolicy  *policy,
2133                                                        const DBusString *service_name);
2134  dbus_bool_t      bus_client_policy_append_rule       (BusClientPolicy  *policy,
2135 diff --git a/configure.ac b/configure.ac
2136 index 71e3515c..f3a2ffc1 100644
2137 --- a/configure.ac
2138 +++ b/configure.ac
2139 @@ -1873,6 +1873,17 @@ AC_ARG_ENABLE([user-session],
2140  AM_CONDITIONAL([DBUS_ENABLE_USER_SESSION],
2141    [test "x$enable_user_session" = xyes])
2142  
2143 +#enable cynara integration
2144 +AC_ARG_ENABLE([cynara], [AS_HELP_STRING([--enable-cynara], [enable Cynara integration])], [], [enable_cynara=no])
2145 +if test "x$enable_cynara" = xyes; then
2146 +  PKG_CHECK_MODULES([CYNARA], [cynara-client-async >= 0.6.0 cynara-session >= 0.6.0],
2147 +     [AC_DEFINE([DBUS_ENABLE_CYNARA], [1], [Define to enable Cynara privilege checks in dbus-daemon])],
2148 +     [AC_MSG_ERROR([libcynara-client-async and cynara-session are required to enable Cynara integration])])
2149 +fi
2150 +
2151 +AC_SUBST([CYNARA_CFLAGS])
2152 +AC_SUBST([CYNARA_LIBS])
2153 +
2154  AC_CONFIG_FILES([
2155  Doxyfile
2156  dbus/Version
2157 @@ -1952,6 +1963,7 @@ echo "
2158          Building bus stats API:   ${enable_stats}
2159          Building SELinux support: ${have_selinux}
2160          Building AppArmor support: ${have_apparmor}
2161 +        Building Cynara support:  ${enable_cynara}
2162          Building inotify support: ${have_inotify}
2163          Building kqueue support:  ${have_kqueue}
2164          Building systemd support: ${have_systemd}
2165 diff --git a/test/Makefile.am b/test/Makefile.am
2166 index 914dd7f2..86882537 100644
2167 --- a/test/Makefile.am
2168 +++ b/test/Makefile.am
2169 @@ -341,6 +341,7 @@ in_data = \
2170         data/valid-config-files/debug-allow-all.conf.in \
2171         data/valid-config-files/finite-timeout.conf.in \
2172         data/valid-config-files/forbidding.conf.in \
2173 +       data/valid-config-files/debug-check-some.conf.in \
2174         data/valid-config-files/incoming-limit.conf.in \
2175         data/valid-config-files/multi-user.conf.in \
2176         data/valid-config-files/systemd-activation.conf.in \
2177 diff --git a/test/data/invalid-config-files/badcheck-1.conf b/test/data/invalid-config-files/badcheck-1.conf
2178 new file mode 100644
2179 index 00000000..fad9f502
2180 --- /dev/null
2181 +++ b/test/data/invalid-config-files/badcheck-1.conf
2182 @@ -0,0 +1,9 @@
2183 +<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
2184 + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
2185 +<busconfig>
2186 +  <user>mybususer</user>
2187 +  <listen>unix:path=/foo/bar</listen>
2188 +  <policy context="default">
2189 +    <allow privilege="foo" send_destination="*"/> <!-- extra privilege="foo" -->
2190 +  </policy>
2191 +</busconfig>
2192 diff --git a/test/data/invalid-config-files/badcheck-2.conf b/test/data/invalid-config-files/badcheck-2.conf
2193 new file mode 100644
2194 index 00000000..63c7ef25
2195 --- /dev/null
2196 +++ b/test/data/invalid-config-files/badcheck-2.conf
2197 @@ -0,0 +1,9 @@
2198 +<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
2199 + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
2200 +<busconfig>
2201 +  <user>mybususer</user>
2202 +  <listen>unix:path=/foo/bar</listen>
2203 +  <policy context="default">
2204 +    <check send_destination="*"/> <!-- missing privilege="foo" -->
2205 +  </policy>
2206 +</busconfig>
2207 diff --git a/test/data/valid-config-files/check-1.conf b/test/data/valid-config-files/check-1.conf
2208 new file mode 100644
2209 index 00000000..ad714733
2210 --- /dev/null
2211 +++ b/test/data/valid-config-files/check-1.conf
2212 @@ -0,0 +1,9 @@
2213 +<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
2214 + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
2215 +<busconfig>
2216 +  <user>mybususer</user>
2217 +  <listen>unix:path=/foo/bar</listen>
2218 +  <policy context="default">
2219 +    <check privilege="foo" send_destination="*"/>
2220 +  </policy>
2221 +</busconfig>
2222 diff --git a/test/data/valid-config-files/debug-check-some.conf.in b/test/data/valid-config-files/debug-check-some.conf.in
2223 new file mode 100644
2224 index 00000000..47ee8548
2225 --- /dev/null
2226 +++ b/test/data/valid-config-files/debug-check-some.conf.in
2227 @@ -0,0 +1,18 @@
2228 +<!-- Bus that listens on a debug pipe and doesn't create any restrictions -->
2229 +
2230 +<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
2231 + "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
2232 +<busconfig>
2233 +  <listen>debug-pipe:name=test-server</listen>
2234 +  <listen>@TEST_LISTEN@</listen>
2235 +  <servicedir>@DBUS_TEST_DATA@/valid-service-files</servicedir>
2236 +  <policy context="default">
2237 +    <allow send_interface="*"/>
2238 +    <allow receive_interface="*"/>
2239 +    <allow own="*"/>
2240 +    <allow user="*"/>
2241 +
2242 +    <deny send_interface="org.freedesktop.TestSuite" send_member="Echo"/>
2243 +    <check privilege="foo" send_interface="org.freedesktop.TestSuite" send_member="Echo"/>
2244 +  </policy>
2245 +</busconfig>
2246 diff --git a/tools/dbus-send.c b/tools/dbus-send.c
2247 index 0dc1f5b3..76ddab3f 100644
2248 --- a/tools/dbus-send.c
2249 +++ b/tools/dbus-send.c
2250 @@ -458,7 +458,7 @@ main (int argc, char *argv[])
2251        char *arg;
2252        char *c;
2253        int type;
2254 -      int secondary_type;
2255 +      int secondary_type = 0;
2256        int container_type;
2257        DBusMessageIter *target_iter;
2258        DBusMessageIter container_iter;
2259 -- 
2260 2.14.3
2261