You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@spamassassin.apache.org by jm...@apache.org on 2004/06/19 04:03:14 UTC

svn commit: rev 21433 - in incubator/spamassassin/trunk: lib/Mail/SpamAssassin lib/Mail/SpamAssassin/Conf rules

Author: jm
Date: Fri Jun 18 19:03:13 2004
New Revision: 21433

Modified:
   incubator/spamassassin/trunk/lib/Mail/SpamAssassin/Conf.pm
   incubator/spamassassin/trunk/lib/Mail/SpamAssassin/Conf/Parser.pm
   incubator/spamassassin/trunk/lib/Mail/SpamAssassin/Dns.pm
   incubator/spamassassin/trunk/lib/Mail/SpamAssassin/EvalTests.pm
   incubator/spamassassin/trunk/lib/Mail/SpamAssassin/Reporter.pm
   incubator/spamassassin/trunk/lib/Mail/SpamAssassin/Util.pm
   incubator/spamassassin/trunk/rules/20_html_tests.cf
   incubator/spamassassin/trunk/rules/20_ratware.cf
Log:
allow_user_rules fixes: block user-defined SenderBase rules; sanitise arguments to many regexp rules; run helper apps as uid=user euid=user; ensure regexps are valid before they are used in the ruleset. fix a couple of rules to work with these changes.

Modified: incubator/spamassassin/trunk/lib/Mail/SpamAssassin/Conf.pm
==============================================================================
--- incubator/spamassassin/trunk/lib/Mail/SpamAssassin/Conf.pm	(original)
+++ incubator/spamassassin/trunk/lib/Mail/SpamAssassin/Conf.pm	Fri Jun 18 19:03:13 2004
@@ -3086,6 +3086,7 @@
 
   $self->{rewrite_header} = { };
   $self->{user_rules_to_compile} = { };
+  $self->{user_defined_rules} = { };
   $self->{headers_spam} = { };
   $self->{headers_ham} = { };
 

Modified: incubator/spamassassin/trunk/lib/Mail/SpamAssassin/Conf/Parser.pm
==============================================================================
--- incubator/spamassassin/trunk/lib/Mail/SpamAssassin/Conf/Parser.pm	(original)
+++ incubator/spamassassin/trunk/lib/Mail/SpamAssassin/Conf/Parser.pm	Fri Jun 18 19:03:13 2004
@@ -625,6 +625,16 @@
     return;
   }
 
+  # all of these rule types are regexps
+  if ($type == $Mail::SpamAssassin::Conf::TYPE_HEAD_TESTS
+        || $type == $Mail::SpamAssassin::Conf::TYPE_BODY_TESTS
+        || $type == $Mail::SpamAssassin::Conf::TYPE_FULL_TESTS
+        || $type == $Mail::SpamAssassin::Conf::TYPE_RAWBODY_TESTS
+        || $type == $Mail::SpamAssassin::Conf::TYPE_URI_TESTS)
+  {
+    return unless $self->is_regexp_valid($name, $text);
+  }
+
   $conf->{tests}->{$name} = $text;
   $conf->{test_types}->{$name} = $type;
   $conf->{tflags}->{$name} ||= '';
@@ -633,6 +643,7 @@
 
   if ($self->{scoresonly}) {
     $conf->{user_rules_to_compile}->{$type} = 1;
+    $conf->{user_defined_rules}->{$name} = 1;
   }
 }
 
@@ -646,6 +657,18 @@
   else {
     # initialize the array, and create one element
     $conf->{regression_tests}->{$name} = [ [$ok_or_fail, $string] ];
+  }
+}
+
+sub is_regexp_valid {
+  my ($self, $name, $re) = @_;
+  if (eval { ("" =~ m{$re}); 1; }) {
+    return 1;
+
+  } else {
+    warn "invalid regexp for rule $name: $re\n";
+    $self->{conf}->{errors}++;
+    return 0;
   }
 }
 

Modified: incubator/spamassassin/trunk/lib/Mail/SpamAssassin/Dns.pm
==============================================================================
--- incubator/spamassassin/trunk/lib/Mail/SpamAssassin/Dns.pm	(original)
+++ incubator/spamassassin/trunk/lib/Mail/SpamAssassin/Dns.pm	Fri Jun 18 19:03:13 2004
@@ -207,6 +207,12 @@
     }
     # senderbase
     elsif ($subtest =~ s/^sb://) {
+      # SB rules are not available to users
+      if ($self->{conf}->{user_defined_rules}->{$rule}) {
+        dbg ("RBL: skipping rule '$rule': not supported when user-defined");
+        next;
+      }
+
       $rdatastr =~ s/^"?\d+-//;
       $rdatastr =~ s/"$//;
       my %sb = ($rdatastr =~ m/(?:^|\|)(\d+)=([^|]+)/g);
@@ -224,11 +230,7 @@
       my $untainted = $1;
       $subtest = $untainted;
 
-      # Mail::SpamAssassin::Util::untaint_var (\%sb);
-      # dbg ("$subtest");
-      # dbg ("$rdatastr");
-
-      $self->got_hit($rule, "SenderBase: ") if !$undef && eval "$subtest";
+      $self->got_hit($rule, "SenderBase: ") if !$undef && eval $subtest;
     }
     # bitmask
     elsif ($subtest =~ /^\d+$/) {
@@ -736,7 +738,12 @@
     }
 
     dbg("DCC command: ".join(' ', $path, "-H", $opts, "< '$tmpf'", "2>&1"),'dcc',-1);
-    my $pid = open(DCC, join(' ', $path, "-H", $opts, "< '$tmpf'", "2>&1", '|')) || die "$!\n";
+
+    # my $pid = open(DCC, join(' ', $path, "-H", $opts, "< '$tmpf'", "2>&1", '|')) || die "$!\n";
+    my $pid = Mail::SpamAssassin::Util::helper_app_pipe_open(*DCC,
+                $tmpf, 1, $path, "-H", split(' ', $opts));
+    $pid or die "$!\n";
+
     my @null = <DCC>;
     close DCC;
 
@@ -869,7 +876,12 @@
     $opts =~ s/[^-A-Za-z0-9 \/_]/_/gs;	# sanitise
  
     dbg("Pyzor command: ".join(' ', $path, $opts, "check", "< '$tmpf'", "2>&1"),'pyzor',-1);
-    my $pid = open(PYZOR, join(' ', $path, $opts, "check", "< '$tmpf'", "2>&1", '|')) || die "$!\n";
+
+    #my $pid = open(PYZOR, join(' ', $path, $opts, "check", "< '$tmpf'", "2>&1", '|')) || die "$!\n";
+    my $pid = Mail::SpamAssassin::Util::helper_app_pipe_open(*PYZOR,
+                $tmpf, 1, $path, split(' ', $opts), "check");
+    $pid or die "$!\n";
+
     $response = <PYZOR>;
     close PYZOR;
 

Modified: incubator/spamassassin/trunk/lib/Mail/SpamAssassin/EvalTests.pm
==============================================================================
--- incubator/spamassassin/trunk/lib/Mail/SpamAssassin/EvalTests.pm	(original)
+++ incubator/spamassassin/trunk/lib/Mail/SpamAssassin/EvalTests.pm	Fri Jun 18 19:03:13 2004
@@ -3113,7 +3113,7 @@
 
   $self->{html}{"inside_$tag"} =~ /^([\<\>\=\!\-\+ 0-9]+)$/;
   my $val = $1;
-  return eval "$val $expr";
+  return eval "\$val $expr";
 }
 
 sub html_image_only {
@@ -3167,7 +3167,8 @@
 }
 
 sub html_eval {
-  my ($self, undef, $test, $expr) = @_;
+  my ($self, undef, $test, $rawexpr) = @_;
+  $rawexpr =~ /^([\<\>\=\!\-\+ 0-9]+)$/; my $expr = $1;
 
   # workaround bug 3320: wierd perl bug where additional, very explicit
   # untainting into a new var is required.
@@ -3175,13 +3176,24 @@
   return unless defined($tainted);
   $tainted =~ /^(.*)$/; my $val = $1;
 
-  return eval "qq{\Q$val\E} $expr";
+  # just use the value in $val, don't copy it needlessly
+  return eval "\$val $expr";
 }
 
-sub html_text {
-  my ($self, undef, $text, $expr) = @_;
+sub html_text_match {
+  my ($self, undef, $text, $regexp) = @_;
   for my $string (@{ $self->{html}{$text} }) {
-    if (defined $string && eval "qq{\Q$string\E} $expr") {
+    if (defined $string && $string =~ /${regexp}/) {
+      return 1;
+    }
+  }
+  return 0;
+}
+
+sub html_text_not_match {
+  my ($self, undef, $text, $regexp) = @_;
+  for my $string (@{ $self->{html}{$text} }) {
+    if (defined $string && $string !~ /${regexp}/) {
       return 1;
     }
   }

Modified: incubator/spamassassin/trunk/lib/Mail/SpamAssassin/Reporter.pm
==============================================================================
--- incubator/spamassassin/trunk/lib/Mail/SpamAssassin/Reporter.pm	(original)
+++ incubator/spamassassin/trunk/lib/Mail/SpamAssassin/Reporter.pm	Fri Jun 18 19:03:13 2004
@@ -286,7 +286,10 @@
       $opts = $1;
     }
 
-    my $pid = open(DCC, join(' ', $path, "-t many", $opts, "< '$tmpf'", ">/dev/null 2>&1", '|')) || die "$!\n";
+    my $pid = Mail::SpamAssassin::Util::helper_app_pipe_open(*DCC,
+                    $tmpf, 1, $path, "-t", "many", split(' ', $opts));
+    $pid or die "$!\n";
+    my @ignored = <DCC>;
     $self->close_pipe_fh (\*DCC);
 
     alarm(0);
@@ -334,7 +337,11 @@
       $opts = $1;
     }
 
-    my $pid = open(PYZOR, join(' ', $path, $opts, "report", "< '$tmpf'", ">/dev/null 2>&1", '|')) || die "$!\n";
+    #my $pid = open(PYZOR, join(' ', $path, $opts, "report", "< '$tmpf'", ">/dev/null 2>&1", '|')) || die "$!\n";
+    my $pid = Mail::SpamAssassin::Util::helper_app_pipe_open(*PYZOR,
+                    $tmpf, 1, $path, split(' ', $opts), "report");
+    $pid or die "$!\n";
+    my @ignored = <PYZOR>;
     $self->close_pipe_fh (\*PYZOR);
 
     alarm(0);

Modified: incubator/spamassassin/trunk/lib/Mail/SpamAssassin/Util.pm
==============================================================================
--- incubator/spamassassin/trunk/lib/Mail/SpamAssassin/Util.pm	(original)
+++ incubator/spamassassin/trunk/lib/Mail/SpamAssassin/Util.pm	Fri Jun 18 19:03:13 2004
@@ -924,6 +924,67 @@
 
 ###########################################################################
 
+sub setuid_to_euid {
+  return if (RUNNING_ON_WINDOWS);
+  if ($< != $>) {
+    dbg ("setting real uid from $< to match effective uid $>");
+    $< = $>;
+    if ($< != $>) { die "setuid $< to $> failed!"; }
+  }
+}
+
+# helper app command-line open
+sub helper_app_pipe_open {
+  if (RUNNING_ON_WINDOWS) {
+    return helper_app_pipe_open_windows (@_);
+  } else {
+    return helper_app_pipe_open_unix (@_);
+  }
+}
+
+sub helper_app_pipe_open_windows {
+  my ($fh, $stdinfile, $duperr2out, @cmdline) = @_;
+
+  # use a traditional open(FOO, "cmd |")
+  my $cmd = join(' ', @cmdline);
+  if ($stdinfile) { $cmd .= " < '$stdinfile'"; }
+  if ($duperr2out) { $cmd .= " 2>&1"; }
+  return open ($fh, $cmd.'|');
+}
+
+sub helper_app_pipe_open_unix {
+  my ($fh, $stdinfile, $duperr2out, @cmdline) = @_;
+
+  # do a fork-open, so we can setuid() back
+  my $pid = open ($fh, '-|');
+  if (!defined $pid) {
+    die "cannot fork: $!";
+  }
+
+  if ($pid != 0) {
+    return $pid;          # parent process; return the child pid
+  }
+
+  # else, child process.  go setuid...
+  setuid_to_euid();
+  dbg ("setuid: helper proc $$: ruid=$< euid=$>");
+
+  if ($stdinfile) {              # < $tmpfile
+    close STDIN;
+    open (STDIN, "<$stdinfile") or die "cannot open $stdinfile: $!";
+  }
+
+  if ($duperr2out) {             # 2>&1
+    close STDERR;
+    open STDERR, ">&STDOUT" or die "dup STDOUT failed: $!";
+  }
+
+  exec @cmdline;
+  die "exec failed: $!";
+}
+
+###########################################################################
+
 sub dbg { Mail::SpamAssassin::dbg (@_); }
 
 1;

Modified: incubator/spamassassin/trunk/rules/20_html_tests.cf
==============================================================================
--- incubator/spamassassin/trunk/rules/20_html_tests.cf	(original)
+++ incubator/spamassassin/trunk/rules/20_html_tests.cf	Fri Jun 18 19:03:13 2004
@@ -82,13 +82,13 @@
 describe HTML_TABLE_THICK_BORD	HTML table has thick border
 
 # HTML comment tests
-body HTML_COMMENT_SHORT		eval:html_text('comment', '=~ /<!(?!-).{0,6}>/')
+body HTML_COMMENT_SHORT		eval:html_text_match('comment', '<!(?!-).{0,6}>')
 describe HTML_COMMENT_SHORT	HTML comment is very short
 
 body HTML_COMMENT_SHOUTING	eval:html_test('comment_shouting')
 describe HTML_COMMENT_SHOUTING	HTML comment inside of "shouting" markup
 
-body HTML_COMMENT_SAVED_URL	eval:html_text('comment', '=~ /<!-- saved from url=\(\d{4}\)/')
+body HTML_COMMENT_SAVED_URL	eval:html_text_match('comment', '<!-- saved from url=\(\d{4}\)')
 describe HTML_COMMENT_SAVED_URL	HTML message is a saved web page
 
 # Comment is a spam sign when following <DIV>
@@ -170,13 +170,13 @@
 body HTML_JAVASCRIPT		eval:html_test('javascript')
 describe HTML_JAVASCRIPT	JavaScript code
 
-body HTML_LINK_PUSH_HERE	eval:html_text('anchor', '=~ /(?:push|go)\s*(?:here|this)/i')
+body HTML_LINK_PUSH_HERE	eval:html_text_match('anchor', '(?i)(?:push|go)\s*(?:here|this)')
 describe HTML_LINK_PUSH_HERE	HTML link text says "push here" or similar
 
-body HTML_LINK_CLICK_HERE	eval:html_text('anchor', '=~ /click\s*(?:here|this)/i')
+body HTML_LINK_CLICK_HERE	eval:html_text_match('anchor', '(?i)click\s*(?:here|this)')
 describe HTML_LINK_CLICK_HERE	HTML link text says "click here"
 
-body HTML_LINK_CLICK_CAPS	eval:html_text('anchor', '=~ /CLICK/')
+body HTML_LINK_CLICK_CAPS	eval:html_text_match('anchor', 'CLICK')
 describe HTML_LINK_CLICK_CAPS	HTML link text says "CLICK"
 
 # HTML obfuscation
@@ -290,7 +290,7 @@
 describe HTML_SHORT_LENGTH	HTML is extremely short
 
 body __HTML_LENGTH_512		eval:html_eval('length', '< 512')
-body __COMMENT_EXISTS		eval:html_text('comment', '=~ /<!.*?>/')
+body __COMMENT_EXISTS		eval:html_text_match('comment', '<!.*?>')
 meta HTML_SHORT_COMMENT		(__HTML_LENGTH_512 && __COMMENT_EXISTS)
 describe HTML_SHORT_COMMENT	HTML is very short with HTML comments
 
@@ -299,13 +299,13 @@
 meta HTML_SHORT_CENTER		(__HTML_LENGTH_384 && __TAG_EXISTS_CENTER)
 describe HTML_SHORT_CENTER	HTML is very short with CENTER tag
 
-body HTML_TITLE_EMPTY		eval:html_text('title', '!~ /\S/s')
+body HTML_TITLE_EMPTY		eval:html_text_not_match('title', '(?s)\S')
 describe HTML_TITLE_EMPTY	HTML title contains no text
 
 body HTML_TITLE_EXTRA		eval:html_test('title_extra')
 describe HTML_TITLE_EXTRA	HTML has more than one title
 
-body HTML_TITLE_UNTITLED	eval:html_text('title', '=~ /untitled|new page \d+/i')
+body HTML_TITLE_UNTITLED	eval:html_text_match('title', '(?i)(?:untitled|new page \d+)')
 describe HTML_TITLE_UNTITLED	HTML title contains "Untitled"
 
 ###########################################################################

Modified: incubator/spamassassin/trunk/rules/20_ratware.cf
==============================================================================
--- incubator/spamassassin/trunk/rules/20_ratware.cf	(original)
+++ incubator/spamassassin/trunk/rules/20_ratware.cf	Fri Jun 18 19:03:13 2004
@@ -133,8 +133,8 @@
 
 # From private mail with developers.  Some top tips here!
 header __THEBAT_MUA		X-Mailer =~ /The Bat!/
-header __THEBAT_MUA_V1		X-Mailer =~ /^\QThe Bat! (v1.\E/
-header __THEBAT_MUA_V2		X-Mailer =~ /^\QThe Bat! (v2.\E/
+header __THEBAT_MUA_V1		X-Mailer =~ /^The Bat! \(v1\./
+header __THEBAT_MUA_V2		X-Mailer =~ /^The Bat! \(v2\./
 header __CTYPE_CHARSET_QUOTED	Content-Type =~ /charset=\"/i
 header __CTYPE_HAS_BOUNDARY	Content-Type =~ /boundary/i
 header __BAT_BOUNDARY		Content-Type =~ /boundary=\"?-{10}/