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();
46 DEBUG(D_receive) debug_printf("SPF_dns_exim_lookup\n");
48 if (dns_lookup(dnsa, US domain, rr_type, NULL) == DNS_SUCCEED)
49 for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
50 rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
51 if ( rr->type == rr_type
52 && Ustrncmp(rr->data+1, "v=spf1", 6) == 0)
58 .domain = CS rr->name, /* query information */
59 .domain_buf_len = DNS_MAXNAME,
62 .num_rr = 1, /* answer information */
68 .herrno = NETDB_SUCCESS,
70 .hook = NULL, /* misc information */
71 .source = spf_dns_server
74 for (int off = 0; off < rr->size; off += chunk_len)
76 chunk_len = (rr->data)[off++];
77 g = string_catn(g, US ((rr->data)+off), chunk_len);
81 HDEBUG(D_host_lookup) debug_printf("IP address lookup yielded an "
82 "empty name: treated as non-existent host name\n");
85 gstring_release_unused(g);
86 s = string_copy_malloc(string_from_gstring(g));
89 /* spfrr->rr must have been malloc()d for this */
90 SPF_dns_rr_dup(&spfrr, &srr);
95 SPF_dns_rr_dup(&spfrr, spf_nxdomain);
102 SPF_dns_exim_new(int debug)
104 SPF_dns_server_t *spf_dns_server;
106 DEBUG(D_receive) debug_printf("SPF_dns_exim_new\n");
108 if (!(spf_dns_server = malloc(sizeof(SPF_dns_server_t))))
110 memset(spf_dns_server, 0, sizeof(SPF_dns_server_t));
112 spf_dns_server->destroy = NULL;
113 spf_dns_server->lookup = SPF_dns_exim_lookup;
114 spf_dns_server->get_spf = NULL;
115 spf_dns_server->get_exp = NULL;
116 spf_dns_server->add_cache = NULL;
117 spf_dns_server->layer_below = NULL;
118 spf_dns_server->name = "exim";
119 spf_dns_server->debug = debug;
121 /* XXX This might have to return NO_DATA sometimes. */
123 spf_nxdomain = SPF_dns_rr_new_init(spf_dns_server,
124 "", ns_t_any, 24 * 60 * 60, HOST_NOT_FOUND);
127 free(spf_dns_server);
131 return spf_dns_server;
137 /* Construct the SPF library stack.
138 Return: Boolean success.
144 SPF_dns_server_t * dc;
147 DEBUG(D_receive) debug = 1;
149 /* We insert our own DNS access layer rather than letting the spf library
150 do it, so that our dns access path is used for debug tracing and for the
153 if (!(dc = SPF_dns_exim_new(debug)))
155 DEBUG(D_receive) debug_printf("spf: SPF_dns_exim_new() failed\n");
158 if (!(dc = SPF_dns_cache_new(dc, NULL, debug, 8)))
160 DEBUG(D_receive) debug_printf("spf: SPF_dns_cache_new() failed\n");
163 if (!(spf_server = SPF_server_new_dns(dc, debug)))
165 DEBUG(D_receive) debug_printf("spf: SPF_server_new() failed.\n");
168 /* Quick hack to override the outdated explanation URL.
169 See https://www.mail-archive.com/mailop@mailop.org/msg08019.html */
170 SPF_server_set_explanation(spf_server, "Please%_see%_http://www.open-spf.org/Why?id=%{S}&ip=%{C}&receiver=%{R}", &spf_response);
171 if (SPF_response_errcode(spf_response) != SPF_E_SUCCESS)
172 log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s", SPF_strerror(SPF_response_errcode(spf_response)));
178 /* Set up a context that can be re-used for several
179 messages on the same SMTP connection (that come from the
180 same host with the same HELO string).
182 Return: Boolean success
186 spf_conn_init(uschar * spf_helo_domain, uschar * spf_remote_addr)
189 debug_printf("spf_conn_init: %s %s\n", spf_helo_domain, spf_remote_addr);
191 if (!spf_server && !spf_init()) return FALSE;
193 if (SPF_server_set_rec_dom(spf_server, CS primary_hostname))
195 DEBUG(D_receive) debug_printf("spf: SPF_server_set_rec_dom(\"%s\") failed.\n",
201 spf_request = SPF_request_new(spf_server);
203 if ( SPF_request_set_ipv4_str(spf_request, CS spf_remote_addr)
204 && SPF_request_set_ipv6_str(spf_request, CS spf_remote_addr)
208 debug_printf("spf: SPF_request_set_ipv4_str() and "
209 "SPF_request_set_ipv6_str() failed [%s]\n", spf_remote_addr);
215 if (SPF_request_set_helo_dom(spf_request, CS spf_helo_domain))
217 DEBUG(D_receive) debug_printf("spf: SPF_set_helo_dom(\"%s\") failed.\n",
228 /* spf_process adds the envelope sender address to the existing
229 context (if any), retrieves the result, sets up expansion
230 strings and evaluates the condition outcome.
235 spf_process(const uschar **listptr, uschar *spf_envelope_sender, int action)
238 const uschar *list = *listptr;
239 uschar *spf_result_id;
240 int rc = SPF_RESULT_PERMERROR;
242 DEBUG(D_receive) debug_printf("spf_process\n");
244 if (!(spf_server && spf_request))
245 /* no global context, assume temp error and skip to evaluation */
246 rc = SPF_RESULT_PERMERROR;
248 else if (SPF_request_set_env_from(spf_request, CS spf_envelope_sender))
249 /* Invalid sender address. This should be a real rare occurrence */
250 rc = SPF_RESULT_PERMERROR;
255 if (action == SPF_PROCESS_FALLBACK)
257 SPF_request_query_fallback(spf_request, &spf_response, CS spf_guess);
258 spf_result_guessed = TRUE;
261 SPF_request_query_mailfrom(spf_request, &spf_response);
263 /* set up expansion items */
264 spf_header_comment = US SPF_response_get_header_comment(spf_response);
265 spf_received = US SPF_response_get_received_spf(spf_response);
266 spf_result = US SPF_strresult(SPF_response_result(spf_response));
267 spf_smtp_comment = US SPF_response_get_smtp_comment(spf_response);
269 rc = SPF_response_result(spf_response);
272 /* We got a result. Now see if we should return OK or FAIL for it */
273 DEBUG(D_acl) debug_printf("SPF result is %s (%d)\n", SPF_strresult(rc), rc);
275 if (action == SPF_PROCESS_GUESS && (!strcmp (SPF_strresult(rc), "none")))
276 return spf_process(listptr, spf_envelope_sender, SPF_PROCESS_FALLBACK);
278 while ((spf_result_id = string_nextinlist(&list, &sep, NULL, 0)))
282 if ((negate = spf_result_id[0] == '!'))
285 result = Ustrcmp(spf_result_id, spf_result_id_list[rc].name) == 0;
286 if (negate != result) return OK;
296 authres_spf(gstring * g)
299 if (!spf_result) return g;
301 g = string_append(g, 2, US";\n\tspf=", spf_result);
302 if (spf_result_guessed)
303 g = string_cat(g, US" (best guess record for domain)");
305 s = expand_string(US"$sender_address_domain");
307 ? string_append(g, 2, US" smtp.mailfrom=", s)
308 : string_cat(g, US" smtp.mailfrom=<>");