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