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