#!/usr/bin/perl

#
#  Henry Baragar <mailto:Henry.Baragar@Instantiated.Ca>
#       March 10, 2004
# 	Based on a script from Larry M. Smith <chains-chkpass@FahQ2.com>
#       Major change:  Converted from C style to perl style 
#
# Provided AS IS and free... Only partialy tested 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.
#
# See below for the original script.
#

# Site specific information (although I question its use)
$localdomain = "drago";

# Other constants
$MINUID = 500;     # We don't want brute force attacks against root, etc.
$EGID = "100 100"; # Don't pass extra groups like wheel, etc.
$RGID = 100;       

# Authenticate
($user,$pass,$timestamp,@rest) = getuserpass();
($name,$passwd,$uid,$gid,$quota,$comment,$gcos,$dir,$shell,$expire)
    = getpwnam($user=mapuser($user));
password_unacceptable("Login failed for $user\n")
    unless $name;
password_misused("Bad UID ($uid) [user=$user]\n") unless $uid >= $MINUID;
password_unacceptable("Login failed for $user\n")
    unless crypt($pass,$passwd) eq $passwd;

# Set up the environment and run the target program
$ENV{USER}=$user;
$ENV{UID}=$uid+0;
$ENV{HOME}=$dir;
$ENV{SHELL}=$shell;
chdir $ENV{HOME};
temporary_problem("Not root\n") unless $ENV{UID};
$) = $EGID;
$( = $RGID;
$> = $ENV{UID};
$< = $ENV{UID};
print STDERR "$user logged in; running @ARGV\n";
exec @ARGV;


sub getuserpass {
    open THREE, "<&=3"
	or temporary_problem("user name and password not on filenum 3!\n");
    my ($three) = <THREE>;
    close THREE;
    return split /\0/, lc $three;
    }

sub mapuser {
    my ($user) = @_;
    %virtuser = getvirtusers();
    while ($mapping = $virtuser{$user}) {
	delete $virtuser{$user};	# prevent infinite loops
	$user = $mapping;
	# I am not sure why we do this ...
	last if $user =~ /\@$localdomain/;
	}
    $user =~ s/\@$localdomain//;
    return $user;
    }

sub getvirtusers {
    open VIRT, "/etc/mail/virtusertable"
	or temporary_problem(qq{Could not open "/etc/mail/virtusertable\n"});
    # Clean up comments, extraneous white space and empty lines from the file
    my @lines = grep {chomp; s/#.*//; s/^\s+//;s/\s+$//; /./} <VIRT>;
    close VIRT;
    temporary_problem("Aborting due to syntax errors in ",
	    qq{"/etc/mail/virtusertable"\n})
	if grep /\s\S+\s/, @lines;
    return map {split} map {lc} @lines;
    }

sub password_unacceptable {
    print STDERR @_;
    exit 1;
    }

sub password_misused {
    print STDERR @_;
    exit 2;
    }

sub temporary_problem {
    print STDERR @_;
    exit 111;
    }


#############################
#############################
######                  #####
######  ORIGINAL CODE   #####
######                  #####
#############################
#############################
##
##
## 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 <chains-chkpass@FahQ2.com>
##
#
##  
## 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 1@a 
#                                                            # If the current line is : 
#                                                            #1@a 2@a
#        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 1@a 2@a and 2@a 1@a
#                $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/^[\@\#\s]/); # 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);
