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