git-svn: trunk@4526
aCaB authored on 2008/12/05 01:26:47... | ... |
@@ -47,7 +47,7 @@ |
47 | 47 |
struct smfiDesc descr = { |
48 | 48 |
"ClamAV", /* filter name */ |
49 | 49 |
SMFI_VERSION, /* milter version */ |
50 |
- SMFIF_ADDHDRS|SMFIF_ADDRCPT, /* flags */ |
|
50 |
+ SMFIF_ADDHDRS|SMFIF_QUARANTINE, /* flags */ |
|
51 | 51 |
clamfi_connect, /* connection info filter */ |
52 | 52 |
NULL, /* SMTP HELO command filter */ |
53 | 53 |
NULL, /* envelope sender filter */ |
... | ... |
@@ -197,12 +197,18 @@ int main(int argc, char **argv) { |
197 | 197 |
} |
198 | 198 |
#endif |
199 | 199 |
|
200 |
- if(localnets_init(copt)) { |
|
200 |
+ if(localnets_init(copt) || init_actions(copt)) { |
|
201 | 201 |
logg_close(); |
202 | 202 |
freecfg(copt); |
203 | 203 |
return 1; |
204 | 204 |
} |
205 | 205 |
|
206 |
+ /* FIXME: find a place for these: |
|
207 |
+ * maxthreads = cfgopt(copt, "MaxThreads")->numarg; |
|
208 |
+ * logclean = cfgopt(copt, "LogClean")->numarg; |
|
209 |
+ */ |
|
210 |
+ |
|
211 |
+ |
|
206 | 212 |
umask(0007); |
207 | 213 |
if(!(my_socket = cfgopt(copt, "MilterSocket")->strarg)) { |
208 | 214 |
logg("!Please configure the MilterSocket directive\n"); |
... | ... |
@@ -29,6 +29,7 @@ |
29 | 29 |
|
30 | 30 |
#include <libmilter/mfapi.h> |
31 | 31 |
|
32 |
+#include "shared/cfgparser.h" |
|
32 | 33 |
#include "shared/output.h" |
33 | 34 |
|
34 | 35 |
#include "connpool.h" |
... | ... |
@@ -36,6 +37,10 @@ |
36 | 36 |
|
37 | 37 |
uint64_t maxfilesize; |
38 | 38 |
|
39 |
+static sfsistat FailAction; |
|
40 |
+static sfsistat (*CleanAction)(SMFICTX *ctx); |
|
41 |
+static sfsistat (*InfectedAction)(SMFICTX *ctx); |
|
42 |
+ |
|
39 | 43 |
#define CLAMFIBUFSZ 1424 |
40 | 44 |
|
41 | 45 |
struct CLAMFI { |
... | ... |
@@ -65,7 +70,7 @@ static sfsistat sendchunk(struct CLAMFI *cf, unsigned char *bodyp, size_t len, S |
65 | 65 |
close(cf->alt); |
66 | 66 |
smfi_setpriv(ctx, NULL); |
67 | 67 |
free(cf); |
68 |
- return SMFIS_TEMPFAIL; |
|
68 |
+ return FailAction; |
|
69 | 69 |
} |
70 | 70 |
len -= n; |
71 | 71 |
bodyp += n; |
... | ... |
@@ -92,7 +97,7 @@ static sfsistat sendchunk(struct CLAMFI *cf, unsigned char *bodyp, size_t len, S |
92 | 92 |
close(cf->main); |
93 | 93 |
smfi_setpriv(ctx, NULL); |
94 | 94 |
free(cf); |
95 |
- return SMFIS_TEMPFAIL; |
|
95 |
+ return FailAction; |
|
96 | 96 |
} |
97 | 97 |
} |
98 | 98 |
cf->totsz += len; |
... | ... |
@@ -104,14 +109,20 @@ sfsistat clamfi_header(SMFICTX *ctx, char *headerf, char *headerv) { |
104 | 104 |
struct CLAMFI *cf; |
105 | 105 |
sfsistat ret; |
106 | 106 |
|
107 |
- if(!(cf = (struct CLAMFI *)smfi_getpriv(ctx))) |
|
108 |
- return SMFIS_CONTINUE; /* whatever */ |
|
107 |
+ if(!(cf = (struct CLAMFI *)smfi_getpriv(ctx))) { |
|
108 |
+ if(!(cf = (struct CLAMFI *)malloc(sizeof(*cf)))) { |
|
109 |
+ logg("!Failed to allocate CLAMFI struct\n"); |
|
110 |
+ return FailAction; |
|
111 |
+ } |
|
112 |
+ cf->totsz = 0; |
|
113 |
+ cf->bufsz = 0; |
|
114 |
+ smfi_setpriv(ctx, (void *)cf); |
|
109 | 115 |
|
110 |
- if(!cf->totsz) { |
|
111 | 116 |
if(nc_connect_rand(&cf->main, &cf->alt, &cf->local)) { |
112 | 117 |
logg("!Failed to initiate streaming/fdpassing\n"); |
118 |
+ smfi_setpriv(ctx, NULL); |
|
113 | 119 |
free(cf); |
114 |
- return SMFIS_TEMPFAIL; |
|
120 |
+ return FailAction; |
|
115 | 121 |
} |
116 | 122 |
if((ret = sendchunk(cf, (unsigned char *)"From clamav-milter\n", 19, ctx)) != SMFIS_CONTINUE) |
117 | 123 |
return ret; |
... | ... |
@@ -149,7 +160,7 @@ sfsistat clamfi_eom(SMFICTX *ctx) { |
149 | 149 |
close(cf->alt); |
150 | 150 |
smfi_setpriv(ctx, NULL); |
151 | 151 |
free(cf); |
152 |
- return SMFIS_TEMPFAIL; |
|
152 |
+ return FailAction; |
|
153 | 153 |
} |
154 | 154 |
|
155 | 155 |
lseek(cf->alt, 0, SEEK_SET); |
... | ... |
@@ -159,7 +170,7 @@ sfsistat clamfi_eom(SMFICTX *ctx) { |
159 | 159 |
close(cf->alt); |
160 | 160 |
smfi_setpriv(ctx, NULL); |
161 | 161 |
free(cf); |
162 |
- return SMFIS_TEMPFAIL; |
|
162 |
+ return FailAction; |
|
163 | 163 |
} |
164 | 164 |
} else { |
165 | 165 |
if(cf->bufsz && nc_send(cf->alt, cf->buffer, cf->bufsz)) { |
... | ... |
@@ -167,7 +178,7 @@ sfsistat clamfi_eom(SMFICTX *ctx) { |
167 | 167 |
close(cf->main); |
168 | 168 |
smfi_setpriv(ctx, NULL); |
169 | 169 |
free(cf); |
170 |
- return SMFIS_TEMPFAIL; |
|
170 |
+ return FailAction; |
|
171 | 171 |
} |
172 | 172 |
close(cf->alt); |
173 | 173 |
} |
... | ... |
@@ -181,7 +192,7 @@ sfsistat clamfi_eom(SMFICTX *ctx) { |
181 | 181 |
logg("!No reply from clamd\n"); |
182 | 182 |
smfi_setpriv(ctx, NULL); |
183 | 183 |
free(cf); |
184 |
- return SMFIS_TEMPFAIL; |
|
184 |
+ return FailAction; |
|
185 | 185 |
} |
186 | 186 |
close(cf->main); |
187 | 187 |
smfi_setpriv(ctx, NULL); |
... | ... |
@@ -189,12 +200,12 @@ sfsistat clamfi_eom(SMFICTX *ctx) { |
189 | 189 |
|
190 | 190 |
len = strlen(reply); |
191 | 191 |
if(len>5 && !strcmp(reply + len - 5, ": OK\n")) |
192 |
- ret = SMFIS_ACCEPT; |
|
192 |
+ ret = CleanAction(ctx); |
|
193 | 193 |
else if (len>7 && !strcmp(reply + len - 7, " FOUND\n")) |
194 |
- ret = SMFIS_REJECT; |
|
194 |
+ ret = InfectedAction(ctx); |
|
195 | 195 |
else { |
196 | 196 |
logg("!Unknown reply from clamd\n"); |
197 |
- ret = SMFIS_TEMPFAIL; |
|
197 |
+ ret = FailAction; |
|
198 | 198 |
} |
199 | 199 |
|
200 | 200 |
free(reply); |
... | ... |
@@ -224,15 +235,112 @@ sfsistat clamfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr) { |
224 | 224 |
} |
225 | 225 |
break; |
226 | 226 |
} |
227 |
+ return SMFIS_CONTINUE; |
|
228 |
+} |
|
227 | 229 |
|
228 |
- if(!(cf = (struct CLAMFI *)malloc(sizeof(*cf)))) { |
|
229 |
- logg("!Failed to allocate CLAMFI struct\n"); |
|
230 |
+ |
|
231 |
+static int parse_action(char *action) { |
|
232 |
+ if(!strcasecmp(action, "Accept")) |
|
233 |
+ return 0; |
|
234 |
+ if(!strcasecmp(action, "Defer")) |
|
235 |
+ return 1; |
|
236 |
+ if(!strcasecmp(action, "Reject")) |
|
237 |
+ return 2; |
|
238 |
+ if(!strcasecmp(action, "Blackhole")) |
|
239 |
+ return 3; |
|
240 |
+ if(!strcasecmp(action, "Quarantine")) |
|
241 |
+ return 4; |
|
242 |
+ logg("!Unknown action %s\n", action); |
|
243 |
+ return -1; |
|
244 |
+} |
|
245 |
+ |
|
246 |
+ |
|
247 |
+static sfsistat action_accept(SMFICTX *ctx) { |
|
248 |
+ return SMFIS_ACCEPT; |
|
249 |
+} |
|
250 |
+static sfsistat action_defer(SMFICTX *ctx) { |
|
251 |
+ return SMFIS_TEMPFAIL; |
|
252 |
+} |
|
253 |
+static sfsistat action_reject(SMFICTX *ctx) { |
|
254 |
+ return SMFIS_REJECT; |
|
255 |
+} |
|
256 |
+static sfsistat action_blackhole(SMFICTX *ctx) { |
|
257 |
+ return SMFIS_DISCARD; |
|
258 |
+} |
|
259 |
+static sfsistat action_quarantine(SMFICTX *ctx) { |
|
260 |
+ if(smfi_quarantine(ctx, "quarantined by clamav-milter") != MI_SUCCESS) { |
|
261 |
+ logg("^Failed to quarantine message\n"); |
|
230 | 262 |
return SMFIS_TEMPFAIL; |
231 | 263 |
} |
232 |
- cf->totsz = 0; |
|
233 |
- cf->bufsz = 0; |
|
234 |
- smfi_setpriv(ctx, (void *)cf); |
|
235 |
- return SMFIS_CONTINUE; |
|
264 |
+ return SMFIS_ACCEPT; |
|
265 |
+} |
|
266 |
+ |
|
267 |
+int init_actions(struct cfgstruct *copt) { |
|
268 |
+ const struct cfgstruct *cpt; |
|
269 |
+ |
|
270 |
+ if((cpt = cfgopt(copt, "OnFail"))->enabled) { |
|
271 |
+ switch(parse_action(cpt->strarg)) { |
|
272 |
+ case 0: |
|
273 |
+ FailAction = SMFIS_ACCEPT; |
|
274 |
+ break; |
|
275 |
+ case 1: |
|
276 |
+ FailAction = SMFIS_TEMPFAIL; |
|
277 |
+ break; |
|
278 |
+ case 2: |
|
279 |
+ FailAction = SMFIS_REJECT; |
|
280 |
+ break; |
|
281 |
+ default: |
|
282 |
+ logg("!Invalid action %s for option OnFail", cpt->strarg); |
|
283 |
+ return 1; |
|
284 |
+ } |
|
285 |
+ } else FailAction = SMFIS_TEMPFAIL; |
|
286 |
+ |
|
287 |
+ if((cpt = cfgopt(copt, "OnClean"))->enabled) { |
|
288 |
+ switch(parse_action(cpt->strarg)) { |
|
289 |
+ case 0: |
|
290 |
+ CleanAction = action_accept; |
|
291 |
+ break; |
|
292 |
+ case 1: |
|
293 |
+ CleanAction = action_defer; |
|
294 |
+ break; |
|
295 |
+ case 2: |
|
296 |
+ CleanAction = action_reject; |
|
297 |
+ break; |
|
298 |
+ case 3: |
|
299 |
+ CleanAction = action_blackhole; |
|
300 |
+ break; |
|
301 |
+ case 4: |
|
302 |
+ CleanAction = action_quarantine; |
|
303 |
+ break; |
|
304 |
+ default: |
|
305 |
+ logg("!Invalid action %s for option OnClean", cpt->strarg); |
|
306 |
+ return 1; |
|
307 |
+ } |
|
308 |
+ } else CleanAction = action_accept; |
|
309 |
+ |
|
310 |
+ if((cpt = cfgopt(copt, "OnInfected"))->enabled) { |
|
311 |
+ switch(parse_action(cpt->strarg)) { |
|
312 |
+ case 0: |
|
313 |
+ InfectedAction = action_accept; |
|
314 |
+ break; |
|
315 |
+ case 1: |
|
316 |
+ InfectedAction = action_defer; |
|
317 |
+ break; |
|
318 |
+ case 2: |
|
319 |
+ InfectedAction = action_reject; |
|
320 |
+ break; |
|
321 |
+ case 3: |
|
322 |
+ InfectedAction = action_blackhole; |
|
323 |
+ break; |
|
324 |
+ case 4: |
|
325 |
+ InfectedAction = action_quarantine; |
|
326 |
+ break; |
|
327 |
+ default: |
|
328 |
+ logg("!Invalid action %s for option OnInfected", cpt->strarg); |
|
329 |
+ return 1; |
|
330 |
+ } |
|
331 |
+ } else InfectedAction = action_quarantine; |
|
332 |
+ return 0; |
|
236 | 333 |
} |
237 | 334 |
|
238 | 335 |
/* |
... | ... |
@@ -1,6 +1,7 @@ |
1 | 1 |
#ifndef _CLAMFI_H |
2 | 2 |
#define _CLAMFI_H |
3 | 3 |
|
4 |
+#include "shared/cfgparser.h" |
|
4 | 5 |
#include <libmilter/mfapi.h> |
5 | 6 |
|
6 | 7 |
uint64_t maxfilesize; |
... | ... |
@@ -9,4 +10,6 @@ sfsistat clamfi_body(SMFICTX *ctx, unsigned char *bodyp, size_t len); |
9 | 9 |
sfsistat clamfi_eom(SMFICTX *ctx); |
10 | 10 |
sfsistat clamfi_header(SMFICTX *ctx, char *headerf, char *headerv); |
11 | 11 |
sfsistat clamfi_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr); |
12 |
+int init_actions(struct cfgstruct *copt); |
|
13 |
+ |
|
12 | 14 |
#endif |
... | ... |
@@ -91,6 +91,38 @@ Example |
91 | 91 |
|
92 | 92 |
|
93 | 93 |
## |
94 |
+## Actions |
|
95 |
+## |
|
96 |
+ |
|
97 |
+# The following group of options controls the delievery process under |
|
98 |
+# different circumstances. |
|
99 |
+# The following actions are available: |
|
100 |
+# - Accept |
|
101 |
+# The message is accepted for delievery |
|
102 |
+# - Reject |
|
103 |
+# Immediately refuse delievery (a 5xx error is returned to the peer) |
|
104 |
+# - Defer |
|
105 |
+# Return a temporary failure message (4xx) to the peer |
|
106 |
+# - Blackhole (not available for OnFail) |
|
107 |
+# Like accept but the message is sent to oblivion |
|
108 |
+# - Quarantine (not available for OnFail) |
|
109 |
+# Like accept but message is quarantined instead of being deilievered |
|
110 |
+# |
|
111 |
+# Action to be performed on clean messages (mostly useful for testing) |
|
112 |
+# Default Accept |
|
113 |
+#OnClean Accept |
|
114 |
+ |
|
115 |
+# Action to be performed on infected messages |
|
116 |
+# Default: Quarantine |
|
117 |
+#OnInfected Quarantine |
|
118 |
+ |
|
119 |
+# Action to be performed on error conditions (this includes failure to |
|
120 |
+# allocate data structures, no scanners available, network timeouts, |
|
121 |
+# unknown scanner replies and the like) |
|
122 |
+# Default Defer |
|
123 |
+#OnFail Defer |
|
124 |
+ |
|
125 |
+## |
|
94 | 126 |
## Logging options |
95 | 127 |
## |
96 | 128 |
|
... | ... |
@@ -183,7 +215,7 @@ Example |
183 | 183 |
#-C --chroot |
184 | 184 |
#-D --debug |
185 | 185 |
#-i --pidfile |
186 |
-#-I --ignore |
|
186 |
+##-I --ignore |
|
187 | 187 |
#-W --whitelist-file |
188 | 188 |
|
189 | 189 |
#Deprecated switches |
... | ... |
@@ -147,6 +147,9 @@ struct cfgoption cfg_options[] = { |
147 | 147 |
{"ClamdSocket", OPT_QUOTESTR, -1, NULL, 1, OPT_MILTER}, |
148 | 148 |
{"MilterSocket", OPT_QUOTESTR, -1, NULL, 1, OPT_MILTER}, |
149 | 149 |
{"LocalNet", OPT_QUOTESTR, -1, NULL, 1, OPT_MILTER}, |
150 |
+ {"ActionClean", OPT_QUOTESTR, -1, "Accept", 1, OPT_MILTER}, |
|
151 |
+ {"ActionInfected", OPT_QUOTESTR, -1, "Quarantine", 1, OPT_MILTER}, |
|
152 |
+ {"ActionFail", OPT_QUOTESTR, -1, "Defer", 1, OPT_MILTER}, |
|
150 | 153 |
|
151 | 154 |
/* Deprecated milter options */ |
152 | 155 |
{"ArchiveBlockEncrypted", OPT_BOOL, 0, NULL, 0, OPT_MILTER | OPT_DEPRECATED}, |