You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
suricata/src/alert-syslog.c

428 lines
13 KiB
C

/* Copyright (C) 2007-2014 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 <gurvindersinghdahiya@gmail.com>
*
* Logs alerts in a line based text format in to syslog.
*
*/
#include "suricata-common.h"
#include "debug.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"
Add option on Tile-Gx for logging for fast.log alerts over PCIe When running on a TILEncore-Gx PCIe card, setting the filetype of fast.log to pcie, will open a connection over PCIe to a host application caleld tile-pcie-logd, that receives the alert strings and writes them to a file on the host. The file name to open is also passed over the PCIe link. This allows running Suricata on the TILEncore-Gx PCIe card, but have the alerts logged to the host system's file system efficiently. The PCIe API that is used is the Tilera Packet Queue (PQ) API which can access PCIe from User Space, thus avoiding system calls. Created util-logopenfile-tile.c and util-logopen-tile.h for the TILE specific PCIe logging functionality. Using Write() and Close() function pointers in LogFileCtx, which default to standard write and close for files and sockets, but are changed to PCIe write and close functions when a PCIe channel is openned for logging. Moved Logging contex out of tm-modules.h into util-logopenfile.h, where it makes more sense. This required including util-logopenfile.h into a couple of alert-*.c files, which previously were getting the definitions from tm-modules.h. The source and Makefile for tile-pcie-logd are added in contrib/tile-pcie-logd. By default, the file name for fast.log specified in suricata.yaml is used as the filename on the host. An optional argument to tile-pcie-logd, --prefix=, can be added to prepend the supplied file path. For example, is the file in suricata.yaml is specified as "/var/log/fast.log" and --prefix="/tmp", then the file will be written to "/tmp/var/log/fast.log". Check for TILERA_ROOT environment variable before building tile_pcie_logd Building tile_pcie_logd on x86 requires the Tilera MDE for its PCIe libraries and API header files. Configure now checs for TILERA_ROOT before enabling builing tile_pcie_logd in contrib/tile_pcie_logd
12 years ago
#include "util-logopenfile.h"
#ifndef OS_WIN32
#define DEFAULT_ALERT_SYSLOG_FACILITY_STR "local0"
#define DEFAULT_ALERT_SYSLOG_FACILITY LOG_LOCAL0
#define DEFAULT_ALERT_SYSLOG_LEVEL LOG_ERR
#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.
*/
OutputCtx *AlertSyslogInitCtx(ConfNode *conf)
{
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 NULL;
}
int facility = SCMapEnumNameToValue(facility_s, SCSyslogGetFacilityMap());
if (facility == -1) {
SCLogWarning(SC_ERR_INVALID_ARGUMENT, "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("AlertSyslogInitCtx: Could not create new OutputCtx");
return NULL;
}
memset(output_ctx, 0x00, sizeof(OutputCtx));
output_ctx->data = logfile_ctx;
output_ctx->DeInit = AlertSyslogDeInitCtx;
SCLogInfo("Syslog output initialized");
return output_ctx;
}
/**
* \brief Function to initialize the AlertSystlogThread 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, 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 Ouptut Context (file pointer and mutex) */
ast->file_ctx = ((OutputCtx *)initdata)->data;
*data = (void *)ast;
return TM_ECODE_OK;
}
/**
* \brief Function to deinitialize the AlertSystlogThread
*
* \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;
char *action = "";
if (p->alerts.cnt == 0)
return TM_ECODE_OK;
SCMutexLock(&ast->file_ctx->fp_mutex);
ast->file_ctx->alerts += p->alerts.cnt;
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] ";
}
if (SCProtoNameValid(IPV4_GET_IPPROTO(p)) == TRUE) {
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,
known_proto[IPV4_GET_IPPROTO(p)], srcip, p->sp, dstip, p->dp);
} else {
syslog(alert_syslog_level, "%s[%" PRIu32 ":%" PRIu32 ":%"
PRIu32 "] %s [Classification: %s] [Priority: %"PRIu32"]"
" {PROTO:%03" PRIu32 "} %s:%" PRIu32 " -> %s:%" PRIu32 "",
action, pa->s->gid, pa->s->id, pa->s->rev, pa->s->msg, pa->s->class_msg,
pa->s->prio, IPV4_GET_IPPROTO(p), 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;
char *action = "";
if (p->alerts.cnt == 0)
return TM_ECODE_OK;
SCMutexLock(&ast->file_ctx->fp_mutex);
ast->file_ctx->alerts += p->alerts.cnt;
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] ";
}
if (SCProtoNameValid(IPV6_GET_L4PROTO(p)) == TRUE) {
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, known_proto[IPV6_GET_L4PROTO(p)], srcip, p->sp,
dstip, p->dp);
} else {
syslog(alert_syslog_level, "%s[%" PRIu32 ":%" PRIu32 ":%"
"" PRIu32 "] %s [Classification: %s] [Priority: %"
"" PRIu32 "] {PROTO:%03" PRIu32 "} %s:%" PRIu32 " -> %s:%" PRIu32 "",
action, pa->s->gid, pa->s->id, pa->s->rev, pa->s->msg, pa->s->class_msg,
pa->s->prio, IPV6_GET_L4PROTO(p), 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
* \param pq pointer the to packet queue
* \param postpq pointer to the post processed packet queue
*
* \return On succes return TM_ECODE_OK
*/
static TmEcode AlertSyslogDecoderEvent(ThreadVars *tv, const Packet *p, void *data)
{
AlertSyslogThread *ast = (AlertSyslogThread *)data;
int i;
char *action = "";
if (p->alerts.cnt == 0)
return TM_ECODE_OK;
SCMutexLock(&ast->file_ctx->fp_mutex);
ast->file_ctx->alerts += p->alerts.cnt;
char temp_buf_hdr[512];
char temp_buf_pkt[65] = "";
char temp_buf_tail[32];
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;
}
/**
* \brief Function to print the total alert while closing the engine
*
* \param tv Pointer to the output threadvars
* \param data Pointer to the AlertSyslogThread data
*/
static void AlertSyslogExitPrintStats(ThreadVars *tv, void *data)
{
AlertSyslogThread *ast = (AlertSyslogThread *)data;
if (ast == NULL) {
return;
}
SCLogInfo("(%s) Alerts %" PRIu64 "", tv->name, ast->file_ctx->alerts);
}
static int AlertSyslogCondition(ThreadVars *tv, 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 TmModuleAlertSyslogRegister (void)
{
#ifndef OS_WIN32
tmm_modules[TMM_ALERTSYSLOG].name = MODULE_NAME;
tmm_modules[TMM_ALERTSYSLOG].ThreadInit = AlertSyslogThreadInit;
tmm_modules[TMM_ALERTSYSLOG].Func = NULL;
tmm_modules[TMM_ALERTSYSLOG].ThreadExitPrintStats = AlertSyslogExitPrintStats;
tmm_modules[TMM_ALERTSYSLOG].ThreadDeinit = AlertSyslogThreadDeinit;
tmm_modules[TMM_ALERTSYSLOG].RegisterTests = NULL;
tmm_modules[TMM_ALERTSYSLOG].cap_flags = 0;
tmm_modules[TMM_ALERTSYSLOG].flags = TM_FLAG_LOGAPI_TM;
OutputRegisterPacketModule(MODULE_NAME, "syslog",
AlertSyslogInitCtx, AlertSyslogLogger, AlertSyslogCondition);
#endif /* !OS_WIN32 */
}