mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-10 08:47:47 +08:00
Merge remote-tracking branch 'origin/GP-1_ghidragon_test_fixes_4_22_26'
This commit is contained in:
+15
-4
@@ -20,7 +20,9 @@ import java.util.List;
|
||||
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.framework.*;
|
||||
import ghidra.framework.remote.GhidraObjectInputFilter;
|
||||
import ghidra.net.DefaultTrustManagerFactory;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
|
||||
public class HeadlessBSimApplicationConfiguration extends ApplicationConfiguration {
|
||||
@@ -29,11 +31,20 @@ public class HeadlessBSimApplicationConfiguration extends ApplicationConfigurati
|
||||
protected void initializeApplication() {
|
||||
super.initializeApplication();
|
||||
|
||||
// Locate certs if found (must be done before module initialization)
|
||||
locateCACertsFile();
|
||||
try {
|
||||
// Install client-side deserialization filters (data/*.serial.filter)
|
||||
GhidraObjectInputFilter.configureClientSerialFilter();
|
||||
|
||||
monitor.setMessage("Performing module initialization...");
|
||||
performModuleInitialization();
|
||||
// Locate certs if found (must be done before module initialization)
|
||||
locateCACertsFile();
|
||||
|
||||
monitor.setMessage("Performing module initialization...");
|
||||
performModuleInitialization();
|
||||
}
|
||||
catch (Throwable t) {
|
||||
Msg.error(this, "Ghidra encountered a severe error during initialization", t);
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
monitor.setMessage("Done initializing");
|
||||
}
|
||||
|
||||
+19
-9
@@ -21,6 +21,7 @@ import java.util.List;
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.GhidraClassLoader;
|
||||
import ghidra.framework.preferences.Preferences;
|
||||
import ghidra.framework.remote.GhidraObjectInputFilter;
|
||||
import ghidra.net.DefaultTrustManagerFactory;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
@@ -31,19 +32,28 @@ public class HeadlessGhidraApplicationConfiguration extends ApplicationConfigura
|
||||
@Override
|
||||
protected void initializeApplication() {
|
||||
super.initializeApplication();
|
||||
|
||||
try {
|
||||
// Install client-side deserialization filters (data/*.serial.filter)
|
||||
GhidraObjectInputFilter.configureClientSerialFilter();
|
||||
|
||||
// Now that preferences are accessible, finalize classpath by adding user plugin paths.
|
||||
// This must be done before class searching.
|
||||
addUserJarAndPluginPathsToClasspath();
|
||||
// Now that preferences are accessible, finalize classpath by adding user plugin paths.
|
||||
// This must be done before class searching.
|
||||
addUserJarAndPluginPathsToClasspath();
|
||||
|
||||
monitor.setMessage("Performing class searching...");
|
||||
performClassSearching();
|
||||
monitor.setMessage("Performing class searching...");
|
||||
performClassSearching();
|
||||
|
||||
// Locate certs if found (must be done before module initialization)
|
||||
locateCACertsFile();
|
||||
// Locate certs if found (must be done before module initialization)
|
||||
locateCACertsFile();
|
||||
|
||||
monitor.setMessage("Performing module initialization...");
|
||||
performModuleInitialization();
|
||||
monitor.setMessage("Performing module initialization...");
|
||||
performModuleInitialization();
|
||||
}
|
||||
catch (Throwable t) {
|
||||
Msg.error(this, "Ghidra encountered a severe error during initialization", t);
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
monitor.setMessage("Done initializing");
|
||||
}
|
||||
|
||||
@@ -1,22 +1,67 @@
|
||||
# Ghidra Server serialization filter patterns
|
||||
# See java.io.ObjectInputFilter.Config#createFilter(String)
|
||||
#
|
||||
# This file establishes allowed and disallowed inbound class de-serialization
|
||||
# rules for the Ghidra Server. If not specifically allowed or disallowed a
|
||||
# de-serialized class will be subject to an internal filter which allows all
|
||||
# primitive and primitive array classes while rejecting all other classes.
|
||||
#
|
||||
|
||||
java.base/java.lang.*;
|
||||
java.base/java.security.**;
|
||||
java.base/javax.security.**;
|
||||
java.base/sun.security.**;
|
||||
java.base/java.util.**;
|
||||
|
||||
ghidra.framework.remote.GhidraPrincipal;
|
||||
ghidra.framework.remote.AnonymousCallback;
|
||||
ghidra.framework.remote.SSHSignatureCallback;
|
||||
ghidra.framework.remote.SignatureCallback;
|
||||
ghidra.framework.remote.User;
|
||||
ghidra.framework.remote.User[];
|
||||
[Lghidra.framework.remote.User;
|
||||
ghidra.framework.store.CheckoutType;
|
||||
|
||||
java.lang.Object;
|
||||
java.lang.String;
|
||||
java.lang.Class;
|
||||
java.lang.Enum;
|
||||
|
||||
java.util.Collections$SynchronizedSet;
|
||||
java.util.Collections$SynchronizedCollection;
|
||||
java.util.LinkedList;
|
||||
|
||||
java.security.cert.Certificate$CertificateRep;
|
||||
java.security.cert.X509Certificate;
|
||||
[Ljava.security.cert.X509Certificate;
|
||||
|
||||
javax.security.auth.Subject;
|
||||
javax.security.auth.Subject$*;
|
||||
javax.security.auth.x500.X500Principal;
|
||||
[Ljavax.security.auth.x500.X500Principal;
|
||||
javax.security.auth.callback.Callback;
|
||||
[Ljavax.security.auth.callback.Callback;
|
||||
javax.security.auth.callback.NameCallback;
|
||||
javax.security.auth.callback.PasswordCallback;
|
||||
|
||||
# TODO: Server Remote API needs to be revised serialize certificates in PEM form.
|
||||
# This affects ghidra.framework.remote.SignatureCallback implementation.
|
||||
sun.security.x509.X509CertImpl;
|
||||
|
||||
# RMI related classes
|
||||
|
||||
java.rmi.server.UID;
|
||||
java.rmi.server.ObjID;
|
||||
|
||||
java.rmi.dgc.DGC;
|
||||
java.rmi.dgc.Lease;
|
||||
java.rmi.dgc.VMID;
|
||||
|
||||
java.rmi.RemoteException;
|
||||
java.rmi.AccessException;
|
||||
java.rmi.ConnectException;
|
||||
java.rmi.ConnectIOException;
|
||||
java.rmi.MarshalException;
|
||||
java.rmi.UnmarshalException;
|
||||
java.rmi.NoSuchObjectException;
|
||||
java.rmi.ServerException;
|
||||
java.rmi.ServerRuntimeException;
|
||||
java.rmi.UnexpectedException;
|
||||
java.rmi.UnknownHostException;
|
||||
|
||||
# The following additional entries may be required if using JMX over RMI for remote
|
||||
# profiling (e.g., VisualVM).
|
||||
|
||||
#javax.management.remote.rmi.RMIConnectionImpl_Stub;
|
||||
#javax.management.remote.rmi.RMIServerImpl_Stub;
|
||||
#java.rmi.server.RemoteObjectInvocationHandler;
|
||||
#sun.rmi.server.UnicastRef;
|
||||
#sun.rmi.transport.LiveRef;
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import ghidra.framework.remote.*;
|
||||
import ghidra.framework.store.FileSystem;
|
||||
import ghidra.framework.store.FileSystemListener;
|
||||
import ghidra.framework.store.local.*;
|
||||
import ghidra.server.remote.RemoteLoggingUtil;
|
||||
import ghidra.server.remote.RepositoryHandleImpl;
|
||||
import ghidra.server.store.RepositoryFile;
|
||||
import ghidra.server.store.RepositoryFolder;
|
||||
@@ -327,9 +328,8 @@ public class Repository implements FileSystemListener, RepositoryLogger {
|
||||
* defined to the repository user manager.
|
||||
* @param currentUser user performing request
|
||||
* @return list of user names.
|
||||
* @throws IOException if an IO error occurs
|
||||
*/
|
||||
public String[] getServerUserList(String currentUser) throws IOException {
|
||||
public String[] getServerUserList(String currentUser) {
|
||||
if (UserManager.ANONYMOUS_USERNAME.equals(currentUser)) {
|
||||
return new String[0];
|
||||
}
|
||||
@@ -779,7 +779,7 @@ public class Repository implements FileSystemListener, RepositoryLogger {
|
||||
RepositoryFolder folder = getFolder(null, parentPath, false);
|
||||
if (folder == null || folder.getFolder(folderName) == null) {
|
||||
RepositoryManager.log(name, RepositoryFolder.makePathname(parentPath, folderName),
|
||||
"ERROR! folder not found", null);
|
||||
"ERROR! folder not found");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -788,7 +788,7 @@ public class Repository implements FileSystemListener, RepositoryLogger {
|
||||
}
|
||||
catch (IOException e) {
|
||||
RepositoryManager.log(name, RepositoryFolder.makePathname(parentPath, folderName),
|
||||
"ERROR! " + e.getMessage(), null);
|
||||
"ERROR! " + e.getMessage());
|
||||
}
|
||||
|
||||
RepositoryChangeEvent event = new RepositoryChangeEvent(
|
||||
@@ -808,7 +808,7 @@ public class Repository implements FileSystemListener, RepositoryLogger {
|
||||
RepositoryFolder folder = getFolder(null, parentPath, false);
|
||||
if (folder == null || folder.getFile(itemName) == null) {
|
||||
RepositoryManager.log(name, RepositoryFolder.makePathname(parentPath, itemName),
|
||||
"file not found", null);
|
||||
"file not found");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -817,7 +817,7 @@ public class Repository implements FileSystemListener, RepositoryLogger {
|
||||
}
|
||||
catch (IOException e) {
|
||||
RepositoryManager.log(name, RepositoryFolder.makePathname(parentPath, itemName),
|
||||
"ERROR! " + e.getMessage(), null);
|
||||
"ERROR! " + e.getMessage());
|
||||
}
|
||||
|
||||
RepositoryChangeEvent event = new RepositoryChangeEvent(
|
||||
@@ -844,7 +844,7 @@ public class Repository implements FileSystemListener, RepositoryLogger {
|
||||
throw new AssertException();
|
||||
}
|
||||
catch (IOException e) {
|
||||
RepositoryManager.log(name, parentPath, "ERROR! " + e.getMessage(), null);
|
||||
RepositoryManager.log(name, parentPath, "ERROR! " + e.getMessage());
|
||||
}
|
||||
|
||||
RepositoryChangeEvent event = new RepositoryChangeEvent(
|
||||
@@ -933,11 +933,10 @@ public class Repository implements FileSystemListener, RepositoryLogger {
|
||||
}
|
||||
catch (IOException e) {
|
||||
RepositoryManager.log(name, RepositoryFolder.makePathname(parentPath, itemName),
|
||||
"ERROR! " + e.getMessage(), null);
|
||||
"ERROR! " + e.getMessage());
|
||||
}
|
||||
if (syncErr) {
|
||||
RepositoryManager.log(name, null, "ERROR! Repository instance may be out-of-sync",
|
||||
null);
|
||||
RepositoryManager.log(name, null, "ERROR! Repository instance may be out-of-sync");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -956,7 +955,7 @@ public class Repository implements FileSystemListener, RepositoryLogger {
|
||||
|
||||
@Override
|
||||
public void log(String path, String msg, String user) {
|
||||
RepositoryManager.log(name, path, msg, user);
|
||||
RemoteLoggingUtil.log(name, path, msg, user, false);
|
||||
}
|
||||
|
||||
static boolean markRepositoryForIndexMigration(File serverDir, String repositoryName,
|
||||
|
||||
@@ -119,10 +119,12 @@ public class RepositoryManager {
|
||||
* given name
|
||||
* @throws UserAccessException if the user does not exist in
|
||||
* the list of known users for this manager
|
||||
* @throws UserAccessException if the currentUser does not have
|
||||
* ability to create a repository
|
||||
* @throws IOException if there was an error creating the repository
|
||||
*/
|
||||
public synchronized Repository createRepository(String currentUser, String name)
|
||||
throws IOException, DuplicateFileException {
|
||||
throws UserAccessException, IOException, DuplicateFileException {
|
||||
|
||||
if (isAnonymousUser(currentUser)) {
|
||||
throw new UserAccessException("Anonymous user not permitted to create repository");
|
||||
@@ -147,7 +149,6 @@ public class RepositoryManager {
|
||||
}
|
||||
|
||||
Repository rep = new Repository(this, currentUser, f, name);
|
||||
log(name, null, "repository created", currentUser);
|
||||
repositoryMap.put(name, rep);
|
||||
return rep;
|
||||
}
|
||||
@@ -187,9 +188,11 @@ public class RepositoryManager {
|
||||
* Delete a specified repository.
|
||||
* @param currentUser current user
|
||||
* @param name repository name
|
||||
* @throws UserAccessException if currentUser does not have Admin priviledge
|
||||
* @throws IOException if error occurs while removing repository
|
||||
*/
|
||||
public synchronized void deleteRepository(String currentUser, String name) throws IOException {
|
||||
public synchronized void deleteRepository(String currentUser, String name)
|
||||
throws UserAccessException, IOException {
|
||||
|
||||
if (isAnonymousUser(currentUser)) {
|
||||
throw new UserAccessException("Anonymous user not permitted to delete repository");
|
||||
@@ -433,23 +436,13 @@ public class RepositoryManager {
|
||||
return host;
|
||||
}
|
||||
|
||||
public static void log(String repositoryName, String path, String msg, String user) {
|
||||
static void log(String repositoryName, String path, String msg) {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
if (repositoryName != null) {
|
||||
buf.append("[");
|
||||
buf.append(repositoryName);
|
||||
buf.append("]");
|
||||
}
|
||||
String host = RepositoryManager.getRMIClient();
|
||||
String userStr = user;
|
||||
if (userStr != null) {
|
||||
if (host != null) {
|
||||
userStr += "@" + host;
|
||||
}
|
||||
}
|
||||
else {
|
||||
userStr = host;
|
||||
}
|
||||
if (path != null) {
|
||||
buf.append(path);
|
||||
}
|
||||
@@ -457,11 +450,6 @@ public class RepositoryManager {
|
||||
buf.append(": ");
|
||||
}
|
||||
buf.append(msg);
|
||||
if (userStr != null) {
|
||||
buf.append(" (");
|
||||
buf.append(userStr);
|
||||
buf.append(")");
|
||||
}
|
||||
log.info(buf.toString());
|
||||
}
|
||||
|
||||
|
||||
+52
-164
@@ -70,9 +70,6 @@ public class GhidraServer extends UnicastRemoteObject implements GhidraServerHan
|
||||
private static final String TLS_SERVER_PROTOCOLS_PROPERTY = "ghidra.tls.server.protocols";
|
||||
private static final String TLS_ENABLED_CIPHERS_PROPERTY = "jdk.tls.server.cipherSuites";
|
||||
|
||||
private static final String SERIALIZATION_FILTER_DISABLED_PROPERTY =
|
||||
"ghidra.server.serialization.filter.disabled";
|
||||
|
||||
private static SslRMIServerSocketFactory serverSocketFactory;
|
||||
private static SslRMIClientSocketFactory clientSocketFactory;
|
||||
private static InetAddress bindAddress;
|
||||
@@ -210,9 +207,6 @@ public class GhidraServer extends UnicastRemoteObject implements GhidraServerHan
|
||||
|
||||
GhidraServer.server = this;
|
||||
|
||||
// Establish serialization filter to address deserialization vulnerabity concerns.
|
||||
setGlobalSerializationFilter();
|
||||
|
||||
// Start block stream server - use RMI serverSocketFactory
|
||||
blockStreamServer = BlockStreamServer.getBlockStreamServer();
|
||||
ServerSocket streamServerSocket;
|
||||
@@ -244,7 +238,7 @@ public class GhidraServer extends UnicastRemoteObject implements GhidraServerHan
|
||||
}
|
||||
catch (Throwable t) {
|
||||
log.error("Failed to generate authentication callbacks", t);
|
||||
throw new RemoteException("Failed to generate authentication callbacks", t);
|
||||
throw new RemoteException("Failed to generate authentication callbacks");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,7 +257,7 @@ public class GhidraServer extends UnicastRemoteObject implements GhidraServerHan
|
||||
|
||||
@Override
|
||||
public RemoteRepositoryServerHandle getRepositoryServer(Subject user, Callback[] authCallbacks)
|
||||
throws LoginException, RemoteException {
|
||||
throws FailedLoginException, RemoteException {
|
||||
|
||||
System.gc();
|
||||
|
||||
@@ -278,29 +272,29 @@ public class GhidraServer extends UnicastRemoteObject implements GhidraServerHan
|
||||
anonymousAuthModule.anonymousAccessRequested(authCallbacks)) {
|
||||
username = UserManager.ANONYMOUS_USERNAME;
|
||||
anonymousAccess = true;
|
||||
RepositoryManager.log(null, null, "Anonymous access allowed", principal.getName());
|
||||
RemoteLoggingUtil.log("Anonymous access allowed", principal.getName());
|
||||
}
|
||||
else if (authModule != null) {
|
||||
NameCallback nameCb =
|
||||
AuthenticationModule.getFirstCallbackOfType(NameCallback.class, authCallbacks);
|
||||
if (nameCb != null) {
|
||||
if (!authModule.isNameCallbackAllowed()) {
|
||||
RepositoryManager.log(null, null,
|
||||
RemoteLoggingUtil.log(
|
||||
"Illegal authentication callback: NameCallback not permitted", username);
|
||||
throw new LoginException("Illegal authentication callback");
|
||||
throw new FailedLoginException("Illegal authentication callback");
|
||||
}
|
||||
String name = nameCb.getName();
|
||||
if (name == null) {
|
||||
RepositoryManager.log(null, null,
|
||||
RemoteLoggingUtil.log(
|
||||
"Illegal authentication callback: NameCallback must specify login name",
|
||||
username);
|
||||
throw new LoginException("Illegal authentication callback");
|
||||
throw new FailedLoginException("Illegal authentication callback");
|
||||
}
|
||||
username = name;
|
||||
}
|
||||
}
|
||||
|
||||
RepositoryManager.log(null, null, "Repository server handle requested", username);
|
||||
RemoteLoggingUtil.log("Repository server handle requested", username);
|
||||
|
||||
boolean supportPasswordChange = false;
|
||||
if (!anonymousAccess) {
|
||||
@@ -310,10 +304,11 @@ public class GhidraServer extends UnicastRemoteObject implements GhidraServerHan
|
||||
username =
|
||||
sshAuthModule.authenticate(mgr.getUserManager(), user, authCallbacks);
|
||||
}
|
||||
catch (LoginException e) {
|
||||
RepositoryManager.log(null, null,
|
||||
"SSH Authentication failed (" + e.getMessage() + ")", username);
|
||||
throw e;
|
||||
catch (FailedLoginException e) {
|
||||
RemoteLoggingUtil.log("SSH Authentication failed (" + e.getMessage() + ")",
|
||||
username);
|
||||
// Create new exceptions so we don't leak config info to the client.
|
||||
throw new FailedLoginException("SSH authentication failed");
|
||||
}
|
||||
}
|
||||
else if (authModule != null) {
|
||||
@@ -325,14 +320,13 @@ public class GhidraServer extends UnicastRemoteObject implements GhidraServerHan
|
||||
if (autoProvisionAuthedUsers) {
|
||||
try {
|
||||
mgr.getUserManager().addUser(username);
|
||||
RepositoryManager.log(null, null,
|
||||
RemoteLoggingUtil.log(
|
||||
"User '" + username + "' successful auto provision",
|
||||
username);
|
||||
}
|
||||
catch (DuplicateNameException | IOException e) {
|
||||
RepositoryManager.log(
|
||||
null, null, "User '" + username +
|
||||
"' auto provision failed. Cause: " + e.getMessage(),
|
||||
RemoteLoggingUtil.log("User '" + username +
|
||||
"' auto provision failed. Cause: " + e.getMessage(),
|
||||
username);
|
||||
throw new LoginException(
|
||||
"Error when trying to auto provision successfully authenticated user: " +
|
||||
@@ -340,7 +334,7 @@ public class GhidraServer extends UnicastRemoteObject implements GhidraServerHan
|
||||
}
|
||||
}
|
||||
else {
|
||||
RepositoryManager.log(null, null,
|
||||
RemoteLoggingUtil.log(
|
||||
"User successfully authenticated, but does not exist in Ghidra user list: " +
|
||||
username,
|
||||
null);
|
||||
@@ -351,36 +345,42 @@ public class GhidraServer extends UnicastRemoteObject implements GhidraServerHan
|
||||
throw new LoginException("Unknown user: " + username);
|
||||
}
|
||||
}
|
||||
RepositoryManager.log(null, null, "User '" + username + "' authenticated",
|
||||
RemoteLoggingUtil.log("User '" + username + "' authenticated",
|
||||
principal.getName());
|
||||
}
|
||||
}
|
||||
catch (LoginException e) {
|
||||
RepositoryManager.log(null, null, "Login failed (" + e.getMessage() + ")",
|
||||
RemoteLoggingUtil.log("Login failed (" + e.getMessage() + ")",
|
||||
username);
|
||||
// Create new exceptions so we don't leak config info to the client.
|
||||
if (e instanceof FailedLoginException) {
|
||||
throw new FailedLoginException("User authentication failed");
|
||||
}
|
||||
throw new LoginException("User login system failure");
|
||||
throw new FailedLoginException("Authentication failed");
|
||||
}
|
||||
if (authModule instanceof PasswordFileAuthenticationModule) {
|
||||
supportPasswordChange = true;
|
||||
}
|
||||
}
|
||||
else if (!mgr.getUserManager().isValidUser(username)) {
|
||||
FailedLoginException e = new FailedLoginException("Unknown user: " + username);
|
||||
RepositoryManager.log(null, null, "Login failed (" + e.getMessage() + ")",
|
||||
RemoteLoggingUtil.log("Login failed (Unknown user: " + username + ")",
|
||||
username);
|
||||
throw e;
|
||||
// Create new exceptions so we don't leak config info to the client.
|
||||
throw new FailedLoginException("Authentication failed");
|
||||
}
|
||||
}
|
||||
if (anonymousAccess) {
|
||||
RepositoryManager.log(null, null, "Anonymous server access granted", null);
|
||||
RemoteLoggingUtil.log("Anonymous server access granted", null);
|
||||
}
|
||||
|
||||
return new RepositoryServerHandleImpl(username, anonymousAccess, mgr,
|
||||
supportPasswordChange);
|
||||
try {
|
||||
return new RepositoryServerHandleImpl(username, anonymousAccess, mgr,
|
||||
supportPasswordChange);
|
||||
}
|
||||
catch (RemoteException e) {
|
||||
RemoteLoggingUtil.log(
|
||||
"Failed to instantiate RepositoryServerHandleImpl: " + e.getMessage(),
|
||||
username);
|
||||
e.printStackTrace();
|
||||
throw new RemoteException("Remote server handle error (see server log)");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -550,8 +550,9 @@ public class GhidraServer extends UnicastRemoteObject implements GhidraServerHan
|
||||
configuration.setInitializeLogging(false);
|
||||
Application.initializeApplication(layout, configuration);
|
||||
}
|
||||
catch (IOException e) {
|
||||
catch (Throwable t) {
|
||||
System.err.println("Failed to initialize the application!");
|
||||
t.printStackTrace();
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
@@ -729,11 +730,22 @@ public class GhidraServer extends UnicastRemoteObject implements GhidraServerHan
|
||||
File serverLogFile = new File(serverRoot, "server.log");
|
||||
Application.initializeLogging(serverLogFile, serverLogFile);
|
||||
|
||||
log = LogManager.getLogger(GhidraServer.class); // init log *after* initializing log system
|
||||
|
||||
// Establish serialization filter to address deserialization vulnerabity concerns
|
||||
try {
|
||||
ResourceFile serialFilterFile = Application.getModuleDataFile(SERIAL_FILTER_FILE);
|
||||
GhidraObjectInputFilter.configureServerSerialFilter(serialFilterFile,
|
||||
() -> RepositoryManager.getRMIClient());
|
||||
}
|
||||
catch (Throwable t) {
|
||||
log.fatal("Failed to initialize serialization filter", t);
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
// In the absence of module initialization - we must invoke directly
|
||||
DefaultSSLContextInitializer.initialize();
|
||||
|
||||
log = LogManager.getLogger(GhidraServer.class); // init log *after* initializing log system
|
||||
|
||||
ServerPortFactory.setBasePort(basePort);
|
||||
|
||||
Runtime.getRuntime().addShutdownHook(new Thread((Runnable) () -> {
|
||||
@@ -806,7 +818,6 @@ public class GhidraServer extends UnicastRemoteObject implements GhidraServerHan
|
||||
// localhost.getCanonicalHostName() + ":" + classSvrPort + "/";
|
||||
// System.setProperty(RMI_CODEBASE_PROPERTY, codeBaseProp);
|
||||
|
||||
|
||||
log.info(" RMI Registry port: " + ServerPortFactory.getRMIRegistryPort());
|
||||
log.info(" RMI SSL port: " + ServerPortFactory.getRMISSLPort());
|
||||
log.info(" Block Stream port: " + ServerPortFactory.getStreamPort());
|
||||
@@ -865,11 +876,6 @@ public class GhidraServer extends UnicastRemoteObject implements GhidraServerHan
|
||||
log.info("Registered Ghidra Server.");
|
||||
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
log.error(e.getMessage());
|
||||
System.exit(-1);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
log.fatal("Server error: " + t.getMessage(), t);
|
||||
System.exit(-1);
|
||||
@@ -898,130 +904,12 @@ public class GhidraServer extends UnicastRemoteObject implements GhidraServerHan
|
||||
server.dispose();
|
||||
}
|
||||
|
||||
public static RMIServerSocketFactory getRMIServerSocketFactory() {
|
||||
static RMIServerSocketFactory getRMIServerSocketFactory() {
|
||||
return serverSocketFactory;
|
||||
}
|
||||
|
||||
public static RMIClientSocketFactory getRMIClientSocketFactory() {
|
||||
static RMIClientSocketFactory getRMIClientSocketFactory() {
|
||||
return clientSocketFactory;
|
||||
}
|
||||
|
||||
private static void setGlobalSerializationFilter() throws IOException {
|
||||
|
||||
// NOTE: Serialization filter may need to be disabled when profiling with VisualVM
|
||||
String disabledStr = System.getProperty(SERIALIZATION_FILTER_DISABLED_PROPERTY);
|
||||
if (Boolean.valueOf(disabledStr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ObjectInputFilter patternFilter = readSerialFilterPatternFile();
|
||||
|
||||
ObjectInputFilter filter = new ObjectInputFilter() {
|
||||
|
||||
@Override
|
||||
public Status checkInput(FilterInfo info) {
|
||||
|
||||
Class<?> clazz = info.serialClass();
|
||||
|
||||
// Give serial filter patterns first shot
|
||||
Status status = patternFilter.checkInput(info);
|
||||
if (status != Status.UNDECIDED) {
|
||||
if (status == Status.REJECTED) {
|
||||
return serialReject(info, "failed by serial.filter pattern");
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
if (clazz == null) {
|
||||
return Status.ALLOWED;
|
||||
}
|
||||
|
||||
Class<?> componentType = clazz.getComponentType();
|
||||
if (componentType != null && componentType.isPrimitive()) {
|
||||
return Status.ALLOWED; // allow all primitive arrays
|
||||
}
|
||||
|
||||
return serialReject(info, "not allowed");
|
||||
}
|
||||
|
||||
private Status serialReject(FilterInfo info, String reason) {
|
||||
String clientHost = RepositoryManager.getRMIClient();
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("Rejected class serialization");
|
||||
if (clientHost != null) {
|
||||
buf.append(" from ");
|
||||
buf.append(clientHost);
|
||||
}
|
||||
buf.append("(");
|
||||
buf.append(reason);
|
||||
buf.append(")");
|
||||
|
||||
Class<?> serialClass = info.serialClass();
|
||||
if (serialClass != null) {
|
||||
buf.append(": ");
|
||||
buf.append(serialClass.getCanonicalName());
|
||||
buf.append(" ");
|
||||
if (serialClass.getComponentType() != null) {
|
||||
buf.append("(");
|
||||
buf.append("array-length=");
|
||||
buf.append(info.arrayLength());
|
||||
buf.append(")");
|
||||
}
|
||||
}
|
||||
|
||||
log.error(buf.toString());
|
||||
return Status.REJECTED;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Install global serial class filter
|
||||
ObjectInputFilter.Config.setSerialFilter(filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read serial.filter file content removing any comments and newlines and generate
|
||||
* corresponding {@link ObjectInputFilter}. See {@link java.io.ObjectInputFilter.Config#createFilter(String)}
|
||||
* for filter syntax.
|
||||
* @return serial filter content
|
||||
* @throws IOException if file error occurs
|
||||
*/
|
||||
private static ObjectInputFilter readSerialFilterPatternFile() throws IOException {
|
||||
|
||||
File serialFilterFile = Application.getModuleDataFile(SERIAL_FILTER_FILE).getFile(false);
|
||||
if (serialFilterFile == null) {
|
||||
// jar mode not supported
|
||||
throw new FileNotFoundException(SERIAL_FILTER_FILE + " not found");
|
||||
}
|
||||
try {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
try (FileReader fr = new FileReader(serialFilterFile);
|
||||
BufferedReader r = new BufferedReader(fr)) {
|
||||
|
||||
for (String line = r.readLine(); line != null; line = r.readLine()) {
|
||||
int ix = line.indexOf('#');
|
||||
if (ix >= 0) {
|
||||
// strip comment
|
||||
line = line.substring(0, ix);
|
||||
}
|
||||
line = line.trim();
|
||||
if (line.length() == 0) {
|
||||
continue;
|
||||
}
|
||||
if (!line.endsWith(";")) {
|
||||
throw new IllegalArgumentException(
|
||||
"all filter statements must end with `;`");
|
||||
}
|
||||
if (line.length() != 0) {
|
||||
buf.append(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ObjectInputFilter.Config.createFilter(buf.toString());
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new IOException("Failed to parse " + SERIAL_FILTER_FILE, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+76
-36
@@ -4,16 +4,16 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package db.buffers;
|
||||
package ghidra.server.remote;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.rmi.NoSuchObjectException;
|
||||
@@ -22,9 +22,9 @@ import java.rmi.server.UnicastRemoteObject;
|
||||
import java.rmi.server.Unreferenced;
|
||||
import java.util.*;
|
||||
|
||||
import db.buffers.*;
|
||||
import ghidra.framework.remote.RemoteRepositoryHandle;
|
||||
import ghidra.server.RepositoryManager;
|
||||
import ghidra.server.remote.*;
|
||||
import ghidra.server.stream.*;
|
||||
|
||||
/**
|
||||
@@ -44,6 +44,7 @@ public class RemoteBufferFileImpl extends UnicastRemoteObject
|
||||
new HashMap<>();
|
||||
|
||||
protected final RepositoryHandleImpl owner;
|
||||
protected final String user;
|
||||
protected final String associatedFilePath;
|
||||
|
||||
private final String clientHost;
|
||||
@@ -56,7 +57,7 @@ public class RemoteBufferFileImpl extends UnicastRemoteObject
|
||||
* @param bufferFile buffer file
|
||||
* @param owner owner object to which this instance should be associated.
|
||||
* @param associatedFilePath repository path of file item associated with this buffer file
|
||||
* @throws RemoteException
|
||||
* @throws RemoteException if failed to instantiate remote object
|
||||
*/
|
||||
public RemoteBufferFileImpl(LocalBufferFile bufferFile, RepositoryHandleImpl owner,
|
||||
String associatedFilePath) throws RemoteException {
|
||||
@@ -68,6 +69,7 @@ public class RemoteBufferFileImpl extends UnicastRemoteObject
|
||||
if (owner == null || associatedFilePath == null) {
|
||||
throw new IllegalArgumentException("Missing one or more required arguments");
|
||||
}
|
||||
this.user = owner.getUser().getName();
|
||||
this.clientHost = RepositoryManager.getRMIClient();
|
||||
addInstance(this);
|
||||
}
|
||||
@@ -155,7 +157,7 @@ public class RemoteBufferFileImpl extends UnicastRemoteObject
|
||||
}
|
||||
|
||||
/**
|
||||
* Return user name@host associated with open file handle.
|
||||
* {@return username@host associated with open file handle}
|
||||
*/
|
||||
public String getUserClient() {
|
||||
if (clientHost != null) {
|
||||
@@ -166,7 +168,9 @@ public class RemoteBufferFileImpl extends UnicastRemoteObject
|
||||
|
||||
/**
|
||||
* Returns list of users with open handles associated with the specified filePath.
|
||||
* @param filePath file path
|
||||
* @param repoName repository name
|
||||
* @param filePath repository file path
|
||||
* @return users with open file handles
|
||||
*/
|
||||
public static String[] getOpenFileUsers(String repoName, String filePath) {
|
||||
String filePathKey = getFilePathKey(repoName, filePath);
|
||||
@@ -218,63 +222,75 @@ public class RemoteBufferFileImpl extends UnicastRemoteObject
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getParameter(String name) throws NoSuchElementException, IOException {
|
||||
public int getParameter(String name) throws NoSuchElementException {
|
||||
// NOTE: NoSuchElementException will get encapsulated within a RemoteException
|
||||
return bufferFile.getParameter(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParameter(String name, int value) throws IOException {
|
||||
public void setParameter(String name, int value) {
|
||||
bufferFile.setParameter(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearParameters() throws IOException {
|
||||
public void clearParameters() {
|
||||
bufferFile.clearParameters();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getParameterNames() throws IOException {
|
||||
public String[] getParameterNames() {
|
||||
return bufferFile.getParameterNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBufferSize() throws IOException {
|
||||
public int getBufferSize() {
|
||||
return bufferFile.getBufferSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIndexCount() throws IOException {
|
||||
public int getIndexCount() {
|
||||
return bufferFile.getIndexCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getFreeIndexes() throws IOException {
|
||||
public int[] getFreeIndexes() {
|
||||
return bufferFile.getFreeIndexes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFreeIndexes(int[] indexes) throws IOException {
|
||||
public void setFreeIndexes(int[] indexes) {
|
||||
bufferFile.setFreeIndexes(indexes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadOnly() throws IOException {
|
||||
public boolean isReadOnly() {
|
||||
return bufferFile.isReadOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setReadOnly() throws IOException {
|
||||
return bufferFile.setReadOnly();
|
||||
try {
|
||||
return bufferFile.setReadOnly();
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throw RemoteExceptionUtil.dispatchIOException(t, "setReadOnly: " + associatedFilePath,
|
||||
user);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
bufferFile.close();
|
||||
dispose();
|
||||
try {
|
||||
bufferFile.close();
|
||||
dispose();
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throw RemoteExceptionUtil.dispatchIOException(t, "close: " + associatedFilePath, user);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean delete() throws IOException {
|
||||
public boolean delete() {
|
||||
boolean rc = false;
|
||||
try {
|
||||
rc = bufferFile.delete();
|
||||
@@ -287,12 +303,24 @@ public class RemoteBufferFileImpl extends UnicastRemoteObject
|
||||
|
||||
@Override
|
||||
public DataBuffer get(int index) throws IOException {
|
||||
return bufferFile.get(new DataBuffer(), index);
|
||||
try {
|
||||
return bufferFile.get(new DataBuffer(), index);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throw RemoteExceptionUtil.dispatchIOException(t,
|
||||
"get(" + index + "): " + associatedFilePath, user);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(DataBuffer buf, int index) throws IOException {
|
||||
bufferFile.put(buf, index);
|
||||
try {
|
||||
bufferFile.put(buf, index);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throw RemoteExceptionUtil.dispatchIOException(t,
|
||||
"put(" + index + "): " + associatedFilePath, user);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -307,27 +335,39 @@ public class RemoteBufferFileImpl extends UnicastRemoteObject
|
||||
|
||||
@Override
|
||||
public BlockStreamHandle<InputBlockStream> getInputBlockStreamHandle() throws IOException {
|
||||
BlockStreamServer blockStreamServer = BlockStreamServer.getBlockStreamServer();
|
||||
InputBlockStream inputBlockStream = bufferFile.getInputBlockStream();
|
||||
RemoteInputBlockStreamHandle streamHandle =
|
||||
new RemoteInputBlockStreamHandle(blockStreamServer, inputBlockStream);
|
||||
if (!blockStreamServer.registerBlockStream(streamHandle, inputBlockStream)) {
|
||||
throw new IOException("request failed: block stream server not running");
|
||||
try {
|
||||
BlockStreamServer blockStreamServer = BlockStreamServer.getBlockStreamServer();
|
||||
InputBlockStream inputBlockStream = bufferFile.getInputBlockStream();
|
||||
RemoteInputBlockStreamHandle streamHandle =
|
||||
new RemoteInputBlockStreamHandle(blockStreamServer, inputBlockStream);
|
||||
if (!blockStreamServer.registerBlockStream(streamHandle, inputBlockStream)) {
|
||||
throw new IOException("request failed: block stream server not running");
|
||||
}
|
||||
return streamHandle;
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throw RemoteExceptionUtil.dispatchIOException(t,
|
||||
"getInputBlockStreamHandle: " + associatedFilePath, user);
|
||||
}
|
||||
return streamHandle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockStreamHandle<OutputBlockStream> getOutputBlockStreamHandle(int blockCount)
|
||||
throws IOException {
|
||||
BlockStreamServer blockStreamServer = BlockStreamServer.getBlockStreamServer();
|
||||
OutputBlockStream outputBlockStream = bufferFile.getOutputBlockStream(blockCount);
|
||||
RemoteOutputBlockStreamHandle streamHandle = new RemoteOutputBlockStreamHandle(
|
||||
blockStreamServer, blockCount, outputBlockStream.getBlockSize());
|
||||
if (!blockStreamServer.registerBlockStream(streamHandle, outputBlockStream)) {
|
||||
throw new IOException("request failed: block stream server not running");
|
||||
try {
|
||||
BlockStreamServer blockStreamServer = BlockStreamServer.getBlockStreamServer();
|
||||
OutputBlockStream outputBlockStream = bufferFile.getOutputBlockStream(blockCount);
|
||||
RemoteOutputBlockStreamHandle streamHandle = new RemoteOutputBlockStreamHandle(
|
||||
blockStreamServer, blockCount, outputBlockStream.getBlockSize());
|
||||
if (!blockStreamServer.registerBlockStream(streamHandle, outputBlockStream)) {
|
||||
throw new IOException("request failed: block stream server not running");
|
||||
}
|
||||
return streamHandle;
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throw RemoteExceptionUtil.dispatchIOException(t,
|
||||
"getOutputBlockStreamHandle: " + associatedFilePath, user);
|
||||
}
|
||||
return streamHandle;
|
||||
}
|
||||
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.server.remote;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.rmi.RemoteException;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public class RemoteExceptionUtil {
|
||||
|
||||
private static final Logger log = LogManager.getLogger(RemoteExceptionUtil.class);
|
||||
|
||||
/**
|
||||
* Allowed IOExceptions (without cause) that are also defined by
|
||||
* {@code ghidra/Ghidra/Framework/FileSystem/data/client.rmi.serial.filter}.
|
||||
*/
|
||||
private static final Set<Class<? extends IOException>> allowedIOExceptionClassSet = Set.of(
|
||||
java.io.IOException.class,
|
||||
java.io.FileNotFoundException.class,
|
||||
ghidra.framework.store.ExclusiveCheckoutException.class,
|
||||
ghidra.util.exception.UserAccessException.class,
|
||||
ghidra.util.exception.DuplicateFileException.class,
|
||||
ghidra.util.exception.FileInUseException.class,
|
||||
ghidra.util.ReadOnlyException.class);
|
||||
|
||||
/**
|
||||
* Sanitize, log and dispatch exceptions to client and comply with client serialization
|
||||
* requirements. Any IOException with a cause will be simplified to an IOException without
|
||||
* a cause. Any other checked exception, {@link RuntimeException}, {@link Throwable} or
|
||||
* {@link Error} will be logged and produce a simplified {@link RemoteException} without cause.
|
||||
*
|
||||
* @param t original exception/error (expected non-IOExceptions which are explicitly thrown should
|
||||
* be caught and conveyed by called instead of passing to this method).
|
||||
* @param logDetail operation descipription (required)
|
||||
* @param user user if known else null
|
||||
* @return IOException to be thrown
|
||||
*/
|
||||
static IOException dispatchIOException(Throwable t, String logDetail, String user) {
|
||||
return dispatchIOException(t, null, null, logDetail, user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize, log and dispatch exceptions to client and comply with client serialization
|
||||
* requirements. Any IOException with a cause will be simplified to an IOException without
|
||||
* a cause. Any other checked exception, {@link RuntimeException}, {@link Throwable} or
|
||||
* {@link Error} will be logged and produce a simplified {@link RemoteException} without cause.
|
||||
*
|
||||
* @param t original exception/error (expected non-IOExceptions which are explicitly thrown should
|
||||
* be caught and conveyed by called instead of passing to this method).
|
||||
* @param repositoryName repository name or null
|
||||
* @param path repository folder/item path or null
|
||||
* @param logDetail operation descipription (required)
|
||||
* @param user user if known else null
|
||||
* @return IOException to be thrown
|
||||
*/
|
||||
static IOException dispatchIOException(Throwable t, String repositoryName, String path,
|
||||
String logDetail, String user) {
|
||||
|
||||
if (t instanceof RemoteException re) {
|
||||
// Assume this was triggered by a failed remote object instantiation
|
||||
return re;
|
||||
}
|
||||
|
||||
Class<?> excClass = t.getClass();
|
||||
Throwable cause = t.getCause();
|
||||
String excKind;
|
||||
|
||||
if (t instanceof IOException ioe) {
|
||||
|
||||
// Only return allowed IOException class which has no cause
|
||||
if (cause == null && allowedIOExceptionClassSet.contains(excClass)) {
|
||||
return ioe;
|
||||
}
|
||||
|
||||
// Log any IOException which has a cause or is not in the allowed set.
|
||||
// Return as simple IOException without a cause.
|
||||
log.error(excClass.getName() + ": " + t.getMessage(), t);
|
||||
return new IOException(ioe.getMessage());
|
||||
}
|
||||
|
||||
if (t instanceof RuntimeException rte) {
|
||||
excKind = "Runtime Exception";
|
||||
}
|
||||
else if (t instanceof Exception) {
|
||||
// Unexpected condition: exception should have been caught and handled by caller
|
||||
excKind = "Checked Exception";
|
||||
}
|
||||
else {
|
||||
excKind = "Error";
|
||||
}
|
||||
|
||||
// Log all non-IOExceptions and return as a RemoteException without cause.
|
||||
RemoteLoggingUtil.log(repositoryName, path, "ERROR: " + logDetail, user, true);
|
||||
log.error(excKind + ": " + t, t);
|
||||
return new RemoteException("Unexpected Server " + excKind);
|
||||
}
|
||||
|
||||
private RemoteExceptionUtil() {
|
||||
// No instantiation
|
||||
}
|
||||
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.server.remote;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import ghidra.server.RepositoryManager;
|
||||
|
||||
public class RemoteLoggingUtil {
|
||||
|
||||
private static Logger log = LogManager.getLogger(GhidraServer.class);
|
||||
|
||||
/**
|
||||
* Generate log message that contains inforamtion message.
|
||||
*
|
||||
* General format where client host may be omitted if unable to determine:
|
||||
* <pre>
|
||||
* msg (host)
|
||||
* </pre>
|
||||
* @param msg log message (required)
|
||||
* @param user user name or null
|
||||
*/
|
||||
public static void log(String msg) {
|
||||
log(null, null, msg, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate log message that contains information message and user details.
|
||||
*
|
||||
* General format where some portions may be omitted if null:
|
||||
* <pre>
|
||||
* msg (user@host)
|
||||
* </pre>
|
||||
* @param msg log message (required)
|
||||
* @param user user name or null
|
||||
*/
|
||||
public static void log(String msg, String user) {
|
||||
log(null, null, msg, user, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate information or error log message that contains repository, path, message
|
||||
* and user details.
|
||||
*
|
||||
* General format where some portions may be omitted if null:
|
||||
* <pre>
|
||||
* [repositoryName]path: msg (user@host)
|
||||
* </pre>
|
||||
* @param repositoryName repository name or null
|
||||
* @param path repository file path or null
|
||||
* @param msg log message (required)
|
||||
* @param user user name or null
|
||||
* @param error true if error log else info
|
||||
*/
|
||||
public static void log(String repositoryName, String path, String msg, String user,
|
||||
boolean error) {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
if (repositoryName != null) {
|
||||
buf.append("[");
|
||||
buf.append(repositoryName);
|
||||
buf.append("]");
|
||||
}
|
||||
String host = RepositoryManager.getRMIClient();
|
||||
String userStr = user;
|
||||
if (userStr != null) {
|
||||
if (host != null) {
|
||||
userStr += "@" + host;
|
||||
}
|
||||
}
|
||||
else {
|
||||
userStr = host;
|
||||
}
|
||||
if (path != null) {
|
||||
buf.append(path);
|
||||
}
|
||||
if (repositoryName != null || path != null) {
|
||||
buf.append(": ");
|
||||
}
|
||||
buf.append(msg);
|
||||
if (userStr != null) {
|
||||
buf.append(" (");
|
||||
buf.append(userStr);
|
||||
buf.append(")");
|
||||
}
|
||||
if (error) {
|
||||
log.error(buf.toString());
|
||||
}
|
||||
else {
|
||||
log.info(buf.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private RemoteLoggingUtil() {
|
||||
// no instantiation
|
||||
}
|
||||
|
||||
}
|
||||
+66
-28
@@ -4,21 +4,21 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package db.buffers;
|
||||
package ghidra.server.remote;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.rmi.RemoteException;
|
||||
|
||||
import ghidra.server.remote.RepositoryHandleImpl;
|
||||
import db.buffers.*;
|
||||
import ghidra.server.stream.BlockStreamServer;
|
||||
import ghidra.server.stream.RemoteInputBlockStreamHandle;
|
||||
|
||||
@@ -38,7 +38,7 @@ public class RemoteManagedBufferFileImpl extends RemoteBufferFileImpl
|
||||
* @param managedBufferFile underlying managed buffer file
|
||||
* @param owner associated repository handle instance
|
||||
* @param associatedFilePath associated file path for logging
|
||||
* @throws RemoteException
|
||||
* @throws RemoteException if failed to instantiate remote object
|
||||
*/
|
||||
public RemoteManagedBufferFileImpl(LocalManagedBufferFile managedBufferFile,
|
||||
RepositoryHandleImpl owner, String associatedFilePath) throws RemoteException {
|
||||
@@ -48,12 +48,18 @@ public class RemoteManagedBufferFileImpl extends RemoteBufferFileImpl
|
||||
|
||||
@Override
|
||||
public RemoteManagedBufferFileHandle getSaveFile() throws IOException {
|
||||
LocalManagedBufferFile sf = (LocalManagedBufferFile) managedBufferFile.getSaveFile();
|
||||
return sf != null ? new RemoteManagedBufferFileImpl(sf, owner, associatedFilePath) : null;
|
||||
try {
|
||||
LocalManagedBufferFile sf = (LocalManagedBufferFile) managedBufferFile.getSaveFile();
|
||||
return sf != null ? new RemoteManagedBufferFileImpl(sf, owner, associatedFilePath) : null;
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throw RemoteExceptionUtil.dispatchIOException(t, "getSaveFile: " + associatedFilePath,
|
||||
user);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean delete() throws IOException {
|
||||
public boolean delete() {
|
||||
if (managedBufferFile.getVersion() == 1) {
|
||||
owner.getRepository().log(associatedFilePath, "aborting file creation",
|
||||
owner.getUserName());
|
||||
@@ -63,44 +69,69 @@ public class RemoteManagedBufferFileImpl extends RemoteBufferFileImpl
|
||||
|
||||
@Override
|
||||
public void saveCompleted(boolean commit) throws IOException {
|
||||
if (!commit) {
|
||||
int version = managedBufferFile.getVersion();
|
||||
owner.getRepository().log(associatedFilePath,
|
||||
"aborting file version " + version + " creation", owner.getUserName());
|
||||
try {
|
||||
if (!commit) {
|
||||
int version = managedBufferFile.getVersion();
|
||||
owner.getRepository().log(associatedFilePath,
|
||||
"aborting file version " + version + " creation", owner.getUserName());
|
||||
}
|
||||
managedBufferFile.saveCompleted(commit);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throw RemoteExceptionUtil.dispatchIOException(t, "saveCompleted: " + associatedFilePath,
|
||||
user);
|
||||
}
|
||||
managedBufferFile.saveCompleted(commit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canSave() throws IOException {
|
||||
public boolean canSave() {
|
||||
return managedBufferFile.canSave();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVersionComment(String comment) throws IOException {
|
||||
public void setVersionComment(String comment) {
|
||||
managedBufferFile.setVersionComment(comment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoteBufferFileHandle getNextChangeDataFile(boolean getFirst) throws IOException {
|
||||
LocalBufferFile cf = (LocalBufferFile) managedBufferFile.getNextChangeDataFile(getFirst);
|
||||
return cf != null ? new RemoteBufferFileImpl(cf, owner, associatedFilePath) : null;
|
||||
try {
|
||||
LocalBufferFile cf =
|
||||
(LocalBufferFile) managedBufferFile.getNextChangeDataFile(getFirst);
|
||||
return cf != null ? new RemoteBufferFileImpl(cf, owner, associatedFilePath) : null;
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throw RemoteExceptionUtil.dispatchIOException(t,
|
||||
"getNextChangeDataFile: " + associatedFilePath, user);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoteBufferFileHandle getSaveChangeDataFile() throws IOException {
|
||||
LocalBufferFile cf = (LocalBufferFile) managedBufferFile.getSaveChangeDataFile();
|
||||
return cf != null ? new RemoteBufferFileImpl(cf, owner, associatedFilePath) : null;
|
||||
try {
|
||||
LocalBufferFile cf = (LocalBufferFile) managedBufferFile.getSaveChangeDataFile();
|
||||
return cf != null ? new RemoteBufferFileImpl(cf, owner, associatedFilePath) : null;
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throw RemoteExceptionUtil.dispatchIOException(t,
|
||||
"getSaveChangeDataFile: " + associatedFilePath, user);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCheckinID() throws IOException {
|
||||
public long getCheckinID() {
|
||||
return managedBufferFile.getCheckinID();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getForwardModMapData(int oldVersion) throws IOException {
|
||||
return managedBufferFile.getForwardModMapData(oldVersion);
|
||||
try {
|
||||
return managedBufferFile.getForwardModMapData(oldVersion);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throw RemoteExceptionUtil.dispatchIOException(t,
|
||||
"getForwardModMapData: " + associatedFilePath, user);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -111,14 +142,21 @@ public class RemoteManagedBufferFileImpl extends RemoteBufferFileImpl
|
||||
@Override
|
||||
public BlockStreamHandle<InputBlockStream> getInputBlockStreamHandle(byte[] changeMapData)
|
||||
throws IOException {
|
||||
BlockStreamServer blockStreamServer = BlockStreamServer.getBlockStreamServer();
|
||||
InputBlockStream inputBlockStream = managedBufferFile.getInputBlockStream(changeMapData);
|
||||
RemoteInputBlockStreamHandle streamHandle =
|
||||
new RemoteInputBlockStreamHandle(blockStreamServer, inputBlockStream);
|
||||
if (!blockStreamServer.registerBlockStream(streamHandle, inputBlockStream)) {
|
||||
throw new IOException("request failed: block stream server not running");
|
||||
try {
|
||||
BlockStreamServer blockStreamServer = BlockStreamServer.getBlockStreamServer();
|
||||
InputBlockStream inputBlockStream =
|
||||
managedBufferFile.getInputBlockStream(changeMapData);
|
||||
RemoteInputBlockStreamHandle streamHandle =
|
||||
new RemoteInputBlockStreamHandle(blockStreamServer, inputBlockStream);
|
||||
if (!blockStreamServer.registerBlockStream(streamHandle, inputBlockStream)) {
|
||||
throw new IOException("request failed: block stream server not running");
|
||||
}
|
||||
return streamHandle;
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throw RemoteExceptionUtil.dispatchIOException(t,
|
||||
"getInputBlockStream with map: " + associatedFilePath, user);
|
||||
}
|
||||
return streamHandle;
|
||||
}
|
||||
|
||||
}
|
||||
+301
-119
File diff suppressed because it is too large
Load Diff
+56
-65
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -37,27 +37,13 @@ public class RepositoryServerHandleImpl extends UnicastRemoteObject
|
||||
private final boolean supportPasswordChange;
|
||||
private final boolean readOnly;
|
||||
|
||||
/*
|
||||
* @see ghidra.framework.remote.RepositoryServerHandle#anonymousAccessAllowed()
|
||||
*/
|
||||
@Override
|
||||
public boolean anonymousAccessAllowed() {
|
||||
return mgr.anonymousAccessAllowed();
|
||||
}
|
||||
|
||||
/*
|
||||
* @see ghidra.framework.remote.RepositoryServerHandle#isReadOnly()
|
||||
*/
|
||||
@Override
|
||||
public boolean isReadOnly() {
|
||||
return readOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a repository server handle for a specific user.
|
||||
* @param user remote user
|
||||
* @param readOnly true if restricted to read-only use
|
||||
* @param mgr repository manager
|
||||
* @throws RemoteException
|
||||
* @param supportPasswordChange true if password change is allowed
|
||||
* @throws RemoteException if failed to instantiate remote object
|
||||
*/
|
||||
public RepositoryServerHandleImpl(String user, boolean readOnly, RepositoryManager mgr,
|
||||
boolean supportPasswordChange) throws RemoteException {
|
||||
@@ -77,98 +63,103 @@ public class RepositoryServerHandleImpl extends UnicastRemoteObject
|
||||
mgr.dropHandle(this);
|
||||
}
|
||||
|
||||
/*
|
||||
* @see rmitest.RepositoryServerHandle#createRepository(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public RemoteRepositoryHandle createRepository(String name) throws IOException {
|
||||
Repository repository = mgr.createRepository(currentUser, name);
|
||||
return new RepositoryHandleImpl(currentUser, repository);
|
||||
public boolean anonymousAccessAllowed() {
|
||||
return mgr.anonymousAccessAllowed();
|
||||
}
|
||||
|
||||
/*
|
||||
* @see rmitest.RepositoryServerHandle#getRepository(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public RemoteRepositoryHandle getRepository(String name) throws IOException {
|
||||
public boolean isReadOnly() {
|
||||
return readOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoteRepositoryHandle createRepository(String name) throws IOException {
|
||||
try {
|
||||
Repository repository = mgr.createRepository(currentUser, name);
|
||||
RemoteLoggingUtil.log(name, null, "repository created", currentUser, false);
|
||||
return new RepositoryHandleImpl(currentUser, repository);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throw RemoteExceptionUtil.dispatchIOException(t, name,
|
||||
null, "Create repository", currentUser);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoteRepositoryHandle getRepository(String name)
|
||||
throws UserAccessException, IOException {
|
||||
|
||||
System.gc();
|
||||
|
||||
Repository repository = mgr.getRepository(currentUser, name);
|
||||
if (repository == null) {
|
||||
return null;
|
||||
try {
|
||||
Repository repository = mgr.getRepository(currentUser, name);
|
||||
if (repository == null) {
|
||||
return null;
|
||||
}
|
||||
return new RepositoryHandleImpl(currentUser, repository);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throw RemoteExceptionUtil.dispatchIOException(t, name,
|
||||
null, "Get repository", currentUser);
|
||||
}
|
||||
return new RepositoryHandleImpl(currentUser, repository);
|
||||
}
|
||||
|
||||
/*
|
||||
* @see ghidra.framework.remote.RepositoryServerHandle#deleteRepository(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void deleteRepository(String name) throws UserAccessException, IOException {
|
||||
mgr.deleteRepository(currentUser, name);
|
||||
try {
|
||||
mgr.deleteRepository(currentUser, name);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throw RemoteExceptionUtil.dispatchIOException(t, name,
|
||||
null, "Delete repository", currentUser);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @see rmitest.RepositoryServerHandle#getRepositoryNames()
|
||||
*/
|
||||
@Override
|
||||
public String[] getRepositoryNames() {
|
||||
return mgr.getRepositoryNames(currentUser);
|
||||
}
|
||||
|
||||
/*
|
||||
* @see ghidra.framework.remote.RepositoryServerHandle#getUser()
|
||||
*/
|
||||
@Override
|
||||
public String getUser() throws IOException {
|
||||
public String getUser() {
|
||||
return currentUser;
|
||||
}
|
||||
|
||||
/*
|
||||
* @see ghidra.framework.remote.RepositoryServerHandle#getAllUsers()
|
||||
*/
|
||||
@Override
|
||||
public String[] getAllUsers() throws IOException {
|
||||
public String[] getAllUsers() {
|
||||
if (readOnly) {
|
||||
return new String[0];
|
||||
}
|
||||
return mgr.getAllUsers(currentUser);
|
||||
}
|
||||
|
||||
/*
|
||||
* @see ghidra.framework.remote.RepositoryServerHandle#canSetPassword()
|
||||
*/
|
||||
@Override
|
||||
public boolean canSetPassword() throws RemoteException {
|
||||
public boolean canSetPassword() {
|
||||
return supportPasswordChange && mgr.getUserManager().canSetPassword(currentUser);
|
||||
}
|
||||
|
||||
/*
|
||||
* @see ghidra.framework.remote.RepositoryServerHandle#getPasswordExpiration()
|
||||
*/
|
||||
@Override
|
||||
public long getPasswordExpiration() throws IOException {
|
||||
public long getPasswordExpiration() {
|
||||
if (canSetPassword()) {
|
||||
return mgr.getUserManager().getPasswordExpiration(currentUser);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* @see ghidra.framework.remote.RepositoryServerHandle#setPassword(char[])
|
||||
*/
|
||||
@Override
|
||||
public boolean setPassword(char[] saltedSHA256PasswordHash) throws IOException {
|
||||
if (!canSetPassword()) {
|
||||
return false;
|
||||
try {
|
||||
if (!canSetPassword()) {
|
||||
return false;
|
||||
}
|
||||
return mgr.getUserManager().setPassword(currentUser, saltedSHA256PasswordHash, false);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throw RemoteExceptionUtil.dispatchIOException(t, "Set password", currentUser);
|
||||
}
|
||||
return mgr.getUserManager().setPassword(currentUser, saltedSHA256PasswordHash, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* @see ghidra.framework.remote.RepositoryServerHandle#connected()
|
||||
*/
|
||||
@Override
|
||||
public void connected() {
|
||||
// do nothing
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
Ghidra server startup parameters.
|
||||
Command line parameters:
|
||||
[-ip <hostname>] [-i #.#.#.#] [-p#] [-n]
|
||||
[-a#] [-d<ad_domain>] [-e<days>] [-jaas <config_file>] [-u] [-autoProvision] [-anonymous] [-ssh]
|
||||
[-a#] [-d<ad_domain>] [-e<days>] [-jaas <config_file>] [-u] [-autoProvision]
|
||||
[-anonymous] [-ssh]
|
||||
<repository_path>
|
||||
|
||||
|
||||
|
||||
+2
-2
@@ -31,8 +31,8 @@ import org.apache.logging.log4j.*;
|
||||
import ghidra.framework.remote.GhidraPrincipal;
|
||||
import ghidra.framework.remote.SignatureCallback;
|
||||
import ghidra.net.*;
|
||||
import ghidra.server.RepositoryManager;
|
||||
import ghidra.server.UserManager;
|
||||
import ghidra.server.remote.RemoteLoggingUtil;
|
||||
|
||||
/**
|
||||
* <code>PKIAuthenticationModule</code> performs client authentication through the
|
||||
@@ -203,7 +203,7 @@ public class PKIAuthenticationModule implements AuthenticationModule {
|
||||
}
|
||||
|
||||
if (UserManager.ANONYMOUS_USERNAME.equals(username)) {
|
||||
RepositoryManager.log(null, null, "Anonymous access allowed for: " +
|
||||
RemoteLoggingUtil.log("Anonymous access allowed for: " +
|
||||
certChain[0].getSubjectX500Principal().toString(), user.getName());
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -140,10 +140,10 @@ public class SSHAuthenticationModule {
|
||||
* @param subject unauthenticated user ID (must be used if name callback not provided/allowed)
|
||||
* @param callbacks authentication callbacks
|
||||
* @return authenticated user ID (may come from callbacks)
|
||||
* @throws LoginException if authentication failure occurs
|
||||
* @throws FailedLoginException if authentication failure occurs
|
||||
*/
|
||||
public String authenticate(UserManager userMgr, Subject subject, Callback[] callbacks)
|
||||
throws LoginException {
|
||||
throws FailedLoginException {
|
||||
|
||||
GhidraPrincipal user = GhidraPrincipal.getGhidraPrincipal(subject);
|
||||
if (user == null) {
|
||||
|
||||
+7
-7
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -30,7 +30,7 @@ import javax.security.auth.spi.LoginModule;
|
||||
import com.sun.security.auth.UserPrincipal;
|
||||
|
||||
import generic.concurrent.io.ProcessConsumer;
|
||||
import ghidra.server.RepositoryManager;
|
||||
import ghidra.server.remote.RemoteLoggingUtil;
|
||||
import ghidra.util.DateUtils;
|
||||
import ghidra.util.timer.Watchdog;
|
||||
|
||||
@@ -209,11 +209,11 @@ public class ExternalProgramLoginModule implements LoginModule {
|
||||
process.set(p);
|
||||
|
||||
ProcessConsumer.consume(p.getInputStream(), stdOutStr -> {
|
||||
RepositoryManager.log(null, null, extProgramName + " STDOUT: " + stdOutStr, null);
|
||||
RemoteLoggingUtil.log(extProgramName + " STDOUT: " + stdOutStr);
|
||||
});
|
||||
|
||||
ProcessConsumer.consume(p.getErrorStream(), errStr -> {
|
||||
RepositoryManager.log(null, null, extProgramName + " STDERR: " + errStr, null);
|
||||
RemoteLoggingUtil.log(extProgramName + " STDERR: " + errStr);
|
||||
});
|
||||
|
||||
PrintWriter outputWriter = new PrintWriter(p.getOutputStream());
|
||||
@@ -230,8 +230,8 @@ public class ExternalProgramLoginModule implements LoginModule {
|
||||
}
|
||||
}
|
||||
catch (IOException | InterruptedException e) {
|
||||
RepositoryManager.log(null, null,
|
||||
"Exception when executing " + extProgramName + ":" + e.getMessage(), null);
|
||||
RemoteLoggingUtil
|
||||
.log("Exception when executing " + extProgramName + ": " + e.getMessage());
|
||||
throw new LoginException("Error executing external program");
|
||||
}
|
||||
finally {
|
||||
|
||||
@@ -24,7 +24,6 @@ import ghidra.framework.remote.User;
|
||||
import ghidra.framework.store.*;
|
||||
import ghidra.framework.store.local.*;
|
||||
import ghidra.server.Repository;
|
||||
import ghidra.server.RepositoryManager;
|
||||
import ghidra.util.InvalidNameException;
|
||||
import ghidra.util.exception.UserAccessException;
|
||||
|
||||
@@ -79,8 +78,7 @@ public class RepositoryFile {
|
||||
pathname += "/";
|
||||
}
|
||||
pathname += name;
|
||||
RepositoryManager.log(repository.getName(), pathname,
|
||||
"file is corrupt or unsupported", null);
|
||||
repository.log(pathname, "file is corrupt or unsupported", null);
|
||||
throw new FileNotFoundException(pathname + " is corrupt or unsupported");
|
||||
}
|
||||
}
|
||||
@@ -177,8 +175,7 @@ public class RepositoryFile {
|
||||
"Unsupported operation for " + folderItem.getClass().getSimpleName());
|
||||
}
|
||||
LocalManagedBufferFile bf = databaseItem.open(version, minChangeDataVer);
|
||||
repository.log(
|
||||
getPathname(), "version " +
|
||||
repository.log(getPathname(), "version " +
|
||||
(version < 0 ? folderItem.getCurrentVersion() : version) + " opened read-only",
|
||||
user);
|
||||
return bf;
|
||||
@@ -316,7 +313,7 @@ public class RepositoryFile {
|
||||
parent.fileMoved(this, oldName, newParent);
|
||||
parent = newParent;
|
||||
pathChanged();
|
||||
RepositoryManager.log(repository.getName(), oldPath, "file moved to " + getPathname(),
|
||||
repository.log(oldPath, "file moved to " + getPathname(),
|
||||
user);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-6
@@ -27,7 +27,6 @@ import ghidra.framework.store.*;
|
||||
import ghidra.framework.store.local.LocalFileSystem;
|
||||
import ghidra.framework.store.local.LocalFolderItem;
|
||||
import ghidra.server.Repository;
|
||||
import ghidra.server.RepositoryManager;
|
||||
import ghidra.util.InvalidNameException;
|
||||
import ghidra.util.exception.DuplicateFileException;
|
||||
import ghidra.util.exception.FileInUseException;
|
||||
@@ -257,7 +256,7 @@ public class RepositoryFolder {
|
||||
// Folder created notification causes RepositoryFolder instance to be added
|
||||
|
||||
RepositoryFolder rf = getFolder(folderName);
|
||||
RepositoryManager.log(repository.getName(), rf.getPathname(), "folder created", user);
|
||||
repository.log(rf.getPathname(), "folder created", user);
|
||||
return rf;
|
||||
}
|
||||
}
|
||||
@@ -289,7 +288,7 @@ public class RepositoryFolder {
|
||||
RepositoryFile rf = new RepositoryFile(repository, fileSystem, this, itemName);
|
||||
fileMap.put(itemName, rf);
|
||||
|
||||
RepositoryManager.log(repository.getName(), makePathname(getPathname(), itemName),
|
||||
repository.log(makePathname(getPathname(), itemName),
|
||||
"file created", user);
|
||||
}
|
||||
}
|
||||
@@ -320,7 +319,7 @@ public class RepositoryFolder {
|
||||
// Buffer file does not yet exist - too early to get folder item needed for RepositoryFile
|
||||
LocalManagedBufferFile bf = fileSystem.createDatabase(getPathname(), itemName, fileID,
|
||||
contentType, bufferSize, user, projectPath);
|
||||
RepositoryManager.log(repository.getName(), makePathname(getPathname(), itemName),
|
||||
repository.log(makePathname(getPathname(), itemName),
|
||||
"file created", user);
|
||||
return bf;
|
||||
}
|
||||
@@ -434,8 +433,7 @@ public class RepositoryFolder {
|
||||
throw new IOException("Folder can not be renamed and moved");
|
||||
}
|
||||
pathChanged();
|
||||
RepositoryManager.log(repository.getName(), oldPath,
|
||||
"folder moved to " + getPathname(), user);
|
||||
repository.log(oldPath, "folder moved to " + getPathname(), user);
|
||||
}
|
||||
finally {
|
||||
repository.flushChangeEvents();
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -27,7 +27,7 @@ import ghidra.util.Msg;
|
||||
*/
|
||||
public class BufferFileAdapter implements BufferFile {
|
||||
|
||||
private BufferFileHandle bufferFileHandle;
|
||||
private final BufferFileHandle bufferFileHandle;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@@ -39,7 +39,16 @@ public class BufferFileAdapter implements BufferFile {
|
||||
|
||||
@Override
|
||||
public int getParameter(String name) throws NoSuchElementException, IOException {
|
||||
return bufferFileHandle.getParameter(name);
|
||||
try {
|
||||
return bufferFileHandle.getParameter(name);
|
||||
}
|
||||
catch (RemoteException e) {
|
||||
Throwable cause = e.getCause();
|
||||
if (cause instanceof NoSuchElementException nse) {
|
||||
throw nse;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -34,6 +34,7 @@ public interface BufferFileHandle {
|
||||
public boolean setReadOnly() throws IOException;
|
||||
|
||||
/**
|
||||
* NOTE: NoSuchElementException is runtime so must be handled if wrapped in RemoteException
|
||||
* @see BufferFile#getParameter(java.lang.String)
|
||||
*/
|
||||
public int getParameter(String name) throws NoSuchElementException, IOException;
|
||||
|
||||
@@ -274,7 +274,7 @@ public class LocalManagedBufferFile extends LocalBufferFile implements ManagedBu
|
||||
/**
|
||||
* @return version associated with this buffer file
|
||||
*/
|
||||
int getVersion() {
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@@ -284,7 +284,7 @@ public class LocalManagedBufferFile extends LocalBufferFile implements ManagedBu
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVersionComment(String comment) throws IOException {
|
||||
public void setVersionComment(String comment) {
|
||||
this.comment = comment;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -17,54 +17,55 @@ package db.buffers;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.rmi.Remote;
|
||||
import java.rmi.RemoteException;
|
||||
import java.rmi.server.RemoteObjectInvocationHandler;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* <code>RemoteBufferFileHandle</code> facilitates access to a remote BufferFile
|
||||
* via RMI.
|
||||
* <p>
|
||||
* Methods from {@link BufferFileHandle} <b>must</b> be re-declared here
|
||||
* IMPORTANT: Methods from {@link BufferFileHandle} <b>must</b> be re-declared here
|
||||
* so they may be properly marshalled for remote invocation via RMI.
|
||||
* This became neccessary with an OpenJDK 11.0.6 change made to
|
||||
* {@link RemoteObjectInvocationHandler}.
|
||||
*/
|
||||
public interface RemoteBufferFileHandle extends BufferFileHandle, Remote {
|
||||
@Override
|
||||
public boolean isReadOnly() throws IOException;
|
||||
public boolean isReadOnly() throws RemoteException;
|
||||
|
||||
@Override
|
||||
public boolean setReadOnly() throws IOException;
|
||||
|
||||
// NoSuchElementException will get wrapped within RemoteException
|
||||
@Override
|
||||
public int getParameter(String name) throws NoSuchElementException, IOException;
|
||||
public int getParameter(String name) throws RemoteException;
|
||||
|
||||
@Override
|
||||
public void setParameter(String name, int value) throws IOException;
|
||||
public void setParameter(String name, int value) throws RemoteException;
|
||||
|
||||
@Override
|
||||
public void clearParameters() throws IOException;
|
||||
public void clearParameters() throws RemoteException;
|
||||
|
||||
@Override
|
||||
public String[] getParameterNames() throws IOException;
|
||||
public String[] getParameterNames() throws RemoteException;
|
||||
|
||||
@Override
|
||||
public int getBufferSize() throws IOException;
|
||||
public int getBufferSize() throws RemoteException;
|
||||
|
||||
@Override
|
||||
public int getIndexCount() throws IOException;
|
||||
public int getIndexCount() throws RemoteException;
|
||||
|
||||
@Override
|
||||
public int[] getFreeIndexes() throws IOException;
|
||||
public int[] getFreeIndexes() throws RemoteException;
|
||||
|
||||
@Override
|
||||
public void setFreeIndexes(int[] indexes) throws IOException;
|
||||
public void setFreeIndexes(int[] indexes) throws RemoteException;
|
||||
|
||||
@Override
|
||||
public void close() throws IOException;
|
||||
|
||||
@Override
|
||||
public boolean delete() throws IOException;
|
||||
public boolean delete() throws RemoteException;
|
||||
|
||||
@Override
|
||||
public DataBuffer get(int index) throws IOException;
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -17,14 +17,14 @@ package db.buffers;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.rmi.Remote;
|
||||
import java.rmi.RemoteException;
|
||||
import java.rmi.server.RemoteObjectInvocationHandler;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* <code>RemoteManagedBufferFileHandle</code> facilitates access to a ManagedBufferFile
|
||||
* via RMI.
|
||||
* <p>
|
||||
* Methods from {@link BufferFileHandle} and {@link ManagedBufferFile} <b>must</b>
|
||||
* IMPORTANT: Methods from {@link BufferFileHandle} and {@link ManagedBufferFile} <b>must</b>
|
||||
* be re-declared here so they may be properly marshalled for remote invocation via RMI.
|
||||
* This became neccessary with an OpenJDK 11.0.6 change made to
|
||||
* {@link RemoteObjectInvocationHandler}.
|
||||
@@ -35,40 +35,41 @@ public interface RemoteManagedBufferFileHandle extends ManagedBufferFileHandle,
|
||||
// BufferFileHandle methods
|
||||
//--------------------------------------------------------------------------
|
||||
@Override
|
||||
public boolean isReadOnly() throws IOException;
|
||||
public boolean isReadOnly() throws RemoteException;
|
||||
|
||||
@Override
|
||||
public boolean setReadOnly() throws IOException;
|
||||
|
||||
// NoSuchElementException will get wrapped within RemoteException
|
||||
@Override
|
||||
public int getParameter(String name) throws NoSuchElementException, IOException;
|
||||
public int getParameter(String name) throws RemoteException;
|
||||
|
||||
@Override
|
||||
public void setParameter(String name, int value) throws IOException;
|
||||
public void setParameter(String name, int value) throws RemoteException;
|
||||
|
||||
@Override
|
||||
public void clearParameters() throws IOException;
|
||||
public void clearParameters() throws RemoteException;
|
||||
|
||||
@Override
|
||||
public String[] getParameterNames() throws IOException;
|
||||
public String[] getParameterNames() throws RemoteException;
|
||||
|
||||
@Override
|
||||
public int getBufferSize() throws IOException;
|
||||
public int getBufferSize() throws RemoteException;
|
||||
|
||||
@Override
|
||||
public int getIndexCount() throws IOException;
|
||||
public int getIndexCount() throws RemoteException;
|
||||
|
||||
@Override
|
||||
public int[] getFreeIndexes() throws IOException;
|
||||
public int[] getFreeIndexes() throws RemoteException;
|
||||
|
||||
@Override
|
||||
public void setFreeIndexes(int[] indexes) throws IOException;
|
||||
public void setFreeIndexes(int[] indexes) throws RemoteException;
|
||||
|
||||
@Override
|
||||
public void close() throws IOException;
|
||||
|
||||
@Override
|
||||
public boolean delete() throws IOException;
|
||||
public boolean delete() throws RemoteException;
|
||||
|
||||
@Override
|
||||
public DataBuffer get(int index) throws IOException;
|
||||
@@ -103,10 +104,10 @@ public interface RemoteManagedBufferFileHandle extends ManagedBufferFileHandle,
|
||||
public void saveCompleted(boolean commit) throws IOException;
|
||||
|
||||
@Override
|
||||
public boolean canSave() throws IOException;
|
||||
public boolean canSave() throws RemoteException;
|
||||
|
||||
@Override
|
||||
public void setVersionComment(String comment) throws IOException;
|
||||
public void setVersionComment(String comment) throws RemoteException;
|
||||
|
||||
@Override
|
||||
public BufferFileHandle getNextChangeDataFile(boolean getFirst) throws IOException;
|
||||
@@ -115,7 +116,7 @@ public interface RemoteManagedBufferFileHandle extends ManagedBufferFileHandle,
|
||||
public BufferFileHandle getSaveChangeDataFile() throws IOException;
|
||||
|
||||
@Override
|
||||
public long getCheckinID() throws IOException;
|
||||
public long getCheckinID() throws RemoteException;
|
||||
|
||||
@Override
|
||||
public byte[] getForwardModMapData(int oldVersion) throws IOException;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
##VERSION: 2.0
|
||||
Module.manifest||GHIDRA||||END|
|
||||
README.md||GHIDRA||||END|
|
||||
data/client.rmi.serial.filter||GHIDRA||||END|
|
||||
data/serialFilterREADME.md||GHIDRA||||END|
|
||||
src/main/java/ghidra/framework/client/package.html||GHIDRA||reviewed||END|
|
||||
src/main/java/ghidra/framework/store/db/package.html||GHIDRA||reviewed||END|
|
||||
src/main/java/ghidra/framework/store/local/package.html||GHIDRA||reviewed||END|
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
# Ghidra Server RMI client deserialization filter patterns.
|
||||
# See GhidraObjectInputFilter javadoc.
|
||||
|
||||
remoteIf=ghidra.framework.remote.GhidraServerHandle;
|
||||
remoteIf=ghidra.framework.remote.RemoteRepositoryServerHandle;
|
||||
remoteIf=ghidra.framework.remote.RemoteRepositoryHandle;
|
||||
remoteIf=db.buffers.RemoteBufferFileHandle;
|
||||
remoteIf=db.buffers.RemoteManagedBufferFileHandle;
|
||||
|
||||
ghidra.framework.remote.*;
|
||||
ghidra.framework.store.*;
|
||||
ghidra.server.stream.*;
|
||||
|
||||
[Lghidra.framework.remote.RepositoryChangeEvent;
|
||||
[Lghidra.framework.remote.RepositoryItem;
|
||||
[Lghidra.framework.store.ItemCheckoutStatus;
|
||||
[Lghidra.framework.store.Version;
|
||||
|
||||
db.buffers.*;
|
||||
|
||||
java.rmi.Remote;
|
||||
java.rmi.dgc.Lease;
|
||||
java.rmi.dgc.VMID;
|
||||
java.rmi.server.ObjID;
|
||||
java.rmi.server.RemoteObject;
|
||||
java.rmi.server.RemoteObjectInvocationHandler;
|
||||
java.rmi.server.UID;
|
||||
|
||||
# RMI exception serialized from server to client
|
||||
java.rmi.AccessException;
|
||||
java.rmi.MarshalException;
|
||||
java.rmi.NoSuchObjectException;
|
||||
java.rmi.NotBoundException;
|
||||
java.rmi.RemoteException;
|
||||
java.rmi.ServerException;
|
||||
java.rmi.ServerError;
|
||||
java.rmi.ServerRuntimeException;
|
||||
java.rmi.server.SocketSecurityException;
|
||||
java.rmi.UnexpectedException;
|
||||
java.rmi.UnmarshalException;
|
||||
|
||||
javax.rmi.ssl.SslRMIClientSocketFactory;
|
||||
|
||||
java.lang.reflect.Proxy;
|
||||
|
||||
java.lang.String;
|
||||
[Ljava.lang.String;
|
||||
|
||||
java.util.Collections$EmptyList;
|
||||
|
||||
javax.security.auth.callback.NameCallback;
|
||||
javax.security.auth.callback.PasswordCallback;
|
||||
|
||||
javax.security.auth.x500.X500Principal;
|
||||
|
||||
javax.security.auth.callback.Callback;
|
||||
[Ljavax.security.auth.callback.Callback;
|
||||
javax.security.auth.x500.X500Principal;
|
||||
[Ljavax.security.auth.x500.X500Principal;
|
||||
|
||||
#
|
||||
# Exceptions thrown by Ghidra Server remote objects
|
||||
# (see above for various RMI remote exception implementations)
|
||||
#
|
||||
|
||||
java.lang.StackTraceElement;
|
||||
[Ljava.lang.StackTraceElement; # Throwable stackTrace array field
|
||||
|
||||
java.lang.Throwable;
|
||||
java.lang.Error;
|
||||
java.lang.Exception;
|
||||
java.lang.RuntimeException;
|
||||
|
||||
java.security.GeneralSecurityException;
|
||||
|
||||
# Exceptions explicitly thrown by Ghidra Server remote objects
|
||||
# as declared by corresponding Remote interface or encapsulated
|
||||
# by a java.rmi.RemoteException.
|
||||
|
||||
javax.security.auth.login.FailedLoginException;
|
||||
javax.security.auth.login.LoginException;
|
||||
|
||||
java.lang.UnsupportedOperationException;
|
||||
|
||||
java.util.NoSuchElementException;
|
||||
|
||||
ghidra.util.exception.DuplicateNameException;
|
||||
ghidra.util.exception.UsrException;
|
||||
ghidra.util.InvalidNameException;
|
||||
|
||||
#
|
||||
# IOExceptions
|
||||
#
|
||||
# NOTE: Any exception thrown on server with a cause will be logged on server and conveyed back
|
||||
# to client as a simple IOException without a cause.
|
||||
#
|
||||
# NOTE: All IOExceptions added below must be included within RemoteExceptionUtil.allowedIOExceptionClassSet
|
||||
#
|
||||
|
||||
java.io.IOException;
|
||||
java.io.FileNotFoundException;
|
||||
|
||||
ghidra.framework.store.ExclusiveCheckoutException;
|
||||
ghidra.util.exception.UserAccessException;
|
||||
ghidra.util.exception.DuplicateFileException;
|
||||
ghidra.util.exception.FileInUseException;
|
||||
ghidra.util.ReadOnlyException;
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
# Ghidra Serialization Filter
|
||||
|
||||
## Overview
|
||||
As of version 12.0.5, Ghidra employs serialization input filters to address concerns about potential
|
||||
serialization vulnerabilities in relation to the use of Java RMI (e.g., Ghidra Server). Filters
|
||||
are employed by both Ghidra Server and client applications. The consequqnce of this filtering is
|
||||
that all Java Object deserialization is subject to the filter even when it corresponds to purely
|
||||
local functionality. This can occur with certain code that relies on serialization to facilitate
|
||||
object cloning (e.g., `org.apache.commons.collections4.functors.PrototypeFactory`). When such cases occur
|
||||
it may be neccessary to add allowed classes to a client-side serial input filter.
|
||||
|
||||
The Ghidra application discovers serial input filter specifications (`*.serial.filter`) files within
|
||||
each Ghidra module's data directory (e.g., `Ghidra/Framework/FileSystem/data`) at startup. The
|
||||
combined filter set is used to establish a global input serialization filter for Ghidra.
|
||||
|
||||
When adding functionality to Ghidra it may be neccessary to adjust the defined serial filter
|
||||
specifications. When the filter rejects a class deserialization an `InvalidClassException` will be
|
||||
thrown and the rejected class name will be logged. The log will need to be consulted since the
|
||||
exception itself does not convey the name of the offending class.
|
||||
|
||||
## Reference Information
|
||||
- [Java Serialization Filtering](https://docs.oracle.com/javase/8/docs/technotes/guides/serialization/filters/serialization-filtering.html)
|
||||
|
||||
## Serial Input Filter Format
|
||||
By default, the filter implementation will allow all primitive types (e.g., `int`, `char`, etc.)
|
||||
and primitive arrays. Filter files need to specify all other Java classes that should allow
|
||||
deserialization. It is important to remember that all filter specifications will be combined into
|
||||
a single global filter.
|
||||
|
||||
**IMPORTANT:** Although supported by Java's serial input filter specification, Ghidra does not
|
||||
support the class rejection pattern starting with the `!` prefix. This restriction stems from
|
||||
Ghidra combining all serial filters into a single unordered filter specification.
|
||||
|
||||
The serial input filters (`*.serial.filter`) support the following entry types where each entry
|
||||
must end with a semicolon `;`. End of line comments may be specified with a leading `#` character.
|
||||
|
||||
- Allowed class name. A class is specified by its full classname including package path:
|
||||
|
||||
```
|
||||
java.lang.String;
|
||||
```
|
||||
- Allowed inner class, anonymous class, or compiler‑generated synthetic class:
|
||||
|
||||
```
|
||||
ghidra.myplugin.Foo$MyInnerClass;
|
||||
|
||||
```
|
||||
- Allowed class array (single dimension). Uses a `[L` prefix before the full classname.
|
||||
NOTE: Anytime an array is allowed the base class must also be allowed.
|
||||
|
||||
```
|
||||
[Ljava.lang.String;
|
||||
````
|
||||
- Allowed class array (two dimension). Uses a `[[L` prefix before the full classname.
|
||||
NOTE: Anytime an array is allowed the base class must also be allowed.
|
||||
|
||||
```
|
||||
[[Ljava.lang.Integer;
|
||||
````
|
||||
- Wildcard class name specification can be used very carefully. The `*` wildcard only spans a single
|
||||
package, while the `**` wildcard will include all subpackages. Do not allow "Gadget classes" that
|
||||
can be exploited.
|
||||
|
||||
```
|
||||
ghidra.myplugin.*;
|
||||
[Lghidra.myplugin.*;
|
||||
ghidra.my*;
|
||||
[Lghidra.my*;
|
||||
```
|
||||
- Allowed remote interface that employ a dynamic Proxy classes (e.g., Java RMI Remote interface).
|
||||
|
||||
```
|
||||
remoteIf=ghidra.remote.MyRemoteIf;
|
||||
```
|
||||
- Maximum number of array elements (default: `32000`). The maximum specified by any filter will be
|
||||
used. A specified value will be ignore if less than the default.
|
||||
|
||||
```
|
||||
maxarray=200000;
|
||||
```
|
||||
- Maximum number of bytes in a serialization stream (default: `33554432` / 32MB). The maximum
|
||||
specified by any filter will be used. A specified value will be ignore if less than the default.
|
||||
|
||||
```
|
||||
maxbytes=100000000;
|
||||
```
|
||||
- Maximum references in a graph between objects (default: `10000`). The maximum specified by any
|
||||
filter will be used. A specified value will be ignore if less than the default.
|
||||
|
||||
```
|
||||
maxrefs=15000;
|
||||
```
|
||||
- Maximum depth of an object graph. (default: `50`). The maximum specified by any filter will be used.
|
||||
A specified value will be ignore if less than the default.
|
||||
|
||||
```
|
||||
maxdepth=75;
|
||||
```
|
||||
**NOTE:** Default values shown above may be adjusted in the future. Please report any filter failures
|
||||
associated with standard Ghidra features.
|
||||
@@ -31,7 +31,8 @@ import ghidra.framework.model.ServerInfo;
|
||||
import ghidra.framework.remote.*;
|
||||
import ghidra.framework.remote.security.SSHKeyManager;
|
||||
import ghidra.net.*;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.SystemUtilities;
|
||||
import ghidra.util.exception.*;
|
||||
import ghidra.util.task.TaskLauncher;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
@@ -207,6 +208,9 @@ public class ClientUtil {
|
||||
Msg.showError(ClientUtil.class, parent, title,
|
||||
"Access denied: " + repository + "\n" + exc.getMessage());
|
||||
}
|
||||
// FIXME: Verify presence and source of ServerException and ServerError which
|
||||
// both originate from UnicastServerRef.dispatch method and are both forms
|
||||
// of RemoteException
|
||||
else if ((exc instanceof ServerException) || (exc instanceof ServerError)) {
|
||||
Msg.showError(ClientUtil.class, parent, title,
|
||||
"Exception occurred on the Ghidra Server.", exc.getCause());
|
||||
|
||||
+3
@@ -576,6 +576,9 @@ public class RepositoryAdapter implements RemoteAdapterListener {
|
||||
if (t instanceof UnmarshalException) {
|
||||
throw new UnsupportedOperationException(operation);
|
||||
}
|
||||
if (t instanceof UnsupportedOperationException uoe) {
|
||||
throw uoe;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+542
File diff suppressed because it is too large
Load Diff
+93
@@ -0,0 +1,93 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.framework.remote;
|
||||
|
||||
import java.io.ObjectInputFilter;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.BinaryOperator;
|
||||
|
||||
/**
|
||||
* {@link GhidraSerialFilterFactory} provides the serial filter factory which imposes
|
||||
* {@link GhidraObjectInputFilter} as a global serial input filter.
|
||||
* <p>
|
||||
* NOTE: With the use of Gradle test JVM instances it may be neccessary for those instances
|
||||
* to specify this class as the serial filter factory and rely on lazy initialization of the
|
||||
* {@link GhidraObjectInputFilter global serial filter}.
|
||||
* <pre>
|
||||
* -Djdk.serialFilterFactory=ghidra.framework.remote.GhidraSerialFilterFactory
|
||||
* </pre>
|
||||
*/
|
||||
public class GhidraSerialFilterFactory implements BinaryOperator<ObjectInputFilter> {
|
||||
|
||||
private static final AtomicReference<GhidraSerialFilterFactory> filterFactoryRef =
|
||||
new AtomicReference<>();
|
||||
|
||||
private final GhidraObjectInputFilter globalFilter;
|
||||
|
||||
/**
|
||||
* Constructor. Caller is reposponsible for installation.
|
||||
* See {@link ObjectInputFilter.Config#setSerialFilterFactory}.
|
||||
*/
|
||||
public GhidraSerialFilterFactory() {
|
||||
if (!filterFactoryRef.compareAndSet(null, this)) {
|
||||
throw new IllegalStateException(
|
||||
"Serial filter factory has previously been instantiated");
|
||||
}
|
||||
globalFilter = new GhidraObjectInputFilter();
|
||||
}
|
||||
|
||||
GhidraObjectInputFilter getSerialFilter() {
|
||||
return globalFilter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectInputFilter apply(ObjectInputFilter current, ObjectInputFilter requested) {
|
||||
// Merge any existing/requested filter with our strict global filter.
|
||||
// Our filter is always applied.
|
||||
if (current == null && requested == null) {
|
||||
return globalFilter;
|
||||
}
|
||||
if (current == null) {
|
||||
return ObjectInputFilter.merge(requested, globalFilter);
|
||||
}
|
||||
if (requested == null) {
|
||||
return ObjectInputFilter.merge(current, globalFilter);
|
||||
}
|
||||
return ObjectInputFilter.merge(ObjectInputFilter.merge(current, requested),
|
||||
globalFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get, and install if neccessary, the serial filter factory instance. If a new factory is
|
||||
* installed it will have an uninitialized {@link GhidraObjectInputFilter} instance.
|
||||
* <p>
|
||||
* See {@link java.io.ObjectInputFilter.Config#setSerialFilterFactory(java.util.function.BinaryOperator)}.
|
||||
*
|
||||
* @return serial filter factory singleton instance
|
||||
* @throws IllegalStateException if the serial input factory has already been established and
|
||||
* cannot be updated.
|
||||
*/
|
||||
static synchronized GhidraSerialFilterFactory getOrInstallInstance()
|
||||
throws IllegalStateException {
|
||||
GhidraSerialFilterFactory factory = filterFactoryRef.get();
|
||||
if (factory != null) {
|
||||
return factory;
|
||||
}
|
||||
GhidraSerialFilterFactory newFactory = new GhidraSerialFilterFactory();
|
||||
ObjectInputFilter.Config.setSerialFilterFactory(newFactory);
|
||||
return newFactory;
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -20,7 +20,7 @@ import java.rmi.RemoteException;
|
||||
|
||||
import javax.security.auth.Subject;
|
||||
import javax.security.auth.callback.Callback;
|
||||
import javax.security.auth.login.LoginException;
|
||||
import javax.security.auth.login.FailedLoginException;
|
||||
|
||||
/**
|
||||
* <code>GhidraServerHandle</code> provides access to a remote server.
|
||||
@@ -51,7 +51,7 @@ public interface GhidraServerHandle extends Remote {
|
||||
* older clients the ability to connect to the server. Remote interface remained
|
||||
* unchanged allowing 9.1 clients to connect to 9.0 server.
|
||||
* 12: Revised RepositoryFile serialization to facilitate support for text-data used
|
||||
* for link-file storage.
|
||||
* for link-file storage (12.0).
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -99,17 +99,17 @@ public interface GhidraServerHandle extends Remote {
|
||||
* @param authCallbacks valid authentication callback objects which have been satisfied, or
|
||||
* null if server does not require authentication.
|
||||
* @return repository server handle.
|
||||
* @throws LoginException if user authentication fails
|
||||
* @throws RemoteException
|
||||
* @throws FailedLoginException if user authentication fails
|
||||
* @throws RemoteException failed to create remote handle
|
||||
* @see #getAuthenticationCallbacks()
|
||||
*/
|
||||
RemoteRepositoryServerHandle getRepositoryServer(Subject user, Callback[] authCallbacks)
|
||||
throws LoginException, RemoteException;
|
||||
throws FailedLoginException, RemoteException;
|
||||
|
||||
/**
|
||||
* Check server interface compatibility
|
||||
* @param serverInterfaceVersion client/server interface version
|
||||
* @throws RemoteException
|
||||
* @throws RemoteException if requested server interface version not available
|
||||
* @see #INTERFACE_VERSION
|
||||
*/
|
||||
void checkCompatibility(int serverInterfaceVersion) throws RemoteException;
|
||||
|
||||
+4
-3
@@ -17,6 +17,7 @@ package ghidra.framework.remote;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.rmi.Remote;
|
||||
import java.rmi.RemoteException;
|
||||
import java.rmi.server.RemoteObjectInvocationHandler;
|
||||
|
||||
import db.buffers.ManagedBufferFileHandle;
|
||||
@@ -33,10 +34,10 @@ import ghidra.util.InvalidNameException;
|
||||
*/
|
||||
public interface RemoteRepositoryHandle extends RepositoryHandle, Remote {
|
||||
@Override
|
||||
String getName() throws IOException;
|
||||
String getName() throws RemoteException;
|
||||
|
||||
@Override
|
||||
User getUser() throws IOException;
|
||||
User getUser() throws RemoteException;
|
||||
|
||||
@Override
|
||||
User[] getUserList() throws IOException;
|
||||
@@ -45,7 +46,7 @@ public interface RemoteRepositoryHandle extends RepositoryHandle, Remote {
|
||||
boolean anonymousAccessAllowed() throws IOException;
|
||||
|
||||
@Override
|
||||
String[] getServerUserList() throws IOException;
|
||||
String[] getServerUserList() throws RemoteException;
|
||||
|
||||
@Override
|
||||
void setUserList(User[] users, boolean anonymousAccessAllowed) throws IOException;
|
||||
|
||||
+12
-11
@@ -4,9 +4,9 @@
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -17,12 +17,13 @@ package ghidra.framework.remote;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.rmi.Remote;
|
||||
import java.rmi.RemoteException;
|
||||
import java.rmi.server.RemoteObjectInvocationHandler;
|
||||
|
||||
/**
|
||||
* <code>RepositoryServerHandle</code> provides access to a remote repository server via RMI.
|
||||
* <p>
|
||||
* Methods from {@link RepositoryServerHandle} <b>must</b> be re-declared here
|
||||
* IMPORTANT: Methods from {@link RepositoryServerHandle} <b>must</b> be re-declared here
|
||||
* so they may be properly marshalled for remote invocation via RMI.
|
||||
* This became neccessary with an OpenJDK 11.0.6 change made to
|
||||
* {@link RemoteObjectInvocationHandler}.
|
||||
@@ -30,10 +31,10 @@ import java.rmi.server.RemoteObjectInvocationHandler;
|
||||
public interface RemoteRepositoryServerHandle extends RepositoryServerHandle, Remote {
|
||||
|
||||
@Override
|
||||
boolean anonymousAccessAllowed() throws IOException;
|
||||
boolean anonymousAccessAllowed() throws RemoteException;
|
||||
|
||||
@Override
|
||||
boolean isReadOnly() throws IOException;
|
||||
boolean isReadOnly() throws RemoteException;
|
||||
|
||||
@Override
|
||||
RepositoryHandle createRepository(String name) throws IOException;
|
||||
@@ -45,24 +46,24 @@ public interface RemoteRepositoryServerHandle extends RepositoryServerHandle, Re
|
||||
void deleteRepository(String name) throws IOException;
|
||||
|
||||
@Override
|
||||
String[] getRepositoryNames() throws IOException;
|
||||
String[] getRepositoryNames() throws RemoteException;
|
||||
|
||||
@Override
|
||||
String getUser() throws IOException;
|
||||
String getUser() throws RemoteException;
|
||||
|
||||
@Override
|
||||
String[] getAllUsers() throws IOException;
|
||||
String[] getAllUsers() throws RemoteException;
|
||||
|
||||
@Override
|
||||
boolean canSetPassword() throws IOException;
|
||||
boolean canSetPassword() throws RemoteException;
|
||||
|
||||
@Override
|
||||
long getPasswordExpiration() throws IOException;
|
||||
long getPasswordExpiration() throws RemoteException;
|
||||
|
||||
@Override
|
||||
boolean setPassword(char[] saltedSHA256PasswordHash) throws IOException;
|
||||
|
||||
@Override
|
||||
void connected() throws IOException;
|
||||
void connected() throws RemoteException;
|
||||
|
||||
}
|
||||
|
||||
+2
-38
@@ -1,13 +1,12 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -109,39 +108,4 @@ public class SignatureCallback implements Callback, Serializable {
|
||||
return null;
|
||||
}
|
||||
|
||||
// private void writeObject(java.io.ObjectOutputStream out) throws IOException {
|
||||
//
|
||||
// out.defaultWriteObject();
|
||||
//
|
||||
// try {
|
||||
// out.writeInt(certChain == null ? -1 : certChain.length);
|
||||
// if (certChain != null) {
|
||||
// for (int i = 0; i < certChain.length; i++) {
|
||||
// out.writeObject(certChain[i].getEncoded());
|
||||
// }
|
||||
// }
|
||||
// } catch (CertificateEncodingException e) {
|
||||
// throw new IOException("Can not serialize certificate chain");
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
//
|
||||
// in.defaultReadObject();
|
||||
//
|
||||
// try {
|
||||
// int cnt = in.readInt();
|
||||
// if (cnt >= 0) {
|
||||
// CertificateFactory cf = CertificateFactory.getInstance("X509");
|
||||
// certChain = new X509Certificate[cnt];
|
||||
// for (int i = 0; i < cnt; i++) {
|
||||
// byte[] bytes = (byte[]) in.readObject();
|
||||
// certChain[i] = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(bytes));
|
||||
// }
|
||||
// }
|
||||
// } catch (CertificateException e) {
|
||||
// throw new IOException("Can not de-serialize certificate chain");
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
+30
-16
@@ -23,6 +23,7 @@ import org.jdom2.input.SAXBuilder;
|
||||
import org.jdom2.output.XMLOutputter;
|
||||
|
||||
import ghidra.framework.store.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.xml.GenericXMLOutputter;
|
||||
import ghidra.util.xml.XmlUtilities;
|
||||
|
||||
@@ -47,7 +48,7 @@ class CheckoutManager {
|
||||
* @param item folder item
|
||||
* @param create if true an empty checkout data file is written, else the
|
||||
* initial data is read from the file.
|
||||
* @throws IOException
|
||||
* @throws IOException if create is true and checkouts data file creation failed
|
||||
*/
|
||||
CheckoutManager(LocalFolderItem item, boolean create) throws IOException {
|
||||
this.item = item;
|
||||
@@ -70,6 +71,7 @@ class CheckoutManager {
|
||||
* @param checkoutType type of checkout
|
||||
* @param user name of user requesting checkout
|
||||
* @param version item version to be checked-out
|
||||
* @param projectPath client-side project and path
|
||||
* @return checkout data or null if exclusive checkout denied due to
|
||||
* existing checkouts.
|
||||
* @throws IOException if checkout fails
|
||||
@@ -109,6 +111,7 @@ class CheckoutManager {
|
||||
*
|
||||
* @param checkoutId checkout ID to be updated
|
||||
* @param version item version to be associated with checkout
|
||||
* @throws IOException if item validation fails
|
||||
*/
|
||||
synchronized void updateCheckout(long checkoutId, int version) throws IOException {
|
||||
validate();
|
||||
@@ -133,7 +136,7 @@ class CheckoutManager {
|
||||
* Terminate the specified checkout
|
||||
*
|
||||
* @param checkoutId checkout ID
|
||||
* @throws IOException
|
||||
* @throws IOException @throws IOException if item validation fails or data update fails
|
||||
*/
|
||||
synchronized void endCheckout(long checkoutId) throws IOException {
|
||||
validate();
|
||||
@@ -156,10 +159,11 @@ class CheckoutManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the specified version of the associated item is
|
||||
* checked-out.
|
||||
* {@return true if the specified version of the associated item has
|
||||
* one or more checkedouts}
|
||||
*
|
||||
* @param version the specific version to check for checkouts.
|
||||
* @throws IOException if item validation fails
|
||||
*/
|
||||
synchronized boolean isCheckedOut(int version) throws IOException {
|
||||
validate();
|
||||
@@ -173,7 +177,8 @@ class CheckoutManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the any version of the associated item is checked-out.
|
||||
* {@return true if one or more checkouts exist for the associated item}
|
||||
* @throws IOException if item validation fails
|
||||
*/
|
||||
synchronized boolean isCheckedOut() throws IOException {
|
||||
validate();
|
||||
@@ -181,10 +186,11 @@ class CheckoutManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the checkout data corresponding to the specified checkout ID.
|
||||
* Null is returned if checkout ID is not found.
|
||||
* {@return the checkout data corresponding to the specified checkout ID.
|
||||
* Null is returned if checkout ID is not found.}
|
||||
*
|
||||
* @param checkoutId checkout ID
|
||||
* @throws IOException if item validation fails
|
||||
*/
|
||||
synchronized ItemCheckoutStatus getCheckout(long checkoutId) throws IOException {
|
||||
validate();
|
||||
@@ -192,8 +198,10 @@ class CheckoutManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the checkout data for all existing checkouts of the associated
|
||||
* item.
|
||||
* {@return the checkout data for all existing checkouts of the associated
|
||||
* item.}
|
||||
*
|
||||
* @throws IOException if item validation fails
|
||||
*/
|
||||
synchronized ItemCheckoutStatus[] getAllCheckouts() throws IOException {
|
||||
validate();
|
||||
@@ -207,6 +215,8 @@ class CheckoutManager {
|
||||
* updated, the checkout data will be re-initialized from the file. This is
|
||||
* undesirable and is only required when multiple instances of a
|
||||
* LocalFolderItem are used for a specific item path (e.g., unit testing).
|
||||
*
|
||||
* @throws IOException if failed to read checkouts file
|
||||
*/
|
||||
private void validate() throws IOException {
|
||||
if (LocalFileSystem.isRefreshRequired()) {
|
||||
@@ -219,6 +229,11 @@ class CheckoutManager {
|
||||
readCheckoutsFile();
|
||||
success = true;
|
||||
}
|
||||
catch (IOException e) {
|
||||
String msg = "Item validation failed: " + item.getPathName();
|
||||
Msg.error(this, msg, e);
|
||||
throw new IOException(msg);
|
||||
}
|
||||
finally {
|
||||
if (!success) {
|
||||
nextCheckoutId = oldNextCheckoutId;
|
||||
@@ -231,9 +246,8 @@ class CheckoutManager {
|
||||
/**
|
||||
* Read data from checkout file.
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws IOException if reading checkout data fails
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void readCheckoutsFile() throws IOException {
|
||||
|
||||
checkouts = new HashMap<>();
|
||||
@@ -266,7 +280,7 @@ class CheckoutManager {
|
||||
}
|
||||
}
|
||||
catch (org.jdom2.JDOMException je) {
|
||||
throw new InvalidObjectException("Invalid checkouts file: " + checkoutsFile);
|
||||
throw new IOException("Invalid checkouts file: " + checkoutsFile, je);
|
||||
}
|
||||
finally {
|
||||
istream.close();
|
||||
@@ -278,7 +292,7 @@ class CheckoutManager {
|
||||
*
|
||||
* @param coElement checkout data element
|
||||
* @return checkout data for specified element
|
||||
* @throws JDOMException
|
||||
* @throws JDOMException if checkout data parse fails
|
||||
*/
|
||||
ItemCheckoutStatus parseCheckoutElement(Element coElement) throws JDOMException {
|
||||
try {
|
||||
@@ -302,7 +316,7 @@ class CheckoutManager {
|
||||
/**
|
||||
* Write checkout data file.
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws IOException if error writing checkout data file occurs
|
||||
*/
|
||||
private void writeCheckoutsFile() throws IOException {
|
||||
|
||||
@@ -341,14 +355,14 @@ class CheckoutManager {
|
||||
oldFile = new File(checkoutsFile.getParentFile(), checkoutsFile.getName() + ".bak");
|
||||
oldFile.delete();
|
||||
if (!checkoutsFile.renameTo(oldFile)) {
|
||||
throw new IOException("Failed to update checkouts: " + item.getPathName());
|
||||
throw new IOException("Failed to update checkout file: " + checkoutsFile);
|
||||
}
|
||||
}
|
||||
if (!tmpFile.renameTo(checkoutsFile)) {
|
||||
if (oldFile != null) {
|
||||
oldFile.renameTo(checkoutsFile);
|
||||
}
|
||||
throw new IOException("Failed to update checkouts: " + item.getPathName());
|
||||
throw new IOException("Failed to update checkout file: " + checkoutsFile);
|
||||
}
|
||||
if (oldFile != null) {
|
||||
oldFile.delete();
|
||||
|
||||
+19
-19
@@ -21,7 +21,6 @@ import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import ghidra.framework.store.*;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.ReadOnlyException;
|
||||
import ghidra.util.exception.*;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
@@ -155,7 +154,7 @@ public abstract class LocalFolderItem implements FolderItem {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns hidden data directory.
|
||||
* {@return data storage directory}
|
||||
* NOTE: Even if a data directory is not required this method will still return one to
|
||||
* allow removal of an unknown item type that may or may not use it.
|
||||
*/
|
||||
@@ -170,7 +169,7 @@ public abstract class LocalFolderItem implements FolderItem {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the oldest/minimum version.
|
||||
* {@return the oldest/minimum version}
|
||||
* @throws IOException thrown if an IO error occurs.
|
||||
*/
|
||||
abstract int getMinimumVersion() throws IOException;
|
||||
@@ -178,7 +177,7 @@ public abstract class LocalFolderItem implements FolderItem {
|
||||
/**
|
||||
* Verify that the specified version of this item is not in use.
|
||||
* @param version the specific version to check for versioned items.
|
||||
* @throws FileInUseException
|
||||
* @throws FileInUseException if specified item version is in use or unable to determine
|
||||
*/
|
||||
void checkInUse(int version) throws FileInUseException {
|
||||
synchronized (fileSystem) {
|
||||
@@ -188,7 +187,7 @@ public abstract class LocalFolderItem implements FolderItem {
|
||||
isCheckedOut = checkoutMgr.isCheckedOut(version);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new FileInUseException(getName() + " versioning error", e);
|
||||
throw new FileInUseException(getName() + " versioning error");
|
||||
}
|
||||
if (isCheckedOut) {
|
||||
throw new FileInUseException(
|
||||
@@ -203,7 +202,7 @@ public abstract class LocalFolderItem implements FolderItem {
|
||||
|
||||
/**
|
||||
* Verify that this item is not in use.
|
||||
* @throws FileInUseException
|
||||
* @throws FileInUseException if item is in use or unable to determine
|
||||
*/
|
||||
void checkInUse() throws FileInUseException {
|
||||
synchronized (fileSystem) {
|
||||
@@ -216,7 +215,8 @@ public abstract class LocalFolderItem implements FolderItem {
|
||||
isCheckedOut = checkoutMgr.isCheckedOut();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new FileInUseException(getName() + " versioning error", e);
|
||||
// A bad checkouts file can cause this (check log)
|
||||
throw new FileInUseException(getName() + " versioning error");
|
||||
}
|
||||
if (isCheckedOut) {
|
||||
throw new FileInUseException(getName() + " is checked out");
|
||||
@@ -231,7 +231,7 @@ public abstract class LocalFolderItem implements FolderItem {
|
||||
/**
|
||||
* Begin the check-in process for a versioned item.
|
||||
* @param checkoutId assigned at time of checkout, becomes the check-in ID.
|
||||
* @throws FileInUseException
|
||||
* @throws FileInUseException if specified item version is in use or unable to determine
|
||||
*/
|
||||
void beginCheckin(long checkoutId) throws FileInUseException {
|
||||
synchronized (fileSystem) {
|
||||
@@ -244,7 +244,7 @@ public abstract class LocalFolderItem implements FolderItem {
|
||||
status = checkoutMgr.getCheckout(checkinId);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new FileInUseException(getName() + " versioning error", e);
|
||||
throw new FileInUseException(getName() + " versioning error");
|
||||
}
|
||||
String byMsg = status != null ? (" by: " + status.getUser()) : "";
|
||||
throw new FileInUseException("Another checkin is in progress" + byMsg);
|
||||
@@ -401,7 +401,7 @@ public abstract class LocalFolderItem implements FolderItem {
|
||||
* never be the only version (i.e., minVersion will always be less
|
||||
* than the currentVersion).
|
||||
* @param user user name
|
||||
* @throws IOException
|
||||
* @throws IOException if item update failure occurs
|
||||
*/
|
||||
abstract void deleteMinimumVersion(String user) throws IOException;
|
||||
|
||||
@@ -411,7 +411,7 @@ public abstract class LocalFolderItem implements FolderItem {
|
||||
* never be the only version (i.e., minVersion will always be less
|
||||
* than the currentVersion).
|
||||
* @param user user name
|
||||
* @throws IOException
|
||||
* @throws IOException if item update failure occurs
|
||||
*/
|
||||
abstract void deleteCurrentVersion(String user) throws IOException;
|
||||
|
||||
@@ -419,10 +419,11 @@ public abstract class LocalFolderItem implements FolderItem {
|
||||
* Move this item into a newFolder which has a path of newPath.
|
||||
* @param newFolder new parent directory/folder
|
||||
* @param newStorageName new storage name
|
||||
* @param newPath new parent path
|
||||
* @throws DuplicateFileException
|
||||
* @throws FileInUseException
|
||||
* @throws IOException
|
||||
* @param newFolderPath new parent path
|
||||
* @param newName new item name
|
||||
* @throws DuplicateFileException if detsination item already exists
|
||||
* @throws FileInUseException if items appears to be in use
|
||||
* @throws IOException if item update failure occurs
|
||||
* @see ghidra.framework.store.FileSystem#moveItem
|
||||
*/
|
||||
void moveTo(File newFolder, String newStorageName, String newFolderPath, String newName)
|
||||
@@ -831,8 +832,7 @@ public abstract class LocalFolderItem implements FolderItem {
|
||||
return checkoutMgr != null && checkoutMgr.isCheckedOut();
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.error(getName() + " versioning error", e);
|
||||
return true;
|
||||
return true; // error already logged
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -879,8 +879,8 @@ public abstract class LocalFolderItem implements FolderItem {
|
||||
* Update this non-versioned item with the contents of the specified item which must be
|
||||
* within the same non-versioned fileSystem. If successful, the specified item will be
|
||||
* removed after its content has been moved into this item.
|
||||
* @param item
|
||||
* @param checkoutVersion
|
||||
* @param item source item for update of this item
|
||||
* @param checkoutVersion version of current checkout
|
||||
* @throws IOException if this file is not a checked-out non-versioned file
|
||||
* or an IO error occurs.
|
||||
*/
|
||||
|
||||
+8
-3
@@ -1,13 +1,12 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -18,6 +17,12 @@ package ghidra.framework.store.local;
|
||||
|
||||
public interface RepositoryLogger {
|
||||
|
||||
/**
|
||||
* Append log with information related to specified folder/item path.
|
||||
* @param path folder or item path
|
||||
* @param msg message
|
||||
* @param user associated user or null
|
||||
*/
|
||||
void log(String path, String msg, String user);
|
||||
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ public class ApplicationKeyManagerFactory {
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new KeyStoreException("Failed to examine keystore: " + keystorePath, e);
|
||||
throw new KeyStoreException("Failed to open keystore: " + keystorePath, e);
|
||||
}
|
||||
|
||||
int tryCount = 0;
|
||||
|
||||
+2
-12
@@ -1,13 +1,12 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@@ -34,13 +33,4 @@ public class FileInUseException extends IOException {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new FileInUseException with the given message and cause.
|
||||
*
|
||||
* @param msg the exception message.
|
||||
*/
|
||||
public FileInUseException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -87,17 +87,15 @@ wrapper.java.additional.11=-Ddb.buffers.DataBuffer.compressedOutput=true
|
||||
# timeouts to their maximum values.
|
||||
#wrapper.java.debug.port=18200
|
||||
|
||||
# Uncomment to allow VisualVM Profiling to avoid "Rejected class serialization..." errors
|
||||
# NOTE: A Java class serialization filter is added for RMI security assurance and should remain
|
||||
# enabled during normal use.
|
||||
#wrapper.java.additional.16=-Dghidra.server.serialization.filter.disabled=true
|
||||
|
||||
# Uncomment to enable remote use of VisualVM for profiling
|
||||
# See JMX documentation for more information: http://docs.oracle.com/javase/8/docs/technotes/guides/management/agent.html
|
||||
#wrapper.java.additional.17=-Dcom.sun.management.jmxremote.port=9010
|
||||
#wrapper.java.additional.18=-Dcom.sun.management.jmxremote.local.only=false
|
||||
#wrapper.java.additional.19=-Dcom.sun.management.jmxremote.authenticate=false
|
||||
#wrapper.java.additional.20=-Dcom.sun.management.jmxremote.ssl=false
|
||||
# Uncomment additional java properties below to enable remote use of VisualVM for profiling.
|
||||
# See JMX documentation for more information:
|
||||
# http://docs.oracle.com/javase/8/docs/technotes/guides/management/agent.html
|
||||
# When JMX over RMI is in use the Ghidra Server serialization filters defined by
|
||||
# file Ghidra/GhidraServer/data/serial.filter may need adjustment.
|
||||
#wrapper.java.additional.16=-Dcom.sun.management.jmxremote.port=9010
|
||||
#wrapper.java.additional.17=-Dcom.sun.management.jmxremote.local.only=false
|
||||
#wrapper.java.additional.18=-Dcom.sun.management.jmxremote.authenticate=false
|
||||
#wrapper.java.additional.19=-Dcom.sun.management.jmxremote.ssl=false
|
||||
|
||||
# YAJSW will by default assume a POSIX spawn for Linux and Mac OS X systems, unfortunately it has
|
||||
# not yet been implemented for Mac OS X. The default process support within YAJSW for Mac OS X is
|
||||
|
||||
+3
-5
@@ -19,7 +19,6 @@ import static org.junit.Assert.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InvalidClassException;
|
||||
import java.rmi.RemoteException;
|
||||
import java.rmi.UnmarshalException;
|
||||
import java.security.Principal;
|
||||
import java.util.HashSet;
|
||||
@@ -117,11 +116,10 @@ public class GhidraServerSerialFilterFailureTest extends AbstractGhidraHeadlessI
|
||||
serverHandle.getRepositoryServer(getBogusUserSubject(), new Callback[0]);
|
||||
fail("serial filter rejection failed to perform");
|
||||
}
|
||||
catch (RemoteException e) {
|
||||
catch (Exception e) {
|
||||
Throwable cause = e.getCause();
|
||||
assertTrue("expected remote unmarshall exception", cause instanceof UnmarshalException);
|
||||
cause = cause.getCause();
|
||||
assertTrue("expected remote invalid class exceptionn",
|
||||
assertTrue("Expected remote unmarshall exception", e instanceof UnmarshalException);
|
||||
assertTrue("Expected remote invalid class exceptionn",
|
||||
cause instanceof InvalidClassException);
|
||||
}
|
||||
|
||||
|
||||
-2
@@ -528,8 +528,6 @@ public class ServerTestUtil {
|
||||
Thread.sleep(200);
|
||||
if (isServerRegistered(portFactory.getRMIRegistryPort()) &&
|
||||
canConnect(portFactory.getRMISSLPort())) {
|
||||
Msg.info(ServerTestUtil.class,
|
||||
"Successfully verified Ghidra Server registration and SSL port availability");
|
||||
success = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -339,6 +339,33 @@ def getCurrentDateTimeLong() {
|
||||
return formattedDate
|
||||
}
|
||||
|
||||
/*********************************************************************************
|
||||
* Returns true if 'project' has a direct or transitive API project dependency
|
||||
* on the project with path 'targetPath'. The 'targetPath' should be specified
|
||||
* in the form ":<project-name>"
|
||||
*********************************************************************************/
|
||||
boolean hasApiProjectDependency(Project project, String targetPath) {
|
||||
def visited = [] as Set
|
||||
|
||||
def walk
|
||||
walk = { Project p ->
|
||||
if (!visited.add(p)) return false
|
||||
|
||||
def apiDeps = p.configurations.api
|
||||
.allDependencies
|
||||
.withType(org.gradle.api.artifacts.ProjectDependency)
|
||||
|
||||
if (apiDeps.any { it.dependencyProject.path == targetPath }) {
|
||||
return true
|
||||
}
|
||||
|
||||
return apiDeps.any { dep -> walk(dep.dependencyProject) }
|
||||
}
|
||||
|
||||
walk(project)
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************************************
|
||||
* Returns a list of all the external library paths declared as dependencies for the
|
||||
* given project
|
||||
|
||||
@@ -148,8 +148,10 @@ def initTestJVM(Task task, String rootDirName) {
|
||||
task.minHeapSize = xms
|
||||
task.maxHeapSize = xmx
|
||||
|
||||
task.doFirst {
|
||||
task.jvmArgs '-DupgradeProgramErrorMessage=' + upgradeProgramErrorMessage,
|
||||
task.doFirst {
|
||||
|
||||
def args = [
|
||||
'-DupgradeProgramErrorMessage=' + upgradeProgramErrorMessage,
|
||||
'-DupgradeTimeErrorMessage=' + upgradeTimeErrorMessage,
|
||||
'-Dlog4j.configurationFile=' + logPropertiesUrl,
|
||||
'-Dghidra.test.property.batch.mode=true',
|
||||
@@ -171,7 +173,10 @@ def initTestJVM(Task task, String rootDirName) {
|
||||
'-Duser.language=en',
|
||||
'-Djdk.attach.allowAttachSelf',
|
||||
'-XX:TieredStopAtLevel=1',
|
||||
'-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=' + debugPort,
|
||||
|
||||
// Note: this following arg is needed to enable remote debug.
|
||||
|
||||
//'-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:' + debugPort,
|
||||
|
||||
// Allow illegal reflective accesses
|
||||
'--add-opens=java.base/java.util.concurrent=ALL-UNNAMED',
|
||||
@@ -181,19 +186,9 @@ def initTestJVM(Task task, String rootDirName) {
|
||||
'--add-opens=java.desktop/javax.swing=ALL-UNNAMED',
|
||||
'--add-opens=java.desktop/javax.swing.text=ALL-UNNAMED'
|
||||
|
||||
// Note: this args are used to speed-up the tests, but are not safe for production code
|
||||
// Note: these args are used to speed-up the tests, but are not safe for production code
|
||||
// -noverify and -XX:TieredStopAtLevel=1
|
||||
|
||||
// Note: modern remote debug invocation;
|
||||
// -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000
|
||||
|
||||
//
|
||||
// We removed these lines, which should not be needed in modern JVMs
|
||||
// -Xdebug
|
||||
// -Xnoagent
|
||||
// -Djava.compiler=NONE
|
||||
// -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000
|
||||
|
||||
//
|
||||
// TODO Future Updates:
|
||||
// The test configuration should be updated to support all known modes of operation:
|
||||
@@ -203,6 +198,17 @@ def initTestJVM(Task task, String rootDirName) {
|
||||
// For better command-line usage we will need to update tests such that they can
|
||||
// share a VM, enabling us to elimnate the use of 'forEver 1' in this file.
|
||||
//
|
||||
]
|
||||
|
||||
if (hasApiProjectDependency(project, ":FileSystem")) {
|
||||
// If project has a FileSystem dependency we must assume there may be a
|
||||
// Ghidra ApplicationConfiguration that will attempt to configure a
|
||||
// serial filter factory which must be done in a lazy fashion when gradle
|
||||
// test workers are used.
|
||||
args << '-Djdk.serialFilterFactory=ghidra.framework.remote.GhidraSerialFilterFactory'
|
||||
}
|
||||
|
||||
task.jvmArgs args
|
||||
}
|
||||
}
|
||||
/*********************************************************************************
|
||||
|
||||
+20
-15
@@ -310,8 +310,10 @@ def initTestJVM(Task task, String rootDirName) {
|
||||
task.minHeapSize xms
|
||||
task.maxHeapSize xmx
|
||||
|
||||
task.doFirst {
|
||||
task.jvmArgs '-DupgradeProgramErrorMessage=' + upgradeProgramErrorMessage,
|
||||
task.doFirst {
|
||||
|
||||
def args = [
|
||||
'-DupgradeProgramErrorMessage=' + upgradeProgramErrorMessage,
|
||||
'-DupgradeTimeErrorMessage=' + upgradeTimeErrorMessage,
|
||||
'-Dlog4j.configurationFile=' + logPropertiesUrl,
|
||||
'-Dghidra.test.property.batch.mode=true',
|
||||
@@ -332,8 +334,10 @@ def initTestJVM(Task task, String rootDirName) {
|
||||
'-Duser.language=en',
|
||||
'-Djdk.attach.allowAttachSelf',
|
||||
'-XX:TieredStopAtLevel=1',
|
||||
'-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=' + debugPort,
|
||||
|
||||
|
||||
// Note: this following arg is needed to enable remote debug.
|
||||
//'-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:' + debugPort,
|
||||
|
||||
// Allow illegal reflective accesses
|
||||
'--add-opens=java.base/java.util.concurrent=ALL-UNNAMED',
|
||||
'--add-opens=java.desktop/sun.awt=ALL-UNNAMED',
|
||||
@@ -342,18 +346,19 @@ def initTestJVM(Task task, String rootDirName) {
|
||||
'--add-opens=java.desktop/javax.swing=ALL-UNNAMED',
|
||||
'--add-opens=java.desktop/javax.swing.text=ALL-UNNAMED'
|
||||
|
||||
// Note: this args are used to speed-up the tests, but are not safe for production code
|
||||
// -noverify and -XX:TieredStopAtLevel=1
|
||||
// Note: these args are used to speed-up the tests, but are not safe for production code
|
||||
// -noverify and -XX:TieredStopAtLevel=1
|
||||
]
|
||||
|
||||
if (hasApiProjectDependency(project, ":FileSystem")) {
|
||||
// If project has a FileSystem dependency we must assume there may be a
|
||||
// Ghidra ApplicationConfiguration that will attempt to configure a
|
||||
// serial filter factory which must be done in a lazy fashion when gradle
|
||||
// test workers are used.
|
||||
args << '-Djdk.serialFilterFactory=ghidra.framework.remote.GhidraSerialFilterFactory'
|
||||
}
|
||||
|
||||
// Note: modern remote debug invocation;
|
||||
// -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000
|
||||
|
||||
//
|
||||
// We removed these lines, which should not be needed in modern JVMs
|
||||
// -Xdebug
|
||||
// -Xnoagent
|
||||
// -Djava.compiler=NONE
|
||||
// -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000
|
||||
task.jvmArgs args
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user