File:  [LON-CAPA] / loncom / lcpasswd
Revision 1.2: download - view: text, annotated - select for diffs
Fri Oct 27 22:02:23 2000 UTC (23 years, 6 months ago) by harris41
Branches: MAIN
CVS tags: HEAD
it works.  implements locking (so that two lcpasswd processes do not muck
things up at the same time).  supposed to work under chmod 6755 (setuid
script).  only runs when the initial (real) uid corresponds to www.  I
do not interface with the unix 'passwd' script, but just do the crypt
calculations separately.  I minimalize environment variables to improve
security.  I do not use smbadduser; I just create a blank but unworking entry
(do not use smbpasswd -a either due to crazy side effect).  I have
to fake the smbpasswd into working in a non setuid mode.  Also, I have
to \w+ grab off of the username in order to "fool" perl into thinking
the username is a safe value to pass to the shell (which after the \w+
grab, indeed is). -Scott

    1: #!/usr/bin/perl
    2: 
    3: use strict;
    4: 
    5: # Scott Harrison
    6: # October 27, 2000
    7: 
    8: # This script is a setuid script that should
    9: # be run by user 'www'.
   10: 
   11: # Standard input usage
   12: # First line is USERNAME
   13: # Second line is CURRENT PASSWORD
   14: # Third line is NEW PASSWORD
   15: 
   16: # Security
   17: $ENV{'PATH'}="/bin:/usr/bin"; # Nullify path information except for what smbpasswd needs
   18: $ENV{'BASH_ENV'}=""; # Nullify shell environment information.
   19: 
   20: open (IN, "</etc/passwd");
   21: my @lines=<IN>;
   22: close IN;
   23: my $wwwid;
   24: for my $l (@lines) {
   25:     chop $l;
   26:     my @F=split(/\:/,$l);
   27:     if ($F[0] eq 'www') {$wwwid=$F[2];}
   28: }
   29: if ($wwwid!=$<) {
   30:     print("User ID mismatch.  This program must be run as user 'www'\n");
   31:     exit 0;
   32: }
   33: &disable_root_capability;
   34: if (@ARGV) {
   35:     print("Error. This program does not accept command-line arguments.\n");
   36:     exit 0;
   37: }
   38: 
   39: # Gather input from standard input.  Should only be 3 lines.
   40: my @input=<>;
   41: if (@input!=3) {
   42:     print("Error. Three lines need to be entered into standard input.\n");
   43:     exit 0;
   44: }
   45: 
   46: # Handle case of another lcpasswd process
   47: unless (&try_to_lock("/tmp/lock_lcpasswd")) {
   48:     print "Error. Too many other simultaneous password change requests being made.\n";
   49:     exit 0;
   50: }
   51: 
   52: my ($username,$oldpwd,$newpwd)=map {chop; $_} @input;
   53: 
   54: # Grab the line corresponding to username
   55: my ($userid,$useroldcryptpwd);
   56: my @F; my @U;
   57: for my $l (@lines) {
   58:     @F=split(/\:/,$l);
   59:     if ($F[0] eq $username) {($userid,$useroldcryptpwd)=($F[2],$F[1]); @U=@F;}
   60: }
   61: 
   62: # Verify existence of user
   63: if (!defined($userid)) {
   64:     print "Error. User $username does not exist.\n";
   65:     exit 0;
   66: }
   67: 
   68: # Verify password entry
   69: if (crypt($oldpwd,$useroldcryptpwd) ne $useroldcryptpwd) {
   70:     print "Error. Invalid entry of current password.\n";
   71:     exit 0;
   72: }
   73: 
   74: # Construct new password entry (random salt)
   75: my $newcryptpwd=crypt($newpwd,(join '', ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[rand 64, rand 64]));
   76: $U[1]=$newcryptpwd;
   77: my $userline=join(":",@U);
   78: my $rootid=&enable_root_capability;
   79: if ($rootid!=0) {
   80:     print "Error.  Root was not successfully enabled.\n";
   81:     exit 0;
   82: }
   83: open PASSWORDFILE, ">/etc/passwd" or die("Cannot open /etc/passwd!");
   84: for my $l (@lines) {
   85:     @F=split(/\:/,$l);
   86:     if ($F[0] eq $username) {print PASSWORDFILE "$userline\n";}
   87:     else {print PASSWORDFILE "$l\n";}
   88: }
   89: close PASSWORDFILE;
   90: $username=~/^(\w+)$/;
   91: my $safeusername=$1;
   92: ($>,$<)=(0,0); # fool smbpasswd here to think this is not a setuid environment
   93: unless (-e "/etc/smbpasswd") {
   94:     open (OUT,">/etc/smbpasswd"); close OUT;
   95: }
   96: my $smbexist=0;
   97: open (IN, "</etc/smbpasswd");
   98: my @lines=<IN>;
   99: close IN;
  100: for my $l (@lines) {
  101:     chop $l;
  102:     my @F=split(/\:/,$l);
  103:     if ($F[0] eq $username) {$smbexist=1;}
  104: }
  105: unless ($smbexist) {
  106:     open(OUT,">>/etc/smbpasswd");
  107:     print OUT join(":",($safeusername,$userid,'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX','','/home/'.$safeusername,'/bin/bash')) . "\n";
  108:     close OUT;
  109: }
  110: open(OUT,"|/usr/bin/smbpasswd -s $safeusername>/dev/null");
  111: print OUT $newpwd; print OUT "\n";
  112: print OUT $newpwd; print OUT "\n";
  113: close OUT;
  114: $<=$wwwid; # unfool the program
  115: &disable_root_capability;
  116: unlink("/tmp/lock_lcpasswd");
  117: 
  118: sub enable_root_capability {
  119:     if ($wwwid==$>) {
  120: 	($<,$>)=($>,$<);
  121: 	($(,$))=($),$();
  122:     }
  123:     else {
  124: 	# root capability is already enabled
  125:     }
  126:     return $>;
  127: }
  128: 
  129: sub disable_root_capability {
  130:     if ($wwwid==$<) {
  131: 	($<,$>)=($>,$<);
  132: 	($(,$))=($),$();
  133:     }
  134:     else {
  135: 	# root capability is already disabled
  136:     }
  137: }
  138: 
  139: sub try_to_lock {
  140:     my ($lockfile)=@_;
  141:     my $currentpid;
  142:     my $lastpid;
  143:     for (0..10) {
  144: 	if (-e $lockfile) {
  145: 	    open(LOCK,"<$lockfile");
  146: 	    $currentpid=<LOCK>;
  147: 	    close LOCK;
  148: 	    if ($currentpid==$lastpid) {
  149: 		last;
  150: 	    }
  151: 	    sleep 3;
  152: 	    $lastpid=$currentpid;
  153: 	}
  154: 	else {
  155: 	    last;
  156: 	}
  157: 	if ($_==10) {
  158: 	    return 0;
  159: 	}
  160:     }
  161:     open(LOCK,">$lockfile");
  162:     print LOCK $$;
  163:     close LOCK;
  164:     return 1;
  165: }

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