source: trunk/src/sh_fInotify.c@ 369

Last change on this file since 369 was 368, checked in by katerina, 13 years ago

Add missing files for ticket #265

File size: 12.2 KB
RevLine 
[368]1/* SAMHAIN file system integrity testing */
2/* Copyright (C) 2011 Rainer Wichmann */
3/* */
4/* This program is free software; you can redistribute it */
5/* and/or modify */
6/* it under the terms of the GNU General Public License as */
7/* published by */
8/* the Free Software Foundation; either version 2 of the License, or */
9/* (at your option) any later version. */
10/* */
11/* This program is distributed in the hope that it will be useful, */
12/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
13/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
14/* GNU General Public License for more details. */
15/* */
16/* You should have received a copy of the GNU General Public License */
17/* along with this program; if not, write to the Free Software */
18/* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
19
20/***************************************************************************
21 *
22 * This file provides a module for samhain to use inotify for file checking.
23 *
24 */
25
26#include "config_xor.h"
27
28#if (defined(SH_WITH_CLIENT) || defined(SH_STANDALONE))
29
30#include "samhain.h"
31#include "sh_utils.h"
32#include "sh_modules.h"
33#include "sh_pthread.h"
34#include "sh_inotify.h"
35#include "sh_unix.h"
36#include "sh_hash.h"
37#include "sh_files.h"
38#include "sh_ignore.h"
39
40#define FIL__ _("sh_fInotify.c")
41
42sh_watches sh_file_watches = SH_INOTIFY_INITIALIZER;
43
44static sh_watches sh_file_missing = SH_INOTIFY_INITIALIZER;
45
46#if defined(HAVE_SYS_INOTIFY_H)
47
48#include <sys/inotify.h>
49
50/* --- Configuration ------- */
51
52static int ShfInotifyActive = S_FALSE;
53
54static unsigned long ShfInotifyWatches = 0;
55
56static int sh_fInotify_active(const char *s)
57{
58 int value;
59
60 SL_ENTER(_("sh_fInotify_active"));
61 value = sh_util_flagval(s, &ShfInotifyActive);
62 if (value == 0 && ShfInotifyActive != S_FALSE)
63 {
64 sh.flag.inotify |= SH_INOTIFY_USE;
65 sh.flag.inotify |= SH_INOTIFY_DOSCAN;
66 sh.flag.inotify |= SH_INOTIFY_NEEDINIT;
67 }
68 if (value == 0 && ShfInotifyActive == S_FALSE)
69 {
70 sh.flag.inotify = 0;
71 }
72 SL_RETURN((value), _("sh_fInotify_active"));
73}
74
75static int sh_fInotify_watches(const char *s)
76{
77 int retval = -1;
78 char * foo;
79 unsigned long value;
80
81 SL_ENTER(_("sh_fInotify_watches"));
82
83 value = strtoul(s, &foo, 0);
84 if (*foo == '\0')
85 {
86 ShfInotifyWatches = (value > 2147483647) ? 2147483647 /* MAX_INT_32 */: value;
87 retval = 0;
88 }
89 SL_RETURN((retval), _("sh_fInotify_watches"));
90}
91
92
93sh_rconf sh_fInotify_table[] = {
94 {
95 N_("inotifyactive"),
96 sh_fInotify_active,
97 },
98 {
99 N_("inotifywatches"),
100 sh_fInotify_watches,
101 },
102 {
103 NULL,
104 NULL
105 }
106};
107
108/* --- End Configuration --- */
109
110static int sh_fInotify_init_internal(void);
111static int sh_fInotify_process(struct inotify_event * event);
112static int sh_fInotify_report(struct inotify_event * event, char * filename,
113 int class, unsigned long check_mask);
114
115int sh_fInotify_init(struct mod_type * arg)
116{
117#ifndef HAVE_PTHREAD
118 (void) arg;
119 return SH_MOD_FAILED;
120#else
121
122 if (ShfInotifyActive == S_FALSE)
123 return SH_MOD_FAILED;
124
125 if (sh.flag.checkSum == SH_CHECK_INIT)
126 return SH_MOD_FAILED;
127
128 if (arg != NULL && arg->initval < 0 &&
129 (sh.flag.isdaemon == S_TRUE || sh.flag.loop == S_TRUE))
130 {
131 /* Init from main thread */
132 sh.flag.inotify |= SH_INOTIFY_DOSCAN;
133 sh.flag.inotify |= SH_INOTIFY_NEEDINIT;
134
135 if (0 == sh_pthread_create(sh_threaded_module_run, (void *)arg))
136 {
137 return SH_MOD_THREAD;
138 }
139 else
140 {
141 sh.flag.inotify = 0;
142 return SH_MOD_FAILED;
143 }
144 }
145 else if (arg != NULL && arg->initval == SH_MOD_THREAD &&
146 (sh.flag.isdaemon == S_TRUE || sh.flag.loop == S_TRUE))
147 {
148 /* Reconfigure from main thread */
149 /* sh_fInotify_init_internal(); */
150 sh.flag.inotify |= SH_INOTIFY_DOSCAN;
151 sh.flag.inotify |= SH_INOTIFY_NEEDINIT;
152 return SH_MOD_THREAD;
153 }
154
155 /* Within thread, init */
156 return sh_fInotify_init_internal();
157#endif
158}
159
160int sh_fInotify_run()
161{
162 ssize_t len = -1;
163 char * buffer = SH_ALLOC(16384);
164
165 if (ShfInotifyActive == S_FALSE)
166 return SH_MOD_FAILED;
167
168 if ( (sh.flag.inotify & SH_INOTIFY_DOSCAN) ||
169 (sh.flag.inotify & SH_INOTIFY_NEEDINIT))
170 {
171 if (0 != sh_fInotify_init_internal())
172 return SH_MOD_FAILED;
173 }
174
175 /* Blocking read from inotify file descriptor.
176 */
177 len = sh_inotify_read(buffer, 16384);
178
179 if (len > 0)
180 {
181 struct inotify_event *event;
182 int i = 0;
183
184 while (i < len)
185 {
186 event = (struct inotify_event *) &(buffer[i]);
187
188 sh_fInotify_process(event);
189
190 i += sizeof (struct inotify_event) + event->len;
191 }
192
193 if ( (sh.flag.inotify & SH_INOTIFY_DOSCAN) ||
194 (sh.flag.inotify & SH_INOTIFY_NEEDINIT))
195 {
196 if (0 != sh_fInotify_init_internal())
197 return SH_MOD_FAILED;
198 }
199
200 }
201
202 /* Re-scan 'dormant' list of sh_file_missing.
203 */
204 sh_inotify_recheck_watches (&sh_file_watches, &sh_file_missing);
205
206 return 0;
207}
208
209/* We block in the read() call on the inotify descriptor,
210 * so we always run.
211 */
212int sh_fInotify_timer(time_t tcurrent)
213{
214 (void) tcurrent;
215 return 1;
216}
217
218int sh_fInotify_cleanup()
219{
220 sh_inotify_purge_dormant(&sh_file_watches);
221 sh_inotify_remove(&sh_file_watches);
222 sh_inotify_init(&sh_file_watches);
223 return 0;
224}
225
226int sh_fInotify_reconf()
227{
228 sh.flag.inotify = 0;
229
230 ShfInotifyWatches = 0;
231 ShfInotifyActive = 0;
232
233 return sh_fInotify_cleanup();
234}
235
236#define PROC_WATCHES_MAX _("/proc/sys/fs/inotify/max_user_watches")
237
238static void sh_fInotify_set_nwatches()
239{
240 if (ShfInotifyWatches == 0)
241 return;
242
243 if (0 == access(PROC_WATCHES_MAX, R_OK|W_OK)) /* flawfinder: ignore */
244 {
245 FILE * fd;
246
247 if (NULL != (fd = fopen(PROC_WATCHES_MAX, "r+")))
248 {
249 char str[128];
250 char * ret;
251 char * ptr;
252 unsigned long wn;
253
254 str[0] = '\0';
255 ret = fgets(str, 128, fd);
256 if (ret && *str != '\0')
257 {
258 wn = strtoul(str, &ptr, 0);
259 if (*ptr == '\0' || *ptr == '\n')
260 {
261 if (wn < ShfInotifyWatches)
262 {
263 sl_snprintf(str, sizeof(str), "%lu\n", ShfInotifyWatches);
264 (void) fseek(fd, 0L, SEEK_SET);
265 fputs(str, fd);
266 }
267 sl_fclose(FIL__, __LINE__, fd);
268 return;
269 }
270 }
271 sl_fclose(FIL__, __LINE__, fd);
272 }
273 }
274 SH_MUTEX_LOCK(mutex_thread_nolog);
275 sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN,
276 _("Cannot set max_user_watches"), _("sh_fInotify_set_nwatches"));
277 SH_MUTEX_UNLOCK(mutex_thread_nolog);
278 return;
279}
280
281/* The watch fd is thread specific. To have it in the fInotify thread,
282 * the main thread writes a list of files/dirs to watch, and here we
283 * now pop files from the list to add watches for them.
284 */
285static int sh_fInotify_init_internal()
286{
287 char * filename;
288 int class;
289 unsigned long check_mask;
290 int retval;
291 int errnum;
292
293 if (ShfInotifyActive == S_FALSE)
294 return SH_MOD_FAILED;
295
296 /* Wait until file scan is finished.
297 */
298 while((sh.flag.inotify & SH_INOTIFY_DOSCAN) != 0)
299 {
300 retry_msleep(1,0);
301
302 if (ShfInotifyActive == S_FALSE)
303 return SH_MOD_FAILED;
304 }
305
306 sh_fInotify_set_nwatches();
307
308 while (NULL != (filename = sh_inotify_pop_dormant(&sh_file_watches,
309 &class, &check_mask)))
310 {
311 retval = sh_inotify_add_watch(filename, &sh_file_watches, &errnum,
312 class, check_mask);
313
314 if (retval < 0)
315 {
316 char errbuf[SH_ERRBUF_SIZE];
317
318 sh_error_message(errnum, errbuf, sizeof(errbuf));
319
320 if ((errnum == ENOENT) || (errnum == EEXIST))
321 {
322 char * epath = sh_util_safe_name (filename);
323 SH_MUTEX_LOCK(mutex_thread_nolog);
324 sh_error_handle( (class == SH_LEVEL_ALLIGNORE) ?
325 ShDFLevel[class] :
326 ShDFLevel[SH_ERR_T_FILE],
327 FIL__, __LINE__, errnum, MSG_E_SUBGPATH,
328 errbuf, _("sh_fInotify_init_internal"), epath);
329 SH_MUTEX_UNLOCK(mutex_thread_nolog);
330 SH_FREE(epath);
331 }
332 else
333 {
334 SH_MUTEX_LOCK(mutex_thread_nolog);
335 sh_error_handle((-1), FIL__, __LINE__, errnum, MSG_E_SUBGEN,
336 errbuf, _("sh_fInotify_init_internal"));
337 SH_MUTEX_UNLOCK(mutex_thread_nolog);
338 }
339 }
340 }
341
342 /* Need this because mod_check() may run after
343 * DOSCAN is finished, hence wouldn't call init().
344 */
345 sh.flag.inotify &= ~SH_INOTIFY_NEEDINIT;
346
347 return 0;
348}
349
350static int sh_fInotify_process(struct inotify_event * event)
351{
352 int class;
353 unsigned long check_mask;
354 char * filename;
355
356 if (event->wd >= 0)
357 {
358 filename = sh_inotify_search_item(&sh_file_watches, event->wd,
359 &class, &check_mask);
360
361 if (filename)
362 {
363 sh_fInotify_report(event, filename, class, check_mask);
364
365 SH_FREE(filename);
366 }
367 else if (sh.flag.inotify & SH_INOTIFY_NEEDINIT)
368 {
369 return 1;
370 }
371 else if ((event->mask & IN_UNMOUNT) == 0 && (event->mask & IN_IGNORED) == 0)
372 {
373 /* Remove watch ? Seems reasonable. */
374 sh_inotify_rm_watch(NULL, NULL, event->wd);
375 SH_MUTEX_LOCK(mutex_thread_nolog);
376 sh_error_handle((-1), FIL__, __LINE__, event->wd, MSG_E_SUBGEN,
377 _("Watch removed: internal error - file path unknown"),
378 _("sh_fInotify_process"));
379 SH_MUTEX_UNLOCK(mutex_thread_nolog);
380 }
381 }
382 else if ((event->mask & IN_Q_OVERFLOW) != 0)
383 {
384 sh.flag.inotify |= SH_INOTIFY_DOSCAN;
385 sh.flag.inotify |= SH_INOTIFY_NEEDINIT;
386 return 1;
387 }
388
389 return 0;
390}
391
392void sh_fInotify_report_add(char * path, int class, unsigned long check_mask)
393{
394 if (S_FALSE == sh_ignore_chk_new(path))
395 {
396 int reported = 0;
397
398 sh_files_clear_file_reported(path);
399
400 sh_files_search_file(path, &class, &check_mask, &reported);
401
402 sh_files_filecheck (class, check_mask, path, NULL,
403 &reported, 0);
404 if (SH_FFLAG_REPORTED_SET(reported))
405 sh_files_set_file_reported(path);
406 }
407 return;
408}
409
410
411static void sh_fInotify_report_miss(char * name, int level)
412{
413 char * tmp = sh_util_safe_name (name);
414
415 SH_MUTEX_LOCK(mutex_thread_nolog);
416 sh_error_handle (level, FIL__, __LINE__, 0, MSG_FI_MISS, tmp);
417 SH_MUTEX_UNLOCK(mutex_thread_nolog);
418
419 SH_FREE(tmp);
420 return;
421}
422
423static int sh_fInotify_report(struct inotify_event * event, char * filename,
424 int class, unsigned long check_mask)
425{
426 char * fullpath = NULL;
427 char * path;
428 int reported;
429
430 if (event->len > 0)
431 {
432 fullpath = sh_util_strconcat(filename, "/", event->name, NULL);
433 path = fullpath;
434 }
435 else
436 {
437 path = filename;
438 }
439
440 if ( (event->mask & (IN_ACCESS|IN_MODIFY)) != 0)
441 {
442 sh_files_search_file(path, &class, &check_mask, &reported);
443
444 sh_files_filecheck (class, check_mask, filename,
445 (event->len > 0) ? event->name : NULL,
446 &reported, 0);
447 }
448 else if ((event->mask & (IN_DELETE|IN_DELETE_SELF|IN_MOVE_SELF|IN_MOVED_FROM)) != 0)
449 {
450 int isdir = (event->mask & IN_ISDIR);
451 int level = (class == SH_LEVEL_ALLIGNORE) ?
452 ShDFLevel[class] :
453 ShDFLevel[(isdir == 0) ? SH_ERR_T_FILE : SH_ERR_T_DIR];
454
455 if (S_FALSE == sh_ignore_chk_del(path))
456 {
457 if (0 != hashreport_missing(path, level))
458 {
459 sh_fInotify_report_miss(path, level);
460 }
461 }
462
463#ifndef REPLACE_OLD
464 sh_hash_set_visited_true(path);
465#else
466 sh_hash_set_missing(path);
467#endif
468 if (sh.flag.reportonce == S_TRUE)
469 sh_files_set_file_reported(path);
470
471 /* Move to 'dormant' list.
472 */
473 sh_inotify_rm_watch(&sh_file_watches, &sh_file_missing, event->wd);
474 }
475 else if((event->mask & (IN_CREATE|IN_MOVED_TO)) != 0)
476 {
477 if (S_FALSE == sh_ignore_chk_new(path))
478 {
479 sh_files_clear_file_reported(path);
480
481 sh_files_search_file(path, &class, &check_mask, &reported);
482
483 sh_files_filecheck (class, check_mask, filename,
484 (event->len > 0) ? event->name : NULL,
485 &reported, 0);
486 if (SH_FFLAG_REPORTED_SET(reported))
487 sh_files_set_file_reported(path);
488 }
489 }
490
491 return 0;
492}
493
494
495#endif
496
497#endif
Note: See TracBrowser for help on using the repository browser.