Skip to content

Fix SecConn(Read|Write)StateLimit on Apache 2.4 #1340

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 48 additions & 30 deletions apache2/mod_security2.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
#include "apr_optional.h"
#include "mod_log_config.h"

#ifdef APLOG_USE_MODULE
APLOG_USE_MODULE(security2);
#endif

#include "msc_logging.h"
#include "msc_util.h"

Expand Down Expand Up @@ -92,11 +96,6 @@ int (*modsecDropAction)(request_rec *r) = NULL;
#endif
static int server_limit, thread_limit;

typedef struct {
int child_num;
int thread_num;
} sb_handle;

/* -- Miscellaneous functions -- */

/**
Expand Down Expand Up @@ -691,6 +690,11 @@ static int hook_post_config(apr_pool_t *mp, apr_pool_t *mp_log, apr_pool_t *mp_t
change_server_signature(s);
}

/* For connection level hook */
ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit);
ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &server_limit);


#if (!(defined(WIN32) || defined(NETWARE)))

/* Internal chroot functionality */
Expand Down Expand Up @@ -1431,36 +1435,50 @@ static void modsec_register_operator(const char *name, void *fn_init, void *fn_e
*/
static int hook_connection_early(conn_rec *conn)
{
sb_handle *sb = conn->sbh;
int i, j;
unsigned long int ip_count_r = 0, ip_count_w = 0;
char *error_msg;
worker_score *ws_record = NULL;
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2
ap_sb_handle_t *sbh = NULL;
ap_sb_handle_t *sbh = conn->sbh;
char *client_ip = conn->client_ip;
#else
sb_handle *sbh = conn->sbh;
char *client_ip = conn->remote_ip;
#endif
int i, j;
unsigned long int ip_count_r = 0, ip_count_w = 0;
char *error_msg;
worker_score *ws_record = NULL;

if (sb != NULL && (conn_read_state_limit > 0 || conn_write_state_limit > 0)) {
if (sbh != NULL && (conn_read_state_limit > 0 || conn_write_state_limit > 0)) {

ws_record = &ap_scoreboard_image->servers[sb->child_num][sb->thread_num];
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2
ws_record = ap_get_scoreboard_worker(sbh);
#else
ws_record = ap_get_scoreboard_worker(sbh->child_num, sbh->thread_num);
#endif
if (ws_record == NULL)
return DECLINED;

apr_cpystrn(ws_record->client, client_ip, sizeof(ws_record->client));
/* If ws_record does not have correct ip yet, we count it already */
if (strcmp(client_ip, ws_record->client) != 0) {
switch (ws_record->status) {
case SERVER_BUSY_READ:
ip_count_r++;
break;
case SERVER_BUSY_WRITE:
ip_count_w++;
break;
default:
break;
}
}

ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, conn,
"ModSecurity: going to loop through %d servers with %d threads",
server_limit, thread_limit);
for (i = 0; i < server_limit; ++i) {
for (j = 0; j < thread_limit; ++j) {

#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2
sbh = conn->sbh;
if (sbh == NULL) {
return DECLINED;
}

ws_record = ap_get_scoreboard_worker(sbh);
ws_record = ap_get_scoreboard_worker_from_indexes(i, j);
#else
ws_record = ap_get_scoreboard_worker(i, j);
#endif
Expand All @@ -1485,14 +1503,18 @@ static int hook_connection_early(conn_rec *conn)
}
}

ap_log_cerror(APLOG_MARK, APLOG_TRACE3, 0, conn,
"ModSecurity: threads in READ: %ld of %ld, WRITE: %ld of %ld, IP: %s",
ip_count_r, conn_read_state_limit, ip_count_w, conn_write_state_limit, client_ip);

if (conn_read_state_limit > 0 && ip_count_r > conn_read_state_limit)
{
if (conn_read_state_suspicious_list &&
(tree_contains_ip(conn->pool,
conn_read_state_suspicious_list, client_ip, NULL, &error_msg) <= 0))
{
if (conn_limits_filter_state == MODSEC_DETECTION_ONLY)
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL,
ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, conn,
"ModSecurity: Too many threads [%ld] of %ld allowed " \
"in READ state from %s - There is a suspission list " \
"but that IP is not part of it, access granted",
Expand All @@ -1502,15 +1524,15 @@ static int hook_connection_early(conn_rec *conn)
conn_read_state_whitelist, client_ip, NULL, &error_msg) > 0)
{
if (conn_limits_filter_state == MODSEC_DETECTION_ONLY)
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL,
ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, conn,
"ModSecurity: Too many threads [%ld] of %ld allowed " \
"in READ state from %s - Ip is on whitelist, access " \
"granted", ip_count_r, conn_read_state_limit,
client_ip);
}
else
{
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL,
ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, conn,
"ModSecurity: Access denied with code 400. Too many " \
"threads [%ld] of %ld allowed in READ state from %s - " \
"Possible DoS Consumption Attack [Rejected]", ip_count_r,
Expand All @@ -1528,7 +1550,7 @@ static int hook_connection_early(conn_rec *conn)
conn_write_state_suspicious_list, client_ip, NULL, &error_msg) <= 0))
{
if (conn_limits_filter_state == MODSEC_DETECTION_ONLY)
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL,
ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, conn,
"ModSecurity: Too many threads [%ld] of %ld allowed " \
"in WRITE state from %s - There is a suspission list " \
"but that IP is not part of it, access granted",
Expand All @@ -1538,15 +1560,15 @@ static int hook_connection_early(conn_rec *conn)
conn_write_state_whitelist, client_ip, NULL, &error_msg) > 0)
{
if (conn_limits_filter_state == MODSEC_DETECTION_ONLY)
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL,
ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, conn,
"ModSecurity: Too many threads [%ld] of %ld allowed " \
"in WRITE state from %s - Ip is on whitelist, " \
"access granted", ip_count_w, conn_read_state_limit,
client_ip);
}
else
{
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL,
ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, conn,
"ModSecurity: Access denied with code 400. Too many " \
"threads [%ld] of %ld allowed in WRITE state from %s - " \
"Possible DoS Consumption Attack [Rejected]", ip_count_w,
Expand Down Expand Up @@ -1655,10 +1677,6 @@ static void register_hooks(apr_pool_t *mp) {
APR_REGISTER_OPTIONAL_FN(modsec_register_reqbody_processor);
#endif

/* For connection level hook */
ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit);
ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &server_limit);

/* Main hooks */
ap_hook_pre_config(hook_pre_config, NULL, NULL, APR_HOOK_FIRST);
ap_hook_post_config(hook_post_config, postconfig_beforeme_list,
Expand Down