From ef6821b03aed2d50816bb29d7c166dc3a010a6d3 Mon Sep 17 00:00:00 2001
From: Peter Wu <lekensteyn@gmail.com>
Date: Sat, 5 Jan 2013 18:28:25 +0100
Subject: [PATCH] Do not require read permissions for try_files/if

---
 src/core/ngx_open_file_cache.c | 60 +++++++++++++++++++++++++++++++++++++++++-
 src/core/ngx_string.h          | 15 +++++++++++
 2 files changed, 74 insertions(+), 1 deletion(-)

diff --git a/src/core/ngx_open_file_cache.c b/src/core/ngx_open_file_cache.c
index c44ac96..d0b6946 100644
--- a/src/core/ngx_open_file_cache.c
+++ b/src/core/ngx_open_file_cache.c
@@ -732,6 +732,8 @@ ngx_file_info_wrapper(ngx_str_t *name, ngx_open_file_info_t *of,
 #else
 
     ngx_fd_t  fd;
+    u_char *p, *end, *sep_pos = NULL;
+    unsigned old_disable_symlinks = of->disable_symlinks;
 
     if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_OFF) {
 
@@ -746,14 +748,70 @@ ngx_file_info_wrapper(ngx_str_t *name, ngx_open_file_info_t *of,
         return rc;
     }
 
+    /*
+     * Find the first non-trailing slash and use that as separator for the
+     * leading directories and the final component name. For example, "/foo"
+     * becomes "/" "foo" and "//foo/" becomes "//" "foo". "/" and "//"
+     * cannot be split.
+     */
+
+    end = name->data + name->len - 1;
+    p = ngx_strlrchr(name->data, end, '/');
+    if (p) {
+        if (p == end) {
+            /* skip all trailing slashes */
+            while (name->data < p && *p == '/') {
+                --p;
+            }
+        }
+
+        if (name->data < p) {
+            sep_pos = ngx_strlrchr(name->data, p, '/');
+        }
+    }
+
+    if (sep_pos != NULL) {
+        size_t len = sep_pos - name->data;
+
+        if (len <= of->disable_symlinks_from) {
+            of->disable_symlinks = NGX_DISABLE_SYMLINKS_OFF;
+        }
+
+        *sep_pos = 0;
+    }
+
     fd = ngx_open_file_wrapper(name, of, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK,
                                NGX_FILE_OPEN, 0, log);
+    if (sep_pos != NULL) {
+        of->disable_symlinks = old_disable_symlinks;
+
+        *sep_pos = '/';
+    }
 
     if (fd == NGX_INVALID_FILE) {
         return NGX_FILE_ERROR;
     }
 
-    rc = ngx_fd_info(fd, fi);
+    if (sep_pos != NULL) {
+        rc = ngx_file_at_info(fd, sep_pos + 1, fi, AT_SYMLINK_NOFOLLOW);
+
+        if (rc != NGX_FILE_ERROR) {
+            if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_NOTOWNER) {
+                ngx_file_info_t realfi;
+                rc = ngx_file_at_info(fd, sep_pos + 1, &realfi, 0);
+
+                if (rc != NGX_FILE_ERROR && fi->st_uid != realfi.st_uid) {
+                    ngx_set_errno(NGX_ELOOP);
+                    rc = NGX_FILE_ERROR;
+                }
+            } else if (ngx_is_link(fi)) {
+                ngx_set_errno(NGX_ELOOP);
+                rc = NGX_FILE_ERROR;
+            }
+        }
+    } else {
+        rc = ngx_fd_info(fd, fi);
+    }
 
     if (rc == NGX_FILE_ERROR) {
         of->err = ngx_errno;
diff --git a/src/core/ngx_string.h b/src/core/ngx_string.h
index 92d246e..646a894 100644
--- a/src/core/ngx_string.h
+++ b/src/core/ngx_string.h
@@ -77,6 +77,21 @@ ngx_strlchr(u_char *p, u_char *last, u_char c)
     return NULL;
 }
 
+static ngx_inline u_char *
+ngx_strlrchr(u_char *first, u_char *last, u_char c)
+{
+    while (first <= last) {
+
+        if (*last == c) {
+            return last;
+        }
+
+        last--;
+    }
+
+    return NULL;
+}
+
 
 /*
  * msvc and icc7 compile memset() to the inline "rep stos"
-- 
1.8.0.3

