diff options
author | Simon McVittie <smcv@debian.org> | 2014-09-15 12:55:54 +0100 |
---|---|---|
committer | Simon McVittie <smcv@debian.org> | 2014-09-15 12:55:54 +0100 |
commit | 403920f796c3cbeca1bba58bda497da4770e7d0a (patch) | |
tree | 22c59d2418690f68e169ff7500b22f92d410f55f /bus | |
parent | d35dfa78f7ee90bebc3c8a290a7f5877feb7eb8b (diff) | |
download | dbus-403920f796c3cbeca1bba58bda497da4770e7d0a.tar.gz |
Imported Upstream version 1.8.8upstream/1.8.8
Diffstat (limited to 'bus')
-rw-r--r-- | bus/Makefile.in | 1 | ||||
-rw-r--r-- | bus/bus.c | 43 | ||||
-rw-r--r-- | bus/bus.h | 3 | ||||
-rw-r--r-- | bus/config-parser.c | 16 | ||||
-rw-r--r-- | bus/connection.c | 113 | ||||
-rw-r--r-- | bus/connection.h | 3 | ||||
-rw-r--r-- | bus/session.conf.in | 4 | ||||
-rw-r--r-- | bus/stats.c | 4 |
8 files changed, 157 insertions, 30 deletions
diff --git a/bus/Makefile.in b/bus/Makefile.in index 46f9e9ab..4425b030 100644 --- a/bus/Makefile.in +++ b/bus/Makefile.in @@ -368,7 +368,6 @@ DBUS_USER = @DBUS_USER@ DBUS_VERSION = @DBUS_VERSION@ DBUS_X_CFLAGS = @DBUS_X_CFLAGS@ DBUS_X_LIBS = @DBUS_X_LIBS@ -DEFAULT_MESSAGE_UNIX_FDS = @DEFAULT_MESSAGE_UNIX_FDS@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ @@ -39,6 +39,7 @@ #include <dbus/dbus-hash.h> #include <dbus/dbus-credentials.h> #include <dbus/dbus-internals.h> +#include <dbus/dbus-server-protected.h> #ifdef DBUS_CYGWIN #include <signal.h> @@ -68,6 +69,7 @@ struct BusContext unsigned int keep_umask : 1; unsigned int allow_anonymous : 1; unsigned int systemd_activation : 1; + dbus_bool_t watches_enabled; }; static dbus_int32_t server_data_slot = -1; @@ -758,6 +760,8 @@ bus_context_new (const DBusString *config_file, goto failed; } + context->watches_enabled = TRUE; + context->registry = bus_registry_new (context); if (context->registry == NULL) { @@ -1237,6 +1241,12 @@ bus_context_get_auth_timeout (BusContext *context) } int +bus_context_get_pending_fd_timeout (BusContext *context) +{ + return context->limits.pending_fd_timeout; +} + +int bus_context_get_max_completed_connections (BusContext *context) { return context->limits.max_completed_connections; @@ -1658,3 +1668,36 @@ bus_context_check_security_policy (BusContext *context, _dbus_verbose ("security policy allowing message\n"); return TRUE; } + +void +bus_context_check_all_watches (BusContext *context) +{ + DBusList *link; + dbus_bool_t enabled = TRUE; + + if (bus_connections_get_n_incomplete (context->connections) >= + bus_context_get_max_incomplete_connections (context)) + { + enabled = FALSE; + } + + if (context->watches_enabled == enabled) + return; + + context->watches_enabled = enabled; + + for (link = _dbus_list_get_first_link (&context->servers); + link != NULL; + link = _dbus_list_get_next_link (&context->servers, link)) + { + /* A BusContext might contains several DBusServer (if there are + * several <listen> configuration items) and a DBusServer might + * contain several DBusWatch in its DBusWatchList (if getaddrinfo + * returns several addresses on a dual IPv4-IPv6 stack or if + * systemd passes several fds). + * We want to enable/disable them all. + */ + DBusServer *server = link->data; + _dbus_server_toggle_all_watches (server, enabled); + } +} @@ -54,6 +54,7 @@ typedef struct long max_message_unix_fds; /**< Max number of unix fds of a single message*/ 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 pending_fd_timeout; /**< How long to wait for a D-Bus message with a fd 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 */ @@ -106,6 +107,7 @@ BusClientPolicy* bus_context_create_client_policy (BusContext DBusError *error); int bus_context_get_activation_timeout (BusContext *context); int bus_context_get_auth_timeout (BusContext *context); +int bus_context_get_pending_fd_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); @@ -125,5 +127,6 @@ dbus_bool_t bus_context_check_security_policy (BusContext DBusConnection *proposed_recipient, DBusMessage *message, DBusError *error); +void bus_context_check_all_watches (BusContext *context); #endif /* BUS_BUS_H */ diff --git a/bus/config-parser.c b/bus/config-parser.c index a6a8e1cf..7bc9c019 100644 --- a/bus/config-parser.c +++ b/bus/config-parser.c @@ -438,7 +438,12 @@ bus_config_parser_new (const DBusString *basedir, * and legitimate auth will fail. If interactive auth (ask user for * password) is allowed, then potentially it has to be quite long. */ - parser->limits.auth_timeout = 30000; /* 30 seconds */ + parser->limits.auth_timeout = 5000; /* 5 seconds */ + + /* Do not allow a fd to stay forever in dbus-daemon + * https://bugs.freedesktop.org/show_bug.cgi?id=80559 + */ + parser->limits.pending_fd_timeout = 150000; /* 2.5 minutes */ parser->limits.max_incomplete_connections = 64; parser->limits.max_connections_per_user = 256; @@ -467,7 +472,7 @@ bus_config_parser_new (const DBusString *basedir, /* this is effectively a limit on message queue size for messages * that require a reply */ - parser->limits.max_replies_per_connection = 1024*8; + parser->limits.max_replies_per_connection = 128; } parser->refcount = 1; @@ -1902,6 +1907,12 @@ set_limit (BusConfigParser *parser, must_be_int = TRUE; parser->limits.auth_timeout = value; } + else if (strcmp (name, "pending_fd_timeout") == 0) + { + must_be_positive = TRUE; + must_be_int = TRUE; + parser->limits.pending_fd_timeout = value; + } else if (strcmp (name, "reply_timeout") == 0) { must_be_positive = TRUE; @@ -3108,6 +3119,7 @@ limits_equal (const BusLimits *a, || a->max_message_unix_fds == b->max_message_unix_fds || a->activation_timeout == b->activation_timeout || a->auth_timeout == b->auth_timeout + || a->pending_fd_timeout == b->pending_fd_timeout || a->max_completed_connections == b->max_completed_connections || a->max_incomplete_connections == b->max_incomplete_connections || a->max_connections_per_user == b->max_connections_per_user diff --git a/bus/connection.c b/bus/connection.c index ea2d155a..519122c5 100644 --- a/bus/connection.c +++ b/bus/connection.c @@ -33,6 +33,7 @@ #include <dbus/dbus-list.h> #include <dbus/dbus-hash.h> #include <dbus/dbus-timeout.h> +#include <dbus/dbus-connection-internal.h> /* Trim executed commands to this length; we want to keep logs readable */ #define MAX_LOG_COMMAND_LEN 50 @@ -102,6 +103,8 @@ typedef struct int peak_match_rules; int peak_bus_names; #endif + int n_pending_unix_fds; + DBusTimeout *pending_unix_fds_timeout; } BusConnectionData; static dbus_bool_t bus_pending_reply_expired (BusExpireList *list, @@ -268,6 +271,15 @@ bus_connection_disconnected (DBusConnection *connection) dbus_connection_set_dispatch_status_function (connection, NULL, NULL, NULL); + + if (d->pending_unix_fds_timeout) + { + _dbus_loop_remove_timeout (bus_context_get_loop (d->connections->context), + d->pending_unix_fds_timeout); + _dbus_timeout_unref (d->pending_unix_fds_timeout); + } + d->pending_unix_fds_timeout = NULL; + _dbus_connection_set_pending_fds_function (connection, NULL, NULL); bus_connection_remove_transactions (connection); @@ -293,6 +305,10 @@ bus_connection_disconnected (DBusConnection *connection) _dbus_list_remove_link (&d->connections->incomplete, d->link_in_connection_list); d->link_in_connection_list = NULL; d->connections->n_incomplete -= 1; + + /* If we have dropped below the max. number of incomplete + * connections, start accept()ing again */ + bus_context_check_all_watches (d->connections->context); } _dbus_assert (d->connections->n_incomplete >= 0); @@ -588,6 +604,42 @@ oom: return FALSE; } +static void +check_pending_fds_cb (DBusConnection *connection) +{ + BusConnectionData *d = BUS_CONNECTION_DATA (connection); + int n_pending_unix_fds_old = d->n_pending_unix_fds; + int n_pending_unix_fds_new; + + n_pending_unix_fds_new = _dbus_connection_get_pending_fds_count (connection); + + _dbus_verbose ("Pending fds count changed on connection %p: %d -> %d\n", + connection, n_pending_unix_fds_old, n_pending_unix_fds_new); + + if (n_pending_unix_fds_old == 0 && n_pending_unix_fds_new > 0) + { + _dbus_timeout_set_interval (d->pending_unix_fds_timeout, + bus_context_get_pending_fd_timeout (d->connections->context)); + _dbus_timeout_set_enabled (d->pending_unix_fds_timeout, TRUE); + } + + if (n_pending_unix_fds_old > 0 && n_pending_unix_fds_new == 0) + { + _dbus_timeout_set_enabled (d->pending_unix_fds_timeout, FALSE); + } + + + d->n_pending_unix_fds = n_pending_unix_fds_new; +} + +static dbus_bool_t +pending_unix_fds_timeout_cb (void *data) +{ + DBusConnection *connection = data; + dbus_connection_close (connection); + return TRUE; +} + dbus_bool_t bus_connections_setup_connection (BusConnections *connections, DBusConnection *connection) @@ -683,36 +735,38 @@ bus_connections_setup_connection (BusConnections *connections, } } + /* Setup pending fds timeout (see #80559) */ + d->pending_unix_fds_timeout = _dbus_timeout_new (100, /* irrelevant */ + pending_unix_fds_timeout_cb, + connection, NULL); + if (d->pending_unix_fds_timeout == NULL) + goto out; + + _dbus_timeout_set_enabled (d->pending_unix_fds_timeout, FALSE); + if (!_dbus_loop_add_timeout (bus_context_get_loop (connections->context), + d->pending_unix_fds_timeout)) + goto out; + + _dbus_connection_set_pending_fds_function (connection, + (DBusPendingFdsChangeFunction) check_pending_fds_cb, + connection); + _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. We call this to free up - * expired connections if possible, and to queue the timeout for our - * own expiration. - */ bus_connections_expire_incomplete (connections); - /* And we might also disconnect ourselves here, but again it - * only takes effect on return to 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_close (connections->incomplete->data); - } + /* The listening socket is removed from the main loop, + * i.e. does not accept(), while n_incomplete is at its + * maximum value; so we shouldn't get here in that case */ + _dbus_assert (connections->n_incomplete <= + bus_context_get_max_incomplete_connections (connections->context)); + + /* If we have the maximum number of incomplete connections, + * stop accept()ing any more, to avert a DoS. See fd.o #80919 */ + bus_context_check_all_watches (d->connections->context); retval = TRUE; @@ -744,6 +798,13 @@ bus_connections_setup_connection (BusConnections *connections, dbus_connection_set_dispatch_status_function (connection, NULL, NULL, NULL); + if (d->pending_unix_fds_timeout) + _dbus_timeout_unref (d->pending_unix_fds_timeout); + + d->pending_unix_fds_timeout = NULL; + + _dbus_connection_set_pending_fds_function (connection, NULL, NULL); + if (d->link_in_connection_list != NULL) { _dbus_assert (d->link_in_connection_list->next == NULL); @@ -1419,6 +1480,10 @@ bus_connection_complete (DBusConnection *connection, _dbus_assert (d->connections->n_incomplete >= 0); _dbus_assert (d->connections->n_completed > 0); + /* If we have dropped below the max. number of incomplete + * connections, start accept()ing again */ + bus_context_check_all_watches (d->connections->context); + /* See if we can remove the timeout */ bus_connections_expire_incomplete (d->connections); @@ -2348,7 +2413,6 @@ bus_transaction_add_cancel_hook (BusTransaction *transaction, return TRUE; } -#ifdef DBUS_ENABLE_STATS int bus_connections_get_n_active (BusConnections *connections) { @@ -2361,6 +2425,7 @@ bus_connections_get_n_incomplete (BusConnections *connections) return connections->n_incomplete; } +#ifdef DBUS_ENABLE_STATS int bus_connections_get_total_match_rules (BusConnections *connections) { diff --git a/bus/connection.h b/bus/connection.h index 9f4f9aea..6fbcd38d 100644 --- a/bus/connection.h +++ b/bus/connection.h @@ -139,9 +139,10 @@ dbus_bool_t bus_transaction_add_cancel_hook (BusTransaction * void *data, DBusFreeFunction free_data_function); -/* called by stats.c, only present if DBUS_ENABLE_STATS */ int bus_connections_get_n_active (BusConnections *connections); int bus_connections_get_n_incomplete (BusConnections *connections); + +/* called by stats.c, only present if DBUS_ENABLE_STATS */ int bus_connections_get_total_match_rules (BusConnections *connections); int bus_connections_get_peak_match_rules (BusConnections *connections); int bus_connections_get_peak_match_rules_per_conn (BusConnections *connections); diff --git a/bus/session.conf.in b/bus/session.conf.in index 74d9d1fd..cfe9544f 100644 --- a/bus/session.conf.in +++ b/bus/session.conf.in @@ -49,9 +49,11 @@ <limit name="max_outgoing_bytes">1000000000</limit> <limit name="max_outgoing_unix_fds">250000000</limit> <limit name="max_message_size">1000000000</limit> - <limit name="max_message_unix_fds">@DEFAULT_MESSAGE_UNIX_FDS@</limit> + <!-- We do not override max_message_unix_fds here since the in-kernel + limit is also relatively low --> <limit name="service_start_timeout">120000</limit> <limit name="auth_timeout">240000</limit> + <limit name="pending_fd_timeout">150000</limit> <limit name="max_completed_connections">100000</limit> <limit name="max_incomplete_connections">10000</limit> <limit name="max_connections_per_user">100000</limit> diff --git a/bus/stats.c b/bus/stats.c index 2bf86d66..24308eb5 100644 --- a/bus/stats.c +++ b/bus/stats.c @@ -40,6 +40,7 @@ bus_stats_handle_get_stats (DBusConnection *connection, DBusMessage *message, DBusError *error) { + BusContext *context; BusConnections *connections; DBusMessage *reply = NULL; DBusMessageIter iter, arr_iter; @@ -48,7 +49,8 @@ bus_stats_handle_get_stats (DBusConnection *connection, _DBUS_ASSERT_ERROR_IS_CLEAR (error); - connections = bus_context_get_connections (transaction->context); + context = bus_transaction_get_context (transaction); + connections = bus_context_get_connections (context); reply = _dbus_asv_new_method_return (message, &iter, &arr_iter); |