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();
46 DEBUG(D_receive) debug_printf("SPF_dns_exim_lookup\n");
48 if (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))
51 if ( rr->type == rr_type
52 && Ustrncmp(rr->data+1, "v=spf1", 6) == 0)
58 .domain = CS rr->name, /* query information */
59 .domain_buf_len = DNS_MAXNAME,
62 .num_rr = 1, /* answer information */
68 .herrno = NETDB_SUCCESS,
70 .hook = NULL, /* misc information */
71 .source = spf_dns_server
74 for (int off = 0; off < rr->size; off += chunk_len)
76 chunk_len = (rr->data)[off++];
77 g = string_catn(g, US ((rr->data)+off), chunk_len);
81 HDEBUG(D_host_lookup) debug_printf("IP address lookup yielded an "
82 "empty name: treated as non-existent host name\n");
85 gstring_release_unused(g);
86 s = string_copy_malloc(string_from_gstring(g));
89 /* spfrr->rr must have been malloc()d for this */
90 SPF_dns_rr_dup(&spfrr, &srr);
95 SPF_dns_rr_dup(&spfrr, spf_nxdomain);
102 SPF_dns_exim_new(int debug)
104 SPF_dns_server_t *spf_dns_server;
106 DEBUG(D_receive) debug_printf("SPF_dns_exim_new\n");
108 if (!(spf_dns_server = malloc(sizeof(SPF_dns_server_t))))
110 memset(spf_dns_server, 0, sizeof(SPF_dns_server_t));
112 spf_dns_server->destroy = NULL;
113 spf_dns_server->lookup = SPF_dns_exim_lookup;
114 spf_dns_server->get_spf = NULL;
115 spf_dns_server->get_exp = NULL;
116 spf_dns_server->add_cache = NULL;
117 spf_dns_server->layer_below = NULL;
118 spf_dns_server->name = "exim";
119 spf_dns_server->debug = debug;
121 /* XXX This might have to return NO_DATA sometimes. */
123 spf_nxdomain = SPF_dns_rr_new_init(spf_dns_server,
124 "", ns_t_any, 24 * 60 * 60, HOST_NOT_FOUND);
127 free(spf_dns_server);
131 return spf_dns_server;
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
138 same host with the same HELO string).
139 XXX the spf_server layer could usefully be separately init'd
140 given that it sets up a dns cache.
142 Return: Boolean success */
145 spf_init(uschar *spf_helo_domain, uschar *spf_remote_addr)
148 SPF_dns_server_t * dc;
152 debug_printf("spf_init: %s %s\n", spf_helo_domain, spf_remote_addr);
156 /* We insert our own DNS access layer rather than letting the spf library
157 do it, so that our dns access path is used for debug tracing and for the
160 if (!(dc = SPF_dns_exim_new(debug)))
162 DEBUG(D_receive) debug_printf("spf: SPF_dns_exim_new() failed\n");
165 if (!(dc = SPF_dns_cache_new(dc, NULL, debug, 8)))
167 DEBUG(D_receive) debug_printf("spf: SPF_dns_cache_new() failed\n");
170 if (!(spf_server = SPF_server_new_dns(dc, debug)))
172 DEBUG(D_receive) debug_printf("spf: SPF_server_new() failed.\n");
176 if (SPF_server_set_rec_dom(spf_server, CS primary_hostname))
178 DEBUG(D_receive) debug_printf("spf: SPF_server_set_rec_dom(\"%s\") failed.\n",
184 spf_request = SPF_request_new(spf_server);
186 if ( SPF_request_set_ipv4_str(spf_request, CS spf_remote_addr)
187 && SPF_request_set_ipv6_str(spf_request, CS spf_remote_addr)
191 debug_printf("spf: SPF_request_set_ipv4_str() and "
192 "SPF_request_set_ipv6_str() failed [%s]\n", spf_remote_addr);
198 if (SPF_request_set_helo_dom(spf_request, CS spf_helo_domain))
200 DEBUG(D_receive) debug_printf("spf: SPF_set_helo_dom(\"%s\") failed.\n",
211 /* spf_process adds the envelope sender address to the existing
212 context (if any), retrieves the result, sets up expansion
213 strings and evaluates the condition outcome.
218 spf_process(const uschar **listptr, uschar *spf_envelope_sender, int action)
221 const uschar *list = *listptr;
222 uschar *spf_result_id;
223 int rc = SPF_RESULT_PERMERROR;
225 DEBUG(D_receive) debug_printf("spf_process\n");
227 if (!(spf_server && spf_request))
228 /* no global context, assume temp error and skip to evaluation */
229 rc = SPF_RESULT_PERMERROR;
231 else if (SPF_request_set_env_from(spf_request, CS spf_envelope_sender))
232 /* Invalid sender address. This should be a real rare occurrence */
233 rc = SPF_RESULT_PERMERROR;
238 if (action == SPF_PROCESS_FALLBACK)
240 SPF_request_query_fallback(spf_request, &spf_response, CS spf_guess);
241 spf_result_guessed = TRUE;
244 SPF_request_query_mailfrom(spf_request, &spf_response);
246 /* set up expansion items */
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);
252 rc = SPF_response_result(spf_response);
255 /* We got a result. Now see if we should return OK or FAIL for it */
256 DEBUG(D_acl) debug_printf("SPF result is %s (%d)\n", SPF_strresult(rc), rc);
258 if (action == SPF_PROCESS_GUESS && (!strcmp (SPF_strresult(rc), "none")))
259 return spf_process(listptr, spf_envelope_sender, SPF_PROCESS_FALLBACK);
261 while ((spf_result_id = string_nextinlist(&list, &sep, NULL, 0)))
265 if ((negate = spf_result_id[0] == '!'))
268 result = Ustrcmp(spf_result_id, spf_result_id_list[rc].name) == 0;
269 if (negate != result) return OK;
279 authres_spf(gstring * g)
282 if (!spf_result) return g;
284 g = string_append(g, 2, US";\n\tspf=", spf_result);
285 if (spf_result_guessed)
286 g = string_cat(g, US" (best guess record for domain)");
288 s = expand_string(US"$sender_address_domain");
290 ? string_append(g, 2, US" smtp.mailfrom=", s)
291 : string_cat(g, US" smtp.mailfrom=<>");