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;
70 } LogTlsStoreLogThread;
71
CreateFileName(const Packet * p,SSLState * state,char * filename,size_t filename_size)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
LogTlsLogPem(LogTlsStoreLogThread * aft,const Packet * p,SSLState * state,int ipproto)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) {
117 SCLogWarning(SC_ERR_FOPEN,
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 = BASE64_BUFFER_SIZE(cert->cert_len);
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",
202 state->server_connp.cert0_subject,
203 state->server_connp.cert0_issuerdn,
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) {
210 SCLogWarning(SC_ERR_FOPEN,
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 */
219 state->server_connp.cert_log_flag &= ~SSL_TLS_LOG_PEM;
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 */
LogTlsStoreCondition(ThreadVars * tv,const Packet * p,void * state,void * tx,uint64_t tx_id)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
LogTlsStoreLogger(ThreadVars * tv,void * thread_data,const Packet * p,Flow * f,void * state,void * tx,uint64_t tx_id)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
LogTlsStoreLogThreadInit(ThreadVars * t,const void * initdata,void ** data)293 static TmEcode LogTlsStoreLogThreadInit(ThreadVars *t, const void *initdata, void **data)
294 {
295 LogTlsStoreLogThread *aft = SCMalloc(sizeof(LogTlsStoreLogThread));
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) {
315 SCLogError(SC_ERR_LOGDIR_CONFIG,
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
LogTlsStoreLogThreadDeinit(ThreadVars * t,void * data)331 static TmEcode LogTlsStoreLogThreadDeinit(ThreadVars *t, void *data)
332 {
333 LogTlsStoreLogThread *aft = (LogTlsStoreLogThread *)data;
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
LogTlsStoreLogExitPrintStats(ThreadVars * tv,void * data)348 static void LogTlsStoreLogExitPrintStats(ThreadVars *tv, void *data)
349 {
350 LogTlsStoreLogThread *aft = (LogTlsStoreLogThread *)data;
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 */
LogTlsStoreLogDeInitCtx(OutputCtx * output_ctx)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 * */
LogTlsStoreLogInitCtx(ConfNode * conf)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 */
406 AppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_TLS);
407
408 result.ctx = output_ctx;
409 result.ok = true;
410 SCReturnCT(result, "OutputInitResult");
411 }
412
LogTlsStoreRegister(void)413 void LogTlsStoreRegister (void)
414 {
415 OutputRegisterTxModuleWithCondition(LOGGER_TLS_STORE, MODULE_NAME,
416 "tls-store", LogTlsStoreLogInitCtx, ALPROTO_TLS, LogTlsStoreLogger,
417 LogTlsStoreCondition, LogTlsStoreLogThreadInit,
418 LogTlsStoreLogThreadDeinit, LogTlsStoreLogExitPrintStats);
419
420 SC_ATOMIC_INIT(cert_id);
421 SC_ATOMIC_SET(cert_id, 1);
422
423 SCLogDebug("registered");
424 }
425