From 68c1c8141c9f83af157dcf358e388f424cc4e0a4 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Fri, 12 Sep 2014 16:06:42 +0100 Subject: [PATCH 02/11] config: change DEFAULT_MESSAGE_UNIX_FDS to 16 Based on a patch by Alban Crequy. Now that it's the same on all platforms, there's little point in it being set by configure/cmake. This change fixes two distinct denials of service: fd.o#82820, part A ------------------ Before this patch, the system bus had the following default configuration: - max_connections_per_user: 256 - DBUS_DEFAULT_MESSAGE_UNIX_FDS: usually 1024 (or 256 on QNX, see fd.o#61176) as defined by configure.ac - max_incoming_unix_fds: DBUS_DEFAULT_MESSAGE_UNIX_FDS*4 = usually 4096 - max_outgoing_unix_fds: DBUS_DEFAULT_MESSAGE_UNIX_FDS*4 = usually 4096 - max_message_unix_fds: DBUS_DEFAULT_MESSAGE_UNIX_FDS = usually 1024 This means that a single user could create 256 connections and transmit 256*4096 = 1048576 file descriptors. The file descriptors stay attached to the dbus-daemon process while they are in the message loader, in the outgoing queue or waiting to be dispatched before D-Bus activation. dbus-daemon is usually limited to 65536 file descriptors (ulimit -n). If the limit is reached and dbus-daemon needs to receive a message with a file descriptor attached, this is signalled by recvfrom with the flag MSG_CTRUNC. Dbus-daemon cannot recover from that error because the kernel does not have any API to retrieve a file descriptor which has been discarded with MSG_CTRUNC. Therefore, it closes the connection of the sender. This is not necessarily the connection which generated the most file descriptors so it can lead to denial-of-service attacks. In order to prevent DoS issues, this patch reduces DEFAULT_MESSAGE_UNIX_FDS to 16: max_connections_per_user * max_incoming_unix_fds = 256 * 64 = 16384 This is less than the usual "ulimit -n" (65536) with a good margin to accomodate the other sources of file descriptors (stdin/stdout/stderr, listening sockets, message loader, etc.). Distributors on non-Linux may need to configure a smaller limit in system.conf, if their limit on the number of fds is smaller than Linux's. fd.o#82820, part B ------------------ On Linux, it's not possible to send more than 253 fds in a single sendmsg() call: sendmsg() would return -EINVAL. #define SCM_MAX_FD 253 SCM_MAX_FD changed value during Linux history: - it used to be (OPEN_MAX-1) - commit c09edd6eb (Jul 2007) changed it to 255 - commit bba14de98 (Nov 2010) changed it to 253 Libdbus always sends all of a message's fds, and the beginning of the message itself, in a single sendmsg() call. Combining these two, a malicious sender could split a message across two or more sendmsg() calls to construct a composite message with 254 or more fds. When dbus-daemon attempted to relay that message to its recipient in a single sendmsg() call, it would receive EINVAL, interpret that as a fatal socket error and disconnect the recipient, resulting in denial of service. This is fixed by keeping max_message_unix_fds <= SCM_MAX_FD. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=82820 --- bus/config-parser.c | 6 +++--- bus/session.conf.in | 3 ++- dbus/dbus-message.c | 3 ++- dbus/dbus-sysdeps.h | 8 ++++++++ 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/bus/config-parser.c b/bus/config-parser.c index e4a0262..cc29ef4 100644 --- a/bus/config-parser.c +++ b/bus/config-parser.c @@ -413,9 +413,9 @@ bus_config_parser_new (const DBusString *basedir, maximum number of file descriptors we can receive. Picking a high value here thus translates directly to more memory allocation. */ - parser->limits.max_incoming_unix_fds = 1024*4; - parser->limits.max_outgoing_unix_fds = 1024*4; - parser->limits.max_message_unix_fds = 1024; + parser->limits.max_incoming_unix_fds = DBUS_DEFAULT_MESSAGE_UNIX_FDS*4; + parser->limits.max_outgoing_unix_fds = DBUS_DEFAULT_MESSAGE_UNIX_FDS*4; + parser->limits.max_message_unix_fds = DBUS_DEFAULT_MESSAGE_UNIX_FDS; /* Making this long means the user has to wait longer for an error * message if something screws up, but making it too short means diff --git a/bus/session.conf.in b/bus/session.conf.in index e121ff9..6ce8503 100644 --- a/bus/session.conf.in +++ b/bus/session.conf.in @@ -49,7 +49,8 @@ 1000000000 250000000 1000000000 - 4096 + 120000 240000 100000 diff --git a/dbus/dbus-message.c b/dbus/dbus-message.c index fc61ae7..b0c641e 100644 --- a/dbus/dbus-message.c +++ b/dbus/dbus-message.c @@ -35,6 +35,7 @@ #include "dbus-list.h" #include "dbus-threads-internal.h" #ifdef HAVE_UNIX_FD_PASSING +#include "dbus-sysdeps.h" #include "dbus-sysdeps-unix.h" #endif @@ -3802,7 +3803,7 @@ _dbus_message_loader_new (void) SCM_RIGHTS works we need to preallocate an fd array of the maximum number of unix fds we want to receive in advance. A try-and-reallocate loop is not possible. */ - loader->max_message_unix_fds = 1024; + loader->max_message_unix_fds = DBUS_DEFAULT_MESSAGE_UNIX_FDS; if (!_dbus_string_init (&loader->data)) { diff --git a/dbus/dbus-sysdeps.h b/dbus/dbus-sysdeps.h index df4c5e0..64b6363 100644 --- a/dbus/dbus-sysdeps.h +++ b/dbus/dbus-sysdeps.h @@ -537,6 +537,14 @@ void _dbus_request_file_descriptor_limit (unsigned int limit); const char * _dbus_replace_install_prefix (const char *configure_time_path); +/* Do not set this too high: it is a denial-of-service risk. + * See + * + * (This needs to be in the non-Unix-specific header so that + * the config-parser can use it.) + */ +#define DBUS_DEFAULT_MESSAGE_UNIX_FDS 16 + /** @} */ DBUS_END_DECLS -- 2.1.0