diff --git a/docs/manual/mod/core.html.en b/docs/manual/mod/core.html.en index 86d9bee..e08034b 100644 --- a/docs/manual/mod/core.html.en +++ b/docs/manual/mod/core.html.en @@ -90,6 +90,7 @@ available
  • MaxRangeOverlaps
  • MaxRangeReversals
  • MaxRanges
  • +
  • MergeSlashes
  • Mutex
  • NameVirtualHost
  • Options
  • @@ -3170,6 +3171,30 @@ resource
    top
    +

    MergeSlashes Directive

    + + + + + + + + +
    Description:Controls whether the server merges consecutive slashes in URLs.
    Syntax:MergeSlashes ON | OFF
    Default:MergeSlashes ON
    Context:server config, virtual host
    Status:Core
    Module:core
    Compatibility:Available in Apache HTTP Server 2.4.6 in Red Hat Enterprise Linux 7
    +

    By default, the server merges (or collapses) multiple consecutive slash + ('/') characters in the path component of the request URL.

    + +

    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 MergeSlashes can be set to + OFF to retain the multiple consecutive slashes. In these + configurations, regular expressions used in the configuration file that match + the path component of the URL (LocationMatch, + RewriteRule, ...) need to take into account multiple + consecutive slashes.

    +
    +
    top

    Mutex Directive

    Description:Configures mutex mechanism and lock file directory for all diff --git a/include/http_core.h b/include/http_core.h index c05d06e..76bf5a4 100644 --- a/include/http_core.h +++ b/include/http_core.h @@ -465,6 +465,17 @@ typedef unsigned long etag_components_t; /* This is the default value used */ #define ETAG_BACKWARD (ETAG_MTIME | ETAG_SIZE) +/* Generic ON/OFF/UNSET for unsigned int foo :2 */ +#define AP_CORE_CONFIG_OFF (0) +#define AP_CORE_CONFIG_ON (1) +#define AP_CORE_CONFIG_UNSET (2) + +/* Generic merge of flag */ +#define AP_CORE_MERGE_FLAG(field, to, base, over) to->field = \ + over->field != AP_CORE_CONFIG_UNSET \ + ? over->field \ + : base->field + /** * @brief Server Signature Enumeration */ @@ -682,7 +693,7 @@ typedef struct { #define AP_HTTP_METHODS_LENIENT 1 #define AP_HTTP_METHODS_REGISTERED 2 char http_methods; - + unsigned int merge_slashes; } core_server_config; /* for AddOutputFiltersByType in core.c */ diff --git a/include/httpd.h b/include/httpd.h index 176ef5e..a552358 100644 --- a/include/httpd.h +++ b/include/httpd.h @@ -1622,11 +1622,21 @@ 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); +/** + * 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); + /** * Remove all ./ and xx/../ substrings from a file name. Also remove * any leading ../ or /../ substrings. diff --git a/server/core.c b/server/core.c index 0e69f8c..67efd7e 100644 --- a/server/core.c +++ b/server/core.c @@ -476,6 +476,7 @@ static void *create_core_server_config(apr_pool_t *a, server_rec *s) */ conf->trace_enable = AP_TRACE_UNSET; + conf->merge_slashes = AP_CORE_CONFIG_UNSET; return (void *)conf; } @@ -536,6 +537,8 @@ static void *merge_core_server_configs(apr_pool_t *p, void *basev, void *virtv) ? virt->merge_trailers : base->merge_trailers; + AP_CORE_MERGE_FLAG(merge_slashes, conf, base, virt); + return conf; } @@ -1673,6 +1676,13 @@ static const char *set_override(cmd_parms *cmd, void *d_, const char *l) return NULL; } +static const char *set_core_server_flag(cmd_parms *cmd, void *s_, int flag) +{ + core_server_config *conf = + ap_get_core_module_config(cmd->server->module_config); + return ap_set_flag_slot(cmd, conf, flag); +} + static const char *set_override_list(cmd_parms *cmd, void *d_, int argc, char *const argv[]) { core_dir_config *d = d_; @@ -4216,6 +4226,10 @@ AP_INIT_ITERATE("HttpProtocolOptions", set_http_protocol_options, NULL, RSRC_CON , AP_INIT_ITERATE("RegisterHttpMethod", set_http_method, NULL, RSRC_CONF, "Registers non-standard HTTP methods"), +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 4eef097..cba3891 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,12 @@ 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); + if (r->parsed_uri.path) { + 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, @@ -1383,20 +1391,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 @@ -1449,7 +1444,7 @@ AP_DECLARE(int) ap_location_walk(request_rec *r) * terminated (or at the end of the string) to match. */ if (entry_core->r - ? ap_regexec(entry_core->r, r->uri, 0, NULL, 0) + ? ap_regexec(entry_core->r, entry_uri, 0, NULL, 0) : (entry_core->d_is_fnmatch ? apr_fnmatch(entry_core->d, cache->cached, APR_FNM_PATHNAME) : (strncmp(entry_core->d, cache->cached, len) diff --git a/server/util.c b/server/util.c index f9e3b51..4eac462 100644 --- a/server/util.c +++ b/server/util.c @@ -561,16 +561,20 @@ 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; + if (!*name) { + return; + } + 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 @@ -587,6 +591,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