)
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
case APP_ID_SSHELL:
case APP_ID_SSL:
case APP_ID_QUIC:
+ case APP_ID_HTTP3:
return true;
}
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;
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);
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)
#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"
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);
SshPatternMatchers ssh_matchers;
PatternClientDetector* client_pattern_detector;
PatternServiceDetector* service_pattern_detector;
+ AlpnPatternMatchers alpn_matchers;
std::array<AppId, APP_ID_PORT_ARRAY_SIZE> tcp_port_only = {}; // port-only TCP services
std::array<AppId, APP_ID_PORT_ARRAY_SIZE> udp_port_only = {}; // port-only UDP services
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<EveProcessEvent&>(event);
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<std::string> 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();
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);
}
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
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);
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;
}
{
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())
(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();
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,
EveCaPatternMatchers::~EveCaPatternMatchers() = default;
SslPatternMatchers::~SslPatternMatchers() = default;
HttpPatternMatchers::~HttpPatternMatchers() = default;
+AlpnPatternMatchers::~AlpnPatternMatchers() = default;
ClientDetector::ClientDetector() { }
EveCaPatternMatchers::~EveCaPatternMatchers() = default;
SipPatternMatchers::~SipPatternMatchers() = default;
SslPatternMatchers::~SslPatternMatchers() = default;
+AlpnPatternMatchers::~AlpnPatternMatchers() = default;
void AppIdModule::reset_stats() {}
bool AppIdInspector::configure(snort::SnortConfig*) { return true; }
auto& ud = *UserData<LuaServiceObject>::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);
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<LuaObject>::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
auto& ud = *UserData<LuaObject>::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);
auto& ud = *UserData<LuaObject>::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;
auto& ud = *UserData<LuaObject>::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;
auto& ud = *UserData<LuaObject>::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;
auto& ud = *UserData<LuaObject>::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;
auto& ud = *UserData<LuaObject>::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;
auto& ud = *UserData<LuaObject>::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;
{
auto& ud = *UserData<LuaObject>::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;
auto& ud = *UserData<LuaObject>::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;
auto& ud = *UserData<LuaObject>::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;
auto& ud = *UserData<LuaObject>::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;
auto& ud = *UserData<LuaObject>::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;
// Verify detector user data and that we are NOT in packet context
auto& ud = *UserData<LuaObject>::check(L, DETECTOR, 1);
ud->validate_lua_state(false);
- if (!init(L)) return 0;
+ if (!init(L))
+ return 0;
int index = 1;
auto& ud = *UserData<LuaObject>::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;
auto& ud = *UserData<LuaObject>::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;
auto& ud = *UserData<LuaObject>::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;
auto& ud = *UserData<LuaObject>::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;
auto& ud = *UserData<LuaObject>::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;
auto& ud = *UserData<LuaObject>::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;
auto& ud = *UserData<LuaObject>::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;
/* 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 },
--- /dev/null
+//--------------------------------------------------------------------------
+// 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 <prbhaler@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "alpn_patterns.h"
+
+#include <algorithm>
+
+#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();
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// 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 <prbhaler@cisco.com>
+
+#ifndef ALPN_PATTERNS_H
+#define ALPN_PATTERNS_H
+
+#include <vector>
+
+#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<AlpnPattern*> 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
+
include_directories ( appid PRIVATE ${APPID_INCLUDE_DIR} )
add_cpputest( service_rsync_test )
+add_cpputest( alpn_patterns_tests )
--- /dev/null
+//--------------------------------------------------------------------------
+// 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 <prbhaler@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "service_plugins/alpn_patterns.cc"
+#include "service_alpn_patterns_mock.h"
+
+#include <CppUTest/CommandLineTestRunner.h>
+#include <CppUTest/TestHarness.h>
+#include <CppUTestExt/MockSupport.h>
+
+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;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// 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 <prbhaler@cisco.com>
+
+#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() { }
+}
+
#include "appid_peg_counts.h"
#include "utils/stats.h"
+#define APPID_UT_ID 1492
+
namespace snort
{
// Stubs for messages
StashGenericObject(STASH_GENERIC_OBJECT_APPID) {}
}
+AlpnPatternMatchers::~AlpnPatternMatchers() {}
EveCaPatternMatchers::~EveCaPatternMatchers() { }
SslPatternMatchers::~SslPatternMatchers() { }
SipPatternMatchers::~SipPatternMatchers() { }
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;}
}
}
+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;
}
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)
{
{
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<string> 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<string> 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);
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_)
{
HttpPatternMatchers::~HttpPatternMatchers() = default;
SipPatternMatchers::~SipPatternMatchers() = default;
SslPatternMatchers::~SslPatternMatchers() = default;
+AlpnPatternMatchers::~AlpnPatternMatchers() = default;
snort::SearchTool::SearchTool(bool) { }
snort::SearchTool::~SearchTool() = 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&) { }
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<std::string> get_alpn() const
+ {
+ return alpn;
+ }
+
+ void set_alpn(std::vector<std::string>& 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<std::string> alpn;
+ bool is_quic = false;
};
-
#endif
TEST(pub_sub_eve_process_event_test, eve_process_event)
{
Packet p;
+ std::vector<std::string> 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);
}