qpsmtpd/plugins/virus/kavscanner
Matt Simerson dbaa9dbd6c POD corrections, additional tests, plugin consistency
on files in plugins dir:
  fixed a number of POD errors

  formatted some # comments into POD

  removed bare 1;  (these are plugins, not perl modules)
    most instances of this were copy/pasted from a previous plugin that had it

  removed instances of # vim ts=N ...
    they weren't consistent, many didn't match .perltidyrc

  on modules that failed perl -c tests, added 'use Qpsmtpd::Constants;'

Conflicts:

	plugins/async/check_earlytalker
	plugins/async/dns_whitelist_soft
	plugins/async/dnsbl
	plugins/async/queue/smtp-forward
	plugins/async/require_resolvable_fromhost
	plugins/async/rhsbl
	plugins/async/uribl
	plugins/auth/auth_checkpassword
	plugins/auth/auth_cvm_unix_local
	plugins/auth/auth_flat_file
	plugins/auth/auth_ldap_bind
	plugins/auth/auth_vpopmail
	plugins/auth/auth_vpopmail_sql
	plugins/auth/authdeny
	plugins/check_badmailfromto
	plugins/check_badrcptto_patterns
	plugins/check_bogus_bounce
	plugins/check_earlytalker
	plugins/check_norelay
	plugins/check_spamhelo
	plugins/connection_time
	plugins/dns_whitelist_soft
	plugins/dnsbl
	plugins/domainkeys
	plugins/greylisting
	plugins/hosts_allow
	plugins/http_config
	plugins/logging/adaptive
	plugins/logging/apache
	plugins/logging/connection_id
	plugins/logging/transaction_id
	plugins/logging/warn
	plugins/milter
	plugins/queue/exim-bsmtp
	plugins/queue/maildir
	plugins/queue/postfix-queue
	plugins/queue/smtp-forward
	plugins/quit_fortune
	plugins/random_error
	plugins/rcpt_map
	plugins/rcpt_regexp
	plugins/relay_only
	plugins/require_resolvable_fromhost
	plugins/rhsbl
	plugins/sender_permitted_from
	plugins/spamassassin
	plugins/tls
	plugins/tls_cert
	plugins/uribl
	plugins/virus/aveclient
	plugins/virus/bitdefender
	plugins/virus/clamav
	plugins/virus/clamdscan
	plugins/virus/hbedv
	plugins/virus/kavscanner
	plugins/virus/klez_filter
	plugins/virus/sophie
	plugins/virus/uvscan
2012-04-29 00:00:10 -07:00

177 lines
5.3 KiB
Perl

#!perl -Tw
# Kasperski-AV plugin.
=head1 NAME
kavscanner - plugin for qpsmtpd which calls the Kasperski anti virus scanner
=head1 DESCRIPTION
Check a mail with the B<kavscanner> and deny if it matches a configured virus
list.
=head1 VERSION
this is B<kavscanner> version 1.0
=head1 CONFIGURATION
Add (perl-)regexps to the F<kav_deny> configuration file, one per line for the
virii you want to block, e.g.:
I-Worm\.Sober\..*
I-Worm\.NetSky\..*
NOTE: untested and disabled currently, need volunteers :-)
If this list does not match the virus found in the mail, you may set
I<bcc_virusadmin viradm@your.company.com> in the plugin config to send a
B<Bcc:> to the given mail address, i.e. the line
kavscanner bcc_virusadmin viradm@your.company.com
in the F<config/plugin> file instead of just
kavscanner
Set the location of the binary with
kavscanner kavscanner_bin /path/to/kavscanner
(default: F</opt/AVP/kavscanner>), NOTE: this may be broken, you want to
set B<kavscanner_bin> explicitly ;-)
=head1 NOTES
This is a merge of the clam_av plugin for qpsmtpd and qmail-scanner-queue.pl
L<http://qmail-scanner.sourceforge.net/> with my own improvements ;-)
Only tested with kavscanner 4.0.x, and bcc_virusadmin untested, as we have no
use for it currently. I wait for an official change in Qpsmtpd::Transaction
(reset/set the RCPT TO list) to activate and test the currently disabled
B<to_virusadmin> option.
=cut
use File::Temp qw(tempfile);
use Mail::Address;
sub register {
my ($self, $qp, @args) = @_;
if (@args % 2) {
$self->log(LOGWARN, "kavscanner: Wrong number of arguments");
$self->{_kavscanner_bin} = "/opt/AVP/kavscanner";
} else {
my %args = @args;
foreach my $key (keys %args) {
my $arg = $key;
$key =~ s/^/_/;
$self->{$key} = $args{$arg};
}
# Untaint scanner location
if (exists $self->{_kavscanner_bin} &&
$self->{_kavscanner_bin} =~ /^(\/[\/\-\_\.a-z0-9A-Z]*)$/) {
$self->{_kavscanner_bin} = $1;
} else {
$self->log(LOGALERT, "FATAL ERROR: Unexpected characters in kavscanner argument");
exit 3;
}
}
}
sub hook_data_post {
my ($self, $transaction) = @_;
my ($temp_fh, $filename) = tempfile();
print $temp_fh $transaction->header->as_string;
print $temp_fh "\n";
$transaction->body_resetpos;
while (my $line = $transaction->body_getline) {
print $temp_fh $line;
}
seek($temp_fh, 0, 0);
# Now do the actual scanning!
my $cmd = $self->{_kavscanner_bin}." -Y -P -B -MP -MD -* $filename 2>&1";
$self->log(LOGNOTICE, "Running: $cmd");
my @output = `$cmd`;
chomp(@output);
my $result = ($? >> 8);
my $signal = ($? & 127);
unlink($filename);
close $temp_fh;
if ($signal) {
$self->log(LOGWARN, "kavscanner exited with signal: $signal");
return (DECLINED);
}
my $description = 'clean';
my @infected = ();
my @suspicious = ();
if ($result > 0) {
if ($result =~ /^(2|3|4|8)$/) {
foreach (@output) {
if (/^.* infected: (.*)$/) {
# This covers the specific
push @infected, $1;
} elsif (/^\s*.* suspicion: (.*)$/) {
# This covers the potential viruses
push @suspicious, $1;
}
}
$description = "infected by: ".join(", ",@infected)."; "
."suspicions: ".join(", ", @suspicious);
# else we may get a veeeery long X-Virus-Details: line or log entry
$description = substr($description,0,60);
$self->log(LOGWARN, "There be a virus! ($description)");
### Untested by now, need volunteers ;-)
#if ($self->qp->config("kav_deny")) {
# foreach my $d (keys %{$self->qp->config("kav_deny", "map")}) {
# foreach my $v (@infected) {
# return(DENY, "Virus found: $description")
# if ($v =~ /^$d$/i);
# }
# foreach my $s (@suspicious) {
# return(DENY, "Virus found: $description")
# if ($s =~ /^$d$/i);
# }
# }
#}
$transaction->header->add('X-Virus-Found', 'Yes');
$transaction->header->add('X-Virus-Details', $description);
### maybe the spamassassin plugin can skip this mail if a virus
### was found (and $transaction->notes('virus_flag') exists :))
### ...ok, works with our spamassassin plugin version
### -- hah
$transaction->notes('virus', $description);
$transaction->notes('virus_flag', 'Yes');
#### requires modification of Qpsmtpd/Transaction.pm:
# if ($self->{_to_virusadmin}) {
# my @addrs = ();
# foreach (@{$transaction->recipients}) {
# push @addr, $_->address;
# }
# $transaction->header->add('X-Virus-Orig-RcptTo', join(", ", @addrs));
# $transaction->set_recipients(@{ Mail::Address->parse($self->{_to_virusadmin}) });
# } elsif ($self->{_bcc_virusadmin}) {
if ($self->{_bcc_virusadmin}) {
foreach ( @{ Mail::Address->parse($self->{_bcc_virusadmin}) } ) {
$transaction->add_recipient($_);
}
}
} else {
$self->log(LOGEMERG, "corrupt or unknown Kaspersky scanner/resource problems - exit status $result");
}
}
$self->log(LOGINFO, "kavscanner results: $description");
$transaction->header->add('X-Virus-Checked', 'Checked by '.$self->qp->config("me"));
return (DECLINED);
}