version 1.2, 2000/10/27 22:02:23
|
version 1.5, 2000/10/28 18:25:47
|
Line 1
|
Line 1
|
#!/usr/bin/perl |
#!/usr/bin/perl |
|
# |
use strict; |
# lcpasswd |
|
# |
# Scott Harrison |
# Scott Harrison |
# October 27, 2000 |
# October 27, 2000 |
|
|
|
use strict; |
|
|
# This script is a setuid script that should |
# This script is a setuid script that should |
# be run by user 'www'. |
# 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 |
# Standard input usage |
# First line is USERNAME |
# First line is USERNAME |
# Second line is CURRENT PASSWORD |
# Second line is CURRENT PASSWORD |
# Third line is NEW PASSWORD |
# Third line is NEW PASSWORD |
|
|
|
# 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 does not accept command-line arguments."), |
|
# (3,"Error. Three lines need to be entered into standard input.\n"), |
|
# (4,"Error. Too many other simultaneous password change requests being made.\n"), |
|
# (5,"Error. User $username does not exist.\n"), |
|
# (6,"Error. Invalid entry of current password.\n"), |
|
# (7,"Error. Root was not successfully enabled.\n") ) |
|
|
# Security |
# Security |
$ENV{'PATH'}="/bin:/usr/bin"; # Nullify path information except for what smbpasswd needs |
$ENV{'PATH'}="/bin:/usr/bin"; # Nullify path information except for what smbpasswd needs |
$ENV{'BASH_ENV'}=""; # Nullify shell environment information. |
$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"); |
open (IN, "</etc/passwd"); |
my @lines=<IN>; |
my @lines=<IN>; |
close IN; |
close IN; |
Line 27 for my $l (@lines) {
|
Line 63 for my $l (@lines) {
|
if ($F[0] eq 'www') {$wwwid=$F[2];} |
if ($F[0] eq 'www') {$wwwid=$F[2];} |
} |
} |
if ($wwwid!=$<) { |
if ($wwwid!=$<) { |
print("User ID mismatch. This program must be run as user 'www'\n"); |
print("User ID mismatch. This program must be run as user 'www'\n") unless $noprint; |
exit 0; |
unlink("/tmp/lock_lcpasswd"); |
|
exit 1; |
} |
} |
&disable_root_capability; |
&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 |
# Handle case of another lcpasswd process |
unless (&try_to_lock("/tmp/lock_lcpasswd")) { |
unless (&try_to_lock("/tmp/lock_lcpasswd")) { |
print "Error. Too many other simultaneous password change requests being made.\n"; |
print "Error. Too many other simultaneous password change requests being made.\n" unless $noprint; |
exit 0; |
exit 4; |
} |
} |
|
|
my ($username,$oldpwd,$newpwd)=map {chop; $_} @input; |
# 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; |
|
|
# Grab the line corresponding to username |
# Grab the line corresponding to username |
my ($userid,$useroldcryptpwd); |
my ($userid,$useroldcryptpwd); |
Line 61 for my $l (@lines) {
|
Line 107 for my $l (@lines) {
|
|
|
# Verify existence of user |
# Verify existence of user |
if (!defined($userid)) { |
if (!defined($userid)) { |
print "Error. User $username does not exist.\n"; |
print "Error. User $username does not exist.\n" unless $noprint; |
exit 0; |
unlink("/tmp/lock_lcpasswd"); |
|
exit 5; |
} |
} |
|
|
# Verify password entry |
# Verify password entry |
if (crypt($oldpwd,$useroldcryptpwd) ne $useroldcryptpwd) { |
if (crypt($oldpwd,$useroldcryptpwd) ne $useroldcryptpwd) { |
print "Error. Invalid entry of current password.\n"; |
print "Error. Invalid entry of current password.\n" unless $noprint; |
exit 0; |
unlink("/tmp/lock_lcpasswd"); |
|
exit 6; |
} |
} |
|
|
# Construct new password entry (random salt) |
# Construct new password entry (random salt) |
Line 77 $U[1]=$newcryptpwd;
|
Line 125 $U[1]=$newcryptpwd;
|
my $userline=join(":",@U); |
my $userline=join(":",@U); |
my $rootid=&enable_root_capability; |
my $rootid=&enable_root_capability; |
if ($rootid!=0) { |
if ($rootid!=0) { |
print "Error. Root was not successfully enabled.\n"; |
print "Error. Root was not successfully enabled.\n" unless $noprint; |
exit 0; |
unlink("/tmp/lock_lcpasswd"); |
|
exit 7; |
} |
} |
open PASSWORDFILE, ">/etc/passwd" or die("Cannot open /etc/passwd!"); |
open PASSWORDFILE, ">/etc/passwd" or die("Cannot open /etc/passwd!"); |
for my $l (@lines) { |
for my $l (@lines) { |
Line 114 close OUT;
|
Line 163 close OUT;
|
$<=$wwwid; # unfool the program |
$<=$wwwid; # unfool the program |
&disable_root_capability; |
&disable_root_capability; |
unlink("/tmp/lock_lcpasswd"); |
unlink("/tmp/lock_lcpasswd"); |
|
exit 0; |
|
|
|
# ----------------------------------------------------------- have setuid script run as root |
sub enable_root_capability { |
sub enable_root_capability { |
if ($wwwid==$>) { |
if ($wwwid==$>) { |
($<,$>)=($>,$<); |
($<,$>)=($>,$<); |
Line 126 sub enable_root_capability {
|
Line 177 sub enable_root_capability {
|
return $>; |
return $>; |
} |
} |
|
|
|
# ----------------------------------------------------------- have setuid script run as www |
sub disable_root_capability { |
sub disable_root_capability { |
if ($wwwid==$<) { |
if ($wwwid==$<) { |
($<,$>)=($>,$<); |
($<,$>)=($>,$<); |
Line 136 sub disable_root_capability {
|
Line 188 sub disable_root_capability {
|
} |
} |
} |
} |
|
|
|
# ----------------------------------- make sure that another lcpasswd process isn't running |
sub try_to_lock { |
sub try_to_lock { |
my ($lockfile)=@_; |
my ($lockfile)=@_; |
my $currentpid; |
my $currentpid; |
my $lastpid; |
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) { |
for (0..10) { |
if (-e $lockfile) { |
if (-e $lockfile) { |
open(LOCK,"<$lockfile"); |
open(LOCK,"<$lockfile"); |