From 37e8b087e01626a7c15440ffd3bc406655469bf3 Mon Sep 17 00:00:00 2001 From: Jean-Paul Roliers Date: Wed, 1 Feb 2012 22:36:44 +0100 Subject: [PATCH 01/10] 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 | 340 ++++++++++++++++++++++++++++++++++++++++ src/log-tlslog.h | 33 ++++ src/suricata.c | 4 + src/tm-threads-common.h | 3 + src/util-error.h | 3 +- suricata.yaml.in | 5 + 7 files changed, 388 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 5f5e34534a2a..447bc0c6c460 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -264,6 +264,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..61df4cb103ed --- /dev/null +++ b/src/log-tlslog.c @@ -0,0 +1,340 @@ +/* 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 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_RDLOCK(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..d7780547126c --- /dev/null +++ b/src/log-tlslog.h @@ -0,0 +1,33 @@ +/* Copyright (C) 2007-2010 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 + */ + +#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 0ae37194525b..a3fc4b2e51a9 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" @@ -1472,6 +1473,9 @@ int main(int argc, char **argv) TmModuleLogHttpLogRegister(); TmModuleLogHttpLogIPv4Register(); TmModuleLogHttpLogIPv6Register(); + TmModuleLogTlsLogRegister(); + TmModuleLogTlsLogIPv4Register(); + TmModuleLogTlsLogIPv6Register(); TmModulePcapLogRegister(); TmModuleLogFileLogRegister(); TmModuleLogFilestoreRegister(); 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 4d3261b20685..77d1d376b762 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -73,6 +73,11 @@ outputs: #extended: yes # enable this for extended logging information #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 663b0f0c2e9e5421172255f5e4801dc8ae1025f8 Mon Sep 17 00:00:00 2001 From: Jean-Paul Roliers Date: Thu, 2 Feb 2012 14:51:31 +0100 Subject: [PATCH 02/10] 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 447bc0c6c460..0836e3f55ca6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -247,6 +247,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 7a49f18ae57e8471da268e6529b7688fe4e534c6 Mon Sep 17 00:00:00 2001 From: Eric Leblond Date: Thu, 5 Apr 2012 16:45:24 +0200 Subject: [PATCH 03/10] 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 eba4489de961d90c245bdbbe54296b404d62cde6 Mon Sep 17 00:00:00 2001 From: Jean-Paul Roliers Date: Thu, 2 Feb 2012 15:07:42 +0100 Subject: [PATCH 04/10] 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 74ae578f7f99f9fcb298b784c81e645a424f84d2 Mon Sep 17 00:00:00 2001 From: Jean-Paul Roliers Date: Sat, 4 Feb 2012 17:15:11 +0100 Subject: [PATCH 05/10] 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 61df4cb103ed..f025a9fd07db 100644 --- a/src/log-tlslog.c +++ b/src/log-tlslog.c @@ -53,6 +53,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 *); @@ -120,6 +123,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) { @@ -194,11 +204,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 ++; @@ -320,6 +335,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 77d1d376b762..8a9d5fe3605c 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -77,6 +77,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 f79e1be6fcb947582dc7088103b5884cc7d4f5f5 Mon Sep 17 00:00:00 2001 From: Jean-Paul Roliers Date: Thu, 2 Feb 2012 16:45:35 +0100 Subject: [PATCH 06/10] tls: adding support for fingerprint rule matching. Add the support for tls.fingerprint keyword in rules. --- src/detect-tls.c | 220 ++++++++++++++++++++++++++++++++++++++++++++++- src/detect-tls.h | 1 + src/detect.h | 1 + 3 files changed, 221 insertions(+), 1 deletion(-) diff --git a/src/detect-tls.c b/src/detect-tls.c index 110f775391a8..3d326921877d 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: @@ -533,6 +562,195 @@ 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]; + + 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; + } + + 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, 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 833891658f9e56a84dcdceef231131b2fceb7096 Mon Sep 17 00:00:00 2001 From: Jean-Paul Roliers Date: Sat, 4 Feb 2012 17:37:41 +0100 Subject: [PATCH 07/10] 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 | 9 ++ src/app-layer-tls-handshake.c | 11 +- src/detect-tls.c | 72 ++++++++++ src/detect.h | 3 + src/log-tlslog.c | 255 +++++++++++++++++++++++++++++----- suricata.yaml.in | 1 + 7 files changed, 316 insertions(+), 37 deletions(-) diff --git a/src/app-layer-ssl.c b/src/app-layer-ssl.c index c092a5f98b8a..814e36763cf1 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 |= SSL_TLS_NOLOG_PEM; + ((SSLState*)ssl_state)->server_connp.cert_log_flag |= SSL_TLS_NOLOG_PEM; return ssl_state; } diff --git a/src/app-layer-ssl.h b/src/app-layer-ssl.h index 85da873c1fe1..b3d95982962a 100644 --- a/src/app-layer-ssl.h +++ b/src/app-layer-ssl.h @@ -64,6 +64,10 @@ enum { #define SSL_AL_FLAG_STATE_SERVER_KEYX 0x1000 #define SSL_AL_FLAG_STATE_UNKNOWN 0x2000 +#define SSL_TLS_NOLOG_PEM 0x0001 +#define SSL_TLS_LOG_PEM 0x0002 + + /* 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 +106,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 3d326921877d..4fb9c6404025 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; @@ -751,6 +763,66 @@ 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_RDLOCK(f); + if (s->flags & SIG_FLAG_TLSSTORE && + (ssl_state->curr_connp->cert_input != NULL) && + (ssl_state->curr_connp->cert_input_len)) { + ssl_state->curr_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 f025a9fd07db..c2fe2266c8c7 100644 --- a/src/log-tlslog.c +++ b/src/log-tlslog.c @@ -46,9 +46,13 @@ #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 @@ -78,6 +82,8 @@ void TmModuleLogTlsLogRegister(void) /* enable the logger for the app layer */ AppLayerRegisterLogger(ALPROTO_TLS); + + SC_ATOMIC_INIT(cert_id); } void TmModuleLogTlsLogIPv4Register(void) @@ -108,6 +114,7 @@ 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; @@ -120,7 +127,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) @@ -130,6 +139,188 @@ 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(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* pembase64 = NULL; + unsigned char* pembase64ptr = NULL; + int ret; + + 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->curr_connp->cert_input_len + 2) / 3) +1; + pembase64 = (unsigned char *) SCMalloc(sizeof(unsigned char*)*pemlen); + if (pembase64 == NULL) { + SCLogWarning(SC_ERR_FOPEN, "Can't create PEM filename"); + goto end_fp; + } + + + memset(pembase64, 0, pemlen); + ret = Base64Encode((unsigned char*) state->curr_connp->cert_input, state->curr_connp->cert_input_len, pembase64, &pemlen); + if (ret != SC_BASE64_OK) { + SCLogWarning(SC_ERR_INVALID_ARGUMENTS, "Invalid return of Base64Encode function"); + goto end_pembase64; + } + + fprintf(fp, PEMHEADER); + + pembase64ptr = pembase64; + while (pemlen > 0) { + unsigned int 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); + SCFree(pembase64); + + //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->curr_connp->cert0_subject, + state->curr_connp->cert0_issuerdn, + state->curr_connp->cert0_fingerprint) < 0) + goto end_fwrite_fpmeta; + + fclose(fpmeta); + } else { + SCLogWarning(SC_ERR_FOPEN, "Can't open meta file: %s", + filename); + SCReturn; + } + 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_pembase64: + SCFree(pembase64); +end_fp: + fclose(fp); +} + + static TmEcode LogTlsLogIPWrapper(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq, int ipproto) { @@ -166,38 +357,12 @@ static TmEcode LogTlsLogIPWrapper(ThreadVars *tv, Packet *p, void *data, PacketQ 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 */ @@ -209,6 +374,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 { @@ -222,6 +388,10 @@ static TmEcode LogTlsLogIPWrapper(ThreadVars *tv, Packet *p, void *data, PacketQ fflush(hlog->file_ctx->fp); SCMutexUnlock(&hlog->file_ctx->fp_mutex); + if (ssl_state->curr_connp->cert_log_flag & SSL_TLS_LOG_PEM) { + LogTlsLogPem(p, ssl_state, hlog, ipproto); + } + end: FLOWLOCK_UNLOCK(p->flow); SCReturnInt(TM_ECODE_OK); @@ -325,6 +495,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; @@ -344,7 +534,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 8a9d5fe3605c..27c6a1d590d9 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -78,6 +78,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 c3edd1c81a1f406416acd5344751ff2b60f77a87 Mon Sep 17 00:00:00 2001 From: Eric Leblond Date: Wed, 18 Jul 2012 15:13:49 +0200 Subject: [PATCH 08/10] 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 | 6 ++++++ 3 files changed, 25 insertions(+) diff --git a/src/app-layer-ssl.c b/src/app-layer-ssl.c index 814e36763cf1..61e6fce2ffb2 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 |= SSL_TLS_NOLOG_PEM; ((SSLState*)ssl_state)->server_connp.cert_log_flag |= SSL_TLS_NOLOG_PEM; + 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 b3d95982962a..bd477ee136a9 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 */ @@ -80,6 +81,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; @@ -109,6 +117,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..0a1e9f86b1cb 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,11 @@ int DecodeTLSHandshakeServerCertificate(SSLState *ssl_state, uint8_t *input, uin return -1; } } + ncert = (SSLCertsChain *)SCMalloc(sizeof(SSLCertsChain)); + 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 d028a2af04c268f0212264a487d98f558f6a1b03 Mon Sep 17 00:00:00 2001 From: Eric Leblond Date: Wed, 18 Jul 2012 15:29:52 +0200 Subject: [PATCH 09/10] 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/app-layer-tls-handshake.c | 4 +++ src/log-tlslog.c | 58 +++++++++++++++++++---------------- 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/src/app-layer-tls-handshake.c b/src/app-layer-tls-handshake.c index 0a1e9f86b1cb..a9cffe09bf5e 100644 --- a/src/app-layer-tls-handshake.c +++ b/src/app-layer-tls-handshake.c @@ -132,6 +132,10 @@ int DecodeTLSHandshakeServerCertificate(SSLState *ssl_state, uint8_t *input, uin } } 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; diff --git a/src/log-tlslog.c b/src/log-tlslog.c index c2fe2266c8c7..e4981fd866d7 100644 --- a/src/log-tlslog.c +++ b/src/log-tlslog.c @@ -212,6 +212,7 @@ static void LogTlsLogPem(Packet *p, SSLState *state, LogTlsFileCtx *log, int ipp unsigned char* pembase64 = NULL; unsigned char* pembase64ptr = NULL; int ret; + SSLCertsChain *cert; CreateFileName(log, p, state, filename); if (strlen(filename) == 0) { @@ -225,40 +226,43 @@ static void LogTlsLogPem(Packet *p, SSLState *state, LogTlsFileCtx *log, int ipp SCReturn; } - pemlen = (4 * (state->curr_connp->cert_input_len + 2) / 3) +1; - pembase64 = (unsigned char *) SCMalloc(sizeof(unsigned char*)*pemlen); - if (pembase64 == NULL) { - SCLogWarning(SC_ERR_FOPEN, "Can't create PEM filename"); - goto end_fp; - } + TAILQ_FOREACH(cert, &state->curr_connp->certs, next) { + pemlen = (4 * (cert->cert_len + 2) / 3) +1; + pembase64 = (unsigned char *) SCMalloc(sizeof(unsigned char*)*pemlen); + if (pembase64 == NULL) { + SCLogWarning(SC_ERR_FOPEN, "Can't create PEM filename"); + goto end_fp; + } - memset(pembase64, 0, pemlen); - ret = Base64Encode((unsigned char*) state->curr_connp->cert_input, state->curr_connp->cert_input_len, pembase64, &pemlen); - if (ret != SC_BASE64_OK) { - SCLogWarning(SC_ERR_INVALID_ARGUMENTS, "Invalid return of Base64Encode function"); - goto end_pembase64; - } + memset(pembase64, 0, pemlen); + ret = Base64Encode((unsigned char*) cert->cert_data, cert->cert_len, pembase64, &pemlen); + if (ret != SC_BASE64_OK) { + SCLogWarning(SC_ERR_INVALID_ARGUMENTS, "Invalid return of Base64Encode function"); + goto end_pembase64; + } - fprintf(fp, PEMHEADER); + if (fprintf(fp, PEMHEADER) < 0) + goto end_fwrite_fp; + + pembase64ptr = pembase64; + while (pemlen > 0) { + int 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; + } - pembase64ptr = pembase64; - while (pemlen > 0) { - unsigned int loffset = pemlen >= 64 ? 64 : pemlen; - if (fwrite(pembase64ptr, 1, loffset, fp) != loffset) - goto end_fwrite_fp; - if (fwrite("\n", 1, 1, fp) != 1) + SCFree(pembase64); + 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); - SCFree(pembase64); //Logging certificate informations memcpy(filename + (strlen(filename) - 3), "meta", 4); From 1acf7ca669d56ad639384d43084285b777a578d2 Mon Sep 17 00:00:00 2001 From: Eric Leblond Date: Tue, 21 Aug 2012 15:42:10 +0200 Subject: [PATCH 10/10] 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 4fb9c6404025..538cdab63127 100644 --- a/src/detect-tls.c +++ b/src/detect-tls.c @@ -292,8 +292,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; } @@ -534,7 +533,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. */ @@ -556,8 +556,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; } @@ -723,7 +725,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. */ @@ -745,8 +748,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; } @@ -796,8 +801,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 e4981fd866d7..eeef5e8c66ca 100644 --- a/src/log-tlslog.c +++ b/src/log-tlslog.c @@ -518,7 +518,6 @@ OutputCtx *LogTlsLogInitCtx(ConfNode *conf) } } - /* FIXME config variable here */ if (SCConfLogOpenGeneric(conf, file_ctx, DEFAULT_LOG_FILENAME) < 0) { LogFileFreeCtx(file_ctx); return NULL;