#!/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=2002 # 02/19 Matthew Hall # # $Id: lcpasswd,v 1.21 2005/04/07 22:27:52 albertel Exp $ ### ############################################################################### ## ## ## 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; # ------------------------------------------------------- Description of script # # 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. # --------------------------------------- Invoking script (standard input only) # # Standard input usage # First line is USERNAME # Second line is NEW 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). # User names must begin with an alphabetical character # (A-Z,a-z). # ---------------------------------------------------- Description of functions # enable_root_capability() : have setuid script run as root # disable_root_capability() : have setuid script run as www # try_to_lock() : make sure that another lcpasswd process isn't running # ------------------------------------------------------------------ Exit codes # 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 set password."), # (9,"Error. The user name specified has invalid characters."), # (10,"Error. A password entry had an invalid character.") ) # ------------------------------------------------------------- Initializations # Security $ENV{'PATH'}='/bin:/usr/bin:/usr/local/sbin:/home/httpd/perl'; # Nullify path # information delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; # nullify potential taints # Do not print error messages my $noprint=1; print "In lcpasswd" unless $noprint; # ----------------------------- Make sure this process is running from user=www my $wwwid=getpwnam('www'); &disable_root_capability; if ($wwwid!=$>) { print("User ID mismatch. This program must be run as user 'www'\n") unless $noprint; exit 1; } # ----------------------------------- Start running script with www permissions &disable_root_capability; # --------------------------- Handle case of another lcpasswd process (locking) unless (&try_to_lock('/tmp/lock_lcpasswd')) { print "Error. Too many other simultaneous password change requests being ". "made.\n" unless $noprint; exit 4; } # ------- Error-check input, need 3 values (user name, password 1, password 2). my @input; @input=<>; if (@input!=3) { print("Error. Three lines need to be entered into standard input.\n") unless $noprint; unlink('/tmp/lock_lcpasswd'); exit 3; } foreach (@input) {chomp;} my ($username,$password1,$password2)=@input; $username=~/^(\w+)$/; my $safeusername=$1; if (($username ne $safeusername) or ($safeusername!~/^[A-Za-z]/)) { print "Error. The user name specified has invalid characters.\n"; unlink('/tmp/lock_lcpasswd'); exit 9; } my $pbad=0; foreach (split(//,$password1)) {if ((ord($_)<32)||(ord($_)>126)){$pbad=1;}} foreach (split(//,$password2)) {if ((ord($_)<32)||(ord($_)>126)){$pbad=1;}} if ($pbad) { print "Error. A password entry had an invalid character.\n"; unlink('/tmp/lock_lcpasswd'); exit 10; } # -- Only add user if the two password arguments match. if ($password1 ne $password2) { print "Error. Password mismatch.\n" unless $noprint; unlink('/tmp/lock_lcpasswd'); exit 13; } # Verify existence of user unless(getpwnam($safeusername)) { print "Error. User $username does not exist.\n" unless $noprint; unlink('/tmp/lock_lcpasswd'); exit 5; } &enable_root_capability; ($>,$<)=(0,0); print "Now $> , $< , -invoking pwchange with $safeusername $password1" unless $noprint; open OUT,"|pwchange $safeusername"; print OUT $password1; print OUT "\n"; close OUT; ($>,$<)=(0,$wwwid); print "pwchange done, back to uid $wwwid" unless $noprint; if ($?) { exit 8; } my $userid=getpwnam($safeusername); if (-e '/usr/bin/smbpasswd') { ($>,$<)=(0,0); # fool smbpasswd here to think this is not a setuid # environment # If the -a switch is put on the smbpasswd # command line, either a new entry will be created or the old one # will be used. # Therefore the old strategy of looking for and adding a dummy entry is # not needed... Finally, the smbpasswd file is in /etc/samba not # /etc/smbpasswd as older versions of the script implied. print "Running smbpasswd" unless $noprint; open(OUT,"|/usr/bin/smbpasswd -s -a $safeusername>/dev/null") or die('cannot run smbpasswd'); print OUT $password2; print OUT "\n"; print OUT $password2; print OUT "\n"; close OUT; $<=$wwwid; # unfool the program print "smbpasswd done" unless $noprint; } &disable_root_capability; unlink('/tmp/lock_lcpasswd'); exit 0; # ---------------------------------------------- have setuid script run as root sub enable_root_capability { if ($wwwid==$>) { ($<,$>)=($>,0); ($(,$))=($),0); } 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=; 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; } =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