Merge remote-tracking branch 'origin/patch'

This commit is contained in:
Ryan Kurtz
2026-06-12 05:05:33 -04:00
4 changed files with 161 additions and 41 deletions

View File

@@ -440,7 +440,12 @@ class GhidraScriptActionManager {
}
ZipEntry entry = entries.nextElement();
monitor.setMessage("Extracting " + entry.getName() + "...");
String name = entry.getName();
File destination = new File(versionedExtractDir, name);
if (!FileUtilities.isPathContainedWithin(versionedExtractDir, destination)) {
throw new IOException("Zip entry escapes target directory: " + name);
}
monitor.setMessage("Extracting " + name + "...");
writeZipEntry(versionedExtractDir, entry, zipFileObject.getInputStream(entry));
monitor.incrementProgress(1);
}

View File

@@ -25,8 +25,10 @@ import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.*;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.rmi.ssl.SslRMIClientSocketFactory;
import javax.rmi.ssl.SslRMIServerSocketFactory;
import javax.security.auth.Subject;
@@ -36,16 +38,17 @@ import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException;
import javax.security.auth.x500.X500Principal;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bouncycastle.asn1.x509.GeneralName;
import generic.jar.ResourceFile;
import generic.random.SecureRandomFactory;
import ghidra.framework.Application;
import ghidra.framework.ApplicationConfiguration;
import ghidra.framework.remote.*;
import ghidra.net.DefaultKeyManagerFactory;
import ghidra.net.DefaultSSLContextInitializer;
import ghidra.net.*;
import ghidra.server.RepositoryManager;
import ghidra.server.UserManager;
import ghidra.server.security.*;
@@ -473,7 +476,7 @@ public class GhidraServer extends UnicastRemoteObject implements GhidraServerHan
private static String initRemoteAccessHostname() throws UnknownHostException {
String hostname = System.getProperty(RMI_SERVER_PROPERTY);
if (hostname == null) {
if (StringUtils.isBlank(hostname)) {
if (bindAddress != null) {
hostname = bindAddress.getHostAddress();
}
@@ -482,7 +485,7 @@ public class GhidraServer extends UnicastRemoteObject implements GhidraServerHan
if (localhost.isLoopbackAddress()) {
localhost = findHost();
if (localhost == null) {
log.fatal("Can't find host ip address!");
log.fatal("Failed to identify host interface IP address");
System.exit(-1);
}
}
@@ -719,7 +722,7 @@ public class GhidraServer extends UnicastRemoteObject implements GhidraServerHan
}
}
if (rootPath == null) {
if (StringUtils.isBlank(rootPath)) {
displayUsage("Repository directory must be specified!");
System.exit(-1);
}
@@ -802,49 +805,28 @@ public class GhidraServer extends UnicastRemoteObject implements GhidraServerHan
}
String preferredKeyStore = DefaultKeyManagerFactory.getPreferredKeyStore();
if (preferredKeyStore == null) {
if (StringUtils.isBlank(preferredKeyStore)) {
// keystore has not been identified - use self-signed certificate
log.info(" Generating self-signed certificate...");
log.info(" Subject Alternative Names:");
log.info(" " + hostname);
DefaultKeyManagerFactory.setDefaultIdentity(new X500Principal("CN=GhidraServer"));
DefaultKeyManagerFactory.addSubjectAlternativeName(hostname);
// Collect alternate hostnames for inclusion in certificate
Enumeration<NetworkInterface> nets = NetworkInterface.getNetworkInterfaces();
while (nets.hasMoreElements()) {
NetworkInterface netint = nets.nextElement();
Enumeration<InetAddress> addrs = netint.getInetAddresses();
while (addrs.hasMoreElements()) {
InetAddress addr = addrs.nextElement();
altNames.add(addr.getHostAddress());
altNames.add(addr.getHostName());
altNames.add(addr.getCanonicalHostName());
}
}
altNames.remove(hostname);
for (String name : altNames) {
log.info(" " + name);
DefaultKeyManagerFactory.addSubjectAlternativeName(name);
}
initSelfSignedCertificateData(hostname, altNames);
}
else {
log.info(" Using server certificate keystore: " + preferredKeyStore);
if (!altNames.isEmpty()) {
log.warn(" -ipAlt use ignored with installed server certificate");
}
}
if (!DefaultKeyManagerFactory.initialize()) {
log.fatal("Failed to initialize PKI/SSL keystore");
System.exit(0);
return;
}
// RMIClassServer.startServer(classSvrPort);
// String codeBaseProp = "http://" +
// localhost.getCanonicalHostName() + ":" + classSvrPort + "/";
// System.setProperty(RMI_CODEBASE_PROPERTY, codeBaseProp);
int signingKeyCount = logServerCertificates("RSA") + logServerCertificates("ECDSA");
if (signingKeyCount == 0) {
log.fatal("Failed to locate a certificate with digital-signature usage");
System.exit(0);
}
log.info(" RMI Registry port: " + ServerPortFactory.getRMIRegistryPort());
log.info(" RMI SSL port: " + ServerPortFactory.getRMISSLPort());
@@ -852,7 +834,6 @@ public class GhidraServer extends UnicastRemoteObject implements GhidraServerHan
log.info(" Block Stream compression: " +
(RemoteBlockStreamHandle.enableCompressedSerializationOutput ? "enabled"
: "disabled"));
// log.info(" Class server port: " + ??);
log.info(" Root: " + serverRoot.getAbsolutePath());
log.info(" Auth: " + authMode.getDescription());
if (authMode == PASSWORD_FILE_LOGIN && defaultPasswordExpiration >= 0) {
@@ -920,6 +901,105 @@ public class GhidraServer extends UnicastRemoteObject implements GhidraServerHan
System.exit(-1);
}
}
private static void initSelfSignedCertificateData(String preferredHostname,
Set<String> altNames) throws SocketException {
DefaultKeyManagerFactory.setDefaultIdentity(new X500Principal("CN=GhidraServer"));
DefaultKeyManagerFactory.addSubjectAlternativeName(preferredHostname);
// Collect alternate hostnames for inclusion in certificate
Enumeration<NetworkInterface> nets = NetworkInterface.getNetworkInterfaces();
while (nets.hasMoreElements()) {
NetworkInterface netint = nets.nextElement();
Enumeration<InetAddress> addrs = netint.getInetAddresses();
while (addrs.hasMoreElements()) {
InetAddress addr = addrs.nextElement();
altNames.add(addr.getHostAddress());
altNames.add(addr.getHostName());
altNames.add(addr.getCanonicalHostName());
}
}
altNames.remove(preferredHostname); // already added as first entry
for (String name : altNames) {
DefaultKeyManagerFactory.addSubjectAlternativeName(name);
}
}
/**
* Log server certiifcates
* @param keyType
* @return number of certificates that support signing
*/
private static int logServerCertificates(String keyType) {
X509ExtendedKeyManager km = DefaultKeyManagerFactory.getKeyManager();
String[] aliases = km.getServerAliases(keyType, null);
if (aliases == null) {
return 0;
}
String pad = " ";
Date now = new Date();
int signingCount = 0;
for (String alias : aliases) {
X509Certificate[] certificateChain = km.getCertificateChain(alias);
X509Certificate x509Cert = certificateChain[certificateChain.length - 1];
if (x509Cert.getKeyUsage()[0]) {
++signingCount;
}
X500Principal subj = x509Cert.getSubjectX500Principal();
X500Principal issuer = x509Cert.getIssuerX500Principal();
String label = "'" + alias + "' (" + keyType + "): ";
if (now.compareTo(x509Cert.getNotAfter()) > 0) {
log.error(
pad + label + subj + ", issued by " + issuer +
", S/N " + x509Cert.getSerialNumber().toString(16) + ", expired " +
x509Cert.getNotAfter() + " **EXPIRED**");
}
else {
log.info(
pad + label + subj + ", issued by " + issuer +
", S/N " + x509Cert.getSerialNumber().toString(16) + ", expires " +
x509Cert.getNotAfter());
}
log.info(pad + "Key Usage: " + PKIUtils.formatKeyUsage(x509Cert));
boolean foundEntries = false;
try {
Collection<List<?>> sanList = x509Cert.getSubjectAlternativeNames();
if (sanList != null) {
for (List<?> sanEntry : sanList) {
Integer type = (Integer) sanEntry.get(0);
Object value = sanEntry.get(1);
if (type == GeneralName.iPAddress || type == GeneralName.dNSName) {
if (!foundEntries) {
log.info(pad + "Subject Alternative Names:");
}
foundEntries = true;
log.info(pad + " " + value);
}
}
}
}
catch (Exception e) {
log.fatal("Error reading certificate SANs: " + e.getMessage());
System.exit(-1);
}
// Generally a server signing cert needs SAN entries
if (x509Cert.getKeyUsage()[0] && !foundEntries) {
log.warn(pad + "** No Hostname or IP Address SANs are defined **");
}
}
return signingCount;
}
private static String[] getEnabledTlsProtocols() {
String protocolList = System.getProperty(TLS_SERVER_PROTOCOLS_PROPERTY);

View File

@@ -385,7 +385,7 @@ public class DefaultKeyManagerFactory {
private synchronized boolean init(String newKeystorePath) throws CancelledException {
if (wrappedKeyManager != null) {
if (StringUtils.equals(keystorePath, newKeystorePath)) {
if (Objects.equals(keystorePath, newKeystorePath)) {
return true;
}
invalidateKey();
@@ -393,7 +393,7 @@ public class DefaultKeyManagerFactory {
isSelfSigned = false;
try {
if (newKeystorePath != null && newKeystorePath.length() != 0) {
if (!StringUtils.isBlank(newKeystorePath)) {
Msg.info(DefaultKeyManagerFactory.class,
"Using certificate keystore: " + newKeystorePath);
// Password optionally specified via property
@@ -411,9 +411,12 @@ public class DefaultKeyManagerFactory {
defaultSubjectAlternativeNames, pwd);
wrappedKeyManager = ApplicationKeyManagerFactory
.getKeyManagerFromKeyStore(selfSignedKeyStore, pwd);
keystorePath = null;
isSelfSigned = true;
}
else {
Msg.error(this,
"Failed to generate certificate without Distinguished Name (DN)");
return false;
}
return true;

View File

@@ -565,4 +565,36 @@ public class PKIUtils {
x509Cert.getNotAfter());
}
}
private static final String[] KEY_USAGE_NAMES = {
"DigitalSignature",
"NonRepudiation",
"KeyEncipherment",
"DataEncipherment",
"KeyAgreement",
"KeyCertSign",
"CRLSign",
"EncipherOnly",
"DecipherOnly"
};
/**
* {@return the key-usages as sequence of comma-separated names for a specified certificate}
* @param cert x509 certificate
*/
public static String formatKeyUsage(X509Certificate cert) {
boolean[] usage = cert.getKeyUsage();
if (usage == null) {
return "No KeyUsage extension present";
}
List<String> enabled = new ArrayList<>();
for (int i = 0; i < usage.length && i < KEY_USAGE_NAMES.length; i++) {
if (usage[i]) {
enabled.add(KEY_USAGE_NAMES[i]);
}
}
return String.join(", ", enabled);
}
}