/usr/share/qpsmtpd/plugins/virus/kavscanner is in qpsmtpd 0.84-11.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | #!/usr/bin/perl -w
# 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);
}
# vim: ts=2 sw=2 expandtab
|