summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGraham Leggett <minfrin@apache.org>2022-01-17 17:10:51 +0100
committerGraham Leggett <minfrin@apache.org>2022-01-17 17:10:51 +0100
commitc51dccd833ccaec00df9420701c879ca6fb0e3b4 (patch)
treea2cac77ceb793e94c8e774f0efb2800b611be3fc
parentbump autuconf min to 2.60 (diff)
downloadapache2-c51dccd833ccaec00df9420701c879ca6fb0e3b4.tar.xz
apache2-c51dccd833ccaec00df9420701c879ca6fb0e3b4.zip
core: Allow an optional expression to be specified for an effective
path in the DirectoryMatch and LocationMatch directives. This allows modules like mod_dav to map URLs to URL spaces or to directories on the filesystem. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1897156 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--CHANGES5
-rw-r--r--docs/log-message-tags/next-number2
-rw-r--r--docs/manual/mod/core.xml55
-rw-r--r--modules/dav/main/mod_dav.c95
-rw-r--r--server/core.c51
5 files changed, 187 insertions, 21 deletions
diff --git a/CHANGES b/CHANGES
index de3bbe22d6..cc04ee13e4 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,11 @@
-*- coding: utf-8 -*-
Changes with Apache 2.5.1
+ *) core: Allow an optional expression to be specified for an effective
+ path in the DirectoryMatch and LocationMatch directives. This allows
+ modules like mod_dav to map URLs to URL spaces or to directories on
+ the filesystem. [Graham Leggett]
+
*) http: Enforce that fully qualified uri-paths not to be forward-proxied
have an http(s) scheme, and that the ones to be forward proxied have a
hostname, per HTTP specifications. [Ruediger Pluem, Yann Ylavic]
diff --git a/docs/log-message-tags/next-number b/docs/log-message-tags/next-number
index 4ca47544e3..427e445179 100644
--- a/docs/log-message-tags/next-number
+++ b/docs/log-message-tags/next-number
@@ -1 +1 @@
-10367
+10369
diff --git a/docs/manual/mod/core.xml b/docs/manual/mod/core.xml
index e22545e8cf..1b98107146 100644
--- a/docs/manual/mod/core.xml
+++ b/docs/manual/mod/core.xml
@@ -926,6 +926,20 @@ named file-system directory, sub-directories, and their contents.</description>
the corresponding <directive type="section">Directory</directive> will
be applied.</p>
+ <p>Some modules require the directory-path prefix in order to do their work,
+ and when a regular expression is provided the directory-path is no longer
+ available. From 2.5.1 onwards, an expression can be specified in addition
+ to the regular expression that resolves to the directory-path prefix. This
+ can allow complex mappings from the URL space to an effective directory.
+ This funcionality is identical to that provided by the
+ <directive>DirectoryMatch</directive> directive below.</p>
+
+ <highlight language="config">
+&lt;Directory ~ /home/%{env:MATCH_PARTITIONNAME}/dav/ ^/dav/(?&lt;PARTITIONNAME&gt;[^/]+)/&gt;
+ Dav on
+&lt;/Directory&gt;
+ </highlight>
+
<p><strong>Note that the default access for
<code>&lt;Directory "/"&gt;</code> is to permit all access.
This means that Apache httpd will serve any file mapped from an URL. It is
@@ -959,7 +973,7 @@ named file-system directory, sub-directories, and their contents.</description>
<name>DirectoryMatch</name>
<description>Enclose directives that apply to
the contents of file-system directories matching a regular expression.</description>
-<syntax>&lt;DirectoryMatch <var>regex</var>&gt;
+<syntax>&lt;DirectoryMatch [<var>expr</var>] <var>regex</var>&gt;
... &lt;/DirectoryMatch&gt;</syntax>
<contextlist><context>server config</context><context>virtual host</context>
</contextlist>
@@ -1007,6 +1021,18 @@ the contents of file-system directories matching a regular expression.</descript
Require ldap-group cn=%{env:MATCH_SITENAME},ou=combined,o=Example
&lt;/DirectoryMatch&gt;
</highlight>
+
+ <p>Some modules require the directory-path prefix in order to do their work,
+ and when a regular expression is provided the directory-path is no longer
+ available. From 2.5.1 onwards, an expression can be specified in addition
+ to the regular expression that resolves to the directory-path prefix. This
+ can allow complex mappings from the URL space to an effective directory.</p>
+
+ <highlight language="config">
+&lt;DirectoryMatch /home/%{env:MATCH_PARTITIONNAME}/dav/ ^/dav/(?&lt;PARTITIONNAME&gt;[^/]+)/&gt;
+ Dav on
+&lt;/DirectoryMatch&gt;
+ </highlight>
</usage>
<seealso><directive type="section" module="core">Directory</directive> for
a description of how regular expressions are mixed in with normal
@@ -3172,6 +3198,19 @@ URLs</description>
&lt;/Location&gt;
</highlight>
+ <p>Some modules require the URL-path prefix in order to do their work, and when
+ a regular expression is provided the URL-path is no longer available. From
+ 2.5.1 onwards, an expression can be specified in addition to the regular
+ expression that resolves to the URL-path prefix. This can allow complex
+ mappings from the URL space to an effective path. This funcionality is identical
+ to that provided by the <directive>LocationMatch</directive> directive below.</p>
+
+ <highlight language="config">
+&lt;Location ~ /dav/%{env:MATCH_PARTITIONNAME} ^/dav/(?&lt;PARTITIONNAME&gt;[^/]+)/&gt;
+ Dav on
+&lt;/Location&gt;
+ </highlight>
+
<note><title>Note about / (slash)</title>
<p>The slash character has special meaning depending on where in a
URL it appears. People may be used to its behavior in the filesystem
@@ -3207,7 +3246,7 @@ URLs</description>
<description>Applies the enclosed directives only to regular-expression
matching URLs</description>
<syntax>&lt;LocationMatch
- <var>regex</var>&gt; ... &lt;/LocationMatch&gt;</syntax>
+ [<var>expr</var>] <var>regex</var>&gt; ... &lt;/LocationMatch&gt;</syntax>
<contextlist><context>server config</context><context>virtual host</context>
</contextlist>
@@ -3250,6 +3289,18 @@ matching URLs</description>
&lt;/LocationMatch&gt;
</highlight>
+ <p>Some modules require the URL-path prefix in order to do their work, and when
+ a regular expression is provided the URL-path is no longer available. From
+ 2.5.1 onwards, an expression can be specified in addition to the regular
+ expression that resolves to the URL-path prefix. This can allow complex
+ mappings from the URL space to an effective path.</p>
+
+ <highlight language="config">
+&lt;LocationMatch /dav/%{env:MATCH_PARTITIONNAME} ^/dav/(?&lt;PARTITIONNAME&gt;[^/]+)/&gt;
+ Dav on
+&lt;/LocationMatch&gt;
+ </highlight>
+
<note><title>Note about / (slash)</title>
<p>The slash character has special meaning depending on where in a
URL it appears. People may be used to its behavior in the filesystem
diff --git a/modules/dav/main/mod_dav.c b/modules/dav/main/mod_dav.c
index 1a4b0663f0..080fb26cf0 100644
--- a/modules/dav/main/mod_dav.c
+++ b/modules/dav/main/mod_dav.c
@@ -83,6 +83,7 @@ typedef struct {
const char *dir;
int locktimeout;
int allow_depthinfinity;
+ const ap_expr_info_t *dir_expr;
} dav_dir_conf;
@@ -203,6 +204,7 @@ static void *dav_merge_dir_config(apr_pool_t *p, void *base, void *overrides)
newconf->dir = DAV_INHERIT_VALUE(parent, child, dir);
newconf->allow_depthinfinity = DAV_INHERIT_VALUE(parent, child,
allow_depthinfinity);
+ newconf->dir_expr = DAV_INHERIT_VALUE(parent, child, dir_expr);
return newconf;
}
@@ -282,6 +284,18 @@ static const char *dav_cmd_dav(cmd_parms *cmd, void *config, const char *arg1)
}
}
+ if (!conf->dir_expr) {
+ const char *expr_err = NULL;
+
+ conf->dir_expr = ap_expr_parse_cmd(cmd, conf->dir, AP_EXPR_FLAG_STRING_RESULT,
+ &expr_err, NULL);
+ if (expr_err) {
+ return apr_pstrcat(cmd->temp_pool,
+ "Cannot parse Directory/Location expression '", conf->dir, "': ",
+ expr_err, NULL);
+ }
+ }
+
return NULL;
}
@@ -723,6 +737,57 @@ static int dav_get_overwrite(request_rec *r)
return -1;
}
+static int uripath_is_canonical(const char *uripath)
+{
+ const char *dot_pos, *ptr = uripath;
+ apr_size_t i, len;
+ unsigned pattern = 0;
+
+ /* URIPATH is canonical if it has:
+ * - no '.' segments
+ * - no closing '/'
+ * - no '//'
+ */
+
+ if (ptr[0] == '.'
+ && (ptr[1] == '/' || ptr[1] == '\0'
+ || (ptr[1] == '.' && (ptr[2] == '/' || ptr[2] == '\0')))) {
+ return 0;
+ }
+
+ /* valid special cases */
+ len = strlen(ptr);
+ if (len < 2) {
+ return 1;
+ }
+
+ /* invalid endings */
+ if (ptr[len - 1] == '/' || (ptr[len - 1] == '.' && ptr[len - 2] == '/')) {
+ return 0;
+ }
+
+ /* '.' are rare. So, search for them globally. There will often be no
+ * more than one hit. Also note that we already checked for invalid
+ * starts and endings, i.e. we only need to check for "/./"
+ */
+ for (dot_pos = memchr(ptr, '.', len); dot_pos;
+ dot_pos = strchr(dot_pos + 1, '.')) {
+ if (dot_pos > ptr && dot_pos[-1] == '/' && dot_pos[1] == '/') {
+ return 0;
+ }
+ }
+
+ /* Now validate the rest of the path. */
+ for (i = 0; i < len - 1; ++i) {
+ pattern = ((pattern & 0xff) << 8) + (unsigned char) ptr[i];
+ if (pattern == 0x101 * (unsigned char) ('/')) {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
/* resolve a request URI to a resource descriptor.
*
* If label_allowed != 0, then allow the request target to be altered by
@@ -737,6 +802,7 @@ DAV_DECLARE(dav_error *) dav_get_resource(request_rec *r, int label_allowed,
{
dav_dir_conf *conf;
const char *label = NULL;
+ const char *dir;
dav_error *err;
/* if the request target can be overridden, get any target selector */
@@ -753,9 +819,34 @@ DAV_DECLARE(dav_error *) dav_get_resource(request_rec *r, int label_allowed,
ap_escape_html(r->pool, r->uri)));
}
+ if (conf->dir_expr) {
+ const char *err = NULL;
+
+ dir = ap_expr_str_exec(r, conf->dir_expr, &err);
+ if (err) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10367)
+ "Director/Location expression '%s' could not be parsed: %s", conf->dir, err);
+ return dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0, 0,
+ apr_psprintf(r->pool,
+ "Directory/Location expression could not be parsed: %s", err));
+ }
+
+ /* safety check - is our path canonical? */
+ if (!uripath_is_canonical(dir)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10368)
+ "Directory/Location is not canonical ('.', '..' and '//' not allowed): %s", dir);
+ return dav_new_error(r->pool, HTTP_BAD_REQUEST, 0, 0,
+ apr_psprintf(r->pool, "Directory/Location is not canonical for: %s",
+ ap_escape_html(r->pool, r->uri)));
+ }
+
+ }
+ else {
+ dir = conf->dir;
+ }
+
/* resolve the resource */
- err = (*conf->provider->repos->get_resource)(r, conf->dir,
- label, use_checked_in,
+ err = (*conf->provider->repos->get_resource)(r, dir, label, use_checked_in,
res_p);
if (err != NULL) {
err = dav_push_error(r->pool, err->status, 0,
diff --git a/server/core.c b/server/core.c
index 67c36f8134..634d4c6b5d 100644
--- a/server/core.c
+++ b/server/core.c
@@ -2506,6 +2506,7 @@ static const char *dirsection(cmd_parms *cmd, void *mconfig, const char *arg)
char *old_path = cmd->path;
core_dir_config *conf;
ap_conf_vector_t *new_dir_conf = ap_create_per_dir_config(cmd->pool);
+ const char *regex;
ap_regex_t *r = NULL;
const command_rec *thiscmd = cmd->cmd;
@@ -2529,15 +2530,20 @@ static const char *dirsection(cmd_parms *cmd, void *mconfig, const char *arg)
if (!strcmp(cmd->path, "~")) {
cmd->path = ap_getword_conf(cmd->pool, &arg);
- if (!cmd->path)
- return "<Directory ~ > block must specify a path";
- r = ap_pregcomp(cmd->pool, cmd->path, AP_REG_EXTENDED|USE_ICASE);
+ if (!*cmd->path) {
+ return "<Directory ~ > block must specify a regex";
+ }
+ regex = ap_getword_conf(cmd->pool, &arg);
+ r = ap_pregcomp(cmd->pool, *regex ? regex : cmd->path,
+ AP_REG_EXTENDED | USE_ICASE);
if (!r) {
return "Regex could not be compiled";
}
}
else if (thiscmd->cmd_data) { /* <DirectoryMatch> */
- r = ap_pregcomp(cmd->pool, cmd->path, AP_REG_EXTENDED|USE_ICASE);
+ regex = ap_getword_conf(cmd->pool, &arg);
+ r = ap_pregcomp(cmd->pool, *regex ? regex : cmd->path,
+ AP_REG_EXTENDED | USE_ICASE);
if (!r) {
return "Regex could not be compiled";
}
@@ -2599,8 +2605,8 @@ static const char *dirsection(cmd_parms *cmd, void *mconfig, const char *arg)
ap_add_per_dir_conf(cmd->server, new_dir_conf);
if (*arg != '\0') {
- return apr_pstrcat(cmd->pool, "Multiple ", thiscmd->name,
- "> arguments not (yet) supported.", NULL);
+ return apr_pstrcat(cmd->pool, "Additional ", thiscmd->name,
+ "> arguments not (yet) supported: ", arg, NULL);
}
cmd->path = old_path;
@@ -2616,6 +2622,7 @@ static const char *urlsection(cmd_parms *cmd, void *mconfig, const char *arg)
int old_overrides = cmd->override;
char *old_path = cmd->path;
core_dir_config *conf;
+ const char *regex;
ap_regex_t *r = NULL;
const command_rec *thiscmd = cmd->cmd;
ap_conf_vector_t *new_url_conf = ap_create_per_dir_config(cmd->pool);
@@ -2638,14 +2645,21 @@ static const char *urlsection(cmd_parms *cmd, void *mconfig, const char *arg)
cmd->override = OR_ALL|ACCESS_CONF;
if (thiscmd->cmd_data) { /* <LocationMatch> */
- r = ap_pregcomp(cmd->pool, cmd->path, AP_REG_EXTENDED);
+ regex = ap_getword_conf(cmd->pool, &arg);
+ r = ap_pregcomp(cmd->pool, *regex ? regex : cmd->path,
+ AP_REG_EXTENDED);
if (!r) {
return "Regex could not be compiled";
}
}
else if (!strcmp(cmd->path, "~")) {
cmd->path = ap_getword_conf(cmd->pool, &arg);
- r = ap_pregcomp(cmd->pool, cmd->path, AP_REG_EXTENDED);
+ if (!*cmd->path) {
+ return "<Location ~ > block must specify a regex";
+ }
+ regex = ap_getword_conf(cmd->pool, &arg);
+ r = ap_pregcomp(cmd->pool, *regex ? regex : cmd->path,
+ AP_REG_EXTENDED);
if (!r) {
return "Regex could not be compiled";
}
@@ -2671,8 +2685,8 @@ static const char *urlsection(cmd_parms *cmd, void *mconfig, const char *arg)
ap_add_per_url_conf(cmd->server, new_url_conf);
if (*arg != '\0') {
- return apr_pstrcat(cmd->pool, "Multiple ", thiscmd->name,
- "> arguments not (yet) supported.", NULL);
+ return apr_pstrcat(cmd->pool, "Additional ", thiscmd->name,
+ "> arguments not (yet) supported: ", arg, NULL);
}
cmd->path = old_path;
@@ -2688,6 +2702,7 @@ static const char *filesection(cmd_parms *cmd, void *mconfig, const char *arg)
int old_overrides = cmd->override;
char *old_path = cmd->path;
core_dir_config *conf;
+ const char *regex;
ap_regex_t *r = NULL;
const command_rec *thiscmd = cmd->cmd;
ap_conf_vector_t *new_file_conf = ap_create_per_dir_config(cmd->pool);
@@ -2715,14 +2730,18 @@ static const char *filesection(cmd_parms *cmd, void *mconfig, const char *arg)
}
if (thiscmd->cmd_data) { /* <FilesMatch> */
- r = ap_pregcomp(cmd->pool, cmd->path, AP_REG_EXTENDED|USE_ICASE);
+ regex = ap_getword_conf(cmd->pool, &arg);
+ r = ap_pregcomp(cmd->pool, *regex ? regex : cmd->path,
+ AP_REG_EXTENDED | USE_ICASE);
if (!r) {
return "Regex could not be compiled";
}
}
else if (!strcmp(cmd->path, "~")) {
cmd->path = ap_getword_conf(cmd->pool, &arg);
- r = ap_pregcomp(cmd->pool, cmd->path, AP_REG_EXTENDED|USE_ICASE);
+ regex = ap_getword_conf(cmd->pool, &arg);
+ r = ap_pregcomp(cmd->pool, *regex ? regex : cmd->path,
+ AP_REG_EXTENDED | USE_ICASE);
if (!r) {
return "Regex could not be compiled";
}
@@ -2758,8 +2777,8 @@ static const char *filesection(cmd_parms *cmd, void *mconfig, const char *arg)
ap_add_file_conf(cmd->pool, (core_dir_config *)mconfig, new_file_conf);
if (*arg != '\0') {
- return apr_pstrcat(cmd->pool, "Multiple ", thiscmd->name,
- "> arguments not (yet) supported.", NULL);
+ return apr_pstrcat(cmd->pool, "Additional ", thiscmd->name,
+ "> arguments not (yet) supported: ", arg, NULL);
}
cmd->path = old_path;
@@ -2845,8 +2864,8 @@ static const char *ifsection(cmd_parms *cmd, void *mconfig, const char *arg)
return errmsg;
if (*arg != '\0') {
- return apr_pstrcat(cmd->pool, "Multiple ", thiscmd->name,
- "> arguments not supported.", NULL);
+ return apr_pstrcat(cmd->pool, "Additional ", thiscmd->name,
+ "> arguments not supported: ", arg, NULL);
}
cmd->path = old_path;