View | Details | Raw Unified | Return to bug 2536
Collapse All | Expand All

(-)spamd.raw.orig (-79 / +148 lines)
Lines 449-458 logmsg("server started on $listeninfo (r Link Here
449
my $current_user;
449
my $current_user;
450
my $client;
450
my $client;
451
451
452
# qmail vpopmail support
453
my $assign = "/var/qmail/users/assign";    # where to find domain to user mappings
454
my (%qmailu, $qu_load);
455
456
my $readvec = "";
452
my $readvec = "";
457
my %pipes = ();
453
my %pipes = ();
458
vec($readvec, $server->fileno, 1) = 1;
454
vec($readvec, $server->fileno, 1) = 1;
Lines 874-897 sub auth_ident Link Here
874
870
875
sub handle_user
871
sub handle_user
876
{
872
{
877
    my $username = shift;
873
    my($username) = @_;
878
874
879
    #
875
    my $userid = $username;
880
    # If vpopmail config enabled then look up userinfo for vpopmail uid
876
    # If vpopmail config enabled then look up userinfo for vpopmail uid
881
    # as defined by $opt{'username'} or as passed via $username
877
    # as defined by $opt{'username'}.
882
    #
878
    if ($opt{'vpopmail'}) {
883
    my $userid = '';
879
        $userid = $opt{'username'} || 'vpopmail';
884
    if ($opt{'vpopmail'} && $opt{'username'}) {
885
	$userid = $opt{'username'};
886
    } elsif ( $opt{'vpopmail'} ) {
887
        $userid = "vpopmail";
888
    } else {
889
	$userid = $username;
890
    }
880
    }
891
    my ($name,$pwd,$uid,$gid,$quota,$comment,$gcos,$dir,$etc) =
892
	getpwnam($userid);
893
881
894
    if ( !$spamtest->{'paranoid'} && !defined($uid) ) {
882
    # Get UID, GID and home directory; those aren't tainted according to
883
    # perlsec.
884
    my($uid, $gid, $userhome) = (getpwnam($userid))[2, 3, 7];
885
886
    if (!defined($uid) && !$spamtest->{'paranoid'}) {
895
	#if we are given a username, but can't look it up,
887
	#if we are given a username, but can't look it up,
896
	#Maybe NIS is down? lets break out here to allow
888
	#Maybe NIS is down? lets break out here to allow
897
	#them to get 'defaults' when we are not running paranoid.
889
	#them to get 'defaults' when we are not running paranoid.
Lines 899-951 sub handle_user Link Here
899
	return 0;
891
	return 0;
900
    }
892
    }
901
893
902
    # not sure if this is required, the doco says it isn't
903
    $uid =~ /^(\d+)$/ and $uid = $1;	# de-taint
904
    $gid =~ /^(\d+)$/ and $gid = $1;	# de-taint
905
906
    if ($setuid_to_user) {
894
    if ($setuid_to_user) {
907
	$) = "$gid $gid"; # change eGID
895
	$) = "$gid $gid"; # change eGID
908
	$> = $uid; # change eUID
896
	$> = $uid; # change eUID
909
        if ( !defined($uid) || ($> != $uid and $> != ($uid-2**32))) {
897
        if ( !defined($uid) || ($> != $uid and $> != ($uid-2**32))) {
910
            logmsg "fatal: setuid to $username failed";
898
            logmsg "fatal: setuid to $username failed";
911
	    die;		# make it fatal to avoid security breaches
899
	    die;		# make it fatal to avoid security breaches
912
	} else {
913
	   logmsg "info: setuid to $username succeeded";
914
	}
900
	}
901
        logmsg "info: setuid to $username succeeded";
915
    }
902
    }
916
903
917
    #
904
    # If vpopmail config enabled then set $homedir to virtual homedir
918
    # If vpopmail config enabled then set $dir to virtual homedir
919
    #
920
    if ($opt{'vpopmail'}) {
905
    if ($opt{'vpopmail'}) {
921
	$dir = `$dir/bin/vuserinfo -d $username`;
906
        # TODO: This path should be configurable, too.
922
	chomp ($dir);
907
        my $vuserinfo = File::Spec->catfile(
908
                          $userhome,
909
                          "bin",
910
                          "vuserinfo"
911
                        );
912
        if (-x $vuserinfo) {
913
            $userhome = Mail::SpamAssassin::Util::untaint_file_path(
914
                          qx($vuserinfo -d $username)
915
                        );
916
        }
917
        if ($! || !-x _) {
918
            logmsg "vpopmail: could not execute $vuserinfo: " . (-x _ ? $! : "No such executable");
919
            return 0;
920
        }
923
    }
921
    }
924
    my $cf_file = $dir."/.spamassassin/user_prefs";
925
922
926
    #
923
    my $cf_file = File::Spec->catfile(
927
    # If vpopmail config enabled then pass virtual homedir onto create_default_cf_needed
924
                    $userhome,
928
    #
925
                    ".spamassassin",
929
    if ($opt{'vpopmail'}) {
926
                    "user_prefs"
930
	if ($opt{'username'}) {
927
                  );
931
	    create_default_cf_if_needed ($cf_file, $username, $dir);
928
    if (!$opt{'vpopmail'}) {
932
	    $spamtest->read_scoreonly_config ($cf_file);
929
        create_default_cf_if_needed(
933
	    $spamtest->signal_user_changed ({ username => $username,
930
          $cf_file,
934
					    user_dir => "$dir" });
931
          $username,
935
	} else {
932
        );
936
	    my $sysnam = get_user_from_address ($username);
933
    }
937
	    $spamtest->read_scoreonly_config ($cf_file);
934
    else {
938
	    $spamtest->signal_user_changed ({ username => $sysnam,
935
        $username = map_address_to_vpopmail_user($username);
939
					    user_dir => "$dir" })
936
        if (!$username) {
937
            logmsg "vpopmail: no user for that address";
938
            return 0;
940
	}
939
	}
941
940
942
    } else {
941
        # If vpopmail config enabled then pass virtual homedir onto
943
	create_default_cf_if_needed ($cf_file, $username);
942
        # create_default_cf_if_needed
944
	$spamtest->read_scoreonly_config ($cf_file);
943
        if ($opt{'username'}) {
945
	$spamtest->signal_user_changed ({ username => $username,
944
            create_default_cf_if_needed(
946
					    user_dir => $dir });
945
              $cf_file,
946
              $username,
947
              $userhome, # this parameter is only set for vpopmail...
948
            );
949
        }
947
    }
950
    }
948
951
952
    $spamtest->read_scoreonly_config($cf_file);
953
    $spamtest->signal_user_changed({
954
      username => $username,
955
      user_dir => $userhome,
956
    });
957
949
    return 1;
958
    return 1;
950
}
959
}
951
960
Lines 1083-1129 sub create_default_cf_if_needed { Link Here
1083
    }
1092
    }
1084
}
1093
}
1085
1094
1086
sub get_user_from_address {
1095
1087
   my ($user, $domain) = split(/@/, $_[0]);
1096
sub map_address_to_vpopmail_user {
1088
   my $dom = lc($domain);
1097
  my($user, $domain) = split(/@/, $_[0]);
1089
1098
1090
   if ( $qmailu{$dom} ne "" ) {
1099
  if ($domain) {
1091
      warn "returning result from cache.\n" if ($opt{'debug'});
1100
    my $uid = query_qmail_assign_cache($domain);
1092
      my $nam = getpwuid($qmailu{$dom});
1101
    if (!defined ($uid)) {
1093
      return $nam;
1102
      warn "vpopmail: no uid for domain $domain" if $opt{'debug'};
1094
   } else {
1103
      return undef;
1095
      warn "cache miss\n" if ($opt{'debug'});
1096
      &fill_qmailu_cache($assign);
1097
      if ( $qmailu{$dom} ne "" ) {
1098
         my $nam = getpwuid($qmailu{$dom});
1099
         return $nam;
1100
      } else {
1104
      } else {
1101
         return 0;
1105
      warn "vpopmail: domain $domain maps to uid $uid" if $opt{'debug'};
1102
      }
1106
      }
1107
    $user = getpwuid($uid);
1103
   }
1108
   }
1109
1110
  return $user || undef;
1104
}
1111
}
1105
1112
1106
sub fill_qmailu_cache {
1113
{
1107
   # rather than parsing the qmail users/assign file every time a message
1114
  # Where to find domain to user mappings
1115
  # TODO: This should be made configurable some day
1116
  my $qmail_assign_file  = "var/qmail/users/assign";
1117
  # The cache hash and its timestamp.
1118
  my %qmail_assign_cache = ();
1119
  my $qmail_assign_cache = 0;
1120
1121
  sub query_qmail_assign_cache {
1122
    my($user) = @_;
1123
1124
    # Rather than parsing the qmail users/assign file every time a message
1108
   # arrives, we run this once when spamd is loaded and check the files
1125
   # arrives, we run this once when spamd is loaded and check the files
1109
   # modified time each query to know when to reload it.
1126
   # modified time each query to know when to reload it.
1127
    my($timestamp) = (stat($_[0]))[9];
1128
    if ($timestamp > $qmail_assign_cache) {
1129
      warn "qmail: (re-)caching qmail-users assign file $qmail_assign_file..." if $opt{'debug'};
1130
      if (fill_qmail_assign_cache()) {
1131
        warn "qmail: done."   if $opt{'debug'};
1132
      } else {
1133
        warn "qmail: failed." if $opt{'debug'};
1134
      }
1135
    }
1136
    else {
1137
      warn "qmail: qmail-users assign cache up-to-date." if $opt{'debug'};
1138
    }
1110
        
1139
        
1111
   my ($READ, $WRITE) = (stat($_[0]))[8,9];
1140
    $user = lc($user);
1112
   if ( $WRITE > $qu_load ) {       
1141
    return $qmail_assign_cache{$user} || undef;
1113
      undef %qmailu;
1142
  }
1114
      $qu_load = time;       
1143
1115
      open(ASSIGN, $_[0]) || die "couldn't open $_[0]: $!\n";
1144
  sub fill_qmail_assign_cache {
1116
         warn "loading $_[0] into cache...." if ($opt{'debug'});
1145
    %qmail_assign_cache = ();
1117
         while(<ASSIGN>) {
1146
1118
            my @data = split(/:/, $_);
1147
    # From qmail-users(5):
1119
            $qmailu{$data[1]} = $data[2];
1148
    # | The  file /var/qmail/users/assign assigns addresses to users. For exam-
1120
         };
1149
    # | ple,
1121
         warn "done.\n" if ($opt{'debug'});
1150
    # |
1122
      close(ASSIGN);
1151
    # |      =joe.shmoe:joe:503:78:/home/joe:::
1152
    # |
1153
    # | says that mail for joe.shmoe should be delivered to user joe, with uid
1154
    # | 503 and gid 78, as specified by /home/joe/.qmail.
1155
    unless (open(ASSIGN, $qmail_assign_file)) {
1156
      logmsg "error: qmail: couldn't open assign file $qmail_assign_file: $!\n";
1157
      return 0;
1158
    }
1159
    my $l = 0;
1160
    while (<ASSIGN>) {
1161
      $l++;
1162
      next if /^\s*#/;
1163
      my @data = split /:/;
1164
1165
      # Untaint the values...
1166
      if (defined($data[1]) && $data[1] =~ /^([a-z0-9.-]+)$/i) {
1167
        $data[1] = lc($1);
1168
      } else {
1169
        logmsg sprintf("warning: qmail: skipping line %i in assign file: invalid value for %s: %s",
1170
                 $l,
1171
                 "user",
1172
                 $data[1] || '(none)',
1173
               );
1174
        next;
1175
      }
1176
      if (defined($data[2]) && $data[2] =~ /^([0-9]+)$/) {
1177
        $data[2] = $1;
1123
   } else {
1178
   } else {
1124
      warn "$_[0] already cached.\n" if ($opt{'debug'});
1179
        logmsg sprintf("warning: qmail: skipping line %i in assign file: invalid value for %s: %s",
1180
                 $l,
1181
                 "uid",
1182
                 $data[2] || '(none)',
1183
               );
1184
        next;
1185
      }
1186
1187
      # ... and store them.
1188
      $qmail_assign_cache{$data[1]} = $data[2];
1189
    }
1190
    close(ASSIGN);
1125
   }
1191
   }
1192
1193
  return 1;
1126
}
1194
}
1195
1127
1196
1128
sub logmsg
1197
sub logmsg
1129
{
1198
{

Return to bug 2536