summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGlenn Strauss <gstrauss@gluelogic.com>2018-11-25 00:28:42 -0500
committerGlenn Strauss <gstrauss@gluelogic.com>2018-11-25 19:52:08 -0500
commite8e59396d3ab45a61b89fd7ee631cb700d6f53d7 (patch)
tree1211242a117fb488ad739b500ad0422ae2e0bbad
parent8a8579802a1fcac6466bfabb8579e26f42137b3d (diff)
downloadlighttpd1.4-e8e59396d3ab45a61b89fd7ee631cb700d6f53d7.tar.gz
lighttpd1.4-e8e59396d3ab45a61b89fd7ee631cb700d6f53d7.zip
[core] reject decoded url-path without leading '/'
buffer_simplify_path() no longer prepends '/' if '/' is missing. Callers must check for leading '/' depending on use, such as in concatenation with others paths, or direct use accessing filesystem Note: lighttpd 1.4.50 provides the server.http-parseopts directive. Recommended settings unless specific use requires looser settings: server.http-parseopts = ( "header-strict" => "enable", "host-strict" => "enable", "host-normalize" => "enable", "url-normalize" => "enable", "url-normalize-unreserved" => "enable", "url-normalize-required" => "enable", "url-ctrls-reject" => "enable", "url-path-2f-decode" => "enable", "url-path-dotseg-remove" => "enable", "url-query-20-plus" => "enable" ) x-ref: https://digi.ninja/blog/lighttpd_rewrite_bypass.php As noted in the link above, mod_access should be preferred instead of mod_rewrite for access controls to URLs.
-rw-r--r--src/buffer.c20
-rw-r--r--src/mod_webdav.c5
-rw-r--r--src/response.c8
-rw-r--r--src/t/test_buffer.c27
-rw-r--r--src/t/test_burl.c1
5 files changed, 32 insertions, 29 deletions
diff --git a/src/buffer.c b/src/buffer.c
index 5e03e273..da65a775 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -806,13 +806,6 @@ void buffer_path_simplify(buffer *dest, buffer *src)
force_assert('\0' == src->ptr[src->used-1]);
- /* might need one character more for the '/' prefix */
- if (src == dest) {
- buffer_string_prepare_append(dest, 1);
- } else {
- buffer_string_prepare_copy(dest, buffer_string_length(src) + 1);
- }
-
#if defined(__WIN32) || defined(__CYGWIN__)
/* cygwin is treating \ and / the same, so we have to that too */
{
@@ -832,14 +825,15 @@ void buffer_path_simplify(buffer *dest, buffer *src)
while (*walk == ' ') {
walk++;
}
+ if (*walk == '.') {
+ if (walk[1] == '/' || walk[1] == '\0')
+ ++walk;
+ else if (walk[1] == '.' && (walk[2] == '/' || walk[2] == '\0'))
+ walk+=2;
+ }
pre1 = 0;
c = *(walk++);
- /* prefix with '/' if not already present */
- if (c != '/') {
- pre1 = '/';
- *(out++) = '/';
- }
while (c != '\0') {
/* assert((src != dest || out <= walk) && slash <= out); */
@@ -859,7 +853,7 @@ void buffer_path_simplify(buffer *dest, buffer *src)
if (c == '/' || c == '\0') {
const size_t toklen = out - slash;
- if (toklen == 3 && pre2 == '.' && pre1 == '.') {
+ if (toklen == 3 && pre2 == '.' && pre1 == '.' && *slash == '/') {
/* "/../" or ("/.." at end of string) */
out = slash;
/* if there is something before "/..", there is at least one
diff --git a/src/mod_webdav.c b/src/mod_webdav.c
index e7d33d26..aa3e9106 100644
--- a/src/mod_webdav.c
+++ b/src/mod_webdav.c
@@ -1998,6 +1998,11 @@ static handler_t mod_webdav_copymove(server *srv, connection *con, plugin_data *
buffer_urldecode_path(p->uri.path);
buffer_path_simplify(p->uri.path, p->uri.path);
+ if (buffer_string_is_empty(p->uri.path) || p->uri.path->ptr[0] != '/') {
+ con->http_status = 400;
+ return HANDLER_FINISHED;
+ }
+
/* we now have a URI which is clean. transform it into a physical path */
buffer_copy_buffer(p->physical.doc_root, con->physical.doc_root);
buffer_copy_buffer(p->physical.rel_path, p->uri.path);
diff --git a/src/response.c b/src/response.c
index 781505b0..aa02b529 100644
--- a/src/response.c
+++ b/src/response.c
@@ -396,6 +396,14 @@ handler_t http_response_prepare(server *srv, connection *con) {
buffer_copy_buffer(con->uri.path, con->uri.path_raw);
buffer_urldecode_path(con->uri.path);
buffer_path_simplify(con->uri.path, con->uri.path);
+ if (buffer_string_is_empty(con->uri.path) || con->uri.path->ptr[0] != '/') {
+ log_error_write(srv, __FILE__, __LINE__, "sbs",
+ "uri-path does not begin with '/':", con->uri.path, "-> 400");
+ con->keep_alive = 0;
+ con->http_status = 400;
+ con->file_finished = 1;
+ return HANDLER_FINISHED;
+ }
}
con->conditional_is_valid[COMP_SERVER_SOCKET] = 1; /* SERVERsocket */
diff --git a/src/t/test_buffer.c b/src/t/test_buffer.c
index 2dd0e400..3e6eb4df 100644
--- a/src/t/test_buffer.c
+++ b/src/t/test_buffer.c
@@ -23,15 +23,6 @@ static void run_buffer_path_simplify(buffer *psrc, buffer *pdest, const char *in
fflush(stderr);
abort();
} else {
- #if 0
- fprintf(stdout,
- "%s.%d: buffer_path_simplify('%s') succeeded: got '%s'\n",
- __FILE__,
- __LINE__,
- in,
- out);
- #endif
-
if (psrc != pdest) buffer_copy_buffer(psrc, pdest);
buffer_path_simplify(pdest, psrc);
@@ -40,7 +31,7 @@ static void run_buffer_path_simplify(buffer *psrc, buffer *pdest, const char *in
"%s.%d: buffer_path_simplify('%s') failed - not idempotent: expected '%s', got '%s'\n",
__FILE__,
__LINE__,
- out,
+ in,
out,
pdest->ptr ? pdest->ptr : "");
fflush(stderr);
@@ -51,21 +42,25 @@ static void run_buffer_path_simplify(buffer *psrc, buffer *pdest, const char *in
static void test_buffer_path_simplify_with(buffer *psrc, buffer *pdest) {
run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN(""), CONST_STR_LEN(""));
- run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN(" "), CONST_STR_LEN("/"));
run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("/"), CONST_STR_LEN("/"));
run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("//"), CONST_STR_LEN("/"));
- run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("abc"), CONST_STR_LEN("/abc"));
- run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("abc//"), CONST_STR_LEN("/abc/"));
- run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("abc/./xyz"), CONST_STR_LEN("/abc/xyz"));
- run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("abc/.//xyz"), CONST_STR_LEN("/abc/xyz"));
+ run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("abc"), CONST_STR_LEN("abc"));
+ run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("abc//"), CONST_STR_LEN("abc/"));
+ run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("abc/./xyz"), CONST_STR_LEN("abc/xyz"));
+ run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("abc/.//xyz"), CONST_STR_LEN("abc/xyz"));
run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("abc/../xyz"), CONST_STR_LEN("/xyz"));
run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("/abc/./xyz"), CONST_STR_LEN("/abc/xyz"));
run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("/abc//./xyz"), CONST_STR_LEN("/abc/xyz"));
run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("/abc/../xyz"), CONST_STR_LEN("/xyz"));
run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("abc/../xyz/."), CONST_STR_LEN("/xyz/"));
run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("/abc/../xyz/."), CONST_STR_LEN("/xyz/"));
- run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("abc/./xyz/.."), CONST_STR_LEN("/abc/"));
+ run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("abc/./xyz/.."), CONST_STR_LEN("abc/"));
run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("/abc/./xyz/.."), CONST_STR_LEN("/abc/"));
+ run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("."), CONST_STR_LEN(""));
+ run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN(".."), CONST_STR_LEN(""));
+ run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("..."), CONST_STR_LEN("..."));
+ run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("...."), CONST_STR_LEN("...."));
+ run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN(".../"), CONST_STR_LEN(".../"));
run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("./xyz/.."), CONST_STR_LEN("/"));
run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN(".//xyz/.."), CONST_STR_LEN("/"));
run_buffer_path_simplify(psrc, pdest, CONST_STR_LEN("/./xyz/.."), CONST_STR_LEN("/"));
diff --git a/src/t/test_burl.c b/src/t/test_burl.c
index e83bebea..7be9be50 100644
--- a/src/t/test_burl.c
+++ b/src/t/test_burl.c
@@ -31,6 +31,7 @@ static void test_burl_normalize (void) {
int flags;
flags = HTTP_PARSEOPT_URL_NORMALIZE_UNRESERVED;
+ run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("no-slash"), CONST_STR_LEN("no-slash"));
run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/"), CONST_STR_LEN("/"));
run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/abc"), CONST_STR_LEN("/abc"));
run_burl_normalize(psrc, ptmp, flags, __LINE__, CONST_STR_LEN("/abc/"), CONST_STR_LEN("/abc/"));