summaryrefslogtreecommitdiffstats
path: root/bus
diff options
context:
space:
mode:
authorHavoc Pennington <hp@redhat.com>2003-04-19 16:16:24 +0000
committerHavoc Pennington <hp@redhat.com>2003-04-19 16:16:24 +0000
commit983200f912f41ba75a873c011bfbcd3b0285bf4c (patch)
tree794516f892061dbbcfb036110d69850885edda3d /bus
parentd3fb6f35716ff1d6f6644dea2043d539007811de (diff)
2003-04-19 Havoc Pennington <hp@pobox.com>
* bus/driver.c (bus_driver_handle_hello): check limits and return an error if they are exceeded. * bus/connection.c: maintain separate lists of active and inactive connections, and a count of each. Maintain count of completed connections per user. Implement code to check connection limits. * dbus/dbus-list.c (_dbus_list_unlink): export * bus/bus.c (bus_context_check_security_policy): enforce a maximum number of bytes in the message queue for a connection
Diffstat (limited to 'bus')
-rw-r--r--bus/bus.c56
-rw-r--r--bus/bus.h51
-rw-r--r--bus/connection.c317
-rw-r--r--bus/connection.h10
-rw-r--r--bus/dispatch.c5
-rw-r--r--bus/driver.c15
6 files changed, 394 insertions, 60 deletions
diff --git a/bus/bus.c b/bus/bus.c
index 2b0f5091..414f15e4 100644
--- a/bus/bus.c
+++ b/bus/bus.c
@@ -45,8 +45,11 @@ struct BusContext
BusRegistry *registry;
BusPolicy *policy;
DBusUserDatabase *user_database;
- 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 */
+ long max_incoming_bytes; /**< How many incoming messages for a connection */
+ long max_outgoing_bytes; /**< How many outgoing bytes can be queued for a connection */
+ long max_message_size; /**< Max size of a single message in bytes */
+ 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 */
@@ -210,6 +213,12 @@ new_connection_callback (DBusServer *server,
*/
dbus_connection_disconnect (new_connection);
}
+
+ dbus_connection_set_max_received_size (new_connection,
+ context->max_incoming_bytes);
+
+ dbus_connection_set_max_message_size (new_connection,
+ context->max_message_size);
/* on OOM, we won't have ref'd the connection so it will die. */
}
@@ -353,6 +362,11 @@ bus_context_new (const DBusString *config_file,
*/
if (!server_data_slot_ref ())
_dbus_assert_not_reached ("second ref of server data slot failed");
+
+ /* Make up some numbers! woot! */
+ context->max_incoming_bytes = _DBUS_ONE_MEGABYTE * 63;
+ context->max_outgoing_bytes = _DBUS_ONE_MEGABYTE * 63;
+ context->max_message_size = _DBUS_ONE_MEGABYTE * 32;
#ifdef DBUS_BUILD_TESTS
context->activation_timeout = 6000; /* 6 seconds */
@@ -375,7 +389,7 @@ bus_context_new (const DBusString *config_file,
* DOS all the other users.
*/
context->max_completed_connections = 1024;
-
+
context->user_database = _dbus_user_database_new ();
if (context->user_database == NULL)
{
@@ -818,6 +832,30 @@ bus_context_get_activation_timeout (BusContext *context)
return context->activation_timeout;
}
+int
+bus_context_get_auth_timeout (BusContext *context)
+{
+ return context->auth_timeout;
+}
+
+int
+bus_context_get_max_completed_connections (BusContext *context)
+{
+ return context->max_completed_connections;
+}
+
+int
+bus_context_get_max_incomplete_connections (BusContext *context)
+{
+ return context->max_incomplete_connections;
+}
+
+int
+bus_context_get_max_connections_per_user (BusContext *context)
+{
+ return context->max_connections_per_user;
+}
+
dbus_bool_t
bus_context_check_security_policy (BusContext *context,
DBusConnection *sender,
@@ -878,5 +916,17 @@ bus_context_check_security_policy (BusContext *context,
return FALSE;
}
+ /* See if limits on size have been exceeded */
+ if (recipient &&
+ dbus_connection_get_outgoing_size (recipient) >
+ context->max_outgoing_bytes)
+ {
+ const char *dest = dbus_message_get_service (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 TRUE;
}
diff --git a/bus/bus.h b/bus/bus.h
index 7369d220..e4e6dabe 100644
--- a/bus/bus.h
+++ b/bus/bus.h
@@ -41,29 +41,34 @@ typedef struct BusRegistry BusRegistry;
typedef struct BusService BusService;
typedef struct BusTransaction BusTransaction;
-BusContext* bus_context_new (const DBusString *config_file,
- int print_addr_fd,
- DBusError *error);
-void bus_context_shutdown (BusContext *context);
-void bus_context_ref (BusContext *context);
-void bus_context_unref (BusContext *context);
-const char* bus_context_get_type (BusContext *context);
-const char* bus_context_get_address (BusContext *context);
-BusRegistry* bus_context_get_registry (BusContext *context);
-BusConnections* bus_context_get_connections (BusContext *context);
-BusActivation* bus_context_get_activation (BusContext *context);
-DBusLoop* bus_context_get_loop (BusContext *context);
-DBusUserDatabase* bus_context_get_user_database (BusContext *context);
-dbus_bool_t bus_context_allow_user (BusContext *context,
- unsigned long uid);
-BusClientPolicy* bus_context_create_client_policy (BusContext *context,
- DBusConnection *connection);
-int bus_context_get_activation_timeout (BusContext *context);
-dbus_bool_t bus_context_check_security_policy (BusContext *context,
- DBusConnection *sender,
- DBusConnection *recipient,
- DBusMessage *message,
- DBusError *error);
+BusContext* bus_context_new (const DBusString *config_file,
+ int print_addr_fd,
+ DBusError *error);
+void bus_context_shutdown (BusContext *context);
+void bus_context_ref (BusContext *context);
+void bus_context_unref (BusContext *context);
+const char* bus_context_get_type (BusContext *context);
+const char* bus_context_get_address (BusContext *context);
+BusRegistry* bus_context_get_registry (BusContext *context);
+BusConnections* bus_context_get_connections (BusContext *context);
+BusActivation* bus_context_get_activation (BusContext *context);
+DBusLoop* bus_context_get_loop (BusContext *context);
+DBusUserDatabase* bus_context_get_user_database (BusContext *context);
+dbus_bool_t bus_context_allow_user (BusContext *context,
+ unsigned long uid);
+BusClientPolicy* bus_context_create_client_policy (BusContext *context,
+ DBusConnection *connection);
+int bus_context_get_activation_timeout (BusContext *context);
+int bus_context_get_auth_timeout (BusContext *context);
+int bus_context_get_max_completed_connections (BusContext *context);
+int bus_context_get_max_incomplete_connections (BusContext *context);
+int bus_context_get_max_connections_per_user (BusContext *context);
+dbus_bool_t bus_context_check_security_policy (BusContext *context,
+ DBusConnection *sender,
+ DBusConnection *recipient,
+ DBusMessage *message,
+ DBusError *error);
+
#endif /* BUS_BUS_H */
diff --git a/bus/connection.c b/bus/connection.c
index 71ab0108..14081e2e 100644
--- a/bus/connection.c
+++ b/bus/connection.c
@@ -26,14 +26,19 @@
#include "services.h"
#include "utils.h"
#include <dbus/dbus-list.h>
+#include <dbus/dbus-hash.h>
static void bus_connection_remove_transactions (DBusConnection *connection);
struct BusConnections
{
int refcount;
- DBusList *list; /**< List of all the connections */
+ DBusList *completed; /**< List of all completed connections */
+ int n_completed; /**< Length of completed list */
+ DBusList *incomplete; /**< List of all not-yet-active connections */
+ int n_incomplete; /**< Length of incomplete list */
BusContext *context;
+ DBusHashTable *completed_by_user; /**< Number of completed connections for each UID */
};
static int connection_data_slot = -1;
@@ -42,6 +47,7 @@ static int connection_data_slot_refcount = 0;
typedef struct
{
BusConnections *connections;
+ DBusList *link_in_connection_list;
DBusConnection *connection;
DBusList *services_owned;
char *name;
@@ -96,6 +102,65 @@ connection_get_loop (DBusConnection *connection)
return bus_context_get_loop (d->connections->context);
}
+
+static int
+get_connections_for_uid (BusConnections *connections,
+ dbus_uid_t uid)
+{
+ void *val;
+ int current_count;
+
+ /* val is NULL is 0 when it isn't in the hash yet */
+
+ val = _dbus_hash_table_lookup_ulong (connections->completed_by_user,
+ uid);
+
+ current_count = _DBUS_POINTER_TO_INT (val);
+
+ return current_count;
+}
+
+static dbus_bool_t
+adjust_connections_for_uid (BusConnections *connections,
+ dbus_uid_t uid,
+ int adjustment)
+{
+ int current_count;
+
+ current_count = get_connections_for_uid (connections, uid);
+
+ _dbus_verbose ("Adjusting connection count for UID " DBUS_UID_FORMAT
+ ": was %d adjustment %d making %d\n",
+ uid, current_count, adjustment, current_count + adjustment);
+
+ _dbus_assert (current_count >= 0);
+
+ current_count += adjustment;
+
+ _dbus_assert (current_count >= 0);
+
+ if (current_count == 0)
+ {
+ _dbus_hash_table_remove_ulong (connections->completed_by_user, uid);
+ return TRUE;
+ }
+ else
+ {
+ dbus_bool_t retval;
+
+ retval = _dbus_hash_table_insert_ulong (connections->completed_by_user,
+ uid, _DBUS_INT_TO_POINTER (current_count));
+
+ /* only positive adjustment can fail as otherwise
+ * a hash entry should already exist
+ */
+ _dbus_assert (adjustment > 0 ||
+ (adjustment <= 0 && retval));
+
+ return retval;
+ }
+}
+
void
bus_connection_disconnected (DBusConnection *connection)
{
@@ -180,8 +245,34 @@ bus_connection_disconnected (DBusConnection *connection)
bus_connection_remove_transactions (connection);
- _dbus_list_remove (&d->connections->list, connection);
+ if (d->link_in_connection_list != NULL)
+ {
+ if (d->name != NULL)
+ {
+ unsigned long uid;
+
+ _dbus_list_remove_link (&d->connections->completed, d->link_in_connection_list);
+ d->link_in_connection_list = NULL;
+ d->connections->n_completed -= 1;
+ if (dbus_connection_get_unix_user (connection, &uid))
+ {
+ if (!adjust_connections_for_uid (d->connections,
+ uid, -1))
+ _dbus_assert_not_reached ("adjusting downward should never fail");
+ }
+ }
+ else
+ {
+ _dbus_list_remove_link (&d->connections->incomplete, d->link_in_connection_list);
+ d->link_in_connection_list = NULL;
+ d->connections->n_incomplete -= 1;
+ }
+
+ _dbus_assert (d->connections->n_incomplete >= 0);
+ _dbus_assert (d->connections->n_completed >= 0);
+ }
+
/* frees "d" as side effect */
dbus_connection_set_data (connection,
connection_data_slot,
@@ -323,6 +414,15 @@ bus_connections_new (BusContext *context)
connection_data_slot_unref ();
return NULL;
}
+
+ connections->completed_by_user = _dbus_hash_table_new (DBUS_HASH_ULONG,
+ NULL, NULL);
+ if (connections->completed_by_user == NULL)
+ {
+ dbus_free (connections);
+ connection_data_slot_unref ();
+ return NULL;
+ }
connections->refcount = 1;
connections->context = context;
@@ -344,19 +444,37 @@ bus_connections_unref (BusConnections *connections)
connections->refcount -= 1;
if (connections->refcount == 0)
{
- while (connections->list != NULL)
+ /* drop all incomplete */
+ while (connections->incomplete != NULL)
{
DBusConnection *connection;
- connection = connections->list->data;
+ connection = connections->incomplete->data;
dbus_connection_ref (connection);
dbus_connection_disconnect (connection);
bus_connection_disconnected (connection);
dbus_connection_unref (connection);
}
+
+ _dbus_assert (connections->n_incomplete == 0);
- _dbus_list_clear (&connections->list);
+ /* drop all real connections */
+ while (connections->completed != NULL)
+ {
+ DBusConnection *connection;
+
+ connection = connections->completed->data;
+
+ dbus_connection_ref (connection);
+ dbus_connection_disconnect (connection);
+ bus_connection_disconnected (connection);
+ dbus_connection_unref (connection);
+ }
+
+ _dbus_assert (connections->n_completed == 0);
+
+ _dbus_hash_table_unref (connections->completed_by_user);
dbus_free (connections);
@@ -405,8 +523,7 @@ bus_connections_setup_connection (BusConnections *connections,
NULL,
connection, NULL))
goto out;
-
-
+
dbus_connection_set_unix_user_function (connection,
allow_user_function,
NULL, NULL);
@@ -415,16 +532,14 @@ bus_connections_setup_connection (BusConnections *connections,
dispatch_status_function,
bus_context_get_loop (connections->context),
NULL);
+
+ d->link_in_connection_list = _dbus_list_alloc_link (connection);
+ if (d->link_in_connection_list == NULL)
+ goto out;
/* Setup the connection with the dispatcher */
if (!bus_dispatch_add_connection (connection))
goto out;
-
- if (!_dbus_list_append (&connections->list, connection))
- {
- bus_dispatch_remove_connection (connection);
- goto out;
- }
if (dbus_connection_get_dispatch_status (connection) != DBUS_DISPATCH_COMPLETE)
{
@@ -434,13 +549,36 @@ bus_connections_setup_connection (BusConnections *connections,
goto out;
}
}
+
+ _dbus_list_append_link (&connections->incomplete, d->link_in_connection_list);
+ connections->n_incomplete += 1;
dbus_connection_ref (connection);
+
+ /* Note that we might disconnect ourselves here, but it only takes
+ * effect on return to the main loop.
+ */
+ if (connections->n_incomplete >
+ bus_context_get_max_incomplete_connections (connections->context))
+ {
+ _dbus_verbose ("Number of incomplete connections exceeds max, dropping oldest one\n");
+
+ _dbus_assert (connections->incomplete != NULL);
+ /* Disconnect the oldest unauthenticated connection. FIXME
+ * would it be more secure to drop a *random* connection? This
+ * algorithm seems to mean that if someone can create new
+ * connections quickly enough, they can keep anyone else from
+ * completing authentication. But random may or may not really
+ * help with that, a more elaborate solution might be required.
+ */
+ dbus_connection_disconnect (connections->incomplete->data);
+ }
+
retval = TRUE;
out:
if (!retval)
- {
+ {
if (!dbus_connection_set_watch_functions (connection,
NULL, NULL, NULL,
connection,
@@ -463,6 +601,13 @@ bus_connections_setup_connection (BusConnections *connections,
connection_data_slot,
NULL, NULL))
_dbus_assert_not_reached ("failed to set connection data to null");
+
+ if (d->link_in_connection_list != NULL)
+ {
+ _dbus_assert (d->link_in_connection_list->next == NULL);
+ _dbus_assert (d->link_in_connection_list->prev == NULL);
+ _dbus_list_free_link (d->link_in_connection_list);
+ }
}
return retval;
@@ -567,6 +712,67 @@ bus_connection_get_policy (DBusConnection *connection)
return d->policy;
}
+static dbus_bool_t
+foreach_active (BusConnections *connections,
+ BusConnectionForeachFunction function,
+ void *data)
+{
+ DBusList *link;
+
+ link = _dbus_list_get_first_link (&connections->completed);
+ while (link != NULL)
+ {
+ DBusConnection *connection = link->data;
+ DBusList *next = _dbus_list_get_next_link (&connections->completed, link);
+
+ if (!(* function) (connection, data))
+ return FALSE;
+
+ link = next;
+ }
+
+ return TRUE;
+}
+
+static dbus_bool_t
+foreach_inactive (BusConnections *connections,
+ BusConnectionForeachFunction function,
+ void *data)
+{
+ DBusList *link;
+
+ link = _dbus_list_get_first_link (&connections->incomplete);
+ while (link != NULL)
+ {
+ DBusConnection *connection = link->data;
+ DBusList *next = _dbus_list_get_next_link (&connections->incomplete, link);
+
+ if (!(* function) (connection, data))
+ return FALSE;
+
+ link = next;
+ }
+
+ return TRUE;
+}
+
+/**
+ * Calls function on each active connection; if the function returns
+ * #FALSE, stops iterating. Active connections are authenticated
+ * and have sent a Hello message.
+ *
+ * @param connections the connections object
+ * @param function the function
+ * @param data data to pass to it as a second arg
+ */
+void
+bus_connections_foreach_active (BusConnections *connections,
+ BusConnectionForeachFunction function,
+ void *data)
+{
+ foreach_active (connections, function, data);
+}
+
/**
* Calls function on each connection; if the function returns
* #FALSE, stops iterating.
@@ -578,21 +784,12 @@ bus_connection_get_policy (DBusConnection *connection)
void
bus_connections_foreach (BusConnections *connections,
BusConnectionForeachFunction function,
- void *data)
+ void *data)
{
- DBusList *link;
-
- link = _dbus_list_get_first_link (&connections->list);
- while (link != NULL)
- {
- DBusConnection *connection = link->data;
- DBusList *next = _dbus_list_get_next_link (&connections->list, link);
+ if (!foreach_active (connections, function, data))
+ return;
- if (!(* function) (connection, data))
- break;
-
- link = next;
- }
+ foreach_inactive (connections, function, data);
}
BusContext*
@@ -789,17 +986,40 @@ bus_connection_set_name (DBusConnection *connection,
const DBusString *name)
{
BusConnectionData *d;
+ unsigned long uid;
d = BUS_CONNECTION_DATA (connection);
_dbus_assert (d != NULL);
_dbus_assert (d->name == NULL);
-
+
if (!_dbus_string_copy_data (name, &d->name))
return FALSE;
_dbus_assert (d->name != NULL);
_dbus_verbose ("Name %s assigned to %p\n", d->name, connection);
+
+ if (dbus_connection_get_unix_user (connection, &uid))
+ {
+ if (!adjust_connections_for_uid (d->connections,
+ uid, 1))
+ {
+ dbus_free (d->name);
+ d->name = NULL;
+ return FALSE;
+ }
+ }
+
+ /* Now the connection is active, move it between lists */
+ _dbus_list_unlink (&d->connections->incomplete,
+ d->link_in_connection_list);
+ d->connections->n_incomplete -= 1;
+ _dbus_list_append_link (&d->connections->completed,
+ d->link_in_connection_list);
+ d->connections->n_completed += 1;
+
+ _dbus_assert (d->connections->n_incomplete >= 0);
+ _dbus_assert (d->connections->n_completed > 0);
return TRUE;
}
@@ -815,6 +1035,47 @@ bus_connection_get_name (DBusConnection *connection)
return d->name;
}
+/**
+ * Check whether completing the passed-in connection would
+ * exceed limits, and if so set error and return #FALSE
+ */
+dbus_bool_t
+bus_connections_check_limits (BusConnections *connections,
+ DBusConnection *requesting_completion,
+ DBusError *error)
+{
+ BusConnectionData *d;
+ unsigned long uid;
+
+ d = BUS_CONNECTION_DATA (requesting_completion);
+ _dbus_assert (d != NULL);
+
+ _dbus_assert (d->name == NULL);
+
+ if (connections->n_completed >=
+ bus_context_get_max_completed_connections (connections->context))
+ {
+ dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
+ "The maximum number of active connections has been reached");
+ return FALSE;
+ }
+
+ if (dbus_connection_get_unix_user (requesting_completion, &uid))
+ {
+ if (get_connections_for_uid (connections, uid) >=
+ bus_context_get_max_connections_per_user (connections->context))
+ {
+ dbus_set_error (error, DBUS_ERROR_LIMITS_EXCEEDED,
+ "The maximum number of active connections for UID %lu has been reached",
+ uid);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
/*
* Transactions
*
diff --git a/bus/connection.h b/bus/connection.h
index c429007b..0d4d3a10 100644
--- a/bus/connection.h
+++ b/bus/connection.h
@@ -40,12 +40,18 @@ dbus_bool_t bus_connections_setup_connection (BusConnections *
void bus_connections_foreach (BusConnections *connections,
BusConnectionForeachFunction function,
void *data);
+void bus_connections_foreach_active (BusConnections *connections,
+ BusConnectionForeachFunction function,
+ void *data);
BusContext* bus_connections_get_context (BusConnections *connections);
-
-BusContext* bus_connection_get_context (DBusConnection *connection);
+BusContext* bus_connection_get_context (DBusConnection *connection);
BusConnections* bus_connection_get_connections (DBusConnection *connection);
BusRegistry* bus_connection_get_registry (DBusConnection *connection);
BusActivation* bus_connection_get_activation (DBusConnection *connection);
+dbus_bool_t bus_connections_check_limits (BusConnections *connections,
+ DBusConnection *requesting_completion,
+ DBusError *error);
+
dbus_bool_t bus_connection_is_active (DBusConnection *connection);
diff --git a/bus/dispatch.c b/bus/dispatch.c
index d1c19fd3..c1e67d6a 100644
--- a/bus/dispatch.c
+++ b/bus/dispatch.c
@@ -48,9 +48,6 @@ static dbus_bool_t
send_one_message (DBusConnection *connection, void *data)
{
SendMessageData *d = data;
-
- if (!bus_connection_is_active (connection))
- return TRUE;
if (!bus_context_check_security_policy (d->context,
d->sender,
@@ -93,7 +90,7 @@ bus_dispatch_broadcast_message (BusTransaction *transaction,
d.transaction = transaction;
d.error = &tmp_error;
- bus_connections_foreach (connections, send_one_message, &d);
+ bus_connections_foreach_active (connections, send_one_message, &d);
if (dbus_error_is_set (&tmp_error))
{
diff --git a/bus/driver.c b/bus/driver.c
index c52020b5..bc58f556 100644
--- a/bus/driver.c
+++ b/bus/driver.c
@@ -262,8 +262,23 @@ bus_driver_handle_hello (DBusConnection *connection,
BusService *service;
dbus_bool_t retval;
BusRegistry *registry;
+ BusConnections *connections;
_DBUS_ASSERT_ERROR_IS_CLEAR (error);
+
+ /* Note that when these limits are exceeded we don't disconnect the
+ * connection; we just sort of leave it hanging there until it times
+ * out or disconnects itself or is dropped due to the max number of
+ * incomplete connections. It's even OK if the connection wants to
+ * retry the hello message, we support that.
+ */
+ connections = bus_connection_get_connections (connection);
+ if (!bus_connections_check_limits (connections, connection,
+ error))
+ {
+ _DBUS_ASSERT_ERROR_IS_SET (error);
+ return FALSE;
+ }
if (!_dbus_string_init (&unique_name))
{