... | ... |
@@ -1,3 +1,7 @@ |
1 |
+Sun May 2 19:13:29 CEST 2010 (acab) |
|
2 |
+------------------------------------ |
|
3 |
+ * clamav-milter: add VirusAction (bb#1867) |
|
4 |
+ |
|
1 | 5 |
Sat May 1 02:05:17 CEST 2010 (acab) |
2 | 6 |
------------------------------------ |
3 | 7 |
* m4: On solaris 8 bzlib.h uses FILE without including stdio.h (bb#1985) |
... | ... |
@@ -26,13 +26,16 @@ |
26 | 26 |
#include <stdlib.h> |
27 | 27 |
#include <string.h> |
28 | 28 |
#include <sys/types.h> |
29 |
+#include <sys/wait.h> |
|
29 | 30 |
#include <unistd.h> |
30 | 31 |
#include <ctype.h> |
32 |
+#include <errno.h> |
|
31 | 33 |
|
32 | 34 |
#include <libmilter/mfapi.h> |
33 | 35 |
|
34 | 36 |
#include "shared/optparser.h" |
35 | 37 |
#include "shared/output.h" |
38 |
+#include "libclamav/others.h" |
|
36 | 39 |
|
37 | 40 |
#include "connpool.h" |
38 | 41 |
#include "netcode.h" |
... | ... |
@@ -54,6 +57,7 @@ static char *rejectfmt = NULL; |
54 | 54 |
|
55 | 55 |
int addxvirus = 0; /* 0 - don't add | 1 - replace | 2 - add */ |
56 | 56 |
char xvirushdr[255]; |
57 |
+char *viraction = NULL; |
|
57 | 58 |
enum { |
58 | 59 |
LOGINF_NONE, |
59 | 60 |
LOGINF_BASIC, |
... | ... |
@@ -62,6 +66,7 @@ enum { |
62 | 62 |
|
63 | 63 |
#define CLAMFIBUFSZ 1424 |
64 | 64 |
static const char *HDR_UNAVAIL = "UNKNOWN"; |
65 |
+static pthread_mutex_t virusaction_lock = PTHREAD_MUTEX_INITIALIZER; |
|
65 | 66 |
|
66 | 67 |
struct CLAMFI { |
67 | 68 |
const char *virusname; |
... | ... |
@@ -213,7 +218,7 @@ sfsistat clamfi_header(SMFICTX *ctx, char *headerf, char *headerv) { |
213 | 213 |
|
214 | 214 |
if(!headerf) return SMFIS_CONTINUE; /* just in case */ |
215 | 215 |
|
216 |
- if(loginfected == LOGINF_FULL) { |
|
216 |
+ if(loginfected == LOGINF_FULL || viraction) { |
|
217 | 217 |
if(!cf->msg_subj && !strcasecmp(headerf, "Subject")) |
218 | 218 |
cf->msg_subj = strdup(headerv ? headerv : ""); |
219 | 219 |
if(!cf->msg_date && !strcasecmp(headerf, "Date")) |
... | ... |
@@ -321,7 +326,7 @@ sfsistat clamfi_eom(SMFICTX *ctx) { |
321 | 321 |
ret = CleanAction(ctx); |
322 | 322 |
} else if (len>7 && !strcmp(reply + len - 7, " FOUND\n")) { |
323 | 323 |
cf->virusname = NULL; |
324 |
- if(loginfected || addxvirus || rejectfmt) { |
|
324 |
+ if(loginfected || addxvirus || rejectfmt || viraction) { |
|
325 | 325 |
char *vir; |
326 | 326 |
|
327 | 327 |
reply[len-7] = '\0'; |
... | ... |
@@ -339,21 +344,67 @@ sfsistat clamfi_eom(SMFICTX *ctx) { |
339 | 339 |
add_x_header(ctx, msg, cf->scanned_count, cf->status_count); |
340 | 340 |
} |
341 | 341 |
|
342 |
- if(loginfected) { |
|
342 |
+ if(loginfected || viraction) { |
|
343 | 343 |
const char *from = smfi_getsymval(ctx, "{mail_addr}"); |
344 | 344 |
const char *to = smfi_getsymval(ctx, "{rcpt_addr}"); |
345 | 345 |
|
346 | 346 |
if(!from) from = HDR_UNAVAIL; |
347 | 347 |
if(!to) to = HDR_UNAVAIL; |
348 |
- if(loginfected == LOGINF_FULL) { |
|
348 |
+ if(loginfected == LOGINF_FULL || viraction) { |
|
349 | 349 |
const char *id = smfi_getsymval(ctx, "{i}"); |
350 | 350 |
const char *msg_subj = makesanehdr(cf->msg_subj); |
351 | 351 |
const char *msg_date = makesanehdr(cf->msg_date); |
352 | 352 |
const char *msg_id = makesanehdr(cf->msg_id); |
353 | 353 |
|
354 | 354 |
if(!id) id = HDR_UNAVAIL; |
355 |
- logg("~Message %s from <%s> to <%s> with subject '%s' message-id '%s' date '%s' infected by %s\n", id, from, to, msg_subj, msg_id, msg_date, vir); |
|
356 |
- } else logg("~Message from <%s> to <%s> infected by %s\n", from, to, vir); |
|
355 |
+ |
|
356 |
+ if(loginfected == LOGINF_FULL) |
|
357 |
+ logg("~Message %s from <%s> to <%s> with subject '%s' message-id '%s' date '%s' infected by %s\n", id, from, to, msg_subj, msg_id, msg_date, vir); |
|
358 |
+ |
|
359 |
+ if(viraction) { |
|
360 |
+ char er[256]; |
|
361 |
+ char *e_id = strdup(id); |
|
362 |
+ char *e_from = strdup(from); |
|
363 |
+ char *e_to = strdup(to); |
|
364 |
+ char *e_msg_subj = strdup(msg_subj); |
|
365 |
+ char *e_msg_date = strdup(msg_date); |
|
366 |
+ char *e_msg_id = strdup(msg_id); |
|
367 |
+ pid_t pid; |
|
368 |
+ |
|
369 |
+ pthread_mutex_lock(&virusaction_lock); |
|
370 |
+ pid = fork(); |
|
371 |
+ if(!pid) { |
|
372 |
+ char * args[9]; /* avoid element is not computable at load time warns */ |
|
373 |
+ args[0]= viraction; |
|
374 |
+ args[1] = vir; |
|
375 |
+ args[2] = e_id; |
|
376 |
+ args[3] = e_from; |
|
377 |
+ args[4] = e_to; |
|
378 |
+ args[5] = e_msg_subj; |
|
379 |
+ args[6] = e_msg_id; |
|
380 |
+ args[7] = e_msg_date; |
|
381 |
+ args[8] = NULL; |
|
382 |
+ logg("*Executing: '%s' '%s' '%s' '%s' '%s' '%s' '%s' '%s'\n", viraction, vir, e_id, e_from, e_to, e_msg_subj, e_msg_id, e_msg_date); |
|
383 |
+ if((ret = execvp(viraction, args)) < 0) { |
|
384 |
+ logg("!VirusEvent: exec failed: %s\n", cli_strerror(errno, er, sizeof(er))); |
|
385 |
+ } |
|
386 |
+ exit(ret); |
|
387 |
+ } else if(pid > 0) { |
|
388 |
+ pthread_mutex_unlock(&virusaction_lock); |
|
389 |
+ waitpid(pid, NULL, 0); |
|
390 |
+ } else { |
|
391 |
+ logg("!VirusEvent: fork failed: %s\n", cli_strerror(errno, er, sizeof(er))); |
|
392 |
+ } |
|
393 |
+ free(e_id); |
|
394 |
+ free(e_from); |
|
395 |
+ free(e_to); |
|
396 |
+ free(e_msg_subj); |
|
397 |
+ free(e_msg_date); |
|
398 |
+ free(e_msg_id); |
|
399 |
+ } |
|
400 |
+ } |
|
401 |
+ if(loginfected == LOGINF_BASIC) |
|
402 |
+ logg("~Message from <%s> to <%s> infected by %s\n", from, to, vir); |
|
357 | 403 |
} |
358 | 404 |
} |
359 | 405 |
} |
... | ... |
@@ -457,6 +508,9 @@ int init_actions(struct optstruct *opts) { |
457 | 457 |
return 1; |
458 | 458 |
} |
459 | 459 |
|
460 |
+ if((opt = optget(opts, "VirusAction"))->enabled) |
|
461 |
+ viraction = strdup(opt->strarg); |
|
462 |
+ |
|
460 | 463 |
if((opt = optget(opts, "OnFail"))->enabled) { |
461 | 464 |
switch(parse_action(opt->strarg)) { |
462 | 465 |
case 0: |
... | ... |
@@ -187,6 +187,13 @@ Example |
187 | 187 |
# Default: disabled |
188 | 188 |
#ReportHostname my.mail.server.name |
189 | 189 |
|
190 |
+# Execute a command when an infected message is processed. |
|
191 |
+# The following parameters are passed to the invoked program in this order: |
|
192 |
+# virus name, queue id, sender, destination, subject, message id, message date. |
|
193 |
+# Note: this requires MTA macroes to be available (see LogInfected below) |
|
194 |
+# Default: disabled |
|
195 |
+#VirusAction /usr/local/bin/my_infected_message_handler |
|
196 |
+ |
|
190 | 197 |
## |
191 | 198 |
## Logging options |
192 | 199 |
## |
... | ... |
@@ -420,6 +420,8 @@ const struct clam_option __clam_options[] = { |
420 | 420 |
|
421 | 421 |
{ "ReportHostname", NULL, 0, TYPE_STRING, NULL, -1, NULL, 0, OPT_MILTER, "When AddHeader is in use, this option allows to arbitrary set the reported\nhostname. This may be desirable in order to avoid leaking internal names.\nIf unset the real machine name is used.", "my.mail.server.name" }, |
422 | 422 |
|
423 |
+ { "VirusAction", NULL, 0, TYPE_STRING, NULL, -1, NULL, 0, OPT_MILTER, "Execute a command when an infected message is processed.\nThe following parameters are passed to the invoked program in this order:\nvirus name, queue id, sender, destination, subject, message id, message date.\nNote: this requires MTA macroes to be available (see LogInfected below)", "/usr/local/bin/my_infected_message_handler" }, |
|
424 |
+ |
|
423 | 425 |
{ "Chroot", NULL, 0, TYPE_STRING, NULL, -1, NULL, 0, OPT_MILTER, "Chroot to the specified directory.\nChrooting is performed just after reading the config file and before\ndropping privileges.", "/newroot" }, |
424 | 426 |
|
425 | 427 |
{ "Whitelist", NULL, 0, TYPE_STRING, NULL, -1, NULL, 0, OPT_MILTER, "This option specifies a file which contains a list of basic POSIX regular\nexpressions. Addresses (sent to or from - see below) matching these regexes\nwill not be scanned. Optionally each line can start with the string \"From:\"\nor \"To:\" (note: no whitespace after the colon) indicating if it is,\nrespectively, the sender or recipient that is to be whitelisted.\nIf the field is missing, \"To:\" is assumed.\nLines starting with #, : or ! are ignored.", "/etc/whitelisted_addresses" }, |