/* Copyright (C) 2007-2021 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free * Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * version 2 along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. */ /** * \file * * \author Gurvinder Singh * * Logs alerts in a line based text format into syslog. * */ #include "suricata-common.h" #include "flow.h" #include "conf.h" #include "threads.h" #include "tm-threads.h" #include "threadvars.h" #include "detect.h" #include "detect-parse.h" #include "detect-engine.h" #include "detect-engine-mpm.h" #include "detect-reference.h" #include "output.h" #include "alert-syslog.h" #include "util-classification-config.h" #include "util-debug.h" #include "util-print.h" #include "util-proto-name.h" #include "util-syslog.h" #include "util-optimize.h" #include "util-logopenfile.h" #include "action-globals.h" #ifndef OS_WIN32 #define MODULE_NAME "AlertSyslog" static int alert_syslog_level = DEFAULT_ALERT_SYSLOG_LEVEL; typedef struct AlertSyslogThread_ { /** LogFileCtx has the pointer to the file and a mutex to allow multithreading */ LogFileCtx* file_ctx; } AlertSyslogThread; /** * \brief Function to clear the memory of the output context and closes the * syslog interface * * \param output_ctx pointer to the output context to be cleared */ static void AlertSyslogDeInitCtx(OutputCtx *output_ctx) { if (output_ctx != NULL) { LogFileCtx *logfile_ctx = (LogFileCtx *)output_ctx->data; if (logfile_ctx != NULL) { LogFileFreeCtx(logfile_ctx); } SCFree(output_ctx); } closelog(); } /** * \brief Create a new LogFileCtx for "syslog" output style. * * \param conf The configuration node for this output. * \return A OutputCtx pointer on success, NULL on failure. */ static OutputInitResult AlertSyslogInitCtx(ConfNode *conf) { OutputInitResult result = { NULL, false }; const char *facility_s = ConfNodeLookupChildValue(conf, "facility"); if (facility_s == NULL) { facility_s = DEFAULT_ALERT_SYSLOG_FACILITY_STR; } LogFileCtx *logfile_ctx = LogFileNewCtx(); if (logfile_ctx == NULL) { SCLogDebug("AlertSyslogInitCtx: Could not create new LogFileCtx"); return result; } int facility = SCMapEnumNameToValue(facility_s, SCSyslogGetFacilityMap()); if (facility == -1) { SCLogWarning("Invalid syslog facility: \"%s\"," " now using \"%s\" as syslog facility", facility_s, DEFAULT_ALERT_SYSLOG_FACILITY_STR); facility = DEFAULT_ALERT_SYSLOG_FACILITY; } const char *level_s = ConfNodeLookupChildValue(conf, "level"); if (level_s != NULL) { int level = SCMapEnumNameToValue(level_s, SCSyslogGetLogLevelMap()); if (level != -1) { alert_syslog_level = level; } } const char *ident = ConfNodeLookupChildValue(conf, "identity"); /* if null we just pass that to openlog, which will then * figure it out by itself. */ openlog(ident, LOG_PID|LOG_NDELAY, facility); OutputCtx *output_ctx = SCMalloc(sizeof(OutputCtx)); if (unlikely(output_ctx == NULL)) { SCLogDebug("could not create new OutputCtx"); LogFileFreeCtx(logfile_ctx); return result; } memset(output_ctx, 0x00, sizeof(OutputCtx)); output_ctx->data = logfile_ctx; output_ctx->DeInit = AlertSyslogDeInitCtx; SCLogInfo("Syslog output initialized"); result.ctx = output_ctx; result.ok = true; return result; } /** * \brief Function to initialize the AlertSyslogThread and sets the output * context pointer * * \param tv Pointer to the threadvars * \param initdata Pointer to the output context * \param data pointer to pointer to point to the AlertSyslogThread */ static TmEcode AlertSyslogThreadInit(ThreadVars *t, const void *initdata, void **data) { if(initdata == NULL) { SCLogDebug("Error getting context for AlertSyslog. \"initdata\" " "argument NULL"); return TM_ECODE_FAILED; } AlertSyslogThread *ast = SCMalloc(sizeof(AlertSyslogThread)); if (unlikely(ast == NULL)) return TM_ECODE_FAILED; memset(ast, 0, sizeof(AlertSyslogThread)); /** Use the Output Context (file pointer and mutex) */ ast->file_ctx = ((OutputCtx *)initdata)->data; *data = (void *)ast; return TM_ECODE_OK; } /** * \brief Function to deinitialize the AlertSyslogThread * * \param tv Pointer to the threadvars * \param data pointer to the AlertSyslogThread to be cleared */ static TmEcode AlertSyslogThreadDeinit(ThreadVars *t, void *data) { AlertSyslogThread *ast = (AlertSyslogThread *)data; if (ast == NULL) { return TM_ECODE_OK; } /* clear memory */ memset(ast, 0, sizeof(AlertSyslogThread)); SCFree(ast); return TM_ECODE_OK; } /** * \brief Function which is called to print the IPv4 alerts to the syslog * * \param tv Pointer to the threadvars * \param p Pointer to the packet * \param data pointer to the AlertSyslogThread * * \return On succes return TM_ECODE_OK */ static TmEcode AlertSyslogIPv4(ThreadVars *tv, const Packet *p, void *data) { AlertSyslogThread *ast = (AlertSyslogThread *)data; int i; const char *action = ""; if (p->alerts.cnt == 0) return TM_ECODE_OK; char proto[16] = ""; const char *protoptr; if (SCProtoNameValid(IPV4_GET_IPPROTO(p))) { protoptr = known_proto[IPV4_GET_IPPROTO(p)]; } else { snprintf(proto, sizeof(proto), "PROTO:%03" PRIu32, IPV4_GET_IPPROTO(p)); protoptr = proto; } /* Not sure if this mutex is needed around calls to syslog. */ SCMutexLock(&ast->file_ctx->fp_mutex); for (i = 0; i < p->alerts.cnt; i++) { const PacketAlert *pa = &p->alerts.alerts[i]; if (unlikely(pa->s == NULL)) { continue; } char srcip[16], dstip[16]; PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), srcip, sizeof(srcip)); PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), dstip, sizeof(dstip)); if ((pa->action & ACTION_DROP) && EngineModeIsIPS()) { action = "[Drop] "; } else if (pa->action & ACTION_DROP) { action = "[wDrop] "; } syslog(alert_syslog_level, "%s[%" PRIu32 ":%" PRIu32 ":%" PRIu32 "] %s [Classification: %s] [Priority: %"PRIu32"]" " {%s} %s:%" PRIu32 " -> %s:%" PRIu32 "", action, pa->s->gid, pa->s->id, pa->s->rev, pa->s->msg, pa->s->class_msg, pa->s->prio, protoptr, srcip, p->sp, dstip, p->dp); } SCMutexUnlock(&ast->file_ctx->fp_mutex); return TM_ECODE_OK; } /** * \brief Function which is called to print the IPv6 alerts to the syslog * * \param tv Pointer to the threadvars * \param p Pointer to the packet * \param data pointer to the AlertSyslogThread * * \return On succes return TM_ECODE_OK */ static TmEcode AlertSyslogIPv6(ThreadVars *tv, const Packet *p, void *data) { AlertSyslogThread *ast = (AlertSyslogThread *)data; int i; const char *action = ""; if (p->alerts.cnt == 0) return TM_ECODE_OK; char proto[16] = ""; const char *protoptr; if (SCProtoNameValid(IPV6_GET_L4PROTO(p))) { protoptr = known_proto[IPV6_GET_L4PROTO(p)]; } else { snprintf(proto, sizeof(proto), "PROTO:03%" PRIu32, IPV6_GET_L4PROTO(p)); protoptr = proto; } SCMutexLock(&ast->file_ctx->fp_mutex); for (i = 0; i < p->alerts.cnt; i++) { const PacketAlert *pa = &p->alerts.alerts[i]; if (unlikely(pa->s == NULL)) { continue; } char srcip[46], dstip[46]; PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p), srcip, sizeof(srcip)); PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p), dstip, sizeof(dstip)); if ((pa->action & ACTION_DROP) && EngineModeIsIPS()) { action = "[Drop] "; } else if (pa->action & ACTION_DROP) { action = "[wDrop] "; } syslog(alert_syslog_level, "%s[%" PRIu32 ":%" PRIu32 ":%" "" PRIu32 "] %s [Classification: %s] [Priority: %" "" PRIu32 "] {%s} %s:%" PRIu32 " -> %s:%" PRIu32 "", action, pa->s->gid, pa->s->id, pa->s->rev, pa->s->msg, pa->s->class_msg, pa->s->prio, protoptr, srcip, p->sp, dstip, p->dp); } SCMutexUnlock(&ast->file_ctx->fp_mutex); return TM_ECODE_OK; } /** * \brief Function which is called to print the decode alerts to the syslog * * \param tv Pointer to the threadvars * \param p Pointer to the packet * \param data pointer to the AlertSyslogThread * * \return On succes return TM_ECODE_OK */ static TmEcode AlertSyslogDecoderEvent(ThreadVars *tv, const Packet *p, void *data) { AlertSyslogThread *ast = (AlertSyslogThread *)data; int i; const char *action = ""; if (p->alerts.cnt == 0) return TM_ECODE_OK; SCMutexLock(&ast->file_ctx->fp_mutex); char temp_buf_hdr[512]; char temp_buf_pkt[65] = ""; char temp_buf_tail[64]; char alert[2048] = ""; for (i = 0; i < p->alerts.cnt; i++) { const PacketAlert *pa = &p->alerts.alerts[i]; if (unlikely(pa->s == NULL)) { continue; } if ((pa->action & ACTION_DROP) && EngineModeIsIPS()) { action = "[Drop] "; } else if (pa->action & ACTION_DROP) { action = "[wDrop] "; } snprintf(temp_buf_hdr, sizeof(temp_buf_hdr), "%s[%" PRIu32 ":%" PRIu32 ":%" PRIu32 "] %s [Classification: %s] [Priority: %" PRIu32 "] [**] [Raw pkt: ", action, pa->s->gid, pa->s->id, pa->s->rev, pa->s->msg, pa->s->class_msg, pa->s->prio); strlcpy(alert, temp_buf_hdr, sizeof(alert)); PrintRawLineHexBuf(temp_buf_pkt, sizeof(temp_buf_pkt), GET_PKT_DATA(p), GET_PKT_LEN(p) < 32 ? GET_PKT_LEN(p) : 32); strlcat(alert, temp_buf_pkt, sizeof(alert)); if (p->pcap_cnt != 0) { snprintf(temp_buf_tail, sizeof(temp_buf_tail), "] [pcap file packet: %"PRIu64"]", p->pcap_cnt); } else { temp_buf_tail[0] = ']'; temp_buf_tail[1] = '\0'; } strlcat(alert, temp_buf_tail, sizeof(alert)); syslog(alert_syslog_level, "%s", alert); } SCMutexUnlock(&ast->file_ctx->fp_mutex); return TM_ECODE_OK; } static int AlertSyslogCondition(ThreadVars *tv, void *thread_data, const Packet *p) { return (p->alerts.cnt > 0 ? TRUE : FALSE); } static int AlertSyslogLogger(ThreadVars *tv, void *thread_data, const Packet *p) { if (PKT_IS_IPV4(p)) { return AlertSyslogIPv4(tv, p, thread_data); } else if (PKT_IS_IPV6(p)) { return AlertSyslogIPv6(tv, p, thread_data); } else if (p->events.cnt > 0) { return AlertSyslogDecoderEvent(tv, p, thread_data); } return TM_ECODE_OK; } #endif /* !OS_WIN32 */ /** \brief Function to register the AlertSyslog module */ void AlertSyslogRegister (void) { #ifndef OS_WIN32 OutputRegisterPacketModule(LOGGER_ALERT_SYSLOG, MODULE_NAME, "syslog", AlertSyslogInitCtx, AlertSyslogLogger, AlertSyslogCondition, AlertSyslogThreadInit, AlertSyslogThreadDeinit, NULL); #endif /* !OS_WIN32 */ }