Imported Upstream version 2.7
[ossec-hids.git] / src / analysisd / testrule.c
index db769f7..d2f5b25 100755 (executable)
@@ -1,14 +1,15 @@
-/* @(#) $Id: testrule.c,v 1.7 2009/08/25 11:30:57 dcid Exp $ */
+/* @(#) $Id: ./src/analysisd/testrule.c, 2012/07/23 dcid Exp $
+ */
 
 /* Copyright (C) 2009 Trend Micro Inc.
  * All rights reserved.
  *
  * This program is a free software; you can redistribute it
  * and/or modify it under the terms of the GNU General Public
- * License (version 3) as published by the FSF - Free Software
+ * License (version 2) as published by the FSF - Free Software
  * Foundation.
  *
- * License details at the LICENSE file included with OSSEC or 
+ * License details at the LICENSE file included with OSSEC or
  * online at: http://www.ossec.net/en/licensing.html
  */
 
@@ -16,7 +17,7 @@
 /* Part of the OSSEC
  * Available at http://www.ossec.net
  */
-  
+
 
 /* ossec-analysisd.
  * Responsible for correlation and log decoding.
@@ -27,6 +28,8 @@
    #define ARGV0 "ossec-testrule"
 #endif
 
+
+
 #include "shared.h"
 
 #include "alerts/alerts.h"
 #include "config.h"
 #include "rules.h"
 #include "stats.h"
+
 #include "eventinfo.h"
 #include "analysisd.h"
 
 
 
 /** Internal Functions **/
-void OS_ReadMSG(int m_queue);
+void OS_ReadMSG(int m_queue, char *ut_str);
 RuleInfo *OS_CheckIfRuleMatch(Eventinfo *lf, RuleNode *curr_node);
 
 
@@ -60,6 +64,7 @@ int GlobalConf(char * cfgfile);
 
 /* For rules */
 void Rules_OP_CreateRules();
+void Lists_OP_CreateLists();
 int Rules_OP_ReadRules(char * cfgfile);
 int _setlevels(RuleNode *node, int nnode);
 int AddHash_Rule(RuleNode *node);
@@ -88,13 +93,38 @@ int ReadDecodeXML(char *file);
 int SetDecodeXML();
 
 
+void logtest_help(const char *prog)
+{
+    print_out(" ");
+    print_out("%s %s - %s (%s)", __name, __version, __author, __contact);
+    print_out("%s", __site);
+    print_out(" ");
+    print_out("  %s: -[Vatfdh] [-U ut_str] [-u user] [-g group] [-c config] [-D dir]", prog);
+    print_out("    -V          Version and license message");
+    print_out("    -a          Alerts output");
+    print_out("    -t          Test configuration");
+    print_out("    -v          Verbose (full) output/rule debugging");
+    print_out("    -d          Execute in debug mode");
+    print_out("    -h          This help message");
+    print_out("    -U <rule:alert:decoder>   Unit test. Refer to contrib/ossec-testing/runtests.py");
+    print_out("    -u <user>   Run as 'user'");
+    print_out("    -g <group>  Run as 'group'");
+    print_out("    -c <config> Read the 'config' file");
+    print_out("    -D <dir>    Chroot to 'dir'");
+    print_out(" ");
+    exit(1);
+}
+
 
 
 /** int main(int argc, char **argv)
  */
 int main(int argc, char **argv)
 {
+    int t_config = 0;
     int c = 0, m_queue = 0;
+    char *ut_str = NULL;
+
     char *dir = DEFAULTDIR;
     char *user = USER;
     char *group = GROUPGLOBAL;
@@ -108,20 +138,30 @@ int main(int argc, char **argv)
     today = 0;
     prev_year = 0;
     full_output = 0;
+    alert_only = 0;
+
     active_responses = NULL;
     memset(prev_month, '\0', 4);
 
-    while((c = getopt(argc, argv, "Vfdhu:g:D:c:")) != -1){
+    while((c = getopt(argc, argv, "VatvdhU:u:g:D:c:")) != -1){
         switch(c){
            case 'V':
                print_version();
                break;
+            case 't':
+                t_config = 1;
+                break;
             case 'h':
-                help(ARGV0);
+                logtest_help(ARGV0);
                 break;
             case 'd':
                 nowDebug();
                 break;
+            case 'U':
+                if(!optarg)
+                    ErrorExit("%s: -U needs an argument",ARGV0);
+                ut_str = optarg;
+                break;
             case 'u':
                 if(!optarg)
                     ErrorExit("%s: -u needs an argument",ARGV0);
@@ -136,22 +176,28 @@ int main(int argc, char **argv)
                 if(!optarg)
                     ErrorExit("%s: -D needs an argument",ARGV0);
                 dir = optarg;
+                break;
             case 'c':
                 if(!optarg)
                     ErrorExit("%s: -c needs an argument",ARGV0);
                 cfg = optarg;
                 break;
-            case 'f':
-                full_output = 1;    
+            case 'a':
+                alert_only = 1;
+                break;
+            case 'v':
+                full_output = 1;
                 break;
             default:
-                help(ARGV0);
+                logtest_help(ARGV0);
                 break;
         }
 
     }
 
 
+
+
     /* Reading configuration file */
     if(GlobalConf(cfg) < 0)
     {
@@ -159,14 +205,14 @@ int main(int argc, char **argv)
     }
 
     debug1(READ_CONFIG, ARGV0);
-        
 
-    
+
+
     /* Getting servers hostname */
     memset(__shost, '\0', 512);
     if(gethostname(__shost, 512 -1) != 0)
     {
-        strncpy(__shost, OSSEC_SERVER, 512 -1);    
+        strncpy(__shost, OSSEC_SERVER, 512 -1);
     }
     else
     {
@@ -177,60 +223,121 @@ int main(int argc, char **argv)
         if(_ltmp)
             *_ltmp = '\0';
     }
-    
+
 
 
     if(chdir(dir) != 0)
         ErrorExit(CHROOT_ERROR,ARGV0,dir);
 
 
-
-    /* Reading decoders */
-    OS_CreateOSDecoderList();
-    if(!ReadDecodeXML("etc/decoder.xml"))
+    /*
+     * Anonymous Section: Load rules, decoders, and lists
+     *
+     * As lists require two pass loading of rules that make use of list lookups
+     * are created with blank database structs, and need to be filled in after
+     * completion of all rules and lists.
+     */
     {
-        ErrorExit(CONFIG_ERROR, ARGV0,  XML_DECODER);
-    }
+        { /* Lad decders */
+            /* Initializing the decoders list */
+            OS_CreateOSDecoderList();
+
+            if(!Config.decoders)
+            { /* Legacy loading */
+                /* Reading decoders */
+                if(!ReadDecodeXML("etc/decoder.xml"))
+                {
+                    ErrorExit(CONFIG_ERROR, ARGV0,  XML_DECODER);
+                }
 
-    c = ReadDecodeXML("etc/local_decoder.xml");
-    if(!c)
-    {
-        if((c != -2))
-            ErrorExit(CONFIG_ERROR, ARGV0,  XML_LDECODER);
-    }
-    SetDecodeXML();
+                /* Reading local ones. */
+                c = ReadDecodeXML("etc/local_decoder.xml");
+                if(!c)
+                {
+                    if((c != -2))
+                        ErrorExit(CONFIG_ERROR, ARGV0,  XML_LDECODER);
+                }
+                else
+                {
+                    verbose("%s: INFO: Reading local decoder file.", ARGV0);
+                }
+            }
+            else
+            { /* New loaded based on file speified in ossec.conf */
+                char **decodersfiles;
+                decodersfiles = Config.decoders;
+                while( decodersfiles && *decodersfiles)
+                {
 
-    
-    /* Creating the rules list */
-    Rules_OP_CreateRules();
+                    verbose("%s: INFO: Reading decoder file %s.", ARGV0, *decodersfiles);
+                    if(!ReadDecodeXML(*decodersfiles))
+                        ErrorExit(CONFIG_ERROR, ARGV0, *decodersfiles);
 
-   
-    /* Reading the rules */
-    {
-        char **rulesfiles;
-        rulesfiles = Config.includes;
-        while(rulesfiles && *rulesfiles)
-        {
-            debug1("%s: INFO: Reading rules file: '%s'", ARGV0, *rulesfiles);
-            if(Rules_OP_ReadRules(*rulesfiles) < 0)
-                ErrorExit(RULES_ERROR, ARGV0, *rulesfiles);
-                
-            free(*rulesfiles);    
-            rulesfiles++;    
+                    free(*decodersfiles);
+                    decodersfiles++;
+                }
+            }
+
+            /* Load decoders */
+            SetDecodeXML();
+        }
+        { /* Load Lists */
+            /* Initializing the lists of list struct */
+            Lists_OP_CreateLists();
+            /* Load each list into list struct */
+            {
+                char **listfiles;
+                listfiles = Config.lists;
+                while(listfiles && *listfiles)
+                {
+                    verbose("%s: INFO: Reading the lists file: '%s'", ARGV0, *listfiles);
+                    if(Lists_OP_LoadList(*listfiles) < 0)
+                        ErrorExit(LISTS_ERROR, ARGV0, *listfiles);
+                    free(*listfiles);
+                    listfiles++;
+                }
+                free(Config.lists);
+                Config.lists = NULL;
+            }
         }
+        { /* Load Rules */
+            /* Creating the rules list */
+            Rules_OP_CreateRules();
 
-        free(Config.includes);
-        Config.includes = NULL;
+            /* Reading the rules */
+            {
+                char **rulesfiles;
+                rulesfiles = Config.includes;
+                while(rulesfiles && *rulesfiles)
+                {
+                    debug1("%s: INFO: Reading rules file: '%s'", ARGV0, *rulesfiles);
+                    if(Rules_OP_ReadRules(*rulesfiles) < 0)
+                        ErrorExit(RULES_ERROR, ARGV0, *rulesfiles);
+
+                    free(*rulesfiles);
+                    rulesfiles++;
+                }
+
+                free(Config.includes);
+                Config.includes = NULL;
+            }
+
+            /* Find all rules with that require list lookups and attache the
+             * the correct list struct to the rule.  This keeps rules from having to
+             * search thought the list of lists for the correct file during rule evaluation.
+             */
+            OS_ListLoadRules();
+        }
     }
-    
-    
+
+
     /* Fixing the levels/accuracy */
     {
         int total_rules;
         RuleNode *tmp_node = OS_GetFirstRule();
 
         total_rules = _setlevels(tmp_node, 0);
-        debug1("%s: INFO: Total rules enabled: '%d'", ARGV0, total_rules);    
+        debug1("%s: INFO: Total rules enabled: '%d'", ARGV0, total_rules);
     }
 
 
@@ -246,17 +353,22 @@ int main(int argc, char **argv)
     }
 
 
-    
+    if(t_config == 1)
+    {
+        exit(0);
+    }
+
+
     /* Start up message */
     verbose(STARTUP_MSG, ARGV0, getpid());
 
 
     /* Going to main loop */   
-    OS_ReadMSG(m_queue);
+    OS_ReadMSG(m_queue, ut_str);
 
 
     exit(0);
-    
+
 }
 
 
@@ -265,10 +377,44 @@ int main(int argc, char **argv)
  * Main function. Receives the messages(events)
  * and analyze them all.
  */
-void OS_ReadMSG(int m_queue)
+void OS_ReadMSG(int m_queue, char *ut_str)
 {
     int i;
     char msg[OS_MAXSTR +1];
+    int exit_code = 0;
+    char *ut_alertlevel = NULL;
+    char *ut_rulelevel = NULL;
+    char *ut_decoder_name = NULL;
+
+    if(ut_str)
+    {
+        /* XXX Break apart string */
+        ut_rulelevel = ut_str;
+        ut_alertlevel =  strchr(ut_rulelevel, ':');
+        if(!ut_alertlevel)
+        {
+            ErrorExit("%s: -U requires the matching format to be "
+                      "\"<rule_id>:<alert_level>:<decoder_name>\"", ARGV0);
+        }
+        else
+        {
+            *ut_alertlevel = '\0';
+            ut_alertlevel++;
+        }
+        ut_decoder_name = strchr(ut_alertlevel, ':');
+        if(!ut_decoder_name)
+        {
+            ErrorExit("%s: -U requires the matching format to be "
+                      "\"<rule_id>:<alert_level>:<decoder_name>\"", ARGV0);
+        }
+        else
+        {
+            *ut_decoder_name = '\0';
+            ut_decoder_name++;
+        }
+    }
+
+    RuleInfoDetail *last_info_detail;
     Eventinfo *lf;
 
 
@@ -285,7 +431,9 @@ void OS_ReadMSG(int m_queue)
     {
         ErrorExit(FTS_LIST_ERROR, ARGV0);
     }
-                                
+
+
+    __crt_ftell = 1;
 
 
     /* Getting currently time before starting */
@@ -294,16 +442,17 @@ void OS_ReadMSG(int m_queue)
 
     /* Doing some cleanup */
     memset(msg, '\0', OS_MAXSTR +1);
-    
 
+
+    if(!alert_only)
     print_out("%s: Type one log per line.\n", ARGV0);
-    
-    
+
+
     /* Daemon loop */
     while(1)
     {
         lf = (Eventinfo *)calloc(1,sizeof(Eventinfo));
-        
+
         /* This shouldn't happen .. */
         if(lf == NULL)
         {
@@ -312,12 +461,12 @@ void OS_ReadMSG(int m_queue)
 
 
         /* Fixing the msg. */
-        strncpy(msg, "1:a:", 5);
-        
-    
-        
+        snprintf(msg, 15, "1:stdin:");
+
+
+
         /* Receive message from queue */
-        if(fgets(msg +4, OS_MAXSTR, stdin))
+        if(fgets(msg +8, OS_MAXSTR, stdin))
         {
             RuleNode *rulenode_pt;
 
@@ -331,14 +480,14 @@ void OS_ReadMSG(int m_queue)
 
 
             /* Make sure we ignore blank lines. */
-            if(strlen(msg) < 6)
+            if(strlen(msg) < 10)
             {
                 continue;
             }
-            
-            
-            print_out("\n");
-            
+
+
+            if(!alert_only)print_out("\n");
+
 
             /* Default values for the log info */
             Zero_Eventinfo(lf);
@@ -367,19 +516,19 @@ void OS_ReadMSG(int m_queue)
 
             /* Decoding event. */
             DecodeEvent(lf);
-            
+
 
             /* Looping all the rules */
             rulenode_pt = OS_GetFirstRule();
-            if(!rulenode_pt) 
+            if(!rulenode_pt)
             {
                 ErrorExit("%s: Rules in an inconsistent state. Exiting.",
                         ARGV0);
             }
 
-            
+
             #ifdef TESTRULE
-            if(full_output)
+            if(full_output && !alert_only)
                 print_out("\n**Rule debugging:");
             #endif
 
@@ -396,9 +545,9 @@ void OS_ReadMSG(int m_queue)
                     /* We go ahead in here and process the alert. */
                     currently_rule = lf->generated_rule;
                 }
-                                                                                                                                                                                            
+
                 /* The categories must match */
-                else if(rulenode_pt->ruleinfo->category != 
+                else if(rulenode_pt->ruleinfo->category !=
                         lf->decoder_info->type)
                 {
                     continue;
@@ -406,19 +555,27 @@ void OS_ReadMSG(int m_queue)
 
 
                 /* Checking each rule. */
-                else if((currently_rule = OS_CheckIfRuleMatch(lf, rulenode_pt)) 
+                else if((currently_rule = OS_CheckIfRuleMatch(lf, rulenode_pt))
                         == NULL)
                 {
                     continue;
                 }
 
                 #ifdef TESTRULE
+                if(!alert_only)
+                {
+                  char *(ruleinfodetail_text[])={"Text","Link","CVE","OSVDB","BUGTRACKID"};
                   print_out("\n**Phase 3: Completed filtering (rules).");
                   print_out("       Rule id: '%d'", currently_rule->sigid);
                   print_out("       Level: '%d'", currently_rule->level);
                   print_out("       Description: '%s'",currently_rule->comment);
+                  for (last_info_detail = currently_rule->info_details; last_info_detail != NULL; last_info_detail = last_info_detail->next)
+                  {
+                      print_out("       Info - %s: '%s'", ruleinfodetail_text[last_info_detail->type], last_info_detail->data);
+                  }
+                }
                 #endif
-                                            
+
 
 
                 /* Ignore level 0 */
@@ -428,7 +585,7 @@ void OS_ReadMSG(int m_queue)
                 }
 
 
-                /* Checking ignore time */ 
+                /* Checking ignore time */
                 if(currently_rule->ignore_time)
                 {
                     if(currently_rule->time_ignored == 0)
@@ -439,7 +596,7 @@ void OS_ReadMSG(int m_queue)
                      * is less than the time it should be ignored,
                      * leave (do not alert again).
                      */
-                    else if((lf->time - currently_rule->time_ignored) 
+                    else if((lf->time - currently_rule->time_ignored)
                             < currently_rule->ignore_time)
                     {
                         break;
@@ -453,7 +610,7 @@ void OS_ReadMSG(int m_queue)
                 /* Pointer to the rule that generated it */
                 lf->generated_rule = currently_rule;
 
-                
+
                 /* Checking if we should ignore it */
                 if(currently_rule->ckignore && IGnore(lf))
                 {
@@ -461,7 +618,7 @@ void OS_ReadMSG(int m_queue)
                     lf->generated_rule = NULL;
                     break;
                 }
-                
+
                 /* Checking if we need to add to ignore list */
                 if(currently_rule->ignore)
                 {
@@ -472,7 +629,15 @@ void OS_ReadMSG(int m_queue)
                 /* Log the alert if configured to ... */
                 if(currently_rule->alert_opts & DO_LOGALERT)
                 {
-                    print_out("**Alert to be generated.\n\n");
+                    if(alert_only)
+                    {
+                        OS_LogOutput(lf);
+                        __crt_ftell++;
+                    }
+                    else
+                    {
+                        print_out("**Alert to be generated.\n\n");
+                    }
                 }
 
 
@@ -485,19 +650,19 @@ void OS_ReadMSG(int m_queue)
                     }
                     else
                     {
-                        lf->sid_node_to_delete = 
+                        lf->sid_node_to_delete =
                             currently_rule->sid_prev_matched->last_node;
                     }
                 }
                 /* Group list */
                 else if(currently_rule->group_prev_matched)
                 {
-                    i = 0;  
-                    
+                    i = 0;
+
                     while(i < currently_rule->group_prev_matched_sz)
                     {
                         if(!OSList_AddData(
-                                currently_rule->group_prev_matched[i], 
+                                currently_rule->group_prev_matched[i],
                                 lf))
                         {
                            merror("%s: Unable to add data to grp list.",ARGV0);
@@ -505,16 +670,39 @@ void OS_ReadMSG(int m_queue)
                         i++;
                     }
                 }
-                
+
                 OS_AddEvent(lf);
 
                 break;
 
             }while((rulenode_pt = rulenode_pt->next) != NULL);
 
+            if(ut_str)
+            {
+                /*setup exit code if we are doing unit testing*/
+                char holder[1024];
+                holder[1] = '\0';
+                exit_code = 3;
+                if(lf->decoder_info->name != NULL && strcasecmp(ut_decoder_name, lf->decoder_info->name) == 0)
+                {
+                    exit_code--;
+                    snprintf(holder, 1023, "%d", currently_rule->sigid);
+                    if(strcasecmp(ut_rulelevel, holder) == 0)
+                    {
+                        exit_code--;
+                        snprintf(holder, 1023, "%d", currently_rule->level);
+                        if(strcasecmp(ut_alertlevel, holder) == 0)
+                        {
+                            exit_code--;
+                            printf("%d\n",exit_code);
+                        }
+                    }
+                }
+            }
+
 
             /* Only clear the memory if the eventinfo was not
-             * added to the stateful memory 
+             * added to the stateful memory
              * -- message is free inside clean event --
              */
             if(lf->generated_rule == NULL)
@@ -523,12 +711,14 @@ void OS_ReadMSG(int m_queue)
         }
         else
         {
-            exit(0);   
+            exit(exit_code);
         }
     }
+    exit(exit_code);
     return;
 }
 
 
 
 /* EOF */
+