in modules/mappers/mod_rewrite.c [4872:5234]
static int hook_uri2file(request_rec *r)
{
rewrite_perdir_conf *dconf;
rewrite_server_conf *conf;
const char *saved_rulestatus;
const char *var;
const char *thisserver;
char *thisport;
const char *thisurl;
unsigned int port;
int rulestatus;
void *skipdata;
char *ofilename;
const char *oargs;
rewriterule_entry *lastsub = NULL;
/*
* retrieve the config structures
*/
conf = ap_get_module_config(r->server->module_config, &rewrite_module);
dconf = (rewrite_perdir_conf *)ap_get_module_config(r->per_dir_config,
&rewrite_module);
/*
* only do something under runtime if the engine is really enabled,
* else return immediately!
*/
if (!dconf || dconf->state == ENGINE_DISABLED) {
return DECLINED;
}
/*
* check for the ugly API case of a virtual host section where no
* mod_rewrite directives exists. In this situation we became no chance
* by the API to setup our default per-server config so we have to
* on-the-fly assume we have the default config. But because the default
* config has a disabled rewriting engine we are lucky because can
* just stop operating now.
*/
if (conf->server != r->server) {
return DECLINED;
}
/* END flag was used as a RewriteRule flag on this request */
apr_pool_userdata_get(&skipdata, really_last_key, r->pool);
if (skipdata != NULL) {
rewritelog(r, 8, NULL, "Declining, no further rewriting due to END flag");
return DECLINED;
}
/* Unless the anyuri option is set, ensure that the input to the
* first rule really is a URL-path, avoiding security issues with
* poorly configured rules. See CVE-2011-3368, CVE-2011-4317. */
if ((dconf->options & OPTION_ANYURI) == 0
&& ((r->unparsed_uri[0] == '*' && r->unparsed_uri[1] == '\0')
|| !r->uri || r->uri[0] != '/')) {
rewritelog(r, 8, NULL, "Declining, request-URI '%s' is not a URL-path. "
"Consult the manual entry for the RewriteOptions directive "
"for options and caveats about matching other strings.",
r->uri);
return DECLINED;
}
/*
* remember the original query string for later check, since we don't
* want to apply URL-escaping when no substitution has changed it.
* also, we'll restore original r->filename if we decline this
* request.
*/
ofilename = r->filename;
oargs = r->args;
/*
* add the SCRIPT_URL variable to the env. this is a bit complicated
* due to the fact that apache uses subrequests and internal redirects
*/
if (r->main == NULL) {
var = apr_table_get(r->subprocess_env, REDIRECT_ENVVAR_SCRIPT_URL);
if (var == NULL) {
apr_table_setn(r->subprocess_env, ENVVAR_SCRIPT_URL, r->uri);
}
else {
apr_table_setn(r->subprocess_env, ENVVAR_SCRIPT_URL, var);
}
}
else {
var = apr_table_get(r->main->subprocess_env, ENVVAR_SCRIPT_URL);
apr_table_setn(r->subprocess_env, ENVVAR_SCRIPT_URL, var);
}
/*
* create the SCRIPT_URI variable for the env
*/
/* add the canonical URI of this URL */
thisserver = ap_get_server_name_for_url(r);
port = ap_get_server_port(r);
if (ap_is_default_port(port, r)) {
thisport = "";
}
else {
thisport = apr_psprintf(r->pool, ":%u", port);
}
thisurl = apr_table_get(r->subprocess_env, ENVVAR_SCRIPT_URL);
/* set the variable */
var = apr_pstrcat(r->pool, ap_http_scheme(r), "://", thisserver, thisport,
thisurl, NULL);
apr_table_setn(r->subprocess_env, ENVVAR_SCRIPT_URI, var);
if (!(saved_rulestatus = apr_table_get(r->notes,"mod_rewrite_rewritten"))) {
/* If r->filename was not initially set or if it's a pre_trans reverse
* "proxy:" scheme, we start with the requested URI.
*/
if (r->filename == NULL || (r->proxyreq == PROXYREQ_REVERSE &&
strncmp(r->filename, "proxy:", 6) == 0)) {
r->filename = apr_pstrdup(r->pool, r->uri);
rewritelog(r, 2, NULL, "init rewrite engine with requested uri "
"%s. Original filename = %s", r->filename,
(ofilename) ? ofilename : "n/a");
}
else {
rewritelog(r, 2, NULL, "init rewrite engine with passed filename "
"%s. Original uri = %s", r->filename, r->uri);
}
/*
* now apply the rules ...
*/
rulestatus = apply_rewrite_list(r, conf->rewriterules, NULL, &lastsub);
apr_table_setn(r->notes, "mod_rewrite_rewritten",
apr_psprintf(r->pool,"%d",rulestatus));
}
else {
rewritelog(r, 2, NULL, "uri already rewritten. Status %s, Uri %s, "
"r->filename %s", saved_rulestatus, r->uri, r->filename);
rulestatus = atoi(saved_rulestatus);
}
if (rulestatus) {
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 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;
r->status = HTTP_OK;
return n;
}
else if (ACTION_STATUS_SET == rulestatus) {
return r->status;
}
/* If a pre_trans reverse "proxy:" filename gets rewritten to
* a non-proxy one this is not a proxy request anymore.
*/
if (r->proxyreq == PROXYREQ_REVERSE && !to_proxyreq) {
if (r->handler && strcmp(r->handler, "proxy-server") == 0) {
r->handler = NULL;
}
r->proxyreq = PROXYREQ_NONE;
}
if (to_proxyreq) {
/* it should be go on as an internal proxy request */
/* check if the proxy module is enabled, so
* we can actually use it!
*/
if (!proxy_available) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00669)
"attempt to make remote request from mod_rewrite "
"without proxy enabled: %s", r->filename);
return HTTP_FORBIDDEN;
}
if (rulestatus == ACTION_NOESCAPE) {
apr_table_setn(r->notes, "proxy-nocanon", "1");
}
/* make sure the QUERY_STRING and
* PATH_INFO parts get incorporated
*/
if (r->path_info != NULL) {
r->filename = apr_pstrcat(r->pool, r->filename,
r->path_info, NULL);
}
if ((r->args != NULL)
&& ((r->proxyreq == PROXYREQ_PROXY)
|| apr_table_get(r->notes, "proxy-nocanon"))) {
/* see proxy_http:proxy_http_canon() */
r->filename = apr_pstrcat(r->pool, r->filename,
"?", r->args, NULL);
}
/* now make sure the request gets handled by the proxy handler */
if (PROXYREQ_NONE == r->proxyreq) {
r->proxyreq = PROXYREQ_REVERSE;
}
r->handler = "proxy-server";
rewritelog(r, 1, NULL, "go-ahead with proxy request %s [OK]",
r->filename);
return OK;
}
else if (skip_absolute > 0) {
int n;
/* it was finally rewritten to a remote URL */
if (rulestatus != ACTION_NOESCAPE) {
rewritelog(r, 1, NULL, "escaping %s for redirect",
r->filename);
r->filename = escape_absolute_uri(r->pool, r->filename, skip_absolute);
}
/* append the QUERY_STRING part */
if (r->args) {
char *escaped_args = NULL;
int noescape = (rulestatus == ACTION_NOESCAPE ||
(oargs && !strcmp(r->args, oargs)));
r->filename = apr_pstrcat(r->pool, r->filename, "?",
noescape
? r->args
: (escaped_args =
ap_escape_uri(r->pool, r->args)),
NULL);
rewritelog(r, 1, NULL, "%s %s to query string for redirect %s",
noescape ? "copying" : "escaping",
r->args ,
noescape ? "" : escaped_args);
}
/* determine HTTP redirect response code */
if (ap_is_HTTP_REDIRECT(r->status)) {
n = r->status;
r->status = HTTP_OK; /* make Apache kernel happy */
}
else {
n = HTTP_MOVED_TEMPORARILY;
}
/* now do the redirection */
apr_table_setn(r->headers_out, "Location", r->filename);
rewritelog(r, 1, NULL, "redirect to %s [REDIRECT/%d]", r->filename,
n);
return n;
}
else if (flen > 12 && strncmp(r->filename, "passthrough:", 12) == 0) {
/*
* Hack because of underpowered API: passing the current
* rewritten filename through to other URL-to-filename handlers
* just as it were the requested URL. This is to enable
* post-processing by mod_alias, etc. which always act on
* r->uri! The difference here is: We do not try to
* add the document root
*/
r->uri = apr_pstrdup(r->pool, r->filename+12);
return DECLINED;
}
else {
/* it was finally rewritten to a local path */
const char *uri_reduced = NULL;
/* expand "/~user" prefix */
#if APR_HAS_USER
r->filename = expand_tildepaths(r, r->filename);
#endif
rewritelog(r, 2, NULL, "local path result: %s", r->filename);
/* the filename must be either an absolute local path or an
* absolute local URL.
*/
if ( *r->filename != '/'
&& !ap_os_is_path_absolute(r->pool, r->filename)) {
return HTTP_BAD_REQUEST;
}
/* We have r->filename as a path in a server-context rewrite without
* the PT flag. The historical behavior is to treat it as a verbatim
* filesystem path iff the first component of the path exists and is
* readable by httpd. Otherwise, it is interpreted as DocumentRoot
* relative.
*
* NOTICE:
* We cannot leave out the prefix_stat because
* - If we always prefix with document_root
* then no absolute path can could ever be used in
* a substitution. e.g. emulating an Alias.
* - If we never prefix with document_root
* then the files under document_root have to
* be references directly and document_root
* gets never used and will be a dummy parameter -
* this is also bad.
* - Later addition: This part is questionable.
* If we had never prefixed, users would just
* need %{DOCUMENT_ROOT} in substitutions or the
* [PT] flag.
*
* BUT:
* Under real Unix systems this is no perf problem,
* because we only do stat() on the first directory
* and this gets cached by the kernel for along time!
*/
if(!(conf->options & OPTION_LEGACY_PREFIX_DOCROOT)) {
uri_reduced = apr_table_get(r->notes, "mod_rewrite_uri_reduced");
}
if (!prefix_stat(r, r->filename, r->pool,
conf->options & OPTION_UNSAFE_PREFIX_STAT ? NULL : lastsub)
|| uri_reduced != NULL) {
int res;
char *tmp = r->uri;
r->uri = r->filename;
res = ap_core_translate(r);
r->uri = tmp;
if (res != OK) {
rewritelog(r, 1, NULL, "prefixing with document_root of %s"
" FAILED", r->filename);
return res;
}
rewritelog(r, 2, NULL, "prefixed with document_root to %s",
r->filename);
}
rewritelog(r, 1, NULL, "go-ahead with %s [OK]", r->filename);
return OK;
}
}
else {
rewritelog(r, 1, NULL, "pass through %s, filename %s",
r->filename, (ofilename) ? ofilename : "n/a");
r->filename = ofilename;
return DECLINED;
}
}