mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
Engine: fix RCON bugs and improve security
- Upgraded hashing algorithm to SHA-512, and store the raw hash instead of a string copy, which is way cheaper to compute and compare. - Only ever close sockets once in CRConServer::SetPassword(). - Made the game server & game client RCON singletons static. - Added calls to gracefully shutdown RCON server and RCON client on Engine/SDK shutdown. - Added more prints so RCON user knows when its shutdown, or when their password change is in effect, etc. - Fixed bug where we could tokenize an empty string when we dispatch a console command.
This commit is contained in:
parent
6afb5fe593
commit
acbe43a210
@ -72,6 +72,7 @@
|
|||||||
#include "rtech/rui/rui.h"
|
#include "rtech/rui/rui.h"
|
||||||
#include "engine/client/cl_ents_parse.h"
|
#include "engine/client/cl_ents_parse.h"
|
||||||
#include "engine/client/cl_main.h"
|
#include "engine/client/cl_main.h"
|
||||||
|
#include "engine/client/cl_rcon.h"
|
||||||
#include "engine/client/cl_splitscreen.h"
|
#include "engine/client/cl_splitscreen.h"
|
||||||
#endif // !DEDICATED
|
#endif // !DEDICATED
|
||||||
#include "engine/client/client.h"
|
#include "engine/client/client.h"
|
||||||
@ -94,6 +95,7 @@
|
|||||||
#include "engine/networkstringtable.h"
|
#include "engine/networkstringtable.h"
|
||||||
#ifndef CLIENT_DLL
|
#ifndef CLIENT_DLL
|
||||||
#include "engine/server/sv_main.h"
|
#include "engine/server/sv_main.h"
|
||||||
|
#include "engine/server/sv_rcon.h"
|
||||||
#endif // !CLIENT_DLL
|
#endif // !CLIENT_DLL
|
||||||
#include "engine/sdk_dll.h"
|
#include "engine/sdk_dll.h"
|
||||||
#include "engine/sys_dll.h"
|
#include "engine/sys_dll.h"
|
||||||
@ -267,6 +269,14 @@ void Systems_Init()
|
|||||||
|
|
||||||
void Systems_Shutdown()
|
void Systems_Shutdown()
|
||||||
{
|
{
|
||||||
|
// Shutdown RCON (closes all open sockets)
|
||||||
|
#ifndef CLIENT_DLL
|
||||||
|
RCONServer()->Shutdown();
|
||||||
|
#endif// !CLIENT_DLL
|
||||||
|
#ifndef SERVER_DLL
|
||||||
|
RCONClient()->Shutdown();
|
||||||
|
#endif // !SERVER_DLL
|
||||||
|
|
||||||
CFastTimer shutdownTimer;
|
CFastTimer shutdownTimer;
|
||||||
shutdownTimer.Start();
|
shutdownTimer.Start();
|
||||||
|
|
||||||
|
@ -29,6 +29,9 @@ CRConClient::CRConClient()
|
|||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
CRConClient::~CRConClient(void)
|
CRConClient::~CRConClient(void)
|
||||||
{
|
{
|
||||||
|
// NOTE: do not call Shutdown() from the destructor as the OS's socket
|
||||||
|
// system would be shutdown by now, call Shutdown() in application
|
||||||
|
// shutdown code instead
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -76,7 +79,7 @@ void CRConClient::Disconnect(const char* szReason)
|
|||||||
szReason = "unknown reason";
|
szReason = "unknown reason";
|
||||||
}
|
}
|
||||||
|
|
||||||
Msg(eDLL_T::CLIENT, "Disconnect: (%s)\n", szReason);
|
Msg(eDLL_T::CLIENT, "RCON disconnect: (%s)\n", szReason);
|
||||||
m_Socket.CloseAcceptedSocket(0);
|
m_Socket.CloseAcceptedSocket(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -221,8 +224,8 @@ bool CRConClient::IsConnected(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
CRConClient* g_RCONClient(new CRConClient());
|
static CRConClient s_RCONClient;
|
||||||
CRConClient* RCONClient() // Singleton RCON Client.
|
CRConClient* RCONClient() // Singleton RCON Client.
|
||||||
{
|
{
|
||||||
return g_RCONClient;
|
return &s_RCONClient;
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,8 @@
|
|||||||
#include "engine/server/sv_rcon.h"
|
#include "engine/server/sv_rcon.h"
|
||||||
#include "protoc/sv_rcon.pb.h"
|
#include "protoc/sv_rcon.pb.h"
|
||||||
#include "protoc/cl_rcon.pb.h"
|
#include "protoc/cl_rcon.pb.h"
|
||||||
#include "mathlib/sha256.h"
|
|
||||||
#include "common/igameserverdata.h"
|
#include "common/igameserverdata.h"
|
||||||
|
#include "mbedtls/include/mbedtls/sha512.h"
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Purpose:
|
// Purpose:
|
||||||
@ -39,6 +39,9 @@ CRConServer::CRConServer(void)
|
|||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
CRConServer::~CRConServer(void)
|
CRConServer::~CRConServer(void)
|
||||||
{
|
{
|
||||||
|
// NOTE: do not call Shutdown() from the destructor as the OS's socket
|
||||||
|
// system would be shutdown by now, call Shutdown() in application
|
||||||
|
// shutdown code instead
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -73,13 +76,17 @@ void CRConServer::Init(void)
|
|||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
void CRConServer::Shutdown(void)
|
void CRConServer::Shutdown(void)
|
||||||
{
|
{
|
||||||
|
m_bInitialized = false;
|
||||||
|
|
||||||
|
const int nConnCount = m_Socket.GetAcceptedSocketCount();
|
||||||
m_Socket.CloseAllAcceptedSockets();
|
m_Socket.CloseAllAcceptedSockets();
|
||||||
|
|
||||||
if (m_Socket.IsListening())
|
if (m_Socket.IsListening())
|
||||||
{
|
{
|
||||||
m_Socket.CloseListenSocket();
|
m_Socket.CloseListenSocket();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_bInitialized = false;
|
Msg(eDLL_T::SERVER, "Remote server access deinitialized ('%i' accepted sockets closed)\n", nConnCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
@ -123,9 +130,6 @@ void CRConServer::Think(void)
|
|||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
bool CRConServer::SetPassword(const char* pszPassword)
|
bool CRConServer::SetPassword(const char* pszPassword)
|
||||||
{
|
{
|
||||||
m_bInitialized = false;
|
|
||||||
m_Socket.CloseAllAcceptedSockets();
|
|
||||||
|
|
||||||
const size_t nLen = strlen(pszPassword);
|
const size_t nLen = strlen(pszPassword);
|
||||||
if (nLen < RCON_MIN_PASSWORD_LEN)
|
if (nLen < RCON_MIN_PASSWORD_LEN)
|
||||||
{
|
{
|
||||||
@ -139,8 +143,26 @@ bool CRConServer::SetPassword(const char* pszPassword)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_svPasswordHash = sha256(pszPassword);
|
// This is here so we only print the confirmation message if the user
|
||||||
Msg(eDLL_T::SERVER, "Password hash ('%s')\n", m_svPasswordHash.c_str());
|
// actually requested to change the password rather than initializing
|
||||||
|
// the RCON server
|
||||||
|
const bool wasInitialized = m_bInitialized;
|
||||||
|
|
||||||
|
m_bInitialized = false;
|
||||||
|
m_Socket.CloseAllAcceptedSockets();
|
||||||
|
|
||||||
|
const int nHashRet = mbedtls_sha512(reinterpret_cast<const uint8_t*>(pszPassword), nLen, m_PasswordHash, NULL);
|
||||||
|
|
||||||
|
if (nHashRet != 0)
|
||||||
|
{
|
||||||
|
Error(eDLL_T::SERVER, 0, "SHA-512 algorithm failed on RCON password [%i]\n", nHashRet);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wasInitialized)
|
||||||
|
{
|
||||||
|
Msg(eDLL_T::SERVER, "Successfully changed RCON server password\n");
|
||||||
|
}
|
||||||
|
|
||||||
m_bInitialized = true;
|
m_bInitialized = true;
|
||||||
return true;
|
return true;
|
||||||
@ -358,24 +380,21 @@ void CRConServer::Authenticate(const cl_rcon::request& request, CConnectedNetCon
|
|||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
// Purpose: sha256 hashed password comparison
|
// Purpose: sha512 hashed password comparison
|
||||||
// Input : &svPassword -
|
// Input : &svPassword -
|
||||||
// Output : true if matches, false otherwise
|
// Output : true if matches, false otherwise
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
bool CRConServer::Comparator(const string& svPassword) const
|
bool CRConServer::Comparator(const string& svPassword) const
|
||||||
{
|
{
|
||||||
string passwordHash = sha256(svPassword);
|
uint8_t clientPasswordHash[RCON_SHA512_HASH_SIZE];
|
||||||
if (sv_rcon_debug->GetBool())
|
mbedtls_sha512(reinterpret_cast<const uint8_t*>(svPassword.c_str()), svPassword.length(),
|
||||||
{
|
clientPasswordHash, NULL);
|
||||||
Msg(eDLL_T::SERVER, "+---------------------------------------------------------------------------+\n");
|
|
||||||
Msg(eDLL_T::SERVER, "[ Server: '%s']\n", m_svPasswordHash.c_str());
|
if (memcmp(clientPasswordHash, m_PasswordHash, RCON_SHA512_HASH_SIZE) == 0)
|
||||||
Msg(eDLL_T::SERVER, "[ Client: '%s']\n", passwordHash.c_str());
|
|
||||||
Msg(eDLL_T::SERVER, "+---------------------------------------------------------------------------+\n");
|
|
||||||
}
|
|
||||||
if (memcmp(passwordHash.data(), m_svPasswordHash.data(), SHA256::DIGEST_SIZE) == 0)
|
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -492,7 +511,13 @@ void CRConServer::Execute(const cl_rcon::request& request) const
|
|||||||
else // Invoke command callback directly.
|
else // Invoke command callback directly.
|
||||||
{
|
{
|
||||||
CCommand cmd;
|
CCommand cmd;
|
||||||
cmd.Tokenize(pValueString, cmd_source_t::kCommandSrcCode);
|
|
||||||
|
// Only tokenize if we actually have strings in the value buffer, some
|
||||||
|
// commands (like 'status') don't need any additional parameters.
|
||||||
|
if (VALID_CHARSTAR(pValueString))
|
||||||
|
{
|
||||||
|
cmd.Tokenize(pValueString, cmd_source_t::kCommandSrcCode);
|
||||||
|
}
|
||||||
|
|
||||||
v_Cmd_Dispatch(ECommandTarget_t::CBUF_SERVER, pCommandBase, &cmd, false);
|
v_Cmd_Dispatch(ECommandTarget_t::CBUF_SERVER, pCommandBase, &cmd, false);
|
||||||
}
|
}
|
||||||
@ -653,8 +678,8 @@ int CRConServer::GetAuthenticatedCount(void) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
CRConServer* g_RCONServer(new CRConServer());
|
static CRConServer s_RCONServer;
|
||||||
CRConServer* RCONServer() // Singleton RCON Server.
|
CRConServer* RCONServer() // Singleton RCON Server.
|
||||||
{
|
{
|
||||||
return g_RCONServer;
|
return &s_RCONServer;
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#define RCON_MIN_PASSWORD_LEN 8
|
#define RCON_MIN_PASSWORD_LEN 8
|
||||||
#define RCON_MAX_BANNEDLIST_SIZE 512
|
#define RCON_MAX_BANNEDLIST_SIZE 512
|
||||||
|
#define RCON_SHA512_HASH_SIZE 64
|
||||||
|
|
||||||
class CRConServer : public CNetConBase
|
class CRConServer : public CNetConBase
|
||||||
{
|
{
|
||||||
@ -59,7 +60,7 @@ private:
|
|||||||
int m_nAuthConnections;
|
int m_nAuthConnections;
|
||||||
bool m_bInitialized;
|
bool m_bInitialized;
|
||||||
std::unordered_set<std::string> m_BannedList;
|
std::unordered_set<std::string> m_BannedList;
|
||||||
std::string m_svPasswordHash;
|
uint8_t m_PasswordHash[RCON_SHA512_HASH_SIZE];
|
||||||
netadr_t m_WhiteListAddress;
|
netadr_t m_WhiteListAddress;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user