Annotation of loncom/lcpasswd, revision 1.12

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.11      harris41    8: # SH: October 29, 2000
1.12    ! harris41    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: ###############################################################################
1.1       harris41   23: 
                     24: use strict;
                     25: 
1.12    ! harris41   26: # ------------------------------------------------------- Description of script
        !            27: #
1.1       harris41   28: # This script is a setuid script that should
1.4       harris41   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.
1.12    ! harris41   33: #
1.5       harris41   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: 
1.12    ! harris41   38: # --------------------------------------- Invoking script (standard input only)
        !            39: #
1.1       harris41   40: # Standard input usage
                     41: # First line is USERNAME
                     42: # Second line is CURRENT PASSWORD
                     43: # Third line is NEW PASSWORD
1.12    ! harris41   44: #
1.8       harris41   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{|}~
1.12    ! harris41   52: #
1.8       harris41   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).
1.12    ! harris41   57: # User names must begin with an alphabetical character
        !            58: # (A-Z,a-z).
1.8       harris41   59: 
1.12    ! harris41   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
1.4       harris41   64: 
1.12    ! harris41   65: # ------------------------------------------------------------------ Exit codes
1.4       harris41   66: # These are the exit codes.
                     67: # ( (0,"ok"),
1.7       harris41   68: #   (1,"User ID mismatch.  This program must be run as user 'www'"),
1.12    ! harris41   69: #   (2,"Error. This program needs 3 command-line arguments (username, old ".
        !            70: #       password, new password)."),
1.6       harris41   71: #   (3,"Error. Three lines need to be entered into standard input."),
1.12    ! harris41   72: #   (4,"Error. Too many other simultaneous password change requests being ".
        !            73: #       made."),
1.6       harris41   74: #   (5,"Error. User $username does not exist."),
                     75: #   (6,"Error. Invalid entry of current password."),
1.10      harris41   76: #   (7,"Error. Root was not successfully enabled."),
1.12    ! harris41   77: #   (8,"Error. Cannot set password."),
1.10      harris41   78: #   (9,"Error. The user name specified has invalid characters."),
                     79: #   (10,"Error. A password entry had an invalid character.") )
1.4       harris41   80: 
1.12    ! harris41   81: # ------------------------------------------------------------- Initializations
1.1       harris41   82: # Security
1.12    ! harris41   83: $ENV{'PATH'}='/bin:/usr/bin:/usr/local/sbin:/home/httpd/perl'; # Nullify path
        !            84:                                                                # information
1.6       harris41   85: $ENV{'BASH_ENV'}=''; # Nullify shell environment information.
1.1       harris41   86: 
1.12    ! harris41   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;
1.4       harris41   96:     exit 1;
1.1       harris41   97: }
1.12    ! harris41   98: 
        !            99: # ----------------------------------- Start running script with www permissions
1.2       harris41  100: &disable_root_capability;
1.1       harris41  101: 
1.12    ! harris41  102: # --------------------------- Handle case of another lcpasswd process (locking)
1.6       harris41  103: unless (&try_to_lock('/tmp/lock_lcpasswd')) {
1.12    ! harris41  104:     print "Error. Too many other simultaneous password change requests being ".
        !           105: 	"made.\n" unless $noprint;
1.5       harris41  106:     exit 4;
                    107: }
                    108: 
1.12    ! harris41  109: # ------- Error-check input, need 3 values (user name, password 1, password 2).
1.4       harris41  110: my @input;
1.12    ! harris41  111: @input=<>;
        !           112: if (@input!=3) {
        !           113:     print("Error. Three lines need to be entered into standard input.\n")
        !           114: 	unless $noprint;
1.6       harris41  115:     unlink('/tmp/lock_lcpasswd');
1.12    ! harris41  116:     exit 3;
1.1       harris41  117: }
1.12    ! harris41  118: map {chomp} @input;
1.1       harris41  119: 
1.12    ! harris41  120: my ($username,$password1,$password2)=@input;
1.8       harris41  121: $username=~/^(\w+)$/;
                    122: my $safeusername=$1;
1.12    ! harris41  123: if (($username ne $safeusername) or ($safeusername!~/^[A-Za-z]/)) {
1.8       harris41  124:     print "Error. The user name specified has invalid characters.\n";
                    125:     unlink('/tmp/lock_lcpasswd');
                    126:     exit 9;
                    127: }
1.9       harris41  128: my $pbad=0;
1.12    ! harris41  129: map {if (($_<32)&&($_>126)){$pbad=1;}} (split(//,$password1));
        !           130: map {if (($_<32)&&($_>126)){$pbad=1;}} (split(//,$password2));
1.9       harris41  131: if ($pbad) {
                    132:     print "Error. A password entry had an invalid character.\n";
                    133:     unlink('/tmp/lock_lcpasswd');
                    134:     exit 10;
                    135: }
1.1       harris41  136: 
1.12    ! harris41  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;
1.1       harris41  142: }
                    143: 
                    144: # Verify existence of user
1.12    ! harris41  145: unless(getpwnam($safeusername)) {
1.4       harris41  146:     print "Error. User $username does not exist.\n" unless $noprint;
1.6       harris41  147:     unlink('/tmp/lock_lcpasswd');
1.4       harris41  148:     exit 5;
1.1       harris41  149: }
                    150: 
1.12    ! harris41  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;
1.1       harris41  161: }
1.12    ! harris41  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:     }
1.1       harris41  189: 
1.12    ! harris41  190:     open(OUT,"|/usr/bin/smbpasswd -s $safeusername>/dev/null");
        !           191:     print OUT $password2; print OUT "\n";
        !           192:     print OUT $password2; print OUT "\n";
1.2       harris41  193:     close OUT;
1.12    ! harris41  194:     $<=$wwwid; # unfool the program
1.2       harris41  195: }
1.12    ! harris41  196: 
1.1       harris41  197: &disable_root_capability;
1.6       harris41  198: unlink('/tmp/lock_lcpasswd');
1.4       harris41  199: exit 0;
1.1       harris41  200: 
1.12    ! harris41  201: # ---------------------------------------------- have setuid script run as root
1.1       harris41  202: sub enable_root_capability {
1.2       harris41  203:     if ($wwwid==$>) {
1.1       harris41  204: 	($<,$>)=($>,$<);
                    205: 	($(,$))=($),$();
                    206:     }
                    207:     else {
                    208: 	# root capability is already enabled
                    209:     }
1.2       harris41  210:     return $>;
1.1       harris41  211: }
                    212: 
1.12    ! harris41  213: # ----------------------------------------------- have setuid script run as www
1.1       harris41  214: sub disable_root_capability {
1.2       harris41  215:     if ($wwwid==$<) {
1.1       harris41  216: 	($<,$>)=($>,$<);
                    217: 	($(,$))=($),$();
                    218:     }
                    219:     else {
                    220: 	# root capability is already disabled
                    221:     }
                    222: }
                    223: 
1.12    ! harris41  224: # ----------------------- make sure that another lcpasswd process isn't running
1.1       harris41  225: sub try_to_lock {
                    226:     my ($lockfile)=@_;
                    227:     my $currentpid;
                    228:     my $lastpid;
1.5       harris41  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.
1.1       harris41  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>