Merge remote-tracking branch 'origin/patch'

This commit is contained in:
Ryan Kurtz
2025-02-13 14:32:43 -05:00
12 changed files with 194 additions and 116 deletions

View File

@@ -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.
@@ -33,10 +33,11 @@ public interface ClientAuthenticator extends KeyStorePasswordProvider {
public Authenticator getAuthenticator();
/**
* Process Ghidra Server password authentication callbacks.
* Process password authentication callbacks.
* @param title password prompt title if GUI is used
* @param serverType type of server (label associated with serverName)
* @param serverName name of server
* @param allowUserNameEntry if true user ID entry will be supported if nameCb is not null.
* @param nameCb provides storage for user login name. A null indicates
* that the default user name will be used, @see ClientUtil#getUserName()
* @param passCb provides storage for user password, @see PasswordCallback#setPassword(char[])
@@ -51,8 +52,8 @@ public interface ClientAuthenticator extends KeyStorePasswordProvider {
* @return true if password provided, false if entry cancelled
*/
public boolean processPasswordCallbacks(String title, String serverType, String serverName,
NameCallback nameCb, PasswordCallback passCb, ChoiceCallback choiceCb,
AnonymousCallback anonymousCb, String loginError);
boolean allowUserNameEntry, NameCallback nameCb, PasswordCallback passCb,
ChoiceCallback choiceCb, AnonymousCallback anonymousCb, String loginError);
/**
* Prompt user for reconnect

View File

@@ -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.
@@ -425,7 +425,8 @@ public class ClientUtil {
"Unsupported authentication callback: " + callbacks[0].getClass().getName());
}
if (!clientAuthenticator.processPasswordCallbacks("Repository Server Authentication",
"Repository Server", serverName, nameCb, passCb, choiceCb, anonymousCb, loginError)) {
"Repository Server", serverName, nameCb != null, nameCb, passCb, choiceCb, anonymousCb,
loginError)) {
return false;
}
String name = defaultUserID;

View File

@@ -72,13 +72,14 @@ public class DefaultClientAuthenticator extends PopupKeyStorePasswordProvider
if (pwd != null) {
// Requesting URL specified password
return new PasswordAuthentication(userName, pwd.toCharArray());
return new PasswordAuthentication(userName, pwd.toCharArray());
}
NameCallback nameCb = new NameCallback("Name: ", userName);
boolean allowUserIDEntry = true;
if (!useDefaultUser) {
// Prevent modification of user name by password prompting
nameCb.setName(userName);
allowUserIDEntry = false;
}
// Prompt for password
@@ -89,10 +90,10 @@ public class DefaultClientAuthenticator extends PopupKeyStorePasswordProvider
PasswordCallback passCb = new PasswordCallback(prompt, false);
try {
ServerPasswordPrompt pp = new ServerPasswordPrompt("Connection Authentication",
"Server", serverName, nameCb, passCb, null, null, null);
"Server", serverName, allowUserIDEntry, nameCb, passCb, null, null, null);
SystemUtilities.runSwingNow(pp);
if (pp.okWasPressed()) {
return new PasswordAuthentication(nameCb.getName(), passCb.getPassword());
return new PasswordAuthentication(nameCb.getName(), passCb.getPassword());
}
}
finally {
@@ -135,10 +136,10 @@ public class DefaultClientAuthenticator extends PopupKeyStorePasswordProvider
@Override
public boolean processPasswordCallbacks(String title, String serverType, String serverName,
NameCallback nameCb, PasswordCallback passCb, ChoiceCallback choiceCb,
AnonymousCallback anonymousCb, String loginError) {
ServerPasswordPrompt pp = new ServerPasswordPrompt(title, serverType, serverName, nameCb,
passCb, choiceCb, anonymousCb, loginError);
boolean allowUserNameEntry, NameCallback nameCb, PasswordCallback passCb,
ChoiceCallback choiceCb, AnonymousCallback anonymousCb, String loginError) {
ServerPasswordPrompt pp = new ServerPasswordPrompt(title, serverType, serverName,
allowUserNameEntry, nameCb, passCb, choiceCb, anonymousCb, loginError);
SystemUtilities.runSwingNow(pp);
return pp.okWasPressed();
}
@@ -172,6 +173,7 @@ public class DefaultClientAuthenticator extends PopupKeyStorePasswordProvider
private String title;
private String serverType; // label for serverName field
private String serverName;
private boolean allowUserIDEntry;
private NameCallback nameCb;
private PasswordCallback passCb;
private ChoiceCallback choiceCb;
@@ -180,11 +182,12 @@ public class DefaultClientAuthenticator extends PopupKeyStorePasswordProvider
private boolean okPressed = false;
ServerPasswordPrompt(String title, String serverType, String serverName,
NameCallback nameCb, PasswordCallback passCb, ChoiceCallback choiceCb,
AnonymousCallback anonymousCb, String errorMsg) {
boolean allowUserIDEntry, NameCallback nameCb, PasswordCallback passCb,
ChoiceCallback choiceCb, AnonymousCallback anonymousCb, String errorMsg) {
this.title = title;
this.serverType = serverType;
this.serverName = serverName;
this.allowUserIDEntry = allowUserIDEntry && (nameCb != null);
this.nameCb = nameCb;
this.passCb = passCb;
this.choiceCb = choiceCb;
@@ -225,20 +228,20 @@ public class DefaultClientAuthenticator extends PopupKeyStorePasswordProvider
String defaultUserName = null;
String namePrompt = null;
if (nameCb != null) {
namePrompt = nameCb.getPrompt();
defaultUserName = nameCb.getName();
if (defaultUserName == null) {
// Name entry only permitted with name callback where name has not be pre-set
if (StringUtils.isBlank(defaultUserName)) {
defaultUserName = nameCb.getDefaultName();
namePrompt = nameCb.getPrompt();
}
}
if (defaultUserName == null) {
if (StringUtils.isBlank(defaultUserName)) {
defaultUserName = getDefaultUserName();
}
PasswordDialog pwdDialog = new PasswordDialog(title, serverType, serverName,
passCb.getPrompt(), namePrompt, defaultUserName, choicePrompt, choices,
getDefaultChoice(), anonymousCb != null);
passCb.getPrompt(), allowUserIDEntry, namePrompt, defaultUserName, choicePrompt,
choices, getDefaultChoice(), anonymousCb != null);
if (errorMsg != null) {
pwdDialog.setErrorText(errorMsg);
@@ -252,7 +255,7 @@ public class DefaultClientAuthenticator extends PopupKeyStorePasswordProvider
}
else {
passCb.setPassword(pwdDialog.getPassword());
if (nameCb != null) {
if (nameCb != null && allowUserIDEntry) {
String username = pwdDialog.getUserID();
nameCb.setName(username);
Preferences.setProperty(NAME_PREFERENCE, username);

View File

@@ -39,22 +39,25 @@ public class HeadlessClientAuthenticator implements ClientAuthenticator {
private final static char[] BADPASSWORD = "".toCharArray();
private static Object sshPrivateKey;
private static String defaultUserName = ClientUtil.getUserName();
private static String preferredName = null;
private static boolean passwordPromptAllowed;
/**
* Simple authentication handler using a default authenticator which may be passed to
* {@link Authenticator#setDefault(Authenticator)}. The preferred username must be set by
* invoking {@link #installHeadlessClientAuthenticator(String, String, boolean)} or
* stipulated by the requesting URL. The {@link ClientUtil#getUserName()} identity is never
* used.
*/
private Authenticator authenticator = new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
if (defaultUserName == null) {
throw new IllegalStateException("Default user name is unknown");
}
String serverName = getRequestingHost();
URL requestingURL = getRequestingURL(); // may be null
String pwd = null;
String userName = defaultUserName;
String name = preferredName;
if (requestingURL != null) {
String userInfo = requestingURL.getUserInfo();
@@ -62,12 +65,12 @@ public class HeadlessClientAuthenticator implements ClientAuthenticator {
// Use user info from URL
int pwdSep = userInfo.indexOf(':');
if (pwdSep < 0) {
userName = userInfo;
name = userInfo;
}
else {
pwd = userInfo.substring(pwdSep + 1);
if (pwdSep != 0) {
userName = userInfo.substring(0, pwdSep);
name = userInfo.substring(0, pwdSep);
}
}
}
@@ -77,20 +80,24 @@ public class HeadlessClientAuthenticator implements ClientAuthenticator {
serverName = minimalURL.toExternalForm();
}
}
Msg.debug(this, "PasswordAuthentication requested for " + serverName);
if (StringUtils.isBlank(name)) {
throw new IllegalStateException("Connection user name is unknown");
}
if (pwd != null) {
// Requesting URL specified password
return new PasswordAuthentication(userName, pwd.toCharArray());
return new PasswordAuthentication(name, pwd.toCharArray());
}
String usage = "Access password requested for " + serverName;
String prompt = getRequestingPrompt();
if (StringUtils.isBlank(prompt) || "security".equals(prompt)) {
prompt = "Password for " + userName +":";
prompt = "Password for " + name + ":";
}
return new PasswordAuthentication(userName, getPassword(usage, prompt));
return new PasswordAuthentication(name, getPassword(usage, prompt));
}
};
@@ -103,7 +110,9 @@ public class HeadlessClientAuthenticator implements ClientAuthenticator {
}
/**
* Install headless client authenticator for Ghidra Server
* Install headless client authenticator for Ghidra Server and when http/https
* connections require authentication and have not specified user information.
*
* @param username optional username to be used with a Ghidra Server which
* allows username to be specified. If null, {@link ClientUtil#getUserName()}
* will be used.
@@ -118,7 +127,7 @@ public class HeadlessClientAuthenticator implements ClientAuthenticator {
boolean allowPasswordPrompt) throws IOException {
passwordPromptAllowed = allowPasswordPrompt;
if (username != null) {
defaultUserName = username;
preferredName = username;
}
// clear existing key store settings
@@ -247,45 +256,48 @@ public class HeadlessClientAuthenticator implements ClientAuthenticator {
@Override
public boolean processPasswordCallbacks(String title, String serverType, String serverName,
NameCallback nameCb, PasswordCallback passCb, ChoiceCallback choiceCb,
AnonymousCallback anonymousCb, String loginError) {
boolean allowUserNameEntry, NameCallback nameCb, PasswordCallback passCb,
ChoiceCallback choiceCb, AnonymousCallback anonymousCb, String loginError) {
if (anonymousCb != null && !passwordPromptAllowed) {
// Assume that login error will not occur with anonymous login
anonymousCb.setAnonymousAccessRequested(true);
return true;
}
if (defaultUserName == null) {
throw new IllegalStateException("Default user name is unknown");
}
if (choiceCb != null) {
choiceCb.setSelectedIndex(1);
}
String userName = null;
if (nameCb != null) {
userName = nameCb.getName();
if (userName == null) {
userName = nameCb.getDefaultName();
if (allowUserNameEntry) {
// use preferred login name
userName = preferredName;
}
if (StringUtils.isBlank(userName)) {
// check for default login name in order of precedence
userName = nameCb.getName();
if (StringUtils.isBlank(userName)) {
userName = nameCb.getDefaultName();
}
}
if (allowUserNameEntry) {
nameCb.setName(userName);
}
}
if (userName == null) {
userName = defaultUserName;
}
if (nameCb != null) {
nameCb.setName(defaultUserName);
if (!StringUtils.isBlank(userName)) {
userName = ClientUtil.getUserName();
}
String usage = null;
if (serverName != null) {
usage = serverType + ": " + serverName;
}
// Ignore prompt specified by passCb
String prompt = "Password for " + userName +":";
String prompt = "Password for " + userName + ":";
char[] password = getPassword(usage, prompt);
passCb.setPassword(password);
return password != null;
@@ -321,7 +333,18 @@ public class HeadlessClientAuthenticator implements ClientAuthenticator {
return false;
}
if (nameCb != null) {
nameCb.setName(defaultUserName);
// presence of NameCallback implies user is allowed to specify name
String userName = preferredName;
if (StringUtils.isBlank(userName)) {
userName = nameCb.getName();
if (StringUtils.isBlank(userName)) {
userName = nameCb.getDefaultName();
}
if (!StringUtils.isBlank(userName)) {
userName = ClientUtil.getUserName();
}
}
nameCb.setName(userName);
}
try {
sshCb.sign(sshPrivateKey);

View File

@@ -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.
@@ -83,14 +83,24 @@ public class PasswordClientAuthenticator implements ClientAuthenticator {
}
@Override
public boolean processPasswordCallbacks(String title, String serverType,
String serverName, NameCallback nameCb, PasswordCallback passCb,
public boolean processPasswordCallbacks(String title, String serverType, String serverName,
boolean allowUserNameEntry, NameCallback nameCb, PasswordCallback passCb,
ChoiceCallback choiceCb, AnonymousCallback anonymousCb, String loginError) {
if (choiceCb != null) {
choiceCb.setSelectedIndex(1);
}
if (nameCb != null && username != null) {
nameCb.setName(username);
if (nameCb != null && allowUserNameEntry) {
String name = username;
if (name == null) {
name = nameCb.getName();
}
if (name == null) {
name = nameCb.getDefaultName();
}
if (name == null) {
name = ClientUtil.getUserName();
}
nameCb.setName(name);
}
passCb.setPassword(password.clone());
return true;