summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.in2
-rw-r--r--src/base.h10
-rw-r--r--src/configfile-glue.c6
-rw-r--r--src/configfile.c15
-rw-r--r--src/mod_staticfile.c13
-rw-r--r--src/response.c20
-rw-r--r--src/server.c17
-rw-r--r--src/stat_cache.c64
-rw-r--r--tests/lighttpd.conf12
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])
diff --git a/src/base.h b/src/base.h
index c270282e..654f2ed2 100644
--- a/src/base.h
+++ b/src/base.h
@@ -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"