diff --git a/common/etc/MailScanner/MailScanner.conf b/common/etc/MailScanner/MailScanner.conf index bbb1c83..82ab027 100644 --- a/common/etc/MailScanner/MailScanner.conf +++ b/common/etc/MailScanner/MailScanner.conf @@ -709,6 +709,7 @@ Virus Scanning = yes # avastd the daemon version from www.avast.com # sophos from www.sophos.com # 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 # esets from www.eset.com # f-secure from www.f-secure.com @@ -929,6 +930,9 @@ Sophos Lib Dir = /opt/sophos-av/lib # scanner setting. 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 # ------------------------------------- diff --git a/common/usr/share/MailScanner/perl/MailScanner/ConfigDefs.pl b/common/usr/share/MailScanner/perl/MailScanner/ConfigDefs.pl index 9799fa0..e82ad08 100644 --- a/common/usr/share/MailScanner/perl/MailScanner/ConfigDefs.pl +++ b/common/usr/share/MailScanner/perl/MailScanner/ConfigDefs.pl @@ -447,6 +447,7 @@ MSMailSocketType inet MSMailSocketDir /var/spool/postfix/public/qmqp KseSocket /var/run/kse/kse.sock FsecureSocket /tmp/.fsav-0 +SAVIDSocket /var/lib/savdid/savdid.sock # # These variables match on any rule matching From:, else anything for To: diff --git a/common/usr/share/MailScanner/perl/MailScanner/SweepViruses.pm b/common/usr/share/MailScanner/perl/MailScanner/SweepViruses.pm index 8da7fd8..c46e15a 100644 --- a/common/usr/share/MailScanner/perl/MailScanner/SweepViruses.pm +++ b/common/usr/share/MailScanner/perl/MailScanner/SweepViruses.pm @@ -82,6 +82,17 @@ my %Scanners = ( SupportScanning => $S_SUPPORTED, SupportDisinfect => $S_NONE, }, + savid => { + Name => 'Sophos', + Lock => 'sophosBusy.lock', + CommonOptions => '', + DisinfectOptions => '', + ScanOptions => '', + InitParser => \&InitSAVIDParser, + ProcessOutput => \&ProcessOutput, + SupportScanning => $S_SUPPORTED, + SupportDisinfect => $S_NONE, + }, sophossavi => { Name => 'SophosSAVI', Lock => 'sophosBusy.lock', @@ -930,6 +941,9 @@ sub TryOneCommercial { } elsif ( $scanner eq 'f-secured' ) { FsecureScan( $subdir, $disinfect, $batch ); exit; + } elsif ( $scanner eq 'savid' ) { + SAVIDScan( $subdir, $disinfect, $batch ); + exit; } else { exec "$sweepcommand $instdir $voptions $subdir"; 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 sub InitSophosSAVIParser { ; @@ -2132,6 +2153,18 @@ sub InstalledScanners { 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 clamav is in the list, replace it with clamd, else add clamd my $foundit = 0; @@ -3312,4 +3345,153 @@ sub Fsecured { $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; diff --git a/config.index/conf_array.php b/config.index/conf_array.php index 83095b9..e52ef08 100644 --- a/config.index/conf_array.php +++ b/config.index/conf_array.php @@ -1876,6 +1876,16 @@ Ignored Web Bug Filenames = spacer pixel.gif pixel.png', scanner setting.', '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' => array ( 'external' => 'mta',