Annotation of loncom/lcpasswd, revision 1.15

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

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