qpsmtpd/plugins/virus/uvscan

135 lines
3.6 KiB
Plaintext
Raw Normal View History

#!perl -Tw
=head1 NAME
uvscan
=head1 DESCRIPTION
A qpsmtpd plugin for the McAfee commandline virus scanner, uvscan.
=head1 INSTALL AND CONFIG
Place this plugin in the plugin/virus directory beneath the standard
qpsmtpd installation. If you installed uvscan with the default path, you
can use this plugin with default options (nothing specified):
=over 4
=item B<uvscan_location>
Full path to the uvscan binary and all signature files; defaults to
/usr/local/bin/uvscan.
=item B<deny_viruses>
Whether the scanner will automatically delete messages which have viruses.
Takes either 'yes' or 'no' (defaults to 'yes').
=back
=head1 AUTHOR
John Peacock <jpeacock@cpan.org>
=head1 COPYRIGHT AND LICENSE
Copyright (c) 2004 John Peacock
Based heavily 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
sub register {
my ($self, $qp, @args) = @_;
while (@args) {
$self->{"_uvscan"}->{pop @args}=pop @args;
}
$self->{"_uvscan"}->{"uvscan_location"}||="/usr/local/bin/uvscan";
}
sub hook_data_post {
my ($self, $transaction) = @_;
return (DECLINED)
if $transaction->data_size > 250_000;
# 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 )
{
Add plugable logging support include sample plugin which replicates the existing core code. Add OK hook. * lib/Qpsmtpd.pm (init_logger): replaced with log_level() (load_logging): NEW - load logging plugins without calling log() (log_level): NEW - set/get global $LogLevel scalar (log): now just a wrapper for varlog(); called only by core code (varlog): initializes logging if not already done, calls logging plugins in turn and falls back to interal logging unless plugins OK or DECLINED (_load_plugins): only display "Loading plugin" when actually loading one (run_hooks): load logging plugins without calling log(); add OK hook as else of the DENY* case (spool_dir): use global $Spool_dir scalar to cache location * lib/Qpsmtpd/Plugin.pm (%hooks): add "logging" and "ok" (register_hook): add local _hook to object cache (log): call varlog() with additional parameters hook and plugin_name except for logging hook (compile): add accessor sub for local _hook scalar * lib/Qpsmtpd/SMTP.pm (mail, rcpt): change loglevel to LOGALERT instead of LOGWARN for from/to * qpsmtpd-forkserver (REAPER): use package ::log() instead of warn() (main): defer calling log until $plugin_loader has been initialized (log): call logging using the $plugin_loader object * plugins/logging/warn NEW: sample plugin which replicates the core logging functionality * plugins/logging/devnull NEW: sample plugin which logs nothing (for testing multiple logging plugin functionality) * config.sample/logging sample configuration file for logging plugins * plugins/virus/uvscan plugins/virus/clamav Increase loglevel for non-serious warnings to LOGWARN from LOGERROR git-svn-id: https://svn.perl.org/qpsmtpd/trunk@398 958fd67b-6ff1-0310-b445-bb7760255be9
2005-03-24 22:16:35 +01:00
$self->log( LOGWARN, "non-multipart mail - skipping" );
return DECLINED;
}
my $filename = $transaction->body_filename;
return (DECLINED) unless $filename;
# Now do the actual scanning!
my @cmd =($self->{"_uvscan"}->{"uvscan_location"},
'--mime', '--unzip', '--secure', '--noboot',
$filename, '2>&1 |');
$self->log(LOGINFO, "Running: ",join(' ', @cmd));
open(FILE, join(' ', @cmd)); #perl 5.6 doesn't properly support the pipe
# mode list form of open, but this is basically the same thing. This form
# of exec is safe(ish).
my $output;
while (<FILE>) { $output.=$_; }
close FILE;
my $result = ($? >> 8);
my $signal = ($? & 127);
my $virus;
if ($output && $output =~ m/.*\W+Found (.*)\n/m) {
$virus=$1;
}
if ($output && $output =~ m/password-protected/m) {
return (DENY, 'We do not accept password-protected zip files!');
}
if ($signal) {
$self->log(LOGWARN, "uvscan exited with signal: $signal");
return (DECLINED);
}
if ($result == 2) {
$self->log(LOGERROR, "Integrity check for a DAT file failed.");
return (DECLINED);
} elsif ($result == 6) {
$self->log(LOGERROR, "A general problem has occurred.");
return (DECLINED);
} elsif ($result == 8) {
$self->log(LOGERROR, "The program could not find a DAT file.");
return (DECLINED);
} elsif ($result == 15) {
$self->log(LOGERROR, "The program self-check failed");
return (DECLINED);
} elsif ( $result ) { # all of the possible virus returns
if ($result == 12) {
$self->log(LOGERROR, "The program tried to clean a file but failed.");
} elsif ($result == 13) {
$self->log(LOGERROR, "One or more virus(es) found");
} elsif ($result == 19) {
$self->log(LOGERROR, "Successfully cleaned the file");
}
if (lc($self->{"_uvscan"}->{"deny_viruses"}) eq "yes") {
return (DENY, "Virus Found: $virus");
}
$transaction->header->add('X-Virus-Found', 'Yes');
$transaction->header->add('X-Virus-Details', $virus);
return (DECLINED);
}
$transaction->header->add('X-Virus-Checked',
"Checked by McAfee uvscan on ".$self->qp->config("me"));
return (DECLINED);
}