From 976e8d9a4111381ebed1ec5ce75c12bbc580c33c Mon Sep 17 00:00:00 2001 From: Amos <48657826+Mauler125@users.noreply.github.com> Date: Mon, 14 Feb 2022 03:02:38 +0100 Subject: [PATCH] Use SHA256 for password comparison Put debug logging under cvar to avoid abusing by attempting to slow down server. --- r5dev/engine/sv_rcon.cpp | 34 ++++++++++++++++++++++++++++++---- r5dev/engine/sv_rcon.h | 4 +++- r5dev/mathlib/sha256.cpp | 11 ++++++----- r5dev/tier0/IConVar.cpp | 11 ++++++----- r5dev/tier0/cvar.cpp | 1 + r5dev/tier0/cvar.h | 1 + 6 files changed, 47 insertions(+), 15 deletions(-) diff --git a/r5dev/engine/sv_rcon.cpp b/r5dev/engine/sv_rcon.cpp index 80090aaf..0106c65c 100644 --- a/r5dev/engine/sv_rcon.cpp +++ b/r5dev/engine/sv_rcon.cpp @@ -41,6 +41,7 @@ void CRConServer::Init(void) m_pAdr2 = new CNetAdr2(rcon_address->GetString(), hostport->GetString()); m_pSocket->CreateListenSocket(*m_pAdr2, false); + m_svPasswordHash = sha256(rcon_password->GetString()); DevMsg(eDLL_T::SERVER, "Remote server access initialized\n"); m_bInitialized = true; @@ -233,8 +234,8 @@ void CRConServer::Authenticate(const cl_rcon::request& cl_request, CConnectedNet } else if (strcmp(cl_request.requestbuf().c_str(), "PASS") == 0) { - if (strcmp(cl_request.requestval().c_str(), rcon_password->GetString()) == 0) - {// TODO: Hash and compare password with SHA256 instead! + if (this->Comparator(cl_request.requestval())) + { pData->m_bAuthorized = true; m_pSocket->CloseListenSocket(); this->CloseNonAuthConnection(); @@ -242,9 +243,12 @@ void CRConServer::Authenticate(const cl_rcon::request& cl_request, CConnectedNet else // Bad password. { CNetAdr2 netAdr2 = m_pSocket->GetAcceptedSocketAddress(m_nConnIndex); - DevMsg(eDLL_T::SERVER, "Bad RCON password attempt from '%s'\n", netAdr2.GetIPAndPort().c_str()); + if (sv_rcon_debug->GetBool()) + { + DevMsg(eDLL_T::SERVER, "Bad RCON password attempt from '%s'\n", netAdr2.GetIPAndPort().c_str()); + } - std::string svWrongPass = this->Serialize(s_pszBannedMessage, "", sv_rcon::response_t::SERVERDATA_RESPONSE_AUTH); + std::string svWrongPass = this->Serialize(s_pszWrongPwMessage, "", sv_rcon::response_t::SERVERDATA_RESPONSE_AUTH); ::send(pData->m_hSocket, svWrongPass.c_str(), static_cast(svWrongPass.size()), MSG_NOSIGNAL); pData->m_bAuthorized = false; @@ -253,6 +257,28 @@ void CRConServer::Authenticate(const cl_rcon::request& cl_request, CConnectedNet } } +//----------------------------------------------------------------------------- +// Purpose: sha256 hashed password comparison +// Input : *svCompare - +// Output : true if matches, false otherwise +//----------------------------------------------------------------------------- +bool CRConServer::Comparator(std::string svPassword) const +{ + svPassword = sha256(svPassword); + if (sv_rcon_debug->GetBool()) + { + DevMsg(eDLL_T::SERVER, "+---------------------------------------------------------------------------+\n"); + DevMsg(eDLL_T::SERVER, "] Server: '%s'[\n", m_svPasswordHash.c_str()); + DevMsg(eDLL_T::SERVER, "] Client: '%s'[\n", svPassword.c_str()); + DevMsg(eDLL_T::SERVER, "+---------------------------------------------------------------------------+\n"); + } + if (memcmp(svPassword.c_str(), m_svPasswordHash.c_str(), SHA256::DIGEST_SIZE) == 0) + { + return true; + } + return false; +} + //----------------------------------------------------------------------------- // Purpose: handles input command buffer // Input : *pszIn - diff --git a/r5dev/engine/sv_rcon.h b/r5dev/engine/sv_rcon.h index 30db0303..97d711d7 100644 --- a/r5dev/engine/sv_rcon.h +++ b/r5dev/engine/sv_rcon.h @@ -12,8 +12,8 @@ class CRConServer { public: void Init(void); - void Think(void); + void Think(void); void RunFrame(void); void Send(const std::string& svMessage) const; @@ -23,6 +23,7 @@ public: cl_rcon::request Deserialize(const std::string& svBuf) const; void Authenticate(const cl_rcon::request& cl_request, CConnectedNetConsoleData* pData); + bool Comparator(std::string svPassword) const; void ProcessBuffer(const char* pszIn, int nRecvLen, CConnectedNetConsoleData* pData); void ProcessMessage(const cl_rcon::request& cl_request); @@ -40,5 +41,6 @@ private: CNetAdr2* m_pAdr2 = new CNetAdr2(); CSocketCreator* m_pSocket = new CSocketCreator(); std::vector m_vBannedAddress; + std::string m_svPasswordHash; }; extern CRConServer* g_pRConServer; \ No newline at end of file diff --git a/r5dev/mathlib/sha256.cpp b/r5dev/mathlib/sha256.cpp index 9b4cd768..32d4bde4 100644 --- a/r5dev/mathlib/sha256.cpp +++ b/r5dev/mathlib/sha256.cpp @@ -27,8 +27,8 @@ void SHA256::transform(const unsigned char *message, unsigned int block_nb) const unsigned char *sub_block; int i; int j; - for (i = 0; i < (int) block_nb; i++) { - sub_block = message + (i << 6); + for (i = 0; i < static_cast(block_nb); i++) { + sub_block = message + (static_cast(i) << 6); for (j = 0; j < 16; j++) { SHA2_PACK32(&sub_block[j << 2], &w[j]); } @@ -104,7 +104,7 @@ void SHA256::final(unsigned char *digest) < (m_len % SHA224_256_BLOCK_SIZE))); len_b = (m_tot_len + m_len) << 3; pm_len = block_nb << 6; - memset(m_block + m_len, 0, pm_len - m_len); + memset(m_block + m_len, '\0', static_cast(pm_len) - m_len); m_block[m_len] = 0x80; SHA2_UNPACK32(len_b, m_block + pm_len - 4); transform(m_block, block_nb); @@ -116,11 +116,12 @@ void SHA256::final(unsigned char *digest) std::string sha256(std::string input) { unsigned char digest[SHA256::DIGEST_SIZE]; - memset(digest,0,SHA256::DIGEST_SIZE); + memset(digest, '\0', SHA256::DIGEST_SIZE); SHA256 ctx = SHA256(); ctx.init(); - ctx.update( (unsigned char*)input.c_str(), input.length()); + ctx.update(const_cast + (reinterpret_cast(input.c_str())), input.length()); ctx.final(digest); char buf[2*SHA256::DIGEST_SIZE+1]{}; diff --git a/r5dev/tier0/IConVar.cpp b/r5dev/tier0/IConVar.cpp index b15b1b2c..c55b8f15 100644 --- a/r5dev/tier0/IConVar.cpp +++ b/r5dev/tier0/IConVar.cpp @@ -55,18 +55,19 @@ void ConVar::Init(void) r_debug_overlay_nodecay = new ConVar("r_debug_overlay_nodecay", "0", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT, "Keeps all debug overlays alive regardless of their lifetime. Use command 'clear_debug_overlays' to clear everything.", false, 0.f, false, 0.f, nullptr, nullptr); // TODO: RconPasswordChanged_f - rcon_address = new ConVar("rcon_address", "::", FCVAR_SERVER_CANNOT_QUERY | FCVAR_DONTRECORD | FCVAR_RELEASE, "Remote console address.", false, 0.f, false, 0.f, nullptr, nullptr); - rcon_password = new ConVar("rcon_password", "", FCVAR_SERVER_CANNOT_QUERY | FCVAR_DONTRECORD | FCVAR_RELEASE, "Remote console password, RCON is disabled if empty.", false, 0.f, false, 0.f, nullptr, nullptr); + rcon_address = new ConVar("rcon_address", "::", FCVAR_SERVER_CANNOT_QUERY | FCVAR_DONTRECORD | FCVAR_RELEASE, "Remote server access address.", false, 0.f, false, 0.f, nullptr, nullptr); + rcon_password = new ConVar("rcon_password", "", FCVAR_SERVER_CANNOT_QUERY | FCVAR_DONTRECORD | FCVAR_RELEASE, "Remote server access password (rcon is disabled if empty).", false, 0.f, false, 0.f, nullptr, nullptr); //------------------------------------------------------------------------- // SERVER | sv_showconnecting = new ConVar("sv_showconnecting", "1", FCVAR_RELEASE, "Logs information about the connecting client to the console.", false, 0.f, false, 0.f, nullptr, nullptr); sv_pylonvisibility = new ConVar("sv_pylonvisibility", "0", FCVAR_RELEASE, "Determines the visiblity to the Pylon Master Server, 0 = Not visible, 1 = Visible, 2 = Hidden BUG BUG: not implemented yet.", false, 0.f, false, 0.f, nullptr, nullptr); #ifdef DEDICATED - sv_rcon_banpenalty = new ConVar("sv_rcon_banpenalty", "10", FCVAR_RELEASE, "Number of minutes to ban users who fail rcon authentication.", false, 0.f, false, 0.f, nullptr, nullptr); + sv_rcon_debug = new ConVar("sv_rcon_debug", "0", FCVAR_RELEASE, "Show rcon debug information ( !slower! ).", false, 0.f, false, 0.f, nullptr, nullptr); + sv_rcon_banpenalty = new ConVar("sv_rcon_banpenalty", "10", FCVAR_RELEASE, "Number of minutes to ban users who fail rcon authentication.", false, 0.f, false, 0.f, nullptr, nullptr); sv_rcon_maxfailures = new ConVar("sv_rcon_maxfailures", "10", FCVAR_RELEASE, "Max number of times a user can fail rcon authentication before being banned.", false, 0.f, false, 0.f, nullptr, nullptr); - sv_rcon_maxignores = new ConVar("sv_rcon_maxignores", "10", FCVAR_RELEASE, "Max number of times a user can ignore the no-auth message before being banned.", false, 0.f, false, 0.f, nullptr, nullptr); - sv_rcon_maxsockets = new ConVar("sv_rcon_maxsockets", "32", FCVAR_RELEASE, "Max number of accepted sockets before the server starts closing redundant sockets.", false, 0.f, false, 0.f, nullptr, nullptr); + sv_rcon_maxignores = new ConVar("sv_rcon_maxignores", "10", FCVAR_RELEASE, "Max number of times a user can ignore the no-auth message before being banned.", false, 0.f, false, 0.f, nullptr, nullptr); + sv_rcon_maxsockets = new ConVar("sv_rcon_maxsockets", "32", FCVAR_RELEASE, "Max number of accepted sockets before the server starts closing redundant sockets.", false, 0.f, false, 0.f, nullptr, nullptr); sv_rcon_whitelist_address = new ConVar("sv_rcon_whitelist_address", "", FCVAR_RELEASE, "When set, rcon failed authentications will never ban this address, e.g. '127.0.0.1'.", false, 0.f, false, 0.f, nullptr, nullptr); #endif // DEDICATED //------------------------------------------------------------------------- diff --git a/r5dev/tier0/cvar.cpp b/r5dev/tier0/cvar.cpp index db99843a..a9ee8608 100644 --- a/r5dev/tier0/cvar.cpp +++ b/r5dev/tier0/cvar.cpp @@ -18,6 +18,7 @@ ConVar* sv_showconnecting = new ConVar(); ConVar* sv_pylonvisibility = new ConVar(); #ifdef DEDICATED +ConVar* sv_rcon_debug = new ConVar(); ConVar* sv_rcon_banpenalty = new ConVar(); // TODO ConVar* sv_rcon_maxfailures = new ConVar(); ConVar* sv_rcon_maxignores = new ConVar(); diff --git a/r5dev/tier0/cvar.h b/r5dev/tier0/cvar.h index 39ffa847..5f04e9af 100644 --- a/r5dev/tier0/cvar.h +++ b/r5dev/tier0/cvar.h @@ -29,6 +29,7 @@ extern ConVar* rcon_password; extern ConVar* sv_showconnecting; extern ConVar* sv_pylonvisibility; #ifdef DEDICATED +extern ConVar* sv_rcon_debug; extern ConVar* sv_rcon_banpenalty; extern ConVar* sv_rcon_maxfailures; extern ConVar* sv_rcon_maxignores;