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