1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
6 Copyright (c) Tom Kistner <tom@duncanthrax.net> 2004 - 2014
8 Copyright (c) The Exim Maintainers 2015 - 2020
11 /* Code for calling spf checks via libspf-alt. Called from acl.c. */
16 /* must be kept in numeric order */
17 static spf_result_id spf_result_id_list[] = {
25 { US"temperror", 6 }, /* RFC 4408 defined */
26 { US"permerror", 7 } /* RFC 4408 defined */
29 SPF_server_t *spf_server = NULL;
30 SPF_request_t *spf_request = NULL;
31 SPF_response_t *spf_response = NULL;
32 SPF_response_t *spf_response_2mx = NULL;
34 SPF_dns_rr_t * spf_nxdomain = NULL;
38 spf_lib_version_report(FILE * fp)
41 SPF_get_lib_version(&maj, &min, &patch);
42 fprintf(fp, "Library version: spf2: Compile: %d.%d.%d\n",
43 SPF_LIB_VERSION_MAJOR, SPF_LIB_VERSION_MINOR, SPF_LIB_VERSION_PATCH);
44 fprintf(fp, " Runtime: %d.%d.%d\n",
51 SPF_dns_exim_lookup(SPF_dns_server_t *spf_dns_server,
52 const char *domain, ns_type rr_type, int should_cache)
54 dns_answer * dnsa = store_get_dns_answer();
60 .domain = CS domain, /* query information */
64 .rr_buf_len = 0, /* answer information */
65 .rr_buf_num = 0, /* no free of s */
68 .hook = NULL, /* misc information */
69 .source = spf_dns_server
73 DEBUG(D_receive) debug_printf("SPF_dns_exim_lookup '%s'\n", domain);
75 /* Shortcircuit SPF RR lookups by returning NO_DATA. They were obsoleted by
76 RFC 6686/7208 years ago. see bug #1294 */
80 HDEBUG(D_host_lookup) debug_printf("faking NO_DATA for SPF RR(99) lookup\n");
82 SPF_dns_rr_dup(&spfrr, &srr);
83 store_free_dns_answer(dnsa);
87 switch (dns_rc = dns_lookup(dnsa, US domain, rr_type, NULL))
89 case DNS_SUCCEED: srr.herrno = NETDB_SUCCESS; break;
90 case DNS_AGAIN: srr.herrno = TRY_AGAIN; break;
91 case DNS_NOMATCH: srr.herrno = HOST_NOT_FOUND; break;
92 case DNS_NODATA: srr.herrno = NO_DATA; break;
94 default: srr.herrno = NO_RECOVERY; break;
97 for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
98 rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
99 if (rr->type == rr_type) found++;
103 SPF_dns_rr_dup(&spfrr, &srr);
104 store_free_dns_answer(dnsa);
108 srr.rr = store_malloc(sizeof(SPF_dns_rr_data_t) * found);
111 for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
112 rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
113 if (rr->type == rr_type)
115 const uschar * s = rr->data;
121 s += 2; /* skip the MX precedence field */
124 uschar * buf = store_malloc(256);
125 (void)dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, s,
126 (DN_EXPAND_ARG4_TYPE)buf, 256);
136 if (strncmpic(rr->data+1, US SPF_VER_STR, 6) != 0)
138 HDEBUG(D_host_lookup) debug_printf("not an spf record: %.*s\n",
143 for (int off = 0; off < rr->size; off += chunk_len)
145 if (!(chunk_len = s[off++])) break;
146 g = string_catn(g, s+off, chunk_len);
150 gstring_release_unused(g);
151 s = string_copy_malloc(string_from_gstring(g));
152 DEBUG(D_receive) debug_printf("SPF_dns_exim_lookup '%s'\n", s);
160 uschar * buf = store_malloc(dnsa->answerlen + 1);
161 s = memcpy(buf, s, dnsa->answerlen + 1);
165 srr.rr[found++] = (void *) s;
168 /* Did we filter out all TXT RRs? Return NO_DATA instead of SUCCESS with
169 empty ANSWER section. */
171 if (!(srr.num_rr = found))
172 srr.herrno = NO_DATA;
174 /* spfrr->rr must have been malloc()d for this */
175 SPF_dns_rr_dup(&spfrr, &srr);
176 store_free_dns_answer(dnsa);
183 SPF_dns_exim_new(int debug)
185 SPF_dns_server_t * spf_dns_server = store_malloc(sizeof(SPF_dns_server_t));
187 DEBUG(D_receive) debug_printf("SPF_dns_exim_new\n");
189 memset(spf_dns_server, 0, sizeof(SPF_dns_server_t));
190 spf_dns_server->destroy = NULL;
191 spf_dns_server->lookup = SPF_dns_exim_lookup;
192 spf_dns_server->get_spf = NULL;
193 spf_dns_server->get_exp = NULL;
194 spf_dns_server->add_cache = NULL;
195 spf_dns_server->layer_below = NULL;
196 spf_dns_server->name = "exim";
197 spf_dns_server->debug = debug;
199 /* XXX This might have to return NO_DATA sometimes. */
201 spf_nxdomain = SPF_dns_rr_new_init(spf_dns_server,
202 "", ns_t_any, 24 * 60 * 60, HOST_NOT_FOUND);
205 free(spf_dns_server);
209 return spf_dns_server;
215 /* Construct the SPF library stack.
216 Return: Boolean success.
222 SPF_dns_server_t * dc;
226 DEBUG(D_receive) debug = 1;
228 /* We insert our own DNS access layer rather than letting the spf library
229 do it, so that our dns access path is used for debug tracing and for the
232 if (!(dc = SPF_dns_exim_new(debug)))
234 DEBUG(D_receive) debug_printf("spf: SPF_dns_exim_new() failed\n");
237 if (!(dc = SPF_dns_cache_new(dc, NULL, debug, 8)))
239 DEBUG(D_receive) debug_printf("spf: SPF_dns_cache_new() failed\n");
242 if (!(spf_server = SPF_server_new_dns(dc, debug)))
244 DEBUG(D_receive) debug_printf("spf: SPF_server_new() failed.\n");
247 /* Override the outdated explanation URL.
248 See https://www.mail-archive.com/mailop@mailop.org/msg08019.html
249 Used to work as "Please%_see%_http://www.open-spf.org/Why?id=%{S}&ip=%{C}&receiver=%{R}",
250 but is broken now (May 18th, 2020) */
251 if (!(s = expand_string(spf_smtp_comment_template)))
252 log_write(0, LOG_MAIN|LOG_PANIC_DIE, "expansion of spf_smtp_comment_template failed");
254 SPF_server_set_explanation(spf_server, CCS s, &spf_response);
255 if (SPF_response_errcode(spf_response) != SPF_E_SUCCESS)
256 log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s", SPF_strerror(SPF_response_errcode(spf_response)));
262 /* Set up a context that can be re-used for several
263 messages on the same SMTP connection (that come from the
264 same host with the same HELO string).
266 Return: Boolean success
270 spf_conn_init(uschar * spf_helo_domain, uschar * spf_remote_addr)
273 debug_printf("spf_conn_init: %s %s\n", spf_helo_domain, spf_remote_addr);
275 if (!spf_server && !spf_init()) return FALSE;
277 if (SPF_server_set_rec_dom(spf_server, CS primary_hostname))
279 DEBUG(D_receive) debug_printf("spf: SPF_server_set_rec_dom(\"%s\") failed.\n",
285 spf_request = SPF_request_new(spf_server);
287 if ( SPF_request_set_ipv4_str(spf_request, CS spf_remote_addr)
288 && SPF_request_set_ipv6_str(spf_request, CS spf_remote_addr)
292 debug_printf("spf: SPF_request_set_ipv4_str() and "
293 "SPF_request_set_ipv6_str() failed [%s]\n", spf_remote_addr);
299 if (SPF_request_set_helo_dom(spf_request, CS spf_helo_domain))
301 DEBUG(D_receive) debug_printf("spf: SPF_set_helo_dom(\"%s\") failed.\n",
313 spf_response_debug(SPF_response_t * spf_response)
315 if (SPF_response_messages(spf_response) == 0)
316 debug_printf(" (no errors)\n");
317 else for (int i = 0; i < SPF_response_messages(spf_response); i++)
319 SPF_error_t * err = SPF_response_message(spf_response, i);
320 debug_printf( "%s_msg = (%d) %s\n",
321 (SPF_error_errorp(err) ? "warn" : "err"),
323 SPF_error_message(err));
328 /* spf_process adds the envelope sender address to the existing
329 context (if any), retrieves the result, sets up expansion
330 strings and evaluates the condition outcome.
335 spf_process(const uschar **listptr, uschar *spf_envelope_sender, int action)
338 const uschar *list = *listptr;
339 uschar *spf_result_id;
340 int rc = SPF_RESULT_PERMERROR;
342 DEBUG(D_receive) debug_printf("spf_process\n");
344 if (!(spf_server && spf_request))
345 /* no global context, assume temp error and skip to evaluation */
346 rc = SPF_RESULT_PERMERROR;
348 else if (SPF_request_set_env_from(spf_request, CS spf_envelope_sender))
349 /* Invalid sender address. This should be a real rare occurrence */
350 rc = SPF_RESULT_PERMERROR;
355 if (action == SPF_PROCESS_FALLBACK)
357 SPF_request_query_fallback(spf_request, &spf_response, CS spf_guess);
358 spf_result_guessed = TRUE;
361 SPF_request_query_mailfrom(spf_request, &spf_response);
363 /* set up expansion items */
364 spf_header_comment = US SPF_response_get_header_comment(spf_response);
365 spf_received = US SPF_response_get_received_spf(spf_response);
366 spf_result = US SPF_strresult(SPF_response_result(spf_response));
367 spf_smtp_comment = US SPF_response_get_smtp_comment(spf_response);
369 rc = SPF_response_result(spf_response);
371 DEBUG(D_acl) spf_response_debug(spf_response);
374 /* We got a result. Now see if we should return OK or FAIL for it */
375 DEBUG(D_acl) debug_printf("SPF result is %s (%d)\n", SPF_strresult(rc), rc);
377 if (action == SPF_PROCESS_GUESS && (!strcmp (SPF_strresult(rc), "none")))
378 return spf_process(listptr, spf_envelope_sender, SPF_PROCESS_FALLBACK);
380 while ((spf_result_id = string_nextinlist(&list, &sep, NULL, 0)))
384 if ((negate = spf_result_id[0] == '!'))
387 result = Ustrcmp(spf_result_id, spf_result_id_list[rc].name) == 0;
388 if (negate != result) return OK;
398 authres_spf(gstring * g)
401 if (!spf_result) return g;
403 g = string_append(g, 2, US";\n\tspf=", spf_result);
404 if (spf_result_guessed)
405 g = string_cat(g, US" (best guess record for domain)");
407 s = expand_string(US"$sender_address_domain");
409 return string_append(g, 2, US" smtp.mailfrom=", s);
411 s = sender_helo_name;
413 ? string_append(g, 2, US" smtp.helo=", s)
414 : string_cat(g, US" smtp.mailfrom=<>");