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+$!;