proxmox-spamassassin/upstream/lib/Mail/SpamAssassin/Plugin/AccessDB.pm
Stoiko Ivanov ae52237fd8 update SpamAssassin to 4.0.0
generated by make update-upstream

Signed-off-by: Stoiko Ivanov <s.ivanov@proxmox.com>
2023-03-13 21:13:17 +01:00

174 lines
4.6 KiB
Perl

# <@LICENSE>
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to you 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.
# </@LICENSE>
=head1 NAME
Mail::SpamAssassin::Plugin::AccessDB - check message against Access Database
=head1 SYNOPSIS
loadplugin Mail::SpamAssassin::Plugin::AccessDB
header ACCESSDB eval:check_access_database('/etc/mail/access.db')
describe ACCESSDB Message would have been caught by accessdb
tflags ACCESSDB userconf
score ACCESSDB 2
=head1 DESCRIPTION
Many MTAs support access databases, such as Sendmail, Postfix, etc.
This plugin does similar checks to see whether a message would have
been flagged.
The rule returns false if an entry isn't found, or the entry has a RHS of
I<OK> or I<SKIP>.
The rule returns true if an entry exists and has a RHS of I<REJECT>, I<ERROR>,
or I<DISCARD>.
Note: only the first word (split on non-word characters) of the RHS
is checked, so C<error:5.7.1:...> means C<ERROR>.
B<AccessDB Pointers:>
http://www.faqs.org/docs/securing/chap22sec178.html
http://www.postfix.org/access.5.html
=cut
package Mail::SpamAssassin::Plugin::AccessDB;
use Mail::SpamAssassin::Plugin;
use Mail::SpamAssassin::Logger;
use Fcntl;
use strict;
use warnings;
# use bytes;
use re 'taint';
our @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);
$self->register_eval_rule("check_access_database", $Mail::SpamAssassin::Conf::TYPE_HEAD_EVALS);
return $self;
}
sub check_access_database {
my ($self, $pms, $path) = @_;
if (!HAS_DB_FILE) {
return 0;
}
my %access;
my %ok = map { $_ => 1 } qw/ OK SKIP /;
my %bad = map { $_ => 1 } qw/ REJECT ERROR DISCARD /;
$path = $self->{main}->sed_path ($path);
dbg("accessdb: tie-ing to DB file R/O in $path");
if (tie %access,"DB_File",$path, O_RDONLY) {
my @lookfor;
# Look for "From:" versions as well!
foreach my $from ($pms->all_from_addrs()) {
# $user."\@"
# rotate through $domain and check
my ($user,$domain) = split(/\@/, $from,2);
push(@lookfor, "From:$from",$from);
if ($user) {
push(@lookfor, "From:$user\@", "$user\@");
}
if ($domain) {
while ($domain =~ /\./) {
push(@lookfor, "From:$domain", $domain);
$domain =~ s/^[^.]*\.//;
}
push(@lookfor, "From:$domain", $domain);
}
}
# we can only match this if we have at least 1 untrusted header
if ($pms->{num_relays_untrusted} > 0) {
my $lastunt = $pms->{relays_untrusted}->[0];
# If there was a reverse lookup, use it in a lookup
if (! $lastunt->{no_reverse_dns}) {
my $rdns = $lastunt->{lc_rdns};
while($rdns =~ /\./) {
push(@lookfor, "From:$rdns", $rdns);
$rdns =~ s/^[^.]*\.//;
}
push(@lookfor, "From:$rdns", $rdns);
}
# do both IP and net (rotate over IP)
my ($ip) = $lastunt->{ip};
$ip =~ tr/0-9.//cd;
while($ip =~ /\./) {
push(@lookfor, "From:$ip", $ip);
$ip =~ s/\.[^.]*$//;
}
push(@lookfor, "From:$ip", $ip);
}
my $retval = 0;
my %cache;
foreach (@lookfor) {
next if ($cache{$_}++);
dbg("accessdb: looking for $_");
# Some systems put a null at the end of the key, most don't...
my $result = $access{$_} || $access{"$_\000"} || next;
my ($type) = split(/\W/,$result);
$type = uc $type;
if (exists $ok{$type}) {
dbg("accessdb: hit OK: $type, $_");
$retval = 0;
last;
}
if (exists $bad{$type} || $type =~ /^\d+$/) {
$retval = 1;
dbg("accessdb: hit not-OK: $type, $_");
}
}
dbg("accessdb: untie-ing DB file $path");
untie %access;
return $retval;
}
else {
dbg("accessdb: cannot open accessdb $path R/O: $!");
}
return 0;
}
1;