* 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
|
||||
|
||||
use File::Temp qw(tempfile);
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
@ -156,28 +154,18 @@ sub register {
|
||||
|
||||
sub clam_scan {
|
||||
my ($self, $transaction) = @_;
|
||||
|
||||
|
||||
if ($transaction->body_size > $self->{_max_size}) {
|
||||
$self->log(LOGWARN, 'Mail too large to scan ('.
|
||||
$transaction->body_size . " vs $self->{_max_size})" );
|
||||
return (DECLINED);
|
||||
}
|
||||
|
||||
my ($temp_fh, $filename) = tempfile("qpsmtpd.clamav.$$.XXXXXX",
|
||||
DIR => $self->{_spool_dir});
|
||||
unless ($temp_fh) {
|
||||
$self->logerror("Couldn't open tempfile in $self->{_spool_dir}: $!");
|
||||
my $filename = $transaction->body_filename;
|
||||
unless (defined $filename) {
|
||||
$self->log(LOGERROR, "didn't get a filename");
|
||||
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];
|
||||
if ( $mode & 07077 ) { # must be sharing spool directory with external app
|
||||
$self->log(LOGWARN,
|
||||
@ -195,7 +183,6 @@ sub clam_scan {
|
||||
my $result = ($? >> 8);
|
||||
my $signal = ($? & 127);
|
||||
|
||||
unlink($filename);
|
||||
chomp($output);
|
||||
|
||||
$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