From 28d8dc25e5052a7e865da5747cd4765b7234aca8 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Sun, 14 Apr 2024 23:25:36 +0200 Subject: [PATCH] Engine: move unrestricted command exec code to separate function --- src/engine/cmd.cpp | 73 +++++++++++++++++++++++++++++++++++ src/engine/cmd.h | 4 ++ src/engine/server/sv_rcon.cpp | 52 +------------------------ 3 files changed, 78 insertions(+), 51 deletions(-) diff --git a/src/engine/cmd.cpp b/src/engine/cmd.cpp index 6b78bc74..fb52a7f8 100644 --- a/src/engine/cmd.cpp +++ b/src/engine/cmd.cpp @@ -110,6 +110,79 @@ bool Cmd_ForwardToServer(const CCommand* args) #endif // DEDICATED } +#ifndef CLIENT_DLL +//----------------------------------------------------------------------------- +// Purpose: execute commands directly (ignores all protection flags) +// Input : *pCommandString - +// *pValueString - +// Output : true on success, false otherwise +// +// NOTE : this function is dangerous, as it allows execution of any command +// without restrictions. Currently, this is only enabled on the +// dedicated server for the local console input and RCON, as they both +// are considered secure (local console needs physical access to the +// terminal application, RCON requires authentication and its protocol +// is secure. Do not use this anywhere else without a valid reason !!! +// +// NOTE : if client support is ever considered (unlikely), then the convar +// flag 'FCVAR_MATERIAL_THREAD_MASK' probably needs to be taken into +// account as well, also, change the DLL context of the warning to +// ENGINE if the client ever utilizes this. +//----------------------------------------------------------------------------- +bool Cmd_ExecuteUnrestricted(const char* const pCommandString, const char* const pValueString) +{ + ConCommandBase* const pCommandBase = g_pCVar->FindCommandBase(pCommandString); + + if (!pCommandBase) + { + // Found nothing. + Warning(eDLL_T::SERVER, "Command '%s' doesn't exist; request '%s' ignored\n", pCommandString, pValueString); + return false; + } + + if (pCommandBase->IsFlagSet(FCVAR_SERVER_FRAME_THREAD)) + ThreadJoinServerJob(); + + if (!pCommandBase->IsCommand()) + { + // Here we want to skip over the command string in the value buffer. + // So if we got 'sv_cheats 1' in our value buffer, we want to skip + // over 'sv_cheats ', so that we are pointing directly to the value. + const char* pFound = V_strstr(pValueString, pCommandString); + const char* pValue = nullptr; + + if (pFound) + { + pValue = pFound + V_strlen(pCommandString); + + // Skip any leading space characters. + while (*pValue == ' ') + { + ++pValue; + } + } + + ConVar* const pConVar = reinterpret_cast(pCommandBase); + pConVar->SetValue(pValue ? pValue : pValueString); + } + else // Invoke command callback directly. + { + CCommand cmd; + + // 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); + } + + return true; +} +#endif // !CLIENT_DLL + /////////////////////////////////////////////////////////////////////////////// void VCmd::Detour(const bool bAttach) const { diff --git a/src/engine/cmd.h b/src/engine/cmd.h index da1caa37..915d218e 100644 --- a/src/engine/cmd.h +++ b/src/engine/cmd.h @@ -26,6 +26,10 @@ FORCEINLINE ECommandTarget_t Cbuf_GetCurrentPlayer(void) extern bool Cbuf_HasRoomForExecutionMarkers(const int cExecutionMarkers); extern bool Cbuf_AddTextWithMarkers(const char* text, const ECmdExecutionMarker markerLeft, const ECmdExecutionMarker markerRight); +#ifndef CLIENT_DLL +extern bool Cmd_ExecuteUnrestricted(const char* const pCommandString, const char* const pValueString); +#endif // CLIENT_DLL + /* ==== COMMAND_BUFFER ================================================================================================================================================== */ inline void(*Cbuf_AddText)(ECommandTarget_t eTarget, const char* pText, cmd_source_t cmdSource); inline void(*Cbuf_AddExecutionMarker)(ECommandTarget_t target, ECmdExecutionMarker marker); diff --git a/src/engine/server/sv_rcon.cpp b/src/engine/server/sv_rcon.cpp index 0c759b88..92c74b1b 100644 --- a/src/engine/server/sv_rcon.cpp +++ b/src/engine/server/sv_rcon.cpp @@ -514,57 +514,7 @@ bool CRConServer::ProcessMessage(const char* pMsgBuf, const int nMsgLen) //----------------------------------------------------------------------------- void CRConServer::Execute(const netcon::request& request) const { - const string& commandString = request.requestmsg(); - const char* const pCommandString = commandString.c_str(); - - ConCommandBase* pCommandBase = g_pCVar->FindCommandBase(pCommandString); - - if (!pCommandBase) - { - // Found nothing. - return; - } - - const char* const pValueString = request.requestval().c_str(); - - if (pCommandBase->IsFlagSet(FCVAR_SERVER_FRAME_THREAD)) - ThreadJoinServerJob(); - - if (!pCommandBase->IsCommand()) - { - // Here we want to skip over the command string in the value buffer. - // So if we got 'sv_cheats 1' in our value buffer, we want to skip - // over 'sv_cheats ', so that we are pointing directly to the value. - const char* pFound = V_strstr(pValueString, pCommandString); - const char* pValue = nullptr; - - if (pFound) - { - pValue = pFound + commandString.length(); - - // Skip any leading space characters. - while (*pValue == ' ') - { - ++pValue; - } - } - - ConVar* pConVar = reinterpret_cast(pCommandBase); - pConVar->SetValue(pValue ? pValue : pValueString); - } - else // Invoke command callback directly. - { - CCommand cmd; - - // 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); - } + Cmd_ExecuteUnrestricted(request.requestmsg().c_str(), request.requestval().c_str()); } //-----------------------------------------------------------------------------