diff --git a/apache2/msc_util.c b/apache2/msc_util.c index 885b378b..33d0ddba 100644 --- a/apache2/msc_util.c +++ b/apache2/msc_util.c @@ -2453,6 +2453,32 @@ not_enough_memory: return headers_length; } +int read_line(char *buf, int len, FILE *fp) +{ + char *tmp; + + if (buf == NULL) + { + return -1; + } + + memset(buf, '\0', len*sizeof(char)); + + if (fgets(buf, len, fp) == NULL) + { + *buf = '\0'; + return 0; + } + else + { + if ((tmp = strrchr(buf, '\n')) != NULL) + { + *tmp = '\0'; + } + } + + return 1; +} int create_radix_tree(apr_pool_t *mp, TreeRoot **rtree, char **error_msg) { diff --git a/apache2/msc_util.h b/apache2/msc_util.h index dc0f8c85..2ef30e1d 100644 --- a/apache2/msc_util.h +++ b/apache2/msc_util.h @@ -159,4 +159,6 @@ int DSOLOCAL tree_contains_ip(apr_pool_t *mp, TreeRoot *rtree, int DSOLOCAL ip_tree_from_param(apr_pool_t *pool, char *param, TreeRoot **rtree, char **error_msg); +int read_line(char *buff, int size, FILE *fp); + #endif diff --git a/apache2/re.h b/apache2/re.h index df659a4b..836e2af2 100644 --- a/apache2/re.h +++ b/apache2/re.h @@ -409,4 +409,9 @@ struct msre_cache_rec { apr_size_t val_len; }; +struct fuzzy_hash_param_data { + const char *file; + int threshold; +}; + #endif diff --git a/apache2/re_operators.c b/apache2/re_operators.c index aa7dd849..72c994b7 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -27,6 +27,10 @@ #include #endif +#ifdef WITH_SSDEEP +#include "fuzzy.h" +#endif + #include "libinjection/libinjection.h" /** @@ -3718,6 +3722,142 @@ static int msre_op_rbl_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, return 0; } +/* fuzzyHash */ +static int msre_op_fuzzy_hash_init(msre_rule *rule, char **error_msg) +{ +#ifdef WITH_SSDEEP + struct fuzzy_hash_param_data *param_data; + char *file; + int param_len,threshold; + + char *data = NULL; + char *threshold_str = NULL; + + param_data = apr_palloc(rule->ruleset->mp, + sizeof(struct fuzzy_hash_param_data)); + + data = apr_pstrdup(rule->ruleset->mp, rule->op_param); + threshold_str = data; +#endif + + if (error_msg == NULL) + { + return -1; + } + + *error_msg = NULL; + +#ifdef WITH_SSDEEP + /* Sanity check */ + param_len = strlen(threshold_str); + threshold_str = threshold_str + param_len; + + if (param_len < 3) + { + goto invalid_parameters; + } + + while (param_len - 1 > 0 && *threshold_str != ' ') + { + param_len--; + threshold_str--; + } + + *threshold_str = '\0'; + threshold_str++; + file = data; + threshold = atoi(threshold_str); + + if ((file == NULL) || (is_empty_string(file)) || (threshold > 100) || + (threshold < 1)) + { + goto invalid_parameters; + } + + file = resolve_relative_path(rule->ruleset->mp, rule->filename, file); + + if (!fopen(file, "r")) + { + *error_msg = apr_psprintf(rule->ruleset->mp, "Not able to open file:" \ + " %s.", file); + return -1; + } + + + param_data->file = file; + param_data->threshold = threshold; + + rule->op_param_data = param_data; +#else + *error_msg = apr_psprintf(rule->ruleset->mp, "ModSecurity was not " \ + "compiled with ssdeep support."); + + rule->op_param_data = NULL; + + return -1; +#endif + return 1; + +invalid_parameters: + *error_msg = apr_psprintf(rule->ruleset->mp, "Operator @fuzzyHash " \ + "requires valid parameters. File and threshold."); + return -1; +} + +static int msre_op_fuzzy_hash_execute(modsec_rec *msr, msre_rule *rule, + msre_var *var, char **error_msg) +{ + +#ifdef WITH_SSDEEP + char result[FUZZY_MAX_RESULT]; + struct fuzzy_hash_param_data *param = rule->op_param_data; + char line[1024]; +#endif + + if (error_msg == NULL) + { + return -1; + } + + *error_msg = NULL; + +#ifdef WITH_SSDEEP + if (fuzzy_hash_buf(var->value, var->value_len, result)) + { + *error_msg = apr_psprintf(rule->ruleset->mp, "Problems generating " \ + "fuzzy hash."); + + return -1; + } + + FILE *fp = fopen(param->file, "r"); + if (!fp) + { + *error_msg = apr_psprintf(rule->ruleset->mp, "Not able to open " \ + "fuzzy hash file: %s", param->file); + + return 1; + } + + while (read_line(line, sizeof(line), fp)) + { + int i = fuzzy_compare(line, result); + if (i >= param->threshold) + { + *error_msg = apr_psprintf(msr->mp, "Fuzzy hash of %s matched " \ + "with %s (from: %s). Socore: %d.", var->name, line, + param->file, i); + return 1; + } + } + + fclose(fp); +#endif + + /* No match. */ + return 0; +} + /* inspectFile */ static int msre_op_inspectFile_init(msre_rule *rule, char **error_msg) { @@ -4552,6 +4692,13 @@ void msre_engine_register_default_operators(msre_engine *engine) { msre_op_inspectFile_execute ); + /* fuzzy_hash */ + msre_engine_op_register(engine, + "fuzzyHash", + msre_op_fuzzy_hash_init, + msre_op_fuzzy_hash_execute + ); + /* validateByteRange */ msre_engine_op_register(engine, "validateByteRange",