*** ..\00\amavisd.orig Mon Aug 13 15:22:26 2001 --- amavisd Sat Jan 12 10:46:00 2002 *************** *** 455,478 **** # Quarantine the original email message? if ($TESTING ne "yes") { ! # Log Message-ID ! my $msgid = $entity->head->get("Resent-Message-ID"); ! my $resent = "resent-"; ! if ($msgid eq "") { ! $msgid = $entity->head->get("Message-ID"); ! $resent = ""; ! } ! chomp $msgid; ! do_log(1,"$resent" . "message-id=$msgid"); ! ! if ($virusbackup eq "yes") { ! $VIRUSFILE = "virus-" . strftime("%Y%m%d-%H%M%S", localtime) . "-" . "$$"; ! `mv $TEMPDIR/email.txt $QUARANTINE/$VIRUSFILE`; ! ! do_log(0,"Virus found - quarantined as $VIRUSFILE"); ! } else { ! do_log(0,"Virus found - not quarantined"); ! } # Then we send email warn_sender() if ($warnsender eq "yes"); --- 455,461 ---- # Quarantine the original email message? if ($TESTING ne "yes") { ! quarantine_message(); # Then we send email warn_sender() if ($warnsender eq "yes"); *************** *** 494,499 **** --- 477,505 ---- } } + + # Do quarantine message + # + sub quarantine_message { + # Log Message-ID + my $msgid = $entity->head->get("Resent-Message-ID"); + my $resent = "resent-"; + if ($msgid eq "") { + $msgid = $entity->head->get("Message-ID"); + $resent = ""; + } + chomp $msgid; + do_log(1,"$resent" . "message-id=$msgid"); + + if ($virusbackup eq "yes") { + $VIRUSFILE = "virus-" . strftime("%Y%m%d-%H%M%S", localtime) . "-" . "$$"; + `mv $TEMPDIR/email.txt $QUARANTINE/$VIRUSFILE`; + do_log(0,"Virus found - quarantined as $VIRUSFILE"); + } else { + do_log(0,"Virus found - not quarantined"); + } + } + # # Notify sender sub warn_sender() { *************** *** 689,698 **** --- 695,829 ---- } } + + #-------------------------------- + # Some anti-DOS-attacks routines + #-------------------------------- + + # + # Returns the size of all files already decompressed in the temporary dir. + sub get_parts_size { + my ($part_path, $part_total_bytes); + opendir(DIR, "$TEMPDIR/parts") || return 0; # If can't open dir, there's something REALLY wrong going on. Someone somewhere else will catch this error... + foreach(readdir(DIR)) { + $part_path = "$TEMPDIR/parts/$_"; + #next if (-d $part_path); # Don't bother to check if is dir (-s will return "0" anyway!) + $part_total_bytes+= -s $part_path; + } + closedir DIR; + return $part_total_bytes; + } + + + # + # Sends an e-mail to the "virusmaster" + sub warn_virusmaster(@) { + my ($message) = shift; + + $mailto='krempel@mpc.com.br'; # ------ capar isto quando mudar o script que gera estatisticas!!! + + open(MAIL, "|$sendmail_wrapper $sendmail_wrapper_args -f$mailfrom") || do_exit($REGERR, __LINE__); + $SENDER = "(empty address)" if ($SENDER eq "<>"); + print MAIL <<"EOF"; + From: $mailfrom + To: $mailto + Subject: COULDN'T SCAN MAIL from $SENDER + + Couldn't scan email from: + + $SENDER + + To: + + EOF + foreach (@RECIPS) { + print MAIL "-> $_\n"; + } + if ($virusbackup eq "yes") { + print MAIL <<"EOF"; + + Because: + + $message + + The message has been quarantined as: + + $QUARANTINE/$VIRUSFILE + EOF + } + print MAIL <<"EOF"; + + Here are the headers: + + ------------------------- BEGIN HEADERS ----------------------------- + EOF + $entity->print_header(\*MAIL); + print MAIL <<"EOF"; + -------------------------- END HEADERS ------------------------------ + + EOF + close(MAIL); + } + + + # Format an integer: 12345 -> 12.345 + # + sub format_int(@) { + my $string = $_[0]; + $string = reverse $string; + while ($string =~ s/(\d{3})(\d)/$1\.$2/) {}; + $string = reverse $string; + return $string; + } + + + # + # QUIT the program if we are dealing with a DOS attack that fills up hd space + sub check_size_limit(@) { + + my $max_bytes_per_message = 1024*1024*600; # This should go to the configuration file ! ! ! + my $temp_mount = '/usr'; # This should go to the configuration file ! ! ! + my $min_free_kbytes = 1024*600; # This should go to the configuration file ! ! ! + + my ($part_total_bytes, $bytes_to_decompress, $part) = @_; + if ($part_total_bytes + $bytes_to_decompress > $max_bytes_per_message) { + my $message = 'Suspicious attachment (maybe a DOS attack): '.$part."\n". + 'This attach would expand to '.format_int($bytes_to_decompress).' bytes'."\n". + 'Total attachs would be '.format_int($part_total_bytes+$bytes_to_decompress).' bytes'."\n". + 'Current limit is '.format_int($max_bytes_per_message).' bytes'; + do_log(0, $message); + quarantine_message(); + warn_virusmaster($message); + if ($TESTING ne "yes") + { do_exit($VIRUSERR, __LINE__); } + else { do_exit(0, __LINE__); } + } + + my @partitions = `/bin/df -k`; # This should go to the configuration file ! ! ! + shift @partitions; + foreach (@partitions) { + my ($partition, undef, undef, $kbytes_available, undef, $mount) = split /\%?\s+/; + if ($mount eq $temp_mount && $kbytes_available < $min_free_kbytes) { + my $message = 'WARNING!!! Very low free disk space: '.$kbytes_available. + ' Kbytes available in partition "'.$partition.'" ('.$mount.')'; + do_log(0, $message); + #quarantine_message(); + warn_virusmaster($message); + if ($TESTING ne "yes") + { do_exit($REGERR, __LINE__); } + else { do_exit(0, __LINE__); } + } + } + + } + + + # # Decompose the parts sub decompose_part(@) { my $part = shift; + my $part_total_bytes; # $part should be safe because we generated the filenames ourselves # but let's be extra paranoid (and make taint happy) *************** *** 757,763 **** my $gz = gzopen("$TEMPDIR/parts/$part", "rb") || do_exit($REGERR, __LINE__); open(OUTPART, ">$newpart") || do_exit($REGERR, __LINE__); ! while ($gz->gzread($buffer) > 0) { print(OUTPART $buffer); } close(OUTPART); --- 888,898 ---- my $gz = gzopen("$TEMPDIR/parts/$part", "rb") || do_exit($REGERR, __LINE__); open(OUTPART, ">$newpart") || do_exit($REGERR, __LINE__); ! $part_total_bytes = get_parts_size(); ! my ($bytes_read, $bytes_to_decompress) = (0,0); ! while ( ($bytes_read = $gz->gzread($buffer)) > 0) { ! $bytes_to_decompress += $bytes_read; ! check_size_limit($part_total_bytes, $bytes_to_decompress, $part); print(OUTPART $buffer); } close(OUTPART); *************** *** 777,785 **** --- 912,924 ---- if ($uncompress ne "" && $filetype =~ /compress'd/i) { do_log(4,"Uncompressing $part"); + check_size_limit(get_parts_size(), 0, $part); + my $newpart = "$TEMPDIR/parts/" . getfilename(); system("$uncompress < $TEMPDIR/parts/$part > $newpart"); + check_size_limit(get_parts_size(), 0, $part); + if ($? != 0) { unlink($newpart); return 0; *************** *** 796,804 **** --- 935,947 ---- if ($bunzip ne "" && $filetype =~ /bzip2 compressed/i) { do_log(4,"Expanding bzip2 archive $part"); + check_size_limit(get_parts_size(), 0, $part); + my $newpart = "$TEMPDIR/parts/" . getfilename(); system("$bunzip < $TEMPDIR/parts/$part > $newpart"); + check_size_limit(get_parts_size(), 0, $part); + if ($? != 0) { unlink($newpart); return 0; *************** *** 1025,1030 **** --- 1168,1179 ---- return 0 if ($ziperr != AZ_OK); + my $bytes_to_decompress = 0; + foreach ($zip->members()) { + $bytes_to_decompress += $_->uncompressedSize; + } + check_size_limit(get_parts_size(), $bytes_to_decompress, $part); + do_log(4,"Unzipping $part"); foreach ($zip->members()) { *************** *** 1065,1070 **** --- 1214,1225 ---- s/^ //; push(@list, $_); } + $_ = ; + chomp; + if (/^\s+\d+\s+(\d+)/) { + my $bytes_to_decompress = $1; + check_size_limit(get_parts_size(), $bytes_to_decompress, $part); + } close(INPART); foreach (@list) { *************** *** 1082,1104 **** sub do_lha(@) { my $part = shift; - my @list; open(INPART, "$lha lq $TEMPDIR/parts/$part|"); while() { chop; my @vals = split(/\ \ */); ! ! if ($vals[0] =~ /DOS/) { ! $_ = $vals[6]; ! } else { ! $_ = $vals[7]; ! } push(@list, $_); } close(INPART); foreach (@list) { unless (/\/$/) { # Ignore directories my $newpart = "$TEMPDIR/parts/" . getfilename(); --- 1237,1257 ---- sub do_lha(@) { my $part = shift; my @list; + my $bytes_to_decompress = 0; open(INPART, "$lha lq $TEMPDIR/parts/$part|"); while() { chop; my @vals = split(/\ \ */); ! $_ = $vals[$#vals]; ! $bytes_to_decompress += $vals[$#vals -5]; push(@list, $_); } close(INPART); + check_size_limit(get_parts_size(), $bytes_to_decompress, $part); + foreach (@list) { unless (/\/$/) { # Ignore directories my $newpart = "$TEMPDIR/parts/" . getfilename(); *************** *** 1115,1120 **** --- 1268,1282 ---- sub do_arc(@) { my $part = shift; + open(INPART, "$arc l $TEMPDIR/parts/$part|"); + while() { + if (/^Total\s+\d+\s+(\d+)/) { + my $bytes_to_decompress = $1; + check_size_limit(get_parts_size(), $bytes_to_decompress, $part); + } + } + close(INPART); + my @list = `$arc ln $TEMPDIR/parts/$part`; chop @list; *************** *** 1132,1141 **** --- 1294,1320 ---- sub do_zoo(@) { my $part = shift; + my $marks_found; # Zoo needs extension of .zoo! symlink("$TEMPDIR/parts/$part", "$TEMPDIR/parts/$part.zoo"); + my @complete_list = `$zoo l $TEMPDIR/parts/$part`; + foreach (@complete_list) { + chomp; + if (/^--------.*/) { + $marks_found++; + next; + } + if ($marks_found >= 2) { + if (/^\s+\d+\s+\d+\%\s+(\d+)/) { + my $bytes_to_decompress = $1; + check_size_limit(get_parts_size(), $bytes_to_decompress, $part); + } + last; + } + } + my @list = `$zoo lf1q $TEMPDIR/parts/$part`; chop @list; *************** *** 1155,1163 **** --- 1334,1359 ---- sub do_unarj(@) { my $part = shift; + my $marks_found; # unarj needs extension of .arj! symlink("$TEMPDIR/parts/$part", "$TEMPDIR/parts/$part.arj"); + + my @complete_list = `$unarj l $TEMPDIR/parts/$part`; + foreach (@complete_list) { + chomp; + if (/^--------.*/) { + $marks_found++; + next; + } + if ($marks_found >= 2) { + if (/^\s+\d+\s+\d+\%\s+(\d+)/) { + my $bytes_to_decompress = $1; + check_size_limit(get_parts_size(), $bytes_to_decompress, $part); + last; + } + } + } # unarj has very limited extraction options! This may not be secure! mkdir("$TEMPDIR/arj", 0700);