Hi,

On Friday 15 February 2013, Carsten Wolff wrote:
> currently, all mail that
> - contains a "mailbomb"
> - contains encrypted parts
> - contains any other undecipherable parts
> falls under content category "UNCHECKED" and thus shares a final
> destination. I think  it might be desirable to have different final
> destinations for these cases. Please take the attached (untested) patch to
> amavis 2.8 as a suggestion. It introduces two sub-cc's for UNCHECKED. One
> for mail that only contains encrypted parts (e.g. S/MIME), the other for
> mail that contains anything over limits (mailbombs?).

attached you'll find a tested version of the patch. I use it to defang 
"mailbombs" like they were in earlier amavis Versions, by the following 
configuration:

$defang_by_ccat{CC_UNCHECKED.",2"} = 1;

Please consider this patch, thank you.

Carsten
--- amavisd.orig	2012-06-30 15:43:31.000000000 +0200
+++ amavisd	2013-04-25 13:38:05.349532975 +0200
@@ -427,9 +427,9 @@
       # referenced indirectly through *_by_ccat variables for compatibility
     [qw(
       $final_virus_destiny $final_banned_destiny $final_unchecked_destiny
-      $final_spam_destiny $final_bad_header_destiny
-      @virus_lovers_maps @spam_lovers_maps @unchecked_lovers_maps
-      @banned_files_lovers_maps @bad_header_lovers_maps
+      $final_encrypted_destiny $final_mailbomb_destiny $final_spam_destiny
+      $final_bad_header_destiny @virus_lovers_maps @spam_lovers_maps
+      @unchecked_lovers_maps @banned_files_lovers_maps @bad_header_lovers_maps
       $always_bcc $dsn_bcc
       $mailfrom_notify_sender $mailfrom_notify_recip
       $mailfrom_notify_admin  $mailfrom_notify_spamadmin
@@ -1126,6 +1126,8 @@
   $final_virus_destiny      = D_DISCARD;
   $final_banned_destiny     = D_DISCARD;
   $final_unchecked_destiny  = D_PASS;
+  $final_encrypted_destiny  = D_PASS;
+  $final_mailbomb_destiny   = D_PASS;
   $final_spam_destiny       = D_PASS;
   $final_bad_header_destiny = D_PASS;
 
@@ -1282,6 +1284,8 @@
     CC_SPAMMY.',1','Spammy3',    # tag3_level
     CC_SPAM,       'Spam',       # kill_level
     CC_UNCHECKED,  'Unchecked',
+    CC_UNCHECKED.',1', 'UncheckedCrypted',
+    CC_UNCHECKED.',2', 'UncheckedOverLimits',
     CC_BANNED,     'Banned',
     CC_VIRUS,      'Virus',
   );
@@ -1720,6 +1724,8 @@
     CC_VIRUS,       sub { c('final_virus_destiny') },
     CC_BANNED,      sub { c('final_banned_destiny') },
     CC_UNCHECKED,   sub { c('final_unchecked_destiny') },
+    CC_UNCHECKED.',1', sub { c('final_encrypted_destiny') },
+    CC_UNCHECKED.',2', sub { c('final_mailbomb_destiny') },
     CC_SPAM,        sub { c('final_spam_destiny') },
     CC_BADH,        sub { c('final_bad_header_destiny') },
     CC_MTA.',1',    D_TEMPFAIL,
@@ -1736,6 +1742,8 @@
     CC_VIRUS,       'id=%n - INFECTED: %V',
     CC_BANNED,      'id=%n - BANNED: %F',
     CC_UNCHECKED,   'id=%n - UNCHECKED',
+    CC_UNCHECKED.',1', 'id=%n - UNCHECKED: encrypted message',
+    CC_UNCHECKED.',2', 'id=%n - UNCHECKED: possible mail bomb',
     CC_SPAM,        'id=%n - spam',
     CC_SPAMMY.',1', 'id=%n - spammy (tag3)',
     CC_SPAMMY,      'id=%n - spammy',
@@ -12532,7 +12540,7 @@
                  # across some permanent problem making us unable to decide
                  # if the message is to be really delivered.
   # is any mail component password protected or otherwise non-decodable?
-  my $any_undecipherable = 0;
+  my $any_undecipherable = 0; my $all_crypted = 1; my $over_levels = 0;
   my $mime_err;  # undef, or MIME parsing error string as given by MIME::Parser
   if (defined $last_task_completed_at) {
     my $dt = $msginfo->rx_time - $last_task_completed_at;
@@ -12793,13 +12801,16 @@
 
       $which_section = "parts_decode_ext";
       snmp_count('OpsDec');
-      ($hold,$any_undecipherable) =
+      ($hold,$any_undecipherable,$all_crypted,$over_levels) =
         Amavis::Unpackers::decompose_mail($msginfo->mail_tempdir,
                                           $file_generator_object);
       if ($hold ne '' || $any_undecipherable) {
-        $msginfo->add_contents_category(CC_UNCHECKED,0);
+        my $subcc = 0;
+        $subcc = 1 if ($any_undecipherable && $all_crypted);
+        $subcc = 2 if ($hold ne '' and $over_levels);
+        $msginfo->add_contents_category(CC_UNCHECKED,$subcc);
         for my $r (@{$msginfo->per_recip_data}) {
-          $r->add_contents_category(CC_UNCHECKED,0)
+          $r->add_contents_category(CC_UNCHECKED,$subcc)
             if !$r->bypass_virus_checks;
         }
       }
@@ -27525,12 +27536,15 @@
   my($tempdir,$file_generator_object) = @_;
 
   my $hold; my(@parts); my $depth = 1; my $any_undecipherable = 0;
+  my $all_crypted = 1;
+  my $over_levels = 0;
   my $which_section = "parts_decode";
   # fetch all not-yet-visited part names, and start a new cycle
 TIER:
   while (@parts = @{$file_generator_object->parts_list}) {
     if ($MAXLEVELS > 0 && $depth > $MAXLEVELS) {
       $hold = "Maximum decoding depth ($MAXLEVELS) exceeded";
+      $over_levels = 1;
       last;
     }
     $file_generator_object->parts_list_reset;  # new cycle of names
@@ -27577,14 +27591,18 @@
     }
     for my $part (@parts) {
       if ($part->exists && !defined($hold))
-        { $hold = decompose_part($part, $tempdir) }
-      $any_undecipherable++  if grep($_ eq 'U', @{ $part->attributes || [] });
+        { ( $hold, $over_levels ) = decompose_part($part, $tempdir) }
+      if ( grep($_ eq 'U', @{ $part->attributes || [] }) ) {
+        $any_undecipherable++;
+        if (! grep($_ eq 'C', @{ $part->attributes || [] }))
+          { $all_crypted = 0; }
+      }
     }
     last TIER  if defined $hold;
     $depth++;
   }
   section_time($which_section); prolong_timer($which_section);
-  ($hold, $any_undecipherable);
+  ($hold, $any_undecipherable, $all_crypted, $over_levels);
 }
 
 # Decompose one part
@@ -27595,7 +27613,7 @@
   # 0 - truly atomic or unknown or archiver failure; consider atomic
   # 1 - some archive, successfully unpacked, result replaces original
   # 2 - probably unpacked, but keep the original (eg self-extracting archive)
-  my $hold; my $eval_stat; my $sts = 0; my $any_called = 0;
+  my $hold; my $eval_stat; my $sts = 0; my $any_called = 0; my $over_levels = 0;
   eval {
     my $type_short = $part->type_short;
     my(@ts) = !defined $type_short ? ()
@@ -27618,7 +27636,7 @@
     my $ll = -1;
     if ($eval_stat =~ /\bExceeded storage quota\b.*\bbytes by/ ||
         $eval_stat =~ /\bMaximum number of files\b.*\bexceeded/) {
-      $hold = $eval_stat; $ll = 1;
+      $hold = $eval_stat; $ll = 1; $over_levels = 1;
     }
     do_log($ll,"Decoding of %s (%s) failed, leaving it unpacked: %s",
                $part->base_name, $part->type_long, $eval_stat);
@@ -27643,7 +27661,7 @@
                     ['atomic','archive, unpacked','source retained']->[$sts]);
   section_time('decompose_part')  if $any_called;
   die $eval_stat  if $eval_stat =~ /^timed out\b/;  # resignal timeout
-  $hold;
+  ( $hold, $over_levels );
 }
 
 # a trivial wrapper around mime_decode() to adjust arguments and result

Reply via email to