* plugins/virus/clamav
Scan temporary file directly now that the spooled file includes the entire message * plugins/virus/bitdefender - John Peacock plugins/virus/hbedv - Hanno Hecker New AV plugins git-svn-id: https://svn.perl.org/qpsmtpd/trunk@377 958fd67b-6ff1-0310-b445-bb7760255be9
This commit is contained in:
parent
ec7aff1415
commit
889845af24
134
plugins/virus/bitdefender
Normal file
134
plugins/virus/bitdefender
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
#!/usr/bin/perl -Tw
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
bitdefender -- BitDefender Linux Edition antivirus plugin for qpsmtpd
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
This plugin scans incoming mail with the BitDefender Linux Edition scanner,
|
||||||
|
and can at your option reject or flag infected messages.
|
||||||
|
|
||||||
|
=head1 CONFIGURATION
|
||||||
|
|
||||||
|
=over 4
|
||||||
|
|
||||||
|
=item B<bitdefender_location>
|
||||||
|
|
||||||
|
Full path to the BitDefender binary and all signature files; defaults to
|
||||||
|
/opt/bdc/bdc.
|
||||||
|
|
||||||
|
=item B<deny_viruses>
|
||||||
|
|
||||||
|
Whether the scanner will automatically delete messages which have viruses.
|
||||||
|
Takes either 'yes' or 'no' (defaults to 'yes').
|
||||||
|
|
||||||
|
=item B<max_size>
|
||||||
|
|
||||||
|
Maximum size in kilobytes for messages which will be scanned; defaults to 128k;
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head1 DEPENDENCIES
|
||||||
|
|
||||||
|
=over 4
|
||||||
|
|
||||||
|
=item B<BitDefender>
|
||||||
|
|
||||||
|
The BitDefender Linux Edition is available to use, free of charge, from
|
||||||
|
this link:
|
||||||
|
|
||||||
|
<http://www.bitdefender.com/bd/site/products.php?p_id=16>
|
||||||
|
|
||||||
|
Please read the documentation for configuring automatic updates of the
|
||||||
|
virus profiles.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=head1 AUTHOR
|
||||||
|
|
||||||
|
John Peacock <jpeacock@cpan.org>
|
||||||
|
|
||||||
|
=head1 COPYRIGHT AND LICENSE
|
||||||
|
|
||||||
|
Copyright (c) 2004 John Peacock
|
||||||
|
|
||||||
|
Based lightly on the clamav plugin
|
||||||
|
|
||||||
|
This plugin is licensed under the same terms as the qpsmtpd package itself.
|
||||||
|
Please see the LICENSE file included with qpsmtpd for details.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
use File::Path;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
sub register {
|
||||||
|
my ( $self, $qp, @args ) = @_;
|
||||||
|
$self->register_hook( "data_post", "bdc_scan" );
|
||||||
|
|
||||||
|
while (@args) {
|
||||||
|
$self->{"_bitd"}->{ pop @args } = pop @args;
|
||||||
|
}
|
||||||
|
$self->{"_bitd"}->{"bitdefender_location"} ||= "/opt/bdc/bdc";
|
||||||
|
$self->{"_bitd"}->{"deny_viruses"} ||= "yes";
|
||||||
|
$self->{"_bitd"}->{"max_size"} ||= 128;
|
||||||
|
$self->{"_bitd"}->{"max_size"} *= 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub bdc_scan {
|
||||||
|
my ( $self, $transaction ) = @_;
|
||||||
|
|
||||||
|
if ( $transaction->body_size > $self->{"_bitd"}->{"max_size"} ) {
|
||||||
|
$self->log( LOGWARN,
|
||||||
|
'Mail too large to scan ('
|
||||||
|
. $transaction->body_size . " vs "
|
||||||
|
. $self->{"_bitd"}->{"max_size"}
|
||||||
|
. ")" );
|
||||||
|
return (DECLINED);
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ignore non-multipart emails
|
||||||
|
my $content_type = $transaction->header->get('Content-Type');
|
||||||
|
$content_type =~ s/\s/ /g if defined $content_type;
|
||||||
|
unless ( $content_type
|
||||||
|
&& $content_type =~ m!\bmultipart/.*\bboundary="?([^"]+)!i )
|
||||||
|
{
|
||||||
|
$self->log( LOGERROR, "non-multipart mail - skipping" );
|
||||||
|
return DECLINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
my $filename = $transaction->body_filename;
|
||||||
|
unless (defined $filename) {
|
||||||
|
$self->log(LOGERROR, "didn't get a filename");
|
||||||
|
return DECLINED;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Now do the actual scanning!
|
||||||
|
open my $bdc, "-|",
|
||||||
|
$self->{"_bitd"}->{"bitdefender_location"}
|
||||||
|
. " --mail --all --arc $filename";
|
||||||
|
|
||||||
|
my $output;
|
||||||
|
while (<$bdc>) {
|
||||||
|
if (/infected: (.+)$/) {
|
||||||
|
$output = $1;
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close $bdc;
|
||||||
|
|
||||||
|
if ($output) {
|
||||||
|
$self->log( LOGINFO, "Virus(es) found: $output" );
|
||||||
|
if ( $self->{"_bitd"}->{"deny_viruses"} eq "yes" ) {
|
||||||
|
return ( DENY, "Virus Found: $output" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (DECLINED);
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
||||||
|
|
@ -101,8 +101,6 @@ Please see the LICENSE file included with qpsmtpd for details.
|
|||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
use File::Temp qw(tempfile);
|
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
use warnings;
|
use warnings;
|
||||||
|
|
||||||
@ -156,28 +154,18 @@ sub register {
|
|||||||
|
|
||||||
sub clam_scan {
|
sub clam_scan {
|
||||||
my ($self, $transaction) = @_;
|
my ($self, $transaction) = @_;
|
||||||
|
|
||||||
if ($transaction->body_size > $self->{_max_size}) {
|
if ($transaction->body_size > $self->{_max_size}) {
|
||||||
$self->log(LOGWARN, 'Mail too large to scan ('.
|
$self->log(LOGWARN, 'Mail too large to scan ('.
|
||||||
$transaction->body_size . " vs $self->{_max_size})" );
|
$transaction->body_size . " vs $self->{_max_size})" );
|
||||||
return (DECLINED);
|
return (DECLINED);
|
||||||
}
|
}
|
||||||
|
|
||||||
my ($temp_fh, $filename) = tempfile("qpsmtpd.clamav.$$.XXXXXX",
|
my $filename = $transaction->body_filename;
|
||||||
DIR => $self->{_spool_dir});
|
unless (defined $filename) {
|
||||||
unless ($temp_fh) {
|
$self->log(LOGERROR, "didn't get a filename");
|
||||||
$self->logerror("Couldn't open tempfile in $self->{_spool_dir}: $!");
|
|
||||||
return DECLINED;
|
return DECLINED;
|
||||||
}
|
}
|
||||||
print $temp_fh "From ",
|
|
||||||
$transaction->sender->format, " " , scalar gmtime, "\n";
|
|
||||||
print $temp_fh $transaction->header->as_string, "\n";
|
|
||||||
$transaction->body_resetpos;
|
|
||||||
while (my $line = $transaction->body_getline) {
|
|
||||||
print $temp_fh $line;
|
|
||||||
}
|
|
||||||
seek($temp_fh, 0, 0);
|
|
||||||
|
|
||||||
my $mode = (stat($self->{_spool_dir}))[2];
|
my $mode = (stat($self->{_spool_dir}))[2];
|
||||||
if ( $mode & 07077 ) { # must be sharing spool directory with external app
|
if ( $mode & 07077 ) { # must be sharing spool directory with external app
|
||||||
$self->log(LOGWARN,
|
$self->log(LOGWARN,
|
||||||
@ -195,7 +183,6 @@ sub clam_scan {
|
|||||||
my $result = ($? >> 8);
|
my $result = ($? >> 8);
|
||||||
my $signal = ($? & 127);
|
my $signal = ($? & 127);
|
||||||
|
|
||||||
unlink($filename);
|
|
||||||
chomp($output);
|
chomp($output);
|
||||||
|
|
||||||
$output =~ s/^.* (.*) FOUND$/$1 /mg;
|
$output =~ s/^.* (.*) FOUND$/$1 /mg;
|
||||||
|
160
plugins/virus/hbedv
Normal file
160
plugins/virus/hbedv
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
#!/usr/bin/perl -w
|
||||||
|
# H+B EDV-AV plugin.
|
||||||
|
#
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
hbedv - plugin for qpsmtpd which calls the H+BEDV anti virus scanner
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
The B<hbedv> plugin checks a mail for viruses with the H+BEDV anti virus
|
||||||
|
scanner (see L<http://www.antivir.de/> for info). It can deny mails if a
|
||||||
|
virus was found with a configurable deny list.
|
||||||
|
|
||||||
|
=head1 VERSION
|
||||||
|
|
||||||
|
this is B<hbedv> version 1.1
|
||||||
|
|
||||||
|
=head1 CONFIGURATION
|
||||||
|
|
||||||
|
Add (perl-)regexps to the F<hbedv_deny> configuration file, one per line for the
|
||||||
|
virii you want to block, e.g.:
|
||||||
|
|
||||||
|
Worm\/Sober\..*
|
||||||
|
Worm\/NetSky\..*
|
||||||
|
|
||||||
|
or just
|
||||||
|
|
||||||
|
.*
|
||||||
|
|
||||||
|
to block any virus ;)
|
||||||
|
|
||||||
|
Set the location of the binary with
|
||||||
|
|
||||||
|
hbedv hbedvscanner /path/to/antivir
|
||||||
|
|
||||||
|
in the plugin config if qpsmtpd, the location defaults to I</usr/bin/antivir>.
|
||||||
|
|
||||||
|
=head1 NOTES
|
||||||
|
|
||||||
|
If the hbedv_deny config file is empty or could not be found, any virus
|
||||||
|
will be blocked.
|
||||||
|
|
||||||
|
This plugin started life as a copy of the B<clamav> plugin.
|
||||||
|
|
||||||
|
=head1 LICENCE
|
||||||
|
|
||||||
|
Written by Hanno Hecker E<lt>hah@uu-x.deE<gt>.
|
||||||
|
|
||||||
|
The B<hbedv> plugin is published under the same licence as qpsmtpd itself.
|
||||||
|
|
||||||
|
=cut
|
||||||
|
|
||||||
|
sub register {
|
||||||
|
my ($self, $qp, @args) = @_;
|
||||||
|
$self->register_hook("data_post", "hbedv_scan");
|
||||||
|
|
||||||
|
if (@args % 2) {
|
||||||
|
$self->log(LOGERROR, "FATAL ERROR: odd number of arguments");
|
||||||
|
exit 3;
|
||||||
|
}
|
||||||
|
my %args = @args;
|
||||||
|
if (!exists $args{hbedvscanner}) {
|
||||||
|
$self->{_hbedvscan_loc} = "/usr/bin/antivir";
|
||||||
|
} else {
|
||||||
|
if ($args{hbedvscanner} =~ /^(\/[\/\-\_\.a-z0-9A-Z]*)$/) {
|
||||||
|
$self->{_hbedvscan_loc} = $1;
|
||||||
|
} else {
|
||||||
|
$self->log(LOGERROR, "FATAL ERROR: Unexpected characters in hbedvscanner argument");
|
||||||
|
exit 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub hbedv_scan {
|
||||||
|
my ($self, $transaction) = @_;
|
||||||
|
|
||||||
|
my $filename = $transaction->body_filename;
|
||||||
|
unless (defined $filename) {
|
||||||
|
$self->log(LOGWARN, "didn't get a file name");
|
||||||
|
return (DECLINED);
|
||||||
|
}
|
||||||
|
|
||||||
|
# Now do the actual scanning!
|
||||||
|
my $cmd = $self->{_hbedvscan_loc}." --archive-max-recursion=50 --alltypes -z -noboot -nombr -rs $filename 2>&1";
|
||||||
|
$self->log(LOGDEBUG, "Running: $cmd");
|
||||||
|
my @output = `$cmd`;
|
||||||
|
|
||||||
|
my $result = ($? >> 8);
|
||||||
|
my $signal = ($? & 127);
|
||||||
|
|
||||||
|
chomp(@output);
|
||||||
|
my @virii = ();
|
||||||
|
foreach my $line (@output) {
|
||||||
|
next unless $line =~ /^ALERT: \[([^\]]+)\s+(\w+)?\]/; # $2 =~ /^(virus|worm)$/;
|
||||||
|
push @virii, $1;
|
||||||
|
}
|
||||||
|
@virii = unique(@virii);
|
||||||
|
|
||||||
|
$self->log(LOGDEBUG, "results: ".join("//",@output));
|
||||||
|
|
||||||
|
if ($signal) {
|
||||||
|
$self->log(LOGWARN, "scanner exited with signal: $signal");
|
||||||
|
return (DECLINED);
|
||||||
|
}
|
||||||
|
my $output = join(", ", @virii);
|
||||||
|
$output = substr($output, 0, 60);
|
||||||
|
if ($result == 1 || $result == 3) {
|
||||||
|
$self->log(LOGWARN, "Virus(es) found: $output");
|
||||||
|
# return (DENY, "Virus Found: $output");
|
||||||
|
# $transaction->header->add('X-Virus-Found', 'Yes', 0);
|
||||||
|
# $transaction->header->add('X-Virus-Details', $output, 0);
|
||||||
|
$transaction->header->add('X-H+BEDV-Virus-Found', 'Yes', 0);
|
||||||
|
$transaction->header->add('X-H+BEDV-Virus-Details', $output, 0);
|
||||||
|
}
|
||||||
|
elsif ($result == 200) {
|
||||||
|
$self->log(LOGWARN, "Program aborted, not enough memory available");
|
||||||
|
}
|
||||||
|
elsif ($result == 211) {
|
||||||
|
$self->log(LOGWARN, "Programm aborted, because the self check failed");
|
||||||
|
}
|
||||||
|
elsif ($result == 214) {
|
||||||
|
$self->log(LOGWARN, "License key not found");
|
||||||
|
}
|
||||||
|
elsif ($result) {
|
||||||
|
$self->log(LOGWARN, "Error: $result, look for exit codes in the output of '"
|
||||||
|
.$self->{_hbedvscan_loc}." --help' for more info\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
# $transaction->header->add('X-Virus-Checked', 'Checked', 0);
|
||||||
|
$transaction->header->add('X-H+BEDV-Virus-Checked', 'Checked', 0);
|
||||||
|
return (DECLINED) unless $result;
|
||||||
|
|
||||||
|
if (@virii) {
|
||||||
|
return(DENY, "Virus found: $output")
|
||||||
|
unless $self->qp->config("hbedv_deny");
|
||||||
|
foreach my $d ($self->qp->config("hbedv_deny")) {
|
||||||
|
foreach my $v (@virii) {
|
||||||
|
if ($v =~ /^$d$/i) {
|
||||||
|
$self->log(LOGWARN, "Denying mail with virus '$v'");
|
||||||
|
return(DENY, "Virus found: $output");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (DECLINED);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub unique {
|
||||||
|
## This is the short version, I haven't tried if any warnings
|
||||||
|
## are generated by perl if you use just this... if you need
|
||||||
|
## every cpu cycle, try this:
|
||||||
|
## my %h;foreach (@_) { ++$h{$_}; }; return keys(%h);
|
||||||
|
my @list = @_;
|
||||||
|
my %hash;
|
||||||
|
foreach my $item (@list) {
|
||||||
|
exists $hash{$item} || ($hash{$item} = 1);
|
||||||
|
}
|
||||||
|
return keys(%hash)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user