Diff for /loncom/lcpasswd between versions 1.4 and 1.22

version 1.4, 2000/10/27 23:32:24 version 1.22, 2010/10/12 10:33:47
Line 1 Line 1
 #!/usr/bin/perl  #!/usr/bin/perl
   # The Learning Online Network with CAPA
 #  #
 # lcpasswd  # lcpasswd - LON-CAPA setuid script to synchronously change all
   #            filesystem-related passwords (samba, unix, etc)
 #  #
 # Scott Harrison  # YEAR=2002
 # October 27, 2000  # 02/19 Matthew Hall
   #
   # $Id$
   ###
   
   ###############################################################################
   ##                                                                           ##
   ## ORGANIZATION OF THIS PERL SCRIPT                                          ##
   ##                                                                           ##
   ## 1. Description of script                                                  ##
   ## 2. Invoking script (standard input only)                                  ##
   ## 3. Example usage inside another piece of code                             ##
   ## 4. Description of functions                                               ##
   ## 5. Exit codes                                                             ##
   ##                                                                           ##
   ###############################################################################
   
 use strict;  use strict;
   
   # ------------------------------------------------------- Description of script
   #
 # This script is a setuid script that should  # This script is a setuid script that should
 # be run by user 'www'.  This script allows  # be run by user 'www'.  This script allows
 # for synchronous entry of passwords into  # for synchronous entry of passwords into
 # both the /etc/passwd and the /etc/smbpasswd  # both the /etc/passwd and the /etc/smbpasswd
 # files.  # files.
   #
   # This script works under the same process control mechanism
   # as lcuseradd and lcpasswd, to make sure that only one of these
   # processes is running at any one time on the system.
   
   # --------------------------------------- Invoking script (standard input only)
   #
 # Standard input usage  # Standard input usage
 # First line is USERNAME  # First line is USERNAME
 # Second line is CURRENT PASSWORD  # Second line is NEW PASSWORD
 # Third line is NEW PASSWORD  # Third line is NEW PASSWORD
   
 # Command-line arguments  
 # Yes, but be very careful here (don't pass shell commands)  
 # and this is only supported to allow perl-system calls.  
   
 # Usage within code  
 # Note: NEVER run as system("NAME OLDPWD NEWPWD")  
 #  #
 # $exitcode=system("NAME","OLDPWD","NEWPWD")/256;  # Valid passwords must consist of the
 # print "uh-oh" if $exitcode;  # ascii characters within the inclusive
   # range of 0x20 (32) to 0x7E (126).
   # These characters are:
   # SPACE and
   # !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO
   # PQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
   #
   # Valid user names must consist of ascii
   # characters that are alphabetical characters
   # (A-Z,a-z), numeric (0-9), or the underscore
   # mark (_). (Essentially, the perl regex \w).
   # User names must begin with an alphabetical character
   # (A-Z,a-z).
   
   # ---------------------------------------------------- Description of functions
   # enable_root_capability() : have setuid script run as root
   # disable_root_capability() : have setuid script run as www
   # try_to_lock() : make sure that another lcpasswd process isn't running
   
   # ------------------------------------------------------------------ Exit codes
 # These are the exit codes.  # These are the exit codes.
 # ( (0,"ok"),  # ( (0,"ok"),
 #   (1,"User ID mismatch.  This program must be run as user 'www'),  #   (1,"User ID mismatch.  This program must be run as user 'www'"),
 #   (2,"Error. This program does not accept command-line arguments."),  #   (2,"Error. This program needs 3 command-line arguments (username, old ".
 #   (3,"Error. Three lines need to be entered into standard input.\n"),  #       password, new password)."),
 #   (4,"Error. Too many other simultaneous password change requests being made.\n"),  #   (3,"Error. Three lines need to be entered into standard input."),
 #   (5,"Error. User $username does not exist.\n"),  #   (4,"Error. Too many other simultaneous password change requests being ".
 #   (6,"Error. Invalid entry of current password.\n"),  #       made."),
 #   (7,"Error.  Root was not successfully enabled.\n") )  #   (5,"Error. User $username does not exist."),
   #   (6,"Error. Invalid entry of current password."),
   #   (7,"Error. Root was not successfully enabled."),
   #   (8,"Error. Cannot set password."),
   #   (9,"Error. The user name specified has invalid characters."),
   #   (10,"Error. A password entry had an invalid character.") )
   
   # ------------------------------------------------------------- Initializations
 # Security  # Security
 $ENV{'PATH'}="/bin:/usr/bin"; # Nullify path information except for what smbpasswd needs  $ENV{'PATH'}='/bin:/usr/bin:/usr/local/sbin:/home/httpd/perl'; # Nullify path
 $ENV{'BASH_ENV'}=""; # Nullify shell environment information.                                                                 # information
   delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; # nullify potential taints
   
   # Do not print error messages
   my $noprint=1;
   
   print "In lcpasswd" unless $noprint;
   
   # ----------------------------- Make sure this process is running from user=www
   my $wwwid=getpwnam('www');
   
 # Do not print error messages if there are command-line arguments  
 my $noprint=0;  
 if (@ARGV) {  
     $noprint=1;  
 }  
   
 open (IN, "</etc/passwd");  
 my @lines=<IN>;  
 close IN;  
 my $wwwid;  
 for my $l (@lines) {  
     chop $l;  
     my @F=split(/\:/,$l);  
     if ($F[0] eq 'www') {$wwwid=$F[2];}  
 }  
 if ($wwwid!=$<) {  if ($wwwid!=$<) {
     print("User ID mismatch.  This program must be run as user 'www'\n") unless $noprint;      print("User ID mismatch.  This program must be run as user 'www'\n")
    unless $noprint;
     exit 1;      exit 1;
 }  }
 &disable_root_capability;  
   
 # Gather input.  Should only be 3 values.  # ----------------------------------- Start running script with www permissions
 my @input;  
 if (@ARGV==3) {  
     @input=@ARGV;  # --------------------------- Handle case of another lcpasswd process (locking)
 }  unless (&try_to_lock('/tmp/lock_lcpasswd')) {
 elsif (@ARGV) {      print "Error. Too many other simultaneous password change requests being ".
     print("Error. This program needs 3 command-line arguments (username, old password, new password).\n") unless $noprint;   "made.\n" unless $noprint;
     exit 2;  
 }  
 else {  
     @input=<>;  
     if (@input!=3) {  
  print("Error. Three lines need to be entered into standard input.\n") unless $noprint;  
  exit 3;  
     }  
     map {chop} @input;  
 }  
 # Handle case of another lcpasswd process  
 unless (&try_to_lock("/tmp/lock_lcpasswd")) {  
     print "Error. Too many other simultaneous password change requests being made.\n" unless $noprint;  
     exit 4;      exit 4;
 }  }
   
 my ($username,$oldpwd,$newpwd)=@input;  # ------- Error-check input, need 3 values (user name, password 1, password 2).
   my @input;
   @input=<>;
   if (@input!=3) {
       print("Error. Three lines need to be entered into standard input.\n")
    unless $noprint;
       unlink('/tmp/lock_lcpasswd');
       exit 3;
   }
   foreach (@input) {chomp;}
   
 # Grab the line corresponding to username  my ($username,$password1,$password2)=@input;
 my ($userid,$useroldcryptpwd);  $username=~/^(\w+)$/;
 my @F; my @U;  my $safeusername=$1;
 for my $l (@lines) {  if (($username ne $safeusername) or ($safeusername!~/^[A-Za-z]/)) {
     @F=split(/\:/,$l);      print "Error. The user name specified has invalid characters.\n";
     if ($F[0] eq $username) {($userid,$useroldcryptpwd)=($F[2],$F[1]); @U=@F;}      unlink('/tmp/lock_lcpasswd');
       exit 9;
   }
   my $pbad=0;
   foreach (split(//,$password1)) {if ((ord($_)<32)||(ord($_)>126)){$pbad=1;}}
   foreach (split(//,$password2)) {if ((ord($_)<32)||(ord($_)>126)){$pbad=1;}}
   if ($pbad) {
       print "Error. A password entry had an invalid character.\n";
       unlink('/tmp/lock_lcpasswd');
       exit 10;
   }
   
   # -- Only add user if the two password arguments match.
   if ($password1 ne $password2) {
       print "Error. Password mismatch.\n" unless $noprint;
       unlink('/tmp/lock_lcpasswd');
       exit 13;
 }  }
   
 # Verify existence of user  # Verify existence of user
 if (!defined($userid)) {  unless(getpwnam($safeusername)) {
     print "Error. User $username does not exist.\n" unless $noprint;      print "Error. User $username does not exist.\n" unless $noprint;
     unlink("/tmp/lock_lcpasswd");      unlink('/tmp/lock_lcpasswd');
     exit 5;      exit 5;
 }  }
   &enable_root_capability;
   ($>,$<)=(0,0);
   
   print "Now $> , $< , -invoking pwchange with $safeusername $password1"
       unless $noprint;
   open OUT,"|pwchange $safeusername";
   print OUT $password1;
   print OUT "\n";
   close OUT;
   ($>,$<)=(0,$wwwid);
   
   print "pwchange done, back to uid $wwwid" unless $noprint;
   
 # Verify password entry  if ($?) {
 if (crypt($oldpwd,$useroldcryptpwd) ne $useroldcryptpwd) {      exit 8;
     print "Error. Invalid entry of current password.\n" unless $noprint;  
     unlink("/tmp/lock_lcpasswd");  
     exit 6;  
 }  
   
 # Construct new password entry (random salt)  
 my $newcryptpwd=crypt($newpwd,(join '', ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[rand 64, rand 64]));  
 $U[1]=$newcryptpwd;  
 my $userline=join(":",@U);  
 my $rootid=&enable_root_capability;  
 if ($rootid!=0) {  
     print "Error.  Root was not successfully enabled.\n" unless $noprint;  
     unlink("/tmp/lock_lcpasswd");  
     exit 7;  
 }  
 open PASSWORDFILE, ">/etc/passwd" or die("Cannot open /etc/passwd!");  
 for my $l (@lines) {  
     @F=split(/\:/,$l);  
     if ($F[0] eq $username) {print PASSWORDFILE "$userline\n";}  
     else {print PASSWORDFILE "$l\n";}  
 }  }
 close PASSWORDFILE;  my $userid=getpwnam($safeusername);
 $username=~/^(\w+)$/;  
 my $safeusername=$1;  if (-e '/usr/bin/smbpasswd') {
 ($>,$<)=(0,0); # fool smbpasswd here to think this is not a setuid environment  
 unless (-e "/etc/smbpasswd") {      ($>,$<)=(0,0); # fool smbpasswd here to think this is not a setuid
     open (OUT,">/etc/smbpasswd"); close OUT;                     # environment
 }  
 my $smbexist=0;  #   If the -a switch is put on the smbpasswd
 open (IN, "</etc/smbpasswd");  # command line, either a new entry will be created or the old one
 my @lines=<IN>;  # will be used. 
 close IN;  # Therefore the old strategy of looking for and adding a dummy entry is 
 for my $l (@lines) {  # not needed... Finally, the smbpasswd file is in /etc/samba not 
     chop $l;  # /etc/smbpasswd as older versions of the script implied.
     my @F=split(/\:/,$l);  
     if ($F[0] eq $username) {$smbexist=1;}      print "Running smbpasswd" unless $noprint;
 }      open(OUT,"|/usr/bin/smbpasswd -s -a $safeusername>/dev/null") or
 unless ($smbexist) {   die('cannot run smbpasswd');
     open(OUT,">>/etc/smbpasswd");      print OUT $password2; print OUT "\n";
     print OUT join(":",($safeusername,$userid,'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX','','/home/'.$safeusername,'/bin/bash')) . "\n";      print OUT $password2; print OUT "\n";
     close OUT;      close OUT;
       $<=$wwwid; # unfool the program
       print "smbpasswd done" unless $noprint;
 }  }
 open(OUT,"|/usr/bin/smbpasswd -s $safeusername>/dev/null");  
 print OUT $newpwd; print OUT "\n";  
 print OUT $newpwd; print OUT "\n";  unlink('/tmp/lock_lcpasswd');
 close OUT;  
 $<=$wwwid; # unfool the program  
 &disable_root_capability;  
 unlink("/tmp/lock_lcpasswd");  
 exit 0;  exit 0;
   
 # ----------------------------------------------------------- have setuid script run as root  # ---------------------------------------------- have setuid script run as root
 sub enable_root_capability {  sub enable_root_capability {
     if ($wwwid==$>) {      if ($wwwid==$>) {
  ($<,$>)=($>,$<);   ($<,$>)=($>,0);
  ($(,$))=($),$();   ($(,$))=($),0);
     }      }
     else {      else {
  # root capability is already enabled   # root capability is already enabled
Line 168  sub enable_root_capability { Line 206  sub enable_root_capability {
     return $>;      return $>;
 }  }
   
 # ----------------------------------------------------------- have setuid script run as www  # ----------------------------------------------- have setuid script run as www
 sub disable_root_capability {  sub disable_root_capability {
     if ($wwwid==$<) {      if ($wwwid==$<) {
  ($<,$>)=($>,$<);   ($<,$>)=($>,$<);
Line 179  sub disable_root_capability { Line 217  sub disable_root_capability {
     }      }
 }  }
   
 # ----------------------------------- make sure that another lcpasswd process isn't running  # ----------------------- make sure that another lcpasswd process isn't running
 sub try_to_lock {  sub try_to_lock {
     my ($lockfile)=@_;      my ($lockfile)=@_;
     my $currentpid;      my $currentpid;
     my $lastpid;      my $lastpid;
   
       # Try to generate lock file.
       # Wait 3 seconds.  If same process id is in
       # lock file, then assume lock file is stale, and
       # go ahead.  If process id's fluctuate, try
       # for a maximum of 10 times.
     for (0..10) {      for (0..10) {
  if (-e $lockfile) {   if (-e $lockfile) {
     open(LOCK,"<$lockfile");      open(LOCK,"<$lockfile");
Line 207  sub try_to_lock { Line 251  sub try_to_lock {
     close LOCK;      close LOCK;
     return 1;      return 1;
 }  }
   
   =head1 NAME
   
   lcpasswd - LON-CAPA setuid script to synchronously change all
              filesystem-related passwords (samba, unix, etc)
   
   =head1 DESCRIPTION
   
   LON-CAPA setuid script to synchronously change all
   filesystem-related passwords (samba, unix, etc)
   
   =head1 README
   
   LON-CAPA setuid script to synchronously change all
   filesystem-related passwords (samba, unix, etc)
   
   =head1 PREREQUISITES
   
   =head1 COREQUISITES
   
   =pod OSNAMES
   
   linux
   
   =pod SCRIPT CATEGORIES
   
   LONCAPA/Administrative
   
   =cut

Removed from v.1.4  
changed lines
  Added in v.1.22


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