mirror of
https://github.com/owasp-modsecurity/ModSecurity.git
synced 2025-08-13 21:36:00 +03:00
Resources load mechanism as the SecRemoteRuels were not showing information about the loaded rules while Apache was reloaded. This patch add such information to reload in the same way that it was showing on restart.
4862 lines
148 KiB
C
4862 lines
148 KiB
C
/*
|
||
* ModSecurity for Apache 2.x, http://www.modsecurity.org/
|
||
* Copyright (c) 2004-2013 Trustwave Holdings, Inc. (http://www.trustwave.com/)
|
||
*
|
||
* You may not use this file except in compliance with
|
||
* the License. You may obtain a copy of the License at
|
||
*
|
||
* http://www.apache.org/licenses/LICENSE-2.0
|
||
*
|
||
* If any of the files related to licensing are missing or if you have any
|
||
* other questions related to licensing please contact Trustwave Holdings, Inc.
|
||
* directly using the email address security@modsecurity.org.
|
||
*/
|
||
|
||
#include "modsecurity.h"
|
||
#include "re.h"
|
||
#include "msc_pcre.h"
|
||
#include "msc_geo.h"
|
||
#include "msc_gsb.h"
|
||
#include "apr_lib.h"
|
||
#include "apr_strmatch.h"
|
||
#include "acmp.h"
|
||
#include "msc_util.h"
|
||
#include "msc_tree.h"
|
||
#include "msc_crypt.h"
|
||
#include "msc_remote_rules.h"
|
||
#include <apr_sha1.h>
|
||
|
||
#if APR_HAVE_ARPA_INET_H
|
||
#include <arpa/inet.h>
|
||
#endif
|
||
|
||
#ifdef WITH_SSDEEP
|
||
#include "fuzzy.h"
|
||
#endif
|
||
|
||
#include "libinjection/libinjection.h"
|
||
|
||
|
||
/**
|
||
*
|
||
*/
|
||
void msre_engine_op_register(msre_engine *engine, const char *name,
|
||
fn_op_param_init_t fn1, fn_op_execute_t fn2)
|
||
{
|
||
msre_op_metadata *metadata = (msre_op_metadata *)apr_pcalloc(engine->mp,
|
||
sizeof(msre_op_metadata));
|
||
if (metadata == NULL) return;
|
||
|
||
metadata->name = name;
|
||
metadata->param_init = fn1;
|
||
metadata->execute = fn2;
|
||
apr_table_setn(engine->operators, name, (void *)metadata);
|
||
}
|
||
|
||
/**
|
||
*
|
||
*/
|
||
msre_op_metadata *msre_engine_op_resolve(msre_engine *engine, const char *name) {
|
||
return (msre_op_metadata *)apr_table_get(engine->operators, name);
|
||
}
|
||
|
||
|
||
|
||
/* -- Operators -- */
|
||
|
||
/* unconditionalMatch */
|
||
|
||
static int msre_op_unconditionalmatch_execute(modsec_rec *msr, msre_rule *rule,
|
||
msre_var *var, char **error_msg)
|
||
{
|
||
*error_msg = "Unconditional match in SecAction.";
|
||
|
||
/* Always match. */
|
||
return 1;
|
||
}
|
||
|
||
/* noMatch */
|
||
|
||
static int msre_op_nomatch_execute(modsec_rec *msr, msre_rule *rule,
|
||
msre_var *var, char **error_msg)
|
||
{
|
||
*error_msg = "No match.";
|
||
|
||
/* Never match. */
|
||
return 0;
|
||
}
|
||
|
||
/* ipmatch */
|
||
|
||
/**
|
||
* \brief Init function to ipmatch operator
|
||
*
|
||
* \param rule ModSecurity rule struct
|
||
* \param error_msg Error message
|
||
*
|
||
* \retval 1 On Success
|
||
* \retval 0 On Fail
|
||
*/
|
||
static int msre_op_ipmatch_param_init(msre_rule *rule, char **error_msg) {
|
||
char *param = NULL;
|
||
int res = 0;
|
||
|
||
if (error_msg == NULL)
|
||
return -1;
|
||
else
|
||
*error_msg = NULL;
|
||
|
||
param = apr_pstrdup(rule->ruleset->mp, rule->op_param);
|
||
|
||
res = ip_tree_from_param(rule->ruleset->mp, param, &rule->ip_op,
|
||
error_msg);
|
||
|
||
if (res)
|
||
return 0;
|
||
|
||
return 1;
|
||
}
|
||
|
||
/**
|
||
* \brief Execution function to ipmatch operator
|
||
*
|
||
* \param msr Pointer internal modsec request structure
|
||
* \param rule Pointer to the rule
|
||
* \param var Pointer to variable structure
|
||
* \param error_msg Pointer to error msg
|
||
*
|
||
* \retval -1 On Failure
|
||
* \retval 1 On Match
|
||
* \retval 0 On No Match
|
||
*/
|
||
static int msre_op_ipmatch_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) {
|
||
TreeRoot *rtree = NULL;
|
||
int res = 0;
|
||
|
||
if (error_msg == NULL)
|
||
return -1;
|
||
else
|
||
*error_msg = NULL;
|
||
|
||
if (rule == NULL || rule->ip_op == NULL) {
|
||
msr_log(msr, 1, "ipMatch Internal Error: ipmatch value is null.");
|
||
return 0;
|
||
}
|
||
|
||
rtree = rule->ip_op;
|
||
|
||
res = tree_contains_ip(msr->mp, rtree, var->value, NULL, error_msg);
|
||
|
||
if (res < 0) {
|
||
msr_log(msr, 1, "%s", *error_msg);
|
||
*error_msg = NULL;
|
||
}
|
||
|
||
if (res > 0) {
|
||
*error_msg = apr_psprintf(msr->mp, "IPmatch: \"%s\" matched at %s.", var->value, var->name);
|
||
}
|
||
|
||
return res;
|
||
}
|
||
|
||
|
||
/**
|
||
* \brief Init function to ipmatchFromFile operator
|
||
*
|
||
* \param rule Pointer to the rule
|
||
* \param error_msg Pointer to error msg
|
||
*
|
||
* \retval 1 On Success
|
||
* \retval 0 On Fail
|
||
*/
|
||
static int msre_op_ipmatchFromFile_param_init(msre_rule *rule, char **error_msg) {
|
||
const char *rootpath = NULL;
|
||
const char *filepath = NULL;
|
||
const char *ipfile_path = NULL;
|
||
char *fn = NULL;
|
||
int res = 0;
|
||
TreeRoot *rtree = NULL;
|
||
|
||
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);
|
||
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);
|
||
return 0;
|
||
}
|
||
filepath = fn;
|
||
|
||
if (strlen(fn) > strlen("http://") &&
|
||
strncmp(fn, "http://", strlen("http://")) == 0)
|
||
{
|
||
*error_msg = apr_psprintf(rule->ruleset->mp, "HTTPS address or file " \
|
||
"path are expected for operator ipmatchFromFile \"%s\"", fn);
|
||
return 0;
|
||
}
|
||
else if (strlen(fn) > strlen("https://") &&
|
||
strncmp(fn, "https://", strlen("https://")) == 0)
|
||
{
|
||
#ifdef WITH_CURL
|
||
res = ip_tree_from_uri(&rtree, fn, rule->ruleset->mp, error_msg);
|
||
if (res == -2)
|
||
{
|
||
/* Failed to download but we won't stop the webserver to start. */
|
||
return 1;
|
||
}
|
||
else if (res)
|
||
{
|
||
return 0;
|
||
}
|
||
#else
|
||
*error_msg = apr_psprintf(rule->ruleset->mp, "ModSecurity was not " \
|
||
"compiled with Curl support, it cannot load: \"%s\"", fn);
|
||
return 0;
|
||
#endif
|
||
}
|
||
else
|
||
{
|
||
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);
|
||
}
|
||
|
||
res = ip_tree_from_file(&rtree, fn, rule->ruleset->mp, error_msg);
|
||
if (res)
|
||
{
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
rule->op_param_data = rtree;
|
||
return 1;
|
||
}
|
||
|
||
/**
|
||
* \brief Execution function to ipmatchFromFile operator
|
||
*
|
||
* \param msr Pointer internal modsec request structure
|
||
* \param rule Pointer to the rule
|
||
* \param var Pointer to variable structure
|
||
* \param error_msg Pointer to error msg
|
||
*
|
||
* \retval -1 On Failure
|
||
* \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) {
|
||
|
||
TreeRoot *rtree = (TreeRoot *)rule->op_param_data;
|
||
int res = 0;
|
||
|
||
if (error_msg == NULL)
|
||
return -1;
|
||
else
|
||
*error_msg = NULL;
|
||
|
||
if (rtree == NULL)
|
||
{
|
||
if (msr->txcfg->debuglog_level >= 6)
|
||
{
|
||
msr_log(msr, 1, "ipMatchFromFile: tree value is null.");
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
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,
|
||
rtree->ipv4_tree->count, rtree->ipv6_tree->count);
|
||
}
|
||
|
||
res = tree_contains_ip(msr->mp, rtree, var->value, msr,
|
||
error_msg);
|
||
|
||
if (res < 0)
|
||
msr_log(msr, 9, "%s", *error_msg);
|
||
|
||
if (res > 0)
|
||
*error_msg = apr_psprintf(msr->mp, "IPmatchFromFile: \"%s\" matched at " \
|
||
"%s.", var->value, var->name);
|
||
|
||
return res;
|
||
}
|
||
|
||
/* rsub */
|
||
|
||
static char *param_remove_escape(msre_rule *rule, char *str, int len) {
|
||
char *parm = apr_pcalloc(rule->ruleset->mp, len);
|
||
char *ret = parm;
|
||
|
||
for(;*str!='\0';str++) {
|
||
if(*str != '\\') {
|
||
*parm++ = *str;
|
||
} else {
|
||
str++;
|
||
if(*str != '/') {
|
||
str--;
|
||
*parm++ = *str;
|
||
} else {
|
||
*parm++ = *str;
|
||
}
|
||
}
|
||
}
|
||
|
||
*parm = '\0';
|
||
return ret;
|
||
}
|
||
|
||
/**
|
||
* \brief Init function to rsub operator
|
||
*
|
||
* \param rule Pointer to the rule
|
||
* \param error_msg Pointer to error msg
|
||
*
|
||
* \retval 1 On Success
|
||
* \retval 0 On Fail
|
||
*/
|
||
#if !defined(MSC_TEST)
|
||
static int msre_op_rsub_param_init(msre_rule *rule, char **error_msg) {
|
||
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 0
|
||
ap_regex_t *regex;
|
||
#else
|
||
regex_t *regex;
|
||
#endif
|
||
const char *pattern = NULL;
|
||
const char *line = NULL;
|
||
char *reg_pattern = NULL;
|
||
char *replace = NULL;
|
||
char *e_pattern = NULL;
|
||
char *parsed_replace = NULL;
|
||
char *flags = NULL;
|
||
char *data = NULL;
|
||
char delim;
|
||
int ignore_case = 0;
|
||
unsigned short int op_len = 0;
|
||
|
||
if (error_msg == NULL) return -1;
|
||
*error_msg = NULL;
|
||
|
||
line = rule->op_param;
|
||
|
||
if (apr_tolower(*line) != 's') {
|
||
*error_msg = apr_psprintf(rule->ruleset->mp, "Error rsub operator format, must be s/ pattern");
|
||
return -1;
|
||
}
|
||
|
||
data = apr_pstrdup(rule->ruleset->mp, line);
|
||
delim = *++data;
|
||
if (delim)
|
||
reg_pattern = ++data;
|
||
if (reg_pattern) {
|
||
|
||
if (*data != delim) {
|
||
for(;*data != '\0' ;data++) {
|
||
if(*data == delim) {
|
||
data--;
|
||
if(*data == '\\') {
|
||
data++;
|
||
continue;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (*data) {
|
||
*++data = '\0';
|
||
++data;
|
||
replace = data;
|
||
}
|
||
|
||
}
|
||
|
||
if (replace) {
|
||
|
||
if (*data != delim) {
|
||
for(;*data != '\0' ;data++) {
|
||
if(*data == delim) {
|
||
data--;
|
||
if(*data == '\\') {
|
||
data++;
|
||
continue;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (*data) {
|
||
*++data = '\0';
|
||
flags = ++data;
|
||
}
|
||
|
||
}
|
||
|
||
if (!delim || !reg_pattern || !replace) {
|
||
*error_msg = apr_psprintf(rule->ruleset->mp, "Error rsub operator format - must be s/regex/str/[flags]");
|
||
return -1;
|
||
}
|
||
|
||
op_len = strlen(replace);
|
||
parsed_replace = apr_pstrdup(rule->ruleset->mp, parse_pm_content(param_remove_escape(rule, replace, strlen(replace)),
|
||
op_len, rule, error_msg));
|
||
|
||
if(!parsed_replace) {
|
||
*error_msg = apr_psprintf(rule->ruleset->mp, "Error rsub operator parsing input data");
|
||
return -1;
|
||
}
|
||
|
||
rule->sub_str = apr_pstrmemdup(rule->ruleset->mp, parsed_replace, strlen(parsed_replace));
|
||
|
||
if (flags) {
|
||
while (*flags) {
|
||
delim = apr_tolower(*flags);
|
||
if (delim == 'i')
|
||
ignore_case = 1;
|
||
else if (delim == 'd')
|
||
rule->escape_re = 1;
|
||
else
|
||
*error_msg = apr_psprintf(rule->ruleset->mp, "Regex flag not supported");
|
||
flags++;
|
||
}
|
||
}
|
||
|
||
e_pattern = param_remove_escape(rule, reg_pattern, strlen(reg_pattern));
|
||
pattern = apr_pstrndup(rule->ruleset->mp, e_pattern, strlen(e_pattern));
|
||
|
||
if(strstr(pattern,"%{") == NULL) {
|
||
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 0
|
||
regex = ap_pregcomp(rule->ruleset->mp, pattern, AP_REG_EXTENDED |
|
||
(ignore_case ? AP_REG_ICASE : 0));
|
||
#else
|
||
regex = ap_pregcomp(rule->ruleset->mp, pattern, REG_EXTENDED |
|
||
(ignore_case ? REG_ICASE : 0));
|
||
#endif
|
||
rule->sub_regex = regex;
|
||
} else {
|
||
rule->re_precomp = 1;
|
||
rule->re_str = apr_pstrndup(rule->ruleset->mp, pattern, strlen(pattern));
|
||
rule->sub_regex = NULL;
|
||
}
|
||
|
||
return 1; /* OK */
|
||
}
|
||
|
||
/**
|
||
* \brief Execution function to rsub operator
|
||
*
|
||
* \param msr Pointer internal modsec request structure
|
||
* \param rule Pointer to the rule
|
||
* \param var Pointer to variable structure
|
||
* \param error_msg Pointer to error msg
|
||
*
|
||
* \retval -1 On Failure
|
||
* \retval 1 On Match
|
||
* \retval 0 On No Match
|
||
*/
|
||
static int msre_op_rsub_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));
|
||
msc_string *re_pattern = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
|
||
char *offset = NULL;
|
||
char *data = NULL, *pattern = NULL;
|
||
char *data_out = NULL;
|
||
unsigned int size = 0;
|
||
unsigned int maxsize=0;
|
||
int output_body = 0, input_body = 0, sl;
|
||
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 0
|
||
ap_regmatch_t pmatch[AP_MAX_REG_MATCH];
|
||
#else
|
||
regmatch_t pmatch[AP_MAX_REG_MATCH];
|
||
#endif
|
||
|
||
if (error_msg == NULL) return -1;
|
||
*error_msg = NULL;
|
||
|
||
if(strcmp(var->name,"STREAM_OUTPUT_BODY") == 0 ) {
|
||
output_body = 1;
|
||
} else if(strcmp(var->name,"STREAM_INPUT_BODY") == 0 ) {
|
||
input_body = 1;
|
||
} else {
|
||
msr_log(msr,9,"Operator rsub only works with STREAM_* variables");
|
||
return -1;
|
||
}
|
||
if(rule->re_precomp == 1) {
|
||
re_pattern->value = apr_pstrndup(msr->mp, rule->re_str, strlen(rule->re_str));
|
||
re_pattern->value_len = strlen(re_pattern->value);
|
||
|
||
expand_macros(msr, re_pattern, rule, msr->mp);
|
||
|
||
if(strlen(re_pattern->value) > 0) {
|
||
if(rule->escape_re == 1) {
|
||
pattern = log_escape_re(msr->mp, re_pattern->value);
|
||
if (msr->txcfg->debuglog_level >= 6) {
|
||
msr_log(msr, 6, "Escaping pattern [%s]",pattern);
|
||
}
|
||
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 0
|
||
rule->sub_regex = ap_pregcomp(msr->mp, pattern, AP_REG_EXTENDED);
|
||
#else
|
||
rule->sub_regex = ap_pregcomp(msr->mp, pattern, REG_EXTENDED);
|
||
#endif
|
||
} else {
|
||
#if AP_SERVER_MAJORVERSION_NUMBER > 1 && AP_SERVER_MINORVERSION_NUMBER > 0
|
||
rule->sub_regex = ap_pregcomp(msr->mp, re_pattern->value, AP_REG_EXTENDED);
|
||
#else
|
||
rule->sub_regex = ap_pregcomp(msr->mp, re_pattern->value, REG_EXTENDED);
|
||
#endif
|
||
}
|
||
} else {
|
||
rule->sub_regex = NULL;
|
||
}
|
||
|
||
}
|
||
|
||
if(rule->sub_regex == NULL) {
|
||
*error_msg = "Internal Error: regex data is null.";
|
||
return -1;
|
||
}
|
||
|
||
str->value = apr_pstrndup(msr->mp, rule->sub_str, strlen(rule->sub_str));
|
||
str->value_len = strlen(str->value);
|
||
|
||
if(strstr(rule->sub_str,"%{") != NULL)
|
||
expand_macros(msr, str, rule, msr->mp);
|
||
|
||
maxsize=var->value_len+(AP_MAX_REG_MATCH*1024)+1;
|
||
|
||
nextround:
|
||
|
||
data = apr_pcalloc(msr->mp, maxsize+1);
|
||
|
||
if(data == NULL) {
|
||
*error_msg = "Internal Error: cannot allocate memory";
|
||
return -1;
|
||
}
|
||
|
||
data_out=data;
|
||
size=0;
|
||
for (offset = (char*)var->value; !ap_regexec(rule->sub_regex, offset, AP_MAX_REG_MATCH, pmatch, 0); ) {
|
||
//Copy of data before the regex match
|
||
int i;
|
||
int s = pmatch [0].rm_so;
|
||
int p_len=pmatch [0].rm_eo - pmatch [0].rm_so;
|
||
if (size+s>maxsize) {
|
||
maxsize*=2;
|
||
goto nextround;
|
||
}
|
||
memcpy(data_out,offset,s);
|
||
data_out+=s;
|
||
size+=s;
|
||
|
||
//Copy of regex match with replacing data \1..\9
|
||
for(i=0;i<str->value_len;) {
|
||
char *x = str->value+i;
|
||
if (*x == '\\' && *(x + 1) > '0' && *(x + 1) <= '9') {
|
||
int capture=*(x + 1) - 48;
|
||
int capture_len=pmatch[capture].rm_eo-pmatch[capture].rm_so;
|
||
|
||
if (size+capture_len>maxsize)
|
||
{
|
||
maxsize*=2;
|
||
goto nextround;
|
||
}
|
||
memcpy(data_out,offset+pmatch[capture].rm_so,capture_len);
|
||
data_out+= capture_len;
|
||
size+=capture_len;
|
||
i+=2;
|
||
} else {
|
||
|
||
if (size+1>maxsize) {
|
||
maxsize*=2;
|
||
goto nextround;
|
||
}
|
||
*data_out=*(str->value+i);
|
||
data_out++;
|
||
size++;
|
||
i++;
|
||
}
|
||
}
|
||
offset+=s;
|
||
offset+=p_len;
|
||
}
|
||
|
||
//Copy of data after the last regex match
|
||
sl = strlen(offset);
|
||
if (size+sl>maxsize) {
|
||
maxsize*=2;
|
||
goto nextround;
|
||
}
|
||
memcpy(data_out,offset,sl);
|
||
data_out+=sl;
|
||
size+=sl;
|
||
*data_out=0;
|
||
|
||
if(msr->stream_output_data != NULL && output_body == 1) {
|
||
|
||
memset(msr->stream_output_data, 0x0, msr->stream_output_length);
|
||
free(msr->stream_output_data);
|
||
msr->stream_output_data = NULL;
|
||
msr->stream_output_length = 0;
|
||
|
||
msr->stream_output_data = (char *)malloc(size+1);
|
||
|
||
if(msr->stream_output_data == NULL) {
|
||
return -1;
|
||
}
|
||
|
||
msr->stream_output_length = size;
|
||
memset(msr->stream_output_data, 0x0, size+1);
|
||
|
||
msr->of_stream_changed = 1;
|
||
|
||
memcpy(msr->stream_output_data, data, size);
|
||
msr->stream_output_data[size] = '\0';
|
||
|
||
var->value_len = size;
|
||
var->value = msr->stream_output_data;
|
||
}
|
||
|
||
if(msr->stream_input_data != NULL && input_body == 1) {
|
||
memset(msr->stream_input_data, 0x0, msr->stream_input_length);
|
||
free(msr->stream_input_data);
|
||
msr->stream_input_data = NULL;
|
||
msr->stream_input_length = 0;
|
||
|
||
msr->stream_input_data = (char *)malloc(size+1);
|
||
|
||
if(msr->stream_input_data == NULL) {
|
||
return -1;
|
||
}
|
||
|
||
msr->stream_input_length = size;
|
||
memset(msr->stream_input_data, 0x0, size+1);
|
||
|
||
msr->if_stream_changed = 1;
|
||
|
||
memcpy(msr->stream_input_data, data, size);
|
||
msr->stream_input_data[size] = '\0';
|
||
|
||
var->value_len = size;
|
||
var->value = msr->stream_input_data;
|
||
}
|
||
|
||
if (! *error_msg) {
|
||
*error_msg = apr_psprintf(msr->mp, "Operator rsub succeeded.");
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
#endif /* MSC_TEST */
|
||
|
||
/**
|
||
* \brief Init function to validateHash
|
||
*
|
||
* \param rule ModSecurity rule struct
|
||
* \param error_msg Error message
|
||
*
|
||
* \retval 1 On success
|
||
* \retval 0 On fail
|
||
*/
|
||
static int msre_op_validateHash_param_init(msre_rule *rule, char **error_msg) {
|
||
const char *errptr = NULL;
|
||
int erroffset;
|
||
msc_regex_t *regex;
|
||
const char *pattern = rule->op_param;
|
||
#ifdef WITH_PCRE_STUDY
|
||
#ifdef WITH_PCRE_JIT
|
||
int rc, jit;
|
||
#endif
|
||
#endif
|
||
|
||
if (error_msg == NULL) return -1;
|
||
*error_msg = NULL;
|
||
|
||
/* Compile pattern */
|
||
if(strstr(pattern,"%{") == NULL) {
|
||
regex = msc_pregcomp_ex(rule->ruleset->mp, pattern, PCRE_DOTALL | PCRE_DOLLAR_ENDONLY, &errptr, &erroffset, msc_pcre_match_limit, msc_pcre_match_limit_recursion);
|
||
if (regex == NULL) {
|
||
*error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern (offset %d): %s",
|
||
erroffset, errptr);
|
||
return 0;
|
||
}
|
||
|
||
#ifdef WITH_PCRE_STUDY
|
||
#ifdef WITH_PCRE_JIT
|
||
rc = msc_fullinfo(regex, PCRE_INFO_JIT, &jit);
|
||
if ((rc != 0) || (jit != 1)) {
|
||
*error_msg = apr_psprintf(rule->ruleset->mp,
|
||
"Rule %pp [id \"%s\"][file \"%s\"][line \"%d\"] - "
|
||
"Execution error - "
|
||
"Does not support JIT (%d)",
|
||
rule,((rule->actionset != NULL)&&((rule->actionset->id != NULL)&&
|
||
(rule->actionset->id != NOT_SET_P))) ? rule->actionset->id : "-",
|
||
rule->filename != NULL ? rule->filename : "-",
|
||
rule->line_num,rc);
|
||
}
|
||
#endif
|
||
#endif
|
||
|
||
rule->op_param_data = regex;
|
||
} else {
|
||
rule->re_precomp = 1;
|
||
rule->re_str = apr_pstrndup(rule->ruleset->mp, pattern, strlen(pattern));
|
||
rule->op_param_data = NULL;
|
||
}
|
||
|
||
return 1; /* OK */
|
||
}
|
||
|
||
/**
|
||
* \brief Execute function to validateHash
|
||
*
|
||
* \param msr ModSecurity transaction resource
|
||
* \param rule ModSecurity rule struct
|
||
* \param var ModSecurity variable struct
|
||
* \param error_msg Error message
|
||
*
|
||
* \retval 1 On success
|
||
* \retval 0 On fail
|
||
*/
|
||
static int msre_op_validateHash_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) {
|
||
msc_regex_t *regex = (msc_regex_t *)rule->op_param_data;
|
||
msc_string *re_pattern = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
|
||
const char *target;
|
||
const char *errptr = NULL;
|
||
int erroffset;
|
||
unsigned int target_length;
|
||
char *my_error_msg = NULL;
|
||
int ovector[33];
|
||
int rc;
|
||
const char *pattern = NULL;
|
||
#ifdef WITH_PCRE_STUDY
|
||
#ifdef WITH_PCRE_JIT
|
||
int jit;
|
||
#endif
|
||
#endif
|
||
|
||
|
||
if (error_msg == NULL) return -1;
|
||
*error_msg = NULL;
|
||
|
||
if (msr->txcfg->hash_enforcement == HASH_DISABLED || msr->txcfg->hash_is_enabled == HASH_DISABLED)
|
||
return 0;
|
||
|
||
if (regex == NULL) {
|
||
if(rule->re_precomp == 0) {
|
||
*error_msg = "Internal Error: regex data is null.";
|
||
return -1;
|
||
} else {
|
||
|
||
if(re_pattern == NULL) {
|
||
*error_msg = "Internal Error: regex variable data is null.";
|
||
return -1;
|
||
}
|
||
|
||
re_pattern->value = apr_pstrndup(msr->mp, rule->re_str, strlen(rule->re_str));
|
||
re_pattern->value_len = strlen(re_pattern->value);
|
||
|
||
expand_macros(msr, re_pattern, rule, msr->mp);
|
||
|
||
pattern = log_escape_re(msr->mp, re_pattern->value);
|
||
if (msr->txcfg->debuglog_level >= 6) {
|
||
msr_log(msr, 6, "Escaping pattern [%s]",pattern);
|
||
}
|
||
|
||
regex = msc_pregcomp_ex(rule->ruleset->mp, pattern, PCRE_DOTALL | PCRE_DOLLAR_ENDONLY, &errptr,
|
||
&erroffset, msc_pcre_match_limit, msc_pcre_match_limit_recursion);
|
||
if (regex == NULL) {
|
||
*error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern (offset %d): %s",
|
||
erroffset, errptr);
|
||
return 0;
|
||
}
|
||
|
||
#ifdef WITH_PCRE_STUDY
|
||
#ifdef WITH_PCRE_JIT
|
||
if (msr->txcfg->debuglog_level >= 4) {
|
||
rc = msc_fullinfo(regex, PCRE_INFO_JIT, &jit);
|
||
if ((rc != 0) || (jit != 1)) {
|
||
*error_msg = apr_psprintf(rule->ruleset->mp,
|
||
"Rule %pp [id \"%s\"][file \"%s\"][line \"%d\"] - "
|
||
"Execution error - "
|
||
"Does not support JIT (%d)",
|
||
rule,((rule->actionset != NULL)&&((rule->actionset->id != NULL)&&
|
||
(rule->actionset->id != NOT_SET_P))) ? rule->actionset->id : "-",
|
||
rule->filename != NULL ? rule->filename : "-",
|
||
rule->line_num,rc);
|
||
msr_log(msr, 4, "%s.", *error_msg);
|
||
}
|
||
}
|
||
#endif
|
||
#endif
|
||
}
|
||
}
|
||
|
||
/* 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;
|
||
}
|
||
|
||
/* We always use capture so that ovector can be used as working space
|
||
* and no memory has to be allocated for any backreferences.
|
||
*/
|
||
rc = msc_regexec_capture(regex, target, target_length, ovector, 30, &my_error_msg);
|
||
if ((rc == PCRE_ERROR_MATCHLIMIT) || (rc == PCRE_ERROR_RECURSIONLIMIT)) {
|
||
msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
|
||
|
||
if (s == NULL) return -1;
|
||
s->name = apr_pstrdup(msr->mp, "MSC_PCRE_LIMITS_EXCEEDED");
|
||
if (s->name == NULL) return -1;
|
||
s->name_len = strlen(s->name);
|
||
s->value = apr_pstrdup(msr->mp, "1");
|
||
if (s->value == NULL) return -1;
|
||
s->value_len = 1;
|
||
apr_table_setn(msr->tx_vars, s->name, (void *)s);
|
||
|
||
*error_msg = apr_psprintf(msr->mp,
|
||
"Rule %pp [id \"%s\"][file \"%s\"][line \"%d\"] - "
|
||
"Execution error - "
|
||
"PCRE limits exceeded (%d): %s",
|
||
rule,((rule->actionset != NULL)&&(rule->actionset->id != NULL)) ? rule->actionset->id : "-",
|
||
rule->filename != NULL ? rule->filename : "-",
|
||
rule->line_num,rc, my_error_msg);
|
||
|
||
msr_log(msr, 3, "%s.", *error_msg);
|
||
|
||
return 0; /* No match. */
|
||
}
|
||
else if (rc < -1) {
|
||
*error_msg = apr_psprintf(msr->mp, "Regex execution failed (%d): %s",
|
||
rc, my_error_msg);
|
||
return -1;
|
||
}
|
||
|
||
if (rc != PCRE_ERROR_NOMATCH) { /* Match. */
|
||
/* We no longer escape the pattern here as it is done when logging */
|
||
char *pattern = apr_pstrdup(msr->mp, log_escape(msr->mp, regex->pattern ? regex->pattern : "<Unknown Match>"));
|
||
char *hmac = NULL, *valid = NULL;
|
||
char *hash_link = NULL, *nlink = NULL;
|
||
|
||
if (strlen(pattern) > 252) {
|
||
*error_msg = apr_psprintf(msr->mp, "Request URI matched \"%.252s ...\" at %s.",
|
||
pattern, var->name);
|
||
} else {
|
||
*error_msg = apr_psprintf(msr->mp, "Request URI matched \"%s\" at %s.",
|
||
pattern, var->name);
|
||
}
|
||
|
||
valid = strstr(target, msr->txcfg->crypto_param_name);
|
||
|
||
if(valid == NULL) {
|
||
|
||
if (msr->txcfg->debuglog_level >= 9)
|
||
msr_log(msr, 9, "Request URI without hash parameter [%s]", target);
|
||
|
||
if (strlen(pattern) > 252) {
|
||
*error_msg = apr_psprintf(msr->mp, "Request URI matched \"%.252s ...\" at %s. No Hash parameter",
|
||
pattern, var->name);
|
||
} else {
|
||
*error_msg = apr_psprintf(msr->mp, "Request URI matched \"%s\" at %s. No Hash parameter",
|
||
pattern, var->name);
|
||
}
|
||
return 1;
|
||
} else {
|
||
|
||
if(strlen(valid) < strlen(msr->txcfg->crypto_param_name)+1)
|
||
return 1;
|
||
|
||
hmac = valid+strlen(msr->txcfg->crypto_param_name)+1;
|
||
|
||
nlink = apr_pstrmemdup(msr->mp, target, strlen(target) - strlen(valid) - 1);
|
||
|
||
msr_log(msr, 9, "Validating URI %s size %zu",nlink,strlen(nlink));
|
||
|
||
hash_link = do_hash_link(msr, (char *)nlink, HASH_ONLY);
|
||
|
||
if(strcmp(hmac, hash_link) != 0) {
|
||
|
||
if (strlen(pattern) > 252) {
|
||
*error_msg = apr_psprintf(msr->mp, "Request URI matched \"%.252s ...\" at %s. Hash parameter hash value = [%s] Requested URI hash value = [%s]",
|
||
pattern, var->name, hmac, hash_link);
|
||
} else {
|
||
*error_msg = apr_psprintf(msr->mp, "Request URI matched \"%s\" at %s. Hash parameter hash value = [%s] Requested URI hash value = [%s]",
|
||
pattern, var->name, hmac, hash_link);
|
||
}
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* rx */
|
||
|
||
static int msre_op_rx_param_init(msre_rule *rule, char **error_msg) {
|
||
const char *errptr = NULL;
|
||
int erroffset;
|
||
msc_regex_t *regex;
|
||
const char *pattern = rule->op_param;
|
||
#ifdef WITH_PCRE_STUDY
|
||
#ifdef WITH_PCRE_JIT
|
||
int rc, jit;
|
||
#endif
|
||
#endif
|
||
|
||
if (error_msg == NULL) return -1;
|
||
*error_msg = NULL;
|
||
|
||
/* Compile pattern */
|
||
if(strstr(pattern,"%{") == NULL) {
|
||
regex = msc_pregcomp_ex(rule->ruleset->mp, pattern, PCRE_DOTALL | PCRE_DOLLAR_ENDONLY, &errptr, &erroffset, msc_pcre_match_limit, msc_pcre_match_limit_recursion);
|
||
if (regex == NULL) {
|
||
*error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern (offset %d): %s",
|
||
erroffset, errptr);
|
||
return 0;
|
||
}
|
||
|
||
#ifdef WITH_PCRE_STUDY
|
||
#ifdef WITH_PCRE_JIT
|
||
rc = msc_fullinfo(regex, PCRE_INFO_JIT, &jit);
|
||
if ((rc != 0) || (jit != 1)) {
|
||
*error_msg = apr_psprintf(rule->ruleset->mp,
|
||
"Rule %pp [id \"%s\"][file \"%s\"][line \"%d\"] - "
|
||
"Execution error - "
|
||
"Does not support JIT (%d)",
|
||
rule,((rule->actionset != NULL)&&((rule->actionset->id != NULL)&&
|
||
(rule->actionset->id != NOT_SET_P))) ? rule->actionset->id : "-",
|
||
rule->filename != NULL ? rule->filename : "-",
|
||
rule->line_num,rc);
|
||
}
|
||
#endif
|
||
#endif
|
||
|
||
rule->op_param_data = regex;
|
||
} else {
|
||
rule->re_precomp = 1;
|
||
rule->re_str = apr_pstrndup(rule->ruleset->mp, pattern, strlen(pattern));
|
||
rule->op_param_data = NULL;
|
||
}
|
||
|
||
return 1; /* OK */
|
||
}
|
||
|
||
static int msre_op_rx_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) {
|
||
msc_regex_t *regex = (msc_regex_t *)rule->op_param_data;
|
||
msc_string *re_pattern = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
|
||
const char *target;
|
||
const char *errptr = NULL;
|
||
int erroffset;
|
||
unsigned int target_length;
|
||
char *my_error_msg = NULL;
|
||
int ovector[33];
|
||
int capture = 0;
|
||
int matched_bytes = 0;
|
||
int matched = 0;
|
||
int rc;
|
||
char *qspos = NULL;
|
||
const char *parm = NULL, *pattern = NULL;
|
||
msc_parm *mparm = NULL;
|
||
#ifdef WITH_PCRE_STUDY
|
||
#ifdef WITH_PCRE_JIT
|
||
int jit;
|
||
#endif
|
||
#endif
|
||
|
||
|
||
if (error_msg == NULL) return -1;
|
||
*error_msg = NULL;
|
||
|
||
if (regex == NULL) {
|
||
if(rule->re_precomp == 0) {
|
||
*error_msg = "Internal Error: regex data is null.";
|
||
return -1;
|
||
} else {
|
||
|
||
if(re_pattern == NULL) {
|
||
*error_msg = "Internal Error: regex variable data is null.";
|
||
return -1;
|
||
}
|
||
|
||
re_pattern->value = apr_pstrndup(msr->mp, rule->re_str, strlen(rule->re_str));
|
||
re_pattern->value_len = strlen(re_pattern->value);
|
||
|
||
expand_macros(msr, re_pattern, rule, msr->mp);
|
||
|
||
pattern = log_escape_re(msr->mp, re_pattern->value);
|
||
if (msr->txcfg->debuglog_level >= 6) {
|
||
msr_log(msr, 6, "Escaping pattern [%s]",pattern);
|
||
}
|
||
|
||
regex = msc_pregcomp_ex(rule->ruleset->mp, pattern, PCRE_DOTALL | PCRE_DOLLAR_ENDONLY, &errptr, &erroffset, msc_pcre_match_limit, msc_pcre_match_limit_recursion);
|
||
if (regex == NULL) {
|
||
*error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern (offset %d): %s",
|
||
erroffset, errptr);
|
||
return 0;
|
||
}
|
||
|
||
#ifdef WITH_PCRE_STUDY
|
||
#ifdef WITH_PCRE_JIT
|
||
if (msr->txcfg->debuglog_level >= 4) {
|
||
rc = msc_fullinfo(regex, PCRE_INFO_JIT, &jit);
|
||
if ((rc != 0) || (jit != 1)) {
|
||
*error_msg = apr_psprintf(rule->ruleset->mp,
|
||
"Rule %pp [id \"%s\"][file \"%s\"][line \"%d\"] - "
|
||
"Execution error - "
|
||
"Does not support JIT (%d)",
|
||
rule,((rule->actionset != NULL)&&((rule->actionset->id != NULL)&&
|
||
(rule->actionset->id != NOT_SET_P))) ? rule->actionset->id : "-",
|
||
rule->filename != NULL ? rule->filename : "-",
|
||
rule->line_num,rc);
|
||
msr_log(msr, 4, "%s.", *error_msg);
|
||
}
|
||
}
|
||
#endif
|
||
#endif
|
||
|
||
|
||
}
|
||
}
|
||
|
||
/* 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;
|
||
}
|
||
|
||
/* Are we supposed to capture subexpressions? */
|
||
capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0;
|
||
matched_bytes = apr_table_get(rule->actionset->actions, "sanitizeMatchedBytes") ? 1 : 0;
|
||
if(!matched_bytes)
|
||
matched_bytes = apr_table_get(rule->actionset->actions, "sanitiseMatchedBytes") ? 1 : 0;
|
||
|
||
matched = apr_table_get(rule->actionset->actions, "sanitizeMatched") ? 1 : 0;
|
||
if(!matched)
|
||
matched = apr_table_get(rule->actionset->actions, "sanitiseMatched") ? 1 : 0;
|
||
|
||
/* Show when the regex captures but "capture" is not set */
|
||
if (msr->txcfg->debuglog_level >= 6) {
|
||
int capcount = 0;
|
||
rc = msc_fullinfo(regex, PCRE_INFO_CAPTURECOUNT, &capcount);
|
||
if (msr->txcfg->debuglog_level >= 6) {
|
||
if ((capture == 0) && (capcount > 0)) {
|
||
msr_log(msr, 6, "Ignoring regex captures since \"capture\" action is not enabled.");
|
||
}
|
||
}
|
||
}
|
||
|
||
/* We always use capture so that ovector can be used as working space
|
||
* and no memory has to be allocated for any backreferences.
|
||
*/
|
||
rc = msc_regexec_capture(regex, target, target_length, ovector, 30, &my_error_msg);
|
||
if ((rc == PCRE_ERROR_MATCHLIMIT) || (rc == PCRE_ERROR_RECURSIONLIMIT)) {
|
||
msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
|
||
|
||
if (s == NULL) return -1;
|
||
s->name = apr_pstrdup(msr->mp, "MSC_PCRE_LIMITS_EXCEEDED");
|
||
if (s->name == NULL) return -1;
|
||
s->name_len = strlen(s->name);
|
||
s->value = apr_pstrdup(msr->mp, "1");
|
||
if (s->value == NULL) return -1;
|
||
s->value_len = 1;
|
||
apr_table_setn(msr->tx_vars, s->name, (void *)s);
|
||
|
||
*error_msg = apr_psprintf(msr->mp,
|
||
"Rule %pp [id \"%s\"][file \"%s\"][line \"%d\"] - "
|
||
"Execution error - "
|
||
"PCRE limits exceeded (%d): %s",
|
||
rule,((rule->actionset != NULL)&&(rule->actionset->id != NULL)) ? rule->actionset->id : "-",
|
||
rule->filename != NULL ? rule->filename : "-",
|
||
rule->line_num,rc, my_error_msg);
|
||
|
||
msr_log(msr, 3, "%s.", *error_msg);
|
||
|
||
return 0; /* No match. */
|
||
}
|
||
else if (rc < -1) {
|
||
*error_msg = apr_psprintf(msr->mp, "Regex execution failed (%d): %s",
|
||
rc, my_error_msg);
|
||
return -1;
|
||
}
|
||
|
||
/* Handle captured subexpressions. */
|
||
if (capture && rc > 0) {
|
||
int i;
|
||
|
||
/* Unset any of the previously set capture variables. */
|
||
apr_table_unset(msr->tx_vars, "0");
|
||
apr_table_unset(msr->tx_vars, "1");
|
||
apr_table_unset(msr->tx_vars, "2");
|
||
apr_table_unset(msr->tx_vars, "3");
|
||
apr_table_unset(msr->tx_vars, "4");
|
||
apr_table_unset(msr->tx_vars, "5");
|
||
apr_table_unset(msr->tx_vars, "6");
|
||
apr_table_unset(msr->tx_vars, "7");
|
||
apr_table_unset(msr->tx_vars, "8");
|
||
apr_table_unset(msr->tx_vars, "9");
|
||
|
||
/* Use the available captures. */
|
||
for(i = 0; i < rc; i++) {
|
||
msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
|
||
if (s == NULL) return -1;
|
||
s->name = apr_psprintf(msr->mp, "%d", i);
|
||
if (s->name == NULL) return -1;
|
||
s->name_len = strlen(s->name);
|
||
s->value = apr_pstrmemdup(msr->mp,
|
||
target + ovector[2 * i], ovector[2 * i + 1] - ovector[2 * i]);
|
||
if (s->value == NULL) return -1;
|
||
|
||
s->value_len = (ovector[2 * i + 1] - ovector[2 * i]);
|
||
|
||
apr_table_addn(msr->tx_vars, s->name, (void *)s);
|
||
|
||
if(((matched == 1) || (matched_bytes == 1)) && (var != NULL) && (var->name != NULL)) {
|
||
qspos = apr_psprintf(msr->mp, "%s", var->name);
|
||
parm = strstr(qspos, ":");
|
||
if (parm != NULL) {
|
||
parm++;
|
||
mparm = apr_palloc(msr->mp, sizeof(msc_parm));
|
||
if (mparm == NULL)
|
||
continue;
|
||
|
||
mparm->value = apr_pstrmemdup(msr->mp,s->value,s->value_len);
|
||
mparm->pad_1 = rule->actionset->arg_min;
|
||
mparm->pad_2 = rule->actionset->arg_max;
|
||
apr_table_addn(msr->pattern_to_sanitize, parm, (void *)mparm);
|
||
} else {
|
||
mparm = apr_palloc(msr->mp, sizeof(msc_parm));
|
||
if (mparm == NULL)
|
||
continue;
|
||
|
||
mparm->value = apr_pstrmemdup(msr->mp,s->value,s->value_len);
|
||
apr_table_addn(msr->pattern_to_sanitize, qspos, (void *)mparm);
|
||
}
|
||
}
|
||
|
||
if (msr->txcfg->debuglog_level >= 9) {
|
||
msr_log(msr, 9, "Added regex subexpression to TX.%d: %s", i,
|
||
log_escape_nq_ex(msr->mp, s->value, s->value_len));
|
||
}
|
||
}
|
||
}
|
||
|
||
if (rc != PCRE_ERROR_NOMATCH) { /* Match. */
|
||
/* We no longer escape the pattern here as it is done when logging */
|
||
char *pattern = apr_pstrdup(msr->mp, log_escape(msr->mp, regex->pattern ? regex->pattern : "<Unknown Match>"));
|
||
|
||
/* This message will be logged. */
|
||
if (strlen(pattern) > 252) {
|
||
*error_msg = apr_psprintf(msr->mp, "Pattern match \"%.252s ...\" at %s.",
|
||
pattern, var->name);
|
||
} else {
|
||
*error_msg = apr_psprintf(msr->mp, "Pattern match \"%s\" at %s.",
|
||
pattern, var->name);
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
/* No match. */
|
||
return 0;
|
||
}
|
||
|
||
/* pm */
|
||
|
||
static int msre_op_pm_param_init(msre_rule *rule, char **error_msg) {
|
||
ACMP *p;
|
||
const char *phrase;
|
||
const char *next;
|
||
unsigned short int op_len;
|
||
|
||
if ((rule->op_param == NULL)||(strlen(rule->op_param) == 0)) {
|
||
*error_msg = apr_psprintf(rule->ruleset->mp, "Missing parameter for operator 'pm'.");
|
||
return 0; /* ERROR */
|
||
}
|
||
|
||
op_len = strlen(rule->op_param);
|
||
|
||
p = acmp_create(0, rule->ruleset->mp);
|
||
if (p == NULL) return 0;
|
||
|
||
phrase = apr_pstrdup(rule->ruleset->mp, parse_pm_content(rule->op_param, op_len, rule, error_msg));
|
||
|
||
if(phrase == NULL)
|
||
phrase = apr_pstrdup(rule->ruleset->mp, rule->op_param);
|
||
|
||
/* Loop through phrases */
|
||
/* ENH: Need to allow quoted phrases w/space */
|
||
for (;;) {
|
||
while((apr_isspace(*phrase) != 0) && (*phrase != '\0')) phrase++;
|
||
if (*phrase == '\0') break;
|
||
next = phrase;
|
||
while((apr_isspace(*next) == 0) && (*next != 0)) next++;
|
||
acmp_add_pattern(p, phrase, NULL, NULL, next - phrase);
|
||
phrase = next;
|
||
}
|
||
acmp_prepare(p);
|
||
rule->op_param_data = p;
|
||
return 1;
|
||
}
|
||
|
||
/* pmFromFile */
|
||
|
||
static int msre_op_pmFromFile_param_init(msre_rule *rule, char **error_msg) {
|
||
char errstr[1024];
|
||
char buf[HUGE_STRING_LEN + 1];
|
||
char *fn = NULL;
|
||
char *next = NULL;
|
||
char *start = NULL;
|
||
char *end = NULL;
|
||
const char *rulefile_path;
|
||
char *processed = NULL;
|
||
unsigned short int op_len;
|
||
apr_status_t rc;
|
||
apr_file_t *fd = NULL;
|
||
ACMP *p;
|
||
|
||
if ((rule->op_param == NULL)||(strlen(rule->op_param) == 0)) {
|
||
*error_msg = apr_psprintf(rule->ruleset->mp, "Missing parameter for operator 'pmFromFile'.");
|
||
return 0; /* ERROR */
|
||
}
|
||
|
||
p = acmp_create(0, rule->ruleset->mp);
|
||
if (p == NULL) return 0;
|
||
|
||
fn = apr_pstrdup(rule->ruleset->mp, rule->op_param);
|
||
|
||
/* Get the path of the rule filename to use as a base */
|
||
rulefile_path = apr_pstrndup(rule->ruleset->mp, rule->filename, strlen(rule->filename) - strlen(apr_filepath_name_get(rule->filename)));
|
||
|
||
#ifdef DEBUG_CONF
|
||
fprintf(stderr, "Rulefile path: \"%s\"\n", rulefile_path);
|
||
#endif
|
||
|
||
/* Loop through filenames */
|
||
/* ENH: Need to allow quoted filenames w/space */
|
||
for (;;) {
|
||
const char *rootpath = NULL;
|
||
const char *filepath = NULL;
|
||
int line = 0;
|
||
|
||
/* Trim whitespace */
|
||
while((apr_isspace(*fn) != 0) && (*fn != '\0')) fn++;
|
||
if (*fn == '\0') break;
|
||
next = fn;
|
||
while((apr_isspace(*next) == 0) && (*next != '\0')) next++;
|
||
while((apr_isspace(*next) != 0) && (*next != '\0')) *(next++) = '\0';
|
||
|
||
/* Add path of the rule filename for a relative phrase filename */
|
||
filepath = fn;
|
||
|
||
if (strlen(fn) > strlen("http://") &&
|
||
strncmp(fn, "http://", strlen("http://")) == 0)
|
||
{
|
||
*error_msg = apr_psprintf(rule->ruleset->mp, "HTTPS address or " \
|
||
"file path are expected for operator pmFromFile \"%s\"", fn);
|
||
return 0;
|
||
}
|
||
else if (strlen(fn) > strlen("https://") &&
|
||
strncmp(fn, "https://", strlen("https://")) == 0)
|
||
{
|
||
#ifdef WITH_CURL
|
||
int res = 0;
|
||
char *word = NULL;
|
||
char *brkt = NULL;
|
||
char *sep = "\n";
|
||
struct msc_curl_memory_buffer_t chunk;
|
||
|
||
res = msc_remote_download_content(rule->ruleset->mp, fn, NULL,
|
||
&chunk, error_msg);
|
||
if (res == -2)
|
||
{
|
||
/* If download failed but SecRemoteRulesFailAction is set to Warn. */
|
||
return 1;
|
||
}
|
||
else if (res < 0)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
for (word = strtok_r(chunk.memory, sep, &brkt);
|
||
word;
|
||
word = strtok_r(NULL, sep, &brkt))
|
||
{
|
||
/* Ignore empty lines and comments */
|
||
if (*word == '#') continue;
|
||
|
||
acmp_add_pattern(p, word, NULL, NULL, strlen(word));
|
||
}
|
||
msc_remote_clean_chunk(&chunk);
|
||
#else
|
||
*error_msg = apr_psprintf(rule->ruleset->mp, "ModSecurity was not " \
|
||
"compiled with Curl support, it cannot load: \"%s\"", fn);
|
||
return 0;
|
||
#endif
|
||
}
|
||
else
|
||
{
|
||
if (apr_filepath_root(&rootpath, &filepath, APR_FILEPATH_TRUENAME, rule->ruleset->mp) != APR_SUCCESS) {
|
||
/* We are not an absolute path. It could mean an error, but
|
||
* let that pass through to the open call for a better error */
|
||
apr_filepath_merge(&fn, rulefile_path, fn, APR_FILEPATH_TRUENAME, rule->ruleset->mp);
|
||
}
|
||
|
||
/* Open file and read */
|
||
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 phrase file \"%s\": %s", fn, apr_strerror(rc, errstr, 1024));
|
||
return 0;
|
||
}
|
||
|
||
#ifdef DEBUG_CONF
|
||
fprintf(stderr, "Loading phrase file: \"%s\"\n", fn);
|
||
#endif
|
||
|
||
/* Read one pattern per line skipping empty/commented */
|
||
for(;;) {
|
||
line++;
|
||
rc = apr_file_gets(buf, HUGE_STRING_LEN, fd);
|
||
if (rc == APR_EOF) break;
|
||
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);
|
||
processed = apr_pstrdup(rule->ruleset->mp, parse_pm_content(buf, op_len, rule, error_msg));
|
||
|
||
/* Trim Whitespace */
|
||
if(processed != NULL)
|
||
start = processed;
|
||
else
|
||
start = buf;
|
||
|
||
while ((apr_isspace(*start) != 0) && (*start != '\0')) start++;
|
||
if(processed != NULL)
|
||
end = processed + strlen(processed);
|
||
else
|
||
end = buf + strlen(buf);
|
||
if (end > start) end--;
|
||
while ((end > start) && (apr_isspace(*end) != 0)) end--;
|
||
if (end > start) {
|
||
*(++end) = '\0';
|
||
}
|
||
|
||
/* Ignore empty lines and comments */
|
||
if ((start == end) || (*start == '#')) continue;
|
||
|
||
acmp_add_pattern(p, start, NULL, NULL, (end - start));
|
||
}
|
||
}
|
||
|
||
fn = next;
|
||
|
||
if (fd != NULL) apr_file_close(fd);
|
||
}
|
||
|
||
acmp_prepare(p);
|
||
rule->op_param_data = p;
|
||
return 1;
|
||
}
|
||
|
||
static int msre_op_pm_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) {
|
||
const char *match = NULL;
|
||
apr_status_t rc = 0;
|
||
int capture;
|
||
ACMPT pt;
|
||
|
||
/* Nothing to read */
|
||
if ((var->value == NULL) || (var->value_len == 0)) return 0;
|
||
|
||
/* Are we supposed to capture subexpressions? */
|
||
capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0;
|
||
|
||
if (rule->op_param_data == NULL)
|
||
{
|
||
if (msr->txcfg->debuglog_level >= 6)
|
||
{
|
||
msr_log(msr, 1, "ACMPTree is null.");
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
pt.parser = (ACMP *)rule->op_param_data;
|
||
pt.ptr = NULL;
|
||
|
||
rc = acmp_process_quick(&pt, &match, var->value, var->value_len);
|
||
if (rc) {
|
||
char *match_escaped = log_escape(msr->mp, match ? match : "<Unknown Match>");
|
||
|
||
/* This message will be logged. */
|
||
if (strlen(match_escaped) > 252) {
|
||
*error_msg = apr_psprintf(msr->mp, "Matched phrase \"%.252s ...\" at %s.",
|
||
match_escaped, var->name);
|
||
} else {
|
||
*error_msg = apr_psprintf(msr->mp, "Matched phrase \"%s\" at %s.",
|
||
match_escaped, var->name);
|
||
}
|
||
|
||
/* Handle capture as tx.0=match */
|
||
if (capture) {
|
||
int i;
|
||
msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
|
||
|
||
if (s == NULL) return -1;
|
||
|
||
s->name = "0";
|
||
s->name_len = strlen(s->name);
|
||
s->value = apr_pstrdup(msr->mp, match);
|
||
if (s->value == NULL) return -1;
|
||
s->value_len = strlen(s->value);
|
||
apr_table_setn(msr->tx_vars, s->name, (void *)s);
|
||
|
||
if (msr->txcfg->debuglog_level >= 9) {
|
||
msr_log(msr, 9, "Added phrase match to TX.0: %s",
|
||
log_escape_nq_ex(msr->mp, s->value, s->value_len));
|
||
}
|
||
|
||
/* Unset the remaining ones (from previous invocations). */
|
||
for(i = rc; i <= 9; i++) {
|
||
char buf[2];
|
||
apr_snprintf(buf, sizeof(buf), "%d", i);
|
||
apr_table_unset(msr->tx_vars, buf);
|
||
}
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
return rc;
|
||
}
|
||
|
||
/* gsbLookup */
|
||
|
||
/**
|
||
* \brief Reduce /./ to /
|
||
*
|
||
* \param pool Pointer to the memory pool
|
||
* \param domain Input data
|
||
*
|
||
* \retval domain On Failure
|
||
* \retval url On Success
|
||
*/
|
||
static const char *gsb_replace_tpath(apr_pool_t *pool, const char *domain, int len) {
|
||
|
||
char *pos = NULL, *data = NULL;
|
||
char *url = NULL;
|
||
int match = 0;
|
||
|
||
url = apr_palloc(pool, len + 1);
|
||
data = apr_palloc(pool, len + 1);
|
||
|
||
memset(data, 0, len+1);
|
||
memset(url, 0, len+1);
|
||
|
||
memcpy(url, domain, len);
|
||
|
||
while(( pos = strstr(url , "/./" )) != NULL) {
|
||
match = 1;
|
||
data[0] = '\0';
|
||
strncat(data, url, pos - url);
|
||
strcat(data , "/");
|
||
strcat(data ,pos + strlen("/./"));
|
||
strncpy(url , data, len);
|
||
}
|
||
|
||
if(match == 0)
|
||
return domain;
|
||
|
||
return url;
|
||
}
|
||
|
||
/**
|
||
* \brief Reduce double dot to single dot
|
||
*
|
||
* \param msr ModSecurity transation resource
|
||
* \param domain Input data
|
||
*
|
||
* \retval domain On Failure
|
||
* \retval reduced On Success
|
||
*/
|
||
static const char *gsb_reduce_char(apr_pool_t *pool, const char *domain) {
|
||
|
||
char *ptr = apr_pstrdup(pool, domain);
|
||
char *data = NULL;
|
||
char *reduced = NULL;
|
||
int skip = 0;
|
||
|
||
|
||
if(ptr == NULL)
|
||
return domain;
|
||
|
||
data = apr_pcalloc(pool, strlen(ptr));
|
||
|
||
if(data == NULL)
|
||
return domain;
|
||
|
||
reduced = data;
|
||
|
||
while(*ptr != '\0') {
|
||
|
||
switch(*ptr) {
|
||
case '.':
|
||
ptr++;
|
||
if(*ptr == '.')
|
||
skip = 1;
|
||
|
||
ptr--;
|
||
break;
|
||
case '/':
|
||
ptr++;
|
||
if(*ptr == '/')
|
||
skip = 1;
|
||
|
||
ptr--;
|
||
break;
|
||
}
|
||
|
||
if(skip == 0) {
|
||
*data = *ptr;
|
||
data++;
|
||
}
|
||
ptr++;
|
||
skip = 0;
|
||
}
|
||
|
||
*data = '\0'; --data;
|
||
|
||
if(*data == '.')
|
||
*data = '\0';
|
||
else
|
||
++data;
|
||
|
||
return reduced;
|
||
}
|
||
|
||
|
||
/**
|
||
* \brief Verify function to gsbLookup operator
|
||
*
|
||
* \param msr ModSecurity transaction resource
|
||
* \param match Pointer to input data
|
||
* \param match_length Input size
|
||
*
|
||
* \retval -1 On Failure
|
||
* \retval 1 On Match
|
||
* \retval 0 On No Match
|
||
*/
|
||
static int verify_gsb(gsb_db *gsb, modsec_rec *msr, const char *match, unsigned int match_length) {
|
||
apr_md5_ctx_t ctx;
|
||
apr_status_t rc;
|
||
unsigned char digest[APR_MD5_DIGESTSIZE];
|
||
const char *hash = NULL;
|
||
const char *search = NULL;
|
||
|
||
memset(digest, 0, sizeof(digest));
|
||
|
||
apr_md5_init(&ctx);
|
||
|
||
if ((rc = apr_md5_update(&ctx, match, match_length)) != APR_SUCCESS)
|
||
return -1;
|
||
|
||
apr_md5_final(digest, &ctx);
|
||
|
||
hash = apr_psprintf(msr->mp, "%s", bytes2hex(msr->mp, digest, 16));
|
||
|
||
if ((hash != NULL) && (gsb->gsb_table != NULL)) {
|
||
search = apr_hash_get(gsb->gsb_table, hash, APR_HASH_KEY_STRING);
|
||
|
||
if (search != NULL)
|
||
return 1;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* \brief Init function to gsbLookup operator
|
||
*
|
||
* \param rule ModSecurity rule struct
|
||
* \param error_msg Error message
|
||
*
|
||
* \retval 1 On Success
|
||
* \retval 0 On Fail
|
||
*/
|
||
static int msre_op_gsbLookup_param_init(msre_rule *rule, char **error_msg) {
|
||
const char *errptr = NULL;
|
||
int erroffset;
|
||
msc_regex_t *regex;
|
||
|
||
if (error_msg == NULL) return -1;
|
||
*error_msg = NULL;
|
||
|
||
/* Compile rule->op_param */
|
||
regex = msc_pregcomp_ex(rule->ruleset->mp, rule->op_param, PCRE_DOTALL | PCRE_MULTILINE, &errptr, &erroffset, msc_pcre_match_limit, msc_pcre_match_limit_recursion);
|
||
|
||
if (regex == NULL) {
|
||
*error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern (offset %d): %s",
|
||
erroffset, errptr);
|
||
return 0;
|
||
}
|
||
|
||
rule->op_param_data = regex;
|
||
|
||
return 1; /* OK */
|
||
}
|
||
|
||
/**
|
||
* \brief Execution function to gsbLookup operator
|
||
*
|
||
* \param msr ModSecurity transaction resource
|
||
* \param rule ModSecurity rule struct
|
||
* \param var ModSecurity variable struct
|
||
* \param error_msg Error message
|
||
*
|
||
* \retval -1 On Failure
|
||
* \retval 1 On Match
|
||
* \retval 0 On No Match
|
||
*/
|
||
static int msre_op_gsbLookup_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) {
|
||
msc_regex_t *regex = (msc_regex_t *)rule->op_param_data;
|
||
char *my_error_msg = NULL;
|
||
int ovector[33];
|
||
unsigned int offset = 0;
|
||
gsb_db *gsb = msr->txcfg->gsb;
|
||
const char *match = NULL;
|
||
unsigned int match_length;
|
||
unsigned int canon_length;
|
||
int rv, i, ret, count_slash;
|
||
unsigned int j = 0;
|
||
unsigned int size = var->value_len;
|
||
char *base = NULL, *domain = NULL, *savedptr = NULL;
|
||
char *str = NULL, *canon = NULL, *dot = NULL;
|
||
char *data = NULL, *ptr = NULL, *url = NULL;
|
||
int capture, domain_len;
|
||
int d_pos = -1;
|
||
int s_pos = -1;
|
||
|
||
if (error_msg == NULL) return -1;
|
||
*error_msg = NULL;
|
||
|
||
if(regex == NULL) {
|
||
*error_msg = "Internal Error: regex is null.";
|
||
return 0;
|
||
}
|
||
|
||
if(gsb == NULL) {
|
||
msr_log(msr, 1, "GSB lookup failed without a database. Set SecGsbLookupDB.");
|
||
return 0;
|
||
}
|
||
|
||
data = apr_pcalloc(rule->ruleset->mp, var->value_len+1);
|
||
|
||
if(data == NULL) {
|
||
*error_msg = "Internal Error: cannot allocate memory for data.";
|
||
return -1;
|
||
}
|
||
|
||
capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0;
|
||
|
||
memcpy(data,var->value,var->value_len);
|
||
|
||
while (offset < size && (rv = msc_regexec_ex(regex, data, size, offset, PCRE_NOTEMPTY, ovector, 30, &my_error_msg)) >= 0)
|
||
{
|
||
for(i = 0; i < rv; ++i)
|
||
{
|
||
match = apr_psprintf(rule->ruleset->mp, "%.*s", ovector[2*i+1] - ovector[2*i], data + ovector[2*i]);
|
||
|
||
if (match == NULL) {
|
||
*error_msg = "Internal Error: cannot allocate memory for match.";
|
||
return -1;
|
||
}
|
||
|
||
match = remove_escape(rule->ruleset->mp, match, strlen(match));
|
||
|
||
match = gsb_replace_tpath(rule->ruleset->mp, match, strlen(match));
|
||
|
||
match = gsb_reduce_char(rule->ruleset->mp, match);
|
||
|
||
match_length = strlen(match);
|
||
|
||
strtolower_inplace((unsigned char *)match);
|
||
|
||
if((strstr(match,"http") == NULL) && (match_length > 0) && (strchr(match,'.'))) {
|
||
|
||
/* full url */
|
||
if (msr->txcfg->debuglog_level >= 4) {
|
||
msr_log(msr, 4, "GSB: Successfully extracted url: %s", match);
|
||
}
|
||
|
||
ret = verify_gsb(gsb, msr, match, match_length);
|
||
|
||
if(ret > 0) {
|
||
set_match_to_tx(msr, capture, match, 0);
|
||
if (! *error_msg) {
|
||
*error_msg = apr_psprintf(msr->mp, "Gsb lookup for \"%s\" succeeded.",
|
||
log_escape_nq(msr->mp, match));
|
||
}
|
||
|
||
str = apr_pstrdup(rule->ruleset->mp,match);
|
||
|
||
base = apr_strtok(str,"/",&savedptr);
|
||
if(base != NULL)
|
||
set_match_to_tx(msr, capture, base, 1);
|
||
|
||
return 1;
|
||
}
|
||
|
||
/* append / in the end of full url */
|
||
if ((match[match_length -1] != '/') && (strchr(match,'?') == NULL)) {
|
||
|
||
canon = apr_psprintf(rule->ruleset->mp, "%s/", match);
|
||
if (canon != NULL) {
|
||
|
||
canon_length = strlen(canon);
|
||
ret = verify_gsb(gsb, msr, canon, canon_length);
|
||
|
||
if(ret > 0) {
|
||
set_match_to_tx(msr, capture, match, 0);
|
||
if (! *error_msg) {
|
||
*error_msg = apr_psprintf(msr->mp, "Gsb lookup for \"%s\" succeeded.",
|
||
log_escape_nq(msr->mp, canon));
|
||
}
|
||
|
||
str = apr_pstrdup(rule->ruleset->mp,match);
|
||
|
||
base = apr_strtok(str,"/",&savedptr);
|
||
if(base != NULL)
|
||
set_match_to_tx(msr, capture, base, 1);
|
||
|
||
return 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Parsing full url */
|
||
|
||
domain = apr_pstrdup(rule->ruleset->mp, match);
|
||
|
||
domain_len = strlen(domain);
|
||
|
||
if(*domain != '/') {
|
||
|
||
if(domain[domain_len-1] == '.')
|
||
domain[domain_len-1] = '\0';
|
||
if(domain[domain_len-1] == '/' && domain[domain_len-2] == '.') {
|
||
domain[domain_len-2] = '/';
|
||
domain[domain_len-1] = '\0';
|
||
}
|
||
|
||
dot = strchr(domain,'.');
|
||
if(dot != NULL) {
|
||
canon = apr_pstrdup(rule->ruleset->mp, domain);
|
||
|
||
ret = verify_gsb(gsb, msr, canon, strlen(canon));
|
||
|
||
if(ret > 0) {
|
||
set_match_to_tx(msr, capture, canon, 0);
|
||
if (! *error_msg) {
|
||
*error_msg = apr_psprintf(msr->mp, "Gsb lookup for \"%s\" succeeded.",
|
||
log_escape_nq(msr->mp, canon));
|
||
}
|
||
|
||
str = apr_pstrdup(rule->ruleset->mp,match);
|
||
|
||
base = apr_strtok(str,"/",&savedptr);
|
||
if(base != NULL)
|
||
set_match_to_tx(msr, capture, base, 1);
|
||
|
||
return 1;
|
||
}
|
||
|
||
|
||
base = apr_strtok(canon,"?",&savedptr);
|
||
|
||
if(base != NULL) {
|
||
ret = verify_gsb(gsb, msr, base, strlen(base));
|
||
|
||
if(ret > 0) {
|
||
set_match_to_tx(msr, capture, base, 0);
|
||
if (! *error_msg) {
|
||
*error_msg = apr_psprintf(msr->mp, "Gsb lookup for \"%s\" succeeded.",
|
||
log_escape_nq(msr->mp, base));
|
||
}
|
||
|
||
str = apr_pstrdup(rule->ruleset->mp,match);
|
||
|
||
base = apr_strtok(str,"/",&savedptr);
|
||
if(base != NULL)
|
||
set_match_to_tx(msr, capture, base, 1);
|
||
|
||
return 1;
|
||
}
|
||
|
||
}
|
||
|
||
url = apr_palloc(rule->ruleset->mp, strlen(canon));
|
||
count_slash = 0;
|
||
|
||
while(*canon != '\0') {
|
||
switch (*canon) {
|
||
case '/':
|
||
ptr = apr_psprintf(rule->ruleset->mp,"%s/",url);
|
||
ret = verify_gsb(gsb, msr, ptr, strlen(ptr));
|
||
if(ret > 0) {
|
||
set_match_to_tx(msr, capture, ptr, 0);
|
||
if (! *error_msg) {
|
||
*error_msg = apr_psprintf(msr->mp, "Gsb lookup for \"%s\" succeeded.",
|
||
log_escape_nq(msr->mp, ptr));
|
||
}
|
||
|
||
str = apr_pstrdup(rule->ruleset->mp,match);
|
||
|
||
base = apr_strtok(str,"/",&savedptr);
|
||
if(base != NULL)
|
||
set_match_to_tx(msr, capture, base, 1);
|
||
return 1;
|
||
}
|
||
|
||
break;
|
||
}
|
||
url[count_slash] = *canon;
|
||
count_slash++;
|
||
canon++;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Do the same for subdomains */
|
||
|
||
for(j=0; j<strlen(match); j++) {
|
||
if(match[j] == '/') {
|
||
s_pos = j;
|
||
break;
|
||
}
|
||
}
|
||
|
||
|
||
str = apr_pstrdup(rule->ruleset->mp, match);
|
||
|
||
while (*str != '\0') {
|
||
|
||
switch(*str) {
|
||
case '.':
|
||
domain++;
|
||
domain_len = strlen(domain);
|
||
|
||
d_pos = strchr(domain,'.') - domain;
|
||
|
||
if(s_pos >= 0 && d_pos >= 0 && d_pos > s_pos)
|
||
break;
|
||
|
||
if(*domain != '/') {
|
||
|
||
if(domain[domain_len-1] == '.')
|
||
domain[domain_len-1] = '\0';
|
||
if(domain[domain_len-1] == '/' && domain[domain_len-2] == '.') {
|
||
domain[domain_len-2] = '/';
|
||
domain[domain_len-1] = '\0';
|
||
}
|
||
|
||
dot = strchr(domain,'.');
|
||
if(dot != NULL) {
|
||
canon = apr_pstrdup(rule->ruleset->mp, domain);
|
||
|
||
ret = verify_gsb(gsb, msr, canon, strlen(canon));
|
||
|
||
if(ret > 0) {
|
||
set_match_to_tx(msr, capture, canon, 0);
|
||
if (! *error_msg) {
|
||
*error_msg = apr_psprintf(msr->mp, "Gsb lookup for \"%s\" succeeded.",
|
||
log_escape_nq(msr->mp, canon));
|
||
}
|
||
str = apr_pstrdup(rule->ruleset->mp,match);
|
||
|
||
base = apr_strtok(str,"/",&savedptr);
|
||
if(base != NULL)
|
||
set_match_to_tx(msr, capture, base, 1);
|
||
return 1;
|
||
}
|
||
|
||
|
||
base = apr_strtok(canon,"?",&savedptr);
|
||
|
||
if(base != NULL) {
|
||
ret = verify_gsb(gsb, msr, base, strlen(base));
|
||
|
||
if(ret > 0) {
|
||
set_match_to_tx(msr, capture, base, 0);
|
||
if (! *error_msg) {
|
||
*error_msg = apr_psprintf(msr->mp, "Gsb lookup for \"%s\" succeeded.",
|
||
log_escape_nq(msr->mp, base));
|
||
}
|
||
str = apr_pstrdup(rule->ruleset->mp,match);
|
||
|
||
base = apr_strtok(str,"/",&savedptr);
|
||
if(base != NULL)
|
||
set_match_to_tx(msr, capture, base, 1);
|
||
return 1;
|
||
}
|
||
|
||
}
|
||
|
||
url = apr_palloc(rule->ruleset->mp, strlen(canon));
|
||
count_slash = 0;
|
||
|
||
while(*canon != '\0') {
|
||
switch (*canon) {
|
||
case '/':
|
||
ptr = apr_psprintf(rule->ruleset->mp,"%s/",url);
|
||
ret = verify_gsb(gsb, msr, ptr, strlen(ptr));
|
||
if(ret > 0) {
|
||
set_match_to_tx(msr, capture, ptr, 0);
|
||
if (! *error_msg) {
|
||
*error_msg = apr_psprintf(msr->mp, "Gsb lookup for \"%s\" succeeded.",
|
||
log_escape_nq(msr->mp, ptr));
|
||
}
|
||
str = apr_pstrdup(rule->ruleset->mp,match);
|
||
|
||
base = apr_strtok(str,"/",&savedptr);
|
||
if(base != NULL)
|
||
set_match_to_tx(msr, capture, base, 1);
|
||
return 1;
|
||
}
|
||
|
||
break;
|
||
}
|
||
url[count_slash] = *canon;
|
||
count_slash++;
|
||
canon++;
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
|
||
domain = str;
|
||
domain++;
|
||
str++;
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
offset = ovector[1];
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* within */
|
||
|
||
static int msre_op_within_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;
|
||
|
||
if (str->value == NULL) {
|
||
*error_msg = "Internal Error: match string is null.";
|
||
return -1;
|
||
}
|
||
|
||
str->value_len = strlen(str->value);
|
||
|
||
if (error_msg == NULL) return -1;
|
||
*error_msg = NULL;
|
||
|
||
expand_macros(msr, str, rule, msr->mp);
|
||
|
||
match = (const char *)str->value;
|
||
match_length = str->value_len;
|
||
|
||
/* If the given target is null we give up without a match */
|
||
if (var->value == NULL) {
|
||
/* No match. */
|
||
return 0;
|
||
}
|
||
|
||
target = var->value;
|
||
target_length = var->value_len;
|
||
|
||
/* The empty string always matches */
|
||
if (target_length == 0) {
|
||
/* Match. */
|
||
*error_msg = apr_psprintf(msr->mp, "String match within \"\" at %s.",
|
||
var->name);
|
||
return 1;
|
||
}
|
||
|
||
/* This is impossible to match */
|
||
if (target_length > match_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 = match_length - target_length;
|
||
for (i = 0; i <= i_max; i++) {
|
||
if (match[i] == target[0]) {
|
||
if (memcmp((target + 1), (match + i + 1), (target_length - 1)) == 0) {
|
||
/* match. */
|
||
*error_msg = apr_psprintf(msr->mp, "String match within \"%s\" at %s.",
|
||
log_escape_ex(msr->mp, match, match_length),
|
||
var->name);
|
||
return 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* No match. */
|
||
return 0;
|
||
}
|
||
|
||
/* contains */
|
||
|
||
static int msre_op_contains_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;
|
||
|
||
if (str->value == NULL) {
|
||
*error_msg = "Internal Error: match string is null.";
|
||
return -1;
|
||
}
|
||
|
||
str->value_len = strlen(str->value);
|
||
|
||
if (error_msg == NULL) return -1;
|
||
*error_msg = NULL;
|
||
|
||
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;
|
||
}
|
||
|
||
/* The empty string always matches */
|
||
if (match_length == 0) {
|
||
/* Match. */
|
||
*error_msg = apr_psprintf(msr->mp, "String match \"\" at %s.", var->name);
|
||
return 1;
|
||
}
|
||
|
||
/* This is impossible to match */
|
||
if (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++) {
|
||
/* First character matched - avoid func call */
|
||
if (target[i] == match[0]) {
|
||
/* See if remaining matches */
|
||
if ( (match_length == 1)
|
||
|| (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),
|
||
var->name);
|
||
return 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* No match. */
|
||
return 0;
|
||
}
|
||
|
||
/** libinjection detectSQLi
|
||
* links against files in libinjection directory
|
||
* See www.client9.com/libinjection for details
|
||
*/
|
||
static int msre_op_detectSQLi_execute(modsec_rec *msr, msre_rule *rule, msre_var *var,
|
||
char **error_msg) {
|
||
|
||
char fingerprint[8];
|
||
int issqli;
|
||
int capture;
|
||
|
||
issqli = libinjection_sqli(var->value, var->value_len, fingerprint);
|
||
capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0;
|
||
|
||
if (issqli) {
|
||
set_match_to_tx(msr, capture, fingerprint, 0);
|
||
|
||
*error_msg = apr_psprintf(msr->mp, "detected SQLi using libinjection with fingerprint '%s'",
|
||
fingerprint);
|
||
if (msr->txcfg->debuglog_level >= 9) {
|
||
msr_log(msr, 9, "ISSQL: libinjection fingerprint '%s' matched input '%s'",
|
||
fingerprint,
|
||
log_escape_ex(msr->mp, var->value, var->value_len));
|
||
}
|
||
} else {
|
||
if (msr->txcfg->debuglog_level >= 9) {
|
||
msr_log(msr, 9, "ISSQL: not sqli, no libinjection sqli fingerprint matched input '%s'",
|
||
log_escape_ex(msr->mp, var->value, var->value_len));
|
||
}
|
||
}
|
||
|
||
return issqli;
|
||
}
|
||
|
||
/** libinjection detectXSS
|
||
*/
|
||
static int msre_op_detectXSS_execute(modsec_rec *msr, msre_rule *rule, msre_var *var,
|
||
char **error_msg) {
|
||
|
||
int is_xss;
|
||
|
||
is_xss = libinjection_xss(var->value, var->value_len);
|
||
|
||
if (is_xss) {
|
||
*error_msg = apr_psprintf(msr->mp, "detected XSS using libinjection.");
|
||
|
||
if (msr->txcfg->debuglog_level >= 9) {
|
||
msr_log(msr, 9, "IS_XSS: libinjection detected XSS.");
|
||
}
|
||
} else {
|
||
if (msr->txcfg->debuglog_level >= 9) {
|
||
msr_log(msr, 9, "IS_XSS: not XSS, libinjection was not able to find any XSS.");
|
||
}
|
||
}
|
||
|
||
return is_xss;
|
||
}
|
||
|
||
|
||
/* 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;
|
||
int rc = 0;
|
||
|
||
str->value = (char *)rule->op_param;
|
||
|
||
if (str->value == NULL) {
|
||
*error_msg = "Internal Error: match string is null.";
|
||
return -1;
|
||
}
|
||
|
||
str->value_len = strlen(str->value);
|
||
|
||
if (error_msg == NULL) return -1;
|
||
*error_msg = NULL;
|
||
|
||
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;
|
||
}
|
||
|
||
/* The empty string always matches */
|
||
if (match_length == 0) {
|
||
/* Match. */
|
||
*error_msg = apr_psprintf(msr->mp, "String match \"\" at %s.", var->name);
|
||
return 1;
|
||
}
|
||
|
||
/* This is impossible to match */
|
||
if (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) && (apr_isalnum(target[i-1])||(target[i-1] == '_')))
|
||
continue;
|
||
|
||
/* First character matched - avoid func call */
|
||
if (target[i] == match[0]) {
|
||
/* See if remaining matches */
|
||
if ( (match_length == 1)
|
||
|| (memcmp((match + 1), (target + i + 1), (match_length - 1)) == 0))
|
||
{
|
||
/* check boundaries */
|
||
if (i == i_max) {
|
||
/* exact/end word match */
|
||
rc = 1;
|
||
}
|
||
else if (!(apr_isalnum(target[i + match_length])||(target[i + match_length] == '_'))) {
|
||
/* start/mid word match */
|
||
rc = 1;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (rc == 1) {
|
||
/* Maybe a match. */
|
||
*error_msg = apr_psprintf(msr->mp, "String match \"%s\" at %s.",
|
||
log_escape_ex(msr->mp, match, match_length),
|
||
var->name);
|
||
return 1;
|
||
}
|
||
|
||
/* 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) {
|
||
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;
|
||
|
||
str->value = (char *)rule->op_param;
|
||
|
||
if (str->value == NULL) {
|
||
*error_msg = "Internal Error: match string is null.";
|
||
return -1;
|
||
}
|
||
|
||
str->value_len = strlen(str->value);
|
||
|
||
if (error_msg == NULL) return -1;
|
||
*error_msg = NULL;
|
||
|
||
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 != target_length) {
|
||
/* No match. */
|
||
return 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),
|
||
var->name);
|
||
return 1;
|
||
}
|
||
|
||
/* No match. */
|
||
return 0;
|
||
}
|
||
|
||
/* beginsWith */
|
||
|
||
static int msre_op_beginsWith_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;
|
||
|
||
str->value = (char *)rule->op_param;
|
||
|
||
if (str->value == NULL) {
|
||
*error_msg = "Internal Error: match string is null.";
|
||
return -1;
|
||
}
|
||
|
||
str->value_len = strlen(str->value);
|
||
|
||
if (error_msg == NULL) return -1;
|
||
*error_msg = NULL;
|
||
|
||
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;
|
||
}
|
||
|
||
/* The empty string always matches */
|
||
if (match_length == 0) {
|
||
/* Match. */
|
||
*error_msg = apr_psprintf(msr->mp, "String match \"\" at %s.", var->name);
|
||
return 1;
|
||
}
|
||
|
||
/* This is impossible to match */
|
||
if (match_length > target_length) {
|
||
/* No match. */
|
||
return 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),
|
||
var->name);
|
||
return 1;
|
||
}
|
||
|
||
/* No match. */
|
||
return 0;
|
||
}
|
||
|
||
/* endsWith */
|
||
|
||
static int msre_op_endsWith_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;
|
||
|
||
str->value = (char *)rule->op_param;
|
||
|
||
if (str->value == NULL) {
|
||
*error_msg = "Internal Error: match string is null.";
|
||
return -1;
|
||
}
|
||
|
||
str->value_len = strlen(str->value);
|
||
|
||
if (error_msg == NULL) return -1;
|
||
*error_msg = NULL;
|
||
|
||
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;
|
||
}
|
||
|
||
/* The empty string always matches */
|
||
if (match_length == 0) {
|
||
/* Match. */
|
||
*error_msg = apr_psprintf(msr->mp, "String match \"\" at %s.", var->name);
|
||
return 1;
|
||
}
|
||
|
||
/* This is impossible to match */
|
||
if (match_length > target_length) {
|
||
/* No match. */
|
||
return 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),
|
||
var->name);
|
||
return 1;
|
||
}
|
||
|
||
/* No match. */
|
||
return 0;
|
||
}
|
||
|
||
/* strmatch */
|
||
|
||
static int msre_op_strmatch_param_init(msre_rule *rule, char **error_msg) {
|
||
const apr_strmatch_pattern *compiled_pattern;
|
||
char *processed = NULL;
|
||
const char *pattern = rule->op_param;
|
||
unsigned short int op_len;
|
||
|
||
if (error_msg == NULL) return -1;
|
||
*error_msg = NULL;
|
||
|
||
op_len = strlen(pattern);
|
||
|
||
/* Process pattern */
|
||
processed = parse_pm_content(pattern, op_len, rule, error_msg);
|
||
if (processed == NULL) {
|
||
return 0;
|
||
}
|
||
|
||
/* Compile pattern */
|
||
compiled_pattern = apr_strmatch_precompile(rule->ruleset->mp, processed, 1);
|
||
if (compiled_pattern == NULL) {
|
||
*error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern: %s", pattern);
|
||
return 0;
|
||
}
|
||
|
||
rule->op_param_data = (void *)compiled_pattern;
|
||
|
||
return 1; /* OK */
|
||
}
|
||
|
||
static int msre_op_strmatch_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) {
|
||
apr_strmatch_pattern *compiled_pattern = (apr_strmatch_pattern *)rule->op_param_data;
|
||
const char *target;
|
||
unsigned int target_length;
|
||
const char *rc;
|
||
|
||
if (error_msg == NULL) return -1;
|
||
*error_msg = NULL;
|
||
|
||
if (compiled_pattern == NULL) {
|
||
*error_msg = "Internal Error: strnmatch data is null.";
|
||
return -1;
|
||
}
|
||
|
||
/* 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;
|
||
}
|
||
|
||
rc = apr_strmatch(compiled_pattern, target, target_length);
|
||
if (rc == NULL) {
|
||
/* No match. */
|
||
return 0;
|
||
}
|
||
|
||
*error_msg = apr_psprintf(msr->mp, "Pattern match \"%s\" at %s.",
|
||
log_escape(msr->mp, rule->op_param), var->name);
|
||
|
||
/* Match. */
|
||
return 1;
|
||
}
|
||
|
||
/* validateDTD */
|
||
|
||
static int msre_op_validateDTD_init(msre_rule *rule, char **error_msg) {
|
||
/* ENH Verify here the file actually exists. */
|
||
return 1;
|
||
}
|
||
|
||
static int msre_op_validateDTD_execute(modsec_rec *msr, msre_rule *rule, msre_var *var,
|
||
char **error_msg)
|
||
{
|
||
xmlValidCtxtPtr cvp;
|
||
xmlDtdPtr dtd;
|
||
|
||
if ((msr->xml == NULL)||(msr->xml->doc == NULL)) {
|
||
*error_msg = apr_psprintf(msr->mp,
|
||
"XML document tree could not be found for DTD validation.");
|
||
return -1;
|
||
}
|
||
|
||
if (msr->xml->well_formed != 1) {
|
||
*error_msg = apr_psprintf(msr->mp,
|
||
"XML: DTD validation failed because content is not well formed.");
|
||
return 1;
|
||
}
|
||
|
||
/* Make sure there were no other generic processing errors */
|
||
if (msr->msc_reqbody_error) {
|
||
*error_msg = apr_psprintf(msr->mp,
|
||
"XML: DTD validation could not proceed due to previous"
|
||
" processing errors.");
|
||
return 1;
|
||
}
|
||
|
||
dtd = xmlParseDTD(NULL, (const xmlChar *)rule->op_param); /* EHN support relative filenames */
|
||
if (dtd == NULL) {
|
||
*error_msg = apr_psprintf(msr->mp, "XML: Failed to load DTD: %s", rule->op_param);
|
||
return -1;
|
||
}
|
||
|
||
cvp = xmlNewValidCtxt();
|
||
if (cvp == NULL) {
|
||
*error_msg = "XML: Failed to create a validation context.";
|
||
xmlFreeDtd(dtd);
|
||
return -1;
|
||
}
|
||
|
||
/* Send validator errors/warnings to msr_log */
|
||
/* NOTE: No xmlDtdSetValidErrors()? */
|
||
cvp->error = (xmlSchemaValidityErrorFunc)msr_log_error;
|
||
cvp->warning = (xmlSchemaValidityErrorFunc)msr_log_warn;
|
||
cvp->userData = msr;
|
||
|
||
if (!xmlValidateDtd(cvp, msr->xml->doc, dtd)) {
|
||
*error_msg = "XML: DTD validation failed.";
|
||
xmlFreeValidCtxt(cvp);
|
||
xmlFreeDtd(dtd);
|
||
return 1; /* No match. */
|
||
}
|
||
|
||
if (msr->txcfg->debuglog_level >= 4) {
|
||
msr_log(msr, 4, "XML: Successfully validated payload against DTD: %s", rule->op_param);
|
||
}
|
||
|
||
xmlFreeValidCtxt(cvp);
|
||
xmlFreeDtd(dtd);
|
||
|
||
/* Match. */
|
||
return 0;
|
||
}
|
||
|
||
/* validateSchema */
|
||
|
||
static int msre_op_validateSchema_init(msre_rule *rule, char **error_msg) {
|
||
/* ENH Verify here the file actually exists. */
|
||
return 1;
|
||
}
|
||
|
||
static int msre_op_validateSchema_execute(modsec_rec *msr, msre_rule *rule, msre_var *var,
|
||
char **error_msg)
|
||
{
|
||
xmlSchemaParserCtxtPtr parserCtx;
|
||
xmlSchemaValidCtxtPtr validCtx;
|
||
xmlSchemaPtr schema;
|
||
int rc;
|
||
|
||
if ((msr->xml == NULL)||(msr->xml->doc == NULL)) {
|
||
*error_msg = apr_psprintf(msr->mp,
|
||
"XML document tree could not be found for schema validation.");
|
||
return -1;
|
||
}
|
||
|
||
if (msr->xml->well_formed != 1) {
|
||
*error_msg = apr_psprintf(msr->mp,
|
||
"XML: Schema validation failed because content is not well formed.");
|
||
return 1;
|
||
}
|
||
|
||
/* Make sure there were no other generic processing errors */
|
||
if (msr->msc_reqbody_error) {
|
||
*error_msg = apr_psprintf(msr->mp,
|
||
"XML: Schema validation could not proceed due to previous"
|
||
" processing errors.");
|
||
return 1;
|
||
}
|
||
|
||
parserCtx = xmlSchemaNewParserCtxt(rule->op_param); /* ENH support relative filenames */
|
||
if (parserCtx == NULL) {
|
||
*error_msg = apr_psprintf(msr->mp, "XML: Failed to load Schema from file: %s",
|
||
rule->op_param);
|
||
return -1;
|
||
}
|
||
|
||
/* Send parser errors/warnings to msr_log */
|
||
xmlSchemaSetParserErrors(parserCtx, (xmlSchemaValidityErrorFunc)msr_log_error, (xmlSchemaValidityWarningFunc)msr_log_warn, msr);
|
||
|
||
schema = xmlSchemaParse(parserCtx);
|
||
if (schema == NULL) {
|
||
*error_msg = apr_psprintf(msr->mp, "XML: Failed to load Schema: %s", rule->op_param);
|
||
xmlSchemaFreeParserCtxt(parserCtx);
|
||
return -1;
|
||
}
|
||
|
||
validCtx = xmlSchemaNewValidCtxt(schema);
|
||
if (validCtx == NULL) {
|
||
*error_msg = "XML: Failed to create validation context.";
|
||
xmlSchemaFree(schema);
|
||
xmlSchemaFreeParserCtxt(parserCtx);
|
||
return -1;
|
||
}
|
||
|
||
/* Send validator errors/warnings to msr_log */
|
||
xmlSchemaSetValidErrors(validCtx, (xmlSchemaValidityErrorFunc)msr_log_error, (xmlSchemaValidityWarningFunc)msr_log_warn, msr);
|
||
|
||
rc = xmlSchemaValidateDoc(validCtx, msr->xml->doc);
|
||
if (rc != 0) {
|
||
*error_msg = "XML: Schema validation failed.";
|
||
xmlSchemaFree(schema);
|
||
xmlSchemaFreeParserCtxt(parserCtx);
|
||
return 1; /* No match. */
|
||
}
|
||
|
||
if (msr->txcfg->debuglog_level >= 4) {
|
||
msr_log(msr, 4, "XML: Successfully validated payload against Schema: %s", rule->op_param);
|
||
}
|
||
|
||
xmlSchemaFree(schema);
|
||
xmlSchemaFreeValidCtxt(validCtx);
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* verifyCC */
|
||
|
||
/**
|
||
* Luhn Mod-10 Method (ISO 2894/ANSI 4.13)
|
||
*/
|
||
static int luhn_verify(const char *ccnumber, int len) {
|
||
int sum[2] = { 0, 0 };
|
||
int odd = 0;
|
||
int digits = 0;
|
||
int i;
|
||
|
||
/* Weighted lookup table which is just a precalculated (i = index):
|
||
* i*2 + (( (i*2) > 9 ) ? -9 : 0)
|
||
*/
|
||
static const int wtable[10] = {0, 2, 4, 6, 8, 1, 3, 5, 7, 9}; /* weight lookup table */
|
||
|
||
/* Add up only digits (weighted digits via lookup table)
|
||
* for both odd and even CC numbers to avoid 2 passes.
|
||
*/
|
||
for (i = 0; i < len; i++) {
|
||
if (apr_isdigit(ccnumber[i])) {
|
||
sum[0] += (!odd ? wtable[ccnumber[i] - '0'] : (ccnumber[i] - '0'));
|
||
sum[1] += (odd ? wtable[ccnumber[i] - '0'] : (ccnumber[i] - '0'));
|
||
odd = 1 - odd; /* alternate weights */
|
||
digits++;
|
||
}
|
||
}
|
||
|
||
/* No digits extracted */
|
||
if (digits == 0) return 0;
|
||
|
||
/* Do a mod 10 on the sum */
|
||
sum[odd] %= 10;
|
||
|
||
/* If the result is a zero the card is valid. */
|
||
return sum[odd] ? 0 : 1;
|
||
}
|
||
|
||
static int msre_op_verifyCC_init(msre_rule *rule, char **error_msg) {
|
||
const char *errptr = NULL;
|
||
int erroffset;
|
||
msc_regex_t *regex;
|
||
|
||
if (error_msg == NULL) return -1;
|
||
*error_msg = NULL;
|
||
|
||
/* Compile rule->op_param */
|
||
regex = msc_pregcomp_ex(rule->ruleset->mp, rule->op_param, PCRE_DOTALL | PCRE_MULTILINE, &errptr, &erroffset, msc_pcre_match_limit, msc_pcre_match_limit_recursion);
|
||
if (regex == NULL) {
|
||
*error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern (offset %d): %s",
|
||
erroffset, errptr);
|
||
return 0;
|
||
}
|
||
|
||
rule->op_param_data = regex;
|
||
|
||
return 1; /* OK */
|
||
}
|
||
|
||
static int msre_op_verifyCC_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) {
|
||
msc_regex_t *regex = (msc_regex_t *)rule->op_param_data;
|
||
const char *target;
|
||
unsigned int target_length;
|
||
char *my_error_msg = NULL;
|
||
int ovector[33];
|
||
int rc;
|
||
int is_cc = 0;
|
||
int offset;
|
||
int matched_bytes = 0;
|
||
char *qspos = NULL;
|
||
const char *parm = NULL;
|
||
msc_parm *mparm = NULL;
|
||
#ifdef WITH_PCRE_STUDY
|
||
#ifdef WITH_PCRE_JIT
|
||
int jit;
|
||
#endif
|
||
#endif
|
||
|
||
if (error_msg == NULL) return -1;
|
||
*error_msg = NULL;
|
||
|
||
if (regex == NULL) {
|
||
*error_msg = "Internal Error: regex data is null.";
|
||
return -1;
|
||
}
|
||
|
||
memset(ovector, 0, sizeof(ovector));
|
||
|
||
#ifdef WITH_PCRE_STUDY
|
||
#ifdef WITH_PCRE_JIT
|
||
if (msr->txcfg->debuglog_level >= 4) {
|
||
rc = msc_fullinfo(regex, PCRE_INFO_JIT, &jit);
|
||
if ((rc != 0) || (jit != 1)) {
|
||
*error_msg = apr_psprintf(rule->ruleset->mp,
|
||
"Rule %pp [id \"%s\"][file \"%s\"][line \"%d\"] - "
|
||
"Execution error - "
|
||
"Does not support JIT (%d)",
|
||
rule,((rule->actionset != NULL)&&((rule->actionset->id != NULL)&&
|
||
(rule->actionset->id != NOT_SET_P))) ? rule->actionset->id : "-",
|
||
rule->filename != NULL ? rule->filename : "-",
|
||
rule->line_num,rc);
|
||
msr_log(msr, 4, "%s.", *error_msg);
|
||
}
|
||
}
|
||
#endif
|
||
#endif
|
||
|
||
|
||
/* 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;
|
||
}
|
||
|
||
for (offset = 0; ((unsigned int)offset < target_length) && (is_cc == 0); offset++) {
|
||
if (msr->txcfg->debuglog_level >= 9) {
|
||
if (offset > 0) {
|
||
msr_log(msr, 9, "Continuing CC# search at target offset %d.", offset);
|
||
}
|
||
}
|
||
|
||
rc = msc_regexec_ex(regex, target, target_length, offset, PCRE_NOTEMPTY, ovector, 30, &my_error_msg);
|
||
|
||
/* If there was no match, then we are done. */
|
||
if (rc == PCRE_ERROR_NOMATCH) {
|
||
break;
|
||
}
|
||
|
||
if (rc < -1) {
|
||
*error_msg = apr_psprintf(msr->mp, "CC# regex execution failed: %s", my_error_msg);
|
||
return -1;
|
||
}
|
||
|
||
/* Verify a match. */
|
||
if (rc > 0) {
|
||
const char *match = target + ovector[0];
|
||
int length = ovector[1] - ovector[0];
|
||
int i = 0;
|
||
|
||
offset = ovector[2*i];
|
||
|
||
/* Check the Luhn using the match string */
|
||
is_cc = luhn_verify(match, length);
|
||
|
||
/* Not a CC number, then try another match where we left off. */
|
||
if (!is_cc) {
|
||
if (msr->txcfg->debuglog_level >= 9) {
|
||
msr_log(msr, 9, "CC# Luhn check failed at target offset %d: \"%.*s\"", offset, length, match);
|
||
}
|
||
|
||
continue;
|
||
}
|
||
|
||
/* We have a potential CC number and need to set any captures
|
||
* and we are done.
|
||
*/
|
||
|
||
matched_bytes = apr_table_get(rule->actionset->actions, "sanitizeMatchedBytes") ? 1 : 0;
|
||
if(!matched_bytes)
|
||
matched_bytes = apr_table_get(rule->actionset->actions, "sanitiseMatchedBytes") ? 1 : 0;
|
||
|
||
|
||
if (apr_table_get(rule->actionset->actions, "capture")) {
|
||
for(; i < rc; i++) {
|
||
msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
|
||
if (s == NULL) return -1;
|
||
s->name = apr_psprintf(msr->mp, "%d", i);
|
||
if (s->name == NULL) return -1;
|
||
s->name_len = strlen(s->name);
|
||
s->value = apr_pstrmemdup(msr->mp, match, length);
|
||
if (s->value == NULL) return -1;
|
||
s->value_len = length;
|
||
|
||
apr_table_setn(msr->tx_vars, s->name, (void *)s);
|
||
|
||
if (msr->txcfg->debuglog_level >= 9) {
|
||
msr_log(msr, 9, "Added regex subexpression to TX.%d: %s", i,
|
||
log_escape_nq_ex(msr->mp, s->value, s->value_len));
|
||
}
|
||
|
||
if((matched_bytes == 1) && (var != NULL) && (var->name != NULL)) {
|
||
qspos = apr_psprintf(msr->mp, "%s", var->name);
|
||
parm = strstr(qspos, ":");
|
||
if (parm != NULL) {
|
||
parm++;
|
||
mparm = apr_palloc(msr->mp, sizeof(msc_parm));
|
||
if (mparm == NULL)
|
||
continue;
|
||
|
||
mparm->value = apr_pstrmemdup(msr->mp,s->value,s->value_len);
|
||
mparm->pad_1 = rule->actionset->arg_min;
|
||
mparm->pad_2 = rule->actionset->arg_max;
|
||
apr_table_addn(msr->pattern_to_sanitize, parm, (void *)mparm);
|
||
} else {
|
||
mparm = apr_palloc(msr->mp, sizeof(msc_parm));
|
||
if (mparm == NULL)
|
||
continue;
|
||
|
||
mparm->value = apr_pstrmemdup(msr->mp,s->value,s->value_len);
|
||
apr_table_addn(msr->pattern_to_sanitize, qspos, (void *)mparm);
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
/* Unset the remaining TX vars (from previous invocations). */
|
||
for(; i <= 9; i++) {
|
||
char buf[24];
|
||
apr_snprintf(buf, sizeof(buf), "%i", i);
|
||
apr_table_unset(msr->tx_vars, buf);
|
||
}
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (is_cc) {
|
||
/* Match. */
|
||
|
||
/* This message will be logged. */
|
||
*error_msg = apr_psprintf(msr->mp, "CC# match \"%s\" at %s. [offset \"%d\"]",
|
||
regex->pattern, var->name, offset);
|
||
|
||
return 1;
|
||
}
|
||
|
||
/* No match. */
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* \brief Check for a valid CPF
|
||
*
|
||
* \param cpfnumber Pointer to cpf
|
||
* \param len cpf length
|
||
*
|
||
* \retval 0 On Invalid CPF
|
||
* \retval 1 On Valid CPF
|
||
*/
|
||
static int cpf_verify(const char *cpfnumber, int len) {
|
||
|
||
int factor, part_1, part_2, var_len = len;
|
||
unsigned int sum = 0, i = 0, cpf_len = 11, c;
|
||
int cpf[11];
|
||
char s_cpf[11];
|
||
char bad_cpf[11][11] = { "00000000000",
|
||
"01234567890",
|
||
"11111111111",
|
||
"22222222222",
|
||
"33333333333",
|
||
"44444444444",
|
||
"55555555555",
|
||
"66666666666",
|
||
"77777777777",
|
||
"88888888888",
|
||
"99999999999"};
|
||
|
||
while((*cpfnumber != '\0') && ( var_len > 0)) {
|
||
|
||
if(*cpfnumber != '-' || *cpfnumber != '.') {
|
||
if(i < cpf_len && isdigit(*cpfnumber)) {
|
||
s_cpf[i] = *cpfnumber;
|
||
cpf[i] = convert_to_int(*cpfnumber);
|
||
i++;
|
||
}
|
||
}
|
||
cpfnumber++;
|
||
var_len--;
|
||
}
|
||
|
||
|
||
if (i != cpf_len)
|
||
return 0;
|
||
else {
|
||
for(i = 0; i< cpf_len; i++) {
|
||
if(strncmp(s_cpf,bad_cpf[i],cpf_len) == 0) {
|
||
return 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
part_1 = convert_to_int(s_cpf[cpf_len-2]);
|
||
part_2 = convert_to_int(s_cpf[cpf_len-1]);
|
||
|
||
c = cpf_len;
|
||
|
||
for(i = 0; i < 9; i++) {
|
||
sum += (cpf[i] * --c);
|
||
}
|
||
|
||
factor = (sum % cpf_len);
|
||
|
||
if(factor < 2) {
|
||
cpf[9] = 0;
|
||
} else {
|
||
cpf[9] = cpf_len-factor;
|
||
}
|
||
|
||
sum = 0;
|
||
c = cpf_len;
|
||
|
||
for(i = 0;i < 10; i++)
|
||
sum += (cpf[i] * c--);
|
||
|
||
factor = (sum % cpf_len);
|
||
|
||
if(factor < 2) {
|
||
cpf[10] = 0;
|
||
} else {
|
||
cpf[10] = cpf_len-factor;
|
||
}
|
||
|
||
if(part_1 == cpf[9] && part_2 == cpf[10])
|
||
return 1;
|
||
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* \brief Init function to CPF operator
|
||
*
|
||
* \param rule ModSecurity rule struct
|
||
* \param error_msg Error message
|
||
*
|
||
* \retval 0 On Failure
|
||
* \retval 1 On Success
|
||
*/
|
||
static int msre_op_verifyCPF_init(msre_rule *rule, char **error_msg) {
|
||
const char *errptr = NULL;
|
||
int erroffset;
|
||
msc_regex_t *regex;
|
||
|
||
if (error_msg == NULL) return -1;
|
||
*error_msg = NULL;
|
||
|
||
/* Compile rule->op_param */
|
||
regex = msc_pregcomp_ex(rule->ruleset->mp, rule->op_param, PCRE_DOTALL | PCRE_MULTILINE, &errptr, &erroffset, msc_pcre_match_limit, msc_pcre_match_limit_recursion);
|
||
if (regex == NULL) {
|
||
*error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern (offset %d): %s",
|
||
erroffset, errptr);
|
||
return 0;
|
||
}
|
||
|
||
rule->op_param_data = regex;
|
||
|
||
return 1; /* OK */
|
||
}
|
||
|
||
/**
|
||
* \brief Execution function to CPF operator
|
||
*
|
||
* \param msr ModSecurity transaction resource
|
||
* \param rule ModSecurity rule struct
|
||
* \param var ModSecurity variable struct
|
||
* \param error_msg Error message
|
||
*
|
||
* \retval -1 On Failure
|
||
* \retval 1 On Match
|
||
* \retval 0 On No Match
|
||
*/
|
||
static int msre_op_verifyCPF_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) {
|
||
msc_regex_t *regex = (msc_regex_t *)rule->op_param_data;
|
||
const char *target;
|
||
unsigned int target_length;
|
||
char *my_error_msg = NULL;
|
||
int ovector[33];
|
||
int rc;
|
||
int is_cpf = 0;
|
||
int offset;
|
||
int matched_bytes = 0;
|
||
char *qspos = NULL;
|
||
const char *parm = NULL;
|
||
msc_parm *mparm = NULL;
|
||
#ifdef WITH_PCRE_STUDY
|
||
#ifdef WITH_PCRE_JIT
|
||
int jit;
|
||
#endif
|
||
#endif
|
||
|
||
|
||
if (error_msg == NULL) return -1;
|
||
*error_msg = NULL;
|
||
|
||
if (regex == NULL) {
|
||
*error_msg = "Internal Error: regex data is null.";
|
||
return -1;
|
||
}
|
||
|
||
memset(ovector, 0, sizeof(ovector));
|
||
|
||
#ifdef WITH_PCRE_STUDY
|
||
#ifdef WITH_PCRE_JIT
|
||
if (msr->txcfg->debuglog_level >= 4) {
|
||
rc = msc_fullinfo(regex, PCRE_INFO_JIT, &jit);
|
||
if ((rc != 0) || (jit != 1)) {
|
||
*error_msg = apr_psprintf(rule->ruleset->mp,
|
||
"Rule %pp [id \"%s\"][file \"%s\"][line \"%d\"] - "
|
||
"Execution error - "
|
||
"Does not support JIT (%d)",
|
||
rule,((rule->actionset != NULL)&&((rule->actionset->id != NULL)&&
|
||
(rule->actionset->id != NOT_SET_P))) ? rule->actionset->id : "-",
|
||
rule->filename != NULL ? rule->filename : "-",
|
||
rule->line_num,rc);
|
||
msr_log(msr, 4, "%s.", *error_msg);
|
||
}
|
||
}
|
||
#endif
|
||
#endif
|
||
|
||
/* 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;
|
||
}
|
||
|
||
for (offset = 0; ((unsigned int)offset < target_length) && (is_cpf == 0); offset++) {
|
||
if (msr->txcfg->debuglog_level >= 9) {
|
||
if (offset > 0) {
|
||
msr_log(msr, 9, "Continuing CPF# search at target offset %d.", offset);
|
||
}
|
||
}
|
||
|
||
rc = msc_regexec_ex(regex, target, target_length, offset, PCRE_NOTEMPTY, ovector, 30, &my_error_msg);
|
||
|
||
/* If there was no match, then we are done. */
|
||
if (rc == PCRE_ERROR_NOMATCH) {
|
||
break;
|
||
}
|
||
|
||
if (rc < -1) {
|
||
*error_msg = apr_psprintf(msr->mp, "CPF# regex execution failed: %s", my_error_msg);
|
||
return -1;
|
||
}
|
||
|
||
/* Verify a match. */
|
||
if (rc > 0) {
|
||
const char *match = target + ovector[0];
|
||
int length = ovector[1] - ovector[0];
|
||
int i = 0;
|
||
|
||
offset = ovector[2*i];
|
||
|
||
/* Check CPF using the match string */
|
||
is_cpf = cpf_verify(match, length);
|
||
|
||
/* Not a CPF number, then try another match where we left off. */
|
||
if (!is_cpf) {
|
||
if (msr->txcfg->debuglog_level >= 9) {
|
||
msr_log(msr, 9, "CPF# check failed at target offset %d: \"%.*s\"", offset, length, match);
|
||
}
|
||
|
||
continue;
|
||
}
|
||
|
||
/* We have a potential CPF number and need to set any captures
|
||
* and we are done.
|
||
*/
|
||
|
||
matched_bytes = apr_table_get(rule->actionset->actions, "sanitizeMatchedBytes") ? 1 : 0;
|
||
if(!matched_bytes)
|
||
matched_bytes = apr_table_get(rule->actionset->actions, "sanitiseMatchedBytes") ? 1 : 0;
|
||
|
||
if (apr_table_get(rule->actionset->actions, "capture")) {
|
||
for(; i < rc; i++) {
|
||
msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
|
||
if (s == NULL) return -1;
|
||
s->name = apr_psprintf(msr->mp, "%d", i);
|
||
if (s->name == NULL) return -1;
|
||
s->name_len = strlen(s->name);
|
||
s->value = apr_pstrmemdup(msr->mp, match, length);
|
||
if (s->value == NULL) return -1;
|
||
s->value_len = length;
|
||
|
||
apr_table_setn(msr->tx_vars, s->name, (void *)s);
|
||
|
||
if (msr->txcfg->debuglog_level >= 9) {
|
||
msr_log(msr, 9, "Added regex subexpression to TX.%d: %s", i,
|
||
log_escape_nq_ex(msr->mp, s->value, s->value_len));
|
||
}
|
||
|
||
if((matched_bytes == 1) && (var != NULL) && (var->name != NULL)) {
|
||
qspos = apr_psprintf(msr->mp, "%s", var->name);
|
||
parm = strstr(qspos, ":");
|
||
if (parm != NULL) {
|
||
parm++;
|
||
mparm = apr_palloc(msr->mp, sizeof(msc_parm));
|
||
if (mparm == NULL)
|
||
continue;
|
||
|
||
mparm->value = apr_pstrmemdup(msr->mp,s->value,s->value_len);
|
||
mparm->pad_1 = rule->actionset->arg_min;
|
||
mparm->pad_2 = rule->actionset->arg_max;
|
||
apr_table_addn(msr->pattern_to_sanitize, parm, (void *)mparm);
|
||
} else {
|
||
mparm = apr_palloc(msr->mp, sizeof(msc_parm));
|
||
if (mparm == NULL)
|
||
continue;
|
||
|
||
mparm->value = apr_pstrmemdup(msr->mp,s->value,s->value_len);
|
||
apr_table_addn(msr->pattern_to_sanitize, qspos, (void *)mparm);
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
/* Unset the remaining TX vars (from previous invocations). */
|
||
for(; i <= 9; i++) {
|
||
char buf[24];
|
||
apr_snprintf(buf, sizeof(buf), "%i", i);
|
||
apr_table_unset(msr->tx_vars, buf);
|
||
}
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (is_cpf) {
|
||
/* Match. */
|
||
|
||
/* This message will be logged. */
|
||
*error_msg = apr_psprintf(msr->mp, "CPF# match \"%s\" at %s. [offset \"%d\"]",
|
||
regex->pattern, var->name, offset);
|
||
|
||
return 1;
|
||
}
|
||
|
||
/* No match. */
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* \brief Check for a valid SSN
|
||
*
|
||
* \param msr ModSecurity transaction source
|
||
* \param ssnumber Pointer to ssn
|
||
* \param len ssn length
|
||
*
|
||
* \retval 0 On Invalid SSN
|
||
* \retval 1 On Valid SSN
|
||
*/
|
||
static int ssn_verify(modsec_rec *msr, const char *ssnumber, int len) {
|
||
int i;
|
||
int num[9];
|
||
int digits = 0;
|
||
int area, serial, grp;
|
||
int sequencial = 0;
|
||
int repetitions = 0;
|
||
char *str_area;
|
||
char *str_grp;
|
||
char *str_serial;
|
||
|
||
for (i = 0; i < len; i++) {
|
||
if (apr_isdigit(ssnumber[i])) {
|
||
if (digits < 9)
|
||
num[digits] = convert_to_int(ssnumber[i]);
|
||
digits++;
|
||
}
|
||
}
|
||
|
||
/* Not a valid number */
|
||
if (digits != 9)
|
||
goto invalid;
|
||
|
||
for (i=0; i < 8; i++) {
|
||
if (num[i] == (num[i+1]-1))
|
||
sequencial++;
|
||
|
||
if (num[i] == num[i+1])
|
||
repetitions++;
|
||
}
|
||
|
||
/* We are blocking when all numbers were sequencial or repeated */
|
||
if (sequencial == 8)
|
||
goto invalid;
|
||
|
||
if (repetitions == 8)
|
||
goto invalid;
|
||
|
||
str_area = apr_psprintf(msr->mp,"%d%d%d",num[0],num[1],num[2]);
|
||
str_grp = apr_psprintf(msr->mp,"%d%d",num[3],num[4]);
|
||
str_serial = apr_psprintf(msr->mp,"%d%d%d%d",num[5],num[6],num[7],num[8]);
|
||
|
||
if(str_area == NULL || str_grp == NULL || str_serial == NULL)
|
||
goto invalid;
|
||
|
||
area = atoi(str_area);
|
||
grp = atoi(str_grp);
|
||
serial = atoi(str_serial);
|
||
|
||
/* Cannot has seroed fields */
|
||
if (area == 0 || serial == 0 || grp == 0)
|
||
goto invalid;
|
||
|
||
/* More tests */
|
||
if (area >= 740 || area == 666)
|
||
goto invalid;
|
||
|
||
return 1;
|
||
|
||
invalid:
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* \brief Init function to SSN operator
|
||
*
|
||
* \param rule ModSecurity rule struct
|
||
* \param error_msg Error message
|
||
*
|
||
* \retval 0 On Failure
|
||
* \retval 1 On Success
|
||
*/
|
||
static int msre_op_verifySSN_init(msre_rule *rule, char **error_msg) {
|
||
const char *errptr = NULL;
|
||
int erroffset;
|
||
msc_regex_t *regex;
|
||
|
||
if (error_msg == NULL) return -1;
|
||
*error_msg = NULL;
|
||
|
||
/* Compile rule->op_param */
|
||
regex = msc_pregcomp_ex(rule->ruleset->mp, rule->op_param, PCRE_DOTALL | PCRE_MULTILINE, &errptr, &erroffset, msc_pcre_match_limit, msc_pcre_match_limit_recursion);
|
||
if (regex == NULL) {
|
||
*error_msg = apr_psprintf(rule->ruleset->mp, "Error compiling pattern (offset %d): %s",
|
||
erroffset, errptr);
|
||
return 0;
|
||
}
|
||
|
||
rule->op_param_data = regex;
|
||
|
||
return 1; /* OK */
|
||
}
|
||
|
||
/**
|
||
* \brief Execution function to SSN operator
|
||
*
|
||
* \param msr ModSecurity transaction resource
|
||
* \param rule ModSecurity rule struct
|
||
* \param var ModSecurity variable struct
|
||
* \param error_msg Error message
|
||
*
|
||
* \retval -1 On Failure
|
||
* \retval 1 On Match
|
||
* \retval 0 On No Match
|
||
*/
|
||
static int msre_op_verifySSN_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) {
|
||
msc_regex_t *regex = (msc_regex_t *)rule->op_param_data;
|
||
const char *target;
|
||
unsigned int target_length;
|
||
char *my_error_msg = NULL;
|
||
int ovector[33];
|
||
int rc;
|
||
int is_ssn = 0;
|
||
int offset;
|
||
int matched_bytes = 0;
|
||
char *qspos = NULL;
|
||
const char *parm = NULL;
|
||
msc_parm *mparm = NULL;
|
||
#ifdef WITH_PCRE_STUDY
|
||
#ifdef WITH_PCRE_JIT
|
||
int jit;
|
||
#endif
|
||
#endif
|
||
|
||
|
||
if (error_msg == NULL) return -1;
|
||
*error_msg = NULL;
|
||
|
||
if (regex == NULL) {
|
||
*error_msg = "Internal Error: regex data is null.";
|
||
return -1;
|
||
}
|
||
|
||
memset(ovector, 0, sizeof(ovector));
|
||
|
||
#ifdef WITH_PCRE_STUDY
|
||
#ifdef WITH_PCRE_JIT
|
||
if (msr->txcfg->debuglog_level >= 4) {
|
||
rc = msc_fullinfo(regex, PCRE_INFO_JIT, &jit);
|
||
if ((rc != 0) || (jit != 1)) {
|
||
*error_msg = apr_psprintf(rule->ruleset->mp,
|
||
"Rule %pp [id \"%s\"][file \"%s\"][line \"%d\"] - "
|
||
"Execution error - "
|
||
"Does not support JIT (%d)",
|
||
rule,((rule->actionset != NULL)&&((rule->actionset->id != NULL)&&
|
||
(rule->actionset->id != NOT_SET_P))) ? rule->actionset->id : "-",
|
||
rule->filename != NULL ? rule->filename : "-",
|
||
rule->line_num,rc);
|
||
msr_log(msr, 4, "%s.", *error_msg);
|
||
}
|
||
}
|
||
#endif
|
||
#endif
|
||
|
||
/* 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;
|
||
}
|
||
|
||
for (offset = 0; ((unsigned int)offset < target_length) && (is_ssn == 0); offset++) {
|
||
if (msr->txcfg->debuglog_level >= 9) {
|
||
if (offset > 0) {
|
||
msr_log(msr, 9, "Continuing SSN# search at target offset %d.", offset);
|
||
}
|
||
}
|
||
|
||
rc = msc_regexec_ex(regex, target, target_length, offset, PCRE_NOTEMPTY, ovector, 30, &my_error_msg);
|
||
|
||
/* If there was no match, then we are done. */
|
||
if (rc == PCRE_ERROR_NOMATCH) {
|
||
break;
|
||
}
|
||
|
||
if (rc < -1) {
|
||
*error_msg = apr_psprintf(msr->mp, "SSN# regex execution failed: %s", my_error_msg);
|
||
return -1;
|
||
}
|
||
|
||
/* Verify a match. */
|
||
if (rc > 0) {
|
||
const char *match = target + ovector[0];
|
||
int length = ovector[1] - ovector[0];
|
||
int i = 0;
|
||
|
||
offset = ovector[2*i];
|
||
|
||
/* Check SSN using the match string */
|
||
is_ssn = ssn_verify(msr, match, length);
|
||
|
||
/* Not a SSN number, then try another match where we left off. */
|
||
if (!is_ssn) {
|
||
if (msr->txcfg->debuglog_level >= 9) {
|
||
msr_log(msr, 9, "SSN# check failed at target offset %d: \"%.*s\"", offset, length, match);
|
||
}
|
||
|
||
continue;
|
||
}
|
||
|
||
/* We have a potential SSN number and need to set any captures
|
||
* and we are done.
|
||
*/
|
||
|
||
matched_bytes = apr_table_get(rule->actionset->actions, "sanitizeMatchedBytes") ? 1 : 0;
|
||
if(!matched_bytes)
|
||
matched_bytes = apr_table_get(rule->actionset->actions, "sanitiseMatchedBytes") ? 1 : 0;
|
||
|
||
if (apr_table_get(rule->actionset->actions, "capture")) {
|
||
for(; i < rc; i++) {
|
||
msc_string *s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
|
||
if (s == NULL) return -1;
|
||
s->name = apr_psprintf(msr->mp, "%d", i);
|
||
if (s->name == NULL) return -1;
|
||
s->name_len = strlen(s->name);
|
||
s->value = apr_pstrmemdup(msr->mp, match, length);
|
||
if (s->value == NULL) return -1;
|
||
s->value_len = length;
|
||
|
||
apr_table_setn(msr->tx_vars, s->name, (void *)s);
|
||
|
||
if (msr->txcfg->debuglog_level >= 9) {
|
||
msr_log(msr, 9, "Added regex subexpression to TX.%d: %s", i,
|
||
log_escape_nq_ex(msr->mp, s->value, s->value_len));
|
||
}
|
||
|
||
if((matched_bytes == 1) && (var != NULL) && (var->name != NULL)) {
|
||
qspos = apr_psprintf(msr->mp, "%s", var->name);
|
||
parm = strstr(qspos, ":");
|
||
if (parm != NULL) {
|
||
parm++;
|
||
mparm = apr_palloc(msr->mp, sizeof(msc_parm));
|
||
if (mparm == NULL)
|
||
continue;
|
||
|
||
mparm->value = apr_pstrmemdup(msr->mp,s->value,s->value_len);
|
||
mparm->pad_1 = rule->actionset->arg_min;
|
||
mparm->pad_2 = rule->actionset->arg_max;
|
||
apr_table_addn(msr->pattern_to_sanitize, parm, (void *)mparm);
|
||
} else {
|
||
mparm = apr_palloc(msr->mp, sizeof(msc_parm));
|
||
if (mparm == NULL)
|
||
continue;
|
||
|
||
mparm->value = apr_pstrmemdup(msr->mp,s->value,s->value_len);
|
||
apr_table_addn(msr->pattern_to_sanitize, qspos, (void *)mparm);
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
/* Unset the remaining TX vars (from previous invocations). */
|
||
for(; i <= 9; i++) {
|
||
char buf[24];
|
||
apr_snprintf(buf, sizeof(buf), "%i", i);
|
||
apr_table_unset(msr->tx_vars, buf);
|
||
}
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (is_ssn) {
|
||
/* Match. */
|
||
|
||
/* This message will be logged. */
|
||
*error_msg = apr_psprintf(msr->mp, "SSN# match \"%s\" at %s. [offset \"%d\"]",
|
||
regex->pattern, var->name, offset);
|
||
|
||
return 1;
|
||
}
|
||
|
||
/* No match. */
|
||
return 0;
|
||
}
|
||
|
||
/**
|
||
* Perform geograpical lookups on an IP/Host.
|
||
*/
|
||
static int msre_op_geoLookup_execute(modsec_rec *msr, msre_rule *rule, msre_var *var,
|
||
char **error_msg)
|
||
{
|
||
geo_rec rec;
|
||
geo_db *geo = msr->txcfg->geo;
|
||
const char *geo_host = var->value;
|
||
msc_string *s = NULL;
|
||
int rc;
|
||
|
||
*error_msg = NULL;
|
||
|
||
if (geo == NULL) {
|
||
msr_log(msr, 1, "Geo lookup for \"%s\" attempted without a database. Set SecGeoLookupDB.", log_escape(msr->mp, geo_host));
|
||
return 0;
|
||
}
|
||
|
||
|
||
rc = geo_lookup(msr, &rec, geo_host, error_msg);
|
||
if (rc <= 0) {
|
||
if (! *error_msg) {
|
||
*error_msg = apr_psprintf(msr->mp, "Geo lookup for \"%s\" failed at %s.", log_escape_nq(msr->mp, geo_host), var->name);
|
||
}
|
||
apr_table_clear(msr->geo_vars);
|
||
return rc;
|
||
}
|
||
if (! *error_msg) {
|
||
*error_msg = apr_psprintf(msr->mp, "Geo lookup for \"%s\" succeeded at %s.",
|
||
log_escape_nq(msr->mp, geo_host), var->name);
|
||
}
|
||
|
||
if (msr->txcfg->debuglog_level >= 9) {
|
||
msr_log(msr, 9, "GEO: %s={country_code=%s, country_code3=%s, country_name=%s, country_continent=%s, region=%s, city=%s, postal_code=%s, latitude=%f, longitude=%f, dma_code=%d, area_code=%d}",
|
||
geo_host,
|
||
rec.country_code,
|
||
rec.country_code3,
|
||
rec.country_name,
|
||
rec.country_continent,
|
||
rec.region,
|
||
rec.city,
|
||
rec.postal_code,
|
||
rec.latitude,
|
||
rec.longitude,
|
||
rec.dma_code,
|
||
rec.area_code);
|
||
}
|
||
|
||
s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
|
||
s->name = apr_pstrdup(msr->mp, "COUNTRY_CODE");
|
||
s->name_len = strlen(s->name);
|
||
s->value = apr_pstrdup(msr->mp, rec.country_code ? rec.country_code : "");
|
||
s->value_len = strlen(s->value);
|
||
apr_table_setn(msr->geo_vars, s->name, (void *)s);
|
||
|
||
s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
|
||
s->name = apr_pstrdup(msr->mp, "COUNTRY_CODE3");
|
||
s->name_len = strlen(s->name);
|
||
s->value = apr_pstrdup(msr->mp, rec.country_code3 ? rec.country_code3 : "");
|
||
s->value_len = strlen(s->value);
|
||
apr_table_setn(msr->geo_vars, s->name, (void *)s);
|
||
|
||
s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
|
||
s->name = apr_pstrdup(msr->mp, "COUNTRY_NAME");
|
||
s->name_len = strlen(s->name);
|
||
s->value = apr_pstrdup(msr->mp, rec.country_name ? rec.country_name : "");
|
||
s->value_len = strlen(s->value);
|
||
apr_table_setn(msr->geo_vars, s->name, (void *)s);
|
||
|
||
s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
|
||
s->name = apr_pstrdup(msr->mp, "COUNTRY_CONTINENT");
|
||
s->name_len = strlen(s->name);
|
||
s->value = apr_pstrdup(msr->mp, rec.country_continent ? rec.country_continent : "");
|
||
s->value_len = strlen(s->value);
|
||
apr_table_setn(msr->geo_vars, s->name, (void *)s);
|
||
|
||
s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
|
||
s->name = apr_pstrdup(msr->mp, "REGION");
|
||
s->name_len = strlen(s->name);
|
||
s->value = apr_pstrdup(msr->mp, rec.region ? rec.region : "");
|
||
s->value_len = strlen(s->value);
|
||
apr_table_setn(msr->geo_vars, s->name, (void *)s);
|
||
|
||
s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
|
||
s->name = apr_pstrdup(msr->mp, "CITY");
|
||
s->name_len = strlen(s->name);
|
||
s->value = apr_pstrdup(msr->mp, rec.city ? rec.city : "");
|
||
s->value_len = strlen(s->value);
|
||
apr_table_setn(msr->geo_vars, s->name, (void *)s);
|
||
|
||
s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
|
||
s->name = apr_pstrdup(msr->mp, "POSTAL_CODE");
|
||
s->name_len = strlen(s->name);
|
||
s->value = apr_pstrdup(msr->mp, rec.postal_code ? rec.postal_code : "");
|
||
s->value_len = strlen(s->value);
|
||
apr_table_setn(msr->geo_vars, s->name, (void *)s);
|
||
|
||
s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
|
||
s->name = apr_pstrdup(msr->mp, "LATITUDE");
|
||
s->name_len = strlen(s->name);
|
||
s->value = apr_psprintf(msr->mp, "%f", rec.latitude);
|
||
s->value_len = strlen(s->value);
|
||
apr_table_setn(msr->geo_vars, s->name, (void *)s);
|
||
|
||
s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
|
||
s->name = apr_pstrdup(msr->mp, "LONGITUDE");
|
||
s->name_len = strlen(s->name);
|
||
s->value = apr_psprintf(msr->mp, "%f", rec.longitude);
|
||
s->value_len = strlen(s->value);
|
||
apr_table_setn(msr->geo_vars, s->name, (void *)s);
|
||
|
||
s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
|
||
s->name = apr_pstrdup(msr->mp, "DMA_CODE");
|
||
s->name_len = strlen(s->name);
|
||
s->value = apr_psprintf(msr->mp, "%d", rec.dma_code);
|
||
s->value_len = strlen(s->value);
|
||
apr_table_setn(msr->geo_vars, s->name, (void *)s);
|
||
|
||
s = (msc_string *)apr_pcalloc(msr->mp, sizeof(msc_string));
|
||
s->name = apr_pstrdup(msr->mp, "AREA_CODE");
|
||
s->name_len = strlen(s->name);
|
||
s->value = apr_psprintf(msr->mp, "%d", rec.area_code);
|
||
s->value_len = strlen(s->value);
|
||
apr_table_setn(msr->geo_vars, s->name, (void *)s);
|
||
|
||
return 1;
|
||
}
|
||
|
||
/* rbl */
|
||
|
||
static int msre_op_rbl_execute(modsec_rec *msr, msre_rule *rule, msre_var *var, char **error_msg) {
|
||
unsigned int h0, h1, h2, h3;
|
||
unsigned int high8bits = 0;
|
||
char *name_to_check = NULL;
|
||
char *target = NULL;
|
||
apr_sockaddr_t *sa = NULL;
|
||
apr_status_t rc;
|
||
int capture = 0;
|
||
|
||
if (error_msg == NULL) return -1;
|
||
*error_msg = NULL;
|
||
|
||
capture = apr_table_get(rule->actionset->actions, "capture") ? 1 : 0;
|
||
|
||
/* ENH Add IPv6 support. */
|
||
|
||
target = apr_pstrmemdup(msr->mp, var->value, var->value_len);
|
||
if (target == NULL) return -1;
|
||
|
||
/* Construct the host name we want to resolve. */
|
||
if (sscanf(target, "%d.%d.%d.%d", &h0, &h1, &h2, &h3) == 4) {
|
||
/* IPv4 address */
|
||
/* If we're using the httpBl blocklist, we need to add the key */
|
||
if(strstr(rule->op_param,"httpbl.org")) {
|
||
if (msr->txcfg->httpBlkey == NULL) {
|
||
if (msr->txcfg->debuglog_level >= 4) {
|
||
msr_log(msr, 4, "RBL httpBl called but no key defined: set SecHttpBlKey");
|
||
}
|
||
*error_msg = "RBL httpBl called but no key defined: set SecHttpBlKey";
|
||
} else {
|
||
name_to_check = apr_psprintf(msr->mp, "%s.%d.%d.%d.%d.%s", msr->txcfg->httpBlkey, h3, h2, h1, h0, rule->op_param);
|
||
}
|
||
} else {
|
||
/* regular IPv4 RBLs */
|
||
name_to_check = apr_psprintf(msr->mp, "%d.%d.%d.%d.%s", h3, h2, h1, h0, rule->op_param);
|
||
}
|
||
} else {
|
||
/* Assume the input is a domain name. */
|
||
name_to_check = apr_psprintf(msr->mp, "%s.%s", target, rule->op_param);
|
||
}
|
||
|
||
if (name_to_check == NULL) return -1;
|
||
|
||
rc = apr_sockaddr_info_get(&sa, name_to_check,
|
||
APR_UNSPEC/*msr->r->connection->remote_addr->family*/, 0, 0, msr->mp);
|
||
if (rc == APR_SUCCESS) {
|
||
|
||
high8bits = sa->sa.sin.sin_addr.s_addr >> 24;
|
||
|
||
/* multi.uribl.com */
|
||
|
||
if(strstr(rule->op_param,"uribl.com")) {
|
||
|
||
switch(high8bits) {
|
||
case 2:
|
||
*error_msg = apr_psprintf(msr->r->pool, "RBL lookup of %s succeeded at %s (BLACK).",
|
||
log_escape_nq(msr->mp, name_to_check), var->name);
|
||
break;
|
||
case 4:
|
||
*error_msg = apr_psprintf(msr->r->pool, "RBL lookup of %s succeeded at %s (GREY).",
|
||
log_escape_nq(msr->mp, name_to_check), var->name);
|
||
break;
|
||
case 8:
|
||
*error_msg = apr_psprintf(msr->r->pool, "RBL lookup of %s succeeded at %s (RED).",
|
||
log_escape_nq(msr->mp, name_to_check), var->name);
|
||
break;
|
||
case 14:
|
||
*error_msg = apr_psprintf(msr->r->pool, "RBL lookup of %s succeeded at %s (BLACK,GREY,RED).",
|
||
log_escape_nq(msr->mp, name_to_check), var->name);
|
||
break;
|
||
case 255:
|
||
*error_msg = apr_psprintf(msr->r->pool, "RBL lookup of %s succeeded at %s (DNS IS BLOCKED).",
|
||
log_escape_nq(msr->mp, name_to_check), var->name);
|
||
break;
|
||
default:
|
||
*error_msg = apr_psprintf(msr->r->pool, "RBL lookup of %s succeeded at %s (WHITE).",
|
||
log_escape_nq(msr->mp, name_to_check), var->name);
|
||
break;
|
||
}
|
||
|
||
set_match_to_tx(msr, capture, *error_msg, 0);
|
||
|
||
} else
|
||
if(strstr(rule->op_param,"spamhaus.org")) {
|
||
|
||
switch(high8bits) {
|
||
case 2:
|
||
case 3:
|
||
*error_msg = apr_psprintf(msr->r->pool, "RBL lookup of %s succeeded at %s (Static UBE sources).",
|
||
log_escape_nq(msr->mp, name_to_check), var->name);
|
||
break;
|
||
case 4:
|
||
case 5:
|
||
case 6:
|
||
case 7:
|
||
*error_msg = apr_psprintf(msr->r->pool, "RBL lookup of %s succeeded at %s (Illegal 3rd party exploits).",
|
||
log_escape_nq(msr->mp, name_to_check), var->name);
|
||
break;
|
||
case 10:
|
||
case 11:
|
||
*error_msg = apr_psprintf(msr->r->pool, "RBL lookup of %s succeeded at %s (Delivering unauthenticated SMTP email).",
|
||
log_escape_nq(msr->mp, name_to_check), var->name);
|
||
break;
|
||
default:
|
||
*error_msg = apr_psprintf(msr->r->pool, "RBL lookup of %s succeeded at %s.",
|
||
log_escape_nq(msr->mp, name_to_check), var->name);
|
||
break;
|
||
}
|
||
|
||
set_match_to_tx(msr, capture, *error_msg, 0);
|
||
|
||
} else
|
||
if(strstr(rule->op_param,"httpbl.org")) {
|
||
char *respBl;
|
||
int first, days, score, type;
|
||
|
||
respBl = inet_ntoa(sa->sa.sin.sin_addr);
|
||
if (sscanf(respBl, "%d.%d.%d.%d", &first, &days, &score, &type) != 4) {
|
||
*error_msg = apr_psprintf(msr->r->pool, "RBL lookup of %s failed: bad response", log_escape_nq(msr->mp, name_to_check));
|
||
} else {
|
||
if (first != 127) {
|
||
*error_msg = apr_psprintf(msr->r->pool, "RBL lookup of %s failed: bad response", log_escape_nq(msr->mp, name_to_check));
|
||
}
|
||
else {
|
||
char *ptype;
|
||
switch(type) {
|
||
case 0:
|
||
ptype = "Search Engine";
|
||
break;
|
||
case 1:
|
||
ptype = "Suspicious IP";
|
||
break;
|
||
case 2:
|
||
ptype = "Harvester IP";
|
||
break;
|
||
case 3:
|
||
ptype = "Suspicious harvester IP";
|
||
break;
|
||
case 4:
|
||
ptype = "Comment spammer IP";
|
||
break;
|
||
case 5:
|
||
ptype = "Suspicious comment spammer IP";
|
||
break;
|
||
case 6:
|
||
ptype = "Harvester and comment spammer IP";
|
||
break;
|
||
case 7:
|
||
ptype = "Suspicious harvester comment spammer IP";
|
||
break;
|
||
default:
|
||
ptype = " ";
|
||
}
|
||
*error_msg = apr_psprintf(msr->r->pool, "RBL lookup of %s succeeded at %s. %s: %d days since last activity, threat score %d",
|
||
log_escape_nq(msr->mp, name_to_check), var->name,
|
||
ptype, days, score);
|
||
}
|
||
}
|
||
set_match_to_tx(msr, capture, *error_msg, 0);
|
||
/* end of httpBl code */
|
||
} else {
|
||
*error_msg = apr_psprintf(msr->r->pool, "RBL lookup of %s succeeded at %s.",
|
||
log_escape_nq(msr->mp, name_to_check), var->name);
|
||
|
||
set_match_to_tx(msr, capture, *error_msg, 0);
|
||
|
||
}
|
||
|
||
return 1; /* Match. */
|
||
}
|
||
|
||
if (msr->txcfg->debuglog_level >= 5) {
|
||
msr_log(msr, 5, "RBL lookup of %s failed at %s.", log_escape_nq(msr->mp, name_to_check), var->name);
|
||
}
|
||
|
||
/* No match. */
|
||
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
|
||
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;
|
||
FILE *fp;
|
||
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;
|
||
}
|
||
|
||
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);
|
||
#else
|
||
*error_msg = apr_psprintf(rule->ruleset->mp, "ModSecurity was not " \
|
||
"compiled with ssdeep support.");
|
||
|
||
return -1;
|
||
|
||
#endif
|
||
|
||
/* No match. */
|
||
return 0;
|
||
}
|
||
|
||
/* inspectFile */
|
||
|
||
static int msre_op_inspectFile_init(msre_rule *rule, char **error_msg) {
|
||
char *filename = (char *)rule->op_param;
|
||
|
||
if (error_msg == NULL) return -1;
|
||
*error_msg = NULL;
|
||
|
||
if ((filename == NULL)||(is_empty_string(filename))) {
|
||
*error_msg = apr_psprintf(rule->ruleset->mp, "Operator @inspectFile requires parameter.");
|
||
return -1;
|
||
}
|
||
|
||
filename = resolve_relative_path(rule->ruleset->mp, rule->filename, filename);
|
||
|
||
#if defined(WITH_LUA)
|
||
/* ENH Write & use string_ends(s, e). */
|
||
if (strlen(rule->op_param) > 4) {
|
||
char *p = filename + strlen(filename) - 4;
|
||
if ((p[0] == '.')&&(p[1] == 'l')&&(p[2] == 'u')&&(p[3] == 'a'))
|
||
{
|
||
msc_script *script = NULL;
|
||
|
||
/* Compile script. */
|
||
*error_msg = lua_compile(&script, filename, rule->ruleset->mp);
|
||
if (*error_msg != NULL) return -1;
|
||
|
||
rule->op_param_data = script;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
if (rule->op_param_data == NULL) {
|
||
/* ENH Verify the script exists and that we have
|
||
* the rights to execute it.
|
||
*/
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
static int msre_op_inspectFile_execute(modsec_rec *msr, msre_rule *rule, msre_var *var,
|
||
char **error_msg)
|
||
{
|
||
if (error_msg == NULL) return -1;
|
||
*error_msg = NULL;
|
||
|
||
if (rule->op_param_data == NULL) {
|
||
/* Execute externally, as native binary/shell script. */
|
||
char *script_output = NULL;
|
||
char const *argv[5];
|
||
const char *approver_script = rule->op_param;
|
||
const char *target_file = apr_pstrmemdup(msr->mp, var->value, var->value_len);
|
||
|
||
if (msr->txcfg->debuglog_level >= 4) {
|
||
msr_log(msr, 4, "Executing %s to inspect %s.", approver_script, target_file);
|
||
}
|
||
|
||
argv[0] = approver_script;
|
||
argv[1] = target_file;
|
||
argv[2] = NULL;
|
||
|
||
if (apache2_exec(msr, approver_script, (const char **)argv, &script_output) <= 0) {
|
||
*error_msg = apr_psprintf(msr->mp, "Execution of the approver script \"%s\" failed (invocation failed).",
|
||
log_escape(msr->mp, approver_script));
|
||
return -1;
|
||
}
|
||
|
||
if (script_output == NULL) {
|
||
*error_msg = apr_psprintf(msr->mp, "Execution of the approver script \"%s\" failed (no output).",
|
||
log_escape(msr->mp, approver_script));
|
||
return -1;
|
||
}
|
||
|
||
if (script_output[0] != '1') {
|
||
*error_msg = apr_psprintf(msr->mp, "File \"%s\" rejected by the approver script \"%s\": %s",
|
||
log_escape(msr->mp, target_file), log_escape(msr->mp, approver_script),
|
||
log_escape_nq(msr->mp, script_output));
|
||
return 1; /* Match. */
|
||
}
|
||
}
|
||
#if defined(WITH_LUA)
|
||
else {
|
||
/* Execute internally, as Lua script. */
|
||
char *target = apr_pstrmemdup(msr->mp, var->value, var->value_len);
|
||
msc_script *script = (msc_script *)rule->op_param_data;
|
||
int rc;
|
||
|
||
rc = lua_execute(script, target, msr, rule, error_msg);
|
||
if (rc < 0) {
|
||
/* Error. */
|
||
return -1;
|
||
}
|
||
|
||
return rc;
|
||
}
|
||
#endif
|
||
|
||
/* No match. */
|
||
return 0;
|
||
}
|
||
|
||
/* validateByteRange */
|
||
|
||
static int msre_op_validateByteRange_init(msre_rule *rule, char **error_msg) {
|
||
char *p = NULL, *saveptr = NULL;
|
||
char *table = NULL, *data = NULL;
|
||
|
||
if (error_msg == NULL) return -1;
|
||
*error_msg = NULL;
|
||
|
||
if (rule->op_param == NULL) {
|
||
*error_msg = apr_psprintf(rule->ruleset->mp, "Missing parameter for validateByteRange.");
|
||
return -1;
|
||
}
|
||
|
||
/* Initialise. */
|
||
data = apr_pstrdup(rule->ruleset->mp, rule->op_param);
|
||
rule->op_param_data = apr_pcalloc(rule->ruleset->mp, 32);
|
||
if ((data == NULL)||(rule->op_param_data == NULL)) return -1;
|
||
table = rule->op_param_data;
|
||
|
||
/* Extract parameters and update table. */
|
||
p = apr_strtok(data, ",", &saveptr);
|
||
while(p != NULL) {
|
||
char *s = strstr(p, "-");
|
||
if (s == NULL) {
|
||
/* Single value. */
|
||
int x = atoi(p);
|
||
if ((x < 0)||(x > 255)) {
|
||
*error_msg = apr_psprintf(rule->ruleset->mp, "Invalid range value: %d", x);
|
||
return 0;
|
||
}
|
||
table[x>>3] = (table[x>>3] | (1 << (x & 0x7)));
|
||
} else {
|
||
/* Range. */
|
||
int start = atoi(p);
|
||
int end = atoi(s + 1);
|
||
|
||
if ((start < 0)||(start > 255)) {
|
||
*error_msg = apr_psprintf(rule->ruleset->mp, "Invalid range start value: %d",
|
||
start);
|
||
return 0;
|
||
}
|
||
if ((end < 0)||(end > 255)) {
|
||
*error_msg = apr_psprintf(rule->ruleset->mp, "Invalid range end value: %d", end);
|
||
return 0;
|
||
}
|
||
if (start > end) {
|
||
*error_msg = apr_psprintf(rule->ruleset->mp, "Invalid range: %d-%d", start, end);
|
||
return 0;
|
||
}
|
||
|
||
while(start <= end) {
|
||
table[start >> 3] = (table[start >> 3] | (1 << (start & 0x7)));
|
||
start++;
|
||
}
|
||
}
|
||
|
||
p = apr_strtok(NULL, ",", &saveptr);
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
static int msre_op_validateByteRange_execute(modsec_rec *msr, msre_rule *rule, msre_var *var,
|
||
char **error_msg)
|
||
{
|
||
char *table = rule->op_param_data;
|
||
unsigned int i, count;
|
||
|
||
if (error_msg == NULL) return -1;
|
||
*error_msg = NULL;
|
||
|
||
if (table == NULL) {
|
||
*error_msg = apr_psprintf(msr->mp, "Internal Error: validateByteRange table not "
|
||
"initialised.");
|
||
return -1;
|
||
}
|
||
|
||
/* Check every byte of the target to detect characters that are not allowed. */
|
||
|
||
count = 0;
|
||
for(i = 0; i < var->value_len; i++) {
|
||
int x = ((unsigned char *)var->value)[i];
|
||
if (!(table[x >> 3] & (1 << (x & 0x7)))) {
|
||
if (msr->txcfg->debuglog_level >= 9) {
|
||
msr_log(msr, 9, "Value %d in %s outside range: %s", x, var->name, rule->op_param);
|
||
}
|
||
count++;
|
||
}
|
||
}
|
||
|
||
if (count == 0) return 0; /* Valid - no match. */
|
||
|
||
*error_msg = apr_psprintf(msr->mp, "Found %d byte(s) in %s outside range: %s.",
|
||
count, var->name, rule->op_param);
|
||
|
||
return 1; /* Invalid - match.*/
|
||
}
|
||
|
||
/* validateUrlEncoding */
|
||
|
||
static int validate_url_encoding(const char *input, long int input_length) {
|
||
int i;
|
||
|
||
if ((input == NULL)||(input_length < 0)) return -1;
|
||
|
||
i = 0;
|
||
while (i < input_length) {
|
||
if (input[i] == '%') {
|
||
if (i + 2 >= input_length) {
|
||
/* Not enough bytes. */
|
||
return -3;
|
||
}
|
||
else {
|
||
/* Here we only decode a %xx combination if it is valid,
|
||
* leaving it as is otherwise.
|
||
*/
|
||
char c1 = input[i + 1];
|
||
char c2 = input[i + 2];
|
||
|
||
if ( (((c1 >= '0')&&(c1 <= '9')) || ((c1 >= 'a')&&(c1 <= 'f')) || ((c1 >= 'A')&&(c1 <= 'F')))
|
||
&& (((c2 >= '0')&&(c2 <= '9')) || ((c2 >= 'a')&&(c2 <= 'f')) || ((c2 >= 'A')&&(c2 <= 'F'))) )
|
||
{
|
||
i += 3;
|
||
} else {
|
||
/* Non-hexadecimal characters used in encoding. */
|
||
return -2;
|
||
}
|
||
}
|
||
} else {
|
||
i++;
|
||
}
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
static int msre_op_validateUrlEncoding_execute(modsec_rec *msr, msre_rule *rule, msre_var *var,
|
||
char **error_msg)
|
||
{
|
||
int rc = validate_url_encoding(var->value, var->value_len);
|
||
switch(rc) {
|
||
case 1 :
|
||
/* Encoding is valid */
|
||
*error_msg = apr_psprintf(msr->mp, "Valid URL Encoding at %s.", var->name);
|
||
break;
|
||
case -2 :
|
||
*error_msg = apr_psprintf(msr->mp, "Invalid URL Encoding: Non-hexadecimal "
|
||
"digits used at %s.", var->name);
|
||
return 1; /* Invalid match. */
|
||
break;
|
||
case -3 :
|
||
*error_msg = apr_psprintf(msr->mp, "Invalid URL Encoding: Not enough characters "
|
||
"at the end of input at %s.", var->name);
|
||
return 1; /* Invalid match. */
|
||
break;
|
||
case -1 :
|
||
default :
|
||
*error_msg = apr_psprintf(msr->mp, "Invalid URL Encoding: Internal Error (rc = %d) at %s", rc, var->name);
|
||
return -1;
|
||
break;
|
||
|
||
}
|
||
|
||
/* No match. */
|
||
return 0;
|
||
}
|
||
|
||
/* validateUtf8Encoding */
|
||
|
||
/* NOTE: This is over-commented for ease of verification */
|
||
static int detect_utf8_character(const unsigned char *p_read, unsigned int length) {
|
||
int unicode_len = 0;
|
||
unsigned int d = 0;
|
||
unsigned char c;
|
||
|
||
if (p_read == NULL) return UNICODE_ERROR_DECODING_ERROR;
|
||
c = *p_read;
|
||
|
||
/* If first byte begins with binary 0 it is single byte encoding */
|
||
if ((c & 0x80) == 0) {
|
||
/* single byte unicode (7 bit ASCII equivilent) has no validation */
|
||
return 1;
|
||
}
|
||
/* If first byte begins with binary 110 it is two byte encoding*/
|
||
else if ((c & 0xE0) == 0xC0) {
|
||
/* check we have at least two bytes */
|
||
if (length < 2) unicode_len = UNICODE_ERROR_CHARACTERS_MISSING;
|
||
/* check second byte starts with binary 10 */
|
||
else if (((*(p_read + 1)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING;
|
||
else {
|
||
unicode_len = 2;
|
||
/* compute character number */
|
||
d = ((c & 0x1F) << 6) | (*(p_read + 1) & 0x3F);
|
||
}
|
||
}
|
||
/* If first byte begins with binary 1110 it is three byte encoding */
|
||
else if ((c & 0xF0) == 0xE0) {
|
||
/* check we have at least three bytes */
|
||
if (length < 3) unicode_len = UNICODE_ERROR_CHARACTERS_MISSING;
|
||
/* check second byte starts with binary 10 */
|
||
else if (((*(p_read + 1)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING;
|
||
/* check third byte starts with binary 10 */
|
||
else if (((*(p_read + 2)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING;
|
||
else {
|
||
unicode_len = 3;
|
||
/* compute character number */
|
||
d = ((c & 0x0F) << 12) | ((*(p_read + 1) & 0x3F) << 6) | (*(p_read + 2) & 0x3F);
|
||
}
|
||
}
|
||
/* If first byte begins with binary 11110 it is four byte encoding */
|
||
else if ((c & 0xF8) == 0xF0) {
|
||
/* restrict characters to UTF-8 range (U+0000 - U+10FFFF)*/
|
||
if (c >= 0xF5) {
|
||
return UNICODE_ERROR_RESTRICTED_CHARACTER;
|
||
}
|
||
/* check we have at least four bytes */
|
||
if (length < 4) unicode_len = UNICODE_ERROR_CHARACTERS_MISSING;
|
||
/* check second byte starts with binary 10 */
|
||
else if (((*(p_read + 1)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING;
|
||
/* check third byte starts with binary 10 */
|
||
else if (((*(p_read + 2)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING;
|
||
/* check forth byte starts with binary 10 */
|
||
else if (((*(p_read + 3)) & 0xC0) != 0x80) unicode_len = UNICODE_ERROR_INVALID_ENCODING;
|
||
else {
|
||
unicode_len = 4;
|
||
/* compute character number */
|
||
d = ((c & 0x07) << 18) | ((*(p_read + 1) & 0x3F) << 12) | ((*(p_read + 2) & 0x3F) < 6) | (*(p_read + 3) & 0x3F);
|
||
}
|
||
}
|
||
/* any other first byte is invalid (RFC 3629) */
|
||
else {
|
||
return UNICODE_ERROR_INVALID_ENCODING;
|
||
}
|
||
|
||
/* invalid UTF-8 character number range (RFC 3629) */
|
||
if ((d >= 0xD800) && (d <= 0xDFFF)) {
|
||
return UNICODE_ERROR_RESTRICTED_CHARACTER;
|
||
}
|
||
|
||
/* check for overlong */
|
||
if ((unicode_len == 4) && (d < 0x010000)) {
|
||
/* four byte could be represented with less bytes */
|
||
return UNICODE_ERROR_OVERLONG_CHARACTER;
|
||
}
|
||
else if ((unicode_len == 3) && (d < 0x0800)) {
|
||
/* three byte could be represented with less bytes */
|
||
return UNICODE_ERROR_OVERLONG_CHARACTER;
|
||
}
|
||
else if ((unicode_len == 2) && (d < 0x80)) {
|
||
/* two byte could be represented with less bytes */
|
||
return UNICODE_ERROR_OVERLONG_CHARACTER;
|
||
}
|
||
|
||
return unicode_len;
|
||
}
|
||
|
||
static int msre_op_validateUtf8Encoding_execute(modsec_rec *msr, msre_rule *rule, msre_var *var,
|
||
char **error_msg)
|
||
{
|
||
unsigned int i, bytes_left;
|
||
|
||
bytes_left = var->value_len;
|
||
|
||
for(i = 0; i < var->value_len;) {
|
||
int rc = detect_utf8_character((unsigned char *)&var->value[i], bytes_left);
|
||
|
||
switch(rc) {
|
||
case UNICODE_ERROR_CHARACTERS_MISSING :
|
||
*error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: "
|
||
"not enough bytes in character "
|
||
"at %s. [offset \"%d\"]", var->name, i);
|
||
return 1;
|
||
break;
|
||
case UNICODE_ERROR_INVALID_ENCODING :
|
||
*error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: "
|
||
"invalid byte value in character "
|
||
"at %s. [offset \"%d\"]", var->name, i);
|
||
return 1;
|
||
break;
|
||
case UNICODE_ERROR_OVERLONG_CHARACTER :
|
||
*error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: "
|
||
"overlong character detected "
|
||
"at %s. [offset \"%d\"]", var->name, i);
|
||
return 1;
|
||
break;
|
||
case UNICODE_ERROR_RESTRICTED_CHARACTER :
|
||
*error_msg = apr_psprintf(msr->mp, "Invalid UTF-8 encoding: "
|
||
"use of restricted character "
|
||
"at %s. [offset \"%d\"]", var->name, i);
|
||
return 1;
|
||
break;
|
||
case UNICODE_ERROR_DECODING_ERROR :
|
||
*error_msg = apr_psprintf(msr->mp, "Error validating UTF-8 decoding "
|
||
"at %s. [offset \"%d\"]", var->name, i);
|
||
return 1;
|
||
break;
|
||
}
|
||
|
||
if (rc <= 0) {
|
||
*error_msg = apr_psprintf(msr->mp, "Internal error during UTF-8 validation "
|
||
"at %s.", var->name);
|
||
return 1;
|
||
}
|
||
|
||
i += rc;
|
||
bytes_left -= rc;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* eq */
|
||
|
||
static int msre_op_eq_execute(modsec_rec *msr, msre_rule *rule, msre_var *var,
|
||
char **error_msg)
|
||
{
|
||
msc_string str;
|
||
int left, right;
|
||
char *target = NULL;
|
||
|
||
if (error_msg == NULL) return -1;
|
||
*error_msg = NULL;
|
||
|
||
if ((var->value == NULL)||(rule->op_param == NULL)) {
|
||
/* NULL values do not match anything. */
|
||
return 0;
|
||
}
|
||
|
||
str.value = (char *)rule->op_param;
|
||
str.value_len = strlen(str.value);
|
||
|
||
expand_macros(msr, &str, rule, msr->mp);
|
||
|
||
target = apr_pstrmemdup(msr->mp, var->value, var->value_len);
|
||
if (target == NULL) return -1;
|
||
left = atoi(target);
|
||
right = atoi(str.value);
|
||
|
||
if (left != right) {
|
||
/* No match. */
|
||
return 0;
|
||
}
|
||
else {
|
||
*error_msg = apr_psprintf(msr->mp, "Operator EQ matched %d at %s.", right, var->name);
|
||
/* Match. */
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
/* gt */
|
||
|
||
static int msre_op_gt_execute(modsec_rec *msr, msre_rule *rule, msre_var *var,
|
||
char **error_msg)
|
||
{
|
||
msc_string str;
|
||
int left, right;
|
||
char *target = NULL;
|
||
|
||
if ((var->value == NULL)||(rule->op_param == NULL)) {
|
||
/* NULL values do not match anything. */
|
||
return 0;
|
||
}
|
||
|
||
if (error_msg == NULL) return -1;
|
||
*error_msg = NULL;
|
||
|
||
if ((var->value == NULL)||(rule->op_param == NULL)) {
|
||
/* NULL values do not match anything. */
|
||
return 0;
|
||
}
|
||
|
||
str.value = (char *)rule->op_param;
|
||
str.value_len = strlen(str.value);
|
||
|
||
expand_macros(msr, &str, rule, msr->mp);
|
||
|
||
target = apr_pstrmemdup(msr->mp, var->value, var->value_len);
|
||
if (target == NULL) return -1;
|
||
left = atoi(target);
|
||
right = atoi(str.value);
|
||
|
||
if (left <= right) {
|
||
/* No match. */
|
||
return 0;
|
||
}
|
||
else {
|
||
*error_msg = apr_psprintf(msr->mp, "Operator GT matched %d at %s.", right, var->name);
|
||
/* Match. */
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
/* lt */
|
||
|
||
static int msre_op_lt_execute(modsec_rec *msr, msre_rule *rule, msre_var *var,
|
||
char **error_msg)
|
||
{
|
||
msc_string str;
|
||
int left, right;
|
||
char *target = NULL;
|
||
|
||
if ((var->value == NULL)||(rule->op_param == NULL)) {
|
||
/* NULL values do not match anything. */
|
||
return 0;
|
||
}
|
||
|
||
if (error_msg == NULL) return -1;
|
||
*error_msg = NULL;
|
||
|
||
if ((var->value == NULL)||(rule->op_param == NULL)) {
|
||
/* NULL values do not match anything. */
|
||
return 0;
|
||
}
|
||
|
||
str.value = (char *)rule->op_param;
|
||
str.value_len = strlen(str.value);
|
||
|
||
expand_macros(msr, &str, rule, msr->mp);
|
||
|
||
target = apr_pstrmemdup(msr->mp, var->value, var->value_len);
|
||
if (target == NULL) return -1;
|
||
left = atoi(target);
|
||
right = atoi(str.value);
|
||
|
||
if (left >= right) {
|
||
/* No match. */
|
||
return 0;
|
||
}
|
||
else {
|
||
*error_msg = apr_psprintf(msr->mp, "Operator LT matched %d at %s.", right, var->name);
|
||
/* Match. */
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
/* ge */
|
||
|
||
static int msre_op_ge_execute(modsec_rec *msr, msre_rule *rule, msre_var *var,
|
||
char **error_msg)
|
||
{
|
||
msc_string str;
|
||
int left, right;
|
||
char *target = NULL;
|
||
|
||
if ((var->value == NULL)||(rule->op_param == NULL)) {
|
||
/* NULL values do not match anything. */
|
||
return 0;
|
||
}
|
||
|
||
if (error_msg == NULL) return -1;
|
||
*error_msg = NULL;
|
||
|
||
if ((var->value == NULL)||(rule->op_param == NULL)) {
|
||
/* NULL values do not match anything. */
|
||
return 0;
|
||
}
|
||
|
||
str.value = (char *)rule->op_param;
|
||
str.value_len = strlen(str.value);
|
||
|
||
expand_macros(msr, &str, rule, msr->mp);
|
||
|
||
target = apr_pstrmemdup(msr->mp, var->value, var->value_len);
|
||
if (target == NULL) return -1;
|
||
left = atoi(target);
|
||
right = atoi(str.value);
|
||
|
||
if (left < right) {
|
||
/* No match. */
|
||
return 0;
|
||
}
|
||
else {
|
||
*error_msg = apr_psprintf(msr->mp, "Operator GE matched %d at %s.", right, var->name);
|
||
/* Match. */
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
/* le */
|
||
|
||
static int msre_op_le_execute(modsec_rec *msr, msre_rule *rule, msre_var *var,
|
||
char **error_msg)
|
||
{
|
||
msc_string str;
|
||
int left, right;
|
||
char *target = NULL;
|
||
|
||
if ((var->value == NULL)||(rule->op_param == NULL)) {
|
||
/* NULL values do not match anything. */
|
||
return 0;
|
||
}
|
||
|
||
if (error_msg == NULL) return -1;
|
||
*error_msg = NULL;
|
||
|
||
if ((var->value == NULL)||(rule->op_param == NULL)) {
|
||
/* NULL values do not match anything. */
|
||
return 0;
|
||
}
|
||
|
||
str.value = (char *)rule->op_param;
|
||
str.value_len = strlen(str.value);
|
||
|
||
expand_macros(msr, &str, rule, msr->mp);
|
||
|
||
target = apr_pstrmemdup(msr->mp, var->value, var->value_len);
|
||
if (target == NULL) return -1;
|
||
left = atoi(target);
|
||
right = atoi(str.value);
|
||
|
||
if (left > right) {
|
||
/* No match. */
|
||
return 0;
|
||
}
|
||
else {
|
||
*error_msg = apr_psprintf(msr->mp, "Operator LE matched %d at %s.", right, var->name);
|
||
/* Match. */
|
||
return 1;
|
||
}
|
||
}
|
||
|
||
/* -------------------------------------------------------------------------- */
|
||
|
||
/**
|
||
*
|
||
*/
|
||
void msre_engine_register_default_operators(msre_engine *engine) {
|
||
/* unconditionalMatch */
|
||
msre_engine_op_register(engine,
|
||
"unconditionalMatch",
|
||
NULL,
|
||
msre_op_unconditionalmatch_execute
|
||
);
|
||
|
||
/* noMatch */
|
||
msre_engine_op_register(engine,
|
||
"noMatch",
|
||
NULL,
|
||
msre_op_nomatch_execute
|
||
);
|
||
|
||
/* ipmatch */
|
||
msre_engine_op_register(engine,
|
||
"ipmatch",
|
||
msre_op_ipmatch_param_init,
|
||
msre_op_ipmatch_execute
|
||
);
|
||
|
||
/* ipmatchFromFile */
|
||
msre_engine_op_register(engine,
|
||
"ipmatchFromFile",
|
||
msre_op_ipmatchFromFile_param_init,
|
||
msre_op_ipmatchFromFile_execute
|
||
);
|
||
/* ipmatchf */
|
||
msre_engine_op_register(engine,
|
||
"ipmatchf",
|
||
msre_op_ipmatchFromFile_param_init,
|
||
msre_op_ipmatchFromFile_execute
|
||
);
|
||
|
||
/* rsub */
|
||
#if !defined(MSC_TEST)
|
||
msre_engine_op_register(engine,
|
||
"rsub",
|
||
msre_op_rsub_param_init,
|
||
msre_op_rsub_execute
|
||
);
|
||
#endif /* MSC_TEST */
|
||
|
||
/* rx */
|
||
msre_engine_op_register(engine,
|
||
"rx",
|
||
msre_op_rx_param_init,
|
||
msre_op_rx_execute
|
||
);
|
||
|
||
/* validateEncyption */
|
||
msre_engine_op_register(engine,
|
||
"validateHash",
|
||
msre_op_validateHash_param_init,
|
||
msre_op_validateHash_execute
|
||
);
|
||
|
||
/* pm */
|
||
msre_engine_op_register(engine,
|
||
"pm",
|
||
msre_op_pm_param_init,
|
||
msre_op_pm_execute
|
||
);
|
||
|
||
/* pmFromFile */
|
||
msre_engine_op_register(engine,
|
||
"pmFromFile",
|
||
msre_op_pmFromFile_param_init,
|
||
msre_op_pm_execute
|
||
);
|
||
|
||
/* pmf */
|
||
msre_engine_op_register(engine,
|
||
"pmf",
|
||
msre_op_pmFromFile_param_init,
|
||
msre_op_pm_execute
|
||
);
|
||
|
||
/* within */
|
||
msre_engine_op_register(engine,
|
||
"within",
|
||
NULL, /* ENH init function to flag var substitution */
|
||
msre_op_within_execute
|
||
);
|
||
|
||
/* contains */
|
||
msre_engine_op_register(engine,
|
||
"contains",
|
||
NULL, /* ENH init function to flag var substitution */
|
||
msre_op_contains_execute
|
||
);
|
||
|
||
/* containsWord */
|
||
msre_engine_op_register(engine,
|
||
"containsWord",
|
||
NULL, /* ENH init function to flag var substitution */
|
||
msre_op_containsWord_execute
|
||
);
|
||
|
||
/* detectSQLi */
|
||
msre_engine_op_register(engine,
|
||
"detectSQLi",
|
||
NULL,
|
||
msre_op_detectSQLi_execute
|
||
);
|
||
|
||
/* detectXSS */
|
||
msre_engine_op_register(engine,
|
||
"detectXSS",
|
||
NULL,
|
||
msre_op_detectXSS_execute
|
||
);
|
||
|
||
/* streq */
|
||
msre_engine_op_register(engine,
|
||
"streq",
|
||
NULL, /* ENH init function to flag var substitution */
|
||
msre_op_streq_execute
|
||
);
|
||
|
||
/* beginsWith */
|
||
msre_engine_op_register(engine,
|
||
"beginsWith",
|
||
NULL, /* ENH init function to flag var substitution */
|
||
msre_op_beginsWith_execute
|
||
);
|
||
|
||
/* endsWith */
|
||
msre_engine_op_register(engine,
|
||
"endsWith",
|
||
NULL, /* ENH init function to flag var substitution */
|
||
msre_op_endsWith_execute
|
||
);
|
||
|
||
/* strmatch */
|
||
msre_engine_op_register(engine,
|
||
"strmatch",
|
||
msre_op_strmatch_param_init,
|
||
msre_op_strmatch_execute
|
||
);
|
||
|
||
/* validateDTD */
|
||
msre_engine_op_register(engine,
|
||
"validateDTD",
|
||
msre_op_validateDTD_init,
|
||
msre_op_validateDTD_execute
|
||
);
|
||
|
||
/* validateSchema */
|
||
msre_engine_op_register(engine,
|
||
"validateSchema",
|
||
msre_op_validateSchema_init,
|
||
msre_op_validateSchema_execute
|
||
);
|
||
|
||
/* verifyCC */
|
||
msre_engine_op_register(engine,
|
||
"verifyCC",
|
||
msre_op_verifyCC_init,
|
||
msre_op_verifyCC_execute
|
||
);
|
||
|
||
/* verifyCPF */
|
||
msre_engine_op_register(engine,
|
||
"verifyCPF",
|
||
msre_op_verifyCPF_init,
|
||
msre_op_verifyCPF_execute
|
||
);
|
||
|
||
/* verifySSN */
|
||
msre_engine_op_register(engine,
|
||
"verifySSN",
|
||
msre_op_verifySSN_init,
|
||
msre_op_verifySSN_execute
|
||
);
|
||
|
||
/* geoLookup */
|
||
msre_engine_op_register(engine,
|
||
"geoLookup",
|
||
NULL,
|
||
msre_op_geoLookup_execute
|
||
);
|
||
|
||
/* gsbLookup */
|
||
msre_engine_op_register(engine,
|
||
"gsbLookup",
|
||
msre_op_gsbLookup_param_init,
|
||
msre_op_gsbLookup_execute
|
||
);
|
||
|
||
/* rbl */
|
||
msre_engine_op_register(engine,
|
||
"rbl",
|
||
NULL, /* ENH init function to validate DNS server */
|
||
msre_op_rbl_execute
|
||
);
|
||
|
||
/* inspectFile */
|
||
msre_engine_op_register(engine,
|
||
"inspectFile",
|
||
msre_op_inspectFile_init,
|
||
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",
|
||
msre_op_validateByteRange_init,
|
||
msre_op_validateByteRange_execute
|
||
);
|
||
|
||
/* validateUrlEncoding */
|
||
msre_engine_op_register(engine,
|
||
"validateUrlEncoding",
|
||
NULL,
|
||
msre_op_validateUrlEncoding_execute
|
||
);
|
||
|
||
/* validateUtf8Encoding */
|
||
msre_engine_op_register(engine,
|
||
"validateUtf8Encoding",
|
||
NULL,
|
||
msre_op_validateUtf8Encoding_execute
|
||
);
|
||
|
||
/* eq */
|
||
msre_engine_op_register(engine,
|
||
"eq",
|
||
NULL,
|
||
msre_op_eq_execute
|
||
);
|
||
|
||
/* gt */
|
||
msre_engine_op_register(engine,
|
||
"gt",
|
||
NULL,
|
||
msre_op_gt_execute
|
||
);
|
||
|
||
/* lt */
|
||
msre_engine_op_register(engine,
|
||
"lt",
|
||
NULL,
|
||
msre_op_lt_execute
|
||
);
|
||
|
||
/* le */
|
||
msre_engine_op_register(engine,
|
||
"le",
|
||
NULL,
|
||
msre_op_le_execute
|
||
);
|
||
|
||
/* ge */
|
||
msre_engine_op_register(engine,
|
||
"ge",
|
||
NULL,
|
||
msre_op_ge_execute
|
||
);
|
||
}
|