File:  [LON-CAPA] / loncom / lcpasswd
Revision 1.4: download - view: text, annotated - select for diffs
Fri Oct 27 23:32:24 2000 UTC (23 years, 6 months ago) by harris41
Branches: MAIN
CVS tags: HEAD
allow for command-line arguments in order to enable use
within other scripts (so they can read exit status);
disable error message printing when command-line arguments
are given

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>