FET: Add support for the Sophos SAVID Daemon Anti-virus Engine (#481)

This commit is contained in:
Andrew Colin Kissa 2020-05-16 14:40:16 +02:00 committed by GitHub
parent 707d7d9001
commit 74ed6cde64
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 197 additions and 0 deletions

View file

@ -709,6 +709,7 @@ Virus Scanning = yes
# avastd the daemon version from www.avast.com # avastd the daemon version from www.avast.com
# sophos from www.sophos.com # sophos from www.sophos.com
# sophossavi (also from www.sophos.com, using the SAVI perl module) # sophossavi (also from www.sophos.com, using the SAVI perl module)
# savid (also from www.sophos.com, using the SAVID daemon)
# bitdefender from www.bitdefender.com # bitdefender from www.bitdefender.com
# esets from www.eset.com # esets from www.eset.com
# f-secure from www.f-secure.com # f-secure from www.f-secure.com
@ -929,6 +930,9 @@ Sophos Lib Dir = /opt/sophos-av/lib
# scanner setting. # scanner setting.
Monitors For Sophos Updates = /opt/sophos-av/lib/sav/*.ide Monitors For Sophos Updates = /opt/sophos-av/lib/sav/*.ide
# SophosSAVID only: location of the socket
SAVID Socket = /var/lib/savdid/savdid.sock
# #
# Options specific to ClamAV Anti-Virus # Options specific to ClamAV Anti-Virus
# ------------------------------------- # -------------------------------------

View file

@ -447,6 +447,7 @@ MSMailSocketType inet
MSMailSocketDir /var/spool/postfix/public/qmqp MSMailSocketDir /var/spool/postfix/public/qmqp
KseSocket /var/run/kse/kse.sock KseSocket /var/run/kse/kse.sock
FsecureSocket /tmp/.fsav-0 FsecureSocket /tmp/.fsav-0
SAVIDSocket /var/lib/savdid/savdid.sock
# #
# These variables match on any rule matching From:, else anything for To: # These variables match on any rule matching From:, else anything for To:

View file

@ -82,6 +82,17 @@ my %Scanners = (
SupportScanning => $S_SUPPORTED, SupportScanning => $S_SUPPORTED,
SupportDisinfect => $S_NONE, SupportDisinfect => $S_NONE,
}, },
savid => {
Name => 'Sophos',
Lock => 'sophosBusy.lock',
CommonOptions => '',
DisinfectOptions => '',
ScanOptions => '',
InitParser => \&InitSAVIDParser,
ProcessOutput => \&ProcessOutput,
SupportScanning => $S_SUPPORTED,
SupportDisinfect => $S_NONE,
},
sophossavi => { sophossavi => {
Name => 'SophosSAVI', Name => 'SophosSAVI',
Lock => 'sophosBusy.lock', Lock => 'sophosBusy.lock',
@ -930,6 +941,9 @@ sub TryOneCommercial {
} elsif ( $scanner eq 'f-secured' ) { } elsif ( $scanner eq 'f-secured' ) {
FsecureScan( $subdir, $disinfect, $batch ); FsecureScan( $subdir, $disinfect, $batch );
exit; exit;
} elsif ( $scanner eq 'savid' ) {
SAVIDScan( $subdir, $disinfect, $batch );
exit;
} else { } else {
exec "$sweepcommand $instdir $voptions $subdir"; exec "$sweepcommand $instdir $voptions $subdir";
MailScanner::Log::WarnLog("Cannot run commercial AV $scanner " . MailScanner::Log::WarnLog("Cannot run commercial AV $scanner " .
@ -1165,6 +1179,13 @@ sub InitGenericParser {
; ;
} }
# Initialise any state variables the Sophos SAVID output parser uses
my (%SAVIDFiles);
sub InitSAVIDParser {
%SAVIDFiles = ();
}
# Initialise any state variables the Sophos SAVI output parser uses # Initialise any state variables the Sophos SAVI output parser uses
sub InitSophosSAVIParser { sub InitSophosSAVIParser {
; ;
@ -2132,6 +2153,18 @@ sub InstalledScanners {
s/^sophos$/sophossavi/i; s/^sophos$/sophossavi/i;
} }
} }
if ( SAVIDScan('ISITINSTALLED') eq 'SAVIDOK' ) {
# if sophos in list replace with savid
my $foundit = 0;
foreach (@installed) {
if ( $_ eq 'sophos' ) {
s/^sophos$/savid/;
$foundit = 1;
last;
}
}
push @installed, 'savid' unless $foundit;
}
if (ClamdScan('ISITINSTALLED') eq 'CLAMDOK') { if (ClamdScan('ISITINSTALLED') eq 'CLAMDOK') {
# If clamav is in the list, replace it with clamd, else add clamd # If clamav is in the list, replace it with clamd, else add clamd
my $foundit = 0; my $foundit = 0;
@ -3312,4 +3345,153 @@ sub Fsecured {
$dh->close; $dh->close;
} }
# Savid functions
sub SAVIDScan {
my ( $dirname, $disinfect, $messagebatch ) = @_;
my $lintonly = 0;
$lintonly = 1 if $dirname eq 'ISITINSTALLED';
my $ScanDir = "$global::MS->{work}->{dir}/$dirname";
$ScanDir =~ s/\/\.$//;
my $TimeOut = MailScanner::Config::Value('virusscannertimeout');
my $Socket = MailScanner::Config::Value('SAVIDSocket');
$Socket = '/var/lib/savdid/savdid.sock' unless ($Socket);
my $line = '';
my $sock;
$sock = ConnectToAV( $Socket, undef, $TimeOut );
print "ERROR:: COULD NOT CONNECT TO SAVID, RECOMMEND RESTARTING DAEMON "
. ":: $dirname\n"
unless $sock || $lintonly;
print "ScAnNeRfAiLeD\n" unless $sock || $lintonly;
MailScanner::Log::WarnLog( "ERROR:: COULD NOT CONNECT TO SAVID, "
. "RECOMMEND RESTARTING DAEMON " )
unless $sock || $lintonly;
return 1 unless $sock;
# $sock->close if $lintonly;
return 'SAVIDOK' if $lintonly;
%SAVIDFiles = ();
if ( SAVID( $ScanDir, $sock ) == -1 ) {
print "ERROR:: COULD NOT SCAN USING SAVID,"
. " RECOMMEND CHECKING DIRECTORY ACCESS "
. ":: $dirname\n";
print "ScAnNeRfAiLeD\n";
return 1;
}
# Read back all the reports
foreach my $key ( keys %SAVIDFiles ) {
my ($path);
$path = $key;
$path =~ s/^$ScanDir/./;
if ( $SAVIDFiles{$key}->{error} ) {
print "ERROR:: $SAVIDFiles{$key}->{errormsg} :: $path\n";
}
elsif ( !$SAVIDFiles{$key}->{error} && $SAVIDFiles{$key}->{infected} ) {
print "INFECTED:: $SAVIDFiles{$key}->{virusname} :: $path\n";
}
elsif ($SAVIDFiles{$key}->{clean}
&& !$SAVIDFiles{$key}->{error}
&& !$SAVIDFiles{$key}->{infected} )
{
print "CLEAN:: message did not match any signature :: $path\n";
}
else {
print "ERROR:: $SAVIDFiles{$key}->{error} :: $path\n";
}
}
$sock->close;
}
sub SAVID {
my ( $dir, $sock ) = @_;
my $dh = new DirHandle $dir;
unless ($dh) {
MailScanner::Log::WarnLog( "SAVID: failed to process directory %s",
$dir );
return -1;
}
my $file;
while ( defined( $file = $dh->read ) ) {
my $f = "$dir/$file";
$f =~ /^(.*)$/;
$f = $1;
next if $file =~ /^\./ && -d $f;
if ( -d $f ) {
SAVID( $f, $sock );
}
else {
my %response;
$response{error} = 0;
$response{clean} = 0;
$response{infected} = 0;
$response{virusname} = '';
$response{errormsg} = '';
# check if we can write to socket
unless ( $sock->print("$f\n") ) {
$response{errormsg} = "SAVID: failed to scan $f";
$response{error} = 1;
$SAVIDFiles{$f} = \%response;
next;
}
# check if we can flush socket
unless ( $sock->flush ) {
$response{errormsg} = "SAVID: failed to scan $f";
$response{error} = 1;
$SAVIDFiles{$f} = \%response;
next;
}
# check if we can read the response
my ( $scan_response, $rc );
my $rc = $sock->sysread( $scan_response, 256 );
unless ($rc) {
$response{error} = 1;
$response{errormsg} = "SAVID: no response to scan $f";
$SAVIDFiles{$f} = \%response;
next;
}
# process the results
if ( $scan_response =~ m/^0/ ) {
$response{clean} = 1;
$response{infected} = 0;
}
elsif ( $scan_response =~ m/^1/ ) {
$response{clean} = 0;
$response{infected} = 1;
my ($virus_name) = $scan_response =~ /^1:(.*)$/;
$response{virusname} = $virus_name;
}
elsif ( $scan_response =~ m/^-1:(.*)$/ ) {
my $error_message = $1;
$error_message ||= 'SAVID: unknown error';
$response{error} = 1;
$response{errormsg} = $error_message;
}
else {
$response{error} = 1;
$response{errormsg} = 'SAVID: unknown response error';
}
$SAVIDFiles{$f} = \%response;
}
}
$dh->close;
}
1; 1;

View file

@ -1876,6 +1876,16 @@ Ignored Web Bug Filenames = spacer pixel.gif pixel.png',
scanner setting.', scanner setting.',
'value' => ' /opt/sophos-av/lib/sav/*.ide', 'value' => ' /opt/sophos-av/lib/sav/*.ide',
), ),
'savidsocket' =>
array (
'external' => 'SAVIDSocket',
'type' => 'other',
'ruleset' => 'no',
'default' => '/var/lib/savdid/savdid.sock',
'name' => 'SAVID Socket',
'desc' => ' SophosSAVID only: location of the socket',
'value' => ' /var/lib/savdid/savdid.sock',
),
'mta' => 'mta' =>
array ( array (
'external' => 'mta', 'external' => 'mta',