Annotation of loncom/lcuseradd, revision 1.5

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

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