From: Michael Altizer (mialtize) Date: Fri, 29 Mar 2019 15:32:49 +0000 (-0400) Subject: Merge pull request #1545 in SNORT/snort3 from ~CWAXMAN/snort3:rule_state to master X-Git-Tag: 3.0.0-251~6 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=192cdfc83d71bfdc1433f56117cc5c83b6982f4b;p=thirdparty%2Fsnort3.git Merge pull request #1545 in SNORT/snort3 from ~CWAXMAN/snort3:rule_state to master Squashed commit of the following: commit 323e859c920a3edbb522200a408a47aaabb74e34 Author: Carter Waxman Date: Tue Mar 12 15:21:40 2019 -0400 detection, snort2lua: added global rule state options for legacy conversions commit b5cb6f3f9a17fb2df26c86475e305946edaaef5c Author: Carter Waxman Date: Fri Mar 8 15:36:25 2019 -0500 detection: fixed incorrect log messages commit eb438448160d41867d5e68a890cea627a04c88fb Author: Carter Waxman Date: Tue Feb 26 08:28:52 2019 -0500 rule_state: added default rule state to ips policy commit 6eec505eb1af7357584eb7a18a49fde409b5e1a3 Author: Carter Waxman Date: Mon Feb 25 15:41:08 2019 -0500 rule_state: add rtn but disable if block is set on non-inline deployment commit 52b20be073639ba0f1b75a0943c6b595f81b7318 Author: Carter Waxman Date: Mon Feb 18 12:27:48 2019 -0500 rule_state: added per-ips-policy rule states --- diff --git a/src/actions/actions.h b/src/actions/actions.h index 75ea1ce02..8d8b89625 100644 --- a/src/actions/actions.h +++ b/src/actions/actions.h @@ -35,7 +35,7 @@ class SO_PUBLIC Actions { public: // FIXIT-L if Type is changed, RateFilterModule and type in actions.cc must be updated - enum Type + enum Type : uint8_t { NONE = 0, LOG, PASS, ALERT, DROP, BLOCK, RESET, MAX }; static const char* get_string(Type); diff --git a/src/detection/CMakeLists.txt b/src/detection/CMakeLists.txt index 4620d7e84..a6d88da14 100644 --- a/src/detection/CMakeLists.txt +++ b/src/detection/CMakeLists.txt @@ -42,6 +42,7 @@ add_library (detection OBJECT regex_offload.cc rtn_checks.cc rtn_checks.h + rules.cc service_map.cc service_map.h sfrim.cc diff --git a/src/detection/fp_create.cc b/src/detection/fp_create.cc index b3c5e4a2a..c75320108 100644 --- a/src/detection/fp_create.cc +++ b/src/detection/fp_create.cc @@ -522,8 +522,7 @@ static int fpAddPortGroupRule( if ( otn->sigInfo.builtin ) return -1; - /* Rule not enabled */ - if ( !otn->enabled ) + if ( !otn->enabled_somewhere() ) return -1; search_api = fp->get_search_api(); @@ -1294,7 +1293,7 @@ static int fpCreatePortGroups(SnortConfig* sc, RulePortTables* p) if (fpCreatePortTablePortGroups(sc, p->udp.dst, add_any_any)) { - LogMessage("fpCreatePorTablePortGroups failed-udp.src\n"); + LogMessage("fpCreatePorTablePortGroups failed-udp.dst\n"); return -1; } @@ -1303,7 +1302,7 @@ static int fpCreatePortGroups(SnortConfig* sc, RulePortTables* p) if (fpCreatePortObject2PortGroup(sc, po2, nullptr)) { - LogMessage("fpCreatePorTablePortGroups failed-udp.src\n"); + LogMessage("fpCreatePorTablePortGroups failed-udp.any\n"); return -1; } @@ -1773,13 +1772,13 @@ void fpDeleteFastPacketDetection(SnortConfig* sc) static void print_nfp_info(const char* group, OptTreeNode* otn) { - if ( otn->warned_fp ) + if ( otn->warned_fp() ) return; ParseWarning(WARN_RULES, "%s rule %u:%u:%u has no fast pattern", group, otn->sigInfo.gid, otn->sigInfo.sid, otn->sigInfo.rev); - otn->warned_fp = true; + otn->set_warned_fp(); } void get_pattern_info(const PatternMatchData* pmd, diff --git a/src/detection/fp_detect.cc b/src/detection/fp_detect.cc index 672faa23e..5fd380f4f 100644 --- a/src/detection/fp_detect.cc +++ b/src/detection/fp_detect.cc @@ -139,10 +139,10 @@ int fpLogEvent(const RuleTreeNode* rtn, const OptTreeNode* otn, Packet* p) int action = -1, rateAction = -1; int override, filterEvent = 0; - if ( Actions::is_pass(rtn->type) ) + if ( Actions::is_pass(rtn->action) ) p->packet_flags |= PKT_PASS_RULE; - if ( otn->stateless ) + if ( otn->stateless() ) { /* Stateless rule, set the stateless bit */ p->packet_flags |= PKT_STATELESS; @@ -158,12 +158,12 @@ int fpLogEvent(const RuleTreeNode* rtn, const OptTreeNode* otn, Packet* p) if ((p->packet_flags & PKT_STREAM_UNEST_UNI) && SnortConfig::assure_established() && (!(p->packet_flags & PKT_REBUILT_STREAM)) && - (otn->stateless == 0)) + !otn->stateless() ) { // We still want to drop packets that are drop rules. // We just don't want to see the alert. - Actions::apply(rtn->type, p); - fpLogOther(p, rtn, otn, rtn->type); + Actions::apply(rtn->action, p); + fpLogOther(p, rtn, otn, rtn->action); return 1; } @@ -178,7 +178,7 @@ int fpLogEvent(const RuleTreeNode* rtn, const OptTreeNode* otn, Packet* p) { return 1; } - action = (rateAction < 0) ? (int)rtn->type : rateAction; + action = (rateAction < 0) ? (int)rtn->action : rateAction; // When rate filters kick in, event filters are still processed. // perform event filtering tests - impacts logging @@ -216,9 +216,9 @@ int fpLogEvent(const RuleTreeNode* rtn, const OptTreeNode* otn, Packet* p) * If its order is lower than 'pass', it should have been passed. * This is consistent with other detection rules */ if ( (p->packet_flags & PKT_PASS_RULE) - &&(SnortConfig::get_eval_index(rtn->type) > SnortConfig::get_eval_index(Actions::PASS))) + &&(SnortConfig::get_eval_index(rtn->action) > SnortConfig::get_eval_index(Actions::PASS))) { - fpLogOther(p, rtn, otn, rtn->type); + fpLogOther(p, rtn, otn, rtn->action); return 1; } @@ -677,7 +677,7 @@ static inline int fpFinalSelectEvent(OtnxMatchData* o, Packet* p) otn = o->matchInfo[i].MatchArray[j]; rtn = getRtnFromOtn(otn); - if (otn && rtn && Actions::is_pass(rtn->type)) + if (otn && rtn && Actions::is_pass(rtn->action)) { /* Already acted on rules, so just don't act on anymore */ if ( tcnt > 0 ) @@ -718,7 +718,7 @@ static inline int fpFinalSelectEvent(OtnxMatchData* o, Packet* p) } /* only log/count one pass */ - if ( otn && rtn && Actions::is_pass(rtn->type)) + if ( otn && rtn && Actions::is_pass(rtn->action)) { p->packet_flags |= PKT_PASS_RULE; return 1; diff --git a/src/detection/rtn_checks.cc b/src/detection/rtn_checks.cc index 04c393ac8..ca9c54bc2 100644 --- a/src/detection/rtn_checks.cc +++ b/src/detection/rtn_checks.cc @@ -61,9 +61,9 @@ static int CheckAddrPort(sfip_var_t* rule_addr, PortObject* po, Packet* p, pkt_port = p->ptrs.sp; if (mode & INVERSE) - any_port_flag = flags & ANY_DST_PORT; + any_port_flag = flags & RuleTreeNode::ANY_DST_PORT; else - any_port_flag = flags & ANY_SRC_PORT; + any_port_flag = flags & RuleTreeNode::ANY_SRC_PORT; } else { @@ -71,9 +71,9 @@ static int CheckAddrPort(sfip_var_t* rule_addr, PortObject* po, Packet* p, pkt_port = p->ptrs.dp; if (mode & INVERSE) - any_port_flag = flags & ANY_SRC_PORT; + any_port_flag = flags & RuleTreeNode::ANY_SRC_PORT; else - any_port_flag = flags & ANY_DST_PORT; + any_port_flag = flags & RuleTreeNode::ANY_DST_PORT; } if (!rule_addr) diff --git a/src/detection/rules.cc b/src/detection/rules.cc new file mode 100644 index 000000000..241e031cc --- /dev/null +++ b/src/detection/rules.cc @@ -0,0 +1,172 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2019-2019 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- + +// rules.cc author Carter Waxman + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "rules.h" + +#include "log/messages.h" +#include "hash/xhash.h" +#include "main/policy.h" +#include "main/snort_config.h" +#include "parser/parser.h" +#include "sfip/sf_ipvar.h" +#include "sfip/sf_vartable.h" + +#include "treenodes.h" + +#ifdef REG_TEST +#define ParseError(...) ParseWarning(WARN_RULES, __VA_ARGS__) +#endif + +using namespace snort; + +void RuleState::apply(SnortConfig* sc) +{ + OptTreeNode* otn = OtnLookup(sc->otn_map, gid, sid); + + if ( otn == nullptr ) + ParseError("Rule state specified for invalid SID: %u GID: %u", sid, gid); + else + { + if ( sc->global_rule_state ) + { + for ( unsigned i = 0; i < sc->policy_map->ips_policy_count(); i++ ) + { + if ( sc->policy_map->get_ips_policy(i) ) + apply(sc, otn, i); + } + } + else + apply(sc, otn, policy); + } +} + +void RuleState::apply(SnortConfig* sc, OptTreeNode* otn, unsigned ips_num) +{ + RuleTreeNode* rtn = getRtnFromOtn(otn, ips_num); + + if ( !rtn ) + return; + + if ( rtn->otnRefCount > 1 ) + { + // duplicate to avoid blanket setting behavior of multiple OTNs + rtn = find_updated_rtn(rtn, sc, ips_num); + if ( !rtn ) + { + rtn = dup_rtn(otn, sc, ips_num); + replace_rtn(otn, rtn, sc, ips_num); + } + + else if ( rtn != getRtnFromOtn(otn, ips_num) ) + replace_rtn(otn, rtn, sc, ips_num); + + update_rtn(getRtnFromOtn(otn, ips_num)); + } + else + { + RuleTreeNode* existing_rtn = find_updated_rtn(rtn, sc, ips_num); + + // dedup to avoid wasting memory when transitioning RTN to behavior of existing one + if ( existing_rtn ) + replace_rtn(otn, existing_rtn, sc, ips_num); + else + update_rtn(getRtnFromOtn(otn, ips_num)); + } +} + +RuleTreeNode* RuleState::find_updated_rtn(RuleTreeNode* rtn, SnortConfig* sc, unsigned ips_num) +{ + RuleTreeNode test_rtn(*rtn); + update_rtn(&test_rtn); + + RuleTreeNodeKey key { &test_rtn, ips_num }; + return (RuleTreeNode*)xhash_find(sc->rtn_hash_table, &key); +} + +void RuleState::replace_rtn(OptTreeNode* otn, RuleTreeNode* replacement, SnortConfig* sc, unsigned ips_num) +{ + RuleTreeNode* rtn = getRtnFromOtn(otn, ips_num); + rtn->otnRefCount--; + + deleteRtnFromOtn(otn, ips_num, sc, rtn->otnRefCount == 0); + addRtnToOtn(snort::SnortConfig::get_conf(), otn, replacement, ips_num); +} + +RuleTreeNode* RuleState::dup_rtn(OptTreeNode* otn, SnortConfig* sc, unsigned ips_num) +{ + RuleTreeNode* rtn = getRtnFromOtn(otn, ips_num); + RuleTreeNode* ret = new RuleTreeNode(*rtn); + ret->otnRefCount = 1; + + auto ip_vartable = sc->policy_map->get_ips_policy(ips_num)->ip_vartable; + + if ( rtn->sip ) + ret->sip = sfvt_lookup_var(ip_vartable, rtn->sip->name); + + if ( rtn->dip ) + ret->dip = sfvt_lookup_var(ip_vartable, rtn->dip->name); + + RuleFpList* from = rtn->rule_func; + + if ( from ) + { + RuleFpList* to = new RuleFpList(*from); + to->next = nullptr; + ret->rule_func = to; + + for ( from = from->next; from; from = from->next ) + { + to->next = new RuleFpList(*from); + to = to->next; + to->next = nullptr; + } + } + + return ret; +} + +void RuleStateAction::update_rtn(RuleTreeNode* rtn) +{ + switch ( action ) + { + case IpsPolicy::LOG: rtn->action = snort::Actions::Type::LOG; break; + case IpsPolicy::PASS: rtn->action = snort::Actions::Type::PASS; break; + case IpsPolicy::ALERT: rtn->action = snort::Actions::Type::ALERT; break; + case IpsPolicy::DROP: rtn->action = snort::Actions::Type::DROP; break; + case IpsPolicy::BLOCK: rtn->action = snort::Actions::Type::BLOCK; break; + case IpsPolicy::RESET: rtn->action = snort::Actions::Type::RESET; break; + case IpsPolicy::INHERIT_ACTION: break; + } +} + +void RuleStateEnable::update_rtn(RuleTreeNode* rtn) +{ + switch( enable ) + { + case IpsPolicy::FALSE: rtn->clear_enabled(); break; + case IpsPolicy::TRUE: rtn->set_enabled(); break; + case IpsPolicy::INHERIT_ENABLE: break; + } +} + diff --git a/src/detection/rules.h b/src/detection/rules.h index 9cd60a9f4..83687b0fd 100644 --- a/src/detection/rules.h +++ b/src/detection/rules.h @@ -25,13 +25,7 @@ // FIXIT-L refactor this header #include "actions/actions.h" - -#define ANY_SRC_PORT 0x01 -#define ANY_DST_PORT 0x02 -#define ANY_FLAGS 0x04 -#define BIDIRECTIONAL 0x08 -#define ANY_SRC_IP 0x10 -#define ANY_DST_IP 0x20 +#include "main/policy.h" #define GID_DEFAULT 1 #define GID_SESSION 135 @@ -45,8 +39,10 @@ namespace snort { class IpsAction; + struct SnortConfig; } struct OutputSet; +struct RuleTreeNode; struct ListHead { @@ -67,12 +63,47 @@ struct RuleListNode }; // for separately overriding rule type -struct RuleState +class RuleState { - uint32_t sid; - uint32_t gid; - int state; - RuleState* next; +public: + RuleState(unsigned g, unsigned s) : gid(g), sid(s) + { policy = snort::get_ips_policy()->policy_id; } + + virtual ~RuleState() = default; + + void apply(snort::SnortConfig*); + virtual void update_rtn(RuleTreeNode*) = 0; + +private: + unsigned gid; + unsigned sid; + unsigned policy; + + void apply(snort::SnortConfig*, OptTreeNode* otn, unsigned ips_num); + RuleTreeNode* find_updated_rtn(RuleTreeNode*, snort::SnortConfig*, unsigned ips_num); + void replace_rtn(OptTreeNode*, RuleTreeNode*, snort::SnortConfig*, unsigned ips_num); + RuleTreeNode* dup_rtn(OptTreeNode*, snort::SnortConfig*, unsigned ips_num); +}; + +class RuleStateAction : public RuleState +{ +public: + RuleStateAction(unsigned g, unsigned s, IpsPolicy::Action a) : RuleState(g, s), action(a) { } + virtual void update_rtn(RuleTreeNode*) override; + +private: + IpsPolicy::Action action; + +}; + +class RuleStateEnable : public RuleState +{ +public: + RuleStateEnable(unsigned g, unsigned s, IpsPolicy::Enable e) : RuleState(g, s), enable(e) { } + virtual void update_rtn(RuleTreeNode*) override; + +private: + IpsPolicy::Enable enable; }; #endif diff --git a/src/detection/service_map.cc b/src/detection/service_map.cc index 0479e64b8..eaeb66aa0 100644 --- a/src/detection/service_map.cc +++ b/src/detection/service_map.cc @@ -258,7 +258,7 @@ int fpCreateServiceMaps(SnortConfig* sc) continue; /* Not enabled, don't do the FP content */ - if ( !otn->enabled ) + if ( !rtn->enabled() ) continue; for (svc_idx = 0; svc_idx < otn->sigInfo.num_services; svc_idx++) diff --git a/src/detection/signature.cc b/src/detection/signature.cc index 3edc2bbee..a7dc523a9 100644 --- a/src/detection/signature.cc +++ b/src/detection/signature.cc @@ -177,12 +177,12 @@ void OtnFree(void* data) { OptFpList* tmp = opt_func; opt_func = opt_func->next; - snort_free(tmp); + snort_free(tmp); // FIXIT-L use c++ operators for all of this } if ( otn->sigInfo.message ) { - if (!otn->generated) + if ( !otn->generated() ) snort_free(otn->sigInfo.message); } for (unsigned svc_idx = 0; svc_idx < otn->sigInfo.num_services; svc_idx++) @@ -211,14 +211,14 @@ void OtnFree(void* data) /* RTN was generated on the fly. Don't necessarily know which policy * at this point so go through all RTNs and delete them */ - if (otn->generated) + if ( otn->generated() ) { for (int i = 0; i < otn->proto_node_num; i++) { RuleTreeNode* rtn = deleteRtnFromOtn(otn, i); if ( rtn ) - snort_free(rtn); + delete rtn; } } @@ -228,8 +228,8 @@ void OtnFree(void* data) if (otn->detection_filter) snort_free(otn->detection_filter); - snort_free(otn->state); - snort_free(otn); + delete[] otn->state; + delete otn; } GHash* OtnLookupNew() diff --git a/src/detection/signature.h b/src/detection/signature.h index ace5d9f80..4a278abb8 100644 --- a/src/detection/signature.h +++ b/src/detection/signature.h @@ -89,21 +89,21 @@ enum Target struct SigInfo { - char* message; - ClassType* class_type; - ReferenceNode* refs; - SignatureServiceInfo* services; + char* message = nullptr; + ClassType* class_type = nullptr; + ReferenceNode* refs = nullptr; + SignatureServiceInfo* services = nullptr; - uint32_t gid; - uint32_t sid; - uint32_t rev; + uint32_t gid = 0; + uint32_t sid = 0; + uint32_t rev = 0; - uint32_t class_id; - uint32_t priority; - uint32_t num_services; + uint32_t class_id = 0; + uint32_t priority = 0; + uint32_t num_services = 0; - bool builtin; - Target target; + bool builtin = false; + Target target = TARGET_NONE; }; snort::GHash* OtnLookupNew(); diff --git a/src/detection/treenodes.h b/src/detection/treenodes.h index db3bae884..1618f9a54 100644 --- a/src/detection/treenodes.h +++ b/src/detection/treenodes.h @@ -72,91 +72,148 @@ struct OtnState { return elapsed > 0_ticks || checks > 0; } }; +/* function pointer list for rule head nodes */ +// FIXIT-L use bit mask to determine what header checks to do +// cheaper than traversing a list and uses much less memory +struct RuleFpList +{ + /* context data for this test */ + void* context = nullptr; + + /* rule check function pointer */ + int (* RuleHeadFunc)(snort::Packet*, RuleTreeNode*, RuleFpList*, int) = nullptr; + + /* pointer to the next rule function node */ + RuleFpList* next = nullptr; +}; + +// one of these per rule per policy +// represents head part of rule +struct RuleTreeNode +{ + using Flag = uint8_t; + static constexpr Flag ENABLED = 0x01; + static constexpr Flag ANY_SRC_PORT = 0x02; + static constexpr Flag ANY_DST_PORT = 0x04; + static constexpr Flag ANY_FLAGS = 0x08; + static constexpr Flag BIDIRECTIONAL = 0x10; + static constexpr Flag ANY_SRC_IP = 0x20; + static constexpr Flag ANY_DST_IP = 0x40; + + RuleFpList* rule_func = nullptr; /* match functions.. (Bidirectional etc.. ) */ + + sfip_var_t* sip = nullptr; + sfip_var_t* dip = nullptr; + + PortObject* src_portobject = nullptr; + PortObject* dst_portobject = nullptr; + + struct ListHead* listhead = nullptr; + + SnortProtocolId snort_protocol_id = 0; + + // reference count from otn. + // Multiple OTNs can reference this RTN with the same policy. + unsigned int otnRefCount = 0; // FIXIT-L shared_ptr? + + snort::Actions::Type action = snort::Actions::Type::NONE; + + uint8_t flags = 0; + + void set_enabled() + { flags |= ENABLED; } + + void clear_enabled() + { flags &= (~ENABLED); } + + bool enabled() const + { return flags & ENABLED; } +}; + // one of these for each rule // represents body part of rule struct OptTreeNode { - /* plugin/detection functions go here */ - OptFpList* opt_func; - OutputSet* outputFuncs; /* per sid enabled output functions */ - snort::IpsOption* agent; + using Flag = uint8_t; + static constexpr Flag GENERATED = 0x01; + static constexpr Flag WARNED_FP = 0x02; + static constexpr Flag ESTABLISHED = 0x04; + static constexpr Flag UNESTABLISHED = 0x08; + static constexpr Flag STATELESS = 0x10; /* metadata about signature */ SigInfo sigInfo; - char* soid; + char* soid = nullptr; - struct THD_NODE* detection_filter; /* if present, evaluated last, after header checks */ - TagData* tag; + /* plugin/detection functions go here */ + OptFpList* opt_func = nullptr; + OutputSet* outputFuncs = nullptr; /* per sid enabled output functions */ + snort::IpsOption* agent = nullptr; - // ptr to list of RTNs (head part); indexed by policyId - RuleTreeNode** proto_nodes; + struct THD_NODE* detection_filter = nullptr; /* if present, evaluated last, after header checks */ + TagData* tag = nullptr; - OtnState* state; + // ptr to list of RTNs (head part); indexed by policyId + RuleTreeNode** proto_nodes = nullptr; - int chain_node_number; - int evalIndex; /* where this rule sits in the evaluation sets */ + OtnState* state = nullptr; - // Added for integrity checks during rule parsing. - SnortProtocolId snort_protocol_id; + int chain_node_number = 0; + int evalIndex = 0; /* where this rule sits in the evaluation sets */ - unsigned ruleIndex; // unique index + unsigned ruleIndex = 0; // unique index - bool warned_fp; - bool enabled; + uint32_t num_detection_opts = 0; + uint32_t plugins = 0; - uint32_t num_detection_opts; - uint32_t plugins; + // Added for integrity checks during rule parsing. + SnortProtocolId snort_protocol_id = 0; /**number of proto_nodes. */ - unsigned short proto_node_num; + unsigned short proto_node_num = 0; - uint16_t longestPatternLen; + uint16_t longestPatternLen = 0; - uint8_t stateless; /* this rule can fire regardless of session state */ - uint8_t established; /* this rule can only fire if it is established */ - uint8_t unestablished; + Flag flags = 0; - char generated; -}; + void set_generated() + { flags |= GENERATED; } -/* function pointer list for rule head nodes */ -// FIXIT-L use bit mask to determine what header checks to do -// cheaper than traversing a list and uses much less memory -struct RuleFpList -{ - /* context data for this test */ - void* context; + bool generated() const + { return flags & GENERATED; } - /* rule check function pointer */ - int (* RuleHeadFunc)(snort::Packet*, RuleTreeNode*, RuleFpList*, int); + void set_warned_fp() + { flags |= WARNED_FP; } - /* pointer to the next rule function node */ - RuleFpList* next; -}; + bool warned_fp() const + { return flags & WARNED_FP; } -// one of these per rule per policy -// represents head part of rule -struct RuleTreeNode -{ - RuleFpList* rule_func; /* match functions.. (Bidirectional etc.. ) */ + void set_established() + { flags |= ESTABLISHED; } - sfip_var_t* sip; - sfip_var_t* dip; + void set_unestablished() + { flags |= UNESTABLISHED; } - PortObject* src_portobject; - PortObject* dst_portobject; + void set_stateless() + { flags |= STATELESS; } - struct ListHead* listhead; + bool established() const + { return flags & ESTABLISHED; } - SnortProtocolId snort_protocol_id; + bool unestablished() const + { return flags & UNESTABLISHED; } - uint32_t flags; /* control flags */ + bool stateless() const + { return flags & STATELESS; } - snort::Actions::Type type; + bool enabled_somewhere() const + { + for ( unsigned i = 0; i < proto_node_num; i++ ) + if ( proto_nodes[i] and proto_nodes[i]->enabled() ) + return true; - // reference count from otn. - // Multiple OTNs can reference this RTN with the same policy. - unsigned int otnRefCount; + return false; + } }; typedef int (* RuleOptEvalFunc)(void*, Cursor&, snort::Packet*); diff --git a/src/framework/parameter.h b/src/framework/parameter.h index 28e354241..2b0327801 100644 --- a/src/framework/parameter.h +++ b/src/framework/parameter.h @@ -67,6 +67,13 @@ struct SO_PUBLIC Parameter const void* range; // nullptr|const char*|RangeQuery*|const Parameter* const char* deflt; const char* help; + bool regex = false; // for name resolution + + Parameter(const char* n, Type t, const void* r, const char* d, const char* h, bool re) : + name(n), type(t), range(r), deflt(d), help(h), regex(re) { } + + Parameter(const char* n, Type t, const void* r, const char* d, const char* h) : + name(n), type(t), range(r), deflt(d), help(h) { } const char* get_type() const; const char* get_range() const; diff --git a/src/ips_options/ips_flow.cc b/src/ips_options/ips_flow.cc index 858d6bf05..a4c492752 100644 --- a/src/ips_options/ips_flow.cc +++ b/src/ips_options/ips_flow.cc @@ -410,13 +410,13 @@ static IpsOption* flow_ctor(Module* p, OptTreeNode* otn) FlowModule* m = (FlowModule*)p; if ( m->data.stateless ) - otn->stateless = 1; + otn->set_stateless(); if ( m->data.established ) - otn->established = 1; + otn->set_established(); if ( m->data.unestablished ) - otn->unestablished = 1; + otn->set_unestablished(); if (otn->snort_protocol_id == SNORT_PROTO_ICMP) { diff --git a/src/main/modules.cc b/src/main/modules.cc index a0f11bfc8..db95f977a 100755 --- a/src/main/modules.cc +++ b/src/main/modules.cc @@ -24,6 +24,8 @@ #include "modules.h" +#include + #include "codecs/codec_module.h" #include "detection/fp_config.h" #include "filters/detection_filter.h" @@ -72,6 +74,12 @@ static const Parameter detection_params[] = { "asn1", Parameter::PT_INT, "0:65535", "0", "maximum decode nodes" }, + { "global_default_rule_state", Parameter::PT_BOOL, nullptr, "true", + "enable or disable rules by default (overridden by ips policy settings)" }, + + { "global_rule_state", Parameter::PT_BOOL, nullptr, "false", + "apply rule_state against all policies" }, + { "offload_limit", Parameter::PT_INT, "0:max32", "99999", "minimum sizeof PDU to offload fast pattern search (defaults to disabled)" }, @@ -119,6 +127,12 @@ bool DetectionModule::set(const char* fqn, Value& v, SnortConfig* sc) if ( v.is("asn1") ) sc->asn1_mem = v.get_uint16(); + else if ( v.is("global_default_rule_state") ) + sc->global_default_rule_state = v.get_bool(); + + else if ( v.is("global_rule_state") ) + sc->global_rule_state = v.get_bool(); + else if ( v.is("offload_limit") ) sc->offload_limit = v.get_uint32(); @@ -664,9 +678,6 @@ static const Parameter alerts_params[] = { "alert_with_interface_name", Parameter::PT_BOOL, nullptr, "false", "include interface in alert info (fast, full, or syslog only)" }, - { "default_rule_state", Parameter::PT_BOOL, nullptr, "true", - "enable or disable ips rules" }, - { "detection_filter_memcap", Parameter::PT_INT, "0:max32", "1048576", "set available MB of memory for detection_filters" }, @@ -713,9 +724,6 @@ bool AlertsModule::set(const char*, Value& v, SnortConfig* sc) if ( v.is("alert_with_interface_name") ) v.update_mask(sc->output_flags, OUTPUT_FLAG__ALERT_IFACE); - else if ( v.is("default_rule_state") ) - sc->default_rule_state = v.get_bool(); - else if ( v.is("detection_filter_memcap") ) sc->detection_filter_config->memcap = v.get_uint32(); @@ -1210,6 +1218,9 @@ bool InspectionModule::set(const char*, Value& v, SnortConfig* sc) static const Parameter ips_params[] = { + { "default_rule_state", Parameter::PT_ENUM, "false | true | inherit", "inherit", + "enable or disable ips rules" }, + { "enable_builtin_rules", Parameter::PT_BOOL, nullptr, "false", "enable events from builtin rules w/o stubs" }, @@ -1254,7 +1265,10 @@ bool IpsModule::set(const char*, Value& v, SnortConfig* sc) { IpsPolicy* p = get_ips_policy(); - if ( v.is("enable_builtin_rules") ) + if ( v.is("default_rule_state") ) + p->default_rule_state = (IpsPolicy::Enable)v.get_uint8(); + + else if ( v.is("enable_builtin_rules") ) p->enable_builtin_rules = v.get_bool(); else if ( v.is("id") ) @@ -1711,68 +1725,73 @@ bool RateFilterModule::end(const char*, int idx, SnortConfig* sc) // rule_state module //------------------------------------------------------------------------- -static const Parameter rule_state_params[] = +static const Parameter single_rule_state_params[] = { - { "gid", Parameter::PT_INT, "0:max32", "0", - "rule generator ID" }, + { "action", Parameter::PT_ENUM, "log | pass | alert | drop | block | reset | inherit", "inherit", + "apply action if rule matches or inherit from rule definition" }, - { "sid", Parameter::PT_INT, "0:max32", "0", - "rule signature ID" }, + { "enable", Parameter::PT_ENUM, "false | true | inherit", "inherit", + "enable or disable rule in current ips policy or use default defined by ips policy" }, - { "enable", Parameter::PT_BOOL, nullptr, "true", - "enable or disable rule in all policies" }, + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } +}; + +static const char* rule_state_gid_sid_regex = "([0-9]+):([0-9]+)"; +static const Parameter rule_state_params[] = +{ + { rule_state_gid_sid_regex, Parameter::PT_TABLE, single_rule_state_params, nullptr, + "defines rule state parameters for gid:sid", true }, { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } }; #define rule_state_help \ - "enable/disable specific IPS rules" + "enable/disable and set actions for specific IPS rules" class RuleStateModule : public Module { public: - RuleStateModule() : Module("rule_state", rule_state_help, rule_state_params, true) { } + RuleStateModule() : Module("rule_state", rule_state_help, rule_state_params, false) { } bool set(const char*, Value&, SnortConfig*) override; - bool begin(const char*, int, SnortConfig*) override; - bool end(const char*, int, SnortConfig*) override; Usage get_usage() const override { return DETECT; } - -private: - RuleState state; }; -bool RuleStateModule::set(const char*, Value& v, SnortConfig*) +bool RuleStateModule::set(const char* fqn, Value& v, SnortConfig* sc) { - if ( v.is("gid") ) - state.gid = v.get_uint32(); + static regex gid_sid(rule_state_gid_sid_regex); - else if ( v.is("sid") ) - state.sid = v.get_uint32(); + // the regex itself is passed as the fqn when declaring rule_state = { } + if ( strstr(fqn, rule_state_gid_sid_regex) ) + return true; + + cmatch match; - else if ( v.is("enable") ) - state.state = v.get_bool(); + if ( regex_search(fqn, match, gid_sid) ) + { + unsigned gid = strtoul(match[1].str().c_str(), nullptr, 10); + unsigned sid = strtoul(match[2].str().c_str(), nullptr, 10); + if ( v.is("action") ) + { + sc->rule_states.emplace_back( + new RuleStateAction(gid, sid, IpsPolicy::Action(v.get_uint8()))); + } + else if ( v.is("enable") ) + { + sc->rule_states.emplace_back( + new RuleStateEnable(gid, sid, IpsPolicy::Enable(v.get_uint8()))); + } + else + return false; + } else return false; return true; } -bool RuleStateModule::begin(const char*, int, SnortConfig*) -{ - memset(&state, 0, sizeof(state)); - return true; -} - -bool RuleStateModule::end(const char*, int idx, SnortConfig* sc) -{ - if ( idx ) - AddRuleState(sc, state); - return true; -} - //------------------------------------------------------------------------- // hosts module //------------------------------------------------------------------------- diff --git a/src/main/policy.h b/src/main/policy.h index 9b47dcc1c..ec67d2464 100644 --- a/src/main/policy.h +++ b/src/main/policy.h @@ -136,6 +136,9 @@ private: struct IpsPolicy { public: + enum Action : uint8_t { LOG, PASS, ALERT, DROP, BLOCK, RESET, INHERIT_ACTION }; + enum Enable : uint8_t { FALSE, TRUE, INHERIT_ENABLE }; + IpsPolicy(PolicyId = 0); ~IpsPolicy(); @@ -159,6 +162,8 @@ public: PortVarTable* portVarTable; /* named entries, uses a hash table */ PortTable* nonamePortVarTable; /* un-named entries */ + Enable default_rule_state = INHERIT_ENABLE; + bool obfuscate_pii; }; diff --git a/src/main/snort_config.cc b/src/main/snort_config.cc index d71668be6..d632719c3 100644 --- a/src/main/snort_config.cc +++ b/src/main/snort_config.cc @@ -245,7 +245,6 @@ SnortConfig::~SnortConfig() return; } - free_rule_state_list(); FreeClassifications(classifications); FreeReferences(references); @@ -330,7 +329,8 @@ void SnortConfig::setup() //print_thresholding(threshold_config, 0); //PrintRuleOrder(rule_lists); - SetRuleStates(this); + for ( auto& state : rule_states ) + state->apply(this); /* Need to do this after dynamic detection stuff is initialized, too */ IpsManager::verify(this); @@ -1050,19 +1050,6 @@ void SnortConfig::enable_syslog() syslog_configured = true; } -void SnortConfig::free_rule_state_list() -{ - RuleState* head = rule_state_list; - - while ( head ) - { - RuleState* tmp = head; - head = head->next; - snort_free(tmp); - } - rule_state_list = nullptr; -} - bool SnortConfig::tunnel_bypass_enabled(uint8_t proto) { return (!((get_conf()->tunnel_mask & proto) or SFDAQ::get_tunnel_bypass(proto))); diff --git a/src/main/snort_config.h b/src/main/snort_config.h index e95f3fc14..3bae004fc 100644 --- a/src/main/snort_config.h +++ b/src/main/snort_config.h @@ -125,7 +125,7 @@ struct LatencyConfig; struct PORT_RULE_MAP; struct RuleListNode; struct RulePortTables; -struct RuleState; +class RuleState; struct DetectionFilterConfig; struct EventQueueConfig; struct IpsActionsConfig; @@ -179,7 +179,6 @@ public: //------------------------------------------------------ // alert module stuff std::string rule_order; - bool default_rule_state = true; SfCidr homenet; @@ -216,6 +215,9 @@ public: unsigned offload_limit = 99999; // disabled unsigned offload_threads = 0; // disabled + bool global_rule_state = false; + bool global_default_rule_state = true; + //------------------------------------------------------ // process stuff @@ -306,7 +308,7 @@ public: int thiszone = 0; - RuleState* rule_state_list = nullptr; + std::vector rule_states; ClassType* classifications = nullptr; ReferenceSystemNode* references = nullptr; GHash* otn_map = nullptr; @@ -422,7 +424,6 @@ public: void set_umask(uint32_t); void set_utc(bool); void set_verbose(bool); - void free_rule_state_list(); //------------------------------------------------------ // Static convenience accessor methods @@ -499,8 +500,21 @@ public: static int get_eval_index(Actions::Type type) { return get_conf()->evalOrder[type]; } - static int get_default_rule_state() - { return get_conf()->default_rule_state; } + static bool get_default_rule_state() + { + switch ( get_ips_policy()->default_rule_state ) + { + case IpsPolicy::INHERIT_ENABLE: + return get_conf()->global_default_rule_state; + + case IpsPolicy::TRUE: + return true; + + case IpsPolicy::FALSE: + return false; + } + return true; + } SO_PUBLIC static bool tunnel_bypass_enabled(uint8_t proto); diff --git a/src/managers/module_manager.cc b/src/managers/module_manager.cc index 166d31697..60809a08b 100644 --- a/src/managers/module_manager.cc +++ b/src/managers/module_manager.cc @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -346,8 +347,16 @@ static const Parameter* get_params( } string name = new_fqn.substr(0, new_fqn.find_first_of('.')); - while ( p->name && name != p->name ) + while ( p->name ) + { + if ( p->regex && regex_match(name, regex(p->name)) ) + break; + + else if ( name == p->name ) + break; + ++p; + } if ( !p->name ) return nullptr; diff --git a/src/parser/parse_conf.cc b/src/parser/parse_conf.cc index 35a4faea3..ee22c7905 100644 --- a/src/parser/parse_conf.cc +++ b/src/parser/parse_conf.cc @@ -232,25 +232,6 @@ ListHead* get_rule_list(SnortConfig* sc, const char* s) return p ? p->RuleList : nullptr; } -void AddRuleState(SnortConfig* sc, const RuleState& rs) // FIXIT-L move to snort config -{ - if (sc == nullptr) - return; - - RuleState* state = (RuleState*)snort_calloc(sizeof(RuleState)); - *state = rs; - - if ( !sc->rule_state_list ) - { - sc->rule_state_list = state; - } - else - { - state->next = sc->rule_state_list; - sc->rule_state_list = state; - } -} - void ParseConfigFile(SnortConfig* sc, const char* fname) { if ( !fname ) diff --git a/src/parser/parse_conf.h b/src/parser/parse_conf.h index aa0adb01c..e28e4f21a 100644 --- a/src/parser/parse_conf.h +++ b/src/parser/parse_conf.h @@ -37,7 +37,6 @@ void ParseConfigString(snort::SnortConfig*, const char* str); void ParseIpVar(snort::SnortConfig*, const char* name, const char* s); void parse_include(snort::SnortConfig*, const char*); -void AddRuleState(snort::SnortConfig*, const RuleState&); void add_service_to_otn(snort::SnortConfig*, OptTreeNode*, const char*); snort::Actions::Type get_rule_type(const char*); diff --git a/src/parser/parse_rule.cc b/src/parser/parse_rule.cc index 605b0d9ce..5badae03e 100644 --- a/src/parser/parse_rule.cc +++ b/src/parser/parse_rule.cc @@ -142,14 +142,14 @@ static int FinishPortListRule( break; default: - rtn->flags |= ANY_SRC_PORT|ANY_DST_PORT; + rtn->flags |= RuleTreeNode::ANY_SRC_PORT|RuleTreeNode::ANY_DST_PORT; dstTable = srcTable = nullptr; aaObject = port_tables->svc_any; prc = &svcCnt; } /* Count rules with both src and dst specific ports */ - if (!(rtn->flags & ANY_DST_PORT) && !(rtn->flags & ANY_SRC_PORT)) + if (!(rtn->flags & RuleTreeNode::ANY_DST_PORT) && !(rtn->flags & RuleTreeNode::ANY_SRC_PORT)) { prc->both++; } @@ -157,19 +157,19 @@ static int FinishPortListRule( /* If not an any-any rule test for port bleedover, if we are using a * single rule group, don't bother */ if (!fp->get_single_rule_group() && - (rtn->flags & (ANY_DST_PORT|ANY_SRC_PORT)) != (ANY_DST_PORT|ANY_SRC_PORT)) + (rtn->flags & (RuleTreeNode::ANY_DST_PORT|RuleTreeNode::ANY_SRC_PORT)) != (RuleTreeNode::ANY_DST_PORT|RuleTreeNode::ANY_SRC_PORT)) { int dst_cnt = 0; int src_cnt = 0; - if (!(rtn->flags & ANY_SRC_PORT)) + if (!(rtn->flags & RuleTreeNode::ANY_SRC_PORT)) { src_cnt = PortObjectPortCount(rtn->src_portobject); if (src_cnt >= fp->get_bleed_over_port_limit()) large_port_group = 1; } - if (!(rtn->flags & ANY_DST_PORT)) + if (!(rtn->flags & RuleTreeNode::ANY_DST_PORT)) { dst_cnt = PortObjectPortCount(rtn->dst_portobject); if (dst_cnt >= fp->get_bleed_over_port_limit()) @@ -198,7 +198,7 @@ static int FinishPortListRule( * any-any port rules... * If we have an any-any rule or a large port group or * were using a single rule group we make it an any-any rule. */ - if (((rtn->flags & (ANY_DST_PORT|ANY_SRC_PORT)) == (ANY_DST_PORT|ANY_SRC_PORT)) || + if (((rtn->flags & (RuleTreeNode::ANY_DST_PORT|RuleTreeNode::ANY_SRC_PORT)) == (RuleTreeNode::ANY_DST_PORT|RuleTreeNode::ANY_SRC_PORT)) || large_port_group || fp->get_single_rule_group()) { if (snort_protocol_id == SNORT_PROTO_IP) @@ -247,7 +247,7 @@ static int FinishPortListRule( } /* add rule index to dst table if we have a specific dst port or port list */ - if (!(rtn->flags & ANY_DST_PORT)) + if (!(rtn->flags & RuleTreeNode::ANY_DST_PORT)) { prc->dst++; @@ -270,7 +270,7 @@ static int FinishPortListRule( PortObjectAddRule(pox, otn->ruleIndex); /* if bidir, add this rule and port group to the src table */ - if (rtn->flags & BIDIRECTIONAL) + if (rtn->flags & RuleTreeNode::BIDIRECTIONAL) { pox = PortTableFindInputPortObjectPorts(srcTable, rtn->dst_portobject); if ( !pox ) @@ -290,7 +290,7 @@ static int FinishPortListRule( } /* add rule index to src table if we have a specific src port or port list */ - if (!(rtn->flags & ANY_SRC_PORT)) + if (!(rtn->flags & RuleTreeNode::ANY_SRC_PORT)) { prc->src++; PortObject* pox = PortTableFindInputPortObjectPorts(srcTable, rtn->src_portobject); @@ -309,7 +309,7 @@ static int FinishPortListRule( PortObjectAddRule(pox, otn->ruleIndex); /* if bidir, add this rule and port group to the dst table */ - if (rtn->flags & BIDIRECTIONAL) + if (rtn->flags & RuleTreeNode::BIDIRECTIONAL) { pox = PortTableFindInputPortObjectPorts(dstTable, rtn->src_portobject); if ( !pox ) @@ -401,7 +401,7 @@ static int ProcessIP(SnortConfig*, const char* addr, RuleTreeNode* rtn, int mode if (rtn->sip->head && rtn->sip->head->flags & SFIP_ANY) { - rtn->flags |= ANY_SRC_IP; + rtn->flags |= RuleTreeNode::ANY_SRC_IP; } } /* mode == DST */ @@ -456,7 +456,7 @@ static int ProcessIP(SnortConfig*, const char* addr, RuleTreeNode* rtn, int mode if (rtn->dip->head && rtn->dip->head->flags & SFIP_ANY) { - rtn->flags |= ANY_DST_IP; + rtn->flags |= RuleTreeNode::ANY_DST_IP; } } @@ -589,9 +589,9 @@ static int ParsePortList( if ( PortObjectHasAny(portobject) ) { if ( dst_flag ) - rtn->flags |= ANY_DST_PORT; + rtn->flags |= RuleTreeNode::ANY_DST_PORT; else - rtn->flags |= ANY_SRC_PORT; + rtn->flags |= RuleTreeNode::ANY_SRC_PORT; } /* check for a pure not rule - fatal if we find one */ @@ -619,7 +619,7 @@ bool same_headers(RuleTreeNode* rule, RuleTreeNode* rtn) if ( !rule or !rtn ) return false; - if (rule->type != rtn->type) + if (rule->action != rtn->action) return false; if (rule->snort_protocol_id != rtn->snort_protocol_id) @@ -666,7 +666,7 @@ static RuleTreeNode* findHeadNode( static void XferHeader(RuleTreeNode* from, RuleTreeNode* to) { to->flags = from->flags; - to->type = from->type; + to->action = from->action; to->sip = from->sip; to->dip = from->dip; @@ -691,7 +691,7 @@ static void AddRuleFuncToList( if ( !idx ) { - rtn->rule_func = (RuleFpList*)snort_calloc(sizeof(RuleFpList)); + rtn->rule_func = new RuleFpList; rtn->rule_func->RuleHeadFunc = rfunc; } else @@ -699,7 +699,7 @@ static void AddRuleFuncToList( while ( idx->next ) idx = idx->next; - idx->next = (RuleFpList*)snort_calloc(sizeof(RuleFpList)); + idx->next = new RuleFpList; idx = idx->next; idx->RuleHeadFunc = rfunc; } @@ -722,14 +722,14 @@ static void AddrToFunc(RuleTreeNode* rtn, int mode) switch (mode) { case SRC: - if ((rtn->flags & ANY_SRC_IP) == 0) + if ((rtn->flags & RuleTreeNode::ANY_SRC_IP) == 0) { AddRuleFuncToList(CheckSrcIP, rtn); } break; case DST: - if ((rtn->flags & ANY_DST_IP) == 0) + if ((rtn->flags & RuleTreeNode::ANY_DST_IP) == 0) { AddRuleFuncToList(CheckDstIP, rtn); } @@ -788,14 +788,14 @@ static void PortToFunc(RuleTreeNode* rtn, int any_flag, int except_flag, int mod // functions (addrs and ports) static void SetupRTNFuncList(RuleTreeNode* rtn) { - if (rtn->flags & BIDIRECTIONAL) + if (rtn->flags & RuleTreeNode::BIDIRECTIONAL) { AddRuleFuncToList(CheckBidirectional, rtn); } else { - PortToFunc(rtn, (rtn->flags & ANY_DST_PORT) ? 1 : 0, 0, DST); - PortToFunc(rtn, (rtn->flags & ANY_SRC_PORT) ? 1 : 0, 0, SRC); + PortToFunc(rtn, (rtn->flags & RuleTreeNode::ANY_DST_PORT) ? 1 : 0, 0, DST); + PortToFunc(rtn, (rtn->flags & RuleTreeNode::ANY_SRC_PORT) ? 1 : 0, 0, SRC); AddrToFunc(rtn, SRC); AddrToFunc(rtn, DST); @@ -817,7 +817,7 @@ static RuleTreeNode* ProcessHeadNode( { head_count++; - rtn = (RuleTreeNode*)snort_calloc(sizeof(RuleTreeNode)); + rtn = new RuleTreeNode; rtn->otnRefCount++; /* copy the prototype header info into the new header block */ @@ -854,7 +854,7 @@ static int mergeDuplicateOtn( RuleTreeNode* rtn_cur = getRtnFromOtn(otn_cur); - if ( rtn_cur and rtn_cur->type != rtn_new->type ) + if ( rtn_cur and rtn_cur->action != rtn_new->action ) { ParseError("GID %u SID %u in rule duplicates previous rule, with different type.", otn_new->sigInfo.gid, otn_new->sigInfo.sid); @@ -988,10 +988,10 @@ void parse_rule_print() void parse_rule_type(SnortConfig* sc, const char* s, RuleTreeNode& rtn) { - memset(&rtn, 0, sizeof(rtn)); - rtn.type = get_rule_type(s); + rtn = RuleTreeNode(); + rtn.action = get_rule_type(s); - if ( rtn.type == Actions::NONE ) + if ( rtn.action == Actions::NONE ) { s_ignore = true; return; @@ -1002,9 +1002,11 @@ void parse_rule_type(SnortConfig* sc, const char* s, RuleTreeNode& rtn) if ( !rtn.listhead ) { - CreateRuleType(sc, s, rtn.type); + CreateRuleType(sc, s, rtn.action); rtn.listhead = get_rule_list(sc, s); } + if ( SnortConfig::get_default_rule_state() ) + rtn.set_enabled(); } if ( !rtn.listhead ) @@ -1070,7 +1072,7 @@ void parse_rule_dir(SnortConfig*, const char* s, RuleTreeNode& rtn) return; if (strcmp(s, "<>") == 0) - rtn.flags |= BIDIRECTIONAL; + rtn.flags |= RuleTreeNode::BIDIRECTIONAL; else if ( strcmp(s, "->") ) ParseError("illegal direction specifier: %s", s); @@ -1119,15 +1121,17 @@ OptTreeNode* parse_rule_open(SnortConfig* sc, RuleTreeNode& rtn, bool stub) parse_rule_nets(sc, "any", false, rtn); parse_rule_ports(sc, "any", false, rtn); } - OptTreeNode* otn = (OptTreeNode*)snort_calloc(sizeof(OptTreeNode)); - otn->state = (OtnState*)snort_calloc(ThreadConfig::get_instance_max(), sizeof(OtnState)); + OptTreeNode* otn = new OptTreeNode; + otn->state = new OtnState[ThreadConfig::get_instance_max()]; if ( !stub ) otn->sigInfo.gid = GID_DEFAULT; otn->chain_node_number = otn_count; otn->snort_protocol_id = rtn.snort_protocol_id; - otn->enabled = SnortConfig::get_default_rule_state(); + + if ( SnortConfig::get_default_rule_state() ) + rtn.set_enabled(); IpsManager::reset_options(); diff --git a/src/parser/parser.cc b/src/parser/parser.cc index 11e28ddcf..2967fd8b8 100644 --- a/src/parser/parser.cc +++ b/src/parser/parser.cc @@ -85,7 +85,7 @@ static void FreeRuleTreeNodes(SnortConfig* sc) /* Autogenerated OTNs along with their respective pseudo RTN * will get cleaned up when the OTN is freed */ - if (otn->generated) + if ( otn->generated() ) continue; for (policyId = 0; @@ -277,7 +277,10 @@ void parser_term(SnortConfig* sc) parse_rule_term(); RuleIndexMapFree(ruleIndexMap); ruleIndexMap = nullptr; - sc->free_rule_state_list(); + + for ( auto& r : sc->rule_states ) + delete r; + sc->rule_states.clear(); } SnortConfig* ParseSnortConf(const SnortConfig* boot_conf, const char* fname, bool is_fatal) @@ -361,7 +364,7 @@ void FreeRuleTreeNode(RuleTreeNode* rtn) { RuleFpList* tmp = idx; idx = idx->next; - snort_free(tmp); + delete tmp; } } @@ -376,36 +379,7 @@ void DestroyRuleTreeNode(RuleTreeNode* rtn) FreeRuleTreeNode(rtn); - snort_free(rtn); -} - -/**************************************************************************** - * Purpose: Adjust the information for a given rule - * relative to the Rule State list - *****************************************************************************/ -void SetRuleStates(SnortConfig* sc) -{ - RuleState* rule_state; - - if (sc == nullptr) - return; - - /* First, cycle through the rule state list and update the - * rule state for each one we find. */ - for (rule_state = sc->rule_state_list; rule_state != nullptr; rule_state = rule_state->next) - { - /* Lookup the OTN by ruleState->sid, ruleState->gid */ - OptTreeNode* otn = OtnLookup(sc->otn_map, rule_state->gid, rule_state->sid); - - if (otn == nullptr) - { - ParseError("Rule state specified for invalid SID: %u GID: %u", - rule_state->sid, rule_state->gid); - return; - } - - otn->enabled = rule_state->state; - } + delete rtn; } void ParseRules(SnortConfig* sc) @@ -637,7 +611,7 @@ static uint32_t rtn_hash_func(HashFnc*, const unsigned char* k, int) const RuleTreeNodeKey* rtnk = (const RuleTreeNodeKey*)k; RuleTreeNode* rtn = rtnk->rtn; - a = rtn->type; + a = rtn->action; b = rtn->flags; c = (uint32_t)(uintptr_t)rtn->listhead; diff --git a/tools/snort2lua/config_states/config_default_rule_state.cc b/tools/snort2lua/config_states/config_default_rule_state.cc index f435d10be..81b1b3e03 100644 --- a/tools/snort2lua/config_states/config_default_rule_state.cc +++ b/tools/snort2lua/config_states/config_default_rule_state.cc @@ -41,16 +41,16 @@ bool DefaultRuleState::convert(std::istringstream& data_stream) bool retval = true; std::string val; - table_api.open_table("alerts"); + table_api.open_table("detection"); if (data_stream >> val && util::case_compare(val, "disabled")) { - table_api.add_option("default_rule_state", false); + table_api.add_option("global_default_rule_state", false); } else { - table_api.add_option("default_rule_state", true); + table_api.add_option("global_default_rule_state", true); } table_api.close_table(); diff --git a/tools/snort2lua/data/data_types/dt_table.cc b/tools/snort2lua/data/data_types/dt_table.cc index d0874ec4a..bb25e76fb 100644 --- a/tools/snort2lua/data/data_types/dt_table.cc +++ b/tools/snort2lua/data/data_types/dt_table.cc @@ -51,6 +51,15 @@ Table::Table(const std::string& table_name, int d) Comments::CommentType::SINGLE_LINE); } +Table::Table(const std::string& table_name, const std::string& key, int d) +{ + this->name = table_name; + this->key = key; + this->depth = d; + this->comments = new Comments(d + 1, + Comments::CommentType::SINGLE_LINE); +} + Table::~Table() { for ( Table* t : tables) @@ -254,7 +263,12 @@ std::ostream& operator<<(std::ostream& out, const Table& t) if ( t.print_whitespace ) out << whitespace; - out << t.name << (t.one_line ? " = " : " =\n"); + out << t.name; + + if ( !t.key.empty() ) + out << "[\"" << t.key << "\"]"; + + out << (t.one_line ? " = " : " =\n"); } out << (t.print_whitespace ? whitespace : "") diff --git a/tools/snort2lua/data/data_types/dt_table.h b/tools/snort2lua/data/data_types/dt_table.h index 7e7fdf24c..c0d9f8bdf 100644 --- a/tools/snort2lua/data/data_types/dt_table.h +++ b/tools/snort2lua/data/data_types/dt_table.h @@ -33,6 +33,7 @@ class Table public: Table(int depth); Table(const std::string& name, int depth); + Table(const std::string& name, const std::string& key, int depth); virtual ~Table(); inline const std::string& get_name() { return name; } @@ -61,6 +62,7 @@ public: private: std::string name; + std::string key; bool one_line = false; bool print_whitespace = true; diff --git a/tools/snort2lua/data/dt_table_api.cc b/tools/snort2lua/data/dt_table_api.cc index 40edcc4a8..10c6f3cbf 100644 --- a/tools/snort2lua/data/dt_table_api.cc +++ b/tools/snort2lua/data/dt_table_api.cc @@ -148,6 +148,20 @@ void TableApi::open_table(bool one_line) } } +void TableApi::open_associative_table(const char* name, const char* key) +{ + if ( should_delegate(name) ) + { + delegate->open_associative_table(name, key); + delegating++; + return; + } + + Table* t = new Table(name, key, 0); + tables.push_back(t); + open_tables.push(t); +} + void TableApi::close_table() { if ( should_delegate() ) diff --git a/tools/snort2lua/data/dt_table_api.h b/tools/snort2lua/data/dt_table_api.h index 6fc745561..a7c6dbe71 100644 --- a/tools/snort2lua/data/dt_table_api.h +++ b/tools/snort2lua/data/dt_table_api.h @@ -87,6 +87,9 @@ public: // open a nested table that does not contain a name --> {...}) void open_table(bool one_line = false); +// open a table using the syntax name["key"] = {...} + void open_associative_table(const char* name, const char* key); + // close the nested table. go to previous table level void close_table(); diff --git a/tools/snort2lua/helpers/converter.cc b/tools/snort2lua/helpers/converter.cc index 07064dfdd..67b4c51c3 100644 --- a/tools/snort2lua/helpers/converter.cc +++ b/tools/snort2lua/helpers/converter.cc @@ -46,8 +46,9 @@ TableDelegation table_delegation = { "ips", true }, { "network", true }, { "normalizer", true }, - { "stream_tcp", true}, - { "suppress", true}, + { "rule_state", true }, + { "stream_tcp", true }, + { "suppress", true }, }; std::string Converter::ips_pattern; diff --git a/tools/snort2lua/keyword_states/kws_rule_state.cc b/tools/snort2lua/keyword_states/kws_rule_state.cc index 44a794826..6519fa1c3 100644 --- a/tools/snort2lua/keyword_states/kws_rule_state.cc +++ b/tools/snort2lua/keyword_states/kws_rule_state.cc @@ -36,38 +36,48 @@ public: }; } // namespace +using namespace std; + bool RuleState::convert(std::istringstream& data_stream) { + static bool did_preamble = false; + std::string arg; bool retval = true; int count = 0; - table_api.open_table("rule_state"); - table_api.open_table(); + if ( !did_preamble ) + { + did_preamble = true; + table_api.open_table("detection"); + table_api.add_option("global_rule_state", true); + table_api.close_table(); + table_api.open_table("rule_state"); + table_api.close_table(); + } + + string gid; + string sid; + string enable; + string action; while (util::get_string(data_stream, arg, ", ")) { switch (count) { case 0: - table_api.add_option("sid", std::stoi(arg)); + sid = arg; count++; break; case 1: - table_api.add_option("gid", std::stoi(arg)); + gid = arg; count++; break; case 2: if (arg == "enabled") - { - table_api.add_diff_option_comment("enabled", "enable"); - table_api.add_option("enable", true); - } + enable = "true"; else if (arg == "disabled") - { - table_api.add_diff_option_comment("disabled", "enable"); - table_api.add_option("enable", false); - } + enable = "false"; else { data_api.failed_conversion(data_stream, "third option must be {enabled|disabled|"); @@ -77,7 +87,7 @@ bool RuleState::convert(std::istringstream& data_stream) count++; break; case 3: - table_api.add_deleted_comment("action"); + action = arg; count++; break; default: @@ -86,8 +96,37 @@ bool RuleState::convert(std::istringstream& data_stream) } } - table_api.close_table(); - table_api.close_table(); + if ( count < 2 ) + { + data_api.failed_conversion(data_stream, "must set a gid and sid for rule state" + arg); + retval = false; + } + + if ( retval ) + { + string key = gid + ":" + sid; + table_api.open_associative_table("rule_state", key.c_str()); + + if ( !enable.empty() ) + { + table_api.add_diff_option_comment("enabled/disabled", "enable"); + table_api.add_option("enable", enable); + } + + if ( !action.empty() ) + { + if ( action == "sdrop" ) + { + action = "drop"; + table_api.add_diff_option_comment("sdrop", "drop"); + } + + table_api.add_option("action", action); + } + + table_api.close_table(); + } + return retval; }