summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES3
-rw-r--r--docs/manual/mod/core.xml26
-rw-r--r--include/ap_mmn.h4
-rw-r--r--include/http_core.h1
-rw-r--r--include/httpd.h15
-rw-r--r--server/core.c7
-rw-r--r--server/request.c25
-rw-r--r--server/util.c10
8 files changed, 69 insertions, 22 deletions
diff --git a/CHANGES b/CHANGES
index afcb891e19..57f0e9e656 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,9 @@
-*- coding: utf-8 -*-
Changes with Apache 2.5.1
+ *) Merge consecutive slashes in URL's. Opt-out with `MergeSlashes OFF`.
+ [Eric Covener]
+
*) mod_proxy/ssl: Cleanup per-request SSL configuration anytime a backend
connection is recycled/reused to avoid a possible crash with some SSLProxy
configurations in <Location> or <Proxy> context. PR 63256. [Yann Ylavic]
diff --git a/docs/manual/mod/core.xml b/docs/manual/mod/core.xml
index f1516a3517..ac428dfe4b 100644
--- a/docs/manual/mod/core.xml
+++ b/docs/manual/mod/core.xml
@@ -5332,4 +5332,30 @@ as if 'QualifyRedirectURL ON' was configured.</compatibility>
</usage>
</directivesynopsis>
+<directivesynopsis>
+<name>MergeSlashes</name>
+<description>Controls whether the server merges consecutive slashes in URLs.
+</description>
+<syntax>MergeSlashes ON|OFF</syntax>
+<default>MergeSlashes ON</default>
+<contextlist><context>server config</context><context>virtual host</context>
+</contextlist>
+<compatibility>Added in 2.5.1</compatibility>
+
+<usage>
+ <p>By default, the server merges (or collapses) multiple consecutive slash
+ ('/') characters in the path component of the request URL.</p>
+
+ <p>When mapping URL's to the filesystem, these multiple slashes are not
+ significant. However, URL's handled other ways, such as by CGI or proxy,
+ might prefer to retain the significance of multiple consecutive slashes.
+ In these cases <directive>MergeSlashes</directive> can be set to
+ <em>OFF</em> to retain the multiple consecutive slashes. In these
+ configurations, regular expressions used in the configuration file that match
+ the path component of the URL (<directive>LocationMatch</directive>,
+ <directive>RewriteRule</directive>, ...) need to take into account multiple
+ consecutive slashes.</p>
+</usage>
+</directivesynopsis>
+
</modulesynopsis>
diff --git a/include/ap_mmn.h b/include/ap_mmn.h
index 7d400b3db6..5a479c075e 100644
--- a/include/ap_mmn.h
+++ b/include/ap_mmn.h
@@ -610,6 +610,8 @@
* 20180906.2 (2.5.1-dev) Add ap_state_dir_relative()
* 20180906.3 (2.5.1-dev) Add ap_dir_nofnmatch() and ap_dir_fnmatch().
* 20191203.1 (2.5.1-dev) Axe bucket number from struct process_score
+ * 20191203.2 (2.5.1-dev) Add ap_no2slash_ex() and merge_slashes to
+ * core_server_conf.
*/
#define MODULE_MAGIC_COOKIE 0x41503235UL /* "AP25" */
@@ -617,7 +619,7 @@
#ifndef MODULE_MAGIC_NUMBER_MAJOR
#define MODULE_MAGIC_NUMBER_MAJOR 20191203
#endif
-#define MODULE_MAGIC_NUMBER_MINOR 1 /* 0...n */
+#define MODULE_MAGIC_NUMBER_MINOR 2 /* 0...n */
/**
* Determine if the server's current MODULE_MAGIC_NUMBER is at least a
diff --git a/include/http_core.h b/include/http_core.h
index fe3836fb34..00aeaa7800 100644
--- a/include/http_core.h
+++ b/include/http_core.h
@@ -771,6 +771,7 @@ typedef struct {
apr_size_t flush_max_threshold;
apr_int32_t flush_max_pipelined;
unsigned int strict_host_check;
+ unsigned int merge_slashes;
} core_server_config;
/* for AddOutputFiltersByType in core.c */
diff --git a/include/httpd.h b/include/httpd.h
index 0005e0cf8a..1c6a242a89 100644
--- a/include/httpd.h
+++ b/include/httpd.h
@@ -1750,11 +1750,22 @@ AP_DECLARE(int) ap_unescape_url_keep2f(char *url, int decode_slashes);
AP_DECLARE(int) ap_unescape_urlencoded(char *query);
/**
- * Convert all double slashes to single slashes
- * @param name The string to convert
+ * Convert all double slashes to single slashes, except where significant
+ * to the filesystem on the current platform.
+ * @param name The string to convert, assumed to be a filesystem path
*/
AP_DECLARE(void) ap_no2slash(char *name)
AP_FN_ATTR_NONNULL_ALL;
+/**
+ * Convert all double slashes to single slashes, except where significant
+ * to the filesystem on the current platform.
+ * @param name The string to convert
+ * @param is_fs_path if set to 0, the significance of any double-slashes is
+ * ignored.
+ */
+AP_DECLARE(void) ap_no2slash_ex(char *name, int is_fs_path)
+ AP_FN_ATTR_NONNULL_ALL;
+
/**
* Remove all ./ and xx/../ substrings from a file name. Also remove
diff --git a/server/core.c b/server/core.c
index 98197d85e9..ceb7dd6bb8 100644
--- a/server/core.c
+++ b/server/core.c
@@ -528,6 +528,7 @@ static void *create_core_server_config(apr_pool_t *a, server_rec *s)
conf->protocols_honor_order = -1;
conf->async_filter = 0;
conf->strict_host_check= AP_CORE_CONFIG_UNSET;
+ conf->merge_slashes = AP_CORE_CONFIG_UNSET;
return (void *)conf;
}
@@ -628,6 +629,7 @@ static void *merge_core_server_configs(apr_pool_t *p, void *basev, void *virtv)
: base->strict_host_check;
AP_CORE_MERGE_FLAG(strict_host_check, conf, base, virt);
+ AP_CORE_MERGE_FLAG(merge_slashes, conf, base, virt);
return conf;
}
@@ -4922,6 +4924,11 @@ AP_INIT_TAKE1("ProtocolsHonorOrder", set_protocols_honor_order, NULL, RSRC_CONF,
AP_INIT_TAKE1("AsyncFilter", set_async_filter, NULL, RSRC_CONF,
"'network', 'connection' (default) or 'request' to limit the "
"types of filters that support asynchronous handling"),
+AP_INIT_FLAG("MergeSlashes", set_core_server_flag,
+ (void *)APR_OFFSETOF(core_server_config, merge_slashes),
+ RSRC_CONF,
+ "Controls whether consecutive slashes in the URI path are merged"),
+
{ NULL }
};
diff --git a/server/request.c b/server/request.c
index 70812fed59..052e20b3dd 100644
--- a/server/request.c
+++ b/server/request.c
@@ -167,6 +167,8 @@ AP_DECLARE(int) ap_process_request_internal(request_rec *r)
int file_req = (r->main && r->filename);
int access_status;
core_dir_config *d;
+ core_server_config *sconf =
+ ap_get_core_module_config(r->server->module_config);
/* Ignore embedded %2F's in path for proxy requests */
if (!r->proxyreq && r->parsed_uri.path) {
@@ -191,6 +193,10 @@ AP_DECLARE(int) ap_process_request_internal(request_rec *r)
}
ap_getparents(r->uri); /* OK --- shrinking transformations... */
+ if (sconf->merge_slashes != AP_CORE_CONFIG_OFF) {
+ ap_no2slash(r->uri);
+ ap_no2slash(r->parsed_uri.path);
+ }
/* All file subrequests are a huge pain... they cannot bubble through the
* next several steps. Only file subrequests are allowed an empty uri,
@@ -1415,20 +1421,7 @@ AP_DECLARE(int) ap_location_walk(request_rec *r)
cache = prep_walk_cache(AP_NOTE_LOCATION_WALK, r);
cached = (cache->cached != NULL);
-
- /* Location and LocationMatch differ on their behaviour w.r.t. multiple
- * slashes. Location matches multiple slashes with a single slash,
- * LocationMatch doesn't. An exception, for backwards brokenness is
- * absoluteURIs... in which case neither match multiple slashes.
- */
- if (r->uri[0] != '/') {
- entry_uri = r->uri;
- }
- else {
- char *uri = apr_pstrdup(r->pool, r->uri);
- ap_no2slash(uri);
- entry_uri = uri;
- }
+ entry_uri = r->uri;
/* If we have an cache->cached location that matches r->uri,
* and the vhost's list of locations hasn't changed, we can skip
@@ -1495,7 +1488,7 @@ AP_DECLARE(int) ap_location_walk(request_rec *r)
pmatch = apr_palloc(rxpool, nmatch*sizeof(ap_regmatch_t));
}
- if (ap_regexec(entry_core->r, r->uri, nmatch, pmatch, 0)) {
+ if (ap_regexec(entry_core->r, entry_uri, nmatch, pmatch, 0)) {
continue;
}
@@ -1505,7 +1498,7 @@ AP_DECLARE(int) ap_location_walk(request_rec *r)
apr_table_setn(r->subprocess_env,
((const char **)entry_core->refs->elts)[i],
apr_pstrndup(r->pool,
- r->uri + pmatch[i].rm_so,
+ entry_uri + pmatch[i].rm_so,
pmatch[i].rm_eo - pmatch[i].rm_so));
}
}
diff --git a/server/util.c b/server/util.c
index 3693bfbff1..89c5abef80 100644
--- a/server/util.c
+++ b/server/util.c
@@ -568,16 +568,16 @@ AP_DECLARE(void) ap_getparents(char *name)
name[l] = '\0';
}
}
-
-AP_DECLARE(void) ap_no2slash(char *name)
+AP_DECLARE(void) ap_no2slash_ex(char *name, int is_fs_path)
{
+
char *d, *s;
s = d = name;
#ifdef HAVE_UNC_PATHS
/* Check for UNC names. Leave leading two slashes. */
- if (s[0] == '/' && s[1] == '/')
+ if (is_fs_path && s[0] == '/' && s[1] == '/')
*d++ = *s++;
#endif
@@ -594,6 +594,10 @@ AP_DECLARE(void) ap_no2slash(char *name)
*d = '\0';
}
+AP_DECLARE(void) ap_no2slash(char *name)
+{
+ ap_no2slash_ex(name, 1);
+}
/*
* copy at most n leading directories of s into d