From e0115208ec9f3f896509dccf373357f0e329e3b0 Mon Sep 17 00:00:00 2001 From: Glenn Strauss Date: Tue, 26 Apr 2016 20:43:26 -0400 Subject: [PATCH] [mod_webdav] map COPY/MOVE Destination to aliases (fixes #1787) attempt to remap COPY/MOVE Destination to aliased physical paths by finding common URI prefix between request URI and Destination and finding how that part of the request URI was mapped to a physical path. This will work if the aliased physical path is above the webdav root. It is not a good idea to remap physical paths within a webdav root. Note: webdav paths and webdav properties are managed by mod_webdav, so do not modify paths externally or else undefined behavior or corruption may occur x-ref: "Bug in mod_webdav when using aliases and MOVE command" https://redmine.lighttpd.net/issues/1787 --- src/mod_webdav.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 9 deletions(-) diff --git a/src/mod_webdav.c b/src/mod_webdav.c index 3968a04c..bac69fbc 100644 --- a/src/mod_webdav.c +++ b/src/mod_webdav.c @@ -1920,15 +1920,62 @@ SUBREQUEST_FUNC(mod_webdav_subrequest_handler_huge) { buffer_to_lower(p->physical.rel_path); } - buffer_copy_buffer(p->physical.path, p->physical.doc_root); - buffer_append_slash(p->physical.path); - buffer_copy_buffer(p->physical.basedir, p->physical.path); - - /* don't add a second / */ - if (p->physical.rel_path->ptr[0] == '/') { - buffer_append_string_len(p->physical.path, p->physical.rel_path->ptr + 1, buffer_string_length(p->physical.rel_path) - 1); - } else { - buffer_append_string_buffer(p->physical.path, p->physical.rel_path); + /* Destination physical path + * src con->physical.path might have been remapped with mod_alias. + * (but mod_alias does not modify con->physical.rel_path) + * Find matching prefix to support use of mod_alias to remap webdav root. + * Aliasing of paths underneath the webdav root might not work. + * Likewise, mod_rewrite URL rewriting might thwart this comparison. + * Use mod_redirect instead of mod_alias to remap paths *under* webdav root. + * Use mod_redirect instead of mod_rewrite on *any* parts of path to webdav. + * (Related, use mod_auth to protect webdav root, but avoid attempting to + * use mod_auth on paths underneath webdav root, as Destination is not + * validated with mod_auth) + * + * tl;dr: webdav paths and webdav properties are managed by mod_webdav, + * so do not modify paths externally or else undefined behavior + * or corruption may occur + */ + { + /* find matching URI prefix + * check if remaining con->physical.rel_path matches suffix + * of con->physical.basedir so that we can use it to + * remap Destination physical path */ + size_t i, remain; + sep = con->uri.path->ptr; + sep2 = p->uri.path->ptr; + for (i = 0; sep[i] && sep[i] == sep2[i]; ++i) ; + if (sep[i] == '\0' && (sep2[i] == '\0' || sep2[i] == '/' || (i > 0 && sep[i-1] == '/'))) { + /* src and dst URI match or dst is nested inside src; invalid COPY or MOVE */ + con->http_status = 403; + return HANDLER_FINISHED; + } + while (i != 0 && sep[--i] != '/') ; /* find matching directory path */ + remain = buffer_string_length(con->uri.path) - i; + if (!con->conf.force_lowercase_filenames + ? buffer_is_equal_right_len(con->physical.path, con->physical.rel_path, remain) + :(buffer_string_length(con->physical.path) >= remain + && 0 == strncasecmp(con->physical.path->ptr+buffer_string_length(con->physical.path)-remain, con->physical.rel_path->ptr+i, remain))) { + /* (at this point, p->physical.rel_path is identical to (or lowercased version of) p->uri.path) */ + buffer_copy_string_len(p->physical.path, con->physical.path->ptr, buffer_string_length(con->physical.path)-remain); + buffer_append_string_len(p->physical.path, p->physical.rel_path->ptr+i, buffer_string_length(p->physical.rel_path)-i); + + buffer_copy_buffer(p->physical.basedir, con->physical.basedir); + buffer_append_slash(p->physical.basedir); + } else { + /* unable to perform physical path remap here; + * assume doc_root/rel_path and no remapping */ + buffer_copy_buffer(p->physical.path, p->physical.doc_root); + buffer_append_slash(p->physical.path); + buffer_copy_buffer(p->physical.basedir, p->physical.path); + + /* don't add a second / */ + if (p->physical.rel_path->ptr[0] == '/') { + buffer_append_string_len(p->physical.path, p->physical.rel_path->ptr + 1, buffer_string_length(p->physical.rel_path) - 1); + } else { + buffer_append_string_buffer(p->physical.path, p->physical.rel_path); + } + } } /* let's see if the source is a directory -- 2.11.4.GIT