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 - 2019
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;
39 SPF_dns_exim_lookup(SPF_dns_server_t *spf_dns_server,
40 const char *domain, ns_type rr_type, int should_cache)
42 dns_answer * dnsa = store_get_dns_answer();
48 .domain = CS domain, /* query information */
52 .rr_buf_len = 0, /* answer information */
53 .rr_buf_num = 0, /* no free of s */
56 .hook = NULL, /* misc information */
57 .source = spf_dns_server
61 DEBUG(D_receive) debug_printf("SPF_dns_exim_lookup '%s'\n", domain);
63 switch (dns_rc = dns_lookup(dnsa, US domain, rr_type, NULL))
65 case DNS_SUCCEED: srr.herrno = NETDB_SUCCESS; break;
66 case DNS_AGAIN: srr.herrno = TRY_AGAIN; break;
67 case DNS_NOMATCH: srr.herrno = HOST_NOT_FOUND; break;
68 case DNS_NODATA: srr.herrno = NO_DATA; break;
70 default: srr.herrno = NO_RECOVERY; break;
73 for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
74 rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
75 if (rr->type == rr_type) found++;
79 SPF_dns_rr_dup(&spfrr, &srr);
83 srr.rr = store_malloc(sizeof(SPF_dns_rr_data_t) * found);
86 for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
87 rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
88 if (rr->type == rr_type)
90 const uschar * s = rr->data;
96 s += 2; /* skip the MX precedence field */
99 uschar * buf = store_malloc(256);
100 (void)dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, s,
101 (DN_EXPAND_ARG4_TYPE)buf, 256);
111 if (strncmpic(rr->data+1, US"v=spf1", 6) != 0)
113 HDEBUG(D_host_lookup) debug_printf("not an spf record\n");
117 for (int off = 0; off < rr->size; off += chunk_len)
119 if (!(chunk_len = s[off++])) break;
120 g = string_catn(g, s+off, chunk_len);
124 gstring_release_unused(g);
125 s = string_copy_malloc(string_from_gstring(g));
133 uschar * buf = store_malloc(dnsa->answerlen + 1);
134 s = memcpy(buf, s, dnsa->answerlen + 1);
138 DEBUG(D_receive) debug_printf("SPF_dns_exim_lookup '%s'\n", s);
139 srr.rr[found++] = (void *) s;
143 /* spfrr->rr must have been malloc()d for this */
144 SPF_dns_rr_dup(&spfrr, &srr);
151 SPF_dns_exim_new(int debug)
153 SPF_dns_server_t * spf_dns_server = store_malloc(sizeof(SPF_dns_server_t));
155 DEBUG(D_receive) debug_printf("SPF_dns_exim_new\n");
157 memset(spf_dns_server, 0, sizeof(SPF_dns_server_t));
158 spf_dns_server->destroy = NULL;
159 spf_dns_server->lookup = SPF_dns_exim_lookup;
160 spf_dns_server->get_spf = NULL;
161 spf_dns_server->get_exp = NULL;
162 spf_dns_server->add_cache = NULL;
163 spf_dns_server->layer_below = NULL;
164 spf_dns_server->name = "exim";
165 spf_dns_server->debug = debug;
167 /* XXX This might have to return NO_DATA sometimes. */
169 spf_nxdomain = SPF_dns_rr_new_init(spf_dns_server,
170 "", ns_t_any, 24 * 60 * 60, HOST_NOT_FOUND);
173 free(spf_dns_server);
177 return spf_dns_server;
183 /* Construct the SPF library stack.
184 Return: Boolean success.
190 SPF_dns_server_t * dc;
193 DEBUG(D_receive) debug = 1;
195 /* We insert our own DNS access layer rather than letting the spf library
196 do it, so that our dns access path is used for debug tracing and for the
199 if (!(dc = SPF_dns_exim_new(debug)))
201 DEBUG(D_receive) debug_printf("spf: SPF_dns_exim_new() failed\n");
204 if (!(dc = SPF_dns_cache_new(dc, NULL, debug, 8)))
206 DEBUG(D_receive) debug_printf("spf: SPF_dns_cache_new() failed\n");
209 if (!(spf_server = SPF_server_new_dns(dc, debug)))
211 DEBUG(D_receive) debug_printf("spf: SPF_server_new() failed.\n");
214 /* Quick hack to override the outdated explanation URL.
215 See https://www.mail-archive.com/mailop@mailop.org/msg08019.html */
216 SPF_server_set_explanation(spf_server, "Please%_see%_http://www.open-spf.org/Why?id=%{S}&ip=%{C}&receiver=%{R}", &spf_response);
217 if (SPF_response_errcode(spf_response) != SPF_E_SUCCESS)
218 log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s", SPF_strerror(SPF_response_errcode(spf_response)));
224 /* Set up a context that can be re-used for several
225 messages on the same SMTP connection (that come from the
226 same host with the same HELO string).
228 Return: Boolean success
232 spf_conn_init(uschar * spf_helo_domain, uschar * spf_remote_addr)
235 debug_printf("spf_conn_init: %s %s\n", spf_helo_domain, spf_remote_addr);
237 if (!spf_server && !spf_init()) return FALSE;
239 if (SPF_server_set_rec_dom(spf_server, CS primary_hostname))
241 DEBUG(D_receive) debug_printf("spf: SPF_server_set_rec_dom(\"%s\") failed.\n",
247 spf_request = SPF_request_new(spf_server);
249 if ( SPF_request_set_ipv4_str(spf_request, CS spf_remote_addr)
250 && SPF_request_set_ipv6_str(spf_request, CS spf_remote_addr)
254 debug_printf("spf: SPF_request_set_ipv4_str() and "
255 "SPF_request_set_ipv6_str() failed [%s]\n", spf_remote_addr);
261 if (SPF_request_set_helo_dom(spf_request, CS spf_helo_domain))
263 DEBUG(D_receive) debug_printf("spf: SPF_set_helo_dom(\"%s\") failed.\n",
274 /* spf_process adds the envelope sender address to the existing
275 context (if any), retrieves the result, sets up expansion
276 strings and evaluates the condition outcome.
281 spf_process(const uschar **listptr, uschar *spf_envelope_sender, int action)
284 const uschar *list = *listptr;
285 uschar *spf_result_id;
286 int rc = SPF_RESULT_PERMERROR;
288 DEBUG(D_receive) debug_printf("spf_process\n");
290 if (!(spf_server && spf_request))
291 /* no global context, assume temp error and skip to evaluation */
292 rc = SPF_RESULT_PERMERROR;
294 else if (SPF_request_set_env_from(spf_request, CS spf_envelope_sender))
295 /* Invalid sender address. This should be a real rare occurrence */
296 rc = SPF_RESULT_PERMERROR;
301 if (action == SPF_PROCESS_FALLBACK)
303 SPF_request_query_fallback(spf_request, &spf_response, CS spf_guess);
304 spf_result_guessed = TRUE;
307 SPF_request_query_mailfrom(spf_request, &spf_response);
309 /* set up expansion items */
310 spf_header_comment = US SPF_response_get_header_comment(spf_response);
311 spf_received = US SPF_response_get_received_spf(spf_response);
312 spf_result = US SPF_strresult(SPF_response_result(spf_response));
313 spf_smtp_comment = US SPF_response_get_smtp_comment(spf_response);
315 rc = SPF_response_result(spf_response);
318 /* We got a result. Now see if we should return OK or FAIL for it */
319 DEBUG(D_acl) debug_printf("SPF result is %s (%d)\n", SPF_strresult(rc), rc);
321 if (action == SPF_PROCESS_GUESS && (!strcmp (SPF_strresult(rc), "none")))
322 return spf_process(listptr, spf_envelope_sender, SPF_PROCESS_FALLBACK);
324 while ((spf_result_id = string_nextinlist(&list, &sep, NULL, 0)))
328 if ((negate = spf_result_id[0] == '!'))
331 result = Ustrcmp(spf_result_id, spf_result_id_list[rc].name) == 0;
332 if (negate != result) return OK;
342 authres_spf(gstring * g)
345 if (!spf_result) return g;
347 g = string_append(g, 2, US";\n\tspf=", spf_result);
348 if (spf_result_guessed)
349 g = string_cat(g, US" (best guess record for domain)");
351 s = expand_string(US"$sender_address_domain");
353 ? string_append(g, 2, US" smtp.mailfrom=", s)
354 : string_cat(g, US" smtp.mailfrom=<>");