]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Pull request #4933: ssl: SSL extractor event
authorOleksandr Stepanov -X (ostepano - SOFTSERVE INC at Cisco) <ostepano@cisco.com>
Thu, 30 Oct 2025 18:18:25 +0000 (18:18 +0000)
committerChris Sherwin (chsherwi) <chsherwi@cisco.com>
Thu, 30 Oct 2025 18:18:25 +0000 (18:18 +0000)
Merge in SNORT/snort3 from ~OSTEPANO/snort3:ssl_metadata_extractor to master

Squashed commit of the following:

commit 45a8012221075eb0d84589631d543b9151d25c95
Author: Oleksandr Stepanov <ostepano@cisco.com>
Date:   Thu Sep 11 04:42:20 2025 -0400

    ssl: SSL extractor event

16 files changed:
doc/user/extractor.txt
src/network_inspectors/appid/service_plugins/service_ssl.cc
src/network_inspectors/extractor/CMakeLists.txt
src/network_inspectors/extractor/extractor.cc
src/network_inspectors/extractor/extractor_enums.h
src/network_inspectors/extractor/extractor_service.cc
src/network_inspectors/extractor/extractor_service.h
src/network_inspectors/extractor/extractor_ssl.cc [new file with mode: 0644]
src/network_inspectors/extractor/extractor_ssl.h [new file with mode: 0644]
src/protocols/ssl.cc
src/protocols/ssl.h
src/protocols/test/ssl_protocol_test.cc
src/pub_sub/ssl_events.h
src/service_inspectors/ssl/ssl_flow_data.h
src/service_inspectors/ssl/ssl_inspector.cc
src/service_inspectors/ssl/ssl_inspector.h

index 0e5db0529c9853ca68382388faa73cd7692fe15c..7c1b73904bae57f7c83ee426480e6f85f1c7023b 100644 (file)
@@ -59,6 +59,8 @@ Services and their events:
   ** `request`
   ** `response`
   ** `eot` (a session defined by the following commands: APPE, DELE, RETR, STOR, STOU, ACCT, PORT, PASV, EPRT, EPSV)
+* SSL
+  ** `tls_metadata_event`
 * DNS
   ** `response`
 * connection (conn)
@@ -115,6 +117,17 @@ Fields supported for FTP:
 * `data_channel.resp_h` - IP address of data channel receiving point
 * `data_channel.resp_p` - TCP port of data channel receiving point
 
+Fields supported for SSL:
+
+* `version` - SSL/TLS version that the server chose
+* `server_name_identifier` - Server Name Identifier ( SNI ) extracted from Client Hello
+* `validation_status` - result of certificate validation
+* `subject` - RFC2253 formatted certificate subject information
+* `issuer` - RFC2253 formatted certificate issuer information
+* `module_identifier` - name of snort module that populated the event
+* `cipher` - SSL/TLS cipher suite that the server chose
+* `curve` - named elliptic curve the server chose
+
 Fields supported for DNS:
 
 * `proto` - transport protocol for DNS connection
index a8dc6ee5cf2b86231d4f61f6e5ebfb375c3c304d..41e5705e843dc2afb5dddcbba7105f5f8c1fca1f 100644 (file)
@@ -195,20 +195,20 @@ ServiceSSLData::~ServiceSSLData()
     ssl_cache_free(cached_data, cached_len);
 }
 
-static ParseCHResult parse_client_initiation(const uint8_t* data, uint16_t size, ServiceSSLData* ss)
+static ParseHelloResult 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 ParseCHResult::FAILED;
+        return ParseHelloResult::FAILURE;
     hdr3 = (const ServiceSSLV3Hdr*)data;
     ver = ntohs(hdr3->version);
     if (hdr3->type != SSL_HANDSHAKE || (ver != 0x0300 && ver != 0x0301 && ver != 0x0302 &&
         ver != 0x0303))
     {
-        return ParseCHResult::FAILED;
+        return ParseHelloResult::FAILURE;
     }
     data += sizeof(ServiceSSLV3Hdr);
     size -= sizeof(ServiceSSLV3Hdr);
@@ -308,14 +308,14 @@ int SslServiceDetector::validate(AppIdDiscoveryArgs& args)
             args.dir == APP_ID_FROM_INITIATOR)
         {
             auto parse_status = parse_client_initiation(data, size, ss);
-            if (parse_status == ParseCHResult::FRAGMENTED_PACKET)
+            if (parse_status == ParseHelloResult::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)
+            else if (parse_status == ParseHelloResult::FAILURE)
             {
                 goto inprocess;
             }
index 46f8dc36b5611e8f448dc4d8fe6f86749b24645f..51e4835a5eb5000c3d0e11fefc43251a768f013d 100644 (file)
@@ -27,6 +27,8 @@ set( FILE_LIST
     extractor_null_conn.h
     extractor_service.cc
     extractor_service.h
+    extractor_ssl.cc
+    extractor_ssl.h
     extractors.cc
     extractors.h
 )
index 15ab87359fb3e2e37a37e2790661da1160f5391d..adde8964a8cbddc8fb7ce76644bac5b3d80f4080 100644 (file)
@@ -50,7 +50,7 @@ THREAD_LOCAL ExtractorLogger* Extractor::logger = nullptr;
 
 static const Parameter extractor_proto_params[] =
 {
-    { "service", Parameter::PT_ENUM, "http | ftp | conn | dns | weird | notice", nullptr,
+    { "service", Parameter::PT_ENUM, "http | ftp | ssl | conn | dns | weird | notice", nullptr,
       "service to extract from" },
 
     { "tenant_id", Parameter::PT_INT, "0:max32", "0",
index 22961a23b728b66680efa00910e5440a75fd71d5..3e586b65285ebcf6fe55edc3680f0e7eed8b46a1 100644 (file)
@@ -29,6 +29,7 @@ public:
     {
         HTTP,
         FTP,
+        SSL,
         CONN,
         DNS,
         IPS_BUILTIN,
@@ -52,6 +53,8 @@ public:
             return "http";
         case FTP:
             return "ftp";
+        case SSL:
+            return "ssl";
         case CONN:
             return "conn";
         case DNS:
index e153f3d5dd8612aff5ad1e2d1eb4751b04bcc009..2b4495792b111ea28cd67b78aeae765f3b1d6b94 100644 (file)
@@ -31,6 +31,7 @@
 #include "extractor_dns.h"
 #include "extractor_ftp.h"
 #include "extractor_http.h"
+#include "extractor_ssl.h"
 
 using namespace snort;
 
@@ -122,6 +123,10 @@ ExtractorService* ExtractorService::make_service(Extractor& ins, const ServiceCo
         srv = new FtpExtractorService(cfg.tenant_id, cfg.fields, cfg.on_events, cfg.service, ins);
         break;
 
+    case ServiceType::SSL:
+        srv = new SslExtractorService(cfg.tenant_id, cfg.fields, cfg.on_events, cfg.service, ins);
+        break;
+
     case ServiceType::CONN:
         srv = new ConnExtractorService(cfg.tenant_id, cfg.fields, cfg.on_events, cfg.service, ins);
         break;
@@ -225,6 +230,11 @@ void ExtractorService::validate(const ServiceConfig& cfg)
         validate_fields(FtpExtractorService::blueprint, cfg.fields);
         break;
 
+    case ServiceType::SSL:
+        validate_events(SslExtractorService::blueprint, cfg.on_events);
+        validate_fields(SslExtractorService::blueprint, cfg.fields);
+        break;
+
     case ServiceType::CONN:
         validate_events(ConnExtractorService::blueprint, cfg.on_events);
         validate_fields(ConnExtractorService::blueprint, cfg.fields);
@@ -354,6 +364,48 @@ const snort::Connector::ID& FtpExtractorService::internal_tinit()
 const snort::Connector::ID& FtpExtractorService::get_log_id()
 { return log_id; }
 
+//-------------------------------------------------------------------------
+//  SslExtractorService
+//-------------------------------------------------------------------------
+
+const ServiceBlueprint SslExtractorService::blueprint =
+{
+    // events
+    {
+        "tls_metadata_event",
+    },
+    // fields
+    {
+        "version",
+        "server_name_identifier",
+        "curve",
+        "cipher",
+        "subject",
+        "issuer",
+        "validation_status",
+        "module_identifier"
+    },
+};
+
+THREAD_LOCAL Connector::ID SslExtractorService::log_id;
+
+SslExtractorService::SslExtractorService(uint32_t tenant, const std::vector<std::string>& srv_fields,
+    const std::vector<std::string>& srv_events, ServiceType s_type, Extractor& ins)
+    : ExtractorService(tenant, srv_fields, srv_events, blueprint, s_type, ins)
+{
+    for (const auto& event : get_events())
+    {
+        if (!strcmp("tls_metadata_event", event.c_str()))
+            handlers.push_back(new SslExtractor(ins, tenant_id, get_fields()));
+    }
+}
+
+const snort::Connector::ID& SslExtractorService::internal_tinit()
+{ return log_id = logger->get_id(type.c_str()); }
+
+const snort::Connector::ID& SslExtractorService::get_log_id()
+{ return log_id; }
+
 //-------------------------------------------------------------------------
 //  ConnExtractorService
 //-------------------------------------------------------------------------
@@ -549,6 +601,7 @@ TEST_CASE("Service Type", "[extractor]")
     {
         ServiceType http = ServiceType::HTTP;
         ServiceType ftp = ServiceType::FTP;
+        ServiceType ssl = ServiceType::SSL;
         ServiceType conn = ServiceType::CONN;
         ServiceType dns = ServiceType::DNS;
         ServiceType weird = ServiceType::IPS_BUILTIN;
@@ -558,6 +611,7 @@ TEST_CASE("Service Type", "[extractor]")
 
         CHECK_FALSE(strcmp("http", http.c_str()));
         CHECK_FALSE(strcmp("ftp", ftp.c_str()));
+        CHECK_FALSE(strcmp("ssl", ssl.c_str()));
         CHECK_FALSE(strcmp("conn", conn.c_str()));
         CHECK_FALSE(strcmp("dns", dns.c_str()));
         CHECK_FALSE(strcmp("weird", weird.c_str()));
index e93164a7e4333ff367749aed1a7a81fd91b89ec4..de9d046eec57ca7286aba6f644f907f3ea1501e7 100644 (file)
@@ -122,6 +122,21 @@ private:
     static THREAD_LOCAL snort::Connector::ID log_id;
 };
 
+class SslExtractorService : public ExtractorService
+{
+public:
+    static const ServiceBlueprint blueprint;
+
+    SslExtractorService(uint32_t tenant, const std::vector<std::string>& fields,
+        const std::vector<std::string>& events, ServiceType, Extractor&);
+
+private:
+    const snort::Connector::ID& internal_tinit() override;
+    const snort::Connector::ID& get_log_id() override;
+
+    static THREAD_LOCAL snort::Connector::ID log_id;
+};
+
 class ConnExtractorService : public ExtractorService
 {
 public:
diff --git a/src/network_inspectors/extractor/extractor_ssl.cc b/src/network_inspectors/extractor/extractor_ssl.cc
new file mode 100644 (file)
index 0000000..7d8bb05
--- /dev/null
@@ -0,0 +1,624 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2025-2025 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.
+//--------------------------------------------------------------------------
+// extractor_ssl.cc author Oleksandr Stepanov <ostepano@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "extractor_ssl.h"
+
+#include <sys/time.h>
+
+#include "detection/detection_engine.h"
+#include "flow/flow_key.h"
+#include "profiler/profiler.h"
+#include "pub_sub/ssl_events.h"
+#include "protocols/ssl.h"
+#include "sfip/sf_ip.h"
+#include "utils/util.h"
+#include "utils/util_net.h"
+
+#include "extractor.h"
+#include "extractor_enums.h"
+
+using namespace snort;
+using namespace std;
+
+static const char* sslv_2_string = "SSL 2";
+static const char* sslv_3_string = "SSL 3";
+static const char* tls_1_0_string = "TLS 1.0";
+static const char* tls_1_1_string = "TLS 1.1";
+static const char* tls_1_2_string = "TLS 1.2";
+static const char* tls_1_3_string = "TLS 1.3";
+
+static const char* get_ssl_string_version(uint32_t version)
+{
+    switch (version)
+    {
+    case 0x0002:
+        return sslv_2_string;
+    case 0x0300:
+        return sslv_3_string;
+    case 0x0301:
+        return tls_1_0_string;
+    case 0x0302:
+        return tls_1_1_string;
+    case 0x0303:
+        return tls_1_2_string;
+    case 0x0304:
+        return tls_1_3_string;
+    default:
+    {
+        static THREAD_LOCAL char unknown_version[32];
+        memset(unknown_version, 0, sizeof(unknown_version));
+        snprintf(unknown_version, sizeof(unknown_version), "Unknown version (0x%04x)", version);
+        return unknown_version;
+    }
+    }
+}
+
+const std::unordered_map<uint16_t, const char*> get_ssl_string_curve_map =
+{
+    {0x0001, "sect163k1"},
+    {0x0002, "sect163r1"},
+    {0x0003, "sect163r2"},
+    {0x0004, "sect193r1"},
+    {0x0005, "sect193r2"},
+    {0x0006, "sect233k1"},
+    {0x0007, "sect233r1"},
+    {0x0008, "sect239k1"},
+    {0x0009, "sect283k1"},
+    {0x000A, "sect283r1"},
+    {0x000B, "sect409k1"},
+    {0x000C, "sect409r1"},
+    {0x000D, "sect571k1"},
+    {0x000E, "sect571r1"},
+    {0x000F, "secp160k1"},
+    {0x0010, "secp160r1"},
+    {0x0011, "secp160r2"},
+    {0x0012, "secp192k1"},
+    {0x0013, "secp192r1"},
+    {0x0014, "secp224k1"},
+    {0x0015, "secp224r1"},
+    {0x0016, "secp256k1"},
+    {0x0017, "secp256r1"},
+    {0x0018, "secp384r1"},
+    {0x0019, "secp521r1"},
+    {0x001A, "brainpoolP256r1"},
+    {0x001B, "brainpoolP384r1"},
+    {0x001C, "brainpoolP512r1"},
+    {0x001D, "x25519"},
+    {0x001E, "x448"},
+    {0x001F, "brainpoolP256r1tls13"},
+    {0x0020, "brainpoolP384r1tls13"},
+    {0x0021, "brainpoolP512r1tls13"},
+    {0x0022, "GC256A"},
+    {0x0023, "GC256B"},
+    {0x0024, "GC256C"},
+    {0x0025, "GC256D"},
+    {0x0026, "GC512A"},
+    {0x0027, "GC512B"},
+    {0x0028, "GC512C"},
+    {0x0029, "curveSM2"},
+    {0x0100, "ffdhe2048"},
+    {0x0101, "ffdhe3072"},
+    {0x0102, "ffdhe4096"},
+    {0x0103, "ffdhe6144"},
+    {0x0104, "ffdhe8192"},
+    {0x0200, "MLKEM512"},
+    {0x0201, "MLKEM768"},
+    {0x0202, "MLKEM1024"},
+    {0x11EB, "SecP256r1MLKEM768"},
+    {0x11EC, "X25519MLKEM768"},
+    {0x11ED, "SecP384r1MLKEM1024"},
+    {0x6399, "X25519Kyber768Draft00"},
+    {0x639A, "SecP256r1Kyber768Draft00"}
+};
+
+const std::unordered_map<uint16_t, const char*> tls_cipher_map =
+{
+    {0x0001, "TLS_RSA_WITH_NULL_MD5"},
+    {0x0002, "TLS_RSA_WITH_NULL_SHA"},
+    {0x0003, "TLS_RSA_EXPORT_WITH_RC4_40_MD5"},
+    {0x0004, "TLS_RSA_WITH_RC4_128_MD5"},
+    {0x0005, "TLS_RSA_WITH_RC4_128_SHA"},
+    {0x0006, "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5"},
+    {0x0007, "TLS_RSA_WITH_IDEA_CBC_SHA"},
+    {0x0008, "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA"},
+    {0x0009, "TLS_RSA_WITH_DES_CBC_SHA"},
+    {0x000A, "TLS_RSA_WITH_3DES_EDE_CBC_SHA"},
+    {0x000B, "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA"},
+    {0x000C, "TLS_DH_DSS_WITH_DES_CBC_SHA"},
+    {0x000D, "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA"},
+    {0x000E, "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA"},
+    {0x000F, "TLS_DH_RSA_WITH_DES_CBC_SHA"},
+    {0x0010, "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA"},
+    {0x0011, "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"},
+    {0x0012, "TLS_DHE_DSS_WITH_DES_CBC_SHA"},
+    {0x0013, "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA"},
+    {0x0014, "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA"},
+    {0x0015, "TLS_DHE_RSA_WITH_DES_CBC_SHA"},
+    {0x0016, "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA"},
+    {0x0017, "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5"},
+    {0x0018, "TLS_DH_anon_WITH_RC4_128_MD5"},
+    {0x0019, "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA"},
+    {0x001A, "TLS_DH_anon_WITH_DES_CBC_SHA"},
+    {0x001B, "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA"},
+    {0x001E, "TLS_KRB5_WITH_DES_CBC_SHA"},
+    {0x001F, "TLS_KRB5_WITH_3DES_EDE_CBC_SHA"},
+    {0x0020, "TLS_KRB5_WITH_RC4_128_SHA"},
+    {0x0021, "TLS_KRB5_WITH_IDEA_CBC_SHA"},
+    {0x0022, "TLS_KRB5_WITH_DES_CBC_MD5"},
+    {0x0023, "TLS_KRB5_WITH_3DES_EDE_CBC_MD5"},
+    {0x0024, "TLS_KRB5_WITH_RC4_128_MD5"},
+    {0x0025, "TLS_KRB5_WITH_IDEA_CBC_MD5"},
+    {0x0026, "TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA"},
+    {0x0027, "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA"},
+    {0x0028, "TLS_KRB5_EXPORT_WITH_RC4_40_SHA"},
+    {0x0029, "TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5"},
+    {0x002A, "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5"},
+    {0x002B, "TLS_KRB5_EXPORT_WITH_RC4_40_MD5"},
+    {0x002C, "TLS_PSK_WITH_NULL_SHA"},
+    {0x002D, "TLS_DHE_PSK_WITH_NULL_SHA"},
+    {0x002E, "TLS_RSA_PSK_WITH_NULL_SHA"},
+    {0x002F, "TLS_RSA_WITH_AES_128_CBC_SHA"},
+    {0x0030, "TLS_DH_DSS_WITH_AES_128_CBC_SHA"},
+    {0x0031, "TLS_DH_RSA_WITH_AES_128_CBC_SHA"},
+    {0x0032, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA"},
+    {0x0033, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA"},
+    {0x0034, "TLS_DH_anon_WITH_AES_128_CBC_SHA"},
+    {0x0035, "TLS_RSA_WITH_AES_256_CBC_SHA"},
+    {0x0036, "TLS_DH_DSS_WITH_AES_256_CBC_SHA"},
+    {0x0037, "TLS_DH_RSA_WITH_AES_256_CBC_SHA"},
+    {0x0038, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA"},
+    {0x0039, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA"},
+    {0x003A, "TLS_DH_anon_WITH_AES_256_CBC_SHA"},
+    {0x003B, "TLS_RSA_WITH_NULL_SHA256"},
+    {0x003C, "TLS_RSA_WITH_AES_128_CBC_SHA256"},
+    {0x003D, "TLS_RSA_WITH_AES_256_CBC_SHA256"},
+    {0x003E, "TLS_DH_DSS_WITH_AES_128_CBC_SHA256"},
+    {0x003F, "TLS_DH_RSA_WITH_AES_128_CBC_SHA256"},
+    {0x0040, "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256"},
+    {0x0041, "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA"},
+    {0x0042, "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA"},
+    {0x0043, "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA"},
+    {0x0044, "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA"},
+    {0x0045, "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA"},
+    {0x0046, "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA"},
+    {0x0067, "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256"},
+    {0x0068, "TLS_DH_DSS_WITH_AES_256_CBC_SHA256"},
+    {0x0069, "TLS_DH_RSA_WITH_AES_256_CBC_SHA256"},
+    {0x006A, "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256"},
+    {0x006B, "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256"},
+    {0x006C, "TLS_DH_anon_WITH_AES_128_CBC_SHA256"},
+    {0x006D, "TLS_DH_anon_WITH_AES_256_CBC_SHA256"},
+    {0x0084, "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA"},
+    {0x0085, "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA"},
+    {0x0086, "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA"},
+    {0x0087, "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA"},
+    {0x0088, "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA"},
+    {0x0089, "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA"},
+    {0x008A, "TLS_PSK_WITH_RC4_128_SHA"},
+    {0x008B, "TLS_PSK_WITH_3DES_EDE_CBC_SHA"},
+    {0x008C, "TLS_PSK_WITH_AES_128_CBC_SHA"},
+    {0x008D, "TLS_PSK_WITH_AES_256_CBC_SHA"},
+    {0x008E, "TLS_DHE_PSK_WITH_RC4_128_SHA"},
+    {0x008F, "TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA"},
+    {0x0090, "TLS_DHE_PSK_WITH_AES_128_CBC_SHA"},
+    {0x0091, "TLS_DHE_PSK_WITH_AES_256_CBC_SHA"},
+    {0x0092, "TLS_RSA_PSK_WITH_RC4_128_SHA"},
+    {0x0093, "TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA"},
+    {0x0094, "TLS_RSA_PSK_WITH_AES_128_CBC_SHA"},
+    {0x0095, "TLS_RSA_PSK_WITH_AES_256_CBC_SHA"},
+    {0x0096, "TLS_RSA_WITH_SEED_CBC_SHA"},
+    {0x0097, "TLS_DH_DSS_WITH_SEED_CBC_SHA"},
+    {0x0098, "TLS_DH_RSA_WITH_SEED_CBC_SHA"},
+    {0x0099, "TLS_DHE_DSS_WITH_SEED_CBC_SHA"},
+    {0x009A, "TLS_DHE_RSA_WITH_SEED_CBC_SHA"},
+    {0x009B, "TLS_DH_anon_WITH_SEED_CBC_SHA"},
+    {0x009C, "TLS_RSA_WITH_AES_128_GCM_SHA256"},
+    {0x009D, "TLS_RSA_WITH_AES_256_GCM_SHA384"},
+    {0x009E, "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256"},
+    {0x009F, "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384"},
+    {0x00A0, "TLS_DH_RSA_WITH_AES_128_GCM_SHA256"},
+    {0x00A1, "TLS_DH_RSA_WITH_AES_256_GCM_SHA384"},
+    {0x00A2, "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256"},
+    {0x00A3, "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384"},
+    {0x00A4, "TLS_DH_DSS_WITH_AES_128_GCM_SHA256"},
+    {0x00A5, "TLS_DH_DSS_WITH_AES_256_GCM_SHA384"},
+    {0x00A6, "TLS_DH_anon_WITH_AES_128_GCM_SHA256"},
+    {0x00A7, "TLS_DH_anon_WITH_AES_256_GCM_SHA384"},
+    {0x00A8, "TLS_PSK_WITH_AES_128_GCM_SHA256"},
+    {0x00A9, "TLS_PSK_WITH_AES_256_GCM_SHA384"},
+    {0x00AA, "TLS_DHE_PSK_WITH_AES_128_GCM_SHA256"},
+    {0x00AB, "TLS_DHE_PSK_WITH_AES_256_GCM_SHA384"},
+    {0x00AC, "TLS_RSA_PSK_WITH_AES_128_GCM_SHA256"},
+    {0x00AD, "TLS_RSA_PSK_WITH_AES_256_GCM_SHA384"},
+    {0x00AE, "TLS_PSK_WITH_AES_128_CBC_SHA256"},
+    {0x00AF, "TLS_PSK_WITH_AES_256_CBC_SHA384"},
+    {0x00B0, "TLS_PSK_WITH_NULL_SHA256"},
+    {0x00B1, "TLS_PSK_WITH_NULL_SHA384"},
+    {0x00B2, "TLS_DHE_PSK_WITH_AES_128_CBC_SHA256"},
+    {0x00B3, "TLS_DHE_PSK_WITH_AES_256_CBC_SHA384"},
+    {0x00B4, "TLS_DHE_PSK_WITH_NULL_SHA256"},
+    {0x00B5, "TLS_DHE_PSK_WITH_NULL_SHA384"},
+    {0x00B6, "TLS_RSA_PSK_WITH_AES_128_CBC_SHA256"},
+    {0x00B7, "TLS_RSA_PSK_WITH_AES_256_CBC_SHA384"},
+    {0x00B8, "TLS_RSA_PSK_WITH_NULL_SHA256"},
+    {0x00B9, "TLS_RSA_PSK_WITH_NULL_SHA384"},
+    {0x00BA, "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256"},
+    {0x00BB, "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256"},
+    {0x00BC, "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256"},
+    {0x00BD, "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256"},
+    {0x00BE, "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256"},
+    {0x00BF, "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256"},
+    {0x00C0, "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256"},
+    {0x00C1, "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256"},
+    {0x00C2, "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256"},
+    {0x00C3, "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256"},
+    {0x00C4, "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256"},
+    {0x00C5, "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256"},
+    {0x00C6, "TLS_SM4_GCM_SM3"},
+    {0x00C7, "TLS_SM4_CCM_SM3"},
+    {0x00FF, "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"},
+    {0x1301, "TLS_AES_128_GCM_SHA256"},
+    {0x1302, "TLS_AES_256_GCM_SHA384"},
+    {0x1303, "TLS_CHACHA20_POLY1305_SHA256"},
+    {0x1304, "TLS_AES_128_CCM_SHA256"},
+    {0x1305, "TLS_AES_128_CCM_8_SHA256"},
+    {0x1306, "TLS_AEGIS_256_SHA512"},
+    {0x1307, "TLS_AEGIS_128L_SHA256"},
+    {0x5600, "TLS_FALLBACK_SCSV"},
+    {0xC001, "TLS_ECDH_ECDSA_WITH_NULL_SHA"},
+    {0xC002, "TLS_ECDH_ECDSA_WITH_RC4_128_SHA"},
+    {0xC003, "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA"},
+    {0xC004, "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA"},
+    {0xC005, "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA"},
+    {0xC006, "TLS_ECDHE_ECDSA_WITH_NULL_SHA"},
+    {0xC007, "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA"},
+    {0xC008, "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA"},
+    {0xC009, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA"},
+    {0xC00A, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA"},
+    {0xC00B, "TLS_ECDH_RSA_WITH_NULL_SHA"},
+    {0xC00C, "TLS_ECDH_RSA_WITH_RC4_128_SHA"},
+    {0xC00D, "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA"},
+    {0xC00E, "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA"},
+    {0xC00F, "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA"},
+    {0xC010, "TLS_ECDHE_RSA_WITH_NULL_SHA"},
+    {0xC011, "TLS_ECDHE_RSA_WITH_RC4_128_SHA"},
+    {0xC012, "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA"},
+    {0xC013, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA"},
+    {0xC014, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA"},
+    {0xC015, "TLS_ECDH_anon_WITH_NULL_SHA"},
+    {0xC016, "TLS_ECDH_anon_WITH_RC4_128_SHA"},
+    {0xC017, "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA"},
+    {0xC018, "TLS_ECDH_anon_WITH_AES_128_CBC_SHA"},
+    {0xC019, "TLS_ECDH_anon_WITH_AES_256_CBC_SHA"},
+    {0xC01A, "TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA"},
+    {0xC01B, "TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA"},
+    {0xC01C, "TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA"},
+    {0xC01D, "TLS_SRP_SHA_WITH_AES_128_CBC_SHA"},
+    {0xC01E, "TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA"},
+    {0xC01F, "TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA"},
+    {0xC020, "TLS_SRP_SHA_WITH_AES_256_CBC_SHA"},
+    {0xC021, "TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA"},
+    {0xC022, "TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA"},
+    {0xC023, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256"},
+    {0xC024, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384"},
+    {0xC025, "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256"},
+    {0xC026, "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384"},
+    {0xC027, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256"},
+    {0xC028, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384"},
+    {0xC029, "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256"},
+    {0xC02A, "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384"},
+    {0xC02B, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"},
+    {0xC02C, "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"},
+    {0xC02D, "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256"},
+    {0xC02E, "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384"},
+    {0xC02F, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"},
+    {0xC030, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384"},
+    {0xC031, "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256"},
+    {0xC032, "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384"},
+    {0xC033, "TLS_ECDHE_PSK_WITH_RC4_128_SHA"},
+    {0xC034, "TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA"},
+    {0xC035, "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA"},
+    {0xC036, "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA"},
+    {0xC037, "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256"},
+    {0xC038, "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384"},
+    {0xC039, "TLS_ECDHE_PSK_WITH_NULL_SHA"},
+    {0xC03A, "TLS_ECDHE_PSK_WITH_NULL_SHA256"},
+    {0xC03B, "TLS_ECDHE_PSK_WITH_NULL_SHA384"},
+    {0xC03C, "TLS_RSA_WITH_ARIA_128_CBC_SHA256"},
+    {0xC03D, "TLS_RSA_WITH_ARIA_256_CBC_SHA384"},
+    {0xC03E, "TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256"},
+    {0xC03F, "TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384"},
+    {0xC040, "TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256"},
+    {0xC041, "TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384"},
+    {0xC042, "TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256"},
+    {0xC043, "TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384"},
+    {0xC044, "TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256"},
+    {0xC045, "TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384"},
+    {0xC046, "TLS_DH_anon_WITH_ARIA_128_CBC_SHA256"},
+    {0xC047, "TLS_DH_anon_WITH_ARIA_256_CBC_SHA384"},
+    {0xC048, "TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256"},
+    {0xC049, "TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384"},
+    {0xC04A, "TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256"},
+    {0xC04B, "TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384"},
+    {0xC04C, "TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256"},
+    {0xC04D, "TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384"},
+    {0xC04E, "TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256"},
+    {0xC04F, "TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384"},
+    {0xC050, "TLS_RSA_WITH_ARIA_128_GCM_SHA256"},
+    {0xC051, "TLS_RSA_WITH_ARIA_256_GCM_SHA384"},
+    {0xC052, "TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256"},
+    {0xC053, "TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384"},
+    {0xC054, "TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256"},
+    {0xC055, "TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384"},
+    {0xC056, "TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256"},
+    {0xC057, "TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384"},
+    {0xC058, "TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256"},
+    {0xC059, "TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384"},
+    {0xC05A, "TLS_DH_anon_WITH_ARIA_128_GCM_SHA256"},
+    {0xC05B, "TLS_DH_anon_WITH_ARIA_256_GCM_SHA384"},
+    {0xC05C, "TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256"},
+    {0xC05D, "TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384"},
+    {0xC05E, "TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256"},
+    {0xC05F, "TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384"},
+    {0xC060, "TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256"},
+    {0xC061, "TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384"},
+    {0xC062, "TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256"},
+    {0xC063, "TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384"},
+    {0xC064, "TLS_PSK_WITH_ARIA_128_CBC_SHA256"},
+    {0xC065, "TLS_PSK_WITH_ARIA_256_CBC_SHA384"},
+    {0xC066, "TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256"},
+    {0xC067, "TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384"},
+    {0xC068, "TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256"},
+    {0xC069, "TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384"},
+    {0xC06A, "TLS_PSK_WITH_ARIA_128_GCM_SHA256"},
+    {0xC06B, "TLS_PSK_WITH_ARIA_256_GCM_SHA384"},
+    {0xC06C, "TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256"},
+    {0xC06D, "TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384"},
+    {0xC06E, "TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256"},
+    {0xC06F, "TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384"},
+    {0xC070, "TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256"},
+    {0xC071, "TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384"},
+    {0xC072, "TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256"},
+    {0xC073, "TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384"},
+    {0xC074, "TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256"},
+    {0xC075, "TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384"},
+    {0xC076, "TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256"},
+    {0xC077, "TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384"},
+    {0xC078, "TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256"},
+    {0xC079, "TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384"},
+    {0xC07A, "TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256"},
+    {0xC07B, "TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384"},
+    {0xC07C, "TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256"},
+    {0xC07D, "TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384"},
+    {0xC07E, "TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256"},
+    {0xC07F, "TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384"},
+    {0xC080, "TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256"},
+    {0xC081, "TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384"},
+    {0xC082, "TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256"},
+    {0xC083, "TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384"},
+    {0xC084, "TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256"},
+    {0xC085, "TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384"},
+    {0xC086, "TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256"},
+    {0xC087, "TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384"},
+    {0xC088, "TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256"},
+    {0xC089, "TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384"},
+    {0xC08A, "TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256"},
+    {0xC08B, "TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384"},
+    {0xC08C, "TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256"},
+    {0xC08D, "TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384"},
+    {0xC08E, "TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256"},
+    {0xC08F, "TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384"},
+    {0xC090, "TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256"},
+    {0xC091, "TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384"},
+    {0xC092, "TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256"},
+    {0xC093, "TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384"},
+    {0xC094, "TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256"},
+    {0xC095, "TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384"},
+    {0xC096, "TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256"},
+    {0xC097, "TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384"},
+    {0xC098, "TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256"},
+    {0xC099, "TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384"},
+    {0xC09A, "TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256"},
+    {0xC09B, "TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384"},
+    {0xC09C, "TLS_RSA_WITH_AES_128_CCM"},
+    {0xC09D, "TLS_RSA_WITH_AES_256_CCM"},
+    {0xC09E, "TLS_DHE_RSA_WITH_AES_128_CCM"},
+    {0xC09F, "TLS_DHE_RSA_WITH_AES_256_CCM"},
+    {0xC0A0, "TLS_RSA_WITH_AES_128_CCM_8"},
+    {0xC0A1, "TLS_RSA_WITH_AES_256_CCM_8"},
+    {0xC0A2, "TLS_DHE_RSA_WITH_AES_128_CCM_8"},
+    {0xC0A3, "TLS_DHE_RSA_WITH_AES_256_CCM_8"},
+    {0xC0A4, "TLS_PSK_WITH_AES_128_CCM"},
+    {0xC0A5, "TLS_PSK_WITH_AES_256_CCM"},
+    {0xC0A6, "TLS_DHE_PSK_WITH_AES_128_CCM"},
+    {0xC0A7, "TLS_DHE_PSK_WITH_AES_256_CCM"},
+    {0xC0A8, "TLS_PSK_WITH_AES_128_CCM_8"},
+    {0xC0A9, "TLS_PSK_WITH_AES_256_CCM_8"},
+    {0xC0AA, "TLS_PSK_DHE_WITH_AES_128_CCM_8"},
+    {0xC0AB, "TLS_PSK_DHE_WITH_AES_256_CCM_8"},
+    {0xC0AC, "TLS_ECDHE_ECDSA_WITH_AES_128_CCM"},
+    {0xC0AD, "TLS_ECDHE_ECDSA_WITH_AES_256_CCM"},
+    {0xC0AE, "TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8"},
+    {0xC0AF, "TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8"},
+    {0xC0B0, "TLS_ECCPWD_WITH_AES_128_GCM_SHA256"},
+    {0xC0B1, "TLS_ECCPWD_WITH_AES_256_GCM_SHA384"},
+    {0xC0B2, "TLS_ECCPWD_WITH_AES_128_CCM_SHA256"},
+    {0xC0B3, "TLS_ECCPWD_WITH_AES_256_CCM_SHA384"},
+    {0xC0B4, "TLS_SHA256_SHA256"},
+    {0xC0B5, "TLS_SHA384_SHA384"},
+    {0xC100, "TLS_GOSTR341112_256_WITH_KUZNYECHIK_CTR_OMAC"},
+    {0xC101, "TLS_GOSTR341112_256_WITH_MAGMA_CTR_OMAC"},
+    {0xC102, "TLS_GOSTR341112_256_WITH_28147_CNT_IMIT"},
+    {0xC103, "TLS_GOSTR341112_256_WITH_KUZNYECHIK_MGM_L"},
+    {0xC104, "TLS_GOSTR341112_256_WITH_MAGMA_MGM_L"},
+    {0xC105, "TLS_GOSTR341112_256_WITH_KUZNYECHIK_MGM_S"},
+    {0xC106, "TLS_GOSTR341112_256_WITH_MAGMA_MGM_S"},
+    {0xCCA8, "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"},
+    {0xCCA9, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256"},
+    {0xCCAA, "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256"},
+    {0xCCAB, "TLS_PSK_WITH_CHACHA20_POLY1305_SHA256"},
+    {0xCCAC, "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256"},
+    {0xCCAD, "TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256"},
+    {0xCCAE, "TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256"},
+    {0xD001, "TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256"},
+    {0xD002, "TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384"},
+    {0xD003, "TLS_ECDHE_PSK_WITH_AES_128_CCM_8_SHA256"},
+    {0xD005, "TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256"},
+    {0xfee0, "SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA"},
+    {0xfee1, "SSL_RSA_FIPS_WITH_DES_CBC_SHA"},
+    {0xfefe, "SSL_RSA_FIPS_WITH_DES_CBC_SHA"},
+    {0xfeff, "SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA"}
+};
+
+const std::unordered_map<uint16_t, const char*> ssl_cipher_map =
+{
+    {0x0700, "SSL_CK_DES_192_EDE3_CBC_WITH_MD5"},
+    {0x0701, "SSL_CK_DES_192_EDE3_CBC_WITH_SHA"},
+    {0x0600, "SSL_CK_DES_64_CBC_WITH_MD5"},
+    {0x0601, "SSL_CK_DES_64_CBC_WITH_SHA"},
+    {0xff08, "SSL_CK_DES_64_CFB64_WITH_MD5_1"},
+    {0x0400, "SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5"},
+    {0x0200, "SSL_CK_RC4_128_EXPORT40_WITH_MD5"},
+    {0x0500, "SSL_CK_IDEA_128_CBC_WITH_MD5"},
+    {0x0300, "SSL_CK_RC2_128_CBC_WITH_MD5"},
+    {0x0800, "SSL_CK_RC4_64_WITH_MD5"},
+    {0x0100, "SSL_CK_RC4_128_WITH_MD5"}
+};
+
+static const char* get_cipher(const DataEvent* event, const Flow*)
+{
+    auto tls_event = (const SslTlsMetadataBaseEvent*)event;
+    auto map = tls_event->get_version() < 0x0300 ? ssl_cipher_map : tls_cipher_map;
+    auto it = map.find(tls_event->get_cipher());
+    if (it != map.end())
+    {
+        return it->second;
+    }
+    else
+    {
+        static THREAD_LOCAL char unknown_cipher[32];
+        memset(unknown_cipher, 0, sizeof(unknown_cipher));
+        snprintf(unknown_cipher, sizeof(unknown_cipher), "Unknown cipher (0x%02x)", tls_event->get_cipher());
+        return unknown_cipher;
+    }
+}
+
+static const char* get_curve(const DataEvent* event, const Flow*)
+{
+    auto it = get_ssl_string_curve_map.find(((const SslTlsMetadataBaseEvent*)event)->get_curve());
+    if (it != get_ssl_string_curve_map.end())
+    {
+        return it->second;
+    }
+    else
+    {
+        static THREAD_LOCAL char unknown_curve[32];
+        memset(unknown_curve, 0, sizeof(unknown_curve));
+        snprintf(unknown_curve, sizeof(unknown_curve), "Unknown curve (0x%02x)", ((const SslTlsMetadataBaseEvent*)event)->get_curve());
+        return unknown_curve;
+    }
+}
+
+static const char* get_version(const DataEvent* event, const Flow*)
+{
+    return get_ssl_string_version(((const SslTlsMetadataBaseEvent*)event)->get_version());
+}
+
+static const char* get_validation_status(const DataEvent* event, const Flow*)
+{
+    return ((const SslTlsMetadataBaseEvent*)event)->get_validation_status().c_str();
+}
+
+static const char* get_subject(const DataEvent* event, const Flow*)
+{
+    return ((const SslTlsMetadataBaseEvent*)event)->get_subject().c_str();
+}
+
+static const char* get_issuer(const DataEvent* event, const Flow*)
+{
+    return ((const SslTlsMetadataBaseEvent*)event)->get_issuer().c_str();
+}
+
+static const char* get_module_identifier(const DataEvent* event, const Flow*)
+{
+    return ((const SslTlsMetadataBaseEvent*)event)->get_module_identifier().c_str();
+}
+
+static const char* get_server_name_identifier(const DataEvent* event, const Flow*)
+{
+    return ((const SslTlsMetadataBaseEvent*)event)->get_server_name_identifier().c_str();
+}
+
+static const map<string, ExtractorEvent::BufGetFn> buf_getters =
+{
+    {"version", get_version},
+    {"server_name_identifier", get_server_name_identifier},
+    {"validation_status", get_validation_status},
+    {"subject", get_subject},
+    {"issuer", get_issuer},
+    {"curve", get_curve},
+    {"cipher", get_cipher},
+    {"module_identifier", get_module_identifier}
+};
+
+THREAD_LOCAL const snort::Connector::ID* SslExtractor::log_id = nullptr;
+
+SslExtractor::SslExtractor(Extractor& i, uint32_t t, const vector<string>& fields) :
+    ExtractorEvent(ServiceType::SSL, i, t)
+{
+    for (const auto& f : fields)
+    {
+        if (append(nts_fields, nts_getters, f))
+            continue;
+        if (append(sip_fields, sip_getters, f))
+            continue;
+        if (append(num_fields, num_getters, f))
+            continue;
+        if (append(buf_fields, buf_getters, f))
+            continue;
+    }
+
+    DataBus::subscribe_global(ssl_pub_key, SslEventIds::SSL_TLS_METADATA_EVENT,
+        new Server(*this, S_NAME), i.get_snort_config());
+}
+
+void SslExtractor::internal_tinit(const snort::Connector::ID* service_id)
+{ log_id = service_id; }
+
+void SslExtractor::handle(DataEvent& event, Flow* flow)
+{
+    // cppcheck-suppress unreadVariable
+    Profile profile(extractor_perf_stats);
+
+    if (!filter(flow))
+         return;
+
+    extractor_stats.total_events++;
+
+    logger->open_record();
+    log(nts_fields, &event, flow);
+    log(sip_fields, &event, flow);
+    log(num_fields, &event, flow);
+    log(buf_fields, &event, flow);
+    logger->close_record(*log_id);
+}
diff --git a/src/network_inspectors/extractor/extractor_ssl.h b/src/network_inspectors/extractor/extractor_ssl.h
new file mode 100644 (file)
index 0000000..a21bb52
--- /dev/null
@@ -0,0 +1,44 @@
+//--------------------------------------------------------------------------
+// Copyright (C) 2025-2025 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.
+//--------------------------------------------------------------------------
+// extractor_ssl.h author Oleksandr Stepanov <ostepano@cisco.com>
+
+#ifndef EXTRACTOR_SSL_H
+#define EXTRACTOR_SSL_H
+
+#include <cassert>
+
+#include "extractors.h"
+
+class SslExtractorFlowData;
+
+class SslExtractor : public ExtractorEvent
+{
+public:
+    SslExtractor(Extractor&, uint32_t tenant, const std::vector<std::string>& fields);
+
+    void handle(DataEvent&, Flow*);
+
+private:
+    using Server = Handler<SslExtractor>;
+
+    void internal_tinit(const snort::Connector::ID*) override;
+
+    static THREAD_LOCAL const snort::Connector::ID* log_id;
+};
+
+#endif
index eae87c9eedceb3a4933dd6ea6df72d553f72968b..dd3280e067c15f42b503bdeef16bc1f3a1aa18ca 100644 (file)
@@ -25,6 +25,7 @@
 
 #include "ssl.h"
 
+#include <openssl/ssl.h>
 #include <openssl/x509.h>
 
 #include "packet.h"
@@ -43,6 +44,9 @@
 #define SSL2_CHELLO_BYTE 0x01
 #define SSL2_SHELLO_BYTE 0x04
 
+SSL_CTX* global_ssl_ctxt = nullptr;
+SSL* global_ssl = nullptr;
+
 SSLV3ClientHelloData::~SSLV3ClientHelloData()
 {
     snort_free(host_name);
@@ -59,6 +63,8 @@ SSLV3ServerCertData::~SSLV3ServerCertData()
     snort_free(certs_data);
     snort_free(common_name);
     snort_free(org_unit);
+    snort_free(issuer_info);
+    snort_free(subject_info);
 }
 
 void SSLV3ServerCertData::clear()
@@ -73,6 +79,17 @@ void SSLV3ServerCertData::clear()
     org_unit = nullptr;
 }
 
+void TLSConnectionData::process(const SSLV3ServerCertData &cert_data)
+{
+    subject_info = cert_data.subject_info ? std::string(cert_data.subject_info) : "";
+    issuer_info = cert_data.issuer_info ? std::string(cert_data.issuer_info) : "";
+}
+
+void TLSConnectionData::process(const SSLV3ClientHelloData &client_hello_data)
+{
+    server_name_identifier = client_hello_data.host_name ? std::string(client_hello_data.host_name) : "";
+}
+
 static uint32_t SSL_decode_version_v3(uint8_t major, uint8_t minor)
 {
     /* Should only be called internally and by functions which have previously
@@ -103,7 +120,7 @@ static uint32_t SSL_decode_version_v3(uint8_t major, uint8_t minor)
 
 static uint32_t SSL_decode_handshake_v3(const uint8_t* pkt, int size,
     uint32_t cur_flags, uint32_t pkt_flags, SSLV3ClientHelloData* client_hello_data,
-    SSLV3ServerCertData* server_cert_data)
+    SSLV3ServerCertData* server_cert_data, TLSConnectionParams* tls_connection_params)
 {
     const SSL_handshake_hello_t* hello;
     const ServiceSSLV3CertsRecord* certs_rec;
@@ -168,11 +185,15 @@ static uint32_t SSL_decode_handshake_v3(const uint8_t* pkt, int size,
 
             hello = (const SSL_handshake_hello_t*)handshake;
             retval |= SSL_decode_version_v3(hello->major, hello->minor);
+            if (tls_connection_params)
+                tls_connection_params->selected_tls_version = ntohs(*((const uint16_t*)((const uint8_t*)hello + offsetof(SSL_handshake_hello_t, major))));
 
             /* Compare version of record with version of handshake */
             if ((cur_flags & SSL_VERFLAGS) != (retval & SSL_VERFLAGS))
                 retval |= SSL_BAD_VER_FLAG;
 
+            snort::parse_server_hello_data((const uint8_t*)handshake, size + SSL_HS_PAYLOAD_OFFSET, tls_connection_params);
+
             break;
 
         case SSL_HS_SHELLO_DONE:
@@ -184,11 +205,16 @@ static uint32_t SSL_decode_handshake_v3(const uint8_t* pkt, int size,
 
         case SSL_HS_SKEYX:
             if (pkt_flags & PKT_FROM_SERVER)
+            {
                 retval |= SSL_SERVER_KEYX_FLAG | SSL_CUR_SERVER_KEYX_FLAG;
+
+                snort::parse_server_key_exchange((const uint8_t*)handshake+SSL_HS_PAYLOAD_OFFSET, size, tls_connection_params);
+            }
             else
+            {
                 retval |= SSL_BOGUS_HS_DIR_FLAG;
+            }
             break;
-
         case SSL_HS_CKEYX:
             if (pkt_flags & PKT_FROM_SERVER)
                 retval |= SSL_BOGUS_HS_DIR_FLAG;
@@ -242,7 +268,7 @@ static uint32_t SSL_decode_handshake_v3(const uint8_t* pkt, int size,
 
 static uint32_t SSL_decode_v3(const uint8_t* pkt, int size, uint32_t pkt_flags,
     uint8_t* alert_flags, uint16_t* partial_rec_len, int max_hb_len, uint32_t* info_flags,
-    SSLV3ClientHelloData* client_hello_data, SSLV3ServerCertData* server_cert_data, uint32_t prev_flags)
+    SSLV3ClientHelloData* client_hello_data, SSLV3ServerCertData* server_cert_data, uint32_t prev_flags, TLSConnectionParams* tls_connection_params)
 {
     uint32_t retval = 0;
     uint16_t hblen;
@@ -336,7 +362,7 @@ static uint32_t SSL_decode_v3(const uint8_t* pkt, int size, uint32_t pkt_flags,
             if (!(retval & SSL_CHANGE_CIPHER_FLAG) && !(prev_flags & SSL_CHANGE_CIPHER_FLAG))
             {
                 int hsize = size < (int)reclen ? size : (int)reclen;
-                retval |= SSL_decode_handshake_v3(pkt, hsize, retval, pkt_flags, client_hello_data, server_cert_data);
+                retval |= SSL_decode_handshake_v3(pkt, hsize, retval, pkt_flags, client_hello_data, server_cert_data, tls_connection_params);
             }
             else if (ccs)
             {
@@ -392,7 +418,7 @@ static inline bool SSL_v3_back_compat_v2(const SSLv2_chello_t* chello)
     return false;
 }
 
-static uint32_t SSL_decode_v2(const uint8_t* pkt, int size, uint32_t pkt_flags)
+static uint32_t SSL_decode_v2(const uint8_t* pkt, int size, uint32_t pkt_flags, TLSConnectionParams* tls_connection_params)
 {
     const SSLv2_chello_t* chello;
     const SSLv2_shello_t* shello;
@@ -455,10 +481,20 @@ static uint32_t SSL_decode_v2(const uint8_t* pkt, int size, uint32_t pkt_flags)
                 break;
             }
 
+            if (tls_connection_params)
+                tls_connection_params->selected_tls_version = ntohs(*((const uint16_t*)(pkt + offsetof(SSLv2_shello_t, major))));
+
             break;
 
         case SSL_V2_CKEY:
-            retval |= SSL_CLIENT_KEYX_FLAG |  SSL_CUR_CLIENT_KEYX_FLAG;
+            if (size < (int)sizeof(SSLv2_client_master_key_t))
+            {
+                retval |= SSL_TRUNCATED_FLAG;
+                break;
+            }
+            if (tls_connection_params)
+                tls_connection_params->cipher = ntohs(*((const uint16_t*)(pkt + offsetof(SSLv2_client_master_key_t, cipher_spec))));
+            retval |= SSL_CLIENT_KEYX_FLAG | SSL_CUR_CLIENT_KEYX_FLAG;
             break;
 
         default:
@@ -480,7 +516,7 @@ namespace snort
 uint32_t SSL_decode(
     const uint8_t* pkt, int size, uint32_t pkt_flags, uint32_t prev_flags,
     uint8_t* alert_flags, uint16_t* partial_rec_len, int max_hb_len, uint32_t* info_flags,
-    SSLV3ClientHelloData* client_hello_data, SSLV3ServerCertData* server_cert_data)
+    SSLV3ClientHelloData* client_hello_data, SSLV3ServerCertData* server_cert_data, TLSConnectionParams* tls_connection_params)
 {
     if (!pkt || !size)
         return SSL_ARG_ERROR_FLAG;
@@ -494,7 +530,7 @@ uint32_t SSL_decode(
 
         /* Only SSL v2 will have these bits set */
         if (((pkt[0] & 0x80) || (pkt[0] & 0x40)) && !(partial_rec_len && *partial_rec_len))
-            return SSL_decode_v2(pkt, size, pkt_flags);
+            return SSL_decode_v2(pkt, size, pkt_flags, tls_connection_params);
 
         /* If this packet is only 5 bytes, it inconclusive whether its SSLv2 or TLS.
          * If it is v2, it's definitely truncated anyway.  By decoding a 5 byte
@@ -502,7 +538,7 @@ uint32_t SSL_decode(
          * indicate that it is truncated. */
         if (size == 5)
             return SSL_decode_v3(pkt, size, pkt_flags, alert_flags, partial_rec_len, max_hb_len, info_flags,
-                client_hello_data, server_cert_data, prev_flags);
+                client_hello_data, server_cert_data, prev_flags, tls_connection_params);
 
         /* At this point, 'size' has to be > 5 */
 
@@ -526,7 +562,7 @@ uint32_t SSL_decode(
                     /* If these lengths match, it's v3
                        Otherwise, it's v2 */
                     if (reclen - SSL_HS_PAYLOAD_OFFSET != datalen)
-                        return SSL_decode_v2(pkt, size, pkt_flags);
+                        return SSL_decode_v2(pkt, size, pkt_flags, tls_connection_params);
                 }
             }
         }
@@ -545,12 +581,12 @@ uint32_t SSL_decode(
             /* If these lengths match, it's v3
                Otherwise, it's v2 */
             if (reclen - SSL_HS_PAYLOAD_OFFSET != datalen)
-                return SSL_decode_v2(pkt, size, pkt_flags);
+                return SSL_decode_v2(pkt, size, pkt_flags, tls_connection_params);
         }
     }
 
     return SSL_decode_v3(pkt, size, pkt_flags, alert_flags, partial_rec_len, max_hb_len, info_flags,
-        client_hello_data, server_cert_data, prev_flags);
+        client_hello_data, server_cert_data, prev_flags, tls_connection_params);
 }
 
 /* very simplistic - just enough to say this is binary data - the rules will make a final
@@ -612,61 +648,61 @@ bool IsSSL(const uint8_t* ptr, int len, int pkt_flags)
     return false;
 }
 
-ParseCHResult parse_client_hello_data(const uint8_t* pkt, uint16_t size, SSLV3ClientHelloData* client_hello_data)
+ParseHelloResult parse_client_hello_data(const uint8_t* pkt, uint16_t size, SSLV3ClientHelloData* client_hello_data)
 {
     if (client_hello_data == nullptr)
-        return ParseCHResult::FAILED;
+        return ParseHelloResult::FAILURE;
 
     if (size < sizeof(ServiceSSLV3Record))
-        return ParseCHResult::FAILED;
+        return ParseHelloResult::FAILURE;
     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 ParseCHResult::FAILED;
+        return ParseHelloResult::FAILURE;
     }
     unsigned length = ntohs(rec->length) + offsetof(ServiceSSLV3Record, version);
     if (size < length)
-        return ParseCHResult::FRAGMENTED_PACKET;
+        return ParseHelloResult::FRAGMENTED_PACKET;
     pkt += sizeof(ServiceSSLV3Record);
     size -= sizeof(ServiceSSLV3Record);
 
     /* Session ID (1-byte length). */
     if (size < 1)
-        return ParseCHResult::FAILED;
+        return ParseHelloResult::FAILURE;
     length = *((const uint8_t*)pkt);
     pkt += length + 1;
     if (size < (length + 1))
-        return ParseCHResult::FAILED;
+        return ParseHelloResult::FAILURE;
     size -= length + 1;
 
     /* Cipher Suites (2-byte length). */
     if (size < 2)
-        return ParseCHResult::FAILED;
+        return ParseHelloResult::FAILURE;
     length = ntohs(*((const uint16_t*)pkt));
     pkt += length + 2;
     if (size < (length + 2))
-        return ParseCHResult::FAILED;
+        return ParseHelloResult::FAILURE;
     size -= length + 2;
 
     /* Compression Methods (1-byte length). */
     if (size < 1)
-        return ParseCHResult::FAILED;
+        return ParseHelloResult::FAILURE;
     length = *((const uint8_t*)pkt);
     pkt += length + 1;
     if (size < (length + 1))
-        return ParseCHResult::FAILED;
+        return ParseHelloResult::FAILURE;
     size -= length + 1;
 
     /* Extensions (2-byte length) */
     if (size < 2)
-        return ParseCHResult::FAILED;
+        return ParseHelloResult::FAILURE;
     length = ntohs(*((const uint16_t*)pkt));
     pkt += 2;
     size -= 2;
     if (size < length)
-        return ParseCHResult::FAILED;
+        return ParseHelloResult::FAILURE;
 
     /* We need at least type (2 bytes) and length (2 bytes) in the extension. */
     while (length >= 4)
@@ -676,27 +712,110 @@ ParseCHResult parse_client_hello_data(const uint8_t* pkt, uint16_t size, SSLV3Cl
         {
             /* Found server host name. */
             if (length < sizeof(ServiceSSLV3ExtensionServerName))
-                return ParseCHResult::FAILED;
+                return ParseHelloResult::FAILURE;
 
             unsigned len = ntohs(ext->string_length);
             if ((length - sizeof(ServiceSSLV3ExtensionServerName)) < len)
-                return ParseCHResult::FAILED;
+                return ParseHelloResult::FAILURE;
 
             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 ParseCHResult::SUCCESS;
+            return ParseHelloResult::SUCCESS;
         }
 
         unsigned len = ntohs(ext->length) + offsetof(ServiceSSLV3ExtensionServerName, list_length);
         if (len > length)
-            return ParseCHResult::FAILED;
+            return ParseHelloResult::FAILURE;
 
         pkt += len;
         length -= len;
     }
 
-    return ParseCHResult::FAILED;
+    return ParseHelloResult::FAILURE;
+}
+
+ParseHelloResult parse_server_hello_data(const uint8_t* pkt, uint16_t size, TLSConnectionParams* tls_connection_params)
+{
+    if (tls_connection_params == nullptr)
+        return ParseHelloResult::FAILURE;
+
+    if (size < sizeof(ServiceSSLV3Record))
+        return ParseHelloResult::FAILURE;
+    const ServiceSSLV3Record* rec = (const ServiceSSLV3Record*)pkt;
+    uint16_t ver = ntohs(rec->version);
+    if (rec->type != SSLV3RecordType::SERVER_HELLO || (ver != 0x0300 && ver != 0x0301 && ver != 0x0302 &&
+        ver != 0x0303) || rec->length_msb)
+    {
+        return ParseHelloResult::FAILURE;
+    }
+    unsigned length = ntohs(rec->length) + offsetof(ServiceSSLV3Record, version);
+    if (size < length)
+        return ParseHelloResult::FRAGMENTED_PACKET;
+    pkt += sizeof(ServiceSSLV3Record);
+    size -= sizeof(ServiceSSLV3Record);
+
+    /* Session ID (1-byte length). */
+    if (size < 1)
+        return ParseHelloResult::FAILURE;
+    length = *((const uint8_t*)pkt);
+    pkt += length + 1;
+    if (size < (length + 1))
+        return ParseHelloResult::FAILURE;
+    size -= length + 1;
+
+    /* Cipher Suite (2-byte length). */
+    if (size < 2)
+        return ParseHelloResult::FAILURE;
+    tls_connection_params->cipher = ntohs(*((const uint16_t*)pkt));
+    pkt += 2;
+    size -= 2;
+
+    /* Compression Methods (1-byte length). */
+    if (size < 1)
+        return ParseHelloResult::FAILURE;
+    length = *((const uint8_t*)pkt);
+    pkt += length + 1;
+    if (size < (length + 1))
+        return ParseHelloResult::FAILURE;
+    size -= length + 1;
+
+    /* Extensions (2-byte length) */
+    if (size < 2)
+        return ParseHelloResult::FAILURE;
+    length = ntohs(*((const uint16_t*)pkt));
+    pkt += 2;
+    size -= 2;
+    if (size < length)
+        return ParseHelloResult::FAILURE;
+
+    /* We need at least type (2 bytes) and length (2 bytes) in the extension. */
+    while (length >= 4)
+    {
+        const ServiceSSLV3ExtensionSupportedVersion* ext = (const ServiceSSLV3ExtensionSupportedVersion*)pkt;
+        if (ntohs(ext->type) == SSL_EXT_SUPPORTED_VERSION)
+        {
+            /* Found supported version extension. */
+            if (length < sizeof(ServiceSSLV3ExtensionSupportedVersion))
+                return ParseHelloResult::FAILURE;
+
+            unsigned len = ntohs(ext->length);
+            if (len != sizeof(ext->supported_version))
+                return ParseHelloResult::FAILURE;
+
+            tls_connection_params->selected_tls_version = ntohs(ext->supported_version);
+            return ParseHelloResult::SUCCESS;
+        }
+
+        unsigned len = ntohs(ext->length) + offsetof(ServiceSSLV3ExtensionSupportedVersion, supported_version);
+        if (len > length)
+            return ParseHelloResult::FAILURE;
+
+        pkt += len;
+        length -= len;
+    }
+
+    return ParseHelloResult::FAILURE;
 }
 
 bool parse_server_certificates(SSLV3ServerCertData* server_cert_data)
@@ -704,17 +823,20 @@ bool parse_server_certificates(SSLV3ServerCertData* server_cert_data)
     if (!server_cert_data->certs_data or !server_cert_data->certs_len)
         return false;
 
-    char* common_name = nullptr;
-    char* org_unit = nullptr;
     const uint8_t* data = server_cert_data->certs_data;
     int len = server_cert_data->certs_len;
+
+    char* common_name = nullptr;
     int common_name_len = 0;
-    int org_unit_len  = 0;
+
+    char* org_unit = nullptr;
+    int org_unit_len = 0;
 
     while (len > 2 and !(common_name and org_unit))
     {
         X509* cert = nullptr;
-        X509_NAME* cert_name = nullptr;
+        X509_NAME* cert_subject = nullptr;
+        X509_NAME* cert_issuer = nullptr;
 
         int cert_len = ntoh3(data);
         data += 3;
@@ -728,19 +850,50 @@ bool parse_server_certificates(SSLV3ServerCertData* server_cert_data)
         if (!cert)
             break;
 
-        if (nullptr == (cert_name = X509_get_subject_name(cert)))
+        cert_subject = X509_get_subject_name(cert);
+        cert_issuer = X509_get_issuer_name(cert);
+
+        if (!cert_subject and !cert_issuer)
         {
             X509_free(cert);
             continue;
         }
 
-        if (!common_name)
+        if (!server_cert_data->issuer_info)
+        {
+            BIO* issuer_bio = BIO_new(BIO_s_mem());
+            int read_issuer_status = X509_NAME_print_ex(issuer_bio, cert_issuer, 0, XN_FLAG_RFC2253);
+            if (read_issuer_status)
+            {
+                BUF_MEM* bptr = nullptr;
+                BIO_get_mem_ptr(issuer_bio, &bptr);
+                server_cert_data->issuer_info = snort_strndup(bptr->data, bptr->length);
+                server_cert_data->issuer_info_strlen = bptr->length;
+            }
+            BIO_free(issuer_bio);
+        }
+        
+        if (!server_cert_data->subject_info)
+        {
+            BIO* subject_bio = BIO_new(BIO_s_mem());
+            int read_subject_status = X509_NAME_print_ex(subject_bio, cert_subject, 0, XN_FLAG_RFC2253);
+            if (read_subject_status)
+            {
+                BUF_MEM* bptr = nullptr;
+                BIO_get_mem_ptr(subject_bio, &bptr);
+                server_cert_data->subject_info = snort_strndup(bptr->data, bptr->length);
+                server_cert_data->subject_info_strlen = bptr->length;
+            }
+            BIO_free(subject_bio);
+        }
+
+        if (cert_subject and !common_name)
         {
             int lastpos = -1;
-            lastpos = X509_NAME_get_index_by_NID(cert_name, NID_commonName, lastpos);
+            lastpos = X509_NAME_get_index_by_NID(cert_subject, NID_commonName, lastpos);
             if (lastpos != -1)
             {
-                X509_NAME_ENTRY* e = X509_NAME_get_entry(cert_name, lastpos);
+                X509_NAME_ENTRY* e = X509_NAME_get_entry(cert_subject, lastpos);
                 const unsigned char* str_data = ASN1_STRING_get0_data(X509_NAME_ENTRY_get_data(e));
                 int length = strlen((const char*)str_data);
 
@@ -753,20 +906,19 @@ bool parse_server_certificates(SSLV3ServerCertData* server_cert_data)
             }
         }
 
-        if (!org_unit)
+        if (cert_subject and !org_unit)
         {
             int lastpos = -1;
-            lastpos = X509_NAME_get_index_by_NID(cert_name, NID_organizationalUnitName, lastpos);
+            lastpos = X509_NAME_get_index_by_NID(cert_subject, NID_organizationalUnitName, lastpos);
             if (lastpos != -1)
             {
-                X509_NAME_ENTRY* e = X509_NAME_get_entry(cert_name, lastpos);
+                X509_NAME_ENTRY* e = X509_NAME_get_entry(cert_subject, lastpos);
                 const unsigned char* str_data = ASN1_STRING_get0_data(X509_NAME_ENTRY_get_data(e));
                 org_unit_len = strlen((const char*)str_data);
                 org_unit = snort_strndup((const char*)(str_data), org_unit_len);
             }
         }
 
-        cert_name = nullptr;
         X509_free(cert);
     }
 
@@ -790,4 +942,25 @@ bool parse_server_certificates(SSLV3ServerCertData* server_cert_data)
     return true;
 }
 
+bool parse_server_key_exchange(const uint8_t* pkt, uint16_t size, TLSConnectionParams* tls_connection_params)
+{
+    if (tls_connection_params == nullptr)
+        return false;
+
+    if (size < 3)
+        return false;
+
+    uint8_t curve_type = *(pkt);
+
+    /* Non named curve, skipping */
+    if (curve_type != 3)
+    {
+        return false;
+    }
+
+    tls_connection_params->curve = ntohs(*(const uint16_t*)(pkt+1));
+
+    return true;
+}
+
 } // namespace snort
index 67340dc7276a1c289a6979e907811e6b28f75002..564a2cee64d264ad457e543e87e851f6fc32f631 100644 (file)
@@ -22,6 +22,8 @@
 #ifndef SSL_H
 #define SSL_H
 
+#include <string>
+
 #include "main/snort_types.h"
 
 #define SSL_NO_FLAG             0x00000000
@@ -81,6 +83,8 @@
     SSL_CUR_SERVER_KEYX_FLAG | SSL_CUR_CLIENT_KEYX_FLAG | \
     SSL_UNKNOWN_FLAG)
 
+#define SSL_TLS_METADATA_FINISH_PACKET (SSL_SERVER_KEYX_FLAG | SSL_CLIENT_KEYX_FLAG | SSL_CHANGE_CIPHER_FLAG)
+
 // Flag set when a client uses SSLv3/TLS backward compatibility and sends a
 // SSLv2 Hello specifying an SSLv3/TLS version.
 #define SSL_V3_BACK_COMPAT_V2   0x04000000
 
 /* Flags for additional info */
 #define SSL_ALERT_LVL_FATAL_FLAG    0x00000001
+#define SSL_TLS_METADATA_PUBLISHED  0x00000002
 
 /* The constants used below are from RFC 2246 */
 
@@ -168,12 +173,20 @@ struct SSL_handshake_t
     uint8_t length[3];
 };
 
+struct SSL_random_t
+{
+    uint32_t gmt_unix_time;
+    uint8_t random_bytes[28];
+};
+
 struct SSL_handshake_hello_t
 {
     uint8_t type;
     uint8_t length[3];
     uint8_t major;
     uint8_t minor;
+    SSL_random_t random;
+    uint8_t session_id_length;
 };
 
 // http://www.mozilla.org/projects/security/pki/nss/ssl/draft02.html
@@ -201,6 +214,13 @@ struct SSLv2_shello_t
     uint8_t minor;
 };
 
+struct SSLv2_client_master_key_t
+{
+    uint16_t length;
+    uint8_t type;
+    uint8_t cipher_spec[3];
+};
+
 struct SO_PUBLIC SSLV3ClientHelloData
 {
     ~SSLV3ClientHelloData();
@@ -213,13 +233,35 @@ struct SO_PUBLIC SSLV3ServerCertData
     ~SSLV3ServerCertData();
     void clear();
     /* While collecting certificates: */
-    unsigned certs_len;   // (Total) length of certificate(s).
+    unsigned certs_len = 0;         // (Total) length of certificate(s).
     uint8_t* certs_data = nullptr;  // Certificate(s) data (each proceeded by length (3 bytes)).
     /* Data collected from certificates afterwards: */
     char* common_name = nullptr;
-    int common_name_strlen;
+    int common_name_strlen = 0;
     char* org_unit = nullptr;
-    int org_unit_strlen;
+    int org_unit_strlen = 0;
+    char* issuer_info = nullptr;
+    int issuer_info_strlen = 0;
+    char* subject_info = nullptr;
+    int subject_info_strlen = 0;
+};
+
+struct SO_PUBLIC TLSConnectionParams
+{
+    uint16_t curve = 0;
+    uint16_t cipher = 0;
+    uint16_t selected_tls_version = 0;
+};
+
+struct SO_PUBLIC TLSConnectionData
+{
+    void process(const SSLV3ServerCertData& cert_data);
+    void process(const SSLV3ClientHelloData& client_hello_data);
+    
+    std::string server_name_identifier;
+    std::string subject_info;
+    std::string issuer_info;
+    TLSConnectionParams tls_params;
 };
 
 enum class SSLV3RecordType : uint8_t
@@ -270,8 +312,16 @@ struct ServiceSSLV3ExtensionServerName
     /* String follows. */
 };
 
+struct ServiceSSLV3ExtensionSupportedVersion
+{
+    uint16_t type;
+    uint16_t length;
+    uint16_t supported_version;
+};
+
 /* Extension types. */
 #define SSL_EXT_SERVER_NAME 0
+#define SSL_EXT_SUPPORTED_VERSION 43
 
 #define SSL_V2_MIN_LEN 5
 
@@ -307,20 +357,22 @@ struct ServiceSSLV3ExtensionServerName
 namespace snort
 {
 
-enum class ParseCHResult
+enum ParseHelloResult : uint8_t
 {
     SUCCESS = 0,
     FRAGMENTED_PACKET = 1,
-    FAILED = 2
+    FAILURE = 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);
+    SSLV3ClientHelloData* data = nullptr, SSLV3ServerCertData* server_cert_data = nullptr, TLSConnectionParams* tls_connection_params = nullptr);
 
-    ParseCHResult parse_client_hello_data(const uint8_t* pkt, uint16_t size, SSLV3ClientHelloData*);
+    ParseHelloResult parse_client_hello_data(const uint8_t* pkt, uint16_t size, SSLV3ClientHelloData*);
+    ParseHelloResult parse_server_hello_data(const uint8_t* pkt, uint16_t size, TLSConnectionParams*);
     bool parse_server_certificates(SSLV3ServerCertData* server_cert_data);
+    bool parse_server_key_exchange(const uint8_t* pkt, uint16_t size, TLSConnectionParams*);
 
 SO_PUBLIC bool IsTlsClientHello(const uint8_t* ptr, const uint8_t* end);
 SO_PUBLIC bool IsTlsServerHello(const uint8_t* ptr, const uint8_t* end);
index e6a349053d2cb6c666c5faaf95d7fe270bcf9d8a..33cead877332fbe8d589f356d0361f70c5f91a51 100644 (file)
@@ -34,6 +34,7 @@ using namespace snort;
 
 typedef struct X509_name_entry_st X509_NAME_ENTRY;
 X509_NAME *X509_get_subject_name(const X509 *a) { return nullptr; }
+X509_NAME *X509_get_issuer_name(const X509 *a) { return nullptr; }
 void X509_free(X509* a) { }
 #if OPENSSL_VERSION_NUMBER < 0x30000000L
 int X509_NAME_get_index_by_NID(X509_NAME *name, int nid, int lastpos)
@@ -44,10 +45,12 @@ int X509_NAME_get_index_by_NID(const X509_NAME *name, int nid, int lastpos)
 X509_NAME_ENTRY *X509_NAME_get_entry(const X509_NAME *name, int loc) { return nullptr; }
 ASN1_STRING *X509_NAME_ENTRY_get_data(const X509_NAME_ENTRY *ne) { return nullptr; }
 const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *x) { return nullptr; }
-X509* d2i_X509(X509 **a, const unsigned char **in, long len)
-{
-    return nullptr;
-}
+X509* d2i_X509(X509 **a, const unsigned char **in, long len) { return nullptr; }
+int X509_NAME_print_ex(BIO *out, const X509_NAME *nm, int indent, unsigned long flags) { return 0; }
+BIO *BIO_new(const BIO_METHOD *type) { return nullptr; }
+int BIO_free(BIO *a) { return 0; }
+long BIO_ctrl(BIO *bp, int cmd, long larg, void *parg) { return 0; }
+const BIO_METHOD *BIO_s_mem(void) { return nullptr; }
 
 namespace snort
 {
@@ -84,7 +87,97 @@ TEST(ssl_protocol_tests, cert_data_incomplete_len_2)
     CHECK_EQUAL(0, test_data.certs_len);
 }
 
+TEST(ssl_protocol_tests, parse_server_key_exchange_normal)
+{
+    TLSConnectionParams tls_params;
+    uint8_t test_data[3] = { 0x03, 0xFF, 0xFF }; // Valid curve type and 0xFFFF curve id
+    auto result = parse_server_key_exchange(test_data, sizeof(test_data), &tls_params);
+    CHECK_EQUAL(true, result);
+    CHECK_EQUAL(0xFFFF, tls_params.curve);
+}
+
+TEST(ssl_protocol_tests, parse_server_key_exchange_invalid_curve_type)
+{
+    TLSConnectionParams tls_params;
+    uint8_t test_data[3] = { 0x02, 0xFF, 0xFF }; // Invalid curve type
+    auto result = parse_server_key_exchange(test_data, sizeof(test_data), &tls_params);
+    CHECK_EQUAL(false, result);
+    CHECK_EQUAL(0, tls_params.curve);
+}
+
+TEST(ssl_protocol_tests, parse_server_key_exchange_invalid_len)
+{
+    TLSConnectionParams tls_params;
+    uint8_t test_data[2] = { 0x03, 0xFF }; // Invalid length, should be at least 3 bytes
+    auto result = parse_server_key_exchange(test_data, sizeof(test_data), &tls_params);
+    CHECK_EQUAL(false, result);
+    CHECK_EQUAL(0, tls_params.curve);
+}
+
+TEST(ssl_protocol_tests, parse_server_hello_tls_1_3)
+{
+    // This is a minimal valid Server Hello packet with TLS 1.3 version in extensions
+    uint8_t test_data[] = {
+        0x02,                   // Handshake Type: Server Hello
+        0x00, 0x00, 0x4e,       // Length
+        0x03, 0x03,             // Version TLS 1.2
+        // Random (32 bytes)
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        // Session ID length
+        0x20,                   
+        // Session ID (32 bytes)
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        // Cipher Suite (2 bytes)
+        0xc0, 0x2b,
+        // Compression Method (1 byte)
+        0x00,
+        // Extensions length (2 bytes)
+        0x00, 0x06,
+        // Extension: Supported Versions (type=43 length=2)
+        0x00, 0x2b,
+        0x00, 0x02,
+        // Supported Version: TLS 1.3 (0x0304)
+        0x03, 0x04
+    };
+
+    TLSConnectionParams tls_params;
+    auto result = parse_server_hello_data(test_data, sizeof(test_data), &tls_params);
+    CHECK_EQUAL(ParseHelloResult::SUCCESS, result);
+    CHECK_EQUAL(0x0304, tls_params.selected_tls_version);
+    CHECK_EQUAL(0xc02b, tls_params.cipher);
+}
+
+TEST(ssl_protocol_tests, parse_server_hello_invalid_packet_len)
+{
+    // This is an incomplete Server Hello packet
+    uint8_t test_data[] = {
+        0x02,                   // Handshake Type: Server Hello
+        0x00, 0x00, 0xF6,       // Length invalid (too large for provided data)
+        0x03, 0x03,             // Version TLS 1.2
+        // Random (32 bytes)
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        // Session ID length
+        0x20,
+        // Session ID truncated
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00
+    };
+
+    TLSConnectionParams tls_params;
+    auto result = parse_server_hello_data(test_data, sizeof(test_data), &tls_params);
+    CHECK_EQUAL(ParseHelloResult::FRAGMENTED_PACKET, result);
+}
+
 int main(int argc, char** argv)
 {
     return CommandLineTestRunner::RunAllTests(argc, argv);
-}
\ No newline at end of file
+}
index 5e2fde383fe12d8d4414f0ab588ee486a26569c5..5ba3f0a31e3abe7b03f0206e98f6a83f28b9fc8c 100644 (file)
@@ -31,6 +31,7 @@ struct SslEventIds
     {
         CHELLO_SERVER_NAME,
         SERVER_COMMON_NAME,
+        SSL_TLS_METADATA_EVENT,
 
         num_ids
     };
@@ -74,4 +75,21 @@ private:
     const snort::Packet* packet;
 };
 
+class SslTlsMetadataBaseEvent : public snort::DataEvent
+{
+public:
+    SslTlsMetadataBaseEvent() = default;
+    virtual ~SslTlsMetadataBaseEvent() override = default;
+
+    /* Values expected to be in host machine byte order */
+    virtual uint16_t get_version() const = 0;
+    virtual uint16_t get_curve() const = 0;
+    virtual uint16_t get_cipher() const = 0;
+    virtual const std::string& get_server_name_identifier() const = 0;
+    virtual const std::string& get_subject() const = 0;
+    virtual const std::string& get_issuer() const = 0;
+    virtual const std::string& get_validation_status() const = 0;
+    virtual const std::string& get_module_identifier() const = 0;
+};
+
 #endif
index b28129ad785a0f9bd253e2ea36c258a94fea1e01..734c3956d7155decb71b994d1c09a5fe84735af8 100644 (file)
@@ -20,6 +20,9 @@
 #define SSL_FLOW_DATA_H
 
 #include "flow/flow_data.h"
+#include "protocols/ssl.h"
+
+#include <string>
 
 #define GID_SSL 137
 
@@ -31,6 +34,7 @@
 struct SSLData
 {
     uint32_t ssn_flags;
+    uint32_t info_flags;
     uint16_t partial_rec_len[4];
 };
 
index 72eeef30a2042eade79ebe4e604d5ee5f67a3d75..5e7fc34ec0a4638f579da10390b709846b2daaf5 100644 (file)
@@ -35,7 +35,6 @@
 #include "protocols/ssl.h"
 #include "pub_sub/finalize_packet_event.h"
 #include "pub_sub/opportunistic_tls_event.h"
-#include "pub_sub/ssl_events.h"
 #include "stream/stream.h"
 #include "stream/stream_splitter.h"
 #include "trace/trace_api.h"
@@ -85,10 +84,74 @@ const PegInfo ssl_peg_names[] =
     { CountType::END, nullptr, nullptr }
 };
 
-SslFlowData::SslFlowData() : SslBaseFlowData()
+//-------------------------------------------------------------------------
+// class stuff
+//-------------------------------------------------------------------------
+class Ssl : public Inspector
+{
+public:
+    Ssl(SSL_PROTO_CONF*);
+    ~Ssl() override;
+
+    void show(const SnortConfig*) const override;
+    void eval(Packet*) override;
+    bool configure(SnortConfig*) override;
+
+    StreamSplitter* get_splitter(bool c2s) override
+    { return new SslSplitter(c2s); }
+
+    const static std::string s_name;
+
+private:
+    SSL_PROTO_CONF* config;
+};
+
+const std::string Ssl::s_name = "ssl";
+
+uint16_t SslMetadataEvent::get_version() const
+{
+    return tls_connection_data.tls_params.selected_tls_version;
+}
+
+const std::string& SslMetadataEvent::get_server_name_identifier() const
+{
+    return tls_connection_data.server_name_identifier;
+}
+
+uint16_t SslMetadataEvent::get_curve() const
+{
+    return tls_connection_data.tls_params.curve;
+}
+
+uint16_t SslMetadataEvent::get_cipher() const
+{
+    return tls_connection_data.tls_params.cipher;
+}
+
+const std::string& SslMetadataEvent::get_subject() const
+{
+    return tls_connection_data.subject_info;
+}
+
+const std::string& SslMetadataEvent::get_issuer() const
+{
+    return tls_connection_data.issuer_info;
+}
+
+const std::string& SslMetadataEvent::get_validation_status() const
+{
+    return validation_status;
+}
+
+const std::string& SslMetadataEvent::get_module_identifier() const
+{
+    return Ssl::s_name;
+}
+
+SslFlowData::SslFlowData(const snort::Flow* flow) : SslBaseFlowData(),
+    finalize_info(), tls_connection_data(), flow_handle(flow)
 {
-    memset(&session, 0, sizeof(session));
-    finalize_info = {};
+    memset(&session, 0, sizeof(SSLData));
     sslstats.concurrent_sessions++;
     if(sslstats.max_concurrent_sessions < sslstats.concurrent_sessions)
         sslstats.max_concurrent_sessions = sslstats.concurrent_sessions;
@@ -98,13 +161,19 @@ SslFlowData::~SslFlowData()
 {
     assert(sslstats.concurrent_sessions > 0);
     sslstats.concurrent_sessions--;
+
+    if (!(session.info_flags & SSL_TLS_METADATA_PUBLISHED))
+    {
+        SslMetadataEvent event(tls_connection_data);
+        DataBus::publish(pub_id, SslEventIds::SSL_TLS_METADATA_EVENT, event, const_cast<snort::Flow*>(flow_handle));
+    }
 }
 
-static SSLData* SetNewSSLData(Packet* p)
+static SslFlowData* SetNewSSLData(Packet* p)
 {
-    SslFlowData* fd = new SslFlowData;
+    SslFlowData* fd = new SslFlowData(p->flow);
     p->flow->set_flow_data(fd);
-    return &fd->get_session();
+    return fd;
 }
 
 static void SSL_UpdateCounts(const uint32_t new_flags)
@@ -285,21 +354,23 @@ static void snort_ssl(SSL_PROTO_CONF* config, Packet* p)
     Profile profile(sslPerfStats);  // cppcheck-suppress unreadVariable
 
     /* Attempt to get a previously allocated SSL block. */
-    SSLData* sd = SslBaseFlowData::get_ssl_session_data(p->flow);
+    SslFlowData* fd = static_cast<SslFlowData*>(p->flow->get_flow_data(SslFlowData::get_ssl_inspector_id()));
 
-    if (sd == nullptr)
+    if (fd == nullptr)
     {
         /* Check the stream session. If it does not currently
          * have our SSL data-block attached, create one.
          */
-        sd = SetNewSSLData(p);
+        fd = SetNewSSLData(p);
 
-        if ( !sd )
+        if ( !fd )
             // Could not get/create the session data for this packet.
             return;
     }
 
-    SSL_CLEAR_TEMPORARY_FLAGS(sd->ssn_flags);
+    SSLData& sd = fd->get_session();
+
+    SSL_CLEAR_TEMPORARY_FLAGS(sd.ssn_flags);
 
     uint8_t dir = (p->is_from_server()) ? 1 : 0;
     uint8_t index = (p->packet_flags & PKT_REBUILT_STREAM) ? 2 : 0;
@@ -307,10 +378,25 @@ static void snort_ssl(SSL_PROTO_CONF* config, Packet* p)
     uint8_t heartbleed_type = 0;
     uint32_t info_flags = 0;
     SSLV3ClientHelloData client_hello_data;
-    SSLV3ServerCertData server_cert_data;
-    uint32_t new_flags = SSL_decode(p->data, (int)p->dsize, p->packet_flags, sd->ssn_flags,
-        &heartbleed_type, &(sd->partial_rec_len[dir+index]), config->max_heartbeat_len, &info_flags, &client_hello_data,
-        &server_cert_data);
+    SSLV3ServerCertData server_cert;
+    uint32_t new_flags = SSL_decode(p->data, (int)p->dsize, p->packet_flags, sd.ssn_flags,
+        &heartbleed_type, &(sd.partial_rec_len[dir+index]), config->max_heartbeat_len, &info_flags, &client_hello_data,
+        &server_cert, &fd->get_tls_connection_data().tls_params);
+    sd.info_flags |= info_flags;
+
+    if (new_flags & SSL_CERTIFICATE_FLAG)
+        fd->get_tls_connection_data().process(server_cert);
+
+    if (new_flags & SSL_CLIENT_HELLO_FLAG)
+        fd->get_tls_connection_data().process(client_hello_data);
+
+    if (((new_flags & SSL_TLS_METADATA_FINISH_PACKET) or (fd->get_tls_connection_data().tls_params.selected_tls_version == 0x0304))
+        and !(sd.info_flags & SSL_TLS_METADATA_PUBLISHED))
+    {
+        SslMetadataEvent event(fd->get_tls_connection_data());
+        DataBus::publish(pub_id, SslEventIds::SSL_TLS_METADATA_EVENT, event, p->flow);
+        sd.info_flags |= SSL_TLS_METADATA_PUBLISHED;
+    }
 
     if (client_hello_data.host_name != nullptr)
     {
@@ -318,9 +404,9 @@ static void snort_ssl(SSL_PROTO_CONF* config, Packet* p)
         DataBus::publish(pub_id, SslEventIds::CHELLO_SERVER_NAME, event);
     }
 
-    if (server_cert_data.common_name != nullptr)
+    if (server_cert.common_name != nullptr)
     {
-        SslServerCommonNameEvent event(server_cert_data.common_name, p);
+        SslServerCommonNameEvent event(server_cert.common_name, p);
         DataBus::publish(pub_id, SslEventIds::SERVER_COMMON_NAME, event);
     }
 
@@ -343,7 +429,7 @@ static void snort_ssl(SSL_PROTO_CONF* config, Packet* p)
             DetectionEngine::queue_event(GID_SSL, SSL_ALERT_HB_RESPONSE);
         }
     }
-    if (sd->ssn_flags & SSL_ENCRYPTED_FLAG )
+    if (sd.ssn_flags & SSL_ENCRYPTED_FLAG )
     {
         sslstats.decoded++;
 
@@ -355,7 +441,7 @@ static void snort_ssl(SSL_PROTO_CONF* config, Packet* p)
             sslstats.disabled++;
         }
 
-        sd->ssn_flags |= new_flags;
+        sd.ssn_flags |= new_flags;
 
         return;
     }
@@ -365,17 +451,17 @@ static void snort_ssl(SSL_PROTO_CONF* config, Packet* p)
 // compatibility flag and the SSLv2 flag since this session will continue
 // as SSLv3/TLS.
 
-    if ((sd->ssn_flags & SSL_V3_BACK_COMPAT_V2) && SSL_V3_SERVER_HELLO(new_flags))
-        sd->ssn_flags &= ~(SSL_VER_SSLV2_FLAG|SSL_V3_BACK_COMPAT_V2);
+    if ((sd.ssn_flags & SSL_V3_BACK_COMPAT_V2) && SSL_V3_SERVER_HELLO(new_flags))
+        sd.ssn_flags &= ~(SSL_VER_SSLV2_FLAG|SSL_V3_BACK_COMPAT_V2);
 
-    if ( (SSL_IS_CHELLO(new_flags) && SSL_IS_CHELLO(sd->ssn_flags) && SSL_IS_SHELLO(sd->ssn_flags) )
-            || (SSL_IS_CHELLO(new_flags) && SSL_IS_SHELLO(sd->ssn_flags) ))
+    if ( (SSL_IS_CHELLO(new_flags) && SSL_IS_CHELLO(sd.ssn_flags) && SSL_IS_SHELLO(sd.ssn_flags) )
+            || (SSL_IS_CHELLO(new_flags) && SSL_IS_SHELLO(sd.ssn_flags) ))
     {
         DetectionEngine::queue_event(GID_SSL, SSL_INVALID_CLIENT_HELLO);
     }
     else if (!(config->trustservers))
     {
-        if ( (SSL_IS_SHELLO(new_flags) && !SSL_IS_CHELLO(sd->ssn_flags) ))
+        if ( (SSL_IS_SHELLO(new_flags) && !SSL_IS_CHELLO(sd.ssn_flags) ))
         {
             if (!(Stream::missed_packets(p->flow, SSN_DIR_FROM_CLIENT)))
                 DetectionEngine::queue_event(GID_SSL, SSL_INVALID_SERVER_HELLO);
@@ -392,15 +478,15 @@ static void snort_ssl(SSL_PROTO_CONF* config, Packet* p)
 
     if (SSL_IS_ALERT(new_flags))
     {
-        sd->ssn_flags = SSLPP_process_alert(config, sd->ssn_flags, new_flags, p, info_flags);
+        sd.ssn_flags = SSLPP_process_alert(config, sd.ssn_flags, new_flags, p, info_flags);
     }
     else if (SSL_IS_HANDSHAKE(new_flags))
     {
-        sd->ssn_flags = SSLPP_process_hs(sd->ssn_flags, new_flags);
+        sd.ssn_flags = SSLPP_process_hs(sd.ssn_flags, new_flags);
     }
     else if (SSL_IS_APP(new_flags))
     {
-        sd->ssn_flags = SSLPP_process_app(config, sd->ssn_flags, new_flags, p);
+        sd.ssn_flags = SSLPP_process_app(config, sd.ssn_flags, new_flags, p);
     }
     else if (SSL_IS_CHANGE_CIPHER(new_flags))
     {
@@ -408,53 +494,31 @@ static void snort_ssl(SSL_PROTO_CONF* config, Packet* p)
          * the encrypted handshake message is inspected, and attempts to process some random type and it fails.
          * To avoid this situation, update the 'change cipher spec' flag in the session to skip processing
          * the encrypted handshake message.*/
-        sd->ssn_flags |= SSL_CHANGE_CIPHER_FLAG;
+        sd.ssn_flags |= SSL_CHANGE_CIPHER_FLAG;
     }
     else
     {
         /* Different record type that we don't care about.
          *          * Either it's a 'change cipher spec' or we failed to recognize the
          *                   * record type.  Do not update session data */
-        SSLPP_process_other(config, sd, new_flags, p);
+        SSLPP_process_other(config, &sd, new_flags, p);
 
         /* Application data is updated inside of SSLPP_process_other */
 
         return;
     }
 
-    sd->ssn_flags |= new_flags;
+    sd.ssn_flags |= new_flags;
 }
 
-//-------------------------------------------------------------------------
-// class stuff
-//-------------------------------------------------------------------------
-static const char* s_name = "ssl";
-
-class Ssl : public Inspector
-{
-public:
-    Ssl(SSL_PROTO_CONF*);
-    ~Ssl() override;
-
-    void show(const SnortConfig*) const override;
-    void eval(Packet*) override;
-    bool configure(SnortConfig*) override;
-
-    StreamSplitter* get_splitter(bool c2s) override
-    { return new SslSplitter(c2s); }
-
-private:
-    SSL_PROTO_CONF* config;
-};
-
 class SslStartTlsEventtHandler : public DataHandler
 {
 public:
-    SslStartTlsEventtHandler() : DataHandler(s_name) { }
+    SslStartTlsEventtHandler() : DataHandler(Ssl::s_name.c_str()) { }
 
     void handle(DataEvent&, Flow* flow) override
     {
-        SslFlowData* fd = new SslFlowData;
+        SslFlowData* fd = new SslFlowData(flow);
         fd->finalize_info.orig_flag = flow->flags.trigger_finalize_event;
         fd->finalize_info.switch_in = true;
         flow->set_flow_data(fd);
@@ -465,7 +529,7 @@ public:
 class SslFinalizePacketHandler : public DataHandler
 {
 public:
-    SslFinalizePacketHandler() : DataHandler(s_name) {}
+    SslFinalizePacketHandler() : DataHandler(Ssl::s_name.c_str()) {}
 
     void handle(DataEvent& e, Flow*) override
     {
@@ -477,7 +541,7 @@ public:
             pkt->flow->flags.trigger_finalize_event = fd->finalize_info.orig_flag;
             fd->finalize_info.switch_in = false;
             pkt->flow->set_proxied();
-            pkt->flow->set_service(const_cast<Packet*>(pkt), s_name);
+            pkt->flow->set_service(const_cast<Packet*>(pkt), Ssl::s_name.c_str());
         }
     }
 };
@@ -565,7 +629,7 @@ const InspectApi ssl_api =
     IT_SERVICE,
     PROTO_BIT__PDU,
     nullptr, // buffers
-    s_name,
+    Ssl::s_name.c_str(),
     ssl_init,
     nullptr, // pterm
     nullptr, // tinit
index dd7cf8eafc12f72db66348ae49d9c6f1292b5b15..c50d1b00cb828ea0ca6daac1ff6862fc9d190a54 100644 (file)
 // Implementation header with definitions, datatypes and flowdata class for SSL service inspector.
 
 #include "flow/flow.h"
+#include "pub_sub/ssl_events.h"
 #include "ssl_flow_data.h"
 
+class SslMetadataEvent : public SslTlsMetadataBaseEvent
+{
+public:
+    SslMetadataEvent(const TLSConnectionData& conn_data)
+        : tls_connection_data(conn_data)
+    { }
+
+    virtual ~SslMetadataEvent() override
+    { }
+
+    uint16_t get_version() const override;
+    uint16_t get_curve() const override;
+    uint16_t get_cipher() const override;
+    const std::string& get_server_name_identifier() const override;
+    const std::string& get_subject() const override;
+    const std::string& get_issuer() const override;
+    const std::string& get_validation_status() const override;
+    const std::string& get_module_identifier() const override;
+
+private:
+    TLSConnectionData tls_connection_data;
+    std::string validation_status;
+};
+
 class SslFlowData : public SslBaseFlowData
 {
 public:
-    SslFlowData();
+    SslFlowData(const snort::Flow* flow);
     ~SslFlowData() override;
 
     static void init()
@@ -36,6 +61,9 @@ public:
     SSLData& get_session() override
     { return session; }
 
+    TLSConnectionData& get_tls_connection_data()
+    { return tls_connection_data; }
+
 public:
     struct {
         bool orig_flag : 1;
@@ -44,6 +72,8 @@ public:
 
 private:
     SSLData session;
+    TLSConnectionData tls_connection_data;
+    const snort::Flow* flow_handle;
 };
 
 #endif