From: Russ Combs (rucombs) Date: Tue, 7 Apr 2020 13:11:54 +0000 (+0000) Subject: Merge pull request #2115 in SNORT/snort3 from ~RUCOMBS/snort3:rule_meta to master X-Git-Tag: 3.0.1-2~34 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d504060151641828dd6f4e167eb2190a44d464c0;p=thirdparty%2Fsnort3.git Merge pull request #2115 in SNORT/snort3 from ~RUCOMBS/snort3:rule_meta to master Squashed commit of the following: commit 9f06fc29f374152d9258636b16e37f966753f6f6 Author: russ Date: Sun Apr 5 11:25:48 2020 -0400 flowbits: relocate bitop.h to helpers commit 28c62396337cab09d8762e2299043fc0dd75a60f Author: russ Date: Sun Apr 5 00:13:29 2020 -0400 flowbits: fix reload mapping commit 6637ad94652470fbff956e90a33760d92f56937e Author: russ Date: Sun Mar 29 20:54:58 2020 -0400 ips: fix rule state mapping and policy lookup commit b12fae905f4d3e2fa845572cf9d51da42b21fde8 Author: russ Date: Sun Mar 29 15:12:52 2020 -0400 src: remove extraneous trailing spaces commit bcab016e2dafb240316bfccaf728bb36dfb291e6 Author: russ Date: Sun Mar 29 10:31:55 2020 -0400 so rules: allow stub gid:sid:rev to override so commit 6e8e8a7e39df9030c0b679aaec1688eb602b1325 Author: russ Date: Sat Mar 28 17:23:49 2020 -0400 metadata-filter: apply to so rule stubs commit 2ba460819894571742f16166286b597963d08652 Author: russ Date: Sat Mar 28 16:04:57 2020 -0400 so rules: allow stub header to override so header commit 9a40462e5b02191f2bd44abd98d40876d87af233 Author: russ Date: Sat Mar 28 10:40:41 2020 -0400 snort: add --dump-rule-state commit 7aa13768693d7037a0525d90bef053866203bad8 Author: russ Date: Sat Mar 28 09:47:31 2020 -0400 snort: add --dump-rule-deps commit 3975f00f8476bccacb4047cd4488555d513d3b22 Author: russ Date: Sat Mar 28 09:11:08 2020 -0400 snort: add rule text to --dump-rule-meta commit 17eff18a925a8fc728fbe1821f2c390b05fac49b Author: russ Date: Mon Mar 23 22:15:30 2020 -0400 snort: enable --dump-rule-meta to work without a conf commit 634dc34e894e0e6dd05568eb6a1184312da5011a Author: russ Date: Sat Mar 14 22:11:23 2020 -0400 snort: add flowbits set and checked to --dump-rule-meta commit dfecdf639ac3027e6fdb0ee9945f59c01490b166 Author: russ Date: Fri Mar 13 19:45:35 2020 -0400 snort: initial implementation of --dump-rule-meta commit b969f1b1b19c9ecdf546828bfd73d80d02d01813 Author: russ Date: Thu Mar 26 11:26:39 2020 -0400 stream_tcp: remove unused session printing cruft commit c9c7b527debe3c6689ffce4e4be5d1caa7b476e2 Author: russ Date: Thu Mar 26 11:15:48 2020 -0400 hyperscan: simplify scratch memory initialization commit 3be58eed3d48ede7c5bd6ae949c25d9f8825e9b2 Author: russ Date: Tue Mar 24 08:49:17 2020 -0400 output: allow error messages in quiet mode commit c63c2cee10e3e924b27719770776b67e31339ad5 Author: russ Date: Mon Mar 23 08:29:27 2020 -0400 session: remove unused IPS option commit d52b37a58007a4e1f8e9f191c96a48529a3aa8d0 Author: russ Date: Sun Mar 22 20:44:10 2020 -0400 snort: remove unused --pcap-reload option commit 616ac76d41aade222a16cf19a7f4634e6f92be9a Author: russ Date: Sun Mar 22 20:22:12 2020 -0400 snort: remove inappropriate fatal errors commit 57ab3b040ba3735eb2f62f442641f49b6ee31ee1 Author: russ Date: Sat Mar 14 12:10:39 2020 -0400 flowbits: refactor implementation ... and 4 more commits --- diff --git a/src/codecs/ip/cd_udp.cc b/src/codecs/ip/cd_udp.cc index 727c98d11..333051de3 100644 --- a/src/codecs/ip/cd_udp.cc +++ b/src/codecs/ip/cd_udp.cc @@ -141,7 +141,7 @@ class UdpModule : public CodecModule { public: UdpModule() : CodecModule(CD_UDP_NAME, CD_UDP_HELP, udp_params) - { + { config = nullptr; } @@ -583,7 +583,7 @@ static void mod_dtor(Module* m) { delete m; } static Codec* ctor(Module* m) -{ +{ UdpModule* mod = (UdpModule*)m; // Codecs can be instantiated without modules. In which case use // the snort defaults for config. diff --git a/src/detection/detection_options.cc b/src/detection/detection_options.cc index c948b7c3f..5c8dd15d1 100644 --- a/src/detection/detection_options.cc +++ b/src/detection/detection_options.cc @@ -360,7 +360,7 @@ int detection_option_node_evaluate( char tmp_noalert_flag = 0; Cursor cursor = orig_cursor; bool continue_loop = true; - char flowbits_setoperation = 0; + bool flowbits_setoperation = false; int loop_count = 0; uint32_t tmp_byte_extract_vars[NUM_IPS_OPTIONS_VARS]; uint64_t cur_eval_context_num = eval_data.p->context->context_num; @@ -443,12 +443,12 @@ int detection_option_node_evaluate( } } - int eval_rtn_result = 0; + bool eval_rtn_result; // Don't include RTN time { RulePause pause(profile); - eval_rtn_result = fpEvalRTN(getRuntimeRtnFromOtn(otn), p, check_ports); + eval_rtn_result = fp_eval_rtn(getRuntimeRtnFromOtn(otn), p, check_ports); } if ( eval_rtn_result ) @@ -515,7 +515,7 @@ int detection_option_node_evaluate( case RULE_OPTION_TYPE_FLOWBIT: if ( node->evaluate ) { - flowbits_setoperation = FlowBits_SetOperation(node->option_data); + flowbits_setoperation = flowbits_setter(node->option_data); if ( flowbits_setoperation ) // set to match so we don't bail early diff --git a/src/detection/fp_detect.cc b/src/detection/fp_detect.cc index 95676a004..f9b42af21 100644 --- a/src/detection/fp_detect.cc +++ b/src/detection/fp_detect.cc @@ -293,37 +293,18 @@ int fpAddMatch(OtnxMatchData* omd, const OptTreeNode* otn) return 0; } -/* -** DESCRIPTION -** Evaluates an RTN against a packet. We can probably get rid of -** the check_ports variable, but it's in there for good luck. :) -** -** FORMAL INPUTS -** RuleTreeNode * - RTN to check packet against. -** Packet * - Packet to evaluate -** int - whether to do a quick enhancement against ports. -** -** FORMAL OUTPUT -** int - 1 if match, 0 if match failed. -*/ -int fpEvalRTN(RuleTreeNode* rtn, Packet* p, int check_ports) +bool fp_eval_rtn(RuleTreeNode* rtn, Packet* p, int check_ports) { - if ( !rtn ) - return 0; + if ( !rtn or !rtn->enabled() ) + return false; if ( rtn->user_mode() ) check_ports = 1; if (!rtn->rule_func->RuleHeadFunc(p, rtn, rtn->rule_func, check_ports)) - { - return 0; - } + return false; - /* - ** Return that there is a rule match and log the event outside - ** of this routine. - */ - return 1; + return true; } int fp_eval_option(void* v, Cursor& c, Packet* p) diff --git a/src/detection/fp_detect.h b/src/detection/fp_detect.h index 4efde0dc3..4148c29be 100644 --- a/src/detection/fp_detect.h +++ b/src/detection/fp_detect.h @@ -50,7 +50,7 @@ extern THREAD_LOCAL snort::ProfileStats rulePerfStats; struct RuleTreeNode; int fpLogEvent(const RuleTreeNode*, const OptTreeNode*, snort::Packet*); -int fpEvalRTN(RuleTreeNode*, snort::Packet*, int check_ports); +bool fp_eval_rtn(RuleTreeNode*, snort::Packet*, int check_ports); int fp_eval_option(void*, Cursor&, snort::Packet*); #define MAX_NUM_RULE_TYPES 16 // max number of allowed rule types diff --git a/src/detection/rules.cc b/src/detection/rules.cc index aca3f338a..1c39b1a66 100644 --- a/src/detection/rules.cc +++ b/src/detection/rules.cc @@ -38,6 +38,22 @@ using namespace snort; +bool operator< (const RuleKey& lhs, const RuleKey& rhs) +{ + if ( lhs.policy_id < rhs.policy_id ) + return true; + + if ( lhs.policy_id == rhs.policy_id ) + { + if ( lhs.gid < rhs.gid ) + return true; + + if ( lhs.gid == rhs.gid and lhs.sid < rhs.sid ) + return true; + } + return false; +} + void RuleStateMap::apply(SnortConfig* sc) { for ( auto it : map ) @@ -58,7 +74,7 @@ void RuleStateMap::apply(SnortConfig* sc) } } else - apply(sc, otn, it.second.policy_id, it.second); + apply(sc, otn, it.first.policy_id, it.second); } } } diff --git a/src/detection/rules.h b/src/detection/rules.h index b31436bf3..33e4ce618 100644 --- a/src/detection/rules.h +++ b/src/detection/rules.h @@ -72,16 +72,15 @@ struct RuleListNode struct RuleKey { + unsigned policy_id; unsigned gid; unsigned sid; - friend bool operator< (const RuleKey& lhs, const RuleKey& rhs) - { return lhs.gid < rhs.gid ? true : (lhs.gid > rhs.gid ? false : (lhs.sid < rhs.sid)); } + friend bool operator< (const RuleKey&, const RuleKey&); }; struct RuleState { - unsigned policy_id; snort::Actions::Type action; IpsPolicy::Enable enable; }; diff --git a/src/detection/signature.cc b/src/detection/signature.cc index f8d5ef51a..62ffe74f0 100644 --- a/src/detection/signature.cc +++ b/src/detection/signature.cc @@ -24,13 +24,18 @@ #include #include +#include #include "signature.h" +#include "framework/decode_data.h" #include "hash/hash_defs.h" #include "hash/ghash.h" +#include "ips_options/ips_flowbits.h" #include "log/messages.h" #include "main/snort_config.h" +#include "main/policy.h" +#include "managers/inspector_manager.h" #include "parser/parser.h" #include "utils/util.h" #include "utils/util_cstring.h" @@ -157,6 +162,7 @@ OptTreeNode::~OptTreeNode() if (detection_filter) snort_free(detection_filter); + delete sigInfo.body; delete[] state; } @@ -223,7 +229,7 @@ void OtnLookupFree(GHash* otn_map) delete otn_map; } -void dump_msg_map(SnortConfig* sc) +void dump_msg_map(const SnortConfig* sc) { GHashNode* ghn = sc->otn_map->find_first(); @@ -245,3 +251,176 @@ void dump_msg_map(SnortConfig* sc) } } +static void get_flow_bits( + const OptTreeNode* otn, std::vector& setters, std::vector& checkers) +{ + OptFpList* p = otn->opt_func; + + while ( p ) + { + if ( p->type == RULE_OPTION_TYPE_FLOWBIT ) + { + bool set; + std::vector bits; + get_flowbits_dependencies(p->ips_opt, set, bits); + + if ( !bits.empty() ) + { + if ( set ) + setters.insert(setters.end(), bits.begin(), bits.end()); + else + checkers.insert(checkers.end(), bits.begin(), bits.end()); + } + } + p = p->next; + } +} + +static void dump_field(const char* key, long val, bool sep = true) +{ if ( sep ) std::cout << ", "; std::cout << key << ": " << val; } + +static void dump_field(const char* key, const std::string& val, bool sep = true) +{ if ( sep ) std::cout << ", "; std::cout << key << ": " << val; } + +static void dump_opt(const char* key, const std::string& val, bool sep = true) +{ + if ( val.empty() ) + return; + + if ( sep ) + std::cout << ", "; + + std::cout << key << ": " << val; +} + +static void dump_info(const SigInfo& si) +{ + dump_field("gid", si.gid, false); + dump_field("sid", si.sid); + dump_field("rev", si.rev); +} + +static void dump_header(const RuleHeader* h) +{ + assert(h); + dump_opt("action", h->action); + dump_opt("src_nets", h->src_nets); + dump_opt("src_ports", h->src_ports); + dump_opt("direction", h->dir); + dump_opt("dst_nets", h->dst_nets); + dump_opt("dst_ports", h->dst_ports); +} + +void dump_rule_meta(const SnortConfig* sc) +{ + GHashNode* ghn = sc->otn_map->find_first(); + + while ( ghn ) + { + const OptTreeNode* otn = (OptTreeNode*)ghn->data; + const SigInfo& si = otn->sigInfo; + + dump_info(si); + + const RuleTreeNode* rtn = otn->proto_nodes[0]; + dump_header(rtn->header); + + dump_field("msg", si.message); + + for ( const auto& svc : si.services ) + dump_field("service", svc.service); + + std::vector setters; + std::vector checkers; + get_flow_bits(otn, setters, checkers); + + for ( const auto& s : setters ) + dump_field("sets", s); + + for ( const auto& s : checkers ) + dump_field("checks", s); + + dump_field("body", *si.body); + + std::cout << std::endl; + ghn = sc->otn_map->find_next(); + } +} + +void dump_rule_state(const SnortConfig* sc) +{ + GHashNode* ghn = sc->otn_map->find_first(); + + while ( ghn ) + { + const OptTreeNode* otn = (OptTreeNode*)ghn->data; + const SigInfo& si = otn->sigInfo; + + dump_field("gid", si.gid, false); + dump_field("sid", si.sid); + dump_field("rev", si.rev); + + for ( unsigned i = 0; i < otn->proto_node_num; ++i ) + { + const RuleTreeNode* rtn = otn->proto_nodes[i]; + + if ( !rtn ) + continue; + + auto pid = snort::get_ips_policy(sc, i)->user_policy_id; + dump_field("policy", pid); + + const char* s = Actions::get_string(rtn->action); + dump_field("action", s); + + s = rtn->enabled() ? "enabled" : "disabled"; + dump_field("state", s); + } + std::cout << std::endl; + ghn = sc->otn_map->find_next(); + } +} + +using SvcMap = std::unordered_map>; + +static SvcMap get_dependencies() +{ + SvcMap map; + std::vector apis = InspectorManager::get_apis(); + + for ( const auto* p : apis ) + { + if ( !p->service ) + continue; + + std::vector& v = map[p->service]; + v.emplace_back(p->base.name); + + // FIXIT-L need NHI to advertise dependency on H2I + if ( !strcmp(p->base.name, "http2_inspect") ) + v.emplace_back("http_inspect"); + + if ( p->proto_bits & (PROTO_BIT__TCP|PROTO_BIT__PDU) ) + v.emplace_back("stream_tcp"); + + if ( p->proto_bits & PROTO_BIT__UDP ) + v.emplace_back("stream_udp"); + } + return map; +} + +void dump_rule_deps(const SnortConfig*) +{ + SvcMap map = get_dependencies(); + + for ( const auto& it : map ) + { + dump_field("service", it.first, false); + + for ( const auto& s : it.second ) + dump_field("requires", s); + + std::cout << std::endl; + } +} + diff --git a/src/detection/signature.h b/src/detection/signature.h index e2bc047c9..1c56ccf79 100644 --- a/src/detection/signature.h +++ b/src/detection/signature.h @@ -91,6 +91,8 @@ enum Target struct SigInfo { std::string message; + std::string* body = nullptr; + std::vector refs; std::vector services; @@ -115,7 +117,10 @@ void OtnRemove(snort::GHash*, OptTreeNode*); OptTreeNode* GetOTN(uint32_t gid, uint32_t sid); -void dump_msg_map(snort::SnortConfig*); +void dump_msg_map(const snort::SnortConfig*); +void dump_rule_deps(const snort::SnortConfig*); +void dump_rule_meta(const snort::SnortConfig*); +void dump_rule_state(const snort::SnortConfig*); #endif diff --git a/src/detection/treenodes.h b/src/detection/treenodes.h index 7449124f9..8451484c9 100644 --- a/src/detection/treenodes.h +++ b/src/detection/treenodes.h @@ -88,6 +88,19 @@ struct RuleFpList RuleFpList* next = nullptr; }; +struct RuleHeader +{ + RuleHeader(const char* s) : action(s) { } + + std::string action; + std::string proto; + std::string src_nets; + std::string src_ports; + std::string dir; + std::string dst_nets; + std::string dst_ports; +}; + // one of these per rule per policy // represents head part of rule struct RuleTreeNode @@ -103,6 +116,7 @@ struct RuleTreeNode static constexpr Flag USER_MODE = 0x80; RuleFpList* rule_func = nullptr; /* match functions.. (Bidirectional etc.. ) */ + RuleHeader* header = nullptr; sfip_var_t* sip = nullptr; sfip_var_t* dip = nullptr; @@ -175,7 +189,6 @@ struct OptTreeNode unsigned evalIndex = 0; /* where this rule sits in the evaluation sets */ unsigned ruleIndex = 0; // unique index uint32_t num_detection_opts = 0; - uint32_t plugins = 0; SnortProtocolId snort_protocol_id = 0; // Added for integrity checks during rule parsing. unsigned short proto_node_num = 0; uint16_t longestPatternLen = 0; @@ -226,12 +239,6 @@ namespace snort SO_PUBLIC bool otn_has_plugin(OptTreeNode* otn, const char* name); } -inline bool otn_has_plugin(OptTreeNode* otn, int id) -{ return (otn->plugins & (0x1 << id)) != 0; } - -inline void otn_set_plugin(OptTreeNode* otn, int id) -{ otn->plugins |= (0x1 << id); } - bool otn_set_agent(OptTreeNode*, snort::IpsOption*); void otn_trigger_actions(const OptTreeNode*, snort::Packet*); diff --git a/src/flow/flow.cc b/src/flow/flow.cc index ffac79d04..d7ea28f06 100644 --- a/src/flow/flow.cc +++ b/src/flow/flow.cc @@ -27,10 +27,10 @@ #include "flow/ha.h" #include "flow/session.h" #include "framework/data_bus.h" +#include "helpers/bitop.h" #include "ips_options/ips_flowbits.h" #include "protocols/packet.h" #include "sfip/sf_ip.h" -#include "utils/bitop.h" #include "utils/stats.h" #include "utils/util.h" diff --git a/src/flow/flow_data.cc b/src/flow/flow_data.cc index af6f9b017..470675aed 100644 --- a/src/flow/flow_data.cc +++ b/src/flow/flow_data.cc @@ -66,6 +66,7 @@ void FlowData::update_deallocations(size_t n) mem_in_use -= n; } -RuleFlowData::RuleFlowData(unsigned u) : FlowData(u, - SnortConfig::get_conf()->so_rules->proxy) { } +RuleFlowData::RuleFlowData(unsigned u) : + FlowData(u, SnortConfig::get_conf()->so_rules->proxy) +{ } diff --git a/src/helpers/CMakeLists.txt b/src/helpers/CMakeLists.txt index 0503609d4..754a9257b 100644 --- a/src/helpers/CMakeLists.txt +++ b/src/helpers/CMakeLists.txt @@ -22,6 +22,7 @@ add_library (helpers OBJECT ${HELPERS_INCLUDES} ${HYPER_SOURCES} base64_encoder.cc + bitop.h boyer_moore_search.cc chunk.cc chunk.h diff --git a/src/utils/bitop.h b/src/helpers/bitop.h similarity index 58% rename from src/utils/bitop.h rename to src/helpers/bitop.h index d175d28b5..d485f2d57 100644 --- a/src/utils/bitop.h +++ b/src/helpers/bitop.h @@ -25,90 +25,64 @@ // A simple, dynamically sized bit vector implementation -#include #include -#include +#include class BitOp { public: - BitOp(size_t); - ~BitOp(); + BitOp(size_t bit) + { bit_buf.resize(index(bit) + 1); } + + ~BitOp() = default; BitOp(const BitOp&) = delete; BitOp& operator=(const BitOp&) = delete; - void reset(); void set(unsigned int bit); bool is_set(unsigned int bit) const; void clear(unsigned int bit); - size_t size() const; +private: + size_t size() const + { return bit_buf.size(); } + + size_t index(size_t bit) const + { return (bit + 7) >> 3; } - // FIXIT-L add operator overloads for [], &=, |=, etc - size_t get_buf_size() const; - uint8_t& get_buf_element(size_t); - const uint8_t& get_buf_element(size_t) const; + uint8_t& byte(size_t bit) + { return bit_buf[index(bit)]; } -private: - uint8_t mask(size_t bit) const; + uint8_t mask(size_t bit) const + { return (uint8_t)(0x80 >> (bit & 7)); } - uint8_t* bit_buf; - const size_t buf_size; + std::vector bit_buf; }; // ----------------------------------------------------------------------------- // implementation // ----------------------------------------------------------------------------- -inline BitOp::BitOp(size_t len) : - buf_size(len ? (len + 7) >> 3 : 1) -{ - bit_buf = new uint8_t[len](); -} - -inline BitOp::~BitOp() -{ delete[] bit_buf; } - -inline uint8_t BitOp::mask(size_t bit) const -{ return (uint8_t)(0x80 >> (bit & 7)); } - -// Reset the bit buffer so that it can be reused -inline void BitOp::reset() -{ memset(bit_buf, 0, buf_size); } - -// Set the bit in the specified position within the bit buffer. inline void BitOp::set(unsigned int bit) { - assert(size() > bit); - bit_buf[bit >> 3] |= mask(bit); + if ( index(bit) >= size() ) + bit_buf.resize(index(bit) + 1); + byte(bit) |= mask(bit); } -// Checks if the bit at the specified position is set inline bool BitOp::is_set(unsigned int bit) const { - assert(size() > bit); - return mask(bit) & bit_buf[bit >> 3]; + if ( index(bit) >= size() ) + return false; + return bit_buf[index(bit)] & mask(bit); } -// Clear the bit in the specified position within the bit buffer. inline void BitOp::clear(unsigned int bit) { - assert(size() > bit); - bit_buf[bit >> 3] &= ~mask(bit); + if ( index(bit) >= size() ) + return; + byte(bit) &= ~mask(bit); } -inline size_t BitOp::size() const -{ return buf_size << 3; } - -inline size_t BitOp::get_buf_size() const -{ return buf_size; } - -inline uint8_t& BitOp::get_buf_element(size_t i) -{ return bit_buf[i]; } - -inline const uint8_t& BitOp::get_buf_element(size_t i) const -{ return bit_buf[i]; } - #endif diff --git a/src/helpers/test/CMakeLists.txt b/src/helpers/test/CMakeLists.txt index 07e1b103d..9b989cb6d 100644 --- a/src/helpers/test/CMakeLists.txt +++ b/src/helpers/test/CMakeLists.txt @@ -14,3 +14,5 @@ if ( HAVE_HYPERSCAN ) ) endif() +add_catch_test( bitop_test ) + diff --git a/src/utils/test/bitop_test.cc b/src/helpers/test/bitop_test.cc similarity index 54% rename from src/utils/test/bitop_test.cc rename to src/helpers/test/bitop_test.cc index 7f1c7efa3..b8acf7be1 100644 --- a/src/utils/test/bitop_test.cc +++ b/src/helpers/test/bitop_test.cc @@ -25,49 +25,69 @@ #include "../bitop.h" -static bool t_bitop_buffer_zero(BitOp& bitop) +static unsigned num_set(BitOp& bitop, size_t max) { - for ( size_t i = 0; i < bitop.get_buf_size(); ++i ) - if ( bitop.get_buf_element(i) ) - return false; + unsigned c = 0; - return true; + for ( size_t i = 0; i < max; ++i ) + { + if ( bitop.is_set(i) ) + c++; + } + return c; } +static bool is_clear(BitOp& bitop, size_t max) +{ return num_set(bitop, max) == 0; } + TEST_CASE( "bitop", "[bitop]" ) { - BitOp bitop(24); + const size_t max = 16; + BitOp bitop(max); SECTION( "zero-initialized" ) { - CHECK( (t_bitop_buffer_zero(bitop) == true) ); + CHECK( (is_clear(bitop, max) == true) ); } - SECTION( "reset" ) + SECTION( "toggle" ) { - bitop.get_buf_element(0) = 0xff; - bitop.reset(); + const size_t bit = 7; + + bitop.set(bit); + CHECK(bitop.is_set(bit)); + + CHECK(num_set(bitop, max) == 1); + + bitop.clear(bit); + CHECK(!bitop.is_set(bit)); - CHECK( (t_bitop_buffer_zero(bitop) == true) ); + CHECK( (is_clear(bitop, max) == true) ); } - SECTION( "set/is_set/clear" ) + SECTION( "over size" ) { - bitop.set(6); + const size_t j = max / 2; + const size_t k = max + 2; - CHECK( (bitop.get_buf_element(0) == 0x02) ); + bitop.set(j); + CHECK(bitop.is_set(j)); - CHECK( bitop.is_set(6) ); - CHECK_FALSE( bitop.is_set(7) ); + CHECK(!bitop.is_set(k)); - bitop.set(7); - bitop.clear(6); + bitop.set(k); + CHECK(bitop.is_set(k)); - CHECK( bitop.get_buf_element(0) == 0x01 ); - } + CHECK(num_set(bitop, k + 2) == 2); + CHECK(bitop.is_set(j)); - SECTION( "size" ) - { - CHECK( (bitop.size() == 24) ); + bitop.clear(k); + CHECK(!bitop.is_set(k)); + + CHECK(bitop.is_set(j)); + bitop.clear(j); + + CHECK( (is_clear(bitop, k + 2) == true) ); } } + diff --git a/src/ips_options/CMakeLists.txt b/src/ips_options/CMakeLists.txt index a1d56704f..65a0af88c 100644 --- a/src/ips_options/CMakeLists.txt +++ b/src/ips_options/CMakeLists.txt @@ -37,7 +37,6 @@ SET( PLUGIN_LIST ips_rev.cc ips_rpc.cc ips_seq.cc - ips_session.cc ips_sid.cc ips_soid.cc ips_tag.cc @@ -129,7 +128,6 @@ else (STATIC_IPS_OPTIONS) add_dynamic_module(ips_rpc ips_options ips_rpc.cc) add_dynamic_module(ips_sid ips_options ips_sid.cc) add_dynamic_module(ips_seq ips_options ips_seq.cc) - add_dynamic_module(ips_session ips_options ips_session.cc) add_dynamic_module(ips_soid ips_options ips_soid.cc) add_dynamic_module(ips_tag ips_options ips_tag.cc) add_dynamic_module(ips_target ips_options ips_target.cc) diff --git a/src/ips_options/ips_classtype.cc b/src/ips_options/ips_classtype.cc index 4c03736d9..70f842ba8 100644 --- a/src/ips_options/ips_classtype.cc +++ b/src/ips_options/ips_classtype.cc @@ -25,6 +25,7 @@ #include "framework/decode_data.h" #include "framework/ips_option.h" #include "framework/module.h" +#include "main/snort_config.h" using namespace snort; @@ -65,6 +66,12 @@ bool ClassTypeModule::set(const char*, Value& v, SnortConfig* sc) type = get_classification(sc, v.get_string()); + if ( !type and sc->dump_rule_info() ) + { + const char* s = v.get_string(); + add_classification(sc, s, s, 1); + type = get_classification(sc, s); + } return type != nullptr; } diff --git a/src/ips_options/ips_flowbits.cc b/src/ips_options/ips_flowbits.cc index 7078efd78..80e8f6325 100644 --- a/src/ips_options/ips_flowbits.cc +++ b/src/ips_options/ips_flowbits.cc @@ -1,6 +1,5 @@ //-------------------------------------------------------------------------- // Copyright (C) 2014-2020 Cisco and/or its affiliates. All rights reserved. -// Copyright (C) 2003-2013 Sourcefire, Inc. // // 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 @@ -25,19 +24,16 @@ #include "ips_flowbits.h" -#include -#include -#include +#include #include "framework/ips_option.h" #include "framework/module.h" -#include "hash/ghash.h" #include "hash/hash_defs.h" #include "hash/hash_key_operations.h" +#include "helpers/bitop.h" #include "log/messages.h" #include "protocols/packet.h" #include "profiler/profiler.h" -#include "utils/bitop.h" #include "utils/sflsq.h" #include "utils/util.h" @@ -45,97 +41,104 @@ using namespace snort; #define s_name "flowbits" -static THREAD_LOCAL ProfileStats flowBitsPerfStats; - -#define ALLOWED_SPECIAL_CHARS ".-_" - -#define FLOWBITS_SET 0x01 -#define FLOWBITS_UNSET 0x02 -#define FLOWBITS_TOGGLE 0x04 -#define FLOWBITS_ISSET 0x08 -#define FLOWBITS_ISNOTSET 0x10 -#define FLOWBITS_RESET 0x20 -#define FLOWBITS_NOALERT 0x40 -#define FLOWBITS_SETX 0x80 - -/** -** The FLOWBITS_OBJECT is used to track the different -** flowbit names that set/unset/etc. bits. We use these -** so that we can verify that the rules that use flowbits -** make sense. -** -** The types element tracks all the different operations that -** may occur for a given object. This is different from how -** the type element is used from the FLOWBITS_OP structure. -*/ -struct FLOWBITS_OBJECT +struct FlowBit { - uint16_t id = 0; - uint8_t types = 0; - int toggle = 0; - int set = 0; - int isset = 0; + uint16_t id = 65535; + uint16_t sets = 0; + uint16_t checks = 0; + + bool is_new() + { return id == 65535; } }; -typedef enum -{ - FLOWBITS_AND, - FLOWBITS_OR, - FLOWBITS_ANY, - FLOWBITS_ALL -}Flowbits_eval; - -/** -** This class is the context ptr for each detection option -** on a rule. The id is associated with a FLOWBITS_OBJECT id. -** -** The type element track only one operation. -*/ -class FLOWBITS_OP +static std::vector bit_keys; +static std::unordered_map bit_map; +static THREAD_LOCAL ProfileStats flowbits_profile; + +//-------------------------------------------------------------------------- +// flowbits option config +//-------------------------------------------------------------------------- + +struct FlowBitCheck { -public: - std::string name; - std::string group; + enum Op { SET, UNSET, IS_SET, IS_NOT_SET, NO_ALERT }; - std::vector ids; - Flowbits_eval eval = FLOWBITS_AND; + FlowBitCheck(Op t) : type(t) { } + bool validate(); - uint32_t group_id = 0; - uint8_t type = 0; /* Set, Unset, Invert, IsSet, IsNotSet, Reset */ -}; + bool is_setter() + { return type == SET or type == UNSET; } -struct FLOWBITS_GRP -{ - std::string name; - BitOp* GrpBitOp = nullptr; + bool is_checker() + { return type == IS_SET or type == IS_NOT_SET; } - uint32_t group_id = 0; + void add(uint16_t); - uint16_t count = 0; - uint16_t max_id = 0; + std::vector ids; + uint16_t max = 0; + bool or_bits = false; + Op type; }; -struct FlowBitState +void FlowBitCheck::add(uint16_t id) { - std::forward_list op_list; - GHash* flowbits_hash = nullptr; - GHash* flowbits_grp_hash = nullptr; - SF_QUEUE* flowbits_bit_queue = nullptr; - unsigned flowbits_count = 0; - unsigned flowbits_grp_count = 0; - int flowbits_toggle = 1; -}; + ids.push_back(id); + if ( id > max ) + max = id; +} + +bool FlowBitCheck::validate() +{ + switch ( type ) + { + case SET: + if ( !or_bits and !ids.empty() ) + return true; + + ParseError("%s: set uses syntax: flowbits:set,bit[&bit].", s_name); + break; + + case UNSET: + if ( !or_bits and !ids.empty() ) + return true; + + ParseError("%s: unset uses syntax: flowbits:unset,bit[&bit].", s_name); + break; + + case IS_SET: + if ( !ids.empty() ) + return true; -// Forward declarations -static void free_item(void*); -static void free_group(void*); + ParseError("%s: isset uses syntax: flowbits:isset,bit[&bit] OR " + "flowbits:isset,bit[|bit].", s_name); + break; + + case IS_NOT_SET: + if ( !ids.empty() ) + return true; + + ParseError("%s: isnotset uses syntax: flowbits:isnotset,bit[&bit] OR " + "flowbits:isnotset,bit[|bit]", s_name); + break; -static IpsOption::EvalStatus check_flowbits(FLOWBITS_OP*, Packet*); + case NO_ALERT: + if ( ids.empty() ) + return true; + + ParseError("%s: noalert uses syntax: flowbits:noalert.", s_name); + break; + } + return false; +} + +//-------------------------------------------------------------------------- +// flowbits option config +//-------------------------------------------------------------------------- class FlowBitsOption : public IpsOption { public: - FlowBitsOption(FLOWBITS_OP* c) : + FlowBitsOption(FlowBitCheck* c) : IpsOption(s_name, RULE_OPTION_TYPE_FLOWBIT), config(c) { } @@ -146,11 +149,16 @@ public: EvalStatus eval(Cursor&, Packet*) override; - bool is_set(uint8_t bits) - { return (config->type & bits) != 0; } + bool is_setter() + { return config->is_setter(); } + + void get_dependencies(bool& set, std::vector& bits); private: - FLOWBITS_OP* config; + bool is_set(BitOp*); + +private: + FlowBitCheck* config; }; //------------------------------------------------------------------------- @@ -165,11 +173,11 @@ FlowBitsOption::~FlowBitsOption() uint32_t FlowBitsOption::hash() const { uint32_t a,b,c; - const FLOWBITS_OP* data = config; + const FlowBitCheck* data = config; unsigned i; unsigned j = 0; - a = data->eval; + a = data->or_bits ? 1 : 0; b = data->type; c = 0; @@ -198,8 +206,6 @@ uint32_t FlowBitsOption::hash() const b += data->ids[data->ids.size() - 1]|data->ids.size() << 16; } - c += data->group_id; - finalize(a,b,c); return c; @@ -213,9 +219,8 @@ bool FlowBitsOption::operator==(const IpsOption& ips) const const FlowBitsOption& rhs = (const FlowBitsOption&)ips; if ( (config->ids.size() != rhs.config->ids.size()) or - (config->eval != rhs.config->eval) or - (config->type != rhs.config->type) or - (config->group_id != rhs.config->group_id) ) + (config->or_bits != rhs.config->or_bits) or + (config->type != rhs.config->type) ) return false; for ( unsigned i = 0; i < config->ids.size(); i++ ) @@ -229,375 +234,138 @@ bool FlowBitsOption::operator==(const IpsOption& ips) const IpsOption::EvalStatus FlowBitsOption::eval(Cursor&, Packet* p) { - RuleProfile profile(flowBitsPerfStats); - return check_flowbits(config, p); -} - -//------------------------------------------------------------------------- -// helper methods -//------------------------------------------------------------------------- - -static inline BitOp* get_flow_bitop(const Packet* p, FlowBitState* flowbit_state) -{ - Flow* flow = p->flow; - - if (!flow) - return nullptr; - - if ( !flow->bitop ) - flow->bitop = new BitOp(flowbit_state->flowbits_count); - - return flow->bitop; -} - -static inline int clear_group_bit( - BitOp* bitop, const std::string& group, FlowBitState* flowbit_state) -{ - if ( group.empty() ) - return 0; - - // FIXIT-M why is the hash lookup done at runtime for flowbits groups? - // a pointer to flowbits_grp should be in flowbits config data - // this *should* be safe but iff splay mode is disabled - auto flowbits_grp = (FLOWBITS_GRP*)flowbit_state->flowbits_grp_hash->find(group.c_str()); - - if ( !flowbits_grp ) - return 0; + RuleProfile profile(flowbits_profile); - if ( !bitop or (bitop->size() <= flowbits_grp->max_id) or !flowbits_grp->count ) - return 0; + if ( !p->flow ) + return IpsOption::NO_MATCH; - auto GrpBitOp = flowbits_grp->GrpBitOp; + BitOp* bitop = p->flow->bitop; - /* note, max_id is an index, not a count. - * Calculate max_bytes by adding 8 to max_id, then dividing by 8. */ - unsigned int max_bytes = (flowbits_grp->max_id + 8) >> 3; + // do ops that don't require a bit + switch ( config->type ) + { + case FlowBitCheck::SET: + break; - for ( unsigned int i = 0; i < max_bytes; i++ ) - bitop->get_buf_element(i) &= ~GrpBitOp->get_buf_element(i); + case FlowBitCheck::UNSET: + if ( !bitop ) + return IpsOption::MATCH; - return 1; -} + for ( auto id : config->ids ) + bitop->clear(id); -static inline int toggle_group_bit( - BitOp* bitop, const std::string& group, FlowBitState* flowbit_state) -{ - if ( group.empty() ) - return 0; + return IpsOption::MATCH; - auto flowbits_grp = (FLOWBITS_GRP*)flowbit_state->flowbits_grp_hash->find(group.c_str()); + case FlowBitCheck::IS_SET: + if ( !bitop ) + return IpsOption::FAILED_BIT; - if ( !flowbits_grp ) - return 0; + if ( is_set(bitop) ) + return IpsOption::MATCH; - if ( !bitop or (bitop->size() <= flowbits_grp->max_id) or !flowbits_grp->count ) - return 0; + return IpsOption::FAILED_BIT; - auto GrpBitOp = flowbits_grp->GrpBitOp; + case FlowBitCheck::IS_NOT_SET: + if ( !bitop or !is_set(bitop) ) + return IpsOption::MATCH; - /* note, max_id is an index, not a count. - * Calculate max_bytes by adding 8 to max_id, then dividing by 8. */ - unsigned int max_bytes = (flowbits_grp->max_id + 8) >> 3; - for ( unsigned int i = 0; i < max_bytes; i++ ) - bitop->get_buf_element(i) ^= GrpBitOp->get_buf_element(i); + return IpsOption::FAILED_BIT; - return 1; -} + case FlowBitCheck::NO_ALERT: + return IpsOption::NO_ALERT; + } -static inline int set_xbits_to_group( - BitOp* bitop, FLOWBITS_OP* fb, FlowBitState* flowbit_state) -{ - if ( !clear_group_bit(bitop, fb->group, flowbit_state) ) - return 0; + // do ops that require a bit (set) + if ( !bitop ) + bitop = p->flow->bitop = new BitOp(config->max); - for ( auto id : fb->ids ) + for ( auto id : config->ids ) bitop->set(id); - return 1; + return IpsOption::MATCH; } -static inline int is_set_flowbits( - BitOp* bitop, FLOWBITS_OP* fb, FlowBitState* flowbit_state) +bool FlowBitsOption::is_set(BitOp* bitop) { - FLOWBITS_GRP* flowbits_grp; - - switch ( fb->eval ) + if ( !config->or_bits ) { - case FLOWBITS_AND: - for ( auto id : fb->ids ) + for ( auto id : config->ids ) { if ( !bitop->is_set(id) ) - return 0; - } - return 1; - - case FLOWBITS_OR: - for ( auto id : fb->ids ) - { - if ( bitop->is_set(id) ) - return 1; - } - return 0; - - case FLOWBITS_ALL: - flowbits_grp = (FLOWBITS_GRP*)flowbit_state->flowbits_grp_hash->find(fb->group.c_str()); - - if ( !flowbits_grp ) - return 0; - - for ( unsigned i = 0; i <= (unsigned int)(flowbits_grp->max_id >>3); i++ ) - { - uint8_t val = bitop->get_buf_element(i) & flowbits_grp->GrpBitOp->get_buf_element(i); - - if ( val != flowbits_grp->GrpBitOp->get_buf_element(i) ) - return 0; - } - return 1; - - case FLOWBITS_ANY: - flowbits_grp = (FLOWBITS_GRP*)flowbit_state->flowbits_grp_hash->find(fb->group.c_str()); - - if ( !flowbits_grp ) - return 0; - - for ( unsigned i = 0; i <= (unsigned int)(flowbits_grp->max_id >>3); i++ ) - { - uint8_t val = bitop->get_buf_element(i) & flowbits_grp->GrpBitOp->get_buf_element(i); - if ( val ) - return 1; + return false; } - return 0; - - default: - return 0; + return true; + } + for ( auto id : config->ids ) + { + if ( bitop->is_set(id) ) + return true; } + return false; } -static IpsOption::EvalStatus check_flowbits(FLOWBITS_OP* fb, Packet* p) +void FlowBitsOption::get_dependencies(bool& set, std::vector& bits) { - int result = 0; - - FlowBitState* flowbit_state = SnortConfig::get_conf()->flowbit_state; - assert(flowbit_state != nullptr); + set = config->is_setter(); - BitOp* bitop = get_flow_bitop(p, flowbit_state); - if (!bitop) - return IpsOption::NO_MATCH; - - switch (fb->type) + for ( auto id : config->ids ) { - case FLOWBITS_SET: - for ( auto id : fb->ids ) - bitop->set(id); - result = 1; - break; - - case FLOWBITS_SETX: - result = set_xbits_to_group(bitop, fb, flowbit_state); - break; - - case FLOWBITS_UNSET: - if (fb->eval == FLOWBITS_ALL ) - clear_group_bit(bitop, fb->group, flowbit_state); - else - { - for ( auto id : fb->ids ) - bitop->clear(id); - } - result = 1; - break; - - case FLOWBITS_RESET: - if ( fb->group.empty() ) - bitop->reset(); - else - clear_group_bit(bitop, fb->group, flowbit_state); - - result = 1; - break; - - case FLOWBITS_ISSET: - if ( is_set_flowbits(bitop, fb, flowbit_state) ) - result = 1; - else - return IpsOption::FAILED_BIT; - break; - - case FLOWBITS_ISNOTSET: - if ( !is_set_flowbits(bitop, fb, flowbit_state) ) - result = 1; - else - return IpsOption::FAILED_BIT; - break; - - case FLOWBITS_TOGGLE: - if ( !fb->group.empty() ) - toggle_group_bit(bitop, fb->group, flowbit_state); - - else for ( auto id : fb->ids ) - { - if (bitop->is_set(id)) - bitop->clear(id); - else - bitop->set(id); - } - result = 1; - - break; - - case FLOWBITS_NOALERT: - /* - ** This logic allows us to put flowbits: noalert any where - ** in the detection chain, and still do bit ops after this - ** option. - */ - return IpsOption::NO_ALERT; - - default: - return IpsOption::NO_MATCH; + assert(id < bit_keys.size()); + bits.emplace_back(bit_keys[id]); } - - if (result == 1) - return IpsOption::MATCH; - - return IpsOption::NO_MATCH; } //------------------------------------------------------------------------- // public methods //------------------------------------------------------------------------- -void flowbits_ginit(SnortConfig* sc) -{ - sc->flowbit_state = new FlowBitState; - sc->flowbit_state->flowbits_hash = new GHash(10000, 0, 0, free_item); - - // this is used during parse time and runtime so do NOT - // enable splay mode (which is NOT useful here anyway) - sc->flowbit_state->flowbits_grp_hash = new GHash(10000, 0, 0, free_group); - sc->flowbit_state->flowbits_bit_queue = sfqueue_new(); -} -void flowbits_gterm(SnortConfig* sc) +bool flowbits_setter(void* option_data) { - FlowBitState* flowbit_state = sc->flowbit_state; - if (flowbit_state == nullptr) - return; - - if ( flowbit_state->flowbits_hash ) - delete flowbit_state->flowbits_hash; - - if ( flowbit_state->flowbits_grp_hash ) - delete flowbit_state->flowbits_grp_hash; - - if ( flowbit_state->flowbits_bit_queue ) - sfqueue_free_all(flowbit_state->flowbits_bit_queue, nullptr); - - delete flowbit_state; - flowbit_state = nullptr; + FlowBitsOption* p = (FlowBitsOption*)option_data; + return p->is_setter(); } -int FlowBits_SetOperation(void* option_data) +void get_flowbits_dependencies(void* option_data, bool& set, std::vector& bits) { FlowBitsOption* p = (FlowBitsOption*)option_data; - - if (p->is_set(FLOWBITS_SET | FLOWBITS_SETX |FLOWBITS_UNSET | FLOWBITS_TOGGLE | - FLOWBITS_RESET)) - { - return 1; - } - return 0; + p->get_dependencies(set, bits); } //------------------------------------------------------------------------- // parsing methods //------------------------------------------------------------------------- -static bool validate_name(const char* name) +static FlowBit* get_bit( + const char* bit, FlowBitCheck* check) { - assert(name); + FlowBit& flow_bit = bit_map[bit]; - for ( unsigned i=0; iflowbits_hash->find(bit); - - if ( !flowbits_item ) - { - flowbits_item = new FLOWBITS_OBJECT; - - if (sfqueue_count(flowbit_state->flowbits_bit_queue) > 0) - { - flowbits_item->id = (uint16_t)(uintptr_t)sfqueue_remove( - flowbit_state->flowbits_bit_queue); - } - else - { - flowbits_item->id = flowbit_state->flowbits_count++; - - if ( !flowbit_state->flowbits_count ) - { - ParseError("The number of flowbit IDs in the current ruleset exceeds " - "the maximum number of IDs that are allowed (%u).", - flowbit_state->flowbits_count-1); - } - } - int hstatus = flowbit_state->flowbits_hash->insert(bit, flowbits_item); + if ( check->is_setter() ) + flow_bit.sets++; - if (hstatus != HASH_OK) - ParseError("Could not add flowbits key (%s) to hash.", bit); - } - flowbits_item->toggle = flowbit_state->flowbits_toggle; - flowbits_item->types |= flowbits->type; + else if ( check->is_checker() ) + flow_bit.checks++; - switch (flowbits->type) - { - case FLOWBITS_SET: - case FLOWBITS_SETX: - case FLOWBITS_UNSET: - case FLOWBITS_TOGGLE: - case FLOWBITS_RESET: - flowbits_item->set++; - break; - case FLOWBITS_ISSET: - case FLOWBITS_ISNOTSET: - flowbits_item->isset++; - break; - default: - break; - } - - return flowbits_item; + return &flow_bit; } -static void parse_flowbits( - const char* flowbits_names, FLOWBITS_OP* flowbits, FlowBitState* flowbit_state) +static bool parse_flowbits(const char* flowbits_names, FlowBitCheck* check) { - FLOWBITS_OBJECT* flowbits_item; - - if ( !flowbits_names or ((*flowbits_names) == 0) ) - return; + assert(flowbits_names); + FlowBit* flow_bit; if ( strchr(flowbits_names, '|') ) { if ( strchr(flowbits_names, '&') ) { ParseError("%s: tag id opcode '|' and '&' are used together.", s_name); - return; + return false; } std::string bits = flowbits_names; std::replace(bits.begin(), bits.end(), '|', ' '); @@ -606,10 +374,10 @@ static void parse_flowbits( while ( ss >> tok ) { - flowbits_item = get_item(tok.c_str(), flowbits, flowbit_state); - flowbits->ids.push_back(flowbits_item->id); + flow_bit = get_bit(tok.c_str(), check); + check->add(flow_bit->id); } - flowbits->eval = FLOWBITS_OR; + check->or_bits = true; } else if ( strchr(flowbits_names, '&') ) { @@ -620,339 +388,59 @@ static void parse_flowbits( while ( ss >> tok ) { - flowbits_item = get_item(tok.c_str(), flowbits, flowbit_state); - flowbits->ids.push_back(flowbits_item->id); - } - flowbits->eval = FLOWBITS_AND; - } - else if ( !strcasecmp(flowbits_names, "all") ) - { - flowbits->eval = FLOWBITS_ALL; - } - else if ( !strcasecmp(flowbits_names, "any") ) - { - flowbits->eval = FLOWBITS_ANY; - } - else - { - flowbits_item = get_item(flowbits_names, flowbits, flowbit_state); - flowbits->ids.push_back(flowbits_item->id); - } -} - -static void validateFlowbitsSyntax(FLOWBITS_OP* flowbits) -{ - switch (flowbits->type) - { - case FLOWBITS_SET: - if ( (flowbits->eval == FLOWBITS_AND) and !flowbits->ids.empty() ) - break; - - ParseError("%s: operation set uses syntax: flowbits:set,bit[&bit],[group].", s_name); - return; - - case FLOWBITS_SETX: - if ( (flowbits->eval == FLOWBITS_AND) and !flowbits->group.empty() and - !flowbits->ids.empty() ) - break; - - ParseError("%s: operation setx uses syntax: flowbits:setx,bit[&bit],group.", s_name); - return; - - case FLOWBITS_UNSET: - if (((flowbits->eval == FLOWBITS_AND) and flowbits->group.empty() and !flowbits->ids.empty()) - or ((flowbits->eval == FLOWBITS_ALL) and !flowbits->group.empty())) - break; - - ParseError("%s: operation unset uses syntax: flowbits:unset,bit[&bit] OR" - " flowbits:unset, all, group.", s_name); - return; - - case FLOWBITS_TOGGLE: - if (((flowbits->eval == FLOWBITS_AND) and flowbits->group.empty() and !flowbits->ids.empty()) - or ((flowbits->eval == FLOWBITS_ALL) and !flowbits->group.empty())) - break; - - ParseError("%s: operation toggle uses syntax: flowbits:toggle,bit[&bit] OR" - " flowbits:toggle,all,group.", s_name); - return; - - case FLOWBITS_ISSET: - if ((((flowbits->eval == FLOWBITS_AND) or (flowbits->eval == FLOWBITS_OR)) and - flowbits->group.empty() and !flowbits->ids.empty()) - or (((flowbits->eval == FLOWBITS_ANY) or (flowbits->eval == FLOWBITS_ALL)) and - !flowbits->group.empty())) - break; - - ParseError("%s: operation isset uses syntax: flowbits:isset,bit[&bit] OR " - "flowbits:isset,bit[|bit] OR flowbits:isset,all,group OR flowbits:isset,any,group.", - s_name); - return; - - case FLOWBITS_ISNOTSET: - if ((((flowbits->eval == FLOWBITS_AND) or (flowbits->eval == FLOWBITS_OR)) and - flowbits->group.empty() and !flowbits->ids.empty()) - or ((((flowbits->eval == FLOWBITS_ANY)) or (flowbits->eval == FLOWBITS_ALL)) and - !flowbits->group.empty())) - break; - - ParseError("%s: operation isnotset uses syntax: flowbits:isnotset,bit[&bit] OR " - "flowbits:isnotset,bit[|bit] OR flowbits:isnotset,all,group OR " - "flowbits:isnotset,any,group.", s_name); - return; - - case FLOWBITS_RESET: - if ( flowbits->ids.empty() ) - break; - - ParseError( - "%s: operation unset uses syntax: flowbits:reset OR flowbits:reset, group.", s_name); - return; - - case FLOWBITS_NOALERT: - if ( flowbits->ids.empty() and flowbits->group.empty() ) - break; - - ParseError("%s: operation noalert uses syntax: flowbits:noalert.", s_name); - return; - - default: - ParseError("%s: unknown opcode.", s_name); - return; - } -} - -static FLOWBITS_GRP* get_group(const char* group, FlowBitState* flowbit_state) -{ - if (!validate_name(group)) - { - ParseAbort( - "%s: flowbits group name is limited to any alphanumeric string including %s", - s_name, ALLOWED_SPECIAL_CHARS); - } - - FLOWBITS_GRP* flowbits_grp = (FLOWBITS_GRP*)flowbit_state->flowbits_grp_hash->find(group); - - if ( !flowbits_grp ) - { - // new group defined, add (bitop set later once we know size) - flowbits_grp = new FLOWBITS_GRP; - flowbit_state->flowbits_grp_hash->insert(group, flowbits_grp); - flowbit_state->flowbits_grp_count++; - flowbits_grp->group_id = flowbit_state->flowbits_grp_count; - flowbits_grp->name = group; - } - - return flowbits_grp; -} - -static void parse_flowbits_with_group( - const char* bits, const char* group, FLOWBITS_OP* flowbits, FlowBitState* flowbit_state) -{ - parse_flowbits(bits, flowbits, flowbit_state); - - if ( group and flowbits->group.empty() ) - { - flowbits->group = group; - FLOWBITS_GRP* flowbits_grp = get_group(group, flowbit_state); - flowbits->group_id = flowbits_grp->group_id; - } - validateFlowbitsSyntax(flowbits); - - if ( !flowbits->group.empty() ) - flowbit_state->op_list.push_front(flowbits); -} - -static FLOWBITS_OP* flowbits_parse( - std::string& op, std::string& bits, std::string& group, SnortConfig* sc) -{ - FlowBitState* flowbit_state = sc->flowbit_state; - assert(flowbit_state != nullptr); - - FLOWBITS_OP* flowbits = new FLOWBITS_OP; - flowbits->name = op; - - if ( op == "set" ) - flowbits->type = FLOWBITS_SET; - - else if ( op == "setx" ) - flowbits->type = FLOWBITS_SETX; - - else if ( op == "unset" ) - flowbits->type = FLOWBITS_UNSET; - - else if ( op == "toggle" ) - flowbits->type = FLOWBITS_TOGGLE; - - else if ( op == "isset" ) - flowbits->type = FLOWBITS_ISSET; - - else if ( op == "isnotset" ) - flowbits->type = FLOWBITS_ISNOTSET; - - else if ( op == "noalert" ) - { - if ( !bits.empty() ) - { - ParseError("%s: invalid configuration.", s_name); - delete flowbits; - return nullptr; - } - - flowbits->type = FLOWBITS_NOALERT; - return flowbits; - } - else if ( op == "reset" ) - { - if ( !group.empty() ) - { - ParseError("%s: invalid configuration.", s_name); - delete flowbits; - return nullptr; + flow_bit = get_bit(tok.c_str(), check); + check->add(flow_bit->id); } - if ( !bits.empty() ) - { - group = bits; - FLOWBITS_GRP* flowbits_grp = get_group(group.c_str(), flowbit_state); - flowbits->group = group; - flowbits->group_id = flowbits_grp->group_id; - } - flowbits->type = FLOWBITS_RESET; - return flowbits; + check->or_bits = false; } else { - ParseError("%s: invalid configuration.", s_name); - delete flowbits; - return nullptr; - } - - parse_flowbits_with_group(bits.c_str(), group.c_str(), flowbits, flowbit_state); - return flowbits; -} - -static void update_group(FLOWBITS_GRP* flowbits_grp, int id) -{ - flowbits_grp->count++; - - if ( flowbits_grp->max_id < id ) - flowbits_grp->max_id = id; - - flowbits_grp->GrpBitOp->set(id); -} - -static void init_groups(FlowBitState* flowbit_state) -{ - if ( !flowbit_state->flowbits_hash or !flowbit_state->flowbits_grp_hash ) - return; - - for (GHashNode* n = flowbit_state->flowbits_grp_hash->find_first(); - n != nullptr; - n= flowbit_state->flowbits_grp_hash->find_next()) - { - FLOWBITS_GRP* fbg = (FLOWBITS_GRP*)n->data; - fbg->GrpBitOp = new BitOp(flowbit_state->flowbits_count); - fbg->GrpBitOp->reset(); - } - - while ( !flowbit_state->op_list.empty() ) - { - const FLOWBITS_OP* fbop = flowbit_state->op_list.front(); - FLOWBITS_GRP* fbg = - (FLOWBITS_GRP*)flowbit_state->flowbits_grp_hash->find(fbop->group.c_str()); - assert(fbg); - - for ( unsigned i = 0; i < fbop->ids.size(); ++i ) - update_group(fbg, fbop->ids[i]); - - flowbit_state->op_list.pop_front(); + flow_bit = get_bit(flowbits_names, check); + check->add(flow_bit->id); } + return true; } -static void flowbits_verify(FlowBitState* flowbit_state) +static void flowbits_verify() { - GHashNode* n; - unsigned num_flowbits = 0; unsigned unchecked = 0, unset = 0; - if (flowbit_state->flowbits_hash == nullptr) - return; - - for (n = flowbit_state->flowbits_hash->find_first(); - n != nullptr; - n = flowbit_state->flowbits_hash->find_next()) + for ( const auto& it : bit_map ) { - FLOWBITS_OBJECT* fb = (FLOWBITS_OBJECT*)n->data; - - if (fb->toggle != flowbit_state->flowbits_toggle) - { - sfqueue_add(flowbit_state->flowbits_bit_queue, (NODE_DATA)(uintptr_t)fb->id); - flowbit_state->flowbits_hash->remove(n->key); - continue; - } - - if ((fb->set > 0) and (fb->isset == 0)) + if ((it.second.sets > 0) and (it.second.checks == 0)) { ParseWarning(WARN_FLOWBITS, "%s key '%s' is set but not checked.", - s_name, (const char*)n->key); + s_name, it.first.c_str()); unchecked++; } - else if ((fb->isset > 0) and (fb->set == 0)) + else if ((it.second.checks > 0) and (it.second.sets == 0)) { - ParseWarning(WARN_FLOWBITS, "%s key '%s' is checked but not ever set.", - s_name, (const char*)n->key); + ParseWarning(WARN_FLOWBITS, "%s key '%s' is checked but not set.", + s_name, it.first.c_str()); unset++; } - else if ((fb->set == 0) and (fb->isset == 0)) - { - continue; /* don't count this bit as used */ - } - - num_flowbits++; } - assert(num_flowbits == flowbit_state->flowbits_count); - flowbit_state->flowbits_toggle ^= 1; - - if ( !num_flowbits ) + if ( !bit_map.size() ) return; LogLabel(s_name); - LogCount("defined", num_flowbits); + LogCount("defined", bit_map.size()); LogCount("not checked", unchecked); LogCount("not set", unset); } -static void free_item(void* d) -{ - FLOWBITS_OBJECT* data = (FLOWBITS_OBJECT*)d; - delete data; -} - -static void free_group(void* d) -{ - FLOWBITS_GRP* data = (FLOWBITS_GRP*)d; - - if (data->GrpBitOp) - delete data->GrpBitOp; - - delete data; -} - //------------------------------------------------------------------------- // module //------------------------------------------------------------------------- static const Parameter s_params[] = { - { "~op", Parameter::PT_STRING, nullptr, nullptr, - "set|reset|isset|etc." }, // FIXIT-L replace this legacy flowbits parsing with PT_SELECT + { "~op", Parameter::PT_ENUM, "set | unset | isset | isnotset | noalert", nullptr, + "bit operation or noalert (no bits)" }, { "~bits", Parameter::PT_STRING, nullptr, nullptr, - "bits or group" }, - - { "~group", Parameter::PT_STRING, nullptr, nullptr, - "group if arg1 is bits" }, + "bit [|bit]* or bit [&bit]*" }, { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } }; @@ -964,64 +452,63 @@ class FlowbitsModule : public Module { public: FlowbitsModule() : Module(s_name, s_help, s_params) { } + ~FlowbitsModule() { delete fbc; } 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 &flowBitsPerfStats; } + { return &flowbits_profile; } Usage get_usage() const override { return DETECT; } - FLOWBITS_OP* get_data(); + FlowBitCheck* get_data(); public: - std::string op; + FlowBitCheck::Op op; std::string bits; - std::string group; - FLOWBITS_OP* fbop = nullptr; + FlowBitCheck* fbc = nullptr; }; bool FlowbitsModule::begin(const char*, int, SnortConfig*) { - op.clear(); + delete fbc; bits.clear(); - group.clear(); return true; } bool FlowbitsModule::set(const char*, Value& v, SnortConfig*) { if ( v.is("~op") ) - op = v.get_string(); + op = static_cast(v.get_uint8()); else if ( v.is("~bits") ) bits = v.get_string(); - else if ( v.is("~group") ) - group = v.get_string(); - else return false; return true; } -bool FlowbitsModule::end(const char*, int, SnortConfig* sc) +bool FlowbitsModule::end(const char*, int, SnortConfig*) { - if ( op.empty() ) - return false; + fbc = new FlowBitCheck(op); + bool ok = true; + + if ( fbc->is_setter() or fbc->is_checker() ) + ok = parse_flowbits(bits.c_str(), fbc); - fbop = flowbits_parse(op, bits, group, sc); - return fbop != nullptr; + ok = ok and fbc->validate(); + return ok; } -FLOWBITS_OP* FlowbitsModule::get_data() +FlowBitCheck* FlowbitsModule::get_data() { - FLOWBITS_OP* tmp = fbop; - fbop = nullptr; + FlowBitCheck* tmp = fbc; + fbc = nullptr; return tmp; } @@ -1037,17 +524,14 @@ static Module* mod_ctor() static void mod_dtor(Module* m) { FlowbitsModule* fb = (FlowbitsModule*)m; - if (fb->fbop) - delete fb->fbop; - delete fb; } static IpsOption* flowbits_ctor(Module* p, OptTreeNode*) { FlowbitsModule* m = (FlowbitsModule*)p; - FLOWBITS_OP* fbop = m->get_data(); - return new FlowBitsOption(fbop); + FlowBitCheck* fbc = m->get_data(); + return new FlowBitsOption(fbc); } static void flowbits_dtor(IpsOption* p) @@ -1055,27 +539,10 @@ static void flowbits_dtor(IpsOption* p) delete p; } -static void flowbits_verify(SnortConfig* sc) -{ - FlowBitState* flowbit_state = sc->flowbit_state; - init_groups(flowbit_state); - flowbits_verify(flowbit_state); -} - -#if 0 -// FIXIT-M if add_detection_option() finds a dup, then -// we can leak the original group name if same as current -// also, why use new group name instead of original? -char* group_name = ((FLOWBITS_OP*)idx_dup)->group; - -if (flowbits->group) +static void flowbits_verify(SnortConfig*) { - if (group_name and strcmp(group_name, flowbits->group)) - snort_free(group_name); - ((FLOWBITS_OP*)idx_dup)->group = snort_strdup(flowbits->group); + flowbits_verify(); } -// ... then delete current and use original -#endif static const IpsApi flowbits_api = { diff --git a/src/ips_options/ips_flowbits.h b/src/ips_options/ips_flowbits.h index ca289551a..08684ea52 100644 --- a/src/ips_options/ips_flowbits.h +++ b/src/ips_options/ips_flowbits.h @@ -1,6 +1,5 @@ //-------------------------------------------------------------------------- // Copyright (C) 2014-2020 Cisco and/or its affiliates. All rights reserved. -// Copyright (C) 2004-2013 Sourcefire, Inc. // // 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 @@ -20,15 +19,11 @@ #ifndef IPS_FLOWBITS_H #define IPS_FLOWBITS_H -#include "main/snort_config.h" -namespace snort -{ -struct SnortConfig; -} +#include +#include -void flowbits_ginit(snort::SnortConfig*); -void flowbits_gterm(snort::SnortConfig*); -int FlowBits_SetOperation(void*); +bool flowbits_setter(void*); +void get_flowbits_dependencies(void*, bool& set, std::vector& bits); #endif diff --git a/src/ips_options/ips_options.cc b/src/ips_options/ips_options.cc index 275ec03bd..6de8f3ff5 100644 --- a/src/ips_options/ips_options.cc +++ b/src/ips_options/ips_options.cc @@ -77,7 +77,6 @@ extern const BaseApi* ips_rem[]; extern const BaseApi* ips_rev[]; extern const BaseApi* ips_rpc[]; extern const BaseApi* ips_seq[]; -extern const BaseApi* ips_session[]; extern const BaseApi* ips_sid[]; extern const BaseApi* ips_soid[]; extern const BaseApi* ips_target[]; @@ -150,7 +149,6 @@ void load_ips_options() PluginManager::load_plugins(ips_rev); PluginManager::load_plugins(ips_rpc); PluginManager::load_plugins(ips_seq); - PluginManager::load_plugins(ips_session); PluginManager::load_plugins(ips_sid); PluginManager::load_plugins(ips_soid); PluginManager::load_plugins(ips_target); diff --git a/src/ips_options/ips_session.cc b/src/ips_options/ips_session.cc deleted file mode 100644 index 598edbe46..000000000 --- a/src/ips_options/ips_session.cc +++ /dev/null @@ -1,413 +0,0 @@ -//-------------------------------------------------------------------------- -// Copyright (C) 2014-2020 Cisco and/or its affiliates. All rights reserved. -// Copyright (C) 2002-2013 Sourcefire, Inc. -// Copyright (C) 1998-2002 Martin Roesch -// -// 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. -//-------------------------------------------------------------------------- - -/* Snort Session Logging Plugin */ - -/* sp_session - * - * Purpose: - * - * Drops data (printable or otherwise) into a SESSION file. Useful for - * logging user sessions (telnet, http, ftp, etc). - * - * Arguments: - * - * This plugin can take two arguments: - * printable => only log the "printable" ASCII characters. - * all => log all traffic in the session, logging non-printable - * chars in "\xNN" hexadecimal format - * - * Effect: - * - * Warning, this plugin may slow Snort *way* down! - * - */ -// FIXIT-L delete this (sp_session) and use session tag instead - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include - -#include "framework/ips_option.h" -#include "framework/module.h" -#include "hash/hash_key_operations.h" -#include "log/messages.h" -#include "main/snort_config.h" -#include "profiler/profiler.h" -#include "protocols/packet.h" -#include "utils/util.h" -#include "utils/util_cstring.h" - -using namespace snort; - -#define s_name "session" - -static THREAD_LOCAL ProfileStats sessionPerfStats; - -#define SESSION_PRINTABLE 1 -#define SESSION_BINARY 2 -#define SESSION_ALL 3 - -struct SessionData -{ - int session_flag; -}; - -class SessionOption : public IpsOption -{ -public: - SessionOption(const SessionData& c) : - IpsOption(s_name) - { config = c; } - - uint32_t hash() const override; - bool operator==(const IpsOption&) const override; - - EvalStatus eval(Cursor&, Packet*) override; - -private: - SessionData config; -}; - -static FILE* OpenSessionFile(Packet*); -static void DumpSessionData(FILE*, Packet*, SessionData*); - -//------------------------------------------------------------------------- -// class methods -//------------------------------------------------------------------------- - -uint32_t SessionOption::hash() const -{ - uint32_t a,b,c; - const SessionData* data = &config; - - a = data->session_flag; - b = 0; - c = 0; - - mix_str(a,b,c,get_name()); - finalize(a,b,c); - - return c; -} - -bool SessionOption::operator==(const IpsOption& ips) const -{ - if ( strcmp(get_name(), ips.get_name()) ) - return false; - - const SessionOption& rhs = (const SessionOption&)ips; - const SessionData* left = &config; - const SessionData* right = &rhs.config; - - if (left->session_flag == right->session_flag) - { - return true; - } - - return false; -} - -IpsOption::EvalStatus SessionOption::eval(Cursor&, Packet* p) -{ - RuleProfile profile(sessionPerfStats); - - if ( !p->dsize || !p->data ) - return MATCH; - - if ( p->is_fragment() ) - return MATCH; - - // FIXIT-L should wrap file open/close in a class to ensure cleanup - { - FILE* session = OpenSessionFile(p); - - if ( !session ) - return MATCH; - - DumpSessionData(session, p, &config); - fclose(session); - } - - return MATCH; -} - -//------------------------------------------------------------------------- -// implementation methods -//------------------------------------------------------------------------- - -static FILE* OpenSessionFile(Packet* p) -{ - char filename[STD_BUF]; - char session_file[STD_BUF]; /* name of session file */ - const SfIp* dst, * src; - - FILE* ret; - - if (p->ptrs.decode_flags & DECODE_FRAG) - { - return nullptr; - } - - memset((char*)session_file, 0, STD_BUF); - - /* figure out which way this packet is headed in relation to the homenet */ - dst = p->ptrs.ip_api.get_dst(); - src = p->ptrs.ip_api.get_src(); - - SfIpString addr; - - if (SnortConfig::get_conf()->homenet.contains(dst) == SFIP_CONTAINS) - { - if (SnortConfig::get_conf()->homenet.contains(src) == SFIP_NOT_CONTAINS) - { - p->ptrs.ip_api.get_src()->ntop(addr); - } - else - { - if (p->ptrs.sp >= p->ptrs.dp) - { - p->ptrs.ip_api.get_src()->ntop(addr); - } - else - { - p->ptrs.ip_api.get_dst()->ntop(addr); - } - } - } - else - { - if (SnortConfig::get_conf()->homenet.contains(src) == SFIP_CONTAINS) - { - p->ptrs.ip_api.get_dst()->ntop(addr); - } - else - { - if (p->ptrs.sp >= p->ptrs.dp) - { - p->ptrs.ip_api.get_src()->ntop(addr); - } - else - { - p->ptrs.ip_api.get_dst()->ntop(addr); - } - } - } - std::string name; - const char* log_path = get_instance_file(name, addr); - - /* build the log directory */ - if (mkdir(log_path,S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) - { - if (errno != EEXIST) - { - FatalError("Problem creating directory %s: %s\n", - log_path, get_error(errno)); - } - } - - if (p->ptrs.sp >= p->ptrs.dp) - SnortSnprintf(session_file, STD_BUF, "%s/SESSION:%d-%d", log_path, p->ptrs.sp, p->ptrs.dp); - else - SnortSnprintf(session_file, STD_BUF, "%s/SESSION:%d-%d", log_path, p->ptrs.dp, p->ptrs.sp); - - - strncpy(filename, session_file, STD_BUF - 1); - filename[STD_BUF - 1] = '\0'; - - ret = fopen(session_file, "a"); - - if (ret == nullptr) - { - FatalError("OpenSessionFile() => fopen(%s) session file: %s\n", - session_file, get_error(errno)); - } - - return ret; -} - -static void DumpSessionData(FILE* fp, Packet* p, SessionData* sessionData) -{ - const uint8_t* idx; - const uint8_t* end; - char conv[] = "0123456789ABCDEF"; /* xlation lookup table */ - - if (p->dsize == 0 || p->data == nullptr || (p->ptrs.decode_flags & DECODE_FRAG)) - return; - - idx = p->data; - end = idx + p->dsize; - - if (sessionData->session_flag == SESSION_PRINTABLE) - { - while (idx != end) - { - if ((*idx > 0x1f && *idx < 0x7f) || *idx == 0x0a || *idx == 0x0d) - { - fputc(*idx, fp); - } - idx++; - } - } - else if (sessionData->session_flag == SESSION_BINARY) - { - fwrite(p->data, p->dsize, sizeof(char), fp); - } - else - { - while (idx != end) - { - if ((*idx > 0x1f && *idx < 0x7f) || *idx == 0x0a || *idx == 0x0d) - { - /* Escape all occurrences of '\' */ - if (*idx == '\\') - fputc('\\', fp); - fputc(*idx, fp); - } - else - { - fputc('\\', fp); - fputc(conv[((*idx&0xFF) >> 4)], fp); - fputc(conv[((*idx&0xFF)&0x0F)], fp); - } - - idx++; - } - } -} - -//------------------------------------------------------------------------- -// module -//------------------------------------------------------------------------- - -static const Parameter s_params[] = -{ - { "~mode", Parameter::PT_ENUM, "printable|binary|all", nullptr, - "output format" }, - - { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } -}; - -#define s_help \ - "rule option to check user data from TCP sessions" - -class SsnModule : public Module -{ -public: - SsnModule() : Module(s_name, s_help, s_params) { } - - bool begin(const char*, int, SnortConfig*) override; - bool set(const char*, Value&, SnortConfig*) override; - - ProfileStats* get_profile() const override - { return &sessionPerfStats; } - - Usage get_usage() const override - { return DETECT; } - -public: - SessionData data; -}; - -bool SsnModule::begin(const char*, int, SnortConfig*) -{ - memset(&data, 0, sizeof(data)); - return true; -} - -bool SsnModule::set(const char*, Value& v, SnortConfig*) -{ - if ( v.is("~mode") ) - data.session_flag = v.get_uint8() + 1; - - else - return false; - - return true; -} - -//------------------------------------------------------------------------- -// api methods -//------------------------------------------------------------------------- - -static Module* mod_ctor() -{ - return new SsnModule; -} - -static void mod_dtor(Module* m) -{ - delete m; -} - -static IpsOption* session_ctor(Module* p, OptTreeNode*) -{ - SsnModule* m = (SsnModule*)p; - return new SessionOption(m->data); -} - -static void session_dtor(IpsOption* p) -{ - delete p; -} - -static const IpsApi session_api = -{ - { - PT_IPS_OPTION, - sizeof(IpsApi), - IPSAPI_VERSION, - 0, - API_RESERVED, - API_OPTIONS, - s_name, - s_help, - mod_ctor, - mod_dtor - }, - OPT_TYPE_LOGGING, - /* - * Theoretically we should only allow this plugin to be used when - * there's a possibility of a session happening (i.e. TCP), but I get - * enough requests that I'm going to pull the verifier so that things - * should work for everyone - */ - 1, /*PROTO_BIT__TCP*/ 0, - nullptr, - nullptr, - nullptr, - nullptr, - session_ctor, - session_dtor, - nullptr -}; - -#ifdef BUILDING_SO -SO_PUBLIC const BaseApi* snort_plugins[] = -#else -const BaseApi* ips_session[] = -#endif -{ - &session_api.base, - nullptr -}; - diff --git a/src/log/messages.cc b/src/log/messages.cc index 75bf2fefe..c6388cd15 100644 --- a/src/log/messages.cc +++ b/src/log/messages.cc @@ -180,9 +180,6 @@ static void WriteLogMessage(FILE* fh, bool prefer_fh, const char* format, va_lis { if ( SnortConfig::get_conf() && !prefer_fh ) { - if ( SnortConfig::log_quiet() ) - return; - if ( SnortConfig::log_syslog() ) { char buf[STD_BUF+1]; @@ -207,6 +204,9 @@ static void WriteLogMessage(FILE* fh, bool prefer_fh, const char* format, va_lis */ void LogMessage(const char* format,...) { + if ( SnortConfig::get_conf() and SnortConfig::log_quiet() ) + return; + va_list ap; va_start(ap, format); @@ -217,6 +217,9 @@ void LogMessage(const char* format,...) void LogMessage(FILE* fh, const char* format,...) { + if ( fh == stdout and SnortConfig::get_conf() and SnortConfig::log_quiet() ) + return; + va_list ap; va_start(ap, format); @@ -239,9 +242,6 @@ void WarningMessage(const char* format,...) { va_list ap; - if ( SnortConfig::get_conf() and SnortConfig::log_quiet() ) - return; - va_start(ap, format); if ( SnortConfig::get_conf() and SnortConfig::log_syslog() ) diff --git a/src/main.cc b/src/main.cc index 24665ae49..ef7309f36 100644 --- a/src/main.cc +++ b/src/main.cc @@ -796,6 +796,24 @@ static bool set_mode() return false; } + if ( SnortConfig::dump_rule_deps() ) + { + dump_rule_deps(SnortConfig::get_conf()); + return false; + } + + if ( SnortConfig::dump_rule_meta() ) + { + dump_rule_meta(SnortConfig::get_conf()); + return false; + } + + if ( SnortConfig::dump_rule_state() ) + { + dump_rule_state(SnortConfig::get_conf()); + return false; + } + if ( just_validate() ) { LogMessage("\nSnort successfully validated the configuration (with %u warnings).\n", diff --git a/src/main/modules.cc b/src/main/modules.cc index 870b8e3aa..8b9ce42ea 100644 --- a/src/main/modules.cc +++ b/src/main/modules.cc @@ -717,7 +717,7 @@ static const Parameter output_params[] = "" }, { "quiet", Parameter::PT_BOOL, nullptr, "false", - "suppress non-fatal information (still show alerts, same as -q)" }, + "suppress normal logging on stdout (same as -q)" }, { "logdir", Parameter::PT_STRING, nullptr, ".", "where to put log files (same as -l)" }, @@ -1801,7 +1801,7 @@ bool RuleStateModule::begin(const char*, int, SnortConfig* sc) else { - key = { 0, 0 }; + key = { 0, 0, 0 }; state.action = snort::Actions::Type::ALERT; state.enable = IpsPolicy::Enable::INHERIT_ENABLE; } @@ -1816,7 +1816,7 @@ bool RuleStateModule::end(const char* fqn, int, SnortConfig* sc) if ( !key.gid or !key.sid ) return false; - state.policy_id = snort::get_ips_policy()->policy_id; + key.policy_id = snort::get_ips_policy()->policy_id; sc->rule_states->add(key, state); return true; diff --git a/src/main/policy.cc b/src/main/policy.cc index cc65d39eb..2cefa7983 100644 --- a/src/main/policy.cc +++ b/src/main/policy.cc @@ -297,10 +297,10 @@ InspectionPolicy* get_inspection_policy() IpsPolicy* get_ips_policy() { return s_detection_policy; } -IpsPolicy* get_ips_policy(SnortConfig* sc, unsigned i) +IpsPolicy* get_ips_policy(const SnortConfig* sc, unsigned i) { return sc && i < sc->policy_map->ips_policy_count() ? - sc->policy_map->get_ips_policy(0) : nullptr; + sc->policy_map->get_ips_policy(i) : nullptr; } InspectionPolicy* get_default_inspection_policy(SnortConfig* sc) diff --git a/src/main/policy.h b/src/main/policy.h index 4821aa674..afc0b7edf 100644 --- a/src/main/policy.h +++ b/src/main/policy.h @@ -278,7 +278,7 @@ namespace snort SO_PUBLIC NetworkPolicy* get_network_policy(); SO_PUBLIC InspectionPolicy* get_inspection_policy(); SO_PUBLIC IpsPolicy* get_ips_policy(); -SO_PUBLIC IpsPolicy* get_ips_policy(snort::SnortConfig*, unsigned i = 0); +SO_PUBLIC IpsPolicy* get_ips_policy(const snort::SnortConfig*, unsigned i = 0); SO_PUBLIC InspectionPolicy* get_default_inspection_policy(snort::SnortConfig*); SO_PUBLIC void set_ips_policy(IpsPolicy* p); SO_PUBLIC void set_network_policy(NetworkPolicy* p); diff --git a/src/main/snort_config.cc b/src/main/snort_config.cc index 89d7ea441..935a8fad6 100644 --- a/src/main/snort_config.cc +++ b/src/main/snort_config.cc @@ -38,7 +38,6 @@ #include "flow/ha_module.h" #include "hash/xhash.h" #include "helpers/process.h" -#include "ips_options/ips_flowbits.h" #include "latency/latency_config.h" #include "log/messages.h" #include "managers/action_manager.h" @@ -176,7 +175,6 @@ void SnortConfig::init(const SnortConfig* const other_conf, ProtocolReference* p memset(evalOrder, 0, sizeof(evalOrder)); proto_ref = new ProtocolReference(protocol_reference); - flowbits_ginit(this); so_rules = new SoRules; } else @@ -257,8 +255,6 @@ SnortConfig::~SnortConfig() } delete fast_pattern_config; - flowbits_gterm(this); - delete policy_map; InspectorManager::delete_config(this); ActionManager::delete_config(this); diff --git a/src/main/snort_config.h b/src/main/snort_config.h index f69d1e0d5..5c2c18bdd 100644 --- a/src/main/snort_config.h +++ b/src/main/snort_config.h @@ -43,7 +43,7 @@ enum RunFlag RUN_FLAG__READ = 0x00000001, RUN_FLAG__DAEMON = 0x00000002, RUN_FLAG__DUMP_MSG_MAP = 0x00000004, - // unused = 0x00000008, + RUN_FLAG__DUMP_RULE_META = 0x00000008, RUN_FLAG__INLINE = 0x00000010, RUN_FLAG__STATIC_HASH = 0x00000020, @@ -68,7 +68,7 @@ enum RunFlag RUN_FLAG__ASSURE_EST = 0x00080000, RUN_FLAG__TREAT_DROP_AS_IGNORE= 0x00100000, - RUN_FLAG__PCAP_RELOAD = 0x00200000, + RUN_FLAG__DUMP_RULE_DEPS = 0x00200000, RUN_FLAG__TEST = 0x00400000, #ifdef SHELL RUN_FLAG__SHELL = 0x00800000, @@ -79,6 +79,8 @@ enum RunFlag RUN_FLAG__MEM_CHECK = 0x02000000, RUN_FLAG__TRACK_ON_SYN = 0x04000000, RUN_FLAG__IP_FRAGS_ONLY = 0x08000000, + + RUN_FLAG__DUMP_RULE_STATE = 0x10000000, }; enum OutputFlag @@ -132,7 +134,6 @@ struct sopg_table_t; struct ClassType; struct DetectionFilterConfig; struct EventQueueConfig; -struct FlowBitState; struct FrameworkConfig; struct HighAvailabilityConfig; struct IpsActionsConfig; @@ -319,7 +320,6 @@ public: ThresholdConfig* threshold_config = nullptr; RateFilterConfig* rate_filter_config = nullptr; DetectionFilterConfig* detection_filter_config = nullptr; - FlowBitState* flowbit_state = nullptr; //------------------------------------------------------ // FIXIT-L command line only stuff, add to conf / module @@ -496,6 +496,22 @@ public: static bool dump_msg_map() { return get_conf()->run_flags & RUN_FLAG__DUMP_MSG_MAP; } + static bool dump_rule_meta() + { return get_conf()->run_flags & RUN_FLAG__DUMP_RULE_META; } + + static bool dump_rule_state() + { return get_conf()->run_flags & RUN_FLAG__DUMP_RULE_STATE; } + + static bool dump_rule_deps() + { return get_conf()->run_flags & RUN_FLAG__DUMP_RULE_DEPS; } + + static bool dump_rule_info() + { + const SnortConfig* sc = get_conf(); + return sc->dump_msg_map() or sc->dump_rule_meta() or + sc->dump_rule_deps() or sc->dump_rule_state(); + } + static bool test_mode() { return get_conf()->run_flags & RUN_FLAG__TEST; } diff --git a/src/main/snort_debug.cc b/src/main/snort_debug.cc index e3ff27705..57b03e8fa 100644 --- a/src/main/snort_debug.cc +++ b/src/main/snort_debug.cc @@ -163,7 +163,7 @@ TEST_CASE("trace all=0", "[trace]") test.set(trace_val); testing_dump[0] = '\0'; - debug_log(test, "my message"); + debug_log(test, "my message"); CHECK( testing_dump[0] == '\0' ); } @@ -178,7 +178,7 @@ TEST_CASE("debug_log", "[trace]") test.set(trace_val); testing_dump[0] = '\0'; - debug_log(test, "my message"); + debug_log(test, "my message"); CHECK( !strcmp(testing_dump, "test:all:1: my message") ); Parameter p_all("all", Parameter::PT_INT, "0:255", "0", "p_all"); @@ -223,7 +223,7 @@ TEST_CASE("debug_log", "[trace]") testing_dump[0] = '\0'; debug_log(3, testing_opt, TEST_TRACE_OPTION3, "log option3 message"); CHECK( !strcmp(testing_dump, "testing_opt:option3:3: log option3 message") ); - + testing_dump[0] = '\0'; debug_log(2, testing_opt, TEST_TRACE_OPTION4, "log option4 message"); CHECK( !strcmp(testing_dump, "testing_opt:option4:2: log option4 message") ); @@ -288,7 +288,7 @@ TEST_CASE("debug_logf", "[trace]") testing_dump[0] = '\0'; debug_logf(6, testing_opt, TEST_TRACE_OPTION3, "%s %s %s", "log", "option3", "message"); CHECK( testing_dump[0] == '\0' ); - + testing_dump[0] = '\0'; debug_logf(2, testing_opt, TEST_TRACE_OPTION4, "%s %s %s", "log", "option4", "message"); CHECK( !strcmp(testing_dump, "testing_opt:option4:2: log option4 message") ); diff --git a/src/main/snort_module.cc b/src/main/snort_module.cc index b7f55bdc4..9163fc6f4 100644 --- a/src/main/snort_module.cc +++ b/src/main/snort_module.cc @@ -250,7 +250,7 @@ static const Parameter s_params[] = "enable inline mode operation" }, { "-q", Parameter::PT_IMPLIED, nullptr, nullptr, - "quiet mode - Don't show banner and status report" }, + "quiet mode - suppress normal logging on stdout" }, { "-R", Parameter::PT_STRING, nullptr, nullptr, " include this rules file in the default policy" }, @@ -345,6 +345,15 @@ static const Parameter s_params[] = { "--dump-defaults", Parameter::PT_STRING, "(optional)", nullptr, "[] output module defaults in Lua format" }, + { "--dump-rule-deps", Parameter::PT_IMPLIED, nullptr, nullptr, + "dump rule dependencies in json format for use by other tools" }, + + { "--dump-rule-meta", Parameter::PT_IMPLIED, nullptr, nullptr, + "dump configured rule info in json format for use by other tools" }, + + { "--dump-rule-state", Parameter::PT_IMPLIED, nullptr, nullptr, + "dump configured rule state in json format for use by other tools" }, + { "--dump-version", Parameter::PT_IMPLIED, nullptr, nullptr, "output the version, the whole version, and only the version" }, @@ -462,9 +471,6 @@ static const Parameter s_params[] = { "--pcap-no-filter", Parameter::PT_IMPLIED, nullptr, nullptr, "reset to use no filter when getting pcaps from file or directory" }, - { "--pcap-reload", Parameter::PT_IMPLIED, nullptr, nullptr, - "if reading multiple pcaps, reload snort config between pcaps" }, - { "--pcap-show", Parameter::PT_IMPLIED, nullptr, nullptr, "print a line saying what pcap is currently being read" }, @@ -840,6 +846,22 @@ bool SnortModule::set(const char*, Value& v, SnortConfig* sc) else if ( v.is("--dump-defaults") ) dump_defaults(sc, v.get_string()); + else if ( v.is("--dump-rule-deps") ) + { + sc->run_flags |= (RUN_FLAG__DUMP_RULE_DEPS | RUN_FLAG__TEST); + sc->set_quiet(true); + } + else if ( v.is("--dump-rule-meta") ) + { + sc->run_flags |= (RUN_FLAG__DUMP_RULE_META | RUN_FLAG__TEST); + sc->output_flags |= OUTPUT_FLAG__ALERT_REFS; + sc->set_quiet(true); + } + else if ( v.is("--dump-rule-state") ) + { + sc->run_flags |= (RUN_FLAG__DUMP_RULE_STATE | RUN_FLAG__TEST); + sc->set_quiet(true); + } else if ( v.is("--dump-version") ) dump_version(sc); @@ -954,9 +976,6 @@ bool SnortModule::set(const char*, Value& v, SnortConfig* sc) else if ( v.is("--pcap-no-filter") ) Trough::set_filter(nullptr); - else if ( v.is("--pcap-reload") ) - sc->run_flags |= RUN_FLAG__PCAP_RELOAD; - else if ( v.is("--pcap-show") ) sc->run_flags |= RUN_FLAG__PCAP_SHOW; diff --git a/src/managers/inspector_manager.cc b/src/managers/inspector_manager.cc index 68e804d52..39dea1609 100644 --- a/src/managers/inspector_manager.cc +++ b/src/managers/inspector_manager.cc @@ -289,6 +289,16 @@ void FrameworkPolicy::vectorize(SnortConfig* sc) // global stuff //------------------------------------------------------------------------- +std::vector InspectorManager::get_apis() +{ + std::vector v; + + for ( const auto* p : s_handlers ) + v.emplace_back(&p->api); + + return v; +} + void InspectorManager::add_plugin(const InspectApi* api) { PHGlobal* g = new PHGlobal(*api); diff --git a/src/managers/inspector_manager.h b/src/managers/inspector_manager.h index d2248aa94..e71850039 100644 --- a/src/managers/inspector_manager.h +++ b/src/managers/inspector_manager.h @@ -44,6 +44,8 @@ public: static void dump_buffers(); static void release_plugins(); + static std::vector get_apis(); + static void new_policy(InspectionPolicy*, InspectionPolicy*); static void delete_policy(InspectionPolicy*, bool cloned); static void update_policy(SnortConfig* sc); diff --git a/src/managers/ips_manager.cc b/src/managers/ips_manager.cc index c69c62ae4..5297a3f5e 100644 --- a/src/managers/ips_manager.cc +++ b/src/managers/ips_manager.cc @@ -320,8 +320,6 @@ bool IpsManager::option_end( if ( ips->is_relative() ) fpl->isRelative = 1; - otn_set_plugin(otn, ips->get_type()); - if ( ips->is_agent() and !otn_set_agent(otn, ips) ) { // FIXIT-L support multiple actions (eg replaces) per rule diff --git a/src/network_inspectors/binder/bind_module.cc b/src/network_inspectors/binder/bind_module.cc index ab731ab07..233bee4cb 100644 --- a/src/network_inspectors/binder/bind_module.cc +++ b/src/network_inspectors/binder/bind_module.cc @@ -326,8 +326,8 @@ bool BinderModule::end(const char* fqn, int idx, SnortConfig* sc) return true; } - // FIXIT-D: remove this when network_policy binding is deleted from - // the binder's options + // FIXIT-D: remove this when network_policy binding is deleted from + // the binder's options if ( work->use.type == NETWORK_KEY ) { delete work; diff --git a/src/parser/cmd_line.cc b/src/parser/cmd_line.cc index 938812dcb..bac02b189 100644 --- a/src/parser/cmd_line.cc +++ b/src/parser/cmd_line.cc @@ -42,20 +42,13 @@ static void check_flags(SnortConfig* sc) if ((sc->run_flags & RUN_FLAG__INLINE) && (sc->run_flags & RUN_FLAG__INLINE_TEST)) { - FatalError("Cannot use inline adapter mode and inline test " + ParseError("Cannot use inline adapter mode and inline test " "mode together. \n"); } if (Trough::get_loop_count() && !(sc->run_flags & RUN_FLAG__READ)) { - FatalError("--pcap-loop can only be used in combination with pcaps " - "on the command line.\n"); - } - - if ((sc->run_flags & RUN_FLAG__PCAP_RELOAD) && - !(sc->run_flags & RUN_FLAG__READ)) - { - FatalError("--pcap-reload can only be used in combination with pcaps " + ParseError("--pcap-loop can only be used in combination with pcaps " "on the command line.\n"); } } diff --git a/src/parser/parse_rule.cc b/src/parser/parse_rule.cc index 011afb993..84beb2062 100644 --- a/src/parser/parse_rule.cc +++ b/src/parser/parse_rule.cc @@ -84,6 +84,22 @@ static rule_count_t ipCnt; static rule_count_t svcCnt; // dummy for now static bool s_ignore = false; // for skipping drop rules when not inline, etc. +static bool s_capture = false; + +static std::string s_body; + +struct SoRule +{ + SoRule(RuleTreeNode* rtn, const OptTreeNode* otn) : + rtn(rtn), gid(otn->sigInfo.gid), sid(otn->sigInfo.sid), rev(otn->sigInfo.rev) { } + + RuleTreeNode* rtn; + uint32_t gid; + uint32_t sid; + uint32_t rev; +}; + +static SoRule* s_so_rule = nullptr; /* * Finish adding the rule to the port tables @@ -99,8 +115,7 @@ static bool s_ignore = false; // for skipping drop rules when not inline, etc. * c)if the rule is bidir add the rule and port-object to both src and dst tables */ static int FinishPortListRule( - RulePortTables* port_tables, RuleTreeNode* rtn, OptTreeNode* otn, - SnortProtocolId snort_protocol_id, FastPatternConfig* fp) + RulePortTables* port_tables, RuleTreeNode* rtn, OptTreeNode* otn, FastPatternConfig* fp) { int large_port_group = 0; PortTable* dstTable; @@ -109,11 +124,9 @@ static int FinishPortListRule( rule_count_t* prc; uint32_t orig_flags = rtn->flags; - assert(otn->snort_protocol_id == snort_protocol_id); - /* Select the Target PortTable for this rule, based on protocol, src/dst * dir, and if there is rule content */ - switch ( snort_protocol_id ) + switch ( otn->snort_protocol_id ) { case SNORT_PROTO_IP: dstTable = port_tables->ip.dst; @@ -198,7 +211,7 @@ static int FinishPortListRule( * were using a single rule group we make it an any-any rule. */ if ( rtn->any_any_port() or large_port_group or fp->get_single_rule_group() ) { - if (snort_protocol_id == SNORT_PROTO_IP) + if (otn->snort_protocol_id == SNORT_PROTO_IP) { PortObjectAddRule(port_tables->icmp.any, otn->ruleIndex); icmpCnt.any++; @@ -285,7 +298,7 @@ static int ValidateIPList(sfip_var_t* addrset, const char* token) return 0; } -static int ProcessIP(SnortConfig*, const char* addr, RuleTreeNode* rtn, int mode, int) +static int ProcessIP(SnortConfig* sc, const char* addr, RuleTreeNode* rtn, int mode, int) { vartable_t* ip_vartable = get_ips_policy()->ip_vartable; @@ -318,6 +331,9 @@ static int ProcessIP(SnortConfig*, const char* addr, RuleTreeNode* rtn, int mode } /* The function sfvt_add_to_var adds 'addr' to the variable 'rtn->sip' */ + if ( ret == SFIP_LOOKUP_FAILURE and sc->dump_rule_info() ) + ret = sfvt_add_to_var(ip_vartable, rtn->sip, "any"); + if (ret != SFIP_SUCCESS) { if (ret == SFIP_LOOKUP_FAILURE) @@ -373,7 +389,10 @@ static int ProcessIP(SnortConfig*, const char* addr, RuleTreeNode* rtn, int mode ret = sfvt_add_to_var(ip_vartable, rtn->dip, addr); } - if (ret != SFIP_SUCCESS) + if ( ret == SFIP_LOOKUP_FAILURE and sc->dump_rule_info() ) + ret = sfvt_add_to_var(ip_vartable, rtn->dip, "any"); + + if ( ret != SFIP_SUCCESS ) { if (ret == SFIP_LOOKUP_FAILURE) { @@ -615,10 +634,13 @@ static void XferHeader(RuleTreeNode* from, RuleTreeNode* to) to->sip = from->sip; to->dip = from->dip; + to->listhead = from->listhead; to->snort_protocol_id = from->snort_protocol_id; to->src_portobject = from->src_portobject; to->dst_portobject = from->dst_portobject; + + to->header = from->header; } /**************************************************************************** @@ -753,26 +775,22 @@ static void SetupRTNFuncList(RuleTreeNode* rtn) AddRuleFuncToList(RuleListEnd, rtn); } -// Process the header block info and add to the block list if necessary -static RuleTreeNode* ProcessHeadNode( - SnortConfig* sc, RuleTreeNode* test_node, ListHead* list) +// if it doesn't match any of the existing nodes, make a new node and +// stick it at the end of the list +static RuleTreeNode* ProcessHeadNode(SnortConfig* sc, RuleTreeNode* test_node) { RuleTreeNode* rtn = findHeadNode( sc, test_node, get_ips_policy()->policy_id); - /* if it doesn't match any of the existing nodes, make a new node and - * stick it at the end of the list */ - if ( !rtn ) + if ( rtn ) + FreeRuleTreeNode(test_node); + + else { head_count++; rtn = new RuleTreeNode; XferHeader(test_node, rtn); SetupRTNFuncList(rtn); - rtn->listhead = list; - } - else - { - FreeRuleTreeNode(test_node); } return rtn; @@ -918,6 +936,10 @@ void parse_rule_print() void parse_rule_type(SnortConfig* sc, const char* s, RuleTreeNode& rtn) { rtn = RuleTreeNode(); + + if ( s_so_rule ) + return; + rtn.action = get_rule_type(s); if ( rtn.action == Actions::NONE ) @@ -925,18 +947,18 @@ void parse_rule_type(SnortConfig* sc, const char* s, RuleTreeNode& rtn) s_ignore = true; return; } - else + if ( sc->dump_rule_meta() ) + rtn.header = new RuleHeader(s); + + rtn.listhead = get_rule_list(sc, s); + + if ( !rtn.listhead ) { + CreateRuleType(sc, s, rtn.action); rtn.listhead = get_rule_list(sc, s); - - if ( !rtn.listhead ) - { - CreateRuleType(sc, s, rtn.action); - rtn.listhead = get_rule_list(sc, s); - } - if ( SnortConfig::get_default_rule_state() ) - rtn.set_enabled(); } + if ( SnortConfig::get_default_rule_state() ) + rtn.set_enabled(); if ( !rtn.listhead ) ParseError("unconfigured rule action '%s'", s); @@ -947,6 +969,9 @@ void parse_rule_proto(SnortConfig* sc, const char* s, RuleTreeNode& rtn) if ( s_ignore ) return; + if ( !s_so_rule and rtn.header ) + rtn.header->proto = s; + if ( !strcmp(s, "tcp") ) rule_proto = PROTO_BIT__TCP; @@ -970,36 +995,63 @@ void parse_rule_proto(SnortConfig* sc, const char* s, RuleTreeNode& rtn) ParseError("bad protocol: %s", s); rule_proto = 0; } + else if ( s_so_rule and s_so_rule->rtn->snort_protocol_id != rtn.snort_protocol_id ) + ParseWarning(WARN_RULES, "so rule proto can not be changed"); } void parse_rule_nets( SnortConfig* sc, const char* s, bool src, RuleTreeNode& rtn) { + if ( s_so_rule ) + return; + if ( s_ignore ) return; + if ( rtn.header ) + { + if ( src ) + rtn.header->src_nets = s; + else + rtn.header->dst_nets = s; + } ProcessIP(sc, s, &rtn, src ? SRC : DST, 0); } void parse_rule_ports( SnortConfig*, const char* s, bool src, RuleTreeNode& rtn) { + if ( s_so_rule ) + return; + if ( s_ignore ) return; + if ( rtn.header ) + { + if ( src ) + rtn.header->src_ports = s; + else + rtn.header->dst_ports = s; + } + IpsPolicy* p = get_ips_policy(); if ( ParsePortList(&rtn, p->portVarTable, p->nonamePortVarTable, s, src ? SRC : DST) ) - { ParseError("bad ports: '%s'", s); - } } void parse_rule_dir(SnortConfig*, const char* s, RuleTreeNode& rtn) { + if ( s_so_rule ) + return; + if ( s_ignore ) return; + if ( rtn.header ) + rtn.header->dir = s; + if (strcmp(s, "<>") == 0) rtn.flags |= RuleTreeNode::BIDIRECTIONAL; @@ -1012,6 +1064,12 @@ void parse_rule_opt_begin(SnortConfig* sc, const char* key) if ( s_ignore ) return; + if ( s_capture ) + { + s_body += " "; + s_body += key; + s_body += ":"; + } IpsManager::option_begin(sc, key, rule_proto); } @@ -1021,6 +1079,16 @@ void parse_rule_opt_set( if ( s_ignore ) return; + if ( s_capture ) + { + s_body += opt; + if ( val and *val ) + { + s_body += " "; + s_body += val; + } + s_body += ","; + } IpsManager::option_set(sc, key, opt, val); } @@ -1029,6 +1097,11 @@ void parse_rule_opt_end(SnortConfig* sc, const char* key, OptTreeNode* otn) if ( s_ignore ) return; + if ( s_capture ) + { + s_body.erase(s_body.length()-1, 1); + s_body += ";"; + } RuleOptType type = OPT_TYPE_MAX; IpsManager::option_end(sc, otn, otn->snort_protocol_id, key, type); @@ -1063,9 +1136,13 @@ OptTreeNode* parse_rule_open(SnortConfig* sc, RuleTreeNode& rtn, bool stub) IpsManager::reset_options(); + s_capture = sc->dump_rule_meta(); + s_body = "("; + return otn; } +// FIXIT-H parse_rule_state needs parsing policy for reload static void parse_rule_state(SnortConfig* sc, const RuleTreeNode& rtn, OptTreeNode* otn) { if ( !otn->sigInfo.gid ) @@ -1076,11 +1153,14 @@ static void parse_rule_state(SnortConfig* sc, const RuleTreeNode& rtn, OptTreeNo ParseError("%u:%u rule state stubs do not support detection options", otn->sigInfo.gid, otn->sigInfo.sid); } - RuleKey key = { otn->sigInfo.gid, otn->sigInfo.sid }; - RuleState state = + RuleKey key = { - // FIXIT-H parse_rule_state needs parsing policy for reload snort::get_ips_policy()->policy_id, + otn->sigInfo.gid, + otn->sigInfo.sid + }; + RuleState state = + { rtn.action, otn->enable }; @@ -1120,24 +1200,31 @@ void parse_rule_close(SnortConfig* sc, RuleTreeNode& rtn, OptTreeNode* otn) if ( otn->is_rule_state_stub() ) { parse_rule_state(sc, rtn, otn); + delete rtn.header; + rtn.header = nullptr; return; } - static bool entered = false; - - if ( entered ) - entered = false; + if ( !s_so_rule and !sc->metadata_filter.empty() and !otn->metadata_matched() ) + { + delete otn; + FreeRuleTreeNode(&rtn); + ClearIpsOptionsVars(); + skip_count++; + return; + } + if ( s_so_rule ) + { + otn->sigInfo.gid = s_so_rule->gid; + otn->sigInfo.sid = s_so_rule->sid; + otn->sigInfo.rev = s_so_rule->rev; + } else if ( otn->soid ) { // for so rules, delete the otn and parse the actual rule - // but if already entered, don't recurse again - - // FIXIT-L RTN should be a proper object with better encapsulation - if ( rtn.sip ) - sfvar_free(rtn.sip); - if ( rtn.dip ) - sfvar_free(rtn.dip); + // keep the stub's rtn to allow user tuning of nets and ports + // if already entered, don't recurse again const char* rule = SoManager::get_so_rule(otn->soid, sc); IpsManager::reset_options(); @@ -1146,26 +1233,20 @@ void parse_rule_close(SnortConfig* sc, RuleTreeNode& rtn, OptTreeNode* otn) ParseError("SO rule %s not loaded.", otn->soid); else { - entered = true; + SoRule so_rule(&rtn, otn); + s_so_rule = &so_rule; parse_rules_string(sc, rule); + s_so_rule = nullptr; } delete otn; return; } - if ( !sc->metadata_filter.empty() and !otn->metadata_matched() ) - { - delete otn; - FreeRuleTreeNode(&rtn); - ClearIpsOptionsVars(); - skip_count++; - return; - } - /* The IPs in the test node get freed in ProcessHeadNode if there is * already a matching RTN. The portobjects will get freed when the * port var table is freed */ - RuleTreeNode* new_rtn = ProcessHeadNode(sc, &rtn, rtn.listhead); + RuleTreeNode* tmp = s_so_rule ? s_so_rule->rtn : &rtn; + RuleTreeNode* new_rtn = ProcessHeadNode(sc, tmp); addRtnToOtn(sc, otn, new_rtn); OptTreeNode* otn_dup = @@ -1236,16 +1317,15 @@ void parse_rule_close(SnortConfig* sc, RuleTreeNode& rtn, OptTreeNode* otn) add_service_to_otn(sc, otn, service.c_str()); } - /* - * The src/dst port parsing must be done before the Head Nodes are processed, since they must - * compare the ports/port_objects to find the right rtn list to add the otn rule to. - * - * After otn processing we can finalize port object processing for this rule - */ - if ( FinishPortListRule( - sc->port_tables, new_rtn, otn, rtn.snort_protocol_id, sc->fast_pattern_config) ) + if ( FinishPortListRule(sc->port_tables, new_rtn, otn, sc->fast_pattern_config) ) ParseError("Failed to finish a port list rule."); + if ( s_capture ) + { + s_body += " )"; + otn->sigInfo.body = new std::string(s_body); + } + ClearIpsOptionsVars(); } diff --git a/src/parser/parse_stream.cc b/src/parser/parse_stream.cc index 61c86348c..f4bb96041 100644 --- a/src/parser/parse_stream.cc +++ b/src/parser/parse_stream.cc @@ -473,16 +473,13 @@ static const State* get_state(int num, TokenType type, const string& tok) struct RuleParseState { RuleTreeNode rtn; - OptTreeNode* otn; + OptTreeNode* otn = nullptr; string key; string opt; string val; bool tbd; - - RuleParseState() - { otn = nullptr; } }; static bool exec( diff --git a/src/parser/parser.cc b/src/parser/parser.cc index a05eb3bda..355ccf4b4 100644 --- a/src/parser/parser.cc +++ b/src/parser/parser.cc @@ -383,6 +383,7 @@ void FreeRuleTreeNode(RuleTreeNode* rtn) idx = idx->next; delete tmp; } + delete rtn->header; } void DestroyRuleTreeNode(RuleTreeNode* rtn) diff --git a/src/ports/port_table.cc b/src/ports/port_table.cc index 26bd6a773..51dde27c4 100644 --- a/src/ports/port_table.cc +++ b/src/ports/port_table.cc @@ -257,8 +257,7 @@ static PortObject2* _merge_N_pol( // Add the plx node to the PLX hash table stat = mhashx->insert(&plx_tmp, ponew); - if ( stat == HASH_INTABLE ) - FatalError("Could not add merged plx to PLX HASH table-INTABLE\n"); + assert(stat == HASH_OK); return ponew; } @@ -510,9 +509,6 @@ static void PortTableCompileMergePortObjects(PortTable* p) { PortObject2* poa = (PortObject2*)node->data; - if ( !poa ) - continue; - if ( !poa->port_list ) poa->port_list = new PortBitSet; } @@ -524,11 +520,7 @@ static void PortTableCompileMergePortObjects(PortTable* p) if ( poa ) { poa->port_cnt++; - - if ( poa->port_list ) - poa->port_list->set(i); - else - FatalError("NULL po->port_list in po on port %d\n", i); + poa->port_list->set(i); } } @@ -564,12 +556,8 @@ static void PortTableCompileMergePortObjects(PortTable* p) } #ifdef DEBUG -/* Verify all rules in 'po' list are in 'po2' hash - * - * return 0 - OK - * !0 - a rule in po is not in po2 - */ -static int _po2_include_po_rules(PortObject2* po2, PortObject* po) +// Verify all rules in 'po' list are in 'po2' hash +static void _po2_include_po_rules(PortObject2* po2, PortObject* po) { SF_LNODE* rpos; @@ -580,18 +568,13 @@ static int _po2_include_po_rules(PortObject2* po2, PortObject* po) { /* find it in po2 */ int* id = (int*)po2->rule_hash->find(pid); - - /* make sure it's in po2 */ - if ( !id ) - return 1; /* error */ + assert(id); } - - return 0; } // consistency check - part 1 // make sure each port is only in one composite port object -static bool PortTableConsistencyCheck(PortTable* p) +static void PortTableConsistencyCheck(PortTable* p) { std::unique_ptr upA(new char[SFPO_MAX_PORTS]); char* parray = upA.get(); @@ -603,11 +586,6 @@ static bool PortTableConsistencyCheck(PortTable* p) { PortObject2* po = (PortObject2*)node->data; - if ( !po ) - { - return false; - } - if ( !po->port_cnt ) /* port object is not used ignore it */ continue; @@ -615,15 +593,11 @@ static bool PortTableConsistencyCheck(PortTable* p) { if ( PortObjectHasPort( (PortObject*)po, i) ) { - if ( parray[i] ) - return false; - + assert(!parray[i]); parray[i] = 1; } } } - - return true; } // consistency check - part 2 @@ -635,7 +609,7 @@ static bool PortTableConsistencyCheck(PortTable* p) * check that each port it reference has all of the rules * referenced to that port in the composite object */ -static bool PortTableConsistencyCheck2(PortTable* p) +static void PortTableConsistencyCheck2(PortTable* p) { SF_LNODE* pos; PortObject2* lastpo = nullptr; @@ -664,17 +638,12 @@ static bool PortTableConsistencyCheck2(PortTable* p) /* small optimization*/ if ( lastpo != p->pt_port_object[i] ) { - if ( _po2_include_po_rules(p->pt_port_object[i], ipo) ) - { - return false; - } + _po2_include_po_rules(p->pt_port_object[i], ipo); lastpo = p->pt_port_object[i]; } } } } - - return true; } #endif @@ -784,8 +753,8 @@ int PortTableCompile(PortTable* p) PortTableCompileMergePortObjects(p); #ifdef DEBUG - assert(PortTableConsistencyCheck(p)); - assert(PortTableConsistencyCheck2(p)); + PortTableConsistencyCheck(p); + PortTableConsistencyCheck2(p); #endif return 0; diff --git a/src/ports/port_var_table.cc b/src/ports/port_var_table.cc index 89ed38436..c7871d71c 100644 --- a/src/ports/port_var_table.cc +++ b/src/ports/port_var_table.cc @@ -25,6 +25,8 @@ #include "hash/ghash.h" #include "hash/hash_defs.h" +#include "main/snort_config.h" +#include "utils/util.h" using namespace snort; @@ -89,6 +91,14 @@ PortObject* PortVarTableFind(PortVarTable* h, const char* name) if (!h || !name) return nullptr; - return (PortObject*)h->find(name); + PortObject* po = (PortObject*)h->find(name); + + if ( !po and SnortConfig::dump_rule_info() and strstr(name, "PORT") ) + { + po = PortObjectNew(); + po->name = snort_strdup(name); + PortVarTableAdd(h, po); + } + return po; } diff --git a/src/search_engines/hyperscan.cc b/src/search_engines/hyperscan.cc index 452c422b6..712a51205 100644 --- a/src/search_engines/hyperscan.cc +++ b/src/search_engines/hyperscan.cc @@ -105,7 +105,6 @@ typedef std::vector PatternVector; static std::vector s_scratch; static unsigned int scratch_index; -static bool scratch_registered = false; static ScratchAllocator* scratcher = nullptr; struct ScanContext @@ -381,12 +380,9 @@ static void mod_dtor(Module* p) static Mpse* hs_ctor( SnortConfig* sc, class Module*, const MpseAgent* a) { - if ( !scratch_registered ) - { - s_scratch.resize(sc->num_slots); - scratch_index = scratcher->get_id(); - scratch_registered = true; - } + if ( s_scratch.empty() ) + s_scratch.resize(sc->num_slots, nullptr); + return new HyperscanMpse(sc, a); } diff --git a/src/service_inspectors/http2_inspect/http2_stream.h b/src/service_inspectors/http2_inspect/http2_stream.h index 3d635f3d3..a409772a0 100644 --- a/src/service_inspectors/http2_inspect/http2_stream.h +++ b/src/service_inspectors/http2_inspect/http2_stream.h @@ -48,7 +48,7 @@ public: uint32_t get_xtradata_mask() { return (current_frame != nullptr) ? current_frame->get_xtradata_mask() : 0; } Http2Frame *get_current_frame() { return current_frame; } - + Http2DataCutter* get_data_cutter(HttpCommon::SourceId source_id); void set_data_cutter(Http2DataCutter* cutter, HttpCommon::SourceId source_id) { data_cutter[source_id] = cutter; } diff --git a/src/service_inspectors/http_inspect/http_cutter.cc b/src/service_inspectors/http_inspect/http_cutter.cc index 0c4f0069d..5898cb01f 100644 --- a/src/service_inspectors/http_inspect/http_cutter.cc +++ b/src/service_inspectors/http_inspect/http_cutter.cc @@ -779,7 +779,7 @@ bool HttpBodyCutter::dangerous(const uint8_t* data, uint32_t length) uint8_t* decomp_output = nullptr; // Zipped flows must be decompressed before we can check them. Unzipping for detained - // inspection is completely separate from the unzipping done later in reassemble(). + // inspection is completely separate from the unzipping done later in reassemble(). if ((compression == CMP_GZIP) || (compression == CMP_DEFLATE)) { const uint32_t decomp_buffer_size = MAX_OCTETS; diff --git a/src/stream/tcp/tcp_stream_session.cc b/src/stream/tcp/tcp_stream_session.cc index ae76bb21c..ef929c9d4 100644 --- a/src/stream/tcp/tcp_stream_session.cc +++ b/src/stream/tcp/tcp_stream_session.cc @@ -398,25 +398,3 @@ void TcpStreamSession::start_proxy() config->policy = StreamPolicy::OS_PROXY; } -//------------------------------------------------------------------------- -// tcp module stuff -//------------------------------------------------------------------------- - -void TcpStreamSession::print() -{ - char buf[64]; - - LogMessage("TcpStreamSession:\n"); - sfip_ntop(&flow->server_ip, buf, sizeof(buf)); - LogMessage(" server IP: %s\n", buf); - sfip_ntop(&flow->client_ip, buf, sizeof(buf)); - LogMessage(" client IP: %s\n", buf); - LogMessage(" server port: %d\n", flow->server_port); - LogMessage(" client port: %d\n", flow->client_port); - LogMessage(" flags: 0x%X\n", flow->get_session_flags()); - LogMessage("Client Tracker:\n"); - client.print(); - LogMessage("Server Tracker:\n"); - server.print(); -} - diff --git a/src/stream/tcp/tcp_stream_session.h b/src/stream/tcp/tcp_stream_session.h index 027fcfaaf..eb4dc54b9 100644 --- a/src/stream/tcp/tcp_stream_session.h +++ b/src/stream/tcp/tcp_stream_session.h @@ -57,7 +57,6 @@ public: void reset(); void start_proxy(); - void print(); void SetPacketHeaderFoo(const snort::Packet* p); void GetPacketHeaderFoo(DAQ_PktHdr_t* pkth, uint32_t dir); diff --git a/src/stream/tcp/tcp_stream_tracker.cc b/src/stream/tcp/tcp_stream_tracker.cc index b3711884c..b2e078c49 100644 --- a/src/stream/tcp/tcp_stream_tracker.cc +++ b/src/stream/tcp/tcp_stream_tracker.cc @@ -729,17 +729,3 @@ void TcpStreamTracker::finalize_held_packet(Flow* flow) } } -void TcpStreamTracker::print() -{ - LogMessage(" + TcpTracker +\n"); - LogMessage(" state: %s\n", tcp_state_names[ tcp_state ]); - LogMessage(" iss: 0x%X\n", iss); - LogMessage(" ts_last: %u\n", ts_last); - LogMessage(" wscale: %u\n", wscale); - LogMessage(" mss: 0x%08X\n", mss); - LogMessage(" snd_una: %X\n", snd_una); - LogMessage(" snd_nxt: %X\n", snd_nxt); - LogMessage(" snd_win: %u\n", snd_wnd); - LogMessage(" rcv_nxt: %X\n", rcv_nxt); - LogMessage(" r_win_base: %X\n", r_win_base); -} diff --git a/src/stream/tcp/tcp_stream_tracker.h b/src/stream/tcp/tcp_stream_tracker.h index bcc622a60..941627717 100644 --- a/src/stream/tcp/tcp_stream_tracker.h +++ b/src/stream/tcp/tcp_stream_tracker.h @@ -257,8 +257,8 @@ public: { return flush_policy; } virtual void init_tcp_state(); - virtual void print(); virtual void init_flush_policy(); + virtual void set_splitter(snort::StreamSplitter* ss); virtual void set_splitter(const snort::Flow* flow); diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt index c06eff91e..d22b039ca 100644 --- a/src/utils/CMakeLists.txt +++ b/src/utils/CMakeLists.txt @@ -1,6 +1,5 @@ set( UTIL_INCLUDES - bitop.h boyer_moore.h cpp_macros.h endian.h diff --git a/src/utils/test/CMakeLists.txt b/src/utils/test/CMakeLists.txt index 181baf831..9a9eb166e 100644 --- a/src/utils/test/CMakeLists.txt +++ b/src/utils/test/CMakeLists.txt @@ -5,4 +5,3 @@ add_cpputest( boyer_moore_test add_cpputest( memcap_allocator_test ) -add_catch_test( bitop_test )