]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
output-json-tls: log certificate and chain
authorMats Klepsland <mats.klepsland@gmail.com>
Fri, 13 Jan 2017 11:20:34 +0000 (12:20 +0100)
committerMats Klepsland <mats.klepsland@gmail.com>
Mon, 20 Feb 2017 10:56:02 +0000 (11:56 +0100)
Log entire certificate and certificate chain Base64 encoded.

src/output-json-tls.c
src/util-error.c
src/util-error.h
suricata.yaml.in

index 485defc4b9b1510337a370b740ab0fe3e0e837e5..2ef4ef714dbb560c41fa88ff39bcc3f9846ec54e 100644 (file)
@@ -66,6 +66,8 @@ SC_ATOMIC_DECLARE(unsigned int, cert_id);
 #define LOG_TLS_FIELD_NOTBEFORE   (1 << 4)
 #define LOG_TLS_FIELD_NOTAFTER    (1 << 5)
 #define LOG_TLS_FIELD_SNI         (1 << 6)
+#define LOG_TLS_FIELD_CERTIFICATE (1 << 7)
+#define LOG_TLS_FIELD_CHAIN       (1 << 8)
 
 typedef struct {
     char *name;
@@ -80,6 +82,8 @@ TlsFields tls_fields[] = {
     { "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 },
     { NULL,          -1 }
 };
 
@@ -178,6 +182,51 @@ static void JsonTlsLogNotAfter(json_t *js, SSLState *ssl_state)
     }
 }
 
+static void JsonTlsLogCertificate(json_t *js, SSLState *ssl_state)
+{
+    if ((ssl_state->server_connp.cert_input == NULL) ||
+            (ssl_state->server_connp.cert_input_len == 0)) {
+        return;
+    }
+
+    SSLCertsChain *cert = TAILQ_FIRST(&ssl_state->server_connp.certs);
+    if (cert == NULL) {
+        return;
+    }
+
+    unsigned long len = cert->cert_len * 2;
+    uint8_t encoded[len];
+    if (Base64Encode(cert->cert_data, cert->cert_len, encoded, &len) ==
+                     SC_BASE64_OK) {
+        json_object_set_new(js, "certificate", json_string((char *)encoded));
+    }
+}
+
+static void JsonTlsLogChain(json_t *js, SSLState *ssl_state)
+{
+    if ((ssl_state->server_connp.cert_input == NULL) ||
+            (ssl_state->server_connp.cert_input_len == 0)) {
+        return;
+    }
+
+    json_t *chain = json_array();
+    if (chain == NULL) {
+        return;
+    }
+
+    SSLCertsChain *cert;
+    TAILQ_FOREACH(cert, &ssl_state->server_connp.certs, next) {
+        unsigned long len = cert->cert_len * 2;
+        uint8_t encoded[len];
+        if (Base64Encode(cert->cert_data, cert->cert_len, encoded, &len) ==
+                         SC_BASE64_OK) {
+            json_array_append_new(chain, json_string((char *)encoded));
+        }
+    }
+
+    json_object_set_new(js, "chain", chain);
+}
+
 void JsonTlsLogJSONBasic(json_t *js, SSLState *ssl_state)
 {
     /* tls.subject */
@@ -217,6 +266,14 @@ static void JsonTlsLogJSONCustom(OutputTlsCtx *tls_ctx, json_t *js,
     /* tls.notafter */
     if (tls_ctx->fields & LOG_TLS_FIELD_NOTAFTER)
         JsonTlsLogNotAfter(js, ssl_state);
+
+    /* tls.certificate */
+    if (tls_ctx->fields & LOG_TLS_FIELD_CERTIFICATE)
+        JsonTlsLogCertificate(js, ssl_state);
+
+    /* tls.chain */
+    if (tls_ctx->fields & LOG_TLS_FIELD_CHAIN)
+        JsonTlsLogChain(js, ssl_state);
 }
 
 void JsonTlsLogJSONExtended(json_t *tjs, SSLState * state)
@@ -447,6 +504,14 @@ OutputCtx *OutputTlsLogInitSub(ConfNode *conf, OutputCtx *parent_ctx)
 
     tls_ctx->file_ctx = ojc->file_ctx;
 
+    if ((tls_ctx->fields & LOG_TLS_FIELD_CERTIFICATE) &&
+            (tls_ctx->fields & LOG_TLS_FIELD_CHAIN)) {
+        SCLogWarning(SC_WARN_DUPLICATE_OUTPUT,
+                     "Both 'certificate' and 'chain' contains the top "
+                     "certificate, so only one of them should be enabled "
+                     "at a time");
+    }
+
     output_ctx->data = tls_ctx;
     output_ctx->DeInit = OutputTlsLogDeinitSub;
 
index 4503986fb5c28a64d78ffb643c4c22c6ef24c6dc..c9ad9854eb7c4503396262099a77999c5f95261e 100644 (file)
@@ -333,6 +333,7 @@ const char * SCErrorToString(SCError err)
         CASE_CODE (SC_ERR_DNP3_CONFIG);
         CASE_CODE (SC_ERR_DIR_OPEN);
         CASE_CODE(SC_WARN_REMOVE_FILE);
+        CASE_CODE (SC_WARN_DUPLICATE_OUTPUT);
         CASE_CODE (SC_ERR_NO_MAGIC_SUPPORT);
         CASE_CODE (SC_ERR_REDIS);
         CASE_CODE (SC_ERR_VAR_LIMIT);
index 59778c5606c44a472387dcbdcd0476b80882bd30..718ba543f4a16d5380c2f1d8dc84dea5eb8bf909 100644 (file)
@@ -326,6 +326,7 @@ typedef enum {
     SC_ERR_NO_MAGIC_SUPPORT,
     SC_ERR_REDIS,
     SC_ERR_VAR_LIMIT,
+    SC_WARN_DUPLICATE_OUTPUT,
 } SCError;
 
 const char *SCErrorToString(SCError);
index 9db8096eacef72c502b780a9a0b4e6e50b0ef88c..0222b187e2abf07b4904de6b27717371302002cc 100644 (file)
@@ -208,7 +208,7 @@ outputs:
             extended: yes     # enable this for extended logging information
             # custom allows to control which tls fields that are included
             # in eve-log
-            #custom: [subject, issuer, fingerprint, sni, version, not_before, not_after]
+            #custom: [subject, issuer, fingerprint, sni, version, not_before, not_after, certificate, chain]
         - files:
             force-magic: no   # force logging magic on all logged files
             # force logging of checksums, available hash functions are md5,