git-svn: trunk@1144
Tomasz Kojm authored on 2004/11/29 08:26:29... | ... |
@@ -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; |
... | ... |
@@ -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 |
+} |