izmjene licence
[ossec-hids.git] / src / analysisd / testrule.c
1 /* @(#) $Id: ./src/analysisd/testrule.c, 2012/07/23 dcid Exp $
2  */
3
4 /* Copyright (C) 2009 Trend Micro Inc.
5  * All rights reserved.
6  *
7  * This program is a free software; you can redistribute it
8  * and/or modify it under the terms of the GNU General Public
9  * License (version 2) as published by the FSF - Free Software
10  * Foundation.
11  *
12  * License details at the LICENSE file included with OSSEC or
13  * online at: http://www.ossec.net/en/licensing.html
14  */
15
16
17 /* Part of the OSSEC
18  * Available at http://www.ossec.net
19  */
20
21
22 /* ossec-analysisd.
23  * Responsible for correlation and log decoding.
24  */
25
26 #ifdef ARGV0
27    #undef ARGV0
28    #define ARGV0 "ossec-testrule"
29 #endif
30
31
32
33 #include "shared.h"
34
35 #include "alerts/alerts.h"
36 #include "alerts/getloglocation.h"
37 #include "os_execd/execd.h"
38
39 #include "os_regex/os_regex.h"
40 #include "os_net/os_net.h"
41
42
43 /** Local headers **/
44 #include "active-response.h"
45 #include "config.h"
46 #include "rules.h"
47 #include "stats.h"
48
49 #include "eventinfo.h"
50 #include "analysisd.h"
51
52
53
54 /** Internal Functions **/
55 void OS_ReadMSG(int m_queue, char *ut_str);
56 RuleInfo *OS_CheckIfRuleMatch(Eventinfo *lf, RuleNode *curr_node);
57
58
59 /** External functions prototypes (only called here) **/
60
61 /* For config  */
62 int GlobalConf(char * cfgfile);
63
64
65 /* For rules */
66 void Rules_OP_CreateRules();
67 void Lists_OP_CreateLists();
68 int Rules_OP_ReadRules(char * cfgfile);
69 int _setlevels(RuleNode *node, int nnode);
70 int AddHash_Rule(RuleNode *node);
71
72
73 /* For cleanmsg */
74 int OS_CleanMSG(char *msg, Eventinfo *lf);
75
76
77 /* for FTS */
78 int FTS_Init();
79 int AddtoIGnore(Eventinfo *lf);
80 int IGnore(Eventinfo *lf);
81
82
83 /* For decoders */
84 void DecodeEvent(Eventinfo *lf);
85
86
87 /* For Decoders */
88 int ReadDecodeXML(char *file);
89 int SetDecodeXML();
90
91
92 void logtest_help(const char *prog)
93 {
94     print_out(" ");
95     print_out("%s %s - %s (%s)", __ossec_name, __version, __author, __contact);
96     print_out("%s", __site);
97     print_out(" ");
98     print_out("  %s: -[Vatfdh] [-U ut_str] [-u user] [-g group] [-c config] [-D dir]", prog);
99     print_out("    -V          Version and license message");
100     print_out("    -a          Alerts output");
101     print_out("    -t          Test configuration");
102     print_out("    -v          Verbose (full) output/rule debugging");
103     print_out("    -d          Execute in debug mode");
104     print_out("    -h          This help message");
105     print_out("    -U <rule:alert:decoder>   Unit test. Refer to contrib/ossec-testing/runtests.py");
106     print_out("    -u <user>   Run as 'user'");
107     print_out("    -g <group>  Run as 'group'");
108     print_out("    -c <config> Read the 'config' file");
109     print_out("    -D <dir>    Chroot to 'dir'");
110     print_out(" ");
111     exit(1);
112 }
113
114
115
116 /** int main(int argc, char **argv)
117  */
118 int main(int argc, char **argv)
119 {
120     int t_config = 0;
121     int c = 0, m_queue = 0;
122     char *ut_str = NULL;
123
124     char *dir = DEFAULTDIR;
125     // TODO: delete or implement
126     char *user __attribute__((unused)) = USER;
127     // TODO: delete or implement
128     char *group __attribute__((unused)) = GROUPGLOBAL;
129
130     char *cfg = DEFAULTCPATH;
131
132     /* Setting the name */
133     OS_SetName(ARGV0);
134
135     thishour = 0;
136     today = 0;
137     prev_year = 0;
138     full_output = 0;
139     alert_only = 0;
140
141     active_responses = NULL;
142     memset(prev_month, '\0', 4);
143
144     while((c = getopt(argc, argv, "VatvdhU:u:g:D:c:")) != -1){
145         switch(c){
146             case 'V':
147                 print_version();
148                 break;
149             case 't':
150                 t_config = 1;
151                 break;
152             case 'h':
153                 logtest_help(ARGV0);
154                 break;
155             case 'd':
156                 nowDebug();
157                 break;
158             case 'U':
159                 if(!optarg)
160                     ErrorExit("%s: -U needs an argument",ARGV0);
161                 ut_str = optarg;
162                 break;
163             case 'u':
164                 if(!optarg)
165                     ErrorExit("%s: -u needs an argument",ARGV0);
166                 user = optarg;
167                 break;
168             case 'g':
169                 if(!optarg)
170                     ErrorExit("%s: -g needs an argument",ARGV0);
171                 group = optarg;
172                 break;
173             case 'D':
174                 if(!optarg)
175                     ErrorExit("%s: -D needs an argument",ARGV0);
176                 dir = optarg;
177                 break;
178             case 'c':
179                 if(!optarg)
180                     ErrorExit("%s: -c needs an argument",ARGV0);
181                 cfg = optarg;
182                 break;
183             case 'a':
184                 alert_only = 1;
185                 break;
186             case 'v':
187                 full_output = 1;
188                 break;
189             default:
190                 logtest_help(ARGV0);
191                 break;
192         }
193
194     }
195
196
197
198
199     /* Reading configuration file */
200     if(GlobalConf(cfg) < 0)
201     {
202         ErrorExit(CONFIG_ERROR,ARGV0, cfg);
203     }
204
205     debug1(READ_CONFIG, ARGV0);
206
207
208
209     /* Getting servers hostname */
210     memset(__shost, '\0', 512);
211     if(gethostname(__shost, 512 -1) != 0)
212     {
213         strncpy(__shost, OSSEC_SERVER, 512 -1);
214     }
215     else
216     {
217         char *_ltmp;
218
219         /* Remove domain part if available */
220         _ltmp = strchr(__shost, '.');
221         if(_ltmp)
222             *_ltmp = '\0';
223     }
224
225
226
227     if(chdir(dir) != 0)
228         ErrorExit(CHROOT_ERROR,ARGV0,dir);
229
230
231     /*
232      * Anonymous Section: Load rules, decoders, and lists
233      *
234      * As lists require two pass loading of rules that make use of list lookups
235      * are created with blank database structs, and need to be filled in after
236      * completion of all rules and lists.
237      */
238     {
239         { /* Lad decders */
240             /* Initializing the decoders list */
241             OS_CreateOSDecoderList();
242
243             if(!Config.decoders)
244             { /* Legacy loading */
245                 /* Reading decoders */
246                 if(!ReadDecodeXML("etc/decoder.xml"))
247                 {
248                     ErrorExit(CONFIG_ERROR, ARGV0,  XML_DECODER);
249                 }
250
251                 /* Reading local ones. */
252                 c = ReadDecodeXML("etc/local_decoder.xml");
253                 if(!c)
254                 {
255                     if((c != -2))
256                         ErrorExit(CONFIG_ERROR, ARGV0,  XML_LDECODER);
257                 }
258                 else
259                 {
260                     verbose("%s: INFO: Reading local decoder file.", ARGV0);
261                 }
262             }
263             else
264             { /* New loaded based on file speified in ossec.conf */
265                 char **decodersfiles;
266                 decodersfiles = Config.decoders;
267                 while( decodersfiles && *decodersfiles)
268                 {
269
270                     verbose("%s: INFO: Reading decoder file %s.", ARGV0, *decodersfiles);
271                     if(!ReadDecodeXML(*decodersfiles))
272                         ErrorExit(CONFIG_ERROR, ARGV0, *decodersfiles);
273
274                     free(*decodersfiles);
275                     decodersfiles++;
276                 }
277             }
278
279             /* Load decoders */
280             SetDecodeXML();
281         }
282         { /* Load Lists */
283             /* Initializing the lists of list struct */
284             Lists_OP_CreateLists();
285             /* Load each list into list struct */
286             {
287                 char **listfiles;
288                 listfiles = Config.lists;
289                 while(listfiles && *listfiles)
290                 {
291                     verbose("%s: INFO: Reading the lists file: '%s'", ARGV0, *listfiles);
292                     if(Lists_OP_LoadList(*listfiles) < 0)
293                         ErrorExit(LISTS_ERROR, ARGV0, *listfiles);
294                     free(*listfiles);
295                     listfiles++;
296                 }
297                 free(Config.lists);
298                 Config.lists = NULL;
299             }
300         }
301         { /* Load Rules */
302             /* Creating the rules list */
303             Rules_OP_CreateRules();
304
305             /* Reading the rules */
306             {
307                 char **rulesfiles;
308                 rulesfiles = Config.includes;
309                 while(rulesfiles && *rulesfiles)
310                 {
311                     debug1("%s: INFO: Reading rules file: '%s'", ARGV0, *rulesfiles);
312                     if(Rules_OP_ReadRules(*rulesfiles) < 0)
313                         ErrorExit(RULES_ERROR, ARGV0, *rulesfiles);
314
315                     free(*rulesfiles);
316                     rulesfiles++;
317                 }
318
319                 free(Config.includes);
320                 Config.includes = NULL;
321             }
322
323             /* Find all rules with that require list lookups and attache the
324              * the correct list struct to the rule.  This keeps rules from having to
325              * search thought the list of lists for the correct file during rule evaluation.
326              */
327             OS_ListLoadRules();
328         }
329     }
330
331
332     /* Fixing the levels/accuracy */
333     {
334         int total_rules;
335         RuleNode *tmp_node = OS_GetFirstRule();
336
337         total_rules = _setlevels(tmp_node, 0);
338         debug1("%s: INFO: Total rules enabled: '%d'", ARGV0, total_rules);
339     }
340
341
342     /* Creating a rules hash (for reading alerts from other servers). */
343     {
344         RuleNode *tmp_node = OS_GetFirstRule();
345         Config.g_rules_hash = OSHash_Create();
346         if(!Config.g_rules_hash)
347         {
348             ErrorExit(MEM_ERROR, ARGV0);
349         }
350         AddHash_Rule(tmp_node);
351     }
352
353
354     if(t_config == 1)
355     {
356         exit(0);
357     }
358
359
360     /* Start up message */
361     verbose(STARTUP_MSG, ARGV0, getpid());
362
363
364     /* Going to main loop */
365     OS_ReadMSG(m_queue, ut_str);
366
367
368     exit(0);
369
370 }
371
372
373
374 /* OS_ReadMSG.
375  * Main function. Receives the messages(events)
376  * and analyze them all.
377  */
378 void OS_ReadMSG(int m_queue, char *ut_str)
379 {
380     int i;
381     char msg[OS_MAXSTR +1];
382     int exit_code = 0;
383     char *ut_alertlevel = NULL;
384     char *ut_rulelevel = NULL;
385     char *ut_decoder_name = NULL;
386
387     if(ut_str)
388     {
389         /* XXX Break apart string */
390         ut_rulelevel = ut_str;
391         ut_alertlevel =  strchr(ut_rulelevel, ':');
392         if(!ut_alertlevel)
393         {
394             ErrorExit("%s: -U requires the matching format to be "
395                       "\"<rule_id>:<alert_level>:<decoder_name>\"", ARGV0);
396         }
397         else
398         {
399             *ut_alertlevel = '\0';
400             ut_alertlevel++;
401         }
402         ut_decoder_name = strchr(ut_alertlevel, ':');
403         if(!ut_decoder_name)
404         {
405             ErrorExit("%s: -U requires the matching format to be "
406                       "\"<rule_id>:<alert_level>:<decoder_name>\"", ARGV0);
407         }
408         else
409         {
410             *ut_decoder_name = '\0';
411             ut_decoder_name++;
412         }
413     }
414
415     RuleInfoDetail *last_info_detail;
416     Eventinfo *lf;
417
418
419     /* Null to global currently pointers */
420     currently_rule = NULL;
421
422
423     /* Creating the event list */
424     OS_CreateEventList(Config.memorysize);
425
426
427     /* Initiating the FTS list */
428     if(!FTS_Init())
429     {
430         ErrorExit(FTS_LIST_ERROR, ARGV0);
431     }
432
433
434     __crt_ftell = 1;
435
436
437     /* Getting currently time before starting */
438     c_time = time(NULL);
439
440
441     /* Doing some cleanup */
442     memset(msg, '\0', OS_MAXSTR +1);
443
444
445     if(!alert_only)
446     print_out("%s: Type one log per line.\n", ARGV0);
447
448
449     /* Daemon loop */
450     while(1)
451     {
452         lf = (Eventinfo *)calloc(1,sizeof(Eventinfo));
453
454         /* This shouldn't happen .. */
455         if(lf == NULL)
456         {
457             ErrorExit(MEM_ERROR,ARGV0);
458         }
459
460
461         /* Fixing the msg. */
462         snprintf(msg, 15, "1:stdin:");
463
464
465
466         /* Receive message from queue */
467         if(fgets(msg +8, OS_MAXSTR -8, stdin))
468         {
469             RuleNode *rulenode_pt;
470
471             /* Getting the time we received the event */
472             c_time = time(NULL);
473
474
475             /* Removing new line. */
476             if(msg[strlen(msg) -1] == '\n')
477                 msg[strlen(msg) -1] = '\0';
478
479
480             /* Make sure we ignore blank lines. */
481             if(strlen(msg) < 10)
482             {
483                 continue;
484             }
485
486
487             if(!alert_only)print_out("\n");
488
489
490             /* Default values for the log info */
491             Zero_Eventinfo(lf);
492
493
494             /* Clean the msg appropriately */
495             if(OS_CleanMSG(msg, lf) < 0)
496             {
497                 merror(IMSG_ERROR,ARGV0,msg);
498
499                 Free_Eventinfo(lf);
500
501                 continue;
502             }
503
504
505             /* Currently rule must be null in here */
506             currently_rule = NULL;
507
508
509             /***  Running decoders ***/
510
511             /* Getting log size */
512             lf->size = strlen(lf->log);
513
514
515             /* Decoding event. */
516             DecodeEvent(lf);
517
518
519             /* Looping all the rules */
520             rulenode_pt = OS_GetFirstRule();
521             if(!rulenode_pt)
522             {
523                 ErrorExit("%s: Rules in an inconsistent state. Exiting.",
524                         ARGV0);
525             }
526
527
528             #ifdef TESTRULE
529             if(full_output && !alert_only)
530                 print_out("\n**Rule debugging:");
531             #endif
532
533
534             do
535             {
536                 if(lf->decoder_info->type == OSSEC_ALERT)
537                 {
538                     if(!lf->generated_rule)
539                     {
540                         break;
541                     }
542
543                     /* We go ahead in here and process the alert. */
544                     currently_rule = lf->generated_rule;
545                 }
546
547                 /* The categories must match */
548                 else if(rulenode_pt->ruleinfo->category !=
549                         lf->decoder_info->type)
550                 {
551                     continue;
552                 }
553
554
555                 /* Checking each rule. */
556                 else if((currently_rule = OS_CheckIfRuleMatch(lf, rulenode_pt))
557                         == NULL)
558                 {
559                     continue;
560                 }
561
562                 #ifdef TESTRULE
563                 if(!alert_only)
564                 {
565                   char *(ruleinfodetail_text[])={"Text","Link","CVE","OSVDB","BUGTRACKID"};
566                   print_out("\n**Phase 3: Completed filtering (rules).");
567                   print_out("       Rule id: '%d'", currently_rule->sigid);
568                   print_out("       Level: '%d'", currently_rule->level);
569                   print_out("       Description: '%s'",currently_rule->comment);
570                   for (last_info_detail = currently_rule->info_details; last_info_detail != NULL; last_info_detail = last_info_detail->next)
571                   {
572                       print_out("       Info - %s: '%s'", ruleinfodetail_text[last_info_detail->type], last_info_detail->data);
573                   }
574                 }
575                 #endif
576
577
578
579                 /* Ignore level 0 */
580                 if(currently_rule->level == 0)
581                 {
582                     break;
583                 }
584
585
586                 /* Checking ignore time */
587                 if(currently_rule->ignore_time)
588                 {
589                     if(currently_rule->time_ignored == 0)
590                     {
591                         currently_rule->time_ignored = lf->time;
592                     }
593                     /* If the currently time - the time the rule was ignored
594                      * is less than the time it should be ignored,
595                      * leave (do not alert again).
596                      */
597                     else if((lf->time - currently_rule->time_ignored)
598                             < currently_rule->ignore_time)
599                     {
600                         break;
601                     }
602                     else
603                     {
604                         currently_rule->time_ignored = 0;
605                     }
606                 }
607
608                 /* Pointer to the rule that generated it */
609                 lf->generated_rule = currently_rule;
610
611
612                 /* Checking if we should ignore it */
613                 if(currently_rule->ckignore && IGnore(lf))
614                 {
615                     /* Ignoring rule */
616                     lf->generated_rule = NULL;
617                     break;
618                 }
619
620                 /* Checking if we need to add to ignore list */
621                 if(currently_rule->ignore)
622                 {
623                     AddtoIGnore(lf);
624                 }
625
626
627                 /* Log the alert if configured to ... */
628                 if(currently_rule->alert_opts & DO_LOGALERT)
629                 {
630                     if(alert_only)
631                     {
632                         OS_LogOutput(lf);
633                         __crt_ftell++;
634                     }
635                     else
636                     {
637                         print_out("**Alert to be generated.\n\n");
638                     }
639                 }
640
641
642                 /* Copy the structure to the state memory of if_matched_sid */
643                 if(currently_rule->sid_prev_matched)
644                 {
645                     if(!OSList_AddData(currently_rule->sid_prev_matched, lf))
646                     {
647                         merror("%s: Unable to add data to sig list.", ARGV0);
648                     }
649                     else
650                     {
651                         lf->sid_node_to_delete =
652                             currently_rule->sid_prev_matched->last_node;
653                     }
654                 }
655                 /* Group list */
656                 else if(currently_rule->group_prev_matched)
657                 {
658                     i = 0;
659
660                     while(i < currently_rule->group_prev_matched_sz)
661                     {
662                         if(!OSList_AddData(
663                                 currently_rule->group_prev_matched[i],
664                                 lf))
665                         {
666                            merror("%s: Unable to add data to grp list.",ARGV0);
667                         }
668                         i++;
669                     }
670                 }
671
672                 OS_AddEvent(lf);
673
674                 break;
675
676             }while((rulenode_pt = rulenode_pt->next) != NULL);
677
678             if(ut_str)
679             {
680                 /*setup exit code if we are doing unit testing*/
681                 char holder[1024];
682                 holder[1] = '\0';
683                 exit_code = 3;
684                 if(lf->decoder_info->name != NULL && strcasecmp(ut_decoder_name, lf->decoder_info->name) == 0)
685                 {
686                     exit_code--;
687                     snprintf(holder, 1023, "%d", currently_rule->sigid);
688                     if(strcasecmp(ut_rulelevel, holder) == 0)
689                     {
690                         exit_code--;
691                         snprintf(holder, 1023, "%d", currently_rule->level);
692                         if(strcasecmp(ut_alertlevel, holder) == 0)
693                         {
694                             exit_code--;
695                             printf("%d\n",exit_code);
696                         }
697                     }
698                 }
699             }
700
701
702             /* Only clear the memory if the eventinfo was not
703              * added to the stateful memory
704              * -- message is free inside clean event --
705              */
706             if(lf->generated_rule == NULL)
707                 Free_Eventinfo(lf);
708
709         }
710         else
711         {
712             exit(exit_code);
713         }
714     }
715     exit(exit_code);
716     return;
717 }
718
719
720
721 /* EOF */
722