mirror of
https://github.com/apache/httpd.git
synced 2025-08-07 04:02:58 +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]
|
[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
|
*) mod_rewrite: Fix LA-U variable look ahead which didn't work correctly
|
||||||
in directory context. Related to PR 8493. [Andr<64> Malo]
|
in directory context. Related to PR 8493. [Andr<64> Malo]
|
||||||
|
|
||||||
|
@@ -134,6 +134,12 @@ extern "C" {
|
|||||||
*/
|
*/
|
||||||
#define AP_MIN_BYTES_TO_WRITE 8000
|
#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
|
* Retrieve the value of Options for this request
|
||||||
* @param r The current 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);
|
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
|
* Check for a definition from the server command line
|
||||||
* @param name The define to check for
|
* @param name The define to check for
|
||||||
@@ -560,6 +582,10 @@ typedef struct {
|
|||||||
char *access_name;
|
char *access_name;
|
||||||
apr_array_header_t *sec_dir;
|
apr_array_header_t *sec_dir;
|
||||||
apr_array_header_t *sec_url;
|
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;
|
} core_server_config;
|
||||||
|
|
||||||
/* for AddOutputFiltersByType in core.c */
|
/* 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,
|
static request_rec *internal_internal_redirect(const char *new_uri,
|
||||||
request_rec *r) {
|
request_rec *r) {
|
||||||
int access_status;
|
int access_status;
|
||||||
request_rec *new = (request_rec *) apr_pcalloc(r->pool,
|
request_rec *new;
|
||||||
sizeof(request_rec));
|
|
||||||
|
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->connection = r->connection;
|
||||||
new->server = r->server;
|
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)
|
AP_DECLARE(void) ap_internal_redirect(const char *new_uri, request_rec *r)
|
||||||
{
|
{
|
||||||
request_rec *new = internal_internal_redirect(new_uri, 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 == OK) {
|
||||||
if ((access_status = ap_invoke_handler(new)) != 0) {
|
if ((access_status = ap_invoke_handler(new)) != 0) {
|
||||||
ap_die(access_status, new);
|
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;
|
int access_status;
|
||||||
request_rec *new = internal_internal_redirect(new_uri, r);
|
request_rec *new = internal_internal_redirect(new_uri, r);
|
||||||
|
|
||||||
|
/* ap_die was already called, if an error occured */
|
||||||
|
if (!new) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (r->handler)
|
if (r->handler)
|
||||||
ap_set_content_type(new, r->content_type);
|
ap_set_content_type(new, r->content_type);
|
||||||
access_status = ap_process_request_internal(new);
|
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_dir = apr_array_make(a, 40, sizeof(ap_conf_vector_t *));
|
||||||
conf->sec_url = 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;
|
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_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->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;
|
return conf;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2613,6 +2625,141 @@ static const char *set_limit_nproc(cmd_parms *cmd, void *conf_,
|
|||||||
}
|
}
|
||||||
#endif
|
#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_,
|
static const char *add_ct_output_filters(cmd_parms *cmd, void *conf_,
|
||||||
const char *arg, const char *arg2)
|
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"),
|
OR_ALL, "soft/hard limits for max number of processes per uid"),
|
||||||
#endif
|
#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,
|
AP_INIT_TAKE1("ForceType", ap_set_string_slot_lower,
|
||||||
(void *)APR_OFFSETOF(core_dir_config, mime_type), OR_FILEINFO,
|
(void *)APR_OFFSETOF(core_dir_config, mime_type), OR_FILEINFO,
|
||||||
"a mime type that overrides other configured type"),
|
"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 */
|
udir = ap_escape_uri(rnew->pool, udir); /* re-escape it */
|
||||||
ap_parse_uri(rnew, ap_make_full_path(rnew->pool, udir, new_file));
|
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
|
/* lookup_uri
|
||||||
* If the content can be served by the quick_handler, we can
|
* If the content can be served by the quick_handler, we can
|
||||||
* safely bypass request_internal processing.
|
* 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);
|
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))) {
|
if ((res = ap_process_request_internal(rnew))) {
|
||||||
rnew->status = res;
|
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, "");
|
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))) {
|
if ((res = ap_process_request_internal(rnew))) {
|
||||||
rnew->status = res;
|
rnew->status = res;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user