in lib/Mail/SpamAssassin/Plugin/DKIM.pm [1022:1151]
sub _check_valid_signature {
my($self, $pms, $type, $signatures) = @_;
my $sig_type = lc $type;
$self->_get_authors($pms) if !$pms->{"dkim_author_addresses"};
my(@valid_signatures);
my $conf = $pms->{conf};
# DKIM signatures check
if ($pms->{"${sig_type}_signatures_ready"}) {
my $sig_result_supported;
# dkim_minimum_key_bits is evaluated for ARC signatures as well
my $minimum_key_bits = $conf->{dkim_minimum_key_bits};
foreach my $signature (@$signatures) {
# old versions of Mail::DKIM would give undef for an invalid signature
next if !defined $signature;
# test for empty selector (must not treat a selector "0" as missing!)
next if !defined $signature->selector || $signature->selector eq "";
my($info, $valid, $expired);
$valid = $signature->result eq 'pass';
$info = $valid ? 'VALID' : 'FAILED';
if ($valid && $signature->UNIVERSAL::can("check_expiration")) {
$expired = !$signature->check_expiration;
$info .= ' EXPIRED' if $expired;
}
my $key_size;
if ($valid && !$expired && $minimum_key_bits) {
$key_size = eval { my $pk = $signature->get_public_key;
$pk && $pk->cork && $pk->cork->size * 8 };
if ($key_size) {
$signature->{_spamassassin_key_size} = $key_size; # stash it for later
$info .= " WEAK($key_size)" if $key_size < $minimum_key_bits;
}
}
push(@valid_signatures, $signature) if $valid && !$expired;
# check if we have a potential Author Domain Signature, valid or not
my ($d) = (defined $signature->identity)? $signature->identity =~ /\@(\S+)/ : ($signature->domain);
if (!defined $d) {
# can be undefined on a broken signature with missing required tags
} else {
$d = lc $d;
if ($pms->{dkim_author_domains}->{$d}) { # SDID matches author domain
$pms->{"${sig_type}_has_any_author_sig"}->{$d} = 1;
if ($valid && !$expired &&
$key_size && $key_size >= $minimum_key_bits) {
$pms->{"${sig_type}_has_valid_author_sig"}->{$d} = 1;
} elsif ( $signature->result_detail
=~ /\b(?:timed out|SERVFAIL)\b/i) {
$pms->{"${sig_type}_author_sig_tempfailed"}->{$d} = 1;
}
}
}
if ($type eq 'DKIM') {
if (would_log("dbg","dkim")) {
dbg("dkim: %s %s, i=%s, d=%s, s=%s, a=%s, c=%s, %s, %s, %s",
$info,
$signature->isa('Mail::DKIM::DkSignature') ? 'DK' : 'DKIM',
map(!defined $_ ? '(undef)' : $_,
$signature->identity, $d, $signature->selector,
$signature->algorithm, scalar($signature->canonicalization),
$key_size ? "key_bits=$key_size" : "unknown key size",
$signature->result ),
defined $d && $pms->{dkim_author_domains}->{$d}
? 'matches author domain'
: 'does not match author domain',
);
}
} elsif ($type eq 'ARC') {
if (would_log("dbg","dkim")) {
dbg("dkim: %s %s, i=%s, d=%s, s=%s, a=%s, c=%s, %s, %s, %s",
$info,
$type,
map(!defined $_ ? '(undef)' : $_,
$signature->identity, $d, $signature->selector,
$signature->algorithm, scalar($signature->canonicalization),
$key_size ? "key_bits=$key_size" : "unknown key size",
$signature->result ),
defined $d && $pms->{dkim_author_domains}->{$d}
? 'matches author domain'
: 'does not match author domain',
);
}
}
}
if (@valid_signatures) {
if ($type eq 'DKIM') {
$pms->{dkim_signed} = 1;
$pms->{dkim_valid} = 1;
# supply values for both tags
my(%seen1, %seen2, %seen3, @identity_list, @domain_list, @selector_list);
@identity_list = grep(defined $_ && $_ ne '' && !$seen1{$_}++,
map($_->identity, @valid_signatures));
@domain_list = grep(defined $_ && $_ ne '' && !$seen2{$_}++,
map($_->domain, @valid_signatures));
@selector_list = grep(defined $_ && $_ ne '' && !$seen3{$_}++,
map($_->selector, @valid_signatures));
$pms->set_tag('DKIMIDENTITY',
@identity_list == 1 ? $identity_list[0] : \@identity_list);
$pms->set_tag('DKIMDOMAIN',
@domain_list == 1 ? $domain_list[0] : \@domain_list);
$pms->set_tag('DKIMSELECTOR',
@selector_list == 1 ? $selector_list[0] : \@selector_list);
} elsif ($type eq 'ARC') {
$pms->{arc_signed} = 1;
$pms->{arc_valid} = 1;
}
# let the result stand out more clearly in the log, use uppercase
my $sig = $valid_signatures[0];
my $sig_res = $sig->result_detail;
dbg("dkim: $type signature verification result: %s", uc($sig_res));
} elsif (@$signatures) {
if ($type eq 'DKIM') {
$pms->{dkim_signed} = 1;
} elsif ($type eq 'ARC') {
$pms->{arc_signed} = 1;
}
my $sig = @$signatures[0];
my $sig_res = $sig->result_detail;
dbg("dkim: $type signature verification result: %s", uc($sig_res));
} else {
dbg("dkim: $type signature verification result: none");
}
}
}