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 |
{ |