diff --git a/buildwrapper.sh b/buildwrapper.sh index 265b90f..4526b8b 100755 --- a/buildwrapper.sh +++ b/buildwrapper.sh @@ -51,7 +51,7 @@ openssl genrsa -passout pass:${CLTCAPASS} -aes256 -out $CLIENT_PATH/private.key echo "[+] Generating Client signing request" openssl req -passin pass:${CLTCAPASS} -new -key $CLIENT_PATH/private.key -out $CLIENT_PATH/request.csr -subj "/CN=${CLIENT_CN}" echo "[+] Generating Client certificate " -openssl x509 -req -passin pass:${CLTCAPASS} -days 365 -in $CLIENT_PATH/request.csr -CA $CLIENT_PATH/ca.crt -CAkey $CLIENT_PATH/ca.key -set_serial 01 -out $CLIENT_PATH/server.crt +openssl x509 -req -passin pass:${CLTCAPASS} -days 365 -in $CLIENT_PATH/request.csr -CA $CLIENT_PATH/ca.crt -CAkey $CLIENT_PATH/ca.key -set_serial 01 -out $CLIENT_PATH/client.crt echo "[+] Removing passphrase from client key" openssl rsa -passin pass:${CLTCAPASS} -in $CLIENT_PATH/private.key -out $CLIENT_PATH/private.key @@ -61,7 +61,7 @@ openssl genrsa -passout pass:dontusethiskey -aes256 -out $TEST_PATH/private.key echo "[+] Generating test signing request" openssl req -passin pass:dontusethiskey -new -key $TEST_PATH/private.key -out $TEST_PATH/request.csr -subj "/CN=${DontUseMe}" echo "[+] Generating test certificate " -openssl x509 -req -passin pass:dontusethiskey -days 365 -in $TEST_PATH/request.csr -CA $TEST_PATH/ca.crt -CAkey $TEST_PATH/ca.key -set_serial 01 -out $TEST_PATH/server.crt +openssl x509 -req -passin pass:dontusethiskey -days 365 -in $TEST_PATH/request.csr -CA $TEST_PATH/ca.crt -CAkey $TEST_PATH/ca.key -set_serial 01 -out $TEST_PATH/test.crt echo "[+] Removing passphrase from test key" openssl rsa -passin pass:dontusethiskey -in $TEST_PATH/private.key -out $TEST_PATH/private.key @@ -75,4 +75,4 @@ echo "[+] creating combine trust store" cat $SERVER_PATH/ca.crt $CLIENT_PATH/ca.crt > resources/truststore.pem echo "[+] initiating gradle build" -./gradlew clear build +./gradlew clean build diff --git a/src/main/java/JobServ/JobServClient.java b/src/main/java/JobServ/JobServClient.java index b3df54f..b56c409 100644 --- a/src/main/java/JobServ/JobServClient.java +++ b/src/main/java/JobServ/JobServClient.java @@ -11,6 +11,13 @@ package JobServ; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import io.grpc.StatusRuntimeException; +import io.grpc.netty.GrpcSslContexts; +import io.grpc.netty.NettyChannelBuilder; +import io.netty.handler.ssl.SslContext; +import io.netty.handler.ssl.SslContextBuilder; + +import javax.net.ssl.SSLException; +import java.io.File; import java.util.InputMismatchException; import java.util.concurrent.TimeUnit; import java.util.logging.Level; @@ -23,7 +30,8 @@ import java.util.Scanner; */ public class JobServClient { - /* The client should not use the same logging module as the server. + /* + * The client should not use the same logging module as the server. * In a more robust product the server logging module will take advantage of system level * log aggregators such as journalctl, which the client should not be writing to on the users system */ @@ -31,33 +39,49 @@ public class JobServClient { private final ManagedChannel channel; - /* blockingStub is used when the client needs to block until the server responds + /* + * blockingStub is used when the client needs to block until the server responds * the client doesnt nessesarily need to support asynchronously firing off commands - * in this shell-like interface it would be disconcerting to get multiple returns out of order + * in this shell-like interface it would be disconcerting to get multiple returns out of order */ private final ShellServerGrpc.ShellServerBlockingStub blockingStub; - // Constructor connects to server - public JobServClient(String host, int port) { - this(ManagedChannelBuilder.forAddress(host, port) - // TODO: MTLS - .usePlaintext() - .build()); - } - - // private overload of constructor, used in the above constructor - JobServClient(ManagedChannel channel) { - this.channel = channel; - blockingStub = ShellServerGrpc.newBlockingStub(channel); + /* + * Constructor + * Creates an SslContext from cert, key, and trust store + * Creates a ManagedChannel object from SSL Parameters + * Spawns a new blockingStub for network operations with the server + */ + public JobServClient(String host, + int port, + String trustStore, + String clientCert, + String clientPrivateKey) throws SSLException { + SslContextBuilder builder = GrpcSslContexts.forClient(); + builder.trustManager(new File(trustStore)); + builder.keyManager(new File(clientCert), new File(clientPrivateKey)); + + this.channel = NettyChannelBuilder.forAddress(host, port) + .sslContext(builder.build()) + .build(); + + blockingStub = ShellServerGrpc.newBlockingStub(this.channel); } + /* + * shutdown() + * Gets called when you press cntrl+c + * takes at most 5 seconds to close its connection + */ public void shutdown() throws InterruptedException { channel.shutdown().awaitTermination(5, TimeUnit.SECONDS); } - // sends the server a request for output from PID - // different from getProcessStatus in output expected from the server - // returns process output as string + /* + * getProcessInfo() + * sends the server a request for output from the process identified by 'pid' + * returns process output as string + */ public String getProcessOutput(int pid) { logger.info("[+] requesting output"); @@ -77,8 +101,11 @@ public class JobServClient { return response.getOutput(); } - // sends the server a command for a new job, blocks until response - // returns new pid of job + /* + * sendNewJobMessage() + * sends a shell command to the api server + * returns new pid of job + */ public int sendNewJobMessage(String command) { // thought of escaping this, but the vulnerability is only client side, from client user input. logger.info("[+] Sending command to server"); @@ -103,8 +130,11 @@ public class JobServClient { return response.getPid(); } - // requests running status of job - // returns true if job still running else false + /* + * getProcessStatus() + * requests running status of process pid + * returns true if process still running else false + */ public Boolean getProcessStatus(int pid) { logger.info("[+] Requesting status of a job"); @@ -124,9 +154,12 @@ public class JobServClient { return response.getIsRunning(); } - // sends PID to server expecting the return cod eof a process - // function returns a 0-255 return code or 277 if still running - // or 278 if error in API + /* + * sends PID to server + * returns process exit code + * returns a 0-255 return code or 277 if still running + * or 278 if error in API + */ public int getProcessReturn(int pid) { logger.info("[+] Requesting return code of a job"); @@ -146,8 +179,11 @@ public class JobServClient { return response.getProcessReturnCode(); } - // send a PID to be killed, returns nothing - // logs warning if job status comes back still running + /* + * killProcess() + * send a PID to be killed, function returns nothing + * logs warning if job status comes back still running + */ public void killProcess(int pid) { logger.info("[+] Killing a job"); @@ -169,40 +205,49 @@ public class JobServClient { } } - // Client entrypoint + /* + * main() + * Client entrypoint + * Parses arguments and calls the correct function + */ public static void main(String[] args) throws Exception { if (args.length == 1 && args[0] == "help"){ outputHelp(); } // check args - if (args.length < 3) { - System.out.println("Usage: $ jobservclient host port command"); - System.out.println("Or try client --help"); + if (args.length < 7) { + System.out.println("Usage: $ ./jobserv-client privatekey, cert, truststore, host, port, command, args"); + System.out.println("Or try $ ./jobserv-client help"); return; } - // start client (or fail if port is improperly formatted) + // start client + // fails if port is improperly formatted or if an ssl exception occurs JobServClient client; try { - client = new JobServClient(args[0], Integer.parseInt(args[1])); + client = new JobServClient(args[0], Integer.parseInt(args[1]), args[2], args[1], args[0]); } catch (NumberFormatException e) { System.out.println("Invalid Port"); return; + } catch (SSLException e) { + System.out.println(e.getMessage()); + return; } - // declare up here so that multiple switch cases can use it + // declare pid up here so that multiple switch cases can use it int candidatePid; + // parse remaining args switch (args[2]) { case "new": - if (args.length < 4) { + if (args.length < 7) { System.out.println("Improper formatting, try client --help"); break; } String command = ""; - for (int token = 3; token < args.length; token++) { + for (int token = 6; token < args.length; token++) { command += " " + args[token]; } @@ -211,15 +256,15 @@ public class JobServClient { break; case "output": - if (args.length != 4) { + if (args.length != 7) { System.out.println("Improper formatting, try client --help"); break; } try { - candidatePid = Integer.parseInt(args[3]); + candidatePid = Integer.parseInt(args[6]); } catch (InputMismatchException e) { - System.out.println(args[3] + " is not a valid int, much less a valid pid"); + System.out.println(args[6] + " is not a valid int, much less a valid pid"); break; } @@ -228,16 +273,16 @@ public class JobServClient { break; case "status": - if (args.length != 4) { + if (args.length != 7) { System.out.println("Improper formatting, try client --help"); break; } try { - candidatePid = Integer.parseInt(args[3]); + candidatePid = Integer.parseInt(args[6]); } catch (InputMismatchException e) { - System.out.println(args[3] + " is not a valid int, much less a valid pid"); + System.out.println(args[6] + " is not a valid int, much less a valid pid"); break; } @@ -246,16 +291,16 @@ public class JobServClient { break; case "kill": - if (args.length != 4) { + if (args.length != 7) { System.out.println("Improper formatting, try client --help"); break; } try { - candidatePid = Integer.parseInt(args[3]); + candidatePid = Integer.parseInt(args[6]); } catch (InputMismatchException e) { - System.out.println(args[3] + " is not a valid int, much less a valid pid"); + System.out.println(args[6] + " is not a valid int, much less a valid pid"); break; } @@ -264,16 +309,16 @@ public class JobServClient { break; case "return": - if (args.length != 4) { + if (args.length != 7) { System.out.println("Improper formatting, try client --help"); break; } try { - candidatePid = Integer.parseInt(args[3]); + candidatePid = Integer.parseInt(args[6]); } catch (InputMismatchException e) { - System.out.println(args[3] + " is not a valid int, much less a valid pid"); + System.out.println(args[6] + " is not a valid int, much less a valid pid"); break; }