diff options
author | Ashlee Young <ashlee@onosfw.com> | 2015-09-09 22:21:41 -0700 |
---|---|---|
committer | Ashlee Young <ashlee@onosfw.com> | 2015-09-09 22:21:41 -0700 |
commit | 8879b125d26e8db1a5633de5a9c692eb2d1c4f83 (patch) | |
tree | c7259d85a991b83dfa85ab2e339360669fc1f58e /framework/src/suricata/src/log-tlsstore.c | |
parent | 13d05bc8458758ee39cb829098241e89616717ee (diff) |
suricata checkin based on commit id a4bce14770beee46a537eda3c3f6e8e8565d5d0a
Change-Id: I9a214fa0ee95e58fc640e50bd604dac7f42db48f
Diffstat (limited to 'framework/src/suricata/src/log-tlsstore.c')
-rw-r--r-- | framework/src/suricata/src/log-tlsstore.c | 439 |
1 files changed, 439 insertions, 0 deletions
diff --git a/framework/src/suricata/src/log-tlsstore.c b/framework/src/suricata/src/log-tlsstore.c new file mode 100644 index 00000000..da23908e --- /dev/null +++ b/framework/src/suricata/src/log-tlsstore.c @@ -0,0 +1,439 @@ +/* Copyright (C) 2014 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + * + * \author Roliers Jean-Paul <popof.fpn@gmail.co> + * \author Eric Leblond <eric@regit.org> + * \author Victor Julien <victor@inliniac.net> + * + * Implements TLS store 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 "app-layer-parser.h" +#include "util-privs.h" +#include "util-buffer.h" + +#include "util-logopenfile.h" +#include "util-crypt.h" +#include "util-time.h" + +#define MODULE_NAME "LogTlsStoreLog" + +static char tls_logfile_base_dir[PATH_MAX] = "/tmp"; +SC_ATOMIC_DECLARE(unsigned int, cert_id); +static char logging_dir_not_writable; + +#define LOGGING_WRITE_ISSUE_LIMIT 6 + +typedef struct LogTlsStoreLogThread_ { + uint32_t tls_cnt; + + uint8_t* enc_buf; + size_t enc_buf_len; +} LogTlsStoreLogThread; + +static int CreateFileName(const 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, + (long int)p->ts.tv_sec, + (long int)p->ts.tv_usec, + file_id); + return 1; +} + +static void LogTlsLogPem(LogTlsStoreLogThread *aft, const Packet *p, SSLState *state, int ipproto) +{ +#define PEMHEADER "-----BEGIN CERTIFICATE-----\n" +#define PEMFOOTER "-----END CERTIFICATE-----\n" + //Logging pem certificate + char filename[PATH_MAX] = ""; + FILE* fp = NULL; + FILE* fpmeta = NULL; + unsigned long pemlen; + unsigned char* pembase64ptr = NULL; + int ret; + uint8_t *ptmp; + SSLCertsChain *cert; + + if ((state->server_connp.cert_input == NULL) || (state->server_connp.cert_input_len == 0)) + SCReturn; + + CreateFileName(p, state, filename); + if (strlen(filename) == 0) { + SCLogWarning(SC_ERR_FOPEN, "Can't create PEM filename"); + SCReturn; + } + + fp = fopen(filename, "w"); + if (fp == NULL) { + if (logging_dir_not_writable < LOGGING_WRITE_ISSUE_LIMIT) { + SCLogWarning(SC_ERR_FOPEN, + "Can't create PEM file '%s' in '%s' directory", + filename, tls_logfile_base_dir); + logging_dir_not_writable++; + } + SCReturn; + } + + TAILQ_FOREACH(cert, &state->server_connp.certs, next) { + pemlen = (4 * (cert->cert_len + 2) / 3) +1; + if (pemlen > aft->enc_buf_len) { + ptmp = (uint8_t*) SCRealloc(aft->enc_buf, sizeof(uint8_t) * pemlen); + if (ptmp == NULL) { + SCFree(aft->enc_buf); + aft->enc_buf = NULL; + SCLogWarning(SC_ERR_MEM_ALLOC, "Can't allocate data for base64 encoding"); + goto end_fp; + } + aft->enc_buf = ptmp; + aft->enc_buf_len = pemlen; + } + + memset(aft->enc_buf, 0, aft->enc_buf_len); + + ret = Base64Encode((unsigned char*) cert->cert_data, cert->cert_len, aft->enc_buf, &pemlen); + if (ret != SC_BASE64_OK) { + SCLogWarning(SC_ERR_INVALID_ARGUMENTS, "Invalid return of Base64Encode function"); + goto end_fwrite_fp; + } + + if (fprintf(fp, PEMHEADER) < 0) + goto end_fwrite_fp; + + pembase64ptr = aft->enc_buf; + while (pemlen > 0) { + size_t loffset = pemlen >= 64 ? 64 : pemlen; + if (fwrite(pembase64ptr, 1, loffset, fp) != loffset) + goto end_fwrite_fp; + if (fwrite("\n", 1, 1, fp) != 1) + goto end_fwrite_fp; + pembase64ptr += 64; + if (pemlen < 64) + break; + pemlen -= 64; + } + + if (fprintf(fp, PEMFOOTER) < 0) + goto end_fwrite_fp; + } + fclose(fp); + + //Logging certificate informations + memcpy(filename + (strlen(filename) - 3), "meta", 4); + fpmeta = fopen(filename, "w"); + if (fpmeta != NULL) { + #define PRINT_BUF_LEN 46 + char srcip[PRINT_BUF_LEN], dstip[PRINT_BUF_LEN]; + char timebuf[64]; + Port sp, dp; + CreateTimeString(&p->ts, timebuf, sizeof(timebuf)); + if (!TLSGetIPInformations(p, srcip, PRINT_BUF_LEN, &sp, dstip, PRINT_BUF_LEN, &dp, ipproto)) + goto end_fwrite_fpmeta; + if (fprintf(fpmeta, "TIME: %s\n", timebuf) < 0) + goto end_fwrite_fpmeta; + if (p->pcap_cnt > 0) { + if (fprintf(fpmeta, "PCAP PKT NUM: %"PRIu64"\n", p->pcap_cnt) < 0) + goto end_fwrite_fpmeta; + } + if (fprintf(fpmeta, "SRC IP: %s\n", srcip) < 0) + goto end_fwrite_fpmeta; + if (fprintf(fpmeta, "DST IP: %s\n", dstip) < 0) + goto end_fwrite_fpmeta; + if (fprintf(fpmeta, "PROTO: %" PRIu32 "\n", p->proto) < 0) + goto end_fwrite_fpmeta; + if (PKT_IS_TCP(p) || PKT_IS_UDP(p)) { + if (fprintf(fpmeta, "SRC PORT: %" PRIu16 "\n", sp) < 0) + goto end_fwrite_fpmeta; + if (fprintf(fpmeta, "DST PORT: %" PRIu16 "\n", dp) < 0) + goto end_fwrite_fpmeta; + } + + if (fprintf(fpmeta, "TLS SUBJECT: %s\n" + "TLS ISSUERDN: %s\n" + "TLS FINGERPRINT: %s\n", + state->server_connp.cert0_subject, + state->server_connp.cert0_issuerdn, + state->server_connp.cert0_fingerprint) < 0) + goto end_fwrite_fpmeta; + + fclose(fpmeta); + } else { + if (logging_dir_not_writable < LOGGING_WRITE_ISSUE_LIMIT) { + SCLogWarning(SC_ERR_FOPEN, + "Can't create meta file '%s' in '%s' directory", + filename, tls_logfile_base_dir); + logging_dir_not_writable++; + } + SCReturn; + } + + /* Reset the store flag */ + state->server_connp.cert_log_flag &= ~SSL_TLS_LOG_PEM; + SCReturn; + +end_fwrite_fp: + fclose(fp); + if (logging_dir_not_writable < LOGGING_WRITE_ISSUE_LIMIT) { + SCLogWarning(SC_ERR_FWRITE, "Unable to write certificate"); + logging_dir_not_writable++; + } +end_fwrite_fpmeta: + if (fpmeta) { + fclose(fpmeta); + if (logging_dir_not_writable < LOGGING_WRITE_ISSUE_LIMIT) { + SCLogWarning(SC_ERR_FWRITE, "Unable to write certificate metafile"); + logging_dir_not_writable++; + } + } + SCReturn; +end_fp: + fclose(fp); + SCReturn; +} + +/** \internal + * \brief Condition function for TLS logger + * \retval bool true or false -- log now? + */ +static int LogTlsStoreCondition(ThreadVars *tv, const Packet *p) +{ + if (p->flow == NULL) { + return FALSE; + } + + if (!(PKT_IS_TCP(p))) { + return FALSE; + } + + FLOWLOCK_RDLOCK(p->flow); + uint16_t proto = FlowGetAppProtocol(p->flow); + if (proto != ALPROTO_TLS) + goto dontlog; + + SSLState *ssl_state = (SSLState *)FlowGetAppState(p->flow); + if (ssl_state == NULL) { + SCLogDebug("no tls state, so no request logging"); + goto dontlog; + } + + /* we only log the state once if we don't have to write + * the cert due to tls.store keyword. */ + if (!(ssl_state->server_connp.cert_log_flag & SSL_TLS_LOG_PEM) && + (ssl_state->flags & SSL_AL_FLAG_STATE_STORED)) + goto dontlog; + + if (ssl_state->server_connp.cert0_issuerdn == NULL || + ssl_state->server_connp.cert0_subject == NULL) + goto dontlog; + + FLOWLOCK_UNLOCK(p->flow); + return TRUE; +dontlog: + FLOWLOCK_UNLOCK(p->flow); + return FALSE; +} + +static int LogTlsStoreLogger(ThreadVars *tv, void *thread_data, const Packet *p) +{ + LogTlsStoreLogThread *aft = (LogTlsStoreLogThread *)thread_data; + int ipproto = (PKT_IS_IPV4(p)) ? AF_INET : AF_INET6; + /* check if we have TLS state or not */ + FLOWLOCK_WRLOCK(p->flow); + uint16_t proto = FlowGetAppProtocol(p->flow); + if (proto != ALPROTO_TLS) + goto end; + + SSLState *ssl_state = (SSLState *)FlowGetAppState(p->flow); + if (unlikely(ssl_state == NULL)) { + goto end; + } + + if (ssl_state->server_connp.cert_log_flag & SSL_TLS_LOG_PEM) { + LogTlsLogPem(aft, p, ssl_state, ipproto); + } + + /* we only store the state once */ + ssl_state->flags |= SSL_AL_FLAG_STATE_STORED; +end: + FLOWLOCK_UNLOCK(p->flow); + return 0; +} + +static TmEcode LogTlsStoreLogThreadInit(ThreadVars *t, void *initdata, void **data) +{ + LogTlsStoreLogThread *aft = SCMalloc(sizeof(LogTlsStoreLogThread)); + if (unlikely(aft == NULL)) + return TM_ECODE_FAILED; + memset(aft, 0, sizeof(LogTlsStoreLogThread)); + + if (initdata == NULL) { + SCLogDebug("Error getting context for LogTlsStore. \"initdata\" argument NULL"); + SCFree(aft); + return TM_ECODE_FAILED; + } + + struct stat stat_buf; + if (stat(tls_logfile_base_dir, &stat_buf) != 0) { + int ret; + ret = mkdir(tls_logfile_base_dir, S_IRWXU|S_IXGRP|S_IRGRP); + if (ret != 0) { + int err = errno; + if (err != EEXIST) { + SCLogError(SC_ERR_LOGDIR_CONFIG, + "Cannot create certs drop directory %s: %s", + tls_logfile_base_dir, strerror(err)); + exit(EXIT_FAILURE); + } + } else { + SCLogInfo("Created certs drop directory %s", + tls_logfile_base_dir); + } + + } + + *data = (void *)aft; + return TM_ECODE_OK; +} + +static TmEcode LogTlsStoreLogThreadDeinit(ThreadVars *t, void *data) +{ + LogTlsStoreLogThread *aft = (LogTlsStoreLogThread *)data; + if (aft == NULL) { + return TM_ECODE_OK; + } + + /* clear memory */ + memset(aft, 0, sizeof(LogTlsStoreLogThread)); + + SCFree(aft); + return TM_ECODE_OK; +} + +static void LogTlsStoreLogExitPrintStats(ThreadVars *tv, void *data) +{ + LogTlsStoreLogThread *aft = (LogTlsStoreLogThread *)data; + if (aft == NULL) { + return; + } + + SCLogInfo("(%s) certificates extracted %" PRIu32 "", tv->name, aft->tls_cnt); +} + +/** + * \internal + * + * \brief deinit the log ctx and write out the waldo + * + * \param output_ctx output context to deinit + */ +static void LogTlsStoreLogDeInitCtx(OutputCtx *output_ctx) +{ + SCFree(output_ctx); +} + +/** \brief Create a new http log LogFilestoreCtx. + * \param conf Pointer to ConfNode containing this loggers configuration. + * \return NULL if failure, LogFilestoreCtx* to the file_ctx if succesful + * */ +static OutputCtx *LogTlsStoreLogInitCtx(ConfNode *conf) +{ + + OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx)); + if (unlikely(output_ctx == NULL)) + return NULL; + + output_ctx->data = NULL; + output_ctx->DeInit = LogTlsStoreLogDeInitCtx; + + /* FIXME we need to implement backward compability here */ + char *s_default_log_dir = NULL; + s_default_log_dir = ConfigGetLogDirectory(); + + 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); + } + } + + SCLogInfo("storing certs in %s", tls_logfile_base_dir); + + SCReturnPtr(output_ctx, "OutputCtx"); +} + +void TmModuleLogTlsStoreRegister (void) +{ + tmm_modules[TMM_TLSSTORE].name = MODULE_NAME; + tmm_modules[TMM_TLSSTORE].ThreadInit = LogTlsStoreLogThreadInit; + tmm_modules[TMM_TLSSTORE].Func = NULL; + tmm_modules[TMM_TLSSTORE].ThreadExitPrintStats = LogTlsStoreLogExitPrintStats; + tmm_modules[TMM_TLSSTORE].ThreadDeinit = LogTlsStoreLogThreadDeinit; + tmm_modules[TMM_TLSSTORE].RegisterTests = NULL; + tmm_modules[TMM_TLSSTORE].cap_flags = 0; + tmm_modules[TMM_TLSSTORE].flags = TM_FLAG_LOGAPI_TM; + tmm_modules[TMM_TLSSTORE].priority = 10; + + OutputRegisterPacketModule(MODULE_NAME, "tls-store", LogTlsStoreLogInitCtx, + LogTlsStoreLogger, LogTlsStoreCondition); + + SC_ATOMIC_INIT(cert_id); + + SCLogDebug("registered"); +} |