Browse code

misc clamav milter

git-svn: trunk@4891

aCaB authored on 2009/03/04 04:42:33
Showing 6 changed files
... ...
@@ -1,3 +1,8 @@
1
+Tue Mar  3 20:40:07 CET 2009 (acab)
2
+-----------------------------------
3
+ * clamav-milter: Allow custom rejection messages, workaround ancient
4
+	systems without strerror_r (bb#1439), remove double syslog include
5
+
1 6
 Tue Mar  3 18:58:06 CET 2009 (tk)
2 7
 ---------------------------------
3 8
  * libclamav/unarj.c: downgrade error message (bb#1444)
... ...
@@ -28,7 +28,6 @@
28 28
 #include <pwd.h>
29 29
 #include <grp.h>
30 30
 #include <string.h>
31
-#include <syslog.h>
32 31
 #ifdef USE_SYSLOG
33 32
 #include <syslog.h>
34 33
 #endif
... ...
@@ -27,6 +27,7 @@
27 27
 #include <string.h>
28 28
 #include <sys/types.h>
29 29
 #include <unistd.h>
30
+#include <ctype.h>
30 31
 
31 32
 #include <libmilter/mfapi.h>
32 33
 
... ...
@@ -49,6 +50,7 @@ uint64_t maxfilesize;
49 49
 static sfsistat FailAction;
50 50
 static sfsistat (*CleanAction)(SMFICTX *ctx);
51 51
 static sfsistat (*InfectedAction)(SMFICTX *ctx);
52
+static char *rejectfmt = NULL;
52 53
 
53 54
 int addxvirus = 0;
54 55
 char xvirushdr[255];
... ...
@@ -57,6 +59,7 @@ char xvirushdr[255];
57 57
 
58 58
 struct CLAMFI {
59 59
     char buffer[CLAMFIBUFSZ];
60
+    const char *virusname;
60 61
     int local;
61 62
     int main;
62 63
     int alt;
... ...
@@ -236,18 +239,23 @@ sfsistat clamfi_eom(SMFICTX *ctx) {
236 236
 	if(addxvirus) add_x_header(ctx, "Clean");
237 237
 	ret = CleanAction(ctx);
238 238
     } else if (len>7 && !strcmp(reply + len - 7, " FOUND\n")) {
239
-	if(addxvirus) {
239
+	cf->virusname = NULL;
240
+	if(addxvirus || rejectfmt) {
240 241
 	    char *vir;
241 242
 
242 243
 	    reply[len-7] = '\0';
243 244
 	    vir = strrchr(reply, ' ');
244 245
 	    if(vir) {
245
-		char msg[255];
246
-
247 246
 		vir++;
248
-		snprintf(msg, sizeof(msg), "Infected (%s)", vir);
249
-		msg[sizeof(msg)-1] = '\0';
250
-		add_x_header(ctx, msg);
247
+
248
+		if(rejectfmt) {
249
+		    cf->virusname = vir;
250
+		} else {
251
+		    char msg[255];
252
+		    snprintf(msg, sizeof(msg), "Infected (%s)", vir);
253
+		    msg[sizeof(msg)-1] = '\0';
254
+		    add_x_header(ctx, msg);
255
+		}
251 256
 	    }
252 257
 	}
253 258
 	ret = InfectedAction(ctx);
... ...
@@ -320,6 +328,18 @@ static sfsistat action_quarantine(SMFICTX *ctx) {
320 320
     }
321 321
     return SMFIS_ACCEPT;
322 322
 }
323
+static sfsistat action_reject_msg(SMFICTX *ctx) {
324
+    struct CLAMFI *cf;
325
+    char buf[1024];
326
+
327
+    if(!rejectfmt || !(cf = (struct CLAMFI *)smfi_getpriv(ctx)))
328
+	return SMFIS_REJECT;
329
+
330
+    snprintf(buf, sizeof(buf), rejectfmt, cf->virusname);
331
+    buf[sizeof(buf)-1] = '\0';
332
+    smfi_setreply(ctx, "550", NULL, buf);
333
+    return SMFIS_REJECT;
334
+}
323 335
 
324 336
 int init_actions(struct optstruct *opts) {
325 337
     const struct optstruct *opt;
... ...
@@ -336,7 +356,7 @@ int init_actions(struct optstruct *opts) {
336 336
 	    FailAction = SMFIS_REJECT;
337 337
 	    break;
338 338
 	default:
339
-	    logg("!Invalid action %s for option OnFail", opt->strarg);
339
+	    logg("!Invalid action %s for option OnFail\n", opt->strarg);
340 340
 	    return 1;
341 341
 	}
342 342
     } else FailAction = SMFIS_TEMPFAIL;
... ...
@@ -359,7 +379,7 @@ int init_actions(struct optstruct *opts) {
359 359
 	    CleanAction = action_quarantine;
360 360
 	    break;
361 361
 	default:
362
-	    logg("!Invalid action %s for option OnClean", opt->strarg);
362
+	    logg("!Invalid action %s for option OnClean\n", opt->strarg);
363 363
 	    return 1;
364 364
 	}
365 365
     } else CleanAction = action_accept;
... ...
@@ -372,15 +392,49 @@ int init_actions(struct optstruct *opts) {
372 372
 	case 1:
373 373
 	    InfectedAction = action_defer;
374 374
 	    break;
375
-	case 2:
376
-	    InfectedAction = action_reject;
377
-	    break;
378 375
 	case 3:
379 376
 	    InfectedAction = action_blackhole;
380 377
 	    break;
381 378
 	case 4:
382 379
 	    InfectedAction = action_quarantine;
383 380
 	    break;
381
+	case 2:
382
+	    InfectedAction = action_reject_msg;
383
+	    if((opt = optget(opts, "RejectMsg"))->enabled) {
384
+		const char *src = opt->strarg;
385
+		char *dst, c;
386
+		int gotpctv = 0;
387
+
388
+		rejectfmt = dst = malloc(strlen(src) * 4 + 1);
389
+		if(!dst) {
390
+		    logg("!Failed to allocate memory for RejectMsg\n");
391
+		    return 1;
392
+		}
393
+		while ((c = *src++)) {
394
+		    if(!isprint(c)) {
395
+			logg("!RejectMsg contains non printable characters\n");
396
+			free(rejectfmt);
397
+			return 1;
398
+		    }
399
+		    *dst++ = c;
400
+		    if(c == '%') {
401
+			if(*src == 'v') {
402
+			    if(gotpctv) {
403
+				logg("!%%v may appear at most once in RejectMsg\n");
404
+				free(rejectfmt);
405
+				return 1;
406
+			    }
407
+			    gotpctv |= 1;
408
+			    src++;
409
+			    *dst++ = 's';
410
+			} else {
411
+			    dst[0] = dst[1] = dst[2] = '%';
412
+			    dst += 3;
413
+			}
414
+		    }
415
+		}
416
+		*dst = '\0';
417
+	    }
384 418
 	default:
385 419
 	    logg("!Invalid action %s for option OnInfected", opt->strarg);
386 420
 	    return 1;
... ...
@@ -40,12 +40,18 @@
40 40
 #include <netdb.h>
41 41
 #include <sys/uio.h>
42 42
 
43
-
44 43
 #include "shared/output.h"
45 44
 #include "shared/optparser.h"
46 45
 #include "libclamav/others.h"
47 46
 #include "netcode.h"
48 47
 
48
+#ifdef HAVE_STRERROR_R
49
+#define strerror_print(msg) \
50
+	strerror_r(errno, er, sizeof(er)); \
51
+	logg(msg": %s\n", er);
52
+#else
53
+	logg(msg"\n");
54
+#endif
49 55
 
50 56
 enum {
51 57
     NON_SMTP,
... ...
@@ -75,21 +81,18 @@ static int nc_socket(struct CP_ENTRY *cpe) {
75 75
     char er[256];
76 76
 
77 77
     if (s == -1) {
78
-	strerror_r(errno, er, sizeof(er));
79
-	logg("!Failed to create socket: %s\n", er);
78
+	strerror_print("!Failed to create socket");
80 79
 	return -1;
81 80
     }
82 81
     flags = fcntl(s, F_GETFL, 0);
83 82
     if (flags == -1) {
84
-	strerror_r(errno, er, sizeof(er));
85
-	logg("!fcntl_get failed: %s\n", er);
83
+	strerror_print("!fcntl_get failed");
86 84
 	close(s);
87 85
 	return -1;
88 86
     }
89 87
     flags |= O_NONBLOCK;
90 88
     if (fcntl(s, F_SETFL, flags) == -1) {
91
-	strerror_r(errno, er, sizeof(er));
92
-	logg("!fcntl_set failed: %s\n", er);
89
+	strerror_print("!fcntl_set failed");
93 90
 	close(s);
94 91
 	return -1;
95 92
     }
... ...
@@ -105,8 +108,7 @@ static int nc_connect(int s, struct CP_ENTRY *cpe) {
105 105
 
106 106
     if (!res) return 0;
107 107
     if (errno != EINPROGRESS) {
108
-	strerror_r(errno, er, sizeof(er));
109
-	logg("*connect failed: %s\n", er);
108
+	strerror_print("*connect failed");
110 109
 	close(s);
111 110
 	return -1;
112 111
     }
... ...
@@ -158,8 +160,7 @@ int nc_send(int s, const void *buff, size_t len) {
158 158
 	    continue;
159 159
 	}
160 160
 	if(errno != EAGAIN && errno != EWOULDBLOCK) {
161
-	    strerror_r(errno, er, sizeof(er));
162
-	    logg("!send failed: %s\n", er);
161
+	    strerror_print("!send failed");
163 162
 	    close(s);
164 163
 	    return 1;
165 164
 	}
... ...
@@ -219,8 +220,7 @@ int nc_sendmsg(int s, int fd) {
219 219
 
220 220
     if((ret = sendmsg(s, &msg, 0)) == -1) {
221 221
 	char er[256];
222
-	strerror_r(errno, er, sizeof(er));
223
-	logg("!clamfi_eom: FD send failed (%s)\n", er);
222
+	strerror_print("!clamfi_eom: FD send failed");
224 223
 	close(s);
225 224
     }
226 225
     return ret;
... ...
@@ -257,8 +257,7 @@ char *nc_recv(int s) {
257 257
 	res = recv(s, &buf[len], sizeof(buf) - len, 0);
258 258
 	if(res==-1) {
259 259
 	    char er[256];
260
-	    strerror_r(errno, er, sizeof(er));
261
-	    logg("!recv failed after successful select: %s\n", er);
260
+	    strerror_print("!recv failed after successful select");
262 261
 	    close(s);
263 262
 	    return NULL;
264 263
 	}
... ...
@@ -145,6 +145,12 @@ Example
145 145
 # Default Defer
146 146
 #OnFail Defer
147 147
 
148
+# This option allows to set a specific rejection reason for infected messages
149
+# and it's therefore only useful together with "OnInfected Reject"
150
+# The string "%v", if present, will be replaced with the virus name.
151
+# Default: MTA specific
152
+#RejectMsg 
153
+
148 154
 # If this option is set to Yes, an "X-Virus-Scanned" and an "X-Virus-Status"
149 155
 # headers will be attached to each processed message, possibly replacing
150 156
 # existing headers. 
... ...
@@ -373,6 +373,8 @@ const struct clam_option clam_options[] = {
373 373
 
374 374
     { "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" },
375 375
 
376
+    { "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" },
377
+
376 378
     { "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" },
377 379
 
378 380
     { "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" },