From efdf96ccbaeca1703b48388d85cf57ad4086287d Mon Sep 17 00:00:00 2001 From: Jean-Paul Roliers Date: Wed, 1 Feb 2012 22:36:44 +0100 Subject: [PATCH] tls: adding TLS Log support Creation of the log-tlslog file in order to log tls message. Need to add some information into suricata.yaml to work. - tls-log: enabled: yes # Log TLS connections. filename: tls.log # File to store TLS logs. --- src/Makefile.am | 1 + src/log-tlslog.c | 341 ++++++++++++++++++++++++++++++++++++++++ src/log-tlslog.h | 34 ++++ src/suricata.c | 4 + src/tm-threads-common.h | 3 + src/util-error.h | 3 +- suricata.yaml.in | 5 + 7 files changed, 390 insertions(+), 1 deletion(-) create mode 100644 src/log-tlslog.c create mode 100644 src/log-tlslog.h diff --git a/src/Makefile.am b/src/Makefile.am index cdb6c22d67..88850018c1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -265,6 +265,7 @@ alert-syslog.c alert-syslog.h \ alert-pcapinfo.c alert-pcapinfo.h \ log-droplog.c log-droplog.h \ log-httplog.c log-httplog.h \ +log-tlslog.c log-tlslog.h \ log-pcap.c log-pcap.h \ log-file.c log-file.h \ log-filestore.c log-filestore.h \ diff --git a/src/log-tlslog.c b/src/log-tlslog.c new file mode 100644 index 0000000000..24602ad7db --- /dev/null +++ b/src/log-tlslog.c @@ -0,0 +1,341 @@ +/* Copyright (C) 2007-2012 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 Roliers Jean-Paul + * \author Eric Leblond + * + * Implements tls 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 "output.h" +#include "log-tlslog.h" +#include "app-layer-ssl.h" +#include "app-layer.h" +#include "util-privs.h" +#include "util-buffer.h" + +#include "util-logopenfile.h" + +#define DEFAULT_LOG_FILENAME "tls.log" + +#define MODULE_NAME "LogTlsLog" + +#define OUTPUT_BUFFER_SIZE 65535 + +TmEcode LogTlsLog(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *); +TmEcode LogTlsLogIPv4(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *); +TmEcode LogTlsLogIPv6(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *); +TmEcode LogTlsLogThreadInit(ThreadVars *, void *, void **); +TmEcode LogTlsLogThreadDeinit(ThreadVars *, void *); +void LogTlsLogExitPrintStats(ThreadVars *, void *); +static void LogTlsLogDeInitCtx(OutputCtx *); + +void TmModuleLogTlsLogRegister(void) +{ + tmm_modules[TMM_LOGTLSLOG].name = MODULE_NAME; + tmm_modules[TMM_LOGTLSLOG].ThreadInit = LogTlsLogThreadInit; + tmm_modules[TMM_LOGTLSLOG].Func = LogTlsLog; + tmm_modules[TMM_LOGTLSLOG].ThreadExitPrintStats = LogTlsLogExitPrintStats; + tmm_modules[TMM_LOGTLSLOG].ThreadDeinit = LogTlsLogThreadDeinit; + tmm_modules[TMM_LOGTLSLOG].RegisterTests = NULL; + tmm_modules[TMM_LOGTLSLOG].cap_flags = 0; + + OutputRegisterModule(MODULE_NAME, "tls-log", LogTlsLogInitCtx); + + /* enable the logger for the app layer */ + AppLayerRegisterLogger(ALPROTO_TLS); +} + +void TmModuleLogTlsLogIPv4Register(void) +{ + tmm_modules[TMM_LOGTLSLOG4].name = "LogTlsLogIPv4"; + tmm_modules[TMM_LOGTLSLOG4].ThreadInit = LogTlsLogThreadInit; + tmm_modules[TMM_LOGTLSLOG4].Func = LogTlsLogIPv4; + tmm_modules[TMM_LOGTLSLOG4].ThreadExitPrintStats = LogTlsLogExitPrintStats; + tmm_modules[TMM_LOGTLSLOG4].ThreadDeinit = LogTlsLogThreadDeinit; + tmm_modules[TMM_LOGTLSLOG4].RegisterTests = NULL; +} + +void TmModuleLogTlsLogIPv6Register(void) +{ + tmm_modules[TMM_LOGTLSLOG6].name = "LogTlsLogIPv6"; + tmm_modules[TMM_LOGTLSLOG6].ThreadInit = LogTlsLogThreadInit; + tmm_modules[TMM_LOGTLSLOG6].Func = LogTlsLogIPv6; + tmm_modules[TMM_LOGTLSLOG6].ThreadExitPrintStats = LogTlsLogExitPrintStats; + tmm_modules[TMM_LOGTLSLOG6].ThreadDeinit = LogTlsLogThreadDeinit; + tmm_modules[TMM_LOGTLSLOG6].RegisterTests = NULL; +} + +typedef struct LogTlsFileCtx_ { + LogFileCtx *file_ctx; + uint32_t flags; /** Store mode */ +} LogTlsFileCtx; + + +typedef struct LogTlsLogThread_ { + LogTlsFileCtx *tlslog_ctx; + /** LogTlsFileCtx has the pointer to the file and a mutex to allow multithreading */ + uint32_t tls_cnt; + + MemBuffer *buffer; +} LogTlsLogThread; + +static void CreateTimeString(const struct timeval *ts, char *str, size_t size) +{ + time_t time = ts->tv_sec; + struct tm local_tm; + struct tm *t = (struct tm *) localtime_r(&time, &local_tm); + + snprintf(str, size, "%02d/%02d/%02d-%02d:%02d:%02d.%06u", t->tm_mon + 1, t->tm_mday, t->tm_year + 1900, t->tm_hour, t->tm_min, t->tm_sec, (uint32_t) ts->tv_usec); +} + +static TmEcode LogTlsLogIPWrapper(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq, int ipproto) +{ + + SCEnter(); + LogTlsLogThread *aft = (LogTlsLogThread *) data; + LogTlsFileCtx *hlog = aft->tlslog_ctx; + + char timebuf[64]; + + /* no flow, no tls state */ + if (p->flow == NULL) { + SCReturnInt(TM_ECODE_OK); + } + + /* check if we have TLS state or not */ + FLOWLOCK_WRLOCK(p->flow); + uint16_t proto = AppLayerGetProtoFromPacket(p); + if (proto != ALPROTO_TLS) + goto end; + + int r = AppLayerTransactionGetLoggedId(p->flow); + + if (r != 0) { + goto end; + } + + SSLState *ssl_state = (SSLState *) AppLayerGetProtoStateFromPacket(p); + if (ssl_state == NULL) { + SCLogDebug("no tls state, so no request logging"); + goto end; + } + + if (ssl_state->server_connp.cert0_issuerdn == NULL || ssl_state->server_connp.cert0_subject == NULL) + goto end; + + CreateTimeString(&p->ts, timebuf, sizeof(timebuf)); + char srcip[46], dstip[46]; + Port sp, dp; + if ((PKT_IS_TOSERVER(p))) { + switch (ipproto) { + case AF_INET: + PrintInet(AF_INET, (const void *) GET_IPV4_SRC_ADDR_PTR(p), srcip, sizeof(srcip)); + PrintInet(AF_INET, (const void *) GET_IPV4_DST_ADDR_PTR(p), dstip, sizeof(dstip)); + break; + case AF_INET6: + PrintInet(AF_INET6, (const void *) GET_IPV6_SRC_ADDR(p), srcip, sizeof(srcip)); + PrintInet(AF_INET6, (const void *) GET_IPV6_DST_ADDR(p), dstip, sizeof(dstip)); + break; + default: + goto end; + } + sp = p->sp; + dp = p->dp; + } else { + switch (ipproto) { + case AF_INET: + PrintInet(AF_INET, (const void *) GET_IPV4_DST_ADDR_PTR(p), srcip, sizeof(srcip)); + PrintInet(AF_INET, (const void *) GET_IPV4_SRC_ADDR_PTR(p), dstip, sizeof(dstip)); + break; + case AF_INET6: + PrintInet(AF_INET6, (const void *) GET_IPV6_DST_ADDR(p), srcip, sizeof(srcip)); + PrintInet(AF_INET6, (const void *) GET_IPV6_SRC_ADDR(p), dstip, sizeof(dstip)); + break; + default: + goto end; + } + sp = p->dp; + dp = p->sp; + } + + /* reset */ + MemBufferReset(aft->buffer); + + MemBufferWriteString(aft->buffer, + "%s %s:%d -> %s:%d TLS: Subject='%s' Issuerdn='%s'\n", + timebuf, srcip, sp, dstip, dp, + ssl_state->server_connp.cert0_subject, ssl_state->server_connp.cert0_issuerdn); + + AppLayerTransactionUpdateLoggedId(p->flow); + + aft->tls_cnt ++; + + SCMutexLock(&hlog->file_ctx->fp_mutex); + MemBufferPrintToFPAsString(aft->buffer, hlog->file_ctx->fp); + fflush(hlog->file_ctx->fp); + SCMutexUnlock(&hlog->file_ctx->fp_mutex); + +end: + FLOWLOCK_UNLOCK(p->flow); + SCReturnInt(TM_ECODE_OK); + +} + +TmEcode LogTlsLogIPv4(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq) +{ + return LogTlsLogIPWrapper(tv, p, data, pq, postpq, AF_INET); +} + +TmEcode LogTlsLogIPv6(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq) +{ + return LogTlsLogIPWrapper(tv, p, data, pq, postpq, AF_INET6); +} + +TmEcode LogTlsLog(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq) +{ + SCEnter(); + + /* no flow, no htp state */ + if (p->flow == NULL) { + SCReturnInt(TM_ECODE_OK); + } + + if (!(PKT_IS_TCP(p))) { + SCReturnInt(TM_ECODE_OK); + } + + if (PKT_IS_IPV4(p)) { + SCReturnInt(LogTlsLogIPv4(tv, p, data, pq, postpq)); + } else if (PKT_IS_IPV6(p)) { + SCReturnInt(LogTlsLogIPv6(tv, p, data, pq, postpq)); + } + + SCReturnInt(TM_ECODE_OK); +} + +TmEcode LogTlsLogThreadInit(ThreadVars *t, void *initdata, void **data) +{ + LogTlsLogThread *aft = SCMalloc(sizeof(LogTlsLogThread)); + if (aft == NULL) + return TM_ECODE_FAILED; + memset(aft, 0, sizeof(LogTlsLogThread)); + + if (initdata == NULL) { + SCLogDebug( "Error getting context for TLSLog. \"initdata\" argument NULL"); + SCFree(aft); + return TM_ECODE_FAILED; + } + + aft->buffer = MemBufferCreateNew(OUTPUT_BUFFER_SIZE); + if (aft->buffer == NULL) { + SCFree(aft); + return TM_ECODE_FAILED; + } + + /* Use the Ouptut Context (file pointer and mutex) */ + aft->tlslog_ctx = ((OutputCtx *) initdata)->data; + + *data = (void *) aft; + return TM_ECODE_OK; +} + +TmEcode LogTlsLogThreadDeinit(ThreadVars *t, void *data) +{ + LogTlsLogThread *aft = (LogTlsLogThread *) data; + if (aft == NULL) { + return TM_ECODE_OK; + } + + MemBufferFree(aft->buffer); + /* clear memory */ + memset(aft, 0, sizeof(LogTlsLogThread)); + + SCFree(aft); + return TM_ECODE_OK; +} + +void LogTlsLogExitPrintStats(ThreadVars *tv, void *data) +{ + LogTlsLogThread *aft = (LogTlsLogThread *) data; + if (aft == NULL) { + return; + } + + SCLogInfo("TLS logger logged %" PRIu32 " requests", aft->tls_cnt); +} + +/** \brief Create a new tls log LogFileCtx. + * \param conf Pointer to ConfNode containing this loggers configuration. + * \return NULL if failure, LogFileCtx* to the file_ctx if succesful + * */ +OutputCtx *LogTlsLogInitCtx(ConfNode *conf) +{ + LogFileCtx* file_ctx = LogFileNewCtx(); + + if (file_ctx == NULL) { + SCLogError(SC_ERR_TLS_LOG_GENERIC, "LogTlsLogInitCtx: Couldn't " + "create new file_ctx"); + return NULL; + } + + if (SCConfLogOpenGeneric(conf, file_ctx, DEFAULT_LOG_FILENAME) < 0) { + LogFileFreeCtx(file_ctx); + return NULL; + } + + LogTlsFileCtx *tlslog_ctx = SCCalloc(1, sizeof(LogTlsFileCtx)); + if (tlslog_ctx == NULL) + return NULL; + tlslog_ctx->file_ctx = file_ctx; + + OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx)); + if (output_ctx == NULL) + return NULL; + output_ctx->data = tlslog_ctx; + output_ctx->DeInit = LogTlsLogDeInitCtx; + + SCLogDebug("TLS log output initialized"); + + return output_ctx; +} + +static void LogTlsLogDeInitCtx(OutputCtx *output_ctx) +{ + LogTlsFileCtx *tlslog_ctx = (LogTlsFileCtx *) output_ctx->data; + LogFileFreeCtx(tlslog_ctx->file_ctx); + SCFree(tlslog_ctx); + SCFree(output_ctx); +} diff --git a/src/log-tlslog.h b/src/log-tlslog.h new file mode 100644 index 0000000000..b7838be6c3 --- /dev/null +++ b/src/log-tlslog.h @@ -0,0 +1,34 @@ +/* Copyright (C) 2007-2012 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 Roliers Jean-Paul + * \author Eric Leblond + */ + +#ifndef __LOG_TLSLOG_H__ +#define __LOG_TLSLOG_H__ + +void TmModuleLogTlsLogRegister (void); +void TmModuleLogTlsLogIPv4Register (void); +void TmModuleLogTlsLogIPv6Register (void); +OutputCtx *LogTlsLogInitCtx(ConfNode *); + +#endif /* __LOG_TLSLOG_H__ */ + diff --git a/src/suricata.c b/src/suricata.c index efdc2ae052..00df8ee0a1 100644 --- a/src/suricata.c +++ b/src/suricata.c @@ -97,6 +97,7 @@ #include "log-droplog.h" #include "log-httplog.h" +#include "log-tlslog.h" #include "log-pcap.h" #include "log-file.h" #include "log-filestore.h" @@ -1500,6 +1501,9 @@ int main(int argc, char **argv) TmModuleLogHttpLogRegister(); TmModuleLogHttpLogIPv4Register(); TmModuleLogHttpLogIPv6Register(); + TmModuleLogTlsLogRegister(); + TmModuleLogTlsLogIPv4Register(); + TmModuleLogTlsLogIPv6Register(); /* pcap log */ TmModulePcapLogRegister(); /* file log */ diff --git a/src/tm-threads-common.h b/src/tm-threads-common.h index acf4528a95..20033d842a 100644 --- a/src/tm-threads-common.h +++ b/src/tm-threads-common.h @@ -55,6 +55,9 @@ typedef enum { TMM_LOGHTTPLOG, TMM_LOGHTTPLOG4, TMM_LOGHTTPLOG6, + TMM_LOGTLSLOG, + TMM_LOGTLSLOG4, + TMM_LOGTLSLOG6, TMM_PCAPLOG, TMM_FILELOG, TMM_FILESTORE, diff --git a/src/util-error.h b/src/util-error.h index a8e3750ca8..c25d70aa55 100644 --- a/src/util-error.h +++ b/src/util-error.h @@ -123,6 +123,7 @@ typedef enum { SC_ERR_DEBUG_LOG_GENERIC, SC_ERR_UNIFIED_LOG_GENERIC, SC_ERR_HTTP_LOG_GENERIC, + SC_ERR_TLS_LOG_GENERIC, SC_ERR_UNIFIED_ALERT_GENERIC, SC_ERR_UNIFIED2_ALERT_GENERIC, SC_ERR_FWRITE, @@ -190,7 +191,7 @@ typedef enum { SC_ERR_LIBNET11_INCOMPATIBLE_WITH_LIBCAP_NG, SC_WARN_FLOW_EMERGENCY, SC_WARN_COMPATIBILITY, - SC_ERR_SVC, + SC_ERR_SVC, SC_ERR_ERF_DAG_OPEN_FAILED, SC_ERR_ERF_DAG_STREAM_OPEN_FAILED, SC_ERR_ERF_DAG_STREAM_START_FAILED, diff --git a/suricata.yaml.in b/suricata.yaml.in index 99c6be8c25..f51cd4a501 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -75,6 +75,11 @@ outputs: #customformat: "%{%D-%H:%M:%S}t.%z %{X-Forwarded-For}i %H %m %h %u %s %B %a:%p -> %A:%P" #filetype: regular # 'regular', 'unix_stream' or 'unix_dgram' + # a line based log of TLS handshake parameters (no alerts) + - tls-log: + enabled: no # Log TLS connections. + filename: tls.log # File to store TLS logs. + # a line based log to used with pcap file study. # this module is dedicated to offline pcap parsing (empty output # if used with an other kind of input). It can interoperate with