Changes with Apache 2.3.0
[ When backported to 2.2.x, remove entry from this file ]
+ *) mod_proxy: add "nocanon" keyword to ProxyPass, to suppress
+ URI-canonicalisation in a reverse proxy.
+ PR 41798 [Nick Kew]
+
*) core; scoreboard: ap_get_scoreboard_worker(sbh) now takes the sbh member
from the connection rec, ap_get_scoreboard_worker(proc, thread) will now
provide the unusual legacy lookup. [William Rowe]
- *) mod_proxy_http: Check but don't escape/unescape forward-proxied URLs
+ *) mod_proxy_http: Don't escape/unescape forward-proxied URLs
PR 42592 [Nick Kew]
*) mpm winnt: fix null pointer dereference
the entity.
PR 39727 [Nick Kew]
- *) mod_proxy_http: Remove Warning headers with wrong date
- PR 16138 [Nick Kew]
-
- *) mod_proxy_http: Correctly parse all Connection headers in proxy.
- PR 43509 [Nick Kew]
-
*) HTTP protocol: Add "DefaultType none" option.
PR 13986 and PR 16139 [Nick Kew]
<name>ProxyPass</name>
<description>Maps remote servers into the local server URL-space</description>
<syntax>ProxyPass [<var>path</var>] !|<var>url</var> [<var>key=value</var>
- <var>[key=value</var> ...]]</syntax>
+ <var>[key=value</var> ...]] [nocanon]</syntax>
<contextlist><context>server config</context><context>virtual host</context>
<context>directory</context>
</contextlist>
</Proxy>
</example>
+ <p>Normally, mod_proxy will canonicalise ProxyPassed URLs.
+ But this may be incompatible with some backends, particularly those
+ that make use of <var>PATH_INFO</var>. The optional <var>nocanon</var>
+ keyword suppresses this, and passes the URL path "raw" to the
+ backend. Note that may affect the security of your backend, as it
+ removes the normal limited protection against URL-based attacks
+ provided by the proxy.</p>
<p>When used inside a <directive type="section" module="core"
>Location</directive> section, the first argument is omitted and the local
else
worker->status &= ~PROXY_WORKER_HOT_STANDBY;
}
- else if (*v == 'I' || *v == 'i') {
- if (mode)
- worker->status |= PROXY_WORKER_IGNORE_ERRORS;
- else
- worker->status &= ~PROXY_WORKER_IGNORE_ERRORS;
- }
+ else if (*v == 'I' || *v == 'i') {
+ if (mode)
+ worker->status |= PROXY_WORKER_IGNORE_ERRORS;
+ else
+ worker->status &= ~PROXY_WORKER_IGNORE_ERRORS;
+ }
else {
return "Unknown status parameter option";
}
const char *fake;
const char *real;
ap_regmatch_t regm[AP_MAX_REG_MATCH];
+ ap_regmatch_t reg1[AP_MAX_REG_MATCH];
char *found = NULL;
+ int mismatch = 0;
if (r->proxyreq) {
/* someone has already set up the proxy, it was possibly ourselves
*/
for (i = 0; i < conf->aliases->nelts; i++) {
+ unsigned int nocanon = ent[i].flags & PROXYPASS_NOCANON;
+ const char *use_uri = nocanon ? r->unparsed_uri : r->uri;
if (dconf->interpolate_env == 1) {
fake = proxy_interpolate(r, ent[i].fake);
real = proxy_interpolate(r, ent[i].real);
if ((real[0] == '!') && (real[1] == '\0')) {
return DECLINED;
}
- found = ap_pregsub(r->pool, real, r->uri, AP_MAX_REG_MATCH,
- regm);
+ /* test that we haven't reduced the URI */
+ if (nocanon && ap_regexec(ent[i].regex, r->unparsed_uri,
+ AP_MAX_REG_MATCH, reg1, 0)) {
+ mismatch = 1;
+ use_uri = r->uri;
+ }
+ found = ap_pregsub(r->pool, real, use_uri, AP_MAX_REG_MATCH,
+ (use_uri == r->uri) ? regm : reg1);
/* Note: The strcmp() below catches cases where there
* was no regex substitution. This is so cases like:
*
found = apr_pstrcat(r->pool, "proxy:", found, NULL);
}
else {
- found = apr_pstrcat(r->pool, "proxy:", real, r->uri,
- NULL);
+ found = apr_pstrcat(r->pool, "proxy:", real,
+ use_uri, NULL);
}
}
}
if ((real[0] == '!') && (real[1] == '\0')) {
return DECLINED;
}
-
+ if (nocanon
+ && len != alias_match(r->unparsed_uri, ent[i].fake)) {
+ mismatch = 1;
+ use_uri = r->uri;
+ }
found = apr_pstrcat(r->pool, "proxy:", real,
- r->uri + len, NULL);
-
+ use_uri + len, NULL);
}
}
+ if (mismatch) {
+ /* We made a reducing transformation, so we can't safely use
+ * unparsed_uri. Safe fallback is to ignore nocanon.
+ */
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
+ "Unescaped URL path matched ProxyPass; ignoring unsafe nocanon");
+ }
+
if (found) {
r->filename = found;
r->handler = "proxy-server";
r->proxyreq = PROXYREQ_REVERSE;
+ if (nocanon && !mismatch) {
+ /* mod_proxy_http needs to be told. Different module. */
+ apr_table_setn(r->notes, "proxy-nocanon", "1");
+ }
return OK;
}
}
const apr_table_entry_t *elts;
int i;
int use_regex = is_regex;
+ unsigned int flags = 0;
while (*arg) {
word = ap_getword_conf(cmd->pool, &arg);
}
f = word;
}
- else if (!r)
+ else if (!r) {
r = word;
+ }
+ else if (!strcasecmp(word,"nocanon")) {
+ flags |= PROXYPASS_NOCANON;
+ }
else {
char *val = strchr(word, '=');
if (!val) {
new = apr_array_push(conf->aliases);
new->fake = apr_pstrdup(cmd->pool, f);
new->real = apr_pstrdup(cmd->pool, r);
+ new->flags = flags;
if (use_regex) {
new->regex = ap_pregcomp(cmd->pool, f, AP_REG_EXTENDED);
if (new->regex == NULL)
/* process path */
/* In a reverse proxy, our URL has been processed, so canonicalise
- * In a forward proxy, we have and MUST NOT MANGLE the original,
- * so just check it for disallowed chars.
+ * unless proxy-nocanon is set to say it's raw
+ * In a forward proxy, we have and MUST NOT MANGLE the original.
*/
switch (r->proxyreq) {
default: /* wtf are we doing here? */
case PROXYREQ_REVERSE:
- path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0, r->proxyreq);
+ if (apr_table_get(r->notes, "proxy-nocanon")) {
+ path = url; /* this is the raw path */
+ }
+ else {
+ path = ap_proxy_canonenc(r->pool, url, strlen(url),
+ enc_path, 0, r->proxyreq);
+ }
break;
case PROXYREQ_PROXY:
path = url;