[SCM] Packaging for the OpenArena engine branch, debian-squeeze, updated. debian/0.8.5-5+squeeze1-1-gcaeb284

Simon McVittie smcv at debian.org
Sun Mar 25 22:37:22 UTC 2012


The following commit has been merged in the debian-squeeze branch:
commit caeb284533211bb0f76872279106a49306290168
Author: Simon McVittie <smcv at debian.org>
Date:   Sun Mar 25 19:55:49 2012 +0100

    Apply ioquake3 r1762 to rate-limit getstatus and rcon connectionless packets, to avoid ther use for traffic amplification. (Hopefully closes: #665656)

diff --git a/debian/changelog b/debian/changelog
index bb1de52..6f934b9 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,11 @@
+openarena (0.8.5-5+squeeze2~try1) UNRELEASED; urgency=low
+
+  * Apply ioquake3 r1762 to rate-limit getstatus and rcon connectionless
+    packets, to avoid ther use for traffic amplification.
+    (Hopefully closes: #665656)
+
+ -- Simon McVittie <smcv at debian.org>  Sun, 25 Mar 2012 19:34:53 +0100
+
 openarena (0.8.5-5+squeeze1) stable; urgency=medium
 
   * Apply upstream r2098 to fix arbitrary code execution by malicious QVM
diff --git a/debian/patches/0043-Rate-limit-getstatus-and-rcon-connectionless-request.patch b/debian/patches/0043-Rate-limit-getstatus-and-rcon-connectionless-request.patch
new file mode 100644
index 0000000..383358c
--- /dev/null
+++ b/debian/patches/0043-Rate-limit-getstatus-and-rcon-connectionless-request.patch
@@ -0,0 +1,294 @@
+From: Tim Angus <tma>
+Date: Sun, 3 Jan 2010 22:12:20 +0000
+Subject: [PATCH] * Rate limit getstatus and rcon connectionless requests
+
+Origin: upstream (ioquake3), commit:1762
+Bug-Debian: http://bugs.debian.org/665656
+---
+ code/server/sv_main.c |  217 ++++++++++++++++++++++++++++++++++++++++++++++---
+ 1 files changed, 207 insertions(+), 10 deletions(-)
+
+diff --git a/engine/code/server/sv_main.c b/engine/code/server/sv_main.c
+index 6215791..b88cc11 100644
+--- a/engine/code/server/sv_main.c
++++ b/engine/code/server/sv_main.c
+@@ -351,6 +351,182 @@ 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;
++	size_t	size;
++	int			i;
++	long		hash = 0;
++
++	switch ( address.type ) {
++		case NA_IP:  ip = address.ip;  size = 4; break;
++		case NA_IP6: ip = address.ip6; size = 16; break;
++		default: break;
++	}
++
++	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;
++
++			case NA_IP6:
++				if ( memcmp( bucket->ipv._6, address.ip6, 16 ) == 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 ) ) {
++			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 == NA_BAD ) {
++			bucket->type = address.type;
++			switch ( address.type ) {
++				case NA_IP:  Com_Memcpy( bucket->ipv._4, address.ip, 4 );   break;
++				case NA_IP6: Com_Memcpy( bucket->ipv._6, address.ip6, 16 ); 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
+@@ -369,12 +545,27 @@ static void SVC_Status( netadr_t from ) {
+ 	int		statusLength;
+ 	int		playerLength;
+ 	char	infostring[MAX_INFO_STRING];
++	static leakyBucket_t bucket;
+ 
+ 	// ignore if we are in single player
+ 	if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER ) {
+ 		return;
+ 	}
+ 
++	// 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 ) );
+ 
+ 	// echo back the parameter to status. so master servers can use it as a challenge
+@@ -493,24 +684,30 @@ Redirect all printfs
+ */
+ static 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 ( (unsigned)( time - lasttime ) < 500u ) {
++	// 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: %s\n", NET_AdrToString (from), Cmd_ArgsFrom(2) );
+ 	} else {
+@@ -579,7 +776,7 @@ static void SV_ConnectionlessPacket( netadr_t from, msg_t *msg ) {
+ 	Com_DPrintf ("SV packet %s : %s\n", NET_AdrToString(from), c);
+ 
+ 	if (!Q_stricmp(c, "getstatus")) {
+-		SVC_Status( from  );
++		SVC_Status( from );
+   } else if (!Q_stricmp(c, "getinfo")) {
+ 		SVC_Info( from );
+ 	} else if (!Q_stricmp(c, "getchallenge")) {
+@@ -597,8 +794,8 @@ static void SV_ConnectionlessPacket( netadr_t from, msg_t *msg ) {
+ 		// server disconnect messages when their new server sees our final
+ 		// sequenced messages to the old client
+ 	} else {
+-		Com_DPrintf ("bad connectionless packet from %s:\n%s\n"
+-		, NET_AdrToString (from), s);
++		Com_DPrintf ("bad connectionless packet from %s:\n%s\n",
++			NET_AdrToString (from), s);
+ 	}
+ }
+ 
+@@ -606,7 +803,7 @@ static void SV_ConnectionlessPacket( netadr_t from, msg_t *msg ) {
+ 
+ /*
+ =================
+-SV_ReadPackets
++SV_PacketEvent
+ =================
+ */
+ void SV_PacketEvent( netadr_t from, msg_t *msg ) {
+-- 
+1.7.9.1
+
diff --git a/debian/patches/series b/debian/patches/series
index b23fbea..5e83306 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -35,3 +35,4 @@
 0040-Add-OPENARENA_081_COMPATIBLE-define-for-network-comp.patch
 0041-If-a-QVM-starts-with-NTVE-followed-by-a-nonempty-str.patch
 0042-G_Damage-check-before-dereferencing-targ-client-whic.patch
+0043-Rate-limit-getstatus-and-rcon-connectionless-request.patch

-- 
Packaging for the OpenArena engine



More information about the Pkg-games-commits mailing list