From b1a1c1d95059e2fefd7b5671eb110ab690409a84 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 25 May 2024 06:41:28 +0200 Subject: Merging upstream version 2.4.59. Signed-off-by: Daniel Baumann --- modules/mappers/mod_rewrite.c | 288 +++++++++++++++++++++++++++++------------- 1 file changed, 198 insertions(+), 90 deletions(-) (limited to 'modules/mappers/mod_rewrite.c') diff --git a/modules/mappers/mod_rewrite.c b/modules/mappers/mod_rewrite.c index 68a33b6..bbcc11b 100644 --- a/modules/mappers/mod_rewrite.c +++ b/modules/mappers/mod_rewrite.c @@ -55,6 +55,12 @@ #include "apr_global_mutex.h" #include "apr_dbm.h" #include "apr_dbd.h" + +#include "apr_version.h" +#if !APR_VERSION_AT_LEAST(2,0,0) +#include "apu_version.h" +#endif + #include "mod_dbd.h" #if APR_HAS_THREADS @@ -93,14 +99,15 @@ #include "http_core.h" #include "http_log.h" #include "http_protocol.h" +#include "http_ssl.h" #include "http_vhost.h" #include "util_mutex.h" -#include "mod_ssl.h" - #include "mod_rewrite.h" #include "ap_expr.h" +#include "test_char.h" + static ap_dbd_t *(*dbd_acquire)(request_rec*) = NULL; static void (*dbd_prepare)(server_rec*, const char*, const char*) = NULL; static const char* really_last_key = "rewrite_really_last"; @@ -168,6 +175,8 @@ static const char* really_last_key = "rewrite_really_last"; #define RULEFLAG_END (1<<17) #define RULEFLAG_ESCAPENOPLUS (1<<18) #define RULEFLAG_QSLAST (1<<19) +#define RULEFLAG_QSNONE (1<<20) /* programattic only */ +#define RULEFLAG_ESCAPECTLS (1<<21) /* return code of the rewrite rule * the result may be escaped - or not @@ -321,7 +330,8 @@ typedef struct { data_item *cookie; /* added cookies */ int skip; /* number of next rules to skip */ int maxrounds; /* limit on number of loops with N flag */ - char *escapes; /* specific backref escapes */ + const char *escapes; /* specific backref escapes */ + const char *noescapes; /* specific backref chars not to escape */ } rewriterule_entry; typedef struct { @@ -421,9 +431,9 @@ static apr_global_mutex_t *rewrite_mapr_lock_acquire = NULL; static const char *rewritemap_mutex_type = "rewrite-map"; /* Optional functions imported from mod_ssl when loaded: */ -static APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *rewrite_ssl_lookup = NULL; -static APR_OPTIONAL_FN_TYPE(ssl_is_https) *rewrite_is_https = NULL; -static char *escape_backref(apr_pool_t *p, const char *path, const char *escapeme, int noplus); +static char *escape_backref(apr_pool_t *p, const char *path, + const char *escapeme, const char *noescapeme, + int flags); /* * +-------------------------------------------------------+ @@ -524,7 +534,7 @@ static unsigned is_absolute_uri(char *uri, int *supportsqs) switch (*uri++) { case 'a': case 'A': - if (!strncasecmp(uri, "jp://", 5)) { /* ajp:// */ + if (!ap_cstr_casecmpn(uri, "jp://", 5)) { /* ajp:// */ *sqs = 1; return 6; } @@ -532,7 +542,7 @@ static unsigned is_absolute_uri(char *uri, int *supportsqs) case 'b': case 'B': - if (!strncasecmp(uri, "alancer://", 10)) { /* balancer:// */ + if (!ap_cstr_casecmpn(uri, "alancer://", 10)) { /* balancer:// */ *sqs = 1; return 11; } @@ -540,10 +550,10 @@ static unsigned is_absolute_uri(char *uri, int *supportsqs) case 'f': case 'F': - if (!strncasecmp(uri, "tp://", 5)) { /* ftp:// */ + if (!ap_cstr_casecmpn(uri, "tp://", 5)) { /* ftp:// */ return 6; } - if (!strncasecmp(uri, "cgi://", 6)) { /* fcgi:// */ + if (!ap_cstr_casecmpn(uri, "cgi://", 6)) { /* fcgi:// */ *sqs = 1; return 7; } @@ -551,26 +561,26 @@ static unsigned is_absolute_uri(char *uri, int *supportsqs) case 'g': case 'G': - if (!strncasecmp(uri, "opher://", 8)) { /* gopher:// */ + if (!ap_cstr_casecmpn(uri, "opher://", 8)) { /* gopher:// */ return 9; } break; case 'h': case 'H': - if (!strncasecmp(uri, "ttp://", 6)) { /* http:// */ + if (!ap_cstr_casecmpn(uri, "ttp://", 6)) { /* http:// */ *sqs = 1; return 7; } - else if (!strncasecmp(uri, "ttps://", 7)) { /* https:// */ + else if (!ap_cstr_casecmpn(uri, "ttps://", 7)) { /* https:// */ *sqs = 1; return 8; } - else if (!strncasecmp(uri, "2://", 4)) { /* h2:// */ + else if (!ap_cstr_casecmpn(uri, "2://", 4)) { /* h2:// */ *sqs = 1; return 5; } - else if (!strncasecmp(uri, "2c://", 5)) { /* h2c:// */ + else if (!ap_cstr_casecmpn(uri, "2c://", 5)) { /* h2c:// */ *sqs = 1; return 6; } @@ -578,14 +588,14 @@ static unsigned is_absolute_uri(char *uri, int *supportsqs) case 'l': case 'L': - if (!strncasecmp(uri, "dap://", 6)) { /* ldap:// */ + if (!ap_cstr_casecmpn(uri, "dap://", 6)) { /* ldap:// */ return 7; } break; case 'm': case 'M': - if (!strncasecmp(uri, "ailto:", 6)) { /* mailto: */ + if (!ap_cstr_casecmpn(uri, "ailto:", 6)) { /* mailto: */ *sqs = 1; return 7; } @@ -593,17 +603,17 @@ static unsigned is_absolute_uri(char *uri, int *supportsqs) case 'n': case 'N': - if (!strncasecmp(uri, "ews:", 4)) { /* news: */ + if (!ap_cstr_casecmpn(uri, "ews:", 4)) { /* news: */ return 5; } - else if (!strncasecmp(uri, "ntp://", 6)) { /* nntp:// */ + else if (!ap_cstr_casecmpn(uri, "ntp://", 6)) { /* nntp:// */ return 7; } break; case 's': case 'S': - if (!strncasecmp(uri, "cgi://", 6)) { /* scgi:// */ + if (!ap_cstr_casecmpn(uri, "cgi://", 6)) { /* scgi:// */ *sqs = 1; return 7; } @@ -611,15 +621,22 @@ static unsigned is_absolute_uri(char *uri, int *supportsqs) case 'w': case 'W': - if (!strncasecmp(uri, "s://", 4)) { /* ws:// */ + if (!ap_cstr_casecmpn(uri, "s://", 4)) { /* ws:// */ *sqs = 1; return 5; } - else if (!strncasecmp(uri, "ss://", 5)) { /* wss:// */ + else if (!ap_cstr_casecmpn(uri, "ss://", 5)) { /* wss:// */ *sqs = 1; return 6; } break; + + case 'u': + case 'U': + if (!ap_cstr_casecmpn(uri, "nix:", 4)) { /* unix: */ + *sqs = 1; + return (uri[4] == '/' && uri[5] == '/') ? 7 : 5; + } } return 0; @@ -643,14 +660,21 @@ static APR_INLINE unsigned char *c2x(unsigned what, unsigned char prefix, * Escapes a backreference in a similar way as php's urlencode does. * Based on ap_os_escape_path in server/util.c */ -static char *escape_backref(apr_pool_t *p, const char *path, const char *escapeme, int noplus) { - char *copy = apr_palloc(p, 3 * strlen(path) + 3); +static char *escape_backref(apr_pool_t *p, const char *path, + const char *escapeme, const char *noescapeme, + int flags) +{ + char *copy = apr_palloc(p, 3 * strlen(path) + 1); const unsigned char *s = (const unsigned char *)path; unsigned char *d = (unsigned char *)copy; - unsigned c; + int noplus = (flags & RULEFLAG_ESCAPENOPLUS) != 0; + int ctls = (flags & RULEFLAG_ESCAPECTLS) != 0; + unsigned char c; while ((c = *s)) { - if (!escapeme) { + if (((ctls ? !TEST_CHAR(c, T_VCHAR_OBSTEXT) : !escapeme) + || (escapeme && ap_strchr_c(escapeme, c))) + && (!noescapeme || !ap_strchr_c(noescapeme, c))) { if (apr_isalnum(c) || c == '_') { *d++ = c; } @@ -661,23 +685,8 @@ static char *escape_backref(apr_pool_t *p, const char *path, const char *escapem d = c2x(c, '%', d); } } - else { - const char *esc = escapeme; - while (*esc) { - if (c == *esc) { - if (c == ' ' && !noplus) { - *d++ = '+'; - } - else { - d = c2x(c, '%', d); - } - break; - } - ++esc; - } - if (!*esc) { - *d++ = c; - } + else { + *d++ = c; } ++s; } @@ -723,7 +732,7 @@ static char *escape_absolute_uri(apr_pool_t *p, char *uri, unsigned scheme) * [dn ["?" [attributes] ["?" [scope] * ["?" [filter] ["?" extensions]]]]]] */ - if (!strncasecmp(uri, "ldap", 4)) { + if (!ap_cstr_casecmpn(uri, "ldap", 4)) { char *token[5]; int c = 0; @@ -759,27 +768,35 @@ static char *escape_absolute_uri(apr_pool_t *p, char *uri, unsigned scheme) * split out a QUERY_STRING part from * the current URI string */ -static void splitout_queryargs(request_rec *r, int qsappend, int qsdiscard, - int qslast) +static void splitout_queryargs(request_rec *r, int flags) { char *q; - int split; + int split, skip; + int qsappend = flags & RULEFLAG_QSAPPEND; + int qsdiscard = flags & RULEFLAG_QSDISCARD; + int qslast = flags & RULEFLAG_QSLAST; + + if (flags & RULEFLAG_QSNONE) { + rewritelog((r, 2, NULL, "discarding query string, no parse from substitution")); + r->args = NULL; + return; + } /* don't touch, unless it's a scheme for which a query string makes sense. * See RFC 1738 and RFC 2368. */ - if (is_absolute_uri(r->filename, &split) + if ((skip = is_absolute_uri(r->filename, &split)) && !split) { r->args = NULL; /* forget the query that's still flying around */ return; } - if ( qsdiscard ) { + if (qsdiscard) { r->args = NULL; /* Discard query string */ rewritelog((r, 2, NULL, "discarding query string")); } - q = qslast ? ap_strrchr(r->filename, '?') : ap_strchr(r->filename, '?'); + q = qslast ? ap_strrchr(r->filename + skip, '?') : ap_strchr(r->filename + skip, '?'); if (q != NULL) { char *olduri; @@ -788,7 +805,7 @@ static void splitout_queryargs(request_rec *r, int qsappend, int qsdiscard, olduri = apr_pstrdup(r->pool, r->filename); *q++ = '\0'; if (qsappend) { - if (*q) { + if (*q) { r->args = apr_pstrcat(r->pool, q, "&" , r->args, NULL); } } @@ -796,9 +813,9 @@ static void splitout_queryargs(request_rec *r, int qsappend, int qsdiscard, r->args = apr_pstrdup(r->pool, q); } - if (r->args) { + if (r->args) { len = strlen(r->args); - + if (!len) { r->args = NULL; } @@ -810,8 +827,6 @@ static void splitout_queryargs(request_rec *r, int qsappend, int qsdiscard, rewritelog((r, 3, NULL, "split uri=%s -> uri=%s, args=%s", olduri, r->filename, r->args ? r->args : "")); } - - return; } /* @@ -825,7 +840,7 @@ static void reduce_uri(request_rec *r) cp = (char *)ap_http_scheme(r); l = strlen(cp); if ( strlen(r->filename) > l+3 - && strncasecmp(r->filename, cp, l) == 0 + && ap_cstr_casecmpn(r->filename, cp, l) == 0 && r->filename[l] == ':' && r->filename[l+1] == '/' && r->filename[l+2] == '/' ) { @@ -1029,6 +1044,7 @@ static void set_cache_value(const char *name, apr_time_t t, char *key, #endif return; } + apr_pool_tag(p, "rewrite_cachedmap"); map = apr_palloc(cachep->pool, sizeof(cachedmap)); map->pool = p; @@ -1106,6 +1122,7 @@ static int init_cache(apr_pool_t *p) cachep = NULL; /* turns off cache */ return 0; } + apr_pool_tag(cachep->pool, "rewrite_cachep"); cachep->maps = apr_hash_make(cachep->pool); #if APR_HAS_THREADS @@ -1353,12 +1370,31 @@ static char *lookup_map_txtfile(request_rec *r, const char *file, char *key) static char *lookup_map_dbmfile(request_rec *r, const char *file, const char *dbmtype, char *key) { +#if APU_MAJOR_VERSION > 1 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 7) + const apr_dbm_driver_t *driver; + const apu_err_t *err; +#endif apr_dbm_t *dbmfp = NULL; apr_datum_t dbmkey; apr_datum_t dbmval; char *value; apr_status_t rv; +#if APU_MAJOR_VERSION > 1 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 7) + if ((rv = apr_dbm_get_driver(&driver, dbmtype, &err, + r->pool)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(10287) + "mod_rewrite: can't load DBM library '%s': %s", + err->reason, err->msg); + return NULL; + } + if ((rv = apr_dbm_open2(&dbmfp, driver, file, APR_DBM_READONLY, + APR_OS_DEFAULT, r->pool)) != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(00656) + "mod_rewrite: can't open DBM RewriteMap %s", file); + return NULL; + } +#else if ((rv = apr_dbm_open_ex(&dbmfp, dbmtype, file, APR_DBM_READONLY, APR_OS_DEFAULT, r->pool)) != APR_SUCCESS) { @@ -1366,6 +1402,7 @@ static char *lookup_map_dbmfile(request_rec *r, const char *file, "mod_rewrite: can't open DBM RewriteMap %s", file); return NULL; } +#endif dbmkey.dptr = key; dbmkey.dsize = strlen(key); @@ -1864,8 +1901,8 @@ static char *lookup_variable(char *var, rewrite_ctx *ctx) result = getenv(var); } } - else if (var[4] && !strncasecmp(var, "SSL", 3) && rewrite_ssl_lookup) { - result = rewrite_ssl_lookup(r->pool, r->server, r->connection, r, + else if (var[4] && !strncasecmp(var, "SSL", 3)) { + result = ap_ssl_var_lookup(r->pool, r->server, r->connection, r, var + 4); } } @@ -1963,7 +2000,7 @@ static char *lookup_variable(char *var, rewrite_ctx *ctx) case 5: if (!strcmp(var, "HTTPS")) { - int flag = rewrite_is_https && rewrite_is_https(r->connection); + int flag = ap_ssl_conn_is_ssl(r->connection); return apr_pstrdup(r->pool, flag ? "on" : "off"); } break; @@ -2430,7 +2467,8 @@ static char *do_expand(char *input, rewrite_ctx *ctx, rewriterule_entry *entry) /* escape the backreference */ char *tmp2, *tmp; tmp = apr_pstrmemdup(pool, bri->source + bri->regmatch[n].rm_so, span); - tmp2 = escape_backref(pool, tmp, entry->escapes, entry->flags & RULEFLAG_ESCAPENOPLUS); + tmp2 = escape_backref(pool, tmp, entry->escapes, entry->noescapes, + entry->flags); rewritelog((ctx->r, 5, ctx->perdir, "escaping backreference '%s' to '%s'", tmp, tmp2)); @@ -2538,6 +2576,7 @@ static void add_cookie(request_rec *r, char *s) char *path; char *secure; char *httponly; + char *samesite; char *tok_cntx; char *cookie; @@ -2572,6 +2611,7 @@ static void add_cookie(request_rec *r, char *s) path = expires ? apr_strtok(NULL, sep, &tok_cntx) : NULL; secure = path ? apr_strtok(NULL, sep, &tok_cntx) : NULL; httponly = secure ? apr_strtok(NULL, sep, &tok_cntx) : NULL; + samesite = httponly ? apr_strtok(NULL, sep, &tok_cntx) : NULL; if (expires) { apr_time_exp_t tms; @@ -2599,18 +2639,23 @@ static void add_cookie(request_rec *r, char *s) : NULL, expires ? (exp_time ? exp_time : "") : NULL, - (secure && (!strcasecmp(secure, "true") + (secure && (!ap_cstr_casecmp(secure, "true") || !strcmp(secure, "1") - || !strcasecmp(secure, + || !ap_cstr_casecmp(secure, "secure"))) ? "; secure" : NULL, - (httponly && (!strcasecmp(httponly, "true") + (httponly && (!ap_cstr_casecmp(httponly, "true") || !strcmp(httponly, "1") - || !strcasecmp(httponly, + || !ap_cstr_casecmp(httponly, "HttpOnly"))) ? "; HttpOnly" : NULL, NULL); + if (samesite && strcmp(samesite, "0") && ap_cstr_casecmp(samesite,"false")) { + cookie = apr_pstrcat(rmain->pool, cookie, "; SameSite=", + samesite, NULL); + } + apr_table_addn(rmain->err_headers_out, "Set-Cookie", cookie); apr_pool_userdata_set("set", notename, NULL, rmain->pool); rewritelog((rmain, 5, NULL, "setting cookie '%s'", cookie)); @@ -2724,7 +2769,7 @@ static apr_status_t rewritelock_remove(void *data) * XXX: what an inclined parser. Seems we have to leave it so * for backwards compat. *sigh* */ -static int parseargline(char *str, char **a1, char **a2, char **a3) +static int parseargline(char *str, char **a1, char **a2, char **a2_end, char **a3) { char quote; @@ -2775,8 +2820,10 @@ static int parseargline(char *str, char **a1, char **a2, char **a3) if (!*str) { *a3 = NULL; /* 3rd argument is optional */ + *a2_end = str; return 0; } + *a2_end = str; *str++ = '\0'; while (apr_isspace(*str)) { @@ -3316,7 +3363,7 @@ static const char *cmd_rewritecond(cmd_parms *cmd, void *in_dconf, rewrite_server_conf *sconf; rewritecond_entry *newcond; ap_regex_t *regexp; - char *a1 = NULL, *a2 = NULL, *a3 = NULL; + char *a1 = NULL, *a2 = NULL, *a2_end, *a3 = NULL; const char *err; sconf = ap_get_module_config(cmd->server->module_config, &rewrite_module); @@ -3334,7 +3381,7 @@ static const char *cmd_rewritecond(cmd_parms *cmd, void *in_dconf, * of the argument line. So we can use a1 .. a3 without * copying them again. */ - if (parseargline(str, &a1, &a2, &a3)) { + if (parseargline(str, &a1, &a2, &a2_end, &a3)) { return apr_pstrcat(cmd->pool, "RewriteCond: bad argument line '", str, "'", NULL); } @@ -3493,13 +3540,24 @@ static const char *cmd_rewriterule_setflag(apr_pool_t *p, void *_cfg, case 'B': if (!*key || !strcasecmp(key, "ackrefescaping")) { cfg->flags |= RULEFLAG_ESCAPEBACKREF; - if (val && *val) { + if (val && *val) { cfg->escapes = val; } } + else if (!strcasecmp(key, "NE")) { + if (val && *val) { + cfg->noescapes = val; + } + else { + return "flag 'BNE' wants a list of characters (i.e. [BNE=...])"; + } + } else if (!strcasecmp(key, "NP") || !strcasecmp(key, "ackrefernoplus")) { cfg->flags |= RULEFLAG_ESCAPENOPLUS; } + else if (!strcasecmp(key, "CTLS")) { + cfg->flags |= RULEFLAG_ESCAPECTLS|RULEFLAG_ESCAPEBACKREF; + } else { ++error; } @@ -3742,7 +3800,7 @@ static const char *cmd_rewriterule(cmd_parms *cmd, void *in_dconf, rewrite_server_conf *sconf; rewriterule_entry *newrule; ap_regex_t *regexp; - char *a1 = NULL, *a2 = NULL, *a3 = NULL; + char *a1 = NULL, *a2 = NULL, *a2_end, *a3 = NULL; const char *err; sconf = ap_get_module_config(cmd->server->module_config, &rewrite_module); @@ -3756,12 +3814,11 @@ static const char *cmd_rewriterule(cmd_parms *cmd, void *in_dconf, } /* parse the argument line ourself */ - if (parseargline(str, &a1, &a2, &a3)) { + if (parseargline(str, &a1, &a2, &a2_end, &a3)) { return apr_pstrcat(cmd->pool, "RewriteRule: bad argument line '", str, "'", NULL); } - /* arg3: optional flags field */ newrule->forced_mimetype = NULL; newrule->forced_handler = NULL; newrule->forced_responsecode = HTTP_MOVED_TEMPORARILY; @@ -3770,6 +3827,9 @@ static const char *cmd_rewriterule(cmd_parms *cmd, void *in_dconf, newrule->cookie = NULL; newrule->skip = 0; newrule->maxrounds = REWRITE_MAX_ROUNDS; + newrule->escapes = newrule->noescapes = NULL; + + /* arg3: optional flags field */ if (a3 != NULL) { if ((err = cmd_parseflagfield(cmd->pool, newrule, a3, cmd_rewriterule_setflag)) != NULL) { @@ -3803,6 +3863,25 @@ static const char *cmd_rewriterule(cmd_parms *cmd, void *in_dconf, newrule->flags |= RULEFLAG_NOSUB; } + if (*(a2_end-1) == '?') { + /* a literal ? at the end of the unsubstituted rewrite rule */ + if (newrule->flags & RULEFLAG_QSAPPEND) { + /* with QSA, splitout_queryargs will safely handle it if RULEFLAG_QSLAST is set */ + newrule->flags |= RULEFLAG_QSLAST; + } + else { + /* avoid getting a query string via inadvertent capture */ + newrule->flags |= RULEFLAG_QSNONE; + /* trailing ? has done its job, but splitout_queryargs will not chop it off */ + *(a2_end-1) = '\0'; + } + } + else if (newrule->flags & RULEFLAG_QSDISCARD) { + if (NULL == ap_strchr(newrule->output, '?')) { + newrule->flags |= RULEFLAG_QSNONE; + } + } + /* now, if the server or per-dir config holds an * array of RewriteCond entries, we take it for us * and clear the array @@ -4090,7 +4169,7 @@ static int apply_rewrite_rule(rewriterule_entry *p, rewrite_ctx *ctx) } /* Additionally we strip the physical path from the url to match - * it independent from the underlaying filesystem. + * it independent from the underlying filesystem. */ if (!is_proxyreq && strlen(ctx->uri) >= dirlen && !strncmp(ctx->uri, ctx->perdir, dirlen)) { @@ -4208,9 +4287,7 @@ static int apply_rewrite_rule(rewriterule_entry *p, rewrite_ctx *ctx) r->path_info = NULL; } - splitout_queryargs(r, p->flags & RULEFLAG_QSAPPEND, - p->flags & RULEFLAG_QSDISCARD, - p->flags & RULEFLAG_QSLAST); + splitout_queryargs(r, p->flags); /* Add the previously stripped per-directory location prefix, unless * (1) it's an absolute URL path and @@ -4516,9 +4593,6 @@ static int post_config(apr_pool_t *p, } } - rewrite_ssl_lookup = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup); - rewrite_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https); - return OK; } @@ -4692,8 +4766,25 @@ static int hook_uri2file(request_rec *r) } if (rulestatus) { - unsigned skip; - apr_size_t flen; + apr_size_t flen = r->filename ? strlen(r->filename) : 0; + unsigned skip_absolute = flen ? is_absolute_uri(r->filename, NULL) : 0; + int to_proxyreq = (flen > 6 && strncmp(r->filename, "proxy:", 6) == 0); + int will_escape = skip_absolute && (rulestatus != ACTION_NOESCAPE); + + if (r->args + && !will_escape + && *(ap_scan_vchar_obstext(r->args))) { + /* + * We have a raw control character or a ' ' in r->args. + * Correct encoding was missed. + * Correct encoding was missed and we're not going to escape + * it before returning. + */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10410) + "Rewritten query string contains control " + "characters or spaces"); + return HTTP_FORBIDDEN; + } if (ACTION_STATUS == rulestatus) { int n = r->status; @@ -4702,8 +4793,7 @@ static int hook_uri2file(request_rec *r) return n; } - flen = r->filename ? strlen(r->filename) : 0; - if (flen > 6 && strncmp(r->filename, "proxy:", 6) == 0) { + if (to_proxyreq) { /* it should be go on as an internal proxy request */ /* check if the proxy module is enabled, so @@ -4745,7 +4835,7 @@ static int hook_uri2file(request_rec *r) r->filename)); return OK; } - else if ((skip = is_absolute_uri(r->filename, NULL)) > 0) { + else if (skip_absolute > 0) { int n; /* it was finally rewritten to a remote URL */ @@ -4753,7 +4843,7 @@ static int hook_uri2file(request_rec *r) if (rulestatus != ACTION_NOESCAPE) { rewritelog((r, 1, NULL, "escaping %s for redirect", r->filename)); - r->filename = escape_absolute_uri(r->pool, r->filename, skip); + r->filename = escape_absolute_uri(r->pool, r->filename, skip_absolute); } /* append the QUERY_STRING part */ @@ -4977,7 +5067,26 @@ static int hook_fixup(request_rec *r) */ rulestatus = apply_rewrite_list(r, dconf->rewriterules, dconf->directory); if (rulestatus) { - unsigned skip; + unsigned skip_absolute = is_absolute_uri(r->filename, NULL); + int to_proxyreq = 0; + int will_escape = 0; + + l = strlen(r->filename); + to_proxyreq = l > 6 && strncmp(r->filename, "proxy:", 6) == 0; + will_escape = skip_absolute && (rulestatus != ACTION_NOESCAPE); + + if (r->args + && !will_escape + && *(ap_scan_vchar_obstext(r->args))) { + /* + * We have a raw control character or a ' ' in r->args. + * Correct encoding was missed. + */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10411) + "Rewritten query string contains control " + "characters or spaces"); + return HTTP_FORBIDDEN; + } if (ACTION_STATUS == rulestatus) { int n = r->status; @@ -4986,8 +5095,7 @@ static int hook_fixup(request_rec *r) return n; } - l = strlen(r->filename); - if (l > 6 && strncmp(r->filename, "proxy:", 6) == 0) { + if (to_proxyreq) { /* it should go on as an internal proxy request */ /* make sure the QUERY_STRING and @@ -5011,7 +5119,7 @@ static int hook_fixup(request_rec *r) "%s [OK]", r->filename)); return OK; } - else if ((skip = is_absolute_uri(r->filename, NULL)) > 0) { + else if (skip_absolute > 0) { /* it was finally rewritten to a remote URL */ /* because we are in a per-dir context @@ -5020,7 +5128,7 @@ static int hook_fixup(request_rec *r) */ if (dconf->baseurl != NULL) { /* skip 'scheme://' */ - cp = r->filename + skip; + cp = r->filename + skip_absolute; if ((cp = ap_strchr(cp, '/')) != NULL && *(++cp)) { rewritelog((r, 2, dconf->directory, @@ -5065,7 +5173,7 @@ static int hook_fixup(request_rec *r) if (rulestatus != ACTION_NOESCAPE) { rewritelog((r, 1, dconf->directory, "escaping %s for redirect", r->filename)); - r->filename = escape_absolute_uri(r->pool, r->filename, skip); + r->filename = escape_absolute_uri(r->pool, r->filename, skip_absolute); } /* append the QUERY_STRING part */ -- cgit v1.2.3