From 1a1d81de626c9f811ef84b48ea723d4654d3f76a Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 1 Nov 2021 23:20:59 +0600 Subject: [PATCH] engine: add stuffcmd filtering, not wired to ClientCmd yet --- engine/common/cmd.c | 186 ++++++++++++++++++++++++++++++----------- engine/common/common.h | 3 +- engine/common/cvar.c | 43 +++++++++- engine/common/cvar.h | 2 +- engine/common/host.c | 2 + 5 files changed, 185 insertions(+), 51 deletions(-) diff --git a/engine/common/cmd.c b/engine/common/cmd.c index e1cc7493..fae7437b 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -30,11 +30,15 @@ typedef struct } cmdbuf_t; qboolean cmd_wait; -cmdbuf_t cmd_text; +cmdbuf_t cmd_text, filteredcmd_text; byte cmd_text_buf[MAX_CMD_BUFFER]; +byte filteredcmd_text_buf[MAX_CMD_BUFFER]; cmdalias_t *cmd_alias; uint cmd_condition; int cmd_condlevel; +static qboolean cmd_currentCommandIsPrivileged; + +static void Cmd_ExecuteStringWithPrivilegeCheck( const char *text, qboolean isPrivileged ); /* ============================================================================= @@ -52,8 +56,10 @@ Cbuf_Init void Cbuf_Init( void ) { cmd_text.data = cmd_text_buf; - cmd_text.maxsize = MAX_CMD_BUFFER; - cmd_text.cursize = 0; + filteredcmd_text.data = filteredcmd_text_buf; + + filteredcmd_text.maxsize = cmd_text.maxsize = MAX_CMD_BUFFER; + filteredcmd_text.cursize = cmd_text.cursize = 0; } /* @@ -63,8 +69,9 @@ Cbuf_Clear */ void Cbuf_Clear( void ) { - memset( cmd_text.data, 0, sizeof( cmd_text_buf )); - cmd_text.cursize = 0; + memset( cmd_text.data, 0, cmd_text.maxsize ); + memset( filteredcmd_text.data, 0, filteredcmd_text.maxsize ); + cmd_text.cursize = filteredcmd_text.cursize = 0; } /* @@ -88,6 +95,19 @@ void *Cbuf_GetSpace( cmdbuf_t *buf, int length ) return data; } +static void Cbuf_AddTextToBuffer( cmdbuf_t *buf, const char *text ) +{ + int l = Q_strlen( text ); + + if(( buf->cursize + l ) >= buf->maxsize ) + { + Con_Reportf( S_WARN "%s: overflow\n", __func__ ); + return; + } + + memcpy( Cbuf_GetSpace( buf, l ), text, l ); +} + /* ============ Cbuf_AddText @@ -97,16 +117,17 @@ Adds command text at the end of the buffer */ void Cbuf_AddText( const char *text ) { - int l = Q_strlen( text ); + Cbuf_AddTextToBuffer( &cmd_text, text ); +} - if(( cmd_text.cursize + l ) >= cmd_text.maxsize ) - { - Con_Reportf( S_WARN "Cbuf_AddText: overflow\n" ); - } - else - { - memcpy( Cbuf_GetSpace( &cmd_text, l ), text, l ); - } +/* +============ +Cbuf_AddFilteredText +============ +*/ +void Cbuf_AddFilteredText( const char *text ) +{ + Cbuf_AddTextToBuffer( &filteredcmd_text, text ); } /* @@ -117,43 +138,55 @@ Adds command text immediately after the current command Adds a \n to the text ============ */ -void Cbuf_InsertText( const char *text ) +static void Cbuf_InsertTextToBuffer( cmdbuf_t *buf, const char *text ) { int l = Q_strlen( text ); - if(( cmd_text.cursize + l ) >= cmd_text.maxsize ) + if(( buf->cursize + l ) >= buf->maxsize ) { Con_Reportf( S_WARN "Cbuf_InsertText: overflow\n" ); } else { - memmove( cmd_text.data + l, cmd_text.data, cmd_text.cursize ); - memcpy( cmd_text.data, text, l ); - cmd_text.cursize += l; + memmove( buf->data + l, buf->data, buf->cursize ); + memcpy( buf->data, text, l ); + buf->cursize += l; } } +void Cbuf_InsertText( const char *text ) +{ + Cbuf_InsertTextToBuffer( &cmd_text, text ); +} + /* ============ Cbuf_Execute ============ */ -void Cbuf_Execute( void ) +void Cbuf_ExecuteCommandsFromBuffer( cmdbuf_t *buf, qboolean isPrivileged, int cmdsToExecute ) { char *text; char line[MAX_CMD_LINE]; int i, quotes; char *comment; - while( cmd_text.cursize ) + while( buf->cursize ) { + // limit amount of commands that can be issued + if( cmdsToExecute >= 0 ) + { + if( !cmdsToExecute-- ) + break; + } + // find a \n or ; line break - text = (char *)cmd_text.data; + text = (char *)buf->data; quotes = false; comment = NULL; - for( i = 0; i < cmd_text.cursize; i++ ) + for( i = 0; i < buf->cursize; i++ ) { if( !comment ) { @@ -162,7 +195,7 @@ void Cbuf_Execute( void ) if( quotes ) { // make sure i doesn't get > cursize which causes a negative size in memmove, which is fatal --blub - if( i < ( cmd_text.cursize - 1 ) && ( text[i+0] == '\\' && (text[i+1] == '"' || text[i+1] == '\\'))) + if( i < ( buf->cursize - 1 ) && ( text[i+0] == '\\' && (text[i+1] == '"' || text[i+1] == '\\'))) i++; } else @@ -191,19 +224,19 @@ void Cbuf_Execute( void ) // delete the text from the command buffer and move remaining commands down // this is necessary because commands (exec) can insert data at the // beginning of the text buffer - if( i == cmd_text.cursize ) + if( i == buf->cursize ) { - cmd_text.cursize = 0; + buf->cursize = 0; } else { i++; - cmd_text.cursize -= i; - memmove( cmd_text.data, text + i, cmd_text.cursize ); + buf->cursize -= i; + memmove( buf->data, text + i, buf->cursize ); } // execute the command line - Cmd_ExecuteString( line ); + Cmd_ExecuteStringWithPrivilegeCheck( line, isPrivileged ); if( cmd_wait ) { @@ -215,6 +248,17 @@ void Cbuf_Execute( void ) } } +/* +============ +Cbuf_Execute +============ +*/ +void Cbuf_Execute( void ) +{ + Cbuf_ExecuteCommandsFromBuffer( &cmd_text, true, -1 ); + Cbuf_ExecuteCommandsFromBuffer( &filteredcmd_text, false, 1 ); +} + /* =============== Cbuf_ExecStuffCmds @@ -282,6 +326,11 @@ void Cbuf_ExecStuffCmds( void ) ============================================================================== */ +qboolean Cmd_CurrentCommandIsPrivileged( void ) +{ + return cmd_currentCommandIsPrivileged; +} + /* =============== Cmd_StuffCmds_f @@ -876,6 +925,32 @@ void Cmd_Else_f( void ) cmd_condition ^= BIT( cmd_condlevel ); } +static qboolean Cmd_ShouldAllowCommand( cmd_t *cmd, qboolean isPrivileged ) +{ + const char *prefixes[] = { "cl_", "gl_", "r_", "m_", "hud_" }; + int i; + + // always allow local commands + if( isPrivileged ) + return true; + + // never allow local only commands from remote + if( FBitSet( cmd->flags, CMD_LOCALONLY )) + return false; + + // allow engine commands if user don't mind + if( cl_filterstuffcmd.value <= 0.0f ) + return true; + + for( i = 0; i < ARRAYSIZE( prefixes ); i++ ) + { + if( !Q_stricmp( cmd->name, prefixes[i] )) + return false; + } + + return true; +} + /* ============ Cmd_ExecuteString @@ -883,7 +958,7 @@ Cmd_ExecuteString A complete command line has been parsed, so try to execute it ============ */ -void Cmd_ExecuteString( const char *text ) +static void Cmd_ExecuteStringWithPrivilegeCheck( const char *text, qboolean isPrivileged ) { cmd_t *cmd = NULL; cmdalias_t *a = NULL; @@ -952,44 +1027,56 @@ void Cmd_ExecuteString( const char *text ) if( !host.apply_game_config ) { // check aliases - if( a ) // already found in basecmd + if( !a ) // if not found in basecmd { - Cbuf_InsertText( a->value ); - return; + for( a = cmd_alias; a; a = a->next ) + { + if( !Q_stricmp( cmd_argv[0], a->name )) + break; + } } - for( a = cmd_alias; a; a = a->next ) + if( a ) { - if( !Q_stricmp( cmd_argv[0], a->name )) - { - Cbuf_InsertText( a->value ); - return; - } + Cbuf_InsertTextToBuffer( + isPrivileged ? &cmd_text : &filteredcmd_text, + a->value ); + return; } } // special mode for restore game.dll archived cvars if( !host.apply_game_config || !Q_strcmp( cmd_argv[0], "exec" )) { - // check functions - if( cmd && cmd->function ) // already found in basecmd + if( !cmd || !cmd->function ) // if not found in basecmd { - cmd->function(); - return; + for( cmd = cmd_functions; cmd; cmd = cmd->next ) + { + if( !Q_stricmp( cmd_argv[0], cmd->name ) && cmd->function ) + break; + } } - for( cmd = cmd_functions; cmd; cmd = cmd->next ) + // check functions + if( cmd && cmd->function ) { - if( !Q_stricmp( cmd_argv[0], cmd->name ) && cmd->function ) + if( Cmd_ShouldAllowCommand( cmd, isPrivileged )) { + cmd_currentCommandIsPrivileged = isPrivileged; cmd->function(); - return; + cmd_currentCommandIsPrivileged = true; + } + else + { + Con_Printf( S_WARN "Could not execute privileged command %s\n", cmd->name ); } + + return; } } // check cvars - if( Cvar_Command( cvar )) return; + if( Cvar_CommandWithPrivilegeCheck( cvar, isPrivileged )) return; if( host.apply_game_config ) return; // don't send nothing to server: we is a server! @@ -1011,6 +1098,11 @@ void Cmd_ExecuteString( const char *text ) } } +void Cmd_ExecuteString( const char *text ) +{ + Cmd_ExecuteStringWithPrivilegeCheck( text, true ); +} + /* =================== Cmd_ForwardToServer diff --git a/engine/common/common.h b/engine/common/common.h index 37ba3d54..c8800c57 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -184,6 +184,7 @@ extern convar_t host_developer; extern convar_t *host_limitlocal; extern convar_t *host_framerate; extern convar_t *host_maxfps; +extern convar_t cl_filterstuffcmd; /* ============================================================== @@ -465,7 +466,7 @@ extern sysinfo_t SI; #define CMD_CLIENTDLL BIT( 1 ) // added by client.dll #define CMD_GAMEUIDLL BIT( 2 ) // added by GameUI.dll #define CMD_LOCALONLY BIT( 3 ) // restricted from server commands -#define CMD_REFDLL BIT( 4 ) // added by ref.dll +#define CMD_REFDLL BIT( 4 ) // added by ref.dll typedef void (*xcommand_t)( void ); diff --git a/engine/common/cvar.c b/engine/common/cvar.c index 1d765037..d3bf0096 100644 --- a/engine/common/cvar.c +++ b/engine/common/cvar.c @@ -16,6 +16,7 @@ GNU General Public License for more details. #include // fabs... #include "common.h" #include "base_cmd.h" +#include "eiface.h" // ARRAYSIZE convar_t *cvar_vars = NULL; // head of list convar_t *cmd_scripting; @@ -754,6 +755,38 @@ static void Cvar_SetGL( const char *name, const char *value ) Cvar_FullSet( name, value, FCVAR_GLCONFIG ); } +static qboolean Cvar_ShouldSetCvar( convar_t *v, qboolean isPrivileged ) +{ + const char *prefixes[] = { "cl_", "gl_", "m_", "r_", "hud_" }; + int i; + + if( isPrivileged ) + return true; + + // TODO: figure this out + //if( v->flags & FCVAR_SERVER ) + // return false; + + if( cl_filterstuffcmd.value <= 0.0f ) + return true; + + // TODO: figure this out too + //if( v->flags & FCVAR_EXTDLL ) + // return false; + + // a1ba: xash3d-fwgs extension + if( v->flags & FCVAR_LOCALONLY ) + return false; + + for( i = 0; i < ARRAYSIZE( prefixes ); i++ ) + { + if( Q_stricmp( v->name, prefixes[i] )) + return false; + } + + return true; +} + /* ============ Cvar_Command @@ -761,7 +794,7 @@ Cvar_Command Handles variable inspection and changing from the console ============ */ -qboolean Cvar_Command( convar_t *v ) +qboolean Cvar_CommandWithPrivilegeCheck( convar_t *v, qboolean isPrivileged ) { // special case for setup opengl configuration if( host.apply_opengl_config ) @@ -773,7 +806,8 @@ qboolean Cvar_Command( convar_t *v ) // check variables if( !v ) // already found in basecmd v = Cvar_FindVar( Cmd_Argv( 0 )); - if( !v ) return false; + if( !v ) + return false; // perform a variable print or set if( Cmd_Argc() == 1 ) @@ -796,6 +830,11 @@ qboolean Cvar_Command( convar_t *v ) Con_Printf( "can't set \"%s\" in multiplayer\n", v->name ); return false; } + else if( !Cvar_ShouldSetCvar( v, isPrivileged )) + { + Con_Printf( "%s is a privileged variable\n", v->name ); + return true; + } else { Cvar_DirectSet( v, Cmd_Argv( 1 )); diff --git a/engine/common/cvar.h b/engine/common/cvar.h index ff9e48cc..d07ca8e2 100644 --- a/engine/common/cvar.h +++ b/engine/common/cvar.h @@ -75,7 +75,7 @@ void Cvar_WriteVariables( file_t *f, int group ); qboolean Cvar_Exists( const char *var_name ); void Cvar_Reset( const char *var_name ); void Cvar_SetCheatState( void ); -qboolean Cvar_Command( convar_t *v ); +qboolean Cvar_CommandWithPrivilegeCheck( convar_t *v, qboolean isPrivileged ); void Cvar_Init( void ); void Cvar_Unlink( int group ); diff --git a/engine/common/host.c b/engine/common/host.c index b9f24904..f194055e 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -52,6 +52,7 @@ struct tests_stats_s tests_stats; CVAR_DEFINE( host_developer, "developer", "0", 0, "engine is in development-mode" ); CVAR_DEFINE_AUTO( sys_ticrate, "100", 0, "framerate in dedicated mode" ); +CVAR_DEFINE_AUTO( cl_filterstuffcmd, "1", FCVAR_ARCHIVE | FCVAR_LOCALONLY, "filter commands coming from server" ); convar_t *host_serverstate; convar_t *host_gameloaded; @@ -1051,6 +1052,7 @@ int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGa Cmd_AddCommand ( "crash", Host_Crash_f, "a way to force a bus error for development reasons"); } + Cvar_RegisterVariable( &cl_filterstuffcmd ); host_serverstate = Cvar_Get( "host_serverstate", "0", FCVAR_READ_ONLY, "displays current server state" ); host_maxfps = Cvar_Get( "fps_max", "72", FCVAR_ARCHIVE, "host fps upper limit" ); host_framerate = Cvar_Get( "host_framerate", "0", 0, "locks frame timing to this value in seconds" );