Browse code

Overhauling clamd onaccess scanning to support dynamic directory determination.

Mickey Sola authored on 2015/08/29 05:03:29
Showing 17 changed files
... ...
@@ -45,8 +45,12 @@ clamd_SOURCES = \
45 45
     others.c \
46 46
     others.h \
47 47
     shared.h \
48
-    fan.c \
49
-    fan.h
48
+    onaccess_fan.c \
49
+    onaccess_fan.h \
50
+    onaccess_ddd.c \
51
+    onaccess_ddd.h \
52
+    onaccess_hash.c \
53
+    onaccess_hash.h
50 54
 
51 55
 AM_CFLAGS=@WERR_CFLAGS@
52 56
 
... ...
@@ -185,14 +185,17 @@ am__clamd_SOURCES_DIST = $(top_srcdir)/shared/output.c \
185 185
 	$(top_srcdir)/shared/misc.h clamd.c tcpserver.c tcpserver.h \
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
-	others.h shared.h fan.c fan.h
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 190
 @BUILD_CLAMD_TRUE@am_clamd_OBJECTS = output.$(OBJEXT) \
190 191
 @BUILD_CLAMD_TRUE@	optparser.$(OBJEXT) getopt.$(OBJEXT) \
191 192
 @BUILD_CLAMD_TRUE@	misc.$(OBJEXT) clamd.$(OBJEXT) \
192 193
 @BUILD_CLAMD_TRUE@	tcpserver.$(OBJEXT) localserver.$(OBJEXT) \
193 194
 @BUILD_CLAMD_TRUE@	session.$(OBJEXT) thrmgr.$(OBJEXT) \
194 195
 @BUILD_CLAMD_TRUE@	server-th.$(OBJEXT) scanner.$(OBJEXT) \
195
-@BUILD_CLAMD_TRUE@	others.$(OBJEXT) fan.$(OBJEXT)
196
+@BUILD_CLAMD_TRUE@	others.$(OBJEXT) onaccess_fan.$(OBJEXT) \
197
+@BUILD_CLAMD_TRUE@	onaccess_ddd.$(OBJEXT) \
198
+@BUILD_CLAMD_TRUE@	onaccess_hash.$(OBJEXT)
196 199
 clamd_OBJECTS = $(am_clamd_OBJECTS)
197 200
 clamd_LDADD = $(LDADD)
198 201
 AM_V_lt = $(am__v_lt_@AM_V@)
... ...
@@ -485,8 +488,12 @@ top_srcdir = @top_srcdir@
485 485
 @BUILD_CLAMD_TRUE@    others.c \
486 486
 @BUILD_CLAMD_TRUE@    others.h \
487 487
 @BUILD_CLAMD_TRUE@    shared.h \
488
-@BUILD_CLAMD_TRUE@    fan.c \
489
-@BUILD_CLAMD_TRUE@    fan.h
488
+@BUILD_CLAMD_TRUE@    onaccess_fan.c \
489
+@BUILD_CLAMD_TRUE@    onaccess_fan.h \
490
+@BUILD_CLAMD_TRUE@    onaccess_ddd.c \
491
+@BUILD_CLAMD_TRUE@    onaccess_ddd.h \
492
+@BUILD_CLAMD_TRUE@    onaccess_hash.c \
493
+@BUILD_CLAMD_TRUE@    onaccess_hash.h
490 494
 
491 495
 @BUILD_CLAMD_TRUE@AM_CFLAGS = @WERR_CFLAGS@
492 496
 AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/shared -I$(top_srcdir)/libclamav @SSL_CPPFLAGS@ @JSON_CPPFLAGS@ @PCRE_CPPFLAGS@
... ...
@@ -606,10 +613,12 @@ distclean-compile:
606 606
 	-rm -f *.tab.c
607 607
 
608 608
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clamd.Po@am__quote@
609
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fan.Po@am__quote@
610 609
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt.Po@am__quote@
611 610
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/localserver.Po@am__quote@
612 611
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc.Po@am__quote@
612
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/onaccess_ddd.Po@am__quote@
613
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/onaccess_fan.Po@am__quote@
614
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/onaccess_hash.Po@am__quote@
613 615
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/optparser.Po@am__quote@
614 616
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/others.Po@am__quote@
615 617
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/output.Po@am__quote@
616 618
deleted file mode 100644
... ...
@@ -1,358 +0,0 @@
1
-/*
2
- *  Copyright (C) 2011 Sourcefire, Inc.
3
- *
4
- *  Authors: Tomasz Kojm
5
- *
6
- *  This program is free software; you can redistribute it and/or modify
7
- *  it under the terms of the GNU General Public License version 2 as
8
- *  published by the Free Software Foundation.
9
- *
10
- *  This program is distributed in the hope that it will be useful,
11
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
- *  GNU General Public License for more details.
14
- *
15
- *  You should have received a copy of the GNU General Public License
16
- *  along with this program; if not, write to the Free Software
17
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18
- *  MA 02110-1301, USA.
19
- */
20
-
21
-#if HAVE_CONFIG_H
22
-#include "clamav-config.h"
23
-#endif
24
-
25
-#ifdef FANOTIFY
26
-
27
-#include <stdio.h>
28
-#include <unistd.h>
29
-#include <sys/types.h>
30
-#include <sys/stat.h>
31
-#include <fcntl.h>
32
-#include <signal.h>
33
-#include <pthread.h>
34
-#include <string.h>
35
-#include <errno.h>
36
-
37
-#include <sys/fanotify.h>
38
-
39
-#include "fan.h"
40
-
41
-#include "libclamav/clamav.h"
42
-#include "libclamav/scanners.h"
43
-
44
-#include "shared/optparser.h"
45
-#include "shared/output.h"
46
-
47
-#include "server.h"
48
-#include "others.h"
49
-#include "scanner.h"
50
-
51
-static void fan_exit(int sig)
52
-{
53
-
54
-    logg("*ScanOnAccess: fan_exit(), signal %d\n", sig);
55
-    pthread_exit(NULL);
56
-    logg("ScanOnAccess: stopped\n");
57
-}
58
-
59
-static int fan_scanfile(int fan_fd, const char *fname, struct fanotify_event_metadata *fmd, int scan, int extinfo, struct thrarg *tharg)
60
-{
61
-	struct fanotify_response res;
62
-	struct cb_context context;
63
-	const char *virname;
64
-	int ret = 0;
65
-
66
-    res.fd = fmd->fd;
67
-    res.response = FAN_ALLOW;
68
-    context.filename = fname;
69
-    context.virsize = 0;
70
-    if(scan && cl_scandesc_callback(fmd->fd, &virname, NULL, tharg->engine, tharg->options, &context) == CL_VIRUS) {
71
-	if(extinfo && context.virsize)
72
-	    logg("ScanOnAccess: %s: %s(%s:%llu) FOUND\n", fname, virname, context.virhash, context.virsize);
73
-	else
74
-	    logg("ScanOnAccess: %s: %s FOUND\n", fname, virname);
75
-	virusaction(fname, virname, tharg->opts);
76
-	res.response = FAN_DENY;
77
-    }
78
-
79
-    if(fmd->mask & FAN_ALL_PERM_EVENTS) {
80
-	ret = write(fan_fd, &res, sizeof(res));
81
-	if(ret == -1)
82
-	    logg("!ScanOnAccess: Internal error (can't write to fanotify)\n");
83
-    }
84
-
85
-    return ret;
86
-}
87
-
88
-void *fan_th(void *arg)
89
-{
90
-	struct thrarg *tharg = (struct thrarg *) arg;
91
-	sigset_t sigset;
92
-        struct sigaction act;
93
-	const struct optstruct *pt;
94
-	short int scan;
95
-	int sizelimit = 0, extinfo;
96
-	STATBUF sb;
97
-        uint64_t fan_mask = FAN_ACCESS | FAN_EVENT_ON_CHILD;
98
-	int fan_fd;
99
-        fd_set rfds;
100
-	char buf[4096];
101
-	ssize_t bread;
102
-	struct fanotify_event_metadata *fmd;
103
-	char fname[1024];
104
-	int ret, len;
105
-	char err[128];
106
-
107
-    /* ignore all signals except SIGUSR1 */
108
-    sigfillset(&sigset);
109
-    sigdelset(&sigset, SIGUSR1);
110
-    /* The behavior of a process is undefined after it ignores a 
111
-     * SIGFPE, SIGILL, SIGSEGV, or SIGBUS signal */
112
-    sigdelset(&sigset, SIGFPE);
113
-    sigdelset(&sigset, SIGILL);
114
-    sigdelset(&sigset, SIGSEGV);
115
-#ifdef SIGBUS    
116
-    sigdelset(&sigset, SIGBUS);
117
-#endif
118
-    pthread_sigmask(SIG_SETMASK, &sigset, NULL);
119
-    memset(&act, 0, sizeof(struct sigaction));
120
-    act.sa_handler = fan_exit;
121
-    sigfillset(&(act.sa_mask));
122
-    sigaction(SIGUSR1, &act, NULL);
123
-    sigaction(SIGSEGV, &act, NULL);
124
-
125
-    fan_fd = fanotify_init(0, O_RDONLY);
126
-    if(fan_fd < 0) {
127
-	logg("!ScanOnAccess: fanotify_init failed: %s\n", cli_strerror(errno, err, sizeof(err)));
128
-	if(errno == EPERM)
129
-	    logg("ScanOnAccess: clamd must be started by root\n");
130
-	return NULL;
131
-    }
132
-
133
-    if((pt = optget(tharg->opts, "OnAccessIncludePath"))->enabled) {
134
-	while(pt) {
135
-	    if(fanotify_mark(fan_fd, FAN_MARK_ADD, fan_mask, fan_fd, pt->strarg) != 0) {
136
-		logg("!ScanOnAccess: Can't include path '%s'\n", pt->strarg);
137
-		return NULL;
138
-	    } else
139
-		logg("ScanOnAccess: Protecting directory '%s'\n", pt->strarg);
140
-	    pt = (struct optstruct *) pt->nextarg;
141
-	}
142
-    } else {
143
-	logg("!ScanOnAccess: Please specify at least one path with OnAccessIncludePath\n");
144
-	return NULL;
145
-    }
146
-
147
-    if((pt = optget(tharg->opts, "OnAccessExcludePath"))->enabled) {
148
-	while(pt) {
149
-            if(fanotify_mark(fan_fd, FAN_MARK_REMOVE, fan_mask, fan_fd, pt->strarg) != 0) {
150
-		logg("!ScanOnAccess: Can't exclude path %s\n", pt->strarg);
151
-		return NULL;
152
-	    } else
153
-		logg("ScanOnAccess: Excluded path %s\n", pt->strarg);
154
-	    pt = (struct optstruct *) pt->nextarg;
155
-	}
156
-    }
157
-
158
-    sizelimit = optget(tharg->opts, "OnAccessMaxFileSize")->numarg;
159
-    if(sizelimit)
160
-	logg("ScanOnAccess: Max file size limited to %d bytes\n", sizelimit);
161
-    else
162
-	logg("ScanOnAccess: File size limit disabled\n");
163
-
164
-    extinfo = optget(tharg->opts, "ExtendedDetectionInfo")->enabled;
165
-
166
-    FD_ZERO(&rfds);
167
-    FD_SET(fan_fd, &rfds);
168
-    do {
169
-        ret = select(fan_fd + 1, &rfds, NULL, NULL, NULL);
170
-    } while(ret == -1 && errno == EINTR);
171
-
172
-    while((bread = read(fan_fd, buf, sizeof(buf))) > 0) {
173
-	fmd = (struct fanotify_event_metadata *) buf;
174
-	while(FAN_EVENT_OK(fmd, bread)) {
175
-	    scan = 1;
176
-	    if(fmd->fd >= 0) {
177
-		sprintf(fname, "/proc/self/fd/%d", fmd->fd);
178
-		len = readlink(fname, fname, sizeof(fname) - 1);
179
-		if(len == -1) {
180
-		    close(fmd->fd);
181
-		    logg("!ScanOnAccess: Internal error (readlink() failed)\n");
182
-		    return NULL;
183
-		}
184
-		fname[len] = 0;
185
-
186
-		if(fan_checkowner(fmd->pid, tharg->opts)) {
187
-		    scan = 0;
188
-		    logg("*ScanOnAccess: %s skipped (excluded UID)\n", fname);
189
-		}
190
-
191
-		if(sizelimit) {
192
-		    if(FSTAT(fmd->fd, &sb) != 0 || sb.st_size > sizelimit) {
193
-			scan = 0;
194
-			/* logg("*ScanOnAccess: %s skipped (size > %d)\n", fname, sizelimit); */
195
-		    }
196
-		}
197
-
198
-		if(fan_scanfile(fan_fd, fname, fmd, scan, extinfo, tharg) == -1) {
199
-		    close(fmd->fd);
200
-		    return NULL;
201
-		}
202
-
203
-		if(close(fmd->fd) == -1) {
204
-		    printf("!ScanOnAccess: Internal error (close(%d) failed)\n", fmd->fd);
205
-		    close(fmd->fd);
206
-		    return NULL;
207
-		}
208
-	    }
209
-	    fmd = FAN_EVENT_NEXT(fmd, bread);
210
-	}
211
-	do {
212
-	    ret = select(fan_fd + 1, &rfds, NULL, NULL, NULL);
213
-	} while(ret == -1 && errno == EINTR);
214
-    }
215
-
216
-    if(bread < 0)
217
-	logg("!ScanOnAccess: Internal error (failed to read data)\n");
218
-
219
-    return NULL;
220
-}
221
-
222
-#elif defined(CLAMAUTH)
223
-
224
-#include <stdio.h>
225
-#include <unistd.h>
226
-#include <sys/types.h>
227
-#include <sys/stat.h>
228
-#include <sys/uio.h>
229
-#include <fcntl.h>
230
-#include <signal.h>
231
-#include <pthread.h>
232
-#include <string.h>
233
-#include <errno.h>
234
-
235
-#include "libclamav/clamav.h"
236
-#include "libclamav/scanners.h"
237
-
238
-#include "shared/optparser.h"
239
-#include "shared/output.h"
240
-
241
-#include "server.h"
242
-#include "others.h"
243
-#include "scanner.h"
244
-
245
-#define SUPPORTED_PROTOCOL  2
246
-
247
-static int cauth_fd = -1;
248
-
249
-struct ClamAuthEvent {
250
-    unsigned int action;
251
-    char path[1024];
252
-    unsigned int pid;
253
-};
254
-
255
-static void cauth_exit(int sig)
256
-{
257
-    logg("*ScanOnAccess: cauth_exit(), signal %d\n", sig);
258
-    if(cauth_fd > 0)
259
-	close(cauth_fd);
260
-    pthread_exit(NULL);
261
-    logg("ScanOnAccess: stopped\n");
262
-}
263
-
264
-static int cauth_scanfile(const char *fname, int extinfo, struct thrarg *tharg)
265
-{
266
-	struct cb_context context;
267
-	const char *virname;
268
-	int ret = 0, fd;
269
-
270
-    context.filename = fname;
271
-    context.virsize = 0;
272
-
273
-    fd = open(fname, O_RDONLY);
274
-    if(fd == -1)
275
-	return -1;
276
-
277
-    if(cl_scandesc_callback(fd, &virname, NULL, tharg->engine, tharg->options, &context) == CL_VIRUS) {
278
-	if(extinfo && context.virsize)
279
-	    logg("ScanOnAccess: %s: %s(%s:%llu) FOUND\n", fname, virname, context.virhash, context.virsize);
280
-	else
281
-	    logg("ScanOnAccess: %s: %s FOUND\n", fname, virname);
282
-	virusaction(fname, virname, tharg->opts);
283
-    }
284
-    close(fd);
285
-    return ret;
286
-}
287
-
288
-void *fan_th(void *arg)
289
-{
290
-	struct thrarg *tharg = (struct thrarg *) arg;
291
-	sigset_t sigset;
292
-        struct sigaction act;
293
-	int eventcnt = 1, extinfo;
294
-	char err[128];
295
-	struct ClamAuthEvent event;
296
-
297
-    /* ignore all signals except SIGUSR1 */
298
-    sigfillset(&sigset);
299
-    sigdelset(&sigset, SIGUSR1);
300
-    /* The behavior of a process is undefined after it ignores a 
301
-     * SIGFPE, SIGILL, SIGSEGV, or SIGBUS signal */
302
-    sigdelset(&sigset, SIGFPE);
303
-    sigdelset(&sigset, SIGILL);
304
-    sigdelset(&sigset, SIGSEGV);
305
-#ifdef SIGBUS    
306
-    sigdelset(&sigset, SIGBUS);
307
-#endif
308
-    pthread_sigmask(SIG_SETMASK, &sigset, NULL);
309
-    memset(&act, 0, sizeof(struct sigaction));
310
-    act.sa_handler = cauth_exit;
311
-    sigfillset(&(act.sa_mask));
312
-    sigaction(SIGUSR1, &act, NULL);
313
-    sigaction(SIGSEGV, &act, NULL);
314
-
315
-    extinfo = optget(tharg->opts, "ExtendedDetectionInfo")->enabled;
316
-
317
-    cauth_fd = open("/dev/clamauth", O_RDONLY);
318
-    if(cauth_fd == -1) {
319
-	logg("!ScanOnAccess: Can't open /dev/clamauth\n");
320
-	if(errno == ENOENT)
321
-	    logg("!ScanOnAccess: Please make sure ClamAuth.kext is loaded\n");
322
-	else if(errno == EACCES)
323
-	    logg("!ScanOnAccess: This application requires root privileges\n");
324
-	else
325
-	    logg("!ScanOnAccess: /dev/clamauth: %s\n", cli_strerror(errno, err, sizeof(err)));
326
-
327
-	return NULL;
328
-    }
329
-
330
-    while(1) {
331
-	if(read(cauth_fd, &event, sizeof(event)) > 0) {
332
-	    if(eventcnt == 1) {
333
-		if(event.action != SUPPORTED_PROTOCOL) {
334
-		    logg("!ScanOnAccess: Protocol version mismatch (tool: %d, driver: %d)\n", SUPPORTED_PROTOCOL, event.action);
335
-		    close(cauth_fd);
336
-		    return NULL;
337
-		}
338
-		if(strncmp(event.path, "ClamAuth", 8)) {
339
-		    logg("!ScanOnAccess: Invalid version event\n");
340
-		    close(cauth_fd);
341
-		    return NULL;
342
-		}
343
-		logg("ScanOnAccess: Driver version: %s, protocol version: %d\n", &event.path[9], event.action);
344
-	    } else {
345
-		cauth_scanfile(event.path, extinfo, tharg);
346
-	    }
347
-	    eventcnt++;
348
-	} else {
349
-	    if(errno == ENODEV) {
350
-		printf("^ScanOnAccess: ClamAuth module deactivated, terminating\n");
351
-		close(cauth_fd);
352
-		return NULL;
353
-	    }
354
-	}
355
-	usleep(200);
356
-    }
357
-}
358
-#endif
359 1
deleted file mode 100644
... ...
@@ -1,26 +0,0 @@
1
-/*
2
- *  Copyright (C) 2011 Sourcefire, Inc.
3
- *
4
- *  Authors: Tomasz Kojm
5
- *
6
- *  This program is free software; you can redistribute it and/or modify
7
- *  it under the terms of the GNU General Public License version 2 as
8
- *  published by the Free Software Foundation.
9
- *
10
- *  This program is distributed in the hope that it will be useful,
11
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
- *  GNU General Public License for more details.
14
- *
15
- *  You should have received a copy of the GNU General Public License
16
- *  along with this program; if not, write to the Free Software
17
- *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18
- *  MA 02110-1301, USA.
19
- */
20
-
21
-#ifndef __FAN_H
22
-#define __FAN_H
23
-
24
-void *fan_th(void *arg);
25
-
26
-#endif
27 1
new file mode 100644
... ...
@@ -0,0 +1,424 @@
0
+/*
1
+ *  Copyright (C) 2015 Sourcefire, Inc.
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
+#define PROTOTYPING 1
25
+#if PROTOTYPING
26
+
27
+#include <stdio.h>
28
+#include <stdlib.h>
29
+#include <unistd.h>
30
+#include <sys/types.h>
31
+#include <sys/stat.h>
32
+#include <fts.h>
33
+#include <fcntl.h>
34
+#include <signal.h>
35
+#include <pthread.h>
36
+#include <string.h>
37
+#include <errno.h>
38
+#include <stdbool.h>
39
+
40
+#include <sys/fanotify.h>
41
+#include <sys/inotify.h>
42
+
43
+#include "onaccess_fan.h"
44
+#include "onaccess_hash.h"
45
+#include "onaccess_ddd.h"
46
+
47
+#include "libclamav/clamav.h"
48
+#include "libclamav/scanners.h"
49
+
50
+#include "shared/optparser.h"
51
+#include "shared/output.h"
52
+
53
+#include "server.h"
54
+#include "others.h"
55
+#include "scanner.h"
56
+
57
+/* TODO: Unglobalize these. */
58
+static struct onas_ht *ddd_ht;
59
+static char **wdlt;
60
+static uint32_t wdlt_len;
61
+
62
+static int onas_ddd_init_ht(uint32_t ht_size) {
63
+
64
+	if (ht_size <= 0)
65
+		ht_size = ONAS_DEFAULT_HT_SIZE;
66
+
67
+	return onas_ht_init(&ddd_ht, ht_size);
68
+}
69
+
70
+static int onas_ddd_init_wdlt(uint32_t nwatches) {
71
+
72
+	wdlt = (char **) cli_calloc(nwatches << 1, sizeof(char*));
73
+	if (!wdlt) return CL_EMEM;
74
+
75
+	wdlt_len = nwatches << 1;
76
+
77
+	return CL_SUCCESS;
78
+}
79
+
80
+static int onas_ddd_grow_wdlt() {
81
+
82
+	char **ptr = NULL;
83
+
84
+	ptr = (char **) cli_realloc(wdlt, wdlt_len << 1);
85
+	if (!ptr) return CL_EMEM;
86
+
87
+	if (ptr == wdlt)
88
+		memset(&ptr[wdlt_len], 0, sizeof(char *) * (wdlt_len - 1));
89
+	else
90
+		return CL_EMEM;
91
+
92
+	wdlt_len <<= 1;
93
+
94
+	return CL_SUCCESS;
95
+}
96
+
97
+
98
+/* TODO: Support configuration for changing/setting number of inotify watches. */
99
+int onas_ddd_init(uint32_t nwatches, size_t ht_size) {
100
+
101
+	const char* nwatch_file = "/proc/sys/fs/inotify/max_user_watches";
102
+	int nwfd = 0;
103
+	int ret = 0;
104
+	char nwatch_str[MAX_WATCH_LEN];
105
+	char *p = NULL;
106
+	nwatches = 0;
107
+
108
+	nwfd = open(nwatch_file, O_RDONLY);
109
+	if (nwfd < 0) return CL_EOPEN;
110
+
111
+	ret = read(nwfd, nwatch_str, MAX_WATCH_LEN);
112
+	close(nwfd);
113
+	if (ret < 0) return CL_EREAD;
114
+
115
+	nwatches = strtol(nwatch_str, &p, 10);
116
+
117
+	ret = onas_ddd_init_wdlt(nwatches);
118
+	if (ret) return ret;
119
+
120
+	ret = onas_ddd_init_ht(ht_size);
121
+	if (ret) return ret;
122
+
123
+	return CL_SUCCESS;
124
+}
125
+
126
+static int onas_ddd_watch(const char *pathname, int fan_fd, uint64_t fan_mask, int in_fd, uint64_t in_mask) {
127
+	if (!pathname || fan_fd <= 0 || in_fd <= 0) return CL_ENULLARG;
128
+
129
+	int ret = CL_SUCCESS;
130
+	size_t len = strlen(pathname);
131
+
132
+	ret = onas_ddd_watch_hierarchy(pathname, len, in_fd, in_mask, ONAS_IN);
133
+	if (ret) return ret;
134
+
135
+	ret = onas_ddd_watch_hierarchy(pathname, len, fan_fd, fan_mask, ONAS_FAN);
136
+	if (ret) return ret;
137
+
138
+	return CL_SUCCESS;
139
+}
140
+
141
+static int onas_ddd_watch_hierarchy(const char* pathname, size_t len, int fd, uint64_t mask, uint32_t type) {
142
+
143
+	if (!pathname || fd <= 0 || !type) return CL_ENULLARG;
144
+
145
+	if (type == (ONAS_IN | ONAS_FAN)) return CL_EARG;
146
+
147
+	struct onas_hnode *hnode = NULL;
148
+	struct onas_element *elem = NULL;
149
+	int wd = 0;
150
+
151
+	if(onas_ht_get(ddd_ht, pathname, len, &elem) != CL_SUCCESS) return CL_EARG;
152
+
153
+	hnode = elem->data;
154
+
155
+	if (type & ONAS_IN) {
156
+		wd = inotify_add_watch(fd, pathname, (uint32_t) mask);
157
+
158
+		if (wd < 0) return CL_EARG;
159
+
160
+		if (wd >= wdlt_len) {
161
+			onas_ddd_grow_wdlt();
162
+		}
163
+
164
+		/* Link the hash node to the watch descriptor lookup table */
165
+		hnode->wd = wd;
166
+		wdlt[wd] = hnode->pathname;
167
+
168
+		hnode->watched |= ONAS_INWATCH;
169
+	} else if (type & ONAS_FAN) {
170
+		if(fanotify_mark(fd, FAN_MARK_ADD, mask, AT_FDCWD, hnode->pathname) < 0) return CL_EARG;
171
+		hnode->watched |= ONAS_FANWATCH;
172
+	} else {
173
+		return CL_EARG;
174
+	}
175
+
176
+	struct onas_lnode *curr = hnode->childhead;
177
+
178
+	while (curr->next != hnode->childtail) {
179
+		curr = curr->next;
180
+
181
+		size_t size = len + strlen(curr->dirname) + 2;
182
+		char *child_path = (char *) cli_malloc(size);
183
+		if (hnode->pathname[len-1] == '/')
184
+			snprintf(child_path, --size, "%s%s", hnode->pathname, curr->dirname);
185
+		else
186
+			snprintf(child_path, size, "%s/%s", hnode->pathname, curr->dirname);
187
+
188
+		if(onas_ddd_watch_hierarchy(child_path, strlen(child_path), fd, mask, type)) return CL_EARG;
189
+		free(child_path);
190
+	}
191
+
192
+	return CL_SUCCESS;
193
+}
194
+
195
+static int onas_ddd_unwatch(const char *pathname, int fan_fd, int in_fd) {
196
+	if (!pathname || fan_fd <= 0 || in_fd <= 0) return CL_ENULLARG;
197
+
198
+	int ret = CL_SUCCESS;
199
+	size_t len = strlen(pathname);
200
+
201
+	ret = onas_ddd_unwatch_hierarchy(pathname, len, in_fd, ONAS_IN);
202
+	if (ret) return ret;
203
+
204
+	ret = onas_ddd_unwatch_hierarchy(pathname, len,fan_fd, ONAS_FAN);
205
+	if (ret) return ret;
206
+
207
+	return CL_SUCCESS;
208
+}
209
+
210
+static int onas_ddd_unwatch_hierarchy(const char* pathname, size_t len, int fd, uint32_t type) {
211
+
212
+	if (!pathname || fd <= 0 || !type) return CL_ENULLARG;
213
+
214
+	if (type == (ONAS_IN | ONAS_FAN)) return CL_EARG;
215
+
216
+	struct onas_hnode *hnode = NULL;
217
+	struct onas_element *elem = NULL;
218
+	int wd = 0;
219
+
220
+	if(onas_ht_get(ddd_ht, pathname, len, &elem)) return CL_EARG;
221
+
222
+	hnode = elem->data;
223
+
224
+	if (type & ONAS_IN) {
225
+		wd = hnode->wd;
226
+
227
+		if(!inotify_rm_watch(fd, wd)) return CL_EARG;
228
+
229
+		/* Unlink the hash node from the watch descriptor lookup table */
230
+		hnode->wd = 0;
231
+		wdlt[wd] = NULL;
232
+
233
+		hnode->watched = ONAS_STOPWATCH;
234
+	} else if (type & ONAS_FAN) {
235
+		if(fanotify_mark(fd, FAN_MARK_REMOVE, 0, AT_FDCWD, hnode->pathname) < 0) return CL_EARG;
236
+		hnode->watched = ONAS_STOPWATCH;
237
+	} else {
238
+		return CL_EARG;
239
+	}
240
+
241
+	struct onas_lnode *curr = hnode->childhead;
242
+
243
+	while (curr->next != hnode->childtail) {
244
+		curr = curr->next;
245
+
246
+		size_t size = len + strlen(curr->dirname) + 2;
247
+		char *child_path = (char *) cli_malloc(size);
248
+		if (hnode->pathname[len-1] == '/')
249
+			snprintf(child_path, --size, "%s%s", hnode->pathname, curr->dirname);
250
+		else
251
+			snprintf(child_path, size, "%s/%s", hnode->pathname, curr->dirname);
252
+
253
+		onas_ddd_unwatch_hierarchy(child_path, strlen(child_path), fd, type);
254
+		free(child_path);
255
+	}
256
+
257
+	return CL_SUCCESS;
258
+}
259
+
260
+void *onas_ddd_th(void *arg) {
261
+	struct ddd_thrarg *tharg = (struct ddd_thrarg *) arg;
262
+	sigset_t sigset;
263
+	struct sigaction act;
264
+	const struct optstruct *pt;
265
+	short int scan;
266
+	int sizelimit = 0, extinfo;
267
+	STATBUF sb;
268
+	uint64_t in_mask = IN_ONLYDIR | IN_MOVE | IN_DELETE | IN_CREATE;
269
+	int in_fd;
270
+	fd_set rfds;
271
+	char buf[4096];
272
+	ssize_t bread;
273
+	const struct inotify_event *event;
274
+	int ret, len;
275
+
276
+	/* ignore all signals except SIGUSR1 */
277
+	sigfillset(&sigset);
278
+	sigdelset(&sigset, SIGUSR1);
279
+	/* The behavior of a process is undefined after it ignores a 
280
+	 * SIGFPE, SIGILL, SIGSEGV, or SIGBUS signal */
281
+	sigdelset(&sigset, SIGFPE);
282
+	sigdelset(&sigset, SIGILL);
283
+	sigdelset(&sigset, SIGSEGV);
284
+#ifdef SIGBUS    
285
+	sigdelset(&sigset, SIGBUS);
286
+#endif
287
+	pthread_sigmask(SIG_SETMASK, &sigset, NULL);
288
+	memset(&act, 0, sizeof(struct sigaction));
289
+	act.sa_handler = onas_ddd_exit;
290
+	sigfillset(&(act.sa_mask));
291
+	sigaction(SIGUSR1, &act, NULL);
292
+	sigaction(SIGSEGV, &act, NULL);
293
+
294
+	in_fd = inotify_init1(IN_NONBLOCK);
295
+	//in_fd = inotify_init();
296
+	if (in_fd == -1) {
297
+		logg("!ScanOnAccess: Could not init inotify.");
298
+		return NULL;
299
+	}
300
+
301
+	ret = onas_ddd_init(0, ONAS_DEFAULT_HT_SIZE);
302
+	if (ret) {
303
+		logg("!ScanOnAccess: Failed to initialize 3D. \n");
304
+		return NULL;
305
+	}
306
+
307
+	/* Add provided paths recursively. */
308
+	if((pt = optget(tharg->opts, "OnAccessIncludePath"))->enabled) {
309
+		while(pt) {
310
+			if(onas_ht_get(ddd_ht, pt->strarg, strlen(pt->strarg), NULL) != CL_SUCCESS) {
311
+				if(onas_ht_add_hierarchy(ddd_ht, pt->strarg)) {
312
+					logg("!ScanOnAccess: Can't include path '%s'\n", pt->strarg);
313
+					return NULL;
314
+				}
315
+			}
316
+
317
+			pt = (struct optstruct *) pt->nextarg;
318
+		}
319
+	} else {
320
+		logg("!ScanOnAccess: Please specify at least one path with OnAccessIncludePath\n");
321
+		return NULL;
322
+	}
323
+
324
+	/* Remove provided paths recursively. */
325
+	if((pt = optget(tharg->opts, "OnAccessExcludePath"))->enabled) {
326
+		while(pt) {
327
+			size_t ptlen = strlen(pt->strarg);
328
+			if(onas_ht_get(ddd_ht, pt->strarg, ptlen, NULL) == CL_SUCCESS) {
329
+				if(onas_ht_rm_hierarchy(ddd_ht, pt->strarg, ptlen, 0)) {
330
+					logg("!ScanOnAccess: Can't exclude path '%s'\n", pt->strarg);
331
+					return NULL;
332
+				}
333
+			}
334
+
335
+			pt = (struct optstruct *) pt->nextarg;
336
+		}
337
+	}
338
+
339
+	/* Watch provided paths recursively */
340
+	if((pt = optget(tharg->opts, "OnAccessIncludePath"))->enabled) {
341
+		while(pt) {
342
+			size_t ptlen = strlen(pt->strarg);
343
+			if(onas_ht_get(ddd_ht, pt->strarg, ptlen, NULL) == CL_SUCCESS) {
344
+				if(onas_ddd_watch(pt->strarg, tharg->fan_fd, tharg->fan_mask, in_fd, in_mask)) {
345
+					logg("!ScanOnAccess: Could not watch path '%s', %s\n", pt->strarg, strerror(errno));
346
+					return NULL;
347
+				}
348
+			}
349
+
350
+			/*onas_print_ht(ddd_ht, pt->strarg, 0);*/
351
+			pt = (struct optstruct *) pt->nextarg;
352
+		}
353
+	}
354
+
355
+
356
+	FD_ZERO(&rfds);
357
+	FD_SET(in_fd, &rfds);
358
+
359
+	while (1) {
360
+		do {
361
+			ret = select(in_fd + 1, &rfds, NULL, NULL, NULL);
362
+		} while(ret == -1 && errno == EINTR);
363
+
364
+		while((bread = read(in_fd, buf, sizeof(buf))) > 0) {
365
+
366
+			/* Handle events. */
367
+			int wd;
368
+			char *p = buf;
369
+			const char *path = NULL;
370
+			const char *child = NULL;
371
+			for(p; p < buf + bread; p += sizeof(struct inotify_event) + event->len) {
372
+
373
+				event = (const struct inotify_event *) p;
374
+				wd = event->wd;
375
+				path = wdlt[wd];
376
+				child = event->name;
377
+
378
+				len = strlen(path);
379
+				size_t size = strlen(child) + len + 2;
380
+				char *child_path = (char *) cli_malloc(size);
381
+				if (path[len-1] == '/')
382
+					snprintf(child_path, --size, "%s%s", path, child);
383
+				else
384
+					snprintf(child_path, size, "%s/%s", path, child);
385
+
386
+				struct stat s;
387
+				if(stat(child_path, &s) == 0 && S_ISREG(s.st_mode)) continue;
388
+				if(!(event->mask & IN_ISDIR)) continue;
389
+
390
+				if (event->mask & IN_DELETE) {
391
+					logg("*ddd: DELETE - Removing %s from %s with wd:%d\n", child_path, path, wd);
392
+					onas_ddd_unwatch(child_path, tharg->fan_fd, in_fd);
393
+					onas_ht_rm_hierarchy(ddd_ht, child_path, strlen(child_path), 0);
394
+
395
+				} else if (event->mask & IN_MOVED_FROM) {
396
+					logg("*ddd: MOVED_FROM - Removing %s from %s with wd:%d\n", child_path, path, wd);
397
+					onas_ddd_unwatch(child_path, tharg->fan_fd, in_fd);
398
+					onas_ht_rm_hierarchy(ddd_ht, child_path, strlen(child_path), 0);
399
+
400
+				} else if (event->mask & IN_CREATE) {
401
+					logg("*ddd: CREATE - Adding %s to %s with wd:%d\n", child_path, path, wd);
402
+					onas_ht_add_hierarchy(ddd_ht, child_path);
403
+					onas_ddd_watch(child_path, tharg->fan_fd, tharg->fan_mask, in_fd, in_mask);
404
+
405
+				} else if (event->mask & IN_MOVED_TO) {
406
+					logg("*ddd: MOVED_TO - Adding %s to %s with wd:%d\n", child_path, path, wd);
407
+					onas_ht_add_hierarchy(ddd_ht, child_path);
408
+					onas_ddd_watch(child_path, tharg->fan_fd, tharg->fan_mask, in_fd, in_mask);
409
+				}
410
+			}
411
+		}
412
+	}
413
+
414
+	return NULL;
415
+}
416
+
417
+static void onas_ddd_exit(int sig) {
418
+	logg("*ScanOnAccess: onas_ddd_exit(), signal %d\n", sig);
419
+	pthread_exit(NULL);
420
+	logg("ScanOnAccess: stopped\n");
421
+}
422
+
423
+#endif
0 424
new file mode 100644
... ...
@@ -0,0 +1,53 @@
0
+/*
1
+ *  Copyright (C) 2011 Sourcefire, Inc.
2
+ *
3
+ *  Authors: Tomasz Kojm
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_IN_H
21
+#define __ONAS_IN_H
22
+
23
+#define ONAS_IN 0x01
24
+#define ONAS_FAN 0x02
25
+
26
+#define MAX_WATCH_LEN 7
27
+
28
+struct ddd_thrarg {
29
+	int sid;
30
+	int options;
31
+	int fan_fd;
32
+	uint64_t fan_mask;
33
+	const struct optstruct *opts;
34
+	const struct cl_engine *engine;
35
+	const struct cl_limits *limits;
36
+};
37
+
38
+static int onas_ddd_init_ht(uint32_t ht_size);
39
+static int onas_ddd_init_wdlt(uint32_t nwatches);
40
+static int onas_ddd_grow_wdlt();
41
+
42
+static int onas_ddd_watch(const char *pathname, int fan_fd, uint64_t fan_mask, int in_fd, uint64_t in_mask);
43
+static int onas_ddd_watch_hierarchy(const char* pathname, size_t len, int fd, uint64_t mask, uint32_t type);
44
+static int onas_ddd_unwatch(const char *pathname, int fan_fd, int in_fd);
45
+static int onas_ddd_unwatch_hierarchy(const char* pathname, size_t len, int fd, uint32_t type);
46
+
47
+int onas_ddd_init(uint32_t nwatches, size_t ht_size);
48
+void *onas_ddd_th(void *arg);
49
+static void onas_ddd_exit(int sig);
50
+
51
+
52
+#endif
0 53
new file mode 100644
1 54
Binary files /dev/null and b/clamd/onaccess_ddd.o differ
2 55
new file mode 100644
... ...
@@ -0,0 +1,366 @@
0
+/*
1
+ *  Copyright (C) 2011 Sourcefire, Inc.
2
+ *
3
+ *  Authors: Tomasz Kojm, 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 <sys/types.h>
29
+#include <sys/stat.h>
30
+#include <fcntl.h>
31
+#include <signal.h>
32
+#include <pthread.h>
33
+#include <string.h>
34
+#include <errno.h>
35
+
36
+#include <sys/fanotify.h>
37
+
38
+#include "libclamav/clamav.h"
39
+#include "libclamav/scanners.h"
40
+
41
+#include "shared/optparser.h"
42
+#include "shared/output.h"
43
+
44
+#include "server.h"
45
+#include "others.h"
46
+#include "scanner.h"
47
+
48
+#include "onaccess_fan.h"
49
+#include "onaccess_hash.h"
50
+#include "onaccess_ddd.h"
51
+
52
+static pthread_t ddd_pid;
53
+
54
+static void onas_fan_exit(int sig)
55
+{
56
+
57
+	logg("*ScanOnAccess: onas_fan_exit(), signal %d\n", sig);
58
+	
59
+	pthread_kill(ddd_pid, SIGUSR1);
60
+	pthread_join(ddd_pid, NULL);
61
+
62
+	pthread_exit(NULL);
63
+	logg("ScanOnAccess: stopped\n");
64
+}
65
+
66
+static int onas_fan_scanfile(int fan_fd, const char *fname, struct fanotify_event_metadata *fmd, int scan, int extinfo, struct thrarg *tharg)
67
+{
68
+	struct fanotify_response res;
69
+	struct cb_context context;
70
+	const char *virname;
71
+	int ret = 0;
72
+	struct thrarg *dummy = tharg;
73
+
74
+    res.fd = fmd->fd;
75
+    res.response = FAN_ALLOW;
76
+    context.filename = fname;
77
+    context.virsize = 0;
78
+    if(scan && cl_scandesc_callback(fmd->fd, &virname, NULL, tharg->engine, tharg->options, &context) == CL_VIRUS) {
79
+	if(extinfo && context.virsize)
80
+	    logg("ScanOnAccess: %s: %s(%s:%llu) FOUND\n", fname, virname, context.virhash, context.virsize);
81
+	else
82
+	    logg("ScanOnAccess: %s: %s FOUND\n", fname, virname);
83
+	virusaction(fname, virname, tharg->opts);
84
+	res.response = FAN_DENY;
85
+    }
86
+
87
+    if(fmd->mask & FAN_ALL_PERM_EVENTS) {
88
+	ret = write(fan_fd, &res, sizeof(res));
89
+	if(ret == -1)
90
+	    logg("!ScanOnAccess: Internal error (can't write to fanotify)\n");
91
+    }
92
+
93
+    return ret;
94
+}
95
+
96
+void *onas_fan_th(void *arg)
97
+{
98
+	struct thrarg *tharg = (struct thrarg *) arg;
99
+	sigset_t sigset;
100
+        struct sigaction act;
101
+	const struct optstruct *pt;
102
+	short int scan;
103
+	int sizelimit = 0, extinfo;
104
+	STATBUF sb;
105
+        uint64_t fan_mask = FAN_OPEN_PERM | FAN_ACCESS_PERM | FAN_EVENT_ON_CHILD;
106
+	int fan_fd;
107
+        fd_set rfds;
108
+	char buf[4096];
109
+	ssize_t bread;
110
+	struct fanotify_event_metadata *fmd;
111
+	char fname[1024];
112
+	int ret, len;
113
+	char err[128];
114
+
115
+	pthread_attr_t ddd_attr;
116
+	struct ddd_thrarg *ddd_tharg = NULL;
117
+
118
+    /* ignore all signals except SIGUSR1 */
119
+    sigfillset(&sigset);
120
+    sigdelset(&sigset, SIGUSR1);
121
+    /* The behavior of a process is undefined after it ignores a 
122
+     * SIGFPE, SIGILL, SIGSEGV, or SIGBUS signal */
123
+    sigdelset(&sigset, SIGFPE);
124
+    sigdelset(&sigset, SIGILL);
125
+    sigdelset(&sigset, SIGSEGV);
126
+#ifdef SIGBUS    
127
+    sigdelset(&sigset, SIGBUS);
128
+#endif
129
+    pthread_sigmask(SIG_SETMASK, &sigset, NULL);
130
+    memset(&act, 0, sizeof(struct sigaction));
131
+    act.sa_handler = onas_fan_exit;
132
+    sigfillset(&(act.sa_mask));
133
+    sigaction(SIGUSR1, &act, NULL);
134
+    sigaction(SIGSEGV, &act, NULL);
135
+
136
+    /* Initialize fanotify */
137
+    fan_fd = fanotify_init(FAN_CLASS_CONTENT | FAN_UNLIMITED_QUEUE | FAN_UNLIMITED_MARKS, O_RDONLY);
138
+    if(fan_fd < 0) {
139
+	logg("!ScanOnAccess: fanotify_init failed: %s\n", cli_strerror(errno, err, sizeof(err)));
140
+	if(errno == EPERM)
141
+	    logg("ScanOnAccess: clamd must be started by root\n");
142
+	return NULL;
143
+    }
144
+
145
+    do {
146
+	    if(pthread_attr_init(&ddd_attr)) break;
147
+	    pthread_attr_setdetachstate(&ddd_attr, PTHREAD_CREATE_JOINABLE);
148
+
149
+	    if(!(ddd_tharg = (struct ddd_thrarg *) malloc(sizeof(struct ddd_thrarg)))) break;
150
+
151
+	    ddd_tharg->fan_fd = fan_fd;
152
+	    ddd_tharg->fan_mask = fan_mask;
153
+	    ddd_tharg->opts = tharg->opts;
154
+	    ddd_tharg->engine = tharg->engine;
155
+	    ddd_tharg->options = tharg->options;
156
+
157
+	    if(!pthread_create(&ddd_pid, &ddd_attr, onas_ddd_th, ddd_tharg)) break;
158
+
159
+	    free(ddd_tharg);
160
+	    ddd_tharg=NULL;
161
+    } while(0);
162
+    if (!tharg) logg("!Unable to start dynamic directory determination.\n");
163
+
164
+    /* Load other options. */
165
+    sizelimit = optget(tharg->opts, "OnAccessMaxFileSize")->numarg;
166
+    if(sizelimit)
167
+	logg("ScanOnAccess: Max file size limited to %d bytes\n", sizelimit);
168
+    else
169
+	logg("ScanOnAccess: File size limit disabled\n");
170
+
171
+    extinfo = optget(tharg->opts, "ExtendedDetectionInfo")->enabled;
172
+
173
+    FD_ZERO(&rfds);
174
+    FD_SET(fan_fd, &rfds);
175
+    do {
176
+        ret = select(fan_fd + 1, &rfds, NULL, NULL, NULL);
177
+    } while(ret == -1 && errno == EINTR);
178
+
179
+    while((bread = read(fan_fd, buf, sizeof(buf))) > 0) {
180
+	fmd = (struct fanotify_event_metadata *) buf;
181
+	while(FAN_EVENT_OK(fmd, bread)) {
182
+	    scan = 1;
183
+	    if(fmd->fd >= 0) {
184
+		sprintf(fname, "/proc/self/fd/%d", fmd->fd);
185
+		len = readlink(fname, fname, sizeof(fname) - 1);
186
+		if(len == -1) {
187
+		    close(fmd->fd);
188
+		    logg("!ScanOnAccess: Internal error (readlink() failed)\n");
189
+		    return NULL;
190
+		}
191
+		fname[len] = 0;
192
+
193
+		if(onas_fan_checkowner(fmd->pid, tharg->opts)) {
194
+		    scan = 0;
195
+		    logg("*ScanOnAccess: %s skipped (excluded UID)\n", fname);
196
+		}
197
+
198
+		if(sizelimit) {
199
+		    if(FSTAT(fmd->fd, &sb) != 0 || sb.st_size > sizelimit) {
200
+			scan = 0;
201
+			/* logg("*ScanOnAccess: %s skipped (size > %d)\n", fname, sizelimit); */
202
+		    }
203
+		}
204
+
205
+		if(onas_fan_scanfile(fan_fd, fname, fmd, scan, extinfo, tharg) == -1) {
206
+		    close(fmd->fd);
207
+		    return NULL;
208
+		}
209
+
210
+		if(close(fmd->fd) == -1) {
211
+		    printf("!ScanOnAccess: Internal error (close(%d) failed)\n", fmd->fd);
212
+		    close(fmd->fd);
213
+		    return NULL;
214
+		}
215
+	    }
216
+	    fmd = FAN_EVENT_NEXT(fmd, bread);
217
+	}
218
+	do {
219
+	    ret = select(fan_fd + 1, &rfds, NULL, NULL, NULL);
220
+	} while(ret == -1 && errno == EINTR);
221
+    }
222
+
223
+    if(bread < 0)
224
+	logg("!ScanOnAccess: Internal error (failed to read data)\n");
225
+
226
+    return NULL;
227
+}
228
+
229
+#elif defined(CLAMAUTH)
230
+
231
+#include <stdio.h>
232
+#include <unistd.h>
233
+#include <sys/types.h>
234
+#include <sys/stat.h>
235
+#include <sys/uio.h>
236
+#include <fcntl.h>
237
+#include <signal.h>
238
+#include <pthread.h>
239
+#include <string.h>
240
+#include <errno.h>
241
+
242
+#include "libclamav/clamav.h"
243
+#include "libclamav/scanners.h"
244
+
245
+#include "shared/optparser.h"
246
+#include "shared/output.h"
247
+
248
+#include "server.h"
249
+#include "others.h"
250
+#include "scanner.h"
251
+
252
+#define SUPPORTED_PROTOCOL  2
253
+
254
+static int cauth_fd = -1;
255
+
256
+struct ClamAuthEvent {
257
+    unsigned int action;
258
+    char path[1024];
259
+    unsigned int pid;
260
+};
261
+
262
+static void cauth_exit(int sig)
263
+{
264
+    logg("*ScanOnAccess: cauth_exit(), signal %d\n", sig);
265
+    if(cauth_fd > 0)
266
+	close(cauth_fd);
267
+    pthread_exit(NULL);
268
+    logg("ScanOnAccess: stopped\n");
269
+}
270
+
271
+static int cauth_scanfile(const char *fname, int extinfo, struct thrarg *tharg)
272
+{
273
+	struct cb_context context;
274
+	const char *virname;
275
+	int ret = 0, fd;
276
+
277
+    context.filename = fname;
278
+    context.virsize = 0;
279
+
280
+    fd = open(fname, O_RDONLY);
281
+    if(fd == -1)
282
+	return -1;
283
+
284
+    if(cl_scandesc_callback(fd, &virname, NULL, tharg->engine, tharg->options, &context) == CL_VIRUS) {
285
+	if(extinfo && context.virsize)
286
+	    logg("ScanOnAccess: %s: %s(%s:%llu) FOUND\n", fname, virname, context.virhash, context.virsize);
287
+	else
288
+	    logg("ScanOnAccess: %s: %s FOUND\n", fname, virname);
289
+	virusaction(fname, virname, tharg->opts);
290
+    }
291
+    close(fd);
292
+    return ret;
293
+}
294
+
295
+void *onas_fan_th(void *arg)
296
+{
297
+	struct thrarg *tharg = (struct thrarg *) arg;
298
+	sigset_t sigset;
299
+        struct sigaction act;
300
+	int eventcnt = 1, extinfo;
301
+	char err[128];
302
+	struct ClamAuthEvent event;
303
+
304
+    /* ignore all signals except SIGUSR1 */
305
+    sigfillset(&sigset);
306
+    sigdelset(&sigset, SIGUSR1);
307
+    /* The behavior of a process is undefined after it ignores a 
308
+     * SIGFPE, SIGILL, SIGSEGV, or SIGBUS signal */
309
+    sigdelset(&sigset, SIGFPE);
310
+    sigdelset(&sigset, SIGILL);
311
+    sigdelset(&sigset, SIGSEGV);
312
+#ifdef SIGBUS    
313
+    sigdelset(&sigset, SIGBUS);
314
+#endif
315
+    pthread_sigmask(SIG_SETMASK, &sigset, NULL);
316
+    memset(&act, 0, sizeof(struct sigaction));
317
+    act.sa_handler = cauth_exit;
318
+    sigfillset(&(act.sa_mask));
319
+    sigaction(SIGUSR1, &act, NULL);
320
+    sigaction(SIGSEGV, &act, NULL);
321
+
322
+    extinfo = optget(tharg->opts, "ExtendedDetectionInfo")->enabled;
323
+
324
+    cauth_fd = open("/dev/clamauth", O_RDONLY);
325
+    if(cauth_fd == -1) {
326
+	logg("!ScanOnAccess: Can't open /dev/clamauth\n");
327
+	if(errno == ENOENT)
328
+	    logg("!ScanOnAccess: Please make sure ClamAuth.kext is loaded\n");
329
+	else if(errno == EACCES)
330
+	    logg("!ScanOnAccess: This application requires root privileges\n");
331
+	else
332
+	    logg("!ScanOnAccess: /dev/clamauth: %s\n", cli_strerror(errno, err, sizeof(err)));
333
+
334
+	return NULL;
335
+    }
336
+
337
+    while(1) {
338
+	if(read(cauth_fd, &event, sizeof(event)) > 0) {
339
+	    if(eventcnt == 1) {
340
+		if(event.action != SUPPORTED_PROTOCOL) {
341
+		    logg("!ScanOnAccess: Protocol version mismatch (tool: %d, driver: %d)\n", SUPPORTED_PROTOCOL, event.action);
342
+		    close(cauth_fd);
343
+		    return NULL;
344
+		}
345
+		if(strncmp(event.path, "ClamAuth", 8)) {
346
+		    logg("!ScanOnAccess: Invalid version event\n");
347
+		    close(cauth_fd);
348
+		    return NULL;
349
+		}
350
+		logg("ScanOnAccess: Driver version: %s, protocol version: %d\n", &event.path[9], event.action);
351
+	    } else {
352
+		cauth_scanfile(event.path, extinfo, tharg);
353
+	    }
354
+	    eventcnt++;
355
+	} else {
356
+	    if(errno == ENODEV) {
357
+		printf("^ScanOnAccess: ClamAuth module deactivated, terminating\n");
358
+		close(cauth_fd);
359
+		return NULL;
360
+	    }
361
+	}
362
+	usleep(200);
363
+    }
364
+}
365
+#endif
0 366
new file mode 100644
... ...
@@ -0,0 +1,30 @@
0
+/*
1
+ *  Copyright (C) 2011 Sourcefire, Inc.
2
+ *
3
+ *  Authors: Tomasz Kojm
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 __FAN_H
21
+#define __FAN_H
22
+
23
+void *onas_fan_th(void *arg);
24
+
25
+// PROTOTYPING
26
+
27
+void *print_tree(struct optstruct *opt);
28
+
29
+#endif
0 30
new file mode 100644
1 31
Binary files /dev/null and b/clamd/onaccess_fan.o differ
2 32
new file mode 100644
... ...
@@ -0,0 +1,689 @@
0
+/*
1
+ *  Copyright (C) 2015 Sourcefire, Inc.
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
+#define PROTOTYPING 1
25
+#if PROTOTYPING
26
+
27
+#include <stdio.h>
28
+#include <unistd.h>
29
+#include <sys/types.h>
30
+#include <sys/stat.h>
31
+#include <fts.h>
32
+#include <fcntl.h>
33
+#include <signal.h>
34
+#include <pthread.h>
35
+#include <string.h>
36
+#include <errno.h>
37
+#include <stdbool.h>
38
+
39
+#include <sys/fanotify.h>
40
+
41
+#include "onaccess_fan.h"
42
+#include "onaccess_hash.h"
43
+#include "onaccess_ddd.h"
44
+
45
+#include "libclamav/clamav.h"
46
+#include "libclamav/scanners.h"
47
+
48
+#include "shared/optparser.h"
49
+#include "shared/output.h"
50
+
51
+#include "server.h"
52
+#include "others.h"
53
+#include "scanner.h"
54
+
55
+
56
+
57
+static inline uint32_t onas_hshift(uint32_t hash) {
58
+
59
+	hash = ~hash;
60
+
61
+	hash += (hash << 15);
62
+	hash ^= (hash >> 12);
63
+	hash += (hash << 2);
64
+	hash ^= (hash >> 4);
65
+	hash += (hash << 3);
66
+	hash += (hash << 11);
67
+	hash ^= (hash >> 16);
68
+
69
+	return hash;
70
+}
71
+
72
+static inline int onas_hash(const char* key, size_t keylen, uint32_t size) {
73
+
74
+	uint32_t hash = 1;
75
+	int i;
76
+
77
+	for (i = 0; i < keylen; i++) {
78
+		hash += key[i];
79
+		hash = onas_hshift(hash);
80
+	}
81
+
82
+	return hash & (size - 1);
83
+}
84
+
85
+int onas_ht_init(struct onas_ht **ht, uint32_t size) {
86
+
87
+	if (size == 0 || (size & (~size + 1)) != size) return CL_EARG;
88
+
89
+	*ht = (struct onas_ht *) cli_malloc(sizeof(struct onas_ht));
90
+	if (!(*ht)) return CL_EMEM;
91
+
92
+	**ht = (struct onas_ht) {
93
+		.htable = NULL,
94
+		.size = size,
95
+		.nbckts = 0,
96
+		.col = 0
97
+	};
98
+
99
+	if (!((*ht)->htable = (struct onas_bucket **) cli_calloc(size, sizeof(struct onas_bucket *)))) {
100
+		onas_free_ht(*ht);
101
+		return CL_EMEM;
102
+	}
103
+
104
+	return CL_SUCCESS;
105
+}
106
+
107
+void onas_free_ht(struct onas_ht *ht) {
108
+
109
+	if (!ht || ht->size == 0) return;
110
+
111
+	if (!ht->htable) {
112
+		free(ht);
113
+		return;
114
+	}
115
+
116
+	int i = 0;
117
+	for (i = 0; i < ht->size; i++) {
118
+		onas_free_bucket(ht->htable[i]);
119
+		ht->htable[i] = NULL;
120
+	}
121
+
122
+	free(ht->htable);
123
+	ht->htable = NULL;
124
+
125
+	free(ht);
126
+
127
+	return;
128
+}
129
+
130
+static struct onas_bucket *onas_bucket_init() {
131
+
132
+	struct onas_bucket *bckt = (struct onas_bucket*) cli_malloc(sizeof(struct onas_bucket));
133
+	if (!bckt) return NULL;
134
+
135
+	*bckt = (struct onas_bucket) {
136
+		.size = 0,
137
+		.head = NULL,
138
+		.tail = NULL
139
+	};
140
+
141
+	return bckt;
142
+}
143
+
144
+static void onas_free_bucket(struct onas_bucket *bckt) {
145
+
146
+	if (!bckt) return;
147
+
148
+	int i = 0;
149
+	struct onas_element *curr = NULL;
150
+
151
+	for (i = 0; i < bckt->size; i++) {
152
+		curr = bckt->head;
153
+		bckt->head = curr->next;
154
+		onas_free_element(curr);
155
+		curr = NULL;
156
+	}
157
+
158
+	free(bckt);
159
+
160
+	return;
161
+}
162
+
163
+struct onas_element *onas_element_init(struct onas_hnode *value, const char *key, size_t klen) {
164
+
165
+	struct onas_element *elem = (struct onas_element *) cli_malloc(sizeof(struct onas_element));
166
+	if (!elem) return NULL;
167
+
168
+	*elem = (struct onas_element) {
169
+		.key = key,
170
+		.klen = klen,
171
+		.data = value,
172
+		.next = NULL,
173
+		.prev = NULL
174
+	};
175
+
176
+	return elem;
177
+}
178
+
179
+void onas_free_element(struct onas_element *elem) {
180
+
181
+	if (!elem) return;
182
+
183
+	onas_free_hashnode(elem->data);
184
+
185
+	elem->prev = NULL;
186
+	elem->next = NULL;
187
+
188
+	free(elem);
189
+
190
+	return;
191
+}
192
+
193
+int onas_ht_insert(struct onas_ht *ht, struct onas_element *elem) {
194
+
195
+	if (!ht || !elem || !elem->key) return CL_ENULLARG;
196
+
197
+	int idx = onas_hash(elem->key, elem->klen, ht->size);
198
+	struct onas_bucket *bckt = ht->htable[idx];
199
+
200
+	int ret = 0;
201
+	int bsize = 0;
202
+
203
+	if (bckt == NULL) {
204
+		ht->htable[idx] = onas_bucket_init();
205
+		bckt = ht->htable[idx];
206
+	} else {
207
+		ht->col++;
208
+	}
209
+
210
+	bsize = bckt->size;
211
+	ret = onas_bucket_insert(bckt, elem);
212
+
213
+	if (ret == CL_SUCCESS)
214
+		if (bsize < bckt->size)
215
+			ht->nbckts++;
216
+
217
+	return ret;
218
+}
219
+
220
+static int onas_bucket_insert(struct onas_bucket *bckt, struct onas_element *elem) {
221
+	if (!bckt || !elem) return CL_ENULLARG;
222
+
223
+	if (bckt->size == 0) {
224
+		bckt->head = elem;
225
+		bckt->tail = elem;
226
+		elem->prev = NULL;
227
+		elem->next = NULL;
228
+		bckt->size++;
229
+	} else {
230
+		struct onas_element *btail = bckt->tail;
231
+
232
+		btail->next = elem;
233
+		elem->prev = btail;
234
+		elem->next = NULL;
235
+		bckt->tail = elem;
236
+		bckt->size++;
237
+	}
238
+
239
+	return CL_SUCCESS;
240
+}
241
+
242
+/* Checks if key exists and optionally stores address to the element corresponding to the key within elem */
243
+int onas_ht_get(struct onas_ht *ht, const char *key, size_t klen, struct onas_element **elem) {
244
+
245
+	if (elem) *elem = NULL;
246
+
247
+	if (!ht || !key || klen <= 0) return CL_ENULLARG;
248
+
249
+	struct onas_bucket *bckt = ht->htable[onas_hash(key, klen, ht->size)];
250
+
251
+	if (!bckt || bckt->size == 0) return CL_EARG;
252
+
253
+	struct onas_element *curr = bckt->head;
254
+
255
+	while (curr && strncmp(curr->key, key, klen)) {
256
+		curr = curr->next;
257
+	}
258
+	
259
+	if (!curr) return CL_EARG;
260
+
261
+	if (elem) *elem = curr;
262
+
263
+	return CL_SUCCESS;
264
+}
265
+
266
+/* Removes the element corresponding to key from the hashtable and optionally returns a pointer to the removed element. */
267
+int onas_ht_remove(struct onas_ht *ht, const char* key, size_t klen, struct onas_element **relem) {
268
+	if (!ht || !key || klen <= 0) return CL_ENULLARG;
269
+
270
+	struct onas_bucket *bckt = ht->htable[onas_hash(key, klen, ht->size)];
271
+
272
+	if (!bckt) return CL_EARG;
273
+
274
+	struct onas_element *elem = NULL;
275
+	onas_ht_get(ht, key, klen, &elem);
276
+
277
+	if (!elem) return CL_EARG;
278
+
279
+	int ret = onas_bucket_remove(bckt, elem);
280
+
281
+	if (relem) *relem = elem;
282
+
283
+	return ret;
284
+}
285
+
286
+static int onas_bucket_remove(struct onas_bucket *bckt, struct onas_element *elem) {
287
+	if (!bckt || !elem) return CL_ENULLARG;
288
+
289
+	struct onas_element *curr = bckt->head;
290
+
291
+	while (curr && curr != elem) {
292
+		curr = curr->next;
293
+	}
294
+
295
+	if (!curr) return CL_EARG;
296
+
297
+	if (bckt->head == elem) {
298
+		bckt->head = elem->next;
299
+		if (bckt->head) bckt->head->prev = NULL;
300
+
301
+		elem->next = NULL;
302
+	} else if (bckt->tail == elem) {
303
+		bckt->tail = elem->prev;
304
+		if (bckt->tail) bckt->tail->next = NULL;
305
+
306
+		elem->prev = NULL;
307
+	} else {
308
+		struct onas_element *tmp = NULL;
309
+
310
+		tmp = elem->prev;
311
+		if (tmp) {
312
+			tmp->next = elem->next;
313
+			tmp = elem->next;
314
+			tmp->prev = elem->prev;
315
+		}
316
+
317
+		elem->prev = NULL;
318
+		elem->next = NULL;
319
+	}
320
+
321
+	bckt->size--;
322
+
323
+	return CL_SUCCESS;
324
+}
325
+
326
+
327
+/* Dealing with hash nodes and list nodes */
328
+
329
+/* Function to initialize hashnode. */
330
+struct onas_hnode *onas_hashnode_init() {
331
+	struct onas_hnode *hnode = NULL;
332
+	if(!(hnode = (struct onas_hnode *) cli_malloc(sizeof(struct onas_hnode)))) {
333
+		return NULL;
334
+	}
335
+
336
+	*hnode = (struct onas_hnode) {
337
+		.pathlen = 0,
338
+		.pathname = NULL,
339
+		.prnt_pathlen = 0,
340
+		.prnt_pathname = NULL,
341
+		.childhead = NULL,
342
+		.childtail = NULL,
343
+		.wd = 0,
344
+		.watched = 0
345
+	};
346
+
347
+	if (!(hnode->childhead = (struct onas_lnode *) onas_listnode_init())) {
348
+		onas_free_hashnode(hnode);
349
+		return NULL;
350
+	}
351
+
352
+	if (!(hnode->childtail = (struct onas_lnode *) onas_listnode_init())) {
353
+		onas_free_hashnode(hnode);
354
+		return NULL;
355
+	}
356
+
357
+	hnode->childhead->next = (struct onas_lnode *) hnode->childtail;
358
+	hnode->childtail->prev = (struct onas_lnode *) hnode->childhead;
359
+
360
+	return hnode;
361
+}
362
+
363
+/* Function to initialize listnode. */
364
+struct onas_lnode *onas_listnode_init() {
365
+	struct onas_lnode *lnode = NULL;
366
+	if(!(lnode = (struct onas_lnode *) cli_malloc(sizeof(struct onas_lnode)))) {
367
+		return NULL;
368
+	}
369
+
370
+	*lnode = (struct onas_lnode) {
371
+		.dirname = NULL,
372
+		.next = NULL,
373
+		.prev = NULL
374
+	};
375
+
376
+	return lnode;
377
+}
378
+
379
+/* Function to free hashnode. */
380
+void onas_free_hashnode(struct onas_hnode *hnode) {
381
+	if (!hnode) return;
382
+
383
+	onas_free_dirlist(hnode->childhead);
384
+	hnode->childhead = NULL;
385
+
386
+	free(hnode->pathname);
387
+	hnode->pathname = NULL;
388
+
389
+	free(hnode->prnt_pathname);
390
+	hnode->prnt_pathname = NULL;
391
+
392
+	free(hnode);
393
+
394
+	return;
395
+}
396
+
397
+
398
+/* Function to free list of listnodes. */
399
+void onas_free_dirlist(struct onas_lnode *head) {
400
+	if (!head) return;
401
+	struct onas_lnode *curr = head;
402
+	struct onas_lnode *tmp = curr;
403
+
404
+	while(curr) {
405
+		tmp = curr->next;
406
+		onas_free_listnode(curr);
407
+		curr = tmp;
408
+	}
409
+
410
+	return;
411
+}
412
+
413
+/* Function to free a listnode. */
414
+void onas_free_listnode(struct onas_lnode *lnode) {
415
+	if (!lnode) return;
416
+
417
+	lnode->next = NULL;
418
+	lnode->prev = NULL;
419
+
420
+	free(lnode->dirname);
421
+	lnode->dirname = NULL;
422
+
423
+	free(lnode);
424
+
425
+	return;
426
+}
427
+
428
+static int onas_add_hashnode_child(struct onas_hnode *node, const char* dirname) {
429
+	if (!node || !dirname) return CL_ENULLARG;
430
+
431
+	struct onas_lnode *child = onas_listnode_init();
432
+	if (!child) return CL_EMEM;
433
+	
434
+	size_t n = strlen(dirname);
435
+	child->dirname = strndup(dirname, n);
436
+
437
+	onas_add_listnode(node->childtail, child);
438
+
439
+	return CL_SUCCESS;
440
+}
441
+
442
+/* Function to add a dir_listnode to a list */
443
+int onas_add_listnode(struct onas_lnode *tail, struct onas_lnode *node) {
444
+	if (!tail || !node) return CL_ENULLARG;
445
+
446
+	struct onas_lnode *tmp = tail->prev;
447
+
448
+	tmp->next = node;
449
+	node->prev = tmp->prev;
450
+
451
+	node->next = tail;
452
+	tail->prev = node;
453
+
454
+	return CL_SUCCESS;
455
+}
456
+
457
+/* Function to remove a listnode based on dirname. */
458
+int onas_rm_listnode(struct onas_lnode *head, const char *dirname) {
459
+	if (!dirname || !head) return CL_ENULLARG;
460
+
461
+	struct onas_lnode *curr = head;
462
+	size_t n = strlen(dirname);
463
+
464
+	while (curr = curr->next) {
465
+		if (!strncmp(curr->dirname, dirname, n)) {
466
+			struct onas_lnode *tmp = curr->prev;
467
+			tmp->next = curr->next;
468
+			tmp = curr->next;
469
+			tmp->prev = curr->prev;
470
+
471
+			onas_free_listnode(curr);
472
+
473
+			return CL_SUCCESS;
474
+		}
475
+	}
476
+
477
+	return -1;
478
+}
479
+
480
+/* PROTO STUFF */
481
+static void onas_print_ht(struct onas_ht *ht, const char *pathname, int level) {
482
+
483
+	if (!ht || !pathname) return;
484
+
485
+	struct onas_hnode *hnode = NULL;
486
+	struct onas_element *elem = NULL;
487
+	size_t len = strlen(pathname);
488
+
489
+	if(onas_ht_get(ht, pathname, len, &elem)) return;
490
+
491
+	hnode = elem->data;
492
+
493
+	printf("%s\twd:%d\n", hnode->pathname, hnode->wd);
494
+
495
+	
496
+	struct onas_lnode *curr = hnode->childhead;
497
+	while (curr->next != hnode->childtail) {
498
+		curr = curr->next;
499
+		int i = 0;
500
+		for(i = 0; i < level; i++) {
501
+			printf("\t");
502
+		}
503
+		printf("\t|____");
504
+		size_t size = len + strlen(curr->dirname) + 2;
505
+		char *child_path = (char *) cli_malloc(size);
506
+		if (hnode->pathname[len-1] == '/')
507
+			snprintf(child_path, size, "%s%s", hnode->pathname, curr->dirname);
508
+		else
509
+			snprintf(child_path, size, "%s/%s", hnode->pathname, curr->dirname);
510
+		onas_print_ht(ht, child_path, level + 1);
511
+		free(child_path);
512
+	}
513
+
514
+	return;
515
+}
516
+
517
+/*** Dealing with parent/child relationships in the table. ***/
518
+
519
+/* Determines parent and returns a copy based on full pathname. */
520
+inline static char *onas_get_parent(const char *pathname, size_t len) {
521
+	if (!pathname || len <= 1) return NULL;
522
+
523
+	int idx = len - 2;
524
+
525
+	while(idx >= 0 && pathname[idx] != '/') {
526
+		idx--;
527
+	}
528
+
529
+	return strndup(pathname, idx);
530
+}
531
+
532
+/* Gets the index at which the name of directory begins from the full pathname. */
533
+inline static int onas_get_dirname_idx(const char *pathname, size_t len) {
534
+	if (!pathname || len <= 1) return -1;
535
+
536
+	int idx = len - 2;
537
+
538
+	while(idx >= 0 && pathname[idx] != '/') {
539
+		idx--;
540
+	}
541
+
542
+	if (pathname[idx] == '/')
543
+		return idx + 1;
544
+
545
+	return idx;
546
+}
547
+
548
+/* Emancipates the specified child from the specified parent. */
549
+int onas_ht_rm_child(struct onas_ht *ht, const char *prntpath, size_t prntlen, const char *childpath, size_t childlen) {
550
+	if (!ht || !prntpath || prntlen <= 0 || !childpath || childlen <= 1) return CL_ENULLARG;
551
+
552
+	struct onas_element *elem = NULL;
553
+	struct onas_hnode *hnode = NULL;
554
+	int idx = onas_get_dirname_idx(childpath, childlen);
555
+
556
+	if(idx <= 0) return CL_SUCCESS;
557
+
558
+	if(onas_ht_get(ht, prntpath, prntlen, &elem)) return CL_EARG;
559
+	hnode = elem->data;
560
+
561
+	return onas_rm_listnode(hnode->childhead, &(childpath[idx]));
562
+}
563
+
564
+/* The specified parent adds the specified child to its list. */
565
+int onas_ht_add_child(struct onas_ht *ht, const char *prntpath, size_t prntlen, const char *childpath, size_t childlen) {
566
+	if (!ht || !prntpath || prntlen <= 0 || !childpath || childlen <= 1) return CL_ENULLARG;
567
+
568
+	struct onas_element *elem = NULL;
569
+	struct onas_hnode *hnode = NULL;
570
+	int idx = onas_get_dirname_idx(childpath, childlen);
571
+
572
+	if(idx <= 0) return CL_SUCCESS;
573
+
574
+	if(onas_ht_get(ht, prntpath, prntlen, &elem)) return CL_EARG;
575
+	hnode = elem->data;
576
+
577
+	return onas_add_hashnode_child(hnode, &(childpath[idx]));
578
+}
579
+
580
+/*** Dealing with hierarchy changes. ***/
581
+
582
+/* Adds the hierarchy under pathname to the tree and allocates all necessary memory. */
583
+int onas_ht_add_hierarchy(struct onas_ht *ht, const char *pathname) {
584
+
585
+	if (!ht || !pathname) return CL_ENULLARG;
586
+
587
+	FTS *ftsp = NULL;
588
+	int ftspopts = FTS_COMFOLLOW | FTS_LOGICAL | FTS_NOCHDIR | FTS_NOSTAT | FTS_XDEV;
589
+	FTSENT *curr = NULL;
590
+	FTSENT *childlist = NULL;
591
+
592
+	size_t len = strlen(pathname);
593
+	char *prnt = onas_get_parent(pathname, len);
594
+	if (prnt) onas_ht_add_child(ht, prnt, strlen(prnt), pathname, len);
595
+	free(prnt);
596
+
597
+	char * const pathargv[] = { pathname, NULL };
598
+	if (!(ftsp = fts_open(pathargv, ftspopts, NULL))) {
599
+		logg("!ScanOnAccess: Could not open '%s'\n", pathname);
600
+		return CL_EARG;
601
+	}
602
+
603
+	while(curr = fts_read(ftsp)) {
604
+
605
+		struct onas_hnode *hnode = NULL;
606
+
607
+		/* May want to handle other options in the future. */
608
+		switch (curr->fts_info) {
609
+			case FTS_D:
610
+				hnode = onas_hashnode_init();
611
+				if (!hnode) return CL_EMEM;
612
+
613
+				hnode->pathlen = curr->fts_pathlen;
614
+				hnode->pathname = strndup(curr->fts_path, hnode->pathlen);
615
+
616
+				hnode->prnt_pathname = onas_get_parent(hnode->pathname, hnode->pathlen);
617
+				if (hnode->prnt_pathname)
618
+					hnode->prnt_pathlen = strlen(hnode->prnt_pathname);
619
+				else
620
+					hnode->prnt_pathlen = 0;
621
+				break;
622
+			default:
623
+				continue;
624
+		}
625
+
626
+		if(childlist = fts_children(ftsp, 0)) {
627
+			do {
628
+				if (childlist->fts_info == FTS_D)
629
+					if(CL_EMEM == onas_add_hashnode_child(hnode, childlist->fts_name))
630
+						return CL_EMEM;
631
+
632
+			} while (childlist = childlist->fts_link);
633
+		}
634
+
635
+		struct onas_element *elem = onas_element_init(hnode, hnode->pathname, hnode->pathlen);
636
+		if (!elem) return CL_EMEM;
637
+
638
+		if (onas_ht_insert(ht, elem)) return -1;
639
+	}
640
+
641
+	fts_close(ftsp);
642
+	return CL_SUCCESS;
643
+}
644
+
645
+/* Removes the underlying hierarchy from the tree and frees all associated memory. */
646
+int onas_ht_rm_hierarchy(struct onas_ht *ht, const char* pathname, size_t len, int level) {
647
+	if (!ht || !pathname || len <= 0) return CL_ENULLARG;
648
+
649
+	struct onas_hnode *hnode = NULL;
650
+	struct onas_element *elem = NULL;
651
+	char *prntname = NULL;
652
+	size_t prntlen = 0;
653
+
654
+	if(onas_ht_get(ht, pathname, len, &elem)) return CL_EARG;
655
+
656
+	hnode = elem->data;
657
+
658
+	struct onas_lnode *curr = hnode->childhead;
659
+
660
+	if(level == 0) {
661
+		if(!(prntname = onas_get_parent(pathname, len))) return CL_EARG;
662
+		prntlen = sizeof(prntname);
663
+		onas_ht_rm_child(ht, prntname, prntlen, pathname, len);
664
+		free(prntname);
665
+	}
666
+
667
+	while (curr->next != hnode->childtail) {
668
+		curr = curr->next;
669
+
670
+		size_t size = len + strlen(curr->dirname) + 2;
671
+		char *child_path = (char *) cli_malloc(size);
672
+		if (hnode->pathname[len-1] == '/')
673
+			snprintf(child_path, size, "%s%s", hnode->pathname, curr->dirname);
674
+		else
675
+			snprintf(child_path, size, "%s/%s", hnode->pathname, curr->dirname);
676
+		onas_ht_rm_hierarchy(ht, child_path, size, level + 1);
677
+		free(child_path);
678
+	}
679
+
680
+	onas_ht_remove(ht, pathname, len, NULL);
681
+	onas_free_element(elem);
682
+
683
+	return CL_SUCCESS;
684
+}
685
+
686
+
687
+
688
+#endif
0 689
new file mode 100644
... ...
@@ -0,0 +1,126 @@
0
+/*
1
+ *  Copyright (C) 2015 Sourcefire, Inc.
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_HASH_H
21
+#define __ONAS_HASH_H
22
+
23
+#define ONAS_FANWATCH   0x1
24
+#define ONAS_INWATCH   0x2
25
+#define ONAS_STOPWATCH 0x3
26
+
27
+#define ONAS_DEFAULT_HT_SIZE 1 << 18
28
+
29
+// PROTOTYPING
30
+struct onas_element {
31
+
32
+	const char* key;
33
+	size_t klen;
34
+	struct onas_hnode *data;
35
+
36
+	struct onas_element *next;
37
+	struct onas_element *prev;
38
+};
39
+
40
+struct onas_bucket {
41
+
42
+	uint32_t size;
43
+
44
+	struct onas_element *head;
45
+	struct onas_element *tail;
46
+};
47
+
48
+struct onas_ht {
49
+
50
+	struct onas_bucket **htable;
51
+
52
+	/* Must be a sufficiently high power of two--will not grow. */
53
+	uint32_t size;
54
+	uint32_t nbckts;
55
+
56
+	/* For testing purposes */
57
+	uint32_t col;
58
+};
59
+
60
+/* Directory node struct for lists */
61
+struct onas_lnode {
62
+
63
+	/* List stuffs */
64
+	char *dirname;
65
+	struct onas_lnode *next;
66
+	struct onas_lnode *prev;
67
+};
68
+
69
+/* Directory node struct for hash tables */
70
+struct onas_hnode {
71
+
72
+	/* Path stuffs */
73
+	int pathlen;
74
+	char *pathname;
75
+
76
+	/* Parent stuffs */
77
+	// This might not be necessary...keep for nao
78
+	int prnt_pathlen;
79
+	char *prnt_pathname;
80
+
81
+	/* Child head and tail are empty sentinels */
82
+	struct onas_lnode *childhead;
83
+	struct onas_lnode *childtail;
84
+
85
+	/* Inotify watch descriptor. */
86
+	int wd;
87
+
88
+	/* Watched stuffs */
89
+	uint32_t watched;
90
+};
91
+
92
+
93
+void onas_free_ht(struct onas_ht *ht);
94
+int onas_ht_init(struct onas_ht **ht, uint32_t table_size);
95
+int onas_ht_insert(struct onas_ht *ht, struct onas_element *elem);
96
+int onas_ht_remove(struct onas_ht *ht, const char *key, size_t klen, struct onas_element **elem);
97
+int onas_ht_get(struct onas_ht *ht, const char *key, size_t klen, struct onas_element **elem);
98
+int onas_ht_rm_hierarchy(struct onas_ht *ht, const char *pathname, size_t len, int level);
99
+int onas_ht_add_hierarchy(struct onas_ht *ht, const char *pathname);
100
+int onas_ht_add_child(struct onas_ht *ht, const char *prntpath, size_t prntlen, const char *childpath, size_t childlen);
101
+int onas_ht_rm_child(struct onas_ht *ht, const char *prntpath, size_t prntlen, const char *childpath, size_t childlen);
102
+
103
+static struct onas_bucket *onas_bucket_init();
104
+static void onas_free_bucket(struct onas_bucket *bckt);
105
+static int onas_bucket_insert(struct onas_bucket *bckt, struct onas_element *elem);
106
+static int onas_bucket_remove(struct onas_bucket *bckt, struct onas_element *elem);
107
+
108
+void onas_free_element(struct onas_element *elem);
109
+struct onas_element *onas_element_init(struct onas_hnode *value, const char *key, size_t klen);
110
+
111
+void onas_free_hashnode(struct onas_hnode *hnode);
112
+struct onas_hnode *onas_hashnode_init();
113
+static int onas_add_hashnode_child(struct onas_hnode *node, const char* dirname);
114
+
115
+void onas_free_listnode(struct onas_lnode *lnode);
116
+struct onas_lnode *onas_listnode_init();
117
+int onas_add_listnode(struct onas_lnode *tail, struct onas_lnode *node);
118
+int onas_rm_listnode(struct onas_lnode *head, const char *dirname);
119
+
120
+void onas_free_dirlist(struct onas_lnode *head);
121
+
122
+/* PROTO STUFF */
123
+static void onas_print_ht(struct onas_ht *ht, const char *pathname, int level);
124
+
125
+#endif
0 126
new file mode 100644
1 127
Binary files /dev/null and b/clamd/onaccess_hash.o differ
... ...
@@ -802,7 +802,7 @@ fds_free (struct fd_data *data)
802 802
 
803 803
 #ifdef FANOTIFY
804 804
 int
805
-fan_checkowner (int pid, const struct optstruct *opts)
805
+onas_fan_checkowner (int pid, const struct optstruct *opts)
806 806
 {
807 807
     char path[32];
808 808
     STATBUF sb;
... ...
@@ -83,7 +83,7 @@ int fds_poll_recv(struct fd_data *data, int timeout, int check_signals, void *ev
83 83
 void fds_free(struct fd_data *data);
84 84
 
85 85
 #ifdef FANOTIFY
86
-int fan_checkowner(int pid, const struct optstruct *opts);
86
+int onas_fan_checkowner(int pid, const struct optstruct *opts);
87 87
 #endif
88 88
 
89 89
 #endif
... ...
@@ -49,7 +49,7 @@
49 49
 #include "shared/output.h"
50 50
 #include "shared/optparser.h"
51 51
 
52
-#include "fan.h"
52
+#include "onaccess_fan.h"
53 53
 #include "server.h"
54 54
 #include "thrmgr.h"
55 55
 #include "session.h"
... ...
@@ -1140,6 +1140,7 @@ int recvloop_th(int *socketds, unsigned nsockets, struct cl_engine *engine, unsi
1140 1140
     acceptdata.max_queue = max_queue;
1141 1141
 
1142 1142
     if(optget(opts, "ScanOnAccess")->enabled)
1143
+
1143 1144
 #if defined(FANOTIFY) || defined(CLAMAUTH)
1144 1145
     {
1145 1146
         do {
... ...
@@ -1149,7 +1150,7 @@ int recvloop_th(int *socketds, unsigned nsockets, struct cl_engine *engine, unsi
1149 1149
 	    tharg->opts = opts;
1150 1150
 	    tharg->engine = engine;
1151 1151
 	    tharg->options = options;
1152
-	    if(!pthread_create(&fan_pid, &fan_attr, fan_th, tharg)) break;
1152
+	    if(!pthread_create(&fan_pid, &fan_attr, onas_fan_th, tharg)) break;
1153 1153
 	    free(tharg);
1154 1154
 	    tharg=NULL;
1155 1155
 	} while(0);
... ...
@@ -1159,6 +1160,7 @@ int recvloop_th(int *socketds, unsigned nsockets, struct cl_engine *engine, unsi
1159 1159
 	logg("!On-access scan is not available\n");
1160 1160
 #endif
1161 1161
 
1162
+
1162 1163
 #ifndef	_WIN32
1163 1164
     /* set up signal handling */
1164 1165
     sigfillset(&sigset);
... ...
@@ -1445,10 +1447,11 @@ int recvloop_th(int *socketds, unsigned nsockets, struct cl_engine *engine, unsi
1445 1445
 	    reload = 0;
1446 1446
 	    time(&reloaded_time);
1447 1447
 	    pthread_mutex_unlock(&reload_mutex);
1448
+
1448 1449
 #if defined(FANOTIFY) || defined(CLAMAUTH)
1449 1450
 	    if(optget(opts, "ScanOnAccess")->enabled && tharg) {
1450 1451
 		tharg->engine = engine;
1451
-		pthread_create(&fan_pid, &fan_attr, fan_th, tharg);
1452
+		pthread_create(&fan_pid, &fan_attr, onas_fan_th, tharg);
1452 1453
 	    }
1453 1454
 #endif
1454 1455
 	    time(&start_time);
... ...
@@ -392,7 +392,7 @@ const struct clam_option __clam_options[] = {
392 392
     /* OnAccess settings */
393 393
     { "ScanOnAccess", NULL, 0, CLOPT_TYPE_BOOL, MATCH_BOOL, -1, NULL, 0, OPT_CLAMD, "This option enables on-access scanning (Linux only)", "no" },
394 394
 
395
-    { "OnAccessIncludePath", NULL, 0, CLOPT_TYPE_STRING, NULL, -1, NULL, FLAG_MULTIPLE, OPT_CLAMD, "This option specifies a directory (including all files and directories\ninside it), which should be scanned on access. This option can\nbe used multiple times.", "/home\n/students" },
395
+    { "OnAccessIncludePath", "on-access-include", 0, CLOPT_TYPE_STRING, NULL, -1, NULL, FLAG_MULTIPLE, OPT_CLAMD, "This option specifies a directory (including all files and directories\ninside it), which should be scanned on access. This option can\nbe used multiple times.", "/home\n/students" },
396 396
 
397 397
     { "OnAccessExcludePath", NULL, 0, CLOPT_TYPE_STRING, NULL, -1, NULL, FLAG_MULTIPLE, OPT_CLAMD, "This option allows excluding directories from on-access scanning. It can\nbe used multiple times.", "/home/bofh\n/root" },
398 398