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; -} - -