#------------------------------------------------------------------------------
# Check for banned files in ZIP files - may add considerable processing time
# especially for large ZIP files or with many members
#
# Version 2.1, Paul Murphy [pmurphy@ionixpharma.com], 5/3/04
# Thanks to Michal Jankowski [Michal.Jankowski@fuw.edu.pl] for base code

#define stuff for scanning inside ZIP files for bad files
my ($bad_zip_exts, $re);

# Important - don't allow ZIP files (or ARJ/BZP/TAR/JAR, etc) in ZIP files!
# Note that this is _NOT_ the default list for Mimedefang, as we're trying to prevent nested archives

$bad_exts = '(ade|adp|app|arj|asd|asf|asx|bas|bat|bzp|chm|cmd|com|cpl|crt|dll|exe|fxp|hlp|hta|hto|inf|ini|ins|isp|jar|jse?|lib|lnk|mdb|mde|msc|msi|msp|mst|ocx|pcd|pif|prg|reg|scr|sct|sh|shb|shs|sys|url|vb|vbe|vbs|vcs|vxd|wmd|wms|wmz|wsc|wsf|wsh|zip|\{[^\}]+\})';

$re = '\.' . $bad_zip_exts . '\.*([^-A-Za-z0-9_.,]|$)';

    if (lc($ext) =~ /\.zip$/) 	# it's a ZIP file
        {
        use Archive::Zip qw(:ERROR_CODES);	# complains about AZ_OK if error codes are not included
	my $path = $entity->bodyhandle->path;
	my $zip = Archive::Zip->new();
        Archive::Zip::setErrorHandler(sub {});	# catch STDERR stuff and discard
	if ($zip->read($path) == AZ_OK) # file is OK and can be read
          {
	  md_syslog('debug', "Scanning zip file, Path=$path");
	  my $tfname = Archive::Zip::tempFileName('.');
	  my @members = $zip->members();
	  foreach my $member (@members) 
            {
	    if (! $member->isDirectory()) # no sense worrying about folders
              {
	      my $file = $member->fileName();
	      $size = $member->uncompressedSize();  # check for DoS content
	      md_syslog('debug', "scanning ZIP member $file, size=$size");
	      if ($size > 50e6) # approx 50Mb
	        {
	        md_graphdefang_log('Archive member too big ', $file, $RelayAddr);
	        action_discard();
	        return;
	        }

	      if ($member->isEncrypted()) 	# confidential... or could be a virus
                {
	        md_syslog('debug', "Scanning Encrypted ZIP member #$file#, size=$size");
	        if ( (lc($file) =~ $re) || (lc($file) =~ $bad_zip_exts) ) 
                  {
		  md_graphdefang_log('Encrypted_badfile', $file, $RelayAddr);
		  action_notify_administrator("A file called $file was detected in an encrypted ZIP file attached to an incoming e-mail - quarantined.");
		  action_quarantine_entire_message("An encrypted ZIP attachment containing $file was removed from this document as it\nconstituted a security hazard.  If you require this document, please contact\nIT Support to arrange for it to be released.\n");
                  # drop it
		  action_discard();
		  return;
	          }  # bad extension
                else 
                  {
		  md_syslog('debug', "Scanning ZIP member $file, size=$size");
                  # check for bad files in the ZIP
		  if ( (lc($file) =~ $re) || (lc($file) =~ $bad_zip_exts) )
                    {
		    md_graphdefang_log('badfile', $file, $RelayAddr);
		    action_notify_administrator("A file called $file was detected in a ZIP file attached to an incoming e-mail - quarantined.");
		    action_quarantine_entire_message("A ZIP attachment containing $file was removed from this document as it\nconstituted a security hazard.  If you require this document, please contact\nIT Support to arrange for it to be released.\n");
		    action_discard();
		    return;
		    }
                  }
	        } # if encrypted
               else 
                 { #not encrypted - check names then virus check the contents
  	         if ( (lc($file) =~ $re) || (lc($file) =~ $bad_zip_exts) )  # bad file
                   {
		   md_graphdefang_log('badfile', $file, $RelayAddr);
		   action_notify_administrator("A file called $file was detected in a ZIP file attached to an incoming e-mail - quarantined.");
		   action_quarantine_entire_message("A ZIP attachment containing $file was removed from this document as it\nconstituted a security hazard.  If you require this document, please contact\nIT Support to arrange for it to be released.\n");
                   # drop it
		   action_discard();
		   return;
	           }  # bad extension
                 else 
                   {
	           $zip->extractMember($member, $tfname);  # let's have a look....
	           use File::Scan;
	           my $scanner = File::Scan->new;
	           my $virus = $scanner->scan($tfname);
	           unlink($tfname);
	           if ($virus) 
                     {
	 	     md_graphdefang_log('virus', $virus, $RelayAddr);
		     action_discard();
		     return;
	             }	# if virus
		   } # not bad file
	         }  # else not encrypted
	       } #if not dir
	     }  #foreach member
           }  #ZIP read OK
         else			# problems reading the file
           {
	   md_graphdefang_log('badzip', $file, $RelayAddr);
	   action_notify_administrator("A corrupt ZIP file was attached to an incoming e-mail - quarantined.");
	   action_quarantine_entire_message("A corrupt ZIP attachment was removed from this document as it\nconstituted a security hazard.  If you require this document, please contact\nIT Support to arrange for it to be released.\n");
           # drop it
	   action_discard();
	   return;
           }
         } # if zipfile
#------------------------------------------------------------------------------
