mirror of
https://github.com/apache/httpd.git
synced 2025-08-08 15:02:10 +03:00
Prevent the server from crashing when entering infinite loops. The
new LimitInternalRecursion directive configures limits of subsequent internal redirects and nested subrequests, after which the request will be aborted. [William Rowe, Jeff Trawick, Andr� Malo] PR: 19753 (and probably others) git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@99911 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
6
CHANGES
6
CHANGES
@@ -2,6 +2,12 @@ Changes with Apache 2.1.0-dev
|
||||
|
||||
[Remove entries to the current 2.0 section below, when backported]
|
||||
|
||||
*) Prevent the server from crashing when entering infinite loops. The
|
||||
new LimitInternalRecursion directive configures limits of subsequent
|
||||
internal redirects and nested subrequests, after which the request
|
||||
will be aborted. PR 19753 (and probably others).
|
||||
[William Rowe, Jeff Trawick, Andr<64> Malo]
|
||||
|
||||
*) mod_rewrite: Fix LA-U variable look ahead which didn't work correctly
|
||||
in directory context. Related to PR 8493. [Andr<64> Malo]
|
||||
|
||||
|
@@ -134,6 +134,12 @@ extern "C" {
|
||||
*/
|
||||
#define AP_MIN_BYTES_TO_WRITE 8000
|
||||
|
||||
/* default maximum of internal redirects */
|
||||
# define AP_DEFAULT_MAX_INTERNAL_REDIRECTS 10
|
||||
|
||||
/* default maximum subrequest nesting level */
|
||||
# define AP_DEFAULT_MAX_SUBREQ_DEPTH 10
|
||||
|
||||
/**
|
||||
* Retrieve the value of Options for this request
|
||||
* @param r The current request
|
||||
@@ -257,6 +263,22 @@ AP_DECLARE(size_t) ap_get_limit_xml_body(const request_rec *r);
|
||||
*/
|
||||
AP_DECLARE(void) ap_custom_response(request_rec *r, int status, const char *string);
|
||||
|
||||
/**
|
||||
* Check if the current request is beyond the configured max. number of redirects
|
||||
* @param r The current request
|
||||
* @return true (is exceeded) or false
|
||||
* @deffunc int ap_is_redirect_limit_exceeded(const request_rec *r)
|
||||
*/
|
||||
AP_DECLARE(int) ap_is_redirect_limit_exceeded(const request_rec *r);
|
||||
|
||||
/**
|
||||
* Check if the current request is beyond the configured subreq nesting level
|
||||
* @param r The current request
|
||||
* @return true (is exceeded) or false
|
||||
* @deffunc int ap_is_subreq_limit_exceeded(const request_rec *r)
|
||||
*/
|
||||
AP_DECLARE(int) ap_is_subreq_limit_exceeded(const request_rec *r);
|
||||
|
||||
/**
|
||||
* Check for a definition from the server command line
|
||||
* @param name The define to check for
|
||||
@@ -560,6 +582,10 @@ typedef struct {
|
||||
char *access_name;
|
||||
apr_array_header_t *sec_dir;
|
||||
apr_array_header_t *sec_url;
|
||||
|
||||
/* recursion backstopper */
|
||||
int redirect_limit; /* maximum number of internal redirects */
|
||||
int subreq_limit; /* maximum nesting level of subrequests */
|
||||
} core_server_config;
|
||||
|
||||
/* for AddOutputFiltersByType in core.c */
|
||||
|
@@ -332,8 +332,14 @@ static apr_table_t *rename_original_env(apr_pool_t *p, apr_table_t *t)
|
||||
static request_rec *internal_internal_redirect(const char *new_uri,
|
||||
request_rec *r) {
|
||||
int access_status;
|
||||
request_rec *new = (request_rec *) apr_pcalloc(r->pool,
|
||||
sizeof(request_rec));
|
||||
request_rec *new;
|
||||
|
||||
if (ap_is_redirect_limit_exceeded(r)) {
|
||||
ap_die(HTTP_INTERNAL_SERVER_ERROR, r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
new = (request_rec *) apr_pcalloc(r->pool, sizeof(request_rec));
|
||||
|
||||
new->connection = r->connection;
|
||||
new->server = r->server;
|
||||
@@ -499,7 +505,14 @@ AP_DECLARE(void) ap_internal_fast_redirect(request_rec *rr, request_rec *r)
|
||||
AP_DECLARE(void) ap_internal_redirect(const char *new_uri, request_rec *r)
|
||||
{
|
||||
request_rec *new = internal_internal_redirect(new_uri, r);
|
||||
int access_status = ap_process_request_internal(new);
|
||||
int access_status;
|
||||
|
||||
/* ap_die was already called, if an error occured */
|
||||
if (!new) {
|
||||
return;
|
||||
}
|
||||
|
||||
access_status = ap_process_request_internal(new);
|
||||
if (access_status == OK) {
|
||||
if ((access_status = ap_invoke_handler(new)) != 0) {
|
||||
ap_die(access_status, new);
|
||||
@@ -520,6 +533,12 @@ AP_DECLARE(void) ap_internal_redirect_handler(const char *new_uri, request_rec *
|
||||
{
|
||||
int access_status;
|
||||
request_rec *new = internal_internal_redirect(new_uri, r);
|
||||
|
||||
/* ap_die was already called, if an error occured */
|
||||
if (!new) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (r->handler)
|
||||
ap_set_content_type(new, r->content_type);
|
||||
access_status = ap_process_request_internal(new);
|
||||
|
151
server/core.c
151
server/core.c
@@ -468,6 +468,10 @@ static void *create_core_server_config(apr_pool_t *a, server_rec *s)
|
||||
conf->sec_dir = apr_array_make(a, 40, sizeof(ap_conf_vector_t *));
|
||||
conf->sec_url = apr_array_make(a, 40, sizeof(ap_conf_vector_t *));
|
||||
|
||||
/* recursion stopper */
|
||||
conf->redirect_limit = 0; /* 0 == unset */
|
||||
conf->subreq_limit = 0;
|
||||
|
||||
return (void *)conf;
|
||||
}
|
||||
|
||||
@@ -491,6 +495,14 @@ static void *merge_core_server_configs(apr_pool_t *p, void *basev, void *virtv)
|
||||
conf->sec_dir = apr_array_append(p, base->sec_dir, virt->sec_dir);
|
||||
conf->sec_url = apr_array_append(p, base->sec_url, virt->sec_url);
|
||||
|
||||
conf->redirect_limit = virt->redirect_limit
|
||||
? virt->redirect_limit
|
||||
: base->redirect_limit;
|
||||
|
||||
conf->subreq_limit = virt->subreq_limit
|
||||
? virt->subreq_limit
|
||||
: base->subreq_limit;
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
@@ -2613,6 +2625,141 @@ static const char *set_limit_nproc(cmd_parms *cmd, void *conf_,
|
||||
}
|
||||
#endif
|
||||
|
||||
static const char *set_recursion_limit(cmd_parms *cmd, void *dummy,
|
||||
const char *arg1, const char *arg2)
|
||||
{
|
||||
core_server_config *conf = ap_get_module_config(cmd->server->module_config,
|
||||
&core_module);
|
||||
int limit = atoi(arg1);
|
||||
|
||||
if (limit <= 0) {
|
||||
return "The recursion limit must be greater than zero.";
|
||||
}
|
||||
if (limit < 4) {
|
||||
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
|
||||
"Limiting internal redirects to very low numbers may "
|
||||
"cause normal requests to fail.");
|
||||
}
|
||||
|
||||
conf->redirect_limit = limit;
|
||||
|
||||
if (arg2) {
|
||||
limit = atoi(arg2);
|
||||
|
||||
if (limit <= 0) {
|
||||
return "The recursion limit must be greater than zero.";
|
||||
}
|
||||
if (limit < 4) {
|
||||
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
|
||||
"Limiting the subrequest depth to a very low level may"
|
||||
" cause normal requests to fail.");
|
||||
}
|
||||
}
|
||||
|
||||
conf->subreq_limit = limit;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void log_backtrace(const request_rec *top, const request_rec *r)
|
||||
{
|
||||
while (top) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
|
||||
"redirected from r->uri = %s",
|
||||
top->uri ? top->uri : "(unexpectedly NULL)");
|
||||
|
||||
if (!top->prev && top->main) {
|
||||
top = top->main;
|
||||
|
||||
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
|
||||
"subrequested from r->uri = %s",
|
||||
top->uri ? top->uri : "(unexpectedly NULL)");
|
||||
}
|
||||
else {
|
||||
top = top->prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* check whether redirect limit is reached
|
||||
*/
|
||||
AP_DECLARE(int) ap_is_redirect_limit_exceeded(const request_rec *r)
|
||||
{
|
||||
core_server_config *conf = ap_get_module_config(r->server->module_config,
|
||||
&core_module);
|
||||
const request_rec *top = r;
|
||||
int redirects = 0;
|
||||
int limit = conf->redirect_limit
|
||||
? conf->redirect_limit
|
||||
: AP_DEFAULT_MAX_INTERNAL_REDIRECTS;
|
||||
|
||||
while (top->prev) {
|
||||
if (++redirects >= limit) {
|
||||
/* uuh, too much. */
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Request exceeded the limit of %d internal redirects "
|
||||
"due to probable configuration error. Use "
|
||||
"'LimitInternalRecursion' to increase the limit if "
|
||||
"necessary. Use 'LogLevel debug' to get a "
|
||||
"backtrace.", limit);
|
||||
|
||||
/* post backtrace */
|
||||
log_backtrace(r->prev, r);
|
||||
|
||||
/* return failure */
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!top->prev && top->main) {
|
||||
top = top->main;
|
||||
}
|
||||
else {
|
||||
top = top->prev;
|
||||
}
|
||||
}
|
||||
|
||||
/* number of redirects is ok */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* check whether subrequest depth limit is reached
|
||||
*/
|
||||
AP_DECLARE(int) ap_is_subreq_limit_exceeded(const request_rec *r)
|
||||
{
|
||||
core_server_config *conf = ap_get_module_config(r->server->module_config,
|
||||
&core_module);
|
||||
const request_rec *top = r;
|
||||
int subreqs = 0;
|
||||
int limit = conf->subreq_limit
|
||||
? conf->subreq_limit
|
||||
: AP_DEFAULT_MAX_SUBREQ_DEPTH;
|
||||
|
||||
while (top->main) {
|
||||
if (++subreqs >= limit) {
|
||||
/* uuh, too much. */
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
"Request exceeded the limit of %d subrequest "
|
||||
"nesting levels due to probable confguration error. "
|
||||
"Use 'LimitInternalRecursion' to increase the limit "
|
||||
"if necessary. Use 'LogLevel debug' to get a "
|
||||
"backtrace.", limit);
|
||||
|
||||
/* post backtrace */
|
||||
log_backtrace(r->main, r);
|
||||
|
||||
/* return failure */
|
||||
return 1;
|
||||
}
|
||||
|
||||
top = top->main;
|
||||
}
|
||||
|
||||
/* number of subrequests is ok */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *add_ct_output_filters(cmd_parms *cmd, void *conf_,
|
||||
const char *arg, const char *arg2)
|
||||
{
|
||||
@@ -3063,6 +3210,10 @@ AP_INIT_TAKE12("RLimitNPROC", no_set_limit, NULL,
|
||||
OR_ALL, "soft/hard limits for max number of processes per uid"),
|
||||
#endif
|
||||
|
||||
/* internal recursion stopper */
|
||||
AP_INIT_TAKE12("LimitInternalRecursion", set_recursion_limit, NULL, RSRC_CONF,
|
||||
"maximum recursion depth of internal redirects and subrequests"),
|
||||
|
||||
AP_INIT_TAKE1("ForceType", ap_set_string_slot_lower,
|
||||
(void *)APR_OFFSETOF(core_dir_config, mime_type), OR_FILEINFO,
|
||||
"a mime type that overrides other configured type"),
|
||||
|
@@ -1631,6 +1631,14 @@ AP_DECLARE(request_rec *) ap_sub_req_method_uri(const char *method,
|
||||
udir = ap_escape_uri(rnew->pool, udir); /* re-escape it */
|
||||
ap_parse_uri(rnew, ap_make_full_path(rnew->pool, udir, new_file));
|
||||
}
|
||||
|
||||
/* We cannot return NULL without violating the API. So just turn this
|
||||
* subrequest into a 500 to indicate the failure. */
|
||||
if (ap_is_subreq_limit_exceeded(r)) {
|
||||
rnew->status = HTTP_INTERNAL_SERVER_ERROR;
|
||||
return rnew;
|
||||
}
|
||||
|
||||
/* lookup_uri
|
||||
* If the content can be served by the quick_handler, we can
|
||||
* safely bypass request_internal processing.
|
||||
@@ -1764,6 +1772,13 @@ AP_DECLARE(request_rec *) ap_sub_req_lookup_dirent(const apr_finfo_t *dirent,
|
||||
ap_parse_uri(rnew, rnew->uri);
|
||||
}
|
||||
|
||||
/* We cannot return NULL without violating the API. So just turn this
|
||||
* subrequest into a 500. */
|
||||
if (ap_is_subreq_limit_exceeded(r)) {
|
||||
rnew->status = HTTP_INTERNAL_SERVER_ERROR;
|
||||
return rnew;
|
||||
}
|
||||
|
||||
if ((res = ap_process_request_internal(rnew))) {
|
||||
rnew->status = res;
|
||||
}
|
||||
@@ -1851,6 +1866,13 @@ AP_DECLARE(request_rec *) ap_sub_req_lookup_file(const char *new_file,
|
||||
rnew->uri = apr_pstrdup(rnew->pool, "");
|
||||
}
|
||||
|
||||
/* We cannot return NULL without violating the API. So just turn this
|
||||
* subrequest into a 500. */
|
||||
if (ap_is_subreq_limit_exceeded(r)) {
|
||||
rnew->status = HTTP_INTERNAL_SERVER_ERROR;
|
||||
return rnew;
|
||||
}
|
||||
|
||||
if ((res = ap_process_request_internal(rnew))) {
|
||||
rnew->status = res;
|
||||
}
|
||||
|
Reference in New Issue
Block a user