From 67180e51ffb93319e1147efc5e3d6a8719dfc2e9 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Fri, 31 Mar 2023 22:37:53 +0200 Subject: [PATCH] Validate client's origin name on connect Recently, there has been a sudden influx of players playing with a name that isn't valid. Names such as " " or "@something $very !crazy". This patch fixes this problem by enforcing checks during connect by default. --- r5dev/ebisusdk/EbisuSDK.cpp | 43 +++++++++++++++++++++++++++------- r5dev/ebisusdk/EbisuSDK.h | 3 ++- r5dev/engine/server/server.cpp | 10 ++++---- r5dev/tier1/cvar.cpp | 16 +++++++++---- r5dev/tier1/cvar.h | 8 +++++-- 5 files changed, 61 insertions(+), 19 deletions(-) diff --git a/r5dev/ebisusdk/EbisuSDK.cpp b/r5dev/ebisusdk/EbisuSDK.cpp index f88d01ca..0872b012 100644 --- a/r5dev/ebisusdk/EbisuSDK.cpp +++ b/r5dev/ebisusdk/EbisuSDK.cpp @@ -1,8 +1,22 @@ #include "core/stdafx.h" +#include "tier1/cvar.h" #include "ebisusdk/EbisuSDK.h" //----------------------------------------------------------------------------- -// Purpose: +// Purpose: sets the EbisuSDK globals for dedicated to satisfy command callbacks +//----------------------------------------------------------------------------- +void HEbisuSDK_Init() +{ +#ifdef DEDICATED + *g_EbisuSDKInit = true; // <- 1st EbisuSDK + *g_EbisuProfileInit = true; // <- 2nd EbisuSDK + *g_NucleusID = true; // <- 3rd EbisuSDK +#endif // DEDICATED +} + +//----------------------------------------------------------------------------- +// Purpose: checks if the EbisuSDK is initialized +// Output : true on success, false on failure //----------------------------------------------------------------------------- bool IsOriginInitialized() { @@ -21,13 +35,26 @@ bool IsOriginInitialized() } //----------------------------------------------------------------------------- -// Purpose: sets the EbisuSDK globals for dedicated to satisfy command callbacks +// Purpose: validates if client's persona name meets EA's criteria +// Input : *pszName - +// Output : true on success, false on failure //----------------------------------------------------------------------------- -void HEbisuSDK_Init() +bool IsValidPersonaName(const char* pszName) { -#ifdef DEDICATED - *g_EbisuSDKInit = true; // <- 1st EbisuSDK - *g_EbisuProfileInit = true; // <- 2nd EbisuSDK - *g_NucleusID = true; // <- 3rd EbisuSDK -#endif // DEDICATED + if (!sv_validatePersonaName->GetBool()) + { + return true; + } + + size_t len = strlen(pszName); + + if (len < sv_minPersonaNameLength->GetInt() || + len > sv_maxPersonaNameLength->GetInt()) + { + return false; + } + + // Check if the name contains any special characters. + size_t pos = strspn(pszName, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"); + return pszName[pos] == '\0'; } diff --git a/r5dev/ebisusdk/EbisuSDK.h b/r5dev/ebisusdk/EbisuSDK.h index 9bdbf9b7..e3396fc0 100644 --- a/r5dev/ebisusdk/EbisuSDK.h +++ b/r5dev/ebisusdk/EbisuSDK.h @@ -21,8 +21,9 @@ inline bool* g_EbisuProfileInit = nullptr; //#endif // DEDICATED /////////////////////////////////////////////////////////////////////////////// -bool IsOriginInitialized(); void HEbisuSDK_Init(); +bool IsOriginInitialized(); +bool IsValidPersonaName(const char* pszName); /////////////////////////////////////////////////////////////////////////////// class VEbisuSDK : public IDetour diff --git a/r5dev/engine/server/server.cpp b/r5dev/engine/server/server.cpp index fe617180..89df487e 100644 --- a/r5dev/engine/server/server.cpp +++ b/r5dev/engine/server/server.cpp @@ -15,6 +15,7 @@ #include "engine/server/server.h" #include "networksystem/pylon.h" #include "networksystem/bansystem.h" +#include "ebisusdk/EbisuSDK.h" #include "public/edict.h" //--------------------------------------------------------------------------------- @@ -94,7 +95,8 @@ bool CServer::AuthClient(user_creds_s* pChallenge) if (bEnableLogging) DevMsg(eDLL_T::SERVER, "Processing connectionless challenge for '%s' ('%llu')\n", pszAddresBuffer, nNucleusID); - if (!pUserID || !pUserID[0] || !IsValidUTF8(pUserID)) // Only proceed connection if the client's name is valid and UTF-8 encoded. + // Only proceed connection if the client's name is valid and UTF-8 encoded. + if (!pUserID || !pUserID[0] || !IsValidUTF8(pUserID) || !IsValidPersonaName(pUserID)) { RejectConnection(m_Socket, &pChallenge->m_nAddr, "#Valve_Reject_Invalid_Name"); if (bEnableLogging) @@ -103,11 +105,11 @@ bool CServer::AuthClient(user_creds_s* pChallenge) return false; } - if (g_pBanSystem->IsBanListValid()) // Is the banned list vector valid? + if (g_pBanSystem->IsBanListValid()) { - if (g_pBanSystem->IsBanned(pszAddresBuffer, nNucleusID)) // Is the client trying to connect banned? + if (g_pBanSystem->IsBanned(pszAddresBuffer, nNucleusID)) { - RejectConnection(m_Socket, &pChallenge->m_nAddr, "#Valve_Reject_Banned"); // RejectConnection for the client. + RejectConnection(m_Socket, &pChallenge->m_nAddr, "#Valve_Reject_Banned"); if (bEnableLogging) Warning(eDLL_T::SERVER, "Connection rejected for '%s' ('%llu' is banned from this server!)\n", pszAddresBuffer, nNucleusID); diff --git a/r5dev/tier1/cvar.cpp b/r5dev/tier1/cvar.cpp index e3c0333d..4ac54aa5 100644 --- a/r5dev/tier1/cvar.cpp +++ b/r5dev/tier1/cvar.cpp @@ -87,14 +87,18 @@ ConVar* sv_forceChatToTeamOnly = nullptr; ConVar* sv_updaterate_mp = nullptr; ConVar* sv_updaterate_sp = nullptr; - ConVar* sv_autoReloadRate = nullptr; -ConVar* sv_quota_stringCmdsPerSecond = nullptr; ConVar* sv_simulateBots = nullptr; ConVar* sv_showhitboxes = nullptr; ConVar* sv_stats = nullptr; +ConVar* sv_quota_stringCmdsPerSecond = nullptr; + +ConVar* sv_validatePersonaName = nullptr; +ConVar* sv_minPersonaNameLength = nullptr; +ConVar* sv_maxPersonaNameLength = nullptr; + //#ifdef DEDICATED ConVar* sv_rcon_debug = nullptr; ConVar* sv_rcon_sendlogs = nullptr; @@ -333,7 +337,6 @@ void ConVar::Init(void) sv_banlistRefreshRate = ConVar::StaticCreate("sv_banlistRefreshRate", "1.0", FCVAR_RELEASE, "Banned list refresh rate (seconds).", true, 1.f, false, 0.f, nullptr, nullptr); sv_statusRefreshRate = ConVar::StaticCreate("sv_statusRefreshRate" , "0.5", FCVAR_RELEASE, "Server status refresh rate (seconds).", false, 0.f, false, 0.f, nullptr, nullptr); sv_autoReloadRate = ConVar::StaticCreate("sv_autoReloadRate" , "0" , FCVAR_RELEASE, "Time in seconds between each server auto-reload (disabled if null). ", true, 0.f, false, 0.f, nullptr, nullptr); - sv_quota_stringCmdsPerSecond = ConVar::StaticCreate("sv_quota_stringCmdsPerSecond", "16", FCVAR_RELEASE, "How many string commands per second clients are allowed to submit, 0 to disallow all string commands.", true, 0.f, false, 0.f, nullptr, nullptr); sv_simulateBots = ConVar::StaticCreate("sv_simulateBots", "1", FCVAR_RELEASE, "Simulate user commands for bots on the server.", true, 0.f, false, 0.f, nullptr, nullptr); sv_rcon_debug = ConVar::StaticCreate("sv_rcon_debug" , "0" , FCVAR_RELEASE, "Show rcon debug information ( !slower! ).", false, 0.f, false, 0.f, nullptr, nullptr); @@ -343,6 +346,11 @@ void ConVar::Init(void) sv_rcon_maxignores = ConVar::StaticCreate("sv_rcon_maxignores" , "15", FCVAR_RELEASE, "Max number of times a user can ignore the instruction message before being banned.", true, 1.f, false, 0.f, nullptr, nullptr); sv_rcon_maxsockets = ConVar::StaticCreate("sv_rcon_maxsockets" , "32", FCVAR_RELEASE, "Max number of accepted sockets before the server starts closing redundant sockets.", true, 1.f, false, 0.f, nullptr, nullptr); sv_rcon_whitelist_address = ConVar::StaticCreate("sv_rcon_whitelist_address", "", FCVAR_RELEASE, "This address is not considered a 'redundant' socket and will never be banned for failed authentication attempts.", false, 0.f, false, 0.f, nullptr, "Format: '::ffff:127.0.0.1'"); + + sv_quota_stringCmdsPerSecond = ConVar::StaticCreate("sv_quota_stringCmdsPerSecond", "16", FCVAR_RELEASE, "How many string commands per second clients are allowed to submit, 0 to disallow all string commands.", true, 0.f, false, 0.f, nullptr, nullptr); + sv_validatePersonaName = ConVar::StaticCreate("sv_validatePersonaName" , "1" , FCVAR_RELEASE, "Validate the client's textual persona name on connect.", true, 0.f, false, 0.f, nullptr, nullptr); + sv_minPersonaNameLength = ConVar::StaticCreate("sv_minPersonaNameLength", "4" , FCVAR_RELEASE, "The minimum length of the client's textual persona name.", true, 0.f, false, 0.f, nullptr, nullptr); + sv_maxPersonaNameLength = ConVar::StaticCreate("sv_maxPersonaNameLength", "16", FCVAR_RELEASE, "The maximum length of the client's textual persona name.", true, 0.f, false, 0.f, nullptr, nullptr); #endif // !CLIENT_DLL #if !defined (GAMEDLL_S0) && !defined (GAMEDLL_S1) bhit_depth_test = ConVar::StaticCreate("bhit_depth_test", "0", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED, "Use depth test for bullet ray trace overlay.", false, 0.f, false, 0.f, nullptr, nullptr); @@ -449,7 +457,7 @@ void ConVar::Init(void) //------------------------------------------------------------------------- // MILES | #ifndef DEDICATED - miles_debug = ConVar::StaticCreate("miles_debug", "0", FCVAR_RELEASE, "Enables debug prints for the Miles Sound System", false, 0.f, false, 0.f, nullptr, " 1 = Print, 0 = No Print"); + miles_debug = ConVar::StaticCreate("miles_debug", "0", FCVAR_RELEASE, "Enables debug prints for the Miles Sound System.", false, 0.f, false, 0.f, nullptr, " 1 = Print, 0 = No Print"); #endif // !DEDICATED //------------------------------------------------------------------------- } diff --git a/r5dev/tier1/cvar.h b/r5dev/tier1/cvar.h index ade257db..79e70278 100644 --- a/r5dev/tier1/cvar.h +++ b/r5dev/tier1/cvar.h @@ -93,14 +93,18 @@ extern ConVar* sv_forceChatToTeamOnly; extern ConVar* sv_updaterate_mp; extern ConVar* sv_updaterate_sp; - extern ConVar* sv_autoReloadRate; -extern ConVar* sv_quota_stringCmdsPerSecond; extern ConVar* sv_simulateBots; extern ConVar* sv_showhitboxes; extern ConVar* sv_stats; +extern ConVar* sv_quota_stringCmdsPerSecond; + +extern ConVar* sv_validatePersonaName; +extern ConVar* sv_minPersonaNameLength; +extern ConVar* sv_maxPersonaNameLength; + //#ifdef DEDICATED extern ConVar* sv_rcon_debug; extern ConVar* sv_rcon_sendlogs;