mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-28 12:45:32 +08:00
Merge remote-tracking branch
'origin/GP-1481_ghidra1_CancelServerConnect--SQUASHED' (Closes #2661)
This commit is contained in:
@@ -32,9 +32,9 @@ import ghidra.framework.remote.*;
|
|||||||
import ghidra.framework.remote.security.SSHKeyManager;
|
import ghidra.framework.remote.security.SSHKeyManager;
|
||||||
import ghidra.net.*;
|
import ghidra.net.*;
|
||||||
import ghidra.util.*;
|
import ghidra.util.*;
|
||||||
import ghidra.util.exception.AssertException;
|
import ghidra.util.exception.*;
|
||||||
import ghidra.util.exception.UserAccessException;
|
|
||||||
import ghidra.util.task.TaskLauncher;
|
import ghidra.util.task.TaskLauncher;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <code>ClientUtil</code> allows a user to connect to a Repository Server and obtain its handle.
|
* <code>ClientUtil</code> allows a user to connect to a Repository Server and obtain its handle.
|
||||||
@@ -294,16 +294,19 @@ public class ClientUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Connect to a Ghidra Server and verify compatibility. This method can be used
|
* Connect to a Ghidra Server and verify compatibility. This method can be used
|
||||||
* to affectively "ping" the Ghidra Server to verify the ability to connect.
|
* to effectively "ping" the Ghidra Server to verify the ability to connect.
|
||||||
* NOTE: Use of this method when PKI authentication is enabled is not supported.
|
* NOTE: Use of this method when PKI authentication is enabled is not supported.
|
||||||
* @param host server hostname
|
* @param host server hostname
|
||||||
* @param port first Ghidra Server port (0=use default)
|
* @param port first Ghidra Server port (0=use default)
|
||||||
|
* @param monitor cancellable monitor
|
||||||
* @throws IOException thrown if an IO Error occurs (e.g., server not found).
|
* @throws IOException thrown if an IO Error occurs (e.g., server not found).
|
||||||
* @throws RemoteException if server interface is incompatible or another server-side
|
* @throws RemoteException if server interface is incompatible or another server-side
|
||||||
* error occurs.
|
* error occurs.
|
||||||
|
* @throws CancelledException if connection attempt was cancelled
|
||||||
*/
|
*/
|
||||||
public static void checkGhidraServer(String host, int port) throws IOException {
|
public static void checkGhidraServer(String host, int port, TaskMonitor monitor)
|
||||||
ServerConnectTask.getGhidraServerHandle(new ServerInfo(host, port));
|
throws IOException, CancelledException {
|
||||||
|
ServerConnectTask.getGhidraServerHandle(new ServerInfo(host, port), monitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -319,24 +322,28 @@ public class ClientUtil {
|
|||||||
* @throws GeneralSecurityException if server authentication fails due to
|
* @throws GeneralSecurityException if server authentication fails due to
|
||||||
* credential access error (e.g., PKI cert failure)
|
* credential access error (e.g., PKI cert failure)
|
||||||
* @throws IOException thrown if an IO Error occurs.
|
* @throws IOException thrown if an IO Error occurs.
|
||||||
|
* @throws CancelledException if connection cancelled by user (does not apply to Headless use)
|
||||||
*/
|
*/
|
||||||
static RemoteRepositoryServerHandle connect(ServerInfo server)
|
static RemoteRepositoryServerHandle connect(ServerInfo server)
|
||||||
throws LoginException, GeneralSecurityException, IOException {
|
throws LoginException, GeneralSecurityException, IOException, CancelledException {
|
||||||
|
|
||||||
getClientAuthenticator();
|
getClientAuthenticator();
|
||||||
boolean allowLoginRetry = (clientAuthenticator instanceof DefaultClientAuthenticator);
|
boolean allowLoginRetry = (clientAuthenticator instanceof DefaultClientAuthenticator);
|
||||||
|
|
||||||
RemoteRepositoryServerHandle hdl = null;
|
RemoteRepositoryServerHandle hdl = null;
|
||||||
ServerConnectTask connectTask = new ServerConnectTask(server, allowLoginRetry);
|
ServerConnectTask connectTask = new ServerConnectTask(server, allowLoginRetry);
|
||||||
if (!SystemUtilities.isInHeadlessMode() && SystemUtilities.isEventDispatchThread()) {
|
if (SystemUtilities.isInHeadlessMode()) {
|
||||||
// Must be done in modal dialog to allow possible authentication prompts
|
connectTask.run(TaskMonitor.DUMMY); // headless - can't cancel
|
||||||
// from another thread.
|
|
||||||
|
|
||||||
TaskLauncher.launch(connectTask);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
connectTask.run(null);
|
// Must be done in modal dialog to allow cancellation and possible authentication prompts
|
||||||
|
// from another thread.
|
||||||
|
TaskLauncher.launch(connectTask);
|
||||||
|
if (connectTask.isCancelled()) {
|
||||||
|
throw new CancelledException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hdl = connectTask.getRepositoryServerHandle();
|
hdl = connectTask.getRepositoryServerHandle();
|
||||||
if (hdl == null) {
|
if (hdl == null) {
|
||||||
Exception e = connectTask.getException();
|
Exception e = connectTask.getException();
|
||||||
|
|||||||
+7
-1
@@ -137,7 +137,13 @@ public class RepositoryServerAdapter {
|
|||||||
|
|
||||||
Throwable cause = null;
|
Throwable cause = null;
|
||||||
try {
|
try {
|
||||||
serverHandle = ClientUtil.connect(server);
|
try {
|
||||||
|
serverHandle = ClientUtil.connect(server);
|
||||||
|
}
|
||||||
|
catch (CancelledException e) {
|
||||||
|
// ignore
|
||||||
|
Msg.debug(this, "Server connect cancelled by user");
|
||||||
|
}
|
||||||
unexpectedDisconnect = false;
|
unexpectedDisconnect = false;
|
||||||
if (serverHandle != null) {
|
if (serverHandle != null) {
|
||||||
Msg.info(this, "Connected to Ghidra Server at " + serverInfoStr);
|
Msg.info(this, "Connected to Ghidra Server at " + serverInfoStr);
|
||||||
|
|||||||
+78
-13
@@ -15,7 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.framework.client;
|
package ghidra.framework.client;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.Socket;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.rmi.*;
|
import java.rmi.*;
|
||||||
import java.rmi.registry.LocateRegistry;
|
import java.rmi.registry.LocateRegistry;
|
||||||
@@ -36,8 +38,8 @@ import ghidra.framework.model.ServerInfo;
|
|||||||
import ghidra.framework.remote.*;
|
import ghidra.framework.remote.*;
|
||||||
import ghidra.net.ApplicationKeyManagerFactory;
|
import ghidra.net.ApplicationKeyManagerFactory;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.task.Task;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Task for connecting to server with Swing thread.
|
* Task for connecting to server with Swing thread.
|
||||||
@@ -56,7 +58,7 @@ class ServerConnectTask extends Task {
|
|||||||
* @param allowLoginRetry true if login retry allowed during authentication
|
* @param allowLoginRetry true if login retry allowed during authentication
|
||||||
*/
|
*/
|
||||||
ServerConnectTask(ServerInfo server, boolean allowLoginRetry) {
|
ServerConnectTask(ServerInfo server, boolean allowLoginRetry) {
|
||||||
super("Connecting to " + server.getServerName(), false, false, true);
|
super("Connecting to " + server.getServerName(), true, false, true);
|
||||||
this.server = server;
|
this.server = server;
|
||||||
this.allowLoginRetry = allowLoginRetry;
|
this.allowLoginRetry = allowLoginRetry;
|
||||||
}
|
}
|
||||||
@@ -64,12 +66,14 @@ class ServerConnectTask extends Task {
|
|||||||
/**
|
/**
|
||||||
* Completes and necessary authentication and obtains a repository handle.
|
* Completes and necessary authentication and obtains a repository handle.
|
||||||
* If a connection error occurs, an exception will be stored ({@link #getException()}.
|
* If a connection error occurs, an exception will be stored ({@link #getException()}.
|
||||||
|
* @throws CancelledException if task cancelled
|
||||||
* @see ghidra.util.task.Task#run(ghidra.util.task.TaskMonitor)
|
* @see ghidra.util.task.Task#run(ghidra.util.task.TaskMonitor)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void run(TaskMonitor monitor) {
|
public void run(TaskMonitor monitor) throws CancelledException {
|
||||||
|
monitor = TaskMonitor.dummyIfNull(monitor);
|
||||||
try {
|
try {
|
||||||
hdl = getRepositoryServerHandle(ClientUtil.getUserName());
|
hdl = getRepositoryServerHandle(ClientUtil.getUserName(), monitor);
|
||||||
}
|
}
|
||||||
catch (RemoteException e) {
|
catch (RemoteException e) {
|
||||||
exc = e;
|
exc = e;
|
||||||
@@ -81,6 +85,12 @@ class ServerConnectTask extends Task {
|
|||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
exc = e;
|
exc = e;
|
||||||
}
|
}
|
||||||
|
finally {
|
||||||
|
if (monitor.isCancelled()) {
|
||||||
|
exc = null;
|
||||||
|
throw new CancelledException();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -142,18 +152,25 @@ class ServerConnectTask extends Task {
|
|||||||
/**
|
/**
|
||||||
* Obtain a remote instance of the Ghidra Server Handle object
|
* Obtain a remote instance of the Ghidra Server Handle object
|
||||||
* @param server server information
|
* @param server server information
|
||||||
|
* @param monitor cancellable monitor
|
||||||
* @return Ghidra Server Handle object
|
* @return Ghidra Server Handle object
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
|
* @throws CancelledException
|
||||||
*/
|
*/
|
||||||
public static GhidraServerHandle getGhidraServerHandle(ServerInfo server) throws IOException {
|
public static GhidraServerHandle getGhidraServerHandle(ServerInfo server, TaskMonitor monitor)
|
||||||
|
throws IOException, CancelledException {
|
||||||
|
|
||||||
GhidraServerHandle gsh = null;
|
GhidraServerHandle gsh = null;
|
||||||
|
boolean canCancel = monitor.isCancelEnabled(); // original state
|
||||||
try {
|
try {
|
||||||
// Test SSL Handshake to ensure that user is able to decrypt keystore.
|
// Test SSL Handshake to ensure that user is able to decrypt keystore.
|
||||||
// This is intended to work around an RMI issue where a continuous
|
// This is intended to work around an RMI issue where a continuous
|
||||||
// retry condition can occur when a user cancels the password entry
|
// retry condition can occur when a user cancels the password entry
|
||||||
// for their keystore which should cancel any connection attempt
|
// for their keystore which should cancel any connection attempt
|
||||||
testServerSSLConnection(server);
|
testServerSSLConnection(server, monitor);
|
||||||
|
|
||||||
|
monitor.setCancelEnabled(false);
|
||||||
|
monitor.setMessage("Connecting...");
|
||||||
|
|
||||||
Registry reg;
|
Registry reg;
|
||||||
try {
|
try {
|
||||||
@@ -191,20 +208,50 @@ class ServerConnectTask extends Task {
|
|||||||
}
|
}
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
finally {
|
||||||
|
monitor.setCancelEnabled(canCancel);
|
||||||
|
monitor.setMessage("");
|
||||||
|
}
|
||||||
return gsh;
|
return gsh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class ConnectCancelledListener implements CancelledListener, Closeable {
|
||||||
|
|
||||||
|
private TaskMonitor monitor;
|
||||||
|
private CancelledListener callback;
|
||||||
|
|
||||||
|
ConnectCancelledListener(TaskMonitor monitor, CancelledListener callback) {
|
||||||
|
this.monitor = monitor;
|
||||||
|
this.callback = callback;
|
||||||
|
monitor.addCancelledListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancelled() {
|
||||||
|
if (callback != null) {
|
||||||
|
callback.cancelled();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
monitor.removeCancelledListener(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempts server connection and completes any necessary authentication.
|
* Attempts server connection and completes any necessary authentication.
|
||||||
* @param defaultUserID
|
* @param defaultUserID
|
||||||
* @return server handle or null if authentication was cancelled by user
|
* @param monitor task monitor for connection cancellation
|
||||||
|
* @return server handle or null if authentication or connection attempt was cancelled by user
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
* @throws LoginException
|
* @throws LoginException
|
||||||
*/
|
*/
|
||||||
private RemoteRepositoryServerHandle getRepositoryServerHandle(String defaultUserID)
|
private RemoteRepositoryServerHandle getRepositoryServerHandle(String defaultUserID,
|
||||||
throws IOException, LoginException {
|
TaskMonitor monitor)
|
||||||
|
throws IOException, LoginException, CancelledException {
|
||||||
|
|
||||||
GhidraServerHandle gsh = getGhidraServerHandle(server);
|
GhidraServerHandle gsh = getGhidraServerHandle(server, monitor);
|
||||||
if (gsh == null) {
|
if (gsh == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -318,19 +365,37 @@ class ServerConnectTask extends Task {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void testServerSSLConnection(ServerInfo server) throws IOException {
|
private static void forceClose(Socket s) {
|
||||||
|
try {
|
||||||
|
s.close();
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void testServerSSLConnection(ServerInfo server, TaskMonitor monitor)
|
||||||
|
throws IOException, CancelledException {
|
||||||
|
|
||||||
RMIServerPortFactory portFactory = new RMIServerPortFactory(server.getPortNumber());
|
RMIServerPortFactory portFactory = new RMIServerPortFactory(server.getPortNumber());
|
||||||
SslRMIClientSocketFactory factory = new SslRMIClientSocketFactory();
|
SslRMIClientSocketFactory factory = new SslRMIClientSocketFactory();
|
||||||
String serverName = server.getServerName();
|
String serverName = server.getServerName();
|
||||||
int sslRmiPort = portFactory.getRMISSLPort();
|
int sslRmiPort = portFactory.getRMISSLPort();
|
||||||
|
|
||||||
try (SSLSocket socket = (SSLSocket) factory.createSocket(serverName, sslRmiPort)) {
|
monitor.setCancelEnabled(true);
|
||||||
|
monitor.setMessage("Checking Server Liveness...");
|
||||||
|
|
||||||
|
try (SSLSocket socket = (SSLSocket) factory.createSocket(serverName, sslRmiPort);
|
||||||
|
ConnectCancelledListener cancelListener =
|
||||||
|
new ConnectCancelledListener(monitor, () -> forceClose(socket))) {
|
||||||
// Complete SSL handshake to trigger client keystore access if required
|
// Complete SSL handshake to trigger client keystore access if required
|
||||||
// which will give user ability to cancel without involving RMI which
|
// which will give user ability to cancel without involving RMI which
|
||||||
// will avoid RMI reconnect attempts
|
// will avoid RMI reconnect attempts
|
||||||
socket.startHandshake();
|
socket.startHandshake();
|
||||||
}
|
}
|
||||||
|
finally {
|
||||||
|
monitor.checkCanceled(); // circumvent any IOException which may have occured
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void checkServerBindNames(Registry reg) throws RemoteException {
|
private static void checkServerBindNames(Registry reg) throws RemoteException {
|
||||||
|
|||||||
+3
-1
@@ -36,6 +36,7 @@ import ghidra.framework.remote.GhidraServerHandle;
|
|||||||
import ghidra.net.ApplicationKeyManagerFactory;
|
import ghidra.net.ApplicationKeyManagerFactory;
|
||||||
import ghidra.server.remote.ServerTestUtil;
|
import ghidra.server.remote.ServerTestUtil;
|
||||||
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
import ghidra.test.AbstractGhidraHeadlessIntegrationTest;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
import utilities.util.FileUtilities;
|
import utilities.util.FileUtilities;
|
||||||
|
|
||||||
@Category(PortSensitiveCategory.class)
|
@Category(PortSensitiveCategory.class)
|
||||||
@@ -110,7 +111,8 @@ public class GhidraServerSerialFilterFailureTest extends AbstractGhidraHeadlessI
|
|||||||
|
|
||||||
ServerInfo server = new ServerInfo("localhost", ServerTestUtil.GHIDRA_TEST_SERVER_PORT);
|
ServerInfo server = new ServerInfo("localhost", ServerTestUtil.GHIDRA_TEST_SERVER_PORT);
|
||||||
|
|
||||||
GhidraServerHandle serverHandle = ServerConnectTask.getGhidraServerHandle(server);
|
GhidraServerHandle serverHandle =
|
||||||
|
ServerConnectTask.getGhidraServerHandle(server, TaskMonitor.DUMMY);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
serverHandle.getRepositoryServer(getBogusUserSubject(), new Callback[0]);
|
serverHandle.getRepositoryServer(getBogusUserSubject(), new Callback[0]);
|
||||||
|
|||||||
Reference in New Issue
Block a user