Annotation of loncom/lcpasswd, revision 1.18

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

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