Fix Solaris build (pt.2)
[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
4a3709fb 46DEBUG(D_receive) debug_printf("SPF_dns_exim_lookup '%s'\n", domain);
b8e97684 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 51 if ( rr->type == rr_type
4a3709fb 52 && (rr_type != T_TXT || Ustrncmp(rr->data+1, "v=spf1", 6) == 0))
b8e97684 53 {
4a3709fb 54 const uschar * s = rr->data;
b8e97684
JH
55 SPF_dns_rr_t srr = {
56 .domain = CS rr->name, /* query information */
57 .domain_buf_len = DNS_MAXNAME,
58 .rr_type = rr->type,
59
60 .num_rr = 1, /* answer information */
61 .rr = NULL,
62 .rr_buf_len = 0,
63 .rr_buf_num = 0,
64 .ttl = rr->ttl,
65 .utc_ttl = 0,
66 .herrno = NETDB_SUCCESS,
67
68 .hook = NULL, /* misc information */
69 .source = spf_dns_server
70 };
71
4a3709fb 72 switch(rr_type)
b8e97684 73 {
4a3709fb
WB
74 case T_MX:
75 s += 2; /* skip the MX precedence field */
76 case T_PTR:
77 {
78 uschar * buf = store_malloc(256);
79 (void)dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, s,
80 (DN_EXPAND_ARG4_TYPE)buf, 256);
81 s = buf;
82 break;
83 }
84
85 case T_TXT:
86 {
87 gstring * g = NULL;
88 uschar chunk_len;
89 for (int off = 0; off < rr->size; off += chunk_len)
90 {
91 if (!(chunk_len = s[off++])) break;
92 g = string_catn(g, s+off, chunk_len);
93 }
94 if (!g)
95 {
96 HDEBUG(D_host_lookup) debug_printf("IP address lookup yielded an "
97 "empty name: treated as non-existent host name\n");
98 continue;
99 }
100 gstring_release_unused(g);
101 s = string_copy_malloc(string_from_gstring(g));
102 break;
103 }
104
105 case T_A:
106 case T_AAAA:
107 default:
108 {
109 uschar * buf = store_malloc(dnsa->answerlen + 1);
110 s = memcpy(buf, s, dnsa->answerlen + 1);
111 break;
112 }
b8e97684 113 }
4a3709fb 114 DEBUG(D_receive) debug_printf("SPF_dns_exim_lookup '%s'\n", s);
b8e97684
JH
115 srr.rr = (void *) &s;
116
117 /* spfrr->rr must have been malloc()d for this */
118 SPF_dns_rr_dup(&spfrr, &srr);
119
120 return spfrr;
121 }
122
123SPF_dns_rr_dup(&spfrr, spf_nxdomain);
124return spfrr;
125}
126
127
128
129SPF_dns_server_t *
130SPF_dns_exim_new(int debug)
131{
132SPF_dns_server_t *spf_dns_server;
133
134DEBUG(D_receive) debug_printf("SPF_dns_exim_new\n");
135
136if (!(spf_dns_server = malloc(sizeof(SPF_dns_server_t))))
137 return NULL;
138memset(spf_dns_server, 0, sizeof(SPF_dns_server_t));
139
140spf_dns_server->destroy = NULL;
141spf_dns_server->lookup = SPF_dns_exim_lookup;
142spf_dns_server->get_spf = NULL;
143spf_dns_server->get_exp = NULL;
144spf_dns_server->add_cache = NULL;
145spf_dns_server->layer_below = NULL;
146spf_dns_server->name = "exim";
147spf_dns_server->debug = debug;
148
149/* XXX This might have to return NO_DATA sometimes. */
150
151spf_nxdomain = SPF_dns_rr_new_init(spf_dns_server,
152 "", ns_t_any, 24 * 60 * 60, HOST_NOT_FOUND);
153if (!spf_nxdomain)
154 {
155 free(spf_dns_server);
156 return NULL;
157 }
158
159return spf_dns_server;
160}
161
162
eb52e2cb 163
8e669ac1 164
73ec116f
JH
165/* Construct the SPF library stack.
166 Return: Boolean success.
167*/
8e669ac1 168
eb52e2cb 169BOOL
73ec116f 170spf_init(void)
eb52e2cb 171{
b8e97684 172SPF_dns_server_t * dc;
73ec116f 173int debug = 0;
b8e97684 174
73ec116f 175DEBUG(D_receive) debug = 1;
b8e97684
JH
176
177/* We insert our own DNS access layer rather than letting the spf library
178do it, so that our dns access path is used for debug tracing and for the
179testsuite. */
8e669ac1 180
b8e97684
JH
181if (!(dc = SPF_dns_exim_new(debug)))
182 {
183 DEBUG(D_receive) debug_printf("spf: SPF_dns_exim_new() failed\n");
184 return FALSE;
185 }
186if (!(dc = SPF_dns_cache_new(dc, NULL, debug, 8)))
187 {
188 DEBUG(D_receive) debug_printf("spf: SPF_dns_cache_new() failed\n");
189 return FALSE;
190 }
191if (!(spf_server = SPF_server_new_dns(dc, debug)))
eb52e2cb
JH
192 {
193 DEBUG(D_receive) debug_printf("spf: SPF_server_new() failed.\n");
194 return FALSE;
8523533c 195 }
05e4f4de
HSHR
196 /* Quick hack to override the outdated explanation URL.
197 See https://www.mail-archive.com/mailop@mailop.org/msg08019.html */
198 SPF_server_set_explanation(spf_server, "Please%_see%_http://www.open-spf.org/Why?id=%{S}&ip=%{C}&receiver=%{R}", &spf_response);
199 if (SPF_response_errcode(spf_response) != SPF_E_SUCCESS)
200 log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s", SPF_strerror(SPF_response_errcode(spf_response)));
201
73ec116f
JH
202return TRUE;
203}
204
205
206/* Set up a context that can be re-used for several
207 messages on the same SMTP connection (that come from the
208 same host with the same HELO string).
209
210Return: Boolean success
211*/
212
213BOOL
214spf_conn_init(uschar * spf_helo_domain, uschar * spf_remote_addr)
215{
216DEBUG(D_receive)
217 debug_printf("spf_conn_init: %s %s\n", spf_helo_domain, spf_remote_addr);
218
219if (!spf_server && !spf_init()) return FALSE;
8523533c 220
eb52e2cb
JH
221if (SPF_server_set_rec_dom(spf_server, CS primary_hostname))
222 {
223 DEBUG(D_receive) debug_printf("spf: SPF_server_set_rec_dom(\"%s\") failed.\n",
224 primary_hostname);
225 spf_server = NULL;
226 return FALSE;
7b3a77e5
TK
227 }
228
eb52e2cb
JH
229spf_request = SPF_request_new(spf_server);
230
231if ( SPF_request_set_ipv4_str(spf_request, CS spf_remote_addr)
232 && SPF_request_set_ipv6_str(spf_request, CS spf_remote_addr)
233 )
234 {
235 DEBUG(D_receive)
236 debug_printf("spf: SPF_request_set_ipv4_str() and "
237 "SPF_request_set_ipv6_str() failed [%s]\n", spf_remote_addr);
238 spf_server = NULL;
239 spf_request = NULL;
240 return FALSE;
8523533c
TK
241 }
242
eb52e2cb
JH
243if (SPF_request_set_helo_dom(spf_request, CS spf_helo_domain))
244 {
245 DEBUG(D_receive) debug_printf("spf: SPF_set_helo_dom(\"%s\") failed.\n",
246 spf_helo_domain);
247 spf_server = NULL;
248 spf_request = NULL;
249 return FALSE;
8523533c 250 }
8e669ac1 251
eb52e2cb 252return TRUE;
8523533c
TK
253}
254
255
256/* spf_process adds the envelope sender address to the existing
257 context (if any), retrieves the result, sets up expansion
eb52e2cb 258 strings and evaluates the condition outcome.
f9ba5e22 259
eb52e2cb
JH
260Return: OK/FAIL */
261
262int
263spf_process(const uschar **listptr, uschar *spf_envelope_sender, int action)
264{
265int sep = 0;
266const uschar *list = *listptr;
267uschar *spf_result_id;
268int rc = SPF_RESULT_PERMERROR;
269
b8e97684
JH
270DEBUG(D_receive) debug_printf("spf_process\n");
271
eb52e2cb
JH
272if (!(spf_server && spf_request))
273 /* no global context, assume temp error and skip to evaluation */
274 rc = SPF_RESULT_PERMERROR;
275
276else if (SPF_request_set_env_from(spf_request, CS spf_envelope_sender))
aded2255 277 /* Invalid sender address. This should be a real rare occurrence */
eb52e2cb
JH
278 rc = SPF_RESULT_PERMERROR;
279
280else
281 {
8523533c 282 /* get SPF result */
65a7d8c3 283 if (action == SPF_PROCESS_FALLBACK)
87e9d061 284 {
7156b1ef 285 SPF_request_query_fallback(spf_request, &spf_response, CS spf_guess);
87e9d061
JH
286 spf_result_guessed = TRUE;
287 }
65a7d8c3
NM
288 else
289 SPF_request_query_mailfrom(spf_request, &spf_response);
8523533c
TK
290
291 /* set up expansion items */
5903c6ff
JH
292 spf_header_comment = US SPF_response_get_header_comment(spf_response);
293 spf_received = US SPF_response_get_received_spf(spf_response);
294 spf_result = US SPF_strresult(SPF_response_result(spf_response));
295 spf_smtp_comment = US SPF_response_get_smtp_comment(spf_response);
8523533c 296
384152a6 297 rc = SPF_response_result(spf_response);
eb52e2cb
JH
298 }
299
300/* We got a result. Now see if we should return OK or FAIL for it */
301DEBUG(D_acl) debug_printf("SPF result is %s (%d)\n", SPF_strresult(rc), rc);
302
303if (action == SPF_PROCESS_GUESS && (!strcmp (SPF_strresult(rc), "none")))
304 return spf_process(listptr, spf_envelope_sender, SPF_PROCESS_FALLBACK);
305
306while ((spf_result_id = string_nextinlist(&list, &sep, NULL, 0)))
307 {
308 BOOL negate, result;
309
310 if ((negate = spf_result_id[0] == '!'))
311 spf_result_id++;
312
313 result = Ustrcmp(spf_result_id, spf_result_id_list[rc].name) == 0;
314 if (negate != result) return OK;
315 }
8523533c 316
eb52e2cb
JH
317/* no match */
318return FAIL;
8523533c
TK
319}
320
dfbcb5ac
JH
321
322
323gstring *
324authres_spf(gstring * g)
325{
87e9d061 326uschar * s;
dfbcb5ac
JH
327if (!spf_result) return g;
328
87e9d061
JH
329g = string_append(g, 2, US";\n\tspf=", spf_result);
330if (spf_result_guessed)
331 g = string_cat(g, US" (best guess record for domain)");
332
333s = expand_string(US"$sender_address_domain");
334return s && *s
335 ? string_append(g, 2, US" smtp.mailfrom=", s)
336 : string_cat(g, US" smtp.mailfrom=<>");
dfbcb5ac
JH
337}
338
339
8523533c 340#endif