Skip to content

Commit

Permalink
No commit message
Browse files Browse the repository at this point in the history
  • Loading branch information
bobode committed Jul 29, 2012
1 parent 9ea2b92 commit 058a4b8
Show file tree
Hide file tree
Showing 7 changed files with 237 additions and 123 deletions.
2 changes: 1 addition & 1 deletion D2BS.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

#define XP_WIN

#define D2BS_VERSION "1.4.1717"
#define D2BS_VERSION "1.5.1717"

#include <windows.h>
#include <vector>
Expand Down
98 changes: 98 additions & 0 deletions File.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@

#include <string>

#include <Windows.h>

#include "File.h"
#include "D2BS.h"

using namespace std;

Expand Down Expand Up @@ -185,7 +188,102 @@ bool writeValue(FILE* fptr, JSContext* cx, jsval value, bool isBinary, bool lock
return false;
}

/** Safely open a file relative to the script dir.
*
* In theory this is the only function used to open files that is exposed to
* javascript.
*
* \param filename Name of the file to open relative to the script folder
*
* \param mode Mode to open in. See fopen_s.
*
* \param cx JSContext that is running. Used to throw errors.
*
* \return The file pointer.
*/
FILE* fileOpenRelScript(const char* filename, const char* mode, JSContext* cx)
{
FILE* f;
char fullPath[_MAX_PATH+_MAX_FNAME];

// Get the relative path
if(getPathRelScript(filename, _MAX_PATH+_MAX_FNAME, fullPath) == NULL)
{
JS_ReportError(cx, "Invalid file name");
return NULL;
}

// Open the file
if(fopen_s(&f, fullPath, mode) != 0 || f == NULL)
{
JS_ReportError(cx, "Couldn't open file %s: %s", filename, _strerror(NULL));
return NULL;
}

return f;
}

/** Get the full path relative to the script path. Does the validation check.
*
* \param filename Name of the file to open relative to the script folder.
*
* \param bufLen Length of the output buffer.
*
* \param fullPath Buffer where the full path will be stored. Empty string if
* location is invalid.
*
* \return fullPath on success or NULL on failure.
*/
char* getPathRelScript(const char* filename, int bufLen, char* fullPath)
{
char fullScriptPath[_MAX_PATH+_MAX_FNAME];
char* relPath;
int strLenScript;
DWORD scrPathLen;

strLenScript = strlen(Vars.szScriptPath);

// Make the filename relative to the script path
relPath = (char*)_alloca(strLenScript+strlen(filename)+2);
strcpy(relPath, Vars.szScriptPath);
relPath[strLenScript] = '\\';
strcpy(relPath+strLenScript+1, filename);

// Transform to the full pathname
GetFullPathName(relPath, bufLen, fullPath, NULL);

// Get the full path of the script path, check it is the prefix of fullPath
scrPathLen = GetFullPathName(Vars.szScriptPath, sizeof(fullScriptPath),
fullScriptPath, NULL);

// Check that fullScriptPath is the prefix of fullPath
// As GetFullPathName seems to not add a trailing \, if there is not a
// trailing \ in fullScriptPath check for it in fullPath
if(strncmp(fullPath, fullScriptPath, scrPathLen) != 0 ||
(fullScriptPath[scrPathLen-1] != '\\' &&
fullPath[scrPathLen] != '\\'))
{
fullPath[0] = '\0';
return NULL;
}

return fullPath;
}

/** Check that the full path of the script path is the prefix of the fullpath
* of name. Also checks that there is no ..\ or ../ sequences or ":?*<>| chars.
*
* \param name The file/path to validate.
*
* \return true if path is valid, false otherwise.
*/
bool isValidPath(const char* name)
{
char fullPath[_MAX_PATH+_MAX_FNAME];

// Use getPathRelScript to validate based on full paths
if(getPathRelScript(name, _MAX_PATH+_MAX_FNAME, fullPath) == NULL)
return false;

return (!strstr(name, "..\\") && !strstr(name, "../") && (strcspn(name, "\":?*<>|") == strlen(name)));
}
2 changes: 2 additions & 0 deletions File.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

const char* readLine(FILE* fptr, bool locking);
bool writeValue(FILE* fptr, JSContext* cx, jsval value, bool isBinary, bool locking);
FILE* fileOpenRelScript(const char* filename, const char* mode, JSContext* cx);
char* getPathRelScript(const char* filename, int bufLen, char* fullPath);
bool isValidPath(const char* name);

#endif
60 changes: 36 additions & 24 deletions JSFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ JSAPI_PROP(file_getProperty)
*vp = JSVAL_ZERO;
break;
case FILE_PATH:
*vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, fdata->path+strlen(Vars.szScriptPath)+1));
*vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, fdata->path+1));
break;
case FILE_POSITION:
if(fdata->fptr)
Expand Down Expand Up @@ -168,40 +168,50 @@ JSAPI_FUNC(file_open)
if(!JSVAL_IS_INT(JS_ARGV(cx, vp)[1]))
THROW_ERROR(cx, "Parameter 2 not a mode");

// check for attempts to break the sandbox and for invalid file name characters
// Convert from JS params to C values
char* file = JS_EncodeString(cx,JSVAL_TO_STRING(JS_ARGV(cx, vp)[0]));
if(!(file && file[0] && isValidPath(file)))
THROW_ERROR(cx, "Invalid file name");

int32 mode;
if(JS_ValueToInt32(cx, JS_ARGV(cx, vp)[1], &mode) == JS_FALSE)
THROW_ERROR(cx, "Could not convert parameter 2");

// this could be simplified to: mode > FILE_APPEND || mode < FILE_READ
// but then it takes a hit for readability
if(!(mode == FILE_READ || mode == FILE_WRITE || mode == FILE_APPEND))
THROW_ERROR(cx, "Invalid file mode");

bool binary = false, autoflush = false, lockFile = false;
bool binary = false;
bool autoflush = false;
bool lockFile = false;
if(argc > 2 && JSVAL_IS_BOOLEAN(JS_ARGV(cx, vp)[2]))
binary = !!JSVAL_TO_BOOLEAN(JS_ARGV(cx, vp)[2]);
if(argc > 3 && JSVAL_IS_BOOLEAN(JS_ARGV(cx, vp)[3]))
autoflush = !!JSVAL_TO_BOOLEAN(JS_ARGV(cx, vp)[3]);
if(argc > 4 && JSVAL_IS_BOOLEAN(JS_ARGV(cx, vp)[4]))
lockFile = !!JSVAL_TO_BOOLEAN(JS_ARGV(cx, vp)[4]);

// Check that the path looks basically ok, validation is handled later
if(file == NULL ||
file[0] == '\0')
THROW_ERROR(cx, "Invalid file name");

// this could be simplified to: mode > FILE_APPEND || mode < FILE_READ
// but then it takes a hit for readability
switch(mode)
{
// Good modes
case FILE_READ:
case FILE_WRITE:
case FILE_APPEND:
break;
// Bad modes
default:
THROW_ERROR(cx, "Invalid file mode");
}

if(binary)
mode += 3;

static const char* modes[] = {"rt", "w+t", "a+t", "rb", "w+b", "a+b"};
char path[_MAX_FNAME+_MAX_PATH];
sprintf_s(path, sizeof(path), "%s\\%s", Vars.szScriptPath, file);

FILE* fptr = NULL;
fopen_s(&fptr, path, modes[mode]);
if(!fptr) {
JS_ReportError(cx, "Couldn't open file %s: %s", file, _strerror(NULL));
FILE* fptr = fileOpenRelScript(file, modes[mode], cx);

// If fileOpenRelScript failed, it already reported the error
if(fptr == NULL)
return JS_FALSE;
}

FileData* fdata = new FileData;
if(!fdata)
Expand All @@ -211,7 +221,7 @@ JSAPI_FUNC(file_open)
}

fdata->mode = mode;
fdata->path = _strdup(path);
fdata->path = _strdup(file);
fdata->autoflush = autoflush;
fdata->locked = lockFile;
fdata->fptr = fptr;
Expand Down Expand Up @@ -272,10 +282,12 @@ JSAPI_FUNC(file_reopen)
if(fdata)
if(!fdata->fptr) {
static const char* modes[] = {"rt", "w+t", "a+t", "rb", "w+b", "a+b"};
fdata->fptr = NULL;
fopen_s(&fdata->fptr, fdata->path, modes[fdata->mode]);
if(!fdata->fptr)
THROW_ERROR(cx, _strerror("Could not reopen file"));
fdata->fptr = fileOpenRelScript(fdata->path, modes[fdata->mode], cx);

// If fileOpenRelScript failed, it already reported the error
if(fdata->fptr == NULL)
return JS_FALSE;

if(fdata->locked)
{
_lock_file(fdata->fptr);
Expand Down
Loading

0 comments on commit 058a4b8

Please sign in to comment.