I've attached a patch against the trunk of Apache 1.3 that backports
support for the AllowEncodedSlashes directive. It should behave
identically to the way it works in 2.0. By default Apache will disallow
any request that includes a %-encoded slash ('/') character (which
is '%2F'), but by enabling this directive an administrator can override
this prevention and allow %2Fs in request URLs. If this is an acceptable
backport, and I can get some +1s for it, I'll be happy to commit it and
update the documentation (at least the English :).

-aaron

Index: src/include/httpd.h
===================================================================
--- src/include/httpd.h (revision 158971)
+++ src/include/httpd.h (working copy)
@@ -976,6 +976,7 @@

 API_EXPORT(int) ap_is_url(const char *u);
 API_EXPORT(int) ap_unescape_url(char *url);
+API_EXPORT(int) ap_unescape_url_keep2f(char *url);
 API_EXPORT(void) ap_no2slash(char *name);
 API_EXPORT(void) ap_getparents(char *name);
 API_EXPORT(char *) ap_escape_path_segment(pool *p, const char *s);
Index: src/include/http_core.h
===================================================================
--- src/include/http_core.h     (revision 158971)
+++ src/include/http_core.h     (working copy)
@@ -318,6 +318,8 @@
     /* Digest auth. */
     char *ap_auth_nonce;

+ unsigned int allow_encoded_slashes : 1; /* URLs may contain %2f w/o being
+ * pitched indiscriminately */
} core_dir_config;


 /* Per-server core configuration */
Index: src/main/util.c
===================================================================
--- src/main/util.c     (revision 158971)
+++ src/main/util.c     (working copy)
@@ -1635,6 +1635,53 @@
        return OK;
 }

+API_EXPORT(int) ap_unescape_url_keep2f(char *url)
+{
+    register int badesc, badpath;
+    char *x, *y;
+
+    badesc = 0;
+    badpath = 0;
+    /* Initial scan for first '%'. Don't bother writing values before
+     * seeing a '%' */
+    y = strchr(url, '%');
+    if (y == NULL) {
+        return OK;
+    }
+    for (x = y; *y; ++x, ++y) {
+        if (*y != '%') {
+            *x = *y;
+        }
+        else {
+            if (!ap_isxdigit(*(y + 1)) || !ap_isxdigit(*(y + 2))) {
+                badesc = 1;
+                *x = '%';
+            }
+            else {
+                char decoded;
+                decoded = x2c(y + 1);
+                if (decoded == '\0') {
+                    badpath = 1;
+                }
+                else {
+                    *x = decoded;
+                    y += 2;
+                }
+            }
+        }
+    }
+    *x = '\0';
+    if (badesc) {
+        return BAD_REQUEST;
+    }
+    else if (badpath) {
+        return NOT_FOUND;
+    }
+    else {
+        return OK;
+    }
+}
+
 API_EXPORT(char *) ap_construct_server(pool *p, const char *hostname,
                                    unsigned port, const request_rec *r)
 {
Index: src/main/http_request.c
===================================================================
--- src/main/http_request.c     (revision 158971)
+++ src/main/http_request.c     (working copy)
@@ -1175,8 +1175,21 @@

     /* Ignore embedded %2F's in path for proxy requests */
     if (r->proxyreq == NOT_PROXY && r->parsed_uri.path) {
-       access_status = ap_unescape_url(r->parsed_uri.path);
+       core_dir_config *d;
+       d = ap_get_module_config(r->per_dir_config, &core_module);
+       if (d->allow_encoded_slashes) {
+           access_status = ap_unescape_url_keep2f(r->parsed_uri.path);
+       }
+       else {
+           access_status = ap_unescape_url(r->parsed_uri.path);
+       }
        if (access_status) {
+           if (! d->allow_encoded_slashes) {
+               ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, r,
+                             "found %%2f (encoded '/') in URI "
+                             "(decoded='%s'), returning 404",
+                             r->parsed_uri.path);
+           }
            ap_die(access_status, r);
            return;
        }
Index: src/main/http_core.c
===================================================================
--- src/main/http_core.c        (revision 158971)
+++ src/main/http_core.c        (working copy)
@@ -143,6 +143,9 @@
     conf->etag_add = ETAG_UNSET;
     conf->etag_remove = ETAG_UNSET;

+    /* disallow %2f (encoded '/') by default */
+    conf->allow_encoded_slashes = 0;
+
     return (void *)conf;
 }

@@ -319,6 +322,8 @@
         conf->cgi_command_args = new->cgi_command_args;
     }

+    conf->allow_encoded_slashes = new->allow_encoded_slashes;
+
     return (void*)conf;
 }

@@ -2309,6 +2314,18 @@
 }
 #endif /* AP_ENABLE_EXCEPTION_HOOK */

+static const char *set_allow2f(cmd_parms *cmd, core_dir_config *d, int arg)
+{
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+
+ if (err != NULL) {
+ return err;
+ }
+
+ d->allow_encoded_slashes = (arg != 0);
+ return NULL;
+}
+
static const char *set_pidfile(cmd_parms *cmd, void *dummy, char *arg)
{
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
@@ -3551,6 +3568,8 @@
{ "ErrorLog", set_server_string_slot,
(void *)XtOffsetOf (server_rec, error_fname), RSRC_CONF, TAKE1,
"The filename of the error log" },
+{ "AllowEncodedSlashes", set_allow2f, NULL, RSRC_CONF, FLAG,
+ "Allow URLs containing '/' encoded as '%2F'"},
{ "PidFile", set_pidfile, NULL, RSRC_CONF, TAKE1,
"A file for logging the server process ID"},
{ "ScoreBoardFile", set_scoreboard, NULL, RSRC_CONF, TAKE1,




Reply via email to