* "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
~~~~~~~~
"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",
use tls_parser::{TlsCipherSuiteID, TlsExtensionType, TlsVersion};
use crate::jsonbuilder::{JsonBuilder, JsonError};
+use crate::tls_version::SCTlsVersion;
#[derive(Debug, PartialEq)]
pub struct 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
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
#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;
{ "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
};
}
}
+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 */
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)
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;
#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,