]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
ssh: add json logger
authorVictor Julien <victor@inliniac.net>
Sat, 1 Mar 2014 22:12:29 +0000 (23:12 +0100)
committerVictor Julien <victor@inliniac.net>
Mon, 3 Mar 2014 16:34:57 +0000 (17:34 +0100)
Sub module of eve-log, but can also run separately as ssh-json-log. Only
one at a time though.

src/Makefile.am
src/app-layer-ssh.h
src/output-json-ssh.c [new file with mode: 0644]
src/output-json-ssh.h [new file with mode: 0644]
src/output.c
src/output.h
src/suricata.c
src/tm-modules.c
src/tm-threads-common.h

index 834162cd23a91c9af7fefab653148481734f0a5e..27a02dc961d7f71296994e99b507c7a7706ac138 100644 (file)
@@ -221,6 +221,7 @@ output-json-dns.c output-json-dns.h \
 output-json-drop.c output-json-drop.h \
 output-json-file.c output-json-file.h \
 output-json-http.c output-json-http.h \
+output-json-ssh.c output-json-ssh.h \
 output-json-tls.c output-json-tls.h \
 output-packet.c output-packet.h \
 output-tx.c output-tx.h \
index 2bcc507117b8f1a0524da743a522d4e65571e803..7a6a9b723427b43d05d77571c1879b202ade7e8a 100644 (file)
@@ -32,6 +32,8 @@
  * must be ciphered, so the parsing finish here */
 #define SSH_FLAG_PARSER_DONE                 0x02
 
+#define SSH_FLAG_STATE_LOGGED                0x04
+
 /* MSG_CODE */
 #define SSH_MSG_NEWKEYS                      21
 
diff --git a/src/output-json-ssh.c b/src/output-json-ssh.c
new file mode 100644 (file)
index 0000000..80a81c9
--- /dev/null
@@ -0,0 +1,322 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ *
+ * Implements SSH JSON logging portion of the engine.
+ */
+
+#include "suricata-common.h"
+#include "debug.h"
+#include "detect.h"
+#include "pkt-var.h"
+#include "conf.h"
+
+#include "threads.h"
+#include "threadvars.h"
+#include "tm-threads.h"
+
+#include "util-print.h"
+#include "util-unittest.h"
+
+#include "util-debug.h"
+#include "app-layer-parser.h"
+#include "output.h"
+#include "app-layer-ssh.h"
+#include "app-layer.h"
+#include "util-privs.h"
+#include "util-buffer.h"
+
+#include "util-logopenfile.h"
+#include "util-crypt.h"
+
+#include "output-json.h"
+
+#ifdef HAVE_LIBJANSSON
+#include <jansson.h>
+
+#define MODULE_NAME "LogSshLog"
+
+typedef struct OutputSshCtx_ {
+    LogFileCtx *file_ctx;
+    uint32_t flags; /** Store mode */
+} OutputSshCtx;
+
+
+typedef struct JsonSshLogThread_ {
+    OutputSshCtx *sshlog_ctx;
+    MemBuffer *buffer;
+} JsonSshLogThread;
+
+static int JsonSshLogger(ThreadVars *tv, void *thread_data, const Packet *p) {
+    JsonSshLogThread *aft = (JsonSshLogThread *)thread_data;
+    MemBuffer *buffer = (MemBuffer *)aft->buffer;
+    OutputSshCtx *ssh_ctx = aft->sshlog_ctx;
+
+    if (unlikely(p->flow == NULL)) {
+        return 0;
+    }
+
+    /* check if we have SSH state or not */
+    FLOWLOCK_WRLOCK(p->flow);
+    uint16_t proto = FlowGetAppProtocol(p->flow);
+    if (proto != ALPROTO_SSH)
+        goto end;
+
+    SshState *ssh_state = (SshState *)FlowGetAppState(p->flow);
+    if (unlikely(ssh_state == NULL)) {
+        goto end;
+    }
+
+    if (ssh_state->cli_hdr.software_version == NULL || ssh_state->srv_hdr.software_version == NULL)
+        goto end;
+
+    json_t *js = CreateJSONHeader((Packet *)p, 0, "ssh");//TODO
+    if (unlikely(js == NULL))
+        goto end;
+
+    json_t *tjs = json_object();
+    if (tjs == NULL) {
+        free(js);
+        goto end;
+    }
+
+    /* reset */
+    MemBufferReset(buffer);
+
+    json_t *cjs = json_object();
+    if (cjs != NULL) {
+        json_object_set_new(cjs, "proto_version",
+                json_string((char *)ssh_state->cli_hdr.proto_version));
+
+        json_object_set_new(cjs, "software_version",
+                json_string((char *)ssh_state->cli_hdr.software_version));
+    }
+    json_object_set_new(tjs, "client", cjs);
+
+    json_t *sjs = json_object();
+    if (sjs != NULL) {
+        json_object_set_new(sjs, "proto_version",
+                json_string((char *)ssh_state->srv_hdr.proto_version));
+
+        json_object_set_new(sjs, "software_version",
+                json_string((char *)ssh_state->srv_hdr.software_version));
+    }
+    json_object_set_new(tjs, "server", sjs);
+
+    json_object_set_new(js, "ssh", tjs);
+
+    OutputJSONBuffer(js, ssh_ctx->file_ctx, buffer);
+    json_object_clear(js);
+    json_decref(js);
+
+    /* we only log the state once */
+    ssh_state->cli_hdr.flags |= SSH_FLAG_STATE_LOGGED;
+end:
+    FLOWLOCK_UNLOCK(p->flow);
+    return 0;
+}
+
+#define OUTPUT_BUFFER_SIZE 65535
+static TmEcode JsonSshLogThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+    JsonSshLogThread *aft = SCMalloc(sizeof(JsonSshLogThread));
+    if (unlikely(aft == NULL))
+        return TM_ECODE_FAILED;
+    memset(aft, 0, sizeof(JsonSshLogThread));
+
+    if(initdata == NULL)
+    {
+        SCLogDebug("Error getting context for HTTPLog.  \"initdata\" argument NULL");
+        SCFree(aft);
+        return TM_ECODE_FAILED;
+    }
+
+    /* Use the Ouptut Context (file pointer and mutex) */
+    aft->sshlog_ctx = ((OutputCtx *)initdata)->data;
+
+    aft->buffer = MemBufferCreateNew(OUTPUT_BUFFER_SIZE);
+    if (aft->buffer == NULL) {
+        SCFree(aft);
+        return TM_ECODE_FAILED;
+    }
+
+    *data = (void *)aft;
+    return TM_ECODE_OK;
+}
+
+static TmEcode JsonSshLogThreadDeinit(ThreadVars *t, void *data)
+{
+    JsonSshLogThread *aft = (JsonSshLogThread *)data;
+    if (aft == NULL) {
+        return TM_ECODE_OK;
+    }
+
+    MemBufferFree(aft->buffer);
+    /* clear memory */
+    memset(aft, 0, sizeof(JsonSshLogThread));
+
+    SCFree(aft);
+    return TM_ECODE_OK;
+}
+
+
+#define DEFAULT_LOG_FILENAME "ssh.json"
+OutputCtx *OutputSshLogInit(ConfNode *conf)
+{
+    if (OutputSshLoggerEnable() != 0) {
+        SCLogError(SC_ERR_CONF_YAML_ERROR, "only one 'ssh' logger "
+            "can be enabled");
+        return NULL;
+    }
+
+    LogFileCtx *file_ctx = LogFileNewCtx();
+    if(file_ctx == NULL) {
+        SCLogError(SC_ERR_HTTP_LOG_GENERIC, "couldn't create new file_ctx");
+        return NULL;
+    }
+
+    if (SCConfLogOpenGeneric(conf, file_ctx, DEFAULT_LOG_FILENAME) < 0) {
+        LogFileFreeCtx(file_ctx);
+        return NULL;
+    }
+
+    OutputSshCtx *ssh_ctx = SCMalloc(sizeof(OutputSshCtx));
+    if (unlikely(ssh_ctx == NULL)) {
+        LogFileFreeCtx(file_ctx);
+        return NULL;
+    }
+
+    OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+    if (unlikely(output_ctx == NULL)) {
+        LogFileFreeCtx(file_ctx);
+        SCFree(ssh_ctx);
+        return NULL;
+    }
+
+    ssh_ctx->file_ctx = file_ctx;
+
+    output_ctx->data = ssh_ctx;
+    output_ctx->DeInit = NULL;
+
+    return output_ctx;
+}
+
+OutputCtx *OutputSshLogInitSub(ConfNode *conf, OutputCtx *parent_ctx)
+{
+    AlertJsonThread *ajt = parent_ctx->data;
+
+    if (OutputSshLoggerEnable() != 0) {
+        SCLogError(SC_ERR_CONF_YAML_ERROR, "only one 'ssh' logger "
+            "can be enabled");
+        return NULL;
+    }
+
+    OutputSshCtx *ssh_ctx = SCMalloc(sizeof(OutputSshCtx));
+    if (unlikely(ssh_ctx == NULL))
+        return NULL;
+
+    OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
+    if (unlikely(output_ctx == NULL)) {
+        SCFree(ssh_ctx);
+        return NULL;
+    }
+
+    ssh_ctx->file_ctx = ajt->file_ctx;
+
+    output_ctx->data = ssh_ctx;
+    output_ctx->DeInit = NULL;
+
+    return output_ctx;
+}
+
+/** \internal
+ *  \brief Condition function for SSH logger
+ *  \retval bool true or false -- log now?
+ */
+static int JsonSshCondition(ThreadVars *tv, const Packet *p) {
+    if (p->flow == NULL) {
+        return FALSE;
+    }
+
+    if (!(PKT_IS_TCP(p))) {
+        return FALSE;
+    }
+
+    FLOWLOCK_RDLOCK(p->flow);
+    uint16_t proto = FlowGetAppProtocol(p->flow);
+    if (proto != ALPROTO_SSH)
+        goto dontlog;
+
+    SshState *ssh_state = (SshState *)FlowGetAppState(p->flow);
+    if (ssh_state == NULL) {
+        SCLogDebug("no ssh state, so no logging");
+        goto dontlog;
+    }
+
+    /* we only log the state once */
+    if (ssh_state->cli_hdr.flags & SSH_FLAG_STATE_LOGGED)
+        goto dontlog;
+
+    if (ssh_state->cli_hdr.software_version == NULL ||
+        ssh_state->srv_hdr.software_version == NULL)
+        goto dontlog;
+
+    /* todo: logic to log once */
+
+    FLOWLOCK_UNLOCK(p->flow);
+    return TRUE;
+dontlog:
+    FLOWLOCK_UNLOCK(p->flow);
+    return FALSE;
+}
+
+void TmModuleJsonSshLogRegister (void) {
+    tmm_modules[TMM_JSONSSHLOG].name = "JsonSshLog";
+    tmm_modules[TMM_JSONSSHLOG].ThreadInit = JsonSshLogThreadInit;
+    tmm_modules[TMM_JSONSSHLOG].ThreadDeinit = JsonSshLogThreadDeinit;
+    tmm_modules[TMM_JSONSSHLOG].RegisterTests = NULL;
+    tmm_modules[TMM_JSONSSHLOG].cap_flags = 0;
+    tmm_modules[TMM_JSONSSHLOG].flags = TM_FLAG_LOGAPI_TM;
+
+    /* register as separate module */
+    OutputRegisterPacketModule("JsonSshLog", "ssh-json-log", OutputSshLogInit,
+            JsonSshLogger, JsonSshCondition);
+
+    /* also register as child of eve-log */
+    OutputRegisterPacketSubModule("eve-log", "JsonSshLog", "eve-log.ssh", OutputSshLogInitSub,
+            JsonSshLogger, JsonSshCondition);
+}
+
+#else
+
+static TmEcode OutputJsonThreadInit(ThreadVars *t, void *initdata, void **data)
+{
+    SCLogInfo("Can't init JSON output - JSON support was disabled during build.");
+    return TM_ECODE_FAILED;
+}
+
+void TmModuleJsonSshLogRegister (void)
+{
+    tmm_modules[TMM_JSONSSHLOG].name = "JsonSshLog";
+    tmm_modules[TMM_JSONSSHLOG].ThreadInit = OutputJsonThreadInit;
+}
+
+#endif
diff --git a/src/output-json-ssh.h b/src/output-json-ssh.h
new file mode 100644 (file)
index 0000000..af0711c
--- /dev/null
@@ -0,0 +1,29 @@
+/* Copyright (C) 2014 Open Information Security Foundation
+ *
+ * You can copy, redistribute or modify this Program under the terms of
+ * the GNU General Public License version 2 as published by the Free
+ * Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * version 2 along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ */
+
+#ifndef __OUTPUT_JSON_SSH_H__
+#define __OUTPUT_JSON_SSH_H__
+
+void TmModuleJsonSshLogRegister (void);
+
+#endif /* __OUTPUT_JSON_SSH_H__ */
index b6960d75e0e2974d04e0fa93d405adf918547e4a..1ab53c633ba51783b398e3603912b02f7e7238e1 100644 (file)
@@ -393,3 +393,11 @@ int OutputTlsLoggerEnable(void) {
     return 0;
 }
 
+static int ssh_loggers = 0;
+
+int OutputSshLoggerEnable(void) {
+    if (ssh_loggers)
+        return -1;
+    ssh_loggers++;
+    return 0;
+}
index d87178135c564acd4168865533ed80647e2cfc7a..a68b02b91ad92e09582f8cb5454ab9b3402b86db 100644 (file)
@@ -85,5 +85,6 @@ void OutputDeregisterAll(void);
 
 int OutputDropLoggerEnable(void);
 int OutputTlsLoggerEnable(void);
+int OutputSshLoggerEnable(void);
 
 #endif /* ! __OUTPUT_H__ */
index d28b69d666c726fc3b1660ab5650354dbabc0c90..22d3015faa0ff43bcf94b36235613f0d525232ad 100644 (file)
@@ -88,6 +88,7 @@
 #include "output-json-dns.h"
 #include "log-tlslog.h"
 #include "output-json-tls.h"
+#include "output-json-ssh.h"
 #include "log-pcap.h"
 #include "log-file.h"
 #include "output-json-file.h"
@@ -816,6 +817,8 @@ void RegisterAllModules()
     /* tls log */
     TmModuleLogTlsLogRegister();
     TmModuleJsonTlsLogRegister();
+    /* ssh */
+    TmModuleJsonSshLogRegister();
     /* pcap log */
     TmModulePcapLogRegister();
     /* file log */
index a8ef7b6f4403efa588d8be61c6d9293b19970368..a8dfc8a8ff1bdb1fecce6c7e5b7a6b15466d9ff6 100644 (file)
@@ -248,6 +248,7 @@ const char * TmModuleTmmIdToString(TmmId id)
         CASE_CODE (TMM_JSONDNSLOG);
         CASE_CODE (TMM_JSONHTTPLOG);
         CASE_CODE (TMM_JSONFILELOG);
+        CASE_CODE (TMM_JSONSSHLOG);
         CASE_CODE (TMM_JSONTLSLOG);
         CASE_CODE (TMM_OUTPUTJSON);
 
index c78cc1d85648b06093b5985a8ea166ec4a394840..5443448950525709d57eb7620015f9d6937fd816 100644 (file)
@@ -86,6 +86,7 @@ typedef enum {
     TMM_JSONDROPLOG,
     TMM_JSONHTTPLOG,
     TMM_JSONDNSLOG,
+    TMM_JSONSSHLOG,
     TMM_JSONTLSLOG,
     TMM_JSONFILELOG,
     TMM_SIZE,