diff --git a/Ghidra/Features/GhidraServer/data/lib/readme.txt b/Ghidra/Features/GhidraServer/data/lib/readme.txt index e69de29bb2..1f5043b8e8 100644 --- a/Ghidra/Features/GhidraServer/data/lib/readme.txt +++ b/Ghidra/Features/GhidraServer/data/lib/readme.txt @@ -0,0 +1 @@ +This directory is for additional JAVA libraries (*.jar) that you want to make available to GhidraServer. diff --git a/Ghidra/Features/GhidraServer/data/os/readme.txt b/Ghidra/Features/GhidraServer/data/os/readme.txt index e69de29bb2..c76be3575c 100644 --- a/Ghidra/Features/GhidraServer/data/os/readme.txt +++ b/Ghidra/Features/GhidraServer/data/os/readme.txt @@ -0,0 +1,4 @@ +This directory is for additional native system libraries that you want to make available to GhidraServer. + +Place the system library in either a win64, linux64, or osx64 subdirectory, depending on the type of the +system library. diff --git a/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/remote/GhidraServer.java b/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/remote/GhidraServer.java index 9cdb23c04c..4b25f4ca89 100644 --- a/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/remote/GhidraServer.java +++ b/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/remote/GhidraServer.java @@ -140,7 +140,8 @@ public class GhidraServer extends UnicastRemoteObject implements GhidraServerHan GhidraServer(File rootDir, AuthMode authMode, String loginDomain, boolean allowUserToSpecifyName, boolean altSSHLoginAllowed, int defaultPasswordExpirationDays, boolean allowAnonymousAccess, - boolean autoProvisionAuthedUsers) throws IOException, CertificateException { + boolean autoProvisionAuthedUsers, File jaasConfigFile) + throws IOException, CertificateException { super(ServerPortFactory.getRMISSLPort(), clientSocketFactory, serverSocketFactory); @@ -179,7 +180,8 @@ public class GhidraServer extends UnicastRemoteObject implements GhidraServerHan } break; case JAAS_LOGIN: - authModule = new JAASAuthenticationModule("auth", allowUserToSpecifyName); + authModule = + new JAASAuthenticationModule("auth", allowUserToSpecifyName, jaasConfigFile); break; case KRB5_AD_LOGIN: if (loginDomain == null || loginDomain.isBlank()) { @@ -329,9 +331,15 @@ public class GhidraServer extends UnicastRemoteObject implements GhidraServerHan } } else { - throw new FailedLoginException( + RepositoryManager.log(null, null, "User successfully authenticated, but does not exist in Ghidra user list: " + - username); + username, + null); + // Throw LoginException instead of FailedLoginException to prevent + // the user from being asked to retry the login, which might + // lead them to try older/different passwords and get their system + // account locked. + throw new LoginException("Unknown user: " + username); } } RepositoryManager.log(null, null, "User '" + username + "' authenticated", @@ -341,7 +349,11 @@ public class GhidraServer extends UnicastRemoteObject implements GhidraServerHan catch (LoginException e) { RepositoryManager.log(null, null, "Login failed (" + e.getMessage() + ")", username); - throw e; + // 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"); } if (authModule instanceof PasswordFileAuthenticationModule) { supportPasswordChange = true; @@ -671,21 +683,19 @@ public class GhidraServer extends UnicastRemoteObject implements GhidraServerHan serverRoot = new File(installRoot.getFile(false), rootPath); } + File jaasConfigFile = null; if (authMode == JAAS_LOGIN) { if (jaasConfigFileStr == null) { displayUsage("JAAS config file argument (-jaas ) not specified"); System.exit(-1); } - File jaasConfigFile = getServerCfgFile(jaasConfigFileStr); - if (!jaasConfigFile.exists() || !jaasConfigFile.isFile()) { + jaasConfigFile = getServerCfgFile(jaasConfigFileStr); + if (!jaasConfigFile.isFile()) { displayUsage( "JAAS config file (-jaas ) does not exist or is not file: " + jaasConfigFile.getAbsolutePath()); System.exit(-1); } - // NOTE: there is a leading '=' char to force this path to be the one-and-only config file - System.setProperty("java.security.auth.login.config", - "=" + jaasConfigFile.getAbsolutePath()); } try { @@ -788,7 +798,7 @@ public class GhidraServer extends UnicastRemoteObject implements GhidraServerHan GhidraServer svr = new GhidraServer(serverRoot, authMode, loginDomain, nameCallbackAllowed, altSSHLoginAllowed, defaultPasswordExpiration, - allowAnonymousAccess, autoProvision); + allowAnonymousAccess, autoProvision, jaasConfigFile); log.info("Registering Ghidra Server..."); diff --git a/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/remote/ServerHelp.txt b/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/remote/ServerHelp.txt index 2ec37f3e9e..ce657b70db 100644 --- a/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/remote/ServerHelp.txt +++ b/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/remote/ServerHelp.txt @@ -27,17 +27,16 @@ Command line parameters: -jaas : specifies the path to the JAAS config file (when using -a4), relative to the ghidra/server directory (if not absolute). - See jaas/jaas.conf for examples and suggestions. + See jaas.conf for examples and suggestions. It is the system administrator's responsibility to craft their own JAAS configuration directive when using the -a4 mode. -u : enable users to be prompted for user ID (does not apply to -a2 PKI mode) - -autoProvision : enable the auto-creation of Ghidra users when the authenticator module - (ie. OS or other authentication method specified by JAAS) authenticates - a new unknown user. - Users deleted in the OS or other source system will need to be - deleted manually from the Ghidra system. + -autoProvision : enable the auto-creation of new Ghidra Server + users when they successfully authenticate to the server (-a1 and -a4 modes only). + Users removed from the authentication provider (e.g., Active Directory) will need to be + deleted manually from the Ghidra Server using svrAdmin command. -anonymous : enables anonymous repository access (see svrREADME.html for details) diff --git a/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/security/JAASAuthenticationModule.java b/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/security/JAASAuthenticationModule.java index 0bfc349201..ac9c079461 100644 --- a/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/security/JAASAuthenticationModule.java +++ b/Ghidra/Features/GhidraServer/src/main/java/ghidra/server/security/JAASAuthenticationModule.java @@ -15,7 +15,11 @@ */ package ghidra.server.security; +import java.io.File; import java.io.IOException; +import java.net.URI; +import java.security.NoSuchAlgorithmException; +import java.security.URIParameter; import java.util.concurrent.atomic.AtomicReference; import javax.security.auth.Subject; @@ -42,6 +46,7 @@ public class JAASAuthenticationModule implements AuthenticationModule { private boolean allowUserToSpecifyName; private String loginContextName; + private File jaasConfigFile; /** * Creates a new {@link JAASAuthenticationModule} instance. @@ -50,18 +55,42 @@ public class JAASAuthenticationModule implements AuthenticationModule { * @param allowUserToSpecifyName flag, if true will include a {@link NameCallback} in the * {@link #getAuthenticationCallbacks()} list, which allows the user to specify a different * name than their {@link GhidraPrincipal}. - * @throws IllegalArgumentException if the loginContextName is not present in the JAAS configuration + * @param jaasConfigFile JAAS config file + * @throws IllegalArgumentException if the loginContextName is not present in the JAAS configuration or + * if the JAAS config file does not exist */ - public JAASAuthenticationModule(String loginContextName, boolean allowUserToSpecifyName) - throws IllegalArgumentException { + public JAASAuthenticationModule(String loginContextName, boolean allowUserToSpecifyName, + File jaasConfigFile) throws IllegalArgumentException { + this.loginContextName = loginContextName; this.allowUserToSpecifyName = allowUserToSpecifyName; + this.jaasConfigFile = jaasConfigFile; - Configuration cfg = Configuration.getConfiguration(); - AppConfigurationEntry[] authEntry = cfg.getAppConfigurationEntry(loginContextName); - if (authEntry == null) { - throw new IllegalArgumentException( - "Missing '" + loginContextName + "' entry in JAAS config file"); + if (jaasConfigFile == null) { + throw new IllegalArgumentException("JAAS config file not specified"); + } + if (!jaasConfigFile.exists() || !jaasConfigFile.isFile()) { + throw new IllegalArgumentException("JAAS config file does not exist or is not file: " + + jaasConfigFile.getAbsolutePath()); + } + // force early check for valid config file + getJAASConfig(); + } + + private Configuration getJAASConfig() { + try { + URI jaasConfigFileUri = jaasConfigFile.toURI(); + Configuration cfg = + Configuration.getInstance("JavaLoginConfig", new URIParameter(jaasConfigFileUri)); + AppConfigurationEntry[] authEntry = cfg.getAppConfigurationEntry(loginContextName); + if (authEntry == null) { + throw new IllegalArgumentException("Missing '" + loginContextName + + "' entry in JAAS config file: " + jaasConfigFile); + } + return cfg; + } + catch (NoSuchAlgorithmException e) { + throw new RuntimeException("JAAS config error", e); } } @@ -70,14 +99,20 @@ public class JAASAuthenticationModule implements AuthenticationModule { throws LoginException { GhidraPrincipal principal = GhidraPrincipal.getGhidraPrincipal(subject); AtomicReference loginName = new AtomicReference<>(); - LoginContext loginCtx = new LoginContext(loginContextName, loginModuleCallbacks -> { - loginName.set(copyCallbackValues(callbacks, loginModuleCallbacks, principal)); - }); try { + Configuration jaasCfg = getJAASConfig(); + LoginContext loginCtx = + new LoginContext(loginContextName, null, loginModuleCallbacks -> { + loginName.set(copyCallbackValues(callbacks, loginModuleCallbacks, principal)); + }, jaasCfg); + // this is where the callback is triggered loginCtx.login(); } + catch (IllegalArgumentException e) { + throw new LoginException("JAAS configuration error: " + e.getMessage()); + } catch (LoginException e) { // Convert plain LoginExceptions to FailedLoginExceptions to enable // the client to retry the login if desired. diff --git a/Ghidra/Features/GhidraServer/src/test.slow/java/ghidra/server/remote/GhidraServerAWTTest.java b/Ghidra/Features/GhidraServer/src/test.slow/java/ghidra/server/remote/GhidraServerAWTTest.java index ed6f44a0a7..cecf1a7ae1 100644 --- a/Ghidra/Features/GhidraServer/src/test.slow/java/ghidra/server/remote/GhidraServerAWTTest.java +++ b/Ghidra/Features/GhidraServer/src/test.slow/java/ghidra/server/remote/GhidraServerAWTTest.java @@ -55,7 +55,7 @@ public class GhidraServerAWTTest extends AbstractGenericTest { // directly instantiate to avoid GhidraServer.main which may // invoke System.exit GhidraServer server = new GhidraServer(myTmpDir, GhidraServer.AuthMode.NO_AUTH_LOGIN, - null, true, true, -1, true, false); + null, true, true, -1, true, false, null); // exercise server elements, including a repository and buffer file RepositoryManager mgr = (RepositoryManager) getInstanceField("mgr", server); diff --git a/Ghidra/RuntimeScripts/Common/server/jaas/jaas.conf b/Ghidra/RuntimeScripts/Common/server/jaas.conf similarity index 91% rename from Ghidra/RuntimeScripts/Common/server/jaas/jaas.conf rename to Ghidra/RuntimeScripts/Common/server/jaas.conf index b31e628dc0..98a9f73696 100644 --- a/Ghidra/RuntimeScripts/Common/server/jaas/jaas.conf +++ b/Ghidra/RuntimeScripts/Common/server/jaas.conf @@ -40,8 +40,8 @@ example_ad_ldap_auth { // JPAM is not included in the Ghidra distro. See http://jpam.sourceforge.net/. // // Additionally: -// the libjpam.so native library needs to be copied to your /server/os/[linux|linux64] directory. -// the JPAM-x.y.jar java library needs to be copied to your /server/lib directory. +// the libjpam.so native library needs to be copied to your Ghidra/Features/GhidraServer/os/linux64 directory. +// the JPAM-x.y.jar java library needs to be copied to your Ghidra/Features/GhidraServer/lib directory. example_jpam_auth { net.sf.jpam.jaas.JpamLoginModule REQUIRED @@ -62,7 +62,7 @@ example_external_auth { ghidra.server.security.loginmodule.ExternalProgramLoginModule REQUIRED // Path to the external program. An absolute path is preferable. - PROGRAM="server/jaas/jaas_external_program.example.sh" + PROGRAM="server/jaas_external_program.example.sh" // Time to wait for external program to finish before killing it, in milliseconds. TIMEOUT="1000" diff --git a/Ghidra/RuntimeScripts/Common/server/jaas/jaas_external_program.example.sh b/Ghidra/RuntimeScripts/Common/server/jaas_external_program.example.sh similarity index 100% rename from Ghidra/RuntimeScripts/Common/server/jaas/jaas_external_program.example.sh rename to Ghidra/RuntimeScripts/Common/server/jaas_external_program.example.sh diff --git a/Ghidra/RuntimeScripts/Common/server/server.conf b/Ghidra/RuntimeScripts/Common/server/server.conf index 0640b99822..3dd4b14f79 100644 --- a/Ghidra/RuntimeScripts/Common/server/server.conf +++ b/Ghidra/RuntimeScripts/Common/server/server.conf @@ -132,17 +132,16 @@ ghidra.repositories.dir=./repositories # # -jaas : specifies the path to the JAAS config file (when using -a4), relative # to the ghidra/server directory (if not absolute). -# See jaas/jaas.conf for examples and suggestions. +# See jaas.conf for examples and suggestions. # It is the system administrator's responsibility to craft their own # JAAS configuration directive when using the -a4 mode. # # -u : enable users to be prompted for user ID (does not apply to -a2 PKI mode) # -# -autoProvision : enable the auto-creation of Ghidra users when the authenticator module -# (ie. OS or other authentication method specified by JAAS) authenticates -# a new unknown user. -# Users deleted in the OS or other source system will need to be -# deleted manually from the Ghidra system. +# -autoProvision : enable the auto-creation of new Ghidra Server +# users when they successfully authenticate to the server (-a1 and -a4 modes only). +# Users removed from the authentication provider (e.g., Active Directory) will need to be +# deleted manually from the Ghidra Server using svrAdmin command. # # -anonymous : enables anonymous repository access (see svrREADME.html for details) # diff --git a/Ghidra/RuntimeScripts/Common/server/svrREADME.html b/Ghidra/RuntimeScripts/Common/server/svrREADME.html index a103c9ecf8..7858190c9d 100644 --- a/Ghidra/RuntimeScripts/Common/server/svrREADME.html +++ b/Ghidra/RuntimeScripts/Common/server/svrREADME.html @@ -259,7 +259,7 @@ The Ghidra Server has been designed to support many possible user authenticatio
  • JAAS - Java Authentication and Authorization Service (-a4) - user authentication is delegated to the JAAS subsystem. The -jaas <config_file> argument is required to specify the JAAS config file. There is an example config file in the GhidraServer - directory called jaas/jaas.conf. + directory called jaas.conf.

    JAAS is architected similar to Linux/Unix PAM, where a named authentication configuration is possibly composed of several different modules. Ghidra's support of JAAS only handles single simple @@ -280,7 +280,7 @@ The Ghidra Server has been designed to support many possible user authenticatio of successful authentication.

    There is an example (and non-useful) implementation of an external authenticator in the GhidraServer - directory called jaas/jaas_external_program.example.sh. + directory called jaas_external_program.example.sh.

    This login module strives to be compatible with Apache's mod_authnz_external API, and you should be able to use any mod_authnz_external authenticator with Ghidra. @@ -367,17 +367,17 @@ public key files may be made without restarting the Ghidra Server.

  • -jaas <config_file>
    Specifies the path to the JAAS config file (when using -a4), relative to the ghidra/server directory (if not absolute).

    - See jaas/jaas.conf for examples and suggestions. It is the system administrator's + See jaas.conf for examples and suggestions. It is the system administrator's responsibility to craft their own JAAS configuration directive when using the -a4 mode.


  • -u
    Allows the server login user ID to be specified at time of login for -a0 authentication mode. Without this option, the users client-side login ID will be assumed.

  • -
  • -autoProvision
    Enable the auto-creation of Ghidra users when - the authenticator module (ie. OS or other authentication method specified by JAAS) authenticates - a new unknown user. Users deleted in the OS or other source system will need to be deleted - manually from the Ghidra system.
  • +
  • -autoProvision
    Enable the auto-creation of new Ghidra Server + users when they successfully authenticate to the server (-a1 and -a4 modes only). + Users removed from the authentication provider (e.g., Active Directory) will need to be + deleted manually from the Ghidra Server using svrAdmin command.

  • -anonymous
    Enable anonymous access support for Ghidra Server and its repositories. Only those repositories which specifically enable anonymous access will be diff --git a/Ghidra/RuntimeScripts/certification.manifest b/Ghidra/RuntimeScripts/certification.manifest index 9ce7c4ced7..ed2a1e0df5 100644 --- a/Ghidra/RuntimeScripts/certification.manifest +++ b/Ghidra/RuntimeScripts/certification.manifest @@ -1,7 +1,7 @@ ##VERSION: 2.0 ##MODULE IP: Copyright Distribution Permitted -Common/server/jaas/jaas.conf||GHIDRA||||END| -Common/server/jaas/jaas_external_program.example.sh||GHIDRA||||END| +Common/server/jaas.conf||GHIDRA||||END| +Common/server/jaas_external_program.example.sh||GHIDRA||||END| Common/server/server.conf||GHIDRA||||END| Common/server/svrREADME.html||GHIDRA||||END| Common/support/analyzeHeadlessREADME.html||GHIDRA||||END|