Imported Upstream version 2.7
[ossec-hids.git] / src / syscheckd / run_check.c
1 /* @(#) $Id: ./src/syscheckd/run_check.c, 2011/09/08 dcid Exp $
2  */
3
4 /* Copyright (C) 2010 Trend Micro Inc.
5  * All right reserved.
6  *
7  * This program is a free software; you can redistribute it
8  * and/or modify it under the terms of the GNU General Public
9  * License (version 2) as published by the FSF - Free Software
10  * Foundation
11  */
12
13
14 /* SCHED_BATCH is Linux specific and is only picked up with _GNU_SOURCE */
15 #ifdef __linux__
16         #define _GNU_SOURCE
17         #include <sched.h>
18 #endif
19
20 #include "shared.h"
21 #include "syscheck.h"
22 #include "os_crypto/md5/md5_op.h"
23 #include "os_crypto/sha1/sha1_op.h"
24 #include "os_crypto/md5_sha1/md5_sha1_op.h"
25
26 #include "rootcheck/rootcheck.h"
27
28
29 /** Prototypes **/
30 int c_read_file(char *file_name, char *oldsum, char *newsum);
31
32
33 /* Send syscheck message.
34  * Send a message related to syscheck change/addition.
35  */
36 int send_syscheck_msg(char *msg)
37 {
38     if(SendMSG(syscheck.queue, msg, SYSCHECK, SYSCHECK_MQ) < 0)
39     {
40         merror(QUEUE_SEND, ARGV0);
41
42         if((syscheck.queue = StartMQ(DEFAULTQPATH,WRITE)) < 0)
43         {
44             ErrorExit(QUEUE_FATAL, ARGV0, DEFAULTQPATH);
45         }
46
47         /* If we reach here, we can try to send it again */
48         SendMSG(syscheck.queue, msg, SYSCHECK, SYSCHECK_MQ);
49     }
50
51     return(0);
52 }
53
54
55
56 /* Send rootcheck message.
57  * Send a message related to rootcheck change/addition.
58  */
59 int send_rootcheck_msg(char *msg)
60 {
61     if(SendMSG(syscheck.queue, msg, ROOTCHECK, ROOTCHECK_MQ) < 0)
62     {
63         merror(QUEUE_SEND, ARGV0);
64
65         if((syscheck.queue = StartMQ(DEFAULTQPATH,WRITE)) < 0)
66         {
67             ErrorExit(QUEUE_FATAL, ARGV0, DEFAULTQPATH);
68         }
69
70         /* If we reach here, we can try to send it again */
71         SendMSG(syscheck.queue, msg, ROOTCHECK, ROOTCHECK_MQ);
72     }
73
74     return(0);
75 }
76
77
78 /* Sends syscheck db to the server.
79  */
80 void send_sk_db()
81 {
82     /* Sending scan start message */
83     if(syscheck.dir[0])
84     {
85         merror("%s: INFO: Starting syscheck scan (forwarding database).", ARGV0);
86         send_rootcheck_msg("Starting syscheck scan.");
87     }
88     else
89     {
90         sleep(syscheck.tsleep +10);
91         return;
92     }
93
94     create_db(1);
95
96
97     /* Sending scan ending message */
98     sleep(syscheck.tsleep +10);
99
100     if(syscheck.dir[0])
101     {
102         merror("%s: INFO: Ending syscheck scan (forwarding database).", ARGV0);
103         send_rootcheck_msg("Ending syscheck scan.");
104     }
105 }
106
107
108
109 /* start_daemon
110  * Run periodicaly the integrity checking
111  */
112 void start_daemon()
113 {
114     int day_scanned = 0;
115     int curr_day = 0;
116
117     time_t curr_time = 0;
118
119     time_t prev_time_rk = 0;
120     time_t prev_time_sk = 0;
121
122     char curr_hour[12];
123
124     struct tm *p;
125
126
127     /* To be used by select. */
128     #ifdef USEINOTIFY
129     struct timeval selecttime;
130     fd_set rfds;
131     #endif
132
133
134     /*
135      * SCHED_BATCH forces the kernel to assume this is a cpu intensive
136      * process
137      * and gives it a lower priority. This keeps ossec-syscheckd
138      * from reducing
139      * the interactity of an ssh session when checksumming large files.
140      * This is available in kernel flavors >= 2.6.16
141      */
142     #ifdef SCHED_BATCH
143     struct sched_param pri;
144     int status;
145
146     pri.sched_priority = 0;
147     status = sched_setscheduler(0, SCHED_BATCH, &pri);
148
149     debug1("%s: Setting SCHED_BATCH returned: %d", ARGV0, status);
150     #endif
151
152
153     #ifdef DEBUG
154     verbose("%s: Starting daemon ..",ARGV0);
155     #endif
156
157
158
159     /* Some time to settle */
160     memset(curr_hour, '\0', 12);
161     sleep(syscheck.tsleep * 10);
162
163
164
165     /* If the scan time/day is set, reset the
166      * syscheck.time/rootcheck.time
167      */
168     if(syscheck.scan_time || syscheck.scan_day)
169     {
170         /* At least once a week. */
171         syscheck.time = 604800;
172         rootcheck.time = 604800;
173     }
174
175
176     /* Will create the db to store syscheck data */
177     if(syscheck.scan_on_start)
178     {
179         sleep(syscheck.tsleep * 15);
180         send_sk_db();
181     }
182     else
183     {
184         prev_time_rk = time(0);
185     }
186
187
188
189     /* Before entering in daemon mode itself */
190     prev_time_sk = time(0);
191     sleep(syscheck.tsleep * 10);
192
193
194     /* If the scan_time or scan_day is set, we need to handle the
195      * current day/time on the loop.
196      */
197     if(syscheck.scan_time || syscheck.scan_day)
198     {
199         curr_time = time(0);
200         p = localtime(&curr_time);
201
202
203         /* Assign hour/min/sec values */
204         snprintf(curr_hour, 9, "%02d:%02d:%02d",
205                 p->tm_hour,
206                 p->tm_min,
207                 p->tm_sec);
208
209
210         curr_day = p->tm_mday;
211
212
213
214         if(syscheck.scan_time && syscheck.scan_day)
215         {
216             if((OS_IsAfterTime(curr_hour, syscheck.scan_time)) &&
217                (OS_IsonDay(p->tm_wday, syscheck.scan_day)))
218             {
219                 day_scanned = 1;
220             }
221         }
222
223         else if(syscheck.scan_time)
224         {
225             if(OS_IsAfterTime(curr_hour, syscheck.scan_time))
226             {
227                 day_scanned = 1;
228             }
229         }
230         else if(syscheck.scan_day)
231         {
232             if(OS_IsonDay(p->tm_wday, syscheck.scan_day))
233             {
234                 day_scanned = 1;
235             }
236         }
237     }
238
239
240     /* Checking every SYSCHECK_WAIT */
241     while(1)
242     {
243         int run_now = 0;
244         curr_time = time(0);
245
246
247         /* Checking if syscheck should be restarted, */
248         run_now = os_check_restart_syscheck();
249
250
251         /* Checking if a day_time or scan_time is set. */
252         if(syscheck.scan_time || syscheck.scan_day)
253         {
254             p = localtime(&curr_time);
255
256
257             /* Day changed. */
258             if(curr_day != p->tm_mday)
259             {
260                 day_scanned = 0;
261                 curr_day = p->tm_mday;
262             }
263
264
265             /* Checking for the time of the scan. */
266             if(!day_scanned && syscheck.scan_time && syscheck.scan_day)
267             {
268                 if((OS_IsAfterTime(curr_hour, syscheck.scan_time)) &&
269                    (OS_IsonDay(p->tm_wday, syscheck.scan_day)))
270                 {
271                     day_scanned = 1;
272                     run_now = 1;
273                 }
274             }
275
276             else if(!day_scanned && syscheck.scan_time)
277             {
278                 /* Assign hour/min/sec values */
279                 snprintf(curr_hour, 9, "%02d:%02d:%02d",
280                                     p->tm_hour, p->tm_min, p->tm_sec);
281
282                 if(OS_IsAfterTime(curr_hour, syscheck.scan_time))
283                 {
284                     run_now = 1;
285                     day_scanned = 1;
286                 }
287             }
288
289             /* Checking for the day of the scan. */
290             else if(!day_scanned && syscheck.scan_day)
291             {
292                 if(OS_IsonDay(p->tm_wday, syscheck.scan_day))
293                 {
294                     run_now = 1;
295                     day_scanned = 1;
296                 }
297             }
298         }
299
300
301
302         /* If time elapsed is higher than the rootcheck_time,
303          * run it.
304          */
305         if(syscheck.rootcheck)
306         {
307             if(((curr_time - prev_time_rk) > rootcheck.time) || run_now)
308             {
309                 run_rk_check();
310                 prev_time_rk = time(0);
311             }
312         }
313
314
315         /* If time elapsed is higher than the syscheck time,
316          * run syscheck time.
317          */
318         if(((curr_time - prev_time_sk) > syscheck.time) || run_now)
319         {
320             /* We need to create the db, if scan on start is not set. */
321             if(syscheck.scan_on_start == 0)
322             {
323                 sleep(syscheck.tsleep * 10);
324                 send_sk_db();
325                 sleep(syscheck.tsleep * 10);
326
327                 syscheck.scan_on_start = 1;
328             }
329
330
331             else
332             {
333                 /* Sending scan start message */
334                 if(syscheck.dir[0])
335                 {
336                     merror("%s: INFO: Starting syscheck scan.", ARGV0);
337                     send_rootcheck_msg("Starting syscheck scan.");
338                 }
339
340
341                 #ifdef WIN32
342                 /* Checking for registry changes on Windows */
343                 os_winreg_check();
344                 #endif
345
346
347                 /* Checking for changes */
348                 run_dbcheck();
349             }
350
351
352             /* Sending scan ending message */
353             sleep(syscheck.tsleep + 20);
354             if(syscheck.dir[0])
355             {
356                 merror("%s: INFO: Ending syscheck scan.", ARGV0);
357                 send_rootcheck_msg("Ending syscheck scan.");
358             }
359
360
361
362             /* Sending database completed message */
363             send_syscheck_msg(HC_SK_DB_COMPLETED);
364             debug2("%s: DEBUG: Sending database completed message.", ARGV0);
365
366
367             prev_time_sk = time(0);
368         }
369
370
371         #ifdef USEINOTIFY
372         if(syscheck.realtime && (syscheck.realtime->fd >= 0))
373         {
374             selecttime.tv_sec = SYSCHECK_WAIT;
375             selecttime.tv_usec = 0;
376
377             /* zero-out the fd_set */
378             FD_ZERO (&rfds);
379
380             FD_SET(syscheck.realtime->fd, &rfds);
381
382             run_now = select(syscheck.realtime->fd + 1, &rfds,
383                              NULL, NULL, &selecttime);
384             if(run_now < 0)
385             {
386                 merror("%s: ERROR: Select failed (for realtime fim).", ARGV0);
387                 sleep(SYSCHECK_WAIT);
388             }
389             else if(run_now == 0)
390             {
391                 /* Timeout. */
392             }
393             else if (FD_ISSET (syscheck.realtime->fd, &rfds))
394             {
395                 realtime_process();
396             }
397         }
398         else
399         {
400             sleep(SYSCHECK_WAIT);
401         }
402
403         #elif WIN32
404         if(syscheck.realtime && (syscheck.realtime->fd >= 0))
405         {
406             run_now = WaitForSingleObjectEx(syscheck.realtime->evt, SYSCHECK_WAIT * 1000, TRUE);
407             if(run_now == WAIT_FAILED)
408             {
409                 merror("%s: ERROR: WaitForSingleObjectEx failed (for realtime fim).", ARGV0);
410                 sleep(SYSCHECK_WAIT);
411             }
412             else
413             {
414                 sleep(1);
415             }
416         }
417         else
418         {
419             sleep(SYSCHECK_WAIT);
420         }
421
422
423         #else
424         sleep(SYSCHECK_WAIT);
425         #endif
426     }
427 }
428
429
430
431
432 /* c_read_file
433  * Read file information and return a pointer
434  * to the checksum
435  */
436 int c_read_file(char *file_name, char *oldsum, char *newsum)
437 {
438     int size = 0, perm = 0, owner = 0, group = 0, md5sum = 0, sha1sum = 0, seechanges = 0;
439
440     struct stat statbuf;
441
442     os_md5 mf_sum;
443     os_sha1 sf_sum;
444
445
446     /* Cleaning sums */
447     strncpy(mf_sum, "xxx", 4);
448     strncpy(sf_sum, "xxx", 4);
449
450
451
452     /* Stating the file */
453     #ifdef WIN32
454     if(stat(file_name, &statbuf) < 0)
455     #else
456     if(lstat(file_name, &statbuf) < 0)
457     #endif
458     {
459         char alert_msg[912 +2];
460
461         alert_msg[912 +1] = '\0';
462         snprintf(alert_msg, 912,"-1 %s", file_name);
463         send_syscheck_msg(alert_msg);
464
465         return(-1);
466     }
467
468     /* Getting the old sum values */
469
470     /* size */
471     if(oldsum[0] == '+')
472         size = 1;
473
474     /* perm */
475     if(oldsum[1] == '+')
476         perm = 1;
477
478     /* owner */
479     if(oldsum[2] == '+')
480         owner = 1;
481
482     /* group */
483     if(oldsum[3] == '+')
484         group = 1;
485
486     /* md5 sum */
487     if(oldsum[4] == '+')
488         md5sum = 1;
489
490     /* sha1 sum */
491     if(oldsum[5] == '+')
492         sha1sum = 1;
493
494     else if(oldsum[5] == 's')
495     {
496         sha1sum = 1;
497         seechanges = 1;
498     }
499     else if(oldsum[5] == 'n')
500     {
501         sha1sum = 0;
502         seechanges = 1;
503     }
504
505
506     /* Generating new checksum */
507     #ifdef WIN32
508     if(S_ISREG(statbuf.st_mode))
509     #else
510     if(S_ISREG(statbuf.st_mode))
511     #endif
512     {
513         if(sha1sum || md5sum)
514         {
515             /* Generating checksums of the file. */
516             if(OS_MD5_SHA1_File(file_name, syscheck.prefilter_cmd, mf_sum, sf_sum) < 0)
517             {
518                 strncpy(sf_sum, "xxx", 4);
519                 strncpy(mf_sum, "xxx", 4);
520             }
521         }
522     }
523     #ifndef WIN32
524     /* If it is a link, we need to check if the actual file is valid. */
525     else if(S_ISLNK(statbuf.st_mode))
526     {
527         struct stat statbuf_lnk;
528         if(stat(file_name, &statbuf_lnk) == 0)
529         {
530             if(S_ISREG(statbuf_lnk.st_mode))
531             {
532                 if(sha1sum || md5sum)
533                 {
534                     /* Generating checksums of the file. */
535                     if(OS_MD5_SHA1_File(file_name, syscheck.prefilter_cmd, mf_sum, sf_sum) < 0)
536                     {
537                         strncpy(sf_sum, "xxx", 4);
538                         strncpy(mf_sum, "xxx", 4);
539                     }
540                 }
541             }
542         }
543     }
544     #endif
545
546     newsum[0] = '\0';
547     newsum[255] = '\0';
548     snprintf(newsum,255,"%d:%d:%d:%d:%s:%s",
549             size == 0?0:(int)statbuf.st_size,
550             perm == 0?0:(int)statbuf.st_mode,
551             owner== 0?0:(int)statbuf.st_uid,
552             group== 0?0:(int)statbuf.st_gid,
553             md5sum   == 0?"xxx":mf_sum,
554             sha1sum  == 0?"xxx":sf_sum);
555
556     return(0);
557 }
558
559 /* EOF */