[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#666222: marked as done (pu: package tremulous/1.1.0-8~squeeze1 (contrib))



Your message dated Sat, 12 May 2012 13:32:55 +0100
with message-id <dda96cc3369bdcdc1a3cdf68c2fc2f56@mail.adsl.funky-badger.org>
and subject line Closing requests for packages included in 6.0.5
has caused the Debian Bug report #666222,
regarding pu: package tremulous/1.1.0-8~squeeze1 (contrib)
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
666222: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=666222
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: pu

This update reduces attackers' ability to perform a reflected DoS attack by
sending spoofed UDP packets to multiple Tremulous servers, by rate-limiting
large responses to those packets. It's the same thing as DSA-2442-1 in
OpenArena, but also incorporates a fix for a regression in that update
(I've just uploaded the corresponding fix for OpenArena to security-master).

As with -7~squeeze1, the security team have instructed me to go directly
to stable since Tremulous is contrib, and the source I propose to upload is
identical to what's in unstable (apart from the changelog).

The reporter of the corresponding OpenArena bug (#665665) is testing
pre-release versions of the Tremulous and OA updates on a public server
running squeeze, and hasn't reported any problems so far.

Changelog below; debdiff attached; separated patch also attached, for better
legibility.

Regards,
    S

tremulous (1.1.0-8~squeeze1) stable; urgency=low

  * Stable update, incorporating a security fix from unstable

 -- Simon McVittie <smcv@debian.org>  Thu, 29 Mar 2012 20:40:49 +0100

tremulous (1.1.0-8) unstable; urgency=medium

  * Backport ioquake3 r1762, r1763, r1898 to rate-limit getstatus and
    rcon connectionless packets, to avoid their use for traffic amplification.
    CVE-2010-5077 (Closes: #665842)
  * Fix an incorrect bug number in revision -6

 -- Simon McVittie <smcv@debian.org>  Tue, 27 Mar 2012 20:33:10 +0100
diffstat for tremulous-1.1.0 tremulous-1.1.0

 changelog                                                               |   15 
 patches/0020-Rate-limit-getstatus-and-rcon-connectionless-request.patch |  263 ++++++++++
 patches/series                                                          |    1 
 3 files changed, 279 insertions(+)

diff -Nru tremulous-1.1.0/debian/changelog tremulous-1.1.0/debian/changelog
--- tremulous-1.1.0/debian/changelog	2012-03-25 13:57:34.000000000 +0100
+++ tremulous-1.1.0/debian/changelog	2012-03-29 20:40:50.000000000 +0100
@@ -1,3 +1,18 @@
+tremulous (1.1.0-8~squeeze1) stable; urgency=low
+
+  * Stable update, incorporating a security fix from unstable
+
+ -- Simon McVittie <smcv@debian.org>  Thu, 29 Mar 2012 20:40:49 +0100
+
+tremulous (1.1.0-8) unstable; urgency=medium
+
+  * Backport ioquake3 r1762, r1763, r1898 to rate-limit getstatus and
+    rcon connectionless packets, to avoid their use for traffic amplification.
+    CVE-2010-5077 (Closes: #665842)
+  * Fix an incorrect bug number in revision -6
+
+ -- Simon McVittie <smcv@debian.org>  Tue, 27 Mar 2012 20:33:10 +0100
+
 tremulous (1.1.0-7~squeeze1) stable; urgency=low
 
   * Stable update (#663104), incorporating security fixes from unstable
diff -Nru tremulous-1.1.0/debian/patches/0020-Rate-limit-getstatus-and-rcon-connectionless-request.patch tremulous-1.1.0/debian/patches/0020-Rate-limit-getstatus-and-rcon-connectionless-request.patch
--- tremulous-1.1.0/debian/patches/0020-Rate-limit-getstatus-and-rcon-connectionless-request.patch	1970-01-01 01:00:00.000000000 +0100
+++ tremulous-1.1.0/debian/patches/0020-Rate-limit-getstatus-and-rcon-connectionless-request.patch	2012-03-27 22:05:46.000000000 +0100
@@ -0,0 +1,263 @@
+From: Simon McVittie <smcv@debian.org>
+Date: Sun, 3 Jan 2010 22:12:20 +0000
+Subject: Rate limit getstatus and rcon connectionless requests
+
+Backport of ioquake3 r1762, r1763, r1898, all by Tim Angus <tma>. This
+also incorporates a fix for a regression in r1762 in which the server would
+stop responding to getstatus after 2**32 ms (about 50 days).
+
+Changes to adapt to Tremulous:
+
+* Remove IPv6 support, Tremulous 1.1.0 does not do IPv6
+* Do not assume that NA_BAD == 0 (in this older version it's 1),
+  look for literal 0 as the indication that a hash bucket has only been
+  zero-filled and not properly initialized
+* Remove cosmetic (whitespace/comment) changes
+
+Origin: backport
+Bug-Debian: http://bugs.debian.org/665842
+CVE: CVE-2010-5077
+---
+ src/server/sv_main.c |  210 +++++++++++++++++++++++++++++++++++++++++++++++---
+ 1 files changed, 200 insertions(+), 10 deletions(-)
+
+diff --git a/src/server/sv_main.c b/src/server/sv_main.c
+index f6d5a7c..9dfa6dc 100644
+--- a/src/server/sv_main.c
++++ b/src/server/sv_main.c
+@@ -332,6 +332,175 @@ CONNECTIONLESS COMMANDS
+ ==============================================================================
+ */
+ 
++typedef struct leakyBucket_s leakyBucket_t;
++struct leakyBucket_s {
++	netadrtype_t	type;
++
++	union {
++		byte	_4[4];
++		byte	_6[16];
++	} ipv;
++
++	int						lastTime;
++	signed char		burst;
++
++	long					hash;
++
++	leakyBucket_t *prev, *next;
++};
++
++// This is deliberately quite large to make it more of an effort to DoS
++#define MAX_BUCKETS			16384
++#define MAX_HASHES			1024
++
++static leakyBucket_t buckets[ MAX_BUCKETS ];
++static leakyBucket_t *bucketHashes[ MAX_HASHES ];
++
++/*
++================
++SVC_HashForAddress
++================
++*/
++static long SVC_HashForAddress( netadr_t address ) {
++	byte 		*ip = NULL;
++	size_t	size = 0;
++	int			i;
++	long		hash = 0;
++
++	switch ( address.type ) {
++		case NA_IP:  ip = address.ip;  size = 4; break;
++		default: return 0;
++	}
++
++	for ( i = 0; i < size; i++ ) {
++		hash += (long)( ip[ i ] ) * ( i + 119 );
++	}
++
++	hash = ( hash ^ ( hash >> 10 ) ^ ( hash >> 20 ) );
++	hash &= ( MAX_HASHES - 1 );
++
++	return hash;
++}
++
++/*
++================
++SVC_BucketForAddress
++
++Find or allocate a bucket for an address
++================
++*/
++static leakyBucket_t *SVC_BucketForAddress( netadr_t address, int burst, int period ) {
++	leakyBucket_t	*bucket = NULL;
++	int						i;
++	long					hash = SVC_HashForAddress( address );
++	int						now = Sys_Milliseconds();
++
++	for ( bucket = bucketHashes[ hash ]; bucket; bucket = bucket->next ) {
++		switch ( bucket->type ) {
++			case NA_IP:
++				if ( memcmp( bucket->ipv._4, address.ip, 4 ) == 0 ) {
++					return bucket;
++				}
++				break;
++
++			default:
++				break;
++		}
++	}
++
++	for ( i = 0; i < MAX_BUCKETS; i++ ) {
++		int interval;
++
++		bucket = &buckets[ i ];
++		interval = now - bucket->lastTime;
++
++		// Reclaim expired buckets
++		if ( bucket->lastTime > 0 && ( interval > ( burst * period ) ||
++					interval < 0 ) ) {
++			if ( bucket->prev != NULL ) {
++				bucket->prev->next = bucket->next;
++			} else {
++				bucketHashes[ bucket->hash ] = bucket->next;
++			}
++			
++			if ( bucket->next != NULL ) {
++				bucket->next->prev = bucket->prev;
++			}
++
++			Com_Memset( bucket, 0, sizeof( leakyBucket_t ) );
++		}
++
++		if ( bucket->type == 0 ) {
++			bucket->type = address.type;
++			switch ( address.type ) {
++				case NA_IP:  Com_Memcpy( bucket->ipv._4, address.ip, 4 );   break;
++				default: break;
++			}
++
++			bucket->lastTime = now;
++			bucket->burst = 0;
++			bucket->hash = hash;
++
++			// Add to the head of the relevant hash chain
++			bucket->next = bucketHashes[ hash ];
++			if ( bucketHashes[ hash ] != NULL ) {
++				bucketHashes[ hash ]->prev = bucket;
++			}
++
++			bucket->prev = NULL;
++			bucketHashes[ hash ] = bucket;
++
++			return bucket;
++		}
++	}
++
++	// Couldn't allocate a bucket for this address
++	return NULL;
++}
++
++/*
++================
++SVC_RateLimit
++================
++*/
++static qboolean SVC_RateLimit( leakyBucket_t *bucket, int burst, int period ) {
++	if ( bucket != NULL ) {
++		int now = Sys_Milliseconds();
++		int interval = now - bucket->lastTime;
++		int expired = interval / period;
++		int expiredRemainder = interval % period;
++
++		if ( expired > bucket->burst ) {
++			bucket->burst = 0;
++			bucket->lastTime = now;
++		} else {
++			bucket->burst -= expired;
++			bucket->lastTime = now - expiredRemainder;
++		}
++
++		if ( bucket->burst < burst ) {
++			bucket->burst++;
++
++			return qfalse;
++		}
++	}
++
++	return qtrue;
++}
++
++/*
++================
++SVC_RateLimitAddress
++
++Rate limit for a particular address
++================
++*/
++static qboolean SVC_RateLimitAddress( netadr_t from, int burst, int period ) {
++	leakyBucket_t *bucket = SVC_BucketForAddress( from, burst, period );
++
++	return SVC_RateLimit( bucket, burst, period );
++}
++
+ /*
+ ================
+ SVC_Status
+@@ -350,6 +519,21 @@ void SVC_Status( netadr_t from ) {
+ 	int		statusLength;
+ 	int		playerLength;
+ 	char	infostring[MAX_INFO_STRING];
++	static leakyBucket_t bucket;
++
++	// Prevent using getstatus as an amplifier
++	if ( SVC_RateLimitAddress( from, 10, 1000 ) ) {
++		Com_DPrintf( "SVC_Status: rate limit from %s exceeded, dropping request\n",
++			NET_AdrToString( from ) );
++		return;
++	}
++
++	// Allow getstatus to be DoSed relatively easily, but prevent
++	// excess outbound bandwidth usage when being flooded inbound
++	if ( SVC_RateLimit( &bucket, 10, 100 ) ) {
++		Com_DPrintf( "SVC_Status: rate limit exceeded, dropping request\n" );
++		return;
++	}
+ 
+ 	strcpy( infostring, Cvar_InfoString( CVAR_SERVERINFO ) );
+ 
+@@ -466,24 +650,30 @@ Redirect all printfs
+ */
+ void SVC_RemoteCommand( netadr_t from, msg_t *msg ) {
+ 	qboolean	valid;
+-	unsigned int time;
+ 	char		remaining[1024];
+ 	// TTimo - scaled down to accumulate, but not overflow anything network wise, print wise etc.
+ 	// (OOB messages are the bottleneck here)
+ #define SV_OUTPUTBUF_LENGTH (1024 - 16)
+ 	char		sv_outputbuf[SV_OUTPUTBUF_LENGTH];
+-	static unsigned int lasttime = 0;
+ 	char *cmd_aux;
+ 
+-	// TTimo - https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=534
+-	time = Com_Milliseconds();
+-	if (time<(lasttime+500)) {
++	// Prevent using rcon as an amplifier and make dictionary attacks impractical
++	if ( SVC_RateLimitAddress( from, 10, 1000 ) ) {
++		Com_DPrintf( "SVC_Status: rate limit from %s exceeded, dropping request\n",
++			NET_AdrToString( from ) );
+ 		return;
+ 	}
+-	lasttime = time;
+ 
+ 	if ( !strlen( sv_rconPassword->string ) ||
+ 		strcmp (Cmd_Argv(1), sv_rconPassword->string) ) {
++		static leakyBucket_t bucket;
++
++		// Make DoS via rcon impractical
++		if ( SVC_RateLimit( &bucket, 10, 1000 ) ) {
++			Com_DPrintf( "SVC_Status: rate limit exceeded, dropping request\n" );
++			return;
++		}
++
+ 		valid = qfalse;
+ 		Com_Printf ("Bad rcon from %s:\n%s\n", NET_AdrToString (from), Cmd_Argv(2) );
+ 	} else {
+
diff -Nru tremulous-1.1.0/debian/patches/series tremulous-1.1.0/debian/patches/series
--- tremulous-1.1.0/debian/patches/series	2012-02-22 09:09:26.000000000 +0000
+++ tremulous-1.1.0/debian/patches/series	2012-03-27 22:05:46.000000000 +0100
@@ -17,3 +17,4 @@
 0017-Sys_Error-do-not-overflow-if-an-error-message-exceed.patch
 0018-Avoid-non-literal-format-strings.patch
 0019-Annotate-printf-and-scanf-like-functions-with-gcc-at.patch
+0020-Rate-limit-getstatus-and-rcon-connectionless-request.patch
From: Simon McVittie <smcv@debian.org>
Date: Sun, 3 Jan 2010 22:12:20 +0000
Subject: Rate limit getstatus and rcon connectionless requests

Backport of ioquake3 r1762, r1763, r1898, all by Tim Angus <tma>. This
also incorporates a fix for a regression in r1762 in which the server would
stop responding to getstatus after 2**32 ms (about 50 days).

Changes to adapt to Tremulous:

* Remove IPv6 support, Tremulous 1.1.0 does not do IPv6
* Do not assume that NA_BAD == 0 (in this older version it's 1),
  look for literal 0 as the indication that a hash bucket has only been
  zero-filled and not properly initialized
* Remove cosmetic (whitespace/comment) changes

Origin: backport
Bug-Debian: http://bugs.debian.org/665842
CVE: CVE-2010-5077
---
 src/server/sv_main.c |  210 +++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 200 insertions(+), 10 deletions(-)

diff --git a/src/server/sv_main.c b/src/server/sv_main.c
index f6d5a7c..9dfa6dc 100644
--- a/src/server/sv_main.c
+++ b/src/server/sv_main.c
@@ -332,6 +332,175 @@ CONNECTIONLESS COMMANDS
 ==============================================================================
 */
 
+typedef struct leakyBucket_s leakyBucket_t;
+struct leakyBucket_s {
+	netadrtype_t	type;
+
+	union {
+		byte	_4[4];
+		byte	_6[16];
+	} ipv;
+
+	int						lastTime;
+	signed char		burst;
+
+	long					hash;
+
+	leakyBucket_t *prev, *next;
+};
+
+// This is deliberately quite large to make it more of an effort to DoS
+#define MAX_BUCKETS			16384
+#define MAX_HASHES			1024
+
+static leakyBucket_t buckets[ MAX_BUCKETS ];
+static leakyBucket_t *bucketHashes[ MAX_HASHES ];
+
+/*
+================
+SVC_HashForAddress
+================
+*/
+static long SVC_HashForAddress( netadr_t address ) {
+	byte 		*ip = NULL;
+	size_t	size = 0;
+	int			i;
+	long		hash = 0;
+
+	switch ( address.type ) {
+		case NA_IP:  ip = address.ip;  size = 4; break;
+		default: return 0;
+	}
+
+	for ( i = 0; i < size; i++ ) {
+		hash += (long)( ip[ i ] ) * ( i + 119 );
+	}
+
+	hash = ( hash ^ ( hash >> 10 ) ^ ( hash >> 20 ) );
+	hash &= ( MAX_HASHES - 1 );
+
+	return hash;
+}
+
+/*
+================
+SVC_BucketForAddress
+
+Find or allocate a bucket for an address
+================
+*/
+static leakyBucket_t *SVC_BucketForAddress( netadr_t address, int burst, int period ) {
+	leakyBucket_t	*bucket = NULL;
+	int						i;
+	long					hash = SVC_HashForAddress( address );
+	int						now = Sys_Milliseconds();
+
+	for ( bucket = bucketHashes[ hash ]; bucket; bucket = bucket->next ) {
+		switch ( bucket->type ) {
+			case NA_IP:
+				if ( memcmp( bucket->ipv._4, address.ip, 4 ) == 0 ) {
+					return bucket;
+				}
+				break;
+
+			default:
+				break;
+		}
+	}
+
+	for ( i = 0; i < MAX_BUCKETS; i++ ) {
+		int interval;
+
+		bucket = &buckets[ i ];
+		interval = now - bucket->lastTime;
+
+		// Reclaim expired buckets
+		if ( bucket->lastTime > 0 && ( interval > ( burst * period ) ||
+					interval < 0 ) ) {
+			if ( bucket->prev != NULL ) {
+				bucket->prev->next = bucket->next;
+			} else {
+				bucketHashes[ bucket->hash ] = bucket->next;
+			}
+			
+			if ( bucket->next != NULL ) {
+				bucket->next->prev = bucket->prev;
+			}
+
+			Com_Memset( bucket, 0, sizeof( leakyBucket_t ) );
+		}
+
+		if ( bucket->type == 0 ) {
+			bucket->type = address.type;
+			switch ( address.type ) {
+				case NA_IP:  Com_Memcpy( bucket->ipv._4, address.ip, 4 );   break;
+				default: break;
+			}
+
+			bucket->lastTime = now;
+			bucket->burst = 0;
+			bucket->hash = hash;
+
+			// Add to the head of the relevant hash chain
+			bucket->next = bucketHashes[ hash ];
+			if ( bucketHashes[ hash ] != NULL ) {
+				bucketHashes[ hash ]->prev = bucket;
+			}
+
+			bucket->prev = NULL;
+			bucketHashes[ hash ] = bucket;
+
+			return bucket;
+		}
+	}
+
+	// Couldn't allocate a bucket for this address
+	return NULL;
+}
+
+/*
+================
+SVC_RateLimit
+================
+*/
+static qboolean SVC_RateLimit( leakyBucket_t *bucket, int burst, int period ) {
+	if ( bucket != NULL ) {
+		int now = Sys_Milliseconds();
+		int interval = now - bucket->lastTime;
+		int expired = interval / period;
+		int expiredRemainder = interval % period;
+
+		if ( expired > bucket->burst ) {
+			bucket->burst = 0;
+			bucket->lastTime = now;
+		} else {
+			bucket->burst -= expired;
+			bucket->lastTime = now - expiredRemainder;
+		}
+
+		if ( bucket->burst < burst ) {
+			bucket->burst++;
+
+			return qfalse;
+		}
+	}
+
+	return qtrue;
+}
+
+/*
+================
+SVC_RateLimitAddress
+
+Rate limit for a particular address
+================
+*/
+static qboolean SVC_RateLimitAddress( netadr_t from, int burst, int period ) {
+	leakyBucket_t *bucket = SVC_BucketForAddress( from, burst, period );
+
+	return SVC_RateLimit( bucket, burst, period );
+}
+
 /*
 ================
 SVC_Status
@@ -350,6 +519,21 @@ void SVC_Status( netadr_t from ) {
 	int		statusLength;
 	int		playerLength;
 	char	infostring[MAX_INFO_STRING];
+	static leakyBucket_t bucket;
+
+	// Prevent using getstatus as an amplifier
+	if ( SVC_RateLimitAddress( from, 10, 1000 ) ) {
+		Com_DPrintf( "SVC_Status: rate limit from %s exceeded, dropping request\n",
+			NET_AdrToString( from ) );
+		return;
+	}
+
+	// Allow getstatus to be DoSed relatively easily, but prevent
+	// excess outbound bandwidth usage when being flooded inbound
+	if ( SVC_RateLimit( &bucket, 10, 100 ) ) {
+		Com_DPrintf( "SVC_Status: rate limit exceeded, dropping request\n" );
+		return;
+	}
 
 	strcpy( infostring, Cvar_InfoString( CVAR_SERVERINFO ) );
 
@@ -466,24 +650,30 @@ Redirect all printfs
 */
 void SVC_RemoteCommand( netadr_t from, msg_t *msg ) {
 	qboolean	valid;
-	unsigned int time;
 	char		remaining[1024];
 	// TTimo - scaled down to accumulate, but not overflow anything network wise, print wise etc.
 	// (OOB messages are the bottleneck here)
 #define SV_OUTPUTBUF_LENGTH (1024 - 16)
 	char		sv_outputbuf[SV_OUTPUTBUF_LENGTH];
-	static unsigned int lasttime = 0;
 	char *cmd_aux;
 
-	// TTimo - https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=534
-	time = Com_Milliseconds();
-	if (time<(lasttime+500)) {
+	// Prevent using rcon as an amplifier and make dictionary attacks impractical
+	if ( SVC_RateLimitAddress( from, 10, 1000 ) ) {
+		Com_DPrintf( "SVC_Status: rate limit from %s exceeded, dropping request\n",
+			NET_AdrToString( from ) );
 		return;
 	}
-	lasttime = time;
 
 	if ( !strlen( sv_rconPassword->string ) ||
 		strcmp (Cmd_Argv(1), sv_rconPassword->string) ) {
+		static leakyBucket_t bucket;
+
+		// Make DoS via rcon impractical
+		if ( SVC_RateLimit( &bucket, 10, 1000 ) ) {
+			Com_DPrintf( "SVC_Status: rate limit exceeded, dropping request\n" );
+			return;
+		}
+
 		valid = qfalse;
 		Com_Printf ("Bad rcon from %s:\n%s\n", NET_AdrToString (from), Cmd_Argv(2) );
 	} else {


--- End Message ---
--- Begin Message ---
Version: 6.0.5

Hi,

All of the packages referenced by the closed bugs were included in the 6.0.5 point release which occured today.

Regards,

Adam


--- End Message ---

Reply to: