[nginx] Dav: destination validation for COPY and MOVE with "alias".
Maxim Dounin
mdounin at mdounin.ru
Tue Mar 31 04:02:22 UTC 2026
details: http://freenginx.org/hg/nginx/rev/aae28b083279
branches:
changeset: 9493:aae28b083279
user: Maxim Dounin <mdounin at mdounin.ru>
date: Tue Mar 31 06:54:50 2026 +0300
description:
Dav: destination validation for COPY and MOVE with "alias".
If COPY or MOVE methods are enabled, the DAV module assumes that it is
configured for the whole server, and does not try validate that the
destination URI is within the particular location. This, however, does
not work well if it is in fact configured in a non-root location,
and this location uses the "alias" directive, such as in the following
configuration:
location /prefix/ {
dav_methods COPY;
alias /foo/;
}
In particular, if the destination URI is shorter than the aliased location
prefix, calling ngx_http_map_uri_to_path() with such destination URI used
to result in segmentation faults (CVE-2026-27654). And if the destination
URI is longer than the aliased location prefix, but does not match it,
calling ngx_http_map_uri_to_path() resulted in unexpected path values,
including ones above the "alias" directory specified.
The fix is to check destination URI prefix if "alias" is used, similarly
to what we do with "try_files". Additionally, the ngx_http_map_uri_to_path()
function was updated with additional sanity checking to prevent similar
issues.
See also:
https://github.com/nginx/nginx/commit/9739e755b8dddba82e65ca2a08d079f4c9826b75
diffstat:
src/http/modules/ngx_http_dav_module.c | 43 +++++++++++++++++++++++----------
src/http/ngx_http_core_module.c | 6 ++++
2 files changed, 36 insertions(+), 13 deletions(-)
diffs (76 lines):
diff --git a/src/http/modules/ngx_http_dav_module.c b/src/http/modules/ngx_http_dav_module.c
--- a/src/http/modules/ngx_http_dav_module.c
+++ b/src/http/modules/ngx_http_dav_module.c
@@ -535,19 +535,20 @@ ngx_http_dav_mkcol_handler(ngx_http_requ
static ngx_int_t
ngx_http_dav_copy_move_handler(ngx_http_request_t *r)
{
- u_char *p, *host, *last, ch;
- size_t len, root;
- ngx_err_t err;
- ngx_int_t rc, depth;
- ngx_uint_t overwrite, slash, dir, flags;
- ngx_str_t path, uri, duri, args;
- ngx_tree_ctx_t tree;
- ngx_copy_file_t cf;
- ngx_file_info_t fi;
- ngx_table_elt_t *dest, *over;
- ngx_ext_rename_file_t ext;
- ngx_http_dav_copy_ctx_t copy;
- ngx_http_dav_loc_conf_t *dlcf;
+ u_char *p, *host, *last, ch;
+ size_t len, root, alias;
+ ngx_err_t err;
+ ngx_int_t rc, depth;
+ ngx_uint_t overwrite, slash, dir, flags;
+ ngx_str_t path, uri, duri, args;
+ ngx_tree_ctx_t tree;
+ ngx_copy_file_t cf;
+ ngx_file_info_t fi;
+ ngx_table_elt_t *dest, *over;
+ ngx_ext_rename_file_t ext;
+ ngx_http_dav_copy_ctx_t copy;
+ ngx_http_dav_loc_conf_t *dlcf;
+ ngx_http_core_loc_conf_t *clcf;
if (r->headers_in.content_length_n > 0 || r->headers_in.chunked) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
@@ -644,6 +645,22 @@ destination_done:
return NGX_HTTP_CONFLICT;
}
+ clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
+ alias = clcf->alias;
+
+ if (alias && alias != NGX_MAX_SIZE_T_VALUE) {
+
+ if (alias > duri.len
+ || ngx_filename_cmp(duri.data, r->uri.data, alias) != 0)
+ {
+ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
+ "\"Destination\" URI \"%V\" must be "
+ "within location prefix when using \"alias\"",
+ &dest->value);
+ return NGX_HTTP_BAD_REQUEST;
+ }
+ }
+
depth = ngx_http_dav_depth(r, NGX_HTTP_DAV_INFINITY_DEPTH);
if (depth != NGX_HTTP_DAV_INFINITY_DEPTH) {
diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -1912,6 +1912,12 @@ ngx_http_map_uri_to_path(ngx_http_reques
return NULL;
}
+ if (alias > r->uri.len && alias != NGX_MAX_SIZE_T_VALUE) {
+ ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
+ "URI shorter than aliased URI part");
+ return NULL;
+ }
+
if (clcf->root_lengths == NULL) {
*root_length = clcf->root.len;
More information about the nginx-devel
mailing list