#include "framework/cursor.h"
#include "framework/ips_option.h"
#include "framework/module.h"
+#include "framework/parameter.h"
#include "hash/hashfcn.h"
#include "helpers/scratch_allocator.h"
#include "log/messages.h"
#include "main/snort_config.h"
+#include "managers/ips_manager.h"
+#include "managers/module_manager.h"
#include "profiler/profiler.h"
#include "utils/util.h"
#define SNORT_OVERRIDE_MATCH_LIMIT 0x00080 // Override default limits on match & match recursion
#define s_name "pcre"
+#define mod_regex_name "regex"
struct PcreData
{
}
return MATCH;
}
+
return NO_MATCH;
}
{ nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
};
+struct PcreStats
+{
+ PegCount pcre_rules;
+ PegCount pcre_to_hyper;
+ PegCount pcre_native;
+ PegCount pcre_negated;
+};
+
+const PegInfo pcre_pegs[] =
+{
+ { CountType::SUM, "pcre_rules", "total rules processed with pcre option" },
+ { CountType::SUM, "pcre_to_hyper", "total pcre rules by hyperscan engine" },
+ { CountType::SUM, "pcre_native", "total pcre rules compiled by pcre engine" },
+ { CountType::SUM, "pcre_negated", "total pcre rules using negation syntax" },
+ { CountType::END, nullptr, nullptr }
+};
+
+PcreStats pcre_stats;
+
#define s_help \
"rule option for matching payload data with pcre"
bool begin(const char*, int, SnortConfig*) override;
bool set(const char*, Value&, SnortConfig*) override;
+ bool end(const char*, int, SnortConfig*) override;
ProfileStats* get_profile() const override
{ return &pcrePerfStats; }
+ const PegInfo* get_pegs() const override;
+ PegCount* get_counts() const override;
+
PcreData* get_data();
Usage get_usage() const override
{ return DETECT; }
+ void get_mod_regex_instance(const char* name, int v, SnortConfig* sc);
+ Module* get_mod_regex() const
+ { return mod_regex; }
+
private:
PcreData* data;
+ Module* mod_regex = nullptr;
+ std::string re;
+
static bool scratch_setup(SnortConfig* sc);
static void scratch_cleanup(SnortConfig* sc);
};
return tmp;
}
-bool PcreModule::begin(const char*, int, SnortConfig*)
+const PegInfo* PcreModule::get_pegs() const
+{ return pcre_pegs; }
+
+PegCount* PcreModule::get_counts() const
+{ return (PegCount*)&pcre_stats; }
+
+void PcreModule::get_mod_regex_instance(const char* name, int v, SnortConfig* sc)
+{
+ if ( sc->pcre_to_regex )
+ {
+ if ( !mod_regex )
+ mod_regex = ModuleManager::get_module(mod_regex_name);
+
+ if( mod_regex )
+ mod_regex = mod_regex->begin(name, v, sc) ? mod_regex : nullptr;
+ }
+}
+
+bool PcreModule::begin(const char* name, int v, SnortConfig* sc)
{
- data = (PcreData*)snort_calloc(sizeof(*data));
+ get_mod_regex_instance(name, v, sc);
return true;
}
-bool PcreModule::set(const char*, Value& v, SnortConfig* sc)
+bool PcreModule::set(const char* name, Value& v, SnortConfig* sc)
{
if ( v.is("~re") )
- pcre_parse(sc, v.get_string(), data);
+ {
+ re = v.get_string();
+ if( mod_regex )
+ mod_regex = mod_regex->set(name, v, sc) ? mod_regex : nullptr;
+ }
else
return false;
return true;
}
+bool PcreModule::end(const char* name, int v, SnortConfig* sc)
+{
+ if( mod_regex )
+ mod_regex = mod_regex->end(name, v, sc) ? mod_regex : nullptr;
+
+ if ( !mod_regex )
+ {
+ data = (PcreData*)snort_calloc(sizeof(*data));
+ pcre_parse(sc, re.c_str(), data);
+ }
+
+ return true;
+}
+
bool PcreModule::scratch_setup(SnortConfig* sc)
{
if ( s_ovector_max < 0 )
//-------------------------------------------------------------------------
static Module* mod_ctor()
-{
- return new PcreModule;
-}
+{ return new PcreModule; }
static void mod_dtor(Module* m)
-{
- delete m;
-}
+{ delete m; }
-static IpsOption* pcre_ctor(Module* p, OptTreeNode*)
+static IpsOption* pcre_ctor(Module* p, OptTreeNode* otn)
{
+ pcre_stats.pcre_rules++;
PcreModule* m = (PcreModule*)p;
- PcreData* d = m->get_data();
- return new PcreOption(d);
+
+ Module* mod_regex = m->get_mod_regex();
+ if ( mod_regex )
+ {
+ pcre_stats.pcre_to_hyper++;
+ const IpsApi* opt_api = IpsManager::get_option_api(mod_regex_name);
+ return opt_api->ctor(mod_regex, otn);
+ }
+ else
+ {
+ pcre_stats.pcre_native++;
+ PcreData* d = m->get_data();
+ return new PcreOption(d);
+ }
}
static void pcre_dtor(IpsOption* p)
-{
- delete p;
-}
+{ delete p; }
static const IpsApi pcre_api =
{
hs_database_t* db;
std::string re;
PatternMatchData pmd;
+ bool pcre_conversion;
RegexConfig()
{ reset(); }
memset(&pmd, 0, sizeof(pmd));
re.clear();
db = nullptr;
+ pcre_conversion = false;
}
};
// see ContentOption::operator==()
bool RegexOption::operator==(const IpsOption& ips) const
{
-#if 0
if ( !IpsOption::operator==(ips) )
return false;
- RegexOption& rhs = (RegexOption&)ips;
+ const RegexOption& rhs = (const RegexOption&)ips;
- if ( config.re == rhs.config.re and
- config.pmd.flags == rhs.config.pmd.flags and
- config.pmd.mpse_flags == rhs.config.pmd.mpse_flags )
- return true;
-#endif
- return this == &ips;
+ if ( config.pcre_conversion && rhs.config.pcre_conversion )
+ if ( config.re == rhs.config.re and
+ config.pmd.flags == rhs.config.pmd.flags and
+ config.pmd.mpse_flags == rhs.config.pmd.mpse_flags )
+ return true;
+
+ return false;
}
struct ScanContext
}
bool RegexOption::retry(Cursor&)
-{
- return !is_relative();
-}
+{ return !is_relative(); }
//-------------------------------------------------------------------------
// module
private:
RegexConfig config;
+ bool convert_pcre_to_regex_form();
};
RegexModule::~RegexModule()
delete scratcher;
}
-bool RegexModule::begin(const char*, int, SnortConfig*)
+bool RegexModule::begin(const char* name, int, SnortConfig*)
{
config.reset();
config.pmd.flags |= PatternMatchData::NO_FP;
config.pmd.mpse_flags |= HS_FLAG_SINGLEMATCH;
+
+ // if regex is in pcre syntax set conversion mode
+ if ( strcmp(name, "pcre") == 0 )
+ config.pcre_conversion = true;
+
return true;
}
+// The regex string received from the ips_pcre plugin must be scrubbed to remove
+// two characters from the front; an extra '"' and the '/' and also the same
+// two characters from the end of the string as well as any pcre modifier flags
+// included in the expression. The modifier flags are checked to set the
+// corresponding hyperscan regex engine flags.
+// Hyperscan regex also does not support negated pcre expression so negated expression
+// are not converted and will be compiled by the pcre engine.
+bool RegexModule::convert_pcre_to_regex_form()
+{
+ size_t pos = config.re.find_first_of("\"!");
+ if (config.re[pos] == '!')
+ return false;
+
+ config.re.erase(0,2);
+ std::size_t re_end = config.re.rfind("/");
+ if ( re_end != std::string::npos )
+ {
+ std::size_t mod_len = (config.re.length() - 2) - re_end;
+ std::string modifiers = config.re.substr(re_end + 1, mod_len);
+ std::size_t erase_len = config.re.length() - re_end;
+ config.re.erase(re_end, erase_len);
+
+ for( char& c : modifiers )
+ {
+ switch (c)
+ {
+ case 'i':
+ config.pmd.mpse_flags |= HS_FLAG_CASELESS;
+ config.pmd.set_no_case();
+ break;
+
+ case 'm':
+ config.pmd.mpse_flags |= HS_FLAG_MULTILINE;
+ break;
+
+ case 's':
+ config.pmd.mpse_flags |= HS_FLAG_DOTALL;
+ break;
+
+ case 'O':
+ break;
+
+ case 'R':
+ config.pmd.set_relative();
+ break;
+
+ default:
+ return false;
+ break;
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
bool RegexModule::set(const char*, Value& v, SnortConfig*)
{
+ bool valid_opt = true;
+
if ( v.is("~re") )
{
config.re = v.get_string();
- // remove quotes
- config.re.erase(0, 1);
- config.re.erase(config.re.length()-1, 1);
+
+ if ( config.pcre_conversion )
+ valid_opt = convert_pcre_to_regex_form();
+ else
+ {
+ // remove quotes
+ config.re.erase(0, 1);
+ config.re.erase(config.re.length() - 1, 1);
+ }
}
else if ( v.is("dotall") )
config.pmd.mpse_flags |= HS_FLAG_DOTALL;
-
else if ( v.is("fast_pattern") )
{
config.pmd.flags &= ~PatternMatchData::NO_FP;
}
else if ( v.is("multiline") )
config.pmd.mpse_flags |= HS_FLAG_MULTILINE;
-
else if ( v.is("nocase") )
{
config.pmd.mpse_flags |= HS_FLAG_CASELESS;
}
else if ( v.is("relative") )
config.pmd.set_relative();
-
else
- return false;
+ valid_opt = false;
- return true;
+ return valid_opt;
}
bool RegexModule::end(const char*, int, SnortConfig*)
if ( hs_compile(config.re.c_str(), config.pmd.mpse_flags, HS_MODE_BLOCK,
nullptr, &config.db, &err) or !config.db )
{
- ParseError("can't compile regex '%s'", config.re.c_str());
+ if ( !config.pcre_conversion )
+ ParseError("can't compile regex '%s'", config.re.c_str());
hs_free_compile_error(err);
return false;
}
{
IpsOption* opt2 = get_option(mod, "bar");
CHECK(opt2);
- CHECK(*opt != *opt2);
uint32_t h1 = opt->hash();
uint32_t h2 = opt2->hash();
{
IpsOption* opt2 = get_option(mod, " foo ");
CHECK(opt2);
- // this is forced unequal for now
- CHECK(*opt != *opt2);
do_cleanup = scratcher->setup(snort_conf);
{ "pcre_override", Parameter::PT_BOOL, nullptr, "true",
"enable pcre match limit overrides when pattern matching (ie ignore /O)" },
+ { "pcre_to_regex", Parameter::PT_BOOL, nullptr, "false",
+ "disable pcre pattern matching" },
+
{ "enable_address_anomaly_checks", Parameter::PT_BOOL, nullptr, "false",
"enable check and alerting of address anomalies" },
else if ( v.is("pcre_override") )
sc->pcre_override = v.get_bool();
+
+ else if ( v.is("pcre_to_regex") )
+ sc->pcre_to_regex = v.get_bool();
else if ( v.is("enable_address_anomaly_checks") )
sc->address_anomaly_check_enabled = v.get_bool();
bool global_rule_state = false;
bool global_default_rule_state = true;
+ bool pcre_to_regex = false;
//------------------------------------------------------
// process stuff
return current_keyword.c_str();
}
+const IpsApi* IpsManager::get_option_api(const char* keyword)
+{
+ Option* opt = get_opt(keyword);
+ if ( opt )
+ return opt->api;
+ else
+ return nullptr;
+}
+
bool IpsManager::option_begin(
SnortConfig* sc, const char* key, SnortProtocolId)
{
static void add_plugin(const snort::IpsApi*);
static void dump_plugins();
static void release_plugins();
-
static void instantiate(const snort::IpsApi*, snort::Module*, snort::SnortConfig*);
-
- static bool get_option(
- snort::SnortConfig*, struct OptTreeNode*, SnortProtocolId,
- const char* keyword, char* args, snort::RuleOptType&);
-
static bool option_begin(snort::SnortConfig*, const char* key, SnortProtocolId);
static bool option_set(
snort::SnortConfig*, const char* key, const char* opt, const char* val);
static bool option_end(
snort::SnortConfig*, OptTreeNode*, SnortProtocolId, const char* key, snort::RuleOptType&);
-
static void delete_option(snort::IpsOption*);
static const char* get_option_keyword();
-
+ SO_PUBLIC static const snort::IpsApi* get_option_api(const char* keyword);
static void global_init(snort::SnortConfig*);
static void global_term(snort::SnortConfig*);
-
static void reset_options();
static void setup_options();
static void clear_options();
config = nullptr;
}
-
const RuleMap* StreamTcpModule::get_rules() const
{ return stream_tcp_rules; }