From 7d51d75b05a9593508040162709043516c0f2a17 Mon Sep 17 00:00:00 2001 From: Thilo Schulz Date: Mon, 3 Jul 2006 21:37:50 +0000 Subject: [PATCH] - Fix arbitrary cvar overwrite flaw: http://aluigi.altervista.org/adv.htm - Add myself to maintainer list :) --- README | 1 + code/client/cl_parse.c | 23 +++++++++++++++++++++-- code/qcommon/cvar.c | 14 ++++++++++++++ code/qcommon/files.c | 19 ++++++++++++++++++- code/qcommon/q_shared.h | 3 +++ code/qcommon/qcommon.h | 4 ++++ 6 files changed, 61 insertions(+), 3 deletions(-) diff --git a/README b/README index bb717312..8fb6c515 100644 --- a/README +++ b/README @@ -244,6 +244,7 @@ Maintainers Aaron Gyes Ludwig Nussel Ryan C. Gordon + Thilo Schulz Tim Angus Zachary J. Slater diff --git a/code/client/cl_parse.c b/code/client/cl_parse.c index 81bdf5cc..b12105bb 100644 --- a/code/client/cl_parse.c +++ b/code/client/cl_parse.c @@ -368,16 +368,35 @@ void CL_SystemInfoChanged( void ) { // scan through all the variables in the systeminfo and locally set cvars to match s = systemInfo; while ( s ) { + int cvar_flags; + Info_NextPair( &s, key, value ); if ( !key[0] ) { break; } + // ehw! - if ( !Q_stricmp( key, "fs_game" ) ) { + if (!Q_stricmp(key, "fs_game")) + { + if(FS_CheckDirTraversal(value)) + { + Com_Printf("WARNING: Server sent invalid fs_game value %s\n", value); + continue; + } + gameSet = qtrue; } - Cvar_Set( key, value ); + if((cvar_flags = Cvar_Flags(key)) == CVAR_NONEXISTENT) + Cvar_Get(key, value, CVAR_SERVER_CREATED | CVAR_ROM); + else + { + // If this cvar may not be modified by a server discard the value. + if(!(cvar_flags & (CVAR_SYSTEMINFO | CVAR_SERVER_CREATED))) + continue; + + Cvar_Set(key, value); + } } // if game folder should not be set and it is set at the client side if ( !gameSet && *Cvar_VariableString("fs_game") ) { diff --git a/code/qcommon/cvar.c b/code/qcommon/cvar.c index 2600880b..a529de6d 100644 --- a/code/qcommon/cvar.c +++ b/code/qcommon/cvar.c @@ -161,6 +161,20 @@ void Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize } } +/* +============ +Cvar_Flags +============ +*/ +int Cvar_Flags(const char *var_name) +{ + cvar_t *var; + + if(! (var = Cvar_FindVar(var_name)) ) + return CVAR_NONEXISTENT; + else + return var->flags; +} /* ============ diff --git a/code/qcommon/files.c b/code/qcommon/files.c index 7411905a..af5518b4 100644 --- a/code/qcommon/files.c +++ b/code/qcommon/files.c @@ -2568,6 +2568,23 @@ qboolean FS_idPak( char *pak, char *base ) { return qfalse; } +/* +================ +FS_idPak + +Check whether the string contains stuff like "../" to prevent directory traversal bugs +and return qtrue if it does. +================ +*/ + +qboolean FS_CheckDirTraversal(const char *checkdir) +{ + if(strstr(checkdir, "../") || strstr(checkdir, "..\\")) + return qtrue; + + return qfalse; +} + /* ================ FS_ComparePaks @@ -2617,7 +2634,7 @@ qboolean FS_ComparePaks( char *neededpaks, int len, qboolean dlstring ) { } // Make sure the server cannot make us write to non-quake3 directories. - if(strstr(fs_serverReferencedPakNames[i], "../") || strstr(fs_serverReferencedPakNames[i], "..\\")) + if(FS_CheckDirTraversal(fs_serverReferencedPakNames[i])) { Com_Printf("WARNING: Invalid download name %s\n", fs_serverReferencedPakNames[i]); continue; diff --git a/code/qcommon/q_shared.h b/code/qcommon/q_shared.h index 705517a7..1e55103e 100644 --- a/code/qcommon/q_shared.h +++ b/code/qcommon/q_shared.h @@ -756,6 +756,9 @@ default values. #define CVAR_CHEAT 512 // can not be changed if cheats are disabled #define CVAR_NORESTART 1024 // do not clear when a cvar_restart is issued +#define CVAR_SERVER_CREATED 2048 // cvar was created by a server the client connected to. +#define CVAR_NONEXISTENT 0xFFFFFFFF // Cvar doesn't exist. + // nothing outside the Cvar_*() functions should modify these fields! typedef struct cvar_s { char *name; diff --git a/code/qcommon/qcommon.h b/code/qcommon/qcommon.h index 202775f2..1069434e 100644 --- a/code/qcommon/qcommon.h +++ b/code/qcommon/qcommon.h @@ -480,6 +480,9 @@ char *Cvar_VariableString( const char *var_name ); void Cvar_VariableStringBuffer( const char *var_name, char *buffer, int bufsize ); // returns an empty string if not defined +int Cvar_Flags(const char *var_name); +// returns CVAR_NONEXISTENT if cvar doesn't exist or the flags of that particular CVAR. + void Cvar_CommandCompletion( void(*callback)(const char *s) ); // callback with each valid string @@ -648,6 +651,7 @@ void FS_PureServerSetLoadedPaks( const char *pakSums, const char *pakNames ); // separated checksums will be checked for files, with the // sole exception of .cfg files. +qboolean FS_CheckDirTraversal(const char *checkdir); qboolean FS_idPak( char *pak, char *base ); qboolean FS_ComparePaks( char *neededpaks, int len, qboolean dlstring );