06dc8b4c62e078efc0505eb527c5f24c066db504
[ossec-hids.git] / src / shared / report_op.c
1 /* @(#) $Id$ */
2
3 /* Copyright (C) 2009 Trend Micro Inc.
4  * All rights reserved.
5  *
6  * This program is a free software; you can redistribute it
7  * and/or modify it under the terms of the GNU General Public
8  * License (version 2) as published by the FSF - Free Software
9  * Foundation
10  */
11
12
13 #include "shared.h"
14
15
16 /** Helper functions. */
17 FILE *__g_rtype = NULL;
18 void l_print_out(const char *msg, ...)
19 {
20     va_list args;
21     va_start(args, msg);
22
23     if(__g_rtype)
24     {
25         (void)vfprintf(__g_rtype, msg, args);
26         (void)fprintf(__g_rtype, "\n");
27     }
28     else
29     {
30         (void)vfprintf(stderr, msg, args);
31         (void)fprintf(stderr, "\n");
32     }
33     va_end(args);
34 }
35
36
37 /* Sort function used by OSStore sort.
38  * Returns if d1 > d2. 
39  */
40 void *_os_report_sort_compare(void *d1, void *d2)
41 {
42    OSList *d1l = (OSList *)d1;
43    OSList *d2l = (OSList *)d2; 
44
45    if(d1l->currently_size > d2l->currently_size)
46    {
47        return(d1l);
48    }
49
50    return(NULL);
51 }
52
53
54 /* Print output header. */
55 void _os_header_print(int t, char *hname)
56 {
57     if(!t)
58     {
59         l_print_out("Top entries for '%s':", hname);
60         l_print_out("------------------------------------------------");
61     }
62     else
63     {
64         l_print_out("Related entries for '%s':", hname);
65         l_print_out("------------------------------------------------");
66     }
67 }
68
69
70 /* Compares if the id is present in the string. */
71 int _os_report_str_int_compare(char *str, int id)
72 {
73     int pt_check = 0;
74     
75     do
76     {
77         if((*str == ',')||(*str == ' '))
78         {
79             pt_check = 0;
80             continue;
81         }
82         else if(*str == '\0')
83         {
84             break;
85         }
86         else if(isdigit((int)*str))
87         {
88             if(pt_check == 0)
89             {
90                 if(id == atoi(str))
91                 {
92                     return(1);
93                 }
94             }
95             pt_check = 1;
96         }
97         else
98         {
99             return(-1);
100         }
101     }while(*str++ != '\0');
102
103     return(0);
104 }
105
106
107
108 /* Check if the al_data should be filtered. */
109 int _os_report_check_filters(alert_data *al_data, report_filter *r_filter)
110 {
111     /* Checking for the filters. */
112     if(r_filter->group)
113     {
114         if(!strstr(al_data->group, r_filter->group))
115         {
116             return(0);
117         }
118     }
119     if(r_filter->rule)
120     {
121         if(_os_report_str_int_compare(r_filter->rule, al_data->rule) != 1)
122         {
123             return(0);
124         }
125     }
126     if(r_filter->location)
127     {
128         if(!OS_Match(r_filter->location, al_data->location))
129         {
130             return(0);
131         }
132     }
133     if(r_filter->level)
134     {
135         if(al_data->level < atoi(r_filter->level))
136         {
137             return(0);
138         }
139     }
140     if(r_filter->srcip)
141     {
142         if(!strstr(al_data->srcip, r_filter->srcip))
143         {
144             return(0);
145         }
146     }
147     if(r_filter->user)
148     {
149         if(!strstr(al_data->user, r_filter->user))
150         {
151             return(0);
152         }
153     }
154     return(1);
155 }
156
157
158
159 /* Sets the proper value for the related entries. */
160 int _report_filter_value(char *filter_by, int prev_filter)
161 {
162     if(strcmp(filter_by, "group") == 0)
163     {
164         if(!(prev_filter & REPORT_REL_GROUP))
165         {
166             prev_filter|=REPORT_REL_GROUP;
167         }
168         return(prev_filter);
169     }
170     else if(strcmp(filter_by, "rule") == 0)
171     {
172         if(!(prev_filter & REPORT_REL_RULE))
173         {
174             prev_filter|=REPORT_REL_RULE;
175         }
176         return(prev_filter);
177     }
178     else if(strcmp(filter_by, "level") == 0)
179     {
180         if(!(prev_filter & REPORT_REL_LEVEL))
181         {
182             prev_filter|=REPORT_REL_LEVEL;
183         }
184         return(prev_filter);
185     }
186     else if(strcmp(filter_by, "location") == 0)
187     {
188         if(!(prev_filter & REPORT_REL_LOCATION))
189         {
190             prev_filter|=REPORT_REL_LOCATION;
191         }
192         return(prev_filter);
193     }
194     else if(strcmp(filter_by, "srcip") == 0)
195     {
196         if(!(prev_filter & REPORT_REL_SRCIP))
197         {
198             prev_filter|=REPORT_REL_SRCIP;
199         }
200         return(prev_filter);
201     }
202     else if(strcmp(filter_by, "user") == 0)
203     {
204         if(!(prev_filter & REPORT_REL_USER))
205         {
206             prev_filter|=REPORT_REL_USER;
207         }
208         return(prev_filter);
209     }
210     else
211     {
212         merror("%s: ERROR: Invalid relation '%s'.", __local_name, filter_by);
213         return(-1);
214     }   
215 }
216
217
218
219 /* Prints related entries. */
220 int _os_report_print_related(int print_related, OSList *st_data)
221 {
222     OSListNode *list_entry;
223     alert_data *list_aldata;
224     alert_data *saved_aldata;
225     
226                 
227     list_entry = OSList_GetFirstNode(st_data);
228     while(list_entry)
229     {
230         saved_aldata = (alert_data *)list_entry->data;
231         
232         /* Removing duplicates. */
233         list_entry = list_entry->prev;
234         while(list_entry)
235         {
236             if(print_related & REPORT_REL_LOCATION)
237             {
238                 list_aldata = (alert_data *)list_entry->data;
239                 if(strcmp(list_aldata->location, saved_aldata->location) == 0)
240                 {
241                     break;
242                 }
243             }
244
245             else if(print_related & REPORT_REL_GROUP)
246             {
247                 list_aldata = (alert_data *)list_entry->data;
248                 if(strcmp(list_aldata->group, saved_aldata->group) == 0)
249                 {
250                     break;
251                 }
252             }
253
254             else if(print_related & REPORT_REL_RULE)
255             {
256                 list_aldata = (alert_data *)list_entry->data;
257                 if(list_aldata->rule == saved_aldata->rule)
258                 {
259                     break;
260                 }
261             }
262
263             else if(print_related & REPORT_REL_USER)
264             {
265                 list_aldata = (alert_data *)list_entry->data;
266                 if(strcmp(list_aldata->user, saved_aldata->user) == 0)
267                 {
268                     break;
269                 }
270             }
271
272             else if(print_related & REPORT_REL_SRCIP)
273             {
274                 list_aldata = (alert_data *)list_entry->data;
275                 if(strcmp(list_aldata->srcip, saved_aldata->srcip) == 0)
276                 {
277                     break;
278                 }
279             }
280
281             else if(print_related & REPORT_REL_LEVEL)
282             {
283                 list_aldata = (alert_data *)list_entry->data;
284                 if(list_aldata->level == saved_aldata->level)
285                 {
286                     break;
287                 }
288             }
289             list_entry = list_entry->prev;
290         }
291
292         if(!list_entry)
293         {
294             if(print_related & REPORT_REL_LOCATION)
295                 l_print_out("   location: '%s'", saved_aldata->location);
296             else if(print_related & REPORT_REL_GROUP)
297                 l_print_out("   group: '%s'", saved_aldata->group);
298             else if(print_related & REPORT_REL_RULE)
299                 l_print_out("   rule: '%d'", saved_aldata->rule);
300             else if(print_related & REPORT_REL_SRCIP)
301                 l_print_out("   srcip: '%s'", saved_aldata->srcip);
302             else if(print_related & REPORT_REL_USER)
303                 l_print_out("   user: '%s'", saved_aldata->user);
304             else if(print_related & REPORT_REL_LEVEL)
305                 l_print_out("   level: '%d'", saved_aldata->level);
306         }
307
308         list_entry = OSList_GetNextNode(st_data);
309     }
310
311     return(0);
312 }
313
314
315
316 /* Add the entry to the hash. */
317 int _os_report_add_tostore(char *key, OSStore *top, void *data)
318 {
319     OSList *top_list;
320
321     /* Adding data to the hash. */
322     top_list = OSStore_Get(top, key);
323     if(top_list)
324     {
325         OSList_AddData(top_list, data);
326     }
327     else
328     {
329         top_list = OSList_Create();
330         if(!top_list)
331         {
332             merror(MEM_ERROR, __local_name);
333             return(0);
334         }
335         OSList_AddData(top_list, data);
336
337         OSStore_Put(top, key, top_list);
338     }
339
340     return(1);
341 }
342
343
344
345 void os_report_printtop(void *topstore_pt, char *hname, int print_related)
346 {
347     int dopdout = 0;
348     OSStore *topstore = (OSStore *)topstore_pt;
349     OSStoreNode *next_node;
350     
351     next_node = OSStore_GetFirstNode(topstore);
352     while(next_node)
353     {
354         OSList *st_data = (OSList *)next_node->data;
355         char *lkey = (char *)next_node->key;
356
357
358         /* With location we leave more space to be clearer. */
359         if(!print_related)
360         {
361             if(strlen(lkey) > 46)
362             {
363                 lkey[44] = '.';
364                 lkey[45] = '.';
365                 lkey[46] = '\0';
366             }
367
368             if(!dopdout)
369             {
370                 _os_header_print(print_related, hname);
371                 dopdout = 1;
372             }
373             l_print_out("%-48s|%-8d|", (char *)next_node->key, st_data->currently_size);
374         }
375
376
377         /* Print each destination. */
378         else
379         {
380             if(!dopdout)
381             {
382                 _os_header_print(print_related, hname);
383                 dopdout = 1;
384             }
385             l_print_out("%-48s|%-8d|", (char *)next_node->key, st_data->currently_size);
386
387             if(print_related & REPORT_REL_LOCATION)
388                 _os_report_print_related(REPORT_REL_LOCATION, st_data);
389             if(print_related & REPORT_REL_SRCIP)
390                 _os_report_print_related(REPORT_REL_SRCIP, st_data);
391             if(print_related & REPORT_REL_USER)
392                 _os_report_print_related(REPORT_REL_USER, st_data);
393             if(print_related & REPORT_REL_RULE)
394                 _os_report_print_related(REPORT_REL_RULE, st_data);
395             if(print_related & REPORT_REL_GROUP)
396                 _os_report_print_related(REPORT_REL_GROUP, st_data);
397             if(print_related & REPORT_REL_LEVEL)
398                 _os_report_print_related(REPORT_REL_LEVEL, st_data);
399
400         }
401
402         next_node = next_node->next;
403     }
404
405
406     if(dopdout == 1)
407     {
408         l_print_out(" ");
409         l_print_out(" ");
410     }
411     return; 
412 }
413
414
415
416 void os_ReportdStart(report_filter *r_filter)
417 {
418     int alerts_processed = 0;
419     int alerts_filtered = 0;
420     char *first_alert = NULL;
421     char *last_alert = NULL;
422     void **data_to_clean = NULL;
423     
424     
425     time_t tm;     
426     struct tm *p;       
427     
428
429     file_queue *fileq;
430     alert_data *al_data;
431
432
433     /* Getting current time before starting */
434     tm = time(NULL);
435     p = localtime(&tm); 
436
437
438
439
440     /* Initating file queue - to read the alerts */
441     os_calloc(1, sizeof(file_queue), fileq);
442
443     if(r_filter->report_type == REPORT_TYPE_DAILY && r_filter->filename)
444     {
445         fileq->fp = fopen(r_filter->filename, "r");
446         if(!fileq->fp)
447         {
448             merror("%s: ERROR: Unable to open alerts file to generate report.", __local_name);
449             return;
450         }
451         if(r_filter->fp)
452         {
453             __g_rtype = r_filter->fp;
454         }
455     }
456     else
457     {
458         fileq->fp = stdin;
459     }
460
461
462     /* Creating top hashes. */
463     r_filter->top_user = OSStore_Create();
464     r_filter->top_srcip = OSStore_Create();
465     r_filter->top_level = OSStore_Create();
466     r_filter->top_rule = OSStore_Create();
467     r_filter->top_group = OSStore_Create();
468     r_filter->top_location = OSStore_Create();
469     
470     Init_FileQueue(fileq, p, CRALERT_READ_ALL|CRALERT_FP_SET);
471
472
473     /* Reading the alerts. */
474     while(1)
475     {
476         /* Get message if available */
477         al_data = Read_FileMon(fileq, p, 1);
478         if(!al_data)
479         {
480             break;
481         }
482
483         alerts_processed++;
484         
485
486         /* Checking the filters. */
487         if(!_os_report_check_filters(al_data, r_filter))
488         {
489             FreeAlertData(al_data);
490             continue;
491         }
492         
493         
494         alerts_filtered++;
495         data_to_clean = os_AddPtArray(al_data, data_to_clean);
496
497
498         /* Setting first and last alert for summary. */
499         if(!first_alert)
500             first_alert = al_data->date;
501         last_alert = al_data->date;
502         
503         
504         /* Adding source ip if it is set properly. */
505         if(strcmp(al_data->srcip, "(none)") != 0)
506             _os_report_add_tostore(al_data->srcip, r_filter->top_srcip, al_data);
507
508         
509         /* Adding user if it is set properly. */
510         if(strcmp(al_data->user, "(none)") != 0)
511             _os_report_add_tostore(al_data->user, r_filter->top_user, al_data);
512
513
514         /* Adding level and severity. */
515         {
516             char mlevel[16];
517             char mrule[76 +1];
518             mrule[76] = '\0';
519             snprintf(mlevel, 16, "Severity %d" , al_data->level);
520             snprintf(mrule, 76, "%d - %s" , al_data->rule, al_data->comment);
521             
522             _os_report_add_tostore(strdup(mlevel), r_filter->top_level, 
523                                    al_data);
524             _os_report_add_tostore(strdup(mrule), r_filter->top_rule, 
525                                    al_data);
526         }
527
528         /* Dealing with the group. */
529         {
530             char *tmp_str;
531             char **mgroup;
532
533             mgroup = OS_StrBreak(',', al_data->group, 32);
534             if(mgroup)
535             {
536                 while(*mgroup)
537                 {
538                     tmp_str = *mgroup;
539                     while(*tmp_str == ' ')
540                         tmp_str++;
541                     if(*tmp_str == '\0')
542                     {
543                         mgroup++;
544                         continue;
545                     }
546                     
547                     _os_report_add_tostore(tmp_str, r_filter->top_group, 
548                                            al_data);
549                     mgroup++;
550                 }
551             }
552             else
553             {
554                 tmp_str = al_data->group;
555                 while(*tmp_str == ' ')
556                     tmp_str++;
557                 if(*tmp_str != '\0')
558                 {
559                     _os_report_add_tostore(tmp_str, r_filter->top_group, 
560                                            al_data);
561                 }
562             }
563         }
564
565
566         /* Adding to the location top filter. */        
567         _os_report_add_tostore(al_data->location, r_filter->top_location, 
568                                al_data);
569     }
570
571     /* No report available */
572     if(alerts_filtered == 0)
573     {
574         if(!r_filter->report_name)
575             merror("%s: INFO: Report completed and zero alerts post-filter.", __local_name);
576         else
577             merror("%s: INFO: Report '%s' completed and zero alerts post-filter.", __local_name, r_filter->report_name);    
578         return;
579     }
580
581     
582     if(r_filter->report_name)
583         verbose("%s: INFO: Report '%s' completed. Creating output...", __local_name, r_filter->report_name);
584     else
585         verbose("%s: INFO: Report completed. Creating output...", __local_name);    
586
587
588     l_print_out(" ");
589     if(r_filter->report_name)
590         l_print_out("Report '%s' completed.", r_filter->report_name);
591     else
592         l_print_out("Report completed. ==");
593     l_print_out("------------------------------------------------");
594             
595     l_print_out("->Processed alerts: %d", alerts_processed);
596     l_print_out("->Post-filtering alerts: %d", alerts_filtered);
597     l_print_out("->First alert: %s", first_alert);
598     l_print_out("->Last alert: %s", last_alert);
599     l_print_out(" ");
600     l_print_out(" ");
601     
602     OSStore_Sort(r_filter->top_srcip, _os_report_sort_compare);
603     OSStore_Sort(r_filter->top_user,  _os_report_sort_compare);
604     OSStore_Sort(r_filter->top_level, _os_report_sort_compare);
605     OSStore_Sort(r_filter->top_group, _os_report_sort_compare);
606     OSStore_Sort(r_filter->top_location, _os_report_sort_compare);
607     OSStore_Sort(r_filter->top_rule, _os_report_sort_compare);
608     
609     if(r_filter->top_srcip)
610         os_report_printtop(r_filter->top_srcip, "Source ip", 0);
611     
612     if(r_filter->top_user)
613         os_report_printtop(r_filter->top_user, "Username", 0);
614     
615     if(r_filter->top_level)
616         os_report_printtop(r_filter->top_level, "Level", 0);
617     
618     if(r_filter->top_group)
619         os_report_printtop(r_filter->top_group, "Group", 0);
620     
621     if(r_filter->top_location)
622         os_report_printtop(r_filter->top_location, "Location", 0);
623     
624     if(r_filter->top_rule)
625         os_report_printtop(r_filter->top_rule, "Rule", 0);
626
627
628     /* Print related events. */
629     if(r_filter->related_srcip)
630         os_report_printtop(r_filter->top_srcip, "Source ip", 
631                            r_filter->related_srcip);
632
633     if(r_filter->related_user)
634         os_report_printtop(r_filter->top_user, "Username", 
635                            r_filter->related_user);
636
637     if(r_filter->related_level)
638         os_report_printtop(r_filter->top_level, "Level", 
639                            r_filter->related_level);
640
641     if(r_filter->related_group)
642         os_report_printtop(r_filter->top_group, "Group", 
643                            r_filter->related_group);
644     
645     if(r_filter->related_location)
646         os_report_printtop(r_filter->top_location, "Location", 
647                            r_filter->related_location);
648     
649     if(r_filter->related_rule)
650         os_report_printtop(r_filter->top_rule, "Rule", 
651                            r_filter->related_rule);
652     
653     
654     /* If we have to dump the alerts. */
655     if(data_to_clean)
656     {
657         int i = 0;
658
659         if(r_filter->show_alerts)
660         {
661             l_print_out("Log dump:");
662             l_print_out("------------------------------------------------");
663         }
664         while(data_to_clean[i])
665         {
666             alert_data *md = data_to_clean[i];
667             if(r_filter->show_alerts)
668                 l_print_out("%s %s\nRule: %d (level %d) -> '%s'\n%s\n\n", md->date, md->location, md->rule, md->level, md->comment, md->log[0]);
669             FreeAlertData(md);
670             i++;
671         }
672         free(data_to_clean);
673         data_to_clean = NULL;
674     }
675 }
676
677
678
679
680
681 /** int os_report_check_filters(char *filter_by, char *filter_value,
682  *                              report_filter *r_filter)
683  * Checks the configuration filters.
684  */
685 int os_report_configfilter(char *filter_by, char *filter_value, 
686                            report_filter *r_filter, int arg_type)
687 {
688     if(!filter_by || !filter_value)
689     {
690         return(-1);
691     }
692     
693     if(arg_type == REPORT_FILTER)
694     {
695         if(strcmp(filter_by, "group") == 0)
696         {
697             r_filter->group = filter_value;    
698         }
699         else if(strcmp(filter_by, "rule") == 0)
700         {
701             r_filter->rule = filter_value;    
702         }
703         else if(strcmp(filter_by, "level") == 0)
704         {
705             r_filter->level = filter_value;    
706         }
707         else if(strcmp(filter_by, "location") == 0)
708         {
709             r_filter->location = filter_value;    
710         }
711         else if(strcmp(filter_by, "user") == 0)
712         {
713             r_filter->user = filter_value;    
714         }
715         else if(strcmp(filter_by, "srcip") == 0)
716         {
717             r_filter->srcip = filter_value;    
718         }
719         else
720         {
721             merror("%s: ERROR: Invalid filter '%s'.", __local_name, filter_by);
722             return(-1);
723         }
724     }
725     else
726     {
727         if(strcmp(filter_by, "group") == 0)
728         {
729             r_filter->related_group = 
730             _report_filter_value(filter_value, r_filter->related_group);
731
732             if(r_filter->related_group == -1)
733                 return(-1);
734         }
735         else if(strcmp(filter_by, "rule") == 0)
736         {
737             r_filter->related_rule = 
738             _report_filter_value(filter_value, r_filter->related_rule);
739
740             if(r_filter->related_rule == -1)
741                 return(-1);
742         }
743         else if(strcmp(filter_by, "level") == 0)
744         {
745             r_filter->related_level = 
746             _report_filter_value(filter_value, r_filter->related_level);
747
748             if(r_filter->related_level == -1)
749                 return(-1);
750         }
751         else if(strcmp(filter_by, "location") == 0)
752         {
753             r_filter->related_location = 
754             _report_filter_value(filter_value, r_filter->related_location);
755
756             if(r_filter->related_location == -1)
757                 return(-1);
758         }
759         else if(strcmp(filter_by, "srcip") == 0)
760         {
761             r_filter->related_srcip = 
762             _report_filter_value(filter_value, r_filter->related_srcip);
763
764             if(r_filter->related_srcip == -1)
765                 return(-1);
766         }
767         else if(strcmp(filter_by, "user") == 0)
768         {
769             r_filter->related_user = 
770             _report_filter_value(filter_value, r_filter->related_user);
771         
772             if(r_filter->related_user == -1)
773                 return(-1);
774         }
775         else
776         {
777             merror("%s: ERROR: Invalid related entry '%s'.", __local_name, filter_by);
778             return(-1);
779         }
780     }
781
782     return(0);
783 }
784
785
786
787 /* EOF */