[LON-CAPA-cvs] cvs: loncom / lond

foxr lon-capa-cvs@mail.lon-capa.org
Wed, 08 Sep 2004 10:19:53 -0000


foxr		Wed Sep  8 06:19:53 2004 EDT

  Modified files:              
    /loncom	lond 
  Log:
  - Add execute_command to safely execute single commands without being
    vulnerable to shell escapes when it's necessary to capture stdout/stderr.
  - Recast the du handler in terms of execute_command.
  - inspect all the Reply and Failure calls to ensure that they send
    \n terminated lines to keep lonc from waiting until timeout before
    completing a transaction. 
  
  
Index: loncom/lond
diff -u loncom/lond:1.250 loncom/lond:1.251
--- loncom/lond:1.250	Tue Sep  7 10:28:30 2004
+++ loncom/lond	Wed Sep  8 06:19:52 2004
@@ -2,7 +2,7 @@
 # The LearningOnline Network
 # lond "LON Daemon" Server (port "LOND" 5663)
 #
-# $Id: lond,v 1.250 2004/09/07 14:28:30 albertel Exp $
+# $Id: lond,v 1.251 2004/09/08 10:19:52 foxr Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -57,7 +57,7 @@
 my $status='';
 my $lastlog='';
 
-my $VERSION='$Revision: 1.250 $'; #' stupid emacs
+my $VERSION='$Revision: 1.251 $'; #' stupid emacs
 my $remoteVERSION;
 my $currenthostid="default";
 my $currentdomainid;
@@ -331,8 +331,43 @@
     
 
 }
-
 #
+#   Safely execute a command (as long as it's not a shel command and doesn
+#   not require/rely on shell escapes.   The function operates by doing a
+#   a pipe based fork and capturing stdout and stderr  from the pipe.
+#
+# Formal Parameters:
+#     $line                    - A line of text to be executed as a command.
+# Returns:
+#     The output from that command.  If the output is multiline the caller
+#     must know how to split up the output.
+#
+#
+sub execute_command {
+    my ($line)    = @_;
+    my @words     = split(/\s/, $line);	# Bust the command up into words.
+    my $output    = "";
+
+    my $pid = open(CHILD, "-|");
+    
+    if($pid) {			# Parent process
+	Debug("In parent process for execute_command");
+	my @data = <CHILD>;	# Read the child's outupt...
+	close CHILD;
+	foreach my $output_line (@data) {
+	    Debug("Adding $output_line");
+	    $output .= $output_line; # Presumably has a \n on it.
+	}
+
+    } else {			# Child process
+	close (STDERR);
+	open  (STDERR, ">&STDOUT");# Combine stderr, and stdout...
+	exec(@words);		# won't return.
+    }
+    return $output;
+}
+
+
 #   GetCertificate: Given a transaction that requires a certificate,
 #   this function will extract the certificate from the transaction
 #   request.  Note that at this point, the only concept of a certificate
@@ -1302,6 +1337,9 @@
 
 sub du_handler {
     my ($cmd, $ududir, $client) = @_;
+    my ($ududir) = split(/:/,$ududir); # Make 'telnet' testing easier.
+    my $userinput = "$cmd:$ududir";
+
     if ($ududir=~/\.\./ || $ududir!~m|^/home/httpd/|) {
 	&Failure($client,"refused\n","$cmd:$ududir");
 	return 1;
@@ -1314,18 +1352,17 @@
     #
     if (-d $ududir) {
 	#  And as Shakespeare would say to make
-	#  assurance double sure, quote the $ududir
-	#  This is in case someone manages to first
-	#  e.g. fabricate a valid directory with a ';'
-	#  in it.  Quoting the dir will help
-	#  keep $ududir completely interpreted as a 
-	#  directory.
-	# 
-	my $duout = `du -ks "$ududir" 2>/dev/null`;
+	#  assurance double sure, 
+	# use execute_command to ensure that the command is not executed in
+	# a shell that can screw us up.
+
+	my $duout = execute_command("du -ks $ududir");
 	$duout=~s/[^\d]//g; #preserve only the numbers
 	&Reply($client,"$duout\n","$cmd:$ududir");
     } else {
-	&Failure($client, "bad_directory:$ududir","$cmd:$ududir"); 
+
+	&Failure($client, "bad_directory:$ududir\n","$cmd:$ududir"); 
+
     }
     return 1;
 }
@@ -1730,7 +1767,7 @@
 	    my $result=&make_passwd_file($uname, $umode,$npass,$passfilename);
 	    &Reply($client, $result, $userinput);
 	} else {	       
-	    &Failure($client, "non_authorized", $userinput); # Fail the user now.
+	    &Failure($client, "non_authorized\n", $userinput); # Fail the user now.
 	}
     }
     return 1;
@@ -2081,14 +2118,14 @@
     my ($fname, $session) = split(/:/, $tail);
     
     chomp($session);
-    my $reply='non_auth';
+    my $reply="non_auth\n";
     if (open(ENVIN,$perlvar{'lonIDsDir'}.'/'.
 	     $session.'.id')) {
 	while (my $line=<ENVIN>) {
-	    if ($line=~ m|userfile\.\Q$fname\E\=|) { $reply='ok'; }
+	    if ($line=~ m|userfile\.\Q$fname\E\=|) { $reply="ok\n"; }
 	}
 	close(ENVIN);
-	&Reply($client, $reply);
+	&Reply($client, $reply, "$cmd:$tail");
     } else {
 	&Failure($client, "invalid_token\n", "$cmd:$tail");
     }
@@ -3799,7 +3836,7 @@
 	$userinput = decipher($userinput);
 	$wasenc=1;
 	if(!$userinput) {	# Cipher not defined.
-	    &Failure($client, "error: Encrypted data without negotated key");
+	    &Failure($client, "error: Encrypted data without negotated key\n");
 	    return 0;
 	}
     }