aboutsummaryrefslogblamecommitdiffstats
path: root/community/mumble/CVE-2018-20743.1.patch
blob: c9d381200fdb9498831a419d36ec4c3c1a8efb36 (plain) (tree)




































































































































































































                                                                                                      
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