From 75e3d76db6a9f2d71456738dcea0d9c9668b4cd6 Mon Sep 17 00:00:00 2001 From: havoc Date: Fri, 12 Jan 2007 00:34:19 +0000 Subject: [PATCH] gamedir is now checked by FS_CheckNastyPath added special isgamedir flag to FS_CheckNastyPath to validate gamedir using more stringent checks than for normal filenames added additional checks in FS_CheckNastyPath for things like . in path elements (still allowed in filename at end but no longer allowed in path elements at all) git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@6684 d7cf8633-e32d-0410-b094-e92efae38249 --- fs.c | 47 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/fs.c b/fs.c index 50490bfb..89f0086c 100644 --- a/fs.c +++ b/fs.c @@ -1143,12 +1143,20 @@ FS_ChangeGameDir */ void Host_SaveConfig_f (void); void Host_LoadConfig_f (void); +int FS_CheckNastyPath (const char *path, qboolean isgamedir); qboolean FS_ChangeGameDir(const char *string) { // if already using the requested gamedir, do nothing if (fs_numgamedirs == 1 && !strcmp(fs_gamedirs[0], string)) return false; + // if string is nasty, reject it + if(FS_CheckNastyPath(string, true)) // overflowed or nasty? + { + Con_Printf("FS_ChangeGameDir(\"%s\"): nasty filename rejected\n", string); + return false; + } + // save the current config Host_SaveConfig_f(); @@ -1200,6 +1208,13 @@ void FS_GameDir_f (void) fs_numgamedirs = 0; for (i = 1;i < Cmd_Argc() && fs_numgamedirs < MAX_GAMEDIRS;i++) { + // if string is nasty, reject it + if(FS_CheckNastyPath(Cmd_Argv(i), true)) // overflowed or nasty? + { + Con_Printf("FS_GameDir_f(\"%s\"): nasty filename rejected\n", Cmd_Argv(i)); + continue; + } + strlcpy(fs_gamedirs[fs_numgamedirs], Cmd_Argv(i), sizeof(fs_gamedirs[fs_numgamedirs])); fs_numgamedirs++; } @@ -1490,8 +1505,12 @@ Return true if the path should be rejected due to one of the following: or are just not a good idea for a mod to be using. ==================== */ -int FS_CheckNastyPath (const char *path) +int FS_CheckNastyPath (const char *path, qboolean isgamedir) { + // all: never allow an empty path, as for gamedir it would access the parent directory and a non-gamedir path it is just useless + if (!path[0]) + return 2; + // Windows: don't allow \ in filenames (windows-only), period. // (on Windows \ is a directory separator, but / is also supported) if (strstr(path, "\\")) @@ -1508,14 +1527,34 @@ int FS_CheckNastyPath (const char *path) if (strstr(path, "//")) return 1; // non-portable attempt to go to parent directory - // all: don't allow going to current directory (./) or parent directory (../ or /../) - if (strstr(path, "./")) + // all: don't allow going to parent directory (../ or /../) + if (strstr(path, "..")) return 2; // attempt to go outside the game directory // Windows and UNIXes: don't allow absolute paths if (path[0] == '/') return 2; // attempt to go outside the game directory + // all: don't allow . characters before the last slash (it should only be used in filenames, not path elements), this catches all imaginable cases of ./, ../, .../, etc + if (strchr(path, '.')) + { + if (isgamedir) + { + // gamedir is entirely path elements, so simply forbid . entirely + return 2; + } + if (strchr(path, '.') < strrchr(path, '/')) + return 2; // possible attempt to go outside the game directory + } + + // all: forbid trailing slash on gamedir + if (isgamedir && path[strlen(path)-1] == '/') + return 2; + + // all: forbid leading dot on any filename for any reason + if (strstr(path, "/.")) + return 2; // attempt to go outside the game directory + // after all these checks we're pretty sure it's a / separated filename // and won't do much if any harm return false; @@ -1651,7 +1690,7 @@ Open a file. The syntax is the same as fopen */ qfile_t* FS_Open (const char* filepath, const char* mode, qboolean quiet, qboolean nonblocking) { - if (FS_CheckNastyPath(filepath)) + if (FS_CheckNastyPath(filepath, false)) { Con_Printf("FS_Open(\"%s\", \"%s\", %s): nasty filename rejected\n", filepath, mode, quiet ? "true" : "false"); return NULL; -- 2.39.2