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 #ifdef ARGV0
11 #undef ARGV0
12 #define ARGV0 "ossec-testrule"
13 #endif
14
15 #include "shared.h"
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"
22 #include "config.h"
23 #include "rules.h"
24 #include "stats.h"
25 #include "eventinfo.h"
26 #include "accumulator.h"
27 #include "analysisd.h"
28 #include "fts.h"
29 #include "cleanevent.h"
30
31 /** Internal Functions **/
32 void OS_ReadMSG(char *ut_str);
33
34 /* Analysisd function */
35 RuleInfo *OS_CheckIfRuleMatch(Eventinfo *lf, RuleNode *curr_node);
36
37 void DecodeEvent(Eventinfo *lf);
38
39 /* Print help statement */
40 __attribute__((noreturn))
help_logtest(void)41 static void help_logtest(void)
42 {
43 print_header();
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");
56 print_out(" ");
57 exit(1);
58 }
59
main(int argc,char ** argv)60 int main(int argc, char **argv)
61 {
62 int test_config = 0;
63 int c = 0;
64 char *ut_str = NULL;
65 const char *dir = DEFAULTDIR;
66 const char *cfg = DEFAULTCPATH;
67 const char *user = USER;
68 const char *group = GROUPGLOBAL;
69 uid_t uid;
70 gid_t gid;
71 int quiet = 0;
72
73 /* Set the name */
74 OS_SetName(ARGV0);
75
76 thishour = 0;
77 today = 0;
78 prev_year = 0;
79 full_output = 0;
80 alert_only = 0;
81
82 active_responses = NULL;
83 memset(prev_month, '\0', 4);
84
85 #ifdef LIBGEOIP_ENABLED
86 geoipdb = NULL;
87 #endif
88
89 while ((c = getopt(argc, argv, "VatvdhU:D:c:q")) != -1) {
90 switch (c) {
91 case 'V':
92 print_version();
93 break;
94 case 't':
95 test_config = 1;
96 break;
97 case 'h':
98 help_logtest();
99 break;
100 case 'd':
101 nowDebug();
102 break;
103 case 'U':
104 if (!optarg) {
105 ErrorExit("%s: -U needs an argument", ARGV0);
106 }
107 ut_str = optarg;
108 break;
109 case 'D':
110 if (!optarg) {
111 ErrorExit("%s: -D needs an argument", ARGV0);
112 }
113 dir = optarg;
114 break;
115 case 'c':
116 if (!optarg) {
117 ErrorExit("%s: -c needs an argument", ARGV0);
118 }
119 cfg = optarg;
120 break;
121 case 'a':
122 alert_only = 1;
123 break;
124 case 'q':
125 quiet = 1;
126 break;
127 case 'v':
128 full_output = 1;
129 break;
130 default:
131 help_logtest();
132 break;
133 }
134 }
135
136 /* Read configuration file */
137 if (GlobalConf(cfg) < 0) {
138 ErrorExit(CONFIG_ERROR, ARGV0, cfg);
139 }
140
141 debug1(READ_CONFIG, ARGV0);
142
143 #ifdef LIBGEOIP_ENABLED
144 Config.geoip_jsonout = getDefine_Int("analysisd", "geoip_jsonout", 0, 1);
145
146 /* Opening GeoIP DB */
147 if(Config.geoipdb_file) {
148 geoipdb = GeoIP_open(Config.geoipdb_file, GEOIP_INDEX_CACHE);
149 if (geoipdb == NULL)
150 {
151 merror("%s: Unable to open GeoIP database from: %s (disabling GeoIP).", ARGV0, Config.geoipdb_file);
152 }
153 }
154 #endif
155
156 /* Get server hostname */
157 memset(__shost, '\0', 512);
158 if (gethostname(__shost, 512 - 1) != 0) {
159 strncpy(__shost, OSSEC_SERVER, 512 - 1);
160 } else {
161 char *_ltmp;
162
163 /* Remove domain part if available */
164 _ltmp = strchr(__shost, '.');
165 if (_ltmp) {
166 *_ltmp = '\0';
167 }
168 }
169
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);
175 }
176
177 /* Set the group */
178 if (Privsep_SetGroup(gid) < 0) {
179 ErrorExit(SETGID_ERROR, ARGV0, group, errno, strerror(errno));
180 }
181
182 /* Chroot */
183 if (Privsep_Chroot(dir) < 0) {
184 ErrorExit(CHROOT_ERROR, ARGV0, dir, errno, strerror(errno));
185 }
186 nowChroot();
187
188 Config.decoder_order_size = (size_t)getDefine_Int("analysisd", "decoder_order_size", 8, MAX_DECODER_ORDER_SIZE);
189
190
191 /*
192 * Anonymous Section: Load rules, decoders, and lists
193 *
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.
197 */
198 {
199 {
200 /* Load decoders */
201 /* Initialize the decoders list */
202 OS_CreateOSDecoderList();
203
204 if (!Config.decoders) {
205 /* Legacy loading */
206 /* Read decoders */
207 if (!ReadDecodeXML("etc/decoder.xml")) {
208 ErrorExit(CONFIG_ERROR, ARGV0, XML_DECODER);
209 }
210
211 /* Read local ones */
212 c = ReadDecodeXML("etc/local_decoder.xml");
213 if (!c) {
214 if ((c != -2)) {
215 ErrorExit(CONFIG_ERROR, ARGV0, XML_LDECODER);
216 }
217 } else {
218 verbose("%s: INFO: Reading local decoder file.", ARGV0);
219 }
220 } else {
221 /* New loaded based on file specified in ossec.conf */
222 char **decodersfiles;
223 decodersfiles = Config.decoders;
224 while ( decodersfiles && *decodersfiles) {
225
226 if(!quiet) {
227 verbose("%s: INFO: Reading decoder file %s.", ARGV0, *decodersfiles);
228 }
229 if (!ReadDecodeXML(*decodersfiles)) {
230 ErrorExit(CONFIG_ERROR, ARGV0, *decodersfiles);
231 }
232
233 free(*decodersfiles);
234 decodersfiles++;
235 }
236 }
237
238 /* Load decoders */
239 SetDecodeXML();
240 }
241 {
242 /* Load Lists */
243 /* Initialize the lists of list struct */
244 Lists_OP_CreateLists();
245 /* Load each list into list struct */
246 {
247 char **listfiles;
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);
253 }
254 free(*listfiles);
255 listfiles++;
256 }
257 free(Config.lists);
258 Config.lists = NULL;
259 }
260 }
261 {
262 /* Load Rules */
263 /* Create the rules list */
264 Rules_OP_CreateRules();
265
266 /* Read the rules */
267 {
268 char **rulesfiles;
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);
274 }
275
276 free(*rulesfiles);
277 rulesfiles++;
278 }
279
280 free(Config.includes);
281 Config.includes = NULL;
282 }
283
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.
288 */
289 OS_ListLoadRules();
290 }
291 }
292
293 /* Fix the levels/accuracy */
294 {
295 int total_rules;
296 RuleNode *tmp_node = OS_GetFirstRule();
297
298 total_rules = _setlevels(tmp_node, 0);
299 debug1("%s: INFO: Total rules enabled: '%d'", ARGV0, total_rules);
300 }
301
302 /* Creating a rules hash (for reading alerts from other servers) */
303 {
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));
308 }
309 AddHash_Rule(tmp_node);
310 }
311
312 if (test_config == 1) {
313 exit(0);
314 }
315
316 /* Set the user */
317 if (Privsep_SetUser(uid) < 0) {
318 ErrorExit(SETUID_ERROR, ARGV0, user, errno, strerror(errno));
319 }
320
321 /* Start up message */
322 verbose(STARTUP_MSG, ARGV0, getpid());
323
324 /* Going to main loop */
325 OS_ReadMSG(ut_str);
326
327 exit(0);
328 }
329
330 /* Receive the messages (events) and analyze them */
331 __attribute__((noreturn))
OS_ReadMSG(char * ut_str)332 void OS_ReadMSG(char *ut_str)
333 {
334 char msg[OS_MAXSTR + 1];
335 int exit_code = 0;
336 char *ut_alertlevel = NULL;
337 char *ut_rulelevel = NULL;
338 char *ut_decoder_name = NULL;
339
340 if (ut_str) {
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);
347 } else {
348 *ut_alertlevel = '\0';
349 ut_alertlevel++;
350 }
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);
355 } else {
356 *ut_decoder_name = '\0';
357 ut_decoder_name++;
358 }
359 }
360
361 RuleInfoDetail *last_info_detail;
362 Eventinfo *lf;
363
364 /* Null global pointer to current rule */
365 currently_rule = NULL;
366
367 /* Create the event list */
368 OS_CreateEventList(Config.memorysize);
369
370 /* Initiate the FTS list */
371 if (!FTS_Init()) {
372 ErrorExit(FTS_LIST_ERROR, ARGV0);
373 }
374
375 /* Initialize the Accumulator */
376 if (!Accumulate_Init()) {
377 merror("accumulator: ERROR: Initialization failed");
378 exit(1);
379 }
380
381 __crt_ftell = 1;
382
383 /* Get current time before starting */
384 c_time = time(NULL);
385
386 /* Do some cleanup */
387 memset(msg, '\0', OS_MAXSTR + 1);
388
389 if (!alert_only) {
390 print_out("%s: Type one log per line.\n", ARGV0);
391 }
392
393 /* Daemon loop */
394 while (1) {
395 lf = (Eventinfo *)calloc(1, sizeof(Eventinfo));
396 os_calloc(Config.decoder_order_size, sizeof(char*), lf->fields);
397
398
399 /* This shouldn't happen */
400 if (lf == NULL) {
401 ErrorExit(MEM_ERROR, ARGV0, errno, strerror(errno));
402 }
403
404 /* Fix the msg */
405 snprintf(msg, 15, "1:stdin:");
406
407 /* Receive message from queue */
408 if (fgets(msg + 8, OS_MAXSTR - 8, stdin)) {
409 RuleNode *rulenode_pt;
410
411 /* Get the time we received the event */
412 c_time = time(NULL);
413
414 /* Remov newline */
415 if (msg[strlen(msg) - 1] == '\n') {
416 msg[strlen(msg) - 1] = '\0';
417 }
418
419 /* Make sure we ignore blank lines */
420 if (strlen(msg) < 10) {
421 continue;
422 }
423
424 if (!alert_only) {
425 print_out("\n");
426 }
427
428 /* Default values for the log info */
429 Zero_Eventinfo(lf);
430
431 /* Clean the msg appropriately */
432 if (OS_CleanMSG(msg, lf) < 0) {
433 merror(IMSG_ERROR, ARGV0, msg);
434
435 Free_Eventinfo(lf);
436
437 continue;
438 }
439
440 /* Current rule must be null in here */
441 currently_rule = NULL;
442
443 /*** Run decoders ***/
444 /* Get log size */
445 lf->size = strlen(lf->log);
446
447 /* Decode event */
448 DecodeEvent(lf);
449
450 /* Run accumulator */
451 if ( lf->decoder_info->accumulate == 1 ) {
452 print_out("\n**ACCUMULATOR: LEVEL UP!!**\n");
453 lf = Accumulate(lf);
454 }
455
456 /* Loop over all the rules */
457 rulenode_pt = OS_GetFirstRule();
458 if (!rulenode_pt) {
459 ErrorExit("%s: Rules in an inconsistent state. Exiting.",
460 ARGV0);
461 }
462
463 #ifdef TESTRULE
464 if (full_output && !alert_only) {
465 print_out("\n**Rule debugging:");
466 }
467 #endif
468
469 do {
470 if (lf->decoder_info->type == OSSEC_ALERT) {
471 if (!lf->generated_rule) {
472 break;
473 }
474
475 /* Process the alert */
476 currently_rule = lf->generated_rule;
477 }
478
479 /* The categories must match */
480 else if (rulenode_pt->ruleinfo->category !=
481 lf->decoder_info->type) {
482 continue;
483 }
484
485 /* Check each rule */
486 else if ((currently_rule = OS_CheckIfRuleMatch(lf, rulenode_pt))
487 == NULL) {
488 continue;
489 }
490
491 #ifdef TESTRULE
492 if (!alert_only) {
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);
500 }
501 }
502 #endif
503
504 /* Ignore level 0 */
505 if (currently_rule->level == 0) {
506 break;
507 }
508
509 /* Check ignore time */
510 if (currently_rule->ignore_time) {
511 if (currently_rule->time_ignored == 0) {
512 currently_rule->time_ignored = lf->time;
513 }
514 /* If the current time - the time the rule was ignored
515 * is less than the time it should be ignored,
516 * do not alert again
517 */
518 else if ((lf->time - currently_rule->time_ignored)
519 < currently_rule->ignore_time) {
520 break;
521 } else {
522 currently_rule->time_ignored = 0;
523 }
524 }
525
526 /* Pointer to the rule that generated it */
527 lf->generated_rule = currently_rule;
528
529
530 /* Check if we should ignore it */
531 if (currently_rule->ckignore && IGnore(lf)) {
532 lf->generated_rule = NULL;
533 break;
534 }
535
536 /* Check if we need to add to ignore list */
537 if (currently_rule->ignore) {
538 AddtoIGnore(lf);
539 }
540
541 /* Log the alert if configured to */
542 if (currently_rule->alert_opts & DO_LOGALERT) {
543 if (alert_only) {
544 OS_LogOutput(lf);
545 __crt_ftell++;
546 } else {
547 print_out("**Alert to be generated.\n\n");
548 }
549 }
550
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);
555 } else {
556 lf->sid_node_to_delete =
557 currently_rule->sid_prev_matched->last_node;
558 }
559 }
560
561 /* Group list */
562 else if (currently_rule->group_prev_matched) {
563 unsigned int i = 0;
564
565 while (i < currently_rule->group_prev_matched_sz) {
566 if (!OSList_AddData(
567 currently_rule->group_prev_matched[i],
568 lf)) {
569 merror("%s: Unable to add data to grp list.", ARGV0);
570 }
571 i++;
572 }
573 }
574
575 OS_AddEvent(lf);
576 break;
577
578 } while ((rulenode_pt = rulenode_pt->next) != NULL);
579
580 if (ut_str) {
581 /* Set up exit code if we are doing unit testing */
582 char holder[1024];
583 holder[1] = '\0';
584 exit_code = 3;
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) {
588 exit_code--;
589
590 if (!currently_rule) {
591 merror("%s: currently_rule not set!", ARGV0);
592 exit(-1);
593 }
594 snprintf(holder, 1023, "%d", currently_rule->sigid);
595 if (strcasecmp(ut_rulelevel, holder) == 0) {
596 exit_code--;
597 snprintf(holder, 1023, "%d", currently_rule->level);
598 if (strcasecmp(ut_alertlevel, holder) == 0) {
599 exit_code--;
600 printf("%d\n", exit_code);
601 }
602 }
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);
606 } else {
607 print_out("decoder matched : 'NULL'");
608 }
609 }
610
611 /* Only clear the memory if the eventinfo was not
612 * added to the stateful memory
613 * -- message is free inside clean event --
614 */
615 if (lf->generated_rule == NULL) {
616 Free_Eventinfo(lf);
617 }
618
619 } else {
620 exit(exit_code);
621 }
622 }
623 exit(exit_code);
624 }
625