File:  [LON-CAPA] / loncom / lcpasswd
Revision 1.12: download - view: text, annotated - select for diffs
Tue Oct 23 03:42:30 2001 UTC (22 years, 6 months ago) by harris41
Branches: MAIN
CVS tags: HEAD
updated and ready to use

    1: #!/usr/bin/perl
    2: #
    3: # lcpasswd
    4: #
    5: # Scott Harrison
    6: # SH: October 27, 2000
    7: # SH: October 28, 2000
    8: # SH: October 29, 2000
    9: # YEAR=2001
   10: # Scott Harrison 10/22
   11: 
   12: ###############################################################################
   13: ##                                                                           ##
   14: ## ORGANIZATION OF THIS PERL SCRIPT                                          ##
   15: ##                                                                           ##
   16: ## 1. Description of script                                                  ##
   17: ## 2. Invoking script (standard input only)                                  ##
   18: ## 3. Example usage inside another piece of code                             ##
   19: ## 4. Description of functions                                               ##
   20: ## 5. Exit codes                                                             ##
   21: ##                                                                           ##
   22: ###############################################################################
   23: 
   24: use strict;
   25: 
   26: # ------------------------------------------------------- Description of script
   27: #
   28: # This script is a setuid script that should
   29: # be run by user 'www'.  This script allows
   30: # for synchronous entry of passwords into
   31: # both the /etc/passwd and the /etc/smbpasswd
   32: # files.
   33: #
   34: # This script works under the same process control mechanism
   35: # as lcuseradd and lcpasswd, to make sure that only one of these
   36: # processes is running at any one time on the system.
   37: 
   38: # --------------------------------------- Invoking script (standard input only)
   39: #
   40: # Standard input usage
   41: # First line is USERNAME
   42: # Second line is CURRENT PASSWORD
   43: # Third line is NEW PASSWORD
   44: #
   45: # Valid passwords must consist of the
   46: # ascii characters within the inclusive
   47: # range of 0x20 (32) to 0x7E (126).
   48: # These characters are:
   49: # SPACE and
   50: # !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO
   51: # PQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
   52: #
   53: # Valid user names must consist of ascii
   54: # characters that are alphabetical characters
   55: # (A-Z,a-z), numeric (0-9), or the underscore
   56: # mark (_). (Essentially, the perl regex \w).
   57: # User names must begin with an alphabetical character
   58: # (A-Z,a-z).
   59: 
   60: # ---------------------------------------------------- Description of functions
   61: # enable_root_capability() : have setuid script run as root
   62: # disable_root_capability() : have setuid script run as www
   63: # try_to_lock() : make sure that another lcpasswd process isn't running
   64: 
   65: # ------------------------------------------------------------------ Exit codes
   66: # These are the exit codes.
   67: # ( (0,"ok"),
   68: #   (1,"User ID mismatch.  This program must be run as user 'www'"),
   69: #   (2,"Error. This program needs 3 command-line arguments (username, old ".
   70: #       password, new password)."),
   71: #   (3,"Error. Three lines need to be entered into standard input."),
   72: #   (4,"Error. Too many other simultaneous password change requests being ".
   73: #       made."),
   74: #   (5,"Error. User $username does not exist."),
   75: #   (6,"Error. Invalid entry of current password."),
   76: #   (7,"Error. Root was not successfully enabled."),
   77: #   (8,"Error. Cannot set password."),
   78: #   (9,"Error. The user name specified has invalid characters."),
   79: #   (10,"Error. A password entry had an invalid character.") )
   80: 
   81: # ------------------------------------------------------------- Initializations
   82: # Security
   83: $ENV{'PATH'}='/bin:/usr/bin:/usr/local/sbin:/home/httpd/perl'; # Nullify path
   84:                                                                # information
   85: $ENV{'BASH_ENV'}=''; # Nullify shell environment information.
   86: 
   87: # Do not print error messages
   88: my $noprint=1;
   89: 
   90: # ----------------------------- Make sure this process is running from user=www
   91: my $wwwid=getpwnam('www');
   92: &disable_root_capability;
   93: if ($wwwid!=$>) {
   94:     print("User ID mismatch.  This program must be run as user 'www'\n")
   95: 	unless $noprint;
   96:     exit 1;
   97: }
   98: 
   99: # ----------------------------------- Start running script with www permissions
  100: &disable_root_capability;
  101: 
  102: # --------------------------- Handle case of another lcpasswd process (locking)
  103: unless (&try_to_lock('/tmp/lock_lcpasswd')) {
  104:     print "Error. Too many other simultaneous password change requests being ".
  105: 	"made.\n" unless $noprint;
  106:     exit 4;
  107: }
  108: 
  109: # ------- Error-check input, need 3 values (user name, password 1, password 2).
  110: my @input;
  111: @input=<>;
  112: if (@input!=3) {
  113:     print("Error. Three lines need to be entered into standard input.\n")
  114: 	unless $noprint;
  115:     unlink('/tmp/lock_lcpasswd');
  116:     exit 3;
  117: }
  118: map {chomp} @input;
  119: 
  120: my ($username,$password1,$password2)=@input;
  121: $username=~/^(\w+)$/;
  122: my $safeusername=$1;
  123: if (($username ne $safeusername) or ($safeusername!~/^[A-Za-z]/)) {
  124:     print "Error. The user name specified has invalid characters.\n";
  125:     unlink('/tmp/lock_lcpasswd');
  126:     exit 9;
  127: }
  128: my $pbad=0;
  129: map {if (($_<32)&&($_>126)){$pbad=1;}} (split(//,$password1));
  130: map {if (($_<32)&&($_>126)){$pbad=1;}} (split(//,$password2));
  131: if ($pbad) {
  132:     print "Error. A password entry had an invalid character.\n";
  133:     unlink('/tmp/lock_lcpasswd');
  134:     exit 10;
  135: }
  136: 
  137: # -- Only add user if the two password arguments match.
  138: if ($password1 ne $password2) {
  139:     print "Error. Password mismatch.\n" unless $noprint;
  140:     unlink('/tmp/lock_lcpasswd');
  141:     exit 13;
  142: }
  143: 
  144: # Verify existence of user
  145: unless(getpwnam($safeusername)) {
  146:     print "Error. User $username does not exist.\n" unless $noprint;
  147:     unlink('/tmp/lock_lcpasswd');
  148:     exit 5;
  149: }
  150: 
  151: &enable_root_capability;
  152: ($>,$<)=(0,0);
  153: open OUT,"|pwchange $safeusername";
  154: print OUT $password1;
  155: print OUT "\n";
  156: close OUT;
  157: ($>,$<)=(0,500);
  158: 
  159: if ($?) {
  160:     exit 8;
  161: }
  162: my $userid=getpwnam($safeusername);
  163: 
  164: unless (-e '/usr/bin/smbpasswd') {
  165: 
  166:     ($>,$<)=(0,0); # fool smbpasswd here to think this is not a setuid
  167:                    # environment
  168:     unless (-e '/etc/smbpasswd') {
  169: 	open (OUT,'>/etc/smbpasswd'); close OUT;
  170:     }
  171: 
  172:     my $smbexist=0;
  173:     open (IN, '</etc/smbpasswd');
  174:     my @lines=<IN>;
  175:     close IN;
  176:     for my $l (@lines) {
  177: 	chop $l;
  178: 	my @F=split(/\:/,$l);
  179: 	if ($F[0] eq $username) {$smbexist=1;}
  180:     }
  181:     unless ($smbexist) {
  182: 	open(OUT,'>>/etc/smbpasswd');
  183: 	print OUT join(':',($safeusername,$userid,
  184: 			    'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:XXXXXXXXXXXXXX'.
  185: 			    'XXXXXXXXXXXXXXXXXX','','/home/'.$safeusername,
  186: 			    '/bin/bash')) . "\n";
  187: 	close OUT;
  188:     }
  189: 
  190:     open(OUT,"|/usr/bin/smbpasswd -s $safeusername>/dev/null");
  191:     print OUT $password2; print OUT "\n";
  192:     print OUT $password2; print OUT "\n";
  193:     close OUT;
  194:     $<=$wwwid; # unfool the program
  195: }
  196: 
  197: &disable_root_capability;
  198: unlink('/tmp/lock_lcpasswd');
  199: exit 0;
  200: 
  201: # ---------------------------------------------- have setuid script run as root
  202: sub enable_root_capability {
  203:     if ($wwwid==$>) {
  204: 	($<,$>)=($>,$<);
  205: 	($(,$))=($),$();
  206:     }
  207:     else {
  208: 	# root capability is already enabled
  209:     }
  210:     return $>;
  211: }
  212: 
  213: # ----------------------------------------------- have setuid script run as www
  214: sub disable_root_capability {
  215:     if ($wwwid==$<) {
  216: 	($<,$>)=($>,$<);
  217: 	($(,$))=($),$();
  218:     }
  219:     else {
  220: 	# root capability is already disabled
  221:     }
  222: }
  223: 
  224: # ----------------------- make sure that another lcpasswd process isn't running
  225: sub try_to_lock {
  226:     my ($lockfile)=@_;
  227:     my $currentpid;
  228:     my $lastpid;
  229:     # Do not manipulate lock file as root
  230:     if ($>==0) {
  231: 	return 0;
  232:     }
  233:     # Try to generate lock file.
  234:     # Wait 3 seconds.  If same process id is in
  235:     # lock file, then assume lock file is stale, and
  236:     # go ahead.  If process id's fluctuate, try
  237:     # for a maximum of 10 times.
  238:     for (0..10) {
  239: 	if (-e $lockfile) {
  240: 	    open(LOCK,"<$lockfile");
  241: 	    $currentpid=<LOCK>;
  242: 	    close LOCK;
  243: 	    if ($currentpid==$lastpid) {
  244: 		last;
  245: 	    }
  246: 	    sleep 3;
  247: 	    $lastpid=$currentpid;
  248: 	}
  249: 	else {
  250: 	    last;
  251: 	}
  252: 	if ($_==10) {
  253: 	    return 0;
  254: 	}
  255:     }
  256:     open(LOCK,">$lockfile");
  257:     print LOCK $$;
  258:     close LOCK;
  259:     return 1;
  260: }

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