9c700b18e1
git-svn-id: https://svn.perl.org/qpsmtpd/trunk@217 958fd67b-6ff1-0310-b445-bb7760255be9
127 lines
3.5 KiB
Plaintext
127 lines
3.5 KiB
Plaintext
|
|
=head1 NAME
|
|
|
|
SPF - plugin to implement Sender Permitted From
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
# in config/plugins
|
|
sender_permitted_from
|
|
|
|
Or if you wish to issue 5xx on SPF fail:
|
|
|
|
sender_permitted_from spf_deny 1
|
|
|
|
Other arguments are 'trust 0' and 'guess 0'. These turn off processing of
|
|
spf.trusted-forwarders.org and the best_guess functionality. It is unlikely
|
|
that you want to turn these off.
|
|
|
|
Adding 'spf_deny 2' will also issue a 5xx on a softfail response.
|
|
|
|
You can also specify local SPF policy with
|
|
|
|
include '<spf mechanism list>'
|
|
|
|
See also http://spf.pobox.com/
|
|
|
|
=cut
|
|
|
|
use Mail::SPF::Query 1.991;
|
|
|
|
sub register {
|
|
my ($self, $qp, @args) = @_;
|
|
%{$self->{_args}} = @args;
|
|
$self->register_hook("mail", "mail_handler");
|
|
$self->register_hook("rcpt", "rcpt_handler");
|
|
$self->register_hook("data_post", "data_handler");
|
|
}
|
|
|
|
sub mail_handler {
|
|
my ($self, $transaction, $sender) = @_;
|
|
|
|
return (DECLINED) unless ($sender->format ne "<>"
|
|
and $sender->host && $sender->user);
|
|
|
|
# If we are receving from a relay permitted host, then we are probably
|
|
# not the delivery system, and so we shouldn't check
|
|
|
|
return (DECLINED) if exists $ENV{RELAYCLIENT};
|
|
my @relay_clients = $self->qp->config("relayclients");
|
|
my $more_relay_clients = $self->qp->config("morerelayclients", "map");
|
|
my %relay_clients = map { $_ => 1 } @relay_clients;
|
|
my $client_ip = $self->qp->connection->remote_ip;
|
|
while ($client_ip) {
|
|
return (DECLINED) if exists $relay_clients{$client_ip};
|
|
return (DECLINED) if exists $more_relay_clients->{$client_ip};
|
|
$client_ip =~ s/\d+\.?$//; # strip off another 8 bits
|
|
}
|
|
|
|
my $host = lc $sender->host;
|
|
my $from = $sender->user . '@' . $host;
|
|
|
|
my $ip = $self->qp->connection->remote_ip;
|
|
my $helo = $self->qp->connection->hello_host;
|
|
|
|
my $query = Mail::SPF::Query->new(ip => $ip, sender => $from, helo => $helo,
|
|
sanitize => 1,
|
|
local => $self->{_args}{local},
|
|
guess => defined($self->{_args}{guess}) ? $self->{_args}{guess} : 1,
|
|
trusted => defined($self->{_args}{trust}) ? $self->{_args}{trust} : 1)
|
|
|| die "Couldn't construct Mail::SPF::Query object";
|
|
$transaction->notes('spfquery', $query);
|
|
|
|
return (DECLINED);
|
|
}
|
|
|
|
sub rcpt_handler {
|
|
my ($self, $transaction, $rcpt) = @_;
|
|
|
|
# special addresses don't get SPF-tested.
|
|
return DECLINED if $rcpt and $rcpt->user and $rcpt->user =~ /^(?:postmaster|abuse|mailer-daemon|root)$/i;
|
|
|
|
my $query = $transaction->notes('spfquery');
|
|
|
|
return DECLINED if !$query;
|
|
my ($result, $smtp_comment, $comment) = $query->result2($rcpt->address);
|
|
|
|
if ($result eq "error") {
|
|
return (DENYSOFT, "SPF error: $smtp_comment");
|
|
}
|
|
|
|
if ($result eq "fail" and $self->{_args}{spf_deny}) {
|
|
return (DENY, "SPF forgery: $smtp_comment");
|
|
}
|
|
|
|
if ($result eq "softfail" and $self->{_args}{spf_deny} > 1) {
|
|
return (DENY, "SPF probable forgery: $smtp_comment");
|
|
}
|
|
|
|
if ($result eq 'fail' or $result eq 'softfail') {
|
|
$self->log(LOGDEBUG, "result for $rcpt->address was $result: $comment");
|
|
}
|
|
|
|
return DECLINED;
|
|
}
|
|
|
|
sub _uri_escape {
|
|
my $str = shift;
|
|
$str =~ s/([^A-Za-z0-9\-_.!~*\'()])/sprintf "%%%X", ord($1)/eg;
|
|
return $str;
|
|
}
|
|
|
|
sub data_handler {
|
|
my ($self, $transaction) = @_;
|
|
|
|
my $query = $transaction->notes('spfquery');
|
|
return DECLINED if !$query;
|
|
|
|
my ($result, $smtp_comment, $comment) = $query->message_result2();
|
|
|
|
$self->log(LOGDEBUG, "result was $result: $comment") if ($result);
|
|
|
|
$transaction->header->add('Received-SPF' => "$result ($comment)", 0);
|
|
|
|
return DECLINED;
|
|
}
|
|
|