Hi all,
I have the following patch available to be used with the default qmail-scanner-1.24 distribution which adds the following funcionality:
+ File::Scan
Configure autodetects if File::Scan is installed and makes this the first scanner used to scan emails. Because its *really* fast, this should not affect most people, but if you have different preferences, you can manually edit the @scanner_array.
+ sub-filescan.pl
This includes a _recursive_ file generator which should be portable across implementations: sub get_file_list(); its only argument being a directory, returning and array of fully qualified filenames (including path), which can potentially be used with perlscanner to do a recursive scan.... (my todo list)
+ sub reject_email() has been added to sub-log_msg.pl
This procedure can be used to reject the email after during the SMTP conversation by sending a permanent error code to the client after receiving the message with the DATA command. Although this will not save bandwidth, the rejected message will not make it into qmail's queues.
+ sub-clamdscan.pl sub-clamscan.pl sub-spamassassin.pl sub-filescan.pl
These have been modified to reject email.
SpamAssassin can reject mail if $score > $sa_reject_score (global variable) is set to a value > 0.
This applies cleanly _only_ to stock qmail-scanner-1.24.tar.gz
-- Jorge Valdes Intercom El Salvador [EMAIL PROTECTED] voz: ++(503) 278-5068 fax: ++(503) 265-7025
diff -rubN qmail-scanner-1.24-orig/configure qmail-scanner-1.24/configure --- qmail-scanner-1.24-orig/configure Mon Oct 18 18:26:55 2004 +++ qmail-scanner-1.24/configure Tue Nov 9 14:06:00 2004 @@ -38,7 +38,7 @@ VERSION=`grep '^# Version: ' qmail-scanner-queue.template` VERSION=`echo $VERSION|awk '{print $NF}'` -SUPPORTED_SCANNERS="clamscan,clamdscan,sweep,sophie,vscan,trophie,uvscan,csav,antivir,kavscanner,AvpLinux,kavdaemon,AvpDaemonClient,fsav,fprot,inocucmd,vexira,bitdefender,nod32,verbose_spamassassin,fast_spamassassin" +SUPPORTED_SCANNERS="filescan,clamscan,clamdscan,sweep,sophie,vscan,trophie,uvscan,csav,antivir,kavscanner,AvpLinux,kavdaemon,AvpDaemonClient,fsav,fprot,inocucmd,vexira,bitdefender,nod32,verbose_spamassassin,fast_spamassassin" SILENT_VIRUSES='klez,bugbear,hybris,yaha,braid,nimda,tanatos,sobig,winevar,palyh,fizzer,gibe,cailont,lovelorn,swen,dumaru,sober,hawawi,holar-i,mimail,poffer,bagle,worm.galil,mydoom,worm.sco,tanx,novarg,[EMAIL PROTECTED]' @@ -97,6 +97,7 @@ QS_USER="qscand" SKIP_SETUID_TEST="" MAX_ZIP_SIZE="1000000000" +USE_FILE_SCAN="" while [ -n "$1" ] do @@ -133,156 +134,166 @@ *) cat <<EOF >&2 valid options: - --qs-user <username> User that Qmail-Scanner runs as (default: $QS_USER) - --qmaildir <top of qmail> defaults to $QMAILDIR/ - --spooldir <spooldir> defaults to $AS_QQ/ - --bindir <installdir> where to install qmail-scanner-queue.pl + --qs-user <username> + User that Qmail-Scanner runs as (default: $QS_USER) + + --qmaildir <top of qmail> + defaults to $QMAILDIR/ + + --spooldir <spooldir> + defaults to $AS_QQ/ + + --bindir <installdir> + where to install qmail-scanner-queue.pl Defaults to /var/qmail/bin/ - --admin <username> user to Email alerts to (default: $USERNAME) - --domain <domain name> "user"@"domain" makes up Email address - to Email alerts to. - --scanners <list of installed content scanners> - Defaults to "auto" - will use - whatever scanners are found on system. - Use this option to override "auto" - set - to one or more of the following: -auto,none,$SUPPORTED_SCANNERS + --admin <username> + user to Email alerts to (default: $USERNAME) - Note the special-case "none". This - will disable all but the internal + --domain <domain name> + "user"@"domain" makes up Email address to Email alerts to. + + --scanners <list of installed content scanners> + Defaults to "auto" - will use whatever scanners are found on system. + Use this option to override "auto" - set to one or more of the + following: + auto,none,$SUPPORTED_SCANNERS + Note the special-case "none". This will disable all but the internal perlscanner module. - --skip-text-msgs [yes|no] Defaults to "yes" - Q-S will skip - running any anti-virus scanners on - any messages it works out are text-only. - i.e. don't have any attachments. - Set to "no" if you want them to be scanned - anyway. - - --notify "none|sender|recips|precips|admin|nmladm|nmlvadm|all" Defaults to "$NOTIFY_ADDRESSES". - Comma-separated list (no spaces!) - of addresses to which alerts should - be sent to. "nmladm" means only - notify admin for "user infections", - i.e. non-mailing-list mail. - "nmlvadm" is the same as nmladm - except - that it also doesn't notify for viral e-mails. - i.e. just "policy" quarantines get e-mails. This allows you to - still notify people when an e-mail is blocked due to - a policy decision (such as blocking password-protected - zip files), but a message tagged as viral by an AV system - will *not* trigger notification. + --skip-text-msgs [yes|no] + Defaults to "yes" - Q-S will skip running any anti-virus scanners on + any messages it works out are text-only. i.e. don't have any attachments. + Set to "no" if you want them to be scanned anyway. + + --notify "none|sender|recips|precips|admin|nmladm|nmlvadm|all" + Defaults to "$NOTIFY_ADDRESSES". Comma-separated list (no spaces!) + of addresses to which alerts should be sent to. "nmladm" means only + notify admin for "user infections", i.e. non-mailing-list mail. + "nmlvadm" is the same as nmladm - except that it also doesn't notify for + viral e-mails. i.e. just "policy" quarantines get e-mails. This allows + you to still notify people when an e-mail is blocked due to a policy + decision (such as blocking password-protected zip files), but a message + tagged as viral by an AV system will *not* trigger notification. Similarly, "psender"/"precips" means notify the sender/recips only if their e-mail was blocked for policy reasons. i.e. if an AV system found a virus, then don't notify the sender/recip as the address was probably forged. - --local-domains "one.domain,two.domain" Defaults to the - value of the "--domain" setting. - Comma-separated list (no spaces!) - of domains that are classified as - "local". This is needed to ensure - alerts are only sent to local users - and not remote when '--notify "*recips"' - is chosen. This will dramatically - reduce the chance of alerts being - sent to mailing-lists. - --silent-viruses "virus1,virus2" Defaults to "auto". - This option allows you to tell - Qmail-Scanner *not* to notify - senders when it quarantines one - of these viruses. Viruses such - as Klez alter the sender address - so that it has no relation to the - actual sender - so there's no point - in responding to Klez messages - it - just confuses people. The admin and - recips will still be notified as set - by "--notify". - Use this option to override "auto". - By default this is set to: - $SILENT_VIRUSES + + --local-domains "one.domain,two.domain" + Defaults to the value of the "--domain" setting. + Comma-separated list (no spaces!) of domains that are classified as + "local". This is needed to ensure alerts are only sent to local users + and not remote when '--notify "*recips"' is chosen. This will dramatically + reduce the chance of alerts being sent to mailing-lists. + + --silent-viruses "virus1,virus2" + Defaults to "auto". This option allows you to tell Qmail-Scanner *not* + to notify senders when it quarantines one of these viruses. Viruses such + as Klez alter the sender address so that it has no relation to the actual + sender - so there's no point in responding to Klez messages - it just + confuses people. The admin and recips will still be notified as set by + "--notify". Use this option to override "auto". By default this is set + to: $SILENT_VIRUSES + --lang "$LANGUAGES" Defaults to $QSLANG. - --archive [yes|no|regex] Defaults to "no". Whether to archive mail after - it as been processed. If "yes", all copies of - processed mail will be moved into the maildir - "$AS_QQ/$ARCHIVEDIR/". Any other string besides - "yes" and "no" will be treated as a REGEX. Only mail - from or to an address that contains that regex will - be archived. e.g. "jhaar|harry" or "[EMAIL PROTECTED]". - Be careful with this option, a badly written regex - will cause Qmail-Scanner to crash. - --redundant [yes|no] Defaults to "yes". Whether or not to let the scanners - also scan any zip files and the original "raw" Email - file. - --log-details [yes|syslog|no] Whether or not to log to mailstats.csv/via - syslog the attachment structure of every Email - message. Logs to "syslog" by default. - --log-crypto [yes|no] Defaults to "no". Whether or not to log the presence - of cryptographic (both signing and encrypting) - technologies in the "log-details". Q-S can flag - PGP, S/MIME and password-protected zip files. This - is informational logging only. - --fix-mime [yes|no|num] Defaults to "yes". Whether or not to attempt to - "fix" broken MIME messages before doing anything - else. Should be safe, but *may* break some - strange, old mailers (none known yet). If you see blocks - occurring due to this setting, try "--fix-mime 1" first - before "--fix-mime no". - --ignore-eol-check [yes|no] Defaults to "no". Making this "yes" stops Qmail-Scanner - from treating "\r" or "\0" chars in the headers of - MIME mail messages as being suspicious enough to quarantine - mail over. Some sites receive so much broken e-mail that this - option has been created so that they can still receive such - messages without having to be as drastic as to "--fix-mime no" - - which disables all sorts of other good stuff. Use only if you - have to. - - --add-dscr-hdrs [yes|no|all] Defaults to "no". This adds the now old-fashion - X-Qmail-Scanner headers to the message. "all" adds - the "rcpt to" headers too - this is a privacy hole. - --debug [yes|no] Whether or not debugging is turned on. On (yes) - by default. Can be also set to a number. Numbers - over 100 cause Q-S to not cleanup working files + + --archive [yes|no|regex] + Defaults to "no". Whether to archive mail after it as been processed. + If "yes", all copies of processed mail will be moved into the maildir + "$AS_QQ/$ARCHIVEDIR/". Any other string besides "yes" and "no" + will be treated as a REGEX. Only mail from or to an address that contains + that regex will be archived. e.g. "jhaar|harry" or "[EMAIL PROTECTED]". Be + careful with this option, a badly written regex will cause Qmail-Scanner + to crash. + + --redundant [yes|no] + Defaults to "yes". Whether or not to let the scanners also scan any zip + files and the original "raw" Email file. + + --log-details [yes|syslog|no] + Whether or not to log to mailstats.csv/via syslog the attachment structure + of every Email message. Logs to "syslog" by default. + + --log-crypto [yes|no] + Defaults to "no". Whether or not to log the presence of cryptographic + (both signing and encrypting) technologies in the "log-details". Q-S can + flag PGP, S/MIME and password-protected zip files. This is informational + logging only. + + --fix-mime [yes|no|num] + Defaults to "yes". Whether or not to attempt to "fix" broken MIME messages + before doing anything else. Should be safe, but *may* break some strange, + old mailers (none known yet). If you see blocks occurring due to this + setting, try "--fix-mime 1" first before "--fix-mime no". + + --ignore-eol-check [yes|no] + Defaults to "no". Making this "yes" stops Qmail-Scanner from treating "\r" + or "\0" chars in the headers of MIME mail messages as being suspicious + enough to quarantine mail over. Some sites receive so much broken e-mail + that this option has been created so that they can still receive such + messages without having to be as drastic as to "--fix-mime no" - which + disables all sorts of other good stuff. Use only if you have to. + + --add-dscr-hdrs [yes|no|all] + Defaults to "no". This adds the now old-fashion X-Qmail-Scanner headers + to the message. "all" adds the "rcpt to" headers too - this is a privacy + hole. + + --debug [yes|no] + Whether or not debugging is turned on. On (yes) by default. Can be also + set to a number. Numbers over 100 cause Q-S to not cleanup working files - thus allowing for offline debugging... - --unzip [yes|no] Whether or not to forcibly unzip all zip files. Off - by default as most AV's do unzip'ping themselves. - --max-zip-size [number] Defaults to 1 Gbytes. - This setting allows you to control the maximum size you - are willing to allow zip file attachments to unpack to. - This is to enable you to limit DoS attacks against your - Qmail-Scanner installation (someone could send you a small zip - file that unpacks to Gbytes of useless files - filling your harddisk). - Set to whatever value you think is appropriate for your system. The - default value of 1Gb is set so large so as not to assume anything about - your system - YOU WILL NEED TO SET THIS VALUE IN ORDER TO GAIN ANY - PROTECTION. Something like "100000000" (100 Mb) might be appropriate. - --block-password-protected [yes|no] Defaults to "no". Setting this to "yes" allows - you to quarantine any incoming zip files that are password - protected. This is primarily to stop viruses such as Bagle which - arrive within a password-protected zip file. - --batch Do not confirm configure information (mainly for scripting) - --install Create directory paths, install perl script, - and change ownerships to match. - --mime-unpacker "reformime" Defaults to reformime. + + --unzip [yes|no] + Whether or not to forcibly unzip all zip files. Off by default as most + AV's do unzip'ping themselves. + + --max-zip-size [number] + Defaults to 1 Gbytes. This setting allows you to control the maximum + size you are willing to allow zip file attachments to unpack to. This + is to enable you to limit DoS attacks against your Qmail-Scanner + installation (someone could send you a small zip file that unpacks to + Gbytes of useless files - filling your harddisk). Set to whatever + value you think is appropriate for your system. The default value of + 1Gb is set so large so as not to assume anything about your system - + YOU WILL NEED TO SET THIS VALUE IN ORDER TO GAIN ANY PROTECTION. + Something like "100000000" (100 Mb) might be appropriate. + + --block-password-protected [yes|no] + Defaults to "no". Setting this to "yes" allows you to quarantine any + incoming zip files that are password protected. This is primarily to + stop viruses such as Bagle which arrive within a password-protected + zip file. + + --batch + Do not confirm configure information (mainly for scripting) + + --install + Create directory paths, install perl script, and change ownerships to + match. + + --mime-unpacker "reformime" + Defaults to reformime. **************** Rarely Used **************** - --no-QQ-check Do not check that the QMAILQUEUE patch is installed. - This explicitly disables any "--install" reference - as that is NOT POSSIBLE with a manual install. - Use ONLY IF YOU MUST. The QMAILQUEUE patch is REALLY - a GOOD THING!!!! - - --skip-setuid-test don't test for setuid perl. Only of use for those wanting - to run the C-wrapper version. - - --qmail-queue-binary Set this to the FULL PATH to the Qmail qmail-queue - binary. This is only EVER set when doing a manual - install. + --no-QQ-check + Do not check that the QMAILQUEUE patch is installed. This explicitly + disables any "--install" reference as that is NOT POSSIBLE with a manual + install. Use ONLY IF YOU MUST. The QMAILQUEUE patch is REALLY a GOOD + THING!!!! + + --skip-setuid-test + don't test for setuid perl. Only of use for those wanting to run the + C-wrapper version. + + --qmail-queue-binary + Set this to the FULL PATH to the Qmail qmail-queue binary. This is only + EVER set when doing a manual install. This script must be run as root so it can detect problems with setuid @@ -997,6 +1008,14 @@ fi done +DD=`$PERL5 -e 'use File::Scan;' 2>&1` +if [ "$?" == "0" ]; then + USE_FILE_SCAN='use File::Scan;' + SCANNER_ARRAY="\"filescan_scanner\"" + INSTALLED_SCANNERS="$INSTALLED_SCANNERS +filescan" +fi + if [ "$FIND_SILENT_VIRUSES" = "$SILENT_VIRUSES" ]; then SILENT_VIRUSES="auto" fi @@ -1619,6 +1638,7 @@ s?BITDEFENDER?$BITDEFENDER?g; s?CLAMSCAN?$CLAMSCAN?g; s?CLAMDSCAN?$CLAMDSCAN?g; +s?USE_FILE_SCAN?$USE_FILE_SCAN?g; s?SPAMASSASSIN_BINARY?$SPAMASSASSIN_BINARY?g; s?SPAMC_BINARY?$SPAMC_BINARY?g; s?SPAMC_OPTIONS?$SPAMC_OPTIONS?g; diff -rubN qmail-scanner-1.24-orig/qmail-scanner-queue.template qmail-scanner-1.24/qmail-scanner-queue.template --- qmail-scanner-1.24-orig/qmail-scanner-queue.template Tue Oct 19 19:49:33 2004 +++ qmail-scanner-1.24/qmail-scanner-queue.template Tue Nov 9 12:03:29 2004 @@ -221,8 +221,10 @@ my $spamc_subject='SPAMC_SUBJECT'; my $spamassassin_binary='SPAMASSASSIN_BINARY'; my ($sa_comment,$sa_level); -my $sa_symbol='+'; -my ($tag_score)=""; +my $sa_symbol='*'; +my $sa_reject_score=0; +my $sa_warn_score=0; +my $tag_score=""; my $SNEAKY_WINDOWS_EXTENSIONS="exe|com|pps|w[pm][szd]|vcf|nws|cmd|bat|pif|sc[rt]|dll|ocx|do[ct]|xl[swt]|p[po]t|rtf|vb[se]?|hta|p[lm]|sh[bs]|hlp|chm|eml|ws[cfh]|ad[ep]|jse?|md[abew]|ms[ip]|reg|as[dfx]|cil|cpl"; my $VALID_WINDOWS_EXTENSIONS="sav|htm|html|pst|ost|txt|gif|jpeg|mpeg|jpg|png|mny|wav|tif|$SNEAKY_WINDOWS_EXTENSIONS"; @@ -268,6 +270,7 @@ #Want microsec times for debugging use Time::HiRes qw( usleep ualarm gettimeofday tv_interval ); use POSIX; +USE_FILE_SCAN use vars qw/ $opt_v $opt_h $opt_g $opt_r $opt_z/; @@ -297,7 +300,6 @@ exit; } - if ( $opt_g || $opt_r) { &generate_quarantine_db; exit 0; @@ -324,15 +326,12 @@ $ENV{'TMP'} = $ENV{'TMPDIR'} = "$scandir/tmp/$file_id"; #$ENV{'QMAILSUSER'} = $ENV{'QMAILSHOST'} = ''; - - if ($mimeunpacker_binary =~ /reformime/) { $mimeunpacker_binary .= " -x$ENV{'TMPDIR'}/"; } elsif ($mimeunpacker_binary =~ /ripmime/) { $mimeunpacker_binary .= " --unique_names -i - -d $ENV{'TMPDIR'}/"; } - #Get current timestamp for logs my ($sec,$min,$hour,$mday,$mon,$year,$nowtime); ($sec,$min,$hour,$mday,$mon,$year) = localtime(time); @@ -359,10 +358,8 @@ exit 0; } - &scanner_info; - if ($ENV{'TCPREMOTEIP'}) { $smtp_sender="via SMTP from $ENV{'TCPREMOTEIP'}"; $remote_smtp_ip=$ENV{'TCPREMOTEIP'}; @@ -454,7 +451,6 @@ } } - #Msg has been delivered now, so don't want hangs in this part #to affect delivery @@ -1047,8 +1043,6 @@ &debug("p_s: starting scan of directory \"$ENV{'TMPDIR'}\"..."); use DB_File; - - tie (%array, 'DB_File', "$db_filename.db", O_RDONLY, 0600) || &error_condition("cannot open $db_filename.db - $!"); if (!$quarantine_event && $illegal_mime && $headers{'mime-version'} && $BAD_MIME_CHECKS) { @@ -1319,7 +1313,6 @@ $still_headers=0 if (/^(\r|\r\n|\n)$/); #Insert Subject: line if e-mail dosn't contain one but must be tagged print QMQ "Subject: $spamc_subject\n" if ((!$still_headers) && ($sa_comment =~ /^yes/i) && (!$altered_subject) && $spamc_subject ne "" ); - } print QMQ; } @@ -1482,7 +1475,6 @@ } } system("$rm_binary -rf $ENV{'TMPDIR'}/ $scandir/$wmaildir/new/$file_id") if ($DEBUG < 100 && $file_id ne ""); - } @@ -1695,9 +1687,9 @@ while (<CLAMS>) { chomp; if (/ersion ([0-9\.\-a-z]+)/i) { - $SCANINFO .="clamdscan: $1. "; + $SCANINFO .="clamdscan: $1; "; } elsif (/^ClamAV ([^\/]+)\/([^\/]+)\//) { - $SCANINFO .="clamdscan: $1/$2. "; + $SCANINFO .="clamdscan: $1/$2; "; } } close(CLAMS); @@ -1712,7 +1704,11 @@ } } close(SPAS); - $SCANINFO .= "spamassassin: $spamassassin_eng. "; + $SCANINFO .= "spamassassin: $spamassassin_eng; "; + } elsif ($scanner eq 'filescan') { + my $fs = File::Scan->new(); + my $fv = $fs->VERSION; + $SCANINFO .= "File::Scan $fv; "; } else { #Catch-all for other ones $SCANINFO .= "$scanner: ???. "; @@ -1830,9 +1826,6 @@ } } - - - sub show_version { my ($scanner); &scanner_info; diff -rubN qmail-scanner-1.24-orig/sub-clamdscan.pl qmail-scanner-1.24/sub-clamdscan.pl --- qmail-scanner-1.24-orig/sub-clamdscan.pl Sun Oct 17 19:40:36 2004 +++ qmail-scanner-1.24/sub-clamdscan.pl Tue Nov 9 08:36:58 2004 @@ -5,6 +5,7 @@ my ($start_clamdscan_time)=[gettimeofday]; my ($DD,$clamdscan_status,$eclamdscan_status,$stop_clamdscan_time,$clamdscan_time); my ($clamdscan_verbose); + my $has_virus = 0; $clamdscan_verbose="-v" if ($DEBUG); &debug("run $clamdscan_binary $clamdscan_options $ENV{'TMPDIR'} 2>&1"); @@ -17,7 +18,7 @@ if ( $eclamdscan_status > 0) { if ($eclamdscan_status == 1 && $DD =~ /\:\s(.*)\sFOUND$/m) { - $quarantine_description=$+; + $quarantine_description=$+; $has_virus++; &debug("There be a virus! ($quarantine_description)"); ($quarantine_event=$quarantine_description)=~s/\s/_/g; $quarantine_event="CLAMDSCAN:".substr($quarantine_event,0,$QE_LEN); @@ -48,4 +49,7 @@ $stop_clamdscan_time=[gettimeofday]; $clamdscan_time = tv_interval ($start_clamdscan_time, $stop_clamdscan_time); &debug("clamdscan: finished scan of dir \"$ENV{'TMPDIR'}\" in $clamdscan_time secs"); + if ($has_virus) { + &reject_email("Rejected VIRUS ",$quarantine_description,33); + } } diff -rubN qmail-scanner-1.24-orig/sub-clamscan.pl qmail-scanner-1.24/sub-clamscan.pl --- qmail-scanner-1.24-orig/sub-clamscan.pl Mon Apr 19 22:04:15 2004 +++ qmail-scanner-1.24/sub-clamscan.pl Tue Nov 9 08:37:57 2004 @@ -5,6 +5,7 @@ my ($start_clamscan_time)=[gettimeofday]; my ($DD,$clamscan_status,$stop_clamscan_time,$clamscan_time); my ($clamscan_verbose,$eclamscan_status); + my $has_virus=0; $clamscan_verbose="-v" if ($DEBUG); &debug("run $clamscan_binary $clamscan_options $ENV{'TMPDIR'} 2>&1"); @@ -17,7 +18,7 @@ if ( $eclamscan_status > 0 ) { if ($eclamscan_status == 1 && $DD =~ /\:\s(.*)\sFOUND$/m) { - $quarantine_description=$+; + $quarantine_description=$+; $has_virus++; &debug("There be a virus! ($quarantine_description)"); ($quarantine_event=$quarantine_description)=~s/\s/_/g; $quarantine_event="CLAMSCAN:".substr($quarantine_event,0,$QE_LEN); @@ -41,4 +42,7 @@ $stop_clamscan_time=[gettimeofday]; $clamscan_time = tv_interval ($start_clamscan_time, $stop_clamscan_time); &debug("clamscan: finished scan of dir \"$ENV{'TMPDIR'}\" in $clamscan_time secs"); + if ($has_virus) { + &reject_email("Rejected VIRUS ",$quarantine_description,33); + } } diff -rubN qmail-scanner-1.24-orig/sub-filescan.pl qmail-scanner-1.24/sub-filescan.pl --- qmail-scanner-1.24-orig/sub-filescan.pl Wed Dec 31 18:00:00 1969 +++ qmail-scanner-1.24/sub-filescan.pl Tue Nov 9 00:30:55 2004 @@ -0,0 +1,63 @@ +############################### +# +## Recursively generates a file-list +## +############################### +sub get_file_list { + my @dfiles; + my @ufiles; + my $wdire = shift || return @ufiles; + opendir(DIRE, $wdire) || return @ufiles; + while (defined(my $file=readdir(DIRE))) { + next if $file eq '.'; + next if $file eq '..'; + my $full = join('/',$wdire,$file); + if (-d $full) { + push @dfiles,$full; + } else { + push @ufiles,$full; + } + } + closedir(DIRE); + foreach my $dd (@dfiles) { + my @fl = &get_file_list($dd); + foreach my $ff (@fl) { + push @ufiles,$ff; + } + } + return @ufiles; +} + +sub filescan_scanner { + #File::Scan scanner + &debug("filescan: starting scan of directory \"$ENV{'TMPDIR'}\"..."); + + my ($start_filescan_time)=[gettimeofday]; + my $filescanner = File::Scan->new(); + my $has_virus = 0; + + my @fl = &get_file_list($ENV{'TMPDIR'}); + + foreach my $file (@fl) { + &debug("filescan: scanning $file..."); + if (my $vv = $filescanner->scan("$file")) { + &debug("filescan: There be a virus! ($vv)"); + $quarantine_description=$vv; $has_virus++; + $quarantine_event="FileScan:$vv"; + $description .= "\n--- File::Scan results --\n$vv FOUND"; + last; + } elsif ( my $ee = $filescanner->error()) { + &debug("File::Scan [Error] = $ee"); + &error_condition("FileScan: Error: $ee"); + last; + } elsif ( my $ss = $filescanner->suspicious()) { + &debug("File::Scan [suspicious]"); + last; + } + } + my ($filescan_time) = tv_interval ($start_filescan_time, [gettimeofday]); + &debug("filescan: finished scan of dir \"$ENV{'TMPDIR'}\" in $filescan_time secs"); + if ($has_virus) { + &reject_email("Rejected VIRUS ",$quarantine_description,33); + } +} diff -rubN qmail-scanner-1.24-orig/sub-log_msg.pl qmail-scanner-1.24/sub-log_msg.pl --- qmail-scanner-1.24-orig/sub-log_msg.pl Thu Sep 9 15:07:51 2004 +++ qmail-scanner-1.24/sub-log_msg.pl Tue Nov 9 08:39:25 2004 @@ -2,9 +2,7 @@ my($msgtype,$status,$elapsed_time,$msgsize,$frm,$recips,$subj,$msgid,$attachs)[EMAIL PROTECTED]; my ($msg,$file); - my $syslogtype='mail|info'; - if ($log_details eq "syslog") { $msgtype =~ s/\s/_/g; @@ -84,4 +82,30 @@ &debug("$msgtype: $msg"); } +sub reject_email { + my ($exit_string,$exit_info,$exit_code)[EMAIL PROTECTED]; + $exit_code=111 if (!$exit_code); + + # st: tell qmail-smtpd why the message is rejected, + # so it can be written to the log + warn "$V_HEADER-$VERSION: $exit_string $exit_info from $$ $remote_smtp_ip\n"; + &debug("r_e: $V_HEADER-$VERSION: $exit_string $exit_info"); + my $reason; + if ($exit_code == 32) { + $reason = "Spam"; + } + elsif ($exit_code == 33) { + $reason = "Virus:$exit_info"; + } + else { + $reason = "Policy"; + } + if ($tag_score !~ m/^:/) { $reason .= ':'; } + $reason .= $tag_score; + $elapsed_time = tv_interval ($start_time, [gettimeofday]); + &log_msg('qmail-scanner',$reason,$elapsed_time,$msg_size,$returnpath, + $recip,$headers{'subject'},$headers{$qsmsgid},''); + &cleanup; + exit $exit_code; +} diff -rubN qmail-scanner-1.24-orig/sub-spamassassin.pl qmail-scanner-1.24/sub-spamassassin.pl --- qmail-scanner-1.24-orig/sub-spamassassin.pl Mon Oct 18 18:58:17 2004 +++ qmail-scanner-1.24/sub-spamassassin.pl Tue Nov 9 16:21:01 2004 @@ -5,6 +5,11 @@ &debug("spamassassin: don't scan as RELAYCLIENT implies this was sent by a local user"); return; } + #Only run SA for real users + if ($returnpath eq "" && $headers{'from'} =~ m/mailer-daemon|postmaster/i) { + &debug("spamassassin: skipping message from POSTMASTER"); + return; + } #SpamAssassin client scanner my ($spamassassin_found,$spamassassin_status); my ($start_spamassassin_time)=[gettimeofday]; @@ -53,6 +58,7 @@ $sa_tag++; $sa_status=1 if ($1 eq "Yes"); $sa_score=$3;$sa_max=$4; + last; } } } @@ -67,10 +73,15 @@ } else { unlink("$scandir/$wmaildir/new/$file_id.spamc"); } - if ($sa_max > $sa_score || ($sa_score == 0)) { + if ($sa_score == 0) { + $tag_score .= "SA:0($sa_score/$sa_max):"; + } + elsif ($sa_max > $sa_score) { $tag_score .= "SA:0($sa_score/$sa_max):"; $sa_comment = "No, hits=$sa_score required=$sa_max" if ($sa_fast); - } else { + &debug("SA: ham message with $sa_score/$sa_max hits"); + } + else { $tag_score .= "SA:1($sa_score/$sa_max):"; $sa_comment = "Yes, hits=$sa_score required=$sa_max" if ($sa_fast); &debug("SA: yup, this smells like SPAM"); @@ -90,6 +101,21 @@ $stop_spamassassin_time=[gettimeofday]; $spamassassin_time = tv_interval ($start_spamassassin_time, $stop_spamassassin_time); &debug("spamassassin: finished scan of dir \"$ENV{'TMPDIR'}\" in $spamassassin_time secs"); + if ($sa_reject_score && ($sa_score > $sa_reject_score)) { + &debug("SA: rejecting message with $sa_score hits"); + &reject_email("Rejected SPAM",sprintf("%.2f/%.2f",$sa_score,$sa_max),32); + } + else { + my $tag = sprintf "%.3f/%.1f",$sa_score,$sa_max; + $spamc_subject=sprintf("[SPAM][%s]",$tag); + if ($sa_score > $sa_max) { + &debug("SA: spam message accepted with $tag"); + warn "$V_HEADER-$VERSION: Accepted SPAM $tag from $$ $remote_smtp_ip\n"; + } else { + &debug("SA: ham message accepted with $tag"); + warn "$V_HEADER-$VERSION: Accepted HAM $tag from $$ $remote_smtp_ip\n"; + } + } }