sub _wlcheck_list()

in lib/Mail/SpamAssassin/Plugin/DKIM.pm [1409:1510]


sub _wlcheck_list {
  my ($self, $pms, $acceptable_sdid_tuples_ref) = @_;

  my %any_match_by_wl;
  my $any_match_at_all = 0;
  my $verifier = $pms->{dkim_verifier};
  my $minimum_key_bits = $pms->{conf}->{dkim_minimum_key_bits};

  # walk through all signatures present in a message
  foreach my $signature (@{$pms->{dkim_signatures}}) {
    # old versions of Mail::DKIM would give undef for an invalid signature
    next if !defined $signature;
    my $sig_result_supported = $signature->UNIVERSAL::can("result_detail");
    # test for empty selector (must not treat a selector "0" as missing!)
    next if !defined $signature->selector || $signature->selector eq "";

    my($info, $valid, $expired, $key_size_weak);
    $valid =
      ($sig_result_supported ? $signature : $verifier)->result eq 'pass';
    $info = $valid ? 'VALID' : 'FAILED';
    if ($valid && $signature->UNIVERSAL::can("check_expiration")) {
      $expired = !$signature->check_expiration;
      $info .= ' EXPIRED'  if $expired;
    }
    if ($valid && !$expired && $minimum_key_bits) {
      my $key_size = $signature->{_spamassassin_key_size};
      if ($key_size && $key_size < $minimum_key_bits) {
        $info .= " WEAK($key_size)"; $key_size_weak = 1;
      }
    }

    my ($sdid) = (defined $signature->identity)? $signature->identity =~ /\@(\S+)/ : ($signature->domain);
    $sdid = lc $sdid  if defined $sdid;

    my %tried_authors;
    foreach my $entry (@$acceptable_sdid_tuples_ref) {
      my($author, $acceptable_sdid, $wl, $welcome_addr) = @$entry;
      # $welcome_addr and $wl are here for logging purposes only, already checked.
      # The $acceptable_sdid is a verifier-acceptable signing domain
      # identifier (to be matched against a 'd' tag in signatures).
      # When $acceptable_sdid is undef or an empty string it implies
      # a check for Author Domain Signature.

      local $1;
      my $author_domain = $author !~ /\@([^\@]+)\z/s ? '' : lc $1;
      $tried_authors{$author} = 1;  # for logging purposes

      my $matches = 0;
      if (!defined $sdid) {
        # don't bother, invalid signature with a missing 'd' or 'i' tag

      } elsif (!defined $acceptable_sdid || $acceptable_sdid eq '') {
        # An "Author Domain Signature" (sometimes called a first-party
        # signature) is a Valid Signature in which the domain name of the
        # DKIM signing entity, i.e., the d= tag in the DKIM-Signature header
        # field, is the same as the domain name in the Author Address.
        # Following [RFC5321], domain name comparisons are case insensitive.

        # checking for Author Domain Signature
        $matches = 1  if $sdid eq $author_domain;

      } else {  # checking for verifier-acceptable signature
        # The second argument to a 'welcomelist_from_dkim' option is now (since
        # version 3.3.0) supposed to be a signing domain (SDID), no longer an
        # identity (AUID). Nevertheless, be prepared to accept the full e-mail
        # address there for compatibility, and just ignore its local-part.

        $acceptable_sdid = $1  if $acceptable_sdid =~ /\@([^\@]*)\z/s;
        if ($acceptable_sdid =~ s/^\*?\.//s) {
          $matches = 1  if $sdid =~ /\.\Q$acceptable_sdid\E\z/si;
        } else {
          $matches = 1  if $sdid eq lc $acceptable_sdid;
        }
      }
      if ($matches) {
        if (would_log("dbg","dkim")) {
          if ($sdid eq $author_domain) {
            dbg("dkim: %s author domain signature by %s, MATCHES %s %s",
                $info, $sdid, $wl, $welcome_addr);
          } else {
            dbg("dkim: %s third-party signature by %s, author domain %s, ".
                "MATCHES %s %s", $info, $sdid, $author_domain, $wl, $welcome_addr);
          }
        }
        # a defined value indicates at least a match, not necessarily valid
        # (this complication servers to preserve logging compatibility)
        $any_match_by_wl{$wl} = ''  if !exists $any_match_by_wl{$wl};
      }
      # only valid signature can cause welcomelisting
      $matches = 0  if !$valid || $expired || $key_size_weak;

      if ($matches) {
        $any_match_at_all = 1;
        $any_match_by_wl{$wl} = $sdid;  # value used for debug logging
      }
    }
    dbg("dkim: %s signature by %s, author %s, no valid matches",
        $info,  defined $sdid ? $sdid : '(undef)',
        join(", ", keys %tried_authors))  if !$any_match_at_all;
  }
  return ($any_match_at_all, \%any_match_by_wl);
}