Annotation of loncom/lcpasswd, revision 1.22
1.1 harris41 1: #!/usr/bin/perl
1.13 harris41 2: # The Learning Online Network with CAPA
3: #
4: # lcpasswd - LON-CAPA setuid script to synchronously change all
5: # filesystem-related passwords (samba, unix, etc)
1.3 harris41 6: #
1.15 matthew 7: # YEAR=2002
8: # 02/19 Matthew Hall
1.13 harris41 9: #
1.22 ! foxr 10: # $Id: lcpasswd,v 1.21 2005/04/07 22:27:52 albertel Exp $
1.13 harris41 11: ###
1.12 harris41 12:
13: ###############################################################################
14: ## ##
15: ## ORGANIZATION OF THIS PERL SCRIPT ##
16: ## ##
17: ## 1. Description of script ##
18: ## 2. Invoking script (standard input only) ##
19: ## 3. Example usage inside another piece of code ##
20: ## 4. Description of functions ##
21: ## 5. Exit codes ##
22: ## ##
23: ###############################################################################
1.1 harris41 24:
25: use strict;
26:
1.12 harris41 27: # ------------------------------------------------------- Description of script
28: #
1.1 harris41 29: # This script is a setuid script that should
1.4 harris41 30: # be run by user 'www'. This script allows
31: # for synchronous entry of passwords into
32: # both the /etc/passwd and the /etc/smbpasswd
33: # files.
1.12 harris41 34: #
1.5 harris41 35: # This script works under the same process control mechanism
36: # as lcuseradd and lcpasswd, to make sure that only one of these
37: # processes is running at any one time on the system.
38:
1.12 harris41 39: # --------------------------------------- Invoking script (standard input only)
40: #
1.1 harris41 41: # Standard input usage
42: # First line is USERNAME
1.16 matthew 43: # Second line is NEW PASSWORD
1.1 harris41 44: # Third line is NEW PASSWORD
1.12 harris41 45: #
1.8 harris41 46: # Valid passwords must consist of the
47: # ascii characters within the inclusive
48: # range of 0x20 (32) to 0x7E (126).
49: # These characters are:
50: # SPACE and
51: # !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO
52: # PQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
1.12 harris41 53: #
1.8 harris41 54: # Valid user names must consist of ascii
55: # characters that are alphabetical characters
56: # (A-Z,a-z), numeric (0-9), or the underscore
57: # mark (_). (Essentially, the perl regex \w).
1.12 harris41 58: # User names must begin with an alphabetical character
59: # (A-Z,a-z).
1.8 harris41 60:
1.12 harris41 61: # ---------------------------------------------------- Description of functions
62: # enable_root_capability() : have setuid script run as root
63: # disable_root_capability() : have setuid script run as www
64: # try_to_lock() : make sure that another lcpasswd process isn't running
1.4 harris41 65:
1.12 harris41 66: # ------------------------------------------------------------------ Exit codes
1.4 harris41 67: # These are the exit codes.
68: # ( (0,"ok"),
1.7 harris41 69: # (1,"User ID mismatch. This program must be run as user 'www'"),
1.12 harris41 70: # (2,"Error. This program needs 3 command-line arguments (username, old ".
71: # password, new password)."),
1.6 harris41 72: # (3,"Error. Three lines need to be entered into standard input."),
1.12 harris41 73: # (4,"Error. Too many other simultaneous password change requests being ".
74: # made."),
1.6 harris41 75: # (5,"Error. User $username does not exist."),
76: # (6,"Error. Invalid entry of current password."),
1.10 harris41 77: # (7,"Error. Root was not successfully enabled."),
1.12 harris41 78: # (8,"Error. Cannot set password."),
1.10 harris41 79: # (9,"Error. The user name specified has invalid characters."),
80: # (10,"Error. A password entry had an invalid character.") )
1.4 harris41 81:
1.12 harris41 82: # ------------------------------------------------------------- Initializations
1.1 harris41 83: # Security
1.12 harris41 84: $ENV{'PATH'}='/bin:/usr/bin:/usr/local/sbin:/home/httpd/perl'; # Nullify path
85: # information
1.13 harris41 86: delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; # nullify potential taints
1.1 harris41 87:
1.12 harris41 88: # Do not print error messages
89: my $noprint=1;
90:
1.18 foxr 91: print "In lcpasswd" unless $noprint;
92:
1.12 harris41 93: # ----------------------------- Make sure this process is running from user=www
94: my $wwwid=getpwnam('www');
1.22 ! foxr 95:
! 96: if ($wwwid!=$<) {
1.12 harris41 97: print("User ID mismatch. This program must be run as user 'www'\n")
98: unless $noprint;
1.4 harris41 99: exit 1;
1.1 harris41 100: }
1.12 harris41 101:
102: # ----------------------------------- Start running script with www permissions
1.22 ! foxr 103:
1.1 harris41 104:
1.12 harris41 105: # --------------------------- Handle case of another lcpasswd process (locking)
1.6 harris41 106: unless (&try_to_lock('/tmp/lock_lcpasswd')) {
1.12 harris41 107: print "Error. Too many other simultaneous password change requests being ".
108: "made.\n" unless $noprint;
1.5 harris41 109: exit 4;
110: }
111:
1.12 harris41 112: # ------- Error-check input, need 3 values (user name, password 1, password 2).
1.4 harris41 113: my @input;
1.12 harris41 114: @input=<>;
115: if (@input!=3) {
116: print("Error. Three lines need to be entered into standard input.\n")
117: unless $noprint;
1.6 harris41 118: unlink('/tmp/lock_lcpasswd');
1.12 harris41 119: exit 3;
1.1 harris41 120: }
1.14 harris41 121: foreach (@input) {chomp;}
1.1 harris41 122:
1.12 harris41 123: my ($username,$password1,$password2)=@input;
1.8 harris41 124: $username=~/^(\w+)$/;
125: my $safeusername=$1;
1.12 harris41 126: if (($username ne $safeusername) or ($safeusername!~/^[A-Za-z]/)) {
1.8 harris41 127: print "Error. The user name specified has invalid characters.\n";
128: unlink('/tmp/lock_lcpasswd');
129: exit 9;
130: }
1.9 harris41 131: my $pbad=0;
1.15 matthew 132: foreach (split(//,$password1)) {if ((ord($_)<32)||(ord($_)>126)){$pbad=1;}}
133: foreach (split(//,$password2)) {if ((ord($_)<32)||(ord($_)>126)){$pbad=1;}}
1.9 harris41 134: if ($pbad) {
135: print "Error. A password entry had an invalid character.\n";
136: unlink('/tmp/lock_lcpasswd');
137: exit 10;
138: }
1.1 harris41 139:
1.12 harris41 140: # -- Only add user if the two password arguments match.
141: if ($password1 ne $password2) {
142: print "Error. Password mismatch.\n" unless $noprint;
143: unlink('/tmp/lock_lcpasswd');
144: exit 13;
1.1 harris41 145: }
146:
147: # Verify existence of user
1.12 harris41 148: unless(getpwnam($safeusername)) {
1.4 harris41 149: print "Error. User $username does not exist.\n" unless $noprint;
1.6 harris41 150: unlink('/tmp/lock_lcpasswd');
1.4 harris41 151: exit 5;
1.1 harris41 152: }
1.12 harris41 153: &enable_root_capability;
154: ($>,$<)=(0,0);
1.18 foxr 155:
156: print "Now $> , $< , -invoking pwchange with $safeusername $password1"
1.17 foxr 157: unless $noprint;
1.12 harris41 158: open OUT,"|pwchange $safeusername";
159: print OUT $password1;
160: print OUT "\n";
161: close OUT;
1.20 albertel 162: ($>,$<)=(0,$wwwid);
1.12 harris41 163:
1.20 albertel 164: print "pwchange done, back to uid $wwwid" unless $noprint;
1.17 foxr 165:
1.12 harris41 166: if ($?) {
167: exit 8;
1.1 harris41 168: }
1.12 harris41 169: my $userid=getpwnam($safeusername);
170:
1.13 harris41 171: if (-e '/usr/bin/smbpasswd') {
1.12 harris41 172:
173: ($>,$<)=(0,0); # fool smbpasswd here to think this is not a setuid
174: # environment
1.1 harris41 175:
1.21 albertel 176: # If the -a switch is put on the smbpasswd
1.17 foxr 177: # command line, either a new entry will be created or the old one
178: # will be used.
179: # Therefore the old strategy of looking for and adding a dummy entry is
180: # not needed... Finally, the smbpasswd file is in /etc/samba not
181: # /etc/smbpasswd as older versions of the script implied.
182:
183: print "Running smbpasswd" unless $noprint;
184: open(OUT,"|/usr/bin/smbpasswd -s -a $safeusername>/dev/null") or
1.13 harris41 185: die('cannot run smbpasswd');
1.12 harris41 186: print OUT $password2; print OUT "\n";
187: print OUT $password2; print OUT "\n";
1.2 harris41 188: close OUT;
1.12 harris41 189: $<=$wwwid; # unfool the program
1.17 foxr 190: print "smbpasswd done" unless $noprint;
1.2 harris41 191: }
1.12 harris41 192:
1.22 ! foxr 193:
1.6 harris41 194: unlink('/tmp/lock_lcpasswd');
1.4 harris41 195: exit 0;
1.1 harris41 196:
1.12 harris41 197: # ---------------------------------------------- have setuid script run as root
1.1 harris41 198: sub enable_root_capability {
1.2 harris41 199: if ($wwwid==$>) {
1.18 foxr 200: ($<,$>)=($>,0);
201: ($(,$))=($),0);
1.1 harris41 202: }
203: else {
204: # root capability is already enabled
205: }
1.2 harris41 206: return $>;
1.1 harris41 207: }
208:
1.12 harris41 209: # ----------------------------------------------- have setuid script run as www
1.1 harris41 210: sub disable_root_capability {
1.2 harris41 211: if ($wwwid==$<) {
1.1 harris41 212: ($<,$>)=($>,$<);
213: ($(,$))=($),$();
214: }
215: else {
216: # root capability is already disabled
217: }
218: }
219:
1.12 harris41 220: # ----------------------- make sure that another lcpasswd process isn't running
1.1 harris41 221: sub try_to_lock {
222: my ($lockfile)=@_;
223: my $currentpid;
224: my $lastpid;
1.22 ! foxr 225:
1.5 harris41 226: # Try to generate lock file.
227: # Wait 3 seconds. If same process id is in
228: # lock file, then assume lock file is stale, and
229: # go ahead. If process id's fluctuate, try
230: # for a maximum of 10 times.
1.1 harris41 231: for (0..10) {
232: if (-e $lockfile) {
233: open(LOCK,"<$lockfile");
234: $currentpid=<LOCK>;
235: close LOCK;
236: if ($currentpid==$lastpid) {
237: last;
238: }
239: sleep 3;
240: $lastpid=$currentpid;
241: }
242: else {
243: last;
244: }
245: if ($_==10) {
246: return 0;
247: }
248: }
249: open(LOCK,">$lockfile");
250: print LOCK $$;
251: close LOCK;
252: return 1;
253: }
1.13 harris41 254:
255: =head1 NAME
256:
257: lcpasswd - LON-CAPA setuid script to synchronously change all
258: filesystem-related passwords (samba, unix, etc)
259:
260: =head1 DESCRIPTION
261:
262: LON-CAPA setuid script to synchronously change all
263: filesystem-related passwords (samba, unix, etc)
264:
265: =head1 README
266:
267: LON-CAPA setuid script to synchronously change all
268: filesystem-related passwords (samba, unix, etc)
269:
270: =head1 PREREQUISITES
271:
272: =head1 COREQUISITES
273:
274: =pod OSNAMES
275:
276: linux
277:
278: =pod SCRIPT CATEGORIES
279:
280: LONCAPA/Administrative
281:
282: =cut
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>