new upstream release (3.3.0); modify package compatibility for Stretch
[ossec-hids.git] / src / shared / rules_op.c
1 /* Copyright (C) 2009 Trend Micro Inc.
2  * All rights reserved.
3  *
4  * This program is a free software; you can redistribute it
5  * and/or modify it under the terms of the GNU General Public
6  * License (version 2) as published by the FSF - Free Software
7  * Foundation.
8  */
9
10 #include "rules_op.h"
11
12 /* Change path for test rule */
13 #ifdef TESTRULE
14 #undef RULEPATH
15 #define RULEPATH "rules/"
16 #endif
17
18 /* Prototypes */
19 static int _OS_GetRulesAttributes(char **attributes,
20                                   char **values,
21                                   RuleInfo *ruleinfo_pt) __attribute__((nonnull));
22 static RuleInfo *_OS_AllocateRule(void);
23
24
25 /* Read the log rules */
26 int OS_ReadXMLRules(const char *rulefile,
27                     void *(*ruleact_function)(RuleInfo *rule_1, void *data_1),
28                     void *data)
29 {
30     OS_XML xml;
31     XML_NODE node = NULL;
32
33     /** XML variables **/
34     /* These are the available options for the rule configuration */
35
36     const char *xml_group = "group";
37     const char *xml_rule = "rule";
38
39     const char *xml_regex = "regex";
40     const char *xml_match = "match";
41     const char *xml_decoded = "decoded_as";
42     const char *xml_category = "category";
43     const char *xml_cve = "cve";
44     const char *xml_info = "info";
45     const char *xml_day_time = "time";
46     const char *xml_week_day = "weekday";
47     const char *xml_comment = "description";
48     const char *xml_ignore = "ignore";
49     const char *xml_check_if_ignored = "check_if_ignored";
50
51     const char *xml_srcip = "srcip";
52     const char *xml_srcport = "srcport";
53     const char *xml_dstip = "dstip";
54     const char *xml_dstport = "dstport";
55     const char *xml_user = "user";
56     const char *xml_url = "url";
57     const char *xml_id = "id";
58     const char *xml_data = "extra_data";
59     const char *xml_hostname = "hostname";
60     const char *xml_program_name = "program_name";
61     const char *xml_status = "status";
62     const char *xml_action = "action";
63     const char *xml_compiled = "compiled_rule";
64
65     const char *xml_if_sid = "if_sid";
66     const char *xml_if_group = "if_group";
67     const char *xml_if_level = "if_level";
68     const char *xml_fts = "if_fts";
69
70     const char *xml_if_matched_regex = "if_matched_regex";
71     const char *xml_if_matched_group = "if_matched_group";
72     const char *xml_if_matched_sid = "if_matched_sid";
73
74     const char *xml_same_source_ip = "same_source_ip";
75     const char *xml_same_src_port = "same_src_port";
76     const char *xml_same_dst_port = "same_dst_port";
77     const char *xml_same_user = "same_user";
78     const char *xml_same_location = "same_location";
79     const char *xml_same_id = "same_id";
80     const char *xml_dodiff = "check_diff";
81
82     const char *xml_different_url = "different_url";
83
84     const char *xml_notsame_source_ip = "not_same_source_ip";
85     const char *xml_notsame_user = "not_same_user";
86     const char *xml_notsame_agent = "not_same_agent";
87     const char *xml_notsame_id = "not_same_id";
88
89     const char *xml_options = "options";
90
91     char *rulepath;
92
93     size_t i;
94
95     /* If no directory in the rulefile, add the default */
96     if ((strchr(rulefile, '/')) == NULL) {
97         /* Build the rule file name + path */
98         i = strlen(RULEPATH) + strlen(rulefile) + 2;
99         rulepath = (char *)calloc(i, sizeof(char));
100         if (!rulepath) {
101             ErrorExit(MEM_ERROR, __local_name, errno, strerror(errno));
102         }
103         snprintf(rulepath, i, "%s/%s", RULEPATH, rulefile);
104     } else {
105         os_strdup(rulefile, rulepath);
106         debug1("%s is the rulefile", rulefile);
107         debug1("Not modifing the rule path");
108     }
109
110     /* Read the XML */
111     if (OS_ReadXML(rulepath, &xml) < 0) {
112         merror(XML_ERROR, __local_name, rulepath, xml.err, xml.err_line);
113         free(rulepath);
114         return (-1);
115     }
116     debug1("%s: DEBUG: read xml for rule '%s'.", __local_name, rulepath);
117
118     /* Apply any variables found */
119     if (OS_ApplyVariables(&xml) != 0) {
120         merror(XML_ERROR_VAR, __local_name, rulepath, xml.err);
121         if (rulepath) {
122             free(rulepath);
123         }
124         return (-1);
125     }
126     debug1("%s: DEBUG: XML Variables applied.", __local_name);
127
128     /* Get the root elements */
129     node = OS_GetElementsbyNode(&xml, NULL);
130     if (!node) {
131         merror(CONFIG_ERROR, __local_name, rulepath);
132         OS_ClearXML(&xml);
133         if (rulepath) {
134             free(rulepath);
135         }
136         return (-1);
137     }
138
139     /* Zero the rule memory -- not used anymore */
140     free(rulepath);
141
142     /* Check if there is any invalid global option */
143     i = 0;
144     while (node[i]) {
145         if (node[i]->element) {
146             /* Verify group */
147             if (strcasecmp(node[i]->element, xml_group) != 0) {
148                 merror(RL_INV_ROOT, __local_name, node[i]->element);
149                 OS_ClearXML(&xml);
150                 return (-1);
151             }
152             /* Check group attribute -- only name is allowed */
153             if ((!node[i]->attributes) || (!node[i]->values) ||
154                     (!node[i]->values[0]) || (!node[i]->attributes[0]) ||
155                     (strcasecmp(node[i]->attributes[0], "name") != 0) ||
156                     (node[i]->attributes[1])) {
157                 merror(RL_INV_ROOT, __local_name, node[i]->element);
158                 OS_ClearXML(&xml);
159                 return (-1);
160             }
161         } else {
162             merror(XML_READ_ERROR, __local_name);
163             OS_ClearXML(&xml);
164             return (-1);
165         }
166         i++;
167     }
168
169     /* Get the rules */
170     i = 0;
171     while (node[i]) {
172         int j = 0;
173         XML_NODE rule = NULL;
174
175         /* Get all rules for a global group */
176         rule = OS_GetElementsbyNode(&xml, node[i]);
177         if (rule == NULL) {
178             i++;
179             continue;
180         }
181
182         /* Loop over the rules node */
183         while (rule[j]) {
184             /* Rules options */
185             int k = 0;
186             char *regex = NULL, *match = NULL, *url = NULL,
187                   *if_matched_regex = NULL, *if_matched_group = NULL,
188                    *user = NULL, *id = NULL, *srcport = NULL,
189                     *dstport = NULL, *status = NULL, *hostname = NULL,
190                      *extra_data = NULL, *program_name = NULL;
191
192             RuleInfo *config_ruleinfo = NULL;
193             XML_NODE rule_opt = NULL;
194
195             /* Check if the rule element is correct */
196             if ((!rule[j]->element) ||
197                     (strcasecmp(rule[j]->element, xml_rule) != 0)) {
198                 merror(RL_INV_RULE, __local_name, node[i]->element);
199                 OS_ClearXML(&xml);
200                 return (-1);
201             }
202
203             /* Check for the attributes of the rule */
204             if ((!rule[j]->attributes) || (!rule[j]->values)) {
205                 merror(RL_INV_RULE, __local_name, rulefile);
206                 OS_ClearXML(&xml);
207                 return (-1);
208             }
209
210             /* Attribute block */
211             config_ruleinfo = _OS_AllocateRule();
212
213             if (_OS_GetRulesAttributes(rule[j]->attributes, rule[j]->values,
214                                        config_ruleinfo) < 0) {
215                 merror(RL_INV_ATTR, __local_name, rulefile);
216                 OS_ClearXML(&xml);
217                 return (-1);
218             }
219
220             /* We must have an id or level */
221             if ((config_ruleinfo->sigid == -1) || (config_ruleinfo->level == -1)) {
222                 merror(RL_INV_ATTR, __local_name, rulefile);
223                 OS_ClearXML(&xml);
224                 return (-1);
225             }
226
227             /* Assign the group name to the rule. The level is correct so
228              * the rule is probably going to be fine.
229              */
230             os_strdup(node[i]->values[0], config_ruleinfo->group);
231
232             /* Get rules options */
233             rule_opt =  OS_GetElementsbyNode(&xml, rule[j]);
234             if (rule_opt == NULL) {
235                 merror(RL_NO_OPT, __local_name, config_ruleinfo->sigid);
236                 OS_ClearXML(&xml);
237                 return (-1);
238             }
239
240             /* Read the whole rule block */
241             while (rule_opt[k]) {
242                 if ((!rule_opt[k]->element) || (!rule_opt[k]->content)) {
243                     break;
244                 } else if (strcasecmp(rule_opt[k]->element, xml_regex) == 0) {
245                     regex =
246                         os_LoadString(regex,
247                                       rule_opt[k]->content);
248                 } else if (strcasecmp(rule_opt[k]->element, xml_match) == 0) {
249                     match =
250                         os_LoadString(match,
251                                       rule_opt[k]->content);
252                 } else if (strcasecmp(rule_opt[k]->element, xml_decoded) == 0) {
253                 } else if (strcasecmp(rule_opt[k]->element, xml_info) == 0) {
254                     config_ruleinfo->info =
255                         os_LoadString(config_ruleinfo->info,
256                                       rule_opt[k]->content);
257                 } else if (strcasecmp(rule_opt[k]->element, xml_day_time) == 0) {
258                     config_ruleinfo->day_time =
259                         OS_IsValidTime(rule_opt[k]->content);
260                     if (!config_ruleinfo->day_time) {
261                         merror(INVALID_CONFIG, __local_name,
262                                rule_opt[k]->element,
263                                rule_opt[k]->content);
264                         return (-1);
265                     }
266
267                     if (!(config_ruleinfo->alert_opts & DO_EXTRAINFO)) {
268                         config_ruleinfo->alert_opts |= DO_EXTRAINFO;
269                     }
270                 } else if (strcasecmp(rule_opt[k]->element, xml_week_day) == 0) {
271                     config_ruleinfo->week_day =
272                         OS_IsValidDay(rule_opt[k]->content);
273
274                     if (!config_ruleinfo->week_day) {
275                         merror(INVALID_CONFIG, __local_name,
276                                rule_opt[k]->element,
277                                rule_opt[k]->content);
278                         return (-1);
279                     }
280                     if (!(config_ruleinfo->alert_opts & DO_EXTRAINFO)) {
281                         config_ruleinfo->alert_opts |= DO_EXTRAINFO;
282                     }
283                 } else if (strcasecmp(rule_opt[k]->element, xml_group) == 0) {
284                     config_ruleinfo->group =
285                         os_LoadString(config_ruleinfo->group,
286                                       rule_opt[k]->content);
287                 } else if (strcasecmp(rule_opt[k]->element, xml_cve) == 0) {
288                     config_ruleinfo->cve =
289                         os_LoadString(config_ruleinfo->cve,
290                                       rule_opt[k]->content);
291                 } else if (strcasecmp(rule_opt[k]->element, xml_comment) == 0) {
292                     char *newline;
293
294                     newline = strchr(rule_opt[k]->content, '\n');
295                     if (newline) {
296                         *newline = ' ';
297                     }
298                     config_ruleinfo->comment =
299                         os_LoadString(config_ruleinfo->comment,
300                                       rule_opt[k]->content);
301                 } else if (strcasecmp(rule_opt[k]->element, xml_srcip) == 0) {
302                     size_t ip_s = 0;
303
304                     /* Get size of source IP list */
305                     while (config_ruleinfo->srcip &&
306                             config_ruleinfo->srcip[ip_s]) {
307                         ip_s++;
308                     }
309
310                     config_ruleinfo->srcip = (os_ip **)
311                                              realloc(config_ruleinfo->srcip,
312                                                      (ip_s + 2) * sizeof(os_ip *));
313
314                     /* Allocate memory for the individual entries */
315                     os_calloc(1, sizeof(os_ip),
316                               config_ruleinfo->srcip[ip_s]);
317                     config_ruleinfo->srcip[ip_s + 1] = NULL;
318
319                     /* Check if the IP is valid */
320                     if (!OS_IsValidIP(rule_opt[k]->content,
321                                       config_ruleinfo->srcip[ip_s])) {
322                         merror(INVALID_IP, __local_name, rule_opt[k]->content);
323                         return (-1);
324                     }
325
326                     if (!(config_ruleinfo->alert_opts & DO_PACKETINFO)) {
327                         config_ruleinfo->alert_opts |= DO_PACKETINFO;
328                     }
329                 } else if (strcasecmp(rule_opt[k]->element, xml_dstip) == 0) {
330                     size_t ip_s = 0;
331
332                     /* Get size of destination IP list */
333                     while (config_ruleinfo->dstip &&
334                             config_ruleinfo->dstip[ip_s]) {
335                         ip_s++;
336                     }
337
338                     config_ruleinfo->dstip = (os_ip **)
339                                              realloc(config_ruleinfo->dstip,
340                                                      (ip_s + 2) * sizeof(os_ip *));
341
342                     /* Allocate memory for the individual entries */
343                     os_calloc(1, sizeof(os_ip),
344                               config_ruleinfo->dstip[ip_s]);
345                     config_ruleinfo->dstip[ip_s + 1] = NULL;
346
347                     /* Checking if the IP is valid */
348                     if (!OS_IsValidIP(rule_opt[k]->content,
349                                       config_ruleinfo->dstip[ip_s])) {
350                         merror(INVALID_IP, __local_name, rule_opt[k]->content);
351                         return (-1);
352                     }
353
354                     if (!(config_ruleinfo->alert_opts & DO_PACKETINFO)) {
355                         config_ruleinfo->alert_opts |= DO_PACKETINFO;
356                     }
357                 } else if (strcasecmp(rule_opt[k]->element, xml_user) == 0) {
358                     user = os_LoadString(user, rule_opt[k]->content);
359
360                     if (!(config_ruleinfo->alert_opts & DO_EXTRAINFO)) {
361                         config_ruleinfo->alert_opts |= DO_EXTRAINFO;
362                     }
363                 } else if (strcasecmp(rule_opt[k]->element, xml_id) == 0) {
364                     id = os_LoadString(id, rule_opt[k]->content);
365                 } else if (strcasecmp(rule_opt[k]->element, xml_srcport) == 0) {
366                     srcport = os_LoadString(srcport, rule_opt[k]->content);
367
368                     if (!(config_ruleinfo->alert_opts & DO_PACKETINFO)) {
369                         config_ruleinfo->alert_opts |= DO_PACKETINFO;
370                     }
371                 } else if (strcasecmp(rule_opt[k]->element, xml_dstport) == 0) {
372                     dstport = os_LoadString(dstport, rule_opt[k]->content);
373
374                     if (!(config_ruleinfo->alert_opts & DO_PACKETINFO)) {
375                         config_ruleinfo->alert_opts |= DO_PACKETINFO;
376                     }
377                 } else if (strcasecmp(rule_opt[k]->element, xml_status) == 0) {
378                     status = os_LoadString(status, rule_opt[k]->content);
379
380                     if (!(config_ruleinfo->alert_opts & DO_EXTRAINFO)) {
381                         config_ruleinfo->alert_opts |= DO_EXTRAINFO;
382                     }
383                 } else if (strcasecmp(rule_opt[k]->element, xml_hostname) == 0) {
384                     hostname = os_LoadString(hostname, rule_opt[k]->content);
385
386                     if (!(config_ruleinfo->alert_opts & DO_EXTRAINFO)) {
387                         config_ruleinfo->alert_opts |= DO_EXTRAINFO;
388                     }
389                 } else if (strcasecmp(rule_opt[k]->element, xml_data) == 0) {
390                     extra_data = os_LoadString(extra_data, rule_opt[k]->content);
391
392                     if (!(config_ruleinfo->alert_opts & DO_EXTRAINFO)) {
393                         config_ruleinfo->alert_opts |= DO_EXTRAINFO;
394                     }
395                 } else if (strcasecmp(rule_opt[k]->element,
396                                       xml_program_name) == 0) {
397                     program_name = os_LoadString(program_name,
398                                                  rule_opt[k]->content);
399                 } else if (strcasecmp(rule_opt[k]->element, xml_action) == 0) {
400                     config_ruleinfo->action =
401                         os_LoadString(config_ruleinfo->action,
402                                       rule_opt[k]->content);
403                 } else if (strcasecmp(rule_opt[k]->element, xml_url) == 0) {
404                     url = os_LoadString(url, rule_opt[k]->content);
405                 }
406
407                 else if (strcasecmp(rule_opt[k]->element, xml_compiled) == 0) {
408                     /* Not using this in here */
409                 }
410
411                 /* We allow these categories so far */
412                 else if (strcasecmp(rule_opt[k]->element, xml_category) == 0) {
413                     if (strcmp(rule_opt[k]->content, "firewall") == 0) {
414                         config_ruleinfo->category = FIREWALL;
415                     } else if (strcmp(rule_opt[k]->content, "ids") == 0) {
416                         config_ruleinfo->category = IDS;
417                     } else if (strcmp(rule_opt[k]->content, "syslog") == 0) {
418                         config_ruleinfo->category = SYSLOG;
419                     } else if (strcmp(rule_opt[k]->content, "web-log") == 0) {
420                         config_ruleinfo->category = WEBLOG;
421                     } else if (strcmp(rule_opt[k]->content, "squid") == 0) {
422                         config_ruleinfo->category = SQUID;
423                     } else if (strcmp(rule_opt[k]->content, "windows") == 0) {
424                         config_ruleinfo->category = DECODER_WINDOWS;
425                     } else if (strcmp(rule_opt[k]->content, "ossec") == 0) {
426                         config_ruleinfo->category = OSSEC_RL;
427                     } else {
428                         merror(INVALID_CAT, __local_name, rule_opt[k]->content);
429                         return (-1);
430                     }
431                 } else if (strcasecmp(rule_opt[k]->element, xml_if_sid) == 0) {
432                     config_ruleinfo->if_sid =
433                         os_LoadString(config_ruleinfo->if_sid,
434                                       rule_opt[k]->content);
435                 } else if (strcasecmp(rule_opt[k]->element, xml_if_level) == 0) {
436                     if (!OS_StrIsNum(rule_opt[k]->content)) {
437                         merror(INVALID_CONFIG, __local_name,
438                                xml_if_level,
439                                rule_opt[k]->content);
440                         return (-1);
441                     }
442
443                     config_ruleinfo->if_level =
444                         os_LoadString(config_ruleinfo->if_level,
445                                       rule_opt[k]->content);
446                 } else if (strcasecmp(rule_opt[k]->element, xml_if_group) == 0) {
447                     config_ruleinfo->if_group =
448                         os_LoadString(config_ruleinfo->if_group,
449                                       rule_opt[k]->content);
450                 } else if (strcasecmp(rule_opt[k]->element,
451                                       xml_if_matched_regex) == 0) {
452                     config_ruleinfo->context = 1;
453                     if_matched_regex =
454                         os_LoadString(if_matched_regex,
455                                       rule_opt[k]->content);
456                 } else if (strcasecmp(rule_opt[k]->element,
457                                       xml_if_matched_group) == 0) {
458                     config_ruleinfo->context = 1;
459                     if_matched_group =
460                         os_LoadString(if_matched_group,
461                                       rule_opt[k]->content);
462                 } else if (strcasecmp(rule_opt[k]->element,
463                                       xml_if_matched_sid) == 0) {
464                     config_ruleinfo->context = 1;
465                     if (!OS_StrIsNum(rule_opt[k]->content)) {
466                         merror(INVALID_CONFIG, __local_name,
467                                rule_opt[k]->element,
468                                rule_opt[k]->content);
469                         return (-1);
470                     }
471                     config_ruleinfo->if_matched_sid =
472                         atoi(rule_opt[k]->content);
473
474                 } else if (strcasecmp(rule_opt[k]->element,
475                                       xml_same_source_ip) == 0) {
476                     config_ruleinfo->context_opts |= SAME_SRCIP;
477                 } else if (strcasecmp(rule_opt[k]->element,
478                                       xml_same_src_port) == 0) {
479                     config_ruleinfo->context_opts |= SAME_SRCPORT;
480
481                     if (!(config_ruleinfo->alert_opts & SAME_EXTRAINFO)) {
482                         config_ruleinfo->alert_opts |= SAME_EXTRAINFO;
483                     }
484                 } else if (strcasecmp(rule_opt[k]->element,
485                                       xml_dodiff) == 0) {
486                     config_ruleinfo->context++;
487                     config_ruleinfo->context_opts |= SAME_DODIFF;
488                     if (!(config_ruleinfo->alert_opts & DO_EXTRAINFO)) {
489                         config_ruleinfo->alert_opts |= DO_EXTRAINFO;
490                     }
491                 } else if (strcasecmp(rule_opt[k]->element,
492                                       xml_same_dst_port) == 0) {
493                     config_ruleinfo->context_opts |= SAME_DSTPORT;
494
495                     if (!(config_ruleinfo->alert_opts & SAME_EXTRAINFO)) {
496                         config_ruleinfo->alert_opts |= SAME_EXTRAINFO;
497                     }
498                 } else if (strcasecmp(rule_opt[k]->element,
499                                       xml_notsame_source_ip) == 0) {
500                     config_ruleinfo->context_opts &= NOT_SAME_SRCIP;
501                 } else if (strcmp(rule_opt[k]->element, xml_same_id) == 0) {
502                     config_ruleinfo->context_opts |= SAME_ID;
503                 } else if (strcmp(rule_opt[k]->element,
504                                   xml_different_url) == 0) {
505                     config_ruleinfo->context_opts |= DIFFERENT_URL;
506
507                     if (!(config_ruleinfo->alert_opts & SAME_EXTRAINFO)) {
508                         config_ruleinfo->alert_opts |= SAME_EXTRAINFO;
509                     }
510                 } else if (strcmp(rule_opt[k]->element, xml_notsame_id) == 0) {
511                     config_ruleinfo->context_opts &= NOT_SAME_ID;
512                 } else if (strcasecmp(rule_opt[k]->element,
513                                       xml_fts) == 0) {
514                     config_ruleinfo->alert_opts |= DO_FTS;
515                 } else if (strcasecmp(rule_opt[k]->element,
516                                       xml_same_user) == 0) {
517                     config_ruleinfo->context_opts |= SAME_USER;
518
519                     if (!(config_ruleinfo->alert_opts & SAME_EXTRAINFO)) {
520                         config_ruleinfo->alert_opts |= SAME_EXTRAINFO;
521                     }
522                 } else if (strcasecmp(rule_opt[k]->element,
523                                       xml_notsame_user) == 0) {
524                     config_ruleinfo->context_opts &= NOT_SAME_USER;
525                 } else if (strcasecmp(rule_opt[k]->element,
526                                       xml_same_location) == 0) {
527                     config_ruleinfo->context_opts |= SAME_LOCATION;
528                     if (!(config_ruleinfo->alert_opts & SAME_EXTRAINFO)) {
529                         config_ruleinfo->alert_opts |= SAME_EXTRAINFO;
530                     }
531                 } else if (strcasecmp(rule_opt[k]->element,
532                                       xml_notsame_agent) == 0) {
533                     config_ruleinfo->context_opts &= NOT_SAME_AGENT;
534                 } else if (strcasecmp(rule_opt[k]->element,
535                                       xml_options) == 0) {
536                     if (strcmp("alert_by_email",
537                                rule_opt[k]->content) == 0) {
538                         if (!(config_ruleinfo->alert_opts & DO_MAILALERT)) {
539                             config_ruleinfo->alert_opts |= DO_MAILALERT;
540                         }
541                     } else if (strcmp("no_email_alert",
542                                       rule_opt[k]->content) == 0) {
543                         if (config_ruleinfo->alert_opts & DO_MAILALERT) {
544                             config_ruleinfo->alert_opts &= 0xfff - DO_MAILALERT;
545                         }
546                     } else if (strcmp("log_alert",
547                                       rule_opt[k]->content) == 0) {
548                         if (!(config_ruleinfo->alert_opts & DO_LOGALERT)) {
549                             config_ruleinfo->alert_opts |= DO_LOGALERT;
550                         }
551                     } else if (strcmp("no_log", rule_opt[k]->content) == 0) {
552                         if (config_ruleinfo->alert_opts & DO_LOGALERT) {
553                             config_ruleinfo->alert_opts &= 0xfff - DO_LOGALERT;
554                         }
555                     } else if (strcmp("no_ar", rule_opt[k]->content) == 0) {
556                         if (!(config_ruleinfo->alert_opts & NO_AR)) {
557                             config_ruleinfo->alert_opts |= NO_AR;
558                         }
559                     } else {
560                         merror(XML_VALUEERR, __local_name, xml_options,
561                                rule_opt[k]->content);
562
563                         merror(INVALID_ELEMENT, __local_name,
564                                rule_opt[k]->element,
565                                rule_opt[k]->content);
566                         OS_ClearXML(&xml);
567                         return (-1);
568                     }
569                 } else if (strcasecmp(rule_opt[k]->element,
570                                       xml_ignore) == 0) {
571                     if (strstr(rule_opt[k]->content, "user") != NULL) {
572                         config_ruleinfo->ignore |= FTS_USER;
573                     }
574                     if (strstr(rule_opt[k]->content, "srcip") != NULL) {
575                         config_ruleinfo->ignore |= FTS_SRCIP;
576                     }
577                     if (strstr(rule_opt[k]->content, "dstip") != NULL) {
578                         config_ruleinfo->ignore |= FTS_DSTIP;
579                     }
580                     if (strstr(rule_opt[k]->content, "id") != NULL) {
581                         config_ruleinfo->ignore |= FTS_ID;
582                     }
583                     if (strstr(rule_opt[k]->content, "location") != NULL) {
584                         config_ruleinfo->ignore |= FTS_LOCATION;
585                     }
586                     if (strstr(rule_opt[k]->content, "data") != NULL) {
587                         config_ruleinfo->ignore |= FTS_DATA;
588                     }
589                     if (strstr(rule_opt[k]->content, "name") != NULL) {
590                         config_ruleinfo->ignore |= FTS_NAME;
591
592                     }
593                     if (!config_ruleinfo->ignore) {
594                         merror(INVALID_ELEMENT, __local_name,
595                                rule_opt[k]->element,
596                                rule_opt[k]->content);
597
598                         return (-1);
599                     }
600                 } else if (strcasecmp(rule_opt[k]->element,
601                                       xml_check_if_ignored) == 0) {
602                     if (strstr(rule_opt[k]->content, "user") != NULL) {
603                         config_ruleinfo->ckignore |= FTS_USER;
604                     }
605                     if (strstr(rule_opt[k]->content, "srcip") != NULL) {
606                         config_ruleinfo->ckignore |= FTS_SRCIP;
607                     }
608                     if (strstr(rule_opt[k]->content, "dstip") != NULL) {
609                         config_ruleinfo->ckignore |= FTS_DSTIP;
610                     }
611                     if (strstr(rule_opt[k]->content, "id") != NULL) {
612                         config_ruleinfo->ckignore |= FTS_ID;
613                     }
614                     if (strstr(rule_opt[k]->content, "location") != NULL) {
615                         config_ruleinfo->ckignore |= FTS_LOCATION;
616                     }
617                     if (strstr(rule_opt[k]->content, "data") != NULL) {
618                         config_ruleinfo->ckignore |= FTS_DATA;
619                     }
620                     if (strstr(rule_opt[k]->content, "name") != NULL) {
621                         config_ruleinfo->ckignore |= FTS_NAME;
622
623                     }
624                     if (!config_ruleinfo->ckignore) {
625                         merror(INVALID_ELEMENT, __local_name,
626                                rule_opt[k]->element,
627                                rule_opt[k]->content);
628
629                         return (-1);
630                     }
631                 }
632                 /* XXX As new features are added into ../analysisd/rules.c
633                  * This code needs to be updated to match, but is out of date
634                  * it's become a nightmare to correct with out just make the
635                  * problem for someone later.
636                  *
637                  * This hack will allow any crap xml to pass without an
638                  * error.  The correct fix is to refactor the code so that
639                  * ../analysisd/rules* and this code are not duplicates
640                  *
641                 else
642                 {
643                     merror(XML_INVELEM, __local_name, rule_opt[k]->element);
644                     OS_ClearXML(&xml);
645                     return(-1);
646                 }
647                 */
648
649                 k++;
650             }
651
652             /* Check for a valid use of frequency */
653             if ((config_ruleinfo->context_opts ||
654                     config_ruleinfo->frequency) &&
655                     !config_ruleinfo->context) {
656                 merror("%s: Invalid use of frequency/context options. "
657                        "Missing if_matched on rule '%d'.",
658                        __local_name, config_ruleinfo->sigid);
659                 OS_ClearXML(&xml);
660                 return (-1);
661             }
662
663             /* If if_matched_group we must have a if_sid or if_group */
664             if (if_matched_group) {
665                 if (!config_ruleinfo->if_sid && !config_ruleinfo->if_group) {
666                     os_strdup(if_matched_group, config_ruleinfo->if_group);
667                 }
668             }
669
670             /* If_matched_sid, we need to get the if_sid */
671             if (config_ruleinfo->if_matched_sid &&
672                     !config_ruleinfo->if_sid &&
673                     !config_ruleinfo->if_group) {
674                 os_calloc(16, sizeof(char), config_ruleinfo->if_sid);
675                 snprintf(config_ruleinfo->if_sid, 15, "%d",
676                          config_ruleinfo->if_matched_sid);
677             }
678
679             /* Check the regexes */
680             if (regex) {
681                 os_calloc(1, sizeof(OSRegex), config_ruleinfo->regex);
682                 if (!OSRegex_Compile(regex, config_ruleinfo->regex, 0)) {
683                     merror(REGEX_COMPILE, __local_name, regex,
684                            config_ruleinfo->regex->error);
685                     if (regex) {
686                         free(regex);
687                     }
688                     return (-1);
689                 }
690                 free(regex);
691                 regex = NULL;
692             }
693
694             /* Add match */
695             if (match) {
696                 os_calloc(1, sizeof(OSMatch), config_ruleinfo->match);
697                 if (!OSMatch_Compile(match, config_ruleinfo->match, 0)) {
698                     merror(REGEX_COMPILE, __local_name, match,
699                            config_ruleinfo->match->error);
700                     if (match) {
701                         free(match);
702                     }
703                     return (-1);
704                 }
705                 free(match);
706                 match = NULL;
707             }
708
709             /* Add id */
710             if (id) {
711                 os_calloc(1, sizeof(OSMatch), config_ruleinfo->id);
712                 if (!OSMatch_Compile(id, config_ruleinfo->id, 0)) {
713                     merror(REGEX_COMPILE, __local_name, id,
714                            config_ruleinfo->id->error);
715                     if (id) {
716                         free(id);
717                     }
718                     return (-1);
719                 }
720                 free(id);
721                 id = NULL;
722             }
723
724             /* Add srcport */
725             if (srcport) {
726                 os_calloc(1, sizeof(OSMatch), config_ruleinfo->srcport);
727                 if (!OSMatch_Compile(srcport, config_ruleinfo->srcport, 0)) {
728                     merror(REGEX_COMPILE, __local_name, srcport,
729                            config_ruleinfo->id->error);
730                     if (srcport) {
731                         free(srcport);
732                     }
733                     return (-1);
734                 }
735                 free(srcport);
736                 srcport = NULL;
737             }
738
739             /* Add dstport */
740             if (dstport) {
741                 os_calloc(1, sizeof(OSMatch), config_ruleinfo->dstport);
742                 if (!OSMatch_Compile(dstport, config_ruleinfo->dstport, 0)) {
743                     merror(REGEX_COMPILE, __local_name, dstport,
744                            config_ruleinfo->id->error);
745                     if (dstport) {
746                         free(dstport);
747                     }
748                     return (-1);
749                 }
750                 free(dstport);
751                 dstport = NULL;
752             }
753
754             /* Add status */
755             if (status) {
756                 os_calloc(1, sizeof(OSMatch), config_ruleinfo->status);
757                 if (!OSMatch_Compile(status, config_ruleinfo->status, 0)) {
758                     merror(REGEX_COMPILE, __local_name, status,
759                            config_ruleinfo->status->error);
760                     if (status) {
761                         free(status);
762                     }
763                     return (-1);
764                 }
765                 free(status);
766                 status = NULL;
767             }
768
769             /* Add hostname */
770             if (hostname) {
771                 os_calloc(1, sizeof(OSMatch), config_ruleinfo->hostname);
772                 if (!OSMatch_Compile(hostname, config_ruleinfo->hostname, 0)) {
773                     merror(REGEX_COMPILE, __local_name, hostname,
774                            config_ruleinfo->hostname->error);
775                     if (hostname) {
776                         free(hostname);
777                     }
778                     return (-1);
779                 }
780                 free(hostname);
781                 hostname = NULL;
782             }
783
784             /* Add extra data */
785             if (extra_data) {
786                 os_calloc(1, sizeof(OSMatch), config_ruleinfo->extra_data);
787                 if (!OSMatch_Compile(extra_data,
788                                      config_ruleinfo->extra_data, 0)) {
789                     merror(REGEX_COMPILE, __local_name, extra_data,
790                            config_ruleinfo->extra_data->error);
791                     if (extra_data) {
792                         free(extra_data);
793                     }
794                     return (-1);
795                 }
796                 free(extra_data);
797                 extra_data = NULL;
798             }
799
800             /* Add in program name */
801             if (program_name) {
802                 os_calloc(1, sizeof(OSMatch), config_ruleinfo->program_name);
803                 if (!OSMatch_Compile(program_name,
804                                      config_ruleinfo->program_name, 0)) {
805                     merror(REGEX_COMPILE, __local_name, program_name,
806                            config_ruleinfo->program_name->error);
807                     if (program_name) {
808                         free(program_name);
809                     }
810                     return (-1);
811                 }
812                 free(program_name);
813                 program_name = NULL;
814             }
815
816             /* Add user */
817             if (user) {
818                 os_calloc(1, sizeof(OSMatch), config_ruleinfo->user);
819                 if (!OSMatch_Compile(user, config_ruleinfo->user, 0)) {
820                     merror(REGEX_COMPILE, __local_name, user,
821                            config_ruleinfo->user->error);
822                     if (user) {
823                         free(user);
824                     }
825                     return (-1);
826                 }
827                 free(user);
828                 user = NULL;
829             }
830
831             /* Add URL */
832             if (url) {
833                 os_calloc(1, sizeof(OSMatch), config_ruleinfo->url);
834                 if (!OSMatch_Compile(url, config_ruleinfo->url, 0)) {
835                     merror(REGEX_COMPILE, __local_name, url,
836                            config_ruleinfo->url->error);
837                     if (url) {
838                         free(url);
839                     }
840                     return (-1);
841                 }
842                 free(url);
843                 url = NULL;
844             }
845
846             /* Add matched_group */
847             if (if_matched_group) {
848                 os_calloc(1, sizeof(OSMatch), config_ruleinfo->if_matched_group);
849
850                 if (!OSMatch_Compile(if_matched_group,
851                                      config_ruleinfo->if_matched_group, 0)) {
852                     merror(REGEX_COMPILE, __local_name, if_matched_group,
853                            config_ruleinfo->if_matched_group->error);
854                     return (-1);
855                 }
856                 free(if_matched_group);
857                 if_matched_group = NULL;
858             }
859
860             /* Add matched_regex */
861             if (if_matched_regex) {
862                 os_calloc(1, sizeof(OSRegex),
863                           config_ruleinfo->if_matched_regex);
864                 if (!OSRegex_Compile(if_matched_regex,
865                                      config_ruleinfo->if_matched_regex, 0)) {
866                     merror(REGEX_COMPILE, __local_name, if_matched_regex,
867                            config_ruleinfo->if_matched_regex->error);
868                     if (if_matched_regex) {
869                         free(if_matched_regex);
870                     }
871                     return (-1);
872                 }
873                 free(if_matched_regex);
874                 if_matched_regex = NULL;
875             }
876
877             /* Call the function provided */
878             ruleact_function(config_ruleinfo, data);
879
880             j++; /* Next rule */
881
882         } /* while(rule[j]) */
883         OS_ClearNode(rule);
884         i++;
885
886     } /* while (node[i]) */
887
888     /* Clean global node */
889     OS_ClearNode(node);
890     OS_ClearXML(&xml);
891
892     return (0);
893 }
894
895 /* Allocate memory for a rule */
896 static RuleInfo *_OS_AllocateRule()
897 {
898     RuleInfo *ruleinfo_pt = NULL;
899
900     /* Allocate memory for structure */
901     ruleinfo_pt = (RuleInfo *)calloc(1, sizeof(RuleInfo));
902     if (ruleinfo_pt == NULL) {
903         ErrorExit(MEM_ERROR, __local_name, errno, strerror(errno));
904     }
905
906     /* Default values */
907     ruleinfo_pt->level = -1;
908
909     /* Default category is syslog */
910     ruleinfo_pt->category = SYSLOG;
911
912     ruleinfo_pt->ar = NULL;
913
914     ruleinfo_pt->context = 0;
915
916     /* Default sigid of -1 */
917     ruleinfo_pt->sigid = -1;
918     ruleinfo_pt->firedtimes = 0;
919     ruleinfo_pt->maxsize = 0;
920     ruleinfo_pt->frequency = 0;
921     ruleinfo_pt->ignore_time = 0;
922     ruleinfo_pt->timeframe = 0;
923     ruleinfo_pt->time_ignored = 0;
924
925     ruleinfo_pt->context_opts = 0;
926     ruleinfo_pt->alert_opts = 0;
927     ruleinfo_pt->ignore = 0;
928     ruleinfo_pt->ckignore = 0;
929
930     ruleinfo_pt->day_time = NULL;
931     ruleinfo_pt->week_day = NULL;
932
933     ruleinfo_pt->group = NULL;
934     ruleinfo_pt->regex = NULL;
935     ruleinfo_pt->match = NULL;
936     ruleinfo_pt->decoded_as = 0;
937
938     ruleinfo_pt->comment = NULL;
939     ruleinfo_pt->info = NULL;
940     ruleinfo_pt->cve = NULL;
941
942     ruleinfo_pt->if_sid = NULL;
943     ruleinfo_pt->if_group = NULL;
944     ruleinfo_pt->if_level = NULL;
945
946     ruleinfo_pt->if_matched_regex = NULL;
947     ruleinfo_pt->if_matched_group = NULL;
948     ruleinfo_pt->if_matched_sid = 0;
949
950     ruleinfo_pt->user = NULL;
951     ruleinfo_pt->srcip = NULL;
952     ruleinfo_pt->srcport = NULL;
953     ruleinfo_pt->dstip = NULL;
954     ruleinfo_pt->dstport = NULL;
955     ruleinfo_pt->url = NULL;
956     ruleinfo_pt->id = NULL;
957     ruleinfo_pt->status = NULL;
958     ruleinfo_pt->hostname = NULL;
959     ruleinfo_pt->program_name = NULL;
960     ruleinfo_pt->action = NULL;
961
962     /* Zero last matched events */
963     ruleinfo_pt->__frequency = 0;
964     ruleinfo_pt->last_events = NULL;
965
966     /* Zero the list of previous matches */
967     ruleinfo_pt->sid_prev_matched = NULL;
968     ruleinfo_pt->group_prev_matched = NULL;
969
970     ruleinfo_pt->sid_search = NULL;
971     ruleinfo_pt->group_search = NULL;
972
973     ruleinfo_pt->event_search = NULL;
974
975     return (ruleinfo_pt);
976 }
977
978 /* Reads the rules attributes and assign them */
979 static int _OS_GetRulesAttributes(char **attributes, char **values,
980                                   RuleInfo *ruleinfo_pt)
981 {
982     int k = 0;
983
984     const char *xml_id = "id";
985     const char *xml_level = "level";
986     const char *xml_maxsize = "maxsize";
987     const char *xml_timeframe = "timeframe";
988     const char *xml_frequency = "frequency";
989     const char *xml_accuracy = "accuracy";
990     const char *xml_noalert = "noalert";
991     const char *xml_ignore_time = "ignore";
992     const char *xml_overwrite = "overwrite";
993
994     /* Get attributes */
995     while (attributes[k]) {
996         if (!values[k]) {
997             merror(RL_EMPTY_ATTR, __local_name, attributes[k]);
998             return (-1);
999         }
1000         /* Get rule Id */
1001         else if (strcasecmp(attributes[k], xml_id) == 0) {
1002             if (OS_StrIsNum(values[k]) && (strlen(values[k]) <= 6 )) {
1003                 ruleinfo_pt->sigid = atoi(values[k]);
1004             } else {
1005                 merror(XML_VALUEERR, __local_name, attributes[k], values[k]);
1006                 return (-1);
1007             }
1008         }
1009         /* Get level */
1010         else if (strcasecmp(attributes[k], xml_level) == 0) {
1011             if (OS_StrIsNum(values[k]) && (strlen(values[k]) <= 3)) {
1012                 ruleinfo_pt->level = atoi(values[k]);
1013             } else {
1014                 merror(XML_VALUEERR, __local_name, attributes[k], values[k]);
1015                 return (-1);
1016             }
1017         }
1018         /* Get maxsize */
1019         else if (strcasecmp(attributes[k], xml_maxsize) == 0) {
1020             if (OS_StrIsNum(values[k]) && (strlen(values[k]) <= 4)) {
1021                 ruleinfo_pt->maxsize = atoi(values[k]);
1022
1023                 /* Add EXTRAINFO options */
1024                 if (ruleinfo_pt->maxsize > 0 &&
1025                         !(ruleinfo_pt->alert_opts & DO_EXTRAINFO)) {
1026                     ruleinfo_pt->alert_opts |= DO_EXTRAINFO;
1027                 }
1028             } else {
1029                 merror(XML_VALUEERR, __local_name, attributes[k], values[k]);
1030                 return (-1);
1031             }
1032         }
1033         /* Get timeframe */
1034         else if (strcasecmp(attributes[k], xml_timeframe) == 0) {
1035             if (OS_StrIsNum(values[k]) && (strlen(values[k]) <= 5)) {
1036                 ruleinfo_pt->timeframe = atoi(values[k]);
1037             } else {
1038                 merror(XML_VALUEERR, __local_name, attributes[k], values[k]);
1039                 return (-1);
1040             }
1041         }
1042         /* Get frequency */
1043         else if (strcasecmp(attributes[k], xml_frequency) == 0) {
1044             if (OS_StrIsNum(values[k]) && (strlen(values[k]) <= 4)) {
1045                 ruleinfo_pt->frequency = atoi(values[k]);
1046             } else {
1047                 merror(XML_VALUEERR, __local_name, attributes[k], values[k]);
1048                 return (-1);
1049             }
1050         }
1051         /* Rule accuracy */
1052         else if (strcasecmp(attributes[k], xml_accuracy) == 0) {
1053             merror("%s: XXX: Use of 'accuracy' isn't supported. Ignoring.",
1054                    __local_name);
1055         }
1056         /* Rule ignore_time */
1057         else if (strcasecmp(attributes[k], xml_ignore_time) == 0) {
1058             if (OS_StrIsNum(values[k]) && (strlen(values[k]) <= 4)) {
1059                 ruleinfo_pt->ignore_time = atoi(values[k]);
1060             } else {
1061                 merror(XML_VALUEERR, __local_name, attributes[k], values[k]);
1062                 return (-1);
1063             }
1064         }
1065         /* Rule noalert */
1066         else if (strcasecmp(attributes[k], xml_noalert) == 0) {
1067             ruleinfo_pt->alert_opts |= NO_ALERT;
1068         } else if (strcasecmp(attributes[k], xml_overwrite) == 0) {
1069             if (strcmp(values[k], "yes") == 0) {
1070                 ruleinfo_pt->alert_opts |= DO_OVERWRITE;
1071             } else if (strcmp(values[k], "no") == 0) {
1072             } else {
1073                 merror(XML_VALUEERR, __local_name, attributes[k], values[k]);
1074                 return (-1);
1075             }
1076         } else {
1077             merror(XML_INVELEM, __local_name, attributes[k]);
1078             return (-1);
1079         }
1080         k++;
1081     }
1082     return (0);
1083 }
1084