From: Ruediger Pluem Date: Mon, 13 Mar 2023 10:24:30 +0000 (+0000) Subject: Do not double encode encoded slashes X-Git-Tag: 2.5.0-alpha2-ci-test-only~56 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2fa15c2c06213dc516a5383faa4bce3c74b9286a;p=thirdparty%2Fapache%2Fhttpd.git Do not double encode encoded slashes In case that AllowEncodedSlashes is set to NoDecode do not double encode encoded slashes in the URL sent by the reverse proxy to the backend. * include/ap_mmn.h: Document the addition of ap_proxy_canonenc_ex to the API. * modules/proxy/mod_proxy.h: Declare ap_proxy_canonenc_ex and define flag values. * modules/proxy/proxy_util.c: Implement ap_proxy_canonenc_ex by modifying ap_proxy_canonenc accordingly and reimplement ap_proxy_canonenc to use ap_proxy_canonenc_ex with the appropriate flag. * modules/http2/mod_proxy_http2.c, modules/proxy/mod_proxy_*.c: Set the correct flag based on the AllowEncodedSlashes configuration and use ap_proxy_canonenc_ex instead of ap_proxy_canonenc. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1908341 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/changes-entries/nodoubleencodeencodedslash.txt b/changes-entries/nodoubleencodeencodedslash.txt new file mode 100644 index 00000000000..a111ede0ed7 --- /dev/null +++ b/changes-entries/nodoubleencodeencodedslash.txt @@ -0,0 +1,4 @@ + + *) mode_proxy: In case that AllowEncodedSlashes is set to NoDecode do not + double encode encoded slashes in the URL sent by the reverse proxy to the + backend. [Ruediger Pluem] diff --git a/include/ap_mmn.h b/include/ap_mmn.h index 2f5a935a62b..2c36bddcfe5 100644 --- a/include/ap_mmn.h +++ b/include/ap_mmn.h @@ -712,6 +712,7 @@ * 20211221.7 (2.5.1-dev) Add ap_h1_append_header() * 20211221.8 (2.5.1-dev) Add ap_sb_get_child_thread() * 20211221.9 (2.5.1-dev) Add additional hcmethod_t enums and PROXY_WORKER_IS_ERROR + * 20211221.10 (2.5.1-dev) Add ap_proxy_canonenc_ex */ #define MODULE_MAGIC_COOKIE 0x41503235UL /* "AP25" */ @@ -719,7 +720,7 @@ #ifndef MODULE_MAGIC_NUMBER_MAJOR #define MODULE_MAGIC_NUMBER_MAJOR 20211221 #endif -#define MODULE_MAGIC_NUMBER_MINOR 9 /* 0...n */ +#define MODULE_MAGIC_NUMBER_MINOR 10 /* 0...n */ /** * Determine if the server's current MODULE_MAGIC_NUMBER is at least a diff --git a/modules/http2/mod_proxy_http2.c b/modules/http2/mod_proxy_http2.c index f1c1bb92f4e..94f28bebb7b 100644 --- a/modules/http2/mod_proxy_http2.c +++ b/modules/http2/mod_proxy_http2.c @@ -159,8 +159,11 @@ static int proxy_http2_canon(request_rec *r, char *url) search = r->args; } else { - path = ap_proxy_canonenc(r->pool, url, (int)strlen(url), - enc_path, 0, r->proxyreq); + core_dir_config *d = ap_get_core_module_config(r->per_dir_config); + int flags = d->allow_encoded_slashes && !d->decode_encoded_slashes ? PROXY_CANONENC_NOENCODEDSLASHENCODING : 0; + + path = ap_proxy_canonenc_ex(r->pool, url, (int)strlen(url), + enc_path, flags, r->proxyreq); search = r->args; } if (search && *ap_scan_vchar_obstext(search)) { diff --git a/modules/proxy/mod_proxy.h b/modules/proxy/mod_proxy.h index 0df3dc28013..04fee22742a 100644 --- a/modules/proxy/mod_proxy.h +++ b/modules/proxy/mod_proxy.h @@ -76,6 +76,10 @@ enum enctype { enc_path, enc_search, enc_user, enc_fpath, enc_parm }; +/* Flags for ap_proxy_canonenc_ex */ +#define PROXY_CANONENC_FORCEDEC 0x01 +#define PROXY_CANONENC_NOENCODEDSLASHENCODING 0x02 + typedef enum { NONE, TCP, OPTIONS, HEAD, GET, CPING, PROVIDER, OPTIONS11, HEAD11, GET11, EOT } hcmethod_t; @@ -693,6 +697,8 @@ PROXY_DECLARE(apr_status_t) ap_proxy_strncpy(char *dst, const char *src, apr_size_t dlen); PROXY_DECLARE(int) ap_proxy_hex2c(const char *x); PROXY_DECLARE(void) ap_proxy_c2hex(int ch, char *x); +PROXY_DECLARE(char *)ap_proxy_canonenc_ex(apr_pool_t *p, const char *x, int len, enum enctype t, + int flags, int proxyreq); PROXY_DECLARE(char *)ap_proxy_canonenc(apr_pool_t *p, const char *x, int len, enum enctype t, int forcedec, int proxyreq); PROXY_DECLARE(char *)ap_proxy_canon_netloc(apr_pool_t *p, char **const urlp, char **userp, diff --git a/modules/proxy/mod_proxy_ajp.c b/modules/proxy/mod_proxy_ajp.c index d7fd47574c6..54862ffa03e 100644 --- a/modules/proxy/mod_proxy_ajp.c +++ b/modules/proxy/mod_proxy_ajp.c @@ -70,8 +70,11 @@ static int proxy_ajp_canon(request_rec *r, char *url) search = r->args; } else { - path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0, - r->proxyreq); + core_dir_config *d = ap_get_core_module_config(r->per_dir_config); + int flags = d->allow_encoded_slashes && !d->decode_encoded_slashes ? PROXY_CANONENC_NOENCODEDSLASHENCODING : 0; + + path = ap_proxy_canonenc_ex(r->pool, url, strlen(url), enc_path, flags, + r->proxyreq); search = r->args; } if (search && *ap_scan_vchar_obstext(search)) { diff --git a/modules/proxy/mod_proxy_balancer.c b/modules/proxy/mod_proxy_balancer.c index 0d510113670..8f3a3615cfa 100644 --- a/modules/proxy/mod_proxy_balancer.c +++ b/modules/proxy/mod_proxy_balancer.c @@ -107,8 +107,11 @@ static int proxy_balancer_canon(request_rec *r, char *url) search = r->args; } else { - path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0, - r->proxyreq); + core_dir_config *d = ap_get_core_module_config(r->per_dir_config); + int flags = d->allow_encoded_slashes && !d->decode_encoded_slashes ? PROXY_CANONENC_NOENCODEDSLASHENCODING : 0; + + path = ap_proxy_canonenc_ex(r->pool, url, strlen(url), enc_path, flags, + r->proxyreq); search = r->args; } if (search && *ap_scan_vchar_obstext(search)) { diff --git a/modules/proxy/mod_proxy_fcgi.c b/modules/proxy/mod_proxy_fcgi.c index a89b9a9c7b6..dd58e1bccec 100644 --- a/modules/proxy/mod_proxy_fcgi.c +++ b/modules/proxy/mod_proxy_fcgi.c @@ -97,8 +97,11 @@ static int proxy_fcgi_canon(request_rec *r, char *url) path = url; /* this is the raw/encoded path */ } else { - path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0, - r->proxyreq); + core_dir_config *d = ap_get_core_module_config(r->per_dir_config); + int flags = d->allow_encoded_slashes && !d->decode_encoded_slashes ? PROXY_CANONENC_NOENCODEDSLASHENCODING : 0; + + path = ap_proxy_canonenc_ex(r->pool, url, strlen(url), enc_path, flags, + r->proxyreq); } if (path == NULL) return HTTP_BAD_REQUEST; diff --git a/modules/proxy/mod_proxy_ftp.c b/modules/proxy/mod_proxy_ftp.c index a63b09929b6..8b75d97c7e5 100644 --- a/modules/proxy/mod_proxy_ftp.c +++ b/modules/proxy/mod_proxy_ftp.c @@ -289,6 +289,8 @@ static int proxy_ftp_canon(request_rec *r, char *url) apr_pool_t *p = r->pool; const char *err; apr_port_t port, def_port; + core_dir_config *d = ap_get_core_module_config(r->per_dir_config); + int flags = d->allow_encoded_slashes && !d->decode_encoded_slashes ? PROXY_CANONENC_NOENCODEDSLASHENCODING : 0; /* */ if (ap_cstr_casecmpn(url, "ftp:", 4) == 0) { @@ -327,7 +329,8 @@ static int proxy_ftp_canon(request_rec *r, char *url) else parms = ""; - path = ap_proxy_canonenc(p, url, strlen(url), enc_path, 0, r->proxyreq); + path = ap_proxy_canonenc_ex(p, url, strlen(url), enc_path, flags, + r->proxyreq); if (path == NULL) return HTTP_BAD_REQUEST; if (!ftp_check_string(path)) diff --git a/modules/proxy/mod_proxy_http.c b/modules/proxy/mod_proxy_http.c index 1ec5fe11340..23c5b691f76 100644 --- a/modules/proxy/mod_proxy_http.c +++ b/modules/proxy/mod_proxy_http.c @@ -123,8 +123,11 @@ static int proxy_http_canon(request_rec *r, char *url) search = r->args; } else { - path = ap_proxy_canonenc(r->pool, url, strlen(url), - enc_path, 0, r->proxyreq); + core_dir_config *d = ap_get_core_module_config(r->per_dir_config); + int flags = d->allow_encoded_slashes && !d->decode_encoded_slashes ? PROXY_CANONENC_NOENCODEDSLASHENCODING : 0; + + path = ap_proxy_canonenc_ex(r->pool, url, strlen(url), enc_path, + flags, r->proxyreq); search = r->args; } if (search && *ap_scan_vchar_obstext(search)) { diff --git a/modules/proxy/mod_proxy_scgi.c b/modules/proxy/mod_proxy_scgi.c index 493757d3c92..4674ccf9065 100644 --- a/modules/proxy/mod_proxy_scgi.c +++ b/modules/proxy/mod_proxy_scgi.c @@ -179,6 +179,8 @@ static int scgi_canon(request_rec *r, char *url) char *host, sport[sizeof(":65535")]; const char *err, *path; apr_port_t port, def_port; + core_dir_config *d = ap_get_core_module_config(r->per_dir_config); + int flags = d->allow_encoded_slashes && !d->decode_encoded_slashes ? PROXY_CANONENC_NOENCODEDSLASHENCODING : 0; if (ap_cstr_casecmpn(url, SCHEME "://", sizeof(SCHEME) + 2)) { return DECLINED; @@ -205,8 +207,8 @@ static int scgi_canon(request_rec *r, char *url) host = apr_pstrcat(r->pool, "[", host, "]", NULL); } - path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0, - r->proxyreq); + path = ap_proxy_canonenc_ex(r->pool, url, strlen(url), enc_path, flags, + r->proxyreq); if (!path) { return HTTP_BAD_REQUEST; } diff --git a/modules/proxy/mod_proxy_uwsgi.c b/modules/proxy/mod_proxy_uwsgi.c index 720dfa84871..1a56bb88b59 100644 --- a/modules/proxy/mod_proxy_uwsgi.c +++ b/modules/proxy/mod_proxy_uwsgi.c @@ -89,8 +89,11 @@ static int uwsgi_canon(request_rec *r, char *url) path = url; /* this is the raw/encoded path */ } else { - path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0, - r->proxyreq); + core_dir_config *d = ap_get_core_module_config(r->per_dir_config); + int flags = d->allow_encoded_slashes && !d->decode_encoded_slashes ? PROXY_CANONENC_NOENCODEDSLASHENCODING : 0; + + path = ap_proxy_canonenc_ex(r->pool, url, strlen(url), enc_path, flags, + r->proxyreq); } if (!path) { return HTTP_BAD_REQUEST; diff --git a/modules/proxy/mod_proxy_wstunnel.c b/modules/proxy/mod_proxy_wstunnel.c index d1db872e119..81f932a864e 100644 --- a/modules/proxy/mod_proxy_wstunnel.c +++ b/modules/proxy/mod_proxy_wstunnel.c @@ -200,8 +200,11 @@ static int proxy_wstunnel_canon(request_rec *r, char *url) search = r->args; } else { - path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0, - r->proxyreq); + core_dir_config *d = ap_get_core_module_config(r->per_dir_config); + int flags = d->allow_encoded_slashes && !d->decode_encoded_slashes ? PROXY_CANONENC_NOENCODEDSLASHENCODING : 0; + + path = ap_proxy_canonenc_ex(r->pool, url, strlen(url), enc_path, flags, + r->proxyreq); search = r->args; } if (search && *ap_scan_vchar_obstext(search)) { diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c index 3dacec38ee2..7d0062b89ff 100644 --- a/modules/proxy/proxy_util.c +++ b/modules/proxy/proxy_util.c @@ -200,14 +200,16 @@ PROXY_DECLARE(void) ap_proxy_c2hex(int ch, char *x) * and encodes those which must be encoded, and does not touch * those which must not be touched. */ -PROXY_DECLARE(char *)ap_proxy_canonenc(apr_pool_t *p, const char *x, int len, - enum enctype t, int forcedec, - int proxyreq) +PROXY_DECLARE(char *)ap_proxy_canonenc_ex(apr_pool_t *p, const char *x, int len, + enum enctype t, int flags, + int proxyreq) { int i, j, ch; char *y; char *allowed; /* characters which should not be encoded */ char *reserved; /* characters which much not be en/de-coded */ + int forcedec = flags & PROXY_CANONENC_FORCEDEC; + int noencslashesenc = flags & PROXY_CANONENC_NOENCODEDSLASHENCODING; /* * N.B. in addition to :@&=, this allows ';' in an http path @@ -256,7 +258,8 @@ PROXY_DECLARE(char *)ap_proxy_canonenc(apr_pool_t *p, const char *x, int len, * decode it if not already done. do not decode reverse proxied URLs * unless specifically forced */ - if ((forcedec || (proxyreq && proxyreq != PROXYREQ_REVERSE)) && ch == '%') { + if ((forcedec || noencslashesenc + ||(proxyreq && proxyreq != PROXYREQ_REVERSE)) && ch == '%') { if (!apr_isxdigit(x[i + 1]) || !apr_isxdigit(x[i + 2])) { return NULL; } @@ -267,7 +270,17 @@ PROXY_DECLARE(char *)ap_proxy_canonenc(apr_pool_t *p, const char *x, int len, y[j] = x[i]; continue; } - i += 2; + if (noencslashesenc && !forcedec && (proxyreq == PROXYREQ_REVERSE)) { + /* + * In the reverse proxy case when we only want to keep encoded + * slashes untouched revert back to '%' which will cause + * '%' to be encoded in the following. + */ + ch = '%'; + } + else { + i += 2; + } } /* recode it, if necessary */ if (!apr_isalnum(ch) && !strchr(allowed, ch)) { @@ -282,6 +295,22 @@ PROXY_DECLARE(char *)ap_proxy_canonenc(apr_pool_t *p, const char *x, int len, return y; } +/* + * Convert a URL-encoded string to canonical form. + * It decodes characters which need not be encoded, + * and encodes those which must be encoded, and does not touch + * those which must not be touched. + */ +PROXY_DECLARE(char *)ap_proxy_canonenc(apr_pool_t *p, const char *x, int len, + enum enctype t, int forcedec, + int proxyreq) +{ + int flags; + + flags = forcedec ? PROXY_CANONENC_FORCEDEC : 0; + return ap_proxy_canonenc_ex(p, x, len, t, flags, proxyreq); +} + /* * Parses network-location. * urlp on input the URL; on output the path, after the leading /