*/
#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
*/
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
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 */
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;
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);
{
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);
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;
}
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;
}
}
#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)
{
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"),
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.
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;
}
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;
}