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;
69 default: srr.herrno = NO_RECOVERY; break;
72 for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
73 rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
74 if (rr->type == rr_type) found++;
78 SPF_dns_rr_dup(&spfrr, &srr);
82 srr.rr = store_malloc(sizeof(SPF_dns_rr_data_t) * found);
85 for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
86 rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
87 if (rr->type == rr_type)
89 const uschar * s = rr->data;
95 s += 2; /* skip the MX precedence field */
98 uschar * buf = store_malloc(256);
99 (void)dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, s,
100 (DN_EXPAND_ARG4_TYPE)buf, 256);
110 if (strncmpic(rr->data+1, US"v=spf1", 6) != 0)
112 HDEBUG(D_host_lookup) debug_printf("not an spf record\n");
116 for (int off = 0; off < rr->size; off += chunk_len)
118 if (!(chunk_len = s[off++])) break;
119 g = string_catn(g, s+off, chunk_len);
123 gstring_release_unused(g);
124 s = string_copy_malloc(string_from_gstring(g));
132 uschar * buf = store_malloc(dnsa->answerlen + 1);
133 s = memcpy(buf, s, dnsa->answerlen + 1);
137 DEBUG(D_receive) debug_printf("SPF_dns_exim_lookup '%s'\n", s);
138 srr.rr[found++] = (void *) s;
142 /* spfrr->rr must have been malloc()d for this */
143 SPF_dns_rr_dup(&spfrr, &srr);
150 SPF_dns_exim_new(int debug)
152 SPF_dns_server_t * spf_dns_server = store_malloc(sizeof(SPF_dns_server_t));
154 DEBUG(D_receive) debug_printf("SPF_dns_exim_new\n");
156 memset(spf_dns_server, 0, sizeof(SPF_dns_server_t));
157 spf_dns_server->destroy = NULL;
158 spf_dns_server->lookup = SPF_dns_exim_lookup;
159 spf_dns_server->get_spf = NULL;
160 spf_dns_server->get_exp = NULL;
161 spf_dns_server->add_cache = NULL;
162 spf_dns_server->layer_below = NULL;
163 spf_dns_server->name = "exim";
164 spf_dns_server->debug = debug;
166 /* XXX This might have to return NO_DATA sometimes. */
168 spf_nxdomain = SPF_dns_rr_new_init(spf_dns_server,
169 "", ns_t_any, 24 * 60 * 60, HOST_NOT_FOUND);
172 free(spf_dns_server);
176 return spf_dns_server;
182 /* Construct the SPF library stack.
183 Return: Boolean success.
189 SPF_dns_server_t * dc;
192 DEBUG(D_receive) debug = 1;
194 /* We insert our own DNS access layer rather than letting the spf library
195 do it, so that our dns access path is used for debug tracing and for the
198 if (!(dc = SPF_dns_exim_new(debug)))
200 DEBUG(D_receive) debug_printf("spf: SPF_dns_exim_new() failed\n");
203 if (!(dc = SPF_dns_cache_new(dc, NULL, debug, 8)))
205 DEBUG(D_receive) debug_printf("spf: SPF_dns_cache_new() failed\n");
208 if (!(spf_server = SPF_server_new_dns(dc, debug)))
210 DEBUG(D_receive) debug_printf("spf: SPF_server_new() failed.\n");
213 /* Quick hack to override the outdated explanation URL.
214 See https://www.mail-archive.com/mailop@mailop.org/msg08019.html */
215 SPF_server_set_explanation(spf_server, "Please%_see%_http://www.open-spf.org/Why?id=%{S}&ip=%{C}&receiver=%{R}", &spf_response);
216 if (SPF_response_errcode(spf_response) != SPF_E_SUCCESS)
217 log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s", SPF_strerror(SPF_response_errcode(spf_response)));
223 /* Set up a context that can be re-used for several
224 messages on the same SMTP connection (that come from the
225 same host with the same HELO string).
227 Return: Boolean success
231 spf_conn_init(uschar * spf_helo_domain, uschar * spf_remote_addr)
234 debug_printf("spf_conn_init: %s %s\n", spf_helo_domain, spf_remote_addr);
236 if (!spf_server && !spf_init()) return FALSE;
238 if (SPF_server_set_rec_dom(spf_server, CS primary_hostname))
240 DEBUG(D_receive) debug_printf("spf: SPF_server_set_rec_dom(\"%s\") failed.\n",
246 spf_request = SPF_request_new(spf_server);
248 if ( SPF_request_set_ipv4_str(spf_request, CS spf_remote_addr)
249 && SPF_request_set_ipv6_str(spf_request, CS spf_remote_addr)
253 debug_printf("spf: SPF_request_set_ipv4_str() and "
254 "SPF_request_set_ipv6_str() failed [%s]\n", spf_remote_addr);
260 if (SPF_request_set_helo_dom(spf_request, CS spf_helo_domain))
262 DEBUG(D_receive) debug_printf("spf: SPF_set_helo_dom(\"%s\") failed.\n",
273 /* spf_process adds the envelope sender address to the existing
274 context (if any), retrieves the result, sets up expansion
275 strings and evaluates the condition outcome.
280 spf_process(const uschar **listptr, uschar *spf_envelope_sender, int action)
283 const uschar *list = *listptr;
284 uschar *spf_result_id;
285 int rc = SPF_RESULT_PERMERROR;
287 DEBUG(D_receive) debug_printf("spf_process\n");
289 if (!(spf_server && spf_request))
290 /* no global context, assume temp error and skip to evaluation */
291 rc = SPF_RESULT_PERMERROR;
293 else if (SPF_request_set_env_from(spf_request, CS spf_envelope_sender))
294 /* Invalid sender address. This should be a real rare occurrence */
295 rc = SPF_RESULT_PERMERROR;
300 if (action == SPF_PROCESS_FALLBACK)
302 SPF_request_query_fallback(spf_request, &spf_response, CS spf_guess);
303 spf_result_guessed = TRUE;
306 SPF_request_query_mailfrom(spf_request, &spf_response);
308 /* set up expansion items */
309 spf_header_comment = US SPF_response_get_header_comment(spf_response);
310 spf_received = US SPF_response_get_received_spf(spf_response);
311 spf_result = US SPF_strresult(SPF_response_result(spf_response));
312 spf_smtp_comment = US SPF_response_get_smtp_comment(spf_response);
314 rc = SPF_response_result(spf_response);
317 /* We got a result. Now see if we should return OK or FAIL for it */
318 DEBUG(D_acl) debug_printf("SPF result is %s (%d)\n", SPF_strresult(rc), rc);
320 if (action == SPF_PROCESS_GUESS && (!strcmp (SPF_strresult(rc), "none")))
321 return spf_process(listptr, spf_envelope_sender, SPF_PROCESS_FALLBACK);
323 while ((spf_result_id = string_nextinlist(&list, &sep, NULL, 0)))
327 if ((negate = spf_result_id[0] == '!'))
330 result = Ustrcmp(spf_result_id, spf_result_id_list[rc].name) == 0;
331 if (negate != result) return OK;
341 authres_spf(gstring * g)
344 if (!spf_result) return g;
346 g = string_append(g, 2, US";\n\tspf=", spf_result);
347 if (spf_result_guessed)
348 g = string_cat(g, US" (best guess record for domain)");
350 s = expand_string(US"$sender_address_domain");
352 ? string_append(g, 2, US" smtp.mailfrom=", s)
353 : string_cat(g, US" smtp.mailfrom=<>");