diff options
Diffstat (limited to 'community/mumble/CVE-2018-20743.1.patch')
-rw-r--r-- | community/mumble/CVE-2018-20743.1.patch | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/community/mumble/CVE-2018-20743.1.patch b/community/mumble/CVE-2018-20743.1.patch new file mode 100644 index 0000000000..c9d381200f --- /dev/null +++ b/community/mumble/CVE-2018-20743.1.patch @@ -0,0 +1,197 @@ +From 0daec57f5cfc4225aa4527b537b4ec4fbbc35635 Mon Sep 17 00:00:00 2001 +From: MadMaurice <madmaurice@zom.bi> +Date: Thu, 30 Aug 2018 15:08:01 +0200 +Subject: [PATCH] Prevent instability and crash due to message flood + +This patch adds a rate limiting to selected patches. The underlying rate limiter +used is the Leaky-Bucket algorithm. It allows for a burst of messages, but +limits them after a specified amount of messages within a time frame. +--- + src/murmur/Messages.cpp | 17 ++++++++++++ + src/murmur/ServerUser.cpp | 58 +++++++++++++++++++++++++++++++++++++++ + src/murmur/ServerUser.h | 29 ++++++++++++++++++++ + 3 files changed, 104 insertions(+) + +diff --git a/src/murmur/Messages.cpp b/src/murmur/Messages.cpp +index 967cff794..1739378e1 100644 +--- a/src/murmur/Messages.cpp ++++ b/src/murmur/Messages.cpp +@@ -42,6 +42,11 @@ + #include "ServerUser.h" + #include "Version.h" + ++#define RATELIMIT(user) \ ++ if (user->leakyBucket.ratelimit(1)) { \ ++ return; \ ++ } ++ + #define MSG_SETUP(st) \ + if (uSource->sState != st) { \ + return; \ +@@ -679,6 +684,10 @@ + bBroadcast = true; + } + ++ if (uSource == pDstServerUser) { ++ RATELIMIT(uSource); ++ } ++ + if (msg.has_channel_id()) { + Channel *c = qhChannels.value(msg.channel_id()); + +@@ -791,6 +800,8 @@ + c = qhChannels.value(msg.channel_id()); + if (! c) + return; ++ } else { ++ RATELIMIT(uSource); + } + + // Check if the parent exists +@@ -1074,6 +1076,8 @@ + QSet<ServerUser *> users; + QQueue<Channel *> q; + ++ RATELIMIT(uSource); ++ + QString text = u8(msg.message()); + bool changed = false; + +@@ -1241,6 +1254,8 @@ void Server::msgACL(ServerUser *uSource, MumbleProto::ACL &msg) { + return; + } + ++ RATELIMIT(uSource); ++ + if (msg.has_query() && msg.query()) { + QStack<Channel *> chans; + Channel *p; +@@ -1497,6 +1512,8 @@ void Server::msgContextAction(ServerUser *uSource, MumbleProto::ContextAction &m + } + + void Server::msgVersion(ServerUser *uSource, MumbleProto::Version &msg) { ++ RATELIMIT(uSource); ++ + if (msg.has_version()) + uSource->uiVersion=msg.version(); + if (msg.has_release()) +diff --git a/src/murmur/ServerUser.cpp b/src/murmur/ServerUser.cpp +index c851d86d8..e5c570d47 100644 +--- a/src/murmur/ServerUser.cpp ++++ b/src/murmur/ServerUser.cpp +@@ -112,3 +112,61 @@ int BandwidthRecord::bandwidth() const { + return static_cast<int>((sum * 1000000ULL) / elapsed); + } + ++#if __cplusplus > 199711LL ++ ++inline static ++time_point now() { ++ return std::chrono::steady_clock::now(); ++} ++ ++inline static ++unsigned long millisecondsBetween(time_point start, time_point end) { ++ return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count(); ++} ++ ++#else ++ ++inline static ++time_point now() { ++ return clock(); ++} ++ ++inline static ++unsigned long millisecondsBetween(time_point start, time_point end) { ++ return 1000 * (end - start) / CLOCKS_PER_SEC; ++} ++ ++#endif ++ ++// Rate limiting: burst up to 30, 4 message per sec limit over longer time ++LeakyBucket::LeakyBucket() : tokensPerSec(4), maxTokens(30), currentTokens(0) { ++ lastUpdate = now(); ++} ++ ++bool LeakyBucket::ratelimit(int tokens) { ++ // First remove tokens we leaked over time ++ time_point tnow = now(); ++ long ms = millisecondsBetween(lastUpdate, tnow); ++ ++ long drainTokens = (ms * tokensPerSec) / 1000; ++ ++ // Prevent constant starvation due to too many updates ++ if (drainTokens > 0) { ++ this->lastUpdate = tnow; ++ ++ this->currentTokens -= drainTokens; ++ if (this->currentTokens < 0) { ++ this->currentTokens = 0; ++ } ++ } ++ ++ // Then try to add tokens ++ bool limit = this->currentTokens > ((static_cast<long>(maxTokens)) - tokens); ++ ++ // If the bucket is not overflowed, allow message and add tokens ++ if (!limit) { ++ this->currentTokens += tokens; ++ } ++ ++ return limit; ++} +diff --git a/src/murmur/ServerUser.h b/src/murmur/ServerUser.h +index 28e582739..0a3828205 100644 +--- a/src/murmur/ServerUser.h ++++ b/src/murmur/ServerUser.h +@@ -14,6 +14,13 @@ + #include <winsock2.h> + #endif + ++// <chrono> was introduced in C++11 ++#if __cplusplus > 199711LL ++#include <chrono> ++#else ++#include <ctime> ++#endif ++ + #include "Connection.h" + #include "Timer.h" + #include "User.h" +@@ -55,6 +62,26 @@ struct WhisperTarget { + + class Server; + ++#if __cplusplus > 199711L ++ typedef std::chrono::time_point<std::chrono::steady_clock> time_point; ++#else ++ typedef clock_t time_point; ++#endif ++ ++// Simple algorithm for rate limiting ++class LeakyBucket { ++ private: ++ unsigned int tokensPerSec, maxTokens; ++ long currentTokens; ++ time_point lastUpdate; ++ ++ public: ++ // Returns true if packets should be dropped ++ bool ratelimit(int tokens); ++ ++ LeakyBucket(); ++}; ++ + class ServerUser : public Connection, public User { + private: + Q_OBJECT +@@ -103,6 +130,8 @@ class ServerUser : public Connection, public User { + QMap<int, TargetCache> qmTargetCache; + QMap<QString, QString> qmWhisperRedirect; + ++ LeakyBucket leakyBucket; ++ + int iLastPermissionCheck; + QMap<int, unsigned int> qmPermissionSent; + #ifdef Q_OS_UNIX |