[nginx] svn commit: r4674 - trunk/src/os/win32

mdounin at mdounin.ru mdounin at mdounin.ru
Tue Jun 5 13:36:09 UTC 2012


Author: mdounin
Date: 2012-06-05 13:36:09 +0000 (Tue, 05 Jun 2012)
New Revision: 4674
URL: http://trac.nginx.org/nginx/changeset/4674/nginx

Log:
Win32: disallowed access to various non-canonical name variants.

This includes trailings dots and spaces, NTFS streams (and short names, as
previously checked).  The checks are now also done in ngx_file_info(), thus
allowing to use the "try_files" directive to protect external scripts.


Modified:
   trunk/src/os/win32/ngx_files.c

Modified: trunk/src/os/win32/ngx_files.c
===================================================================
--- trunk/src/os/win32/ngx_files.c	2012-06-05 13:17:05 UTC (rev 4673)
+++ trunk/src/os/win32/ngx_files.c	2012-06-05 13:36:09 UTC (rev 4674)
@@ -11,6 +11,8 @@
 
 #define NGX_UTF16_BUFLEN  256
 
+static ngx_int_t ngx_win32_check_filename(u_char *name, u_short *u,
+    size_t len);
 static u_short *ngx_utf8_to_utf16(u_short *utf16, u_char *utf8, size_t *len);
 
 
@@ -20,8 +22,7 @@
 ngx_open_file(u_char *name, u_long mode, u_long create, u_long access)
 {
     size_t      len;
-    u_long      n;
-    u_short    *u, *lu;
+    u_short    *u;
     ngx_fd_t    fd;
     ngx_err_t   err;
     u_short     utf16[NGX_UTF16_BUFLEN];
@@ -34,25 +35,11 @@
     }
 
     fd = INVALID_HANDLE_VALUE;
-    lu = NULL;
 
-    if (create == NGX_FILE_OPEN) {
-
-        lu = malloc(len * 2);
-        if (lu == NULL) {
-            goto failed;
-        }
-
-        n = GetLongPathNameW(u, lu, len);
-
-        if (n == 0) {
-            goto failed;
-        }
-
-        if (n != len - 1 || _wcsicmp(u, lu) != 0) {
-            ngx_set_errno(NGX_ENOENT);
-            goto failed;
-        }
+    if (create == NGX_FILE_OPEN
+        && ngx_win32_check_filename(name, u, len) != NGX_OK)
+    {
+        goto failed;
     }
 
     fd = CreateFileW(u, mode,
@@ -61,18 +48,12 @@
 
 failed:
 
-    err = ngx_errno;
-
-    if (lu) {
-        ngx_free(lu);
-    }
-
     if (u != utf16) {
+        err = ngx_errno;
         ngx_free(u);
+        ngx_set_errno(err);
     }
 
-    ngx_set_errno(err);
-
     return fd;
 }
 
@@ -294,14 +275,14 @@
         return NGX_FILE_ERROR;
     }
 
-    rc = GetFileAttributesExW(u, GetFileExInfoStandard, &fa);
+    rc = NGX_FILE_ERROR;
 
-    if (u != utf16) {
-        err = ngx_errno;
-        ngx_free(u);
-        ngx_set_errno(err);
+    if (ngx_win32_check_filename(file, u, len) != NGX_OK) {
+        goto failed;
     }
 
+    rc = GetFileAttributesExW(u, GetFileExInfoStandard, &fa);
+
     sb->dwFileAttributes = fa.dwFileAttributes;
     sb->ftCreationTime = fa.ftCreationTime;
     sb->ftLastAccessTime = fa.ftLastAccessTime;
@@ -309,6 +290,14 @@
     sb->nFileSizeHigh = fa.nFileSizeHigh;
     sb->nFileSizeLow = fa.nFileSizeLow;
 
+failed:
+
+    if (u != utf16) {
+        err = ngx_errno;
+        ngx_free(u);
+        ngx_set_errno(err);
+    }
+
     return rc;
 }
 
@@ -640,6 +629,148 @@
 }
 
 
+static ngx_int_t
+ngx_win32_check_filename(u_char *name, u_short *u, size_t len)
+{
+    u_char     *p, ch;
+    u_long      n;
+    u_short    *lu;
+    ngx_err_t   err;
+    enum {
+        sw_start = 0,
+        sw_normal,
+        sw_after_slash,
+        sw_after_colon,
+        sw_after_dot
+    } state;
+
+    /* check for NTFS streams (":"), trailing dots and spaces */
+
+    lu = NULL;
+    state = sw_start;
+
+    for (p = name; *p; p++) {
+        ch = *p;
+
+        switch (state) {
+
+        case sw_start:
+
+            /*
+             * skip till first "/" to allow paths starting with drive and
+             * relative path, like "c:html/"
+             */
+
+            if (ch == '/' || ch == '\\') {
+                state = sw_after_slash;
+            }
+
+            break;
+
+        case sw_normal:
+
+            if (ch == ':') {
+                state = sw_after_colon;
+                break;
+            }
+
+            if (ch == '.' || ch == ' ') {
+                state = sw_after_dot;
+                break;
+            }
+
+            if (ch == '/' || ch == '\\') {
+                state = sw_after_slash;
+                break;
+            }
+
+            break;
+
+        case sw_after_slash:
+
+            if (ch == '/' || ch == '\\') {
+                break;
+            }
+
+            if (ch == '.') {
+                break;
+            }
+
+            if (ch == ':') {
+                state = sw_after_colon;
+                break;
+            }
+
+            state = sw_normal;
+            break;
+
+        case sw_after_colon:
+
+            if (ch == '/' || ch == '\\') {
+                state = sw_after_slash;
+                break;
+            }
+
+            goto invalid;
+
+        case sw_after_dot:
+
+            if (ch == '/' || ch == '\\') {
+                goto invalid;
+            }
+
+            if (ch == ':') {
+                goto invalid;
+            }
+       
+            if (ch == '.' || ch == ' ') {
+                break;
+            }
+
+            state = sw_normal;
+            break;
+        }
+    }
+
+    if (state == sw_after_dot) {
+        goto invalid;
+    }
+
+    /* check if long name match */
+
+    lu = malloc(len * 2);
+    if (lu == NULL) {
+        return NGX_ERROR;
+    }
+
+    n = GetLongPathNameW(u, lu, len);
+
+    if (n == 0) {
+        goto failed;
+    }
+
+    if (n != len - 1 || _wcsicmp(u, lu) != 0) {
+        goto invalid;
+    }
+
+    return NGX_OK;
+
+invalid:
+
+    ngx_set_errno(NGX_ENOENT);
+
+failed:
+
+    if (lu) {
+        err = ngx_errno;
+        ngx_free(lu);
+        ngx_set_errno(err);
+    }
+
+    return NGX_ERROR;
+}
+
+
 static u_short *
 ngx_utf8_to_utf16(u_short *utf16, u_char *utf8, size_t *len)
 {



More information about the nginx-devel mailing list