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;
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 switch (dns_rc = dns_lookup(dnsa, US domain, rr_type, NULL))
77 case DNS_SUCCEED: srr.herrno = NETDB_SUCCESS; break;
78 case DNS_AGAIN: srr.herrno = TRY_AGAIN; break;
79 case DNS_NOMATCH: srr.herrno = HOST_NOT_FOUND; break;
80 case DNS_NODATA: srr.herrno = NO_DATA; break;
82 default: srr.herrno = NO_RECOVERY; break;
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) found++;
91 SPF_dns_rr_dup(&spfrr, &srr);
95 srr.rr = store_malloc(sizeof(SPF_dns_rr_data_t) * found);
98 for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
99 rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
100 if (rr->type == rr_type)
102 const uschar * s = rr->data;
108 s += 2; /* skip the MX precedence field */
111 uschar * buf = store_malloc(256);
112 (void)dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, s,
113 (DN_EXPAND_ARG4_TYPE)buf, 256);
123 if (strncmpic(rr->data+1, US SPF_VER_STR, 6) != 0)
125 HDEBUG(D_host_lookup) debug_printf("not an spf record\n");
129 for (int off = 0; off < rr->size; off += chunk_len)
131 if (!(chunk_len = s[off++])) break;
132 g = string_catn(g, s+off, chunk_len);
136 gstring_release_unused(g);
137 s = string_copy_malloc(string_from_gstring(g));
145 uschar * buf = store_malloc(dnsa->answerlen + 1);
146 s = memcpy(buf, s, dnsa->answerlen + 1);
150 DEBUG(D_receive) debug_printf("SPF_dns_exim_lookup '%s'\n", s);
151 srr.rr[found++] = (void *) s;
155 /* spfrr->rr must have been malloc()d for this */
156 SPF_dns_rr_dup(&spfrr, &srr);
163 SPF_dns_exim_new(int debug)
165 SPF_dns_server_t * spf_dns_server = store_malloc(sizeof(SPF_dns_server_t));
167 DEBUG(D_receive) debug_printf("SPF_dns_exim_new\n");
169 memset(spf_dns_server, 0, sizeof(SPF_dns_server_t));
170 spf_dns_server->destroy = NULL;
171 spf_dns_server->lookup = SPF_dns_exim_lookup;
172 spf_dns_server->get_spf = NULL;
173 spf_dns_server->get_exp = NULL;
174 spf_dns_server->add_cache = NULL;
175 spf_dns_server->layer_below = NULL;
176 spf_dns_server->name = "exim";
177 spf_dns_server->debug = debug;
179 /* XXX This might have to return NO_DATA sometimes. */
181 spf_nxdomain = SPF_dns_rr_new_init(spf_dns_server,
182 "", ns_t_any, 24 * 60 * 60, HOST_NOT_FOUND);
185 free(spf_dns_server);
189 return spf_dns_server;
195 /* Construct the SPF library stack.
196 Return: Boolean success.
202 SPF_dns_server_t * dc;
205 DEBUG(D_receive) debug = 1;
207 /* We insert our own DNS access layer rather than letting the spf library
208 do it, so that our dns access path is used for debug tracing and for the
211 if (!(dc = SPF_dns_exim_new(debug)))
213 DEBUG(D_receive) debug_printf("spf: SPF_dns_exim_new() failed\n");
216 if (!(dc = SPF_dns_cache_new(dc, NULL, debug, 8)))
218 DEBUG(D_receive) debug_printf("spf: SPF_dns_cache_new() failed\n");
221 if (!(spf_server = SPF_server_new_dns(dc, debug)))
223 DEBUG(D_receive) debug_printf("spf: SPF_server_new() failed.\n");
226 /* Quick hack to override the outdated explanation URL.
227 See https://www.mail-archive.com/mailop@mailop.org/msg08019.html */
228 SPF_server_set_explanation(spf_server, "Please%_see%_http://www.open-spf.org/Why?id=%{S}&ip=%{C}&receiver=%{R}", &spf_response);
229 if (SPF_response_errcode(spf_response) != SPF_E_SUCCESS)
230 log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s", SPF_strerror(SPF_response_errcode(spf_response)));
236 /* Set up a context that can be re-used for several
237 messages on the same SMTP connection (that come from the
238 same host with the same HELO string).
240 Return: Boolean success
244 spf_conn_init(uschar * spf_helo_domain, uschar * spf_remote_addr)
247 debug_printf("spf_conn_init: %s %s\n", spf_helo_domain, spf_remote_addr);
249 if (!spf_server && !spf_init()) return FALSE;
251 if (SPF_server_set_rec_dom(spf_server, CS primary_hostname))
253 DEBUG(D_receive) debug_printf("spf: SPF_server_set_rec_dom(\"%s\") failed.\n",
259 spf_request = SPF_request_new(spf_server);
261 if ( SPF_request_set_ipv4_str(spf_request, CS spf_remote_addr)
262 && SPF_request_set_ipv6_str(spf_request, CS spf_remote_addr)
266 debug_printf("spf: SPF_request_set_ipv4_str() and "
267 "SPF_request_set_ipv6_str() failed [%s]\n", spf_remote_addr);
273 if (SPF_request_set_helo_dom(spf_request, CS spf_helo_domain))
275 DEBUG(D_receive) debug_printf("spf: SPF_set_helo_dom(\"%s\") failed.\n",
286 /* spf_process adds the envelope sender address to the existing
287 context (if any), retrieves the result, sets up expansion
288 strings and evaluates the condition outcome.
293 spf_process(const uschar **listptr, uschar *spf_envelope_sender, int action)
296 const uschar *list = *listptr;
297 uschar *spf_result_id;
298 int rc = SPF_RESULT_PERMERROR;
300 DEBUG(D_receive) debug_printf("spf_process\n");
302 if (!(spf_server && spf_request))
303 /* no global context, assume temp error and skip to evaluation */
304 rc = SPF_RESULT_PERMERROR;
306 else if (SPF_request_set_env_from(spf_request, CS spf_envelope_sender))
307 /* Invalid sender address. This should be a real rare occurrence */
308 rc = SPF_RESULT_PERMERROR;
313 if (action == SPF_PROCESS_FALLBACK)
315 SPF_request_query_fallback(spf_request, &spf_response, CS spf_guess);
316 spf_result_guessed = TRUE;
319 SPF_request_query_mailfrom(spf_request, &spf_response);
321 /* set up expansion items */
322 spf_header_comment = US SPF_response_get_header_comment(spf_response);
323 spf_received = US SPF_response_get_received_spf(spf_response);
324 spf_result = US SPF_strresult(SPF_response_result(spf_response));
325 spf_smtp_comment = US SPF_response_get_smtp_comment(spf_response);
327 rc = SPF_response_result(spf_response);
330 /* We got a result. Now see if we should return OK or FAIL for it */
331 DEBUG(D_acl) debug_printf("SPF result is %s (%d)\n", SPF_strresult(rc), rc);
333 if (action == SPF_PROCESS_GUESS && (!strcmp (SPF_strresult(rc), "none")))
334 return spf_process(listptr, spf_envelope_sender, SPF_PROCESS_FALLBACK);
336 while ((spf_result_id = string_nextinlist(&list, &sep, NULL, 0)))
340 if ((negate = spf_result_id[0] == '!'))
343 result = Ustrcmp(spf_result_id, spf_result_id_list[rc].name) == 0;
344 if (negate != result) return OK;
354 authres_spf(gstring * g)
357 if (!spf_result) return g;
359 g = string_append(g, 2, US";\n\tspf=", spf_result);
360 if (spf_result_guessed)
361 g = string_cat(g, US" (best guess record for domain)");
363 s = expand_string(US"$sender_address_domain");
365 ? string_append(g, 2, US" smtp.mailfrom=", s)
366 : string_cat(g, US" smtp.mailfrom=<>");