From 7b8f32d6e1b7dbe35eccf4dc42cb72bbf3a18f9a Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Fri, 12 Sep 2014 15:51:39 +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. https://bugs.freedesktop.org/show_bug.cgi?id=82820 --- bus/session.conf.in | 3 ++- cmake/CMakeLists.txt | 4 ---- cmake/config.h.cmake | 2 -- configure.ac | 11 ----------- dbus/dbus-message.c | 1 + dbus/dbus-sysdeps.h | 8 ++++++++ 6 files changed, 11 insertions(+), 18 deletions(-) diff --git a/bus/session.conf.in b/bus/session.conf.in index 74d9d1f..d473036 100644 --- a/bus/session.conf.in +++ b/bus/session.conf.in @@ -49,7 +49,8 @@ 1000000000 250000000 1000000000 - @DEFAULT_MESSAGE_UNIX_FDS@ + 120000 240000 100000 diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index b7c2529..c767c17 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -417,10 +417,6 @@ endif (WIN32) set (DBUS_USER ) -# In Autotools this has a different default on QNX, but there seems little -# point in replicating that here; if you're on an unusual Unix, use Autotools. -set (DEFAULT_MESSAGE_UNIX_FDS 1024) - # This won't work on Windows. It's not meant to - the system bus is # meaningless on Windows anyway. # diff --git a/cmake/config.h.cmake b/cmake/config.h.cmake index bd4cd44..eaec1e9 100644 --- a/cmake/config.h.cmake +++ b/cmake/config.h.cmake @@ -82,8 +82,6 @@ # define DBUS_ENABLE_X11_AUTOLAUNCH 1 #endif -#define DBUS_DEFAULT_MESSAGE_UNIX_FDS @DEFAULT_MESSAGE_UNIX_FDS@ - #define _DBUS_VA_COPY_ASSIGN(a1,a2) { a1 = a2; } #cmakedefine DBUS_VA_COPY_FUNC diff --git a/configure.ac b/configure.ac index b7b91be..25b31bb 100644 --- a/configure.ac +++ b/configure.ac @@ -1238,17 +1238,6 @@ if test x$with_valgrind != xno; then AC_DEFINE([WITH_VALGRIND], [1], [Define to add Valgrind instrumentation]) fi -# Determine maximum number of Unix fds which may be passed -AS_CASE([$host_os], - [*qnx*], - [DEFAULT_MESSAGE_UNIX_FDS=256], - [*], - [DEFAULT_MESSAGE_UNIX_FDS=1024]) -AC_DEFINE_UNQUOTED([DBUS_DEFAULT_MESSAGE_UNIX_FDS], - [$DEFAULT_MESSAGE_UNIX_FDS], - [Default for dbus_connection_get_max_message_unix_fds()]) -AC_SUBST([DEFAULT_MESSAGE_UNIX_FDS]) - #### Set up final flags LIBDBUS_LIBS="$THREAD_LIBS $NETWORK_libs" AC_SUBST([LIBDBUS_LIBS]) diff --git a/dbus/dbus-message.c b/dbus/dbus-message.c index 78df755..f4787b0 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 diff --git a/dbus/dbus-sysdeps.h b/dbus/dbus-sysdeps.h index 21033eb..47ba2f4 100644 --- a/dbus/dbus-sysdeps.h +++ b/dbus/dbus-sysdeps.h @@ -558,6 +558,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