diff --git a/apache2/apache2_config.c b/apache2/apache2_config.c index 6e0dc5d4..9d2d9ffa 100644 --- a/apache2/apache2_config.c +++ b/apache2/apache2_config.c @@ -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, diff --git a/apache2/mod_security2.c b/apache2/mod_security2.c index 2d67b310..69d84cfa 100644 --- a/apache2/mod_security2.c +++ b/apache2/mod_security2.c @@ -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; + } } } diff --git a/apache2/modsecurity.h b/apache2/modsecurity.h index 429806d3..6386781d 100644 --- a/apache2/modsecurity.h +++ b/apache2/modsecurity.h @@ -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; diff --git a/apache2/msc_tree.c b/apache2/msc_tree.c index ce0e70af..127ae24a 100644 --- a/apache2/msc_tree.c +++ b/apache2/msc_tree.c @@ -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; diff --git a/apache2/msc_util.c b/apache2/msc_util.c index b3fbb265..092a2610 100644 --- a/apache2/msc_util.c +++ b/apache2/msc_util.c @@ -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(¤t->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 = ¤t->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; +} + diff --git a/apache2/msc_util.h b/apache2/msc_util.h index d5f19151..7d6588ba 100644 --- a/apache2/msc_util.h +++ b/apache2/msc_util.h @@ -27,6 +27,7 @@ #include "modsecurity.h" #include "re.h" +#include "msc_tree.h" #ifdef WIN32 #include @@ -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 diff --git a/apache2/re_operators.c b/apache2/re_operators.c index f5f4700c..41a4f8e3 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -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(¤t->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 = ¤t->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 */