in apache2/mod_security2.c [164:436]
int perform_interception(modsec_rec *msr) {
msre_actionset *actionset = NULL;
const char *message = NULL;
const char *phase_text = "";
unsigned int pause = 0;
int status = DECLINED;
int log_level = 1;
/* Sanity checks first. */
if (msr->was_intercepted == 0) {
msr_log(msr, 1, "Internal Error: Asked to intercept request but was_intercepted is zero");
return DECLINED;
}
if (msr->phase > 4) {
msr_log(msr, 1, "Internal Error: Asked to intercept request in phase %d.", msr->phase);
msr->was_intercepted = 0;
return DECLINED;
}
/* OK, we're good to go. */
actionset = msr->intercept_actionset;
phase_text = apr_psprintf(msr->mp, " (phase %d)", msr->phase);
/* By default we log at level 1 but we switch to 4
* if a nolog action was used or this is not the initial request
* to hide the message.
*/
log_level = (actionset->log != 1) ? 4 : 1;
/* Pause the request first (if configured and the initial request). */
if (actionset->intercept_pause != NULL) {
if(strstr(actionset->intercept_pause,"%{") != NULL) {
msc_string *var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
var->value = (char *)actionset->intercept_pause;
var->value_len = strlen(actionset->intercept_pause);
expand_macros(msr, var, NULL, msr->mp);
pause = atoi(var->value);
if ((pause == LONG_MAX)||(pause == LONG_MIN)||(pause <= 0))
pause = 0;
msr_log(msr, (log_level > 3 ? log_level : log_level + 1), "Pausing transaction for "
"%d msec.", pause);
/* apr_sleep accepts microseconds */
apr_sleep((apr_interval_time_t)(pause * 1000));
} else {
pause = atoi(actionset->intercept_pause);
if ((pause == LONG_MAX)||(pause == LONG_MIN)||(pause <= 0))
pause = 0;
msr_log(msr, (log_level > 3 ? log_level : log_level + 1), "Pausing transaction for "
"%d msec.", pause);
/* apr_sleep accepts microseconds */
apr_sleep((apr_interval_time_t)(pause * 1000));
}
}
/* Determine how to respond and prepare the log message. */
switch(actionset->intercept_action) {
case ACTION_DENY :
if (actionset->intercept_status != 0) {
status = actionset->intercept_status;
message = apr_psprintf(msr->mp, "Access denied with code %d%s.",
status, phase_text);
} else {
log_level = 1;
status = HTTP_INTERNAL_SERVER_ERROR;
message = apr_psprintf(msr->mp, "Access denied with code 500%s "
"(Internal Error: Invalid status code requested %d).",
phase_text, actionset->intercept_status);
}
break;
case ACTION_PROXY :
#if !(defined(VERSION_IIS)) && !(defined(VERSION_NGINX)) && !(defined(VERSION_STANDALONE))
if (msr->phase < 3) {
if (ap_find_linked_module("mod_proxy.c") == NULL) {
log_level = 1;
status = HTTP_INTERNAL_SERVER_ERROR;
message = apr_psprintf(msr->mp, "Access denied with code 500%s "
"(Configuration Error: Proxy action to %s requested but mod_proxy not found).",
phase_text,
log_escape_nq(msr->mp, actionset->intercept_uri));
} else {
msr->r->filename = apr_psprintf(msr->mp, "proxy:%s", actionset->intercept_uri);
msr->r->proxyreq = PROXYREQ_REVERSE;
msr->r->handler = "proxy-server";
status = OK;
message = apr_psprintf(msr->mp, "Access denied using proxy to%s %s.",
phase_text,
log_escape_nq(msr->mp, actionset->intercept_uri));
}
} else {
log_level = 1;
status = HTTP_INTERNAL_SERVER_ERROR;
message = apr_psprintf(msr->mp, "Access denied with code 500%s "
"(Configuration Error: Proxy action requested but it does not work in output phases).",
phase_text);
}
#else
log_level = 1;
status = HTTP_INTERNAL_SERVER_ERROR;
message = apr_psprintf(msr->mp, "Access denied with code 500%s "
"(Configuration Error: Proxy action to %s requested but "
"proxy is only available in Apache version).",
phase_text,
log_escape_nq(msr->mp, actionset->intercept_uri));
#endif
break;
case ACTION_DROP :
/* ENH This does not seem to work on Windows. Is there a
* better way to drop a connection anyway?
*/
#if !defined(WIN32) && !defined(VERSION_NGINX)
{
extern module core_module;
apr_socket_t *csd;
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2 && AP_SERVER_PATCHLEVEL_NUMBER > 17
/* For mod_http2 used by HTTP/2 there is a virtual connection so must go through
* master to get the main connection or the drop request doesn't seem to do anything.
* For HTTP/1.1 master will not be defined so just go through normal connection.
* More details here: https://github.com/icing/mod_h2/issues/127
*/
if (msr->r->connection->master) {
csd = ap_get_module_config(msr->r->connection->master->conn_config, &core_module);
} else {
csd = ap_get_module_config(msr->r->connection->conn_config, &core_module);
}
#else
csd = ap_get_module_config(msr->r->connection->conn_config, &core_module);
#endif
if (csd) {
if (apr_socket_close(csd) == APR_SUCCESS) {
status = HTTP_FORBIDDEN;
message = apr_psprintf(msr->mp, "Access denied with connection close%s.",
phase_text);
} else {
log_level = 1;
status = HTTP_INTERNAL_SERVER_ERROR;
message = apr_psprintf(msr->mp, "Access denied with code 500%s "
"(Error: Connection drop requested but failed to close the "
" socket).",
phase_text);
}
} else {
log_level = 1;
status = HTTP_INTERNAL_SERVER_ERROR;
message = apr_psprintf(msr->mp, "Access denied with code 500%s "
"(Error: Connection drop requested but socket not found.",
phase_text);
}
}
#else
{
if (modsecDropAction == NULL) {
log_level = 1;
status = HTTP_INTERNAL_SERVER_ERROR;
message = apr_psprintf(msr->mp, "Access denied with code 500%s "
"(Error: Connection drop not implemented on this platform.",
phase_text);
} else if (modsecDropAction(msr->r) == 0) {
status = HTTP_FORBIDDEN;
message = apr_psprintf(msr->mp, "Access denied with connection close%s.",
phase_text);
} else {
log_level = 1;
status = HTTP_INTERNAL_SERVER_ERROR;
message = apr_psprintf(msr->mp, "Access denied with code 500%s "
"(Error: Connection drop request failed.",
phase_text);
}
}
#endif
break;
case ACTION_REDIRECT :
if(strstr(actionset->intercept_uri,"%{") != NULL) {
msc_string *var = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
var->value = (char *)actionset->intercept_uri;
var->value_len = strlen(actionset->intercept_uri);
expand_macros(msr, var, NULL, msr->mp);
apr_table_setn(msr->r->headers_out, "Location", var->value);
if ((actionset->intercept_status == 301)||(actionset->intercept_status == 302)
||(actionset->intercept_status == 303)||(actionset->intercept_status == 307))
{
status = actionset->intercept_status;
} else {
status = HTTP_MOVED_TEMPORARILY;
}
message = apr_psprintf(msr->mp, "Access denied with redirection to %s using "
"status %d%s.",
log_escape_nq(msr->mp, var->value), status,
phase_text);
} else {
apr_table_setn(msr->r->headers_out, "Location", actionset->intercept_uri);
if ((actionset->intercept_status == 301)||(actionset->intercept_status == 302)
||(actionset->intercept_status == 303)||(actionset->intercept_status == 307))
{
status = actionset->intercept_status;
} else {
status = HTTP_MOVED_TEMPORARILY;
}
message = apr_psprintf(msr->mp, "Access denied with redirection to %s using "
"status %d%s.",
log_escape_nq(msr->mp, actionset->intercept_uri), status,
phase_text);
}
break;
case ACTION_ALLOW :
status = DECLINED;
message = apr_psprintf(msr->mp, "Access allowed%s.", phase_text);
msr->was_intercepted = 0;
msr->allow_scope = ACTION_ALLOW;
break;
case ACTION_PAUSE :
status = DECLINED;
message = apr_psprintf(msr->mp, "Paused Access%s.", phase_text);
msr->was_intercepted = 0;
msr->allow_scope = ACTION_ALLOW;
break;
case ACTION_ALLOW_PHASE :
status = DECLINED;
message = apr_psprintf(msr->mp, "Access to phase allowed%s.", phase_text);
msr->was_intercepted = 0;
msr->allow_scope = ACTION_ALLOW_PHASE;
break;
case ACTION_ALLOW_REQUEST :
status = DECLINED;
message = apr_psprintf(msr->mp, "Access to request allowed%s.", phase_text);
msr->was_intercepted = 0;
msr->allow_scope = ACTION_ALLOW_REQUEST;
break;
default :
log_level = 1;
status = HTTP_INTERNAL_SERVER_ERROR;
message = apr_psprintf(msr->mp, "Access denied with code 500%s "
"(Internal Error: invalid interception action %d).",
phase_text, actionset->intercept_action);
break;
}
/* If the level is not high enough to add an alert message, but "auditlog"
* is enabled, then still add the message. */
if ((log_level > 3) && (actionset->auditlog != 0)) {
*(const char **)apr_array_push(msr->alerts) = msc_alert_message(msr, actionset, NULL, message);
}
/* Log the message now. */
msc_alert(msr, log_level, actionset, message, msr->intercept_message);
/* However, this will mark the txn relevant again if it is <= 3,
* which will mess up noauditlog. We need to compensate for this
* so that we do not increment twice when auditlog is enabled and
* prevent incrementing when auditlog is disabled.
*/
if ((actionset->auditlog == 0) && (log_level <= 3)) {
msr->is_relevant--;
}
return status;
}