Hi Justin,

Justin Mason wrote:

+      no re 'taint';
+      my $username = ( $username =~ /^([-:,[EMAIL PROTECTED])$/ ? $1 : undef );

A better way to do that is to leave taint active, and instead do this:

     if ($username =~ /^([-:,[EMAIL PROTECTED])$/) {
       # note, separate variable for untainted value
$untainted_user = $1; # ...use that
     }

it's a minor difference, but it allows taint to stay on inside the rest
of that scope.

not sure if your concern is the "no re 'taint'" or overwriting $username, but I agree that both isn't exactly elegant. Therefore I'm now enabling "re 'taint'" after using the untainted $1. Also variables were split up as suggested, although I inverted the naming for $vpopusername + $vpopusername_tainted. IMO that's ok, as the scope of both is limited to handle_user_vpopmail().

regarding the use of the timeout:

+  $vptimer->run(sub {
+    my $vpopusername = $username;
+    local $SIG{ALRM} = sub { die "failed to resolve vpopmail user/alias '$username' 
in time ($vpoptimeout seconds)" };
+    do {
+      $vpopusername = `$vpopdir/bin/valias \Q$vpopusername\E`;
+      if ($vpopusername =~ /.+ -> &?(.+)/) {
+        $vpopusername = $1;

it's important that $SIG{ALRM} not be modified inside the timeout scope --
since the Timeout class uses it.  Instead I recommend calling that die()
after the run() call, in the timed_out() action.

And I also removed the alarm 0; I forgot...

Thanks for commenting,

Cheers, Daniel
Index: spamd/spamd.raw
===================================================================
--- spamd/spamd.raw     (revision 673160)
+++ spamd/spamd.raw     (working copy)
@@ -2084,6 +2084,18 @@

     if ($prefsfrom eq $suidto) {
       $userdir = $suiddir;      # reuse the already-looked-up info
+    } elsif ( $opt{'vpopmail'} ) {
+      #
+      # If vpopmail config enabled then set $userdir to virtual homedir
+      #
+      no re 'taint';
+      my $username_untainted = ( $username =~ /^([-:,[EMAIL PROTECTED])$/ ? $1 : undef );
+      use re 'taint';
+      my $vpopdir = $suiddir; # This should work with common vpopmail setups
+      $userdir = `$vpopdir/bin/vuserinfo -d \Q$username_untainted\E`;
+      if ($? != 0) {
+        $userdir = handle_user_vpopmail($username_untainted,$vpopdir);
+      }
     } else {
       $userdir = (getpwnam($prefsfrom))[7];
     }
@@ -2105,28 +2117,44 @@
   handle_user_set_user_prefs($userdir, $username);
 }

-sub handle_user_set_user_prefs {
-  my ($dir, $username) = @_;
+sub handle_user_vpopmail {
+  #
+  # 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 });

-  # If vpopmail config enabled then set $dir to virtual homedir
-  #
-  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,;
+  $vptimer->run(sub {
+    my $vpopusername = $username;
+    do {
+      my $vpopusername_tainted = `$vpopdir/bin/valias \Q$vpopusername\E`;
+      no re 'taint';
+      if ($vpopusername_tainted =~ /.+ -> &?(.+)/) {
+        $vpopusername = $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) {
     my $cf_file = $dir . "/.spamassassin/user_prefs";

Reply via email to