--- loncom/lcpasswd	2000/10/28 18:25:47	1.5
+++ loncom/lcpasswd	2000/10/30 03:30:26	1.11
@@ -3,7 +3,9 @@
 # lcpasswd
 #
 # Scott Harrison
-# October 27, 2000
+# SH: October 27, 2000
+# SH: October 28, 2000
+# SH: October 29, 2000
 
 use strict;
 
@@ -22,6 +24,19 @@ use strict;
 # Second line is CURRENT PASSWORD
 # Third line is NEW PASSWORD
 
+# Valid passwords must consist of the
+# ascii characters within the inclusive
+# range of 0x20 (32) to 0x7E (126).
+# These characters are:
+# 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).
+
 # Command-line arguments
 # Yes, but be very careful here (don't pass shell commands)
 # and this is only supported to allow perl-system calls.
@@ -34,17 +49,20 @@ use strict;
 
 # These are the exit codes.
 # ( (0,"ok"),
-#   (1,"User ID mismatch.  This program must be run as user 'www'),
-#   (2,"Error. This program does not accept command-line arguments."),
-#   (3,"Error. Three lines need to be entered into standard input.\n"),
-#   (4,"Error. Too many other simultaneous password change requests being made.\n"),
-#   (5,"Error. User $username does not exist.\n"),
-#   (6,"Error. Invalid entry of current password.\n"),
-#   (7,"Error.  Root was not successfully enabled.\n") )
+#   (1,"User ID mismatch.  This program must be run as user 'www'"),
+#   (2,"Error. This program needs 3 command-line arguments (username, old password, new password)."),
+#   (3,"Error. Three lines need to 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. Invalid entry of current password."),
+#   (7,"Error. Root was not successfully enabled."),
+#   (8,"Error. Cannot open /etc/passwd."),
+#   (9,"Error. The user name specified has invalid characters."),
+#   (10,"Error. A password entry had an invalid character.") )
 
 # Security
-$ENV{'PATH'}="/bin:/usr/bin"; # Nullify path information except for what smbpasswd needs
-$ENV{'BASH_ENV'}=""; # Nullify shell environment information.
+$ENV{'PATH'}='/bin:/usr/bin'; # Nullify path information except for what smbpasswd needs
+$ENV{'BASH_ENV'}=''; # Nullify shell environment information.
 
 # Do not print error messages if there are command-line arguments
 my $noprint=0;
@@ -53,7 +71,7 @@ if (@ARGV) {
 }
 
 # Read in /etc/passwd, and make sure this process is running from user=www
-open (IN, "</etc/passwd");
+open (IN, '</etc/passwd');
 my @lines=<IN>;
 close IN;
 my $wwwid;
@@ -64,13 +82,12 @@ for my $l (@lines) {
 }
 if ($wwwid!=$<) {
     print("User ID mismatch.  This program must be run as user 'www'\n") unless $noprint;
-    unlink("/tmp/lock_lcpasswd");
     exit 1;
 }
 &disable_root_capability;
 
 # Handle case of another lcpasswd process
-unless (&try_to_lock("/tmp/lock_lcpasswd")) {
+unless (&try_to_lock('/tmp/lock_lcpasswd')) {
     print "Error. Too many other simultaneous password change requests being made.\n" unless $noprint;
     exit 4;
 }
@@ -82,20 +99,35 @@ if (@ARGV==3) {
 }
 elsif (@ARGV) {
     print("Error. This program needs 3 command-line arguments (username, old password, new password).\n") unless $noprint;
-    unlink("/tmp/lock_lcpasswd");
+    unlink('/tmp/lock_lcpasswd');
     exit 2;
 }
 else {
     @input=<>;
     if (@input!=3) {
 	print("Error. Three lines need to be entered into standard input.\n") unless $noprint;
-	unlink("/tmp/lock_lcpasswd");
+	unlink('/tmp/lock_lcpasswd');
 	exit 3;
     }
     map {chop} @input;
 }
 
 my ($username,$oldpwd,$newpwd)=@input;
+$username=~/^(\w+)$/;
+my $safeusername=$1;
+if ($username ne $safeusername) {
+    print "Error. The user name specified has invalid characters.\n";
+    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));
+if ($pbad) {
+    print "Error. A password entry had an invalid character.\n";
+    unlink('/tmp/lock_lcpasswd');
+    exit 10;
+}
 
 # Grab the line corresponding to username
 my ($userid,$useroldcryptpwd);
@@ -108,42 +140,41 @@ for my $l (@lines) {
 # Verify existence of user
 if (!defined($userid)) {
     print "Error. User $username does not exist.\n" unless $noprint;
-    unlink("/tmp/lock_lcpasswd");
+    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");
+    unlink('/tmp/lock_lcpasswd');
     exit 6;
 }
 
 # 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 $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");
+    unlink('/tmp/lock_lcpasswd');
     exit 7;
 }
-open PASSWORDFILE, ">/etc/passwd" or die("Cannot open /etc/passwd!");
+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;
-$username=~/^(\w+)$/;
-my $safeusername=$1;
+
 ($>,$<)=(0,0); # fool smbpasswd here to think this is not a setuid environment
-unless (-e "/etc/smbpasswd") {
-    open (OUT,">/etc/smbpasswd"); close OUT;
+unless (-e '/etc/smbpasswd') {
+    open (OUT,'>/etc/smbpasswd'); close OUT;
 }
 my $smbexist=0;
-open (IN, "</etc/smbpasswd");
+open (IN, '</etc/smbpasswd');
 my @lines=<IN>;
 close IN;
 for my $l (@lines) {
@@ -152,8 +183,8 @@ for my $l (@lines) {
     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";
+    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");
@@ -162,7 +193,7 @@ print OUT $newpwd; print OUT "\n";
 close OUT;
 $<=$wwwid; # unfool the program
 &disable_root_capability;
-unlink("/tmp/lock_lcpasswd");
+unlink('/tmp/lock_lcpasswd');
 exit 0;
 
 # ----------------------------------------------------------- have setuid script run as root