Background
The applet starts by requesting a proxy connection through the web server to localhost port 7 where a Java server is listening.
The applet diagnostic window displays the web server response:
* Connecting to ftp.ece.villanova.edu:80 * Sending proxy request: CONNECT localhost:7 HTTP/1.0 200 Connection Established Proxy-agent: Apache * Sending authentication data for frodoNote that no authentication is provided to the web server, anyone can connect through the server to localhost:7, for example, from fog.misty.com:
fog% telnet ftp.ece.villanova.edu 80 Trying 153.104.63.227... Connected to ftp.ece.villanova.edu. Escape character is '^]'. CONNECT localhost:7 HTTP/1.0 200 Connection Established Proxy-agent: Apache abc 123 ^] telnet> quit Connection closed. fog%Once the proxy connection is established, the web server is transparent and simply forwards data, including authentication data, back and forth between the remote user and the server on localhost port 7.
When it receives a connection, the Java server creates a client thread to communicate with the applet.
In the figure above, the transparent web server proxy between the Applet and Copy/Client threads is not shown. After creating the client thread, the Java server is no longer involved in the communications but is shown to complete the diagram of parent/child process relationships drawn with dashed lines. That is, the Server, running as root, creates the Client thread, also running as root. After verifying authentication data from the applet, the Client thread creates a Copy thread, running as root, to handle copying program output back to the applet. The Client thread also creates a Run process, running as the user, which runs and monitors the user program. When needed for the Save option, the Client thread creates a temporary Save process which is not shown in the figure.
Authentication
The first question regarding authentication is: where should it occur? One answer is to have the web server perform authentication. But this would require the applet to send the UserID and Password, and an applet does not have access to that data even if cached by the web browser. So the applet would have to prompt the user to enter their UserID and Password, and that would happen over and over again, any time the IO option is selected or the applet restarted. That would be an excessive annoying burden for the user, considering that they have already authenticated in order to get the applet web page in the first place.
Furthermore, if the web server performs authentication, it would not pass the authentication data through to the localhost:7 connection, so the Java server would have no way of securely knowing the UserID. The UserID could be a parameter passed by the applet to the server, but that could easily be faked and allow one user to access another users files through the server.
So it seems that authentication must be performed by the Java server, not the web proxy, and it must be done without have the user reenter their UserID and Password. This is implemented in VECR/IO using a secure authenticated ticket as follows.
When the IO option is selected, the user has already been authenticated to the web server, and the following shell code is run:
IO) # # generate IO applet HTML # case "$file" in # add debug here to run gdb ZZ *.c) prog=./`basename "$file" .c`;; *.java) prog="$JAVA"; b=`basename "$file" .java`; args="$b $args";; *.sh) prog="/bin/sh"; args="$file $args";; esac echo "Content-type: text/html\n <html><head><title>VECR/IO</title></head><body bgcolor=white text=black> <b>${user} - ${COURSE} - <a href=\"/prog/${COURSE}?edit+${sub}+${file}\">${sub}/${file}</a> - <a href=\"/prog/${COURSE}?edit+${sub}\">${sub}</a> - <a href=\"/prog/${COURSE}\">Home</a></b> - Running: $prog $args <br><applet codebase=\"/vecr/IO\" code=\"IOapplet.class\" width=700 height=600>" cd "/vecr/IO/bin" || exit 1 $JAVA IOticket "$user" "$uid" echo "<param name=\"dir\" value=\"$dir/$sub\"> <param name=\"prog\" value=\"${prog}\"> <param name=\"args\" value=\"${args}\"> Your browser does not support Java, or Java is not enabled. Sorry! </applet></body></html>" exit 0 ;;Note the use of IOticket.java, which takes the UserID and numerical uid and creates a ticket:
SecretKeySpec KS = new SecretKeySpec( secret, "HmacSHA256"); Mac mac = Mac.getInstance( "HmacSHA256"); mac.init( KS); String time = Long.toString(System.currentTimeMillis()); mac.update( time.getBytes()); mac.update( user.getBytes()); mac.update( uid.getBytes()); String ticket = hex( mac.doFinal()); System.out.println( "<param name=\"user\" value=\"" + user + "\">"); System.out.println( "<param name=\"uid\" value=\"" + uid + "\">"); System.out.println( "<param name=\"time\" value=\"" + time + "\">"); System.out.println( "<param name=\"ticket\" value=\"" + ticket + "\">");Note that the ticket includes the current time; this is checked on the server so that a ticket only remains valid for 10 minutes:
// check authentication ticket time limit // long mytime = System.currentTimeMillis(); long yourtime = Long.parseLong( time); if( mytime - yourtime > 600000) // 10 minute limit { out.write( "Authentication data is too old\n".getBytes()); out.flush(); throw new SecurityException( "Stale authentication data for user " + user + " from " + name); }The authentication data, as well as the user directory, program to run, and command-line arguments, are obtained by the applet from parameters passed in the generated IO web page, for example:
<applet codebase="/vecr/IO" code="IOapplet.class" width=700 height=600> <param name="user" value="frodo"> <param name="uid" value="3001"> <param name="time" value="1108241035769"> <param name="ticket" value="a43c774caf9abdaaebc3c33cae9325aa1bc79c01b78757e3174ebb5006084844"> <param name="dir" value="/vecr/fc/home/frodo/a4"> <param name="prog" value="./p1"> <param name="args" value="">When the applet starts, it sends all of that data to the server:
// send authentication data // String user = getParameter( "user"); diag.append( "* Sending authentication data for "); diag.append( user); diag.append( newline); out.writeUTF( user); out.writeUTF( getParameter( "uid")); out.writeUTF( getParameter( "time")); out.writeUTF( getParameter( "ticket")); out.writeUTF( getParameter( "dir")); out.writeUTF( getParameter( "prog")); out.writeUTF( getParameter( "args")); out.flush();Authentication consists of verifying the ticket (and checking that it is not too old which was shown above):
// construct authentication data // SecretKeySpec KS = new SecretKeySpec( secret, "HmacSHA256"); Mac mac = Mac.getInstance( "HmacSHA256"); mac.init( KS); mac.update( time.getBytes()); mac.update( user.getBytes()); mac.update( uid.getBytes()); String auth = hex( mac.doFinal()); if( auth.compareTo( ticket) != 0) // client authentication ticket is invalid { out.write( "Authentication failed\n".getBytes()); out.flush(); throw new SecurityException( "Authentication failed for user " + user + " from " + name); }Since the user was already known to be authenticated at the time the IO option was initiated, the ticket allows that authentication to propagate from the applet to the server without having the user resend their userid and password.
Security Issues
The applet web page can be downloaded, saved, modified, uploaded, given to another user, etc.
The secret is created/recreated periodically like this:
dd if=/dev/random ibs=1 obs=1 count=64 > secret.datThe directory and file are protected:
% ls -ld . secret.dat drwxr-x--- 2 root root 512 Feb 13 11:02 . -rw------- 1 root root 64 Feb 13 11:35 secret.datThe secret.dat file is read as follows:
// read secret data // FileInputStream fin = new FileInputStream( "/vecr/IO/bin/secret.dat"); byte secret[] = new byte[64]; int n = fin.read( secret); fin.close(); if( n < 64) throw new SecurityException( "secret.dat is too small");