Annotation of loncom/lcpasswd, revision 1.11
1.1 harris41 1: #!/usr/bin/perl
1.3 harris41 2: #
3: # lcpasswd
4: #
5: # Scott Harrison
1.6 harris41 6: # SH: October 27, 2000
7: # SH: October 28, 2000
1.11 ! harris41 8: # SH: October 29, 2000
1.1 harris41 9:
10: use strict;
11:
12: # This script is a setuid script that should
1.4 harris41 13: # be run by user 'www'. This script allows
14: # for synchronous entry of passwords into
15: # both the /etc/passwd and the /etc/smbpasswd
16: # files.
1.1 harris41 17:
1.5 harris41 18: # This script works under the same process control mechanism
19: # as lcuseradd and lcpasswd, to make sure that only one of these
20: # processes is running at any one time on the system.
21:
1.1 harris41 22: # Standard input usage
23: # First line is USERNAME
24: # Second line is CURRENT PASSWORD
25: # Third line is NEW PASSWORD
26:
1.8 harris41 27: # Valid passwords must consist of the
28: # ascii characters within the inclusive
29: # range of 0x20 (32) to 0x7E (126).
30: # These characters are:
31: # SPACE and
32: # !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO
33: # PQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
34:
35: # Valid user names must consist of ascii
36: # characters that are alphabetical characters
37: # (A-Z,a-z), numeric (0-9), or the underscore
38: # mark (_). (Essentially, the perl regex \w).
39:
1.4 harris41 40: # Command-line arguments
41: # Yes, but be very careful here (don't pass shell commands)
42: # and this is only supported to allow perl-system calls.
43:
44: # Usage within code
1.5 harris41 45: # Note: NEVER run as system("/home/httpd/perl/lcpasswd NAME OLDPWD NEWPWD")
1.4 harris41 46: #
1.5 harris41 47: # $exitcode=system("/home/httpd/perl/lcpasswd","NAME","OLDPWD","NEWPWD")/256;
1.4 harris41 48: # print "uh-oh" if $exitcode;
49:
50: # These are the exit codes.
51: # ( (0,"ok"),
1.7 harris41 52: # (1,"User ID mismatch. This program must be run as user 'www'"),
1.10 harris41 53: # (2,"Error. This program needs 3 command-line arguments (username, old password, new password)."),
1.6 harris41 54: # (3,"Error. Three lines need to be entered into standard input."),
55: # (4,"Error. Too many other simultaneous password change requests being made."),
56: # (5,"Error. User $username does not exist."),
57: # (6,"Error. Invalid entry of current password."),
1.10 harris41 58: # (7,"Error. Root was not successfully enabled."),
59: # (8,"Error. Cannot open /etc/passwd."),
60: # (9,"Error. The user name specified has invalid characters."),
61: # (10,"Error. A password entry had an invalid character.") )
1.4 harris41 62:
1.1 harris41 63: # Security
1.6 harris41 64: $ENV{'PATH'}='/bin:/usr/bin'; # Nullify path information except for what smbpasswd needs
65: $ENV{'BASH_ENV'}=''; # Nullify shell environment information.
1.1 harris41 66:
1.4 harris41 67: # Do not print error messages if there are command-line arguments
68: my $noprint=0;
69: if (@ARGV) {
70: $noprint=1;
71: }
72:
1.5 harris41 73: # Read in /etc/passwd, and make sure this process is running from user=www
1.6 harris41 74: open (IN, '</etc/passwd');
1.1 harris41 75: my @lines=<IN>;
76: close IN;
77: my $wwwid;
78: for my $l (@lines) {
79: chop $l;
80: my @F=split(/\:/,$l);
81: if ($F[0] eq 'www') {$wwwid=$F[2];}
82: }
83: if ($wwwid!=$<) {
1.4 harris41 84: print("User ID mismatch. This program must be run as user 'www'\n") unless $noprint;
85: exit 1;
1.1 harris41 86: }
1.2 harris41 87: &disable_root_capability;
1.1 harris41 88:
1.5 harris41 89: # Handle case of another lcpasswd process
1.6 harris41 90: unless (&try_to_lock('/tmp/lock_lcpasswd')) {
1.5 harris41 91: print "Error. Too many other simultaneous password change requests being made.\n" unless $noprint;
92: exit 4;
93: }
94:
1.4 harris41 95: # Gather input. Should only be 3 values.
96: my @input;
97: if (@ARGV==3) {
98: @input=@ARGV;
99: }
100: elsif (@ARGV) {
101: print("Error. This program needs 3 command-line arguments (username, old password, new password).\n") unless $noprint;
1.6 harris41 102: unlink('/tmp/lock_lcpasswd');
1.4 harris41 103: exit 2;
104: }
105: else {
106: @input=<>;
107: if (@input!=3) {
108: print("Error. Three lines need to be entered into standard input.\n") unless $noprint;
1.6 harris41 109: unlink('/tmp/lock_lcpasswd');
1.4 harris41 110: exit 3;
111: }
112: map {chop} @input;
1.1 harris41 113: }
114:
1.4 harris41 115: my ($username,$oldpwd,$newpwd)=@input;
1.8 harris41 116: $username=~/^(\w+)$/;
117: my $safeusername=$1;
118: if ($username ne $safeusername) {
119: print "Error. The user name specified has invalid characters.\n";
120: unlink('/tmp/lock_lcpasswd');
121: exit 9;
122: }
1.9 harris41 123: my $pbad=0;
124: map {if (($_<32)&&($_>126)){$pbad=1;}} (split(//,$oldpwd));
125: map {if (($_<32)&&($_>126)){$pbad=1;}} (split(//,$newpwd));
126: if ($pbad) {
127: print "Error. A password entry had an invalid character.\n";
128: unlink('/tmp/lock_lcpasswd');
129: exit 10;
130: }
1.1 harris41 131:
132: # Grab the line corresponding to username
133: my ($userid,$useroldcryptpwd);
134: my @F; my @U;
135: for my $l (@lines) {
136: @F=split(/\:/,$l);
137: if ($F[0] eq $username) {($userid,$useroldcryptpwd)=($F[2],$F[1]); @U=@F;}
138: }
139:
140: # Verify existence of user
141: if (!defined($userid)) {
1.4 harris41 142: print "Error. User $username does not exist.\n" unless $noprint;
1.6 harris41 143: unlink('/tmp/lock_lcpasswd');
1.4 harris41 144: exit 5;
1.1 harris41 145: }
146:
147: # Verify password entry
148: if (crypt($oldpwd,$useroldcryptpwd) ne $useroldcryptpwd) {
1.4 harris41 149: print "Error. Invalid entry of current password.\n" unless $noprint;
1.6 harris41 150: unlink('/tmp/lock_lcpasswd');
1.4 harris41 151: exit 6;
1.1 harris41 152: }
153:
1.2 harris41 154: # Construct new password entry (random salt)
155: my $newcryptpwd=crypt($newpwd,(join '', ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[rand 64, rand 64]));
1.1 harris41 156: $U[1]=$newcryptpwd;
1.6 harris41 157: my $userline=join(':',@U);
1.2 harris41 158: my $rootid=&enable_root_capability;
159: if ($rootid!=0) {
1.4 harris41 160: print "Error. Root was not successfully enabled.\n" unless $noprint;
1.6 harris41 161: unlink('/tmp/lock_lcpasswd');
1.4 harris41 162: exit 7;
1.2 harris41 163: }
1.6 harris41 164: open PASSWORDFILE, '>/etc/passwd' or (print("Error. Cannot open /etc/passwd.\n") && unlink('/tmp/lock_lcpasswd') && exit(8));
1.1 harris41 165: for my $l (@lines) {
166: @F=split(/\:/,$l);
167: if ($F[0] eq $username) {print PASSWORDFILE "$userline\n";}
168: else {print PASSWORDFILE "$l\n";}
169: }
170: close PASSWORDFILE;
1.8 harris41 171:
1.2 harris41 172: ($>,$<)=(0,0); # fool smbpasswd here to think this is not a setuid environment
1.6 harris41 173: unless (-e '/etc/smbpasswd') {
174: open (OUT,'>/etc/smbpasswd'); close OUT;
1.2 harris41 175: }
176: my $smbexist=0;
1.6 harris41 177: open (IN, '</etc/smbpasswd');
1.2 harris41 178: my @lines=<IN>;
179: close IN;
180: for my $l (@lines) {
181: chop $l;
182: my @F=split(/\:/,$l);
183: if ($F[0] eq $username) {$smbexist=1;}
184: }
185: unless ($smbexist) {
1.6 harris41 186: open(OUT,'>>/etc/smbpasswd');
187: print OUT join(':',($safeusername,$userid,'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX','','/home/'.$safeusername,'/bin/bash')) . "\n";
1.2 harris41 188: close OUT;
189: }
190: open(OUT,"|/usr/bin/smbpasswd -s $safeusername>/dev/null");
191: print OUT $newpwd; print OUT "\n";
192: print OUT $newpwd; print OUT "\n";
193: close OUT;
194: $<=$wwwid; # unfool the program
1.1 harris41 195: &disable_root_capability;
1.6 harris41 196: unlink('/tmp/lock_lcpasswd');
1.4 harris41 197: exit 0;
1.1 harris41 198:
1.4 harris41 199: # ----------------------------------------------------------- have setuid script run as root
1.1 harris41 200: sub enable_root_capability {
1.2 harris41 201: if ($wwwid==$>) {
1.1 harris41 202: ($<,$>)=($>,$<);
203: ($(,$))=($),$();
204: }
205: else {
206: # root capability is already enabled
207: }
1.2 harris41 208: return $>;
1.1 harris41 209: }
210:
1.4 harris41 211: # ----------------------------------------------------------- have setuid script run as www
1.1 harris41 212: sub disable_root_capability {
1.2 harris41 213: if ($wwwid==$<) {
1.1 harris41 214: ($<,$>)=($>,$<);
215: ($(,$))=($),$();
216: }
217: else {
218: # root capability is already disabled
219: }
220: }
221:
1.4 harris41 222: # ----------------------------------- make sure that another lcpasswd process isn't running
1.1 harris41 223: sub try_to_lock {
224: my ($lockfile)=@_;
225: my $currentpid;
226: my $lastpid;
1.5 harris41 227: # Do not manipulate lock file as root
228: if ($>==0) {
229: return 0;
230: }
231: # Try to generate lock file.
232: # Wait 3 seconds. If same process id is in
233: # lock file, then assume lock file is stale, and
234: # go ahead. If process id's fluctuate, try
235: # for a maximum of 10 times.
1.1 harris41 236: for (0..10) {
237: if (-e $lockfile) {
238: open(LOCK,"<$lockfile");
239: $currentpid=<LOCK>;
240: close LOCK;
241: if ($currentpid==$lastpid) {
242: last;
243: }
244: sleep 3;
245: $lastpid=$currentpid;
246: }
247: else {
248: last;
249: }
250: if ($_==10) {
251: return 0;
252: }
253: }
254: open(LOCK,">$lockfile");
255: print LOCK $$;
256: close LOCK;
257: return 1;
258: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>