in apache2/mod_security2.c [919:1129]
static int hook_request_late(request_rec *r) {
char *my_error_msg = NULL;
modsec_rec *msr = NULL;
int rc;
/* This function needs to run only once per transaction
* (i.e. subrequests and redirects are excluded).
*/
if ((r->main != NULL)||(r->prev != NULL)) {
return DECLINED;
}
/* Find the transaction context and make sure
* we are supposed to proceed.
*/
msr = retrieve_tx_context(r);
if (msr == NULL) {
/* If we can't find the context that probably means it's
* a subrequest that was not initiated from the outside.
*/
return DECLINED;
}
/* Has this phase been completed already? */
if (msr->phase_request_body_complete) {
msr_log(msr, 1, "Internal Error: Attempted to process the request body more than once.");
return DECLINED;
}
msr->phase_request_body_complete = 1;
msr->remote_user = r->user;
/* Get the second configuration context. */
msr->dcfg2 = (directory_config *)ap_get_module_config(r->per_dir_config,
&security2_module);
/* Create a transaction context. */
msr->txcfg = create_directory_config(msr->mp, NULL);
if (msr->txcfg == NULL) return DECLINED;
if (msr->dcfg2 != NULL) {
msr->txcfg = merge_directory_configs(msr->mp, msr->txcfg, msr->dcfg2);
if (msr->txcfg == NULL) return DECLINED;
}
/* Update with the explicit user settings. */
msr->txcfg = merge_directory_configs(msr->mp, msr->txcfg, msr->usercfg);
init_directory_config(msr->txcfg);
if (msr->txcfg->is_enabled == 0) {
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Processing disabled, skipping (hook request_late).");
}
return DECLINED;
}
#ifndef REQUEST_EARLY
/* Phase 1 */
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "First phase starting (dcfg %pp).", msr->dcfg2);
}
/* Process phase REQUEST_HEADERS */
if (modsecurity_process_phase(msr, PHASE_REQUEST_HEADERS) > 0) {
/* There was a match; see if we need to intercept. */
rc = perform_interception(msr);
if (rc != DECLINED) {
/* Intercepted */
return rc;
}
}
#endif
/* The rule engine could have been disabled in phase 1. */
if (msr->txcfg->is_enabled == MODSEC_DISABLED) {
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Skipping phase 2 as the rule engine was disabled by a rule in phase 1.");
}
return DECLINED;
}
/* Phase 2 */
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "Second phase starting (dcfg %pp).", msr->dcfg2);
}
/* Check that the request body is not too long, but only
* if configuration allows for request body access.
*/
msr->inbound_error = 0;
if (msr->txcfg->reqbody_access == 1) {
/* Check request body limit (non-chunked requests only). */
if (msr->request_content_length > msr->txcfg->reqbody_limit) {
if((msr->txcfg->is_enabled == MODSEC_ENABLED) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_REJECT)) {
msr->inbound_error = 1;
msr_log(msr, 1, "Request body (Content-Length) is larger than the "
"configured limit (%ld). Deny with status (%d)", msr->txcfg->reqbody_limit, HTTP_REQUEST_ENTITY_TOO_LARGE);
return HTTP_REQUEST_ENTITY_TOO_LARGE;
} else if ((msr->txcfg->is_enabled == MODSEC_ENABLED) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_PARTIAL)){
msr->inbound_error = 1;
msr_log(msr, 1, "Request body (Content-Length) is larger than the "
"configured limit (%ld).", msr->txcfg->reqbody_limit);
} else if ((msr->txcfg->is_enabled == MODSEC_DETECTION_ONLY) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_PARTIAL)){
msr_log(msr, 1, "Request body (Content-Length) is larger than the "
"configured limit (%ld).", msr->txcfg->reqbody_limit);
msr->inbound_error = 1;
} else {
msr_log(msr, 1, "Request body (Content-Length) is larger than the "
"configured limit (%ld).", msr->txcfg->reqbody_limit);
msr->inbound_error = 1;
}
}
}
/* Figure out whether to extract multipart files. */
if ((msr->txcfg->upload_keep_files != KEEP_FILES_OFF) /* user might want to keep them */
|| (msr->txcfg->upload_validates_files)) /* user might want to validate them */
{
msr->upload_extract_files = 1;
msr->upload_remove_files = 1;
}
rc = read_request_body(msr, &my_error_msg);
if (rc < 0 && msr->txcfg->is_enabled == MODSEC_ENABLED) {
switch(rc) {
case -1 :
if (my_error_msg != NULL) {
msr_log(msr, 1, "%s", my_error_msg);
}
return HTTP_INTERNAL_SERVER_ERROR;
break;
case -4 : /* Timeout. */
if (my_error_msg != NULL) {
msr_log(msr, 4, "%s", my_error_msg);
}
r->connection->keepalive = AP_CONN_CLOSE;
return HTTP_REQUEST_TIME_OUT;
break;
case -5 : /* Request body limit reached. */
msr->inbound_error = 1;
if((msr->txcfg->is_enabled == MODSEC_ENABLED) && (msr->txcfg->if_limit_action == REQUEST_BODY_LIMIT_ACTION_REJECT)) {
r->connection->keepalive = AP_CONN_CLOSE;
if (my_error_msg != NULL) {
msr_log(msr, 1, "%s. Deny with code (%d)", my_error_msg, HTTP_REQUEST_ENTITY_TOO_LARGE);
}
return HTTP_REQUEST_ENTITY_TOO_LARGE;
} else {
if (my_error_msg != NULL) {
msr_log(msr, 1, "%s", my_error_msg);
}
}
break;
case -6 : /* EOF when reading request body. */
if (my_error_msg != NULL) {
msr_log(msr, 4, "%s", my_error_msg);
}
r->connection->keepalive = AP_CONN_CLOSE;
return HTTP_BAD_REQUEST;
break;
case -7 : /* Partial recieved */
if (my_error_msg != NULL) {
msr_log(msr, 4, "%s", my_error_msg);
}
r->connection->keepalive = AP_CONN_CLOSE;
return HTTP_BAD_REQUEST;
break;
default :
/* allow through */
break;
}
msr->msc_reqbody_error = 1;
msr->msc_reqbody_error_msg = my_error_msg;
}
/* Update the request headers. They might have changed after
* the body was read (trailers).
*
* TODO We might still want to hold onto the original headers
* so that we can log them. Keeping them is probably not
* going to increase our memory requirements (because all
* headers are allocated from the request memory pool
* anyway).
*/
msr->request_headers = apr_table_copy(msr->mp, r->headers_in);
/* Process phase REQUEST_BODY */
rc = DECLINED;
if (modsecurity_process_phase(msr, PHASE_REQUEST_BODY) > 0) {
rc = perform_interception(msr);
}
if(msr->txcfg->stream_inbody_inspection && msr->msc_reqbody_read) {
const char *clen = NULL;
clen = apr_psprintf(msr->mp,"%"APR_SIZE_T_FMT,msr->stream_input_length);
if(clen)
apr_table_setn(r->headers_in, "Content-Length",clen);
}
/* Remove the compression ability indications the client set,
* but only if we need to disable backend compression.
*/
if (msr->txcfg->disable_backend_compression) {
apr_table_unset(r->headers_in, "Accept-Encoding");
apr_table_unset(r->headers_in, "TE");
}
return rc;
}