Browse code

add file descriptor passing support to clamdscan (from contrib/clamd_fdscan)

git-svn: trunk@4080

Török Edvin authored on 2008/08/04 23:01:21
Showing 10 changed files
... ...
@@ -1,3 +1,8 @@
1
+Mon Aug  4 16:46:46 EEST 2008 (edwin)
2
+-------------------------------------
3
+  * clamdscan: add support for file descriptor passing (from
4
+  contrib/clamd_fdscan) (bb #1117)
5
+
1 6
 Mon Aug  4 02:40:33 CEST 2008 (acab)
2 7
 ------------------------------------
3 8
   * test: add clam.impl.zip
... ...
@@ -31,6 +31,9 @@ clamdscan_SOURCES = \
31 31
     $(top_srcdir)/shared/getopt.h \
32 32
     $(top_srcdir)/shared/options.c \
33 33
     $(top_srcdir)/shared/options.h \
34
+    $(top_srcdir)/libclamav/regex/strlcpy.c\
35
+    clamd_fdscan.c \
36
+    clamd_fdscan.h \
34 37
     clamdscan.c \
35 38
     client.c \
36 39
     client.h \
... ...
@@ -71,11 +71,13 @@ am__clamdscan_SOURCES_DIST = $(top_srcdir)/shared/output.c \
71 71
 	$(top_srcdir)/shared/cfgparser.h $(top_srcdir)/shared/misc.c \
72 72
 	$(top_srcdir)/shared/misc.h $(top_srcdir)/shared/getopt.c \
73 73
 	$(top_srcdir)/shared/getopt.h $(top_srcdir)/shared/options.c \
74
-	$(top_srcdir)/shared/options.h clamdscan.c client.c client.h \
75
-	defaults.h
74
+	$(top_srcdir)/shared/options.h \
75
+	$(top_srcdir)/libclamav/regex/strlcpy.c clamd_fdscan.c \
76
+	clamd_fdscan.h clamdscan.c client.c client.h defaults.h
76 77
 @BUILD_CLAMD_TRUE@am_clamdscan_OBJECTS = output.$(OBJEXT) \
77 78
 @BUILD_CLAMD_TRUE@	cfgparser.$(OBJEXT) misc.$(OBJEXT) \
78 79
 @BUILD_CLAMD_TRUE@	getopt.$(OBJEXT) options.$(OBJEXT) \
80
+@BUILD_CLAMD_TRUE@	strlcpy.$(OBJEXT) clamd_fdscan.$(OBJEXT) \
79 81
 @BUILD_CLAMD_TRUE@	clamdscan.$(OBJEXT) client.$(OBJEXT)
80 82
 clamdscan_OBJECTS = $(am_clamdscan_OBJECTS)
81 83
 clamdscan_LDADD = $(LDADD)
... ...
@@ -240,6 +242,9 @@ top_srcdir = @top_srcdir@
240 240
 @BUILD_CLAMD_TRUE@    $(top_srcdir)/shared/getopt.h \
241 241
 @BUILD_CLAMD_TRUE@    $(top_srcdir)/shared/options.c \
242 242
 @BUILD_CLAMD_TRUE@    $(top_srcdir)/shared/options.h \
243
+@BUILD_CLAMD_TRUE@    $(top_srcdir)/libclamav/regex/strlcpy.c\
244
+@BUILD_CLAMD_TRUE@    clamd_fdscan.c \
245
+@BUILD_CLAMD_TRUE@    clamd_fdscan.h \
243 246
 @BUILD_CLAMD_TRUE@    clamdscan.c \
244 247
 @BUILD_CLAMD_TRUE@    client.c \
245 248
 @BUILD_CLAMD_TRUE@    client.h \
... ...
@@ -336,12 +341,14 @@ distclean-compile:
336 336
 	-rm -f *.tab.c
337 337
 
338 338
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cfgparser.Po@am__quote@
339
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clamd_fdscan.Po@am__quote@
339 340
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clamdscan.Po@am__quote@
340 341
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client.Po@am__quote@
341 342
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt.Po@am__quote@
342 343
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc.Po@am__quote@
343 344
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/options.Po@am__quote@
344 345
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/output.Po@am__quote@
346
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strlcpy.Po@am__quote@
345 347
 
346 348
 .c.o:
347 349
 @am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
... ...
@@ -434,6 +441,20 @@ options.obj: $(top_srcdir)/shared/options.c
434 434
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
435 435
 @am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o options.obj `if test -f '$(top_srcdir)/shared/options.c'; then $(CYGPATH_W) '$(top_srcdir)/shared/options.c'; else $(CYGPATH_W) '$(srcdir)/$(top_srcdir)/shared/options.c'; fi`
436 436
 
437
+strlcpy.o: $(top_srcdir)/libclamav/regex/strlcpy.c
438
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT strlcpy.o -MD -MP -MF $(DEPDIR)/strlcpy.Tpo -c -o strlcpy.o `test -f '$(top_srcdir)/libclamav/regex/strlcpy.c' || echo '$(srcdir)/'`$(top_srcdir)/libclamav/regex/strlcpy.c
439
+@am__fastdepCC_TRUE@	mv -f $(DEPDIR)/strlcpy.Tpo $(DEPDIR)/strlcpy.Po
440
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$(top_srcdir)/libclamav/regex/strlcpy.c' object='strlcpy.o' libtool=no @AMDEPBACKSLASH@
441
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
442
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o strlcpy.o `test -f '$(top_srcdir)/libclamav/regex/strlcpy.c' || echo '$(srcdir)/'`$(top_srcdir)/libclamav/regex/strlcpy.c
443
+
444
+strlcpy.obj: $(top_srcdir)/libclamav/regex/strlcpy.c
445
+@am__fastdepCC_TRUE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT strlcpy.obj -MD -MP -MF $(DEPDIR)/strlcpy.Tpo -c -o strlcpy.obj `if test -f '$(top_srcdir)/libclamav/regex/strlcpy.c'; then $(CYGPATH_W) '$(top_srcdir)/libclamav/regex/strlcpy.c'; else $(CYGPATH_W) '$(srcdir)/$(top_srcdir)/libclamav/regex/strlcpy.c'; fi`
446
+@am__fastdepCC_TRUE@	mv -f $(DEPDIR)/strlcpy.Tpo $(DEPDIR)/strlcpy.Po
447
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='$(top_srcdir)/libclamav/regex/strlcpy.c' object='strlcpy.obj' libtool=no @AMDEPBACKSLASH@
448
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
449
+@am__fastdepCC_FALSE@	$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o strlcpy.obj `if test -f '$(top_srcdir)/libclamav/regex/strlcpy.c'; then $(CYGPATH_W) '$(top_srcdir)/libclamav/regex/strlcpy.c'; else $(CYGPATH_W) '$(srcdir)/$(top_srcdir)/libclamav/regex/strlcpy.c'; fi`
450
+
437 451
 mostlyclean-libtool:
438 452
 	-rm -f *.lo
439 453
 
440 454
new file mode 100644
... ...
@@ -0,0 +1,112 @@
0
+/*	$Id: clamd_fdscan.c,v 1.2 2007/01/18 16:59:50 mbalmer Exp $	*/
1
+
2
+/*
3
+ * Copyright (c) 2007 Marc Balmer <mbalmer@openbsd.org>
4
+ *
5
+ * Permission to use, copy, modify, and distribute this software for any
6
+ * purpose with or without fee is hereby granted, provided that the above
7
+ * copyright notice and this permission notice appear in all copies.
8
+ *
9
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
+ */
17
+
18
+#ifdef HAVE_CONFIG_H
19
+#include "clamav-config.h"
20
+#endif
21
+
22
+#ifdef HAVE_FD_PASSING
23
+#include <sys/types.h>
24
+#include <sys/socket.h>
25
+#include <sys/un.h>
26
+#include <sys/uio.h>
27
+#include <string.h>
28
+
29
+#include <stdio.h>
30
+#include <err.h>
31
+#include <unistd.h>
32
+
33
+#include "clamd_fdscan.h"
34
+
35
+#define CLAMD_BUFSIZ	256
36
+
37
+size_t cli_strlcpy(char *dst, const char *src, size_t siz);
38
+/*
39
+ * clamd_fdscan lets a running clamd process scan the contents of an open
40
+ * filedescriptor by passing the filedescriptor to clamd.  The parameters
41
+ * are as follows:
42
+ * s            socket connected to clamd
43
+ * fd		the open filedescriptor to pass for scanning
44
+ * name		virus name, if a virus is found
45
+ * len		max len of the virus name
46
+ *
47
+ * The functions returns 0 if the file was scanned and contains no virus,
48
+ * -1 if an error occurs and 1 if a virus is found.
49
+ */
50
+int
51
+clamd_fdscan(int s, int fd, char *name, size_t len)
52
+{
53
+	struct sockaddr_un addr;
54
+	struct msghdr msg;
55
+	struct cmsghdr *cmsg;
56
+	unsigned char fdbuf[CMSG_SPACE(sizeof(int))];
57
+	FILE *sp;
58
+	char buf[CLAMD_BUFSIZ], *p, *q;
59
+	off_t pos;
60
+	struct iovec iov[1];
61
+
62
+	iov[0].iov_base = "";
63
+	iov[0].iov_len = 1;
64
+
65
+	pos = lseek(fd, 0, SEEK_CUR);
66
+
67
+	memset(&msg, 0, sizeof(msg));
68
+	msg.msg_control = fdbuf;
69
+	/* must send/receive at least one byte */
70
+	msg.msg_iov = iov;
71
+	msg.msg_iovlen = 1;
72
+	msg.msg_controllen = CMSG_LEN(sizeof(int));
73
+
74
+	cmsg = CMSG_FIRSTHDR(&msg);
75
+	cmsg->cmsg_len = CMSG_LEN(sizeof(int));
76
+	cmsg->cmsg_level = SOL_SOCKET;
77
+	cmsg->cmsg_type = SCM_RIGHTS;
78
+	*(int *)CMSG_DATA(cmsg) = fd;
79
+
80
+	write(s, "FILDES\n", sizeof("FILDES\n")-1);
81
+	if (sendmsg(s, &msg, 0) == -1) {
82
+		perror("sendmsg");
83
+		close(s);
84
+		return -1;
85
+	}
86
+
87
+	sp = fdopen(s,"r");
88
+	fgets(buf, sizeof(buf), sp);
89
+	fclose(sp);
90
+	close(s);
91
+
92
+	if (pos != -1)
93
+		lseek(fd, pos, SEEK_SET);
94
+	if ((p = strrchr(buf, ' ')) != NULL) {
95
+		++p;
96
+		if (!strncmp(p, "OK", 2))
97
+			return 0;
98
+		else if (!strncmp(p, "FOUND", 5)) {
99
+			if (name != NULL) {
100
+				*--p = '\0';
101
+				q = strrchr(buf, ' ') + 1;
102
+				cli_strlcpy(name, q, len);
103
+			}
104
+			return 1;
105
+		} else {
106
+			puts(buf);
107
+		}
108
+	}
109
+	return -1;
110
+}
111
+#endif
0 112
new file mode 100644
... ...
@@ -0,0 +1,19 @@
0
+/*	$Id: clamd_fdscan.h,v 1.1.1.1 2007/01/18 14:01:24 mbalmer Exp $	*/
1
+
2
+/*
3
+ * Copyright (c) 2007 Marc Balmer <mbalmer@openbsd.org>
4
+ *
5
+ * Permission to use, copy, modify, and distribute this software for any
6
+ * purpose with or without fee is hereby granted, provided that the above
7
+ * copyright notice and this permission notice appear in all copies.
8
+ *
9
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
+ */
17
+
18
+extern int clamd_fdscan(int s, int fd, char *name, size_t len);
... ...
@@ -60,7 +60,7 @@ int main(int argc, char **argv)
60 60
 	struct optstruct *opt;
61 61
 	const char *clamdscan_accepted[] = { "help", "version", "verbose", "quiet",
62 62
 				  "stdout", "log", "move", "copy", "remove",
63
-				  "config-file", "no-summary",
63
+				  "config-file", "no-summary",  "fdpass",
64 64
 				  "disable-summary", "multiscan", NULL };
65 65
 
66 66
 
... ...
@@ -160,6 +160,7 @@ void help(void)
160 160
     mprintf("    --multiscan           -m           Force MULTISCAN mode\n");
161 161
     mprintf("    --infected            -i           Only print infected files\n");
162 162
     mprintf("    --no-summary                       Disable summary at end of scanning\n");
163
+    mprintf("    --fdpass                           pass filedescriptor to clamd (useful if clamd is running as a different user)\n");
163 164
     mprintf("\n");
164 165
 
165 166
     exit(0);
... ...
@@ -262,7 +262,7 @@ static char *abpath(const char *filename)
262 262
     return fullpath;
263 263
 }
264 264
 
265
-static int dconnect(const struct optstruct *opt)
265
+static int dconnect(const struct optstruct *opt, int *is_unix)
266 266
 {
267 267
 	struct sockaddr_un server;
268 268
 	struct sockaddr_in server2;
... ...
@@ -272,7 +272,8 @@ static int dconnect(const struct optstruct *opt)
272 272
 	const char *clamav_conf = opt_arg(opt, "config-file");
273 273
 	int sockd;
274 274
 
275
-
275
+    if(is_unix)
276
+	    *is_unix = 0;
276 277
     if(!clamav_conf)
277 278
 	clamav_conf = DEFAULT_CFG;
278 279
 
... ...
@@ -307,7 +308,8 @@ static int dconnect(const struct optstruct *opt)
307 307
 	    freecfg(copt);
308 308
 	    return -1;
309 309
 	}
310
-
310
+	if(is_unix)
311
+		*is_unix = 1;
311 312
     } else if((cpt = cfgopt(copt, "TCPSocket"))->enabled) {
312 313
 
313 314
 	if((sockd = socket(SOCKET_INET, SOCK_STREAM, 0)) < 0) {
... ...
@@ -356,7 +358,7 @@ int get_clamd_version(const struct optstruct *opt)
356 356
 	int bread, sockd;
357 357
 
358 358
 
359
-    if((sockd = dconnect(opt)) < 0)
359
+    if((sockd = dconnect(opt, NULL)) < 0)
360 360
 	return 2;
361 361
 
362 362
     if(write(sockd, "VERSION", 7) <= 0) {
... ...
@@ -394,7 +396,7 @@ int client(const struct optstruct *opt, int *infected)
394 394
 	    return 2;
395 395
 	}
396 396
 
397
-	if((sockd = dconnect(opt)) < 0)
397
+	if((sockd = dconnect(opt, NULL)) < 0)
398 398
 	    return 2;
399 399
 
400 400
 	if((ret = dsfile(sockd, scantype, cwd, opt)) >= 0)
... ...
@@ -405,10 +407,29 @@ int client(const struct optstruct *opt, int *infected)
405 405
 	close(sockd);
406 406
 
407 407
     } else if(!strcmp(opt->filename, "-")) { /* scan data from stdin */
408
-	if((sockd = dconnect(opt)) < 0)
408
+        int is_unix;
409
+	if((sockd = dconnect(opt, &is_unix)) < 0)
409 410
 	    return 2;
410 411
 
411
-	if((ret = dsstream(sockd, opt)) >= 0)
412
+	if(opt_check(opt,"fdpass")) {
413
+#ifndef HAVE_FD_PASSING
414
+		logg("^File descriptor pass support not compiled in, falling back to stream scan\n");
415
+		ret = dsstream(sockd, opt);
416
+#else
417
+		if(!is_unix) {
418
+			logg("^File descriptor passing can only work on local (unix) sockets! Falling back to stream scan\n");
419
+			/* fall back to stream */
420
+			ret = dsstream(sockd, opt);
421
+		} else {
422
+			char buff[4096];
423
+			memset(buff, 0, sizeof(buff));
424
+			ret = clamd_fdscan(sockd, 0, buff, sizeof(buff));
425
+			logg("fd: %s%s",buff, ret == 1 ? " FOUND" : ret == -1 ? " ERROR" : "OK");
426
+		}
427
+#endif
428
+	} else
429
+		ret = dsstream(sockd, opt);
430
+	if(ret >= 0)
412 431
 	    *infected += ret;
413 432
 	else
414 433
 	    errors++;
... ...
@@ -439,7 +460,7 @@ int client(const struct optstruct *opt, int *infected)
439 439
 		switch(sb.st_mode & S_IFMT) {
440 440
 		    case S_IFREG:
441 441
 		    case S_IFDIR:
442
-			if((sockd = dconnect(opt)) < 0)
442
+			if((sockd = dconnect(opt, NULL)) < 0)
443 443
 			    return 2;
444 444
 
445 445
 			if((ret = dsfile(sockd, scantype, fullpath, opt)) >= 0)
... ...
@@ -86,7 +86,7 @@ static struct option clamscan_longopt[] = {
86 86
     /* developers only */
87 87
     {"dev-ac-only", 0, 0, 0},
88 88
     {"dev-ac-depth", 1, 0, 0},
89
-
89
+    {"fdpass", 0, 0, 0},
90 90
     {0, 0, 0, 0}
91 91
 };
92 92
 
... ...
@@ -44,6 +44,10 @@ Move infected files into DIRECTORY.
44 44
 .TP 
45 45
 \fB\-\-no\-summary\fR
46 46
 Do not display summary at the end of scanning.
47
+.TP
48
+\fB\-\-fdpass\fR
49
+Pass the file descriptor permissions clamd. This is useful if clamd is running as a different user as it is faster than streaming the file to clamd.
50
+Only available if connected to clamd through local(unix) sockets and scanning stdin.
47 51
 .SH "EXAMPLES"
48 52
 .LP 
49 53
 .TP 
... ...
@@ -58,6 +62,10 @@ Do not display summary at the end of scanning.
58 58
 (2) To scan all files in /home:
59 59
 
60 60
 \fBclamdscan /home\fR
61
+.TP
62
+(3) To scan a file when clamd is running as a different user:
63
+
64
+\fBclamdscan - <file_to_scan\fR
61 65
 .SH "RETURN CODES"
62 66
 .LP 
63 67
 0 : No virus found.
... ...
@@ -18,6 +18,19 @@ run_clamd_test() {
18 18
 	test /tmp/clamd-test.pid && kill `cat /tmp/clamd-test.pid` 
19 19
 }
20 20
 
21
+run_clamd_fdpass_test() {
22
+	conf_file=$1
23
+	shift
24
+	rm -f clamdscan.log
25
+	../clamd/clamd -c $conf_file || { echo "Failed to start clamd!" >&2; die 1;}
26
+	../clamdscan/clamdscan --quiet --fdpass --config-file $conf_file - <$1 --log=clamdscan.log
27
+	if test $? = 2; then
28
+		echo "Failed to run clamdscan!" >&2;
29
+		die 3;
30
+	fi
31
+	test /tmp/clamd-test.pid && kill `cat /tmp/clamd-test.pid`
32
+}
33
+
21 34
 mkdir -p test-db
22 35
 cat <<EOF >test-db/test.hdb
23 36
 aa15bcf478d165efd2065190eb473bcb:544:ClamAV-Test-File
... ...
@@ -65,4 +78,16 @@ if test ! $?; then
65 65
 	cat clamdscan.log;
66 66
 	die 6;
67 67
 fi
68
+
69
+if grep "^#define HAVE_FD_PASSING 1" ../clamav-config.h >/dev/null; then
70
+	run_clamd_fdpass_test $srcdir/test-clamd.conf ../test/clam.exe
71
+	grep "ClamAV-Test-File" clamdscan.log >/dev/null 2>/dev/null;
72
+	if test ! $?; then
73
+		echo "FDpassing test failed!" >&2;
74
+		cat clamdscan.log;
75
+		die 7;
76
+	fi
77
+else
78
+	echo "No FD passing support, skipping test"
79
+fi
68 80
 die 0;