From: Michael Altizer (mialtize) Date: Tue, 6 Oct 2020 20:54:26 +0000 (+0000) Subject: Merge pull request #2494 in SNORT/snort3 from ~MIALTIZE/snort3:binder_rework to master X-Git-Tag: 3.0.3-2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=98a4cad5fb0e92db6e5a4a11305d1e3ab03fe73f;p=thirdparty%2Fsnort3.git Merge pull request #2494 in SNORT/snort3 from ~MIALTIZE/snort3:binder_rework to master Squashed commit of the following: commit c7420f49c5918ac276b666c5740997b3cefe85fe Author: Michael Altizer Date: Fri Sep 18 15:40:17 2020 -0400 binder: Allow binding based on address spaces commit 37dc13fc0a0d9ebc1653daab256218dfa1690203 Author: Michael Altizer Date: Fri Sep 18 15:40:17 2020 -0400 binder: Allow directional binding based on interfaces commit 9fdb963c5382952289f45a5c84a3f12389ecd988 Author: Michael Altizer Date: Fri Sep 18 15:40:17 2020 -0400 binder: Enforce directionality, add intfs, rename groups, cleanup - The src parameters now strictly apply to the client, while the dst parameters apply to the server. Previously, it would match in either direction as long as all directional fields matched in a given direction. - The zones, src_zone, and dst_zone parameters have been renamed to groups, src_groups, and dst_groups. - The ifaces parameter has been renamed to intfs. - Intfs and groups can now handle the full range of legal values (int32 and int16, respectively). - When role is used in a session binding, it will now only apply the session inspector binding to the side of the conversation associated with the role. (Previously, it would apply the session inspector to both sides.) - Binder configuration validation has gotten a bit stricter and more informative in the case of violations. commit f6cc5b21bfbc4a0cbbedb2f57ce09f5c0623df87 Author: Michael Altizer Date: Fri Sep 18 15:40:17 2020 -0400 normalizer: Move TTL configuration toggle to inspector configure() This prevents non-deterministic behavior influenced by the order of the network and normalizer module configurations being parsed from Lua. --- diff --git a/src/flow/flow.h b/src/flow/flow.h index 8177b0d4a..f6d3d766e 100644 --- a/src/flow/flow.h +++ b/src/flow/flow.h @@ -274,12 +274,24 @@ public: ssn_client->add_ref(); } + void clear_client() + { + ssn_client->rem_ref(); + ssn_client = nullptr; + } + void set_server(Inspector* ins) { ssn_server = ins; ssn_server->add_ref(); } + void clear_server() + { + ssn_server->rem_ref(); + ssn_server = nullptr; + } + void set_clouseau(Inspector* ins) { clouseau = ins; @@ -424,14 +436,22 @@ public: // FIXIT-M privatize if possible uint32_t default_session_timeout; + int32_t client_intf; + int32_t server_intf; + + int16_t client_group; + int16_t server_group; + uint16_t client_port; uint16_t server_port; uint16_t ssn_policy; uint16_t session_state; - uint8_t inner_client_ttl, inner_server_ttl; - uint8_t outer_client_ttl, outer_server_ttl; + uint8_t inner_client_ttl; + uint8_t inner_server_ttl; + uint8_t outer_client_ttl; + uint8_t outer_server_ttl; uint8_t response_count; diff --git a/src/flow/flow_control.cc b/src/flow/flow_control.cc index d2286c700..dd573692b 100644 --- a/src/flow/flow_control.cc +++ b/src/flow/flow_control.cc @@ -204,8 +204,9 @@ static void init_roles_ip(const Packet* p, Flow* flow) flow->server_ip = *p->ptrs.ip_api.get_dst(); } -static void init_roles_tcp(const Packet* p, Flow* flow) +static bool init_roles_tcp(const Packet* p, Flow* flow) { + bool swapped; if ( p->ptrs.tcph->is_syn_only() ) { flow->ssn_state.direction = FROM_CLIENT; @@ -213,6 +214,7 @@ static void init_roles_tcp(const Packet* p, Flow* flow) flow->client_port = p->ptrs.sp; flow->server_ip = *p->ptrs.ip_api.get_dst(); flow->server_port = p->ptrs.dp; + swapped = false; } else if ( p->ptrs.tcph->is_syn_ack() ) { @@ -221,6 +223,7 @@ static void init_roles_tcp(const Packet* p, Flow* flow) flow->client_port = p->ptrs.dp; flow->server_ip = *p->ptrs.ip_api.get_src(); flow->server_port = p->ptrs.sp; + swapped = true; } else if (p->ptrs.sp > p->ptrs.dp) { @@ -229,6 +232,7 @@ static void init_roles_tcp(const Packet* p, Flow* flow) flow->client_port = p->ptrs.sp; flow->server_ip = *p->ptrs.ip_api.get_dst(); flow->server_port = p->ptrs.dp; + swapped = false; } else { @@ -237,7 +241,9 @@ static void init_roles_tcp(const Packet* p, Flow* flow) flow->client_port = p->ptrs.dp; flow->server_ip = *p->ptrs.ip_api.get_src(); flow->server_port = p->ptrs.sp; + swapped = true; } + return swapped; } static void init_roles_udp(const Packet* p, Flow* flow) @@ -249,8 +255,9 @@ static void init_roles_udp(const Packet* p, Flow* flow) flow->server_port = p->ptrs.dp; } -static void init_roles_user(const Packet* p, Flow* flow) +static bool init_roles_user(const Packet* p, Flow* flow) { + bool swapped; if ( p->ptrs.decode_flags & DECODE_C2S ) { flow->ssn_state.direction = FROM_CLIENT; @@ -258,6 +265,7 @@ static void init_roles_user(const Packet* p, Flow* flow) flow->client_port = p->ptrs.sp; flow->server_ip = *p->ptrs.ip_api.get_dst(); flow->server_port = p->ptrs.dp; + swapped = false; } else { @@ -266,35 +274,54 @@ static void init_roles_user(const Packet* p, Flow* flow) flow->client_port = p->ptrs.dp; flow->server_ip = *p->ptrs.ip_api.get_src(); flow->server_port = p->ptrs.sp; + swapped = true; } + return swapped; } // FIXIT-L init_roles should take const Packet* static void init_roles(Packet* p, Flow* flow) { + bool swapped = false; switch ( flow->pkt_type ) { - case PktType::IP: - case PktType::ICMP: - init_roles_ip(p, flow); - break; + case PktType::IP: + case PktType::ICMP: + init_roles_ip(p, flow); + break; - case PktType::TCP: - init_roles_tcp(p, flow); - break; + case PktType::TCP: + swapped = init_roles_tcp(p, flow); + break; - case PktType::UDP: - init_roles_udp(p, flow); - break; + case PktType::UDP: + init_roles_udp(p, flow); + break; - case PktType::PDU: - case PktType::FILE: - init_roles_user(p, flow); - break; + case PktType::PDU: + case PktType::FILE: + swapped = init_roles_user(p, flow); + break; - default: - break; + default: + break; + } + + if (swapped) + { + flow->client_intf = p->pkth->egress_index; + flow->server_intf = p->pkth->ingress_index; + flow->client_group = p->pkth->egress_group; + flow->server_group = p->pkth->ingress_group; } + else + { + flow->client_intf = p->pkth->ingress_index; + flow->server_intf = p->pkth->egress_index; + flow->client_group = p->pkth->ingress_group; + flow->server_group = p->pkth->egress_group; + } + flow->flags.app_direction_swapped = false; if ( flow->ssn_state.direction == FROM_CLIENT ) p->packet_flags |= PKT_FROM_CLIENT; diff --git a/src/framework/bits.h b/src/framework/bits.h index e4fa8e920..60fe4c7d3 100644 --- a/src/framework/bits.h +++ b/src/framework/bits.h @@ -27,7 +27,6 @@ typedef std::bitset<65536> PortBitSet; typedef std::bitset<4096> VlanBitSet; typedef std::bitset<256> ByteBitSet; -typedef std::bitset<64> ZoneBitSet; #endif diff --git a/src/framework/parameter.cc b/src/framework/parameter.cc index ec15144ff..3deed16a5 100644 --- a/src/framework/parameter.cc +++ b/src/framework/parameter.cc @@ -126,13 +126,13 @@ int64_t Parameter::get_int(const char* r) // validation methods //-------------------------------------------------------------------------- -static bool valid_bool(Value& v, const char*) +static bool valid_bool(const Value& v, const char*) { return v.get_type() == Value::VT_BOOL; } // FIXIT-L allow multiple , separated ranges -static bool valid_int(Value& v, const char* r) +static bool valid_int(const Value& v, const char* r) { if ( v.get_type() != Value::VT_NUM ) return false; @@ -183,11 +183,11 @@ static bool valid_int(Value& v, const char* r) // their actual, specific semantics instead of trying to explain the syntax. this // also ensures that an int-type range is not applied to a string. -static bool valid_interval(Value&, const char*) +static bool valid_interval(const Value&, const char*) { return true; } // FIXIT-L allow multiple , separated ranges -static bool valid_real(Value& v, const char* r) +static bool valid_real(const Value& v, const char* r) { if ( v.get_type() != Value::VT_NUM ) return false; @@ -224,7 +224,7 @@ static bool valid_real(Value& v, const char* r) return true; } -static bool valid_string(Value& v, const char* r) +static bool valid_string(const Value& v, const char* r) { if ( v.get_type() != Value::VT_STR ) return false; @@ -241,7 +241,7 @@ static bool valid_string(Value& v, const char* r) return len <= max; } -static bool valid_select(Value& v, const char* r) +static bool valid_select(const Value& v, const char* r) { if ( v.get_type() != Value::VT_STR ) return false; @@ -554,7 +554,7 @@ TEST_CASE("bool", "[Parameter]") struct { bool expected; - bool (*validate)(Value&, const char*); + bool (*validate)(const Value&, const char*); double value; const char* range; } @@ -621,11 +621,11 @@ TEST_CASE("num", "[Parameter]") struct { bool expected; - bool (*validate)(Value&, const char*); + bool (*validate)(const Value&, const char*); const char* value; const char* range; } -string_tests[] = +const_string_tests[] = { // __STRDUMP_DISABLE__ { true, valid_string, "green", "(optional)" }, @@ -638,6 +638,20 @@ string_tests[] = { false, valid_select, "blue", "red | green | yellow" }, { false, valid_select, "green", nullptr }, + { false, nullptr, 0, nullptr } +// __STRDUMP_ENABLE__ +}; + +struct +{ + bool expected; + bool (*validate)(Value&, const char*); + const char* value; + const char* range; +} +string_tests[] = +{ +// __STRDUMP_DISABLE__ { true, valid_enum, "green", "red | green | yellow" }, { false, valid_enum, "blue", "red | green | yellow" }, { false, valid_enum, "green", nullptr }, @@ -680,8 +694,16 @@ string_tests[] = TEST_CASE("string", "[Parameter]") { - auto test = string_tests; + auto ctest = const_string_tests; + while ( ctest->validate ) + { + Value v(ctest->value); + bool result = ctest->validate(v, ctest->range); + CHECK(result == ctest->expected); + ++ctest; + } + auto test = string_tests; while ( test->validate ) { Value v(test->value); diff --git a/src/framework/value.cc b/src/framework/value.cc index 9f1d9746e..16bdf0c45 100644 --- a/src/framework/value.cc +++ b/src/framework/value.cc @@ -108,19 +108,6 @@ void Value::get_bits(PortBitSet& list) const } } -void Value::get_bits(ZoneBitSet& list) const -{ - list.reset(); - std::size_t len = str.size(); - assert(len == list.size()); - - for ( std::size_t n = 0; n < len; ++n ) - { - if ( str[n] == '1' ) - list.set(n); - } -} - void Value::get_bits(VlanBitSet& list) const { list.reset(); diff --git a/src/framework/value.h b/src/framework/value.h index aa93044f0..2934c1fc1 100644 --- a/src/framework/value.h +++ b/src/framework/value.h @@ -76,7 +76,7 @@ public: return *this; } - ValueType get_type() + ValueType get_type() const { return type; } ~Value() @@ -176,7 +176,6 @@ public: void get_bits(PortBitSet&) const; void get_bits(VlanBitSet&) const; void get_bits(ByteBitSet&) const; - void get_bits(ZoneBitSet&) const; void lower() { std::transform(str.begin(), str.end(), str.begin(), ::tolower); } diff --git a/src/main/policy.cc b/src/main/policy.cc index 5c12f4438..19df09fc7 100644 --- a/src/main/policy.cc +++ b/src/main/policy.cc @@ -151,7 +151,7 @@ PolicyMap::PolicyMap(PolicyMap* other_map) { add_shell(new Shell(nullptr, true)); empty_ips_policy = new IpsPolicy(ips_policy.size()); - ips_policy.emplace_back(empty_ips_policy); + ips_policy.push_back(empty_ips_policy); } set_inspection_policy(inspection_policy[0]); @@ -232,40 +232,36 @@ void PolicyMap::clone(PolicyMap *other_map) user_network = other_map->user_network; } -unsigned PolicyMap::add_inspection_shell(Shell* sh) +InspectionPolicy* PolicyMap::add_inspection_shell(Shell* sh) { unsigned idx = inspection_policy.size(); - shells.emplace_back(sh); - inspection_policy.emplace_back(new InspectionPolicy(idx)); + InspectionPolicy* p = new InspectionPolicy(idx); - shell_map[sh] = std::make_shared(inspection_policy.back(), nullptr, nullptr); - return idx; + shells.push_back(sh); + inspection_policy.push_back(p); + shell_map[sh] = std::make_shared(p, nullptr, nullptr); + + return p; } -unsigned PolicyMap::add_ips_shell(Shell* sh) +IpsPolicy* PolicyMap::add_ips_shell(Shell* sh) { unsigned idx = ips_policy.size(); - shells.emplace_back(sh); - ips_policy.emplace_back(new IpsPolicy(idx)); - shell_map[sh] = std::make_shared(nullptr, ips_policy.back(), nullptr); - return idx; -} + IpsPolicy* p = new IpsPolicy(idx); -unsigned PolicyMap::add_network_shell(Shell* sh) -{ - unsigned idx = network_policy.size(); - shells.emplace_back(sh); - network_policy.emplace_back(new NetworkPolicy(idx)); - shell_map[sh] = std::make_shared(nullptr, nullptr, network_policy.back()); - return idx; + shells.push_back(sh); + ips_policy.push_back(p); + shell_map[sh] = std::make_shared(nullptr, p, nullptr); + + return p; } std::shared_ptr PolicyMap::add_shell(Shell* sh) { - shells.emplace_back(sh); - inspection_policy.emplace_back(new InspectionPolicy(inspection_policy.size())); - ips_policy.emplace_back(new IpsPolicy(ips_policy.size())); - network_policy.emplace_back(new NetworkPolicy(network_policy.size())); + shells.push_back(sh); + inspection_policy.push_back(new InspectionPolicy(inspection_policy.size())); + ips_policy.push_back(new IpsPolicy(ips_policy.size())); + network_policy.push_back(new NetworkPolicy(network_policy.size())); return shell_map[sh] = std::make_shared(inspection_policy.back(), ips_policy.back(), network_policy.back()); @@ -275,7 +271,7 @@ std::shared_ptr PolicyMap::get_policies(Shell* sh) { const auto& pt = shell_map.find(sh); - return pt == shell_map.end() ? nullptr:pt->second; + return pt == shell_map.end() ? nullptr : pt->second; } //------------------------------------------------------------------------- diff --git a/src/main/policy.h b/src/main/policy.h index 0fa0a5c60..89681ad71 100644 --- a/src/main/policy.h +++ b/src/main/policy.h @@ -211,9 +211,8 @@ public: PolicyMap(PolicyMap* old_map = nullptr); ~PolicyMap(); - unsigned add_inspection_shell(Shell*); - unsigned add_ips_shell(Shell*); - unsigned add_network_shell(Shell*); + InspectionPolicy* add_inspection_shell(Shell*); + IpsPolicy* add_ips_shell(Shell*); std::shared_ptr add_shell(Shell*); std::shared_ptr get_policies(Shell* sh); void clone(PolicyMap *old_map); diff --git a/src/managers/inspector_manager.cc b/src/managers/inspector_manager.cc index d77bc3afa..f2c2da647 100644 --- a/src/managers/inspector_manager.cc +++ b/src/managers/inspector_manager.cc @@ -1105,7 +1105,6 @@ static inline void execute( } } -// FIXIT-L use inspection events instead of exec void InspectorManager::bumble(Packet* p) { Flow* flow = p->flow; diff --git a/src/managers/module_manager.cc b/src/managers/module_manager.cc index 5248f070c..4e0206f13 100644 --- a/src/managers/module_manager.cc +++ b/src/managers/module_manager.cc @@ -435,7 +435,7 @@ static bool ignored(const char* fqn) // // FIXIT-L presently no way to catch errors like EXTERNAL_NET = not HOME_NET // which becomes a bool var and is ignored. -static bool set_var(const char* fqn, Value& v) +static bool set_var(const char* fqn, const Value& v) { bool to_be_set = v.get_type() == Value::VT_STR; diff --git a/src/network_inspectors/binder/CMakeLists.txt b/src/network_inspectors/binder/CMakeLists.txt index 2bb28f3ed..d74028fad 100644 --- a/src/network_inspectors/binder/CMakeLists.txt +++ b/src/network_inspectors/binder/CMakeLists.txt @@ -1,6 +1,7 @@ set(FILE_LIST binder.cc + binding.cc binding.h bind_module.cc bind_module.h diff --git a/src/network_inspectors/binder/bind_module.cc b/src/network_inspectors/binder/bind_module.cc index 93755f83d..1cdcd784b 100644 --- a/src/network_inspectors/binder/bind_module.cc +++ b/src/network_inspectors/binder/bind_module.cc @@ -24,9 +24,12 @@ #include "bind_module.h" +#include + #include "log/messages.h" #include "main/shell.h" #include "main/snort_config.h" +#include "managers/module_manager.h" #include "parser/parse_ip.h" #include "protocols/packet.h" @@ -41,11 +44,15 @@ THREAD_LOCAL BindStats bstats; static const PegInfo bind_pegs[] = { - { CountType::SUM, "packets", "initial bindings" }, - { CountType::SUM, "resets", "reset bindings" }, - { CountType::SUM, "blocks", "block bindings" }, - { CountType::SUM, "allows", "allow bindings" }, - { CountType::SUM, "inspects", "inspect bindings" }, + { CountType::SUM, "new_flows", "new flows evaluated" }, + { CountType::SUM, "service_changes", "flow service changes evaluated" }, + { CountType::SUM, "assistant_inspectors", "flow assistant inspector requests handled" }, + { CountType::SUM, "new_standby_flows", "new HA flows evaluated" }, + { CountType::SUM, "no_match", "binding evaluations that had no matches" }, + { CountType::SUM, "resets", "reset actions bound" }, + { CountType::SUM, "blocks", "block actions bound" }, + { CountType::SUM, "allows", "allow actions bound" }, + { CountType::SUM, "inspects", "inspect actions bound" }, { CountType::END, nullptr, nullptr } }; @@ -58,12 +65,9 @@ static const Parameter binder_when_params[] = // FIXIT-L when.policy_id should be an arbitrary string auto converted // into index for binder matching and lookups - { "ips_policy_id", Parameter::PT_INT, "0:max32", "0", + { "ips_policy_id", Parameter::PT_INT, "0:max32", nullptr, "unique ID for selection of this config by external logic" }, - { "ifaces", Parameter::PT_BIT_LIST, "255", nullptr, - "list of interface indices" }, - { "vlans", Parameter::PT_BIT_LIST, "4095", nullptr, "list of VLAN IDs" }, @@ -88,14 +92,26 @@ static const Parameter binder_when_params[] = { "dst_ports", Parameter::PT_BIT_LIST, "65535", nullptr, "list of destination ports" }, - { "zones", Parameter::PT_BIT_LIST, "63", nullptr, - "zones" }, + { "intfs", Parameter::PT_STRING, nullptr, nullptr, + "list of interface IDs" }, + + { "src_intfs", Parameter::PT_STRING, nullptr, nullptr, + "list of source interface IDs" }, + + { "dst_intfs", Parameter::PT_STRING, nullptr, nullptr, + "list of destination interface IDs" }, - { "src_zone", Parameter::PT_BIT_LIST, "63", nullptr, - "source zone" }, + { "groups", Parameter::PT_STRING, nullptr, nullptr, + "list of interface group IDs" }, - { "dst_zone", Parameter::PT_BIT_LIST, "63", nullptr, - "destination zone" }, + { "src_groups", Parameter::PT_STRING, nullptr, nullptr, + "list of source interface group IDs" }, + + { "dst_groups", Parameter::PT_STRING, nullptr, nullptr, + "list of destination group IDs" }, + + { "addr_spaces", Parameter::PT_STRING, nullptr, nullptr, + "list of address space IDs" }, { "role", Parameter::PT_ENUM, "client | server | any", "any", "use the given configuration on one or any end of a session" }, @@ -103,6 +119,16 @@ static const Parameter binder_when_params[] = { "service", Parameter::PT_STRING, nullptr, nullptr, "override default configuration" }, + // FIXIT-D deprecated zone parameters to be removed + { "zones", Parameter::PT_STRING, nullptr, nullptr, + "deprecated alias for groups" }, + + { "src_zone", Parameter::PT_STRING, nullptr, nullptr, + "deprecated alias for src_groups" }, + + { "dst_zone", Parameter::PT_STRING, nullptr, nullptr, + "deprecated alias for dst_groups" }, + { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } }; @@ -143,24 +169,56 @@ static const Parameter s_params[] = { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr } }; -BinderModule::BinderModule() : Module(BIND_NAME, BIND_HELP, s_params, true) -{ work = nullptr; } +template +static bool parse_int_set(const Value& v, unordered_set& set) +{ + assert(v.get_type() == Value::VT_STR); + + set.clear(); + + string pl = v.get_string(); + + stringstream ss(pl); + ss >> setbase(0); + + uint64_t n; + + while ( ss >> n ) + { + if ( n > numeric_limits::max() ) + return false; + + set.insert(n); + } + if ( !ss.eof() ) + return false; + + return true; +} + +BinderModule::BinderModule() : Module(BIND_NAME, BIND_HELP, s_params, true) { } BinderModule::~BinderModule() { - if ( work ) - delete work; + bindings.clear(); + binding.clear(); } ProfileStats* BinderModule::get_profile() const { return &bindPerfStats; } -void BinderModule::add_file(const char* name, const char* type) +bool BinderModule::add_policy_file(const char* name, const char* type) { - work->use.name = name; - work->use.type = type; - use_name_count++; - use_type_count++; + if (!policy_type.empty()) + { + ParseError("Only one type of policy may be specified per binding"); + return false; + } + + policy_filename = name; + policy_type = type; + + return true; } static void set_ip_var(sfip_var_t*& var, const char* val) @@ -170,40 +228,55 @@ static void set_ip_var(sfip_var_t*& var, const char* val) var = sfip_var_from_string(val, "binder"); } -bool BinderModule::set(const char* fqn, Value& v, SnortConfig*) +bool BinderModule::begin(const char* fqn, int idx, SnortConfig*) { - if ( !work ) - return false; + if ( idx && !strcmp(fqn, BIND_NAME) ) + { + binding.clear(); + policy_filename.clear(); + policy_type.clear(); + } - // both - else if ( !strcmp(fqn, "binder.when.service") ) - work->when.svc = v.get_string(); + return true; +} +bool BinderModule::set(const char* fqn, Value& v, SnortConfig*) +{ + // both + if ( !strcmp(fqn, "binder.when.service") ) + { + binding.when.svc = v.get_string(); + binding.when.add_criteria(BindWhen::Criteria::BWC_SVC); + } else if ( !strcmp(fqn, "binder.use.service") ) - work->use.svc = v.get_string(); + binding.use.svc = v.get_string(); // when - else if ( v.is("ifaces") ) - v.get_bits(work->when.ifaces); - + else if ( v.is("ips_policy_id") ) + { + binding.when.ips_id_user = v.get_uint32(); + binding.when.add_criteria(BindWhen::Criteria::BWC_IPS_ID); + } + else if ( v.is("vlans") ) + { + v.get_bits(binding.when.vlans); + binding.when.add_criteria(BindWhen::Criteria::BWC_VLANS); + } else if ( v.is("nets") ) { - set_ip_var(work->when.src_nets, v.get_string()); - unsplit_nets = true; + set_ip_var(binding.when.src_nets, v.get_string()); + binding.when.add_criteria(BindWhen::Criteria::BWC_NETS); } else if ( v.is("src_nets") ) { - set_ip_var(work->when.src_nets, v.get_string()); - work->when.split_nets = true; + set_ip_var(binding.when.src_nets, v.get_string()); + binding.when.add_criteria(BindWhen::Criteria::BWC_SPLIT_NETS); } else if ( v.is("dst_nets") ) { - set_ip_var(work->when.dst_nets, v.get_string()); - work->when.split_nets = true; + set_ip_var(binding.when.dst_nets, v.get_string()); + binding.when.add_criteria(BindWhen::Criteria::BWC_SPLIT_NETS); } - else if ( v.is("ips_policy_id") ) - work->when.ips_id_user = v.get_uint32(); - else if ( v.is("proto") ) { const unsigned mask[] = @@ -211,174 +284,243 @@ bool BinderModule::set(const char* fqn, Value& v, SnortConfig*) PROTO_BIT__ANY_TYPE, PROTO_BIT__IP, PROTO_BIT__ICMP, PROTO_BIT__TCP, PROTO_BIT__UDP, PROTO_BIT__PDU, PROTO_BIT__FILE }; - work->when.protos = mask[v.get_uint8()]; + binding.when.protos = mask[v.get_uint8()]; + binding.when.add_criteria(BindWhen::Criteria::BWC_PROTO); } else if ( v.is("ports") ) { - v.get_bits(work->when.src_ports); - unsplit_ports = true; + v.get_bits(binding.when.src_ports); + binding.when.add_criteria(BindWhen::Criteria::BWC_PORTS); } else if ( v.is("src_ports") ) { - v.get_bits(work->when.src_ports); - work->when.split_ports = true; + v.get_bits(binding.when.src_ports); + binding.when.add_criteria(BindWhen::Criteria::BWC_SPLIT_PORTS); } else if ( v.is("dst_ports") ) { - v.get_bits(work->when.dst_ports); - work->when.split_ports = true; + v.get_bits(binding.when.dst_ports); + binding.when.add_criteria(BindWhen::Criteria::BWC_SPLIT_PORTS); } - - else if ( v.is("zones") ) + else if ( v.is("intfs") ) { - v.get_bits(work->when.src_zones); - unsplit_zones = true; + if (!parse_int_set(v, binding.when.src_intfs)) + return false; + binding.when.add_criteria(BindWhen::Criteria::BWC_INTFS); } - else if ( v.is("src_zone") ) + else if ( v.is("src_intfs") ) { - v.get_bits(work->when.src_zones); - work->when.split_zones = true; + if (!parse_int_set(v, binding.when.src_intfs)) + return false; + binding.when.add_criteria(BindWhen::Criteria::BWC_SPLIT_INTFS); } - else if ( v.is("dst_zone") ) + else if ( v.is("dst_intfs") ) { - v.get_bits(work->when.dst_zones); - work->when.split_zones = true; + if (!parse_int_set(v, binding.when.dst_intfs)) + return false; + binding.when.add_criteria(BindWhen::Criteria::BWC_SPLIT_INTFS); + } + else if ( v.is("groups") || v.is("zones") ) + { + if (!parse_int_set(v, binding.when.src_groups)) + return false; + binding.when.add_criteria(BindWhen::Criteria::BWC_GROUPS); + } + else if ( v.is("src_groups") || v.is("src_zone") ) + { + if (!parse_int_set(v, binding.when.src_groups)) + return false; + binding.when.add_criteria(BindWhen::Criteria::BWC_SPLIT_GROUPS); + } + else if ( v.is("dst_groups") || v.is("dst_zone") ) + { + if (!parse_int_set(v, binding.when.dst_groups)) + return false; + binding.when.add_criteria(BindWhen::Criteria::BWC_SPLIT_GROUPS); + } + else if ( v.is("addr_spaces") ) + { + if (!parse_int_set(v, binding.when.addr_spaces)) + return false; + binding.when.add_criteria(BindWhen::Criteria::BWC_ADDR_SPACES); } - else if ( v.is("role") ) - work->when.role = (BindWhen::Role)v.get_uint8(); - - else if ( v.is("vlans") ) - v.get_bits(work->when.vlans); + binding.when.role = (BindWhen::Role)v.get_uint8(); // use else if ( v.is("action") ) - work->use.action = (BindUse::Action)(v.get_uint8()); + binding.use.action = (BindUse::Action)(v.get_uint8()); else if ( v.is("file") ) - add_file(v.get_string(), FILE_KEY); - - else if ( v.is("inspection_policy") ) - add_file(v.get_string(), INSPECTION_KEY); - - else if ( v.is("ips_policy") ) - add_file(v.get_string(), IPS_KEY); - - else if ( v.is("name") ) { - work->use.name = v.get_string(); - use_name_count++; + if (!add_policy_file(v.get_string(), FILE_KEY)) + return false; } - else if ( v.is("type") ) + else if ( v.is("inspection_policy") ) { - work->use.type = v.get_string(); - if ( work->use.type == "gtp" ) - work->use.type = "gtp_inspect"; - use_type_count++; + if (!add_policy_file(v.get_string(), INSPECTION_KEY)) + return false; } - else - return false; - - return true; -} - -bool BinderModule::begin(const char* fqn, int idx, SnortConfig*) -{ - if ( idx && !strcmp(fqn, BIND_NAME) ) + else if ( v.is("ips_policy") ) { - work = new Binding; - unsplit_nets = false; - unsplit_ports = false; - unsplit_zones = false; - use_name_count = 0; - use_type_count = 0; + if (!add_policy_file(v.get_string(), IPS_KEY)) + return false; } + else if ( v.is("name") ) + binding.use.name = v.get_string(); + + else if ( v.is("type") ) + binding.use.type = v.get_string(); return true; } -static void file_name_type_error() -{ ParseError("you can't set binder.use file, detection_policy, or inspection_policy with type or name"); } - -static void split_nets_warning() -{ ParseWarning(WARN_CONF, "src_nets and dst_nets override nets"); } - -static void split_ports_warning() -{ ParseWarning(WARN_CONF, "src_ports and dst_ports override ports"); } - -static void split_zones_warning() -{ ParseWarning(WARN_CONF, "src_zones and dst_zones override zones"); } - bool BinderModule::end(const char* fqn, int idx, SnortConfig* sc) { - if ( idx && !strcmp(fqn, BIND_NAME) ) + if ( !strcmp(fqn, BIND_NAME) && idx ) { - if ( !work ) + // When validation + if ( binding.when.has_criteria(BindWhen::Criteria::BWC_NETS | BindWhen::Criteria::BWC_SPLIT_NETS) ) { - ParseError("invalid %s[%d]", fqn, idx); - return true; + ParseError("Can't specify 'nets' in combination with either of 'src_nets' or 'dst_nets'"); + return false; } - if ( unsplit_nets && work->when.split_nets ) - split_nets_warning(); - - if ( unsplit_ports && work->when.split_ports ) - split_ports_warning(); - - if ( unsplit_zones && work->when.split_zones ) - split_zones_warning(); + if ( binding.when.has_criteria(BindWhen::Criteria::BWC_PORTS | BindWhen::Criteria::BWC_SPLIT_PORTS) ) + { + ParseError("Can't specify 'ports' in combination with either of 'src_ports' or 'dst_ports'"); + return false; + } - if ( use_type_count > 1 || use_name_count > 1 ) - file_name_type_error(); + if ( binding.when.has_criteria(BindWhen::Criteria::BWC_INTFS | BindWhen::Criteria::BWC_SPLIT_INTFS) ) + { + ParseError("Can't specify 'intfs' in combination with either of 'src_intfs' or 'dst_intfs'"); + return false; + } - if ( work->use.type == FILE_KEY ) + if ( binding.when.has_criteria(BindWhen::Criteria::BWC_GROUPS | BindWhen::Criteria::BWC_SPLIT_GROUPS) ) { - Shell* sh = new Shell(work->use.name.c_str()); - auto policies = sc->policy_map->add_shell(sh); - work->use.inspection_index = policies->inspection->policy_id + 1; - work->use.ips_index = policies->ips->policy_id + 1; + ParseError("Can't specify 'groups' in combination with either of 'src_groups' or 'dst_groups'"); + return false; } - else if ( work->use.type == INSPECTION_KEY ) + + // Use validation + if ( !policy_filename.empty() ) { - Shell* sh = new Shell(work->use.name.c_str()); - work->use.inspection_index = sc->policy_map->add_inspection_shell(sh) + 1; + // Policy binding - Co-opts the binding structure, but doesn't want most of it. + if ( !binding.use.svc.empty() || !binding.use.type.empty() || + !binding.use.name.empty() || binding.use.action != BindUse::BA_INSPECT ) + { + ParseError("Policy bindings cannot specify any other use options"); + return false; + } + + if ( policy_type == FILE_KEY ) + { + Shell* sh = new Shell(policy_filename.c_str()); + auto policies = sc->policy_map->add_shell(sh); + binding.use.inspection_index = policies->inspection->policy_id; + binding.use.ips_index = policies->ips->policy_id; + } + else if ( policy_type == INSPECTION_KEY ) + { + Shell* sh = new Shell(policy_filename.c_str()); + InspectionPolicy* inspection_policy = sc->policy_map->add_inspection_shell(sh); + binding.use.inspection_index = inspection_policy->policy_id; + } + else if ( policy_type == IPS_KEY ) + { + Shell* sh = new Shell(policy_filename.c_str()); + IpsPolicy* ips_policy = sc->policy_map->add_ips_shell(sh); + binding.use.ips_index = ips_policy->policy_id; + } + + // Store the policy type and filename for verbose output + binding.use.type = policy_type; + binding.use.name = policy_filename; + + commit_policy_binding(); } - else if ( work->use.type == IPS_KEY ) + else { - Shell* sh = new Shell(work->use.name.c_str()); - work->use.ips_index = sc->policy_map->add_ips_shell(sh) + 1; + // Normal type binding (if name is given, it will be resolved to an inspector instance + // during configure) + if ( !binding.use.type.empty() ) + { + // Ensure that we can resolve the type to an extant module and that it is bindable + const char *mod_name = binding.use.type.c_str(); + const Module* m = ModuleManager::get_module(mod_name); + if ( !m ) + { + ParseError("Can't bind to unknown type '%s'", mod_name); + return false; + } + else if ( !m->is_bindable() ) + { + ParseError("Type '%s' is not bindable", mod_name); + return false; + } + else if ( m->get_usage() == Module::GLOBAL ) + binding.use.global_type = true; + + if ( binding.use.name.empty() ) + binding.use.name = binding.use.type; + } + else if ( !binding.use.name.empty() ) + { + ParseError("Missing binding type for name '%s'", binding.use.name.c_str()); + return false; + } + + commit_binding(); } - - if ( work->use.name.empty() ) - work->use.name = work->use.type; - - bindings.emplace_back(work); - work = nullptr; } + return true; } void BinderModule::add(const char* svc, const char* type) { - Binding* b = new Binding; - b->when.svc = svc; - b->use.type = type; - b->use.name = type; - bindings.emplace_back(b); + binding.clear(); + binding.when.svc = svc; + binding.when.add_criteria(BindWhen::Criteria::BWC_SVC); + binding.use.type = type; + binding.use.name = type; + commit_binding(); } void BinderModule::add(unsigned proto, const char* type) { - Binding* b = new Binding; - b->when.protos = proto; - b->use.type = type; - b->use.name = type; - bindings.emplace_back(b); + binding.clear(); + binding.when.protos = proto; + binding.when.add_criteria(BindWhen::Criteria::BWC_PROTO); + binding.use.type = type; + binding.use.name = type; + commit_binding(); +} + +void BinderModule::commit_binding() +{ + bindings.emplace_back(binding); + binding.when.src_nets = nullptr; + binding.when.dst_nets = nullptr; +} + +void BinderModule::commit_policy_binding() +{ + policy_bindings.emplace_back(binding); + binding.when.src_nets = nullptr; + binding.when.dst_nets = nullptr; +} + +vector& BinderModule::get_bindings() +{ + return bindings; // move semantics } -vector& BinderModule::get_data() +vector& BinderModule::get_policy_bindings() { - return bindings; // move semantics + return policy_bindings; // move semantics } const PegInfo* BinderModule::get_pegs() const diff --git a/src/network_inspectors/binder/bind_module.h b/src/network_inspectors/binder/bind_module.h index 0c2cac964..5e411ceb4 100644 --- a/src/network_inspectors/binder/bind_module.h +++ b/src/network_inspectors/binder/bind_module.h @@ -31,7 +31,11 @@ struct BindStats { - PegCount packets; + PegCount new_flows; + PegCount service_changes; + PegCount assistant_inspectors; + PegCount new_standby_flows; + PegCount no_match; PegCount verdicts[BindUse::BA_MAX]; }; @@ -56,21 +60,22 @@ public: PegCount* get_counts() const override; snort::ProfileStats* get_profile() const override; - std::vector& get_data(); + std::vector& get_bindings(); + std::vector& get_policy_bindings(); Usage get_usage() const override { return INSPECT; } private: - Binding* work; - std::vector bindings; - bool unsplit_nets; - bool unsplit_ports; - bool unsplit_zones; - unsigned use_name_count; - unsigned use_type_count; - - void add_file(const char* name, const char* type); + Binding binding; + std::vector bindings; + std::vector policy_bindings; + std::string policy_filename; + std::string policy_type; + + bool add_policy_file(const char* name, const char* type); + void commit_binding(); + void commit_policy_binding(); }; #endif diff --git a/src/network_inspectors/binder/binder.cc b/src/network_inspectors/binder/binder.cc index 7db2a0dab..255797275 100644 --- a/src/network_inspectors/binder/binder.cc +++ b/src/network_inspectors/binder/binder.cc @@ -21,26 +21,15 @@ #include "config.h" #endif -#include -#include - #include "detection/detection_engine.h" #include "flow/flow.h" -#include "flow/flow_key.h" -#include "framework/data_bus.h" #include "log/messages.h" -#include "main/snort_config.h" #include "managers/inspector_manager.h" -#include "managers/module_manager.h" #include "profiler/profiler.h" -#include "protocols/packet.h" -#include "protocols/tcp.h" -#include "protocols/udp.h" #include "pub_sub/assistant_gadget_event.h" #include "stream/stream.h" #include "stream/stream_splitter.h" #include "target_based/host_attributes.h" -#include "target_based/snort_protocols.h" #include "bind_module.h" #include "binding.h" @@ -50,412 +39,27 @@ using namespace std; THREAD_LOCAL ProfileStats bindPerfStats; -// FIXIT-P these lookups should be optimized when the dust settles -#define INS_IP "stream_ip" -#define INS_ICMP "stream_icmp" -#define INS_TCP "stream_tcp" -#define INS_UDP "stream_udp" -#define INS_USER "stream_user" -#define INS_FILE "stream_file" - -//------------------------------------------------------------------------- -// binding -//------------------------------------------------------------------------- - -Binding::Binding() -{ - when.split_nets = false; - when.src_nets = nullptr; - when.dst_nets = nullptr; - - when.split_ports = false; - when.src_ports.set(); - when.dst_ports.set(); - - when.split_zones = false; - when.src_zones.set(); - when.dst_zones.set(); - - when.protos = PROTO_BIT__ANY_TYPE; - when.vlans.set(); - when.ifaces.reset(); - - when.ips_id = 0; - when.ips_id_user = 0; - when.role = BindWhen::BR_EITHER; - - use.inspection_index = 0; - use.ips_index = 0; - use.action = BindUse::BA_INSPECT; - - use.what = BindUse::BW_NONE; - use.object = nullptr; -} - -Binding::~Binding() -{ - if (when.src_nets) - sfvar_free(when.src_nets); - - if (when.dst_nets) - sfvar_free(when.dst_nets); -} - -inline bool Binding::check_ips_policy(const Flow* flow) const -{ - if (!when.ips_id) - return true; - - if (when.ips_id == flow->ips_policy_id) - return true; - - return false; -} - -inline bool Binding::check_addr(const Flow* flow) const -{ - if (when.split_nets) - return true; - - if (!when.src_nets) - return true; - - switch (when.role) - { - case BindWhen::BR_SERVER: - if (sfvar_ip_in(when.src_nets, &flow->server_ip)) - return true; - break; - - case BindWhen::BR_CLIENT: - if (sfvar_ip_in(when.src_nets, &flow->client_ip)) - return true; - break; - - case BindWhen::BR_EITHER: - if (sfvar_ip_in(when.src_nets, &flow->client_ip) or - sfvar_ip_in(when.src_nets, &flow->server_ip)) - return true; - break; - - default: - break; - } - return false; -} - -inline bool Binding::check_proto(const Flow* flow) const -{ - if (when.protos & BIT((unsigned)flow->pkt_type)) - return true; - - return false; -} - -inline bool Binding::check_iface(const Packet* p) const -{ - if (!p or when.ifaces.none()) - return true; - - auto in = p->pkth->ingress_index; - auto out = p->pkth->egress_index; - - if (in > 0 and when.ifaces.test(out)) - return true; - - if (out > 0 and when.ifaces.test(in)) - return true; - - return false; -} - -inline bool Binding::check_vlan(const Flow* flow) const -{ - unsigned v = flow->key->vlan_tag; - return when.vlans.test(v); -} - -inline bool Binding::check_port(const Flow* flow) const -{ - if (when.split_ports) - return true; - - switch (when.role) - { - case BindWhen::BR_SERVER: - return when.src_ports.test(flow->server_port); - case BindWhen::BR_CLIENT: - return when.src_ports.test(flow->client_port); - case BindWhen::BR_EITHER: - return (when.src_ports.test(flow->client_port) or - when.src_ports.test(flow->server_port)); - default: - break; - } - return false; -} - -inline bool Binding::check_service(const Flow* flow) const -{ - if (!flow->service) - return when.svc.empty(); - - if (when.svc == flow->service) - return true; - - return false; -} - -inline bool Binding::check_service(const char* service) const -{ - if (when.svc == service) - return true; - - return false; -} - -// we want to correlate src_zone to src_nets and src_ports, and dst_zone to dst_nets and -// dst_ports. it doesn't matter if the packet is actually moving in the opposite direction as -// binder is only evaluated once per flow and we need to capture the correct binding from -// either side of the conversation -template -static Binding::DirResult directional_match(const When& when_src, const When& when_dst, - const Traffic& traffic_src, const Traffic& traffic_dst, - const Binding::DirResult dr, const Compare& compare) -{ - bool src_in_src = false; - bool src_in_dst = false; - bool dst_in_src = false; - bool dst_in_dst = false; - bool forward_match = false; - bool reverse_match = false; - - switch (dr) - { - case Binding::DR_ANY_MATCH: - src_in_src = compare(when_src, traffic_src); - src_in_dst = compare(when_dst, traffic_src); - dst_in_src = compare(when_src, traffic_dst); - dst_in_dst = compare(when_dst, traffic_dst); - - forward_match = src_in_src and dst_in_dst; - reverse_match = dst_in_src and src_in_dst; - - if (forward_match and reverse_match) - return dr; - - if (forward_match) - return Binding::DR_FORWARD; - - if (reverse_match) - return Binding::DR_REVERSE; - - return Binding::DR_NO_MATCH; - - case Binding::DR_FORWARD: - src_in_src = compare(when_src, traffic_src); - dst_in_dst = compare(when_dst, traffic_dst); - return src_in_src and dst_in_dst ? dr : Binding::DR_NO_MATCH; - - case Binding::DR_REVERSE: - src_in_dst = compare(when_dst, traffic_src); - dst_in_src = compare(when_src, traffic_dst); - return src_in_dst and dst_in_src ? dr : Binding::DR_NO_MATCH; - - default: - break; - } - - return Binding::DR_NO_MATCH; -} - -inline Binding::DirResult Binding::check_split_addr( - const Flow* flow, const Packet* p, const Binding::DirResult dr) const -{ - if (!when.split_nets) - return dr; - - if (!when.src_nets && !when.dst_nets) - return dr; - - const SfIp* src_ip; - const SfIp* dst_ip; - - if (p && p->ptrs.ip_api.is_ip()) - { - src_ip = p->ptrs.ip_api.get_src(); - dst_ip = p->ptrs.ip_api.get_dst(); - } - else - { - src_ip = &flow->client_ip; - dst_ip = &flow->server_ip; - } - - return directional_match(when.src_nets, when.dst_nets, src_ip, dst_ip, dr, - [](sfip_var_t* when_val, const SfIp* traffic_val) - { return when_val ? sfvar_ip_in(when_val, traffic_val) : true; }); -} - -inline Binding::DirResult Binding::check_split_port( - const Flow* flow, const Packet* p, const Binding::DirResult dr) const -{ - if (!when.split_ports) - return dr; - - uint16_t src_port; - uint16_t dst_port; - - if (!p) - { - src_port = flow->client_port; - dst_port = flow->server_port; - } - else if (p->is_tcp() or p->is_udp()) - { - src_port = p->ptrs.sp; - dst_port = p->ptrs.dp; - } - else - return dr; - - return directional_match(when.src_ports, when.dst_ports, src_port, dst_port, dr, - [](const PortBitSet& when_val, uint16_t traffic_val) - { return when_val.test(traffic_val); }); -} - -inline bool Binding::check_zone(const Packet* p) const -{ - if (when.split_zones or !p) - return true; - - if (p->pkth->egress_group == DAQ_PKTHDR_UNKNOWN or - p->pkth->ingress_group == DAQ_PKTHDR_UNKNOWN) - return true; - - assert(((unsigned)p->pkth->ingress_group) < when.src_zones.size()); - assert(((unsigned)p->pkth->egress_group) < when.dst_zones.size()); - - if (when.src_zones.test((unsigned)p->pkth->ingress_group) or - when.dst_zones.test((unsigned)p->pkth->egress_group)) - return true; - return false; -} - -inline Binding::DirResult Binding::check_split_zone(const Packet* p, const Binding::DirResult dr) const -{ - if (!when.split_zones) - return dr; - - int src_zone; - int dst_zone; - - if (p) - { - src_zone = p->pkth->ingress_group; - dst_zone = p->pkth->egress_group; - } - else - return dr; - - return directional_match(when.src_zones, when.dst_zones, src_zone, dst_zone, dr, - [](const ZoneBitSet& when_val, int traffic_val) - { return traffic_val == DAQ_PKTHDR_UNKNOWN ? true : when_val.test(traffic_val); }); -} - -bool Binding::check_all(const Flow* flow, Packet* p, const char* service) const -{ - Binding::DirResult dir = Binding::DR_ANY_MATCH; - - if (!check_ips_policy(flow)) - return false; - - if (!check_iface(p)) - return false; - - if (!check_vlan(flow)) - return false; - - // FIXIT-M need to check role and addr/ports relative to it - if (!check_addr(flow)) - return false; - - dir = check_split_addr(flow, p, dir); - if (dir == Binding::DR_NO_MATCH) - return false; - - if (!check_proto(flow)) - return false; - - if (!check_port(flow)) - return false; - - dir = check_split_port(flow, p, dir); - if (dir == Binding::DR_NO_MATCH) - return false; - - dir = check_split_zone(p, dir); - if (dir == Binding::DR_NO_MATCH) - return false; - - if (service) - { - if (!check_service(service)) - return false; - } - else if (!check_service(flow)) - return false; - - if (!check_zone(p)) - return false; - - return true; -} - //------------------------------------------------------------------------- // helpers //------------------------------------------------------------------------- -static void set_session(Flow* flow, const char* key) -{ - Inspector* pin = InspectorManager::get_inspector(key); - - if (pin) - { - // FIXIT-M need to set ssn client and server independently - flow->set_client(pin); - flow->set_server(pin); - flow->clouseau = nullptr; - } -} - -static void set_session(Flow* flow) -{ - flow->ssn_client = nullptr; - flow->ssn_server = nullptr; - flow->clouseau = nullptr; -} - -static void set_service(Flow* flow, const HostAttributesEntry host) -{ - Stream::set_snort_protocol_id(flow, host, FROM_SERVER); -} - -static Inspector* get_gadget(Flow* flow) +static Inspector* get_gadget(const Flow& flow) { - if (flow->ssn_state.snort_protocol_id == UNKNOWN_PROTOCOL_ID) + if (flow.ssn_state.snort_protocol_id == UNKNOWN_PROTOCOL_ID) return nullptr; - const char* s = SnortConfig::get_conf()->proto_ref->get_name(flow->ssn_state.snort_protocol_id); + const SnortConfig* sc = SnortConfig::get_conf(); + const char* s = sc->proto_ref->get_name(flow.ssn_state.snort_protocol_id); return InspectorManager::get_inspector_by_service(s); } -static Inspector* get_gadget_by_id(const char* service) +static Inspector* get_gadget_by_service(const char* service) { const SnortConfig* sc = SnortConfig::get_conf(); const SnortProtocolId id = sc->proto_ref->find(service); const char* s = sc->proto_ref->get_name(id); + return InspectorManager::get_inspector_by_service(s); } @@ -510,16 +114,37 @@ static std::string to_string(const std::bitset& bitset) return str; } +template +static std::string to_string(const std::unordered_set& set) +{ + if (set.empty()) + return ""; + + std::vector elements; + elements.insert(elements.end(), set.begin(), set.end()); + std::sort(elements.begin(), elements.end()); + + std::stringstream ss; + for (auto e : elements) + ss << e << " "; + + auto str = ss.str(); + if (!str.empty()) + str.pop_back(); + + return str; +} + static std::string to_string(const BindWhen::Role& role) { switch(role) { - case BindWhen::BR_CLIENT: - return "client"; - case BindWhen::BR_SERVER: - return "server"; - default: - return ""; + case BindWhen::BR_CLIENT: + return "client"; + case BindWhen::BR_SERVER: + return "server"; + default: + return ""; } } @@ -527,20 +152,20 @@ static std::string proto_to_string(unsigned proto) { switch(proto) { - case PROTO_BIT__IP: - return "ip"; - case PROTO_BIT__ICMP: - return "icmp"; - case PROTO_BIT__TCP: - return "tcp"; - case PROTO_BIT__UDP: - return "udp"; - case PROTO_BIT__PDU: - return "user"; - case PROTO_BIT__FILE: - return "file"; - default: - return ""; + case PROTO_BIT__IP: + return "ip"; + case PROTO_BIT__ICMP: + return "icmp"; + case PROTO_BIT__TCP: + return "tcp"; + case PROTO_BIT__UDP: + return "udp"; + case PROTO_BIT__PDU: + return "user"; + case PROTO_BIT__FILE: + return "file"; + default: + return ""; } } @@ -548,14 +173,14 @@ static std::string to_string(const BindUse::Action& action) { switch(action) { - case BindUse::BA_RESET: - return "reset"; - case BindUse::BA_BLOCK: - return "block"; - case BindUse::BA_ALLOW: - return "allow"; - default: - return ""; + case BindUse::BA_RESET: + return "reset"; + case BindUse::BA_BLOCK: + return "block"; + case BindUse::BA_ALLOW: + return "allow"; + default: + return ""; } } @@ -565,51 +190,93 @@ static std::string to_string(const BindWhen& bw) when += "{"; - if (bw.ips_id_user) - when += " ips_policy_id = " + std::to_string(bw.ips_id_user) + ","; - - if (!bw.svc.empty()) - when += " service = " + bw.svc + ","; - auto role = to_string(bw.role); if (!role.empty()) when += " role = " + role + ","; - auto proto = proto_to_string(bw.protos); - if (!proto.empty()) - when += " proto = " + proto + ","; + if (bw.has_criteria(BindWhen::Criteria::BWC_IPS_ID)) + when += " ips_policy_id = " + std::to_string(bw.ips_id_user) + ","; - auto src_nets = to_string(bw.src_nets); - auto dst_nets = to_string(bw.dst_nets); + if (bw.has_criteria(BindWhen::Criteria::BWC_VLANS)) + { + auto vlans = to_string<4096>(bw.vlans); + when += " vlans = " + vlans + ","; + } - if (!src_nets.empty()) - when += (bw.split_nets ? " src_nets = " : " nets = ") + src_nets + ","; - if (bw.split_nets and !dst_nets.empty()) - when += " dst_nets = " + dst_nets + ","; + if (bw.has_criteria(BindWhen::Criteria::BWC_SVC)) + when += " service = " + bw.svc + ","; - auto src_ports = to_string<65536>(bw.src_ports); - auto dst_ports = to_string<65536>(bw.dst_ports); + if (bw.has_criteria(BindWhen::Criteria::BWC_SPLIT_NETS)) + { + auto src_nets = to_string(bw.src_nets); + auto dst_nets = to_string(bw.dst_nets); + if (!src_nets.empty()) + when += " src_nets = " + src_nets + ","; + if (!dst_nets.empty()) + when += " dst_nets = " + dst_nets + ","; + } + else if (bw.has_criteria(BindWhen::Criteria::BWC_NETS)) + { + auto nets = to_string(bw.src_nets); + when += " nets = " + nets + ","; + } - if (!src_ports.empty()) - when += (bw.split_ports ? " src_ports = " : " ports = ") + src_ports + ","; - if (bw.split_ports and !dst_ports.empty()) - when += " dst_ports = " + dst_ports + ","; + if (bw.has_criteria(BindWhen::Criteria::BWC_PROTO)) + { + auto proto = proto_to_string(bw.protos); + when += " proto = " + proto + ","; + } - auto src_zones = to_string<64>(bw.src_zones); - auto dst_zones = to_string<64>(bw.dst_zones); + if (bw.has_criteria(BindWhen::Criteria::BWC_SPLIT_PORTS)) + { + auto src_ports = to_string<65536>(bw.src_ports); + auto dst_ports = to_string<65536>(bw.dst_ports); + if (!src_ports.empty()) + when += " src_ports = " + src_ports + ","; + if (!dst_ports.empty()) + when += " dst_ports = " + dst_ports + ","; + } + else if (bw.has_criteria(BindWhen::Criteria::BWC_PORTS)) + { + auto ports = to_string<65536>(bw.src_ports); + when += " ports = " + ports + ","; + } - if (!src_zones.empty()) - when += (bw.split_zones ? " src_zones = " : " zones = ") + src_zones + ","; - if (bw.split_zones and !dst_zones.empty()) - when += " dst_zones = " + dst_zones + ","; + if (bw.has_criteria(BindWhen::Criteria::BWC_SPLIT_GROUPS)) + { + auto src_groups = to_string(bw.src_groups); + auto dst_groups = to_string(bw.dst_groups); + if (!src_groups.empty()) + when += " src_groups = " + src_groups + ","; + if (!dst_groups.empty()) + when += " dst_groups = " + dst_groups + ","; + } + else if (bw.has_criteria(BindWhen::Criteria::BWC_GROUPS)) + { + auto groups = to_string(bw.src_groups); + when += " groups = " + groups + ","; + } - auto ifaces = to_string<256>(bw.ifaces); - if (!ifaces.empty()) - when += " ifaces = " + ifaces + ","; + if (bw.has_criteria(BindWhen::Criteria::BWC_SPLIT_INTFS)) + { + auto src_intfs = to_string(bw.src_intfs); + auto dst_intfs = to_string(bw.dst_intfs); + if (!src_intfs.empty()) + when += " src_intfs = " + src_intfs + ","; + if (!dst_intfs.empty()) + when += " dst_intfs = " + dst_intfs + ","; + } + else if (bw.has_criteria(BindWhen::Criteria::BWC_INTFS)) + { + auto intfs = to_string(bw.src_intfs); + when += " intfs = " + intfs + ","; + } - auto vlans = to_string<4096>(bw.vlans); - if (!vlans.empty()) - when += " vlans = " + vlans + ","; + if (bw.has_criteria(BindWhen::Criteria::BWC_ADDR_SPACES)) + { + auto addr_spaces = to_string(bw.addr_spaces); + when += " addr_spaces = " + addr_spaces + ","; + } if (when.length() > 1) when.pop_back(); @@ -652,170 +319,138 @@ static std::string to_string(const BindUse& bu) struct Stuff { - BindUse::Action action; - - Inspector* client; - Inspector* server; - Inspector* wizard; - Inspector* gadget; - Inspector* data; + BindUse::Action action = BindUse::BA_INSPECT; - Stuff() - { - action = BindUse::BA_INSPECT; - client = server = nullptr; - wizard = gadget = nullptr; - data = nullptr; - } + Inspector* client = nullptr; + Inspector* server = nullptr; + Inspector* wizard = nullptr; + Inspector* gadget = nullptr; + Inspector* data = nullptr; - bool update(Binding*); + bool update(const Binding&); - bool apply_action(Flow*); - void apply_session(Flow*, const HostAttributesEntry); - void apply_service(Flow*, const HostAttributesEntry); - void apply_assistant(Flow*, const char*); + void apply_action(Flow&); + void apply_session(Flow&, const HostAttributesEntry); + void apply_service(Flow&, const HostAttributesEntry); + void apply_assistant(Flow&, const char*); }; -bool Stuff::update(Binding* pb) +bool Stuff::update(const Binding& pb) { - if (pb->use.action != BindUse::BA_INSPECT) + if (pb.use.action != BindUse::BA_INSPECT) { - action = pb->use.action; + action = pb.use.action; return true; } - switch (pb->use.what) + switch (pb.use.what) { - case BindUse::BW_NONE: - break; - case BindUse::BW_PASSIVE: - data = (Inspector*)pb->use.object; - break; - case BindUse::BW_CLIENT: - client = (Inspector*)pb->use.object; - break; - case BindUse::BW_SERVER: - server = (Inspector*)pb->use.object; - break; - case BindUse::BW_STREAM: - client = server = (Inspector*)pb->use.object; - break; - case BindUse::BW_WIZARD: - wizard = (Inspector*)pb->use.object; - return true; - case BindUse::BW_GADGET: - gadget = (Inspector*)pb->use.object; - return true; - default: - break; + case BindUse::BW_NONE: + break; + case BindUse::BW_PASSIVE: + data = pb.use.inspector; + break; + case BindUse::BW_CLIENT: + client = pb.use.inspector; + break; + case BindUse::BW_SERVER: + server = pb.use.inspector; + break; + case BindUse::BW_STREAM: + client = server = pb.use.inspector; + break; + case BindUse::BW_WIZARD: + wizard = pb.use.inspector; + return true; + case BindUse::BW_GADGET: + gadget = pb.use.inspector; + return true; + default: + break; } return false; } -bool Stuff::apply_action(Flow* flow) +void Stuff::apply_action(Flow& flow) { switch (action) { - case BindUse::BA_RESET: - flow->set_state(Flow::FlowState::RESET); - return false; - - case BindUse::BA_BLOCK: - flow->set_state(Flow::FlowState::BLOCK); - return false; - - case BindUse::BA_ALLOW: - flow->set_state(Flow::FlowState::ALLOW); - return false; - - default: - break; + case BindUse::BA_RESET: + flow.set_state(Flow::FlowState::RESET); + break; + case BindUse::BA_BLOCK: + flow.set_state(Flow::FlowState::BLOCK); + break; + case BindUse::BA_ALLOW: + flow.set_state(Flow::FlowState::ALLOW); + break; + case BindUse::BA_INSPECT: + flow.set_state(Flow::FlowState::INSPECT); + break; + default: + break; } - flow->set_state(Flow::FlowState::INSPECT); - return true; } -void Stuff::apply_session(Flow* flow, const HostAttributesEntry host) +void Stuff::apply_session(Flow& flow, const HostAttributesEntry host) { - if (server) - { - flow->set_server(server); - - if (client) - flow->set_client(client); - else - flow->set_client(server); + if (client) + flow.set_client(client); + else if (flow.ssn_client) + flow.clear_client(); - return; - } + if (server) + flow.set_server(server); + else if (flow.ssn_server) + flow.clear_server(); - switch (flow->pkt_type) + switch (flow.pkt_type) { - case PktType::IP: - set_session(flow, INS_IP); - flow->ssn_policy = host ? host->get_frag_policy() : 0; - break; - - case PktType::ICMP: - set_session(flow, INS_ICMP); - break; - - case PktType::TCP: - set_session(flow, INS_TCP); - flow->ssn_policy = host ? host->get_stream_policy() : 0; - break; - - case PktType::UDP: - set_session(flow, INS_UDP); - break; - - case PktType::PDU: - set_session(flow, INS_USER); - break; - - case PktType::FILE: - set_session(flow, INS_FILE); - break; - - default: - set_session(flow); + case PktType::IP: + flow.ssn_policy = host ? host->get_frag_policy() : 0; + break; + case PktType::TCP: + flow.ssn_policy = host ? host->get_stream_policy() : 0; + break; + default: + break; } } -void Stuff::apply_service(Flow* flow, const HostAttributesEntry host) +void Stuff::apply_service(Flow& flow, const HostAttributesEntry host) { if (data) - flow->set_data(data); + flow.set_data(data); if (host) - set_service(flow, host); + Stream::set_snort_protocol_id(&flow, host, FROM_SERVER); if (!gadget) gadget = get_gadget(flow); if (gadget) { - if (gadget != flow->gadget) + if (gadget != flow.gadget) { - flow->set_gadget(gadget); + flow.set_gadget(gadget); - if (flow->ssn_state.snort_protocol_id == UNKNOWN_PROTOCOL_ID) - flow->ssn_state.snort_protocol_id = gadget->get_service(); + if (flow.ssn_state.snort_protocol_id == UNKNOWN_PROTOCOL_ID) + flow.ssn_state.snort_protocol_id = gadget->get_service(); DataBus::publish(SERVICE_INSPECTOR_CHANGE_EVENT, DetectionEngine::get_current_packet()); } } else if (wizard) - flow->set_clouseau(wizard); + flow.set_clouseau(wizard); } -void Stuff::apply_assistant(Flow* flow, const char* service) +void Stuff::apply_assistant(Flow& flow, const char* service) { if (!gadget) - gadget = get_gadget_by_id(service); + gadget = get_gadget_by_service(service); if (gadget) - flow->set_assistant_gadget(gadget); + flow.set_assistant_gadget(gadget); } //------------------------------------------------------------------------- @@ -825,7 +460,7 @@ void Stuff::apply_assistant(Flow* flow, const char* service) class Binder : public Inspector { public: - Binder(vector&); + Binder(vector&, vector&); ~Binder() override; void remove_inspector_binding(SnortConfig*, const char*) override; @@ -835,23 +470,21 @@ public: void eval(Packet*) override { } - void add(Binding* b) - { bindings.emplace_back(b); } - - void handle_flow_setup(Packet*); - void handle_flow_service_change(Flow*); - void handle_new_standby_flow(Flow*); - void handle_assistant_gadget(const char* service, Packet*); + void handle_flow_setup(Flow&, bool standby = false); + void handle_flow_service_change(Flow&); + void handle_assistant_gadget(const char* service, Flow&); private: - void set_binding(SnortConfig*, Binding*); - void get_bindings(Flow*, Stuff&, Packet* = nullptr, const char* = nullptr); // may be null when dealing with HA flows - void apply(Flow*, Stuff&); - void apply_assistant(Flow*, Stuff&, const char*); - Inspector* find_gadget(Flow*); + void get_policy_bindings(Flow&, const char* service); + void get_bindings(Flow&, Stuff&, const char* service = nullptr); + void apply(Flow&, Stuff&); + void apply_assistant(Flow&, Stuff&, const char*); + Inspector* find_gadget(Flow&); private: - vector bindings; + vector bindings; + vector policy_bindings; + Inspector* default_ssn_inspectors[to_utype(PktType::MAX)]{}; }; class FlowStateSetupHandler : public DataHandler @@ -859,11 +492,11 @@ class FlowStateSetupHandler : public DataHandler public: FlowStateSetupHandler() : DataHandler(BIND_NAME) { } - void handle(DataEvent& event, Flow* flow) override + void handle(DataEvent&, Flow* flow) override { Binder* binder = InspectorManager::get_binder(); if (binder && flow) - binder->handle_flow_setup(const_cast(event.get_packet())); + binder->handle_flow_setup(*flow); } }; @@ -877,7 +510,7 @@ public: { Binder* binder = InspectorManager::get_binder(); if (binder && flow) - binder->handle_flow_service_change(flow); + binder->handle_flow_service_change(*flow); } }; @@ -890,7 +523,7 @@ public: { Binder* binder = InspectorManager::get_binder(); if (binder && flow) - binder->handle_new_standby_flow(flow); + binder->handle_flow_setup(*flow, true); } }; @@ -899,48 +532,55 @@ class AssistantGadgetHandler : public DataHandler public: AssistantGadgetHandler() : DataHandler(BIND_NAME) { } - void handle(DataEvent& event, Flow*) override + void handle(DataEvent& event, Flow* flow) override { Binder* binder = InspectorManager::get_binder(); AssistantGadgetEvent* assistant_event = (AssistantGadgetEvent*)&event; - if (binder) - binder->handle_assistant_gadget(assistant_event->get_service(), - assistant_event->get_packet()); + if (binder && flow) + binder->handle_assistant_gadget(assistant_event->get_service(), *flow); } }; -Binder::Binder(vector& v) +Binder::Binder(vector& bv, vector& pbv) { - bindings = std::move(v); + bindings = std::move(bv); + policy_bindings = std::move(pbv); } Binder::~Binder() { - for (auto* p : bindings) - delete p; + for (Binding& b : bindings) + b.clear(); + + for (Binding& b : policy_bindings) + b.clear(); } bool Binder::configure(SnortConfig* sc) { - unsigned sz = bindings.size(); + for (Binding& b : bindings) + b.configure(sc); - for (unsigned i = 0; i < sz; i++) - { - Binding* pb = bindings[i]; + for (Binding& b : policy_bindings) + b.configure(sc); - // Update with actual policy indices instead of user provided names - if (pb->when.ips_id_user) + // Grab default session inspectors if they exist for this policy + for (int proto = to_utype(PktType::NONE); proto < to_utype(PktType::MAX); proto++) + { + const char* name; + switch (static_cast(proto)) { - IpsPolicy* p = sc->policy_map->get_user_ips(pb->when.ips_id_user); - if (p) - pb->when.ips_id = p->policy_id; - else - ParseError("can't bind. ips_policy_id %u does not exist", pb->when.ips_id); + case PktType::IP: name = "stream_ip"; break; + case PktType::TCP: name = "stream_tcp"; break; + case PktType::UDP: name = "stream_udp"; break; + case PktType::ICMP: name = "stream_icmp"; break; + case PktType::PDU: name = "stream_user"; break; + case PktType::FILE: name = "stream_file"; break; + default: name = nullptr; break; } - - if (!pb->use.ips_index and !pb->use.inspection_index) - set_binding(sc, pb); + if (name) + default_ssn_inspectors[proto] = InspectorManager::get_inspector(name); } DataBus::subscribe(FLOW_STATE_SETUP_EVENT, new FlowStateSetupHandler()); @@ -953,17 +593,26 @@ bool Binder::configure(SnortConfig* sc) void Binder::show(const SnortConfig*) const { - std::once_flag once; + std::once_flag b_once; - if (!bindings.size()) - return; + for (const Binding& b : bindings) + { + std::call_once(b_once, []{ ConfigLogger::log_option("bindings"); }); - for (const auto& b : bindings) + auto bind_when = "{ when = " + to_string(b.when) + ","; + auto bind_use = "use = " + to_string(b.use) + " }"; + ConfigLogger::log_list("", bind_when.c_str(), " "); + ConfigLogger::log_list("", bind_use.c_str(), " ", true); + } + + std::once_flag pb_once; + + for (const Binding& b : policy_bindings) { - std::call_once(once, []{ ConfigLogger::log_option("bindings"); }); + std::call_once(pb_once, []{ ConfigLogger::log_option("policy_bindings"); }); - auto bind_when = "{ when = " + to_string(b->when) + ","; - auto bind_use = "use = " + to_string(b->use) + " }"; + auto bind_when = "{ when = " + to_string(b.when) + ","; + auto bind_use = "use = " + to_string(b.use) + " }"; ConfigLogger::log_list("", bind_when.c_str(), " "); ConfigLogger::log_list("", bind_use.c_str(), " ", true); } @@ -971,265 +620,207 @@ void Binder::show(const SnortConfig*) const void Binder::remove_inspector_binding(SnortConfig*, const char* name) { - vector::iterator it; - for (it = bindings.begin(); it != bindings.end(); ++it) + for (auto it = bindings.begin(); it != bindings.end(); ++it) { const char* key; - Binding *pb = *it; - if (pb->use.svc.empty()) - key = pb->use.name.c_str(); + const Binding &b = *it; + if (b.use.svc.empty()) + key = b.use.name.c_str(); else - key = pb->use.svc.c_str(); + key = b.use.svc.c_str(); if (!strcmp(key, name)) { bindings.erase(it); - delete pb; return; } } } -void Binder::handle_flow_setup(Packet* p) +void Binder::handle_flow_setup(Flow& flow, bool standby) { Profile profile(bindPerfStats); - Stuff stuff; - Flow* flow = p->flow; - get_bindings(flow, stuff, p); + Stuff stuff; + get_bindings(flow, stuff); apply(flow, stuff); - ++bstats.verdicts[stuff.action]; - ++bstats.packets; + if (standby) + bstats.new_standby_flows++; + else + bstats.new_flows++; + bstats.verdicts[stuff.action]++; } -void Binder::handle_flow_service_change(Flow* flow) +void Binder::handle_flow_service_change(Flow& flow) { Profile profile(bindPerfStats); - assert(flow); - Inspector* ins = nullptr; - if (flow->service) + if (flow.service) { ins = find_gadget(flow); - if (flow->gadget != ins) + if (flow.gadget != ins) { - if (flow->gadget) - flow->clear_gadget(); + if (flow.gadget) + flow.clear_gadget(); if (ins) { - flow->set_gadget(ins); - flow->ssn_state.snort_protocol_id = ins->get_service(); + flow.set_gadget(ins); + flow.ssn_state.snort_protocol_id = ins->get_service(); DataBus::publish(SERVICE_INSPECTOR_CHANGE_EVENT, DetectionEngine::get_current_packet()); } else - flow->ssn_state.snort_protocol_id = UNKNOWN_PROTOCOL_ID; + flow.ssn_state.snort_protocol_id = UNKNOWN_PROTOCOL_ID; } } else { // reset to wizard when service is not specified - unsigned sz = bindings.size(); - for (unsigned i = 0; i < sz; i++) + for (const Binding& b : bindings) { - Binding* pb = bindings[i]; - if (pb->use.ips_index or pb->use.inspection_index) - continue; - - if (pb->use.what == BindUse::BW_WIZARD) + if (b.use.what == BindUse::BW_WIZARD) { - ins = (Inspector*)pb->use.object; + ins = b.use.inspector; break; } } - if (flow->gadget) - flow->clear_gadget(); - if (flow->clouseau) - flow->clear_clouseau(); + if (flow.gadget) + flow.clear_gadget(); + if (flow.clouseau) + flow.clear_clouseau(); if (ins) - { - flow->set_clouseau(ins); - } - flow->ssn_state.snort_protocol_id = UNKNOWN_PROTOCOL_ID; + flow.set_clouseau(ins); + flow.ssn_state.snort_protocol_id = UNKNOWN_PROTOCOL_ID; } // If there is no inspector bound to this flow after the service change, see if there's at least // an associated protocol ID. - if (!ins && flow->service) - flow->ssn_state.snort_protocol_id = SnortConfig::get_conf()->proto_ref->find(flow->service); - - if (!flow->is_stream()) - return; + if (!ins && flow.service) + flow.ssn_state.snort_protocol_id = SnortConfig::get_conf()->proto_ref->find(flow.service); - if (ins) - { - Stream::set_splitter(flow, true, ins->get_splitter(true)); - Stream::set_splitter(flow, false, ins->get_splitter(false)); - } - else + if (flow.is_stream()) { - Stream::set_splitter(flow, true, new AtomSplitter(true)); - Stream::set_splitter(flow, false, new AtomSplitter(false)); + if (ins) + { + Stream::set_splitter(&flow, true, ins->get_splitter(true)); + Stream::set_splitter(&flow, false, ins->get_splitter(false)); + } + else + { + Stream::set_splitter(&flow, true, new AtomSplitter(true)); + Stream::set_splitter(&flow, false, new AtomSplitter(false)); + } } + + bstats.service_changes++; } -void Binder::handle_new_standby_flow(Flow* flow) +void Binder::handle_assistant_gadget(const char* service, Flow& flow) { Profile profile(bindPerfStats); Stuff stuff; - get_bindings(flow, stuff); - apply(flow, stuff); + get_bindings(flow, stuff, service); + apply_assistant(flow, stuff, service); - ++bstats.verdicts[stuff.action]; + bstats.assistant_inspectors++; } -void Binder::handle_assistant_gadget(const char* service, Packet* p) +void Binder::get_policy_bindings(Flow& flow, const char* service) { - Profile profile(bindPerfStats); - Stuff stuff; - Flow* flow = p->flow; + const SnortConfig* sc = SnortConfig::get_conf(); + unsigned inspection_index = 0; + unsigned ips_index = 0; - get_bindings(flow, stuff, p, service); - apply_assistant(flow, stuff, service); -} + // FIXIT-L This will select the first policy ID of each type that it finds and ignore the rest. + // It gets potentially hairy if people start specifying overlapping policy types in + // overlapping rules. + for (const Binding& b : policy_bindings) + { + // Skip any rules that don't contain an ID for a policy type we haven't set yet. + if ((!b.use.inspection_index || inspection_index) && (!b.use.ips_index || ips_index)) + continue; -//------------------------------------------------------------------------- -// implementation stuff -//------------------------------------------------------------------------- + if (!b.check_all(flow, service)) + continue; -void Binder::set_binding(SnortConfig* sc, Binding* pb) -{ - if (pb->use.action != BindUse::BA_INSPECT) - return; + if (b.use.inspection_index && !inspection_index) + inspection_index = b.use.inspection_index; - const char *mod_name = pb->use.type.c_str(); - Module* m = ModuleManager::get_module(mod_name); + if (b.use.ips_index && !ips_index) + ips_index = b.use.ips_index; + } - if ( m and !m->is_bindable() ) + if (inspection_index) { - ParseError("can't bind %s", mod_name); - return; + set_inspection_policy(sc, inspection_index); + if (!service) + flow.inspection_policy_id = inspection_index; } - bool is_global = m ? m->get_usage() == Module::GLOBAL : false; - - const char* key = pb->use.name.c_str(); - pb->use.object = InspectorManager::get_inspector(key, is_global, sc); - - if (pb->use.object) + if (ips_index) { - switch (((Inspector*)pb->use.object)->get_api()->type) - { - case IT_STREAM: pb->use.what = BindUse::BW_STREAM; break; - case IT_WIZARD: pb->use.what = BindUse::BW_WIZARD; break; - case IT_SERVICE: pb->use.what = BindUse::BW_GADGET; break; - case IT_PASSIVE: pb->use.what = BindUse::BW_PASSIVE; break; - default: break; - } + set_ips_policy(sc, ips_index); + if (!service) + flow.ips_policy_id = ips_index; } - if (!pb->use.object) - pb->use.what = BindUse::BW_NONE; - - if (pb->use.what == BindUse::BW_NONE) - ParseError("can't bind %s", key); } // FIXIT-P this is a simple linear search until functionality is nailed // down. performance should be the focus of the next iteration. -void Binder::get_bindings(Flow* flow, Stuff& stuff, Packet* p, const char* service) +void Binder::get_bindings(Flow& flow, Stuff& stuff, const char* service) { - unsigned sz = bindings.size(); - // Evaluate policy ID bindings first - // FIXIT-P The way these are being used, the policy bindings should be a separate list if not a - // separate table entirely - // FIXIT-L This will select the first policy ID of each type that it finds and ignore the rest. - // It gets potentially hairy if people start specifying overlapping policy types in - // overlapping rules. - bool inspection_set = false, ips_set = false; - const SnortConfig* sc = SnortConfig::get_conf(); - - for (unsigned i = 0; i < sz; i++) - { - Binding* pb = bindings[i]; - - // Skip any rules that don't contain an ID for a policy type we haven't set yet. - if ((!pb->use.inspection_index or inspection_set) and - (!pb->use.ips_index or ips_set)) - continue; - - if (!pb->check_all(flow, p, service)) - continue; - - if (pb->use.inspection_index and !inspection_set) - { - set_inspection_policy(sc, pb->use.inspection_index - 1); - if (!service) - flow->inspection_policy_id = pb->use.inspection_index - 1; - inspection_set = true; - } - - if (pb->use.ips_index and !ips_set) - { - set_ips_policy(sc, pb->use.ips_index - 1); - if (!service) - flow->ips_policy_id = pb->use.ips_index - 1; - ips_set = true; - } - - } - - Binder* sub = InspectorManager::get_binder(); + get_policy_bindings(flow, service); // If policy selection produced a new binder to use, use that instead. + Binder* sub = InspectorManager::get_binder(); if (sub && sub != this) { - sub->get_bindings(flow, stuff, p); + sub->get_bindings(flow, stuff, service); return; } // If we got here, that means that a sub-policy with a binder was not invoked. // Continue using this binder for the rest of processing. - for (unsigned i = 0; i < sz; i++) - { - Binding* pb = bindings[i]; - if (pb->use.ips_index or pb->use.inspection_index) - continue; + // Initialize the session inspector for both client and server to the default for this policy. + stuff.client = stuff.server = default_ssn_inspectors[to_utype(flow.pkt_type)]; - if (!pb->check_all(flow, p, service)) + for (const Binding& b : bindings) + { + if (!b.check_all(flow, service)) continue; - if (stuff.update(pb)) + if (stuff.update(b)) return; } + + bstats.no_match++; } -Inspector* Binder::find_gadget(Flow* flow) +Inspector* Binder::find_gadget(Flow& flow) { Stuff stuff; get_bindings(flow, stuff); return stuff.gadget; } -void Binder::apply(Flow* flow, Stuff& stuff) +void Binder::apply(Flow& flow, Stuff& stuff) { - // setup action - if (!stuff.apply_action(flow)) + stuff.apply_action(flow); + if (flow.flow_state != Flow::FlowState::INSPECT) return; - const HostAttributesEntry host = HostAttributesManager::find_host(flow->server_ip); + const HostAttributesEntry host = HostAttributesManager::find_host(flow.server_ip); - // setup session stuff.apply_session(flow, host); - // setup service stuff.apply_service(flow, host); } -void Binder::apply_assistant(Flow* flow, Stuff& stuff, const char* service) +void Binder::apply_assistant(Flow& flow, Stuff& stuff, const char* service) { stuff.apply_assistant(flow, service); } @@ -1247,8 +838,9 @@ static void mod_dtor(Module* m) static Inspector* bind_ctor(Module* m) { BinderModule* mod = (BinderModule*)m; - vector& pb = mod->get_data(); - return new Binder(pb); + vector& bv = mod->get_bindings(); + vector& pbv = mod->get_policy_bindings(); + return new Binder(bv, pbv); } static void bind_dtor(Inspector* p) diff --git a/src/network_inspectors/binder/binding.cc b/src/network_inspectors/binder/binding.cc new file mode 100644 index 000000000..83dc3bfab --- /dev/null +++ b/src/network_inspectors/binder/binding.cc @@ -0,0 +1,417 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2020-2020 Cisco and/or its affiliates. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License Version 2 as published +// by the Free Software Foundation. You may not use, modify or distribute +// this program under any other version of the GNU General Public License. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +//-------------------------------------------------------------------------- +// binding.cc author Michael Altizer + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "binding.h" + +#include "flow/flow.h" +#include "flow/flow_key.h" +#include "log/messages.h" +#include "main/snort_config.h" +#include "managers/inspector_manager.h" + +using namespace snort; + +Binding::Binding() +{ + when.src_nets = nullptr; + when.dst_nets = nullptr; + clear(); +} + +void Binding::clear() +{ + when.ips_id = 0; + when.ips_id_user = 0; + when.protos = PROTO_BIT__ANY_TYPE; + when.role = BindWhen::BR_EITHER; + when.svc.clear(); + + if (when.src_nets) + { + sfvar_free(when.src_nets); + when.src_nets = nullptr; + } + if (when.dst_nets) + { + sfvar_free(when.dst_nets); + when.dst_nets = nullptr; + } + + when.vlans.reset(); + + when.src_ports.set(); + when.dst_ports.set(); + + when.src_intfs.clear(); + when.dst_intfs.clear(); + + when.src_groups.clear(); + when.dst_groups.clear(); + + when.addr_spaces.clear(); + + when.criteria_flags = 0; + + use.svc.clear(); + use.type.clear(); + use.name.clear(); + + use.action = BindUse::BA_INSPECT; + use.inspection_index = 0; + use.ips_index = 0; + use.what = BindUse::BW_NONE; + use.inspector = nullptr; + use.global_type = false; +} + +void Binding::configure(const SnortConfig* sc) +{ + // Update with actual policy indices instead of user-provided identifiers + if (when.ips_id_user) + { + IpsPolicy* p = sc->policy_map->get_user_ips(when.ips_id_user); + if (p) + when.ips_id = p->policy_id; + else + ParseError("Can't bind for unrecognized ips_policy_id %u", when.ips_id_user); + } + + if (use.ips_index || use.inspection_index) + return; + + if (use.action != BindUse::BA_INSPECT) + return; + + if (!use.name.empty()) + { + const char* name = use.name.c_str(); + Inspector* ins = InspectorManager::get_inspector(name, use.global_type, sc); + if (ins) + { + switch (ins->get_api()->type) + { + case IT_STREAM: + switch (when.role) + { + case BindWhen::BR_CLIENT: use.what = BindUse::BW_CLIENT; break; + case BindWhen::BR_SERVER: use.what = BindUse::BW_SERVER; break; + default: use.what = BindUse::BW_STREAM; break; + } + break; + case IT_WIZARD: use.what = BindUse::BW_WIZARD; break; + case IT_SERVICE: use.what = BindUse::BW_GADGET; break; + case IT_PASSIVE: use.what = BindUse::BW_PASSIVE; break; + default: break; + } + } + if (use.what == BindUse::BW_NONE) + ParseError("Couldn't bind '%s'", name); + else + use.inspector = ins; + } +} + +inline bool Binding::check_ips_policy(const Flow& flow) const +{ + if (!when.has_criteria(BindWhen::Criteria::BWC_IPS_ID)) + return true; + + return when.ips_id == flow.ips_policy_id; +} + +inline bool Binding::check_vlan(const Flow& flow) const +{ + if (!when.has_criteria(BindWhen::Criteria::BWC_VLANS)) + return true; + + return when.vlans.test(flow.key->vlan_tag); +} + +inline bool Binding::check_addr(const Flow& flow) const +{ + if (!when.has_criteria(BindWhen::Criteria::BWC_NETS)) + return true; + + switch (when.role) + { + case BindWhen::BR_SERVER: + if (sfvar_ip_in(when.src_nets, &flow.server_ip)) + return true; + break; + + case BindWhen::BR_CLIENT: + if (sfvar_ip_in(when.src_nets, &flow.client_ip)) + return true; + break; + + case BindWhen::BR_EITHER: + if (sfvar_ip_in(when.src_nets, &flow.client_ip) || + sfvar_ip_in(when.src_nets, &flow.server_ip)) + return true; + break; + + default: + break; + } + return false; +} + +inline bool Binding::check_split_addr(const Flow& flow) const +{ + if (!when.has_criteria(BindWhen::Criteria::BWC_SPLIT_NETS)) + return true; + + if (when.src_nets && !sfvar_ip_in(when.src_nets, &flow.client_ip)) + return false; + + if (when.dst_nets && !sfvar_ip_in(when.dst_nets, &flow.server_ip)) + return false; + + return true; +} + +inline bool Binding::check_proto(const Flow& flow) const +{ + if (!when.has_criteria(BindWhen::Criteria::BWC_PROTO)) + return true; + + unsigned proto_bit = 1 << ((unsigned)flow.pkt_type - 1); + return (when.protos & proto_bit) != 0; +} + +inline bool Binding::check_port(const Flow& flow) const +{ + if (!when.has_criteria(BindWhen::Criteria::BWC_PORTS)) + return true; + + if (flow.pkt_type != PktType::TCP && flow.pkt_type != PktType::UDP) + return false; + + switch (when.role) + { + case BindWhen::BR_SERVER: + if (when.src_ports.test(flow.server_port)) + return true; + break; + + case BindWhen::BR_CLIENT: + if (when.src_ports.test(flow.client_port)) + return true; + break; + + case BindWhen::BR_EITHER: + if (when.src_ports.test(flow.client_port) || + when.src_ports.test(flow.server_port)) + return true; + break; + + default: + break; + } + return false; +} + +inline bool Binding::check_split_port(const Flow& flow) const +{ + if (!when.has_criteria(BindWhen::Criteria::BWC_SPLIT_PORTS)) + return true; + + if (flow.pkt_type != PktType::TCP && flow.pkt_type != PktType::UDP) + return false; + + if (!when.src_ports.test(flow.client_port)) + return false; + + if (!when.dst_ports.test(flow.server_port)) + return false; + + return true; +} + +inline bool Binding::check_intf(const Flow& flow) const +{ + if (!when.has_criteria(BindWhen::Criteria::BWC_INTFS)) + return true; + + switch (when.role) + { + case BindWhen::BR_SERVER: + if (when.src_intfs.count(flow.server_intf)) + return true; + break; + + case BindWhen::BR_CLIENT: + if (when.src_intfs.count(flow.client_intf)) + return true; + break; + + case BindWhen::BR_EITHER: + if (when.src_intfs.count(flow.client_intf) || + when.src_intfs.count(flow.server_intf)) + return true; + break; + + default: + break; + } + + return false; +} + +inline bool Binding::check_split_intf(const Flow& flow) const +{ + if (!when.has_criteria(BindWhen::Criteria::BWC_SPLIT_INTFS)) + return true; + + if (!when.src_intfs.empty() && !when.src_intfs.count(flow.client_intf)) + return false; + + if (!when.dst_intfs.empty() && !when.dst_intfs.count(flow.server_intf)) + return false; + + return true; +} + +inline bool Binding::check_group(const Flow& flow) const +{ + if (!when.has_criteria(BindWhen::Criteria::BWC_GROUPS)) + return true; + + switch (when.role) + { + case BindWhen::BR_SERVER: + if (when.src_groups.count(flow.server_group)) + return true; + break; + + case BindWhen::BR_CLIENT: + if (when.src_groups.count(flow.client_group)) + return true; + break; + + case BindWhen::BR_EITHER: + if (when.src_groups.count(flow.client_group) || + when.src_groups.count(flow.server_group)) + return true; + break; + + default: + break; + } + + return false; +} + +inline bool Binding::check_split_group(const Flow& flow) const +{ + if (!when.has_criteria(BindWhen::Criteria::BWC_SPLIT_GROUPS)) + return true; + + if (!when.src_groups.empty() && !when.src_groups.count(flow.client_group)) + return false; + + if (!when.dst_groups.empty() && !when.dst_groups.count(flow.server_group)) + return false; + + return true; +} + +inline bool Binding::check_address_space(const Flow& flow) const +{ + if (!when.has_criteria(BindWhen::Criteria::BWC_ADDR_SPACES)) + return true; + + return when.addr_spaces.count(flow.key->addressSpaceId) != 0; +} + +inline bool Binding::check_service(const Flow& flow) const +{ + if (!when.has_criteria(BindWhen::Criteria::BWC_SVC)) + return true; + + if (!flow.service) + return false; + + return when.svc == flow.service; +} + +inline bool Binding::check_service(const char* service) const +{ + // Special case for explicit service lookups: Service criteria must be + // specified (and match) to succeed. + if (!when.has_criteria(BindWhen::Criteria::BWC_SVC)) + return false; + + return when.svc == service; +} + +bool Binding::check_all(const Flow& flow, const char* service) const +{ + // Do the service check first to optimize service change re-evaluations + if (service) + { + if (!check_service(service)) + return false; + } + else if (!check_service(flow)) + return false; + + if (!check_ips_policy(flow)) + return false; + + if (!check_vlan(flow)) + return false; + + if (!check_addr(flow)) + return false; + + if (!check_split_addr(flow)) + return false; + + if (!check_proto(flow)) + return false; + + if (!check_port(flow)) + return false; + + if (!check_split_port(flow)) + return false; + + if (!check_intf(flow)) + return false; + + if (!check_split_intf(flow)) + return false; + + if (!check_group(flow)) + return false; + + if (!check_split_group(flow)) + return false; + + if (!check_address_space(flow)) + return false; + + return true; +} + diff --git a/src/network_inspectors/binder/binding.h b/src/network_inspectors/binder/binding.h index 77ba0ddce..88cf28bc0 100644 --- a/src/network_inspectors/binder/binding.h +++ b/src/network_inspectors/binder/binding.h @@ -15,7 +15,7 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //-------------------------------------------------------------------------- -// binder.cc author Russ Combs +// binding.h author Russ Combs #ifndef BINDING_H #define BINDING_H @@ -29,7 +29,8 @@ namespace snort { class Flow; -struct Packet; +class Inspector; +struct SnortConfig; } struct BindWhen @@ -43,20 +44,44 @@ struct BindWhen Role role; std::string svc; - bool split_nets; sfip_var_t* src_nets; sfip_var_t* dst_nets; - ByteBitSet ifaces; VlanBitSet vlans; - bool split_ports; PortBitSet src_ports; PortBitSet dst_ports; - bool split_zones; - ZoneBitSet src_zones; - ZoneBitSet dst_zones; + std::unordered_set src_intfs; + std::unordered_set dst_intfs; + + std::unordered_set src_groups; + std::unordered_set dst_groups; + + std::unordered_set addr_spaces; + + enum Criteria + { + BWC_IPS_ID = 0x0001, + BWC_PROTO = 0x0002, + BWC_SVC = 0x0004, + BWC_NETS = 0x0008, + BWC_SPLIT_NETS = 0x0010, + BWC_VLANS = 0x0020, + BWC_PORTS = 0x0040, + BWC_SPLIT_PORTS = 0x0080, + BWC_INTFS = 0x0100, + BWC_SPLIT_INTFS = 0x0200, + BWC_GROUPS = 0x0400, + BWC_SPLIT_GROUPS = 0x0800, + BWC_ADDR_SPACES = 0x1000 + }; + uint16_t criteria_flags; + + void add_criteria(uint16_t flags) + { criteria_flags |= flags; } + bool has_criteria(uint16_t flags) const + { return (criteria_flags & flags) == flags; } }; struct BindUse @@ -75,46 +100,34 @@ struct BindUse unsigned inspection_index; unsigned ips_index; What what; - void* object; + snort::Inspector* inspector; + bool global_type; }; struct Binding { - enum DirResult - { - // Did not match - DR_NO_MATCH, - - // Matched but direction could not be determined - DR_ANY_MATCH, - - // On flow: src_* matched client, dst_* matched server - // On packet: src_* matched p->src_*, dst_* matched p->dst_* - DR_FORWARD, - - // On flow: src_* matched server, dst_* matched client - // On packet: src_* matched p->dst_*, dst_* matched p->src_* - DR_REVERSE, - }; - BindWhen when; BindUse use; Binding(); - ~Binding(); - - bool check_all(const snort::Flow*, snort::Packet*, const char* = nullptr) const; - bool check_ips_policy(const snort::Flow*) const; - bool check_iface(const snort::Packet*) const; - bool check_vlan(const snort::Flow*) const; - bool check_addr(const snort::Flow*) const; - DirResult check_split_addr(const snort::Flow*, const snort::Packet*, const DirResult) const; - bool check_proto(const snort::Flow*) const; - bool check_port(const snort::Flow*) const; - DirResult check_split_port(const snort::Flow*, const snort::Packet*, const DirResult) const; - bool check_zone(const snort::Packet*) const; - DirResult check_split_zone(const snort::Packet*, const DirResult) const; - bool check_service(const snort::Flow*) const; + + void clear(); + void configure(const snort::SnortConfig* sc); + + bool check_all(const snort::Flow&, const char* = nullptr) const; + bool check_ips_policy(const snort::Flow&) const; + bool check_vlan(const snort::Flow&) const; + bool check_addr(const snort::Flow&) const; + bool check_split_addr(const snort::Flow&) const; + bool check_proto(const snort::Flow&) const; + bool check_port(const snort::Flow&) const; + bool check_split_port(const snort::Flow&) const; + bool check_intf(const snort::Flow&) const; + bool check_split_intf(const snort::Flow&) const; + bool check_group(const snort::Flow&) const; + bool check_split_group(const snort::Flow&) const; + bool check_address_space(const snort::Flow& flow) const; + bool check_service(const snort::Flow&) const; bool check_service(const char* service) const; }; diff --git a/src/network_inspectors/normalize/norm_module.cc b/src/network_inspectors/normalize/norm_module.cc index 40d622585..c16124923 100644 --- a/src/network_inspectors/normalize/norm_module.cc +++ b/src/network_inspectors/normalize/norm_module.cc @@ -354,32 +354,6 @@ bool NormalizeModule::begin(const char* fqn, int, SnortConfig*) return true; } -bool NormalizeModule::end(const char* fqn, int, SnortConfig* sc) -{ - if ( !strcmp(fqn, NORM_NAME) ) - { - NetworkPolicy* policy = get_network_policy(); - - // FIXIT-M untangle these policies. this is a workaround for loading inspection-only confs - if ( policy == nullptr ) - { - set_network_policy(sc); - policy = get_network_policy(); - set_network_policy((NetworkPolicy*)nullptr); - } - - if ( (policy->new_ttl > 1) && (policy->new_ttl >= policy->min_ttl) ) - { - if ( Norm_IsEnabled(&config, NORM_IP4_BASE) ) - Norm_Enable(&config, NORM_IP4_TTL); - - if ( Norm_IsEnabled(&config, NORM_IP6_BASE) ) - Norm_Enable(&config, NORM_IP6_TTL); - } - } - return true; -} - void NormalizeModule::add_test_peg(const PegInfo& norm) const { PegInfo test; diff --git a/src/network_inspectors/normalize/norm_module.h b/src/network_inspectors/normalize/norm_module.h index 8e185684f..66460d39d 100644 --- a/src/network_inspectors/normalize/norm_module.h +++ b/src/network_inspectors/normalize/norm_module.h @@ -37,7 +37,6 @@ public: bool set(const char*, snort::Value&, snort::SnortConfig*) override; bool begin(const char*, int, snort::SnortConfig*) override; - bool end(const char*, int, snort::SnortConfig*) override; snort::ProfileStats* get_profile() const override; const PegInfo* get_pegs() const override; diff --git a/src/network_inspectors/normalize/normalize.cc b/src/network_inspectors/normalize/normalize.cc index 4ded34b71..d5ef75a96 100644 --- a/src/network_inspectors/normalize/normalize.cc +++ b/src/network_inspectors/normalize/normalize.cc @@ -231,11 +231,20 @@ bool Normalizer::configure(SnortConfig*) // network policy with whichever instantiation of an inspection policy this normalize is in NetworkPolicy* nap = get_network_policy(); - nap->normal_mask = config.normalizer_flags; - if ( nap->new_ttl && nap->new_ttl < nap->min_ttl ) nap->new_ttl = nap->min_ttl; + if ( (nap->new_ttl > 1) && (nap->new_ttl >= nap->min_ttl) ) + { + if ( Norm_IsEnabled(&config, NORM_IP4_BASE) ) + Norm_Enable(&config, NORM_IP4_TTL); + + if ( Norm_IsEnabled(&config, NORM_IP6_BASE) ) + Norm_Enable(&config, NORM_IP6_TTL); + } + + nap->normal_mask = config.normalizer_flags; + Norm_SetConfig(&config); return true; } diff --git a/src/service_inspectors/http2_inspect/http2_stream_splitter.cc b/src/service_inspectors/http2_inspect/http2_stream_splitter.cc index 72ed92337..a74f6d897 100644 --- a/src/service_inspectors/http2_inspect/http2_stream_splitter.cc +++ b/src/service_inspectors/http2_inspect/http2_stream_splitter.cc @@ -52,7 +52,7 @@ StreamSplitter::Status Http2StreamSplitter::scan(Packet* pkt, const uint8_t* dat if (session_data == nullptr) { AssistantGadgetEvent event(pkt, "http"); - DataBus::publish(FLOW_ASSISTANT_GADGET_EVENT, event); + DataBus::publish(FLOW_ASSISTANT_GADGET_EVENT, event, pkt->flow); if (pkt->flow->assistant_gadget == nullptr) return HttpStreamSplitter::status_value(StreamSplitter::ABORT, true); pkt->flow->set_flow_data(session_data = new Http2FlowData(pkt->flow));