Imported Upstream version 2.5.1
[ossec-hids.git] / src / syscheckd / run_realtime.c
1 /* @(#) $Id$ */
2
3 /* Copyright (C) 2009 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 #include <stdio.h>
14 #include <string.h>
15 #include <stdlib.h>
16 #include <sys/types.h>
17 #include <unistd.h>
18 #include <limits.h>
19 #include <errno.h>
20
21
22 #ifdef WIN32
23 #include <windows.h>
24 #include <winsock.h>
25 #include <io.h>
26
27 #define sleep(x) Sleep(x * 1000)
28 #define os_calloc(x,y,z) (z = calloc(x,y))?(void)1:ErrorExit(MEM_ERROR, ARGV0)
29 #define os_strdup(x,y) (y = strdup(x))?(void)1:ErrorExit(MEM_ERROR, ARGV0)
30 #endif
31
32
33 #include "hash_op.h"
34 #include "debug_op.h"
35 #include "syscheck.h"
36 #include "error_messages/error_messages.h"
37
38
39 #ifdef USEINOTIFY
40 #include <sys/inotify.h>
41 #define OS_SIZE_6144    6144
42 #define OS_MAXSTR       OS_SIZE_6144    /* Size for logs, sockets, etc */
43 #else
44 #include "shared.h"
45 #endif
46
47
48
49 /** Global functions for all realtime options. **/
50 int c_read_file(char *file_name, char *oldsum, char *newsum);
51
52
53 /* Checking sum of the realtime file being monitored. */
54 int realtime_checksumfile(char *file_name)
55 {
56     char *buf;
57
58     buf = OSHash_Get(syscheck.fp, file_name);
59     if(buf != NULL)
60     {
61         char c_sum[256 +2];
62
63         c_sum[0] = '\0';
64         c_sum[255] = '\0';
65
66
67          /* If it returns < 0, we will already have alerted. */
68          if(c_read_file(file_name, buf, c_sum) < 0)
69          {
70              return(0);
71          }
72
73
74          if(strcmp(c_sum, buf+6) != 0)
75          {
76              char *fullalert = NULL;
77              char alert_msg[OS_MAXSTR +1];
78              alert_msg[OS_MAXSTR] = '\0';
79              if(buf[5] == 's' || buf[5] == 'n')
80              {
81                  fullalert = seechanges_addfile(file_name);
82                  if(fullalert)
83                  {
84                     snprintf(alert_msg, OS_MAXSTR, "%s %s\n%s", c_sum, file_name, fullalert);
85                     free(fullalert);
86                     fullalert = NULL;
87                  }
88                  else
89                  {
90                      snprintf(alert_msg, 912, "%s %s", c_sum, file_name);
91                  }
92              }
93              else
94              {
95                  snprintf(alert_msg, 912, "%s %s", c_sum, file_name);
96              }
97              send_syscheck_msg(alert_msg);
98
99              return(1);
100          }
101
102          return(0);
103
104     }
105
106     return(0);
107 }
108
109
110
111
112 #ifdef USEINOTIFY
113 #include <sys/inotify.h>
114
115
116 #define REALTIME_MONITOR_FLAGS  IN_MODIFY|IN_ATTRIB|IN_MOVED_TO|IN_DELETE|IN_MOVED_FROM
117 #define REALTIME_EVENT_SIZE     (sizeof (struct inotify_event))
118 #define REALTIME_EVENT_BUFFER   (2048 * (REALTIME_EVENT_SIZE + 16))
119
120
121
122 /* Starts real time monitoring using inotify. */
123 int realtime_start()
124 {
125     verbose("%s: INFO: Initializing real time file monitoring (not started).", ARGV0);
126
127     syscheck.realtime = calloc(1, sizeof(rtfim));
128     if(syscheck.realtime == NULL)
129     {
130         ErrorExit(MEM_ERROR, ARGV0);
131     }
132     syscheck.realtime->dirtb = (void *)OSHash_Create();
133     syscheck.realtime->fd = -1;
134
135     #ifdef USEINOTIFY
136     syscheck.realtime->fd = inotify_init();
137     if(syscheck.realtime->fd < 0)
138     {
139         merror("%s: ERROR: Unable to initialize inotify.", ARGV0);
140         return(-1);
141     }
142     #endif    
143
144     return(1);
145 }
146
147
148
149 /* Adds a directory to real time checking. */
150 int realtime_adddir(char *dir)
151 {
152     if(!syscheck.realtime)
153     {
154         realtime_start();
155     }
156
157
158     /* Checking if it is ready to use. */
159     if(syscheck.realtime->fd < 0)
160     {
161         return(-1);
162     }
163     else
164     {
165         int wd = 0;
166
167         wd = inotify_add_watch(syscheck.realtime->fd,
168                                dir,
169                                REALTIME_MONITOR_FLAGS); 
170         if(wd < 0)
171         {
172             merror("%s: ERROR: Unable to add directory to real time " 
173                    "monitoring: '%s'. %d %d", ARGV0, dir, wd, errno);
174         }
175         else
176         {
177             char wdchar[32 +1];
178             wdchar[32] = '\0';
179             snprintf(wdchar, 32, "%d", wd);
180
181             /* Entry not present. */
182             if(!OSHash_Get(syscheck.realtime->dirtb, wdchar))
183             {
184                 char *ndir;
185
186                 ndir = strdup(dir);
187                 if(ndir == NULL)
188                 {
189                     ErrorExit("%s: ERROR: Out of memory. Exiting.", ARGV0);
190                 }
191
192                 OSHash_Add(syscheck.realtime->dirtb, strdup(wdchar), ndir);
193                 debug1("%s: DEBUG: Directory added for real time monitoring: "
194                        "'%s'.", ARGV0, ndir);
195             }
196         }
197     }
198
199     return(1);
200 }
201
202
203 /* Process events in the real time queue. */
204 int realtime_process()
205 {
206     int len, i = 0;
207     char buf[REALTIME_EVENT_BUFFER +1];
208     struct inotify_event *event;
209
210     buf[REALTIME_EVENT_BUFFER] = '\0';
211
212
213     len = read(syscheck.realtime->fd, buf, REALTIME_EVENT_BUFFER);
214     if (len < 0) 
215     {
216         merror("%s: ERROR: Unable to read from real time buffer.", ARGV0);
217     } 
218     else if (len > 0)
219     {
220         while (i < len) 
221         {
222             event = (struct inotify_event *) &buf[i];
223
224             if(event->len)
225             {
226                 char wdchar[32 +1];
227                 char final_name[MAX_LINE +1];
228
229                 wdchar[32] = '\0';
230                 final_name[MAX_LINE] = '\0';
231
232                 snprintf(wdchar, 32, "%d", event->wd);
233
234                 snprintf(final_name, MAX_LINE, "%s/%s", 
235                          (char *)OSHash_Get(syscheck.realtime->dirtb, wdchar),
236                          event->name);
237                 realtime_checksumfile(final_name);
238             }
239
240             i += REALTIME_EVENT_SIZE + event->len;
241         }
242     }
243
244     return(0);
245 }
246
247
248
249 #elif WIN32
250 typedef struct _win32rtfim
251 {
252     HANDLE h;
253     OVERLAPPED overlap;
254
255     char *dir;
256     TCHAR buffer[12288];
257 }win32rtfim;
258
259 int realtime_win32read(win32rtfim *rtlocald);
260
261 void CALLBACK RTCallBack(DWORD dwerror, DWORD dwBytes, LPOVERLAPPED overlap)
262 {
263     int lcount;
264     size_t offset = 0;
265
266     char *ptfile;
267     char wdchar[32 +1];
268     char final_path[MAX_LINE +1];
269
270     win32rtfim *rtlocald;
271
272     PFILE_NOTIFY_INFORMATION pinfo;
273     TCHAR finalfile[MAX_PATH];
274
275     if(dwBytes == 0)
276     {
277         merror("%s: ERROR: real time call back called, but 0 bytes.", ARGV0);
278         return;
279     }
280
281     if(dwerror != ERROR_SUCCESS)
282     {
283         merror("%s: ERROR: real time call back called, but error is set.", 
284                ARGV0);
285         return;
286     }
287
288
289     /* Getting hash to parse the data. */
290     wdchar[32] = '\0';
291     snprintf(wdchar, 32, "%d", (int)overlap->Offset);
292     rtlocald = OSHash_Get(syscheck.realtime->dirtb, wdchar);
293     if(rtlocald == NULL)
294     {
295         merror("%s: ERROR: real time call back called, but hash is empty.", 
296                ARGV0);
297         return;
298     }
299
300         
301
302     do
303     {
304         pinfo = (PFILE_NOTIFY_INFORMATION) &rtlocald->buffer[offset];
305         offset += pinfo->NextEntryOffset;
306
307         lcount = WideCharToMultiByte(CP_ACP, 0, pinfo->FileName,
308                                      pinfo->FileNameLength / sizeof(WCHAR),
309                                      finalfile, MAX_PATH - 1, NULL, NULL);
310         finalfile[lcount] = TEXT('\0');
311
312
313         /* Change forward slashes to backslashes on finalfile. */
314         ptfile = strchr(finalfile, '\\');
315         while(ptfile)
316         {
317             *ptfile = '/';
318             ptfile++;
319
320             ptfile = strchr(ptfile, '\\');
321         }
322
323         final_path[MAX_LINE] = '\0';
324         snprintf(final_path, MAX_LINE, "%s/%s", rtlocald->dir, finalfile);
325
326
327         /* Checking the change. */
328         realtime_checksumfile(final_path);
329
330
331         /*
332         if(pinfo->Action == FILE_ACTION_ADDED)
333         else if(pinfo->Action == FILE_ACTION_REMOVED)
334         else if(pinfo->Action == FILE_ACTION_MODIFIED)
335         else if(pinfo->Action == FILE_ACTION_RENAMED_OLD_NAME)
336         else if(pinfo->Action == FILE_ACTION_RENAMED_NEW_NAME)
337         else
338         */
339
340     }while(pinfo->NextEntryOffset != 0);
341
342
343     realtime_win32read(rtlocald);
344
345
346     return;
347 }
348
349
350
351 int realtime_start()
352 {
353     verbose("%s: INFO: Initializing real time file monitoring (not started).", ARGV0);
354
355     os_calloc(1, sizeof(rtfim), syscheck.realtime);
356     syscheck.realtime->dirtb = (void *)OSHash_Create();
357     syscheck.realtime->fd = -1;
358     syscheck.realtime->evt = CreateEvent(NULL, TRUE, FALSE, NULL);
359     return(0);
360 }
361
362 int realtime_win32read(win32rtfim *rtlocald)
363 {
364     int rc;
365
366     rc = ReadDirectoryChangesW(rtlocald->h,
367                                rtlocald->buffer,
368                                sizeof(rtlocald->buffer) / sizeof(TCHAR),
369                                TRUE,
370                                FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_DIR_NAME|FILE_NOTIFY_CHANGE_SIZE|FILE_NOTIFY_CHANGE_LAST_WRITE,
371                                0,
372                                &rtlocald->overlap, 
373                                RTCallBack);
374     if(rc == 0)
375     {
376         merror("%s: ERROR: Unable to set directory for monitoring: %s", 
377                ARGV0, rtlocald->dir);
378         sleep(2);
379     }
380
381     return(0);
382 }
383
384 int realtime_adddir(char *dir)
385 {
386     char wdchar[32 +1];
387     win32rtfim *rtlocald;
388
389
390     if(!syscheck.realtime)
391     {
392         realtime_start();
393     }
394
395
396     /* Maximum limit for realtime on Windows. */
397     if(syscheck.realtime->fd > 256)
398     {
399         merror("%s: ERROR: Unable to add directory to real time "
400                "monitoring: '%s' - Maximum size permitted.", ARGV0, dir);
401         return(0);
402     }
403
404
405     os_calloc(1, sizeof(win32rtfim), rtlocald);
406     
407
408     rtlocald->h = CreateFile(dir,
409                              FILE_LIST_DIRECTORY,
410                              FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE,
411                              NULL,
412                              OPEN_EXISTING,
413                              FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED,
414                              NULL);
415
416
417     if(rtlocald->h == INVALID_HANDLE_VALUE || 
418        rtlocald->h == NULL) 
419     {
420         free(rtlocald);
421         rtlocald = NULL;
422         merror("%s: ERROR: Unable to add directory to real time "
423                "monitoring: '%s'.", ARGV0, dir);
424         return(0);
425     }
426
427     rtlocald->overlap.Offset = ++syscheck.realtime->fd;
428
429
430
431     /* Setting key for hash. */
432     wdchar[32] = '\0';
433     snprintf(wdchar, 32, "%d", (int)rtlocald->overlap.Offset);
434
435
436     if(OSHash_Get(syscheck.realtime->dirtb, wdchar))
437     {
438         merror("%s: ERROR: Entry already in the real time hash: %s", 
439                ARGV0, wdchar);
440         CloseHandle(rtlocald->overlap.hEvent);
441         free(rtlocald);
442         rtlocald = NULL;
443         return(0);
444     }
445
446
447     /* Adding final elements to the hash. */
448     os_strdup(dir, rtlocald->dir);
449
450     OSHash_Add(syscheck.realtime->dirtb, strdup(wdchar), rtlocald);
451
452
453     /* Adding directory to be monitored. */
454     realtime_win32read(rtlocald);
455
456
457     return(1);
458 }
459
460
461
462
463
464 #else
465 int realtime_start()
466 {
467     verbose("%s: ERROR: Unable to initalize real time file monitoring.", ARGV0);
468     return(0);
469 }
470
471 int realtime_adddir(char *dir)
472 {
473     return(0);
474 }
475
476 int realtime_process()
477 {
478     return(0);
479 }
480
481 #endif
482 /* EOF */