1 /* Copyright (C) 2009 Trend Micro Inc.
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
12 #define ARGV0 "ossec-testrule"
16 #include "alerts/alerts.h"
17 #include "alerts/getloglocation.h"
18 #include "os_execd/execd.h"
19 #include "os_regex/os_regex.h"
20 #include "os_net/os_net.h"
21 #include "active-response.h"
25 #include "eventinfo.h"
26 #include "accumulator.h"
27 #include "analysisd.h"
29 #include "cleanevent.h"
31 /** Internal Functions **/
32 void OS_ReadMSG(char *ut_str);
34 /* Analysisd function */
35 RuleInfo *OS_CheckIfRuleMatch(Eventinfo *lf, RuleNode *curr_node);
37 void DecodeEvent(Eventinfo *lf);
39 /* Print help statement */
40 __attribute__((noreturn))
41 static void help_logtest(void)
44 print_out(" %s: -[Vhdtva] [-c config] [-D dir] [-U rule:alert:decoder]", ARGV0);
45 print_out(" -V Version and license message");
46 print_out(" -h This help message");
47 print_out(" -d Execute in debug mode. This parameter");
48 print_out(" can be specified multiple times");
49 print_out(" to increase the debug level.");
50 print_out(" -t Test configuration");
51 print_out(" -a Alerts output");
52 print_out(" -v Verbose (full) output/rule debugging");
53 print_out(" -c <config> Configuration file to use (default: %s)", DEFAULTCPATH);
54 print_out(" -D <dir> Directory to chroot into (default: %s)", DEFAULTDIR);
55 print_out(" -U <rule:alert:decoder> Unit test. Refer to contrib/ossec-testing/runtests.py");
60 int main(int argc, char **argv)
65 const char *dir = DEFAULTDIR;
66 const char *cfg = DEFAULTCPATH;
67 const char *user = USER;
68 const char *group = GROUPGLOBAL;
82 active_responses = NULL;
83 memset(prev_month, '\0', 4);
85 #ifdef LIBGEOIP_ENABLED
89 while ((c = getopt(argc, argv, "VatvdhU:D:c:q")) != -1) {
105 ErrorExit("%s: -U needs an argument", ARGV0);
111 ErrorExit("%s: -D needs an argument", ARGV0);
117 ErrorExit("%s: -c needs an argument", ARGV0);
136 /* Read configuration file */
137 if (GlobalConf(cfg) < 0) {
138 ErrorExit(CONFIG_ERROR, ARGV0, cfg);
141 debug1(READ_CONFIG, ARGV0);
143 #ifdef LIBGEOIP_ENABLED
144 Config.geoip_jsonout = getDefine_Int("analysisd", "geoip_jsonout", 0, 1);
146 /* Opening GeoIP DB */
147 if(Config.geoipdb_file) {
148 geoipdb = GeoIP_open(Config.geoipdb_file, GEOIP_INDEX_CACHE);
151 merror("%s: Unable to open GeoIP database from: %s (disabling GeoIP).", ARGV0, Config.geoipdb_file);
156 /* Get server hostname */
157 memset(__shost, '\0', 512);
158 if (gethostname(__shost, 512 - 1) != 0) {
159 strncpy(__shost, OSSEC_SERVER, 512 - 1);
163 /* Remove domain part if available */
164 _ltmp = strchr(__shost, '.');
170 /* Check if the user/group given are valid */
171 uid = Privsep_GetUser(user);
172 gid = Privsep_GetGroup(group);
173 if (uid == (uid_t) - 1 || gid == (gid_t) - 1) {
174 ErrorExit(USER_ERROR, ARGV0, user, group);
178 if (Privsep_SetGroup(gid) < 0) {
179 ErrorExit(SETGID_ERROR, ARGV0, group, errno, strerror(errno));
183 if (Privsep_Chroot(dir) < 0) {
184 ErrorExit(CHROOT_ERROR, ARGV0, dir, errno, strerror(errno));
188 Config.decoder_order_size = (size_t)getDefine_Int("analysisd", "decoder_order_size", 8, MAX_DECODER_ORDER_SIZE);
192 * Anonymous Section: Load rules, decoders, and lists
194 * As lists require two pass loading of rules that make use of list lookups
195 * are created with blank database structs, and need to be filled in after
196 * completion of all rules and lists.
201 /* Initialize the decoders list */
202 OS_CreateOSDecoderList();
204 if (!Config.decoders) {
207 if (!ReadDecodeXML("etc/decoder.xml")) {
208 ErrorExit(CONFIG_ERROR, ARGV0, XML_DECODER);
211 /* Read local ones */
212 c = ReadDecodeXML("etc/local_decoder.xml");
215 ErrorExit(CONFIG_ERROR, ARGV0, XML_LDECODER);
218 verbose("%s: INFO: Reading local decoder file.", ARGV0);
221 /* New loaded based on file specified in ossec.conf */
222 char **decodersfiles;
223 decodersfiles = Config.decoders;
224 while ( decodersfiles && *decodersfiles) {
227 verbose("%s: INFO: Reading decoder file %s.", ARGV0, *decodersfiles);
229 if (!ReadDecodeXML(*decodersfiles)) {
230 ErrorExit(CONFIG_ERROR, ARGV0, *decodersfiles);
233 free(*decodersfiles);
243 /* Initialize the lists of list struct */
244 Lists_OP_CreateLists();
245 /* Load each list into list struct */
248 listfiles = Config.lists;
249 while (listfiles && *listfiles) {
250 verbose("%s: INFO: Reading the lists file: '%s'", ARGV0, *listfiles);
251 if (Lists_OP_LoadList(*listfiles) < 0) {
252 ErrorExit(LISTS_ERROR, ARGV0, *listfiles);
263 /* Create the rules list */
264 Rules_OP_CreateRules();
269 rulesfiles = Config.includes;
270 while (rulesfiles && *rulesfiles) {
271 debug1("%s: INFO: Reading rules file: '%s'", ARGV0, *rulesfiles);
272 if (Rules_OP_ReadRules(*rulesfiles) < 0) {
273 ErrorExit(RULES_ERROR, ARGV0, *rulesfiles);
280 free(Config.includes);
281 Config.includes = NULL;
284 /* Find all rules with that require list lookups and attache the
285 * the correct list struct to the rule. This keeps rules from
286 * having to search thought the list of lists for the correct file
287 * during rule evaluation.
293 /* Fix the levels/accuracy */
296 RuleNode *tmp_node = OS_GetFirstRule();
298 total_rules = _setlevels(tmp_node, 0);
299 debug1("%s: INFO: Total rules enabled: '%d'", ARGV0, total_rules);
302 /* Creating a rules hash (for reading alerts from other servers) */
304 RuleNode *tmp_node = OS_GetFirstRule();
305 Config.g_rules_hash = OSHash_Create();
306 if (!Config.g_rules_hash) {
307 ErrorExit(MEM_ERROR, ARGV0, errno, strerror(errno));
309 AddHash_Rule(tmp_node);
312 if (test_config == 1) {
317 if (Privsep_SetUser(uid) < 0) {
318 ErrorExit(SETUID_ERROR, ARGV0, user, errno, strerror(errno));
321 /* Start up message */
322 verbose(STARTUP_MSG, ARGV0, getpid());
324 /* Going to main loop */
330 /* Receive the messages (events) and analyze them */
331 __attribute__((noreturn))
332 void OS_ReadMSG(char *ut_str)
334 char msg[OS_MAXSTR + 1];
336 char *ut_alertlevel = NULL;
337 char *ut_rulelevel = NULL;
338 char *ut_decoder_name = NULL;
341 /* XXX Break apart string */
342 ut_rulelevel = ut_str;
343 ut_alertlevel = strchr(ut_rulelevel, ':');
344 if (!ut_alertlevel) {
345 ErrorExit("%s: -U requires the matching format to be "
346 "\"<rule_id>:<alert_level>:<decoder_name>\"", ARGV0);
348 *ut_alertlevel = '\0';
351 ut_decoder_name = strchr(ut_alertlevel, ':');
352 if (!ut_decoder_name) {
353 ErrorExit("%s: -U requires the matching format to be "
354 "\"<rule_id>:<alert_level>:<decoder_name>\"", ARGV0);
356 *ut_decoder_name = '\0';
361 RuleInfoDetail *last_info_detail;
364 /* Null global pointer to current rule */
365 currently_rule = NULL;
367 /* Create the event list */
368 OS_CreateEventList(Config.memorysize);
370 /* Initiate the FTS list */
372 ErrorExit(FTS_LIST_ERROR, ARGV0);
375 /* Initialize the Accumulator */
376 if (!Accumulate_Init()) {
377 merror("accumulator: ERROR: Initialization failed");
383 /* Get current time before starting */
386 /* Do some cleanup */
387 memset(msg, '\0', OS_MAXSTR + 1);
390 print_out("%s: Type one log per line.\n", ARGV0);
395 lf = (Eventinfo *)calloc(1, sizeof(Eventinfo));
396 os_calloc(Config.decoder_order_size, sizeof(char*), lf->fields);
399 /* This shouldn't happen */
401 ErrorExit(MEM_ERROR, ARGV0, errno, strerror(errno));
405 snprintf(msg, 15, "1:stdin:");
407 /* Receive message from queue */
408 if (fgets(msg + 8, OS_MAXSTR - 8, stdin)) {
409 RuleNode *rulenode_pt;
411 /* Get the time we received the event */
415 if (msg[strlen(msg) - 1] == '\n') {
416 msg[strlen(msg) - 1] = '\0';
419 /* Make sure we ignore blank lines */
420 if (strlen(msg) < 10) {
428 /* Default values for the log info */
431 /* Clean the msg appropriately */
432 if (OS_CleanMSG(msg, lf) < 0) {
433 merror(IMSG_ERROR, ARGV0, msg);
440 /* Current rule must be null in here */
441 currently_rule = NULL;
443 /*** Run decoders ***/
445 lf->size = strlen(lf->log);
450 /* Run accumulator */
451 if ( lf->decoder_info->accumulate == 1 ) {
452 print_out("\n**ACCUMULATOR: LEVEL UP!!**\n");
456 /* Loop over all the rules */
457 rulenode_pt = OS_GetFirstRule();
459 ErrorExit("%s: Rules in an inconsistent state. Exiting.",
464 if (full_output && !alert_only) {
465 print_out("\n**Rule debugging:");
470 if (lf->decoder_info->type == OSSEC_ALERT) {
471 if (!lf->generated_rule) {
475 /* Process the alert */
476 currently_rule = lf->generated_rule;
479 /* The categories must match */
480 else if (rulenode_pt->ruleinfo->category !=
481 lf->decoder_info->type) {
485 /* Check each rule */
486 else if ((currently_rule = OS_CheckIfRuleMatch(lf, rulenode_pt))
493 const char *(ruleinfodetail_text[]) = {"Text", "Link", "CVE", "OSVDB", "BUGTRACKID"};
494 print_out("\n**Phase 3: Completed filtering (rules).");
495 print_out(" Rule id: '%d'", currently_rule->sigid);
496 print_out(" Level: '%d'", currently_rule->level);
497 print_out(" Description: '%s'", currently_rule->comment);
498 for (last_info_detail = currently_rule->info_details; last_info_detail != NULL; last_info_detail = last_info_detail->next) {
499 print_out(" Info - %s: '%s'", ruleinfodetail_text[last_info_detail->type], last_info_detail->data);
505 if (currently_rule->level == 0) {
509 /* Check ignore time */
510 if (currently_rule->ignore_time) {
511 if (currently_rule->time_ignored == 0) {
512 currently_rule->time_ignored = lf->time;
514 /* If the current time - the time the rule was ignored
515 * is less than the time it should be ignored,
518 else if ((lf->time - currently_rule->time_ignored)
519 < currently_rule->ignore_time) {
522 currently_rule->time_ignored = 0;
526 /* Pointer to the rule that generated it */
527 lf->generated_rule = currently_rule;
530 /* Check if we should ignore it */
531 if (currently_rule->ckignore && IGnore(lf)) {
532 lf->generated_rule = NULL;
536 /* Check if we need to add to ignore list */
537 if (currently_rule->ignore) {
541 /* Log the alert if configured to */
542 if (currently_rule->alert_opts & DO_LOGALERT) {
547 print_out("**Alert to be generated.\n\n");
551 /* Copy the structure to the state memory of if_matched_sid */
552 if (currently_rule->sid_prev_matched) {
553 if (!OSList_AddData(currently_rule->sid_prev_matched, lf)) {
554 merror("%s: Unable to add data to sig list.", ARGV0);
556 lf->sid_node_to_delete =
557 currently_rule->sid_prev_matched->last_node;
562 else if (currently_rule->group_prev_matched) {
565 while (i < currently_rule->group_prev_matched_sz) {
567 currently_rule->group_prev_matched[i],
569 merror("%s: Unable to add data to grp list.", ARGV0);
578 } while ((rulenode_pt = rulenode_pt->next) != NULL);
581 /* Set up exit code if we are doing unit testing */
585 print_out("lf->decoder_info->name: '%s'", lf->decoder_info->name);
586 print_out("ut_decoder_name : '%s'", ut_decoder_name);
587 if (lf->decoder_info->name != NULL && strcasecmp(ut_decoder_name, lf->decoder_info->name) == 0) {
590 if (!currently_rule) {
591 merror("%s: currently_rule not set!", ARGV0);
594 snprintf(holder, 1023, "%d", currently_rule->sigid);
595 if (strcasecmp(ut_rulelevel, holder) == 0) {
597 snprintf(holder, 1023, "%d", currently_rule->level);
598 if (strcasecmp(ut_alertlevel, holder) == 0) {
600 printf("%d\n", exit_code);
603 } else if (lf->decoder_info->name != NULL) {
604 print_out("decoder matched : '%s'", lf->decoder_info->name);
605 print_out("decoder expected: '%s'", ut_decoder_name);
607 print_out("decoder matched : 'NULL'");
611 /* Only clear the memory if the eventinfo was not
612 * added to the stateful memory
613 * -- message is free inside clean event --
615 if (lf->generated_rule == NULL) {