libSPF v1.0: main.c Source File

Main Page | Data Structures | File List | Data Fields | Globals | Search for

# main.c

Go to the documentation of this file.

    00001 /* libspf - Sender Policy Framework library
    00002 *
    00003 *  ANSI C implementation of spf-draft-200405.txt
    00004 *
    00005 *  Author: James Couzens <jcouzens@codeshare.ca>
    00006 *  Author: Sean Comeau   <scomeau@obscurity.org>
    00007 *
    00008 *  File:   spf.c
    00009 *  Desc:   Main library functionality
    00010 *
    00011 *  License:
    00012 *
    00013 *  The libspf Software License, Version 1.0
    00014 *
    00015 *  Copyright (c) 2004 James Couzens & Sean Comeau  All rights
    00016 *  reserved.
    00017 *
    00018 *  Redistribution and use in source and binary forms, with or without
    00019 *  modification, are permitted provided that the following conditions
    00020 *  are met:
    00021 *
    00022 *  1. Redistributions of source code must retain the above copyright
    00023 *     notice, this list of conditions and the following disclaimer.
    00024 *
    00025 *  2. Redistributions in binary form must reproduce the above copyright
    00026 *     notice, this list of conditions and the following disclaimer in
    00027 *     the documentation and/or other materials provided with the
    00028 *     distribution.
    00029 *
    00030 *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
    00031 *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
    00032 *  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    00033 *  DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS MAKING USE OF THIS LICESEN
    00034 *  OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    00035 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    00036 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
    00037 *  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    00038 *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
    00039 *  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
    00040 *  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    00041 *  SUCH DAMAGE.
    00042 *
    00043 */
    00044
    00045 #ifdef _WITH_PTHREADS
    00046 #include <pthread.h>           /* pthread_mutex */
    00047 #endif /* _WITH_PTHREDS */
    00048
    00049 #include "../../config.h"      /* autoconf */
    00050 #include "main.h"              /* our header */
    00051 #include "util.h"              /* Utility functions */
    00052 #include "dns.h"               /* DNS functions */
    00053 #include "macro.h"             /* MACRO parsing functions */
    00054
    00055 #undef VERSION                 /* autoconf */
    00056
    00057 spf_config_t confg;            /* global config struct */
    00058
    00059 int h_errno;                   /* error handling */
    00060
    00061 #ifdef _WITH_PTHREADS
    00062 /* mutex for _DNS_gethostbyname_r wrapper */
    00063 extern pthread_mutex_t dns_mutex;
    00064 #endif /* _WITH_PTHREADS */
    00065
    00066 static SPF_BOOL _SPF_clear_holdbufs(peer_info_t *);
    00067
    00068 /* SPF_init
    00069 *
    00070 *  Author: James Couzens <jcouzens@codeshare.ca>
    00071 *
    00072 *  Date:   12/25/03
    00073 *
    00074 *  Desc:
    00075 *         Applies config options and returns allocated memory to a peer_info_t
    00076 *  structure.  Malloc returns NULL on a failure, and as such this function exits
    00077 *  with NULL upon failure to allocate memory which there by results in a
    00078 *  complete failure of the entire library.
    00079 *
    00080 */
    00081 peer_info_t *SPF_init(const char *local, const char *rip, const char *expl,
    00082   const char *tf, const char *guess, u_int32_t use_trust, u_int32_t use_guess)
    00083 {
    00084   time_t curtime;           /* time */
    00085
    00086   char *cur_utc_time;       /* time */
    00087
    00088   peer_info_t *p = NULL;    /* return structure buffer */
    00089
    00090    /*
    00091    *  spf_result struct including header texts.  This structure is used
    00092    *  during message header generation and has intended future purposes.
    00093    *  Each string has associated with it its actual size which is used
    00094    *  to provide some measure of protection against overflowing the strings
    00095    *  as they are (currently) statically defined in util.h
    00096   */
    00097   static spf_result_t spf_result[] =
    00098   {{SIZEOF(HR_PASS),    HR_PASS,    SPF_PASS,    SIZEOF(HDR_PASS),    HDR_PASS,    '+' },
    00099    {SIZEOF(HR_NONE),    HR_NONE,    SPF_NONE,    SIZEOF(HDR_NONE),    HDR_NONE,    '\0'},
    00100    {SIZEOF(HR_S_FAIL),  HR_S_FAIL,  SPF_S_FAIL,  SIZEOF(HDR_S_FAIL),  HDR_S_FAIL,  '~' },
    00101    {SIZEOF(HR_H_FAIL),  HR_H_FAIL,  SPF_H_FAIL,  SIZEOF(HDR_H_FAIL),  HDR_H_FAIL,  '-' },
    00102    {SIZEOF(HR_ERROR),   HR_ERROR,   SPF_ERROR,   SIZEOF(HDR_ERROR),   HDR_ERROR,   '\0'},
    00103    {SIZEOF(HR_NEUTRAL), HR_NEUTRAL, SPF_NEUTRAL, SIZEOF(HDR_NEUTRAL), HDR_NEUTRAL, '?' },
    00104    {SIZEOF(HR_UNKNOWN), HR_UNKNOWN, SPF_UNKNOWN, SIZEOF(HDR_UNKNOWN), HDR_UNKNOWN, '\0'},
    00105    {SIZEOF(HR_UNMECH),  HR_UNMECH,  SPF_UNMECH,  SIZEOF(HDR_UNMECH),  HDR_UNMECH,  '\0'}};
    00106
    00107    /*
    00108    * This structure here is the heart and soul or effectively the 'context'
    00109    * of libSPF.  This structure is passed back and forth between nearly
    00110    * every function which will operate on it in one way or another.
    00111   */
    00112   p = xmalloc(SIZEOF(peer_info_t));
    00113
    00114   p->spf_result = spf_result;
    00115
    00116
    00117    /*
    00118    * Set the SPF query recursion level to zero.  This counter is
    00119    * incremeneted to a maximum of SPF_MAX_RECURSE (default value of
    00120    * that macro is 20) to provide a limit of recursiveness.  Not only does
    00121    * it astound me to see people creating infinite loops unintentionally
    00122    * through the use of the 'include' and 'redirect' facilities within SPF,
    00123    * but there is also the greater than average chance that a skriptkiddie
    00124    * or some other individual with malicious intentions would use a lack of
    00125    * such a level (or a poorly set one, you REALLY should not raise this
    00126    * above 20) to faciliate a very effective DoS or dDoS attack against your
    00127    * server using SPF checks.
    00128   */
    00129   p->spf_rlevel = 0;
    00130
    00131    /*
    00132    * RFC2821 (pre-DATA) related variables
    00133    *
    00134   */
    00135
    00136   p->helo = NULL;
    00137   p->ehlo = NULL;
    00138   p->from = NULL;
    00139
    00140   if (local != NULL && (*local && *(local + 1)))
    00141   {
    00142     p->mta_hname = xstrndup(local, SPF_MAX_LOCAL_PART);
    00143     p->r_vhname  = xstrndup(local, SPF_MAX_LOCAL_PART);
    00144   }
    00145   else
    00146   {
    00147     p->mta_hname = NULL;
    00148     p->r_vhname  = NULL;
    00149     xepprintf("Warning: Invalid local-part detected (DSN or NULL?)\n");
    00150   }
    00151
    00152   /* don't change this, this is for Solaris which can't print NULL strings */
    00153   xvprintf("Called with: (%s) (%s) (%s) (%s) (%s) %u:%u\n",
    00154      local ? local : "NULL",
    00155      rip   ? rip   : "NULL",
    00156      expl  ? expl  : "NULL",
    00157      tf    ? tf    : "NULL",
    00158      guess ? guess : "NULL",
    00159      use_trust, use_guess);
    00160
    00161    /*
    00162    * Set the default SPF version level to zero.  This is important because
    00163    * the lack of a valid SPF version here is relied upon within SPF_parse_policy
    00164    * to determine an appropriate exit enforcing the RFC requirement that each
    00165    * SPF record publish a valid version within their SPF query.
    00166   */
    00167   p->spf_ver = 0;
    00168
    00169    /*
    00170    * "Trusted Forwarder" enabled?  This is also what you could use if you wanted to
    00171    * operate an 'in-house' whitelist for your server.  For more information about
    00172    * the officla "Trusted Forwarder" service visit http://trustedforwarder.org
    00173   */
    00174   if (use_trust == SPF_TRUE)
    00175   {
    00176     p->use_trust = SPF_TRUE;
    00177   }
    00178   else
    00179   {
    00180     p->use_trust = SPF_FALSE;
    00181   }
    00182
    00183    /*
    00184    * "Best Guess" enabled?  This is a great way to provide a "generic" SPF check
    00185    * against domains who do not publish SPF either through ignorance or refusal
    00186    * to do so.  The default best guess is defined in spf.h and is comprised of
    00187    * SPF syntax facilitating 'a', 'mx', and 'ptr' checks against the connected
    00188    * host when enabled in an attempt to validate them
    00189   */
    00190   if (use_guess == SPF_TRUE)
    00191   {
    00192     p->use_guess = SPF_TRUE;
    00193   }
    00194   else
    00195   {
    00196     p->use_guess = SPF_FALSE;
    00197   }
    00198
    00199   p->ALL = SPF_FALSE;
    00200   p->p   = NULL;
    00201
    00202    /*
    00203    * SPF Explanation.  This can be optionally pre-pended to the RFC2822
    00204    * message body as a means of informing a client when rejecting their
    00205    * message for a failure to appropriate pass a desired SPF level.
    00206   */
    00207   if (expl != NULL && (*expl && *(expl + 1)))
    00208   {
    00209     p->explain = xstrndup(expl, (strlen(expl) + 1));
    00210   }
    00211   else
    00212   {
    00213     p->explain = NULL;
    00214   }
    00215
    00216   if (guess != NULL && (*guess && *(guess + 1)))
    00217   {
    00218     p->guess = xstrndup(guess, (strlen(guess) + 1));
    00219   }
    00220   else
    00221   {
    00222     p->guess = xstrndup(SPF_GUESS, (SIZEOF(SPF_GUESS) + 1));
    00223   }
    00224
    00225   if ((tf != NULL) && (*tf && *(tf + 1)))
    00226   {
    00227     p->trusted = xstrndup(tf, (strlen(tf) + 1));
    00228   }
    00229   else
    00230   {
    00231     p->trusted = xstrndup(SPF_TRUSTED, (SIZEOF(SPF_TRUSTED) + 1));
    00232   }
    00233
    00234   p->ptr_mhost        = NULL;    /* MTA hostname */
    00235   p->current_domain   = NULL;    /* Current domain name (of client) */
    00236   p->cur_eaddr        = NULL;    /* Current email-address (of client) */
    00237
    00238    /*
    00239    * inet_pton is used to convert the IP address of the remotely connected
    00240    * client into a network address structure which will be used during
    00241    * and SPF parse to perform CIDR calculations against a variety of SPF
    00242    * mechanisms ('ip4', 'ip6', etc.).  Currently hard set for IPv4.
    00243    *
    00244    * A character version of the remote peer is retained within the
    00245    * peer_info_t structure by reason that there is no sense not doign
    00246    * so since we will need to reference it in a human readable form,
    00247    * and since the MTA has already performed the labour necessary to
    00248    * get it into this form, we might as well keep it.
    00249   */
    00250   if (((rip != NULL) && (*rip && *(rip + 1))) && (inet_pton(AF_INET, rip,
    00251     &p->addr) >= 0))
    00252   {
    00253      p->r_ip = xstrndup(rip, SPF_MAX_IP_ADDR);
    00254   }
    00255   else
    00256   {
    00257     xepprintf("Warning: Unable to execute inet_print (bad passed ipaddr?)\n");
    00258     SPF_close(p);
    00259
    00260     return(NULL);
    00261   }
    00262
    00263    /*
    00264    * 'ip_ver' is the Internet Address protocol version being used
    00265    * at any given time during an SPF parse.  Whilst this is intended to
    00266    * support IPv6 at this time it is hard set to IPv4 (AF_INET)
    00267   */
    00268   snprintf(p->ip_ver, SPF_MAX_IP_ADDR, "in-addr");
    00269
    00270    /*
    00271    * Obtain the number of seconds since the UNIX Epoch or UTC time
    00272    * which we may optionally be used when pre-pending the Received-SPF
    00273    * header after an SPF parse
    00274   */
    00275   cur_utc_time = xmalloc(SPF_MAX_DATETIME);
    00276   snprintf(cur_utc_time, SPF_MAX_DATETIME, "%lu", time(&curtime));
    00277   memcpy(p->utc_time, cur_utc_time, SPF_MAX_DATETIME);
    00278   xfree(cur_utc_time);
    00279
    00280    /*
    00281    * As per the SPF RFC, localhost shall always be considered exempt from
    00282    * any SPF checks.  There is no facility to override this, nor should
    00283    * you.
    00284   */
    00285   if ((strcmp(rip, "127.0.0.1") == 0) || (strcmp(rip, "localhost") == 0))
    00286   {
    00287     UTIL_assoc_prefix(p, SPF_PASS, NULL);
    00288   }
    00289   else
    00290   {
    00291     UTIL_assoc_prefix(p, SPF_NEUTRAL, NULL);
    00292   }
    00293
    00294   xprintf("libspf initialized succesfully. (%i bytes allocated)\n",
    00295     SIZEOF(peer_info_t));
    00296
    00297   return(p);
    00298 }
    00299
    00300
    00301 /* SPF_close
    00302 *
    00303 *  Author: James Couzens <jcouzens@codeshare.ca>
    00304 *
    00305 *  Date: 12/25/03
    00306 *
    00307 *  Desc:
    00308 *       Free memory associated with passed peer_info_t structure.
    00309 *  p _MUST_ be nulled, checks in the various MTA code rely
    00310 *  on it to be NULL for various checks.
    00311 *
    00312 */
    00313 peer_info_t *SPF_close(peer_info_t *p)
    00314 {
    00315   if (p == NULL)
    00316   {
    00317     xepprintf("peer structure null.  Aborting!\n");
    00318
    00319     return(NULL);
    00320   }
    00321
    00322   xfree(p->mta_hname);
    00323   xfree(p->helo);
    00324   xfree(p->from);
    00325   xfree(p->current_domain);
    00326   xfree(p->r_ip);
    00327   xfree(p->ptr_mhost);
    00328   xfree(p->cur_eaddr);
    00329   xfree(p->trusted);
    00330   xfree(p->guess);
    00331   xfree(p->explain);
    00332   xfree(p->r_vhname);
    00333
    00334   xfree(p);
    00335   p = NULL;
    00336
    00337   return(p);
    00338 }
    00339
    00340
    00341 /* SPF_policy_main
    00342 *
    00343 *  Author: James Couzens <jcouzens@codeshare.ca>
    00344 *
    00345 *  Date:   12/10/03
    00346 *  Date:   01/24/04 by James <jcouzens@codeshare.ca>
    00347 *  Date:   05/02/04 by Teddy <teddy@teddy.ch>
    00348 *
    00349 *  Desc:
    00350 *          This calls only the recursive version SPF_policy_main.
    00351 *  In earlier versions, SPF_policy_main was not able to handle CNAME
    00352 *
    00353 */
    00354 /*
    00355 SPF_RESULT SPF_policy_main(peer_info_t *p)
    00356 {
    00357   return(SPF_policy_main(p, 0));
    00358 }
    00359 */
    00360
    00361
    00362 /* SPF_policy_main
    00363 *
    00364 *  Author: James Couzens <jcouzens@codeshare.ca>
    00365 *
    00366 *  Date:   09/08/04 by James <jcouzens@codeshare.ca>
    00367 *
    00368 *  Desc:
    00369 *          This is the primary SPF function from which effectively all underlying
    00370 *  operation of the library is manipulated.  Passed a 'peer_info_t' structure when
    00371 *  properly populated DNS queries of T_TXT and T_CNAME are made to obtain and
    00372 *  recusrively follow SPF records through DNS up to a limit hard set through
    00373 *  the macro 'SPF_MAX_RECURSE'.
    00374 *
    00375 *  As per the RFC 'include' and 'redirect' are cached until all other mechanisms
    00376 *  and modifiers of a particular SPF query are exhausted before being attempted
    00377 *  as they are the most expensive SPF operations.
    00378 *
    00379 *  Returns 'SPF_RESULT' which describes the overall outcome of however many
    00380 *  actual parses may have been involved.  Based on this outcome the calling
    00381 *  function is expected to further call SPF_build_header and SPF_get_explain,
    00382 *  and finally SPF_close to finalize their SPF query.
    00383 *
    00384 *  SPF return types are often represented by single characters however not all
    00385 *  SPF_RESULT return types may be references this way.  The characters are
    00386 *  as follows:
    00387 *
    00388 *  '+' - SPF_PASS      (pass)
    00389 *  '-' - SPF_HARD_FAIL (fail)
    00390 *  '~' - SPF_SOFT_FAIL (softfail)
    00391 *  '?' - SPF_NEUTRAL   (neutral)
    00392 *
    00393 *  These chars and default error messages related to them are all managed
    00394 *  through the structure spf_result_t so if you wish to modify them
    00395 *  you will need to seek out the various macro's that are referenced
    00396 *  through this structure (which is declared within the SPF_init function).
    00397 *
    00398 */
    00399 SPF_RESULT SPF_policy_main(peer_info_t *p)
    00400 {
    00401   SPF_RESULT res;               /* result of an SPF parse */
    00402
    00403   char *tmp          = NULL;    /* temporary pointer */
    00404   char *cname_buf    = NULL;    /* tmp cname placeholder */
    00405   char *include_buf  = NULL;    /* tmp include placeholder */
    00406   char *redirect_buf = NULL;    /* tmp redirect placeholder */
    00407
    00408
    00409   if (p == NULL)
    00410   {
    00411     xepprintf("Unable to continue with a NULL peer_info_t structure!\n");
    00412
    00413     return(SPF_UNKNOWN);
    00414   }
    00415
    00416   if (p->spf_rlevel >= SPF_MAX_RECURSE)
    00417   {
    00418     xvprintf("recursion level exceeded (%u) levels; Aborting.\n",
    00419       p->spf_rlevel);
    00420
    00421     return(SPF_UNKNOWN);
    00422   }
    00423
    00424   xvprintf("[%i] current domain: (%s)\n", p->spf_rlevel, p->current_domain);
    00425   xvprintf("[%i] include buffer: (%s)\n", p->spf_rlevel, p->include_buf);
    00426   xvprintf("[%i] redirect buffer: (%s)\n", p->spf_rlevel, p->redirect_buf);
    00427
    00428   /* increment recursion count */
    00429   p->spf_rlevel++;
    00430
    00431    /*
    00432    * ensure that our buffer placeholders are clean within our
    00433    * peer_info_t structure otherwise you will get exceptionally
    00434    * negative and unpredictable results from the rest of this
    00435    * function!
    00436   */
    00437   _SPF_clear_holdbufs(p);
    00438
    00439   /* fire off a peer_info_t structure for an SPF policy parse */
    00440   res = SPF_policy_main_rec(p);
    00441
    00442   xvprintf("result of SPF parse is %i\n", res);
    00443
    00444   /* store these here for now, we'll take them back in a moment */
    00445   cname_buf    = p->cname_buf;
    00446   include_buf  = p->include_buf;
    00447   redirect_buf = p->redirect_buf;
    00448
    00449   /* play it safe */
    00450   p->cname_buf     = NULL;
    00451   p->include_buf   = NULL;
    00452   p->redirect_buf  = NULL;
    00453
    00454   if (res != SPF_PASS)
    00455   {
    00456     /* store this here for now, we'll take it back in a moment */
    00457     tmp = p->current_domain;
    00458
    00459     xvprintf("tmp is holding the current domain: (%s)\n", tmp);
    00460
    00461      /*
    00462      * First the CNAME is looked at, and separate from the 'include'
    00463      * and 'redirect' buffer placeholders because its not possible
    00464      * to have either 'include' or 'redirect' if you have a CNAME!
    00465     */
    00466     if (cname_buf != NULL)
    00467     {
    00468       p->current_domain = cname_buf;
    00469
    00470       xvprintf("Current domain: (%s); CNAME Buffer: (%s)\n",
    00471         p->current_domain, cname_buf);
    00472
    00473       res = SPF_policy_main(p);
    00474     }
    00475     else
    00476     {
    00477        /*
    00478        * In the event that during our original parse we happend
    00479        * upon either the 'include' or 'redirect' markers, we would
    00480        * have saved their contents in the placeholder buffer within
    00481        * the peer_info_t structure until after the entire other
    00482        * items within the SPF language have been exhausted.  Once
    00483        * this has taken place we perform a brand new parse with the
    00484        * contents of either of the two buffers shouldl they have
    00485        * any data.
    00486       */
    00487       if (include_buf != NULL)
    00488       {
    00489         p->current_domain = include_buf;
    00490
    00491         xvprintf("current domain is now (%s) from INCLUDE\n",
    00492           p->current_domain);
    00493
    00494         res = SPF_policy_main(p);
    00495
    00496         xvprintf("result of SPF parse was %i\n", res);
    00497
    00498          /*
    00499          * this is quick hack and should go somewhere else but I
    00500          * don't have time to do that right now.  If an include
    00501          * results in SPF_NONE then the RFC states we must return
    00502          * 'unknown'
    00503         */
    00504         if (res == SPF_NONE)
    00505         {
    00506           xpprintf("INCLUDE resulted in SPF_NONE, returning UNKNOWN\n");
    00507           res = SPF_UNKNOWN;
    00508           UTIL_assoc_prefix(p, SPF_UNKNOWN, NULL);
    00509         }
    00510       }
    00511
    00512        /*
    00513        * The 'include_buf' induced SPF parse may have resulted in
    00514        * an SPF_PASS result.  In the event that it didn't we then
    00515        * examine the 'redirect_buf' and send it off if necessary
    00516        * trying again with a brand new SPF query (effectively).
    00517       */
    00518       if ((res != SPF_PASS) && (redirect_buf != NULL))
    00519       {
    00520         xvprintf("found a redirect buffer with valid data (%s)\n",
    00521           redirect_buf);
    00522
    00523         p->current_domain = redirect_buf;
    00524
    00525         xvprintf("current domain is now: (%s) from REDIRECT\n",
    00526           p->current_domain);
    00527
    00528         res = SPF_policy_main(p);
    00529
    00530         xvprintf("result of SPF parse was %i\n", res);
    00531       }
    00532     }
    00533
    00534     /* reset our original domain */
    00535     p->current_domain = tmp;
    00536
    00537     xvprintf("current domain is now: (%s); received back from tmp\n",
    00538       p->current_domain);
    00539   }
    00540
    00541   /* place the old values back into our peer_info_t structure */
    00542   p->cname_buf     = cname_buf;
    00543   p->include_buf   = include_buf;
    00544   p->redirect_buf  = redirect_buf;
    00545
    00546   _SPF_clear_holdbufs(p);
    00547
    00548   xvprintf("leaving function; returning with value: (%i)\n", res);
    00549
    00550   return(res);
    00551 }
    00552
    00553
    00554
    00555 /* SPF_policy_main
    00556 *
    00557 *  Author: James Couzens <jcouzens@codeshare.ca>
    00558 *
    00559 *  Date:    12/10/03
    00560 *  Date:    01/24/04 by James <jcouzens@codeshare.ca>
    00561 *  Date:    05/02/04 by Teddy <teddy@teddy.ch>
    00562 *
    00563 *  Desc:
    00564 *          Fetches the SERVER_POLICY records from the FQDN's NS server
    00565 *  and returns an SPF_RESULT enumeration indicating the level of
    00566 *  compliance with an SPF policy (match, fail, error etc.. see spf.h)
    00567 *  Recursion inserted by Teddy
    00568 *
    00569 */
    00570 SPF_RESULT SPF_policy_main_rec(peer_info_t *p)
    00571 {
    00572   SPF_RESULT res = SPF_NEUTRAL;   /* SPF parse result */
    00573   char *rr_data  = NULL;          /* record */
    00574
    00575
    00576   if (p == NULL)
    00577   {
    00578     xepprintf("Error peer structure is NULL.  Aborting\n");
    00579
    00580     UTIL_assoc_prefix(p, SPF_ERROR, NULL);
    00581
    00582 #ifdef _SPF_LOGFILE_STATS
    00583     UTIL_log_result(p);
    00584 #endif /* _SPF_LOGFILE_STATS */
    00585
    00586
    00587     xepprintf("leaving function; returning SPF_ERROR\n");
    00588
    00589     return(SPF_ERROR);
    00590   }
    00591   else if (p->addr.s_addr <= 0)
    00592   {
    00593     xepprintf("Error no idea who the remote peer is.  Abort!\n");
    00594
    00595     UTIL_assoc_prefix(p, SPF_ERROR, NULL);
    00596
    00597 #ifdef _SPF_LOGFILE_STATS
    00598     UTIL_log_result(p);
    00599 #endif /* _SPF_LOGFILE_STATS */
    00600
    00601     xepprintf("leaving function; returning SPF_ERROR\n");
    00602
    00603     return(SPF_ERROR);
    00604   }
    00605
    00606    /*
    00607    * As is stated in SPF_init, localhost is always exempt from SPF
    00608    * checks.  A check for this was done in SPF_init and to keep
    00609    * actions being brought about by the proper functions p->RES
    00610    * would have been intentionally set to SPF_PASS and we check
    00611    * for that here.
    00612   */
    00613   if (p->RES == SPF_PASS)
    00614   {
    00615 #ifdef _SPF_LOGFILE_STATS
    00616     UTIL_log_result(p);
    00617 #endif /* _SPF_LOGFILE_STATS */
    00618
    00619     xpprintf("localhost exempt from SPF checks; returning SPF_PASS\n");
    00620
    00621     return(SPF_PASS);
    00622   }
    00623
    00624   if ((rr_data = DNS_query(p, p->current_domain, T_TXT, NULL)) != NULL)
    00625   {
    00626     xprintf("DNS_query returned with answer: (%s)\n", rr_data);
    00627
    00628     SPF_parse_policy(p, rr_data);
    00629
    00630     xfree(rr_data);
    00631   }
    00632   else if ((rr_data = DNS_query(p, p->current_domain, T_CNAME, NULL)) != NULL)
    00633   {
    00634     xvprintf("domain (%s) is CNAME of (%s). Restarting SPF_policy_main.",
    00635       p->current_domain, rr_data);
    00636
    00637     /* store the result of the CNAME lookup in the CNAME buf */
    00638     p->cname_buf = rr_data;
    00639   } /* if rr_data */
    00640
    00641    /*
    00642    * If either of the three placeholder buffers have anything in
    00643    * them check the peer_info_t structure for an SPF parse result
    00644    * and return it.
    00645   */
    00646   if (p->cname_buf || p->include_buf || p->redirect_buf)
    00647   {
    00648     if (p->RES != SPF_PASS)
    00649     {
    00650       xprintf("leaving function; returning (%i)\n", p->RES);
    00651
    00652       return(p->RES);
    00653     }
    00654   }
    00655
    00656    /*
    00657    *  this is intentionally outside of the above statements so it can be
    00658    *  called if the user has decided to implement best guess but has
    00659    *  disabled trusted forwarder.
    00660   */
    00661   if ((p->RES != SPF_PASS) && (p->use_trust == SPF_TRUE))
    00662   {
    00663     xvprintf("Error obtaining record, trying Trusted Fwdr (%s)\n",
    00664       p->trusted);
    00665
    00666     res = SPF_parse_policy(p, p->trusted);
    00667
    00668     xvprintf("SPF_parse_policy returned %i\n", res);
    00669   }
    00670
    00671   if ((p->RES != SPF_PASS) && (p->include_buf || p->redirect_buf))
    00672   {
    00673     return(p->RES);
    00674   }
    00675
    00676   if ((p->RES != SPF_PASS) && (p->use_guess == SPF_TRUE))
    00677   {
    00678     xvprintf("Attempting a best guess effort as a last resort "
    00679       "using: (%s)\n", p->guess);
    00680
    00681     res = SPF_parse_policy(p, p->guess);
    00682
    00683     xvprintf("SPF_parse_policy returned %i\n", res);
    00684   }
    00685
    00686   if ((p->RES != SPF_PASS) && (p->include_buf || p->redirect_buf))
    00687   {
    00688     return(p->RES);
    00689   }
    00690
    00691   xprintf("Return policy %i on mech: (%s) with outcome: (%s)\n",
    00692     p->RES, p->last_m, p->rs);
    00693
    00694   /*
    00695    * If logging is enabled this will write to a statistics log
    00696    * at (by default) /var/log/spflog.txt where you can over time
    00697    * get a good idea of how SPF performs on your network.
    00698    *
    00699   */
    00700 #ifdef _SPF_LOGFILE_STATS
    00701     UTIL_log_result(p);
    00702 #endif /* _SPF_LOGFILE_STATS */
    00703
    00704   return(p->RES);
    00705 }
    00706
    00707
    00708 /*  SPF_parse_policy
    00709 *
    00710 *  Author: James Couzens <jcouzens@codeshare.ca>
    00711 *
    00712 *  Date:   12/19/03
    00713 *
    00714 *  Desc:
    00715 *          Parses the passed 'policy' and interprets the policies
    00716 *  contained within it to decide if based on various comparisons against
    00717 *  r_ip (remote peer's ipaddress) whether or not it meets the SPF
    00718 *  policy.
    00719 *
    00720 */
    00721 SPF_BOOL SPF_parse_policy(peer_info_t *p, const char *policy)
    00722 {
    00723   SPF_MECHANISM POLICY_TYPE;    /* SPF policy mechanism type */
    00724
    00725   SPF_RESULT PREFIX_TYPE;       /* SPF mechanism prefix type */
    00726
    00727   SPF_BOOL POLICY_MATCH;        /* T / F on a policy match */
    00728   SPF_BOOL POLICY_ERROR;        /* T / F for errors to return early */
    00729
    00730   int16_t pos;                  /* position in the policy */
    00731   int16_t s_pos;                /* position in a policy token */
    00732
    00733   size_t p_len;                 /* length of passed policy string */
    00734
    00735   char *copy;                   /* storage container for passed policy */
    00736   char *cp;                     /* working pointer to passed policy */
    00737
    00738   char *token;                  /* token pointer to piece of policy */
    00739   char *token_ptr;              /* working pointer for token */
    00740
    00741   char *tmp_ptr;                /* temporary utility pointer */
    00742   char *tmp_ptr2;               /* 2nd temporary utility pointer */
    00743   char *macro;                  /* expanded macro storage container */
    00744
    00745   /* include */
    00746   peer_info_t *i_p;             /* copy of p with replaced domain */
    00747
    00748   policy_addr_t *policy_ip;     /* ip address of a policy record */
    00749
    00750
    00751   POLICY_MATCH    = SPF_FALSE;
    00752   POLICY_ERROR    = SPF_FALSE;
    00753   PREFIX_TYPE     = SPF_NEUTRAL;
    00754
    00755   policy_ip       = NULL;
    00756   i_p             = NULL;
    00757   pos             = 0;
    00758   token           = NULL;
    00759   token_ptr       = NULL;
    00760   macro           = NULL;
    00761
    00762   if ((policy == NULL) || (p == NULL))
    00763   {
    00764     xepprintf("Unable to continue called with NULL structure\n");
    00765
    00766     return(SPF_FALSE);
    00767   }
    00768
    00769   if (p->spf_rlevel >= SPF_MAX_RECURSE)
    00770   {
    00771     xvprintf("recursion breach (%i levels); Terminated.\n",
    00772       p->spf_rlevel);
    00773
    00774     snprintf(p->error, SPF_MAX_ERROR, "Recursion loop, terminated.");
    00775     UTIL_assoc_prefix(p, SPF_UNKNOWN, NULL);
    00776
    00777     return(SPF_FALSE);
    00778   }
    00779
    00780   p_len = strlen(policy);
    00781
    00782   xprintf("about to parse (%s) of len: %i (%s)\n",
    00783     policy, p_len, p->spf_result[p->RES].s);
    00784
    00785   /* allocate memory and assign working pointer for policy */
    00786   cp = copy = xstrndup(policy, (p_len + 1));
    00787
    00788   /* loops through the entire policy string until its exhausted */
    00789   while (*cp)
    00790   {
    00791     /* skip white space */
    00792     while ((*cp == ' ') && (*(cp + 1) == ' '))
    00793     {
    00794       cp++;
    00795     }
    00796     pos = UTIL_index(cp, ' ');
    00797
    00798     if (!*(cp + 1))
    00799     {
    00800       xfree(copy);
    00801       xpprintf("leaving function; nothing more to parse, returning SPF_TRUE\n");
    00802
    00803       return(SPF_TRUE);
    00804     }
    00805
    00806     token     = xstrndup(cp, (pos + 1));
    00807     token_ptr = token;
    00808     s_pos     = 0;
    00809
    00810     /* look for a mechanism modifier prefix */
    00811     if (UTIL_is_spf_result(*token_ptr) ||
    00812       (strncmp(token_ptr, "default", 7) == 0))
    00813     {
    00814       PREFIX_TYPE = UTIL_get_mech_prefix(p, token_ptr);
    00815       snprintf(p->last_m, SPF_MAX_MECHANISM, "%s", token_ptr);
    00816
    00817       if  (*token_ptr != 'd')
    00818       {
    00819         /* move ahead one because a prefix was specified */
    00820         token_ptr++;
    00821       }
    00822     }
    00823     else
    00824     {
    00825       snprintf(p->last_m, SPF_MAX_MECHANISM, "%s", token_ptr);
    00826       PREFIX_TYPE = SPF_PASS;
    00827     }
    00828
    00829     /* figure out what sort of mechanism we're working with */
    00830     POLICY_TYPE = UTIL_get_policy_mech(token_ptr);
    00831
    00832     xprintf("SPF Policy Mechanism: %i (token: %s) (pos: %i)\n",
    00833       POLICY_TYPE, token_ptr, pos);
    00834
    00835     switch (POLICY_TYPE)
    00836     {
    00837
    00838        /*
    00839        *  no policy set
    00840       */
    00841       case NO_POLICY:
    00842         break;
    00843
    00844        /*
    00845        *  unrecognized mechanism
    00846       */
    00847       case UNMECH:
    00848         UTIL_assoc_prefix(p, SPF_UNMECH, NULL);
    00849
    00850         xvprintf("Unrecognized mechanism (%s); returning SPF_FALSE\n", token);
    00851
    00852         xfree(token);
    00853         xfree(copy);
    00854
    00855         return(SPF_FALSE);
    00856
    00857        /*
    00858        *  'version' mechanism
    00859       */
    00860       case VERSION:
    00861         xprintf("policy mechanism is version (%s)\n", token_ptr);
    00862
    00863         if ((s_pos = UTIL_index(token_ptr, '=')) > 0)
    00864         {
    00865           token_ptr += s_pos + 4; /* skip =spf */
    00866
    00867           if (atoi(token_ptr) > SPF_VERSION)
    00868           {
    00869             UTIL_assoc_prefix(p, SPF_NONE, NULL);
    00870
    00871             xfree(token);
    00872             xfree(copy);
    00873
    00874             return(SPF_FALSE);
    00875           }
    00876
    00877           p->spf_ver = atoi(token_ptr);
    00878
    00879           xvprintf("SPF Version defined as: %u\n", p->spf_ver);
    00880         }
    00881         else
    00882         {
    00883            xvprintf("SPF version redefined! (%u)\n", p->spf_ver);
    00884            UTIL_assoc_prefix(p, SPF_S_FAIL, NULL);
    00885
    00886            xfree(token);
    00887            xfree(copy);
    00888
    00889            return(SPF_FALSE);
    00890         }
    00891
    00892         break;
    00893
    00894        /*
    00895        *  'all' mechanism
    00896       */
    00897       case ALL:
    00898         xprintf("policy mechanism is all (%s) policy: (%i)\n",
    00899           token_ptr, POLICY_TYPE);
    00900
    00901         UTIL_assoc_prefix(p, PREFIX_TYPE, NULL);
    00902         POLICY_MATCH = SPF_TRUE;
    00903         p->ALL       = SPF_TRUE;
    00904
    00905         break;
    00906
    00907        /*
    00908        *  'default' policy
    00909       */
    00910       case DEFAULT:
    00911         xprintf("policy mechanism is default (%s) policy: (%i)\n",
    00912           token_ptr, POLICY_TYPE);
    00913
    00914         POLICY_MATCH = SPF_TRUE;
    00915         p->ALL       = SPF_TRUE;
    00916
    00917         break;
    00918
    00919        /*
    00920        *  'include' mechanism
    00921       */
    00922       case INCLUDE:
    00923         xprintf("policy mechanism is include (%s)\n", token_ptr);
    00924
    00925         if ((tmp_ptr = strstr(token_ptr, ":")) != NULL)
    00926         {
    00927           tmp_ptr++;
    00928
    00929           if (UTIL_is_macro(tmp_ptr) == SPF_TRUE)
    00930           {
    00931             xvprintf("this INCLUDE mechanism contained macros (%s)\n", tmp_ptr);
    00932
    00933             if (p->include_buf == NULL)
    00934             {
    00935               if ((macro = MACRO_expand(p, tmp_ptr)) != NULL)
    00936               {
    00937                 p->include_buf = xstrndup(macro, SPF_MAX_STR);
    00938
    00939                 xfree(macro);
    00940               }
    00941             }
    00942             else
    00943             {
    00944               xvprintf("Got include=%s, but include already set. Ignoring.\n",
    00945                 tmp_ptr);
    00946             }
    00947           }
    00948           else
    00949           {
    00950             xvprintf("storing INCLUDE (%s) for later...\n", tmp_ptr);
    00951
    00952             if (p->include_buf == NULL)
    00953             {
    00954               p->include_buf = xstrndup(tmp_ptr, SPF_MAX_STR);
    00955             }
    00956             else
    00957             {
    00958               xvprintf("Got include=%s, but include already set. Ignoring.\n",
    00959                 tmp_ptr);
    00960             }
    00961           }
    00962         }
    00963         else
    00964         {
    00965           /* 'include' with no options returns unknown */
    00966           POLICY_MATCH = SPF_TRUE;
    00967           UTIL_assoc_prefix(p, SPF_UNKNOWN, token_ptr);
    00968
    00969           xfree(token);
    00970           xfree(copy);
    00971
    00972           return(SPF_TRUE);
    00973         }
    00974         break;
    00975
    00976        /*
    00977        *  'a' mechanism
    00978       */
    00979       case A:
    00980         xprintf("policy mechanism is A (%s)\n", token_ptr);
    00981
    00982         if ((tmp_ptr = strstr(token_ptr, "/")) != NULL)
    00983         {
    00984           tmp_ptr++;
    00985           POLICY_MATCH = UTIL_a_cmp(p, token_ptr, atoi(tmp_ptr));
    00986         }
    00987         else
    00988         {
    00989           POLICY_MATCH = UTIL_a_cmp(p, token_ptr, 32);
    00990         }
    00991
    00992         /* mechanism was supplied with a - indicating a failure on */
    00993         if ((PREFIX_TYPE == SPF_H_FAIL) && (POLICY_MATCH == SPF_TRUE))
    00994         {
    00995           xpprintf("Found a match on a negative prefix.  Halting parse.\n");
    00996           UTIL_assoc_prefix(p, SPF_H_FAIL, token_ptr);
    00997
    00998           xfree(token);
    00999           xfree(copy);
    01000
    01001           return(SPF_FALSE);
    01002         }
    01003
    01004         break;
    01005
    01006        /*
    01007        *  'mx' mechanism
    01008       */
    01009       case MX:
    01010         xprintf("policy mechanism is mx (%s)\n", token_ptr);
    01011
    01012         /* we've been supplied an MX to use instead.. grab it */
    01013         /* use tmp_ptr2 so we can use tmp_ptr again */
    01014         if ((tmp_ptr = strstr(token_ptr, ":")) != NULL)
    01015         {
    01016           tmp_ptr++;
    01017           tmp_ptr2 = tmp_ptr;
    01018         }
    01019         else
    01020         {
    01021           tmp_ptr2 = p->current_domain;
    01022         }
    01023
    01024         if ((tmp_ptr = strstr(token_ptr, "/")) != NULL)
    01025         {
    01026           tmp_ptr++;
    01027           POLICY_MATCH = UTIL_mx_cmp(p, tmp_ptr2, atoi(tmp_ptr));
    01028         }
    01029         else
    01030         {
    01031           POLICY_MATCH = UTIL_mx_cmp(p, tmp_ptr2, 32);
    01032         }
    01033
    01034         /* mechanism was supplied with a - indicating a failure on */
    01035         if ((PREFIX_TYPE == SPF_H_FAIL) && (POLICY_MATCH == SPF_TRUE))
    01036         {
    01037           xpprintf("Found a match on a negative prefix.  Halting parse.\n");
    01038
    01039           UTIL_assoc_prefix(p, SPF_H_FAIL, token_ptr);
    01040
    01041           xfree(token);
    01042           xfree(copy);
    01043
    01044           return(SPF_FALSE);
    01045         }
    01046
    01047         break;
    01048
    01049        /*
    01050        *  'ptr' mechanism
    01051       */
    01052       case PTR:
    01053         xprintf("policy mechanism is ptr (%s)\n", token_ptr);
    01054
    01055         POLICY_MATCH = UTIL_ptr_cmp(p, token_ptr);
    01056
    01057         /* mechanism was supplied with a - indicating a failure on */
    01058         if ((PREFIX_TYPE == SPF_H_FAIL) && (POLICY_MATCH == SPF_TRUE))
    01059         {
    01060           xpprintf("Found a match on a negative prefix.  Halting parse.\n");
    01061
    01062           break;
    01063         }
    01064
    01065         break;
    01066
    01067
    01068        /*
    01069        *  'ip4' mechanism
    01070       */
    01071       case IP4:
    01072         xprintf("policy mechanism is ip4 (%s)\n", token_ptr);
    01073
    01074         if ((policy_ip = UTIL_expand_ip(token_ptr)) == NULL)
    01075         {
    01076           xpprintf("ERROR: inet_pton unable to convert ip to binary\n");
    01077
    01078           break;
    01079         }
    01080
    01081         xvprintf("POL: %lu (%s) PEER: %lu (%s)\n",
    01082           policy_ip->addr.s_addr, token_ptr, p->addr.s_addr, p->r_ip);
    01083
    01084         POLICY_MATCH = UTIL_cidr_cmp(policy_ip, &p->addr);
    01085         xfree(policy_ip);
    01086
    01087         /* mechanism was supplied with a - indicating a failure on */
    01088         if ((PREFIX_TYPE == SPF_H_FAIL) && (POLICY_MATCH == SPF_TRUE))
    01089         {
    01090           xpprintf("Found a match on a negative prefix.  Halting parse.\n");
    01091           UTIL_assoc_prefix(p, SPF_H_FAIL, token_ptr);
    01092
    01093           xfree(token);
    01094           xfree(copy);
    01095
    01096           return(SPF_FALSE);
    01097         }
    01098
    01099         break;
    01100
    01101        /*
    01102        *  'ip6' mechanism
    01103       */
    01104       case IP6:
    01105         xprintf("policy mechanism is ip6 (%s)\n", token_ptr);
    01106
    01107         break;
    01108
    01109        /*
    01110        *  'exists' mechanism
    01111       */
    01112       case EXISTS:
    01113         xprintf("policy mechanism is exists (%s)\n", token_ptr);
    01114
    01115         if ((tmp_ptr = strstr(token_ptr, ":")) != NULL)
    01116         {
    01117           tmp_ptr++;
    01118
    01119           if ((macro = MACRO_expand(p, tmp_ptr)) == NULL)
    01120           {
    01121             xvprintf("Unable to expand macro (%s). Aborting.\n", tmp_ptr);
    01122
    01123             break;
    01124           }
    01125
    01126           if (DNS_query(p, macro, T_A, NULL) == (char *)SPF_TRUE)
    01127           {
    01128             POLICY_MATCH = SPF_TRUE;
    01129           }
    01130
    01131           xfree(macro);
    01132         }
    01133         break;
    01134
    01135        /*
    01136        *  'redirect' modifier
    01137       */
    01138       case REDIRECT:
    01139         xprintf("modifier is redirect (%s)\n", token_ptr);
    01140
    01141         if ((tmp_ptr = strstr(token_ptr, "=")) != NULL)
    01142         {
    01143           tmp_ptr++;
    01144
    01145           if (UTIL_is_macro(tmp_ptr) == SPF_TRUE)
    01146           {
    01147             if (p->redirect_buf == NULL)
    01148             {
    01149               if ((macro = MACRO_expand(p, tmp_ptr)) != NULL)
    01150               {
    01151                 p->redirect_buf = xstrndup(macro, SPF_MAX_STR);
    01152                 xfree(macro);
    01153               }
    01154             }
    01155             else
    01156             {
    01157               xvprintf("Got redir=%s, but redirect already set. Ignoring.\n",
    01158                 tmp_ptr);
    01159             }
    01160           }
    01161           else
    01162           {
    01163             if (p->redirect_buf == NULL)
    01164             {
    01165               xvprintf("setting redirect_buf to %s\n", tmp_ptr);
    01166               p->redirect_buf = xstrndup(tmp_ptr, SPF_MAX_STR);
    01167             }
    01168             else
    01169             {
    01170               xvprintf("Got redir=%s, but redirect already set. Ignoring.\n",
    01171                 tmp_ptr);
    01172             }
    01173           }
    01174         }
    01175         break;
    01176
    01177        /*
    01178        *  'explain' mechanism
    01179       */
    01180       case EXPLAIN:
    01181         xprintf("policy mechanism is explain (%s)\n", token_ptr);
    01182
    01183         if ((tmp_ptr = strstr(token_ptr, "=")) != NULL)
    01184         {
    01185           tmp_ptr++;
    01186
    01187           if ((macro = MACRO_expand(p, tmp_ptr)) == NULL)
    01188           {
    01189             xvprintf("Unable to expand macro (%s). Aborting.\n", tmp_ptr);
    01190
    01191             break;
    01192           }
    01193
    01194           p->explain = xstrndup(macro, (strlen(macro) + 1));
    01195           xfree(macro);
    01196         }
    01197         else
    01198         {
    01199           xpprintf("EXPLAIN modifier must be accompanied " \
    01200             "by arguments and I found none.");
    01201
    01202           /* 'include' with no options returns unknown */
    01203           POLICY_MATCH = SPF_TRUE;
    01204           UTIL_assoc_prefix(p, SPF_UNKNOWN, token_ptr);
    01205
    01206           xfree(token);
    01207           xfree(copy);
    01208
    01209           return(SPF_TRUE);
    01210         }
    01211
    01212         break;
    01213     } /* switch */
    01214
    01215     xfree(token);
    01216     cp += pos + 1;  /* move over to the next mechanism */
    01217
    01218     if ((POLICY_MATCH == SPF_TRUE) && (p->spf_ver > 0))
    01219     {
    01220       /*p->RES = SPF_PASS;*/
    01221       UTIL_assoc_prefix(p, PREFIX_TYPE, p->last_m);
    01222       p->RES_P = p->RES;
    01223
    01224       xpprintf("returning SPF_TRUE\n");
    01225       xfree(copy);
    01226
    01227       return(SPF_TRUE);
    01228     }
    01229   } /* while parsing policy */
    01230
    01231   xfree(copy);
    01232
    01233   return(SPF_FALSE);
    01234 }
    01235
    01236
    01237 /* SPF_result
    01238 *
    01239 *  Author: James Couzens <jcouzens@codeshare.ca>
    01240 *
    01241 *  Date:   02/02/04
    01242 *
    01243 *  Desc:
    01244 *         Returns a string of allocated memory detailing a response
    01245 *  that the MTA will return to the client based on the result
    01246 *  received from an SPF query.
    01247 *
    01248 */
    01249 char *SPF_result(peer_info_t *p)
    01250 {
    01251   char  *buf;    /* return buffer */
    01252
    01253
    01254   buf = xmalloc(SPF_MAX_HEADER);
    01255
    01256   switch (p->RES)
    01257   {
    01258     case SPF_PASS:
    01259       snprintf(buf, SPF_MAX_SMTP_RES, RES_PASS, p->from, p->r_ip);
    01260       break;
    01261
    01262     case SPF_NONE:
    01263       snprintf(buf, SPF_MAX_SMTP_RES, RES_NONE, p->from);
    01264       break;
    01265
    01266     case SPF_S_FAIL:
    01267       snprintf(buf, SPF_MAX_SMTP_RES, RES_S_FAIL, p->from, p->r_ip);
    01268       break;
    01269
    01270     case SPF_H_FAIL:
    01271       snprintf(buf, SPF_MAX_SMTP_RES, RES_H_FAIL, p->from, p->r_ip);
    01272       break;
    01273
    01274     case SPF_ERROR:
    01275       snprintf(buf, SPF_MAX_SMTP_RES, RES_ERROR, p->from);
    01276       break;
    01277
    01278     case SPF_NEUTRAL:
    01279       snprintf(buf, SPF_MAX_SMTP_RES, RES_NEUTRAL, p->r_ip, p->from);
    01280       break;
    01281
    01282     case SPF_UNKNOWN:
    01283       snprintf(buf, SPF_MAX_SMTP_RES, RES_UNKNOWN, p->from);
    01284       break;
    01285
    01286     case SPF_UNMECH:
    01287       snprintf(buf, SPF_MAX_SMTP_RES, RES_UNMECH, p->from);
    01288       break;
    01289   }
    01290
    01291   xprintf("response: (%s)\n", buf);
    01292
    01293   return(buf);
    01294 }
    01295
    01296
    01297 /*  SPF_get_explain
    01298 *
    01299 *  Author: James Couzens <jcouzens@codeshare.ca>
    01300 *
    01301 *  Date:   01/25/04
    01302 *
    01303 *  Desc:
    01304 *         Takes the contents of a returned explain parse (if any),
    01305 *  allocates memory for it and returns that memory with the contents of
    01306 *  that string.  Calling function is required to free this memory.
    01307 *
    01308 *
    01309 */
    01310 char *SPF_get_explain(peer_info_t *p)
    01311 {
    01312   char *buf = NULL;    /* return buffer */
    01313
    01314
    01315   if (p->explain != NULL)
    01316   {
    01317     if ((buf = MACRO_expand(p, SPF_EXPLAIN)) != NULL)
    01318     {
    01319       xprintf("Prepending explain: (%s)\n", buf);
    01320
    01321       return(buf);
    01322     }
    01323   }
    01324
    01325   return(NULL);
    01326 }
    01327
    01328
    01329 /*  SPF_build_header
    01330 *
    01331 *  Author: James Couzens <jcouzens@codeshare.ca>
    01332 *
    01333 *  Date:   01/25/04
    01334 *
    01335 *  Desc:
    01336 *         Allocates memory and builds a string which contains a
    01337 *  Received-SPF header.  The calling function is responsible to free
    01338 *  this memory.
    01339 *
    01340 *
    01341 */
    01342 char *SPF_build_header(peer_info_t *p)
    01343 {
    01344   char  *buf;    /* return buffer */
    01345
    01346   buf = xmalloc(SPF_MAX_HEADER);
    01347
    01348   switch (p->RES)
    01349   {
    01350     /* */
    01351     case SPF_PASS:
    01352       snprintf(buf, SPF_MAX_HEADER, p->spf_result[SPF_PASS].h,
    01353         p->mta_hname, p->from, p->r_ip, p->mta_hname,
    01354         p->r_ip, p->from);
    01355       break;
    01356
    01357     /* */
    01358     case SPF_NONE:
    01359       snprintf(buf, SPF_MAX_HEADER, p->spf_result[SPF_NONE].h,
    01360         p->mta_hname, p->from);
    01361       break;
    01362
    01363     /* */
    01364     case SPF_S_FAIL:
    01365       snprintf(buf, SPF_MAX_HEADER, p->spf_result[SPF_S_FAIL].h,
    01366         p->mta_hname, p->from, p->r_ip, p->mta_hname,
    01367         p->r_ip, p->from);
    01368       break;
    01369
    01370     /* */
    01371     case SPF_H_FAIL:
    01372       snprintf(buf, SPF_MAX_HEADER, p->spf_result[SPF_H_FAIL].h,
    01373         p->mta_hname, p->from, p->r_ip, p->mta_hname,
    01374         p->r_ip, p->from);
    01375       break;
    01376
    01377     /* */
    01378     case SPF_ERROR:
    01379       snprintf(buf, SPF_MAX_HEADER, p->spf_result[SPF_ERROR].h,
    01380         p->mta_hname, p->from, p->error);
    01381       break;
    01382
    01383     /* */
    01384     case SPF_NEUTRAL:
    01385       snprintf(buf, SPF_MAX_HEADER, p->spf_result[SPF_NEUTRAL].h,
    01386         p->mta_hname, p->from, p->r_ip);
    01387       break;
    01388
    01389     /* */
    01390     case SPF_UNKNOWN:
    01391       snprintf(buf, SPF_MAX_HEADER, p->spf_result[SPF_UNKNOWN].h,
    01392         p->mta_hname, p->from, p->current_domain, p->last_m);
    01393       break;
    01394
    01395     /* */
    01396     case SPF_UNMECH:
    01397       snprintf(buf, SPF_MAX_HEADER, p->spf_result[SPF_UNMECH].h,
    01398         p->last_m, p->mta_hname, p->from);
    01399       break;
    01400   }
    01401
    01402   xvprintf("Prepending header string: (%s)\n", buf);
    01403
    01404   return(buf);
    01405 }
    01406
    01407
    01408 /* SPF_smtp_helo
    01409 *
    01410 *  Author: Sean Comeau <scomeau@obscurity.org>
    01411 *          James Couzens <jcouzens@codeshare.ca>
    01412 *          Patrick Earl (http://patearl.net/)
    01413 *
    01414 *  Date:   12/10/03
    01415 *          12/25/03 (modified / renamed)
    01416 *          02/04/04 (modified)
    01417 *
    01418 *  Desc:
    01419 *          Fetches the HELO from MTA and places into the global
    01420 *  p structure.
    01421 *
    01422 */
    01423 SPF_BOOL SPF_smtp_helo(peer_info_t *p, const char *s)
    01424 {
    01425
    01426   xprintf("called with (%s)\n", s);
    01427
    01428   if (p->helo)
    01429   {
    01430     xfree(p->helo);
    01431   }
    01432
    01433   p->helo = xstrdup(s);
    01434   p->ehlo = p->helo;
    01435
    01436   return (strlen(p->helo) > 0);
    01437 }
    01438
    01439
    01440 /* SPF_smtp_from
    01441 *
    01442 *  Author: James Couzens <jcouzens@codeshare.ca>
    01443 *          Patrick Earl (http://patearl.net/)
    01444 *
    01445 *  Date:   12/10/03
    01446 *  Date:   12/25/03 James Couzens <jcouzens@codeshare.ca> (rewrite)
    01447 *  Date:   02/04/04 Patrick Earl (http://patearl.net) (modified)
    01448 *  Date:   02/05/04 Teddy <teddy@teddy.ch>
    01449 *
    01450 *  Desc:
    01451 *          Fetches the FROM from MTA and places into the global
    01452 *  peer_info_t structure.
    01453 *
    01454 *
    01455 */
    01456 SPF_BOOL SPF_smtp_from(peer_info_t *p, const char *s)
    01457 {
    01458   int len;      /* utility */
    01459
    01460   char *pos;    /* position in string */
    01461
    01462
    01463   xfree(p->from);
    01464   xfree(p->current_domain);
    01465
    01466   if (s == NULL)
    01467   {
    01468     if (*(p->helo) == '\0')
    01469     {
    01470       p->from = xstrndup("unknown", 7);
    01471     }
    01472     else
    01473     {
    01474       p->from = xstrndup(p->helo, SPF_MAX_ENV_HELO);
    01475     }
    01476
    01477     xvprintf("NULL or invalid MAIL FROM rcvd.  Using %s from now on.\n",
    01478       p->from ? p->from : p->helo);
    01479
    01480     return(SPF_FALSE);
    01481   }
    01482
    01483   p->from = xstrndup(s, SPF_MAX_STR);
    01484
    01485   /* strips < and > from E-Mail-Address */
    01486   if (*p->from == 60)
    01487   {
    01488     pos = p->from;
    01489     (*(p->from + strlen(p->from) - 1)) = '\0';
    01490     p->from++;
    01491     p->from = xstrndup(p->from, SPF_MAX_STR);
    01492
    01493     xfree(pos);
    01494   }
    01495
    01496   if (*p->from == '\0')
    01497   {
    01498     if (*(p->helo) == '\0')
    01499     {
    01500       p->from = xstrndup("unknown", 7);
    01501     }
    01502     else
    01503     {
    01504       p->from = xstrndup(p->helo, SPF_MAX_ENV_HELO);
    01505     }
    01506   }
    01507
    01508   pos = p->from;
    01509
    01510   xprintf("MAIL-FROM: (%s) (called with: %s)\n", p->from, s);
    01511
    01512   if ((pos = strstr(pos, "@")) != NULL)
    01513   {
    01514     len = (pos - p->from);
    01515
    01516     if (len >= SPF_MAX_LOCAL_PART)
    01517     {
    01518       xvprintf("%i is >= %i causing data overrun\n", len, SPF_MAX_LOCAL_PART);
    01519     }
    01520
    01521     /* Copy everything up to the @ into local_part.  Do not include the @. */
    01522     memcpy(p->local_part, p->from, len);
    01523     p->local_part[len] = '\0';
    01524
    01525     pos++;    /* skip past the @ */
    01526     p->current_domain = xstrndup(pos, SPF_MAX_STR);
    01527
    01528     xprintf("Current domain: (%s)\n", p->current_domain);
    01529
    01530   }
    01531   else
    01532   {
    01533     snprintf(p->local_part, 11, "postmaster");
    01534     p->current_domain = xstrndup(p->from, SPF_MAX_STR);
    01535   }
    01536
    01537   xvprintf("local-part: (%s); domain: (%s); sender: (%s)\n",
    01538     p->local_part, p->current_domain, p->from);
    01539
    01540   return(SPF_TRUE);
    01541 }
    01542
    01543
    01544 /* _SPF_clear_holdbufs
    01545 *
    01546 *  Author: Travis Anderson <tanderson@codeshare.ca>
    01547 *  Author: James Couzens <jcouzens@codeshare.ca>
    01548 *
    01549 *  Date:   09/11/04
    01550 *
    01551 *  Desc:
    01552 *          There are three possible 'holding' buffers that are used
    01553 *  only after an original parse comes back negative (ie anything other
    01554 *  that SPF_PASS), and this function simply goes through and free's
    01555 *  any memory they might have associated and assigns them a NULL
    01556 *  pointer.
    01557 *
    01558 *  This function is *PRIVATE* and you will never need to call it,
    01559 *  nor can it be referenced outside of this file, its purely
    01560 *  written for the benefit for SPF_parse_policy when dealing with
    01561 *  recursion for INCLUDE, REDIRECT, and CNAME.
    01562 *
    01563 */
    01564 static SPF_BOOL _SPF_clear_holdbufs(peer_info_t *p)
    01565 {
    01566   if (p == NULL)
    01567   {
    01568     xepprintf("peer_info_t structure was NULL!  Aborting!\n");
    01569
    01570     return(SPF_FALSE);
    01571   }
    01572
    01573   if (p->cname_buf != NULL)
    01574   {
    01575     xfree(p->cname_buf);
    01576     p->cname_buf = NULL;
    01577   }
    01578
    01579   if (p->include_buf != NULL)
    01580   {
    01581     xfree(p->include_buf);
    01582     p->include_buf = NULL;
    01583   }
    01584
    01585   if (p->redirect_buf)
    01586   {
    01587     xfree(p->redirect_buf);
    01588     p->redirect_buf = NULL;
    01589   }
    01590
    01591   return(SPF_TRUE);
    01592 }
    01593
    01594 /* end spf.c */