From efdf96ccbaeca1703b48388d85cf57ad4086287d Mon Sep 17 00:00:00 2001 From: Jean-Paul Roliers Date: Wed, 1 Feb 2012 22:36:44 +0100 Subject: [PATCH 01/11] 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 cdb6c22d675a..88850018c1f0 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 000000000000..24602ad7dbad --- /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 000000000000..b7838be6c3b7 --- /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 efdc2ae0522a..00df8ee0a118 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 acf4528a9532..20033d842aa2 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 a8e3750ca81e..c25d70aa55af 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 99c6be8c25d5..f51cd4a501ea 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 From 9071bcf98333cb5166ebf3bb7a48ad1d262800e0 Mon Sep 17 00:00:00 2001 From: Jean-Paul Roliers Date: Thu, 2 Feb 2012 14:51:31 +0100 Subject: [PATCH 02/11] tls: adding cryptographic functions. Adding util-crypt containing cryptographic functions as SHA1 and Base64. --- src/Makefile.am | 1 + src/util-crypt.c | 271 +++++++++++++++++++++++++++++++++++++++++++++++ src/util-crypt.h | 78 ++++++++++++++ 3 files changed, 350 insertions(+) create mode 100644 src/util-crypt.c create mode 100644 src/util-crypt.h diff --git a/src/Makefile.am b/src/Makefile.am index 88850018c1f0..17becad4bc8d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -248,6 +248,7 @@ util-runmodes.c util-runmodes.h \ util-file.c util-file.h \ util-path.c util-path.h \ util-buffer.c util-buffer.h \ +util-crypt.c util-crypt.h \ tm-modules.c tm-modules.h \ tm-queues.c tm-queues.h \ tm-queuehandlers.c tm-queuehandlers.h \ diff --git a/src/util-crypt.c b/src/util-crypt.c new file mode 100644 index 000000000000..5f97215eaa23 --- /dev/null +++ b/src/util-crypt.c @@ -0,0 +1,271 @@ +/* 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 + * + * Implements cryptographic functions. + * Based on the libtomcrypt library ( http://libtom.org/?page=features&newsitems=5&whatfile=crypt ) + */ + +#include "util-crypt.h" + +#define F0(x,y,z) (z ^ (x & (y ^ z))) +#define F1(x,y,z) (x ^ y ^ z) +#define F2(x,y,z) ((x & y) | (z & (x | y))) +#define F3(x,y,z) (x ^ y ^ z) + + +static int Sha1Compress(HashState *md, unsigned char *buf) +{ + uint32_t a,b,c,d,e,W[80],i; + /* copy the state into 512-bits into W[0..15] */ + for (i = 0; i < 16; i++) { + LOAD32H(W[i], buf + (4*i)); + } + + /* copy state */ + a = md->sha1.state[0]; + b = md->sha1.state[1]; + c = md->sha1.state[2]; + d = md->sha1.state[3]; + e = md->sha1.state[4]; + + /* expand it */ + for (i = 16; i < 80; i++) { + W[i] = ROL(W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16], 1); + } + + /* compress */ + /* round one */ + #define FF0(a,b,c,d,e,i) e = (ROLc(a, 5) + F0(b,c,d) + e + W[i] + 0x5a827999UL); b = ROLc(b, 30); + #define FF1(a,b,c,d,e,i) e = (ROLc(a, 5) + F1(b,c,d) + e + W[i] + 0x6ed9eba1UL); b = ROLc(b, 30); + #define FF2(a,b,c,d,e,i) e = (ROLc(a, 5) + F2(b,c,d) + e + W[i] + 0x8f1bbcdcUL); b = ROLc(b, 30); + #define FF3(a,b,c,d,e,i) e = (ROLc(a, 5) + F3(b,c,d) + e + W[i] + 0xca62c1d6UL); b = ROLc(b, 30); + + for (i = 0; i < 20; ) { + FF0(a,b,c,d,e,i++); + FF0(e,a,b,c,d,i++); + FF0(d,e,a,b,c,i++); + FF0(c,d,e,a,b,i++); + FF0(b,c,d,e,a,i++); + } + + /* round two */ + for (; i < 40; ) { + FF1(a,b,c,d,e,i++); + FF1(e,a,b,c,d,i++); + FF1(d,e,a,b,c,i++); + FF1(c,d,e,a,b,i++); + FF1(b,c,d,e,a,i++); + } + + /* round three */ + for (; i < 60; ) { + FF2(a,b,c,d,e,i++); + FF2(e,a,b,c,d,i++); + FF2(d,e,a,b,c,i++); + FF2(c,d,e,a,b,i++); + FF2(b,c,d,e,a,i++); + } + + /* round four */ + for (; i < 80; ) { + FF3(a,b,c,d,e,i++); + FF3(e,a,b,c,d,i++); + FF3(d,e,a,b,c,i++); + FF3(c,d,e,a,b,i++); + FF3(b,c,d,e,a,i++); + } + + #undef FF0 + #undef FF1 + #undef FF2 + #undef FF3 + + /* store */ + md->sha1.state[0] = md->sha1.state[0] + a; + md->sha1.state[1] = md->sha1.state[1] + b; + md->sha1.state[2] = md->sha1.state[2] + c; + md->sha1.state[3] = md->sha1.state[3] + d; + md->sha1.state[4] = md->sha1.state[4] + e; + + return SC_SHA_1_OK; +} + +static int Sha1Init(HashState * md) +{ + if(md == NULL) + { + return SC_SHA_1_NOK; + } + md->sha1.state[0] = 0x67452301UL; + md->sha1.state[1] = 0xefcdab89UL; + md->sha1.state[2] = 0x98badcfeUL; + md->sha1.state[3] = 0x10325476UL; + md->sha1.state[4] = 0xc3d2e1f0UL; + md->sha1.curlen = 0; + md->sha1.length = 0; + return SC_SHA_1_OK; +} + +static int Sha1Process (HashState * md, const unsigned char *in, unsigned long inlen) +{ + if(md == NULL || in == NULL) { + return SC_SHA_1_INVALID_ARG; + } + + unsigned long n; + int err; + + if (md->sha1.curlen > sizeof(md->sha1.buf)) { + return SC_SHA_1_INVALID_ARG; + } + while (inlen > 0) { + if (md-> sha1.curlen == 0 && inlen >= 64) { + if ((err = Sha1Compress(md, (unsigned char *)in)) != SC_SHA_1_OK) { + return err; + } + md-> sha1 .length += 64 * 8; + in += 64; + inlen -= 64; + } else { + n = MIN(inlen, (64 - md-> sha1 .curlen)); + memcpy(md-> sha1 .buf + md-> sha1.curlen, in, (size_t)n); + md-> sha1 .curlen += n; + in += n; + inlen -= n; + if (md-> sha1 .curlen == 64) { + if ((err = Sha1Compress(md, md-> sha1 .buf)) != SC_SHA_1_OK) { + return err; + } + md-> sha1 .length += 8*64; + md-> sha1 .curlen = 0; + } + } + } + return SC_SHA_1_OK; +} + + + +static int Sha1Done(HashState * md, unsigned char *out) +{ + int i; + + if (md == NULL || out == NULL) + { + return SC_SHA_1_NOK; + } + + if (md->sha1.curlen >= sizeof(md->sha1.buf)) { + return SC_SHA_1_INVALID_ARG; + } + + /* increase the length of the message */ + md->sha1.length += md->sha1.curlen * 8; + + /* append the '1' bit */ + md->sha1.buf[md->sha1.curlen++] = (unsigned char)0x80; + + /* if the length is currently above 56 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (md->sha1.curlen > 56) { + while (md->sha1.curlen < 64) { + md->sha1.buf[md->sha1.curlen++] = (unsigned char)0; + } + Sha1Compress(md, md->sha1.buf); + md->sha1.curlen = 0; + } + + /* pad upto 56 bytes of zeroes */ + while (md->sha1.curlen < 56) { + md->sha1.buf[md->sha1.curlen++] = (unsigned char)0; + } + + /* store length */ + STORE64H(md->sha1.length, md->sha1.buf+56); + Sha1Compress(md, md->sha1.buf); + + /* copy output */ + for (i = 0; i < 5; i++) { + STORE32H(md->sha1.state[i], out+(4*i)); + } + + memset(md, 0, sizeof(HashState)); + + return SC_SHA_1_OK; +} + +unsigned char* ComputeSHA1(unsigned char* buff, int bufflen) +{ + HashState md; + unsigned char* lResult = (unsigned char*) SCMalloc((sizeof(unsigned char) * 20)); + if (lResult == NULL) + return NULL; + Sha1Init(&md); + Sha1Process(&md, buff, bufflen); + Sha1Done(&md, lResult); + return lResult; +} + +static const char *b64codes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +int Base64Encode(const unsigned char *in, unsigned long inlen, + unsigned char *out, unsigned long *outlen) +{ + unsigned long i, len2, leven; + unsigned char *p; + if(in == NULL || out == NULL || outlen == NULL) + { + return SC_BASE64_INVALID_ARG; + } + /* valid output size ? */ + len2 = 4 * ((inlen + 2) / 3); + if (*outlen < len2 + 1) { + *outlen = len2 + 1; + return SC_BASE64_OVERFLOW; + } + p = out; + leven = 3*(inlen / 3); + for (i = 0; i < leven; i += 3) { + *p++ = b64codes[(in[0] >> 2) & 0x3F]; + *p++ = b64codes[(((in[0] & 3) << 4) + (in[1] >> 4)) & 0x3F]; + *p++ = b64codes[(((in[1] & 0xf) << 2) + (in[2] >> 6)) & 0x3F]; + *p++ = b64codes[in[2] & 0x3F]; + in += 3; + } + /* Pad it if necessary... */ + if (i < inlen) { + unsigned a = in[0]; + unsigned b = (i+1 < inlen) ? in[1] : 0; + + *p++ = b64codes[(a >> 2) & 0x3F]; + *p++ = b64codes[(((a & 3) << 4) + (b >> 4)) & 0x3F]; + *p++ = (i+1 < inlen) ? b64codes[(((b & 0xf) << 2)) & 0x3F] : '='; + *p++ = '='; + } + /* append a NULL byte */ + *p = '\0'; + /* return ok */ + *outlen = p - out; + return SC_BASE64_OK; +} diff --git a/src/util-crypt.h b/src/util-crypt.h new file mode 100644 index 000000000000..3a7ca427a3b2 --- /dev/null +++ b/src/util-crypt.h @@ -0,0 +1,78 @@ +/* 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 + * + * Implements cryptographic functions. + * Based on the libtomcrypt library ( http://libtom.org/?page=features&newsitems=5&whatfile=crypt ) + */ + +#ifndef UTIL_CRYPT_H_ +#define UTIL_CRYPT_H_ + +#include "suricata-common.h" + +#define LOAD32H(x, y) \ + { x = ((unsigned long)((y)[0] & 255)<<24) | \ + ((unsigned long)((y)[1] & 255)<<16) | \ + ((unsigned long)((y)[2] & 255)<<8) | \ + ((unsigned long)((y)[3] & 255)); } + +#define STORE64H(x, y) \ + { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255); \ + (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255); \ + (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255); \ + (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); } + +#define STORE32H(x, y) \ + { (y)[0] = (unsigned char)(((x)>>24)&255); (y)[1] = (unsigned char)(((x)>>16)&255); \ + (y)[2] = (unsigned char)(((x)>>8)&255); (y)[3] = (unsigned char)((x)&255); } + +#define ROL(x, y) ( (((unsigned long)(x)<<(unsigned long)((y)&31)) | (((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL) +#define ROLc(x, y) ( (((unsigned long)(x)<<(unsigned long)((y)&31)) | (((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL) +#define MIN(x, y) ( ((x)<(y))?(x):(y) ) + +typedef enum { + SC_SHA_1_OK, + SC_SHA_1_NOK, + SC_SHA_1_INVALID_ARG, + + SC_BASE64_OK, + SC_BASE64_INVALID_ARG, + SC_BASE64_OVERFLOW, + +} CryptId; + +typedef struct Sha1State_ { + uint64_t length; + uint32_t state[5], curlen; + unsigned char buf[64]; +} Sha1State; + +typedef union HashState_ { + char dummy[1]; + Sha1State sha1; + void *data; +} HashState; + +unsigned char* ComputeSHA1(unsigned char* buff, int bufflen); +int Base64Encode(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen); + +#endif /* UTIL_CRYPT_H_ */ From afbf82cb59b0b60b01943a648c232d030ef15e0e Mon Sep 17 00:00:00 2001 From: Eric Leblond Date: Thu, 5 Apr 2012 16:45:24 +0200 Subject: [PATCH 03/11] tls: add NSS version for SHA1 computing function. --- src/util-crypt.c | 31 +++++++++++++++++++++++++++++++ src/util-crypt.h | 4 ++++ 2 files changed, 35 insertions(+) diff --git a/src/util-crypt.c b/src/util-crypt.c index 5f97215eaa23..9ec881f10b16 100644 --- a/src/util-crypt.c +++ b/src/util-crypt.c @@ -22,10 +22,16 @@ * * Implements cryptographic functions. * Based on the libtomcrypt library ( http://libtom.org/?page=features&newsitems=5&whatfile=crypt ) + * + * Implementation of function using NSS is not linked with libtomcrypt. */ +#include "suricata-common.h" +#include "suricata.h" #include "util-crypt.h" +#ifndef HAVE_NSS + #define F0(x,y,z) (z ^ (x & (y ^ z))) #define F1(x,y,z) (x ^ y ^ z) #define F2(x,y,z) ((x & y) | (z & (x | y))) @@ -227,6 +233,31 @@ unsigned char* ComputeSHA1(unsigned char* buff, int bufflen) return lResult; } +#else /* HAVE_NSS */ + +unsigned char* ComputeSHA1(unsigned char* buff, int bufflen) +{ + HASHContext *sha1_ctx = HASH_Create(HASH_AlgSHA1); + unsigned char* lResult = NULL; + if (sha1_ctx == NULL) { + return NULL; + } + + lResult = (unsigned char*) SCMalloc((sizeof(unsigned char) * 20)); + if (lResult == NULL) { + HASH_Destroy(sha1_ctx); + return NULL; + } + HASH_Begin(sha1_ctx); + HASH_Update(&md, buff, bufflen); + HASH_End(sha1_ctx, lResult, (sizeof(unsigned char) * 20)); + HASH_Destroy(sha1_ctx); + + return lResult; +} + +#endif /* HAVE_NSS */ + static const char *b64codes = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; int Base64Encode(const unsigned char *in, unsigned long inlen, diff --git a/src/util-crypt.h b/src/util-crypt.h index 3a7ca427a3b2..a78e1106cdcc 100644 --- a/src/util-crypt.h +++ b/src/util-crypt.h @@ -29,6 +29,8 @@ #include "suricata-common.h" +#ifndef HAVE_NSS + #define LOAD32H(x, y) \ { x = ((unsigned long)((y)[0] & 255)<<24) | \ ((unsigned long)((y)[1] & 255)<<16) | \ @@ -72,6 +74,8 @@ typedef union HashState_ { void *data; } HashState; +#endif /* don't HAVE_NSS */ + unsigned char* ComputeSHA1(unsigned char* buff, int bufflen); int Base64Encode(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen); From 5e545e452b423c9e0ff504d53513196f383cbeab Mon Sep 17 00:00:00 2001 From: Jean-Paul Roliers Date: Thu, 2 Feb 2012 15:07:42 +0100 Subject: [PATCH 04/11] tls: adding fingerprint calculation. Adding a pointer in ssl_state struct and compute fingerprint during certificate decoding. --- src/app-layer-ssl.c | 4 ++++ src/app-layer-ssl.h | 1 + src/app-layer-tls-handshake.c | 29 +++++++++++++++++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/src/app-layer-ssl.c b/src/app-layer-ssl.c index f8012c32e233..c092a5f98b8a 100644 --- a/src/app-layer-ssl.c +++ b/src/app-layer-ssl.c @@ -878,6 +878,8 @@ void SSLStateFree(void *p) SCFree(ssl_state->client_connp.cert0_subject); if (ssl_state->client_connp.cert0_issuerdn) SCFree(ssl_state->client_connp.cert0_issuerdn); + if (ssl_state->client_connp.cert0_fingerprint) + SCFree(ssl_state->client_connp.cert0_fingerprint); if (ssl_state->server_connp.trec) SCFree(ssl_state->server_connp.trec); @@ -885,6 +887,8 @@ void SSLStateFree(void *p) SCFree(ssl_state->server_connp.cert0_subject); if (ssl_state->server_connp.cert0_issuerdn) SCFree(ssl_state->server_connp.cert0_issuerdn); + if (ssl_state->server_connp.cert0_fingerprint) + SCFree(ssl_state->server_connp.cert0_fingerprint); SCFree(ssl_state); diff --git a/src/app-layer-ssl.h b/src/app-layer-ssl.h index f96e04d3a573..85da873c1fe1 100644 --- a/src/app-layer-ssl.h +++ b/src/app-layer-ssl.h @@ -100,6 +100,7 @@ typedef struct SSLStateConnp_ { char *cert0_subject; char *cert0_issuerdn; + char *cert0_fingerprint; /* buffer for the tls record. * We use a malloced buffer, if the record is fragmented */ diff --git a/src/app-layer-tls-handshake.c b/src/app-layer-tls-handshake.c index 053b6d09481b..ce0349cafdea 100644 --- a/src/app-layer-tls-handshake.c +++ b/src/app-layer-tls-handshake.c @@ -50,6 +50,8 @@ #include "util-decode-der.h" #include "util-decode-der-get.h" +#include "util-crypt.h" + #define SSLV3_RECORD_LEN 5 static void TLSCertificateErrCodeToWarning(SSLState *ssl_state, uint32_t errcode) @@ -143,6 +145,32 @@ int DecodeTLSHandshakeServerCertificate(SSLState *ssl_state, uint8_t *input, uin } } DerFree(cert); + + if (i == 0 && ssl_state->server_connp.cert0_fingerprint == NULL) { + int msg_len = cur_cert_length; + int hash_len = 20; + int out_len = 60; + char out[out_len]; + unsigned char* hash; + hash = ComputeSHA1((unsigned char*) input, (int) msg_len); + char *p = out; + int j = 0; + + if (hash == NULL) { + SCLogWarning(SC_ERR_MEM_ALLOC, "Can not allocate fingerprint string"); + } else { + + for (j = 0; j < hash_len; j++, p += 3) { + snprintf(p, 4, j == hash_len - 1 ? "%02x" : "%02x:", hash[j]); + } + SCFree(hash); + ssl_state->server_connp.cert0_fingerprint = SCStrdup(out); + if (ssl_state->server_connp.cert0_fingerprint == NULL) { + SCLogWarning(SC_ERR_MEM_ALLOC, "Can not allocate fingerprint string"); + } + } + } + } i++; @@ -152,5 +180,6 @@ int DecodeTLSHandshakeServerCertificate(SSLState *ssl_state, uint8_t *input, uin } return parsed; + } From 79ea794d7153dfc6b747493c07d967101d177df8 Mon Sep 17 00:00:00 2001 From: Jean-Paul Roliers Date: Sat, 4 Feb 2012 17:15:11 +0100 Subject: [PATCH 05/11] tls: adding fingerprint to TLS Log information. Improve TLS logging by adding the certificate fingerprint to TLS Log file. Add the extending option to the tls-log entry in suricata.yaml. --- src/log-tlslog.c | 27 ++++++++++++++++++++++++++- suricata.yaml.in | 1 + 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/log-tlslog.c b/src/log-tlslog.c index 24602ad7dbad..bb1fae1b6ae9 100644 --- a/src/log-tlslog.c +++ b/src/log-tlslog.c @@ -54,6 +54,9 @@ #define OUTPUT_BUFFER_SIZE 65535 +#define LOG_TLS_DEFAULT 0 +#define LOG_TLS_EXTENDED 1 + TmEcode LogTlsLog(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *); TmEcode LogTlsLogIPv4(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *); TmEcode LogTlsLogIPv6(ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *); @@ -121,6 +124,13 @@ static void CreateTimeString(const struct timeval *ts, char *str, size_t size) 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 void LogTlsLogExtended(LogTlsLogThread *aft, SSLState * state) +{ + if (state->server_connp.cert0_fingerprint != NULL) { + MemBufferWriteString(aft->buffer, " SHA1='%s'\n", state->server_connp.cert0_fingerprint); + } +} + static TmEcode LogTlsLogIPWrapper(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq, int ipproto) { @@ -195,11 +205,16 @@ static TmEcode LogTlsLogIPWrapper(ThreadVars *tv, Packet *p, void *data, PacketQ MemBufferReset(aft->buffer); MemBufferWriteString(aft->buffer, - "%s %s:%d -> %s:%d TLS: Subject='%s' Issuerdn='%s'\n", + "%s %s:%d -> %s:%d TLS: Subject='%s' Issuerdn='%s'", timebuf, srcip, sp, dstip, dp, ssl_state->server_connp.cert0_subject, ssl_state->server_connp.cert0_issuerdn); AppLayerTransactionUpdateLoggedId(p->flow); + if (hlog->flags & LOG_TLS_EXTENDED) { + LogTlsLogExtended(aft, ssl_state); + } else { + MemBufferWriteString(aft->buffer, "\n"); + } aft->tls_cnt ++; @@ -321,6 +336,16 @@ OutputCtx *LogTlsLogInitCtx(ConfNode *conf) return NULL; tlslog_ctx->file_ctx = file_ctx; + const char *extended = ConfNodeLookupChildValue(conf, "extended"); + if (extended == NULL) { + tlslog_ctx->flags |= LOG_TLS_DEFAULT; + } else { + if (ConfValIsTrue(extended)) { + tlslog_ctx->flags |= LOG_TLS_EXTENDED; + } + } + + OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx)); if (output_ctx == NULL) return NULL; diff --git a/suricata.yaml.in b/suricata.yaml.in index f51cd4a501ea..e6ce8bacc961 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -79,6 +79,7 @@ outputs: - tls-log: enabled: no # Log TLS connections. filename: tls.log # File to store TLS logs. + #extended: yes # Log extended information like fingerprint # a line based log to used with pcap file study. # this module is dedicated to offline pcap parsing (empty output From a12e481b9235aadcc922b845730fee671c2dd88b Mon Sep 17 00:00:00 2001 From: Jean-Paul Roliers Date: Thu, 2 Feb 2012 16:45:35 +0100 Subject: [PATCH 06/11] tls: adding support for fingerprint rule matching. Add the support for tls.fingerprint keyword in rules. --- src/detect-tls.c | 292 ++++++++++++++++++++++++++++++++++++++++------- src/detect-tls.h | 1 + src/detect.h | 1 + 3 files changed, 254 insertions(+), 40 deletions(-) diff --git a/src/detect-tls.c b/src/detect-tls.c index 110f775391a8..ba88083f7b74 100644 --- a/src/detect-tls.c +++ b/src/detect-tls.c @@ -63,12 +63,16 @@ /** * \brief Regex for parsing "id" option, matching number or "number" */ + #define PARSE_REGEX "^\\s*(\\!*)\\s*([A-z0-9\\s\\-\\.=,\\*]+|\"[A-z0-9\\s\\-\\.=,\\*]+\")\\s*$" +#define PARSE_REGEX_FINGERPRINT "^\\s*(\\!*)\\s*([A-z0-9\\:\\*]+|\"[A-z0-9\\:\\* ]+\")\\s*$" static pcre *subject_parse_regex; static pcre_extra *subject_parse_regex_study; static pcre *issuerdn_parse_regex; static pcre_extra *issuerdn_parse_regex_study; +static pcre *fingerprint_parse_regex; +static pcre_extra *fingerprint_parse_regex_study; static int DetectTlsSubjectMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *, uint8_t, void *, Signature *, SigMatch *); static int DetectTlsSubjectSetup (DetectEngineCtx *, Signature *, char *); @@ -78,7 +82,9 @@ static int DetectTlsIssuerDNMatch (ThreadVars *, DetectEngineThreadCtx *, Flow * static int DetectTlsIssuerDNSetup (DetectEngineCtx *, Signature *, char *); static void DetectTlsIssuerDNRegisterTests(void); static void DetectTlsIssuerDNFree(void *); - +static int DetectTlsFingerprintMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *, uint8_t, void *, Signature *, SigMatch *); +static int DetectTlsFingerprintSetup (DetectEngineCtx *, Signature *, char *); +static void DetectTlsFingerprintFree(void *); /** * \brief Registration function for keyword: tls.version */ @@ -99,6 +105,14 @@ void DetectTlsRegister (void) { sigmatch_table[DETECT_AL_TLS_ISSUERDN].Free = DetectTlsIssuerDNFree; sigmatch_table[DETECT_AL_TLS_ISSUERDN].RegisterTests = DetectTlsIssuerDNRegisterTests; + sigmatch_table[DETECT_AL_TLS_FINGERPRINT].name = "tls.fingerprint"; + sigmatch_table[DETECT_AL_TLS_FINGERPRINT].Match = NULL; + sigmatch_table[DETECT_AL_TLS_FINGERPRINT].AppLayerMatch = DetectTlsFingerprintMatch; + sigmatch_table[DETECT_AL_TLS_FINGERPRINT].alproto = ALPROTO_TLS; + sigmatch_table[DETECT_AL_TLS_FINGERPRINT].Setup = DetectTlsFingerprintSetup; + sigmatch_table[DETECT_AL_TLS_FINGERPRINT].Free = DetectTlsFingerprintFree; + sigmatch_table[DETECT_AL_TLS_FINGERPRINT].RegisterTests = NULL; + const char *eb; int eo; int opts = 0; @@ -132,6 +146,21 @@ void DetectTlsRegister (void) { SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb); goto error; } + + SCLogDebug("registering tls.fingerprint rule option"); + + fingerprint_parse_regex = pcre_compile(PARSE_REGEX_FINGERPRINT, opts, &eb, &eo, NULL); + if (fingerprint_parse_regex == NULL) { + SCLogError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_REGEX_FINGERPRINT, eo, eb); + goto error; + } + + fingerprint_parse_regex_study = pcre_study(fingerprint_parse_regex, 0, &eb); + if (eb != NULL) { + SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb); + goto error; + } + return; error: @@ -208,6 +237,10 @@ static DetectTlsData *DetectTlsSubjectParse (char *str) #define MAX_SUBSTRINGS 30 int ret = 0, res = 0; int ov[MAX_SUBSTRINGS]; + const char *str_ptr; + char *orig; + char *tmp_str; + uint32_t flag = 0; ret = pcre_exec(subject_parse_regex, subject_parse_regex_study, str, strlen(str), 0, 0, ov, MAX_SUBSTRINGS); @@ -217,52 +250,45 @@ static DetectTlsData *DetectTlsSubjectParse (char *str) goto error; } - if (ret == 3) { - const char *str_ptr; - char *orig; - char *tmp_str; - uint32_t flag = 0; - - res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 1, &str_ptr); - if (res < 0) { - SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); - goto error; - } - if (str_ptr[0] == '!') - flag = DETECT_CONTENT_NEGATED; + res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 1, &str_ptr); + if (res < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; + } + if (str_ptr[0] == '!') + flag = DETECT_CONTENT_NEGATED; - res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 2, &str_ptr); - if (res < 0) { - SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); - goto error; - } + res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 2, &str_ptr); + if (res < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; + } - /* We have a correct id option */ - tls = SCMalloc(sizeof(DetectTlsData)); - if (tls == NULL) - goto error; - tls->subject = NULL; - tls->flags = flag; + /* We have a correct id option */ + tls = SCMalloc(sizeof(DetectTlsData)); + if (tls == NULL) + goto error; + tls->subject = NULL; + tls->flags = flag; - orig = SCStrdup((char*)str_ptr); - tmp_str=orig; - if (tmp_str == NULL) { - goto error; - } + orig = SCStrdup((char*)str_ptr); + tmp_str=orig; + if (tmp_str == NULL) { + goto error; + } - /* Let's see if we need to escape "'s */ - if (tmp_str[0] == '"') - { - tmp_str[strlen(tmp_str) - 1] = '\0'; - tmp_str += 1; - } + /* Let's see if we need to escape "'s */ + if (tmp_str[0] == '"') + { + tmp_str[strlen(tmp_str) - 1] = '\0'; + tmp_str += 1; + } - tls->subject = SCStrdup(tmp_str); + tls->subject = SCStrdup(tmp_str); - SCFree(orig); + SCFree(orig); - SCLogDebug("will look for TLS subject %s", tls->subject); - } + SCLogDebug("will look for TLS subject %s", tls->subject); return tls; @@ -533,6 +559,192 @@ static void DetectTlsIssuerDNFree(void *ptr) SCFree(id_d); } +/** + * \brief This function is used to parse fingerprint passed via keyword: "fingerprint" + * + * \param idstr Pointer to the user provided fingerprint option + * + * \retval pointer to DetectTlsData on success + * \retval NULL on failure + */ +static DetectTlsData *DetectTlsFingerprintParse (char *str) +{ + DetectTlsData *tls = NULL; +#define MAX_SUBSTRINGS 30 + int ret = 0, res = 0; + int ov[MAX_SUBSTRINGS]; + const char *str_ptr; + char *orig; + char *tmp_str; + uint32_t flag = 0; + + ret = pcre_exec(fingerprint_parse_regex, fingerprint_parse_regex_study, str, strlen(str), 0, 0, + ov, MAX_SUBSTRINGS); + + if (ret != 3) { + SCLogError(SC_ERR_PCRE_MATCH, "invalid tls.fingerprint option"); + goto error; + } + + res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 1, &str_ptr); + if (res < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; + } + if (str_ptr[0] == '!') + flag = DETECT_CONTENT_NEGATED; + + res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 2, &str_ptr); + if (res < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; + } + + /* We have a correct id option */ + tls = SCMalloc(sizeof(DetectTlsData)); + if (tls == NULL) + goto error; + tls->fingerprint = NULL; + tls->flags = flag; + + orig = SCStrdup((char*)str_ptr); + tmp_str=orig; + if (tmp_str == NULL) { + goto error; + } + + /* Let's see if we need to escape "'s */ + if (tmp_str[0] == '"') + { + tmp_str[strlen(tmp_str) - 1] = '\0'; + tmp_str += 1; + } + + tls->fingerprint = SCStrdup(tmp_str); + if (tls->fingerprint == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "Unable to allocate fingerprint"); + } + + SCFree(orig); + + SCLogDebug("will look for TLS fingerprint %s", tls->subject); + + return tls; + +error: + if (tls != NULL) + DetectTlsFingerprintFree(tls); + return NULL; + +} +/** + * \brief match the specified fingerprint on a tls session + * + * \param t pointer to thread vars + * \param det_ctx pointer to the pattern matcher thread + * \param p pointer to the current packet + * \param m pointer to the sigmatch that we will cast into DetectTlsData + * + * \retval 0 no match + * \retval 1 match + */ +static int DetectTlsFingerprintMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state, Signature *s, SigMatch *m) +{ + SCEnter(); + DetectTlsData *tls_data = (DetectTlsData *)m->ctx; + SSLState *ssl_state = (SSLState *)state; + if (ssl_state == NULL) { + SCLogDebug("no tls state, no match"); + SCReturnInt(0); + } + + int ret = 0; + FLOWLOCK_RDLOCK(f); + + if (tls_data->flags & DETECT_CONTENT_NEGATED) { + ret = 1; + } else { + ret = 0; + } + if (ssl_state->server_connp.cert0_fingerprint != NULL) { + SCLogDebug("TLS: Fingerprint is [%s], looking for [%s]\n", + ssl_state->server_connp.cert0_fingerprint, + tls_data->fingerprint); + + if (tls_data->fingerprint && + (strstr(ssl_state->server_connp.cert0_fingerprint, + tls_data->fingerprint) != NULL)) { + if (tls_data->flags & DETECT_CONTENT_NEGATED) { + ret = 0; + } else { + ret = 1; + + } + } + } + + FLOWLOCK_UNLOCK(f); + + SCReturnInt(ret); +} + +/** + * \brief this function is used to add the parsed "fingerprint" option + * \brief into the current signature + * + * \param de_ctx pointer to the Detection Engine Context + * \param s pointer to the Current Signature + * \param id pointer to the user provided "fingerprint" option + * + * \retval 0 on Success + * \retval -1 on Failure + */ +static int DetectTlsFingerprintSetup (DetectEngineCtx *de_ctx, Signature *s, char *str) +{ + DetectTlsData *tls = NULL; + SigMatch *sm = NULL; + + tls = DetectTlsFingerprintParse(str); + if (tls == NULL) goto error; + + /* Okay so far so good, lets get this into a SigMatch + * and put it in the Signature. */ + sm = SigMatchAlloc(); + if (sm == NULL) + goto error; + + sm->type = DETECT_AL_TLS_FINGERPRINT; + sm->ctx = (void *)tls; + + SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_AMATCH); + + if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_TLS) { + SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords."); + goto error; + } + + s->alproto = ALPROTO_TLS; + return 0; + +error: + if (tls != NULL) DetectTlsFingerprintFree(tls); + if (sm != NULL) SCFree(sm); + return -1; + +} + +/** + * \brief this function will free memory associated with DetectTlsData + * + * \param pointer to DetectTlsData + */ +static void DetectTlsFingerprintFree(void *ptr) { + DetectTlsData *id_d = (DetectTlsData *)ptr; + if (id_d->fingerprint) + SCFree(id_d->fingerprint); + SCFree(id_d); +} + /** * \brief this function registers unit tests for DetectTlsIssuerDN */ diff --git a/src/detect-tls.h b/src/detect-tls.h index 550052e8be11..71652eb99dec 100644 --- a/src/detect-tls.h +++ b/src/detect-tls.h @@ -39,6 +39,7 @@ typedef struct DetectTlsData_ { uint32_t flags; /** flags containing match variant (Negation for example) */ char * subject; /** tls certificate subject substring to match */ char * issuerdn; /** tls certificate issuerDN substring to match */ + char * fingerprint; /** tls fingerprint substring to match */ } DetectTlsData; /* prototypes */ diff --git a/src/detect.h b/src/detect.h index 2b0041aafe12..86ca8d3ff09b 100644 --- a/src/detect.h +++ b/src/detect.h @@ -995,6 +995,7 @@ enum { DETECT_AL_TLS_VERSION, DETECT_AL_TLS_SUBJECT, DETECT_AL_TLS_ISSUERDN, + DETECT_AL_TLS_FINGERPRINT, DETECT_AL_HTTP_COOKIE, DETECT_AL_HTTP_METHOD, From bffc946cc9a67abb6a64452f4d92488688f2e641 Mon Sep 17 00:00:00 2001 From: Jean-Paul Roliers Date: Sat, 4 Feb 2012 17:37:41 +0100 Subject: [PATCH 07/11] tls: adding store option for TLS This patch adds a TLS store option to save certificate in PEM format. Each time the store action is met, a file and a metafile are created. Reworked-by: Eric Leblond --- src/app-layer-ssl.c | 2 + src/app-layer-ssl.h | 8 + src/app-layer-tls-handshake.c | 11 +- src/detect-tls.c | 70 +++++++++ src/detect.h | 3 + src/log-tlslog.c | 284 +++++++++++++++++++++++++++++----- suricata.yaml.in | 1 + 7 files changed, 336 insertions(+), 43 deletions(-) diff --git a/src/app-layer-ssl.c b/src/app-layer-ssl.c index c092a5f98b8a..6c71a44c1983 100644 --- a/src/app-layer-ssl.c +++ b/src/app-layer-ssl.c @@ -860,6 +860,8 @@ void *SSLStateAlloc(void) if (ssl_state == NULL) return NULL; memset(ssl_state, 0, sizeof(SSLState)); + ((SSLState*)ssl_state)->client_connp.cert_log_flag = 0; + ((SSLState*)ssl_state)->server_connp.cert_log_flag = 0; return ssl_state; } diff --git a/src/app-layer-ssl.h b/src/app-layer-ssl.h index 85da873c1fe1..1d6f1f81bbcf 100644 --- a/src/app-layer-ssl.h +++ b/src/app-layer-ssl.h @@ -64,6 +64,9 @@ enum { #define SSL_AL_FLAG_STATE_SERVER_KEYX 0x1000 #define SSL_AL_FLAG_STATE_UNKNOWN 0x2000 +#define SSL_TLS_LOG_PEM (1 << 0) + + /* SSL versions. We'll use a unified format for all, with the top byte * holding the major version and the lower byte the minor version */ @@ -102,6 +105,11 @@ typedef struct SSLStateConnp_ { char *cert0_issuerdn; char *cert0_fingerprint; + uint8_t *cert_input; + uint32_t cert_input_len; + + uint32_t cert_log_flag; + /* buffer for the tls record. * We use a malloced buffer, if the record is fragmented */ uint8_t *trec; diff --git a/src/app-layer-tls-handshake.c b/src/app-layer-tls-handshake.c index ce0349cafdea..8c086cbeec9e 100644 --- a/src/app-layer-tls-handshake.c +++ b/src/app-layer-tls-handshake.c @@ -124,8 +124,8 @@ int DecodeTLSHandshakeServerCertificate(SSLState *ssl_state, uint8_t *input, uin } else { //SCLogInfo("TLS Cert %d: %s\n", i, buffer); if (i==0) { - ssl_state->curr_connp->cert0_subject = SCStrdup(buffer); - if (ssl_state->curr_connp->cert0_subject == NULL) { + ssl_state->server_connp.cert0_subject = SCStrdup(buffer); + if (ssl_state->server_connp.cert0_subject == NULL) { DerFree(cert); return -1; } @@ -137,8 +137,8 @@ int DecodeTLSHandshakeServerCertificate(SSLState *ssl_state, uint8_t *input, uin } else { //SCLogInfo("TLS IssuerDN %d: %s\n", i, buffer); if (i==0) { - ssl_state->curr_connp->cert0_issuerdn = SCStrdup(buffer); - if (ssl_state->curr_connp->cert0_issuerdn == NULL) { + ssl_state->server_connp.cert0_issuerdn = SCStrdup(buffer); + if (ssl_state->server_connp.cert0_issuerdn == NULL) { DerFree(cert); return -1; } @@ -169,6 +169,9 @@ int DecodeTLSHandshakeServerCertificate(SSLState *ssl_state, uint8_t *input, uin SCLogWarning(SC_ERR_MEM_ALLOC, "Can not allocate fingerprint string"); } } + + ssl_state->server_connp.cert_input = input; + ssl_state->server_connp.cert_input_len = cur_cert_length; } } diff --git a/src/detect-tls.c b/src/detect-tls.c index ba88083f7b74..2075494bd614 100644 --- a/src/detect-tls.c +++ b/src/detect-tls.c @@ -85,6 +85,9 @@ static void DetectTlsIssuerDNFree(void *); static int DetectTlsFingerprintMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *, uint8_t, void *, Signature *, SigMatch *); static int DetectTlsFingerprintSetup (DetectEngineCtx *, Signature *, char *); static void DetectTlsFingerprintFree(void *); +static int DetectTlsStoreSetup (DetectEngineCtx *, Signature *, char *); +static int DetectTlsStoreMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *, uint8_t, void *, Signature *, SigMatch *); + /** * \brief Registration function for keyword: tls.version */ @@ -113,6 +116,15 @@ void DetectTlsRegister (void) { sigmatch_table[DETECT_AL_TLS_FINGERPRINT].Free = DetectTlsFingerprintFree; sigmatch_table[DETECT_AL_TLS_FINGERPRINT].RegisterTests = NULL; + sigmatch_table[DETECT_AL_TLS_STORE].name = "tls.store"; + sigmatch_table[DETECT_AL_TLS_STORE].Match = NULL; + sigmatch_table[DETECT_AL_TLS_STORE].AppLayerMatch = DetectTlsStoreMatch; + sigmatch_table[DETECT_AL_TLS_STORE].alproto = ALPROTO_TLS; + sigmatch_table[DETECT_AL_TLS_STORE].Setup = DetectTlsStoreSetup; + sigmatch_table[DETECT_AL_TLS_STORE].Free = NULL; + sigmatch_table[DETECT_AL_TLS_STORE].RegisterTests = NULL; + sigmatch_table[DETECT_AL_TLS_STORE].flags |= SIGMATCH_NOOPT; + const char *eb; int eo; int opts = 0; @@ -745,6 +757,64 @@ static void DetectTlsFingerprintFree(void *ptr) { SCFree(id_d); } +/** + * \brief this function is used to add the parsed "store" option + * \brief into the current signature + * + * \param de_ctx pointer to the Detection Engine Context + * \param s pointer to the Current Signature + * \param idstr pointer to the user provided "store" option + * + * \retval 0 on Success + * \retval -1 on Failure + */ +static int DetectTlsStoreSetup (DetectEngineCtx *de_ctx, Signature *s, char *str) +{ + SigMatch *sm = NULL; + + s->flags |= SIG_FLAG_TLSSTORE; + + sm = SigMatchAlloc(); + if (sm == NULL) + goto error; + + sm->type = DETECT_AL_TLS_STORE; + SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_AMATCH); + + if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_TLS) { + SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords."); + goto error; + } + + s->alproto = ALPROTO_TLS; + return 0; + +error: + if (sm != NULL) SCFree(sm); + return -1; + +} + +static int DetectTlsStoreMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state, Signature *s, SigMatch *m) +{ + SCEnter(); + + SSLState *ssl_state = (SSLState *)state; + if (ssl_state == NULL) { + SCLogDebug("no tls state, no match"); + SCReturnInt(1); + } + + FLOWLOCK_WRLOCK(f); + if (s->flags & SIG_FLAG_TLSSTORE) { + ssl_state->server_connp.cert_log_flag |= SSL_TLS_LOG_PEM; + } + + FLOWLOCK_UNLOCK(f); + SCReturnInt(1); +} + + /** * \brief this function registers unit tests for DetectTlsIssuerDN */ diff --git a/src/detect.h b/src/detect.h index 86ca8d3ff09b..be9607e45950 100644 --- a/src/detect.h +++ b/src/detect.h @@ -261,6 +261,8 @@ typedef struct DetectPort_ { #define SIG_FLAG_TOSERVER (1<<19) #define SIG_FLAG_TOCLIENT (1<<20) +#define SIG_FLAG_TLSSTORE (1<<21) + /* signature init flags */ #define SIG_FLAG_INIT_DEONLY 1 /**< decode event only signature */ #define SIG_FLAG_INIT_PACKET (1<<1) /**< signature has matches against a packet (as opposed to app layer) */ @@ -996,6 +998,7 @@ enum { DETECT_AL_TLS_SUBJECT, DETECT_AL_TLS_ISSUERDN, DETECT_AL_TLS_FINGERPRINT, + DETECT_AL_TLS_STORE, DETECT_AL_HTTP_COOKIE, DETECT_AL_HTTP_METHOD, diff --git a/src/log-tlslog.c b/src/log-tlslog.c index bb1fae1b6ae9..b7514c17b5f6 100644 --- a/src/log-tlslog.c +++ b/src/log-tlslog.c @@ -47,12 +47,17 @@ #include "util-buffer.h" #include "util-logopenfile.h" +#include "util-crypt.h" #define DEFAULT_LOG_FILENAME "tls.log" +static char tls_logfile_base_dir[PATH_MAX] = "/tmp"; +SC_ATOMIC_DECLARE(unsigned int, cert_id); + #define MODULE_NAME "LogTlsLog" #define OUTPUT_BUFFER_SIZE 65535 +#define CERT_ENC_BUFFER_SIZE 2048 #define LOG_TLS_DEFAULT 0 #define LOG_TLS_EXTENDED 1 @@ -79,6 +84,8 @@ void TmModuleLogTlsLogRegister(void) /* enable the logger for the app layer */ AppLayerRegisterLogger(ALPROTO_TLS); + + SC_ATOMIC_INIT(cert_id); } void TmModuleLogTlsLogIPv4Register(void) @@ -109,10 +116,13 @@ typedef struct 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; + uint8_t* enc_buf; + size_t enc_buf_len; } LogTlsLogThread; static void CreateTimeString(const struct timeval *ts, char *str, size_t size) @@ -121,7 +131,9 @@ static void CreateTimeString(const struct timeval *ts, char *str, size_t size) 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); + 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 void LogTlsLogExtended(LogTlsLogThread *aft, SSLState * state) @@ -131,6 +143,194 @@ static void LogTlsLogExtended(LogTlsLogThread *aft, SSLState * state) } } +static int GetIPInformations(Packet *p, char* srcip, size_t srcip_len, + Port* sp, char* dstip, size_t dstip_len, + Port* dp, int ipproto) +{ + if ((PKT_IS_TOSERVER(p))) { + switch (ipproto) { + case AF_INET: + PrintInet(AF_INET, (const void *) GET_IPV4_SRC_ADDR_PTR(p), srcip, srcip_len); + PrintInet(AF_INET, (const void *) GET_IPV4_DST_ADDR_PTR(p), dstip, dstip_len); + break; + case AF_INET6: + PrintInet(AF_INET6, (const void *) GET_IPV6_SRC_ADDR(p), srcip, srcip_len); + PrintInet(AF_INET6, (const void *) GET_IPV6_DST_ADDR(p), dstip, dstip_len); + break; + default: + return 0; + } + *sp = p->sp; + *dp = p->dp; + } else { + switch (ipproto) { + case AF_INET: + PrintInet(AF_INET, (const void *) GET_IPV4_DST_ADDR_PTR(p), srcip, srcip_len); + PrintInet(AF_INET, (const void *) GET_IPV4_SRC_ADDR_PTR(p), dstip, dstip_len); + break; + case AF_INET6: + PrintInet(AF_INET6, (const void *) GET_IPV6_DST_ADDR(p), srcip, srcip_len); + PrintInet(AF_INET6, (const void *) GET_IPV6_SRC_ADDR(p), dstip, dstip_len); + break; + default: + return 0; + } + *sp = p->dp; + *dp = p->sp; + } + return 1; +} + +static int CreateFileName(LogTlsFileCtx *log, Packet *p, SSLState *state, char *filename) +{ +#define FILELEN 64 //filename len + extention + ending path / + some space + + int filenamelen = FILELEN + strlen(tls_logfile_base_dir); + int file_id = SC_ATOMIC_ADD(cert_id, 1); + + if (filenamelen + 1 > PATH_MAX) { + return 0; + } + + /* Use format : packet time + incremental ID + * When running on same pcap it will overwrite + * On a live device, we will not be able to overwrite */ + snprintf(filename, filenamelen, "%s/%ld.%ld-%d.pem", + tls_logfile_base_dir, + p->ts.tv_sec, + p->ts.tv_usec, + file_id); + return 1; +} + + +static void LogTlsLogPem(LogTlsLogThread *aft, Packet *p, SSLState *state, LogTlsFileCtx *log, int ipproto) +{ +#define PEMHEADER "-----BEGIN CERTIFICATE-----\n" +#define PEMFOOTER "-----END CERTIFICATE-----\n" + //Logging pem certificate + char filename[PATH_MAX] = ""; + FILE* fp = NULL; + FILE* fpmeta = NULL; + unsigned long pemlen; + unsigned char* pembase64ptr = NULL; + int ret; + + if ((state->server_connp.cert_input == NULL) || (state->server_connp.cert_input_len == 0)) + SCReturn; + + CreateFileName(log, p, state, filename); + if (strlen(filename) == 0) { + SCLogWarning(SC_ERR_FOPEN, "Can't create PEM filename"); + SCReturn; + } + + fp = fopen(filename, "w"); + if (fp == NULL) { + SCLogWarning(SC_ERR_FOPEN, "Can't create PEM file: %s", filename); + SCReturn; + } + + pemlen = (4 * (state->server_connp.cert_input_len + 2) / 3) +1; + if (pemlen > aft->enc_buf_len) { + aft->enc_buf = (uint8_t*) SCRealloc(aft->enc_buf, sizeof(uint8_t) * pemlen); + if (aft->enc_buf == NULL) { + SCLogWarning(SC_ERR_MEM_ALLOC, "Can't allocate data for base64 encoding"); + goto end_fp; + } + aft->enc_buf_len = pemlen; + } + + memset(aft->enc_buf, 0, aft->enc_buf_len); + + ret = Base64Encode((unsigned char*) state->server_connp.cert_input, state->server_connp.cert_input_len, aft->enc_buf, &pemlen); + if (ret != SC_BASE64_OK) { + SCLogWarning(SC_ERR_INVALID_ARGUMENTS, "Invalid return of Base64Encode function"); + goto end_fwrite_fp; + } + + if (fprintf(fp, PEMHEADER) < 0) + goto end_fwrite_fp; + + pembase64ptr = aft->enc_buf; + while (pemlen > 0) { + size_t loffset = pemlen >= 64 ? 64 : pemlen; + if (fwrite(pembase64ptr, 1, loffset, fp) != loffset) + goto end_fwrite_fp; + if (fwrite("\n", 1, 1, fp) != 1) + goto end_fwrite_fp; + pembase64ptr += 64; + if (pemlen < 64) + break; + pemlen -= 64; + } + + if (fprintf(fp, PEMFOOTER) < 0) + goto end_fwrite_fp; + fclose(fp); + + //Logging certificate informations + memcpy(filename + (strlen(filename) - 3), "meta", 4); + fpmeta = fopen(filename, "w"); + if (fpmeta != NULL) { + #define PRINT_BUF_LEN 46 + char srcip[PRINT_BUF_LEN], dstip[PRINT_BUF_LEN]; + char timebuf[64]; + Port sp, dp; + CreateTimeString(&p->ts, timebuf, sizeof(timebuf)); + if (!GetIPInformations(p, srcip, PRINT_BUF_LEN, &sp, dstip, PRINT_BUF_LEN, &dp, ipproto)) + goto end_fwrite_fpmeta; + if (fprintf(fpmeta, "TIME: %s\n", timebuf) < 0) + goto end_fwrite_fpmeta; + if (p->pcap_cnt > 0) { + if (fprintf(fpmeta, "PCAP PKT NUM: %"PRIu64"\n", p->pcap_cnt) < 0) + goto end_fwrite_fpmeta; + } + if (fprintf(fpmeta, "SRC IP: %s\n", srcip) < 0) + goto end_fwrite_fpmeta; + if (fprintf(fpmeta, "DST IP: %s\n", dstip) < 0) + goto end_fwrite_fpmeta; + if (fprintf(fpmeta, "PROTO: %" PRIu32 "\n", p->proto) < 0) + goto end_fwrite_fpmeta; + if (PKT_IS_TCP(p) || PKT_IS_UDP(p)) { + if (fprintf(fpmeta, "SRC PORT: %" PRIu16 "\n", sp) < 0) + goto end_fwrite_fpmeta; + if (fprintf(fpmeta, "DST PORT: %" PRIu16 "\n", dp) < 0) + goto end_fwrite_fpmeta; + } + + if (fprintf(fpmeta, "TLS SUBJECT: %s\n" + "TLS ISSUERDN: %s\n" + "TLS FINGERPRINT: %s\n", + state->server_connp.cert0_subject, + state->server_connp.cert0_issuerdn, + state->server_connp.cert0_fingerprint) < 0) + goto end_fwrite_fpmeta; + + fclose(fpmeta); + } else { + SCLogWarning(SC_ERR_FOPEN, "Can't open meta file: %s", + filename); + SCReturn; + } + + /* Reset the store flag */ + state->server_connp.cert_log_flag &= ~SSL_TLS_LOG_PEM; + SCReturn; + +end_fwrite_fp: + fclose(fp); + SCLogWarning(SC_ERR_FWRITE, "Unable to write certificate"); +end_fwrite_fpmeta: + if (fpmeta) { + fclose(fpmeta); + SCLogWarning(SC_ERR_FWRITE, "Unable to write certificate metafile"); + } +end_fp: + fclose(fp); +} + + static TmEcode LogTlsLogIPWrapper(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq, int ipproto) { @@ -151,12 +351,6 @@ static TmEcode LogTlsLogIPWrapper(ThreadVars *tv, Packet *p, void *data, PacketQ 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"); @@ -166,39 +360,23 @@ static TmEcode LogTlsLogIPWrapper(ThreadVars *tv, Packet *p, void *data, PacketQ if (ssl_state->server_connp.cert0_issuerdn == NULL || ssl_state->server_connp.cert0_subject == NULL) goto end; + if (ssl_state->server_connp.cert_log_flag & SSL_TLS_LOG_PEM) { + LogTlsLogPem(aft, p, ssl_state, hlog, ipproto); + } + + int r = AppLayerTransactionGetLoggedId(p->flow); + + if (r != 0) { + goto end; + } + CreateTimeString(&p->ts, timebuf, sizeof(timebuf)); - char srcip[46], dstip[46]; + #define PRINT_BUF_LEN 46 + char srcip[PRINT_BUF_LEN], dstip[PRINT_BUF_LEN]; 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; + if (!GetIPInformations(p, srcip, PRINT_BUF_LEN, + &sp, dstip, PRINT_BUF_LEN, &dp, ipproto)) { + goto end; } /* reset */ @@ -210,6 +388,7 @@ static TmEcode LogTlsLogIPWrapper(ThreadVars *tv, Packet *p, void *data, PacketQ ssl_state->server_connp.cert0_subject, ssl_state->server_connp.cert0_issuerdn); AppLayerTransactionUpdateLoggedId(p->flow); + if (hlog->flags & LOG_TLS_EXTENDED) { LogTlsLogExtended(aft, ssl_state); } else { @@ -280,6 +459,14 @@ TmEcode LogTlsLogThreadInit(ThreadVars *t, void *initdata, void **data) return TM_ECODE_FAILED; } + aft->enc_buf = SCMalloc(CERT_ENC_BUFFER_SIZE); + if (aft->enc_buf == NULL) { + SCFree(aft); + return TM_ECODE_FAILED; + } + aft->enc_buf_len = CERT_ENC_BUFFER_SIZE; + memset(aft->enc_buf, 0, aft->enc_buf_len); + /* Use the Ouptut Context (file pointer and mutex) */ aft->tlslog_ctx = ((OutputCtx *) initdata)->data; @@ -326,6 +513,26 @@ OutputCtx *LogTlsLogInitCtx(ConfNode *conf) return NULL; } + char *s_default_log_dir = NULL; + if (ConfGet("default-log-dir", &s_default_log_dir) != 1) + s_default_log_dir = DEFAULT_LOG_DIR; + + const char *s_base_dir = NULL; + s_base_dir = ConfNodeLookupChildValue(conf, "certs-log-dir"); + if (s_base_dir == NULL || strlen(s_base_dir) == 0) { + strlcpy(tls_logfile_base_dir, + s_default_log_dir, sizeof(tls_logfile_base_dir)); + } else { + if (PathIsAbsolute(s_base_dir)) { + strlcpy(tls_logfile_base_dir, + s_base_dir, sizeof(tls_logfile_base_dir)); + } else { + snprintf(tls_logfile_base_dir, sizeof(tls_logfile_base_dir), + "%s/%s", s_default_log_dir, s_base_dir); + } + } + + /* FIXME config variable here */ if (SCConfLogOpenGeneric(conf, file_ctx, DEFAULT_LOG_FILENAME) < 0) { LogFileFreeCtx(file_ctx); return NULL; @@ -345,7 +552,6 @@ OutputCtx *LogTlsLogInitCtx(ConfNode *conf) } } - OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx)); if (output_ctx == NULL) return NULL; diff --git a/suricata.yaml.in b/suricata.yaml.in index e6ce8bacc961..c0526405ca0b 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -80,6 +80,7 @@ outputs: enabled: no # Log TLS connections. filename: tls.log # File to store TLS logs. #extended: yes # Log extended information like fingerprint + certs-log-dir: certs # directory to store the certificates files # a line based log to used with pcap file study. # this module is dedicated to offline pcap parsing (empty output From 917dcc3029143e37b30846cc10a48bf0eb47b370 Mon Sep 17 00:00:00 2001 From: Eric Leblond Date: Wed, 18 Jul 2012 15:13:49 +0200 Subject: [PATCH 08/11] tls: keep pointers to all certificates in chain When multiple certificates forming a chain are sent. A pointer to the start of each certificate is kept. This will allow treatment on certificates chains. --- src/app-layer-ssl.c | 9 +++++++++ src/app-layer-ssl.h | 10 ++++++++++ src/app-layer-tls-handshake.c | 10 ++++++++++ 3 files changed, 29 insertions(+) diff --git a/src/app-layer-ssl.c b/src/app-layer-ssl.c index 6c71a44c1983..1571636317bf 100644 --- a/src/app-layer-ssl.c +++ b/src/app-layer-ssl.c @@ -862,6 +862,7 @@ void *SSLStateAlloc(void) memset(ssl_state, 0, sizeof(SSLState)); ((SSLState*)ssl_state)->client_connp.cert_log_flag = 0; ((SSLState*)ssl_state)->server_connp.cert_log_flag = 0; + TAILQ_INIT(&((SSLState*)ssl_state)->server_connp.certs); return ssl_state; } @@ -873,6 +874,7 @@ void *SSLStateAlloc(void) void SSLStateFree(void *p) { SSLState *ssl_state = (SSLState *)p; + SSLCertsChain *item; if (ssl_state->client_connp.trec) SCFree(ssl_state->client_connp.trec); @@ -892,6 +894,13 @@ void SSLStateFree(void *p) if (ssl_state->server_connp.cert0_fingerprint) SCFree(ssl_state->server_connp.cert0_fingerprint); + /* Free certificate chain */ + while ((item = TAILQ_FIRST(&ssl_state->server_connp.certs))) { + TAILQ_REMOVE(&ssl_state->server_connp.certs, item, next); + SCFree(item); + } + TAILQ_INIT(&ssl_state->server_connp.certs); + SCFree(ssl_state); return; diff --git a/src/app-layer-ssl.h b/src/app-layer-ssl.h index 1d6f1f81bbcf..9ca516f7b4e3 100644 --- a/src/app-layer-ssl.h +++ b/src/app-layer-ssl.h @@ -27,6 +27,7 @@ #define __APP_LAYER_SSL_H__ #include "decode-events.h" +#include "queue.h" enum { /* TLS protocol messages */ @@ -79,6 +80,13 @@ enum { TLS_VERSION_12 = 0x0303, }; +typedef struct SSLCertsChain_ { + uint8_t *cert_data; + uint32_t cert_len; + TAILQ_ENTRY(SSLCertsChain_) next; +} SSLCertsChain; + + typedef struct SSLStateConnp_ { /* record length */ uint32_t record_length; @@ -108,6 +116,8 @@ typedef struct SSLStateConnp_ { uint8_t *cert_input; uint32_t cert_input_len; + TAILQ_HEAD(, SSLCertsChain_) certs; + uint32_t cert_log_flag; /* buffer for the tls record. diff --git a/src/app-layer-tls-handshake.c b/src/app-layer-tls-handshake.c index 8c086cbeec9e..a9cffe09bf5e 100644 --- a/src/app-layer-tls-handshake.c +++ b/src/app-layer-tls-handshake.c @@ -122,6 +122,7 @@ int DecodeTLSHandshakeServerCertificate(SSLState *ssl_state, uint8_t *input, uin if (rc != 0) { TLSCertificateErrCodeToWarning(ssl_state, errcode); } else { + SSLCertsChain *ncert; //SCLogInfo("TLS Cert %d: %s\n", i, buffer); if (i==0) { ssl_state->server_connp.cert0_subject = SCStrdup(buffer); @@ -130,6 +131,15 @@ int DecodeTLSHandshakeServerCertificate(SSLState *ssl_state, uint8_t *input, uin return -1; } } + ncert = (SSLCertsChain *)SCMalloc(sizeof(SSLCertsChain)); + if (ncert == NULL) { + DerFree(cert); + return -1; + } + memset(ncert, 0, sizeof(*ncert)); + ncert->cert_data = input; + ncert->cert_len = cur_cert_length; + TAILQ_INSERT_TAIL(&ssl_state->server_connp.certs, ncert, next); } rc = Asn1DerGetIssuerDN(cert, buffer, sizeof(buffer), &errcode); if (rc != 0) { From 0a4d841a7578f06407abae9474ce0142c708b32e Mon Sep 17 00:00:00 2001 From: Eric Leblond Date: Thu, 23 Aug 2012 09:25:15 +0200 Subject: [PATCH 09/11] tls: store all the certificates chain in the written PEM file. When using the tls.store command, a dump of all certificates in the chain is now done on the disk. --- src/log-tlslog.c | 61 +++++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/src/log-tlslog.c b/src/log-tlslog.c index b7514c17b5f6..a1c9072c6560 100644 --- a/src/log-tlslog.c +++ b/src/log-tlslog.c @@ -215,6 +215,7 @@ static void LogTlsLogPem(LogTlsLogThread *aft, Packet *p, SSLState *state, LogTl unsigned long pemlen; unsigned char* pembase64ptr = NULL; int ret; + SSLCertsChain *cert; if ((state->server_connp.cert_input == NULL) || (state->server_connp.cert_input_len == 0)) SCReturn; @@ -231,42 +232,44 @@ static void LogTlsLogPem(LogTlsLogThread *aft, Packet *p, SSLState *state, LogTl SCReturn; } - pemlen = (4 * (state->server_connp.cert_input_len + 2) / 3) +1; - if (pemlen > aft->enc_buf_len) { - aft->enc_buf = (uint8_t*) SCRealloc(aft->enc_buf, sizeof(uint8_t) * pemlen); - if (aft->enc_buf == NULL) { - SCLogWarning(SC_ERR_MEM_ALLOC, "Can't allocate data for base64 encoding"); - goto end_fp; + TAILQ_FOREACH(cert, &state->server_connp.certs, next) { + pemlen = (4 * (cert->cert_len + 2) / 3) +1; + if (pemlen > aft->enc_buf_len) { + aft->enc_buf = (uint8_t*) SCRealloc(aft->enc_buf, sizeof(uint8_t) * pemlen); + if (aft->enc_buf == NULL) { + SCLogWarning(SC_ERR_MEM_ALLOC, "Can't allocate data for base64 encoding"); + goto end_fp; + } + aft->enc_buf_len = pemlen; } - aft->enc_buf_len = pemlen; - } - - memset(aft->enc_buf, 0, aft->enc_buf_len); - ret = Base64Encode((unsigned char*) state->server_connp.cert_input, state->server_connp.cert_input_len, aft->enc_buf, &pemlen); - if (ret != SC_BASE64_OK) { - SCLogWarning(SC_ERR_INVALID_ARGUMENTS, "Invalid return of Base64Encode function"); - goto end_fwrite_fp; - } + memset(aft->enc_buf, 0, aft->enc_buf_len); - if (fprintf(fp, PEMHEADER) < 0) - goto end_fwrite_fp; + ret = Base64Encode((unsigned char*) cert->cert_data, cert->cert_len, aft->enc_buf, &pemlen); + if (ret != SC_BASE64_OK) { + SCLogWarning(SC_ERR_INVALID_ARGUMENTS, "Invalid return of Base64Encode function"); + goto end_fwrite_fp; + } - pembase64ptr = aft->enc_buf; - while (pemlen > 0) { - size_t loffset = pemlen >= 64 ? 64 : pemlen; - if (fwrite(pembase64ptr, 1, loffset, fp) != loffset) + if (fprintf(fp, PEMHEADER) < 0) goto end_fwrite_fp; - if (fwrite("\n", 1, 1, fp) != 1) + + pembase64ptr = aft->enc_buf; + while (pemlen > 0) { + size_t loffset = pemlen >= 64 ? 64 : pemlen; + if (fwrite(pembase64ptr, 1, loffset, fp) != loffset) + goto end_fwrite_fp; + if (fwrite("\n", 1, 1, fp) != 1) + goto end_fwrite_fp; + pembase64ptr += 64; + if (pemlen < 64) + break; + pemlen -= 64; + } + + if (fprintf(fp, PEMFOOTER) < 0) goto end_fwrite_fp; - pembase64ptr += 64; - if (pemlen < 64) - break; - pemlen -= 64; } - - if (fprintf(fp, PEMFOOTER) < 0) - goto end_fwrite_fp; fclose(fp); //Logging certificate informations From f0b243aec6efbd267a976c9a97fd5fd52e41c55e Mon Sep 17 00:00:00 2001 From: Eric Leblond Date: Tue, 21 Aug 2012 15:42:10 +0200 Subject: [PATCH 10/11] detect-tls: various indent fixes. And delete a useless FIXME. --- src/detect-tls.c | 26 ++++++++++++++++---------- src/log-tlslog.c | 1 - 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/detect-tls.c b/src/detect-tls.c index 2075494bd614..deeab3f1ecfc 100644 --- a/src/detect-tls.c +++ b/src/detect-tls.c @@ -290,8 +290,7 @@ static DetectTlsData *DetectTlsSubjectParse (char *str) } /* Let's see if we need to escape "'s */ - if (tmp_str[0] == '"') - { + if (tmp_str[0] == '"') { tmp_str[strlen(tmp_str) - 1] = '\0'; tmp_str += 1; } @@ -531,7 +530,8 @@ static int DetectTlsIssuerDNSetup (DetectEngineCtx *de_ctx, Signature *s, char * SigMatch *sm = NULL; tls = DetectTlsIssuerDNParse(str); - if (tls == NULL) goto error; + if (tls == NULL) + goto error; /* Okay so far so good, lets get this into a SigMatch * and put it in the Signature. */ @@ -553,8 +553,10 @@ static int DetectTlsIssuerDNSetup (DetectEngineCtx *de_ctx, Signature *s, char * return 0; error: - if (tls != NULL) DetectTlsIssuerDNFree(tls); - if (sm != NULL) SCFree(sm); + if (tls != NULL) + DetectTlsIssuerDNFree(tls); + if (sm != NULL) + SCFree(sm); return -1; } @@ -717,7 +719,8 @@ static int DetectTlsFingerprintSetup (DetectEngineCtx *de_ctx, Signature *s, cha SigMatch *sm = NULL; tls = DetectTlsFingerprintParse(str); - if (tls == NULL) goto error; + if (tls == NULL) + goto error; /* Okay so far so good, lets get this into a SigMatch * and put it in the Signature. */ @@ -739,8 +742,10 @@ static int DetectTlsFingerprintSetup (DetectEngineCtx *de_ctx, Signature *s, cha return 0; error: - if (tls != NULL) DetectTlsFingerprintFree(tls); - if (sm != NULL) SCFree(sm); + if (tls != NULL) + DetectTlsFingerprintFree(tls); + if (sm != NULL) + SCFree(sm); return -1; } @@ -790,8 +795,9 @@ static int DetectTlsStoreSetup (DetectEngineCtx *de_ctx, Signature *s, char *str return 0; error: - if (sm != NULL) SCFree(sm); - return -1; + if (sm != NULL) + SCFree(sm); + return -1; } diff --git a/src/log-tlslog.c b/src/log-tlslog.c index a1c9072c6560..3d808ea95eda 100644 --- a/src/log-tlslog.c +++ b/src/log-tlslog.c @@ -535,7 +535,6 @@ OutputCtx *LogTlsLogInitCtx(ConfNode *conf) } } - /* FIXME config variable here */ if (SCConfLogOpenGeneric(conf, file_ctx, DEFAULT_LOG_FILENAME) < 0) { LogFileFreeCtx(file_ctx); return NULL; From f425bf50f1236ded4090397186af4352b6413cf6 Mon Sep 17 00:00:00 2001 From: Eric Leblond Date: Thu, 23 Aug 2012 17:06:39 +0200 Subject: [PATCH 11/11] tls: suppress always true condition. --- src/detect-tls.c | 75 +++++++++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 39 deletions(-) diff --git a/src/detect-tls.c b/src/detect-tls.c index deeab3f1ecfc..a54c5ba8cefc 100644 --- a/src/detect-tls.c +++ b/src/detect-tls.c @@ -448,6 +448,10 @@ static DetectTlsData *DetectTlsIssuerDNParse(char *str) #define MAX_SUBSTRINGS 30 int ret = 0, res = 0; int ov[MAX_SUBSTRINGS]; + const char *str_ptr; + char *orig; + char *tmp_str; + uint32_t flag = 0; ret = pcre_exec(issuerdn_parse_regex, issuerdn_parse_regex_study, str, strlen(str), 0, 0, ov, MAX_SUBSTRINGS); @@ -457,52 +461,45 @@ static DetectTlsData *DetectTlsIssuerDNParse(char *str) goto error; } - if (ret == 3) { - const char *str_ptr; - char *orig; - char *tmp_str; - uint32_t flag = 0; + res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 1, &str_ptr); + if (res < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; + } + if (str_ptr[0] == '!') + flag = DETECT_CONTENT_NEGATED; - res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 1, &str_ptr); - if (res < 0) { - SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); - goto error; - } - if (str_ptr[0] == '!') - flag = DETECT_CONTENT_NEGATED; + res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 2, &str_ptr); + if (res < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; + } - res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 2, &str_ptr); - if (res < 0) { - SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); - goto error; - } + /* We have a correct id option */ + tls = SCMalloc(sizeof(DetectTlsData)); + if (tls == NULL) + goto error; + tls->issuerdn = NULL; + tls->flags = flag; - /* We have a correct id option */ - tls = SCMalloc(sizeof(DetectTlsData)); - if (tls == NULL) - goto error; - tls->issuerdn = NULL; - tls->flags = flag; - - orig = SCStrdup((char*)str_ptr); - tmp_str=orig; - if (tmp_str == NULL) { - goto error; - } + orig = SCStrdup((char*)str_ptr); + tmp_str=orig; + if (tmp_str == NULL) { + goto error; + } - /* Let's see if we need to escape "'s */ - if (tmp_str[0] == '"') - { - tmp_str[strlen(tmp_str) - 1] = '\0'; - tmp_str += 1; - } + /* Let's see if we need to escape "'s */ + if (tmp_str[0] == '"') + { + tmp_str[strlen(tmp_str) - 1] = '\0'; + tmp_str += 1; + } - tls->issuerdn = SCStrdup(tmp_str); + tls->issuerdn = SCStrdup(tmp_str); - SCFree(orig); + SCFree(orig); - SCLogDebug("will look for TLS issuerdn %s", tls->issuerdn); - } + SCLogDebug("Will look for TLS issuerdn %s", tls->issuerdn); return tls;