Sophie

Sophie

distrib > Mageia > 3 > i586 > media > core-release-src > by-pkgid > c0bcc14ecce09b70a3db0e233cd31092 > files > 2

perl-Mail-SpamAssassin-Plugin-POPAuth-0-8.mga3.src.rpm

=head1 NAME

Mail::SpamAssassin::Plugin::POPAuth

=head1 SYNOPSIS

 loadplugin Mail::SpamAssassin::Plugin::POPAuth [/path/to/POPAuth.pm]

 popauth_hash_file /path/to/access.db

=head1 DESCRIPTION

Utilizes an access.db style hash file to extend the SpamAssassin
trusted_networks to 'POPAuth' or 'POP-before-SMTP' hosts by
dynamically adding and removing the hosts or networks found in the
specified database to SpamAssassin's trusted_networks configuration.

LHS hosts or networks in the database may be specified in either classful
or classless notations.

Each entry found in the database with a RHS of I<OK> or I<RELAY> is
added to the trusted_networks.  Any other entry is ignored.  Only the first
entry for a host or network is used.  Subsequent entries are ignored.

Note: only the first word (split on non-word characters) of the RHS is
checked.

B<AccessDB Pointers:>

  http://www.faqs.org/docs/securing/chap22sec178.html
  http://www.postfix.org/access.5.html

=head1 AUTHOR

Daryl C. W. O'Shea, DOS Technologies <spamassassin@dostech.ca>

=head1 COPYRIGHT

Copyright (c) 2005 Daryl C. W. O'Shea, DOS Technologies. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

=cut

package Mail::SpamAssassin::Plugin::POPAuth;

use Mail::SpamAssassin::Plugin;
use Mail::SpamAssassin::Logger;
use Fcntl;
use strict;
use warnings;
use bytes;

use vars qw(@ISA);
@ISA = qw(Mail::SpamAssassin::Plugin);

use constant HAS_DB_FILE => eval { require DB_File; };


sub new {
  my $class = shift;
  my $mailsaobject = shift;

  $class = ref($class) || $class;
  my $self = $class->SUPER::new($mailsaobject);
  bless ($self, $class);

  # Ignore the first message except when using 'spamassassin' to avoid copy_config issues.
  $self->{linted} = ($0 =~ /\/spamassassin$/ ? 1 : 0);

  $self->{failed} = 0;

  $self->set_config($mailsaobject->{conf});

  return $self;
}


sub set_config {
  my($self, $conf) = @_;
  my @cmds = ();

=item popauth_hash_file /path/to/access.db	default: /etc/mail/access.db

Path to your POPAuth hash database.

B<Note:> This file MUST be readable by any user that a SpamAssassin child may
potentially run as (any user specified in a -u parameter that would be getting
mail from a POPAuth host). In most cases the file will need to be globally
readable.

=cut

  push (@cmds, {
    setting => 'popauth_hash_file',
    is_admin => 1,
    default => "/etc/mail/access.db",
    type => $Mail::SpamAssassin::Conf::CONF_TYPE_STRING,
    code => sub {
      my ($self, $key, $value, $line) = @_;
      unless (defined $value && $value !~ /^$/) {
        return $Mail::SpamAssassin::Conf::MISSING_REQUIRED_VALUE;
      }
      if (-d $value) {
        return $Mail::SpamAssassin::Conf::INVALID_VALUE;
      }
      $self->{popauth_hash_file} = $value;
      dbg("config: setting POPAuth hash file: $value");
    }
  });

  $conf->{parser}->register_commands(\@cmds);
}


sub check_start {
  my ($self, $opts) = @_;

  return unless (HAS_DB_FILE);

  # prevent M::SA->copy_config from copying dynamically added trusted_networks
  unless ($self->{linted}) {
    $self->{linted} = 1;
    return;
  }

  dbg_trusted($self, 0);

  my ($num_trusted, %trusted) = $self->_read_hash_db($self);

  unless (defined $num_trusted) {
    $opts->{permsgstatus}->{main}->{conf}->{headers_spam}->{POPAuth} = "Database Failure";
    $opts->{permsgstatus}->{main}->{conf}->{headers_ham}->{POPAuth} = "Database Failure";
    return;
  }

  dbg("config: $num_trusted useful lines found in POPAuth database");

  my $added = 0;
  while (my($quad, $bits) = each(%trusted)) {
    if ($self->{main}->{conf}->{trusted_networks}->contains_ip($quad)) {
      dbg("config: ip: $quad already exists in trusted_networks");
    } else {
      dbg("config: ip: $quad not in trusted_networks");

      if ($self->{main}->{conf}->{trusted_networks}->add_cidr("$quad/$bits")) {
	dbg("config: added $quad/$bits to trusted_networks");
	$added++;
      } else {
	dbg("config: failed to add $quad/$bits to trusted_networks");
      }

      my $aton = Mail::SpamAssassin::Util::my_inet_aton($quad);
      my $mask = 0xFFffFFff ^ ((2 ** (32-$bits)) - 1);

      $self->{added_aton}->{$aton} = $mask;
    }
  }

  dbg("config: added $added POPAuth entries to trusted_networks");
  dbg_trusted($self, 0);

  return;
}


sub parsed_metadata {
  my ($self, $opts) = @_;

  return unless (HAS_DB_FILE && $self->{linted} && !$self->{failed});

  # We have to check every relay as the last one might not be the one that
  # caused all of them to be trusted.  Be aware of netmasks.
  foreach my $relay (@{$opts->{permsgstatus}->{relays_trusted}}) {
    my $relay_aton = Mail::SpamAssassin::Util::my_inet_aton($relay->{ip});
    while (my($aton, $mask) = each (%{$self->{added_aton}})) {
      if (($relay_aton & $mask) == $aton) {
	$opts->{permsgstatus}->{main}->{conf}->{headers_ham}->{POPAuth} = 'Yes';
	$opts->{permsgstatus}->{main}->{conf}->{headers_spam}->{POPAuth} = 'Yes';
	return;
      }
    }
  }

  $opts->{permsgstatus}->{main}->{conf}->{headers_ham}->{POPAuth} = 'No';
  $opts->{permsgstatus}->{main}->{conf}->{headers_spam}->{POPAuth} = 'No';
  return;
}


sub _read_hash_db {
  my ($self) = @_;

  my %access;
  my %trusted;
  my %ok = map { $_ => 1 } qw/ OK RELAY /;

  my $path = $self->{main}->{conf}->{popauth_hash_file};

  $path = $self->{main}->sed_path ($path);
  dbg("config: tie-ing to DB file R/O in $path");

  if (tie %access,"DB_File",$path, O_RDONLY) {
    my %cache = ();
    while(my($key, $value) = each(%access)) {
      # We only care about the first entry for any given host/network.
      next if ($cache{$key}++);

      # Some systems put a null at the end of the key, most don't.
      $key =~ s/\000//;

      # We only care about the first word of the RHS value.
      my ($type) = split(/\W/,$value);
      $type = uc $type;

      if (exists $ok{$type}) {
	# If it's in CIDR notation get the number of bits.
	my $bits;
	if ($key =~ s/^\s*([\d.]+)\/(\d{1,2})\s*$/$1/) { $bits = $2; }

	# We'll probably only ever get /32 addresses, but what the heck.
	if    ($key =~ /^\s*(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\s*$/) { $trusted{"$1.$2.$3.$4"} = ($bits || 32); }
	elsif ($key =~ /^\s*(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.?\s*$/)	     { $trusted{"$1.$2.$3.0"} = ($bits || 24); }
	elsif ($key =~ /^\s*(\d{1,3})\.(\d{1,3})\.?\s*$/)		     { $trusted{"$1.$2.0.0"} = ($bits || 16); }
	elsif ($key =~ /^\s*(\d{1,3})\.?\s*$/)		   		     { $trusted{"$1.0.0.0"} = ($bits || 8); }
	else  { dbg("config: could not parse POPAuth database pair: '$key' => '$value', skipping"); }
      }
    }

    dbg("config: untie-ing DB file $path");
    untie %access;

  } else {
    dbg("config: failed to tie DB");
    $self->{failed} = 1;
    return (undef, %trusted);
  }

  my $size = keys %trusted;
  return ($size, %trusted);
}


sub dbg_trusted {
  my ($self, $verbose) = @_;

  dbg("config: current number of trusted_networks: " . $self->{main}->{conf}->{trusted_networks}->get_num_nets);
  if ($verbose) {
    foreach my $net (@{$self->{main}->{conf}->{trusted_networks}->{nets}}) {
      dbg("config: trusted ip: $net->{ip} mask: $net->{mask}");
    }
  }
}


1;