/* -*- mode: C; c-file-style: "gnu" -*- */
/* bus.c message bus context object
*
- * Copyright (C) 2003 Red Hat, Inc.
+ * Copyright (C) 2003, 2004 Red Hat, Inc.
*
- * Licensed under the Academic Free License version 1.2
+ * Licensed under the Academic Free License version 2.0
*
* 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
*/
#include "bus.h"
-#include "loop.h"
#include "activation.h"
#include "connection.h"
#include "services.h"
#include "utils.h"
#include "policy.h"
#include "config-parser.h"
+#include "signals.h"
+#include "selinux.h"
#include <dbus/dbus-list.h>
#include <dbus/dbus-hash.h>
#include <dbus/dbus-internals.h>
struct BusContext
{
int refcount;
+ char *config_file;
char *type;
char *address;
- BusLoop *loop;
+ char *pidfile;
+ char *user;
+ DBusLoop *loop;
DBusList *servers;
BusConnections *connections;
BusActivation *activation;
BusRegistry *registry;
- DBusList *default_rules; /**< Default policy rules */
- DBusList *mandatory_rules; /**< Mandatory policy rules */
- DBusHashTable *rules_by_uid; /**< per-UID policy rules */
- DBusHashTable *rules_by_gid; /**< per-GID policy rules */
- int activation_timeout; /**< How long to wait for an activation to time out */
- int auth_timeout; /**< How long to wait for an authentication to time out */
- int max_completed_connections; /**< Max number of authorized connections */
- int max_incomplete_connections; /**< Max number of incomplete connections */
- int max_connections_per_user; /**< Max number of connections auth'd as same user */
+ BusPolicy *policy;
+ BusMatchmaker *matchmaker;
+ DBusUserDatabase *user_database;
+ BusLimits limits;
+ unsigned int fork : 1;
};
-static int server_data_slot = -1;
-static int server_data_slot_refcount = 0;
+static dbus_int32_t server_data_slot = -1;
typedef struct
{
#define BUS_SERVER_DATA(server) (dbus_server_get_data ((server), server_data_slot))
-static dbus_bool_t
-server_data_slot_ref (void)
-{
- if (server_data_slot < 0)
- {
- server_data_slot = dbus_server_allocate_data_slot ();
-
- if (server_data_slot < 0)
- return FALSE;
-
- _dbus_assert (server_data_slot_refcount == 0);
- }
-
- server_data_slot_refcount += 1;
-
- return TRUE;
-}
-
-static void
-server_data_slot_unref (void)
-{
- _dbus_assert (server_data_slot_refcount > 0);
-
- server_data_slot_refcount -= 1;
-
- if (server_data_slot_refcount == 0)
- {
- dbus_server_free_data_slot (server_data_slot);
- server_data_slot = -1;
- }
-}
-
static BusContext*
server_get_context (DBusServer *server)
{
BusContext *context;
BusServerData *bd;
- if (!server_data_slot_ref ())
+ if (!dbus_server_allocate_data_slot (&server_data_slot))
return NULL;
bd = BUS_SERVER_DATA (server);
if (bd == NULL)
{
- server_data_slot_unref ();
+ dbus_server_free_data_slot (&server_data_slot);
return NULL;
}
context = bd->context;
- server_data_slot_unref ();
+ dbus_server_free_data_slot (&server_data_slot);
return context;
}
unsigned int condition,
void *data)
{
- DBusServer *server = data;
-
- return dbus_server_handle_watch (server, watch, condition);
+ /* FIXME this can be done in dbus-mainloop.c
+ * if the code in activation.c for the babysitter
+ * watch handler is fixed.
+ */
+
+ return dbus_watch_handle (watch, condition);
}
static dbus_bool_t
context = server_get_context (server);
- return bus_loop_add_watch (context->loop,
- watch, server_watch_callback, server,
- NULL);
+ return _dbus_loop_add_watch (context->loop,
+ watch, server_watch_callback, server,
+ NULL);
}
static void
context = server_get_context (server);
- bus_loop_remove_watch (context->loop,
- watch, server_watch_callback, server);
+ _dbus_loop_remove_watch (context->loop,
+ watch, server_watch_callback, server);
}
context = server_get_context (server);
- return bus_loop_add_timeout (context->loop,
- timeout, server_timeout_callback, server, NULL);
+ return _dbus_loop_add_timeout (context->loop,
+ timeout, server_timeout_callback, server, NULL);
}
static void
context = server_get_context (server);
- bus_loop_remove_timeout (context->loop,
- timeout, server_timeout_callback, server);
+ _dbus_loop_remove_timeout (context->loop,
+ timeout, server_timeout_callback, server);
}
static void
*/
dbus_connection_disconnect (new_connection);
}
-
- /* on OOM, we won't have ref'd the connection so it will die. */
-}
-
-static void
-free_rule_func (void *data,
- void *user_data)
-{
- BusPolicyRule *rule = data;
- bus_policy_rule_unref (rule);
-}
+ dbus_connection_set_max_received_size (new_connection,
+ context->limits.max_incoming_bytes);
-static void
-free_rule_list_func (void *data)
-{
- DBusList **list = data;
-
- _dbus_list_foreach (list, free_rule_func, NULL);
+ dbus_connection_set_max_message_size (new_connection,
+ context->limits.max_message_size);
- _dbus_list_clear (list);
-
- dbus_free (list);
+ /* on OOM, we won't have ref'd the connection so it will die. */
}
static void
return TRUE;
}
-BusContext*
-bus_context_new (const DBusString *config_file,
- DBusError *error)
+/* This code only gets executed the first time the
+ config files are parsed. It is not executed
+ when config files are reloaded.*/
+static dbus_bool_t
+process_config_first_time_only (BusContext *context,
+ BusConfigParser *parser,
+ DBusError *error)
{
- BusContext *context;
DBusList *link;
DBusList **addresses;
- BusConfigParser *parser;
- DBusString full_address;
- const char *user;
+ const char *user, *pidfile;
char **auth_mechanisms;
DBusList **auth_mechanisms_list;
int len;
-
- _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ dbus_bool_t retval;
- if (!_dbus_string_init (&full_address))
- {
- BUS_SET_OOM (error);
- return NULL;
- }
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
- if (!server_data_slot_ref ())
- {
- BUS_SET_OOM (error);
- _dbus_string_free (&full_address);
- return NULL;
- }
-
- parser = NULL;
- context = NULL;
+ retval = FALSE;
auth_mechanisms = NULL;
-
- parser = bus_config_load (config_file, error);
- if (parser == NULL)
- goto failed;
-
- context = dbus_new0 (BusContext, 1);
- if (context == NULL)
- {
- BUS_SET_OOM (error);
- goto failed;
- }
-
- context->refcount = 1;
- /* we need another ref of the server data slot for the context
- * to own
- */
- if (!server_data_slot_ref ())
- _dbus_assert_not_reached ("second ref of server data slot failed");
-
-#ifdef DBUS_BUILD_TESTS
- context->activation_timeout = 6000; /* 6 seconds */
-#else
- context->activation_timeout = 15000; /* 15 seconds */
-#endif
-
- /* Making this long risks making a DOS attack easier, but too short
- * and legitimate auth will fail. If interactive auth (ask user for
- * password) is allowed, then potentially it has to be quite long.
- * Ultimately it needs to come from the configuration file.
- */
- context->auth_timeout = 3000; /* 3 seconds */
-
- context->max_incomplete_connections = 32;
- context->max_connections_per_user = 128;
-
- /* Note that max_completed_connections / max_connections_per_user
- * is the number of users that would have to work together to
- * DOS all the other users.
+ /* Check for an existing pid file. Of course this is a race;
+ * we'd have to use fcntl() locks on the pid file to
+ * avoid that. But we want to check for the pid file
+ * before overwriting any existing sockets, etc.
*/
- context->max_completed_connections = 1024;
-
- context->loop = bus_loop_new ();
- if (context->loop == NULL)
+ pidfile = bus_config_parser_get_pidfile (parser);
+ if (pidfile != NULL)
{
- BUS_SET_OOM (error);
- goto failed;
+ DBusString u;
+ DBusStat stbuf;
+ DBusError tmp_error;
+
+ dbus_error_init (&tmp_error);
+ _dbus_string_init_const (&u, pidfile);
+
+ if (_dbus_stat (&u, &stbuf, &tmp_error))
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "The pid file \"%s\" exists, if the message bus is not running, remove this file",
+ pidfile);
+ dbus_error_free (&tmp_error);
+ goto failed;
+ }
}
+ /* keep around the pid filename so we can delete it later */
+ context->pidfile = _dbus_strdup (pidfile);
+
/* Build an array of auth mechanisms */
auth_mechanisms_list = bus_config_parser_get_mechanisms (parser);
auth_mechanisms = dbus_new0 (char*, len + 1);
if (auth_mechanisms == NULL)
- goto failed;
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
i = 0;
link = _dbus_list_get_first_link (auth_mechanisms_list);
{
auth_mechanisms[i] = _dbus_strdup (link->data);
if (auth_mechanisms[i] == NULL)
- goto failed;
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
link = _dbus_list_get_next_link (auth_mechanisms_list, link);
}
}
server = dbus_server_listen (link->data, error);
if (server == NULL)
- goto failed;
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto failed;
+ }
else if (!setup_server (context, server, auth_mechanisms, error))
- goto failed;
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto failed;
+ }
if (!_dbus_list_append (&context->servers, server))
{
link = _dbus_list_get_next_link (addresses, link);
}
- /* Here we change our credentials if required,
- * as soon as we've set up our sockets
- */
+ /* note that type may be NULL */
+ context->type = _dbus_strdup (bus_config_parser_get_type (parser));
+ if (bus_config_parser_get_type (parser) != NULL && context->type == NULL)
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
user = bus_config_parser_get_user (parser);
if (user != NULL)
{
- DBusCredentials creds;
- DBusString u;
+ context->user = _dbus_strdup (user);
+ if (context->user == NULL)
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+ }
- _dbus_string_init_const (&u, user);
+ context->fork = bus_config_parser_get_fork (parser);
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ retval = TRUE;
- if (!_dbus_credentials_from_username (&u, &creds) ||
- creds.uid < 0 ||
- creds.gid < 0)
- {
- dbus_set_error (error, DBUS_ERROR_FAILED,
- "Could not get UID and GID for username \"%s\"",
- user);
- goto failed;
- }
-
- if (!_dbus_change_identity (creds.uid, creds.gid, error))
- goto failed;
- }
+ failed:
+ dbus_free_string_array (auth_mechanisms);
+ return retval;
+}
- /* note that type may be NULL */
- context->type = _dbus_strdup (bus_config_parser_get_type (parser));
+/* This code gets executed every time the config files
+ are parsed: both during BusContext construction
+ and on reloads. */
+static dbus_bool_t
+process_config_every_time (BusContext *context,
+ BusConfigParser *parser,
+ dbus_bool_t is_reload,
+ DBusError *error)
+{
+ DBusString full_address;
+ DBusList *link;
+ DBusHashTable *service_sid_table;
+ dbus_bool_t retval;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ retval = FALSE;
+
+ if (!_dbus_string_init (&full_address))
+ {
+ BUS_SET_OOM (error);
+ return FALSE;
+ }
+
+ /* get our limits and timeout lengths */
+ bus_config_parser_get_limits (parser, &context->limits);
+
+ context->policy = bus_config_parser_steal_policy (parser);
+ _dbus_assert (context->policy != NULL);
+
/* We have to build the address backward, so that
* <listen> later in the config file have priority
*/
link = _dbus_list_get_prev_link (&context->servers, link);
}
+ if (is_reload)
+ dbus_free (context->address);
+
if (!_dbus_string_copy_data (&full_address, &context->address))
{
BUS_SET_OOM (error);
/* Create activation subsystem */
+ if (is_reload)
+ bus_activation_unref (context->activation);
+
context->activation = bus_activation_new (context, &full_address,
bus_config_parser_get_service_dirs (parser),
error);
goto failed;
}
- context->connections = bus_connections_new (context);
- if (context->connections == NULL)
+ service_sid_table = bus_config_parser_steal_service_sid_table (parser);
+ bus_registry_set_service_sid_table (context->registry,
+ service_sid_table);
+ _dbus_hash_table_unref (service_sid_table);
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ retval = TRUE;
+
+ failed:
+ _dbus_string_free (&full_address);
+ return retval;
+}
+
+static dbus_bool_t
+load_config (BusContext *context,
+ dbus_bool_t is_reload,
+ DBusError *error)
+{
+ BusConfigParser *parser;
+ DBusString config_file;
+ dbus_bool_t retval;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ retval = FALSE;
+ parser = NULL;
+
+ _dbus_string_init_const (&config_file, context->config_file);
+ parser = bus_config_load (&config_file, TRUE, NULL, error);
+ if (parser == NULL)
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto failed;
+ }
+
+ if (!is_reload && !process_config_first_time_only (context, parser, error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto failed;
+ }
+
+ if (!process_config_every_time (context, parser, is_reload, error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto failed;
+ }
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ retval = TRUE;
+
+ failed:
+ if (parser)
+ bus_config_parser_unref (parser);
+ return retval;
+}
+
+BusContext*
+bus_context_new (const DBusString *config_file,
+ dbus_bool_t force_fork,
+ int print_addr_fd,
+ int print_pid_fd,
+ DBusError *error)
+{
+ BusContext *context;
+
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ if (!dbus_server_allocate_data_slot (&server_data_slot))
+ {
+ BUS_SET_OOM (error);
+ return NULL;
+ }
+
+ context = dbus_new0 (BusContext, 1);
+ if (context == NULL)
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+ context->refcount = 1;
+
+ if (!_dbus_string_copy_data (config_file, &context->config_file))
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ context->loop = _dbus_loop_new ();
+ if (context->loop == NULL)
{
BUS_SET_OOM (error);
goto failed;
goto failed;
}
- context->rules_by_uid = _dbus_hash_table_new (DBUS_HASH_ULONG,
- NULL,
- free_rule_list_func);
- if (context->rules_by_uid == NULL)
+ if (!load_config (context, FALSE, error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto failed;
+ }
+
+ /* we need another ref of the server data slot for the context
+ * to own
+ */
+ if (!dbus_server_allocate_data_slot (&server_data_slot))
+ _dbus_assert_not_reached ("second ref of server data slot failed");
+
+ context->user_database = _dbus_user_database_new ();
+ if (context->user_database == NULL)
{
BUS_SET_OOM (error);
goto failed;
}
+
+ /* Note that we don't know whether the print_addr_fd is
+ * one of the sockets we're using to listen on, or some
+ * other random thing. But I think the answer is "don't do
+ * that then"
+ */
+ if (print_addr_fd >= 0)
+ {
+ DBusString addr;
+ const char *a = bus_context_get_address (context);
+ int bytes;
+
+ _dbus_assert (a != NULL);
+ if (!_dbus_string_init (&addr))
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ if (!_dbus_string_append (&addr, a) ||
+ !_dbus_string_append (&addr, "\n"))
+ {
+ _dbus_string_free (&addr);
+ BUS_SET_OOM (error);
+ goto failed;
+ }
- context->rules_by_gid = _dbus_hash_table_new (DBUS_HASH_ULONG,
- NULL,
- free_rule_list_func);
- if (context->rules_by_gid == NULL)
+ bytes = _dbus_string_get_length (&addr);
+ if (_dbus_write (print_addr_fd, &addr, 0, bytes) != bytes)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Printing message bus address: %s\n",
+ _dbus_strerror (errno));
+ _dbus_string_free (&addr);
+ goto failed;
+ }
+
+ if (print_addr_fd > 2)
+ _dbus_close (print_addr_fd, NULL);
+
+ _dbus_string_free (&addr);
+ }
+
+ context->connections = bus_connections_new (context);
+ if (context->connections == NULL)
{
BUS_SET_OOM (error);
goto failed;
}
+ context->matchmaker = bus_matchmaker_new ();
+ if (context->matchmaker == NULL)
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
/* Now become a daemon if appropriate */
- if (bus_config_parser_get_fork (parser))
+ if (force_fork || context->fork)
+ {
+ DBusString u;
+
+ if (context->pidfile)
+ _dbus_string_init_const (&u, context->pidfile);
+
+ if (!_dbus_become_daemon (context->pidfile ? &u : NULL, error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto failed;
+ }
+ }
+ else
{
- if (!_dbus_become_daemon (error))
- goto failed;
+ /* Need to write PID file for ourselves, not for the child process */
+ if (context->pidfile != NULL)
+ {
+ DBusString u;
+
+ _dbus_string_init_const (&u, context->pidfile);
+
+ if (!_dbus_write_pid_file (&u, _dbus_getpid (), error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto failed;
+ }
+ }
+ }
+
+ /* Write PID if requested */
+ if (print_pid_fd >= 0)
+ {
+ DBusString pid;
+ int bytes;
+
+ if (!_dbus_string_init (&pid))
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ if (!_dbus_string_append_int (&pid, _dbus_getpid ()) ||
+ !_dbus_string_append (&pid, "\n"))
+ {
+ _dbus_string_free (&pid);
+ BUS_SET_OOM (error);
+ goto failed;
+ }
+
+ bytes = _dbus_string_get_length (&pid);
+ if (_dbus_write (print_pid_fd, &pid, 0, bytes) != bytes)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Printing message bus PID: %s\n",
+ _dbus_strerror (errno));
+ _dbus_string_free (&pid);
+ goto failed;
+ }
+
+ if (print_pid_fd > 2)
+ _dbus_close (print_pid_fd, NULL);
+
+ _dbus_string_free (&pid);
}
- bus_config_parser_unref (parser);
- _dbus_string_free (&full_address);
- dbus_free_string_array (auth_mechanisms);
- server_data_slot_unref ();
+ /* Here we change our credentials if required,
+ * as soon as we've set up our sockets and pidfile
+ */
+ if (context->user != NULL)
+ {
+ DBusCredentials creds;
+ DBusString u;
+
+ _dbus_string_init_const (&u, context->user);
+
+ if (!_dbus_credentials_from_username (&u, &creds) ||
+ creds.uid < 0 ||
+ creds.gid < 0)
+ {
+ dbus_set_error (error, DBUS_ERROR_FAILED,
+ "Could not get UID and GID for username \"%s\"",
+ context->user);
+ goto failed;
+ }
+
+ if (!_dbus_change_identity (creds.uid, creds.gid, error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ goto failed;
+ }
+ }
+
+ dbus_server_free_data_slot (&server_data_slot);
return context;
failed:
- if (parser != NULL)
- bus_config_parser_unref (parser);
-
if (context != NULL)
bus_context_unref (context);
- _dbus_string_free (&full_address);
- dbus_free_string_array (auth_mechanisms);
-
- server_data_slot_unref ();
+ if (server_data_slot >= 0)
+ dbus_server_free_data_slot (&server_data_slot);
return NULL;
}
+dbus_bool_t
+bus_context_reload_config (BusContext *context,
+ DBusError *error)
+{
+ return load_config (context,
+ TRUE, /* yes, we are re-loading */
+ error);
+}
+
static void
shutdown_server (BusContext *context,
DBusServer *server)
}
}
-void
+BusContext *
bus_context_ref (BusContext *context)
{
_dbus_assert (context->refcount > 0);
context->refcount += 1;
+
+ return context;
}
void
}
_dbus_list_clear (&context->servers);
- if (context->rules_by_uid)
+ if (context->policy)
{
- _dbus_hash_table_unref (context->rules_by_uid);
- context->rules_by_uid = NULL;
+ bus_policy_unref (context->policy);
+ context->policy = NULL;
}
-
- if (context->rules_by_gid)
+
+ if (context->loop)
{
- _dbus_hash_table_unref (context->rules_by_gid);
- context->rules_by_gid = NULL;
+ _dbus_loop_unref (context->loop);
+ context->loop = NULL;
}
- if (context->loop)
+ if (context->matchmaker)
{
- bus_loop_unref (context->loop);
- context->loop = NULL;
+ bus_matchmaker_unref (context->matchmaker);
+ context->matchmaker = NULL;
}
+ dbus_free (context->config_file);
dbus_free (context->type);
dbus_free (context->address);
+ dbus_free (context->user);
+
+ if (context->pidfile)
+ {
+ DBusString u;
+ _dbus_string_init_const (&u, context->pidfile);
+
+ /* Deliberately ignore errors here, since there's not much
+ * we can do about it, and we're exiting anyways.
+ */
+ _dbus_delete_file (&u, NULL);
+
+ dbus_free (context->pidfile);
+ }
+
+ if (context->user_database != NULL)
+ _dbus_user_database_unref (context->user_database);
+
dbus_free (context);
- server_data_slot_unref ();
+ dbus_server_free_data_slot (&server_data_slot);
}
}
return context->activation;
}
-BusLoop*
+BusMatchmaker*
+bus_context_get_matchmaker (BusContext *context)
+{
+ return context->matchmaker;
+}
+
+DBusLoop*
bus_context_get_loop (BusContext *context)
{
return context->loop;
}
-static dbus_bool_t
-list_allows_user (dbus_bool_t def,
- DBusList **list,
- unsigned long uid,
- const unsigned long *group_ids,
- int n_group_ids)
+DBusUserDatabase*
+bus_context_get_user_database (BusContext *context)
{
- DBusList *link;
- dbus_bool_t allowed;
-
- allowed = def;
-
- link = _dbus_list_get_first_link (list);
- while (link != NULL)
- {
- BusPolicyRule *rule = link->data;
- link = _dbus_list_get_next_link (list, link);
-
- if (rule->type == BUS_POLICY_RULE_USER)
- {
- if (rule->d.user.uid != uid)
- continue;
- }
- else if (rule->type == BUS_POLICY_RULE_GROUP)
- {
- int i;
-
- i = 0;
- while (i < n_group_ids)
- {
- if (rule->d.group.gid == group_ids[i])
- break;
- ++i;
- }
-
- if (i == n_group_ids)
- continue;
- }
- else
- continue;
-
- allowed = rule->allow;
- }
-
- return allowed;
+ return context->user_database;
}
dbus_bool_t
bus_context_allow_user (BusContext *context,
unsigned long uid)
{
- dbus_bool_t allowed;
- unsigned long *group_ids;
- int n_group_ids;
+ return bus_policy_allow_user (context->policy,
+ context->user_database,
+ uid);
+}
+
+BusPolicy *
+bus_context_get_policy (BusContext *context)
+{
+ return context->policy;
+}
- /* On OOM or error we always reject the user */
- if (!_dbus_get_groups (uid, &group_ids, &n_group_ids))
- {
- _dbus_verbose ("Did not get any groups for UID %lu\n",
- uid);
- return FALSE;
- }
+BusClientPolicy*
+bus_context_create_client_policy (BusContext *context,
+ DBusConnection *connection,
+ DBusError *error)
+{
+ _DBUS_ASSERT_ERROR_IS_CLEAR (error);
+ return bus_policy_create_client_policy (context->policy, connection,
+ error);
+}
+
+int
+bus_context_get_activation_timeout (BusContext *context)
+{
- allowed = FALSE;
+ return context->limits.activation_timeout;
+}
- allowed = list_allows_user (allowed,
- &context->default_rules,
- uid,
- group_ids, n_group_ids);
+int
+bus_context_get_auth_timeout (BusContext *context)
+{
+ return context->limits.auth_timeout;
+}
- allowed = list_allows_user (allowed,
- &context->mandatory_rules,
- uid,
- group_ids, n_group_ids);
+int
+bus_context_get_max_completed_connections (BusContext *context)
+{
+ return context->limits.max_completed_connections;
+}
- dbus_free (group_ids);
+int
+bus_context_get_max_incomplete_connections (BusContext *context)
+{
+ return context->limits.max_incomplete_connections;
+}
- return allowed;
+int
+bus_context_get_max_connections_per_user (BusContext *context)
+{
+ return context->limits.max_connections_per_user;
}
-static dbus_bool_t
-add_list_to_policy (DBusList **list,
- BusPolicy *policy)
+int
+bus_context_get_max_pending_activations (BusContext *context)
{
- DBusList *link;
+ return context->limits.max_pending_activations;
+}
- link = _dbus_list_get_first_link (list);
- while (link != NULL)
- {
- BusPolicyRule *rule = link->data;
- link = _dbus_list_get_next_link (list, link);
+int
+bus_context_get_max_services_per_connection (BusContext *context)
+{
+ return context->limits.max_services_per_connection;
+}
- switch (rule->type)
- {
- case BUS_POLICY_RULE_USER:
- case BUS_POLICY_RULE_GROUP:
- /* These aren't per-connection policies */
- break;
-
- case BUS_POLICY_RULE_OWN:
- case BUS_POLICY_RULE_SEND:
- case BUS_POLICY_RULE_RECEIVE:
- /* These are per-connection */
- if (!bus_policy_append_rule (policy, rule))
- return FALSE;
- break;
- }
- }
-
- return TRUE;
+int
+bus_context_get_max_match_rules_per_connection (BusContext *context)
+{
+ return context->limits.max_match_rules_per_connection;
+}
+
+int
+bus_context_get_max_replies_per_connection (BusContext *context)
+{
+ return context->limits.max_replies_per_connection;
}
-BusPolicy*
-bus_context_create_connection_policy (BusContext *context,
- DBusConnection *connection)
+int
+bus_context_get_reply_timeout (BusContext *context)
{
- BusPolicy *policy;
- unsigned long uid;
- DBusList **list;
+ return context->limits.reply_timeout;
+}
- _dbus_assert (dbus_connection_get_is_authenticated (connection));
+/*
+ * addressed_recipient is the recipient specified in the message.
+ *
+ * proposed_recipient is the recipient we're considering sending
+ * to right this second, and may be an eavesdropper.
+ *
+ * sender is the sender of the message.
+ *
+ * NULL for proposed_recipient or sender definitely means the bus driver.
+ *
+ * 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)
+{
+ BusClientPolicy *sender_policy;
+ BusClientPolicy *recipient_policy;
+ int type;
+ dbus_bool_t requested_reply;
- policy = bus_policy_new ();
- if (policy == NULL)
- return NULL;
+ type = dbus_message_get_type (message);
+
+ /* dispatch.c was supposed to ensure these invariants */
+ _dbus_assert (dbus_message_get_destination (message) != NULL ||
+ type == DBUS_MESSAGE_TYPE_SIGNAL ||
+ (sender == NULL && !bus_connection_is_active (proposed_recipient)));
+ _dbus_assert (type == DBUS_MESSAGE_TYPE_SIGNAL ||
+ addressed_recipient != NULL ||
+ strcmp (dbus_message_get_destination (message), DBUS_SERVICE_ORG_FREEDESKTOP_DBUS) == 0);
+
+ switch (type)
+ {
+ case DBUS_MESSAGE_TYPE_METHOD_CALL:
+ case DBUS_MESSAGE_TYPE_SIGNAL:
+ case DBUS_MESSAGE_TYPE_METHOD_RETURN:
+ case DBUS_MESSAGE_TYPE_ERROR:
+ break;
+
+ default:
+ _dbus_verbose ("security check disallowing message of unknown type %d\n",
+ type);
- if (!add_list_to_policy (&context->default_rules,
- policy))
- goto failed;
+ dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
+ "Message bus will not accept messages of unknown type\n");
+
+ return FALSE;
+ }
- /* we avoid the overhead of looking up user's groups
- * if we don't have any group rules anyway
- */
- if (_dbus_hash_table_get_n_entries (context->rules_by_gid) > 0)
+ requested_reply = FALSE;
+
+ if (sender != NULL)
{
- const unsigned long *groups;
- int n_groups;
- int i;
-
- if (!bus_connection_get_groups (connection, &groups, &n_groups))
- goto failed;
-
- i = 0;
- while (i < n_groups)
+ /* First verify the SELinux access controls. If allowed then
+ * go on with the standard checks.
+ */
+ if (!bus_selinux_allows_send (sender, proposed_recipient))
+ {
+ const char *dest = dbus_message_get_destination (message);
+ dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
+ "An SELinux policy prevents this sender "
+ "from sending this message to this recipient "
+ "(rejected message had interface \"%s\" "
+ "member \"%s\" error name \"%s\" destination \"%s\")",
+ dbus_message_get_interface (message) ?
+ dbus_message_get_interface (message) : "(unset)",
+ dbus_message_get_member (message) ?
+ dbus_message_get_member (message) : "(unset)",
+ dbus_message_get_error_name (message) ?
+ dbus_message_get_error_name (message) : "(unset)",
+ dest ? dest : DBUS_SERVICE_ORG_FREEDESKTOP_DBUS);
+ _dbus_verbose ("SELinux security check denying send to service\n");
+ return FALSE;
+ }
+
+ if (bus_connection_is_active (sender))
{
- list = _dbus_hash_table_lookup_ulong (context->rules_by_gid,
- groups[i]);
+ sender_policy = bus_connection_get_policy (sender);
+ _dbus_assert (sender_policy != NULL);
- if (list != NULL)
+ /* Fill in requested_reply variable with TRUE if this is a
+ * reply and the reply was pending.
+ */
+ if (dbus_message_get_reply_serial (message) != 0)
{
- if (!add_list_to_policy (list, policy))
- goto failed;
+ if (proposed_recipient != NULL /* not to the bus driver */ &&
+ addressed_recipient == proposed_recipient /* not eavesdropping */)
+ {
+ DBusError error2;
+
+ dbus_error_init (&error2);
+ requested_reply = bus_connections_check_reply (bus_connection_get_connections (sender),
+ transaction,
+ sender, addressed_recipient, message,
+ &error2);
+ if (dbus_error_is_set (&error2))
+ {
+ dbus_move_error (&error2, error);
+ return FALSE;
+ }
+ }
+ }
+ }
+ else
+ {
+ /* Policy for inactive connections is that they can only send
+ * the hello message to the bus driver
+ */
+ if (proposed_recipient == NULL &&
+ dbus_message_is_method_call (message,
+ DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS,
+ "Hello"))
+ {
+ _dbus_verbose ("security check allowing %s message\n",
+ "Hello");
+ return TRUE;
+ }
+ else
+ {
+ _dbus_verbose ("security check disallowing non-%s message\n",
+ "Hello");
+
+ dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
+ "Client tried to send a message other than %s without being registered",
+ "Hello");
+
+ return FALSE;
}
-
- ++i;
}
}
+ else
+ {
+ sender_policy = NULL;
- if (!dbus_connection_get_unix_user (connection, &uid))
- goto failed;
-
- list = _dbus_hash_table_lookup_ulong (context->rules_by_uid,
- uid);
+ /* If the sender is the bus driver, we assume any reply was a
+ * requested reply as bus driver won't send bogus ones
+ */
+ if (addressed_recipient == proposed_recipient /* not eavesdropping */ &&
+ dbus_message_get_reply_serial (message) != 0)
+ requested_reply = TRUE;
+ }
- if (!add_list_to_policy (list, policy))
- goto failed;
+ _dbus_assert ((sender != NULL && sender_policy != NULL) ||
+ (sender == NULL && sender_policy == NULL));
- if (!add_list_to_policy (&context->mandatory_rules,
- policy))
- goto failed;
-
- bus_policy_optimize (policy);
+ if (proposed_recipient != NULL)
+ {
+ /* only the bus driver can send to an inactive recipient (as it
+ * owns no services, so other apps can't address it). Inactive
+ * recipients can receive any message.
+ */
+ if (bus_connection_is_active (proposed_recipient))
+ {
+ recipient_policy = bus_connection_get_policy (proposed_recipient);
+ _dbus_assert (recipient_policy != NULL);
+ }
+ else if (sender == NULL)
+ {
+ _dbus_verbose ("security check using NULL recipient policy for message from bus\n");
+ recipient_policy = NULL;
+ }
+ else
+ {
+ _dbus_assert_not_reached ("a message was somehow sent to an inactive recipient from a source other than the message bus\n");
+ recipient_policy = NULL;
+ }
+ }
+ else
+ recipient_policy = NULL;
- return policy;
+ _dbus_assert ((proposed_recipient != NULL && recipient_policy != NULL) ||
+ (proposed_recipient != NULL && sender == NULL && recipient_policy == NULL) ||
+ (proposed_recipient == NULL && recipient_policy == NULL));
- failed:
- bus_policy_unref (policy);
- return NULL;
-}
+ if (sender_policy &&
+ !bus_client_policy_check_can_send (sender_policy,
+ context->registry,
+ requested_reply,
+ proposed_recipient,
+ message))
+ {
+ const char *dest = dbus_message_get_destination (message);
+ dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
+ "A security policy in place prevents this sender "
+ "from sending this message to this recipient, "
+ "see message bus configuration file (rejected message "
+ "had interface \"%s\" member \"%s\" error name \"%s\" destination \"%s\")",
+ dbus_message_get_interface (message) ?
+ dbus_message_get_interface (message) : "(unset)",
+ dbus_message_get_member (message) ?
+ dbus_message_get_member (message) : "(unset)",
+ dbus_message_get_error_name (message) ?
+ dbus_message_get_error_name (message) : "(unset)",
+ dest ? dest : DBUS_SERVICE_ORG_FREEDESKTOP_DBUS);
+ _dbus_verbose ("security policy disallowing message due to sender policy\n");
+ return FALSE;
+ }
-int
-bus_context_get_activation_timeout (BusContext *context)
-{
+ if (recipient_policy &&
+ !bus_client_policy_check_can_receive (recipient_policy,
+ context->registry,
+ requested_reply,
+ sender,
+ addressed_recipient, proposed_recipient,
+ message))
+ {
+ const char *dest = dbus_message_get_destination (message);
+ dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
+ "A security policy in place prevents this recipient "
+ "from receiving this message from this sender, "
+ "see message bus configuration file (rejected message "
+ "had interface \"%s\" member \"%s\" error name \"%s\" destination \"%s\" reply serial %u requested_reply=%d)",
+ dbus_message_get_interface (message) ?
+ dbus_message_get_interface (message) : "(unset)",
+ dbus_message_get_member (message) ?
+ dbus_message_get_member (message) : "(unset)",
+ dbus_message_get_error_name (message) ?
+ dbus_message_get_error_name (message) : "(unset)",
+ dest ? dest : DBUS_SERVICE_ORG_FREEDESKTOP_DBUS,
+ dbus_message_get_reply_serial (message),
+ requested_reply);
+ _dbus_verbose ("security policy disallowing message due to recipient policy\n");
+ return FALSE;
+ }
+
+ /* See if limits on size have been exceeded */
+ if (proposed_recipient &&
+ dbus_connection_get_outgoing_size (proposed_recipient) >
+ context->limits.max_outgoing_bytes)
+ {
+ const char *dest = dbus_message_get_destination (message);
+ dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
+ "The destination service \"%s\" has a full message queue",
+ dest ? dest : (proposed_recipient ?
+ bus_connection_get_name (proposed_recipient) :
+ DBUS_SERVICE_ORG_FREEDESKTOP_DBUS));
+ _dbus_verbose ("security policy disallowing message due to full message queue\n");
+ return FALSE;
+ }
+
+ if (type == DBUS_MESSAGE_TYPE_METHOD_CALL)
+ {
+ /* Record that we will allow a reply here in the future (don't
+ * bother if the recipient is the bus). Only the addressed recipient
+ * may reply.
+ */
+ if (sender && addressed_recipient &&
+ !bus_connections_expect_reply (bus_connection_get_connections (sender),
+ transaction,
+ sender, addressed_recipient,
+ message, error))
+ {
+ _dbus_verbose ("Failed to record reply expectation or problem with the message expecting a reply\n");
+ return FALSE;
+ }
+ }
- return context->activation_timeout;
+ _dbus_verbose ("security policy allowing message\n");
+ return TRUE;
}