From eecf24b20cdb103007b76d1ec8a7667205fae429 Mon Sep 17 00:00:00 2001 From: Sergey Lyubka Date: Wed, 9 Jan 2013 14:24:50 +0000 Subject: [PATCH] Improved CGI disclosure protection --- mongoose.c | 47 +++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/mongoose.c b/mongoose.c index e883ba09..881725af 100644 --- a/mongoose.c +++ b/mongoose.c @@ -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 p = buf + strlen(buf) - 1; - // Trim trailing backslash character - while (p > buf && *p == '\\' && p[-1] != ':') { - *p-- = '\0'; - } - - // Protect from CGI code disclosure. - // This is very nasty hole. Windows happily opens files with - // 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); + // 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'; - } 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) { 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, struct file *filep) { wchar_t wbuf[PATH_MAX]; @@ -1139,6 +1132,12 @@ static int mg_stat(struct mg_connection *conn, const char *path, info.ftLastWriteTime.dwLowDateTime, info.ftLastWriteTime.dwHighDateTime); 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)); + } } }