--- loncom/Attic/lcuseradd 2000/10/30 02:31:45 1.8
+++ loncom/Attic/lcuseradd 2002/05/03 03:43:54 1.21
@@ -1,25 +1,69 @@
#!/usr/bin/perl
+
+# The Learning Online Network with CAPA
+#
+# lcuseradd - LON-CAPA setuid script to coordinate all actions
+# with adding a user with filesystem privileges (e.g. author)
#
-# lcuseradd
+# YEAR=2000
+# 10/27,10/29,10/30 Scott Harrison
+# YEAR=2001
+# 10/21,11/13,11/15 Scott Harrison
+# YEAR=2002
+# May 19, 2002 Ron Fox
+# - Removed creation of the pulic_html directory. This directory
+# can now be added in two ways:
+# o The user can add it themselves if they want some local web
+# space which may or may not contain construction items.
+# o LonCapa will add it if/when the user is granted an Author
+# role.
#
-# Scott Harrison
-# October 27, 2000
+# $Id: lcuseradd,v 1.21 2002/05/03 03:43:54 foxr Exp $
+###
+
+###############################################################################
+## ##
+## ORGANIZATION OF THIS PERL SCRIPT ##
+## ##
+## 1. Description of script ##
+## 2. Invoking script (standard input) ##
+## 3. Example usage inside another piece of code ##
+## 4. Description of functions ##
+## 5. Exit codes ##
+## 6. Initializations ##
+## 7. Make sure this process is running from user=www ##
+## 8. Start running script with www permissions ##
+## 9. Handle case of another lcpasswd process (locking) ##
+## 10. Error-check input, need 3 values (user name, password 1, password 2) ##
+## 11. Start running script with root permissions ##
+## 12. Add user and make www a member of the user-specific group ##
+## 13. Set password ##
+## 14. Make final modifications to the user directory ##
+## 15. Exit script (unlock) ##
+## ##
+###############################################################################
use strict;
+# ------------------------------------------------------- Description of script
+#
# This script is a setuid script that should
-# be run by user 'www'. It creates a /home/USERNAME directory
-# as well as a /home/USERNAME/public_html directory.
-# It adds user entries to
-# /etc/passwd and /etc/groups.
+# be run by user 'www'. It creates a /home/USERNAME directory.
+# It adds a user to the unix system.
# Passwords are set with lcpasswd.
# www becomes a member of this user group.
-# Standard input usage
+# -------------- Invoking script (standard input versus command-line arguments)
+#
+# Standard input (STDIN) usage
# First line is USERNAME
# Second line is PASSWORD
# Third line is PASSWORD
-
+#
+# Command-line arguments [USERNAME] [PASSWORD] [PASSWORD]
+# Yes, but be very careful here (don't pass shell commands)
+# and this is only supported to allow perl-system calls.
+#
# Valid passwords must consist of the
# ascii characters within the inclusive
# range of 0x20 (32) to 0x7E (126).
@@ -27,236 +71,214 @@ use strict;
# SPACE and
# !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO
# PQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
-
+#
# Valid user names must consist of ascii
# characters that are alphabetical characters
# (A-Z,a-z), numeric (0-9), or the underscore
# mark (_). (Essentially, the perl regex \w).
+# User names must begin with an alphabetical character
+# (A-Z,a-z).
-# Command-line arguments [USERNAME] [PASSWORD] [PASSWORD]
-# Yes, but be very careful here (don't pass shell commands)
-# and this is only supported to allow perl-system calls.
-
+# ---------------------------------- Example usage inside another piece of code
# Usage within code
#
-# $exitcode=system("/home/httpd/perl/lcuseradd","NAME","PASSWORD1","PASSWORD2")/256;
+# $exitcode=
+# system("/home/httpd/perl/lcuseradd","NAME","PASSWORD1","PASSWORD2")/256;
# print "uh-oh" if $exitcode;
+# ---------------------------------------------------- Description of functions
+# enable_root_capability() : have setuid script run as root
+# disable_root_capability() : have setuid script run as www
+# try_to_lock() : make sure that another lcpasswd process isn't running
+
+# ------------------------------------------------------------------ Exit codes
# These are the exit codes.
+# ( (0,"ok"),
+# (1,"User ID mismatch. This program must be run as user 'www'"),
+# (2,"Error. This program needs 3 command-line arguments (username, ".
+# "password 1, password 2)."),
+# (3,"Error. Three lines should be entered into standard input."),
+# (4,"Error. Too many other simultaneous password change requests being ".
+# "made."),
+# (5,"Error. User $username does not exist."),
+# (6,"Error. Could not make www a member of the group \"$safeusername\"."),
+# (7,"Error. Root was not successfully enabled.),
+# (8,"Error. Cannot set password."),
+# (9,"Error. The user name specified has invalid characters."),
+# (10,"Error. A password entry had an invalid character."),
+# (11,"Error. User already exists.),
+# (12,"Error. Something went wrong with the addition of user ".
+# "\"$safeusername\"."),
+# (13,"Error. Password mismatch."),
+# ------------------------------------------------------------- Initializations
# Security
-$ENV{'PATH'}=""; # Nullify path information.
-$ENV{'BASH_ENV'}=""; # Nullify shell environment information.
+$ENV{'PATH'}='/bin/:/usr/bin:/usr/local/sbin:/home/httpd/perl'; # Nullify path
+ # information
+delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; # nullify potential taints
-# Do not print error messages if there are command-line arguments
-my $noprint=0;
-if (@ARGV) {
- $noprint=1;
-}
-
-# Read in /etc/passwd, and make sure this process is running from user=www
-open (IN, ";
-close IN;
-my $wwwid;
-for my $l (@lines) {
- chop $l;
- my @F=split(/\:/,$l);
- if ($F[0] eq 'www') {$wwwid=$F[2];}
-}
-if ($wwwid!=$<) {
- print("User ID mismatch. This program must be run as user 'www'\n") unless $noprint;
+# Do not print error messages.
+my $noprint=1;
+
+# ----------------------------- Make sure this process is running from user=www
+my $wwwid=getpwnam('www');
+&disable_root_capability;
+if ($wwwid!=$>) {
+ print("User ID mismatch. This program must be run as user 'www'\n")
+ unless $noprint;
exit 1;
}
+
+# ----------------------------------- Start running script with www permissions
&disable_root_capability;
-# Handle case of another lcpasswd process
+# --------------------------- Handle case of another lcpasswd process (locking)
unless (&try_to_lock("/tmp/lock_lcpasswd")) {
- print "Error. Too many other simultaneous password change requests being made.\n" unless $noprint;
+ print "Error. Too many other simultaneous password change requests being ".
+ "made.\n" unless $noprint;
exit 4;
}
-# Gather input. Should be 3 values (user name, password 1, password 2).
+# ------- Error-check input, need 3 values (user name, password 1, password 2).
my @input;
if (@ARGV==3) {
@input=@ARGV;
}
elsif (@ARGV) {
- print("Error. This program needs 3 command-line arguments (username, password 1, password 2).\n") unless $noprint;
+ print("Error. This program needs 3 command-line arguments (username, ".
+ "password 1, password 2).\n") unless $noprint;
unlink('/tmp/lock_lcpasswd');
exit 2;
}
else {
@input=<>;
if (@input!=3) {
- print("Error. Three lines should be entered into standard input.\n") unless $noprint;
+ print("Error. Three lines should be entered into standard input.\n")
+ unless $noprint;
unlink('/tmp/lock_lcpasswd');
exit 3;
}
- map {chop} @input;
+ foreach (@input) {chomp;}
}
my ($username,$password1,$password2)=@input;
$username=~/^(\w+)$/;
my $safeusername=$1;
-if ($username ne $safeusername) {
- print "Error. The user name specified has invalid characters.\n";
+if (($username ne $safeusername) or ($safeusername!~/^[A-Za-z]/)) {
+ print "Error. The user name specified has invalid characters.\n"
+ unless $noprint;
unlink('/tmp/lock_lcpasswd');
exit 9;
}
my $pbad=0;
-map {if (($_<32)&&($_>126)){$pbad=1;}} (split(//,$oldpwd));
-map {if (($_<32)&&($_>126)){$pbad=1;}} (split(//,$newpwd));
+foreach (split(//,$password1)) {if ((ord($_)<32)||(ord($_)>126)){$pbad=1;}}
+foreach (split(//,$password2)) {if ((ord($_)<32)||(ord($_)>126)){$pbad=1;}}
if ($pbad) {
print "Error. A password entry had an invalid character.\n";
unlink('/tmp/lock_lcpasswd');
exit 10;
}
-# Only add user if we can create a brand new home directory (/home/username).
+# -- Only add user if we can create a brand new home directory (/home/username)
if (-e "/home/$safeusername") {
print "Error. User already exists.\n" unless $noprint;
unlink('/tmp/lock_lcpasswd');
- exit 8;
+ exit 11;
}
-# Only add user if the two password arguments match.
+# -- Only add user if the two password arguments match.
if ($password1 ne $password2) {
print "Error. Password mismatch.\n" unless $noprint;
unlink('/tmp/lock_lcpasswd');
- exit 7;
+ exit 13;
}
+# ---------------------------------- Start running script with root permissions
&enable_root_capability;
-# Add user entry to /etc/passwd and /etc/groups in such
-# a way that www is a member of the user-specific group
-
+# ------------------- Add user and make www a member of the user-specific group
+# -- Add user
if (system('/usr/sbin/useradd','-c','LON-CAPA user',$safeusername)) {
- print "Error. Something went wrong with the addition of user \"$safeusername\".\n" unless $noprint;
+ print "Error. Something went wrong with the addition of user ".
+ "\"$safeusername\".\n" unless $noprint;
unlink('/tmp/lock_lcpasswd');
- exit 5;
+ exit 12;
}
# Make www a member of that user group.
-if (system('/usr/sbin/usermod','-G',$safeusername,'www')) {
- print "Error. Could not make www a member of the group \"$safeusername\".\n" unless $noprint;
+my $groups=`/usr/bin/groups www` or exit(6);
+chomp $groups; $groups=~s/^\S+\s+\:\s+//;
+my @grouplist=split(/\s+/,$groups);
+my @ugrouplist=grep {!/www|$safeusername/} @grouplist;
+my $gl=join(',',(@ugrouplist,$safeusername));
+if (system('/usr/sbin/usermod','-G',$gl,'www')) {
+ print "Error. Could not make www a member of the group ".
+ "\"$safeusername\".\n" unless $noprint;
unlink('/tmp/lock_lcpasswd');
exit 6;
}
-# Set password with lcpasswd-style algorithm (which creates smbpasswd entry).
-# I cannot place a direct shell call to lcpasswd since I have to allow
-# for crazy characters in the password, and the setuid environment of perl
-# requires me to make everything safe.
-
-my $pbad=0;
-map {if (($_<32)&&($_>126)){$pbad=1;}} (split(//,$oldpwd));
-map {if (($_<32)&&($_>126)){$pbad=1;}} (split(//,$newpwd));
-if ($pbad) {
- print "Error. A password entry had an invalid character.\n";
- unlink('/tmp/lock_lcpasswd');
- exit 10;
-}
+# ---------------------------------------------------------------- Set password
+# Set password with lcpasswd (which creates smbpasswd entry).
-# Grab the line corresponding to username
-my ($userid,$useroldcryptpwd);
-my @F; my @U;
-for my $l (@lines) {
- @F=split(/\:/,$l);
- if ($F[0] eq $username) {($userid,$useroldcryptpwd)=($F[2],$F[1]); @U=@F;}
-}
-
-# Verify existence of user
-if (!defined($userid)) {
- print "Error. User $username does not exist.\n" unless $noprint;
- unlink('/tmp/lock_lcpasswd');
- exit 5;
-}
-
-# Verify password entry
-if (crypt($oldpwd,$useroldcryptpwd) ne $useroldcryptpwd) {
- print "Error. Invalid entry of current password.\n" unless $noprint;
- unlink('/tmp/lock_lcpasswd');
- exit 6;
+unlink('/tmp/lock_lcpasswd');
+&disable_root_capability;
+($>,$<)=($wwwid,$wwwid);
+open OUT,"|/home/httpd/perl/lcpasswd";
+print OUT $safeusername;
+print OUT "\n";
+print OUT $password1;
+print OUT "\n";
+print OUT $password1;
+print OUT "\n";
+close OUT;
+if ($?) {
+ exit 8;
}
+($>,$<)=($wwwid,0);
+&enable_root_capability;
-# Construct new password entry (random salt)
-my $newcryptpwd=crypt($newpwd,(join '', ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[rand 64, rand 64]));
-$U[1]=$newcryptpwd;
-my $userline=join(':',@U);
-my $rootid=&enable_root_capability;
-if ($rootid!=0) {
- print "Error. Root was not successfully enabled.\n" unless $noprint;
- unlink('/tmp/lock_lcpasswd');
- exit 7;
-}
-open PASSWORDFILE, '>/etc/passwd' or (print("Error. Cannot open /etc/passwd.\n") && unlink('/tmp/lock_lcpasswd') && exit(8));
-for my $l (@lines) {
- @F=split(/\:/,$l);
- if ($F[0] eq $username) {print PASSWORDFILE "$userline\n";}
- else {print PASSWORDFILE "$l\n";}
-}
-close PASSWORDFILE;
-
-($>,$<)=(0,0); # fool smbpasswd here to think this is not a setuid environment
-unless (-e '/etc/smbpasswd') {
- open (OUT,'>/etc/smbpasswd'); close OUT;
-}
-my $smbexist=0;
-open (IN, ';
-close IN;
-for my $l (@lines) {
- chop $l;
- my @F=split(/\:/,$l);
- if ($F[0] eq $username) {$smbexist=1;}
-}
-unless ($smbexist) {
- open(OUT,'>>/etc/smbpasswd');
- print OUT join(':',($safeusername,$userid,'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX','','/home/'.$safeusername,'/bin/bash')) . "\n";
- close OUT;
-}
-open(OUT,"|/usr/bin/smbpasswd -s $safeusername>/dev/null");
-print OUT $newpwd; print OUT "\n";
-print OUT $newpwd; print OUT "\n";
-close OUT;
-$<=$wwwid; # unfool the program
+# -- Don't add public_html... that can be added either by the user
+# or by lchtmldir when the user is granted an authorship role.
-# Make final modifications to the user directory.
-# Add a public_html file with a stand-in index.html file.
+# ------------------------------ Make final modifications to the user directory
+# -- Add a public_html file with a stand-in index.html file
-mkdir "/home/$safeusername/public_html",0750;
-# chmod -R
-open OUT,">/home/$safeusername/public_html";
+ system('/bin/chmod','-R','0660',"/home/$safeusername");
+system('/bin/chmod','0710',"/home/$safeusername");
+mkdir "/home/$safeusername/public_html",0755;
+system('/bin/chmod','02770',"/home/$safeusername/public_html");
+open OUT,">/home/$safeusername/public_html/index.html";
print OUT<
+
+
Learning Online Network
-
+
This area provides for:
-$safeusername
-$safeusername
+
-
-
-
+