From c51dccd833ccaec00df9420701c879ca6fb0e3b4 Mon Sep 17 00:00:00 2001
From: Graham Leggett
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
+
Note that the default access for
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. 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 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
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. 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 "/">
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.