From: Oleksandr Stepanov -X (ostepano - SOFTSERVE INC at Cisco) Date: Thu, 30 Oct 2025 18:18:25 +0000 (+0000) Subject: Pull request #4933: ssl: SSL extractor event X-Git-Tag: 3.9.7.0~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=68d4fb3805ca22782794ad9638c554256f4cd10a;p=thirdparty%2Fsnort3.git Pull request #4933: ssl: SSL extractor event Merge in SNORT/snort3 from ~OSTEPANO/snort3:ssl_metadata_extractor to master Squashed commit of the following: commit 45a8012221075eb0d84589631d543b9151d25c95 Author: Oleksandr Stepanov Date: Thu Sep 11 04:42:20 2025 -0400 ssl: SSL extractor event --- diff --git a/doc/user/extractor.txt b/doc/user/extractor.txt index 0e5db0529..7c1b73904 100644 --- a/doc/user/extractor.txt +++ b/doc/user/extractor.txt @@ -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 diff --git a/src/network_inspectors/appid/service_plugins/service_ssl.cc b/src/network_inspectors/appid/service_plugins/service_ssl.cc index a8dc6ee5c..41e5705e8 100644 --- a/src/network_inspectors/appid/service_plugins/service_ssl.cc +++ b/src/network_inspectors/appid/service_plugins/service_ssl.cc @@ -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; } diff --git a/src/network_inspectors/extractor/CMakeLists.txt b/src/network_inspectors/extractor/CMakeLists.txt index 46f8dc36b..51e4835a5 100644 --- a/src/network_inspectors/extractor/CMakeLists.txt +++ b/src/network_inspectors/extractor/CMakeLists.txt @@ -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 ) diff --git a/src/network_inspectors/extractor/extractor.cc b/src/network_inspectors/extractor/extractor.cc index 15ab87359..adde8964a 100644 --- a/src/network_inspectors/extractor/extractor.cc +++ b/src/network_inspectors/extractor/extractor.cc @@ -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", diff --git a/src/network_inspectors/extractor/extractor_enums.h b/src/network_inspectors/extractor/extractor_enums.h index 22961a23b..3e586b652 100644 --- a/src/network_inspectors/extractor/extractor_enums.h +++ b/src/network_inspectors/extractor/extractor_enums.h @@ -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: diff --git a/src/network_inspectors/extractor/extractor_service.cc b/src/network_inspectors/extractor/extractor_service.cc index e153f3d5d..2b4495792 100644 --- a/src/network_inspectors/extractor/extractor_service.cc +++ b/src/network_inspectors/extractor/extractor_service.cc @@ -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& srv_fields, + const std::vector& 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())); diff --git a/src/network_inspectors/extractor/extractor_service.h b/src/network_inspectors/extractor/extractor_service.h index e93164a7e..de9d046ee 100644 --- a/src/network_inspectors/extractor/extractor_service.h +++ b/src/network_inspectors/extractor/extractor_service.h @@ -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& fields, + const std::vector& 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 index 000000000..7d8bb0556 --- /dev/null +++ b/src/network_inspectors/extractor/extractor_ssl.cc @@ -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 + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "extractor_ssl.h" + +#include + +#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 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 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 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 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& 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 index 000000000..a21bb529f --- /dev/null +++ b/src/network_inspectors/extractor/extractor_ssl.h @@ -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 + +#ifndef EXTRACTOR_SSL_H +#define EXTRACTOR_SSL_H + +#include + +#include "extractors.h" + +class SslExtractorFlowData; + +class SslExtractor : public ExtractorEvent +{ +public: + SslExtractor(Extractor&, uint32_t tenant, const std::vector& fields); + + void handle(DataEvent&, Flow*); + +private: + using Server = Handler; + + void internal_tinit(const snort::Connector::ID*) override; + + static THREAD_LOCAL const snort::Connector::ID* log_id; +}; + +#endif diff --git a/src/protocols/ssl.cc b/src/protocols/ssl.cc index eae87c9ee..dd3280e06 100644 --- a/src/protocols/ssl.cc +++ b/src/protocols/ssl.cc @@ -25,6 +25,7 @@ #include "ssl.h" +#include #include #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 diff --git a/src/protocols/ssl.h b/src/protocols/ssl.h index 67340dc72..564a2cee6 100644 --- a/src/protocols/ssl.h +++ b/src/protocols/ssl.h @@ -22,6 +22,8 @@ #ifndef SSL_H #define SSL_H +#include + #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 @@ -102,6 +106,7 @@ /* 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); diff --git a/src/protocols/test/ssl_protocol_test.cc b/src/protocols/test/ssl_protocol_test.cc index e6a349053..33cead877 100644 --- a/src/protocols/test/ssl_protocol_test.cc +++ b/src/protocols/test/ssl_protocol_test.cc @@ -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 +} diff --git a/src/pub_sub/ssl_events.h b/src/pub_sub/ssl_events.h index 5e2fde383..5ba3f0a31 100644 --- a/src/pub_sub/ssl_events.h +++ b/src/pub_sub/ssl_events.h @@ -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 diff --git a/src/service_inspectors/ssl/ssl_flow_data.h b/src/service_inspectors/ssl/ssl_flow_data.h index b28129ad7..734c3956d 100644 --- a/src/service_inspectors/ssl/ssl_flow_data.h +++ b/src/service_inspectors/ssl/ssl_flow_data.h @@ -20,6 +20,9 @@ #define SSL_FLOW_DATA_H #include "flow/flow_data.h" +#include "protocols/ssl.h" + +#include #define GID_SSL 137 @@ -31,6 +34,7 @@ struct SSLData { uint32_t ssn_flags; + uint32_t info_flags; uint16_t partial_rec_len[4]; }; diff --git a/src/service_inspectors/ssl/ssl_inspector.cc b/src/service_inspectors/ssl/ssl_inspector.cc index 72eeef30a..5e7fc34ec 100644 --- a/src/service_inspectors/ssl/ssl_inspector.cc +++ b/src/service_inspectors/ssl/ssl_inspector.cc @@ -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(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(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(pkt), s_name); + pkt->flow->set_service(const_cast(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 diff --git a/src/service_inspectors/ssl/ssl_inspector.h b/src/service_inspectors/ssl/ssl_inspector.h index dd7cf8eaf..c50d1b00c 100644 --- a/src/service_inspectors/ssl/ssl_inspector.h +++ b/src/service_inspectors/ssl/ssl_inspector.h @@ -22,12 +22,37 @@ // 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