output/tls: Allow logging of cl-handshake params

Ticket: 6695

Add new custom log fields:

"client_handshake" which logs the following:
1. TLS version used during handshake
2. TLS extensions, excluding GREASE, SNI and ALPN
3. All cipher suites, excluding GREASE
4. All signature algorithms, excluding GREASE

The use-case is for logging TLS handshake parameters in order to survey
them, and so that JA4 hashes can be computed offline (in the case that
they're not already computed for the purposes of rule matching).
pull/13251/head
Richard McConnell 1 year ago committed by Victor Julien
parent 912030cbf4
commit 94c8be22d4

@ -1053,6 +1053,9 @@ In addition to this, custom logging also allows the following fields:
* "certificate": The TLS certificate base64 encoded
* "chain": The entire TLS certificate chain base64 encoded
* "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)
Examples
~~~~~~~~

@ -7069,6 +7069,39 @@
"type": "string"
}
},
"client_handshake": {
"type": "object",
"properties": {
"version": {
"description": "TLS version in client hello",
"type": "string"
},
"ciphers": {
"description": "TLS client cipher(s)",
"type": "array",
"minItems": 1,
"items": {
"type": "integer"
}
},
"exts": {
"description": "TLS client extension(s)",
"type": "array",
"minItems": 1,
"items": {
"type": "integer"
}
},
"sig_algs": {
"description": "TLS client signature algorithm(s)",
"type": "array",
"minItems": 1,
"items": {
"type": "integer"
}
}
}
},
"server_alpns": {
"description": "TLS server ALPN field(s)",
"type": "array",

@ -24,6 +24,7 @@ use std::os::raw::c_char;
use tls_parser::{TlsCipherSuiteID, TlsExtensionType, TlsVersion};
use crate::jsonbuilder::{JsonBuilder, JsonError};
use crate::tls_version::SCTlsVersion;
#[derive(Debug, PartialEq)]
pub struct HandshakeParams {
@ -124,6 +125,52 @@ impl HandshakeParams {
js.close()?;
Ok(())
}
fn log_version(&self, js: &mut JsonBuilder) -> Result<(), JsonError> {
let vers = self.tls_version.map(|v| v.0).unwrap_or_default();
let ver_str = SCTlsVersion::try_from(vers).map_err(|_| JsonError::InvalidState)?;
js.set_string("version", ver_str.as_str())?;
Ok(())
}
fn log_exts(&self, js: &mut JsonBuilder) -> Result<(), JsonError> {
if self.extensions.is_empty() {
return Ok(());
}
js.open_array("exts")?;
for v in &self.extensions {
js.append_uint(v.0.into())?;
}
js.close()?;
Ok(())
}
fn log_ciphers(&self, js: &mut JsonBuilder) -> Result<(), JsonError> {
if self.ciphersuites.is_empty() {
return Ok(());
}
js.open_array("ciphers")?;
for v in &self.ciphersuites {
js.append_uint(v.0.into())?;
}
js.close()?;
Ok(())
}
fn log_sig_algs(&self, js: &mut JsonBuilder) -> Result<(), JsonError> {
if self.signature_algorithms.is_empty() {
return Ok(());
}
js.open_array("sig_algs")?;
for v in &self.signature_algorithms {
js.append_uint(*v as u64)?;
}
js.close()?;
Ok(())
}
}
// Objects used to allow C to call into this struct via the below C ABI
@ -179,6 +226,38 @@ pub unsafe extern "C" fn SCTLSHandshakeFree(hs: &mut HandshakeParams) {
std::mem::drop(hs);
}
#[no_mangle]
pub unsafe extern "C" fn SCTLSHandshakeLogVersion(hs: &HandshakeParams, js: *mut JsonBuilder) -> bool {
if js.is_null() {
return false;
}
return hs.log_version(js.as_mut().unwrap()).is_ok()
}
#[no_mangle]
pub unsafe extern "C" fn SCTLSHandshakeLogCiphers(hs: &HandshakeParams, js: *mut JsonBuilder) -> bool {
if js.is_null() {
return false;
}
return hs.log_ciphers(js.as_mut().unwrap()).is_ok()
}
#[no_mangle]
pub unsafe extern "C" fn SCTLSHandshakeLogExtensions(hs: &HandshakeParams, js: *mut JsonBuilder) -> bool {
if js.is_null() {
return false;
}
return hs.log_exts(js.as_mut().unwrap()).is_ok()
}
#[no_mangle]
pub unsafe extern "C" fn SCTLSHandshakeLogSigAlgs(hs: &HandshakeParams, js: *mut JsonBuilder) -> bool {
if js.is_null() {
return false;
}
return hs.log_sig_algs(js.as_mut().unwrap()).is_ok()
}
#[no_mangle]
pub unsafe extern "C" fn SCTLSHandshakeLogALPNs(
hs: &HandshakeParams, js: *mut JsonBuilder, ptr: *const c_char

@ -37,26 +37,27 @@
#include "util-ja3.h"
#include "util-time.h"
#define LOG_TLS_FIELD_VERSION BIT_U64(0)
#define LOG_TLS_FIELD_SUBJECT BIT_U64(1)
#define LOG_TLS_FIELD_ISSUER BIT_U64(2)
#define LOG_TLS_FIELD_SERIAL BIT_U64(3)
#define LOG_TLS_FIELD_FINGERPRINT BIT_U64(4)
#define LOG_TLS_FIELD_NOTBEFORE BIT_U64(5)
#define LOG_TLS_FIELD_NOTAFTER BIT_U64(6)
#define LOG_TLS_FIELD_SNI BIT_U64(7)
#define LOG_TLS_FIELD_CERTIFICATE BIT_U64(8)
#define LOG_TLS_FIELD_CHAIN BIT_U64(9)
#define LOG_TLS_FIELD_SESSION_RESUMED BIT_U64(10)
#define LOG_TLS_FIELD_JA3 BIT_U64(11)
#define LOG_TLS_FIELD_JA3S BIT_U64(12)
#define LOG_TLS_FIELD_CLIENT BIT_U64(13) /**< client fields (issuer, subject, etc) */
#define LOG_TLS_FIELD_CLIENT_CERT BIT_U64(14)
#define LOG_TLS_FIELD_CLIENT_CHAIN BIT_U64(15)
#define LOG_TLS_FIELD_JA4 BIT_U64(16)
#define LOG_TLS_FIELD_SUBJECTALTNAME BIT_U64(17)
#define LOG_TLS_FIELD_CLIENT_ALPNS BIT_U64(18)
#define LOG_TLS_FIELD_SERVER_ALPNS BIT_U64(19)
#define LOG_TLS_FIELD_VERSION BIT_U64(0)
#define LOG_TLS_FIELD_SUBJECT BIT_U64(1)
#define LOG_TLS_FIELD_ISSUER BIT_U64(2)
#define LOG_TLS_FIELD_SERIAL BIT_U64(3)
#define LOG_TLS_FIELD_FINGERPRINT BIT_U64(4)
#define LOG_TLS_FIELD_NOTBEFORE BIT_U64(5)
#define LOG_TLS_FIELD_NOTAFTER BIT_U64(6)
#define LOG_TLS_FIELD_SNI BIT_U64(7)
#define LOG_TLS_FIELD_CERTIFICATE BIT_U64(8)
#define LOG_TLS_FIELD_CHAIN BIT_U64(9)
#define LOG_TLS_FIELD_SESSION_RESUMED BIT_U64(10)
#define LOG_TLS_FIELD_JA3 BIT_U64(11)
#define LOG_TLS_FIELD_JA3S BIT_U64(12)
#define LOG_TLS_FIELD_CLIENT BIT_U64(13) /**< client fields (issuer, subject, etc) */
#define LOG_TLS_FIELD_CLIENT_CERT BIT_U64(14)
#define LOG_TLS_FIELD_CLIENT_CHAIN BIT_U64(15)
#define LOG_TLS_FIELD_JA4 BIT_U64(16)
#define LOG_TLS_FIELD_SUBJECTALTNAME BIT_U64(17)
#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)
typedef struct {
const char *name;
@ -85,6 +86,7 @@ TlsFields tls_fields[] = {
{ "subjectaltname", LOG_TLS_FIELD_SUBJECTALTNAME },
{ "client_alpns", LOG_TLS_FIELD_CLIENT_ALPNS },
{ "server_alpns", LOG_TLS_FIELD_SERVER_ALPNS },
{ "client_handshake", LOG_TLS_FIELD_CLIENT_HANDSHAKE },
{ NULL, -1 },
// clang-format on
};
@ -360,6 +362,27 @@ static void JsonTlsLogClientCert(
}
}
static void JsonTlsLogClientHandshake(SCJsonBuilder *js, SSLState *ssl_state)
{
if (ssl_state->client_connp.hs == NULL) {
return;
}
// Don't write an empty handshake
if (SCTLSHandshakeIsEmpty(ssl_state->client_connp.hs)) {
return;
}
SCJbOpenObject(js, "client_handshake");
SCTLSHandshakeLogVersion(ssl_state->client_connp.hs, js);
SCTLSHandshakeLogCiphers(ssl_state->client_connp.hs, js);
SCTLSHandshakeLogExtensions(ssl_state->client_connp.hs, js);
SCTLSHandshakeLogSigAlgs(ssl_state->client_connp.hs, js);
SCJbClose(js);
}
static void JsonTlsLogFields(SCJsonBuilder *js, SSLState *ssl_state, uint64_t fields)
{
/* tls subject */
@ -391,8 +414,9 @@ static void JsonTlsLogFields(SCJsonBuilder *js, SSLState *ssl_state, uint64_t fi
JsonTlsLogSni(js, ssl_state);
/* tls version */
if (fields & LOG_TLS_FIELD_VERSION)
if (fields & LOG_TLS_FIELD_VERSION) {
JsonTlsLogVersion(js, ssl_state);
}
/* tls notbefore */
if (fields & LOG_TLS_FIELD_NOTBEFORE)
@ -430,6 +454,10 @@ static void JsonTlsLogFields(SCJsonBuilder *js, SSLState *ssl_state, uint64_t fi
JsonTlsLogAlpns(js, &ssl_state->server_connp, "server_alpns");
}
/* tls client handshake parameters */
if (fields & LOG_TLS_FIELD_CLIENT_HANDSHAKE)
JsonTlsLogClientHandshake(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;

@ -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]
#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]
- files:
force-magic: no # force logging magic on all logged files
# force logging of checksums, available hash functions are md5,

Loading…
Cancel
Save