1 /*
2 ** Copyright (C) 2002-2009 Sourcefire, Inc.
3 ** Copyright (C) 1998-2002 Martin Roesch <roesch@sourcefire.com>
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License Version 2 as
7 ** published by the Free Software Foundation.  You may not use, modify or
8 ** distribute this program under any other version of the GNU General
9 ** Public License.
10 **
11 ** This program is distributed in the hope that it will be useful,
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 ** GNU General Public License for more details.
15 **
16 ** You should have received a copy of the GNU General Public License
17 ** along with this program; if not, write to the Free Software
18 ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 */
20 /* $Id$ */
21 
22 /* spo_alert_syslog
23  *
24  * Purpose:
25  *
26  * This module sends alerts to the syslog service.
27  *
28  * Arguments:
29  *
30  * Logging mechanism?
31  *
32  * Effect:
33  *
34  * Alerts are written to the syslog service with in the facility indicated by
35  * the module arguments.
36  *
37  * Comments:
38  *
39  * First try
40  *
41  */
42 
43 #ifdef HAVE_CONFIG_H
44 #include "config.h"
45 #endif
46 
47 #include <sys/types.h>
48 #include <syslog.h>
49 #include <stdlib.h>
50 #ifdef HAVE_STRINGS_H
51 #include <strings.h>
52 #endif
53 
54 #ifndef WIN32
55 #include <sys/socket.h>
56 #include <netinet/in.h>
57 #include <arpa/inet.h>
58 #endif /* !WIN32 */
59 
60 #include "barnyard2.h"
61 #include "decode.h"
62 #include "debug.h"
63 #include "map.h"
64 #include "mstring.h"
65 #include "parser.h"
66 #include "strlcatu.h"
67 #include "strlcpyu.h"
68 #include "plugbase.h"
69 #include "unified2.h"
70 #include "util.h"
71 #include "ipv6_port.h"
72 
73 typedef struct _CEFData
74 {
75     int facility;
76     int priority;
77     int options;
78 } CEFData;
79 
80 void AlertCEFInit(char *);
81 CEFData *ParseCEFArgs(char *);
82 void AlertCEF(Packet *, void *, u_int32_t, void *);
83 void AlertCEFCleanExit(int, void *);
84 void AlertCEFRestart(int, void *);
85 
86 
87 
88 /*
89  * Function: SetupCEF()
90  *
91  * Purpose: Registers the output plugin keyword and initialization
92  *          function into the output plugin list.  This is the function that
93  *          gets called from InitOutputPlugins() in plugbase.c.
94  *
95  * Arguments: None.
96  *
97  * Returns: void function
98  *
99  */
AlertCEFSetup(void)100 void AlertCEFSetup(void)
101 {
102     /* link the preprocessor keyword to the init function in
103        the preproc list */
104     RegisterOutputPlugin("alert_cef", OUTPUT_TYPE_FLAG__ALERT, AlertCEFInit);
105     DEBUG_WRAP(DebugMessage(DEBUG_INIT,"Output plugin: Alert-CEF is setup...\n"););
106 }
107 
108 
109 /*
110  * Function: AlertCEFInit(char *)
111  *
112  * Purpose: Calls the argument parsing function, performs final setup on data
113  *          structs, links the preproc function into the function list.
114  *
115  * Arguments: args => ptr to argument string
116  *
117  * Returns: void function
118  *
119  */
AlertCEFInit(char * args)120 void AlertCEFInit(char *args)
121 {
122     CEFData *data;
123     DEBUG_WRAP(DebugMessage(DEBUG_INIT, "Output: Alert-CEF Initialized\n"););
124 
125     /* parse the argument list from the rules file */
126     data = ParseCEFArgs(args);
127 
128     if (BcDaemonMode())
129         data->options |= LOG_PID;
130 
131     openlog("barnyard2", data->options, data->facility);
132 
133     DEBUG_WRAP(DebugMessage(DEBUG_INIT,"Linking CEF alert function to call list...\n"););
134 
135     /* Set the preprocessor function into the function list */
136     AddFuncToOutputList(AlertCEF, OUTPUT_TYPE__ALERT, data);
137     AddFuncToCleanExitList(AlertCEFCleanExit, data);
138     AddFuncToRestartList(AlertCEFRestart, data);
139 }
140 
141 
142 
143 /*
144  * Function: ParseCEFArgs(char *)
145  *
146  * Purpose: Process the preprocessor arguements from the rules file and
147  *          initialize the preprocessor's data struct.  This function doesn't
148  *          have to exist if it makes sense to parse the args in the init
149  *          function.
150  *
151  * Arguments: args => argument list
152  *
153  * Returns: void function
154  *
155  */
ParseCEFArgs(char * args)156 CEFData *ParseCEFArgs(char *args)
157 {
158 #ifdef WIN32
159     char *DEFAULT_SYSLOG_HOST = "127.0.0.1";
160     int   DEFAULT_SYSLOG_PORT = 514;
161     char **config_toks;
162     char **host_toks;
163     char  *host_string = args;
164     int num_config_toks, num_host_toks;
165 #endif
166     char **facility_toks;
167     char  *facility_string = args;
168     int num_facility_toks = 0;
169     int i = 0;
170     CEFData *data;
171     char *tmp;
172 
173     data = (CEFData *)SnortAlloc(sizeof(CEFData));
174 
175     /* default values for syslog output */
176     data->options = 0;
177     data->facility = LOG_AUTH;
178     data->priority = LOG_INFO;
179 
180     if(args == NULL)
181     {
182 		ErrorMessage("No arguments to alert_cef preprocessor!\n");
183 
184         return data;
185     }
186 
187     /*
188      * NON-WIN32:  Config should be in the format:
189      *   output alert_cef: LOG_AUTH LOG_ALERT
190      *
191      * WIN32:  Config can be in any of these formats:
192      *   output alert_cef: LOG_AUTH LOG_ALERT
193      *   output alert_cef: host=hostname, LOG_AUTH LOG_ALERT
194      *   output alert_cef: host=hostname:port, LOG_AUTH LOG_ALERT
195      */
196 
197 #ifdef WIN32
198     /* split the host/port part from the facilities/priorities part */
199     config_toks = mSplit(args, ",", 2, &num_config_toks, '\\');
200     switch( num_config_toks )
201     {
202         case 1:  /* config consists of only facility/priority info */
203             LogMessage("alert_cef output processor is defaulting to syslog "
204                     "server on %s port %d!\n",
205                     DEFAULT_SYSLOG_HOST, DEFAULT_SYSLOG_PORT);
206             strncpy(pv.syslog_server, DEFAULT_SYSLOG_HOST, STD_BUF-1);
207             pv.syslog_server_port = DEFAULT_SYSLOG_PORT;
208             facility_string = config_toks[0];
209             break;
210 
211         case 2:  /* config consists of host info, and facility/priority info */
212             host_string     = config_toks[0];
213             facility_string = config_toks[1];
214             /* split host_string into "host" vs. "server" vs. "port" */
215             host_toks = mSplit(host_string, "=:", 3, &num_host_toks, 0);
216             if(num_host_toks > 0 && strcmp(host_toks[0], "host") != 0 )
217             {
218                 FatalError("Badly formed alert_cef 'host' "
219                         "argument ('%s')\n", host_string);
220             }
221             /* check for empty strings */
222             if((num_host_toks >= 1 && strlen(host_toks[0]) == 0) ||
223                     (num_host_toks >= 2 && strlen(host_toks[1]) == 0) ||
224                     (num_host_toks >= 3 && strlen(host_toks[2]) == 0))
225             {
226                 FatalError("Badly formed alert_cef 'host' "
227                         "argument ('%s')\n", host_string);
228             }
229             switch(num_host_toks)
230             {
231                 case 2:  /* ie,  host=localhost (defaults to port 514) */
232                     strncpy(pv.syslog_server, host_toks[1], STD_BUF-1);
233                     pv.syslog_server_port = DEFAULT_SYSLOG_PORT;  /* default */
234                     break;
235 
236                 case 3:  /* ie.  host=localhost:514 */
237                     strncpy(pv.syslog_server, host_toks[1], STD_BUF-1);
238                     pv.syslog_server_port = atoi(host_toks[2]);
239                     if( pv.syslog_server_port == 0 )
240                     {
241                         pv.syslog_server_port = DEFAULT_SYSLOG_PORT; /*default*/
242                         LogMessage("WARNING => alert_cef port "
243                                 "appears to be non-numeric ('%s').  Defaulting "
244                                 "to port %d!\n", host_toks[2], DEFAULT_SYSLOG_PORT);
245 
246                     }
247                     break;
248 
249                 default:  /* badly formed, should never occur */
250                     FatalError("Badly formed alert_cef 'host' "
251                             "argument ('%s')\n", host_string);
252             }
253             mSplitFree(&host_toks, num_host_toks);
254             break;
255 
256         default:
257             FatalError("Badly formed alert_cef arguments ('%s')\n", args);
258     }
259 
260     DEBUG_WRAP(DebugMessage(DEBUG_INIT, "Logging alerts to syslog "
261                 "server %s on port %d\n", pv.syslog_server,
262                 pv.syslog_server_port););
263     mSplitFree(&config_toks, num_facility_toks);
264 #endif /* WIN32 */
265 
266 
267 
268     /* tokenize the facility/priority argument list */
269     facility_toks = mSplit(facility_string, " |", 31, &num_facility_toks, '\\');
270 
271     for(i = 0; i < num_facility_toks; i++)
272     {
273         if(*facility_toks[i] == '$')
274         {
275             if((tmp = VarGet(facility_toks[i]+1)) == NULL)
276             {
277                 FatalError("Undefined variable %s\n", facility_toks[i]);
278             }
279         }
280         else
281         {
282             tmp = facility_toks[i];
283         }
284 
285         /* possible openlog options */
286 
287 #ifdef LOG_CONS
288         if(!strcasecmp("LOG_CONS", tmp))
289         {
290             data->options |= LOG_CONS;
291         }
292         else
293 #endif
294 #ifdef LOG_NDELAY
295         if(!strcasecmp("LOG_NDELAY", tmp))
296         {
297             data->options |= LOG_NDELAY;
298         }
299         else
300 #endif
301 #ifdef LOG_PERROR
302         if(!strcasecmp("LOG_PERROR", tmp))
303         {
304             data->options |= LOG_PERROR;
305         }
306         else
307 #endif
308 #ifdef LOG_PID
309         if(!strcasecmp("LOG_PID", tmp))
310         {
311             data->options |= LOG_PID;
312         }
313         else
314 #endif
315 #ifdef LOG_NOWAIT
316         if(!strcasecmp("LOG_NOWAIT", tmp))
317         {
318             data->options |= LOG_NOWAIT;
319         }
320         else
321 #endif
322         /* possible openlog facilities */
323 
324 #ifdef LOG_AUTHPRIV
325         if(!strcasecmp("LOG_AUTHPRIV", tmp))
326         {
327             data->facility = LOG_AUTHPRIV;
328         }
329         else
330 #endif
331 #ifdef LOG_AUTH
332         if(!strcasecmp("LOG_AUTH", tmp))
333         {
334             data->facility = LOG_AUTH;
335         }
336         else
337 #endif
338 #ifdef LOG_DAEMON
339         if(!strcasecmp("LOG_DAEMON", tmp))
340         {
341             data->facility = LOG_DAEMON;
342         }
343         else
344 #endif
345 #ifdef LOG_LOCAL0
346         if(!strcasecmp("LOG_LOCAL0", tmp))
347         {
348             data->facility = LOG_LOCAL0;
349         }
350         else
351 #endif
352 #ifdef LOG_LOCAL1
353         if(!strcasecmp("LOG_LOCAL1", tmp))
354         {
355             data->facility = LOG_LOCAL1;
356         }
357         else
358 #endif
359 #ifdef LOG_LOCAL2
360         if(!strcasecmp("LOG_LOCAL2", tmp))
361         {
362             data->facility = LOG_LOCAL2;
363         }
364         else
365 #endif
366 #ifdef LOG_LOCAL3
367         if(!strcasecmp("LOG_LOCAL3", tmp))
368         {
369             data->facility = LOG_LOCAL3;
370         }
371         else
372 #endif
373 #ifdef LOG_LOCAL4
374         if(!strcasecmp("LOG_LOCAL4", tmp))
375         {
376             data->facility = LOG_LOCAL4;
377         }
378         else
379 #endif
380 #ifdef LOG_LOCAL5
381         if(!strcasecmp("LOG_LOCAL5", tmp))
382         {
383             data->facility = LOG_LOCAL5;
384         }
385         else
386 #endif
387 #ifdef LOG_LOCAL6
388         if(!strcasecmp("LOG_LOCAL6", tmp))
389         {
390             data->facility = LOG_LOCAL6;
391         }
392         else
393 #endif
394 #ifdef LOG_LOCAL7
395         if(!strcasecmp("LOG_LOCAL7", tmp))
396         {
397             data->facility = LOG_LOCAL7;
398         }
399         else
400 #endif
401 #ifdef LOG_USER
402         if(!strcasecmp("LOG_USER", tmp))
403         {
404             data->facility = LOG_USER;
405         }
406         else
407 #endif
408 
409         /* possible syslog priorities */
410 
411 #ifdef LOG_EMERG
412         if(!strcasecmp("LOG_EMERG", tmp))
413         {
414             data->priority = LOG_EMERG;
415         }
416         else
417 #endif
418 #ifdef LOG_ALERT
419         if(!strcasecmp("LOG_ALERT", tmp))
420         {
421             data->priority = LOG_ALERT;
422         }
423         else
424 #endif
425 #ifdef LOG_CRIT
426         if(!strcasecmp("LOG_CRIT", tmp))
427         {
428             data->priority = LOG_CRIT;
429         }
430         else
431 #endif
432 #ifdef LOG_ERR
433         if(!strcasecmp("LOG_ERR", tmp))
434         {
435             data->priority = LOG_ERR;
436         }
437         else
438 #endif
439 #ifdef LOG_WARNING
440         if(!strcasecmp("LOG_WARNING", tmp))
441         {
442             data->priority = LOG_WARNING;
443         }
444         else
445 #endif
446 #ifdef LOG_NOTICE
447         if(!strcasecmp("LOG_NOTICE", tmp))
448         {
449             data->priority = LOG_NOTICE;
450         }
451         else
452 #endif
453 #ifdef LOG_INFO
454         if(!strcasecmp("LOG_INFO", tmp))
455         {
456             data->priority = LOG_INFO;
457         }
458         else
459 #endif
460 #ifdef LOG_DEBUG
461         if(!strcasecmp("LOG_DEBUG", tmp))
462         {
463             data->priority = LOG_DEBUG;
464         }
465         else
466 #endif
467         {
468             LogMessage("WARNING => Unrecognized syslog "
469                     "facility/priority: %s\n", tmp);
470         }
471     }
472     mSplitFree(&facility_toks, num_facility_toks);
473 
474     return data;
475 }
476 
477 
478 /*
479  * Function: PreprocFunction(Packet *)
480  *
481  * Purpose: Perform the preprocessor's intended function.  This can be
482  *          simple (statistics collection) or complex (IP defragmentation)
483  *          as you like.  Try not to destroy the performance of the whole
484  *          system by trying to do too much....
485  *
486  * Arguments: p => pointer to the current packet data struct
487  *
488  * Returns: void function
489  *
490  */
AlertCEF(Packet * p,void * event,u_int32_t event_type,void * arg)491 void AlertCEF(Packet *p, void *event, u_int32_t event_type, void *arg)
492 {
493     char sip[16];
494     char dip[16];
495 #define SYSLOG_BUF  1024
496     int                 cef_severity;
497     char                cef_message[SYSLOG_BUF];
498     CEFData			 	*data;
499 	SigNode				*sn;
500 	ClassType			*cn;
501 
502 	if ( p == NULL || event == NULL || arg == NULL )
503 	{
504 		return;
505 	}
506 
507 	data = (CEFData *)arg;
508 	sn = GetSigByGidSid(ntohl(((Unified2EventCommon *)event)->generator_id),
509 			    ntohl(((Unified2EventCommon *)event)->signature_id),
510 			    ntohl(((Unified2EventCommon *)event)->signature_revision));
511 
512 	cn = ClassTypeLookupById(barnyard2_conf, ntohl(((Unified2EventCommon *)event)->classification_id));
513 
514     /* Remove this check when we support IPv6 below. */
515     /* sip and dip char arrays need to change size for IPv6. */
516     if (!IS_IP4(p))
517     {
518         return;
519     }
520 
521     SnortSnprintf(cef_message, SYSLOG_BUF, "CEF:0|snort|barnyard2|%s", VERSION);
522 
523     if(p && IPH_IS_VALID(p))
524     {
525         if (strlcpy(sip, inet_ntoa(GET_SRC_ADDR(p)), sizeof(sip)) >= sizeof(sip))
526             return;
527 
528         if (strlcpy(dip, inet_ntoa(GET_DST_ADDR(p)), sizeof(dip)) >= sizeof(dip))
529             return;
530 
531         /* calculate CEF severity */
532         cef_severity = (11 - ntohl(((Unified2EventCommon *)event)->priority_id));
533         if ( cef_severity < 0 )
534             cef_severity = 0;
535         else if ( cef_severity > 10 )
536             cef_severity = 10;
537 
538 
539         if( SnortSnprintfAppend(cef_message, SYSLOG_BUF, "%lu:%lu:%lu|%s|%d|",
540                           (unsigned long) ntohl(((Unified2EventCommon *)event)->generator_id),
541                           (unsigned long) ntohl(((Unified2EventCommon *)event)->signature_id),
542                           (unsigned long) ntohl(((Unified2EventCommon *)event)->signature_revision),
543                           sn == NULL ? "ALERT" : sn->msg, cef_severity) != SNORT_SNPRINTF_SUCCESS )
544                 return ;
545 
546         if( (GET_IPH_PROTO(p) != IPPROTO_TCP && GET_IPH_PROTO(p) != IPPROTO_UDP) || p->frag_flag )
547         {
548             if( SnortSnprintfAppend(cef_message, SYSLOG_BUF, "src=%s dst=%s proto=%s",
549                                     sip, dip,
550                                     protocol_names[GET_IPH_PROTO(p)]) != SNORT_SNPRINTF_SUCCESS )
551                 return;
552         }
553         else
554         {
555             if( SnortSnprintfAppend(cef_message, SYSLOG_BUF, "src=%s dst=%s spt=%i dpt=%i proto=%s",
556                                  sip, dip, p->sp, p->dp,
557                                  protocol_names[GET_IPH_PROTO(p)]) != SNORT_SNPRINTF_SUCCESS )
558                 return ;
559         }
560 
561         syslog(data->priority, "%s", cef_message);
562     }
563 
564     return;
565 }
566 
567 
AlertCEFCleanExit(int signal,void * arg)568 void AlertCEFCleanExit(int signal, void *arg)
569 {
570     CEFData *data = (CEFData *)arg;
571     DEBUG_WRAP(DebugMessage(DEBUG_LOG, "AlertCEFCleanExit\n"););
572     /* free memory from SyslogData */
573     if(data)
574         free(data);
575 
576     closelog();
577 }
578 
AlertCEFRestart(int signal,void * arg)579 void AlertCEFRestart(int signal, void *arg)
580 {
581     CEFData *data = (CEFData *)arg;
582     DEBUG_WRAP(DebugMessage(DEBUG_LOG, "AlertCEFRestartFunc\n"););
583     /* free memory from SyslogData */
584     if(data)
585         free(data);
586 
587     closelog();
588 }
589 
590