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 HOST_NOT_FOUND (shortest code path
76 in libspf2). They were obsoleted by RFC 6686/7208 years ago. see bug #1294
81 HDEBUG(D_host_lookup) debug_printf("faking HOST_NOT_FOUND for SPF RR(99) lookup\n");
82 srr.herrno = HOST_NOT_FOUND;
83 SPF_dns_rr_dup(&spfrr, &srr);
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);
107 srr.rr = store_malloc(sizeof(SPF_dns_rr_data_t) * found);
110 for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
111 rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
112 if (rr->type == rr_type)
114 const uschar * s = rr->data;
120 s += 2; /* skip the MX precedence field */
123 uschar * buf = store_malloc(256);
124 (void)dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, s,
125 (DN_EXPAND_ARG4_TYPE)buf, 256);
135 if (strncmpic(rr->data+1, US SPF_VER_STR, 6) != 0)
137 HDEBUG(D_host_lookup) debug_printf("not an spf record: %.*s\n",
142 for (int off = 0; off < rr->size; off += chunk_len)
144 if (!(chunk_len = s[off++])) break;
145 g = string_catn(g, s+off, chunk_len);
149 gstring_release_unused(g);
150 s = string_copy_malloc(string_from_gstring(g));
151 DEBUG(D_receive) debug_printf("SPF_dns_exim_lookup '%s'\n", s);
159 uschar * buf = store_malloc(dnsa->answerlen + 1);
160 s = memcpy(buf, s, dnsa->answerlen + 1);
164 srr.rr[found++] = (void *) s;
167 /* Did we filter out all TXT RRs? Return NO_DATA instead of SUCCESS with
168 empty ANSWER section. */
170 if (!(srr.num_rr = found))
171 srr.herrno = NO_DATA;
173 /* spfrr->rr must have been malloc()d for this */
174 SPF_dns_rr_dup(&spfrr, &srr);
181 SPF_dns_exim_new(int debug)
183 SPF_dns_server_t * spf_dns_server = store_malloc(sizeof(SPF_dns_server_t));
185 DEBUG(D_receive) debug_printf("SPF_dns_exim_new\n");
187 memset(spf_dns_server, 0, sizeof(SPF_dns_server_t));
188 spf_dns_server->destroy = NULL;
189 spf_dns_server->lookup = SPF_dns_exim_lookup;
190 spf_dns_server->get_spf = NULL;
191 spf_dns_server->get_exp = NULL;
192 spf_dns_server->add_cache = NULL;
193 spf_dns_server->layer_below = NULL;
194 spf_dns_server->name = "exim";
195 spf_dns_server->debug = debug;
197 /* XXX This might have to return NO_DATA sometimes. */
199 spf_nxdomain = SPF_dns_rr_new_init(spf_dns_server,
200 "", ns_t_any, 24 * 60 * 60, HOST_NOT_FOUND);
203 free(spf_dns_server);
207 return spf_dns_server;
213 /* Construct the SPF library stack.
214 Return: Boolean success.
220 SPF_dns_server_t * dc;
223 DEBUG(D_receive) debug = 1;
225 /* We insert our own DNS access layer rather than letting the spf library
226 do it, so that our dns access path is used for debug tracing and for the
229 if (!(dc = SPF_dns_exim_new(debug)))
231 DEBUG(D_receive) debug_printf("spf: SPF_dns_exim_new() failed\n");
234 if (!(dc = SPF_dns_cache_new(dc, NULL, debug, 8)))
236 DEBUG(D_receive) debug_printf("spf: SPF_dns_cache_new() failed\n");
239 if (!(spf_server = SPF_server_new_dns(dc, debug)))
241 DEBUG(D_receive) debug_printf("spf: SPF_server_new() failed.\n");
244 /* Quick hack to override the outdated explanation URL.
245 See https://www.mail-archive.com/mailop@mailop.org/msg08019.html */
246 SPF_server_set_explanation(spf_server, "Please%_see%_http://www.open-spf.org/Why?id=%{S}&ip=%{C}&receiver=%{R}", &spf_response);
247 if (SPF_response_errcode(spf_response) != SPF_E_SUCCESS)
248 log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s", SPF_strerror(SPF_response_errcode(spf_response)));
254 /* Set up a context that can be re-used for several
255 messages on the same SMTP connection (that come from the
256 same host with the same HELO string).
258 Return: Boolean success
262 spf_conn_init(uschar * spf_helo_domain, uschar * spf_remote_addr)
265 debug_printf("spf_conn_init: %s %s\n", spf_helo_domain, spf_remote_addr);
267 if (!spf_server && !spf_init()) return FALSE;
269 if (SPF_server_set_rec_dom(spf_server, CS primary_hostname))
271 DEBUG(D_receive) debug_printf("spf: SPF_server_set_rec_dom(\"%s\") failed.\n",
277 spf_request = SPF_request_new(spf_server);
279 if ( SPF_request_set_ipv4_str(spf_request, CS spf_remote_addr)
280 && SPF_request_set_ipv6_str(spf_request, CS spf_remote_addr)
284 debug_printf("spf: SPF_request_set_ipv4_str() and "
285 "SPF_request_set_ipv6_str() failed [%s]\n", spf_remote_addr);
291 if (SPF_request_set_helo_dom(spf_request, CS spf_helo_domain))
293 DEBUG(D_receive) debug_printf("spf: SPF_set_helo_dom(\"%s\") failed.\n",
305 spf_response_debug(SPF_response_t * spf_response)
307 if (SPF_response_messages(spf_response) == 0)
308 debug_printf(" (no errors)\n");
309 else for (int i = 0; i < SPF_response_messages(spf_response); i++)
311 SPF_error_t * err = SPF_response_message(spf_response, i);
312 debug_printf( "%s_msg = (%d) %s\n",
313 (SPF_error_errorp(err) ? "warn" : "err"),
315 SPF_error_message(err));
320 /* spf_process adds the envelope sender address to the existing
321 context (if any), retrieves the result, sets up expansion
322 strings and evaluates the condition outcome.
327 spf_process(const uschar **listptr, uschar *spf_envelope_sender, int action)
330 const uschar *list = *listptr;
331 uschar *spf_result_id;
332 int rc = SPF_RESULT_PERMERROR;
334 DEBUG(D_receive) debug_printf("spf_process\n");
336 if (!(spf_server && spf_request))
337 /* no global context, assume temp error and skip to evaluation */
338 rc = SPF_RESULT_PERMERROR;
340 else if (SPF_request_set_env_from(spf_request, CS spf_envelope_sender))
341 /* Invalid sender address. This should be a real rare occurrence */
342 rc = SPF_RESULT_PERMERROR;
347 if (action == SPF_PROCESS_FALLBACK)
349 SPF_request_query_fallback(spf_request, &spf_response, CS spf_guess);
350 spf_result_guessed = TRUE;
353 SPF_request_query_mailfrom(spf_request, &spf_response);
355 /* set up expansion items */
356 spf_header_comment = US SPF_response_get_header_comment(spf_response);
357 spf_received = US SPF_response_get_received_spf(spf_response);
358 spf_result = US SPF_strresult(SPF_response_result(spf_response));
359 spf_smtp_comment = US SPF_response_get_smtp_comment(spf_response);
361 rc = SPF_response_result(spf_response);
363 DEBUG(D_acl) spf_response_debug(spf_response);
366 /* We got a result. Now see if we should return OK or FAIL for it */
367 DEBUG(D_acl) debug_printf("SPF result is %s (%d)\n", SPF_strresult(rc), rc);
369 if (action == SPF_PROCESS_GUESS && (!strcmp (SPF_strresult(rc), "none")))
370 return spf_process(listptr, spf_envelope_sender, SPF_PROCESS_FALLBACK);
372 while ((spf_result_id = string_nextinlist(&list, &sep, NULL, 0)))
376 if ((negate = spf_result_id[0] == '!'))
379 result = Ustrcmp(spf_result_id, spf_result_id_list[rc].name) == 0;
380 if (negate != result) return OK;
390 authres_spf(gstring * g)
393 if (!spf_result) return g;
395 g = string_append(g, 2, US";\n\tspf=", spf_result);
396 if (spf_result_guessed)
397 g = string_cat(g, US" (best guess record for domain)");
399 s = expand_string(US"$sender_address_domain");
401 ? string_append(g, 2, US" smtp.mailfrom=", s)
402 : string_cat(g, US" smtp.mailfrom=<>");