source: trunk/src/sh_log_evalrule.c@ 265

Last change on this file since 265 was 265, checked in by katerina, 15 years ago

Enhance logfile monitoring (tickets #183, #184, #185).

File size: 27.9 KB
Line 
1
2#include "config_xor.h"
3
4#include <stdio.h>
5#include <stdarg.h>
6#include <string.h>
7#include <ctype.h>
8#include <time.h>
9#include <limits.h>
10#include <sys/types.h>
11
12#ifdef USE_LOGFILE_MONITOR
13
14#undef FIL__
15#define FIL__ _("sh_log_evalrule.c")
16
17/* Debian/Ubuntu: libpcre3-dev */
18#ifdef HAVE_PCRE_PCRE_H
19#include <pcre/pcre.h>
20#else
21#include <pcre.h>
22#endif
23
24#ifndef PCRE_NO_AUTO_CAPTURE
25#define PCRE_NO_AUTO_CAPTURE 0
26#endif
27
28#include "samhain.h"
29#include "sh_pthread.h"
30#include "sh_utils.h"
31#include "sh_string.h"
32#include "sh_log_check.h"
33#include "sh_log_evalrule.h"
34#include "sh_log_correlate.h"
35#include "sh_log_mark.h"
36#include "sh_log_repeat.h"
37#include "zAVLTree.h"
38
39extern int flag_err_debug;
40
41/* #define DEBUG_EVALRULES */
42
43#ifdef DEBUG_EVALRULES
44static void DEBUG(const char *fmt, ...)
45{
46 va_list ap;
47 va_start(ap, fmt);
48 vfprintf(stderr, fmt, ap); /* flawfinder: ignore *//* we control fmt string */
49 va_end(ap);
50 return;
51}
52#else
53static void DEBUG(const char *fmt, ...)
54{
55 (void) fmt;
56 return;
57}
58#endif
59
60struct sh_ceval /* Counter for summarizing */
61{
62 sh_string * hostname;
63 sh_string * counted_str;
64 sh_string * filename;
65 unsigned long count;
66 time_t start;
67 time_t interval;
68};
69
70void sh_ceval_free(void * item)
71{
72 struct sh_ceval * counter = (struct sh_ceval *) item;
73 if (!counter)
74 return;
75 sh_string_destroy(&(counter->hostname));
76 sh_string_destroy(&(counter->counted_str));
77 sh_string_destroy(&(counter->filename));
78 SH_FREE(counter);
79}
80
81enum {
82 RFL_ISRULE = 1 << 0,
83 RFL_ISGROUP = 1 << 1,
84 RFL_KEEP = 1 << 2,
85 RFL_MARK = 1 << 3
86};
87
88
89/*--------------------------------------------------------------
90 *
91 * Adding rules/groups/hosts
92 *
93 *--------------------------------------------------------------*/
94
95struct sh_geval /* Group of rules (may be a single rule) */
96{
97 sh_string * label; /* label for this group */
98 pcre * rule; /* compiled regex for rule */
99 pcre_extra * rule_extra;
100 int * ovector; /* captured substrings */
101 int ovecnum; /* how many captured */
102 int captures; /* (captures+1)*3 required */
103 int flags; /* bit flags */
104 unsigned long delay; /* delay for keep rules */
105 zAVLTree * counterlist; /* counters if EVAL_SUM */
106 struct sh_qeval * queue; /* queue for this rule */
107 struct sh_geval * nextrule; /* next rule in this group */
108 struct sh_geval * next; /* next group of rules */
109 struct sh_geval * gnext; /* grouplist next */
110};
111
112struct sh_heval /* host-specific rules */
113{
114 pcre * hostname; /* compiled regex for hostname */
115 pcre_extra * hostname_extra;
116 struct sh_geval * rulegroups; /* list of group of rules */
117 struct sh_heval * next;
118};
119
120static struct sh_heval * hostlist = NULL;
121static struct sh_qeval * queuelist = NULL;
122static struct sh_geval * grouplist = NULL;
123
124/* These flags are set if we are within
125 * the define of a host/rule group.
126 */
127static struct sh_heval * host_open = NULL;
128static struct sh_geval * group_open = NULL;
129
130int sh_eval_gend (const char * str)
131{
132 (void) str;
133 if (group_open) {
134 group_open = NULL;
135 return 0;
136 }
137 return -1;
138}
139
140int sh_eval_gadd (const char * str)
141{
142 struct sh_geval * ng;
143 struct sh_geval * tmp;
144 pcre * group;
145 pcre_extra * group_extra;
146 const char * error;
147 int erroffset;
148 unsigned int nfields = 2;
149 size_t lengths[2];
150 char * new = sh_util_strdup(str);
151 char ** splits = split_array(new, &nfields, ':', lengths);
152
153 if (group_open)
154 group_open = NULL;
155
156 if (nfields != 2)
157 {
158 SH_FREE(splits);
159 SH_FREE(new);
160 return -1;
161 }
162
163 group = pcre_compile(splits[1], PCRE_NO_AUTO_CAPTURE,
164 &error, &erroffset, NULL);
165 if (!group)
166 {
167 sh_string * msg = sh_string_new(0);
168 sh_string_add_from_char(msg, _("Bad regex: "));
169 sh_string_add_from_char(msg, splits[1]);
170
171 SH_MUTEX_LOCK(mutex_thread_nolog);
172 sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
173 sh_string_str(msg),
174 _("sh_eval_gadd"));
175 SH_MUTEX_UNLOCK(mutex_thread_nolog);
176 sh_string_destroy(&msg);
177
178 SH_FREE(splits);
179 SH_FREE(new);
180 return -1;
181 }
182 group_extra = NULL; /* pcre_study(group, 0, &error); */
183
184 ng = SH_ALLOC(sizeof(struct sh_geval));
185 ng->label = sh_string_new_from_lchar(splits[0], lengths[0]);
186 ng->flags = RFL_ISGROUP;
187
188 ng->rule = group;
189 ng->rule_extra = group_extra;
190 ng->ovector = NULL;
191 ng->ovecnum = 0;
192 ng->captures = 0;
193 ng->counterlist = NULL;
194 ng->queue = NULL;
195 ng->nextrule = NULL;
196 ng->next = NULL;
197 ng->gnext = NULL;
198
199 if (!host_open)
200 {
201 if (0 != sh_eval_hadd("^.*"))
202 {
203 pcre_free(group);
204 sh_string_destroy(&(ng->label));
205 SH_FREE(splits);
206 SH_FREE(new);
207 SH_FREE(ng);
208 return -1;
209 }
210 }
211
212 /*
213 * Insert at end, to keep user-defined order
214 */
215
216 if (host_open)
217 {
218 if (grouplist)
219 {
220 tmp = grouplist;
221 while (tmp->gnext != NULL) { tmp = tmp->gnext; }
222 tmp->gnext = ng;
223 } else {
224 grouplist = ng;
225 }
226
227
228 /*
229 * If there is an open host group, add it to its
230 * rulegroups
231 */
232
233 if (host_open->rulegroups)
234 {
235 tmp = host_open->rulegroups;
236 while (tmp->next != NULL) { tmp = tmp->next; }
237 tmp->next = ng;
238 } else {
239 host_open->rulegroups = ng;
240 }
241 }
242
243 group_open = ng;
244 SH_FREE(splits);
245 SH_FREE(new);
246 return 0;
247}
248
249int sh_eval_hend (const char * str)
250{
251 (void) str;
252 if (host_open) {
253 host_open = NULL;
254 return 0;
255 }
256 return -1;
257}
258
259int sh_eval_hadd (const char * str)
260{
261 struct sh_heval * nh;
262 struct sh_heval * tmp;
263 pcre * host;
264 pcre_extra * host_extra;
265 const char * error;
266 int erroffset;
267
268 if (host_open)
269 host_open = NULL;
270
271 host = pcre_compile(str, PCRE_NO_AUTO_CAPTURE,
272 &error, &erroffset, NULL);
273 if (!host)
274 {
275 sh_string * msg = sh_string_new(0);
276 sh_string_add_from_char(msg, _("Bad regex: "));
277 sh_string_add_from_char(msg, str);
278
279 SH_MUTEX_LOCK(mutex_thread_nolog);
280 sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
281 sh_string_str(msg),
282 _("sh_eval_hadd"));
283 SH_MUTEX_UNLOCK(mutex_thread_nolog);
284 sh_string_destroy(&msg);
285
286 return -1;
287 }
288 host_extra = NULL; /* pcre_study(host, 0, &error); */
289
290 nh = SH_ALLOC(sizeof(struct sh_heval));
291 nh->hostname = host;
292 nh->hostname_extra = host_extra;
293 nh->rulegroups = NULL;
294
295 /*
296 * Insert at end, to keep user-defined order
297 */
298 nh->next = NULL;
299 if (hostlist) {
300 tmp = hostlist;
301 while (tmp->next != NULL) { tmp = tmp->next; }
302 tmp->next = nh;
303 } else {
304 hostlist = nh;
305 }
306 host_open = nh;
307
308 return 0;
309}
310
311int sh_eval_qadd (const char * str)
312{
313 struct sh_qeval * nq;
314 int severity;
315 unsigned int nfields = 4; /* label:interval:(report|sum):severity */
316 size_t lengths[4];
317 char * new = sh_util_strdup(str);
318 char ** splits = split_array(new, &nfields, ':', lengths);
319
320 if (nfields != 4)
321 {
322 SH_FREE(splits);
323 SH_FREE(new);
324 return -1;
325 }
326
327 if (strcmp(splits[2], _("sum")) && strcmp(splits[2], _("report")))
328 {
329 SH_FREE(splits);
330 SH_FREE(new);
331 return -1;
332 }
333
334 if (!strcmp(splits[2], _("sum")) && atoi(splits[1]) < 0)
335 {
336 SH_FREE(splits);
337 SH_FREE(new);
338 return -1;
339 }
340
341 if (!strcmp(splits[1], _("trash"))) /* predefined, reserved */
342 {
343 SH_FREE(splits);
344 SH_FREE(new);
345 return -1;
346 }
347
348 severity = sh_error_convert_level (splits[3]);
349 if (severity < 0)
350 {
351 SH_FREE(splits);
352 SH_FREE(new);
353 return -1;
354 }
355
356 nq = SH_ALLOC(sizeof(struct sh_qeval));
357 nq->label = sh_string_new_from_lchar(splits[0], lengths[0]);
358
359 DEBUG("debug: splits[2] = %s, policy = %d\n",splits[2],nq->policy);
360 if (0 == strcmp(splits[2], _("report"))) {
361 nq->policy = EVAL_REPORT;
362 nq->interval = 0;
363 }
364 else {
365 nq->policy = EVAL_SUM;
366 nq->interval = (time_t) atoi(splits[1]);
367 }
368
369 nq->severity = severity;
370 nq->next = queuelist;
371 queuelist = nq;
372
373 SH_FREE(splits);
374 SH_FREE(new);
375 return 0;
376}
377
378struct sh_qeval * sh_log_find_queue(const char * str)
379{
380 struct sh_qeval * retval = queuelist;
381
382 if (!str)
383 return NULL;
384
385 while (retval)
386 {
387 if (0 == strcmp(str, sh_string_str(retval->label)))
388 break;
389 retval = retval->next;
390 }
391 return retval;
392}
393
394int sh_log_lookup_severity(const char * str)
395{
396 struct sh_qeval * queue;
397
398 if (str)
399 {
400 if (0 != strcmp(str, _("trash")))
401 {
402 queue = sh_log_find_queue(str);
403
404 if (queue)
405 return queue->severity;
406 }
407 }
408 return SH_ERR_SEVERE;
409}
410
411static char * get_label_and_time(const char * inprefix, char * str,
412 unsigned long * seconds)
413{
414 char * res = NULL;
415 char * endptr = NULL;
416
417 unsigned int nfields = 2; /* seconds:label */
418 size_t lengths[2];
419 char * prefix = sh_util_strdup(inprefix);
420 char * new = sh_util_strdup(str);
421 char ** splits = split_array_braced(new, prefix, &nfields, lengths);
422
423 if (splits && nfields == 2 && lengths[0] > 0 && lengths[1] > 0)
424 {
425 *seconds = strtoul(splits[0], &endptr, 10);
426 if ((endptr == '\0' || endptr != splits[0]) && (*seconds != ULONG_MAX))
427 {
428 res = sh_util_strdup(splits[1]);
429 }
430 }
431 if (splits)
432 SH_FREE(splits);
433 SH_FREE(new);
434 SH_FREE(prefix);
435 return res;
436}
437
438static struct sh_qeval ** dummy_queue;
439static char ** dummy_dstr;
440
441int sh_eval_radd (const char * str)
442{
443 struct sh_geval * nr;
444 struct sh_geval * tmp;
445 struct sh_qeval * queue = NULL;
446 pcre * rule;
447 pcre_extra * rule_extra;
448 const char * error;
449 int erroffset;
450 int captures = 0;
451 unsigned int nfields = 2; /* queue:regex */
452 size_t lengths[3];
453 char * new = sh_util_strdup(str);
454 char ** splits;
455
456 int qpos = 0;
457 volatile int rpos = 1;
458 unsigned long dsec = 0;
459 char * dstr = NULL;
460 char * s = new;
461 volatile char pflag = '-';
462
463 while ( *s && isspace((int)*s) ) ++s;
464 if (0 == strncmp(s, _("KEEP"), 4) ||
465 0 == strncmp(s, _("CORRELATE"), 9) ||
466 0 == strncmp(s, _("MARK"), 4))
467 {
468 pflag = s[0];
469 nfields = 3;
470 }
471
472 splits = split_array(new, &nfields, ':', lengths);
473
474 dummy_queue = &queue;
475 dummy_dstr = &dstr;
476
477 if (nfields < 2 || nfields > 3)
478 {
479 SH_FREE(splits);
480 SH_FREE(new);
481 return -1;
482 }
483
484 if (nfields == 3)
485 {
486 if (pflag == 'K')
487 {
488 /* KEEP(nsec,label):queue:regex
489 */
490 dstr = get_label_and_time(_("KEEP"), splits[0], &dsec);
491 if (!dstr)
492 {
493 SH_FREE(splits);
494 SH_FREE(new);
495 return -1;
496 }
497 }
498 else if (pflag == 'C')
499 {
500 /* CORRELATE(description):queue:regex
501 */
502 int retval = sh_keep_match_add(splits[0], splits[1], splits[2]);
503 SH_FREE(splits);
504 SH_FREE(new);
505 return retval;
506 }
507 else if (pflag == 'M')
508 {
509 /* MARK(description, interval):queue:regex
510 */
511 int retval = -1;
512
513 dstr = get_label_and_time(_("MARK"), splits[0], &dsec);
514 if (dstr)
515 {
516 retval = sh_log_mark_add(dstr, dsec, splits[1]);
517 }
518 if (retval != 0)
519 {
520 SH_FREE(splits);
521 SH_FREE(new);
522 return retval;
523 }
524 }
525 ++qpos; ++rpos;
526 }
527
528 if (0 != strcmp(splits[qpos], _("trash")))
529 {
530 queue = sh_log_find_queue(splits[qpos]);
531 if (!queue)
532 {
533 SH_FREE(splits);
534 SH_FREE(new);
535 return -1;
536 }
537 }
538
539 rule = pcre_compile(splits[rpos], 0,
540 &error, &erroffset, NULL);
541 if (!rule)
542 {
543 sh_string * msg = sh_string_new(0);
544 sh_string_add_from_char(msg, _("Bad regex: "));
545 sh_string_add_from_char(msg, splits[rpos]);
546
547 SH_MUTEX_LOCK(mutex_thread_nolog);
548 sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
549 sh_string_str(msg),
550 _("sh_eval_radd"));
551 SH_MUTEX_UNLOCK(mutex_thread_nolog);
552 sh_string_destroy(&msg);
553
554 SH_FREE(splits);
555 SH_FREE(new);
556 return -1;
557 }
558 rule_extra = NULL; /* pcre_study(rule, 0, &error); */
559 pcre_fullinfo(rule, rule_extra, PCRE_INFO_CAPTURECOUNT, &captures);
560
561 if (flag_err_debug == SL_TRUE)
562 {
563 char * emsg = SH_ALLOC(SH_ERRBUF_SIZE);
564 if (dstr)
565 sl_snprintf(emsg, SH_ERRBUF_SIZE, _("Adding rule: |%s| with %d captures, keep(%lu,%s)"),
566 splits[rpos], captures, dsec, dstr);
567 else
568 sl_snprintf(emsg, SH_ERRBUF_SIZE, _("Adding rule: |%s| with %d captures"),
569 splits[rpos], captures);
570 SH_MUTEX_LOCK(mutex_thread_nolog);
571 sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, 0, MSG_E_SUBGEN,
572 emsg, _("sh_eval_radd"));
573 SH_MUTEX_UNLOCK(mutex_thread_nolog);
574 SH_FREE(emsg);
575 }
576
577 DEBUG("adding rule: |%s| with %d captures\n", splits[rpos], captures);
578
579 SH_FREE(splits);
580 SH_FREE(new);
581
582 nr = SH_ALLOC(sizeof(struct sh_geval));
583 nr->label = NULL;
584 nr->flags = RFL_ISRULE;
585 nr->delay = 0;
586
587 nr->rule = rule;
588 nr->rule_extra = rule_extra;
589 nr->captures = captures;
590 nr->ovector = SH_ALLOC(sizeof(int) * (captures+1) * 3);
591 nr->ovecnum = 0;
592 nr->counterlist = NULL;
593 nr->queue = queue;
594 nr->nextrule = NULL;
595 nr->next = NULL;
596 nr->gnext = NULL;
597
598
599 if (pflag == 'K')
600 {
601 nr->label = sh_string_new_from_lchar(dstr, strlen(dstr));
602 nr->flags |= RFL_KEEP;
603 nr->delay = dsec;
604 SH_FREE(dstr);
605 }
606 else if (pflag == 'M')
607 {
608 nr->label = sh_string_new_from_lchar(dstr, strlen(dstr));
609 nr->flags |= RFL_MARK;
610 nr->delay = dsec;
611 SH_FREE(dstr);
612 }
613
614 /*
615 * If there is an open group, add it to its
616 * rules
617 */
618 if (group_open)
619 {
620 if (flag_err_debug == SL_TRUE)
621 {
622 char * emsg = SH_ALLOC(SH_ERRBUF_SIZE);
623 sl_snprintf(emsg, SH_ERRBUF_SIZE, _("Adding rule to group |%s|"),
624 sh_string_str(group_open->label));
625 SH_MUTEX_LOCK(mutex_thread_nolog);
626 sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, 0, MSG_E_SUBGEN,
627 emsg, _("sh_eval_radd"));
628 SH_MUTEX_UNLOCK(mutex_thread_nolog);
629 SH_FREE(emsg);
630 }
631
632 DEBUG("adding rule to group |%s|\n", sh_string_str(group_open->label));
633
634 if (group_open->nextrule)
635 {
636 tmp = group_open->nextrule;
637 while (tmp->nextrule != NULL) { tmp = tmp->nextrule; } /* next -> nextrule */
638 tmp->nextrule = nr; /* next -> nextrule */
639 } else {
640 group_open->nextrule = nr;
641 }
642 }
643
644 /*
645 * ..else, add it to the currently open host (open the
646 * default host, if there is no open one)
647 */
648 else
649 {
650 if (!host_open)
651 {
652 if (0 != sh_eval_hadd("^.*"))
653 {
654 if (nr->label)
655 sh_string_destroy(&(nr->label));
656 SH_FREE(nr->ovector);
657 SH_FREE(nr);
658 return -1;
659 }
660 }
661
662 if (host_open)
663 {
664 /*
665 * Add rule as member to grouplist, to facilitate cleanup
666 */
667
668 DEBUG("adding solitary rule to grouplist\n");
669
670 if (grouplist)
671 {
672 tmp = grouplist;
673 while (tmp->gnext != NULL) { tmp = tmp->gnext; }
674 tmp->gnext = nr;
675 } else {
676 grouplist = nr;
677 }
678
679
680 /*
681 * Add rule to host rulegroups
682 */
683 DEBUG("adding solitary rule to host rulegroups\n");
684
685 if (host_open->rulegroups)
686 {
687 /* Second, third, ... rule go to host_open->rulegroups->next,
688 * since test_grules() iterates over nextrules
689 */
690 tmp = host_open->rulegroups;
691 while (tmp->next != NULL) { tmp = tmp->next; }
692 tmp->next = nr;
693 }
694 else
695 {
696 /* First rule goes to host_open->rulegroups */
697 host_open->rulegroups = nr;
698 }
699 }
700 else
701 {
702 if (nr->label)
703 sh_string_destroy(&(nr->label));
704 SH_FREE(nr->ovector);
705 SH_FREE(nr);
706 return -1;
707 }
708 }
709
710 return 0;
711}
712
713void sh_eval_cleanup()
714{
715 struct sh_geval * gtmp;
716 struct sh_qeval * qtmp;
717 struct sh_heval * htmp;
718
719 while (grouplist)
720 {
721 gtmp = grouplist;
722 grouplist = gtmp->gnext;
723
724 if (gtmp->label) sh_string_destroy(&(gtmp->label));
725 if (gtmp->rule_extra) (*pcre_free)(gtmp->rule_extra);
726 if (gtmp->rule) (*pcre_free)(gtmp->rule);
727 if (gtmp->counterlist)
728 zAVLFreeTree(gtmp->counterlist, sh_ceval_free);
729 if (gtmp->ovector)
730 SH_FREE(gtmp->ovector);
731#if 0
732 while (gtmp->nextrule)
733 {
734 tmp = gtmp->nextrule;
735 gtmp->nextrule = tmp->nextrule;
736
737 if (tmp->rule_extra) (*pcre_free)(tmp->rule_extra);
738 if (tmp->rule) (*pcre_free)(tmp->rule);
739 if (tmp->counterlist)
740 zAVLFreeTree(tmp->counterlist, sh_ceval_free);
741 if (tmp->ovector)
742 SH_FREE(tmp->ovector);
743 SH_FREE(tmp);
744 }
745#endif
746 SH_FREE(gtmp);
747 }
748
749 qtmp = queuelist;
750 while (qtmp)
751 {
752 if (qtmp->label) sh_string_destroy(&(qtmp->label));
753 queuelist = qtmp->next;
754 SH_FREE(qtmp);
755 qtmp = queuelist;
756 }
757
758 htmp = hostlist;
759 while (htmp)
760 {
761 if (htmp->hostname_extra) (*pcre_free)(htmp->hostname_extra);
762 if (htmp->hostname) (*pcre_free)(htmp->hostname);
763 hostlist = htmp->next;
764 SH_FREE(htmp);
765 htmp = hostlist;
766 }
767
768 sh_keep_destroy();
769 sh_keep_match_del();
770
771 return;
772}
773
774/**********************************************************************
775 *
776 * Actual rule processing
777 *
778 **********************************************************************/
779
780/* Test a list of rules against msg; return matched rule, with ovector
781 * filled in
782 */
783static struct sh_geval ** dummy1;
784
785static struct sh_geval * test_rule (struct sh_geval * rule, sh_string *msg, time_t tstamp)
786{
787 int res;
788 volatile int count;
789 volatile time_t timestamp = tstamp;
790
791 dummy1 = &rule;
792
793 if (!rule)
794 DEBUG("debug: (NULL) rule\n");
795
796 if (rule && sh_string_len(msg) < (size_t)INT_MAX)
797 {
798 count = 1;
799 do {
800
801 if (flag_err_debug == SL_TRUE)
802 {
803 char * emsg = SH_ALLOC(SH_ERRBUF_SIZE);
804 sl_snprintf(emsg, SH_ERRBUF_SIZE, _("Check rule %d for |%s|"),
805 count, sh_string_str(msg));
806 SH_MUTEX_LOCK(mutex_thread_nolog);
807 sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, 0, MSG_E_SUBGEN,
808 emsg, _("test_rule"));
809 SH_MUTEX_UNLOCK(mutex_thread_nolog);
810 SH_FREE(emsg);
811 }
812
813 DEBUG("debug: check rule %d for <%s>\n", count, msg->str);
814 res = pcre_exec(rule->rule, rule->rule_extra,
815 sh_string_str(msg), (int)sh_string_len(msg), 0,
816 0, rule->ovector, (3*(1+rule->captures)));
817 if (res >= 0)
818 {
819 rule->ovecnum = res;
820
821 if (flag_err_debug == SL_TRUE)
822 {
823 char * emsg = SH_ALLOC(SH_ERRBUF_SIZE);
824 if ( rule->flags & RFL_KEEP )
825 sl_snprintf(emsg, SH_ERRBUF_SIZE, _("Rule %d matches, result = %d (keep)"),
826 count, res);
827 else if ( rule->flags & RFL_MARK )
828 sl_snprintf(emsg, SH_ERRBUF_SIZE, _("Rule %d matches, result = %d (mark)"),
829 count, res);
830 else
831 sl_snprintf(emsg, SH_ERRBUF_SIZE, _("Rule %d matches, result = %d"),
832 count, res);
833 SH_MUTEX_LOCK(mutex_thread_nolog);
834 sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, 0, MSG_E_SUBGEN,
835 emsg, _("test_rule"));
836 SH_MUTEX_UNLOCK(mutex_thread_nolog);
837 SH_FREE(emsg);
838 }
839
840 if ( rule->flags & RFL_KEEP )
841 {
842 DEBUG("debug: rule %d matches (keep)\n", count);
843 sh_keep_add(rule->label, rule->delay,
844 timestamp == 0 ? time(NULL) : timestamp);
845 }
846
847 else if ( rule->flags & RFL_MARK )
848 {
849 DEBUG("debug: rule %d matches (mark)\n", count);
850 sh_log_mark_update(rule->label,
851 timestamp == 0 ? time(NULL) : timestamp);
852 }
853
854 break; /* return the matching rule; ovector is filled in */
855 }
856
857 if (flag_err_debug == SL_TRUE)
858 {
859 char * emsg = SH_ALLOC(SH_ERRBUF_SIZE);
860 sl_snprintf(emsg, SH_ERRBUF_SIZE, _("Rule %d did not match"),
861 count);
862 SH_MUTEX_LOCK(mutex_thread_nolog);
863 sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, 0, MSG_E_SUBGEN,
864 emsg, _("test_rule"));
865 SH_MUTEX_UNLOCK(mutex_thread_nolog);
866 SH_FREE(emsg);
867 }
868 DEBUG("debug: rule %d did not match\n", count);
869
870 rule = rule->nextrule; ++count;
871 } while (rule);
872 }
873 if (!rule)
874 DEBUG("debug: no match found\n");
875 /* If there was no match, this is NULL */
876 return rule;
877}
878
879/* Test a (struct sh_geval *), which may be single rule or a group of rules,
880 * against msg
881 */
882static struct sh_geval ** dummy2;
883static struct sh_geval ** dummy3;
884
885static struct sh_geval * test_grules (struct sh_heval * host,
886 sh_string * msg,
887 time_t timestamp)
888{
889 struct sh_geval * result = NULL;
890 struct sh_geval * group = host->rulegroups;
891
892 dummy2 = &result;
893 dummy3 = &group;
894
895 if (group && sh_string_len(msg) < (size_t)INT_MAX)
896 {
897 DEBUG("debug: if group\n");
898 do {
899 if( (group->label != NULL) && (0 != (group->flags & RFL_ISGROUP)))
900 {
901 /* this is a rule group */
902
903 if (flag_err_debug == SL_TRUE)
904 {
905 char * emsg = SH_ALLOC(SH_ERRBUF_SIZE);
906 sl_snprintf(emsg, SH_ERRBUF_SIZE, _("Checking group |%s| of rules against |%s|"),
907 sh_string_str(group->label), sh_string_str(msg));
908 SH_MUTEX_LOCK(mutex_thread_nolog);
909 sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, 0, MSG_E_SUBGEN,
910 emsg, _("test_rule"));
911 SH_MUTEX_UNLOCK(mutex_thread_nolog);
912 SH_FREE(emsg);
913 }
914
915 DEBUG("debug: if group->label %s\n", sh_string_str(group->label));
916 if (pcre_exec(group->rule, group->rule_extra,
917 sh_string_str(msg), (int) sh_string_len(msg),
918 0, 0, NULL, 0) >= 0)
919 {
920 result = test_rule(group->nextrule, msg, timestamp);
921 if (result)
922 break;
923 }
924 }
925 else
926 {
927 /* If there is no group label, the 'group' is actually a solitary
928 * rule (not within any group).
929 */
930
931 if (flag_err_debug == SL_TRUE)
932 {
933 char * emsg = SH_ALLOC(SH_ERRBUF_SIZE);
934 sl_snprintf(emsg, SH_ERRBUF_SIZE, _("Checking solitary rules"));
935 SH_MUTEX_LOCK(mutex_thread_nolog);
936 sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, 0, MSG_E_SUBGEN,
937 emsg, _("test_rule"));
938 SH_MUTEX_UNLOCK(mutex_thread_nolog);
939 SH_FREE(emsg);
940 }
941
942 DEBUG("debug: else (single rule)\n");
943 result = test_rule(group, msg, timestamp);
944 if (result)
945 break;
946 }
947 group = group->next; /* next group of rules */
948 } while (group);
949 }
950 return result;
951}
952
953/* Top-level find_rule() function
954 */
955static struct sh_geval * find_rule (sh_string *host,
956 sh_string *msg,
957 time_t timestamp)
958{
959 struct sh_geval * result = NULL;
960 struct sh_heval * hlist = hostlist;
961
962 if (hlist && sh_string_len(host) < (size_t)INT_MAX)
963 {
964 do {
965 if (pcre_exec(hlist->hostname, hlist->hostname_extra,
966 sh_string_str(host), (int) sh_string_len(host),
967 0, 0, NULL, 0) >= 0)
968 {
969 /* matching host, check rules/groups of rules */
970 result = test_grules(hlist, msg, timestamp);
971 if (result)
972 break;
973 }
974 hlist = hlist->next;
975 } while (hlist);
976 }
977 return result;
978}
979
980/* copy the message and replace captured substrings with '___'
981 */
982static sh_string * replace_captures(const sh_string * message,
983 int * ovector, int ovecnum)
984{
985 sh_string * retval = sh_string_new_from_lchar(sh_string_str(message),
986 sh_string_len(message));
987
988 if (ovecnum > 1)
989 {
990 retval = sh_string_replace(retval, &(ovector[2]), (ovecnum-1), "___", 3);
991 }
992 return retval;
993}
994
995static void msg_report(int severity, struct sh_geval * rule, struct sh_logrecord * record)
996{
997 char * tmp;
998 char * msg;
999 sh_string * mmm = NULL;
1000 char * ttt;
1001
1002
1003 SH_MUTEX_LOCK(mutex_thread_nolog);
1004 if (rule) {
1005 mmm = replace_captures(record->message, rule->ovector,
1006 rule->ovecnum);
1007 msg = sh_util_safe_name_keepspace (sh_string_str(mmm));
1008 }
1009 else {
1010 msg = sh_util_safe_name_keepspace (sh_string_str(record->message));
1011 }
1012 tmp = sh_util_safe_name (record->filename);
1013 ttt = sh_util_safe_name_keepspace (sh_string_str(record->timestr));
1014 sh_error_handle (severity, FIL__, __LINE__, 0, MSG_LOGMON_REP,
1015 msg,
1016 ttt,
1017 sh_string_str(record->host),
1018 tmp);
1019 SH_FREE(ttt);
1020 SH_FREE(msg);
1021 SH_FREE(tmp);
1022 if (mmm)
1023 sh_string_destroy(&mmm);
1024 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1025}
1026
1027static void sum_report(int severity, sh_string * host, sh_string * message, sh_string * path)
1028{
1029 char * tmp;
1030 char * msg;
1031
1032 SH_MUTEX_LOCK(mutex_thread_nolog);
1033 tmp = sh_util_safe_name (sh_string_str(path));
1034 msg = sh_util_safe_name_keepspace (sh_string_str(message));
1035 sh_error_handle (severity, FIL__, __LINE__, 0, MSG_LOGMON_SUM,
1036 msg,
1037 sh_string_str(host),
1038 tmp);
1039 SH_FREE(msg);
1040 SH_FREE(tmp);
1041 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1042}
1043
1044static zAVLKey sh_eval_getkey(void const *item)
1045{
1046 return ((struct sh_ceval *)item)->hostname->str;
1047}
1048
1049/* Find the counter, or initialize one if there is none already
1050 */
1051static struct sh_ceval * find_counter(struct sh_geval * rule,
1052 sh_string * host, time_t interval)
1053{
1054 struct sh_ceval * counter;
1055
1056 if (!(rule->counterlist))
1057 {
1058 DEBUG("debug: allocate new counterlist AVL tree\n");
1059 rule->counterlist = zAVLAllocTree(sh_eval_getkey);
1060 }
1061
1062 counter = (struct sh_ceval *) zAVLSearch (rule->counterlist,
1063 sh_string_str(host));
1064
1065 if (!counter)
1066 {
1067 DEBUG("debug: no counter found\n");
1068
1069 counter = SH_ALLOC(sizeof(struct sh_ceval));
1070 counter->hostname = sh_string_new_from_lchar(sh_string_str(host),
1071 sh_string_len(host));
1072 counter->counted_str = NULL;
1073 counter->filename = NULL;
1074 counter->count = 0;
1075 counter->start = time(NULL);
1076 counter->interval = interval;
1077
1078 zAVLInsert(rule->counterlist, counter);
1079 }
1080 return counter;
1081
1082}
1083
1084
1085/* process the counter for a SUM rule
1086 */
1087static int process_counter(struct sh_ceval * counter,
1088 struct sh_geval * rule, struct sh_logrecord * record)
1089{
1090 int retval = -1;
1091 time_t now;
1092
1093 if (!(counter->counted_str))
1094 {
1095 counter->counted_str = replace_captures(record->message, rule->ovector,
1096 rule->ovecnum);
1097 counter->filename = sh_string_new_from_lchar(record->filename,
1098 strlen(record->filename));
1099 DEBUG("debug: counted_str after replace: %s\n",
1100 sh_string_str(counter->counted_str));
1101 }
1102
1103 ++(counter->count);
1104 now = time(NULL); now -= counter->start;
1105 DEBUG("debug: count %lu, interval %lu, time %lu\n",
1106 counter->count, counter->interval, now);
1107 if (now >= counter->interval)
1108 {
1109 DEBUG("debug: report count\n");
1110 sum_report(rule->queue->severity, counter->hostname,
1111 counter->counted_str, counter->filename);
1112 counter->start = time(NULL);
1113 counter->count = 0;
1114 }
1115 return retval;
1116}
1117
1118/* Process a rule
1119 */
1120static int process_rule(struct sh_geval * rule, struct sh_logrecord * record)
1121{
1122 int retval = -1;
1123 struct sh_qeval * queue = rule->queue;
1124
1125 if (queue)
1126 {
1127 DEBUG("debug: queue policy = %d found\n", queue->policy);
1128 if (queue->policy == EVAL_REPORT)
1129 {
1130 DEBUG("debug: EVAL_REPORT host: %s, message: %s\n",
1131 sh_string_str(record->host),
1132 sh_string_str(record->message));
1133 msg_report(queue->severity, rule, record);
1134 retval = 0;
1135 }
1136 else if (queue->policy == EVAL_SUM)
1137 {
1138
1139 struct sh_ceval * counter =
1140 find_counter(rule, record->host, queue->interval);
1141 DEBUG("debug: EVAL_SUM host: %s, message: %s\n",
1142 sh_string_str(record->host),
1143 sh_string_str(record->message));
1144 if (counter)
1145 {
1146 DEBUG("debug: counter found\n");
1147 retval = process_counter(counter, rule, record);
1148 }
1149 }
1150 }
1151 else
1152 {
1153 DEBUG("debug: no queue found -- trash\n");
1154 /* No queue means 'trash' */
1155 retval = 0;
1156 }
1157 return retval;
1158}
1159
1160#define DEFAULT_SEVERITY (-1)
1161
1162int sh_eval_process_msg(struct sh_logrecord * record)
1163{
1164 static unsigned long i = 0;
1165 if (record)
1166 {
1167 struct sh_geval * rule = find_rule (record->host,
1168 record->message,
1169 record->timestamp);
1170
1171 if (rule)
1172 {
1173 DEBUG("debug: (%lu) rule found\n", i); ++i;
1174 return process_rule(rule, record);
1175 }
1176 else
1177 {
1178 DEBUG("debug: (%lu) no rule found\n", i); ++i;
1179 msg_report(DEFAULT_SEVERITY, NULL, record);
1180 }
1181
1182 sh_repeat_message_check(record->host,
1183 record->message,
1184 record->timestamp);
1185
1186 return 0;
1187 }
1188 return -1;
1189}
1190
1191#endif
Note: See TracBrowser for help on using the repository browser.