// VECR IO client // // spawned by the IO server to handle one client import java.net.*; import java.io.*; import java.util.*; import javax.crypto.*; import javax.crypto.spec.*; public class IOclient extends Thread { // IO protocol // static final byte DATA = 0; static final byte EOF = 1; static final byte STOP = 2; static final byte SAVE = 3; // network I/O // private Socket s; private DataInputStream in; private BufferedOutputStream out; private String name; // process I/O // private Process p; private ProcessBuilder pb; private BufferedInputStream pin; private BufferedOutputStream pout; private IOcopy copy; /* hex conversion code adapted from cryptix.util.core.Hex */ private static final char[] hexDigits = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' }; // convert byte array to hex // public static String hex(byte[] ba) { char[] buf = new char[ba.length * 2]; int j = 0; int k; for (int i = 0; i < ba.length; i++) { k = ba[i]; buf[j++] = hexDigits[(k >>> 4) & 0x0F]; buf[j++] = hexDigits[ k & 0x0F]; } return new String(buf); } public IOclient( Socket s, String name) throws IOException { this.s = s; this.name = name; in = new DataInputStream( new BufferedInputStream( s.getInputStream())); out = new BufferedOutputStream( s.getOutputStream()); } // save applet output window to "output.txt" // public void save( String data, String uid, String dir) { try { // prepare arguments for subprocess // ArrayList argv = new ArrayList(10); argv.add( "/vecr/IO/bin/save"); argv.add( "--setuidgid"); argv.add( uid); argv.add( uid); argv.add( "--chdir"); argv.add( dir); // create save subprocess // ProcessBuilder pb = new ProcessBuilder( argv); pb.redirectErrorStream( true); Process p = pb.start(); BufferedOutputStream pout = new BufferedOutputStream( p.getOutputStream()); pout.write( data.getBytes()); pout.close(); } catch(Exception e) { synchronized( System.out) { e.printStackTrace( System.out); System.out.flush(); } } } public void run() { String line; byte b; try { // read parameters sent by the applet // String user = in.readUTF(); String uid = in.readUTF(); String time = in.readUTF(); String ticket = in.readUTF(); String dir = in.readUTF(); String prog = in.readUTF(); String args = in.readUTF(); // 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"); // 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); } synchronized( System.out) { System.out.println( "Authentication succeeded for user " + user + " from " + name); System.out.flush(); } // check authentication ticket time limit // long mytime = System.currentTimeMillis(); long yourtime = Long.parseLong( time); if( mytime - yourtime > 1800000) // 30 minute limit { out.write( "Authentication data is too old\n".getBytes()); out.flush(); throw new SecurityException( "Stale authentication data for user " + user + " from " + name); } // prepare arguments for subprocess // ArrayList argv = new ArrayList(25); argv.add( "/vecr/IO/bin/run"); argv.add( "--setuidgid"); argv.add( uid); argv.add( uid); argv.add( "--chdir"); argv.add( dir); argv.add( prog); String[] user_argv = args.split( "\\s+"); for( int j = 0; j < user_argv.length; ++j) argv.add( user_argv[j]); String msg = "Running in " + dir + ": " + prog + " " + args + "\n"; out.write( msg.getBytes()); out.flush(); // create run subprocess // pb = new ProcessBuilder( argv); pb.redirectErrorStream( true); p = pb.start(); pin = new BufferedInputStream( p.getInputStream()); // reads from subprocess stdout pout = new BufferedOutputStream( p.getOutputStream()); // writes to subprocess stdin // create IO copy thread // copy = new IOcopy( pin, out); copy.start(); // loop until applet sends STOP // do { b = in.readByte(); switch( b) { case DATA: line = in.readUTF(); if( pout != null) { pout.write( line.getBytes()); pout.write( '\n'); pout.flush(); } break; case EOF: if( pout != null) { pout.close(); pout = null; } break; case STOP: out.write( "\nStop.\n".getBytes()); out.flush(); p.destroy(); p = null; break; case SAVE: save( in.readUTF(), uid, dir); out.write( "\nSaved.\n".getBytes()); out.flush(); break; default: // shouldn't happen b = STOP; break; } } while( b != STOP); } catch(Exception e) { synchronized( System.out) { e.printStackTrace( System.out); System.out.flush(); } } finally { Date now = new Date(); synchronized( System.out) { System.out.println( now + ": disconnect " + name); System.out.flush(); try { if( copy != null) copy.interrupt(); if( pin != null) pin.close(); if( pout != null) pout.close(); if( p != null) p.destroy(); in.close(); out.close(); s.close(); } catch( Exception e) { e.printStackTrace( System.out); System.out.flush(); } } } } }