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