Hello all, Me (with a ton of perl help from sil of www.silenttech.com) wrote this short addition to Larry M Smith's version of checkpassword.pl.
Basically, it reads /etc/mail/virtusertable, and recurses on the email used as the login until it gets to an address that is local. If the user enters no domain it skips the virtusertable search all together. I tried to check for problems in virtusertable and bad input as much as I could. First time ever using perl, and first time writing anything like this, so it could be all messed up. :) I tried to comment heavily, I'm hoping someone here might have some helpful hints or be able to point out any glaring errors. :) Barring those, it does seem to work so far. :) Thanks! Ben
#!/usr/bin/perl # # Code added to this by me with a ton of help from sil of www.silenttech.com. # I could never have figured out all this perl without his help. :-) # # So my modification basically reads through virtusertable and tries to rewrite # # all those aliases down to real addresses. # - ben 03/01/04 # checkpassword.pl # # Larry M. Smith <[EMAIL PROTECTED]> # # # Expects tcpserver environmental variables $TCPLOCALPORT and $TCPREMOTEIP. # See http://cr.yp.to/ucspi-tcp/environment.html # # Provided AS IS and free... It works on my system, your's *MAY* be # different!!! YOU as sysadmin, are expected to TEST everything that # you bring online!!! Also, you may not like the way I log failed # passwords. # # If you see something that could/should be done differently please let me # know. # # # You will need these modules installed on your system. # Please read the respective man pages. # use User::pwent; use Unix::Syslog qw(:macros); use Unix::Syslog qw(:subs); # # Change these to match your system/site polices. # my $MINUID = 500; # We don't want brute force attacks against root, etc. my $EGID = "100 100"; # Don't pass extra groups like wheel, etc. my $RGID = 100; $|=1; my $ipaddr = $ENV{'TCPREMOTEIP'}; my $port = $ENV{'TCPLOCALPORT'}; #%ENV=(); my($len,$buf); open (USER, "<&=3") or exit (-3); $len = read(USER, $buf, 512); close USER; exit(-3) if $len < 4; my($user, $pass) = split /\x00/, $buf; $user = lc $user; $buf = "\x00" x $len; ############### MODIFICATION ############### # === recursive_rewrite function === sub recursive_rewrite { my($user, $localdomain, $count) = @_; if($count > 50) # Number of times to recurse (don't want to run out of memory!! { return $user; # don't recurse forever, that would be bad if something # were to go run and your memory got eaten up # (crashed evolution for me!) } $size = @users; for($x=0; $x<$size; $x++) # loop through virtusertable { ($first, $second) = split(/\s+/, $users[$x], 2); # $first should equal [EMAIL PROTECTED] # If the current line is : [EMAIL PROTECTED] [EMAIL PROTECTED] chomp($second); # Get rid of trailing newlines from virtusertable if($first == $user) # Found our username in virtusertable. { $user = $second; # Set the new user to the match ($garbage, $domain) = split(/@/, $user, 2); # Get domain of user we just found if($domain ne $localdomain) # Then we haven't found a local user # yet, so keep looking! { # Well, we already looked at this match, so clear it from # array This should prevent the looping if [EMAIL PROTECTED] [EMAIL PROTECTED] and [EMAIL PROTECTED] [EMAIL PROTECTED] $users[$x] = ""; # pass the new username back into this function and look for # the next match! $count++; if($user eq "") { exit(-3); } $user = recursive_rewrite($user, $localdomain, $count); } else { # Remove the domain from $user (if there is one) because by now # the user should be local. ($user, $domain) = split(/@/, $user, 2); return $user # Found local user! Might as well return and stop looking # through the array here. } # end of if($domain != $localdomain) } # end of if($first_email_address == $username) } # end of for loop return $user; # looked through all of virtusertable and didn't find a match so # return the original username (should be unchanged if no # match) } # === end of function recursive_rewrite === # === I think this should go here, I'm not sure where the function should go === $localdomain = "drago"; $user, $domain = split(/@/, $user, 2); # Split the login into user and domain if(($domain) && ($domain != $localdomain)) # if they passed in a domain at all AND { # if that domain is not already a local # domain - rewrite recursively open (FILE, "/etc/mail/virtusertable"); foreach $line (<FILE>) { push(@users, $line) unless ($line =~ m/[EMAIL PROTECTED]/); # Get lines from virtusertable } close (FILE); $user = recursive_rewrite($user, $localdomain, 0); # Fire up the recursive function } # === end my added code === ############### MODIFICATION ############### my $pw = getpwnam($user) || err_unknown(); my $uid = $pw->uid; my $phash = $pw->passwd; my $home = $pw->dir; my $shell = $pw->shell; if ($uid < $MINUID) { err_minuid(); } if (crypt($pass, $phash) ne $phash) { err_badpass(); } $ENV{USER}=$user; $ENV{UID}=$uid+0; $ENV{HOME}=$home; $ENV{SHELL}=$shell; exit(-4) unless $ENV{UID}; chdir $ENV{HOME}; $) = $EGID; $( = $RGID; $> = $ENV{UID}; $< = $ENV{UID}; log_pop3(); exec @ARGV; sub err_unknown { # openlog("checkpassword.pl: ", LOG_PID, LOG_MAIL); # syslog(LOG_INFO, "Attempt to login port %d with unknown user (%s) from [%s]", $port, $user, $ipaddr); # closelog; exit(-3); } sub err_minuid{ openlog("checkpassword.pl: ", LOG_PID, LOG_MAIL); # syslog(LOG_INFO, "Attempt to login port %d with UID lt %d (%s) from [%s]",$port, $MINUID, $user, $ipaddr); # closelog; exit(-3); } sub err_badpass{ # openlog("checkpassword.pl: ", LOG_PID, LOG_MAIL); # syslog(LOG_INFO, "Attempt to login port %d failed for UID %d (%s - %s) from [%s] ",$port, $uid, $user, $pass, $ipaddr); # closelog(); exit(-3); } sub log_pop3{ # openlog("checkpassword.pl: ", LOG_PID, LOG_MAIL); # syslog(LOG_INFO, "port %d login successful UID %d (%s) from [%s]",$port, $uid, $user, $ipaddr); # closelog(); } sleep(10); exit(-4);
