version 1.9, 2000/10/30 02:31:45
|
version 1.16, 2002/08/30 13:37:59
|
Line 1
|
Line 1
|
#!/usr/bin/perl |
#!/usr/bin/perl |
|
|
|
# The Learning Online Network with CAPA |
|
# |
|
# lcpasswd - LON-CAPA setuid script to synchronously change all |
|
# filesystem-related passwords (samba, unix, etc) |
|
# |
|
# YEAR=2000 |
|
# 10/27,10/28,10/29,10/30 Scott Harrison |
# |
# |
# lcpasswd |
# YEAR=2001 |
|
# 10/22,10/23,11/13,11/15 Scott Harrison |
|
# |
|
# YEAR=2002 |
|
# 02/19 Matthew Hall |
# |
# |
# Scott Harrison |
# $Id$ |
# SH: October 27, 2000 |
### |
# SH: October 28, 2000 |
|
|
############################################################################### |
|
## ## |
|
## ORGANIZATION OF THIS PERL SCRIPT ## |
|
## ## |
|
## 1. Description of script ## |
|
## 2. Invoking script (standard input only) ## |
|
## 3. Example usage inside another piece of code ## |
|
## 4. Description of functions ## |
|
## 5. Exit codes ## |
|
## ## |
|
############################################################################### |
|
|
use strict; |
use strict; |
|
|
|
# ------------------------------------------------------- Description of script |
|
# |
# This script is a setuid script that should |
# This script is a setuid script that should |
# be run by user 'www'. This script allows |
# be run by user 'www'. This script allows |
# for synchronous entry of passwords into |
# for synchronous entry of passwords into |
# both the /etc/passwd and the /etc/smbpasswd |
# both the /etc/passwd and the /etc/smbpasswd |
# files. |
# files. |
|
# |
# This script works under the same process control mechanism |
# This script works under the same process control mechanism |
# as lcuseradd and lcpasswd, to make sure that only one of these |
# as lcuseradd and lcpasswd, to make sure that only one of these |
# processes is running at any one time on the system. |
# processes is running at any one time on the system. |
|
|
|
# --------------------------------------- Invoking script (standard input only) |
|
# |
# Standard input usage |
# Standard input usage |
# First line is USERNAME |
# First line is USERNAME |
# Second line is CURRENT PASSWORD |
# Second line is NEW PASSWORD |
# Third line is NEW PASSWORD |
# Third line is NEW PASSWORD |
|
# |
# Valid passwords must consist of the |
# Valid passwords must consist of the |
# ascii characters within the inclusive |
# ascii characters within the inclusive |
# range of 0x20 (32) to 0x7E (126). |
# range of 0x20 (32) to 0x7E (126). |
Line 30 use strict;
|
Line 57 use strict;
|
# SPACE and |
# SPACE and |
# !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO |
# !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO |
# PQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ |
# PQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ |
|
# |
# Valid user names must consist of ascii |
# Valid user names must consist of ascii |
# characters that are alphabetical characters |
# characters that are alphabetical characters |
# (A-Z,a-z), numeric (0-9), or the underscore |
# (A-Z,a-z), numeric (0-9), or the underscore |
# mark (_). (Essentially, the perl regex \w). |
# mark (_). (Essentially, the perl regex \w). |
|
# User names must begin with an alphabetical character |
|
# (A-Z,a-z). |
|
|
# Command-line arguments |
# ---------------------------------------------------- Description of functions |
# Yes, but be very careful here (don't pass shell commands) |
# enable_root_capability() : have setuid script run as root |
# and this is only supported to allow perl-system calls. |
# disable_root_capability() : have setuid script run as www |
|
# try_to_lock() : make sure that another lcpasswd process isn't running |
# 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; |
|
|
|
|
# ------------------------------------------------------------------ Exit codes |
# These are the exit codes. |
# These are the exit codes. |
# ( (0,"ok"), |
# ( (0,"ok"), |
# (1,"User ID mismatch. This program must be run as user 'www'"), |
# (1,"User ID mismatch. This program must be run as user 'www'"), |
# (2,"Error. This program does not accept command-line arguments."), |
# (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."), |
# (3,"Error. Three lines need to be entered into standard input."), |
# (4,"Error. Too many other simultaneous password change requests being made."), |
# (4,"Error. Too many other simultaneous password change requests being ". |
|
# made."), |
# (5,"Error. User $username does not exist."), |
# (5,"Error. User $username does not exist."), |
# (6,"Error. Invalid entry of current password."), |
# (6,"Error. Invalid entry of current password."), |
# (7,"Error. Root was not successfully enabled."), |
# (7,"Error. Root was not successfully enabled."), |
# (8,"Error. Cannot open /etc/passwd.") ) |
# (8,"Error. Cannot set password."), |
|
# (9,"Error. The user name specified has invalid characters."), |
|
# (10,"Error. A password entry had an invalid character.") ) |
|
|
|
# ------------------------------------------------------------- Initializations |
# Security |
# Security |
$ENV{'PATH'}='/bin:/usr/bin'; # Nullify path information except for what smbpasswd needs |
$ENV{'PATH'}='/bin:/usr/bin:/usr/local/sbin:/home/httpd/perl'; # Nullify path |
$ENV{'BASH_ENV'}=''; # Nullify shell environment information. |
# information |
|
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; # nullify potential taints |
|
|
# Do not print error messages if there are command-line arguments |
# Do not print error messages |
my $noprint=0; |
my $noprint=1; |
if (@ARGV) { |
|
$noprint=1; |
# ----------------------------- Make sure this process is running from user=www |
} |
my $wwwid=getpwnam('www'); |
|
&disable_root_capability; |
# Read in /etc/passwd, and make sure this process is running from user=www |
if ($wwwid!=$>) { |
open (IN, '</etc/passwd'); |
print("User ID mismatch. This program must be run as user 'www'\n") |
my @lines=<IN>; |
unless $noprint; |
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; |
exit 1; |
} |
} |
|
|
|
# ----------------------------------- Start running script with www permissions |
&disable_root_capability; |
&disable_root_capability; |
|
|
# Handle case of another lcpasswd process |
# --------------------------- Handle case of another lcpasswd process (locking) |
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" unless $noprint; |
print "Error. Too many other simultaneous password change requests being ". |
|
"made.\n" unless $noprint; |
exit 4; |
exit 4; |
} |
} |
|
|
# Gather input. Should only be 3 values. |
# ------- Error-check input, need 3 values (user name, password 1, password 2). |
my @input; |
my @input; |
if (@ARGV==3) { |
@input=<>; |
@input=@ARGV; |
if (@input!=3) { |
} |
print("Error. Three lines need to be entered into standard input.\n") |
elsif (@ARGV) { |
unless $noprint; |
print("Error. This program needs 3 command-line arguments (username, old password, new password).\n") unless $noprint; |
|
unlink('/tmp/lock_lcpasswd'); |
unlink('/tmp/lock_lcpasswd'); |
exit 2; |
exit 3; |
} |
|
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; |
|
} |
} |
|
foreach (@input) {chomp;} |
|
|
my ($username,$oldpwd,$newpwd)=@input; |
my ($username,$password1,$password2)=@input; |
$username=~/^(\w+)$/; |
$username=~/^(\w+)$/; |
my $safeusername=$1; |
my $safeusername=$1; |
if ($username ne $safeusername) { |
if (($username ne $safeusername) or ($safeusername!~/^[A-Za-z]/)) { |
print "Error. The user name specified has invalid characters.\n"; |
print "Error. The user name specified has invalid characters.\n"; |
unlink('/tmp/lock_lcpasswd'); |
unlink('/tmp/lock_lcpasswd'); |
exit 9; |
exit 9; |
} |
} |
my $pbad=0; |
my $pbad=0; |
map {if (($_<32)&&($_>126)){$pbad=1;}} (split(//,$oldpwd)); |
foreach (split(//,$password1)) {if ((ord($_)<32)||(ord($_)>126)){$pbad=1;}} |
map {if (($_<32)&&($_>126)){$pbad=1;}} (split(//,$newpwd)); |
foreach (split(//,$password2)) {if ((ord($_)<32)||(ord($_)>126)){$pbad=1;}} |
if ($pbad) { |
if ($pbad) { |
print "Error. A password entry had an invalid character.\n"; |
print "Error. A password entry had an invalid character.\n"; |
unlink('/tmp/lock_lcpasswd'); |
unlink('/tmp/lock_lcpasswd'); |
exit 10; |
exit 10; |
} |
} |
|
|
# Grab the line corresponding to username |
# -- Only add user if the two password arguments match. |
my ($userid,$useroldcryptpwd); |
if ($password1 ne $password2) { |
my @F; my @U; |
print "Error. Password mismatch.\n" unless $noprint; |
for my $l (@lines) { |
unlink('/tmp/lock_lcpasswd'); |
@F=split(/\:/,$l); |
exit 13; |
if ($F[0] eq $username) {($userid,$useroldcryptpwd)=($F[2],$F[1]); @U=@F;} |
|
} |
} |
|
|
# Verify existence of user |
# Verify existence of user |
if (!defined($userid)) { |
unless(getpwnam($safeusername)) { |
print "Error. User $username does not exist.\n" unless $noprint; |
print "Error. User $username does not exist.\n" unless $noprint; |
unlink('/tmp/lock_lcpasswd'); |
unlink('/tmp/lock_lcpasswd'); |
exit 5; |
exit 5; |
} |
} |
|
|
# Verify password entry |
&enable_root_capability; |
if (crypt($oldpwd,$useroldcryptpwd) ne $useroldcryptpwd) { |
($>,$<)=(0,0); |
print "Error. Invalid entry of current password.\n" unless $noprint; |
open OUT,"|pwchange $safeusername"; |
unlink('/tmp/lock_lcpasswd'); |
print OUT $password1; |
exit 6; |
print OUT "\n"; |
} |
close OUT; |
|
($>,$<)=(0,500); |
|
|
# Construct new password entry (random salt) |
if ($?) { |
my $newcryptpwd=crypt($newpwd,(join '', ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[rand 64, rand 64])); |
exit 8; |
$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)); |
my $userid=getpwnam($safeusername); |
for my $l (@lines) { |
|
@F=split(/\:/,$l); |
if (-e '/usr/bin/smbpasswd') { |
if ($F[0] eq $username) {print PASSWORDFILE "$userline\n";} |
|
else {print PASSWORDFILE "$l\n";} |
($>,$<)=(0,0); # fool smbpasswd here to think this is not a setuid |
} |
# environment |
close PASSWORDFILE; |
unless (-e '/etc/smbpasswd') { |
|
open (OUT,'>/etc/smbpasswd'); close OUT; |
($>,$<)=(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 $smbexist=0; |
my @lines=<IN>; |
open (IN, '</etc/smbpasswd'); |
close IN; |
my @lines=<IN>; |
for my $l (@lines) { |
close IN; |
chop $l; |
for my $l (@lines) { |
my @F=split(/\:/,$l); |
chop $l; |
if ($F[0] eq $username) {$smbexist=1;} |
my @F=split(/\:/,$l); |
} |
if ($F[0] eq $username) {$smbexist=1;} |
unless ($smbexist) { |
} |
open(OUT,'>>/etc/smbpasswd'); |
unless ($smbexist) { |
print OUT join(':',($safeusername,$userid, |
open(OUT,'>>/etc/smbpasswd'); |
'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:XXXXXXXXXXXXXX'. |
print OUT join(':',($safeusername,$userid,'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX','','/home/'.$safeusername,'/bin/bash')) . "\n"; |
'XXXXXXXXXXXXXXXXXX','','/home/'.$safeusername, |
|
'/bin/bash')) . "\n"; |
|
close OUT; |
|
} |
|
|
|
open(OUT,"|/usr/bin/smbpasswd -s $safeusername>/dev/null") or |
|
die('cannot run smbpasswd'); |
|
print OUT $password2; print OUT "\n"; |
|
print OUT $password2; print OUT "\n"; |
close OUT; |
close OUT; |
|
$<=$wwwid; # unfool the program |
} |
} |
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; |
&disable_root_capability; |
unlink('/tmp/lock_lcpasswd'); |
unlink('/tmp/lock_lcpasswd'); |
exit 0; |
exit 0; |
|
|
# ----------------------------------------------------------- have setuid script run as root |
# ---------------------------------------------- have setuid script run as root |
sub enable_root_capability { |
sub enable_root_capability { |
if ($wwwid==$>) { |
if ($wwwid==$>) { |
($<,$>)=($>,$<); |
($<,$>)=($>,$<); |
Line 205 sub enable_root_capability {
|
Line 219 sub enable_root_capability {
|
return $>; |
return $>; |
} |
} |
|
|
# ----------------------------------------------------------- have setuid script run as www |
# ----------------------------------------------- have setuid script run as www |
sub disable_root_capability { |
sub disable_root_capability { |
if ($wwwid==$<) { |
if ($wwwid==$<) { |
($<,$>)=($>,$<); |
($<,$>)=($>,$<); |
Line 216 sub disable_root_capability {
|
Line 230 sub disable_root_capability {
|
} |
} |
} |
} |
|
|
# ----------------------------------- make sure that another lcpasswd process isn't running |
# ----------------------- 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; |
Line 253 sub try_to_lock {
|
Line 267 sub try_to_lock {
|
close LOCK; |
close LOCK; |
return 1; |
return 1; |
} |
} |
|
|
|
=head1 NAME |
|
|
|
lcpasswd - LON-CAPA setuid script to synchronously change all |
|
filesystem-related passwords (samba, unix, etc) |
|
|
|
=head1 DESCRIPTION |
|
|
|
LON-CAPA setuid script to synchronously change all |
|
filesystem-related passwords (samba, unix, etc) |
|
|
|
=head1 README |
|
|
|
LON-CAPA setuid script to synchronously change all |
|
filesystem-related passwords (samba, unix, etc) |
|
|
|
=head1 PREREQUISITES |
|
|
|
=head1 COREQUISITES |
|
|
|
=pod OSNAMES |
|
|
|
linux |
|
|
|
=pod SCRIPT CATEGORIES |
|
|
|
LONCAPA/Administrative |
|
|
|
=cut |