#include "bus.h"
#include <stdio.h>
+#include <stdlib.h>
#include "activation.h"
#include "connection.h"
#include "apparmor.h"
#include "audit.h"
#include "dir-watch.h"
+#include "check.h"
+#include <dbus/dbus-auth.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;
{
BusContext *context = data;
+ /* If this fails it logs a warning, so we don't need to do that */
if (!bus_connections_setup_connection (context->connections, new_connection))
{
- _dbus_verbose ("No memory to setup new connection\n");
-
/* if we don't do this, it will get unref'd without
* being disconnected... kind of strange really
* that we have to do this, people won't get it right
DBusList **auth_mechanisms_list;
int len;
dbus_bool_t retval;
+ DBusLogFlags log_flags = DBUS_LOG_FLAGS_STDERR;
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
auth_mechanisms = NULL;
pidfile = NULL;
- _dbus_init_system_log ("dbus-daemon", TRUE);
+ if (flags & BUS_CONTEXT_FLAG_SYSLOG_ALWAYS)
+ {
+ context->syslog = TRUE;
+ log_flags |= DBUS_LOG_FLAGS_SYSTEM_LOG;
+
+ if (flags & BUS_CONTEXT_FLAG_SYSLOG_ONLY)
+ log_flags &= ~DBUS_LOG_FLAGS_STDERR;
+ }
+ else if (flags & BUS_CONTEXT_FLAG_SYSLOG_NEVER)
+ {
+ context->syslog = FALSE;
+ }
+ else
+ {
+ context->syslog = bus_config_parser_get_syslog (parser);
+
+ if (context->syslog)
+ log_flags |= DBUS_LOG_FLAGS_SYSTEM_LOG;
+ }
+
+ _dbus_init_system_log ("dbus-daemon", log_flags);
if (flags & BUS_CONTEXT_FLAG_SYSTEMD_ACTIVATION)
context->systemd_activation = TRUE;
link = _dbus_list_get_first_link (auth_mechanisms_list);
while (link != NULL)
{
+ DBusString name;
+ _dbus_string_init_const (&name, link->data);
+ if (!_dbus_auth_is_supported_mechanism (&name))
+ {
+ DBusString list;
+ if (!_dbus_string_init (&list))
+ goto oom;
+
+ if (!_dbus_auth_dump_supported_mechanisms (&list))
+ {
+ _dbus_string_free (&list);
+ goto oom;
+ }
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Unsupported auth mechanism \"%s\" in bus config file detected. Supported mechanisms are \"%s\".",
+ (char*)link->data,
+ _dbus_string_get_const_data (&list));
+ _dbus_string_free (&list);
+ goto failed;
+ }
auth_mechanisms[i] = _dbus_strdup (link->data);
if (auth_mechanisms[i] == NULL)
goto oom;
}
context->fork = bus_config_parser_get_fork (parser);
- context->syslog = bus_config_parser_get_syslog (parser);
context->keep_umask = bus_config_parser_get_keep_umask (parser);
context->allow_anonymous = bus_config_parser_get_allow_anonymous (parser);
return retval;
}
-static dbus_bool_t
-list_concat_new (DBusList **a,
- DBusList **b,
- DBusList **result)
-{
- DBusList *link;
-
- *result = NULL;
-
- for (link = _dbus_list_get_first_link (a); link; link = _dbus_list_get_next_link (a, link))
- {
- if (!_dbus_list_append (result, link->data))
- goto oom;
- }
- for (link = _dbus_list_get_first_link (b); link; link = _dbus_list_get_next_link (b, link))
- {
- if (!_dbus_list_append (result, link->data))
- goto oom;
- }
-
- return TRUE;
-oom:
- _dbus_list_clear (result);
- return FALSE;
-}
-
static void
raise_file_descriptor_limit (BusContext *context)
{
/* We used to compute a suitable rlimit based on the configured number
* of connections, but that breaks down as soon as we allow fd-passing,
* because each connection is allowed to pass 64 fds to us, and if
- * they all did, we'd hit kernel limits. We now hard-code 64k as a
- * good limit, like systemd does: that's enough to avoid DoS from
- * anything short of multiple uids conspiring against us.
+ * they all did, we'd hit kernel limits. We now hard-code a good
+ * limit that is enough to avoid DoS from anything short of multiple
+ * uids conspiring against us, much like systemd does.
*/
- if (!_dbus_rlimit_raise_fd_limit_if_privileged (65536, &error))
+ if (!_dbus_rlimit_raise_fd_limit (&error))
{
bus_context_log (context, DBUS_SYSTEM_LOG_WARNING,
"%s: %s", error.name, error.message);
DBusHashTable *service_context_table;
DBusList *watched_dirs = NULL;
- raise_file_descriptor_limit (context);
-
service_context_table = bus_config_parser_steal_service_context_table (parser);
if (!bus_registry_set_service_context_table (context->registry,
service_context_table))
/* We need to monitor both the configuration directories and directories
* containing .service files.
*/
- if (!list_concat_new (bus_config_parser_get_conf_dirs (parser),
- bus_config_parser_get_service_dirs (parser),
- &watched_dirs))
+ if (!bus_config_parser_get_watched_dirs (parser, &watched_dirs))
{
BUS_SET_OOM (error);
return FALSE;
!_dbus_pipe_is_stdout_or_stderr (print_pid_pipe))
_dbus_pipe_close (print_pid_pipe, NULL);
+ /* Raise the file descriptor limits before dropping the privileges
+ * required to do so.
+ */
+ raise_file_descriptor_limit (context);
+
/* Here we change our credentials if required,
* as soon as we've set up our sockets and pidfile.
* This must be done before initializing LSMs, so that the netlink
if (!bus_selinux_full_init ())
{
- bus_context_log (context, DBUS_SYSTEM_LOG_FATAL, "SELinux enabled but D-Bus initialization failed; check system log\n");
+ bus_context_log (context, DBUS_SYSTEM_LOG_ERROR,
+ "SELinux enabled but D-Bus initialization failed; "
+ "check system log");
+ exit (1);
}
if (!bus_apparmor_full_init (error))
parser = NULL;
}
+ 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)
return context->initial_fd_limit;
}
+dbus_bool_t
+bus_context_get_using_syslog (BusContext *context)
+{
+ return context->syslog;
+}
+
void
bus_context_log (BusContext *context, DBusSystemLogSeverity severity, const char *msg, ...)
{
va_list args;
- if (!context->syslog)
- {
- /* we're not syslogging; just output to stderr */
- va_start (args, msg);
- vfprintf (stderr, msg, args);
- fprintf (stderr, "\n");
- va_end (args);
-
- if (severity == DBUS_SYSTEM_LOG_FATAL)
- _dbus_exit (1);
-
- return;
- }
-
va_start (args, msg);
if (context->log_prefix)
if (!_dbus_string_append_printf_valist (&full_msg, msg, args))
goto oom_out;
- _dbus_system_log (severity, "%s", _dbus_string_get_const_data (&full_msg));
+ _dbus_log (severity, "%s", _dbus_string_get_const_data (&full_msg));
oom_out:
_dbus_string_free (&full_msg);
}
else
- _dbus_system_logv (severity, msg, args);
+ _dbus_logv (severity, msg, args);
out:
va_end (args);
DBusSystemLogSeverity severity,
const char *msg)
{
- if (!context->syslog)
- {
- fputs (msg, stderr);
- fputc ('\n', stderr);
-
- if (severity == DBUS_SYSTEM_LOG_FATAL)
- _dbus_exit (1);
- }
- else
- {
- _dbus_system_log (severity, "%s%s", nonnull (context->log_prefix, ""),
- msg);
- }
+ _dbus_log (severity, "%s%s", nonnull (context->log_prefix, ""), msg);
}
void
DBusConnection *proposed_recipient,
dbus_bool_t requested_reply,
dbus_bool_t log,
- DBusError *error)
+ const char *privilege,
+ DBusError *error,
+ const char *rule)
{
DBusError stack_error = DBUS_ERROR_INIT;
const char *sender_name;
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) "
+ "rule(%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),
- proposed_recipient_loginfo);
+ nonnull (privilege, "(n/a)"),
+ proposed_recipient_loginfo,
+ rule);
/* If we hit OOM while setting the error, this will syslog "out of memory"
* which is itself an indication that something is seriously wrong */
*
* sender is the sender of the message.
*
- * NULL for proposed_recipient or sender definitely means the bus driver.
+ * NULL for sender definitely means the bus driver.
+ *
+ * NULL for proposed_recipient may mean the bus driver, or may mean
+ * we are checking whether service-activation is allowed as a first
+ * pass before all details of the activated service are known.
*
* 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,
+ BusActivationEntry *activation_entry,
+ 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;
+ char *out_rule = NULL;
+ BusResult can_send_result = BUS_RESULT_TRUE;
type = dbus_message_get_type (message);
src = dbus_message_get_sender (message);
(sender == NULL && !bus_connection_is_active (proposed_recipient)));
_dbus_assert (type == DBUS_MESSAGE_TYPE_SIGNAL ||
addressed_recipient != NULL ||
+ activation_entry != NULL ||
strcmp (dest, DBUS_SERVICE_DBUS) == 0);
switch (type)
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;
}
}
}
dbus_message_get_interface (message),
dbus_message_get_member (message),
dbus_message_get_error_name (message),
- dest ? dest : DBUS_SERVICE_DBUS, error))
+ dest ? dest : DBUS_SERVICE_DBUS,
+ activation_entry,
+ error))
{
if (error != NULL && !dbus_error_is_set (error))
{
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, NULL);
_dbus_verbose ("SELinux security check denying send to service\n");
}
dbus_message_get_error_name (message),
dest ? dest : DBUS_SERVICE_DBUS,
src ? src : DBUS_SERVICE_DBUS,
+ activation_entry,
error))
return FALSE;
{
_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;
}
}
}
}
else
{
- _dbus_assert_not_reached ("a message was somehow sent to an inactive recipient from a source other than the message bus\n");
+ _dbus_assert_not_reached ("a message was somehow sent to an inactive recipient from a source other than the message bus");
recipient_policy = NULL;
}
}
(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) {
+ can_send_result = bus_client_policy_check_can_send (sender,
+ sender_policy,
+ context->registry,
+ requested_reply,
+ addressed_recipient,
+ proposed_recipient,
+ message, &toggles, &log, &privilege,
+ deferred_message, &out_rule);
+ if (can_send_result == 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) || (type == DBUS_MESSAGE_TYPE_SIGNAL),
+ privilege,
+ error, out_rule);
+ _dbus_verbose ("security policy disallowing message due to sender policy\n");
+ if (out_rule)
+ free (out_rule);
+ return BUS_RESULT_FALSE;
+ }
+ }
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, 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, &out_rule))
+ {
+ 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) || (type == DBUS_MESSAGE_TYPE_SIGNAL),
+ privilege, error, out_rule);
+ _dbus_verbose(
+ "security policy disallowing message due to recipient policy\n");
+ if (out_rule)
+ free (out_rule);
+ if (deferred_message && *deferred_message)
+ bus_deferred_message_set_response (*deferred_message, BUS_RESULT_FALSE);
+ return BUS_RESULT_FALSE;
+ case BUS_RESULT_LATER:
+ return BUS_RESULT_LATER;
+ }
+ }
+
+ if (can_send_result == BUS_RESULT_LATER)
+ return BUS_RESULT_LATER;
/* See if limits on size have been exceeded */
- if (proposed_recipient &&
- ((dbus_connection_get_outgoing_size (proposed_recipient) > context->limits.max_outgoing_bytes) ||
- (dbus_connection_get_outgoing_unix_fds (proposed_recipient) > context->limits.max_outgoing_unix_fds)))
- {
- complain_about_message (context, DBUS_ERROR_LIMITS_EXCEEDED,
- "Rejected: destination has a full message queue",
- 0, message, sender, proposed_recipient, requested_reply, TRUE,
- error);
- _dbus_verbose ("security policy disallowing message due to full message queue\n");
- return FALSE;
- }
+ if (!bus_context_check_recipient_message_limits(context, proposed_recipient, sender, message,
+ requested_reply, error))
+ return BUS_RESULT_FALSE;
/* Record that we will allow a reply here in the future (don't
* bother if the recipient is the bus or this is an eavesdropping
* connection). Only the addressed recipient may reply.
+ *
+ * This isn't done for activation attempts because they have no addressed
+ * or proposed recipient; when we check whether to actually deliver the
+ * message, later, we'll record the reply expectation at that point.
*/
if (type == DBUS_MESSAGE_TYPE_METHOD_CALL &&
sender &&
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
_dbus_server_toggle_all_watches (server, enabled);
}
}
+
+void
+bus_context_complain_about_message (BusContext *context,
+ const char *error_name,
+ const char *complaint,
+ int matched_rules,
+ DBusMessage *message,
+ DBusConnection *sender,
+ DBusConnection *proposed_recipient,
+ dbus_bool_t requested_reply,
+ dbus_bool_t log,
+ const char *privilege,
+ DBusError *error)
+{
+ complain_about_message(context, error_name, complaint, matched_rules, message, sender,
+ proposed_recipient, requested_reply, log, privilege, error, NULL);
+}
+
+dbus_bool_t bus_context_check_recipient_message_limits (BusContext *context,
+ DBusConnection *recipient,
+ DBusConnection *sender,
+ DBusMessage *message,
+ dbus_bool_t requested_reply,
+ DBusError *error)
+
+{
+ if (recipient &&
+ ((dbus_connection_get_outgoing_size (recipient) > context->limits.max_outgoing_bytes) ||
+ (dbus_connection_get_outgoing_unix_fds (recipient) > context->limits.max_outgoing_unix_fds)))
+ {
+ complain_about_message (context, DBUS_ERROR_LIMITS_EXCEEDED,
+ "Rejected: destination has a full message queue",
+ 0, message, sender, recipient, requested_reply, TRUE, NULL,
+ error, NULL);
+ _dbus_verbose ("security policy disallowing message due to full message queue\n");
+ return FALSE;
+ }
+ return TRUE;
+}