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>
500 Internal Server Error

Internal Server Error

The server encountered an internal error or misconfiguration and was unable to complete your request.

Please contact the server administrator at root@localhost to inform them of the time this error occurred, and the actions you performed just before this error.

More information about this error may be available in the server error log.