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