1 /* Copyright (C) 2007-2020 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 Anoop Saldanha <anoopsaldanha@gmail.com>
22  *
23  * Debug utility functions
24  */
25 
26 #include "suricata-common.h"
27 #include "threads.h"
28 #include "util-debug.h"
29 #include "util-error.h"
30 #include "util-enum.h"
31 #include "util-debug-filters.h"
32 
33 #include "decode.h"
34 #include "detect.h"
35 #include "packet-queue.h"
36 #include "threadvars.h"
37 #include "output.h"
38 
39 #include "tm-queuehandlers.h"
40 #include "tm-queues.h"
41 #include "tm-threads.h"
42 
43 #include "util-unittest.h"
44 #include "util-syslog.h"
45 #include "rust.h"
46 
47 
48 #include "conf.h"
49 
50 /* holds the string-enum mapping for the enums held in the table SCLogLevel */
51 SCEnumCharMap sc_log_level_map[ ] = {
52     { "Not set",        SC_LOG_NOTSET},
53     { "None",           SC_LOG_NONE },
54     { "Emergency",      SC_LOG_EMERGENCY },
55     { "Alert",          SC_LOG_ALERT },
56     { "Critical",       SC_LOG_CRITICAL },
57     { "Error",          SC_LOG_ERROR },
58     { "Warning",        SC_LOG_WARNING },
59     { "Notice",         SC_LOG_NOTICE },
60     { "Info",           SC_LOG_INFO },
61     { "Perf",           SC_LOG_PERF },
62     { "Config",         SC_LOG_CONFIG },
63     { "Debug",          SC_LOG_DEBUG },
64     { NULL,             -1 }
65 };
66 
67 /* holds the string-enum mapping for the enums held in the table SCLogOPIface */
68 SCEnumCharMap sc_log_op_iface_map[ ] = {
69     { "Console",        SC_LOG_OP_IFACE_CONSOLE },
70     { "File",           SC_LOG_OP_IFACE_FILE },
71     { "Syslog",         SC_LOG_OP_IFACE_SYSLOG },
72     { NULL,             -1 }
73 };
74 
75 #if defined (OS_WIN32)
76 /**
77  * \brief Used for synchronous output on WIN32
78  */
79 static SCMutex sc_log_stream_lock;
80 #endif /* OS_WIN32 */
81 
82 /**
83  * \brief Holds the config state for the logging module
84  */
85 static SCLogConfig *sc_log_config = NULL;
86 
87 /**
88  * \brief Returns the full path given a file and configured log dir
89  */
90 static char *SCLogGetLogFilename(const char *);
91 
92 /**
93  * \brief Holds the global log level.  Is the same as sc_log_config->log_level
94  */
95 SCLogLevel sc_log_global_log_level;
96 
97 /**
98  * \brief Used to indicate whether the logging module has been init or not
99  */
100 int sc_log_module_initialized = 0;
101 
102 /**
103  * \brief Used to indicate whether the logging module has been cleaned or not
104  */
105 int sc_log_module_cleaned = 0;
106 
107 /**
108  * \brief Maps the SC logging level to the syslog logging level
109  *
110  * \param The SC logging level that has to be mapped to the syslog_log_level
111  *
112  * \retval syslog_log_level The mapped syslog_api_log_level, for the logging
113  *                          module api's internal log_level
114  */
SCLogMapLogLevelToSyslogLevel(int log_level)115 static inline int SCLogMapLogLevelToSyslogLevel(int log_level)
116 {
117     int syslog_log_level = 0;
118 
119     switch (log_level) {
120         case SC_LOG_EMERGENCY:
121             syslog_log_level = LOG_EMERG;
122             break;
123         case SC_LOG_ALERT:
124             syslog_log_level = LOG_ALERT;
125             break;
126         case SC_LOG_CRITICAL:
127             syslog_log_level = LOG_CRIT;
128             break;
129         case SC_LOG_ERROR:
130             syslog_log_level = LOG_ERR;
131             break;
132         case SC_LOG_WARNING:
133             syslog_log_level = LOG_WARNING;
134             break;
135         case SC_LOG_NOTICE:
136             syslog_log_level = LOG_NOTICE;
137             break;
138         case SC_LOG_INFO:
139             syslog_log_level = LOG_INFO;
140             break;
141         case SC_LOG_CONFIG:
142         case SC_LOG_DEBUG:
143         case SC_LOG_PERF:
144             syslog_log_level = LOG_DEBUG;
145             break;
146         default:
147             syslog_log_level = LOG_EMERG;
148             break;
149     }
150 
151     return syslog_log_level;
152 }
153 
154 /**
155  * \brief Output function that logs a character string out to a file descriptor
156  *
157  * \param fd  Pointer to the file descriptor
158  * \param msg Pointer to the character string that should be logged
159  */
SCLogPrintToStream(FILE * fd,char * msg)160 static inline void SCLogPrintToStream(FILE *fd, char *msg)
161 {
162     /* Would only happen if the log file failed to re-open during rotation. */
163     if (fd == NULL) {
164         return;
165     }
166 
167 #if defined (OS_WIN32)
168 	SCMutexLock(&sc_log_stream_lock);
169 #endif /* OS_WIN32 */
170 
171     if (fprintf(fd, "%s\n", msg) < 0)
172         printf("Error writing to stream using fprintf\n");
173 
174     fflush(fd);
175 
176 #if defined (OS_WIN32)
177 	SCMutexUnlock(&sc_log_stream_lock);
178 #endif /* OS_WIN32 */
179 
180     return;
181 }
182 
183 /**
184  * \brief Output function that logs a character string throught the syslog iface
185  *
186  * \param syslog_log_level Holds the syslog_log_level that the message should be
187  *                         logged as
188  * \param msg              Pointer to the char string, that should be logged
189  *
190  * \todo syslog is thread-safe according to POSIX manual and glibc code, but we
191  *       we will have to look into non POSIX compliant boxes like freeBSD
192  */
SCLogPrintToSyslog(int syslog_log_level,const char * msg)193 static inline void SCLogPrintToSyslog(int syslog_log_level, const char *msg)
194 {
195     //static struct syslog_data data = SYSLOG_DATA_INIT;
196     //syslog_r(syslog_log_level, NULL, "%s", msg);
197 
198     syslog(syslog_log_level, "%s", msg);
199 
200     return;
201 }
202 
203 /**
204  */
SCLogMessageJSON(struct timeval * tval,char * buffer,size_t buffer_size,SCLogLevel log_level,const char * file,unsigned line,const char * function,SCError error_code,const char * message)205 static int SCLogMessageJSON(struct timeval *tval, char *buffer, size_t buffer_size,
206         SCLogLevel log_level, const char *file,
207         unsigned line, const char *function, SCError error_code,
208         const char *message)
209 {
210     json_t *js = json_object();
211     if (unlikely(js == NULL))
212         goto error;
213     json_t *ejs = json_object();
214     if (unlikely(ejs == NULL))
215         goto error;
216 
217     char timebuf[64];
218     CreateIsoTimeString(tval, timebuf, sizeof(timebuf));
219     json_object_set_new(js, "timestamp", json_string(timebuf));
220 
221     const char *s = SCMapEnumValueToName(log_level, sc_log_level_map);
222     if (s != NULL) {
223         json_object_set_new(js, "log_level", json_string(s));
224     } else {
225         json_object_set_new(js, "log_level", json_string("INVALID"));
226     }
227 
228     json_object_set_new(js, "event_type", json_string("engine"));
229 
230     if (error_code > 0) {
231         json_object_set_new(ejs, "error_code", json_integer(error_code));
232         json_object_set_new(ejs, "error", json_string(SCErrorToString(error_code)));
233     }
234 
235     if (message)
236         json_object_set_new(ejs, "message", json_string(message));
237 
238     if (log_level >= SC_LOG_DEBUG) {
239         if (function)
240             json_object_set_new(ejs, "function", json_string(function));
241 
242         if (file)
243             json_object_set_new(ejs, "file", json_string(file));
244 
245         if (line > 0)
246             json_object_set_new(ejs, "line", json_integer(line));
247     }
248 
249     json_object_set_new(js, "engine", ejs);
250 
251     char *js_s = json_dumps(js,
252             JSON_PRESERVE_ORDER|JSON_COMPACT|JSON_ENSURE_ASCII|
253             JSON_ESCAPE_SLASH);
254     snprintf(buffer, buffer_size, "%s", js_s);
255     free(js_s);
256 
257     json_object_del(js, "engine");
258     json_object_clear(js);
259     json_decref(js);
260 
261     return 0;
262 error:
263     return -1;
264 }
265 
266 /**
267  * \brief Adds the global log_format to the outgoing buffer
268  *
269  * \param log_level log_level of the message that has to be logged
270  * \param msg       Buffer containing the outgoing message
271  * \param file      File_name from where the message originated
272  * \param function  Function_name from where the message originated
273  * \param line      Line_no from where the messaged originated
274  *
275  * \retval SC_OK on success; else an error code
276  */
SCLogMessageGetBuffer(struct timeval * tval,int color,SCLogOPType type,char * buffer,size_t buffer_size,const char * log_format,const SCLogLevel log_level,const char * file,const unsigned int line,const char * function,const SCError error_code,const char * message)277 static SCError SCLogMessageGetBuffer(
278         struct timeval *tval, int color, SCLogOPType type,
279                      char *buffer, size_t buffer_size,
280                      const char *log_format,
281 
282                      const SCLogLevel log_level, const char *file,
283                      const unsigned int line, const char *function,
284                      const SCError error_code, const char *message)
285 {
286     if (type == SC_LOG_OP_TYPE_JSON)
287         return SCLogMessageJSON(tval, buffer, buffer_size, log_level, file, line, function, error_code, message);
288 
289     char *temp = buffer;
290     const char *s = NULL;
291     struct tm *tms = NULL;
292 
293     const char *redb = "";
294     const char *red = "";
295     const char *yellowb = "";
296     const char *yellow = "";
297     const char *green = "";
298     const char *blue = "";
299     const char *reset = "";
300     if (color) {
301         redb = "\x1b[1;31m";
302         red = "\x1b[31m";
303         yellowb = "\x1b[1;33m";
304         yellow = "\x1b[33m";
305         green = "\x1b[32m";
306         blue = "\x1b[34m";
307         reset = "\x1b[0m";
308     }
309     /* no of characters_written(cw) by snprintf */
310     int cw = 0;
311 
312     BUG_ON(sc_log_module_initialized != 1);
313 
314     /* make a copy of the format string as it will be modified below */
315     char local_format[strlen(log_format) + 1];
316     strlcpy(local_format, log_format, sizeof(local_format));
317     char *temp_fmt = local_format;
318     char *substr = temp_fmt;
319 
320 	while ( (temp_fmt = strchr(temp_fmt, SC_LOG_FMT_PREFIX)) ) {
321         if ((temp - buffer) > SC_LOG_MAX_LOG_MSG_LEN) {
322             return SC_OK;
323         }
324         switch(temp_fmt[1]) {
325             case SC_LOG_FMT_TIME:
326                 temp_fmt[0] = '\0';
327 
328                 struct tm local_tm;
329                 tms = SCLocalTime(tval->tv_sec, &local_tm);
330 
331                 cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
332                               "%s%s%d/%d/%04d -- %02d:%02d:%02d%s",
333                               substr, green, tms->tm_mday, tms->tm_mon + 1,
334                               tms->tm_year + 1900, tms->tm_hour, tms->tm_min,
335                               tms->tm_sec, reset);
336                 if (cw < 0)
337                     return SC_ERR_SPRINTF;
338                 temp += cw;
339                 temp_fmt++;
340                 substr = temp_fmt;
341                 substr++;
342                 break;
343 
344             case SC_LOG_FMT_PID:
345                 temp_fmt[0] = '\0';
346                 cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
347                               "%s%s%u%s", substr, yellow, getpid(), reset);
348                 if (cw < 0)
349                     return SC_ERR_SPRINTF;
350                 temp += cw;
351                 temp_fmt++;
352                 substr = temp_fmt;
353                 substr++;
354                 break;
355 
356             case SC_LOG_FMT_TID:
357                 temp_fmt[0] = '\0';
358                 cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
359                               "%s%s%lu%s", substr, yellow, SCGetThreadIdLong(), reset);
360                 if (cw < 0)
361                     return SC_ERR_SPRINTF;
362                 temp += cw;
363                 temp_fmt++;
364                 substr = temp_fmt;
365                 substr++;
366                 break;
367 
368             case SC_LOG_FMT_TM:
369                 temp_fmt[0] = '\0';
370 /* disabled to prevent dead lock:
371  * log or alloc (which calls log on error) can call TmThreadsGetCallingThread
372  * which will lock tv_root_lock. This can happen while we already hold this
373  * lock. */
374 #if 0
375                 ThreadVars *tv = TmThreadsGetCallingThread();
376                 cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - *msg),
377                               "%s%s", substr, ((tv != NULL)? tv->name: "UNKNOWN TM"));
378 #endif
379                 cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
380                               "%s%s", substr, "N/A");
381                 if (cw < 0)
382                     return SC_ERR_SPRINTF;
383                 temp += cw;
384                 temp_fmt++;
385                 substr = temp_fmt;
386                 substr++;
387                 break;
388 
389             case SC_LOG_FMT_LOG_LEVEL:
390                 temp_fmt[0] = '\0';
391                 s = SCMapEnumValueToName(log_level, sc_log_level_map);
392                 if (s != NULL) {
393                     if (log_level <= SC_LOG_ERROR)
394                         cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
395                                   "%s%s%s%s", substr, redb, s, reset);
396                     else if (log_level == SC_LOG_WARNING)
397                         cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
398                                   "%s%s%s%s", substr, red, s, reset);
399                     else if (log_level == SC_LOG_NOTICE)
400                         cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
401                                   "%s%s%s%s", substr, yellowb, s, reset);
402                     else
403                         cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
404                                   "%s%s%s%s", substr, yellow, s, reset);
405                 } else {
406                     cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
407                                   "%s%s", substr, "INVALID");
408                 }
409                 if (cw < 0)
410                     return SC_ERR_SPRINTF;
411                 temp += cw;
412                 temp_fmt++;
413                 substr = temp_fmt;
414                 substr++;
415                 break;
416 
417             case SC_LOG_FMT_FILE_NAME:
418                 temp_fmt[0] = '\0';
419                 cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
420                               "%s%s%s%s", substr, blue, file, reset);
421                 if (cw < 0)
422                     return SC_ERR_SPRINTF;
423                 temp += cw;
424                 temp_fmt++;
425                 substr = temp_fmt;
426                 substr++;
427                 break;
428 
429             case SC_LOG_FMT_LINE:
430                 temp_fmt[0] = '\0';
431                 cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
432                               "%s%s%u%s", substr, green, line, reset);
433                 if (cw < 0)
434                     return SC_ERR_SPRINTF;
435                 temp += cw;
436                 temp_fmt++;
437                 substr = temp_fmt;
438                 substr++;
439                 break;
440 
441             case SC_LOG_FMT_FUNCTION:
442                 temp_fmt[0] = '\0';
443                 cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
444                               "%s%s%s%s", substr, green, function, reset);
445                 if (cw < 0)
446                     return SC_ERR_SPRINTF;
447                 temp += cw;
448                 temp_fmt++;
449                 substr = temp_fmt;
450                 substr++;
451                 break;
452 
453         }
454         temp_fmt++;
455 	}
456     if ((temp - buffer) > SC_LOG_MAX_LOG_MSG_LEN) {
457         return SC_OK;
458     }
459     cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer), "%s", substr);
460     if (cw < 0) {
461         return SC_ERR_SPRINTF;
462     }
463     temp += cw;
464     if ((temp - buffer) > SC_LOG_MAX_LOG_MSG_LEN) {
465         return SC_OK;
466     }
467 
468     if (error_code != SC_OK) {
469         cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer),
470                 "[%sERRCODE%s: %s%s%s(%s%d%s)] - ", yellow, reset, red, SCErrorToString(error_code), reset, yellow, error_code, reset);
471         if (cw < 0) {
472             return SC_ERR_SPRINTF;
473         }
474         temp += cw;
475         if ((temp - buffer) > SC_LOG_MAX_LOG_MSG_LEN) {
476             return SC_OK;
477         }
478     }
479 
480     const char *hi = "";
481     if (error_code > SC_OK)
482         hi = red;
483     else if (log_level <= SC_LOG_NOTICE)
484         hi = yellow;
485     cw = snprintf(temp, SC_LOG_MAX_LOG_MSG_LEN - (temp - buffer), "%s%s%s", hi, message, reset);
486     if (cw < 0) {
487         return SC_ERR_SPRINTF;
488     }
489     temp += cw;
490     if ((temp - buffer) > SC_LOG_MAX_LOG_MSG_LEN) {
491         return SC_OK;
492     }
493 
494     if (sc_log_config->op_filter_regex != NULL) {
495 #define MAX_SUBSTRINGS 30
496         int ov[MAX_SUBSTRINGS];
497 
498         if (pcre_exec(sc_log_config->op_filter_regex,
499                       sc_log_config->op_filter_regex_study,
500                       buffer, strlen(buffer), 0, 0, ov, MAX_SUBSTRINGS) < 0)
501         {
502             return SC_ERR_LOG_FG_FILTER_MATCH; // bit hacky, but just return !0
503         }
504 #undef MAX_SUBSTRINGS
505     }
506 
507     return SC_OK;
508 }
509 
510 /** \internal
511  *  \brief try to reopen file
512  *  \note no error reporting here, as we're called by SCLogMessage
513  *  \retval status 0 ok, -1 error */
SCLogReopen(SCLogOPIfaceCtx * op_iface_ctx)514 static int SCLogReopen(SCLogOPIfaceCtx *op_iface_ctx)
515 {
516     if (op_iface_ctx->file == NULL) {
517         return 0;
518     }
519 
520     if (op_iface_ctx->file_d != NULL) {
521         fclose(op_iface_ctx->file_d);
522     }
523     op_iface_ctx->file_d = fopen(op_iface_ctx->file, "a");
524     if (op_iface_ctx->file_d == NULL) {
525         return -1;
526     }
527     return 0;
528 }
529 
530 /**
531  * \brief Adds the global log_format to the outgoing buffer
532  *
533  * \param log_level log_level of the message that has to be logged
534  * \param msg       Buffer containing the outgoing message
535  * \param file      File_name from where the message originated
536  * \param function  Function_name from where the message originated
537  * \param line      Line_no from where the messaged originated
538  *
539  * \retval SC_OK on success; else an error code
540  */
SCLogMessage(const SCLogLevel log_level,const char * file,const unsigned int line,const char * function,const SCError error_code,const char * message)541 SCError SCLogMessage(const SCLogLevel log_level, const char *file,
542                      const unsigned int line, const char *function,
543                      const SCError error_code, const char *message)
544 {
545     char buffer[SC_LOG_MAX_LOG_MSG_LEN] = "";
546     SCLogOPIfaceCtx *op_iface_ctx = NULL;
547 
548     if (sc_log_module_initialized != 1) {
549         printf("Logging module not initialized.  Call SCLogInitLogModule() "
550                "first before using the debug API\n");
551         return SC_OK;
552     }
553 
554     /* get ts here so we log the same ts to each output */
555     struct timeval tval;
556     gettimeofday(&tval, NULL);
557 
558     op_iface_ctx = sc_log_config->op_ifaces;
559     while (op_iface_ctx != NULL) {
560         if (log_level != SC_LOG_NOTSET && log_level > op_iface_ctx->log_level) {
561             op_iface_ctx = op_iface_ctx->next;
562             continue;
563         }
564 
565         switch (op_iface_ctx->iface) {
566             case SC_LOG_OP_IFACE_CONSOLE:
567                 if (SCLogMessageGetBuffer(&tval, op_iface_ctx->use_color, op_iface_ctx->type,
568                                           buffer, sizeof(buffer),
569                                           op_iface_ctx->log_format ?
570                                               op_iface_ctx->log_format : sc_log_config->log_format,
571                                           log_level, file, line, function,
572                                           error_code, message) == 0)
573                 {
574                     SCLogPrintToStream((log_level == SC_LOG_ERROR)? stderr: stdout, buffer);
575                 }
576                 break;
577             case SC_LOG_OP_IFACE_FILE:
578                 if (SCLogMessageGetBuffer(&tval, 0, op_iface_ctx->type, buffer, sizeof(buffer),
579                                           op_iface_ctx->log_format ?
580                                               op_iface_ctx->log_format : sc_log_config->log_format,
581                                           log_level, file, line, function,
582                                           error_code, message) == 0)
583                 {
584                     int r = 0;
585                     SCMutexLock(&op_iface_ctx->fp_mutex);
586                     if (op_iface_ctx->rotation_flag) {
587                         r = SCLogReopen(op_iface_ctx);
588                         op_iface_ctx->rotation_flag = 0;
589                     }
590                     SCLogPrintToStream(op_iface_ctx->file_d, buffer);
591                     SCMutexUnlock(&op_iface_ctx->fp_mutex);
592 
593                     /* report error outside of lock to avoid recursion */
594                     if (r == -1) {
595                         SCLogError(SC_ERR_FOPEN, "re-opening file \"%s\" failed: %s",
596                                 op_iface_ctx->file, strerror(errno));
597                     }
598                 }
599                 break;
600             case SC_LOG_OP_IFACE_SYSLOG:
601                 if (SCLogMessageGetBuffer(&tval, 0, op_iface_ctx->type, buffer, sizeof(buffer),
602                                           op_iface_ctx->log_format ?
603                                               op_iface_ctx->log_format : sc_log_config->log_format,
604                                           log_level, file, line, function,
605                                           error_code, message) == 0)
606                 {
607                     SCLogPrintToSyslog(SCLogMapLogLevelToSyslogLevel(log_level), buffer);
608                 }
609                 break;
610             default:
611                 break;
612         }
613         op_iface_ctx = op_iface_ctx->next;
614     }
615     return SC_OK;
616 }
617 
SCLog(int x,const char * file,const char * func,const int line,const char * fmt,...)618 void SCLog(int x, const char *file, const char *func, const int line,
619         const char *fmt, ...)
620 {
621     if (sc_log_global_log_level >= x &&
622             (sc_log_fg_filters_present == 0 ||
623              SCLogMatchFGFilterWL(file, func, line) == 1 ||
624              SCLogMatchFGFilterBL(file, func, line) == 1) &&
625             (sc_log_fd_filters_present == 0 ||
626              SCLogMatchFDFilter(func) == 1))
627     {
628         char msg[SC_LOG_MAX_LOG_MSG_LEN];
629         va_list ap;
630         va_start(ap, fmt);
631         vsnprintf(msg, sizeof(msg), fmt, ap);
632         va_end(ap);
633         SCLogMessage(x, file, line, func, SC_OK, msg);
634     }
635 }
636 
SCLogErr(int x,const char * file,const char * func,const int line,const int err,const char * fmt,...)637 void SCLogErr(int x, const char *file, const char *func, const int line,
638         const int err, const char *fmt, ...)
639 {
640     if (sc_log_global_log_level >= x &&
641             (sc_log_fg_filters_present == 0 ||
642              SCLogMatchFGFilterWL(file, func, line) == 1 ||
643              SCLogMatchFGFilterBL(file, func, line) == 1) &&
644             (sc_log_fd_filters_present == 0 ||
645              SCLogMatchFDFilter(func) == 1))
646     {
647         char msg[SC_LOG_MAX_LOG_MSG_LEN];
648         va_list ap;
649         va_start(ap, fmt);
650         vsnprintf(msg, sizeof(msg), fmt, ap);
651         va_end(ap);
652         SCLogMessage(x, file, line, func, err, msg);
653     }
654 }
655 
656 /**
657  * \brief Returns whether debug messages are enabled to be logged or not
658  *
659  * \retval 1 if debug messages are enabled to be logged
660  * \retval 0 if debug messages are not enabled to be logged
661  */
SCLogDebugEnabled(void)662 int SCLogDebugEnabled(void)
663 {
664 #ifdef DEBUG
665     if (sc_log_global_log_level == SC_LOG_DEBUG)
666         return 1;
667     else
668         return 0;
669 #else
670     return 0;
671 #endif
672 }
673 
674 /**
675  * \brief Allocates an output buffer for an output interface.  Used when we
676  *        want the op_interface log_format to override the global_log_format.
677  *        Currently not used.
678  *
679  * \retval buffer Pointer to the newly created output_buffer
680  */
SCLogAllocLogOPBuffer(void)681 SCLogOPBuffer *SCLogAllocLogOPBuffer(void)
682 {
683     SCLogOPBuffer *buffer = NULL;
684     SCLogOPIfaceCtx *op_iface_ctx = NULL;
685     int i = 0;
686 
687     if ( (buffer = SCMalloc(sc_log_config->op_ifaces_cnt *
688                           sizeof(SCLogOPBuffer))) == NULL) {
689         FatalError(SC_ERR_FATAL,
690                    "Fatal error encountered in SCLogAllocLogOPBuffer. Exiting...");
691     }
692 
693     op_iface_ctx = sc_log_config->op_ifaces;
694     for (i = 0;
695          i < sc_log_config->op_ifaces_cnt;
696          i++, op_iface_ctx = op_iface_ctx->next) {
697         buffer[i].log_format = op_iface_ctx->log_format;
698         buffer[i].temp = buffer[i].msg;
699     }
700 
701     return buffer;
702 }
703 
704 /*----------------------The logging module initialization code--------------- */
705 
706 /**
707  * \brief Returns a new output_interface_context
708  *
709  * \retval iface_ctx Pointer to a newly allocated output_interface_context
710  * \initonly
711  */
SCLogAllocLogOPIfaceCtx(void)712 static inline SCLogOPIfaceCtx *SCLogAllocLogOPIfaceCtx(void)
713 {
714     SCLogOPIfaceCtx *iface_ctx = NULL;
715 
716     if ( (iface_ctx = SCMalloc(sizeof(SCLogOPIfaceCtx))) == NULL) {
717         FatalError(SC_ERR_FATAL,
718                    "Fatal error encountered in SCLogallocLogOPIfaceCtx. Exiting...");
719     }
720     memset(iface_ctx, 0, sizeof(SCLogOPIfaceCtx));
721 
722     return iface_ctx;
723 }
724 
725 /**
726  * \brief Initializes the file output interface
727  *
728  * \param file       Path to the file used for logging purposes
729  * \param log_format Pointer to the log_format for this op interface, that
730  *                   overrides the global_log_format
731  * \param log_level  Override of the global_log_level by this interface
732  *
733  * \retval iface_ctx Pointer to the file output interface context created
734  * \initonly
735  */
SCLogInitFileOPIface(const char * file,const char * log_format,int log_level,SCLogOPType type)736 static inline SCLogOPIfaceCtx *SCLogInitFileOPIface(const char *file,
737                                                     const char *log_format,
738                                                     int log_level,
739                                                     SCLogOPType type)
740 {
741     SCLogOPIfaceCtx *iface_ctx = SCLogAllocLogOPIfaceCtx();
742 
743     if (iface_ctx == NULL) {
744         FatalError(SC_ERR_FATAL,
745                    "Fatal error encountered in SCLogInitFileOPIface. Exiting...");
746     }
747 
748     if (file == NULL) {
749         goto error;
750     }
751 
752     iface_ctx->iface = SC_LOG_OP_IFACE_FILE;
753     iface_ctx->type = type;
754 
755     if ( (iface_ctx->file_d = fopen(file, "a")) == NULL) {
756         printf("Error opening file %s\n", file);
757         goto error;
758     }
759 
760     if ((iface_ctx->file = SCStrdup(file)) == NULL) {
761         goto error;
762     }
763 
764     if (log_format != NULL && (iface_ctx->log_format = SCStrdup(log_format)) == NULL) {
765         goto error;
766     }
767 
768     SCMutexInit(&iface_ctx->fp_mutex, NULL);
769     OutputRegisterFileRotationFlag(&iface_ctx->rotation_flag);
770 
771     iface_ctx->log_level = log_level;
772 
773     return iface_ctx;
774 
775 error:
776     if (iface_ctx->file != NULL) {
777         SCFree((char *)iface_ctx->file);
778         iface_ctx->file = NULL;
779     }
780     if (iface_ctx->log_format != NULL) {
781         SCFree((char *)iface_ctx->log_format);
782         iface_ctx->log_format = NULL;
783     }
784     if (iface_ctx->file_d != NULL) {
785         fclose(iface_ctx->file_d);
786         iface_ctx->file_d = NULL;
787     }
788     SCFree(iface_ctx);
789     return NULL;
790 }
791 
792 /**
793  * \brief Initializes the console output interface and deals with possible
794  *        env var overrides.
795  *
796  * \param log_format Pointer to the log_format for this op interface, that
797  *                   overrides the global_log_format
798  * \param log_level  Override of the global_log_level by this interface
799  *
800  * \retval iface_ctx Pointer to the console output interface context created
801  * \initonly
802  */
SCLogInitConsoleOPIface(const char * log_format,SCLogLevel log_level,SCLogOPType type)803 static inline SCLogOPIfaceCtx *SCLogInitConsoleOPIface(const char *log_format,
804                                                        SCLogLevel log_level, SCLogOPType type)
805 {
806     SCLogOPIfaceCtx *iface_ctx = SCLogAllocLogOPIfaceCtx();
807 
808     if (iface_ctx == NULL) {
809         FatalError(SC_ERR_FATAL,
810                    "Fatal error encountered in SCLogInitConsoleOPIface. Exiting...");
811     }
812 
813     iface_ctx->iface = SC_LOG_OP_IFACE_CONSOLE;
814     iface_ctx->type = type;
815 
816     /* console log format is overridden by envvars */
817     const char *tmp_log_format = log_format;
818     const char *s = getenv(SC_LOG_ENV_LOG_FORMAT);
819     if (s != NULL) {
820 #if 0
821         printf("Overriding setting for \"console.format\" because of env "
822                 "var SC_LOG_FORMAT=\"%s\".\n", s);
823 #endif
824         tmp_log_format = s;
825     }
826 
827     if (tmp_log_format != NULL &&
828         (iface_ctx->log_format = SCStrdup(tmp_log_format)) == NULL) {
829         printf("Error allocating memory\n");
830         exit(EXIT_FAILURE);
831     }
832 
833     /* console log level is overridden by envvars */
834     SCLogLevel tmp_log_level = log_level;
835     s = getenv(SC_LOG_ENV_LOG_LEVEL);
836     if (s != NULL) {
837         SCLogLevel l = SCMapEnumNameToValue(s, sc_log_level_map);
838         if (l > SC_LOG_NOTSET && l < SC_LOG_LEVEL_MAX) {
839 #if 0
840             printf("Overriding setting for \"console.level\" because of env "
841                     "var SC_LOG_LEVEL=\"%s\".\n", s);
842 #endif
843             tmp_log_level = l;
844         }
845     }
846     iface_ctx->log_level = tmp_log_level;
847 
848 #ifndef OS_WIN32
849     if (isatty(fileno(stdout)) && isatty(fileno(stderr))) {
850         iface_ctx->use_color = TRUE;
851     }
852 #endif
853 
854     return iface_ctx;
855 }
856 
857 /**
858  * \brief Initializes the syslog output interface
859  *
860  * \param facility   The facility code for syslog
861  * \param log_format Pointer to the log_format for this op interface, that
862  *                   overrides the global_log_format
863  * \param log_level  Override of the global_log_level by this interface
864  *
865  * \retval iface_ctx Pointer to the syslog output interface context created
866  */
SCLogInitSyslogOPIface(int facility,const char * log_format,SCLogLevel log_level,SCLogOPType type)867 static inline SCLogOPIfaceCtx *SCLogInitSyslogOPIface(int facility,
868                                                       const char *log_format,
869                                                       SCLogLevel log_level,
870                                                       SCLogOPType type)
871 {
872     SCLogOPIfaceCtx *iface_ctx = SCLogAllocLogOPIfaceCtx();
873 
874     if ( iface_ctx == NULL) {
875         FatalError(SC_ERR_FATAL,
876                    "Fatal error encountered in SCLogInitSyslogOPIface. Exiting...");
877     }
878 
879     iface_ctx->iface = SC_LOG_OP_IFACE_SYSLOG;
880     iface_ctx->type = type;
881 
882     if (facility == -1)
883         facility = SC_LOG_DEF_SYSLOG_FACILITY;
884     iface_ctx->facility = facility;
885 
886     if (log_format != NULL &&
887         (iface_ctx->log_format = SCStrdup(log_format)) == NULL) {
888         printf("Error allocating memory\n");
889         exit(EXIT_FAILURE);
890     }
891 
892     iface_ctx->log_level = log_level;
893 
894     openlog(NULL, LOG_NDELAY, iface_ctx->facility);
895 
896     return iface_ctx;
897 }
898 
899 /**
900  * \brief Frees the output_interface context supplied as an argument
901  *
902  * \param iface_ctx Pointer to the op_interface_context to be freed
903  */
SCLogFreeLogOPIfaceCtx(SCLogOPIfaceCtx * iface_ctx)904 static inline void SCLogFreeLogOPIfaceCtx(SCLogOPIfaceCtx *iface_ctx)
905 {
906     SCLogOPIfaceCtx *temp = NULL;
907 
908     while (iface_ctx != NULL) {
909         temp = iface_ctx;
910 
911         if (iface_ctx->file_d != NULL) {
912             fclose(iface_ctx->file_d);
913             SCMutexDestroy(&iface_ctx->fp_mutex);
914         }
915 
916         if (iface_ctx->file != NULL)
917             SCFree((void *)iface_ctx->file);
918 
919         if (iface_ctx->log_format != NULL)
920             SCFree((void *)iface_ctx->log_format);
921 
922         if (iface_ctx->iface == SC_LOG_OP_IFACE_SYSLOG) {
923             closelog();
924         }
925 
926         iface_ctx = iface_ctx->next;
927 
928         SCFree(temp);
929     }
930 
931     return;
932 }
933 
934 /**
935  * \brief Internal function used to set the logging module global_log_level
936  *        during the initialization phase
937  *
938  * \param sc_lid The initialization data supplied.
939  * \param sc_lc  The logging module context which has to be updated.
940  */
SCLogSetLogLevel(SCLogInitData * sc_lid,SCLogConfig * sc_lc)941 static inline void SCLogSetLogLevel(SCLogInitData *sc_lid, SCLogConfig *sc_lc)
942 {
943     SCLogLevel log_level = SC_LOG_NOTSET;
944     const char *s = NULL;
945 
946     /* envvar overrides config */
947     s = getenv(SC_LOG_ENV_LOG_LEVEL);
948     if (s != NULL) {
949         log_level = SCMapEnumNameToValue(s, sc_log_level_map);
950     } else if (sc_lid != NULL) {
951         log_level = sc_lid->global_log_level;
952     }
953 
954     /* deal with the global_log_level to be used */
955     if (log_level > SC_LOG_NOTSET && log_level < SC_LOG_LEVEL_MAX)
956         sc_lc->log_level = log_level;
957     else {
958         sc_lc->log_level = SC_LOG_DEF_LOG_LEVEL;
959 #ifndef UNITTESTS
960         if (sc_lid != NULL) {
961             printf("Warning: Invalid/No global_log_level assigned by user.  Falling "
962                    "back on the default_log_level \"%s\"\n",
963                    SCMapEnumValueToName(sc_lc->log_level, sc_log_level_map));
964         }
965 #endif
966     }
967 
968     /* we also set it to a global var, as it is easier to access it */
969     sc_log_global_log_level = sc_lc->log_level;
970 
971     return;
972 }
973 
SCLogGetLogLevel(void)974 SCLogLevel SCLogGetLogLevel(void)
975 {
976     return sc_log_global_log_level;
977 }
978 
SCLogGetDefaultLogFormat(void)979 static inline const char *SCLogGetDefaultLogFormat(void)
980 {
981     const char *prog_ver = GetProgramVersion();
982     if (strstr(prog_ver, "RELEASE") != NULL) {
983         return SC_LOG_DEF_LOG_FORMAT_REL;
984     }
985     return SC_LOG_DEF_LOG_FORMAT_DEV;
986 }
987 
988 /**
989  * \brief Internal function used to set the logging module global_log_format
990  *        during the initialization phase
991  *
992  * \param sc_lid The initialization data supplied.
993  * \param sc_lc  The logging module context which has to be updated.
994  */
SCLogSetLogFormat(SCLogInitData * sc_lid,SCLogConfig * sc_lc)995 static inline void SCLogSetLogFormat(SCLogInitData *sc_lid, SCLogConfig *sc_lc)
996 {
997     const char *format = NULL;
998 
999     /* envvar overrides config */
1000     format = getenv(SC_LOG_ENV_LOG_FORMAT);
1001     if (format == NULL) {
1002         if (sc_lid != NULL) {
1003             format = sc_lid->global_log_format;
1004         }
1005     }
1006 
1007     /* deal with the global log format to be used */
1008     if (format == NULL || strlen(format) > SC_LOG_MAX_LOG_FORMAT_LEN) {
1009         format = SCLogGetDefaultLogFormat();
1010 #ifndef UNITTESTS
1011         if (sc_lid != NULL) {
1012             printf("Warning: Invalid/No global_log_format supplied by user or format "
1013                    "length exceeded limit of \"%d\" characters.  Falling back on "
1014                    "default log_format \"%s\"\n", SC_LOG_MAX_LOG_FORMAT_LEN,
1015                    format);
1016         }
1017 #endif
1018     }
1019 
1020     if (format != NULL && (sc_lc->log_format = SCStrdup(format)) == NULL) {
1021         printf("Error allocating memory\n");
1022         exit(EXIT_FAILURE);
1023     }
1024 
1025     return;
1026 }
1027 
1028 /**
1029  * \brief Internal function used to set the logging module global_op_ifaces
1030  *        during the initialization phase
1031  *
1032  * \param sc_lid The initialization data supplied.
1033  * \param sc_lc  The logging module context which has to be updated.
1034  */
SCLogSetOPIface(SCLogInitData * sc_lid,SCLogConfig * sc_lc)1035 static inline void SCLogSetOPIface(SCLogInitData *sc_lid, SCLogConfig *sc_lc)
1036 {
1037     SCLogOPIfaceCtx *op_ifaces_ctx = NULL;
1038     int op_iface = 0;
1039     const char *s = NULL;
1040 
1041     if (sc_lid != NULL && sc_lid->op_ifaces != NULL) {
1042         sc_lc->op_ifaces = sc_lid->op_ifaces;
1043         sc_lid->op_ifaces = NULL;
1044         sc_lc->op_ifaces_cnt = sc_lid->op_ifaces_cnt;
1045     } else {
1046         s = getenv(SC_LOG_ENV_LOG_OP_IFACE);
1047         if (s != NULL) {
1048             op_iface = SCMapEnumNameToValue(s, sc_log_op_iface_map);
1049 
1050             if(op_iface < 0 || op_iface >= SC_LOG_OP_IFACE_MAX) {
1051                 op_iface = SC_LOG_DEF_LOG_OP_IFACE;
1052 #ifndef UNITTESTS
1053                 printf("Warning: Invalid output interface supplied by user.  "
1054                        "Falling back on default_output_interface \"%s\"\n",
1055                        SCMapEnumValueToName(op_iface, sc_log_op_iface_map));
1056 #endif
1057             }
1058         }
1059         else {
1060             op_iface = SC_LOG_DEF_LOG_OP_IFACE;
1061 #ifndef UNITTESTS
1062             if (sc_lid != NULL) {
1063                 printf("Warning: Output_interface not supplied by user.  Falling "
1064                        "back on default_output_interface \"%s\"\n",
1065                        SCMapEnumValueToName(op_iface, sc_log_op_iface_map));
1066             }
1067 #endif
1068         }
1069 
1070         switch (op_iface) {
1071             case SC_LOG_OP_IFACE_CONSOLE:
1072                 op_ifaces_ctx = SCLogInitConsoleOPIface(NULL, SC_LOG_LEVEL_MAX,0);
1073                 break;
1074             case SC_LOG_OP_IFACE_FILE:
1075                 s = getenv(SC_LOG_ENV_LOG_FILE);
1076                 if (s == NULL) {
1077                     char *str = SCLogGetLogFilename(SC_LOG_DEF_LOG_FILE);
1078                     if (str != NULL) {
1079                         op_ifaces_ctx = SCLogInitFileOPIface(str, NULL, SC_LOG_LEVEL_MAX,0);
1080                         SCFree(str);
1081                     }
1082                 } else {
1083                     op_ifaces_ctx = SCLogInitFileOPIface(s, NULL, SC_LOG_LEVEL_MAX,0);
1084                 }
1085                 break;
1086             case SC_LOG_OP_IFACE_SYSLOG:
1087                 s = getenv(SC_LOG_ENV_LOG_FACILITY);
1088                 if (s == NULL)
1089                     s = SC_LOG_DEF_SYSLOG_FACILITY_STR;
1090 
1091                 op_ifaces_ctx = SCLogInitSyslogOPIface(SCMapEnumNameToValue(s, SCSyslogGetFacilityMap()), NULL, -1,0);
1092                 break;
1093         }
1094         sc_lc->op_ifaces = op_ifaces_ctx;
1095         sc_lc->op_ifaces_cnt++;
1096     }
1097     return;
1098 }
1099 
1100 /**
1101  * \brief Internal function used to set the logging module op_filter
1102  *        during the initialization phase
1103  *
1104  * \param sc_lid The initialization data supplied.
1105  * \param sc_lc  The logging module context which has to be updated.
1106  */
SCLogSetOPFilter(SCLogInitData * sc_lid,SCLogConfig * sc_lc)1107 static inline void SCLogSetOPFilter(SCLogInitData *sc_lid, SCLogConfig *sc_lc)
1108 {
1109     const char *filter = NULL;
1110 
1111     int opts = 0;
1112     const char *ep;
1113     int eo = 0;
1114 
1115     /* envvar overrides */
1116     filter = getenv(SC_LOG_ENV_LOG_OP_FILTER);
1117     if (filter == NULL) {
1118         if (sc_lid != NULL) {
1119             filter = sc_lid->op_filter;
1120         }
1121     }
1122 
1123     if (filter != NULL && strcmp(filter, "") != 0) {
1124         sc_lc->op_filter = SCStrdup(filter);
1125         if (sc_lc->op_filter == NULL) {
1126             printf("pcre filter alloc failed\n");
1127             return;
1128         }
1129         sc_lc->op_filter_regex = pcre_compile(filter, opts, &ep, &eo, NULL);
1130         if (sc_lc->op_filter_regex == NULL) {
1131             SCFree(sc_lc->op_filter);
1132             printf("pcre compile of \"%s\" failed at offset %d : %s\n", filter,
1133                    eo, ep);
1134             return;
1135         }
1136 
1137         sc_lc->op_filter_regex_study = pcre_study(sc_lc->op_filter_regex, 0,
1138                                                   &ep);
1139         if (ep != NULL) {
1140             printf("pcre study failed: %s\n", ep);
1141             return;
1142         }
1143     }
1144 
1145     return;
1146 }
1147 
1148 /**
1149  * \brief Returns a pointer to a new SCLogInitData.  This is a public interface
1150  *        intended to be used after the logging paramters are read from the
1151  *        conf file
1152  *
1153  * \retval sc_lid Pointer to the newly created SCLogInitData
1154  * \initonly
1155  */
SCLogAllocLogInitData(void)1156 SCLogInitData *SCLogAllocLogInitData(void)
1157 {
1158     SCLogInitData *sc_lid = NULL;
1159 
1160     /* not using SCMalloc here because if it fails we can't log */
1161     if ( (sc_lid = SCMalloc(sizeof(SCLogInitData))) == NULL)
1162         return NULL;
1163 
1164     memset(sc_lid, 0, sizeof(SCLogInitData));
1165 
1166     return sc_lid;
1167 }
1168 
1169 #ifdef UNITTESTS
1170 #ifndef OS_WIN32
1171 /**
1172  * \brief Frees a SCLogInitData
1173  *
1174  * \param sc_lid Pointer to the SCLogInitData to be freed
1175  */
SCLogFreeLogInitData(SCLogInitData * sc_lid)1176 static void SCLogFreeLogInitData(SCLogInitData *sc_lid)
1177 {
1178     if (sc_lid != NULL) {
1179         SCLogFreeLogOPIfaceCtx(sc_lid->op_ifaces);
1180         SCFree(sc_lid);
1181     }
1182 
1183     return;
1184 }
1185 #endif
1186 #endif
1187 
1188 /**
1189  * \brief Frees the logging module context
1190  */
SCLogFreeLogConfig(SCLogConfig * sc_lc)1191 static inline void SCLogFreeLogConfig(SCLogConfig *sc_lc)
1192 {
1193     if (sc_lc != NULL) {
1194         if (sc_lc->startup_message != NULL)
1195             SCFree(sc_lc->startup_message);
1196         if (sc_lc->log_format != NULL)
1197             SCFree(sc_lc->log_format);
1198         if (sc_lc->op_filter != NULL)
1199             SCFree(sc_lc->op_filter);
1200 
1201         if (sc_lc->op_filter_regex != NULL)
1202             pcre_free(sc_lc->op_filter_regex);
1203         if (sc_lc->op_filter_regex_study)
1204             pcre_free_study(sc_lc->op_filter_regex_study);
1205 
1206         SCLogFreeLogOPIfaceCtx(sc_lc->op_ifaces);
1207         SCFree(sc_lc);
1208     }
1209 
1210     return;
1211 }
1212 
1213 /**
1214  * \brief Appends an output_interface to the output_interface list sent in head
1215  *
1216  * \param iface_ctx Pointer to the output_interface that has to be added to head
1217  * \param head      Pointer to the output_interface list
1218  */
SCLogAppendOPIfaceCtx(SCLogOPIfaceCtx * iface_ctx,SCLogInitData * sc_lid)1219 void SCLogAppendOPIfaceCtx(SCLogOPIfaceCtx *iface_ctx, SCLogInitData *sc_lid)
1220 {
1221     SCLogOPIfaceCtx *temp = NULL, *prev = NULL;
1222     SCLogOPIfaceCtx **head = &sc_lid->op_ifaces;
1223 
1224     if (iface_ctx == NULL) {
1225 #ifdef DEBUG
1226         printf("Argument(s) to SCLogAppendOPIfaceCtx() NULL\n");
1227 #endif
1228         return;
1229     }
1230 
1231     temp = *head;
1232     while (temp != NULL) {
1233         prev = temp;
1234         temp = temp->next;
1235     }
1236 
1237     if (prev == NULL)
1238         *head = iface_ctx;
1239     else
1240         prev->next = iface_ctx;
1241 
1242     sc_lid->op_ifaces_cnt++;
1243 
1244     return;
1245 }
1246 
1247 
1248 /**
1249  * \brief Creates a new output interface based on the arguments sent.  The kind
1250  *        of output interface to be created is decided by the iface_name arg.
1251  *        If iface_name is "file", the arg argument will hold the filename to be
1252  *        used for logging purposes.  If iface_name is "syslog", the arg
1253  *        argument holds the facility code.  If iface_name is "console", arg is
1254  *        NULL.
1255  *
1256  * \param iface_name Interface name.  Can be "console", "file" or "syslog"
1257  * \param log_format Override for the global_log_format
1258  * \param log_level  Override for the global_log_level
1259  * \param log_level  Parameter required by a particular interface.  Explained in
1260  *                   the function description
1261  *
1262  * \retval iface_ctx Pointer to the newly created output interface
1263  */
SCLogInitOPIfaceCtx(const char * iface_name,const char * log_format,int log_level,const char * arg)1264 SCLogOPIfaceCtx *SCLogInitOPIfaceCtx(const char *iface_name,
1265                                      const char *log_format,
1266                                      int log_level, const char *arg)
1267 {
1268     int iface = SCMapEnumNameToValue(iface_name, sc_log_op_iface_map);
1269 
1270     if (log_level < SC_LOG_NONE || log_level > SC_LOG_DEBUG) {
1271 #ifndef UNITTESTS
1272         printf("Warning: Supplied log_level_override for op_interface \"%s\" "
1273                "is invalid.  Defaulting to not specifying an override\n",
1274                iface_name);
1275 #endif
1276         log_level = SC_LOG_NOTSET;
1277     }
1278 
1279     switch (iface) {
1280         case SC_LOG_OP_IFACE_CONSOLE:
1281             return SCLogInitConsoleOPIface(log_format, log_level, SC_LOG_OP_TYPE_REGULAR);
1282         case SC_LOG_OP_IFACE_FILE:
1283             return SCLogInitFileOPIface(arg, log_format, log_level, SC_LOG_OP_TYPE_REGULAR);
1284         case SC_LOG_OP_IFACE_SYSLOG:
1285             return SCLogInitSyslogOPIface(SCMapEnumNameToValue(arg, SCSyslogGetFacilityMap()),
1286                     log_format, log_level, SC_LOG_OP_TYPE_REGULAR);
1287         default:
1288 #ifdef DEBUG
1289             printf("Output Interface \"%s\" not supported by the logging module",
1290                    iface_name);
1291 #endif
1292             return NULL;
1293     }
1294 }
1295 
1296 /**
1297  * \brief Initializes the logging module.
1298  *
1299  * \param sc_lid The initialization data for the logging module.  If sc_lid is
1300  *               NULL, we would stick to the default configuration for the
1301  *               logging subsystem.
1302  * \initonly
1303  */
SCLogInitLogModule(SCLogInitData * sc_lid)1304 void SCLogInitLogModule(SCLogInitData *sc_lid)
1305 {
1306     /* De-initialize the logging context, if it has already init by the
1307      * environment variables at the start of the engine */
1308     SCLogDeInitLogModule();
1309 
1310 #if defined (OS_WIN32)
1311     if (SCMutexInit(&sc_log_stream_lock, NULL) != 0) {
1312         FatalError(SC_ERR_FATAL, "Failed to initialize log mutex.");
1313     }
1314 #endif /* OS_WIN32 */
1315 
1316     /* sc_log_config is a global variable */
1317     if ( (sc_log_config = SCMalloc(sizeof(SCLogConfig))) == NULL) {
1318         FatalError(SC_ERR_FATAL,
1319                    "Fatal error encountered in SCLogInitLogModule. Exiting...");
1320     }
1321     memset(sc_log_config, 0, sizeof(SCLogConfig));
1322 
1323     SCLogSetLogLevel(sc_lid, sc_log_config);
1324     SCLogSetLogFormat(sc_lid, sc_log_config);
1325     SCLogSetOPIface(sc_lid, sc_log_config);
1326     SCLogSetOPFilter(sc_lid, sc_log_config);
1327 
1328     sc_log_module_initialized = 1;
1329     sc_log_module_cleaned = 0;
1330 
1331     //SCOutputPrint(sc_did->startup_message);
1332 
1333     rs_log_set_level(sc_log_global_log_level);
1334     return;
1335 }
1336 
SCLogLoadConfig(int daemon,int verbose)1337 void SCLogLoadConfig(int daemon, int verbose)
1338 {
1339     ConfNode *outputs;
1340     SCLogInitData *sc_lid;
1341     int have_logging = 0;
1342     int max_level = 0;
1343     SCLogLevel min_level = 0;
1344 
1345     /* If verbose logging was requested, set the minimum as
1346      * SC_LOG_NOTICE plus the extra verbosity. */
1347     if (verbose) {
1348         min_level = SC_LOG_NOTICE + verbose;
1349     }
1350 
1351     outputs = ConfGetNode("logging.outputs");
1352     if (outputs == NULL) {
1353         SCLogDebug("No logging.output configuration section found.");
1354         return;
1355     }
1356 
1357     sc_lid = SCLogAllocLogInitData();
1358     if (sc_lid == NULL) {
1359         SCLogDebug("Could not allocate memory for log init data");
1360         return;
1361     }
1362 
1363     /* Get default log level and format. */
1364     const char *default_log_level_s = NULL;
1365     if (ConfGet("logging.default-log-level", &default_log_level_s) == 1) {
1366         SCLogLevel default_log_level =
1367             SCMapEnumNameToValue(default_log_level_s, sc_log_level_map);
1368         if (default_log_level == -1) {
1369             SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid default log level: %s",
1370                 default_log_level_s);
1371             exit(EXIT_FAILURE);
1372         }
1373         sc_lid->global_log_level = MAX(min_level, default_log_level);
1374     }
1375     else {
1376         sc_lid->global_log_level = MAX(min_level, SC_LOG_NOTICE);
1377     }
1378 
1379     if (ConfGet("logging.default-log-format", &sc_lid->global_log_format) != 1)
1380         sc_lid->global_log_format = SCLogGetDefaultLogFormat();
1381 
1382     (void)ConfGet("logging.default-output-filter", &sc_lid->op_filter);
1383 
1384     ConfNode *seq_node, *output;
1385     TAILQ_FOREACH(seq_node, &outputs->head, next) {
1386         SCLogLevel level = sc_lid->global_log_level;
1387         SCLogOPIfaceCtx *op_iface_ctx = NULL;
1388         const char *format;
1389         const char *level_s;
1390 
1391         output = ConfNodeLookupChild(seq_node, seq_node->val);
1392         if (output == NULL)
1393             continue;
1394 
1395         /* By default an output is enabled. */
1396         const char *enabled = ConfNodeLookupChildValue(output, "enabled");
1397         if (enabled != NULL && ConfValIsFalse(enabled))
1398             continue;
1399 
1400         SCLogOPType type = SC_LOG_OP_TYPE_REGULAR;
1401         const char *type_s = ConfNodeLookupChildValue(output, "type");
1402         if (type_s != NULL) {
1403             if (strcmp(type_s, "regular") == 0)
1404                 type = SC_LOG_OP_TYPE_REGULAR;
1405             else if (strcmp(type_s, "json") == 0) {
1406                 type = SC_LOG_OP_TYPE_JSON;
1407             }
1408         }
1409 
1410         /* if available use the log format setting for this output,
1411          * otherwise fall back to the global setting. */
1412         format = ConfNodeLookupChildValue(output, "format");
1413         if (format == NULL)
1414             format = sc_lid->global_log_format;
1415 
1416         level_s = ConfNodeLookupChildValue(output, "level");
1417         if (level_s != NULL) {
1418             level = SCMapEnumNameToValue(level_s, sc_log_level_map);
1419             if (level == -1) {
1420                 SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid log level: %s",
1421                     level_s);
1422                 exit(EXIT_FAILURE);
1423             }
1424             max_level = MAX(max_level, level);
1425         }
1426 
1427         /* Increase the level of extra verbosity was requested. */
1428         level = MAX(min_level, level);
1429 
1430         if (strcmp(output->name, "console") == 0) {
1431             op_iface_ctx = SCLogInitConsoleOPIface(format, level, type);
1432         }
1433         else if (strcmp(output->name, "file") == 0) {
1434             const char *filename = ConfNodeLookupChildValue(output, "filename");
1435             if (filename == NULL) {
1436                     FatalError(SC_ERR_FATAL,
1437                                "Logging to file requires a filename");
1438             }
1439             char *path = NULL;
1440             if (!(PathIsAbsolute(filename))) {
1441                 path = SCLogGetLogFilename(filename);
1442             } else {
1443                 path = SCStrdup(filename);
1444             }
1445             if (path == NULL)
1446                 FatalError(SC_ERR_FATAL, "failed to setup output to file");
1447             have_logging = 1;
1448             op_iface_ctx = SCLogInitFileOPIface(path, format, level, type);
1449             SCFree(path);
1450         }
1451         else if (strcmp(output->name, "syslog") == 0) {
1452             int facility = SC_LOG_DEF_SYSLOG_FACILITY;
1453             const char *facility_s = ConfNodeLookupChildValue(output,
1454                 "facility");
1455             if (facility_s != NULL) {
1456                 facility = SCMapEnumNameToValue(facility_s, SCSyslogGetFacilityMap());
1457                 if (facility == -1) {
1458                     SCLogWarning(SC_ERR_INVALID_ARGUMENT, "Invalid syslog "
1459                             "facility: \"%s\", now using \"%s\" as syslog "
1460                             "facility", facility_s, SC_LOG_DEF_SYSLOG_FACILITY_STR);
1461                     facility = SC_LOG_DEF_SYSLOG_FACILITY;
1462                 }
1463             }
1464             SCLogDebug("Initializing syslog logging with format \"%s\"", format);
1465             have_logging = 1;
1466             op_iface_ctx = SCLogInitSyslogOPIface(facility, format, level, type);
1467         }
1468         else {
1469             SCLogWarning(SC_ERR_INVALID_ARGUMENT, "Invalid logging method: %s, "
1470                 "ignoring", output->name);
1471         }
1472         if (op_iface_ctx != NULL) {
1473             SCLogAppendOPIfaceCtx(op_iface_ctx, sc_lid);
1474         }
1475     }
1476 
1477     if (daemon && (have_logging == 0)) {
1478         SCLogError(SC_ERR_MISSING_CONFIG_PARAM,
1479                    "NO logging compatible with daemon mode selected,"
1480                    " suricata won't be able to log. Please update "
1481                    " 'logging.outputs' in the YAML.");
1482     }
1483 
1484     /* Set the global log level to that of the max level used. */
1485     sc_lid->global_log_level = MAX(sc_lid->global_log_level, max_level);
1486     SCLogInitLogModule(sc_lid);
1487 
1488     SCLogDebug("sc_log_global_log_level: %d", sc_log_global_log_level);
1489     SCLogDebug("sc_lc->log_format: %s", sc_log_config->log_format);
1490     SCLogDebug("SCLogSetOPFilter: filter: %s", sc_log_config->op_filter);
1491 
1492     if (sc_lid != NULL)
1493         SCFree(sc_lid);
1494 }
1495 
1496 /**
1497  * \brief Returns a full file path given a filename uses log dir specified in
1498  *        conf or DEFAULT_LOG_DIR
1499  *
1500  * \param filearg The relative filename for which we want a full path include
1501  *                log directory
1502  *
1503  * \retval log_filename The fullpath of the logfile to open
1504  */
SCLogGetLogFilename(const char * filearg)1505 static char *SCLogGetLogFilename(const char *filearg)
1506 {
1507     const char *log_dir = ConfigGetLogDirectory();
1508     char *log_filename = SCMalloc(PATH_MAX);
1509     if (unlikely(log_filename == NULL))
1510         return NULL;
1511     snprintf(log_filename, PATH_MAX, "%s/%s", log_dir, filearg);
1512     return log_filename;
1513 }
1514 
1515 /**
1516  * \brief De-Initializes the logging module
1517  */
SCLogDeInitLogModule(void)1518 void SCLogDeInitLogModule(void)
1519 {
1520     SCLogFreeLogConfig(sc_log_config);
1521 
1522     /* reset the global logging_module variables */
1523     sc_log_global_log_level = 0;
1524     sc_log_module_initialized = 0;
1525     sc_log_module_cleaned = 1;
1526     sc_log_config = NULL;
1527 
1528     /* de-init the FD filters */
1529     SCLogReleaseFDFilters();
1530     /* de-init the FG filters */
1531     SCLogReleaseFGFilters();
1532 
1533 #if defined (OS_WIN32)
1534     SCMutexDestroy(&sc_log_stream_lock);
1535 #endif /* OS_WIN32 */
1536 
1537     return;
1538 }
1539 
1540 //------------------------------------Unit_Tests--------------------------------
1541 
1542 /* The logging engine should be tested to the maximum extent possible, since
1543  * logging code would be used throughout the codebase, and hence we can't afford
1544  * to have a single bug here(not that you can afford to have a bug
1545  * elsewhere ;) ). Please report a bug, if you get a slightest hint of a bug
1546  * from the logging module.
1547  */
1548 
1549 #ifdef UNITTESTS
1550 
SCLogTestInit01(void)1551 static int SCLogTestInit01(void)
1552 {
1553 #ifndef OS_WIN32
1554     /* unset any environment variables set for the logging module */
1555     unsetenv(SC_LOG_ENV_LOG_LEVEL);
1556     unsetenv(SC_LOG_ENV_LOG_OP_IFACE);
1557     unsetenv(SC_LOG_ENV_LOG_FORMAT);
1558 
1559     SCLogInitLogModule(NULL);
1560 
1561     FAIL_IF_NULL(sc_log_config);
1562 
1563     FAIL_IF_NOT(SC_LOG_DEF_LOG_LEVEL == sc_log_config->log_level);
1564     FAIL_IF_NOT(sc_log_config->op_ifaces != NULL &&
1565                SC_LOG_DEF_LOG_OP_IFACE == sc_log_config->op_ifaces->iface);
1566     FAIL_IF_NOT(sc_log_config->log_format != NULL &&
1567                strcmp(SCLogGetDefaultLogFormat(), sc_log_config->log_format) == 0);
1568 
1569     SCLogDeInitLogModule();
1570 
1571     setenv(SC_LOG_ENV_LOG_LEVEL, "Debug", 1);
1572     setenv(SC_LOG_ENV_LOG_OP_IFACE, "Console", 1);
1573     setenv(SC_LOG_ENV_LOG_FORMAT, "%n- %l", 1);
1574 
1575     SCLogInitLogModule(NULL);
1576 
1577     FAIL_IF_NOT(SC_LOG_DEBUG == sc_log_config->log_level);
1578     FAIL_IF_NOT(sc_log_config->op_ifaces != NULL &&
1579                SC_LOG_OP_IFACE_CONSOLE == sc_log_config->op_ifaces->iface);
1580     FAIL_IF_NOT(sc_log_config->log_format != NULL &&
1581                !strcmp("%n- %l", sc_log_config->log_format));
1582 
1583     unsetenv(SC_LOG_ENV_LOG_LEVEL);
1584     unsetenv(SC_LOG_ENV_LOG_OP_IFACE);
1585     unsetenv(SC_LOG_ENV_LOG_FORMAT);
1586 
1587     SCLogDeInitLogModule();
1588 #endif
1589     PASS;
1590 }
1591 
SCLogTestInit02(void)1592 static int SCLogTestInit02(void)
1593 {
1594 #ifndef OS_WIN32
1595     SCLogInitData *sc_lid = NULL;
1596     SCLogOPIfaceCtx *sc_iface_ctx = NULL;
1597     char *logfile = SCLogGetLogFilename("boo.txt");
1598     sc_lid = SCLogAllocLogInitData();
1599     FAIL_IF_NULL(sc_lid);
1600     sc_lid->startup_message = "Test02";
1601     sc_lid->global_log_level = SC_LOG_DEBUG;
1602     sc_lid->op_filter = "boo";
1603     sc_iface_ctx = SCLogInitOPIfaceCtx("file", "%m - %d", SC_LOG_ALERT,
1604                                        logfile);
1605     SCLogAppendOPIfaceCtx(sc_iface_ctx, sc_lid);
1606     sc_iface_ctx = SCLogInitOPIfaceCtx("console", NULL, SC_LOG_ERROR,
1607                                        NULL);
1608     SCLogAppendOPIfaceCtx(sc_iface_ctx, sc_lid);
1609 
1610     SCLogInitLogModule(sc_lid);
1611 
1612     FAIL_IF_NULL(sc_log_config);
1613 
1614     FAIL_IF_NOT(SC_LOG_DEBUG == sc_log_config->log_level);
1615     FAIL_IF_NOT(sc_log_config->op_ifaces != NULL &&
1616                SC_LOG_OP_IFACE_FILE == sc_log_config->op_ifaces->iface);
1617     FAIL_IF_NOT(sc_log_config->op_ifaces != NULL &&
1618                sc_log_config->op_ifaces->next != NULL &&
1619                SC_LOG_OP_IFACE_CONSOLE == sc_log_config->op_ifaces->next->iface);
1620     FAIL_IF_NOT(sc_log_config->log_format != NULL &&
1621                strcmp(SCLogGetDefaultLogFormat(), sc_log_config->log_format) == 0);
1622     FAIL_IF_NOT(sc_log_config->op_ifaces != NULL &&
1623                sc_log_config->op_ifaces->log_format != NULL &&
1624                strcmp("%m - %d", sc_log_config->op_ifaces->log_format) == 0);
1625     FAIL_IF_NOT(sc_log_config->op_ifaces != NULL &&
1626                sc_log_config->op_ifaces->next != NULL &&
1627                sc_log_config->op_ifaces->next->log_format == NULL);
1628 
1629     SCLogFreeLogInitData(sc_lid);
1630     SCLogDeInitLogModule();
1631 
1632     sc_lid = SCLogAllocLogInitData();
1633     FAIL_IF_NULL(sc_lid);
1634     sc_lid->startup_message = "Test02";
1635     sc_lid->global_log_level = SC_LOG_DEBUG;
1636     sc_lid->op_filter = "boo";
1637     sc_lid->global_log_format = "kaboo";
1638 
1639     SCLogInitLogModule(sc_lid);
1640 
1641     FAIL_IF_NULL(sc_log_config);
1642 
1643     FAIL_IF_NOT(SC_LOG_DEBUG == sc_log_config->log_level);
1644     FAIL_IF_NOT(sc_log_config->op_ifaces != NULL &&
1645                SC_LOG_OP_IFACE_CONSOLE == sc_log_config->op_ifaces->iface);
1646     FAIL_IF_NOT(sc_log_config->op_ifaces != NULL &&
1647                sc_log_config->op_ifaces->next == NULL);
1648     FAIL_IF_NOT(sc_log_config->log_format != NULL &&
1649                strcmp("kaboo", sc_log_config->log_format) == 0);
1650     FAIL_IF_NOT(sc_log_config->op_ifaces != NULL &&
1651                sc_log_config->op_ifaces->log_format == NULL);
1652     FAIL_IF_NOT(sc_log_config->op_ifaces != NULL &&
1653                sc_log_config->op_ifaces->next == NULL);
1654 
1655     SCLogFreeLogInitData(sc_lid);
1656     SCLogDeInitLogModule();
1657     SCFree(logfile);
1658 #endif
1659     PASS;
1660 }
1661 
SCLogTestInit03(void)1662 static int SCLogTestInit03(void)
1663 {
1664     SCLogInitLogModule(NULL);
1665 
1666     SCLogAddFGFilterBL(NULL, "bamboo", -1);
1667     SCLogAddFGFilterBL(NULL, "soo", -1);
1668     SCLogAddFGFilterBL(NULL, "dummy", -1);
1669 
1670     FAIL_IF_NOT(SCLogPrintFGFilters() == 3);
1671 
1672     SCLogAddFGFilterBL(NULL, "dummy1", -1);
1673     SCLogAddFGFilterBL(NULL, "dummy2", -1);
1674 
1675     FAIL_IF_NOT(SCLogPrintFGFilters() == 5);
1676 
1677     SCLogDeInitLogModule();
1678 
1679     PASS;
1680 }
1681 
SCLogTestInit04(void)1682 static int SCLogTestInit04(void)
1683 {
1684     SCLogInitLogModule(NULL);
1685 
1686     SCLogAddFDFilter("bamboo");
1687     SCLogAddFDFilter("soo");
1688     SCLogAddFDFilter("foo");
1689     SCLogAddFDFilter("roo");
1690 
1691     FAIL_IF_NOT(SCLogPrintFDFilters() == 4);
1692 
1693     SCLogAddFDFilter("loo");
1694     SCLogAddFDFilter("soo");
1695 
1696     FAIL_IF_NOT(SCLogPrintFDFilters() == 5);
1697 
1698     SCLogRemoveFDFilter("bamboo");
1699     SCLogRemoveFDFilter("soo");
1700     SCLogRemoveFDFilter("foo");
1701     SCLogRemoveFDFilter("noo");
1702 
1703     FAIL_IF_NOT(SCLogPrintFDFilters() == 2);
1704 
1705     SCLogDeInitLogModule();
1706 
1707     PASS;
1708 }
1709 
SCLogTestInit05(void)1710 static int SCLogTestInit05(void)
1711 {
1712     char str[4096];
1713     memset(str, 'A', sizeof(str));
1714     SCLogInfo("%s", str);
1715 
1716     PASS;
1717 }
1718 
1719 #endif /* UNITTESTS */
1720 
SCLogRegisterTests()1721 void SCLogRegisterTests()
1722 {
1723 
1724 #ifdef UNITTESTS
1725 
1726     UtRegisterTest("SCLogTestInit01", SCLogTestInit01);
1727     UtRegisterTest("SCLogTestInit02", SCLogTestInit02);
1728     UtRegisterTest("SCLogTestInit03", SCLogTestInit03);
1729     UtRegisterTest("SCLogTestInit04", SCLogTestInit04);
1730     UtRegisterTest("SCLogTestInit05", SCLogTestInit05);
1731 
1732 #endif /* UNITTESTS */
1733 
1734    return;
1735 }
1736