I have updated the script, few if-s were optimized and default behavior is fixed to reject nothing. See attached file for details. You will need two CPAN modules pre-installed on your system:

$cpan -i Mail::SPF::Query Mail::DKIM::Verifier

Zdravko Stoychev wrote:
Hi all! I have managed to run a perl script (called from .qmail or
control/defaultdelivery) which performs SPF and DKIM checks on incoming
messages. It is work in progress and needs some tuning as white-listing
IP-nets is hardcoded (when sending from Intranet) and $dkim object
usage, behavior on soft/hard-errors is hardcoded too, etc. Still it is
working well on our ~150 mailboxes server so far. This is temporary
solution for me until real qmail-src implementation is (probably) added
for the SPF and/or DKIM RFCs. Feel free to enhance it and provide feedback.

[attached: abusecheck.pl]


--
 Zdravko Stoychev
 System Software and Support
 MPS Ltd.
 [EMAIL PROTECTED]
 +359-2-491-1827 (ext.271)

Ако не отговарям на писмата Ви - погледнете тук: http://6lyokavitza.org/mail

--------------------------------------
"Quality is never an accident; it is always the result of high intention, sincere 
effort, intelligent direction, and skillful execution; it presents the wise choice of 
many alternatives."
- W. Foster.

--------------------------------------
This e-mail is intended only for the addressee(s) and may contain privileged 
and confidential information. It should not be disseminated, distributed, or 
copied. If you have received this e-mail message by mistake, please inform the 
sender, and delete it from your system.

#!/usr/bin/perl
#
#       If you are using qmail as your MTA stick this file in the
#       /var/qmail/bin/abusecheck.pl and chmod +x it, and then for 
#       each .qmail file which needs SPF/DKIM processing just add
#
#       |/var/qmail/bin/abusecheck.pl 
#
#       to the top of the .qmail file -- normally before the Maildir
#       line.
#
#       By default, this rejects nothing, but adds the Received-SPF
#       or X-SPF-Guess header (for SPF), or Authentication-Results 
#       header (for DKIM). Rejecting is pretty useless as you know 
#       that the sender is forged!
#
#       Philip Gladstone - original SPF implementation
#       Zdravko Stoychev - added DKIM implementation
#
#       Rev:2

use strict;

use Sys::Hostname;
use Mail::SPF::Query;
use Mail::DKIM::Verifier;

my $sender = $ENV{SENDER};
my $src;
my $helo;
my $ourhost = hostname;

my $softfail = 0;       # 0 = Don't delay ERROR messages, 
                        # 1 = Delay SPF/DKIM msgs temporary

my $hardfail = 0;       # 0 = Don't reject DENY messages, 
                        # 1 = Reject SPF/DKIM FAIL messages

if (exists $ENV{RELAYCLIENT}) {
    # This is a well-known allowed relaying client.
    # Signify that delivery is to continue
    exit 0;
}

my @msg;
my $dkim = Mail::DKIM::Verifier->new();

while (<>) {
    push @msg, $_;
    if (/^Received-SPF/i || /^X-SPF-Guess/i || /^Authentication-Results/i) {
        # I tought to check that it is my record but since it appears 
        # before the first received, it probably is mine.
        # Signify that delivery is to continue
        exit 0;
    }

    # remove local line terminators
    chomp;
    s/\015$//;
    # use SMTP line terminators
    $dkim->PRINT("$_\015\012");

    if (!$sender && /^Return-path: <(.*)>/i) {
        $sender = $1;
    }
    if (!$src && /^Received:\s*from\s+(\S+)\s.*\(\[([.\d]+)\]\)/i) {
        my $from = $1;
        $src = $2;
        ($helo) = /\(HELO (\S+)\)/;
        $helo ||= $from;
        last;
    }
}

if (!$src || $src =~ /^10\.0\./ || $src =~ /^192\.168\.0\./) {
    # This is probably a local delivery or sending from Intranet
    # Signify that delivery is to continue
    exit 0;
}

# OK, perform SPF check...
my $spfheader = "Received-SPF"; # No spaces please!

my $query = Mail::SPF::Query->new(ip => $src, sender => $sender, helo => $helo, 
    guess => 1, trusted => 1, myhostname => $ourhost);

my ($spfresult, $spferror, $spfcomment) = $query->result();

if ($spfresult eq "fail") { 
    # domain is forged!!!
    if ($hardfail) {
        print "SPF failed: $spferror";
        # Fail the delivery
        exit 100; 
    }
} elsif ($spfresult eq "error") {
    # DNS temporary error
    if ($softfail) {
        print $spferror;
        # Delay the delivery
        exit 111; 
    }
}

$spfresult ||= 'unknown';

my $spfoutput = "$spfresult";
if ($spfcomment ne "") {
    $spfoutput = "$spfoutput ($spfcomment)";
}

# NEXT, perform DKIM check...
while (<>) {
    push @msg, $_;
    # remove local line terminators
    chomp;
    s/\015$//;
    # use SMTP line terminators
    $dkim->PRINT("$_\015\012");
}
$dkim->CLOSE;

# what is the result of the verify?
my $dkimheader  = "Authentication-Results"; # No spaces please!
my $dkimresult  = $dkim->result;
my $dkimpolicy  = $dkim->fetch_author_policy;
my $dkimcomment = $dkim->result_detail;

if (defined $dkimpolicy && $dkimpolicy->testing) {
    # this DKIM policy is in testing mode,
    # do not check for FAIL codes
} else {
  if ($dkimresult eq "fail") { 
    # domain is forged!!!
    if ($hardfail) {
        print "DKIM failed: $dkimcomment";
        # Fail the delivery
        exit 100; 
    }
  } elsif ($dkimresult eq "invalid") { 
    # SIG temporary error
    if ($softfail) {
        print "DKIM signature: $dkimcomment";
        # Delay the delivery
        exit 111; 
    }
  }
}

$dkimresult ||= 'unknown';

my $dkimoutput  = "$ourhost; mail.from=$sender; dkim=$dkimcomment";

if (defined $dkimpolicy) {
    my $dkimwhy = $dkimpolicy->apply($dkim);
    $dkimoutput = "$dkimoutput; dkim.policy=$dkimwhy";
    if ($dkimpolicy->testing) {
        $dkimoutput = "$dkimoutput (testing)";
    }
}

# Done
($ENV{QMAILSUSER}, $ENV{QMAILSHOST}) = $sender =~ /(.*)@([EMAIL PROTECTED])/;

# Instead using LOCAL you might want to use RECIPIENT bellow,
# depending of your qmail configuration
open (MAIL, "|-") || exec 'qmail-inject', $ENV{LOCAL};

# output checks results
print MAIL "$dkimheader: $dkimoutput\n";
print MAIL "$spfheader: $spfoutput\n";

# output the message
print MAIL @msg;
# just in case
#while (<>) { print MAIL; }
close(MAIL) || exit 0;

# Delivery OK, but don't continue in .qmail file
exit 99;

Attachment: smime.p7s
Description: S/MIME Cryptographic Signature

Reply via email to