Download | Plain Text | Line Numbers


--- spamd/spamd.raw.orig	2009-12-15 22:09:03.000000000 +0100
+++ spamd/spamd.raw	2009-12-15 22:15:34.000000000 +0100
@@ -88,6 +88,7 @@
 use Mail::SpamAssassin::SubProcBackChannel;
 use Mail::SpamAssassin::SpamdForkScaling qw(:pfstates);
 use Mail::SpamAssassin::Logger qw(:DEFAULT log_message);
+use Mail::SpamAssassin::Util qw(untaint_var);
 use Mail::SpamAssassin::Timeout;
 
 use Getopt::Long;
@@ -2076,7 +2076,21 @@
     my $prefsfrom = $username;  # the one passed, NOT $opt{username}
 
-    if ($prefsfrom eq $suidto || $opt{'vpopmail'}) {
-      $userdir = $suiddir;      # reuse the already-looked-up info
+    if ($prefsfrom eq $suidto) {
+      $userdir = $suiddir;      # reuse the already-looked-up info, tainted
+    } elsif ( $opt{'vpopmail'} ) {
+      #
+      # If vpopmail config enabled then set $userdir to virtual homedir
+      #
+      my $username_untainted;
+      $username_untainted =
+        untaint_var($username)  if $username =~ /^[-:,.=+A-Za-z0-9_\@~]+\z/;
+      my $vpopdir = $suiddir; # This should work with common vpopmail setups
+      $userdir = `$vpopdir/bin/vuserinfo -d \Q$username_untainted\E`;
+      if ($? == 0) {
+        chomp($userdir);
+      } else {
+        $userdir = handle_user_vpopmail($username_untainted,$vpopdir);
+      }
     } else {
       $userdir = (getpwnam($prefsfrom))[7];
     }
@@ -2095,30 +2108,47 @@
 
   # call this anyway, regardless of --user-config, so that
   # signal_user_changed() is called
-  handle_user_set_user_prefs($userdir, $username);
+  handle_user_set_user_prefs(untaint_var($userdir), $username);
 }
 
-sub handle_user_set_user_prefs {
-  my ($dir, $username) = @_;
-
-  # If vpopmail config enabled then set $dir to virtual homedir
+sub handle_user_vpopmail {
   #
-  if ( $opt{'vpopmail'} ) {
-    my $vpopdir = $dir;
-    $dir = `$vpopdir/bin/vuserinfo -d \Q$username\E`;
-    if ($? != 0) {
-      #
-      # If vuserinfo failed $username could be an alias
-      #
-      $dir = `$vpopdir/bin/valias \Q$username\E`;
-      if ($? == 0 && $dir !~ /.+ -> &/) {
-        $dir =~ s,.+ -> (/.+)/Maildir/,$1,;
+  # If vuserinfo failed $username could be an alias
+  # As the alias could be an alias itself we'll try to resolve it recursively
+  # Because we're mistrusting vpopmail we'll set off an alarm
+  #
+  my $username = shift;
+  my $vpopdir = shift;
+  my $userdir;
+  my $vpoptimeout = 5;
+  my $vptimer = Mail::SpamAssassin::Timeout->new({ secs => $vpoptimeout });
+
+  $vptimer->run(sub {
+    my $vpopusername = $username;
+    local $1;
+    do {
+      my $vpopusername_tainted = `$vpopdir/bin/valias \Q$vpopusername\E`;
+      no re 'taint';
+      if ($vpopusername_tainted =~ /.+ -> &?(.+)/) {
+        $vpopusername = untaint_var($1);
       } else {
-        undef($dir);
+        die "failed to resolve vpopmail user/alias '$username' using vuserinfo/valias";
       }
-    }
-    chomp($dir);
+    } until (($userdir = `$vpopdir/bin/vuserinfo -d \Q$vpopusername\E`) && $? == 0);
+    $userdir =~ s{.+ -> (/.+)/Maildir/}{$1};
+  });
+
+  if ($vptimer->timed_out()) {
+    undef $userdir;
+    die "failed to resolve vpopmail user/alias '$username' in time ($vpoptimeout seconds)";
+  } else {
+    chomp($userdir);
   }
+  return $userdir;
+}
+
+sub handle_user_set_user_prefs {
+  my ($dir, $username) = @_;
 
   # don't do this if we weren't passed a directory
   if ($dir) {
--- lib/Mail/SpamAssassin/Util.pm.orig	2008-06-10 11:20:22.000000000 +0200
+++ lib/Mail/SpamAssassin/Util.pm	2009-12-15 22:17:41.000000000 +0100
@@ -50,7 +50,7 @@
 require Exporter;
 
 @ISA = qw(Exporter);
-@EXPORT = qw(local_tz base64_decode);
+@EXPORT = qw(local_tz base64_decode untaint_var);
 
 use Mail::SpamAssassin;
 use Mail::SpamAssassin::Util::RegistrarBoundaries;