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