diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index 6324ada2..540e496e 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -1440,6 +1440,34 @@ static const char *cmd_conn_read_state_limit(cmd_parms *cmd, void *_dcfg, return NULL; } +/* +* \brief Add SecWriteStateLimit configuration option +* +* \param cmd Pointer to configuration data +* \param _dcfg Pointer to directory configuration +* \param p1 Pointer to configuration option +* +* \retval NULL On failure +* \retval apr_psprintf On Success +*/ +static const char *cmd_conn_write_state_limit(cmd_parms *cmd, void *_dcfg, + const char *p1) +{ + directory_config *dcfg = (directory_config *)_dcfg; + long int limit; + + if (dcfg == NULL) return NULL; + + limit = strtol(p1, NULL, 10); + if ((limit == LONG_MAX)||(limit == LONG_MIN)||(limit <= 0)) { + return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for SecWriteStateLimit: %s", p1); + } + + conn_write_state_limit = limit; + + return NULL; +} + static const char *cmd_request_body_inmemory_limit(cmd_parms *cmd, void *_dcfg, const char *p1) @@ -2347,6 +2375,14 @@ const command_rec module_directives[] = { "maximum number of threads in READ_BUSY state per ip address" ), + AP_INIT_TAKE1 ( + "SecWriteStateLimit", + cmd_conn_write_state_limit, + NULL, + CMD_SCOPE_ANY, + "maximum number of threads in WRITE_BUSY state per ip address" + ), + AP_INIT_TAKE1 ( "SecRequestBodyInMemoryLimit", cmd_request_body_inmemory_limit, diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index 5ee72b04..85adf233 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -57,6 +57,8 @@ unsigned long int DSOLOCAL msc_pcre_match_limit_recursion = 0; unsigned long int DSOLOCAL conn_read_state_limit = 0; +unsigned long int DSOLOCAL conn_write_state_limit = 0; + static int server_limit, thread_limit; typedef struct { @@ -1200,13 +1202,13 @@ static int hook_connection_early(conn_rec *conn) { sb_handle *sb = conn->sbh; int i, j; - unsigned long int ip_count = 0; + unsigned long int ip_count = 0, ip_count_w = 0; worker_score *ws_record = NULL; #if APR_MAJOR_VERSION > 1 ap_sb_handle_t *sbh = NULL; #endif - if(sb != NULL && conn_read_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) @@ -1235,15 +1237,22 @@ static int hook_connection_early(conn_rec *conn) if (strcmp(conn->remote_ip, ws_record->client) == 0) ip_count++; break; + case SERVER_BUSY_WRITE: + if (strcmp(conn->remote_ip, ws_record->client) == 0) + ip_count_w++; + break; default: break; } } } - if (ip_count > conn_read_state_limit) { + if ((conn_read_state_limit > 0) && (ip_count > conn_read_state_limit)) { 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); return OK; + } else if ((conn_write_state_limit > 0) && (ip_count_w > conn_write_state_limit)) { + 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); + return OK; } else { return DECLINED; } diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index d7e8122f..a5d3dfa2 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -134,6 +134,8 @@ extern DSOLOCAL unsigned long int msc_pcre_match_limit_recursion; extern DSOLOCAL unsigned long int conn_read_state_limit; +extern DSOLOCAL unsigned long int conn_write_state_limit; + #define RESBODY_STATUS_NOT_READ 0 /* we were not configured to read the body */ #define RESBODY_STATUS_ERROR 1 /* error occured while we were reading the body */ #define RESBODY_STATUS_PARTIAL 2 /* partial body content available in the brigade */