1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Code for calling Brightmail AntiSpam.
6 Copyright (c) Tom Kistner <tom@duncanthrax.net> 2004
8 /* Copyright (c) The Exim Maintainers 2021 - 2022 */
11 #ifdef EXPERIMENTAL_BRIGHTMAIL
15 uschar *bmi_current_optin = NULL;
17 uschar *bmi_process_message(header_line *header_list, int data_fd) {
18 BmiSystem *system = NULL;
19 BmiMessage *message = NULL;
21 BmiErrorLocation err_loc;
22 BmiErrorType err_type;
23 const BmiVerdict *verdict = NULL;
25 uschar data_buffer[4096];
26 uschar localhost[] = "127.0.0.1";
28 uschar *verdicts = NULL;
31 err = bmiInitSystem(BMI_VERSION, CS bmi_config_file, &system);
32 if (bmiErrorIsFatal(err) == BMI_TRUE) {
33 err_loc = bmiErrorGetLocation(err);
34 err_type = bmiErrorGetType(err);
35 log_write(0, LOG_PANIC,
36 "bmi error [loc %d type %d]: could not initialize Brightmail system.", (int)err_loc, (int)err_type);
40 err = bmiInitMessage(system, &message);
41 if (bmiErrorIsFatal(err) == BMI_TRUE) {
42 err_loc = bmiErrorGetLocation(err);
43 err_type = bmiErrorGetType(err);
44 log_write(0, LOG_PANIC,
45 "bmi error [loc %d type %d]: could not initialize Brightmail message.", (int)err_loc, (int)err_type);
46 bmiFreeSystem(system);
50 /* Send IP address of sending host */
51 if (sender_host_address == NULL)
52 host_address = localhost;
54 host_address = sender_host_address;
55 err = bmiProcessConnection(CS host_address, message);
56 if (bmiErrorIsFatal(err) == BMI_TRUE) {
57 err_loc = bmiErrorGetLocation(err);
58 err_type = bmiErrorGetType(err);
59 log_write(0, LOG_PANIC,
60 "bmi error [loc %d type %d]: bmiProcessConnection() failed (IP %s).", (int)err_loc, (int)err_type, CS host_address);
61 bmiFreeMessage(message);
62 bmiFreeSystem(system);
66 /* Send envelope sender address */
67 err = bmiProcessFROM(CS sender_address, message);
68 if (bmiErrorIsFatal(err) == BMI_TRUE) {
69 err_loc = bmiErrorGetLocation(err);
70 err_type = bmiErrorGetType(err);
71 log_write(0, LOG_PANIC,
72 "bmi error [loc %d type %d]: bmiProcessFROM() failed (address %s).", (int)err_loc, (int)err_type, CS sender_address);
73 bmiFreeMessage(message);
74 bmiFreeSystem(system);
78 /* Send envelope recipients */
79 for(i=0;i<recipients_count;i++) {
80 recipient_item *r = recipients_list + i;
81 BmiOptin *optin = NULL;
83 /* create optin object if optin string is given */
84 if ((r->bmi_optin != NULL) && (Ustrlen(r->bmi_optin) > 1)) {
85 debug_printf("passing bmiOptin string: %s\n", r->bmi_optin);
87 err = bmiOptinMset(optin, r->bmi_optin, ':');
88 if (bmiErrorIsFatal(err) == BMI_TRUE) {
89 log_write(0, LOG_PANIC|LOG_MAIN,
90 "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);
97 err = bmiAccumulateTO(CS r->address, optin, message);
102 if (bmiErrorIsFatal(err) == BMI_TRUE) {
103 err_loc = bmiErrorGetLocation(err);
104 err_type = bmiErrorGetType(err);
105 log_write(0, LOG_PANIC,
106 "bmi error [loc %d type %d]: bmiAccumulateTO() failed (address %s).", (int)err_loc, (int)err_type, CS r->address);
107 bmiFreeMessage(message);
108 bmiFreeSystem(system);
112 err = bmiEndTO(message);
113 if (bmiErrorIsFatal(err) == BMI_TRUE) {
114 err_loc = bmiErrorGetLocation(err);
115 err_type = bmiErrorGetType(err);
116 log_write(0, LOG_PANIC,
117 "bmi error [loc %d type %d]: bmiEndTO() failed.", (int)err_loc, (int)err_type);
118 bmiFreeMessage(message);
119 bmiFreeSystem(system);
123 /* Send message headers */
124 while (header_list != NULL) {
125 /* skip deleted headers */
126 if (header_list->type == '*') {
127 header_list = header_list->next;
130 err = bmiAccumulateHeaders(CCS header_list->text, header_list->slen, message);
131 if (bmiErrorIsFatal(err) == BMI_TRUE) {
132 err_loc = bmiErrorGetLocation(err);
133 err_type = bmiErrorGetType(err);
134 log_write(0, LOG_PANIC,
135 "bmi error [loc %d type %d]: bmiAccumulateHeaders() failed.", (int)err_loc, (int)err_type);
136 bmiFreeMessage(message);
137 bmiFreeSystem(system);
140 header_list = header_list->next;
142 err = bmiEndHeaders(message);
143 if (bmiErrorIsFatal(err) == BMI_TRUE) {
144 err_loc = bmiErrorGetLocation(err);
145 err_type = bmiErrorGetType(err);
146 log_write(0, LOG_PANIC,
147 "bmi error [loc %d type %d]: bmiEndHeaders() failed.", (int)err_loc, (int)err_type);
148 bmiFreeMessage(message);
149 bmiFreeSystem(system);
154 data_file = fdopen(data_fd,"r");
156 j = fread(data_buffer, 1, sizeof(data_buffer), data_file);
158 err = bmiAccumulateBody(CCS data_buffer, j, message);
159 if (bmiErrorIsFatal(err) == BMI_TRUE) {
160 err_loc = bmiErrorGetLocation(err);
161 err_type = bmiErrorGetType(err);
162 log_write(0, LOG_PANIC,
163 "bmi error [loc %d type %d]: bmiAccumulateBody() failed.", (int)err_loc, (int)err_type);
164 bmiFreeMessage(message);
165 bmiFreeSystem(system);
170 err = bmiEndBody(message);
171 if (bmiErrorIsFatal(err) == BMI_TRUE) {
172 err_loc = bmiErrorGetLocation(err);
173 err_type = bmiErrorGetType(err);
174 log_write(0, LOG_PANIC,
175 "bmi error [loc %d type %d]: bmiEndBody() failed.", (int)err_loc, (int)err_type);
176 bmiFreeMessage(message);
177 bmiFreeSystem(system);
183 err = bmiEndMessage(message);
184 if (bmiErrorIsFatal(err) == BMI_TRUE) {
185 err_loc = bmiErrorGetLocation(err);
186 err_type = bmiErrorGetType(err);
187 log_write(0, LOG_PANIC,
188 "bmi error [loc %d type %d]: bmiEndMessage() failed.", (int)err_loc, (int)err_type);
189 bmiFreeMessage(message);
190 bmiFreeSystem(system);
194 /* Get store for the verdict string. Since we are processing message data, assume that
195 the verdict is tainted. XXX this should use a growable-string */
197 verdicts = store_get(1, GET_TAINTED);
200 for ( err = bmiAccessFirstVerdict(message, &verdict);
202 err = bmiAccessNextVerdict(message, verdict, &verdict) ) {
205 err = bmiCreateStrFromVerdict(verdict,&verdict_str);
206 if (!store_extend(verdicts,
207 Ustrlen(verdicts)+1, Ustrlen(verdicts)+1+strlen(verdict_str)+1)) {
208 /* can't allocate more store */
211 if (*verdicts != '\0')
212 Ustrcat(verdicts, US ":");
213 Ustrcat(verdicts, US verdict_str);
214 bmiFreeStr(verdict_str);
217 DEBUG(D_receive) debug_printf("bmi verdicts: %s\n", verdicts);
219 if (Ustrlen(verdicts) == 0)
226 int bmi_get_delivery_status(uschar *base64_verdict) {
228 BmiErrorLocation err_loc;
229 BmiErrorType err_type;
230 BmiVerdict *verdict = NULL;
231 int rc = 1; /* deliver by default */
233 /* always deliver when there is no verdict */
234 if (base64_verdict == NULL)
237 /* create verdict from base64 string */
238 err = bmiCreateVerdictFromStr(CS base64_verdict, &verdict);
239 if (bmiErrorIsFatal(err) == BMI_TRUE) {
240 err_loc = bmiErrorGetLocation(err);
241 err_type = bmiErrorGetType(err);
242 log_write(0, LOG_PANIC,
243 "bmi error [loc %d type %d]: bmiCreateVerdictFromStr() failed. [%s]", (int)err_loc, (int)err_type, base64_verdict);
247 err = bmiVerdictError(verdict);
248 if (bmiErrorIsFatal(err) == BMI_TRUE) {
249 /* deliver normally due to error */
252 else if (bmiVerdictDestinationIsDefault(verdict) == BMI_TRUE) {
253 /* deliver normally */
256 else if (bmiVerdictAccessDestination(verdict) == NULL) {
261 /* deliver to alternate location */
265 bmiFreeVerdict(verdict);
270 uschar *bmi_get_alt_location(uschar *base64_verdict) {
272 BmiErrorLocation err_loc;
273 BmiErrorType err_type;
274 BmiVerdict *verdict = NULL;
277 /* always deliver when there is no verdict */
278 if (base64_verdict == NULL)
281 /* create verdict from base64 string */
282 err = bmiCreateVerdictFromStr(CS base64_verdict, &verdict);
283 if (bmiErrorIsFatal(err) == BMI_TRUE) {
284 err_loc = bmiErrorGetLocation(err);
285 err_type = bmiErrorGetType(err);
286 log_write(0, LOG_PANIC,
287 "bmi error [loc %d type %d]: bmiCreateVerdictFromStr() failed. [%s]", (int)err_loc, (int)err_type, base64_verdict);
291 err = bmiVerdictError(verdict);
292 if (bmiErrorIsFatal(err) == BMI_TRUE) {
293 /* deliver normally due to error */
296 else if (bmiVerdictDestinationIsDefault(verdict) == BMI_TRUE) {
297 /* deliver normally */
300 else if (bmiVerdictAccessDestination(verdict) == NULL) {
305 /* deliver to alternate location */
306 rc = store_get(strlen(bmiVerdictAccessDestination(verdict))+1, GET_TAINTED);
307 Ustrcpy(rc, bmiVerdictAccessDestination(verdict));
308 rc[strlen(bmiVerdictAccessDestination(verdict))] = '\0';
311 bmiFreeVerdict(verdict);
315 uschar *bmi_get_base64_verdict(uschar *bmi_local_part, uschar *bmi_domain) {
317 BmiErrorLocation err_loc;
318 BmiErrorType err_type;
319 BmiVerdict *verdict = NULL;
320 const BmiRecipient *recipient = NULL;
321 const char *verdict_str = NULL;
323 uschar *verdict_buffer = NULL;
326 /* return nothing if there are no verdicts available */
327 if (bmi_verdicts == NULL)
330 /* allocate room for the b64 verdict string */
331 verdict_buffer = store_get(Ustrlen(bmi_verdicts)+1, GET_TAINTED);
333 /* loop through verdicts */
334 verdict_ptr = bmi_verdicts;
335 while ((verdict_str = CCS string_nextinlist(&verdict_ptr, &sep,
337 Ustrlen(bmi_verdicts)+1)) != NULL) {
339 /* create verdict from base64 string */
340 err = bmiCreateVerdictFromStr(verdict_str, &verdict);
341 if (bmiErrorIsFatal(err) == BMI_TRUE) {
342 err_loc = bmiErrorGetLocation(err);
343 err_type = bmiErrorGetType(err);
344 log_write(0, LOG_PANIC,
345 "bmi error [loc %d type %d]: bmiCreateVerdictFromStr() failed. [%s]", (int)err_loc, (int)err_type, verdict_str);
349 /* loop through rcpts for this verdict */
350 for ( recipient = bmiVerdictAccessFirstRecipient(verdict);
352 recipient = bmiVerdictAccessNextRecipient(verdict, recipient)) {
353 uschar *rcpt_local_part;
356 /* compare address against our subject */
357 rcpt_local_part = US bmiRecipientAccessAddress(recipient);
358 rcpt_domain = Ustrchr(rcpt_local_part,'@');
359 if (rcpt_domain == NULL) {
367 if ( (strcmpic(rcpt_local_part, bmi_local_part) == 0) &&
368 (strcmpic(rcpt_domain, bmi_domain) == 0) ) {
370 bmiFreeVerdict(verdict);
371 return US verdict_str;
375 bmiFreeVerdict(verdict);
382 uschar *bmi_get_base64_tracker_verdict(uschar *base64_verdict) {
384 BmiErrorLocation err_loc;
385 BmiErrorType err_type;
386 BmiVerdict *verdict = NULL;
389 /* always deliver when there is no verdict */
390 if (base64_verdict == NULL)
393 /* create verdict from base64 string */
394 err = bmiCreateVerdictFromStr(CS base64_verdict, &verdict);
395 if (bmiErrorIsFatal(err) == BMI_TRUE) {
396 err_loc = bmiErrorGetLocation(err);
397 err_type = bmiErrorGetType(err);
398 log_write(0, LOG_PANIC,
399 "bmi error [loc %d type %d]: bmiCreateVerdictFromStr() failed. [%s]", (int)err_loc, (int)err_type, base64_verdict);
403 /* create old tracker string from verdict */
404 err = bmiCreateOldStrFromVerdict(verdict, &rc);
405 if (bmiErrorIsFatal(err) == BMI_TRUE) {
406 err_loc = bmiErrorGetLocation(err);
407 err_type = bmiErrorGetType(err);
408 log_write(0, LOG_PANIC,
409 "bmi error [loc %d type %d]: bmiCreateOldStrFromVerdict() failed. [%s]", (int)err_loc, (int)err_type, base64_verdict);
413 bmiFreeVerdict(verdict);
418 int bmi_check_rule(uschar *base64_verdict, uschar *option_list) {
420 BmiErrorLocation err_loc;
421 BmiErrorType err_type;
422 BmiVerdict *verdict = NULL;
426 uschar rule_buffer[32];
430 /* no verdict -> no rule fired */
431 if (base64_verdict == NULL)
434 /* create verdict from base64 string */
435 err = bmiCreateVerdictFromStr(CS base64_verdict, &verdict);
436 if (bmiErrorIsFatal(err) == BMI_TRUE) {
437 err_loc = bmiErrorGetLocation(err);
438 err_type = bmiErrorGetType(err);
439 log_write(0, LOG_PANIC,
440 "bmi error [loc %d type %d]: bmiCreateVerdictFromStr() failed. [%s]", (int)err_loc, (int)err_type, base64_verdict);
444 err = bmiVerdictError(verdict);
445 if (bmiErrorIsFatal(err) == BMI_TRUE) {
446 /* error -> no rule fired */
447 bmiFreeVerdict(verdict);
451 /* loop through numbers */
452 /* option_list doesn't seem to be expanded so cannot be tainted. If it ever is we
454 rule_ptr = option_list;
455 while ((rule_num = string_nextinlist(&rule_ptr, &sep,
456 rule_buffer, sizeof(rule_buffer)))) {
459 /* try to translate to int */
460 (void)sscanf(rule_num, "%d", &rule_int);
462 debug_printf("checking rule #%d\n", rule_int);
463 /* check if rule fired on the message */
464 if (bmiVerdictRuleFired(verdict, rule_int) == BMI_TRUE) {
465 debug_printf("rule #%d fired\n", rule_int);
472 bmiFreeVerdict(verdict);