--- loncom/Attic/lcuseradd	2002/09/19 02:02:59	1.23
+++ loncom/Attic/lcuseradd	2004/09/02 19:04:00	1.30
@@ -5,20 +5,8 @@
 # lcuseradd - LON-CAPA setuid script to coordinate all actions
 #             with adding a user with filesystem privileges (e.g. author)
 #
-# 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.
 #
-# $Id: lcuseradd,v 1.23 2002/09/19 02:02:59 foxr Exp $
+# $Id: lcuseradd,v 1.30 2004/09/02 19:04:00 albertel Exp $
 ###
 
 ###############################################################################
@@ -54,11 +42,20 @@ use strict;
 # www becomes a member of this user group.
 
 # -------------- Invoking script (standard input versus command-line arguments)
+#                Otherwise sensitive information will be available to ps-ers for
+#                a small but exploitable time window.
 #
 # Standard input (STDIN) usage
 # First line is USERNAME
 # Second line is PASSWORD
 # Third line is PASSWORD
+# Fouth line is the name of a file to which an error code will be written.
+#            If the fourth line is omitted, no error file will be written.
+#            In either case, the program Exits with the code as its Exit status.
+#            The error file will just be a single line containing an
+#            error code.
+#            
+#  
 #
 # Command-line arguments [USERNAME] [PASSWORD] [PASSWORD]
 # Yes, but be very careful here (don't pass shell commands)
@@ -82,9 +79,9 @@ use strict;
 # ---------------------------------- Example usage inside another piece of code
 # Usage within code
 #
-# $exitcode=
+# $Exitcode=
 #      system("/home/httpd/perl/lcuseradd","NAME","PASSWORD1","PASSWORD2")/256;
-# print "uh-oh" if $exitcode;
+# print "uh-oh" if $Exitcode;
 
 # ---------------------------------------------------- Description of functions
 # enable_root_capability() : have setuid script run as root
@@ -92,7 +89,7 @@ use strict;
 # try_to_lock() : make sure that another lcpasswd process isn't running
 
 # ------------------------------------------------------------------ Exit codes
-# These are the 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, ".
@@ -110,6 +107,7 @@ use strict;
 # (12,"Error. Something went wrong with the addition of user ".
 #     "\"$safeusername\"."),
 # (13,"Error. Password mismatch."),
+# (14, "Error filename is invalid")
 
 # ------------------------------------------------------------- Initializations
 # Security
@@ -120,6 +118,10 @@ delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}
 # Do not print error messages.
 my $noprint=1;
 
+#  Error file:
+
+my $error_file;			# This is either the error file name or undef.
+
 print "In lcuseradd\n" unless $noprint;
 
 # ----------------------------- Make sure this process is running from user=www
@@ -128,7 +130,7 @@ my $wwwid=getpwnam('www');
 if ($wwwid!=$>) {
     print("User ID mismatch.  This program must be run as user 'www'\n")
 	unless $noprint;
-    exit 1;
+    &Exit(1);
 }
 
 # ----------------------------------- Start running script with www permissions
@@ -138,32 +140,30 @@ if ($wwwid!=$>) {
 unless (&try_to_lock("/tmp/lock_lcpasswd")) {
     print "Error. Too many other simultaneous password change requests being ".
 	"made.\n" unless $noprint;
-    exit 4;
+    &Exit(4);
 }
 
 # ------- Error-check input, need 3 values (user name, password 1, password 2).
 my @input;
-if (@ARGV==3) {
+if (@ARGV>=3) {
     @input=@ARGV;
-}
-elsif (@ARGV) {
-    print("Error. This program needs 3 command-line arguments (username, ".
-	  "password 1, password 2).\n") unless $noprint;
+} elsif (@ARGV) {
+    print("Error. This program needs at least 3 command-line arguments (username, ".
+	  "password 1, password 2 [errorfile]).\n") unless $noprint;
     unlink('/tmp/lock_lcpasswd');
-    exit 2;
-}
-else {
+    &Exit(2);
+} else {
     @input=<>;
-    if (@input!=3) {
-	print("Error. Three lines should be entered into standard input.\n")
+    if (@input < 3) {
+	print("Error. At least three lines should be entered into standard input.\n")
 	    unless $noprint;
 	unlink('/tmp/lock_lcpasswd');
-	exit 3;
+	&Exit(3);
     }
     foreach (@input) {chomp;}
 }
 
-my ($username,$password1,$password2)=@input;
+my ($username,$password1,$password2, $error_file)=@input;
 print "Username = ".$username."\n" unless $noprint;
 $username=~/^(\w+)$/;
 print "Username after substitution - ".$username unless $noprint;
@@ -174,22 +174,51 @@ if (($username ne $safeusername) or ($sa
     print "Error. The user name specified $username $safeusername  has invalid characters.\n"
 	unless $noprint;
     unlink('/tmp/lock_lcpasswd');
-    exit 9;
+    &Exit(9);
 }
 my $pbad=0;
 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";
+    print "Error. A password entry had an invalid character.\n" unless $noprint;
     unlink('/tmp/lock_lcpasswd');
-    exit 10;
+    &Exit(10);
+}
+
+#
+#   Safe the filename.  For our case, it must only have alpha, numeric, period
+#   and path sparators..
+#
+
+print "Error file is $error_file \n" unless $noprint;
+
+if($error_file) {
+    if($error_file =~ /^([(\w)(\d)\.\/]+)$/) {
+	print "Error file matched pattern $error_file : $1\n" unless $noprint;
+	my $safe_error_file = $1;	# Untainted I think.
+	print "Error file after transform $safe_error_file\n"
+	    unless $noprint;
+	if($error_file == $safe_error_file) {
+	    $error_file = $safe_error_file; # untainted error_file.
+	} else {
+	    $error_file ="";
+	    print "Invalid error filename\n" unless $noprint;
+	    Exit(14);
+	}
+
+    } else {
+	$error_file="";
+	print "Invalid error filename\n" unless $noprint;
+	Exit(14);
+    }
 }
 
+
 # -- 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 11;
+    &Exit(11);
 }
 
 # -- Only add user if the two password arguments match.
@@ -197,7 +226,7 @@ if (-e "/home/$safeusername") {
 if ($password1 ne $password2) {
     print "Error. Password mismatch.\n" unless $noprint;
     unlink('/tmp/lock_lcpasswd');
-    exit 13;
+    &Exit(13);
 }
 print "enabling root\n" unless $noprint;
 # ---------------------------------- Start running script with root permissions
@@ -213,11 +242,14 @@ if ($status) {
 	  "\"$safeusername\".\n" unless $noprint;
     print "Final status of useradd = $status";
     unlink('/tmp/lock_lcpasswd');
-    exit 12;
+    &Exit(12);
 }
 print "Done adding user\n" unless $noprint;
 # Make www a member of that user group.
-my $groups=`/usr/bin/groups www` or exit(6);
+my $groups=`/usr/bin/groups www` or &Exit(6);
+# untaint
+my ($safegroups)=($groups=~/:\s*([\s\w]+)/);
+$groups=$safegroups;
 chomp $groups; $groups=~s/^\S+\s+\:\s+//;
 my @grouplist=split(/\s+/,$groups);
 my @ugrouplist=grep {!/www|$safeusername/} @grouplist;
@@ -227,7 +259,7 @@ if (system('/usr/sbin/usermod','-G',$gl,
     print "Error. Could not make www a member of the group ".
 	  "\"$safeusername\".\n" unless $noprint;
     unlink('/tmp/lock_lcpasswd');
-    exit 6;
+    &Exit(6);
 }
 
 # ---------------------------------------------------------------- Set password
@@ -246,8 +278,8 @@ print OUT $password1;
 print OUT "\n";
 close OUT;
 if ($?) {
-    print "abnormal exit from close lcpasswd\n" unless $noprint;
-    exit 8;
+    print "abnormal Exit from close lcpasswd\n" unless $noprint;
+    &Exit(8);
 }
 ($>,$<)=($wwwid,0);
 &enable_root_capability;
@@ -269,37 +301,37 @@ print OUT<<END;
 <title>$safeusername</title>
 </head>
 <body>
-<h1>$safeusername</h1>
-<p>
-Learning Online Network
-</p>
-<p>
-This area provides for:
-</p>
-<ul>
-<li>resource construction</li>
-<li>resource publication</li>
-<li>record-keeping</li>
-</ul>
+<h1>Construction Space</h1>
+<h3>$safeusername</h3>
 </body>
 </html>
 END
 close OUT;
 
-print "lcuseradd exiting\n" unless $noprint;
+print "lcuseradd ownership\n" unless $noprint;
 system('/bin/chown','-R',"$safeusername:$safeusername","/home/$safeusername");
-
+# ---------------------------------------------------- Gracefull Apache Restart
+if (-e '/var/run/httpd.pid') {
+    print "lcuseradd Apache restart\n" unless $noprint;
+    open(PID,'/var/run/httpd.pid');
+    my $pid=<PID>;
+    close(PID);
+    my ($safepid)=($pid=~s/(\D+)//g);
+    if ($pid) {
+	system('kill','-USR1',"$safepid");
+    }
+}
 # -------------------------------------------------------- Exit script
+print "lcuseradd Exiting\n" unless $noprint;
 &disable_root_capability;
-exit 0;
+&Exit(0);
 
 # ---------------------------------------------- Have setuid script run as root
 sub enable_root_capability {
     if ($wwwid==$>) {
 	($<,$>)=($>,0);
 	($(,$))=($),0);
-    }
-    else {
+    } else {
 	# root capability is already enabled
     }
     return $>;
@@ -310,8 +342,7 @@ sub disable_root_capability {
     if ($wwwid==$<) {
 	($<,$>)=($>,$<);
 	($(,$))=($),$();
-    }
-    else {
+    } else {
 	# root capability is already disabled
     }
 }
@@ -340,8 +371,7 @@ sub try_to_lock {
 	    }
 	    sleep 3;
 	    $lastpid=$currentpid;
-	}
-	else {
+	} else {
 	    last;
 	}
 	if ($_==10) {
@@ -353,6 +383,22 @@ sub try_to_lock {
     close LOCK;
     return 1;
 }
+#-------------------------- Exit...
+#
+#   Write the file if the error_file is defined.  Regardless
+#   Exit with the status code.
+#
+sub Exit {
+    my ($code) = @_;		# Status code.
+
+    print "Exiting with status $code error file is $error_file\n" unless $noprint;
+    if($error_file) {
+	open(FH, ">$error_file");
+	print FH  "$code\n";
+	close(FH);
+    }
+    exit $code;
+}
 
 =head1 NAME