Merge remote-tracking branch 'origin/GP-5167_ghidra1_BSimUsername--SQUASHED'

This commit is contained in:
ghidra1 2024-12-12 14:17:53 -05:00
commit e07aa499ea
34 changed files with 1097 additions and 429 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.
@ -38,7 +38,8 @@ public class PasswordDialog extends DialogComponentProvider {
private JPasswordField passwordField;
private JComboBox<String> choiceCB;
private JCheckBox anonymousAccess;
boolean okPressed = false;
private boolean okPressed = false;
private String defaultUserID;
/**
* Construct a new PasswordDialog.
@ -47,7 +48,7 @@ public class PasswordDialog extends DialogComponentProvider {
* @param serverName name of server or keystore pathname
* @param passPrompt password prompt to show in the dialog; may be null, in which case
* "Password:" is displayed next to the password field
* @param namePrompt name prompt to show in the dialog, if null a name will not be prompted for.
* @param userIdPrompt User ID / Name prompt to show in the dialog, if null a name will not be prompted for.
* @param defaultUserID default name when prompting for a name
* @param choicePrompt namePrompt name prompt to show in the dialog, if null a name will not be prompted for.
* @param choices array of choices to present if choicePrompt is not null
@ -55,9 +56,9 @@ public class PasswordDialog extends DialogComponentProvider {
* @param includeAnonymousOption true signals to add a checkbox to request anonymous login
*/
public PasswordDialog(String title, String serverType, String serverName, String passPrompt,
String namePrompt, String defaultUserID, String choicePrompt, String[] choices,
String userIdPrompt, String defaultUserID, String choicePrompt, String[] choices,
int defaultChoice, boolean includeAnonymousOption) {
this(title, serverType, serverName, passPrompt, namePrompt, defaultUserID);
this(title, serverType, serverName, passPrompt, userIdPrompt, defaultUserID);
if (choicePrompt != null) {
workPanel.add(new GLabel(choicePrompt));
choiceCB = new GComboBox<>(choices);
@ -94,12 +95,12 @@ public class PasswordDialog extends DialogComponentProvider {
* @param serverName name of server or keystore pathname
* @param passPrompt password prompt to show in the dialog; may be null, in which case
* "Password:" is displayed next to the password field
* @param namePrompt name prompt to show in the dialog, if null a name will not be prompted for.
* @param userIdPrompt User ID / Name prompt to show in the dialog, if null a name will not be prompted for.
* @param defaultUserID default name when prompting for a name
*/
public PasswordDialog(String title, String serverType, String serverName, String passPrompt,
String namePrompt, String defaultUserID) {
this(title, serverType, serverName, passPrompt, namePrompt, defaultUserID, true);
String userIdPrompt, String defaultUserID) {
this(title, serverType, serverName, passPrompt, userIdPrompt, defaultUserID, true);
}
/**
@ -109,14 +110,17 @@ public class PasswordDialog extends DialogComponentProvider {
* @param serverName name of server or keystore pathname
* @param passPrompt password prompt to show in the dialog; may be null, in which case
* "Password:" is displayed next to the password field
* @param namePrompt name prompt to show in the dialog, if null a name will not be prompted for.
* @param userIdPrompt User ID / Name prompt to show in the dialog, if null a name will not be prompted for.
* @param defaultUserID default name when prompting for a name
* @param hasMessages true if the client will set messages on this dialog. If true, the
* dialog's minimum size will be increased
*/
public PasswordDialog(String title, String serverType, String serverName, String passPrompt,
String namePrompt, String defaultUserID, boolean hasMessages) {
String userIdPrompt, String defaultUserID, boolean hasMessages) {
super(title, true);
this.defaultUserID = defaultUserID;
setRememberSize(false);
setTransient(true);
@ -132,8 +136,8 @@ public class PasswordDialog extends DialogComponentProvider {
workPanel.add(new GLabel(serverName));
}
if (namePrompt != null) {
workPanel.add(new GLabel(namePrompt));
if (userIdPrompt != null) {
workPanel.add(new GLabel(userIdPrompt));
nameField = new JTextField(defaultUserID, 16);
nameField.setName("NAME-ENTRY-COMPONENT");
workPanel.add(nameField);
@ -237,11 +241,11 @@ public class PasswordDialog extends DialogComponentProvider {
}
/**
* Return the user ID entered in the password field
* @return the user ID entered in the password field
* Return the user ID / Name entered in the password field
* @return the user ID / Name entered in the password field
*/
public String getUserID() {
return nameField != null ? nameField.getText().trim() : null;
return nameField != null ? nameField.getText().trim() : defaultUserID;
}
/**

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.
@ -16,11 +16,12 @@
package ghidra.framework.client;
import java.awt.Component;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.net.*;
import javax.security.auth.callback.*;
import org.apache.commons.lang3.StringUtils;
import docking.DockingWindowManager;
import docking.widgets.*;
import ghidra.framework.preferences.Preferences;
@ -35,23 +36,63 @@ public class DefaultClientAuthenticator extends PopupKeyStorePasswordProvider
private Authenticator authenticator = new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
Msg.debug(this, "PasswordAuthentication requested for " + getRequestingURL());
NameCallback nameCb = null;
if (!"NO_NAME".equals(getRequestingScheme())) {
nameCb = new NameCallback("Name: ", ClientUtil.getUserName());
String serverName = getRequestingHost();
URL requestingURL = getRequestingURL();
String pwd = null;
String userName = ClientUtil.getUserName();
boolean useDefaultUser = true;
if (requestingURL != null) {
String userInfo = requestingURL.getUserInfo();
if (userInfo != null) {
// Use user info from URL
int pwdSep = userInfo.indexOf(':');
if (pwdSep < 0) {
userName = userInfo;
useDefaultUser = false;
}
else {
pwd = userInfo.substring(pwdSep + 1);
if (pwdSep != 0) {
userName = userInfo.substring(0, pwdSep);
useDefaultUser = false;
}
}
}
URL minimalURL = DefaultClientAuthenticator.getMinimalURL(requestingURL);
if (minimalURL != null) {
serverName = minimalURL.toExternalForm();
}
}
Msg.debug(this, "PasswordAuthentication requested for " + serverName);
if (pwd != null) {
// Requesting URL specified password
return new PasswordAuthentication(userName, pwd.toCharArray());
}
NameCallback nameCb = new NameCallback("Name: ", userName);
if (!useDefaultUser) {
// Prevent modification of user name by password prompting
nameCb.setName(userName);
}
// Prompt for password
String prompt = getRequestingPrompt();
if (prompt == null) {
prompt = "Password:";
if (StringUtils.isBlank(prompt) || "security".equals(prompt)) {
prompt = "Password:"; // assume dialog will show user name via nameCb
}
PasswordCallback passCb = new PasswordCallback(prompt, false);
try {
ServerPasswordPrompt pp = new ServerPasswordPrompt("Connection Authentication",
"Server", getRequestingHost(), nameCb, passCb, null, null, null);
"Server", serverName, nameCb, passCb, null, null, null);
SystemUtilities.runSwingNow(pp);
if (pp.okWasPressed()) {
return new PasswordAuthentication(nameCb != null ? nameCb.getName() : null,
passCb.getPassword());
return new PasswordAuthentication(nameCb.getName(), passCb.getPassword());
}
}
finally {
@ -61,6 +102,21 @@ public class DefaultClientAuthenticator extends PopupKeyStorePasswordProvider
}
};
/**
* Produce minimal URL (i.e., protocol, host and port)
* @param url request URL
* @return minimal URL
*/
public static URL getMinimalURL(URL url) {
try {
return new URL(url, "/");
}
catch (MalformedURLException e) {
// ignore
}
return null;
}
@Override
public Authenticator getAuthenticator() {
return authenticator;
@ -165,10 +221,25 @@ public class DefaultClientAuthenticator extends PopupKeyStorePasswordProvider
choicePrompt = choiceCb.getPrompt();
choices = choiceCb.getChoices();
}
PasswordDialog pwdDialog =
new PasswordDialog(title, serverType, serverName, passCb.getPrompt(),
nameCb != null ? nameCb.getPrompt() : null, getDefaultUserName(), choicePrompt,
choices, getDefaultChoice(), anonymousCb != null);
String defaultUserName = null;
String namePrompt = null;
if (nameCb != null) {
defaultUserName = nameCb.getName();
if (defaultUserName == null) {
// Name entry only permitted with name callback where name has not be pre-set
defaultUserName = nameCb.getDefaultName();
namePrompt = nameCb.getPrompt();
}
}
if (defaultUserName == null) {
defaultUserName = getDefaultUserName();
}
PasswordDialog pwdDialog = new PasswordDialog(title, serverType, serverName,
passCb.getPrompt(), namePrompt, defaultUserName, choicePrompt, choices,
getDefaultChoice(), anonymousCb != null);
if (errorMsg != null) {
pwdDialog.setErrorText(errorMsg);
}

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.
@ -39,38 +39,58 @@ public class HeadlessClientAuthenticator implements ClientAuthenticator {
private final static char[] BADPASSWORD = "".toCharArray();
private static Object sshPrivateKey;
private static String userID = ClientUtil.getUserName(); // default username
private static String defaultUserName = ClientUtil.getUserName();
private static boolean passwordPromptAllowed;
private Authenticator authenticator = new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
Msg.debug(this, "PasswordAuthentication requested for " + getRequestingURL());
String usage = null;
String prompt = getRequestingPrompt();
if ("security".equals(prompt)) {
prompt = null; // squash generic "security" prompt
if (defaultUserName == null) {
throw new IllegalStateException("Default user name is unknown");
}
URL requestingURL = getRequestingURL();
String serverName = getRequestingHost();
URL requestingURL = getRequestingURL(); // may be null
String pwd = null;
String userName = defaultUserName;
if (requestingURL != null) {
URL minimalURL = null;
try {
minimalURL = new URL(requestingURL, "/");
String userInfo = requestingURL.getUserInfo();
if (userInfo != null) {
// Use user info from URL
int pwdSep = userInfo.indexOf(':');
if (pwdSep < 0) {
userName = userInfo;
}
else {
pwd = userInfo.substring(pwdSep + 1);
if (pwdSep != 0) {
userName = userInfo.substring(0, pwdSep);
}
}
}
catch (MalformedURLException e) {
// ignore
URL minimalURL = DefaultClientAuthenticator.getMinimalURL(requestingURL);
if (minimalURL != null) {
serverName = minimalURL.toExternalForm();
}
usage = "Access password requested for " +
(minimalURL != null ? minimalURL.toExternalForm()
: requestingURL.getAuthority());
prompt = "Password:";
}
if (prompt == null) {
// Assume Ghidra Server access
String host = getRequestingHost();
prompt = (host != null ? (host + " ") : "") + "(" + userID + ") Password:";
Msg.debug(this, "PasswordAuthentication requested for " + serverName);
if (pwd != null) {
// Requesting URL specified password
return new PasswordAuthentication(userName, pwd.toCharArray());
}
return new PasswordAuthentication(userID, getPassword(usage, prompt));
String usage = "Access password requested for " + serverName;
String prompt = getRequestingPrompt();
if (StringUtils.isBlank(prompt) || "security".equals(prompt)) {
prompt = "Password for " + userName +":";
}
return new PasswordAuthentication(userName, getPassword(usage, prompt));
}
};
@ -85,7 +105,8 @@ public class HeadlessClientAuthenticator implements ClientAuthenticator {
/**
* Install headless client authenticator for Ghidra Server
* @param username optional username to be used with a Ghidra Server which
* allows username to be specified
* allows username to be specified. If null, {@link ClientUtil#getUserName()}
* will be used.
* @param keystorePath optional PKI or SSH keystore path. May also be specified
* as resource path for SSH key.
* @param allowPasswordPrompt if true the user may be prompted for passwords
@ -97,7 +118,7 @@ public class HeadlessClientAuthenticator implements ClientAuthenticator {
boolean allowPasswordPrompt) throws IOException {
passwordPromptAllowed = allowPasswordPrompt;
if (username != null) {
userID = username;
defaultUserName = username;
}
// clear existing key store settings
@ -175,7 +196,7 @@ public class HeadlessClientAuthenticator implements ClientAuthenticator {
passwordPrompt += "\n";
}
if (prompt == null) {
if (StringUtils.isBlank(prompt)) {
prompt = "Password:";
}
@ -233,17 +254,39 @@ public class HeadlessClientAuthenticator implements ClientAuthenticator {
anonymousCb.setAnonymousAccessRequested(true);
return true;
}
if (defaultUserName == null) {
throw new IllegalStateException("Default user name is unknown");
}
if (choiceCb != null) {
choiceCb.setSelectedIndex(1);
}
if (nameCb != null && userID != null) {
nameCb.setName(userID);
String userName = null;
if (nameCb != null) {
userName = nameCb.getName();
if (userName == null) {
userName = nameCb.getDefaultName();
}
}
if (userName == null) {
userName = defaultUserName;
}
if (nameCb != null) {
nameCb.setName(defaultUserName);
}
String usage = null;
if (serverName != null) {
usage = serverType + ": " + serverName;
}
char[] password = getPassword(usage, passCb.getPrompt());
// Ignore prompt specified by passCb
String prompt = "Password for " + userName +":";
char[] password = getPassword(usage, prompt);
passCb.setPassword(password);
return password != null;
}
@ -278,7 +321,7 @@ public class HeadlessClientAuthenticator implements ClientAuthenticator {
return false;
}
if (nameCb != null) {
nameCb.setName(userID);
nameCb.setName(defaultUserName);
}
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.
@ -687,6 +687,10 @@ public class GhidraURL {
if (StringUtils.isBlank(host)) {
throw new IllegalArgumentException("host required");
}
// TODO: Need to improve checks and use of URL encoding
if (host.indexOf('@') >= 0) { // prevent user info with hostname
throw new IllegalArgumentException("invalid host name");
}
if (StringUtils.isBlank(repositoryName)) {
throw new IllegalArgumentException("repository name required");
}

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.
@ -109,6 +109,9 @@ public class GhidraURLConnection extends URLConnection {
public GhidraURLConnection(URL url, GhidraProtocolHandler protocolHandler)
throws MalformedURLException {
super(url);
if (url.getUserInfo() != null) {
throw new MalformedURLException("User info not supported by Ghidra URLs");
}
if (protocolHandler == null) {
throw new IllegalArgumentException("missing required protocol handler");
}