postfix-policyd-spf-perl/trunk/postfix-policyd -> postfix-policyd-spf
* Renamed file. * Version 1.01.
This commit is contained in:
parent
7df1322ffd
commit
9090c7b7f2
1 changed files with 19 additions and 161 deletions
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue