aboutsummaryrefslogtreecommitdiffstats
path: root/community/mumble/CVE-2018-20743.1.patch
diff options
context:
space:
mode:
Diffstat (limited to 'community/mumble/CVE-2018-20743.1.patch')
-rw-r--r--community/mumble/CVE-2018-20743.1.patch197
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