Hello,

amavis currently fails to handle 7z files with encrypted content.

To reproduce create a 7z file with encrypted content:

$ 7z a -ppass enc.7z /etc/hosts

This will result in amavisd logging the following error:

Dec  2 10:14:41 xxxxxxx amavis[16806]: (16806-01) (!!)collect_results from
[21779] (/usr/bin/7za): exit 2
\n7-Zip (A) [64] 9.20  Copyright (c) 1999-2010 Igor Pavlov
2010-11-18\np7zip Version 9.20 (locale=en_U
S.UTF-8,Utf16=on,HugeFiles=on,1 CPU)\n\nProcessing archive:
/var/spool/amavisd/tmp/amavis-20141202T1014
40-16806-dAeC3ffA/parts/p002\n\nExtracting  xxxxxxxxx\n\nEnter
password (will not be echoed) :Extr
acting  issue     Data Error in encrypted file. Wrong password?\n\nSub
items Errors: 1\n\n

Attached is a first attempt to make do_7zip() only extract unencrypted
content.

I still see 2 weak points in the patch:

 * make sure quoting is right for 7z
 * the file list may be to big for @ARGV

 Markus
diff --git a/amavisd b/amavisd
index f50a708..5496d17 100755
--- a/amavisd
+++ b/amavisd
@@ -31406,21 +31406,38 @@ sub do_7zip($$$;$) {
   eval {
     ($proc_fh,$pid) = run_command(undef, '&1', $archiver,
                                   'l', '-slt', "-w$tempdir/parts", '--', $fn);
-    my $ln; my($name,$size,$attr); my $entries_cnt = 0;
+    my @list;
+    my $ln; my($name,$size,$attr,$enc); my $entries_cnt = 0;
     for ($! = 0; defined($ln=$proc_fh->getline); $! = 0) {
       $last_line = $ln  if $ln !~ /^\s*$/;  # keep last nonempty line
       chomp($ln); local($1);
       if ($ln =~ /^\s*\z/) {
-        if (defined $name || defined $size) {
+	if( defined $attr && $attr =~ /^D/ ) {
+          do_log(5,'do_7zip: member: %s "%s", (skipped directory)', $attr,$name);
+	} elsif( defined $enc && defined $name ) {
+          do_log(5,'do_7zip: member: %s "%s", %s bytes (skipped encrypted)', $attr,$name,$size);
+          # make a phantom entry - carrying only name and attributes
+          my $parent_placement = $part->mime_placement;
+          my $newpart_obj =
+            Amavis::Unpackers::Part->new("$tempdir/parts",$part);
+          $newpart_obj->mime_placement("$parent_placement/$entries_cnt");
+          $newpart_obj->name_declared($name);
+          $newpart_obj->attributes_add('U','C');
+	} elsif (defined $name || defined $size) {
           do_log(5,'do_7zip: member: %s "%s", %s bytes', $attr,$name,$size);
           if ($entries_cnt++, $MAXFILES && $entries_cnt > $MAXFILES)
             { die "Maximum number of files ($MAXFILES) exceeded" }
-          if (defined $size && $size > 0) { $bytes += $size; $mem_cnt++ }
+          if ( defined $size && $size > 0) {
+            $name =~ s/"/\\$1/g;
+	    push( @list, '"'.untaint($name).'"' );
+            $bytes += $size; $mem_cnt++
+	  }
         }
-        undef $name; undef $size; undef $attr;
+        undef $name; undef $size; undef $attr; undef $enc;
       } elsif ($ln =~ /^Path = (.*)\z/s)     { $name = $1 }
       elsif ($ln =~ /^Size = ([0-9]+)\z/s)   { $size = $1 }
       elsif ($ln =~ /^Attributes = (.*)\z/s) { $attr = $1 }
+      elsif ($ln =~ /^Encrypted = \+\z/s) { $enc = 1 }
     }
     defined $ln || $! == 0 || $! == EAGAIN  or die "Error reading (1): $!";
     do_log(-1,"unexpected(do_7zip_1): %s",$!) if !defined($ln) && $! == EAGAIN;
@@ -31446,7 +31463,7 @@ sub do_7zip($$$;$) {
       consumed_bytes($bytes, 'do_7zip-pre', 1);  # pre-check on estimated size
       snmp_count("OpsDecBy\u${decompressor_name}");
       ($proc_fh,$pid) = run_command(undef, '&1', $archiver, 'x', '-bd', '-y',
-                       "-w$tempdir/parts", "-o$tempdir/parts/7zip", '--', $fn);
+                       "-w$tempdir/parts", "-o$tempdir/parts/7zip", '--', $fn, @list);
       collect_results($proc_fh,$pid,$archiver,16384,[0,1]);
       undef $proc_fh; undef $pid;
       my $errn = lstat("$tempdir/parts/7zip") ? 0 : 0+$!;

Reply via email to