diff --git a/src/Makefile.am b/src/Makefile.am index 5f5e34534a2a..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 \ @@ -264,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/app-layer-ssl.c b/src/app-layer-ssl.c index f8012c32e233..61e6fce2ffb2 100644 --- a/src/app-layer-ssl.c +++ b/src/app-layer-ssl.c @@ -860,6 +860,9 @@ 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; + TAILQ_INIT(&((SSLState*)ssl_state)->server_connp.certs); return ssl_state; } @@ -871,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); @@ -878,6 +882,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 +891,15 @@ 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); + + /* 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); diff --git a/src/app-layer-ssl.h b/src/app-layer-ssl.h index f96e04d3a573..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 */ @@ -64,6 +65,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 */ @@ -76,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; @@ -100,6 +112,14 @@ typedef struct SSLStateConnp_ { char *cert0_subject; char *cert0_issuerdn; + char *cert0_fingerprint; + + uint8_t *cert_input; + uint32_t cert_input_len; + + TAILQ_HEAD(, SSLCertsChain_) certs; + + uint32_t cert_log_flag; /* 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..a9cffe09bf5e 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) @@ -120,14 +122,24 @@ 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->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; } } + 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) { @@ -135,14 +147,43 @@ 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; } } } 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"); + } + } + + ssl_state->server_connp.cert_input = input; + ssl_state->server_connp.cert_input_len = cur_cert_length; + } + } i++; @@ -152,5 +193,6 @@ int DecodeTLSHandshakeServerCertificate(SSLState *ssl_state, uint8_t *input, uin } return parsed; + } diff --git a/src/detect-tls.c b/src/detect-tls.c index 110f775391a8..538cdab63127 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,6 +82,11 @@ 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 *); +static int DetectTlsStoreSetup (DetectEngineCtx *, Signature *, char *); +static int DetectTlsStoreMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *, uint8_t, void *, Signature *, SigMatch *); /** * \brief Registration function for keyword: tls.version @@ -99,6 +108,23 @@ 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; + + 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; @@ -132,6 +158,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: @@ -251,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; } @@ -493,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. */ @@ -515,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; } @@ -533,6 +576,259 @@ 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 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-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..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) */ @@ -995,6 +997,8 @@ enum { DETECT_AL_TLS_VERSION, 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 new file mode 100644 index 000000000000..eeef5e8c66ca --- /dev/null +++ b/src/log-tlslog.c @@ -0,0 +1,557 @@ +/* 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" +#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 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 *); +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); + + SC_ATOMIC_INIT(cert_id); +} + +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 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 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; + SSLCertsChain *cert; + + 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; + } + + 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*) 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; + } + + 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; + } + + SCFree(pembase64); + 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->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) +{ + + 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)); + #define PRINT_BUF_LEN 46 + char srcip[PRINT_BUF_LEN], dstip[PRINT_BUF_LEN]; + Port sp, dp; + if (!GetIPInformations(p, srcip, PRINT_BUF_LEN, + &sp, dstip, PRINT_BUF_LEN, &dp, ipproto)) { + goto end; + } + + /* reset */ + MemBufferReset(aft->buffer); + + MemBufferWriteString(aft->buffer, + "%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 ++; + + SCMutexLock(&hlog->file_ctx->fp_mutex); + MemBufferPrintToFPAsString(aft->buffer, hlog->file_ctx->fp); + 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); + +} + +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; + } + + 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); + } + } + + 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; + + 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; + 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-crypt.c b/src/util-crypt.c new file mode 100644 index 000000000000..9ec881f10b16 --- /dev/null +++ b/src/util-crypt.c @@ -0,0 +1,302 @@ +/* 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 ) + * + * 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))) +#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; +} + +#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, + 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..a78e1106cdcc --- /dev/null +++ b/src/util-crypt.h @@ -0,0 +1,82 @@ +/* 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" + +#ifndef HAVE_NSS + +#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; + +#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); + +#endif /* UTIL_CRYPT_H_ */ 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..27c6a1d590d9 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -73,6 +73,13 @@ 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. + #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 # if used with an other kind of input). It can interoperate with