changeset 4480:5a3cb84545e5

Disable symlinks: fixed edge cases of path handling. This includes non-absolute pathnames, multiple slashes and trailing slashes. In collaboration with Valentin Bartenev.
author Maxim Dounin <mdounin@mdounin.ru>
date Wed, 15 Feb 2012 12:18:55 +0000
parents 5e6436812c9a
children 2397e9c72f1b
files src/core/ngx_open_file_cache.c
diffstat 1 files changed, 60 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/src/core/ngx_open_file_cache.c
+++ b/src/core/ngx_open_file_cache.c
@@ -562,9 +562,10 @@ ngx_open_file_wrapper(ngx_str_t *name, n
 
 #else
 
-    u_char     *p, *cp, *end;
-    ngx_fd_t    at_fd;
-    ngx_str_t   at_name;
+    u_char           *p, *cp, *end;
+    ngx_fd_t          at_fd;
+    ngx_str_t         at_name;
+    ngx_file_info_t   fi;
 
     if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_OFF) {
         fd = ngx_open_file(name->data, mode, create, access);
@@ -578,20 +579,26 @@ ngx_open_file_wrapper(ngx_str_t *name, n
         return fd;
     }
 
-    at_fd = ngx_openat_file(AT_FDCWD, "/", NGX_FILE_RDONLY|NGX_FILE_NONBLOCK,
-                            NGX_FILE_OPEN, 0);
+    p = name->data;
+    end = p + name->len;
+
+    at_fd = AT_FDCWD;
+    at_name = *name;
 
-    if (at_fd == NGX_INVALID_FILE) {
-        of->err = ngx_errno;
-        of->failed = ngx_openat_file_n;
-        return NGX_INVALID_FILE;
-    }
+    if (p[0] == '/') {
+        at_fd = ngx_openat_file(at_fd, "/",
+                                NGX_FILE_RDONLY|NGX_FILE_NONBLOCK,
+                                NGX_FILE_OPEN, 0);
 
-    at_name = *name;
-    at_name.len = 1;
+        if (at_fd == NGX_FILE_ERROR) {
+            of->err = ngx_errno;
+            of->failed = ngx_openat_file_n;
+            return NGX_FILE_ERROR;
+        }
 
-    end = name->data + name->len;
-    p = name->data + 1;
+        at_name.len = 1;
+        p++;
+    }
 
     for ( ;; ) {
         cp = ngx_strlchr(p, end, '/');
@@ -599,6 +606,11 @@ ngx_open_file_wrapper(ngx_str_t *name, n
             break;
         }
 
+        if (cp == p) {
+            p++;
+            continue;
+        }
+
         *cp = '\0';
 
         if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_NOTOWNER) {
@@ -630,6 +642,40 @@ ngx_open_file_wrapper(ngx_str_t *name, n
         at_name.len = cp - at_name.data;
     }
 
+    if (p == end && at_fd != AT_FDCWD) {
+
+        /*
+         * If pathname ends with a trailing slash, check if last path
+         * component is a directory; if not, fail with ENOTDIR as per
+         * POSIX.
+         *
+         * We use separate check instead of O_DIRECTORY in the loop above,
+         * as O_DIRECTORY doesn't work on FreeBSD 8.
+         *
+         * Note this returns already opened file descriptor, with different
+         * mode/create/access.  This is believed to be safe as we don't
+         * use this codepath to create directories.
+         */
+
+        if (ngx_fd_info(at_fd, &fi) == NGX_FILE_ERROR) {
+            of->err = ngx_errno;
+            of->failed = ngx_fd_info_n;
+            fd = NGX_INVALID_FILE;
+
+            goto failed;
+        }
+
+        if (ngx_is_dir(&fi)) {
+            return at_fd;
+        }
+
+        of->err = ENOTDIR;
+        of->failed = ngx_openat_file_n;
+        fd = NGX_INVALID_FILE;
+
+        goto failed;
+    }
+
     if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_NOTOWNER) {
         fd = ngx_openat_file_owner(at_fd, p, mode, create, access, log);