]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #2485 in SNORT/snort3 from ~MASHASAN/snort3:ua_fp to master
authorMasud Hasan (mashasan) <mashasan@cisco.com>
Mon, 21 Sep 2020 19:02:28 +0000 (19:02 +0000)
committerMasud Hasan (mashasan) <mashasan@cisco.com>
Mon, 21 Sep 2020 19:02:28 +0000 (19:02 +0000)
Squashed commit of the following:

commit b363e332c5bca6a23f0d434171c2ebeb8f1bd79a
Author: Masud Hasan <mashasan@cisco.com>
Date:   Tue Sep 15 13:09:27 2020 -0400

    rna: Supporting user agent fingerprints

15 files changed:
src/host_tracker/host_tracker.cc
src/host_tracker/host_tracker.h
src/network_inspectors/rna/CMakeLists.txt
src/network_inspectors/rna/dev_notes.txt
src/network_inspectors/rna/rna_app_discovery.cc
src/network_inspectors/rna/rna_app_discovery.h
src/network_inspectors/rna/rna_config.h
src/network_inspectors/rna/rna_fingerprint.h
src/network_inspectors/rna/rna_fingerprint_ua.cc [new file with mode: 0644]
src/network_inspectors/rna/rna_fingerprint_ua.h [new file with mode: 0644]
src/network_inspectors/rna/rna_inspector.cc
src/network_inspectors/rna/rna_logger.cc
src/network_inspectors/rna/rna_logger.h
src/network_inspectors/rna/rna_module.cc
src/network_inspectors/rna/test/rna_module_mock.h

index 591cb25b11486db30aa302f14aa00cb21c6e1140..a5770c499f81ae3a1229d859cc8e362597b2af59 100644 (file)
@@ -369,6 +369,38 @@ bool HostTracker::add_tcp_fingerprint(uint32_t fpid)
     return result.second;
 }
 
+DeviceFingerprint::DeviceFingerprint(uint32_t id, uint32_t type, bool jb, const char* dev) :
+    fpid(id), fp_type(type), jail_broken(jb)
+{
+    if ( dev )
+    {
+        strncpy(device, dev, INFO_SIZE);
+        device[INFO_SIZE-1] = '\0';
+    }
+}
+
+bool HostTracker::add_ua_fingerprint(uint32_t fpid, uint32_t fp_type, bool jail_broken,
+    const char* device, uint8_t max_devices)
+{
+    lock_guard<mutex> lck(host_tracker_lock);
+
+    int count = 0;
+    for ( const auto& fp : ua_fps )
+    {
+        if ( fpid != fp.fpid or fp_type != fp.fp_type )
+            continue;
+        ++count; // only count same fpid with different device information
+        if ( count >= max_devices )
+            return false;
+        if ( jail_broken == fp.jail_broken and ( ( !device and fp.device[0] == '\0') or
+            ( device and strncmp(fp.device, device, INFO_SIZE) == 0) ) )
+            return false;
+    }
+
+    ua_fps.emplace_back(fpid, fp_type, jail_broken, device);
+    return true;
+}
+
 size_t HostTracker::get_client_count()
 {
     lock_guard<mutex> lck(host_tracker_lock);
@@ -484,4 +516,19 @@ void HostTracker::stringify(string& str)
         for ( const auto& fpid : tcp_fpids )
             str += to_string(fpid) + (--total ? ", " : "");
     }
+
+    total = ua_fps.size();
+    if ( total )
+    {
+        str += "\nua fingerprint: ";
+        for ( const auto& fp : ua_fps )
+        {
+            str += to_string(fp.fpid) + " (type: " + to_string(fp.fp_type);
+            if ( fp.jail_broken )
+                str += ", jail-broken";
+            if ( fp.device[0] != '\0' )
+                str += ", device: " + string(fp.device);
+            str += string(")") + (--total ? ", " : "");
+        }
+    }
 }
index e34f5db5959122a8a915d1f16694d948ca2f1e77..1d7890b732d9a99cf90b2a4ad5351a193e300315 100644 (file)
@@ -100,6 +100,15 @@ struct HostClient
     AppId service;
 };
 
+struct DeviceFingerprint
+{
+    DeviceFingerprint(uint32_t id, uint32_t type, bool jb, const char* dev);
+    uint32_t fpid;
+    uint32_t fp_type;
+    bool jail_broken;
+    char device[INFO_SIZE] = { 0 };
+};
+
 enum HostType
 {
     HOST_TYPE_HOST=0,
@@ -112,6 +121,7 @@ enum HostType
 typedef HostCacheAllocIp<HostMac> HostMacAllocator;
 typedef HostCacheAllocIp<HostApplication> HostAppAllocator;
 typedef HostCacheAllocIp<HostClient> HostClientAllocator;
+typedef HostCacheAllocIp<DeviceFingerprint> HostDeviceFpAllocator;
 
 class SO_PUBLIC HostTracker
 {
@@ -212,6 +222,8 @@ public:
     size_t get_client_count();
     HostClient get_client(AppId id, const char* version, AppId service, bool& is_new);
     bool add_tcp_fingerprint(uint32_t fpid);
+    bool add_ua_fingerprint(uint32_t fpid, uint32_t fp_type, bool jail_broken,
+        const char* device_info, uint8_t max_devices);
 
     //  This should be updated whenever HostTracker data members are changed
     void stringify(std::string& str);
@@ -227,6 +239,7 @@ private:
     std::vector<HostApplication, HostAppAllocator> services;
     std::vector<HostClient, HostClientAllocator> clients;
     std::set<uint32_t, std::less<uint32_t>, HostCacheAllocIp<uint32_t>> tcp_fpids;
+    std::vector<DeviceFingerprint, HostDeviceFpAllocator> ua_fps;
 
     bool vlan_tag_present = false;
     vlan::VlanTagHdr vlan_tag;
index 57d672cf7dd071c36c961a0e77cac742b1a7d107..800fec670cb942f75bbf711ed5eef853fd8ecbc6 100644 (file)
@@ -1,6 +1,7 @@
 set (RNA_INCLUDES
     rna_fingerprint.h
     rna_fingerprint_tcp.h
+    rna_fingerprint_ua.h
     rna_inspector.h
     rna_logger.h
     rna_name.h
@@ -15,7 +16,7 @@ set ( RNA_SOURCES
     rna_fingerprint.cc
     rna_fingerprint.h
     rna_fingerprint_tcp.cc
-    rna_fingerprint_tcp.h
+    rna_fingerprint_ua.cc
     rna_inspector.cc
     rna_inspector.h
     rna_logger.cc
index c1cc66c277f57552c1e90f2267b996e9c179232c..2f59c2cbefce1a6a07fc5de56f884979400835f7 100644 (file)
@@ -219,3 +219,18 @@ topts: tcp options
 ws:    window scale
        FpElementType::RANGE
        FpElementType::DONT_CARE
+
+Similar to the TCP fingerprints, user-agent based fingerprints loads different types of
+fingerprint patterns from Lua configuration, namely os (operating system), device
+(mobile device information), jail-broken (hacked system), and jail-broken-host
+(host information of the hacked system to confirm matching). During packet processing,
+the rna module depends on the HTTP user agent and host information found by the appid
+module and tries to match all parts of user-agent patterns. A sample configuration
+looks like this:
+{
+    fpid = 1,
+    uuid = "10000000-0000-0000-0000-111111111111",
+    ua_type = "device",
+    user_agent = { { substring = "CPU" }, { substring = "OS 3_0" }, { substring = "My Company" } },
+    device = "My Mobile",
+}
index 0ebd1402ad206172dbae26894309daf93761b91d..0163c872238307c482bbe0b8cea55b1db2d8bbcf 100644 (file)
@@ -27,6 +27,7 @@
 #include "detection/detection_engine.h"
 #include "network_inspectors/appid/appid_session_api.h"
 
+#include "rna_fingerprint_ua.h"
 #include "rna_logger_common.h"
 
 using namespace snort;
@@ -90,6 +91,24 @@ void RnaAppDiscovery::process(AppidEvent* appid_event, DiscoveryFilter& filter,
         appid_session_api.get_service_info(vendor, version, subtype);
         update_service_info(p, proto, vendor, version, ht, src_ip, src_mac, logger);
     }
+
+    if ( p->is_from_client() and ( appid_change_bits[APPID_HOST_BIT] or
+        appid_change_bits[APPID_USERAGENT_BIT] ) )
+    {
+        const AppIdHttpSession* hsession;
+
+        if ( appid_event->get_is_http2() )
+            hsession = appid_session_api.get_http_session(appid_event->get_http2_stream_index());
+        else
+            hsession = appid_session_api.get_http_session();
+
+        if ( hsession )
+        {
+            const char* host = hsession->get_cfield(REQ_HOST_FID);
+            const char* uagent = hsession->get_cfield(REQ_AGENT_FID);
+            analyze_user_agent_fingerprint(p, host, uagent, ht, src_ip, src_mac, logger);
+        }
+    }
 }
 
 void RnaAppDiscovery::discover_service(const Packet* p, IpProtocol proto, RnaTracker& rt,
@@ -166,3 +185,27 @@ void RnaAppDiscovery::discover_client(const Packet* p, RnaTracker& rt,
         logger.log(RNA_EVENT_NEW, NEW_CLIENT_APP, p, &rt, src_ip, src_mac, &hc);
     }
 }
+
+void RnaAppDiscovery::analyze_user_agent_fingerprint(const Packet* p, const char* host,
+    const char* uagent, RnaTracker& rt, const SfIp* ip, const uint8_t* src_mac, RnaLogger& logger)
+{
+    if ( !host or !uagent )
+        return;
+
+    const auto& processor = get_ua_fp_processor();
+    if ( !processor )
+        return;
+
+    const UaFingerprint* uafp = nullptr;
+    const char* device_info = nullptr;
+    bool jail_broken = false;
+    processor->match_mpse(host, uagent, uafp, device_info, jail_broken);
+
+    if ( uafp and rt->add_ua_fingerprint(uafp->fpid, uafp->fp_type, jail_broken,
+        device_info, MAX_USER_AGENT_DEVICES) )
+    {
+        logger.log(RNA_EVENT_NEW, NEW_OS, p, &rt, (const struct in6_addr*)ip->get_ip6_ptr(),
+            src_mac, (FpFingerprint*)uafp);
+    }
+}
+
index 3717b76796ddefdcca6f590a49bd9d963a8a19e0..6e44f28a59268ba846b92e13ec639e886429914b 100644 (file)
@@ -38,6 +38,9 @@ private:
     static void update_service_info(const snort::Packet* p, IpProtocol proto, const char* vendor,
         const char* version, RnaTracker& rt, const snort::SfIp* ip, const uint8_t* src_mac,
         RnaLogger& logger);
+    static void analyze_user_agent_fingerprint(const snort::Packet* p, const char* host,
+        const char* uagent, RnaTracker& rt, const snort::SfIp* ip, const uint8_t* src_mac,
+        RnaLogger& logger);
 };
 
 #endif
index b9626f243d8b195404dfc2abdf5bafe978f5d8d2..781802c1356338202a4dbf593f39142cb7002c88 100644 (file)
@@ -24,6 +24,7 @@
 namespace snort
 {
 class TcpFpProcessor;
+class UaFpProcessor;
 }
 
 struct RnaModuleConfig
@@ -31,7 +32,8 @@ struct RnaModuleConfig
     std::string rna_conf_path;
     bool enable_logger;
     bool log_when_idle;
-    snort::TcpFpProcessor* processor = nullptr;
+    snort::TcpFpProcessor* tcp_processor = nullptr;
+    snort::UaFpProcessor* ua_processor = nullptr;
 };
 
 // Give default values so that RNA can work even if rna_conf_path is not provided
index a0eb1c523062307490c40fe537230012c1466c75..3bb35452f53cf6ad3d42af34de7f2c102e93de36 100644 (file)
@@ -23,6 +23,7 @@
 
 #include <cstdint>
 #include <string>
+#include <vector>
 
 #include "main/snort_types.h"
 
@@ -114,6 +115,14 @@ private:
 
 }
 
+enum UserAgentInfoType
+{
+    OS_INFO,
+    DEVICE_INFO,
+    JAIL_BROKEN_INFO,
+    JAIL_BROKEN_HOST
+};
+
 class RawFingerprint
 {
 public:
@@ -130,6 +139,11 @@ public:
     std::string ws;
     bool df = false;
 
+    UserAgentInfoType ua_type = OS_INFO;
+    std::vector<std::string> user_agent;
+    std::string host_name;
+    std::string device;
+
     void clear()
     {
         fpid = 0;
@@ -141,9 +155,12 @@ public:
         id.clear();
         topts.clear();
         ws.clear();
-        df=false;
+        df = false;
+        ua_type = OS_INFO;
+        user_agent.clear();
+        host_name.clear();
+        device.clear();
     }
-
 };
 
 #endif
diff --git a/src/network_inspectors/rna/rna_fingerprint_ua.cc b/src/network_inspectors/rna/rna_fingerprint_ua.cc
new file mode 100644 (file)
index 0000000..7929c94
--- /dev/null
@@ -0,0 +1,206 @@
+//--------------------------------------------------------------------------
+// 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.
+//--------------------------------------------------------------------------
+
+// rna_fingerprint_ua.cc author Masud Hasan <mashasan@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "rna_fingerprint_ua.h"
+
+#include <algorithm>
+#include <cstring>
+
+#include "main/thread.h"
+
+using namespace snort;
+using namespace std;
+
+static THREAD_LOCAL UaFpProcessor* ua_fp_processor = nullptr;
+
+UaFpProcessor* get_ua_fp_processor()
+{
+    return ua_fp_processor;
+}
+
+void set_ua_fp_processor(UaFpProcessor* processor)
+{
+    ua_fp_processor = processor;
+}
+
+bool UaFingerprint::operator==(const UaFingerprint& y) const
+{
+    return fpid == y.fpid and part_num == y.part_num and total_parts == y.total_parts;
+}
+
+UaFpProcessor::~UaFpProcessor()
+{
+    delete os_mpse;
+    delete device_mpse;
+    delete jb_mpse;
+    delete jb_host_mpse;
+}
+
+void UaFpProcessor::push(const RawFingerprint& rfp)
+{
+    if ( rfp.ua_type == JAIL_BROKEN_HOST )
+    {
+        if ( rfp.host_name.empty() )
+            return;
+        UaFingerprint uafp;
+        uafp.fpid = rfp.fpid;
+        uafp.fpuuid = rfp.fpuuid;
+        uafp.fp_type = FpFingerprint::FpType::FP_TYPE_MOBILE;
+        uafp.host_name = rfp.host_name;
+        uafp.part_num = 0;
+        uafp.total_parts = 1;
+        push_jb_host(uafp);
+    }
+    else
+    {
+        for (size_t i = 0; i < rfp.user_agent.size(); ++i)
+        {
+            UaFingerprint uafp;
+            uafp.fpid = rfp.fpid;
+            uafp.fpuuid = rfp.fpuuid;
+            uafp.user_agent = rfp.user_agent[i];
+            uafp.part_num = i;
+            uafp.total_parts = rfp.user_agent.size();
+            if ( rfp.ua_type == OS_INFO )
+            {
+                uafp.fp_type = FpFingerprint::FpType::FP_TYPE_USERAGENT;
+                push_agent(uafp);
+            }
+            else if ( rfp.ua_type == DEVICE_INFO )
+            {
+                uafp.device = rfp.device;
+                uafp.fp_type = FpFingerprint::FpType::FP_TYPE_MOBILE;
+                push_device(uafp);
+            }
+            else
+            {
+                uafp.fp_type = FpFingerprint::FpType::FP_TYPE_MOBILE;
+                push_jb(uafp);
+            }
+        }
+    }
+}
+
+void UaFpProcessor::make_mpse(SnortConfig* sc)
+{
+    SearchTool::set_conf(sc);
+    if ( !os_fps.empty() )
+    {
+        os_mpse = new SearchTool;
+        for (auto& fp : os_fps)
+            os_mpse->add(fp.user_agent.c_str(), fp.user_agent.size(), &fp);
+        os_mpse->prep();
+    }
+
+    if ( !device_fps.empty() )
+    {
+        device_mpse = new SearchTool;
+        for (auto& fp : device_fps)
+            device_mpse->add(fp.user_agent.c_str(), fp.user_agent.size(), &fp);
+        device_mpse->prep();
+    }
+
+    if ( !jb_fps.empty() )
+    {
+        jb_mpse = new SearchTool;
+        for (auto& fp : jb_fps)
+            jb_mpse->add(fp.user_agent.c_str(), fp.user_agent.size(), &fp);
+        jb_mpse->prep();
+    }
+
+    if ( !jb_host_fps.empty() )
+    {
+        jb_host_mpse = new SearchTool;
+        for (auto& fp : jb_host_fps)
+            jb_host_mpse->add(fp.host_name.c_str(), fp.host_name.size(), &fp);
+        jb_host_mpse->prep();
+    }
+    SearchTool::set_conf(nullptr);
+}
+
+static int match_ua_part(void* id, void*, int, void* data, void*)
+{
+    auto cur_fp = (UaFingerprint*) id;
+    auto matched_parts = (vector<UaFingerprint*>*)data;
+
+    for (const auto& fp : *matched_parts)
+        if ( *fp == *cur_fp )
+            return 0; // ignore already recorded matching part
+
+    matched_parts->emplace_back(cur_fp);
+    return 0; // search continues for the next match
+}
+
+struct CompareParts
+{
+    bool operator()(const UaFingerprint* p1, const UaFingerprint* p2) const
+    {
+        return (p1->fpid < p2->fpid) or (p1->fpid == p2->fpid and p1->part_num < p2->part_num);
+    }
+};
+
+static inline UaFingerprint* search_ua_fp(SearchTool* mpse, const char* start, unsigned len)
+{
+    if ( !mpse )
+        return nullptr;
+
+    vector<UaFingerprint*> matched_parts;
+    mpse->find_all(start, len, match_ua_part, false, (void*)&matched_parts);
+    if ( matched_parts.empty() )
+        return nullptr;
+
+    sort(matched_parts.begin(), matched_parts.end(), CompareParts());
+
+    uint32_t cur_fpid = 0, part_num = 0;
+    for (auto& fp : matched_parts)
+    {
+        if ( cur_fpid != fp->fpid )
+        {
+            cur_fpid = fp->fpid;
+            part_num = 0;
+        }
+
+        if ( part_num == fp->part_num )
+        {
+            if ( ++part_num == fp->total_parts )
+                return fp;
+        }
+    }
+    return nullptr;
+}
+
+void UaFpProcessor::match_mpse(const char* host, const char* uagent, const UaFingerprint*& osfp,
+    const char*& device_info, bool& jail_broken)
+{
+    unsigned len = strlen(uagent);
+    osfp = search_ua_fp(os_mpse, uagent, len);
+
+    auto devicefp = search_ua_fp(device_mpse, uagent, len);
+    if ( devicefp )
+        device_info = devicefp->device.c_str();
+
+    auto jbfp = search_ua_fp(jb_mpse, uagent, len);
+    if ( jbfp and search_ua_fp(jb_host_mpse, host, strlen(host)) )
+        jail_broken = true;
+}
diff --git a/src/network_inspectors/rna/rna_fingerprint_ua.h b/src/network_inspectors/rna/rna_fingerprint_ua.h
new file mode 100644 (file)
index 0000000..b9bafa9
--- /dev/null
@@ -0,0 +1,88 @@
+//--------------------------------------------------------------------------
+// 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.
+//--------------------------------------------------------------------------
+
+// rna_fingerprint_ua.h author Masud Hasan <mashasan@cisco.com>
+
+#ifndef RNA_FINGERPRINT_UA_H
+#define RNA_FINGERPRINT_UA_H
+
+#include "main/snort_config.h"
+#include "main/snort_types.h"
+#include "search_engines/search_tool.h"
+
+#include "rna_fingerprint.h"
+
+#define MAX_USER_AGENT_DEVICES 16
+
+namespace snort
+{
+
+class SO_PUBLIC UaFingerprint : public FpFingerprint
+{
+public:
+    std::string user_agent;
+    std::string host_name;
+    std::string device;
+    uint32_t part_num = 0;
+    uint32_t total_parts = 0;
+
+    bool operator==(const UaFingerprint& y) const;
+};
+
+class SO_PUBLIC UaFpProcessor
+{
+public:
+    ~UaFpProcessor();
+
+    void make_mpse(SnortConfig* sc);
+
+    void match_mpse(const char* host, const char* uagent, const UaFingerprint*& osfp,
+        const char*& device_info, bool& jail_broken);
+
+    void push(const RawFingerprint& rfp);
+
+    void push_agent(const UaFingerprint& uafp)
+    { os_fps.emplace_back(uafp); }
+
+    void push_device(const UaFingerprint& uafp)
+    { device_fps.emplace_back(uafp); }
+
+    void push_jb(const UaFingerprint& uafp)
+    { jb_fps.emplace_back(uafp); }
+
+    void push_jb_host(const UaFingerprint& uafp)
+    { jb_host_fps.emplace_back(uafp); }
+
+private:
+    std::vector<UaFingerprint> os_fps;
+    std::vector<UaFingerprint> device_fps;
+    std::vector<UaFingerprint> jb_fps;
+    std::vector<UaFingerprint> jb_host_fps;
+
+    snort::SearchTool* os_mpse = nullptr;
+    snort::SearchTool* device_mpse = nullptr;
+    snort::SearchTool* jb_mpse = nullptr;
+    snort::SearchTool* jb_host_mpse = nullptr;
+};
+
+} // end of namespace snort
+
+snort::UaFpProcessor* get_ua_fp_processor();
+SO_PUBLIC void set_ua_fp_processor(snort::UaFpProcessor*);
+
+#endif
index a1223824355ad2961fed16c02526b8c4fab73cec..88698318c7460657b7e8a56d9f74129a186b21e9 100644 (file)
@@ -35,6 +35,7 @@
 
 #include "rna_event_handler.h"
 #include "rna_fingerprint_tcp.h"
+#include "rna_fingerprint_ua.h"
 #include "rna_module.h"
 #include "rna_pnd.h"
 
@@ -67,8 +68,11 @@ RnaInspector::~RnaInspector()
     delete pnd;
     delete rna_conf;
     if (mod_conf)
-        delete mod_conf->processor;
-    delete mod_conf;
+    {
+        delete mod_conf->tcp_processor;
+        delete mod_conf->ua_processor;
+        delete mod_conf;
+    }
 }
 
 bool RnaInspector::configure(SnortConfig* sc)
@@ -128,7 +132,8 @@ void RnaInspector::show(const SnortConfig*) const
 void RnaInspector::tinit()
 {
     // thread local initialization
-    set_tcp_fp_processor(mod_conf->processor);
+    set_tcp_fp_processor(mod_conf->tcp_processor);
+    set_ua_fp_processor(mod_conf->ua_processor);
 }
 
 void RnaInspector::tterm()
@@ -193,9 +198,9 @@ TcpFpProcessor* RnaInspector::get_or_create_fp_processor()
 {
     if (mod_conf)
     {
-        if (!mod_conf->processor)
-            mod_conf->processor = new TcpFpProcessor;
-        return mod_conf->processor;
+        if (!mod_conf->tcp_processor)
+            mod_conf->tcp_processor = new TcpFpProcessor;
+        return mod_conf->tcp_processor;
     }
     return nullptr;
 }
@@ -204,8 +209,8 @@ void RnaInspector::set_fp_processor(TcpFpProcessor* tfp)
 {
     if ( mod_conf )
     {
-        delete mod_conf->processor;
-        mod_conf->processor = tfp;
+        delete mod_conf->tcp_processor;
+        mod_conf->tcp_processor = tfp;
     }
 }
 
index a4e7a9c165eaa43f32b5d2d8b77e9f6d818d0c79..da8e94266e500bff87c63f9f7db95eb95a76a947 100644 (file)
@@ -30,7 +30,7 @@
 #include "managers/event_manager.h"
 #include "protocols/packet.h"
 
-#include "rna_fingerprint_tcp.h"
+#include "rna_fingerprint.h"
 #include "rna_logger_common.h"
 #include "rna_module.h"
 
@@ -90,10 +90,10 @@ void RnaLogger::log(uint16_t type, uint16_t subtype, const Packet* p, RnaTracker
 }
 
 void RnaLogger::log(uint16_t type, uint16_t subtype, const Packet* p, RnaTracker* ht,
-    const struct in6_addr* src_ip, const uint8_t* src_mac, const TcpFingerprint* tfp)
+    const struct in6_addr* src_ip, const uint8_t* src_mac, const FpFingerprint* fp)
 {
     log(type, subtype, src_ip, src_mac, ht, p, 0, 0,
-        nullptr, nullptr, tfp, nullptr, nullptr);
+        nullptr, nullptr, fp, nullptr, nullptr);
 }
 
 void RnaLogger::log(uint16_t type, uint16_t subtype, const Packet* p, RnaTracker* ht,
@@ -126,7 +126,7 @@ void RnaLogger::log(uint16_t type, uint16_t subtype, const Packet* p, const uint
 
 bool RnaLogger::log(uint16_t type, uint16_t subtype, const struct in6_addr* src_ip,
     const uint8_t* src_mac, RnaTracker* ht, const Packet* p, uint32_t event_time,
-    uint16_t proto, const HostMac* hm, const HostApplication* ha, const TcpFingerprint* tfp,
+    uint16_t proto, const HostMac* hm, const HostApplication* ha, const FpFingerprint* fp,
     void* cond_var, const HostClient* hc)
 {
     if ( !enabled )
@@ -134,7 +134,7 @@ bool RnaLogger::log(uint16_t type, uint16_t subtype, const struct in6_addr* src_
 
     assert(ht);
 
-    RnaLoggerEvent rle(type, subtype, src_mac, ht, hm, proto, cond_var, ha, tfp, hc);
+    RnaLoggerEvent rle(type, subtype, src_mac, ht, hm, proto, cond_var, ha, fp, hc);
     if ( src_ip and (!IN6_IS_ADDR_V4MAPPED(src_ip) or src_ip->s6_addr32[3]) )
         rle.ip = src_ip;
     else
index 34e9539e48e275b6d73435340828237f0e8545d4..af41ae81066b49f2464b3be8338db9c75fdc7ba4 100644 (file)
@@ -28,7 +28,7 @@ namespace snort
 {
 class Flow;
 struct Packet;
-class TcpFingerprint;
+class FpFingerprint;
 }
 
 using RnaTracker = std::shared_ptr<snort::HostTracker>;
@@ -37,8 +37,8 @@ struct RnaLoggerEvent : public Event
 {
     RnaLoggerEvent (uint16_t t, uint16_t st, const uint8_t* mc, const RnaTracker* rt,
         const snort::HostMac* hmp, uint16_t pr, void* cv, const snort::HostApplication* hap,
-        const snort::TcpFingerprint* tf, const snort::HostClient* hcp) : type(t), subtype(st),
-            mac(mc), ht(rt), hm(hmp), proto(pr), cond_var(cv), ha(hap), tfp(tf), hc(hcp) { }
+        const snort::FpFingerprint* fpr, const snort::HostClient* hcp) : type(t), subtype(st),
+            mac(mc), ht(rt), hm(hmp), proto(pr), cond_var(cv), ha(hap), fp(fpr), hc(hcp) { }
 
     uint16_t type;
     uint16_t subtype;
@@ -49,7 +49,7 @@ struct RnaLoggerEvent : public Event
     uint16_t proto;
     void* cond_var;
     const snort::HostApplication* ha;
-    const snort::TcpFingerprint* tfp;
+    const snort::FpFingerprint* fp;
     const snort::HostClient* hc;
 };
 
@@ -66,9 +66,9 @@ public:
     void log(uint16_t type, uint16_t subtype, const snort::Packet* p, RnaTracker* ht,
         const struct in6_addr* src_ip, const uint8_t* src_mac, const snort::HostClient* hcp);
 
-    // for tcp fingerprint
+    // for fingerprint
     void log(uint16_t type, uint16_t subtype, const snort::Packet* p, RnaTracker* ht,
-        const struct in6_addr* src_ip, const uint8_t* src_mac, const snort::TcpFingerprint* tfp);
+        const struct in6_addr* src_ip, const uint8_t* src_mac, const snort::FpFingerprint* fp);
 
     // for event time
     void log(uint16_t type, uint16_t subtype, const snort::Packet* p, RnaTracker* ht,
@@ -92,7 +92,7 @@ public:
     bool log(uint16_t type, uint16_t subtype, const struct in6_addr* src_ip,
         const uint8_t* src_mac, RnaTracker* ht, const snort::Packet* p = nullptr,
         uint32_t event_time = 0, uint16_t proto = 0, const snort::HostMac* hm = nullptr,
-        const snort::HostApplication* ha = nullptr, const snort::TcpFingerprint* tfp = nullptr,
+        const snort::HostApplication* ha = nullptr, const snort::FpFingerprint* fp = nullptr,
         void* cond_var = nullptr, const snort::HostClient* hc = nullptr);
 
 private:
index a04703998093ce196f32e8fed02c35381a6150da..9e26d9d151d0814af94684c5cb7e9104c0a7ad10 100644 (file)
@@ -37,6 +37,7 @@
 #include "utils/util.h"
 
 #include "rna_fingerprint_tcp.h"
+#include "rna_fingerprint_ua.h"
 #include "rna_mac_cache.h"
 
 #ifdef UNIT_TEST
@@ -81,6 +82,13 @@ static const Command rna_cmds[] =
     { nullptr, nullptr, nullptr, nullptr }
 };
 
+static const Parameter user_agent_parts[] =
+{
+    { "substring", Parameter::PT_STRING, nullptr, nullptr, "a substring of user agent string" },
+
+    { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
+};
+
 static const Parameter rna_fp_params[] =
 {
     { "fpid", Parameter::PT_INT, "0:max32", "0",
@@ -113,6 +121,18 @@ static const Parameter rna_fp_params[] =
     { "df", Parameter::PT_BOOL, nullptr, "false",
       "fingerprint don't fragment flag" },
 
+    { "ua_type", Parameter::PT_ENUM, "os | device | jail-broken | jail-broken-host",
+      "os", "type of user agent fingerprints" },
+
+    { "user_agent", Parameter::PT_LIST, user_agent_parts, nullptr,
+      "list of user agent information parts to match" },
+
+    { "host_name", Parameter::PT_STRING, nullptr, nullptr,
+      "host name information" },
+
+    { "device", Parameter::PT_STRING, nullptr, nullptr,
+      "device information" },
+
     { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
 };
 
@@ -131,7 +151,10 @@ static const Parameter rna_params[] =
       "file name to dump RNA mac cache on shutdown; won't dump by default" },
 
     { "tcp_fingerprints", Parameter::PT_LIST, rna_fp_params, nullptr,
-      "list tcp fingerprints" },
+      "list of tcp fingerprints" },
+
+    { "ua_fingerprints", Parameter::PT_LIST, rna_fp_params, nullptr,
+      "list of user agent fingerprints" },
 
     { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
 };
@@ -182,8 +205,14 @@ bool RnaModule::begin(const char* fqn, int, SnortConfig*)
     if (!strcmp(fqn, "rna.tcp_fingerprints"))
     {
         fingerprint.clear();
-        if (!mod_conf->processor)
-            mod_conf->processor = new TcpFpProcessor;
+        if (!mod_conf->tcp_processor)
+            mod_conf->tcp_processor = new TcpFpProcessor;
+    }
+    else if (!strcmp(fqn, "rna.ua_fingerprints"))
+    {
+        fingerprint.clear();
+        if (!mod_conf->ua_processor)
+            mod_conf->ua_processor = new UaFpProcessor;
     }
 
     return true;
@@ -203,8 +232,8 @@ bool RnaModule::set(const char* fqn, Value& v, SnortConfig*)
             snort_free((void*)dump_file);
         dump_file = snort_strdup(v.get_string());
     }
-
-    else if (fqn && strstr(fqn, "rna.tcp_fingerprints"))
+    else if ( fqn and ( strstr(fqn, "rna.tcp_fingerprints") or
+        strstr(fqn, "rna.ua_fingerprints") ) )
     {
         if (v.is("fpid"))
             fingerprint.fpid = v.get_uint32();
@@ -226,8 +255,24 @@ bool RnaModule::set(const char* fqn, Value& v, SnortConfig*)
             fingerprint.ws = v.get_string();
         else if (v.is("df"))
             fingerprint.df = v.get_uint8();
+        else if (v.is("ua_type"))
+            fingerprint.ua_type = (UserAgentInfoType)v.get_uint8();
+        else if (v.is("host_name"))
+            fingerprint.host_name = v.get_string();
+        else if (v.is("device"))
+            fingerprint.device = v.get_string();
+        else if (v.is("user_agent"))
+            return true;
+        else if (v.is("substring"))
+        {
+            const auto& ua_part = v.get_string();
+            if ( !ua_part )
+                return false;
+            fingerprint.user_agent.emplace_back(ua_part);
+        }
+        else
+            return false;
     }
-
     else
         return false;
 
@@ -249,18 +294,26 @@ bool RnaModule::end(const char* fqn, int index, SnortConfig* sc)
             sc->clear_run_flags(RUN_FLAG__IP_FRAGS_ONLY);
         }
 
-        if (mod_conf->processor)
+        if ( mod_conf->tcp_processor )
         {
-            mod_conf->processor->make_tcp_fp_tables(TcpFpProcessor::TCP_FP_MODE::SERVER);
-            mod_conf->processor->make_tcp_fp_tables(TcpFpProcessor::TCP_FP_MODE::CLIENT);
+            mod_conf->tcp_processor->make_tcp_fp_tables(TcpFpProcessor::TCP_FP_MODE::SERVER);
+            mod_conf->tcp_processor->make_tcp_fp_tables(TcpFpProcessor::TCP_FP_MODE::CLIENT);
         }
+
+        if ( mod_conf->ua_processor )
+            mod_conf->ua_processor->make_mpse(sc);
     }
 
-    if ( index > 0 && mod_conf->processor && !strcmp(fqn, "rna.tcp_fingerprints") )
+    if ( index > 0 and mod_conf->tcp_processor and !strcmp(fqn, "rna.tcp_fingerprints") )
     {
         // there is an implicit conversion here from raw fingerprint (all
         // strings) to tcp fingerprint, done by the tcp fingerprint constructor
-        mod_conf->processor->push(fingerprint);
+        mod_conf->tcp_processor->push(fingerprint);
+        fingerprint.clear();
+    }
+    else if ( index > 0 and mod_conf->ua_processor and !strcmp(fqn, "rna.ua_fingerprints") )
+    {
+        mod_conf->ua_processor->push(fingerprint);
         fingerprint.clear();
     }
 
@@ -337,7 +390,8 @@ bool RnaModule::log_mac_cache(const char* outfile)
 
 bool RnaModule::is_valid_fqn(const char* fqn) const
 {
-    return !strcmp(fqn, RNA_NAME) || !strcmp(fqn, "rna.tcp_fingerprints");
+    return !strcmp(fqn, RNA_NAME) or !strcmp(fqn, "rna.tcp_fingerprints") or
+        !strcmp(fqn, "rna.ua_fingerprints") or !strcmp(fqn, "rna.ua_fingerprints.user_agent");
 }
 
 
index b983ee230f5d3b8cf31338c1ddcdc12db4d900b2..d32d8dba149a894df958e546fb9c32cc1d931b38 100644 (file)
@@ -63,6 +63,10 @@ void set_tcp_fp_processor(TcpFpProcessor*) { }
 TcpFingerprint::TcpFingerprint(const RawFingerprint&) { }
 bool TcpFingerprint::operator==(const TcpFingerprint&) const { return true; }
 
+UaFpProcessor::~UaFpProcessor() { }
+void UaFpProcessor::make_mpse(SnortConfig*) { }
+void UaFpProcessor::push(RawFingerprint const&) { }
+
 // inspector
 class RnaInspector
 {
@@ -79,15 +83,15 @@ RnaInspector(RnaModule* mod)
 {
     if (mod_conf)
     {
-       if (mod_conf->processor)
-           delete mod_conf->processor;
-       delete mod_conf;
-    }
+           delete mod_conf->tcp_processor;
+           delete mod_conf->ua_processor;
+           delete mod_conf;
+       }
 }
 
 TcpFpProcessor* get_fp_processor()
 {
-    return mod_conf->processor;
+    return mod_conf->tcp_processor;
 }
 
 private: