diff options
-rw-r--r-- | configure.in | 2 | ||||
-rw-r--r-- | src/base.h | 10 | ||||
-rw-r--r-- | src/configfile-glue.c | 6 | ||||
-rw-r--r-- | src/configfile.c | 15 | ||||
-rw-r--r-- | src/mod_staticfile.c | 13 | ||||
-rw-r--r-- | src/response.c | 20 | ||||
-rw-r--r-- | src/server.c | 17 | ||||
-rw-r--r-- | src/stat_cache.c | 64 | ||||
-rw-r--r-- | tests/lighttpd.conf | 12 |
9 files changed, 144 insertions, 15 deletions
diff --git a/configure.in b/configure.in index acd7c922..1d879ab5 100644 --- a/configure.in +++ b/configure.in @@ -440,7 +440,7 @@ case $host_os in esac AC_CHECK_FUNCS([dup2 getcwd inet_ntoa inet_ntop memset mmap munmap strchr \ - strdup strerror strstr strtol sendfile getopt socket \ + strdup strerror strstr strtol sendfile getopt socket lstat \ gethostbyname poll sigtimedwait epoll_ctl getrlimit chroot \ getuid select signal pathconf madvise posix_fadvise posix_madvise \ writev sigaction sendfile64 send_file kqueue port_create localtime_r]) @@ -86,7 +86,8 @@ typedef enum { T_CONFIG_UNSET, T_CONFIG_BOOLEAN, T_CONFIG_ARRAY, T_CONFIG_LOCAL, - T_CONFIG_DEPRECATED + T_CONFIG_DEPRECATED, + T_CONFIG_UNSUPPORTED } config_values_type_t; typedef enum { T_CONFIG_SCOPE_UNSET, @@ -206,6 +207,10 @@ typedef struct { time_t stat_ts; +#ifdef HAVE_LSTAT + char is_symlink; +#endif + #ifdef HAVE_FAM_H int dir_version; int dir_ndx; @@ -572,7 +577,8 @@ typedef struct server { server_config srvconf; - int config_deprecated; + short int config_deprecated; + short int config_unsupported; connections *conns; connections *joblist; diff --git a/src/configfile-glue.c b/src/configfile-glue.c index 6471686e..d0d9a051 100644 --- a/src/configfile-glue.c +++ b/src/configfile-glue.c @@ -119,6 +119,12 @@ int config_insert_values_internal(server *srv, array *ca, const config_values_t case T_CONFIG_LOCAL: case T_CONFIG_UNSET: break; + case T_CONFIG_UNSUPPORTED: + log_error_write(srv, __FILE__, __LINE__, "ssss", "ERROR: found unsupported key:", cv[i].key, "-", (char *)(cv[i].destination)); + + srv->config_unsupported = 1; + + break; case T_CONFIG_DEPRECATED: log_error_write(srv, __FILE__, __LINE__, "ssss", "ERROR: found deprecated key:", cv[i].key, "-", (char *)(cv[i].destination)); diff --git a/src/configfile.c b/src/configfile.c index 4d9946d3..e5d1610a 100644 --- a/src/configfile.c +++ b/src/configfile.c @@ -54,7 +54,14 @@ static int config_insert(server *srv) { { "server.max-write-idle", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 21 */ { "server.error-handler-404", NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 22 */ { "server.max-fds", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_SERVER }, /* 23 */ +#ifdef HAVE_LSTAT { "server.follow-symlink", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 24 */ +#else + { "server.follow-symlink", + "Your system lacks lstat(). We can not differ symlinks from files." + "Please remove server.follow-symlinks from your config.", + T_CONFIG_UNSUPPORTED, T_CONFIG_SCOPE_UNSET }, /* 24 */ +#endif { "server.kbytes-per-second", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 25 */ { "connection.kbytes-per-second", NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION }, /* 26 */ { "mimetype.use-xattr", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 27 */ @@ -150,7 +157,9 @@ static int config_insert(server *srv) { s->is_ssl = 0; s->ssl_use_sslv2 = 1; s->use_ipv6 = 0; +#ifdef HAVE_LSTAT s->follow_symlink = 1; +#endif s->kbytes_per_second = 0; s->allow_http11 = 1; s->range_requests = 1; @@ -175,7 +184,9 @@ static int config_insert(server *srv) { cv[20].destination = &(s->max_read_idle); cv[21].destination = &(s->max_write_idle); cv[22].destination = s->error_handler; +#ifdef HAVE_LSTAT cv[24].destination = &(s->follow_symlink); +#endif /* 23 -> max-fds */ cv[25].destination = &(s->global_kbytes_per_second); cv[26].destination = &(s->kbytes_per_second); @@ -238,7 +249,9 @@ int config_setup_connection(server *srv, connection *con) { PATCH(use_xattr); PATCH(error_handler); PATCH(errorfile_prefix); +#ifdef HAVE_LSTAT PATCH(follow_symlink); +#endif PATCH(server_tag); PATCH(kbytes_per_second); PATCH(global_kbytes_per_second); @@ -314,8 +327,10 @@ int config_patch_connection(server *srv, connection *con, comp_key_t comp) { PATCH(ssl_cipher_list); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("ssl.engine"))) { PATCH(is_ssl); +#ifdef HAVE_LSTAT } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.follow-symlink"))) { PATCH(follow_symlink); +#endif } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.name"))) { buffer_copy_string_buffer(con->server_name, s->server_name); } else if (buffer_is_equal_string(du->key, CONST_STR_LEN("server.tag"))) { diff --git a/src/mod_staticfile.c b/src/mod_staticfile.c index 6fac65b1..f5308cd7 100644 --- a/src/mod_staticfile.c +++ b/src/mod_staticfile.c @@ -397,6 +397,19 @@ URIHANDLER_FUNC(mod_staticfile_subrequest) { } /* we only handline regular files */ +#ifdef HAVE_LSTAT + if ((sce->is_symlink == 1) && !con->conf.follow_symlink) { + con->http_status = 403; + + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "-- access denied due symlink restriction"); + log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); + } + + buffer_reset(con->physical.path); + return HANDLER_FINISHED; + } +#endif if (!S_ISREG(sce->st.st_mode)) { con->http_status = 404; diff --git a/src/response.c b/src/response.c index 4f23ea9a..606b98f7 100644 --- a/src/response.c +++ b/src/response.c @@ -451,7 +451,7 @@ handler_t http_response_prepare(server *srv, connection *con) { log_error_write(srv, __FILE__, __LINE__, "s", "-- handling physical path"); log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); } - + if (HANDLER_ERROR != stat_cache_get_entry(srv, con, con->physical.path, &sce)) { /* file exists */ @@ -459,7 +459,19 @@ handler_t http_response_prepare(server *srv, connection *con) { log_error_write(srv, __FILE__, __LINE__, "s", "-- file found"); log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); } - +#ifdef HAVE_LSTAT + if ((sce->is_symlink != 0) && !con->conf.follow_symlink) { + con->http_status = 403; + + if (con->conf.log_request_handling) { + log_error_write(srv, __FILE__, __LINE__, "s", "-- access denied due symlink restriction"); + log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path); + } + + buffer_reset(con->physical.path); + return HANDLER_FINISHED; + }; +#endif if (S_ISDIR(sce->st.st_mode)) { if (con->physical.path->ptr[con->physical.path->used - 2] != '/') { /* redirect to .../ */ @@ -468,7 +480,11 @@ handler_t http_response_prepare(server *srv, connection *con) { return HANDLER_FINISHED; } +#ifdef HAVE_LSTAT + } else if (!S_ISREG(sce->st.st_mode) && !sce->is_symlink) { +#else } else if (!S_ISREG(sce->st.st_mode)) { +#endif /* any special handling of non-reg files ?*/ diff --git a/src/server.c b/src/server.c index d4f9eba5..5a29a75b 100644 --- a/src/server.c +++ b/src/server.c @@ -861,22 +861,29 @@ int main (int argc, char **argv) { } } } - + + if (srv->config_unsupported) { + log_error_write(srv, __FILE__, __LINE__, "s", + "Configuration contains unsupported keys. Going down."); + } + if (srv->config_deprecated) { log_error_write(srv, __FILE__, __LINE__, "s", "Configuration contains deprecated keys. Going down."); - + } + + if (srv->config_unsupported || srv->config_deprecated) { plugins_free(srv); network_close(srv); server_free(srv); - + return -1; } - + if (-1 == log_error_open(srv)) { log_error_write(srv, __FILE__, __LINE__, "s", "opening errorlog failed, dying"); - + plugins_free(srv); network_close(srv); server_free(srv); diff --git a/src/stat_cache.c b/src/stat_cache.c index 148f4c83..623e5ea5 100644 --- a/src/stat_cache.c +++ b/src/stat_cache.c @@ -39,7 +39,7 @@ #define lstat stat #endif -#if 0 +#if 1 /* enables debug code for testing if all nodes in the stat-cache as accessable */ #define DEBUG_STAT_CACHE #endif @@ -328,6 +328,20 @@ static int buffer_copy_dirname(buffer *dst, buffer *file) { } #endif +#ifdef HAVE_LSTAT +static int stat_cache_lstat(server *srv, char *dname, struct stat *lst) { + if (lstat(dname, lst) == 0) { + return S_ISLNK(lst->st_mode) ? 0 : 1; + } + else { + log_error_write(srv, __FILE__, __LINE__, "sss", + "lstat failed for:", + dname, strerror(errno)); + }; + return -1; +} +#endif + /*** * * @@ -450,10 +464,9 @@ handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_ /* * *lol* * - open() + fstat() on a named-pipe results in a (intended) hang. - * - stat() if regualar file + open() to see if we can read from it is better + * - stat() if regular file + open() to see if we can read from it is better * * */ - if (-1 == stat(name->ptr, &st)) { return HANDLER_ERROR; } @@ -509,11 +522,52 @@ handler_t stat_cache_get_entry(server *srv, connection *con, buffer *name, stat_ * and keeping the file open for the rest of the time. But this can * only be done at network level. * ++ * per default it is not a symlink * */ - if (S_ISLNK(st.st_mode) && !con->conf.follow_symlink) { - return HANDLER_ERROR; +#ifdef HAVE_LSTAT + sce->is_symlink = 0; + struct stat lst; + if (stat_cache_lstat(srv, name->ptr, &lst) == 0) { +#ifdef DEBUG_STAT_CACHE + log_error_write(srv, __FILE__, __LINE__, "sb", + "found symlink", name); +#endif + sce->is_symlink = 1; } + /* + * we assume "/" can not be symlink, so + * skip the symlink stuff if our path is / + **/ + else if ((name->used > 2)) { + char *dname, *s_cur; + + dname = strndup(name->ptr, name->used); + while ((s_cur = strrchr(dname,'/'))) { + *s_cur = '\0'; + if (dname == s_cur) { +#ifdef DEBUG_STAT_CACHE + log_error_write(srv, __FILE__, __LINE__, "s", "reached /"); +#endif + break; + } +#ifdef DEBUG_STAT_CACHE + log_error_write(srv, __FILE__, __LINE__, "sss", + "checking if", dname, "is a symlink"); +#endif + if (stat_cache_lstat(srv, dname, &lst) == 0) { + sce->is_symlink = 1; +#ifdef DEBUG_STAT_CACHE + log_error_write(srv, __FILE__, __LINE__, "ss", + "found symlink", dname); +#endif + break; + }; + }; + free(dname); + }; +#endif + if (S_ISREG(st.st_mode)) { /* determine mimetype */ buffer_reset(sce->content_type); diff --git a/tests/lighttpd.conf b/tests/lighttpd.conf index 79e48c91..1d08bb9b 100644 --- a/tests/lighttpd.conf +++ b/tests/lighttpd.conf @@ -147,6 +147,18 @@ $HTTP["host"] == "zzz.example.org" { server.name = "zzz.example.org" } +$HTTP["host"] == "symlink.example.org" { + server.document-root = env.SRCDIR + "/tmp/lighttpd/servers/www.example.org/pages/" + server.name = "symlink.example.org" + server.follow-symlink = "enable" +} + +$HTTP["host"] == "nosymlink.example.org" { + server.document-root = env.SRCDIR + "/tmp/lighttpd/servers/www.example.org/pages/" + server.name = "symlink.example.org" + server.follow-symlink = "disable" +} + $HTTP["host"] == "no-simple.example.org" { server.document-root = "@SRCDIR@/tmp/lighttpd/servers/123.example.org/pages/" server.name = "zzz.example.org" |