*/
#include "bus.h"
-#include "loop.h"
#include "activation.h"
#include "connection.h"
#include "services.h"
int refcount;
char *type;
char *address;
- BusLoop *loop;
+ char *pidfile;
+ 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;
+ DBusUserDatabase *user_database;
+ BusLimits limits;
};
static int server_data_slot = -1;
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;
+ dbus_connection_set_max_received_size (new_connection,
+ context->limits.max_incoming_bytes);
- bus_policy_rule_unref (rule);
-}
-
-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
DBusList **addresses;
BusConfigParser *parser;
DBusString full_address;
- const char *user;
+ const char *user, *pidfile;
char **auth_mechanisms;
DBusList **auth_mechanisms_list;
int len;
parser = bus_config_load (config_file, error);
if (parser == NULL)
goto failed;
+
+ /* 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.
+ */
+ pidfile = bus_config_parser_get_pidfile (parser);
+ if (pidfile != NULL)
+ {
+ 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;
+ }
+ }
context = dbus_new0 (BusContext, 1);
if (context == NULL)
context->refcount = 1;
+ /* get our limits and timeout lengths */
+ bus_config_parser_get_limits (parser, &context->limits);
+
/* 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.
- */
- context->max_completed_connections = 1024;
+ context->user_database = _dbus_user_database_new ();
+ if (context->user_database == NULL)
+ {
+ BUS_SET_OOM (error);
+ goto failed;
+ }
- context->loop = bus_loop_new ();
+ context->loop = _dbus_loop_new ();
if (context->loop == NULL)
{
BUS_SET_OOM (error);
link = _dbus_list_get_next_link (addresses, link);
}
- /* Here we change our credentials if required,
- * as soon as we've set up our sockets
- */
- user = bus_config_parser_get_user (parser);
- if (user != NULL)
- {
- DBusCredentials creds;
- DBusString u;
-
- _dbus_string_init_const (&u, 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\"",
- user);
- goto failed;
- }
-
- if (!_dbus_change_identity (creds.uid, creds.gid, error))
- goto failed;
- }
-
/* note that type may be NULL */
context->type = _dbus_strdup (bus_config_parser_get_type (parser));
BUS_SET_OOM (error);
goto failed;
}
+
+ context->policy = bus_config_parser_steal_policy (parser);
+ _dbus_assert (context->policy != NULL);
- context->rules_by_uid = _dbus_hash_table_new (DBUS_HASH_ULONG,
- NULL,
- free_rule_list_func);
- if (context->rules_by_uid == NULL)
+ /* Now become a daemon if appropriate */
+ if (bus_config_parser_get_fork (parser))
{
- BUS_SET_OOM (error);
- goto failed;
- }
+ DBusString u;
- context->rules_by_gid = _dbus_hash_table_new (DBUS_HASH_ULONG,
- NULL,
- free_rule_list_func);
- if (context->rules_by_gid == NULL)
+ if (pidfile)
+ _dbus_string_init_const (&u, pidfile);
+
+ if (!_dbus_become_daemon (pidfile ? &u : NULL, error))
+ goto failed;
+ }
+ else
{
- BUS_SET_OOM (error);
- goto failed;
+ /* Need to write PID file for ourselves, not for the child process */
+ if (pidfile != NULL)
+ {
+ DBusString u;
+
+ _dbus_string_init_const (&u, pidfile);
+
+ if (!_dbus_write_pid_file (&u, _dbus_getpid (), error))
+ goto failed;
+ }
}
- /* Now become a daemon if appropriate */
- if (bus_config_parser_get_fork (parser))
+ /* keep around the pid filename so we can delete it later */
+ context->pidfile = _dbus_strdup (pidfile);
+
+ /* Here we change our credentials if required,
+ * as soon as we've set up our sockets and pidfile
+ */
+ user = bus_config_parser_get_user (parser);
+ if (user != NULL)
{
- if (!_dbus_become_daemon (error))
+ DBusCredentials creds;
+ DBusString u;
+
+ _dbus_string_init_const (&u, 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\"",
+ user);
+ goto failed;
+ }
+
+ if (!_dbus_change_identity (creds.uid, creds.gid, error))
goto failed;
}
}
_dbus_list_clear (&context->servers);
- if (context->rules_by_uid)
- {
- _dbus_hash_table_unref (context->rules_by_uid);
- context->rules_by_uid = NULL;
- }
-
- if (context->rules_by_gid)
+ if (context->policy)
{
- _dbus_hash_table_unref (context->rules_by_gid);
- context->rules_by_gid = NULL;
+ bus_policy_unref (context->policy);
+ context->policy = NULL;
}
-
+
if (context->loop)
{
- bus_loop_unref (context->loop);
+ _dbus_loop_unref (context->loop);
context->loop = NULL;
}
dbus_free (context->type);
dbus_free (context->address);
+
+ 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);
+ }
+
+ _dbus_user_database_unref (context->user_database);
+
dbus_free (context);
server_data_slot_unref ();
return context->activation;
}
-BusLoop*
+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;
-
- /* 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;
- }
-
- allowed = FALSE;
+ return bus_policy_allow_user (context->policy,
+ context->user_database,
+ uid);
+}
- allowed = list_allows_user (allowed,
- &context->default_rules,
- uid,
- group_ids, n_group_ids);
+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);
+}
- allowed = list_allows_user (allowed,
- &context->mandatory_rules,
- uid,
- group_ids, n_group_ids);
+int
+bus_context_get_activation_timeout (BusContext *context)
+{
+
+ return context->limits.activation_timeout;
+}
- dbus_free (group_ids);
+int
+bus_context_get_auth_timeout (BusContext *context)
+{
+ return context->limits.auth_timeout;
+}
- return allowed;
+int
+bus_context_get_max_completed_connections (BusContext *context)
+{
+ return context->limits.max_completed_connections;
}
-static dbus_bool_t
-add_list_to_policy (DBusList **list,
- BusPolicy *policy)
+int
+bus_context_get_max_incomplete_connections (BusContext *context)
{
- DBusList *link;
+ return context->limits.max_incomplete_connections;
+}
- 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_connections_per_user (BusContext *context)
+{
+ return context->limits.max_connections_per_user;
+}
- 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_pending_activations (BusContext *context)
+{
+ return context->limits.max_pending_activations;
}
-BusPolicy*
-bus_context_create_connection_policy (BusContext *context,
- DBusConnection *connection)
+int
+bus_context_get_max_services_per_connection (BusContext *context)
{
- BusPolicy *policy;
- unsigned long uid;
- DBusList **list;
+ return context->limits.max_services_per_connection;
+}
- _dbus_assert (dbus_connection_get_is_authenticated (connection));
-
- policy = bus_policy_new ();
- if (policy == NULL)
- return NULL;
+dbus_bool_t
+bus_context_check_security_policy (BusContext *context,
+ DBusConnection *sender,
+ DBusConnection *recipient,
+ DBusMessage *message,
+ DBusError *error)
+{
+ BusClientPolicy *sender_policy;
+ BusClientPolicy *recipient_policy;
- if (!add_list_to_policy (&context->default_rules,
- policy))
- goto failed;
+ /* NULL sender/receiver means the bus driver */
- /* 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)
+ 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)
+ if (bus_connection_is_active (sender))
{
- list = _dbus_hash_table_lookup_ulong (context->rules_by_gid,
- groups[i]);
-
- if (list != NULL)
+ sender_policy = bus_connection_get_policy (sender);
+ _dbus_assert (sender_policy != NULL);
+ }
+ else
+ {
+ /* Policy for inactive connections is that they can only send
+ * the hello message to the bus driver
+ */
+ if (recipient == NULL &&
+ dbus_message_has_name (message, DBUS_MESSAGE_HELLO))
{
- if (!add_list_to_policy (list, policy))
- goto failed;
+ _dbus_verbose ("security check allowing %s message\n",
+ DBUS_MESSAGE_HELLO);
+ return TRUE;
+ }
+ else
+ {
+ _dbus_verbose ("security check disallowing non-%s message\n",
+ DBUS_MESSAGE_HELLO);
+
+ dbus_set_error (error, DBUS_ERROR_ACCESS_DENIED,
+ "Client tried to send a message other than %s without being registered",
+ DBUS_MESSAGE_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 (!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 (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 (recipient))
+ {
+ recipient_policy = bus_connection_get_policy (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 ((recipient != NULL && recipient_policy != NULL) ||
+ (recipient != NULL && sender == NULL && recipient_policy == NULL) ||
+ (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, 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 name \"%s\" destination \"%s\")",
+ dbus_message_get_name (message),
+ dest ? dest : DBUS_SERVICE_DBUS);
+ return FALSE;
+ }
-int
-bus_context_get_activation_timeout (BusContext *context)
-{
+ if (recipient_policy &&
+ !bus_client_policy_check_can_receive (recipient_policy,
+ context->registry, sender,
+ 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 name \"%s\" destination \"%s\")",
+ dbus_message_get_name (message),
+ dest ? dest : DBUS_SERVICE_DBUS);
+ return FALSE;
+ }
+
+ /* See if limits on size have been exceeded */
+ if (recipient &&
+ dbus_connection_get_outgoing_size (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 : DBUS_SERVICE_DBUS);
+ return FALSE;
+ }
- return context->activation_timeout;
+ return TRUE;
}