File:  [LON-CAPA] / loncom / lcpasswd
Revision 1.2: download - view: text, annotated - select for diffs
Fri Oct 27 22:02:23 2000 UTC (23 years, 5 months ago) by harris41
Branches: MAIN
CVS tags: HEAD
it works.  implements locking (so that two lcpasswd processes do not muck
things up at the same time).  supposed to work under chmod 6755 (setuid
script).  only runs when the initial (real) uid corresponds to www.  I
do not interface with the unix 'passwd' script, but just do the crypt
calculations separately.  I minimalize environment variables to improve
security.  I do not use smbadduser; I just create a blank but unworking entry
(do not use smbpasswd -a either due to crazy side effect).  I have
to fake the smbpasswd into working in a non setuid mode.  Also, I have
to \w+ grab off of the username in order to "fool" perl into thinking
the username is a safe value to pass to the shell (which after the \w+
grab, indeed is). -Scott

#!/usr/bin/perl

use strict;

# Scott Harrison
# October 27, 2000

# This script is a setuid script that should
# be run by user 'www'.

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

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

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");
    exit 0;
}
&disable_root_capability;
if (@ARGV) {
    print("Error. This program does not accept command-line arguments.\n");
    exit 0;
}

# Gather input from standard input.  Should only be 3 lines.
my @input=<>;
if (@input!=3) {
    print("Error. Three lines need to be entered into standard input.\n");
    exit 0;
}

# 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";
    exit 0;
}

my ($username,$oldpwd,$newpwd)=map {chop; $_} @input;

# 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";
    exit 0;
}

# Verify password entry
if (crypt($oldpwd,$useroldcryptpwd) ne $useroldcryptpwd) {
    print "Error. Invalid entry of current password.\n";
    exit 0;
}

# 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";
    exit 0;
}
open PASSWORDFILE, ">/etc/passwd" or die("Cannot open /etc/passwd!");
for my $l (@lines) {
    @F=split(/\:/,$l);
    if ($F[0] eq $username) {print PASSWORDFILE "$userline\n";}
    else {print PASSWORDFILE "$l\n";}
}
close PASSWORDFILE;
$username=~/^(\w+)$/;
my $safeusername=$1;
($>,$<)=(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");

sub enable_root_capability {
    if ($wwwid==$>) {
	($<,$>)=($>,$<);
	($(,$))=($),$();
    }
    else {
	# root capability is already enabled
    }
    return $>;
}

sub disable_root_capability {
    if ($wwwid==$<) {
	($<,$>)=($>,$<);
	($(,$))=($),$();
    }
    else {
	# root capability is already disabled
    }
}

sub try_to_lock {
    my ($lockfile)=@_;
    my $currentpid;
    my $lastpid;
    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>