Annotation of loncom/lcuseradd, revision 1.7

1.1       harris41    1: #!/usr/bin/perl
                      2: #
                      3: # lcuseradd
                      4: #
                      5: # Scott Harrison
                      6: # October 27, 2000
                      7: 
                      8: use strict;
                      9: 
                     10: # This script is a setuid script that should
                     11: # be run by user 'www'.  It creates a /home/USERNAME directory
                     12: # as well as a /home/USERNAME/public_html directory.
                     13: # It adds user entries to
                     14: # /etc/passwd and /etc/groups.
1.2       harris41   15: # Passwords are set with lcpasswd.
                     16: # www becomes a member of this user group.
1.1       harris41   17: 
                     18: # Standard input usage
                     19: # First line is USERNAME
                     20: # Second line is PASSWORD
1.3       harris41   21: # Third line is PASSWORD
1.1       harris41   22: 
1.7     ! harris41   23: # Valid passwords must consist of the
        !            24: # ascii characters within the inclusive
        !            25: # range of 0x20 (32) to 0x7E (126).
        !            26: # These characters are:
        !            27: # SPACE and
        !            28: # !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO
        !            29: # PQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
        !            30: 
        !            31: # Valid user names must consist of ascii
        !            32: # characters that are alphabetical characters
        !            33: # (A-Z,a-z), numeric (0-9), or the underscore
        !            34: # mark (_). (Essentially, the perl regex \w).
        !            35: 
1.3       harris41   36: # Command-line arguments [USERNAME] [PASSWORD] [PASSWORD]
1.1       harris41   37: # Yes, but be very careful here (don't pass shell commands)
                     38: # and this is only supported to allow perl-system calls.
                     39: 
1.4       harris41   40: # Usage within code
                     41: #
                     42: # $exitcode=system("/home/httpd/perl/lcuseradd","NAME","PASSWORD1","PASSWORD2")/256;
                     43: # print "uh-oh" if $exitcode;
                     44: 
                     45: # These are the exit codes.
                     46: 
1.1       harris41   47: # Security
                     48: $ENV{'PATH'}=""; # Nullify path information.
                     49: $ENV{'BASH_ENV'}=""; # Nullify shell environment information.
1.2       harris41   50: 
1.4       harris41   51: # Do not print error messages if there are command-line arguments
                     52: my $noprint=0;
                     53: if (@ARGV) {
                     54:     $noprint=1;
                     55: }
                     56: 
                     57: # Read in /etc/passwd, and make sure this process is running from user=www
                     58: open (IN, "</etc/passwd");
                     59: my @lines=<IN>;
                     60: close IN;
                     61: my $wwwid;
                     62: for my $l (@lines) {
                     63:     chop $l;
                     64:     my @F=split(/\:/,$l);
                     65:     if ($F[0] eq 'www') {$wwwid=$F[2];}
                     66: }
                     67: if ($wwwid!=$<) {
                     68:     print("User ID mismatch.  This program must be run as user 'www'\n") unless $noprint;
                     69:     exit 1;
                     70: }
                     71: &disable_root_capability;
                     72: 
                     73: # Handle case of another lcpasswd process
                     74: unless (&try_to_lock("/tmp/lock_lcpasswd")) {
                     75:     print "Error. Too many other simultaneous password change requests being made.\n" unless $noprint;
                     76:     exit 4;
                     77: }
                     78: 
                     79: # Gather input.  Should be 3 values (user name, password 1, password 2).
                     80: my @input;
1.5       harris41   81: if (@ARGV==3) {
1.4       harris41   82:     @input=@ARGV;
                     83: }
                     84: elsif (@ARGV) {
                     85:     print("Error. This program needs 3 command-line arguments (username, password 1, password 2).\n") unless $noprint;
                     86:     unlink('/tmp/lock_lcpasswd');
                     87:     exit 2;
                     88: }
                     89: else {
                     90:     @input=<>;
1.5       harris41   91:     if (@input!=3) {
1.4       harris41   92: 	print("Error. Three lines should be entered into standard input.\n") unless $noprint;
                     93: 	unlink('/tmp/lock_lcpasswd');
                     94: 	exit 3;
                     95:     }
                     96:     map {chop} @input;
                     97: }
                     98: 
                     99: my ($username,$password1,$password2)=@input;
                    100: $username=~/^(\w+)$/;
                    101: my $safeusername=$1;
1.5       harris41  102: 
1.7     ! harris41  103: # Only add user if we can create a brand new home directory (/home/username).
        !           104: if (-e "/home/$safeusername") {
        !           105:     print "Error. User already exists.\n" unless $noprint;
        !           106:     unlink('/tmp/lock_lcpasswd');
        !           107:     exit 8;
        !           108: }
        !           109: 
        !           110: # Only add user if the two password arguments match.
1.5       harris41  111: if ($password1 ne $password2) {
1.6       harris41  112:     print "Error. Password mismatch.\n" unless $noprint;
1.5       harris41  113:     unlink('/tmp/lock_lcpasswd');
                    114:     exit 7;
                    115: }
1.4       harris41  116: 
                    117: &enable_root_capability;
                    118: 
1.3       harris41  119: # Add user entry to /etc/passwd and /etc/groups in such
                    120: # a way that www is a member of the user-specific group
                    121: 
1.5       harris41  122: if (system('/usr/sbin/useradd','-c','LON-CAPA user',$safeusername)) {
1.6       harris41  123:     print "Error.  Something went wrong with the addition of user \"$safeusername\".\n" unless $noprint;
1.4       harris41  124:     unlink('/tmp/lock_lcpasswd');
                    125:     exit 5;
                    126: }
1.7     ! harris41  127: 
        !           128: # Make www a member of that user group.
1.5       harris41  129: if (system('/usr/sbin/usermod','-G',$safeusername,'www')) {
1.6       harris41  130:     print "Error. Could not make www a member of the group \"$safeusername\".\n" unless $noprint;
1.5       harris41  131:     unlink('/tmp/lock_lcpasswd');
                    132:     exit 6;
                    133: }
                    134: 
                    135: # Set password with lcpasswd-style algorithm (which creates smbpasswd entry).
                    136: # I cannot place a direct shell call to lcpasswd since I have to allow
                    137: # for crazy characters in the password, and the setuid environment of perl
                    138: # requires me to make everything safe.
1.7     ! harris41  139: 
1.5       harris41  140: 
1.2       harris41  141: 
1.1       harris41  142: 
1.5       harris41  143: # ----------------------------------------------------------- have setuid script run as root
                    144: sub enable_root_capability {
                    145:     if ($wwwid==$>) {
                    146: 	($<,$>)=($>,$<);
                    147: 	($(,$))=($),$();
                    148:     }
                    149:     else {
                    150: 	# root capability is already enabled
                    151:     }
                    152:     return $>;
                    153: }
                    154: 
                    155: # ----------------------------------------------------------- have setuid script run as www
                    156: sub disable_root_capability {
                    157:     if ($wwwid==$<) {
                    158: 	($<,$>)=($>,$<);
                    159: 	($(,$))=($),$();
                    160:     }
                    161:     else {
                    162: 	# root capability is already disabled
                    163:     }
                    164: }
                    165: 
                    166: # ----------------------------------- make sure that another lcpasswd process isn't running
                    167: sub try_to_lock {
                    168:     my ($lockfile)=@_;
                    169:     my $currentpid;
                    170:     my $lastpid;
                    171:     # Do not manipulate lock file as root
                    172:     if ($>==0) {
                    173: 	return 0;
                    174:     }
                    175:     # Try to generate lock file.
                    176:     # Wait 3 seconds.  If same process id is in
                    177:     # lock file, then assume lock file is stale, and
                    178:     # go ahead.  If process id's fluctuate, try
                    179:     # for a maximum of 10 times.
                    180:     for (0..10) {
                    181: 	if (-e $lockfile) {
                    182: 	    open(LOCK,"<$lockfile");
                    183: 	    $currentpid=<LOCK>;
                    184: 	    close LOCK;
                    185: 	    if ($currentpid==$lastpid) {
                    186: 		last;
                    187: 	    }
                    188: 	    sleep 3;
                    189: 	    $lastpid=$currentpid;
                    190: 	}
                    191: 	else {
                    192: 	    last;
                    193: 	}
                    194: 	if ($_==10) {
                    195: 	    return 0;
                    196: 	}
                    197:     }
                    198:     open(LOCK,">$lockfile");
                    199:     print LOCK $$;
                    200:     close LOCK;
                    201:     return 1;
                    202: }

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