1 /*
2 **
3 ** Copyright (C) 2008-2013 Ian Firns (SecurixLive) <dev@securixlive.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 **
21 */
22 
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 #include <sys/types.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <unistd.h>
33 #include <fcntl.h>
34 #include <dirent.h>
35 #include <sys/stat.h>
36 
37 #include "barnyard2.h"
38 #include "debug.h"
39 #include "plugbase.h"
40 #include "spooler.h"
41 #include "unified2.h"
42 #include "util.h"
43 
44 
45 
46 /*
47 ** PRIVATE FUNCTIONS
48 */
49 Spooler *spoolerOpen(const char *, const char *, uint32_t);
50 int spoolerClose(Spooler *);
51 int spoolerReadRecordHeader(Spooler *);
52 int spoolerReadRecord(Spooler *);
53 void spoolerProcessRecord(Spooler *, int);
54 void spoolerFreeRecord(Record *record);
55 
56 int spoolerWriteWaldo(Waldo *, Spooler *);
57 int spoolerOpenWaldo(Waldo *, uint8_t);
58 int spoolerCloseWaldo(Waldo *);
59 
60 
61 int spoolerPacketCacheAdd(Spooler *, Packet *);
62 int spoolerPacketCacheClear(Spooler *);
63 
64 int spoolerEventCachePush(Spooler *, uint32_t, void *);
65 EventRecordNode * spoolerEventCacheGetByEventID(Spooler *, uint32_t);
66 EventRecordNode * spoolerEventCacheGetHead(Spooler *);
67 uint8_t spoolerEventCacheHeadUsed(Spooler *);
68 int spoolerEventCacheClean(Spooler *);
69 
70 /* Find the next spool file timestamp extension with a value equal to or
71  * greater than timet.  If extension != NULL, the extension will be
72  * returned.
73  *
74  * @retval 0    file found
75  * @retval -1   error
76  * @retval 1    no file found
77  *
78  * Bugs:  This function presumes a 1 character delimeter between the base
79  * filename and the extension
80  */
FindNextExtension(const char * dirpath,const char * filebase,uint32_t timestamp,uint32_t * extension)81 static int FindNextExtension(const char *dirpath, const char *filebase,
82         uint32_t timestamp, uint32_t *extension)
83 {
84     DIR                 *dir = NULL;
85     struct dirent       *dir_entry;
86     size_t              filebase_len;
87     uint32_t            timestamp_min = 0;
88     char *endptr;
89 
90     DEBUG_WRAP(DebugMessage(DEBUG_SPOOLER,"Looking in %s %s\n", dirpath, filebase););
91 
92     /* peform sanity checks */
93     if (dirpath == NULL || filebase == NULL)
94         return SPOOLER_EXTENSION_EPARAM;
95 
96     /* calculate filebase length */
97     filebase_len = strlen(filebase);
98 
99     /* open the directory */
100     if ( !(dir=opendir(dirpath)) )
101     {
102         LogMessage("ERROR: Unable to open directory '%s' (%s)\n", dirpath,
103                 strerror(errno));
104         return SPOOLER_EXTENSION_EOPEN;
105     }
106 
107     /* step through each entry in the directory */
108     while ( (dir_entry=readdir(dir)) )
109     {
110         unsigned long   file_timestamp;
111 
112         if (strncmp(filebase, dir_entry->d_name, filebase_len) != 0)
113             continue;
114 
115         /* this is a file we may want */
116         file_timestamp = strtol(dir_entry->d_name + filebase_len + 1, &endptr, 10);
117         if ((errno == ERANGE) || (*endptr != '\0'))
118         {
119             LogMessage("WARNING: Can't extract timestamp extension from '%s'"
120                     "using base '%s'\n", dir_entry->d_name, filebase);
121 
122             continue;
123         }
124 
125         /* exact match */
126         if (timestamp != 0 && file_timestamp == timestamp)
127         {
128             timestamp_min = file_timestamp;
129             break;
130         }
131         /* possible overshoot */
132         else if (file_timestamp > timestamp)
133         {
134             /*  realign the minimum timestamp threshold */
135             if ( timestamp_min == 0 || (file_timestamp < timestamp_min) )
136                 timestamp_min = file_timestamp;
137         }
138     }
139 
140     closedir(dir);
141 
142     /* no newer extensions were found */
143     if (timestamp_min == 0)
144         return SPOOLER_EXTENSION_NONE;
145 
146     /* update the extension variable if it exists */
147     if (extension != NULL)
148         *extension = timestamp_min;
149 
150     return SPOOLER_EXTENSION_FOUND;
151 }
152 
spoolerOpen(const char * dirpath,const char * filename,uint32_t extension)153 Spooler *spoolerOpen(const char *dirpath, const char *filename, uint32_t extension)
154 {
155     Spooler             *spooler = NULL;
156     int                 ret;
157 
158     /* perform sanity checks */
159     if ( filename == NULL )
160         return NULL;
161 
162     /* create the spooler structure and allocate all memory */
163     spooler = (Spooler *)SnortAlloc(sizeof(Spooler));
164 
165     RegisterSpooler(spooler);
166 
167     /* allocate some extra structures required (ie. Packet) */
168 
169     spooler->fd = -1;
170 
171     /* build the full filepath */
172     if (extension == 0)
173     {
174         ret = SnortSnprintf(spooler->filepath, MAX_FILEPATH_BUF, "%s", filename);
175     }
176     else
177     {
178         ret = SnortSnprintf(spooler->filepath, MAX_FILEPATH_BUF, "%s/%s.%u", dirpath, filename,
179                 extension);
180     }
181 
182     /* sanity check the filepath */
183     if (ret != SNORT_SNPRINTF_SUCCESS)
184     {
185 	UnRegisterSpooler(spooler);
186         spoolerClose(spooler);
187         FatalError("spooler: filepath too long!\n");
188     }
189 
190     spooler->timestamp = extension;
191 
192     LogMessage("Opened spool file '%s'\n", spooler->filepath);
193 
194     /* open the file non-blocking */
195     if ( (spooler->fd=open(spooler->filepath, O_RDONLY | O_NONBLOCK, 0)) == -1 )
196     {
197         LogMessage("ERROR: Unable to open log spool file '%s' (%s)\n",
198                     spooler->filepath, strerror(errno));
199 	UnRegisterSpooler(spooler);
200         spoolerClose(spooler);
201         spooler = NULL;
202         return NULL;
203     }
204 
205     /* set state to initially be open */
206     spooler->state = SPOOLER_STATE_OPENED;
207 
208     spooler->ifn = GetInputPlugin("unified2");
209 
210     if (spooler->ifn == NULL)
211     {
212 	UnRegisterSpooler(spooler);
213         spoolerClose(spooler);
214         spooler = NULL;
215         FatalError("ERROR: No suitable input plugin found!\n");
216     }
217 
218     return spooler;
219 }
220 
spoolerClose(Spooler * spooler)221 int spoolerClose(Spooler *spooler)
222 {
223     /* perform sanity checks */
224     if (spooler == NULL)
225         return -1;
226 
227     LogMessage("Closing spool file '%s'. Read %d records\n",
228                spooler->filepath, spooler->record_idx);
229 
230     if (spooler->fd != -1)
231         close(spooler->fd);
232 
233     /* free record */
234     spoolerFreeRecord(&spooler->record);
235 
236     free(spooler);
237     spooler = NULL;
238 
239     return 0;
240 }
241 
RegisterSpooler(Spooler * spooler)242 void RegisterSpooler(Spooler *spooler)
243 {
244     Barnyard2Config *bc =  BcGetConfig();
245 
246     if(!bc)
247 	return;
248 
249 
250     if(bc->spooler)
251     {
252 	/* XXX */
253 	FatalError("[%s()], can't register spooler. \n",
254 		   __FUNCTION__);
255     }
256     else
257     {
258 	bc->spooler = spooler;
259     }
260 
261     return;
262 }
263 
UnRegisterSpooler(Spooler * spooler)264 void UnRegisterSpooler(Spooler *spooler)
265 {
266     Barnyard2Config *bc =  BcGetConfig();
267 
268     if(!bc)
269 	return;
270 
271     if(bc->spooler != spooler)
272     {
273 	/* XXX */
274 	FatalError("[%s()], can't un-register spooler. \n",
275 		   __FUNCTION__);
276     }
277     else
278     {
279 	bc->spooler = NULL;
280     }
281 
282     return;
283 }
284 
285 
286 
spoolerReadRecordHeader(Spooler * spooler)287 int spoolerReadRecordHeader(Spooler *spooler)
288 {
289     int                 ret;
290 
291     /* perform sanity checks */
292     if ( spooler == NULL )
293         return -1;
294 
295     if (spooler->state != SPOOLER_STATE_OPENED && spooler->state != SPOOLER_STATE_RECORD_READ)
296     {
297         LogMessage("ERROR: Invalid attempt to read record header.\n");
298         return -1;
299     }
300 
301     if (spooler->ifn->readRecordHeader)
302     {
303         ret = spooler->ifn->readRecordHeader(spooler);
304 
305         if (ret != 0)
306             return ret;
307 
308         spooler->state = SPOOLER_STATE_HEADER_READ;
309         spooler->offset = 0;
310     }
311     else
312     {
313         LogMessage("WARNING: No function defined to read header.\n");
314         return -1;
315     }
316 
317     return 0;
318 }
319 
spoolerReadRecord(Spooler * spooler)320 int spoolerReadRecord(Spooler *spooler)
321 {
322     int                 ret;
323 
324     /* perform sanity checks */
325     if (spooler == NULL)
326         return -1;
327 
328     if (spooler->state != SPOOLER_STATE_HEADER_READ)
329     {
330         LogMessage("ERROR: Invalid attempt to read record.\n");
331         return -1;
332     }
333 
334     if (spooler->ifn->readRecord)
335     {
336         ret = spooler->ifn->readRecord(spooler);
337 
338         if (ret != 0)
339             return ret;
340 
341         spooler->state = SPOOLER_STATE_RECORD_READ;
342         spooler->record_idx++;
343         spooler->offset = 0;
344     }
345     else
346     {
347         LogMessage("WARNING: No function defined to read header.\n");
348         return -1;
349     }
350 
351     return 0;
352 }
353 
ProcessBatch(const char * dirpath,const char * filename)354 int ProcessBatch(const char *dirpath, const char *filename)
355 {
356     Spooler             *spooler = NULL;
357     int                 ret = 0;
358     int                 pb_ret = 0;
359 
360     /* Open the spool file */
361     if ( (spooler=spoolerOpen("", filename, 0)) == NULL)
362     {
363         FatalError("Unable to create spooler: %s\n", strerror(errno));
364     }
365 
366     while (exit_signal == 0 && pb_ret == 0)
367     {
368 	/* for SIGUSR1 / dropstats */
369 	SignalCheck();
370 
371         switch (spooler->state)
372         {
373             case SPOOLER_STATE_OPENED:
374             case SPOOLER_STATE_RECORD_READ:
375                 ret = spoolerReadRecordHeader(spooler);
376 
377                 if (ret == BARNYARD2_READ_EOF)
378                 {
379                     pb_ret = -1;
380                 }
381                 else if (ret != 0)
382                 {
383                     LogMessage("ERROR: Input file '%s' is corrupted! (%u)\n",
384                                 spooler->filepath, ret);
385                     pb_ret = -1;
386                 }
387                 break;
388 
389             default:
390                 ret = spoolerReadRecord(spooler);
391 
392                 if (ret == 0)
393                 {
394                     /* process record, firing output as required */
395                     spoolerProcessRecord(spooler, 1);
396                 }
397                 else if (ret == BARNYARD2_READ_EOF)
398                 {
399                     pb_ret = -1;
400                 }
401                 else
402                 {
403                     LogMessage("ERROR: Input file '%s' is corrupted! (%u)\n",
404                                 spooler->filepath, ret);
405                     pb_ret = -1;
406                 }
407 
408                 spoolerFreeRecord(&spooler->record);
409                 break;
410         }
411     }
412 
413     /* we've finished with the spooler so destroy and cleanup */
414     spoolerClose(spooler);
415     spooler = NULL;
416 
417     return pb_ret;
418 }
419 
420 /*
421 ** ProcessContinuous(const char *dirpath, const char *filebase, uint32_t record_start, time_t timestamp)
422 **
423 **
424 **
425 */
ProcessContinuous(const char * dirpath,const char * filebase,uint32_t record_start,uint32_t timestamp)426 int ProcessContinuous(const char *dirpath, const char *filebase,
427         uint32_t record_start, uint32_t timestamp)
428 {
429     Spooler             *spooler = NULL;
430     int                 ret = 0;
431     int                 pc_ret = 0;
432     int                 new_file_available = 0;
433     int                 waiting_logged = 0;
434     uint32_t            skipped = 0;
435     uint32_t            extension = 0;
436 
437     u_int32_t waldo_timestamp = 0;
438     waldo_timestamp = timestamp; /* fix possible bug by keeping invocated timestamp at the time of the initial call */
439 
440     if (BcProcessNewRecordsOnly())
441     {
442         LogMessage("Processing new records only.\n");
443 
444         /* Find newest file extension */
445         while (FindNextExtension(dirpath, filebase, timestamp, &extension) == 0)
446         {
447             if (timestamp > 0 && BcLogVerbose())
448                 LogMessage("Skipping file: %s/%s.%u\n", dirpath,
449                         filebase, timestamp);
450 
451             timestamp = extension + 1;
452         }
453 
454         timestamp = extension;
455     }
456 
457     /* Start the main process loop */
458     while (exit_signal == 0)
459     {
460 	/* for SIGUSR1 / dropstats */
461 	SignalCheck();
462 
463         /* no spooler exists so let's create one */
464         if (spooler == NULL)
465         {
466             /* find the next file to spool */
467             ret = FindNextExtension(dirpath, filebase, timestamp, &extension);
468 
469 	    /* The file found is not the same as specified in the waldo,
470                thus we need to reset record_start, since we are obviously not processing the same file*/
471             if(waldo_timestamp != extension)
472             {
473                 record_start = 0; /* There is no danger to resetting record_start to 0
474                                      if called timestamp is not the same */
475             }
476 
477 
478             /* no new extensions found */
479             if (ret == SPOOLER_EXTENSION_NONE)
480             {
481                 if (waiting_logged == 0)
482                 {
483                     if (BcProcessNewRecordsOnly())
484                        LogMessage("Skipped %u old records\n", skipped);
485 
486                     LogMessage("Waiting for new spool file\n");
487                     waiting_logged = 1;
488                     barnyard2_conf->process_new_records_only_flag = 0;
489                 }
490 
491                 sleep(1);
492                 continue;
493             }
494             /* an error occured whilst looking for new extensions */
495             else if (ret != SPOOLER_EXTENSION_FOUND)
496             {
497                 LogMessage("ERROR: Unable to find the next spool file!\n");
498                 exit_signal = -1;
499                 pc_ret = -1;
500                 continue;
501             }
502 
503             /* found a new extension so create a new spooler */
504             if ( (spooler=spoolerOpen(dirpath, filebase, extension)) == NULL )
505             {
506                 LogMessage("ERROR: Unable to create spooler!\n");
507                 exit_signal = -1;
508                 pc_ret = -1;
509 		continue;
510             }
511 	    else
512 	    {
513 		/* Make sure we create a new waldo even if we did not have processed an event */
514 		if(waldo_timestamp != extension)
515 		{
516 		    spooler->record_idx = 0;
517 		    spoolerWriteWaldo(&barnyard2_conf->waldo, spooler);
518 		}
519 		waiting_logged = 0;
520 
521 		/* set timestamp to ensure we look for a newer file next time */
522 		timestamp = extension + 1;
523 	    }
524 
525             continue;
526         }
527 
528         /* act according to current spooler state */
529         switch(spooler->state)
530         {
531             case SPOOLER_STATE_OPENED:
532             case SPOOLER_STATE_RECORD_READ:
533                 ret = spoolerReadRecordHeader(spooler);
534                 break;
535 
536             case SPOOLER_STATE_HEADER_READ:
537                 ret = spoolerReadRecord(spooler);
538                 break;
539 
540             default:
541                 LogMessage("ERROR: Invalid spooler state (%i). Closing '%s'\n",
542                             spooler->state, spooler->filepath);
543 
544 #ifndef WIN32
545                 /* archive the spool file */
546                 if (BcArchiveDir() != NULL)
547                     ArchiveFile(spooler->filepath, BcArchiveDir());
548 #endif
549 
550                 /* we've finished with the spooler so destroy and cleanup */
551 		UnRegisterSpooler(spooler);
552                 spoolerClose(spooler);
553                 spooler = NULL;
554 
555                 record_start = 0;
556                 break;
557         }
558 
559         /* if no spooler exists, we are waiting for a newer file to arrive */
560         if (spooler == NULL)
561             continue;
562 
563         if (ret == 0)
564         {
565             /* check for a successful record read */
566             if (spooler->state == SPOOLER_STATE_RECORD_READ)
567             {
568                 if (record_start > 0)
569                 {
570                     /* skip this record */
571                     record_start--;
572                     DEBUG_WRAP(DebugMessage(DEBUG_SPOOLER,"Skipping due to record start offset (%lu)...\n",
573                                  (long unsigned)record_start););
574 
575                     /* process record to ensure correlation context, but DO NOT fire output*/
576                     spoolerProcessRecord(spooler, 0);
577                 }
578                 else if (BcProcessNewRecordsOnly())
579                 {
580                     /* skip this record */
581                     skipped++;
582                     DEBUG_WRAP(DebugMessage(DEBUG_SPOOLER,"Skipping due to new records only flag...\n"););
583 
584                     /* process record to ensure correlation context, but DO NOT fire output*/
585                     spoolerProcessRecord(spooler, 0);
586                 }
587                 else
588                 {
589                     /* process record, firing output as required */
590                     spoolerProcessRecord(spooler, 1);
591                 }
592             }
593 
594             spoolerFreeRecord(&spooler->record);
595         }
596         else if (ret == BARNYARD2_FILE_ERROR)
597         {
598             LogMessage("ERROR: Reading current file!\n");
599             exit_signal = -3;
600             pc_ret = -1;
601             continue;
602         }
603         else
604         {
605             if (new_file_available)
606             {
607                 switch(spooler->state)
608                 {
609                     case SPOOLER_STATE_OPENED:
610                     case SPOOLER_STATE_HEADER_READ:
611                     case SPOOLER_STATE_RECORD_READ:
612                         if (ret == BARNYARD2_ETRUNC)
613                             LogMessage("Truncated record in '%s'\n", spooler->filepath);
614                         break;
615 
616                     default:
617                         if (ret == BARNYARD2_READ_PARTIAL)
618                             LogMessage("Partial read from '%s'\n",
619                                     spooler->filepath);
620                         break;
621                 }
622 
623                 /* archive the file */
624                 if (BcArchiveDir() != NULL)
625                     ArchiveFile(spooler->filepath, BcArchiveDir());
626 
627                 /* close (ie. destroy and cleanup) the spooler so we can rotate */
628 		UnRegisterSpooler(spooler);
629                 spoolerClose(spooler);
630                 spooler = NULL;
631 
632                 record_start = 0;
633                 new_file_available = 0;
634             }
635             else
636             {
637                 ret = FindNextExtension(dirpath, filebase, timestamp, NULL);
638                 if (ret == 0)
639                 {
640                     new_file_available = 1;
641                 }
642                 else if (ret == -1)
643                 {
644                     LogMessage("ERROR: Looking for next spool file!\n");
645                     exit_signal = -3;
646                     pc_ret = -1;
647                 }
648                 else
649                 {
650                     if (!waiting_logged)
651                     {
652                         if (BcProcessNewRecordsOnly())
653                             LogMessage("Skipped %u old records\n", skipped);
654 
655                         LogMessage("Waiting for new data\n");
656                         waiting_logged = 1;
657                         barnyard2_conf->process_new_records_only_flag = 0;
658                     }
659 
660                     sleep(1);
661                     continue;
662                 }
663             }
664         }
665     }
666 
667     /* close waldo if appropriate */
668     if(barnyard2_conf)
669     	spoolerCloseWaldo(&barnyard2_conf->waldo);
670 
671     return pc_ret;
672 }
673 
ProcessContinuousWithWaldo(Waldo * waldo)674 int ProcessContinuousWithWaldo(Waldo *waldo)
675 {
676     if (waldo == NULL)
677         return -1;
678 
679     return ProcessContinuous(waldo->data.spool_dir, waldo->data.spool_filebase,
680                              waldo->data.record_idx, waldo->data.timestamp);
681 }
682 
683 
684 /*
685 ** RECORD PROCESSING EVENTS
686 */
687 
spoolerProcessRecord(Spooler * spooler,int fire_output)688 void spoolerProcessRecord(Spooler *spooler, int fire_output)
689 {
690     struct pcap_pkthdr      pkth;
691     uint32_t                type;
692     EventRecordNode         *ernCache;
693 
694     /* convert type once */
695     type = ntohl(((Unified2RecordHeader *)spooler->record.header)->type);
696 
697     /* increment the stats */
698     pc.total_records++;
699     switch (type)
700     {
701         case UNIFIED2_PACKET:
702             pc.total_packets++;
703             break;
704         case UNIFIED2_IDS_EVENT:
705         case UNIFIED2_IDS_EVENT_IPV6:
706         case UNIFIED2_IDS_EVENT_MPLS:
707         case UNIFIED2_IDS_EVENT_IPV6_MPLS:
708         case UNIFIED2_IDS_EVENT_VLAN:
709         case UNIFIED2_IDS_EVENT_IPV6_VLAN:
710             pc.total_events++;
711             break;
712         default:
713             pc.total_unknown++;
714     }
715 
716     /* check if it's packet */
717     if (type == UNIFIED2_PACKET)
718     {
719         /* convert event id once */
720         uint32_t event_id = ntohl(((Unified2Packet *)spooler->record.data)->event_id);
721 
722         /* check if there is a previously cached event that matches this event id */
723         ernCache = spoolerEventCacheGetByEventID(spooler, event_id);
724 
725         /* allocate space for the packet and construct the packet header */
726         spooler->record.pkt = SnortAlloc(sizeof(Packet));
727 
728         pkth.caplen = ntohl(((Unified2Packet *)spooler->record.data)->packet_length);
729         pkth.len = pkth.caplen;
730         pkth.ts.tv_sec = ntohl(((Unified2Packet *)spooler->record.data)->packet_second);
731         pkth.ts.tv_usec = ntohl(((Unified2Packet *)spooler->record.data)->packet_microsecond);
732 
733         /* decode the packet from the Unified2Packet information */
734         datalink = ntohl(((Unified2Packet *)spooler->record.data)->linktype);
735         DecodePacket(datalink, spooler->record.pkt, &pkth,
736                      ((Unified2Packet *)spooler->record.data)->packet_data);
737 
738 	/* This is a fixup for portscan... */
739 	if( (spooler->record.pkt->iph == NULL) &&
740 	    ((spooler->record.pkt->inner_iph != NULL) && (spooler->record.pkt->inner_iph->ip_proto == 255)))
741 	    {
742 		spooler->record.pkt->iph = spooler->record.pkt->inner_iph;
743 	    }
744 
745         /* check if it's been re-assembled */
746         if (spooler->record.pkt->packet_flags & PKT_REBUILT_STREAM)
747         {
748             DEBUG_WRAP(DebugMessage(DEBUG_SPOOLER,"Packet has been rebuilt from a stream\n"););
749         }
750 
751         /* if the packet and cached event share the same id */
752         if ( ernCache != NULL )
753         {
754             /* call output plugins with a "SPECIAL" alert format (both Event and Packet information) */
755             DEBUG_WRAP(DebugMessage(DEBUG_SPOOLER,"Firing SPECIAL style (Packet+Event)\n"););
756 
757             if ( fire_output &&
758                  ((ernCache->used == 0) || BcAlertOnEachPacketInStream()) )
759                 CallOutputPlugins(OUTPUT_TYPE__SPECIAL,
760                               spooler->record.pkt,
761                               ernCache->data,
762                               ernCache->type);
763 
764             /* indicate that the cached event has been used */
765             ernCache->used = 1;
766         }
767         else
768         {
769             /* fire the event cache head only if not already used (ie dirty) */
770             if ( spoolerEventCacheHeadUsed(spooler) == 0 )
771             {
772                 ernCache = spoolerEventCacheGetHead(spooler);
773 
774                 /* call output plugins with an "ALERT" format (cached Event information only) */
775                 DEBUG_WRAP(DebugMessage(DEBUG_SPOOLER,"Firing ALERT style (Event only)\n"););
776 
777                 if (fire_output)
778                     CallOutputPlugins(OUTPUT_TYPE__ALERT,
779                                       NULL,
780                                       ernCache->data,
781                                       ernCache->type);
782 
783                 /* set the event cache used flag */
784                 ernCache->used = 1;
785             }
786 
787             /* call output plugins with a "LOG" format (Packet information only) */
788             DEBUG_WRAP(DebugMessage(DEBUG_SPOOLER,"Firing LOG style (Packet)\n"););
789 
790             if (fire_output)
791                 CallOutputPlugins(OUTPUT_TYPE__SPECIAL,
792                                   spooler->record.pkt,
793                                   NULL,
794                                   0);
795         }
796 
797         /* free the memory allocated in this function */
798         free(spooler->record.pkt);
799         spooler->record.pkt = NULL;
800 
801         /* waldo operations occur after the output plugins are called */
802         if (fire_output)
803             spoolerWriteWaldo(&barnyard2_conf->waldo, spooler);
804     }
805     /* check if it's an event of known sorts */
806     else if(type == UNIFIED2_IDS_EVENT || type == UNIFIED2_IDS_EVENT_IPV6 ||
807             type == UNIFIED2_IDS_EVENT_MPLS || type == UNIFIED2_IDS_EVENT_IPV6_MPLS ||
808             type == UNIFIED2_IDS_EVENT_VLAN || type == UNIFIED2_IDS_EVENT_IPV6_VLAN)
809     {
810         /* fire the cached event only if not already used (ie dirty) */
811         if ( spoolerEventCacheHeadUsed(spooler) == 0 )
812         {
813             /* call output plugins with an "ALERT" format (cached Event information only) */
814             DEBUG_WRAP(DebugMessage(DEBUG_SPOOLER,"Firing ALERT style (Event only)\n"););
815 
816             ernCache = spoolerEventCacheGetHead(spooler);
817 
818             if (fire_output)
819                 CallOutputPlugins(OUTPUT_TYPE__ALERT,
820                               NULL,
821                               ernCache->data,
822                               ernCache->type);
823 
824             /* flush the event cache flag */
825             ernCache->used = 1;
826         }
827 
828         /* cache new data */
829         spoolerEventCachePush(spooler, type, spooler->record.data);
830         spooler->record.data = NULL;
831 
832         /* waldo operations occur after the output plugins are called */
833         if (fire_output)
834             spoolerWriteWaldo(&barnyard2_conf->waldo, spooler);
835     }
836     else if (type == UNIFIED2_EXTRA_DATA)
837     {
838         /* waldo operations occur after the output plugins are called */
839         if (fire_output)
840             spoolerWriteWaldo(&barnyard2_conf->waldo, spooler);
841     }
842     else
843     {
844         /* fire the cached event only if not already used (ie dirty) */
845         if ( spoolerEventCacheHeadUsed(spooler) == 0 )
846         {
847             /* call output plugins with an "ALERT" format (cached Event information only) */
848             DEBUG_WRAP(DebugMessage(DEBUG_SPOOLER,"Firing ALERT style (Event only)\n"););
849 
850             ernCache = spoolerEventCacheGetHead(spooler);
851 
852             if (fire_output)
853                 CallOutputPlugins(OUTPUT_TYPE__ALERT,
854                               NULL,
855                               ernCache->data,
856                               ernCache->type);
857 
858             /* waldo operations occur after the output plugins are called */
859             if (fire_output)
860                 spoolerWriteWaldo(&barnyard2_conf->waldo, spooler);
861         }
862     }
863 
864     /* clean the cache out */
865     spoolerEventCacheClean(spooler);
866 }
867 
spoolerEventCachePush(Spooler * spooler,uint32_t type,void * data)868 int spoolerEventCachePush(Spooler *spooler, uint32_t type, void *data)
869 {
870     EventRecordNode     *ernNode;
871 
872     DEBUG_WRAP(DebugMessage(DEBUG_SPOOLER,"Caching event...\n"););
873 
874     /* allocate memory */
875     ernNode = (EventRecordNode *)SnortAlloc(sizeof(EventRecordNode));
876 
877     /* create the new node */
878     ernNode->used = 0;
879     ernNode->type = type;
880     ernNode->data = data;
881 
882     /* add new events to the front of the cache */
883     ernNode->next = spooler->event_cache;
884 
885     spooler->event_cache = ernNode;
886     spooler->events_cached++;
887 
888     DEBUG_WRAP(DebugMessage(DEBUG_SPOOLER,"Cached event: %d\n", spooler->events_cached););
889 
890     return 0;
891 }
892 
spoolerEventCacheGetByEventID(Spooler * spooler,uint32_t event_id)893 EventRecordNode *spoolerEventCacheGetByEventID(Spooler *spooler, uint32_t event_id)
894 {
895     EventRecordNode     *ernCurrent = spooler->event_cache;
896 
897     while (ernCurrent != NULL)
898     {
899         if ( ntohl(((Unified2EventCommon *)ernCurrent->data)->event_id) == event_id )
900         {
901             return ernCurrent;
902         }
903 
904         ernCurrent = ernCurrent->next;
905     }
906 
907     return NULL;
908 }
909 
spoolerEventCacheGetHead(Spooler * spooler)910 EventRecordNode *spoolerEventCacheGetHead(Spooler *spooler)
911 {
912     if ( spooler == NULL )
913         return NULL;
914 
915     return spooler->event_cache;
916 }
917 
spoolerEventCacheHeadUsed(Spooler * spooler)918 uint8_t spoolerEventCacheHeadUsed(Spooler *spooler)
919 {
920     if ( spooler == NULL || spooler->event_cache == NULL )
921         return 255;
922 
923     return spooler->event_cache->used;
924 }
925 
spoolerEventCacheClean(Spooler * spooler)926 int spoolerEventCacheClean(Spooler *spooler)
927 {
928     EventRecordNode     *ernCurrent = NULL;
929     EventRecordNode     *ernPrev = NULL;
930     EventRecordNode     *ernNext = NULL;
931 
932     if (spooler == NULL || spooler->event_cache == NULL )
933         return 1;
934 
935     ernPrev = spooler->event_cache;
936     ernCurrent = spooler->event_cache;
937 
938     while (ernCurrent != NULL && spooler->events_cached > barnyard2_conf->event_cache_size )
939     {
940 	ernNext = ernCurrent->next;
941 
942 	if ( ernCurrent->used == 1 )
943         {
944 	    /* Delete from list */
945 	    if (ernCurrent == spooler->event_cache)
946 	    {
947                 spooler->event_cache = ernNext;
948 	    }
949             else
950 	    {
951                 ernPrev->next = ernNext;
952 	    }
953 
954             spooler->events_cached--;
955 
956 	    if(ernCurrent->data != NULL)
957 	    {
958 		free(ernCurrent->data);
959 	    }
960 
961 	    if(ernCurrent != NULL)
962 	    {
963 		free(ernCurrent);
964 	    }
965         }
966 
967 	if(ernCurrent != NULL)
968 	{
969 	    ernPrev = ernCurrent;
970 	}
971 
972 	ernCurrent = ernNext;
973 
974     }
975 
976     return 0;
977 }
978 
spoolerEventCacheFlush(Spooler * spooler)979 void spoolerEventCacheFlush(Spooler *spooler)
980 {
981     EventRecordNode *next_ptr = NULL;
982     EventRecordNode *evt_ptr = NULL;
983 
984     if (spooler == NULL || spooler->event_cache == NULL )
985         return;
986 
987     evt_ptr = spooler->event_cache;
988 
989     while(evt_ptr != NULL)
990     {
991 	next_ptr = evt_ptr->next;
992 
993 	if(evt_ptr->data)
994 	{
995 	    free(evt_ptr->data);
996 	    evt_ptr->data = NULL;
997 	}
998 
999 	free(evt_ptr);
1000 
1001 	evt_ptr = next_ptr;
1002     }
1003 
1004     spooler->event_cache = NULL;
1005 
1006     return;
1007 }
1008 
1009 
spoolerFreeRecord(Record * record)1010 void spoolerFreeRecord(Record *record)
1011 {
1012     if (record->data)
1013     {
1014         free(record->data);
1015     }
1016 
1017 
1018     record->data = NULL;
1019 }
1020 
1021 /*
1022 ** WALDO FILE OPERATIONS
1023 */
1024 
1025 /*
1026 ** spoolerOpenWaldo(Waldo *waldo, uint8_t mode)
1027 **
1028 ** Description:
1029 **   Open the waldo file, non-blocking, defined in the Waldo structure
1030 */
spoolerOpenWaldo(Waldo * waldo,uint8_t mode)1031 int spoolerOpenWaldo(Waldo *waldo, uint8_t mode)
1032 {
1033     struct stat         waldo_info;
1034     int                 waldo_file_flags = 0;
1035     mode_t              waldo_file_mode = 0;
1036     int                 ret = 0;
1037 
1038     /* check if waldo file is already open and in the correct mode */
1039     if ( (waldo->state & WALDO_STATE_OPEN) && (waldo->fd != -1) && (waldo->mode == mode) )
1040     {
1041         return WALDO_FILE_SUCCESS;
1042     }
1043 
1044     /* check that a waldo file has been specified */
1045     if ( waldo->filepath[0] == '\0' )
1046     {
1047         return WALDO_FILE_EEXIST;
1048     }
1049 
1050     /* stat the file to see it exists */
1051     ret = stat(waldo->filepath, &waldo_info);
1052 
1053     if ( mode == WALDO_MODE_READ )
1054         waldo_file_flags = ( O_RDONLY );
1055     else if ( mode == WALDO_MODE_WRITE )
1056     {
1057         waldo_file_flags = ( O_CREAT | O_WRONLY );
1058         waldo_file_mode = ( S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ) ;
1059     }
1060 
1061     /* open the file non-blocking */
1062     if ( (waldo->fd=open(waldo->filepath, waldo_file_flags, waldo_file_mode)) == -1 )
1063     {
1064         LogMessage("WARNING: Unable to open waldo file '%s' (%s)\n", waldo->filepath,
1065                     strerror(errno));
1066         return WALDO_FILE_EOPEN;
1067     }
1068 
1069     if ( ret != 0 )
1070         return WALDO_FILE_EEXIST;
1071 
1072     /* set waldo state and mode */
1073     waldo->state |= WALDO_STATE_OPEN;
1074     waldo->mode = mode;
1075 
1076     return WALDO_FILE_SUCCESS;
1077 }
1078 
1079 /*
1080 ** spoolerCloseWaldo(Waldo *waldo)
1081 **
1082 ** Description:
1083 **   Open the waldo file, non-blocking, defined in the Waldo structure
1084 **
1085 */
spoolerCloseWaldo(Waldo * waldo)1086 int spoolerCloseWaldo(Waldo *waldo)
1087 {
1088     if(waldo == NULL)
1089 	return WALDO_STRUCT_EMPTY;
1090 
1091     /* check we have a valid file descriptor */
1092     if (waldo->state & WALDO_STATE_OPEN)
1093         return WALDO_FILE_EOPEN;
1094 
1095     /* close the file */
1096     if(waldo->fd > 0)
1097 	close(waldo->fd);
1098 
1099     waldo->fd = -1;
1100 
1101     /* reset open state and mode */
1102     waldo->state &= ( ~WALDO_STATE_OPEN );
1103     waldo->mode = WALDO_MODE_NULL;
1104 
1105     return WALDO_FILE_SUCCESS;
1106 }
1107 
1108 /*
1109 ** spoolReadWaldo(Waldo *waldo)
1110 **
1111 ** Description:
1112 **   Read the waldo file defined in the Waldo structure and populate all values
1113 ** within.
1114 **
1115 */
spoolerReadWaldo(Waldo * waldo)1116 int spoolerReadWaldo(Waldo *waldo)
1117 {
1118     int                 ret;
1119     WaldoData           wd;
1120 
1121     /* check if we have a file in the correct mode (READ) */
1122     if ( waldo->mode != WALDO_MODE_READ )
1123     {
1124 	/* close waldo if appropriate */
1125 	if(barnyard2_conf)
1126 	    spoolerCloseWaldo(waldo);
1127 
1128         if ( (ret=spoolerOpenWaldo(waldo, WALDO_MODE_READ)) != WALDO_FILE_SUCCESS )
1129             return ret;
1130     }
1131     else if ( ! (waldo->state & WALDO_STATE_OPEN) )
1132     {
1133         if ( (ret=spoolerOpenWaldo(waldo, WALDO_MODE_READ)) != WALDO_FILE_SUCCESS )
1134             return ret;
1135     }
1136     else
1137     {
1138         /* ensure we are at the beggining since we must be open and in read */
1139         lseek(waldo->fd, 0, SEEK_SET);
1140     }
1141 
1142     /* read values into temporary WaldoData structure */
1143     ret = read(waldo->fd, &wd, sizeof(WaldoData));
1144 
1145     /* TODO: additional checks on the waldo file data to test corruption */
1146     if ( ret != sizeof(WaldoData) )
1147         return WALDO_FILE_ETRUNC;
1148 
1149     /* copy waldo file contents to the directory structure */
1150     memcpy(&waldo->data, &wd, sizeof(WaldoData));
1151 
1152     DEBUG_WRAP(DebugMessage(DEBUG_SPOOLER,
1153         "Waldo read\n\tdir:  %s\n\tbase: %s\n\ttime: %lu\n\tidx:  %d\n",
1154         waldo->data.spool_dir, waldo->data.spool_filebase,
1155         waldo->data.timestamp, waldo->data.record_idx););
1156 
1157 
1158     /* close waldo if appropriate */
1159     if(barnyard2_conf)
1160 	spoolerCloseWaldo(waldo);
1161 
1162     return WALDO_FILE_SUCCESS;
1163 }
1164 
1165 /*
1166 ** spoolerWriteWaldo(Waldo *waldo)
1167 **
1168 ** Description:
1169 **   Write to the waldo file
1170 **
1171 */
spoolerWriteWaldo(Waldo * waldo,Spooler * spooler)1172 int spoolerWriteWaldo(Waldo *waldo, Spooler *spooler)
1173 {
1174     int                 ret;
1175 
1176     /* check if we are using waldo files */
1177     if ( ! (waldo->state & WALDO_STATE_ENABLED) )
1178         return WALDO_STRUCT_EMPTY;
1179 
1180     /* check that a waldo file exists before continued */
1181     if (waldo == NULL)
1182         return WALDO_STRUCT_EMPTY;
1183 
1184     /* update fields */
1185     waldo->data.timestamp = spooler->timestamp;
1186     waldo->data.record_idx = spooler->record_idx;
1187 
1188     /* check if we have a file in the correct mode (READ) */
1189     if ( waldo->mode != WALDO_MODE_WRITE )
1190     {
1191 	/* close waldo if appropriate */
1192         if(barnyard2_conf)
1193             spoolerCloseWaldo(waldo);
1194 
1195 
1196         spoolerOpenWaldo(waldo, WALDO_MODE_WRITE);
1197     }
1198     else if ( ! (waldo->state & WALDO_STATE_OPEN) )
1199     {
1200         spoolerOpenWaldo(waldo, WALDO_MODE_WRITE);
1201     }
1202     else
1203     {
1204         /* ensure we are at the start since we must be open and in write */
1205         lseek(waldo->fd, 0, SEEK_SET);
1206     }
1207 
1208     /* write values */
1209     ret = write(waldo->fd, &waldo->data, sizeof(WaldoData));
1210 
1211     if (ret != sizeof(WaldoData) )
1212         return WALDO_FILE_ETRUNC;
1213 
1214     DEBUG_WRAP(DebugMessage(DEBUG_SPOOLER,
1215         "Waldo write\n\tdir:  %s\n\tbase: %s\n\ttime: %lu\n\tidx:  %d\n",
1216         waldo->data.spool_dir, waldo->data.spool_filebase,
1217         waldo->data.timestamp, waldo->data.record_idx););
1218 
1219     return WALDO_FILE_SUCCESS;
1220 }
1221 
1222