1 /*
2  * This program is free software; you can redistribute it and/or modify
3  * it under the terms of either:
4  *
5  *   a) The GNU Lesser General Public License as published by the Free
6  *      Software Foundation; either version 2.1, or (at your option) any
7  *      later version,
8  *
9  *   OR
10  *
11  *   b) The two-clause BSD license.
12  *
13  * These licenses can be found with the distribution in the file LICENSES
14  */
15 
16 #include "spf_sys_config.h"
17 
18 #ifdef STDC_HEADERS
19 # include <stdio.h>        /* stdin / stdout */
20 # include <stdlib.h>       /* malloc / free */
21 #endif
22 
23 #ifdef HAVE_STRING_H
24 # include <string.h>       /* strstr / strdup */
25 #else
26 # ifdef HAVE_STRINGS_H
27 #  include <strings.h>       /* strstr / strdup */
28 # endif
29 #endif
30 
31 
32 #include "spf.h"
33 #include "spf_dns.h"
34 #include "spf_request.h"
35 #include "spf_internal.h"
36 
37 #define SPF_FREE(x) \
38 		do { if (x) free(x); (x) = NULL; } while(0)
39 
40 SPF_request_t *
SPF_request_new(SPF_server_t * spf_server)41 SPF_request_new(SPF_server_t *spf_server)
42 {
43 	SPF_request_t	*sr;
44 
45 	sr = (SPF_request_t *)malloc(sizeof(SPF_request_t));
46 	if (! sr)
47 		return sr;
48 	memset(sr, 0, sizeof(SPF_request_t));
49 
50 	sr->spf_server = spf_server;
51 	sr->client_ver = AF_UNSPEC;
52 	sr->ipv4.s_addr = htonl(INADDR_ANY);
53 	sr->ipv6 = in6addr_any;
54 
55 	return sr;
56 }
57 
58 void
SPF_request_free(SPF_request_t * sr)59 SPF_request_free(SPF_request_t *sr)
60 {
61 	SPF_ASSERT_NOTNULL(sr);
62 	SPF_FREE(sr->client_dom);
63 	SPF_FREE(sr->helo_dom);
64 	SPF_FREE(sr->env_from);
65 	SPF_FREE(sr->env_from_lp);
66 	SPF_FREE(sr->env_from_dp);
67 	free(sr);
68 }
69 
70 SPF_errcode_t
SPF_request_set_ipv4(SPF_request_t * sr,struct in_addr addr)71 SPF_request_set_ipv4(SPF_request_t *sr, struct in_addr addr)
72 {
73 	if (sr->client_dom) {
74 		free(sr->client_dom);
75 		sr->client_dom = NULL;
76 	}
77 	sr->client_ver = AF_INET;
78 	sr->ipv4 = addr;
79 	return SPF_E_SUCCESS;
80 }
81 
82 SPF_errcode_t
SPF_request_set_ipv6(SPF_request_t * sr,struct in6_addr addr)83 SPF_request_set_ipv6(SPF_request_t *sr, struct in6_addr addr)
84 {
85 	if (sr->client_dom) {
86 		free(sr->client_dom);
87 		sr->client_dom = NULL;
88 	}
89 	sr->client_ver = AF_INET6;
90 	sr->ipv6 = addr;
91 	return SPF_E_SUCCESS;
92 }
93 
94 SPF_errcode_t
SPF_request_set_ipv4_str(SPF_request_t * sr,const char * astr)95 SPF_request_set_ipv4_str(SPF_request_t *sr, const char *astr)
96 {
97 	struct in_addr	addr;
98 	if (astr == NULL)
99 		astr = "0.0.0.0";
100 	if (inet_pton(AF_INET, astr, &addr) <= 0)
101 		return SPF_E_INVALID_IP4;
102 	return SPF_request_set_ipv4(sr, addr);
103 }
104 
105 SPF_errcode_t
SPF_request_set_ipv6_str(SPF_request_t * sr,const char * astr)106 SPF_request_set_ipv6_str(SPF_request_t *sr, const char *astr)
107 {
108 	struct in6_addr	addr;
109 	if (astr == NULL)
110 		astr = "::";
111 	if (inet_pton(AF_INET6, astr, &addr) <= 0)
112 		return SPF_E_INVALID_IP6;
113 	return SPF_request_set_ipv6(sr, addr);
114 }
115 
116 SPF_errcode_t
SPF_request_set_helo_dom(SPF_request_t * sr,const char * dom)117 SPF_request_set_helo_dom(SPF_request_t *sr, const char *dom)
118 {
119 	SPF_ASSERT_NOTNULL(dom);
120 	SPF_FREE(sr->helo_dom);
121 	sr->helo_dom = strdup(dom);
122 	if (! sr->helo_dom)
123 		return SPF_E_NO_MEMORY;
124 	/* set cur_dom and env_from? */
125 	if (sr->env_from == NULL)
126 		return SPF_request_set_env_from(sr, dom);
127 	return SPF_E_SUCCESS;
128 }
129 
130 const char *
SPF_request_get_rec_dom(SPF_request_t * sr)131 SPF_request_get_rec_dom(SPF_request_t *sr)
132 {
133 	SPF_server_t	*spf_server;
134 	spf_server = sr->spf_server;
135 	return spf_server->rec_dom;
136 }
137 
138 int
SPF_request_set_env_from(SPF_request_t * sr,const char * from)139 SPF_request_set_env_from(SPF_request_t *sr, const char *from)
140 {
141 	char	*cp;
142 	size_t	 len;
143 
144 	SPF_ASSERT_NOTNULL(from);
145 	SPF_FREE(sr->env_from);
146 	SPF_FREE(sr->env_from_lp);
147 	SPF_FREE(sr->env_from_dp);
148 
149 	if (*from == '\0' && sr->helo_dom != NULL)
150 		from = sr->helo_dom;
151 	cp = strrchr(from, '@');
152 	if (cp && (cp != from)) {
153 		sr->env_from = strdup(from);
154 		if (! sr->env_from)
155 			return SPF_E_NO_MEMORY;
156 
157 		len = cp - from;
158 		sr->env_from_lp = malloc(len + 1);
159 		if (!sr->env_from_lp) {
160 			SPF_FREE(sr->env_from);
161 			return SPF_E_NO_MEMORY;
162 		}
163 		strncpy(sr->env_from_lp, from, len);
164 		sr->env_from_lp[len] = '\0';
165 		sr->env_from_dp = strdup(cp + 1);
166 		if (!sr->env_from_dp) {
167 			SPF_FREE(sr->env_from);
168 			SPF_FREE(sr->env_from_lp);
169 			return SPF_E_NO_MEMORY;
170 		}
171 	}
172 	else {
173 		if (cp == from) from++; /* "@domain.example" */
174 		len = sizeof("postmaster@") + strlen(from);
175 		sr->env_from = malloc(len + 1);	/* sizeof("") == 1? */
176 		if (! sr->env_from)
177 			return SPF_E_NO_MEMORY;
178 		sprintf(sr->env_from, "postmaster@%s", from);
179 		sr->env_from_lp = strdup("postmaster");
180 		if (!sr->env_from_lp) {
181 			SPF_FREE(sr->env_from);
182 			return SPF_E_NO_MEMORY;
183 		}
184 		sr->env_from_dp = strdup(from);
185 		if (!sr->env_from_dp) {
186 			SPF_FREE(sr->env_from);
187 			SPF_FREE(sr->env_from_lp);
188 			return SPF_E_NO_MEMORY;
189 		}
190 	}
191 
192 	return 0;	// SPF_E_SUCCESS
193 }
194 
195 const char *
SPF_request_get_client_dom(SPF_request_t * sr)196 SPF_request_get_client_dom(SPF_request_t *sr)
197 {
198 	SPF_server_t	*spf_server;
199 
200 	SPF_ASSERT_NOTNULL(sr);
201 	spf_server = sr->spf_server;
202 	SPF_ASSERT_NOTNULL(spf_server);
203 
204 	if (sr->client_dom == NULL) {
205 		sr->client_dom = SPF_dns_get_client_dom(spf_server->resolver,
206 						sr);
207 	}
208 	return sr->client_dom;
209 }
210 
211 int
SPF_request_is_loopback(SPF_request_t * sr)212 SPF_request_is_loopback(SPF_request_t *sr)
213 {
214     if (sr->client_ver == AF_INET) {
215 		if ((ntohl(sr->ipv4.s_addr) & IN_CLASSA_NET) ==
216 						(IN_LOOPBACKNET << 24)) {
217 			return TRUE;
218 		}
219     }
220     else if (sr->client_ver == AF_INET6) {
221 		if (IN6_IS_ADDR_LOOPBACK(&sr->ipv6))
222 			return TRUE;
223     }
224     return FALSE;
225 }
226 
227 static SPF_errcode_t
SPF_request_prepare(SPF_request_t * sr)228 SPF_request_prepare(SPF_request_t *sr)
229 {
230 	if (sr->use_helo)
231 		sr->cur_dom = sr->helo_dom;
232 	else
233 		sr->cur_dom = sr->env_from_dp;
234 	return SPF_E_SUCCESS;
235 }
236 
237 /**
238  * The common tail-end of a few methods below.
239  */
240 static SPF_errcode_t
SPF_request_query_record(SPF_request_t * spf_request,SPF_response_t * spf_response,SPF_record_t * spf_record,SPF_errcode_t err)241 SPF_request_query_record(SPF_request_t *spf_request,
242 				SPF_response_t *spf_response,
243 				SPF_record_t *spf_record,
244 				SPF_errcode_t err)
245 {
246 	if (err != SPF_E_SUCCESS) {
247 		if (spf_record)
248 			SPF_record_free(spf_record);
249 		return err;
250 	}
251 	/* Now, in theory, SPF_response_errors(spf_response) == 0 */
252 	if (SPF_response_errors(spf_response) > 0)
253 		SPF_infof("Warning: %d errors in response, "
254 						"but no error code. Evaluating.",
255 						SPF_response_errors(spf_response));
256 	/* If we get here, spf_record better not be NULL */
257 	spf_response->spf_record_exp = spf_record;
258 	err = SPF_record_interpret(spf_record,
259 					spf_request, spf_response, 0);
260 	SPF_record_free(spf_record);
261 
262 	return err;
263 }
264 
265 /**
266  * The big entry point.
267  */
268 SPF_errcode_t
SPF_request_query_mailfrom(SPF_request_t * spf_request,SPF_response_t ** spf_responsep)269 SPF_request_query_mailfrom(SPF_request_t *spf_request,
270 				SPF_response_t **spf_responsep)
271 {
272 	SPF_server_t	*spf_server;
273 	SPF_record_t	*spf_record;
274 	SPF_errcode_t	 err;
275 
276 	SPF_ASSERT_NOTNULL(spf_request);
277 	spf_server = spf_request->spf_server;
278 	SPF_ASSERT_NOTNULL(spf_server);
279 
280 	*spf_responsep = SPF_response_new(spf_request);
281 	if (! *spf_responsep)
282 		return SPF_E_NO_MEMORY;
283 
284 	/* Give localhost a free ride */
285 	if (SPF_request_is_loopback(spf_request))
286 		return SPF_i_done(*spf_responsep, SPF_RESULT_PASS,
287 						SPF_REASON_LOCALHOST, SPF_E_SUCCESS);
288 
289 	SPF_request_prepare(spf_request);
290 
291 	err = SPF_server_get_record(spf_server, spf_request,
292 					*spf_responsep, &spf_record);
293 	return SPF_request_query_record(spf_request, *spf_responsep,
294 					spf_record, err);
295 }
296 
297 /* This interface isn't finalised. */
298 SPF_errcode_t
SPF_request_query_fallback(SPF_request_t * spf_request,SPF_response_t ** spf_responsep,const char * record)299 SPF_request_query_fallback(SPF_request_t *spf_request,
300 				SPF_response_t **spf_responsep,
301 				const char *record)
302 {
303 	SPF_server_t	*spf_server;
304 	SPF_record_t	*spf_record;
305 	SPF_errcode_t	 err;
306 
307 	SPF_ASSERT_NOTNULL(spf_request);
308 	spf_server = spf_request->spf_server;
309 	SPF_ASSERT_NOTNULL(spf_server);
310 
311 	*spf_responsep = SPF_response_new(spf_request);
312 	if (! *spf_responsep)
313 		return SPF_E_NO_MEMORY;
314 
315 	/* Give localhost a free ride */
316 	if (SPF_request_is_loopback(spf_request))
317 		return SPF_i_done(*spf_responsep, SPF_RESULT_PASS,
318 						SPF_REASON_LOCALHOST, SPF_E_SUCCESS);
319 
320 	SPF_request_prepare(spf_request);
321 
322 	err = SPF_record_compile(spf_server,
323 					*spf_responsep, &spf_record,
324 					record);
325 	return SPF_request_query_record(spf_request, *spf_responsep,
326 					spf_record, err);
327 }
328 
329 /**
330  * This replaces _2mx
331  *
332  * build record as SPF_VER_STR " mx:%s"
333  * Set cur_dom to the rcpt_to domain.
334  * Query on the 'fixed' 2mx record.
335  * Clobber the primary result.
336  */
337 /* FIXME: Check the implementation of this. */
338 SPF_errcode_t
SPF_request_query_rcptto(SPF_request_t * spf_request,SPF_response_t ** spf_responsep,const char * rcpt_to)339 SPF_request_query_rcptto(SPF_request_t *spf_request,
340 				SPF_response_t **spf_responsep,
341 				const char *rcpt_to)
342 {
343 	SPF_server_t	*spf_server;
344 	SPF_record_t	*spf_record;
345 	SPF_errcode_t	 err;
346 	const char		*rcpt_to_dom;
347 	char			*record;
348 	size_t			 len;
349 
350 	if ( (spf_request == NULL) || (rcpt_to == NULL) )
351 		return SPF_E_INVALID_OPT;
352 
353 	SPF_ASSERT_NOTNULL(spf_request);
354 	spf_server = spf_request->spf_server;
355 	SPF_ASSERT_NOTNULL(spf_server);
356 
357 	*spf_responsep = SPF_response_new(spf_request);
358 	if (! *spf_responsep)
359 		return SPF_E_NO_MEMORY;
360 
361 	/* Give localhost a free ride */
362 	if (SPF_request_is_loopback(spf_request))
363 		return SPF_i_done(*spf_responsep, SPF_RESULT_PASS,
364 						SPF_REASON_LOCALHOST, SPF_E_SUCCESS);
365 
366 	rcpt_to_dom = strchr(rcpt_to, '@');
367 	if (rcpt_to_dom == NULL)
368 		rcpt_to_dom = rcpt_to;
369 	spf_request->cur_dom = rcpt_to_dom;
370 
371 	len = sizeof(SPF_VER_STR) + 64 + strlen(rcpt_to_dom);
372 	record = malloc(len);
373 	if (! record)
374 		return SPF_E_NO_MEMORY;
375 	snprintf(record, len, SPF_VER_STR " mx:%s", rcpt_to_dom);
376 	err = SPF_record_compile(spf_server,
377 					*spf_responsep, &spf_record,
378 					record);
379 	free(record);
380 	return SPF_request_query_record(spf_request, *spf_responsep,
381 					spf_record, err);
382 }
383