From 37bd124ae4cd22c9dd7f2ee36d4bf787256d2af9 Mon Sep 17 00:00:00 2001 From: Glenn Strauss Date: Sun, 10 Mar 2019 23:06:49 -0400 Subject: [PATCH] [core] pass conf.follow_symlink in more places --- src/chunk.c | 3 ++- src/fdevent.c | 47 +++++++++++++++++++++++++++++++++-------------- src/fdevent.h | 4 ++-- src/gw_backend.c | 2 +- src/http_chunk.c | 2 +- src/mod_cgi.c | 4 ++-- src/mod_cml_lua.c | 2 +- src/mod_deflate.c | 2 +- src/mod_webdav.c | 4 ++-- src/rand.c | 2 +- src/server.c | 4 ++-- src/stat_cache.c | 22 ++-------------------- src/stat_cache.h | 2 +- 13 files changed, 51 insertions(+), 49 deletions(-) diff --git a/src/chunk.c b/src/chunk.c index 573e6bfa..c61afebf 100644 --- a/src/chunk.c +++ b/src/chunk.c @@ -807,7 +807,8 @@ int chunkqueue_open_file_chunk(server *srv, chunkqueue *cq) { toSend = c->file.length - c->offset; if (-1 == c->file.fd) { - if (-1 == (c->file.fd = fdevent_open_cloexec(c->mem->ptr, O_RDONLY, 0))) { + /* (permit symlinks; should already have been checked. However, TOC-TOU remains) */ + if (-1 == (c->file.fd = fdevent_open_cloexec(c->mem->ptr, 1, O_RDONLY, 0))) { log_error_write(srv, __FILE__, __LINE__, "ssb", "open failed:", strerror(errno), c->mem); return -1; } diff --git a/src/fdevent.c b/src/fdevent.c index 6f7d3577..bd19ec68 100644 --- a/src/fdevent.c +++ b/src/fdevent.c @@ -484,15 +484,38 @@ int fdevent_socket_nb_cloexec(int domain, int type, int protocol) { return fd; } +#ifndef O_BINARY +#define O_BINARY 0 +#endif +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif #ifndef O_NOCTTY #define O_NOCTTY 0 #endif +#ifndef O_NOFOLLOW +#define O_NOFOLLOW 0 +#endif + +/*(O_NOFOLLOW is not handled here)*/ +/*(Note: O_NOFOLLOW affects only the final path segment, the target file, + * not any intermediate symlinks along the path)*/ -int fdevent_open_cloexec(const char *pathname, int flags, mode_t mode) { +/* O_CLOEXEC handled further below, if defined) */ +#ifdef O_NONBLOCK +#define FDEVENT_O_FLAGS \ + (O_BINARY | O_LARGEFILE | O_NOCTTY | O_NONBLOCK) +#else +#define FDEVENT_O_FLAGS \ + (O_BINARY | O_LARGEFILE | O_NOCTTY ) +#endif + +int fdevent_open_cloexec(const char *pathname, int symlinks, int flags, mode_t mode) { + if (!symlinks) flags |= O_NOFOLLOW; #ifdef O_CLOEXEC - return open(pathname, flags | O_CLOEXEC | O_NOCTTY, mode); + return open(pathname, flags | O_CLOEXEC | FDEVENT_O_FLAGS, mode); #else - int fd = open(pathname, flags | O_NOCTTY, mode); + int fd = open(pathname, flags | FDEVENT_O_FLAGS, mode); #ifdef FD_CLOEXEC if (fd != -1) force_assert(-1 != fcntl(fd, F_SETFD, FD_CLOEXEC)); @@ -504,14 +527,14 @@ int fdevent_open_cloexec(const char *pathname, int flags, mode_t mode) { int fdevent_open_devnull(void) { #if defined(_WIN32) - return fdevent_open_cloexec("nul", O_RDWR, 0); + return fdevent_open_cloexec("nul", 0, O_RDWR, 0); #else - return fdevent_open_cloexec("/dev/null", O_RDWR, 0); + return fdevent_open_cloexec("/dev/null", 0, O_RDWR, 0); #endif } -int fdevent_open_dirname(char *path) { +int fdevent_open_dirname(char *path, int symlinks) { /*(handle special cases of no dirname or dirname is root directory)*/ char * const c = strrchr(path, '/'); const char * const dname = (NULL != c ? c == path ? "/" : path : "."); @@ -521,7 +544,7 @@ int fdevent_open_dirname(char *path) { flags |= O_DIRECTORY; #endif if (NULL != c) *c = '\0'; - dfd = fdevent_open_cloexec(dname, flags, 0); + dfd = fdevent_open_cloexec(dname, symlinks, flags, 0); if (NULL != c) *c = '/'; return dfd; } @@ -861,14 +884,10 @@ static int fdevent_open_logger_pipe(const char *logger) { } -#ifndef O_LARGEFILE -#define O_LARGEFILE 0 -#endif - int fdevent_open_logger(const char *logger) { - if (logger[0] != '|') { - int flags = O_APPEND | O_WRONLY | O_CREAT | O_LARGEFILE; - return fdevent_open_cloexec(logger, flags, 0644); + if (logger[0] != '|') { /*(permit symlinks)*/ + int flags = O_APPEND | O_WRONLY | O_CREAT; + return fdevent_open_cloexec(logger, 1, flags, 0644); } else { return fdevent_open_logger_pipe(logger+1); /*(skip the '|')*/ diff --git a/src/fdevent.h b/src/fdevent.h index 9f27b023..ba5db631 100644 --- a/src/fdevent.h +++ b/src/fdevent.h @@ -79,14 +79,14 @@ int fdevent_fcntl_set_nb_cloexec(fdevents *ev, int fd); int fdevent_fcntl_set_nb_cloexec_sock(fdevents *ev, int fd); int fdevent_socket_cloexec(int domain, int type, int protocol); int fdevent_socket_nb_cloexec(int domain, int type, int protocol); -int fdevent_open_cloexec(const char *pathname, int flags, mode_t mode); +int fdevent_open_cloexec(const char *pathname, int symlinks, int flags, mode_t mode); struct sockaddr; int fdevent_accept_listenfd(int listenfd, struct sockaddr *addr, size_t *addrlen); char ** fdevent_environ(void); int fdevent_open_devnull(void); -int fdevent_open_dirname(char *path); +int fdevent_open_dirname(char *path, int symlinks); int fdevent_set_stdin_stdout_stderr(int fdin, int fdout, int fderr); pid_t fdevent_fork_execve(const char *name, char *argv[], char *envp[], int fdin, int fdout, int fderr, int dfd); int fdevent_open_logger(const char *logger); diff --git a/src/gw_backend.c b/src/gw_backend.c index 5d99e05e..b7976ec5 100644 --- a/src/gw_backend.c +++ b/src/gw_backend.c @@ -562,7 +562,7 @@ static int gw_spawn_connection(server *srv, gw_host *host, gw_proc *proc, int de env.ptr[env.used] = NULL; } - dfd = fdevent_open_dirname(host->args.ptr[0]); + dfd = fdevent_open_dirname(host->args.ptr[0], 1); /* permit symlinks */ if (-1 == dfd) { log_error_write(srv, __FILE__, __LINE__, "sss", "open dirname failed:", strerror(errno), diff --git a/src/http_chunk.c b/src/http_chunk.c index 1bdf2fa8..54ae154f 100644 --- a/src/http_chunk.c +++ b/src/http_chunk.c @@ -41,7 +41,7 @@ static int http_chunk_append_file_open_fstat(server *srv, connection *con, buffe if (HANDLER_ERROR == stat_cache_get_entry(srv, con, fn, &sce)) return -1; } - return stat_cache_open_rdonly_fstat(srv, con, fn, st); + return stat_cache_open_rdonly_fstat(fn, st, con->conf.follow_symlink); } static void http_chunk_append_file_fd_range(server *srv, connection *con, buffer *fn, int fd, off_t offset, off_t len) { diff --git a/src/mod_cgi.c b/src/mod_cgi.c index 0b06b96b..96a56f8e 100644 --- a/src/mod_cgi.c +++ b/src/mod_cgi.c @@ -551,7 +551,7 @@ static ssize_t cgi_write_file_chunk_mmap(server *srv, connection *con, int fd, c /*(simplified from chunk.c:chunkqueue_open_file_chunk())*/ UNUSED(con); if (-1 == c->file.fd) { - if (-1 == (c->file.fd = fdevent_open_cloexec(c->mem->ptr, O_RDONLY, 0))) { + if (-1 == (c->file.fd = fdevent_open_cloexec(c->mem->ptr, con->conf.follow_symlink, O_RDONLY, 0))) { log_error_write(srv, __FILE__, __LINE__, "ssb", "open failed:", strerror(errno), c->mem); return -1; } @@ -804,7 +804,7 @@ static int cgi_create_env(server *srv, connection *con, plugin_data *p, handler_ args[i ] = NULL; } - dfd = fdevent_open_dirname(con->physical.path->ptr); + dfd = fdevent_open_dirname(con->physical.path->ptr, con->conf.follow_symlink); if (-1 == dfd) { log_error_write(srv, __FILE__, __LINE__, "ssb", "open dirname failed:", strerror(errno), con->physical.path); } diff --git a/src/mod_cml_lua.c b/src/mod_cml_lua.c index 41b55436..926f3ff6 100644 --- a/src/mod_cml_lua.c +++ b/src/mod_cml_lua.c @@ -236,7 +236,7 @@ int cache_parse_lua(server *srv, connection *con, plugin_data *p, buffer *fn) { buffer_copy_string(b, lua_tostring(L, -1)); } - fd = stat_cache_open_rdonly_fstat(srv, con, b, &st); + fd = stat_cache_open_rdonly_fstat(b, &st, con->conf.follow_symlink); if (fd < 0) { /* stat failed */ diff --git a/src/mod_deflate.c b/src/mod_deflate.c index 03324e9b..690b2feb 100644 --- a/src/mod_deflate.c +++ b/src/mod_deflate.c @@ -732,7 +732,7 @@ static int mod_deflate_file_chunk(server *srv, connection *con, handler_ctx *hct #endif if (-1 == c->file.fd) { /* open the file if not already open */ - if (-1 == (c->file.fd = fdevent_open_cloexec(c->mem->ptr, O_RDONLY, 0))) { + if (-1 == (c->file.fd = fdevent_open_cloexec(c->mem->ptr, con->conf.follow_symlink, O_RDONLY, 0))) { log_error_write(srv, __FILE__, __LINE__, "sbs", "open failed for:", c->mem, strerror(errno)); return -1; diff --git a/src/mod_webdav.c b/src/mod_webdav.c index 990560da..fb69236a 100644 --- a/src/mod_webdav.c +++ b/src/mod_webdav.c @@ -1128,7 +1128,7 @@ static int webdav_parse_chunkqueue(server *srv, connection *con, handler_ctx *hc data = c->file.mmap.start + c->offset; } else { if (-1 == c->file.fd && /* open the file if not already open */ - -1 == (c->file.fd = fdevent_open_cloexec(c->mem->ptr, O_RDONLY, 0))) { + -1 == (c->file.fd = fdevent_open_cloexec(c->mem->ptr, con->conf.follow_symlink, O_RDONLY, 0))) { log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno)); return -1; @@ -1838,7 +1838,7 @@ static handler_t mod_webdav_put(server *srv, connection *con, plugin_data *p, ha data = c->file.mmap.start + c->offset; } else { if (-1 == c->file.fd && /* open the file if not already open */ - -1 == (c->file.fd = fdevent_open_cloexec(c->mem->ptr, O_RDONLY, 0))) { + -1 == (c->file.fd = fdevent_open_cloexec(c->mem->ptr, con->conf.follow_symlink, O_RDONLY, 0))) { log_error_write(srv, __FILE__, __LINE__, "ss", "open failed: ", strerror(errno)); close(fd); return HANDLER_ERROR; diff --git a/src/rand.c b/src/rand.c index af24d46d..12c6b191 100644 --- a/src/rand.c +++ b/src/rand.c @@ -106,7 +106,7 @@ static int li_rand_device_bytes (unsigned char *buf, int num) for (unsigned int u = 0; u < sizeof(devices)/sizeof(devices[0]); ++u) { /*(some systems might have symlink to another device; omit O_NOFOLLOW)*/ - int fd = fdevent_open_cloexec(devices[u], O_RDONLY, 0); + int fd = fdevent_open_cloexec(devices[u], 0, O_RDONLY, 0); if (fd >= 0) { ssize_t rd = 0; #ifdef RNDGETENTCNT diff --git a/src/server.c b/src/server.c index a182a539..0f872382 100644 --- a/src/server.c +++ b/src/server.c @@ -1266,7 +1266,7 @@ static int server_main (server * const srv, int argc, char **argv) { /* open pid file BEFORE chroot */ if (-2 == pid_fd) pid_fd = -1; /*(initial startup state)*/ if (-1 == pid_fd && !buffer_string_is_empty(srv->srvconf.pid_file)) { - if (-1 == (pid_fd = fdevent_open_cloexec(srv->srvconf.pid_file->ptr, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) { + if (-1 == (pid_fd = fdevent_open_cloexec(srv->srvconf.pid_file->ptr, 0, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) { struct stat st; if (errno != EEXIST) { log_error_write(srv, __FILE__, __LINE__, "sbs", @@ -1285,7 +1285,7 @@ static int server_main (server * const srv, int argc, char **argv) { return -1; } - if (-1 == (pid_fd = fdevent_open_cloexec(srv->srvconf.pid_file->ptr, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) { + if (-1 == (pid_fd = fdevent_open_cloexec(srv->srvconf.pid_file->ptr, 0, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))) { log_error_write(srv, __FILE__, __LINE__, "sbs", "opening pid-file failed:", srv->srvconf.pid_file, strerror(errno)); return -1; diff --git a/src/stat_cache.c b/src/stat_cache.c index 90b90563..1eb7a81e 100644 --- a/src/stat_cache.c +++ b/src/stat_cache.c @@ -770,27 +770,10 @@ handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_ return HANDLER_GO_ON; } -int stat_cache_open_rdonly_fstat (server *srv, connection *con, buffer *name, struct stat *st) { +int stat_cache_open_rdonly_fstat (buffer *name, struct stat *st, int symlinks) { /*(Note: O_NOFOLLOW affects only the final path segment, the target file, * not any intermediate symlinks along the path)*/ - #ifndef O_BINARY - #define O_BINARY 0 - #endif - #ifndef O_LARGEFILE - #define O_LARGEFILE 0 - #endif - #ifndef O_NOCTTY - #define O_NOCTTY 0 - #endif - #ifndef O_NONBLOCK - #define O_NONBLOCK 0 - #endif - #ifndef O_NOFOLLOW - #define O_NOFOLLOW 0 - #endif - const int oflags = O_BINARY | O_LARGEFILE | O_NOCTTY | O_NONBLOCK - | (con->conf.follow_symlink ? 0 : O_NOFOLLOW); - const int fd = fdevent_open_cloexec(name->ptr, O_RDONLY | oflags, 0); + const int fd = fdevent_open_cloexec(name->ptr, symlinks, O_RDONLY, 0); if (fd >= 0) { if (0 == fstat(fd, st)) { return fd; @@ -798,7 +781,6 @@ int stat_cache_open_rdonly_fstat (server *srv, connection *con, buffer *name, st close(fd); } } - UNUSED(srv); /*(might log_error_write(srv, ...) in the future)*/ return -1; } diff --git a/src/stat_cache.h b/src/stat_cache.h index efbe30ea..fb2dc01d 100644 --- a/src/stat_cache.h +++ b/src/stat_cache.h @@ -44,7 +44,7 @@ const buffer * stat_cache_mimetype_by_ext(const connection *con, const char *nam const buffer * stat_cache_content_type_get(server *srv, connection *con, const buffer *name, stat_cache_entry *sce); const buffer * stat_cache_etag_get(stat_cache_entry *sce, etag_flags_t flags); handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_cache_entry **sce); -int stat_cache_open_rdonly_fstat (server *srv, connection *con, buffer *name, struct stat *st); +int stat_cache_open_rdonly_fstat (buffer *name, struct stat *st, int symlinks); int stat_cache_trigger_cleanup(server *srv); #endif