瀏覽代碼

Improved CGI disclosure protection

Sergey Lyubka 12 年之前
父節點
當前提交
eecf24b20c
共有 1 個文件被更改,包括 23 次插入24 次删除
  1. 23 24
      mongoose.c

+ 23 - 24
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));
+      }
     }
   }