You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@spamassassin.apache.org by do...@apache.org on 2005/03/11 04:37:04 UTC

svn commit: r157037 - in spamassassin/trunk: lib/Mail/SpamAssassin/EvalTests.pm lib/Mail/SpamAssassin/Plugin/SPF.pm rules/50_scores.cf rules/60_whitelist.cf

Author: dos
Date: Thu Mar 10 19:37:02 2005
New Revision: 157037

URL: http://svn.apache.org/viewcvs?view=rev&rev=157037
Log:
bug 3487: implements whitelist_from_spf & def_whitelist_from_spf

Modified:
    spamassassin/trunk/lib/Mail/SpamAssassin/EvalTests.pm
    spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/SPF.pm
    spamassassin/trunk/rules/50_scores.cf
    spamassassin/trunk/rules/60_whitelist.cf

Modified: spamassassin/trunk/lib/Mail/SpamAssassin/EvalTests.pm
URL: http://svn.apache.org/viewcvs/spamassassin/trunk/lib/Mail/SpamAssassin/EvalTests.pm?view=diff&r1=157036&r2=157037
==============================================================================
--- spamassassin/trunk/lib/Mail/SpamAssassin/EvalTests.pm (original)
+++ spamassassin/trunk/lib/Mail/SpamAssassin/EvalTests.pm Thu Mar 10 19:37:02 2005
@@ -100,6 +100,12 @@
   }
 }
 
+sub check_for_matching_env_and_hdr_from {
+  my ($self) =@_;
+  # two blank headers match so don't bother checking
+  return (lc $self->get('EnvelopeFrom:addr') eq lc $self->get('From:addr'));
+}
+
 sub sorted_recipients {
   my ($self) = @_;
 

Modified: spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/SPF.pm
URL: http://svn.apache.org/viewcvs/spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/SPF.pm?view=diff&r1=157036&r2=157037
==============================================================================
--- spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/SPF.pm (original)
+++ spamassassin/trunk/lib/Mail/SpamAssassin/Plugin/SPF.pm Thu Mar 10 19:37:02 2005
@@ -61,12 +61,62 @@
   $self->register_eval_rule ("check_for_spf_helo_pass");
   $self->register_eval_rule ("check_for_spf_helo_fail");
   $self->register_eval_rule ("check_for_spf_helo_softfail");
+  $self->register_eval_rule ("check_for_spf_whitelist_from");
+  $self->register_eval_rule ("check_for_def_spf_whitelist_from");
+
+  $self->set_config($mailsaobject->{conf});
 
   return $self;
 }
 
 ###########################################################################
 
+sub set_config {
+  my($self, $conf) = @_;
+  my @cmds = ();
+
+=item whitelist_from_spf add@ress.com
+
+Use this to supplement the whitelist_from addresses with a check against the
+domain's SPF record. Aside from the name 'whitelist_from_spf', the syntax is
+exactly the same as the syntax for 'whitelist_from'.
+
+Just like whitelist_from, multiple addresses per line, separated by spaces,
+are OK. Multiple C<whitelist_from_spf> lines are also OK.
+
+The headers checked for whitelist_from_spf addresses are the same headers
+used for SPF checks (Envelope-From, Return-Path, X-Envelope-From, etc).
+
+Since this whitelist requires an SPF check to be made network tests must be
+enabled. It is also required that your trust path be correctly configured.
+See the section on C<trusted_networks> for more info on trust paths.
+
+e.g.
+
+  whitelist_from_spf joe@example.com fred@example.com
+  whitelist_from_spf *@example.com
+
+=item def_whitelist_from_spf add@ress.com
+
+Same as C<whitelist_from_spf>, but used for the default whitelist entries
+in the SpamAssassin distribution.  The whitelist score is lower, because
+these are often targets for spammer spoofing.
+
+=cut
+
+  push (@cmds, {
+    setting => 'whitelist_from_spf',
+    type => $Mail::SpamAssassin::Conf::CONF_TYPE_ADDRLIST
+  });
+
+  push (@cmds, {
+    setting => 'def_whitelist_from_spf',
+    type => $Mail::SpamAssassin::Conf::CONF_TYPE_ADDRLIST
+  });
+
+  $conf->{parser}->register_commands(\@cmds);
+}
+
 # SPF support
 sub check_for_spf_pass {
   my ($self, $scanner) = @_;
@@ -116,6 +166,18 @@
   $scanner->{spf_helo_softfail};
 }
 
+sub check_for_spf_whitelist_from {
+  my ($self, $scanner) = @_;
+  $self->_check_spf_whitelist($scanner) unless $scanner->{spf_whitelist_from_checked};
+  $scanner->{spf_whitelist_from};
+}
+
+sub check_for_def_spf_whitelist_from {
+  my ($self, $scanner) = @_;
+  $self->_check_def_spf_whitelist($scanner) unless $scanner->{def_spf_whitelist_from_checked};
+  $scanner->{def_spf_whitelist_from};
+}
+
 sub _check_spf {
   my ($self, $scanner, $ishelo) = @_;
 
@@ -150,7 +212,7 @@
 
   my $ip = $lasthop->{ip};
   my $helo = $lasthop->{helo};
-  my $sender = '';
+  $scanner->{sender} = '' unless $scanner->{sender_got};
 
   if ($ishelo) {
     dbg("spf: checking HELO (helo=$helo, ip=$ip)");
@@ -163,34 +225,13 @@
     dbg("spf: trimmed HELO down to '$helo'");
 
   } else {
-    $sender = $lasthop->{envfrom};
-
-    if ($sender) {
-      dbg("spf: found Envelope-From in last untrusted Received header");
-    }
-    else {
-      # We cannot use the env-from data, since it went through 1 or
-      # more relays since the untrusted sender and they may have
-      # rewritten it.
-      #
-      if ($scanner->{num_relays_trusted} > 0) {
-	dbg("spf: relayed through one or more trusted relays, cannot use header-based Envelope-From, skipping");
-	return;
-      }
-
-      # we can (apparently) use whatever the current Envelope-From was,
-      # from the Return-Path, X-Envelope-From, or whatever header.
-      # it's better to get it from Received though, as that is updated
-      # hop-by-hop.
-      #
-      $sender = $scanner->get ("EnvelopeFrom");
-    }
+    $self->_get_sender($scanner) unless $scanner->{sender_got};
 
-    if (!$sender) {
-      dbg("spf: cannot get Envelope-From, cannot use SPF");
+    if (!$scanner->{sender}) {
+      # we already dbg'd that we couldn't get an Envelope-From and can't do SPF
       return;
     }
-    dbg("spf: checking EnvelopeFrom (helo=$helo, ip=$ip, envfrom=$sender)");
+    dbg("spf: checking EnvelopeFrom (helo=$helo, ip=$ip, envfrom=$scanner->{sender})");
   }
 
   # this test could probably stand to be more strict, but try to test
@@ -216,7 +257,7 @@
       die "spf: Mail::SPF::Query 1.996 or later required, this is $Mail::SPF::Query::VERSION\n";
     }
     $query = Mail::SPF::Query->new (ip => $ip,
-				    sender => $sender,
+				    sender => $scanner->{sender},
 				    helo => $helo,
 				    debug => Mail::SpamAssassin::dbg_check('+rbl'),
 				    trusted => 0);
@@ -272,7 +313,124 @@
     }
   }
 
-  dbg("spf: query for $sender/$ip/$helo: result: $result, comment: $comment");
+  dbg("spf: query for $scanner->{sender}/$ip/$helo: result: $result, comment: $comment");
+}
+
+sub _get_sender {
+  my ($self, $scanner) = @_;
+  my $sender;
+
+  $scanner->{sender_got} = 1;
+  $scanner->{sender} = '';
+
+  if (exists($scanner->{relays_untrusted}->[0])) {
+    $sender = $scanner->{relays_untrusted}->[0]->{envfrom};
+  }
+
+  if ($sender) {
+    dbg("spf: found Envelope-From in last untrusted Received header");
+  }
+  else {
+    # We cannot use the env-from data, since it went through 1 or more relays 
+    # since the untrusted sender and they may have rewritten it.
+    if ($scanner->{num_relays_trusted} > 0) {
+      dbg("spf: relayed through one or more trusted relays, cannot use header-based Envelope-From, skipping");
+      return;
+    }
+
+    # we can (apparently) use whatever the current Envelope-From was,
+    # from the Return-Path, X-Envelope-From, or whatever header.
+    # it's better to get it from Received though, as that is updated
+    # hop-by-hop.
+    $sender = $scanner->get ("EnvelopeFrom");
+  }
+
+  if (!$sender) {
+    dbg("spf: cannot get Envelope-From, cannot use SPF");
+    return;  # avoid setting $scanner->{sender} to undef
+  }
+
+  return $scanner->{sender} = lc $sender;
+}
+
+sub _check_spf_whitelist {
+  my ($self, $scanner) = @_;
+
+  return unless $scanner->is_dns_available();
+
+  $scanner->{spf_whitelist_from_checked} = 1;
+  $scanner->{spf_whitelist_from} = 0;
+
+  $self->_get_sender($scanner) unless $scanner->{sender_got};
+
+  unless ($scanner->{sender}) {
+    dbg("spf: spf_whitelist_from: could not find useable envelope sender");
+    return;
+  }
+
+  if (defined ($scanner->{conf}->{whitelist_from_spf}->{$scanner->{sender}})) {
+    $scanner->{spf_whitelist_from} = 1;
+  } else {
+    study $scanner->{sender};
+    foreach my $regexp (values %{$scanner->{conf}->{whitelist_from_spf}}) {
+      if ($scanner->{sender} =~ qr/$regexp/i) {
+        $scanner->{spf_whitelist_from} = 1;
+        last;
+      }
+    }
+  }
+
+  # if the message doesn't pass SPF validation, it can't pass an SPF whitelist
+  if ($scanner->{spf_whitelist_from}) {
+    if ($self->check_for_spf_pass($scanner)) {
+      dbg("spf: whitelist_from_spf: $scanner->{sender} is in user's WHITELIST_FROM_SPF and passed SPF check");
+    } else {
+      dbg("spf: whitelist_from_spf: $scanner->{sender} is in user's WHITELIST_FROM_SPF but failed SPF check");
+      $scanner->{spf_whitelist_from} = 0;
+    }
+  } else {
+    dbg("spf: whitelist_from_spf: $scanner->{sender} is not in user's WHITELIST_FROM_SPF");
+  }
+}
+
+sub _check_def_spf_whitelist {
+  my ($self, $scanner) = @_;
+
+  return unless $scanner->is_dns_available();
+
+  $scanner->{def_spf_whitelist_from_checked} = 1;
+  $scanner->{def_spf_whitelist_from} = 0;
+
+  $self->_get_sender($scanner) unless $scanner->{sender_got};
+
+  unless ($scanner->{sender}) {
+    dbg("spf: def_spf_whitelist_from: could not find useable envelope sender");
+    return;
+  }
+
+  if (defined ($scanner->{conf}->{def_whitelist_from_spf}->{$scanner->{sender}})) {
+    $scanner->{def_spf_whitelist_from} = 1;
+  } else {
+    study $scanner->{sender};
+    foreach my $regexp (values %{$scanner->{conf}->{def_whitelist_from_spf}}) {
+      if ($scanner->{sender} =~ qr/$regexp/i) {
+        $scanner->{def_spf_whitelist_from} = 1;
+        last;
+      }
+    }
+  }
+
+  # if the message doesn't pass SPF validation, it can't pass an SPF whitelist
+  if ($scanner->{def_spf_whitelist_from}) {
+    if ($self->check_for_spf_pass($scanner)) {
+      dbg("spf: def_whitelist_from_spf: $scanner->{sender} is in DEF_WHITELIST_FROM_SPF and passed SPF check");
+    } else {
+      dbg("spf: def_whitelist_from_spf: $scanner->{sender} is in DEF_WHITELIST_FROM_SPF but failed SPF check");
+      $scanner->{def_spf_whitelist_from} = 0;
+    }
+  } else {
+    dbg("spf: def_whitelist_from_spf: $scanner->{sender} is not in DEF_WHITELIST_FROM_SPF");
+  }
 }
 
 ###########################################################################

Modified: spamassassin/trunk/rules/50_scores.cf
URL: http://svn.apache.org/viewcvs/spamassassin/trunk/rules/50_scores.cf?view=diff&r1=157036&r2=157037
==============================================================================
--- spamassassin/trunk/rules/50_scores.cf (original)
+++ spamassassin/trunk/rules/50_scores.cf Thu Mar 10 19:37:02 2005
@@ -587,6 +587,12 @@
 score USER_IN_DEF_WHITELIST -15.000
 score USER_IN_BLACKLIST_TO 10.000
 
+ifplugin Mail::SpamAssassin::Plugin::SPF
+score USER_IN_SPF_WHITELIST -100.000
+score USER_IN_DEF_SPF_WL -7.500
+score ENV_AND_HDR_SPF_MATCH -7.500
+endif # Mail::SpamAssassin::Plugin::SPF
+
 # not really false positives but the user wants spam!
 score USER_IN_WHITELIST_TO -6.000
 score USER_IN_MORE_SPAM_TO -20.000

Modified: spamassassin/trunk/rules/60_whitelist.cf
URL: http://svn.apache.org/viewcvs/spamassassin/trunk/rules/60_whitelist.cf?view=diff&r1=157036&r2=157037
==============================================================================
--- spamassassin/trunk/rules/60_whitelist.cf (original)
+++ spamassassin/trunk/rules/60_whitelist.cf Thu Mar 10 19:37:02 2005
@@ -42,6 +42,19 @@
 describe USER_IN_DEF_WHITELIST	From: address is in the default white-list
 tflags USER_IN_DEF_WHITELIST	userconf nice noautolearn
 
+header USER_IN_SPF_WHITELIST	eval:check_for_spf_whitelist_from()
+describe USER_IN_SPF_WHITELIST	From: address is in the user's SPF whitelist
+tflags USER_IN_SPF_WHITELIST	userconf nice noautolearn net
+
+header USER_IN_DEF_SPF_WL	eval:check_for_def_spf_whitelist_from()
+describe USER_IN_DEF_SPF_WL	From: address is in the default SPF white-list
+tflags USER_IN_DEF_SPF_WL	userconf nice noautolearn net
+
+header __ENV_AND_HDR_FROM_MATCH	eval:check_for_matching_env_and_hdr_from()
+meta ENV_AND_HDR_SPF_MATCH	(USER_IN_DEF_SPF_WL && __ENV_AND_HDR_FROM_MATCH)
+describe ENV_AND_HDR_SPF_MATCH	Env and Hdr From used in default SPF WL Match
+tflags ENV_AND_HDR_SPF_MATCH	userconf nice noautolearn net
+
 header USER_IN_BLACKLIST_TO     eval:check_to_in_blacklist()
 describe USER_IN_BLACKLIST_TO   User is listed in 'blacklist_to'
 tflags USER_IN_BLACKLIST_TO     userconf nice noautolearn
@@ -70,68 +83,81 @@
 # Please do not add unmoderated public mailing lists here.  They are
 # too easily abused by spammers.
 
+# we need the SPF plugin to do SPF validated whitelists, so:
+ifplugin Mail::SpamAssassin::Plugin::SPF
+
+def_whitelist_from_spf   *@nytimes.com
+def_whitelist_from_spf   *@amazon.com
+def_whitelist_from_spf   *@*.amazon.com
+def_whitelist_from_spf   *@amazon.co.uk
+def_whitelist_from_spf   *@*.amazon.co.uk
+def_whitelist_from_spf   *@ora.com
+def_whitelist_from_spf   *@*.ora.com
+def_whitelist_from_spf   *@bn.com
+def_whitelist_from_spf   *@mypoints.com
+def_whitelist_from_spf   *@*.mypoints.com
+def_whitelist_from_spf   *@paypal.com
+def_whitelist_from_spf   *@ebay.com
+def_whitelist_from_spf   *@foolsubs.com
+def_whitelist_from_spf   *@match.com
+
+# bugtraq: can contain malicious Javascript etc.
+def_whitelist_from_spf   *@securityfocus.com
+
+def_whitelist_from_spf   *@mediaunspun.imakenews.net
+
+# sender of Cringley newsletter
+def_whitelist_from_spf   *@bdcimail.com
+
+# Silicon.com newslettters - we see thousands of these
+def_whitelist_from_spf   *@silicon.com
+
+# C|Net news.com newsletters
+def_whitelist_from_spf   *@newsletter.online.com
+
+# bug 1348
+def_whitelist_from_spf   *@enews.buy.com
+def_whitelist_from_spf   *@palm.m0.net
+def_whitelist_from_spf   *@handspring.4at1.com
+
+endif # Mail::SpamAssassin::Plugin::SPF
+
+
+# the remaining default whitelists use the received headers and do not require
+# the SPF plugin
+
 def_whitelist_from_rcvd  billing@networksolutions.com           networksolutions.com
 def_whitelist_from_rcvd  outbound-response@networksolutions.com networksolutions.com
 def_whitelist_from_rcvd  hostmaster@internic.net                internic.net
 def_whitelist_from_rcvd  support@register.com                   register.com
-def_whitelist_from_rcvd  nytdirect@nytimes.com                  nytimes.com
-def_whitelist_from_rcvd  petitions@petitiononline.com           petitiononline.com
 
-def_whitelist_from_rcvd  *@amazon.com                           amazon.com
-def_whitelist_from_rcvd  *@*.amazon.com                         amazon.com
-def_whitelist_from_rcvd  *@amazon.co.uk                         amazon.com
-def_whitelist_from_rcvd  *@*.amazon.co.uk                       amazon.com
-def_whitelist_from_rcvd  *@ora.com		                    oreilly.com
-def_whitelist_from_rcvd  *@*.ora.com                            oreilly.com
+def_whitelist_from_rcvd  petitions@petitiononline.com           petitiononline.com
 def_whitelist_from_rcvd  *@walmart.com                          walmart.com
-def_whitelist_from_rcvd  *@bn.com                               bn.com
 def_whitelist_from_rcvd  *@online.telstra.com.au                telstra.com.au
-
 def_whitelist_from_rcvd  *@yahoo-inc.com                        yahoo-inc.com
 def_whitelist_from_rcvd  *@yahoo-inc.com                        yahoo.com
-def_whitelist_from_rcvd  *@mypoints.com                         mypoints.com
-def_whitelist_from_rcvd  *@*.mypoints.com                       mypoints.com
 def_whitelist_from_rcvd  *@orbitz.com                           orbitz.com
-def_whitelist_from_rcvd  *@paypal.com                           paypal.com
-
-# bugtraq: can contain malicious Javascript etc.
-def_whitelist_from_rcvd  *@securityfocus.com                    securityfocus.com
-def_whitelist_from_rcvd  *@LISTSERV.NTBUGTRAQ.COM		    lsoft.com
 
+def_whitelist_from_rcvd  *@LISTSERV.NTBUGTRAQ.COM                  lsoft.com
 def_whitelist_from_rcvd  freshmeat-news-admin@lists.freshmeat.net  freshmeat.net
 def_whitelist_from_rcvd  *@mailer.whitehat.com                     whitehat.com
-def_whitelist_from_rcvd  *@mediaunspun.imakenews.net               imakenews.com
 def_whitelist_from_rcvd  *@spamex.com                              spamex.com
+def_whitelist_from_rcvd  *enotify@usenix.org                       voyager.usenix.org
 
-def_whitelist_from_rcvd  *enotify@usenix.org                    voyager.usenix.org
-
-# sender of Cringley newsletter
-def_whitelist_from_rcvd *@bdcimail.com                          mailcontrol.bellevuedata.com
 # internet.com lists
-def_whitelist_from_rcvd listsupport@internet.com                lyris.net
-
-# Silicon.com newslettters - we see thousands of these
-def_whitelist_from_rcvd *@silicon.com                           lists.nmtv.net
+def_whitelist_from_rcvd  listsupport@internet.com               lyris.net
 
 # FT.com newsletters
-def_whitelist_from_rcvd *@newsbyemail.ft.com                    lodo.exactis.com
-
-# C|Net news.com newsletters
-def_whitelist_from_rcvd *@newsletter.online.com                 cnet.com
+def_whitelist_from_rcvd  *@newsbyemail.ft.com                   lodo.exactis.com
 
 # Friends re-united (popular UK old-school-network)
-def_whitelist_from_rcvd *@friendsreunited.co.uk                 friendsreunited.co.uk
+def_whitelist_from_rcvd  *@friendsreunited.co.uk                friendsreunited.co.uk
 
 # RFC Editor mails
-def_whitelist_from_rcvd rfc-editor@rfc-editor.org		    isi.edu
-
-# Verisign: bug 1056
-def_whitelist_from_rcvd *@*.nsi-direct.com		 	    e-dialog.com
+def_whitelist_from_rcvd  rfc-editor@rfc-editor.org              isi.edu
+def_whitelist_from_rcvd  *@*.nsi-direct.com                     e-dialog.com
 
 # bug 1348
-def_whitelist_from_rcvd *@warehouse.com			    warehouse.com
-def_whitelist_from_rcvd *@enews.buy.com			    enews.buy.com
-def_whitelist_from_rcvd *@palm.m0.net			    palm.m0.net
-def_whitelist_from_rcvd *@handspring.4at1.com		    handspring.4at1.com
-def_whitelist_from_rcvd *@*.efax.com			    efax.com
+def_whitelist_from_rcvd  *@warehouse.com                        warehouse.com
+def_whitelist_from_rcvd  *@*.efax.com                           efax.com