Sophie

Sophie

distrib > Mageia > 1 > i586 > media > core-updates-src > by-pkgid > 500e6ffc762ef741c06f9c5419c6d76b > files > 4

openttd-1.1.0-1.3.mga1.src.rpm

Subject: fix for vulnerability CVE-2012-0049 for OpenTTD 1.1.0 - 1.1.4 (Denial of service (server) via slow read attack)
From: OpenTTD developer team <info@openttd.org>
Origin: backport, http://vcs.openttd.org/svn/changeset/23764 
Bug: http://bugs.openttd.org/task/4955 

Using a slow read type attack it is possible to prevent anyone from joining 
a server with virtually no resources. Once downloading the map no other
downloads of the map can start, so downloading really slowly will prevent   
others from joining. This can be further aggravated by the pause-on-join
setting in which case the game is paused and the players cannot continue the 
game during such an attack. This attack requires that the user is not banned 
and passes the authorization to the server, although for many servers there 
is no server password and thus authorization is easy.

A similar attack can be done when performing the attack during the
authorization phase itself, however you will not block anyone else from
joining, unless you use connection multiple times until the connection limit 
is reached, or stop the continuation of the game of the already joined
players. This attack requires the user to be merely not banned.

Note that versions before 0.6.0 are vulnerable as well. However, these
versions are over five years old and not supported anymore. Therefore no
patches for earlier versions are provided. Before 0.3.5 it is not possible  
to exploit this bug via the internet as multiplayer over internet did not   
exist yet. The provided patch is a simplification of the fix in 1.1.5
because that version slightly changes the network protocol to tell people   
they got kicked due to the (password) timeout.

The attached patch does not change network compatability. The fix in trunk
does change network compatability.

Index: src/table/settings.h
===================================================================
--- src/table/settings.h	(revision 23768)
+++ src/table/settings.h	(working copy)
@@ -645,6 +645,8 @@
 	  SDTC_VAR(network.bytes_per_frame,      SLE_UINT16, S, NO,     8,        1,    65535, 0, STR_NULL,                                       NULL),
 	  SDTC_VAR(network.bytes_per_frame_burst,SLE_UINT16, S, NO,   256,        1,    65535, 0, STR_NULL,                                       NULL),
 	  SDTC_VAR(network.max_join_time,        SLE_UINT16, S, NO,   500,        0,    32000, 0, STR_NULL,                                       NULL),
+	  SDTC_VAR(network.max_download_time,    SLE_UINT16, S, NO,  1000,        0,    32000, 0, STR_NULL,                                       NULL),
+	  SDTC_VAR(network.max_password_time,    SLE_UINT16, S, NO,  2000,        0,    32000, 0, STR_NULL,                                       NULL),
 	 SDTC_BOOL(network.pause_on_join,                    S, NO,  true,                        STR_NULL,                                       NULL),
 	  SDTC_VAR(network.server_port,          SLE_UINT16, S, NO,NETWORK_DEFAULT_PORT,0,65535,0,STR_NULL,                                       NULL),
 	  SDTC_VAR(network.server_admin_port,    SLE_UINT16, S, NO,  NETWORK_ADMIN_PORT,0,65535,0,STR_NULL,                                       NULL),
Index: src/settings_type.h
===================================================================
--- src/settings_type.h	(revision 23768)
+++ src/settings_type.h	(working copy)
@@ -155,6 +155,8 @@
 	uint16 bytes_per_frame;                               ///< how many bytes may, over a long period, be received per frame?
 	uint16 bytes_per_frame_burst;                         ///< how many bytes may, over a short period, be received?
 	uint16 max_join_time;                                 ///< maximum amount of time, in game ticks, a client may take to join
+	uint16 max_download_time;                             ///< maximum amount of time, in game ticks, a client may take to download the map
+	uint16 max_password_time;                             ///< maximum amount of time, in game ticks, a client may take to enter the password
 	bool   pause_on_join;                                 ///< pause the game when people join
 	uint16 server_port;                                   ///< port the server listens on
 	uint16 server_admin_port;                             ///< port the server listens on for the admin network
Index: src/network/network_server.cpp
===================================================================
--- src/network/network_server.cpp	(revision 23768)
+++ src/network/network_server.cpp	(working copy)
@@ -1752,20 +1752,49 @@
 				cs->CloseConnection(NETWORK_RECV_STATUS_SERVER_ERROR);
 				continue;
 			}
-		} else if (cs->status == NetworkClientSocket::STATUS_PRE_ACTIVE) {
+		} else if (cs->status == NetworkClientSocket::STATUS_DONE_MAP ||
+					cs->status == NetworkClientSocket::STATUS_PRE_ACTIVE) {
+			/* The map has been sent, so this is for loading the map and syncing up. */
 			uint lag = NetworkCalculateLag(cs);
 			if (lag > _settings_client.network.max_join_time) {
 				IConsolePrintF(CC_ERROR,"Client #%d is dropped because it took longer than %d ticks for him to join", cs->client_id, _settings_client.network.max_join_time);
 				cs->CloseConnection(NETWORK_RECV_STATUS_SERVER_ERROR);
 				continue;
 			}
-		} else if (cs->status == NetworkClientSocket::STATUS_INACTIVE) {
+		} else if (cs->status == NetworkClientSocket::STATUS_INACTIVE ||
+				cs->status == NetworkClientSocket::STATUS_NEWGRFS_CHECK ||
+				cs->status == NetworkClientSocket::STATUS_AUTHORIZED) {
+			/* NewGRF check and authorized states should be handled almost instantly.
+			 * So give them some lee-way, likewise for the query with inactive. */
 			uint lag = NetworkCalculateLag(cs);
 			if (lag > 4 * DAY_TICKS) {
 				IConsolePrintF(CC_ERROR,"Client #%d is dropped because it took longer than %d ticks to start the joining process", cs->client_id, 4 * DAY_TICKS);
 				cs->CloseConnection(NETWORK_RECV_STATUS_SERVER_ERROR);
 				continue;
 			}
+		} else if (cs->status == NetworkClientSocket::STATUS_MAP) {
+			/* Downloading the map... this is the amount of time since starting the saving. */
+			uint lag = NetworkCalculateLag(cs);
+			if (lag > _settings_client.network.max_download_time) {
+				IConsolePrintF(CC_ERROR,"Client #%d is dropped because it took longer than %d ticks for him to download the map", cs->client_id, _settings_client.network.max_download_time);
+				cs->CloseConnection(NETWORK_RECV_STATUS_SERVER_ERROR);
+				continue;
+			}
+		} else if (cs->status == NetworkClientSocket::STATUS_AUTH_GAME ||
+				cs->status == NetworkClientSocket::STATUS_AUTH_COMPANY) {
+			/* Waiting for the password. */
+			uint lag = NetworkCalculateLag(cs);
+			if (lag > _settings_client.network.max_password_time) {
+				IConsolePrintF(CC_ERROR,"Client #%d is dropped because it took longer than %d ticks to enter the password", cs->client_id, _settings_client.network.max_password_time);
+				cs->CloseConnection(NETWORK_RECV_STATUS_SERVER_ERROR);
+				continue;
+			}
+		} else if (cs->status == NetworkClientSocket::STATUS_MAP_WAIT) {
+			/* This is an internal state where we do not wait
+			 * on the client to move to a different state. */
+		} else {
+			/* Bad server/code. */
+			NOT_REACHED();
 		}
 
 		if (cs->status >= NetworkClientSocket::STATUS_PRE_ACTIVE) {