Project

General

Profile

Feature #250 » unixsocket.2.patch

Patch incorporating initial comments - Vivek Rajagopalan, 03/15/2011 06:09 AM

View differences:

src/Makefile.am
alert-unified-alert.c alert-unified-alert.h \
alert-unified2-alert.c alert-unified2-alert.h \
alert-syslog.c alert-syslog.h \
alert-unix-socket.c alert-unix-socket.h \
log-droplog.c log-droplog.h \
log-httplog.c log-httplog.h \
stream.c stream.h \
src/alert-unix-socket.c
/* Copyright (C) 2007-2010 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 Vivek Rajagopalan <vivek@unleashnetworks.com>
*
* Log alerts in Snort 2.9 compatible format to a Unix Domain Socket
*/
#include "suricata-common.h"
#include "debug.h"
#include "detect.h"
#include "conf.h"
#include "threads.h"
#include "threadvars.h"
#include "tm-modules.h"
#include "util-unittest.h"
#include "util-error.h"
#include "output.h"
#include "alert-unix-socket.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#define DEFAULT_LOG_FILENAME "suricata-unix-socket"
#define ERRBUF_LENGTH 256
/* prototypes */
TmEcode UnixSocketAlert (ThreadVars *, Packet *, void *, PacketQueue *, PacketQueue *);
TmEcode UnixSocketAlertThreadInit(ThreadVars *, void *, void **);
TmEcode UnixSocketAlertThreadDeinit(ThreadVars *, void *);
int UnixSocketPacketTypeAlert(ThreadVars *, Packet *, void *);
void UnixSocketRegisterTests();
static void UnixSocketAlertDeInitCtx(OutputCtx *);
/**
* UnixSocket thread vars
*
* Used for storing file options.
*/
typedef struct UnixSocketAlertThread_ {
LogFileCtx *file_ctx; /** LogFileCtx pointer */
struct sockaddr_un remote; /** contains unsock file name ready to sendto */
} UnixSocketAlertThread;
/**
* Desired output format from Snort 2.9 codebase
*/
#define SNORT_2_9_ALERTMSG_LENGTH 256
typedef struct _Event
{
uint32_t sig_generator; /* which part of snort generated the alert? */
uint32_t sig_id; /* sig id for this generator */
uint32_t sig_rev; /* sig revision for this id */
uint32_t classification; /* event classification */
uint32_t priority; /* event priority */
uint32_t event_id; /* event ID */
uint32_t event_reference; /* reference to other events that have gone off,
* such as in the case of tagged packets...
* */
struct {
uint32_t tv_sec;
uint32_t tv_usec;
} ref_time; /* reference time for the event reference */
/* Don't add to this structure because this is the serialized data
* struct for unified logging.
*/
} Event;
/**
* SNORT_2_9_ALERTMSG_LENGTH is actually a fixed length, so is pkt in 2.9 snort
*/
/* this struct is for the alert socket code.... */
typedef struct _Alertpkt
{
uint8_t alertmsg[SNORT_2_9_ALERTMSG_LENGTH]; /* variable.. */
struct {
uint32_t tv_sec;
uint32_t tv_usec;
uint32_t caplen;
uint32_t len;
} pkth;
uint32_t dlthdr; /* datalink header offset. (ethernet, etc.. ) */
uint32_t nethdr; /* network header offset. (ip etc...) */
uint32_t transhdr; /* transport header offset (tcp/udp/icmp ..) */
uint32_t data;
uint32_t val; /* which fields are valid. (NULL could be
* valids also) */
/* Packet struct --> was null */
#define NOPACKET_STRUCT 0x1
/* no transport headers in packet */
#define NO_TRANSHDR 0x2
uint8_t pkt[65535];
Event event;
} Alertpkt;
#define MODULE_NAME "UnixSocketAlert"
void TmModuleUnixSocketAlertRegister (void) {
tmm_modules[TMM_ALERTUNIXSOCKET].name = MODULE_NAME;
tmm_modules[TMM_ALERTUNIXSOCKET].ThreadInit = UnixSocketAlertThreadInit;
tmm_modules[TMM_ALERTUNIXSOCKET].Func = UnixSocketAlert;
tmm_modules[TMM_ALERTUNIXSOCKET].ThreadDeinit = UnixSocketAlertThreadDeinit;
tmm_modules[TMM_ALERTUNIXSOCKET].RegisterTests = UnixSocketRegisterTests;
tmm_modules[TMM_ALERTUNIXSOCKET].cap_flags = 0;
OutputRegisterModule(MODULE_NAME, "unix-socket", UnixSocketAlertInitCtx);
}
/**
* \brief Log alert into unix socket in a Snort 2.9.x compatible format
* Key points - 1) use DGRAM 2) no need for host/network order
*
* \param t Thread Variable containing input/output queue, cpu affinity etc.
* \param p Packet struct used to decide for ipv4 or ipv6
* \param data UnixSocket thread data.
* \param pq Packet queue
* \retval 0 on succces
* \retval -1 on failure
*/
TmEcode UnixSocketAlert (ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue * postpq)
{
UnixSocketAlertThread *aun = (UnixSocketAlertThread *)data;
PacketAlert *pa;
PacketAlert pa_tag;
Alertpkt ap;
memset(&ap,0,sizeof(Alertpkt));
if (p->alerts.cnt == 0 )
return TM_ECODE_OK;
/* extract as much as possible into the snort structure */
ap.pkth.tv_sec = p->ts.tv_sec;
ap.pkth.tv_usec = p->ts.tv_usec;
ap.pkth.len = p->pktlen;
ap.pkth.caplen = p->pktlen;
/* ethernet offset nfq has no ethernet header so pad */
if (p->ethh == NULL) {
ap.dlthdr = sizeof(EthernetHdr);
} else {
ap.dlthdr = (uint8_t*) p->ethh - GET_PKT_DATA(p);
}
if (PKT_IS_IPV4(p)) {
ap.nethdr = (uint8_t*) p->ip4h - GET_PKT_DATA(p);
} else if (PKT_IS_IPV6(p)) {
ap.nethdr = (uint8_t*) p->ip6h - GET_PKT_DATA(p);
}
if (p->icmpv4h != NULL ) {
ap.transhdr = (uint8_t*) p->icmpv4h - GET_PKT_DATA(p);
} else if (p->tcph != NULL ) {
ap.transhdr = (uint8_t*) p->tcph - GET_PKT_DATA(p);
} else if (p->udph != NULL ) {
ap.transhdr = (uint8_t*) p->udph - GET_PKT_DATA(p);
} else if (p->sctph != NULL ) {
ap.transhdr = (uint8_t*) p->sctph - GET_PKT_DATA(p);
} else {
ap.transhdr = 0;
ap.val|=NO_TRANSHDR;
}
if (p->payload != NULL ) {
ap.data = (uint8_t*) p->payload - GET_PKT_DATA(p);
}
if (GET_PKT_LEN(p)==0 || GET_PKT_DATA(p)==NULL) {
ap.val|=NOPACKET_STRUCT;
} else {
memcpy(ap.pkt,GET_PKT_DATA(p),GET_PKT_LEN(p));
}
if (p->flags & PKT_HAS_TAG)
PacketAlertAppendTag(p, &pa_tag);
uint16_t i = 0;
for (; i < p->alerts.cnt + 1; i++) {
if (i < p->alerts.cnt)
pa = &p->alerts.alerts[i];
else
if (p->flags & PKT_HAS_TAG)
pa = &pa_tag;
else
break;
pa = &p->alerts.alerts[i];
if (pa->msg != NULL ) {
strlcpy((char*) ap.alertmsg,pa->msg,SNORT_2_9_ALERTMSG_LENGTH);
}
ap.event.sig_generator = pa->gid;
ap.event.sig_id = pa->sid;
ap.event.sig_rev= pa->rev;
ap.event.classification = pa->class;
ap.event.priority = pa->prio;
ap.event.event_id = 0;
ap.event.ref_time.tv_sec = p->ts.tv_sec;
ap.event.ref_time.tv_usec =p->ts.tv_usec;
if (-1 == sendto(aun->file_ctx->fd,&ap,sizeof(Alertpkt),0,(struct sockaddr*) &aun->remote, SUN_LEN(&aun->remote) )) {
char errbuf[ERRBUF_LENGTH];
SCLogError(SC_ERR_COMM_WRITE, "Error: unix socket write failed: %s", strerror_r(errno,errbuf,ERRBUF_LENGTH));
/* returning fail here will respawn this thread, so error messages only indication it failed */
return TM_ECODE_OK;
}
}
aun->file_ctx->alerts += p->alerts.cnt;
return TM_ECODE_OK;
}
/**
* \brief Thread init function.
*
* \param t Thread Variable containing input/output queue, cpu affinity etc.
* \param initdata UnixSocket thread initial data.
* \param data UnixSocket thread data.
* \retval TM_ECODE_OK on succces
* \retval TM_ECODE_FAILED on failure
*/
TmEcode UnixSocketAlertThreadInit(ThreadVars *t, void *initdata, void **data)
{
UnixSocketAlertThread *aun = SCMalloc(sizeof(UnixSocketAlertThread));
if (aun == NULL)
return TM_ECODE_FAILED;
memset(aun, 0, sizeof(UnixSocketAlertThread));
if(initdata == NULL)
{
SCLogDebug("Error getting context for UnixSocketAlert. \"initdata\" argument NULL");
SCFree(aun);
return TM_ECODE_FAILED;
}
aun->file_ctx = ((OutputCtx *)initdata)->data;
memset(&aun->remote,0,sizeof(struct sockaddr_un));
aun->remote.sun_family = AF_UNIX;
strlcpy(aun->remote.sun_path, aun->file_ctx->filename, sizeof(aun->remote.sun_path));
*data = (void *)aun;
return TM_ECODE_OK;
}
/**
* \brief Thread deinit function.
*
* \param t Thread Variable containing input/output queue, cpu affinity etc.
* \param data UnixSocket thread data.
* \retval TM_ECODE_OK on succces
* \retval TM_ECODE_FAILED on failure
*/
TmEcode UnixSocketAlertThreadDeinit(ThreadVars *t, void *data)
{
UnixSocketAlertThread *aun = (UnixSocketAlertThread *)data;
if (aun == NULL) {
goto error;
}
if (!(aun->file_ctx->flags & LOGFILE_ALERTS_PRINTED)) {
SCLogInfo("Alert unix socket module wrote %"PRIu64" alerts",
aun->file_ctx->alerts);
/* Do not print it for each thread */
aun->file_ctx->flags |= LOGFILE_ALERTS_PRINTED;
}
/* clear memory */
memset(aun, 0, sizeof(UnixSocketAlertThread));
SCFree(aun);
return TM_ECODE_OK;
error:
return TM_ECODE_FAILED;
}
/** \brief Create a new LogFileCtx from the provided ConfNode.
* \param conf The configuration node for this output.
* \return NULL if failure, LogFileCtx* with valid socket fd & filename if successful
* */
OutputCtx *UnixSocketAlertInitCtx(ConfNode *conf)
{
int s;
LogFileCtx* file_ctx = NULL;
file_ctx = LogFileNewCtx();
if (file_ctx == NULL) {
SCLogError(SC_ERR_INITIALIZATION, "Couldn't create new file_ctx");
goto error;
}
/* use the socket filename in the yaml file, if absolute, make it relative to log-dir */
const char *filename = NULL;
if (conf != NULL) { /* To faciliate unit tests. */
filename = ConfNodeLookupChildValue(conf, "filename");
}
if (filename == NULL)
filename = DEFAULT_LOG_FILENAME;
file_ctx->filename = SCMalloc(PATH_MAX);
memset(file_ctx->filename,0,PATH_MAX);
/* relativize it if not absolute path */
if (filename[0] != '/')
{
char *log_dir;
if (ConfGet("default-log-dir", &log_dir) != 1)
log_dir = DEFAULT_LOG_DIR;
/* need sanity test due to use of fixed length PATH_MAX buffer */
if (strlen(filename) + strlen(log_dir) + 2 >= PATH_MAX)
{
SCLogError(SC_ERR_INVALID_ARGUMENT,
"Failed to initialize unix socket output, filename + log_dir too big: Max allowed = %d",
PATH_MAX);
goto error;
}
strlcpy(file_ctx->filename,log_dir, PATH_MAX);
if (log_dir[strlen(log_dir)-1]!='/')
strlcat(file_ctx->filename,"/",PATH_MAX);
strlcat(file_ctx->filename,filename,PATH_MAX);
}
else
{
/* need sanity test due to use of fixed length PATH_MAX buffer */
if (strlen(filename) >= PATH_MAX)
{
SCLogError(SC_ERR_INVALID_ARGUMENT,
"Failed to initialize unix socket output, filename too big: Max allowed = %d",
PATH_MAX);
goto error;
}
strlcpy(file_ctx->filename,filename,PATH_MAX);
}
/* create a unix domain socket - connectionless is okay because all comms are local anyway */
if ((s = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1)
{
char errbuf[ERRBUF_LENGTH];
SCLogError(SC_ERR_COMM_OPEN,
"Cant create unix socket %d : %s", errno, strerror_r(errno,errbuf,ERRBUF_LENGTH));
goto error;
}
file_ctx->fd = s;
/* pack everything into an output context */
OutputCtx *output_ctx = SCCalloc(1, sizeof(OutputCtx));
if (output_ctx == NULL)
goto error;
output_ctx->data = file_ctx;
output_ctx->DeInit = UnixSocketAlertDeInitCtx;
SCLogInfo("unix-socket successfully initialized: filename %s",
file_ctx->filename );
return output_ctx;
error:
if (file_ctx != NULL) {
LogFileFreeCtx(file_ctx);
}
return NULL;
}
static void UnixSocketAlertDeInitCtx(OutputCtx *output_ctx)
{
if (output_ctx != NULL) {
LogFileCtx *logfile_ctx = (LogFileCtx *)output_ctx->data;
if (logfile_ctx != NULL) {
LogFileFreeCtx(logfile_ctx);
}
free(output_ctx);
}
}
#ifdef UNITTESTS
/*
* No unit tests as of now
*/
#endif
/**
* \brief this function registers unit tests for UnixSocket
*/
void UnixSocketRegisterTests (void) {
#ifdef UNITTESTS
#endif /* UNITTESTS */
}
src/alert-unix-socket.h
/* Copyright (C) 2007-2010 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 alert-unix-socket snort 2.9.x compatible alerts to Unix Socket
* \author Vivek Rajagopalan <vivek@unleashnetworks.com>
*/
#ifndef __ALERT_UNIX_SOCKET_H
#define __ALERT_UNIX_SOCKET_H
void TmModuleUnixSocketAlertRegister (void);
OutputCtx *UnixSocketAlertInitCtx(ConfNode *conf);
#endif /* __ALERT_UNIX_SOCKET_H */
src/suricata.c
#include "alert-unified-log.h"
#include "alert-unified-alert.h"
#include "alert-unified2-alert.h"
#include "alert-unix-socket.h"
#include "alert-debuglog.h"
#include "alert-prelude.h"
#include "alert-syslog.h"
......
TmModuleAlertSyslogIPv6Register();
TmModuleAlertUnifiedLogRegister();
TmModuleAlertUnifiedAlertRegister();
TmModuleUnixSocketAlertRegister();
TmModuleUnified2AlertRegister();
TmModuleAlertSyslogRegister();
TmModuleLogDropLogRegister();
src/tm-modules.c
return NULL;
memset(lf_ctx, 0, sizeof(LogFileCtx));
lf_ctx->fd = -1; /* 0 is a valid fd */
SCMutexInit(&lf_ctx->fp_mutex,NULL);
return lf_ctx;
......
SCMutexUnlock(&lf_ctx->fp_mutex);
}
if (lf_ctx->fd != -1)
{
close(lf_ctx->fd);
lf_ctx->fd = -1;
}
SCMutexDestroy(&lf_ctx->fp_mutex);
if (lf_ctx->prefix != NULL)
src/tm-modules.h
TMM_DECODEERFFILE,
TMM_RECEIVEERFDAG,
TMM_DECODEERFDAG,
TMM_ALERTUNIXSOCKET,
TMM_SIZE,
};
......
/** Global structure for Output Context */
typedef struct LogFileCtx_ {
FILE *fp;
/** Any output file descriptor
* unix socket, (future) TCP socket, etc */
int fd;
/** It will be locked if the log/alert
* record cannot be written to the file in one call */
SCMutex fp_mutex;
src/util-error.c
CASE_CODE (SC_ERR_HTTP_COOKIE_NEEDS_PRECEEDING_CONTENT);
CASE_CODE (SC_ERR_HTTP_COOKIE_INCOMPATIBLE_WITH_RAWBYTES);
CASE_CODE (SC_ERR_HTTP_COOKIE_RELATIVE_MISSING);
CASE_CODE (SC_ERR_COMM_OPEN);
CASE_CODE (SC_ERR_COMM_WRITE);
CASE_CODE (SC_ERR_COMM_READ);
default:
return "UNKNOWN_ERROR";
src/util-error.h
SC_ERR_LIBNET11_INCOMPATIBLE_WITH_LIBCAP_NG,
SC_WARN_FLOW_EMERGENCY,
SC_WARN_COMPATIBILITY,
SC_ERR_SVC,
SC_ERR_SVC,
SC_ERR_ERF_DAG_OPEN_FAILED,
SC_ERR_ERF_DAG_STREAM_OPEN_FAILED,
SC_ERR_ERF_DAG_STREAM_START_FAILED,
......
SC_ERR_HTTP_COOKIE_NEEDS_PRECEEDING_CONTENT,
SC_ERR_HTTP_COOKIE_INCOMPATIBLE_WITH_RAWBYTES,
SC_ERR_HTTP_COOKIE_RELATIVE_MISSING,
SC_ERR_COMM_OPEN, /* error opening communication channel, unix sockets, ipcs, tcp sockets */
SC_ERR_COMM_WRITE, /* write errors on comm channel*/
SC_ERR_COMM_READ, /* read error on comm channel */
} SCError;
const char *SCErrorToString(SCError);
suricata.yaml
- unified2-alert:
enabled: yes
filename: unified2.alert
# Limit in MB.
#limit: 32
......
filename: http.log
append: yes
# unix sockets
- unix-socket:
enabled: yes
filename : /tmp/suricata.sock
# a full alerts log containing much information for signature writers
# or for investigating suspected false positives.
- alert-debug:
(3-3/5)