... | ... |
@@ -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 |
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 |
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 |
... | ... |
@@ -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 |
|