This is a discussion on [AMaViS-user] [patch] replace banned attachments within the Amavis User forums, part of the Anti-Spam and Anti-Virus Related Forums category; Hello, here is a patch to replace banned attachments with a text notice. Guess a lot of people are going ...
|
|||||||
| FAQ | Members List | Calendar | Search | Today's Posts | Mark Forums Read |
|
|||
|
Hello,
here is a patch to replace banned attachments with a text notice. Guess a lot of people are going to be happy :-) Features: * Replace banned attachments with notice of your choice * Bans root/source attachment for compressed archives * Original message gets quarantined for later reinjection * Doesn't alter the email if no banned attachments are found * Works on nested messages Limitations: * Doesn't work on a per recipient basis, if a banned attachment is found for one recipient, it get's banned for all. * All original files need to be retained (needed for reconstructing the email), so effectivly compressed archives get passed twice to the virus scanner. How to use it: * Apply the inlined patch to amavisd-new-2.3.3 or get it from here: http://www.intra2net.com/opensource/...tachment.patch * Set the new configuration variables: $banned_replace_attachment = 1; $banned_replace_attachment_filename = "File %F was replaced.txt"; $banned_replace_attachment_content_type = "text/plain"; $banned_replace_attachment_text = "The file %F was replaced.\n\n Please contact your administrator for further details."; Modify this existing variable to retain all attachments: @keep_decoded_original_maps = (new_RE( qr '.' )); Mark: Maybe this step can be automatated or even modified that all files are kept on disc, but not passed to the virus scanner. * Restart amavisd and enjoy. I gave the patch extensive testing for several hours, so far it's not causing any trouble. As simple as the patch may look now, it took more than 20 hours to cook this one up. Please test it before putting it in productive usage. Cheers, Thomas diff -u -r amavisd-new-2.3.3/amavisd amavisd-new.banned/amavisd --- amavisd-new-2.3.3/amavisd 2005-08-22 01:46:15.000000000 +0200 +++ amavisd-new.banned/amavisd 2005-11-07 13:28:37.902497471 +0100 @@ -215,6 +215,10 @@ $per_recip_whitelist_sender_lookup_tables $per_recip_blacklist_sender_lookup_tables + $banned_replace_attachment $banned_replace_attachment_filename + $banned_replace_attachment_content_type + $banned_replace_attachment_text + %sql_clause @local_domains_maps @mynetworks_maps @@ -3547,6 +3551,8 @@ { my($self)=shift; !@_ ? $$self[13] : ($$self[13]=shift) } sub banned_rhs # right-hand side of matching rules (a ref to a list) { my($self)=shift; !@_ ? $$self[14] : ($$self[14]=shift) } +sub banned_parts_obj # corresponding amavis part object + { my($self)=shift; !@_ ? $$self[15] : ($$self[15]=shift) } sub recip_final_addr { # return recip_addr_modified if set, else recip_addr my($self)=shift; @@ -5114,6 +5120,8 @@ { my($self)=shift; !@_ ? $self->{children}||[] : ($self->{children}=shift) }; sub mime_placement # part location within a MIME tree, e.g. "1/1/3" { my($self)=shift; !@_ ? $self->{place} : ($self->{place}=shift) }; +sub mime_entity # original MIME entity, needed by banned attachment replacing + { my($self)=shift; !@_ ? $self->{entity} : ($self->{entity}=shift) }; sub type_short # string or a ref to a list of strings { my($self)=shift; !@_ ? $self->{ty_short} : ($self->{ty_short}=shift) }; sub type_long @@ -5417,9 +5425,11 @@ push(@$a,$matchingkey); $r->banned_keys($a); $a = $r->banned_rhs; $a = [] if !defined($a); push(@$a,$result); $r->banned_rhs($a); + $a = $r->banned_parts_obj; $a = [] if !defined($a); + push(@$a,$part); $r->banned_parts_obj($a); } } - last if !grep {!$_->{result}} @recip_tables; # stop if all recips true + last if !c('banned_replace_attachment') && !grep {!$_->{result}} @recip_tables; # stop if all recips true and attachment replacment is disabled } # endfor: message tree traversal } # endif: doing parts checking } @@ -5436,7 +5446,7 @@ use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION); $VERSION = '2.043'; @ISA = qw(Exporter); - @EXPORT_OK = qw(&mime_decode); + @EXPORT_OK = qw(&mime_decode &banned_replace_attachment); } use Errno qw(ENOENT EACCES); use IO::File qw(O_CREAT O_EXCL O_WRONLY); @@ -5532,6 +5542,7 @@ } if (defined $part) { $part->mime_placement($placement); + $part->mime_entity($entity); # Save entity object for attachment banning $part->type_declared($mt eq $et ? $mt : [$mt, $et]); my(@rn); # recommended file names, both raw and RFC 2047 decoded my($val, $val_decoded); @@ -5594,6 +5605,45 @@ ($entity, $mime_err); } +# Build mime entity for attachment replacement +sub banned_create_replacement_entity($); # prototype +sub banned_create_replacement_entity($) { + my($filename) = @_; + + my($entity_filename) = c('banned_replace_attachment_filename'); + $entity_filename =~ s/%F/$filename/g; + my($entity_data) = c('banned_replace_attachment_text'); + $entity_data =~ s/%F/$filename/g; + + # TODO: What about encoding of special chars? + my($new_entity) = MIME::Entity->build(Type => c('banned_replace_attachment_content_type'), + Filename => $entity_filename, + Data => $entity_data, + Disposition => "attachment", + 'X-Mailer' => undef); + return $new_entity; +} + +sub banned_replace_attachment($$$); # prototype +sub banned_replace_attachment($$$) { + my($entity, $filename, $replace_entity) = @_; + + # recursive descent all child parts + # note: don't have to check ourselves as we are a child of the root pseudo-part + if ($entity->parts) { + my(@done_parts); + for my $e ($entity->parts) { + if ($e == $replace_entity) { + push(@done_parts, banned_create_replacement_entity($filename)); + } else { + banned_replace_attachment($e, $filename, $replace_entity); + push(@done_parts, $e); + } + } + $entity->parts(\@done_parts); + } +} + 1; # @@ -6071,7 +6121,7 @@ first_received_from); import Amavis::Unpackers::Validity qw( check_header_validity check_for_banned_names); - import Amavis::Unpackers::MIME qw(mime_decode); + import Amavis::Unpackers::MIME qw(mime_decode banned_replace_attachment); import Amavis::Expand qw(expand); import Amavis::Notify qw(delivery_status_notification delivery_short_report string_to_mime_entity defanged_mime_entity); @@ -7328,7 +7378,14 @@ ? "250 2.7.1 Ok, discarded" : "550 5.7.1 Message content rejected") . ", id=$am_id - $reason"); - $r->recip_done(1); + + # don't mark as delivered if we want to send + # a version with replaced banned attachments + if (!$r->infected && defined($r->banned_parts) && @{$r->banned_parts} + && c('banned_replace_attachment')) { + } else { + $r->recip_done(1); + } # note that 5xx status rejects may later be converted to bounces or # discards, according to $*_destiny setting } @@ -7338,6 +7395,48 @@ # send notifications, quarantine it do_virus($conn, $msginfo, $virus_dejavu); + # Replace attachments if neccessary (if it's no virus) + if ($banned_filename_any && !@virusname && c('banned_replace_attachment')) { + $which_section = "banned_replace_attachment"; + do_log(1, "Replacing banned attachments"); + ensure_mime_entity($msginfo, $fh, $tempdir, \@virusname, $parts_root); + + # Replace banned attachments for all recipients + for my $r (@{$msginfo->per_recip_data}) { + if (defined($r->banned_parts_obj)) { + for my $bp (@{$r->banned_parts_obj}) { + # Search for (parent) MIME entity + my($check_part) = $bp; + my($banned_filename) = undef; + my($replace_entity) = undef; + while(defined($check_part) && !defined($replace_entity)) { + ll(5) && do_log(5, "Replace banned attachments: Looking for original MIME entity in part ".$check_part->number); + $banned_filename = $check_part->name_declared; + $replace_entity = $check_part->mime_entity; + defined($replace_entity) && ll(5) && do_log(5, "Replace banned attachments: Found original MIME entity in part " + .$check_part->number. " - ".$banned_filename); + $check_part = $check_part->parent; + } + + if (!defined($replace_entity)) { + do_log(-1, "Replace banned attachments: WARNING: Original banned attachment not found in MIME entities"); + } else { + banned_replace_attachment($msginfo->mime_entity, $banned_filename, $replace_entity); + } + } + } + } + + # Remove message id as quarantined (full) email got the same ID + my($hdr_edits) = $msginfo->header_edits; + $hdr_edits = Amavis::Out::EditHeader->new if !$hdr_edits; + $hdr_edits->delete_header("Message-ID"); + $msginfo->header_edits($hdr_edits); + + # Replace mail with rewritten version + $msginfo->mail_text($msginfo->mime_entity); + $msginfo->mail_text_fn(undef); # remove filename information + } } else { # perhaps some recips consider it spam? # spaminess is an individual matter, we must compare spam level # with each recipient setting, there is no single global criterium @@ -13297,9 +13396,6 @@ do_log(4, "flatten_and_tidy_dir: NONREGULAR FILE \"$fname\""); $cnt_u++; $newpart_obj->attributes_add('S'); unlink($fname) or die "Can't remove nonregular file \"$fname\": $!"; - } elsif (-z _) { - $cnt_u++; - unlink($fname) or die "Can't remove empty file \"$fname\": $!"; } else { chmod(0750, $fname) or die "Can't change protection of file \"$fname\": $!"; @@ -13414,11 +13510,6 @@ $part->exists(0); $part->type_short(-l _ ? 'symlink' : -d _ ? 'dir' : 'special') if !defined $part->type_short; - } elsif (-z _) { # empty file - unlink($fname) or die "Can't remove \"$fname\": $!"; - $part->exists(0); - $part->type_short('empty') if !defined $part->type_short; - $part->type_long('empty') if !defined $part->type_long; } else { $part->exists(1); } ------------------------------------------------------- SF.Net email is sponsored by: Tame your development challenges with Apache's Geronimo App Server. Download it for free - -and be entered to win a 42" plasma tv or your very own Sony(tm)PSP. Click here to play: http://sourceforge.net/geronimo.php _______________________________________________ AMaViS-user mailing list AMaViS-user@lists.sourceforge.net https://lists.sourceforge.net/lists/...fo/amavis-user AMaViS-FAQ:http://www.amavis.org/amavis-faq.php3 AMaViS-HowTos:http://www.amavis.org/howto/ |