about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authoroy <Tom_Adams@web.de>2011-07-31 13:05:12 +0200
committeroy <Tom_Adams@web.de>2011-07-31 13:05:12 +0200
commit94188021509a376fc352b7707d9a324335a97eba (patch)
treee0ad6c8d81a91217e4b035d764f3d57fb8a2c2c1 /src
parent1705c90e148d680c4b8002d12ac15a7d228f6408 (diff)
downloadzcatch-94188021509a376fc352b7707d9a324335a97eba.tar.gz
zcatch-94188021509a376fc352b7707d9a324335a97eba.zip
protected econ authentication against brute force
Diffstat (limited to 'src')
-rw-r--r--src/engine/shared/config_variables.h1
-rw-r--r--src/engine/shared/econ.cpp18
-rw-r--r--src/engine/shared/econ.h6
-rw-r--r--src/engine/shared/network.h18
-rw-r--r--src/engine/shared/network_console.cpp87
5 files changed, 126 insertions, 4 deletions
diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h
index 0789476e..c812063a 100644
--- a/src/engine/shared/config_variables.h
+++ b/src/engine/shared/config_variables.h
@@ -92,6 +92,7 @@ MACRO_CONFIG_INT(SvAutoDemoMax, sv_auto_demo_max, 10, 0, 1000, CFGFLAG_SERVER, "
 MACRO_CONFIG_STR(EcBindaddr, ec_bindaddr, 128, "localhost", CFGFLAG_SERVER, "Address to bind the external console to. Anything but 'localhost' is dangerous")
 MACRO_CONFIG_INT(EcPort, ec_port, 0, 0, 0, CFGFLAG_SERVER, "Port to use for the external console")
 MACRO_CONFIG_STR(EcPassword, ec_password, 32, "", CFGFLAG_SERVER, "External console password")
+MACRO_CONFIG_INT(EcBantime, ec_bantime, 0, 0, 1440, CFGFLAG_SERVER, "The time a client gets banned if econ authentication fails. 0 just closes the connection")
 MACRO_CONFIG_INT(EcAuthTimeout, ec_auth_timeout, 30, 1, 120, CFGFLAG_SERVER, "Time in seconds before the the econ authentification times out")
 MACRO_CONFIG_INT(EcOutputLevel, ec_output_level, 1, 0, 2, CFGFLAG_SERVER, "Adjusts the amount of information in the external console")
 
diff --git a/src/engine/shared/econ.cpp b/src/engine/shared/econ.cpp
index 18a551e7..617cdbd6 100644
--- a/src/engine/shared/econ.cpp
+++ b/src/engine/shared/econ.cpp
@@ -16,6 +16,7 @@ int CEcon::NewClientCallback(int ClientID, void *pUser)
 
 	pThis->m_aClients[ClientID].m_State = CClient::STATE_CONNECTED;
 	pThis->m_aClients[ClientID].m_TimeConnected = time_get();
+	pThis->m_aClients[ClientID].m_AuthTries = 0;
 
 	pThis->m_NetConsole.Send(ClientID, "Enter password:");
 	return 0;
@@ -112,7 +113,22 @@ void CEcon::Update()
 				Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "econ", aBuf);
 			}
 			else
-				m_NetConsole.Send(ClientID, "Wrong password");
+			{
+				m_aClients[ClientID].m_AuthTries++;
+				char aBuf[128];
+				str_format(aBuf, sizeof(aBuf), "Wrong password %d/%d.", m_aClients[ClientID].m_AuthTries, MAX_AUTH_TRIES);
+				m_NetConsole.Send(ClientID, aBuf);
+				if(m_aClients[ClientID].m_AuthTries >= MAX_AUTH_TRIES)
+				{
+					if(!g_Config.m_EcBantime)
+						m_NetConsole.Drop(ClientID, "Too many authentication tries");
+					else
+					{
+						NETADDR Addr = m_NetConsole.ClientAddr(ClientID);
+						m_NetConsole.AddBan(Addr, g_Config.m_EcBantime*60);
+					}
+				}
+			}
 		}
 		else if(m_aClients[ClientID].m_State == CClient::STATE_AUTHED)
 		{
diff --git a/src/engine/shared/econ.h b/src/engine/shared/econ.h
index 3ec19e28..daec34c4 100644
--- a/src/engine/shared/econ.h
+++ b/src/engine/shared/econ.h
@@ -5,6 +5,11 @@
 
 class CEcon
 {
+	enum
+	{
+		MAX_AUTH_TRIES=3,
+	};
+
 	class CClient
 	{
 	public:
@@ -17,6 +22,7 @@ class CEcon
 
 		int m_State;
 		int64 m_TimeConnected;
+		int m_AuthTries;
 	};
 	CClient m_aClients[NET_MAX_CONSOLE_CLIENTS];
 
diff --git a/src/engine/shared/network.h b/src/engine/shared/network.h
index 94e2824c..d10c03b6 100644
--- a/src/engine/shared/network.h
+++ b/src/engine/shared/network.h
@@ -325,7 +325,21 @@ public:
 
 class CNetConsole
 {
-private:
+	enum
+	{
+		MAX_BANS=128,
+	};
+
+	int FindBan(NETADDR Addr);
+	void UpdateBans();
+
+	struct CBanEntry
+	{
+		NETADDR m_Addr;
+		int m_Expires;
+	} m_aBans[MAX_BANS];
+	int m_NumBans;
+
 	struct CSlot
 	{
 		CConsoleNetConnection m_Connection;
@@ -356,6 +370,8 @@ public:
 	int AcceptClient(NETSOCKET Socket, const NETADDR *pAddr);
 	int Drop(int ClientID, const char *pReason);
 
+	bool AddBan(NETADDR Addr, int Seconds);
+
 	// status requests
 	NETADDR ClientAddr(int ClientID) const { return m_aSlots[ClientID].m_Connection.PeerAddress(); }
 };
diff --git a/src/engine/shared/network_console.cpp b/src/engine/shared/network_console.cpp
index f77e40f2..13ed3751 100644
--- a/src/engine/shared/network_console.cpp
+++ b/src/engine/shared/network_console.cpp
@@ -97,9 +97,28 @@ int CNetConsole::Update()
 	NETSOCKET Socket;
 	NETADDR Addr;
 
-	while(net_tcp_accept(m_Socket, &Socket, &Addr) > 0)
+	if(net_tcp_accept(m_Socket, &Socket, &Addr) > 0)
 	{
-		AcceptClient(Socket, &Addr);
+		int Index = FindBan(Addr);
+		if(Index == -1)
+			AcceptClient(Socket, &Addr);
+		else
+		{
+			char aBuf[128];
+			if(m_aBans[Index].m_Expires > -1)
+			{
+				int Mins = (m_aBans[Index].m_Expires-time_timestamp()+ 59) / 60;
+				if(Mins <= 1)
+					str_format(aBuf, sizeof(aBuf), "You have been banned for 1 minute");
+				else
+					str_format(aBuf, sizeof(aBuf), "You have been banned for %d minutes", Mins);
+			}
+			else
+				str_format(aBuf, sizeof(aBuf), "You have been banned for life");
+			
+			net_tcp_send(Socket, aBuf, str_length(aBuf));
+			net_tcp_close(Socket);
+		}
 	}
 
 	for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++)
@@ -110,6 +129,8 @@ int CNetConsole::Update()
 			Drop(i, m_aSlots[i].m_Connection.ErrorString());
 	}
 
+	UpdateBans();
+
 	return 0;
 }
 
@@ -134,3 +155,65 @@ int CNetConsole::Send(int ClientID, const char *pLine)
 	else
 		return -1;
 }
+
+int CNetConsole::FindBan(NETADDR Addr)
+{
+	Addr.port = 0;
+	for(int i = 0; i < m_NumBans; i++)
+		if(net_addr_comp(&m_aBans[i].m_Addr, &Addr) == 0)
+			return i;
+
+	return -1;
+}
+
+bool CNetConsole::AddBan(NETADDR Addr, int Seconds)
+{
+	if(m_NumBans == MAX_BANS)
+		return false;
+	
+	Addr.port = 0;
+	int Index = FindBan(Addr);
+	if(Index == -1)
+	{
+		Index = m_NumBans++;
+		m_aBans[Index].m_Addr = Addr;
+	}
+	m_aBans[Index].m_Expires = Seconds>0 ? time_timestamp()+Seconds : -1;
+
+	for(int i = 0; i < NET_MAX_CONSOLE_CLIENTS; i++)
+	{
+		if(m_aSlots[i].m_Connection.State() != NET_CONNSTATE_OFFLINE)
+		{
+			NETADDR PeerAddr = m_aSlots[i].m_Connection.PeerAddress();
+			PeerAddr.port = 0;
+			if(net_addr_comp(&Addr, &PeerAddr) == 0)
+			{
+				char aBuf[128];
+				if(Seconds>0)
+				{
+					int Mins = (Seconds + 59) / 60;
+					if(Mins <= 1)
+						str_format(aBuf, sizeof(aBuf), "You have been banned for 1 minute");
+					else
+						str_format(aBuf, sizeof(aBuf), "You have been banned for %d minutes", Mins);
+				}
+				else
+					str_format(aBuf, sizeof(aBuf), "You have been banned for life");
+				Drop(i, aBuf);
+			}
+		}
+	}
+	return true;
+}
+
+void CNetConsole::UpdateBans()
+{
+	int Now = time_timestamp();
+	for(int i = 0; i < m_NumBans; ++i)
+		if(m_aBans[i].m_Expires > 0 && m_aBans[i].m_Expires < Now)
+		{
+			m_aBans[i] = m_aBans[m_NumBans-1];
+			--m_NumBans;
+			break;
+		}
+}