Logo Search packages:      
Sourcecode: sagan version File versions  Download package

sagan-unified2.c

/*
** Copyright (C) 2009-2011 Softwink, Inc. 
** Copyright (C) 2009-2011 Champ Clark III <champ@softwink.com>
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License Version 2 as
** published by the Free Software Foundation.  You may not use, modify or
** distribute this program under any other version of the GNU General
** Public License.
**
** 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
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

/* sagan-unified2.c
 *
 * This allows Sagan to output to a Snort's 'unified2' format.  This format
 * can then be read by programs like barnyard2,  etc. 
 *
 */


#ifdef HAVE_CONFIG_H
#include "config.h"             /* From autoconf */
#endif

#ifdef HAVE_LIBDNET

#include <stdio.h>
#include <stdint.h>
#include <stdarg.h>
#include <stdlib.h>
#include <errno.h>
#include <arpa/inet.h>
#include <string.h>
#include <dnet.h>
#include <unistd.h>

#include "sagan.h"
#include "sagan-unified2.h"

sbool endian;
uint64_t unified_event_id;

struct rule_struct *rulestruct;
struct class_struct *classstruct;
struct _SaganConfig *config;
struct _SaganCounters *counters;

static void Unified2Write(uint8_t *, uint32_t);
static int SafeMemcpy(void *, const void *, size_t, const void *, const void *);
static int inBounds(const uint8_t *, const uint8_t *, const uint8_t *);
static void Unified2RotateFile( void );

/* Future note on IPv6 - This would have been 
 * Serial_Unified2IDSEventIPv6_legacy.  
 * - Champ Clark III - 02/15/2011 
 */

static uint8_t write_pkt_buffer[sizeof(Serial_Unified2_Header) +
                                sizeof(Serial_Unified2IDSEvent_legacy) + IP_MAXPACKET];

#define write_pkt_end (write_pkt_buffer + sizeof(write_pkt_buffer))

char *eth_addr="00:11:22:33:44:55"; /* Bogus ethernet address for ethernet frame */

/*********************************************************/
/* Unified2InitFile - Initializes the file to be openned */
/*********************************************************/

void Unified2InitFile( void  )
{

    char filepath[1024];
    char *fname_ptr;

    if (config == NULL) sagan_log(1, "[%s, line %d] Could not init Unified2. Config data is null", __FILE__, __LINE__ ); 

    config->unified2_timestamp = (uint32_t)time(NULL);

    if (!config->unified2_nostamp)
    {
        if (SaganSnprintf(filepath, sizeof(filepath), "%s.%u",
                          config->unified2_filepath, config->unified2_timestamp) != SAGAN_SNPRINTF_SUCCESS)
                    sagan_log(1, "[%s, line %d] Failed to copy Unified2 file path", __FILE__, __LINE__);
        
      fname_ptr = filepath;
    }
    else
    {
        fname_ptr = config->unified2_filepath;
    }

    if ((config->unified2_stream = fopen(fname_ptr, "wb")) == NULL)
       sagan_log(1, "[%s, line %d] Cannot open file %s.", __FILE__, __LINE__, fname_ptr);
}


/****************************************************/
/* Sagan_Unified2 - Write the Unified2 event        */
/****************************************************/

void Sagan_Unified2( SaganEvent *Event )
{ 


Serial_Unified2_Header hdr;
Serial_Unified2IDSEvent_legacy alertdata;
uint32_t write_len = sizeof(Serial_Unified2_Header) + sizeof(Serial_Unified2IDSEvent_legacy);
int i=0;

memset(&alertdata, 0, sizeof(alertdata));

alertdata.event_id = htonl(unified_event_id);                           // Event ID (increments)
alertdata.event_second = htonl(Event->event_time_sec);                        // Time of event;
alertdata.event_microsecond = htonl(0);                                 // Not recording to the microsecond
alertdata.signature_id = htonl(atoi(rulestruct[Event->found].s_sid));         // Rule triggered (sid #)
alertdata.signature_revision = htonl(atoi(rulestruct[Event->found].s_rev));   // Rule Revision

/* Seach for the classification type. */

/* There's probably a better way to do this - Champ Clark III - 02/17/2011 */

for(i=0; i < counters->classcount; i++) {
    if (!strcmp(rulestruct[Event->found].s_classtype, classstruct[i].s_shortname)) alertdata.classification_id = htonl(i + 1);
}

alertdata.priority_id = htonl(rulestruct[Event->found].s_pri);                // Rule priority
alertdata.protocol = rulestruct[Event->found].ip_proto;                       // Protocol
alertdata.generator_id = htonl(1);                                // Typically comes from gen-msg.map

alertdata.ip_source = htonl(ip2bit(Event->ip_src, endian));
alertdata.ip_destination = htonl(ip2bit(Event->ip_dst, endian));

alertdata.sport_itype = htons(Event->src_port);
alertdata.dport_icode = htons(Event->dst_port);

/* Rotate if log has gotten to big */

if ((config->unified2_current + write_len) > config->unified2_limit)
     Unified2RotateFile();


hdr.length = htonl(sizeof(Serial_Unified2IDSEvent_legacy));
hdr.type = htonl(UNIFIED2_IDS_EVENT);

if (SafeMemcpy(write_pkt_buffer, &hdr, sizeof(Serial_Unified2_Header),
               write_pkt_buffer, write_pkt_end) != SAFEMEM_SUCCESS)
   {
      sagan_log(0, "Failed to copy Serial_Unified2_Header\n");
      return;
   }

if (SafeMemcpy(write_pkt_buffer + sizeof(Serial_Unified2_Header),
               &alertdata, sizeof(Serial_Unified2IDSEvent_legacy),
               write_pkt_buffer, write_pkt_end) != SAFEMEM_SUCCESS)
  {
      sagan_log(0, "Failed to copy Serial_Unified2IDSEvent_legacy\n");
      return;
  }

Unified2Write(write_pkt_buffer, write_len);

}

/*****************************************************************************/
/* Sagan_Unified2LogPacketAlert - Create's a raw TCP/UDP/IP/ICMP 'packet'    */
/* This packet is "fake",  as we are taking syslog data and 'building'       */
/* a packet with libdnet.  This fake packet is then fed to the Unified2      */
/* file for reading by Barnyard2, etc.                                       */
/*****************************************************************************/

void Sagan_Unified2LogPacketAlert( SaganEvent *Event )
{

Serial_Unified2_Header hdr;
Serial_Unified2Packet logheader;
uint32_t pkt_length = 0;
uint32_t i = 0;
uint32_t write_len = sizeof(Serial_Unified2_Header) + sizeof(Serial_Unified2Packet) - 4;

/* Ethernet */

u_char *p_eth, eth_buf[ETH_LEN_MAX];
struct eth_hdr *eth;
int len_eth = 0;
struct addr addr;

/* IP header */

struct ip_hdr *ip;
u_char *p_iphdr, iphdr_buf[IP_LEN_MAX];
int len_iphdr = 0;

/* TCP header */

struct tcp_hdr *tcp;
u_char *p_tcp, tcp_buf[IP_LEN_MAX];

/* UDP header */

struct udp_hdr *udp;
u_char *p_udp, udp_buf[IP_LEN_MAX];  

/* ICMP header */

struct icmp_hdr *icmp;
u_char *p_icmp, icmp_buf[IP_LEN_MAX]; 

/* 'Packet' payload (syslog data) */

u_char packet_buf[IP_LEN_MAX];
uint8_t packet_data[63556];
int p_len = 0;

unsigned int len_payload = strlen(Event->message);          // Our payload 'length' 

/* Build the ethernet frame */

eth = (struct eth_hdr *)eth_buf;
memset(eth, 0, sizeof(*eth));

eth->eth_type = htons(ETH_TYPE_IP);

addr_aton(eth_addr, &addr);
memcpy(&eth->eth_src, &addr.addr_eth, ETH_ADDR_LEN);

addr_aton(eth_addr, &addr);
memcpy(&eth->eth_dst, &addr.addr_eth, ETH_ADDR_LEN);

p_eth = eth_buf + ETH_HDR_LEN;
len_eth = p_eth - eth_buf;



/* Type == UDP */


if ( rulestruct[Event->found].ip_proto == 17 ) { 

      udp = (struct udp_hdr *)udp_buf;
      memset(udp, 0, sizeof(*udp));

      udp->uh_sport = htons(Event->src_port);
      udp->uh_dport = htons(Event->dst_port);

      p_udp = udp_buf + UDP_HDR_LEN;
      p_len = p_udp - udp_buf;
      udp->uh_ulen = htons(p_len + len_payload);
      memcpy(packet_buf, udp_buf, sizeof(packet_buf));
}


/* Type == TCP */

else if ( rulestruct[Event->found].ip_proto == 6 ) { 

      tcp = (struct tcp_hdr *)tcp_buf;
      memset(tcp, 0, sizeof(*tcp));

      tcp->th_sport = htons(Event->src_port);
      tcp->th_dport = htons(Event->dst_port);

      tcp->th_seq = 0;
      tcp->th_ack = 0;
      tcp->th_off = 5;               
      tcp->th_flags = TH_SYN;   // or TH_FIN, TH_PUSH
      tcp->th_win = TCP_WIN_MAX;
      tcp->th_urp = 0;

      p_tcp = tcp_buf + TCP_HDR_LEN;
      p_len =  p_tcp - tcp_buf;
      memcpy(packet_buf, tcp_buf, sizeof(packet_buf));

}

/* Type == ICMP */

else if ( rulestruct[Event->found].ip_proto == 1 ) { 

      icmp = (struct icmp_hdr *)icmp_buf;
      memset(icmp, 0, sizeof(*icmp));

      icmp->icmp_type = ICMP_ECHO;
      icmp->icmp_code = 0;
      p_icmp = icmp_buf + ICMP_HDR_LEN;
      p_len = p_icmp - icmp_buf;
      memcpy(packet_buf, icmp_buf, sizeof(packet_buf));
}


/* We'll always need a IP header,  so build it here */

ip = (struct ip_hdr *)iphdr_buf;
memset(ip, 0, sizeof(*ip));

ip->ip_hl = 5;
ip->ip_v = 4;
ip->ip_tos = 0;
ip->ip_id = 0;
ip->ip_off = 0;
ip->ip_ttl = IP_TTL_MAX;
ip->ip_p = rulestruct[Event->found].ip_proto;         // Protocol
ip->ip_sum = 0;

ip->ip_src = htonl(ip2bit(Event->ip_src, endian));
ip->ip_dst = htonl(ip2bit(Event->ip_dst, endian));

p_iphdr = iphdr_buf + IP_HDR_LEN;
len_iphdr = p_iphdr - iphdr_buf;

pkt_length = strlen(Event->message) + p_len + len_iphdr;
ip->ip_len = htons( len_payload + p_len + len_iphdr);  // Don't include eth frame. 
ip_checksum(iphdr_buf, len_iphdr);               // Valid checksum

pkt_length = len_eth + len_iphdr + p_len + len_payload; 
write_len += pkt_length;

/***************************************************************************/
/* Here we populate the data needed for the Packet portion of the Unified2 */
/* output.                                                                 */
/***************************************************************************/

logheader.sensor_id = 0;
logheader.linktype = htonl(1);                        // linktype set to ethernet (don't need tokenring, etc).
logheader.event_id = htonl(unified_event_id);
logheader.event_second = htonl(Event->event_time_sec);      
logheader.packet_second = htonl(Event->event_time_sec);
logheader.packet_microsecond = htonl(0);
logheader.packet_length = htonl(len_eth + len_iphdr + p_len + len_payload);

hdr.length = htonl(sizeof(Serial_Unified2Packet) - 4 + pkt_length);
hdr.type = htonl(UNIFIED2_PACKET);

if (SafeMemcpy(write_pkt_buffer, &hdr, sizeof(Serial_Unified2_Header),
               write_pkt_buffer, write_pkt_end) != SAFEMEM_SUCCESS)
   {
      sagan_log(0, "[%s, line %d] Failed to copy Serial_Unified2_Header.", __FILE__, __LINE__);
      return;
   }

if (SafeMemcpy(write_pkt_buffer + sizeof(Serial_Unified2_Header),
               &logheader, sizeof(Serial_Unified2Packet) - 4,
                write_pkt_buffer, write_pkt_end) != SAFEMEM_SUCCESS)
   {
      sagan_log(0, "[%s, line %d] Failed to copy Serial_Unified2Packet.", __FILE__, __LINE__ ); 
      return;
   }

/* packet_data stores our fake 'packet' information.  We now start building
 * the packet for use */

/* Ethernet */
for ( i = 0; i < len_eth; i++ ) packet_data[i] = eth_buf[i];

/* IP header */

for ( i = 0; i < len_iphdr; i++ ) packet_data[i + len_eth] = iphdr_buf[i];

/* UDP/TCP/ICMP header */
for ( i = 0; i < p_len-1; i++ ) packet_data[i + len_eth + len_iphdr] = packet_buf[i]; 

/* Payload ( Syslog message ) */

for ( i = 0; i < len_payload; i++ ) packet_data[i + len_eth + len_iphdr + p_len ] = Event->message[i];

/* Copy it to our Unified2/write_pkt_buffer */

if (SafeMemcpy(write_pkt_buffer + sizeof(Serial_Unified2_Header) +
               sizeof(Serial_Unified2Packet) - 4,
               packet_data, pkt_length,
               write_pkt_buffer, write_pkt_end) != SAFEMEM_SUCCESS)
   {
   sagan_log(0, "[%s, line %d] Failed to copy pseudo packet data.", __FILE__, __LINE__);
   return;
   }

      
Unified2Write(write_pkt_buffer, write_len);
unified_event_id++;           

}


/*****************************************************************************/
/* The below functions where taken from Sourcefire's "Snort" for direct      */
/* compatibility.                                                            */
/*****************************************************************************/

void Unified2CleanExit( void )
{
    if (config != NULL)
    {   
        if (config->unified2_stream != NULL)
            fclose(config->unified2_stream);
        free(config);
    }
}

static void Unified2RotateFile( void )
{
    fclose(config->unified2_stream);
    config->unified2_current = 0;
    Unified2InitFile();
}


void *SaganAlloc(unsigned long size)
{
    void *tmp;

    tmp = (void *) calloc(size, sizeof(char));

    if(tmp == NULL)
      sagan_log(1, "[%s, line %d] Unable to allocate memory! (%lu requested)", __FILE__, __LINE__, size);

    return tmp;
}

int SaganSnprintf(char *buf, size_t buf_size, const char *format, ...)
{
    va_list ap;
    int ret;

    if (buf == NULL || buf_size <= 0 || format == NULL)
        return SAGAN_SNPRINTF_ERROR;

    /* zero first byte in case an error occurs with
     * vsnprintf, so buffer is null terminated with
     * zero length */

    buf[0] = '\0';
    buf[buf_size - 1] = '\0';

    va_start(ap, format);

    ret = vsnprintf(buf, buf_size, format, ap);

    va_end(ap);

    if (ret < 0)
        return SAGAN_SNPRINTF_ERROR;

    if (buf[buf_size - 1] != '\0' || (size_t)ret >= buf_size)
    {
        /* result was truncated */
        buf[buf_size - 1] = '\0';
        return SAGAN_SNPRINTF_TRUNCATION;
    }

    return SAGAN_SNPRINTF_SUCCESS;
}


int SafeMemcpy(void *dst, const void *src, size_t n, const void *start, const void *end)
{
    void *tmp;

    if(n < 1)
    {
        ERRORRET;
    }

    if (!dst || !src || !start || !end)
    {
        ERRORRET;
    }

    tmp = ((uint8_t*)dst) + (n-1);
    if (tmp < dst)
    {
        ERRORRET;
    }

    if(!inBounds(start,end, dst) || !inBounds(start,end,tmp))
    {
        ERRORRET;
    }

    memcpy(dst, src, n);

    return SAFEMEM_SUCCESS;
}

int inBounds(const uint8_t *start, const uint8_t *end, const uint8_t *p)
{
    if(p >= start && p < end)
    {
        return 1;
    }

    return 0;
}

static void Unified2Write(uint8_t *buf, uint32_t buf_len)
{
    size_t fwcount = 0;
    int ffstatus = 0;

    /* Nothing to write or nothing to write to */
    if ((buf == NULL) || (config == NULL) || (config->unified2_stream == NULL))
        return;

    /* Don't use fsync().  It is a total performance killer */
    if (((fwcount = fwrite(buf, (size_t)buf_len, 1, config->unified2_stream)) != 1) ||
        ((ffstatus = fflush(config->unified2_stream)) != 0))
    {
        /* errno is saved just to avoid other intervening calls
         * (e.g. ErrorMessage) potentially reseting it to something else. */
        int error = errno;
        int max_retries = 3;

        /* On iterations other than the first, the only non-zero error will be
         * EINTR or interrupt.  Only iterate a maximum of max_retries times so 
         * there is no chance of infinite looping if for some reason the write
         * is constantly interrupted */
        while ((error != 0) && (max_retries != 0))
        {
            if (config->unified2_nostamp)
            {
            sagan_log(1, "[%s, line %d] Failed to write Unified2 file (%s): %s", __FILE__, __LINE__, config->unified2_filepath, strerror(error));
            }
            else
            {
            sagan_log(1, "[%s, line %d] Failed to write to Unified2 file. (%s.%u): %s", __FILE__, __LINE__, config->unified2_filepath, config->unified2_timestamp, strerror(error));
            }

            while ((error == EINTR) && (max_retries != 0))
            {
                max_retries--;

                /* Supposedly an interrupt can only occur before anything
                 * has been written.  Try again */

             sagan_log(0, "[%s, line %d] Got interrupt. Retry write to Unified2.", __FILE__, __LINE__);
                
            if (fwcount != 1)
                {
                    /* fwrite() failed.  Redo fwrite and fflush */
                    if (((fwcount = fwrite(buf, (size_t)buf_len, 1, config->unified2_stream)) == 1) &&
                        ((ffstatus = fflush(config->unified2_stream)) == 0))
                    {
                      sagan_log(0, "[%s, line %d] Write to Unified2 file succeeded!", __FILE__, __LINE__);
                      error = 0;
                      break;
                    }
                }
                else if ((ffstatus = fflush(config->unified2_stream)) == 0)
                {
                sagan_log(0, "[%s, line %d] Write to Unified2 file succeeded!", __FILE__, __LINE__);
                    error = 0;
                    break;
                }

                error = errno;
            sagan_log(1, "[%s, line %d] Retrying write to Unified2 file failed", __FILE__, __LINE__);
            }

            /* If we've reached the maximum number of interrupt retries,
             * just bail out of the main while loop */
            if (max_retries == 0)
                continue;

            switch (error)
            {
                case 0:
                    break;

                case EIO:
                    sagan_log(1, "[%s, line %d] Unified2 file is corrupt", __FILE__, __LINE__);
                    
                Unified2RotateFile();

                    if (config->unified2_nostamp)
                    {
                sagan_log(0, "[%s, line %d] New Unified2 file: %s", __FILE__, __LINE__, config->unified2_filepath);
                    }
                    else
                    {
                sagan_log(0, "[%s, line %d] New Unified2 file: %s.%u", __FILE__, __LINE__, config->unified2_filepath, config->unified2_timestamp);
                    }

                    if (((fwcount = fwrite(buf, (size_t)buf_len, 1, config->unified2_stream)) == 1) &&
                        ((ffstatus = fflush(config->unified2_stream)) == 0))
                    {
                    sagan_log(0, "[%s, line %d] Write to Unified2 file succeeded!", __FILE__, __LINE__);
                        error = 0;
                        break;
                    }

                    error = errno;

                    /* Loop again if interrupt */
                    if (error == EINTR)
                        break;

                    /* Write out error message again, then fall through and fatal */
                    if (config->unified2_nostamp)
                    {
                  sagan_log(1, "[%s, line %d] Failed to write to Unified2 file", __FILE__, __LINE__);
                    }
                    else
                    {
                  sagan_log(1, "[%s, line %d] Failed to write to Unified2 file", __FILE__, __LINE__);
                    }                    /* Fall through */

                case EAGAIN:  /* We're not in non-blocking mode */
                case EBADF:
                case EFAULT:
                case EFBIG:
                case EINVAL:
                case ENOSPC:
                case EPIPE:
                default:
                sagan_log(1, "[%s, line %d] Cannot write to device", __FILE__, __LINE__);
            }
        }

        if ((max_retries == 0) && (error != 0)) sagan_log(1, "[%s, line %d] Maximum number of interrupts exceeded.", __FILE__, __LINE__);
    }
    config->unified2_current += buf_len;
}

#endif

Generated by  Doxygen 1.6.0   Back to index