From: Pranav Bhalerao (prbhaler) Date: Sat, 30 Apr 2022 02:30:17 +0000 (+0000) Subject: Pull request #3383: appid: add alpn matchers X-Git-Tag: 3.1.29.0~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e3121e8c3fca0120359bef5f14676117cc32e315;p=thirdparty%2Fsnort3.git Pull request #3383: appid: add alpn matchers Merge in SNORT/snort3 from ~PRBHALER/snort3:quic_alpn to master Squashed commit of the following: commit 77be6266b97de2535006e3ecaa2dc84c8202aefd Author: Pranav Bhalerao Date: Mon Apr 4 22:16:02 2022 +0530 appid: add alpn matchers --- diff --git a/src/network_inspectors/appid/CMakeLists.txt b/src/network_inspectors/appid/CMakeLists.txt index 7b7ff55be..a1331e54f 100644 --- a/src/network_inspectors/appid/CMakeLists.txt +++ b/src/network_inspectors/appid/CMakeLists.txt @@ -40,6 +40,8 @@ set ( CP_APPID_SOURCES ) set ( SP_APPID_SOURCES + service_plugins/alpn_patterns.cc + service_plugins/alpn_patterns.h service_plugins/dcerpc.cc service_plugins/dcerpc.h service_plugins/service_bgp.cc diff --git a/src/network_inspectors/appid/appid_api.h b/src/network_inspectors/appid/appid_api.h index 313321b56..b979e2bb3 100644 --- a/src/network_inspectors/appid/appid_api.h +++ b/src/network_inspectors/appid/appid_api.h @@ -71,6 +71,7 @@ public: case APP_ID_SSHELL: case APP_ID_SSL: case APP_ID_QUIC: + case APP_ID_HTTP3: return true; } diff --git a/src/network_inspectors/appid/appid_app_descriptor.h b/src/network_inspectors/appid/appid_app_descriptor.h index 26b142de3..da6e65434 100644 --- a/src/network_inspectors/appid/appid_app_descriptor.h +++ b/src/network_inspectors/appid/appid_app_descriptor.h @@ -207,8 +207,19 @@ public: return service_group; } + void set_alpn_service_app_id(AppId id) + { + alpn_service_app_id = id; + } + + AppId get_alpn_service_app_id() const + { + return alpn_service_app_id; + } + private: AppId port_service_id = APP_ID_NONE; + AppId alpn_service_app_id = APP_ID_NONE; bool deferred = false; using ApplicationDescriptor::set_id; std::string my_vendor; diff --git a/src/network_inspectors/appid/appid_config.cc b/src/network_inspectors/appid/appid_config.cc index 4f040b53c..13cd42f34 100644 --- a/src/network_inspectors/appid/appid_config.cc +++ b/src/network_inspectors/appid/appid_config.cc @@ -168,6 +168,7 @@ void OdpContext::initialize(AppIdInspector& inspector) client_disco_mgr.finalize_client_patterns(); http_matchers.finalize_patterns(); eve_ca_matchers.finalize_patterns(); + alpn_matchers.finalize_patterns(); // sip patterns need to be finalized after http patterns because they // are dependent on http patterns sip_matchers.finalize_patterns(*this); @@ -188,6 +189,7 @@ void OdpContext::reload() sip_matchers.reload_patterns(); ssl_matchers.reload_patterns(); dns_matchers.reload_patterns(); + alpn_matchers.reload_patterns(); } void OdpContext::add_port_service_id(IpProtocol proto, uint16_t port, AppId appid) diff --git a/src/network_inspectors/appid/appid_config.h b/src/network_inspectors/appid/appid_config.h index 4a2383947..1573fd500 100644 --- a/src/network_inspectors/appid/appid_config.h +++ b/src/network_inspectors/appid/appid_config.h @@ -39,6 +39,7 @@ #include "length_app_cache.h" #include "lua_detector_flow_api.h" #include "lua_detector_module.h" +#include "service_plugins/alpn_patterns.h" #include "service_plugins/service_discovery.h" #include "detector_plugins/ssh_patterns.h" #include "tp_appid_module_api.h" @@ -209,6 +210,11 @@ public: return *service_pattern_detector; } + AlpnPatternMatchers& get_alpn_matchers() + { + return alpn_matchers; + } + void add_port_service_id(IpProtocol, uint16_t, AppId); void add_protocol_service_id(IpProtocol, AppId); AppId get_port_service_id(IpProtocol, uint16_t); @@ -228,6 +234,7 @@ private: SshPatternMatchers ssh_matchers; PatternClientDetector* client_pattern_detector; PatternServiceDetector* service_pattern_detector; + AlpnPatternMatchers alpn_matchers; std::array tcp_port_only = {}; // port-only TCP services std::array udp_port_only = {}; // port-only UDP services diff --git a/src/network_inspectors/appid/appid_eve_process_event_handler.cc b/src/network_inspectors/appid/appid_eve_process_event_handler.cc index 1789b2b22..e3bcf76fb 100644 --- a/src/network_inspectors/appid/appid_eve_process_event_handler.cc +++ b/src/network_inspectors/appid/appid_eve_process_event_handler.cc @@ -34,13 +34,35 @@ using namespace snort; void AppIdEveProcessEventHandler::handle(DataEvent& event, Flow* flow) { assert(flow); + + if (!pkt_thread_odp_ctxt) + return; + + Packet* p = DetectionEngine::get_current_packet(); + assert(p); + AppIdSession* asd = appid_api.get_appid_session(*flow); - if (!asd or - !asd->get_session_flags(APPID_SESSION_DISCOVER_APP | APPID_SESSION_SPECIAL_MONITORED)) + if (!asd) + { + AppidSessionDirection dir; + + dir = p->is_from_client() ? APP_ID_FROM_INITIATOR : APP_ID_FROM_RESPONDER; + + asd = AppIdSession::allocate_session(p, p->get_ip_proto_next(), dir, + inspector, *pkt_thread_odp_ctxt); + if (appidDebug->is_enabled()) + { + appidDebug->activate(flow, asd, inspector.get_ctxt().config.log_all_sessions); + if (appidDebug->is_active()) + LogMessage("AppIdDbg %s New AppId session at mercury event\n", + appidDebug->get_debug_session()); + } + } + + if (!asd->get_session_flags(APPID_SESSION_DISCOVER_APP | APPID_SESSION_SPECIAL_MONITORED)) return; - if (!pkt_thread_odp_ctxt or - (pkt_thread_odp_ctxt->get_version() != asd->get_odp_ctxt_version())) + if (pkt_thread_odp_ctxt->get_version() != asd->get_odp_ctxt_version()) return; const EveProcessEvent &eve_process_event = static_cast(event); @@ -48,27 +70,54 @@ void AppIdEveProcessEventHandler::handle(DataEvent& event, Flow* flow) const std::string& name = eve_process_event.get_process_name(); uint8_t conf = eve_process_event.get_process_confidence(); const std::string& server_name = eve_process_event.get_server_name(); - AppId app_id = APP_ID_NONE; + const std::string& user_agent = eve_process_event.get_user_agent(); + std::vector alpn_vec = eve_process_event.get_alpn(); + const bool is_quic = eve_process_event.is_flow_quic(); + + AppidChangeBits change_bits; - if (!name.empty()) + if (is_quic && alpn_vec.size()) { - app_id = asd->get_odp_ctxt().get_eve_ca_matchers().match_eve_ca_pattern(name, - conf); + AppId service_id = APP_ID_NONE; + service_id = asd->get_odp_ctxt().get_alpn_matchers().match_alpn_pattern(alpn_vec[0]); + if (service_id) + { + asd->set_alpn_service_app_id(service_id); + asd->update_encrypted_app_id(service_id); + } + else + { + asd->set_service_appid_data(APP_ID_QUIC, change_bits); + asd->set_session_flags(APPID_SESSION_SERVICE_DETECTED); + } + } + + AppId client_id = APP_ID_NONE; + if (!user_agent.empty()) + { + char* version = nullptr; + AppId service_id = APP_ID_NONE; - asd->set_eve_client_app_id(app_id); + asd->get_odp_ctxt().get_http_matchers().identify_user_agent(user_agent.c_str(), + user_agent.size(), service_id, client_id, &version); + + if (client_id != APP_ID_NONE) + asd->set_client_appid_data(client_id, change_bits, version); + + snort_free(version); } + else if (!name.empty()) + { + client_id = asd->get_odp_ctxt().get_eve_ca_matchers().match_eve_ca_pattern(name, + conf); - if (appidDebug->is_active()) - LogMessage("AppIdDbg %s encrypted client app %d process name '%s', " - "confidence: %d, server name '%s'\n", appidDebug->get_debug_session(), app_id, - name.c_str(), conf, server_name.c_str()); + asd->set_eve_client_app_id(client_id); + } if (!server_name.empty()) { - AppId client_id; - AppId payload_id; - AppidChangeBits change_bits; - snort::Packet* p = snort::DetectionEngine::get_current_packet(); + AppId client_id = APP_ID_NONE; + AppId payload_id = APP_ID_NONE; if (!asd->tsession) asd->tsession = new TlsSession(); @@ -80,7 +129,34 @@ void AppIdEveProcessEventHandler::handle(DataEvent& event, Flow* flow) server_name.length(), client_id, payload_id); asd->set_payload_id(payload_id); asd->set_ss_application_ids_payload(payload_id, change_bits); + } - asd->publish_appid_event(change_bits, *p); + if (appidDebug->is_active()) + { + std::string debug_str; + + debug_str += "encrypted client app: " + std::to_string(client_id); + if (!name.empty()) + debug_str += ", process name: " + name + ", confidence: " + std::to_string(conf); + + if (!server_name.empty()) + debug_str += ", server name: " + server_name; + + if (!user_agent.empty()) + debug_str += ", user agent: " + user_agent; + + if (is_quic && alpn_vec.size()) + { + debug_str += ", alpn: [ "; + for(unsigned int i = 0; i < alpn_vec.size(); i++) + debug_str += alpn_vec[i] + " "; + debug_str += "]"; + } + + LogMessage("AppIdDbg %s %s\n", + appidDebug->get_debug_session(), debug_str.c_str()); } + + if (change_bits.any()) + asd->publish_appid_event(change_bits, *p); } diff --git a/src/network_inspectors/appid/appid_eve_process_event_handler.h b/src/network_inspectors/appid/appid_eve_process_event_handler.h index 38454feab..13046eed3 100644 --- a/src/network_inspectors/appid/appid_eve_process_event_handler.h +++ b/src/network_inspectors/appid/appid_eve_process_event_handler.h @@ -27,9 +27,13 @@ class AppIdEveProcessEventHandler : public snort::DataHandler { public: - AppIdEveProcessEventHandler() : DataHandler(MOD_NAME) { } + AppIdEveProcessEventHandler(AppIdInspector& inspector) : + DataHandler(MOD_NAME), inspector(inspector) { } void handle(snort::DataEvent& event, snort::Flow* flow) override; + +private: + AppIdInspector& inspector; }; #endif diff --git a/src/network_inspectors/appid/appid_inspector.cc b/src/network_inspectors/appid/appid_inspector.cc index ac6521849..34d01362b 100644 --- a/src/network_inspectors/appid/appid_inspector.cc +++ b/src/network_inspectors/appid/appid_inspector.cc @@ -138,7 +138,7 @@ bool AppIdInspector::configure(SnortConfig* sc) DataBus::subscribe_global(OPPORTUNISTIC_TLS_EVENT, new AppIdOpportunisticTlsEventHandler(), *sc); - DataBus::subscribe_global(EVE_PROCESS_EVENT, new AppIdEveProcessEventHandler(), *sc); + DataBus::subscribe_global(EVE_PROCESS_EVENT, new AppIdEveProcessEventHandler(*this), *sc); DataBus::subscribe_global(SSH_EVENT, new SshEventHandler(), *sc); diff --git a/src/network_inspectors/appid/appid_session.cc b/src/network_inspectors/appid/appid_session.cc index 6a510e644..7e4cf57a6 100644 --- a/src/network_inspectors/appid/appid_session.cc +++ b/src/network_inspectors/appid/appid_session.cc @@ -493,6 +493,9 @@ void AppIdSession::update_encrypted_app_id(AppId service_id) case APP_ID_POP3: misc_app_id = APP_ID_POP3S; break; + case APP_ID_HTTP3: + case APP_ID_SMB_OVER_QUIC: + misc_app_id = APP_ID_QUIC; default: break; } @@ -768,6 +771,9 @@ AppId AppIdSession::pick_service_app_id() const { AppId rval = APP_ID_NONE; + if (api.service.get_alpn_service_app_id() > APP_ID_NONE) + return api.service.get_alpn_service_app_id(); + if (!tp_appid_ctxt) { if (is_service_detected()) diff --git a/src/network_inspectors/appid/appid_session.h b/src/network_inspectors/appid/appid_session.h index 32db58ea6..792deb0d8 100644 --- a/src/network_inspectors/appid/appid_session.h +++ b/src/network_inspectors/appid/appid_session.h @@ -526,6 +526,16 @@ public: (api.client.get_id() == APP_ID_SSL_CLIENT or api.client.get_id() <= APP_ID_NONE)); } + void set_alpn_service_app_id(AppId id) + { + api.service.set_alpn_service_app_id(id); + } + + AppId get_alpn_service_app_id() const + { + return api.service.get_alpn_service_app_id(); + } + AppId get_payload_id() const { return api.payload.get_id(); diff --git a/src/network_inspectors/appid/application_ids.h b/src/network_inspectors/appid/application_ids.h index 8ad49b095..c9223a05a 100644 --- a/src/network_inspectors/appid/application_ids.h +++ b/src/network_inspectors/appid/application_ids.h @@ -1015,6 +1015,8 @@ enum ApplicationIds : AppId APP_ID_SMB_VERSION_1 = 4645, APP_ID_SMB_VERSION_2 = 4646, APP_ID_SMB_VERSION_3 = 4647, + APP_ID_HTTP3 = 4667, + APP_ID_SMB_OVER_QUIC = 4668, #ifdef REG_TEST APP_ID_DNS_OVER_TLS = 4615, APP_ID_REGTEST = 10000, diff --git a/src/network_inspectors/appid/detector_plugins/test/detector_sip_test.cc b/src/network_inspectors/appid/detector_plugins/test/detector_sip_test.cc index 9db13097d..c1f38cfe8 100644 --- a/src/network_inspectors/appid/detector_plugins/test/detector_sip_test.cc +++ b/src/network_inspectors/appid/detector_plugins/test/detector_sip_test.cc @@ -156,6 +156,7 @@ DnsPatternMatchers::~DnsPatternMatchers() = default; EveCaPatternMatchers::~EveCaPatternMatchers() = default; SslPatternMatchers::~SslPatternMatchers() = default; HttpPatternMatchers::~HttpPatternMatchers() = default; +AlpnPatternMatchers::~AlpnPatternMatchers() = default; ClientDetector::ClientDetector() { } diff --git a/src/network_inspectors/appid/detector_plugins/test/http_url_patterns_test.cc b/src/network_inspectors/appid/detector_plugins/test/http_url_patterns_test.cc index 2fe6aebf9..303b9224b 100644 --- a/src/network_inspectors/appid/detector_plugins/test/http_url_patterns_test.cc +++ b/src/network_inspectors/appid/detector_plugins/test/http_url_patterns_test.cc @@ -87,6 +87,7 @@ DnsPatternMatchers::~DnsPatternMatchers() = default; EveCaPatternMatchers::~EveCaPatternMatchers() = default; SipPatternMatchers::~SipPatternMatchers() = default; SslPatternMatchers::~SslPatternMatchers() = default; +AlpnPatternMatchers::~AlpnPatternMatchers() = default; void AppIdModule::reset_stats() {} bool AppIdInspector::configure(snort::SnortConfig*) { return true; } diff --git a/src/network_inspectors/appid/lua_detector_api.cc b/src/network_inspectors/appid/lua_detector_api.cc index 6cbc2593e..7780d7136 100644 --- a/src/network_inspectors/appid/lua_detector_api.cc +++ b/src/network_inspectors/appid/lua_detector_api.cc @@ -173,7 +173,8 @@ static int service_init(lua_State* L) auto& ud = *UserData::check(L, DETECTOR, 1); // Verify detector user data and that we are NOT in packet context ud->validate_lua_state(false); - if (!init(L)) return 0; + if (!init(L)) + return 0; // auto pServiceName = luaL_checkstring(L, 2); auto pValidator = luaL_checkstring(L, 3); @@ -986,6 +987,40 @@ static int client_add_payload(lua_State* L) return 1; } +/** Add a alpn to service app mapping. + * @param Lua_State* - Lua state variable. + * @param appid/stack - the AppId to map the data to. + * @param alpn - application protocol negotiations string. + */ +static int add_alpn_to_service_mapping(lua_State* L) +{ + auto& ud = *UserData::check(L, DETECTOR, 1); + // Verify detector user data and that we are NOT in packet context + ud->validate_lua_state(false); + if (!init(L)) + return 0; + int index = 1; + + uint32_t appid = lua_tointeger(L, ++index); + + // Verify that alpn is a valid string + const char* tmp_string = lua_tostring(L, ++index); + if (!tmp_string) + { + ErrorMessage("appid: Invalid alpn service string: appid %u.\n", appid); + return 0; + } + const std::string service_name(tmp_string); + const std::string detector_name = ud->get_detector()->get_name(); + + ud->get_odp_ctxt().get_alpn_matchers().add_alpn_pattern(appid, service_name, detector_name); + + ud->get_odp_ctxt().get_app_info_mgr().set_app_info_active(appid); + + return 0; +} + + /** Add a fp process to client app mapping. * @param Lua_State* - Lua state variable. * @param appid/stack - the AppId to map the fp data to @@ -997,7 +1032,8 @@ static int add_process_to_client_mapping(lua_State* L) auto& ud = *UserData::check(L, DETECTOR, 1); // Verify detector user data and that we are NOT in packet context ud->validate_lua_state(false); - if (!init(L)) return 0; + if (!init(L)) + return 0; int index = 1; uint32_t appid = lua_tointeger(L, ++index); @@ -1054,7 +1090,8 @@ static int detector_add_http_pattern(lua_State* L) auto& ud = *UserData::check(L, DETECTOR, 1); // Verify detector user data and that we are NOT in packet context ud->validate_lua_state(false); - if (!init(L)) return 0; + if (!init(L)) + return 0; int index = 1; @@ -1096,7 +1133,8 @@ static int detector_add_ssl_cert_pattern(lua_State* L) auto& ud = *UserData::check(L, DETECTOR, 1); // Verify detector user data and that we are NOT in packet context ud->validate_lua_state(false); - if (!init(L)) return 0; + if (!init(L)) + return 0; int index = 1; @@ -1123,7 +1161,8 @@ static int detector_add_dns_host_pattern(lua_State* L) auto& ud = *UserData::check(L, DETECTOR, 1); // Verify detector user data and that we are NOT in packet context ud->validate_lua_state(false); - if (!init(L)) return 0; + if (!init(L)) + return 0; int index = 1; @@ -1149,7 +1188,8 @@ static int detector_add_ssl_cname_pattern(lua_State* L) auto& ud = *UserData::check(L, DETECTOR, 1); // Verify detector user data and that we are NOT in packet context ud->validate_lua_state(false); - if (!init(L)) return 0; + if (!init(L)) + return 0; int index = 1; @@ -1176,7 +1216,8 @@ static int detector_add_host_port_application(lua_State* L) auto& ud = *UserData::check(L, DETECTOR, 1); // Verify detector user data and that we are NOT in packet context ud->validate_lua_state(false); - if (!init(L)) return 0; + if (!init(L)) + return 0; SfIp ip_address; int index = 1; @@ -1255,7 +1296,8 @@ static int detector_add_content_type_pattern(lua_State* L) auto& ud = *UserData::check(L, DETECTOR, 1); // Verify detector user data and that we are NOT in packet context ud->validate_lua_state(false); - if (!init(L)) return 0; + if (!init(L)) + return 0; size_t stringSize = 0; int index = 1; @@ -1284,7 +1326,8 @@ static int detector_add_ssh_client_pattern(lua_State* L) { auto& ud = *UserData::check(L, DETECTOR, 1); ud->validate_lua_state(false); - if (!init(L)) return 0; + if (!init(L)) + return 0; size_t string_size = 0; int index = 1; @@ -1476,7 +1519,8 @@ static int detector_chp_create_application(lua_State* L) auto& ud = *UserData::check(L, DETECTOR, 1); // Verify detector user data and that we are NOT in packet context ud->validate_lua_state(false); - if (!init(L)) return 0; + if (!init(L)) + return 0; int index = 1; @@ -1628,7 +1672,8 @@ static int detector_add_chp_action(lua_State* L) auto& ud = *UserData::check(L, DETECTOR, 1); // Verify detector user data and that we are NOT in packet context ud->validate_lua_state(false); - if (!init(L)) return 0; + if (!init(L)) + return 0; HttpFieldIds ptype; size_t psize; @@ -1719,7 +1764,8 @@ static int detector_add_chp_multi_action(lua_State* L) auto& ud = *UserData::check(L, DETECTOR, 1); // Verify detector user data and that we are NOT in packet context ud->validate_lua_state(false); - if (!init(L)) return 0; + if (!init(L)) + return 0; HttpFieldIds ptype; size_t psize; @@ -1765,7 +1811,8 @@ static int detector_port_only_service(lua_State* L) auto& ud = *UserData::check(L, DETECTOR, 1); // Verify detector user data and that we are NOT in packet context ud->validate_lua_state(false); - if (!init(L)) return 0; + if (!init(L)) + return 0; int index = 1; @@ -1911,7 +1958,8 @@ static int detector_add_url_application(lua_State* L) // Verify detector user data and that we are NOT in packet context auto& ud = *UserData::check(L, DETECTOR, 1); ud->validate_lua_state(false); - if (!init(L)) return 0; + if (!init(L)) + return 0; int index = 1; @@ -2004,7 +2052,8 @@ static int detector_add_rtmp_url(lua_State* L) auto& ud = *UserData::check(L, DETECTOR, 1); // Verify detector user data and that we are NOT in packet context ud->validate_lua_state(false); - if (!init(L)) return 0; + if (!init(L)) + return 0; int index = 1; @@ -2097,7 +2146,8 @@ static int detector_add_sip_user_agent(lua_State* L) auto& ud = *UserData::check(L, DETECTOR, 1); // Verify detector user data and that we are NOT in packet context ud->validate_lua_state(false); - if (!init(L)) return 0; + if (!init(L)) + return 0; int index = 1; @@ -2216,7 +2266,8 @@ static int add_http_pattern(lua_State* L) auto& ud = *UserData::check(L, DETECTOR, 1); // Verify detector user data and that we are NOT in packet context ud->validate_lua_state(false); - if (!init(L)) return 0; + if (!init(L)) + return 0; int index = 1; @@ -2255,7 +2306,8 @@ static int add_url_pattern(lua_State* L) auto& ud = *UserData::check(L, DETECTOR, 1); // Verify detector user data and that we are NOT in packet context ud->validate_lua_state(false); - if (!init(L)) return 0; + if (!init(L)) + return 0; int index = 1; @@ -2348,7 +2400,8 @@ static int add_port_pattern_client(lua_State* L) auto& ud = *UserData::check(L, DETECTOR, 1); // Verify detector user data and that we are NOT in packet context ud->validate_lua_state(false); - if (!init(L)) return 0; + if (!init(L)) + return 0; size_t patternSize = 0; int index = 1; @@ -2402,7 +2455,8 @@ static int add_port_pattern_service(lua_State* L) auto& ud = *UserData::check(L, DETECTOR, 1); // Verify detector user data and that we are NOT in packet context ud->validate_lua_state(false); - if (!init(L)) return 0; + if (!init(L)) + return 0; size_t patternSize = 0; int index = 1; @@ -2437,7 +2491,8 @@ static int detector_add_sip_server(lua_State* L) auto& ud = *UserData::check(L, DETECTOR, 1); // Verify detector user data and that we are NOT in packet context ud->validate_lua_state(false); - if (!init(L)) return 0; + if (!init(L)) + return 0; int index = 1; @@ -2725,6 +2780,7 @@ static const luaL_Reg detector_methods[] = /* add client mapping for process name derived by fingerprinting */ { "addProcessToClientMapping", add_process_to_client_mapping }, + { "addAlpnToServiceMapping", add_alpn_to_service_mapping }, //HTTP Multi Pattern engine { "CHPCreateApp", detector_chp_create_application }, diff --git a/src/network_inspectors/appid/service_plugins/alpn_patterns.cc b/src/network_inspectors/appid/service_plugins/alpn_patterns.cc new file mode 100644 index 000000000..44abafa25 --- /dev/null +++ b/src/network_inspectors/appid/service_plugins/alpn_patterns.cc @@ -0,0 +1,117 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2022 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. +//-------------------------------------------------------------------------- + +// alpn_patterns.cc author Pranav Bhalerao + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "alpn_patterns.h" + +#include + +#include "log/messages.h" +#include "utils/util.h" +#include "appid_debug.h" + +using namespace snort; +using namespace std; + +void AlpnPatternMatchers::add_alpn_pattern(AppId app_id, const string& pattern_str, + const string& detector) +{ + auto match = find_if(alpn_load_list.begin(), alpn_load_list.end(), + [pattern_str] (AlpnPattern* alpn) { return alpn->pattern == pattern_str; }); + if (match != alpn_load_list.end()) + { + if ((*match)->app_id != app_id) + WarningMessage("appid: detector %s - alpn '%s' for service app %d is already " + "mapped to service app %d\n", detector.c_str(), (*match)->pattern.c_str(), app_id, + (*match)->app_id); + } + else + { + AlpnPattern* new_alpn_pattern = new AlpnPattern(app_id, pattern_str); + alpn_load_list.push_back(new_alpn_pattern); + } +} + +static int alpn_pattern_match(void* id, void*, int, void* data, void*) +{ + AlpnPatternList* alpn_match_list = (AlpnPatternList *)data; + alpn_match_list->push_back((AlpnPattern *)id); + return 0; +} + +AppId AlpnPatternMatchers::match_alpn_pattern(const string& pattern) +{ + AlpnPatternList* alpn_match_list = new AlpnPatternList(); + AlpnPattern* best_match = nullptr; + + alpn_pattern_matcher.find_all(pattern.data(), pattern.size(), alpn_pattern_match, + false, alpn_match_list); + + for (auto &mp : *alpn_match_list) + { + if (mp->pattern.size() == pattern.size()) + { + best_match = mp; + } + else if (!best_match or (mp->pattern.size() > best_match->pattern.size())) + { + best_match = mp; + continue; + } + } + + AppId ret_app_id = APP_ID_NONE; + if (best_match) + ret_app_id = best_match->app_id; + + delete alpn_match_list; + + return ret_app_id; +} + +AlpnPatternMatchers::~AlpnPatternMatchers() +{ + for (auto& p : alpn_load_list) + delete p; + alpn_load_list.clear(); +} + +void AlpnPatternMatchers::finalize_patterns() +{ + for (auto& p : alpn_load_list) + { + alpn_pattern_matcher.add(p->pattern.data(), p->pattern.size(), p, true); + + #ifdef REG_TEST + LogMessage("Adding ALPN service App pattern %d %s\n", + p->app_id, p->pattern.c_str()); + #endif + } + alpn_pattern_matcher.prep(); +} + +void AlpnPatternMatchers::reload_patterns() +{ + alpn_pattern_matcher.reload(); +} + diff --git a/src/network_inspectors/appid/service_plugins/alpn_patterns.h b/src/network_inspectors/appid/service_plugins/alpn_patterns.h new file mode 100644 index 000000000..a2aa88fe9 --- /dev/null +++ b/src/network_inspectors/appid/service_plugins/alpn_patterns.h @@ -0,0 +1,58 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2022 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. +//-------------------------------------------------------------------------- + +// alpn_patterns.h author Pranav Bhalerao + +#ifndef ALPN_PATTERNS_H +#define ALPN_PATTERNS_H + +#include + +#include "search_engines/search_tool.h" +#include "application_ids.h" + +struct AlpnPattern +{ + const AppId app_id; + const std::string pattern; + + AlpnPattern(AppId id, const std::string& name) : app_id(id), pattern(name){} + + ~AlpnPattern() {} +}; + +typedef std::vector AlpnPatternList; + +class AlpnPatternMatchers +{ +public: + ~AlpnPatternMatchers(); + AppId match_alpn_pattern(const std::string&); + void add_alpn_pattern(AppId, const std::string&, const std::string&); + void finalize_patterns(); + void reload_patterns(); + + const AlpnPatternList& get_alpn_load_list() const { return alpn_load_list; } + +private: + snort::SearchTool alpn_pattern_matcher = snort::SearchTool(); + AlpnPatternList alpn_load_list; +}; + +#endif + diff --git a/src/network_inspectors/appid/service_plugins/test/CMakeLists.txt b/src/network_inspectors/appid/service_plugins/test/CMakeLists.txt index d221dfda3..de668cdb2 100644 --- a/src/network_inspectors/appid/service_plugins/test/CMakeLists.txt +++ b/src/network_inspectors/appid/service_plugins/test/CMakeLists.txt @@ -2,4 +2,5 @@ include_directories ( appid PRIVATE ${APPID_INCLUDE_DIR} ) add_cpputest( service_rsync_test ) +add_cpputest( alpn_patterns_tests ) diff --git a/src/network_inspectors/appid/service_plugins/test/alpn_patterns_tests.cc b/src/network_inspectors/appid/service_plugins/test/alpn_patterns_tests.cc new file mode 100644 index 000000000..891735ad3 --- /dev/null +++ b/src/network_inspectors/appid/service_plugins/test/alpn_patterns_tests.cc @@ -0,0 +1,98 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2022 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. +//-------------------------------------------------------------------------- +// +// alpn_patterns_tests.cc author Pranav Bhalerao + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "service_plugins/alpn_patterns.cc" +#include "service_alpn_patterns_mock.h" + +#include +#include +#include + +static AlpnPatternMatchers* alpn_matcher = nullptr; +AlpnPattern alpn_pattern(APPID_UT_ID, "h3"); + +namespace snort +{ +int SearchTool::find_all(const char* pattern, unsigned, MpseMatch, bool, void* data) +{ + if (strcmp(pattern, "h3") == 0) + alpn_pattern_match(&alpn_pattern, nullptr, 0, data, nullptr); + return 0; +} +} + +TEST_GROUP(alpn_patterns_tests) +{ + void setup() override + { + alpn_matcher = new AlpnPatternMatchers(); + } + void teardown() override + { + delete alpn_matcher; + } +}; + + +TEST(alpn_patterns_tests, alpn_pattern_match) +{ + AlpnPatternList data; + AlpnPattern alpn1(APPID_UT_ID + 1, "h3"); + alpn_pattern_match(&alpn1, nullptr, 0, &data, nullptr); + AlpnPattern* alpn = data.back(); + CHECK(alpn->app_id == alpn1.app_id); + CHECK(alpn->pattern == alpn1.pattern); + + AlpnPattern alpn2(APPID_UT_ID + 2, "smb"); + alpn_pattern_match(&alpn2, nullptr, 0, &data, nullptr); + alpn = data.back(); + CHECK(alpn->app_id == alpn2.app_id); + CHECK(alpn->pattern == alpn2.pattern); +} + +TEST(alpn_patterns_tests, match_alpn_pattern) +{ + // 1. pattern not present in pattern matcher list + CHECK(alpn_matcher->match_alpn_pattern("smb") == 0); + + // 2. pattern present in pattern matcher list + CHECK(alpn_matcher->match_alpn_pattern("h3") == APPID_UT_ID); +} + +TEST(alpn_patterns_tests, add_alpn_pattern) +{ + // same alpn mapped to different appid + alpn_matcher->add_alpn_pattern(APPID_UT_ID + 1, "h3", "custom_detector.lua"); + alpn_matcher->add_alpn_pattern(APPID_UT_ID + 2, "h3", "odp_detector.lua"); + + CHECK(alpn_matcher->get_alpn_load_list().size() == 1); + CHECK(alpn_matcher->get_alpn_load_list()[0]->app_id == APPID_UT_ID + 1); +} + +int main(int argc, char** argv) +{ + int return_value = CommandLineTestRunner::RunAllTests(argc, argv); + return return_value; +} + diff --git a/src/network_inspectors/appid/service_plugins/test/service_alpn_patterns_mock.h b/src/network_inspectors/appid/service_plugins/test/service_alpn_patterns_mock.h new file mode 100644 index 000000000..d2c107aa2 --- /dev/null +++ b/src/network_inspectors/appid/service_plugins/test/service_alpn_patterns_mock.h @@ -0,0 +1,38 @@ +//-------------------------------------------------------------------------- +// Copyright (C) 2022 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. +//-------------------------------------------------------------------------- +// alpn_patterns_mock.h author Pranav Bhalerao + +#define APPID_UT_ID 1492 + +namespace snort +{ +// Stubs for messages +void LogMessage(const char*,...) { } +void WarningMessage(const char*,...) { } + +// Stubs for search_tool.cc +SearchTool::SearchTool(bool) { } +SearchTool::~SearchTool() = default; +void SearchTool::add(const char*, unsigned, int, bool) { } +void SearchTool::add(const char*, unsigned, void*, bool) { } +void SearchTool::add(const uint8_t*, unsigned, int, bool) { } +void SearchTool::add(const uint8_t*, unsigned, void*, bool) { } +void SearchTool::prep() { } +void SearchTool::reload() { } +} + diff --git a/src/network_inspectors/appid/service_plugins/test/service_plugin_mock.h b/src/network_inspectors/appid/service_plugins/test/service_plugin_mock.h index 40d83fae4..2e6729c15 100644 --- a/src/network_inspectors/appid/service_plugins/test/service_plugin_mock.h +++ b/src/network_inspectors/appid/service_plugins/test/service_plugin_mock.h @@ -24,6 +24,8 @@ #include "appid_peg_counts.h" #include "utils/stats.h" +#define APPID_UT_ID 1492 + namespace snort { // Stubs for messages @@ -80,6 +82,7 @@ AppIdSessionApi::AppIdSessionApi(const AppIdSession*, const SfIp&) : StashGenericObject(STASH_GENERIC_OBJECT_APPID) {} } +AlpnPatternMatchers::~AlpnPatternMatchers() {} EveCaPatternMatchers::~EveCaPatternMatchers() { } SslPatternMatchers::~SslPatternMatchers() { } SipPatternMatchers::~SipPatternMatchers() { } diff --git a/src/network_inspectors/appid/test/appid_discovery_test.cc b/src/network_inspectors/appid/test/appid_discovery_test.cc index 281f14383..39fdcc59e 100644 --- a/src/network_inspectors/appid/test/appid_discovery_test.cc +++ b/src/network_inspectors/appid/test/appid_discovery_test.cc @@ -143,6 +143,7 @@ EveCaPatternMatchers::~EveCaPatternMatchers() = default; HttpPatternMatchers::~HttpPatternMatchers() = default; SipPatternMatchers::~SipPatternMatchers() = default; SslPatternMatchers::~SslPatternMatchers() = default; +AlpnPatternMatchers::~AlpnPatternMatchers() = default; void ApplicationDescriptor::set_id(const Packet&, AppIdSession&, AppidSessionDirection, AppId, AppidChangeBits&) { } void ApplicationDescriptor::set_id(AppId app_id){my_id = app_id;} diff --git a/src/network_inspectors/appid/test/appid_eve_process_event_handler_test.cc b/src/network_inspectors/appid/test/appid_eve_process_event_handler_test.cc index 58def9f44..9b59fbb95 100644 --- a/src/network_inspectors/appid/test/appid_eve_process_event_handler_test.cc +++ b/src/network_inspectors/appid/test/appid_eve_process_event_handler_test.cc @@ -60,13 +60,20 @@ Packet* DetectionEngine::get_current_packet() } } +AppIdSession* AppIdSession::allocate_session(const Packet*, IpProtocol, + AppidSessionDirection, AppIdInspector&, OdpContext&) +{ + return nullptr; +} + void AppIdSession::publish_appid_event(AppidChangeBits&, const Packet&, bool, uint32_t) { return; } -bool SslPatternMatchers::scan_hostname(const uint8_t*, size_t, AppId&, AppId&) +bool SslPatternMatchers::scan_hostname(const uint8_t*, size_t, AppId&, AppId& payload) { + payload = APPID_UT_ID + 1; return true; } @@ -75,11 +82,34 @@ void AppIdSession::set_ss_application_ids_payload(AppId, AppidChangeBits&) return; } +void AppIdSession::set_client_appid_data(AppId, AppidChangeBits&, char*) +{ + set_client_id(APPID_UT_ID); + return; +} + void ApplicationDescriptor::set_id(const Packet&, AppIdSession&, AppidSessionDirection, AppId, AppidChangeBits&) { } void AppIdModule::reset_stats() { } void AppIdDebug::activate(snort::Flow const*, AppIdSession const*, bool) { } +void AppIdSession::update_encrypted_app_id(AppId) {} +void HttpPatternMatchers::identify_user_agent(const char*, int, AppId&, AppId& client, char**) +{ + client = APPID_UT_ID; +} + +void AppIdSession::set_service_appid_data(AppId, AppidChangeBits&, char*) +{ +} + +AppId AlpnPatternMatchers::match_alpn_pattern(const string& str) +{ + if (!str.compare("h3")) + return APPID_UT_ID + 2; + else + return APP_ID_NONE; +} AppId EveCaPatternMatchers::match_eve_ca_pattern(const string&, uint8_t) { @@ -109,13 +139,65 @@ TEST(appid_eve_process_event_handler_tests, eve_process_event_handler) { Packet p; EveProcessEvent event(p, "firefox", 90); - AppIdEveProcessEventHandler event_handler; + AppIdEveProcessEventHandler event_handler(dummy_appid_inspector); Flow* flow = new Flow(); event_handler.handle(event, flow); CHECK(session->get_eve_client_app_id() == APPID_UT_ID); delete flow; } +TEST(appid_eve_process_event_handler_tests, eve_user_agent_event_handler) +{ + Packet p; + EveProcessEvent event(p, "firefox", 90); + event.set_user_agent("chrome"); + AppIdEveProcessEventHandler event_handler(dummy_appid_inspector); + Flow* flow = new Flow(); + event_handler.handle(event, flow); + CHECK(session->get_client_id() == APPID_UT_ID); + delete flow; +} + +TEST(appid_eve_process_event_handler_tests, eve_server_name_event_handler) +{ + Packet p; + EveProcessEvent event(p, "firefox", 90); + event.set_server_name("www.google.com"); + AppIdEveProcessEventHandler event_handler(dummy_appid_inspector); + Flow* flow = new Flow(); + event_handler.handle(event, flow); + CHECK(session->get_payload_id() == APPID_UT_ID + 1); + delete flow; +} + +TEST(appid_eve_process_event_handler_tests, eve_alpn_event_handler) +{ + Packet p; + vector alpn = {"h3"}; + EveProcessEvent event(p, "firefox", 90); + event.set_alpn(alpn); + event.set_quic(true); + AppIdEveProcessEventHandler event_handler(dummy_appid_inspector); + Flow* flow = new Flow(); + event_handler.handle(event, flow); + CHECK(session->get_alpn_service_app_id() == APPID_UT_ID + 2); + delete flow; +} + +TEST(appid_eve_process_event_handler_tests, eve_unknown_alpn_event_handler) +{ + Packet p; + vector alpn = {"smb"}; + EveProcessEvent event(p, "firefox", 90); + event.set_alpn(alpn); + event.set_quic(true); + AppIdEveProcessEventHandler event_handler(dummy_appid_inspector); + Flow* flow = new Flow(); + event_handler.handle(event, flow); + CHECK(session->get_alpn_service_app_id() == APP_ID_NONE); + delete flow; +} + int main(int argc, char** argv) { int return_value = CommandLineTestRunner::RunAllTests(argc, argv); diff --git a/src/network_inspectors/appid/test/appid_mock_definitions.h b/src/network_inspectors/appid/test/appid_mock_definitions.h index 054df4f5f..844bdab08 100644 --- a/src/network_inspectors/appid/test/appid_mock_definitions.h +++ b/src/network_inspectors/appid/test/appid_mock_definitions.h @@ -84,6 +84,7 @@ EveCaPatternMatchers::~EveCaPatternMatchers() = default; HttpPatternMatchers::~HttpPatternMatchers() = default; SipPatternMatchers::~SipPatternMatchers() = default; SslPatternMatchers::~SslPatternMatchers() = default; +AlpnPatternMatchers::~AlpnPatternMatchers() = default; void Field::set(int32_t length, const uint8_t* start, bool own_the_buffer_) { diff --git a/src/network_inspectors/appid/test/service_state_test.cc b/src/network_inspectors/appid/test/service_state_test.cc index 507d58391..031da2b59 100644 --- a/src/network_inspectors/appid/test/service_state_test.cc +++ b/src/network_inspectors/appid/test/service_state_test.cc @@ -125,6 +125,7 @@ EveCaPatternMatchers::~EveCaPatternMatchers() = default; HttpPatternMatchers::~HttpPatternMatchers() = default; SipPatternMatchers::~SipPatternMatchers() = default; SslPatternMatchers::~SslPatternMatchers() = default; +AlpnPatternMatchers::~AlpnPatternMatchers() = default; snort::SearchTool::SearchTool(bool) { } snort::SearchTool::~SearchTool() = default; diff --git a/src/network_inspectors/appid/test/tp_lib_handler_test.cc b/src/network_inspectors/appid/test/tp_lib_handler_test.cc index 0f3848a5a..6045ac200 100644 --- a/src/network_inspectors/appid/test/tp_lib_handler_test.cc +++ b/src/network_inspectors/appid/test/tp_lib_handler_test.cc @@ -60,6 +60,7 @@ EveCaPatternMatchers::~EveCaPatternMatchers() = default; HttpPatternMatchers::~HttpPatternMatchers() = default; SipPatternMatchers::~SipPatternMatchers() = default; SslPatternMatchers::~SslPatternMatchers() = default; +AlpnPatternMatchers::~AlpnPatternMatchers() = default; AppIdConfig::~AppIdConfig() = default; OdpContext::OdpContext(const AppIdConfig&, snort::SnortConfig*) { } void ServiceDiscovery::initialize(AppIdInspector&) { } diff --git a/src/pub_sub/eve_process_event.h b/src/pub_sub/eve_process_event.h index 764b6b116..53d425ceb 100644 --- a/src/pub_sub/eve_process_event.h +++ b/src/pub_sub/eve_process_event.h @@ -56,11 +56,45 @@ public: server_name = server; } + const std::string& get_user_agent() const + { + return user_agent; + } + + void set_user_agent(const char* u_a) + { + if (u_a) + user_agent = u_a; + } + + const std::vector get_alpn() const + { + return alpn; + } + + void set_alpn(std::vector& alpn_vec) + { + if(alpn_vec.size()) + alpn = alpn_vec; + } + + void set_quic(bool flag) + { + is_quic = flag; + } + + bool is_flow_quic() const + { + return is_quic; + } + private: const snort::Packet &p; std::string process_name; uint8_t process_confidence = 0; std::string server_name; + std::string user_agent; + std::vector alpn; + bool is_quic = false; }; - #endif diff --git a/src/pub_sub/test/pub_sub_eve_process_event_test.cc b/src/pub_sub/test/pub_sub_eve_process_event_test.cc index 4690ca1f2..70d06f58a 100644 --- a/src/pub_sub/test/pub_sub_eve_process_event_test.cc +++ b/src/pub_sub/test/pub_sub_eve_process_event_test.cc @@ -44,9 +44,16 @@ TEST_GROUP(pub_sub_eve_process_event_test) TEST(pub_sub_eve_process_event_test, eve_process_event) { Packet p; + std::vector alpn = {"h3"}; EveProcessEvent event(p, "process", 10); + event.set_user_agent("chrome"); + event.set_server_name("www.google.com"); + event.set_alpn(alpn); CHECK(event.get_process_name() == "process"); CHECK(event.get_process_confidence() == 10); + CHECK(event.get_user_agent() == "chrome"); + CHECK(event.get_server_name() == "www.google.com"); + CHECK(event.get_alpn()[0] == "h3"); CHECK(event.get_packet() == &p); }