Browse code

add VirusAction to clamav-milter - bb#1867

aCaB authored on 2010/05/03 01:36:27
Showing 4 changed files
... ...
@@ -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" },