diff -ur amavis-ng-0.1.3/AMAVIS/AV/AVP.pm amavis-ng-0.1.3-sg1/AMAVIS/AV/AVP.pm --- amavis-ng-0.1.3/AMAVIS/AV/AVP.pm Tue Apr 30 10:21:33 2002 +++ amavis-ng-0.1.3-sg1/AMAVIS/AV/AVP.pm Sat Jun 15 04:05:49 2002 @@ -15,6 +15,7 @@ use vars qw( $cfg_avp_binary + $cfg_chroot ); sub init { @@ -22,11 +23,12 @@ my $args = shift; push @{$$args{'virus_scanners'}}, 'Kaspersky AntiViral Toolkit Pro'; $cfg_avp_binary = $AMAVIS::cfg->val('AVP', 'avp'); + $cfg_chroot = $AMAVIS::cfg->val('security','chroot') || ""; if ($cfg_avp_binary eq '') { writelog($args,LOG_CRIT, "Path to AVP not specified"); return 0; } - if (! -x $cfg_avp_binary) { + if (! -x $cfg_chroot.$cfg_avp_binary) { writelog($args,LOG_CRIT, "AVP not executable"); return 0; } diff -ur amavis-ng-0.1.3/AMAVIS/AV/NAI.pm amavis-ng-0.1.3-sg1/AMAVIS/AV/NAI.pm --- amavis-ng-0.1.3/AMAVIS/AV/NAI.pm Tue Apr 30 10:21:33 2002 +++ amavis-ng-0.1.3-sg1/AMAVIS/AV/NAI.pm Sat Jun 15 04:05:49 2002 @@ -15,18 +15,21 @@ use vars qw( $cfg_uvscan_binary + $cfg_chroot ); sub init { my $self = shift; my $args = shift; + push @{$$args{'virus_scanners'}}, 'NAI VirusScan'; $cfg_uvscan_binary = $AMAVIS::cfg->val('NAI', 'uvscan'); + $cfg_chroot = $AMAVIS::cfg->val('security','chroot') || ""; if ($cfg_uvscan_binary eq '') { writelog($args,LOG_CRIT, "Path to uvscan not specified"); return 0; } - if (! -x $cfg_uvscan_binary) { + if (! -x $cfg_chroot.$cfg_uvscan_binary) { writelog($args,LOG_CRIT, "uvscan ($cfg_uvscan_binary) not executable"); return 0; } @@ -50,14 +53,25 @@ '--manalyze', '--panalyze', '--ignore-compressed', - "$$args{directory}/parts"); + "$$args{directory}/parts") + or do { + writelog($args,LOG_ERR, __PACKAGE__.": Error running program - $!"); + return 0; + }; + while(<$output_handle>) { chomp; s/[\000-\037]*//g; writelog($args,LOG_DEBUG,$_); push @output, $_; } - wait; + $output_handle->close() + or do { + if ($!) { + writelog($args,LOG_ERR, __PACKAGE__.": Error closing program - $!"); + return 0; + }; + }; my $errval = ($? >> 8); writelog($args,LOG_DEBUG,"Return code $errval"); diff -ur amavis-ng-0.1.3/AMAVIS/Extract/ARC.pm amavis-ng-0.1.3-sg1/AMAVIS/Extract/ARC.pm --- amavis-ng-0.1.3/AMAVIS/Extract/ARC.pm Tue Apr 30 10:21:41 2002 +++ amavis-ng-0.1.3-sg1/AMAVIS/Extract/ARC.pm Sat Jun 15 04:05:49 2002 @@ -18,9 +18,13 @@ ); sub init { - my $self = shift; + my $class = shift; my $args = shift; my $types = shift; + + my $self = {}; + bless $self, $class; + $cfg_arc_binary=$AMAVIS::cfg->val('external', 'arc'); if (! -x $cfg_arc_binary) { writelog($args,LOG_CRIT, __PACKAGE__. @@ -37,6 +41,8 @@ my $args = shift; my $filename = shift; my $unpacked_size = 0; + + $self->clearerr(); writelog($args,LOG_DEBUG, "Attempting to unpack $filename as ARC file"); @@ -44,24 +50,44 @@ my $len; # Determine contents of archive... + $! = 0; my $output_handle = cmd_pipe($args, $cfg_arc_binary, 'ln', - "$$args{'directory'}/parts/$filename"); + "$$args{'directory'}/parts/$filename") + or do { + if ($!) + { + return $self->reterr($args,0,"Error running unpacker - $!"); + } + else + { + return $self->reterr($args,0,"Unpacker exited with code $?","perm"); + } + }; my @list; while (<$output_handle>) { chop; push @list, $_; } - $output_handle->close(); - + $output_handle->close() + or do { + if ($!) + { + return $self->reterr($args,0,"Error running unpacker - $!"); + } + else + { + return $self->reterr($args,0,"Unpacker exited with code $?","perm"); + } + }; + # Extract each file through a pipe. Slower but more secure. # FIXME: What about shell metacharacters and such? foreach (@list) { unless (/.*\/$/) { # Ignore directories if ($$args{'unpacked_files'}++ > $cfg_maxfiles) { - writelog($args,LOG_ERR, __PACKAGE__.": Unpacking uses too many files"); - return 0; + return $self->reterr($args,0,"Unpacking uses too many files","perm"); } my $in_handle=cmd_pipe($args, $cfg_arc_binary, 'p', @@ -75,16 +101,69 @@ if ($$args{'unpacked_size'} + $unpacked_size >= $cfg_maxspace) { $in_handle->close(); $out_handle->close(); - writelog($args,LOG_ERR, __PACKAGE__.": Unpacking takes too much space"); - return 0; + return $self->reterr($args,0,"Unpacking takes too much space","perm"); } } - $in_handle->close(); - $out_handle->close(); - $ {$$args{'contents'}}{$securename} = {'original_filename' => $_}; + $in_handle->close() + or do { + if ($!) + { + return $self->reterr($args,0,"Error running unpacker - $!"); + } + else + { + return $self->reterr($args,0,"Unpacker exited with code $?","perm"); + } + }; + $out_handle->close() or do { + if ($!) + { + return $self->reterr($args,0,"Error running unpacker - $!"); + } + else + { + return $self->reterr($args,0,"Unpacker exited with code $?","perm"); + } + }; + ${$$args{'contents'}}{$securename} = {'original_filename' => $_}; } } return 1; } +sub reterr +{ + my($self,$args,$val,$err,$flags)=@_; + + $self->doerr($args,$err,$flags); + return $val; +} + +sub dieerr +{ + my($self,$args,$err,$flags) = @_; + + die $self->doerr($args,$err,$flags)."\n"; +} + +sub doerr +{ + my($self,$args,$err,$flags) = @_; + + $err = "Error unpacking ARC compressed file: $err"; + writelog($args,LOG_ERR, __PACKAGE__.$err); + $self->{lasterror}=$err; + if ($flags && ($flags eq 'perm')) + { + $self->{permerror}=1; + } +} + +sub clearerr +{ + my($self) = @_; + delete $self->{lasterror}; + delete $self->{permerror}; +} + 1; diff -ur amavis-ng-0.1.3/AMAVIS/Extract/ARJ.pm amavis-ng-0.1.3-sg1/AMAVIS/Extract/ARJ.pm --- amavis-ng-0.1.3/AMAVIS/Extract/ARJ.pm Tue Apr 30 10:21:41 2002 +++ amavis-ng-0.1.3-sg1/AMAVIS/Extract/ARJ.pm Sat Jun 15 04:05:49 2002 @@ -13,9 +13,13 @@ ); sub init { - my $self = shift; + my $class = shift; my $args = shift; my $types = shift; + + my $self = {}; + bless $self, $class; + $cfg_unarj_binary=$AMAVIS::cfg->val('external', 'unarj'); if (! -x $cfg_unarj_binary) { writelog($args,LOG_CRIT,__PACKAGE__. @@ -32,6 +36,8 @@ my $args = shift; my $filename = shift; + $self->clearerr(); + # Work around DOSism braindamage in unarj. if ($filename !~ /.arj$/) { $filename.='.'; @@ -43,8 +49,20 @@ writelog($args,LOG_DEBUG, "Attempting to unpack $filename as ARJ file"); + $! = 0; my $output_handle = cmd_pipe($args, $cfg_unarj_binary, 'l', - "$$args{'directory'}/parts/$filename"); + "$$args{'directory'}/parts/$filename") + or do { + if ($!) + { + return $self->reterr($args,0,"Error running unpacker - $!"); + } + else + { + return $self->reterr($args,0,"Unpacker exited with code $?","perm"); + } + }; + # Skip over while(<$output_handle>) { last if (/^----.*----$/); @@ -58,12 +76,22 @@ } while(<$output_handle>) { } - wait; + $output_handle->close() + or do { + if ($!) + { + return $self->reterr($args,0,"Error running unpacker - $!"); + } + else + { + return $self->reterr($args,0,"Unpacker exited with code $?","perm"); + } + }; +# wait; foreach (@arj_filenames) { if ($$args{'unpacked_files'}++ > $cfg_maxfiles) { - writelog($args,LOG_ERR, __PACKAGE__.": Unpacking uses too many files"); - return 0; + return $self->reterr($args,0,"Unpacking uses too many files","perm"); } writelog($args,LOG_DEBUG, join(' ',$cfg_unarj_binary, 'pq', @@ -82,8 +110,7 @@ if ($$args{'unpacked_size'} + $unpacked_size >= $cfg_maxspace) { $in_handle->close(); $out_handle->close(); - writelog($args,LOG_ERR, __PACKAGE__.": Unpacking takes too much space"); - return 0; + return $self->reterr($args,0,"Unpacking takes too much space","perm"); } } $in_handle->close(); @@ -94,4 +121,39 @@ return 1; } +sub reterr +{ + my($self,$args,$val,$err,$flags)=@_; + + $self->doerr($args,$err,$flags); + return $val; +} + +sub dieerr +{ + my($self,$args,$err,$flags) = @_; + + die $self->doerr($args,$err,$flags)."\n"; +} + +sub doerr +{ + my($self,$args,$err,$flags) = @_; + + $err = "Error unpacking ARJ compressed file: $err"; + writelog($args,LOG_ERR, __PACKAGE__.$err); + $self->{lasterror}=$err; + if ($flags && ($flags eq 'perm')) + { + $self->{permerror}=1; + } +} + +sub clearerr +{ + my($self) = @_; + delete $self->{lasterror}; + delete $self->{permerror}; +} + 1; diff -ur amavis-ng-0.1.3/AMAVIS/Extract/BZIP2.pm amavis-ng-0.1.3-sg1/AMAVIS/Extract/BZIP2.pm --- amavis-ng-0.1.3/AMAVIS/Extract/BZIP2.pm Tue Apr 30 10:21:41 2002 +++ amavis-ng-0.1.3-sg1/AMAVIS/Extract/BZIP2.pm Sat Jun 15 04:05:49 2002 @@ -17,9 +17,13 @@ ); sub init { - my $self = shift; + my $class = shift; my $args = shift; my $types = shift; + + my $self = {}; + bless $self, $class; + $cfg_bzip2_binary=$AMAVIS::cfg->val('external', 'bzip2'); if (! -x $cfg_bzip2_binary) { writelog($args,LOG_CRIT, __PACKAGE__. @@ -38,44 +42,106 @@ my $securename=get_secure_filename($args); my $unpacked_size = 0; + $self->clearerr(); writelog($args,LOG_DEBUG, "Attempting to unpack $filename as bzip2 file"); if ($$args{'unpacked_files'}++ > $cfg_maxfiles) { - writelog($args,LOG_ERR, __PACKAGE__.": Unpacking uses too many files"); - return 0; + return $self->reterr($args,0,"Unpacking uses too many files","perm"); } + $! = 0; my $in_handle = cmd_pipe($args, $cfg_bzip2_binary, '-cd', - "$$args{'directory'}/parts/$filename"); - unless (defined $in_handle) { - writelog($args,LOG_ERR, __PACKAGE__.": Error extracting file"); - return 0; - } + "$$args{'directory'}/parts/$filename") + or do { + if ($!) + { + return $self->reterr($args,0,"Error running unpacker - $!"); + } + else + { + return $self->reterr($args,0,"Unpacker exited with code $?","perm"); + } + }; + my $out_handle=IO::File->new(">$$args{'directory'}/parts/" .$securename); unless (defined $out_handle) { - writelog($args,LOG_ERR, __PACKAGE__.": Error extracting file"); - return 0; + return $self->reterr($args,0,"Error opening output file"); } while ((my $len=$in_handle->read(my $buffer, 4096)) > 0) { - $out_handle->write($buffer, $len); + $out_handle->write($buffer, $len) + or return $self->reterr($args,0,"write error - $!"); $unpacked_size += $len; if ($$args{'unpacked_size'} + $unpacked_size > $cfg_maxspace) { $in_handle->close(); $out_handle->close(); - writelog($args,LOG_ERR, __PACKAGE__.": Unpacking takes too much space"); - return 0; + return $self->reterr($args,0,"Unpacking takes too much space","perm"); } } - $in_handle->close(); - $out_handle->close(); + $in_handle->close() + or do { + if ($!) + { + return $self->reterr($args,0,"Error running unpacker - $!"); + } + else + { + return $self->reterr($args,0,"Unpacker exited with code $@","perm"); + } + }; + $out_handle->close() + or do { + if ($!) + { + return $self->reterr($args,0,"Error running unpacker - $!"); + } + else + { + return $self->reterr($args,0,"Unpacker exited with code $@","perm"); + } + }; $ {$$args{'contents'}}{$securename} = {}; $$args{'unpacked_size'} += $unpacked_size; return 1; } +sub reterr +{ + my($self,$args,$val,$err,$flags)=@_; + + $self->doerr($args,$err,$flags); + return $val; +} + +sub dieerr +{ + my($self,$args,$err,$flags) = @_; + + die $self->doerr($args,$err,$flags)."\n"; +} + +sub doerr +{ + my($self,$args,$err,$flags) = @_; + + $err = "Error unpacking BZIP2 compressed file: $err"; + writelog($args,LOG_ERR, __PACKAGE__.$err); + $self->{lasterror}=$err; + if ($flags && ($flags eq 'perm')) + { + $self->{permerror}=1; + } +} + +sub clearerr +{ + my($self) = @_; + delete $self->{lasterror}; + delete $self->{permerror}; +} + 1; diff -ur amavis-ng-0.1.3/AMAVIS/Extract/GZIP.pm amavis-ng-0.1.3-sg1/AMAVIS/Extract/GZIP.pm --- amavis-ng-0.1.3/AMAVIS/Extract/GZIP.pm Tue Apr 30 10:21:41 2002 +++ amavis-ng-0.1.3-sg1/AMAVIS/Extract/GZIP.pm Sat Jun 15 04:05:49 2002 @@ -22,9 +22,13 @@ use Compress::Zlib; sub init { - my $self = shift; + my $class = shift; my $args = shift; my $types = shift; + + my $self = {}; + bless $self,$class; + $$types{'application/x-gzip'}=$self; # Ummm, hopefully Zlib doesn't get confused by compress'd files. $$types{'application/x-compressed'}=$self; @@ -39,42 +43,107 @@ my $securename=get_secure_filename($args); my $unpacked_size = 0; + $self->clearerr(); + $! = 0; + writelog($args,LOG_DEBUG, "Attempting to unpack $filename as gzipped file"); if ($$args{'unpacked_files'}++ > $cfg_maxfiles) { - writelog($args,LOG_ERR, __PACKAGE__.": Unpacking uses too many files"); - return 0; + return $self->reterr($args,0,"Unpacking uses too many files","perm"); } my $gz = gzopen("$$args{'directory'}/parts/$filename", "rb") or do { - writelog($args,LOG_ERR, "Unable to open .gz file. GZERRNO=$gzerrno"); - return 0; + return $self->reterr($args,0,"Unable to open .gz file. GZERRNO=$gzerrno"); }; my $out_handle=IO::File->new(">$$args{'directory'}/parts/" - .$securename); + .$securename) + or do { + return $self->reterr($args,0,"Unable to create new part - $!","perm"); + }; while ((my $len = $gz->gzread(my $buffer, 4096)) > 0) { - $out_handle->write($buffer, $len); + $out_handle->write($buffer, $len) + or last; $unpacked_size += $len; if ($$args{'unpacked_size'} + $unpacked_size >= $cfg_maxspace) { $out_handle->close(); - writelog($args,LOG_ERR, __PACKAGE__.": Unpacking takes too much space"); - return 0; + return $self->reterr($args,0,"Unpacking takes too much space","perm"); } } - $out_handle->close(); - if ($gzerrno != Z_STREAM_END) { - writelog($args,LOG_ERR, "Error reading from $filename: $gzerrno. ".($gzerrno+0)); - $gz->gzclose(); - return 0; + $out_handle->close() + or do { + if ($!) + { + return $self->reterr($args,0,"Error running unpacker - $!"); + } + else + { + return $self->reterr($args,0,"Unpacker exited with code $?","perm"); + } + }; + + if ($gz->gzerror() != Z_STREAM_END) { + my $gzerrnum = $gz->gzerror() + 0; + my $gzerrmsg = $gz->gzerror(); + $gz->gzclose(); + + if ( ($gzerrnum == Z_STREAM_ERROR) or ($gzerrnum == Z_DATA_ERROR) ) + { + return $self->reterr($args,0, "Error decoding GZIP file - Zlib error $gzerrmsg"); + } + elsif ($gzerrnum == Z_ERRNO) + { + return $self->reterr($args,0,"Error decoding GZIP file - System error $!"); + } + else + { + return $self->reterr($args,0,"Error decoding GZIP file - Zlib error $gzerrmsg"); # " + } } $gz->gzclose(); + # Should we do error checking here? --sg $ {$$args{'contents'}}{$securename} = {}; $$args{'unpacked_size'} += $unpacked_size; return 1; } +sub reterr +{ + my($self,$args,$val,$err,$flags)=@_; + + $self->doerr($args,$err,$flags); + return $val; +} + +sub dieerr +{ + my($self,$args,$err,$flags) = @_; + + die $self->doerr($args,$err,$flags)."\n"; +} + +sub doerr +{ + my($self,$args,$err,$flags) = @_; + + $err = "Error unpacking GZIP compressed file: $err"; + writelog($args,LOG_ERR, __PACKAGE__.$err); + $self->{lasterror}=$err; + if ($flags && ($flags eq 'perm')) + { + $self->{permerror}=1; + } +} + +sub clearerr +{ + my($self) = @_; + delete $self->{lasterror}; + delete $self->{permerror}; +} + + 1; diff -ur amavis-ng-0.1.3/AMAVIS/Extract/LHA.pm amavis-ng-0.1.3-sg1/AMAVIS/Extract/LHA.pm --- amavis-ng-0.1.3/AMAVIS/Extract/LHA.pm Tue Apr 30 10:21:41 2002 +++ amavis-ng-0.1.3-sg1/AMAVIS/Extract/LHA.pm Sat Jun 15 04:05:49 2002 @@ -17,9 +17,13 @@ ); sub init { - my $self = shift; + my $class = shift; my $args = shift; my $types = shift; + + my $self = {}; + bless $self,$class; + $cfg_lha_binary=$AMAVIS::cfg->val('external', 'lha'); if (! -x $cfg_lha_binary) { writelog($args,LOG_CRIT, __PACKAGE__. @@ -38,47 +42,99 @@ my $unpacked_size = 0; my @lha_filenames; + $self->clearerr(); writelog($args,LOG_DEBUG, "Attempting to unpack $filename as LHA file"); # Determine contents of archive... # This is safe. No filenames specified from outside + $! = 0; my @list; my $output_handle = cmd_pipe($args, $cfg_lha_binary, 'lq', - "$$args{'directory'}/parts/$filename"); + "$$args{'directory'}/parts/$filename") + or do { + if ($!) + { + return $self->reterr($args,0,"Error running unpacker - $!"); + } + else + { + return $self->reterr($args,0,"Unpacker exited with code $?","perm"); + } + }; + while (<$output_handle>) { chop; my @vals = split(/\ \ */); # Put filenames into hash, leave directories out. push(@lha_filenames, $vals[7]) unless ($vals[7] =~ /.*\/$/); } - wait; + $output_handle->close() + or do { + if ($!) + { + return $self->reterr($args,0,"Error running unpacker - $!"); + } + else + { + return $self->reterr($args,0,"Unpacker exited with code $?","perm"); + } + }; +# wait; foreach (@lha_filenames) { if ($$args{'unpacked_files'}++ > $cfg_maxfiles) { - writelog($args,LOG_ERR, __PACKAGE__.": Unpacking uses too many files"); - return 0; + return $self->reterr($args,0,"Unpacking uses too many files","perm"); } my $in_handle=cmd_pipe($args, $cfg_lha_binary, 'pq', "$$args{'directory'}/parts/$filename", - $_); + $_) or do { + if ($!) + { + return $self->reterr($args,0,"Error running unpacker - $!"); + } + else + { + return $self->reterr($args,0,"Unpacker exited with code $?","perm"); + } + }; my $securename=get_secure_filename($args); my $out_handle=IO::File->new(">$$args{'directory'}/parts/" - .$securename); + .$securename) + or return $self->reterr($args,0,"Error opening part - $!\n"); while (my $len=$in_handle->read(my $buffer, 4096)) { $out_handle->write($buffer, $len); $unpacked_size += $len; if ($$args{'unpacked_size'} + $unpacked_size >= $cfg_maxspace) { $in_handle->close(); $out_handle->close(); - writelog($args,LOG_ERR, __PACKAGE__.": Unpacking takes too much space"); - return 0; + return $self->reterr($args,0,"Unpacking takes too much space","perm"); } } - $in_handle->close(); - $out_handle->close(); + $in_handle->close() + or do { + if ($!) + { + return $self->reterr($args,0,"Error running unpacker - $!"); + } + else + { + return $self->reterr($args,0,"Unpacker exited with code $?","perm"); + } + }; + $out_handle->close() + or do { + if ($!) + { + return $self->reterr($args,0,"Error running unpacker - $!"); + } + else + { + return $self->reterr($args,0,"Unpacker exited with code $?","perm"); + } + }; $ {$$args{'contents'}}{$securename} = {'original_filename' => $_}; } @@ -86,4 +142,39 @@ return 1; } +sub reterr +{ + my($self,$args,$val,$err,$flags)=@_; + + $self->doerr($args,$err,$flags); + return $val; +} + +sub dieerr +{ + my($self,$args,$err,$flags) = @_; + + die $self->doerr($args,$err,$flags)."\n"; +} + +sub doerr +{ + my($self,$args,$err,$flags) = @_; + + $err = "Error unpacking LHA compressed file: $err"; + writelog($args,LOG_ERR, __PACKAGE__.$err); + $self->{lasterror}=$err; + if ($flags && ($flags eq 'perm')) + { + $self->{permerror}=1; + } +} + +sub clearerr +{ + my($self) = @_; + delete $self->{lasterror}; + delete $self->{permerror}; +} + 1; diff -ur amavis-ng-0.1.3/AMAVIS/Extract/Mail.pm amavis-ng-0.1.3-sg1/AMAVIS/Extract/Mail.pm --- amavis-ng-0.1.3/AMAVIS/Extract/Mail.pm Tue Apr 30 10:21:41 2002 +++ amavis-ng-0.1.3-sg1/AMAVIS/Extract/Mail.pm Sat Jun 15 04:05:49 2002 @@ -15,16 +15,19 @@ use IO::File; sub init { - my $self = shift; + my $class = shift; my $args = shift; my $types = shift; + + my $self = {}; + bless $self,$class; + $$types{'message/rfc822'}=$self; $$types{'message/news'}=$self; writelog($args,LOG_DEBUG,__PACKAGE__." initialized."); return 1; } - sub extract { my $self=shift; my $args=shift; @@ -35,65 +38,155 @@ my $len; my $unpacked_size = 0; - my $parser = new MIME::Parser; + $self->clearerr(); - $parser->output_dir($$args{'directory'}); + eval { + my $parser = new MIME::Parser; - my $handle = IO::File->new("<$$args{'directory'}/parts/$filename"); + $parser->ignore_errors(0); + $parser->output_dir($$args{'directory'}); + + my $handle = IO::File->new("<$$args{'directory'}/parts/$filename") + or $self->dieerr($args,"Error opening part - $!"); + + + $parser->extract_nested_messages("REST"); + + $parser->ignore_errors(1); + my $entity = eval { $parser->parse($handle) }; + my $error = ($@ || $parser->last_error); + if ($error) { + my %errors = (); + writelog($args,LOG_ERR,"MIME::Parser error string begin"); + foreach my $errorstring (split /\n/, $error) { + if ($errorstring !~ /^ *$/) { + writelog($args,LOG_ERR,"$errorstring"); + $errors{$errorstring}++; + }; + } + writelog($args,LOG_ERR,"MIME::Parser error string end"); + + foreach (keys %errors) { + if ($_ !~ /^error:\s*unexpected end of header$/) { + # Return unsuccessfully + $self->dieerr($args,"Unexpected end of header","perm"); + } + } + writelog($args,LOG_ERR,"MIME::Parser error is not fatal, proceeding further"); + } + + # Have a look at all parts of $entity, starting with $entity itself + foreach my $part ($entity->parts_DFS()) { + writelog($args,LOG_DEBUG,"MIME parser recommended filename '".$part->head->recommended_filename."'"); + if (attachment_name_banned($args,$part->head->recommended_filename)) { + $self->dieerr($args,"MIME parser rejected banned filename","perm"); + } + + writelog($args,LOG_DEBUG,__PACKAGE__.": MIME filename passed inspection!"); + my $in_handle=$part->open('r') or next; + if ($$args{'unpacked_files'}++ > $cfg_maxfiles) { + $self->dieerr($args,"Unpacking uses too many files","perm"); + } + + # Write entity to file unless empty + my $securename=get_secure_filename($args); + my $out_handle=IO::File->new(">$$args{'directory'}/parts/" + .$securename) + or $self->dieerr($args,"Error opening part - $!"); + + while ($len=$in_handle->read($buffer, 4096)) { + $out_handle->write($buffer, $len) + or last; + $unpacked_size += $len; + if ($$args{'unpacked_size'} + $unpacked_size >= $cfg_maxspace) { + $in_handle->close(); + $out_handle->close(); + $self->dieerr($args,"Unpacking takes too much space"); + } + } + $in_handle->close() + or $self->dieerror($args,"Error closing MIME file: $!"); + + $out_handle->close() + or $self->dieerror($args,"Error closing MIME file: $!"); + + $ {$$args{'contents'}}{$securename} = {}; + } + $parser->filer->purge; + undef $parser; + }; + writelog($args,LOG_DEBUG, "Finished with eval ('$@')"); + + if ($@) { + writelog($args,LOG_ERR, __PACKAGE__.": MIME parsing returned error '$@'"); + return $self->reterr($args,0,$@); + } - $parser->extract_nested_messages("REST"); + $$args{'unpacked_size'} += $unpacked_size; + return 1; +} - $parser->ignore_errors(1); - my $entity = eval { $parser->parse($handle) }; - my $error = ($@ || $parser->last_error); - if ($error) { - my %errors = (); - writelog($args,LOG_ERR,"MIME::Parser error string begin"); - foreach my $errorstring (split /\n/, $error) { - if ($errorstring !~ /^ *$/) { - writelog($args,LOG_ERR,"$errorstring"); - $errors{$errorstring}++; - }; +use vars qw($extban); +sub attachment_name_banned +{ + my($args,$name)=@_; + + return 0 unless ($name); + + writelog($args,LOG_DEBUG,__PACKAGE__.": Attachment has filename '$name'"); + if (!defined($extban)) + { + my $banlist; + $extban = {}; + if ($banlist = $AMAVIS::cfg->val('filtering', 'ban_attachment_extensions') ) + { + map { $extban->{lc $_} = 1 } map { s/^\s+//; s/\s+$//; $_ } split(/,/,$banlist); + } + writelog($args,LOG_DEBUG,__PACKAGE__.": Built attachment ban list '$banlist'"); } - writelog($args,LOG_ERR,"MIME::Parser error string end"); - - foreach (keys %errors) { - if ($_ !~ /^error:\s*unexpected end of header$/) { - # Return unsuccessfully - return 0; - } + + if ($name =~ /\.([^\.]+)$/) + { + writelog($args,LOG_DEBUG,__PACKAGE__.": Attachment has extension '$1'"); + return $extban->{lc $1}; } - writelog($args,LOG_ERR,"MIME::Parser error is not fatal, proceeding further"); - } + return 0; +} - # Have a look at all parts of $entity, starting with $entity itself - foreach my $part ($entity->parts_DFS()) { - my $in_handle=$part->open('r') or next; - if ($$args{'unpacked_files'}++ > $cfg_maxfiles) { - writelog($args,LOG_ERR, __PACKAGE__.": Unpacking uses too many files"); - return 0; - } - # Write entity to file unless empty - my $securename=get_secure_filename($args); - my $out_handle=IO::File->new(">$$args{'directory'}/parts/" - .$securename); - while ($len=$in_handle->read($buffer, 4096)) { - $out_handle->write($buffer, $len); - $unpacked_size += $len; - if ($$args{'unpacked_size'} + $unpacked_size >= $cfg_maxspace) { - $in_handle->close(); - $out_handle->close(); - writelog($args,LOG_ERR, __PACKAGE__.": Unpacking takes too much space"); - return 0; - } +sub reterr +{ + my($self,$args,$val,$err,$flags)=@_; + + $self->doerr($args,$err,$flags); + return $val; +} + +sub dieerr +{ + my($self,$args,$err,$flags) = @_; + + die $self->doerr($args,$err,$flags)."\n"; +} + +sub doerr +{ + my($self,$args,$err,$flags) = @_; + + $err = "Error unpacking Mail message: $err"; + writelog($args,LOG_ERR, __PACKAGE__.$err); + $self->{lasterror}=$err; + if ($flags && ($flags eq 'perm')) + { + $self->{permerror}=1; } - $in_handle->close(); - $out_handle->close(); - $ {$$args{'contents'}}{$securename} = {}; - } - $parser->filer->purge; - $$args{'unpacked_size'} += $unpacked_size; - return 1; +} + +sub clearerr +{ + my($self) = @_; + delete $self->{lasterror}; + delete $self->{permerror}; } 1; + diff -ur amavis-ng-0.1.3/AMAVIS/Extract/RAR.pm amavis-ng-0.1.3-sg1/AMAVIS/Extract/RAR.pm --- amavis-ng-0.1.3/AMAVIS/Extract/RAR.pm Tue Apr 30 10:21:41 2002 +++ amavis-ng-0.1.3-sg1/AMAVIS/Extract/RAR.pm Sat Jun 15 04:05:49 2002 @@ -17,9 +17,13 @@ ); sub init { - my $self = shift; + my $class = shift; my $args = shift; my $types = shift; + + my $self = {}; + bless $self,$class; + $cfg_unrar_binary=$AMAVIS::cfg->val('external', 'unrar'); if (! -x $cfg_unrar_binary) { writelog($args,LOG_CRIT, __PACKAGE__. @@ -40,13 +44,26 @@ my $len; my $unpacked_size = 0; + $self->clearerr(); writelog($args,LOG_DEBUG, "Attempting to unpack $filename as RAR file"); my @list; # Get listing + $! = 0; my $output_handle = cmd_pipe($args, $cfg_unrar_binary, 'v', - "$$args{'directory'}/parts/$filename"); + "$$args{'directory'}/parts/$filename") + or do { + if ($!) + { + return $self->reterr($args,0,"Error running unpacker - $!"); + } + else + { + return $self->reterr($args,0,"Unpacker exited with code $?","perm"); + } + }; + while(<$output_handle>) { last if /^------.*/; } @@ -59,37 +76,108 @@ s/^ //; push(@list, $_); } - wait; + $output_handle->close() + or do { + if ($!) + { + return $self->reterr($args,0,"Error running unpacker - $!"); + } + else + { + return $self->reterr($args,0,"Unpacker exited with code $?","perm"); + } + }; foreach (@list) { # Push every file through a pipe. if ($$args{'unpacked_files'}++ > $cfg_maxfiles) { - writelog($args,LOG_ERR, __PACKAGE__.": Unpacking uses too many files"); - return 0; + return $self->reterr($args,0,"Unpacking uses too many files","perm"); } my $in_handle=cmd_pipe($args, $cfg_unrar_binary, 'p', '-inul', "$$args{'directory'}/parts/$filename", - $_); + $_) + or do { + if ($!) + { + return $self->reterr($args,0,"Error running unpacker - $!"); + } + else + { + return $self->reterr($args,0,"Unpacker exited with code $?","perm"); + } + }; + my $securename=get_secure_filename($args); my $out_handle=IO::File->new(">$$args{'directory'}/parts/" - .$securename); + .$securename) + or do { + return $self->reterr($args,0,"Error opening part - $!"); + }; + while ($len=$in_handle->read($buffer, 4096)) { - $out_handle->write($buffer, $len); + $out_handle->write($buffer, $len) + or last; $unpacked_size += $len; if ($$args{'unpacked_size'} + $unpacked_size >= $cfg_maxspace) { $in_handle->close(); $out_handle->close(); - writelog($args,LOG_ERR, __PACKAGE__.": Unpacking takes too much space"); - return 0; + return $self->reterr($args,0,"Unpacking takes too much space","perm"); } } - $out_handle->close(); + $out_handle->close() + or do { + if ($!) + { + return $self->reterr($args,0,"Error running unpacker - $!"); + } + else + { + return $self->reterr($args,0,"Unpacker exited with code $?","perm"); + } + }; + $ {$$args{'contents'}}{$securename} = {'original_filename' => $_}; } $$args{'unpacked_size'} += $unpacked_size; return 1; } +sub reterr +{ + my($self,$args,$val,$err,$flags)=@_; + + $self->doerr($args,$err,$flags); + return $val; +} + +sub dieerr +{ + my($self,$args,$err,$flags) = @_; + + die $self->doerr($args,$err,$flags)."\n"; +} + +sub doerr +{ + my($self,$args,$err,$flags) = @_; + + $err = "Error unpacking RAR compressed file: $err"; + writelog($args,LOG_ERR, __PACKAGE__.$err); + $self->{lasterror}=$err; + if ($flags && ($flags eq 'perm')) + { + $self->{permerror}=1; + } +} + +sub clearerr +{ + my($self) = @_; + delete $self->{lasterror}; + delete $self->{permerror}; +} + + 1; diff -ur amavis-ng-0.1.3/AMAVIS/Extract/TNEF.pm amavis-ng-0.1.3-sg1/AMAVIS/Extract/TNEF.pm --- amavis-ng-0.1.3/AMAVIS/Extract/TNEF.pm Tue Apr 30 10:21:41 2002 +++ amavis-ng-0.1.3-sg1/AMAVIS/Extract/TNEF.pm Sat Jun 15 04:05:49 2002 @@ -16,9 +16,13 @@ use Convert::TNEF; sub init { - my $self = shift; + my $class = shift; my $args = shift; my $types = shift; + + my $self = {}; + bless $self,$class; + $$types{'application/ms-tnef'}=$self; writelog($args,LOG_DEBUG,__PACKAGE__." initialized."); return 1; @@ -32,11 +36,24 @@ writelog($args,LOG_DEBUG, "Attempting to unpack $filename as TNEF attachment"); + $! = 0; + $self->clearerr(); + my $tnef = Convert::TNEF->read_in("$$args{'directory'}/parts/$filename", - {ignore_checksum=>"true"}); + {ignore_checksum=>"true"}) + or do { + if ($!) + { + return $self->reterr($args,0,"Error running unpacker - $!"); + } + else + { + return $self->reterr($args,0,"Unpacker exited with code $@","perm"); + } + }; + my $counter=0; - if ($tnef) { # my $message=$tnef->message; # my $securename=get_secure_filename($args); # my $out_handle=IO::File->new(">$$args{'directory'}/parts/" @@ -52,42 +69,86 @@ # {'original_filename' => $attachment->name()}; # writelog($args,LOG_DEBUG,"TNEF Attachment # $counter"); - for my $attachment ($tnef->attachments) { + for my $attachment ($tnef->attachments) { ++$counter; if ($$args{'unpacked_files'}++ > $cfg_maxfiles) { - writelog($args,LOG_ERR, __PACKAGE__.": Unpacking uses too many files"); - return 0; + return $self->reterr($args,0,"Unpacking uses too many files","perm"); } writelog($args,LOG_DEBUG,"TNEF Attachment # $counter"); - - my $securename=get_secure_filename($args); + + my $securename=get_secure_filename($args); my $out_handle=IO::File->new(">$$args{'directory'}/parts/" - .$securename); - + .$securename) + or return $self->reterr($args,0,"Error opening attachment - $!"); + unless ($out_handle) { - writelog($args,LOG_ERR, __PACKAGE__.'Error writing file'); - next; + writelog($args,LOG_ERR, __PACKAGE__.'Error writing file'); + next; } # FIXME: Possible OOM DoS? $unpacked_size += length($attachment->data()); if ($$args{'unpacked_size'} + $unpacked_size >= $cfg_maxspace) { - $out_handle->close(); - writelog($args,LOG_ERR, __PACKAGE__.": Unpacking takes too much space"); - return 0; + $out_handle->close(); + return $self->reterr($args,0,"Unpacking takes too much space","perm"); } $out_handle->print($attachment->data()); - $out_handle->close(); + $out_handle->close() + or do { + if ($!) + { + return $self->reterr($args,0,"Error running unpacker - $!"); + } + else + { + return $self->reterr($args,0,"Unpacker exited with code $@","perm"); + } + }; + + $ {$$args{'contents'}}{$securename} = - {'original_filename' => $attachment->name()}; - } - $tnef->purge(); - } - else { - writelog($args,LOG_ERR, $Convert::TNEF::errstr); - return 0; + {'original_filename' => $attachment->name()}; } + $tnef->purge(); + $$args{'unpacked_size'} += $unpacked_size; return 1; } + +sub reterr +{ + my($self,$args,$val,$err,$flags)=@_; + + $self->doerr($args,$err,$flags); + return $val; +} + +sub dieerr +{ + my($self,$args,$err,$flags) = @_; + + die $self->doerr($args,$err,$flags)."\n"; +} + +sub doerr +{ + my($self,$args,$err,$flags) = @_; + + $err = "Error unpacking ARC compressed file: $err"; + writelog($args,LOG_ERR, __PACKAGE__.$err); + $self->{lasterror}=$err; + if ($flags && ($flags eq 'perm')) + { + $self->{permerror}=1; + } +} + +sub clearerr +{ + my($self) = @_; + delete $self->{lasterror}; + delete $self->{permerror}; +} + + 1; diff -ur amavis-ng-0.1.3/AMAVIS/Extract/Tar.pm amavis-ng-0.1.3-sg1/AMAVIS/Extract/Tar.pm --- amavis-ng-0.1.3/AMAVIS/Extract/Tar.pm Tue Apr 30 10:21:41 2002 +++ amavis-ng-0.1.3-sg1/AMAVIS/Extract/Tar.pm Sat Jun 15 04:05:49 2002 @@ -15,9 +15,13 @@ use Archive::Tar; sub init { - my $self = shift; + my $class = shift; my $args = shift; my $types = shift; + + my $self = {}; + bless $self,$class; + $$types{'application/x-tar'}=$self; $$types{'application/x-gtar'}=$self; writelog($args,LOG_DEBUG,__PACKAGE__." initialized."); @@ -30,40 +34,84 @@ my $filename = shift; my $unpacked_size = 0; + $self->clearerr(); writelog($args,LOG_DEBUG, "Attempting to unpack $filename as TAR file"); # my $tar = eval ( Archive::Tar->new("$$args{'directory'}/parts/$filename") ); + $! = 0; my $tar = Archive::Tar->new("$$args{'directory'}/parts/$filename"); unless (defined $tar) { - writelog($args,LOG_ERR, __PACKAGE__.": Corrupt tar file."); - return 0; + if ($!) + { + return $self->reterr($args,0,"Error running unpacker - $!"); + } + else + { + return $self->reterr($args,0,"Unpacker error: ".Archive::Tar::error(),"perm"); + } } - + my @list = $tar->list_files(); - + foreach (@list) { unless (/.*\/$/) { # Ignore directories if ($$args{'unpacked_files'}++ > $cfg_maxfiles) { - writelog($args,LOG_ERR, __PACKAGE__.": Unpacking uses too many files"); - return 0; + return $self->reterr($args,0,"Unpacking uses too many files","perm"); } my $data = $tar->get_content($_); my $securename=get_secure_filename($args); my $out_handle=IO::File->new(">$$args{'directory'}/parts/" - .$securename); + .$securename) + or return $self->reterr($args,0,"Error opening part - $!"); $unpacked_size+=length($data); if ($$args{'unpacked_size'} + $unpacked_size >= $cfg_maxspace) { $out_handle->close(); - writelog($args,LOG_ERR, __PACKAGE__.": Unpacking takes too much space"); - return 0; + return $self->reterr($args,0,"Unpacking takes too much space","perm"); } $out_handle->print($data); - $out_handle->close(); + $out_handle->close() + or return $self->reterr($args,0,"Error closing output file: $!"); $ {$$args{'contents'}}{$securename} = {}; } } + $$args{'unpacked_size'} += $unpacked_size; return 1; } +sub reterr +{ + my($self,$args,$val,$err,$flags)=@_; + + $self->doerr($args,$err,$flags); + return $val; +} + +sub dieerr +{ + my($self,$args,$err,$flags) = @_; + + die $self->doerr($args,$err,$flags)."\n"; +} + +sub doerr +{ + my($self,$args,$err,$flags) = @_; + + $err = "Error unpacking Tar archive file: $err"; + writelog($args,LOG_ERR, __PACKAGE__.$err); + $self->{lasterror}=$err; + if ($flags && ($flags eq 'perm')) + { + $self->{permerror}=1; + } +} + +sub clearerr +{ + my($self) = @_; + delete $self->{lasterror}; + delete $self->{permerror}; +} + 1; diff -ur amavis-ng-0.1.3/AMAVIS/Extract/Text.pm amavis-ng-0.1.3-sg1/AMAVIS/Extract/Text.pm --- amavis-ng-0.1.3/AMAVIS/Extract/Text.pm Tue Apr 30 10:21:41 2002 +++ amavis-ng-0.1.3-sg1/AMAVIS/Extract/Text.pm Sat Jun 15 04:05:49 2002 @@ -15,9 +15,13 @@ use Convert::UUlib ':all'; sub init { - my $self = shift; + my $class = shift; my $args = shift; my $types = shift; + + my $self = {}; + bless $self,$class; + $$types{'text/plain'}=$self; writelog($args,LOG_DEBUG,__PACKAGE__." initialized."); return 1; @@ -29,6 +33,7 @@ my $filename = shift; my $unpacked_size = 0; + $self->clearerr(); writelog($args,LOG_DEBUG, "Attempting to unpack $filename as text file"); my ($retval, $count) = LoadFile("$$args{'directory'}/parts/$filename"); @@ -43,21 +48,26 @@ for ($i = 0;$uu = GetFileListItem($i); $i++) { if ($uu->state & FILE_OK) { if ($$args{'unpacked_files'}++ > $cfg_maxfiles) { - writelog($args,LOG_ERR, __PACKAGE__.": Unpacking uses too many files"); - return 0; + return $self->reterr($args,0,"Unpacking uses too many files","perm"); } my $securename=get_secure_filename($args); writelog($args,LOG_DEBUG, "Extracting uuencoded data to $securename"); my $newpart = "$$args{'directory'}/parts/$securename"; $unpacked_size += $uu->size; if ($$args{'unpacked_size'} + $unpacked_size >= $cfg_maxspace) { - writelog($args,LOG_ERR, __PACKAGE__.": Unpacking takes too much space"); - return 0; + return $self->reterr($args,0,"Unpacking takes too much space","perm"); } + $! = 0; $uu->decode($newpart); if (!$uu->state || !FILE_OK || -z $newpart) { - writelog($args,LOG_ERR, "Convert::UUlib failed..."); - return 0; + if ($!) + { + return $self->reterr($args,0,"Error running unpacker - $!"); + } + else + { + return $self->reterr($args,0,"Unpacker exited with code ".$uu->state,"perm"); + } } $ {$$args{'contents'}}{$securename} = {}; } @@ -69,4 +79,41 @@ return 1; } +sub reterr +{ + my($self,$args,$val,$err,$flags)=@_; + + $self->doerr($args,$err,$flags); + return $val; +} + +sub dieerr +{ + my($self,$args,$err,$flags) = @_; + + die $self->doerr($args,$err,$flags)."\n"; +} + +sub doerr +{ + my($self,$args,$err,$flags) = @_; + + $err = "Error processing text file: $err"; + writelog($args,LOG_ERR, __PACKAGE__.$err); + $self->{lasterror}=$err; + if ($flags && ($flags eq 'perm')) + { + $self->{permerror}=1; + } +} + +sub clearerr +{ + my($self) = @_; + delete $self->{lasterror}; + delete $self->{permerror}; +} + + + 1; diff -ur amavis-ng-0.1.3/AMAVIS/Extract/ZOO.pm amavis-ng-0.1.3-sg1/AMAVIS/Extract/ZOO.pm --- amavis-ng-0.1.3/AMAVIS/Extract/ZOO.pm Tue Apr 30 10:21:41 2002 +++ amavis-ng-0.1.3-sg1/AMAVIS/Extract/ZOO.pm Sat Jun 15 04:05:49 2002 @@ -17,9 +17,13 @@ ); sub init { - my $self = shift; + my $class = shift; my $args = shift; my $types = shift; + + my $self = {}; + bless $self,$class; + $zoo_binary=$AMAVIS::cfg->val('external', 'zoo'); $$types{'application/x-zoo'}=$self; if (! -x $zoo_binary) { @@ -40,54 +44,145 @@ my $buffer; my $len; + $self->clearerr(); writelog($args,LOG_DEBUG, "Attempting to unpack $filename as ZOO file"); # Zoo needs extension of .zoo! symlink("$$args{'directory'}/parts/$filename", - "$$args{'directory'}/parts/$filename.zoo"); + "$$args{'directory'}/parts/$filename.zoo") + or return $self->reterr($args,0,"Error creating symlink - $!"); # This is safe. No filenames specified from outside my @list; + $! = 0; my $output_handle = cmd_pipe($args, $zoo_binary, 'lf1q', - "$$args{'directory'}/parts/$filename.zoo"); + "$$args{'directory'}/parts/$filename.zoo") + or do { + if ($!) + { + return $self->reterr($args,0,"Error running unpacker - $!"); + } + else + { + return $self->reterr($args,0,"Unpacker exited with code $?","perm"); + } + }; + while (<$output_handle>) { chop; push @list, $_; } + $output_handle->close() + or do { + if ($!) + { + return $self->reterr($args,0,"Error running unpacker - $!"); + } + else + { + return $self->reterr($args,0,"Unpacker exited with code $?","perm"); + } + }; foreach (@list) { # Ignore directories unless (/.*\/$/) { if ($$args{'unpacked_files'}++ > $cfg_maxfiles) { - writelog($args,LOG_ERR, __PACKAGE__.": Unpacking uses too many files"); - return 0; + return $self->reterr($args,0,"Unpacking uses too many files","perm"); } my $in_handle=cmd_pipe($args, $zoo_binary, 'xpqqq:', "$$args{'directory'}/parts/$filename.zoo", - $_); + $_) or do { + if ($!) + { + return $self->reterr($args,0,"Error running unpacker - $!"); + } + else + { + return $self->reterr($args,0,"Unpacker exited with code $?","perm"); + } + }; + my $securename=get_secure_filename($args); my $out_handle=IO::File->new(">$$args{'directory'}/parts/" - .$securename); + .$securename) + or return $self->reterr($args,0,"Error creating part - $!"); while ($len=$in_handle->read($buffer, 4096)) { - $out_handle->write($buffer, $len); + $out_handle->write($buffer, $len) + or return $self->reterr($args,0,"Write error - $!"); $unpacked_size += $len; if ($$args{'unpacked_size'} + $unpacked_size >= $cfg_maxspace) { $in_handle->close(); $out_handle->close(); - writelog($args,LOG_ERR, __PACKAGE__.": Unpacking takes too much space"); - return 0; + return $self->reterr($args,0,"Unpacking takes too much space","perm"); } } + $in_handle->close() or do { + if ($!) + { + return $self->reterr($args,0,"Error running unpacker - $!"); + } + else + { + return $self->reterr($args,0,"Unpacker exited with code $?","perm"); + } + }; + $out_handle->close() or do { + if ($!) + { + return $self->reterr($args,0,"Error running unpacker - $!"); + } + else + { + return $self->reterr($args,0,"Unpacker exited with code $?","perm"); + } + }; $ {$$args{'contents'}}{$securename} = {'original_filename' => $_}; } } - unlink("$$args{'directory'}/parts/$filename.zoo"); + unlink("$$args{'directory'}/parts/$filename.zoo") + or return $self->reterr($args,0,"Error removing symlink - $!"); $$args{'unpacked_size'} += $unpacked_size; return 1; } +sub reterr +{ + my($self,$args,$val,$err,$flags)=@_; + + $self->doerr($args,$err,$flags); + return $val; +} + +sub dieerr +{ + my($self,$args,$err,$flags) = @_; + + die $self->doerr($args,$err,$flags)."\n"; +} + +sub doerr +{ + my($self,$args,$err,$flags) = @_; + + $err = "Error unpacking ARC compressed file: $err"; + writelog($args,LOG_ERR, __PACKAGE__.$err); + $self->{lasterror}=$err; + if ($flags && ($flags eq 'perm')) + { + $self->{permerror}=1; + } +} + +sub clearerr +{ + my($self) = @_; + delete $self->{lasterror}; + delete $self->{permerror}; +} + 1; diff -ur amavis-ng-0.1.3/AMAVIS/Extract/Zip.pm amavis-ng-0.1.3-sg1/AMAVIS/Extract/Zip.pm --- amavis-ng-0.1.3/AMAVIS/Extract/Zip.pm Tue Apr 30 10:21:41 2002 +++ amavis-ng-0.1.3-sg1/AMAVIS/Extract/Zip.pm Sat Jun 15 04:05:49 2002 @@ -15,9 +15,13 @@ use Archive::Zip qw( :ERROR_CODES :CONSTANTS ); sub init { - my $self = shift; + my $class = shift; my $args = shift; my $types = shift; + + my $self = {}; + bless $self,$class; + $$types{'application/x-zip'}=$self; writelog($args,LOG_DEBUG,__PACKAGE__." initialized."); return 1; @@ -29,6 +33,7 @@ my $filename = shift; my $unpacked_size = 0; + $self->clearerr(); writelog($args,LOG_DEBUG, "Attempting to unpack $filename as Zip file"); my $ziperr; @@ -39,10 +44,17 @@ Archive::Zip::setErrorHandler(\&Carp::carp); $Carp::CarpLevel++; + $! = 0; if ($ziperr != AZ_OK) { - writelog($args,LOG_ERR, __PACKAGE__.": Error reading zip file"); - return 0; - } + if ($!) + { + return $self->reterr($args,0,"Error running unpacker - $!"); + } + else + { + return $self->reterr($args,0,"Unpacker exited with code $?","perm"); + } + }; foreach my $member ($zip->members()) { my $membername=$member->fileName(); @@ -51,8 +63,7 @@ # ignore encrypted files next if ($member->isEncrypted()); if ($$args{'unpacked_files'}++ > $cfg_maxfiles) { - writelog($args,LOG_ERR, __PACKAGE__.": Unpacking uses too many files"); - return 0; + return $self->reterr($args,0,"Unpacking uses too many files","perm"); } # We don't trust any of the filenames in the zip # archive and always use our own. @@ -60,16 +71,23 @@ $unpacked_size += $member->uncompressedSize(); if ($$args{'unpacked_size'} + $unpacked_size >= $cfg_maxspace) { - writelog($args,LOG_ERR, __PACKAGE__.": Unpacking takes too much space"); - return 0; + return $self->reterr($args,0,"Unpacking takes too much space","perm"); } - if ($member->extractToFileNamed("$$args{'directory'}/parts/$securename") + $! = 0; + if (my $e = $member->extractToFileNamed("$$args{'directory'}/parts/$securename") != AZ_OK) { - writelog($args,LOG_ERR, "Error writing file"); + if ($!) + { + return $self->reterr($args,0,"Error running unpacker - $!"); + } + else + { + return $self->reterr($args,0,"Unpacker exited with code $e","perm"); + } } else { - $ {$$args{'contents'}}{$securename} = {'original_filename' => + ${$$args{'contents'}}{$securename} = {'original_filename' => $membername}; } } @@ -85,4 +103,41 @@ return 5 } + +sub reterr +{ + my($self,$args,$val,$err,$flags)=@_; + + $self->doerr($args,$err,$flags); + return $val; +} + +sub dieerr +{ + my($self,$args,$err,$flags) = @_; + + die $self->doerr($args,$err,$flags)."\n"; +} + +sub doerr +{ + my($self,$args,$err,$flags) = @_; + + $err = "Error unpacking ARC compressed file: $err"; + writelog($args,LOG_ERR, __PACKAGE__.$err); + $self->{lasterror}=$err; + if ($flags && ($flags eq 'perm')) + { + $self->{permerror}=1; + } +} + +sub clearerr +{ + my($self) = @_; + delete $self->{lasterror}; + delete $self->{permerror}; +} + + 1; diff -ur amavis-ng-0.1.3/AMAVIS/Logging.pm amavis-ng-0.1.3-sg1/AMAVIS/Logging.pm --- amavis-ng-0.1.3/AMAVIS/Logging.pm Tue Apr 30 10:22:48 2002 +++ amavis-ng-0.1.3-sg1/AMAVIS/Logging.pm Sat Jun 15 04:05:49 2002 @@ -18,7 +18,16 @@ use vars qw($VERSION); $VERSION='0.1'; -use AMAVIS; +sub LOG_EMERG {0} +sub LOG_ALERT {1} +sub LOG_CRIT {2} +sub LOG_ERR {3} +sub LOG_WARNING {4} +sub LOG_NOTICE {5} +sub LOG_INFO {6} +sub LOG_DEBUG {7} + +# use AMAVIS; use POSIX qw(strftime geteuid setuid uname setsid); use POSIX ":sys_wait_h"; use Fcntl qw(:flock); @@ -33,15 +42,6 @@ @AMAVIS::Logging::EXPORT = qw( LOG_EMERG LOG_ALERT LOG_CRIT LOG_ERR LOG_WARNING LOG_NOTICE LOG_INFO LOG_DEBUG writelog); -sub LOG_EMERG {0} -sub LOG_ALERT {1} -sub LOG_CRIT {2} -sub LOG_ERR {3} -sub LOG_WARNING {4} -sub LOG_NOTICE {5} -sub LOG_INFO {6} -sub LOG_DEBUG {7} - use vars qw( $cfg_syslog $cfg_syslog_loglevel @@ -83,16 +83,20 @@ $cfg_logfile_loglevel = $AMAVIS::cfg->val('logging', 'logfile loglevel'); if ($cfg_logfile ne '') { - $log_fd = IO::File->new(">>$cfg_logfile") or die; + if ($cfg_logfile =~ /^ *STDERR *$/) { + $log_fd = IO::File->new(">&2") or die; + } else { + $log_fd = IO::File->new(">>$cfg_logfile") or die; + } + $log_fd->autoflush(1); } if ((not defined $log_fd) && ($cfg_syslog =~ /^no$\|^off$\|^\s$/)) { # will have to use crystal ball for debugging... - print "Warning: No logging enabled." - } - else { - writelog($args, LOG_DEBUG, "Logging system initialized"); + warn "No logging configured!\n"; } + + writelog($args, LOG_EMERG, "Logging system initialized"); } # Write message to log file. @@ -120,9 +124,9 @@ if (defined $log_fd) { if ($level <= $cfg_logfile_loglevel) { - flock $log_fd, LOCK_EX; +# flock $log_fd, LOCK_EX; print($log_fd $line); - flock $log_fd, LOCK_UN; +# flock $log_fd, LOCK_UN; } } } diff -ur amavis-ng-0.1.3/AMAVIS/MTA/SMTP.pm amavis-ng-0.1.3-sg1/AMAVIS/MTA/SMTP.pm --- amavis-ng-0.1.3/AMAVIS/MTA/SMTP.pm Mon May 6 20:56:45 2002 +++ amavis-ng-0.1.3-sg1/AMAVIS/MTA/SMTP.pm Sat Jun 15 04:05:49 2002 @@ -124,16 +124,20 @@ }; # crate PID file. - my $pidfile=IO::File->new(">$cfg_pidfile"); - unless (defined $pidfile) { - writelog($args,LOG_ERR, __PACKAGE__. - ": Unable to create PID file."); - return 0; - } - else { - $pidfile->print("$$\n"); - $pidfile->close; + if ($cfg_pidfile) + { + my $pidfile=IO::File->new(">$cfg_pidfile"); + unless (defined $pidfile) { + writelog($args,LOG_ERR, __PACKAGE__. + ": Unable to create PID file."); + return 0; + } + else { + $pidfile->print("$$\n"); + $pidfile->close; + } } + $0='amavisd'; if ($AMAVIS::cfg->val('global', 'x-header') eq 'true') { @@ -249,25 +253,15 @@ $mta_result='accept'; - if (defined $cfg_smtp_session_timeout) { - my $result=$conn->print("250 I got it. Seems virus-free.\r\n"); - $conn->close(); - # We won't care if the timeout alarm hits us now. - $SIG{'ALRM'}='IGNORE'; - # If the client has disconnected, it should try to retransmit. - unless ($result) { - writelog($args,LOG_ERR,__PACKAGE__. - ": Client has dropped connection on us."); - return 0; - } - } - # Send message out via SMTP. - my $smtpclient = new Net::SMTP("$cfg_smtp_out:$cfg_smtp_port_out", + writelog($args,LOG_DEBUG, __PACKAGE__. + ": Connecting to SMTP client with host='$cfg_smtp_out', port='$cfg_smtp_port_out'."); + my $smtpclient = new Net::SMTP($cfg_smtp_out, + Port => $cfg_smtp_port_out, Hello => 'localhost', Timeout => 30) || do { writelog($args,LOG_ERR, __PACKAGE__. - ": Unable to connect to SMTP server on $cfg_smtp_out:$cfg_smtp_port_out."); + ": Unable to connect to SMTP server on $cfg_smtp_out:$cfg_smtp_port_out: $@"); return 0; }; @@ -298,10 +292,23 @@ # while (my $line=$$args{'filehandle'}->getline()) { $smtpclient->datasend("$_\r\n"); } - $smtpclient->dataend(); + $result=$smtpclient->dataend(); + return 0 unless ($result); writelog($args,LOG_DEBUG, "DATA done."); $smtpclient->quit(); # Return successfully + if (defined $cfg_smtp_session_timeout) { + my $result=$conn->print("250 I got it. Seems virus-free.\r\n"); + $conn->close(); + # We won't care if the timeout alarm hits us now. + $SIG{'ALRM'}='IGNORE'; + # If the client has disconnected, it should try to retransmit. + unless ($result) { + writelog($args,LOG_ERR,__PACKAGE__. + ": Client has dropped connection on us."); + return 0; + } + } return 1; } @@ -389,8 +396,11 @@ writelog($args,LOG_DEBUG, __PACKAGE__.": Sending mail from $sender to ". join(', ',@recipients)); - my $smtpclient = new Net::SMTP("$cfg_smtp_out:$cfg_smtp_port_out", + writelog($args,LOG_ERR, __PACKAGE__.": Creating SMTP client with host='$cfg_smtp_out' port='$cfg_smtp_port_out'"); + my $smtpclient = new Net::SMTP($cfg_smtp_out, + Port => $cfg_smtp_port_out, Hello => 'localhost', + Debug => 9, Timeout => 30) || do { writelog($args,LOG_ERR, __PACKAGE__. ": Unable to connect to SMTP server on $cfg_smtp_out:$cfg_smtp_port_out."); @@ -437,119 +447,153 @@ my $from=undef; my @to=(); - # Send an SMTP greeting - $conn->print("220 AMaViS SMTP Ready.\r\n"); - SMTP:while (<$conn>) { - # Remove leading, trailing spaces and \r\n - chomp; - s/^\s+//; - s/\s+$//; - foreach ($_) { - # RFC 822 4.1.1 - /^HELO (.*)/i && do { - $conn->print("250 Nice to meet you, $1.\r\n"); - next; - }; - /^MAIL FROM:(.*)/i && do { - $from=$1; - $conn->print("250 Ok.\r\n"); - next; - }; - /^RCPT TO:(.*)/i && do { - push @to,$1; - $conn->print("250 Ok.\r\n"); - next; - }; - /^DATA$/i && do { - # We will not accept data without either sender or recipients. - unless (defined $from) { - $conn->print("503 Who are you?\r\n"); - next; - } - unless ($#to >= 0) { - $conn->print("503 Give me at least ONE recipient\r\n"); - next; - } - $conn->print("354 Just go ahead. Don't forget to end with a dot\r\n"); + eval { + # Send an SMTP greeting + $conn->print("220 AMaViS ESMTP Ready.\r\n") + or die "Sending greeting\n"; + SMTP:while (<$conn>) { + # Remove leading, trailing spaces and \r\n + chomp; + s/^\s+//; + s/\s+$//; + foreach ($_) { + # RFC 822 4.1.1 + /^EHLO (.*)/i && do { + $conn->print("250-AMaViS\r\n250-PIPELINING\r\n250 8BITMIME\r\n") + or die "Sending EHLO response\n"; + next; + }; + /^HELO (.*)/i && do { + $conn->print("250 Nice to meet you, $1.\r\n") + or die "Sending HELO response\n"; + next; + }; + /^MAIL FROM:(.*)/i && do { + $from=$1; + $conn->print("250 Ok.\r\n"); + next; + }; + /^RCPT TO:(.*)/i && do { + push @to,$1; + $conn->print("250 Ok.\r\n") + or die "Sending RCPT TO response\n"; + next; + }; + /^DATA$/i && do { + # We will not accept data without either sender or recipients. + unless (defined $from) { + $conn->print("503 Who are you?\r\n") + or die "Sending DATA error\n"; + next; + } + unless ($#to >= 0) { + $conn->print("503 Give me at least ONE recipient\r\n") + or die "Sending DATA error\n"; + next; + } + $conn->print("354 Just go ahead. Don't forget to end with a dot\r\n") + or die "Sending DATA response\n"; - # Open message file that is later going to be disected and scanned - my $output = IO::File->new("+>$$args{'directory'}/email.txt"); - - my $done=undef; - my $headers=1; - DATA:while (<$conn>) { - s/(\r\n?|\n\r?)//; - if(/^\.$/) { - writelog($args,LOG_DEBUG,__PACKAGE__. - ": Received end of DATA."); - $done = 1; - last DATA; - } - # chomp; - # s/\r\n//; - # Replace '..' at beginning of line with '.' - s/^\.\./\./; - if (/^\s*$/) { - $headers=0; - } - if ($headers==1) { - $$args{'headers'}.="$_\n"; - } - $output->print("$_\n"); - } - # Message file has been written, reset file pointer and put it into - # the record. - $output->seek(0,0); - $$args{'filehandle'} = $output; - - unless (defined $done) { - $conn->print("550 DATA didn't end with a dot\r\n"); - $conn->close(); - writelog($args,LOG_ERR,__PACKAGE__. - ": DATA didn't end with a dot"); - return 0; - } - # Body and dot have been received. - # If no timeout is set, the message is acknowledged - # immediately. - unless (defined $cfg_smtp_session_timeout) { - $conn->print("250 Message received. Processing. Thanks. Bye.\r\n"); - $conn->close(); + # Open message file that is later going to be disected and scanned + my $output = IO::File->new("+>$$args{'directory'}/email.txt") + or die "Opening email.txt\n"; + + + my $done=undef; + my $headers=1; + DATA:while (<$conn>) { + s/(\r\n?|\n\r?)//; + if(/^\.$/) { + writelog($args,LOG_DEBUG,__PACKAGE__. + ": Received end of DATA."); + $done = 1; + last DATA; + } + # chomp; + # s/\r\n//; + # Replace '..' at beginning of line with '.' + s/^\.\./\./; + if (/^\s*$/) { + $headers=0; + } + if ($headers==1) { + $$args{'headers'}.="$_\n"; + } + $output->print("$_\n") + or die "Writing to email.txt\n"; + } + # Message file has been written, reset file pointer and put it into + # the record. + $output->seek(0,0) + or die "Seeking in email.txt\n"; + $$args{'filehandle'} = $output; + + unless (defined $done) { + $conn->print("550 DATA didn't end with a dot\r\n") + or die "Sending response to end of DATA\n"; + $conn->close() + or die "Closing connection\n"; + writelog($args,LOG_ERR,__PACKAGE__. + ": DATA didn't end with a dot"); + return 0; + } + # Body and dot have been received. + # If no timeout is set, the message is acknowledged + # immediately. + unless (defined $cfg_smtp_session_timeout) { + $conn->print("250 Message received. Processing. Thanks. Bye.\r\n") + or die "Sending response to end of DATA\n"; + $conn->close() + or die "Closing connection\n"; + } + last SMTP; + }; + /^RSET/i && do { + $from=undef; + @to=(); + $conn->print("250 Okay, back to Square One\r\n") + or die "Sending response to RSET\n"; + }; + # Do not confirm that recipient exists. + /^VRFY/i && do { + $conn->print("252 No.\r\n") + or die "Sending response to VRFY\n"; + next; + }; + # Do not confirm that recipient expands to a mailing list. + /^EXPN/i && do { + $conn->print("252 No.\r\n") + or die "Sending response to EXPN\n"; + next; + }; + /^HELP/i && do { + $conn->print("214 Help yourself\r\n") + or die "Sending response to HELP\n"; + next; + }; + /^NOOP/i && do { + $conn->print("250 Yawn\r\n") + or die "Sending response to NOOP\n"; + next; + }; + /^QUIT/i && do { + $conn->print("221 See you later\r\n") + or die "Sending response to QUIT\n"; + $conn->close() + or die "Closing connection"; + return 0; + }; + # If none of the above commands was recognized, tell the other + # side so. + $conn->print("500 Sprechen Sie SMTP?\r\n") + or die "Sending response to unknown command\n"; } - last SMTP; - }; - /^RSET/i && do { - $from=undef; - @to=(); - $conn->print("250 Okay, back to Square One\r\n"); - }; - # Do not confirm that recipient exists. - /^VRFY/i && do { - $conn->print("252 No.\r\n"); - next; - }; - # Do not confirm that recipient expands to a mailing list. - /^EXPN/i && do { - $conn->print("252 No.\r\n"); - next; - }; - /^HELP/i && do { - $conn->print("214 Help yourself\r\n"); - next; - }; - /^NOOP/i && do { - $conn->print("250 Yawn\r\n"); - next; - }; - /^QUIT/i && do { - $conn->print("221 See you later\r\n"); - $conn->close(); - return 0; - }; - # If none of the above commands was recognized, tell the other - # side so. - $conn->print("500 Sprechen Sie SMTP?\r\n"); } + }; + if ($@) + { + $conn->close(); + return 0; } $$args{'sender'} = $from; diff -ur amavis-ng-0.1.3/AMAVIS/Notify/Admin.pm amavis-ng-0.1.3-sg1/AMAVIS/Notify/Admin.pm --- amavis-ng-0.1.3/AMAVIS/Notify/Admin.pm Mon May 6 20:56:03 2002 +++ amavis-ng-0.1.3-sg1/AMAVIS/Notify/Admin.pm Sat Jun 15 04:05:49 2002 @@ -50,8 +50,9 @@ To: $recipient Subject: FOUND VIRUS IN MAIL FROM $orig_sender. -The virus checker has found potentially malicious code in a mail by -$orig_sender. Delivery has been stopped. +The virus checker has found potentially malicious code in a mail +sent by $orig_sender. +Delivery has been stopped. The recipient(s) for this message were: @@ -70,14 +71,19 @@ $$args{'quarantine_file'}.log. EOF }; + $message.=" + +This message was rejected because: +"; foreach my $scanner (@{$$args{'virus_scanners'}}) { - $message.="\n$scanner found:\n"; - foreach my $virus (@{$$args{'found_viruses'}{$scanner}}) { - $message.=" $virus\n"; - } + if (@{$$args{'found_viruses'}{$scanner}}) { + $message.="\n$scanner found:\n"; + foreach my $virus (@{$$args{'found_viruses'}{$scanner}}) { + $message.=" $virus\n"; + } + } } - - $message.="\n".('-'x 72)."\nMessage headers follow:\n"; + $message.="\n--- Below this line is a copy of the message headers.\n\n"; $message.=$$args{'headers'}; $AMAVIS::mta->send_message($args, $message, '<>', $recipient) diff -ur amavis-ng-0.1.3/AMAVIS/Notify/Recipients.pm amavis-ng-0.1.3-sg1/AMAVIS/Notify/Recipients.pm --- amavis-ng-0.1.3/AMAVIS/Notify/Recipients.pm Mon May 6 20:56:04 2002 +++ amavis-ng-0.1.3-sg1/AMAVIS/Notify/Recipients.pm Sat Jun 15 04:05:49 2002 @@ -50,12 +50,12 @@ my $message = <<"EOF"; From: $cfg_sender To: $recipients -Subject: VIRUS IN MAIL FOR YOU FROM $orig_sender. - -Our virus checker has found potentially malicious code in a mail from -$orig_sender addressed to you. Delivery has been stopped. For further -questions, please contact $cfg_sender. +Subject: VIRUS WARNING: Message from $orig_sender failed virus check. +Our virus checker has found potentially malicious code in a mail addressed +to you from $orig_sender. +Delivery has been stopped. +If you have any questions, please contact $cfg_sender. EOF if (defined $$args{'quarantine_file'}) { $message.= << "EOF"; @@ -64,14 +64,19 @@ EOF }; + $message.=" + +This message was rejected because: +"; foreach my $scanner (@{$$args{'virus_scanners'}}) { - $message.="\n$scanner found:\n"; - foreach my $virus (@{$$args{'found_viruses'}{$scanner}}) { - $message.=" $virus\n"; - } + if (@{$$args{'found_viruses'}{$scanner}}) { + $message.="\n$scanner found:\n"; + foreach my $virus (@{$$args{'found_viruses'}{$scanner}}) { + $message.=" $virus\n"; + } + } } - - $message.="\n".('-'x 72)."\nMessage headers follow:\n"; + $message.="\n--- Below this line is a copy of the message headers.\n\n"; $message.=$$args{'headers'}; $AMAVIS::mta->send_message($args, $message, '<>', @recipients) diff -ur amavis-ng-0.1.3/AMAVIS/Notify/Sender.pm amavis-ng-0.1.3-sg1/AMAVIS/Notify/Sender.pm --- amavis-ng-0.1.3/AMAVIS/Notify/Sender.pm Mon May 6 20:56:04 2002 +++ amavis-ng-0.1.3-sg1/AMAVIS/Notify/Sender.pm Sat Jun 15 04:05:49 2002 @@ -44,11 +44,11 @@ my $message = <<"EOF"; From: $cfg_sender To: $recipient -Subject: VIRUS IN YOUR MAIL +Subject: Failure notice: Message failed virus check Our virus checker has found potentially malicious code in a mail by -you. Delivery has been stopped. For further questions, please contact -$cfg_sender. +you. Delivery has been stopped. If you have any questions, please +contact $cfg_sender. The recipient(s) for this message were: EOF @@ -59,17 +59,22 @@ The message has been quarantined as $$args{'quarantine_file'}. - EOF }; + $message.=" + +This message was rejected because: +"; foreach my $scanner (@{$$args{'virus_scanners'}}) { - $message.="\n$scanner found:\n"; - foreach my $virus (@{$$args{'found_viruses'}{$scanner}}) { - $message.=" $virus\n"; - } + if (@{$$args{'found_viruses'}{$scanner}}) { + $message.="\n$scanner found:\n"; + foreach my $virus (@{$$args{'found_viruses'}{$scanner}}) { + $message.=" $virus\n"; + } + } } - $message.="\n".('-'x 72)."\nMessage headers follow:\n"; + $message.="\n--- Below this line is a copy of the message headers.\n\n"; $message.=$$args{'headers'}; $AMAVIS::mta->send_message($args, $message, '<>', $recipient) diff -ur amavis-ng-0.1.3/AMAVIS.pm amavis-ng-0.1.3-sg1/AMAVIS.pm --- amavis-ng-0.1.3/AMAVIS.pm Sat May 11 11:05:26 2002 +++ amavis-ng-0.1.3-sg1/AMAVIS.pm Sat Jun 15 04:06:01 2002 @@ -40,7 +40,7 @@ use Config::IniFiles; use Sys::Syslog qw(:DEFAULT setlogsock); -use POSIX qw(strftime geteuid setuid uname setsid setuid setgid); +use POSIX qw(strftime uname setsid); use POSIX ":sys_wait_h"; use File::Copy; @@ -84,6 +84,7 @@ $cfg_uid $cfg_gid + $cfg_chroot $cfg_quarantine_dir @@ -125,6 +126,7 @@ $cfg_uid = $cfg->val('security', 'uid'); $cfg_gid = $cfg->val('security', 'gid'); + $cfg_chroot = $cfg->val('security', 'chroot'); # If UID, GID are not numeric or not set at all... if ((! defined $cfg_uid) or ($cfg_uid !~ /^\d+$/)) { @@ -271,9 +273,34 @@ } writelog($args,LOG_DEBUG, __PACKAGE__." initialized"); + my $uid = getpwuid($<) || "Kilroy"; my $euid = getpwuid($>) || "Kilroy"; - writelog($args,LOG_DEBUG, __PACKAGE__.": Running as UID/EUID $uid($<)/$euid($>)"); + + if ($cfg_chroot) + { + chroot($cfg_chroot) + or die "Error in chroot($cfg_chroot): $!\n"; + chdir("/") + or die "Error in chdir(/): $!\n"; + } + + # Drop privileges if UID=root + if ($< == 0) { + $( = $) = $cfg_gid; + if ( ($( != $cfg_gid) || ($) != $cfg_gid) ) { + writelog($args,LOG_ERR, __PACKAGE__.": Can't drop GID to $cfg_gid"); + die; + } + + $< = $> = $cfg_uid; + if ( ($< != $cfg_uid) || ($> != $cfg_uid) ) { + writelog($args,LOG_ERR, __PACKAGE__.": Can't drop UID to $cfg_uid"); + die; + } + } + + writelog($args,LOG_DEBUG, __PACKAGE__.": Running as UID/EUID $"); } # "Universal" extraction routine @@ -299,6 +326,13 @@ # We know how to unpack this sucker... $type_extractors{$filetype}->extract($args, $filename) or do { writelog($args,LOG_ERR, "Error while unpacking $filename"); + if ($type_extractors{$filetype}->{permerror}) + { + writelog($args,LOG_DEBUG,__PACKAGE__.": Error from extractor was permanent.\n"); + # Make up a virus so we get an error in the output. + push(@{$$args{'virus_scanners'}},"Message Processing"); + push(@{$$args{'found_viruses'}{'Message Processing'}},"An error occured processing the message for virus scanning:\n".$type_extractors{$filetype}->{lasterror}); + } return 0; }; $newfilesfound=1; @@ -342,82 +376,87 @@ $$args{'sender'} = ''; } - # chdir to tmp directory (which has to be writable!) - my $tempdir = $cfg->val('paths', 'unpack'); - chdir $tempdir or do { - writelog($args,LOG_CRIT, "Couldn't chdir to $tempdir"); - $mta->freeze_message($args); - return 0; - }; - - my $securename=get_secure_filename($args); - - # Initialize unpacked_size to size of mail file... - my @stat = stat("$$args{'directory'}/email.txt"); - $$args{'unpacked_size'} = $stat[7]; - # ...and unpacked_files to 1. - $$args{'unpacked_files'} = 1; - - # The first file to be analyzed is the mail file - link( "$$args{'directory'}/email.txt", - "$$args{'directory'}/parts/$securename") - or do { - writelog($args,LOG_CRIT, "Unable to link parts/$securename to email.txt"); - $mta->freeze_message($args); - return 0; - }; + eval { + # chdir to tmp directory (which has to be writable!) + my $tempdir = $cfg->val('paths', 'unpack'); + chdir $tempdir or do { + writelog($args,LOG_CRIT, "Couldn't chdir to $tempdir"); + die "freeze"; + }; - # Add filename to contents field of message. - $ {$$args{'contents'}}{$securename} = {}; + my $securename=get_secure_filename($args); + + # Initialize unpacked_size to size of mail file... + my @stat = stat("$$args{'directory'}/email.txt"); + $$args{'unpacked_size'} = $stat[7]; + # ...and unpacked_files to 1. + $$args{'unpacked_files'} = 1; + + # The first file to be analyzed is the mail file + link( "$$args{'directory'}/email.txt", + "$$args{'directory'}/parts/$securename") + or do { + writelog($args,LOG_CRIT, "Unable to link parts/$securename to email.txt: $!"); + die "freeze\n"; + }; - extract($args,$securename) or do { - writelog($args,LOG_CRIT, "Error while unpacking message"); - $mta->freeze_message($args); - return 0; - }; + # Add filename to contents field of message. + $ {$$args{'contents'}}{$securename} = {}; + + extract($args,$securename) or do { + writelog($args,LOG_CRIT, "Error while unpacking message"); + die "freeze\n"; + }; # foreach my $policy (@policy) { # $policy->scan($args) or # writelog($args,LOG_CRIT,"Error while scanning for policy"); # } - - foreach my $av (@av) { - $av->scan($args) or do { - writelog($args,LOG_CRIT,"Error while scanning for viruses with $av"); - $mta->freeze_message($args); - return 0; - }; - } - + + foreach my $av (@av) { + writelog($args,LOG_DEBUG,"Scanning with object $av"); + $av->scan($args) or do { + writelog($args,LOG_CRIT,"Error while scanning for viruses with $av"); + die "freeze\n"; + }; + } + }; + my $freeze = $@; + # Decide what to do with the message if (defined $$args{'found_viruses'}) { - # Log what viruses have been found. - foreach my $scanner (@{$$args{'virus_scanners'}}) { - writelog($args,LOG_INFO, "$scanner found:"); - foreach my $virus (@{$$args{'found_viruses'}{$scanner}}) { - writelog($args,LOG_INFO, " $virus"); + # Log what viruses have been found. + foreach my $scanner (@{$$args{'virus_scanners'}}) { + writelog($args,LOG_INFO, "$scanner found:"); + foreach my $virus (@{$$args{'found_viruses'}{$scanner}}) { + writelog($args,LOG_INFO, " $virus"); + } } - } - - # Tell MTA module to drop the message - $mta->drop_message($args); - - # Quarantine message - if ($cfg_quarantine_dir ne 'no') { - $self->quarantine_message($args); - } - - # Complain to everybody who cares if policy check failed or a - # virus was found - foreach my $notifier (@notifiers) { - $notifier->notify($args) or - writelog($args,LOG_CRIT, "Error while notifying $notifier"); - } + + # Tell MTA module to drop the message + $mta->drop_message($args); + + # Quarantine message + if ($cfg_quarantine_dir ne 'no') { + $self->quarantine_message($args); + } + + # Complain to everybody who cares if policy check failed or a + # virus was found + foreach my $notifier (@notifiers) { + $notifier->notify($args) or + writelog($args,LOG_CRIT, "Error while notifying $notifier"); + } + } + elsif ($freeze) + { + $mta->freeze_message($args); + return 0; } else { - $mta->accept_message($args); + $mta->accept_message($args); } - + # Return successfully return 1; } @@ -450,21 +489,23 @@ my $pid = open($handle, "-|"); unless (defined $pid ) { writelog($args,LOG_ERR, __PACKAGE__.": Can't fork: $!"); - die "Can't fork: $!"; + return undef; } if ($pid) { # parent return $handle; } else { # Drop privileges if UID=root if ($< == 0) { - setuid($cfg_uid) or do { - writelog($args,LOG_ERR, __PACKAGE__.": Can't drop UID to $cfg_uid"); -# die; - }; - setgid($cfg_gid) or do { - writelog($args,LOG_ERR, __PACKAGE__.": Can't drop GID to $cfg_gid"); -# die; - }; + $< = $> = $cfg_uid; + if ( ($< != $cfg_uid) || ($> != $cfg_uid) ) { + writelog($args,LOG_ERR, __PACKAGE__.": Can't drop UID to $cfg_uid"); + die; + }; + $) = $( = "$cfg_gid $cfg_gid"; + if (($( ne "$cfg_gid $cfg_gid") || ($) ne "$cfg_gid $cfg_gid") ) { + writelog($args,LOG_ERR, __PACKAGE__.": Can't drop GID to $cfg_gid"); + die; + }; } $ENV{PATH} = "/bin:/usr/bin"; exec @_ @@ -477,11 +518,14 @@ sub cleanup { my $self = shift; my $args = shift; + + return 1; + if ($cfg->val('paths','cleanup') eq 'yes') { writelog($args,LOG_INFO, __PACKAGE__.": Cleaning up."); writelog($args,LOG_INFO, __PACKAGE__.": Removing $$args{'directory'}"); eval { - rmtree($$args{'directory'}) or do { + rmtree($$args{'directory'},1,1) or do { writelog($args,LOG_CRIT, __PACKAGE__.": Error: Unable to remove directory"); return 0 }; diff -ur amavis-ng-0.1.3/amavis.pl amavis-ng-0.1.3-sg1/amavis.pl --- amavis-ng-0.1.3/amavis.pl Mon Mar 25 07:28:22 2002 +++ amavis-ng-0.1.3-sg1/amavis.pl Sat Jun 15 04:06:10 2002 @@ -5,6 +5,14 @@ use AMAVIS; use Getopt::Long; +# Use these ASAP so we can chroot. +use Carp::Heavy; +use POSIX qw(setuid); +use IO::Select; +use IO::Dir; +use MIME::Decoder::NBit; +use MIME::Decoder::Base64; + use vars qw( $conffile ); diff -ur amavis-ng-0.1.3/etc/amavis.conf amavis-ng-0.1.3-sg1/etc/amavis.conf --- amavis-ng-0.1.3/etc/amavis.conf Fri May 17 02:08:58 2002 +++ amavis-ng-0.1.3-sg1/etc/amavis.conf Sun May 19 15:34:51 2002 @@ -11,7 +11,7 @@ ; mail-transfer-agent = EximPerl ; mail-transfer-agent = Postfix ; mail-transfer-agent = Sendmail -; mail-transfer-agent = SMTP +mail-transfer-agent = SMTP ;; Which virus scanner to use. Use more than one if you desire ; virus-scanner = FSAV @@ -23,7 +23,7 @@ ; virus-scanner = Bitdefender ; virus-scanner = FPROT ; virus-scanner = MKS -; virus-scanner = NAI +virus-scanner = NAI ; virus-scanner = NVC ; virus-scanner = Panda ; virus-scanner = CLAM @@ -33,6 +33,7 @@ ;; "Mail" should definitely be used, it is used for decoding MIME ;; attachments ; extractors=Mail, Text, GZIP, BZIP2, LHA, ARC, Zip, Tar, ZOO, RAR +extractors=Mail, Text, GZIP, BZIP2, Zip, Tar ;; Who should be notified? notifiers=Sender, Recipients, Admin @@ -41,7 +42,7 @@ ;; through? x-header = true x-header-tag = X-Scanned-By -x-header-line = AMaViS at a badly configured site. +x-header-line = AMaViS at FAAR umask = 002 @@ -70,11 +71,11 @@ ;; Which domains should be considered local? Recipients are notified ;; about mail that was stopped only if they are local. The domain name ;; is matches against this Perl regular expression. -local domain = .*example\.com +local domain = flinthomes.net ;; What address will appear in the From:-header of warning messages mail from = postmaster@example.com ;; Who is the mail admin -admin = postmaster@example.com +admin = postmaster@flinthomes.net [Exim] @@ -102,7 +103,7 @@ [Qmail] -qmail-queue = /usr/sbin/qmail-queue +qmail-queue = /var/qmail/bin/qmail-queue ;; If problems occur, put message into this directory problem dir = /var/spool/amavis/problems @@ -119,9 +120,9 @@ ;; >0: timeout is set to n seconds. After n seconds, processing ;; is aborted if it has not been finished. -; session timeout = 240 +session timeout = 2700 ; Problem dir is not needed if SMTP timeout is set. -problem dir = /var/spool/amavis/problems +; problem dir = /var/spool/amavis/problems pidfile = /var/run/amavis/amavisd.pid @@ -176,7 +177,7 @@ [NAI] -uvscan = /usr/bin/uvscan +uvscan = /usr/local/bin/uvscan [NVC] @@ -200,4 +201,4 @@ [Trend] -vscan = /usr/bin/vscan \ No newline at end of file +vscan = /usr/bin/vscan