This is an automated email from the ASF dual-hosted git repository. lgoldstein pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/mina-sshd.git
commit 9e17380d9837002d268258dc02f3b92037da3765 Author: Lyor Goldstein <lgoldst...@apache.org> AuthorDate: Sat Nov 9 19:30:22 2019 +0200 [SSHD-955] Provide configurable control over auto-detected password prompt in client-side 'UserAuthKeyboardInteractive' implementation --- CHANGES.md | 3 + docs/client-setup.md | 6 ++ .../sshd/cli/client/SshClientCliSupport.java | 4 +- .../org/apache/sshd/cli/client/SshKeyScanMain.java | 3 +- .../sshd/cli/server/SshServerCliSupport.java | 4 +- .../apache/sshd/common/PropertyResolverUtils.java | 19 +++++- .../common/config/ConfigFileReaderSupport.java | 25 ++++---- .../sshd/common/util/security/SecurityUtils.java | 5 +- .../auth/keyboard/UserAuthKeyboardInteractive.java | 63 +++++++++++++++---- .../sshd/client/auth/keyboard/UserInteraction.java | 62 +++++++++++++++++- .../server/auth/keyboard/InteractiveChallenge.java | 5 +- .../server/config/SshServerConfigFileReader.java | 2 +- .../java/org/apache/sshd/client/ClientTest.java | 73 ++++++++++++---------- .../java/org/apache/sshd/server/ServerTest.java | 3 +- .../sshd/server/auth/WelcomeBannerPhaseTest.java | 8 ++- .../apache/sshd/server/auth/WelcomeBannerTest.java | 13 +++- .../sshd/common/util/net/LdapNetworkConnector.java | 7 ++- .../config/keys/loader/openpgp/PGPUtils.java | 5 +- 18 files changed, 232 insertions(+), 78 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index dfacf54..f656e5d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -69,6 +69,8 @@ the message type=30 (old request). * Provide configurable control over the client-side `ChannelSession` _stdin_ pump chunk size. +* Client side `UserAuthKeyboardInteractive` allows configurable detection of plain-text password prompt. + ## Behavioral changes and enhancements * [SSHD-926](https://issues.apache.org/jira/browse/SSHD-930) - Add support for OpenSSH 'lsets...@openssh.com' SFTP protocol extension. @@ -101,3 +103,4 @@ for the server's identification before sending KEX-INIT message. * [SSHD-953](https://issues.apache.org/jira/browse/SSHD-953) - Parse and strip quoted command arguments when executing a server-side command via local shell. +* [SSHD-955](https://issues.apache.org/jira/browse/SSHD-955) - Provide configurable control over auto-detected password prompt in client-side `UserAuthKeyboardInteractive` implementation. diff --git a/docs/client-setup.md b/docs/client-setup.md index 93292b5..6e275df 100644 --- a/docs/client-setup.md +++ b/docs/client-setup.md @@ -104,6 +104,12 @@ While RFC-4256 support is the primary purpose of this interface, it can also be in [RFC 4252 section 5.4](https://www.ietf.org/rfc/rfc4252.txt) as well as its initial identification string as described in [RFC 4253 section 4.2](https://tools.ietf.org/html/rfc4253#section-4.2). +In this context, regardless of whether such interaction is configured, the default implementation for the client side contains code +that attempts to auto-detect a password prompt. If it detects it, then it attempts to use one of the registered passwords (if any) as +the interactive response to the server's challenge - (see client-side implementation of `UserAuthKeyboardInteractive#useCurrentPassword` +method). Basically, detection occurs by checking if the server sent **exactly one** challenge with no requested echo, and the challenge +string looks like `"... password ...:"` (**Note:** the auto-detection and password prompt detection patterns are configurable). + ## Using the `SshClient` to connect to a server Once the `SshClient` instance is properly configured it needs to be `start()`-ed in order to connect to a server. diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java index ca1919b..0116ea8 100644 --- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java +++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientCliSupport.java @@ -502,7 +502,9 @@ public abstract class SshClientCliSupport extends CliSupport { } @Override - public String[] interactive(ClientSession clientSession, String name, String instruction, String lang, String[] prompt, boolean[] echo) { + public String[] interactive( + ClientSession clientSession, String name, String instruction, + String lang, String[] prompt, boolean[] echo) { int numPropmts = GenericUtils.length(prompt); String[] answers = new String[numPropmts]; try { diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshKeyScanMain.java b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshKeyScanMain.java index 924ad60..3115ef7 100644 --- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshKeyScanMain.java +++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshKeyScanMain.java @@ -220,7 +220,8 @@ public class SshKeyScanMain implements Channel, Callable<Void>, ServerKeyVerifie @Override public String[] interactive( - ClientSession session, String name, String instruction, String lang, String[] prompt, boolean[] echo) { + ClientSession session, String name, String instruction, + String lang, String[] prompt, boolean[] echo) { return null; } diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java b/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java index eee5daa..3095497 100644 --- a/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java +++ b/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerCliSupport.java @@ -192,7 +192,7 @@ public abstract class SshServerCliSupport extends CliSupport { String nameList = (options == null) ? null : options.getString(ConfigFileReaderSupport.SUBSYSTEM_CONFIG_PROP); - if ("none".equalsIgnoreCase(nameList)) { + if (PropertyResolverUtils.isNoneValue(nameList)) { return Collections.emptyList(); } @@ -238,7 +238,7 @@ public abstract class SshServerCliSupport extends CliSupport { return DEFAULT_SHELL_FACTORY; } - if ("none".equalsIgnoreCase(factory)) { + if (PropertyResolverUtils.isNoneValue(factory)) { return null; } diff --git a/sshd-common/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java index 7e0a680..280c447 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java @@ -36,25 +36,37 @@ import org.apache.sshd.common.util.ValidateUtils; * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> */ public final class PropertyResolverUtils { + public static final String NONE_VALUE = "none"; + /** * Case <U>insensitive</U> {@link NavigableSet} of values considered {@code true} by {@link #parseBoolean(String)} */ public static final NavigableSet<String> TRUE_VALUES = Collections.unmodifiableNavigableSet( - GenericUtils.asSortedSet(String.CASE_INSENSITIVE_ORDER, "true", "t", "yes", "y", "on")); + GenericUtils.asSortedSet( + String.CASE_INSENSITIVE_ORDER, "true", "t", "yes", "y", "on")); /** * Case <U>insensitive</U> {@link NavigableSet} of values considered {@code false} by {@link #parseBoolean(String)} */ public static final NavigableSet<String> FALSE_VALUES = Collections.unmodifiableNavigableSet( - GenericUtils.asSortedSet(String.CASE_INSENSITIVE_ORDER, "false", "f", "no", "n", "off")); + GenericUtils.asSortedSet( + String.CASE_INSENSITIVE_ORDER, "false", "f", "no", "n", "off")); private PropertyResolverUtils() { throw new UnsupportedOperationException("No instance allowed"); } /** + * @param v Value to examine + * @return {@code true} if equals to {@value #NONE_VALUE} - case <U>insensitive</U> + */ + public static boolean isNoneValue(String v) { + return NONE_VALUE.equalsIgnoreCase(v); + } + + /** * @param resolver The {@link PropertyResolver} instance - ignored if {@code null} * @param name The property name * @param defaultValue The default value to return if the specified property @@ -374,7 +386,8 @@ public final class PropertyResolverUtils { * @param defaultValue The default value to return if property not set or empty * @return The set value (if not {@code null}/empty) or default one */ - public static String getStringProperty(PropertyResolver resolver, String name, String defaultValue) { + public static String getStringProperty( + PropertyResolver resolver, String name, String defaultValue) { String value = getString(resolver, name); if (GenericUtils.isEmpty(value)) { return defaultValue; diff --git a/sshd-common/src/main/java/org/apache/sshd/common/config/ConfigFileReaderSupport.java b/sshd-common/src/main/java/org/apache/sshd/common/config/ConfigFileReaderSupport.java index 6a1fb96..05a107b 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/config/ConfigFileReaderSupport.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/config/ConfigFileReaderSupport.java @@ -78,22 +78,24 @@ public final class ConfigFileReaderSupport { // see http://manpages.ubuntu.com/manpages/precise/en/man5/sshd_config.5.html public static final String CIPHERS_CONFIG_PROP = "Ciphers"; public static final String DEFAULT_CIPHERS = - "aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128,aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc,arcfour"; + "aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128,aes128-cbc,3des-cbc" + + ",blowfish-cbc,cast128-cbc,aes192-cbc,aes256-cbc,arcfour"; // see http://manpages.ubuntu.com/manpages/precise/en/man5/sshd_config.5.html public static final String MACS_CONFIG_PROP = "MACs"; public static final String DEFAULT_MACS = - "hmac-md5,hmac-sha1,umac...@openssh.com,hmac-ripemd160,hmac-sha1-96,hmac-md5-96,hmac-sha2-256,hmac-sha2-256-96,hmac-sha2-512,hmac-sha2-512-96"; + "hmac-md5,hmac-sha1,umac...@openssh.com,hmac-ripemd160,hmac-sha1-96" + + ",hmac-md5-96,hmac-sha2-256,hmac-sha2-256-96,hmac-sha2-512,hmac-sha2-512-96"; // see http://manpages.ubuntu.com/manpages/precise/en/man5/sshd_config.5.html public static final String KEX_ALGORITHMS_CONFIG_PROP = "KexAlgorithms"; public static final String DEFAULT_KEX_ALGORITHMS = - "ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521" - + "," + "diffie-hellman-group-exchange-sha256,diffie-hellman-group-exchange-sha1" - // RFC-8268 groups - + "," + "diffie-hellman-group18-sha512,diffie-hellman-group17-sha512" - + "," + "diffie-hellman-group16-sha512,diffie-hellman-group15-sha512" - + "," + "diffie-hellman-group14-sha256" - // Legacy groups - + "," + "diffie-hellman-group14-sha1,diffie-hellman-group1-sha1"; + "ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521" + + "," + "diffie-hellman-group-exchange-sha256,diffie-hellman-group-exchange-sha1" + // RFC-8268 groups + + "," + "diffie-hellman-group18-sha512,diffie-hellman-group17-sha512" + + "," + "diffie-hellman-group16-sha512,diffie-hellman-group15-sha512" + + "," + "diffie-hellman-group14-sha256" + // Legacy groups + + "," + "diffie-hellman-group14-sha1,diffie-hellman-group1-sha1"; // see http://linux.die.net/man/5/ssh_config public static final String HOST_KEY_ALGORITHMS_CONFIG_PROP = "HostKeyAlgorithms"; // see https://tools.ietf.org/html/rfc5656 @@ -124,7 +126,8 @@ public final class ConfigFileReaderSupport { } public static Properties readConfigFile(InputStream input, boolean okToClose) throws IOException { - try (Reader reader = new InputStreamReader(NoCloseInputStream.resolveInputStream(input, okToClose), StandardCharsets.UTF_8)) { + try (Reader reader = new InputStreamReader( + NoCloseInputStream.resolveInputStream(input, okToClose), StandardCharsets.UTF_8)) { return readConfigFile(reader, true); } } diff --git a/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java b/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java index 2e2ce04..ae48186 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/util/security/SecurityUtils.java @@ -57,6 +57,7 @@ import javax.crypto.Mac; import javax.crypto.spec.DHParameterSpec; import org.apache.sshd.common.NamedResource; +import org.apache.sshd.common.PropertyResolverUtils; import org.apache.sshd.common.config.keys.FilePasswordProvider; import org.apache.sshd.common.config.keys.KeyUtils; import org.apache.sshd.common.config.keys.PrivateKeyEntryDecoder; @@ -337,7 +338,7 @@ public final class SecurityUtils { } String name = System.getProperty(PROP_DEFAULT_SECURITY_PROVIDER); - choice = (GenericUtils.isEmpty(name) || "none".equalsIgnoreCase(name)) + choice = (GenericUtils.isEmpty(name) || PropertyResolverUtils.isNoneValue(name)) ? SecurityProviderChoice.EMPTY : SecurityProviderChoice.toSecurityProviderChoice(name); DEFAULT_PROVIDER_HOLDER.set(choice); @@ -389,7 +390,7 @@ public final class SecurityUtils { String regsList = System.getProperty(SECURITY_PROVIDER_REGISTRARS, GenericUtils.join(DEFAULT_SECURITY_PROVIDER_REGISTRARS, ',')); boolean bouncyCastleRegistered = false; - if ((GenericUtils.length(regsList) > 0) && (!"none".equalsIgnoreCase(regsList))) { + if ((GenericUtils.length(regsList) > 0) && (!PropertyResolverUtils.isNoneValue(regsList))) { String[] classes = GenericUtils.split(regsList, ','); Logger logger = LoggerFactory.getLogger(SecurityUtils.class); boolean debugEnabled = logger.isDebugEnabled(); diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractive.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractive.java index 99635be..fa81847 100644 --- a/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractive.java +++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserAuthKeyboardInteractive.java @@ -25,6 +25,7 @@ import java.util.concurrent.atomic.AtomicInteger; import org.apache.sshd.client.ClientAuthenticationManager; import org.apache.sshd.client.auth.AbstractUserAuth; import org.apache.sshd.client.session.ClientSession; +import org.apache.sshd.common.PropertyResolverUtils; import org.apache.sshd.common.RuntimeSshException; import org.apache.sshd.common.SshConstants; import org.apache.sshd.common.util.GenericUtils; @@ -200,7 +201,8 @@ public class UserAuthKeyboardInteractive extends AbstractUserAuth { } int numResponses = rep.length; - buffer = session.createBuffer(SshConstants.SSH_MSG_USERAUTH_INFO_RESPONSE, numResponses * Long.SIZE + Byte.SIZE); + buffer = session.createBuffer( + SshConstants.SSH_MSG_USERAUTH_INFO_RESPONSE, numResponses * Long.SIZE + Byte.SIZE); buffer.putInt(numResponses); for (int index = 0; index < numResponses; index++) { String r = rep[index]; @@ -274,12 +276,16 @@ public class UserAuthKeyboardInteractive extends AbstractUserAuth { return GenericUtils.EMPTY_STRING_ARRAY; } - String candidate = getCurrentPasswordCandidate(); - if (useCurrentPassword(candidate, name, instruction, lang, prompt, echo)) { - if (debugEnabled) { - log.debug("getUserResponses({}) use password candidate for interaction={}", session, name); + if (PropertyResolverUtils.getBooleanProperty( + session, UserInteraction.AUTO_DETECT_PASSWORD_PROMPT, + UserInteraction.DEFAULT_AUTO_DETECT_PASSWORD_PROMPT)) { + String candidate = getCurrentPasswordCandidate(); + if (useCurrentPassword(session, candidate, name, instruction, lang, prompt, echo)) { + if (debugEnabled) { + log.debug("getUserResponses({}) use password candidate for interaction={}", session, name); + } + return new String[]{candidate}; } - return new String[]{candidate}; } UserInteraction ui = session.getUserInteraction(); @@ -304,22 +310,55 @@ public class UserAuthKeyboardInteractive extends AbstractUserAuth { return null; } - protected boolean useCurrentPassword(String password, String name, String instruction, String lang, String[] prompt, boolean[] echo) { + /** + * Checks if we have a candidate password and <U>exactly one</U> prompt is requested + * with no echo, and the prompt matches a configurable pattern. + * + * @param session The {@link ClientSession} through which the request is received + * @param password The current password candidate to use + * @param name The service name + * @param instruction The request instruction + * @param lang The reported language tag + * @param prompt The requested prompts + * @param echo The matching prompts echo flags + * @return Whether to use the password candidate as reply to the prompts + * @see UserInteraction#INTERACTIVE_PASSWORD_PROMPT INTERACTIVE_PASSWORD_PROMPT + * @see UserInteraction#CHECK_INTERACTIVE_PASSWORD_DELIM CHECK_INTERACTIVE_PASSWORD_DELIM + */ + protected boolean useCurrentPassword( + ClientSession session, String password, String name, + String instruction, String lang, String[] prompt, boolean[] echo) { int num = GenericUtils.length(prompt); if ((num != 1) || (password == null) || echo[0]) { return false; } - // check that prompt is something like "XXX password YYY:" - String value = GenericUtils.trimToEmpty(prompt[0]).toLowerCase(); - int passPos = value.lastIndexOf("password"); + // check if prompt is something like "XXX password YYY:" + String value = GenericUtils.trimToEmpty(prompt[0]); + // Don't care about the case + value = value.toLowerCase(); + + String promptList = PropertyResolverUtils.getStringProperty( + session, UserInteraction.INTERACTIVE_PASSWORD_PROMPT, + UserInteraction.DEFAULT_INTERACTIVE_PASSWORD_PROMPT); + int passPos = UserInteraction.findPromptComponentLastPosition(value, promptList); if (passPos < 0) { // no password keyword in prompt return false; } - int sepPos = value.lastIndexOf(':'); - return sepPos > passPos; + String delimList = PropertyResolverUtils.getStringProperty( + session, UserInteraction.CHECK_INTERACTIVE_PASSWORD_DELIM, + UserInteraction.DEFAULT_CHECK_INTERACTIVE_PASSWORD_DELIM); + if (PropertyResolverUtils.isNoneValue(delimList)) { + return true; + } + int sepPos = UserInteraction.findPromptComponentLastPosition(value, delimList); + if (sepPos < passPos) { + return false; + } + + return true; } public static String getAuthCommandName(int cmd) { diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserInteraction.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserInteraction.java index 5c294fa..1381d13 100644 --- a/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserInteraction.java +++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/keyboard/UserInteraction.java @@ -21,6 +21,7 @@ package org.apache.sshd.client.auth.keyboard; import java.util.List; import org.apache.sshd.client.session.ClientSession; +import org.apache.sshd.common.util.GenericUtils; /** * Interface used by the ssh client to communicate with the end user. @@ -30,6 +31,38 @@ import org.apache.sshd.client.session.ClientSession; */ public interface UserInteraction { /** + * Whether to auto-detect password challenge prompt + * + * @see #INTERACTIVE_PASSWORD_PROMPT + * @see #CHECK_INTERACTIVE_PASSWORD_DELIM + */ + String AUTO_DETECT_PASSWORD_PROMPT = "user-interaction-auto-detect-password-prompt"; + + /** Default value for {@value #AUTO_DETECT_PASSWORD_PROMPT} */ + boolean DEFAULT_AUTO_DETECT_PASSWORD_PROMPT = true; + + /** + * Comma separated list of values used to detect request for a + * password in interactive mode. <B>Note:</B> the matched prompt + * is assumed to be <U>lowercase</U>. + */ + String INTERACTIVE_PASSWORD_PROMPT = "user-interaction-password-prompt"; + + /** Default value for {@value #INTERACTIVE_PASSWORD_PROMPT} */ + String DEFAULT_INTERACTIVE_PASSWORD_PROMPT = "password"; + + /** + * If password prompt detected then check it ends with + * <U>any</U> of the comma separated list of these values. + * Use "none" to disable this extra check. <B>Note:</B> + * the matched prompt is assumed to be <U>lowercase</U>. + */ + String CHECK_INTERACTIVE_PASSWORD_DELIM = "user-interaction-check-password-delimiter"; + + /** Default value of {@value #CHECK_INTERACTIVE_PASSWORD_DELIM} */ + String DEFAULT_CHECK_INTERACTIVE_PASSWORD_DELIM = ":"; + + /** * A useful "placeholder" that indicates that no interaction is expected. * <B>Note:</B> throws {@link IllegalStateException} is any of the interaction * methods is called @@ -41,7 +74,9 @@ public interface UserInteraction { } @Override - public String[] interactive(ClientSession session, String name, String instruction, String lang, String[] prompt, boolean[] echo) { + public String[] interactive( + ClientSession session, String name, String instruction, + String lang, String[] prompt, boolean[] echo) { throw new IllegalStateException("interactive(" + session + ")[" + name + "] unexpected call"); } @@ -105,7 +140,8 @@ public interface UserInteraction { * however we do not enforce it since it is defined as the <U>server's</U> * job to check and manage this violation. */ - String[] interactive(ClientSession session, String name, String instruction, String lang, String[] prompt, boolean[] echo); + String[] interactive( + ClientSession session, String name, String instruction, String lang, String[] prompt, boolean[] echo); /** * Invoked when the server returns an {@code SSH_MSG_USERAUTH_PASSWD_CHANGEREQ} @@ -121,4 +157,26 @@ public interface UserInteraction { * be it other passwords, public keys, etc...) */ String getUpdatedPassword(ClientSession session, String prompt, String lang); + + /** + * @param prompt The user interaction prompt + * @param tokensList A comma-separated list of tokens whose + * <U>last</U> index is prompt is sought. + * @return The position of any token in the prompt - negative if not found + */ + static int findPromptComponentLastPosition(String prompt, String tokensList) { + if (GenericUtils.isEmpty(prompt) || GenericUtils.isEmpty(tokensList)) { + return -1; + } + + String[] tokens = GenericUtils.split(tokensList, ','); + for (String t : tokens) { + int pos = prompt.lastIndexOf(t); + if (pos >= 0) { + return pos; + } + } + + return -1; + } } diff --git a/sshd-core/src/main/java/org/apache/sshd/server/auth/keyboard/InteractiveChallenge.java b/sshd-core/src/main/java/org/apache/sshd/server/auth/keyboard/InteractiveChallenge.java index a2f1b24..662cb58 100644 --- a/sshd-core/src/main/java/org/apache/sshd/server/auth/keyboard/InteractiveChallenge.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/auth/keyboard/InteractiveChallenge.java @@ -125,6 +125,9 @@ public class InteractiveChallenge implements Cloneable { @Override public String toString() { - return getInteractionName() + "[" + getInteractionInstruction() + "](" + getLanguageTag() + "): " + getPrompts(); + return getInteractionName() + + "[" + getInteractionInstruction() + "]" + + "(" + getLanguageTag() + ")" + + ": " + getPrompts(); } } diff --git a/sshd-core/src/main/java/org/apache/sshd/server/config/SshServerConfigFileReader.java b/sshd-core/src/main/java/org/apache/sshd/server/config/SshServerConfigFileReader.java index 1d15b21..d5746d3 100644 --- a/sshd-core/src/main/java/org/apache/sshd/server/config/SshServerConfigFileReader.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/config/SshServerConfigFileReader.java @@ -161,7 +161,7 @@ public final class SshServerConfigFileReader { if (GenericUtils.isEmpty(bannerOption)) { return "Welcome to SSHD\n"; - } else if ("none".equals(bannerOption)) { + } else if (PropertyResolverUtils.isNoneValue(bannerOption)) { return null; } else if (ServerAuthenticationManager.AUTO_WELCOME_BANNER_VALUE.equalsIgnoreCase(bannerOption)) { return bannerOption; diff --git a/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java b/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java index 434ac66..d3403c1 100644 --- a/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java +++ b/sshd-core/src/test/java/org/apache/sshd/client/ClientTest.java @@ -1143,24 +1143,29 @@ public class ClientTest extends BaseTestSupport { @Test // see SSHD-504 public void testDefaultKeyboardInteractivePasswordPromptLocationIndependence() throws Exception { Collection<String> mismatchedPrompts = new LinkedList<>(); - client.setUserAuthFactories(Collections.singletonList(new UserAuthKeyboardInteractiveFactory() { - @Override - public UserAuthKeyboardInteractive createUserAuth(ClientSession session) throws IOException { - return new UserAuthKeyboardInteractive() { + client.setUserAuthFactories( + Collections.singletonList( + new UserAuthKeyboardInteractiveFactory() { @Override - protected boolean useCurrentPassword( - String password, String name, String instruction, String lang, String[] prompt, boolean[] echo) { - boolean expected = GenericUtils.length(password) > 0; - boolean actual = super.useCurrentPassword(password, name, instruction, lang, prompt, echo); - if (expected != actual) { - System.err.println("Mismatched usage result for prompt=" + prompt[0] + ": expected=" + expected + ", actual=actual"); - mismatchedPrompts.add(prompt[0]); - } - return actual; + public UserAuthKeyboardInteractive createUserAuth(ClientSession session) throws IOException { + return new UserAuthKeyboardInteractive() { + @Override + protected boolean useCurrentPassword( + ClientSession session, String password, String name, String instruction, + String lang, String[] prompt, boolean[] echo) { + boolean expected = GenericUtils.length(password) > 0; + boolean actual = super.useCurrentPassword( + session, password, name, instruction, lang, prompt, echo); + if (expected != actual) { + System.err.println("Mismatched usage result for prompt=" + prompt[0] + + ": expected=" + expected + ", actual=actual"); + mismatchedPrompts.add(prompt[0]); + } + return actual; + } + }; } - }; - } - })); + })); client.start(); Function<String, String> stripper = input -> { @@ -1179,21 +1184,22 @@ public class ClientTest extends BaseTestSupport { input -> getCurrentTestName() + " " + stripper.apply(input) + " " + getCurrentTestName() + ":" )); - sshd.setKeyboardInteractiveAuthenticator(new DefaultKeyboardInteractiveAuthenticator() { - private int xformerIndex; + sshd.setKeyboardInteractiveAuthenticator( + new DefaultKeyboardInteractiveAuthenticator() { + private int xformerIndex; - @Override - protected String getInteractionPrompt(ServerSession session) { - String original = super.getInteractionPrompt(session); - if (xformerIndex < xformers.size()) { - Function<String, String> x = xformers.get(xformerIndex); - xformerIndex++; - return x.apply(original); - } else { - return original; + @Override + protected String getInteractionPrompt(ServerSession session) { + String original = super.getInteractionPrompt(session); + if (xformerIndex < xformers.size()) { + Function<String, String> x = xformers.get(xformerIndex); + xformerIndex++; + return x.apply(original); + } else { + return original; + } } - } - }); + }); try { for (int index = 0; index < xformers.size(); index++) { @@ -1249,7 +1255,8 @@ public class ClientTest extends BaseTestSupport { @Override public String[] interactive( - ClientSession session, String name, String instruction, String lang, String[] prompt, boolean[] echo) { + ClientSession session, String name, String instruction, + String lang, String[] prompt, boolean[] echo) { validateSession("interactive", session); count.incrementAndGet(); return badResponse; @@ -1320,7 +1327,8 @@ public class ClientTest extends BaseTestSupport { @Override public String[] interactive( - ClientSession clientSession, String name, String instruction, String lang, String[] prompt, boolean[] echo) { + ClientSession clientSession, String name, String instruction, + String lang, String[] prompt, boolean[] echo) { assertSame("Mismatched interactive session", session, clientSession); count.incrementAndGet(); return new String[]{getCurrentTestName()}; @@ -1374,7 +1382,8 @@ public class ClientTest extends BaseTestSupport { @Override public String[] interactive( - ClientSession clientSession, String name, String instruction, String lang, String[] prompt, boolean[] echo) { + ClientSession clientSession, String name, String instruction, + String lang, String[] prompt, boolean[] echo) { assertSame("Mismatched interactive session", session, clientSession); int attemptId = count.incrementAndGet(); return new String[]{"bad#" + attemptId}; diff --git a/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java b/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java index faebaf1..62f4371 100644 --- a/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java +++ b/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java @@ -860,7 +860,8 @@ public class ServerTest extends BaseTestSupport { @Override public String[] interactive( - ClientSession session, String name, String instruction, String lang, String[] prompt, boolean[] echo) { + ClientSession session, String name, String instruction, + String lang, String[] prompt, boolean[] echo) { clientCount.incrementAndGet(); return replies; } diff --git a/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerPhaseTest.java b/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerPhaseTest.java index bcb5311..b58fea3 100644 --- a/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerPhaseTest.java +++ b/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerPhaseTest.java @@ -116,12 +116,16 @@ public class WelcomeBannerPhaseTest extends BaseTestSupport { } @Override - public String[] interactive(ClientSession session, String name, String instruction, String lang, String[] prompt, boolean[] echo) { + public String[] interactive( + ClientSession session, String name, String instruction, + String lang, String[] prompt, boolean[] echo) { return null; } }); - try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) { + try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port) + .verify(7L, TimeUnit.SECONDS) + .getSession()) { session.addPasswordIdentity(getCurrentTestName()); session.auth().verify(5L, TimeUnit.SECONDS); } diff --git a/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerTest.java b/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerTest.java index f3a5400..5b567fb 100644 --- a/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerTest.java +++ b/sshd-core/src/test/java/org/apache/sshd/server/auth/WelcomeBannerTest.java @@ -173,7 +173,9 @@ public class WelcomeBannerTest extends BaseTestSupport { } @Override - public String[] interactive(ClientSession session, String name, String instruction, String lang, String[] prompt, boolean[] echo) { + public String[] interactive( + ClientSession session, String name, String instruction, + String lang, String[] prompt, boolean[] echo) { throw new UnsupportedOperationException("Unexpected interactive call"); } @@ -183,7 +185,10 @@ public class WelcomeBannerTest extends BaseTestSupport { } }); PropertyResolverUtils.updateProperty(sshd, ServerAuthenticationManager.WELCOME_BANNER, getCurrentTestName()); - try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession()) { + + try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port) + .verify(7L, TimeUnit.SECONDS) + .getSession()) { assertTrue("Welcome not signalled on time", sigSem.tryAcquire(11L, TimeUnit.SECONDS)); session.addPasswordIdentity(getCurrentTestName()); session.auth().verify(5L, TimeUnit.SECONDS); @@ -228,7 +233,9 @@ public class WelcomeBannerTest extends BaseTestSupport { } @Override - public String[] interactive(ClientSession session, String name, String instruction, String lang, String[] prompt, boolean[] echo) { + public String[] interactive( + ClientSession session, String name, String instruction, + String lang, String[] prompt, boolean[] echo) { validateSession("interactive", session); return null; } diff --git a/sshd-ldap/src/main/java/org/apache/sshd/common/util/net/LdapNetworkConnector.java b/sshd-ldap/src/main/java/org/apache/sshd/common/util/net/LdapNetworkConnector.java index 9dbd853..5c42675 100644 --- a/sshd-ldap/src/main/java/org/apache/sshd/common/util/net/LdapNetworkConnector.java +++ b/sshd-ldap/src/main/java/org/apache/sshd/common/util/net/LdapNetworkConnector.java @@ -40,6 +40,7 @@ import javax.naming.directory.InitialDirContext; import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; +import org.apache.sshd.common.PropertyResolverUtils; import org.apache.sshd.common.util.GenericUtils; import org.apache.sshd.common.util.ValidateUtils; import org.apache.sshd.common.util.buffer.BufferUtils; @@ -412,7 +413,9 @@ public class LdapNetworkConnector<C> extends NetworkConnector { * or just the original one with some changes in it * @throws NamingException If failed to set up the environment */ - protected Map<String, Object> setupDirContextEnvironment(C queryContext, Map<String, Object> env, String username, String password) throws NamingException { + protected Map<String, Object> setupDirContextEnvironment( + C queryContext, Map<String, Object> env, String username, String password) + throws NamingException { if (!env.containsKey(Context.PROVIDER_URL)) { int port = getPort(); ValidateUtils.checkTrue(port > 0, "No port configured"); @@ -423,7 +426,7 @@ public class LdapNetworkConnector<C> extends NetworkConnector { } String mode = Objects.toString(env.get(Context.SECURITY_AUTHENTICATION), null); - boolean anonymous = GenericUtils.isEmpty(mode) || "none".equalsIgnoreCase(mode); + boolean anonymous = GenericUtils.isEmpty(mode) || PropertyResolverUtils.isNoneValue(mode); if (!anonymous) { Object[] bindParams = {username, password}; if (!env.containsKey(Context.SECURITY_PRINCIPAL)) { diff --git a/sshd-openpgp/src/main/java/org/apache/sshd/common/config/keys/loader/openpgp/PGPUtils.java b/sshd-openpgp/src/main/java/org/apache/sshd/common/config/keys/loader/openpgp/PGPUtils.java index e33fecb..95f7e5d 100644 --- a/sshd-openpgp/src/main/java/org/apache/sshd/common/config/keys/loader/openpgp/PGPUtils.java +++ b/sshd-openpgp/src/main/java/org/apache/sshd/common/config/keys/loader/openpgp/PGPUtils.java @@ -27,6 +27,7 @@ import java.util.NavigableMap; import java.util.Set; import java.util.TreeMap; +import org.apache.sshd.common.PropertyResolverUtils; import org.apache.sshd.common.config.keys.IdentityUtils; import org.apache.sshd.common.util.GenericUtils; import org.apache.sshd.common.util.OsUtils; @@ -51,13 +52,13 @@ public final class PGPUtils { public static final String PGP_ENCRYPTED_FILE = "application/pgp-encrypted"; /** Alias for {@link EncryptionAlgorithm#Unencrypted Unencrypted} */ - public static final String NO_CIPHER_PLACEHOLDER = "none"; + public static final String NO_CIPHER_PLACEHOLDER = PropertyResolverUtils.NONE_VALUE; public static final Set<EncryptionAlgorithm> CIPHERS = Collections.unmodifiableSet(EnumSet.allOf(EncryptionAlgorithm.class)); /** Alias for {@link CompressionAlgorithm#Uncompressed Uncompressed} */ - public static final String NO_COMPRESSION_PLACEHOLDER = "none"; + public static final String NO_COMPRESSION_PLACEHOLDER = PropertyResolverUtils.NONE_VALUE; public static final Set<CompressionAlgorithm> COMPRESSIONS = Collections.unmodifiableSet(EnumSet.allOf(CompressionAlgorithm.class));