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