8145838: JShell: restrict RemoteAgent connection socket to localhost

Also reviewed by Chris Ries

Reviewed-by: rfield
This commit is contained in:
Jan Lahoda 2016-11-11 12:54:47 +01:00
parent 5f24c132d9
commit d969a1a3fa
2 changed files with 62 additions and 2 deletions

View File

@ -24,18 +24,23 @@
*/
package jdk.jshell.execution;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import com.sun.jdi.BooleanValue;
import com.sun.jdi.ClassNotLoadedException;
import com.sun.jdi.Field;
@ -111,7 +116,7 @@ public class JdiDefaultExecutionControl extends JdiExecutionControl {
*/
private static ExecutionControl create(ExecutionEnv env,
boolean isLaunch, String host) throws IOException {
try (final ServerSocket listener = new ServerSocket(0)) {
try (final ServerSocket listener = new ServerSocket(0, 1, InetAddress.getLoopbackAddress())) {
// timeout after 60 seconds
listener.setSoTimeout(60000);
int port = listener.getLocalPort();
@ -122,6 +127,14 @@ public class JdiDefaultExecutionControl extends JdiExecutionControl {
VirtualMachine vm = jdii.vm();
Process process = jdii.process();
OutputStream processOut = process.getOutputStream();
SecureRandom rng = new SecureRandom();
byte[] randomBytes = new byte[VERIFY_HASH_LEN];
rng.nextBytes(randomBytes);
processOut.write(randomBytes);
processOut.flush();
List<Consumer<String>> deathListeners = new ArrayList<>();
deathListeners.add(s -> env.closeDown());
Util.detectJdiExitEvent(vm, s -> {
@ -130,6 +143,8 @@ public class JdiDefaultExecutionControl extends JdiExecutionControl {
}
});
ByteArrayOutputStream receivedRandomBytes = new ByteArrayOutputStream();
// Set-up the commands/reslts on the socket. Piggy-back snippet
// output.
Socket socket = listener.accept();
@ -138,11 +153,35 @@ public class JdiDefaultExecutionControl extends JdiExecutionControl {
Map<String, OutputStream> outputs = new HashMap<>();
outputs.put("out", env.userOut());
outputs.put("err", env.userErr());
outputs.put("echo", new OutputStream() {
@Override public void write(int b) throws IOException {
synchronized (receivedRandomBytes) {
receivedRandomBytes.write(b);
receivedRandomBytes.notify();
}
}
});
Map<String, InputStream> input = new HashMap<>();
input.put("in", env.userIn());
return remoteInputOutput(socket.getInputStream(), out, outputs, input, (objIn, objOut) -> new JdiDefaultExecutionControl(objOut, objIn, vm, process, deathListeners));
return remoteInputOutput(socket.getInputStream(), out, outputs, input, (objIn, objOut) -> {
synchronized (receivedRandomBytes) {
while (receivedRandomBytes.size() < randomBytes.length) {
try {
receivedRandomBytes.wait();
} catch (InterruptedException ex) {
//ignore
}
}
if (!Arrays.equals(receivedRandomBytes.toByteArray(), randomBytes)) {
throw new IllegalStateException("Invalid connection!");
}
}
return new JdiDefaultExecutionControl(objOut, objIn, vm, process, deathListeners);
});
}
}
//where:
private static final int VERIFY_HASH_LEN = 20;
/**
* Create an instance.

View File

@ -24,6 +24,7 @@
*/
package jdk.jshell.execution;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
@ -57,6 +58,7 @@ public class RemoteExecutionControl extends DirectExecutionControl implements Ex
* @throws Exception any unexpected exception
*/
public static void main(String[] args) throws Exception {
InputStream fd0 = System.in;
String loopBack = null;
Socket socket = new Socket(loopBack, Integer.parseInt(args[0]));
InputStream inStream = socket.getInputStream();
@ -64,6 +66,25 @@ public class RemoteExecutionControl extends DirectExecutionControl implements Ex
Map<String, Consumer<OutputStream>> outputs = new HashMap<>();
outputs.put("out", st -> System.setOut(new PrintStream(st, true)));
outputs.put("err", st -> System.setErr(new PrintStream(st, true)));
outputs.put("echo", st -> {
new Thread(() -> {
try {
int read;
while ((read = fd0.read()) != (-1)) {
st.write(read);
}
} catch (IOException ex) {
ex.printStackTrace();
} finally {
try {
st.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}).start();
});
Map<String, Consumer<InputStream>> input = new HashMap<>();
input.put("in", st -> System.setIn(st));
forwardExecutionControlAndIO(new RemoteExecutionControl(), inStream, outStream, outputs, input);