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 
19 #ifdef STDC_HEADERS
20 # include <stdio.h>        /* stdin / stdout */
21 # include <stdlib.h>       /* malloc / free */
22 # include <ctype.h>        /* isupper / tolower */
23 #endif
24 
25 #ifdef HAVE_INTTYPES_H
26 #include <inttypes.h>
27 #endif
28 
29 #ifdef HAVE_NETDB_H
30 #include <netdb.h>
31 #endif
32 
33 #ifdef HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36 
37 #ifdef HAVE_STRING_H
38 # include <string.h>       /* strstr / strdup */
39 #else
40 # ifdef HAVE_STRINGS_H
41 #  include <strings.h>       /* strstr / strdup */
42 # endif
43 #endif
44 
45 #ifdef HAVE_NETDB_H
46 # include <netdb.h>
47 #endif
48 
49 #ifndef HOST_NAME_MAX
50 #define HOST_NAME_MAX 255
51 #endif
52 
53 
54 #include "spf.h"
55 #include "spf_response.h"
56 #include "spf_record.h"
57 #include "spf_server.h"
58 #include "spf_dns.h"
59 #include "spf_dns_resolv.h"
60 #include "spf_dns_cache.h"
61 #include "spf_dns_zone.h"
62 #include "spf_internal.h"
63 #include "spf_dns_internal.h"
64 
65 
66 __attribute__((warn_unused_result))
67 static SPF_errcode_t
SPF_server_set_rec_dom_ghbn(SPF_server_t * sp)68 SPF_server_set_rec_dom_ghbn(SPF_server_t *sp)
69 {
70 	sp->rec_dom = malloc(HOST_NAME_MAX);
71 	if (! sp->rec_dom)
72 		return SPF_E_NO_MEMORY;
73 #ifdef _WIN32
74 	gethostnameFQDN(sp->rec_dom, HOST_NAME_MAX);
75 	return 0;	/* XXX FIXME? */
76 #else
77 	if (gethostname(sp->rec_dom, HOST_NAME_MAX) < 0)
78 		/* XXX Error using strerror. */
79 		return SPF_E_INTERNAL_ERROR;
80 #endif
81 	return SPF_E_SUCCESS;
82 }
83 
84 static void
SPF_server_new_common_pre(SPF_server_t * sp,int debug)85 SPF_server_new_common_pre(SPF_server_t *sp, int debug)
86 {
87 	SPF_errcode_t		 err;
88 
89 	memset(sp, 0, sizeof(SPF_server_t));
90 
91 	sp->max_dns_mech = SPF_MAX_DNS_MECH;
92 	sp->max_dns_ptr = SPF_MAX_DNS_PTR;
93 	sp->max_dns_mx = SPF_MAX_DNS_MX;
94 	sp->debug = debug;
95 
96 	err = SPF_server_set_rec_dom_ghbn(sp);
97 	if (err != SPF_E_SUCCESS)
98 		SPF_error("Failed to set rec_dom using gethostname()");
99 }
100 
101 static void
SPF_server_new_common_post(SPF_server_t * sp)102 SPF_server_new_common_post(SPF_server_t *sp)
103 {
104 	SPF_response_t		*spf_response;
105 	SPF_errcode_t		 err;
106 
107 	spf_response = NULL;
108 	err = SPF_server_set_explanation(sp, SPF_DEFAULT_EXP,
109 					&spf_response);
110 	if (err != SPF_E_SUCCESS)
111 		SPF_errorf("Error code %d compiling default explanation", err);
112 	if (spf_response) {
113 		/* XXX Print the errors?! */
114 		if (SPF_response_messages(spf_response) > 0)
115 			SPF_error("Response errors compiling default explanation");
116 		SPF_response_free(spf_response);
117 	}
118 
119 	spf_response = NULL;
120 	err = SPF_server_set_localpolicy(sp, "", 0, &spf_response);
121 	if (err != SPF_E_SUCCESS)
122 		SPF_errorf("Error code %d compiling default whitelist", err);
123 	if (spf_response) {
124 		/* XXX Print the errors?! */
125 		if (SPF_response_messages(spf_response) > 0)
126 			SPF_error("Response errors compiling default whitelist");
127 		SPF_response_free(spf_response);
128 	}
129 }
130 
131 SPF_server_t *
SPF_server_new(SPF_server_dnstype_t dnstype,int debug)132 SPF_server_new(SPF_server_dnstype_t dnstype, int debug)
133 {
134 	SPF_dns_server_t	*dc_r;
135 	SPF_dns_server_t	*dc_c;
136 	SPF_dns_server_t	*dc_z;
137 	SPF_server_t		*sp;
138 
139 	sp = (SPF_server_t *)malloc(sizeof(SPF_server_t));
140 	if (! sp)
141 		return sp;
142 	SPF_server_new_common_pre(sp, debug);
143 	sp->destroy_resolver = 1;
144 
145 	switch (dnstype) {
146 		case SPF_DNS_RESOLV:
147 			dc_r = SPF_dns_resolv_new(NULL, NULL, debug);
148 			if (dc_r == NULL)
149 				SPF_error("Failed to create DNS resolver");
150 			sp->resolver = dc_r;
151 			break;
152 
153 		case SPF_DNS_CACHE:
154 			dc_r = SPF_dns_resolv_new(NULL, NULL, debug);
155 			if (dc_r == NULL)
156 				SPF_error("Failed to create DNS resolver");
157 			dc_c = SPF_dns_cache_new(dc_r, NULL, debug, 8);
158 			if (dc_c == NULL)
159 				SPF_error("Failed to create DNS cache");
160 			sp->resolver = dc_c;
161 			break;
162 
163 		case SPF_DNS_ZONE:
164 			dc_z = SPF_dns_zone_new(NULL, NULL, debug);
165 			if (dc_z == NULL)
166 				SPF_error("Failed to create DNS zone");
167 			sp->resolver = dc_z;
168 			break;
169 
170 		default:
171 			SPF_errorf("Unknown DNS type %d", dnstype);
172 	}
173 
174 	SPF_server_new_common_post(sp);
175 
176 	return sp;
177 }
178 
179 SPF_server_t *
SPF_server_new_dns(SPF_dns_server_t * dns,int debug)180 SPF_server_new_dns(SPF_dns_server_t *dns, int debug)
181 {
182 	SPF_server_t	*sp;
183 
184 	sp = (SPF_server_t *)malloc(sizeof(SPF_server_t));
185 	if (! sp)
186 		return sp;
187 	SPF_server_new_common_pre(sp, debug);
188 	sp->destroy_resolver = 0;
189 	sp->resolver = dns;
190 	SPF_server_new_common_post(sp);
191 	return sp;
192 }
193 
194 /**
195  * This function destroys the DNS layer as well.
196  * If the (custom) DNS layer has no destructor,
197  * then this cannot and does not destroy it.
198  */
199 void
SPF_server_free(SPF_server_t * sp)200 SPF_server_free(SPF_server_t *sp)
201 {
202 	if (sp->resolver && sp->destroy_resolver)
203 		SPF_dns_free(sp->resolver);
204 	if (sp->local_policy)
205 		SPF_record_free(sp->local_policy);
206 	if (sp->explanation)
207 		SPF_macro_free(sp->explanation);
208 	if (sp->rec_dom)
209 		free(sp->rec_dom);
210 	/* XXX TODO: Free other parts of the structure. */
211 	free(sp);
212 }
213 
214 SPF_errcode_t
SPF_server_set_rec_dom(SPF_server_t * sp,const char * dom)215 SPF_server_set_rec_dom(SPF_server_t *sp, const char *dom)
216 {
217 	if (sp->rec_dom)
218 		free(sp->rec_dom);
219 	if (dom == NULL)
220 		return SPF_server_set_rec_dom_ghbn(sp);
221 	sp->rec_dom = strdup(dom);
222 	if (! sp->rec_dom)
223 		return SPF_E_NO_MEMORY;
224 	return SPF_E_SUCCESS;
225 }
226 
227 SPF_errcode_t
SPF_server_set_sanitize(SPF_server_t * sp,int sanitize)228 SPF_server_set_sanitize(SPF_server_t *sp, int sanitize)
229 {
230 	sp->sanitize = sanitize;
231 	return SPF_E_SUCCESS;
232 }
233 
234 SPF_errcode_t
SPF_server_set_explanation(SPF_server_t * sp,const char * exp,SPF_response_t ** spf_responsep)235 SPF_server_set_explanation(SPF_server_t *sp, const char *exp,
236 				SPF_response_t **spf_responsep)
237 {
238 	SPF_macro_t		*spf_macro = NULL;
239 	SPF_errcode_t	 err;
240 
241 	SPF_ASSERT_NOTNULL(exp);
242 
243 	/* This is a hackish way to get the errors. */
244 	if (! *spf_responsep) {
245 		*spf_responsep = SPF_response_new(NULL);
246 		if (! *spf_responsep)
247 			return SPF_E_NO_MEMORY;
248 	}
249 
250 	err = SPF_record_compile_macro(sp, *spf_responsep, &spf_macro, exp);
251 	if (err == SPF_E_SUCCESS) {
252 		if (sp->explanation)
253 			SPF_macro_free(sp->explanation);
254 		sp->explanation = spf_macro;
255 	}
256 	else {
257 		SPF_response_add_error(*spf_responsep, err,
258 				"Failed to compile explanation '%s'", exp);
259 		if (spf_macro)
260 			SPF_macro_free(spf_macro);
261 	}
262 
263 	return err;
264 }
265 
266 SPF_errcode_t
SPF_server_set_localpolicy(SPF_server_t * sp,const char * policy,int use_default_whitelist,SPF_response_t ** spf_responsep)267 SPF_server_set_localpolicy(SPF_server_t *sp, const char *policy,
268 				int use_default_whitelist,
269 				SPF_response_t **spf_responsep)
270 {
271 	SPF_record_t	*spf_record = NULL;
272 	SPF_errcode_t	 err;
273 	char			*record;
274 	size_t			 len;
275 
276 	SPF_ASSERT_NOTNULL(policy);
277 
278 	/* This is a hackish way to get the errors. */
279 	if (! *spf_responsep) {
280 		*spf_responsep = SPF_response_new(NULL);
281 		if (! *spf_responsep)
282 			return SPF_E_NO_MEMORY;
283 	}
284 
285 	len = sizeof(SPF_VER_STR) + strlen(policy) + 20;
286 	if (use_default_whitelist)
287 		len += sizeof(SPF_DEFAULT_WHITELIST);
288 	record = malloc(len);
289 	if (! record)
290 		return SPF_E_NO_MEMORY;
291 	if (use_default_whitelist)
292 		snprintf(record, len, "%s %s %s",
293 						SPF_VER_STR, policy, SPF_DEFAULT_WHITELIST);
294 	else
295 		snprintf(record, len, "%s %s", SPF_VER_STR, policy);
296 
297 	err = SPF_record_compile(sp, *spf_responsep, &spf_record, record);
298 	if (err == SPF_E_SUCCESS) {
299 		if (sp->local_policy)
300 			SPF_record_free(sp->local_policy);
301 		sp->local_policy = spf_record;
302 	}
303 	else {
304 		SPF_response_add_error(*spf_responsep, err,
305 				"Failed to compile local policy '%s'", policy);
306 		if (spf_record)
307 			SPF_record_free(spf_record);
308 	}
309 
310 	free(record);
311 
312 	return err;
313 }
314 
315 SPF_errcode_t
SPF_server_get_record(SPF_server_t * spf_server,SPF_request_t * spf_request,SPF_response_t * spf_response,SPF_record_t ** spf_recordp)316 SPF_server_get_record(SPF_server_t *spf_server,
317 				SPF_request_t *spf_request,
318 				SPF_response_t *spf_response,
319 				SPF_record_t **spf_recordp)
320 {
321 	SPF_dns_server_t		*resolver;
322 	SPF_dns_rr_t			*rr_txt;
323 	SPF_errcode_t			 err;
324 	SPF_dns_stat_t			 herrno;
325 	const char				*domain;
326 	ns_type					 rr_type;
327 	int						 num_found;
328 	int						 idx_found;
329 	int						 i;
330 
331 
332 	SPF_ASSERT_NOTNULL(spf_server);
333 	SPF_ASSERT_NOTNULL(spf_request);
334 	SPF_ASSERT_NOTNULL(spf_server->resolver);
335 	SPF_ASSERT_NOTNULL(spf_recordp);
336 
337 	domain = spf_request->cur_dom;
338 	SPF_ASSERT_NOTNULL(domain);
339 
340 	*spf_recordp = NULL;
341 
342 	resolver = spf_server->resolver;
343 
344 	if (resolver->get_spf)
345 		return resolver->get_spf(spf_server, spf_request,
346 						spf_response, spf_recordp);
347 
348 	/* I am VERY, VERY sorry about the gotos. Shevek. */
349 	rr_type = ns_t_spf;
350 retry:
351 	rr_txt = SPF_dns_lookup(resolver, domain, rr_type, TRUE);
352 
353 	switch (rr_txt->herrno) {
354 		case HOST_NOT_FOUND:
355 			if (spf_server->debug > 0)
356 				SPF_debugf("get_record(%s): HOST_NOT_FOUND", domain);
357 			SPF_dns_rr_free(rr_txt);
358 			if (rr_type == ns_t_spf) {
359 				rr_type = ns_t_txt;
360 				goto retry;
361 			}
362 			spf_response->result = SPF_RESULT_NONE;
363 			spf_response->reason = SPF_REASON_FAILURE;
364 			return SPF_response_add_error(spf_response, SPF_E_NOT_SPF,
365 					"Host '%s' not found.", domain);
366 			// break;
367 
368 		case NO_DATA:
369 			if (spf_server->debug > 0)
370 				SPF_debugf("get_record(%s): NO_DATA", domain);
371 			SPF_dns_rr_free(rr_txt);
372 			if (rr_type == ns_t_spf) {
373 				rr_type = ns_t_txt;
374 				goto retry;
375 			}
376 			spf_response->result = SPF_RESULT_NONE;
377 			spf_response->reason = SPF_REASON_FAILURE;
378 			return SPF_response_add_error(spf_response, SPF_E_NOT_SPF,
379 					"No DNS data for '%s'.", domain);
380 			// break;
381 
382 		case TRY_AGAIN:
383 			if (spf_server->debug > 0)
384 				SPF_debugf("get_record(%s): TRY_AGAIN", domain);
385 			SPF_dns_rr_free(rr_txt);
386 			return SPF_response_add_error(spf_response, SPF_E_DNS_ERROR,
387 					"Temporary DNS failure for '%s'.", domain);
388 			// break;
389 
390 		case NO_RECOVERY:
391 			if (spf_server->debug > 0)
392 				SPF_debugf("get_record(%s): NO_RECOERY", domain);
393 			SPF_dns_rr_free(rr_txt);
394 			return SPF_response_add_error(spf_response, SPF_E_DNS_ERROR,
395 					"Unrecoverable DNS failure for '%s'.", domain);
396 			// break;
397 
398 		case NETDB_SUCCESS:
399 			if (spf_server->debug > 0)
400 				SPF_debugf("get_record(%s): NETDB_SUCCESS", domain);
401 			break;
402 
403 		default:
404 			if (spf_server->debug > 0)
405 				SPF_debugf("get_record(%s): UNKNOWN_ERROR", domain);
406 			herrno = rr_txt->herrno;	// Avoid use-after-free
407 			SPF_dns_rr_free(rr_txt);
408 			return SPF_response_add_error(spf_response, SPF_E_DNS_ERROR,
409 					"Unknown DNS failure for '%s': %d.",
410 					domain, herrno);
411 			// break;
412 	}
413 
414 	if (rr_txt->num_rr == 0) {
415 		SPF_dns_rr_free(rr_txt);
416 		if (rr_type == ns_t_spf) {
417 			rr_type = ns_t_txt;
418 			goto retry;
419 		}
420 		return SPF_response_add_error(spf_response, SPF_E_NOT_SPF,
421 				"No TXT records returned from DNS lookup for '%s'",
422 				domain);
423 	}
424 
425 	/* Actually, this could never be used uninitialised anyway. */
426 	idx_found = 0;
427 
428 	/* check for multiple SPF records */
429 	num_found = 0;
430 	for (i = 0; i < rr_txt->num_rr; i++) {
431 		/*
432 		if (spf_server->debug > 1)
433 			SPF_debugf("Comparing '%s' with '%s'",
434 					SPF_VER_STR " ", rr_txt->rr[i]->txt);
435 		*/
436 		if (strncasecmp(rr_txt->rr[i]->txt,
437 					  SPF_VER_STR, sizeof(SPF_VER_STR) - 1) == 0) {
438 			char	e = rr_txt->rr[i]->txt[sizeof(SPF_VER_STR) - 1];
439 			if (e == ' ' || e == '\0') {
440 				if (spf_server->debug > 0)
441 					SPF_debugf("found SPF record: %s", rr_txt->rr[i]->txt);
442 				num_found++;
443 				idx_found = i;
444 			}
445 		}
446 	}
447 
448 	if (num_found == 0) {
449 		SPF_dns_rr_free(rr_txt);
450 		if (rr_type == ns_t_spf) {
451 			rr_type = ns_t_txt;
452 			goto retry;
453 		}
454 		spf_response->result = SPF_RESULT_NONE;
455 		spf_response->reason = SPF_REASON_FAILURE;
456 		return SPF_response_add_error(spf_response, SPF_E_NOT_SPF,
457 				"No SPF records for '%s'", domain);
458 	}
459 	if (num_found > 1) {
460 		SPF_dns_rr_free(rr_txt);
461 		// rfc4408 requires permerror here.
462 		/* XXX This could be refactored with SPF_i_done. */
463 		spf_response->result = SPF_RESULT_PERMERROR;
464 		spf_response->reason = SPF_REASON_FAILURE;
465 		return SPF_response_add_error(spf_response, SPF_E_MULTIPLE_RECORDS,
466 				"Multiple SPF records for '%s'", domain);
467 	}
468 
469 	/* try to compile the SPF record */
470 	err = SPF_record_compile(spf_server,
471 					spf_response, spf_recordp,
472 					rr_txt->rr[idx_found]->txt );
473 	SPF_dns_rr_free(rr_txt);
474 
475 	/* FIXME: support multiple versions */
476 	if (err != SPF_E_SUCCESS)
477 		return SPF_response_add_error(spf_response, SPF_E_NOT_SPF,
478 				"Failed to compile SPF record for '%s'", domain);
479 
480 	return SPF_E_SUCCESS;
481 }
482 
483 /**
484  * Various accessors.
485  *
486  * The user is permitted to override the maximums.
487  */
488 #define SPF_ACCESS_INT(f) \
489 	SPF_errcode_t SPF_server_set_ ## f(SPF_server_t *s, int n) { \
490 		s->f = n; return SPF_E_SUCCESS; \
491 	} \
492 	int SPF_server_get_ ## f(SPF_server_t *s) { \
493 		return s->f; \
494 	}
495 
496 /**
497  * The return values from these getter functions are used without
498  * modification. If you set a value higher than the specified
499  * maximum, it will be used. Beware.
500  */
501 SPF_ACCESS_INT(max_dns_mech);
502 SPF_ACCESS_INT(max_dns_ptr);
503 SPF_ACCESS_INT(max_dns_mx);
504