DMARC: Use exim facilities for DNS lookups
[exim.git] / src / src / spf.c
CommitLineData
8523533c
TK
1/*************************************************
2* Exim - an Internet mail transport agent *
3*************************************************/
8e669ac1 4
b8e97684 5/* SPF support.
5a66c31b 6 Copyright (c) Tom Kistner <tom@duncanthrax.net> 2004 - 2014
80fea873 7 License: GPL
b8e97684 8 Copyright (c) The Exim Maintainers 2015 - 2019
80fea873 9*/
8e669ac1 10
8523533c
TK
11/* Code for calling spf checks via libspf-alt. Called from acl.c. */
12
13#include "exim.h"
7952eef9 14#ifdef SUPPORT_SPF
8523533c 15
6d06cf48
TK
16/* must be kept in numeric order */
17static spf_result_id spf_result_id_list[] = {
f2ed27cf
JH
18 /* name value */
19 { US"invalid", 0},
20 { US"neutral", 1 },
21 { US"pass", 2 },
22 { US"fail", 3 },
23 { US"softfail", 4 },
24 { US"none", 5 },
f2ed27cf
JH
25 { US"temperror", 6 }, /* RFC 4408 defined */
26 { US"permerror", 7 } /* RFC 4408 defined */
6d06cf48
TK
27};
28
384152a6
TK
29SPF_server_t *spf_server = NULL;
30SPF_request_t *spf_request = NULL;
31SPF_response_t *spf_response = NULL;
32SPF_response_t *spf_response_2mx = NULL;
8523533c 33
b8e97684
JH
34SPF_dns_rr_t * spf_nxdomain = NULL;
35
36
37
38static SPF_dns_rr_t *
39SPF_dns_exim_lookup(SPF_dns_server_t *spf_dns_server,
40const char *domain, ns_type rr_type, int should_cache)
41{
8743d3ac 42dns_answer * dnsa = store_get_dns_answer();
b8e97684
JH
43dns_scan dnss;
44SPF_dns_rr_t * spfrr;
45
46DEBUG(D_receive) debug_printf("SPF_dns_exim_lookup\n");
47
8743d3ac
JH
48if (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))
b8e97684
JH
51 if ( rr->type == rr_type
52 && Ustrncmp(rr->data+1, "v=spf1", 6) == 0)
53 {
54 gstring * g = NULL;
55 uschar chunk_len;
56 uschar * s;
57 SPF_dns_rr_t srr = {
58 .domain = CS rr->name, /* query information */
59 .domain_buf_len = DNS_MAXNAME,
60 .rr_type = rr->type,
61
62 .num_rr = 1, /* answer information */
63 .rr = NULL,
64 .rr_buf_len = 0,
65 .rr_buf_num = 0,
66 .ttl = rr->ttl,
67 .utc_ttl = 0,
68 .herrno = NETDB_SUCCESS,
69
70 .hook = NULL, /* misc information */
71 .source = spf_dns_server
72 };
73
74 for (int off = 0; off < rr->size; off += chunk_len)
75 {
76 chunk_len = (rr->data)[off++];
77 g = string_catn(g, US ((rr->data)+off), chunk_len);
78 }
79 if (!g)
80 {
81 HDEBUG(D_host_lookup) debug_printf("IP address lookup yielded an "
82 "empty name: treated as non-existent host name\n");
83 continue;
84 }
85 gstring_release_unused(g);
86 s = string_copy_malloc(string_from_gstring(g));
87 srr.rr = (void *) &s;
88
89 /* spfrr->rr must have been malloc()d for this */
90 SPF_dns_rr_dup(&spfrr, &srr);
91
92 return spfrr;
93 }
94
95SPF_dns_rr_dup(&spfrr, spf_nxdomain);
96return spfrr;
97}
98
99
100
101SPF_dns_server_t *
102SPF_dns_exim_new(int debug)
103{
104SPF_dns_server_t *spf_dns_server;
105
106DEBUG(D_receive) debug_printf("SPF_dns_exim_new\n");
107
108if (!(spf_dns_server = malloc(sizeof(SPF_dns_server_t))))
109 return NULL;
110memset(spf_dns_server, 0, sizeof(SPF_dns_server_t));
111
112spf_dns_server->destroy = NULL;
113spf_dns_server->lookup = SPF_dns_exim_lookup;
114spf_dns_server->get_spf = NULL;
115spf_dns_server->get_exp = NULL;
116spf_dns_server->add_cache = NULL;
117spf_dns_server->layer_below = NULL;
118spf_dns_server->name = "exim";
119spf_dns_server->debug = debug;
120
121/* XXX This might have to return NO_DATA sometimes. */
122
123spf_nxdomain = SPF_dns_rr_new_init(spf_dns_server,
124 "", ns_t_any, 24 * 60 * 60, HOST_NOT_FOUND);
125if (!spf_nxdomain)
126 {
127 free(spf_dns_server);
128 return NULL;
129 }
130
131return spf_dns_server;
132}
133
134
eb52e2cb 135
8523533c
TK
136/* spf_init sets up a context that can be re-used for several
137 messages on the same SMTP connection (that come from the
b8e97684
JH
138 same host with the same HELO string).
139XXX the spf_server layer could usefully be separately init'd
140given that it sets up a dns cache.
8e669ac1 141
eb52e2cb 142Return: Boolean success */
8e669ac1 143
eb52e2cb
JH
144BOOL
145spf_init(uschar *spf_helo_domain, uschar *spf_remote_addr)
146{
b8e97684
JH
147int debug = 0;
148SPF_dns_server_t * dc;
149
150DEBUG(D_receive)
151 {
152 debug_printf("spf_init: %s %s\n", spf_helo_domain, spf_remote_addr);
153 debug = 1;
154 }
155
156/* We insert our own DNS access layer rather than letting the spf library
157do it, so that our dns access path is used for debug tracing and for the
158testsuite. */
8e669ac1 159
b8e97684
JH
160if (!(dc = SPF_dns_exim_new(debug)))
161 {
162 DEBUG(D_receive) debug_printf("spf: SPF_dns_exim_new() failed\n");
163 return FALSE;
164 }
165if (!(dc = SPF_dns_cache_new(dc, NULL, debug, 8)))
166 {
167 DEBUG(D_receive) debug_printf("spf: SPF_dns_cache_new() failed\n");
168 return FALSE;
169 }
170if (!(spf_server = SPF_server_new_dns(dc, debug)))
eb52e2cb
JH
171 {
172 DEBUG(D_receive) debug_printf("spf: SPF_server_new() failed.\n");
173 return FALSE;
8523533c
TK
174 }
175
eb52e2cb
JH
176if (SPF_server_set_rec_dom(spf_server, CS primary_hostname))
177 {
178 DEBUG(D_receive) debug_printf("spf: SPF_server_set_rec_dom(\"%s\") failed.\n",
179 primary_hostname);
180 spf_server = NULL;
181 return FALSE;
7b3a77e5
TK
182 }
183
eb52e2cb
JH
184spf_request = SPF_request_new(spf_server);
185
186if ( SPF_request_set_ipv4_str(spf_request, CS spf_remote_addr)
187 && SPF_request_set_ipv6_str(spf_request, CS spf_remote_addr)
188 )
189 {
190 DEBUG(D_receive)
191 debug_printf("spf: SPF_request_set_ipv4_str() and "
192 "SPF_request_set_ipv6_str() failed [%s]\n", spf_remote_addr);
193 spf_server = NULL;
194 spf_request = NULL;
195 return FALSE;
8523533c
TK
196 }
197
eb52e2cb
JH
198if (SPF_request_set_helo_dom(spf_request, CS spf_helo_domain))
199 {
200 DEBUG(D_receive) debug_printf("spf: SPF_set_helo_dom(\"%s\") failed.\n",
201 spf_helo_domain);
202 spf_server = NULL;
203 spf_request = NULL;
204 return FALSE;
8523533c 205 }
8e669ac1 206
eb52e2cb 207return TRUE;
8523533c
TK
208}
209
210
211/* spf_process adds the envelope sender address to the existing
212 context (if any), retrieves the result, sets up expansion
eb52e2cb 213 strings and evaluates the condition outcome.
f9ba5e22 214
eb52e2cb
JH
215Return: OK/FAIL */
216
217int
218spf_process(const uschar **listptr, uschar *spf_envelope_sender, int action)
219{
220int sep = 0;
221const uschar *list = *listptr;
222uschar *spf_result_id;
223int rc = SPF_RESULT_PERMERROR;
224
b8e97684
JH
225DEBUG(D_receive) debug_printf("spf_process\n");
226
eb52e2cb
JH
227if (!(spf_server && spf_request))
228 /* no global context, assume temp error and skip to evaluation */
229 rc = SPF_RESULT_PERMERROR;
230
231else if (SPF_request_set_env_from(spf_request, CS spf_envelope_sender))
aded2255 232 /* Invalid sender address. This should be a real rare occurrence */
eb52e2cb
JH
233 rc = SPF_RESULT_PERMERROR;
234
235else
236 {
8523533c 237 /* get SPF result */
65a7d8c3 238 if (action == SPF_PROCESS_FALLBACK)
87e9d061 239 {
7156b1ef 240 SPF_request_query_fallback(spf_request, &spf_response, CS spf_guess);
87e9d061
JH
241 spf_result_guessed = TRUE;
242 }
65a7d8c3
NM
243 else
244 SPF_request_query_mailfrom(spf_request, &spf_response);
8523533c
TK
245
246 /* set up expansion items */
5903c6ff
JH
247 spf_header_comment = US SPF_response_get_header_comment(spf_response);
248 spf_received = US SPF_response_get_received_spf(spf_response);
249 spf_result = US SPF_strresult(SPF_response_result(spf_response));
250 spf_smtp_comment = US SPF_response_get_smtp_comment(spf_response);
8523533c 251
384152a6 252 rc = SPF_response_result(spf_response);
eb52e2cb
JH
253 }
254
255/* We got a result. Now see if we should return OK or FAIL for it */
256DEBUG(D_acl) debug_printf("SPF result is %s (%d)\n", SPF_strresult(rc), rc);
257
258if (action == SPF_PROCESS_GUESS && (!strcmp (SPF_strresult(rc), "none")))
259 return spf_process(listptr, spf_envelope_sender, SPF_PROCESS_FALLBACK);
260
261while ((spf_result_id = string_nextinlist(&list, &sep, NULL, 0)))
262 {
263 BOOL negate, result;
264
265 if ((negate = spf_result_id[0] == '!'))
266 spf_result_id++;
267
268 result = Ustrcmp(spf_result_id, spf_result_id_list[rc].name) == 0;
269 if (negate != result) return OK;
270 }
8523533c 271
eb52e2cb
JH
272/* no match */
273return FAIL;
8523533c
TK
274}
275
dfbcb5ac
JH
276
277
278gstring *
279authres_spf(gstring * g)
280{
87e9d061 281uschar * s;
dfbcb5ac
JH
282if (!spf_result) return g;
283
87e9d061
JH
284g = string_append(g, 2, US";\n\tspf=", spf_result);
285if (spf_result_guessed)
286 g = string_cat(g, US" (best guess record for domain)");
287
288s = expand_string(US"$sender_address_domain");
289return s && *s
290 ? string_append(g, 2, US" smtp.mailfrom=", s)
291 : string_cat(g, US" smtp.mailfrom=<>");
dfbcb5ac
JH
292}
293
294
8523533c 295#endif