postfix-policyd-spf-perl/trunk/postfix-policyd -> postfix-policyd-spf

* Renamed file.
* Version 1.01.
This commit is contained in:
Julian Mehnle 2006-06-17 16:31:08 +00:00
commit 9090c7b7f2

View file

@ -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 (<STDIN>) {
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;
}