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

* Medium clean-up.
This commit is contained in:
Julian Mehnle 2007-02-04 19:20:58 +00:00
commit 1af44573f7

View file

@ -25,21 +25,23 @@ use version; our $VERSION = qv('1.990');
use strict; use strict;
use Fcntl; use IO::Handle;
use Sys::Syslog qw(:DEFAULT setlogsock); use Sys::Syslog qw(:DEFAULT setlogsock);
use Mail::SPF; use Mail::SPF;
# ---------------------------------------------------------- # ----------------------------------------------------------
# configuration # configuration
# ---------------------------------------------------------- # ----------------------------------------------------------
my $spf_server = Mail::SPF::Server->new(); my $spf_server = Mail::SPF::Server->new();
my @HANDLERS; my @HANDLERS;
push @HANDLERS, "sender_policy_framework"; push(@HANDLERS, \&sender_policy_framework);
#Leaving this to make it easier to add others later. # Leaving this to make it easier to add others later.
my $VERBOSE = 0; my $VERBOSE = 0;
my $DEFAULT_RESPONSE = "DUNNO"; my $DEFAULT_RESPONSE = 'DUNNO';
# #
# Syslogging options for verbose mode and for fatal errors. # 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_socktype = 'unix'; # inet, unix, stream, console
my $syslog_facility = "mail"; my $syslog_facility = 'mail';
my $syslog_options = "pid"; my $syslog_options = 'pid';
my $syslog_ident = "postfix/policy-spf"; my $syslog_ident = 'postfix/policy-spf';
# ---------------------------------------------------------- # ----------------------------------------------------------
# initialization # initialization
@ -63,20 +65,20 @@ sub fatal_exit {
syslog(err => "fatal_exit: @_"); syslog(err => "fatal_exit: @_");
syslog(warning => "fatal_exit: @_"); syslog(warning => "fatal_exit: @_");
syslog(info => "fatal_exit: @_"); syslog(info => "fatal_exit: @_");
die "fatal: @_"; die("fatal: @_");
} }
# #
# Unbuffer standard output. # 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 # This process runs as a daemon, so it can't log to a terminal. Use
# syslog so that people can actually see our messages. # syslog so that people can actually see our messages.
# #
setlogsock $syslog_socktype; setlogsock($syslog_socktype);
openlog $syslog_ident, $syslog_options, $syslog_facility; openlog($syslog_ident, $syslog_options, $syslog_facility);
# ---------------------------------------------------------- # ----------------------------------------------------------
# main # main
@ -85,11 +87,19 @@ openlog $syslog_ident, $syslog_options, $syslog_facility;
# #
# Receive a bunch of attributes, evaluate the policy, send the result. # Receive a bunch of attributes, evaluate the policy, send the result.
# #
my %attr;
while (<STDIN>) { while (<STDIN>) {
chomp; 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) { if ($VERBOSE) {
for (sort keys %attr) { for (sort keys %attr) {
@ -100,47 +110,49 @@ while (<STDIN>) {
my $action = $DEFAULT_RESPONSE; my $action = $DEFAULT_RESPONSE;
my %responses; my %responses;
foreach my $handler (@HANDLERS) { foreach my $handler (@HANDLERS) {
no strict 'refs'; my $response = $handler->(attr => \%attr);
my $response = $handler->(attr=>\%attr);
if ($VERBOSE) { if ($VERBOSE) {
syslog(debug => "handler %s: %s", $handler, $response); 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) { if ($response and $response !~ /^dunno/i) {
syslog(info => "handler %s: is decisive.", $handler); syslog(info => "handler %s: is decisive.", $handler);
$action = $response; last; $action = $response;
last;
} }
} }
syslog(info => "Policy action=%s", $action); syslog(info => "Policy action=%s", $action);
print STDOUT "action=$action\n\n"; STDOUT->print("action=$action\n\n");
%attr = ();
} }
# ---------------------------------------------------------- # ----------------------------------------------------------
# plugin: SPF # plugin: SPF
# ---------------------------------------------------------- # ----------------------------------------------------------
sub sender_policy_framework { sub sender_policy_framework {
local %_ = @_; my %options = @_;
my %attr = %{ $_{attr} }; my $attr = $options{attr};
# Always do HELO check first. If no HELO policy it's only one lookup. # 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. # Avoids the need to do any Mail From processing for null sender.
my $helo_request = eval { my $helo_request = eval {
Mail::SPF::Request->new( Mail::SPF::Request->new(
scope => 'helo', # 'mfrom' or 'helo', 'pra' scope => 'helo',
identity => $attr{helo_name}, identity => $attr->{helo_name},
ip_address => $attr{client_address}, ip_address => $attr->{client_address}
helo_identity # optional,
=> $attr{helo_name}
); );
}; };
# If initializing helo_request throws an error, don't use it. # If initializing helo_request throws an error, don't use it.
if ($@) { if ($@) {
my $errmsg = $@;
$errmsg = $errmsg->text if UNIVERSAL::isa($@, 'Mail::SPF::Exception');
syslog( syslog(
info => "%s: Mail::SPF->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}, $@ $attr->{queue_id}, $attr->{client_address}, $attr->{sender}, $attr->{helo_name}, $errmsg
); );
return "DUNNO"; return "DUNNO";
} }
@ -155,29 +167,38 @@ sub sender_policy_framework {
syslog( syslog(
info => "%s: SPF %s: HELO/EHLO: %s, IP Address: %s, Recipient: %s", 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}
); );
# Reject on HELO fail. Defer on HELO temperror if message would otherwis # Reject on HELO fail. Defer on HELO temperror if message would otherwise
# be accepted. Use the HELO result and return for null sender. # be accepted. Use the HELO result and return for null sender.
if ($helo_result_code eq "fail") { return "REJECT $helo_authority_exp"; } if ($helo_result->is_code('fail')) {
elsif ($helo_result_code eq "temperror") { return "DEFER_IF_PERMIT SPF-Result=$helo_local_exp"; } return "REJECT $helo_authority_exp";
elsif ($attr{sender} eq '') { return "PREPEND $helo_spf_header"; } }
}; 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. # Do mail from is HELO doesn't give a definitive result.
my $mfrom_request = eval { my $mfrom_request = eval {
Mail::SPF::Request->new( Mail::SPF::Request->new(
scope => 'mfrom', # 'mfrom' or 'helo', 'pra' scope => 'mfrom',
identity => $attr{sender}, identity => $attr->{sender},
ip_address => $attr{client_address}, ip_address => $attr->{client_address},
helo_identity # optional, helo_identity => $attr->{helo_name} # for %{h} macro expansion
=> $attr{helo_name} # for %{h} macro expansion
); );
}; };
if ($@) { if ($@) {
my $errmsg = $@;
$errmsg = $errmsg->text if UNIVERSAL::isa($@, 'Mail::SPF::Exception');
syslog( syslog(
info => "%s: Mail::SPF->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}, $@ $attr->{queue_id}, $attr->{client_address}, $attr->{sender}, $attr->{helo_name}, $errmsg
); );
return "DUNNO"; return "DUNNO";
} }
@ -189,14 +210,21 @@ sub sender_policy_framework {
my $mfrom_authority_exp = $mfrom_result->authority_explanation my $mfrom_authority_exp = $mfrom_result->authority_explanation
if $mfrom_result->is_code('fail'); 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( syslog(
info => "%s: SPF %s: Envelope-from: %s, IP Address: %s, Recipient: %s", 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.... # Same approach as HELO....
if ($mfrom_result_code eq "fail") { return "REJECT $mfrom_authority_exp"; } if ($mfrom_result->is_code('fail')) {
elsif ($mfrom_result_code eq "mfrom_temperror") { return "DEFER_IF_PERMIT SPF-Result=$mfrom_local_exp"; } return "REJECT $mfrom_authority_exp";
else { return "PREPEND $mfrom_spf_header"; } }
elsif ($mfrom_result->is_code('temperror')) {
return "DEFER_IF_PERMIT SPF-Result=$mfrom_local_exp";
}
else {
return "PREPEND $mfrom_spf_header";
}
} }
} }