Adds support to suspicious and whitelist to Read and Write limits

The operators @ipMatch, @ipMatchF and @ipMatchFromFile were
added to the functions: SecReadStateLimit and SecReadStateLimit,
by using them it is possible to declare a suspicious list. When
a suspicious list is given, the {Read|Write}StateLimit will be
applied just to the IPs that belongs to that restricted list.
Note that the negative of those operators (e.g. !@ipMatch) can be
used to place a whitelist. The {Read|Write}StateLimit
restrictions will not be applied to those in the whitelist.
This current version the Sec{Read|Write}StateLimit can be used
varios times to add elements to both lists, however, the
last informed limit will be applied for the entire group. This
feature is experimental, and suggestions on how to improve it
are very welcome. For further discussion use the issue: #353.
This commit is contained in:
Felipe Zimmerle
2013-10-30 19:54:09 -07:00
parent 8ff3de5b6f
commit b9fdc4fe3b
7 changed files with 519 additions and 242 deletions

View File

@@ -64,6 +64,15 @@ unsigned long int DSOLOCAL msc_pcre_match_limit_recursion = 0;
int DSOLOCAL status_engine_state = STATUS_ENGINE_DISABLED;
unsigned long int DSOLOCAL conn_read_state_limit = 0;
TreeRoot DSOLOCAL *conn_read_state_whitelist = 0;
TreeRoot DSOLOCAL *conn_read_state_suspicious_list = 0;
msre_ipmatch DSOLOCAL *conn_read_state_whitelist_param = 0;
msre_ipmatch DSOLOCAL *conn_read_state_suspicious_list_param = 0;
TreeRoot DSOLOCAL *conn_write_state_whitelist = 0;
TreeRoot DSOLOCAL *conn_write_state_suspicious_list = 0;
msre_ipmatch DSOLOCAL *conn_write_state_whitelist_param = 0;
msre_ipmatch DSOLOCAL *conn_write_state_suspicious_list_param = 0;
unsigned long int DSOLOCAL conn_write_state_limit = 0;
@@ -1363,29 +1372,30 @@ static int hook_connection_early(conn_rec *conn)
{
sb_handle *sb = conn->sbh;
int i, j;
unsigned long int ip_count = 0, ip_count_w = 0;
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;
char *client_ip = conn->client_ip;
#else
char *client_ip = conn->remote_ip;
#endif
if(sb != NULL && (conn_read_state_limit > 0 || conn_write_state_limit > 0)) {
if (sb != NULL && (conn_read_state_limit > 0 || conn_write_state_limit > 0)) {
ws_record = &ap_scoreboard_image->servers[sb->child_num][sb->thread_num];
if(ws_record == NULL)
if (ws_record == NULL)
return DECLINED;
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2
apr_cpystrn(ws_record->client, conn->client_ip, sizeof(ws_record->client));
#else
apr_cpystrn(ws_record->client, conn->remote_ip, sizeof(ws_record->client));
#endif
apr_cpystrn(ws_record->client, client_ip, sizeof(ws_record->client));
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) {
if (sbh == NULL) {
return DECLINED;
}
@@ -1394,27 +1404,19 @@ static int hook_connection_early(conn_rec *conn)
ws_record = ap_get_scoreboard_worker(i, j);
#endif
if(ws_record == NULL)
if (ws_record == NULL)
return DECLINED;
switch (ws_record->status) {
case SERVER_BUSY_READ:
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2
if (strcmp(conn->client_ip, ws_record->client) == 0)
ip_count++;
#else
if (strcmp(conn->remote_ip, ws_record->client) == 0)
ip_count++;
#endif
if (strcmp(client_ip, ws_record->client) == 0)
ip_count_r++;
break;
case SERVER_BUSY_WRITE:
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2
if (strcmp(conn->client_ip, ws_record->client) == 0)
if (strcmp(client_ip, ws_record->client) == 0)
ip_count_w++;
#else
if (strcmp(conn->remote_ip, ws_record->client) == 0)
ip_count_w++;
#endif
break;
default:
break;
@@ -1422,22 +1424,76 @@ static int hook_connection_early(conn_rec *conn)
}
}
if ((conn_read_state_limit > 0) && (ip_count > conn_read_state_limit)) {
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, "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,conn_read_state_limit,conn->client_ip);
#else
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, "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,conn_read_state_limit,conn->remote_ip);
#endif
return OK;
} else if ((conn_write_state_limit > 0) && (ip_count_w > conn_write_state_limit)) {
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 2
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, "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,conn_write_state_limit,conn->client_ip);
#else
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, "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,conn_write_state_limit,conn->remote_ip);
#endif
return OK;
} else {
return DECLINED;
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) ||
(list_contains_ip(conn->pool,
conn_read_state_suspicious_list_param, client_ip, &error_msg) <= 0))))
{
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL,
"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", ip_count_r,
conn_read_state_limit, client_ip);
}
else if ((tree_contains_ip(conn->pool,
conn_read_state_whitelist, client_ip, NULL, &error_msg) > 0) ||
(list_contains_ip(conn->pool,
conn_read_state_whitelist_param, client_ip, &error_msg) > 0))
{
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL,
"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,
"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,
conn_read_state_limit, client_ip);
return OK;
}
}
if (conn_write_state_limit > 0 && ip_count_w > conn_write_state_limit)
{
if (conn_write_state_suspicious_list &&
(!((tree_contains_ip(conn->pool,
conn_write_state_suspicious_list, client_ip, NULL, &error_msg) <= 0) ||
(list_contains_ip(conn->pool,
conn_write_state_suspicious_list_param, client_ip, &error_msg) <= 0))))
{
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL,
"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", ip_count_w,
conn_read_state_limit, client_ip);
}
else if ((tree_contains_ip(conn->pool,
conn_write_state_whitelist, client_ip, NULL, &error_msg) > 0) ||
(list_contains_ip(conn->pool,
conn_write_state_whitelist_param, client_ip, &error_msg) > 0))
{
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL,
"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,
"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,
conn_write_state_limit, client_ip);
return OK;
}
}
}