Commit eecf24b2 authored by Sergey Lyubka's avatar Sergey Lyubka

Improved CGI disclosure protection

parent 0470e813
...@@ -1032,31 +1032,14 @@ static void to_unicode(const char *path, wchar_t *wbuf, size_t wbuf_len) { ...@@ -1032,31 +1032,14 @@ static void to_unicode(const char *path, wchar_t *wbuf, size_t wbuf_len) {
// Point p to the end of the file name // Point p to the end of the file name
p = buf + strlen(buf) - 1; p = buf + strlen(buf) - 1;
// Trim trailing backslash character // Convert to Unicode and back. If doubly-converted string does not
while (p > buf && *p == '\\' && p[-1] != ':') { // match the original, something is fishy, reject.
*p-- = '\0'; memset(wbuf, 0, wbuf_len * sizeof(wchar_t));
} MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len);
WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2),
// Protect from CGI code disclosure. NULL, NULL);
// This is very nasty hole. Windows happily opens files with if (strcmp(buf, buf2) != 0) {
// some garbage in the end of file name. So fopen("a.cgi ", "r")
// actually opens "a.cgi", and does not return an error!
if (*p == 0x20 || // No space at the end
(*p == 0x2e && p > buf) || // No '.' but allow '.' as full path
*p == 0x2b || // No '+'
(*p & ~0x7f)) { // And generally no non-ASCII chars
(void) fprintf(stderr, "Rejecting suspicious path: [%s]", buf);
wbuf[0] = L'\0'; wbuf[0] = L'\0';
} else {
// Convert to Unicode and back. If doubly-converted string does not
// match the original, something is fishy, reject.
memset(wbuf, 0, wbuf_len * sizeof(wchar_t));
MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, (int) wbuf_len);
WideCharToMultiByte(CP_UTF8, 0, wbuf, (int) wbuf_len, buf2, sizeof(buf2),
NULL, NULL);
if (strcmp(buf, buf2) != 0) {
wbuf[0] = L'\0';
}
} }
} }
...@@ -1126,6 +1109,16 @@ static int mg_rename(const char* oldname, const char* newname) { ...@@ -1126,6 +1109,16 @@ static int mg_rename(const char* oldname, const char* newname) {
return MoveFileW(woldbuf, wnewbuf) ? 0 : -1; return MoveFileW(woldbuf, wnewbuf) ? 0 : -1;
} }
// Windows happily opens files with some garbage at the end of file name.
// For example, fopen("a.cgi ", "r") on Windows successfully opens
// "a.cgi", despite one would expect an error back.
// This function returns non-0 if path ends with some garbage.
static int path_cannot_disclose_cgi(const char *path) {
static const char *allowed_last_characters = "_-";
int last = path[strlen(path) - 1];
return isalnum(last) || strchr(allowed_last_characters, last) != NULL;
}
static int mg_stat(struct mg_connection *conn, const char *path, static int mg_stat(struct mg_connection *conn, const char *path,
struct file *filep) { struct file *filep) {
wchar_t wbuf[PATH_MAX]; wchar_t wbuf[PATH_MAX];
...@@ -1139,6 +1132,12 @@ static int mg_stat(struct mg_connection *conn, const char *path, ...@@ -1139,6 +1132,12 @@ static int mg_stat(struct mg_connection *conn, const char *path,
info.ftLastWriteTime.dwLowDateTime, info.ftLastWriteTime.dwLowDateTime,
info.ftLastWriteTime.dwHighDateTime); info.ftLastWriteTime.dwHighDateTime);
filep->is_directory = info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; filep->is_directory = info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
// If file name is fishy, reset the file structure and return error.
// Note it is important to reset, not just return the error, cause
// functions like is_file_opened() check the struct.
if (!filep->is_directory && !path_cannot_disclose_cgi(path)) {
memset(filep, 0, sizeof(*filep));
}
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment