[Raw Msg Headers][Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

SPF support diff



Matti,

I hope that you will eventually find some time for Zmailer :)
I am submitting a patch that adds support for SPF in smtpserver.
SPF (http://spf.pobox.com/, "Sender Policy Framework") is a draft RFC
that allows a domain owner to specify (via DNS) which servers are
expected to send mail on behalf of that domain, thus (somewhat)
preventing "mail from" forgery.

My diff adds an smtp policy attribute "spf +" telling that SPF check
must be performed for the class of sender IP addresses, and two PARAM
verbs telling if "Received-SPF:" header must be generated, and "level of
certainty" under which incoming submissions will be rejected.  I did not
implement SRS (http://spf.pobox.com/srs.html) support.

The thing relies on "libspf_alt" library by wayne, available here:
http://www.midwestcs.com/spf/libspf-alt/

Of course, the whole thing is optional, use "./configure --with-spf" to
compile in SPF support.

Please consider.

Thanks
Eugene

Index: config.h.in
===================================================================
RCS file: /cvsroot/zmailer/config.h.in,v
retrieving revision 1.48
diff -u -r1.48 config.h.in
--- config.h.in	4 Nov 2003 00:49:23 -0000	1.48
+++ config.h.in	4 Mar 2004 10:39:34 -0000
@@ -652,6 +652,9 @@
 /* Define to 1 if you have the <whoson.h> header file. */
 #undef HAVE_WHOSON_H
 
+/* Define to 1 if you have the <spf_alt/spf.h> header file. */
+#undef HAVE_SPF_ALT_SPF_H
+
 /* Have YP/NIS in the system, and want to use it */
 #undef HAVE_YP
 
@@ -669,6 +672,18 @@
 
 /* Define to 1 if you have the </usr/local/include/whoson.h> header file. */
 #undef HAVE__USR_LOCAL_INCLUDE_WHOSON_H
+
+/* Define to 1 if you have the </local/include/spf_alt/spf.h> header file. */
+#undef HAVE__LOCAL_INCLUDE_SPF_ALT_SPF_H
+
+/* Define to 1 if you have the </opt/include/spf_alt/spf.h> header file. */
+#undef HAVE__OPT_INCLUDE_SPF_ALT_SPF_H
+
+/* Define to 1 if you have the </opt/local/include/spf_alt/spf.h> header file. */
+#undef HAVE__OPT_LOCAL_INCLUDE_SPF_ALT_SPF_H
+
+/* Define to 1 if you have the </usr/local/include/spf_alt/spf.h> header file. */
+#undef HAVE__USR_LOCAL_INCLUDE_SPF_ALT_SPF_H
 
 /* Want to use IPv6, in case it is supported in the system */
 #undef INET6
Index: configure.in
===================================================================
RCS file: /cvsroot/zmailer/configure.in,v
retrieving revision 1.144
diff -u -r1.144 configure.in
--- configure.in	19 Nov 2003 23:18:31 -0000	1.144
+++ configure.in	4 Mar 2004 10:39:34 -0000
@@ -2008,7 +2008,7 @@
 AC_SUBST(LIBWHOSON)
 AC_SUBST(INCLWHOSON)
 use_whoson=0
-AC_ARG_WITH(whoson,   [  --with-whoson[=DIRPATH]    Where the WHOSON libs can be found],
+AC_ARG_WITH(whoson,   [  --with-whoson[[=DIRPATH]]  Where the WHOSON libs can be found],
         [if test "$withval" = yes; then
                 use_whoson=2
          else
@@ -2157,6 +2157,165 @@
     fi
     unset ac_cv_lib_whoson
     AC_DEFINE(HAVE_WHOSON_H)
+  fi
+fi
+
+CPPFLAGS="$t_oldCpp"
+LIBS="$t_oldLibs"
+
+AC_SUBST(LIBSPF)
+AC_SUBST(INCLSPF)
+use_spf=0
+AC_ARG_WITH(spf,   [  --with-spf[[=DIRPATH]]     Where the SPF libs can be found.  Needs spf_alt
+                           from http://www.midwestcs.com/spf/libspf-alt/],
+        [if test "$withval" = yes; then
+                use_spf=2
+         else
+                use_spf=1
+         fi])
+
+t_oldLibs="$LIBS"
+t_oldCpp="$CPPFLAGS"
+
+if test "$use_spf" != 0 ; then
+  if test "$use_spf" = 2 ; then
+
+        AC_CHECK_HEADERS(spf_alt/spf.h)
+        if test "$ac_cv_header_spf_alt_spf_h" = yes; then
+            LIBSPF="-lspf_alt"
+            INCLSPF=""
+            spfloc="sysdefault"
+
+            LIBS="$LIBS $LIBSOCKET $LIBSPF"
+            CPPFLAGS="$CPPFLAGS $INCLSPF"
+            AC_TRY_LINK([#include <sys/types.h>
+			#include <spf_alt/spf.h>
+                        int major, minor;],
+			[SPF_get_lib_version(&major,&minor);],
+                        ac_cv_lib_spf=yes,ac_cv_lib_spf=no)
+dnl AC_MSG_RESULT([ ... link -lspf yields: $ac_cv_lib_spf])
+            if test "$ac_cv_lib_spf" = no; then
+                LIBS="$t_oldLibs"
+                LIBS="$LIBS $LIBSOCKET -L/usr/local/lib $LIBSPF"
+                AC_TRY_LINK([#include <sys/types.h>
+				#include <spf_alt/spf.h>
+				int major, minor;],
+				[SPF_get_lib_version(&major,&minor);],
+                        ac_cv_lib_spf=yes,ac_cv_lib_spf=no)
+dnl AC_MSG_RESULT([ ... link -L/usr/local/lib -lspf yields: $ac_cv_lib_spf])
+                if test  "$ac_cv_lib_spf" = yes; then
+                    spfloc="-L/usr/local/lib"
+                    LIBSPF="-L/usr/local/lib $LIBSPF"
+                fi
+            fi
+            if test "$ac_cv_lib_spf" = no; then
+                LIBS="$t_oldLibs"
+                LIBS="$LIBS $LIBSOCKET -L/local/lib $LIBSPF"
+                AC_TRY_LINK([#include <sys/types.h>
+				#include <spf_alt/spf.h>
+				int major, minor;],
+				[SPF_get_lib_version(&major,&minor);],
+                        ac_cv_lib_spf=yes,ac_cv_lib_spf=no)
+dnl AC_MSG_RESULT([ ... link -L/local/lib -lspf yields: $ac_cv_lib_spf])
+                if test  "$ac_cv_lib_spf" = yes; then
+                    spfloc="-L/local/lib"
+                    LIBSPF="-L/local/lib $LIBSPF"
+                fi
+            fi
+            if test "$ac_cv_lib_spf" = no; then
+                LIBS="$t_oldLibs"
+                LIBS="$LIBS $LIBSOCKET -L/opt/lib $LIBSPF"
+                AC_TRY_LINK([#include <sys/types.h>
+				#include <spf_alt/spf.h>
+				int major, minor;],
+				[SPF_get_lib_version(&major,&minor);],
+                        ac_cv_lib_spf=yes,ac_cv_lib_spf=no)
+dnl AC_MSG_RESULT([ ... link -L/opt/lib -lspf yields: $ac_cv_lib_spf])
+                if test  "$ac_cv_lib_spf" = yes; then
+                    spfloc="-L/opt/lib"
+                    LIBSPF="-L/opt/lib $LIBSPF"
+                fi
+            fi
+            if test "$ac_cv_lib_spf" = no; then
+                LIBS="$t_oldLibs"
+                LIBS="$LIBS $LIBSOCKET -L/opt/local/lib $LIBSPF"
+                AC_TRY_LINK([#include <sys/types.h>
+				#include <spf_alt/spf.h>
+				int major, minor;],
+				[SPF_get_lib_version(&major,&minor);],
+                        ac_cv_lib_spf=yes,ac_cv_lib_spf=no)
+                if test  "$ac_cv_lib_spf" = yes; then
+                    spfloc="-L/opt/local/lib"
+                    LIBSPF="-L/opt/local/lib $LIBSPF"
+                fi
+            fi
+            if test "$ac_cv_lib_spf" = no; then
+                unset ac_cv_lib_spf
+            fi
+
+            CPPFLAGS="$t_oldCpp"
+            LIBS="$t_oldLibs"
+
+        else
+            AC_CHECK_HEADERS(/local/include/spf_alt/spf.h /usr/local/include/spf_alt/spf.h \
+                             /opt/include/spf_alt/spf.h /opt/local/include/spf_alt/spf.h)
+            if test -n "$ac_cv_header__local_include_spf_alt_spf_h"; then
+                LIBSPF="-L/local/lib -lspf_alt"
+                INCLSPF="-I/local/include"
+                spfloc="-L/local/lib -I/local/include"
+            elif test -n "$ac_cv_header__usr_local_include_spf_alt_spf_h"; then
+                LIBSPF="-L/usr/local/lib -lspf_alt"
+                INCLSPF="-I/usr/local/include"
+                spfloc="-L/usr/local/lib -I/usr/local/include"
+            elif test -n "$ac_cv_header__opt_include_spf_alt_spf_h"; then
+                LIBSPF="-L/opt/lib -lspf_alt"
+                INCLSPF="-I/opt/include"
+                spfloc="-L/opt/lib -I/opt/include"
+            elif test -n "$ac_cv_header__opt_local_include_spf_alt_spf_h"; then
+                LIBSPF="-L/opt/local/lib -lspf_alt"
+                INCLSPF="-I/opt/local/include"
+                spfloc="-L/opt/local/lib -I/opt/local/include"
+            else
+                LIBSPF=""
+                INCLSPF=""
+                spfloc="nowhere?"
+            fi
+        fi
+        LIBS="$LIBS $LIBSOCKET $LIBSPF"
+        CPPFLAGS="$CPPFLAGS $INCLSPF"
+        AC_CACHE_VAL(ac_cv_lib_spf,
+          [AC_TRY_LINK([#include <sys/types.h>
+			#include <spf_alt/spf.h>
+			int major, minor;],
+			[SPF_get_lib_version(&major,&minor);],
+                        ac_cv_lib_spf=yes,ac_cv_lib_spf=no)
+          ])
+        if test "$ac_cv_lib_spf" = yes; then
+            AC_MSG_RESULT([SPF libs  available at $spfloc: -lspf_alt !]);
+            AC_DEFINE(HAVE_SPF_H)
+        else
+            AC_MSG_RESULT([No spf libs available at system default locations ?])
+            LIBSPF=""
+            INCLSPF=""
+        fi
+  #  fi
+  else
+    if test -f "$withval"/lib/libspf_alt.a ; then
+        LIBSPF="-L$withval/lib -lspf_alt"
+        AC_MSG_RESULT([Using spf libs at directory: $withval/lib]);
+    else
+        LIBSPF="-L$withval -lspf_alt"
+        AC_MSG_RESULT([Using spf libs at directory: $withval]);
+    fi
+    if test -f "$withval"/include/spf_alt/spf.h ; then
+        INCLSPF="-I$withval/include"
+        AC_MSG_RESULT([Using spf includes at directory: $withval]);
+    else
+        INCLSPF="-I$withval"
+        AC_MSG_RESULT([Using spf includes at directory: $withval]);
+    fi
+    unset ac_cv_lib_spf
+    AC_DEFINE(HAVE_SPF_H)
   fi
 fi
 
Index: include/policy.h
===================================================================
RCS file: /cvsroot/zmailer/include/policy.h,v
retrieving revision 1.13
diff -u -r1.13 policy.h
--- include/policy.h	18 Aug 2003 10:14:51 -0000	1.13
+++ include/policy.h	4 Mar 2004 10:39:34 -0000
@@ -90,9 +90,10 @@
 #define P_A_WarnRcptDnsRBL	24
 #define P_A_Filtering           25
 #define P_A_MaxSameIpSource	26
+#define P_A_CheckSPF	        27
 
 #define P_A_FirstAttr	        2
-#define P_A_LastAttr	        26
+#define P_A_LastAttr	        27
 /* Note: Attribute codes outside range 1..31 cause problems at policystate
          processing!  If you ever need modify these, fix the  policytest.c,
 	 and  policytest.h: struct policystate { char values[]; } array,
@@ -144,7 +145,8 @@
 	"test-rcpt-dns-rbl",
 	"warn-rcpt-dns-rbl",
 	"filtering",
-	"maxsameipsource"
+	"maxsameipsource",
+	"spf"
 };
 #define KA(x) ((((x)>0)&&((x)<=P_A_LastAttr))?_KA[(x) & 0xFF]:"??")
 
Index: man/smtpserver.8.in
===================================================================
RCS file: /cvsroot/zmailer/man/smtpserver.8.in,v
retrieving revision 1.63
diff -u -r1.63 smtpserver.8.in
--- man/smtpserver.8.in	21 Nov 2003 01:03:02 -0000	1.63
+++ man/smtpserver.8.in	4 Mar 2004 10:39:34 -0000
@@ -597,6 +597,25 @@
 .I "however ZMailer is no real LMTP server, and this feature is"
 .I "only for debug purposes."
 .RE
+.IP "PARAM spf\-received"
+.RS
+Create Received\-SPF header if SPF check is done and data is available.
+.br
+See http://spf.pobox.com/ for explanation of SPF.
+.RE
+.IP "PARAM spf\-threshold keyword"
+.RS
+Accept incoming messages with level equal or higher than specified threshold.
+Levels are sorted as follows:
+.nf
+      fail:      1
+      softfail:  2
+      none:      3
+      neutral:   4
+      pass:      5
+.fi
+See http://spf.pobox.com/ for explanation of SPF.
+.RE
 .IP "Here is a possible configuration file:"
 .RS 2em
 .nf
Index: proto/smtpserver.conf.in
===================================================================
RCS file: /cvsroot/zmailer/proto/smtpserver.conf.in,v
retrieving revision 1.49
diff -u -r1.49 smtpserver.conf.in
--- proto/smtpserver.conf.in	29 Aug 2003 16:51:35 -0000	1.49
+++ proto/smtpserver.conf.in	4 Mar 2004 10:39:34 -0000
@@ -20,6 +20,21 @@
 #                                       # MAIL FROM session. Minimum: 100
 #PARAM sum-sizeoption-value
 #
+# SPF support options (see http://spf.pobox.com/)
+# Zmailer needs to be compiled with libspf_alt
+# (see http://www.midwestcs.com/spf/libspf-alt/)
+#
+#PARAM spf-received			# create "Received-SPF:" header
+#PARAM spf-threshold        softfail	# worst acceptable SPF check result
+#					# 'fail' - always accept
+#					# 'softfail' - accept if 'softfail',
+#					#   'unpublished', 'neutral', 'pass'
+#					# 'none' - treat 'softfail' as 'fail'
+#					#   accept all better results
+#					# 'neutral' - reject unpublished
+#					# 'pass' - accept only 'pass' and
+#					#   DNS errors.
+#
 #PARAM BindPort                   25    # Binding port
 #PARAM BindAddress         [0.0.0.0]    # Binding address - for multihomers..
 #PARAM BindAddress       [IPv6.0::0]    # and here is for IPv6 - NO SPACES!
Index: proto/db/smtp-policy.src
===================================================================
RCS file: /cvsroot/zmailer/proto/db/smtp-policy.src,v
retrieving revision 1.24
diff -u -r1.24 smtp-policy.src
--- proto/db/smtp-policy.src	25 Aug 2003 06:03:38 -0000	1.24
+++ proto/db/smtp-policy.src	4 Mar 2004 10:39:34 -0000
@@ -93,7 +93,13 @@
 #|
 #| If your system has ``whoson'' server (see contrib/whoson-*.tgz),
 #| you can activate it by adding  'trust-whoson +' attribute pair to
-#| the wild-card IP address test:  [0.0.0.0]/0  of your choise.
+#| the wild-card IP address test:  [0.0.0.0]/0  of your choice.
+#|
+#|-----------
+#|
+#| If you compiled the package with SPF support (http://spf.pobox.com/),
+#| adding 'spf +' attribute pair will turn on SPF checks (and  creation
+#| of 'Received-SPF:' header if requested in smtpserver.conf).
 #|
 #|-----------
 #|
Index: smtpserver/Makefile.in
===================================================================
RCS file: /cvsroot/zmailer/smtpserver/Makefile.in,v
retrieving revision 1.35
diff -u -r1.35 Makefile.in
--- smtpserver/Makefile.in	18 Aug 2003 10:14:51 -0000	1.35
+++ smtpserver/Makefile.in	4 Mar 2004 10:39:34 -0000
@@ -16,8 +16,8 @@
 MAILBIN=	$(DESTDIR)@MAILBIN@
 DEFS=		@DEFS@
 INSTALL=	@INSTALL@
-SMTPSERVER_INCL= @INCLRESOLV@ @INCLWRAP@ @INCLWHOSON@ @OPENSSLINCL@ @GENINCL@ @SASL2INCL@
-SMTPSERVER_LIB=	 @GETPWLIB@ @LIBLOCALDBMS@ @LIBWRAP@ @LIBWHOSON@ @LIBRESOLV@ @OPENSSLLIB@ @AUTHLIB@ @LIBSOCKET@ @GENLIB@ @SASL2LIB@
+SMTPSERVER_INCL= @INCLRESOLV@ @INCLWRAP@ @INCLWHOSON@ @INCLSPF@ @OPENSSLINCL@ @GENINCL@ @SASL2INCL@
+SMTPSERVER_LIB=	 @GETPWLIB@ @LIBLOCALDBMS@ @LIBWRAP@ @LIBWHOSON@ @LIBSPF@ @LIBRESOLV@ @OPENSSLLIB@ @AUTHLIB@ @LIBSOCKET@ @GENLIB@ @SASL2LIB@
 #  old vestiges: @LIBLOADAVER@
 #
 #   Above the  LIBSOCKET (for SysVR4) must be last
Index: smtpserver/cfgread.c
===================================================================
RCS file: /cvsroot/zmailer/smtpserver/cfgread.c,v
retrieving revision 1.49
diff -u -r1.49 cfgread.c
--- smtpserver/cfgread.c	29 Aug 2003 16:51:35 -0000	1.49
+++ smtpserver/cfgread.c	4 Mar 2004 10:39:34 -0000
@@ -455,6 +455,32 @@
       }
     }
 
+    /* SPF related things */
+    /* Generate SPF-Received header */
+    else if (cistrcmp(name, "spf-received") == 0) {
+      use_spf=1;
+      spf_received=1;
+    }
+    /* Reject mail if SPF query result is equal or higher than threshold */
+    else if (cistrcmp(name, "spf-threshold") == 0 && param1 /* 1 param */) {
+      use_spf=1;
+      if (cistrcmp(param1, "fail")) {
+	spf_threshold=1;	/* relaxed - they say: fail but we accept */
+      } else if (cistrcmp(param1, "softfail")) {
+	spf_threshold=2;	/* default - they don't assume real reject */
+      } else if (cistrcmp(param1, "none")) {
+	spf_threshold=3;	/* stricter - but allow all who don't publish */
+      } else if (cistrcmp(param1, "neutral")) {
+	spf_threshold=4;	/* draconian - SFP-less won't pass */
+      } else if (cistrcmp(param1, "pass")) {
+	spf_threshold=5;	/* extreme - allow only explicit 'pass' */
+      } else {
+	type(NULL,0,NULL, "Cfgfile '%s' line %d param %s has bad arg: '%s'",
+		cfgfilename, linenum, name, param1);
+	spf_threshold=0;	/* always accept (even 'fail') */
+      }
+    }
+
     else {
       /* XX: report error for unrecognized PARAM keyword ?? */
       type(NULL,0,NULL, "Cfgfile '%s' line %d has bad PARAM keyword/missing parameters: '%s'", cfgfilename, linenum, name);
@@ -542,6 +568,11 @@
 	    bindaddrs[ bindaddrs_count-1 ] = bindaddr;
 	}
     }
+#ifndef HAVE_SPF_ALT_SPF_H
+    if (use_spf) {
+      type(NULL,0,NULL, "SPF parameters specified but SPF support not compiled in");
+    }
+#endif
     return head;
 }
 
Index: smtpserver/policytest.c
===================================================================
RCS file: /cvsroot/zmailer/smtpserver/policytest.c,v
retrieving revision 1.81
diff -u -r1.81 policytest.c
--- smtpserver/policytest.c	16 Nov 2003 18:01:20 -0000	1.81
+++ smtpserver/policytest.c	4 Mar 2004 10:39:35 -0000
@@ -55,9 +55,17 @@
 #include "libc.h"
 #include "libz.h"
 
+#ifdef HAVE_SPF_ALT_SPF_H
+#include <spf_alt/spf.h>
+#include <spf_alt/spf_dns_resolv.h>
+#endif
+
 #define _POLICYTEST_INTERNAL_
 #include "policytest.h"
 
+int use_spf;
+int spf_received;
+int spf_threshold;
 
 /* We are not including  "smtpserver.h",  thus have to do local prototype.. */
 #if defined(HAVE_STDARG_H) && defined(HAVE_VPRINTF)
@@ -777,6 +785,9 @@
 #ifdef HAVE_WHOSON_H
     state->whoson_result = whosonrc;
 #endif
+#ifdef HAVE_SPF_ALT_SPF_H
+    state->check_spf=0;
+#endif
     state->maxsameiplimit = -1;
     return 0;
 }
@@ -833,7 +844,8 @@
 			 1 << P_A_MaxSameIpSource    );
     if (!myaddress)
       state->request |= ( 1 << P_A_TestDnsRBL       |
-			  1 << P_A_RcptDnsRBL        );
+			  1 << P_A_RcptDnsRBL       |
+			  1 << P_A_CheckSPF          );
 
     state->maxinsize  = -1;
     state->maxoutsize = -1;
@@ -979,6 +991,36 @@
       }
     }
 
+    if (valueeq(state->values[P_A_CheckSPF], "+")) {
+#ifdef HAVE_SPF_ALT_SPF_H
+      if (debug)
+	type(NULL,0,NULL," policytestaddr: 'spf +' found");
+      state->check_spf=1;
+/* must be in the policystate destructor
+      SPF_destroy_default_config();
+*/
+      if (state->spfcid) SPF_destroy_config(state->spfcid);
+      if ((state->spfcid=SPF_create_config()) == NULL) {
+	type(NULL,0,NULL," SPF_create_config() failed");
+	state->check_spf=0;
+      }
+      if (state->spfdcid) SPF_dns_destroy_config_resolv(state->spfdcid);
+      if ((state->spfdcid=SPF_dns_create_config_resolv(NULL, 0)) == NULL) {
+	type(NULL,0,NULL," SPF_dns_create_config() failed");
+	state->check_spf=0;
+      }
+      /* SPF_free_c_results(&state->local_policy); */
+      SPF_init_c_results(&state->local_policy);
+      if (SPF_compile_local_policy(state->spfcid,NULL,0,&state->local_policy)) {
+	type(NULL,0,NULL," SPF_compile_local_policy() failed: %s",
+						state->local_policy.err_msg);
+	state->check_spf=0;
+      }
+#else
+      type(NULL,0,NULL," compiled without SPF support, 'spf +' ignored");
+#endif
+    }
+
     if (state->values[P_A_TestDnsRBL] &&
 	!valueeq(state->values[P_A_TestDnsRBL], "-")) {
       int rc;
@@ -1101,6 +1143,31 @@
     state->content_filter = -1;
 
     rc = _addrtest_(rel, state, pbuf, 1);
+
+#ifdef HAVE_SPF_ALT_SPF_H
+    if (state->check_spf) {
+      if (debug) {
+	char aaa[32];
+	inet_ntop(raddr->v4.sin_family,&raddr->v4.sin_addr,aaa,sizeof(aaa));
+	type(NULL,0,NULL,"doing SPF_set_ipv4(%s)",aaa);
+      }
+#if defined(AF_INET6) && defined(INET6)
+      if (raddr->v6.sin6_family == AF_INET6) {
+	if (SPF_set_ipv6(state->spfcid, raddr->v6.sin6_addr)) {
+	  type(NULL,0,NULL,"SPF_set_ipv6() failed");
+	  state->check_spf=0;
+	}
+      } else
+#endif
+      {
+	if (SPF_set_ipv4(state->spfcid, raddr->v4.sin_addr)) {
+	  type(NULL,0,NULL,"SPF_set_ipv4() failed");
+	  state->check_spf=0;
+	}
+      }
+    }
+#endif /* HAVE_SPF_ALT_SPF_H */
+
     if (debug) fflush(stdout);
     return rc;
 }
@@ -1320,6 +1387,15 @@
      *
      */
 
+#ifdef HAVE_SPF_ALT_SPF_H
+    if (state->check_spf) {
+      type(NULL,0,NULL,"doing SPF_set_helo_dom(\"%s\")",str);
+      if (SPF_set_helo_dom(state->spfcid, str)) {
+	  type(NULL,0,NULL,"SPF_set_helo_dom() failed");
+	  state->check_spf=0;
+      }
+    }
+#endif
 
     if (*str != '[') { /* Don't test address literals! */
 
@@ -1430,12 +1506,26 @@
 {
     const char *at;
     int requestmask = 0;
+    int rc;
 
     state->rcpt_nocheck  = 0;
     state->sender_reject = 0;
     state->sender_freeze = 0;
     state->sender_norelay = 0;
 
+#ifdef HAVE_SPF_ALT_SPF_H
+    if (state->check_spf) {
+      char *nstr=strdup(str);
+      nstr[len]='\0';
+      type(NULL,0,NULL,"doing SPF_set_env_from(\"%s\")",nstr);
+      if (SPF_set_env_from(state->spfcid, nstr)) {
+	  type(NULL,0,NULL,"SPF_set_env_from(\"%s\") failed",nstr);
+	  state->check_spf=0;
+      }
+      free(nstr);
+    }
+#endif
+
     if (state->always_reject)
 	return -1;
     if (state->always_freeze)
@@ -1543,7 +1633,52 @@
 	return  0;
     }
 #endif
-    return 0;
+    rc=0;
+#ifdef HAVE_SPF_ALT_SPF_H
+    if (state->check_spf) {
+      int spf_level;
+      SPF_output_t spf_output=SPF_result(state->spfcid,state->spfdcid,NULL);
+      if (debug) {
+	type(NULL,0,NULL," SPF_result=%d (%s) reason=%d  (%s) error=%d",
+					spf_output.result,
+					SPF_strresult(spf_output.result),
+					spf_output.reason,
+					SPF_strreason(spf_output.reason),
+					spf_output.err);
+	type(NULL,0,NULL,"%s",spf_output.smtp_comment?spf_output.smtp_comment:"<null>");
+      }
+      if (state->spf_received_hdr != NULL)
+	free(state->spf_received_hdr);
+      state->spf_received_hdr=strdup(spf_output.received_spf);
+      if (debug)
+	type(NULL,0,NULL,"%s",state->spf_received_hdr?
+					state->spf_received_hdr:"<null>");
+
+      switch (spf_output.result) {
+      case SPF_RESULT_PASS:	spf_level=5; break;
+      case SPF_RESULT_UNKNOWN:	spf_level=5; break;
+      case SPF_RESULT_ERROR:	spf_level=5; break;
+      case SPF_RESULT_NEUTRAL:	spf_level=4; break;
+      case SPF_RESULT_NONE:	spf_level=3; break;
+      case SPF_RESULT_SOFTFAIL:	spf_level=2; break;
+      case SPF_RESULT_FAIL:	spf_level=1; break;
+      default:			spf_level=5; break;
+      }
+      if (debug)
+	type(NULL,0,NULL,"rejecting if spf_level(%d) < spf_threshold(%d)",
+			spf_level,spf_threshold);
+      if (spf_level < spf_threshold) {
+	if (spf_output.smtp_comment) {
+	  state->message=strdup(spf_output.smtp_comment);
+	} else {
+	  PICK_PA_MSG(P_A_CheckSPF);
+	}
+	rc=-1;
+      }
+      SPF_free_output(&spf_output);
+    }
+#endif
+    return rc;
 }
 
 static int pt_rcptto(rel, state, str, len)
@@ -1863,6 +1998,16 @@
 {
     return state->message;
 }
+
+#ifdef HAVE_SPF_ALT_SPF_H
+char *
+policyspfhdr(rel, state)
+struct policytest *rel;
+struct policystate *state;
+{
+    return state->spf_received_hdr;
+}
+#endif
 
 long
 policyinsizelimit(rel, state)
Index: smtpserver/policytest.h
===================================================================
RCS file: /cvsroot/zmailer/smtpserver/policytest.h,v
retrieving revision 1.21
diff -u -r1.21 policytest.h
--- smtpserver/policytest.h	18 Aug 2003 10:14:51 -0000	1.21
+++ smtpserver/policytest.h	4 Mar 2004 10:39:35 -0000
@@ -49,6 +49,13 @@
 #ifdef HAVE_WHOSON_H
     int whoson_result;
 #endif
+#ifdef HAVE_SPF_ALT_SPF_H
+    int check_spf;
+    char *spf_received_hdr;
+    SPF_config_t spfcid;
+    SPF_dns_config_t spfdcid;
+    SPF_c_results_t local_policy;
+#endif
 };
 
 
@@ -118,6 +125,7 @@
 extern int policytest __((struct policytest * rel, struct policystate * ps, PolicyTest how, const char *str, const int len, const char *authuser));
 extern int policytestaddr __((struct policytest * rel, struct policystate * ps, PolicyTest how, Usockaddr * raddr));
 extern char *policymsg __((struct policytest *rel, struct policystate *ps));
+extern char *policyspfhdr __((struct policytest *rel, struct policystate *ps));
 extern long  policyinsizelimit __((struct policytest *rel, struct policystate *ps));
 extern long  policysameiplimit __((struct policytest *rel, struct policystate *ps));
 
Index: smtpserver/readpolicy.c
===================================================================
RCS file: /cvsroot/zmailer/smtpserver/readpolicy.c,v
retrieving revision 1.14
diff -u -r1.14 readpolicy.c
--- smtpserver/readpolicy.c	6 Nov 2003 17:35:08 -0000	1.14
+++ smtpserver/readpolicy.c	4 Mar 2004 10:39:35 -0000
@@ -181,6 +181,7 @@
 #ifdef HAVE_WHOSON_H
   { "trust-whoson",	P_A_TrustWhosOn		},
 #endif
+  { "spf",		P_A_CheckSPF		},
   { "filtering",	P_A_Filtering		},
   { "maxsameipsource",	P_A_MaxSameIpSource	},
   { NULL, 0 },
Index: smtpserver/smtpdata.c
===================================================================
RCS file: /cvsroot/zmailer/smtpserver/smtpdata.c,v
retrieving revision 1.56
diff -u -r1.56 smtpdata.c
--- smtpserver/smtpdata.c	21 Nov 2003 00:04:08 -0000	1.56
+++ smtpserver/smtpdata.c	4 Mar 2004 10:39:35 -0000
@@ -169,6 +169,12 @@
       fprintf(SS->mfp, "X-Comment: RFC 2476 MSA function at %s logged sender identity as: %s\n", SS->myhostname, SS->authuser);
     }
 
+#ifdef HAVE_SPF_ALT_SPF_H
+    if (spf_received && policyspfhdr(policydb, &SS->policystate)) {
+      fprintf(SS->mfp,"%s\n", policyspfhdr(policydb, &SS->policystate));
+    }
+#endif
+
     /* We set alarm()s inside the mvdata() */
     *msg = 0;
     filsiz = mvdata(SS, msg);
@@ -461,6 +467,11 @@
 	if (msa_mode && SS->authuser != NULL ) {
 	  fprintf(SS->mfp, "X-Comment: RFC 2476 MSA function at %s logged sender identity as: %s\n", SS->myhostname, SS->authuser);
 	}
+#ifdef HAVE_SPF_ALT_SPF_H
+	if (spf_received && policyspfhdr(policydb, &SS->policystate)) {
+	  fprintf(SS->mfp,"%s\n", policyspfhdr(policydb, &SS->policystate));
+	}
+#endif
     }
     /* We set alarm()s inside the mvbdata() */
     *msg = 0;
Index: smtpserver/smtpserver.h
===================================================================
RCS file: /cvsroot/zmailer/smtpserver/smtpserver.h,v
retrieving revision 1.86
diff -u -r1.86 smtpserver.h
--- smtpserver/smtpserver.h	7 Nov 2003 11:29:56 -0000	1.86
+++ smtpserver/smtpserver.h	4 Mar 2004 10:39:35 -0000
@@ -181,6 +181,9 @@
 #define _Z_FD_ISSET(i,var) ((var & (1 << i)) != 0)
 #endif
 
+#ifdef HAVE_SPF_ALT_SPF_H
+#include <spf_alt/spf.h>
+#endif
 
 #include "policytest.h"
 
@@ -433,6 +436,8 @@
 extern char *AuthMechanisms;
 extern int detect_incorrect_tls_use;
 extern int force_rcpt_notify_never;
+
+extern int use_spf, spf_received, spf_threshold;
 
 extern int bindaddr_set, bindport_set, testaddr_set;
 extern u_short   bindport;