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-crypt.h"
55 #include "util-time.h"
56 
57 #define MODULE_NAME "LogTlsStoreLog"
58 
59 static char tls_logfile_base_dir[PATH_MAX] = "/tmp";
60 SC_ATOMIC_DECLARE(unsigned int, cert_id);
61 static char logging_dir_not_writable;
62 
63 #define LOGGING_WRITE_ISSUE_LIMIT 6
64 
65 typedef struct LogTlsStoreLogThread_ {
66  uint32_t tls_cnt;
67 
68  uint8_t* enc_buf;
69  size_t enc_buf_len;
71 
72 static int CreateFileName(const Packet *p, SSLState *state, char *filename, size_t filename_size)
73 {
74  char path[PATH_MAX];
75  int file_id = SC_ATOMIC_ADD(cert_id, 1);
76 
77  /* Use format : packet time + incremental ID
78  * When running on same pcap it will overwrite
79  * On a live device, we will not be able to overwrite */
80  if (snprintf(path, sizeof(path), "%s/%ld.%ld-%d.pem",
81  tls_logfile_base_dir,
82  (long int)p->ts.tv_sec,
83  (long int)p->ts.tv_usec,
84  file_id) == sizeof(path))
85  return 0;
86 
87  strlcpy(filename, path, filename_size);
88  return 1;
89 }
90 
91 static void LogTlsLogPem(LogTlsStoreLogThread *aft, const Packet *p, SSLState *state, int ipproto)
92 {
93 #define PEMHEADER "-----BEGIN CERTIFICATE-----\n"
94 #define PEMFOOTER "-----END CERTIFICATE-----\n"
95  //Logging pem certificate
96  char filename[PATH_MAX] = "";
97  FILE* fp = NULL;
98  FILE* fpmeta = NULL;
99  unsigned long pemlen;
100  unsigned char* pembase64ptr = NULL;
101  int ret;
102  uint8_t *ptmp;
103  SSLCertsChain *cert;
104 
105  if (TAILQ_EMPTY(&state->server_connp.certs))
106  SCReturn;
107 
108  CreateFileName(p, state, filename, sizeof(filename));
109  if (strlen(filename) == 0) {
110  SCLogWarning(SC_ERR_FOPEN, "Can't create PEM filename");
111  SCReturn;
112  }
113 
114  fp = fopen(filename, "w");
115  if (fp == NULL) {
116  if (logging_dir_not_writable < LOGGING_WRITE_ISSUE_LIMIT) {
118  "Can't create PEM file '%s' in '%s' directory",
119  filename, tls_logfile_base_dir);
120  logging_dir_not_writable++;
121  }
122  SCReturn;
123  }
124 
125  TAILQ_FOREACH(cert, &state->server_connp.certs, next) {
126  pemlen = (4 * (cert->cert_len + 2) / 3) +1;
127  if (pemlen > aft->enc_buf_len) {
128  ptmp = (uint8_t*) SCRealloc(aft->enc_buf, sizeof(uint8_t) * pemlen);
129  if (ptmp == NULL) {
130  SCFree(aft->enc_buf);
131  aft->enc_buf = NULL;
132  aft->enc_buf_len = 0;
133  SCLogWarning(SC_ERR_MEM_ALLOC, "Can't allocate data for base64 encoding");
134  goto end_fp;
135  }
136  aft->enc_buf = ptmp;
137  aft->enc_buf_len = pemlen;
138  }
139 
140  memset(aft->enc_buf, 0, aft->enc_buf_len);
141 
142  ret = Base64Encode((unsigned char*) cert->cert_data, cert->cert_len, aft->enc_buf, &pemlen);
143  if (ret != SC_BASE64_OK) {
144  SCLogWarning(SC_ERR_INVALID_ARGUMENTS, "Invalid return of Base64Encode function");
145  goto end_fwrite_fp;
146  }
147 
148  if (fprintf(fp, PEMHEADER) < 0)
149  goto end_fwrite_fp;
150 
151  pembase64ptr = aft->enc_buf;
152  while (pemlen > 0) {
153  size_t loffset = pemlen >= 64 ? 64 : pemlen;
154  if (fwrite(pembase64ptr, 1, loffset, fp) != loffset)
155  goto end_fwrite_fp;
156  if (fwrite("\n", 1, 1, fp) != 1)
157  goto end_fwrite_fp;
158  pembase64ptr += 64;
159  if (pemlen < 64)
160  break;
161  pemlen -= 64;
162  }
163 
164  if (fprintf(fp, PEMFOOTER) < 0)
165  goto end_fwrite_fp;
166  }
167  fclose(fp);
168 
169  //Logging certificate informations
170  memcpy(filename + (strlen(filename) - 3), "meta", 4);
171  fpmeta = fopen(filename, "w");
172  if (fpmeta != NULL) {
173  #define PRINT_BUF_LEN 46
174  char srcip[PRINT_BUF_LEN], dstip[PRINT_BUF_LEN];
175  char timebuf[64];
176  Port sp, dp;
177  CreateTimeString(&p->ts, timebuf, sizeof(timebuf));
178  if (!TLSGetIPInformations(p, srcip, PRINT_BUF_LEN, &sp, dstip, PRINT_BUF_LEN, &dp, ipproto))
179  goto end_fwrite_fpmeta;
180  if (fprintf(fpmeta, "TIME: %s\n", timebuf) < 0)
181  goto end_fwrite_fpmeta;
182  if (p->pcap_cnt > 0) {
183  if (fprintf(fpmeta, "PCAP PKT NUM: %"PRIu64"\n", p->pcap_cnt) < 0)
184  goto end_fwrite_fpmeta;
185  }
186  if (fprintf(fpmeta, "SRC IP: %s\n", srcip) < 0)
187  goto end_fwrite_fpmeta;
188  if (fprintf(fpmeta, "DST IP: %s\n", dstip) < 0)
189  goto end_fwrite_fpmeta;
190  if (fprintf(fpmeta, "PROTO: %" PRIu32 "\n", p->proto) < 0)
191  goto end_fwrite_fpmeta;
192  if (PKT_IS_TCP(p) || PKT_IS_UDP(p)) {
193  if (fprintf(fpmeta, "SRC PORT: %" PRIu16 "\n", sp) < 0)
194  goto end_fwrite_fpmeta;
195  if (fprintf(fpmeta, "DST PORT: %" PRIu16 "\n", dp) < 0)
196  goto end_fwrite_fpmeta;
197  }
198 
199  if (fprintf(fpmeta, "TLS SUBJECT: %s\n"
200  "TLS ISSUERDN: %s\n"
201  "TLS FINGERPRINT: %s\n",
204  state->server_connp.cert0_fingerprint) < 0)
205  goto end_fwrite_fpmeta;
206 
207  fclose(fpmeta);
208  } else {
209  if (logging_dir_not_writable < LOGGING_WRITE_ISSUE_LIMIT) {
211  "Can't create meta file '%s' in '%s' directory",
212  filename, tls_logfile_base_dir);
213  logging_dir_not_writable++;
214  }
215  SCReturn;
216  }
217 
218  /* Reset the store flag */
220  SCReturn;
221 
222 end_fwrite_fp:
223  fclose(fp);
224  if (logging_dir_not_writable < LOGGING_WRITE_ISSUE_LIMIT) {
225  SCLogWarning(SC_ERR_FWRITE, "Unable to write certificate");
226  logging_dir_not_writable++;
227  }
228 end_fwrite_fpmeta:
229  if (fpmeta) {
230  fclose(fpmeta);
231  if (logging_dir_not_writable < LOGGING_WRITE_ISSUE_LIMIT) {
232  SCLogWarning(SC_ERR_FWRITE, "Unable to write certificate metafile");
233  logging_dir_not_writable++;
234  }
235  }
236  SCReturn;
237 end_fp:
238  fclose(fp);
239  SCReturn;
240 }
241 
242 /** \internal
243  * \brief Condition function for TLS logger
244  * \retval bool true or false -- log now?
245  */
246 static int LogTlsStoreCondition(ThreadVars *tv, const Packet *p, void *state,
247  void *tx, uint64_t tx_id)
248 {
249  if (p->flow == NULL) {
250  return FALSE;
251  }
252 
253  if (!(PKT_IS_TCP(p))) {
254  return FALSE;
255  }
256 
257  SSLState *ssl_state = (SSLState *)state;
258  if (ssl_state == NULL) {
259  SCLogDebug("no tls state, so no request logging");
260  goto dontlog;
261  }
262 
263  if ((ssl_state->server_connp.cert_log_flag & SSL_TLS_LOG_PEM) == 0)
264  goto dontlog;
265 
266  if (ssl_state->server_connp.cert0_issuerdn == NULL ||
267  ssl_state->server_connp.cert0_subject == NULL)
268  goto dontlog;
269 
270  return TRUE;
271 dontlog:
272  return FALSE;
273 }
274 
275 static int LogTlsStoreLogger(ThreadVars *tv, void *thread_data, const Packet *p,
276  Flow *f, void *state, void *tx, uint64_t tx_id)
277 {
278  LogTlsStoreLogThread *aft = (LogTlsStoreLogThread *)thread_data;
279  int ipproto = (PKT_IS_IPV4(p)) ? AF_INET : AF_INET6;
280 
281  SSLState *ssl_state = (SSLState *)state;
282  if (unlikely(ssl_state == NULL)) {
283  return 0;
284  }
285 
286  if (ssl_state->server_connp.cert_log_flag & SSL_TLS_LOG_PEM) {
287  LogTlsLogPem(aft, p, ssl_state, ipproto);
288  }
289 
290  return 0;
291 }
292 
293 static TmEcode LogTlsStoreLogThreadInit(ThreadVars *t, const void *initdata, void **data)
294 {
296  if (unlikely(aft == NULL))
297  return TM_ECODE_FAILED;
298  memset(aft, 0, sizeof(LogTlsStoreLogThread));
299 
300  if (initdata == NULL) {
301  SCLogDebug("Error getting context for LogTLSStore. \"initdata\" argument NULL");
302  SCFree(aft);
303  return TM_ECODE_FAILED;
304  }
305 
306  struct stat stat_buf;
307  /* coverity[toctou] */
308  if (stat(tls_logfile_base_dir, &stat_buf) != 0) {
309  int ret;
310  /* coverity[toctou] */
311  ret = SCMkDir(tls_logfile_base_dir, S_IRWXU|S_IXGRP|S_IRGRP);
312  if (ret != 0) {
313  int err = errno;
314  if (err != EEXIST) {
316  "Cannot create certs drop directory %s: %s",
317  tls_logfile_base_dir, strerror(err));
318  exit(EXIT_FAILURE);
319  }
320  } else {
321  SCLogInfo("Created certs drop directory %s",
322  tls_logfile_base_dir);
323  }
324 
325  }
326 
327  *data = (void *)aft;
328  return TM_ECODE_OK;
329 }
330 
331 static TmEcode LogTlsStoreLogThreadDeinit(ThreadVars *t, void *data)
332 {
334  if (aft == NULL) {
335  return TM_ECODE_OK;
336  }
337 
338  if (aft->enc_buf != NULL)
339  SCFree(aft->enc_buf);
340 
341  /* clear memory */
342  memset(aft, 0, sizeof(LogTlsStoreLogThread));
343 
344  SCFree(aft);
345  return TM_ECODE_OK;
346 }
347 
348 static void LogTlsStoreLogExitPrintStats(ThreadVars *tv, void *data)
349 {
351  if (aft == NULL) {
352  return;
353  }
354 
355  SCLogInfo("(%s) certificates extracted %" PRIu32 "", tv->name, aft->tls_cnt);
356 }
357 
358 /**
359  * \internal
360  *
361  * \brief deinit the log ctx and write out the waldo
362  *
363  * \param output_ctx output context to deinit
364  */
365 static void LogTlsStoreLogDeInitCtx(OutputCtx *output_ctx)
366 {
367  SCFree(output_ctx);
368 }
369 
370 /** \brief Create a new http log LogFilestoreCtx.
371  * \param conf Pointer to ConfNode containing this loggers configuration.
372  * \return NULL if failure, LogFilestoreCtx* to the file_ctx if succesful
373  * */
374 static OutputInitResult LogTlsStoreLogInitCtx(ConfNode *conf)
375 {
376  OutputInitResult result = { NULL, false };
377  OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
378  if (unlikely(output_ctx == NULL))
379  return result;
380 
381  output_ctx->data = NULL;
382  output_ctx->DeInit = LogTlsStoreLogDeInitCtx;
383 
384  /* FIXME we need to implement backward compability here */
385  const char *s_default_log_dir = NULL;
386  s_default_log_dir = ConfigGetLogDirectory();
387 
388  const char *s_base_dir = NULL;
389  s_base_dir = ConfNodeLookupChildValue(conf, "certs-log-dir");
390  if (s_base_dir == NULL || strlen(s_base_dir) == 0) {
391  strlcpy(tls_logfile_base_dir,
392  s_default_log_dir, sizeof(tls_logfile_base_dir));
393  } else {
394  if (PathIsAbsolute(s_base_dir)) {
395  strlcpy(tls_logfile_base_dir,
396  s_base_dir, sizeof(tls_logfile_base_dir));
397  } else {
398  snprintf(tls_logfile_base_dir, sizeof(tls_logfile_base_dir),
399  "%s/%s", s_default_log_dir, s_base_dir);
400  }
401  }
402 
403  SCLogInfo("storing certs in %s", tls_logfile_base_dir);
404 
405  /* enable the logger for the app layer */
407 
408  result.ctx = output_ctx;
409  result.ok = true;
410  SCReturnCT(result, "OutputInitResult");
411 }
412 
414 {
416  "tls-store", LogTlsStoreLogInitCtx, ALPROTO_TLS, LogTlsStoreLogger,
417  LogTlsStoreCondition, LogTlsStoreLogThreadInit,
418  LogTlsStoreLogThreadDeinit, LogTlsStoreLogExitPrintStats);
419 
420  SC_ATOMIC_INIT(cert_id);
421 
422  SCLogDebug("registered");
423 }
void LogTlsStoreRegister(void)
Definition: log-tlsstore.c:413
char * cert0_subject
#define SCLogDebug(...)
Definition: util-debug.h:335
struct Flow_ * flow
Definition: decode.h:444
#define TAILQ_FOREACH(var, head, field)
Definition: queue.h:350
struct HtpBodyChunk_ * next
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: util-strlcpyu.c:43
#define FALSE
#define SCMkDir(a, b)
Definition: util-path.h:29
#define unlikely(expr)
Definition: util-optimize.h:35
char * cert0_fingerprint
#define MODULE_NAME
Definition: log-tlsstore.c:57
SSLStateConnp server_connp
#define SC_ATOMIC_ADD(name, val)
add a value to our atomic variable
Definition: util-atomic.h:108
void(* DeInit)(struct OutputCtx_ *)
Definition: tm-modules.h:84
uint64_t pcap_cnt
Definition: decode.h:566
#define TRUE
#define PKT_IS_IPV4(p)
Definition: decode.h:250
#define SCReturnCT(x, type)
Definition: util-debug.h:351
SC_ATOMIC_DECLARE(unsigned int, cert_id)
SSLv[2.0|3.[0|1|2|3]] state structure.
#define SC_ATOMIC_INIT(name)
Initialize the previously declared atomic variable and it&#39;s lock.
Definition: util-atomic.h:82
#define SCCalloc(nm, a)
Definition: util-mem.h:205
const char * ConfNodeLookupChildValue(const ConfNode *node, const char *name)
Lookup the value of a child configuration node by name.
Definition: conf.c:843
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:145
uint8_t proto
Definition: decode.h:429
#define SCLogError(err_code,...)
Macro used to log ERROR messages.
Definition: util-debug.h:294
#define PEMFOOTER
void AppLayerParserRegisterLogger(uint8_t ipproto, AppProto alproto)
int PathIsAbsolute(const char *path)
Check if a path is absolute.
Definition: util-path.c:39
uint16_t Port
Definition: decode.h:234
#define SSL_TLS_LOG_PEM
#define LOGGING_WRITE_ISSUE_LIMIT
Definition: log-tlsstore.c:63
#define PRINT_BUF_LEN
#define SCLogWarning(err_code,...)
Macro used to log WARNING messages.
Definition: util-debug.h:281
struct LogTlsStoreLogThread_ LogTlsStoreLogThread
#define SCRealloc(x, a)
Definition: util-mem.h:190
Definition: conf.h:32
OutputCtx * ctx
Definition: output.h:42
const char * ConfigGetLogDirectory()
Definition: util-conf.c:36
#define SCMalloc(a)
Definition: util-mem.h:174
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition: util-debug.h:254
#define SCFree(a)
Definition: util-mem.h:236
uint32_t cert_log_flag
#define PKT_IS_TCP(p)
Definition: decode.h:252
uint32_t cert_len
uint16_t tx_id
uint8_t * cert_data
void * data
Definition: tm-modules.h:81
#define PEMHEADER
int Base64Encode(const unsigned char *in, unsigned long inlen, unsigned char *out, unsigned long *outlen)
Definition: util-crypt.c:272
char name[16]
Definition: threadvars.h:59
#define TAILQ_EMPTY(head)
Definition: queue.h:347
#define SCReturn
Definition: util-debug.h:339
#define PKT_IS_UDP(p)
Definition: decode.h:253
Per thread variable structure.
Definition: threadvars.h:57
struct timeval ts
Definition: decode.h:450
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:326
Flow data structure.
Definition: flow.h:327
char * cert0_issuerdn
void CreateTimeString(const struct timeval *ts, char *str, size_t size)
Definition: util-time.c:237