"Fossies" - the Fresh Open Source Software Archive 
Member "libspf2-1.2.10/src/libspf2/spf_interpret.c" (28 Jan 2012, 33183 Bytes) of package /linux/privat/libspf2-1.2.10.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "spf_interpret.c" see the
Fossies "Dox" file reference documentation.
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 #ifdef HAVE_NETDB_H
32 #include <netdb.h>
33 #endif
34
35 #include <ctype.h>
36
37 #include "spf.h"
38 #include "spf_dns.h"
39 #include "spf_internal.h"
40 #include "spf_dns_internal.h"
41 #include "spf_server.h"
42
43 static SPF_errcode_t
44 SPF_i_set_explanation(SPF_response_t *spf_response)
45 {
46 SPF_server_t *spf_server;
47 SPF_request_t *spf_request;
48 SPF_record_t *spf_record;
49 SPF_errcode_t err;
50 char *buf;
51 size_t buflen;
52
53 SPF_ASSERT_NOTNULL(spf_response);
54 spf_request = spf_response->spf_request;
55 SPF_ASSERT_NOTNULL(spf_request);
56 spf_server = spf_request->spf_server;
57 SPF_ASSERT_NOTNULL(spf_server);
58
59 spf_record = spf_response->spf_record_exp;
60 SPF_ASSERT_NOTNULL(spf_record);
61
62 if (spf_response->explanation)
63 free(spf_response->explanation);
64 spf_response->explanation = NULL;
65
66 buflen = SPF_SMTP_COMMENT_SIZE + 1;
67 buf = malloc(buflen);
68 if (buf == NULL)
69 return SPF_E_NO_MEMORY;
70 memset(buf, '\0', buflen);
71
72 err = SPF_request_get_exp(spf_server, spf_request,
73 spf_response, spf_record, &buf, &buflen);
74 if (err != SPF_E_SUCCESS) {
75 free(buf);
76 return err;
77 }
78
79 spf_response->explanation = buf;
80
81 return SPF_E_SUCCESS;
82 }
83
84 static SPF_errcode_t
85 SPF_i_set_smtp_comment(SPF_response_t *spf_response)
86 {
87 SPF_server_t *spf_server;
88 SPF_request_t *spf_request;
89 SPF_errcode_t err;
90 char buf[SPF_SMTP_COMMENT_SIZE];
91
92 SPF_ASSERT_NOTNULL(spf_response);
93 spf_request = spf_response->spf_request;
94 SPF_ASSERT_NOTNULL(spf_request);
95 spf_server = spf_request->spf_server;
96 SPF_ASSERT_NOTNULL(spf_server);
97
98 if (spf_response->smtp_comment)
99 free(spf_response->smtp_comment);
100 spf_response->smtp_comment = NULL;
101
102 /* smtp_comment = exp= + <why string> */
103 switch (spf_response->result) {
104 case SPF_RESULT_FAIL:
105 case SPF_RESULT_SOFTFAIL:
106 case SPF_RESULT_NEUTRAL:
107 case SPF_RESULT_NONE:
108
109 err = SPF_i_set_explanation(spf_response);
110 if (err != SPF_E_SUCCESS)
111 return err;
112
113 memset(buf, '\0', sizeof(buf));
114 snprintf(buf, SPF_SMTP_COMMENT_SIZE, "%s : Reason: %s",
115 spf_response->explanation,
116 SPF_strreason(spf_response->reason));
117 buf[SPF_SMTP_COMMENT_SIZE - 1] = '\0';
118
119 /* It doesn't really hurt much if this fails. */
120 spf_response->smtp_comment = strdup(buf);
121 if (! spf_response->smtp_comment)
122 return SPF_E_NO_MEMORY;
123
124 break;
125 case SPF_RESULT_INVALID:
126 case SPF_RESULT_PASS:
127 case SPF_RESULT_TEMPERROR:
128 case SPF_RESULT_PERMERROR:
129 default:
130 break;
131 }
132
133 return SPF_E_SUCCESS;
134 }
135
136 static SPF_errcode_t
137 SPF_i_set_header_comment(SPF_response_t *spf_response)
138 {
139 SPF_server_t *spf_server;
140 SPF_request_t *spf_request;
141 char *spf_source;
142
143 size_t len;
144
145 char ip4_buf[ INET_ADDRSTRLEN ];
146 char ip6_buf[ INET6_ADDRSTRLEN ];
147 const char *ip;
148
149 char *buf;
150 char *sender_dom;
151 char *p, *p_end;
152
153 SPF_ASSERT_NOTNULL(spf_response);
154 spf_request = spf_response->spf_request;
155 SPF_ASSERT_NOTNULL(spf_request);
156 spf_server = spf_request->spf_server;
157 SPF_ASSERT_NOTNULL(spf_server);
158
159 if (spf_response->header_comment)
160 free(spf_response->header_comment);
161 spf_response->header_comment = NULL;
162
163 /* Is this cur_dom? */
164 sender_dom = spf_request->env_from_dp;
165 if (sender_dom == NULL)
166 sender_dom = spf_request->helo_dom;
167
168 if ( spf_response->reason == SPF_REASON_LOCAL_POLICY ) {
169 spf_source = strdup( "local policy" );
170 }
171 else if ( spf_response->reason == SPF_REASON_2MX ) {
172 if ( spf_request->rcpt_to_dom == NULL || spf_request->rcpt_to_dom[0] == '\0' )
173 SPF_error( "RCPT TO domain is NULL" );
174
175 spf_source = strdup( spf_request->rcpt_to_dom );
176 }
177 else if ( sender_dom == NULL ) {
178 spf_source = strdup( "unknown domain" );
179 }
180 else {
181 len = strlen( sender_dom ) + sizeof( "domain of " );
182 spf_source = malloc( len );
183 if ( spf_source )
184 snprintf( spf_source, len, "domain of %s", sender_dom );
185 }
186
187 if ( spf_source == NULL )
188 return SPF_E_INTERNAL_ERROR;
189
190 ip = NULL;
191 if ( spf_request->client_ver == AF_INET ) {
192 ip = inet_ntop( AF_INET, &spf_request->ipv4,
193 ip4_buf, sizeof( ip4_buf ) );
194 }
195 else if (spf_request->client_ver == AF_INET6 ) {
196 ip = inet_ntop( AF_INET6, &spf_request->ipv6,
197 ip6_buf, sizeof( ip6_buf ) );
198 }
199 if ( ip == NULL )
200 ip = "(unknown ip address)";
201
202 len = strlen( SPF_request_get_rec_dom(spf_request) ) + strlen( spf_source ) + strlen( ip ) + 80;
203 buf = malloc( len );
204 if ( buf == NULL ) {
205 free( spf_source );
206 return SPF_E_INTERNAL_ERROR;
207 }
208
209 p = buf;
210 p_end = p + len;
211
212 /* create the stock header comment */
213 p += snprintf( p, p_end - p, "%s: ", SPF_request_get_rec_dom(spf_request) );
214
215 switch(spf_response->result)
216 {
217 case SPF_RESULT_PASS:
218 if ( spf_response->reason == SPF_REASON_LOCALHOST )
219 snprintf( p, p_end - p, "localhost is always allowed." );
220 else if ( spf_response->reason == SPF_REASON_2MX )
221 snprintf( p, p_end - p, "message received from %s which is an MX secondary for %s.",
222 ip, spf_source );
223 else
224 snprintf( p, p_end - p, "%s designates %s as permitted sender",
225 spf_source, ip );
226 break;
227
228 case SPF_RESULT_FAIL:
229 snprintf( p, p_end - p, "%s does not designate %s as permitted sender",
230 spf_source, ip );
231 break;
232
233 case SPF_RESULT_SOFTFAIL:
234 snprintf( p, p_end - p, "transitioning %s does not designate %s as permitted sender",
235 spf_source, ip );
236 break;
237
238 case SPF_RESULT_PERMERROR:
239 snprintf(p, p_end - p, "error in processing during lookup of %s: %s",
240 spf_source, SPF_strerror(spf_response->err));
241 break;
242
243 case SPF_RESULT_NEUTRAL:
244 snprintf(p, p_end - p, "%s is neither permitted nor denied by %s",
245 ip, spf_source);
246 break;
247 case SPF_RESULT_NONE:
248 snprintf(p, p_end - p, "%s does not provide an SPF record",
249 spf_source);
250 break;
251
252 case SPF_RESULT_TEMPERROR:
253 snprintf(p, p_end - p, "encountered temporary error during SPF processing of %s",
254 spf_source );
255 break;
256
257
258 default:
259 snprintf( p, p_end - p, "error: unknown SPF result %d encountered while checking %s for %s",
260 spf_response->result, ip, spf_source );
261 break;
262 }
263
264 if (spf_source)
265 free(spf_source);
266
267 spf_response->header_comment = SPF_sanitize(spf_server, buf);
268
269 return SPF_E_SUCCESS;
270 }
271
272 static SPF_errcode_t
273 SPF_i_set_received_spf(SPF_response_t *spf_response)
274 {
275 SPF_server_t *spf_server;
276 SPF_request_t *spf_request;
277 char ip4_buf[ INET_ADDRSTRLEN ];
278 char ip6_buf[ INET6_ADDRSTRLEN ];
279 const char *ip;
280
281 char *buf;
282 size_t buflen = SPF_RECEIVED_SPF_SIZE;
283 char *buf_value;
284
285 char *p, *p_end;
286
287 SPF_ASSERT_NOTNULL(spf_response);
288 spf_request = spf_response->spf_request;
289 SPF_ASSERT_NOTNULL(spf_request);
290 spf_server = spf_request->spf_server;
291 SPF_ASSERT_NOTNULL(spf_server);
292
293 if (spf_response->received_spf)
294 free(spf_response->received_spf);
295 spf_response->received_spf = NULL;
296
297 buf = malloc( buflen );
298 if ( buf == NULL )
299 return SPF_E_INTERNAL_ERROR;
300
301 p = buf;
302 p_end = p + buflen;
303
304 /* create the stock Received-SPF: header */
305
306 p += snprintf( p, p_end - p, "Received-SPF: ");
307 buf_value = p;
308
309 do { /* A prop for a structured goto called 'break' */
310 p += snprintf( p, p_end - p, "%s (%s)",
311 SPF_strresult( spf_response->result ),
312 spf_response->header_comment );
313 if ( p_end - p <= 0 ) break;
314
315
316
317 /* add in the optional ip address keyword */
318 ip = NULL;
319 if ( spf_request->client_ver == AF_INET ) {
320 ip = inet_ntop( AF_INET, &spf_request->ipv4,
321 ip4_buf, sizeof( ip4_buf ) );
322 }
323 else if (spf_request->client_ver == AF_INET6 ) {
324 ip = inet_ntop( AF_INET6, &spf_request->ipv6,
325 ip6_buf, sizeof( ip6_buf ) );
326 }
327
328 if ( ip != NULL ) {
329 p += snprintf( p, p_end - p, " client-ip=%s;", ip );
330 if ( p_end - p <= 0 ) break;
331 }
332
333
334 /* add in the optional envelope-from keyword */
335 if ( spf_request->env_from != NULL ) {
336 p += snprintf( p, p_end - p, " envelope-from=%s;", spf_request->env_from );
337 if ( p_end - p <= 0 ) break;
338 }
339
340
341 /* add in the optional helo domain keyword */
342 if ( spf_request->helo_dom != NULL ) {
343 p += snprintf( p, p_end - p, " helo=%s;", spf_request->helo_dom );
344 if ( p_end - p <= 0 ) break;
345 }
346
347
348 /* FIXME: Add in full compiler errors. */
349 #if 0
350 /* add in the optional compiler error keyword */
351 if ( output.err_msg != NULL ) {
352 p += snprintf( p, p_end - p, " problem=%s;", output.err_msg );
353 if ( p_end - p <= 0 ) break;
354 }
355 else if ( c_results.err_msg != NULL ) {
356 p += snprintf( p, p_end - p, " problem=%s;", c_results.err_msg );
357 if ( p_end - p <= 0 ) break;
358 }
359 #endif
360
361 /* FIXME should the explanation string be included in the header? */
362
363 /* FIXME should the header be reformated to include line breaks? */
364 } while(0);
365
366 spf_response->received_spf = SPF_sanitize(spf_server, buf);
367 spf_response->received_spf_value = buf_value;
368
369 return SPF_E_SUCCESS;
370 }
371
372
373
374 #define DONE(result,reason,err) SPF_i_done(spf_response, result, reason, err)
375 #define DONE_TEMPERR(err) DONE(SPF_RESULT_TEMPERROR,SPF_REASON_NONE,err)
376 #define DONE_PERMERR(err) DONE(SPF_RESULT_PERMERROR,SPF_REASON_NONE,err)
377 #define DONE_MECH(result) DONE(result, SPF_REASON_MECH, SPF_E_SUCCESS)
378
379 /**
380 * This must be called with EITHER
381 * spf_response->spf_record_exp != NULL
382 * OR
383 * result in { SPF_RESULT_PASS SPF_RESULT_INVALID
384 * SPF_RESULT_TEMPERROR SPF_RESULT_PERMERROR }
385 * or the library will abort when it tries to generate an explanation.
386 */
387 SPF_errcode_t
388 SPF_i_done(SPF_response_t *spf_response,
389 SPF_result_t result, SPF_reason_t reason, SPF_errcode_t err)
390 {
391 SPF_request_t *spf_request;
392 SPF_server_t *spf_server;
393
394 SPF_ASSERT_NOTNULL(spf_response);
395 spf_request = spf_response->spf_request;
396 SPF_ASSERT_NOTNULL(spf_request);
397 spf_server = spf_request->spf_server;
398 SPF_ASSERT_NOTNULL(spf_server);
399
400 spf_response->result = result;
401 spf_response->reason = reason;
402 spf_response->err = err;
403
404 SPF_i_set_smtp_comment(spf_response);
405 SPF_i_set_header_comment(spf_response);
406 SPF_i_set_received_spf(spf_response);
407
408 return err;
409 }
410
411 /*
412 * FIXME: Everything before this line could go into a separate file.
413 */
414
415
416
417
418 #define INET_NTOP(af, src, dst, cnt) do { \
419 if (inet_ntop(af, src, dst, cnt) == NULL) \
420 snprintf(dst, cnt, "ip-error" ); \
421 } while(0)
422
423 static int
424 SPF_i_mech_cidr(SPF_request_t *spf_request, SPF_mech_t *mech)
425 {
426 SPF_data_t *data;
427
428 SPF_ASSERT_NOTNULL(mech);
429
430 switch( mech->mech_type )
431 {
432 case MECH_IP4:
433 case MECH_IP6:
434 return mech->mech_len;
435 break;
436
437 case MECH_A:
438 case MECH_MX:
439 data = SPF_mech_data( mech );
440 /* XXX this was <= but I think that was wrong. */
441 if ( data < SPF_mech_end_data( mech )
442 && data->dc.parm_type == PARM_CIDR )
443 {
444 if ( spf_request->client_ver == AF_INET )
445 return data->dc.ipv4;
446 else if ( spf_request->client_ver == AF_INET6 )
447 return data->dc.ipv6;
448 }
449 break;
450 }
451
452 return 0;
453 }
454
455
456
457 static int
458 SPF_i_match_ip4(SPF_server_t *spf_server,
459 SPF_request_t *spf_request,
460 SPF_mech_t *mech,
461 struct in_addr ipv4 )
462 {
463 char src_ip4_buf[ INET_ADDRSTRLEN ];
464 char dst_ip4_buf[ INET_ADDRSTRLEN ];
465 char mask_ip4_buf[ INET_ADDRSTRLEN ];
466
467 struct in_addr src_ipv4;
468 int cidr, mask;
469
470
471 if ( spf_request->client_ver != AF_INET )
472 return FALSE;
473
474 src_ipv4 = spf_request->ipv4;
475
476 cidr = SPF_i_mech_cidr( spf_request, mech );
477 if ( cidr == 0 )
478 cidr = 32;
479 mask = 0xffffffff << (32 - cidr);
480 mask = htonl(mask);
481
482 if (spf_server->debug) {
483 INET_NTOP(AF_INET, &src_ipv4.s_addr,
484 src_ip4_buf, sizeof(src_ip4_buf));
485 INET_NTOP(AF_INET, &ipv4.s_addr,
486 dst_ip4_buf, sizeof(dst_ip4_buf));
487 INET_NTOP(AF_INET, &mask,
488 mask_ip4_buf, sizeof(mask_ip4_buf));
489 SPF_debugf( "ip_match: %s == %s (/%d %s): %d",
490 src_ip4_buf, dst_ip4_buf, cidr, mask_ip4_buf,
491 (src_ipv4.s_addr & mask) == (ipv4.s_addr & mask));
492 }
493
494 return (src_ipv4.s_addr & mask) == (ipv4.s_addr & mask);
495 }
496
497
498 static int
499 SPF_i_match_ip6(SPF_server_t *spf_server,
500 SPF_request_t *spf_request,
501 SPF_mech_t *mech,
502 struct in6_addr ipv6 )
503 {
504 char src_ip6_buf[ INET6_ADDRSTRLEN ];
505 char dst_ip6_buf[ INET6_ADDRSTRLEN ];
506
507 struct in6_addr src_ipv6;
508 int cidr, cidr_save, mask;
509 int i;
510 int match;
511
512 if ( spf_request->client_ver != AF_INET6 )
513 return FALSE;
514
515 src_ipv6 = spf_request->ipv6;
516
517 cidr = SPF_i_mech_cidr(spf_request, mech);
518 if ( cidr == 0 )
519 cidr = 128;
520 cidr_save = cidr;
521
522 match = TRUE;
523 for( i = 0; i < array_elem( ipv6.s6_addr ) && match; i++ )
524 {
525 if ( cidr > 8 )
526 mask = 0xff;
527 else if ( cidr > 0 )
528 mask = (0xff << (8 - cidr)) & 0xff;
529 else
530 break;
531 cidr -= 8;
532
533 match = (src_ipv6.s6_addr[i] & mask) == (ipv6.s6_addr[i] & mask);
534 }
535
536 if (spf_server->debug) {
537 INET_NTOP(AF_INET6, &src_ipv6.s6_addr,
538 src_ip6_buf, sizeof(src_ip6_buf));
539 INET_NTOP(AF_INET6, &ipv6.s6_addr,
540 dst_ip6_buf, sizeof(dst_ip6_buf));
541 SPF_debugf( "ip_match: %s == %s (/%d): %d",
542 src_ip6_buf, dst_ip6_buf, cidr_save, match );
543 }
544
545 return match;
546 }
547
548 static int
549 SPF_i_match_domain(SPF_server_t *spf_server,
550 const char *hostname, const char *domain)
551 {
552 const char *hp;
553 size_t hlen;
554 size_t dlen;
555
556 if (spf_server->debug)
557 SPF_debugf( "%s ?=? %s", hostname, domain );
558
559 hlen = strlen(hostname);
560 dlen = strlen(domain);
561
562 /* A host cannot be a member of a domain longer than it is. */
563 if (dlen > hlen)
564 return 0;
565
566 /* The two may be equal? */
567 if (dlen == hlen)
568 return (strcasecmp(hostname, domain) == 0);
569
570 /* The domain may match a trailing portion preceded by a dot. */
571 hp = hostname + (hlen - dlen);
572
573 if (*(hp - 1) != '.')
574 return 0;
575
576 return (strcasecmp(hp, domain) == 0);
577 }
578
579
580 /*
581 * Set cur_dom (to either sender or or helo_dom) before calling this.
582 */
583
584 SPF_errcode_t
585 SPF_record_interpret(SPF_record_t *spf_record,
586 SPF_request_t *spf_request, SPF_response_t *spf_response,
587 int depth)
588 {
589 SPF_server_t *spf_server;
590
591 /* Temporaries */
592 int i, j;
593 int m; /* Mechanism iterator */
594 SPF_mech_t *mech;
595 SPF_data_t *data;
596 SPF_data_t *data_end; /* XXX Replace with size_t data_len */
597
598 /* Where to insert the local policy (whitelist) */
599 SPF_mech_t *local_policy; /* Not the local policy */
600 int found_all; /* A crappy temporary. */
601
602 char *buf = NULL;
603 size_t buf_len = 0;
604 ns_type fetch_ns_type;
605 const char *lookup;
606
607 SPF_dns_rr_t *rr_a;
608 SPF_dns_rr_t *rr_aaaa;
609 SPF_dns_rr_t *rr_ptr;
610 SPF_dns_rr_t *rr_mx;
611
612 SPF_errcode_t err;
613
614 SPF_dns_server_t*resolver;
615
616 /* An SPF record for subrequests - replaces c_results */
617 SPF_record_t *spf_record_subr;
618
619 SPF_response_t *save_spf_response;
620 SPF_response_t *spf_response_subr;
621 const char *save_cur_dom;
622
623 struct in_addr addr4;
624 struct in6_addr addr6;
625
626 int max_ptr;
627 int max_mx;
628 int max_exceeded;
629
630 char ip4_buf[ INET_ADDRSTRLEN ];
631 char ip6_buf[ INET6_ADDRSTRLEN ];
632
633
634 /*
635 * make sure we were passed valid data to work with
636 */
637 SPF_ASSERT_NOTNULL(spf_record);
638 SPF_ASSERT_NOTNULL(spf_request);
639 SPF_ASSERT_NOTNULL(spf_response);
640 spf_server = spf_record->spf_server;
641 SPF_ASSERT_NOTNULL(spf_server);
642
643 SPF_ASSERT_NOTNULL(spf_response->spf_record_exp);
644
645 if (depth > 20)
646 return DONE_PERMERR(SPF_E_RECURSIVE);
647
648 if ( spf_request->client_ver != AF_INET && spf_request->client_ver != AF_INET6 )
649 return DONE_PERMERR(SPF_E_NOT_CONFIG);
650
651 if (spf_request->cur_dom == NULL)
652 return DONE_PERMERR(SPF_E_NOT_CONFIG);
653
654
655 /*
656 * localhost always gets a free ride
657 */
658
659 #if 0
660 /* This should have been done already before we got here. */
661 if ( SPF_request_is_loopback( spf_request ) )
662 return DONE(SPF_RESULT_PASS,SPF_REASON_LOCALHOST,SPF_E_SUCCESS);
663 #endif
664
665 /*
666 * Do some start up stuff if we haven't recursed yet
667 */
668
669 local_policy = NULL;
670
671 if ( spf_request->use_local_policy ) {
672 /*
673 * find the location for the whitelist execution
674 *
675 * Philip Gladstone says:
676 *
677 * I think that the localpolicy should only be inserted if the
678 * final mechanism is '-all', and it should be inserted after
679 * the last mechanism which is not '-'.
680 *
681 * Thus for the case of 'v=spf1 +a +mx -all', this would be
682 * interpreted as 'v=spf1 +a +mx +localpolicy -all'. Whereas
683 * 'v=spf1 -all' would remain the same (no non-'-'
684 * mechanism). 'v=spf1 +a +mx -exists:%stuff -all' would
685 * become 'v=spf1 +a +mx +localpolicy -exists:%stuff -all'.
686 */
687
688 if ( spf_server->local_policy ) {
689 mech = spf_record->mech_first;
690
691 found_all = FALSE;
692 for(m = 0; m < spf_record->num_mech; m++)
693 {
694 if ( mech->mech_type == MECH_ALL
695 && (mech->prefix_type == PREFIX_FAIL
696 || mech->prefix_type == PREFIX_UNKNOWN
697 || mech->prefix_type == PREFIX_SOFTFAIL
698 )
699 )
700 found_all = TRUE;
701
702 if ( mech->prefix_type != PREFIX_FAIL
703 && mech->prefix_type != PREFIX_SOFTFAIL
704 )
705 local_policy = mech;
706
707 mech = SPF_mech_next( mech );
708 }
709
710 if ( !found_all )
711 local_policy = NULL;
712 }
713
714 }
715
716
717 /*
718 * evaluate the mechanisms
719 */
720
721 #define SPF_ADD_DNS_MECH() do { spf_response->num_dns_mech++; } while(0)
722
723 #define SPF_MAYBE_SKIP_CIDR() \
724 do { \
725 if ( data < data_end && data->dc.parm_type == PARM_CIDR ) \
726 data = SPF_data_next( data ); \
727 } while(0)
728
729 #define SPF_GET_LOOKUP_DATA() \
730 do { \
731 if ( data == data_end ) \
732 lookup = spf_request->cur_dom; \
733 else { \
734 err = SPF_record_expand_data( spf_server, \
735 spf_request, spf_response, \
736 data, ((char *)data_end - (char *)data), \
737 &buf, &buf_len ); \
738 if (err == SPF_E_NO_MEMORY) { \
739 SPF_FREE_LOOKUP_DATA(); \
740 return DONE_TEMPERR(err); \
741 } \
742 if (err) { \
743 SPF_FREE_LOOKUP_DATA(); \
744 return DONE_PERMERR(err); \
745 } \
746 lookup = buf; \
747 } \
748 } while(0)
749 #define SPF_FREE_LOOKUP_DATA() \
750 do { if (buf != NULL) { free(buf); buf = NULL; } } while(0)
751
752
753 resolver = spf_server->resolver;
754
755 mech = spf_record->mech_first;
756 for (m = 0; m < spf_record->num_mech; m++) {
757
758 /* This is as good a place as any. */
759 /* XXX Rip this out and put it into a macro which can go into inner loops. */
760 if (spf_response->num_dns_mech > spf_server->max_dns_mech) {
761 SPF_FREE_LOOKUP_DATA();
762 return DONE(SPF_RESULT_PERMERROR, SPF_REASON_NONE, SPF_E_BIG_DNS);
763 }
764
765 data = SPF_mech_data(mech);
766 data_end = SPF_mech_end_data(mech);
767
768 switch (mech->mech_type) {
769 case MECH_A:
770 SPF_ADD_DNS_MECH();
771 SPF_MAYBE_SKIP_CIDR();
772 SPF_GET_LOOKUP_DATA();
773
774 if (spf_request->client_ver == AF_INET)
775 fetch_ns_type = ns_t_a;
776 else
777 fetch_ns_type = ns_t_aaaa;
778
779 rr_a = SPF_dns_lookup(resolver, lookup, fetch_ns_type, TRUE);
780
781 if (spf_server->debug)
782 SPF_debugf("found %d A records for %s (herrno: %d)",
783 rr_a->num_rr, lookup, rr_a->herrno);
784
785 if (rr_a->herrno == TRY_AGAIN) {
786 SPF_dns_rr_free(rr_a);
787 SPF_FREE_LOOKUP_DATA();
788 return DONE_TEMPERR(SPF_E_DNS_ERROR); /* REASON_MECH */
789 }
790
791 for (i = 0; i < rr_a->num_rr; i++) {
792 /* XXX Should this be hoisted? */
793 if (rr_a->rr_type != fetch_ns_type)
794 continue;
795
796 if (spf_request->client_ver == AF_INET) {
797 if (SPF_i_match_ip4(spf_server, spf_request, mech, rr_a->rr[i]->a)) {
798 SPF_dns_rr_free(rr_a);
799 SPF_FREE_LOOKUP_DATA();
800 return DONE_MECH(mech->prefix_type);
801 }
802 }
803 else {
804 if (SPF_i_match_ip6(spf_server, spf_request, mech, rr_a->rr[i]->aaaa)) {
805 SPF_dns_rr_free(rr_a);
806 SPF_FREE_LOOKUP_DATA();
807 return DONE_MECH(mech->prefix_type);
808 }
809 }
810 }
811
812 SPF_dns_rr_free(rr_a);
813 break;
814
815 case MECH_MX:
816 SPF_ADD_DNS_MECH();
817 SPF_MAYBE_SKIP_CIDR();
818 SPF_GET_LOOKUP_DATA();
819
820 rr_mx = SPF_dns_lookup(resolver, lookup, ns_t_mx, TRUE);
821
822 if (spf_server->debug)
823 SPF_debugf("found %d MX records for %s (herrno: %d)",
824 rr_mx->num_rr, lookup, rr_mx->herrno);
825
826 if (rr_mx->herrno == TRY_AGAIN) {
827 SPF_dns_rr_free(rr_mx);
828 SPF_FREE_LOOKUP_DATA();
829 return DONE_TEMPERR(SPF_E_DNS_ERROR);
830 }
831
832 /* The maximum number of MX records we will inspect. */
833 max_mx = rr_mx->num_rr;
834 max_exceeded = 0;
835 if (max_mx > spf_server->max_dns_mx) {
836 max_exceeded = 1;
837 max_mx = SPF_server_get_max_dns_mx(spf_server);
838 }
839
840 for (j = 0; j < max_mx; j++) {
841 /* XXX Should this be hoisted? */
842 if (rr_mx->rr_type != ns_t_mx)
843 continue;
844
845 if (spf_request->client_ver == AF_INET)
846 fetch_ns_type = ns_t_a;
847 else
848 fetch_ns_type = ns_t_aaaa;
849
850 rr_a = SPF_dns_lookup(resolver, rr_mx->rr[j]->mx,
851 fetch_ns_type, TRUE );
852
853 if (spf_server->debug)
854 SPF_debugf("%d: found %d A records for %s (herrno: %d)",
855 j, rr_a->num_rr, rr_mx->rr[j]->mx, rr_a->herrno);
856 if (rr_a->herrno == TRY_AGAIN) {
857 SPF_dns_rr_free(rr_mx);
858 SPF_dns_rr_free(rr_a);
859 SPF_FREE_LOOKUP_DATA();
860 return DONE_TEMPERR(SPF_E_DNS_ERROR);
861 }
862
863 for (i = 0; i < rr_a->num_rr; i++) {
864 /* XXX Should this be hoisted? */
865 if (rr_a->rr_type != fetch_ns_type)
866 continue;
867
868 if (spf_request->client_ver == AF_INET) {
869 if (SPF_i_match_ip4(spf_server, spf_request, mech,
870 rr_a->rr[i]->a)) {
871 SPF_dns_rr_free(rr_mx);
872 SPF_dns_rr_free(rr_a);
873 SPF_FREE_LOOKUP_DATA();
874 return DONE(mech->prefix_type, SPF_REASON_MECH,
875 SPF_E_SUCCESS);
876 }
877 }
878 else {
879 if (SPF_i_match_ip6(spf_server, spf_request, mech,
880 rr_a->rr[i]->aaaa)) {
881 SPF_dns_rr_free(rr_mx);
882 SPF_dns_rr_free(rr_a);
883 SPF_FREE_LOOKUP_DATA();
884 return DONE(mech->prefix_type, SPF_REASON_MECH,
885 SPF_E_SUCCESS);
886 }
887 }
888 }
889 SPF_dns_rr_free(rr_a);
890 }
891
892 SPF_dns_rr_free( rr_mx );
893 if (max_exceeded) {
894 SPF_FREE_LOOKUP_DATA();
895 return DONE(SPF_RESULT_PERMERROR, SPF_REASON_NONE, SPF_E_BIG_DNS);
896 }
897 break;
898
899 case MECH_PTR:
900 SPF_ADD_DNS_MECH();
901 SPF_GET_LOOKUP_DATA();
902
903 if (spf_request->client_ver == AF_INET) {
904 rr_ptr = SPF_dns_rlookup(resolver,
905 spf_request->ipv4, ns_t_ptr, TRUE);
906
907 if (spf_server->debug) {
908 INET_NTOP(AF_INET, &spf_request->ipv4.s_addr,
909 ip4_buf, sizeof(ip4_buf));
910 SPF_debugf("got %d PTR records for %s (herrno: %d)",
911 rr_ptr->num_rr, ip4_buf, rr_ptr->herrno);
912 }
913
914 if (rr_ptr->herrno == TRY_AGAIN) {
915 SPF_dns_rr_free(rr_ptr);
916 SPF_FREE_LOOKUP_DATA();
917 return DONE_TEMPERR(SPF_E_DNS_ERROR);
918 }
919
920
921 /* The maximum number of PTR records we will inspect. */
922 max_ptr = rr_ptr->num_rr;
923 max_exceeded = 0;
924 if (max_ptr > spf_server->max_dns_ptr) {
925 max_exceeded = 1;
926 max_ptr = SPF_server_get_max_dns_ptr(spf_server);
927 }
928
929 for (i = 0; i < max_ptr; i++) {
930 /* XXX MX has a 'continue' case here which should be hoisted. */
931
932 rr_a = SPF_dns_lookup(resolver,
933 rr_ptr->rr[i]->ptr, ns_t_a, TRUE);
934
935 if (spf_server->debug)
936 SPF_debugf( "%d: found %d A records for %s (herrno: %d)",
937 i, rr_a->num_rr, rr_ptr->rr[i]->ptr, rr_a->herrno );
938 if (rr_a->herrno == TRY_AGAIN) {
939 SPF_dns_rr_free(rr_ptr);
940 SPF_dns_rr_free(rr_a);
941 SPF_FREE_LOOKUP_DATA();
942 return DONE_TEMPERR( SPF_E_DNS_ERROR );
943 }
944
945 for (j = 0; j < rr_a->num_rr; j++) {
946 /* XXX MX has a 'continue' case here which should be hoisted. */
947
948 if (spf_server->debug) {
949 INET_NTOP(AF_INET, &rr_a->rr[j]->a.s_addr,
950 ip4_buf, sizeof(ip4_buf));
951 SPF_debugf("%d: %d: found %s",
952 i, j, ip4_buf);
953 }
954
955 if (rr_a->rr[j]->a.s_addr ==
956 spf_request->ipv4.s_addr) {
957 if (SPF_i_match_domain(spf_server,
958 rr_ptr->rr[i]->ptr, lookup)) {
959 SPF_dns_rr_free(rr_ptr);
960 SPF_dns_rr_free(rr_a);
961 SPF_FREE_LOOKUP_DATA();
962 return DONE_MECH(mech->prefix_type);
963 }
964 }
965 }
966 SPF_dns_rr_free(rr_a);
967 }
968 SPF_dns_rr_free(rr_ptr);
969
970 if (max_exceeded) {
971 SPF_FREE_LOOKUP_DATA();
972 return DONE(SPF_RESULT_PERMERROR, SPF_REASON_NONE, SPF_E_BIG_DNS);
973 }
974 }
975
976 else if ( spf_request->client_ver == AF_INET6 ) {
977 rr_ptr = SPF_dns_rlookup6(resolver,
978 spf_request->ipv6, ns_t_ptr, TRUE);
979
980 if ( spf_server->debug ) {
981 INET_NTOP( AF_INET6, &spf_request->ipv6.s6_addr,
982 ip6_buf, sizeof( ip6_buf ) );
983 SPF_debugf( "found %d PTR records for %s (herrno: %d)",
984 rr_ptr->num_rr, ip6_buf, rr_ptr->herrno );
985 }
986 if( rr_ptr->herrno == TRY_AGAIN ) {
987 SPF_dns_rr_free(rr_ptr);
988 SPF_FREE_LOOKUP_DATA();
989 return DONE_TEMPERR( SPF_E_DNS_ERROR );
990 }
991
992
993 max_ptr = rr_ptr->num_rr;
994 max_exceeded = 0;
995 if (max_ptr > spf_server->max_dns_ptr) {
996 max_ptr = SPF_server_get_max_dns_ptr(spf_server);
997 max_exceeded = 1;
998 }
999
1000 for (i = 0; i < max_ptr; i++) {
1001 /* XXX MX has a 'continue' case here which should be hoisted. */
1002
1003 rr_aaaa = SPF_dns_lookup(resolver,
1004 rr_ptr->rr[i]->ptr, ns_t_aaaa, TRUE);
1005
1006 if ( spf_server->debug )
1007 SPF_debugf("%d: found %d AAAA records for %s (herrno: %d)",
1008 i, rr_aaaa->num_rr, rr_ptr->rr[i]->ptr, rr_aaaa->herrno);
1009 if( rr_aaaa->herrno == TRY_AGAIN ) {
1010 SPF_dns_rr_free(rr_ptr);
1011 SPF_dns_rr_free(rr_aaaa);
1012 SPF_FREE_LOOKUP_DATA();
1013 return DONE_TEMPERR( SPF_E_DNS_ERROR );
1014 }
1015
1016 for( j = 0; j < rr_aaaa->num_rr; j++ ) {
1017 /* XXX MX has a 'continue' case here which should be hoisted. */
1018 if ( spf_server->debug ) {
1019 INET_NTOP(AF_INET6, &rr_aaaa->rr[j]->aaaa.s6_addr,
1020 ip6_buf, sizeof(ip6_buf));
1021 SPF_debugf( "%d: %d: found %s",
1022 i, j, ip6_buf );
1023 }
1024
1025 if (memcmp(&rr_aaaa->rr[j]->aaaa,
1026 &spf_request->ipv6,
1027 sizeof(spf_request->ipv6)) == 0) {
1028 if (SPF_i_match_domain(spf_server,
1029 rr_ptr->rr[i]->ptr, lookup)) {
1030 SPF_dns_rr_free( rr_ptr );
1031 SPF_dns_rr_free(rr_aaaa);
1032 SPF_FREE_LOOKUP_DATA();
1033 return DONE_MECH( mech->prefix_type );
1034 }
1035 }
1036 }
1037 SPF_dns_rr_free(rr_aaaa);
1038 }
1039 SPF_dns_rr_free(rr_ptr);
1040
1041 if (max_exceeded) {
1042 SPF_FREE_LOOKUP_DATA();
1043 return DONE(SPF_RESULT_PERMERROR, SPF_REASON_NONE, SPF_E_BIG_DNS);
1044 }
1045 }
1046
1047
1048 break;
1049
1050 case MECH_INCLUDE:
1051 case MECH_REDIRECT:
1052 SPF_ADD_DNS_MECH();
1053
1054 err = SPF_record_expand_data(spf_server,
1055 spf_request, spf_response,
1056 SPF_mech_data(mech), SPF_mech_data_len(mech),
1057 &buf, &buf_len );
1058 if ( err == SPF_E_NO_MEMORY ) {
1059 SPF_FREE_LOOKUP_DATA();
1060 return DONE_TEMPERR( err );
1061 }
1062 if ( err ) {
1063 SPF_FREE_LOOKUP_DATA();
1064 return DONE_PERMERR( err );
1065 }
1066 lookup = buf;
1067
1068 /* XXX Maintain a stack depth here. Limit at 10. */
1069 if (strcmp(lookup, spf_request->cur_dom) == 0) {
1070 SPF_FREE_LOOKUP_DATA();
1071 return DONE_PERMERR( SPF_E_RECURSIVE );
1072 }
1073
1074 /*
1075 * get the (compiled) SPF record
1076 */
1077
1078 spf_record_subr = NULL;
1079 /* Remember to reset this. */
1080 save_cur_dom = spf_request->cur_dom;
1081 spf_request->cur_dom = lookup;
1082 err = SPF_server_get_record(spf_server, spf_request,
1083 spf_response, &spf_record_subr);
1084
1085 if ( spf_server->debug > 0 )
1086 SPF_debugf( "include/redirect: got SPF record: %s",
1087 SPF_strerror( err ) );
1088
1089 if (err != SPF_E_SUCCESS) {
1090 spf_request->cur_dom = save_cur_dom;
1091 if (spf_record_subr)
1092 SPF_record_free(spf_record_subr);
1093 SPF_FREE_LOOKUP_DATA();
1094 if (err == SPF_E_DNS_ERROR)
1095 return DONE_TEMPERR( err );
1096 else
1097 return DONE_PERMERR( err );
1098 }
1099
1100 SPF_ASSERT_NOTNULL(spf_record_subr);
1101
1102 /*
1103 * If we are a redirect which is not within the scope
1104 * of any include.
1105 */
1106 if (mech->mech_type == MECH_REDIRECT) {
1107 save_spf_response = NULL;
1108 if (spf_response->spf_record_exp == spf_record)
1109 spf_response->spf_record_exp = spf_record_subr;
1110 SPF_ASSERT_NOTNULL(spf_response->spf_record_exp);
1111 }
1112 else {
1113 save_spf_response = spf_response;
1114 spf_response = SPF_response_new(spf_request);
1115 if (! spf_response) {
1116 if (spf_record_subr)
1117 SPF_record_free(spf_record_subr);
1118 SPF_FREE_LOOKUP_DATA();
1119 return DONE_TEMPERR(SPF_E_NO_MEMORY);
1120 }
1121 spf_response->spf_record_exp = spf_record;
1122 SPF_ASSERT_NOTNULL(spf_response->spf_record_exp);
1123 }
1124 /*
1125 * find out whether this configuration passes
1126 */
1127 err = SPF_record_interpret(spf_record_subr,
1128 spf_request, spf_response, depth + 1);
1129 spf_request->cur_dom = save_cur_dom;
1130 /* Now, if we were a redirect, the child called done()
1131 * and used spf_record_exp. In that case, we need not
1132 * worry that spf_record_subr is invalid after the free.
1133 * If we were not a redirect, then spf_record_subr
1134 * is still the record it was in the first place.
1135 * Thus we do not need to reset it now. */
1136 SPF_record_free(spf_record_subr);
1137 spf_record_subr = NULL;
1138
1139 if ( spf_server->debug > 0 )
1140 SPF_debugf( "include/redirect: executed SPF record: %s result: %s reason: %s",
1141 SPF_strerror( err ),
1142 SPF_strresult( spf_response->result ),
1143 SPF_strreason( spf_response->reason ) );
1144 if (mech->mech_type == MECH_REDIRECT) {
1145 SPF_FREE_LOOKUP_DATA();
1146 return err; /* One way or the other */
1147 }
1148 else { // if (spf_response->result != SPF_RESULT_INVALID) {
1149 /* Set everything up properly again. */
1150 spf_response_subr = spf_response;
1151 spf_response = save_spf_response;
1152 save_spf_response = NULL;
1153
1154 /* Rewrite according to prefix of include */
1155 switch (SPF_response_result(spf_response_subr)) {
1156 case SPF_RESULT_PASS:
1157 /* Pass */
1158 SPF_FREE_LOOKUP_DATA();
1159 SPF_response_free(spf_response_subr);
1160 return DONE_MECH( mech->prefix_type );
1161
1162 case SPF_RESULT_FAIL:
1163 case SPF_RESULT_SOFTFAIL:
1164 case SPF_RESULT_NEUTRAL:
1165 /* No match */
1166 SPF_response_free(spf_response_subr);
1167 break;
1168
1169 case SPF_RESULT_TEMPERROR:
1170 /* Generate TempError */
1171 err = SPF_response_errcode(spf_response_subr);
1172 SPF_FREE_LOOKUP_DATA();
1173 SPF_response_free(spf_response_subr);
1174 return DONE_TEMPERR( err );
1175
1176 case SPF_RESULT_NONE:
1177 /* Generate PermError */
1178 SPF_FREE_LOOKUP_DATA();
1179 SPF_response_free(spf_response_subr);
1180 return DONE_PERMERR(SPF_E_INCLUDE_RETURNED_NONE);
1181 case SPF_RESULT_PERMERROR:
1182 case SPF_RESULT_INVALID:
1183 /* Generate PermError */
1184 err = SPF_response_errcode(spf_response_subr);
1185 SPF_FREE_LOOKUP_DATA();
1186 SPF_response_free(spf_response_subr);
1187 return DONE_PERMERR( err );
1188
1189 }
1190 #if 0
1191 SPF_FREE_LOOKUP_DATA();
1192 return err; /* The sub-interpret called done() */
1193 #endif
1194 }
1195
1196 break;
1197
1198 case MECH_IP4:
1199 memcpy(&addr4, SPF_mech_ip4_data(mech), sizeof(addr4));
1200 if ( SPF_i_match_ip4( spf_server, spf_request, mech, addr4 ) ) {
1201 SPF_FREE_LOOKUP_DATA();
1202 return DONE_MECH( mech->prefix_type );
1203 }
1204 break;
1205
1206 case MECH_IP6:
1207 memcpy(&addr6, SPF_mech_ip6_data(mech), sizeof(addr6));
1208 if ( SPF_i_match_ip6( spf_server, spf_request, mech, addr6 ) ) {
1209 SPF_FREE_LOOKUP_DATA();
1210 return DONE_MECH( mech->prefix_type );
1211 }
1212 break;
1213
1214 case MECH_EXISTS:
1215 SPF_ADD_DNS_MECH();
1216
1217 err = SPF_record_expand_data(spf_server,
1218 spf_request, spf_response,
1219 SPF_mech_data(mech),SPF_mech_data_len(mech),
1220 &buf, &buf_len);
1221 if (err != SPF_E_SUCCESS) {
1222 SPF_FREE_LOOKUP_DATA();
1223 return DONE_TEMPERR( err );
1224 }
1225 lookup = buf;
1226
1227 rr_a = SPF_dns_lookup(resolver, lookup, ns_t_a, FALSE );
1228
1229 if ( spf_server->debug )
1230 SPF_debugf( "found %d A records for %s (herrno: %d)",
1231 rr_a->num_rr, lookup, rr_a->herrno );
1232
1233 if( rr_a->herrno == TRY_AGAIN ) {
1234 SPF_dns_rr_free(rr_a);
1235 SPF_FREE_LOOKUP_DATA();
1236 return DONE_TEMPERR(SPF_E_DNS_ERROR);
1237 }
1238 if ( rr_a->num_rr > 0 ) {
1239 SPF_dns_rr_free(rr_a);
1240 SPF_FREE_LOOKUP_DATA();
1241 return DONE_MECH(mech->prefix_type);
1242 }
1243
1244 SPF_dns_rr_free(rr_a);
1245 break;
1246
1247 case MECH_ALL:
1248 SPF_FREE_LOOKUP_DATA();
1249 if (mech->prefix_type == PREFIX_UNKNOWN)
1250 return DONE_PERMERR(SPF_E_UNKNOWN_MECH);
1251 return DONE_MECH(mech->prefix_type);
1252 break;
1253
1254 default:
1255 SPF_FREE_LOOKUP_DATA();
1256 return DONE_PERMERR(SPF_E_UNKNOWN_MECH);
1257 break;
1258 }
1259
1260 /*
1261 * execute the local policy
1262 */
1263
1264 if ( mech == local_policy ) {
1265 err = SPF_record_interpret(spf_server->local_policy,
1266 spf_request, spf_response, depth + 1);
1267
1268 if ( spf_server->debug > 0 )
1269 SPF_debugf( "local_policy: executed SPF record: %s result: %s reason: %s",
1270 SPF_strerror( err ),
1271 SPF_strresult( spf_response->result ),
1272 SPF_strreason( spf_response->reason ) );
1273
1274 if (spf_response->result != SPF_RESULT_INVALID) {
1275 SPF_FREE_LOOKUP_DATA();
1276 return err;
1277 }
1278 }
1279
1280 mech = SPF_mech_next( mech );
1281 }
1282
1283 SPF_FREE_LOOKUP_DATA();
1284 /* falling off the end is the same as ?all */
1285 return DONE( SPF_RESULT_NEUTRAL, SPF_REASON_DEFAULT, SPF_E_SUCCESS );
1286 }