DKIM: preferences for verify algorithms
[exim.git] / src / src / spf.c
CommitLineData
8523533c
TK
1/*************************************************
2* Exim - an Internet mail transport agent *
3*************************************************/
8e669ac1 4
8523533c 5/* Experimental SPF support.
5a66c31b 6 Copyright (c) Tom Kistner <tom@duncanthrax.net> 2004 - 2014
80fea873 7 License: GPL
f9ba5e22 8 Copyright (c) The Exim Maintainers 2015 - 2018
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
eb52e2cb 34
8523533c
TK
35/* spf_init sets up a context that can be re-used for several
36 messages on the same SMTP connection (that come from the
eb52e2cb 37 same host with the same HELO string)
8e669ac1 38
eb52e2cb 39Return: Boolean success */
8e669ac1 40
eb52e2cb
JH
41BOOL
42spf_init(uschar *spf_helo_domain, uschar *spf_remote_addr)
43{
44spf_server = SPF_server_new(SPF_DNS_CACHE, 0);
8e669ac1 45
eb52e2cb
JH
46if (!spf_server)
47 {
48 DEBUG(D_receive) debug_printf("spf: SPF_server_new() failed.\n");
49 return FALSE;
8523533c
TK
50 }
51
eb52e2cb
JH
52if (SPF_server_set_rec_dom(spf_server, CS primary_hostname))
53 {
54 DEBUG(D_receive) debug_printf("spf: SPF_server_set_rec_dom(\"%s\") failed.\n",
55 primary_hostname);
56 spf_server = NULL;
57 return FALSE;
7b3a77e5
TK
58 }
59
eb52e2cb
JH
60spf_request = SPF_request_new(spf_server);
61
62if ( SPF_request_set_ipv4_str(spf_request, CS spf_remote_addr)
63 && SPF_request_set_ipv6_str(spf_request, CS spf_remote_addr)
64 )
65 {
66 DEBUG(D_receive)
67 debug_printf("spf: SPF_request_set_ipv4_str() and "
68 "SPF_request_set_ipv6_str() failed [%s]\n", spf_remote_addr);
69 spf_server = NULL;
70 spf_request = NULL;
71 return FALSE;
8523533c
TK
72 }
73
eb52e2cb
JH
74if (SPF_request_set_helo_dom(spf_request, CS spf_helo_domain))
75 {
76 DEBUG(D_receive) debug_printf("spf: SPF_set_helo_dom(\"%s\") failed.\n",
77 spf_helo_domain);
78 spf_server = NULL;
79 spf_request = NULL;
80 return FALSE;
8523533c 81 }
8e669ac1 82
eb52e2cb 83return TRUE;
8523533c
TK
84}
85
86
87/* spf_process adds the envelope sender address to the existing
88 context (if any), retrieves the result, sets up expansion
eb52e2cb 89 strings and evaluates the condition outcome.
f9ba5e22 90
eb52e2cb
JH
91Return: OK/FAIL */
92
93int
94spf_process(const uschar **listptr, uschar *spf_envelope_sender, int action)
95{
96int sep = 0;
97const uschar *list = *listptr;
98uschar *spf_result_id;
99int rc = SPF_RESULT_PERMERROR;
100
101if (!(spf_server && spf_request))
102 /* no global context, assume temp error and skip to evaluation */
103 rc = SPF_RESULT_PERMERROR;
104
105else if (SPF_request_set_env_from(spf_request, CS spf_envelope_sender))
aded2255 106 /* Invalid sender address. This should be a real rare occurrence */
eb52e2cb
JH
107 rc = SPF_RESULT_PERMERROR;
108
109else
110 {
8523533c 111 /* get SPF result */
65a7d8c3 112 if (action == SPF_PROCESS_FALLBACK)
87e9d061 113 {
7156b1ef 114 SPF_request_query_fallback(spf_request, &spf_response, CS spf_guess);
87e9d061
JH
115 spf_result_guessed = TRUE;
116 }
65a7d8c3
NM
117 else
118 SPF_request_query_mailfrom(spf_request, &spf_response);
8523533c
TK
119
120 /* set up expansion items */
5903c6ff
JH
121 spf_header_comment = US SPF_response_get_header_comment(spf_response);
122 spf_received = US SPF_response_get_received_spf(spf_response);
123 spf_result = US SPF_strresult(SPF_response_result(spf_response));
124 spf_smtp_comment = US SPF_response_get_smtp_comment(spf_response);
8523533c 125
384152a6 126 rc = SPF_response_result(spf_response);
eb52e2cb
JH
127 }
128
129/* We got a result. Now see if we should return OK or FAIL for it */
130DEBUG(D_acl) debug_printf("SPF result is %s (%d)\n", SPF_strresult(rc), rc);
131
132if (action == SPF_PROCESS_GUESS && (!strcmp (SPF_strresult(rc), "none")))
133 return spf_process(listptr, spf_envelope_sender, SPF_PROCESS_FALLBACK);
134
135while ((spf_result_id = string_nextinlist(&list, &sep, NULL, 0)))
136 {
137 BOOL negate, result;
138
139 if ((negate = spf_result_id[0] == '!'))
140 spf_result_id++;
141
142 result = Ustrcmp(spf_result_id, spf_result_id_list[rc].name) == 0;
143 if (negate != result) return OK;
144 }
8523533c 145
eb52e2cb
JH
146/* no match */
147return FAIL;
8523533c
TK
148}
149
dfbcb5ac
JH
150
151
152gstring *
153authres_spf(gstring * g)
154{
87e9d061 155uschar * s;
dfbcb5ac
JH
156if (!spf_result) return g;
157
87e9d061
JH
158g = string_append(g, 2, US";\n\tspf=", spf_result);
159if (spf_result_guessed)
160 g = string_cat(g, US" (best guess record for domain)");
161
162s = expand_string(US"$sender_address_domain");
163return s && *s
164 ? string_append(g, 2, US" smtp.mailfrom=", s)
165 : string_cat(g, US" smtp.mailfrom=<>");
dfbcb5ac
JH
166}
167
168
8523533c 169#endif