From: Richard McConnell Date: Fri, 25 Apr 2025 09:03:41 +0000 (+0100) Subject: output/tls: Allow logging of sv-handshake params X-Git-Tag: suricata-8.0.0-rc1~267 X-Git-Url: http://git.ipfire.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=d81b76d85251789ef4578e9a43e153663a02b09d;p=thirdparty%2Fsuricata.git output/tls: Allow logging of sv-handshake params 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 --- diff --git a/doc/userguide/output/eve/eve-json-format.rst b/doc/userguide/output/eve/eve-json-format.rst index 2890b3f556..ff2f3643d9 100644 --- a/doc/userguide/output/eve/eve-json-format.rst +++ b/doc/userguide/output/eve/eve-json-format.rst @@ -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 ~~~~~~~~ diff --git a/etc/schema.json b/etc/schema.json index 37c09111a6..a589376462 100644 --- a/etc/schema.json +++ b/etc/schema.json @@ -7102,6 +7102,27 @@ } } }, + "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", diff --git a/rust/src/handshake.rs b/rust/src/handshake.rs index df33af4dca..345ae40096 100644 --- a/rust/src/handshake.rs +++ b/rust/src/handshake.rs @@ -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() { diff --git a/src/app-layer-ssl.c b/src/app-layer-ssl.c index c16fc5544d..6ec0adbf23 100644 --- a/src/app-layer-ssl.c +++ b/src/app-layer-ssl.c @@ -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); } diff --git a/src/output-json-tls.c b/src/output-json-tls.c index 3fc07e826c..7c26c6507e 100644 --- a/src/output-json-tls.c +++ b/src/output-json-tls.c @@ -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; diff --git a/suricata.yaml.in b/suricata.yaml.in index b6506b11f0..13535197fd 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -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,