1 /********************************************************************
2 ** OPENDMARC_SPF.C -- Process the spf record of the inbound message
3 **********************************************************************/
4 # include "opendmarc_internal.h"
5
6 /* libbsd if found */
7 #ifdef USE_BSD_H
8 # include <bsd/string.h>
9 #endif /* USE_BSD_H */
10
11 /* libstrl if needed */
12 #ifdef USE_STRL_H
13 # include <strl.h>
14 #endif /* USE_STRL_H */
15
16 /* opendmarc_strl if needed */
17 #ifdef USE_DMARCSTRL_H
18 # include <opendmarc_strl.h>
19 #endif /* USE_DMARCSTRL_H */
20
21 # include "dmarc.h"
22
23 #if WITH_SPF
24
25 #if HAVE_SPF2_H
26 // Here we have spf.h, so libspf2 is available.
27
28 SPF_CTX_T *
opendmarc_spf2_alloc_ctx()29 opendmarc_spf2_alloc_ctx()
30 {
31 SPF_CTX_T *spfctx = NULL;
32
33 spfctx = malloc(sizeof(SPF_CTX_T));
34 if (spfctx == NULL)
35 return NULL;
36 (void) memset(spfctx, '\0', sizeof(SPF_CTX_T));
37 spfctx->spf_server = SPF_server_new(SPF_DNS_CACHE, 0);
38 spfctx->spf_request = SPF_request_new(spfctx->spf_server);
39 return spfctx;
40 }
41
42 SPF_CTX_T *
opendmarc_spf2_free_ctx(SPF_CTX_T * spfctx)43 opendmarc_spf2_free_ctx(SPF_CTX_T *spfctx)
44 {
45 if (spfctx == NULL)
46 return spfctx;
47
48 if (spfctx->spf_response != NULL)
49 SPF_response_free(spfctx->spf_response);
50 if (spfctx->spf_request != NULL)
51 SPF_request_free(spfctx->spf_request);
52 if (spfctx->spf_server != NULL)
53 SPF_server_free(spfctx->spf_server);
54 (void) free(spfctx);
55 spfctx = NULL;
56 return spfctx;
57 }
58
59 int
opendmarc_spf2_find_mailfrom_domain(SPF_CTX_T * spfctx,char * raw_address,char * mailfrom,size_t mailfrom_len,int * use_flag)60 opendmarc_spf2_find_mailfrom_domain(SPF_CTX_T *spfctx, char *raw_address, char *mailfrom, size_t mailfrom_len, int *use_flag)
61 {
62 char copy[sizeof spfctx->mailfrom_addr];
63 char *cp;
64 char *ep;
65
66 if (use_flag != NULL)
67 *use_flag = FALSE;
68
69 if (spfctx == NULL)
70 return EINVAL;
71
72 if (mailfrom == NULL || raw_address == NULL)
73 return EINVAL;
74
75 (void) memset(copy, '\0', sizeof copy);
76 (void) strlcpy(copy, raw_address, sizeof copy);
77
78 cp = strrchr(copy, '<');
79 if (cp == NULL)
80 cp = copy;
81 else
82 ++cp;
83 ep = strchr(cp, '>');
84 if (ep != NULL)
85 *ep = '\0';
86
87 ep = strchr(cp, '@');
88 if (ep != NULL)
89 {
90 cp = ep+1;
91 if (use_flag != NULL)
92 *use_flag = TRUE;
93 }
94
95 if (strcasecmp(cp, "MAILER_DAEMON") == 0)
96 cp = "";
97
98 (void) memset(mailfrom, '\0', mailfrom_len);
99 (void) strlcpy(mailfrom, cp, mailfrom_len);
100 return 0;
101 }
102
103 int
opendmarc_spf2_specify_ip_address(SPF_CTX_T * spfctx,char * ip_address,size_t ip_address_len)104 opendmarc_spf2_specify_ip_address(SPF_CTX_T *spfctx, char *ip_address, size_t ip_address_len)
105 {
106 if (spfctx == NULL)
107 return EINVAL;
108
109 if (ip_address == NULL)
110 return EINVAL;
111
112 /*
113 * we don't care at this point if it is ipv6 or ipv4
114 */
115 SPF_request_set_ipv4_str(spfctx->spf_request, ip_address);
116 SPF_request_set_ipv6_str(spfctx->spf_request, ip_address);
117 return 0;
118 }
119
120 int
opendmarc_spf2_test(char * ip_address,char * mail_from_domain,char * helo_domain,char * spf_record,int softfail_okay_flag,char * human_readable,size_t human_readable_len,int * used_mfrom)121 opendmarc_spf2_test(char *ip_address, char *mail_from_domain, char *helo_domain, char *spf_record, int softfail_okay_flag, char *human_readable, size_t human_readable_len, int *used_mfrom)
122 {
123 SPF_CTX_T * ctx;
124 int ret;
125 char xbuf[BUFSIZ];
126 char helo[512];
127 char mfrom[512];
128
129 if (used_mfrom != NULL)
130 *used_mfrom = FALSE;
131
132 (void) memset(xbuf, '\0', sizeof xbuf);
133 ctx = opendmarc_spf2_alloc_ctx();
134 if (ctx == NULL)
135 {
136 if (human_readable != NULL)
137 (void) strlcpy(human_readable, strerror(errno), human_readable_len);
138 return DMARC_POLICY_SPF_OUTCOME_TMPFAIL;
139 }
140
141 if (ip_address == NULL)
142 {
143 if (human_readable != NULL)
144 (void) strlcpy(human_readable, "No IP address available", human_readable_len);
145 ctx = opendmarc_spf2_free_ctx(ctx);
146 return DMARC_POLICY_SPF_OUTCOME_FAIL;
147 }
148
149 if (mail_from_domain == NULL && helo_domain == NULL)
150 {
151 if (human_readable != NULL)
152 (void) strlcpy(human_readable, "No Domain name available to check", human_readable_len);
153 ctx = opendmarc_spf2_free_ctx(ctx);
154 return DMARC_POLICY_SPF_OUTCOME_FAIL;
155 }
156
157 ret = opendmarc_spf2_specify_ip_address(ctx, ip_address, strlen(ip_address));
158 if (ret != 0)
159 {
160 if (human_readable != NULL)
161 (void) strlcpy(human_readable, strerror(errno), human_readable_len);
162 ctx = opendmarc_spf2_free_ctx(ctx);
163 return DMARC_POLICY_SPF_OUTCOME_TMPFAIL;
164 }
165
166 ret = opendmarc_spf2_find_mailfrom_domain(ctx, mail_from_domain, mfrom, sizeof mfrom, used_mfrom);
167 if (ret != 0 || *used_mfrom == FALSE)
168 {
169 (void) strlcpy(helo, helo_domain, sizeof helo);
170 SPF_request_set_helo_dom(ctx->spf_request, helo);
171 }
172 else
173 {
174 SPF_request_set_env_from(ctx->spf_request, mfrom);
175 }
176 ctx->spf_response = NULL;
177 SPF_request_query_mailfrom(ctx->spf_request, &(ctx->spf_response));
178
179 if (human_readable != NULL)
180 (void) strlcpy(human_readable, SPF_strresult(SPF_response_result(ctx->spf_response)), human_readable_len);
181 ctx->spf_result = SPF_response_result(ctx->spf_response);
182 ret = (int) ctx->spf_result;
183 ctx = opendmarc_spf2_free_ctx(ctx);
184
185 if (ret != SPF_RESULT_PASS)
186 {
187 switch (ret)
188 {
189 case SPF_RESULT_NONE:
190 return DMARC_POLICY_SPF_OUTCOME_NONE;
191
192 case SPF_RESULT_NEUTRAL:
193 case SPF_RESULT_SOFTFAIL:
194 if (softfail_okay_flag == TRUE)
195 return DMARC_POLICY_SPF_OUTCOME_PASS;
196 else
197 return DMARC_POLICY_SPF_OUTCOME_FAIL;
198 break;
199 case SPF_RESULT_TEMPERROR:
200 return DMARC_POLICY_SPF_OUTCOME_TMPFAIL;
201 }
202 return DMARC_POLICY_SPF_OUTCOME_FAIL;
203 }
204 return DMARC_POLICY_SPF_OUTCOME_PASS;
205 }
206
207 #else /* HAVE_SPF2_H */
208
209 // No spf.h so no libspf2 to use so we use the internal spf check.
210 #ifndef TRUE
211 # define TRUE (1)
212 #endif
213
214 #ifndef FALSE
215 # define FALSE (0)
216 #endif
217
218 #ifndef MAXDNSHOSTNAME
219 # define MAXDNSHOSTNAME (256)
220 #endif
221 #define SPF_MAX_SPF_RECORD_LEN (4096)
222
223 #define SPF_IN_TOKEN_NONE (0)
224 #define SPF_IN_TOKEN_VERSION (1)
225 #define SPF_IN_TOKEN_A (2)
226 #define SPF_IN_TOKEN_MX (3)
227 #define SPF_IN_TOKEN_IP4 (4)
228 #define SPF_IN_TOKEN_IP6 (5)
229 #define SPF_IN_TOKEN_PTR (6)
230 #define SPF_IN_TOKEN_INCLUDE (7)
231 #define SPF_IN_TOKEN_REDIRECT (8)
232 #define SPF_IN_TOKEN_EXISTS (9)
233 #define SPF_IN_TOKEN_EXP (10)
234
235 const char *
opendmarc_spf_status_to_msg(SPF_CTX_T * spfctx,int status)236 opendmarc_spf_status_to_msg(SPF_CTX_T *spfctx, int status)
237 {
238 const char *r;
239
240 if (status != 0 && spfctx != NULL && spfctx->did_get_exp)
241 return spfctx->exp_buf;
242
243 switch (status)
244 {
245 #define SPF_RETURN_UNDECIDED (-1)
246 case SPF_RETURN_UNDECIDED:
247 r = "Undecided";
248 break;
249
250 #define SPF_RETURN_OK_PASSED (0)
251 case SPF_RETURN_OK_PASSED:
252 r = "Passed";
253 break;
254
255 #define SPF_RETURN_INTERNAL (5)
256 case SPF_RETURN_INTERNAL:
257 r = "No Domain To Check";
258 break;
259
260 #define SPF_RETURN_RECORD_TOOLONG (6)
261 case SPF_RETURN_RECORD_TOOLONG:
262 r = "Record Too Big";
263 break;
264 #define SPF_RETURN_BAD_SYNTAX_VERSION (7)
265 case SPF_RETURN_BAD_SYNTAX_VERSION:
266 r = "Bad Version";
267 break;
268 #define SPF_RETURN_A_BUT_NO_A_RECORD (8)
269 case SPF_RETURN_A_BUT_NO_A_RECORD:
270 r = "Required 'A' lookup failed to find 'A' records";
271 break;
272 #define SPF_RETURN_DASH_FORCED_HARD_FAIL (9)
273 case SPF_RETURN_DASH_FORCED_HARD_FAIL:
274 r = "Required 'A' bu no 'A' records with -a specified";
275 break;
276 #define SPF_RETURN_A_BUT_BAD_SYNTAX (10)
277 case SPF_RETURN_A_BUT_BAD_SYNTAX:
278 r = "IP Address Badly Formed";
279 break;
280 #define SPF_RETURN_BAD_SYNTAX_INCLUDE (11)
281 case SPF_RETURN_BAD_SYNTAX_INCLUDE:
282 r = "'INCLUDE' Syntax Error";
283 break;
284 #define SPF_RETURN_INCLUDE_NO_DOMAIN (12)
285 case SPF_RETURN_INCLUDE_NO_DOMAIN:
286 r = "'INCLUDE' Domain Lookup Failed";
287 break;
288 #define SPF_RETURN_BAD_SYNTAX_REDIRECT (13)
289 case SPF_RETURN_BAD_SYNTAX_REDIRECT:
290 r = "'REDIRECT' Syntax Error";
291 break;
292 #define SPF_RETURN_REDIRECT_NO_DOMAIN (14)
293 case SPF_RETURN_REDIRECT_NO_DOMAIN:
294 r = "'REDIRECT' Domain Lookup Failed";
295 break;
296 #define SPF_RETURN_DASH_ALL_HARD_FAIL (15)
297 case SPF_RETURN_DASH_ALL_HARD_FAIL:
298 r = "Hard Fail: Reject";
299 break;
300 #define SPF_RETURN_TILDE_ALL_SOFT_FAIL (16)
301 case SPF_RETURN_TILDE_ALL_SOFT_FAIL:
302 r = "Soft Fail: Subject to Policy";
303 break;
304 #define SPF_RETURN_QMARK_ALL_NEUTRAL (17)
305 case SPF_RETURN_QMARK_ALL_NEUTRAL:
306 r = "Neutral Fail: Subject to Policy";
307 break;
308 #define SPF_RETURN_UNKNOWN_KEYWORD (18)
309 case SPF_RETURN_UNKNOWN_KEYWORD:
310 r = "Unrecognized Keyword";
311 break;
312 #define SPF_RETURN_BAD_MACRO_SYNTAX (19)
313 case SPF_RETURN_BAD_MACRO_SYNTAX:
314 r = "Macros Used But Syntax Bad";
315 break;
316 #define SPF_RETURN_NOT_EXISTS_HARDFAIL (20)
317 case SPF_RETURN_NOT_EXISTS_HARDFAIL:
318 r = "'A' Record lookup, No Such Host";
319 break;
320 #define SPF_RETURN_BAD_SYNTAX_EXISTS (21)
321 case SPF_RETURN_BAD_SYNTAX_EXISTS:
322 r = "'EXISTS' Omitted A domain";
323 break;
324 #define SPF_RETURN_BAD_SYNTAX_EXP (22)
325 case SPF_RETURN_BAD_SYNTAX_EXP:
326 r = "'EXP' Bad Syntax";
327 break;
328 #define SPF_RETURN_NOT_EXP_HARDFAIL (23)
329 case SPF_RETURN_NOT_EXP_HARDFAIL:
330 r = "'-EXP' Hard Failure";
331 break;
332 #define SPF_RETURN_TOO_MANY_DNS_QUERIES (24)
333 case SPF_RETURN_TOO_MANY_DNS_QUERIES:
334 r = "Too Many DNS Lookups Without Success.";
335 break;
336 default:
337 #define SPF_RETURN_INTERNAL_ERROR (25)
338 r = "Undefined Internal Error";
339 break;
340 }
341 return r;
342 }
343
344 /****************************************************************
345 ** SPF_STATUS_TO_PASS -- convert ctx->status into a decision
346 ** Returns 1 for pass
347 ** Returns 0 for fail
348 ** Returns -1 for maybe fail (~all means you decide)
349 ****************************************************************/
350 int
opendmarc_spf_status_to_pass(int status,int none_pass)351 opendmarc_spf_status_to_pass(int status, int none_pass)
352 {
353 int r;
354
355 switch (status)
356 {
357 case SPF_RETURN_UNDECIDED:
358 if (none_pass == 1)
359 r = 1;
360 else
361 r = 0;
362 break;
363 case SPF_RETURN_OK_PASSED:
364 r = 1;
365 break;
366 case SPF_RETURN_INTERNAL:
367 r = 0;
368 break;
369 case SPF_RETURN_RECORD_TOOLONG:
370 r = 0;
371 break;
372 case SPF_RETURN_BAD_SYNTAX_VERSION:
373 r = 0;
374 break;
375 case SPF_RETURN_A_BUT_NO_A_RECORD:
376 r = 0;
377 break;
378 case SPF_RETURN_DASH_FORCED_HARD_FAIL:
379 r = 0;
380 break;
381 case SPF_RETURN_A_BUT_BAD_SYNTAX:
382 r = 0;
383 break;
384 case SPF_RETURN_BAD_SYNTAX_INCLUDE:
385 r = 0;
386 break;
387 case SPF_RETURN_INCLUDE_NO_DOMAIN:
388 r = 0;
389 break;
390 case SPF_RETURN_BAD_SYNTAX_REDIRECT:
391 r = 0;
392 break;
393 case SPF_RETURN_REDIRECT_NO_DOMAIN:
394 r = 0;
395 break;
396 case SPF_RETURN_DASH_ALL_HARD_FAIL:
397 r = 0;
398 break;
399 case SPF_RETURN_TILDE_ALL_SOFT_FAIL:
400 r = -1;
401 break;
402 case SPF_RETURN_QMARK_ALL_NEUTRAL:
403 r = 1;
404 break;
405 case SPF_RETURN_UNKNOWN_KEYWORD:
406 r = 0;
407 break;
408 case SPF_RETURN_BAD_MACRO_SYNTAX:
409 r = 0;
410 break;
411 case SPF_RETURN_NOT_EXISTS_HARDFAIL:
412 r = 0;
413 break;
414 case SPF_RETURN_BAD_SYNTAX_EXISTS:
415 r = 0;
416 break;
417 case SPF_RETURN_BAD_SYNTAX_EXP:
418 r = 0;
419 break;
420 case SPF_RETURN_TOO_MANY_DNS_QUERIES:
421 r = 0;
422 break;
423 case SPF_RETURN_INTERNAL_ERROR:
424 r = 0;
425 break;
426 default:
427 r = 0;
428 break;
429 }
430 return r;
431 }
432
433 /*
434 ** OPENDMARC_SPF_CIDR_ADDRESS -- see if an IP address is covered by a CIDR
435 ** expression
436 **
437 ** Parameters:
438 ** ip -- IP address to test, in network byte order
439 ** cidr_addr -- CIDR expression to which to compare it
440 **
441 ** Return value:
442 ** TRUE iff "ip" is inside (or equal to) "cidr_addr".
443 */
444
445 int
opendmarc_spf_cidr_address(uint32_t ip,char * cidr_addr)446 opendmarc_spf_cidr_address(uint32_t ip, char *cidr_addr)
447 {
448 char *cidr;
449 char *cp, *ep;
450 char buf[BUFSIZ];
451 uint32_t i;
452 uint32_t bits;
453 uint32_t mask;
454 uint32_t high, low;
455 struct sockaddr_in sin;
456
457 if (cidr_addr == NULL)
458 return FALSE;
459
460 (void) memset(buf, '\0', sizeof buf);
461 (void) strlcpy(buf, cidr_addr, sizeof buf);
462
463 cidr = strchr(buf, '/');
464 if (cidr == NULL)
465 {
466 if (inet_aton(cidr_addr, &sin.sin_addr) != 0)
467 {
468 (void)memcpy(&low, &sin.sin_addr.s_addr, sizeof(sin.sin_addr.s_addr));
469 (void)memcpy(&high, &sin.sin_addr.s_addr, sizeof(sin.sin_addr.s_addr));
470 if (ip >= low && ip <= high)
471 return TRUE;
472 }
473 return FALSE;
474 }
475 *cidr++ = '\0';
476 bits = strtoul(cidr, NULL, 10);
477
478 cp = buf;
479 ep = strchr(buf, '.');
480 if (ep == NULL)
481 return FALSE;
482 *ep++ = '\0';
483 i = strtoul(cp, NULL, 10) << 24;
484
485 cp = ep;
486 ep = strchr(cp, '.');
487 if (ep == NULL)
488 return FALSE;
489 *ep++ = '\0';
490 i += strtoul(cp, NULL, 10) << 16;
491
492 cp = ep;
493 ep = strchr(cp, '.');
494 if (ep == NULL)
495 return FALSE;
496 *ep++ = '\0';
497 i += strtoul(cp, NULL, 10) << 8;
498
499 cp = ep;
500 i += strtoul(cp, NULL, 10);
501
502 mask = (bits == 0) ? 0 : ~(u_long)0 << (32 - bits);
503
504 low = i & mask;
505 high = i | (~mask & 0xFFFFFFFF);
506
507 ip = ntohl(ip);
508 if (ip >= low && ip <= high)
509 return TRUE;
510 return FALSE;
511 }
512
513 /**************************************************************
514 ** opendmarc_spf_reverse -- Reverse doman name on dot or ip address
515 ** on dot or colon boundaries
516 ** e.g. a.b.c becomes c.b.a
517 ** and FFFF::EEEE becomes EEEE::FFFF
518 ** and 12.34.56.78 becomes 78.56.34.12
519 ** Input:
520 ** str -- the string to reverse
521 ** buf -- buffer to hold the reversed string
522 ** buflen -- size in bytes of the buffer
523 ** Returns:
524 ** NULL On error
525 ** buf On success
526 ** Side Effects:
527 ** Overwrites previous contents of buf
528 ****************************************************************/
529 static char *
opendmarc_spf_reverse(char * str,char * buf,size_t buflen)530 opendmarc_spf_reverse(char *str, char *buf, size_t buflen)
531 {
532 char * sp;
533 char * ep;
534 char * dotp;
535 int dotorcolon = 0;
536 char dotorcolon_str[2];
537 char dupe[BUFSIZ];
538
539 if (str == NULL || buf == NULL || buflen == 0)
540 return NULL;
541 if (buflen > BUFSIZ)
542 buflen = BUFSIZ;
543
544 (void) memset(buf, '\0', buflen);
545 (void) memset(dupe, '\0', buflen + 1);
546 (void) strlcpy(dupe, str, buflen + 1);
547
548 dotp = strchr(dupe, '.');
549 if (dotp != NULL)
550 dotorcolon = '.';
551 else
552 {
553 dotp = strchr(dupe, ':');
554 if (dotp != NULL)
555 dotorcolon = ':';
556 }
557 if (dotorcolon == 0)
558 return NULL;
559
560 dotorcolon_str[0] = dotorcolon;
561 dotorcolon_str[1] = '\0';
562
563 ep = dupe + strlen(dupe);
564 /*
565 * strip tailing dotorcolons.
566 */
567 do
568 {
569 for (sp = ep; sp >= dupe; --sp)
570 if (*sp == dotorcolon)
571 break;
572 if (sp < dupe)
573 {
574 strlcat(buf, sp+1, buflen);
575 break;
576 }
577 ep = sp;
578 if (*sp == dotorcolon)
579 ++sp;
580 if (*sp != '\0')
581 strlcat(buf, sp, buflen);
582 if (*ep == dotorcolon)
583 {
584 strlcat(buf, dotorcolon_str, buflen);
585 *ep = '\0';
586 --ep;
587 }
588 } while (sp >= dupe);
589 return buf;
590 }
591
592 typedef struct {
593 u_int values[8];
594 int nvalues;
595 } INT_ARY_T;
596
597 typedef struct {
598 char strs[8][32];
599 int nstrs;
600 } TXT_ARY_T;
601
602
603 static int
opendmarc_spf_ipv6_cidr_populate(TXT_ARY_T * lp,INT_ARY_T * ap)604 opendmarc_spf_ipv6_cidr_populate(TXT_ARY_T *lp, INT_ARY_T *ap)
605 {
606 int i, f;
607 int dots;
608
609 /*
610 * Populate the base, expected variations are:
611 * ::ipv4
612 * :FFFF:ipv4
613 * :hex::hex: ...
614 */
615
616 /* Clear to zero in case we got an auto array */
617 for (i = 0; i < 8; ++i)
618 ap->values[i] = 0;
619
620 f = 0; /* index into the output array of values. */
621 for (i = 0; i < lp->nstrs; ++i)
622 {
623 int value;
624 int value2;
625 char *cp = NULL;
626
627 dots = 0;
628 if (i == 0)
629 {
630 char *dp;
631
632 /*
633 * The rightmost address might be IPv4
634 */
635 for (dp = lp->strs[0]; *dp != '\0'; ++dp)
636 if (*dp == '.')
637 ++dots;
638 }
639
640 if (i >= 0 || dots == 0)
641 {
642 /*
643 * No dots or not the rightmost value, then
644 * a hexedecimal value.
645 */
646 if (lp->strs[i] != NULL)
647 {
648 ap->values[f] = strtoul(lp->strs[i], NULL, 16);
649 f = f + 1;
650 }
651 continue;
652 }
653
654 /*
655 * From here down deals with the special case of
656 * an ipv4 address at the righthand side.
657 */
658 if (dots > 3)
659 {
660 return errno = EINVAL;
661 }
662 if (dots)
663 {
664 cp = strrchr(lp->strs[0], '.');
665 value2 = strtoul(cp+1, NULL, 10);
666
667 for (cp = cp-1; cp > lp->strs[0]; --cp)
668 if (*cp == '.')
669 break;
670 if (*cp == '.')
671 value = strtoul(cp+1, NULL, 10);
672 else
673 value = strtoul(cp, NULL, 10);
674 value <<= 8;
675 value &= 0xFFFF;
676 value += value2;
677 ap->values[0] = value;
678 ap->values[1] = 0;
679 }
680 if (dots > 1)
681 {
682 for (cp = cp-1; cp > lp->strs[0]; --cp)
683 if (*cp == '.')
684 break;
685 if (*cp == '.')
686 value = strtoul(cp+1, NULL, 10);
687 else
688 value = strtoul(cp+1, NULL, 10);
689 ap->values[1] = value;
690 }
691 if (dots > 2)
692 {
693 cp = lp->strs[0];
694 value = strtoul(cp, NULL, 10);
695 value <<= 8;
696 ap->values[1] += value;
697 }
698 f += 2;
699 continue;
700 }
701 ap->nvalues = f;
702 return 0;
703 }
704
705 static int
opendmarc_spf_ipv6_explode(char * str,TXT_ARY_T * ap)706 opendmarc_spf_ipv6_explode(char *str, TXT_ARY_T *ap)
707 {
708 char *cp, *ep;
709 int i;
710 int ncolons;
711 char copy[128];
712
713 if (str == NULL || ap == NULL)
714 return errno = EINVAL;
715
716 (void) memset(ap, '\0', sizeof(TXT_ARY_T));
717 (void) memset(copy, '\0', sizeof copy);
718 (void) strlcpy(copy, str, sizeof copy);
719
720 ncolons = 0;
721 for (cp = copy; *cp != '\0'; ++cp)
722 if (*cp == ':')
723 ++ncolons;
724 ncolons = 7 - ncolons;
725
726 cp = copy;
727 for (i = 7; i >= 0; i--)
728 {
729 ep = strchr(cp, ':');
730 if (ep != NULL)
731 *ep = '\0';
732 if (strlen(cp) == 0)
733 {
734 (void) strlcpy((char *)ap->strs[i], "0", sizeof ap->strs[i]);
735 }
736 else
737 {
738 (void) strlcpy((char *)ap->strs[i], cp, sizeof ap->strs[i]);
739 }
740 if (ep && *(ep + 1) == ':' && ncolons > 0)
741 {
742 for (i--; i >= 0 && ncolons != 0; --ncolons, --i)
743 {
744 (void) strlcpy((char *)ap->strs[i], "0", sizeof ap->strs[i]);
745 }
746 i+= 1;
747 }
748 cp = ep+1;
749 }
750 ap->nstrs = 8 - i;
751 return 0;
752 }
753
754 int
opendmarc_spf_ipv6_cidr_check(char * ipv6_str,char * cidr_string)755 opendmarc_spf_ipv6_cidr_check(char *ipv6_str, char *cidr_string)
756 {
757 int cidr_bits;
758 TXT_ARY_T ipv6_ary;
759 TXT_ARY_T cidr_ary;
760 INT_ARY_T base_iary;
761 INT_ARY_T low_iary;
762 INT_ARY_T hi_iary;
763 INT_ARY_T ip_iary;
764 char * cp;
765 int ret;
766 int i;
767 int taghi, taglo;
768 char cidr_str[256];
769
770 if (ipv6_str == NULL || cidr_string == NULL)
771 {
772 return FALSE;
773 }
774
775 if (strchr(ipv6_str, ':') == NULL)
776 return FALSE;
777 if (strchr(cidr_string, ':') == NULL)
778 return FALSE;
779
780 (void) strlcpy(cidr_str, cidr_string, sizeof cidr_str);
781
782 cp = strchr(cidr_str, '/');
783 if (cp == NULL)
784 {
785 cidr_bits = 0;
786 }
787 else
788 {
789 cidr_bits = strtoul(cp+1, NULL, 10);
790 *cp = '\0';
791 cp = strchr(cidr_str, ':');
792 if (cp == NULL)
793 cidr_bits = 32 - cidr_bits;
794 else
795 cidr_bits = 128 - cidr_bits;
796 }
797
798 ret = opendmarc_spf_ipv6_explode(ipv6_str, &ipv6_ary);
799 if (ret != 0)
800 {
801 return FALSE;
802 }
803
804 ret = opendmarc_spf_ipv6_explode(cidr_str, &cidr_ary);
805 if (ret != 0)
806 {
807 return FALSE;
808 }
809
810 ret = opendmarc_spf_ipv6_cidr_populate(&cidr_ary, &base_iary);
811 if (ret != 0)
812 {
813 return FALSE;
814 }
815 ret = opendmarc_spf_ipv6_cidr_populate(&ipv6_ary, &ip_iary);
816 if (ret != 0)
817 {
818 return FALSE;
819 }
820
821 if (cidr_bits == 0)
822 {
823 /*
824 * Requre an exact match.
825 */
826 for (i = 0; i < base_iary.nvalues; i++)
827 {
828 if (base_iary.values[i] != ip_iary.values[i])
829 {
830 return FALSE;
831 }
832 }
833 return TRUE;
834 }
835
836 (void) memcpy(&low_iary, &base_iary, sizeof(INT_ARY_T));
837 (void) memcpy(&hi_iary, &base_iary, sizeof(INT_ARY_T));
838
839 for (i = 0; i < 8; i++)
840 {
841 int twobyte_mask, tmp_mask;
842
843 if (cidr_bits >= 16)
844 {
845 low_iary.values[i] = 0;
846 hi_iary.values[i] = 0xFFFF;
847 cidr_bits = cidr_bits - 16;
848 continue;
849 }
850 twobyte_mask = cidr_bits % 16;
851 tmp_mask = (0xFFFF << twobyte_mask);
852 low_iary.values[i] = low_iary.values[i] & tmp_mask;
853
854 tmp_mask = ((~tmp_mask) & 0xFFFF);
855 hi_iary.values[i] = hi_iary.values[i] | tmp_mask;
856 if (cidr_bits < 16)
857 break;
858 cidr_bits = cidr_bits - 16;
859 }
860
861 taghi = FALSE;
862 taglo = FALSE;
863
864 for (i = 7; i >= 0; --i)
865 {
866 if (ip_iary.values[i] == low_iary.values[i] && ip_iary.values[i] == hi_iary.values[i])
867 {
868 continue;
869 }
870 if (ip_iary.values[i] == hi_iary.values[i])
871 {
872 taghi = TRUE;
873 continue;
874 }
875 if (ip_iary.values[i] == low_iary.values[i])
876 {
877 taglo = TRUE;
878 continue;
879 }
880 if (taghi == TRUE)
881 {
882 if (ip_iary.values[i] > hi_iary.values[i])
883 {
884 return FALSE;
885 }
886 continue;
887 }
888 if (taglo == TRUE)
889 {
890 if (ip_iary.values[i] < low_iary.values[i])
891 {
892 return FALSE;
893 }
894 continue;
895 }
896 if (ip_iary.values[i] < low_iary.values[i] || ip_iary.values[i] > hi_iary.values[i])
897 {
898 return FALSE;
899 }
900 }
901 return TRUE;
902 }
903
904
905 /******************************************************************
906 ** SPF_STRIP_DOTS -- Remove trailing and leading dots from
907 ** a domain name.
908 ******************************************************************/
909 static char *
opendmarc_spf_strip_dots(char * str,char * dot,char * buf,size_t buflen)910 opendmarc_spf_strip_dots(char *str, char *dot, char *buf, size_t buflen)
911 {
912 char *cp;
913 char dupe[BUFSIZ];
914
915 if (buflen > BUFSIZ)
916 buflen = BUFSIZ;
917 if (str == NULL || buf == NULL || buflen == 0)
918 return NULL;
919 (void) memset(buf, '\0', buflen);
920 (void) memset(dupe, '\0', buflen);
921 (void) strlcpy(dupe, str, buflen);
922
923 for (cp = dupe + strlen(dupe) - 1; cp > dupe; --cp)
924 {
925 if (*cp == '.')
926 *cp = '\0';
927 else
928 break;
929 }
930 for (cp = dupe; *cp != '\0'; ++cp)
931 {
932 if (*cp != '.')
933 break;
934 }
935 (void) strlcpy(buf, cp, buflen);
936 return buf;
937 }
938
939
940
941 int
opendmarc_spf_subdomain(char * dom,char * sub)942 opendmarc_spf_subdomain(char *dom, char *sub)
943 {
944 char dcopy[MAXDNSHOSTNAME];
945 char scopy[MAXDNSHOSTNAME];
946 char scratch[MAXDNSHOSTNAME];
947 char *cp;
948 int dlen;
949 int slen;
950
951 if (dom == NULL || sub == NULL)
952 return FALSE;
953 (void) memset(dcopy, '\0', sizeof dcopy);
954 (void) memset(scopy, '\0', sizeof scopy);
955 (void) memset(scratch, '\0', sizeof scratch);
956
957 cp = opendmarc_spf_strip_dots(dom, ".", scratch, sizeof scratch);
958 if (cp == NULL)
959 return FALSE;
960 cp = opendmarc_spf_reverse(scratch, dcopy, sizeof dcopy);
961 if (cp == NULL)
962 return FALSE;
963
964 cp = opendmarc_spf_strip_dots(sub, ".", scratch, sizeof scratch);
965 if (cp == NULL)
966 return FALSE;
967 cp = opendmarc_spf_reverse(scratch, scopy, sizeof scopy);
968 if (cp == NULL)
969 return FALSE;
970
971 (void) strlcat(dcopy, ".", sizeof dcopy);
972 (void) strlcat(scopy, ".", sizeof scopy);
973
974 dlen = strlen(dcopy);
975 slen = strlen(scopy);
976
977 if (dlen == slen)
978 {
979 if (strcasecmp(dcopy, scopy) == 0)
980 return TRUE;
981 return FALSE;
982 }
983 if (strncasecmp(dcopy, scopy, dlen) == 0)
984 return TRUE;
985 return FALSE;
986 }
987 static int
opendmarc_spf_ptr_domain(SPF_CTX_T * spfctx,char * domain)988 opendmarc_spf_ptr_domain(SPF_CTX_T *spfctx, char *domain)
989 {
990 char ** dry = NULL;
991 int dry_len = 0;
992 char ** dpp;
993 char ** ary = NULL;
994 int ary_len = 0;
995 char ** app;
996 char ** nary = NULL;
997 int nary_len = 0;
998 char ** npp;
999 int good = FALSE;;
1000
1001 if (spfctx->validated_domain[0] != '\0')
1002 return TRUE;
1003 dry = opendmarc_spf_dns_lookup_ptr(spfctx->ip_address, dry, &dry_len);
1004
1005 /*
1006 * There can be muiltple host names returned.
1007 */
1008 for (dpp = dry; dpp != NULL && *dpp != NULL; ++dpp)
1009 {
1010 ary = opendmarc_spf_dns_lookup_a(*dpp, ary, &ary_len);
1011 if (ary == NULL)
1012 continue;
1013
1014 /*
1015 * Compare the addresses returned for that host name
1016 * to the specified IP address and if it compares
1017 * save the host name for later.
1018 */
1019 for (app = ary; app != NULL && *app != NULL; ++app)
1020 {
1021 if (strcasecmp(*app, spfctx->ip_address) == 0)
1022 {
1023 nary = opendmarc_util_pushnargv(*dpp, nary, &nary_len);
1024 break;
1025 }
1026 }
1027 if (nary == NULL)
1028 break;
1029 for (npp = nary; *npp != NULL; ++npp)
1030 {
1031 char *dp;
1032
1033 if (domain == NULL)
1034 dp = spfctx->mailfrom_domain;
1035 else
1036 dp = domain;
1037 if (opendmarc_spf_subdomain(dp, *dpp) == TRUE)
1038 {
1039 (void) strlcpy(spfctx->validated_domain, *dpp, sizeof spfctx->validated_domain);
1040 good = TRUE;
1041 break;
1042 }
1043 }
1044 nary = opendmarc_util_freenargv(nary, &nary_len);
1045 if (good == TRUE)
1046 break;
1047 }
1048 dry = opendmarc_util_freenargv(dry, &dry_len);
1049 return good;
1050 }
1051
1052 static char *
opendmarc_spf_macro_expand(SPF_CTX_T * spfctx,char * str,char * buf,size_t buflen,int is_exp)1053 opendmarc_spf_macro_expand(SPF_CTX_T *spfctx, char *str, char *buf, size_t buflen, int is_exp)
1054 {
1055 char *sp;
1056 char *xp;
1057 char *ep;
1058 char *bp;
1059 char scratch[MAXDNSHOSTNAME];
1060 time_t t;
1061 int num;
1062 int rev;
1063
1064 if (spfctx == NULL || str == NULL || buf == NULL || strlen(str) > buflen)
1065 {
1066 return NULL;
1067 }
1068 sp = str;
1069 ep = str + strlen(str);
1070 (void) memset(buf, '\0', buflen);
1071 bp = buf;
1072
1073 for (sp = str; sp < ep; )
1074 {
1075 if (*sp != '%')
1076 {
1077 *bp++ = *sp++;
1078 continue;
1079 }
1080 ++sp;
1081 switch ((int)*sp)
1082 {
1083 case '%':
1084 *bp++ = *sp++;
1085 continue;
1086 case '_':
1087 *bp++ = ' ';
1088 ++sp;
1089 continue;
1090 case '-':
1091 *bp++ = '%';
1092 *bp++ = '2';
1093 *bp++ = '0';
1094 ++sp;
1095 continue;
1096 case '{':
1097 break;
1098 default:
1099 return NULL;
1100 }
1101 ++sp;
1102 num = 0;
1103 rev = FALSE;
1104 xp = sp+1;
1105 if (*xp == 'r')
1106 {
1107 rev = TRUE;
1108 ++xp;
1109 }
1110 if (isdigit((int)*xp))
1111 {
1112 num = strtoul(xp, &xp, 10);
1113 }
1114 char * cp;
1115 switch ((int)*sp)
1116 {
1117 case 's':
1118 if (rev == TRUE)
1119 (void) opendmarc_spf_reverse(spfctx->mailfrom_domain, scratch, MAXDNSHOSTNAME);
1120 else
1121 (void) strlcpy(scratch, spfctx->mailfrom_domain, MAXDNSHOSTNAME);
1122 if (num > 0 && num < MAXDNSHOSTNAME)
1123 scratch[num] = '\0';
1124 for (cp = scratch; *cp != '\0'; )
1125 *bp++ = *cp++;
1126 break;
1127 case 'l':
1128 (void) strlcpy(scratch, spfctx->mailfrom_addr, MAXDNSHOSTNAME);
1129 cp = strchr(scratch, '@');
1130 if (cp != NULL)
1131 *cp = '\0';
1132 if (num > 0 && num < MAXDNSHOSTNAME)
1133 scratch[num] = '\0';
1134 for (cp = scratch; *cp != '\0'; )
1135 *bp++ = *cp++;
1136 break;
1137 case 'o':
1138 (void) strlcpy(scratch, spfctx->mailfrom_addr, MAXDNSHOSTNAME);
1139 cp = strchr(scratch, '@');
1140 if (cp != NULL)
1141 ++cp;
1142 else
1143 cp = scratch;
1144 if (num > 0 && num < (MAXDNSHOSTNAME - (cp - scratch)))
1145 cp[num] = '\0';
1146 for (; *cp != '\0'; )
1147 *bp++ = *cp++;
1148 break;
1149 case 'd':
1150 if (rev == TRUE)
1151 (void) opendmarc_spf_reverse(spfctx->mailfrom_domain, scratch, MAXDNSHOSTNAME);
1152 else
1153 (void) strlcpy(scratch, spfctx->mailfrom_domain, MAXDNSHOSTNAME);
1154 if (num > 0 && num < MAXDNSHOSTNAME)
1155 scratch[num] = '\0';
1156 for (cp = scratch; *cp != '\0'; )
1157 *bp++ = *cp++;
1158 break;
1159 case 'i':
1160 if (rev == TRUE)
1161 (void) opendmarc_spf_reverse(spfctx->ip_address, scratch, MAXDNSHOSTNAME);
1162 else
1163 if (num > 0 && num < MAXDNSHOSTNAME)
1164 scratch[num] = '\0';
1165 for (cp = scratch; *cp != '\0'; )
1166 *bp++ = *cp++;
1167 break;
1168 case 'h':
1169 if (rev == TRUE)
1170 (void) opendmarc_spf_reverse(spfctx->helo_domain, scratch, MAXDNSHOSTNAME);
1171 else
1172 (void) strlcpy(scratch, spfctx->helo_domain, MAXDNSHOSTNAME);
1173 if (num > 0 && num < MAXDNSHOSTNAME)
1174 scratch[num] = '\0';
1175 for (cp = scratch; *cp != '\0'; )
1176 *bp++ = *cp++;
1177 break;
1178 case 'v':
1179 /* if ip is ipv6 use "ip6" instead */
1180 for (cp = "in-addr"; *cp != '\0'; )
1181 *bp++ = *cp++;
1182 break;
1183 case 'p':
1184 if (spfctx->validated_domain[0] == '\0')
1185 (void) opendmarc_spf_ptr_domain(spfctx, NULL);
1186 if (rev == TRUE)
1187 (void) opendmarc_spf_reverse(spfctx->validated_domain, scratch, MAXDNSHOSTNAME);
1188 else
1189 (void) strlcpy(scratch, spfctx->validated_domain, MAXDNSHOSTNAME);
1190 if (num > 0 && num < MAXDNSHOSTNAME)
1191 scratch[num] = '\0';
1192 for (cp = scratch; *cp != '\0'; )
1193 *bp++ = *cp++;
1194 break;
1195 case 'c':
1196 if (is_exp == FALSE)
1197 return NULL;
1198 if (rev == TRUE)
1199 (void) opendmarc_spf_reverse(spfctx->ip_address, scratch, MAXDNSHOSTNAME);
1200 else
1201 (void) strlcpy(scratch, spfctx->ip_address, MAXDNSHOSTNAME);
1202 if (num > 0 && num < MAXDNSHOSTNAME)
1203 scratch[num] = '\0';
1204 for (cp = scratch; *cp != '\0'; )
1205 *bp++ = *cp++;
1206 break;
1207 case 'r':
1208 /* do rev and num apply to this one? */
1209 if (is_exp == FALSE)
1210 return NULL;
1211 if (gethostname(scratch, sizeof scratch) == 0)
1212 {
1213 for (cp = scratch; *cp != '\0'; )
1214 *bp++ = *cp++;
1215 }
1216 break;
1217 case 't':
1218 /* do rev and num apply to this one? */
1219 if (is_exp == FALSE)
1220 return NULL;
1221 t = time(NULL);
1222 (void) opendmarc_util_ultoa(t, scratch, sizeof scratch);
1223 for (cp = scratch; *cp != '\0'; )
1224 *bp++ = *cp++;
1225 break;
1226 }
1227 if (*xp != '}')
1228 return NULL;
1229 sp = xp+1;
1230 continue;
1231 }
1232 *bp++ = '\0';
1233 return buf;
1234 }
1235
1236
1237 /***************************************************************
1238 ** libspf_parse -- parse the record
1239 **
1240 ** Arguments:
1241 ** ctx -- SPF_CTX_T
1242 ** xbuf -- buffer into which errors are written.
1243 ** xbuf_len -- size of buffer
1244 **
1245 ** Returns:
1246 ** spfctx->status
1247 **
1248 ** Side Effects:
1249 ** Makes a connections to the local name server and blocks
1250 ** on each waiting for a reply.
1251 **
1252 ***************************************************************/
1253 #define MAX_SPF_STACK_DEPTH (10)
1254 #define MAX_SPF_DNS_LOOKUPS (10)
1255 typedef struct {
1256 char domain[MAXDNSHOSTNAME];
1257 char spf[SPF_MAX_SPF_RECORD_LEN];
1258 char *sp;
1259 char *ep;
1260 char *esp;
1261 } SPF_STACK_T;
1262
1263 #define SPF_SP (stack[s].sp)
1264 #define SPF_EP (stack[s].ep)
1265 #define SPF_ESP (stack[s].esp)
1266 #define PUSHLINE i = spfctx->nlines; if (i < MAX_SPF_STACK_DEPTH) { spfctx->lines[i] = strdup(xbuf); spfctx->nlines = ++i; }
1267 int
opendmarc_spf_parse(SPF_CTX_T * spfctx,int dns_count,char * xbuf,size_t xbuf_len)1268 opendmarc_spf_parse(SPF_CTX_T *spfctx, int dns_count, char *xbuf, size_t xbuf_len)
1269 {
1270 char ipnum[64];
1271 char *vp = NULL;
1272 int i;
1273 size_t len;
1274 int prefix;
1275 u_long ip = 0;
1276 int split = 0;
1277 #define SPLIT_COLON (1)
1278 #define SPLIT_EQUAL (2)
1279 #define SPLIT_SLASH (3)
1280 SPF_STACK_T stack[MAX_SPF_STACK_DEPTH];
1281 int s = 0;
1282 int up = FALSE;
1283 int ret;
1284
1285 spfctx->in_token = SPF_IN_TOKEN_NONE;
1286 if (spfctx == NULL)
1287 {
1288 (void) strlcat(xbuf, "Oddly the context was NULL: FAILED", xbuf_len);
1289 PUSHLINE
1290 return spfctx->status = SPF_RETURN_INTERNAL;
1291 }
1292 if (spfctx->mailfrom_domain[0] == '\0')
1293 {
1294 if (spfctx->helo_domain[0] == '\0')
1295 {
1296 (void) strlcat(xbuf, "Spf present but oddly no domain specified: FAILED", xbuf_len);
1297 PUSHLINE
1298 return spfctx->status = SPF_RETURN_INTERNAL;
1299 }
1300 (void) strlcpy(spfctx->mailfrom_domain, spfctx->helo_domain, sizeof spfctx->mailfrom_domain);
1301 }
1302 if (spfctx->spf_record[0] == '\0')
1303 {
1304 (void) strlcat(xbuf, "Spf TXT record existed, but was empty: FAILED", xbuf_len);
1305 PUSHLINE
1306 return spfctx->status = SPF_RETURN_INTERNAL;
1307 }
1308 if (spfctx->ip_address[0] == '\0')
1309 {
1310 (void) strlcat(xbuf, "Spf present but no IP address to check: FAILED", xbuf_len);
1311 PUSHLINE
1312 return spfctx->status = SPF_RETURN_INTERNAL;
1313 }
1314 len = strlen(spfctx->spf_record);
1315 if (len >= SPF_MAX_SPF_RECORD_LEN -1)
1316 {
1317 (void) strlcat(xbuf, "Spf TXT record existed, but was absurdly large: FAILED", xbuf_len);
1318 PUSHLINE
1319 return spfctx->status = SPF_RETURN_RECORD_TOOLONG;
1320 }
1321
1322 (void) memset(stack[s].domain, '\0', MAXDNSHOSTNAME);
1323 (void) strlcpy(stack[s].domain, spfctx->mailfrom_domain, MAXDNSHOSTNAME);
1324
1325 (void) memset(ipnum, '\0', sizeof ipnum);
1326 (void) strlcpy(ipnum, spfctx->ip_address, sizeof ipnum);
1327 ip = inet_addr(ipnum);
1328
1329 (void) strlcpy(stack[s].spf, spfctx->spf_record, SPF_MAX_SPF_RECORD_LEN);
1330 SPF_SP = stack[s].spf;
1331 SPF_EP = stack[s].spf + strlen(stack[s].spf);
1332 SPF_ESP = stack[s].spf - 1;
1333 up = TRUE;
1334
1335 while (s >= 0)
1336 {
1337 if (up == TRUE)
1338 {
1339 (void) memset(xbuf, '\0', xbuf_len);
1340 (void) strlcpy(xbuf, stack[s].domain, xbuf_len);
1341 (void) strlcat(xbuf, ": ", xbuf_len);
1342 (void) strlcat(xbuf, stack[s].spf, xbuf_len);
1343 PUSHLINE
1344 }
1345
1346 for (;;)
1347 {
1348 if (dns_count > MAX_SPF_DNS_LOOKUPS)
1349 {
1350 (void) strlcat(xbuf, " Too Many DNS queries (10 max): FAILED", xbuf_len);
1351 PUSHLINE
1352 return spfctx->status = SPF_RETURN_TOO_MANY_DNS_QUERIES;
1353 }
1354 if (SPF_ESP >= SPF_EP-1)
1355 {
1356 --s;
1357 up = FALSE;
1358 break;
1359 }
1360 SPF_SP = SPF_ESP + 1;
1361
1362 while (isspace((int)*SPF_SP) && *SPF_SP != '\0' && SPF_SP < SPF_EP)
1363 ++SPF_SP;
1364 if (*SPF_SP == '\0' || SPF_SP >= SPF_EP)
1365 {
1366 --s;
1367 up = FALSE;
1368 break;
1369 }
1370
1371 /* find the next space delimit point */
1372 SPF_ESP = SPF_SP;
1373 while(! isspace((int)*SPF_ESP) && *SPF_ESP != '\0' && SPF_ESP < SPF_EP)
1374 ++SPF_ESP;
1375 if (SPF_ESP > SPF_EP)
1376 {
1377 --s;
1378 up = FALSE;
1379 break;
1380 }
1381 *SPF_ESP = '\0';
1382
1383 /* show each step */
1384 (void) memset(xbuf, '\0', xbuf_len);
1385 (void) strlcpy(xbuf, stack[s].domain, xbuf_len);
1386 (void) strlcat(xbuf, ": ", xbuf_len);
1387
1388 /* ignore the qualifiers for now */
1389 prefix = '\0';
1390 if (*SPF_SP == '+' || *SPF_SP == '?' || *SPF_SP == '~' || *SPF_SP == '-')
1391 {
1392 prefix = *SPF_SP;
1393 ++SPF_SP;
1394 }
1395
1396 /* split at any =, /, or : into name=sp, value=vp */
1397 vp = strchr(SPF_SP, '=');
1398 if (vp != NULL)
1399 {
1400 split = SPLIT_EQUAL;
1401 *vp++ = '\0';
1402 }
1403 else
1404 {
1405 vp = strchr(SPF_SP, ':');
1406 if (vp != NULL)
1407 {
1408 split = SPLIT_COLON;
1409 *vp++ = '\0';
1410 }
1411 else
1412 {
1413 vp = strchr(SPF_SP, '/');
1414 if (vp != NULL)
1415 {
1416 split = SPLIT_SLASH;
1417 *vp++ = '\0';
1418 }
1419 else
1420 {
1421 vp = NULL;
1422 }
1423 }
1424 }
1425
1426 if (strcasecmp(SPF_SP, "v") == 0)
1427 {
1428 spfctx->in_token = SPF_IN_TOKEN_VERSION;
1429 if (vp == NULL || strcasecmp(vp, "spf1") != 0)
1430 {
1431 (void) strlcat(xbuf, " Expected \"v=spf1\": FAILED", xbuf_len);
1432 PUSHLINE
1433 return spfctx->status = SPF_RETURN_BAD_SYNTAX_VERSION;
1434 }
1435 /* version was okay */
1436 continue;
1437 }
1438 if (strncasecmp(SPF_SP, "spf2.0", 6) == 0)
1439 continue;
1440
1441 if (strcasecmp("a", SPF_SP) == 0 || strcasecmp("ip4", SPF_SP) == 0)
1442 {
1443 char ** ary = NULL;
1444 int ary_len = 0;
1445 char ** app;
1446 char abuf[BUFSIZ];
1447
1448 if (vp == NULL || split == SPLIT_SLASH)
1449 {
1450
1451 /*
1452 * Don't know what to do with a/24.
1453 * look up the a, and do each address/24?
1454 */
1455 if (ary != NULL)
1456 ary = opendmarc_util_freenargv(ary, &ary_len);
1457 ++dns_count;
1458 ary = opendmarc_spf_dns_lookup_a(stack[s].domain, ary, &ary_len);
1459 if (ary != NULL)
1460 {
1461 for (app = ary; *app != NULL; ++app)
1462 {
1463 (void) memset(abuf, '\0', sizeof abuf);
1464 (void) strlcpy(abuf, *app, sizeof abuf);
1465 if (vp != NULL)
1466 {
1467 (void) strlcat(abuf, "/", sizeof abuf);
1468 (void) strlcat(abuf, vp, sizeof abuf);
1469 }
1470 spfctx->iplist = opendmarc_util_pushnargv(abuf, spfctx->iplist, &(spfctx->ipcount));
1471 if (opendmarc_spf_cidr_address(ip, abuf) == TRUE)
1472 {
1473 (void) strlcat(xbuf, " ", xbuf_len);
1474 (void) strlcat(xbuf, ipnum, xbuf_len);
1475 (void) strlcat(xbuf, " was found in ", xbuf_len);
1476 (void) strlcat(xbuf, abuf, xbuf_len);
1477 (void) strlcat(xbuf, ": PASSED", xbuf_len);
1478 PUSHLINE
1479 ary = opendmarc_util_freenargv(ary, &ary_len);
1480 return spfctx->status = SPF_RETURN_OK_PASSED;
1481 }
1482 }
1483 ary = opendmarc_util_freenargv(ary, &ary_len);
1484 continue;
1485 }
1486 (void) strlcat(xbuf, " ", xbuf_len);
1487 (void) strlcat(xbuf, stack[s].domain, xbuf_len);
1488 (void) strlcat(xbuf, " had no A records: FAILED", xbuf_len);
1489 if (s == 0 && prefix == '-')
1490 {
1491 return spfctx->status = SPF_RETURN_DASH_FORCED_HARD_FAIL;
1492 }
1493 PUSHLINE
1494 return spfctx->status = SPF_RETURN_A_BUT_NO_A_RECORD;
1495 }
1496 if (strcasecmp("a", SPF_SP) == 0 && vp != NULL)
1497 {
1498 char * slashp = NULL;
1499 char ** a_ary = NULL;
1500 int a_ary_len = 0;
1501 char ** a_app = NULL;
1502 char a_abuf[BUFSIZ];
1503 (void) opendmarc_spf_macro_expand(spfctx, vp, a_abuf, sizeof a_abuf, FALSE);
1504 ++dns_count;
1505 a_ary = (char **)opendmarc_spf_dns_lookup_a(a_abuf, a_ary, &a_ary_len);
1506 if (a_ary == NULL)
1507 {
1508 (void) strlcat(xbuf, " ", xbuf_len);
1509 (void) strlcat(xbuf, vp, xbuf_len);
1510 (void) strlcat(xbuf, " had no A records: FAILED", xbuf_len);
1511 if (s == 0 && prefix == '-')
1512 {
1513 return spfctx->status = SPF_RETURN_DASH_FORCED_HARD_FAIL;
1514 }
1515 PUSHLINE
1516 return spfctx->status = SPF_RETURN_A_BUT_NO_A_RECORD;
1517 }
1518
1519 for (a_app = a_ary; *a_app != NULL; ++a_app)
1520 {
1521 (void) memset(a_abuf, '\0', sizeof a_abuf);
1522 (void) strlcpy(a_abuf, *a_app, sizeof a_abuf);
1523 if (slashp != NULL)
1524 {
1525 (void) strlcat(a_abuf, "/", sizeof a_abuf);
1526 (void) strlcat(a_abuf, slashp+1, sizeof a_abuf);
1527 }
1528 spfctx->iplist = opendmarc_util_pushnargv(a_abuf, spfctx->iplist, &(spfctx->ipcount));
1529 if (opendmarc_spf_cidr_address(ip, a_abuf) == TRUE)
1530 {
1531 (void) strlcat(xbuf, " ", xbuf_len);
1532 (void) strlcat(xbuf, ipnum, xbuf_len);
1533 (void) strlcat(xbuf, " was found: PASSED", xbuf_len);
1534 PUSHLINE
1535 a_ary = opendmarc_util_freenargv(a_ary, &ary_len);
1536 return spfctx->status = SPF_RETURN_OK_PASSED;
1537 }
1538 if (s == 0 && prefix == '-')
1539 {
1540 return spfctx->status = SPF_RETURN_DASH_FORCED_HARD_FAIL;
1541 }
1542 }
1543 a_ary = opendmarc_util_freenargv(a_ary, &ary_len);
1544 continue;
1545 }
1546 if (strcasecmp("ip4", SPF_SP) == 0 && vp != NULL)
1547 {
1548 spfctx->iplist = opendmarc_util_pushnargv(vp, spfctx->iplist, &(spfctx->ipcount));
1549 ret = opendmarc_spf_cidr_address(ip, vp);
1550 if (ret == TRUE)
1551 {
1552 (void) strlcat(xbuf, " ", xbuf_len);
1553 (void) strlcat(xbuf, ipnum, xbuf_len);
1554 (void) strlcat(xbuf, " was found: PASSED", xbuf_len);
1555 PUSHLINE
1556 return spfctx->status = SPF_RETURN_OK_PASSED;
1557 }
1558 if (s == 0 && prefix == '-')
1559 {
1560 return spfctx->status = SPF_RETURN_DASH_FORCED_HARD_FAIL;
1561 }
1562 continue;
1563 }
1564 if (vp == NULL)
1565 vp = "<nil>";
1566 (void) strlcat(xbuf, " ", xbuf_len);
1567 (void) strlcat(xbuf, vp, xbuf_len);
1568 (void) strlcat(xbuf, " Badly formed: FAILED", xbuf_len);
1569 PUSHLINE
1570 return spfctx->status = SPF_RETURN_A_BUT_BAD_SYNTAX;
1571 }
1572 if (strcasecmp("mx", SPF_SP) == 0)
1573 {
1574 char ** ary = NULL;
1575 int ary_len = 0;
1576 char ** app = NULL;
1577 char mxbuf[BUFSIZ];
1578
1579 if (vp != NULL && split != SPLIT_SLASH)
1580 (void) opendmarc_spf_macro_expand(spfctx, vp, mxbuf, sizeof mxbuf, FALSE);
1581 else
1582 (void) opendmarc_spf_macro_expand(spfctx, stack[s].domain, mxbuf, sizeof mxbuf, FALSE);
1583 ++dns_count;
1584 ary = opendmarc_spf_dns_lookup_mx(mxbuf, ary, &ary_len);
1585 if (ary == NULL)
1586 {
1587 (void) strlcat(xbuf, mxbuf, xbuf_len);
1588 (void) strlcat(xbuf, ": MX listed but no MX records", xbuf_len);
1589 if (s == 0 && prefix == '-')
1590 {
1591 return spfctx->status = SPF_RETURN_DASH_FORCED_HARD_FAIL;
1592 }
1593 PUSHLINE
1594 continue;
1595 }
1596 for (app = ary; *app != NULL; ++app)
1597 {
1598 spfctx->iplist = opendmarc_util_pushnargv(*app, spfctx->iplist, &(spfctx->ipcount));
1599 if (opendmarc_spf_cidr_address(ip, *app) == TRUE)
1600 {
1601 (void) strlcat(xbuf, " ", xbuf_len);
1602 (void) strlcat(xbuf, ipnum, xbuf_len);
1603 (void) strlcat(xbuf, " was found: PASSED", xbuf_len);
1604 PUSHLINE
1605 ary = opendmarc_util_freenargv(ary, &ary_len);
1606 return spfctx->status = SPF_RETURN_OK_PASSED;
1607 }
1608 }
1609 ary = opendmarc_util_freenargv(ary, &ary_len);
1610 if (s == 0 && prefix == '-')
1611 {
1612 return spfctx->status = SPF_RETURN_DASH_FORCED_HARD_FAIL;
1613 }
1614 continue;
1615 }
1616 if (strcasecmp("include", SPF_SP) == 0)
1617 {
1618 char *spf_ret;
1619 char spfbuf[SPF_MAX_SPF_RECORD_LEN];
1620 char cname[MAXDNSHOSTNAME];
1621 char query[MAXDNSHOSTNAME];
1622 int reply;
1623
1624 if (vp == NULL || strlen(vp) == 0)
1625 {
1626 (void) strlcat(xbuf, "\"include:\" Lacked a domain specification.", xbuf_len);
1627 if (s == 0 && prefix == '-')
1628 {
1629 return spfctx->status = SPF_RETURN_DASH_FORCED_HARD_FAIL;
1630 }
1631 PUSHLINE
1632 return spfctx->status = SPF_RETURN_BAD_SYNTAX_INCLUDE;
1633 }
1634 (void) memset(query, '\0', sizeof query);
1635 (void) strlcpy(query, vp, sizeof query);
1636 (void) memset(cname, '\0', sizeof cname);
1637 (void) memset(spfbuf, '\0', sizeof spfbuf);
1638 ++dns_count;
1639 spf_ret = opendmarc_spf_dns_get_record(query, &reply, spfbuf, sizeof spfbuf, cname, sizeof cname, TRUE);
1640 if (spf_ret == NULL)
1641 {
1642 (void) strlcat(xbuf, vp, xbuf_len);
1643 (void) strlcat(xbuf, " Lacked lacked an SPF record: FAILED", xbuf_len);
1644 if (s == 0 && prefix == '-')
1645 {
1646 return spfctx->status = SPF_RETURN_DASH_FORCED_HARD_FAIL;
1647 }
1648 PUSHLINE
1649 return spfctx->status = SPF_RETURN_INCLUDE_NO_DOMAIN;
1650 }
1651 if (s+1 >= MAX_SPF_STACK_DEPTH)
1652 {
1653 char nbuf[16];
1654
1655 (void) strlcat(xbuf, stack[s].domain, xbuf_len);
1656 (void) strlcat(xbuf, " Too many levels of includes, ", xbuf_len);
1657 (void) opendmarc_util_ultoa(MAX_SPF_STACK_DEPTH, nbuf, sizeof nbuf);
1658 (void) strlcat(xbuf, nbuf, xbuf_len);
1659 (void) strlcat(xbuf, " Max", xbuf_len);
1660 if (s == 0 && prefix == '-')
1661 {
1662 return spfctx->status = SPF_RETURN_DASH_FORCED_HARD_FAIL;
1663 }
1664 PUSHLINE
1665 continue;
1666 }
1667 if (s > 0)
1668 {
1669 for (i = 0; i < s; i++)
1670 {
1671 if (strcasecmp(vp, stack[i].domain) == 0)
1672 break;
1673 }
1674 if (i < s)
1675 {
1676 (void) strlcat(xbuf, query, xbuf_len);
1677 (void) strlcat(xbuf, " Include LOOP detected and supressed", xbuf_len);
1678 PUSHLINE
1679 continue;
1680 }
1681 }
1682 s += 1;
1683 up = TRUE;
1684 (void) memset(stack[s].domain, '\0', MAXDNSHOSTNAME);
1685 (void) strlcpy(stack[s].domain, vp, MAXDNSHOSTNAME);
1686 (void) memset(stack[s].spf, '\0', SPF_MAX_SPF_RECORD_LEN);
1687 (void) strlcpy(stack[s].spf, spfbuf, SPF_MAX_SPF_RECORD_LEN);
1688 SPF_SP = stack[s].spf;
1689 SPF_EP = stack[s].spf + strlen(stack[s].spf);
1690 SPF_ESP = stack[s].spf - 1;
1691 if (s == 0 && prefix == '-')
1692 {
1693 return spfctx->status = SPF_RETURN_DASH_FORCED_HARD_FAIL;
1694 }
1695 break;
1696 }
1697 if (strcasecmp("all", SPF_SP) == 0)
1698 {
1699 char p[2];
1700
1701 p[0] = prefix;
1702 p[1] = '\0';
1703 (void) memset(xbuf, '\0', xbuf_len);
1704 (void) strlcpy(xbuf, stack[s].domain, xbuf_len);
1705 (void) strlcat(xbuf, ": ", xbuf_len);
1706 (void) strlcat(xbuf, p, xbuf_len);
1707 (void) strlcat(xbuf, "all, status=", xbuf_len);
1708
1709 if (s == 0)
1710 {
1711 if (prefix == '-')
1712 {
1713 (void) strlcat(xbuf, " ", xbuf_len);
1714 (void) strlcat(xbuf, ipnum, xbuf_len);
1715 (void) strlcat(xbuf, " Not found, so: FAILED", xbuf_len);
1716 PUSHLINE
1717 return spfctx->status = SPF_RETURN_DASH_ALL_HARD_FAIL;
1718 }
1719 if (prefix == '~')
1720 {
1721 (void) strlcat(xbuf, " ", xbuf_len);
1722 (void) strlcat(xbuf, ipnum, xbuf_len);
1723 (void) strlcat(xbuf, " Not found, so: SOFT-FAILED", xbuf_len);
1724 PUSHLINE
1725 return spfctx->status = SPF_RETURN_TILDE_ALL_SOFT_FAIL;
1726 }
1727 if (prefix == '?')
1728 {
1729 (void) strlcat(xbuf, " ", xbuf_len);
1730 (void) strlcat(xbuf, ipnum, xbuf_len);
1731 (void) strlcat(xbuf, " Not found, but: NEUTRAL", xbuf_len);
1732 PUSHLINE
1733 return spfctx->status = SPF_RETURN_QMARK_ALL_NEUTRAL;
1734 }
1735 else
1736 {
1737 (void) strlcat(xbuf, " ", xbuf_len);
1738 (void) strlcat(xbuf, ipnum, xbuf_len);
1739 (void) strlcat(xbuf, " Not found, but: PASS", xbuf_len);
1740 PUSHLINE
1741 return spfctx->status = SPF_RETURN_OK_PASSED;
1742 }
1743 }
1744 continue;
1745 }
1746 if (strcasecmp("ip6", SPF_SP) == 0)
1747 {
1748 int ret;
1749 /*
1750 * Open issue: Should we convert an ipv4 address in spfctx->ip_address
1751 * into ipv6 for this check? e.g. 1.2.3.4 -> :FFFF:1.2.3.4
1752 */
1753 spfctx->iplist = opendmarc_util_pushnargv(vp, spfctx->iplist, &(spfctx->ipcount));
1754 ret = opendmarc_spf_ipv6_cidr_check(spfctx->ip_address, vp);
1755 if (ret == TRUE)
1756 {
1757 return spfctx->status = SPF_RETURN_OK_PASSED;
1758 }
1759 if (s == 0 && prefix == '-')
1760 {
1761 return spfctx->status = SPF_RETURN_DASH_FORCED_HARD_FAIL;
1762 }
1763 continue;
1764 }
1765 if (strcasecmp("ptr", SPF_SP) == 0)
1766 {
1767 int good;
1768
1769 good = opendmarc_spf_ptr_domain(spfctx, vp);
1770 if (good == TRUE)
1771 {
1772 return spfctx->status = SPF_RETURN_OK_PASSED;
1773 }
1774 if (s == 0 && prefix == '-')
1775 {
1776 return spfctx->status = SPF_RETURN_DASH_FORCED_HARD_FAIL;
1777 }
1778 continue;
1779 }
1780 if (strcasecmp("exists", SPF_SP) == 0)
1781 {
1782 char * xp;
1783 char ** ary = NULL;
1784 int ary_len = 0;
1785 char ** app;
1786
1787 if (vp == NULL || strlen(vp) == 0)
1788 {
1789 (void) strlcpy(xbuf, "\"exists:\" Lacked a domain specification.", xbuf_len);
1790 PUSHLINE
1791 return spfctx->status = SPF_RETURN_BAD_SYNTAX_EXISTS;
1792 }
1793 /* see http://old.openspf.org/macros.html for macros */
1794 /* altavista.net uses +exists:CL.%{i}.FR.%{s}.HE.%{h}.null.spf.altavista.com */
1795 xp = opendmarc_spf_macro_expand(spfctx, vp, xbuf, xbuf_len, TRUE);
1796 if (xp == NULL)
1797 {
1798 (void) strlcpy(xbuf, "\"exists:\" record had syntactially bad macros:" , xbuf_len);
1799 (void) strlcat(xbuf, vp, xbuf_len);
1800 (void) strlcat(xbuf, ": FAILED", xbuf_len);
1801 PUSHLINE
1802 return spfctx->status = SPF_RETURN_BAD_MACRO_SYNTAX;
1803 }
1804 ++dns_count;
1805 if (ary != NULL)
1806 ary = opendmarc_util_freenargv(ary, &ary_len);
1807 ary = opendmarc_spf_dns_lookup_a(xbuf, ary, &ary_len);
1808 if (ary == NULL)
1809 {
1810 /* lookup failed */
1811 if (prefix != '-')
1812 continue;
1813 (void) strlcpy(xbuf, "\"exists:\" record lookup: ", xbuf_len);
1814 (void) strlcat(xbuf, vp, xbuf_len);
1815 (void) strlcat(xbuf, ": FAILED", xbuf_len);
1816 PUSHLINE
1817 return spfctx->status = SPF_RETURN_NOT_EXISTS_HARDFAIL;
1818 }
1819 for (app = ary; *app != NULL; ++app)
1820 {
1821 if (strcmp(spfctx->ip_address, *app) == 0)
1822 return spfctx->status = SPF_RETURN_OK_PASSED;
1823 }
1824 ary = opendmarc_util_freenargv(ary, &ary_len);
1825 continue;
1826 }
1827 if (strcasecmp("exp", SPF_SP) == 0)
1828 {
1829 char * xp;
1830
1831 if (vp == NULL || strlen(vp) == 0)
1832 {
1833 (void) strlcpy(xbuf, "\"exp:\" Lacked a domain specification.", xbuf_len);
1834 PUSHLINE
1835 return spfctx->status = SPF_RETURN_BAD_SYNTAX_EXP;
1836 }
1837 xp = opendmarc_spf_macro_expand(spfctx, vp, xbuf, xbuf_len, FALSE);
1838 if (xp == NULL)
1839 {
1840 (void) strlcpy(xbuf, "\"exists:\" record had syntactially bad macros:" , xbuf_len);
1841 (void) strlcat(xbuf, vp, xbuf_len);
1842 (void) strlcat(xbuf, ": FAILED", xbuf_len);
1843 PUSHLINE
1844 return spfctx->status = SPF_RETURN_BAD_MACRO_SYNTAX;
1845 }
1846 (void) memset(spfctx->exp_buf, '\0', sizeof spfctx->exp_buf);
1847 (void) strlcpy(spfctx->exp_buf, xp, sizeof spfctx->exp_buf);
1848 spfctx->did_get_exp = TRUE;
1849 continue;
1850 }
1851 if (strcasecmp("redirect", SPF_SP) == 0)
1852 {
1853 /*
1854 * Some people think that redirect and include are the same.
1855 * Rather than fail due to that belief, there is really no harm
1856 * in treating them the same.
1857 */
1858 int reply;
1859 char * xp;
1860 char query[MAXDNSHOSTNAME];
1861 char * spf_ret;
1862 char cname[128];
1863 char spfbuf[BUFSIZ];
1864
1865 if (vp == NULL)
1866 {
1867 (void) strlcat(xbuf, " Lacked a domain specification: FAILED", xbuf_len);
1868 PUSHLINE
1869 return spfctx->status = SPF_RETURN_REDIRECT_NO_DOMAIN;
1870 }
1871 (void) memset(query, '\0', sizeof query);
1872 xp = opendmarc_spf_macro_expand(spfctx, vp, query, sizeof query, TRUE);
1873 if (xp == NULL)
1874 {
1875 (void) strlcpy(xbuf, "\"redirect:\" record had syntactially bad macros:" , xbuf_len);
1876 (void) strlcat(xbuf, vp, xbuf_len);
1877 (void) strlcat(xbuf, ": FAILED", xbuf_len);
1878 PUSHLINE
1879 return spfctx->status = SPF_RETURN_BAD_MACRO_SYNTAX;
1880 }
1881 ++dns_count;
1882 spf_ret = opendmarc_spf_dns_get_record(query, &reply, spfbuf, sizeof spfbuf, cname, sizeof cname, TRUE);
1883 if (spf_ret == NULL)
1884 {
1885 (void) strlcat(xbuf, vp, xbuf_len);
1886 (void) strlcat(xbuf, " Lacked lacked an SPF record: FAILED", xbuf_len);
1887 if (s == 0 && prefix == '-')
1888 {
1889 return spfctx->status = SPF_RETURN_DASH_FORCED_HARD_FAIL;
1890 }
1891 PUSHLINE
1892 return spfctx->status = SPF_RETURN_BAD_SYNTAX_REDIRECT;
1893 }
1894 (void) memset(stack[s].domain, '\0', MAXDNSHOSTNAME);
1895 (void) strlcpy(stack[s].domain, vp, MAXDNSHOSTNAME);
1896 (void) memset(stack[s].spf, '\0', SPF_MAX_SPF_RECORD_LEN);
1897 (void) strlcpy(stack[s].spf, spfbuf, SPF_MAX_SPF_RECORD_LEN);
1898 SPF_SP = stack[s].spf;
1899 SPF_EP = stack[s].spf + strlen(stack[s].spf);
1900 SPF_ESP = stack[s].spf - 1;
1901 up = TRUE;
1902 break;
1903 }
1904 if (strlen(SPF_SP) > 0)
1905 {
1906 (void) strlcat(xbuf, "\"", xbuf_len);
1907 (void) strlcat(xbuf, SPF_SP, xbuf_len);
1908 (void) strlcat(xbuf, "\": Unrecognized SPF keyword, WARNING", xbuf_len);
1909 PUSHLINE
1910 /* return spfctx->status = SPF_RETURN_UNKNOWN_KEYWORD; */
1911 continue;
1912 }
1913 }
1914 }
1915 return spfctx->status;
1916 }
1917
1918 SPF_CTX_T *
opendmarc_spf_alloc_ctx()1919 opendmarc_spf_alloc_ctx()
1920 {
1921 SPF_CTX_T *spfctx = NULL;
1922
1923 spfctx = malloc(sizeof(SPF_CTX_T));
1924 if (spfctx == NULL)
1925 return NULL;
1926
1927 (void) memset(spfctx, '\0', sizeof(SPF_CTX_T));
1928 spfctx->status = SPF_RETURN_UNDECIDED;
1929 return spfctx;
1930 }
1931
1932 SPF_CTX_T *
opendmarc_spf_free_ctx(SPF_CTX_T * spfctx)1933 opendmarc_spf_free_ctx(SPF_CTX_T *spfctx)
1934 {
1935 int i;
1936
1937 if (spfctx == NULL)
1938 return spfctx;
1939
1940 for (i = 0; i < spfctx->nlines; i++)
1941 {
1942 if (spfctx->lines[i] != NULL)
1943 (void) free(spfctx->lines[i]);
1944 }
1945 spfctx->iplist = opendmarc_util_freenargv(spfctx->iplist, &(spfctx->ipcount));
1946 (void) free(spfctx);
1947 spfctx = NULL;
1948 return spfctx;
1949 }
1950
1951 int
opendmarc_spf_specify_ip_address(SPF_CTX_T * spfctx,char * ip_address,size_t ip_address_len)1952 opendmarc_spf_specify_ip_address(SPF_CTX_T *spfctx, char *ip_address, size_t ip_address_len)
1953 {
1954 if (spfctx == NULL)
1955 return EINVAL;
1956
1957 if (ip_address == NULL)
1958 return EINVAL;
1959
1960 /*
1961 * we don't care at this point if it is ipv6 or ipv4
1962 */
1963 (void) memset(spfctx->ip_address, '\0', sizeof spfctx->ip_address);
1964 (void) strlcpy(spfctx->ip_address, ip_address, sizeof spfctx->ip_address);
1965 return 0;
1966 }
1967
1968 int
opendmarc_spf_specify_helo_domain(SPF_CTX_T * spfctx,char * helo_domain,size_t helo_domain_len)1969 opendmarc_spf_specify_helo_domain(SPF_CTX_T *spfctx, char *helo_domain, size_t helo_domain_len)
1970 {
1971 char copy[sizeof spfctx->mailfrom_addr];
1972 char *cp;
1973 char *ep;
1974
1975 if (spfctx == NULL)
1976 return EINVAL;
1977
1978 if (helo_domain == NULL)
1979 return 0;
1980
1981 (void) memset(copy, '\0', sizeof copy);
1982 (void) strlcpy(copy, helo_domain, sizeof copy);
1983 cp = strrchr(copy, '<');
1984 if (cp == NULL)
1985 cp = copy;
1986 ep = strchr(cp, '>');
1987 if (ep != NULL)
1988 *ep = '\0';
1989 ep = strchr(cp, '@');
1990 if (ep != NULL)
1991 cp = ep+1;
1992
1993 (void) memset(spfctx->helo_domain, '\0', sizeof spfctx->helo_domain);
1994 (void) strlcpy(spfctx->helo_domain, cp, sizeof spfctx->helo_domain);
1995 return 0;
1996 }
1997
1998 int
opendmarc_spf_specify_mailfrom(SPF_CTX_T * spfctx,char * mailfrom,size_t mailfrom_len,int * use_flag)1999 opendmarc_spf_specify_mailfrom(SPF_CTX_T *spfctx, char *mailfrom, size_t mailfrom_len, int *use_flag)
2000 {
2001 char copy[sizeof spfctx->mailfrom_addr];
2002 char *cp;
2003 char *ep;
2004
2005 if (use_flag != NULL)
2006 *use_flag = FALSE;
2007
2008 if (spfctx == NULL)
2009 return EINVAL;
2010
2011 if (mailfrom == NULL)
2012 return EINVAL;
2013
2014 (void) memset(copy, '\0', sizeof copy);
2015 (void) strlcpy(copy, mailfrom, sizeof copy);
2016
2017 cp = strrchr(copy, '<');
2018 if (cp == NULL)
2019 cp = copy;
2020 else
2021 ++cp;
2022 ep = strchr(cp, '>');
2023 if (ep != NULL)
2024 *ep = '\0';
2025
2026 (void) memset(spfctx->mailfrom_addr, '\0', sizeof spfctx->mailfrom_addr);
2027 (void) strlcpy(spfctx->mailfrom_addr, cp, sizeof spfctx->mailfrom_addr);
2028
2029 ep = strchr(cp, '@');
2030 if (ep != NULL)
2031 {
2032 cp = ep+1;
2033 if (use_flag != NULL)
2034 *use_flag = TRUE;
2035 }
2036
2037 if (strcasecmp(cp, "MAILER_DAEMON") == 0)
2038 cp = "";
2039
2040 (void) memset(spfctx->mailfrom_domain, '\0', sizeof spfctx->mailfrom_domain);
2041 (void) strlcpy(spfctx->mailfrom_domain, cp, sizeof spfctx->mailfrom_domain);
2042 return 0;
2043 }
2044
2045 int
opendmarc_spf_specify_record(SPF_CTX_T * spfctx,char * spf_record,size_t spf_record_length)2046 opendmarc_spf_specify_record(SPF_CTX_T *spfctx, char *spf_record, size_t spf_record_length)
2047 {
2048 if (spfctx == NULL)
2049 {
2050 return EINVAL;
2051 }
2052 (void) memset(spfctx->spf_record, '\0', sizeof spfctx->spf_record);
2053 if (spf_record == NULL)
2054 {
2055 char * spf_ret;
2056 int reply;
2057 char cname[256];
2058 char spfbuf[BUFSIZ];
2059
2060 /* look it up */
2061 spf_ret = opendmarc_spf_dns_get_record(spfctx->mailfrom_domain, &reply, spfbuf, sizeof spfbuf, cname, sizeof cname, TRUE);
2062 if (spf_ret == NULL)
2063 {
2064 switch(reply)
2065 {
2066 case HOST_NOT_FOUND:
2067 case NO_DATA:
2068 return DMARC_POLICY_SPF_OUTCOME_NONE;
2069 break;
2070 case NO_RECOVERY:
2071 case TRY_AGAIN:
2072 return DMARC_POLICY_SPF_OUTCOME_TMPFAIL;
2073 break;
2074 }
2075 return DMARC_POLICY_SPF_OUTCOME_NONE;
2076 }
2077 (void) strlcpy(spfctx->spf_record, spfbuf, sizeof spfctx->spf_record);
2078 return 0;
2079 }
2080 (void) strlcpy(spfctx->spf_record, spf_record, sizeof spfctx->spf_record);
2081 return 0;
2082 }
2083
2084 int
opendmarc_spf_test(char * ip_address,char * mail_from_domain,char * helo_domain,char * spf_record,int softfail_okay_flag,char * human_readable,size_t human_readable_len,int * used_mfrom)2085 opendmarc_spf_test(char *ip_address, char *mail_from_domain, char *helo_domain, char *spf_record, int softfail_okay_flag, char *human_readable, size_t human_readable_len, int *used_mfrom)
2086 {
2087 SPF_CTX_T * ctx;
2088 int ret;
2089 int len;
2090 char xbuf[BUFSIZ];
2091
2092 if (used_mfrom != NULL)
2093 *used_mfrom = FALSE;
2094
2095 (void) memset(xbuf, '\0', sizeof xbuf);
2096 ctx = opendmarc_spf_alloc_ctx();
2097 if (ctx == NULL)
2098 {
2099 if (human_readable != NULL)
2100 (void) strlcpy(human_readable, strerror(errno), human_readable_len);
2101 return DMARC_POLICY_SPF_OUTCOME_TMPFAIL;
2102 }
2103
2104 if (ip_address == NULL)
2105 {
2106 if (human_readable != NULL)
2107 (void) strlcpy(human_readable, "No IP address available", human_readable_len);
2108 ctx = opendmarc_spf_free_ctx(ctx);
2109 return DMARC_POLICY_SPF_OUTCOME_FAIL;
2110 }
2111
2112 if (mail_from_domain == NULL && helo_domain == NULL)
2113 {
2114 if (human_readable != NULL)
2115 (void) strlcpy(human_readable, "No Domain name available to check", human_readable_len);
2116 ctx = opendmarc_spf_free_ctx(ctx);
2117 return DMARC_POLICY_SPF_OUTCOME_FAIL;
2118 }
2119
2120 ret = opendmarc_spf_specify_mailfrom(ctx, mail_from_domain, strlen(mail_from_domain), used_mfrom);
2121 if (ret != 0)
2122 {
2123 if (human_readable != NULL)
2124 (void) strlcpy(human_readable, strerror(errno), human_readable_len);
2125 ctx = opendmarc_spf_free_ctx(ctx);
2126 return DMARC_POLICY_SPF_OUTCOME_TMPFAIL;
2127 }
2128
2129 ret = opendmarc_spf_specify_helo_domain(ctx, helo_domain, strlen(helo_domain));
2130 if (ret != 0)
2131 {
2132 if (human_readable != NULL)
2133 (void) strlcpy(human_readable, strerror(errno), human_readable_len);
2134 ctx = opendmarc_spf_free_ctx(ctx);
2135 return DMARC_POLICY_SPF_OUTCOME_TMPFAIL;
2136 }
2137
2138 ret = opendmarc_spf_specify_ip_address(ctx, ip_address, strlen(ip_address));
2139 if (ret != 0)
2140 {
2141 if (human_readable != NULL)
2142 (void) strlcpy(human_readable, strerror(errno), human_readable_len);
2143 ctx = opendmarc_spf_free_ctx(ctx);
2144 return DMARC_POLICY_SPF_OUTCOME_TMPFAIL;
2145 }
2146
2147 if (spf_record == NULL)
2148 len = 0;
2149 else
2150 len = strlen(spf_record);
2151 ret = opendmarc_spf_specify_record(ctx, spf_record, len);
2152 if (ret != 0)
2153 {
2154 if (human_readable != NULL)
2155 (void) strlcpy(human_readable, hstrerror(h_errno), human_readable_len);
2156 ctx = opendmarc_spf_free_ctx(ctx);
2157 return ret;
2158 }
2159
2160 ret = opendmarc_spf_parse(ctx, 0, xbuf, sizeof xbuf);
2161 if (human_readable != NULL)
2162 (void) strlcpy(human_readable, opendmarc_spf_status_to_msg(ctx, ret), human_readable_len);
2163 ctx = opendmarc_spf_free_ctx(ctx);
2164
2165 if (ret != SPF_RETURN_OK_PASSED)
2166 {
2167 switch (ret)
2168 {
2169 case SPF_RETURN_UNDECIDED:
2170 case SPF_RETURN_QMARK_ALL_NEUTRAL:
2171 case SPF_RETURN_TILDE_ALL_SOFT_FAIL:
2172 if (softfail_okay_flag == TRUE)
2173 return DMARC_POLICY_SPF_OUTCOME_PASS;
2174 else
2175 return DMARC_POLICY_SPF_OUTCOME_FAIL;
2176 break;
2177 case SPF_RETURN_INTERNAL:
2178 return DMARC_POLICY_SPF_OUTCOME_TMPFAIL;
2179 }
2180 return DMARC_POLICY_SPF_OUTCOME_FAIL;
2181 }
2182 return DMARC_POLICY_SPF_OUTCOME_PASS;
2183 }
2184
2185 #endif /* HAVE_SPF2_H */
2186
2187 #endif /* WITH_SPF */
2188