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
60 DEBUG(D_receive) debug_printf("SPF_dns_exim_lookup '%s'\n", domain);
62 if (dns_lookup(dnsa, US domain, rr_type, NULL) == DNS_NOMATCH)
64 SPF_dns_rr_dup(&spfrr, spf_nxdomain);
68 for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
69 rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
70 if (rr->type == rr_type) found++;
73 srr.rr = store_malloc(sizeof(SPF_dns_rr_data_t) * found);
77 for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
78 rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
79 if (rr->type == rr_type)
81 const uschar * s = rr->data;
87 s += 2; /* skip the MX precedence field */
90 uschar * buf = store_malloc(256);
91 (void)dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, s,
92 (DN_EXPAND_ARG4_TYPE)buf, 256);
102 if (strncmpic(rr->data+1, US"v=spf1", 6) != 0)
104 HDEBUG(D_host_lookup) debug_printf("not an spf record\n");
108 for (int off = 0; off < rr->size; off += chunk_len)
110 if (!(chunk_len = s[off++])) break;
111 g = string_catn(g, s+off, chunk_len);
115 gstring_release_unused(g);
116 s = string_copy_malloc(string_from_gstring(g));
124 uschar * buf = store_malloc(dnsa->answerlen + 1);
125 s = memcpy(buf, s, dnsa->answerlen + 1);
129 DEBUG(D_receive) debug_printf("SPF_dns_exim_lookup '%s'\n", s);
130 srr.rr[found++] = (void *) s;
133 /* spfrr->rr must have been malloc()d for this */
134 SPF_dns_rr_dup(&spfrr, &srr);
141 SPF_dns_exim_new(int debug)
143 SPF_dns_server_t * spf_dns_server = store_malloc(sizeof(SPF_dns_server_t));
145 DEBUG(D_receive) debug_printf("SPF_dns_exim_new\n");
147 memset(spf_dns_server, 0, sizeof(SPF_dns_server_t));
148 spf_dns_server->destroy = NULL;
149 spf_dns_server->lookup = SPF_dns_exim_lookup;
150 spf_dns_server->get_spf = NULL;
151 spf_dns_server->get_exp = NULL;
152 spf_dns_server->add_cache = NULL;
153 spf_dns_server->layer_below = NULL;
154 spf_dns_server->name = "exim";
155 spf_dns_server->debug = debug;
157 /* XXX This might have to return NO_DATA sometimes. */
159 spf_nxdomain = SPF_dns_rr_new_init(spf_dns_server,
160 "", ns_t_any, 24 * 60 * 60, HOST_NOT_FOUND);
163 free(spf_dns_server);
167 return spf_dns_server;
173 /* Construct the SPF library stack.
174 Return: Boolean success.
180 SPF_dns_server_t * dc;
183 DEBUG(D_receive) debug = 1;
185 /* We insert our own DNS access layer rather than letting the spf library
186 do it, so that our dns access path is used for debug tracing and for the
189 if (!(dc = SPF_dns_exim_new(debug)))
191 DEBUG(D_receive) debug_printf("spf: SPF_dns_exim_new() failed\n");
194 if (!(dc = SPF_dns_cache_new(dc, NULL, debug, 8)))
196 DEBUG(D_receive) debug_printf("spf: SPF_dns_cache_new() failed\n");
199 if (!(spf_server = SPF_server_new_dns(dc, debug)))
201 DEBUG(D_receive) debug_printf("spf: SPF_server_new() failed.\n");
204 /* Quick hack to override the outdated explanation URL.
205 See https://www.mail-archive.com/mailop@mailop.org/msg08019.html */
206 SPF_server_set_explanation(spf_server, "Please%_see%_http://www.open-spf.org/Why?id=%{S}&ip=%{C}&receiver=%{R}", &spf_response);
207 if (SPF_response_errcode(spf_response) != SPF_E_SUCCESS)
208 log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s", SPF_strerror(SPF_response_errcode(spf_response)));
214 /* Set up a context that can be re-used for several
215 messages on the same SMTP connection (that come from the
216 same host with the same HELO string).
218 Return: Boolean success
222 spf_conn_init(uschar * spf_helo_domain, uschar * spf_remote_addr)
225 debug_printf("spf_conn_init: %s %s\n", spf_helo_domain, spf_remote_addr);
227 if (!spf_server && !spf_init()) return FALSE;
229 if (SPF_server_set_rec_dom(spf_server, CS primary_hostname))
231 DEBUG(D_receive) debug_printf("spf: SPF_server_set_rec_dom(\"%s\") failed.\n",
237 spf_request = SPF_request_new(spf_server);
239 if ( SPF_request_set_ipv4_str(spf_request, CS spf_remote_addr)
240 && SPF_request_set_ipv6_str(spf_request, CS spf_remote_addr)
244 debug_printf("spf: SPF_request_set_ipv4_str() and "
245 "SPF_request_set_ipv6_str() failed [%s]\n", spf_remote_addr);
251 if (SPF_request_set_helo_dom(spf_request, CS spf_helo_domain))
253 DEBUG(D_receive) debug_printf("spf: SPF_set_helo_dom(\"%s\") failed.\n",
264 /* spf_process adds the envelope sender address to the existing
265 context (if any), retrieves the result, sets up expansion
266 strings and evaluates the condition outcome.
271 spf_process(const uschar **listptr, uschar *spf_envelope_sender, int action)
274 const uschar *list = *listptr;
275 uschar *spf_result_id;
276 int rc = SPF_RESULT_PERMERROR;
278 DEBUG(D_receive) debug_printf("spf_process\n");
280 if (!(spf_server && spf_request))
281 /* no global context, assume temp error and skip to evaluation */
282 rc = SPF_RESULT_PERMERROR;
284 else if (SPF_request_set_env_from(spf_request, CS spf_envelope_sender))
285 /* Invalid sender address. This should be a real rare occurrence */
286 rc = SPF_RESULT_PERMERROR;
291 if (action == SPF_PROCESS_FALLBACK)
293 SPF_request_query_fallback(spf_request, &spf_response, CS spf_guess);
294 spf_result_guessed = TRUE;
297 SPF_request_query_mailfrom(spf_request, &spf_response);
299 /* set up expansion items */
300 spf_header_comment = US SPF_response_get_header_comment(spf_response);
301 spf_received = US SPF_response_get_received_spf(spf_response);
302 spf_result = US SPF_strresult(SPF_response_result(spf_response));
303 spf_smtp_comment = US SPF_response_get_smtp_comment(spf_response);
305 rc = SPF_response_result(spf_response);
308 /* We got a result. Now see if we should return OK or FAIL for it */
309 DEBUG(D_acl) debug_printf("SPF result is %s (%d)\n", SPF_strresult(rc), rc);
311 if (action == SPF_PROCESS_GUESS && (!strcmp (SPF_strresult(rc), "none")))
312 return spf_process(listptr, spf_envelope_sender, SPF_PROCESS_FALLBACK);
314 while ((spf_result_id = string_nextinlist(&list, &sep, NULL, 0)))
318 if ((negate = spf_result_id[0] == '!'))
321 result = Ustrcmp(spf_result_id, spf_result_id_list[rc].name) == 0;
322 if (negate != result) return OK;
332 authres_spf(gstring * g)
335 if (!spf_result) return g;
337 g = string_append(g, 2, US";\n\tspf=", spf_result);
338 if (spf_result_guessed)
339 g = string_cat(g, US" (best guess record for domain)");
341 s = expand_string(US"$sender_address_domain");
343 ? string_append(g, 2, US" smtp.mailfrom=", s)
344 : string_cat(g, US" smtp.mailfrom=<>");