diff --git a/CHANGES b/CHANGES index 3be55b24..4efae210 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,9 @@ 01 Oct 2007 - 2.5.0-dev3 ------------------------ + * Added a @containsWord operator that will match a given string anywhere in + the target value, but only on word boundaries. + * Used new TX:LAST_MATCHED_VAR_NAME to store the last matched variable name so that it can be more easily used by rules. diff --git a/apache2/re_operators.c b/apache2/re_operators.c index 2cdf6f4a..a91cf90d 100644 --- a/apache2/re_operators.c +++ b/apache2/re_operators.c @@ -411,7 +411,7 @@ static int msre_op_within_execute(modsec_rec *msr, msre_rule *rule, msre_var *va i_max = match_length - target_length; for (i = 0; i <= i_max; i++) { if (match[i] == target[0]) { - if (strncmp(target, (match + i), target_length) == 0) { + if (memcmp((target + 1), (match + i + 1), (target_length - 1)) == 0) { /* match. */ *error_msg = apr_psprintf(msr->mp, "String match %s=\"%s\" within \"%s\".", var->name, @@ -476,7 +476,7 @@ static int msre_op_contains_execute(modsec_rec *msr, msre_rule *rule, msre_var * i_max = target_length - match_length; for (i = 0; i <= i_max; i++) { if (target[i] == match[0]) { - if (strncmp(match, (target + i), match_length) == 0) { + if (memcmp((match + 1), (target + i + 1), (match_length - 1)) == 0) { /* Match. */ *error_msg = apr_psprintf(msr->mp, "String match \"%s\" at %s.", log_escape_ex(msr->mp, match, match_length), @@ -490,6 +490,98 @@ static int msre_op_contains_execute(modsec_rec *msr, msre_rule *rule, msre_var * return 0; } +/* containsWord */ + +static int msre_op_containsWord_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { + msc_string *str = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string)); + const char *match = NULL; + const char *target; + unsigned int match_length; + unsigned int target_length = 0; + unsigned int i, i_max; + + str->value = (char *)rule->op_param; + str->value_len = strlen(str->value); + + if (error_msg == NULL) return -1; + *error_msg = NULL; + + if (str->value == NULL) { + *error_msg = "Internal Error: match string is null."; + return -1; + } + + expand_macros(msr, str, rule, msr->mp); + + match = (const char *)str->value; + match_length = str->value_len; + + /* If the given target is null run against an empty + * string. This is a behaviour consistent with previous + * releases. + */ + if (var->value == NULL) { + target = ""; + target_length = 0; + } else { + target = var->value; + target_length = var->value_len; + } + + /* These are impossible to match */ + if ((match_length == 0) || (match_length > target_length)) { + /* No match. */ + return 0; + } + + /* scan for first character, then compare from there until we + * have a match or there is no room left in the target + */ + i_max = target_length - match_length; + for (i = 0; i <= i_max; i++) { + + /* Previous char must have been a start or non-word */ + if ((i > 0) && (isalnum(target[i-1])||(target[i-1] == '_'))) + continue; + + + /* First character matched */ + if (target[i] == match[0]) { + + /* Maybe a match. */ + *error_msg = apr_psprintf(msr->mp, "String match \"%s\" at %s.", + log_escape_ex(msr->mp, match, match_length), + var->name); + + /* only one character */ + if (match_length == 1) return 1; + + /* remaining matched */ + if (memcmp((match + 1), (target + i + 1), (match_length - 1)) == 0) { + + + /* check boundaries */ + if (i == i_max) { + /* exact/end word match */ + return 1; + } + else if (!(isalnum(target[i+match_length])||(target[i+match_length] == '_'))) { + /* start/mid word match */ + return 1; + } + + /* No word match */ + *error_msg = NULL; + return 0; + } + } + } + + /* No match. */ + *error_msg = NULL; + return 0; +} + /* streq */ static int msre_op_streq_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) { @@ -533,7 +625,7 @@ static int msre_op_streq_execute(modsec_rec *msr, msre_rule *rule, msre_var *var return 0; } - if (strncmp(match, target, target_length) == 0) { + if (memcmp(match, target, target_length) == 0) { /* Match. */ *error_msg = apr_psprintf(msr->mp, "String match \"%s\" at %s.", log_escape_ex(msr->mp, match, match_length), @@ -588,7 +680,7 @@ static int msre_op_beginsWith_execute(modsec_rec *msr, msre_rule *rule, msre_var return 0; } - if (strncmp(match, target, match_length) == 0) { + if (memcmp(match, target, match_length) == 0) { /* Match. */ *error_msg = apr_psprintf(msr->mp, "String match \"%s\" at %s.", log_escape_ex(msr->mp, match, match_length), @@ -643,7 +735,7 @@ static int msre_op_endsWith_execute(modsec_rec *msr, msre_rule *rule, msre_var * return 0; } - if (strncmp(match, (target + (target_length - match_length)), match_length) == 0) { + if (memcmp(match, (target + (target_length - match_length)), match_length) == 0) { /* Match. */ *error_msg = apr_psprintf(msr->mp, "String match \"%s\" at %s.", log_escape_ex(msr->mp, match, match_length), @@ -1501,6 +1593,13 @@ void msre_engine_register_default_operators(msre_engine *engine) { msre_op_contains_execute ); + /* containsWord */ + msre_engine_op_register(engine, + "containsWord", + NULL, /* ENH init function to flag var substitution */ + msre_op_containsWord_execute + ); + /* is */ msre_engine_op_register(engine, "streq",