]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Pull request #4623: appid: added flag to enable inspection of ooo packets
authorOleksandr Stepanov -X (ostepano - SOFTSERVE INC at Cisco) <ostepano@cisco.com>
Fri, 14 Mar 2025 17:47:47 +0000 (17:47 +0000)
committerChris Sherwin (chsherwi) <chsherwi@cisco.com>
Fri, 14 Mar 2025 17:47:47 +0000 (17:47 +0000)
Merge in SNORT/snort3 from ~OSTEPANO/snort3:ssl_ooo_ch to master

Squashed commit of the following:

commit ec43974fa2a3ddc6acf1716f6c1bec0fb5dad657
Author: Oleksandr Stepanov <ostepano@cisco.com>
Date:   Mon Feb 17 07:34:48 2025 -0500

    appid: added flag to inspect ooo packets

src/network_inspectors/appid/app_info_table.cc
src/network_inspectors/appid/appid_config.cc
src/network_inspectors/appid/appid_config.h
src/network_inspectors/appid/appid_discovery.cc
src/network_inspectors/appid/service_plugins/service_ssl.cc
src/protocols/ssl.cc
src/protocols/ssl.h

index 81390a3c9897a3349d0361f2483ac5543163f1e8..8a8cfeaa99371210968bf8801d064c6bc04f64c2 100644 (file)
@@ -616,6 +616,14 @@ void AppInfoManager::load_odp_config(OdpContext& odp_ctxt, const char* path)
                     odp_ctxt.brute_force_inprocess_threshold = brute_force_inprocess_threshold;
                 }
             }
+            else if (!(strcasecmp(conf_key, "inspect_ooo_flows")))
+            {
+                if (!(strcasecmp(conf_val, "enabled")))
+                {
+                    odp_ctxt.inspect_ooo_flows = true;
+                    continue;
+                }
+            }
             /* App Priority bit set*/
             else if (!(strcasecmp(conf_key, "app_priority")))
             {
index 43d22929de10355e8590a7dd7875a584d3a24e45..20b6a5d42b635477e850e9e2e928a0cef842b657 100644 (file)
@@ -226,6 +226,7 @@ void OdpContext::dump_appid_config()
     APPID_LOG(nullptr, TRACE_INFO_LEVEL, "Appid Config: appid_cpu_profiler                   %s\n", (appid_cpu_profiler ? "True" : "False"));
     APPID_LOG(nullptr, TRACE_INFO_LEVEL, "Appid Config: brute_force_inprocess_threshold      %" PRId8" \n", brute_force_inprocess_threshold);
     APPID_LOG(nullptr, TRACE_INFO_LEVEL, "Appid Config: failed_state_expiration_secs         %" PRId32" \n", failed_state_expiration_secs);
+    APPID_LOG(nullptr, TRACE_INFO_LEVEL, "Appid Config: inspect_ooo_flows                    %s\n", inspect_ooo_flows ? "True" : "False");
 }
 
 bool OdpContext::is_appid_cpu_profiler_running()
index f121a99a98f49033c3b18e759b2ccdfca6aba129..9468fd12c819cf622df06f6d2ddcb9ff6fad3964 100644 (file)
@@ -146,6 +146,7 @@ public:
     bool recheck_for_portservice_appid = false;
     bool eve_http_client = true;
     bool appid_cpu_profiler = true;
+    bool inspect_ooo_flows = false;
     uint8_t brute_force_inprocess_threshold = DEFAULT_BRUTE_FORCE_INPROCESS_STATE_THRESHOLD;
     uint16_t max_packet_before_service_fail = DEFAULT_MAX_PKTS_BEFORE_SERVICE_FAIL;
     uint16_t max_packet_service_fail_ignore_bytes = DEFAULT_MAX_PKT_BEFORE_SERVICE_FAIL_IGNORE_BYTES;
@@ -160,6 +161,7 @@ public:
     uint32_t host_port_app_cache_lookup_range = 100000;
     uint64_t max_bytes_before_service_fail = DEFAULT_MAX_BYTES_BEFORE_SERVICE_FAIL;
     FirstPktAppIdDiscovered first_pkt_appid_prefix = NO_APPID_FOUND;
+    
 
     OdpContext(const AppIdConfig&, snort::SnortConfig*);
     void initialize(AppIdInspector& inspector);
index 436832a6bde69b3c418f50e6c823167d720e96b5..e9e147e1e57d48518b8dc0c3ea4e983f9e3f000a 100644 (file)
@@ -346,8 +346,8 @@ bool AppIdDiscovery::do_pre_discovery(Packet* p, AppIdSession*& asd, AppIdInspec
 
             // Shut off service/client discoveries, since they skip not-ok data packets and
             // may keep failing on subsequent data packets causing performance degradation
-            if (!asd->get_session_flags(APPID_SESSION_MID) or
-                (p->ptrs.sp != 21 and p->ptrs.dp != 21)) // exception for ftp-control
+            if ((!asd->get_session_flags(APPID_SESSION_MID) or
+                (p->ptrs.sp != 21 and p->ptrs.dp != 21)) and !odp_ctxt.inspect_ooo_flows) // exception for ftp-control
             {
                 asd->service_disco_state = APPID_DISCO_STATE_FINISHED;
                 if (asd->get_payload_id() == APP_ID_NONE and
@@ -772,7 +772,7 @@ bool AppIdDiscovery::do_discovery(Packet* p, AppIdSession& asd, IpProtocol proto
         }
     }
     // FIXIT-M - snort 2.x has added a check for midstream pickup to this, do we need that?
-    else if (protocol != IpProtocol::TCP or (p->packet_flags & PKT_STREAM_ORDER_OK))
+    else if (protocol != IpProtocol::TCP or ((p->packet_flags & PKT_STREAM_ORDER_OK) || asd.get_session_flags(APPID_SESSION_OOO)))
     {
         if (asd.service_disco_state != APPID_DISCO_STATE_FINISHED)
             is_discovery_done =
index 266c29116090196754ed2439fb963430da9c9793..1ca43a334eefceffde11639abc50496bb24ea998 100644 (file)
@@ -68,6 +68,7 @@ public:
     int certs_curr_len = 0;   // Current amount of collected certificate data.
     uint8_t* cached_data = nullptr;
     uint16_t cached_len = 0;
+    bool cached_client_data = false;
 };
 
 #pragma pack(1)
@@ -194,25 +195,25 @@ ServiceSSLData::~ServiceSSLData()
     ssl_cache_free(cached_data, cached_len);
 }
 
-static void parse_client_initiation(const uint8_t* data, uint16_t size, ServiceSSLData* ss)
+static ParseCHResult parse_client_initiation(const uint8_t* data, uint16_t size, ServiceSSLData* ss)
 {
     const ServiceSSLV3Hdr* hdr3;
     uint16_t ver;
 
     /* Sanity check header stuff. */
     if (size < sizeof(ServiceSSLV3Hdr))
-        return;
+        return ParseCHResult::FAILED;
     hdr3 = (const ServiceSSLV3Hdr*)data;
     ver = ntohs(hdr3->version);
     if (hdr3->type != SSL_HANDSHAKE || (ver != 0x0300 && ver != 0x0301 && ver != 0x0302 &&
         ver != 0x0303))
     {
-        return;
+        return ParseCHResult::FAILED;
     }
     data += sizeof(ServiceSSLV3Hdr);
     size -= sizeof(ServiceSSLV3Hdr);
 
-    parse_client_hello_data(data, size, &ss->client_hello);
+    return parse_client_hello_data(data, size, &ss->client_hello);
 }
 
 static void save_ssl_cache(ServiceSSLData* ss, uint16_t size, const uint8_t* data)
@@ -247,14 +248,52 @@ int SslServiceDetector::validate(AppIdDiscoveryArgs& args)
     uint16_t size = args.size;
     uint8_t* reallocated_data = nullptr;
 
-    if (ss->cached_len and ss->cached_data and (args.dir == APP_ID_FROM_RESPONDER))
+    if (!size)
+        goto inprocess;
+
+    ss = (ServiceSSLData*)data_get(args.asd);
+    if (!ss)
+    {
+        ss = (ServiceSSLData*)snort_calloc(sizeof(ServiceSSLData));
+        data_add(args.asd, ss);
+        ss->state = SSL_STATE_INITIATE;
+        ss->cached_data = nullptr;
+        ss->cached_len = 0;
+    }
+
+    if ( args.asd.get_session_flags(APPID_SESSION_OOO)
+         and ss->state == SSL_STATE_INITIATE
+         and args.dir == APP_ID_FROM_INITIATOR
+         and !(args.asd.scan_flags & SCAN_CERTVIZ_ENABLED_FLAG))
+    {
+        if (ss->cached_data)
+        {
+            reallocated_data = (uint8_t*)snort_calloc(ss->cached_len + size, sizeof(uint8_t));
+            memcpy(reallocated_data, args.data, args.size);
+            memcpy(reallocated_data + args.size, ss->cached_data, ss->cached_len);
+            size = ss->cached_len + args.size;
+            ssl_cache_free(ss->cached_data, ss->cached_len);
+            data = reallocated_data;
+        }
+        else
+        {
+            save_ssl_cache(ss, size, data);
+            ss->cached_client_data = true;
+            goto inprocess;
+        }
+    }
+
+    if (ss->cached_len and ss->cached_data)
     {
-        reallocated_data = (uint8_t*)snort_calloc(ss->cached_len + size, sizeof(uint8_t));
-        memcpy(reallocated_data, ss->cached_data, ss->cached_len);
-        memcpy(reallocated_data + ss->cached_len, args.data, args.size);
-        size = ss->cached_len + args.size;
-        ssl_cache_free(ss->cached_data, ss->cached_len);
-        data = reallocated_data;
+        if ( (ss->cached_client_data and (args.dir == APP_ID_FROM_INITIATOR)) or (!ss->cached_client_data and (args.dir == APP_ID_FROM_RESPONDER)) )
+        {
+            reallocated_data = (uint8_t*)snort_calloc(ss->cached_len + size, sizeof(uint8_t));
+            memcpy(reallocated_data, ss->cached_data, ss->cached_len);
+            memcpy(reallocated_data + ss->cached_len, args.data, args.size);
+            size = ss->cached_len + args.size;
+            ssl_cache_free(ss->cached_data, ss->cached_len);
+            data = reallocated_data;
+        }
     }
     /* Start off with a Client Hello from client to server. */
     if (ss->state == SSL_STATE_INITIATE)
@@ -264,8 +303,18 @@ int SslServiceDetector::validate(AppIdDiscoveryArgs& args)
         if (!(args.asd.scan_flags & SCAN_CERTVIZ_ENABLED_FLAG) and
             args.dir == APP_ID_FROM_INITIATOR)
         {
-            parse_client_initiation(data, size, ss);
-            goto inprocess;
+            auto parse_status = parse_client_initiation(data, size, ss);
+            if (parse_status == ParseCHResult::FRAGMENTED_PACKET)
+            {
+                save_ssl_cache(ss, size, data);
+                ss->cached_client_data = true;
+                ss->state = SSL_STATE_INITIATE;
+                goto inprocess;
+            }
+            else if (parse_status == ParseCHResult::FAILED)
+            {
+                goto inprocess;
+            }
         }
     }
 
@@ -343,6 +392,7 @@ int SslServiceDetector::validate(AppIdDiscoveryArgs& args)
                     if (size < sizeof(ServiceSSLV3Hdr))
                     {
                         save_ssl_cache(ss, size, data);
+                        ss->cached_client_data = false;
                         goto inprocess;
                     }
 
@@ -368,6 +418,7 @@ int SslServiceDetector::validate(AppIdDiscoveryArgs& args)
                 if (size < offsetof(ServiceSSLV3Record, version))
                 {
                     save_ssl_cache(ss, size, data);
+                    ss->cached_client_data = false;
                     goto inprocess;
                 }
 
@@ -498,12 +549,6 @@ fail:
     return APPID_NOMATCH;
 
 success:
-    if (reallocated_data)
-    {
-        snort_free(reallocated_data);
-        reallocated_data = nullptr;
-    }
-
     if (ss->server_cert.certs_data && ss->server_cert.certs_len)
     {
         if (!(args.asd.scan_flags & SCAN_CERTVIZ_ENABLED_FLAG) and
@@ -513,6 +558,12 @@ success:
         }
     }
 
+    if (reallocated_data)
+    {
+        snort_free(reallocated_data);
+        reallocated_data = nullptr;
+    }
+
     args.asd.set_session_flags(APPID_SESSION_SSL_SESSION);
     if (ss->client_hello.host_name || ss->server_cert.common_name || ss->server_cert.org_unit)
     {
@@ -613,3 +664,4 @@ bool is_service_over_ssl(AppId appId)
 
     return false;
 }
+
index fe6babea65ab7981801f737c1004afda50eabe85..c146c8428e69dec36229494a376b520c8226e51a 100644 (file)
@@ -612,61 +612,61 @@ bool IsSSL(const uint8_t* ptr, int len, int pkt_flags)
     return false;
 }
 
-void parse_client_hello_data(const uint8_t* pkt, uint16_t size, SSLV3ClientHelloData* client_hello_data)
+ParseCHResult parse_client_hello_data(const uint8_t* pkt, uint16_t size, SSLV3ClientHelloData* client_hello_data)
 {
     if (client_hello_data == nullptr)
-        return;
+        return ParseCHResult::FAILED;
 
     if (size < sizeof(ServiceSSLV3Record))
-        return;
+        return ParseCHResult::FAILED;
     const ServiceSSLV3Record* rec = (const ServiceSSLV3Record*)pkt;
     uint16_t ver = ntohs(rec->version);
     if (rec->type != SSLV3RecordType::CLIENT_HELLO || (ver != 0x0300 && ver != 0x0301 && ver != 0x0302 &&
         ver != 0x0303) || rec->length_msb)
     {
-        return;
+        return ParseCHResult::FAILED;
     }
     unsigned length = ntohs(rec->length) + offsetof(ServiceSSLV3Record, version);
     if (size < length)
-        return;
+        return ParseCHResult::FRAGMENTED_PACKET;
     pkt += sizeof(ServiceSSLV3Record);
     size -= sizeof(ServiceSSLV3Record);
 
     /* Session ID (1-byte length). */
     if (size < 1)
-        return;
+        return ParseCHResult::FAILED;
     length = *((const uint8_t*)pkt);
     pkt += length + 1;
     if (size < (length + 1))
-        return;
+        return ParseCHResult::FAILED;
     size -= length + 1;
 
     /* Cipher Suites (2-byte length). */
     if (size < 2)
-        return;
+        return ParseCHResult::FAILED;
     length = ntohs(*((const uint16_t*)pkt));
     pkt += length + 2;
     if (size < (length + 2))
-        return;
+        return ParseCHResult::FAILED;
     size -= length + 2;
 
     /* Compression Methods (1-byte length). */
     if (size < 1)
-        return;
+        return ParseCHResult::FAILED;
     length = *((const uint8_t*)pkt);
     pkt += length + 1;
     if (size < (length + 1))
-        return;
+        return ParseCHResult::FAILED;
     size -= length + 1;
 
     /* Extensions (2-byte length) */
     if (size < 2)
-        return;
+        return ParseCHResult::FAILED;
     length = ntohs(*((const uint16_t*)pkt));
     pkt += 2;
     size -= 2;
     if (size < length)
-        return;
+        return ParseCHResult::FAILED;
 
     /* We need at least type (2 bytes) and length (2 bytes) in the extension. */
     while (length >= 4)
@@ -676,25 +676,27 @@ void parse_client_hello_data(const uint8_t* pkt, uint16_t size, SSLV3ClientHello
         {
             /* Found server host name. */
             if (length < sizeof(ServiceSSLV3ExtensionServerName))
-                return;
+                return ParseCHResult::FAILED;
 
             unsigned len = ntohs(ext->string_length);
             if ((length - sizeof(ServiceSSLV3ExtensionServerName)) < len)
-                return;
+                return ParseCHResult::FAILED;
 
             const uint8_t* str = pkt + offsetof(ServiceSSLV3ExtensionServerName, string_length) +
                 sizeof(ext->string_length);
             client_hello_data->host_name = snort_strndup((const char*)str, len);
-            return;
+            return ParseCHResult::SUCCESS;
         }
 
         unsigned len = ntohs(ext->length) + offsetof(ServiceSSLV3ExtensionServerName, list_length);
         if (len > length)
-            return;
+            return ParseCHResult::FAILED;
 
         pkt += len;
         length -= len;
     }
+    
+    return ParseCHResult::FAILED;
 }
 
 bool parse_server_certificates(SSLV3ServerCertData* server_cert_data)
index c127e54969b3697d41cb552c8abacfa7ee35e0c2..67340dc7276a1c289a6979e907811e6b28f75002 100644 (file)
@@ -306,12 +306,20 @@ struct ServiceSSLV3ExtensionServerName
 
 namespace snort
 {
+
+enum class ParseCHResult
+{
+    SUCCESS = 0,
+    FRAGMENTED_PACKET = 1,
+    FAILED = 2
+};
+
 SO_PUBLIC uint32_t SSL_decode(
     const uint8_t* pkt, int size, uint32_t pktflags, uint32_t prevflags,
     uint8_t* alert_flags, uint16_t* partial_rec_len, int hblen, uint32_t* info_flags = nullptr,
     SSLV3ClientHelloData* data = nullptr, SSLV3ServerCertData* server_cert_data = nullptr);
 
-    void parse_client_hello_data(const uint8_t* pkt, uint16_t size, SSLV3ClientHelloData*);
+    ParseCHResult parse_client_hello_data(const uint8_t* pkt, uint16_t size, SSLV3ClientHelloData*);
     bool parse_server_certificates(SSLV3ServerCertData* server_cert_data);
 
 SO_PUBLIC bool IsTlsClientHello(const uint8_t* ptr, const uint8_t* end);