3
3
# MailScanner - SMTP E-Mail Virus Scanner
4
4
# Copyright (C) 2002 Julian Field
6
# $Id: mailscanner,v 1.142.2.133 2004/05/01 12:32:26 jkf Exp $
6
# $Id: mailscanner,v 1.142.2.188 2004/12/30 14:33:26 jkf Exp $
8
8
# This program is free software; you can redistribute it and/or modify
9
9
# it under the terms of the GNU General Public License as published by
39
# Awkard BEGIN block so that we pick up MIME::Base64 from the right place!
41
my(@oldinc,@safecopy,$path);
43
# Look in /usr/local/MailScanner/utils for the modules
47
# Duplicate path with /usr/local/MailScanner/utils stuck on the front
49
foreach $path (reverse @oldinc) {
50
next unless $path =~ /\//;
51
$path =~ s/^\/usr/\/usr\/lib\/MailScanner\/utils/;
56
require MIME::QuotedPrint;
102
124
# First production release will be 4.00.1.
104
$MailScanner::Config::MailScannerVersion = '4.30.3';
126
$MailScanner::Config::MailScannerVersion = '4.37.7';
128
# Are we just printing version numbers and exiting?
129
if (grep /^-+[vV]/, @ARGV) {
130
my @Modules = qw/AnyDBM_File Archive::Zip Carp Convert::BinHex Convert::TNEF Data::Dumper DirHandle Fcntl File::Basename File::Copy FileHandle File::Path File::Temp HTML::Entities HTML::Parser HTML::TokeParser IO IO::File IO::Pipe Mail::ClamAV Mail::SpamAssassin MIME::Base64 MIME::Decoder MIME::Decoder::UU MIME::Head MIME::Parser MIME::QuotedPrint MIME::Tools MIME::WordDecoder Net::CIDR POSIX SAVI Socket Sys::Syslog Time::localtime/;
131
my @Optional = qw#DB_File.pm Digest.pm Digest/HMAC.pm Digest/MD5.pm Digest/SHA1.pm Inline.pm Mail/ClamAV.pm Mail/SpamAssassin.pm Mail/SPF/Query.pm Net/CIDR/Lite.pm Net/DNS.pm Net/LDAP.pm Parse/RecDescent.pm SAVI.pm Sys/Hostname/Long.pm Test/Harness.pm Test/Simple.pm Text/Balanced.pm URI.pm#;
132
my($module, $s, $v, $m);
134
printf("Running on\n%s", `uname -a`);
135
printf("This is %s", `cat /etc/redhat-release`) if -f "/etc/redhat-release";
136
printf("This is %s", `head -1 /etc/SuSE-release`) if -f "/etc/SuSE-release";
137
printf("This is Perl version %f (%vd)\n", $], $^V);
138
print "\nThis is MailScanner version " . $MailScanner::Config::MailScannerVersion . "\n";
139
print "Module versions are:\n";
140
foreach $module (@Modules) {
141
$s = '$' . "$module" . '::VERSION';
143
print "$v\t$module\n" if $v ne "";
145
print "\nOptional module versions are:\n";
146
foreach $module (@Optional) {
150
$s = '$' . "$m" . '::VERSION';
151
$v = eval("require \"$module\"; $s") || "missing";
106
157
# Work out what directory we're in and add it onto the front
107
158
# of the include path so that we can work if we're just chucked
129
180
my $ConfFile = $ARGV[0];
130
181
$ConfFile = '/opt/MailScanner/etc/MailScanner.conf' unless $ConfFile;
183
# Check the MailScanner version number against what is in MailScanner.conf
184
my $NeedVersion = MailScanner::Config::QuickPeek($ConfFile,
185
'mailscannerversionnumber');
187
my($ConfMajor, $ConfMinor, $ConfRelease);
188
my($Error, $AreMajor, $AreMinor, $AreRelease);
190
$NeedVersion =~ /^(\d+)\.(\d+)\.(\d+)$/;
191
($ConfMajor, $ConfMinor, $ConfRelease) = ($1+0, $2+0, $3+0);
192
$ConfMajor = 0 unless $ConfMajor;
193
$ConfMinor = 0 unless $ConfMinor;
194
$ConfRelease = 0 unless $ConfRelease;
195
$MailScanner::Config::MailScannerVersion =~ /^(\d+)\.(\d+)\.(\d+)$/;
196
($AreMajor, $AreMinor, $AreRelease) = ($1+0, $2+0, $3+0);
197
$AreMajor = 0 unless $AreMajor;
198
$AreMinor = 0 unless $AreMinor;
199
$AreRelease = 0 unless $AreRelease;
200
if ($ConfMajor > $AreMajor) {
202
} elsif ($ConfMajor == $AreMajor) {
203
if ($ConfMinor > $AreMinor) {
205
} elsif ($ConfMinor == $AreMinor) {
206
if ($ConfRelease > $AreRelease) {
212
print STDERR "The configuration file $ConfFile\nis too new for this version of MailScanner.\nThis is version " . $MailScanner::Config::MailScannerVersion . " but the config file is for at least version $NeedVersion\n";
132
217
# Load the MTA modules we need
133
218
my($MTAmod, $MTADSmod);
134
219
# LEOH:if (MailScanner::Config::QuickPeek($ConfFile, 'mta') =~ /exim/i) {
184
269
# WorkDir to be able to clean up after killed children
185
270
# BayesRebuildPeriod to be able to rebuild the Bayes database regularly
272
use vars qw($RunInForeground);
273
$RunInForeground= MailScanner::Config::QuickPeek($ConfFile, 'runinforeground');
187
274
my $MaxChildren = MailScanner::Config::QuickPeek($ConfFile, 'maxchildren');
188
275
my $Debug = MailScanner::Config::QuickPeek($ConfFile, 'debug');
189
276
my $WorkDir = MailScanner::Config::QuickPeek($ConfFile, 'incomingworkdir');
193
280
# to critical system files, or create pidfiles that point to critical
194
281
# system processes)
195
282
$Debug = 0 unless $Debug =~ /yes|1/i;
283
$RunInForeground = 0 unless $RunInForeground =~ /yes|1/i;
285
# Enable STDOUT flushing if running in foreground
286
# to be able to actively capture it with a logger
287
$| = 1 if $RunInForeground;
197
289
# Give the user their shell back
198
290
ForkDaemon($Debug);
223
315
# Set when the next rebuild is due if regular rebuilds are being done
224
316
$NextRebuildDueTime = time + $BayesRebuildPeriod if $BayesRebuildPeriod;
318
# If we run in foreground, SIGKILL to the parent will try to reload
319
# by SIGKILLing its children
320
$SIG{'HUP'} = 'ReloadParent' if $RunInForeground;
227
323
while($NumberOfChildren < $MaxChildren) {
228
324
# Trigger 1 Bayes rebuild if the period has expired
232
328
$NextRebuildDueTime = time + $BayesRebuildPeriod;
330
print STDOUT sprintf("About to fork child #%d of %d...\n",
331
$NumberOfChildren+1, $MaxChildren)
234
333
my $born_pid = fork();
235
334
if (!defined($born_pid)) {
236
335
die "Cannot fork off child process, $!";
377
477
$StartTime = time;
378
478
$RestartTime = $StartTime + MailScanner::Config::Value('restartevery');
480
my $FirstCheck = MailScanner::Config::Value('firstcheck');
481
MailScanner::Log::WarnLog("First Check must be set to MCP or spam")
482
unless $FirstCheck =~ /mcp|spam/i;
483
my $VirusBeforeSpamMCP = MailScanner::Config::Value('virusbeforespammcp');
380
485
while (time>=$StartTime && time<$RestartTime) {
381
486
$workarea->Clear();
382
487
$batch = new MailScanner::MessageBatch();
412
517
# Archive untouched incoming messages to directories
413
518
$batch->ArchiveToFilesystem();
416
$batch->StartTiming('mcp', 'MCP Checks');
419
$batch->HandleNonMCP();
420
$batch->StopTiming('mcp', 'MCP Checks');
423
$batch->StartTiming('spam', 'Spam Checks');
424
$batch->SpamChecks();
425
$batch->HandleSpam();
427
$batch->StopTiming('spam', 'Spam Checks');
520
# Have to do this very early as it's needed for MCP and spam bouncing
521
$global::MS->{work}->BuildInDirs($batch);
523
# Yes I know this isn't elegant, but it's very short so it will do :-)
524
if ($FirstCheck =~ /mcp/i) {
526
$batch->StartTiming('mcp', 'MCP Checks');
529
$batch->HandleNonMCP();
530
$batch->StopTiming('mcp', 'MCP Checks');
533
$batch->StartTiming('spam', 'Spam Checks');
534
$batch->SpamChecks();
535
$batch->HandleSpam();
537
$batch->StopTiming('spam', 'Spam Checks');
540
$batch->StartTiming('spam', 'Spam Checks');
541
$batch->SpamChecks();
542
$batch->HandleSpam();
544
$batch->StopTiming('spam', 'Spam Checks');
547
$batch->StartTiming('mcp', 'MCP Checks');
550
$batch->HandleNonMCP();
551
$batch->StopTiming('mcp', 'MCP Checks');
429
554
# Deliver all the messages we are not scanning at all,
430
555
# and mark them for deletion.
435
560
# Extract all the attachments
436
561
$batch->StartTiming('virus', 'Virus Scanning');
437
$global::MS->{work}->BuildInDirs($batch);
562
# Moved upwards: $global::MS->{work}->BuildInDirs($batch);
438
563
$batch->Explode();
440
565
# Report all the unparsable messages, but don't delete anything
467
592
# Quarantine all the infected attachments
468
593
$batch->QuarantineInfections();
595
# Remove any infected spam from the spam+mcp archives
596
$batch->RemoveInfectedSpam();
470
598
# Clean all the infections out of the messages
553
681
MailScanner::Log::InfoLog("MailScanner child caught a SIG%s", $sig);
554
682
# Finish off any incoming queue file deletes that were pending
555
683
MailScanner::SMDiskStore::DoPendingDeletes();
684
# Kill off any commercial virus scanner process groups that are still running
685
kill -15, $MailScanner::SweepViruses::ScannerPID
686
if $MailScanner::SweepViruses::ScannerPID;
556
687
# Destroy the incoming work dir
557
688
$global::MS->{work}->Destroy() if $global::MS && $global::MS->{work};
558
689
# Close down all the user's custom functions
567
# SIGTERM handler for parent process.
568
# HUP all the children, then commit suicide.
569
# Cannot log as no logging in the parent.
572
my($sig) = @_; # Arg is the signal name
573
697
my($child, @dirlist);
575
699
#print STDERR "Killing child processes...\n";
700
if ($RunInForeground) {
701
print STDOUT "Killing child processes ";
702
print STDOUT join( '/', keys %Children);
576
704
kill 1, keys %Children;
705
print STDOUT " and giving them time to die...\n" if $RunInForeground;
577
707
sleep 3; # Give them time to die peacefully
708
print STDOUT "Cleaning up..." if $RunInForeground;
579
710
# Clean up after the dying processes in case they left a mess.
580
711
foreach $child (keys %Children) {
581
712
#push @dirlist, "$WorkDir/$child" if -d "$WorkDir/$child";
582
713
rmtree("$WorkDir/$child", 0, 1) if -d "$WorkDir/$child";
585
#system($global::rm . " -rf \"" . join("\" \"", @dirlist) . "\"")
715
print STDOUT "Done\n" if $RunInForeground;
719
# SIGKILL handler for parent process.
720
# HUP all the children, then keep working.
723
my($sig) = @_; # Arg is the signal name
725
print STDOUT "MailScanner parent caught a SIG$sig - reload\n"
730
print STDOUT "MailScanner reloaded.\n" if $RunInForeground;
736
# SIGTERM handler for parent process.
737
# HUP all the children, then commit suicide.
738
# Cannot log as no logging in the parent.
741
my($sig) = @_; # Arg is the signal name
743
print STDOUT "MailScanner parent caught a SIG$sig\n" if $RunInForeground;
747
print STDOUT "Exiting MailScanner - Bye.\n" if $RunInForeground;
630
791
# Get current debugging flag, and invert it:
631
792
my $current = config MIME::ToolUtils 'DEBUGGING';
632
793
#config MIME::ToolUtils DEBUGGING => !$current;
794
} elsif ($RunInForeground) {
795
# PERT-BBY we don't close STDXX neither fork() nor setsid()
796
# if we want to run in the foreground
797
print STDOUT "MailScanner $MailScanner::Config::MailScannerVersion " .
798
"starting in foreground mode - pid is [$$]\n";
634
800
$SIG{'CHLD'} = \&Reaper;
750
916
# Check the versions of the MIME and SpamAssassin modules
752
918
sub CheckModuleVersions {
753
my($varname, $module_version);
755
# These version numbers are what come in the MIME-tools v5.410 package,
757
my %mime_required = (
769
foreach (keys %mime_required) {
770
$varname = "MIME::". ucfirst lc($_) ."::VERSION";
771
defined $$varname or next;
772
$module_version = $$varname;
773
$module_version >= $mime_required{$_} or
774
MailScanner::Log::DieLog("FATAL: Newer MIME-tools module needed: %s" .
775
" is only MIME::$_ -- %s required",
776
$module_version, $mime_required{$_});
921
# Check the MIME-tools version
922
MailScanner::Log::DieLog("FATAL: Newer MIME::Tools module needed: " .
923
"MIME::Tools is only %s -- 5.412 required",
924
$MIME::Tools::VERSION)
925
if defined $MIME::Tools::VERSION &&
926
$MIME::Tools::VERSION<"5.412";
779
929
# And check the SpamAssassin version
780
$varname = "Mail::SpamAssassin::VERSION";
781
930
MailScanner::Log::DieLog("FATAL: Newer Mail::SpamAssassin module needed: " .
782
931
"Mail::SpamAssassin is only %s -- 2.1 required",
783
932
$Mail::SpamAssassin::VERSION)