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