]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
output/tls: Allow logging of sv-handshake params
authorRichard McConnell <Richard_McConnell@rapid7.com>
Fri, 25 Apr 2025 09:03:41 +0000 (10:03 +0100)
committerVictor Julien <victor@inliniac.net>
Fri, 16 May 2025 19:33:54 +0000 (21:33 +0200)
Ticket: 6695

"server_handshake" which logs the following:
1. TLS version used during handshake
2. The chosen cipher suite, excluding GREASE
3. TLS extensions, excluding GREASE

doc/userguide/output/eve/eve-json-format.rst
etc/schema.json
rust/src/handshake.rs
src/app-layer-ssl.c
src/output-json-tls.c
suricata.yaml.in

index 2890b3f556b5288250c9464e160e39bcdfbb3931..ff2f3643d9c6e74fd3db23cbef3166b8b3ca2445 100644 (file)
@@ -1056,6 +1056,8 @@ In addition to this, custom logging also allows the following fields:
 * "client_handshake": structure containing "version", "ciphers" ([u16]), "exts" ([u16]), "sig_algs" ([u16]),
   for client hello supported cipher suites, extensions, and signature algorithms,
   respectively, in the order that they're mentioned (ie. unsorted)
+* "server_handshake": structure containing "version", "chosen cipher", "exts" ([u16]), for server hello
+  in the order that they're mentioned (ie. unsorted)
 
 Examples
 ~~~~~~~~
index 37c09111a677a8a926d147a24101f49538d8c4f5..a58937646223da17ee307f022fab1e3d68d6a116 100644 (file)
                         }
                     }
                 },
+                "server_handshake": {
+                    "type": "object",
+                    "properties": {
+                        "version": {
+                            "description": "TLS version in server hello",
+                            "type": "string"
+                        },
+                        "cipher": {
+                            "description": "TLS server's chosen cipher",
+                            "type": "integer"
+                        },
+                        "exts": {
+                            "description": "TLS server extension(s)",
+                            "type": "array",
+                            "minItems": 1,
+                            "items": {
+                                "type": "integer"
+                            }
+                        }
+                    }
+                },
                 "server_alpns": {
                     "description": "TLS server ALPN field(s)",
                     "type": "array",
index df33af4dcacdf76c12b7958cf37667dd19f3ff4a..345ae4009680111067b8c7384fa0be162384f803 100644 (file)
@@ -146,6 +146,12 @@ impl HandshakeParams {
         Ok(())
     }
 
+    fn log_first_cipher(&self, js: &mut JsonBuilder) -> Result<(), JsonError> {
+        let chosen = self.ciphersuites.first().map(|&v| *v).unwrap_or(0);
+        js.set_uint("cipher", chosen)?;
+        Ok(())
+    }
+
     fn log_ciphers(&self, js: &mut JsonBuilder) -> Result<(), JsonError> {
         if self.ciphersuites.is_empty() {
             return Ok(());
@@ -242,6 +248,14 @@ pub unsafe extern "C" fn SCTLSHandshakeLogCiphers(hs: &HandshakeParams, js: *mut
     return hs.log_ciphers(js.as_mut().unwrap()).is_ok()
 }
 
+#[no_mangle]
+pub unsafe extern "C" fn SCTLSHandshakeLogFirstCipher(hs: &HandshakeParams, js: *mut JsonBuilder) -> bool {
+    if js.is_null() {
+        return false;
+    }
+    return hs.log_first_cipher(js.as_mut().unwrap()).is_ok()
+}
+
 #[no_mangle]
 pub unsafe extern "C" fn SCTLSHandshakeLogExtensions(hs: &HandshakeParams, js: *mut JsonBuilder) -> bool {
     if js.is_null() {
index c16fc5544ddbaf819c68e988b882799834c5ce4e..6ec0adbf23dc0aedc27119c8a2521e7f36c8c96e 100644 (file)
@@ -664,7 +664,8 @@ static inline int TLSDecodeHSHelloVersion(SSLState *ssl_state,
     uint16_t version = (uint16_t)(*input << 8) | *(input + 1);
     ssl_state->curr_connp->version = version;
 
-    if (ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) {
+    if (ssl_state->current_flags &
+            (SSL_AL_FLAG_STATE_CLIENT_HELLO | SSL_AL_FLAG_STATE_SERVER_HELLO)) {
         SCTLSHandshakeSetTLSVersion(ssl_state->curr_connp->hs, version);
     }
 
@@ -836,7 +837,8 @@ static inline int TLSDecodeHSHelloCipherSuites(SSLState *ssl_state,
         input += 2;
 
         if (TLSDecodeValueIsGREASE(cipher_suite) != 1) {
-            if (ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) {
+            if (ssl_state->current_flags &
+                    (SSL_AL_FLAG_STATE_CLIENT_HELLO | SSL_AL_FLAG_STATE_SERVER_HELLO)) {
                 SCTLSHandshakeAddCipher(ssl_state->curr_connp->hs, cipher_suite);
             }
             if (enable_ja3) {
@@ -1428,7 +1430,8 @@ static inline int TLSDecodeHSHelloExtensions(SSLState *ssl_state,
             }
         }
 
-        if (ssl_state->current_flags & SSL_AL_FLAG_STATE_CLIENT_HELLO) {
+        if (ssl_state->current_flags &
+                (SSL_AL_FLAG_STATE_CLIENT_HELLO | SSL_AL_FLAG_STATE_SERVER_HELLO)) {
             if (TLSDecodeValueIsGREASE(ext_type) != 1) {
                 SCTLSHandshakeAddExtension(ssl_state->curr_connp->hs, ext_type);
             }
index 3fc07e826cd5ee672aabe3b5f26d85ac5120569b..7c26c6507e46a13280b480b1d6c2ea02282807b5 100644 (file)
@@ -58,6 +58,7 @@
 #define LOG_TLS_FIELD_CLIENT_ALPNS     BIT_U64(18)
 #define LOG_TLS_FIELD_SERVER_ALPNS     BIT_U64(19)
 #define LOG_TLS_FIELD_CLIENT_HANDSHAKE BIT_U64(20)
+#define LOG_TLS_FIELD_SERVER_HANDSHAKE BIT_U64(21)
 
 typedef struct {
     const char *name;
@@ -87,6 +88,7 @@ TlsFields tls_fields[] = {
     { "client_alpns", LOG_TLS_FIELD_CLIENT_ALPNS },
     { "server_alpns", LOG_TLS_FIELD_SERVER_ALPNS },
     { "client_handshake", LOG_TLS_FIELD_CLIENT_HANDSHAKE },
+    { "server_handshake", LOG_TLS_FIELD_SERVER_HANDSHAKE },
     { NULL, -1 },
     // clang-format on
 };
@@ -383,6 +385,25 @@ static void JsonTlsLogClientHandshake(SCJsonBuilder *js, SSLState *ssl_state)
     SCJbClose(js);
 }
 
+static void JsonTlsLogServerHandshake(SCJsonBuilder *js, SSLState *ssl_state)
+{
+    if (ssl_state->server_connp.hs == NULL) {
+        return;
+    }
+
+    if (SCTLSHandshakeIsEmpty(ssl_state->server_connp.hs)) {
+        return;
+    }
+
+    SCJbOpenObject(js, "server_handshake");
+
+    SCTLSHandshakeLogVersion(ssl_state->server_connp.hs, js);
+    SCTLSHandshakeLogFirstCipher(ssl_state->server_connp.hs, js);
+    SCTLSHandshakeLogExtensions(ssl_state->server_connp.hs, js);
+
+    SCJbClose(js);
+}
+
 static void JsonTlsLogFields(SCJsonBuilder *js, SSLState *ssl_state, uint64_t fields)
 {
     /* tls subject */
@@ -458,6 +479,10 @@ static void JsonTlsLogFields(SCJsonBuilder *js, SSLState *ssl_state, uint64_t fi
     if (fields & LOG_TLS_FIELD_CLIENT_HANDSHAKE)
         JsonTlsLogClientHandshake(js, ssl_state);
 
+    /* tls server handshake parameters */
+    if (fields & LOG_TLS_FIELD_SERVER_HANDSHAKE)
+        JsonTlsLogServerHandshake(js, ssl_state);
+
     if (fields & LOG_TLS_FIELD_CLIENT) {
         const bool log_cert = (fields & LOG_TLS_FIELD_CLIENT_CERT) != 0;
         const bool log_chain = (fields & LOG_TLS_FIELD_CLIENT_CHAIN) != 0;
index b6506b11f0e2bf84f770782db3a938941f6d54ce..13535197fd157eb6555ffdced9067a4cb29bc871 100644 (file)
@@ -288,7 +288,7 @@ outputs:
             #session-resumption: no
             # custom controls which TLS fields that are included in eve-log
             # WARNING: enabling custom disables extended logging.
-            #custom: [subject, issuer, session_resumed, serial, fingerprint, sni, version, not_before, not_after, certificate, chain, ja3, ja3s, ja4, subjectaltname, client, client_certificate, client_chain, client_alpns, server_alpns, client_handshake]
+            #custom: [subject, issuer, session_resumed, serial, fingerprint, sni, version, not_before, not_after, certificate, chain, ja3, ja3s, ja4, subjectaltname, client, client_certificate, client_chain, client_alpns, server_alpns, client_handshake, server_handshake]
         - files:
             force-magic: no   # force logging magic on all logged files
             # force logging of checksums, available hash functions are md5,