Back to index

courier  0.68.2
spf.c
Go to the documentation of this file.
00001 /*
00002 ** Copyright 2004-2011 Double Precision, Inc.
00003 ** See COPYING for distribution information.
00004 */
00005 
00006 #include      "config.h"
00007 #include      "spf.h"
00008 #include      "rfc1035mxlist.h"
00009 #include      <stdio.h>
00010 #include      <ctype.h>
00011 #include      <stdlib.h>
00012 #include      <string.h>
00013 #include      <errno.h>
00014 #include      <sys/types.h>
00015 #if TIME_WITH_SYS_TIME
00016 #include      <sys/time.h>
00017 #include      <time.h>
00018 #else
00019 #if HAVE_SYS_TIME_H
00020 #include      <sys/time.h>
00021 #else
00022 #include      <time.h>
00023 #endif
00024 #endif
00025 
00026 struct rfc1035_spf_info {
00027        const char *mailfrom;
00028        const char *current_domain;
00029        const char *tcpremoteip;
00030        const char *tcpremotehost;
00031        const char *helodomain;
00032        const char *mydomain;
00033        char *errmsg_buf;
00034        size_t errmsg_buf_size;
00035 
00036        size_t *lookup_cnt;
00037 
00038        struct rfc1035_res res;
00039 };
00040 
00041 static void set_err_msg(char *errmsg_buf,
00042                      size_t errmsg_buf_size,
00043                      const char *errmsg)
00044 {
00045        size_t l=strlen(errmsg);
00046 
00047        if (errmsg_buf_size == 0)
00048               return;
00049 
00050        --errmsg_buf_size;
00051 
00052        if (l >= errmsg_buf_size)
00053               l=errmsg_buf_size;
00054        memcpy(errmsg_buf, errmsg, l);
00055        errmsg_buf[l]=0;
00056 }
00057 
00058 static char lookup(struct rfc1035_spf_info *info);
00059 
00060 char rfc1035_spf_lookup(const char *mailfrom,
00061                      const char *tcpremoteip,
00062                      const char *tcpremotehost,
00063                      const char *helodomain,
00064                      const char *mydomain,
00065                      char *errmsg_buf,
00066                      size_t errmsg_buf_size)
00067 {
00068        size_t lookup_cnt=0;
00069        struct rfc1035_spf_info info;
00070        char result;
00071 
00072        if (!tcpremoteip) tcpremoteip="";
00073        if (!tcpremotehost) tcpremotehost="";
00074        if (!helodomain) helodomain="";
00075        if (!mydomain) mydomain="";
00076 
00077        if (errmsg_buf && errmsg_buf_size)
00078               *errmsg_buf=0;
00079 
00080        /*
00081        ** If the <responsible-sender> has no localpart, clients MUST
00082        ** substitute the string "postmaster" for the localpart.
00083        */
00084        if (strchr(mailfrom, '@') == NULL)
00085        {
00086               char *buf=malloc(sizeof("postmaster@")+strlen(mailfrom));
00087               char err_code;
00088 
00089               if (buf == NULL)
00090               {
00091                      set_err_msg(errmsg_buf, errmsg_buf_size,
00092                                 strerror(errno));
00093                      return SPF_ERROR;
00094               }
00095 
00096               err_code=rfc1035_spf_lookup(strcat(strcpy(buf, "postmaster@"),
00097                                              mailfrom),
00098                                        tcpremoteip,
00099                                        tcpremotehost,
00100                                        helodomain,
00101                                        mydomain,
00102                                        errmsg_buf,
00103                                        errmsg_buf_size);
00104               free(buf);
00105               return err_code;
00106        }
00107 
00108        memset(&info, 0, sizeof(info));
00109 
00110        info.mailfrom=mailfrom;
00111 
00112        /*
00113        ** The <current-domain> is initially drawn from the
00114        ** <responsible-sender>.  Recursive mechanisms such as
00115        ** Include and Redirect replace the initial
00116        ** <current-domain> with another domain.  However, they
00117        ** do not change the value of the <responsible-sender>.
00118        */
00119        info.current_domain=strrchr(mailfrom, '@')+1;
00120 
00121        info.tcpremoteip=tcpremoteip;
00122        info.tcpremotehost=tcpremotehost;
00123        info.errmsg_buf=errmsg_buf;
00124        info.errmsg_buf_size=errmsg_buf_size;
00125        info.helodomain=helodomain;
00126        info.mydomain=mydomain;
00127        info.lookup_cnt=&lookup_cnt;
00128 
00129        rfc1035_init_resolv(&info.res);
00130 
00131        result=lookup(&info);
00132 
00133        if (errmsg_buf[0] == 0)
00134        {
00135               static const char errmsg[]="Address %s the Sender Policy Framework";
00136               char *p=malloc(sizeof(errmsg)+strlen(mailfrom)+20);
00137 
00138               if (p)
00139                      sprintf(p, errmsg, result == SPF_PASS
00140                             ? "passes":"does not pass");
00141 
00142               set_err_msg(errmsg_buf, errmsg_buf_size,
00143                          p ? p:strerror(errno));
00144               if (p) free(p);
00145        }
00146        rfc1035_destroy_resolv(&info.res);
00147        return result;
00148 }
00149 
00150 static int isspf1(struct rfc1035_reply *reply, int n)
00151 {
00152        char   txtbuf[256];
00153        const char *p;
00154 
00155        rfc1035_rr_gettxt(reply->allrrs[n], 0, txtbuf);
00156 
00157        for (p=txtbuf; *p; p++)
00158               if (!isspace((int)(unsigned char)*p))
00159                      break;
00160 
00161        if (strncasecmp(p, "v=spf1", 6) == 0 &&
00162            (p[6] == 0 ||
00163             isspace((int)(unsigned char)p[6])))
00164               return 1;
00165 
00166        return 0;
00167 }
00168 
00169 char rfc1035_spf_gettxt_res(const char *current_domain,
00170                          char *buf,
00171                          struct rfc1035_res *res)
00172 {
00173        struct rfc1035_reply *reply;
00174        char   namebuf[RFC1035_MAXNAMESIZE+1];
00175        int n, o;
00176 
00177 
00178        namebuf[0]=0;
00179        strncat(namebuf, current_domain, RFC1035_MAXNAMESIZE);
00180 
00181        if (rfc1035_resolve_cname(res, namebuf,
00182                               RFC1035_TYPE_TXT, RFC1035_CLASS_IN,
00183                               &reply, 0) < 0 ||
00184            reply == 0 ||
00185            (n=rfc1035_replysearch_an(res, reply, namebuf, RFC1035_TYPE_TXT,
00186                                   RFC1035_CLASS_IN, 0)) < 0)
00187        {
00188               switch (reply ? reply->rcode:
00189                      RFC1035_RCODE_SERVFAIL) {
00190               case RFC1035_RCODE_NOERROR:
00191                      rfc1035_replyfree(reply);
00192                      return SPF_NONE;
00193               case RFC1035_RCODE_NXDOMAIN:
00194                      rfc1035_replyfree(reply);
00195                      return SPF_UNKNOWN;
00196               default:
00197                      break;
00198               }
00199               if (reply)
00200                      rfc1035_replyfree(reply);
00201               return SPF_ERROR;
00202        }
00203 
00204        while (n >= 0)
00205        {
00206               if (isspf1(reply, n))
00207                      break;
00208 
00209               n=rfc1035_replysearch_an(res, reply, namebuf,
00210                                     RFC1035_TYPE_TXT, RFC1035_CLASS_IN,
00211                                     n+1);
00212        }
00213 
00214        if (n >= 0)
00215        {
00216               for (o=n; (o=rfc1035_replysearch_an(res, reply, namebuf,
00217                                               RFC1035_TYPE_TXT,
00218                                               RFC1035_CLASS_IN,
00219                                               o+1)) >= 0; )
00220               {
00221 
00222                      /*
00223                      **
00224                      ** A domain MUST NOT return multiple records that
00225                      ** begin with the version "v=spf1".  If more than
00226                      ** one "v=spf1" record is returned, this constitutes
00227                      ** a syntax error and the result is "unknown".
00228                      */
00229 
00230                      if (isspf1(reply, o))
00231                      {
00232                             rfc1035_replyfree(reply);
00233                             return SPF_UNKNOWN;
00234                      }
00235               }
00236 
00237               rfc1035_rr_gettxt(reply->allrrs[n], 0, buf);
00238               rfc1035_replyfree(reply);
00239               return SPF_PASS;
00240        }
00241        rfc1035_replyfree(reply);
00242        return SPF_UNKNOWN;
00243 }
00244 
00245 char rfc1035_spf_gettxt(const char *current_domain,
00246                      char *buf)
00247 {
00248        struct rfc1035_res res;
00249        char c;
00250 
00251        rfc1035_init_resolv(&res);
00252 
00253        c=rfc1035_spf_gettxt_res(current_domain, buf, &res);
00254 
00255        rfc1035_destroy_resolv(&res);
00256 
00257        return c;
00258 }
00259 
00260 /*
00261 ** Chop up an SPF record into whitespace-delimited words.
00262 ** get_words() is called twice: once with wordptr=NULL - return # of words,
00263 ** second time with wordptr!=NULL, parse the words.
00264 */
00265 
00266 static unsigned get_words(char *record, char **wordptr)
00267 {
00268        unsigned n=0;
00269 
00270        while (*record)
00271        {
00272               if (isspace((int)(unsigned char)*record))
00273               {
00274                      ++record;
00275                      continue;
00276               }
00277 
00278               if (wordptr)
00279                      *wordptr++=record;
00280               ++n;
00281 
00282               while (*record)
00283               {
00284                      if (isspace((int)(unsigned char)*record))
00285                             break;
00286                      ++record;
00287               }
00288 
00289               if (*record && wordptr)
00290                      *record++=0;
00291        }
00292        return n;
00293 }
00294 
00295 static char spf_compute(char **words,
00296                      struct rfc1035_spf_info *info);
00297 
00298 char rfc1035_spf_compute(char *record,
00299                       struct rfc1035_spf_info *info)
00300 {
00301        unsigned n=get_words(record, NULL);
00302        char **words=malloc((n+1)*sizeof(char *));
00303        char rc;
00304 
00305        if (words == NULL)
00306        {
00307               set_err_msg(info->errmsg_buf, info->errmsg_buf_size,
00308                          strerror(errno));
00309               return SPF_ERROR;
00310        }
00311 
00312        get_words(record, words);
00313        words[n]=0;
00314 
00315        rc=spf_compute(words, info);
00316        free(words);
00317        return rc;
00318 }
00319 
00320 static char mechanism(const char *name,
00321                     struct rfc1035_spf_info *info);
00322 
00323 static void setexp(const char *name,
00324                  struct rfc1035_spf_info *info);
00325 static char *expand(const char *str,
00326                   struct rfc1035_spf_info *info);
00327 
00328 
00329 static char spf_compute(char **words,
00330                      struct rfc1035_spf_info *info)
00331 {
00332        size_t i;
00333        char rc;
00334        const char *exp=NULL;
00335        const char *redirect=NULL;
00336 
00337        for (i=0; words[i]; i++)
00338               if (strncasecmp(words[i], "exp=", 4) == 0)
00339                      exp=words[i]+4;
00340 
00341        for (i=0; words[i]; i++)
00342        {
00343               const char *name;
00344               char prefix;
00345 
00346               if (strncasecmp(words[i], "redirect=", 9) == 0)
00347                      redirect=words[i]+9;
00348 
00349               if (strchr(words[i], '='))
00350                      continue;
00351 
00352               name=words[i];
00353 
00354               switch (*name) {
00355               case '+':
00356               case '-':
00357               case '?':
00358               case '~':
00359                      prefix=*name++;
00360                      break;
00361               default:
00362                      prefix='+';
00363                      break;
00364               }
00365 
00366               rc=mechanism(name, info);
00367 
00368               /*
00369               ** When a mechanism is evaluated, one of three things can
00370               ** happen: it can match, it can not match, or it can throw an
00371               ** exception.
00372               */
00373 
00374               if (rc == SPF_PASS)
00375               {
00376                      /*
00377                      ** If it matches, processing ends and the prefix value
00378                      ** is returned as the result of that record.
00379                      */
00380 
00381                      if (prefix != SPF_PASS && exp)
00382                             setexp(exp, info);
00383                      return prefix;
00384               }
00385 
00386               if (rc == SPF_FAIL)
00387               {
00388                      /*
00389                      ** If it does not match, processing continues with
00390                      ** the next
00391                      ** mechanism.
00392                      */
00393 
00394                      continue;
00395               }
00396 
00397               /*
00398               ** If it throws an exception, mechanism processing ends and
00399               ** the exception value is returned (either "error"
00400               ** indicating a temporary failure, usually DNS-related, or
00401               ** "unknown" indicating a syntax error or other permanent
00402               ** failure resulting in incomplete processing.)
00403               */
00404               return rc;
00405        }
00406 
00407        if (redirect)
00408        {
00409               /*
00410               ** If all mechanisms fail to match, and a redirect modifier
00411               ** is present, then processing proceeds as follows.
00412               **
00413               ** The domain-spec portion of the redirect section is expanded
00414               ** as per the macro rules in section 7.  The resulting string
00415               ** is a new domain that is now queried:  The <current-domain>
00416               ** is set to this new domain, and the new domain's SPF record
00417               ** is fetched and processed.  Note that <responsible-sender>
00418               ** does not change.
00419               **
00420               ** The result of this new query is then considered the result
00421               ** of original query.
00422               */
00423 
00424 
00425               char *new_domain;
00426               struct rfc1035_spf_info newinfo;
00427               char rc;
00428 
00429               new_domain=expand(redirect, info);
00430 
00431               if (!new_domain)
00432                      return SPF_ERROR;
00433 
00434               newinfo= *info;
00435               newinfo.current_domain=new_domain;
00436 
00437               rc=lookup(&newinfo);
00438               free(new_domain);
00439               return rc;
00440        }
00441 
00442        /*
00443        ** If none of the mechanisms match and there is no redirect modifier,
00444        ** then the result of the SPF query is "neutral".
00445        */
00446 
00447        if (exp)
00448               setexp(exp, info);
00449 
00450        return SPF_NEUTRAL;
00451 }
00452 
00453 static int get_dual_cidr_length(const char *p)
00454 {
00455 #if RFC1035_IPV6
00456        const char *q;
00457 
00458        for (q=p; *q; q++)
00459               if (*q == '/' && q[1] == '/')
00460                      return atoi(q+2);
00461 
00462        return atoi(p+1)+12*8;
00463 #else
00464        if (p[1] == '/')
00465               return -1;
00466        return atoi(p+1);
00467 #endif
00468 }
00469 
00470 static int ip_compare(const RFC1035_ADDR *a,
00471                     const RFC1035_ADDR *b,
00472                     int pfix)
00473 {
00474        const unsigned char *ca, *cb;
00475        unsigned i;
00476 
00477        if (pfix < 0)
00478               return 0;
00479 
00480        ca=(const unsigned char *)a;
00481        cb=(const unsigned char *)b;
00482 
00483        for (i=0; i<sizeof(RFC1035_ADDR); i++)
00484        {
00485               int bits=pfix>8?8:pfix;
00486               unsigned char m=(unsigned char )(~0 << (8-bits));
00487 
00488               if ((ca[i] & m) != (cb[i] & m))
00489                      return 0;
00490 
00491               pfix -= bits;
00492        }
00493        return 1;
00494 }
00495 
00496 static void get_domain_pfix(struct rfc1035_spf_info *info,
00497                          const char *start,
00498                          char **domain_ptr,
00499                          int  *pfix_ptr)
00500 {
00501        *pfix_ptr=sizeof(RFC1035_ADDR)*8;
00502 
00503        if (*start == 0 || *start == '/')
00504        {
00505               *domain_ptr=strdup(strrchr(info->mailfrom, '@')+1);
00506 
00507               if (*start == '/')
00508                      *pfix_ptr=get_dual_cidr_length(start);
00509        }
00510        else
00511        {
00512               char *p;
00513 
00514               *domain_ptr=strdup(*start == ':' ? start+1:start);
00515 
00516               p=strchr(*domain_ptr, '/');
00517               if (p)
00518               {
00519                      *pfix_ptr=get_dual_cidr_length(p);
00520                      *p++=0;
00521               }
00522 
00523               if (*domain_ptr == 0)
00524               {
00525                      free(*domain_ptr);
00526                      *domain_ptr=strdup(strrchr(info->mailfrom,
00527                                              '@')+1);
00528               }
00529        }
00530 
00531        if (!*domain_ptr)
00532               set_err_msg(info->errmsg_buf, info->errmsg_buf_size,
00533                          strerror(errno));
00534 }
00535 
00536 struct ptr_info {
00537        const char *name;
00538        struct rfc1035_spf_info *info;
00539        RFC1035_ADDR addr;
00540        int found;
00541        int error;
00542 };
00543 
00544 static void check_ptr(const char *ptr, void *void_arg)
00545 {
00546        struct ptr_info *pinfo=(struct ptr_info *)void_arg;
00547        RFC1035_ADDR *addr;
00548        unsigned addr_cnt;
00549        int rc;
00550        unsigned i;
00551 
00552        if (pinfo->found)
00553               return; /* No need */
00554 
00555        rc=rfc1035_a(&pinfo->info->res, ptr, &addr, &addr_cnt);
00556 
00557        if (rc > 0)
00558               pinfo->error=1;
00559        if (rc)
00560               return;
00561 
00562        for (i=0; i<addr_cnt; i++)
00563        {
00564               if (memcmp(&addr[i], &pinfo->addr,
00565                         sizeof(pinfo->addr)) == 0)
00566                      break;
00567        }
00568 
00569        if (i < addr_cnt)
00570        {
00571               size_t l1, l2;
00572 
00573               if (strcasecmp(ptr, pinfo->name) == 0)
00574                      pinfo->found=1;
00575 
00576               l1=strlen(ptr);
00577               l2=strlen(pinfo->name);
00578 
00579               if (l2 < l1)
00580               {
00581                      ptr=ptr+l1-l2;
00582 
00583                      if (ptr[-1] == '.' && strcasecmp(ptr, pinfo->name)==0)
00584                             pinfo->found=1;
00585               }
00586        }
00587        free(addr);
00588 }
00589 
00590 static char do_ptr(const char *name,
00591                  struct rfc1035_spf_info *info)
00592 {
00593        struct ptr_info pinfo;
00594 
00595        if (*name++ == ':')
00596               pinfo.name=name;
00597        else
00598               pinfo.name=strrchr(info->mailfrom, '@')+1;
00599 
00600        pinfo.info=info;
00601        pinfo.found=0;
00602        pinfo.error=0;
00603 
00604        /*
00605        ** First the <sending-host>'s name is looked up using this
00606        ** procedure:
00607        **
00608        ** perform a PTR lookup against the <sending-host>'s IP.  For
00609        ** each record returned, validate the host name by looking up
00610        ** its IP address.  If the <sending-host>'s IP is among the
00611        ** returned IP addresses, then that host name is validated.
00612        **
00613        ** Check all validated hostnames to see if they end in the
00614        ** <target-name> domain.  If any do, this mechanism matches.
00615        ** If no validated hostname can be found, or if none of the
00616        ** validated hostnames end in the <target-name>, this
00617        ** mechanism fails to match.
00618        */
00619 
00620        if (rfc1035_aton(info->tcpremoteip, &pinfo.addr) < 0)
00621        {
00622               set_err_msg(info->errmsg_buf, info->errmsg_buf_size,
00623                          "Invalid tcpremoteip.\n");
00624               return SPF_FAIL;
00625        }
00626 
00627        if (rfc1035_ptr_x(&info->res, &pinfo.addr,
00628                        check_ptr, &pinfo) < 0)
00629        {
00630               if (errno == ENOENT)
00631                      return SPF_FAIL;
00632 
00633               return SPF_ERROR;
00634        }
00635 
00636        if (pinfo.found)
00637               return SPF_PASS;
00638        if (pinfo.error)
00639        {
00640               set_err_msg(info->errmsg_buf, info->errmsg_buf_size,
00641                          "ptr lookup failed.\n");
00642               return SPF_UNKNOWN;
00643        }
00644        return SPF_FAIL;
00645 }
00646 
00647 static char do_ipcheck(const char *name, struct rfc1035_spf_info *info,
00648                      int pfix_add)
00649 {
00650        char *addrptr;
00651        char *p;
00652        int pfix;
00653        RFC1035_ADDR addr, addrcmp;
00654 
00655        /*
00656        ** These mechanisms test if the <sending-host> falls into a
00657        ** given IP network.
00658        **
00659        ** The <sending-host> is compared to the given network.  If
00660        ** they match, the mechanism matches.
00661        */
00662 
00663        if (rfc1035_aton(info->tcpremoteip, &addr) < 0)
00664        {
00665               set_err_msg(info->errmsg_buf, info->errmsg_buf_size,
00666                          "Invalid tcpremoteip.\n");
00667               return SPF_FAIL;
00668        }
00669 
00670        if ((addrptr=strdup(name+4)) == NULL)
00671        {
00672               set_err_msg(info->errmsg_buf, info->errmsg_buf_size,
00673                          strerror(errno));
00674               return SPF_ERROR;
00675        }
00676 
00677        p=strrchr(addrptr, '/');
00678        pfix=sizeof(RFC1035_ADDR)*8;
00679 
00680        if (p)
00681        {
00682               *p++=0;
00683               pfix=atol(p)+pfix_add;
00684        }
00685 
00686        if (rfc1035_aton(addrptr, &addrcmp) < 0)
00687        {
00688               free(addrptr);
00689               return SPF_FAIL;
00690        }
00691        free(addrptr);
00692        if (ip_compare(&addr, &addrcmp, pfix))
00693               return SPF_PASS;
00694        return SPF_FAIL;
00695 }
00696 
00697 static char mechanism(const char *name,
00698                     struct rfc1035_spf_info *info)
00699 {
00700        if (strcasecmp(name, "all") == 0)
00701        {
00702               /*
00703               ** The "all" mechanism is a test that always matches.  It is
00704               ** used as the rightmost mechanism in an SPF record to
00705               ** provide an explicit default.
00706               */
00707               return SPF_PASS;
00708        }
00709 
00710        if (strncasecmp(name, "include:", 8) == 0)
00711        {
00712               char *new_domain;
00713               struct rfc1035_spf_info newinfo;
00714               char rc;
00715 
00716               /*
00717               ** The "include" mechanism triggers a recursive SPF query.  The
00718               ** domain-spec is expanded as per section 7.  Then a new query
00719               ** is launched using the resulting string as the
00720               ** <current-domain>.  The <responsible-sender> stays the same.
00721               */
00722 
00723               new_domain=expand(name+8, info);
00724 
00725               if (!new_domain)
00726                      return SPF_ERROR;
00727 
00728               newinfo= *info;
00729               newinfo.current_domain=new_domain;
00730 
00731               /*
00732               **      included    include
00733               **      query       mechanism      SPF
00734               **      result      result         processing
00735               **      -------- -- -------------- -------------------------------------
00736               **      pass     => match,         return the prefix value for "include"
00737               **      fail     => no match,      continue processing
00738               **      softfail => no match,      continue processing
00739               **      neutral  => no match,      continue processing
00740               **      error    => throw error,   abort processing, return error
00741               **      unknown  => throw unknown, abort processing, return unknown
00742               **      none     => throw unknown, abort processing, return unknown
00743               */
00744 
00745               rc=lookup(&newinfo);
00746               free(new_domain);
00747 
00748               switch (rc) {
00749               case SPF_PASS:
00750                      return SPF_PASS;
00751               case SPF_FAIL:
00752               case SPF_SOFTFAIL:
00753               case SPF_NEUTRAL:
00754                      return SPF_FAIL;
00755               case SPF_ERROR:
00756                      return SPF_ERROR;
00757               default:
00758                      return SPF_UNKNOWN;
00759               }
00760        }
00761 
00762        if (strncasecmp(name, "a", 1) == 0 &&
00763            (name[1] == 0 || name[1] == ':' || name[1] == '/'))
00764        {
00765               char *domain_spec;
00766               int pfix;
00767               RFC1035_ADDR addr;
00768 
00769               RFC1035_ADDR *iaptr;
00770               unsigned iasize;
00771               int rc;
00772               unsigned ii;
00773 
00774               /*
00775               ** This mechanism matches if the <sending-host> is one of the
00776               ** <target-name>'s IP addresses.
00777               **
00778               ** A = "a" [ ":" domain-spec ] [ dual-cidr-length ]
00779               **
00780               ** The <sending-host> is compared to the IP address(es) of the
00781               ** <target-name>.  If any address matches, the mechanism
00782               ** matches.
00783               */
00784 
00785               get_domain_pfix(info, name+1, &domain_spec, &pfix);
00786 
00787               if (!domain_spec)
00788                      return SPF_ERROR;
00789 
00790               if (rfc1035_aton(info->tcpremoteip, &addr) < 0)
00791               {
00792                      free(domain_spec);
00793                      set_err_msg(info->errmsg_buf, info->errmsg_buf_size,
00794                                 "Invalid tcpremoteip.\n");
00795                      return SPF_FAIL;
00796               }
00797 
00798               rc=rfc1035_a(&info->res,
00799                           domain_spec,
00800                           &iaptr,
00801                           &iasize);
00802 
00803               free(domain_spec);
00804 
00805               if (rc != 0)
00806               {
00807                      set_err_msg(info->errmsg_buf, info->errmsg_buf_size,
00808                                 "IP address lookup failed.\n");
00809                      return SPF_UNKNOWN;
00810               }
00811 
00812               for (ii=0; ii<iasize; ii++)
00813                      if (ip_compare(&addr, iaptr+ii, pfix))
00814                      {
00815                             free(iaptr);
00816                             return SPF_PASS;
00817                      }
00818 
00819               free(iaptr);
00820               return SPF_FAIL;
00821        }
00822 
00823 
00824        if (strncasecmp(name, "mx", 2) == 0 &&
00825            (name[2] == 0 || name[2] == ':' || name[2] == '/'))
00826        {
00827               char *domain_spec;
00828               int pfix;
00829               int rc;
00830               struct rfc1035_mxlist *mxlist, *mxp;
00831               RFC1035_ADDR addr;
00832 
00833               /*
00834               ** This mechanism matches if the <sending-host> is one of the
00835               ** MX hosts for a domain name.
00836    
00837               ** MX = "mx" [ ":" domain-spec ] [ dual-cidr-length ]
00838     
00839               ** SPF clients first perform an MX lookup on the <target-name>.
00840               ** SPF clients then perform an A lookup on each MX name
00841               ** returned, in order of MX priority.  The <sending-host> is
00842               ** compared to each returned IP address.  If any address
00843               ** matches, the mechanism matches.
00844               */
00845 
00846               get_domain_pfix(info, name+2, &domain_spec, &pfix);
00847 
00848               if (!domain_spec)
00849                      return SPF_ERROR;
00850 
00851               if (rfc1035_aton(info->tcpremoteip, &addr) < 0)
00852               {
00853                      free(domain_spec);
00854                      set_err_msg(info->errmsg_buf, info->errmsg_buf_size,
00855                                 "Invalid tcpremoteip.\n");
00856                      return SPF_FAIL;
00857               }
00858 
00859               rc=rfc1035_mxlist_create_x(&info->res,
00860                                       domain_spec, 0,
00861                                       &mxlist);
00862               free(domain_spec);
00863               if (rc)
00864               {
00865                      rfc1035_mxlist_free(mxlist);
00866                      set_err_msg(info->errmsg_buf, info->errmsg_buf_size,
00867                                 "DNS MX lookup failed.\n");
00868                      return SPF_ERROR;
00869               }
00870 
00871               for (mxp=mxlist; mxp; mxp=mxp->next)
00872               {
00873                      RFC1035_ADDR addrcmp;
00874 
00875                      if (rfc1035_sockaddrip(&mxp->address,
00876                                           sizeof(mxp->address),
00877                                           &addrcmp) < 0)
00878                             continue;
00879 
00880                      if (ip_compare(&addr, &addrcmp, pfix))
00881                      {
00882                             rfc1035_mxlist_free(mxlist);
00883                             return SPF_PASS;
00884                      }
00885               }
00886               rfc1035_mxlist_free(mxlist);
00887               return SPF_FAIL;
00888        }
00889 
00890        if (strncasecmp(name, "ip4:", 4) == 0)
00891        {
00892               if (strchr(name+4, ':'))
00893                      return SPF_FAIL; /* What does IPv6 addr doing here? */
00894 
00895 #if RFC1035_IPV6
00896               return do_ipcheck(name, info, 12*8);
00897 #else
00898               return do_ipcheck(name, info, 0);
00899 #endif
00900        }
00901 
00902        if (strncasecmp(name, "ip6:", 4) == 0)
00903        {
00904 #if RFC1035_IPV6
00905               return do_ipcheck(name, info, 0);
00906 #else
00907               return SPF_FAIL;
00908 #endif
00909        }
00910 
00911        if (strncasecmp(name, "ptr", 3) == 0 &&
00912            (name[3] == 0 || name[3] == ':'))
00913        {
00914               return do_ptr(name+3, info);
00915        }
00916 
00917        if (strncasecmp(name, "exists:", 7) == 0)
00918        {
00919               char *domain_spec;
00920               RFC1035_ADDR *iaptr;
00921               unsigned iasize;
00922               int rc;
00923 
00924               /*
00925               ** This mechanism is used to construct an arbitrary host name
00926               ** that is used for a DNS A record query.  It allows for
00927               ** complicated schemes involving arbitrary parts of the mail
00928               ** envelope to determine what is legal.
00929               **
00930               ** exists = "exists" ":" domain-spec
00931               **
00932               ** The domain-spec is expanded as per Section 7.  The
00933               ** resulting domain name is used for a DNS A lookup.  If any
00934               ** A record is returned, this mechanism matches.  The lookup
00935               ** type is 'A' even when the connection type is IPv6.
00936               */
00937 
00938               domain_spec=expand(name+7, info);
00939               if (!domain_spec)
00940                      return SPF_ERROR;
00941 
00942               rc=rfc1035_a(&info->res,
00943                           domain_spec,
00944                           &iaptr,
00945                           &iasize);
00946               free(domain_spec);
00947 
00948               if (rc < 0)
00949                      return SPF_FAIL;
00950               if (rc > 0)
00951                      return SPF_ERROR;
00952               free(iaptr);
00953               return SPF_PASS;
00954        }
00955 
00956        return SPF_FAIL;
00957 }
00958 
00959 static void setexp(const char *exp,
00960                  struct rfc1035_spf_info *info)
00961 {
00962        struct rfc1035_reply *reply;
00963        char   namebuf[RFC1035_MAXNAMESIZE+1];
00964        char   txtbuf[256];
00965        int n;
00966        char   *str;
00967 
00968        /*
00969        ** The argument to the explanation modifier is a domain-spec
00970        ** to be TXT queried.  The result of the TXT query is a
00971        ** macro-string that is macro-expanded.  If SPF processing
00972        ** results in a rejection, the expanded result SHOULD be
00973        ** shown to the sender in the SMTP reject message.  This
00974        ** string allows the publishing domain to communicate further
00975        ** information via the SMTP receiver to legitimate senders in
00976        ** the form of a short message or URL.
00977        */
00978 
00979 
00980        namebuf[0]=0;
00981        strncat(namebuf, exp, RFC1035_MAXNAMESIZE);
00982 
00983        if (rfc1035_resolve_cname(&info->res, namebuf,
00984                               RFC1035_TYPE_TXT, RFC1035_CLASS_IN,
00985                               &reply, 0) < 0 ||
00986            reply == 0 ||
00987            (n=rfc1035_replysearch_an(&info->res,
00988                                   reply, namebuf, RFC1035_TYPE_TXT,
00989                                   RFC1035_CLASS_IN, 0)) < 0)
00990        {
00991               set_err_msg(info->errmsg_buf,
00992                          info->errmsg_buf_size,
00993                          "A DNS lookup error occured while"
00994                          " fetching the SPF explanation record.");
00995        }
00996        else
00997        {
00998               rfc1035_rr_gettxt(reply->allrrs[n], 0, txtbuf);
00999 
01000               str=expand(txtbuf, info);
01001 
01002               set_err_msg(info->errmsg_buf,
01003                          info->errmsg_buf_size,
01004                          str ? str:strerror(errno));
01005               if (str)
01006                      free(str);
01007        }
01008        rfc1035_replyfree(reply);
01009 }
01010 
01011 static char lookup(struct rfc1035_spf_info *info)
01012 {
01013        char record[256];
01014        char c;
01015 
01016        /*
01017        ** 
01018        ** If a loop is detected, or if more than 20 subqueries are triggered,
01019        ** an SPF client MAY abort the lookup and return the result "unknown".
01020        */
01021 
01022        if (++*info->lookup_cnt > 20)
01023        {
01024               set_err_msg(info->errmsg_buf, info->errmsg_buf_size,
01025                          "Maximum of 20 nested SPF queries exceeded.");
01026               return SPF_UNKNOWN;
01027        }
01028 
01029        c=rfc1035_spf_gettxt(info->current_domain, record);
01030 
01031        if (c != SPF_PASS)
01032               return c;
01033 
01034        return rfc1035_spf_compute(record, info);
01035 }
01036 
01037 /*
01038 **
01039 ** Certain directives perform macro interpolation on their arguments.
01040 **
01041 ** Two passes: count # of chars in the expanded macro, generate the macro.
01042 */
01043 
01044 static int do_expand(const char *str, struct rfc1035_spf_info *info,
01045                    void (*cb_func)(const char *, size_t n, void *),
01046                    void *void_arg);
01047 
01048 static void do_count(const char *p, size_t n, void *va)
01049 {
01050        *(size_t *)va += n;
01051 }
01052 
01053 static void do_save(const char *p, size_t n, void *va)
01054 {
01055        char **b=(char **)va;
01056 
01057        memcpy(*b, p, n);
01058        *b += n;
01059 }
01060 
01061 static char *expand(const char *str,
01062                   struct rfc1035_spf_info *info)
01063 {
01064        size_t cnt=1;
01065        char *buf;
01066        char *p;
01067 
01068        if (do_expand(str, info, do_count, &cnt) < 0)
01069               return NULL;
01070 
01071        buf=malloc(cnt);
01072 
01073        if (!buf)
01074        {
01075               set_err_msg(info->errmsg_buf, info->errmsg_buf_size,
01076                          strerror(errno));
01077               return NULL;
01078        }
01079 
01080        p=buf;
01081        if (do_expand(str, info, do_save, &p) < 0)
01082        {
01083               free(buf);
01084               return NULL;
01085        }
01086 
01087        *p=0;
01088        return buf;
01089 }
01090 
01091 static char *get_macro(struct rfc1035_spf_info *info, char name);
01092 
01093 static char *transform(char *macro,
01094                      unsigned transformer_count,
01095                      char transformer_reverse,
01096                      char delimiter_char);
01097 
01098 static int do_expand(const char *str, struct rfc1035_spf_info *info,
01099                     void (*cb_func)(const char *, size_t, void *),
01100                     void *void_arg)
01101 {
01102        unsigned char alpha, lalpha;
01103        unsigned transformer_count;
01104        char transformer_reverse;
01105        char delimiter_char;
01106        char *macro;
01107 
01108        /*
01109        **     macro-string = *( macro-char / VCHAR )
01110        **     macro-char   = ( "%{" ALPHA transformer *delimiter "}" )
01111        **                    / "%%" / "%_" / "%-"
01112        **     transformer  = [ *DIGIT ] [ "r" ]
01113        **     delimiter    = "." / "-" / "+" / "," / "/" / "_" / "="
01114        **
01115        */
01116 
01117        while (*str)
01118        {
01119               size_t i;
01120 
01121               for (i=0; str[i]; i++)
01122                      if (str[i] == '%')
01123                             break;
01124 
01125               if (i)
01126               {
01127                      (*cb_func)(str, i, void_arg);
01128                      str += i;
01129                      continue;
01130               }
01131 
01132               /*
01133               **   A literal "%" is expressed by "%%".
01134               **   %_ expands to a single " " space.
01135               **   %- expands to a URL-encoded space, viz. "%20".
01136               */
01137 
01138               switch (str[i+1]) {
01139               case '{':
01140                      break;
01141               case '%':
01142                      (*cb_func)("%", 1, void_arg);
01143                      str += 2;
01144                      continue;
01145               case '_':
01146                      (*cb_func)(" ", 1, void_arg);
01147                      str += 2;
01148                      continue;
01149               case '-':
01150                      (*cb_func)("%20", 3, void_arg);
01151                      str += 2;
01152                      continue;
01153               default:
01154                      ++str;
01155                      continue;
01156               }
01157 
01158               str += 2;
01159 
01160               if (!*str)
01161                      continue;
01162               alpha=(unsigned char)*str++;
01163               transformer_count=0;
01164               while (*str && isdigit((unsigned char)*str))
01165               {
01166                      transformer_count=transformer_count * 10 +
01167                             (*str++ - '0');
01168               }
01169 
01170               transformer_reverse=0;
01171               delimiter_char=0;
01172 
01173               while (*str && *str != '}')
01174               {
01175                      switch (*str) {
01176                      case 'r':
01177                      case 'R':
01178                             transformer_reverse='r';
01179                             break;
01180                      case '.':
01181                      case '-':
01182                      case '+':
01183                      case ',':
01184                      case '/':
01185                      case '_':
01186                      case '=':
01187                             delimiter_char= *str;
01188                             break;
01189                      }
01190                      ++str;
01191               }
01192               lalpha=tolower(alpha);
01193 
01194               macro=get_macro(info, lalpha);
01195               if (macro && (transformer_reverse || transformer_count))
01196               {
01197                      char *new_macro=transform(macro, transformer_count,
01198                                             transformer_reverse,
01199                                             delimiter_char);
01200 
01201                      free(macro);
01202                      macro=new_macro;
01203               }
01204 
01205               if (macro && lalpha != alpha)
01206               {
01207                      static const char validchars[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
01208                      size_t l=1;
01209                      size_t i,j;
01210                      char *p;
01211 
01212                      for (i=0; macro[i]; i++)
01213                      {
01214                             ++l;
01215                             if (strchr(validchars, macro[i]) == NULL)
01216                                    l += 2;
01217                      }
01218 
01219                      p=malloc(l);
01220                      for (i=j=0; p && macro[i]; i++)
01221                      {
01222                             if (strchr(validchars, macro[i]))
01223                             {
01224                                    p[j]=macro[i];
01225                                    ++j;
01226                             }
01227                             else
01228                             {
01229                                    sprintf(p+j, "%%%02X",
01230                                           (int)(unsigned char)macro[i]);
01231                                    j += 3;
01232                             }
01233                      }
01234                      if (p)
01235                             p[j]=0;
01236                      free(macro);
01237                      macro=p;
01238               }
01239 
01240               if (macro == NULL)
01241               {
01242                      set_err_msg(info->errmsg_buf,
01243                                 info->errmsg_buf_size,
01244                                 strerror(errno));
01245                      return -1;
01246               }
01247 
01248               (*cb_func)(macro, strlen(macro), void_arg);
01249               free(macro);
01250               if (*str == '}')
01251                      ++str;
01252        }
01253        return 0;
01254 }
01255 
01256 static char *expandc(const char *ipaddr);
01257 static char *expandi(const char *ipaddr);
01258 
01259 static char *get_macro(struct rfc1035_spf_info *info, char name)
01260 {
01261        char *p;
01262        const char *cp;
01263 
01264        switch (name) {
01265        case 'l':
01266               /* l = local-part of responsible-sender */
01267 
01268               cp=strrchr(info->mailfrom, '@');
01269               p=malloc(cp-info->mailfrom+1);
01270               if (!p)
01271                      return p;
01272               memcpy(p, info->mailfrom, cp-info->mailfrom);
01273               p[cp-info->mailfrom]=0;
01274               return p;
01275        case 's':
01276               /* s = responsible-sender */
01277               return strdup(info->mailfrom);
01278        case 'o':
01279               return strdup(strrchr(info->mailfrom, '@')+1);
01280               /* o = responsible-domain */
01281        case 'd':
01282               return strdup(info->current_domain);
01283               /* d = current-domain */
01284        case 'c':
01285               return expandc(info->tcpremoteip);
01286               /* c = SMTP client IP (easily readable format) */
01287        case 'i':
01288               return expandi(info->tcpremoteip);
01289               /* i = SMTP client IP (nibble format when an IPv6 address) */
01290        case 'p':
01291               return strdup(info->tcpremotehost);
01292               /* p = SMTP client domain name */
01293        case 'v':
01294               return (strdup(strchr(info->tcpremoteip, ':') &&
01295                             strncmp(info->tcpremoteip, "::ffff:", 7)
01296                             ? "ip6":"in-addr"));
01297               /* v = client IP version string: "in-addr" for ipv4 or "ip6" for ipv6 */
01298        case 'h':
01299        /* h = HELO/EHLO domain */
01300               return strdup(info->helodomain);
01301        case 'r':
01302        /* r = receiving domain */
01303               return strdup(info->mydomain);
01304        }
01305 
01306        return strdup("");
01307 }
01308 
01309 /*
01310 **
01311 **   For IPv4 addresses, both the "i" and "c" macros expand to the
01312 **   standard dotted-quad format.
01313 **
01314 **   For IPv6 addresses, the "i" macro expands to dot-format address; it
01315 **   is intended for use in %{ir}.  The "c" macro may expand to any of
01316 **   the hexadecimal colon-format addresses specified in [RFC3513] section
01317 **   2.2.  It is intended for humans to read.
01318 */
01319 
01320 static char *expandc(const char *ipaddr)
01321 {
01322        if (strncmp(ipaddr, "::ffff:", 7) == 0)
01323               return strdup(ipaddr+7);
01324        return strdup(ipaddr);
01325 }
01326 
01327 static char *expandi(const char *ipaddr)
01328 {
01329        if (strchr(ipaddr, ':') &&
01330            strncmp(ipaddr, "::ffff:", 7))
01331        {
01332               RFC1035_ADDR addr;
01333 
01334               if (rfc1035_aton(ipaddr, &addr) == 0)
01335               {
01336                      char name[sizeof(addr)*4+1];
01337                      char *p=name;
01338                      int i;
01339                      unsigned char *q=(unsigned char *)&addr;
01340 
01341                      for (i=0; i<sizeof(addr); i++)
01342                      {
01343                             sprintf(p, "%s%x.%x", i ? ".":"",
01344                                    (int)((q[i] >> 4) & 0x0F),
01345                                    (int)(q[i] & 0x0F));
01346                             p += strlen(p);
01347                      }
01348                      return strdup(name);
01349               }
01350 
01351        }
01352        return expandc(ipaddr);
01353 }
01354 
01355 /*
01356 **   If transformers or delimiters are provided, the macro strings are
01357 **   split into parts.  After performing any reversal operation or
01358 **   removal of left-hand parts, the parts are rejoined using "." and not
01359 **   the original splitting characters.
01360 */
01361 
01362 static unsigned tsplit(char *macro, char delimiter, char **wordptr)
01363 {
01364        /* Two passes */
01365        unsigned cnt=0;
01366 
01367        if (!delimiter)
01368               delimiter='.';
01369 
01370        while (*macro)
01371        {
01372               ++cnt;
01373 
01374               if (wordptr)
01375                      *wordptr++=macro;
01376 
01377               while (*macro && *macro != delimiter)
01378                      ++macro;
01379 
01380               if (*macro)
01381               {
01382                      if (wordptr)
01383                             *macro=0;
01384                      ++macro;
01385               }
01386 
01387        }
01388        return cnt;
01389 }             
01390 
01391 static char *transform(char *macro,
01392                      unsigned transformer_count,
01393                      char transformer_reverse,
01394                      char delimiter_char)
01395 {
01396        char **words;
01397        unsigned n=tsplit(macro, delimiter_char, NULL);
01398        unsigned start;
01399        unsigned i;
01400        char *buf;
01401        size_t len;
01402 
01403        if ((words=malloc(sizeof(char *)*(n+1))) == NULL)
01404               return NULL;
01405        tsplit(macro, delimiter_char, words);
01406        words[n]=NULL;
01407 
01408        /*
01409        ** The DIGIT transformer indicates the number of right-hand parts to
01410        ** use after optional reversal.  If a DIGIT is specified, it MUST be
01411        ** nonzero.  If no DIGITs are specified, or if the value specifies more
01412        ** parts than are available, all the available parts are used.  If the
01413        ** DIGIT was 5, and only 3 parts were available, the macro interpreter
01414        ** would pretend the DIGIT was 3.  Implementations MAY limit the
01415        ** number, but MUST support at least a value of 9.
01416        */
01417 
01418        if (transformer_count > n || transformer_count <= 0)
01419               transformer_count=n;
01420 
01421        if (transformer_reverse)
01422        {
01423               start=0;
01424               n=transformer_count;
01425        }
01426        else
01427        {
01428               start=n-transformer_count;
01429        }
01430 
01431        len=1;
01432 
01433        for (i=start; i<n; i++)
01434        {
01435               len += strlen(words[i])+1;
01436        }
01437 
01438        buf=malloc(len);
01439        if (!buf)
01440        {
01441               free(words);
01442               return NULL;
01443        }
01444 
01445        *buf=0;
01446        if (transformer_reverse)
01447        {
01448               for (i=n; i>start; )
01449               {
01450                      if (*buf)
01451                             strcat(buf, ".");
01452                      strcat(buf, words[--i]);
01453               }
01454        }
01455        else
01456        {
01457               for (i=start; i<n; i++)
01458               {
01459                      if (*buf)
01460                             strcat(buf, ".");
01461                      strcat(buf, words[i]);
01462               }
01463        }
01464        free(words);
01465        return buf;
01466 }