1 /* Copyright (C) 2010 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
10 /* SCHED_BATCH is Linux specific and is only picked up with _GNU_SOURCE */
23 #include "os_crypto/md5/md5_op.h"
24 #include "os_crypto/sha1/sha1_op.h"
25 #include "os_crypto/md5_sha1/md5_sha1_op.h"
26 #include "rootcheck/rootcheck.h"
29 static void send_sk_db(void);
32 /* Send a message related to syscheck change/addition */
33 int send_syscheck_msg(const char *msg)
35 if (SendMSG(syscheck.queue, msg, SYSCHECK, SYSCHECK_MQ) < 0) {
36 merror(QUEUE_SEND, ARGV0);
38 if ((syscheck.queue = StartMQ(DEFAULTQPATH, WRITE)) < 0) {
39 ErrorExit(QUEUE_FATAL, ARGV0, DEFAULTQPATH);
42 /* Try to send it again */
43 SendMSG(syscheck.queue, msg, SYSCHECK, SYSCHECK_MQ);
48 /* Send a message related to rootcheck change/addition */
49 int send_rootcheck_msg(const char *msg)
51 if (SendMSG(syscheck.queue, msg, ROOTCHECK, ROOTCHECK_MQ) < 0) {
52 merror(QUEUE_SEND, ARGV0);
54 if ((syscheck.queue = StartMQ(DEFAULTQPATH, WRITE)) < 0) {
55 ErrorExit(QUEUE_FATAL, ARGV0, DEFAULTQPATH);
58 /* Try to send it again */
59 SendMSG(syscheck.queue, msg, ROOTCHECK, ROOTCHECK_MQ);
64 /* Send syscheck db to the server */
65 static void send_sk_db()
67 /* Send scan start message */
68 if (syscheck.dir[0]) {
69 merror("%s: INFO: Starting syscheck scan (forwarding database).", ARGV0);
70 send_rootcheck_msg("Starting syscheck scan.");
77 /* Send scan ending message */
78 sleep(syscheck.tsleep + 10);
80 if (syscheck.dir[0]) {
81 merror("%s: INFO: Ending syscheck scan (forwarding database).", ARGV0);
82 send_rootcheck_msg("Ending syscheck scan.");
86 /* Periodically run the integrity checker */
92 time_t prev_time_rk = 0;
93 time_t prev_time_sk = 0;
97 #ifdef INOTIFY_ENABLED
98 /* To be used by select */
99 struct timeval selecttime;
103 /* SCHED_BATCH forces the kernel to assume this is a cpu intensive
104 * process and gives it a lower priority. This keeps ossec-syscheckd
105 * from reducing the interactivity of an ssh session when checksumming
106 * large files. This is available in kernel flavors >= 2.6.16.
109 struct sched_param pri;
112 pri.sched_priority = 0;
113 status = sched_setscheduler(0, SCHED_BATCH, &pri);
115 debug1("%s: Setting SCHED_BATCH returned: %d", ARGV0, status);
119 verbose("%s: Starting daemon ..", ARGV0);
122 /* Some time to settle */
123 memset(curr_hour, '\0', 12);
124 sleep(syscheck.tsleep * 10);
126 /* If the scan time/day is set, reset the
127 * syscheck.time/rootcheck.time
129 if (syscheck.scan_time || syscheck.scan_day) {
130 /* At least once a week */
131 syscheck.time = 604800;
132 rootcheck.time = 604800;
135 /* Will create the db to store syscheck data */
136 if (syscheck.scan_on_start) {
137 sleep(syscheck.tsleep * 15);
141 /* Check for registry changes on Windows */
144 /* Send database completed message */
145 send_syscheck_msg(HC_SK_DB_COMPLETED);
146 debug2("%s: DEBUG: Sending database completed message.", ARGV0);
149 prev_time_rk = time(0);
152 /* Before entering in daemon mode itself */
153 prev_time_sk = time(0);
154 sleep(syscheck.tsleep * 10);
156 /* If the scan_time or scan_day is set, we need to handle the
157 * current day/time on the loop.
159 if (syscheck.scan_time || syscheck.scan_day) {
161 p = localtime(&curr_time);
163 /* Assign hour/min/sec values */
164 snprintf(curr_hour, 9, "%02d:%02d:%02d",
169 curr_day = p->tm_mday;
171 if (syscheck.scan_time && syscheck.scan_day) {
172 if ((OS_IsAfterTime(curr_hour, syscheck.scan_time)) &&
173 (OS_IsonDay(p->tm_wday, syscheck.scan_day))) {
176 } else if (syscheck.scan_time) {
177 if (OS_IsAfterTime(curr_hour, syscheck.scan_time)) {
180 } else if (syscheck.scan_day) {
181 if (OS_IsonDay(p->tm_wday, syscheck.scan_day)) {
187 /* Check every SYSCHECK_WAIT */
192 /* Check if syscheck should be restarted */
193 run_now = os_check_restart_syscheck();
195 /* Check if a day_time or scan_time is set */
196 if (syscheck.scan_time || syscheck.scan_day) {
197 p = localtime(&curr_time);
200 if (curr_day != p->tm_mday) {
202 curr_day = p->tm_mday;
205 /* Check for the time of the scan */
206 if (!day_scanned && syscheck.scan_time && syscheck.scan_day) {
207 /* Assign hour/min/sec values */
208 snprintf(curr_hour, 9, "%02d:%02d:%02d",
209 p->tm_hour, p->tm_min, p->tm_sec);
211 if ((OS_IsAfterTime(curr_hour, syscheck.scan_time)) &&
212 (OS_IsonDay(p->tm_wday, syscheck.scan_day))) {
216 } else if (!day_scanned && syscheck.scan_time) {
217 /* Assign hour/min/sec values */
218 snprintf(curr_hour, 9, "%02d:%02d:%02d",
219 p->tm_hour, p->tm_min, p->tm_sec);
221 if (OS_IsAfterTime(curr_hour, syscheck.scan_time)) {
225 } else if (!day_scanned && syscheck.scan_day) {
226 /* Check for the day of the scan */
227 if (OS_IsonDay(p->tm_wday, syscheck.scan_day)) {
234 /* If time elapsed is higher than the rootcheck_time, run it */
235 if (syscheck.rootcheck) {
236 if (((curr_time - prev_time_rk) > rootcheck.time) || run_now) {
238 prev_time_rk = time(0);
242 /* If time elapsed is higher than the syscheck time, run syscheck time */
243 if (((curr_time - prev_time_sk) > syscheck.time) || run_now) {
244 if (syscheck.scan_on_start == 0) {
245 /* Need to create the db if scan on start is not set */
246 sleep(syscheck.tsleep * 10);
248 sleep(syscheck.tsleep * 10);
250 syscheck.scan_on_start = 1;
252 /* Send scan start message */
253 if (syscheck.dir[0]) {
254 merror("%s: INFO: Starting syscheck scan.", ARGV0);
255 send_rootcheck_msg("Starting syscheck scan.");
258 /* Check for registry changes on Windows */
261 /* Check for changes */
265 /* Send scan ending message */
266 sleep(syscheck.tsleep + 20);
267 if (syscheck.dir[0]) {
268 merror("%s: INFO: Ending syscheck scan.", ARGV0);
269 send_rootcheck_msg("Ending syscheck scan.");
272 /* Send database completed message */
273 send_syscheck_msg(HC_SK_DB_COMPLETED);
274 debug2("%s: DEBUG: Sending database completed message.", ARGV0);
276 prev_time_sk = time(0);
279 #ifdef INOTIFY_ENABLED
280 if (syscheck.realtime && (syscheck.realtime->fd >= 0)) {
281 selecttime.tv_sec = SYSCHECK_WAIT;
282 selecttime.tv_usec = 0;
284 /* zero-out the fd_set */
286 FD_SET(syscheck.realtime->fd, &rfds);
288 run_now = select(syscheck.realtime->fd + 1, &rfds,
289 NULL, NULL, &selecttime);
291 merror("%s: ERROR: Select failed (for realtime fim).", ARGV0);
292 sleep(SYSCHECK_WAIT);
293 } else if (run_now == 0) {
295 } else if (FD_ISSET (syscheck.realtime->fd, &rfds)) {
299 sleep(SYSCHECK_WAIT);
302 if (syscheck.realtime && (syscheck.realtime->fd >= 0)) {
303 if (WaitForSingleObjectEx(syscheck.realtime->evt, SYSCHECK_WAIT * 1000, TRUE) == WAIT_FAILED) {
304 merror("%s: ERROR: WaitForSingleObjectEx failed (for realtime fim).", ARGV0);
305 sleep(SYSCHECK_WAIT);
310 sleep(SYSCHECK_WAIT);
313 sleep(SYSCHECK_WAIT);
318 /* Read file information and return a pointer to the checksum */
319 int c_read_file(const char *file_name, const char *oldsum, char *newsum)
321 int size = 0, perm = 0, owner = 0, group = 0, md5sum = 0, sha1sum = 0;
322 int return_error = 0;
328 strncpy(mf_sum, "xxx", 4);
329 strncpy(sf_sum, "xxx", 4);
333 return_error = (stat(file_name, &statbuf) < 0);
335 return_error = (lstat(file_name, &statbuf) < 0);
339 char alert_msg[PATH_MAX+4];
341 alert_msg[PATH_MAX + 3] = '\0';
342 snprintf(alert_msg, PATH_MAX + 4, "-1 %s", file_name);
343 send_syscheck_msg(alert_msg);
348 /* Get the old sum values */
351 if (oldsum[0] == '+') {
356 if (oldsum[1] == '+') {
361 if (oldsum[2] == '+') {
366 if (oldsum[3] == '+') {
371 if (oldsum[4] == '+') {
376 if (oldsum[5] == '+') {
378 } else if (oldsum[5] == 's') {
380 } else if (oldsum[5] == 'n') {
384 /* Generate new checksum */
385 if (S_ISREG(statbuf.st_mode))
387 if (sha1sum || md5sum) {
388 /* Generate checksums of the file */
389 if (OS_MD5_SHA1_File(file_name, syscheck.prefilter_cmd, mf_sum, sf_sum, OS_BINARY) < 0) {
390 strncpy(sf_sum, "xxx", 4);
391 strncpy(mf_sum, "xxx", 4);
396 /* If it is a link, check if the actual file is valid */
397 else if (S_ISLNK(statbuf.st_mode)) {
398 struct stat statbuf_lnk;
399 if (stat(file_name, &statbuf_lnk) == 0) {
400 if (S_ISREG(statbuf_lnk.st_mode)) {
401 if (sha1sum || md5sum) {
402 /* Generate checksums of the file */
403 if (OS_MD5_SHA1_File(file_name, syscheck.prefilter_cmd, mf_sum, sf_sum, OS_BINARY) < 0) {
404 strncpy(sf_sum, "xxx", 4);
405 strncpy(mf_sum, "xxx", 4);
416 snprintf(newsum, 255, "%ld:%d:%d:%d:%s:%s",
417 size == 0 ? 0 : (long)statbuf.st_size,
418 perm == 0 ? 0 : (int)statbuf.st_mode,
419 owner == 0 ? 0 : (int)statbuf.st_uid,
420 group == 0 ? 0 : (int)statbuf.st_gid,
421 md5sum == 0 ? "xxx" : mf_sum,
422 sha1sum == 0 ? "xxx" : sf_sum);
424 HANDLE hFile = CreateFile(file_name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
425 if (hFile == INVALID_HANDLE_VALUE) {
426 DWORD dwErrorCode = GetLastError();
427 char alert_msg[PATH_MAX+4];
428 alert_msg[PATH_MAX + 3] = '\0';
429 snprintf(alert_msg, PATH_MAX + 4, "CreateFile=%ld %s", dwErrorCode, file_name);
430 send_syscheck_msg(alert_msg);
434 PSID pSidOwner = NULL;
435 PSECURITY_DESCRIPTOR pSD = NULL;
436 DWORD dwRtnCode = GetSecurityInfo(hFile, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, &pSidOwner, NULL, NULL, NULL, &pSD);
437 if (dwRtnCode != ERROR_SUCCESS) {
438 DWORD dwErrorCode = GetLastError();
440 char alert_msg[PATH_MAX+4];
441 alert_msg[PATH_MAX + 3] = '\0';
442 snprintf(alert_msg, PATH_MAX + 4, "GetSecurityInfo=%ld %s", dwErrorCode, file_name);
443 send_syscheck_msg(alert_msg);
448 ConvertSidToStringSid(pSidOwner, &szSID);
451 st_uid = (char *) calloc( strlen(szSID) + 1, 1 );
452 memcpy( st_uid, szSID, strlen(szSID) );
457 snprintf(newsum, 255, "%ld:%d:%s:%d:%s:%s",
458 size == 0 ? 0 : (long)statbuf.st_size,
459 perm == 0 ? 0 : (int)statbuf.st_mode,
460 owner == 0 ? "0" : st_uid,
461 group == 0 ? 0 : (int)statbuf.st_gid,
462 md5sum == 0 ? "xxx" : mf_sum,
463 sha1sum == 0 ? "xxx" : sf_sum);