1 /* @(#) $Id: run_check.c,v 1.49 2009/11/05 15:15:14 dcid Exp $ */
3 /* Copyright (C) 2009 Trend Micro Inc.
6 * This program is a free software; you can redistribute it
7 * and/or modify it under the terms of the GNU General Public
8 * License (version 3) as published by the FSF - Free Software
13 /* SCHED_BATCH is Linux specific and is only picked up with _GNU_SOURCE */
21 #include "os_crypto/md5/md5_op.h"
22 #include "os_crypto/sha1/sha1_op.h"
23 #include "os_crypto/md5_sha1/md5_sha1_op.h"
25 #include "rootcheck/rootcheck.h"
29 int c_read_file(char *file_name, char *oldsum, char *newsum);
32 /* Send syscheck message.
33 * Send a message related to syscheck change/addition.
35 int send_syscheck_msg(char *msg)
37 if(SendMSG(syscheck.queue, msg, SYSCHECK, SYSCHECK_MQ) < 0)
39 merror(QUEUE_SEND, ARGV0);
41 if((syscheck.queue = StartMQ(DEFAULTQPATH,WRITE)) < 0)
43 ErrorExit(QUEUE_FATAL, ARGV0, DEFAULTQPATH);
46 /* If we reach here, we can try to send it again */
47 SendMSG(syscheck.queue, msg, SYSCHECK, SYSCHECK_MQ);
55 /* Send rootcheck message.
56 * Send a message related to rootcheck change/addition.
58 int send_rootcheck_msg(char *msg)
60 if(SendMSG(syscheck.queue, msg, ROOTCHECK, ROOTCHECK_MQ) < 0)
62 merror(QUEUE_SEND, ARGV0);
64 if((syscheck.queue = StartMQ(DEFAULTQPATH,WRITE)) < 0)
66 ErrorExit(QUEUE_FATAL, ARGV0, DEFAULTQPATH);
69 /* If we reach here, we can try to send it again */
70 SendMSG(syscheck.queue, msg, ROOTCHECK, ROOTCHECK_MQ);
77 /* Sends syscheck db to the server.
81 char buf[MAX_LINE +1];
86 if(fseek(syscheck.fp, 0, SEEK_SET) == -1)
88 ErrorExit(FSEEK_ERROR, ARGV0, "syscheck_db");
92 /* Sending scan start message */
95 merror("%s: INFO: Starting syscheck scan (forwarding database).", ARGV0);
96 send_rootcheck_msg("Starting syscheck scan.");
100 sleep(syscheck.tsleep +10);
106 while(fgets(buf,MAX_LINE, syscheck.fp) != NULL)
108 if((buf[0] != '#') && (buf[0] != ' ') && (buf[0] != '\n'))
112 /* Removing the \n before sending to the analysis server */
113 n_buf = strchr(buf,'\n');
120 /* First 6 characters are for internal use */
124 send_syscheck_msg(n_buf);
127 /* A count and a sleep to avoid flooding the server.
128 * Time or speed are not requirements in here
133 /* sleep X every Y files */
134 if(file_count >= syscheck.sleep_after)
136 sleep(syscheck.tsleep);
143 /* Sending scan ending message */
144 sleep(syscheck.tsleep +10);
148 merror("%s: INFO: Ending syscheck scan (forwarding database).", ARGV0);
149 send_rootcheck_msg("Ending syscheck scan.");
156 * Run periodicaly the integrity checking
163 time_t curr_time = 0;
165 time_t prev_time_rk = 0;
166 time_t prev_time_sk = 0;
173 /* To be used by select. */
175 struct timeval selecttime;
181 * SCHED_BATCH forces the kernel to assume this is a cpu intensive
183 * and gives it a lower priority. This keeps ossec-syscheckd
185 * the interactity of an ssh session when checksumming large files.
186 * This is available in kernel flavors >= 2.6.16
189 struct sched_param pri;
192 pri.sched_priority = 0;
193 status = sched_setscheduler(0, SCHED_BATCH, &pri);
195 debug1("%s: Setting SCHED_BATCH returned: %d", ARGV0, status);
200 verbose("%s: Starting daemon ..",ARGV0);
205 /* Some time to settle */
206 memset(curr_hour, '\0', 12);
207 sleep(syscheck.tsleep * 10);
211 /* If the scan time/day is set, reset the
212 * syscheck.time/rootcheck.time
214 if(syscheck.scan_time || syscheck.scan_day)
216 /* At least once a week. */
217 syscheck.time = 604800;
218 rootcheck.time = 604800;
222 /* Will create the db to store syscheck data */
223 if(syscheck.scan_on_start)
228 sleep(syscheck.tsleep * 60);
233 prev_time_rk = time(0);
238 /* Before entering in daemon mode itself */
239 prev_time_sk = time(0);
240 sleep(syscheck.tsleep * 10);
243 /* If the scan_time or scan_day is set, we need to handle the
244 * current day/time on the loop.
246 if(syscheck.scan_time || syscheck.scan_day)
249 p = localtime(&curr_time);
252 /* Assign hour/min/sec values */
253 snprintf(curr_hour, 9, "%02d:%02d:%02d",
259 curr_day = p->tm_mday;
263 if(syscheck.scan_time && syscheck.scan_day)
265 if((OS_IsAfterTime(curr_hour, syscheck.scan_time)) &&
266 (OS_IsonDay(p->tm_wday, syscheck.scan_day)))
272 else if(syscheck.scan_time)
274 if(OS_IsAfterTime(curr_hour, syscheck.scan_time))
279 else if(syscheck.scan_day)
281 if(OS_IsonDay(p->tm_wday, syscheck.scan_day))
289 #if defined (USEINOTIFY) || defined (WIN32)
290 if(syscheck.realtime && (syscheck.realtime->fd >= 0))
291 verbose("%s: INFO: Starting real time file monitoring.", ARGV0);
295 /* Checking every SYSCHECK_WAIT */
302 /* Checking if syscheck should be restarted, */
303 run_now = os_check_restart_syscheck();
306 /* Checking if a day_time or scan_time is set. */
307 if(syscheck.scan_time || syscheck.scan_day)
309 p = localtime(&curr_time);
313 if(curr_day != p->tm_mday)
316 curr_day = p->tm_mday;
320 /* Checking for the time of the scan. */
321 if(!day_scanned && syscheck.scan_time && syscheck.scan_day)
323 if((OS_IsAfterTime(curr_hour, syscheck.scan_time)) &&
324 (OS_IsonDay(p->tm_wday, syscheck.scan_day)))
331 else if(!day_scanned && syscheck.scan_time)
333 /* Assign hour/min/sec values */
334 snprintf(curr_hour, 9, "%02d:%02d:%02d",
335 p->tm_hour, p->tm_min, p->tm_sec);
337 if(OS_IsAfterTime(curr_hour, syscheck.scan_time))
344 /* Checking for the day of the scan. */
345 else if(!day_scanned && syscheck.scan_day)
347 if(OS_IsonDay(p->tm_wday, syscheck.scan_day))
357 /* If time elapsed is higher than the rootcheck_time,
360 if(syscheck.rootcheck)
362 if(((curr_time - prev_time_rk) > rootcheck.time) || run_now)
365 prev_time_rk = time(0);
370 /* If time elapsed is higher than the syscheck time,
373 if(((curr_time - prev_time_sk) > syscheck.time) || run_now)
375 /* We need to create the db, if scan on start is not set. */
376 if(syscheck.scan_on_start == 0)
381 sleep(syscheck.tsleep * 10);
383 sleep(syscheck.tsleep * 10);
385 syscheck.scan_on_start = 1;
391 /* Sending scan start message */
394 merror("%s: INFO: Starting syscheck scan.", ARGV0);
395 send_rootcheck_msg("Starting syscheck scan.");
400 /* Checking for registry changes on Windows */
408 /* Set syscheck.fp to the begining of the file */
409 fseek(syscheck.fp, 0, SEEK_SET);
412 /* Checking for changes */
417 /* Sending scan ending message */
418 sleep(syscheck.tsleep + 20);
421 merror("%s: INFO: Ending syscheck scan.", ARGV0);
422 send_rootcheck_msg("Ending syscheck scan.");
427 /* Sending database completed message */
428 send_syscheck_msg(HC_SK_DB_COMPLETED);
429 debug2("%s: DEBUG: Sending database completed message.", ARGV0);
432 prev_time_sk = time(0);
437 if(syscheck.realtime && (syscheck.realtime->fd >= 0))
439 selecttime.tv_sec = SYSCHECK_WAIT;
440 selecttime.tv_usec = 0;
442 /* zero-out the fd_set */
445 FD_SET(syscheck.realtime->fd, &rfds);
447 run_now = select(syscheck.realtime->fd + 1, &rfds,
448 NULL, NULL, &selecttime);
451 merror("%s: ERROR: Select failed (for realtime fim).", ARGV0);
452 sleep(SYSCHECK_WAIT);
454 else if(run_now == 0)
458 else if (FD_ISSET (syscheck.realtime->fd, &rfds))
465 sleep(SYSCHECK_WAIT);
469 if(syscheck.realtime && (syscheck.realtime->fd >= 0))
471 run_now = WaitForSingleObjectEx(syscheck.realtime->evt, SYSCHECK_WAIT * 1000, TRUE);
472 if(run_now == WAIT_FAILED)
474 merror("%s: ERROR: WaitForSingleObjectEx failed (for realtime fim).", ARGV0);
475 sleep(SYSCHECK_WAIT);
484 sleep(SYSCHECK_WAIT);
489 sleep(SYSCHECK_WAIT);
496 * Read the database and check if the binary has changed
501 char alert_msg[912 +2];
502 char buf[MAX_LINE +2];
506 /* Cleaning buffer */
507 memset(buf, '\0', MAX_LINE +1);
508 memset(alert_msg, '\0', 912 +1);
509 memset(c_sum, '\0', 256 +1);
511 /* fgets garantee the null termination */
512 while(fgets(buf, MAX_LINE, syscheck.fp) != NULL)
514 /* Buf should be in the following format:
515 * header checksum file_name (checksum space filename)
517 char *n_file; /* file read from the db */
518 char *n_sum; /* md5sum read from the db */
519 char *tmp_c; /* tmp_char */
522 /* Avoiding wrong formats in the database. Alert about them */
523 if(buf[0] == '#' || buf[0] == ' ' || buf[0] == '\n')
525 merror("%s: Invalid entry in the integrity database: '%s'",
530 /* Adding a sleep in here -- avoid floods and extreme CPU usage
531 * on the client side -- speed not necessary
534 if(file_count >= (syscheck.sleep_after))
536 sleep(syscheck.tsleep);
541 /* Finding the file name */
542 n_file = strchr(buf, ' ');
545 merror("%s: Invalid entry in the integrity check database.",ARGV0);
549 /* Zeroing the ' ' and messing up with buf */
553 /* Setting n_file to the begining of the file name */
557 /* Removing the '\n' if present and setting it to \0 */
558 tmp_c = strchr(n_file,'\n');
565 /* Setting n_sum to the begining of buf */
569 /* Cleaning up c_sum */
570 memset(c_sum, '\0', 16);
574 /* If it returns < 0, we will already have alerted. */
575 if(c_read_file(n_file, n_sum, c_sum) < 0)
579 if(strcmp(c_sum, n_sum+6) != 0)
581 /* Sending the new checksum to the analysis server */
582 alert_msg[912 +1] = '\0';
583 snprintf(alert_msg, 912, "%s %s", c_sum, n_file);
584 send_syscheck_msg(alert_msg);
589 /* FILE OK if reached here */
595 * Read file information and return a pointer
598 int c_read_file(char *file_name, char *oldsum, char *newsum)
600 int size = 0, perm = 0, owner = 0, group = 0, md5sum = 0, sha1sum = 0;
609 strncpy(mf_sum, "xxx", 4);
610 strncpy(sf_sum, "xxx", 4);
614 /* Stating the file */
616 if(stat(file_name, &statbuf) < 0)
618 if(lstat(file_name, &statbuf) < 0)
621 char alert_msg[912 +2];
623 alert_msg[912 +1] = '\0';
624 snprintf(alert_msg, 912,"-1 %s", file_name);
625 send_syscheck_msg(alert_msg);
630 /* Getting the old sum values */
657 /* Generating new checksum */
659 if(S_ISREG(statbuf.st_mode))
661 if(S_ISREG(statbuf.st_mode))
664 if(sha1sum || md5sum)
666 /* Generating checksums of the file. */
667 if(OS_MD5_SHA1_File(file_name, mf_sum, sf_sum) < 0)
669 strncpy(sf_sum, "xxx", 4);
670 strncpy(mf_sum, "xxx", 4);
675 /* If it is a link, we need to check if the actual file is valid. */
676 else if(S_ISLNK(statbuf.st_mode))
678 struct stat statbuf_lnk;
679 if(stat(file_name, &statbuf_lnk) == 0)
681 if(S_ISREG(statbuf_lnk.st_mode))
683 if(sha1sum || md5sum)
685 /* Generating checksums of the file. */
686 if(OS_MD5_SHA1_File(file_name, mf_sum, sf_sum) < 0)
688 strncpy(sf_sum, "xxx", 4);
689 strncpy(mf_sum, "xxx", 4);
699 snprintf(newsum,255,"%d:%d:%d:%d:%s:%s",
700 size == 0?0:(int)statbuf.st_size,
701 perm == 0?0:(int)statbuf.st_mode,
702 owner== 0?0:(int)statbuf.st_uid,
703 group== 0?0:(int)statbuf.st_gid,
704 md5sum == 0?"xxx":mf_sum,
705 sha1sum == 0?"xxx":sf_sum);