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