From 9090c7b7f24239328540d58cbebeb7bf56f9f93a Mon Sep 17 00:00:00 2001 From: Julian Mehnle <> Date: Sat, 17 Jun 2006 16:31:08 +0000 Subject: [PATCH 01/87] postfix-policyd-spf-perl/trunk/postfix-policyd -> postfix-policyd-spf * Renamed file. * Version 1.01. --- postfix-policyd => postfix-policyd-spf | 180 +++---------------------- 1 file changed, 19 insertions(+), 161 deletions(-) rename postfix-policyd => postfix-policyd-spf (54%) diff --git a/postfix-policyd b/postfix-policyd-spf similarity index 54% rename from postfix-policyd rename to postfix-policyd-spf index d78163f..9ae657f 100755 --- a/postfix-policyd +++ b/postfix-policyd-spf @@ -2,32 +2,28 @@ # mengwong@pobox.com # Wed Dec 10 03:52:04 EST 2003 -# postfix-policyd -# version 1.0 +# postfix-policyd-spf +# version 1.01 # see http://spf.pobox.com/ -use DB_File; use Fcntl; use Sys::Syslog qw(:DEFAULT setlogsock); use strict; -use Mail::SPF::Query; # ---------------------------------------------------------- # configuration # ---------------------------------------------------------- -my @HANDLERS = ( - "testing", - "sender_permitted_from", - "greylisting", - ); +# to use SPF, install Mail::SPF::Query from CPAN or from the SPF website at http://spf.pobox.com/downloads.html +# then uncomment the SPF line. + + my @HANDLERS; + push @HANDLERS, "testing"; +# push @HANDLERS, "sender_permitted_from"; use Mail::SPF::Query; my $VERBOSE = 1; -my @Greylisting_Whitelisted_Senders = - ( - qr(^postmaster)i, - ); +my $DEFAULT_RESPONSE = "DUNNO"; # # Syslogging options for verbose mode and for fatal errors. @@ -40,18 +36,6 @@ my $syslog_facility = "mail"; my $syslog_options = "pid"; my $syslog_priority = "info"; -# -# greylist status database and greylist time interval. DO NOT create the -# greylist status database in a world-writable directory such as /tmp -# or /var/tmp. DO NOT create the greylist database in a file system -# that can run out of space. -# -# In case of database corruption, this script saves the database as -# $database_name.time(), so that the mail system does not get stuck. -# -my $database_name="/var/spool/postfix/smtpd-policy.db"; -my $GREYLIST_DELAY=60; - # ---------------------------------------------------------- # minimal documentation # ---------------------------------------------------------- @@ -59,13 +43,16 @@ my $GREYLIST_DELAY=60; # # Usage: smtpd-policy.pl [-v] # -# Demo delegated Postfix SMTPD policy server. This server -# implements greylisting and SPF. -# -# State for greylisting is kept in a Berkeley DB database. +# Demo delegated Postfix SMTPD policy server. +# This server implements SPF. +# Another server implements greylisting. +# Postfix has a pluggable policy server architecture. +# You can call one or both from Postfix. # # The SPF handler uses Mail::SPF::Query to do the heavy lifting. # +# This documentation assumes you have read Postfix's README_FILES/SMTPD_POLICY_README +# # Logging is sent to syslogd. # # How it works: each time a Postfix SMTP server process is started @@ -145,20 +132,6 @@ sub fatal_exit { # select((select(STDOUT), $| = 1)[0]); -# -# Signal 11 means that we have some kind of database corruption (yes -# Berkeley DB should handle this better). Move the corrupted database -# out of the way, and start with a new database. -# -sub sigsegv_handler { - my $backup = $database_name . "." . time(); - - rename $database_name, $backup || fatal_exit ("Can't save $database_name as $backup): $!"); - fatal_exit ("Caught signal 11; the corrupted database is saved as $backup"); -} - -$SIG{'SEGV'} = 'sigsegv_handler'; - # # This process runs as a daemon, so it can't log to a terminal. Use # syslog so that people can actually see our messages. @@ -187,14 +160,13 @@ while () { fatal_exit ("unrecognized request type: '$attr{request}'") unless $attr{request} eq "smtpd_access_policy"; - my $action = "ok"; + my $action = $DEFAULT_RESPONSE; my %responses; foreach my $handler (@HANDLERS) { no strict 'refs'; my $response = $handler->(attr=>\%attr); syslog(debug=> "handler %s: %s", $handler, $response); - if ($response !~ /^(ok|dunno)/i) { - + if ($response and $response !~ /^dunno/i) { syslog(info=> "handler %s: %s is decisive.", $handler, $response); $action = $response; last; } @@ -226,7 +198,7 @@ sub sender_permitted_from { elsif ($result eq "error") { return "DUNNO"; } else { return "DUNNO"; } - # TODO XXX: prepend Received-SPF header. + # TODO XXX: prepend Received-SPF header. Wietse says he will add that functionality soon. } # ---------------------------------------------------------- @@ -265,117 +237,3 @@ sub address_stripped { return $string; } -# ---------------------------------------------------------- -# plugin: greylisting -# ---------------------------------------------------------- - -my $Database_Obj; -my %DB_Hash; - -# -# Demo SMTPD access policy routine. The result is an action just like -# it would be specified on the right-hand side of a Postfix access -# table. Request attributes are available via the %attr hash. -# -sub greylisting { - local %_ = @_; - my %attr = %{ $_{attr} }; - - my($key, $time_stamp, $now); - - return "DUNNO" if grep { $attr{sender} =~ $_ } @Greylisting_Whitelisted_Senders; - - # Open the database on the fly. - open_database() unless $Database_Obj; - - # Lookup the time stamp for this client/sender/recipient. - $key = lc join "/", @attr{qw( client_address sender recipient )}; - $time_stamp = read_database($key); - $now = time(); - - # If this is a new request add this client/sender/recipient to the database. - if ($time_stamp == 0) { - $time_stamp = $now; - update_database($key, $time_stamp); - } - - # In case of success, return DUNNO instead of OK so that the - # check_policy_service restriction can be followed by other restrictions. - # In case of failure, specify DEFER_IF_PERMIT so that mail can - # still be blocked by other access restrictions. - syslog $syslog_priority, "request age %d", $now - $time_stamp if $VERBOSE; - if ($now - $time_stamp > $GREYLIST_DELAY) { - syslog(debug=> "handler %s: %s showed up in the database more than $GREYLIST_DELAY seconds ago.", - "greylisting", $key); - return "dunno"; - } else { - syslog(debug=> "handler %s: %s has not been in the database $GREYLIST_DELAY seconds. denying.", - "greylisting", $key); - return "defer_if_permit Greylisting delay; please try again after $GREYLIST_DELAY seconds."; - } -} - -# -# You should not have to make changes below this point. -# -sub LOCK_SH { 1 }; # Shared lock (used for reading). -sub LOCK_EX { 2 }; # Exclusive lock (used for writing). -sub LOCK_NB { 4 }; # Don't block (for testing). -sub LOCK_UN { 8 }; # Release lock. - -# -# Open hash database. -# -sub open_database { - my($database_fd); - - # Use tied database to make complex manipulations easier to express. - $Database_Obj = tie(%DB_Hash, 'DB_File', $database_name, - O_CREAT|O_RDWR, 0644, $DB_BTREE) || - fatal_exit "Cannot open database %s while running as $>: $!", $database_name; - $database_fd = $Database_Obj->fd; - open DATABASE_HANDLE, "+<&=$database_fd" || - fatal_exit "Cannot fdopen database %s: $!", $database_name; - syslog $syslog_priority, "open %s", $database_name if $VERBOSE; -} - -# -# Read database. Use a shared lock to avoid reading the database -# while it is being changed. XXX There should be a way to synchronize -# our cache from the on-file database before looking up the key. -# -sub read_database { - my($key) = @_; - my($value); - - flock DATABASE_HANDLE, LOCK_SH || - fatal_exit "Can't get shared lock on %s: $!", $database_name; - # XXX Synchronize our cache from the on-disk copy before lookup. - $value = $DB_Hash{$key}; - syslog $syslog_priority, "lookup %s: %s", $key, $value if $VERBOSE; - flock DATABASE_HANDLE, LOCK_UN || - fatal_exit "Can't unlock %s: $!", $database_name; - return $value; -} - -# -# Update database. Use an exclusive lock to avoid collisions with -# other updaters, and to avoid surprises in database readers. XXX -# There should be a way to synchronize our cache from the on-file -# database before updating the database. -# -sub update_database { - my($key, $value) = @_; - - syslog $syslog_priority, "store %s: %s", $key, $value if $VERBOSE; - flock DATABASE_HANDLE, LOCK_EX || - fatal_exit "Can't exclusively lock %s: $!", $database_name; - # XXX Synchronize our cache from the on-disk copy before update. - $DB_Hash{$key} = $value; - $Database_Obj->sync() && - fatal_exit "Can't update %s: $!", $database_name; - flock DATABASE_HANDLE, LOCK_UN || - fatal_exit "Can't unlock %s: $!", $database_name; -} - - From e2fac258ff21178d2a5c4f8c1b8656ee3a8a51be Mon Sep 17 00:00:00 2001 From: Julian Mehnle <> Date: Sat, 17 Jun 2006 16:33:05 +0000 Subject: [PATCH 02/87] postfix-policyd-spf-perl/trunk/postfix-policyd-spf * Version 1.02. --- postfix-policyd-spf | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/postfix-policyd-spf b/postfix-policyd-spf index 9ae657f..7ba9084 100755 --- a/postfix-policyd-spf +++ b/postfix-policyd-spf @@ -3,7 +3,7 @@ # mengwong@pobox.com # Wed Dec 10 03:52:04 EST 2003 # postfix-policyd-spf -# version 1.01 +# version 1.02 # see http://spf.pobox.com/ use Fcntl; @@ -35,6 +35,7 @@ my $syslog_socktype = 'unix'; # inet, unix, stream, console my $syslog_facility = "mail"; my $syslog_options = "pid"; my $syslog_priority = "info"; +my $syslog_ident = "postfix/policy-spf"; # ---------------------------------------------------------- # minimal documentation @@ -137,7 +138,7 @@ select((select(STDOUT), $| = 1)[0]); # syslog so that people can actually see our messages. # setlogsock $syslog_socktype; -openlog $0, $syslog_options, $syslog_facility; +openlog $syslog_ident, $syslog_options, $syslog_facility; # ---------------------------------------------------------- # main From c8d2974eff43ada1d9f57324991d9dd7bbf08889 Mon Sep 17 00:00:00 2001 From: Julian Mehnle <> Date: Sat, 17 Jun 2006 16:34:02 +0000 Subject: [PATCH 03/87] postfix-policyd-spf-perl/trunk/postfix-policyd-spf * Version 1.03. --- postfix-policyd-spf | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/postfix-policyd-spf b/postfix-policyd-spf index 7ba9084..53a93e4 100755 --- a/postfix-policyd-spf +++ b/postfix-policyd-spf @@ -3,7 +3,7 @@ # mengwong@pobox.com # Wed Dec 10 03:52:04 EST 2003 # postfix-policyd-spf -# version 1.02 +# version 1.03 # see http://spf.pobox.com/ use Fcntl; @@ -196,8 +196,9 @@ sub sender_permitted_from { if ($result eq "pass") { return "DUNNO"; } elsif ($result eq "fail") { return "REJECT " . ($smtp_comment || $header_comment); } - elsif ($result eq "error") { return "DUNNO"; } + elsif ($result eq "error") { return "450 temporary failure: $smtp_comment"; } else { return "DUNNO"; } + # unknown, softfail, neutral and none all return DUNNO # TODO XXX: prepend Received-SPF header. Wietse says he will add that functionality soon. } From 2cba66720bbf0f0fdf9a328415b8093271ed3695 Mon Sep 17 00:00:00 2001 From: Julian Mehnle <> Date: Sat, 17 Jun 2006 16:41:58 +0000 Subject: [PATCH 04/87] postfix-policyd-spf-perl/trunk/postfix-policyd-spf * Version 1.05. --- postfix-policyd-spf | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/postfix-policyd-spf b/postfix-policyd-spf index 53a93e4..d9240d1 100755 --- a/postfix-policyd-spf +++ b/postfix-policyd-spf @@ -3,7 +3,7 @@ # mengwong@pobox.com # Wed Dec 10 03:52:04 EST 2003 # postfix-policyd-spf -# version 1.03 +# version 1.05 # see http://spf.pobox.com/ use Fcntl; @@ -15,11 +15,10 @@ use strict; # ---------------------------------------------------------- # to use SPF, install Mail::SPF::Query from CPAN or from the SPF website at http://spf.pobox.com/downloads.html -# then uncomment the SPF line. my @HANDLERS; push @HANDLERS, "testing"; -# push @HANDLERS, "sender_permitted_from"; use Mail::SPF::Query; + push @HANDLERS, "sender_permitted_from"; use Mail::SPF::Query; my $VERBOSE = 1; @@ -186,9 +185,14 @@ sub sender_permitted_from { local %_ = @_; my %attr = %{ $_{attr} }; - my $query = new Mail::SPF::Query (ip =>$attr{client_address}, - sender=>$attr{sender}, - helo =>$attr{helo_name}); + my $query = eval { new Mail::SPF::Query (ip =>$attr{client_address}, + sender=>$attr{sender}, + helo =>$attr{helo_name}) }; + if ($@) { + syslog(info=>"%s: Mail::SPF::Query->new(%s, %s, %s) failed: %s", + $attr{queue_id}, $attr{client_address}, $attr{sender}, $attr{helo_name}, $@); + return "DUNNO"; + } my ($result, $smtp_comment, $header_comment) = $query->result(); syslog(info=>"%s: SPF %s: smtp_comment=%s, header_comment=%s", From 5e0cd7d9a06cf2f1a368636f8c379256c1b601ab Mon Sep 17 00:00:00 2001 From: Julian Mehnle <> Date: Sat, 17 Jun 2006 16:45:10 +0000 Subject: [PATCH 05/87] postfix-policyd-spf-perl/trunk/postfix-policyd-spf * Version 1.06. --- postfix-policyd-spf | 60 ++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 33 deletions(-) diff --git a/postfix-policyd-spf b/postfix-policyd-spf index d9240d1..94840d8 100755 --- a/postfix-policyd-spf +++ b/postfix-policyd-spf @@ -1,26 +1,25 @@ #!/usr/bin/perl -# mengwong@pobox.com -# Wed Dec 10 03:52:04 EST 2003 # postfix-policyd-spf -# version 1.05 -# see http://spf.pobox.com/ +# http://www.openspf.org +# version 1.06 +# $Id$ use Fcntl; use Sys::Syslog qw(:DEFAULT setlogsock); use strict; # ---------------------------------------------------------- -# configuration +# configuration # ---------------------------------------------------------- -# to use SPF, install Mail::SPF::Query from CPAN or from the SPF website at http://spf.pobox.com/downloads.html +# to use SPF, install Mail::SPF::Query from CPAN or from the SPF website at http://www.openspf.org/downloads.html my @HANDLERS; push @HANDLERS, "testing"; push @HANDLERS, "sender_permitted_from"; use Mail::SPF::Query; -my $VERBOSE = 1; +my $VERBOSE = 0; my $DEFAULT_RESPONSE = "DUNNO"; @@ -37,7 +36,7 @@ my $syslog_priority = "info"; my $syslog_ident = "postfix/policy-spf"; # ---------------------------------------------------------- -# minimal documentation +# minimal documentation # ---------------------------------------------------------- # @@ -70,10 +69,10 @@ my $syslog_ident = "postfix/policy-spf"; # To use this from Postfix SMTPD, use in /etc/postfix/main.cf: # # smtpd_recipient_restrictions = -# ... -# reject_unauth_destination -# check_policy_service unix:private/policy -# ... +# ... +# reject_unauth_destination +# check_policy_service unix:private/policy +# ... # # NOTE: specify check_policy_service AFTER reject_unauth_destination # or else your system can become an open relay. @@ -114,7 +113,7 @@ my $syslog_ident = "postfix/policy-spf"; # Jul 23 18:43:29 dumbo/dumbo policyd[21171]: Attribute: sender=mengwong@newbabe.mengwong.com # ---------------------------------------------------------- -# initialization +# initialization # ---------------------------------------------------------- # @@ -140,7 +139,7 @@ setlogsock $syslog_socktype; openlog $syslog_ident, $syslog_options, $syslog_facility; # ---------------------------------------------------------- -# main +# main # ---------------------------------------------------------- # @@ -179,36 +178,32 @@ while () { } # ---------------------------------------------------------- -# plugin: SPF +# plugin: SPF # ---------------------------------------------------------- sub sender_permitted_from { local %_ = @_; my %attr = %{ $_{attr} }; my $query = eval { new Mail::SPF::Query (ip =>$attr{client_address}, - sender=>$attr{sender}, - helo =>$attr{helo_name}) }; + sender=>$attr{sender}, + helo =>$attr{helo_name}) }; if ($@) { syslog(info=>"%s: Mail::SPF::Query->new(%s, %s, %s) failed: %s", - $attr{queue_id}, $attr{client_address}, $attr{sender}, $attr{helo_name}, $@); + $attr{queue_id}, $attr{client_address}, $attr{sender}, $attr{helo_name}, $@); return "DUNNO"; } my ($result, $smtp_comment, $header_comment) = $query->result(); syslog(info=>"%s: SPF %s: smtp_comment=%s, header_comment=%s", - $attr{queue_id}, $result, $smtp_comment, $header_comment); + $attr{queue_id}, $result, $smtp_comment, $header_comment); - if ($result eq "pass") { return "DUNNO"; } - elsif ($result eq "fail") { return "REJECT " . ($smtp_comment || $header_comment); } - elsif ($result eq "error") { return "450 temporary failure: $smtp_comment"; } - else { return "DUNNO"; } - # unknown, softfail, neutral and none all return DUNNO - - # TODO XXX: prepend Received-SPF header. Wietse says he will add that functionality soon. + if ($result eq "fail") { return "REJECT $smtp_comment"; } + elsif ($result eq "error") { return "DEFER_IF_PERMIT $smtp_comment"; } + else { return "PREPEND Received-SPF: $result ($header_comment)"; } } # ---------------------------------------------------------- -# plugin: testing +# plugin: testing # ---------------------------------------------------------- sub testing { local %_ = @_; @@ -220,15 +215,15 @@ sub testing { $attr{recipient} =~ /policyblock/) { syslog(info=>"%s: testing: will block as requested", - $attr{queue_id}); + $attr{queue_id}); return "REJECT smtpd-policy blocking $attr{recipient}"; } else { syslog(info=>"%s: testing: stripped sender=%s, stripped rcpt=%s", - $attr{queue_id}, - address_stripped($attr{sender}), - address_stripped($attr{recipient}), - ); + $attr{queue_id}, + address_stripped($attr{sender}), + address_stripped($attr{recipient}), + ); } return "DUNNO"; @@ -242,4 +237,3 @@ sub address_stripped { } return $string; } - From 0bd77bdc15a2ebb6511fc7d815a29c4bfc07764c Mon Sep 17 00:00:00 2001 From: Julian Mehnle <> Date: Sat, 17 Jun 2006 16:46:46 +0000 Subject: [PATCH 06/87] postfix-policyd-spf-perl/trunk/postfix-policyd-spf * Version 1.07. --- postfix-policyd-spf | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/postfix-policyd-spf b/postfix-policyd-spf index 94840d8..10842a3 100755 --- a/postfix-policyd-spf +++ b/postfix-policyd-spf @@ -2,8 +2,8 @@ # postfix-policyd-spf # http://www.openspf.org -# version 1.06 -# $Id$ +# version 1.07 +# $Id: postfix-policyd-spf 147 2006-03-14 21:51:58Z julian $ use Fcntl; use Sys::Syslog qw(:DEFAULT setlogsock); @@ -120,9 +120,9 @@ my $syslog_ident = "postfix/policy-spf"; # Log an error and abort. # sub fatal_exit { - syslog(err => "fatal_exit: @_"); - syslog(warn => "fatal_exit: @_"); - syslog(info => "fatal_exit: @_"); + syslog(err => "fatal_exit: @_"); + syslog(warning => "fatal_exit: @_"); + syslog(info => "fatal_exit: @_"); die "fatal: @_"; } @@ -149,11 +149,11 @@ my %attr; while () { chomp; if (/=/) { my ($k, $v) = split (/=/, $_, 2); $attr{$k} = $v; next } - elsif (length) { syslog(warn=>sprintf("warning: ignoring garbage: %.100s", $_)); next; } + elsif (length) { syslog(warning => sprintf("warning: ignoring garbage: %.100s", $_)); next; } if ($VERBOSE) { for (sort keys %attr) { - syslog(debug=> "Attribute: %s=%s", $_, $attr{$_}); + syslog(debug => "Attribute: %s=%s", $_, $attr{$_}); } } @@ -164,14 +164,14 @@ while () { foreach my $handler (@HANDLERS) { no strict 'refs'; my $response = $handler->(attr=>\%attr); - syslog(debug=> "handler %s: %s", $handler, $response); + syslog(debug => "handler %s: %s", $handler, $response); if ($response and $response !~ /^dunno/i) { - syslog(info=> "handler %s: %s is decisive.", $handler, $response); + syslog(info => "handler %s: %s is decisive.", $handler, $response); $action = $response; last; } } - syslog(info=> "decided action=%s", $action); + syslog(info => "decided action=%s", $action); print STDOUT "action=$action\n\n"; %attr = (); @@ -188,13 +188,13 @@ sub sender_permitted_from { sender=>$attr{sender}, helo =>$attr{helo_name}) }; if ($@) { - syslog(info=>"%s: Mail::SPF::Query->new(%s, %s, %s) failed: %s", + syslog(info => "%s: Mail::SPF::Query->new(%s, %s, %s) failed: %s", $attr{queue_id}, $attr{client_address}, $attr{sender}, $attr{helo_name}, $@); return "DUNNO"; } my ($result, $smtp_comment, $header_comment) = $query->result(); - syslog(info=>"%s: SPF %s: smtp_comment=%s, header_comment=%s", + syslog(info => "%s: SPF %s: smtp_comment=%s, header_comment=%s", $attr{queue_id}, $result, $smtp_comment, $header_comment); if ($result eq "fail") { return "REJECT $smtp_comment"; } @@ -214,12 +214,11 @@ sub testing { and $attr{recipient} =~ /policyblock/) { - syslog(info=>"%s: testing: will block as requested", - $attr{queue_id}); + syslog(info => "%s: testing: will block as requested", $attr{queue_id}); return "REJECT smtpd-policy blocking $attr{recipient}"; } else { - syslog(info=>"%s: testing: stripped sender=%s, stripped rcpt=%s", + syslog(info => "%s: testing: stripped sender=%s, stripped rcpt=%s", $attr{queue_id}, address_stripped($attr{sender}), address_stripped($attr{recipient}), From 57be2e1c1155bfa67512ce16d589e600c72d5c72 Mon Sep 17 00:00:00 2001 From: Julian Mehnle <> Date: Sat, 17 Jun 2006 16:53:44 +0000 Subject: [PATCH 07/87] postfix-policyd-spf-perl/trunk/postfix-policyd-spf * Version 1.08. --- postfix-policyd-spf | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/postfix-policyd-spf b/postfix-policyd-spf index 10842a3..b668f25 100755 --- a/postfix-policyd-spf +++ b/postfix-policyd-spf @@ -1,8 +1,8 @@ #!/usr/bin/perl -# postfix-policyd-spf -# http://www.openspf.org -# version 1.07 +# postfix-policyd-spf-perl +# http://www.openspf.org/source/software/postfix-policyd-spf/ +# version 1.08 # $Id: postfix-policyd-spf 147 2006-03-14 21:51:58Z julian $ use Fcntl; @@ -60,6 +60,10 @@ my $syslog_ident = "postfix/policy-spf"; # process terminates after 100 seconds of idle time, or after serving # 100 clients. Thus, the cost of starting this PERL script is smoothed # out over time. +# The default policy_time_limit is 1000 seconds.  This may be to short +# for some SMTP transactions to complete.  As recommended in +# SMTPD_POLICY_README, this should be extended to 3600 seconds.  To do +# so, set "policy_time_limit = 3600" in /etc/postfix/main.cf. # # To run this from /etc/postfix/master.cf: # From 711623e5c9c3f70e3283336c74d3720bf7e97379 Mon Sep 17 00:00:00 2001 From: Julian Mehnle <> Date: Sat, 17 Jun 2006 18:42:33 +0000 Subject: [PATCH 08/87] postfix-policyd-spf-perl/trunk/postfix-policyd-spf -> postfix-policyd-spf-perl * Renamed file. * Moved documentation from executable into separate README and INSTALL files. * Minor and purely cosmetic code clean-up. postfix-policyd-spf-perl/trunk/README * Added, adopting non-installation documentation from postfix-policyd-spf and improving it. postfix-policyd-spf-perl/trunk/INSTALL * Added, adopting installation documentation from postfix-policyd-spf and improving it. postfix-policyd-spf-perl/trunk/CHANGES * Added, describing changes for 1.08 release. postfix-policyd-spf-perl/trunk/debian postfix-policyd-spf-perl/trunk/debian/* * Added. --- CHANGES | 15 +++ INSTALL | 32 ++++++ README | 66 +++++++++++++ debian/changelog | 5 + debian/compat | 1 + debian/control | 14 +++ debian/copyright | 13 +++ debian/rules | 56 +++++++++++ ...ix-policyd-spf => postfix-policyd-spf-perl | 97 ++----------------- 9 files changed, 211 insertions(+), 88 deletions(-) create mode 100644 CHANGES create mode 100644 INSTALL create mode 100644 README create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100755 debian/rules rename postfix-policyd-spf => postfix-policyd-spf-perl (57%) diff --git a/CHANGES b/CHANGES new file mode 100644 index 0000000..25ddd84 --- /dev/null +++ b/CHANGES @@ -0,0 +1,15 @@ +# Legend: +# --- = A new release +# + = Added a feature (in a backwards compatible way) +# ! = Changed something significant, or removed a feature +# * = Fixed a bug, or made a minor improvement + +--- 1.08 (2006-06-17 20:00) + + * Added Debian package control files. + * Moved documentation from executable into separate README and INSTALL + files. Improved documentation. + * Minor and purely cosmetic code clean-up. + +# $Id$ +# vim:tw=79 sts=2 sw=2 diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..9e9c3cd --- /dev/null +++ b/INSTALL @@ -0,0 +1,32 @@ +System Requirements +------------------- + +The following Perl version and packages are required for running +postfix-policyd-spf-perl: + + Perl 5.6 + Mail::SPF::Query + +Installing +---------- + + 1. Copy postfix-policyd-spf-perl to /usr/lib/postfix/policyd-spf-perl + + 2. Add the following line to /etc/postfix/master.cf: + + policy unix - n n - - spawn + user=nobody argv=/usr/bin/perl /usr/lib/postfix/policyd-spf-perl + + 3. Configure the Postfix policy service in /etc/postfix/main.cf: + + smtpd_recipient_restrictions = + ... + reject_unauth_destination + check_policy_service unix:private/policy + ... + + NOTE: Specify check_policy_service AFTER reject_unauth_destination or + else your system can become an open relay. + +# $Id: README 167 2005-01-17 18:26:45Z julian $ +# vim:tw=79 diff --git a/README b/README new file mode 100644 index 0000000..75bf03d --- /dev/null +++ b/README @@ -0,0 +1,66 @@ +postfix-policyd-spf-perl 1.08 +A Postfix SMTPd policy server for SPF checking +(C) 2003-2004 Meng Weng Wong +Contributions by various members of the SPF project + +============================================================================== + +postfix-policyd-spf-perl is a Postfix SMTPd policy daemon for SPF checking. +It is implemented in pure Perl and uses the Mail::SPF::Query CPAN module. + +See INSTALL for installation instructions. + +Usage: + policyd-spf-perl [-v] + +This documentation assumes you have read Postfix's README_FILES/ +SMTPD_POLICY_README. + +Logging is sent to syslogd. + +Each time a Postfix SMTP server process is started it connects to the policy +service socket, and Postfix runs one instance of this Perl script. By +default, a Postfix SMTP server process terminates after 100 seconds of idle +time, or after serving 100 clients. Thus, the cost of starting this Perl +script is smoothed out over time. + +The default policy_time_limit is 1000 seconds.  This may be to short for some +SMTP transactions to complete.  As recommended in SMTPD_POLICY_README, this +should be extended to 3600 seconds.  To do so, set "policy_time_limit = 3600" +in /etc/postfix/main.cf. + +Testing the policy daemon +------------------------- + +To test the policy daemon by hand, execute: + + % perl /usr/lib/postfix/policyd-spf-perl + +Each query is a bunch of attributes. Order does not matter, and the daemon +uses only a few of all the attributes shown below: + + request=smtpd_access_policy + protocol_state=RCPT + protocol_name=SMTP + helo_name=some.domain.tld + queue_id=8045F2AB23 + sender=foo@bar.tld + recipient=bar@foo.tld + client_address=1.2.3.4 + client_name=another.domain.tld + [empty line] + +The policy daemon will answer in the same style, with an attribute list +followed by a empty line: + + action=dunno + [empty line] + +License +------- + +Thingy is free software. You may use, modify, and distribute it under the +GNU GPL (version 2 or later). + +# $Id$ +# vim:tw=79 diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..63b4f20 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +postfix-policyd-spf-perl (1.08) unstable; urgency=low + + * Initial release as a Debian package. + + -- Julian Mehnle Sat, 17 Jun 2006 19:32:31 +0000 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..7ed6ff8 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +5 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..987d0b8 --- /dev/null +++ b/debian/control @@ -0,0 +1,14 @@ +Source: postfix-policyd-spf-perl +Section: mail +Priority: optional +Maintainer: Julian Mehnle +Build-Depends-Indep: debhelper (>= 5), perl (>= 5.6) +Standards-Version: 3.7.2 + +Package: postfix-policyd-spf-perl +Architecture: all +Depends: libmail-spf-query-perl +Recommends: postfix +Description: pure-Perl Postfix policy daemon for SPF checking + postfix-policyd-spf-perl is a Postfix SMTPd policy daemon for SPF checking. + It is implemented in pure Perl and uses the Mail::SPF::Query module. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..2f5d76e --- /dev/null +++ b/debian/copyright @@ -0,0 +1,13 @@ +This is the Debian package for postfix-policyd-spf-perl, which is available +from . + +(C) 2003-2004 Meng Weng Wong +Contributions by various members of the SPF project + +Julian Mehnle is the maintainer of the Debian package. + +This is free software; you can redistribute it and/or modify it under the terms +of the GNU General Public License (version 2 or later). + +On Debian systems, the complete text of the GPL v2 can be found here: +/usr/share/common-licenses/GPL-2 diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..435fce5 --- /dev/null +++ b/debian/rules @@ -0,0 +1,56 @@ +#!/usr/bin/make -f + +PACKAGE = $(shell dh_listpackages) + +ifndef PERL + PERL = /usr/bin/perl +endif + +TMP = $(CURDIR)/debian/$(PACKAGE) + +build: build-stamp + # Nothing to do. + +clean: + dh_testdir + dh_testroot + dh_clean build-stamp install-stamp + +install: install-stamp +install-stamp: + dh_testdir + dh_testroot + dh_clean -k + + install -D postfix-policyd-spf-perl $(TMP)/usr/lib/postfix/policyd-spf-perl + + touch install-stamp + +# Build architecture-independent files here: +binary-indep: build install + dh_testdir + dh_testroot + + dh_install + dh_installdirs + dh_installdocs README + dh_installchangelogs CHANGES + #dh_installexamples examples/* + #dh_installman + #dh_link + + dh_compress + dh_fixperms + dh_installdeb + dh_perl + dh_gencontrol + dh_md5sums + dh_builddeb + +# Build architecture-dependent files here: +binary-arch: + # Nothing to do. + +binary: binary-indep binary-arch + +.PHONY: build clean binary-indep binary-arch binary install diff --git a/postfix-policyd-spf b/postfix-policyd-spf-perl similarity index 57% rename from postfix-policyd-spf rename to postfix-policyd-spf-perl index b668f25..b533d83 100755 --- a/postfix-policyd-spf +++ b/postfix-policyd-spf-perl @@ -1,23 +1,25 @@ #!/usr/bin/perl # postfix-policyd-spf-perl -# http://www.openspf.org/source/software/postfix-policyd-spf/ +# http://www.openspf.org/source/software/postfix-policyd-spf-perl/ # version 1.08 # $Id: postfix-policyd-spf 147 2006-03-14 21:51:58Z julian $ +our $VERSION = '1.08'; + +use strict; + use Fcntl; use Sys::Syslog qw(:DEFAULT setlogsock); -use strict; +use Mail::SPF::Query; # ---------------------------------------------------------- # configuration # ---------------------------------------------------------- -# to use SPF, install Mail::SPF::Query from CPAN or from the SPF website at http://www.openspf.org/downloads.html - - my @HANDLERS; - push @HANDLERS, "testing"; - push @HANDLERS, "sender_permitted_from"; use Mail::SPF::Query; +my @HANDLERS; +push @HANDLERS, "testing"; +push @HANDLERS, "sender_permitted_from"; my $VERBOSE = 0; @@ -35,87 +37,6 @@ my $syslog_options = "pid"; my $syslog_priority = "info"; my $syslog_ident = "postfix/policy-spf"; -# ---------------------------------------------------------- -# minimal documentation -# ---------------------------------------------------------- - -# -# Usage: smtpd-policy.pl [-v] -# -# Demo delegated Postfix SMTPD policy server. -# This server implements SPF. -# Another server implements greylisting. -# Postfix has a pluggable policy server architecture. -# You can call one or both from Postfix. -# -# The SPF handler uses Mail::SPF::Query to do the heavy lifting. -# -# This documentation assumes you have read Postfix's README_FILES/SMTPD_POLICY_README -# -# Logging is sent to syslogd. -# -# How it works: each time a Postfix SMTP server process is started -# it connects to the policy service socket, and Postfix runs one -# instance of this PERL script. By default, a Postfix SMTP server -# process terminates after 100 seconds of idle time, or after serving -# 100 clients. Thus, the cost of starting this PERL script is smoothed -# out over time. -# The default policy_time_limit is 1000 seconds.  This may be to short -# for some SMTP transactions to complete.  As recommended in -# SMTPD_POLICY_README, this should be extended to 3600 seconds.  To do -# so, set "policy_time_limit = 3600" in /etc/postfix/main.cf. -# -# To run this from /etc/postfix/master.cf: -# -# policy unix - n n - - spawn -# user=nobody argv=/usr/bin/perl /usr/libexec/postfix/smtpd-policy.pl -# -# To use this from Postfix SMTPD, use in /etc/postfix/main.cf: -# -# smtpd_recipient_restrictions = -# ... -# reject_unauth_destination -# check_policy_service unix:private/policy -# ... -# -# NOTE: specify check_policy_service AFTER reject_unauth_destination -# or else your system can become an open relay. -# -# To test this script by hand, execute: -# -# % perl smtpd-policy.pl -# -# Each query is a bunch of attributes. Order does not matter, and -# the demo script uses only a few of all the attributes shown below: -# -# request=smtpd_access_policy -# protocol_state=RCPT -# protocol_name=SMTP -# helo_name=some.domain.tld -# queue_id=8045F2AB23 -# sender=foo@bar.tld -# recipient=bar@foo.tld -# client_address=1.2.3.4 -# client_name=another.domain.tld -# [empty line] -# -# The policy server script will answer in the same style, with an -# attribute list followed by a empty line: -# -# action=dunno -# [empty line] -# - -# Jul 23 18:43:29 dumbo/dumbo policyd[21171]: Attribute: client_address=208.210.125.227 -# Jul 23 18:43:29 dumbo/dumbo policyd[21171]: Attribute: client_name=newbabe.mengwong.com -# Jul 23 18:43:29 dumbo/dumbo policyd[21171]: Attribute: helo_name=newbabe.mengwong.com -# Jul 23 18:43:29 dumbo/dumbo policyd[21171]: Attribute: protocol_name=ESMTP -# Jul 23 18:43:29 dumbo/dumbo policyd[21171]: Attribute: protocol_state=RCPT -# Jul 23 18:43:29 dumbo/dumbo policyd[21171]: Attribute: queue_id= -# Jul 23 18:43:29 dumbo/dumbo policyd[21171]: Attribute: recipient=mengwong@dumbo.pobox.com -# Jul 23 18:43:29 dumbo/dumbo policyd[21171]: Attribute: request=smtpd_access_policy -# Jul 23 18:43:29 dumbo/dumbo policyd[21171]: Attribute: sender=mengwong@newbabe.mengwong.com - # ---------------------------------------------------------- # initialization # ---------------------------------------------------------- From 8eb6a55e9f3c8b1c4700b9e0a80b53ed275d9e40 Mon Sep 17 00:00:00 2001 From: Julian Mehnle <> Date: Sat, 17 Jun 2006 18:58:27 +0000 Subject: [PATCH 09/87] postfix-policyd-spf-perl/trunk/README * Refer to README.Debian in addition to INSTALL. postfix-policyd-spf-perl/trunk/INSTALL * Minor corrections and additions. postfix-policyd-spf-perl/trunk/debian/README.Debian * Added, including installation instructions from INSTALL that are relevant on Debian systems. postfix-policyd-spf-perl/trunk/debian/control * Build-Depends: debhelper (was: Build-Depends-Indep:) postfix-policyd-spf-perl/trunk/debian/rules * "build" target no longer depends on inexistent "build-stamp" target. --- INSTALL | 4 +++- README | 2 +- debian/README.Debian | 23 +++++++++++++++++++++++ debian/control | 3 ++- debian/rules | 2 +- 5 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 debian/README.Debian diff --git a/INSTALL b/INSTALL index 9e9c3cd..1f057e9 100644 --- a/INSTALL +++ b/INSTALL @@ -12,7 +12,7 @@ Installing 1. Copy postfix-policyd-spf-perl to /usr/lib/postfix/policyd-spf-perl - 2. Add the following line to /etc/postfix/master.cf: + 2. Add the following to /etc/postfix/master.cf: policy unix - n n - - spawn user=nobody argv=/usr/bin/perl /usr/lib/postfix/policyd-spf-perl @@ -28,5 +28,7 @@ Installing NOTE: Specify check_policy_service AFTER reject_unauth_destination or else your system can become an open relay. + 4. Restart Postfix. + # $Id: README 167 2005-01-17 18:26:45Z julian $ # vim:tw=79 diff --git a/README b/README index 75bf03d..80f551c 100644 --- a/README +++ b/README @@ -8,7 +8,7 @@ Contributions by various members of the SPF project postfix-policyd-spf-perl is a Postfix SMTPd policy daemon for SPF checking. It is implemented in pure Perl and uses the Mail::SPF::Query CPAN module. -See INSTALL for installation instructions. +See INSTALL or README.Debian for installation instructions. Usage: policyd-spf-perl [-v] diff --git a/debian/README.Debian b/debian/README.Debian new file mode 100644 index 0000000..af1060e --- /dev/null +++ b/debian/README.Debian @@ -0,0 +1,23 @@ +Installing +---------- + + 1. Add the following to /etc/postfix/master.cf: + + policy unix - n n - - spawn + user=nobody argv=/usr/bin/perl /usr/lib/postfix/policyd-spf-perl + + 2. Configure the Postfix policy service in /etc/postfix/main.cf: + + smtpd_recipient_restrictions = + ... + reject_unauth_destination + check_policy_service unix:private/policy + ... + + NOTE: Specify check_policy_service AFTER reject_unauth_destination or + else your system can become an open relay. + + 3. Restart Postfix. + +# $Id: README 167 2005-01-17 18:26:45Z julian $ +# vim:tw=79 diff --git a/debian/control b/debian/control index 987d0b8..f1c9337 100644 --- a/debian/control +++ b/debian/control @@ -2,7 +2,8 @@ Source: postfix-policyd-spf-perl Section: mail Priority: optional Maintainer: Julian Mehnle -Build-Depends-Indep: debhelper (>= 5), perl (>= 5.6) +Build-Depends: debhelper (>= 5) +Build-Depends-Indep: perl (>= 5.6) Standards-Version: 3.7.2 Package: postfix-policyd-spf-perl diff --git a/debian/rules b/debian/rules index 435fce5..0c49c0f 100755 --- a/debian/rules +++ b/debian/rules @@ -8,7 +8,7 @@ endif TMP = $(CURDIR)/debian/$(PACKAGE) -build: build-stamp +build: # Nothing to do. clean: From 292876bc3684623b724c8a93595dec3e98eeaf8e Mon Sep 17 00:00:00 2001 From: Julian Mehnle <> Date: Wed, 10 Jan 2007 14:19:43 +0000 Subject: [PATCH 10/87] postfix-policyd-spf-perl/trunk/LICENSE * Added an explicit copy of the GPLv2 to satisfy Ubuntu. postfix-policyd-spf-perl/trunk/README * Mentioned the explicit copy of the license and fixed a bogus wording that was leftover from another package from which the README file was originally copied. postfix-policyd-spf-perl/trunk/postfix-policyd-spf-perl * Removed superfluous definition of the $syslog_priority variable. postfix-policyd-spf-python postfix-policyd-spf-python/trunk postfix-policyd-spf-python/tags * Added. --- LICENSE | 339 +++++++++++++++++++++++++++++++++++++++ README | 4 +- postfix-policyd-spf-perl | 1 - 3 files changed, 341 insertions(+), 3 deletions(-) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d511905 --- /dev/null +++ b/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/README b/README index 80f551c..98a0e71 100644 --- a/README +++ b/README @@ -59,8 +59,8 @@ followed by a empty line: License ------- -Thingy is free software. You may use, modify, and distribute it under the -GNU GPL (version 2 or later). +postfix-policyd-spf-perl is free software. You may use, modify, and distribute +it under the GNU GPL (version 2 or later); see the LICENSE file. # $Id$ # vim:tw=79 diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index b533d83..1d5f4bb 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -34,7 +34,6 @@ my $DEFAULT_RESPONSE = "DUNNO"; my $syslog_socktype = 'unix'; # inet, unix, stream, console my $syslog_facility = "mail"; my $syslog_options = "pid"; -my $syslog_priority = "info"; my $syslog_ident = "postfix/policy-spf"; # ---------------------------------------------------------- From 01e939ac3d3cc1c74f14a69aec5c6afef987efa5 Mon Sep 17 00:00:00 2001 From: Julian Mehnle <> Date: Wed, 10 Jan 2007 20:00:57 +0000 Subject: [PATCH 11/87] trunk/postfix-policyd-spf-perl * use version; * Bumped version number to 1.08.1. * Purely cosmetic code clean-up. * Added svn properties: svn:mime-type = text/x-perl svn:keywords = "Author Date Id Rev URL" svn:eol-style = native trunk/README * Bumped version number to 1.08.1. * Added Scott Kitterman to copyright statement. * Updated website URL. trunk/INSTALL * Added version Perl module to list of run-time requirements. trunk/INSTALL trunk/LICENSE * Added svn properties: svn:mime-type = text/plain svn:keywords = "Author Date Id Rev URL" svn:eol-style = native trunk/CHANGES trunk/debian/changelog * Described changes for 1.08.1 release. trunk/debian/control * New maintainer: Scott Kitterman * Priority: extra (was: optional) * Removed Build-Depends-Indep: perl, as there really is no need for it. * Depends: libversion-perl trunk/debian/copyright * Updated for 1.08.1 release. --- CHANGES | 16 ++++ INSTALL | 3 +- README | 7 +- debian/changelog | 18 +++++ debian/control | 7 +- debian/copyright | 9 ++- postfix-policyd-spf-perl | 158 ++++++++++++++++++++------------------- 7 files changed, 130 insertions(+), 88 deletions(-) diff --git a/CHANGES b/CHANGES index 25ddd84..f4d352a 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,22 @@ # ! = Changed something significant, or removed a feature # * = Fixed a bug, or made a minor improvement +--- 1.08.1 (2007-01-10 21:00) + + postfix-policyd-spf-perl: + * Minor and purely cosmetic code clean-up. + + Miscellaneous: + * Updated README file with new website URL and copyright. + * Added LICENSE file as an explicit copy of the GPLv2. + + Debian: + * New maintainer: Scott Kitterman + * Priority: extra (was: optional) + * Removed Build-Depends-Indep: perl, as there really is no need for it. + * Depends: libversion-perl + * Updated debian/copyright. + --- 1.08 (2006-06-17 20:00) * Added Debian package control files. diff --git a/INSTALL b/INSTALL index 1f057e9..cbdd598 100644 --- a/INSTALL +++ b/INSTALL @@ -5,6 +5,7 @@ The following Perl version and packages are required for running postfix-policyd-spf-perl: Perl 5.6 + version Mail::SPF::Query Installing @@ -30,5 +31,5 @@ Installing 4. Restart Postfix. -# $Id: README 167 2005-01-17 18:26:45Z julian $ +# $Id$ # vim:tw=79 diff --git a/README b/README index 98a0e71..7f9c403 100644 --- a/README +++ b/README @@ -1,8 +1,9 @@ -postfix-policyd-spf-perl 1.08 +postfix-policyd-spf-perl 1.08.1 A Postfix SMTPd policy server for SPF checking -(C) 2003-2004 Meng Weng Wong +(C) 2007 Scott Kitterman + 2003-2004 Meng Weng Wong Contributions by various members of the SPF project - + ============================================================================== postfix-policyd-spf-perl is a Postfix SMTPd policy daemon for SPF checking. diff --git a/debian/changelog b/debian/changelog index 63b4f20..364aa3a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,21 @@ +postfix-policyd-spf-perl (1.08.1) unstable; urgency=low + + Debian: + * New maintainer: Scott Kitterman + * Priority: extra (was: optional) + * Removed Build-Depends-Indep: perl, as there really is no need for it. + * Depends: libversion-perl + * Updated debian/copyright. + + postfix-policyd-spf-perl: + * Minor and purely cosmetic code clean-up. + + Miscellaneous: + * Updated README file with new website URL and copyright. + * Added LICENSE file as an explicit copy of the GPLv2. + + -- Scott Kitterman Wed, 10 Jan 2007 21:00:00 +0000 + postfix-policyd-spf-perl (1.08) unstable; urgency=low * Initial release as a Debian package. diff --git a/debian/control b/debian/control index f1c9337..00a6fca 100644 --- a/debian/control +++ b/debian/control @@ -1,14 +1,13 @@ Source: postfix-policyd-spf-perl Section: mail -Priority: optional -Maintainer: Julian Mehnle +Priority: extra +Maintainer: Scott Kitterman Build-Depends: debhelper (>= 5) -Build-Depends-Indep: perl (>= 5.6) Standards-Version: 3.7.2 Package: postfix-policyd-spf-perl Architecture: all -Depends: libmail-spf-query-perl +Depends: libversion-perl, libmail-spf-query-perl Recommends: postfix Description: pure-Perl Postfix policy daemon for SPF checking postfix-policyd-spf-perl is a Postfix SMTPd policy daemon for SPF checking. diff --git a/debian/copyright b/debian/copyright index 2f5d76e..7b59635 100644 --- a/debian/copyright +++ b/debian/copyright @@ -1,13 +1,14 @@ This is the Debian package for postfix-policyd-spf-perl, which is available -from . +from . -(C) 2003-2004 Meng Weng Wong +(C) 2007 Scott Kitterman + 2003-2004 Meng Weng Wong Contributions by various members of the SPF project -Julian Mehnle is the maintainer of the Debian package. +Scott Kitterman is the maintainer of the Debian package. This is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License (version 2 or later). On Debian systems, the complete text of the GPL v2 can be found here: -/usr/share/common-licenses/GPL-2 + /usr/share/common-licenses/GPL-2 diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index 1d5f4bb..b31eddf 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -3,9 +3,9 @@ # postfix-policyd-spf-perl # http://www.openspf.org/source/software/postfix-policyd-spf-perl/ # version 1.08 -# $Id: postfix-policyd-spf 147 2006-03-14 21:51:58Z julian $ +# $Id$ -our $VERSION = '1.08'; +use version; our $VERSION = qv('1.08.1'); use strict; @@ -44,10 +44,10 @@ my $syslog_ident = "postfix/policy-spf"; # Log an error and abort. # sub fatal_exit { - syslog(err => "fatal_exit: @_"); - syslog(warning => "fatal_exit: @_"); - syslog(info => "fatal_exit: @_"); - die "fatal: @_"; + syslog(err => "fatal_exit: @_"); + syslog(warning => "fatal_exit: @_"); + syslog(info => "fatal_exit: @_"); + die "fatal: @_"; } # @@ -71,92 +71,98 @@ openlog $syslog_ident, $syslog_options, $syslog_facility; # my %attr; while () { - chomp; - if (/=/) { my ($k, $v) = split (/=/, $_, 2); $attr{$k} = $v; next } - elsif (length) { syslog(warning => sprintf("warning: ignoring garbage: %.100s", $_)); next; } - - if ($VERBOSE) { - for (sort keys %attr) { - syslog(debug => "Attribute: %s=%s", $_, $attr{$_}); + chomp; + if (/=/) { my ($k, $v) = split (/=/, $_, 2); $attr{$k} = $v; next } + elsif (length) { syslog(warning => sprintf("warning: ignoring garbage: %.100s", $_)); next; } + + if ($VERBOSE) { + for (sort keys %attr) { + syslog(debug => "Attribute: %s=%s", $_, $attr{$_}); + } } - } - - fatal_exit ("unrecognized request type: '$attr{request}'") unless $attr{request} eq "smtpd_access_policy"; - - my $action = $DEFAULT_RESPONSE; - my %responses; - foreach my $handler (@HANDLERS) { - no strict 'refs'; - my $response = $handler->(attr=>\%attr); - syslog(debug => "handler %s: %s", $handler, $response); - if ($response and $response !~ /^dunno/i) { - syslog(info => "handler %s: %s is decisive.", $handler, $response); - $action = $response; last; + + fatal_exit("unrecognized request type: '$attr{request}'") unless $attr{request} eq "smtpd_access_policy"; + + my $action = $DEFAULT_RESPONSE; + my %responses; + foreach my $handler (@HANDLERS) { + no strict 'refs'; + my $response = $handler->(attr=>\%attr); + syslog(debug => "handler %s: %s", $handler, $response); + if ($response and $response !~ /^dunno/i) { + syslog(info => "handler %s: %s is decisive.", $handler, $response); + $action = $response; last; + } } - } - - syslog(info => "decided action=%s", $action); - - print STDOUT "action=$action\n\n"; - %attr = (); + + syslog(info => "decided action=%s", $action); + + print STDOUT "action=$action\n\n"; + %attr = (); } # ---------------------------------------------------------- # plugin: SPF # ---------------------------------------------------------- sub sender_permitted_from { - local %_ = @_; - my %attr = %{ $_{attr} }; - - my $query = eval { new Mail::SPF::Query (ip =>$attr{client_address}, - sender=>$attr{sender}, - helo =>$attr{helo_name}) }; - if ($@) { - syslog(info => "%s: Mail::SPF::Query->new(%s, %s, %s) failed: %s", - $attr{queue_id}, $attr{client_address}, $attr{sender}, $attr{helo_name}, $@); - return "DUNNO"; - } - my ($result, $smtp_comment, $header_comment) = $query->result(); - - syslog(info => "%s: SPF %s: smtp_comment=%s, header_comment=%s", - $attr{queue_id}, $result, $smtp_comment, $header_comment); - - if ($result eq "fail") { return "REJECT $smtp_comment"; } - elsif ($result eq "error") { return "DEFER_IF_PERMIT $smtp_comment"; } - else { return "PREPEND Received-SPF: $result ($header_comment)"; } + local %_ = @_; + my %attr = %{ $_{attr} }; + + my $query = eval { + Mail::SPF::Query->new( + ip => $attr{client_address}, + sender => $attr{sender}, + helo => $attr{helo_name} + ) + }; + if ($@) { + syslog( + info => "%s: Mail::SPF::Query->new(%s, %s, %s) failed: %s", + $attr{queue_id}, $attr{client_address}, $attr{sender}, $attr{helo_name}, $@ + ); + return "DUNNO"; + } + my ($result, $smtp_comment, $header_comment) = $query->result(); + + syslog( + info => "%s: SPF %s: smtp_comment=%s, header_comment=%s", + $attr{queue_id}, $result, $smtp_comment, $header_comment + ); + + if ($result eq "fail") { return "REJECT $smtp_comment"; } + elsif ($result eq "error") { return "DEFER_IF_PERMIT $smtp_comment"; } + else { return "PREPEND Received-SPF: $result ($header_comment)"; } } # ---------------------------------------------------------- # plugin: testing # ---------------------------------------------------------- sub testing { - local %_ = @_; - my %attr = %{ $_{attr} }; - - if (lc address_stripped($attr{sender}) eq - lc address_stripped($attr{recipient}) - and - $attr{recipient} =~ /policyblock/) { - - syslog(info => "%s: testing: will block as requested", $attr{queue_id}); - return "REJECT smtpd-policy blocking $attr{recipient}"; - } - else { - syslog(info => "%s: testing: stripped sender=%s, stripped rcpt=%s", - $attr{queue_id}, - address_stripped($attr{sender}), - address_stripped($attr{recipient}), - ); + local %_ = @_; + my %attr = %{ $_{attr} }; - } - return "DUNNO"; + if ( + lc(address_stripped($attr{sender})) eq lc(address_stripped($attr{recipient})) and + $attr{recipient} =~ /policyblock/ + ) { + + syslog(info => "%s: testing: will block as requested", $attr{queue_id}); + return "REJECT smtpd-policy blocking $attr{recipient}"; + } + else { + syslog( + info => "%s: testing: stripped sender=%s, stripped rcpt=%s", + $attr{queue_id}, + address_stripped($attr{sender}), + address_stripped($attr{recipient}), + ); + } + return "DUNNO"; } +# my $foo = address_stripped('foo+bar@baz.com'); # returns 'foo@baz.com' sub address_stripped { - # my $foo = localpart_lhs('foo+bar@baz.com'); # returns 'foo@baz.com' - my $string = shift; - for ($string) { - s/[+-].*\@/\@/; - } - return $string; + my $string = shift; + $string =~ s/[+-].*\@/\@/; + return $string; } From dcb2c8e8c4332dded63a498a48de5768e7241caa Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Wed, 10 Jan 2007 20:24:53 +0000 Subject: [PATCH 12/87] --- postfix-policyd-spf-perl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index b31eddf..e442935 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -1,8 +1,8 @@ #!/usr/bin/perl # postfix-policyd-spf-perl -# http://www.openspf.org/source/software/postfix-policyd-spf-perl/ -# version 1.08 +# http://www.openspf.org/Software +# version 1.08.1 # $Id$ use version; our $VERSION = qv('1.08.1'); From e81f28bf984af0005217a653d0b960bda2688a0d Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Sat, 13 Jan 2007 15:57:32 +0000 Subject: [PATCH 13/87] Fix README typo --- README | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README b/README index 7f9c403..aa11c01 100644 --- a/README +++ b/README @@ -25,9 +25,9 @@ default, a Postfix SMTP server process terminates after 100 seconds of idle time, or after serving 100 clients. Thus, the cost of starting this Perl script is smoothed out over time. -The default policy_time_limit is 1000 seconds.  This may be to short for some -SMTP transactions to complete.  As recommended in SMTPD_POLICY_README, this -should be extended to 3600 seconds.  To do so, set "policy_time_limit = 3600" +The default policy_time_limit is 1000 seconds. This may be too short for some +SMTP transactions to complete. As recommended in SMTPD_POLICY_README, this +should be extended to 3600 seconds. To do so, set "policy_time_limit = 3600" in /etc/postfix/main.cf. Testing the policy daemon From 1d57e605d3195e1f9d4dff5eeae30f45d804da23 Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Sat, 3 Feb 2007 21:46:00 +0000 Subject: [PATCH 14/87] Trunk changed for mail:SPF. 1.08.1/debian tags updated for Ubuntu lessons learned. --- CHANGES | 15 +++++ INSTALL | 4 +- README | 20 ++++-- debian/README.Debian | 4 +- debian/changelog | 30 ++++----- debian/control | 6 +- debian/copyright | 75 +++++++++++++++++++-- debian/rules | 11 +-- postfix-policyd-spf-perl | 141 +++++++++++++++++++++++---------------- 9 files changed, 204 insertions(+), 102 deletions(-) diff --git a/CHANGES b/CHANGES index f4d352a..d7c2d20 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,21 @@ # ! = Changed something significant, or removed a feature # * = Fixed a bug, or made a minor improvement +--- 1.99 (2007-02-10 16:00) + + postfix-policyd-spf-perl: + ! Changed from Mail::SPF::Query to Mail::SPF for RFC 4408 compliance + ! Removed Testing handler (usage was undocumented). + * Simplified logging. Policy server is less chatty. Logs are clearer. + + Miscellaneous: + * Updated README file: + - Updated for new SPF library + - Added more detail on results/responses + - Explained how to get verbose logging + + Debian: + --- 1.08.1 (2007-01-10 21:00) postfix-policyd-spf-perl: diff --git a/INSTALL b/INSTALL index cbdd598..b3efa2a 100644 --- a/INSTALL +++ b/INSTALL @@ -6,7 +6,7 @@ postfix-policyd-spf-perl: Perl 5.6 version - Mail::SPF::Query + Mail::SPF (not Mail::SPF::Query) Installing ---------- @@ -31,5 +31,3 @@ Installing 4. Restart Postfix. -# $Id$ -# vim:tw=79 diff --git a/README b/README index aa11c01..dbf722a 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -postfix-policyd-spf-perl 1.08.1 +postfix-policyd-spf-perl 1.99 A Postfix SMTPd policy server for SPF checking (C) 2007 Scott Kitterman 2003-2004 Meng Weng Wong @@ -7,7 +7,19 @@ Contributions by various members of the SPF project ============================================================================== postfix-policyd-spf-perl is a Postfix SMTPd policy daemon for SPF checking. -It is implemented in pure Perl and uses the Mail::SPF::Query CPAN module. +It is implemented in pure Perl and uses the Mail::SPF CPAN module. Note that +Mail::SPF is a complete re-implementation of SPF based on the final SPF RFC, +RFC 4408. It shares no code with the older Mail::SPF::Query that was the +original SPF development implementation. If you are upgrading from on older +version of this policy server you will need to install Mail::SPF. + +This version of the policy server will reject mail that fails either Mail From +or HELO SPF checks. It always checks HELO (older versions just checked HELO if +Mail From was null). It will defer mail if there is a temporary SPF error and +the message would othersise be permitted (DEFER_IF_PERMIT). Otherwise, it will +PREPEND the appropriate SPF Received header. In the case of multi-recipient +mail, multiple headers will get appended. Error conditions within the policy +server (that don't result in a crash) or from Mail::SPF will return DUNNO. See INSTALL or README.Debian for installation instructions. @@ -57,11 +69,11 @@ followed by a empty line: action=dunno [empty line] +If you want more detail in the system logs change $VERBOSE to 1. + License ------- postfix-policyd-spf-perl is free software. You may use, modify, and distribute it under the GNU GPL (version 2 or later); see the LICENSE file. -# $Id$ -# vim:tw=79 diff --git a/debian/README.Debian b/debian/README.Debian index af1060e..da19cd9 100644 --- a/debian/README.Debian +++ b/debian/README.Debian @@ -4,7 +4,7 @@ Installing 1. Add the following to /etc/postfix/master.cf: policy unix - n n - - spawn - user=nobody argv=/usr/bin/perl /usr/lib/postfix/policyd-spf-perl + user=nobody argv=/usr/bin/perl /usr/sbin/postfix-policyd-spf-perl 2. Configure the Postfix policy service in /etc/postfix/main.cf: @@ -19,5 +19,3 @@ Installing 3. Restart Postfix. -# $Id: README 167 2005-01-17 18:26:45Z julian $ -# vim:tw=79 diff --git a/debian/changelog b/debian/changelog index 364aa3a..768f3ae 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,23 +1,17 @@ -postfix-policyd-spf-perl (1.08.1) unstable; urgency=low +postfix-policyd-spf-perl (1.99-0ubuntu1) feisty; urgency=low - Debian: - * New maintainer: Scott Kitterman - * Priority: extra (was: optional) - * Removed Build-Depends-Indep: perl, as there really is no need for it. - * Depends: libversion-perl - * Updated debian/copyright. + * New upstream release for RFC compliant SPF checking. + * Updated control to use libmail-spf-perl instead of the unmaintained + libmail-spf-query-perl and changed maintainer to Scott Kitterman (was + MOTU). + * Updated man pages for new library and upstream documentation udpates. - postfix-policyd-spf-perl: - * Minor and purely cosmetic code clean-up. + -- Scott Kitterman Sat, 03 Feb 2007 16:33:08 -0500 - Miscellaneous: - * Updated README file with new website URL and copyright. - * Added LICENSE file as an explicit copy of the GPLv2. +postfix-policyd-spf-perl (1.08.1-0ubuntu1) feisty; urgency=low - -- Scott Kitterman Wed, 10 Jan 2007 21:00:00 +0000 + * Initial debianization of the package. + * Removed upstream provided /debian directory + * Modified documentation for Debian specific file locations -postfix-policyd-spf-perl (1.08) unstable; urgency=low - - * Initial release as a Debian package. - - -- Julian Mehnle Sat, 17 Jun 2006 19:32:31 +0000 + -- Scott Kitterman Thu, 11 Jan 2007 04:59:08 -0500 diff --git a/debian/control b/debian/control index 00a6fca..49d0cea 100644 --- a/debian/control +++ b/debian/control @@ -7,8 +7,10 @@ Standards-Version: 3.7.2 Package: postfix-policyd-spf-perl Architecture: all -Depends: libversion-perl, libmail-spf-query-perl +Depends: libversion-perl, libmail-spf-perl, ${perl:Depends} Recommends: postfix Description: pure-Perl Postfix policy daemon for SPF checking postfix-policyd-spf-perl is a Postfix SMTPd policy daemon for SPF checking. - It is implemented in pure Perl and uses the Mail::SPF::Query module. + It is implemented in pure Perl and uses the Mail::SPF module. The SPF web + site is http://www.openspf.org/. The Postfix configuration must be changed to + check SPF. See README.Debian or man 8 postfix-policyd-spf-perl for details. diff --git a/debian/copyright b/debian/copyright index 7b59635..3a5ecdc 100644 --- a/debian/copyright +++ b/debian/copyright @@ -1,14 +1,77 @@ -This is the Debian package for postfix-policyd-spf-perl, which is available -from . +This package was debianized by Scott Kitterman on +Thu, 11 Jan 2007 04:29:13 -0500. + +It was downloaded from . + +Upstream authors Meng Weng Wong and Scott Kitterman + + +Copyright: (C) 2007 Scott Kitterman - 2003-2004 Meng Weng Wong -Contributions by various members of the SPF project - -Scott Kitterman is the maintainer of the Debian package. +(C) 2003-2004 Meng Weng Wong This is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License (version 2 or later). + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + On Debian systems, the complete text of the GPL v2 can be found here: /usr/share/common-licenses/GPL-2 + diff --git a/debian/rules b/debian/rules index 0c49c0f..9bf0142 100755 --- a/debian/rules +++ b/debian/rules @@ -22,8 +22,7 @@ install-stamp: dh_testroot dh_clean -k - install -D postfix-policyd-spf-perl $(TMP)/usr/lib/postfix/policyd-spf-perl - + install -D postfix-policyd-spf-perl $(TMP)/usr/sbin/postfix-policyd-spf-perl touch install-stamp # Build architecture-independent files here: @@ -31,13 +30,9 @@ binary-indep: build install dh_testdir dh_testroot - dh_install - dh_installdirs - dh_installdocs README + dh_installdocs + dh_installman debian/postfix-policyd-spf-perl.8 dh_installchangelogs CHANGES - #dh_installexamples examples/* - #dh_installman - #dh_link dh_compress dh_fixperms diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index e442935..24f739b 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -2,24 +2,40 @@ # postfix-policyd-spf-perl # http://www.openspf.org/Software -# version 1.08.1 +# version 1.99 # $Id$ +#(C) 2007 Scott Kitterman +#(C) 2003-2004 Meng Weng Wong +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -use version; our $VERSION = qv('1.08.1'); +use version; our $VERSION = qv('1.99'); use strict; use Fcntl; use Sys::Syslog qw(:DEFAULT setlogsock); -use Mail::SPF::Query; +use Mail::SPF; # ---------------------------------------------------------- # configuration # ---------------------------------------------------------- - +my $spf_server = Mail::SPF::Server->new(); my @HANDLERS; -push @HANDLERS, "testing"; -push @HANDLERS, "sender_permitted_from"; +push @HANDLERS, "sender_policy_framework"; +#Leaving this to make it easier to add others later. my $VERBOSE = 0; @@ -88,14 +104,17 @@ while () { foreach my $handler (@HANDLERS) { no strict 'refs'; my $response = $handler->(attr=>\%attr); - syslog(debug => "handler %s: %s", $handler, $response); + if ($VERBOSE) { + syslog(debug => "handler %s: %s", $handler, $response); + } + #Picks whatever response is not dunno if ($response and $response !~ /^dunno/i) { - syslog(info => "handler %s: %s is decisive.", $handler, $response); + syslog(info => "handler %s: is decisive.", $handler); $action = $response; last; } } - syslog(info => "decided action=%s", $action); + syslog(info => "Policy action=%s", $action); print STDOUT "action=$action\n\n"; %attr = (); @@ -104,65 +123,71 @@ while () { # ---------------------------------------------------------- # plugin: SPF # ---------------------------------------------------------- -sub sender_permitted_from { +sub sender_policy_framework { local %_ = @_; my %attr = %{ $_{attr} }; - - my $query = eval { - Mail::SPF::Query->new( - ip => $attr{client_address}, - sender => $attr{sender}, - helo => $attr{helo_name} - ) + + #Always do HELO check first. If no HELO policy it's only one lookup. + #Avoids the need to do any Mail From processing for null sender. + my $helo_request = eval { + Mail::SPF::Request->new( + scope => 'helo', # 'mfrom' or 'helo', 'pra' + identity => $attr{helo_name}, + ip_address => $attr{client_address}, + helo_identity # optional, + => $attr{helo_name} + ); }; + my $helo_result = $spf_server->process($helo_request); + + my $helo_result_code = $helo_result->code; # 'pass', 'fail', etc. + my $helo_local_exp = $helo_result->local_explanation; + my $helo_authority_exp = $helo_result->authority_explanation + if $helo_result->is_code('fail'); + my $helo_spf_header = $helo_result->received_spf_header; + + syslog( + info => "%s: SPF %s: HELO/EHLO: %s, IP Address: %s, Recipient: %s", + $attr{queue_id}, $helo_result, $attr{helo_name}, $attr{client_address}, $attr{recipient} + ); + + # Reject on HELO fail. Defer on HELO temperror if message would otherwis + # be accepted. Use the HELO result and return for null sender. + if ($helo_result_code eq "fail") { return "REJECT $helo_authority_exp"; } + elsif ($helo_result_code eq "temperror") { return "DEFER_IF_PERMIT SPF-Result=$helo_local_exp"; } + elsif ($attr{sender} eq '') { return "PREPEND $helo_spf_header"; } + + #Do mail from is HELO doesn't give a definitive result. + my $mfrom_request = eval { + Mail::SPF::Request->new( + scope => 'mfrom', # 'mfrom' or 'helo', 'pra' + identity => $attr{sender}, + ip_address => $attr{client_address}, + helo_identity # optional, + => $attr{helo_name} # for %{h} macro expansion + ); + }; + my $mfrom_result = $spf_server->process($mfrom_request); + + my $mfrom_result_code = $mfrom_result->code; # 'pass', 'fail', etc. + my $mfrom_local_exp = $mfrom_result->local_explanation; + my $mfrom_authority_exp = $mfrom_result->authority_explanation + if $mfrom_result->is_code('fail'); + my $mfrom_spf_header = $mfrom_result->received_spf_header; if ($@) { syslog( - info => "%s: Mail::SPF::Query->new(%s, %s, %s) failed: %s", + info => "%s: Mail::SPF->new(%s, %s, %s) failed: %s", $attr{queue_id}, $attr{client_address}, $attr{sender}, $attr{helo_name}, $@ ); return "DUNNO"; } - my ($result, $smtp_comment, $header_comment) = $query->result(); - syslog( - info => "%s: SPF %s: smtp_comment=%s, header_comment=%s", - $attr{queue_id}, $result, $smtp_comment, $header_comment + info => "%s: SPF %s: Envelope-from: %s, IP Address: %s, Recipient: %s", + $attr{queue_id}, $mfrom_result, $attr{sender}, $attr{client_address}, $attr{recipient} ); - if ($result eq "fail") { return "REJECT $smtp_comment"; } - elsif ($result eq "error") { return "DEFER_IF_PERMIT $smtp_comment"; } - else { return "PREPEND Received-SPF: $result ($header_comment)"; } -} - -# ---------------------------------------------------------- -# plugin: testing -# ---------------------------------------------------------- -sub testing { - local %_ = @_; - my %attr = %{ $_{attr} }; - - if ( - lc(address_stripped($attr{sender})) eq lc(address_stripped($attr{recipient})) and - $attr{recipient} =~ /policyblock/ - ) { - - syslog(info => "%s: testing: will block as requested", $attr{queue_id}); - return "REJECT smtpd-policy blocking $attr{recipient}"; - } - else { - syslog( - info => "%s: testing: stripped sender=%s, stripped rcpt=%s", - $attr{queue_id}, - address_stripped($attr{sender}), - address_stripped($attr{recipient}), - ); - } - return "DUNNO"; -} - -# my $foo = address_stripped('foo+bar@baz.com'); # returns 'foo@baz.com' -sub address_stripped { - my $string = shift; - $string =~ s/[+-].*\@/\@/; - return $string; + #Same approach as HELO.... + if ($mfrom_result_code eq "fail") { return "REJECT $mfrom_authority_exp"; } + elsif ($mfrom_result_code eq "mfrom_temperror") { return "DEFER_IF_PERMIT SPF-Result=$mfrom_local_exp"; } + else { return "PREPEND $mfrom_spf_header"; } } From 9c264c38671a26c9b3d497f17036182b4edf84b2 Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Sun, 4 Feb 2007 02:25:39 +0000 Subject: [PATCH 15/87] Correct error trapping so policy server won't crash on bad input. --- CHANGES | 10 +---- INSTALL | 2 +- README | 2 +- debian/changelog | 2 +- postfix-policyd-spf-perl | 91 ++++++++++++++++++++++------------------ 5 files changed, 54 insertions(+), 53 deletions(-) diff --git a/CHANGES b/CHANGES index d7c2d20..622c5e4 100644 --- a/CHANGES +++ b/CHANGES @@ -4,21 +4,13 @@ # ! = Changed something significant, or removed a feature # * = Fixed a bug, or made a minor improvement ---- 1.99 (2007-02-10 16:00) +--- 1.990 (2007-02-10 16:00) postfix-policyd-spf-perl: ! Changed from Mail::SPF::Query to Mail::SPF for RFC 4408 compliance ! Removed Testing handler (usage was undocumented). * Simplified logging. Policy server is less chatty. Logs are clearer. - Miscellaneous: - * Updated README file: - - Updated for new SPF library - - Added more detail on results/responses - - Explained how to get verbose logging - - Debian: - --- 1.08.1 (2007-01-10 21:00) postfix-policyd-spf-perl: diff --git a/INSTALL b/INSTALL index b3efa2a..86a8e5c 100644 --- a/INSTALL +++ b/INSTALL @@ -6,7 +6,7 @@ postfix-policyd-spf-perl: Perl 5.6 version - Mail::SPF (not Mail::SPF::Query) + Mail-SPF (not Mail-SPF-Query) Installing ---------- diff --git a/README b/README index dbf722a..cabef8b 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -postfix-policyd-spf-perl 1.99 +postfix-policyd-spf-perl 1.990 A Postfix SMTPd policy server for SPF checking (C) 2007 Scott Kitterman 2003-2004 Meng Weng Wong diff --git a/debian/changelog b/debian/changelog index 768f3ae..3b23795 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -postfix-policyd-spf-perl (1.99-0ubuntu1) feisty; urgency=low +postfix-policyd-spf-perl (1.990-0ubuntu1) feisty; urgency=low * New upstream release for RFC compliant SPF checking. * Updated control to use libmail-spf-perl instead of the unmaintained diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index 24f739b..61bbb29 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -2,7 +2,7 @@ # postfix-policyd-spf-perl # http://www.openspf.org/Software -# version 1.99 +# version 1.990 # $Id$ #(C) 2007 Scott Kitterman #(C) 2003-2004 Meng Weng Wong @@ -21,7 +21,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -use version; our $VERSION = qv('1.99'); +use version; our $VERSION = qv('1.990'); use strict; @@ -96,9 +96,7 @@ while () { syslog(debug => "Attribute: %s=%s", $_, $attr{$_}); } } - - fatal_exit("unrecognized request type: '$attr{request}'") unless $attr{request} eq "smtpd_access_policy"; - + my $action = $DEFAULT_RESPONSE; my %responses; foreach my $handler (@HANDLERS) { @@ -107,7 +105,7 @@ while () { if ($VERBOSE) { syslog(debug => "handler %s: %s", $handler, $response); } - #Picks whatever response is not dunno + # Picks whatever response is not dunno if ($response and $response !~ /^dunno/i) { syslog(info => "handler %s: is decisive.", $handler); $action = $response; last; @@ -127,8 +125,8 @@ sub sender_policy_framework { local %_ = @_; my %attr = %{ $_{attr} }; - #Always do HELO check first. If no HELO policy it's only one lookup. - #Avoids the need to do any Mail From processing for null sender. + # Always do HELO check first. If no HELO policy it's only one lookup. + # Avoids the need to do any Mail From processing for null sender. my $helo_request = eval { Mail::SPF::Request->new( scope => 'helo', # 'mfrom' or 'helo', 'pra' @@ -138,26 +136,35 @@ sub sender_policy_framework { => $attr{helo_name} ); }; - my $helo_result = $spf_server->process($helo_request); + # If initializing helo_request throws an error, don't use it. + if ($@) { + syslog( + info => "%s: Mail::SPF->new(%s, %s, %s) failed: %s", + $attr{queue_id}, $attr{client_address}, $attr{sender}, $attr{helo_name}, $@ + ); + return "DUNNO"; + } + else { + my $helo_result = $spf_server->process($helo_request); - my $helo_result_code = $helo_result->code; # 'pass', 'fail', etc. - my $helo_local_exp = $helo_result->local_explanation; - my $helo_authority_exp = $helo_result->authority_explanation - if $helo_result->is_code('fail'); - my $helo_spf_header = $helo_result->received_spf_header; + my $helo_result_code = $helo_result->code; # 'pass', 'fail', etc. + my $helo_local_exp = $helo_result->local_explanation; + my $helo_authority_exp = $helo_result->authority_explanation + if $helo_result->is_code('fail'); + my $helo_spf_header = $helo_result->received_spf_header; - syslog( - info => "%s: SPF %s: HELO/EHLO: %s, IP Address: %s, Recipient: %s", - $attr{queue_id}, $helo_result, $attr{helo_name}, $attr{client_address}, $attr{recipient} - ); + syslog( + info => "%s: SPF %s: HELO/EHLO: %s, IP Address: %s, Recipient: %s", + $attr{queue_id}, $helo_result, $attr{helo_name}, $attr{client_address}, $attr{recipient} + ); - # Reject on HELO fail. Defer on HELO temperror if message would otherwis - # be accepted. Use the HELO result and return for null sender. - if ($helo_result_code eq "fail") { return "REJECT $helo_authority_exp"; } - elsif ($helo_result_code eq "temperror") { return "DEFER_IF_PERMIT SPF-Result=$helo_local_exp"; } - elsif ($attr{sender} eq '') { return "PREPEND $helo_spf_header"; } - - #Do mail from is HELO doesn't give a definitive result. + # Reject on HELO fail. Defer on HELO temperror if message would otherwis + # be accepted. Use the HELO result and return for null sender. + if ($helo_result_code eq "fail") { return "REJECT $helo_authority_exp"; } + elsif ($helo_result_code eq "temperror") { return "DEFER_IF_PERMIT SPF-Result=$helo_local_exp"; } + elsif ($attr{sender} eq '') { return "PREPEND $helo_spf_header"; } + }; + # Do mail from is HELO doesn't give a definitive result. my $mfrom_request = eval { Mail::SPF::Request->new( scope => 'mfrom', # 'mfrom' or 'helo', 'pra' @@ -167,27 +174,29 @@ sub sender_policy_framework { => $attr{helo_name} # for %{h} macro expansion ); }; - my $mfrom_result = $spf_server->process($mfrom_request); - - my $mfrom_result_code = $mfrom_result->code; # 'pass', 'fail', etc. - my $mfrom_local_exp = $mfrom_result->local_explanation; - my $mfrom_authority_exp = $mfrom_result->authority_explanation - if $mfrom_result->is_code('fail'); - my $mfrom_spf_header = $mfrom_result->received_spf_header; if ($@) { syslog( info => "%s: Mail::SPF->new(%s, %s, %s) failed: %s", $attr{queue_id}, $attr{client_address}, $attr{sender}, $attr{helo_name}, $@ ); return "DUNNO"; - } - syslog( - info => "%s: SPF %s: Envelope-from: %s, IP Address: %s, Recipient: %s", - $attr{queue_id}, $mfrom_result, $attr{sender}, $attr{client_address}, $attr{recipient} - ); + } + else { + my $mfrom_result = $spf_server->process($mfrom_request); + + my $mfrom_result_code = $mfrom_result->code; # 'pass', 'fail', etc. + my $mfrom_local_exp = $mfrom_result->local_explanation; + my $mfrom_authority_exp = $mfrom_result->authority_explanation + if $mfrom_result->is_code('fail'); + my $mfrom_spf_header = $mfrom_result->received_spf_header; + syslog( + info => "%s: SPF %s: Envelope-from: %s, IP Address: %s, Recipient: %s", + $attr{queue_id}, $mfrom_result, $attr{sender}, $attr{client_address}, $attr{recipient} + ); - #Same approach as HELO.... - if ($mfrom_result_code eq "fail") { return "REJECT $mfrom_authority_exp"; } - elsif ($mfrom_result_code eq "mfrom_temperror") { return "DEFER_IF_PERMIT SPF-Result=$mfrom_local_exp"; } - else { return "PREPEND $mfrom_spf_header"; } + # Same approach as HELO.... + if ($mfrom_result_code eq "fail") { return "REJECT $mfrom_authority_exp"; } + elsif ($mfrom_result_code eq "mfrom_temperror") { return "DEFER_IF_PERMIT SPF-Result=$mfrom_local_exp"; } + else { return "PREPEND $mfrom_spf_header"; } + } } From da0f7279b62e75dc6af0eb466f71aed6c43729d9 Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Sun, 4 Feb 2007 02:27:25 +0000 Subject: [PATCH 16/87] Added test_cases for offline testing - do not include in tagged release. --- test_cases | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 test_cases diff --git a/test_cases b/test_cases new file mode 100644 index 0000000..eb288bb --- /dev/null +++ b/test_cases @@ -0,0 +1,55 @@ +#HELO and mfrom pass +request=smtpd_access_policy +client_address=72.81.252.18 +helo_name=mailout02.controlledmail.com +sender=scott@kitterman.com +recipient=bogus@kitterman.org +queue_id=q1234 + +#HELO fail and mfrom pass +request=smtpd_access_policy +client_address=72.81.252.18 +helo_name=mailout00.controlledmail.com +sender=scott@kitterman.com +recipient=bogus@kitterman.org +queue_id=q1234 + +#no HELO and mfrom pass +request=smtpd_access_policy +client_address=72.81.252.18 +helo_name=mailout03.controlledmail.com +sender=scott@kitterman.com +recipient=bogus@kitterman.org +queue_id=q1234 + +#helo pass and mfrom fail +request=smtpd_access_policy +client_address=72.81.252.19 +helo_name=mailout00.controlledmail.com +sender=scott@kitterman.org +recipient=bogus@kitterman.org +queue_id=q1234 + +#helo pass and mfrom none +request=smtpd_access_policy +client_address=72.81.252.18 +helo_name=mailout02.controlledmail.com +sender=scott@yahoo.com +recipient=bogus@kitterman.org +queue_id=q1234 + +#helo pass and mfrom null +request=smtpd_access_policy +client_address=72.81.252.18 +helo_name=mailout02.controlledmail.com +sender= +recipient=bogus@kitterman.org +queue_id=q1234 + +#helo fail and mfrom null +request=smtpd_access_policy +client_address=72.81.252.19 +helo_name=mailout02.controlledmail.com +sender= +recipient=bogus@kitterman.org +queue_id=q1234 From 1af44573f75cb5c6097362a080fbf13710ec2575 Mon Sep 17 00:00:00 2001 From: Julian Mehnle <> Date: Sun, 4 Feb 2007 19:20:58 +0000 Subject: [PATCH 17/87] postfix-policyd-spf-perl/trunk/postfix-policyd-spf-perl * Medium clean-up. --- postfix-policyd-spf-perl | 176 +++++++++++++++++++++++---------------- 1 file changed, 102 insertions(+), 74 deletions(-) diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index 61bbb29..d1a1925 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -25,21 +25,23 @@ use version; our $VERSION = qv('1.990'); use strict; -use Fcntl; +use IO::Handle; use Sys::Syslog qw(:DEFAULT setlogsock); use Mail::SPF; # ---------------------------------------------------------- # configuration # ---------------------------------------------------------- + my $spf_server = Mail::SPF::Server->new(); + my @HANDLERS; -push @HANDLERS, "sender_policy_framework"; -#Leaving this to make it easier to add others later. +push(@HANDLERS, \&sender_policy_framework); + # Leaving this to make it easier to add others later. my $VERBOSE = 0; -my $DEFAULT_RESPONSE = "DUNNO"; +my $DEFAULT_RESPONSE = 'DUNNO'; # # Syslogging options for verbose mode and for fatal errors. @@ -48,9 +50,9 @@ my $DEFAULT_RESPONSE = "DUNNO"; # my $syslog_socktype = 'unix'; # inet, unix, stream, console -my $syslog_facility = "mail"; -my $syslog_options = "pid"; -my $syslog_ident = "postfix/policy-spf"; +my $syslog_facility = 'mail'; +my $syslog_options = 'pid'; +my $syslog_ident = 'postfix/policy-spf'; # ---------------------------------------------------------- # initialization @@ -63,20 +65,20 @@ sub fatal_exit { syslog(err => "fatal_exit: @_"); syslog(warning => "fatal_exit: @_"); syslog(info => "fatal_exit: @_"); - die "fatal: @_"; + die("fatal: @_"); } # # Unbuffer standard output. # -select((select(STDOUT), $| = 1)[0]); +STDOUT->autoflush(1); # # This process runs as a daemon, so it can't log to a terminal. Use # syslog so that people can actually see our messages. # -setlogsock $syslog_socktype; -openlog $syslog_ident, $syslog_options, $syslog_facility; +setlogsock($syslog_socktype); +openlog($syslog_ident, $syslog_options, $syslog_facility); # ---------------------------------------------------------- # main @@ -85,118 +87,144 @@ openlog $syslog_ident, $syslog_options, $syslog_facility; # # Receive a bunch of attributes, evaluate the policy, send the result. # -my %attr; while () { chomp; - if (/=/) { my ($k, $v) = split (/=/, $_, 2); $attr{$k} = $v; next } - elsif (length) { syslog(warning => sprintf("warning: ignoring garbage: %.100s", $_)); next; } + + my %attr; + if (/=/) { + my ($key, $value) =split (/=/, $_, 2); + $attr{$key} = $value; + next; + } + elsif (length) { + syslog(warning => sprintf("warning: ignoring garbage: %.100s", $_)); + next; + } if ($VERBOSE) { for (sort keys %attr) { syslog(debug => "Attribute: %s=%s", $_, $attr{$_}); } } - + my $action = $DEFAULT_RESPONSE; my %responses; foreach my $handler (@HANDLERS) { - no strict 'refs'; - my $response = $handler->(attr=>\%attr); + my $response = $handler->(attr => \%attr); + if ($VERBOSE) { syslog(debug => "handler %s: %s", $handler, $response); } + # Picks whatever response is not dunno if ($response and $response !~ /^dunno/i) { syslog(info => "handler %s: is decisive.", $handler); - $action = $response; last; + $action = $response; + last; } } syslog(info => "Policy action=%s", $action); - print STDOUT "action=$action\n\n"; - %attr = (); + STDOUT->print("action=$action\n\n"); } # ---------------------------------------------------------- # plugin: SPF # ---------------------------------------------------------- sub sender_policy_framework { - local %_ = @_; - my %attr = %{ $_{attr} }; + my %options = @_; + my $attr = $options{attr}; # Always do HELO check first. If no HELO policy it's only one lookup. # Avoids the need to do any Mail From processing for null sender. my $helo_request = eval { Mail::SPF::Request->new( - scope => 'helo', # 'mfrom' or 'helo', 'pra' - identity => $attr{helo_name}, - ip_address => $attr{client_address}, - helo_identity # optional, - => $attr{helo_name} + scope => 'helo', + identity => $attr->{helo_name}, + ip_address => $attr->{client_address} ); }; + # If initializing helo_request throws an error, don't use it. if ($@) { + my $errmsg = $@; + $errmsg = $errmsg->text if UNIVERSAL::isa($@, 'Mail::SPF::Exception'); syslog( info => "%s: Mail::SPF->new(%s, %s, %s) failed: %s", - $attr{queue_id}, $attr{client_address}, $attr{sender}, $attr{helo_name}, $@ - ); - return "DUNNO"; - } - else { - my $helo_result = $spf_server->process($helo_request); - - my $helo_result_code = $helo_result->code; # 'pass', 'fail', etc. - my $helo_local_exp = $helo_result->local_explanation; - my $helo_authority_exp = $helo_result->authority_explanation - if $helo_result->is_code('fail'); - my $helo_spf_header = $helo_result->received_spf_header; - - syslog( - info => "%s: SPF %s: HELO/EHLO: %s, IP Address: %s, Recipient: %s", - $attr{queue_id}, $helo_result, $attr{helo_name}, $attr{client_address}, $attr{recipient} - ); - - # Reject on HELO fail. Defer on HELO temperror if message would otherwis - # be accepted. Use the HELO result and return for null sender. - if ($helo_result_code eq "fail") { return "REJECT $helo_authority_exp"; } - elsif ($helo_result_code eq "temperror") { return "DEFER_IF_PERMIT SPF-Result=$helo_local_exp"; } - elsif ($attr{sender} eq '') { return "PREPEND $helo_spf_header"; } - }; - # Do mail from is HELO doesn't give a definitive result. - my $mfrom_request = eval { - Mail::SPF::Request->new( - scope => 'mfrom', # 'mfrom' or 'helo', 'pra' - identity => $attr{sender}, - ip_address => $attr{client_address}, - helo_identity # optional, - => $attr{helo_name} # for %{h} macro expansion - ); - }; - if ($@) { - syslog( - info => "%s: Mail::SPF->new(%s, %s, %s) failed: %s", - $attr{queue_id}, $attr{client_address}, $attr{sender}, $attr{helo_name}, $@ + $attr->{queue_id}, $attr->{client_address}, $attr->{sender}, $attr->{helo_name}, $errmsg ); return "DUNNO"; } else { - my $mfrom_result = $spf_server->process($mfrom_request); + my $helo_result = $spf_server->process($helo_request); + + my $helo_result_code = $helo_result->code; # 'pass', 'fail', etc. + my $helo_local_exp = $helo_result->local_explanation; + my $helo_authority_exp = $helo_result->authority_explanation + if $helo_result->is_code('fail'); + my $helo_spf_header = $helo_result->received_spf_header; - my $mfrom_result_code = $mfrom_result->code; # 'pass', 'fail', etc. - my $mfrom_local_exp = $mfrom_result->local_explanation; - my $mfrom_authority_exp = $mfrom_result->authority_explanation + syslog( + info => "%s: SPF %s: HELO/EHLO: %s, IP Address: %s, Recipient: %s", + $attr->{queue_id}, $helo_result, $attr->{helo_name}, $attr->{client_address}, $attr->{recipient} + ); + + # Reject on HELO fail. Defer on HELO temperror if message would otherwise + # be accepted. Use the HELO result and return for null sender. + if ($helo_result->is_code('fail')) { + return "REJECT $helo_authority_exp"; + } + elsif ($helo_result->is_code('temperror')) { + return "DEFER_IF_PERMIT SPF-Result=$helo_local_exp"; + } + elsif ($attr->{sender} eq '') { + return "PREPEND $helo_spf_header"; + } + } + + # Do mail from is HELO doesn't give a definitive result. + my $mfrom_request = eval { + Mail::SPF::Request->new( + scope => 'mfrom', + identity => $attr->{sender}, + ip_address => $attr->{client_address}, + helo_identity => $attr->{helo_name} # for %{h} macro expansion + ); + }; + + if ($@) { + my $errmsg = $@; + $errmsg = $errmsg->text if UNIVERSAL::isa($@, 'Mail::SPF::Exception'); + syslog( + info => "%s: Mail::SPF->new(%s, %s, %s) failed: %s", + $attr->{queue_id}, $attr->{client_address}, $attr->{sender}, $attr->{helo_name}, $errmsg + ); + return "DUNNO"; + } + else { + my $mfrom_result = $spf_server->process($mfrom_request); + + my $mfrom_result_code = $mfrom_result->code; # 'pass', 'fail', etc. + my $mfrom_local_exp = $mfrom_result->local_explanation; + my $mfrom_authority_exp = $mfrom_result->authority_explanation if $mfrom_result->is_code('fail'); - my $mfrom_spf_header = $mfrom_result->received_spf_header; + my $mfrom_spf_header = $mfrom_result->received_spf_header; + syslog( info => "%s: SPF %s: Envelope-from: %s, IP Address: %s, Recipient: %s", - $attr{queue_id}, $mfrom_result, $attr{sender}, $attr{client_address}, $attr{recipient} + $attr->{queue_id}, $mfrom_result, $attr->{sender}, $attr->{client_address}, $attr->{recipient} ); # Same approach as HELO.... - if ($mfrom_result_code eq "fail") { return "REJECT $mfrom_authority_exp"; } - elsif ($mfrom_result_code eq "mfrom_temperror") { return "DEFER_IF_PERMIT SPF-Result=$mfrom_local_exp"; } - else { return "PREPEND $mfrom_spf_header"; } + if ($mfrom_result->is_code('fail')) { + return "REJECT $mfrom_authority_exp"; + } + elsif ($mfrom_result->is_code('temperror')) { + return "DEFER_IF_PERMIT SPF-Result=$mfrom_local_exp"; + } + else { + return "PREPEND $mfrom_spf_header"; + } } } From 305b57ec148ba0e5456165279b1b6cb1ebed3f16 Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Sun, 4 Feb 2007 22:25:18 +0000 Subject: [PATCH 18/87] Logging and other code cleanup. --- postfix-policyd-spf-perl | 30 ++++++++++++++++++------------ test_cases | 1 + 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index d1a1925..229f961 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -87,10 +87,10 @@ openlog($syslog_ident, $syslog_options, $syslog_facility); # # Receive a bunch of attributes, evaluate the policy, send the result. # +my %attr; while () { chomp; - my %attr; if (/=/) { my ($key, $value) =split (/=/, $_, 2); $attr{$key} = $value; @@ -124,9 +124,10 @@ while () { } } - syslog(info => "Policy action=%s", $action); - + syslog(info => "%s: Policy action=%s", $attr{queue_id}, $action); + STDOUT->print("action=$action\n\n"); + %attr = (); } # ---------------------------------------------------------- @@ -164,11 +165,13 @@ sub sender_policy_framework { my $helo_authority_exp = $helo_result->authority_explanation if $helo_result->is_code('fail'); my $helo_spf_header = $helo_result->received_spf_header; - - syslog( - info => "%s: SPF %s: HELO/EHLO: %s, IP Address: %s, Recipient: %s", - $attr->{queue_id}, $helo_result, $attr->{helo_name}, $attr->{client_address}, $attr->{recipient} - ); + if ($VERBOSE) { + syslog( + info => "%s: SPF %s: HELO/EHLO: %s, IP Address: %s, Recipient: %s", + $attr->{queue_id}, $helo_result, $attr->{helo_name}, $attr->{client_address}, + $attr->{recipient} + ); + }; # Reject on HELO fail. Defer on HELO temperror if message would otherwise # be accepted. Use the HELO result and return for null sender. @@ -211,10 +214,13 @@ sub sender_policy_framework { if $mfrom_result->is_code('fail'); my $mfrom_spf_header = $mfrom_result->received_spf_header; - syslog( - info => "%s: SPF %s: Envelope-from: %s, IP Address: %s, Recipient: %s", - $attr->{queue_id}, $mfrom_result, $attr->{sender}, $attr->{client_address}, $attr->{recipient} - ); + if ($VERBOSE) { + syslog( + info => "%s: SPF %s: Envelope-from: %s, IP Address: %s, Recipient: %s", + $attr->{queue_id}, $mfrom_result, $attr->{sender}, $attr->{client_address}, + $attr->{recipient} + ); + }; # Same approach as HELO.... if ($mfrom_result->is_code('fail')) { diff --git a/test_cases b/test_cases index eb288bb..d0c1cf0 100644 --- a/test_cases +++ b/test_cases @@ -53,3 +53,4 @@ helo_name=mailout02.controlledmail.com sender= recipient=bogus@kitterman.org queue_id=q1234 + From 364bc8a75420cf033bcf5cf7958a87c908ae97e5 Mon Sep 17 00:00:00 2001 From: Julian Mehnle <> Date: Sun, 4 Feb 2007 22:36:52 +0000 Subject: [PATCH 19/87] postfix-policyd-spf-perl/trunk/postfix-policyd-spf-perl * Fixed handlers handling, keeping handler name and code separate, avoiding the use of symbolic references (i.e. the dual use of the handler name as both its name and its _function_ name). --- postfix-policyd-spf-perl | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index 229f961..8ad66e4 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -35,9 +35,13 @@ use Mail::SPF; my $spf_server = Mail::SPF::Server->new(); -my @HANDLERS; -push(@HANDLERS, \&sender_policy_framework); - # Leaving this to make it easier to add others later. +# Leaving this to make it easier to add more handlers later: +my @HANDLERS = ( + { + name => 'sender_policy_framework', + code => \&sender_policy_framework + } +); my $VERBOSE = 0; @@ -110,15 +114,18 @@ while () { my $action = $DEFAULT_RESPONSE; my %responses; foreach my $handler (@HANDLERS) { - my $response = $handler->(attr => \%attr); + my $handler_name = $handler->{name}; + my $handler_code = $handler->{code}; + + my $response = $handler_code->(attr => \%attr); if ($VERBOSE) { - syslog(debug => "handler %s: %s", $handler, $response); + syslog(debug => "handler %s: %s", $handler_name, $response); } # Picks whatever response is not dunno if ($response and $response !~ /^dunno/i) { - syslog(info => "handler %s: is decisive.", $handler); + syslog(info => "handler %s: is decisive.", $handler_name); $action = $response; last; } From 6af1839810178708f206e1593821b8495da04c24 Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Sun, 4 Feb 2007 22:45:50 +0000 Subject: [PATCH 20/87] Remove test cases from svn and add debian/docs and debian/postfix-policyd-spf-perl.8 --- debian/docs | 2 + debian/postfix-policyd-spf-perl.8 | 244 ++++++++++++++++++++++++++++++ test_cases | 56 ------- 3 files changed, 246 insertions(+), 56 deletions(-) create mode 100644 debian/docs create mode 100644 debian/postfix-policyd-spf-perl.8 delete mode 100644 test_cases diff --git a/debian/docs b/debian/docs new file mode 100644 index 0000000..22772e8 --- /dev/null +++ b/debian/docs @@ -0,0 +1,2 @@ +README +debian/INSTALL.Debian diff --git a/debian/postfix-policyd-spf-perl.8 b/debian/postfix-policyd-spf-perl.8 new file mode 100644 index 0000000..cf2c6af --- /dev/null +++ b/debian/postfix-policyd-spf-perl.8 @@ -0,0 +1,244 @@ +\" +.\" Standard preamble: +.\" ======================================================================== +.de Sh \" Subsection heading +.br +.if t .Sp +.ne 5 +.PP +\fB\\$1\fR +.PP +.. +.de Sp \" Vertical space (when we can't use .PP) +.if t .sp .5v +.if n .sp +.. +.de Vb \" Begin verbatim text +.ft CW +.nf +.ne \\$1 +.. +.de Ve \" End verbatim text +.ft R +.fi +.. +.\" Set up some character translations and predefined strings. \*(-- will +.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left +.\" double quote, and \*(R" will give a right double quote. \*(C+ will +.\" give a nicer C++. Capital omega is used to do unbreakable dashes and +.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, +.\" nothing in troff, for use with C<>. +.tr \(*W- +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.ie n \{\ +. ds -- \(*W- +. ds PI pi +. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +. ds L" "" +. ds R" "" +. ds C` "" +. ds C' "" +'br\} +.el\{\ +. ds -- \|\(em\| +. ds PI \(*p +. ds L" `` +. ds R" '' +'br\} +.\" +.\" If the F register is turned on, we'll generate index entries on stderr for +.\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index +.\" entries marked with X<> in POD. Of course, you'll have to process the +.\" output yourself in some meaningful fashion. +.if \nF \{\ +. de IX +. tm Index:\\$1\t\\n%\t"\\$2" +.. +. nr % 0 +. rr F +.\} +.\" +.\" For nroff, turn off justification. Always turn off hyphenation; it makes +.\" way too many mistakes in technical documents. +.hy 0 +.if n .na +.\" +.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). +.\" Fear. Run. Save yourself. No user-serviceable parts. +. \" fudge factors for nroff and troff +.if n \{\ +. ds #H 0 +. ds #V .8m +. ds #F .3m +. ds #[ \f1 +. ds #] \fP +.\} +.if t \{\ +. ds #H ((1u-(\\\\n(.fu%2u))*.13m) +. ds #V .6m +. ds #F 0 +. ds #[ \& +. ds #] \& +.\} +. \" simple accents for nroff and troff +.if n \{\ +. ds ' \& +. ds ` \& +. ds ^ \& +. ds , \& +. ds ~ ~ +. ds / +.\} +.if t \{\ +. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" +. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' +. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' +. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' +. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' +. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' +.\} +. \" troff and (daisy-wheel) nroff accents +.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' +.ds 8 \h'\*(#H'\(*b\h'-\*(#H' +.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] +.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' +.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' +.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] +.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] +.ds ae a\h'-(\w'a'u*4/10)'e +.ds Ae A\h'-(\w'A'u*4/10)'E +. \" corrections for vroff +.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' +.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' +. \" for low resolution devices (crt and lpr) +.if \n(.H>23 .if \n(.V>19 \ +\{\ +. ds : e +. ds 8 ss +. ds o a +. ds d- d\h'-1'\(ga +. ds D- D\h'-1'\(hy +. ds th \o'bp' +. ds Th \o'LP' +. ds ae ae +. ds Ae AE +.\} +.rm #[ #] #H #V #F C +.\" ======================================================================== +.\" +.IX Title "postfix-policyd-spf-perl 1p" +.TH postfix-policyd-spf-perl 8p "2007-01-11" +.SH "NAME" +postfix-policyd-spf-perl \- pure-Perl Postfix policy daemon for SPF checking +.SH "VERSION" +.IX Header "VERSION" +1\.990 + +.SH "USAGE" +.IX Header "USAGE" +Usage: + policyd-spf-perl [-v] + +.SH "OTHER DOCUMENTATION" +.IX Header "OTHER DOCUMENTATION" +This documentation assumes you have read Postfix's README_FILES/ +SMTPD_POLICY_README. + +.SH "SYNOPSIS" +.IX Header "SYNOPSIS" + +postfix-policyd-spf-perl is a Postfix SMTPd policy daemon for SPF checking. +It is implemented in pure Perl and uses the Mail::SPF CPAN module. Note that +Mail::SPF is a complete re-implementation of SPF based on the final SPF RFC, +RFC 4408. It shares no code with the older Mail::SPF::Query that was the +original SPF development implementation. + +This version of the policy server will reject mail that fails either Mail From +or HELO SPF checks. It always checks HELO (older versions just checked HELO if +Mail From was null). It will defer mail if there is a temporary SPF error and +the message would othersise be permitted (DEFER_IF_PERMIT). Otherwise, it will +PREPEND the appropriate SPF Received header. In the case of multi-recipient +mail, multiple headers will get appended. Error conditions within the policy +server (that don't result in a crash) or from Mail::SPF will return DUNNO. + +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" + +Logging is sent to syslogd. + +Each time a Postfix SMTP server process is started it connects to the policy +service socket and Postfix runs one instance of this Perls script. By +default, a Postfix SMTP server process terminates after 100 seconds of idle +time, or after serving 100 clients. Thus, the cost of starting this Perl +script is smoothed over time + +The default policy_time_limit is 1000 seconds. This may be too short for some +SMTP transactions to complete. As recommended in SMTPD_POLICY_README, this +should be extended to 3600 seconds. To do so, set "policy_time_limit = 3600" +in /etc/postfix/main.cf. + +.SH "TESTING THE POLICY DAEMON" +.IX Header "TESTING THE POLICY DAEMON" +Testing the policy daemon + +To test the policy daemon by hand, execute: + + % perl /usr/sbin/postfix-policyd-spf-perl + +Each query is a bunch of attributes. Order does not matter, and the daemon +uses only a few of all the attributes shown below: + + request=smtpd_access_policy + protocol_state=RCPT + protocol_name=SMTP + helo_name=some.domain.tld + queue_id=8045F2AB23 + sender=foo@bar.tld + recipient=bar@foo.tld + client_address=1.2.3.4 + client_name=another.domain.tld + [empty line] + +The policy daemon will answer in the same style, with an attribute list +followed by a empty line: + + action=dunno + [empty line] + +If you want more detail in the system logs change $VERBOSE to 1. + +.SH "POSTFIX INTEGRATION" +.IX Header "POSTFIX INTEGRATION" + + 1. Add the following to /etc/postfix/master.cf: + + policy unix - n n - - spawn + user=nobody argv=/usr/bin/perl /usr/sbin/policyd-spf-perl + + 2. Configure the Postfix policy service in /etc/postfix/main.cf: + + smtpd_recipient_restrictions = + ... + reject_unauth_destination + check_policy_service unix:private/policy + ... + policy_time_limit = 3600 + + NOTE: Specify check_policy_service AFTER reject_unauth_destination or + else your system can become an open relay. + + + 3. Restart Postfix. + +.SH "SEE ALSO" +.IX Header "SEE ALSO" +libmail-spf-perl, + +.SH "AUTHORS" +.IX Header "AUTHORS" +This version of \fBpolicyd-spf-perl\fR was written by Meng Weng Wong + and updated for libmail-spf-perl by Scott Kitterman +. +.PP +This man-page was written by Scott Kitterman . diff --git a/test_cases b/test_cases deleted file mode 100644 index d0c1cf0..0000000 --- a/test_cases +++ /dev/null @@ -1,56 +0,0 @@ -#HELO and mfrom pass -request=smtpd_access_policy -client_address=72.81.252.18 -helo_name=mailout02.controlledmail.com -sender=scott@kitterman.com -recipient=bogus@kitterman.org -queue_id=q1234 - -#HELO fail and mfrom pass -request=smtpd_access_policy -client_address=72.81.252.18 -helo_name=mailout00.controlledmail.com -sender=scott@kitterman.com -recipient=bogus@kitterman.org -queue_id=q1234 - -#no HELO and mfrom pass -request=smtpd_access_policy -client_address=72.81.252.18 -helo_name=mailout03.controlledmail.com -sender=scott@kitterman.com -recipient=bogus@kitterman.org -queue_id=q1234 - -#helo pass and mfrom fail -request=smtpd_access_policy -client_address=72.81.252.19 -helo_name=mailout00.controlledmail.com -sender=scott@kitterman.org -recipient=bogus@kitterman.org -queue_id=q1234 - -#helo pass and mfrom none -request=smtpd_access_policy -client_address=72.81.252.18 -helo_name=mailout02.controlledmail.com -sender=scott@yahoo.com -recipient=bogus@kitterman.org -queue_id=q1234 - -#helo pass and mfrom null -request=smtpd_access_policy -client_address=72.81.252.18 -helo_name=mailout02.controlledmail.com -sender= -recipient=bogus@kitterman.org -queue_id=q1234 - -#helo fail and mfrom null -request=smtpd_access_policy -client_address=72.81.252.19 -helo_name=mailout02.controlledmail.com -sender= -recipient=bogus@kitterman.org -queue_id=q1234 - From 8f82cbeb7ca7ec02c544e684bf4d4edfb7727ca3 Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Mon, 5 Feb 2007 21:47:52 +0000 Subject: [PATCH 21/87] Kicked version to 2.000 in preparation for release. --- CHANGES | 5 ++++- debian/changelog | 8 ++++---- debian/control | 4 ++-- debian/postfix-policyd-spf-perl.8 | 2 +- postfix-policyd-spf-perl | 4 ++-- 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/CHANGES b/CHANGES index 622c5e4..cbe39be 100644 --- a/CHANGES +++ b/CHANGES @@ -4,7 +4,10 @@ # ! = Changed something significant, or removed a feature # * = Fixed a bug, or made a minor improvement ---- 1.990 (2007-02-10 16:00) +--- 2.000 (2007-02-06 16:00) + * Updated version for final release. + +--- 1.990 (2007-02-03 16:00) postfix-policyd-spf-perl: ! Changed from Mail::SPF::Query to Mail::SPF for RFC 4408 compliance diff --git a/debian/changelog b/debian/changelog index 3b23795..9530ac8 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,12 +1,12 @@ -postfix-policyd-spf-perl (1.990-0ubuntu1) feisty; urgency=low +postfix-policyd-spf-perl (2.000-0ubuntu1) feisty; urgency=low * New upstream release for RFC compliant SPF checking. * Updated control to use libmail-spf-perl instead of the unmaintained libmail-spf-query-perl and changed maintainer to Scott Kitterman (was - MOTU). - * Updated man pages for new library and upstream documentation udpates. + MOTU). Updated package description. + * Updated man pages for new library and upstream documentation updates. - -- Scott Kitterman Sat, 03 Feb 2007 16:33:08 -0500 + -- Scott Kitterman Tue, 06 Feb 2007 08:00:00 -0500 postfix-policyd-spf-perl (1.08.1-0ubuntu1) feisty; urgency=low diff --git a/debian/control b/debian/control index 49d0cea..f74c9a6 100644 --- a/debian/control +++ b/debian/control @@ -9,8 +9,8 @@ Package: postfix-policyd-spf-perl Architecture: all Depends: libversion-perl, libmail-spf-perl, ${perl:Depends} Recommends: postfix -Description: pure-Perl Postfix policy daemon for SPF checking - postfix-policyd-spf-perl is a Postfix SMTPd policy daemon for SPF checking. +Description: pure-Perl Postfix policy daemon for RFC 4408 compliant SPF checking + postfix-policyd-spf-perl is a Postfix SMTPd policy server for SPF checking. It is implemented in pure Perl and uses the Mail::SPF module. The SPF web site is http://www.openspf.org/. The Postfix configuration must be changed to check SPF. See README.Debian or man 8 postfix-policyd-spf-perl for details. diff --git a/debian/postfix-policyd-spf-perl.8 b/debian/postfix-policyd-spf-perl.8 index cf2c6af..af75208 100644 --- a/debian/postfix-policyd-spf-perl.8 +++ b/debian/postfix-policyd-spf-perl.8 @@ -133,7 +133,7 @@ postfix-policyd-spf-perl \- pure-Perl Postfix policy daemon for SPF checking .SH "VERSION" .IX Header "VERSION" -1\.990 +2\.000 .SH "USAGE" .IX Header "USAGE" diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index 8ad66e4..37ca138 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -2,7 +2,7 @@ # postfix-policyd-spf-perl # http://www.openspf.org/Software -# version 1.990 +# version 2.000 # $Id$ #(C) 2007 Scott Kitterman #(C) 2003-2004 Meng Weng Wong @@ -21,7 +21,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -use version; our $VERSION = qv('1.990'); +use version; our $VERSION = qv('2.000'); use strict; From f876c707b90f71754d2b42b8c1d328bdedca16e1 Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Tue, 6 Feb 2007 18:18:42 +0000 Subject: [PATCH 22/87] * Change reject reply to 550 for RFC 2821 complianse. * Clarified wording for some verbose logging. * Added more information about HELO checking to README --- CHANGES | 5 ++++- README | 37 ++++++++++++++++++++++--------- debian/postfix-policyd-spf-perl.8 | 35 ++++++++++++++++++++--------- postfix-policyd-spf-perl | 10 ++++----- 4 files changed, 60 insertions(+), 27 deletions(-) diff --git a/CHANGES b/CHANGES index cbe39be..571d141 100644 --- a/CHANGES +++ b/CHANGES @@ -5,13 +5,16 @@ # * = Fixed a bug, or made a minor improvement --- 2.000 (2007-02-06 16:00) - * Updated version for final release. + * Change reject reply to 550 for RFC 2821 complianse. + * Clarified wording for some verbose logging. + * Added more information about HELO checking to README. --- 1.990 (2007-02-03 16:00) postfix-policyd-spf-perl: ! Changed from Mail::SPF::Query to Mail::SPF for RFC 4408 compliance ! Removed Testing handler (usage was undocumented). + ! Removed debian/ dir from release tarball (still provided via SVN). * Simplified logging. Policy server is less chatty. Logs are clearer. --- 1.08.1 (2007-01-10 21:00) diff --git a/README b/README index cabef8b..d29ca39 100644 --- a/README +++ b/README @@ -1,8 +1,8 @@ -postfix-policyd-spf-perl 1.990 +postfix-policyd-spf-perl 2.000 A Postfix SMTPd policy server for SPF checking (C) 2007 Scott Kitterman 2003-2004 Meng Weng Wong -Contributions by various members of the SPF project +Thanks for contributions by various members of the SPF project ============================================================================== @@ -13,15 +13,23 @@ RFC 4408. It shares no code with the older Mail::SPF::Query that was the original SPF development implementation. If you are upgrading from on older version of this policy server you will need to install Mail::SPF. -This version of the policy server will reject mail that fails either Mail From -or HELO SPF checks. It always checks HELO (older versions just checked HELO if -Mail From was null). It will defer mail if there is a temporary SPF error and -the message would othersise be permitted (DEFER_IF_PERMIT). Otherwise, it will -PREPEND the appropriate SPF Received header. In the case of multi-recipient -mail, multiple headers will get appended. Error conditions within the policy -server (that don't result in a crash) or from Mail::SPF will return DUNNO. +This version of the policy server always checks HELO before Mail From (older +versions just checked HELO if Mail From was null). It will reject mail that +fails either Mail From or HELO SPF checks. It will defer mail if there is a +temporary SPF error and the message would othersise be permitted +(DEFER_IF_PERMIT). If the HELO check produces a REJECT/DEFER result, Mail From +will not be checked. -See INSTALL or README.Debian for installation instructions. +If the message is not rejected or deferred, the policy server will PREPEND the +appropriate SPF Received header. In the case of multi-recipient mail, multiple +headers will get appended. If Mail From is anything other than completely empty +(i.e. <>) then the Mail From result will be used for SPF Received (e.g. Mail +From None even if HELO is Pass). + +Error conditions within the policy server (that don't result in a crash) or from +Mail::SPF will return DUNNO. + +See INSTALL for installation instructions. Usage: policyd-spf-perl [-v] @@ -66,7 +74,14 @@ uses only a few of all the attributes shown below: The policy daemon will answer in the same style, with an attribute list followed by a empty line: - action=dunno + action=550 Please see http://www.openspf.org/Why?id=foo@bar.tld&ip=1.2.3.4& + receiver=bar@foo.tld + [empty line] + +To test HELO checking sender should be empty: + + sender= + ... More attributes... [empty line] If you want more detail in the system logs change $VERBOSE to 1. diff --git a/debian/postfix-policyd-spf-perl.8 b/debian/postfix-policyd-spf-perl.8 index af75208..a68287c 100644 --- a/debian/postfix-policyd-spf-perl.8 +++ b/debian/postfix-policyd-spf-perl.8 @@ -152,15 +152,24 @@ postfix-policyd-spf-perl is a Postfix SMTPd policy daemon for SPF checking. It is implemented in pure Perl and uses the Mail::SPF CPAN module. Note that Mail::SPF is a complete re-implementation of SPF based on the final SPF RFC, RFC 4408. It shares no code with the older Mail::SPF::Query that was the -original SPF development implementation. +original SPF development implementation. If you are upgrading from on older +version of this policy server you will need to install Mail::SPF. -This version of the policy server will reject mail that fails either Mail From -or HELO SPF checks. It always checks HELO (older versions just checked HELO if -Mail From was null). It will defer mail if there is a temporary SPF error and -the message would othersise be permitted (DEFER_IF_PERMIT). Otherwise, it will -PREPEND the appropriate SPF Received header. In the case of multi-recipient -mail, multiple headers will get appended. Error conditions within the policy -server (that don't result in a crash) or from Mail::SPF will return DUNNO. +This version of the policy server always checks HELO before Mail From (older +versions just checked HELO if Mail From was null). It will reject mail that +fails either Mail From or HELO SPF checks. It will defer mail if there is a +temporary SPF error and the message would othersise be permitted +(DEFER_IF_PERMIT). If the HELO check produces a REJECT/DEFER result, Mail From +will not be checked. + +If the message is not rejected or deferred, the policy server will PREPEND the +appropriate SPF Received header. In the case of multi-recipient mail, multiple +headers will get appended. If Mail From is anything other than completely empty +(i.e. <>) then the Mail From result will be used for SPF Received (e.g. Mail +From None even if HELO is Pass). + +Error conditions within the policy server (that don't result in a crash) or from +Mail::SPF will return DUNNO. .SH "DESCRIPTION" .IX Header "DESCRIPTION" @@ -203,7 +212,14 @@ uses only a few of all the attributes shown below: The policy daemon will answer in the same style, with an attribute list followed by a empty line: - action=dunno + action=550 Please see http://www.openspf.org/Why?id=foo@bar.tld&ip=1.2.3.4& + receiver=bar@foo.tld + [empty line] + +To test HELO checking sender should be empty: + + sender= + ... More attributes... [empty line] If you want more detail in the system logs change $VERBOSE to 1. @@ -228,7 +244,6 @@ If you want more detail in the system logs change $VERBOSE to 1. NOTE: Specify check_policy_service AFTER reject_unauth_destination or else your system can become an open relay. - 3. Restart Postfix. .SH "SEE ALSO" diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index 37ca138..dc6f612 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -43,7 +43,7 @@ my @HANDLERS = ( } ); -my $VERBOSE = 0; +my $VERBOSE = 1; my $DEFAULT_RESPONSE = 'DUNNO'; @@ -159,7 +159,7 @@ sub sender_policy_framework { my $errmsg = $@; $errmsg = $errmsg->text if UNIVERSAL::isa($@, 'Mail::SPF::Exception'); syslog( - info => "%s: Mail::SPF->new(%s, %s, %s) failed: %s", + info => "%s:HELO check failed - Mail::SPF->new(%s, %s, %s) failed: %s", $attr->{queue_id}, $attr->{client_address}, $attr->{sender}, $attr->{helo_name}, $errmsg ); return "DUNNO"; @@ -183,7 +183,7 @@ sub sender_policy_framework { # Reject on HELO fail. Defer on HELO temperror if message would otherwise # be accepted. Use the HELO result and return for null sender. if ($helo_result->is_code('fail')) { - return "REJECT $helo_authority_exp"; + return "550 $helo_authority_exp"; } elsif ($helo_result->is_code('temperror')) { return "DEFER_IF_PERMIT SPF-Result=$helo_local_exp"; @@ -207,7 +207,7 @@ sub sender_policy_framework { my $errmsg = $@; $errmsg = $errmsg->text if UNIVERSAL::isa($@, 'Mail::SPF::Exception'); syslog( - info => "%s: Mail::SPF->new(%s, %s, %s) failed: %s", + info => "%s: Mail From (sender) check failed - Mail::SPF->new(%s, %s, %s) failed: %s", $attr->{queue_id}, $attr->{client_address}, $attr->{sender}, $attr->{helo_name}, $errmsg ); return "DUNNO"; @@ -231,7 +231,7 @@ sub sender_policy_framework { # Same approach as HELO.... if ($mfrom_result->is_code('fail')) { - return "REJECT $mfrom_authority_exp"; + return "550 $mfrom_authority_exp"; } elsif ($mfrom_result->is_code('temperror')) { return "DEFER_IF_PERMIT SPF-Result=$mfrom_local_exp"; From 515d21722d2330c589b8f456ee4166f31a739158 Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Tue, 6 Feb 2007 18:19:39 +0000 Subject: [PATCH 23/87] Fix stupid typo in chagnes. --- CHANGES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 571d141..8c6e24d 100644 --- a/CHANGES +++ b/CHANGES @@ -5,7 +5,7 @@ # * = Fixed a bug, or made a minor improvement --- 2.000 (2007-02-06 16:00) - * Change reject reply to 550 for RFC 2821 complianse. + * Change reject reply to 550 for RFC 2821 compliance. * Clarified wording for some verbose logging. * Added more information about HELO checking to README. From 0d8075552ec6b646b124c619076fc5276096314b Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Wed, 7 Feb 2007 12:59:17 +0000 Subject: [PATCH 24/87] * Skip SPF checks for local (127.) connections * TODO skip localhost for IPv6. --- CHANGES | 3 ++- README | 5 ++++- debian/postfix-policyd-spf-perl.8 | 3 +++ postfix-policyd-spf-perl | 35 ++++++++++++++++++------------- 4 files changed, 30 insertions(+), 16 deletions(-) diff --git a/CHANGES b/CHANGES index 8c6e24d..66e9eca 100644 --- a/CHANGES +++ b/CHANGES @@ -4,8 +4,9 @@ # ! = Changed something significant, or removed a feature # * = Fixed a bug, or made a minor improvement ---- 2.000 (2007-02-06 16:00) +--- 2.000 (2007-02-07 16:00) * Change reject reply to 550 for RFC 2821 compliance. + * Skip SPF checks for local (127.) connections * Clarified wording for some verbose logging. * Added more information about HELO checking to README. diff --git a/README b/README index d29ca39..00b043b 100644 --- a/README +++ b/README @@ -24,7 +24,10 @@ If the message is not rejected or deferred, the policy server will PREPEND the appropriate SPF Received header. In the case of multi-recipient mail, multiple headers will get appended. If Mail From is anything other than completely empty (i.e. <>) then the Mail From result will be used for SPF Received (e.g. Mail -From None even if HELO is Pass). +From None even if HELO is Pass). + +The policy server skips SPF checks for connections from the localhost (127.) and +instead prepends and logs 'SPF skipped - localhost is always allowed.' Error conditions within the policy server (that don't result in a crash) or from Mail::SPF will return DUNNO. diff --git a/debian/postfix-policyd-spf-perl.8 b/debian/postfix-policyd-spf-perl.8 index a68287c..811fd41 100644 --- a/debian/postfix-policyd-spf-perl.8 +++ b/debian/postfix-policyd-spf-perl.8 @@ -168,6 +168,9 @@ headers will get appended. If Mail From is anything other than completely empty (i.e. <>) then the Mail From result will be used for SPF Received (e.g. Mail From None even if HELO is Pass). +The policy server skips SPF checks for connections from the localhost (127.) and +instead prepends and logs 'SPF skipped - localhost is always allowed.' + Error conditions within the policy server (that don't result in a crash) or from Mail::SPF will return DUNNO. diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index dc6f612..6d88662 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -43,7 +43,7 @@ my @HANDLERS = ( } ); -my $VERBOSE = 1; +my $VERBOSE = 0; my $DEFAULT_RESPONSE = 'DUNNO'; @@ -113,24 +113,31 @@ while () { my $action = $DEFAULT_RESPONSE; my %responses; - foreach my $handler (@HANDLERS) { - my $handler_name = $handler->{name}; - my $handler_code = $handler->{code}; + # Skip SPF check for local connections + + if ($attr{client_address}=~ /^127\./) { + $action = "PREPEND SPF skipped - localhost is always allowed." + } + else { + foreach my $handler (@HANDLERS) { + my $handler_name = $handler->{name}; + my $handler_code = $handler->{code}; - my $response = $handler_code->(attr => \%attr); + my $response = $handler_code->(attr => \%attr); - if ($VERBOSE) { - syslog(debug => "handler %s: %s", $handler_name, $response); - } + if ($VERBOSE) { + syslog(debug => "handler %s: %s", $handler_name, $response); + } - # Picks whatever response is not dunno - if ($response and $response !~ /^dunno/i) { - syslog(info => "handler %s: is decisive.", $handler_name); - $action = $response; - last; + # Picks whatever response is not dunno + if ($response and $response !~ /^dunno/i) { + syslog(info => "handler %s: is decisive.", $handler_name); + $action = $response; + last; + } } } - + syslog(info => "%s: Policy action=%s", $attr{queue_id}, $action); STDOUT->print("action=$action\n\n"); From ce99d4e74f73b35e1f6c7d021c65733ba4458ad8 Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Wed, 7 Feb 2007 22:10:45 +0000 Subject: [PATCH 25/87] postfix-policyd-spf-perl 2.000 release --- CHANGES | 2 +- postfix-policyd-spf-perl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 66e9eca..5f001d1 100644 --- a/CHANGES +++ b/CHANGES @@ -4,7 +4,7 @@ # ! = Changed something significant, or removed a feature # * = Fixed a bug, or made a minor improvement ---- 2.000 (2007-02-07 16:00) +--- 2.000 (2007-02-07 17:07) * Change reject reply to 550 for RFC 2821 compliance. * Skip SPF checks for local (127.) connections * Clarified wording for some verbose logging. diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index 6d88662..19277b2 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -3,7 +3,7 @@ # postfix-policyd-spf-perl # http://www.openspf.org/Software # version 2.000 -# $Id$ +# #(C) 2007 Scott Kitterman #(C) 2003-2004 Meng Weng Wong # @@ -116,7 +116,7 @@ while () { # Skip SPF check for local connections if ($attr{client_address}=~ /^127\./) { - $action = "PREPEND SPF skipped - localhost is always allowed." + $action = "PREPEND X-SPF skipped - localhost is always allowed." } else { foreach my $handler (@HANDLERS) { From 0e669aaccaede80b56e606e3f08e7962d7727240 Mon Sep 17 00:00:00 2001 From: Julian Mehnle <> Date: Wed, 7 Feb 2007 23:13:46 +0000 Subject: [PATCH 26/87] postfix-policyd-spf-perl/trunk/postfix-policyd-spf-perl * use NetAddr::IP * Added another handler (sub exempt_localhost) at the front that exempts localhost (IPv4 and IPv6) addresses from further processing. Dropped special-cased localhost exemption. * Changed localhost message header from 'X-SPF' to 'X-Comment', and adjusted the header text slightly. * Various minor clean-ups. postfix-policyd-spf-perl/trunk/INSTALL postfix-policyd-spf-perl/trunk/debian/control * Note that NetAddr-IP 4 is required. --- INSTALL | 1 + debian/control | 2 +- postfix-policyd-spf-perl | 65 ++++++++++++++++++++++++++-------------- 3 files changed, 44 insertions(+), 24 deletions(-) diff --git a/INSTALL b/INSTALL index 86a8e5c..37d04f9 100644 --- a/INSTALL +++ b/INSTALL @@ -6,6 +6,7 @@ postfix-policyd-spf-perl: Perl 5.6 version + NetAddr-IP 4 Mail-SPF (not Mail-SPF-Query) Installing diff --git a/debian/control b/debian/control index f74c9a6..b04b78a 100644 --- a/debian/control +++ b/debian/control @@ -7,7 +7,7 @@ Standards-Version: 3.7.2 Package: postfix-policyd-spf-perl Architecture: all -Depends: libversion-perl, libmail-spf-perl, ${perl:Depends} +Depends: libversion-perl, libnetaddr-ip-perl (>= 4), libmail-spf-perl, ${perl:Depends} Recommends: postfix Description: pure-Perl Postfix policy daemon for RFC 4408 compliant SPF checking postfix-policyd-spf-perl is a Postfix SMTPd policy server for SPF checking. diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index 19277b2..9c6c92c 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -27,6 +27,7 @@ use strict; use IO::Handle; use Sys::Syslog qw(:DEFAULT setlogsock); +use NetAddr::IP; use Mail::SPF; # ---------------------------------------------------------- @@ -37,6 +38,10 @@ my $spf_server = Mail::SPF::Server->new(); # Leaving this to make it easier to add more handlers later: my @HANDLERS = ( + { + name => 'exempt_localhost', + code => \&exempt_localhost + }, { name => 'sender_policy_framework', code => \&sender_policy_framework @@ -58,6 +63,11 @@ my $syslog_facility = 'mail'; my $syslog_options = 'pid'; my $syslog_ident = 'postfix/policy-spf'; +use constant localhost_addresses => map( + NetAddr::IP->new($_), + qw( 127.0.0.0/8 ::ffff:127.0.0.0/104 ::1 ) +); # Does Postfix ever say "client_address=::ffff:"? + # ---------------------------------------------------------- # initialization # ---------------------------------------------------------- @@ -115,26 +125,21 @@ while () { my %responses; # Skip SPF check for local connections - if ($attr{client_address}=~ /^127\./) { - $action = "PREPEND X-SPF skipped - localhost is always allowed." - } - else { - foreach my $handler (@HANDLERS) { - my $handler_name = $handler->{name}; - my $handler_code = $handler->{code}; - - my $response = $handler_code->(attr => \%attr); - - if ($VERBOSE) { - syslog(debug => "handler %s: %s", $handler_name, $response); - } - - # Picks whatever response is not dunno - if ($response and $response !~ /^dunno/i) { - syslog(info => "handler %s: is decisive.", $handler_name); - $action = $response; - last; - } + foreach my $handler (@HANDLERS) { + my $handler_name = $handler->{name}; + my $handler_code = $handler->{code}; + + my $response = $handler_code->(attr => \%attr); + + if ($VERBOSE) { + syslog(debug => "handler %s: %s", $handler_name, $response); + } + + # Picks whatever response is not 'DUNNO' + if ($response and $response !~ /^DUNNO/i) { + syslog(info => "handler %s: is decisive.", $handler_name); + $action = $response; + last; } } @@ -145,8 +150,22 @@ while () { } # ---------------------------------------------------------- -# plugin: SPF +# handler: localhost exemption # ---------------------------------------------------------- + +sub exempt_localhost { + my %options = @_; + my $attr = $options{attr}; + my $client_address = NetAddr::IP->new($attr->{client_address}); + return 'PREPEND X-Comment SPF not applicable to localhost connection, skipped check' + if grep($_->contains($client_address), localhost_addresses); + return 'DUNNO'; +} + +# ---------------------------------------------------------- +# handler: SPF +# ---------------------------------------------------------- + sub sender_policy_framework { my %options = @_; my $attr = $options{attr}; @@ -169,7 +188,7 @@ sub sender_policy_framework { info => "%s:HELO check failed - Mail::SPF->new(%s, %s, %s) failed: %s", $attr->{queue_id}, $attr->{client_address}, $attr->{sender}, $attr->{helo_name}, $errmsg ); - return "DUNNO"; + return 'DUNNO'; } else { my $helo_result = $spf_server->process($helo_request); @@ -217,7 +236,7 @@ sub sender_policy_framework { info => "%s: Mail From (sender) check failed - Mail::SPF->new(%s, %s, %s) failed: %s", $attr->{queue_id}, $attr->{client_address}, $attr->{sender}, $attr->{helo_name}, $errmsg ); - return "DUNNO"; + return 'DUNNO'; } else { my $mfrom_result = $spf_server->process($mfrom_request); From 2e961db6b5d650df4240114efd29e174c379ffb0 Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Thu, 8 Feb 2007 05:34:14 +0000 Subject: [PATCH 27/87] * Don't crash if IP address not supplied --- postfix-policyd-spf-perl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index 9c6c92c..2ede3e6 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -156,9 +156,11 @@ while () { sub exempt_localhost { my %options = @_; my $attr = $options{attr}; - my $client_address = NetAddr::IP->new($attr->{client_address}); - return 'PREPEND X-Comment SPF not applicable to localhost connection, skipped check' - if grep($_->contains($client_address), localhost_addresses); + if ($attr->{client_address} != '') { + my $client_address = NetAddr::IP->new($attr->{client_address}); + return 'PREPEND X-Comment SPF not applicable to localhost connection, skipped check' + if grep($_->contains($client_address), localhost_addresses); + }; return 'DUNNO'; } From 871b66f5961e0147953e455d8052e094ffcc37ba Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Thu, 8 Feb 2007 05:37:44 +0000 Subject: [PATCH 28/87] * Release version 2.001 --- CHANGES | 3 +++ README | 2 +- debian/changelog | 2 +- debian/postfix-policyd-spf-perl.8 | 2 +- postfix-policyd-spf-perl | 4 ++-- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index 5f001d1..03d5375 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,9 @@ # ! = Changed something significant, or removed a feature # * = Fixed a bug, or made a minor improvement +--- 2.001 (2007-02-08 00:36) + * Safer check for local connections. + --- 2.000 (2007-02-07 17:07) * Change reject reply to 550 for RFC 2821 compliance. * Skip SPF checks for local (127.) connections diff --git a/README b/README index 00b043b..206fe1d 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -postfix-policyd-spf-perl 2.000 +postfix-policyd-spf-perl 2.001 A Postfix SMTPd policy server for SPF checking (C) 2007 Scott Kitterman 2003-2004 Meng Weng Wong diff --git a/debian/changelog b/debian/changelog index 9530ac8..e073f73 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -postfix-policyd-spf-perl (2.000-0ubuntu1) feisty; urgency=low +postfix-policyd-spf-perl (2.001-0ubuntu1) feisty; urgency=low * New upstream release for RFC compliant SPF checking. * Updated control to use libmail-spf-perl instead of the unmaintained diff --git a/debian/postfix-policyd-spf-perl.8 b/debian/postfix-policyd-spf-perl.8 index 811fd41..e537a3a 100644 --- a/debian/postfix-policyd-spf-perl.8 +++ b/debian/postfix-policyd-spf-perl.8 @@ -133,7 +133,7 @@ postfix-policyd-spf-perl \- pure-Perl Postfix policy daemon for SPF checking .SH "VERSION" .IX Header "VERSION" -2\.000 +2\.001 .SH "USAGE" .IX Header "USAGE" diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index 2ede3e6..6dcadf2 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -2,7 +2,7 @@ # postfix-policyd-spf-perl # http://www.openspf.org/Software -# version 2.000 +# version 2.001 # #(C) 2007 Scott Kitterman #(C) 2003-2004 Meng Weng Wong @@ -21,7 +21,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -use version; our $VERSION = qv('2.000'); +use version; our $VERSION = qv('2.001'); use strict; From 0f355dacc269d29ad1bae461084c3c87e27731e2 Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Mon, 19 Feb 2007 19:38:19 +0000 Subject: [PATCH 29/87] ! Only prepend a single SPF Received header for multi-recipient mail. --- CHANGES | 3 +++ README | 9 ++++----- debian/postfix-policyd-spf-perl.8 | 12 ++++++------ postfix-policyd-spf-perl | 15 +++++++++++---- 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/CHANGES b/CHANGES index 03d5375..ed5987e 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,9 @@ # ! = Changed something significant, or removed a feature # * = Fixed a bug, or made a minor improvement +--- 2.002 (2007-02-19 14:35) + ! Only prepend SPF received header once for multi-recipient mail. + --- 2.001 (2007-02-08 00:36) * Safer check for local connections. diff --git a/README b/README index 206fe1d..f3b3378 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -postfix-policyd-spf-perl 2.001 +postfix-policyd-spf-perl 2.002 A Postfix SMTPd policy server for SPF checking (C) 2007 Scott Kitterman 2003-2004 Meng Weng Wong @@ -21,10 +21,9 @@ temporary SPF error and the message would othersise be permitted will not be checked. If the message is not rejected or deferred, the policy server will PREPEND the -appropriate SPF Received header. In the case of multi-recipient mail, multiple -headers will get appended. If Mail From is anything other than completely empty -(i.e. <>) then the Mail From result will be used for SPF Received (e.g. Mail -From None even if HELO is Pass). +appropriate SPF Received header. If Mail From is anything other than completely +empty (i.e. <>) then the Mail From result will be used for SPF Received (e.g. +Mail From None even if HELO is Pass). The policy server skips SPF checks for connections from the localhost (127.) and instead prepends and logs 'SPF skipped - localhost is always allowed.' diff --git a/debian/postfix-policyd-spf-perl.8 b/debian/postfix-policyd-spf-perl.8 index e537a3a..89dd8ae 100644 --- a/debian/postfix-policyd-spf-perl.8 +++ b/debian/postfix-policyd-spf-perl.8 @@ -128,12 +128,12 @@ .\" ======================================================================== .\" .IX Title "postfix-policyd-spf-perl 1p" -.TH postfix-policyd-spf-perl 8p "2007-01-11" +.TH postfix-policyd-spf-perl 8p "2007-02-19" .SH "NAME" postfix-policyd-spf-perl \- pure-Perl Postfix policy daemon for SPF checking .SH "VERSION" .IX Header "VERSION" -2\.001 +2\.002 .SH "USAGE" .IX Header "USAGE" @@ -163,10 +163,9 @@ temporary SPF error and the message would othersise be permitted will not be checked. If the message is not rejected or deferred, the policy server will PREPEND the -appropriate SPF Received header. In the case of multi-recipient mail, multiple -headers will get appended. If Mail From is anything other than completely empty -(i.e. <>) then the Mail From result will be used for SPF Received (e.g. Mail -From None even if HELO is Pass). +appropriate SPF Received header. If Mail From is anything other than completely +empty (i.e. <>) then the Mail From result will be used for SPF Received (e.g. +Mail From None even if HELO is Pass). The policy server skips SPF checks for connections from the localhost (127.) and instead prepends and logs 'SPF skipped - localhost is always allowed.' @@ -206,6 +205,7 @@ uses only a few of all the attributes shown below: protocol_name=SMTP helo_name=some.domain.tld queue_id=8045F2AB23 + instance=12345.6789 sender=foo@bar.tld recipient=bar@foo.tld client_address=1.2.3.4 diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index 6dcadf2..5793ac9 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -2,7 +2,7 @@ # postfix-policyd-spf-perl # http://www.openspf.org/Software -# version 2.001 +# version 2.002 # #(C) 2007 Scott Kitterman #(C) 2003-2004 Meng Weng Wong @@ -21,7 +21,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -use version; our $VERSION = qv('2.001'); +use version; our $VERSION = qv('2.002'); use strict; @@ -51,6 +51,7 @@ my @HANDLERS = ( my $VERBOSE = 0; my $DEFAULT_RESPONSE = 'DUNNO'; +my $accepted = "UNDEF"; # # Syslogging options for verbose mode and for fatal errors. @@ -120,7 +121,7 @@ while () { syslog(debug => "Attribute: %s=%s", $_, $attr{$_}); } } - + my $instance = $attr{instance}; my $action = $DEFAULT_RESPONSE; my %responses; # Skip SPF check for local connections @@ -130,7 +131,12 @@ while () { my $handler_code = $handler->{code}; my $response = $handler_code->(attr => \%attr); - + + if($instance && $instance eq $accepted) { + $response = 'DUNNO'; + } + + if ($VERBOSE) { syslog(debug => "handler %s: %s", $handler_name, $response); } @@ -146,6 +152,7 @@ while () { syslog(info => "%s: Policy action=%s", $attr{queue_id}, $action); STDOUT->print("action=$action\n\n"); + $accepted = $instance; %attr = (); } From e041d210e31c7504544766b4b336e3a10075d498 Mon Sep 17 00:00:00 2001 From: Julian Mehnle <> Date: Mon, 19 Feb 2007 23:44:05 +0000 Subject: [PATCH 30/87] software/postfix-policyd-spf-perl/trunk/postfix-policyd-spf-perl * Added myself to copyright statement after all. * Implemented results cache in order to prevent redundant SPF checks in multiple invocations per message instance. This also enables us to prepend a "Received-SPF" header only once per message instance (as opposed to once per recipient address). * Minor code and comments clean-up. --- postfix-policyd-spf-perl | 230 +++++++++++++++++++++------------------ 1 file changed, 126 insertions(+), 104 deletions(-) diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index 5793ac9..dbc61ec 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -4,8 +4,9 @@ # http://www.openspf.org/Software # version 2.002 # -#(C) 2007 Scott Kitterman -#(C) 2003-2004 Meng Weng Wong +# (C) 2007 Scott Kitterman +# (C) 2007 Julian Mehnle +# (C) 2003-2004 Meng Weng Wong # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -36,7 +37,7 @@ use Mail::SPF; my $spf_server = Mail::SPF::Server->new(); -# Leaving this to make it easier to add more handlers later: +# Adding more handlers is easy: my @HANDLERS = ( { name => 'exempt_localhost', @@ -51,7 +52,6 @@ my @HANDLERS = ( my $VERBOSE = 0; my $DEFAULT_RESPONSE = 'DUNNO'; -my $accepted = "UNDEF"; # # Syslogging options for verbose mode and for fatal errors. @@ -69,6 +69,8 @@ use constant localhost_addresses => map( qw( 127.0.0.0/8 ::ffff:127.0.0.0/104 ::1 ) ); # Does Postfix ever say "client_address=::ffff:"? +my %results_cache; # by message instance + # ---------------------------------------------------------- # initialization # ---------------------------------------------------------- @@ -121,38 +123,33 @@ while () { syslog(debug => "Attribute: %s=%s", $_, $attr{$_}); } } - my $instance = $attr{instance}; + + my $message_instance = $attr{instance}; + my $cache = defined($message_instance) ? $results_cache{$message_instance} ||= {} : {}; + my $action = $DEFAULT_RESPONSE; - my %responses; - # Skip SPF check for local connections - + foreach my $handler (@HANDLERS) { my $handler_name = $handler->{name}; my $handler_code = $handler->{code}; - - my $response = $handler_code->(attr => \%attr); - - if($instance && $instance eq $accepted) { - $response = 'DUNNO'; - } - - + + my $response = $handler_code->(attr => \%attr, cache => $cache); + if ($VERBOSE) { syslog(debug => "handler %s: %s", $handler_name, $response); } - - # Picks whatever response is not 'DUNNO' + + # Pick whatever response is not 'DUNNO' if ($response and $response !~ /^DUNNO/i) { syslog(info => "handler %s: is decisive.", $handler_name); $action = $response; last; } } - + syslog(info => "%s: Policy action=%s", $attr{queue_id}, $action); - + STDOUT->print("action=$action\n\n"); - $accepted = $instance; %attr = (); } @@ -177,102 +174,127 @@ sub exempt_localhost { sub sender_policy_framework { my %options = @_; - my $attr = $options{attr}; - - # Always do HELO check first. If no HELO policy it's only one lookup. - # Avoids the need to do any Mail From processing for null sender. - my $helo_request = eval { - Mail::SPF::Request->new( - scope => 'helo', - identity => $attr->{helo_name}, - ip_address => $attr->{client_address} - ); - }; + my $attr = $options{attr}; + my $cache = $options{cache}; - # If initializing helo_request throws an error, don't use it. - if ($@) { - my $errmsg = $@; - $errmsg = $errmsg->text if UNIVERSAL::isa($@, 'Mail::SPF::Exception'); - syslog( - info => "%s:HELO check failed - Mail::SPF->new(%s, %s, %s) failed: %s", - $attr->{queue_id}, $attr->{client_address}, $attr->{sender}, $attr->{helo_name}, $errmsg - ); - return 'DUNNO'; - } - else { - my $helo_result = $spf_server->process($helo_request); + # ------------------------------------------------------------------------- + # Always do HELO check first. If no HELO policy, it's only one lookup. + # This avoids the need to do any MAIL FROM processing for null sender. + # ------------------------------------------------------------------------- + + my $helo_result = $cache->{helo_result}; + + if (not defined($helo_result)) { + # No HELO result has been cached from earlier checks on this message. - my $helo_result_code = $helo_result->code; # 'pass', 'fail', etc. - my $helo_local_exp = $helo_result->local_explanation; - my $helo_authority_exp = $helo_result->authority_explanation - if $helo_result->is_code('fail'); - my $helo_spf_header = $helo_result->received_spf_header; - if ($VERBOSE) { - syslog( - info => "%s: SPF %s: HELO/EHLO: %s, IP Address: %s, Recipient: %s", - $attr->{queue_id}, $helo_result, $attr->{helo_name}, $attr->{client_address}, - $attr->{recipient} + my $helo_request = eval { + Mail::SPF::Request->new( + scope => 'helo', + identity => $attr->{helo_name}, + ip_address => $attr->{client_address} ); }; - # Reject on HELO fail. Defer on HELO temperror if message would otherwise - # be accepted. Use the HELO result and return for null sender. - if ($helo_result->is_code('fail')) { - return "550 $helo_authority_exp"; - } - elsif ($helo_result->is_code('temperror')) { - return "DEFER_IF_PERMIT SPF-Result=$helo_local_exp"; - } - elsif ($attr->{sender} eq '') { - return "PREPEND $helo_spf_header"; + if ($@) { + # An unexpected error occurred during request creation, + # probably due to invalid input data! + my $errmsg = $@; + $errmsg = $errmsg->text if UNIVERSAL::isa($@, 'Mail::SPF::Exception'); + syslog( + info => "%s:HELO check failed - Mail::SPF->new(%s, %s, %s) failed: %s", + $attr->{queue_id}, $attr->{client_address}, $attr->{sender}, $attr->{helo_name}, $errmsg + ); + return; } + + $helo_result = $cache->{helo_result} = $spf_server->process($helo_request); } - # Do mail from is HELO doesn't give a definitive result. - my $mfrom_request = eval { - Mail::SPF::Request->new( - scope => 'mfrom', - identity => $attr->{sender}, - ip_address => $attr->{client_address}, - helo_identity => $attr->{helo_name} # for %{h} macro expansion + my $helo_result_code = $helo_result->code; # 'pass', 'fail', etc. + my $helo_local_exp = $helo_result->local_explanation; + my $helo_authority_exp = $helo_result->authority_explanation + if $helo_result->is_code('fail'); + my $helo_spf_header = $helo_result->received_spf_header; + + if ($VERBOSE) { + syslog( + info => "%s: SPF %s: HELO/EHLO: %s, IP Address: %s, Recipient: %s", + $attr->{queue_id}, $helo_result, $attr->{helo_name}, $attr->{client_address}, + $attr->{recipient} ); }; - if ($@) { - my $errmsg = $@; - $errmsg = $errmsg->text if UNIVERSAL::isa($@, 'Mail::SPF::Exception'); - syslog( - info => "%s: Mail From (sender) check failed - Mail::SPF->new(%s, %s, %s) failed: %s", - $attr->{queue_id}, $attr->{client_address}, $attr->{sender}, $attr->{helo_name}, $errmsg - ); - return 'DUNNO'; - } - else { - my $mfrom_result = $spf_server->process($mfrom_request); + # Reject on HELO fail. Defer on HELO temperror if message would otherwise + # be accepted. Use the HELO result and return for null sender. + if ($helo_result->is_code('fail')) { + return "550 $helo_authority_exp"; + } + elsif ($helo_result->is_code('temperror')) { + return "DEFER_IF_PERMIT SPF-Result=$helo_local_exp"; + } + elsif ($attr->{sender} eq '') { + return "PREPEND $helo_spf_header" + unless $cache->{added_spf_header}++; + } + + # ------------------------------------------------------------------------- + # Do MAIL FROM check (as HELO did not give a definitive result) + # ------------------------------------------------------------------------- + + my $mfrom_result = $cache->{mfrom_result}; + + if (not defined($mfrom_result)) { + # No MAIL FROM result has been cached from earlier checks on this message. - my $mfrom_result_code = $mfrom_result->code; # 'pass', 'fail', etc. - my $mfrom_local_exp = $mfrom_result->local_explanation; - my $mfrom_authority_exp = $mfrom_result->authority_explanation - if $mfrom_result->is_code('fail'); - my $mfrom_spf_header = $mfrom_result->received_spf_header; - - if ($VERBOSE) { - syslog( - info => "%s: SPF %s: Envelope-from: %s, IP Address: %s, Recipient: %s", - $attr->{queue_id}, $mfrom_result, $attr->{sender}, $attr->{client_address}, - $attr->{recipient} + my $mfrom_request = eval { + Mail::SPF::Request->new( + scope => 'mfrom', + identity => $attr->{sender}, + ip_address => $attr->{client_address}, + helo_identity => $attr->{helo_name} # for %{h} macro expansion ); }; - - # Same approach as HELO.... - if ($mfrom_result->is_code('fail')) { - return "550 $mfrom_authority_exp"; - } - elsif ($mfrom_result->is_code('temperror')) { - return "DEFER_IF_PERMIT SPF-Result=$mfrom_local_exp"; - } - else { - return "PREPEND $mfrom_spf_header"; - } + + if ($@) { + # An unexpected error occurred during request creation, + # probably due to invalid input data! + my $errmsg = $@; + $errmsg = $errmsg->text if UNIVERSAL::isa($@, 'Mail::SPF::Exception'); + syslog( + info => "%s: Mail From (sender) check failed - Mail::SPF->new(%s, %s, %s) failed: %s", + $attr->{queue_id}, $attr->{client_address}, $attr->{sender}, $attr->{helo_name}, $errmsg + ); + return; + } + + $mfrom_result = $cache->{mfrom_result} = $spf_server->process($mfrom_request); } + + my $mfrom_result_code = $mfrom_result->code; # 'pass', 'fail', etc. + my $mfrom_local_exp = $mfrom_result->local_explanation; + my $mfrom_authority_exp = $mfrom_result->authority_explanation + if $mfrom_result->is_code('fail'); + my $mfrom_spf_header = $mfrom_result->received_spf_header; + + if ($VERBOSE) { + syslog( + info => "%s: SPF %s: Envelope-from: %s, IP Address: %s, Recipient: %s", + $attr->{queue_id}, $mfrom_result, $attr->{sender}, $attr->{client_address}, + $attr->{recipient} + ); + }; + + # Same approach as HELO.... + if ($mfrom_result->is_code('fail')) { + return "550 $mfrom_authority_exp"; + } + elsif ($mfrom_result->is_code('temperror')) { + return "DEFER_IF_PERMIT SPF-Result=$mfrom_local_exp"; + } + else { + return "PREPEND $mfrom_spf_header" + unless $cache->{added_spf_header}++; + } + + return; } From 90fa45972c45d4e33fabe5ab77d84e8d3ee04fe7 Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Tue, 20 Feb 2007 10:51:21 +0000 Subject: [PATCH 31/87] Final pre-release cleanup for 2.002 release. --- CHANGES | 9 ++- README | 5 +- debian/{README.Debian => INSTALL.Debian} | 0 debian/changelog | 10 ++++ debian/control | 3 +- debian/copyright | 7 ++- debian/postfix-policyd-spf-perl.8 | 2 +- test_cases | 73 ++++++++++++++++++++++++ 8 files changed, 100 insertions(+), 9 deletions(-) rename debian/{README.Debian => INSTALL.Debian} (100%) create mode 100644 test_cases diff --git a/CHANGES b/CHANGES index ed5987e..847422f 100644 --- a/CHANGES +++ b/CHANGES @@ -4,8 +4,13 @@ # ! = Changed something significant, or removed a feature # * = Fixed a bug, or made a minor improvement ---- 2.002 (2007-02-19 14:35) - ! Only prepend SPF received header once for multi-recipient mail. +--- 2.002 (2007-02-20 05:45) + * Added Julian Menhle to copyright statement. + * Implemented results cache in order to prevent redundant SPF checks in + multiple invocations per message instance. This also enables us to prepend + a "Received-SPF" header only once per message instance (as opposed to once + per recipient address). + * Minor code and comments clean-up. --- 2.001 (2007-02-08 00:36) * Safer check for local connections. diff --git a/README b/README index f3b3378..9a386ae 100644 --- a/README +++ b/README @@ -1,7 +1,8 @@ postfix-policyd-spf-perl 2.002 A Postfix SMTPd policy server for SPF checking -(C) 2007 Scott Kitterman - 2003-2004 Meng Weng Wong +(C) 2007 Scott Kitterman and Julian Mehnle + +(C) 2003-2004 Meng Weng Wong Thanks for contributions by various members of the SPF project ============================================================================== diff --git a/debian/README.Debian b/debian/INSTALL.Debian similarity index 100% rename from debian/README.Debian rename to debian/INSTALL.Debian diff --git a/debian/changelog b/debian/changelog index e073f73..998e876 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,13 @@ +postfix-policyd-spf-perl (2.002-0ubuntu1) feisty; urgency=low + + * New upstream release to prevent appending multiple SPF received headers to + multi-recipient mail.. + * changed maintainer to back to MOTU. + * Added Julian Mehnle to debian/copyright. + * Updated man pages for upstream documentation updates. + + -- Scott Kitterman Tue, 20 Feb 2007 05:45:00 -0500 + postfix-policyd-spf-perl (2.001-0ubuntu1) feisty; urgency=low * New upstream release for RFC compliant SPF checking. diff --git a/debian/control b/debian/control index b04b78a..e384ef9 100644 --- a/debian/control +++ b/debian/control @@ -1,7 +1,8 @@ Source: postfix-policyd-spf-perl Section: mail Priority: extra -Maintainer: Scott Kitterman +Maintainer: Ubuntu MOTU Developers +XBSC-Original-Maintainer: Scott Kitterman Build-Depends: debhelper (>= 5) Standards-Version: 3.7.2 diff --git a/debian/copyright b/debian/copyright index 3a5ecdc..13a4e65 100644 --- a/debian/copyright +++ b/debian/copyright @@ -3,12 +3,13 @@ Thu, 11 Jan 2007 04:29:13 -0500. It was downloaded from . -Upstream authors Meng Weng Wong and Scott Kitterman - +Upstream authors Meng Weng Wong , Scott Kitterman +, and Julian Mehnle Copyright: -(C) 2007 Scott Kitterman +(C) 2007 Scott Kitterman and Julian Mehnle + (C) 2003-2004 Meng Weng Wong This is free software; you can redistribute it and/or modify it under the terms diff --git a/debian/postfix-policyd-spf-perl.8 b/debian/postfix-policyd-spf-perl.8 index 89dd8ae..9ea5b0b 100644 --- a/debian/postfix-policyd-spf-perl.8 +++ b/debian/postfix-policyd-spf-perl.8 @@ -257,6 +257,6 @@ libmail-spf-perl, .IX Header "AUTHORS" This version of \fBpolicyd-spf-perl\fR was written by Meng Weng Wong and updated for libmail-spf-perl by Scott Kitterman -. + and Julian Mehnle . .PP This man-page was written by Scott Kitterman . diff --git a/test_cases b/test_cases new file mode 100644 index 0000000..6d9e0a7 --- /dev/null +++ b/test_cases @@ -0,0 +1,73 @@ +#HELO and mfrom pass +request=smtpd_access_policy +client_address=72.81.252.18 +helo_name=mailout02.controlledmail.com +sender=scott@kitterman.com +recipient=bogus@kitterman.org +queue_id=q1234 +instance=1 + +#HELO fail and mfrom pass +request=smtpd_access_policy +client_address=72.81.252.18 +helo_name=mailout00.controlledmail.com +sender=scott@kitterman.com +recipient=bogus@kitterman.org +queue_id=q1234 +instance=2 + +#no HELO and mfrom pass +request=smtpd_access_policy +client_address=72.81.252.18 +helo_name=mailout03.controlledmail.com +sender=scott@kitterman.com +recipient=bogus@kitterman.org +queue_id=q1234 +instance=3 + +#helo pass and mfrom fail +request=smtpd_access_policy +client_address=72.81.252.19 +helo_name=mailout00.controlledmail.com +sender=scott@kitterman.org +recipient=bogus@kitterman.org +queue_id=q1234 +instance=4 + +#helo pass and mfrom none +request=smtpd_access_policy +client_address=72.81.252.18 +helo_name=mailout02.controlledmail.com +sender=scott@yahoo.com +recipient=bogus@kitterman.org +queue_id=q1234 +instance=5 + +#helo pass and mfrom null +request=smtpd_access_policy +client_address=72.81.252.18 +helo_name=mailout02.controlledmail.com +sender= +recipient=bogus@kitterman.org +queue_id=q1234 +instance=6 + +#helo fail and mfrom null +request=smtpd_access_policy +client_address=72.81.252.19 +helo_name=mailout02.controlledmail.com +sender= +recipient=bogus@kitterman.org +queue_id=q1234 +instance=7 + +#Multi-recipient dunno +request=smtpd_access_policy +client_address=72.81.252.18 +helo_name=mailout03.controlledmail.com +sender=scott@kitterman.com +recipient=bogus2@kitterman.org +queue_id=q1234 +instance=3 + + From fa2ddd3d0e0b04d9a5e1d18f938a981d16026029 Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Wed, 21 Feb 2007 14:43:25 +0000 Subject: [PATCH 32/87] Fix typo in debian/changelog. --- debian/changelog | 2 +- debian/control | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index 998e876..f26b6dd 100644 --- a/debian/changelog +++ b/debian/changelog @@ -2,7 +2,7 @@ postfix-policyd-spf-perl (2.002-0ubuntu1) feisty; urgency=low * New upstream release to prevent appending multiple SPF received headers to multi-recipient mail.. - * changed maintainer to back to MOTU. + * changed maintainer back to MOTU. * Added Julian Mehnle to debian/copyright. * Updated man pages for upstream documentation updates. diff --git a/debian/control b/debian/control index e384ef9..b08733c 100644 --- a/debian/control +++ b/debian/control @@ -2,7 +2,7 @@ Source: postfix-policyd-spf-perl Section: mail Priority: extra Maintainer: Ubuntu MOTU Developers -XBSC-Original-Maintainer: Scott Kitterman +XSBC-Original-Maintainer: Scott Kitterman Build-Depends: debhelper (>= 5) Standards-Version: 3.7.2 From 1e72ea4676df45c4e4a225d61366a65a8e357008 Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Wed, 21 Feb 2007 16:35:42 +0000 Subject: [PATCH 33/87] Fix file reference in debian/control package description. --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index b08733c..92db204 100644 --- a/debian/control +++ b/debian/control @@ -14,4 +14,4 @@ Description: pure-Perl Postfix policy daemon for RFC 4408 compliant SPF checking postfix-policyd-spf-perl is a Postfix SMTPd policy server for SPF checking. It is implemented in pure Perl and uses the Mail::SPF module. The SPF web site is http://www.openspf.org/. The Postfix configuration must be changed to - check SPF. See README.Debian or man 8 postfix-policyd-spf-perl for details. + check SPF. See INSTALL.Debian or man 8 postfix-policyd-spf-perl for details. From 15fe331c67a59905122075c806d4b64160d8af3b Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Mon, 26 Feb 2007 15:10:54 +0000 Subject: [PATCH 34/87] * Added single file version of 2.002 for postfix examples * Minor documentation cleanup in svn head. --- CHANGES | 3 +++ INSTALL | 4 ++-- README | 7 ++++--- debian/changelog | 6 ++++++ debian/postfix-policyd-spf-perl.8 | 8 ++++---- postfix-policyd-spf-perl | 4 ++-- 6 files changed, 21 insertions(+), 11 deletions(-) diff --git a/CHANGES b/CHANGES index 847422f..83327d4 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,9 @@ # ! = Changed something significant, or removed a feature # * = Fixed a bug, or made a minor improvement +--- 2.003 (2007-02-26 10:00) + * Minor documentation cleanup. + --- 2.002 (2007-02-20 05:45) * Added Julian Menhle to copyright statement. * Implemented results cache in order to prevent redundant SPF checks in diff --git a/INSTALL b/INSTALL index 37d04f9..282b9a6 100644 --- a/INSTALL +++ b/INSTALL @@ -12,12 +12,12 @@ postfix-policyd-spf-perl: Installing ---------- - 1. Copy postfix-policyd-spf-perl to /usr/lib/postfix/policyd-spf-perl + 1. Copy postfix-policyd-spf-perl to /usr/local/lib/policyd-spf-perl 2. Add the following to /etc/postfix/master.cf: policy unix - n n - - spawn - user=nobody argv=/usr/bin/perl /usr/lib/postfix/policyd-spf-perl + user=nobody argv=/usr/bin/perl /usr/local/lib/policyd-spf-perl 3. Configure the Postfix policy service in /etc/postfix/main.cf: diff --git a/README b/README index 9a386ae..92dd3a6 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -postfix-policyd-spf-perl 2.002 +postfix-policyd-spf-perl 2.003 A Postfix SMTPd policy server for SPF checking (C) 2007 Scott Kitterman and Julian Mehnle @@ -58,7 +58,7 @@ Testing the policy daemon To test the policy daemon by hand, execute: - % perl /usr/lib/postfix/policyd-spf-perl + % perl /usr/local/lib/policyd-spf-perl Each query is a bunch of attributes. Order does not matter, and the daemon uses only a few of all the attributes shown below: @@ -67,7 +67,8 @@ uses only a few of all the attributes shown below: protocol_state=RCPT protocol_name=SMTP helo_name=some.domain.tld - queue_id=8045F2AB23 + queue_id= + instance=71b0.45e2f5f1.d4da1.0 sender=foo@bar.tld recipient=bar@foo.tld client_address=1.2.3.4 diff --git a/debian/changelog b/debian/changelog index f26b6dd..25bedf0 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +postfix-policyd-spf-perl (2.003-0ubuntu1) feisty; urgency=low + + * New upstream release + + -- Scott Kitterman Mon, 26 Feb 2007 10:00:00 -0500 + postfix-policyd-spf-perl (2.002-0ubuntu1) feisty; urgency=low * New upstream release to prevent appending multiple SPF received headers to diff --git a/debian/postfix-policyd-spf-perl.8 b/debian/postfix-policyd-spf-perl.8 index 9ea5b0b..c92f02f 100644 --- a/debian/postfix-policyd-spf-perl.8 +++ b/debian/postfix-policyd-spf-perl.8 @@ -128,12 +128,12 @@ .\" ======================================================================== .\" .IX Title "postfix-policyd-spf-perl 1p" -.TH postfix-policyd-spf-perl 8p "2007-02-19" +.TH postfix-policyd-spf-perl 8p "2007-02-26" .SH "NAME" postfix-policyd-spf-perl \- pure-Perl Postfix policy daemon for SPF checking .SH "VERSION" .IX Header "VERSION" -2\.002 +2\.003 .SH "USAGE" .IX Header "USAGE" @@ -204,8 +204,8 @@ uses only a few of all the attributes shown below: protocol_state=RCPT protocol_name=SMTP helo_name=some.domain.tld - queue_id=8045F2AB23 - instance=12345.6789 + queue_id= + instance=71b0.45e2f5f1.d4da1.0 sender=foo@bar.tld recipient=bar@foo.tld client_address=1.2.3.4 diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index dbc61ec..75c6396 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -2,7 +2,7 @@ # postfix-policyd-spf-perl # http://www.openspf.org/Software -# version 2.002 +# version 2.003 # # (C) 2007 Scott Kitterman # (C) 2007 Julian Mehnle @@ -22,7 +22,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -use version; our $VERSION = qv('2.002'); +use version; our $VERSION = qv('2.003'); use strict; From f5c6870c3cb82d3bea0152147c43d0a2acfbd405 Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Tue, 17 Apr 2007 11:37:07 +0000 Subject: [PATCH 35/87] * Add handler for list of relay addresses to bypass. --- postfix-policyd-spf-perl | 24 ++++++++++++++++++++++++ test_cases | 8 ++++++++ 2 files changed, 32 insertions(+) diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index 75c6396..cb75f7e 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -43,6 +43,10 @@ my @HANDLERS = ( name => 'exempt_localhost', code => \&exempt_localhost }, + { + name => 'exempt_relay', + code => \&exempt_relay + }, { name => 'sender_policy_framework', code => \&sender_policy_framework @@ -69,6 +73,11 @@ use constant localhost_addresses => map( qw( 127.0.0.0/8 ::ffff:127.0.0.0/104 ::1 ) ); # Does Postfix ever say "client_address=::ffff:"? +use constant relay_addresses => map( + NetAddr::IP->new($_), + qw( ) +); # add addresses to qw ( ) above separated by spaces using CIDR notation. + my %results_cache; # by message instance # ---------------------------------------------------------- @@ -168,6 +177,21 @@ sub exempt_localhost { return 'DUNNO'; } +# ---------------------------------------------------------- +# handler: relay exemption +# ---------------------------------------------------------- + +sub exempt_relay { + my %options = @_; + my $attr = $options{attr}; + if ($attr->{client_address} != '') { + my $client_address = NetAddr::IP->new($attr->{client_address}); + return 'PREPEND X-Comment SPF skipped for whitelisted relay' + if grep($_->contains($client_address), relay_addresses); + }; + return 'DUNNO'; +} + # ---------------------------------------------------------- # handler: SPF # ---------------------------------------------------------- diff --git a/test_cases b/test_cases index 6d9e0a7..0a4056c 100644 --- a/test_cases +++ b/test_cases @@ -70,4 +70,12 @@ recipient=bogus2@kitterman.org queue_id=q1234 instance=3 +#localhost bypass +request=smtpd_access_policy +client_address=192.168.0.1 +helo_name=mailout03.controlledmail.com +sender=scott@kitterman.com +recipient=bogus2@kitterman.org +queue_id=q1234 +instance=12 From 59bc8e46b934fd7c930812fe46d99580d1d79ab4 Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Tue, 17 Apr 2007 11:53:50 +0000 Subject: [PATCH 36/87] * Documentation update for 2.003 changes. --- CHANGES | 3 ++- README | 6 +++++- debian/changelog | 6 ++++-- debian/postfix-policyd-spf-perl.8 | 6 +++++- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index 83327d4..59fb2b6 100644 --- a/CHANGES +++ b/CHANGES @@ -4,8 +4,9 @@ # ! = Changed something significant, or removed a feature # * = Fixed a bug, or made a minor improvement ---- 2.003 (2007-02-26 10:00) +--- 2.003 (2007-04-17 08:50) * Minor documentation cleanup. + + Add handler for list of relay addresses to bypass. --- 2.002 (2007-02-20 05:45) * Added Julian Menhle to copyright statement. diff --git a/README b/README index 92dd3a6..5081964 100644 --- a/README +++ b/README @@ -27,7 +27,11 @@ empty (i.e. <>) then the Mail From result will be used for SPF Received (e.g. Mail From None even if HELO is Pass). The policy server skips SPF checks for connections from the localhost (127.) and -instead prepends and logs 'SPF skipped - localhost is always allowed.' +instead prepends and logs 'SPF skipped - localhost is always allowed.' If you +have relays that you want to skip SPF checks for, you can add them to +relay_addresses on line 78 using standard CIDR notation in a space separated +list. For these addresses, 'X-Comment SPF skipped for whitelisted relay' is +prepended and logged. Error conditions within the policy server (that don't result in a crash) or from Mail::SPF will return DUNNO. diff --git a/debian/changelog b/debian/changelog index 25bedf0..c5d6a03 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,8 +1,10 @@ -postfix-policyd-spf-perl (2.003-0ubuntu1) feisty; urgency=low +postfix-policyd-spf-perl (2.003-0ubuntu1) gutsy; urgency=low * New upstream release + - Add handler for list of relay addresses to bypass. + * Update man page for upstream changes. - -- Scott Kitterman Mon, 26 Feb 2007 10:00:00 -0500 + -- Scott Kitterman Mon, 17 April 2007 08:52:00 -0400 postfix-policyd-spf-perl (2.002-0ubuntu1) feisty; urgency=low diff --git a/debian/postfix-policyd-spf-perl.8 b/debian/postfix-policyd-spf-perl.8 index c92f02f..6d340be 100644 --- a/debian/postfix-policyd-spf-perl.8 +++ b/debian/postfix-policyd-spf-perl.8 @@ -168,7 +168,11 @@ empty (i.e. <>) then the Mail From result will be used for SPF Received (e.g. Mail From None even if HELO is Pass). The policy server skips SPF checks for connections from the localhost (127.) and -instead prepends and logs 'SPF skipped - localhost is always allowed.' +instead prepends and logs 'SPF skipped - localhost is always allowed.' If you +have relays that you want to skip SPF checks for, you can add them to +relay_addresses on line 78 using standard CIDR notation in a space separated +list. For these addresses, 'X-Comment SPF skipped for whitelisted relay' is +prepended and logged. Error conditions within the policy server (that don't result in a crash) or from Mail::SPF will return DUNNO. From 937657ed40f92498954800ad3a317e574e7aba6a Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Tue, 17 Apr 2007 14:03:37 +0000 Subject: [PATCH 37/87] Update debian dir for 2.003. --- debian/INSTALL.Debian | 21 --------------------- debian/changelog | 2 ++ debian/docs | 2 -- debian/postfix-policyd-spf-perl.8 | 2 +- 4 files changed, 3 insertions(+), 24 deletions(-) delete mode 100644 debian/INSTALL.Debian delete mode 100644 debian/docs diff --git a/debian/INSTALL.Debian b/debian/INSTALL.Debian deleted file mode 100644 index da19cd9..0000000 --- a/debian/INSTALL.Debian +++ /dev/null @@ -1,21 +0,0 @@ -Installing ----------- - - 1. Add the following to /etc/postfix/master.cf: - - policy unix - n n - - spawn - user=nobody argv=/usr/bin/perl /usr/sbin/postfix-policyd-spf-perl - - 2. Configure the Postfix policy service in /etc/postfix/main.cf: - - smtpd_recipient_restrictions = - ... - reject_unauth_destination - check_policy_service unix:private/policy - ... - - NOTE: Specify check_policy_service AFTER reject_unauth_destination or - else your system can become an open relay. - - 3. Restart Postfix. - diff --git a/debian/changelog b/debian/changelog index c5d6a03..030974d 100644 --- a/debian/changelog +++ b/debian/changelog @@ -3,6 +3,8 @@ postfix-policyd-spf-perl (2.003-0ubuntu1) gutsy; urgency=low * New upstream release - Add handler for list of relay addresses to bypass. * Update man page for upstream changes. + * Removed INSTALL.Debian and debian/docs. + - No longer needed - redundant to the man page. -- Scott Kitterman Mon, 17 April 2007 08:52:00 -0400 diff --git a/debian/docs b/debian/docs deleted file mode 100644 index 22772e8..0000000 --- a/debian/docs +++ /dev/null @@ -1,2 +0,0 @@ -README -debian/INSTALL.Debian diff --git a/debian/postfix-policyd-spf-perl.8 b/debian/postfix-policyd-spf-perl.8 index 6d340be..a50495d 100644 --- a/debian/postfix-policyd-spf-perl.8 +++ b/debian/postfix-policyd-spf-perl.8 @@ -128,7 +128,7 @@ .\" ======================================================================== .\" .IX Title "postfix-policyd-spf-perl 1p" -.TH postfix-policyd-spf-perl 8p "2007-02-26" +.TH postfix-policyd-spf-perl 8p "2007-04-17" .SH "NAME" postfix-policyd-spf-perl \- pure-Perl Postfix policy daemon for SPF checking .SH "VERSION" From dc7716ff14fe63a0f4acff34ddc962dcac962079 Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Tue, 17 Apr 2007 14:09:43 +0000 Subject: [PATCH 38/87] Corrected package description in debian/control. --- debian/changelog | 1 + debian/control | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index 030974d..f35e64a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -5,6 +5,7 @@ postfix-policyd-spf-perl (2.003-0ubuntu1) gutsy; urgency=low * Update man page for upstream changes. * Removed INSTALL.Debian and debian/docs. - No longer needed - redundant to the man page. + * Removed installation hints from debian/control package description. -- Scott Kitterman Mon, 17 April 2007 08:52:00 -0400 diff --git a/debian/control b/debian/control index 92db204..2dcc68f 100644 --- a/debian/control +++ b/debian/control @@ -13,5 +13,4 @@ Recommends: postfix Description: pure-Perl Postfix policy daemon for RFC 4408 compliant SPF checking postfix-policyd-spf-perl is a Postfix SMTPd policy server for SPF checking. It is implemented in pure Perl and uses the Mail::SPF module. The SPF web - site is http://www.openspf.org/. The Postfix configuration must be changed to - check SPF. See INSTALL.Debian or man 8 postfix-policyd-spf-perl for details. + site is http://www.openspf.org/. From acfe60007f77d8603eea99b16fb2ad1545a2355d Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Wed, 18 Apr 2007 18:28:08 +0000 Subject: [PATCH 39/87] Fix header texts to meet access 5 requirements. --- postfix-policyd-spf-perl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index cb75f7e..85f5b47 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -171,7 +171,7 @@ sub exempt_localhost { my $attr = $options{attr}; if ($attr->{client_address} != '') { my $client_address = NetAddr::IP->new($attr->{client_address}); - return 'PREPEND X-Comment SPF not applicable to localhost connection, skipped check' + return 'PREPEND X-Comment: SPF not applicable to localhost connection - skipped check' if grep($_->contains($client_address), localhost_addresses); }; return 'DUNNO'; @@ -186,7 +186,7 @@ sub exempt_relay { my $attr = $options{attr}; if ($attr->{client_address} != '') { my $client_address = NetAddr::IP->new($attr->{client_address}); - return 'PREPEND X-Comment SPF skipped for whitelisted relay' + return 'PREPEND X-Comment: SPF skipped for whitelisted relay' if grep($_->contains($client_address), relay_addresses); }; return 'DUNNO'; From 2cc732f3fad60e2ce1c3e5b2e764a2ef3fc0c4db Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Wed, 18 Apr 2007 18:41:31 +0000 Subject: [PATCH 40/87] Update for 2.004 release --- CHANGES | 3 +++ debian/changelog | 3 ++- debian/postfix-policyd-spf-perl.8 | 6 +++--- postfix-policyd-spf-perl | 4 ++-- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index 59fb2b6..685bc08 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,9 @@ # ! = Changed something significant, or removed a feature # * = Fixed a bug, or made a minor improvement +--- 2.004 (2007-04-18 15:36) + * Fix header text to work with Postfix (access 5 requirements). + --- 2.003 (2007-04-17 08:50) * Minor documentation cleanup. + Add handler for list of relay addresses to bypass. diff --git a/debian/changelog b/debian/changelog index f35e64a..f923fd9 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,7 +1,8 @@ -postfix-policyd-spf-perl (2.003-0ubuntu1) gutsy; urgency=low +postfix-policyd-spf-perl (2.004-0ubuntu1) gutsy; urgency=low * New upstream release - Add handler for list of relay addresses to bypass. + - Correct header text so it's actually prepended. * Update man page for upstream changes. * Removed INSTALL.Debian and debian/docs. - No longer needed - redundant to the man page. diff --git a/debian/postfix-policyd-spf-perl.8 b/debian/postfix-policyd-spf-perl.8 index a50495d..02ddddf 100644 --- a/debian/postfix-policyd-spf-perl.8 +++ b/debian/postfix-policyd-spf-perl.8 @@ -128,12 +128,12 @@ .\" ======================================================================== .\" .IX Title "postfix-policyd-spf-perl 1p" -.TH postfix-policyd-spf-perl 8p "2007-04-17" +.TH postfix-policyd-spf-perl 8p "2007-04-18" .SH "NAME" postfix-policyd-spf-perl \- pure-Perl Postfix policy daemon for SPF checking .SH "VERSION" .IX Header "VERSION" -2\.003 +2\.004 .SH "USAGE" .IX Header "USAGE" @@ -171,7 +171,7 @@ The policy server skips SPF checks for connections from the localhost (127.) and instead prepends and logs 'SPF skipped - localhost is always allowed.' If you have relays that you want to skip SPF checks for, you can add them to relay_addresses on line 78 using standard CIDR notation in a space separated -list. For these addresses, 'X-Comment SPF skipped for whitelisted relay' is +list. For these addresses, 'X-Comment: SPF skipped for whitelisted relay' is prepended and logged. Error conditions within the policy server (that don't result in a crash) or from diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index 85f5b47..41767d7 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -2,7 +2,7 @@ # postfix-policyd-spf-perl # http://www.openspf.org/Software -# version 2.003 +# version 2.004 # # (C) 2007 Scott Kitterman # (C) 2007 Julian Mehnle @@ -22,7 +22,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -use version; our $VERSION = qv('2.003'); +use version; our $VERSION = qv('2.004'); use strict; From 5a78efaa2edb14d16af10d55141f132c514bf258 Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Wed, 18 Apr 2007 18:42:56 +0000 Subject: [PATCH 41/87] Update README for 2.004. --- README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README b/README index 5081964..568c1cb 100644 --- a/README +++ b/README @@ -30,7 +30,7 @@ The policy server skips SPF checks for connections from the localhost (127.) and instead prepends and logs 'SPF skipped - localhost is always allowed.' If you have relays that you want to skip SPF checks for, you can add them to relay_addresses on line 78 using standard CIDR notation in a space separated -list. For these addresses, 'X-Comment SPF skipped for whitelisted relay' is +list. For these addresses, 'X-Comment: SPF skipped for whitelisted relay' is prepended and logged. Error conditions within the policy server (that don't result in a crash) or from From bd71097eaacd26ae02b43180a6984723dbe28986 Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Wed, 18 Apr 2007 19:46:06 +0000 Subject: [PATCH 42/87] Packaging cleanup from 2.004. --- debian/changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index f923fd9..02de124 100644 --- a/debian/changelog +++ b/debian/changelog @@ -8,7 +8,7 @@ postfix-policyd-spf-perl (2.004-0ubuntu1) gutsy; urgency=low - No longer needed - redundant to the man page. * Removed installation hints from debian/control package description. - -- Scott Kitterman Mon, 17 April 2007 08:52:00 -0400 + -- Scott Kitterman Tue, 18 April 2007 15:45:00 -0400 postfix-policyd-spf-perl (2.002-0ubuntu1) feisty; urgency=low From e8211e7c8913488240a8014b682bde6a541d1169 Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Thu, 19 Apr 2007 00:27:59 +0000 Subject: [PATCH 43/87] Adjust debian dir for Debian instead of Ubuntu. --- debian/changelog | 38 ++------------------------------------ debian/control | 3 +-- debian/copyright | 2 +- 3 files changed, 4 insertions(+), 39 deletions(-) diff --git a/debian/changelog b/debian/changelog index 02de124..4c8f36c 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,39 +1,5 @@ -postfix-policyd-spf-perl (2.004-0ubuntu1) gutsy; urgency=low +postfix-policyd-spf-perl (2.004-1) unstable; urgency=low - * New upstream release - - Add handler for list of relay addresses to bypass. - - Correct header text so it's actually prepended. - * Update man page for upstream changes. - * Removed INSTALL.Debian and debian/docs. - - No longer needed - redundant to the man page. - * Removed installation hints from debian/control package description. + * Initial Debian package (Closes: #236701) -- Scott Kitterman Tue, 18 April 2007 15:45:00 -0400 - -postfix-policyd-spf-perl (2.002-0ubuntu1) feisty; urgency=low - - * New upstream release to prevent appending multiple SPF received headers to - multi-recipient mail.. - * changed maintainer back to MOTU. - * Added Julian Mehnle to debian/copyright. - * Updated man pages for upstream documentation updates. - - -- Scott Kitterman Tue, 20 Feb 2007 05:45:00 -0500 - -postfix-policyd-spf-perl (2.001-0ubuntu1) feisty; urgency=low - - * New upstream release for RFC compliant SPF checking. - * Updated control to use libmail-spf-perl instead of the unmaintained - libmail-spf-query-perl and changed maintainer to Scott Kitterman (was - MOTU). Updated package description. - * Updated man pages for new library and upstream documentation updates. - - -- Scott Kitterman Tue, 06 Feb 2007 08:00:00 -0500 - -postfix-policyd-spf-perl (1.08.1-0ubuntu1) feisty; urgency=low - - * Initial debianization of the package. - * Removed upstream provided /debian directory - * Modified documentation for Debian specific file locations - - -- Scott Kitterman Thu, 11 Jan 2007 04:59:08 -0500 diff --git a/debian/control b/debian/control index 2dcc68f..584b34f 100644 --- a/debian/control +++ b/debian/control @@ -1,8 +1,7 @@ Source: postfix-policyd-spf-perl Section: mail Priority: extra -Maintainer: Ubuntu MOTU Developers -XSBC-Original-Maintainer: Scott Kitterman +Maintainer: Scott Kitterman Build-Depends: debhelper (>= 5) Standards-Version: 3.7.2 diff --git a/debian/copyright b/debian/copyright index 13a4e65..20a38a0 100644 --- a/debian/copyright +++ b/debian/copyright @@ -1,7 +1,7 @@ This package was debianized by Scott Kitterman on Thu, 11 Jan 2007 04:29:13 -0500. -It was downloaded from . +It was downloaded from . Upstream authors Meng Weng Wong , Scott Kitterman , and Julian Mehnle From ce45ad4c60c202679ffdc7dfb8c7ec35364073f2 Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Mon, 30 Apr 2007 22:11:36 +0000 Subject: [PATCH 44/87] Update docs for new master.cf recommendations. --- CHANGES | 3 +++ INSTALL | 2 +- debian/postfix-policyd-spf-perl.8 | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 685bc08..eceac30 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,9 @@ # + = Added a feature (in a backwards compatible way) # ! = Changed something significant, or removed a feature # * = Fixed a bug, or made a minor improvement +--- UNRELEASED (2007-04-30 19:03) + * Adjust master.cf recommendations in INSTALL for new recommendations from + Weitse Venema (postfix-users mailing list). --- 2.004 (2007-04-18 15:36) * Fix header text to work with Postfix (access 5 requirements). diff --git a/INSTALL b/INSTALL index 282b9a6..c334fd0 100644 --- a/INSTALL +++ b/INSTALL @@ -16,7 +16,7 @@ Installing 2. Add the following to /etc/postfix/master.cf: - policy unix - n n - - spawn + policy unix - n n - 0 spawn user=nobody argv=/usr/bin/perl /usr/local/lib/policyd-spf-perl 3. Configure the Postfix policy service in /etc/postfix/main.cf: diff --git a/debian/postfix-policyd-spf-perl.8 b/debian/postfix-policyd-spf-perl.8 index 02ddddf..9321668 100644 --- a/debian/postfix-policyd-spf-perl.8 +++ b/debian/postfix-policyd-spf-perl.8 @@ -236,7 +236,7 @@ If you want more detail in the system logs change $VERBOSE to 1. 1. Add the following to /etc/postfix/master.cf: - policy unix - n n - - spawn + policy unix - n n - 0 spawn user=nobody argv=/usr/bin/perl /usr/sbin/policyd-spf-perl 2. Configure the Postfix policy service in /etc/postfix/main.cf: From 83d2c72f685a8fec7b1f66a58963658589172f4b Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Mon, 28 May 2007 18:33:29 +0000 Subject: [PATCH 45/87] Correct executable name in Postfix integration section of man page. --- debian/postfix-policyd-spf-perl.8 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/postfix-policyd-spf-perl.8 b/debian/postfix-policyd-spf-perl.8 index 9321668..9e94b97 100644 --- a/debian/postfix-policyd-spf-perl.8 +++ b/debian/postfix-policyd-spf-perl.8 @@ -237,7 +237,7 @@ If you want more detail in the system logs change $VERBOSE to 1. 1. Add the following to /etc/postfix/master.cf: policy unix - n n - 0 spawn - user=nobody argv=/usr/bin/perl /usr/sbin/policyd-spf-perl + user=nobody argv=/usr/bin/perl /usr/sbin/postfix-policyd-spf-perl 2. Configure the Postfix policy service in /etc/postfix/main.cf: From 0465ef6c156e5c3ad8f8249806c14475c6bb8d8a Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Mon, 9 Jul 2007 20:23:20 +0000 Subject: [PATCH 46/87] Added more test cases --- test_cases | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/test_cases b/test_cases index 0a4056c..55d5d5e 100644 --- a/test_cases +++ b/test_cases @@ -72,10 +72,27 @@ instance=3 #localhost bypass request=smtpd_access_policy -client_address=192.168.0.1 +client_address=127.0.0.1 helo_name=mailout03.controlledmail.com sender=scott@kitterman.com recipient=bogus2@kitterman.org queue_id=q1234 instance=12 +#Helo Neutral and mfrom pass +request=smtpd_access_policy +client_address=72.81.252.18 +helo_name=aol.com +sender=scott@kitterman.com +recipient=bogus2@kitterman.org +queue_id=q1234 +instance=13 + +#Whitelist +request=smtpd_access_policy +client_address=192.168.0.1 +helo_name=mailout03.controlledmail.com +sender=scott@kitterman.com +recipient=bogus2@kitterman.org +queue_id=q1234 +instance=14 \ No newline at end of file From b6063a747d25599033057978ec687f8aecb9f555 Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Mon, 9 Jul 2007 20:24:11 +0000 Subject: [PATCH 47/87] Corrected one test case. --- test_cases | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_cases b/test_cases index 55d5d5e..499e614 100644 --- a/test_cases +++ b/test_cases @@ -95,4 +95,4 @@ helo_name=mailout03.controlledmail.com sender=scott@kitterman.com recipient=bogus2@kitterman.org queue_id=q1234 -instance=14 \ No newline at end of file +instance=14 From a3aa9ff55c0d5ea43f5985c5e6f6bc1cbd35f6f5 Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Mon, 13 Aug 2007 15:59:43 +0000 Subject: [PATCH 48/87] Added test cases. --- test_cases | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test_cases b/test_cases index 499e614..75782e4 100644 --- a/test_cases +++ b/test_cases @@ -96,3 +96,31 @@ sender=scott@kitterman.com recipient=bogus2@kitterman.org queue_id=q1234 instance=14 + +# multi-recipient mfrom fail +request=smtpd_access_policy +client_address=72.81.252.19 +helo_name=mailout00.controlledmail.com +sender=scott@kitterman.org +recipient=bogus2@kitterman.org +queue_id=q1234 +instance=4 + +# multi-recipient HELO fail and mfrom pass +request=smtpd_access_policy +client_address=72.81.252.18 +helo_name=mailout00.controlledmail.com +sender=scott@kitterman.com +recipient=bogus2@kitterman.org +queue_id=q1234 +instance=2 + +# No SPF +request=smtpd_access_policy +client_address=72.81.252.18 +helo_name=mailout00.yahoo.com +sender=scott@yahoo.com +recipient=bogus2@kitterman.org +queue_id=q1234 +instance=15 + From c4938b0da1c63d8b28fed80fa64016651fee0fff Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Wed, 17 Oct 2007 19:48:08 +0000 Subject: [PATCH 49/87] Add permerror test case. --- test_cases | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test_cases b/test_cases index 75782e4..9a1442e 100644 --- a/test_cases +++ b/test_cases @@ -124,3 +124,11 @@ recipient=bogus2@kitterman.org queue_id=q1234 instance=15 +# Permerror reject +request=smtpd_access_policy +client_address=72.81.252.18 +helo_name=mailout00.yahoo.com +sender=scott@elvey.com +recipient=bogus2@kitterman.org +queue_id=q1234 +instance=16 From 1486640042b77fff185f4978bf8b33d31ace8f48 Mon Sep 17 00:00:00 2001 From: Julian Mehnle <> Date: Fri, 14 Dec 2007 23:05:20 +0000 Subject: [PATCH 50/87] postfix-policyd-spf-perl/trunk/postfix-policyd-spf-perl * Bumped version number to 2.005 in prospect of the next release. * Decreased timeout for DNS queries via UDP to 10s from Net::DNS::Resolver's default of 40s (by doing only 1 retransmission rather than 3 after a query fails). Until Mail::SPF provides an explicit option for this, we just create our own resolver object and make Mail::SPF use that. postfix-policyd-spf-perl/trunk/CHANGES * Described the above change. * Fixed typos in the names of Wietse Venema and Julian Mehnle. --- CHANGES | 9 +++++++-- postfix-policyd-spf-perl | 14 +++++++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index eceac30..33f5fff 100644 --- a/CHANGES +++ b/CHANGES @@ -3,9 +3,14 @@ # + = Added a feature (in a backwards compatible way) # ! = Changed something significant, or removed a feature # * = Fixed a bug, or made a minor improvement + --- UNRELEASED (2007-04-30 19:03) + * Decreased timeout for DNS queries via UDP to 10s from Net::DNS::Resolver's + default of 40s (by doing only 1 retransmission rather than 3 after a query + fails). Until Mail::SPF provides an explicit option for this, we just + create our own resolver object and make Mail::SPF use that. * Adjust master.cf recommendations in INSTALL for new recommendations from - Weitse Venema (postfix-users mailing list). + Wietse Venema (postfix-users mailing list). --- 2.004 (2007-04-18 15:36) * Fix header text to work with Postfix (access 5 requirements). @@ -15,7 +20,7 @@ + Add handler for list of relay addresses to bypass. --- 2.002 (2007-02-20 05:45) - * Added Julian Menhle to copyright statement. + * Added Julian Mehnle to copyright statement. * Implemented results cache in order to prevent redundant SPF checks in multiple invocations per message instance. This also enables us to prepend a "Received-SPF" header only once per message instance (as opposed to once diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index 41767d7..3915555 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -2,7 +2,7 @@ # postfix-policyd-spf-perl # http://www.openspf.org/Software -# version 2.004 +# version 2.005 # # (C) 2007 Scott Kitterman # (C) 2007 Julian Mehnle @@ -22,7 +22,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -use version; our $VERSION = qv('2.004'); +use version; our $VERSION = qv('2.005'); use strict; @@ -35,7 +35,15 @@ use Mail::SPF; # configuration # ---------------------------------------------------------- -my $spf_server = Mail::SPF::Server->new(); +my $resolver = Net::DNS::Resolver->new( + retrans => 5, # Net::DNS::Resolver default: 5 + retry => 2, # Net::DNS::Resolver default: 4 + # Makes for a total timeout for UDP queries of 5s * 2 = 10s. +); + +my $spf_server = Mail::SPF::Server->new( + dns_resolver => $resolver +); # Adding more handlers is easy: my @HANDLERS = ( From f778a5ebbfc41d0150ebae52414106c0fb32a007 Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Sat, 15 Dec 2007 06:20:30 +0000 Subject: [PATCH 51/87] Documentation cleanup. --- CHANGES | 3 ++- INSTALL | 4 +++- README | 24 ++++++++++++------------ test_cases | 17 +++++++++++++++-- 4 files changed, 32 insertions(+), 16 deletions(-) diff --git a/CHANGES b/CHANGES index 33f5fff..cd9e420 100644 --- a/CHANGES +++ b/CHANGES @@ -4,13 +4,14 @@ # ! = Changed something significant, or removed a feature # * = Fixed a bug, or made a minor improvement ---- UNRELEASED (2007-04-30 19:03) +--- 2.005 (2007-12-14 23:29 -0500) * Decreased timeout for DNS queries via UDP to 10s from Net::DNS::Resolver's default of 40s (by doing only 1 retransmission rather than 3 after a query fails). Until Mail::SPF provides an explicit option for this, we just create our own resolver object and make Mail::SPF use that. * Adjust master.cf recommendations in INSTALL for new recommendations from Wietse Venema (postfix-users mailing list). + * Other minor documentation cleanup --- 2.004 (2007-04-18 15:36) * Fix header text to work with Postfix (access 5 requirements). diff --git a/INSTALL b/INSTALL index c334fd0..e1277ac 100644 --- a/INSTALL +++ b/INSTALL @@ -30,5 +30,7 @@ Installing NOTE: Specify check_policy_service AFTER reject_unauth_destination or else your system can become an open relay. - 4. Restart Postfix. + 4. Add "policy_time_limit = 3600" to main.cf + + 5. Restart Postfix. diff --git a/README b/README index 568c1cb..c6f11e5 100644 --- a/README +++ b/README @@ -1,6 +1,6 @@ -postfix-policyd-spf-perl 2.003 +postfix-policyd-spf-perl 2.005 A Postfix SMTPd policy server for SPF checking -(C) 2007 Scott Kitterman and Julian Mehnle +(C) 2007 Scott Kitterman and Julian Mehnle (C) 2003-2004 Meng Weng Wong Thanks for contributions by various members of the SPF project @@ -10,31 +10,31 @@ Thanks for contributions by various members of the SPF project postfix-policyd-spf-perl is a Postfix SMTPd policy daemon for SPF checking. It is implemented in pure Perl and uses the Mail::SPF CPAN module. Note that Mail::SPF is a complete re-implementation of SPF based on the final SPF RFC, -RFC 4408. It shares no code with the older Mail::SPF::Query that was the -original SPF development implementation. If you are upgrading from on older +RFC 4408. It shares no code with the older Mail::SPF::Query that was the +original SPF development implementation. If you are upgrading from on older version of this policy server you will need to install Mail::SPF. -This version of the policy server always checks HELO before Mail From (older -versions just checked HELO if Mail From was null). It will reject mail that -fails either Mail From or HELO SPF checks. It will defer mail if there is a +This version of the policy server always checks HELO before Mail From (older +versions just checked HELO if Mail From was null). It will reject mail that +fails either Mail From or HELO SPF checks. It will defer mail if there is a temporary SPF error and the message would othersise be permitted (DEFER_IF_PERMIT). If the HELO check produces a REJECT/DEFER result, Mail From will not be checked. -If the message is not rejected or deferred, the policy server will PREPEND the +If the message is not rejected or deferred, the policy server will PREPEND the appropriate SPF Received header. If Mail From is anything other than completely empty (i.e. <>) then the Mail From result will be used for SPF Received (e.g. Mail From None even if HELO is Pass). The policy server skips SPF checks for connections from the localhost (127.) and -instead prepends and logs 'SPF skipped - localhost is always allowed.' If you -have relays that you want to skip SPF checks for, you can add them to +instead prepends and logs 'SPF skipped - localhost is always allowed.' If you +have relays that you want to skip SPF checks for, you can add them to relay_addresses on line 78 using standard CIDR notation in a space separated list. For these addresses, 'X-Comment: SPF skipped for whitelisted relay' is prepended and logged. -Error conditions within the policy server (that don't result in a crash) or from -Mail::SPF will return DUNNO. +Error conditions within the policy server (that don't result in a crash) or from +Mail::SPF will return DUNNO. See INSTALL for installation instructions. diff --git a/test_cases b/test_cases index 9a1442e..ccd0553 100644 --- a/test_cases +++ b/test_cases @@ -19,7 +19,7 @@ instance=2 #no HELO and mfrom pass request=smtpd_access_policy client_address=72.81.252.18 -helo_name=mailout03.controlledmail.com +helo_name=72.81.252.18 sender=scott@kitterman.com recipient=bogus@kitterman.org queue_id=q1234 @@ -127,8 +127,21 @@ instance=15 # Permerror reject request=smtpd_access_policy client_address=72.81.252.18 -helo_name=mailout00.yahoo.com +helo_name=elvey.com sender=scott@elvey.com recipient=bogus2@kitterman.org queue_id=q1234 instance=16 + +# None and None +request=smtpd_access_policy +client_address=71.17.127.27 +helo_name=71-17-127-27.estv.hsdb.sasknet.sk.ca +sender=dalbecbhoj@accessamericatransport.com +recipient=hostmaster@jamux.com + +request=smtpd_access_policy +client_address=200.120.31.84 +helo_name=autohaus-knabe.de +sender=daniel.hahnomjy@autohaus-knabe.de +recipient=jam@jamux.com \ No newline at end of file From 258fa566345643e251d3b797143cc14b9b41fd7a Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Sat, 15 Dec 2007 06:23:14 +0000 Subject: [PATCH 52/87] Debian packaging for 2.005 --- debian/changelog | 18 ++++++ debian/control | 12 ++-- debian/copyright | 58 ++----------------- ...-spf-perl.8 => postfix-policyd-spf-perl.1} | 39 +++++++------ debian/rules | 3 +- debian/watch | 2 + 6 files changed, 52 insertions(+), 80 deletions(-) rename debian/{postfix-policyd-spf-perl.8 => postfix-policyd-spf-perl.1} (94%) create mode 100644 debian/watch diff --git a/debian/changelog b/debian/changelog index 4c8f36c..99ac609 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,21 @@ +postfix-policyd-spf-perl (2.005-1) unstable; urgency=low + + * New upstream release + - Reduce DNS timeout period from 20 seconds to 10 seconds for faster + performance with broken DNS servers + * Move postfix from recommends to depends + * Add watch file + * Correct debian/copyright to only refer to GPL V2 and trim excess license + quoting + * Added Homepage field + * Update standards version to 3.7.3 without further change + * Clarify package description + * Minor updates to postfix-policyd-spf-perl and move from section 8 to + section 1 + * Minor white space cleanup in debian/control + + -- Scott Kitterman Fri, 14 Dec 2007 23:40:45 -0500 + postfix-policyd-spf-perl (2.004-1) unstable; urgency=low * Initial Debian package (Closes: #236701) diff --git a/debian/control b/debian/control index 584b34f..d4a7c5d 100644 --- a/debian/control +++ b/debian/control @@ -3,13 +3,13 @@ Section: mail Priority: extra Maintainer: Scott Kitterman Build-Depends: debhelper (>= 5) -Standards-Version: 3.7.2 +Standards-Version: 3.7.3 +Homepage: http://www.openspf.org/Software Package: postfix-policyd-spf-perl Architecture: all -Depends: libversion-perl, libnetaddr-ip-perl (>= 4), libmail-spf-perl, ${perl:Depends} -Recommends: postfix +Depends: postfix, libversion-perl, libnetaddr-ip-perl (>= 4), libmail-spf-perl, ${perl:Depends} Description: pure-Perl Postfix policy daemon for RFC 4408 compliant SPF checking - postfix-policyd-spf-perl is a Postfix SMTPd policy server for SPF checking. - It is implemented in pure Perl and uses the Mail::SPF module. The SPF web - site is http://www.openspf.org/. + postfix-policyd-spf-perl is a Postfix SMTP policy server for SPF checking. + It is implemented in pure Perl and uses the Mail::SPF module. The SPF + project web site is http://www.openspf.org/. diff --git a/debian/copyright b/debian/copyright index 20a38a0..e6d550f 100644 --- a/debian/copyright +++ b/debian/copyright @@ -1,19 +1,19 @@ -This package was debianized by Scott Kitterman on +This package was debianized by Scott Kitterman on Thu, 11 Jan 2007 04:29:13 -0500. It was downloaded from . -Upstream authors Meng Weng Wong , Scott Kitterman +Upstream authors Meng Weng Wong , Scott Kitterman , and Julian Mehnle Copyright: -(C) 2007 Scott Kitterman and Julian Mehnle +(C) 2007 Scott Kitterman and Julian Mehnle (C) 2003-2004 Meng Weng Wong This is free software; you can redistribute it and/or modify it under the terms -of the GNU General Public License (version 2 or later). +of the GNU General Public License (version 2). GNU GENERAL PUBLIC LICENSE Version 2, June 1991 @@ -23,56 +23,6 @@ of the GNU General Public License (version 2 or later). Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - On Debian systems, the complete text of the GPL v2 can be found here: /usr/share/common-licenses/GPL-2 diff --git a/debian/postfix-policyd-spf-perl.8 b/debian/postfix-policyd-spf-perl.1 similarity index 94% rename from debian/postfix-policyd-spf-perl.8 rename to debian/postfix-policyd-spf-perl.1 index 9e94b97..9910799 100644 --- a/debian/postfix-policyd-spf-perl.8 +++ b/debian/postfix-policyd-spf-perl.1 @@ -128,12 +128,12 @@ .\" ======================================================================== .\" .IX Title "postfix-policyd-spf-perl 1p" -.TH postfix-policyd-spf-perl 8p "2007-04-18" +.TH postfix-policyd-spf-perl 1 "2007-12-14" .SH "NAME" -postfix-policyd-spf-perl \- pure-Perl Postfix policy daemon for SPF checking +postfix-policyd-spf-perl \- pure-Perl Postfix policy server for SPF checking .SH "VERSION" .IX Header "VERSION" -2\.004 +2\.005 .SH "USAGE" .IX Header "USAGE" @@ -148,34 +148,34 @@ SMTPD_POLICY_README. .SH "SYNOPSIS" .IX Header "SYNOPSIS" -postfix-policyd-spf-perl is a Postfix SMTPd policy daemon for SPF checking. +postfix-policyd-spf-perl is a Postfix SMTP policy server for SPF checking. It is implemented in pure Perl and uses the Mail::SPF CPAN module. Note that Mail::SPF is a complete re-implementation of SPF based on the final SPF RFC, -RFC 4408. It shares no code with the older Mail::SPF::Query that was the -original SPF development implementation. If you are upgrading from on older +RFC 4408. It shares no code with the older Mail::SPF::Query that was the +original SPF development implementation. If you are upgrading from on older version of this policy server you will need to install Mail::SPF. -This version of the policy server always checks HELO before Mail From (older -versions just checked HELO if Mail From was null). It will reject mail that -fails either Mail From or HELO SPF checks. It will defer mail if there is a -temporary SPF error and the message would othersise be permitted +This version of the policy server always checks HELO before Mail From (older +versions just checked HELO if Mail From was null). It will reject mail that +fails either Mail From or HELO SPF checks. It will defer mail if there is a +temporary SPF error and the message would othersise be permitted (DEFER_IF_PERMIT). If the HELO check produces a REJECT/DEFER result, Mail From will not be checked. -If the message is not rejected or deferred, the policy server will PREPEND the +If the message is not rejected or deferred, the policy server will PREPEND the appropriate SPF Received header. If Mail From is anything other than completely empty (i.e. <>) then the Mail From result will be used for SPF Received (e.g. -Mail From None even if HELO is Pass). +Mail From None even if HELO is Pass). The policy server skips SPF checks for connections from the localhost (127.) and -instead prepends and logs 'SPF skipped - localhost is always allowed.' If you -have relays that you want to skip SPF checks for, you can add them to +instead prepends and logs 'SPF skipped - localhost is always allowed.' If you +have relays that you want to skip SPF checks for, you can add them to relay_addresses on line 78 using standard CIDR notation in a space separated list. For these addresses, 'X-Comment: SPF skipped for whitelisted relay' is prepended and logged. -Error conditions within the policy server (that don't result in a crash) or from -Mail::SPF will return DUNNO. +Error conditions within the policy server (that don't result in a crash) or from +Mail::SPF will return DUNNO. .SH "DESCRIPTION" .IX Header "DESCRIPTION" @@ -186,7 +186,7 @@ Each time a Postfix SMTP server process is started it connects to the policy service socket and Postfix runs one instance of this Perls script. By default, a Postfix SMTP server process terminates after 100 seconds of idle time, or after serving 100 clients. Thus, the cost of starting this Perl -script is smoothed over time +script is smoothed over time. The default policy_time_limit is 1000 seconds. This may be too short for some SMTP transactions to complete. As recommended in SMTPD_POLICY_README, this @@ -201,7 +201,7 @@ To test the policy daemon by hand, execute: % perl /usr/sbin/postfix-policyd-spf-perl -Each query is a bunch of attributes. Order does not matter, and the daemon +Each query is a bunch of attributes. Order does not matter, and the server uses only a few of all the attributes shown below: request=smtpd_access_policy @@ -259,8 +259,9 @@ libmail-spf-perl, .SH "AUTHORS" .IX Header "AUTHORS" -This version of \fBpolicyd-spf-perl\fR was written by Meng Weng Wong +This version of \fBpolicyd-spf-perl\fR was written by Meng Weng Wong and updated for libmail-spf-perl by Scott Kitterman and Julian Mehnle . .PP This man-page was written by Scott Kitterman . + diff --git a/debian/rules b/debian/rules index 9bf0142..f99af48 100755 --- a/debian/rules +++ b/debian/rules @@ -31,7 +31,7 @@ binary-indep: build install dh_testroot dh_installdocs - dh_installman debian/postfix-policyd-spf-perl.8 + dh_installman debian/postfix-policyd-spf-perl.1 dh_installchangelogs CHANGES dh_compress @@ -49,3 +49,4 @@ binary-arch: binary: binary-indep binary-arch .PHONY: build clean binary-indep binary-arch binary install + diff --git a/debian/watch b/debian/watch new file mode 100644 index 0000000..a941ba1 --- /dev/null +++ b/debian/watch @@ -0,0 +1,2 @@ +version=3 +http://www.openspf.org/blobs/postfix-policyd-spf-perl-(.*)\.tar\.gz debian uupdate From 94b4dabfaf3d3325648129bca78b7e28b8f70d91 Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Sun, 16 Dec 2007 03:17:20 +0000 Subject: [PATCH 53/87] Final changes for 2.005-1 upload to Debian. --- debian/changelog | 9 +++++++-- debian/control | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/debian/changelog b/debian/changelog index 99ac609..5c7acd1 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,11 +1,12 @@ postfix-policyd-spf-perl (2.005-1) unstable; urgency=low + [ Scott Kitterman ] * New upstream release - Reduce DNS timeout period from 20 seconds to 10 seconds for faster performance with broken DNS servers * Move postfix from recommends to depends * Add watch file - * Correct debian/copyright to only refer to GPL V2 and trim excess license + * Correct debian/copyright to only refer to GPL V2 and trim excess license quoting * Added Homepage field * Update standards version to 3.7.3 without further change @@ -14,7 +15,11 @@ postfix-policyd-spf-perl (2.005-1) unstable; urgency=low section 1 * Minor white space cleanup in debian/control - -- Scott Kitterman Fri, 14 Dec 2007 23:40:45 -0500 + [ Philipp Kern ] + * Added `XS-DM-Upload-Allowed: yes' to `debian/control' to allow uploads + from Debian Maintainers. + + -- Scott Kitterman Sat, 15 Dec 2007 20:32:55 +0100 postfix-policyd-spf-perl (2.004-1) unstable; urgency=low diff --git a/debian/control b/debian/control index d4a7c5d..a71d346 100644 --- a/debian/control +++ b/debian/control @@ -5,6 +5,7 @@ Maintainer: Scott Kitterman Build-Depends: debhelper (>= 5) Standards-Version: 3.7.3 Homepage: http://www.openspf.org/Software +XS-DM-Upload-Allowed: yes Package: postfix-policyd-spf-perl Architecture: all From bb35819cd5807fe5cbed7ef7f7be7aebb4cf04a6 Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Sun, 16 Dec 2007 03:32:48 +0000 Subject: [PATCH 54/87] Fix Debian priority for next upload. --- debian/changelog | 6 ++++++ debian/control | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/debian/changelog b/debian/changelog index 5c7acd1..eb9e8df 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +postfix-policyd-spf-perl (2.005-2) UNRELEASED; urgency=low + + * Correct priority (change back to optional) + + -- Scott Kitterman Sat, 15 Dec 2007 22:30:55 -0500 + postfix-policyd-spf-perl (2.005-1) unstable; urgency=low [ Scott Kitterman ] diff --git a/debian/control b/debian/control index a71d346..dd3fe3d 100644 --- a/debian/control +++ b/debian/control @@ -1,6 +1,6 @@ Source: postfix-policyd-spf-perl Section: mail -Priority: extra +Priority: optional Maintainer: Scott Kitterman Build-Depends: debhelper (>= 5) Standards-Version: 3.7.3 From 2fddc34a57753358db610454bb8ca0ae2628e8df Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Fri, 18 Jul 2008 03:27:59 +0000 Subject: [PATCH 55/87] Add more logging and correct test case. --- postfix-policyd-spf-perl | 22 +++++++++++++++++++--- test_cases | 2 +- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index 3915555..04e4ec2 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -2,9 +2,9 @@ # postfix-policyd-spf-perl # http://www.openspf.org/Software -# version 2.005 +# version 2.006 # -# (C) 2007 Scott Kitterman +# (C) 2007-2008 Scott Kitterman # (C) 2007 Julian Mehnle # (C) 2003-2004 Meng Weng Wong # @@ -22,7 +22,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -use version; our $VERSION = qv('2.005'); +use version; our $VERSION = qv('2.006'); use strict; @@ -259,12 +259,24 @@ sub sender_policy_framework { # Reject on HELO fail. Defer on HELO temperror if message would otherwise # be accepted. Use the HELO result and return for null sender. if ($helo_result->is_code('fail')) { + syslog( + info => "%s: SPF %s: HELO/EHLO: %s", + $attr->{queue_id}, $helo_result, $attr->{helo_name} + ); return "550 $helo_authority_exp"; } elsif ($helo_result->is_code('temperror')) { + syslog( + info => "%s: SPF %s: HELO/EHLO: %s", + $attr->{queue_id}, $helo_result, $attr->{helo_name} + ); return "DEFER_IF_PERMIT SPF-Result=$helo_local_exp"; } elsif ($attr->{sender} eq '') { + syslog( + info => "%s: SPF %s: HELO/EHLO (Null Sender): %s", + $attr->{queue_id}, $helo_result, $attr->{helo_name} + ); return "PREPEND $helo_spf_header" unless $cache->{added_spf_header}++; } @@ -317,6 +329,10 @@ sub sender_policy_framework { }; # Same approach as HELO.... + syslog( + info => "%s: SPF %s: Envelope-from: %s", + $attr->{queue_id}, $mfrom_result, $attr->{sender} + ); if ($mfrom_result->is_code('fail')) { return "550 $mfrom_authority_exp"; } diff --git a/test_cases b/test_cases index ccd0553..cae5156 100644 --- a/test_cases +++ b/test_cases @@ -25,7 +25,7 @@ recipient=bogus@kitterman.org queue_id=q1234 instance=3 -#helo pass and mfrom fail +#helo pass and mfrom pass request=smtpd_access_policy client_address=72.81.252.19 helo_name=mailout00.controlledmail.com From 2357259089a5e9551d746ab7c0d03ec89fd56563 Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Fri, 18 Jul 2008 03:46:57 +0000 Subject: [PATCH 56/87] Remove debian dir from svn - more trouble than it's worth. --- README | 12 +- debian/changelog | 34 ---- debian/compat | 1 - debian/control | 16 -- debian/copyright | 28 ---- debian/postfix-policyd-spf-perl.1 | 267 ------------------------------ debian/rules | 52 ------ debian/watch | 2 - 8 files changed, 6 insertions(+), 406 deletions(-) delete mode 100644 debian/changelog delete mode 100644 debian/compat delete mode 100644 debian/control delete mode 100644 debian/copyright delete mode 100644 debian/postfix-policyd-spf-perl.1 delete mode 100755 debian/rules delete mode 100644 debian/watch diff --git a/README b/README index c6f11e5..d9be8a9 100644 --- a/README +++ b/README @@ -1,18 +1,18 @@ -postfix-policyd-spf-perl 2.005 +postfix-policyd-spf-perl 2.006 A Postfix SMTPd policy server for SPF checking -(C) 2007 Scott Kitterman and Julian Mehnle - +(C) 2007-2008 Scott Kitterman +(C) 2007 Julian Mehnle (C) 2003-2004 Meng Weng Wong Thanks for contributions by various members of the SPF project ============================================================================== -postfix-policyd-spf-perl is a Postfix SMTPd policy daemon for SPF checking. +postfix-policyd-spf-perl is a Postfix SMTPd policy server for SPF checking. It is implemented in pure Perl and uses the Mail::SPF CPAN module. Note that Mail::SPF is a complete re-implementation of SPF based on the final SPF RFC, RFC 4408. It shares no code with the older Mail::SPF::Query that was the original SPF development implementation. If you are upgrading from on older -version of this policy server you will need to install Mail::SPF. +(<< 2.000) version of this policy server you will need to install Mail::SPF. This version of the policy server always checks HELO before Mail From (older versions just checked HELO if Mail From was null). It will reject mail that @@ -31,7 +31,7 @@ instead prepends and logs 'SPF skipped - localhost is always allowed.' If you have relays that you want to skip SPF checks for, you can add them to relay_addresses on line 78 using standard CIDR notation in a space separated list. For these addresses, 'X-Comment: SPF skipped for whitelisted relay' is -prepended and logged. +prepended and logged. IPv6 localhost is also skipped. Error conditions within the policy server (that don't result in a crash) or from Mail::SPF will return DUNNO. diff --git a/debian/changelog b/debian/changelog deleted file mode 100644 index eb9e8df..0000000 --- a/debian/changelog +++ /dev/null @@ -1,34 +0,0 @@ -postfix-policyd-spf-perl (2.005-2) UNRELEASED; urgency=low - - * Correct priority (change back to optional) - - -- Scott Kitterman Sat, 15 Dec 2007 22:30:55 -0500 - -postfix-policyd-spf-perl (2.005-1) unstable; urgency=low - - [ Scott Kitterman ] - * New upstream release - - Reduce DNS timeout period from 20 seconds to 10 seconds for faster - performance with broken DNS servers - * Move postfix from recommends to depends - * Add watch file - * Correct debian/copyright to only refer to GPL V2 and trim excess license - quoting - * Added Homepage field - * Update standards version to 3.7.3 without further change - * Clarify package description - * Minor updates to postfix-policyd-spf-perl and move from section 8 to - section 1 - * Minor white space cleanup in debian/control - - [ Philipp Kern ] - * Added `XS-DM-Upload-Allowed: yes' to `debian/control' to allow uploads - from Debian Maintainers. - - -- Scott Kitterman Sat, 15 Dec 2007 20:32:55 +0100 - -postfix-policyd-spf-perl (2.004-1) unstable; urgency=low - - * Initial Debian package (Closes: #236701) - - -- Scott Kitterman Tue, 18 April 2007 15:45:00 -0400 diff --git a/debian/compat b/debian/compat deleted file mode 100644 index 7ed6ff8..0000000 --- a/debian/compat +++ /dev/null @@ -1 +0,0 @@ -5 diff --git a/debian/control b/debian/control deleted file mode 100644 index dd3fe3d..0000000 --- a/debian/control +++ /dev/null @@ -1,16 +0,0 @@ -Source: postfix-policyd-spf-perl -Section: mail -Priority: optional -Maintainer: Scott Kitterman -Build-Depends: debhelper (>= 5) -Standards-Version: 3.7.3 -Homepage: http://www.openspf.org/Software -XS-DM-Upload-Allowed: yes - -Package: postfix-policyd-spf-perl -Architecture: all -Depends: postfix, libversion-perl, libnetaddr-ip-perl (>= 4), libmail-spf-perl, ${perl:Depends} -Description: pure-Perl Postfix policy daemon for RFC 4408 compliant SPF checking - postfix-policyd-spf-perl is a Postfix SMTP policy server for SPF checking. - It is implemented in pure Perl and uses the Mail::SPF module. The SPF - project web site is http://www.openspf.org/. diff --git a/debian/copyright b/debian/copyright deleted file mode 100644 index e6d550f..0000000 --- a/debian/copyright +++ /dev/null @@ -1,28 +0,0 @@ -This package was debianized by Scott Kitterman on -Thu, 11 Jan 2007 04:29:13 -0500. - -It was downloaded from . - -Upstream authors Meng Weng Wong , Scott Kitterman -, and Julian Mehnle - -Copyright: - -(C) 2007 Scott Kitterman and Julian Mehnle - -(C) 2003-2004 Meng Weng Wong - -This is free software; you can redistribute it and/or modify it under the terms -of the GNU General Public License (version 2). - - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -On Debian systems, the complete text of the GPL v2 can be found here: - /usr/share/common-licenses/GPL-2 - diff --git a/debian/postfix-policyd-spf-perl.1 b/debian/postfix-policyd-spf-perl.1 deleted file mode 100644 index 9910799..0000000 --- a/debian/postfix-policyd-spf-perl.1 +++ /dev/null @@ -1,267 +0,0 @@ -\" -.\" Standard preamble: -.\" ======================================================================== -.de Sh \" Subsection heading -.br -.if t .Sp -.ne 5 -.PP -\fB\\$1\fR -.PP -.. -.de Sp \" Vertical space (when we can't use .PP) -.if t .sp .5v -.if n .sp -.. -.de Vb \" Begin verbatim text -.ft CW -.nf -.ne \\$1 -.. -.de Ve \" End verbatim text -.ft R -.fi -.. -.\" Set up some character translations and predefined strings. \*(-- will -.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left -.\" double quote, and \*(R" will give a right double quote. \*(C+ will -.\" give a nicer C++. Capital omega is used to do unbreakable dashes and -.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, -.\" nothing in troff, for use with C<>. -.tr \(*W- -.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' -.ie n \{\ -. ds -- \(*W- -. ds PI pi -. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch -. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch -. ds L" "" -. ds R" "" -. ds C` "" -. ds C' "" -'br\} -.el\{\ -. ds -- \|\(em\| -. ds PI \(*p -. ds L" `` -. ds R" '' -'br\} -.\" -.\" If the F register is turned on, we'll generate index entries on stderr for -.\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index -.\" entries marked with X<> in POD. Of course, you'll have to process the -.\" output yourself in some meaningful fashion. -.if \nF \{\ -. de IX -. tm Index:\\$1\t\\n%\t"\\$2" -.. -. nr % 0 -. rr F -.\} -.\" -.\" For nroff, turn off justification. Always turn off hyphenation; it makes -.\" way too many mistakes in technical documents. -.hy 0 -.if n .na -.\" -.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). -.\" Fear. Run. Save yourself. No user-serviceable parts. -. \" fudge factors for nroff and troff -.if n \{\ -. ds #H 0 -. ds #V .8m -. ds #F .3m -. ds #[ \f1 -. ds #] \fP -.\} -.if t \{\ -. ds #H ((1u-(\\\\n(.fu%2u))*.13m) -. ds #V .6m -. ds #F 0 -. ds #[ \& -. ds #] \& -.\} -. \" simple accents for nroff and troff -.if n \{\ -. ds ' \& -. ds ` \& -. ds ^ \& -. ds , \& -. ds ~ ~ -. ds / -.\} -.if t \{\ -. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" -. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' -. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' -. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' -. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' -. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' -.\} -. \" troff and (daisy-wheel) nroff accents -.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' -.ds 8 \h'\*(#H'\(*b\h'-\*(#H' -.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] -.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' -.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' -.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] -.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] -.ds ae a\h'-(\w'a'u*4/10)'e -.ds Ae A\h'-(\w'A'u*4/10)'E -. \" corrections for vroff -.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' -.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' -. \" for low resolution devices (crt and lpr) -.if \n(.H>23 .if \n(.V>19 \ -\{\ -. ds : e -. ds 8 ss -. ds o a -. ds d- d\h'-1'\(ga -. ds D- D\h'-1'\(hy -. ds th \o'bp' -. ds Th \o'LP' -. ds ae ae -. ds Ae AE -.\} -.rm #[ #] #H #V #F C -.\" ======================================================================== -.\" -.IX Title "postfix-policyd-spf-perl 1p" -.TH postfix-policyd-spf-perl 1 "2007-12-14" -.SH "NAME" -postfix-policyd-spf-perl \- pure-Perl Postfix policy server for SPF checking -.SH "VERSION" -.IX Header "VERSION" -2\.005 - -.SH "USAGE" -.IX Header "USAGE" -Usage: - policyd-spf-perl [-v] - -.SH "OTHER DOCUMENTATION" -.IX Header "OTHER DOCUMENTATION" -This documentation assumes you have read Postfix's README_FILES/ -SMTPD_POLICY_README. - -.SH "SYNOPSIS" -.IX Header "SYNOPSIS" - -postfix-policyd-spf-perl is a Postfix SMTP policy server for SPF checking. -It is implemented in pure Perl and uses the Mail::SPF CPAN module. Note that -Mail::SPF is a complete re-implementation of SPF based on the final SPF RFC, -RFC 4408. It shares no code with the older Mail::SPF::Query that was the -original SPF development implementation. If you are upgrading from on older -version of this policy server you will need to install Mail::SPF. - -This version of the policy server always checks HELO before Mail From (older -versions just checked HELO if Mail From was null). It will reject mail that -fails either Mail From or HELO SPF checks. It will defer mail if there is a -temporary SPF error and the message would othersise be permitted -(DEFER_IF_PERMIT). If the HELO check produces a REJECT/DEFER result, Mail From -will not be checked. - -If the message is not rejected or deferred, the policy server will PREPEND the -appropriate SPF Received header. If Mail From is anything other than completely -empty (i.e. <>) then the Mail From result will be used for SPF Received (e.g. -Mail From None even if HELO is Pass). - -The policy server skips SPF checks for connections from the localhost (127.) and -instead prepends and logs 'SPF skipped - localhost is always allowed.' If you -have relays that you want to skip SPF checks for, you can add them to -relay_addresses on line 78 using standard CIDR notation in a space separated -list. For these addresses, 'X-Comment: SPF skipped for whitelisted relay' is -prepended and logged. - -Error conditions within the policy server (that don't result in a crash) or from -Mail::SPF will return DUNNO. - -.SH "DESCRIPTION" -.IX Header "DESCRIPTION" - -Logging is sent to syslogd. - -Each time a Postfix SMTP server process is started it connects to the policy -service socket and Postfix runs one instance of this Perls script. By -default, a Postfix SMTP server process terminates after 100 seconds of idle -time, or after serving 100 clients. Thus, the cost of starting this Perl -script is smoothed over time. - -The default policy_time_limit is 1000 seconds. This may be too short for some -SMTP transactions to complete. As recommended in SMTPD_POLICY_README, this -should be extended to 3600 seconds. To do so, set "policy_time_limit = 3600" -in /etc/postfix/main.cf. - -.SH "TESTING THE POLICY DAEMON" -.IX Header "TESTING THE POLICY DAEMON" -Testing the policy daemon - -To test the policy daemon by hand, execute: - - % perl /usr/sbin/postfix-policyd-spf-perl - -Each query is a bunch of attributes. Order does not matter, and the server -uses only a few of all the attributes shown below: - - request=smtpd_access_policy - protocol_state=RCPT - protocol_name=SMTP - helo_name=some.domain.tld - queue_id= - instance=71b0.45e2f5f1.d4da1.0 - sender=foo@bar.tld - recipient=bar@foo.tld - client_address=1.2.3.4 - client_name=another.domain.tld - [empty line] - -The policy daemon will answer in the same style, with an attribute list -followed by a empty line: - - action=550 Please see http://www.openspf.org/Why?id=foo@bar.tld&ip=1.2.3.4& - receiver=bar@foo.tld - [empty line] - -To test HELO checking sender should be empty: - - sender= - ... More attributes... - [empty line] - -If you want more detail in the system logs change $VERBOSE to 1. - -.SH "POSTFIX INTEGRATION" -.IX Header "POSTFIX INTEGRATION" - - 1. Add the following to /etc/postfix/master.cf: - - policy unix - n n - 0 spawn - user=nobody argv=/usr/bin/perl /usr/sbin/postfix-policyd-spf-perl - - 2. Configure the Postfix policy service in /etc/postfix/main.cf: - - smtpd_recipient_restrictions = - ... - reject_unauth_destination - check_policy_service unix:private/policy - ... - policy_time_limit = 3600 - - NOTE: Specify check_policy_service AFTER reject_unauth_destination or - else your system can become an open relay. - - 3. Restart Postfix. - -.SH "SEE ALSO" -.IX Header "SEE ALSO" -libmail-spf-perl, - -.SH "AUTHORS" -.IX Header "AUTHORS" -This version of \fBpolicyd-spf-perl\fR was written by Meng Weng Wong - and updated for libmail-spf-perl by Scott Kitterman - and Julian Mehnle . -.PP -This man-page was written by Scott Kitterman . - diff --git a/debian/rules b/debian/rules deleted file mode 100755 index f99af48..0000000 --- a/debian/rules +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/make -f - -PACKAGE = $(shell dh_listpackages) - -ifndef PERL - PERL = /usr/bin/perl -endif - -TMP = $(CURDIR)/debian/$(PACKAGE) - -build: - # Nothing to do. - -clean: - dh_testdir - dh_testroot - dh_clean build-stamp install-stamp - -install: install-stamp -install-stamp: - dh_testdir - dh_testroot - dh_clean -k - - install -D postfix-policyd-spf-perl $(TMP)/usr/sbin/postfix-policyd-spf-perl - touch install-stamp - -# Build architecture-independent files here: -binary-indep: build install - dh_testdir - dh_testroot - - dh_installdocs - dh_installman debian/postfix-policyd-spf-perl.1 - dh_installchangelogs CHANGES - - dh_compress - dh_fixperms - dh_installdeb - dh_perl - dh_gencontrol - dh_md5sums - dh_builddeb - -# Build architecture-dependent files here: -binary-arch: - # Nothing to do. - -binary: binary-indep binary-arch - -.PHONY: build clean binary-indep binary-arch binary install - diff --git a/debian/watch b/debian/watch deleted file mode 100644 index a941ba1..0000000 --- a/debian/watch +++ /dev/null @@ -1,2 +0,0 @@ -version=3 -http://www.openspf.org/blobs/postfix-policyd-spf-perl-(.*)\.tar\.gz debian uupdate From 33fd3c035330a19674708bcaee736f30da24c20d Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Fri, 18 Jul 2008 03:50:46 +0000 Subject: [PATCH 57/87] Add changelog entry to 2.006. --- CHANGES | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES b/CHANGES index cd9e420..e373ba4 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,10 @@ # ! = Changed something significant, or removed a feature # * = Fixed a bug, or made a minor improvement +--- 2.006 (2008-07-18 00:49 -0400) + * Add default logging to make it easier to determine what SPF identity is + being used + --- 2.005 (2007-12-14 23:29 -0500) * Decreased timeout for DNS queries via UDP to 10s from Net::DNS::Resolver's default of 40s (by doing only 1 retransmission rather than 3 after a query From de4919466ba1d2357ab6ff658a58010fbc4d9fb9 Mon Sep 17 00:00:00 2001 From: Scott Kitterman <> Date: Sat, 26 Jul 2008 01:30:51 +0000 Subject: [PATCH 58/87] Fix up documentation a bit in response to Debian bugs. --- CHANGES | 4 ++++ INSTALL | 11 ++++++++--- README | 4 ++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index e373ba4..65adf7f 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,10 @@ # ! = Changed something significant, or removed a feature # * = Fixed a bug, or made a minor improvement +--- 2.007 (2008-07-25 22:24 -0400) + * Update documentation and examples, see Debian bugs 492420 and 492421 for + details. + --- 2.006 (2008-07-18 00:49 -0400) * Add default logging to make it easier to determine what SPF identity is being used diff --git a/INSTALL b/INSTALL index e1277ac..21b2fb8 100644 --- a/INSTALL +++ b/INSTALL @@ -17,7 +17,7 @@ Installing 2. Add the following to /etc/postfix/master.cf: policy unix - n n - 0 spawn - user=nobody argv=/usr/bin/perl /usr/local/lib/policyd-spf-perl + user=nobody argv=/usr/local/lib/policyd-spf-perl 3. Configure the Postfix policy service in /etc/postfix/main.cf: @@ -27,8 +27,13 @@ Installing check_policy_service unix:private/policy ... - NOTE: Specify check_policy_service AFTER reject_unauth_destination or - else your system can become an open relay. + NOTES: + Specify check_policy_service AFTER reject_unauth_destination or + else your system can become an open relay. + + The user 'nobody' is used in this example. This is appropriate if you + do not have any other services running as nobody. If you do, create a + dedicated user for this service and use it instead. 4. Add "policy_time_limit = 3600" to main.cf diff --git a/README b/README index d9be8a9..36e7555 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -postfix-policyd-spf-perl 2.006 +postfix-policyd-spf-perl 2.007 A Postfix SMTPd policy server for SPF checking (C) 2007-2008 Scott Kitterman (C) 2007 Julian Mehnle @@ -62,7 +62,7 @@ Testing the policy daemon To test the policy daemon by hand, execute: - % perl /usr/local/lib/policyd-spf-perl + % /usr/local/lib/policyd-spf-perl Each query is a bunch of attributes. Order does not matter, and the daemon uses only a few of all the attributes shown below: From 2d4c4a5b3f594a63d564f3809744ecfdbf648629 Mon Sep 17 00:00:00 2001 From: Scott Kitterman Date: Wed, 11 Jan 2012 19:37:51 -0500 Subject: [PATCH 59/87] Bump version and copyright dates to start 2.008. --- CHANGES | 3 +++ postfix-policyd-spf-perl | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 65adf7f..4577a69 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,9 @@ # ! = Changed something significant, or removed a feature # * = Fixed a bug, or made a minor improvement +--- 2.008 UNRELEASED + * Fix incorrect version string + --- 2.007 (2008-07-25 22:24 -0400) * Update documentation and examples, see Debian bugs 492420 and 492421 for details. diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index 04e4ec2..e77ec60 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -2,9 +2,9 @@ # postfix-policyd-spf-perl # http://www.openspf.org/Software -# version 2.006 +# version 2.008 # -# (C) 2007-2008 Scott Kitterman +# (C) 2007-2008,2012 Scott Kitterman # (C) 2007 Julian Mehnle # (C) 2003-2004 Meng Weng Wong # @@ -22,7 +22,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -use version; our $VERSION = qv('2.006'); +use version; our $VERSION = qv('2.008'); use strict; From fef173c8e298b7627379f03e3bba4f2cdae748c1 Mon Sep 17 00:00:00 2001 From: Scott Kitterman Date: Wed, 11 Jan 2012 19:41:59 -0500 Subject: [PATCH 60/87] * Initialize variables alternately as when passing to syslog to avoid uninitialized values. * Temporarily set VERBOSE = 1 for development and testing. --- CHANGES | 1 + postfix-policyd-spf-perl | 39 ++++++++++++++++++++++++--------------- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/CHANGES b/CHANGES index 4577a69..8e72c42 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,7 @@ --- 2.008 UNRELEASED * Fix incorrect version string + * Ensure all variables are initialized prior to being passed to syslog --- 2.007 (2008-07-25 22:24 -0400) * Update documentation and examples, see Debian bugs 492420 and 492421 for diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index e77ec60..85bf971 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -61,7 +61,7 @@ my @HANDLERS = ( } ); -my $VERBOSE = 0; +my $VERBOSE = 1; my $DEFAULT_RESPONSE = 'DUNNO'; @@ -137,7 +137,7 @@ while () { if ($VERBOSE) { for (sort keys %attr) { - syslog(debug => "Attribute: %s=%s", $_, $attr{$_}); + syslog(debug => "Attribute: %s=%s", $_ || '', $attr{$_} || ''); } } @@ -153,18 +153,18 @@ while () { my $response = $handler_code->(attr => \%attr, cache => $cache); if ($VERBOSE) { - syslog(debug => "handler %s: %s", $handler_name, $response); + syslog(debug => "handler %s: %s", $handler_name || '', $response || ''); } # Pick whatever response is not 'DUNNO' if ($response and $response !~ /^DUNNO/i) { - syslog(info => "handler %s: is decisive.", $handler_name); + syslog(info => "handler %s: is decisive.", $handler_name || ''); $action = $response; last; } } - syslog(info => "%s: Policy action=%s", $attr{queue_id}, $action); + syslog(info => "%s: Policy action=%s", $attr{queue_id} || '', $action || ''); STDOUT->print("action=$action\n\n"); %attr = (); @@ -234,7 +234,9 @@ sub sender_policy_framework { $errmsg = $errmsg->text if UNIVERSAL::isa($@, 'Mail::SPF::Exception'); syslog( info => "%s:HELO check failed - Mail::SPF->new(%s, %s, %s) failed: %s", - $attr->{queue_id}, $attr->{client_address}, $attr->{sender}, $attr->{helo_name}, $errmsg + $attr->{queue_id} || '', $attr->{client_address} || '', + $attr->{sender} || '', $attr->{helo_name} || '', + $errmsg || '' ); return; } @@ -251,8 +253,9 @@ sub sender_policy_framework { if ($VERBOSE) { syslog( info => "%s: SPF %s: HELO/EHLO: %s, IP Address: %s, Recipient: %s", - $attr->{queue_id}, $helo_result, $attr->{helo_name}, $attr->{client_address}, - $attr->{recipient} + $attr->{queue_id} || '', $helo_result || '', + $attr->{helo_name} || '', $attr->{client_address} || '', + $attr->{recipient} || '' ); }; @@ -261,21 +264,24 @@ sub sender_policy_framework { if ($helo_result->is_code('fail')) { syslog( info => "%s: SPF %s: HELO/EHLO: %s", - $attr->{queue_id}, $helo_result, $attr->{helo_name} + $attr->{queue_id} || '', $helo_result || '', + $attr->{helo_name} || '' ); return "550 $helo_authority_exp"; } elsif ($helo_result->is_code('temperror')) { syslog( info => "%s: SPF %s: HELO/EHLO: %s", - $attr->{queue_id}, $helo_result, $attr->{helo_name} + $attr->{queue_id} || '', $helo_result || '', + $attr->{helo_name} || '' ); return "DEFER_IF_PERMIT SPF-Result=$helo_local_exp"; } elsif ($attr->{sender} eq '') { syslog( info => "%s: SPF %s: HELO/EHLO (Null Sender): %s", - $attr->{queue_id}, $helo_result, $attr->{helo_name} + $attr->{queue_id} || '', $helo_result || '', + $attr->{helo_name} || '' ); return "PREPEND $helo_spf_header" unless $cache->{added_spf_header}++; @@ -306,7 +312,8 @@ sub sender_policy_framework { $errmsg = $errmsg->text if UNIVERSAL::isa($@, 'Mail::SPF::Exception'); syslog( info => "%s: Mail From (sender) check failed - Mail::SPF->new(%s, %s, %s) failed: %s", - $attr->{queue_id}, $attr->{client_address}, $attr->{sender}, $attr->{helo_name}, $errmsg + $attr->{queue_id} || '', $attr->{client_address} || '', + $attr->{sender} || '', $attr->{helo_name} || '', $errmsg || '' ); return; } @@ -323,15 +330,17 @@ sub sender_policy_framework { if ($VERBOSE) { syslog( info => "%s: SPF %s: Envelope-from: %s, IP Address: %s, Recipient: %s", - $attr->{queue_id}, $mfrom_result, $attr->{sender}, $attr->{client_address}, - $attr->{recipient} + $attr->{queue_id} || '', $mfrom_result || '', + $attr->{sender} || '', $attr->{client_address} || '', + $attr->{recipient} || '' ); }; # Same approach as HELO.... syslog( info => "%s: SPF %s: Envelope-from: %s", - $attr->{queue_id}, $mfrom_result, $attr->{sender} + $attr->{queue_id} || '', $mfrom_result || '', + $attr->{sender} || '' ); if ($mfrom_result->is_code('fail')) { return "550 $mfrom_authority_exp"; From f613668cb40a90eff0820e13c877a24f1641dd76 Mon Sep 17 00:00:00 2001 From: Scott Kitterman Date: Thu, 12 Jan 2012 10:30:45 -0500 Subject: [PATCH 61/87] ! Query only TXT and not DNS RR Type SPF records to reduce unnecessary DNS lookups (LP: #161133) --- CHANGES | 2 ++ postfix-policyd-spf-perl | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 8e72c42..b3d21ec 100644 --- a/CHANGES +++ b/CHANGES @@ -5,6 +5,8 @@ # * = Fixed a bug, or made a minor improvement --- 2.008 UNRELEASED + ! Query only TXT and not DNS RR Type SPF records to reduce unnecessary DNS + lookups (LP: #161133) * Fix incorrect version string * Ensure all variables are initialized prior to being passed to syslog diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index 85bf971..f36108f 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -41,8 +41,11 @@ my $resolver = Net::DNS::Resolver->new( # Makes for a total timeout for UDP queries of 5s * 2 = 10s. ); +# query_rr_type_all will query both type TXT and type SPF. This upstream +# default is changed due to there being essentiall no type SPF deployment. my $spf_server = Mail::SPF::Server->new( - dns_resolver => $resolver + dns_resolver => $resolver, + query_rr_types => Mail::SPF::Server->query_rr_type_txt ); # Adding more handlers is easy: From 685891fa76ca28be24a7f5cb6325ce6054b7bb75 Mon Sep 17 00:00:00 2001 From: Scott Kitterman Date: Thu, 12 Jan 2012 12:58:46 -0500 Subject: [PATCH 62/87] * Changed default_authority_explanation ('Why' reject text) to point to openspf.net instead of openspf.org due to extended outage --- CHANGES | 2 ++ postfix-policyd-spf-perl | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index b3d21ec..a87a480 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,8 @@ --- 2.008 UNRELEASED ! Query only TXT and not DNS RR Type SPF records to reduce unnecessary DNS lookups (LP: #161133) + * Changed default_authority_explanation ('Why' reject text) to point to + openspf.net instead of openspf.org due to extended outage * Fix incorrect version string * Ensure all variables are initialized prior to being passed to syslog diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index f36108f..b36447b 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -45,7 +45,9 @@ my $resolver = Net::DNS::Resolver->new( # default is changed due to there being essentiall no type SPF deployment. my $spf_server = Mail::SPF::Server->new( dns_resolver => $resolver, - query_rr_types => Mail::SPF::Server->query_rr_type_txt + query_rr_types => Mail::SPF::Server->query_rr_type_txt, + default_authority_explanation => + 'Please see http://www.openspf.net/Why?s=%{_scope};id=%{S};ip=%{C};r=%{R}' ); # Adding more handlers is easy: From 77a37aff41370d9421c2323e81804ae1220d1755 Mon Sep 17 00:00:00 2001 From: Scott Kitterman Date: Thu, 19 Jan 2012 13:53:15 -0500 Subject: [PATCH 63/87] Final pre-release cleanups of documentation for 2.008. --- CHANGES | 3 ++- INSTALL | 2 +- README | 7 ++++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index a87a480..c41e949 100644 --- a/CHANGES +++ b/CHANGES @@ -4,13 +4,14 @@ # ! = Changed something significant, or removed a feature # * = Fixed a bug, or made a minor improvement ---- 2.008 UNRELEASED +--- 2.008 (2012-01-19 13:46 -0500) ! Query only TXT and not DNS RR Type SPF records to reduce unnecessary DNS lookups (LP: #161133) * Changed default_authority_explanation ('Why' reject text) to point to openspf.net instead of openspf.org due to extended outage * Fix incorrect version string * Ensure all variables are initialized prior to being passed to syslog + (LP: #806926) --- 2.007 (2008-07-25 22:24 -0400) * Update documentation and examples, see Debian bugs 492420 and 492421 for diff --git a/INSTALL b/INSTALL index 21b2fb8..edc3cec 100644 --- a/INSTALL +++ b/INSTALL @@ -7,7 +7,7 @@ postfix-policyd-spf-perl: Perl 5.6 version NetAddr-IP 4 - Mail-SPF (not Mail-SPF-Query) + Mail-SPF (not Mail-SPF-Query) version 2.006 or later Installing ---------- diff --git a/README b/README index 36e7555..86993a4 100644 --- a/README +++ b/README @@ -1,10 +1,10 @@ -postfix-policyd-spf-perl 2.007 +postfix-policyd-spf-perl 2.008 A Postfix SMTPd policy server for SPF checking -(C) 2007-2008 Scott Kitterman +(C) 2007-2008,2012 Scott Kitterman (C) 2007 Julian Mehnle (C) 2003-2004 Meng Weng Wong Thanks for contributions by various members of the SPF project - + ============================================================================== postfix-policyd-spf-perl is a Postfix SMTPd policy server for SPF checking. @@ -13,6 +13,7 @@ Mail::SPF is a complete re-implementation of SPF based on the final SPF RFC, RFC 4408. It shares no code with the older Mail::SPF::Query that was the original SPF development implementation. If you are upgrading from on older (<< 2.000) version of this policy server you will need to install Mail::SPF. +At least version 2.006 of Mail::SPF is required. This version of the policy server always checks HELO before Mail From (older versions just checked HELO if Mail From was null). It will reject mail that From 0632517790a1afcb5cd7a098c1dfe59d3a7df068 Mon Sep 17 00:00:00 2001 From: Scott Kitterman Date: Thu, 19 Jan 2012 15:08:55 -0500 Subject: [PATCH 64/87] Urgh! Set verbose back to 0 - cowboyed into 2.008 too. --- postfix-policyd-spf-perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index b36447b..d464ddd 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -66,7 +66,7 @@ my @HANDLERS = ( } ); -my $VERBOSE = 1; +my $VERBOSE = 0; my $DEFAULT_RESPONSE = 'DUNNO'; From 101de98f4f2e6f599be8e5fb1330a079311c9412 Mon Sep 17 00:00:00 2001 From: Scott Kitterman Date: Wed, 25 Jan 2012 22:44:27 -0500 Subject: [PATCH 65/87] * Bump versions to start version 2.009. * Set verbose for the development period again. --- CHANGES | 1 + README | 2 +- postfix-policyd-spf-perl | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index c41e949..d5269bb 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,7 @@ # ! = Changed something significant, or removed a feature # * = Fixed a bug, or made a minor improvement +--- 2.009 UNRELEASED --- 2.008 (2012-01-19 13:46 -0500) ! Query only TXT and not DNS RR Type SPF records to reduce unnecessary DNS lookups (LP: #161133) diff --git a/README b/README index 86993a4..0f60ae9 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -postfix-policyd-spf-perl 2.008 +postfix-policyd-spf-perl 2.009 A Postfix SMTPd policy server for SPF checking (C) 2007-2008,2012 Scott Kitterman (C) 2007 Julian Mehnle diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index d464ddd..6b43403 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -2,7 +2,7 @@ # postfix-policyd-spf-perl # http://www.openspf.org/Software -# version 2.008 +# version 2.009 # # (C) 2007-2008,2012 Scott Kitterman # (C) 2007 Julian Mehnle @@ -22,7 +22,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -use version; our $VERSION = qv('2.008'); +use version; our $VERSION = qv('2.009'); use strict; @@ -66,7 +66,7 @@ my @HANDLERS = ( } ); -my $VERBOSE = 0; +my $VERBOSE = 1; my $DEFAULT_RESPONSE = 'DUNNO'; From 39a6d9bcd6fde6e1dd492c1b2e7c0f55eb1ce7bd Mon Sep 17 00:00:00 2001 From: Scott Kitterman Date: Wed, 25 Jan 2012 22:54:02 -0500 Subject: [PATCH 66/87] * Chomp erroneus NULLs off the end of local and authority explanations to work around a Mail::SPF bug (in Mail::SPF versions prior to 2.008) - Patch thanks to Allison Randal --- CHANGES | 3 +++ postfix-policyd-spf-perl | 21 +++++++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index d5269bb..f68d5c4 100644 --- a/CHANGES +++ b/CHANGES @@ -5,6 +5,9 @@ # * = Fixed a bug, or made a minor improvement --- 2.009 UNRELEASED + * Chomp erroneus NULLs off the end of local and authority explanations to + work around a Mail::SPF bug (in Mail::SPF versions prior to 2.008) + - Patch thanks to Allison Randal --- 2.008 (2012-01-19 13:46 -0500) ! Query only TXT and not DNS RR Type SPF records to reduce unnecessary DNS lookups (LP: #161133) diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index 6b43403..4293394 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -250,8 +250,8 @@ sub sender_policy_framework { } my $helo_result_code = $helo_result->code; # 'pass', 'fail', etc. - my $helo_local_exp = $helo_result->local_explanation; - my $helo_authority_exp = $helo_result->authority_explanation + my $helo_local_exp = nullchomp($helo_result->local_explanation); + my $helo_authority_exp = nullchomp($helo_result->authority_explanation) if $helo_result->is_code('fail'); my $helo_spf_header = $helo_result->received_spf_header; @@ -327,8 +327,8 @@ sub sender_policy_framework { } my $mfrom_result_code = $mfrom_result->code; # 'pass', 'fail', etc. - my $mfrom_local_exp = $mfrom_result->local_explanation; - my $mfrom_authority_exp = $mfrom_result->authority_explanation + my $mfrom_local_exp = nullchomp($mfrom_result->local_explanation); + my $mfrom_authority_exp = nullchomp($mfrom_result->authority_explanation) if $mfrom_result->is_code('fail'); my $mfrom_spf_header = $mfrom_result->received_spf_header; @@ -360,3 +360,16 @@ sub sender_policy_framework { return; } + +# ---------------------------------------------------------- +# utility, string cleaning +# ---------------------------------------------------------- + +sub nullchomp { + my $value = shift; + + # Remove one or more null characters from the + # end of the input. + $value =~ s/\0+$//; + return $value; +} From b2d1889dfaf52f3d1f5aebc9c1153279c53b66db Mon Sep 17 00:00:00 2001 From: Scott Kitterman Date: Wed, 25 Jan 2012 23:00:43 -0500 Subject: [PATCH 67/87] Add copyright information for Allison Randal --- README | 5 +++-- postfix-policyd-spf-perl | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/README b/README index 0f60ae9..b6952f3 100644 --- a/README +++ b/README @@ -1,8 +1,9 @@ postfix-policyd-spf-perl 2.009 A Postfix SMTPd policy server for SPF checking (C) 2007-2008,2012 Scott Kitterman -(C) 2007 Julian Mehnle -(C) 2003-2004 Meng Weng Wong +(C) 2012 Allison Randal +(C) 2007 Julian Mehnle +(C) 2003-2004 Meng Weng Wong Thanks for contributions by various members of the SPF project ============================================================================== diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index 4293394..e77ded7 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -5,8 +5,9 @@ # version 2.009 # # (C) 2007-2008,2012 Scott Kitterman -# (C) 2007 Julian Mehnle -# (C) 2003-2004 Meng Weng Wong +# (C) 2012 Allison Randal +# (C) 2007 Julian Mehnle +# (C) 2003-2004 Meng Weng Wong # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by From 291238fe731ae8ec8ba7ae2a0efdbe8eee8f9a1f Mon Sep 17 00:00:00 2001 From: Scott Kitterman Date: Fri, 3 Feb 2012 22:27:11 -0500 Subject: [PATCH 68/87] Remove test_cases file since these are maintained in the pypolicyd-spf repository now. --- test_cases | 147 ----------------------------------------------------- 1 file changed, 147 deletions(-) delete mode 100644 test_cases diff --git a/test_cases b/test_cases deleted file mode 100644 index cae5156..0000000 --- a/test_cases +++ /dev/null @@ -1,147 +0,0 @@ -#HELO and mfrom pass -request=smtpd_access_policy -client_address=72.81.252.18 -helo_name=mailout02.controlledmail.com -sender=scott@kitterman.com -recipient=bogus@kitterman.org -queue_id=q1234 -instance=1 - -#HELO fail and mfrom pass -request=smtpd_access_policy -client_address=72.81.252.18 -helo_name=mailout00.controlledmail.com -sender=scott@kitterman.com -recipient=bogus@kitterman.org -queue_id=q1234 -instance=2 - -#no HELO and mfrom pass -request=smtpd_access_policy -client_address=72.81.252.18 -helo_name=72.81.252.18 -sender=scott@kitterman.com -recipient=bogus@kitterman.org -queue_id=q1234 -instance=3 - -#helo pass and mfrom pass -request=smtpd_access_policy -client_address=72.81.252.19 -helo_name=mailout00.controlledmail.com -sender=scott@kitterman.org -recipient=bogus@kitterman.org -queue_id=q1234 -instance=4 - -#helo pass and mfrom none -request=smtpd_access_policy -client_address=72.81.252.18 -helo_name=mailout02.controlledmail.com -sender=scott@yahoo.com -recipient=bogus@kitterman.org -queue_id=q1234 -instance=5 - -#helo pass and mfrom null -request=smtpd_access_policy -client_address=72.81.252.18 -helo_name=mailout02.controlledmail.com -sender= -recipient=bogus@kitterman.org -queue_id=q1234 -instance=6 - -#helo fail and mfrom null -request=smtpd_access_policy -client_address=72.81.252.19 -helo_name=mailout02.controlledmail.com -sender= -recipient=bogus@kitterman.org -queue_id=q1234 -instance=7 - -#Multi-recipient dunno -request=smtpd_access_policy -client_address=72.81.252.18 -helo_name=mailout03.controlledmail.com -sender=scott@kitterman.com -recipient=bogus2@kitterman.org -queue_id=q1234 -instance=3 - -#localhost bypass -request=smtpd_access_policy -client_address=127.0.0.1 -helo_name=mailout03.controlledmail.com -sender=scott@kitterman.com -recipient=bogus2@kitterman.org -queue_id=q1234 -instance=12 - -#Helo Neutral and mfrom pass -request=smtpd_access_policy -client_address=72.81.252.18 -helo_name=aol.com -sender=scott@kitterman.com -recipient=bogus2@kitterman.org -queue_id=q1234 -instance=13 - -#Whitelist -request=smtpd_access_policy -client_address=192.168.0.1 -helo_name=mailout03.controlledmail.com -sender=scott@kitterman.com -recipient=bogus2@kitterman.org -queue_id=q1234 -instance=14 - -# multi-recipient mfrom fail -request=smtpd_access_policy -client_address=72.81.252.19 -helo_name=mailout00.controlledmail.com -sender=scott@kitterman.org -recipient=bogus2@kitterman.org -queue_id=q1234 -instance=4 - -# multi-recipient HELO fail and mfrom pass -request=smtpd_access_policy -client_address=72.81.252.18 -helo_name=mailout00.controlledmail.com -sender=scott@kitterman.com -recipient=bogus2@kitterman.org -queue_id=q1234 -instance=2 - -# No SPF -request=smtpd_access_policy -client_address=72.81.252.18 -helo_name=mailout00.yahoo.com -sender=scott@yahoo.com -recipient=bogus2@kitterman.org -queue_id=q1234 -instance=15 - -# Permerror reject -request=smtpd_access_policy -client_address=72.81.252.18 -helo_name=elvey.com -sender=scott@elvey.com -recipient=bogus2@kitterman.org -queue_id=q1234 -instance=16 - -# None and None -request=smtpd_access_policy -client_address=71.17.127.27 -helo_name=71-17-127-27.estv.hsdb.sasknet.sk.ca -sender=dalbecbhoj@accessamericatransport.com -recipient=hostmaster@jamux.com - -request=smtpd_access_policy -client_address=200.120.31.84 -helo_name=autohaus-knabe.de -sender=daniel.hahnomjy@autohaus-knabe.de -recipient=jam@jamux.com \ No newline at end of file From 2f5a4ff1ba8e6a981839d2390d66fb542ae5b0a8 Mon Sep 17 00:00:00 2001 From: Scott Kitterman Date: Fri, 3 Feb 2012 22:42:16 -0500 Subject: [PATCH 69/87] Remove logging of queue ID - It's virtually never available and just clutters logs. --- postfix-policyd-spf-perl | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index e77ded7..2357b13 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -170,7 +170,7 @@ while () { } } - syslog(info => "%s: Policy action=%s", $attr{queue_id} || '', $action || ''); + syslog(info => "Policy action=%s", $action || ''); STDOUT->print("action=$action\n\n"); %attr = (); @@ -239,8 +239,8 @@ sub sender_policy_framework { my $errmsg = $@; $errmsg = $errmsg->text if UNIVERSAL::isa($@, 'Mail::SPF::Exception'); syslog( - info => "%s:HELO check failed - Mail::SPF->new(%s, %s, %s) failed: %s", - $attr->{queue_id} || '', $attr->{client_address} || '', + info => "HELO check failed - Mail::SPF->new(%s, %s, %s) failed: %s", + $attr->{client_address} || '', $attr->{sender} || '', $attr->{helo_name} || '', $errmsg || '' ); @@ -258,8 +258,8 @@ sub sender_policy_framework { if ($VERBOSE) { syslog( - info => "%s: SPF %s: HELO/EHLO: %s, IP Address: %s, Recipient: %s", - $attr->{queue_id} || '', $helo_result || '', + info => "SPF %s: HELO/EHLO: %s, IP Address: %s, Recipient: %s", + $helo_result || '', $attr->{helo_name} || '', $attr->{client_address} || '', $attr->{recipient} || '' ); @@ -269,24 +269,24 @@ sub sender_policy_framework { # be accepted. Use the HELO result and return for null sender. if ($helo_result->is_code('fail')) { syslog( - info => "%s: SPF %s: HELO/EHLO: %s", - $attr->{queue_id} || '', $helo_result || '', + info => "SPF %s: HELO/EHLO: %s", + $helo_result || '', $attr->{helo_name} || '' ); return "550 $helo_authority_exp"; } elsif ($helo_result->is_code('temperror')) { syslog( - info => "%s: SPF %s: HELO/EHLO: %s", - $attr->{queue_id} || '', $helo_result || '', + info => "SPF %s: HELO/EHLO: %s", + $helo_result || '', $attr->{helo_name} || '' ); return "DEFER_IF_PERMIT SPF-Result=$helo_local_exp"; } elsif ($attr->{sender} eq '') { syslog( - info => "%s: SPF %s: HELO/EHLO (Null Sender): %s", - $attr->{queue_id} || '', $helo_result || '', + info => "SPF %s: HELO/EHLO (Null Sender): %s", + $helo_result || '', $attr->{helo_name} || '' ); return "PREPEND $helo_spf_header" @@ -317,8 +317,8 @@ sub sender_policy_framework { my $errmsg = $@; $errmsg = $errmsg->text if UNIVERSAL::isa($@, 'Mail::SPF::Exception'); syslog( - info => "%s: Mail From (sender) check failed - Mail::SPF->new(%s, %s, %s) failed: %s", - $attr->{queue_id} || '', $attr->{client_address} || '', + info => "Mail From (sender) check failed - Mail::SPF->new(%s, %s, %s) failed: %s", + $attr->{client_address} || '', $attr->{sender} || '', $attr->{helo_name} || '', $errmsg || '' ); return; @@ -335,8 +335,8 @@ sub sender_policy_framework { if ($VERBOSE) { syslog( - info => "%s: SPF %s: Envelope-from: %s, IP Address: %s, Recipient: %s", - $attr->{queue_id} || '', $mfrom_result || '', + info => "SPF %s: Envelope-from: %s, IP Address: %s, Recipient: %s", + $mfrom_result || '', $attr->{sender} || '', $attr->{client_address} || '', $attr->{recipient} || '' ); @@ -344,8 +344,8 @@ sub sender_policy_framework { # Same approach as HELO.... syslog( - info => "%s: SPF %s: Envelope-from: %s", - $attr->{queue_id} || '', $mfrom_result || '', + info => "SPF %s: Envelope-from: %s", + $mfrom_result || '', $attr->{sender} || '' ); if ($mfrom_result->is_code('fail')) { From 3156020666ba99db68e3c0c62e817ae90ec6b5a5 Mon Sep 17 00:00:00 2001 From: Scott Kitterman Date: Fri, 3 Feb 2012 23:13:46 -0500 Subject: [PATCH 70/87] Reduce non-verbose logging to a single line per message. --- CHANGES | 3 ++ postfix-policyd-spf-perl | 82 +++++++++++++++++++++++----------------- 2 files changed, 51 insertions(+), 34 deletions(-) diff --git a/CHANGES b/CHANGES index f68d5c4..a0b4a0a 100644 --- a/CHANGES +++ b/CHANGES @@ -8,6 +8,9 @@ * Chomp erroneus NULLs off the end of local and authority explanations to work around a Mail::SPF bug (in Mail::SPF versions prior to 2.008) - Patch thanks to Allison Randal + * Stop logging queue ID since it is virtually never available and clutters + the logs + * Reduced non-verbose logging to a single line per-message --- 2.008 (2012-01-19 13:46 -0500) ! Query only TXT and not DNS RR Type SPF records to reduce unnecessary DNS lookups (LP: #161133) diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index 2357b13..ec8f9a3 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -145,7 +145,7 @@ while () { for (sort keys %attr) { syslog(debug => "Attribute: %s=%s", $_ || '', $attr{$_} || ''); } - } + }; my $message_instance = $attr{instance}; my $cache = defined($message_instance) ? $results_cache{$message_instance} ||= {} : {}; @@ -160,11 +160,13 @@ while () { if ($VERBOSE) { syslog(debug => "handler %s: %s", $handler_name || '', $response || ''); - } + }; # Pick whatever response is not 'DUNNO' if ($response and $response !~ /^DUNNO/i) { - syslog(info => "handler %s: is decisive.", $handler_name || ''); + if ($VERBOSE) { + syslog(info => "handler %s: is decisive.", $handler_name || ''); + } $action = $response; last; } @@ -238,12 +240,14 @@ sub sender_policy_framework { # probably due to invalid input data! my $errmsg = $@; $errmsg = $errmsg->text if UNIVERSAL::isa($@, 'Mail::SPF::Exception'); - syslog( - info => "HELO check failed - Mail::SPF->new(%s, %s, %s) failed: %s", - $attr->{client_address} || '', - $attr->{sender} || '', $attr->{helo_name} || '', - $errmsg || '' - ); + if ($VERBOSE) { + syslog( + info => "HELO check failed - Mail::SPF->new(%s, %s, %s) failed: %s", + $attr->{client_address} || '', + $attr->{sender} || '', $attr->{helo_name} || '', + $errmsg || '' + ); + }; return; } @@ -268,27 +272,33 @@ sub sender_policy_framework { # Reject on HELO fail. Defer on HELO temperror if message would otherwise # be accepted. Use the HELO result and return for null sender. if ($helo_result->is_code('fail')) { - syslog( - info => "SPF %s: HELO/EHLO: %s", - $helo_result || '', - $attr->{helo_name} || '' - ); + if ($VERBOSE) { + syslog( + info => "SPF %s: HELO/EHLO: %s", + $helo_result || '', + $attr->{helo_name} || '' + ); + }; return "550 $helo_authority_exp"; } elsif ($helo_result->is_code('temperror')) { - syslog( - info => "SPF %s: HELO/EHLO: %s", - $helo_result || '', - $attr->{helo_name} || '' - ); + if ($VERBOSE) { + syslog( + info => "SPF %s: HELO/EHLO: %s", + $helo_result || '', + $attr->{helo_name} || '' + ); + }; return "DEFER_IF_PERMIT SPF-Result=$helo_local_exp"; } elsif ($attr->{sender} eq '') { - syslog( - info => "SPF %s: HELO/EHLO (Null Sender): %s", - $helo_result || '', - $attr->{helo_name} || '' - ); + if ($VERBOSE) { + syslog( + info => "SPF %s: HELO/EHLO (Null Sender): %s", + $helo_result || '', + $attr->{helo_name} || '' + ); + }; return "PREPEND $helo_spf_header" unless $cache->{added_spf_header}++; } @@ -316,11 +326,13 @@ sub sender_policy_framework { # probably due to invalid input data! my $errmsg = $@; $errmsg = $errmsg->text if UNIVERSAL::isa($@, 'Mail::SPF::Exception'); - syslog( - info => "Mail From (sender) check failed - Mail::SPF->new(%s, %s, %s) failed: %s", - $attr->{client_address} || '', - $attr->{sender} || '', $attr->{helo_name} || '', $errmsg || '' - ); + if ($VERBOSE) { + syslog( + info => "Mail From (sender) check failed - Mail::SPF->new(%s, %s, %s) failed: %s", + $attr->{client_address} || '', + $attr->{sender} || '', $attr->{helo_name} || '', $errmsg || '' + ); + }; return; } @@ -343,11 +355,13 @@ sub sender_policy_framework { }; # Same approach as HELO.... - syslog( - info => "SPF %s: Envelope-from: %s", - $mfrom_result || '', - $attr->{sender} || '' - ); + if ($VERBOSE) { + syslog( + info => "SPF %s: Envelope-from: %s", + $mfrom_result || '', + $attr->{sender} || '' + ); + }; if ($mfrom_result->is_code('fail')) { return "550 $mfrom_authority_exp"; } From c05c21182231cf9a880fd96bc22c3b33a3089c9c Mon Sep 17 00:00:00 2001 From: Scott Kitterman Date: Fri, 3 Feb 2012 23:23:59 -0500 Subject: [PATCH 71/87] Changelog cleanups in preparation for release. --- CHANGES | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index a0b4a0a..ebeb02c 100644 --- a/CHANGES +++ b/CHANGES @@ -4,13 +4,15 @@ # ! = Changed something significant, or removed a feature # * = Fixed a bug, or made a minor improvement ---- 2.009 UNRELEASED +--- 2.009 2012-02-03 * Chomp erroneus NULLs off the end of local and authority explanations to work around a Mail::SPF bug (in Mail::SPF versions prior to 2.008) + (LP: #806926) - Patch thanks to Allison Randal * Stop logging queue ID since it is virtually never available and clutters the logs - * Reduced non-verbose logging to a single line per-message + * Reduced non-verbose logging to a single line per message + --- 2.008 (2012-01-19 13:46 -0500) ! Query only TXT and not DNS RR Type SPF records to reduce unnecessary DNS lookups (LP: #161133) @@ -18,7 +20,6 @@ openspf.net instead of openspf.org due to extended outage * Fix incorrect version string * Ensure all variables are initialized prior to being passed to syslog - (LP: #806926) --- 2.007 (2008-07-25 22:24 -0400) * Update documentation and examples, see Debian bugs 492420 and 492421 for From 5970525467ff70afb5fee5cd4cef1c84341efbdd Mon Sep 17 00:00:00 2001 From: Scott Kitterman Date: Fri, 3 Feb 2012 23:40:07 -0500 Subject: [PATCH 72/87] Set logging back to non-verbose by default for release. --- postfix-policyd-spf-perl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index ec8f9a3..1cf8d5e 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -67,7 +67,7 @@ my @HANDLERS = ( } ); -my $VERBOSE = 1; +my $VERBOSE = 0; my $DEFAULT_RESPONSE = 'DUNNO'; From 2aa104973fca2f363a527c63442a2eb3b0497cb9 Mon Sep 17 00:00:00 2001 From: Scott Kitterman Date: Sun, 17 Jun 2012 22:26:46 -0400 Subject: [PATCH 73/87] * Start 2.010 * Fixed incorrect use of != instead of ne for string comparison --- CHANGES | 4 ++++ README | 2 +- postfix-policyd-spf-perl | 8 ++++---- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index ebeb02c..949f62e 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,10 @@ # ! = Changed something significant, or removed a feature # * = Fixed a bug, or made a minor improvement +--- 2.010 2012-06-17 + * Fixed incorrect use of != instead of ne for string comparison + (LP: #1014243) + --- 2.009 2012-02-03 * Chomp erroneus NULLs off the end of local and authority explanations to work around a Mail::SPF bug (in Mail::SPF versions prior to 2.008) diff --git a/README b/README index b6952f3..9d8fc13 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -postfix-policyd-spf-perl 2.009 +postfix-policyd-spf-perl 2.010 A Postfix SMTPd policy server for SPF checking (C) 2007-2008,2012 Scott Kitterman (C) 2012 Allison Randal diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index 1cf8d5e..cd31cf4 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -2,7 +2,7 @@ # postfix-policyd-spf-perl # http://www.openspf.org/Software -# version 2.009 +# version 2.010 # # (C) 2007-2008,2012 Scott Kitterman # (C) 2012 Allison Randal @@ -23,7 +23,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -use version; our $VERSION = qv('2.009'); +use version; our $VERSION = qv('2.010'); use strict; @@ -185,7 +185,7 @@ while () { sub exempt_localhost { my %options = @_; my $attr = $options{attr}; - if ($attr->{client_address} != '') { + if ($attr->{client_address} ne '') { my $client_address = NetAddr::IP->new($attr->{client_address}); return 'PREPEND X-Comment: SPF not applicable to localhost connection - skipped check' if grep($_->contains($client_address), localhost_addresses); @@ -200,7 +200,7 @@ sub exempt_localhost { sub exempt_relay { my %options = @_; my $attr = $options{attr}; - if ($attr->{client_address} != '') { + if ($attr->{client_address} ne '') { my $client_address = NetAddr::IP->new($attr->{client_address}); return 'PREPEND X-Comment: SPF skipped for whitelisted relay' if grep($_->contains($client_address), relay_addresses); From 8c2ec2083f7ca295f6f216e7e7379ae781d83564 Mon Sep 17 00:00:00 2001 From: Scott Kitterman Date: Sun, 17 Jun 2012 23:49:11 -0400 Subject: [PATCH 74/87] ! Changed non-standard X-Comment header fields for localhost and whitelisted addresses to use RFC 5451 Authentication Results header fields ! Added depenency on Sys::Hostname::Long for local hostname determination --- CHANGES | 4 ++++ INSTALL | 3 ++- postfix-policyd-spf-perl | 9 +++++++-- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 949f62e..a4262b4 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,10 @@ --- 2.010 2012-06-17 * Fixed incorrect use of != instead of ne for string comparison (LP: #1014243) + ! Changed non-standard X-Comment header fields for localhost and + whitelisted addresses to use RFC 5451 Authentication Results header + fields + ! Added depenency on Sys::Hostname::Long for local hostname determination --- 2.009 2012-02-03 * Chomp erroneus NULLs off the end of local and authority explanations to diff --git a/INSTALL b/INSTALL index edc3cec..3e13787 100644 --- a/INSTALL +++ b/INSTALL @@ -7,7 +7,8 @@ postfix-policyd-spf-perl: Perl 5.6 version NetAddr-IP 4 - Mail-SPF (not Mail-SPF-Query) version 2.006 or later + Mail::SPF (not Mail-SPF-Query) version 2.006 or later + Sys::Hostname::Long Installing ---------- diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index cd31cf4..06c9a51 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -31,6 +31,7 @@ use IO::Handle; use Sys::Syslog qw(:DEFAULT setlogsock); use NetAddr::IP; use Mail::SPF; +use Sys::Hostname::Long 'hostname_long'; # ---------------------------------------------------------- # configuration @@ -92,6 +93,10 @@ use constant relay_addresses => map( qw( ) ); # add addresses to qw ( ) above separated by spaces using CIDR notation. +# Fully qualified hostname, if available, for use in authentication results +# headers now provided by the localhost and whitelist checks. +my $host = hostname_long; + my %results_cache; # by message instance # ---------------------------------------------------------- @@ -187,7 +192,7 @@ sub exempt_localhost { my $attr = $options{attr}; if ($attr->{client_address} ne '') { my $client_address = NetAddr::IP->new($attr->{client_address}); - return 'PREPEND X-Comment: SPF not applicable to localhost connection - skipped check' + return "PREPEND Authentication-Results: $host; none (SPF not checked for localhost)" if grep($_->contains($client_address), localhost_addresses); }; return 'DUNNO'; @@ -202,7 +207,7 @@ sub exempt_relay { my $attr = $options{attr}; if ($attr->{client_address} ne '') { my $client_address = NetAddr::IP->new($attr->{client_address}); - return 'PREPEND X-Comment: SPF skipped for whitelisted relay' + return "PREPEND Authentication-Results: $host; none (SPF not checked for whitelisted relay)" if grep($_->contains($client_address), relay_addresses); }; return 'DUNNO'; From 92dc6cbd4ff72ad8bde5f9216e2c219be8f2c954 Mon Sep 17 00:00:00 2001 From: Scott Kitterman Date: Tue, 10 Jan 2017 17:19:17 -0500 Subject: [PATCH 75/87] * Added mention of the requirement for Sys::Syslog to INSTALL * Add references to the current upstream location on Launchpad --- CHANGES | 4 ++++ INSTALL | 1 + README | 1 + postfix-policyd-spf-perl | 5 +++-- 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index a4262b4..1fe6493 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,10 @@ # ! = Changed something significant, or removed a feature # * = Fixed a bug, or made a minor improvement +--- 2.011 UNRELEASED + * Added mention of the requirement for Sys::Syslog to INSTALL + * Add references to the current upstream location on Launchpad + --- 2.010 2012-06-17 * Fixed incorrect use of != instead of ne for string comparison (LP: #1014243) diff --git a/INSTALL b/INSTALL index 3e13787..7912567 100644 --- a/INSTALL +++ b/INSTALL @@ -9,6 +9,7 @@ postfix-policyd-spf-perl: NetAddr-IP 4 Mail::SPF (not Mail-SPF-Query) version 2.006 or later Sys::Hostname::Long + Sys::Syslog Installing ---------- diff --git a/README b/README index 9d8fc13..85adea8 100644 --- a/README +++ b/README @@ -6,6 +6,7 @@ A Postfix SMTPd policy server for SPF checking (C) 2003-2004 Meng Weng Wong Thanks for contributions by various members of the SPF project + ============================================================================== postfix-policyd-spf-perl is a Postfix SMTPd policy server for SPF checking. diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index 06c9a51..18941b4 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -1,8 +1,9 @@ #!/usr/bin/perl # postfix-policyd-spf-perl +# https://launchpad.net/postfix-policyd-spf-perl # http://www.openspf.org/Software -# version 2.010 +# version 2.011 # # (C) 2007-2008,2012 Scott Kitterman # (C) 2012 Allison Randal @@ -23,7 +24,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -use version; our $VERSION = qv('2.010'); +use version; our $VERSION = qv('2.011'); use strict; From b916c542c6c963cbea49473de3f74a7ecfdb0d8e Mon Sep 17 00:00:00 2001 From: Scott Savarese Date: Thu, 26 Jul 2018 00:42:40 -0400 Subject: [PATCH 76/87] Add option to skip SPF checks on exempt domains based on /etc/postfix/exempt_spf_domains --- README | 6 +++++ postfix-policyd-spf-perl | 51 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/README b/README index 85adea8..ecd954b 100644 --- a/README +++ b/README @@ -36,6 +36,12 @@ relay_addresses on line 78 using standard CIDR notation in a space separated list. For these addresses, 'X-Comment: SPF skipped for whitelisted relay' is prepended and logged. IPv6 localhost is also skipped. +A configuration file, /etc/postfix/exempt_spf_domains, can be used to +ignore domains that have broken SPF configurations that would normally +fail. For those domains, add the domain to the file (one per line), and +restart postfix so that the policy server can reload its configuration. +The policy server will ignore the domain going forward. + Error conditions within the policy server (that don't result in a crash) or from Mail::SPF will return DUNNO. diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index 18941b4..b5f36a9 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -64,6 +64,10 @@ my @HANDLERS = ( code => \&exempt_relay }, { + name => 'exempt_domains', + code => \&exempt_domains + }, + { name => 'sender_policy_framework', code => \&sender_policy_framework } @@ -73,6 +77,9 @@ my $VERBOSE = 0; my $DEFAULT_RESPONSE = 'DUNNO'; +# Read in exempt domains list +my $exempt_domains = get_exempt_domains( "/etc/postfix/exempt_spf_domains" ); + # # Syslogging options for verbose mode and for fatal errors. # NOTE: comment out the $syslog_socktype line if syslogging does not @@ -184,6 +191,50 @@ while () { %attr = (); } +# ---------------------------------------------------------- +# handler: domain exemption +# ---------------------------------------------------------- +sub get_exempt_domains { + my ( $file ) = @_; + + my $list = {}; + + # Return nothing if file not found + if ( ! -r $file ) { + return $list; + } + + # Read the file into one variable, split on space or comma (or all) + open ( FILE, $file ) or die "Can't open $file: $!\n"; + my $text = ""; + while ( my $tmp = ) { + $text .= $tmp; + } + close( FILE ); + + foreach my $domain ( split( /[\s,]+/, $text ) ) { + $list->{$domain} = 1; + } + + return $list; +} + +sub exempt_domains { + my %options = @_; + my $attr = $options{attr}; + + my $domain = ( split( /\@/, $attr->{sender} ) )[1]; + return 'DUNNO' if ( ( ! defined( $domain ) ) or ( $domain eq '' ) ); + + # Check the domain against our list of ignored domains + if ( defined( $exempt_domains->{$domain} ) ) { + return "PREPEND Authentication-Results: $host; none " . + "(SPF exempted by policy)"; + } + + return 'DUNNO'; +} + # ---------------------------------------------------------- # handler: localhost exemption # ---------------------------------------------------------- From 341d0a775d0ba0c3533d2e139f50ff975afbd114 Mon Sep 17 00:00:00 2001 From: Scott Kitterman Date: Thu, 26 Jul 2018 00:51:58 -0400 Subject: [PATCH 77/87] CHANGES and titivation of last commit --- CHANGES | 2 ++ postfix-policyd-spf-perl | 13 +++++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index 1fe6493..e777128 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,8 @@ --- 2.011 UNRELEASED * Added mention of the requirement for Sys::Syslog to INSTALL * Add references to the current upstream location on Launchpad + + Add option to skip SPF checks on exempt domains based on /etc/postfix/ + exempt_spf_domains --- 2.010 2012-06-17 * Fixed incorrect use of != instead of ne for string comparison diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index b5f36a9..3491f00 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -5,10 +5,11 @@ # http://www.openspf.org/Software # version 2.011 # -# (C) 2007-2008,2012 Scott Kitterman -# (C) 2012 Allison Randal -# (C) 2007 Julian Mehnle -# (C) 2003-2004 Meng Weng Wong +# (C) 2007-2008,2012,2018 Scott Kitterman +# (C) 2018 Scott Savarese +# (C) 2012 Allison Randal +# (C) 2007 Julian Mehnle +# (C) 2003-2004 Meng Weng Wong # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -64,8 +65,8 @@ my @HANDLERS = ( code => \&exempt_relay }, { - name => 'exempt_domains', - code => \&exempt_domains + name => 'exempt_domains', + code => \&exempt_domains }, { name => 'sender_policy_framework', From 652e07ced7cfbd8bc895384bdd8dacabeb25d2b6 Mon Sep 17 00:00:00 2001 From: Scott Kitterman Date: Thu, 26 Jul 2018 03:06:18 -0400 Subject: [PATCH 78/87] Non-working attempt at config file for relay exemption --- postfix-policyd-spf-perl | 41 ++++++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index 3491f00..0e2da49 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -78,8 +78,9 @@ my $VERBOSE = 0; my $DEFAULT_RESPONSE = 'DUNNO'; -# Read in exempt domains list +# Read in exemption lists my $exempt_domains = get_exempt_domains( "/etc/postfix/exempt_spf_domains" ); +my $relay_addresses = get_exempt_address("/etc/postfix/exempt_spf_addresses"); # # Syslogging options for verbose mode and for fatal errors. @@ -97,11 +98,6 @@ use constant localhost_addresses => map( qw( 127.0.0.0/8 ::ffff:127.0.0.0/104 ::1 ) ); # Does Postfix ever say "client_address=::ffff:"? -use constant relay_addresses => map( - NetAddr::IP->new($_), - qw( ) -); # add addresses to qw ( ) above separated by spaces using CIDR notation. - # Fully qualified hostname, if available, for use in authentication results # headers now provided by the localhost and whitelist checks. my $host = hostname_long; @@ -195,6 +191,7 @@ while () { # ---------------------------------------------------------- # handler: domain exemption # ---------------------------------------------------------- + sub get_exempt_domains { my ( $file ) = @_; @@ -255,6 +252,38 @@ sub exempt_localhost { # handler: relay exemption # ---------------------------------------------------------- +sub get_exempt_address { + my ( $file ) = @_; + + my $list = {}; + + # Return nothing if file not found + if ( ! -r $file ) { + return $list; + } + + # Read the file into one variable, split on space or comma (or all) + open ( FILE, $file ) or die "Can't open $file: $!\n"; + my $text = ""; + while ( my $tmp = ) { + $text .= $tmp; + } + close( FILE ); + + #$list => map( + # NetAddr::IP->new($_), + # qw( $text ) + #); # add addresses to qw ( ) above separated by spaces using CIDR notation. + + foreach my $addr ( split( /[\s,]+/, $text ) ) { + map( + NetAddr::IP->new($list), + qw( $addr ) + ); + } + return $list; +} + sub exempt_relay { my %options = @_; my $attr = $options{attr}; From eae14d029dd0712d71664c39f664d316667a2dc7 Mon Sep 17 00:00:00 2001 From: Scott Savarese Date: Sun, 29 Jul 2018 13:02:49 -0400 Subject: [PATCH 79/87] Fix switch to use /etc/postfix/exempt_spf_addresses for skipped relays --- postfix-policyd-spf-perl | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index 0e2da49..cb1c47f 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -255,7 +255,7 @@ sub exempt_localhost { sub get_exempt_address { my ( $file ) = @_; - my $list = {}; + my $list = []; # Return nothing if file not found if ( ! -r $file ) { @@ -270,16 +270,8 @@ sub get_exempt_address { } close( FILE ); - #$list => map( - # NetAddr::IP->new($_), - # qw( $text ) - #); # add addresses to qw ( ) above separated by spaces using CIDR notation. - foreach my $addr ( split( /[\s,]+/, $text ) ) { - map( - NetAddr::IP->new($list), - qw( $addr ) - ); + push( @$list, NetAddr::IP->new($addr) ); } return $list; } @@ -290,7 +282,7 @@ sub exempt_relay { if ($attr->{client_address} ne '') { my $client_address = NetAddr::IP->new($attr->{client_address}); return "PREPEND Authentication-Results: $host; none (SPF not checked for whitelisted relay)" - if grep($_->contains($client_address), relay_addresses); + if grep($_->contains($client_address), @$relay_addresses); }; return 'DUNNO'; } From 8530b85957f921fe3f9c3f9edafd4ba21ad47dfd Mon Sep 17 00:00:00 2001 From: Scott Kitterman Date: Sun, 29 Jul 2018 13:15:42 -0400 Subject: [PATCH 80/87] Add CHANGES for exempt_spf_addresses changes --- CHANGES | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index e777128..e8d31e0 100644 --- a/CHANGES +++ b/CHANGES @@ -8,7 +8,10 @@ * Added mention of the requirement for Sys::Syslog to INSTALL * Add references to the current upstream location on Launchpad + Add option to skip SPF checks on exempt domains based on /etc/postfix/ - exempt_spf_domains + exempt_spf_domains (Thanks to Scott Savarese for the patch) + ! Switch relay_addresses to use /etc/postfix/exempt_spf_addresses instead + of hand editing the code (Thanks to Scott Savarese for pushing this over + the finish line) --- 2.010 2012-06-17 * Fixed incorrect use of != instead of ne for string comparison From 5d8ffda27bd8d523adb0871936901f882140cbe8 Mon Sep 17 00:00:00 2001 From: Scott Kitterman Date: Sun, 29 Jul 2018 14:57:27 -0400 Subject: [PATCH 81/87] Add config.sh and update README/INSTALL so we can configure the script for non-standard configuration directories --- INSTALL | 2 + README | 15 +- config.sh | 7 + postfix-policyd-spf-perl | 0 postfix-policyd-spf-perl.in | 469 ++++++++++++++++++++++++++++++++++++ 5 files changed, 489 insertions(+), 4 deletions(-) create mode 100755 config.sh mode change 100755 => 100644 postfix-policyd-spf-perl create mode 100755 postfix-policyd-spf-perl.in diff --git a/INSTALL b/INSTALL index 7912567..01911c7 100644 --- a/INSTALL +++ b/INSTALL @@ -13,6 +13,8 @@ postfix-policyd-spf-perl: Installing ---------- + 0. If your postfix config directory is not /etc/postifx, see the README for + additional instructions and adjust the path in step 2 accordingly. 1. Copy postfix-policyd-spf-perl to /usr/local/lib/policyd-spf-perl diff --git a/README b/README index ecd954b..1e039fc 100644 --- a/README +++ b/README @@ -31,10 +31,11 @@ Mail From None even if HELO is Pass). The policy server skips SPF checks for connections from the localhost (127.) and instead prepends and logs 'SPF skipped - localhost is always allowed.' If you -have relays that you want to skip SPF checks for, you can add them to -relay_addresses on line 78 using standard CIDR notation in a space separated -list. For these addresses, 'X-Comment: SPF skipped for whitelisted relay' is -prepended and logged. IPv6 localhost is also skipped. +have relays that you want to skip SPF checks for, create a configuration file, +/etc/postfix/exempt_spf_addresses and add them on one using standard CIDR +notation in a space separated list. For these addresses, 'X-Comment: SPF +skipped for whitelisted relay' is prepended and logged. IPv6 localhost is also +skipped. A configuration file, /etc/postfix/exempt_spf_domains, can be used to ignore domains that have broken SPF configurations that would normally @@ -42,6 +43,12 @@ fail. For those domains, add the domain to the file (one per line), and restart postfix so that the policy server can reload its configuration. The policy server will ignore the domain going forward. +The standard build for the policy server assumes that the postfix config file +directory is /etc/postfix. If this is not correct for your operating systemn, +run the provided config.sh file from the package directory and it will update +the config file directory based on the output of postconf -h config_directory. +This needs to be done before package installation. + Error conditions within the policy server (that don't result in a crash) or from Mail::SPF will return DUNNO. diff --git a/config.sh b/config.sh new file mode 100755 index 0000000..9a39b5d --- /dev/null +++ b/config.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +set -e + +CDIR=$(postconf -h config_directory) +echo "Config directory: ${CDIR}" +sed -e 's|@@CDIR@@|'"${CDIR}"'|g' postfix-policyd-spf-perl.in > postfix-policyd-spf-perl diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl old mode 100755 new mode 100644 diff --git a/postfix-policyd-spf-perl.in b/postfix-policyd-spf-perl.in new file mode 100755 index 0000000..60df32e --- /dev/null +++ b/postfix-policyd-spf-perl.in @@ -0,0 +1,469 @@ +#!/usr/bin/perl + +# postfix-policyd-spf-perl +# https://launchpad.net/postfix-policyd-spf-perl +# http://www.openspf.org/Software +# version 2.011 +# +# (C) 2007-2008,2012,2018 Scott Kitterman +# (C) 2018 Scott Savarese +# (C) 2012 Allison Randal +# (C) 2007 Julian Mehnle +# (C) 2003-2004 Meng Weng Wong +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use version; our $VERSION = qv('2.011'); + +use strict; + +use IO::Handle; +use Sys::Syslog qw(:DEFAULT setlogsock); +use NetAddr::IP; +use Mail::SPF; +use Sys::Hostname::Long 'hostname_long'; + +# ---------------------------------------------------------- +# configuration +# ---------------------------------------------------------- + +my $resolver = Net::DNS::Resolver->new( + retrans => 5, # Net::DNS::Resolver default: 5 + retry => 2, # Net::DNS::Resolver default: 4 + # Makes for a total timeout for UDP queries of 5s * 2 = 10s. +); + +# query_rr_type_all will query both type TXT and type SPF. This upstream +# default is changed due to there being essentiall no type SPF deployment. +my $spf_server = Mail::SPF::Server->new( + dns_resolver => $resolver, + query_rr_types => Mail::SPF::Server->query_rr_type_txt, + default_authority_explanation => + 'Please see http://www.openspf.net/Why?s=%{_scope};id=%{S};ip=%{C};r=%{R}' +); + +# Adding more handlers is easy: +my @HANDLERS = ( + { + name => 'exempt_localhost', + code => \&exempt_localhost + }, + { + name => 'exempt_relay', + code => \&exempt_relay + }, + { + name => 'exempt_domains', + code => \&exempt_domains + }, + { + name => 'sender_policy_framework', + code => \&sender_policy_framework + } +); + +my $VERBOSE = 0; + +my $DEFAULT_RESPONSE = 'DUNNO'; + +# Read in exemption lists +my $exempt_domains = get_exempt_domains( "@@CDIR@@/exempt_spf_domains" ); +my $relay_addresses = get_exempt_address("@@CDIR@@/exempt_spf_addresses"); + +# +# Syslogging options for verbose mode and for fatal errors. +# NOTE: comment out the $syslog_socktype line if syslogging does not +# work on your system. +# + +my $syslog_socktype = 'unix'; # inet, unix, stream, console +my $syslog_facility = 'mail'; +my $syslog_options = 'pid'; +my $syslog_ident = 'postfix/policy-spf'; + +use constant localhost_addresses => map( + NetAddr::IP->new($_), + qw( 127.0.0.0/8 ::ffff:127.0.0.0/104 ::1 ) +); # Does Postfix ever say "client_address=::ffff:"? + +# Fully qualified hostname, if available, for use in authentication results +# headers now provided by the localhost and whitelist checks. +my $host = hostname_long; + +my %results_cache; # by message instance + +# ---------------------------------------------------------- +# initialization +# ---------------------------------------------------------- + +# +# Log an error and abort. +# +sub fatal_exit { + syslog(err => "fatal_exit: @_"); + syslog(warning => "fatal_exit: @_"); + syslog(info => "fatal_exit: @_"); + die("fatal: @_"); +} + +# +# Unbuffer standard output. +# +STDOUT->autoflush(1); + +# +# This process runs as a daemon, so it can't log to a terminal. Use +# syslog so that people can actually see our messages. +# +setlogsock($syslog_socktype); +openlog($syslog_ident, $syslog_options, $syslog_facility); + +# ---------------------------------------------------------- +# main +# ---------------------------------------------------------- + +# +# Receive a bunch of attributes, evaluate the policy, send the result. +# +my %attr; +while () { + chomp; + + if (/=/) { + my ($key, $value) =split (/=/, $_, 2); + $attr{$key} = $value; + next; + } + elsif (length) { + syslog(warning => sprintf("warning: ignoring garbage: %.100s", $_)); + next; + } + + if ($VERBOSE) { + for (sort keys %attr) { + syslog(debug => "Attribute: %s=%s", $_ || '', $attr{$_} || ''); + } + }; + + my $message_instance = $attr{instance}; + my $cache = defined($message_instance) ? $results_cache{$message_instance} ||= {} : {}; + + my $action = $DEFAULT_RESPONSE; + + foreach my $handler (@HANDLERS) { + my $handler_name = $handler->{name}; + my $handler_code = $handler->{code}; + + my $response = $handler_code->(attr => \%attr, cache => $cache); + + if ($VERBOSE) { + syslog(debug => "handler %s: %s", $handler_name || '', $response || ''); + }; + + # Pick whatever response is not 'DUNNO' + if ($response and $response !~ /^DUNNO/i) { + if ($VERBOSE) { + syslog(info => "handler %s: is decisive.", $handler_name || ''); + } + $action = $response; + last; + } + } + + syslog(info => "Policy action=%s", $action || ''); + + STDOUT->print("action=$action\n\n"); + %attr = (); +} + +# ---------------------------------------------------------- +# handler: domain exemption +# ---------------------------------------------------------- + +sub get_exempt_domains { + my ( $file ) = @_; + + my $list = {}; + + # Return nothing if file not found + if ( ! -r $file ) { + return $list; + } + + # Read the file into one variable, split on space or comma (or all) + open ( FILE, $file ) or die "Can't open $file: $!\n"; + my $text = ""; + while ( my $tmp = ) { + $text .= $tmp; + } + close( FILE ); + + foreach my $domain ( split( /[\s,]+/, $text ) ) { + $list->{$domain} = 1; + } + + return $list; +} + +sub exempt_domains { + my %options = @_; + my $attr = $options{attr}; + + my $domain = ( split( /\@/, $attr->{sender} ) )[1]; + return 'DUNNO' if ( ( ! defined( $domain ) ) or ( $domain eq '' ) ); + + # Check the domain against our list of ignored domains + if ( defined( $exempt_domains->{$domain} ) ) { + return "PREPEND Authentication-Results: $host; none " . + "(SPF exempted by policy)"; + } + + return 'DUNNO'; +} + +# ---------------------------------------------------------- +# handler: localhost exemption +# ---------------------------------------------------------- + +sub exempt_localhost { + my %options = @_; + my $attr = $options{attr}; + if ($attr->{client_address} ne '') { + my $client_address = NetAddr::IP->new($attr->{client_address}); + return "PREPEND Authentication-Results: $host; none (SPF not checked for localhost)" + if grep($_->contains($client_address), localhost_addresses); + }; + return 'DUNNO'; +} + +# ---------------------------------------------------------- +# handler: relay exemption +# ---------------------------------------------------------- + +sub get_exempt_address { + my ( $file ) = @_; + + my $list = []; + + # Return nothing if file not found + if ( ! -r $file ) { + return $list; + } + + # Read the file into one variable, split on space or comma (or all) + open ( FILE, $file ) or die "Can't open $file: $!\n"; + my $text = ""; + while ( my $tmp = ) { + $text .= $tmp; + } + close( FILE ); + + foreach my $addr ( split( /[\s,]+/, $text ) ) { + push( @$list, NetAddr::IP->new($addr) ); + } + return $list; +} + +sub exempt_relay { + my %options = @_; + my $attr = $options{attr}; + if ($attr->{client_address} ne '') { + my $client_address = NetAddr::IP->new($attr->{client_address}); + return "PREPEND Authentication-Results: $host; none (SPF not checked for whitelisted relay)" + if grep($_->contains($client_address), @$relay_addresses); + }; + return 'DUNNO'; +} + +# ---------------------------------------------------------- +# handler: SPF +# ---------------------------------------------------------- + +sub sender_policy_framework { + my %options = @_; + my $attr = $options{attr}; + my $cache = $options{cache}; + + # ------------------------------------------------------------------------- + # Always do HELO check first. If no HELO policy, it's only one lookup. + # This avoids the need to do any MAIL FROM processing for null sender. + # ------------------------------------------------------------------------- + + my $helo_result = $cache->{helo_result}; + + if (not defined($helo_result)) { + # No HELO result has been cached from earlier checks on this message. + + my $helo_request = eval { + Mail::SPF::Request->new( + scope => 'helo', + identity => $attr->{helo_name}, + ip_address => $attr->{client_address} + ); + }; + + if ($@) { + # An unexpected error occurred during request creation, + # probably due to invalid input data! + my $errmsg = $@; + $errmsg = $errmsg->text if UNIVERSAL::isa($@, 'Mail::SPF::Exception'); + if ($VERBOSE) { + syslog( + info => "HELO check failed - Mail::SPF->new(%s, %s, %s) failed: %s", + $attr->{client_address} || '', + $attr->{sender} || '', $attr->{helo_name} || '', + $errmsg || '' + ); + }; + return; + } + + $helo_result = $cache->{helo_result} = $spf_server->process($helo_request); + } + + my $helo_result_code = $helo_result->code; # 'pass', 'fail', etc. + my $helo_local_exp = nullchomp($helo_result->local_explanation); + my $helo_authority_exp = nullchomp($helo_result->authority_explanation) + if $helo_result->is_code('fail'); + my $helo_spf_header = $helo_result->received_spf_header; + + if ($VERBOSE) { + syslog( + info => "SPF %s: HELO/EHLO: %s, IP Address: %s, Recipient: %s", + $helo_result || '', + $attr->{helo_name} || '', $attr->{client_address} || '', + $attr->{recipient} || '' + ); + }; + + # Reject on HELO fail. Defer on HELO temperror if message would otherwise + # be accepted. Use the HELO result and return for null sender. + if ($helo_result->is_code('fail')) { + if ($VERBOSE) { + syslog( + info => "SPF %s: HELO/EHLO: %s", + $helo_result || '', + $attr->{helo_name} || '' + ); + }; + return "550 $helo_authority_exp"; + } + elsif ($helo_result->is_code('temperror')) { + if ($VERBOSE) { + syslog( + info => "SPF %s: HELO/EHLO: %s", + $helo_result || '', + $attr->{helo_name} || '' + ); + }; + return "DEFER_IF_PERMIT SPF-Result=$helo_local_exp"; + } + elsif ($attr->{sender} eq '') { + if ($VERBOSE) { + syslog( + info => "SPF %s: HELO/EHLO (Null Sender): %s", + $helo_result || '', + $attr->{helo_name} || '' + ); + }; + return "PREPEND $helo_spf_header" + unless $cache->{added_spf_header}++; + } + + # ------------------------------------------------------------------------- + # Do MAIL FROM check (as HELO did not give a definitive result) + # ------------------------------------------------------------------------- + + my $mfrom_result = $cache->{mfrom_result}; + + if (not defined($mfrom_result)) { + # No MAIL FROM result has been cached from earlier checks on this message. + + my $mfrom_request = eval { + Mail::SPF::Request->new( + scope => 'mfrom', + identity => $attr->{sender}, + ip_address => $attr->{client_address}, + helo_identity => $attr->{helo_name} # for %{h} macro expansion + ); + }; + + if ($@) { + # An unexpected error occurred during request creation, + # probably due to invalid input data! + my $errmsg = $@; + $errmsg = $errmsg->text if UNIVERSAL::isa($@, 'Mail::SPF::Exception'); + if ($VERBOSE) { + syslog( + info => "Mail From (sender) check failed - Mail::SPF->new(%s, %s, %s) failed: %s", + $attr->{client_address} || '', + $attr->{sender} || '', $attr->{helo_name} || '', $errmsg || '' + ); + }; + return; + } + + $mfrom_result = $cache->{mfrom_result} = $spf_server->process($mfrom_request); + } + + my $mfrom_result_code = $mfrom_result->code; # 'pass', 'fail', etc. + my $mfrom_local_exp = nullchomp($mfrom_result->local_explanation); + my $mfrom_authority_exp = nullchomp($mfrom_result->authority_explanation) + if $mfrom_result->is_code('fail'); + my $mfrom_spf_header = $mfrom_result->received_spf_header; + + if ($VERBOSE) { + syslog( + info => "SPF %s: Envelope-from: %s, IP Address: %s, Recipient: %s", + $mfrom_result || '', + $attr->{sender} || '', $attr->{client_address} || '', + $attr->{recipient} || '' + ); + }; + + # Same approach as HELO.... + if ($VERBOSE) { + syslog( + info => "SPF %s: Envelope-from: %s", + $mfrom_result || '', + $attr->{sender} || '' + ); + }; + if ($mfrom_result->is_code('fail')) { + return "550 $mfrom_authority_exp"; + } + elsif ($mfrom_result->is_code('temperror')) { + return "DEFER_IF_PERMIT SPF-Result=$mfrom_local_exp"; + } + else { + return "PREPEND $mfrom_spf_header" + unless $cache->{added_spf_header}++; + } + + return; +} + +# ---------------------------------------------------------- +# utility, string cleaning +# ---------------------------------------------------------- + +sub nullchomp { + my $value = shift; + + # Remove one or more null characters from the + # end of the input. + $value =~ s/\0+$//; + return $value; +} From 570e9836d71f0b9801d601bdb4e5a04af5b83e83 Mon Sep 17 00:00:00 2001 From: Scott Kitterman Date: Sun, 29 Jul 2018 15:00:03 -0400 Subject: [PATCH 82/87] Fixup CHANGES for release --- CHANGES | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/CHANGES b/CHANGES index e8d31e0..bb7dc04 100644 --- a/CHANGES +++ b/CHANGES @@ -4,22 +4,24 @@ # ! = Changed something significant, or removed a feature # * = Fixed a bug, or made a minor improvement ---- 2.011 UNRELEASED - * Added mention of the requirement for Sys::Syslog to INSTALL - * Add references to the current upstream location on Launchpad - + Add option to skip SPF checks on exempt domains based on /etc/postfix/ - exempt_spf_domains (Thanks to Scott Savarese for the patch) - ! Switch relay_addresses to use /etc/postfix/exempt_spf_addresses instead - of hand editing the code (Thanks to Scott Savarese for pushing this over - the finish line) +--- 2.011 2018-07-29 + * Added mention of the requirement for Sys::Syslog to INSTALL + * Add references to the current upstream location on Launchpad + + Add option to skip SPF checks on exempt domains based on /etc/postfix/ + exempt_spf_domains (Thanks to Scott Savarese for the patch) + ! Switch relay_addresses to use /etc/postfix/exempt_spf_addresses instead + of hand editing the code (Thanks to Scott Savarese for pushing this over + the finish line) + + Add config.sh (see README for details) to support different postfix + configuration directories --- 2.010 2012-06-17 - * Fixed incorrect use of != instead of ne for string comparison - (LP: #1014243) - ! Changed non-standard X-Comment header fields for localhost and - whitelisted addresses to use RFC 5451 Authentication Results header - fields - ! Added depenency on Sys::Hostname::Long for local hostname determination + * Fixed incorrect use of != instead of ne for string comparison + (LP: #1014243) + ! Changed non-standard X-Comment header fields for localhost and + whitelisted addresses to use RFC 5451 Authentication Results header + fields + ! Added depenency on Sys::Hostname::Long for local hostname determination --- 2.009 2012-02-03 * Chomp erroneus NULLs off the end of local and authority explanations to From ad8e2849cb5ec066d94f28cd3addff4b9a7cc87a Mon Sep 17 00:00:00 2001 From: Scott Kitterman Date: Sun, 29 Jul 2018 15:00:57 -0400 Subject: [PATCH 83/87] Fixup execture permissions --- postfix-policyd-spf-perl | 0 postfix-policyd-spf-perl.in | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 postfix-policyd-spf-perl mode change 100755 => 100644 postfix-policyd-spf-perl.in diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl old mode 100644 new mode 100755 diff --git a/postfix-policyd-spf-perl.in b/postfix-policyd-spf-perl.in old mode 100755 new mode 100644 From 151caf449bda9973b88da74403ec98d8230a2d87 Mon Sep 17 00:00:00 2001 From: Scott Kitterman Date: Sun, 29 Jul 2018 15:03:39 -0400 Subject: [PATCH 84/87] More CHANGES --- CHANGES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index bb7dc04..b1cc6d9 100644 --- a/CHANGES +++ b/CHANGES @@ -11,7 +11,7 @@ exempt_spf_domains (Thanks to Scott Savarese for the patch) ! Switch relay_addresses to use /etc/postfix/exempt_spf_addresses instead of hand editing the code (Thanks to Scott Savarese for pushing this over - the finish line) + the finish line) (Debian #902801) + Add config.sh (see README for details) to support different postfix configuration directories From ce6fbdf659e04b61446cc872f75b867bc34794f4 Mon Sep 17 00:00:00 2001 From: Scott Kitterman Date: Sun, 29 Jul 2018 15:10:04 -0400 Subject: [PATCH 85/87] * Change domain back to openspf.org, it has been back for a long time (Debian #900512) --- CHANGES | 2 ++ README | 2 +- postfix-policyd-spf-perl | 2 +- postfix-policyd-spf-perl.in | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index b1cc6d9..c260f5b 100644 --- a/CHANGES +++ b/CHANGES @@ -14,6 +14,8 @@ the finish line) (Debian #902801) + Add config.sh (see README for details) to support different postfix configuration directories + * Change domain back to openspf.org, it has been back for a long time + (Debian #900512) --- 2.010 2012-06-17 * Fixed incorrect use of != instead of ne for string comparison diff --git a/README b/README index 1e039fc..8c7f3f6 100644 --- a/README +++ b/README @@ -5,7 +5,7 @@ A Postfix SMTPd policy server for SPF checking (C) 2007 Julian Mehnle (C) 2003-2004 Meng Weng Wong Thanks for contributions by various members of the SPF project - + ============================================================================== diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index cb1c47f..29d5f3f 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -51,7 +51,7 @@ my $spf_server = Mail::SPF::Server->new( dns_resolver => $resolver, query_rr_types => Mail::SPF::Server->query_rr_type_txt, default_authority_explanation => - 'Please see http://www.openspf.net/Why?s=%{_scope};id=%{S};ip=%{C};r=%{R}' + 'Please see http://www.openspf.org/Why?s=%{_scope};id=%{S};ip=%{C};r=%{R}' ); # Adding more handlers is easy: diff --git a/postfix-policyd-spf-perl.in b/postfix-policyd-spf-perl.in index 60df32e..83dbf78 100644 --- a/postfix-policyd-spf-perl.in +++ b/postfix-policyd-spf-perl.in @@ -51,7 +51,7 @@ my $spf_server = Mail::SPF::Server->new( dns_resolver => $resolver, query_rr_types => Mail::SPF::Server->query_rr_type_txt, default_authority_explanation => - 'Please see http://www.openspf.net/Why?s=%{_scope};id=%{S};ip=%{C};r=%{R}' + 'Please see http://www.openspf.org/Why?s=%{_scope};id=%{S};ip=%{C};r=%{R}' ); # Adding more handlers is easy: From c9757b4635c856c09436948765fcd5119324e34f Mon Sep 17 00:00:00 2001 From: Scott Kitterman Date: Sun, 29 Jul 2018 15:17:23 -0400 Subject: [PATCH 86/87] Mention file permissions in README --- README | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README b/README index 8c7f3f6..17c334a 100644 --- a/README +++ b/README @@ -43,6 +43,9 @@ fail. For those domains, add the domain to the file (one per line), and restart postfix so that the policy server can reload its configuration. The policy server will ignore the domain going forward. +If defined, the configuration files described above need to have permissions +to allow the policy server to read the files. + The standard build for the policy server assumes that the postfix config file directory is /etc/postfix. If this is not correct for your operating systemn, run the provided config.sh file from the package directory and it will update From 5db125ae861f2e8455d417460eb3473ca810815f Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 15 Oct 2025 21:15:05 +0700 Subject: [PATCH 87/87] The script now operates in monitoring/observation mode, where SPF results are recorded in headers but never cause email rejection or deferral. --- postfix-policyd-spf-perl | 16 ++++++++++------ postfix-policyd-spf-perl.in | 16 ++++++++++------ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/postfix-policyd-spf-perl b/postfix-policyd-spf-perl index 29d5f3f..322111e 100755 --- a/postfix-policyd-spf-perl +++ b/postfix-policyd-spf-perl @@ -348,8 +348,8 @@ sub sender_policy_framework { ); }; - # Reject on HELO fail. Defer on HELO temperror if message would otherwise - # be accepted. Use the HELO result and return for null sender. + # Prepend header on HELO fail instead of rejecting. + # Use the HELO result and return for null sender. if ($helo_result->is_code('fail')) { if ($VERBOSE) { syslog( @@ -358,7 +358,8 @@ sub sender_policy_framework { $attr->{helo_name} || '' ); }; - return "550 $helo_authority_exp"; + return "PREPEND $helo_spf_header" + unless $cache->{added_spf_header}++; } elsif ($helo_result->is_code('temperror')) { if ($VERBOSE) { @@ -368,7 +369,8 @@ sub sender_policy_framework { $attr->{helo_name} || '' ); }; - return "DEFER_IF_PERMIT SPF-Result=$helo_local_exp"; + return "PREPEND $helo_spf_header" + unless $cache->{added_spf_header}++; } elsif ($attr->{sender} eq '') { if ($VERBOSE) { @@ -442,10 +444,12 @@ sub sender_policy_framework { ); }; if ($mfrom_result->is_code('fail')) { - return "550 $mfrom_authority_exp"; + return "PREPEND $mfrom_spf_header" + unless $cache->{added_spf_header}++; } elsif ($mfrom_result->is_code('temperror')) { - return "DEFER_IF_PERMIT SPF-Result=$mfrom_local_exp"; + return "PREPEND $mfrom_spf_header" + unless $cache->{added_spf_header}++; } else { return "PREPEND $mfrom_spf_header" diff --git a/postfix-policyd-spf-perl.in b/postfix-policyd-spf-perl.in index 83dbf78..a626c89 100644 --- a/postfix-policyd-spf-perl.in +++ b/postfix-policyd-spf-perl.in @@ -348,8 +348,8 @@ sub sender_policy_framework { ); }; - # Reject on HELO fail. Defer on HELO temperror if message would otherwise - # be accepted. Use the HELO result and return for null sender. + # Prepend header on HELO fail instead of rejecting. + # Use the HELO result and return for null sender. if ($helo_result->is_code('fail')) { if ($VERBOSE) { syslog( @@ -358,7 +358,8 @@ sub sender_policy_framework { $attr->{helo_name} || '' ); }; - return "550 $helo_authority_exp"; + return "PREPEND $helo_spf_header" + unless $cache->{added_spf_header}++; } elsif ($helo_result->is_code('temperror')) { if ($VERBOSE) { @@ -368,7 +369,8 @@ sub sender_policy_framework { $attr->{helo_name} || '' ); }; - return "DEFER_IF_PERMIT SPF-Result=$helo_local_exp"; + return "PREPEND $helo_spf_header" + unless $cache->{added_spf_header}++; } elsif ($attr->{sender} eq '') { if ($VERBOSE) { @@ -442,10 +444,12 @@ sub sender_policy_framework { ); }; if ($mfrom_result->is_code('fail')) { - return "550 $mfrom_authority_exp"; + return "PREPEND $mfrom_spf_header" + unless $cache->{added_spf_header}++; } elsif ($mfrom_result->is_code('temperror')) { - return "DEFER_IF_PERMIT SPF-Result=$mfrom_local_exp"; + return "PREPEND $mfrom_spf_header" + unless $cache->{added_spf_header}++; } else { return "PREPEND $mfrom_spf_header"