Browse code

onas: adding optional extra scanning for inotify events

Mickey Sola authored on 2015/10/06 02:48:03
Showing 8 changed files
... ...
@@ -50,7 +50,9 @@ clamd_SOURCES = \
50 50
     onaccess_ddd.c \
51 51
     onaccess_ddd.h \
52 52
     onaccess_hash.c \
53
-    onaccess_hash.h
53
+    onaccess_hash.h \
54
+    onaccess_scth.c \
55
+    onaccess_scth.h
54 56
 
55 57
 AM_CFLAGS=@WERR_CFLAGS@
56 58
 
... ...
@@ -186,7 +186,8 @@ am__clamd_SOURCES_DIST = $(top_srcdir)/shared/output.c \
186 186
 	localserver.c localserver.h session.c session.h thrmgr.c \
187 187
 	thrmgr.h server-th.c server.h scanner.c scanner.h others.c \
188 188
 	others.h shared.h onaccess_fan.c onaccess_fan.h onaccess_ddd.c \
189
-	onaccess_ddd.h onaccess_hash.c onaccess_hash.h
189
+	onaccess_ddd.h onaccess_hash.c onaccess_hash.h onaccess_scth.c \
190
+	onaccess_scth.h
190 191
 @BUILD_CLAMD_TRUE@am_clamd_OBJECTS = output.$(OBJEXT) \
191 192
 @BUILD_CLAMD_TRUE@	optparser.$(OBJEXT) getopt.$(OBJEXT) \
192 193
 @BUILD_CLAMD_TRUE@	misc.$(OBJEXT) clamd.$(OBJEXT) \
... ...
@@ -195,7 +196,8 @@ am__clamd_SOURCES_DIST = $(top_srcdir)/shared/output.c \
195 195
 @BUILD_CLAMD_TRUE@	server-th.$(OBJEXT) scanner.$(OBJEXT) \
196 196
 @BUILD_CLAMD_TRUE@	others.$(OBJEXT) onaccess_fan.$(OBJEXT) \
197 197
 @BUILD_CLAMD_TRUE@	onaccess_ddd.$(OBJEXT) \
198
-@BUILD_CLAMD_TRUE@	onaccess_hash.$(OBJEXT)
198
+@BUILD_CLAMD_TRUE@	onaccess_hash.$(OBJEXT) \
199
+@BUILD_CLAMD_TRUE@	onaccess_scth.$(OBJEXT)
199 200
 clamd_OBJECTS = $(am_clamd_OBJECTS)
200 201
 clamd_LDADD = $(LDADD)
201 202
 AM_V_lt = $(am__v_lt_@AM_V@)
... ...
@@ -493,7 +495,9 @@ top_srcdir = @top_srcdir@
493 493
 @BUILD_CLAMD_TRUE@    onaccess_ddd.c \
494 494
 @BUILD_CLAMD_TRUE@    onaccess_ddd.h \
495 495
 @BUILD_CLAMD_TRUE@    onaccess_hash.c \
496
-@BUILD_CLAMD_TRUE@    onaccess_hash.h
496
+@BUILD_CLAMD_TRUE@    onaccess_hash.h \
497
+@BUILD_CLAMD_TRUE@    onaccess_scth.c \
498
+@BUILD_CLAMD_TRUE@    onaccess_scth.h
497 499
 
498 500
 @BUILD_CLAMD_TRUE@AM_CFLAGS = @WERR_CFLAGS@
499 501
 AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/shared -I$(top_srcdir)/libclamav @SSL_CPPFLAGS@ @JSON_CPPFLAGS@ @PCRE_CPPFLAGS@
... ...
@@ -619,6 +623,7 @@ distclean-compile:
619 619
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/onaccess_ddd.Po@am__quote@
620 620
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/onaccess_fan.Po@am__quote@
621 621
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/onaccess_hash.Po@am__quote@
622
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/onaccess_scth.Po@am__quote@
622 623
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/optparser.Po@am__quote@
623 624
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/others.Po@am__quote@
624 625
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/output.Po@am__quote@
... ...
@@ -44,6 +44,7 @@
44 44
 #include "onaccess_fan.h"
45 45
 #include "onaccess_hash.h"
46 46
 #include "onaccess_ddd.h"
47
+#include "onaccess_scth.h"
47 48
 
48 49
 #include "libclamav/clamav.h"
49 50
 #include "libclamav/scanners.h"
... ...
@@ -285,12 +286,12 @@ void *onas_ddd_th(void *arg) {
285 285
 	/* ignore all signals except SIGUSR1 */
286 286
 	sigfillset(&sigset);
287 287
 	sigdelset(&sigset, SIGUSR1);
288
-	/* The behavior of a process is undefined after it ignores a 
288
+	/* The behavior of a process is undefined after it ignores a
289 289
 	 * SIGFPE, SIGILL, SIGSEGV, or SIGBUS signal */
290 290
 	sigdelset(&sigset, SIGFPE);
291 291
 	sigdelset(&sigset, SIGILL);
292 292
 	sigdelset(&sigset, SIGSEGV);
293
-#ifdef SIGBUS    
293
+#ifdef SIGBUS
294 294
 	sigdelset(&sigset, SIGBUS);
295 295
 #endif
296 296
 	pthread_sigmask(SIG_SETMASK, &sigset, NULL);
... ...
@@ -366,6 +367,10 @@ void *onas_ddd_th(void *arg) {
366 366
 		}
367 367
 	}
368 368
 
369
+	if(optget(tharg->opts, "OnAccessExtraScanning")->enabled) {
370
+		logg("ScanOnAccess: Extra scanning and notifications enabled.\n");
371
+	}
372
+
369 373
 
370 374
 	FD_ZERO(&rfds);
371 375
 	FD_SET(onas_in_fd, &rfds);
... ...
@@ -393,35 +398,24 @@ void *onas_ddd_th(void *arg) {
393 393
 				size_t size = strlen(child) + len + 2;
394 394
 				char *child_path = (char *) cli_malloc(size);
395 395
 				if (child_path == NULL)
396
-					return CL_EMEM;
396
+					return NULL;
397
+
397 398
 				if (path[len-1] == '/')
398 399
 					snprintf(child_path, --size, "%s%s", path, child);
399 400
 				else
400 401
 					snprintf(child_path, size, "%s/%s", path, child);
401 402
 
402
-				struct stat s;
403
-				if(stat(child_path, &s) == 0 && S_ISREG(s.st_mode)) continue;
404
-				if(!(event->mask & IN_ISDIR)) continue;
405
-
406 403
 				if (event->mask & IN_DELETE) {
407
-					logg("*ddd: DELETE - Removing %s from %s with wd:%d\n", child_path, path, wd);
408
-					onas_ddd_unwatch(child_path, tharg->fan_fd, onas_in_fd);
409
-					onas_ht_rm_hierarchy(ddd_ht, child_path, strlen(child_path), 0);
404
+					onas_ddd_handle_in_delete(tharg, path, child_path, event, wd);
410 405
 
411 406
 				} else if (event->mask & IN_MOVED_FROM) {
412
-					logg("*ddd: MOVED_FROM - Removing %s from %s with wd:%d\n", child_path, path, wd);
413
-					onas_ddd_unwatch(child_path, tharg->fan_fd, onas_in_fd);
414
-					onas_ht_rm_hierarchy(ddd_ht, child_path, strlen(child_path), 0);
407
+					onas_ddd_handle_in_moved_from(tharg, path, child_path, event, wd);
415 408
 
416 409
 				} else if (event->mask & IN_CREATE) {
417
-					logg("*ddd: CREATE - Adding %s to %s with wd:%d\n", child_path, path, wd);
418
-					onas_ht_add_hierarchy(ddd_ht, child_path);
419
-					onas_ddd_watch(child_path, tharg->fan_fd, tharg->fan_mask, onas_in_fd, in_mask);
410
+					onas_ddd_handle_in_create(tharg, path, child_path, event, wd, in_mask);
420 411
 
421 412
 				} else if (event->mask & IN_MOVED_TO) {
422
-					logg("*ddd: MOVED_TO - Adding %s to %s with wd:%d\n", child_path, path, wd);
423
-					onas_ht_add_hierarchy(ddd_ht, child_path);
424
-					onas_ddd_watch(child_path, tharg->fan_fd, tharg->fan_mask, onas_in_fd, in_mask);
413
+					onas_ddd_handle_in_moved_to(tharg, path, child_path, event, wd, in_mask);
425 414
 				}
426 415
 			}
427 416
 		}
... ...
@@ -430,6 +424,118 @@ void *onas_ddd_th(void *arg) {
430 430
 	return NULL;
431 431
 }
432 432
 
433
+static void onas_ddd_handle_in_delete(struct ddd_thrarg *tharg,
434
+		const char *path, const char *child_path, const struct inotify_event *event, int wd) {
435
+
436
+	struct stat s;
437
+	if(stat(child_path, &s) == 0 && S_ISREG(s.st_mode)) return;
438
+	if(!(event->mask & IN_ISDIR)) return;
439
+
440
+	logg("*ddd: DELETE - Removing %s from %s with wd:%d\n", child_path, path, wd);
441
+	onas_ddd_unwatch(child_path, tharg->fan_fd, onas_in_fd);
442
+	onas_ht_rm_hierarchy(ddd_ht, child_path, strlen(child_path), 0);
443
+
444
+	return;
445
+}
446
+
447
+
448
+static void onas_ddd_handle_in_moved_from(struct ddd_thrarg *tharg,
449
+		const char *path, const char *child_path, const struct inotify_event *event, int wd) {
450
+
451
+	struct stat s;
452
+	if(stat(child_path, &s) == 0 && S_ISREG(s.st_mode)) return;
453
+	if(!(event->mask & IN_ISDIR)) return;
454
+
455
+	logg("*ddd: MOVED_FROM - Removing %s from %s with wd:%d\n", child_path, path, wd);
456
+	onas_ddd_unwatch(child_path, tharg->fan_fd, onas_in_fd);
457
+	onas_ht_rm_hierarchy(ddd_ht, child_path, strlen(child_path), 0);
458
+
459
+	return;
460
+}
461
+
462
+
463
+static void onas_ddd_handle_in_create(struct ddd_thrarg *tharg,
464
+		const char *path, const char *child_path, const struct inotify_event *event, int wd, uint64_t in_mask) {
465
+
466
+	struct stat s;
467
+	if (optget(tharg->opts, "OnAccessExtraScanning")->enabled) {
468
+		if(stat(child_path, &s) == 0 && S_ISREG(s.st_mode)) {
469
+			onas_ddd_handle_extra_scanning(tharg, child_path, ONAS_SCTH_ISFILE);
470
+
471
+		} else if(stat(child_path, &s) == 0 && S_ISDIR(s.st_mode)) {
472
+			logg("*ddd: CREATE - Adding %s to %s with wd:%d\n", child_path, path, wd);
473
+			onas_ht_add_hierarchy(ddd_ht, child_path);
474
+			onas_ddd_watch(child_path, tharg->fan_fd, tharg->fan_mask, onas_in_fd, in_mask);
475
+
476
+			onas_ddd_handle_extra_scanning(tharg, child_path, ONAS_SCTH_ISDIR);
477
+		}
478
+	} else {
479
+
480
+		if(stat(child_path, &s) == 0 && S_ISREG(s.st_mode)) return;
481
+		if(!(event->mask & IN_ISDIR)) return;
482
+
483
+		logg("*ddd: MOVED_TO - Adding %s to %s with wd:%d\n", child_path, path, wd);
484
+		onas_ht_add_hierarchy(ddd_ht, child_path);
485
+		onas_ddd_watch(child_path, tharg->fan_fd, tharg->fan_mask, onas_in_fd, in_mask);
486
+	}
487
+
488
+	return;
489
+}
490
+
491
+static void onas_ddd_handle_in_moved_to(struct ddd_thrarg *tharg,
492
+		const char *path, const char *child_path, const struct inotify_event *event, int wd, uint64_t in_mask) {
493
+
494
+	struct stat s;
495
+	if (optget(tharg->opts, "OnAccessExtraScanning")->enabled) {
496
+		if(stat(child_path, &s) == 0 && S_ISREG(s.st_mode)) {
497
+			onas_ddd_handle_extra_scanning(tharg, child_path, ONAS_SCTH_ISFILE);
498
+
499
+		} else if(stat(child_path, &s) == 0 && S_ISDIR(s.st_mode)) {
500
+			logg("*ddd: MOVED_TO - Adding %s to %s with wd:%d\n", child_path, path, wd);
501
+			onas_ht_add_hierarchy(ddd_ht, child_path);
502
+			onas_ddd_watch(child_path, tharg->fan_fd, tharg->fan_mask, onas_in_fd, in_mask);
503
+
504
+			onas_ddd_handle_extra_scanning(tharg, child_path, ONAS_SCTH_ISDIR);
505
+		}
506
+	} else {
507
+		if(stat(child_path, &s) == 0 && S_ISREG(s.st_mode)) return;
508
+		if(!(event->mask & IN_ISDIR)) return;
509
+
510
+		logg("*ddd: MOVED_TO - Adding %s to %s with wd:%d\n", child_path, path, wd);
511
+		onas_ht_add_hierarchy(ddd_ht, child_path);
512
+		onas_ddd_watch(child_path, tharg->fan_fd, tharg->fan_mask, onas_in_fd, in_mask);
513
+	}
514
+
515
+	return;
516
+}
517
+
518
+static void onas_ddd_handle_extra_scanning(struct ddd_thrarg *tharg, const char *pathname, int options) {
519
+
520
+	struct scth_thrarg *scth_tharg = NULL;
521
+	pthread_attr_t scth_attr;
522
+	pthread_t scth_pid = 0;
523
+
524
+	do {
525
+		if (pthread_attr_init(&scth_attr)) break;
526
+		pthread_attr_setdetachstate(&scth_attr, PTHREAD_CREATE_JOINABLE);
527
+
528
+		if (!(scth_tharg = (struct scth_thrarg *) malloc(sizeof(struct scth_thrarg)))) break;
529
+
530
+		scth_tharg->options = options;
531
+		scth_tharg->opts = tharg->opts;
532
+		scth_tharg->pathname = strdup(pathname);
533
+
534
+		if (!pthread_create(&scth_pid, &scth_attr, onas_scan_th, scth_tharg)) break;
535
+
536
+		free(scth_tharg);
537
+		scth_tharg = NULL;
538
+	} while(0);
539
+	if (!scth_tharg) logg("!ScanOnAccess: Unable to kick off extra scanning.\n");
540
+
541
+	return;
542
+}
543
+
544
+
433 545
 static void onas_ddd_exit(int sig) {
434 546
 	logg("*ScanOnAccess: onas_ddd_exit(), signal %d\n", sig);
435 547
 
... ...
@@ -437,7 +543,7 @@ static void onas_ddd_exit(int sig) {
437 437
 
438 438
 	onas_free_ht(ddd_ht);
439 439
 	free(wdlt);
440
-	
440
+
441 441
 	pthread_exit(NULL);
442 442
 	logg("ScanOnAccess: stopped\n");
443 443
 }
... ...
@@ -46,6 +46,12 @@ static int onas_ddd_watch_hierarchy(const char* pathname, size_t len, int fd, ui
46 46
 static int onas_ddd_unwatch(const char *pathname, int fan_fd, int in_fd);
47 47
 static int onas_ddd_unwatch_hierarchy(const char* pathname, size_t len, int fd, uint32_t type);
48 48
 
49
+static void onas_ddd_handle_in_moved_to(struct ddd_thrarg *tharg, const char *path, const char *child_path, const struct inotify_event *event, int wd, uint64_t in_mask);
50
+static void onas_ddd_handle_in_create(struct ddd_thrarg *tharg, const char *path, const char *child_path, const struct inotify_event *event, int wd, uint64_t in_mask);
51
+static void onas_ddd_handle_in_moved_from(struct ddd_thrarg *tharg, const char *path, const char *child_path, const struct inotify_event *event, int wd);
52
+static void onas_ddd_handle_in_delete(struct ddd_thrarg *tharg, const char *path, const char *child_path, const struct inotify_event *event, int wd);
53
+static void onas_ddd_handle_extra_scanning(struct ddd_thrarg *tharg, const char *pathname, int options);
54
+
49 55
 int onas_ddd_init(uint64_t nwatches, size_t ht_size);
50 56
 void *onas_ddd_th(void *arg);
51 57
 static void onas_ddd_exit(int sig);
52 58
new file mode 100644
... ...
@@ -0,0 +1,117 @@
0
+/*
1
+ *  Copyright (C) 2015 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
2
+ *
3
+ *  Authors: Mickey Sola
4
+ *
5
+ *  This program is free software; you can redistribute it and/or modify
6
+ *  it under the terms of the GNU General Public License version 2 as
7
+ *  published by the Free Software Foundation.
8
+ *
9
+ *  This program is distributed in the hope that it will be useful,
10
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
+ *  GNU General Public License for more details.
13
+ *
14
+ *  You should have received a copy of the GNU General Public License
15
+ *  along with this program; if not, write to the Free Software
16
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17
+ *  MA 02110-1301, USA.
18
+ */
19
+
20
+#if HAVE_CONFIG_H
21
+#include "clamav-config.h"
22
+#endif
23
+
24
+#if defined(FANOTIFY)
25
+
26
+#include <stdio.h>
27
+#include <unistd.h>
28
+#include <string.h>
29
+#include <fcntl.h>
30
+#include <fts.h>
31
+#include <signal.h>
32
+#include <pthread.h>
33
+
34
+#include "shared/optparser.h"
35
+#include "shared/output.h"
36
+
37
+#include "others.h"
38
+
39
+#include "onaccess_scth.h"
40
+
41
+static void onas_scth_exit(int sig) {
42
+	logg("*ScanOnAccess: onas_scth_exit(), signal %d\n", sig);
43
+
44
+	pthread_exit(NULL);
45
+}
46
+
47
+static void onas_scth_handle_dir(const char *pathname) {
48
+	FTS *ftsp = NULL;
49
+	int ftspopts = FTS_PHYSICAL | FTS_XDEV;
50
+	FTSENT *curr = NULL;
51
+
52
+	char *const pathargv[] = { pathname, NULL };
53
+	if (!(ftsp = fts_open(pathargv, ftspopts, NULL))) return;
54
+
55
+	/* Offload scanning work to fanotify thread to avoid potential deadlocks. */
56
+	while (curr = fts_read(ftsp)) {
57
+		if (curr->fts_info != FTS_D) {
58
+			int fd = open(curr->fts_path, O_RDONLY);
59
+			if (fd > 0) close(fd);
60
+		}
61
+	}
62
+
63
+	return;
64
+}
65
+
66
+
67
+static void onas_scth_handle_file(const char *pathname) {
68
+	if (!pathname) return;
69
+
70
+	/* Offload scanning work to fanotify thread to avoid potential deadlocks. */
71
+	int fd = open(pathname, O_RDONLY);
72
+	if (fd > 0) close(fd);
73
+
74
+	return;
75
+}
76
+
77
+void *onas_scan_th(void *arg) {
78
+	struct scth_thrarg *tharg = (struct scth_thrarg *) arg;
79
+	sigset_t sigset;
80
+	struct sigaction act;
81
+	const struct optstruct *pt;
82
+	short int scan;
83
+
84
+	/* ignore all signals except SIGUSR1 */
85
+	sigfillset(&sigset);
86
+	sigdelset(&sigset, SIGUSR1);
87
+	/* The behavior of a process is undefined after it ignores a
88
+	 * SIGFPE, SIGILL, SIGSEGV, or SIGBUS signal */
89
+	sigdelset(&sigset, SIGFPE);
90
+	sigdelset(&sigset, SIGILL);
91
+	sigdelset(&sigset, SIGSEGV);
92
+#ifdef SIGBUS
93
+	sigdelset(&sigset, SIGBUS);
94
+#endif
95
+	pthread_sigmask(SIG_SETMASK, &sigset, NULL);
96
+	memset(&act, 0, sizeof(struct sigaction));
97
+	act.sa_handler = onas_scth_exit;
98
+	sigfillset(&(act.sa_mask));
99
+	sigaction(SIGUSR1, &act, NULL);
100
+	sigaction(SIGSEGV, &act, NULL);
101
+
102
+
103
+	if (tharg->options & ONAS_SCTH_ISDIR) {
104
+		logg("ScanOnAccess: Performing additional scanning on directory '%s'\n", tharg->pathname);
105
+		onas_scth_handle_dir(tharg->pathname);
106
+	} else if (tharg->options & ONAS_SCTH_ISFILE) {
107
+		logg("ScanOnAccess: Performing additional scanning on file '%s'\n", tharg->pathname);
108
+		onas_scth_handle_file(tharg->pathname);
109
+	}
110
+
111
+	free(tharg->pathname);
112
+	tharg->pathname = NULL;
113
+
114
+	return NULL;
115
+}
116
+#endif
0 117
new file mode 100644
... ...
@@ -0,0 +1,39 @@
0
+/*
1
+ *  Copyright (C) 2015 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
2
+ *
3
+ *  Authors: Mickey Sola
4
+ *
5
+ *  This program is free software; you can redistribute it and/or modify
6
+ *  it under the terms of the GNU General Public License version 2 as
7
+ *  published by the Free Software Foundation.
8
+ *
9
+ *  This program is distributed in the hope that it will be useful,
10
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
+ *  GNU General Public License for more details.
13
+ *
14
+ *  You should have received a copy of the GNU General Public License
15
+ *  along with this program; if not, write to the Free Software
16
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17
+ *  MA 02110-1301, USA.
18
+ */
19
+
20
+#ifndef __ONAS_SCTH_H
21
+#define __ONAS_SCTH_H
22
+
23
+#define ONAS_SCTH_ISDIR  0x01
24
+#define ONAS_SCTH_ISFILE 0x02
25
+
26
+struct scth_thrarg {
27
+	int options;
28
+	const struct optstruct *opts;
29
+	const char *pathname;
30
+};
31
+
32
+static void onas_scth_handle_dir(const char *pathname);
33
+static void onas_scth_handle_file(const char *pathname);
34
+
35
+void *onas_scan_th(void *arg);
36
+static void onas_scth_exit(int sig);
37
+
38
+#endif
... ...
@@ -599,6 +599,12 @@ Example
599 599
 # Default: no
600 600
 #OnAccessPrevention yes
601 601
 
602
+# Toggles extra scanning and notifications when a file or directory is created or moved.
603
+# Requires the  DDD system to kick-off extra scans.
604
+# (On-access scan only)
605
+# Default: no
606
+#OnAccessExtraScanning yes
607
+
602 608
 ##
603 609
 ## Bytecode
604 610
 ##
... ...
@@ -407,6 +407,8 @@ const struct clam_option __clam_options[] = {
407 407
 
408 408
     { "OnAccessPrevention", NULL, 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD, "This option changes fanotify behavior to prevent access attempts on malicious files instead of simply notifying the user (On Access scan only).", "yes" },
409 409
 
410
+    { "OnAccessExtraScanning", NULL, 0, CLOPT_TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_CLAMD, "Enables extra scanning and notification after catching certain inotify events. Only works with the DDD system enabled.", "yes" },
411
+
410 412
     /* FIXME: mark these as private and don't output into clamd.conf/man */
411 413
     { "DevACOnly", "dev-ac-only", 0, CLOPT_TYPE_BOOL, MATCH_BOOL, -1, NULL, FLAG_HIDDEN, OPT_CLAMD | OPT_CLAMSCAN, "", "" },
412 414