whitelist_to getting exploited

Leonardo Helman mailscanner at lists.com.ar
Wed Jan 3 13:56:09 CET 2007


On Wed, Jan 03, 2007 at 12:58:31PM +0100, Glenn Steen wrote:
> On 02/01/07, Furnish, Trever G <TGFurnish at herffjones.com> wrote:
> >Thanks for your comments, Glenn.  Some responses below.
> >
> >> -----Original Message-----
> >> From: mailscanner-bounces at lists.mailscanner.info
> >> [mailto:mailscanner-bounces at lists.mailscanner.info] On Behalf
> >> Of Glenn Steen
> >> Sent: Saturday, December 30, 2006 6:39 AM
> >> To: MailScanner discussion
> >> Subject: Re: whitelist_to getting exploited
> >>
> >> Hi Trever,
> >>
> >> Just a few odd comments below...
> >>
> >> > > Of Ramprasad
> >> > > Sent: Friday, December 29, 2006 5:22 AM
> >> > > To: MailScanner discussion
> >> > > Subject: Re: whitelist_to getting exploited
> >> > >
> >> > > On Fri, 2006-12-29 at 19:34 +1000, Res wrote:
> >> > > > On Fri, 29 Dec 2006, Ramprasad wrote:
> >> > > But user-1 wants all mails including spam  , not others
> >> > >
> >> > > For eg If I want to allow abuse at mydomain to get all mail without
> >> > > check someone sends a mail To:the_top_man at domain,abuse at domain
> >> > >
> >> > > Then this mail would bypass spam checks and reach
> >> > > the_top_man at domain
> >> > > Obviously this would be a concern to everyone , how are you folks
> >> > > getting over this issue
> >> >
> >> > Mailscanner can't split one message into several and treat them
> >> > differently based on recipient.  Doing so would risk queue filename
> >> > conflicts.
> >
> >> This should be possible to handle....:-).
> >
> 

I am splitting the message letting equal choices in antivirus
scanning and antispam scanning to be kept together

We don't have filename conflicts in zmailer (the name of the file
is the inode numbers)
I don't know how to apply (if it could be applied) to other MTAs


This is my patch (I'm using zmailer, and doesn't mind if there
are a few messages more)
The patch worked a very long time ago, maybe there where modifications
over there, so I don't know if it can be applied as is or if
you have to tweak it a little.

The idea is, you have a custom function choosing what the recipients
wants to do with their mails (if they wan't AV/AS)
For example
Virus Scanning = &DBI_AVCheck( "/path/to/avasconfig/file.conf" )
Spam Checks = &DBI_ASCheck( "/path/to/avasconfig/file.conf" )
Split Message Function List = DBI_AVCheck,DBI_ASCheck

If the custom function is analyzed in array context it returns an array with
1/0 for each rcpt "Spam Checks"

Then that array is used for splitting the mail (Split Message Function List)

But if the custom function is analyzed in scalar context, it returns
if the first recipient wants "Spam Checks" or no (1/0)





diff -Naur MailScanner-4.54.1.auth/lib/MailScanner/ConfigDefs.pl MailScanner-4.54.1/lib/MailScanner/ConfigDefs.pl
--- MailScanner-4.54.1.auth/lib/MailScanner/ConfigDefs.pl	Thu May 11 15:05:36 2006
+++ MailScanner-4.54.1/lib/MailScanner/ConfigDefs.pl	Thu May 11 16:59:17 2006
@@ -179,6 +179,7 @@
 spamstarscharacter		= spamscorecharacter
 spamstarsheader			= spamscoreheader
 spamwhitelist			= isdefinitelynotspam
+splitfunctions			= splitmessagefunctionlist
 storedcontentmessage		= storedbadcontentmessagereport
 storedfilenamemessage		= storedbadfilenamemessagereport
 storedvirusmessage		= storedvirusmessagereport
@@ -483,6 +484,7 @@
 MCPSubjectText			{Restricted?}
 SpamSubjectText			{SPAM?}
 SpamStarsHeader			X-MailScanner-SpamScore:
+SplitFunctions		
 MCPHeader			X-MailScanner-MCPCheck:
 IdHeader			X-MailScanner-Id:
 UnscannedHeader			Not scanned: please contact your Internet E-Mail Service Provider for details
diff -Naur MailScanner-4.54.1.auth/lib/MailScanner/Message.pm MailScanner-4.54.1/lib/MailScanner/Message.pm
--- MailScanner-4.54.1.auth/lib/MailScanner/Message.pm	Thu May 11 15:05:36 2006
+++ MailScanner-4.54.1/lib/MailScanner/Message.pm	Thu May 11 16:59:17 2006
@@ -244,12 +244,69 @@
 
   bless $this, $type;
 
+  if( $this->SplitMail($this) ) {
+    return undef;
+  }
+
   # PERT-BBY: generate msids (uniq ids)
   $this->msids;
 
   return $this;
 }
 
+# Split mail with the "Split Message Function List parameter"
+sub SplitMail {
+  my( $this )=@_;
+  my @partsarray=();
+  if( MailScanner::Config::Value('splitfunctions') ) {
+    for my $funcname (split(/\s*,\s*/,MailScanner::Config::Value('splitfunctions'))){
+      if ($funcname) {
+        $funcname = 'MailScanner::CustomConfig::' . $funcname;
+        no strict 'refs';
+        push @partsarray, [ &$funcname($this) ];
+        use strict 'refs';
+        #return $result;
+      }
+    }
+    # A: Here we have ( [ 1 1 0 1 0 ] [ 1 1 1 0 0 ] ... ), one per splitfunction
+    # A: We want to join the users who can be processed together
+    if( (my @splitted=$this->PartsArray2To( @partsarray )) > 1 ) {
+      $this->{store}->WriteSplittedByDestination( $this, @splitted );
+      return 1;
+    }
+  }
+  return 0;
+}
+
+## Toma un array de arrays anonimos de 1 y 0. Una linea por cada 
+# pregunta (tiene antivirus?, antispan?), y un 0 o un 1 para cada
+# "To" que haya en el mail, y devuelve una array de arrays anonimos
+# con grupos de "To"s, en los que hay que cortar el mail con la
+# funcion WriteSplittedByDestination
+# El algoritmo de separacion de los mails es el siguiente:
+# Dado el array:
+# ([a_11 a_12 ... a_1in] [a_21 a_22... a_2in] [a_jn1 a_jn2 ...a_jnin])
+# Hago las sumatorias de a_j para cada i
+# Y en base a eso, separo los mails en:
+# a) Todos los que dan jn  (toda la columna en 1)
+# b) Todos los que dan 0
+# c) Todos los demas por separado
+sub PartsArray2To {
+  my( $this, @partsarray )=@_;
+
+  my %distintos=();
+  my $iN=0;
+  map({$iN=@$_ if(@$_ > $iN );} @partsarray);
+  for( my $i=0; $i<$iN;$i++) {
+    my $key="";
+    for( my $j=0; $j<@partsarray;$j++) {
+      $key.= $partsarray[$j][$i] . "X";
+    }
+    push @{$distintos{$key}}, $this->{to}[$i];
+  }
+
+  return values(%distintos);
+}
 
 # Take an email address. Return (user, domain).
 sub address2userdomain {
diff -Naur MailScanner-4.54.1.auth/lib/MailScanner/ZMDiskStore.pm MailScanner-4.54.1/lib/MailScanner/ZMDiskStore.pm
--- MailScanner-4.54.1.auth/lib/MailScanner/ZMDiskStore.pm	Wed May 10 13:59:40 2006
+++ MailScanner-4.54.1/lib/MailScanner/ZMDiskStore.pm	Thu May 11 16:59:17 2006
@@ -250,6 +250,68 @@
   MailScanner::Log::InfoLog("ZM: message %s renamed into %s",$message->{id},$message->{newid});
 }
 
+# Write n messges in the inqueue, filtering destination address
+# @destination_list is a list of references to lists
+# If there are n elements in @destination_list, it will
+# make n new archives, and delete the original one
+# Performance tip: Call this function only if there are
+# multiple types of destinations
+sub WriteSplittedByDestination {
+	my($this, $message, @destination_list) = @_;
+
+	my($tfile, $Tf);
+
+	my @original_metadata= @{$message->{metadata}};
+	my $archive_number=1;
+	for my $array_destination (@destination_list) {
+		# Borro todos los mails que estan en todos 
+		my @borrar=map({@$_} @destination_list);
+		for my $to (@$array_destination) {
+			@borrar= grep( {$_ ne $to} @borrar);
+		}
+		
+		$message->{metadata}=[@original_metadata];
+		for my $to (@borrar) {
+			$global::MS->{mta}->DeleteRecipients($message,$to);
+		}
+		$tfile = $this->{dir} . '/' . $this->{tname} . "-" . $archive_number++;
+
+		umask 0077; # Add this to try to stop 0666 qf files
+		$Tf = new FileHandle;
+		MailScanner::Lock::openlock($Tf, ">$tfile", "w")
+			or MailScanner::Log::DieLog("Cannot create + lock clean tempfile %s, %s",
+																	$tfile, $!);
+
+		# Esto no sirve por que WriteEntireMessage, escribe desde el env-end
+		# y CreateQf,  escribe \n \n al final
+		#$global::MS->{mta}->AddHeadersToQf($message);
+		#MailScanner::Sendmail::CreateQf($message))
+
+		unless( grep( /^env-end$|^env-eof$/i, @{$message->{metadata}} ) ) {
+			push @{$message->{metadata}}, 'env-end';
+		}
+		$Tf->print( join("\n", @{$message->{metadata}}) . "\n" )
+					or MailScanner::Log::DieLog("Failed to write headers for unscanned " .
+																			"message %s, %s", $message->{id}, $!);
+
+		$this->WriteEntireMessage( $message, $Tf );
+
+		MailScanner::Lock::unlockclose($Tf);
+
+		my $newid = MailScanner::Sendmail::HDOutFileName($tfile);
+		my $hdoutfile=$tfile;
+		$message->{newid} = $newid;
+		$hdoutfile =~ s/[^\/]+$/$newid/;
+		#print STDERR "tfile = $tfile and hdoutfile = $hdoutfile\n";
+		rename "$tfile", "$hdoutfile"
+							or MailScanner::Log::DieLog("Cannot split clean %s to %s, %s",
+																					$tfile, $hdoutfile, $!);
+		MailScanner::Log::InfoLog("ZM: message %s splitted into %s",$message->{id},$message->{newid});
+	}
+	$this->DeleteUnlock();
+
+}
+
 
 # Return the size of the message (Header+body)
 #REVISO LEOH




-------------- next part --------------
diff -Naur MailScanner-4.54.1.auth/lib/MailScanner/ConfigDefs.pl MailScanner-4.54.1/lib/MailScanner/ConfigDefs.pl
--- MailScanner-4.54.1.auth/lib/MailScanner/ConfigDefs.pl	Thu May 11 15:05:36 2006
+++ MailScanner-4.54.1/lib/MailScanner/ConfigDefs.pl	Thu May 11 16:59:17 2006
@@ -179,6 +179,7 @@
 spamstarscharacter		= spamscorecharacter
 spamstarsheader			= spamscoreheader
 spamwhitelist			= isdefinitelynotspam
+splitfunctions			= splitmessagefunctionlist
 storedcontentmessage		= storedbadcontentmessagereport
 storedfilenamemessage		= storedbadfilenamemessagereport
 storedvirusmessage		= storedvirusmessagereport
@@ -483,6 +484,7 @@
 MCPSubjectText			{Restricted?}
 SpamSubjectText			{SPAM?}
 SpamStarsHeader			X-MailScanner-SpamScore:
+SplitFunctions		
 MCPHeader			X-MailScanner-MCPCheck:
 IdHeader			X-MailScanner-Id:
 UnscannedHeader			Not scanned: please contact your Internet E-Mail Service Provider for details
diff -Naur MailScanner-4.54.1.auth/lib/MailScanner/Message.pm MailScanner-4.54.1/lib/MailScanner/Message.pm
--- MailScanner-4.54.1.auth/lib/MailScanner/Message.pm	Thu May 11 15:05:36 2006
+++ MailScanner-4.54.1/lib/MailScanner/Message.pm	Thu May 11 16:59:17 2006
@@ -244,12 +244,69 @@
 
   bless $this, $type;
 
+  if( $this->SplitMail($this) ) {
+    return undef;
+  }
+
   # PERT-BBY: generate msids (uniq ids)
   $this->msids;
 
   return $this;
 }
 
+# Split mail with the "Split Message Function List parameter"
+sub SplitMail {
+  my( $this )=@_;
+  my @partsarray=();
+  if( MailScanner::Config::Value('splitfunctions') ) {
+    for my $funcname (split(/\s*,\s*/,MailScanner::Config::Value('splitfunctions'))){
+      if ($funcname) {
+        $funcname = 'MailScanner::CustomConfig::' . $funcname;
+        no strict 'refs';
+        push @partsarray, [ &$funcname($this) ];
+        use strict 'refs';
+        #return $result;
+      }
+    }
+    # A: Here we have ( [ 1 1 0 1 0 ] [ 1 1 1 0 0 ] ... ), one per splitfunction
+    # A: We want to join the users who can be processed together
+    if( (my @splitted=$this->PartsArray2To( @partsarray )) > 1 ) {
+      $this->{store}->WriteSplittedByDestination( $this, @splitted );
+      return 1;
+    }
+  }
+  return 0;
+}
+
+## Toma un array de arrays anonimos de 1 y 0. Una linea por cada 
+# pregunta (tiene antivirus?, antispan?), y un 0 o un 1 para cada
+# "To" que haya en el mail, y devuelve una array de arrays anonimos
+# con grupos de "To"s, en los que hay que cortar el mail con la
+# funcion WriteSplittedByDestination
+# El algoritmo de separacion de los mails es el siguiente:
+# Dado el array:
+# ([a_11 a_12 ... a_1in] [a_21 a_22... a_2in] [a_jn1 a_jn2 ...a_jnin])
+# Hago las sumatorias de a_j para cada i
+# Y en base a eso, separo los mails en:
+# a) Todos los que dan jn  (toda la columna en 1)
+# b) Todos los que dan 0
+# c) Todos los demas por separado
+sub PartsArray2To {
+  my( $this, @partsarray )=@_;
+
+  my %distintos=();
+  my $iN=0;
+  map({$iN=@$_ if(@$_ > $iN );} @partsarray);
+  for( my $i=0; $i<$iN;$i++) {
+    my $key="";
+    for( my $j=0; $j<@partsarray;$j++) {
+      $key.= $partsarray[$j][$i] . "X";
+    }
+    push @{$distintos{$key}}, $this->{to}[$i];
+  }
+
+  return values(%distintos);
+}
 
 # Take an email address. Return (user, domain).
 sub address2userdomain {
diff -Naur MailScanner-4.54.1.auth/lib/MailScanner/ZMDiskStore.pm MailScanner-4.54.1/lib/MailScanner/ZMDiskStore.pm
--- MailScanner-4.54.1.auth/lib/MailScanner/ZMDiskStore.pm	Wed May 10 13:59:40 2006
+++ MailScanner-4.54.1/lib/MailScanner/ZMDiskStore.pm	Thu May 11 16:59:17 2006
@@ -250,6 +250,68 @@
   MailScanner::Log::InfoLog("ZM: message %s renamed into %s",$message->{id},$message->{newid});
 }
 
+# Write n messges in the inqueue, filtering destination address
+# @destination_list is a list of references to lists
+# If there are n elements in @destination_list, it will
+# make n new archives, and delete the original one
+# Performance tip: Call this function only if there are
+# multiple types of destinations
+sub WriteSplittedByDestination {
+	my($this, $message, @destination_list) = @_;
+
+	my($tfile, $Tf);
+
+	my @original_metadata= @{$message->{metadata}};
+	my $archive_number=1;
+	for my $array_destination (@destination_list) {
+		# Borro todos los mails que estan en todos 
+		my @borrar=map({@$_} @destination_list);
+		for my $to (@$array_destination) {
+			@borrar= grep( {$_ ne $to} @borrar);
+		}
+		
+		$message->{metadata}=[@original_metadata];
+		for my $to (@borrar) {
+			$global::MS->{mta}->DeleteRecipients($message,$to);
+		}
+		$tfile = $this->{dir} . '/' . $this->{tname} . "-" . $archive_number++;
+
+		umask 0077; # Add this to try to stop 0666 qf files
+		$Tf = new FileHandle;
+		MailScanner::Lock::openlock($Tf, ">$tfile", "w")
+			or MailScanner::Log::DieLog("Cannot create + lock clean tempfile %s, %s",
+																	$tfile, $!);
+
+		# Esto no sirve por que WriteEntireMessage, escribe desde el env-end
+		# y CreateQf,  escribe \n \n al final
+		#$global::MS->{mta}->AddHeadersToQf($message);
+		#MailScanner::Sendmail::CreateQf($message))
+
+		unless( grep( /^env-end$|^env-eof$/i, @{$message->{metadata}} ) ) {
+			push @{$message->{metadata}}, 'env-end';
+		}
+		$Tf->print( join("\n", @{$message->{metadata}}) . "\n" )
+					or MailScanner::Log::DieLog("Failed to write headers for unscanned " .
+																			"message %s, %s", $message->{id}, $!);
+
+		$this->WriteEntireMessage( $message, $Tf );
+
+		MailScanner::Lock::unlockclose($Tf);
+
+		my $newid = MailScanner::Sendmail::HDOutFileName($tfile);
+		my $hdoutfile=$tfile;
+		$message->{newid} = $newid;
+		$hdoutfile =~ s/[^\/]+$/$newid/;
+		#print STDERR "tfile = $tfile and hdoutfile = $hdoutfile\n";
+		rename "$tfile", "$hdoutfile"
+							or MailScanner::Log::DieLog("Cannot split clean %s to %s, %s",
+																					$tfile, $hdoutfile, $!);
+		MailScanner::Log::InfoLog("ZM: message %s splitted into %s",$message->{id},$message->{newid});
+	}
+	$this->DeleteUnlock();
+
+}
+
 
 # Return the size of the message (Header+body)
 #REVISO LEOH


More information about the MailScanner mailing list