postfix-policyd-spf-perl/trunk/postfix-policyd-spf-perl
* Medium clean-up.
This commit is contained in:
parent
da0f7279b6
commit
1af44573f7
1 changed files with 86 additions and 58 deletions
|
|
@ -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";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue