mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 19:42:36 +02:00
Merge remote-tracking branch 'origin/GP-5167_ghidra1_BSimUsername--SQUASHED'
This commit is contained in:
commit
e07aa499ea
34 changed files with 1097 additions and 429 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue