$(ADT_LIBS) \
$(NETWORK_libs) \
$(LIBSMACK_LIBS) \
+ $(CYNARA_LIBS) \
$(NULL)
DBUS_LAUNCHER_LIBS = \
-DDBUS_COMPILATION \
-DDBUS_STATIC_BUILD \
$(LIBSMACK_CFLAGS) \
+ $(CYNARA_CFLAGS) \
$(NULL)
# if assertions are enabled, improve backtraces
audit.h \
bus.c \
bus.h \
+ check.c \
+ check.h \
config-parser.c \
config-parser.h \
config-parser-common.c \
config-parser-common.h \
connection.c \
connection.h \
+ cynara.c \
+ cynara.h \
desktop-file.c \
desktop-file.h \
$(DIR_WATCH_SOURCE) \
#include "apparmor.h"
#include "audit.h"
#include "dir-watch.h"
+#include "check.h"
#include <dbus/dbus-list.h>
#include <dbus/dbus-hash.h>
#include <dbus/dbus-credentials.h>
BusRegistry *registry;
BusPolicy *policy;
BusMatchmaker *matchmaker;
+ BusCheck *check;
BusLimits limits;
DBusRLimit *initial_fd_limit;
unsigned int fork : 1;
bus_audit_init (context);
+ context->check = bus_check_new(context, error);
+ if (context->check == NULL)
+ goto failed;
+
dbus_server_free_data_slot (&server_data_slot);
return context;
bus_context_shutdown (context);
+ if (context->check)
+ {
+ bus_check_unref(context->check);
+ context->check = NULL;
+ }
+
if (context->connections)
{
bus_connections_unref (context->connections);
return context->loop;
}
+BusCheck*
+bus_context_get_check (BusContext *context)
+{
+ return context->check;
+}
+
dbus_bool_t
bus_context_allow_unix_user (BusContext *context,
unsigned long uid)
DBusConnection *proposed_recipient,
dbus_bool_t requested_reply,
dbus_bool_t log,
+ const char *privilege,
DBusError *error)
{
DBusError stack_error = DBUS_ERROR_INIT;
dbus_set_error (&stack_error, error_name,
"%s, %d matched rules; type=\"%s\", sender=\"%s\" (%s) "
"interface=\"%s\" member=\"%s\" error name=\"%s\" "
- "requested_reply=\"%d\" destination=\"%s\" (%s)",
+ "requested_reply=\"%d\" destination=\"%s\" "
+ "privilege=\"%s\" (%s)",
complaint,
matched_rules,
dbus_message_type_to_string (dbus_message_get_type (message)),
nonnull (dbus_message_get_error_name (message), "(unset)"),
requested_reply,
nonnull (dbus_message_get_destination (message), DBUS_SERVICE_DBUS),
+ nonnull (privilege, "(n/a)"),
proposed_recipient_loginfo);
/* If we hit OOM while setting the error, this will syslog "out of memory"
* NULL for addressed_recipient may mean the bus driver, or may mean
* no destination was specified in the message (e.g. a signal).
*/
-dbus_bool_t
-bus_context_check_security_policy (BusContext *context,
- BusTransaction *transaction,
- DBusConnection *sender,
- DBusConnection *addressed_recipient,
- DBusConnection *proposed_recipient,
- DBusMessage *message,
- DBusError *error)
+BusResult
+bus_context_check_security_policy (BusContext *context,
+ BusTransaction *transaction,
+ DBusConnection *sender,
+ DBusConnection *addressed_recipient,
+ DBusConnection *proposed_recipient,
+ DBusMessage *message,
+ DBusError *error,
+ BusDeferredMessage **deferred_message)
{
const char *src, *dest;
BusClientPolicy *sender_policy;
dbus_bool_t log;
int type;
dbus_bool_t requested_reply;
+ const char *privilege;
type = dbus_message_get_type (message);
src = dbus_message_get_sender (message);
dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
"Message bus will not accept messages of unknown type\n");
- return FALSE;
+ return BUS_RESULT_FALSE;
}
requested_reply = FALSE;
if (dbus_error_is_set (&error2))
{
dbus_move_error (&error2, error);
- return FALSE;
+ return BUS_RESULT_FALSE;
}
}
}
complain_about_message (context, DBUS_ERROR_ACCESS_DENIED,
"An SELinux policy prevents this sender from sending this "
"message to this recipient",
- 0, message, sender, proposed_recipient, FALSE, FALSE, error);
+ 0, message, sender, proposed_recipient, FALSE, FALSE, NULL, error);
_dbus_verbose ("SELinux security check denying send to service\n");
}
{
_dbus_verbose ("security check allowing %s message\n",
"Hello");
- return TRUE;
+ return BUS_RESULT_TRUE;
}
else
{
"Client tried to send a message other than %s without being registered",
"Hello");
- return FALSE;
+ return BUS_RESULT_FALSE;
}
}
}
(proposed_recipient == NULL && recipient_policy == NULL));
log = FALSE;
- if (sender_policy &&
- !bus_client_policy_check_can_send (sender_policy,
- context->registry,
- requested_reply,
- proposed_recipient,
- message, &toggles, &log))
- {
- complain_about_message (context, DBUS_ERROR_ACCESS_DENIED,
- "Rejected send message", toggles,
- message, sender, proposed_recipient, requested_reply,
- (addressed_recipient == proposed_recipient), error);
- _dbus_verbose ("security policy disallowing message due to sender policy\n");
- return FALSE;
- }
+ if (sender_policy) {
+ switch (bus_client_policy_check_can_send (sender,
+ sender_policy,
+ context->registry,
+ requested_reply,
+ addressed_recipient,
+ proposed_recipient,
+ message, &toggles, &log, &privilege,
+ deferred_message))
+ {
+ case BUS_RESULT_TRUE:
+ break;
+ case BUS_RESULT_FALSE:
+ complain_about_message (context, DBUS_ERROR_ACCESS_DENIED,
+ "Rejected send message", toggles,
+ message, sender, proposed_recipient, requested_reply,
+ (addressed_recipient == proposed_recipient), privilege,
+ error);
+ _dbus_verbose ("security policy disallowing message due to sender policy\n");
+ return BUS_RESULT_FALSE;
+ break;
+ case BUS_RESULT_LATER:
+ return BUS_RESULT_LATER;
+ break;
+ }
+ }
if (log)
{
complain_about_message (context, DBUS_ERROR_ACCESS_DENIED,
"Would reject message", toggles,
message, sender, proposed_recipient, requested_reply,
- TRUE, NULL);
+ TRUE, privilege, NULL);
}
- if (recipient_policy &&
- !bus_client_policy_check_can_receive (recipient_policy,
- context->registry,
- requested_reply,
- sender,
- addressed_recipient, proposed_recipient,
- message, &toggles))
- {
- complain_about_message (context, DBUS_ERROR_ACCESS_DENIED,
- "Rejected receive message", toggles,
- message, sender, proposed_recipient, requested_reply,
- (addressed_recipient == proposed_recipient), error);
- _dbus_verbose ("security policy disallowing message due to recipient policy\n");
- return FALSE;
- }
+ if (recipient_policy) {
+ switch (bus_client_policy_check_can_receive (recipient_policy,
+ context->registry,
+ requested_reply,
+ sender,
+ addressed_recipient, proposed_recipient,
+ message, &toggles, &privilege, deferred_message))
+ {
+ case BUS_RESULT_TRUE:
+ break;
+ case BUS_RESULT_FALSE:
+ complain_about_message(context, DBUS_ERROR_ACCESS_DENIED,
+ "Rejected receive message", toggles, message, sender,
+ proposed_recipient, requested_reply,
+ (addressed_recipient == proposed_recipient), privilege, error);
+ _dbus_verbose(
+ "security policy disallowing message due to recipient policy\n");
+ return BUS_RESULT_FALSE;
+ case BUS_RESULT_LATER:
+ return BUS_RESULT_LATER;
+ }
+ }
/* See if limits on size have been exceeded */
if (proposed_recipient &&
{
complain_about_message (context, DBUS_ERROR_LIMITS_EXCEEDED,
"Rejected: destination has a full message queue",
- 0, message, sender, proposed_recipient, requested_reply, TRUE,
+ 0, message, sender, proposed_recipient, requested_reply, TRUE, NULL,
error);
_dbus_verbose ("security policy disallowing message due to full message queue\n");
- return FALSE;
+ return BUS_RESULT_FALSE;
}
/* Record that we will allow a reply here in the future (don't
message, error))
{
_dbus_verbose ("Failed to record reply expectation or problem with the message expecting a reply\n");
- return FALSE;
+ return BUS_RESULT_FALSE;
}
_dbus_verbose ("security policy allowing message\n");
- return TRUE;
+ return BUS_RESULT_TRUE;
}
void
typedef struct BusTransaction BusTransaction;
typedef struct BusMatchmaker BusMatchmaker;
typedef struct BusMatchRule BusMatchRule;
+typedef struct BusCheck BusCheck;
+typedef struct BusDeferredMessage BusDeferredMessage;
+typedef struct BusCynara BusCynara;
+
+/**
+ * This uses BUS_RESULT_TRUE = 0 != TRUE intentionally, to trigger
+ * runtime failures where code uses a simple boolean check or
+ * comparison with TRUE/FALSE or returns TRUE/FALSE when it should use
+ * one of these enums. Such broken code unfortunately does not trigger
+ * compile time errors in C.
+ */
+typedef enum {
+ /** operation allowed or succeeded */
+ BUS_RESULT_TRUE,
+ /** operation denied or failed */
+ BUS_RESULT_FALSE,
+ /** no result yet, ask again later */
+ BUS_RESULT_LATER
+} BusResult;
typedef struct
{
BusActivation* bus_context_get_activation (BusContext *context);
BusMatchmaker* bus_context_get_matchmaker (BusContext *context);
DBusLoop* bus_context_get_loop (BusContext *context);
+BusCheck * bus_context_get_check (BusContext *context);
dbus_bool_t bus_context_allow_unix_user (BusContext *context,
unsigned long uid);
dbus_bool_t bus_context_allow_windows_user (BusContext *context,
const char *name,
const char *msg,
...) _DBUS_GNUC_PRINTF (5, 6);
-dbus_bool_t bus_context_check_security_policy (BusContext *context,
- BusTransaction *transaction,
- DBusConnection *sender,
- DBusConnection *addressed_recipient,
- DBusConnection *proposed_recipient,
- DBusMessage *message,
- DBusError *error);
void bus_context_check_all_watches (BusContext *context);
+BusResult bus_context_check_security_policy (BusContext *context,
+ BusTransaction *transaction,
+ DBusConnection *sender,
+ DBusConnection *addressed_recipient,
+ DBusConnection *proposed_recipient,
+ DBusMessage *message,
+ DBusError *error,
+ BusDeferredMessage **deferred_message);
#endif /* BUS_BUS_H */
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* check.c Bus security policy runtime check
+ *
+ * Copyright (C) 2014 Intel, Inc.
+ * Copyright (c) 2014 Samsung Electronics, Ltd.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <config.h>
+#include "check.h"
+#include "connection.h"
+#include "dispatch.h"
+#include "cynara.h"
+#include "utils.h"
+#include <dbus/dbus-connection-internal.h>
+#include <dbus/dbus-message-internal.h>
+#include <dbus/dbus-internals.h>
+
+
+typedef struct BusCheck
+{
+ int refcount;
+
+ BusContext *context;
+ BusCynara *cynara;
+} BusCheck;
+
+typedef struct BusDeferredMessage
+{
+ int refcount;
+
+ DBusMessage *message;
+ DBusConnection *sender;
+ DBusConnection *proposed_recipient;
+ DBusConnection *addressed_recipient;
+ dbus_bool_t full_dispatch;
+ BusDeferredMessageStatus status;
+ BusResult response;
+ BusCheckResponseFunc response_callback;
+} BusDeferredMessage;
+
+BusCheck *
+bus_check_new (BusContext *context, DBusError *error)
+{
+ BusCheck *check;
+
+ check = dbus_new(BusCheck, 1);
+ if (check == NULL)
+ {
+ BUS_SET_OOM(error);
+ return NULL;
+ }
+
+ check->refcount = 1;
+ check->context = context;
+ check->cynara = bus_cynara_new(check, error);
+ if (dbus_error_is_set(error))
+ {
+ dbus_free(check);
+ return NULL;
+ }
+
+ return check;
+}
+
+BusCheck *
+bus_check_ref (BusCheck *check)
+{
+ _dbus_assert (check->refcount > 0);
+ check->refcount += 1;
+
+ return check;
+}
+
+void
+bus_check_unref (BusCheck *check)
+{
+ _dbus_assert (check->refcount > 0);
+
+ check->refcount -= 1;
+
+ if (check->refcount == 0)
+ {
+ bus_cynara_unref(check->cynara);
+ dbus_free(check);
+ }
+}
+
+BusContext *
+bus_check_get_context (BusCheck *check)
+{
+ return check->context;
+}
+
+BusCynara *
+bus_check_get_cynara (BusCheck *check)
+{
+ return check->cynara;
+}
+
+BusResult
+bus_check_privilege (BusCheck *check,
+ DBusMessage *message,
+ DBusConnection *sender,
+ DBusConnection *addressed_recipient,
+ DBusConnection *proposed_recipient,
+ const char *privilege,
+ BusDeferredMessageStatus check_type,
+ BusDeferredMessage **deferred_message)
+{
+ BusResult result = BUS_RESULT_FALSE;
+ BusCynara *cynara;
+ DBusConnection *connection;
+
+ connection = check_type == BUS_DEFERRED_MESSAGE_CHECK_RECEIVE ? proposed_recipient : sender;
+
+ if (!dbus_connection_get_is_connected(connection))
+ {
+ return BUS_RESULT_FALSE;
+ }
+
+ /* ask policy checkers */
+#ifdef DBUS_ENABLE_CYNARA
+ cynara = bus_check_get_cynara(check);
+ result = bus_cynara_check_privilege(cynara, message, sender, addressed_recipient,
+ proposed_recipient, privilege, check_type, deferred_message);
+#endif
+
+ if (result == BUS_RESULT_LATER && deferred_message != NULL)
+ {
+ (*deferred_message)->status |= check_type;
+ }
+ return result;
+}
+
+BusDeferredMessage *bus_deferred_message_new (DBusMessage *message,
+ DBusConnection *sender,
+ DBusConnection *addressed_recipient,
+ DBusConnection *proposed_recipient,
+ BusResult response)
+{
+ BusDeferredMessage *deferred_message;
+
+ deferred_message = dbus_new(BusDeferredMessage, 1);
+ if (deferred_message == NULL)
+ {
+ return NULL;
+ }
+
+ deferred_message->refcount = 1;
+ deferred_message->sender = sender != NULL ? dbus_connection_ref(sender) : NULL;
+ deferred_message->addressed_recipient = addressed_recipient != NULL ? dbus_connection_ref(addressed_recipient) : NULL;
+ deferred_message->proposed_recipient = proposed_recipient != NULL ? dbus_connection_ref(proposed_recipient) : NULL;
+ deferred_message->message = dbus_message_ref(message);
+ deferred_message->response = response;
+ deferred_message->status = 0;
+ deferred_message->full_dispatch = FALSE;
+ deferred_message->response_callback = NULL;
+
+ return deferred_message;
+}
+
+BusDeferredMessage *
+bus_deferred_message_ref (BusDeferredMessage *deferred_message)
+{
+ _dbus_assert (deferred_message->refcount > 0);
+ deferred_message->refcount += 1;
+ return deferred_message;
+}
+
+void
+bus_deferred_message_unref (BusDeferredMessage *deferred_message)
+{
+ _dbus_assert (deferred_message->refcount > 0);
+
+ deferred_message->refcount -= 1;
+
+ if (deferred_message->refcount == 0)
+ {
+ dbus_message_unref(deferred_message->message);
+ if (deferred_message->sender != NULL)
+ dbus_connection_unref(deferred_message->sender);
+ if (deferred_message->addressed_recipient != NULL)
+ dbus_connection_unref(deferred_message->addressed_recipient);
+ if (deferred_message->proposed_recipient != NULL)
+ dbus_connection_unref(deferred_message->proposed_recipient);
+ dbus_free(deferred_message);
+ }
+}
+
+void
+bus_deferred_message_response_received (BusDeferredMessage *deferred_message,
+ BusResult result)
+{
+ if (deferred_message->response_callback != NULL)
+ {
+ deferred_message->response_callback(deferred_message, result);
+ }
+}
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* check.h Bus security policy runtime check
+ *
+ * Copyright (C) 2014 Intel, Inc.
+ * Copyright (c) 2014 Samsung Electronics, Ltd.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef BUS_CHECK_H
+#define BUS_CHECK_H
+
+#include "bus.h"
+#include "policy.h"
+
+
+typedef void (*BusCheckResponseFunc) (BusDeferredMessage *message,
+ BusResult result);
+
+typedef enum {
+ BUS_DEFERRED_MESSAGE_CHECK_SEND = 1 << 0,
+ BUS_DEFERRED_MESSAGE_CHECK_RECEIVE = 1 << 1,
+ BUS_DEFERRED_MESSAGE_CHECK_OWN = 1 << 2,
+} BusDeferredMessageStatus;
+
+
+BusCheck *bus_check_new (BusContext *context,
+ DBusError *error);
+BusCheck *bus_check_ref (BusCheck *check);
+void bus_check_unref (BusCheck *check);
+
+BusContext *bus_check_get_context (BusCheck *check);
+BusCynara *bus_check_get_cynara (BusCheck *check);
+BusResult bus_check_privilege (BusCheck *check,
+ DBusMessage *message,
+ DBusConnection *sender,
+ DBusConnection *addressed_recipient,
+ DBusConnection *proposed_recipient,
+ const char *privilege,
+ BusDeferredMessageStatus check_type,
+ BusDeferredMessage **deferred_message);
+
+BusDeferredMessage *bus_deferred_message_new (DBusMessage *message,
+ DBusConnection *sender,
+ DBusConnection *addressed_recipient,
+ DBusConnection *proposed_recipient,
+ BusResult response);
+
+BusDeferredMessage *bus_deferred_message_ref (BusDeferredMessage *deferred_message);
+void bus_deferred_message_unref (BusDeferredMessage *deferred_message);
+void bus_deferred_message_response_received (BusDeferredMessage *deferred_message,
+ BusResult result);
+#endif /* BUS_CHECK_H */
#include <dbus/dbus-timeout.h>
#include <dbus/dbus-connection-internal.h>
#include <dbus/dbus-internals.h>
+#ifdef DBUS_ENABLE_CYNARA
+#include <stdlib.h>
+#include <cynara-session.h>
+#endif
/* Trim executed commands to this length; we want to keep logs readable */
#define MAX_LOG_COMMAND_LEN 50
/** non-NULL if and only if this is a monitor */
DBusList *link_in_monitors;
+#ifdef DBUS_ENABLE_CYNARA
+ char *cynara_session_id;
+#endif
} BusConnectionData;
static dbus_bool_t bus_pending_reply_expired (BusExpireList *list,
#define BUS_CONNECTION_DATA(connection) (dbus_connection_get_data ((connection), connection_data_slot))
-static DBusLoop*
-connection_get_loop (DBusConnection *connection)
+DBusLoop*
+bus_connection_get_loop (DBusConnection *connection)
{
BusConnectionData *d;
{
DBusConnection *connection = data;
- return _dbus_loop_add_watch (connection_get_loop (connection), watch);
+ return _dbus_loop_add_watch (bus_connection_get_loop (connection), watch);
}
static void
{
DBusConnection *connection = data;
- _dbus_loop_remove_watch (connection_get_loop (connection), watch);
+ _dbus_loop_remove_watch (bus_connection_get_loop (connection), watch);
}
static void
{
DBusConnection *connection = data;
- _dbus_loop_toggle_watch (connection_get_loop (connection), watch);
+ _dbus_loop_toggle_watch (bus_connection_get_loop (connection), watch);
}
static dbus_bool_t
{
DBusConnection *connection = data;
- return _dbus_loop_add_timeout (connection_get_loop (connection), timeout);
+ return _dbus_loop_add_timeout (bus_connection_get_loop (connection), timeout);
}
static void
{
DBusConnection *connection = data;
- _dbus_loop_remove_timeout (connection_get_loop (connection), timeout);
+ _dbus_loop_remove_timeout (bus_connection_get_loop (connection), timeout);
}
static void
dbus_free (d->name);
+#ifdef DBUS_ENABLE_CYNARA
+ free (d->cynara_session_id);
+#endif
+
dbus_free (d);
}
return d->policy;
}
+#ifdef DBUS_ENABLE_CYNARA
+const char *bus_connection_get_cynara_session_id (DBusConnection *connection)
+{
+ BusConnectionData *d = BUS_CONNECTION_DATA (connection);
+ _dbus_assert (d != NULL);
+
+ if (d->cynara_session_id == NULL)
+ {
+ unsigned long pid;
+ if (dbus_connection_get_unix_process_id(connection, &pid))
+ d->cynara_session_id = cynara_session_from_pid(pid);
+ }
+ return d->cynara_session_id;
+}
+#endif
+
static dbus_bool_t
foreach_active (BusConnections *connections,
BusConnectionForeachFunction function,
* if we're actively capturing messages, it's nice to log that we
* tried to send it and did not allow ourselves to do so.
*/
- if (!bus_context_check_security_policy (bus_transaction_get_context (transaction),
- transaction,
- NULL, connection, connection, message, &error))
- {
+ switch (bus_context_check_security_policy (bus_transaction_get_context (transaction),
+ transaction,
+ NULL, connection, connection, message, &error,
+ NULL))
+ {
+ case BUS_RESULT_TRUE:
+ break;
+ case BUS_RESULT_FALSE:
if (!bus_transaction_capture_error_reply (transaction, &error, message))
{
bus_context_log (transaction->context, DBUS_SYSTEM_LOG_WARNING,
* message (see reasoning above) */
dbus_error_free (&error);
return TRUE;
+ break;
+ case BUS_RESULT_LATER:
+ _dbus_verbose ("Cannot delay sending message from bus driver, dropping it\n");
+ return TRUE;
+ break;
}
return bus_transaction_send (transaction, connection, message);
typedef dbus_bool_t (* BusConnectionForeachFunction) (DBusConnection *connection,
void *data);
+DBusLoop* bus_connection_get_loop (DBusConnection *connection);
BusConnections* bus_connections_new (BusContext *context);
BusConnections* bus_connections_ref (BusConnections *connections);
BusTransaction *transaction,
DBusList **rules,
DBusError *error);
+#ifdef DBUS_ENABLE_CYNARA
+const char *bus_connection_get_cynara_session_id (DBusConnection *connection);
+#endif
/* transaction API so we can send or not send a block of messages as a whole */
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* cynara.c Cynara runtime privilege checking
+ *
+ * Copyright (c) 2014 Samsung Electronics, Ltd.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <config.h>
+#include "cynara.h"
+#include "check.h"
+#include "utils.h"
+
+#include <stdio.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-watch.h>
+#include <bus/connection.h>
+#ifdef DBUS_ENABLE_CYNARA
+#include <cynara-client-async.h>
+#endif
+
+
+#ifdef DBUS_ENABLE_CYNARA
+typedef struct BusCynara
+{
+ int refcount;
+
+ BusContext *context;
+ BusCheck *check;
+ cynara_async *cynara;
+ DBusWatch *cynara_watch;
+} BusCynara;
+
+static dbus_bool_t bus_cynara_watch_callback(DBusWatch *watch,
+ unsigned int flags,
+ void *data);
+
+static void status_callback(int old_fd,
+ int new_fd,
+ cynara_async_status status,
+ void *user_status_data);
+static void bus_cynara_check_response_callback (cynara_check_id check_id,
+ cynara_async_call_cause cause,
+ int response,
+ void *user_response_data);
+#endif
+
+
+BusCynara *
+bus_cynara_new(BusCheck *check, DBusError *error)
+{
+#ifdef DBUS_ENABLE_CYNARA
+ BusContext *context;
+ BusCynara *cynara;
+ int ret;
+
+ cynara = dbus_new(BusCynara, 1);
+ if (cynara == NULL)
+ {
+ BUS_SET_OOM(error);
+ return NULL;
+ }
+
+ context = bus_check_get_context(check);
+
+ cynara->refcount = 1;
+ cynara->check = check;
+ cynara->context = context;
+ cynara->cynara_watch = NULL;
+
+ ret = cynara_async_initialize(&cynara->cynara, NULL, &status_callback, cynara);
+ if (ret != CYNARA_API_SUCCESS)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED, "Failed to initialize Cynara client");
+ dbus_free(cynara);
+ return NULL;
+ }
+
+ return cynara;
+#else
+ return NULL;
+#endif
+}
+
+BusCynara *
+bus_cynara_ref (BusCynara *cynara)
+{
+#ifdef DBUS_ENABLE_CYNARA
+ _dbus_assert (cynara->refcount > 0);
+ cynara->refcount += 1;
+
+ return cynara;
+#else
+ return NULL;
+#endif
+}
+
+void
+bus_cynara_unref (BusCynara *cynara)
+{
+#ifdef DBUS_ENABLE_CYNARA
+ _dbus_assert (cynara->refcount > 0);
+
+ cynara->refcount -= 1;
+
+ if (cynara->refcount == 0)
+ {
+ if (cynara->cynara != NULL)
+ cynara_async_finish(cynara->cynara);
+ dbus_free(cynara);
+ }
+#endif
+}
+
+BusResult
+bus_cynara_check_privilege (BusCynara *cynara,
+ DBusMessage *message,
+ DBusConnection *sender,
+ DBusConnection *addressed_recipient,
+ DBusConnection *proposed_recipient,
+ const char *privilege,
+ BusDeferredMessageStatus check_type,
+ BusDeferredMessage **deferred_message_param)
+{
+#ifdef DBUS_ENABLE_CYNARA
+ int result;
+ unsigned long uid;
+ const char *label;
+ const char *session_id;
+ char user[32];
+ cynara_check_id check_id;
+ DBusConnection *connection = check_type == BUS_DEFERRED_MESSAGE_CHECK_RECEIVE ? proposed_recipient : sender;
+ BusDeferredMessage *deferred_message;
+
+ _dbus_assert(connection != NULL);
+
+ if (dbus_connection_get_unix_user(connection, &uid) == FALSE)
+ return BUS_RESULT_FALSE;
+
+#ifdef DBUS_ENABLE_SMACK
+ if (dbus_connection_get_smack_label (connection, &label) == FALSE)
+ return BUS_RESULT_FALSE;
+#else
+#error Cannot get connection label with smack disabled
+#endif
+
+ session_id = bus_connection_get_cynara_session_id (connection);
+ if (session_id == NULL)
+ return BUS_RESULT_FALSE;
+
+ snprintf(user, sizeof(user), "%lu", uid);
+
+
+ result = cynara_async_check_cache(cynara->cynara, label, session_id, user, privilege);
+ switch (result)
+ {
+ case CYNARA_API_ACCESS_ALLOWED:
+ _dbus_verbose("Cynara: got ALLOWED answer from cache (client=%s session_id=%s user=%s privilege=%s)\n",
+ label, session_id, user, privilege);
+ return BUS_RESULT_TRUE;
+
+ case CYNARA_API_ACCESS_DENIED:
+ _dbus_verbose("Cynara: got DENIED answer from cache (client=%s session_id=%s user=%s privilege=%s)\n",
+ label, session_id, user, privilege);
+ return BUS_RESULT_FALSE;
+
+ case CYNARA_API_CACHE_MISS:
+ deferred_message = bus_deferred_message_new(message, sender, addressed_recipient,
+ proposed_recipient, BUS_RESULT_LATER);
+ if (deferred_message == NULL)
+ {
+ _dbus_verbose("Failed to allocate memory for deferred message\n");
+ return BUS_RESULT_FALSE;
+ }
+
+ /* callback is supposed to unref deferred_message*/
+ result = cynara_async_create_request(cynara->cynara, label, session_id, user, privilege, &check_id,
+ &bus_cynara_check_response_callback, deferred_message);
+ if (result == CYNARA_API_SUCCESS)
+ {
+ _dbus_verbose("Created Cynara request: client=%s session_id=%s user=%s privilege=%s check_id=%u "
+ "deferred_message=%p\n", label, session_id, user, privilege, (unsigned int)check_id, deferred_message);
+ if (deferred_message_param != NULL)
+ *deferred_message_param = deferred_message;
+ return BUS_RESULT_LATER;
+ }
+ else
+ {
+ _dbus_verbose("Error on cynara request create: %i\n", result);
+ bus_deferred_message_unref(deferred_message);
+ return BUS_RESULT_FALSE;
+ }
+ break;
+ default:
+ _dbus_verbose("Error when accessing Cynara cache: %i\n", result);
+ return BUS_RESULT_FALSE;
+ }
+
+#else
+ return BUS_RESULT_FALSE;
+#endif
+}
+
+
+
+#ifdef DBUS_ENABLE_CYNARA
+static void
+status_callback(int old_fd, int new_fd, cynara_async_status status,
+ void *user_status_data)
+{
+ BusCynara *cynara = (BusCynara *)user_status_data;
+ DBusLoop *loop = bus_context_get_loop(cynara->context);
+
+ if (cynara->cynara_watch != NULL)
+ {
+ _dbus_loop_remove_watch(loop, cynara->cynara_watch);
+ _dbus_watch_invalidate(cynara->cynara_watch);
+ _dbus_watch_unref(cynara->cynara_watch);
+ cynara->cynara_watch = NULL;
+ }
+
+ if (new_fd != -1)
+ {
+ unsigned int flags;
+ DBusWatch *watch;
+
+ switch (status)
+ {
+ case CYNARA_STATUS_FOR_READ:
+ flags = DBUS_WATCH_READABLE;
+ break;
+ case CYNARA_STATUS_FOR_RW:
+ flags = DBUS_WATCH_READABLE | DBUS_WATCH_WRITABLE;
+ break;
+ default:
+ /* Cynara passed unknown status - warn and add RW watch */
+ _dbus_verbose("Cynara passed unknown status value: 0x%08X\n", (unsigned int)status);
+ flags = DBUS_WATCH_READABLE | DBUS_WATCH_WRITABLE;
+ break;
+ }
+
+ watch = _dbus_watch_new(new_fd, flags, TRUE, &bus_cynara_watch_callback, cynara, NULL);
+ if (watch != NULL)
+ {
+ if (_dbus_loop_add_watch(loop, watch) == TRUE)
+ {
+ cynara->cynara_watch = watch;
+ return;
+ }
+
+ _dbus_watch_invalidate(watch);
+ _dbus_watch_unref(watch);
+ }
+
+ /* It seems like not much can be done at this point. Cynara events won't be processed
+ * until next Cynara function call triggering status callback */
+ _dbus_verbose("Failed to add dbus watch\n");
+ }
+}
+
+static dbus_bool_t
+bus_cynara_watch_callback(DBusWatch *watch,
+ unsigned int flags,
+ void *data)
+{
+ BusCynara *cynara = (BusCynara *)data;
+ int result = cynara_async_process(cynara->cynara);
+ if (result != CYNARA_API_SUCCESS)
+ _dbus_verbose("cynara_async_process returned %d\n", result);
+
+ return result != CYNARA_API_OUT_OF_MEMORY ? TRUE : FALSE;
+}
+
+static inline const char *
+call_cause_to_string(cynara_async_call_cause cause)
+{
+ switch (cause)
+ {
+ case CYNARA_CALL_CAUSE_ANSWER:
+ return "ANSWER";
+ case CYNARA_CALL_CAUSE_CANCEL:
+ return "CANCEL";
+ case CYNARA_CALL_CAUSE_FINISH:
+ return "FINSIH";
+ case CYNARA_CALL_CAUSE_SERVICE_NOT_AVAILABLE:
+ return "SERVICE NOT AVAILABLE";
+ default:
+ return "INVALID";
+ }
+}
+
+static void
+bus_cynara_check_response_callback (cynara_check_id check_id,
+ cynara_async_call_cause cause,
+ int response,
+ void *user_response_data)
+{
+ BusDeferredMessage *deferred_message = user_response_data;
+ BusResult result;
+
+ _dbus_verbose("Cynara callback: check_id=%u, cause=%s response=%i response_data=%p\n",
+ (unsigned int)check_id, call_cause_to_string(cause), response, user_response_data);
+
+ if (deferred_message == NULL)
+ return;
+
+ if (cause == CYNARA_CALL_CAUSE_ANSWER && response == CYNARA_API_ACCESS_ALLOWED)
+ result = BUS_RESULT_TRUE;
+ else
+ result = BUS_RESULT_FALSE;
+
+ bus_deferred_message_response_received(deferred_message, result);
+ bus_deferred_message_unref(deferred_message);
+}
+
+#endif /* DBUS_ENABLE_CYNARA */
--- /dev/null
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* cynara.h Cynara runtime privilege checking
+ *
+ * Copyright (c) 2014 Samsung Electronics, Ltd.
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "bus.h"
+#include "check.h"
+
+BusCynara *bus_cynara_new (BusCheck *check, DBusError *error);
+BusCynara *bus_cynara_ref (BusCynara *cynara);
+void bus_cynara_unref (BusCynara *cynara);
+BusResult bus_cynara_check_privilege (BusCynara *cynara,
+ DBusMessage *message,
+ DBusConnection *sender,
+ DBusConnection *addressed_recipient,
+ DBusConnection *proposed_recipient,
+ const char *privilege,
+ BusDeferredMessageStatus check_type,
+ BusDeferredMessage **deferred_message);
#include <config.h>
#include "dispatch.h"
+#include "check.h"
#include "connection.h"
#include "driver.h"
#include "services.h"
DBusError *error)
{
DBusError stack_error = DBUS_ERROR_INIT;
+ BusDeferredMessage *deferred_message;
+ BusResult result;
- if (!bus_context_check_security_policy (context, transaction,
- sender,
- addressed_recipient,
- connection,
- message,
- &stack_error))
+ result = bus_context_check_security_policy (context, transaction, sender, addressed_recipient,
+ connection, message, NULL, &deferred_message);
+
+ if (result != BUS_RESULT_TRUE)
{
if (!bus_transaction_capture_error_reply (transaction, &stack_error,
message))
BusMatchmaker *matchmaker;
DBusList *link;
BusContext *context;
+ BusDeferredMessage *deferred_message;
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
/* First, send the message to the addressed_recipient, if there is one. */
if (addressed_recipient != NULL)
{
- if (!bus_context_check_security_policy (context, transaction,
- sender, addressed_recipient,
- addressed_recipient,
- message, error))
- return FALSE;
+ switch (bus_context_check_security_policy (context, transaction,
+ sender, addressed_recipient,
+ addressed_recipient,
+ message, error,
+ &deferred_message))
+ {
+ case BUS_RESULT_TRUE:
+ break;
+ case BUS_RESULT_FALSE:
+ return BUS_RESULT_FALSE;
+ case BUS_RESULT_LATER:
+ dbus_set_error (error,
+ DBUS_ERROR_ACCESS_DENIED,
+ "Rejecting message because time is needed to check security policy");
+ return BUS_RESULT_FALSE;
+ }
if (dbus_message_contains_unix_fds (message) &&
!dbus_connection_can_send_type (addressed_recipient,
if (service_name &&
strcmp (service_name, DBUS_SERVICE_DBUS) == 0) /* to bus driver */
{
- if (!bus_context_check_security_policy (context, transaction,
- connection, NULL, NULL, message, &error))
+ BusDeferredMessage *deferred_message;
+
+ switch (bus_context_check_security_policy (context, transaction,
+ connection, NULL, NULL, message, &error,
+ &deferred_message))
{
+ case BUS_RESULT_TRUE:
+ break;
+ case BUS_RESULT_FALSE:
_dbus_verbose ("Security policy rejected message\n");
goto out;
+ case BUS_RESULT_LATER:
+ dbus_set_error (&error,
+ DBUS_ERROR_ACCESS_DENIED,
+ "Rejecting message because time is needed to check security policy");
+ _dbus_verbose ("Security policy needs time to check policy. Dropping message\n");
+ goto out;
}
_dbus_verbose ("Giving message to %s\n", DBUS_SERVICE_DBUS);
*/
#include <config.h>
+#include "check.h"
#include "policy.h"
#include "services.h"
#include "test.h"
return TRUE;
}
-dbus_bool_t
-bus_client_policy_check_can_send (BusClientPolicy *policy,
- BusRegistry *registry,
- dbus_bool_t requested_reply,
- DBusConnection *receiver,
- DBusMessage *message,
- dbus_int32_t *toggles,
- dbus_bool_t *log)
+BusResult
+bus_client_policy_check_can_send (DBusConnection *sender,
+ BusClientPolicy *policy,
+ BusRegistry *registry,
+ dbus_bool_t requested_reply,
+ DBusConnection *addressed_recipient,
+ DBusConnection *receiver,
+ DBusMessage *message,
+ dbus_int32_t *toggles,
+ dbus_bool_t *log,
+ const char **privilege_param,
+ BusDeferredMessage **deferred_message)
{
DBusList *link;
- dbus_bool_t allowed;
+ BusResult result;
+ const char *privilege;
/* policy->rules is in the order the rules appeared
* in the config file, i.e. last rule that applies wins
_dbus_verbose (" (policy) checking send rules\n");
*toggles = 0;
- allowed = FALSE;
+ result = BUS_RESULT_FALSE;
link = _dbus_list_get_first_link (&policy->rules);
while (link != NULL)
{
}
/* Use this rule */
- allowed = rule->access == BUS_POLICY_RULE_ACCESS_ALLOW;
+ switch (rule->access)
+ {
+ case BUS_POLICY_RULE_ACCESS_ALLOW:
+ result = BUS_RESULT_TRUE;
+ break;
+ case BUS_POLICY_RULE_ACCESS_DENY:
+ result = BUS_RESULT_FALSE;
+ break;
+ case BUS_POLICY_RULE_ACCESS_CHECK:
+ result = BUS_RESULT_LATER;
+ privilege = rule->privilege;
+ break;
+ }
+
*log = rule->d.send.log;
(*toggles)++;
- _dbus_verbose (" (policy) used rule, allow now = %d\n",
- allowed);
+ _dbus_verbose (" (policy) used rule, result now = %d\n",
+ result);
}
- return allowed;
+ if (result == BUS_RESULT_LATER)
+ {
+ BusContext *context = bus_connection_get_context(sender);
+ BusCheck *check = bus_context_get_check(context);
+
+ result = bus_check_privilege(check, message, sender, addressed_recipient, receiver,
+ privilege, BUS_DEFERRED_MESSAGE_CHECK_SEND, deferred_message);
+ }
+ else
+ privilege = NULL;
+
+ if (privilege_param != NULL)
+ *privilege_param = privilege;
+
+ return result;
}
/* See docs on what the args mean on bus_context_check_security_policy()
* comment
*/
-dbus_bool_t
-bus_client_policy_check_can_receive (BusClientPolicy *policy,
- BusRegistry *registry,
- dbus_bool_t requested_reply,
- DBusConnection *sender,
- DBusConnection *addressed_recipient,
- DBusConnection *proposed_recipient,
- DBusMessage *message,
- dbus_int32_t *toggles)
+BusResult
+bus_client_policy_check_can_receive (BusClientPolicy *policy,
+ BusRegistry *registry,
+ dbus_bool_t requested_reply,
+ DBusConnection *sender,
+ DBusConnection *addressed_recipient,
+ DBusConnection *proposed_recipient,
+ DBusMessage *message,
+ dbus_int32_t *toggles,
+ const char **privilege_param,
+ BusDeferredMessage **deferred_message)
{
DBusList *link;
- dbus_bool_t allowed;
dbus_bool_t eavesdropping;
+ BusResult result;
+ const char *privilege;
eavesdropping =
addressed_recipient != proposed_recipient &&
_dbus_verbose (" (policy) checking receive rules, eavesdropping = %d\n", eavesdropping);
*toggles = 0;
- allowed = FALSE;
+ result = BUS_RESULT_FALSE;
link = _dbus_list_get_first_link (&policy->rules);
while (link != NULL)
{
}
/* Use this rule */
- allowed = rule->access == BUS_POLICY_RULE_ACCESS_ALLOW;
+ switch (rule->access)
+ {
+ case BUS_POLICY_RULE_ACCESS_ALLOW:
+ result = BUS_RESULT_TRUE;
+ break;
+ case BUS_POLICY_RULE_ACCESS_DENY:
+ result = BUS_RESULT_FALSE;
+ break;
+ case BUS_POLICY_RULE_ACCESS_CHECK:
+ result = BUS_RESULT_LATER;
+ privilege = rule->privilege;
+ break;
+ }
+
(*toggles)++;
- _dbus_verbose (" (policy) used rule, allow now = %d\n",
- allowed);
+ _dbus_verbose (" (policy) used rule, result now = %d\n",
+ result);
}
- return allowed;
+
+ if (result == BUS_RESULT_LATER)
+ {
+ BusContext *context = bus_connection_get_context(proposed_recipient);
+ BusCheck *check = bus_context_get_check(context);
+
+ result = bus_check_privilege(check, message, sender, addressed_recipient, proposed_recipient,
+ privilege, BUS_DEFERRED_MESSAGE_CHECK_RECEIVE, deferred_message);
+ }
+ else
+ privilege = NULL;
+
+ if (privilege_param != NULL)
+ *privilege_param = privilege;
+
+ return result;
}
BusClientPolicy* bus_client_policy_new (void);
BusClientPolicy* bus_client_policy_ref (BusClientPolicy *policy);
void bus_client_policy_unref (BusClientPolicy *policy);
-dbus_bool_t bus_client_policy_check_can_send (BusClientPolicy *policy,
- BusRegistry *registry,
- dbus_bool_t requested_reply,
- DBusConnection *receiver,
- DBusMessage *message,
- dbus_int32_t *toggles,
- dbus_bool_t *log);
-dbus_bool_t bus_client_policy_check_can_receive (BusClientPolicy *policy,
- BusRegistry *registry,
- dbus_bool_t requested_reply,
- DBusConnection *sender,
- DBusConnection *addressed_recipient,
- DBusConnection *proposed_recipient,
- DBusMessage *message,
- dbus_int32_t *toggles);
+BusResult bus_client_policy_check_can_send (DBusConnection *sender,
+ BusClientPolicy *policy,
+ BusRegistry *registry,
+ dbus_bool_t requested_reply,
+ DBusConnection *addressed_recipient,
+ DBusConnection *receiver,
+ DBusMessage *message,
+ dbus_int32_t *toggles,
+ dbus_bool_t *log,
+ const char **privilege_param,
+ BusDeferredMessage **deferred_message);
+BusResult bus_client_policy_check_can_receive (BusClientPolicy *policy,
+ BusRegistry *registry,
+ dbus_bool_t requested_reply,
+ DBusConnection *sender,
+ DBusConnection *addressed_recipient,
+ DBusConnection *proposed_recipient,
+ DBusMessage *message,
+ dbus_int32_t *toggles,
+ const char **privilege_param,
+ BusDeferredMessage **deferred_message);
dbus_bool_t bus_client_policy_check_can_own (BusClientPolicy *policy,
const DBusString *service_name);
dbus_bool_t bus_client_policy_append_rule (BusClientPolicy *policy,
AC_SUBST([LIBSMACK_CFLAGS])
AC_SUBST([LIBSMACK_LIBS])
+#enable cynara integration
+AC_ARG_ENABLE([cynara], [AS_HELP_STRING([--enable-cynara], [enable Cynara integration])], [], [enable_cynara=no])
+if test "x$enable_cynara" = xyes; then
+ PKG_CHECK_MODULES([CYNARA], [cynara-client-async >= 0.4.2 cynara-session >= 0.4.2],
+ [AC_DEFINE([DBUS_ENABLE_CYNARA], [1], [Define to enable Cynara privilege checks in dbus-daemon])],
+ [AC_MSG_ERROR([libcynara-client-async and cynara-session are required to enable Cynara integration])])
+fi
+
+AC_SUBST([CYNARA_CFLAGS])
+AC_SUBST([CYNARA_LIBS])
+
+
AC_CONFIG_FILES([
Doxyfile
dbus/Version
BuildRequires: pkgconfig(libsystemd-daemon)
BuildRequires: pkgconfig(libsystemd-login)
%endif
+BuildRequires: pkgconfig(cynara-client-async)
+BuildRequires: pkgconfig(cynara-session)
Version: 1.8.2
Release: 0
Source0: http://dbus.freedesktop.org/releases/dbus/dbus-%{version}.tar.gz
%endif
--with-console-auth-dir=/var/run/dbus/at_console/ \
--with-systemdsystemunitdir=%{_unitdir} \
- --enable-smack
+ --enable-smack \
+ --enable-cynara
make %{?_smp_mflags}