Browse code

add support for --move and --remove options in clamdscan

git-svn: trunk@1144

Tomasz Kojm authored on 2004/11/29 08:26:29
Showing 9 changed files
... ...
@@ -1,3 +1,7 @@
1
+Mon Nov 29 00:23:55 CET 2004 (tk)
2
+---------------------------------
3
+  * clamdscan: add support for --move and --remove options
4
+
1 5
 Sun Nov 28 21:08:44 GMT 2004 (njh)
2 6
 ----------------------------------
3 7
   * libclamav:	Email headers of only white space are no longer treated as
... ...
@@ -40,6 +40,8 @@ void help(void);
40 40
 
41 41
 short printinfected = 0;
42 42
 
43
+extern int notremoved, notmoved;
44
+
43 45
 void clamscan(struct optstruct *opt)
44 46
 {
45 47
 	int ds, dms, ret, infected;
... ...
@@ -105,6 +107,14 @@ void clamscan(struct optstruct *opt)
105 105
 	    logg("\n-- summary --\n");
106 106
 	mprintf("Infected files: %d\n", infected);
107 107
 	    logg("Infected files: %d\n", infected);
108
+	if(notremoved) {
109
+	    mprintf("Not removed: %d\n", notremoved);
110
+		logg("Not removed: %d\n", notremoved);
111
+	}
112
+	if(notmoved) {
113
+	    mprintf("Not moved: %d\n", notmoved);
114
+		logg("Not moved: %d\n", notmoved);
115
+	}
108 116
 	mprintf("Time: %d.%3.3d sec (%d m %d s)\n", ds, dms/1000, ds/60, ds%60);
109 117
 	    logg("Time: %d.%3.3d sec (%d m %d s)\n", ds, dms/1000, ds/60, ds%60);
110 118
     }
... ...
@@ -128,6 +138,8 @@ void help(void)
128 128
     mprintf("    --stdout                           Write to stdout instead of stderr\n");
129 129
     mprintf("                                       (this help is always written to stdout)\n");
130 130
     mprintf("    --log=FILE          -l FILE        Save scan report in FILE\n");
131
+    mprintf("    --remove                           Remove infected files. Be careful!\n");
132
+    mprintf("    --move=DIRECTORY                   Move infected files into DIRECTORY\n");
131 133
     mprintf("    --config-file=FILE                 Read configuration from FILE.\n");
132 134
     mprintf("    --no-summary                       Disable summary at end of scanning\n");
133 135
     mprintf("\n");
... ...
@@ -21,15 +21,17 @@
21 21
 #endif
22 22
 
23 23
 #include <stdio.h>
24
+#include <unistd.h>
25
+#include <string.h>
24 26
 #include <sys/types.h>
25 27
 #include <sys/stat.h>
26
-#include <unistd.h>
27 28
 #include <sys/socket.h>
28 29
 #include <sys/un.h>
29 30
 #include <netinet/in.h>
30 31
 #include <arpa/inet.h>
31 32
 #include <netdb.h>
32
-#include <string.h>
33
+#include <utime.h>
34
+#include <errno.h>
33 35
 
34 36
 #include "others.h"
35 37
 #include "defaults.h"
... ...
@@ -38,7 +40,9 @@
38 38
 #include "cfgparser.h"
39 39
 #include "memory.h"
40 40
 #include "output.h"
41
+#include "misc.h"
41 42
 #include "str.h"
43
+#include "strrcpy.h" /* libclamav */
42 44
 
43 45
 #ifdef PF_INET
44 46
 # define SOCKET_INET	PF_INET
... ...
@@ -46,11 +50,13 @@
46 46
 # define SOCKET_INET	AF_INET
47 47
 #endif
48 48
 
49
+void move_infected(const char *filename, const struct optstruct *opt);
50
+int notremoved = 0, notmoved = 0;
49 51
 
50
-int dsfile(int sockd, const char *filename)
52
+int dsfile(int sockd, const char *filename, const struct optstruct *opt)
51 53
 {
52 54
 	int infected = 0, waserror = 0;
53
-	char buff[4096], *scancmd;
55
+	char buff[4096], *scancmd, *pt;
54 56
 	FILE *fd;
55 57
 
56 58
 
... ...
@@ -75,8 +81,25 @@ int dsfile(int sockd, const char *filename)
75 75
 	    infected++;
76 76
 	    logg("%s", buff);
77 77
 	    mprintf("%s", buff);
78
+	    if(optl(opt, "move")) {
79
+		pt = strrchr(buff, ':');
80
+		*pt = 0;
81
+		move_infected(buff, opt);
82
+
83
+	    } else if(optl(opt, "remove")) {
84
+		pt = strrchr(buff, ':');
85
+		*pt = 0;
86
+		if(unlink(buff)) {
87
+		    mprintf("%s: Can't remove.\n", buff);
88
+		    logg("%s: Can't remove.\n", buff);
89
+		    notremoved++;
90
+		} else {
91
+		    mprintf("%s: Removed.\n", buff);
92
+		    logg("%s: Removed.\n", buff);
93
+		}
94
+	    }
78 95
 	}
79
-	if (strstr(buff, "ERROR\n")) {
96
+	if(strstr(buff, "ERROR\n")) {
80 97
 	    logg("%s", buff);
81 98
 	    mprintf("%s", buff);
82 99
 	    waserror = 1;
... ...
@@ -91,7 +114,7 @@ int dsfile(int sockd, const char *filename)
91 91
     return infected ? infected : (waserror ? -1 : 0);
92 92
 }
93 93
 
94
-int dsstream(int sockd)
94
+int dsstream(int sockd, const struct optstruct *opt)
95 95
 {
96 96
 	int wsockd, loopw = 60, bread, port, infected = 0;
97 97
 	struct sockaddr_in server;
... ...
@@ -172,6 +195,23 @@ int dsstream(int sockd)
172 172
 	if(strstr(buff, "FOUND\n")) {
173 173
 	    infected++;
174 174
 	    logg("%s", buff);
175
+	    if(optl(opt, "move")) {
176
+		pt = strrchr(buff, ':');
177
+		*pt = 0;
178
+		move_infected(buff, opt);
179
+
180
+	    } else if(optl(opt, "remove")) {
181
+		pt = strrchr(buff, ':');
182
+		*pt = 0;
183
+		if(unlink(buff)) {
184
+		    mprintf("%s: Can't remove.\n", buff);
185
+		    logg("%s: Can't remove.\n", buff);
186
+		    notremoved++;
187
+		} else {
188
+		    mprintf("%s: Removed.\n", buff);
189
+		    logg("%s: Removed.\n", buff);
190
+		}
191
+	    }
175 192
 	}
176 193
 	if(strstr(buff, "ERROR\n")) {
177 194
 	    logg("%s", buff);
... ...
@@ -307,7 +347,7 @@ int client(const struct optstruct *opt, int *infected)
307 307
 	if((sockd = dconnect(opt)) < 0)
308 308
 	    return 2;
309 309
 
310
-	if((ret = dsfile(sockd, cwd)) >= 0)
310
+	if((ret = dsfile(sockd, cwd, opt)) >= 0)
311 311
 	    *infected += ret;
312 312
 	else
313 313
 	    errors++;
... ...
@@ -318,7 +358,7 @@ int client(const struct optstruct *opt, int *infected)
318 318
 	if((sockd = dconnect(opt)) < 0)
319 319
 	    return 2;
320 320
 
321
-	if((ret = dsstream(sockd)) >= 0)
321
+	if((ret = dsstream(sockd, opt)) >= 0)
322 322
 	    *infected += ret;
323 323
 	else
324 324
 	    errors++;
... ...
@@ -352,7 +392,7 @@ int client(const struct optstruct *opt, int *infected)
352 352
 			if((sockd = dconnect(opt)) < 0)
353 353
 			    return 2;
354 354
 
355
-			if((ret = dsfile(sockd, fullpath)) >= 0)
355
+			if((ret = dsfile(sockd, fullpath, opt)) >= 0)
356 356
 			    *infected += ret;
357 357
 			else
358 358
 			    errors++;
... ...
@@ -372,3 +412,112 @@ int client(const struct optstruct *opt, int *infected)
372 372
 
373 373
     return *infected ? 1 : (errors ? 2 : 0);
374 374
 }
375
+
376
+void move_infected(const char *filename, const struct optstruct *opt)
377
+{
378
+	char *movedir, *movefilename, *tmp, numext[4 + 1];
379
+	struct stat fstat, mfstat;
380
+	int n, len, movefilename_size;
381
+	struct utimbuf ubuf;
382
+
383
+
384
+    if(!(movedir = getargl(opt, "move"))) {
385
+        /* Should never reach here */
386
+        mprintf("@getargc() returned NULL\n", filename);
387
+        notmoved++;
388
+        return;
389
+    }
390
+
391
+    if(access(movedir, W_OK|X_OK) == -1) {
392
+        mprintf("@error moving file '%s': cannot write to '%s': %s\n", filename, movedir, strerror(errno));
393
+        notmoved++;
394
+        return;
395
+    }
396
+
397
+    if(stat(filename, &fstat) == -1) {
398
+        mprintf("@Can't stat file %s\n", movefilename);
399
+	mprintf("Try to run clamdscan with clamd privileges\n");
400
+        notmoved++;
401
+	return;
402
+    }
403
+
404
+    if(!(tmp = strrchr(filename, '/')))
405
+	tmp = (char *) filename;
406
+
407
+    movefilename_size = sizeof(char) * (strlen(movedir) + strlen(tmp) + sizeof(numext) + 2);
408
+
409
+    if(!(movefilename = mmalloc(movefilename_size))) {
410
+        mprintf("@Memory allocation error\n");
411
+	exit(2);
412
+    }
413
+
414
+    if(!(strrcpy(movefilename, movedir))) {
415
+        mprintf("@strrcpy() returned NULL\n");
416
+        notmoved++;
417
+        free(movefilename);
418
+        return;
419
+    }
420
+
421
+    strcat(movefilename, "/");
422
+
423
+    if(!(strcat(movefilename, tmp))) {
424
+        mprintf("@strcat() returned NULL\n");
425
+        notmoved++;
426
+        free(movefilename);
427
+        return;
428
+    }
429
+
430
+    if(!stat(movefilename, &mfstat)) {
431
+        if(fstat.st_ino == mfstat.st_ino) { /* It's the same file*/
432
+            mprintf("File excluded '%s'\n", filename);
433
+            logg("File excluded '%s'\n", filename);
434
+            notmoved++;
435
+            free(movefilename);
436
+            return;
437
+        } else {
438
+            /* file exists - try to append an ordinal number to the
439
+	     * quranatined file in an attempt not to overwrite existing
440
+	     * files in quarantine  
441
+	     */
442
+            len = strlen(movefilename);
443
+            n = 0;        		        		
444
+            do {
445
+                /* reset the movefilename to it's initial value by
446
+		 * truncating to the original filename length
447
+		 */
448
+                movefilename[len] = 0;
449
+                /* append .XXX */
450
+                sprintf(numext, ".%03d", n++);
451
+                strcat(movefilename, numext);            	
452
+            } while(!stat(movefilename, &mfstat) && (n < 1000));
453
+       }
454
+    }
455
+
456
+    if(rename(filename, movefilename) == -1) {
457
+	if(filecopy(filename, movefilename) == -1) {
458
+	    mprintf("@cannot move '%s' to '%s': %s\n", filename, movefilename, strerror(errno));
459
+	    notmoved++;
460
+	    free(movefilename);
461
+	    return;
462
+	}
463
+
464
+	chmod(movefilename, fstat.st_mode);
465
+	chown(movefilename, fstat.st_uid, fstat.st_gid);
466
+
467
+	ubuf.actime = fstat.st_atime;
468
+	ubuf.modtime = fstat.st_mtime;
469
+	utime(movefilename, &ubuf);
470
+
471
+	if(unlink(filename)) {
472
+	    mprintf("@cannot unlink '%s': %s\n", filename, strerror(errno));
473
+	    notremoved++;            
474
+	    free(movefilename);
475
+	    return;
476
+	}
477
+    }
478
+
479
+    mprintf("%s: moved to '%s'\n", filename, movefilename);
480
+    logg("%s: moved to '%s'\n", filename, movefilename);
481
+
482
+    free(movefilename);
483
+}
... ...
@@ -39,7 +39,8 @@
39 39
 extern int clamscan(struct optstruct *opt);
40 40
 
41 41
 static char *clamdscan_long[] = { "help", "version", "verbose", "quiet",
42
-				  "stdout", "log", "config-file", "no-summary",
42
+				  "stdout", "log", "move", "remove",
43
+				  "config-file", "no-summary",
43 44
 				  "disable-summary", NULL };
44 45
 
45 46
 static char clamdscan_short[] = { 'h', 'V', 'v', 'l', 0 };
... ...
@@ -113,46 +113,6 @@ int checkaccess(const char *path, const char *username, int mode)
113 113
     return ret;
114 114
 }
115 115
 
116
-int filecopy(const char *src, const char *dest)
117
-{
118
-
119
-#ifdef C_DARWIN
120
-    /* On Mac OS X use ditto and copy resource fork, too. */
121
-    char *ditto = (char *) mcalloc(strlen(src) + strlen(dest) + 30, sizeof(char));
122
-    sprintf(ditto, "/usr/bin/ditto --rsrc %s %s", src, dest);
123
-
124
-    if(system(ditto)) {
125
-	free(ditto);
126
-	return -1;
127
-    }
128
-
129
-    free(ditto);
130
-    return 0;
131
-
132
-#else
133
-	char buffer[FILEBUFF];
134
-	int s, d, bytes;
135
-
136
-    if((s = open(src, O_RDONLY)) == -1)
137
-	return -1;
138
-
139
-    if((d = open(dest, O_CREAT|O_WRONLY|O_TRUNC)) == -1) {
140
-	close(s);
141
-	return -1;
142
-    }
143
-
144
-    while((bytes = read(s, buffer, FILEBUFF)) > 0)
145
-	write(d, buffer, bytes);
146
-
147
-    close(s);
148
-
149
-    /* njh@bandsman.co.uk: check result of close for NFS file */
150
-    return close(d);
151
-
152
-#endif //__APPLE_CC__
153
-
154
-}
155
-
156 116
 int isnumb(const char *str)
157 117
 {
158 118
 	int i;
... ...
@@ -21,7 +21,6 @@
21 21
 
22 22
 int fileinfo(const char *filename, short i);
23 23
 int checkaccess(const char *path, const char *username, int mode);
24
-int filecopy(const char *src, const char *dest);
25 24
 int isnumb(const char *str);
26 25
 
27 26
 #endif
... ...
@@ -1,5 +1,5 @@
1 1
 .\" Manual page created by Tomasz Kojm, 20021121
2
-.TH "Clamd client" "1" "February 20, 2004" "Tomasz Kojm" "Clam AntiVirus"
2
+.TH "Clamd client" "1" "November 29, 2004" "Tomasz Kojm" "Clam AntiVirus"
3 3
 .SH "NAME"
4 4
 .LP 
5 5
 clamdscan \- scan files and directories against viruses using Clam AntiVirus Daemon
... ...
@@ -34,6 +34,12 @@ Read clamd settings from FILE.
34 34
 \fB\-l FILE, \-\-log=FILE\fR
35 35
 Save the scan report to FILE.
36 36
 .TP 
37
+\fB\-\-remove\fR
38
+Remove infected files. \fBBe careful.\fR
39
+.TP 
40
+\fB\-\-move=DIRECTORY\fR
41
+Move infected files into DIRECTORY.
42
+.TP 
37 43
 \fB\-\-no\-summary\fR
38 44
 Do not display summary at the end of scanning.
39 45
 .SH "EXAMPLES"
... ...
@@ -21,8 +21,13 @@
21 21
 #endif
22 22
 
23 23
 #include <stdio.h>
24
+#include <stdlib.h>
25
+#include <unistd.h>
24 26
 #include <string.h>
25 27
 #include <time.h>
28
+#include <sys/types.h>
29
+#include <sys/stat.h>
30
+#include <fcntl.h>
26 31
 
27 32
 #include "clamav.h"
28 33
 #include "cfgparser.h"
... ...
@@ -92,3 +97,43 @@ void print_version(void)
92 92
 
93 93
     free(path);
94 94
 }
95
+
96
+int filecopy(const char *src, const char *dest)
97
+{
98
+
99
+#ifdef C_DARWIN
100
+    /* On Mac OS X use ditto and copy resource fork, too. */
101
+    char *ditto = (char *) mcalloc(strlen(src) + strlen(dest) + 30, sizeof(char));
102
+    sprintf(ditto, "/usr/bin/ditto --rsrc %s %s", src, dest);
103
+
104
+    if(system(ditto)) {
105
+	free(ditto);
106
+	return -1;
107
+    }
108
+
109
+    free(ditto);
110
+    return 0;
111
+
112
+#else
113
+	char buffer[FILEBUFF];
114
+	int s, d, bytes;
115
+
116
+    if((s = open(src, O_RDONLY)) == -1)
117
+	return -1;
118
+
119
+    if((d = open(dest, O_CREAT|O_WRONLY|O_TRUNC)) == -1) {
120
+	close(s);
121
+	return -1;
122
+    }
123
+
124
+    while((bytes = read(s, buffer, FILEBUFF)) > 0)
125
+	write(d, buffer, bytes);
126
+
127
+    close(s);
128
+
129
+    /* njh@bandsman.co.uk: check result of close for NFS file */
130
+    return close(d);
131
+
132
+#endif
133
+
134
+}
... ...
@@ -23,5 +23,6 @@
23 23
 
24 24
 const char *freshdbdir(void);
25 25
 void print_version(void);
26
+int filecopy(const char *src, const char *dest);
26 27
 
27 28
 #endif