Commit | Line | Data |
---|---|---|
8523533c TK |
1 | /************************************************* |
2 | * Exim - an Internet mail transport agent * | |
3 | *************************************************/ | |
8e669ac1 | 4 | |
8523533c TK |
5 | /* Code for calling Brightmail AntiSpam. |
6 | Copyright (c) Tom Kistner <tom@duncanthrax.net> 2004 | |
7 | License: GPL */ | |
8 | ||
9 | #include "exim.h" | |
10 | #ifdef EXPERIMENTAL_BRIGHTMAIL | |
11 | ||
12 | #include "bmi_spam.h" | |
13 | ||
14 | uschar *bmi_current_optin = NULL; | |
15 | ||
16 | uschar *bmi_process_message(header_line *header_list, int data_fd) { | |
17 | BmiSystem *system = NULL; | |
18 | BmiMessage *message = NULL; | |
19 | BmiError err; | |
20 | BmiErrorLocation err_loc; | |
21 | BmiErrorType err_type; | |
22 | const BmiVerdict *verdict = NULL; | |
23 | FILE *data_file; | |
24 | uschar data_buffer[4096]; | |
25 | uschar localhost[] = "127.0.0.1"; | |
26 | uschar *host_address; | |
27 | uschar *verdicts = NULL; | |
28 | int i,j; | |
8e669ac1 | 29 | |
5903c6ff | 30 | err = bmiInitSystem(BMI_VERSION, CS bmi_config_file, &system); |
8523533c TK |
31 | if (bmiErrorIsFatal(err) == BMI_TRUE) { |
32 | err_loc = bmiErrorGetLocation(err); | |
33 | err_type = bmiErrorGetType(err); | |
34 | log_write(0, LOG_PANIC, | |
35 | "bmi error [loc %d type %d]: could not initialize Brightmail system.", (int)err_loc, (int)err_type); | |
36 | return NULL; | |
37 | } | |
38 | ||
39 | err = bmiInitMessage(system, &message); | |
40 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
41 | err_loc = bmiErrorGetLocation(err); | |
42 | err_type = bmiErrorGetType(err); | |
43 | log_write(0, LOG_PANIC, | |
44 | "bmi error [loc %d type %d]: could not initialize Brightmail message.", (int)err_loc, (int)err_type); | |
45 | bmiFreeSystem(system); | |
46 | return NULL; | |
47 | } | |
48 | ||
49 | /* Send IP address of sending host */ | |
50 | if (sender_host_address == NULL) | |
51 | host_address = localhost; | |
52 | else | |
53 | host_address = sender_host_address; | |
5903c6ff | 54 | err = bmiProcessConnection(CS host_address, message); |
8523533c TK |
55 | if (bmiErrorIsFatal(err) == BMI_TRUE) { |
56 | err_loc = bmiErrorGetLocation(err); | |
57 | err_type = bmiErrorGetType(err); | |
58 | log_write(0, LOG_PANIC, | |
5903c6ff | 59 | "bmi error [loc %d type %d]: bmiProcessConnection() failed (IP %s).", (int)err_loc, (int)err_type, CS host_address); |
8523533c TK |
60 | bmiFreeMessage(message); |
61 | bmiFreeSystem(system); | |
62 | return NULL; | |
63 | }; | |
64 | ||
65 | /* Send envelope sender address */ | |
5903c6ff | 66 | err = bmiProcessFROM(CS sender_address, message); |
8523533c TK |
67 | if (bmiErrorIsFatal(err) == BMI_TRUE) { |
68 | err_loc = bmiErrorGetLocation(err); | |
69 | err_type = bmiErrorGetType(err); | |
70 | log_write(0, LOG_PANIC, | |
5903c6ff | 71 | "bmi error [loc %d type %d]: bmiProcessFROM() failed (address %s).", (int)err_loc, (int)err_type, CS sender_address); |
8523533c TK |
72 | bmiFreeMessage(message); |
73 | bmiFreeSystem(system); | |
74 | return NULL; | |
75 | }; | |
76 | ||
77 | /* Send envelope recipients */ | |
78 | for(i=0;i<recipients_count;i++) { | |
79 | recipient_item *r = recipients_list + i; | |
80 | BmiOptin *optin = NULL; | |
8e669ac1 | 81 | |
8523533c TK |
82 | /* create optin object if optin string is given */ |
83 | if ((r->bmi_optin != NULL) && (Ustrlen(r->bmi_optin) > 1)) { | |
84 | debug_printf("passing bmiOptin string: %s\n", r->bmi_optin); | |
85 | bmiOptinInit(&optin); | |
86 | err = bmiOptinMset(optin, r->bmi_optin, ':'); | |
87 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
88 | log_write(0, LOG_PANIC|LOG_MAIN, | |
5903c6ff | 89 | "bmi warning: [loc %d type %d]: bmiOptinMSet() failed (address '%s', string '%s').", (int)err_loc, (int)err_type, CS r->address, CS r->bmi_optin); |
8523533c TK |
90 | if (optin != NULL) |
91 | bmiOptinFree(optin); | |
92 | optin = NULL; | |
93 | }; | |
94 | }; | |
8e669ac1 | 95 | |
5903c6ff | 96 | err = bmiAccumulateTO(CS r->address, optin, message); |
8e669ac1 | 97 | |
8523533c TK |
98 | if (optin != NULL) |
99 | bmiOptinFree(optin); | |
8e669ac1 | 100 | |
8523533c TK |
101 | if (bmiErrorIsFatal(err) == BMI_TRUE) { |
102 | err_loc = bmiErrorGetLocation(err); | |
103 | err_type = bmiErrorGetType(err); | |
104 | log_write(0, LOG_PANIC, | |
5903c6ff | 105 | "bmi error [loc %d type %d]: bmiAccumulateTO() failed (address %s).", (int)err_loc, (int)err_type, CS r->address); |
8523533c TK |
106 | bmiFreeMessage(message); |
107 | bmiFreeSystem(system); | |
108 | return NULL; | |
109 | }; | |
110 | }; | |
111 | err = bmiEndTO(message); | |
112 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
113 | err_loc = bmiErrorGetLocation(err); | |
114 | err_type = bmiErrorGetType(err); | |
115 | log_write(0, LOG_PANIC, | |
116 | "bmi error [loc %d type %d]: bmiEndTO() failed.", (int)err_loc, (int)err_type); | |
117 | bmiFreeMessage(message); | |
118 | bmiFreeSystem(system); | |
119 | return NULL; | |
120 | }; | |
8e669ac1 | 121 | |
8523533c TK |
122 | /* Send message headers */ |
123 | while (header_list != NULL) { | |
124 | /* skip deleted headers */ | |
125 | if (header_list->type == '*') { | |
126 | header_list = header_list->next; | |
127 | continue; | |
128 | }; | |
5903c6ff | 129 | err = bmiAccumulateHeaders(CCS header_list->text, header_list->slen, message); |
8523533c TK |
130 | if (bmiErrorIsFatal(err) == BMI_TRUE) { |
131 | err_loc = bmiErrorGetLocation(err); | |
132 | err_type = bmiErrorGetType(err); | |
133 | log_write(0, LOG_PANIC, | |
134 | "bmi error [loc %d type %d]: bmiAccumulateHeaders() failed.", (int)err_loc, (int)err_type); | |
135 | bmiFreeMessage(message); | |
136 | bmiFreeSystem(system); | |
137 | return NULL; | |
138 | }; | |
139 | header_list = header_list->next; | |
140 | }; | |
141 | err = bmiEndHeaders(message); | |
142 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
143 | err_loc = bmiErrorGetLocation(err); | |
144 | err_type = bmiErrorGetType(err); | |
145 | log_write(0, LOG_PANIC, | |
146 | "bmi error [loc %d type %d]: bmiEndHeaders() failed.", (int)err_loc, (int)err_type); | |
147 | bmiFreeMessage(message); | |
148 | bmiFreeSystem(system); | |
149 | return NULL; | |
150 | }; | |
8e669ac1 | 151 | |
8523533c TK |
152 | /* Send body */ |
153 | data_file = fdopen(data_fd,"r"); | |
154 | do { | |
155 | j = fread(data_buffer, 1, sizeof(data_buffer), data_file); | |
156 | if (j > 0) { | |
5903c6ff | 157 | err = bmiAccumulateBody(CCS data_buffer, j, message); |
8523533c TK |
158 | if (bmiErrorIsFatal(err) == BMI_TRUE) { |
159 | err_loc = bmiErrorGetLocation(err); | |
160 | err_type = bmiErrorGetType(err); | |
161 | log_write(0, LOG_PANIC, | |
162 | "bmi error [loc %d type %d]: bmiAccumulateBody() failed.", (int)err_loc, (int)err_type); | |
163 | bmiFreeMessage(message); | |
164 | bmiFreeSystem(system); | |
165 | return NULL; | |
166 | }; | |
167 | }; | |
168 | } while (j > 0); | |
169 | err = bmiEndBody(message); | |
170 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
171 | err_loc = bmiErrorGetLocation(err); | |
172 | err_type = bmiErrorGetType(err); | |
173 | log_write(0, LOG_PANIC, | |
174 | "bmi error [loc %d type %d]: bmiEndBody() failed.", (int)err_loc, (int)err_type); | |
175 | bmiFreeMessage(message); | |
176 | bmiFreeSystem(system); | |
177 | return NULL; | |
178 | }; | |
8e669ac1 PH |
179 | |
180 | ||
8523533c TK |
181 | /* End message */ |
182 | err = bmiEndMessage(message); | |
183 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
184 | err_loc = bmiErrorGetLocation(err); | |
185 | err_type = bmiErrorGetType(err); | |
186 | log_write(0, LOG_PANIC, | |
187 | "bmi error [loc %d type %d]: bmiEndMessage() failed.", (int)err_loc, (int)err_type); | |
188 | bmiFreeMessage(message); | |
189 | bmiFreeSystem(system); | |
190 | return NULL; | |
191 | }; | |
8e669ac1 | 192 | |
f3ebb786 JH |
193 | /* Get store for the verdict string. Since we are processing message data, assume that |
194 | the verdict is tainted. XXX this should use a growable-string */ | |
195 | ||
196 | verdicts = store_get(1, TRUE); | |
8523533c | 197 | *verdicts = '\0'; |
8e669ac1 | 198 | |
8523533c TK |
199 | for ( err = bmiAccessFirstVerdict(message, &verdict); |
200 | verdict != NULL; | |
201 | err = bmiAccessNextVerdict(message, verdict, &verdict) ) { | |
202 | char *verdict_str; | |
203 | ||
204 | err = bmiCreateStrFromVerdict(verdict,&verdict_str); | |
f3ebb786 JH |
205 | if (!store_extend(verdicts, TRUE, |
206 | Ustrlen(verdicts)+1, Ustrlen(verdicts)+1+strlen(verdict_str)+1)) { | |
8523533c TK |
207 | /* can't allocate more store */ |
208 | return NULL; | |
209 | }; | |
210 | if (*verdicts != '\0') | |
211 | Ustrcat(verdicts, US ":"); | |
212 | Ustrcat(verdicts, US verdict_str); | |
213 | bmiFreeStr(verdict_str); | |
214 | }; | |
215 | ||
216 | DEBUG(D_receive) debug_printf("bmi verdicts: %s\n", verdicts); | |
217 | ||
218 | if (Ustrlen(verdicts) == 0) | |
219 | return NULL; | |
220 | else | |
221 | return verdicts; | |
222 | } | |
223 | ||
224 | ||
225 | int bmi_get_delivery_status(uschar *base64_verdict) { | |
226 | BmiError err; | |
227 | BmiErrorLocation err_loc; | |
228 | BmiErrorType err_type; | |
229 | BmiVerdict *verdict = NULL; | |
230 | int rc = 1; /* deliver by default */ | |
8e669ac1 | 231 | |
8523533c TK |
232 | /* always deliver when there is no verdict */ |
233 | if (base64_verdict == NULL) | |
234 | return 1; | |
235 | ||
236 | /* create verdict from base64 string */ | |
237 | err = bmiCreateVerdictFromStr(CS base64_verdict, &verdict); | |
238 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
239 | err_loc = bmiErrorGetLocation(err); | |
240 | err_type = bmiErrorGetType(err); | |
241 | log_write(0, LOG_PANIC, | |
242 | "bmi error [loc %d type %d]: bmiCreateVerdictFromStr() failed. [%s]", (int)err_loc, (int)err_type, base64_verdict); | |
243 | return 1; | |
244 | }; | |
245 | ||
246 | err = bmiVerdictError(verdict); | |
247 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
248 | /* deliver normally due to error */ | |
249 | rc = 1; | |
250 | } | |
251 | else if (bmiVerdictDestinationIsDefault(verdict) == BMI_TRUE) { | |
252 | /* deliver normally */ | |
8e669ac1 | 253 | rc = 1; |
8523533c TK |
254 | } |
255 | else if (bmiVerdictAccessDestination(verdict) == NULL) { | |
256 | /* do not deliver */ | |
257 | rc = 0; | |
258 | } | |
259 | else { | |
260 | /* deliver to alternate location */ | |
261 | rc = 1; | |
262 | }; | |
8e669ac1 | 263 | |
8523533c TK |
264 | bmiFreeVerdict(verdict); |
265 | return rc; | |
266 | } | |
267 | ||
268 | ||
269 | uschar *bmi_get_alt_location(uschar *base64_verdict) { | |
270 | BmiError err; | |
271 | BmiErrorLocation err_loc; | |
272 | BmiErrorType err_type; | |
273 | BmiVerdict *verdict = NULL; | |
274 | uschar *rc = NULL; | |
8e669ac1 | 275 | |
8523533c TK |
276 | /* always deliver when there is no verdict */ |
277 | if (base64_verdict == NULL) | |
278 | return NULL; | |
279 | ||
280 | /* create verdict from base64 string */ | |
281 | err = bmiCreateVerdictFromStr(CS base64_verdict, &verdict); | |
282 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
283 | err_loc = bmiErrorGetLocation(err); | |
284 | err_type = bmiErrorGetType(err); | |
285 | log_write(0, LOG_PANIC, | |
286 | "bmi error [loc %d type %d]: bmiCreateVerdictFromStr() failed. [%s]", (int)err_loc, (int)err_type, base64_verdict); | |
287 | return NULL; | |
288 | }; | |
8e669ac1 | 289 | |
8523533c TK |
290 | err = bmiVerdictError(verdict); |
291 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
292 | /* deliver normally due to error */ | |
293 | rc = NULL; | |
294 | } | |
295 | else if (bmiVerdictDestinationIsDefault(verdict) == BMI_TRUE) { | |
296 | /* deliver normally */ | |
8e669ac1 | 297 | rc = NULL; |
8523533c TK |
298 | } |
299 | else if (bmiVerdictAccessDestination(verdict) == NULL) { | |
300 | /* do not deliver */ | |
301 | rc = NULL; | |
302 | } | |
303 | else { | |
304 | /* deliver to alternate location */ | |
f3ebb786 | 305 | rc = store_get(strlen(bmiVerdictAccessDestination(verdict))+1, TRUE); |
8523533c TK |
306 | Ustrcpy(rc, bmiVerdictAccessDestination(verdict)); |
307 | rc[strlen(bmiVerdictAccessDestination(verdict))] = '\0'; | |
308 | }; | |
8e669ac1 | 309 | |
8523533c TK |
310 | bmiFreeVerdict(verdict); |
311 | return rc; | |
312 | } | |
313 | ||
314 | uschar *bmi_get_base64_verdict(uschar *bmi_local_part, uschar *bmi_domain) { | |
315 | BmiError err; | |
316 | BmiErrorLocation err_loc; | |
317 | BmiErrorType err_type; | |
318 | BmiVerdict *verdict = NULL; | |
319 | const BmiRecipient *recipient = NULL; | |
320 | const char *verdict_str = NULL; | |
321 | uschar *verdict_ptr; | |
322 | uschar *verdict_buffer = NULL; | |
323 | int sep = 0; | |
8e669ac1 | 324 | |
8523533c TK |
325 | /* return nothing if there are no verdicts available */ |
326 | if (bmi_verdicts == NULL) | |
327 | return NULL; | |
8e669ac1 | 328 | |
8523533c | 329 | /* allocate room for the b64 verdict string */ |
f3ebb786 | 330 | verdict_buffer = store_get(Ustrlen(bmi_verdicts)+1, TRUE); |
8e669ac1 | 331 | |
8523533c TK |
332 | /* loop through verdicts */ |
333 | verdict_ptr = bmi_verdicts; | |
5903c6ff | 334 | while ((verdict_str = CCS string_nextinlist(&verdict_ptr, &sep, |
8523533c TK |
335 | verdict_buffer, |
336 | Ustrlen(bmi_verdicts)+1)) != NULL) { | |
8e669ac1 | 337 | |
8523533c TK |
338 | /* create verdict from base64 string */ |
339 | err = bmiCreateVerdictFromStr(verdict_str, &verdict); | |
340 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
341 | err_loc = bmiErrorGetLocation(err); | |
342 | err_type = bmiErrorGetType(err); | |
343 | log_write(0, LOG_PANIC, | |
344 | "bmi error [loc %d type %d]: bmiCreateVerdictFromStr() failed. [%s]", (int)err_loc, (int)err_type, verdict_str); | |
345 | return NULL; | |
346 | }; | |
8e669ac1 | 347 | |
8523533c TK |
348 | /* loop through rcpts for this verdict */ |
349 | for ( recipient = bmiVerdictAccessFirstRecipient(verdict); | |
350 | recipient != NULL; | |
351 | recipient = bmiVerdictAccessNextRecipient(verdict, recipient)) { | |
352 | uschar *rcpt_local_part; | |
353 | uschar *rcpt_domain; | |
8e669ac1 | 354 | |
8523533c | 355 | /* compare address against our subject */ |
5903c6ff | 356 | rcpt_local_part = US bmiRecipientAccessAddress(recipient); |
8523533c TK |
357 | rcpt_domain = Ustrchr(rcpt_local_part,'@'); |
358 | if (rcpt_domain == NULL) { | |
359 | rcpt_domain = US""; | |
360 | } | |
361 | else { | |
362 | *rcpt_domain = '\0'; | |
363 | rcpt_domain++; | |
364 | }; | |
365 | ||
366 | if ( (strcmpic(rcpt_local_part, bmi_local_part) == 0) && | |
367 | (strcmpic(rcpt_domain, bmi_domain) == 0) ) { | |
368 | /* found verdict */ | |
369 | bmiFreeVerdict(verdict); | |
5903c6ff | 370 | return US verdict_str; |
8e669ac1 | 371 | }; |
8523533c | 372 | }; |
8e669ac1 | 373 | |
8523533c TK |
374 | bmiFreeVerdict(verdict); |
375 | }; | |
8e669ac1 | 376 | |
8523533c TK |
377 | return NULL; |
378 | } | |
379 | ||
380 | ||
381 | uschar *bmi_get_base64_tracker_verdict(uschar *base64_verdict) { | |
382 | BmiError err; | |
383 | BmiErrorLocation err_loc; | |
384 | BmiErrorType err_type; | |
385 | BmiVerdict *verdict = NULL; | |
386 | uschar *rc = NULL; | |
8e669ac1 | 387 | |
8523533c TK |
388 | /* always deliver when there is no verdict */ |
389 | if (base64_verdict == NULL) | |
390 | return NULL; | |
391 | ||
392 | /* create verdict from base64 string */ | |
393 | err = bmiCreateVerdictFromStr(CS base64_verdict, &verdict); | |
394 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
395 | err_loc = bmiErrorGetLocation(err); | |
396 | err_type = bmiErrorGetType(err); | |
397 | log_write(0, LOG_PANIC, | |
398 | "bmi error [loc %d type %d]: bmiCreateVerdictFromStr() failed. [%s]", (int)err_loc, (int)err_type, base64_verdict); | |
399 | return NULL; | |
400 | }; | |
8e669ac1 | 401 | |
8523533c TK |
402 | /* create old tracker string from verdict */ |
403 | err = bmiCreateOldStrFromVerdict(verdict, &rc); | |
404 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
405 | err_loc = bmiErrorGetLocation(err); | |
406 | err_type = bmiErrorGetType(err); | |
407 | log_write(0, LOG_PANIC, | |
408 | "bmi error [loc %d type %d]: bmiCreateOldStrFromVerdict() failed. [%s]", (int)err_loc, (int)err_type, base64_verdict); | |
409 | return NULL; | |
410 | }; | |
8e669ac1 | 411 | |
8523533c TK |
412 | bmiFreeVerdict(verdict); |
413 | return rc; | |
414 | } | |
415 | ||
416 | ||
417 | int bmi_check_rule(uschar *base64_verdict, uschar *option_list) { | |
418 | BmiError err; | |
419 | BmiErrorLocation err_loc; | |
420 | BmiErrorType err_type; | |
421 | BmiVerdict *verdict = NULL; | |
422 | int rc = 0; | |
423 | uschar *rule_num; | |
424 | uschar *rule_ptr; | |
425 | uschar rule_buffer[32]; | |
426 | int sep = 0; | |
8e669ac1 PH |
427 | |
428 | ||
8523533c TK |
429 | /* no verdict -> no rule fired */ |
430 | if (base64_verdict == NULL) | |
431 | return 0; | |
8e669ac1 | 432 | |
8523533c TK |
433 | /* create verdict from base64 string */ |
434 | err = bmiCreateVerdictFromStr(CS base64_verdict, &verdict); | |
435 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
436 | err_loc = bmiErrorGetLocation(err); | |
437 | err_type = bmiErrorGetType(err); | |
438 | log_write(0, LOG_PANIC, | |
439 | "bmi error [loc %d type %d]: bmiCreateVerdictFromStr() failed. [%s]", (int)err_loc, (int)err_type, base64_verdict); | |
440 | return 0; | |
441 | }; | |
8e669ac1 | 442 | |
8523533c TK |
443 | err = bmiVerdictError(verdict); |
444 | if (bmiErrorIsFatal(err) == BMI_TRUE) { | |
445 | /* error -> no rule fired */ | |
446 | bmiFreeVerdict(verdict); | |
447 | return 0; | |
448 | } | |
8e669ac1 | 449 | |
8523533c TK |
450 | /* loop through numbers */ |
451 | rule_ptr = option_list; | |
452 | while ((rule_num = string_nextinlist(&rule_ptr, &sep, | |
453 | rule_buffer, 32)) != NULL) { | |
454 | int rule_int = -1; | |
8e669ac1 | 455 | |
8523533c | 456 | /* try to translate to int */ |
ff790e47 | 457 | (void)sscanf(rule_num, "%d", &rule_int); |
8523533c TK |
458 | if (rule_int > 0) { |
459 | debug_printf("checking rule #%d\n", rule_int); | |
460 | /* check if rule fired on the message */ | |
461 | if (bmiVerdictRuleFired(verdict, rule_int) == BMI_TRUE) { | |
462 | debug_printf("rule #%d fired\n", rule_int); | |
463 | rc = 1; | |
464 | break; | |
465 | }; | |
466 | }; | |
467 | }; | |
468 | ||
469 | bmiFreeVerdict(verdict); | |
470 | return rc; | |
471 | }; | |
472 | ||
473 | #endif |