Expansions: new ${authresults {mch}} for an Authentication-Results header
[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 },
25 { US"err_temp", 6 }, /* Deprecated Apr 2014 */
26 { US"err_perm", 7 }, /* Deprecated Apr 2014 */
27 { US"temperror", 6 }, /* RFC 4408 defined */
28 { US"permerror", 7 } /* RFC 4408 defined */
6d06cf48
TK
29};
30
384152a6
TK
31SPF_server_t *spf_server = NULL;
32SPF_request_t *spf_request = NULL;
33SPF_response_t *spf_response = NULL;
34SPF_response_t *spf_response_2mx = NULL;
8523533c 35
eb52e2cb 36
8523533c
TK
37/* spf_init sets up a context that can be re-used for several
38 messages on the same SMTP connection (that come from the
eb52e2cb 39 same host with the same HELO string)
8e669ac1 40
eb52e2cb 41Return: Boolean success */
8e669ac1 42
eb52e2cb
JH
43BOOL
44spf_init(uschar *spf_helo_domain, uschar *spf_remote_addr)
45{
46spf_server = SPF_server_new(SPF_DNS_CACHE, 0);
8e669ac1 47
eb52e2cb
JH
48if (!spf_server)
49 {
50 DEBUG(D_receive) debug_printf("spf: SPF_server_new() failed.\n");
51 return FALSE;
8523533c
TK
52 }
53
eb52e2cb
JH
54if (SPF_server_set_rec_dom(spf_server, CS primary_hostname))
55 {
56 DEBUG(D_receive) debug_printf("spf: SPF_server_set_rec_dom(\"%s\") failed.\n",
57 primary_hostname);
58 spf_server = NULL;
59 return FALSE;
7b3a77e5
TK
60 }
61
eb52e2cb
JH
62spf_request = SPF_request_new(spf_server);
63
64if ( SPF_request_set_ipv4_str(spf_request, CS spf_remote_addr)
65 && SPF_request_set_ipv6_str(spf_request, CS spf_remote_addr)
66 )
67 {
68 DEBUG(D_receive)
69 debug_printf("spf: SPF_request_set_ipv4_str() and "
70 "SPF_request_set_ipv6_str() failed [%s]\n", spf_remote_addr);
71 spf_server = NULL;
72 spf_request = NULL;
73 return FALSE;
8523533c
TK
74 }
75
eb52e2cb
JH
76if (SPF_request_set_helo_dom(spf_request, CS spf_helo_domain))
77 {
78 DEBUG(D_receive) debug_printf("spf: SPF_set_helo_dom(\"%s\") failed.\n",
79 spf_helo_domain);
80 spf_server = NULL;
81 spf_request = NULL;
82 return FALSE;
8523533c 83 }
8e669ac1 84
eb52e2cb 85return TRUE;
8523533c
TK
86}
87
88
89/* spf_process adds the envelope sender address to the existing
90 context (if any), retrieves the result, sets up expansion
eb52e2cb 91 strings and evaluates the condition outcome.
f9ba5e22 92
eb52e2cb
JH
93Return: OK/FAIL */
94
95int
96spf_process(const uschar **listptr, uschar *spf_envelope_sender, int action)
97{
98int sep = 0;
99const uschar *list = *listptr;
100uschar *spf_result_id;
101int rc = SPF_RESULT_PERMERROR;
102
103if (!(spf_server && spf_request))
104 /* no global context, assume temp error and skip to evaluation */
105 rc = SPF_RESULT_PERMERROR;
106
107else if (SPF_request_set_env_from(spf_request, CS spf_envelope_sender))
108 /* Invalid sender address. This should be a real rare occurence */
109 rc = SPF_RESULT_PERMERROR;
110
111else
112 {
8523533c 113 /* get SPF result */
65a7d8c3 114 if (action == SPF_PROCESS_FALLBACK)
7156b1ef 115 SPF_request_query_fallback(spf_request, &spf_response, CS spf_guess);
65a7d8c3
NM
116 else
117 SPF_request_query_mailfrom(spf_request, &spf_response);
8523533c
TK
118
119 /* set up expansion items */
5903c6ff
JH
120 spf_header_comment = US SPF_response_get_header_comment(spf_response);
121 spf_received = US SPF_response_get_received_spf(spf_response);
122 spf_result = US SPF_strresult(SPF_response_result(spf_response));
123 spf_smtp_comment = US SPF_response_get_smtp_comment(spf_response);
8523533c 124
384152a6 125 rc = SPF_response_result(spf_response);
eb52e2cb
JH
126 }
127
128/* We got a result. Now see if we should return OK or FAIL for it */
129DEBUG(D_acl) debug_printf("SPF result is %s (%d)\n", SPF_strresult(rc), rc);
130
131if (action == SPF_PROCESS_GUESS && (!strcmp (SPF_strresult(rc), "none")))
132 return spf_process(listptr, spf_envelope_sender, SPF_PROCESS_FALLBACK);
133
134while ((spf_result_id = string_nextinlist(&list, &sep, NULL, 0)))
135 {
136 BOOL negate, result;
137
138 if ((negate = spf_result_id[0] == '!'))
139 spf_result_id++;
140
141 result = Ustrcmp(spf_result_id, spf_result_id_list[rc].name) == 0;
142 if (negate != result) return OK;
143 }
8523533c 144
eb52e2cb
JH
145/* no match */
146return FAIL;
8523533c
TK
147}
148
dfbcb5ac
JH
149
150
151gstring *
152authres_spf(gstring * g)
153{
154if (!spf_result) return g;
155
156return string_append(g, 4, US";\\n\\tspf=", spf_result,
157 US" smtp.mailfrom=", expand_string(US"$sender_address_domain"));
158}
159
160
8523533c 161#endif