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