Fix fs_game '..' reading outside of home and base path
VMs could set fs_game to '..' at anytime to access files outside of home and base path. fs_game sent by server to clients could also be '..' to access files outside of home and base path. '..' was not caught by FS_CheckDirTraversal() as it expects filenames not a single directory. I've made fs_game be latched to prevent VMs from changing it with no good way to validate it before it's used. com_basegame and fs_basegame are now latched as well. Additionally, it's now possible to change com_basegame while the engine is running. game_restart or vid_restart will make it take affect. com_homepath is now CVAR_PROTECTED to prevent VMs from changing it to a directory traversal. This requires my two previous commits for preventing VMs from changing engine latch cvars and only Cvar_Get fs_game in FS_Startup (so CVAR_INIT isn't added in serveral other places). Reported by Noah Metzger (Chomenor).
This commit is contained in:
parent
78ca670d4f
commit
3638f69dff
5 changed files with 54 additions and 32 deletions
|
@ -3067,6 +3067,24 @@ qboolean FS_CheckDirTraversal(const char *checkdir)
|
|||
return qfalse;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
FS_InvalidGameDir
|
||||
|
||||
return true if path is a reference to current directory or directory traversal
|
||||
================
|
||||
*/
|
||||
qboolean FS_InvalidGameDir( const char *gamedir ) {
|
||||
if ( !strcmp( gamedir, "." ) || !strcmp( gamedir, ".." )
|
||||
|| !strcmp( gamedir, "/" ) || !strcmp( gamedir, "\\" )
|
||||
|| strstr( gamedir, "/.." ) || strstr( gamedir, "\\.." )
|
||||
|| FS_CheckDirTraversal( gamedir ) ) {
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
return qfalse;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
FS_ComparePaks
|
||||
|
@ -3301,13 +3319,34 @@ static void FS_Startup( const char *gameName )
|
|||
|
||||
fs_debug = Cvar_Get( "fs_debug", "0", 0 );
|
||||
fs_basepath = Cvar_Get ("fs_basepath", Sys_DefaultInstallPath(), CVAR_INIT|CVAR_PROTECTED );
|
||||
fs_basegame = Cvar_Get ("fs_basegame", "", CVAR_INIT );
|
||||
fs_basegame = Cvar_Get ("fs_basegame", "", CVAR_LATCH|CVAR_NORESTART );
|
||||
homePath = Sys_DefaultHomePath();
|
||||
if (!homePath || !homePath[0]) {
|
||||
homePath = fs_basepath->string;
|
||||
}
|
||||
fs_homepath = Cvar_Get ("fs_homepath", homePath, CVAR_INIT|CVAR_PROTECTED );
|
||||
fs_gamedirvar = Cvar_Get ("fs_game", "", CVAR_INIT|CVAR_SYSTEMINFO );
|
||||
fs_gamedirvar = Cvar_Get ("fs_game", "", CVAR_LATCH|CVAR_NORESTART|CVAR_SYSTEMINFO );
|
||||
|
||||
if (!gameName[0]) {
|
||||
Cvar_ForceReset( "com_basegame" );
|
||||
}
|
||||
|
||||
if (!FS_FilenameCompare(fs_gamedirvar->string, gameName)) {
|
||||
// This is the standard base game. Servers and clients should
|
||||
// use "" and not the standard basegame name because this messes
|
||||
// up pak file negotiation and lots of other stuff
|
||||
Cvar_ForceReset( "fs_game" );
|
||||
}
|
||||
|
||||
if (FS_InvalidGameDir(gameName)) {
|
||||
Com_Error( ERR_DROP, "Invalid com_basegame '%s'", gameName );
|
||||
}
|
||||
if (FS_InvalidGameDir(fs_basegame->string)) {
|
||||
Com_Error( ERR_DROP, "Invalid fs_basegame '%s'", fs_basegame->string );
|
||||
}
|
||||
if (FS_InvalidGameDir(fs_gamedirvar->string)) {
|
||||
Com_Error( ERR_DROP, "Invalid fs_game '%s'", fs_gamedirvar->string );
|
||||
}
|
||||
|
||||
// add search path elements in reverse priority order
|
||||
fs_gogpath = Cvar_Get ("fs_gogpath", Sys_GogPath(), CVAR_INIT|CVAR_PROTECTED );
|
||||
|
@ -3391,8 +3430,6 @@ static void FS_Startup( const char *gameName )
|
|||
// print the current search paths
|
||||
FS_Path_f();
|
||||
|
||||
fs_gamedirvar->modified = qfalse; // We just loaded, it's not modified
|
||||
|
||||
Com_Printf( "----------------------\n" );
|
||||
|
||||
#ifdef FS_MISSING
|
||||
|
@ -4040,17 +4077,10 @@ Return qtrue if restarting due to game directory changed, qfalse otherwise
|
|||
*/
|
||||
qboolean FS_ConditionalRestart(int checksumFeed, qboolean disconnect)
|
||||
{
|
||||
if(fs_gamedirvar->modified)
|
||||
if (com_basegame->latchedString || fs_basegame->latchedString || fs_gamedirvar->latchedString)
|
||||
{
|
||||
if(FS_FilenameCompare(lastValidGame, fs_gamedirvar->string) &&
|
||||
(*lastValidGame || FS_FilenameCompare(fs_gamedirvar->string, com_basegame->string)) &&
|
||||
(*fs_gamedirvar->string || FS_FilenameCompare(lastValidGame, com_basegame->string)))
|
||||
{
|
||||
Com_GameRestart(checksumFeed, disconnect);
|
||||
return qtrue;
|
||||
}
|
||||
else
|
||||
fs_gamedirvar->modified = qfalse;
|
||||
Com_GameRestart(checksumFeed, disconnect);
|
||||
return qtrue;
|
||||
}
|
||||
|
||||
if(checksumFeed != fs_checksumFeed)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue