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;
81 default: srr.herrno = NO_RECOVERY; break;
84 for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
85 rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
86 if (rr->type == rr_type) found++;
90 SPF_dns_rr_dup(&spfrr, &srr);
94 srr.rr = store_malloc(sizeof(SPF_dns_rr_data_t) * found);
97 for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
98 rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
99 if (rr->type == rr_type)
101 const uschar * s = rr->data;
107 s += 2; /* skip the MX precedence field */
110 uschar * buf = store_malloc(256);
111 (void)dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, s,
112 (DN_EXPAND_ARG4_TYPE)buf, 256);
122 if (strncmpic(rr->data+1, US SPF_VER_STR, 6) != 0)
124 HDEBUG(D_host_lookup) debug_printf("not an spf record\n");
128 for (int off = 0; off < rr->size; off += chunk_len)
130 if (!(chunk_len = s[off++])) break;
131 g = string_catn(g, s+off, chunk_len);
135 gstring_release_unused(g);
136 s = string_copy_malloc(string_from_gstring(g));
144 uschar * buf = store_malloc(dnsa->answerlen + 1);
145 s = memcpy(buf, s, dnsa->answerlen + 1);
149 DEBUG(D_receive) debug_printf("SPF_dns_exim_lookup '%s'\n", s);
150 srr.rr[found++] = (void *) s;
154 /* spfrr->rr must have been malloc()d for this */
155 SPF_dns_rr_dup(&spfrr, &srr);
162 SPF_dns_exim_new(int debug)
164 SPF_dns_server_t * spf_dns_server = store_malloc(sizeof(SPF_dns_server_t));
166 DEBUG(D_receive) debug_printf("SPF_dns_exim_new\n");
168 memset(spf_dns_server, 0, sizeof(SPF_dns_server_t));
169 spf_dns_server->destroy = NULL;
170 spf_dns_server->lookup = SPF_dns_exim_lookup;
171 spf_dns_server->get_spf = NULL;
172 spf_dns_server->get_exp = NULL;
173 spf_dns_server->add_cache = NULL;
174 spf_dns_server->layer_below = NULL;
175 spf_dns_server->name = "exim";
176 spf_dns_server->debug = debug;
178 /* XXX This might have to return NO_DATA sometimes. */
180 spf_nxdomain = SPF_dns_rr_new_init(spf_dns_server,
181 "", ns_t_any, 24 * 60 * 60, HOST_NOT_FOUND);
184 free(spf_dns_server);
188 return spf_dns_server;
194 /* Construct the SPF library stack.
195 Return: Boolean success.
201 SPF_dns_server_t * dc;
204 DEBUG(D_receive) debug = 1;
206 /* We insert our own DNS access layer rather than letting the spf library
207 do it, so that our dns access path is used for debug tracing and for the
210 if (!(dc = SPF_dns_exim_new(debug)))
212 DEBUG(D_receive) debug_printf("spf: SPF_dns_exim_new() failed\n");
215 if (!(dc = SPF_dns_cache_new(dc, NULL, debug, 8)))
217 DEBUG(D_receive) debug_printf("spf: SPF_dns_cache_new() failed\n");
220 if (!(spf_server = SPF_server_new_dns(dc, debug)))
222 DEBUG(D_receive) debug_printf("spf: SPF_server_new() failed.\n");
225 /* Quick hack to override the outdated explanation URL.
226 See https://www.mail-archive.com/mailop@mailop.org/msg08019.html */
227 SPF_server_set_explanation(spf_server, "Please%_see%_http://www.open-spf.org/Why?id=%{S}&ip=%{C}&receiver=%{R}", &spf_response);
228 if (SPF_response_errcode(spf_response) != SPF_E_SUCCESS)
229 log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s", SPF_strerror(SPF_response_errcode(spf_response)));
235 /* Set up a context that can be re-used for several
236 messages on the same SMTP connection (that come from the
237 same host with the same HELO string).
239 Return: Boolean success
243 spf_conn_init(uschar * spf_helo_domain, uschar * spf_remote_addr)
246 debug_printf("spf_conn_init: %s %s\n", spf_helo_domain, spf_remote_addr);
248 if (!spf_server && !spf_init()) return FALSE;
250 if (SPF_server_set_rec_dom(spf_server, CS primary_hostname))
252 DEBUG(D_receive) debug_printf("spf: SPF_server_set_rec_dom(\"%s\") failed.\n",
258 spf_request = SPF_request_new(spf_server);
260 if ( SPF_request_set_ipv4_str(spf_request, CS spf_remote_addr)
261 && SPF_request_set_ipv6_str(spf_request, CS spf_remote_addr)
265 debug_printf("spf: SPF_request_set_ipv4_str() and "
266 "SPF_request_set_ipv6_str() failed [%s]\n", spf_remote_addr);
272 if (SPF_request_set_helo_dom(spf_request, CS spf_helo_domain))
274 DEBUG(D_receive) debug_printf("spf: SPF_set_helo_dom(\"%s\") failed.\n",
285 /* spf_process adds the envelope sender address to the existing
286 context (if any), retrieves the result, sets up expansion
287 strings and evaluates the condition outcome.
292 spf_process(const uschar **listptr, uschar *spf_envelope_sender, int action)
295 const uschar *list = *listptr;
296 uschar *spf_result_id;
297 int rc = SPF_RESULT_PERMERROR;
299 DEBUG(D_receive) debug_printf("spf_process\n");
301 if (!(spf_server && spf_request))
302 /* no global context, assume temp error and skip to evaluation */
303 rc = SPF_RESULT_PERMERROR;
305 else if (SPF_request_set_env_from(spf_request, CS spf_envelope_sender))
306 /* Invalid sender address. This should be a real rare occurrence */
307 rc = SPF_RESULT_PERMERROR;
312 if (action == SPF_PROCESS_FALLBACK)
314 SPF_request_query_fallback(spf_request, &spf_response, CS spf_guess);
315 spf_result_guessed = TRUE;
318 SPF_request_query_mailfrom(spf_request, &spf_response);
320 /* set up expansion items */
321 spf_header_comment = US SPF_response_get_header_comment(spf_response);
322 spf_received = US SPF_response_get_received_spf(spf_response);
323 spf_result = US SPF_strresult(SPF_response_result(spf_response));
324 spf_smtp_comment = US SPF_response_get_smtp_comment(spf_response);
326 rc = SPF_response_result(spf_response);
329 /* We got a result. Now see if we should return OK or FAIL for it */
330 DEBUG(D_acl) debug_printf("SPF result is %s (%d)\n", SPF_strresult(rc), rc);
332 if (action == SPF_PROCESS_GUESS && (!strcmp (SPF_strresult(rc), "none")))
333 return spf_process(listptr, spf_envelope_sender, SPF_PROCESS_FALLBACK);
335 while ((spf_result_id = string_nextinlist(&list, &sep, NULL, 0)))
339 if ((negate = spf_result_id[0] == '!'))
342 result = Ustrcmp(spf_result_id, spf_result_id_list[rc].name) == 0;
343 if (negate != result) return OK;
353 authres_spf(gstring * g)
356 if (!spf_result) return g;
358 g = string_append(g, 2, US";\n\tspf=", spf_result);
359 if (spf_result_guessed)
360 g = string_cat(g, US" (best guess record for domain)");
362 s = expand_string(US"$sender_address_domain");
364 ? string_append(g, 2, US" smtp.mailfrom=", s)
365 : string_cat(g, US" smtp.mailfrom=<>");