new upstream release (3.3.0); modify package compatibility for Stretch
[ossec-hids.git] / src / syscheckd / run_check.c
1 /* Copyright (C) 2010 Trend Micro Inc.
2  * All right 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 /* SCHED_BATCH is Linux specific and is only picked up with _GNU_SOURCE */
11 #ifdef __linux__
12 #define _GNU_SOURCE
13 #include <sched.h>
14 #endif
15 #ifdef WIN32
16 #include <winsock2.h>
17 #include <aclapi.h>
18 #include <sddl.h>
19 #endif
20
21 #include "shared.h"
22 #include "syscheck.h"
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"
27
28 /* Prototypes */
29 static void send_sk_db(void);
30
31
32 /* Send a message related to syscheck change/addition */
33 int send_syscheck_msg(const char *msg)
34 {
35     if (SendMSG(syscheck.queue, msg, SYSCHECK, SYSCHECK_MQ) < 0) {
36         merror(QUEUE_SEND, ARGV0);
37
38         if ((syscheck.queue = StartMQ(DEFAULTQPATH, WRITE)) < 0) {
39             ErrorExit(QUEUE_FATAL, ARGV0, DEFAULTQPATH);
40         }
41
42         /* Try to send it again */
43         SendMSG(syscheck.queue, msg, SYSCHECK, SYSCHECK_MQ);
44     }
45     return (0);
46 }
47
48 /* Send a message related to rootcheck change/addition */
49 int send_rootcheck_msg(const char *msg)
50 {
51     if (SendMSG(syscheck.queue, msg, ROOTCHECK, ROOTCHECK_MQ) < 0) {
52         merror(QUEUE_SEND, ARGV0);
53
54         if ((syscheck.queue = StartMQ(DEFAULTQPATH, WRITE)) < 0) {
55             ErrorExit(QUEUE_FATAL, ARGV0, DEFAULTQPATH);
56         }
57
58         /* Try to send it again */
59         SendMSG(syscheck.queue, msg, ROOTCHECK, ROOTCHECK_MQ);
60     }
61     return (0);
62 }
63
64 /* Send syscheck db to the server */
65 static void send_sk_db()
66 {
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.");
71     } else {
72         return;
73     }
74
75     create_db();
76
77     /* Send scan ending message */
78     sleep(syscheck.tsleep + 10);
79
80     if (syscheck.dir[0]) {
81         merror("%s: INFO: Ending syscheck scan (forwarding database).", ARGV0);
82         send_rootcheck_msg("Ending syscheck scan.");
83     }
84 }
85
86 /* Periodically run the integrity checker */
87 void start_daemon()
88 {
89     int day_scanned = 0;
90     int curr_day = 0;
91     time_t curr_time = 0;
92     time_t prev_time_rk = 0;
93     time_t prev_time_sk = 0;
94     char curr_hour[12];
95     struct tm *p;
96
97 #ifdef INOTIFY_ENABLED
98     /* To be used by select */
99     struct timeval selecttime;
100     fd_set rfds;
101 #endif
102
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.
107      */
108 #ifdef SCHED_BATCH
109     struct sched_param pri;
110     int status;
111
112     pri.sched_priority = 0;
113     status = sched_setscheduler(0, SCHED_BATCH, &pri);
114
115     debug1("%s: Setting SCHED_BATCH returned: %d", ARGV0, status);
116 #endif
117
118 #ifdef DEBUG
119     verbose("%s: Starting daemon ..", ARGV0);
120 #endif
121
122     /* Some time to settle */
123     memset(curr_hour, '\0', 12);
124     sleep(syscheck.tsleep * 10);
125
126     /* If the scan time/day is set, reset the
127      * syscheck.time/rootcheck.time
128      */
129     if (syscheck.scan_time || syscheck.scan_day) {
130         /* At least once a week */
131         syscheck.time = 604800;
132         rootcheck.time = 604800;
133     }
134
135     /* Will create the db to store syscheck data */
136     if (syscheck.scan_on_start) {
137         sleep(syscheck.tsleep * 15);
138         send_sk_db();
139
140 #ifdef WIN32
141         /* Check for registry changes on Windows */
142         os_winreg_check();
143 #endif
144         /* Send database completed message */
145         send_syscheck_msg(HC_SK_DB_COMPLETED);
146         debug2("%s: DEBUG: Sending database completed message.", ARGV0);
147
148     } else {
149         prev_time_rk = time(0);
150     }
151
152     /* Before entering in daemon mode itself */
153     prev_time_sk = time(0);
154     sleep(syscheck.tsleep * 10);
155
156     /* If the scan_time or scan_day is set, we need to handle the
157      * current day/time on the loop.
158      */
159     if (syscheck.scan_time || syscheck.scan_day) {
160         curr_time = time(0);
161         p = localtime(&curr_time);
162
163         /* Assign hour/min/sec values */
164         snprintf(curr_hour, 9, "%02d:%02d:%02d",
165                  p->tm_hour,
166                  p->tm_min,
167                  p->tm_sec);
168
169         curr_day = p->tm_mday;
170
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))) {
174                 day_scanned = 1;
175             }
176         } else if (syscheck.scan_time) {
177             if (OS_IsAfterTime(curr_hour, syscheck.scan_time)) {
178                 day_scanned = 1;
179             }
180         } else if (syscheck.scan_day) {
181             if (OS_IsonDay(p->tm_wday, syscheck.scan_day)) {
182                 day_scanned = 1;
183             }
184         }
185     }
186
187     /* Check every SYSCHECK_WAIT */
188     while (1) {
189         int run_now = 0;
190         curr_time = time(0);
191
192         /* Check if syscheck should be restarted */
193         run_now = os_check_restart_syscheck();
194
195         /* Check if a day_time or scan_time is set */
196         if (syscheck.scan_time || syscheck.scan_day) {
197             p = localtime(&curr_time);
198
199             /* Day changed */
200             if (curr_day != p->tm_mday) {
201                 day_scanned = 0;
202                 curr_day = p->tm_mday;
203             }
204
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);
210
211                 if ((OS_IsAfterTime(curr_hour, syscheck.scan_time)) &&
212                         (OS_IsonDay(p->tm_wday, syscheck.scan_day))) {
213                     day_scanned = 1;
214                     run_now = 1;
215                 }
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);
220
221                 if (OS_IsAfterTime(curr_hour, syscheck.scan_time)) {
222                     run_now = 1;
223                     day_scanned = 1;
224                 }
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)) {
228                     run_now = 1;
229                     day_scanned = 1;
230                 }
231             }
232         }
233
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) {
237                 run_rk_check();
238                 prev_time_rk = time(0);
239             }
240         }
241
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);
247                 send_sk_db();
248                 sleep(syscheck.tsleep * 10);
249
250                 syscheck.scan_on_start = 1;
251             } else {
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.");
256                 }
257 #ifdef WIN32
258                 /* Check for registry changes on Windows */
259                 os_winreg_check();
260 #endif
261                 /* Check for changes */
262                 run_dbcheck();
263             }
264
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.");
270             }
271
272             /* Send database completed message */
273             send_syscheck_msg(HC_SK_DB_COMPLETED);
274             debug2("%s: DEBUG: Sending database completed message.", ARGV0);
275
276             prev_time_sk = time(0);
277         }
278
279 #ifdef INOTIFY_ENABLED
280         if (syscheck.realtime && (syscheck.realtime->fd >= 0)) {
281             selecttime.tv_sec = SYSCHECK_WAIT;
282             selecttime.tv_usec = 0;
283
284             /* zero-out the fd_set */
285             FD_ZERO (&rfds);
286             FD_SET(syscheck.realtime->fd, &rfds);
287
288             run_now = select(syscheck.realtime->fd + 1, &rfds,
289                              NULL, NULL, &selecttime);
290             if (run_now < 0) {
291                 merror("%s: ERROR: Select failed (for realtime fim).", ARGV0);
292                 sleep(SYSCHECK_WAIT);
293             } else if (run_now == 0) {
294                 /* Timeout */
295             } else if (FD_ISSET (syscheck.realtime->fd, &rfds)) {
296                 realtime_process();
297             }
298         } else {
299             sleep(SYSCHECK_WAIT);
300         }
301 #elif defined(WIN32)
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);
306             } else {
307                 sleep(1);
308             }
309         } else {
310             sleep(SYSCHECK_WAIT);
311         }
312 #else
313         sleep(SYSCHECK_WAIT);
314 #endif
315     }
316 }
317
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)
320 {
321     int size = 0, perm = 0, owner = 0, group = 0, md5sum = 0, sha1sum = 0;
322     int return_error = 0;
323     struct stat statbuf;
324     os_md5 mf_sum;
325     os_sha1 sf_sum;
326
327     /* Clean sums */
328     strncpy(mf_sum, "xxx", 4);
329     strncpy(sf_sum, "xxx", 4);
330
331     /* Stat the file */
332 #ifdef WIN32
333     return_error = (stat(file_name, &statbuf) < 0);
334 #else
335     return_error = (lstat(file_name, &statbuf) < 0);
336 #endif
337     if (return_error)
338     {
339         char alert_msg[PATH_MAX+4];
340
341         alert_msg[PATH_MAX + 3] = '\0';
342         snprintf(alert_msg, PATH_MAX + 4, "-1 %s", file_name);
343         send_syscheck_msg(alert_msg);
344
345         return (-1);
346     }
347
348     /* Get the old sum values */
349
350     /* size */
351     if (oldsum[0] == '+') {
352         size = 1;
353     }
354
355     /* perm */
356     if (oldsum[1] == '+') {
357         perm = 1;
358     }
359
360     /* owner */
361     if (oldsum[2] == '+') {
362         owner = 1;
363     }
364
365     /* group */
366     if (oldsum[3] == '+') {
367         group = 1;
368     }
369
370     /* md5 sum */
371     if (oldsum[4] == '+') {
372         md5sum = 1;
373     }
374
375     /* sha1 sum */
376     if (oldsum[5] == '+') {
377         sha1sum = 1;
378     } else if (oldsum[5] == 's') {
379         sha1sum = 1;
380     } else if (oldsum[5] == 'n') {
381         sha1sum = 0;
382     }
383
384     /* Generate new checksum */
385     if (S_ISREG(statbuf.st_mode))
386     {
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);
392             }
393         }
394     }
395 #ifndef WIN32
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);
406                     }
407                 }
408             }
409         }
410     }
411 #endif
412
413     newsum[0] = '\0';
414     newsum[255] = '\0';
415 #ifndef WIN32
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);
423 #else
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);
431         return -1;
432     }
433
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();
439         CloseHandle(hFile);
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);
444         return -1;
445     }
446
447     LPSTR szSID = NULL;
448     ConvertSidToStringSid(pSidOwner, &szSID);
449     char* st_uid = NULL;
450     if( szSID ) {
451       st_uid = (char *) calloc( strlen(szSID) + 1, 1 );
452       memcpy( st_uid, szSID, strlen(szSID) );
453     }
454     LocalFree(szSID);
455     CloseHandle(hFile);
456
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);
464
465     free(st_uid);
466 #endif
467
468     return (0);
469 }