git-svn: trunk@5041
aCaB authored on 2009/04/16 01:56:17... | ... |
@@ -1,3 +1,12 @@ |
1 |
+Tue Apr 15 18:47:22 CEST 2009 (acab) |
|
2 |
+------------------------------------ |
|
3 |
+ * clamav-milter: add support for action QuarantineReject (bb#1549) |
|
4 |
+ |
|
5 |
+Tue Apr 15 17:03:30 CEST 2009 (acab) |
|
6 |
+------------------------------------ |
|
7 |
+ * clamav-milter: add support for AddHeader=Add, properly remove |
|
8 |
+ existing headers (bb#1549) |
|
9 |
+ |
|
1 | 10 |
Mon Apr 13 11:48:33 EEST 2009 (edwin) |
2 | 11 |
------------------------------------- |
3 | 12 |
* contrib/split-tarball.sh: Apply modifications by Michael |
... | ... |
@@ -56,7 +56,7 @@ int main(int argc, char **argv) { |
56 | 56 |
memset(&descr, 0, sizeof(struct smfiDesc)); |
57 | 57 |
descr.xxfi_name = "ClamAV"; /* filter name */ |
58 | 58 |
descr.xxfi_version = SMFI_VERSION; /* milter version */ |
59 |
- descr.xxfi_flags = SMFIF_CHGHDRS|SMFIF_QUARANTINE; /* flags */ |
|
59 |
+ descr.xxfi_flags = SMFIF_QUARANTINE; /* flags */ |
|
60 | 60 |
descr.xxfi_connect = clamfi_connect; /* connection info filter */ |
61 | 61 |
descr.xxfi_envfrom = clamfi_envfrom; /* envelope sender filter */ |
62 | 62 |
descr.xxfi_envrcpt = clamfi_envrcpt; /* envelope recipient filter */ |
... | ... |
@@ -219,7 +219,8 @@ int main(int argc, char **argv) { |
219 | 219 |
return 1; |
220 | 220 |
} |
221 | 221 |
|
222 |
- if(optget(opts, "AddHeader")->enabled) { |
|
222 |
+ pt = optget(opts, "AddHeader")->strarg; |
|
223 |
+ if(strcasecmp(pt, "No")) { |
|
223 | 224 |
char myname[255]; |
224 | 225 |
|
225 | 226 |
if(!gethostname(myname, sizeof(myname))) { |
... | ... |
@@ -230,7 +231,15 @@ int main(int argc, char **argv) { |
230 | 230 |
snprintf(xvirushdr, sizeof(xvirushdr), "clamav-milter %s", get_version()); |
231 | 231 |
xvirushdr[sizeof(xvirushdr)-1] = '\0'; |
232 | 232 |
} |
233 |
- addxvirus = 1; |
|
233 |
+ |
|
234 |
+ descr.xxfi_flags |= SMFIF_ADDHDRS; |
|
235 |
+ |
|
236 |
+ if(strcasecmp(pt, "Add")) { /* Replace or Yes */ |
|
237 |
+ descr.xxfi_flags |= SMFIF_CHGHDRS; |
|
238 |
+ addxvirus = 1; |
|
239 |
+ } else { /* Add */ |
|
240 |
+ addxvirus = 2; |
|
241 |
+ } |
|
234 | 242 |
} |
235 | 243 |
|
236 | 244 |
if(!(my_socket = optget(opts, "MilterSocket")->strarg)) { |
... | ... |
@@ -52,7 +52,7 @@ static sfsistat (*CleanAction)(SMFICTX *ctx); |
52 | 52 |
static sfsistat (*InfectedAction)(SMFICTX *ctx); |
53 | 53 |
static char *rejectfmt = NULL; |
54 | 54 |
|
55 |
-int addxvirus = 0; |
|
55 |
+int addxvirus = 0; /* 0 - don't add | 1 - replace | 2 - add */ |
|
56 | 56 |
char xvirushdr[255]; |
57 | 57 |
enum { |
58 | 58 |
LOGINF_NONE, |
... | ... |
@@ -76,12 +76,24 @@ struct CLAMFI { |
76 | 76 |
unsigned int bufsz; |
77 | 77 |
unsigned int all_whitelisted; |
78 | 78 |
unsigned int gotbody; |
79 |
+ unsigned int scanned_count; |
|
80 |
+ unsigned int status_count; |
|
79 | 81 |
}; |
80 | 82 |
|
81 | 83 |
|
82 |
-static void add_x_header(SMFICTX *ctx, char *st) { |
|
83 |
- smfi_chgheader(ctx, (char *)"X-Virus-Scanned", 1, xvirushdr); |
|
84 |
- smfi_chgheader(ctx, (char *)"X-Virus-Status", 1, st); |
|
84 |
+static void add_x_header(SMFICTX *ctx, char *st, unsigned int scanned, unsigned int status) { |
|
85 |
+ if(addxvirus == 1) { |
|
86 |
+ while(scanned) |
|
87 |
+ if(smfi_chgheader(ctx, (char *)"X-Virus-Scanned", scanned--, NULL) != MI_SUCCESS) |
|
88 |
+ logg("^Failed to remove existing X-Virus-Scanned header\n"); |
|
89 |
+ while(status) |
|
90 |
+ if(smfi_chgheader(ctx, (char *)"X-Virus-Status", status--, NULL) != MI_SUCCESS) |
|
91 |
+ logg("^Failed to remove existing X-Virus-Status header\n"); |
|
92 |
+ } |
|
93 |
+ if(smfi_addheader(ctx, (char *)"X-Virus-Scanned", xvirushdr) != MI_SUCCESS) |
|
94 |
+ logg("^Failed to add X-Virus-Scanned header\n"); |
|
95 |
+ if(smfi_addheader(ctx, (char *)"X-Virus-Status", st) != MI_SUCCESS) |
|
96 |
+ logg("^Failed to add X-Virus-Status header\n"); |
|
85 | 97 |
} |
86 | 98 |
|
87 | 99 |
enum CFWHAT { |
... | ... |
@@ -201,6 +213,11 @@ sfsistat clamfi_header(SMFICTX *ctx, char *headerf, char *headerv) { |
201 | 201 |
cf->msg_id = strdup(headerv ? headerv : ""); |
202 | 202 |
} |
203 | 203 |
|
204 |
+ if(addxvirus==1) { |
|
205 |
+ if(!strcasecmp(headerf, "X-Virus-Scanned")) cf->scanned_count++; |
|
206 |
+ if(!strcasecmp(headerf, "X-Virus-Status")) cf->status_count++; |
|
207 |
+ } |
|
208 |
+ |
|
204 | 209 |
if((ret = sendchunk(cf, (unsigned char *)headerf, strlen(headerf), ctx)) != SMFIS_CONTINUE) |
205 | 210 |
return ret; |
206 | 211 |
if((ret = sendchunk(cf, (unsigned char *)": ", 2, ctx)) != SMFIS_CONTINUE) |
... | ... |
@@ -290,7 +307,7 @@ sfsistat clamfi_eom(SMFICTX *ctx) { |
290 | 290 |
|
291 | 291 |
len = strlen(reply); |
292 | 292 |
if(len>5 && !strcmp(reply + len - 5, ": OK\n")) { |
293 |
- if(addxvirus) add_x_header(ctx, "Clean"); |
|
293 |
+ if(addxvirus) add_x_header(ctx, "Clean", cf->scanned_count, cf->status_count); |
|
294 | 294 |
ret = CleanAction(ctx); |
295 | 295 |
} else if (len>7 && !strcmp(reply + len - 7, " FOUND\n")) { |
296 | 296 |
cf->virusname = NULL; |
... | ... |
@@ -309,7 +326,7 @@ sfsistat clamfi_eom(SMFICTX *ctx) { |
309 | 309 |
char msg[255]; |
310 | 310 |
snprintf(msg, sizeof(msg), "Infected (%s)", vir); |
311 | 311 |
msg[sizeof(msg)-1] = '\0'; |
312 |
- add_x_header(ctx, msg); |
|
312 |
+ add_x_header(ctx, msg, cf->scanned_count, cf->status_count); |
|
313 | 313 |
} |
314 | 314 |
|
315 | 315 |
if(loginfected) { |
... | ... |
@@ -378,6 +395,8 @@ static int parse_action(char *action) { |
378 | 378 |
return 3; |
379 | 379 |
if(!strcasecmp(action, "Quarantine")) |
380 | 380 |
return 4; |
381 |
+ if(!strcasecmp(action, "QuarantineReject")) |
|
382 |
+ return 5; |
|
381 | 383 |
logg("!Unknown action %s\n", action); |
382 | 384 |
return -1; |
383 | 385 |
} |
... | ... |
@@ -395,12 +414,18 @@ static sfsistat action_reject(_UNUSED_ SMFICTX *ctx) { |
395 | 395 |
static sfsistat action_blackhole(_UNUSED_ SMFICTX *ctx) { |
396 | 396 |
return SMFIS_DISCARD; |
397 | 397 |
} |
398 |
-static sfsistat action_quarantine(SMFICTX *ctx) { |
|
398 |
+static sfsistat quarantine_common(SMFICTX *ctx, sfsistat ret) { |
|
399 | 399 |
if(smfi_quarantine(ctx, "quarantined by clamav-milter") != MI_SUCCESS) { |
400 | 400 |
logg("^Failed to quarantine message\n"); |
401 | 401 |
return SMFIS_TEMPFAIL; |
402 | 402 |
} |
403 |
- return SMFIS_ACCEPT; |
|
403 |
+ return ret; |
|
404 |
+} |
|
405 |
+static sfsistat action_quarantine(SMFICTX *ctx) { |
|
406 |
+ return quarantine_common(ctx, SMFIS_ACCEPT); |
|
407 |
+} |
|
408 |
+static sfsistat action_quarantine_reject(SMFICTX *ctx) { |
|
409 |
+ return quarantine_common(ctx, SMFIS_REJECT); |
|
404 | 410 |
} |
405 | 411 |
static sfsistat action_reject_msg(SMFICTX *ctx) { |
406 | 412 |
struct CLAMFI *cf; |
... | ... |
@@ -463,6 +488,9 @@ int init_actions(struct optstruct *opts) { |
463 | 463 |
case 4: |
464 | 464 |
CleanAction = action_quarantine; |
465 | 465 |
break; |
466 |
+ case 5: |
|
467 |
+ CleanAction = action_quarantine_reject; |
|
468 |
+ break; |
|
466 | 469 |
default: |
467 | 470 |
logg("!Invalid action %s for option OnClean\n", opt->strarg); |
468 | 471 |
return 1; |
... | ... |
@@ -483,6 +511,9 @@ int init_actions(struct optstruct *opts) { |
483 | 483 |
case 4: |
484 | 484 |
InfectedAction = action_quarantine; |
485 | 485 |
break; |
486 |
+ case 5: |
|
487 |
+ InfectedAction = action_quarantine_reject; |
|
488 |
+ break; |
|
486 | 489 |
case 2: |
487 | 490 |
InfectedAction = action_reject_msg; |
488 | 491 |
if((opt = optget(opts, "RejectMsg"))->enabled) { |
... | ... |
@@ -554,6 +585,10 @@ sfsistat clamfi_envfrom(SMFICTX *ctx, char **argv) { |
554 | 554 |
cf->all_whitelisted = 1; |
555 | 555 |
cf->gotbody = 0; |
556 | 556 |
cf->msg_subj = cf->msg_date = cf->msg_id = NULL; |
557 |
+ if(addxvirus==1) { |
|
558 |
+ cf->scanned_count = 0; |
|
559 |
+ cf->status_count = 0; |
|
560 |
+ } |
|
557 | 561 |
smfi_setpriv(ctx, (void *)cf); |
558 | 562 |
|
559 | 563 |
return SMFIS_CONTINUE; |
... | ... |
@@ -132,11 +132,15 @@ Example |
132 | 132 |
# - Defer |
133 | 133 |
# Return a temporary failure message (4xx) to the peer |
134 | 134 |
# - Blackhole (not available for OnFail) |
135 |
-# Like accept but the message is sent to oblivion |
|
135 |
+# Like Accept but the message is sent to oblivion |
|
136 | 136 |
# - Quarantine (not available for OnFail) |
137 |
-# Like accept but message is quarantined instead of being delivered |
|
138 |
-# In sendmail the quarantine queue can be examined via mailq -qQ |
|
139 |
-# For Postfix this causes the message to be accepted but placed on hold |
|
137 |
+# Like Accept but message is quarantined instead of being delivered |
|
138 |
+# - QuarantineReject (not available for OnFail) |
|
139 |
+# like Reject but a copy of the message is also quarantined |
|
140 |
+# Note: in Postfix this acts the same as Quarantine |
|
141 |
+# |
|
142 |
+# NOTE: In Sendmail the quarantine queue can be examined via mailq -qQ |
|
143 |
+# For Postfix this causes the message to be placed on hold |
|
140 | 144 |
# |
141 | 145 |
# Action to be performed on clean messages (mostly useful for testing) |
142 | 146 |
# Default Accept |
... | ... |
@@ -158,11 +162,15 @@ Example |
158 | 158 |
# Default: MTA specific |
159 | 159 |
#RejectMsg |
160 | 160 |
|
161 |
-# If this option is set to Yes, an "X-Virus-Scanned" and an "X-Virus-Status" |
|
162 |
-# headers will be attached to each processed message, possibly replacing |
|
163 |
-# existing headers. |
|
164 |
-# Default: No |
|
165 |
-#AddHeader Yes |
|
161 |
+# If this option is set to "Replace" (or "Yes"), an "X-Virus-Scanned" and an |
|
162 |
+# "X-Virus-Status" headers will be attached to each processed message, possibly |
|
163 |
+# replacing existing headers. |
|
164 |
+# If it is set to Add, the X-Virus headers are added possibly on top of the |
|
165 |
+# existing ones. |
|
166 |
+# Note that while "Replace" can potentially break DKIM signatures, "Add" may |
|
167 |
+# confuse procmail and similar filters. |
|
168 |
+# Default: no |
|
169 |
+#AddHeader Replace |
|
166 | 170 |
|
167 | 171 |
|
168 | 172 |
## |
... | ... |
@@ -373,15 +373,15 @@ const struct clam_option clam_options[] = { |
373 | 373 |
|
374 | 374 |
{ "LocalNet", NULL, 0, TYPE_STRING, NULL, -1, NULL, FLAG_MULTIPLE, OPT_MILTER, "Messages originating from these hosts/networks will not be scanned\nThis option takes a host(name)/mask pair in CIRD notation and can be\nrepeated several times. If \"/mask\" is omitted, a host is assumed.\nTo specify a locally orignated, non-smtp, email use the keyword \"local\".", "local\n192.168.0.0/24\n1111:2222:3333::/48" }, |
375 | 375 |
|
376 |
- { "OnClean", NULL, 0, TYPE_STRING, "^(Accept|Reject|Defer|Blackhole|Quarantine)$", -1, "Accept", 0, OPT_MILTER, "Action to be performed on clean messages (mostly useful for testing).\nThe following actions are available:\nAccept: the message is accepted for delievery;\nReject: immediately refuse delievery (a 5xx error is returned to the peer);\nDefer: return a temporary failure message (4xx) to the peer;\nBlackhole: like accept but the message is sent to oblivion;\nQuarantine: like accept but message is quarantined instead of being delivered,\n in sendmail the quarantine queue can be examined via mailq -qQ,\n for Postfix this causes the message to be accepted but placed on hold.", "Accept" }, |
|
376 |
+ { "OnClean", NULL, 0, TYPE_STRING, "^(Accept|Reject|Defer|Blackhole|Quarantine|QuarantineReject)$", -1, "Accept", 0, OPT_MILTER, "Action to be performed on clean messages (mostly useful for testing).\nThe following actions are available:\nAccept: the message is accepted for delievery\nReject: immediately refuse delievery (a 5xx error is returned to the peer)\nDefer: return a temporary failure message (4xx) to the peer\nBlackhole: like Accept but the message is sent to oblivion\nQuarantine: like Accept but message is quarantined instead of being delivered\nQuarantineReject: like Reject but a copy of the message is also quarantined", "Accept" }, |
|
377 | 377 |
|
378 |
- { "OnInfected", NULL, 0, TYPE_STRING, "^(Accept|Reject|Defer|Blackhole|Quarantine)$", -1, "Quarantine", 0, OPT_MILTER, "Action to be performed on infected messages.\nThe following actions are available:\nAccept: the message is accepted for delievery;\nReject: immediately refuse delievery (a 5xx error is returned to the peer);\nDefer: return a temporary failure message (4xx) to the peer;\nBlackhole: like accept but the message is sent to oblivion;\nQuarantine: like accept but message is quarantined instead of being delivered,\n in sendmail the quarantine queue can be examined via mailq -qQ,\n for Postfix this causes the message to be accepted but placed on hold.", "Quarantine" }, |
|
378 |
+ { "OnInfected", NULL, 0, TYPE_STRING, "^(Accept|Reject|Defer|Blackhole|Quarantine|QuarantineReject)$", -1, "Quarantine", 0, OPT_MILTER, "Action to be performed on clean messages (mostly useful for testing).\nThe following actions are available:\nAccept: the message is accepted for delievery\nReject: immediately refuse delievery (a 5xx error is returned to the peer)\nDefer: return a temporary failure message (4xx) to the peer\nBlackhole: like Accept but the message is sent to oblivion\nQuarantine: like Accept but message is quarantined instead of being delivered\nQuarantineReject: like Reject but a copy of the message is also quarantined", "Quarantine" }, |
|
379 | 379 |
|
380 |
- { "OnFail", NULL, 0, TYPE_STRING, "^(Accept|Reject|Defer)$", -1, "Defer", 0, OPT_MILTER, "Action to be performed on error conditions (this includes failure to\nallocate data structures, no scanners available, network timeouts, unknown\nscanner replies and the like).\nThe following actions are available:\nAccept: the message is accepted for delievery;\nReject: immediately refuse delievery (a 5xx error is returned to the peer);\nDefer: return a temporary failure message (4xx) to the peer.", "Defer" }, |
|
380 |
+ { "OnFail", NULL, 0, TYPE_STRING, "^(Accept|Reject|Defer)$", -1, "Defer", 0, OPT_MILTER, "Action to be performed on error conditions (this includes failure to\nallocate data structures, no scanners available, network timeouts, unknown\nscanner replies and the like.\nThe following actions are available:\nAccept: the message is accepted for delievery;\nReject: immediately refuse delievery (a 5xx error is returned to the peer);\nDefer: return a temporary failure message (4xx) to the peer.", "Defer" }, |
|
381 | 381 |
|
382 | 382 |
{ "RejectMsg", NULL, 0, TYPE_STRING, NULL, -1, NULL, 0, OPT_MILTER, "This option allows to set a specific rejection reason for infected messages\nand it's therefore only useful together with \"OnInfected Reject\"\nThe string \"%v\", if present, will be replaced with the virus name.", "MTA specific" }, |
383 | 383 |
|
384 |
- { "AddHeader", NULL, 0, TYPE_BOOL, MATCH_BOOL, 0, NULL, 0, OPT_MILTER, "If this option is set to Yes, an \"X-Virus-Scanned\" and an \"X-Virus-Status\"\nheaders will be attached to each processed message, possibly replacing\nexisting headers.", "Yes" }, |
|
384 |
+ { "AddHeader", NULL, 0, TYPE_STRING, "^(No|Replace|Yes|Add)$", -1, "no", 0, OPT_MILTER, "If this option is set to \"Replace\" (or \"Yes\"), an \"X-Virus-Scanned\" and an\n\"X-Virus-Status\" headers will be attached to each processed message, possibly\nreplacing existing headers.\nIf it is set to Add, the X-Virus headers are added possibly on top of the\nexisting ones.\nNote that while \"Replace\" can potentially break DKIM signatures, \"Add\" may\nconfuse procmail and similar filters.", "Replace" }, |
|
385 | 385 |
|
386 | 386 |
{ "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" }, |
387 | 387 |
|