suricata
log-tlsstore.c
Go to the documentation of this file.
1 /* Copyright (C) 2014 Open Information Security Foundation
2  *
3  * You can copy, redistribute or modify this Program under the terms of
4  * the GNU General Public License version 2 as published by the Free
5  * Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * version 2 along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15  * 02110-1301, USA.
16  */
17 
18 /**
19  * \file
20  *
21  * \author Roliers Jean-Paul <popof.fpn@gmail.co>
22  * \author Eric Leblond <eric@regit.org>
23  * \author Victor Julien <victor@inliniac.net>
24  *
25  * Implements TLS store portion of the engine.
26  *
27  */
28 
29 #include "suricata-common.h"
30 #include "debug.h"
31 #include "detect.h"
32 #include "pkt-var.h"
33 #include "conf.h"
34 
35 #include "threads.h"
36 #include "threadvars.h"
37 #include "tm-threads.h"
38 
39 #include "util-print.h"
40 #include "util-unittest.h"
41 
42 #include "util-debug.h"
43 
44 #include "output.h"
45 #include "log-tlslog.h"
46 #include "log-tlsstore.h"
47 #include "app-layer-ssl.h"
48 #include "app-layer.h"
49 #include "app-layer-parser.h"
50 #include "util-privs.h"
51 #include "util-buffer.h"
52 
53 #include "util-logopenfile.h"
54 #include "util-time.h"
55 
56 #define MODULE_NAME "LogTlsStoreLog"
57 
58 static char tls_logfile_base_dir[PATH_MAX] = "/tmp";
59 SC_ATOMIC_DECLARE(unsigned int, cert_id);
60 static char logging_dir_not_writable;
61 
62 #define LOGGING_WRITE_ISSUE_LIMIT 6
63 
64 typedef struct LogTlsStoreLogThread_ {
65  uint32_t tls_cnt;
66 
67  uint8_t* enc_buf;
68  size_t enc_buf_len;
70 
71 static int CreateFileName(const Packet *p, SSLState *state, char *filename, size_t filename_size)
72 {
73  char path[PATH_MAX];
74  int file_id = SC_ATOMIC_ADD(cert_id, 1);
75 
76  /* Use format : packet time + incremental ID
77  * When running on same pcap it will overwrite
78  * On a live device, we will not be able to overwrite */
79  if (snprintf(path, sizeof(path), "%s/%ld.%ld-%d.pem",
80  tls_logfile_base_dir,
81  (long int)p->ts.tv_sec,
82  (long int)p->ts.tv_usec,
83  file_id) == sizeof(path))
84  return 0;
85 
86  strlcpy(filename, path, filename_size);
87  return 1;
88 }
89 
90 static void LogTlsLogPem(LogTlsStoreLogThread *aft, const Packet *p, SSLState *state, int ipproto)
91 {
92 #define PEMHEADER "-----BEGIN CERTIFICATE-----\n"
93 #define PEMFOOTER "-----END CERTIFICATE-----\n"
94  //Logging pem certificate
95  char filename[PATH_MAX] = "";
96  FILE* fp = NULL;
97  FILE* fpmeta = NULL;
98  unsigned long pemlen;
99  unsigned char* pembase64ptr = NULL;
100  int ret;
101  uint8_t *ptmp;
102  SSLCertsChain *cert;
103 
104  if (TAILQ_EMPTY(&state->server_connp.certs))
105  SCReturn;
106 
107  CreateFileName(p, state, filename, sizeof(filename));
108  if (strlen(filename) == 0) {
109  SCLogWarning(SC_ERR_FOPEN, "Can't create PEM filename");
110  SCReturn;
111  }
112 
113  fp = fopen(filename, "w");
114  if (fp == NULL) {
115  if (logging_dir_not_writable < LOGGING_WRITE_ISSUE_LIMIT) {
117  "Can't create PEM file '%s' in '%s' directory",
118  filename, tls_logfile_base_dir);
119  logging_dir_not_writable++;
120  }
121  SCReturn;
122  }
123 
124  TAILQ_FOREACH(cert, &state->server_connp.certs, next) {
125  pemlen = Base64EncodeBufferSize(cert->cert_len);
126  if (pemlen > aft->enc_buf_len) {
127  ptmp = (uint8_t*) SCRealloc(aft->enc_buf, sizeof(uint8_t) * pemlen);
128  if (ptmp == NULL) {
129  SCFree(aft->enc_buf);
130  aft->enc_buf = NULL;
131  aft->enc_buf_len = 0;
132  SCLogWarning(SC_ERR_MEM_ALLOC, "Can't allocate data for base64 encoding");
133  goto end_fp;
134  }
135  aft->enc_buf = ptmp;
136  aft->enc_buf_len = pemlen;
137  }
138 
139  memset(aft->enc_buf, 0, aft->enc_buf_len);
140 
141  ret = Base64Encode((unsigned char*) cert->cert_data, cert->cert_len, aft->enc_buf, &pemlen);
142  if (ret != SC_BASE64_OK) {
143  SCLogWarning(SC_ERR_INVALID_ARGUMENTS, "Invalid return of Base64Encode function");
144  goto end_fwrite_fp;
145  }
146 
147  if (fprintf(fp, PEMHEADER) < 0)
148  goto end_fwrite_fp;
149 
150  pembase64ptr = aft->enc_buf;
151  while (pemlen > 0) {
152  size_t loffset = pemlen >= 64 ? 64 : pemlen;
153  if (fwrite(pembase64ptr, 1, loffset, fp) != loffset)
154  goto end_fwrite_fp;
155  if (fwrite("\n", 1, 1, fp) != 1)
156  goto end_fwrite_fp;
157  pembase64ptr += 64;
158  if (pemlen < 64)
159  break;
160  pemlen -= 64;
161  }
162 
163  if (fprintf(fp, PEMFOOTER) < 0)
164  goto end_fwrite_fp;
165  }
166  fclose(fp);
167 
168  //Logging certificate informations
169  memcpy(filename + (strlen(filename) - 3), "meta", 4);
170  fpmeta = fopen(filename, "w");
171  if (fpmeta != NULL) {
172  #define PRINT_BUF_LEN 46
173  char srcip[PRINT_BUF_LEN], dstip[PRINT_BUF_LEN];
174  char timebuf[64];
175  Port sp, dp;
176  CreateTimeString(&p->ts, timebuf, sizeof(timebuf));
177  if (!TLSGetIPInformations(p, srcip, PRINT_BUF_LEN, &sp, dstip, PRINT_BUF_LEN, &dp, ipproto))
178  goto end_fwrite_fpmeta;
179  if (fprintf(fpmeta, "TIME: %s\n", timebuf) < 0)
180  goto end_fwrite_fpmeta;
181  if (p->pcap_cnt > 0) {
182  if (fprintf(fpmeta, "PCAP PKT NUM: %"PRIu64"\n", p->pcap_cnt) < 0)
183  goto end_fwrite_fpmeta;
184  }
185  if (fprintf(fpmeta, "SRC IP: %s\n", srcip) < 0)
186  goto end_fwrite_fpmeta;
187  if (fprintf(fpmeta, "DST IP: %s\n", dstip) < 0)
188  goto end_fwrite_fpmeta;
189  if (fprintf(fpmeta, "PROTO: %" PRIu32 "\n", p->proto) < 0)
190  goto end_fwrite_fpmeta;
191  if (PKT_IS_TCP(p) || PKT_IS_UDP(p)) {
192  if (fprintf(fpmeta, "SRC PORT: %" PRIu16 "\n", sp) < 0)
193  goto end_fwrite_fpmeta;
194  if (fprintf(fpmeta, "DST PORT: %" PRIu16 "\n", dp) < 0)
195  goto end_fwrite_fpmeta;
196  }
197 
198  if (fprintf(fpmeta, "TLS SUBJECT: %s\n"
199  "TLS ISSUERDN: %s\n"
200  "TLS FINGERPRINT: %s\n",
203  state->server_connp.cert0_fingerprint) < 0)
204  goto end_fwrite_fpmeta;
205 
206  fclose(fpmeta);
207  } else {
208  if (logging_dir_not_writable < LOGGING_WRITE_ISSUE_LIMIT) {
210  "Can't create meta file '%s' in '%s' directory",
211  filename, tls_logfile_base_dir);
212  logging_dir_not_writable++;
213  }
214  SCReturn;
215  }
216 
217  /* Reset the store flag */
219  SCReturn;
220 
221 end_fwrite_fp:
222  fclose(fp);
223  if (logging_dir_not_writable < LOGGING_WRITE_ISSUE_LIMIT) {
224  SCLogWarning(SC_ERR_FWRITE, "Unable to write certificate");
225  logging_dir_not_writable++;
226  }
227 end_fwrite_fpmeta:
228  if (fpmeta) {
229  fclose(fpmeta);
230  if (logging_dir_not_writable < LOGGING_WRITE_ISSUE_LIMIT) {
231  SCLogWarning(SC_ERR_FWRITE, "Unable to write certificate metafile");
232  logging_dir_not_writable++;
233  }
234  }
235  SCReturn;
236 end_fp:
237  fclose(fp);
238  SCReturn;
239 }
240 
241 /** \internal
242  * \brief Condition function for TLS logger
243  * \retval bool true or false -- log now?
244  */
245 static int LogTlsStoreCondition(ThreadVars *tv, const Packet *p, void *state,
246  void *tx, uint64_t tx_id)
247 {
248  if (p->flow == NULL) {
249  return FALSE;
250  }
251 
252  if (!(PKT_IS_TCP(p))) {
253  return FALSE;
254  }
255 
256  SSLState *ssl_state = (SSLState *)state;
257  if (ssl_state == NULL) {
258  SCLogDebug("no tls state, so no request logging");
259  goto dontlog;
260  }
261 
262  if ((ssl_state->server_connp.cert_log_flag & SSL_TLS_LOG_PEM) == 0)
263  goto dontlog;
264 
265  if (ssl_state->server_connp.cert0_issuerdn == NULL ||
266  ssl_state->server_connp.cert0_subject == NULL)
267  goto dontlog;
268 
269  return TRUE;
270 dontlog:
271  return FALSE;
272 }
273 
274 static int LogTlsStoreLogger(ThreadVars *tv, void *thread_data, const Packet *p,
275  Flow *f, void *state, void *tx, uint64_t tx_id)
276 {
277  LogTlsStoreLogThread *aft = (LogTlsStoreLogThread *)thread_data;
278  int ipproto = (PKT_IS_IPV4(p)) ? AF_INET : AF_INET6;
279 
280  SSLState *ssl_state = (SSLState *)state;
281  if (unlikely(ssl_state == NULL)) {
282  return 0;
283  }
284 
285  if (ssl_state->server_connp.cert_log_flag & SSL_TLS_LOG_PEM) {
286  LogTlsLogPem(aft, p, ssl_state, ipproto);
287  }
288 
289  return 0;
290 }
291 
292 static TmEcode LogTlsStoreLogThreadInit(ThreadVars *t, const void *initdata, void **data)
293 {
295  if (unlikely(aft == NULL))
296  return TM_ECODE_FAILED;
297  memset(aft, 0, sizeof(LogTlsStoreLogThread));
298 
299  if (initdata == NULL) {
300  SCLogDebug("Error getting context for LogTLSStore. \"initdata\" argument NULL");
301  SCFree(aft);
302  return TM_ECODE_FAILED;
303  }
304 
305  struct stat stat_buf;
306  /* coverity[toctou] */
307  if (stat(tls_logfile_base_dir, &stat_buf) != 0) {
308  int ret;
309  /* coverity[toctou] */
310  ret = SCMkDir(tls_logfile_base_dir, S_IRWXU|S_IXGRP|S_IRGRP);
311  if (ret != 0) {
312  int err = errno;
313  if (err != EEXIST) {
315  "Cannot create certs drop directory %s: %s",
316  tls_logfile_base_dir, strerror(err));
317  exit(EXIT_FAILURE);
318  }
319  } else {
320  SCLogInfo("Created certs drop directory %s",
321  tls_logfile_base_dir);
322  }
323 
324  }
325 
326  *data = (void *)aft;
327  return TM_ECODE_OK;
328 }
329 
330 static TmEcode LogTlsStoreLogThreadDeinit(ThreadVars *t, void *data)
331 {
333  if (aft == NULL) {
334  return TM_ECODE_OK;
335  }
336 
337  if (aft->enc_buf != NULL)
338  SCFree(aft->enc_buf);
339 
340  /* clear memory */
341  memset(aft, 0, sizeof(LogTlsStoreLogThread));
342 
343  SCFree(aft);
344  return TM_ECODE_OK;
345 }
346 
347 static void LogTlsStoreLogExitPrintStats(ThreadVars *tv, void *data)
348 {
350  if (aft == NULL) {
351  return;
352  }
353 
354  SCLogInfo("(%s) certificates extracted %" PRIu32 "", tv->name, aft->tls_cnt);
355 }
356 
357 /**
358  * \internal
359  *
360  * \brief deinit the log ctx and write out the waldo
361  *
362  * \param output_ctx output context to deinit
363  */
364 static void LogTlsStoreLogDeInitCtx(OutputCtx *output_ctx)
365 {
366  SCFree(output_ctx);
367 }
368 
369 /** \brief Create a new http log LogFilestoreCtx.
370  * \param conf Pointer to ConfNode containing this loggers configuration.
371  * \return NULL if failure, LogFilestoreCtx* to the file_ctx if succesful
372  * */
373 static OutputInitResult LogTlsStoreLogInitCtx(ConfNode *conf)
374 {
375  OutputInitResult result = { NULL, false };
376  OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
377  if (unlikely(output_ctx == NULL))
378  return result;
379 
380  output_ctx->data = NULL;
381  output_ctx->DeInit = LogTlsStoreLogDeInitCtx;
382 
383  /* FIXME we need to implement backward compability here */
384  const char *s_default_log_dir = NULL;
385  s_default_log_dir = ConfigGetLogDirectory();
386 
387  const char *s_base_dir = NULL;
388  s_base_dir = ConfNodeLookupChildValue(conf, "certs-log-dir");
389  if (s_base_dir == NULL || strlen(s_base_dir) == 0) {
390  strlcpy(tls_logfile_base_dir,
391  s_default_log_dir, sizeof(tls_logfile_base_dir));
392  } else {
393  if (PathIsAbsolute(s_base_dir)) {
394  strlcpy(tls_logfile_base_dir,
395  s_base_dir, sizeof(tls_logfile_base_dir));
396  } else {
397  snprintf(tls_logfile_base_dir, sizeof(tls_logfile_base_dir),
398  "%s/%s", s_default_log_dir, s_base_dir);
399  }
400  }
401 
402  SCLogInfo("storing certs in %s", tls_logfile_base_dir);
403 
404  /* enable the logger for the app layer */
406 
407  result.ctx = output_ctx;
408  result.ok = true;
409  SCReturnCT(result, "OutputInitResult");
410 }
411 
413 {
415  "tls-store", LogTlsStoreLogInitCtx, ALPROTO_TLS, LogTlsStoreLogger,
416  LogTlsStoreCondition, LogTlsStoreLogThreadInit,
417  LogTlsStoreLogThreadDeinit, LogTlsStoreLogExitPrintStats);
418 
419  SC_ATOMIC_INIT(cert_id);
420  SC_ATOMIC_SET(cert_id, 1);
421 
422  SCLogDebug("registered");
423 }
SC_ERR_FWRITE
@ SC_ERR_FWRITE
Definition: util-error.h:129
PKT_IS_UDP
#define PKT_IS_UDP(p)
Definition: decode.h:256
LogTlsStoreRegister
void LogTlsStoreRegister(void)
Definition: log-tlsstore.c:412
tm-threads.h
SSLStateConnp_::cert0_subject
char * cert0_subject
Definition: app-layer-ssl.h:209
Packet_::proto
uint8_t proto
Definition: decode.h:455
SSLState_
SSLv[2.0|3.[0|1|2|3]] state structure.
Definition: app-layer-ssl.h:240
SSLCertsChain_::cert_len
uint32_t cert_len
Definition: app-layer-ssl.h:181
ThreadVars_::name
char name[16]
Definition: threadvars.h:63
SC_ATOMIC_INIT
#define SC_ATOMIC_INIT(name)
wrapper for initializing an atomic variable.
Definition: util-atomic.h:315
unlikely
#define unlikely(expr)
Definition: util-optimize.h:35
SC_ATOMIC_SET
#define SC_ATOMIC_SET(name, val)
Set the value for the atomic variable.
Definition: util-atomic.h:387
ALPROTO_TLS
@ ALPROTO_TLS
Definition: app-layer-protos.h:33
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:296
Packet_::pcap_cnt
uint64_t pcap_cnt
Definition: decode.h:594
next
struct HtpBodyChunk_ * next
Definition: app-layer-htp.h:0
SSLState_::server_connp
SSLStateConnp server_connp
Definition: app-layer-ssl.h:258
threads.h
Flow_
Flow data structure.
Definition: flow.h:353
SC_ATOMIC_ADD
#define SC_ATOMIC_ADD(name, val)
add a value to our atomic variable
Definition: util-atomic.h:333
TAILQ_EMPTY
#define TAILQ_EMPTY(head)
Definition: queue.h:248
TAILQ_FOREACH
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:252
util-privs.h
TM_ECODE_FAILED
@ TM_ECODE_FAILED
Definition: tm-threads-common.h:83
SC_ERR_INVALID_ARGUMENTS
@ SC_ERR_INVALID_ARGUMENTS
Definition: util-error.h:82
util-unittest.h
LogTlsStoreLogThread_
Definition: log-tlsstore.c:64
SSLStateConnp_::cert0_issuerdn
char * cert0_issuerdn
Definition: app-layer-ssl.h:210
OutputCtx_::data
void * data
Definition: tm-modules.h:81
TM_ECODE_OK
@ TM_ECODE_OK
Definition: tm-threads-common.h:82
OutputCtx_
Definition: tm-modules.h:78
strlcpy
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: util-strlcpyu.c:43
PKT_IS_TCP
#define PKT_IS_TCP(p)
Definition: decode.h:255
util-debug.h
OutputInitResult_::ctx
OutputCtx * ctx
Definition: output.h:45
AppLayerParserRegisterLogger
void AppLayerParserRegisterLogger(uint8_t ipproto, AppProto alproto)
Definition: app-layer-parser.c:492
SSLCertsChain_
Definition: app-layer-ssl.h:179
util-print.h
detect.h
ThreadVars_
Per thread variable structure.
Definition: threadvars.h:56
pkt-var.h
LogTlsStoreLogThread
struct LogTlsStoreLogThread_ LogTlsStoreLogThread
util-time.h
OutputInitResult_::ok
bool ok
Definition: output.h:46
log-tlslog.h
app-layer-parser.h
TRUE
#define TRUE
Definition: suricata-common.h:33
LogTlsStoreLogThread_::enc_buf_len
size_t enc_buf_len
Definition: log-tlsstore.c:68
FALSE
#define FALSE
Definition: suricata-common.h:34
SCReturn
#define SCReturn
Definition: util-debug.h:300
SC_ERR_FOPEN
@ SC_ERR_FOPEN
Definition: util-error.h:74
Packet_
Definition: decode.h:433
conf.h
Port
uint16_t Port
Definition: decode.h:237
TmEcode
TmEcode
Definition: tm-threads-common.h:81
CreateTimeString
void CreateTimeString(const struct timeval *ts, char *str, size_t size)
Definition: util-time.c:278
SSLCertsChain_::cert_data
uint8_t * cert_data
Definition: app-layer-ssl.h:180
SCLogInfo
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition: util-debug.h:215
SCRealloc
#define SCRealloc(ptr, sz)
Definition: util-mem.h:50
OutputInitResult_
Definition: output.h:44
Packet_::flow
struct Flow_ * flow
Definition: decode.h:470
Packet_::ts
struct timeval ts
Definition: decode.h:476
suricata-common.h
OutputCtx_::DeInit
void(* DeInit)(struct OutputCtx_ *)
Definition: tm-modules.h:84
PEMFOOTER
#define PEMFOOTER
PathIsAbsolute
int PathIsAbsolute(const char *path)
Check if a path is absolute.
Definition: util-path.c:45
SCLogError
#define SCLogError(err_code,...)
Macro used to log ERROR messages.
Definition: util-debug.h:255
PRINT_BUF_LEN
#define PRINT_BUF_LEN
SCMkDir
#define SCMkDir(a, b)
Definition: util-path.h:29
MODULE_NAME
#define MODULE_NAME
Definition: log-tlsstore.c:56
tv
ThreadVars * tv
Definition: fuzz_decodepcapfile.c:31
SSL_TLS_LOG_PEM
#define SSL_TLS_LOG_PEM
Definition: app-layer-ssl.h:132
threadvars.h
SCMalloc
#define SCMalloc(sz)
Definition: util-mem.h:47
TLSGetIPInformations
int TLSGetIPInformations(const Packet *p, char *srcip, size_t srcip_len, Port *sp, char *dstip, size_t dstip_len, Port *dp, int ipproto)
Definition: log-tlslog.c:96
ConfigGetLogDirectory
const char * ConfigGetLogDirectory()
Definition: util-conf.c:36
SCLogWarning
#define SCLogWarning(err_code,...)
Macro used to log WARNING messages.
Definition: util-debug.h:242
SCFree
#define SCFree(p)
Definition: util-mem.h:61
ConfNode_
Definition: conf.h:32
util-logopenfile.h
util-buffer.h
SSLStateConnp_::cert_log_flag
uint32_t cert_log_flag
Definition: app-layer-ssl.h:223
SCReturnCT
#define SCReturnCT(x, type)
Definition: util-debug.h:312
SC_ERR_MEM_ALLOC
@ SC_ERR_MEM_ALLOC
Definition: util-error.h:31
LOGGING_WRITE_ISSUE_LIMIT
#define LOGGING_WRITE_ISSUE_LIMIT
Definition: log-tlsstore.c:62
LogTlsStoreLogThread_::enc_buf
uint8_t * enc_buf
Definition: log-tlsstore.c:67
LOGGER_TLS_STORE
@ LOGGER_TLS_STORE
Definition: suricata-common.h:442
SSLStateConnp_::cert0_fingerprint
char * cert0_fingerprint
Definition: app-layer-ssl.h:214
SC_ATOMIC_DECLARE
SC_ATOMIC_DECLARE(unsigned int, cert_id)
log-tlsstore.h
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
PKT_IS_IPV4
#define PKT_IS_IPV4(p)
Definition: decode.h:253
SC_ERR_LOGDIR_CONFIG
@ SC_ERR_LOGDIR_CONFIG
Definition: util-error.h:146
OutputRegisterTxModuleWithCondition
void OutputRegisterTxModuleWithCondition(LoggerId id, const char *name, const char *conf_name, OutputInitFunc InitFunc, AppProto alproto, TxLogger TxLogFunc, TxLoggerCondition TxLogCondition, ThreadInitFunc ThreadInit, ThreadDeinitFunc ThreadDeinit, ThreadExitPrintStatsFunc ThreadExitPrintStats)
Register a tx output module with condition.
Definition: output.c:339
app-layer-ssl.h
PEMHEADER
#define PEMHEADER
debug.h
output.h
app-layer.h
LogTlsStoreLogThread_::tls_cnt
uint32_t tls_cnt
Definition: log-tlsstore.c:65
ConfNodeLookupChildValue
const char * ConfNodeLookupChildValue(const ConfNode *node, const char *name)
Lookup the value of a child configuration node by name.
Definition: conf.c:798