From 32b000bad7df8f0303d74a5c02fbd62e9241d17b Mon Sep 17 00:00:00 2001 From: Maxim Dounin Date: Wed, 15 Feb 2012 12:18:55 +0000 Subject: Disable symlinks: fixed edge cases of path handling. This includes non-absolute pathnames, multiple slashes and trailing slashes. In collaboration with Valentin Bartenev. --- src/core/ngx_open_file_cache.c | 74 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 60 insertions(+), 14 deletions(-) diff --git a/src/core/ngx_open_file_cache.c b/src/core/ngx_open_file_cache.c index 4b3d05f41..c871f74b1 100644 --- 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, ngx_open_file_info_t *of, #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, ngx_open_file_info_t *of, return fd; } - at_fd = ngx_openat_file(AT_FDCWD, "/", NGX_FILE_RDONLY|NGX_FILE_NONBLOCK, - NGX_FILE_OPEN, 0); - - if (at_fd == NGX_INVALID_FILE) { - of->err = ngx_errno; - of->failed = ngx_openat_file_n; - return NGX_INVALID_FILE; - } + p = name->data; + end = p + name->len; + at_fd = AT_FDCWD; at_name = *name; - at_name.len = 1; - end = name->data + name->len; - p = name->data + 1; + if (p[0] == '/') { + at_fd = ngx_openat_file(at_fd, "/", + NGX_FILE_RDONLY|NGX_FILE_NONBLOCK, + NGX_FILE_OPEN, 0); + + if (at_fd == NGX_FILE_ERROR) { + of->err = ngx_errno; + of->failed = ngx_openat_file_n; + return NGX_FILE_ERROR; + } + + at_name.len = 1; + p++; + } for ( ;; ) { cp = ngx_strlchr(p, end, '/'); @@ -599,6 +606,11 @@ ngx_open_file_wrapper(ngx_str_t *name, ngx_open_file_info_t *of, 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, ngx_open_file_info_t *of, 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); -- cgit v1.2.3