Merge commit 'v2.5.1'
[ossec-hids.git] / src / analysisd / rules.c
index 8c97197..8ce5f8d 100755 (executable)
@@ -1,11 +1,11 @@
-/* @(#) $Id: rules.c,v 1.79 2009/11/04 18:45:37 dcid Exp $ */
+/* @(#) $Id$ */
 
 /* 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 
@@ -57,8 +57,6 @@ void Rules_OP_CreateRules()
     return;
 }
 
-
-
 /* Rules_OP_ReadRules, v0.3, 2005/03/21
  * Read the log rules.
  * v0.3: Fixed many memory problems.
@@ -99,7 +97,18 @@ int Rules_OP_ReadRules(char * rulefile)
     char *xml_status = "status";
     char *xml_action = "action";
     char *xml_compiled = "compiled_rule";
-    
+
+    char *xml_list = "list";
+    char *xml_list_lookup = "lookup";
+    char *xml_list_field = "field";
+    char *xml_list_cvalue = "check_value";
+    char *xml_match_key = "match_key";
+    char *xml_not_match_key = "not_match_key";
+    char *xml_match_key_value = "match_key_value";
+    char *xml_address_key = "address_match_key";
+    char *xml_not_address_key = "not_address_match_key";
+    char *xml_address_key_value = "address_match_key_value";         
+
     char *xml_if_sid = "if_sid";
     char *xml_if_group = "if_group";
     char *xml_if_level = "if_level";
@@ -115,6 +124,7 @@ int Rules_OP_ReadRules(char * rulefile)
     char *xml_same_user = "same_user";
     char *xml_same_location = "same_location";
     char *xml_same_id = "same_id";
+    char *xml_dodiff = "check_diff";
 
     char *xml_different_url = "different_url";
     
@@ -131,15 +141,25 @@ int Rules_OP_ReadRules(char * rulefile)
     int default_timeframe = 360;
 
 
-    /* Building the rule file name + path */
-    i = strlen(RULEPATH) + strlen(rulefile) + 2;
-    rulepath = (char *)calloc(i,sizeof(char));
-    if(!rulepath)
+    /* If no directory in the rulefile add the default */
+    if((strchr(rulefile, '/')) == NULL)
     {
-        ErrorExit(MEM_ERROR,ARGV0);
+        /* Building the rule file name + path */
+        i = strlen(RULEPATH) + strlen(rulefile) + 2;
+        rulepath = (char *)calloc(i,sizeof(char));
+        if(!rulepath)
+        {
+            ErrorExit(MEM_ERROR,ARGV0);
+        }
+        snprintf(rulepath,i,"%s/%s",RULEPATH,rulefile);
+    }
+    else
+    {
+        os_strdup(rulefile, rulepath);
+        debug1("%s is the rulefile", rulefile);
+        debug1("Not modifing the rule path");
     }
     
-    snprintf(rulepath,i,"%s/%s",RULEPATH,rulefile);
     
     i = 0;    
     
@@ -338,6 +358,9 @@ int Rules_OP_ReadRules(char * rulefile)
             /* Rule elements block */
             {
                 int k = 0;
+                int info_type = 0;
+                int count_info_detail = 0;
+                RuleInfoDetail *last_info_detail = NULL;
                 char *regex = NULL;
                 char *match = NULL;
                 char *url = NULL;
@@ -393,8 +416,61 @@ int Rules_OP_ReadRules(char * rulefile)
                             return(-1); 
                         }
                     }
+                    else if(strcasecmp(rule_opt[k]->element,xml_cve)==0)
+                    {
+                        if(config_ruleinfo->info_details == NULL)
+                        {
+                            config_ruleinfo->info_details = zeroinfodetails(RULEINFODETAIL_CVE,
+                                    rule_opt[k]->content);
+                        }
+                        else
+                        {
+                            for (last_info_detail = config_ruleinfo->info_details;
+                                    last_info_detail->next != NULL; 
+                                    last_info_detail = last_info_detail->next)
+                            {
+                                count_info_detail++;
+                            }
+                            /* Silently Drop info messages if their are more then MAX_RULEINFODETAIL */
+                            if (count_info_detail <= MAX_RULEINFODETAIL)
+                            {
+                                last_info_detail->next = zeroinfodetails(RULEINFODETAIL_CVE,
+                                        rule_opt[k]->content);
+                            }
+                        }
+
+                        /* keep old methods for now */
+                        config_ruleinfo->cve=
+                            loadmemory(config_ruleinfo->cve,
+                                    rule_opt[k]->content);
+                    }
                     else if(strcasecmp(rule_opt[k]->element,xml_info)==0)
                     {
+
+                        info_type = get_info_attributes(rule_opt[k]->attributes,
+                                                        rule_opt[k]->values);
+                        debug1("info_type = %d", info_type);
+
+                        if(config_ruleinfo->info_details == NULL)
+                        {
+                            config_ruleinfo->info_details = zeroinfodetails(info_type, 
+                                    rule_opt[k]->content);
+                        }
+                        else
+                        {
+                            for (last_info_detail = config_ruleinfo->info_details;
+                                    last_info_detail->next != NULL; 
+                                    last_info_detail = last_info_detail->next) {
+                                count_info_detail++;
+                            }
+                            /* Silently Drop info messages if their are more then MAX_RULEINFODETAIL */
+                            if (count_info_detail <= MAX_RULEINFODETAIL) {
+                                last_info_detail->next = zeroinfodetails(info_type, rule_opt[k]->content);
+                            }
+                        }
+
+
+                        /* keep old methods for now */
                         config_ruleinfo->info=
                             loadmemory(config_ruleinfo->info,
                                     rule_opt[k]->content);
@@ -435,12 +511,6 @@ int Rules_OP_ReadRules(char * rulefile)
                             loadmemory(config_ruleinfo->group,
                                     rule_opt[k]->content);
                     }
-                    else if(strcasecmp(rule_opt[k]->element,xml_cve)==0)
-                    {
-                        config_ruleinfo->cve=
-                            loadmemory(config_ruleinfo->cve,
-                                    rule_opt[k]->content);
-                    }
                     else if(strcasecmp(rule_opt[k]->element,xml_comment)==0)
                     {
                         char *newline;
@@ -593,6 +663,132 @@ int Rules_OP_ReadRules(char * rulefile)
                             loadmemory(config_ruleinfo->action,
                                     rule_opt[k]->content);
                     }
+                    else if(strcasecmp(rule_opt[k]->element,xml_list)==0)
+                    {
+                        debug1("-> %s == %s",rule_opt[k]->element, xml_list);
+                        if (rule_opt[k]->attributes && rule_opt[k]->values && rule_opt[k]->content)
+                        {
+                            int list_att_num=0;
+                            int rule_type=0;
+                            OSMatch *matcher=NULL;
+                            int lookup_type = LR_STRING_MATCH;
+                            while(rule_opt[k]->attributes[list_att_num])
+                            {
+                                if(strcasecmp(rule_opt[k]->attributes[list_att_num], xml_list_lookup) == 0)
+                                {
+                                    if(strcasecmp(rule_opt[k]->values[list_att_num],xml_match_key) == 0)
+                                        lookup_type = LR_STRING_MATCH;
+                                    else if(strcasecmp(rule_opt[k]->values[list_att_num],xml_not_match_key)==0)
+                                        lookup_type = LR_STRING_NOT_MATCH;
+                                    else if(strcasecmp(rule_opt[k]->values[list_att_num],xml_match_key_value)==0)
+                                        lookup_type = LR_STRING_MATCH_VALUE;
+                                    else if(strcasecmp(rule_opt[k]->values[list_att_num],xml_address_key)==0)
+                                        lookup_type = LR_ADDRESS_MATCH;
+                                    else if(strcasecmp(rule_opt[k]->values[list_att_num],xml_not_address_key)==0)
+                                        lookup_type = LR_ADDRESS_NOT_MATCH;
+                                    else if(strcasecmp(rule_opt[k]->values[list_att_num],xml_address_key_value)==0)
+                                        lookup_type = LR_ADDRESS_MATCH_VALUE;
+                                    else 
+                                    {
+                                        merror(INVALID_CONFIG, ARGV0, 
+                                               rule_opt[k]->element, 
+                                               rule_opt[k]->content);
+                                        merror("%s: List match lookup=\"%s\" is not valid.", 
+                                                ARGV0,rule_opt[k]->values[list_att_num]);
+                                        return(-1);
+                                     }
+                                }
+                                else if(strcasecmp(rule_opt[k]->attributes[list_att_num], xml_list_field)==0)
+                                {
+                                    if(strcasecmp(rule_opt[k]->values[list_att_num],xml_srcip)==0)
+                                        rule_type = RULE_SRCIP;
+                                    else if (strcasecmp(rule_opt[k]->values[list_att_num],xml_srcport)==0)
+                                        rule_type = RULE_SRCPORT;
+                                    else if (strcasecmp(rule_opt[k]->values[list_att_num],xml_dstip)==0)
+                                        rule_type = RULE_DSTIP;
+                                    else if (strcasecmp(rule_opt[k]->values[list_att_num],xml_dstport)==0)
+                                        rule_type = RULE_DSTPORT;
+                                    else if (strcasecmp(rule_opt[k]->values[list_att_num],xml_user)==0)
+                                        rule_type = RULE_USER;
+                                    else if (strcasecmp(rule_opt[k]->values[list_att_num],xml_url)==0)
+                                        rule_type = RULE_URL;
+                                    else if (strcasecmp(rule_opt[k]->values[list_att_num],xml_id)==0)
+                                        rule_type = RULE_ID;
+                                    else if (strcasecmp(rule_opt[k]->values[list_att_num],xml_hostname)==0)
+                                        rule_type = RULE_HOSTNAME;
+                                    else if (strcasecmp(rule_opt[k]->values[list_att_num],xml_program_name)==0)
+                                        rule_type = RULE_PROGRAM_NAME;
+                                    else if (strcasecmp(rule_opt[k]->values[list_att_num],xml_status)==0)
+                                        rule_type = RULE_STATUS;
+                                    else if (strcasecmp(rule_opt[k]->values[list_att_num],xml_action)==0)
+                                        rule_type = RULE_ACTION;
+                                    else 
+                                    {
+                                        merror(INVALID_CONFIG, ARGV0, 
+                                               rule_opt[k]->element, 
+                                               rule_opt[k]->content);
+                                        merror("%s: List match field=\"%s\" is not valid.", 
+                                                ARGV0,rule_opt[k]->values[list_att_num]);
+                                        return(-1);
+                                     }
+                                }
+                                else if(strcasecmp(rule_opt[k]->attributes[list_att_num], xml_list_cvalue)==0)
+                                {
+                                    os_calloc(1, sizeof(OSMatch), matcher);
+                                    if(!OSMatch_Compile(rule_opt[k]->values[list_att_num], matcher, 0))
+                                    {
+                                        merror(INVALID_CONFIG, ARGV0, 
+                                               rule_opt[k]->element, 
+                                               rule_opt[k]->content);
+                                        merror(REGEX_COMPILE, 
+                                               ARGV0, 
+                                               rule_opt[k]->values[list_att_num], 
+                                               matcher->error);
+                                        return(-1);
+                                    }
+                                }
+                                else
+                                {
+                                       merror("%s:List feild=\"%s\" is not valid",ARGV0,
+                                           rule_opt[k]->values[list_att_num]);
+                                    merror(INVALID_CONFIG, ARGV0, 
+                                           rule_opt[k]->element, rule_opt[k]->content);
+                                    return(-1);
+                                }
+                                list_att_num++;
+                            }
+                            if(rule_type == 0)
+                            {
+                                merror("%s:List requires the field=\"\" Attrubute",ARGV0);
+                                merror(INVALID_CONFIG, ARGV0, 
+                                       rule_opt[k]->element, rule_opt[k]->content);
+                                return(-1);
+                            }
+
+                            /* Wow it's all ready - this seams too complex to get to this point */
+                            config_ruleinfo->lists = OS_AddListRule(config_ruleinfo->lists,
+                                           lookup_type, 
+                                           rule_type, 
+                                           rule_opt[k]->content,
+                                           matcher);
+                            if (config_ruleinfo->lists == NULL)
+                            {
+                                merror("%s: List error: Could not load %s", ARGV0, rule_opt[k]->content);
+                                return(-1);
+                            }
+                        }
+                        else
+                        {
+                            merror("%s:List must have a correctly formatted feild attribute",
+                                   ARGV0);
+                            merror(INVALID_CONFIG, 
+                                   ARGV0, 
+                                   rule_opt[k]->element, 
+                                   rule_opt[k]->content);
+                            return(-1);
+                        }                        
+                        /* xml_list eval is done */
+                    }
                     else if(strcasecmp(rule_opt[k]->element,xml_url)==0)
                     {
                         url=
@@ -735,6 +931,14 @@ int Rules_OP_ReadRules(char * rulefile)
                             config_ruleinfo->alert_opts |= SAME_EXTRAINFO;
                     }
                     else if(strcasecmp(rule_opt[k]->element,
+                               xml_dodiff)==0)
+                    {
+                        config_ruleinfo->context++;
+                        config_ruleinfo->context_opts|= SAME_DODIFF;
+                        if(!(config_ruleinfo->alert_opts & DO_EXTRAINFO))
+                            config_ruleinfo->alert_opts |= DO_EXTRAINFO;
+                    }
+                    else if(strcasecmp(rule_opt[k]->element,
                                 xml_same_dst_port) == 0)
                     {
                         config_ruleinfo->context_opts|= SAME_DSTPORT;
@@ -827,6 +1031,13 @@ int Rules_OP_ReadRules(char * rulefile)
                               config_ruleinfo->alert_opts &=0xfff-DO_LOGALERT;
                             }
                         }
+                        else if(strcmp("no_ar", rule_opt[k]->content) == 0)
+                        {
+                            if(!(config_ruleinfo->alert_opts & NO_AR))
+                            {
+                                config_ruleinfo->alert_opts|= NO_AR;
+                            }
+                        }
                         else
                         {               
                             merror(XML_VALUEERR, ARGV0, xml_options,
@@ -1238,8 +1449,16 @@ int Rules_OP_ReadRules(char * rulefile)
             }
             else if(config_ruleinfo->context)
             {
-                config_ruleinfo->event_search = 
+                if((config_ruleinfo->context == 1) && 
+                   (config_ruleinfo->context_opts & SAME_DODIFF))
+                {
+                    config_ruleinfo->context = 0;
+                }
+                else
+                {
+                    config_ruleinfo->event_search = 
                                  (void *)Search_LastEvents;
+                }
             }
 
         } /* while(rule[j]) */
@@ -1335,6 +1554,29 @@ char *loadmemory(char *at, char *str)
 }
 
 
+RuleInfoDetail *zeroinfodetails(int type, char *data)
+{
+    RuleInfoDetail *info_details_pt = NULL;
+
+    info_details_pt = (RuleInfoDetail *)calloc(1,sizeof(RuleInfoDetail));
+
+    if (info_details_pt == NULL)
+    {
+        ErrorExit(MEM_ERROR,ARGV0);
+    }
+    /* type */
+    info_details_pt->type = type;
+
+    /* data */
+    os_strdup(data, info_details_pt->data);
+
+    info_details_pt->next = NULL;
+    
+
+    return(info_details_pt);
+}
+
+
 RuleInfo *zerorulemember(int id, int level, 
                          int maxsize, int frequency,
                          int timeframe, int noalert, 
@@ -1403,6 +1645,7 @@ RuleInfo *zerorulemember(int id, int level,
     ruleinfo_pt->comment = NULL;
     ruleinfo_pt->info = NULL;
     ruleinfo_pt->cve = NULL;
+    ruleinfo_pt->info_details = NULL;
     
     ruleinfo_pt->if_sid = NULL;
     ruleinfo_pt->if_group = NULL;
@@ -1437,10 +1680,48 @@ RuleInfo *zerorulemember(int id, int level,
     
     ruleinfo_pt->event_search = NULL;
     ruleinfo_pt->compiled_rule = NULL;
+    ruleinfo_pt->lists = NULL;
 
     return(ruleinfo_pt);
 }
 
+int get_info_attributes(char **attributes, char **values)
+{
+    char *xml_type = "type";
+    int k=0;
+    if(!attributes)
+        return(RULEINFODETAIL_TEXT);
+
+    while(attributes[k])
+    {
+        if (!values[k])
+        {
+            merror("rules_op: Entry info type \"%s\" does not have a value", 
+                    attributes[k]);
+            return (-1);
+        }
+        else if(strcasecmp(attributes[k],xml_type) == 0)
+        {
+            if(strcmp(values[k], "text") == 0)
+            {
+                return(RULEINFODETAIL_TEXT);
+            } 
+            else if(strcmp(values[k], "link") == 0)
+            {
+                return(RULEINFODETAIL_LINK);
+            }
+            else if(strcmp(values[k], "cve") == 0)
+            {
+                return(RULEINFODETAIL_CVE);
+            }
+            else if(strcmp(values[k], "osvdb") == 0)
+            {
+                return(RULEINFODETAIL_OSVDB);
+            }
+        }
+    }
+    return(RULEINFODETAIL_TEXT);
+}
 
 /* Get the attributes */
 int getattributes(char **attributes, char **values,
@@ -1641,6 +1922,12 @@ void Rule_AddAR(RuleInfo *rule_config)
         return;
     }
 
+    /* No AR when options no_ar is set */
+    if(rule_config->alert_opts & NO_AR)
+    {
+        return;
+    }
+
     if(!active_responses)
     {
         return;