From: Victor Julien Date: Tue, 23 Aug 2022 09:35:41 +0000 (+0200) Subject: eve/tls: implement client cert logging X-Git-Tag: suricata-7.0.0-beta1~193 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4a283d480d2b851c24962d37ea84ae7a429596b3;p=thirdparty%2Fsuricata.git eve/tls: implement client cert logging Enable client logging in extended mode. Add "client", "client_certificate" and "client_chain", where the latter two depend on "client". --- diff --git a/src/output-json-tls.c b/src/output-json-tls.c index 972ce02c1e..f825fe38c6 100644 --- a/src/output-json-tls.c +++ b/src/output-json-tls.c @@ -73,28 +73,24 @@ SC_ATOMIC_EXTERN(unsigned int, cert_id); #define LOG_TLS_FIELD_SESSION_RESUMED (1 << 10) #define LOG_TLS_FIELD_JA3 (1 << 11) #define LOG_TLS_FIELD_JA3S (1 << 12) +#define LOG_TLS_FIELD_CLIENT (1 << 13) /**< client fields (issuer, subject, etc) */ +#define LOG_TLS_FIELD_CLIENT_CERT (1 << 14) +#define LOG_TLS_FIELD_CLIENT_CHAIN (1 << 15) typedef struct { const char *name; uint64_t flag; } TlsFields; -TlsFields tls_fields[] = { - { "version", LOG_TLS_FIELD_VERSION }, - { "subject", LOG_TLS_FIELD_SUBJECT }, - { "issuer", LOG_TLS_FIELD_ISSUER }, - { "serial", LOG_TLS_FIELD_SERIAL }, - { "fingerprint", LOG_TLS_FIELD_FINGERPRINT }, - { "not_before", LOG_TLS_FIELD_NOTBEFORE }, - { "not_after", LOG_TLS_FIELD_NOTAFTER }, - { "sni", LOG_TLS_FIELD_SNI }, - { "certificate", LOG_TLS_FIELD_CERTIFICATE }, - { "chain", LOG_TLS_FIELD_CHAIN }, - { "session_resumed", LOG_TLS_FIELD_SESSION_RESUMED }, - { "ja3", LOG_TLS_FIELD_JA3 }, - { "ja3s", LOG_TLS_FIELD_JA3S }, - { NULL, -1 } -}; +TlsFields tls_fields[] = { { "version", LOG_TLS_FIELD_VERSION }, + { "subject", LOG_TLS_FIELD_SUBJECT }, { "issuer", LOG_TLS_FIELD_ISSUER }, + { "serial", LOG_TLS_FIELD_SERIAL }, { "fingerprint", LOG_TLS_FIELD_FINGERPRINT }, + { "not_before", LOG_TLS_FIELD_NOTBEFORE }, { "not_after", LOG_TLS_FIELD_NOTAFTER }, + { "sni", LOG_TLS_FIELD_SNI }, { "certificate", LOG_TLS_FIELD_CERTIFICATE }, + { "chain", LOG_TLS_FIELD_CHAIN }, { "session_resumed", LOG_TLS_FIELD_SESSION_RESUMED }, + { "ja3", LOG_TLS_FIELD_JA3 }, { "ja3s", LOG_TLS_FIELD_JA3S }, + { "client", LOG_TLS_FIELD_CLIENT }, { "client_certificate", LOG_TLS_FIELD_CLIENT_CERT }, + { "client_chain", LOG_TLS_FIELD_CLIENT_CHAIN }, { NULL, -1 } }; typedef struct OutputTlsCtx_ { uint32_t flags; /** Store mode */ @@ -285,6 +281,53 @@ static void JsonTlsLogChain(JsonBuilder *js, SSLStateConnp *connp) jb_close(js); } +static bool HasClientCert(SSLStateConnp *connp) +{ + if (connp->cert0_subject || connp->cert0_issuerdn) + return true; + return false; +} + +static void JsonTlsLogClientCert( + JsonBuilder *js, SSLStateConnp *connp, const bool log_cert, const bool log_chain) +{ + if (connp->cert0_subject != NULL) { + jb_set_string(js, "subject", connp->cert0_subject); + } + if (connp->cert0_issuerdn != NULL) { + jb_set_string(js, "issuerdn", connp->cert0_issuerdn); + } + if (connp->cert0_fingerprint) { + jb_set_string(js, "fingerprint", connp->cert0_fingerprint); + } + if (connp->cert0_serial) { + jb_set_string(js, "serial", connp->cert0_serial); + } + if (connp->cert0_not_before != 0) { + char timebuf[64]; + struct timeval tv; + tv.tv_sec = connp->cert0_not_before; + tv.tv_usec = 0; + CreateUtcIsoTimeString(&tv, timebuf, sizeof(timebuf)); + jb_set_string(js, "notbefore", timebuf); + } + if (connp->cert0_not_after != 0) { + char timebuf[64]; + struct timeval tv; + tv.tv_sec = connp->cert0_not_after; + tv.tv_usec = 0; + CreateUtcIsoTimeString(&tv, timebuf, sizeof(timebuf)); + jb_set_string(js, "notafter", timebuf); + } + + if (log_cert) { + JsonTlsLogCertificate(js, connp); + } + if (log_chain) { + JsonTlsLogChain(js, connp); + } +} + void JsonTlsLogJSONBasic(JsonBuilder *js, SSLState *ssl_state) { /* tls subject */ @@ -351,6 +394,16 @@ static void JsonTlsLogJSONCustom(OutputTlsCtx *tls_ctx, JsonBuilder *js, /* tls ja3s */ if (tls_ctx->fields & LOG_TLS_FIELD_JA3S) JsonTlsLogJa3S(js, ssl_state); + + if (tls_ctx->fields & LOG_TLS_FIELD_CLIENT) { + const bool log_cert = (tls_ctx->fields & LOG_TLS_FIELD_CLIENT_CERT) != 0; + const bool log_chain = (tls_ctx->fields & LOG_TLS_FIELD_CLIENT_CHAIN) != 0; + if (HasClientCert(&ssl_state->client_connp)) { + jb_open_object(js, "client"); + JsonTlsLogClientCert(js, &ssl_state->client_connp, log_cert, log_chain); + jb_close(js); + } + } } void JsonTlsLogJSONExtended(JsonBuilder *tjs, SSLState * state) @@ -380,6 +433,12 @@ void JsonTlsLogJSONExtended(JsonBuilder *tjs, SSLState * state) /* tls ja3s */ JsonTlsLogJa3S(tjs, state); + + if (HasClientCert(&state->client_connp)) { + jb_open_object(tjs, "client"); + JsonTlsLogClientCert(tjs, &state->client_connp, false, false); + jb_close(tjs); + } } static int JsonTlsLogger(ThreadVars *tv, void *thread_data, const Packet *p, @@ -540,6 +599,24 @@ static OutputTlsCtx *OutputTlsInitCtx(ConfNode *conf) "certificate, so only one of them should be enabled " "at a time"); } + if ((tls_ctx->fields & LOG_TLS_FIELD_CLIENT_CERT) && + (tls_ctx->fields & LOG_TLS_FIELD_CLIENT_CHAIN)) { + SCLogWarning(SC_WARN_DUPLICATE_OUTPUT, + "Both 'client_certificate' and 'client_chain' contains the top " + "certificate, so only one of them should be enabled " + "at a time"); + } + + if ((tls_ctx->fields & LOG_TLS_FIELD_CLIENT) == 0) { + if (tls_ctx->fields & LOG_TLS_FIELD_CLIENT_CERT) { + SCLogConfig("enabling \"client\" as a dependency of \"client_certificate\""); + tls_ctx->fields |= LOG_TLS_FIELD_CLIENT; + } + if (tls_ctx->fields & LOG_TLS_FIELD_CLIENT_CHAIN) { + SCLogConfig("enabling \"client\" as a dependency of \"client_chain\""); + tls_ctx->fields |= LOG_TLS_FIELD_CLIENT; + } + } return tls_ctx; }