1 /* Copyright (C) 2019 Trend Micro Inc.
4 * This program is a free software; you can redistribute it
5 * and/or modify it under the terms of the GNU General Public
6 * License (version 2) as published by the FSF - Free Software
12 /* Helper functions */
13 static void l_print_out(const char *msg, ...) __attribute__((format(printf, 1, 2))) __attribute__((nonnull));
14 static void *_os_report_sort_compare(void *d1, void *d2) __attribute__((nonnull));
15 static void _os_header_print(int t, const char *hname) __attribute__((nonnull));
16 static int _os_report_str_int_compare(const char *str, int id) __attribute__((nonnull));
17 static int _os_report_check_filters(const alert_data *al_data, const report_filter *r_filter) __attribute__((nonnull));
18 static int _report_filter_value(const char *filter_by, int prev_filter) __attribute__((nonnull));
19 static int _os_report_print_related(int print_related, OSList *st_data) __attribute__((nonnull));
20 static int _os_report_add_tostore(const char *key, OSStore *top, void *data) __attribute__((nonnull(1, 2)));
21 static FILE *__g_rtype = NULL;
24 static void l_print_out(const char *msg, ...)
30 (void)vfprintf(__g_rtype, msg, args);
31 (void)fprintf(__g_rtype, "\r\n");
33 (void)vfprintf(stderr, msg, args);
34 (void)fprintf(stderr, "\r\n");
39 /* Sort function used by OSStore sort
42 static void *_os_report_sort_compare(void *d1, void *d2)
44 OSList *d1l = (OSList *)d1;
45 OSList *d2l = (OSList *)d2;
47 if (d1l->currently_size > d2l->currently_size) {
54 /* Print output header */
55 static void _os_header_print(int t, const char *hname)
58 l_print_out("Top entries for '%s':", hname);
59 l_print_out("------------------------------------------------");
61 l_print_out("Related entries for '%s':", hname);
62 l_print_out("------------------------------------------------");
66 /* Compare if the id is present in the string */
67 static int _os_report_str_int_compare(const char *str, int id)
72 if ((*str == ',') || (*str == ' ')) {
75 } else if (*str == '\0') {
77 } else if (isdigit((int)*str)) {
79 if (id == atoi(str)) {
87 } while (*str++ != '\0');
92 /* Check if the al_data should be filtered */
93 static int _os_report_check_filters(const alert_data *al_data, const report_filter *r_filter)
95 /* Check for the filters */
96 if (r_filter->group) {
97 if (al_data->group) { /* Probably unnecessary, all (?) alerts should have groups) */
98 if (!strstr(al_data->group, r_filter->group)) {
103 if (r_filter->rule) {
104 if (_os_report_str_int_compare(r_filter->rule, al_data->rule) != 1) {
108 if (r_filter->location) {
109 if (!OS_Match(r_filter->location, al_data->location)) {
113 if (r_filter->level) {
114 if (al_data->level < (unsigned int) atoi(r_filter->level)) {
118 if (r_filter->srcip) {
119 if(!al_data->srcip) {
122 if (al_data->srcip) {
123 if (!strstr(al_data->srcip, r_filter->srcip)) {
130 if (r_filter->user) {
135 if (!strstr(al_data->user, r_filter->user)) {
142 if (r_filter->files) {
143 if(!al_data->filename) {
146 if (al_data->filename) {
147 if (!strstr(al_data->filename, r_filter->files)) {
157 /* Set the proper value for the related entries */
158 static int _report_filter_value(const char *filter_by, int prev_filter)
160 if (strcmp(filter_by, "group") == 0) {
161 if (!(prev_filter & REPORT_REL_GROUP)) {
162 prev_filter |= REPORT_REL_GROUP;
164 return (prev_filter);
165 } else if (strcmp(filter_by, "rule") == 0) {
166 if (!(prev_filter & REPORT_REL_RULE)) {
167 prev_filter |= REPORT_REL_RULE;
169 return (prev_filter);
170 } else if (strcmp(filter_by, "level") == 0) {
171 if (!(prev_filter & REPORT_REL_LEVEL)) {
172 prev_filter |= REPORT_REL_LEVEL;
174 return (prev_filter);
175 } else if (strcmp(filter_by, "location") == 0) {
176 if (!(prev_filter & REPORT_REL_LOCATION)) {
177 prev_filter |= REPORT_REL_LOCATION;
179 return (prev_filter);
180 } else if (strcmp(filter_by, "srcip") == 0) {
181 if (!(prev_filter & REPORT_REL_SRCIP)) {
182 prev_filter |= REPORT_REL_SRCIP;
184 return (prev_filter);
185 } else if (strcmp(filter_by, "user") == 0) {
186 if (!(prev_filter & REPORT_REL_USER)) {
187 prev_filter |= REPORT_REL_USER;
189 return (prev_filter);
190 } else if (strcmp(filter_by, "filename") == 0) {
191 if (!(prev_filter & REPORT_REL_FILE)) {
192 prev_filter |= REPORT_REL_FILE;
194 return (prev_filter);
196 merror("%s: ERROR: Invalid relation '%s'.", __local_name, filter_by);
201 /* Print related entries */
202 static int _os_report_print_related(int print_related, OSList *st_data)
204 OSListNode *list_entry;
205 alert_data *list_aldata;
206 alert_data *saved_aldata;
208 list_entry = OSList_GetFirstNode(st_data);
210 saved_aldata = (alert_data *)list_entry->data;
212 /* Remove duplicates */
213 list_entry = list_entry->prev;
215 if (print_related & REPORT_REL_LOCATION) {
216 list_aldata = (alert_data *)list_entry->data;
217 if (strcmp(list_aldata->location, saved_aldata->location) == 0) {
222 else if (print_related & REPORT_REL_GROUP) {
223 list_aldata = (alert_data *)list_entry->data;
224 if (strcmp(list_aldata->group, saved_aldata->group) == 0) {
229 else if (print_related & REPORT_REL_RULE) {
230 list_aldata = (alert_data *)list_entry->data;
231 if (list_aldata->rule == saved_aldata->rule) {
236 else if (print_related & REPORT_REL_USER) {
237 list_aldata = (alert_data *)list_entry->data;
238 if (list_aldata->user == NULL || saved_aldata->user == NULL) {
239 } else if (strcmp(list_aldata->user, saved_aldata->user) == 0) {
244 else if (print_related & REPORT_REL_SRCIP) {
245 list_aldata = (alert_data *)list_entry->data;
246 if (list_aldata->srcip == NULL || saved_aldata->srcip == NULL) {
247 } else if (strcmp(list_aldata->srcip, saved_aldata->srcip) == 0) {
252 else if (print_related & REPORT_REL_LEVEL) {
253 list_aldata = (alert_data *)list_entry->data;
254 if (list_aldata->level == saved_aldata->level) {
257 } else if (print_related & REPORT_REL_FILE) {
258 list_aldata = (alert_data *)list_entry->data;
259 if (list_aldata->filename == NULL || saved_aldata->filename == NULL) {
260 } else if (strcmp(list_aldata->filename, saved_aldata->filename) == 0) {
264 list_entry = list_entry->prev;
268 if (print_related & REPORT_REL_LOCATION) {
269 l_print_out(" location: '%s'", saved_aldata->location);
270 } else if (print_related & REPORT_REL_GROUP) {
271 l_print_out(" group: '%s'", saved_aldata->group);
272 } else if (print_related & REPORT_REL_RULE) {
273 l_print_out(" rule: '%d'", saved_aldata->rule);
274 } else if ((print_related & REPORT_REL_SRCIP) && saved_aldata->srcip) {
275 l_print_out(" srcip: '%s'", saved_aldata->srcip);
276 } else if ((print_related & REPORT_REL_USER) && saved_aldata->user) {
277 l_print_out(" user: '%s'", saved_aldata->user);
278 } else if (print_related & REPORT_REL_LEVEL) {
279 l_print_out(" level: '%d'", saved_aldata->level);
280 } else if ((print_related & REPORT_REL_FILE) && saved_aldata->filename) {
281 l_print_out(" filename: '%s'", saved_aldata->filename);
285 list_entry = OSList_GetNextNode(st_data);
291 /* Add the entry to the hash */
292 static int _os_report_add_tostore(const char *key, OSStore *top, void *data)
296 /* Add data to the hash */
297 top_list = (OSList *) OSStore_Get(top, key);
299 OSList_AddData(top_list, data);
301 top_list = OSList_Create();
303 merror(MEM_ERROR, __local_name, errno, strerror(errno));
306 OSList_AddData(top_list, data);
308 OSStore_Put(top, key, top_list);
314 void os_report_printtop(void *topstore_pt, const char *hname, int print_related)
317 OSStore *topstore = (OSStore *)topstore_pt;
318 OSStoreNode *next_node;
320 next_node = OSStore_GetFirstNode(topstore);
322 OSList *st_data = (OSList *)next_node->data;
323 char *lkey = (char *)next_node->key;
325 /* With location we leave more space to be clearer */
326 if (!print_related) {
327 if (strlen(lkey) > 76) {
334 _os_header_print(print_related, hname);
337 l_print_out("%-78s|%-8d|", (char *)next_node->key, st_data->currently_size);
340 /* Print each destination */
343 _os_header_print(print_related, hname);
346 l_print_out("%-78s|%-8d|", (char *)next_node->key, st_data->currently_size);
348 if (print_related & REPORT_REL_LOCATION) {
349 _os_report_print_related(REPORT_REL_LOCATION, st_data);
351 if (print_related & REPORT_REL_SRCIP) {
352 _os_report_print_related(REPORT_REL_SRCIP, st_data);
354 if (print_related & REPORT_REL_USER) {
355 _os_report_print_related(REPORT_REL_USER, st_data);
357 if (print_related & REPORT_REL_RULE) {
358 _os_report_print_related(REPORT_REL_RULE, st_data);
360 if (print_related & REPORT_REL_GROUP) {
361 _os_report_print_related(REPORT_REL_GROUP, st_data);
363 if (print_related & REPORT_REL_LEVEL) {
364 _os_report_print_related(REPORT_REL_LEVEL, st_data);
366 if (print_related & REPORT_REL_FILE) {
367 _os_report_print_related(REPORT_REL_FILE, st_data);
371 next_node = next_node->next;
381 void os_ReportdStart(report_filter *r_filter)
383 int alerts_processed = 0;
384 int alerts_filtered = 0;
385 char *first_alert = NULL;
386 char *last_alert = NULL;
387 alert_data **data_to_clean = NULL;
395 /* Get current time before starting */
399 /* Initiate file queue - to read the alerts */
400 os_calloc(1, sizeof(file_queue), fileq);
402 if (r_filter->report_type == REPORT_TYPE_DAILY && r_filter->filename) {
403 fileq->fp = fopen(r_filter->filename, "r");
405 merror("%s: ERROR: Unable to open alerts file to generate report.", __local_name);
409 __g_rtype = r_filter->fp;
415 /* Create top hashes */
416 r_filter->top_user = OSStore_Create();
417 r_filter->top_srcip = OSStore_Create();
418 r_filter->top_level = OSStore_Create();
419 r_filter->top_rule = OSStore_Create();
420 r_filter->top_group = OSStore_Create();
421 r_filter->top_location = OSStore_Create();
422 r_filter->top_files = OSStore_Create();
424 if (!r_filter->top_user || !r_filter->top_srcip || !r_filter->top_level || !r_filter->top_rule
425 || !r_filter->top_group || !r_filter->top_location || !r_filter->top_files) {
426 merror(MEM_ERROR, __local_name, errno, strerror((errno)));
428 if (r_filter->top_user) {
429 OSStore_Free(r_filter->top_user);
431 if (r_filter->top_srcip) {
432 OSStore_Free(r_filter->top_srcip);
434 if (r_filter->top_level) {
435 OSStore_Free(r_filter->top_level);
437 if (r_filter->top_rule) {
438 OSStore_Free(r_filter->top_rule);
440 if (r_filter->top_group) {
441 OSStore_Free(r_filter->top_group);
443 if (r_filter->top_location) {
444 OSStore_Free(r_filter->top_location);
446 if (r_filter->top_files) {
447 OSStore_Free(r_filter->top_files);
455 Init_FileQueue(fileq, p, CRALERT_READ_ALL | CRALERT_FP_SET);
457 /* Read the alerts */
459 /* Get message if available */
460 al_data = Read_FileMon(fileq, p, 1);
467 /* Check the filters */
468 if (!_os_report_check_filters(al_data, r_filter)) {
469 FreeAlertData(al_data);
474 data_to_clean = (alert_data **) os_AddPtArray(al_data, (void **)data_to_clean);
476 /* Set first and last alert for summary */
478 first_alert = al_data->date;
480 last_alert = al_data->date;
482 /* Add source IP if it is set properly */
483 if (al_data->srcip != NULL && strcmp(al_data->srcip, "(none)") != 0) {
484 _os_report_add_tostore(al_data->srcip, r_filter->top_srcip, al_data);
487 /* Add user if it is set properly */
488 if (al_data->user != NULL && strcmp(al_data->user, "(none)") != 0) {
489 _os_report_add_tostore(al_data->user, r_filter->top_user, al_data);
492 /* Add level and severity */
497 snprintf(mlevel, 16, "Severity %d" , al_data->level);
498 snprintf(mrule, 76, "%d - %s" , al_data->rule, al_data->comment);
500 _os_report_add_tostore(mlevel, r_filter->top_level,
502 _os_report_add_tostore(mrule, r_filter->top_rule,
506 /* Deal with the group */
511 mgroup = OS_StrBreak(',', al_data->group, 32);
515 while (*tmp_str == ' ') {
518 if (*tmp_str == '\0') {
524 _os_report_add_tostore(tmp_str, r_filter->top_group,
533 tmp_str = al_data->group;
534 while (*tmp_str == ' ') {
537 if (*tmp_str != '\0') {
538 _os_report_add_tostore(tmp_str, r_filter->top_group,
544 /* Add to the location top filter */
545 _os_report_add_tostore(al_data->location, r_filter->top_location,
548 if (al_data->filename != NULL) {
549 _os_report_add_tostore(al_data->filename, r_filter->top_files,
556 /* No report available */
557 if (alerts_filtered == 0) {
558 if (!r_filter->report_name) {
559 merror("%s: INFO: Report completed and zero alerts post-filter.", __local_name);
561 merror("%s: INFO: Report '%s' completed and zero alerts post-filter.", __local_name, r_filter->report_name);
567 if (r_filter->report_name) {
568 verbose("%s: INFO: Report '%s' completed. Creating output...", __local_name, r_filter->report_name);
570 verbose("%s: INFO: Report completed. Creating output...", __local_name);
574 if (r_filter->report_name) {
575 l_print_out("Report '%s' completed.", r_filter->report_name);
577 l_print_out("Report completed. ==");
579 l_print_out("------------------------------------------------");
581 l_print_out("->Processed alerts: %d", alerts_processed);
582 l_print_out("->Post-filtering alerts: %d", alerts_filtered);
583 l_print_out("->First alert: %s", first_alert);
584 l_print_out("->Last alert: %s", last_alert);
588 OSStore_Sort(r_filter->top_srcip, _os_report_sort_compare);
589 OSStore_Sort(r_filter->top_user, _os_report_sort_compare);
590 OSStore_Sort(r_filter->top_level, _os_report_sort_compare);
591 OSStore_Sort(r_filter->top_group, _os_report_sort_compare);
592 OSStore_Sort(r_filter->top_location, _os_report_sort_compare);
593 OSStore_Sort(r_filter->top_rule, _os_report_sort_compare);
594 OSStore_Sort(r_filter->top_files, _os_report_sort_compare);
596 os_report_printtop(r_filter->top_srcip, "Source ip", 0);
597 os_report_printtop(r_filter->top_user, "Username", 0);
598 os_report_printtop(r_filter->top_level, "Level", 0);
599 os_report_printtop(r_filter->top_group, "Group", 0);
600 os_report_printtop(r_filter->top_location, "Location", 0);
601 os_report_printtop(r_filter->top_rule, "Rule", 0);
602 os_report_printtop(r_filter->top_files, "Filenames", 0);
604 /* Print related events */
605 if (r_filter->related_srcip)
606 os_report_printtop(r_filter->top_srcip, "Source ip",
607 r_filter->related_srcip);
609 if (r_filter->related_user)
610 os_report_printtop(r_filter->top_user, "Username",
611 r_filter->related_user);
613 if (r_filter->related_level)
614 os_report_printtop(r_filter->top_level, "Level",
615 r_filter->related_level);
617 if (r_filter->related_group)
618 os_report_printtop(r_filter->top_group, "Group",
619 r_filter->related_group);
621 if (r_filter->related_location)
622 os_report_printtop(r_filter->top_location, "Location",
623 r_filter->related_location);
625 if (r_filter->related_rule)
626 os_report_printtop(r_filter->top_rule, "Rule",
627 r_filter->related_rule);
629 if (r_filter->related_file)
630 os_report_printtop(r_filter->top_files, "Filename",
631 r_filter->related_file);
633 /* If we have to dump the alerts */
637 if (r_filter->show_alerts) {
638 l_print_out("Log dump:");
639 l_print_out("------------------------------------------------");
641 while (data_to_clean[i]) {
642 alert_data *md = data_to_clean[i];
643 if (r_filter->show_alerts) {
644 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]);
650 data_to_clean = NULL;
654 if (fileq->fp && fileq->fp != stdin) {
661 /* Check the configuration filters */
662 int os_report_configfilter(const char *filter_by, const char *filter_value,
663 report_filter *r_filter, int arg_type)
665 if (!filter_by || !filter_value) {
669 if (arg_type == REPORT_FILTER) {
670 if (strcmp(filter_by, "group") == 0) {
671 r_filter->group = filter_value;
672 } else if (strcmp(filter_by, "rule") == 0) {
673 r_filter->rule = filter_value;
674 } else if (strcmp(filter_by, "level") == 0) {
675 r_filter->level = filter_value;
676 } else if (strcmp(filter_by, "location") == 0) {
677 r_filter->location = filter_value;
678 } else if (strcmp(filter_by, "user") == 0) {
679 r_filter->user = filter_value;
680 } else if (strcmp(filter_by, "srcip") == 0) {
681 r_filter->srcip = filter_value;
682 } else if (strcmp(filter_by, "filename") == 0) {
683 r_filter->files = filter_value;
685 merror("%s: ERROR: Invalid filter '%s'.", __local_name, filter_by);
689 if (strcmp(filter_by, "group") == 0) {
690 r_filter->related_group =
691 _report_filter_value(filter_value, r_filter->related_group);
693 if (r_filter->related_group == -1) {
696 } else if (strcmp(filter_by, "rule") == 0) {
697 r_filter->related_rule =
698 _report_filter_value(filter_value, r_filter->related_rule);
700 if (r_filter->related_rule == -1) {
703 } else if (strcmp(filter_by, "level") == 0) {
704 r_filter->related_level =
705 _report_filter_value(filter_value, r_filter->related_level);
707 if (r_filter->related_level == -1) {
710 } else if (strcmp(filter_by, "location") == 0) {
711 r_filter->related_location =
712 _report_filter_value(filter_value, r_filter->related_location);
714 if (r_filter->related_location == -1) {
717 } else if (strcmp(filter_by, "srcip") == 0) {
718 r_filter->related_srcip =
719 _report_filter_value(filter_value, r_filter->related_srcip);
721 if (r_filter->related_srcip == -1) {
724 } else if (strcmp(filter_by, "user") == 0) {
725 r_filter->related_user =
726 _report_filter_value(filter_value, r_filter->related_user);
728 if (r_filter->related_user == -1) {
731 } else if (strcmp(filter_by, "filename") == 0) {
732 r_filter->related_file =
733 _report_filter_value(filter_value, r_filter->related_file);
735 if (r_filter->related_file == -1) {
739 merror("%s: ERROR: Invalid related entry '%s'.", __local_name, filter_by);