1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
6 Copyright (c) The Exim Maintainers 2015 - 2022
7 Copyright (c) Tom Kistner <tom@duncanthrax.net> 2004 - 2014
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(gstring * g)
42 SPF_get_lib_version(&maj, &min, &patch);
43 g = string_fmt_append(g, "Library version: spf2: Compile: %d.%d.%d\n",
44 SPF_LIB_VERSION_MAJOR, SPF_LIB_VERSION_MINOR, SPF_LIB_VERSION_PATCH);
45 g = string_fmt_append(g, " Runtime: %d.%d.%d\n",
53 SPF_dns_exim_lookup(SPF_dns_server_t *spf_dns_server,
54 const char *domain, ns_type rr_type, int should_cache)
56 dns_answer * dnsa = store_get_dns_answer();
62 .domain = CS domain, /* query information */
66 .rr_buf_len = 0, /* answer information */
67 .rr_buf_num = 0, /* no free of s */
70 .hook = NULL, /* misc information */
71 .source = spf_dns_server
75 DEBUG(D_receive) debug_printf("SPF_dns_exim_lookup '%s'\n", domain);
77 /* Shortcircuit SPF RR lookups by returning NO_DATA. They were obsoleted by
78 RFC 6686/7208 years ago. see bug #1294 */
82 HDEBUG(D_host_lookup) debug_printf("faking NO_DATA for SPF RR(99) lookup\n");
84 SPF_dns_rr_dup(&spfrr, &srr);
85 store_free_dns_answer(dnsa);
89 switch (dns_rc = dns_lookup(dnsa, US domain, rr_type, NULL))
91 case DNS_SUCCEED: srr.herrno = NETDB_SUCCESS; break;
92 case DNS_AGAIN: srr.herrno = TRY_AGAIN; break;
93 case DNS_NOMATCH: srr.herrno = HOST_NOT_FOUND; break;
94 case DNS_NODATA: srr.herrno = NO_DATA; break;
96 default: srr.herrno = NO_RECOVERY; break;
99 for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
100 rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
101 if (rr->type == rr_type) found++;
105 SPF_dns_rr_dup(&spfrr, &srr);
106 store_free_dns_answer(dnsa);
110 srr.rr = store_malloc(sizeof(SPF_dns_rr_data_t) * found);
113 for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
114 rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
115 if (rr->type == rr_type)
117 const uschar * s = rr->data;
123 s += 2; /* skip the MX precedence field */
126 uschar * buf = store_malloc(256);
127 (void)dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, s,
128 (DN_EXPAND_ARG4_TYPE)buf, 256);
138 if (strncmpic(rr->data+1, US SPF_VER_STR, 6) != 0)
140 HDEBUG(D_host_lookup) debug_printf("not an spf record: %.*s\n",
145 for (int off = 0; off < rr->size; off += chunk_len)
147 if (!(chunk_len = s[off++])) break;
148 g = string_catn(g, s+off, chunk_len);
152 gstring_release_unused(g);
153 s = string_copy_malloc(string_from_gstring(g));
154 DEBUG(D_receive) debug_printf("SPF_dns_exim_lookup '%s'\n", s);
162 uschar * buf = store_malloc(dnsa->answerlen + 1);
163 s = memcpy(buf, s, dnsa->answerlen + 1);
167 srr.rr[found++] = (void *) s;
170 /* Did we filter out all TXT RRs? Return NO_DATA instead of SUCCESS with
171 empty ANSWER section. */
173 if (!(srr.num_rr = found))
174 srr.herrno = NO_DATA;
176 /* spfrr->rr must have been malloc()d for this */
177 SPF_dns_rr_dup(&spfrr, &srr);
178 store_free_dns_answer(dnsa);
185 SPF_dns_exim_new(int debug)
187 SPF_dns_server_t * spf_dns_server = store_malloc(sizeof(SPF_dns_server_t));
189 DEBUG(D_receive) debug_printf("SPF_dns_exim_new\n");
191 memset(spf_dns_server, 0, sizeof(SPF_dns_server_t));
192 spf_dns_server->destroy = NULL;
193 spf_dns_server->lookup = SPF_dns_exim_lookup;
194 spf_dns_server->get_spf = NULL;
195 spf_dns_server->get_exp = NULL;
196 spf_dns_server->add_cache = NULL;
197 spf_dns_server->layer_below = NULL;
198 spf_dns_server->name = "exim";
199 spf_dns_server->debug = debug;
201 /* XXX This might have to return NO_DATA sometimes. */
203 spf_nxdomain = SPF_dns_rr_new_init(spf_dns_server,
204 "", ns_t_any, 24 * 60 * 60, HOST_NOT_FOUND);
207 free(spf_dns_server);
211 return spf_dns_server;
217 /* Construct the SPF library stack.
218 Return: Boolean success.
224 SPF_dns_server_t * dc;
228 DEBUG(D_receive) debug = 1;
230 /* We insert our own DNS access layer rather than letting the spf library
231 do it, so that our dns access path is used for debug tracing and for the
234 if (!(dc = SPF_dns_exim_new(debug)))
236 DEBUG(D_receive) debug_printf("spf: SPF_dns_exim_new() failed\n");
239 if (!(dc = SPF_dns_cache_new(dc, NULL, debug, 8)))
241 DEBUG(D_receive) debug_printf("spf: SPF_dns_cache_new() failed\n");
244 if (!(spf_server = SPF_server_new_dns(dc, debug)))
246 DEBUG(D_receive) debug_printf("spf: SPF_server_new() failed.\n");
249 /* Override the outdated explanation URL.
250 See https://www.mail-archive.com/mailop@mailop.org/msg08019.html
251 Used to work as "Please%_see%_http://www.open-spf.org/Why?id=%{S}&ip=%{C}&receiver=%{R}",
252 but is broken now (May 18th, 2020) */
253 if (!(s = expand_string(spf_smtp_comment_template)))
254 log_write(0, LOG_MAIN|LOG_PANIC_DIE, "expansion of spf_smtp_comment_template failed");
256 SPF_server_set_explanation(spf_server, CCS s, &spf_response);
257 if (SPF_response_errcode(spf_response) != SPF_E_SUCCESS)
258 log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s", SPF_strerror(SPF_response_errcode(spf_response)));
264 /* Set up a context that can be re-used for several
265 messages on the same SMTP connection (that come from the
266 same host with the same HELO string).
268 Return: Boolean success
272 spf_conn_init(uschar * spf_helo_domain, uschar * spf_remote_addr)
275 debug_printf("spf_conn_init: %s %s\n", spf_helo_domain, spf_remote_addr);
277 if (!spf_server && !spf_init()) return FALSE;
279 if (SPF_server_set_rec_dom(spf_server, CS primary_hostname))
281 DEBUG(D_receive) debug_printf("spf: SPF_server_set_rec_dom(\"%s\") failed.\n",
287 spf_request = SPF_request_new(spf_server);
289 if ( SPF_request_set_ipv4_str(spf_request, CS spf_remote_addr)
290 && SPF_request_set_ipv6_str(spf_request, CS spf_remote_addr)
294 debug_printf("spf: SPF_request_set_ipv4_str() and "
295 "SPF_request_set_ipv6_str() failed [%s]\n", spf_remote_addr);
301 if (SPF_request_set_helo_dom(spf_request, CS spf_helo_domain))
303 DEBUG(D_receive) debug_printf("spf: SPF_set_helo_dom(\"%s\") failed.\n",
315 spf_response_debug(SPF_response_t * spf_response)
317 if (SPF_response_messages(spf_response) == 0)
318 debug_printf(" (no errors)\n");
319 else for (int i = 0; i < SPF_response_messages(spf_response); i++)
321 SPF_error_t * err = SPF_response_message(spf_response, i);
322 debug_printf( "%s_msg = (%d) %s\n",
323 (SPF_error_errorp(err) ? "warn" : "err"),
325 SPF_error_message(err));
330 /* spf_process adds the envelope sender address to the existing
331 context (if any), retrieves the result, sets up expansion
332 strings and evaluates the condition outcome.
337 spf_process(const uschar **listptr, uschar *spf_envelope_sender, int action)
340 const uschar *list = *listptr;
341 uschar *spf_result_id;
342 int rc = SPF_RESULT_PERMERROR;
344 DEBUG(D_receive) debug_printf("spf_process\n");
346 if (!(spf_server && spf_request))
347 /* no global context, assume temp error and skip to evaluation */
348 rc = SPF_RESULT_PERMERROR;
350 else if (SPF_request_set_env_from(spf_request, CS spf_envelope_sender))
351 /* Invalid sender address. This should be a real rare occurrence */
352 rc = SPF_RESULT_PERMERROR;
357 if (action == SPF_PROCESS_FALLBACK)
359 SPF_request_query_fallback(spf_request, &spf_response, CS spf_guess);
360 spf_result_guessed = TRUE;
363 SPF_request_query_mailfrom(spf_request, &spf_response);
365 /* set up expansion items */
366 spf_header_comment = US SPF_response_get_header_comment(spf_response);
367 spf_received = US SPF_response_get_received_spf(spf_response);
368 spf_result = US SPF_strresult(SPF_response_result(spf_response));
369 spf_smtp_comment = US SPF_response_get_smtp_comment(spf_response);
371 rc = SPF_response_result(spf_response);
373 DEBUG(D_acl) spf_response_debug(spf_response);
376 /* We got a result. Now see if we should return OK or FAIL for it */
377 DEBUG(D_acl) debug_printf("SPF result is %s (%d)\n", SPF_strresult(rc), rc);
379 if (action == SPF_PROCESS_GUESS && (!strcmp (SPF_strresult(rc), "none")))
380 return spf_process(listptr, spf_envelope_sender, SPF_PROCESS_FALLBACK);
382 while ((spf_result_id = string_nextinlist(&list, &sep, NULL, 0)))
386 if ((negate = spf_result_id[0] == '!'))
389 result = Ustrcmp(spf_result_id, spf_result_id_list[rc].name) == 0;
390 if (negate != result) return OK;
400 authres_spf(gstring * g)
403 if (!spf_result) return g;
405 g = string_append(g, 2, US";\n\tspf=", spf_result);
406 if (spf_result_guessed)
407 g = string_cat(g, US" (best guess record for domain)");
409 s = expand_string(US"$sender_address_domain");
411 return string_append(g, 2, US" smtp.mailfrom=", s);
413 s = sender_helo_name;
415 ? string_append(g, 2, US" smtp.helo=", s)
416 : string_cat(g, US" smtp.mailfrom=<>");