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

@ -1670,18 +1670,79 @@ static const char *cmd_rule_perf_time(cmd_parms *cmd, void *_dcfg,
return NULL;
}
char *parser_conn_limits_operator(apr_pool_t *mp, const char *p2,
TreeRoot **whitelist, msre_ipmatch **whitelist_param,
TreeRoot **suspicious_list, msre_ipmatch **suspicious_list_param,
const char *filename)
{
int res = 0;
char *config_orig_path;
char *param = strchr(p2, ' ');
char *file = NULL;
char *error_msg = NULL;
param++;
config_orig_path = apr_pstrndup(mp, filename,
strlen(filename) - strlen(apr_filepath_name_get(filename)));
apr_filepath_merge(&file, config_orig_path, param, APR_FILEPATH_TRUENAME,
mp);
if ((strncasecmp(p2, "!@ipMatchFromFile", strlen("!@ipMatchFromFile")) == 0) ||
(strncasecmp(p2, "!@ipMatchF", strlen("!@ipMatchF")) == 0)) {
res = ip_tree_from_file(whitelist,
file, mp, NULL);
}
else if (strncasecmp(p2, "!@ipMatch", strlen("!@ipMatch")) == 0) {
res = ip_list_from_param(mp, param,
whitelist_param, &error_msg);
}
else if ((strncasecmp(p2, "@ipMatchFromFile", strlen("@ipMatchFromFile")) == 0) ||
(strncasecmp(p2, "@ipMatchF", strlen("@ipMatchF")) == 0)) {
res = ip_tree_from_file(suspicious_list,
file, mp, NULL);
}
else if (strncasecmp(p2, "@ipMatch", strlen("@ipMatch")) == 0) {
res = ip_list_from_param(mp, param,
suspicious_list_param, &error_msg);
}
else {
return apr_psprintf(mp, "ModSecurity: Invalid operator for " \
"SecReadStateLimit: %s, expected operators: @ipMatch, @ipMatchF " \
"or @ipMatchFromFile with or without !", p2);
}
if (res) {
char *error;
error = apr_psprintf(mp, "ModSecurity: failed to load IPs " \
"from: %s", param);
if (*error_msg) {
error = apr_psprintf(mp, "%s %s", error, error_msg);
}
return error;
}
return NULL;
}
/**
* \brief Add SecReadStateLimit configuration option
*
* \param cmd Pointer to configuration data
* \param _dcfg Pointer to directory configuration
* \param p1 Pointer to configuration option
* \param p2 Pointer to configuration option
*
* \retval NULL On failure
* \retval apr_psprintf On Success
*/
static const char *cmd_conn_read_state_limit(cmd_parms *cmd, void *_dcfg,
const char *p1)
const char *p1, const char *p2)
{
directory_config *dcfg = (directory_config *)_dcfg;
long int limit;
@ -1689,10 +1750,21 @@ static const char *cmd_conn_read_state_limit(cmd_parms *cmd, void *_dcfg,
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 SecReadStateLimit: %s", p1);
if ((limit == LONG_MAX) || (limit == LONG_MIN) || (limit <= 0)) {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for " \
"SecReadStateLimit: %s", p1);
}
if (p2 != NULL) {
char *param = parser_conn_limits_operator(cmd->pool, p2,
&conn_read_state_whitelist, &conn_read_state_whitelist_param,
&conn_read_state_suspicious_list,
&conn_read_state_suspicious_list_param, cmd->directive->filename);
if (param)
return param;
}
conn_read_state_limit = limit;
return NULL;
@ -1704,12 +1776,13 @@ static const char *cmd_conn_read_state_limit(cmd_parms *cmd, void *_dcfg,
* \param cmd Pointer to configuration data
* \param _dcfg Pointer to directory configuration
* \param p1 Pointer to configuration option
* \param p2 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)
const char *p1, const char *p2)
{
directory_config *dcfg = (directory_config *)_dcfg;
long int limit;
@ -1717,8 +1790,19 @@ static const char *cmd_conn_write_state_limit(cmd_parms *cmd, void *_dcfg,
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);
if ((limit == LONG_MAX) || (limit == LONG_MIN) || (limit <= 0)) {
return apr_psprintf(cmd->pool, "ModSecurity: Invalid value for " \
"SecWriteStateLimit: %s", p1);
}
if (p2 != NULL) {
char *param = parser_conn_limits_operator(cmd->pool, p2,
&conn_write_state_whitelist, &conn_write_state_whitelist_param,
&conn_write_state_suspicious_list,
&conn_write_state_suspicious_list_param, cmd->directive->filename);
if (param)
return param;
}
conn_write_state_limit = limit;
@ -3192,7 +3276,7 @@ const command_rec module_directives[] = {
"Threshold to log slow rules in usecs."
),
AP_INIT_TAKE1 (
AP_INIT_TAKE12 (
"SecReadStateLimit",
cmd_conn_read_state_limit,
NULL,
@ -3200,7 +3284,7 @@ const command_rec module_directives[] = {
"maximum number of threads in READ_BUSY state per ip address"
),
AP_INIT_TAKE1 (
AP_INIT_TAKE12 (
"SecWriteStateLimit",
cmd_conn_write_state_limit,
NULL,

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;
}
}
}

View File

@ -40,6 +40,7 @@ typedef struct msc_parm msc_parm;
#include "msc_util.h"
#include "msc_json.h"
#include "msc_xml.h"
#include "msc_tree.h"
#include "msc_geo.h"
#include "msc_gsb.h"
#include "msc_unicode.h"
@ -145,8 +146,16 @@ extern DSOLOCAL unsigned long int msc_pcre_match_limit_recursion;
extern DSOLOCAL int status_engine_state;
extern DSOLOCAL unsigned long int conn_read_state_limit;
extern DSOLOCAL TreeRoot *conn_read_state_whitelist;
extern DSOLOCAL TreeRoot *conn_read_state_suspicious_list;
extern DSOLOCAL msre_ipmatch *conn_read_state_whitelist_param;
extern DSOLOCAL msre_ipmatch *conn_read_state_suspicious_list_param;
extern DSOLOCAL unsigned long int conn_write_state_limit;
extern DSOLOCAL TreeRoot *conn_write_state_whitelist;
extern DSOLOCAL TreeRoot *conn_write_state_suspicious_list;
extern DSOLOCAL msre_ipmatch *conn_write_state_whitelist_param;
extern DSOLOCAL msre_ipmatch *conn_write_state_suspicious_list_param;
extern DSOLOCAL unsigned long int unicode_codepage;

View File

@ -538,7 +538,7 @@ int TreePrefixNetmask(modsec_rec *msr, TreePrefix *prefix, unsigned int netmask,
int ret = 0;
if (prefix == NULL) {
if (msr->txcfg->debuglog_level >= 9) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "TreePrefixNetmask: prefix is NULL.");
}
return 0;
@ -551,19 +551,19 @@ int TreePrefixNetmask(modsec_rec *msr, TreePrefix *prefix, unsigned int netmask,
if(prefix_data == NULL) return 0;
if (prefix_data->netmask != netmask) {
if (msr->txcfg->debuglog_level >= 9) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "TreePrefixNetmask: Cannot find a prefix with correct netmask.");
}
return 0;
} else {
if (msr->txcfg->debuglog_level >= 9) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "TreePrefixNetmask: Found a prefix with correct netmask.");
}
return 1;
}
}
if (msr->txcfg->debuglog_level >= 9) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "TreePrefixNetmask: Check if a prefix has a the correct netmask");
}
@ -576,14 +576,14 @@ TreeNode *CPTRetriveNode(modsec_rec *msr, unsigned char *buffer, unsigned int ip
unsigned int x, y;
if(node == NULL) {
if (msr->txcfg->debuglog_level >= 9) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTRetriveNode: Node tree is NULL.");
}
return NULL;
}
if(buffer == NULL) {
if (msr->txcfg->debuglog_level >= 9) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTRetriveNode: Empty ip address. Nothing to search for.");
}
return NULL;
@ -602,7 +602,7 @@ TreeNode *CPTRetriveNode(modsec_rec *msr, unsigned char *buffer, unsigned int ip
}
}
if (msr->txcfg->debuglog_level >= 9) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTRetriveNode: Found the node for provided ip address.");
}
@ -627,7 +627,7 @@ TreeNode *CPTFindElementIPNetblock(modsec_rec *msr, unsigned char *ipdata, unsig
node = CPTRetriveParentNode(node);
if (node == NULL) {
if (msr->txcfg->debuglog_level >= 9) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTFindElementIPNetblock: Node tree is NULL.");
}
return NULL;
@ -657,14 +657,14 @@ TreeNode *CPTFindElementIPNetblock(modsec_rec *msr, unsigned char *ipdata, unsig
node = CPTRetriveNode(msr, ipdata, ip_bitmask, node);
if (node && node->bit != ip_bitmask) {
if (msr->txcfg->debuglog_level >= 9) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTFindElementIPNetblock: Found a tree node but netmask is different.");
}
return NULL;
}
if (node && node->prefix == NULL) {
if (msr->txcfg->debuglog_level >= 9) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTFindElementIPNetblock: Found a tree node but prefix is NULL.");
}
return NULL;
@ -675,7 +675,7 @@ TreeNode *CPTFindElementIPNetblock(modsec_rec *msr, unsigned char *ipdata, unsig
if ((ip_bitmask % 8) == 0) {
if (TreePrefixNetmask(msr, node->prefix, netmask_node->netmasks[j], FALSE)) {
if (msr->txcfg->debuglog_level >= 9) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTFindElementIPNetblock: Node found for provided ip address");
}
return node;
@ -684,7 +684,7 @@ TreeNode *CPTFindElementIPNetblock(modsec_rec *msr, unsigned char *ipdata, unsig
if ((node->prefix->buffer[bytes] & mask) == (ipdata[bytes] & mask)) {
if (TreePrefixNetmask(msr, node->prefix, netmask_node->netmasks[j], FALSE)) {
if (msr->txcfg->debuglog_level >= 9) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTFindElementIPNetblock: Node found for provided ip address");
}
return node;
@ -704,14 +704,14 @@ TreeNode *CPTFindElement(modsec_rec *msr, unsigned char *ipdata, unsigned int ip
unsigned char temp_data[NETMASK_256-1];
if (tree == NULL) {
if (msr->txcfg->debuglog_level >= 9) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTFindElement: Tree is NULL. Cannot proceed searching the ip.");
}
return node;
}
if (tree->head == NULL) {
if (msr->txcfg->debuglog_level >= 9) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTFindElement: Tree head is NULL. Cannot proceed searching the ip.");
}
return node;
@ -720,7 +720,7 @@ TreeNode *CPTFindElement(modsec_rec *msr, unsigned char *ipdata, unsigned int ip
node = tree->head;
if (ip_bitmask > (NETMASK_256-1)) {
if (msr->txcfg->debuglog_level >= 9) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTFindElement: Netmask cannot be greater than 255");
}
return NULL;
@ -734,21 +734,21 @@ TreeNode *CPTFindElement(modsec_rec *msr, unsigned char *ipdata, unsigned int ip
node = CPTRetriveNode(msr, temp_data, ip_bitmask, node);
if (node && (node->bit != ip_bitmask)) {
if (msr->txcfg->debuglog_level >= 9) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTFindElement: Found a tree node but netmask is different.");
}
return NULL;
}
if(node == NULL) {
if (msr->txcfg->debuglog_level >= 9) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTFindElement: Node tree is NULL.");
}
return node;
}
if(node->prefix == NULL) {
if (msr->txcfg->debuglog_level >= 9) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTFindElement: Found a tree node but prefix is NULL.");
}
return node;
@ -759,7 +759,7 @@ TreeNode *CPTFindElement(modsec_rec *msr, unsigned char *ipdata, unsigned int ip
if ((ip_bitmask % 8) == 0) {
if (TreePrefixNetmask(msr, node->prefix, ip_bitmask, TRUE)) {
if (msr->txcfg->debuglog_level >= 9) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTFindElement: Node found for provided ip address");
}
return node;
@ -768,7 +768,7 @@ TreeNode *CPTFindElement(modsec_rec *msr, unsigned char *ipdata, unsigned int ip
if ((node->prefix->buffer[bytes] & mask) == (temp_data[bytes] & mask)) {
if (TreePrefixNetmask(msr, node->prefix, ip_bitmask, TRUE)) {
if (msr->txcfg->debuglog_level >= 9) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTFindElement: Node found for provided ip address");
}
return node;
@ -782,14 +782,14 @@ TreeNode *CPTFindElement(modsec_rec *msr, unsigned char *ipdata, unsigned int ip
TreeNode *CPTIpMatch(modsec_rec *msr, unsigned char *ipdata, CPTTree *tree, int type) {
if(tree == NULL) {
if (msr->txcfg->debuglog_level >= 9) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTIpMatch: Tree is NULL. Cannot proceed searching the ip.");
}
return NULL;
}
if(ipdata == NULL) {
if (msr->txcfg->debuglog_level >= 9) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTIpMatch: Empty ip address. Nothing to search for.");
}
return NULL;
@ -797,17 +797,17 @@ TreeNode *CPTIpMatch(modsec_rec *msr, unsigned char *ipdata, CPTTree *tree, int
switch(type) {
case IPV4_TREE:
if (msr->txcfg->debuglog_level >= 9) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTIpMatch: Searching ip type 0x%x", type);
}
return CPTFindElement(msr, ipdata, NETMASK_32, tree);
case IPV6_TREE:
if (msr->txcfg->debuglog_level >= 9) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTIpMatch: Searching ip type 0x%x", type);
}
return CPTFindElement(msr, ipdata, NETMASK_128, tree);
default:
if (msr->txcfg->debuglog_level >= 9) {
if (msr && msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "CPTIpMatch: Unknown ip type 0x%x", type);
}
return NULL;

View File

@ -2389,6 +2389,7 @@ char *construct_single_var(modsec_rec *msr, char *name) {
return (char *)vx->value;
}
<<<<<<< HEAD
/**
* @brief Transforms an apr_array_header_t to a text buffer
@ -2454,3 +2455,234 @@ not_enough_memory:
return headers_length;
}
int ip_tree_from_file(TreeRoot **rtree_, char *uri,
apr_pool_t *mp, char **error_msg)
{
TreeNode *tnode = NULL;
apr_status_t rc;
int line = 0;
apr_file_t *fd;
char *start;
char *end;
char buf[HUGE_STRING_LEN + 1]; // FIXME: 2013-10-29 zimmerle: dynamic?
char errstr[1024]; //
TreeRoot *rtree;
rtree = apr_palloc(mp, sizeof(TreeRoot));
if (rtree == NULL)
{
*error_msg = apr_psprintf(mp, "Failed allocating " \
"memory to TreeRoot.");
return -1;
}
memset(rtree, 0, sizeof(TreeRoot));
rtree->ipv4_tree = CPTCreateRadixTree(mp);
if (rtree->ipv4_tree == NULL)
{
*error_msg = apr_psprintf(mp, "ipmatchFromFile: Tree initialization " \
"failed.");
return -1;
}
rtree->ipv6_tree = CPTCreateRadixTree(mp);
if (rtree->ipv6_tree == NULL)
{
*error_msg = apr_psprintf(mp, "ipmatchFromFile: Tree initialization " \
"failed.");
return -1;
}
rc = apr_file_open(&fd, uri, APR_READ | APR_BUFFERED | APR_FILE_NOCLEANUP,
0, mp);
if (rc != APR_SUCCESS)
{
*error_msg = apr_psprintf(mp, "Could not open ipmatch file \"%s\": %s",
uri, apr_strerror(rc, errstr, 1024));
return -1;
}
while ((rc = apr_file_gets(buf, HUGE_STRING_LEN, fd)) != APR_EOF)
{
line++;
if (rc != APR_SUCCESS)
{
*error_msg = apr_psprintf(mp, "Could not read \"%s\" line %d: %s",
uri, line, apr_strerror(rc, errstr, 1024));
return -1;
}
start = buf;
while ((apr_isspace(*start) != 0) && (*start != '\0'))
{
start++;
}
for (end = start; end != NULL || *end != '\0' || *end != '\n'; end++)
{
if (apr_isxdigit(*end) || *end == '.' || *end == '/' || *end == ':')
{
continue;
}
if (*end != '\n')
{
*error_msg = apr_psprintf(mp, "Invalid char \"%c\" in line %d " \
"of file %s", *end, line, uri);
}
break;
}
*end = '\0';
if ((start == end) || (*start == '#'))
{
continue;
}
if (strchr(start, ':') == NULL)
{
tnode = TreeAddIP(start, rtree->ipv4_tree, IPV4_TREE);
}
else
{
tnode = TreeAddIP(start, rtree->ipv6_tree, IPV6_TREE);
}
if (tnode == NULL)
{
*error_msg = apr_psprintf(mp, "Could not add entry " \
"\"%s\" in line %d of file %s to IP list", start, line, uri);
return -1;
}
}
if (fd != NULL)
{
apr_file_close(fd);
}
*rtree_ = rtree;
return 0;
}
int tree_contains_ip(apr_pool_t *mp, TreeRoot *rtree,
const char *value, modsec_rec *msr, char **error_msg)
{
struct in_addr in;
#if APR_HAVE_IPV6
struct in6_addr in6;
#endif
if (rtree == NULL)
{
return 0;
}
if (strchr(value, ':') == NULL) {
if (inet_pton(AF_INET, value, &in) <= 0) {
*error_msg = apr_psprintf(mp, "IPmatchFromFile: bad IPv4 " \
"specification \"%s\".", value);
return -1;
}
if (CPTIpMatch(msr, (unsigned char *)&in.s_addr, rtree->ipv4_tree,
IPV4_TREE) != NULL) {
return 1;
}
}
#if APR_HAVE_IPV6
else {
if (inet_pton(AF_INET6, value, &in6) <= 0) {
*error_msg = apr_psprintf(mp, "IPmatchFromFile: bad IPv6 " \
"specification \"%s\".", value);
return -1;
}
if (CPTIpMatch(msr, (unsigned char *)&in6.s6_addr, rtree->ipv6_tree,
IPV6_TREE) != NULL) {
return 1;
}
}
#endif
return 0;
}
int ip_list_from_param(apr_pool_t *pool,
char *param, msre_ipmatch **last, char **error_msg)
{
char *saved = NULL;
char *str = NULL;
apr_status_t rv;
msre_ipmatch *current;
str = apr_strtok(param, ",", &saved);
while( str != NULL) {
const char *ipstr, *mask, *sep;
/* get the IP address and mask strings */
sep = strchr(str, '/');
if (sep) {
ipstr = apr_pstrndup(pool, str, (sep - str) );
mask = apr_pstrdup(pool, (sep + 1) );
}
else {
ipstr = apr_pstrdup(pool, str);
mask = NULL;
}
/* create a new msre_ipmatch containing a new apr_ipsubnet_t*, and add it to the linked list */
current = apr_pcalloc(pool, sizeof(msre_ipmatch));
rv = apr_ipsubnet_create(&current->ipsubnet, ipstr, mask, pool);
if ( rv != APR_SUCCESS ) {
char msgbuf[120];
apr_strerror(rv, msgbuf, sizeof msgbuf);
*error_msg = apr_pstrcat(pool, "Error: ", msgbuf, NULL);
return -1;
}
current->address = str;
current->next = NULL;
*last = current;
last = &current->next;
str = apr_strtok(NULL, ",",&saved);
}
return 0;
}
int list_contains_ip(apr_pool_t *mp, msre_ipmatch *current,
const char *value, char **error_msg)
{
apr_sockaddr_t *sa;
if (current == NULL)
{
return 0;
}
/* create an apr_sockaddr_t for the value string */
if (apr_sockaddr_info_get(&sa, value,
APR_UNSPEC, 0, 0, mp) != APR_SUCCESS ) {
*error_msg = apr_psprintf(mp, "ipMatch Internal Error: Invalid " \
"ip address.");
return -1;
}
/* look through the linked list for a match */
while (current) {
if (apr_ipsubnet_test(current->ipsubnet, sa)) {
*error_msg = apr_psprintf(mp, "IPmatch \"%s\" matched \"%s\"",
value, current->address);
return 1;
}
current = current->next;
}
return 0;
}

View File

@ -27,6 +27,7 @@
#include "modsecurity.h"
#include "re.h"
#include "msc_tree.h"
#ifdef WIN32
#include <ws2tcpip.h>
@ -148,4 +149,17 @@ unsigned char DSOLOCAL is_netmask_v4(char *ip_strv4);
unsigned char DSOLOCAL is_netmask_v6(char *ip_strv6);
int DSOLOCAL msc_headers_to_buffer(const apr_array_header_t *arr, char *buffer, int max_length);
int DSOLOCAL ip_tree_from_file(TreeRoot **rtree, char *uri,
apr_pool_t *mp, char **error_msg);
int DSOLOCAL tree_contains_ip(apr_pool_t *mp, TreeRoot *rtree,
const char *value, modsec_rec *msr, char **error_msg);
int DSOLOCAL ip_list_from_param(apr_pool_t *pool,
char *param, msre_ipmatch **last, char **error_msg);
int list_contains_ip(apr_pool_t *mp, msre_ipmatch *current,
const char *value, char **error_msg);
#endif

View File

@ -90,50 +90,21 @@ static int msre_op_nomatch_execute(modsec_rec *msr, msre_rule *rule,
* \retval 0 On Fail
*/
static int msre_op_ipmatch_param_init(msre_rule *rule, char **error_msg) {
apr_status_t rv;
char *str = NULL;
char *saved = NULL;
char *param = NULL;
msre_ipmatch *current;
msre_ipmatch **last = &rule->ip_op;
int res = 0;
if (error_msg == NULL)
return -1;
else
*error_msg = NULL;
param = apr_pstrdup(rule->ruleset->mp, rule->op_param);
param = apr_pstrdup(rule->ruleset->mp, rule->op_param);//, &rule->ip_op);
str = apr_strtok(param, ",", &saved);
while( str != NULL) {
const char *ipstr, *mask, *sep;
res = ip_list_from_param(rule->ruleset->mp, param, &rule->ip_op,
error_msg);
/* get the IP address and mask strings */
sep = strchr(str, '/');
if (sep) {
ipstr = apr_pstrndup(rule->ruleset->mp, str, (sep - str) );
mask = apr_pstrdup(rule->ruleset->mp, (sep + 1) );
}
else {
ipstr = apr_pstrdup(rule->ruleset->mp, str);
mask = NULL;
}
/* create a new msre_ipmatch containing a new apr_ipsubnet_t*, and add it to the linked list */
current = apr_pcalloc(rule->ruleset->mp, sizeof(msre_ipmatch));
rv = apr_ipsubnet_create(&current->ipsubnet, ipstr, mask, rule->ruleset->mp);
if ( rv != APR_SUCCESS ) {
char msgbuf[120];
apr_strerror(rv, msgbuf, sizeof msgbuf);
*error_msg = apr_pstrcat(rule->ruleset->mp, "Error: ", msgbuf, NULL);
return -1;
}
current->address = str;
current->next = NULL;
*last = current;
last = &current->next;
str = apr_strtok(NULL, ",",&saved);
}
if (res)
return 0;
return 1;
}
@ -152,7 +123,7 @@ static int msre_op_ipmatch_param_init(msre_rule *rule, char **error_msg) {
*/
static int msre_op_ipmatch_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) {
msre_ipmatch *current = rule->ip_op;
apr_sockaddr_t *sa;
int res = 0;
if (error_msg == NULL)
return -1;
@ -164,24 +135,21 @@ static int msre_op_ipmatch_execute(modsec_rec *msr, msre_rule *rule, msre_var *v
return 0;
}
/* create an apr_sockaddr_t for the value string */
if ( apr_sockaddr_info_get(&sa, var->value, APR_UNSPEC, 0, 0, msr->mp) != APR_SUCCESS ) {
msr_log(msr, 1, "ipMatch Internal Error: Invalid ip address.");
return 0;
res = list_contains_ip(msr->mp, current, var->value, error_msg);
if (res < 0) {
msr_log(msr, 1, "%s", *error_msg);
*error_msg = NULL;
}
if (res > 0) {
*error_msg = apr_psprintf(msr->mp, "%s at %s.", *error_msg, var->name);
}
/* look through the linked list for a match */
while (current) {
if (apr_ipsubnet_test(current->ipsubnet, sa)) {
*error_msg = apr_psprintf(msr->mp, "IPmatch \"%s\" matched \"%s\" at %s.", var->value, current->address, var->name);
return 1;
}
current = current->next;
}
return 0;
return res;
}
/**
* \brief Init function to ipmatchFromFile operator
*
@ -192,109 +160,45 @@ static int msre_op_ipmatch_execute(modsec_rec *msr, msre_rule *rule, msre_var *v
* \retval 0 On Fail
*/
static int msre_op_ipmatchFromFile_param_init(msre_rule *rule, char **error_msg) {
char errstr[1024];
char buf[HUGE_STRING_LEN + 1];
const char *rootpath = NULL;
const char *filepath = NULL;
char *fn;
char *start;
char *end;
const char *ipfile_path;
int line = 0;
unsigned short int op_len;
apr_status_t rc;
apr_file_t *fd;
const char *ipfile_path = NULL;
char *fn = NULL;
int res = 0;
TreeRoot *rtree = NULL;
TreeNode *tnode = NULL;
if (error_msg == NULL)
return -1;
else
*error_msg = NULL;
rtree = apr_palloc(rule->ruleset->mp, sizeof(TreeRoot));
if(rtree == NULL) {
*error_msg = apr_psprintf(rule->ruleset->mp, "Failed allocating memory to TreeRoot.");
return 0;
}
memset(rtree, 0, sizeof(TreeRoot));
if ((rule->op_param == NULL)||(strlen(rule->op_param) == 0)) {
*error_msg = apr_psprintf(rule->ruleset->mp, "Missing parameter for operator 'ipmatchFromFile'.");
return 0;
}
rtree->ipv4_tree = CPTCreateRadixTree(rule->ruleset->mp);
if (rtree->ipv4_tree == NULL) {
*error_msg = apr_psprintf(rule->ruleset->mp, "ipmatchFromFile: Tree tree initialization failed.");
return 0;
}
rtree->ipv6_tree = CPTCreateRadixTree(rule->ruleset->mp);
if (rtree->ipv6_tree == NULL) {
*error_msg = apr_psprintf(rule->ruleset->mp, "ipmatchFromFile: Tree tree initialization failed.");
if ((rule->op_param == NULL) || (strlen(rule->op_param) == 0))
{
*error_msg = apr_psprintf(rule->ruleset->mp, "Missing parameter for " \
"operator 'ipmatchFromFile'.");
return 0;
}
fn = apr_pstrdup(rule->ruleset->mp, rule->op_param);
ipfile_path = apr_pstrndup(rule->ruleset->mp, rule->filename, strlen(rule->filename) - strlen(apr_filepath_name_get(rule->filename)));
while((apr_isspace(*fn) != 0) && (*fn != '\0')) fn++;
while ((apr_isspace(*fn) != 0) && (*fn != '\0'))
{
fn++;
}
if (*fn == '\0') {
*error_msg = apr_psprintf(rule->ruleset->mp, "Empty file specification for operator ipmatchFromFile \"%s\"", fn);
*error_msg = apr_psprintf(rule->ruleset->mp, "Empty file specification " \
"for operator ipmatchFromFile \"%s\"", fn);
return 0;
}
filepath = fn;
if (apr_filepath_root(&rootpath, &filepath, APR_FILEPATH_TRUENAME, rule->ruleset->mp) != APR_SUCCESS) {
ipfile_path = apr_pstrndup(rule->ruleset->mp, rule->filename,
strlen(rule->filename) - strlen(apr_filepath_name_get(rule->filename)));
if (apr_filepath_root(&rootpath, &filepath, APR_FILEPATH_TRUENAME,
rule->ruleset->mp) != APR_SUCCESS) {
apr_filepath_merge(&fn, ipfile_path, fn, APR_FILEPATH_TRUENAME, rule->ruleset->mp);
}
rc = apr_file_open(&fd, fn, APR_READ | APR_BUFFERED | APR_FILE_NOCLEANUP, 0, rule->ruleset->mp);
if (rc != APR_SUCCESS) {
*error_msg = apr_psprintf(rule->ruleset->mp, "Could not open ipmatch file \"%s\": %s", fn, apr_strerror(rc, errstr, 1024));
res = ip_tree_from_file(&rtree, fn, rule->ruleset->mp, error_msg);
if (res)
{
return 0;
}
while((rc = apr_file_gets(buf, HUGE_STRING_LEN, fd)) != APR_EOF) {
line++;
if (rc != APR_SUCCESS) {
*error_msg = apr_psprintf(rule->ruleset->mp, "Could not read \"%s\" line %d: %s", fn, line, apr_strerror(rc, errstr, 1024));
return 0;
}
op_len = strlen(buf);
start = buf;
while ((apr_isspace(*start) != 0) && (*start != '\0')) start++;
for (end = start; end != NULL || *end != '\0' || *end != '\n'; end++) {
if (apr_isxdigit(*end) || *end == '.' || *end == '/' || *end == ':') {
continue;
}
else {
if (*end != '\n') {
*error_msg = apr_psprintf(rule->ruleset->mp, "Invalid char \"%c\" in line %d of file %s", *end, line, fn);
}
break;
}
}
*end = '\0';
if ((start == end) || (*start == '#')) continue;
if (strchr(start, ':') == NULL) {
tnode = TreeAddIP(start, rtree->ipv4_tree, IPV4_TREE);
}
else {
tnode = TreeAddIP(start, rtree->ipv6_tree, IPV6_TREE);
}
if (tnode == NULL) {
*error_msg = apr_psprintf(rule->ruleset->mp, "Could not add entry \"%s\" in line %d of file %s to IP list", start, line, fn);
}
}
if (fd != NULL) apr_file_close(fd);
rule->op_param_data = rtree;
return 1;
}
@ -311,13 +215,11 @@ static int msre_op_ipmatchFromFile_param_init(msre_rule *rule, char **error_msg)
* \retval 1 On Match
* \retval 0 On No Match
*/
static int msre_op_ipmatchFromFile_execute(modsec_rec *msr, msre_rule *rule,
msre_var *var, char **error_msg) {
static int msre_op_ipmatchFromFile_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) {
TreeRoot *rtree = (TreeRoot *)rule->op_param_data;
struct in_addr in;
#if APR_HAVE_IPV6
struct in6_addr in6;
#endif
int res = 0;
if (error_msg == NULL)
return -1;
@ -330,42 +232,22 @@ static int msre_op_ipmatchFromFile_execute(modsec_rec *msr, msre_rule *rule, msr
}
if (msr->txcfg->debuglog_level >= 4) {
msr_log(msr, 4, "IPmatchFromFile: Total tree entries: %d, ipv4 %d ipv6 %d", rtree->ipv4_tree->count+rtree->ipv6_tree->count,
msr_log(msr, 4, "IPmatchFromFile: Total tree entries: %d, ipv4 %d " \
"ipv6 %d", rtree->ipv4_tree->count+rtree->ipv6_tree->count,
rtree->ipv4_tree->count, rtree->ipv6_tree->count);
}
if (strchr(var->value, ':') == NULL) {
if (inet_pton(AF_INET, var->value, &in) <= 0) {
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "IPmatchFromFile: bad IPv4 specification \"%s\".", var->value);
}
*error_msg = apr_psprintf(msr->mp, "IPmatchFromFile: bad IP specification \"%s\".", var->value);
return 0;
}
res = tree_contains_ip(msr->mp, rtree, var->value, msr,
error_msg);
if (CPTIpMatch(msr, (unsigned char *)&in.s_addr, rtree->ipv4_tree, IPV4_TREE) != NULL) {
*error_msg = apr_psprintf(msr->mp, "IPmatchFromFile \"%s\" matched at %s.", var->value, var->name);
return 1;
}
}
#if APR_HAVE_IPV6
else {
if (inet_pton(AF_INET6, var->value, &in6) <= 0) {
if (msr->txcfg->debuglog_level >= 9) {
msr_log(msr, 9, "IPmatchFromFile: bad IPv6 specification \"%s\".", var->value);
}
*error_msg = apr_psprintf(msr->mp, "IPmatchFromFile: bad IP specification \"%s\".", var->value);
return 0;
}
if (res < 0)
msr_log(msr, 9, "%s", *error_msg);
if (CPTIpMatch(msr, (unsigned char *)&in6.s6_addr, rtree->ipv6_tree, IPV6_TREE) != NULL) {
*error_msg = apr_psprintf(msr->mp, "IPmatchFromFile \"%s\" matched at %s.", var->value, var->name);
return 1;
}
}
#endif
if (res > 0)
*error_msg = apr_psprintf(msr->mp, "IPmatchFromFile: \"%s\" matched at " \
"%s.", var->value, var->name);
return 0;
return res;
}
/* rsub */