File:  [LON-CAPA] / loncom / lcpasswd
Revision 1.11: download - view: text, annotated - select for diffs
Mon Oct 30 03:30:26 2000 UTC (23 years, 7 months ago) by harris41
Branches: MAIN
CVS tags: HEAD
scripts are almost all working. lcuserdel still has some problems, and lcuseradd
doesn't get the permissions quite right yet.

#!/usr/bin/perl
#
# lcpasswd
#
# Scott Harrison
# SH: October 27, 2000
# SH: October 28, 2000
# SH: October 29, 2000

use strict;

# This script is a setuid script that should
# be run by user 'www'.  This script allows
# for synchronous entry of passwords into
# both the /etc/passwd and the /etc/smbpasswd
# files.

# This script works under the same process control mechanism
# as lcuseradd and lcpasswd, to make sure that only one of these
# processes is running at any one time on the system.

# Standard input usage
# First line is USERNAME
# Second line is CURRENT PASSWORD
# Third line is NEW PASSWORD

# Valid passwords must consist of the
# ascii characters within the inclusive
# range of 0x20 (32) to 0x7E (126).
# These characters are:
# SPACE and
# !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO
# PQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~

# Valid user names must consist of ascii
# characters that are alphabetical characters
# (A-Z,a-z), numeric (0-9), or the underscore
# mark (_). (Essentially, the perl regex \w).

# Command-line arguments
# Yes, but be very careful here (don't pass shell commands)
# and this is only supported to allow perl-system calls.

# Usage within code
# Note: NEVER run as system("/home/httpd/perl/lcpasswd NAME OLDPWD NEWPWD")
#
# $exitcode=system("/home/httpd/perl/lcpasswd","NAME","OLDPWD","NEWPWD")/256;
# print "uh-oh" if $exitcode;

# These are the exit codes.
# ( (0,"ok"),
#   (1,"User ID mismatch.  This program must be run as user 'www'"),
#   (2,"Error. This program needs 3 command-line arguments (username, old password, new password)."),
#   (3,"Error. Three lines need to be entered into standard input."),
#   (4,"Error. Too many other simultaneous password change requests being made."),
#   (5,"Error. User $username does not exist."),
#   (6,"Error. Invalid entry of current password."),
#   (7,"Error. Root was not successfully enabled."),
#   (8,"Error. Cannot open /etc/passwd."),
#   (9,"Error. The user name specified has invalid characters."),
#   (10,"Error. A password entry had an invalid character.") )

# Security
$ENV{'PATH'}='/bin:/usr/bin'; # Nullify path information except for what smbpasswd needs
$ENV{'BASH_ENV'}=''; # Nullify shell environment information.

# Do not print error messages if there are command-line arguments
my $noprint=0;
if (@ARGV) {
    $noprint=1;
}

# Read in /etc/passwd, and make sure this process is running from user=www
open (IN, '</etc/passwd');
my @lines=<IN>;
close IN;
my $wwwid;
for my $l (@lines) {
    chop $l;
    my @F=split(/\:/,$l);
    if ($F[0] eq 'www') {$wwwid=$F[2];}
}
if ($wwwid!=$<) {
    print("User ID mismatch.  This program must be run as user 'www'\n") unless $noprint;
    exit 1;
}
&disable_root_capability;

# Handle case of another lcpasswd process
unless (&try_to_lock('/tmp/lock_lcpasswd')) {
    print "Error. Too many other simultaneous password change requests being made.\n" unless $noprint;
    exit 4;
}

# Gather input.  Should only be 3 values.
my @input;
if (@ARGV==3) {
    @input=@ARGV;
}
elsif (@ARGV) {
    print("Error. This program needs 3 command-line arguments (username, old password, new password).\n") unless $noprint;
    unlink('/tmp/lock_lcpasswd');
    exit 2;
}
else {
    @input=<>;
    if (@input!=3) {
	print("Error. Three lines need to be entered into standard input.\n") unless $noprint;
	unlink('/tmp/lock_lcpasswd');
	exit 3;
    }
    map {chop} @input;
}

my ($username,$oldpwd,$newpwd)=@input;
$username=~/^(\w+)$/;
my $safeusername=$1;
if ($username ne $safeusername) {
    print "Error. The user name specified has invalid characters.\n";
    unlink('/tmp/lock_lcpasswd');
    exit 9;
}
my $pbad=0;
map {if (($_<32)&&($_>126)){$pbad=1;}} (split(//,$oldpwd));
map {if (($_<32)&&($_>126)){$pbad=1;}} (split(//,$newpwd));
if ($pbad) {
    print "Error. A password entry had an invalid character.\n";
    unlink('/tmp/lock_lcpasswd');
    exit 10;
}

# Grab the line corresponding to username
my ($userid,$useroldcryptpwd);
my @F; my @U;
for my $l (@lines) {
    @F=split(/\:/,$l);
    if ($F[0] eq $username) {($userid,$useroldcryptpwd)=($F[2],$F[1]); @U=@F;}
}

# Verify existence of user
if (!defined($userid)) {
    print "Error. User $username does not exist.\n" unless $noprint;
    unlink('/tmp/lock_lcpasswd');
    exit 5;
}

# Verify password entry
if (crypt($oldpwd,$useroldcryptpwd) ne $useroldcryptpwd) {
    print "Error. Invalid entry of current password.\n" unless $noprint;
    unlink('/tmp/lock_lcpasswd');
    exit 6;
}

# Construct new password entry (random salt)
my $newcryptpwd=crypt($newpwd,(join '', ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[rand 64, rand 64]));
$U[1]=$newcryptpwd;
my $userline=join(':',@U);
my $rootid=&enable_root_capability;
if ($rootid!=0) {
    print "Error.  Root was not successfully enabled.\n" unless $noprint;
    unlink('/tmp/lock_lcpasswd');
    exit 7;
}
open PASSWORDFILE, '>/etc/passwd' or (print("Error.  Cannot open /etc/passwd.\n") && unlink('/tmp/lock_lcpasswd') && exit(8));
for my $l (@lines) {
    @F=split(/\:/,$l);
    if ($F[0] eq $username) {print PASSWORDFILE "$userline\n";}
    else {print PASSWORDFILE "$l\n";}
}
close PASSWORDFILE;

($>,$<)=(0,0); # fool smbpasswd here to think this is not a setuid environment
unless (-e '/etc/smbpasswd') {
    open (OUT,'>/etc/smbpasswd'); close OUT;
}
my $smbexist=0;
open (IN, '</etc/smbpasswd');
my @lines=<IN>;
close IN;
for my $l (@lines) {
    chop $l;
    my @F=split(/\:/,$l);
    if ($F[0] eq $username) {$smbexist=1;}
}
unless ($smbexist) {
    open(OUT,'>>/etc/smbpasswd');
    print OUT join(':',($safeusername,$userid,'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX','','/home/'.$safeusername,'/bin/bash')) . "\n";
    close OUT;
}
open(OUT,"|/usr/bin/smbpasswd -s $safeusername>/dev/null");
print OUT $newpwd; print OUT "\n";
print OUT $newpwd; print OUT "\n";
close OUT;
$<=$wwwid; # unfool the program
&disable_root_capability;
unlink('/tmp/lock_lcpasswd');
exit 0;

# ----------------------------------------------------------- have setuid script run as root
sub enable_root_capability {
    if ($wwwid==$>) {
	($<,$>)=($>,$<);
	($(,$))=($),$();
    }
    else {
	# root capability is already enabled
    }
    return $>;
}

# ----------------------------------------------------------- have setuid script run as www
sub disable_root_capability {
    if ($wwwid==$<) {
	($<,$>)=($>,$<);
	($(,$))=($),$();
    }
    else {
	# root capability is already disabled
    }
}

# ----------------------------------- make sure that another lcpasswd process isn't running
sub try_to_lock {
    my ($lockfile)=@_;
    my $currentpid;
    my $lastpid;
    # Do not manipulate lock file as root
    if ($>==0) {
	return 0;
    }
    # Try to generate lock file.
    # Wait 3 seconds.  If same process id is in
    # lock file, then assume lock file is stale, and
    # go ahead.  If process id's fluctuate, try
    # for a maximum of 10 times.
    for (0..10) {
	if (-e $lockfile) {
	    open(LOCK,"<$lockfile");
	    $currentpid=<LOCK>;
	    close LOCK;
	    if ($currentpid==$lastpid) {
		last;
	    }
	    sleep 3;
	    $lastpid=$currentpid;
	}
	else {
	    last;
	}
	if ($_==10) {
	    return 0;
	}
    }
    open(LOCK,">$lockfile");
    print LOCK $$;
    close LOCK;
    return 1;
}

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>
500 Internal Server Error

Internal Server Error

The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator at root@localhost to inform them of the time this error occurred, and the actions you performed just before this error.

More information about this error may be available in the server error log.