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 b43e9df4ed059b2ebd1c80e8f13321fce5720994 Author: Lyor Goldstein <lgoldst...@apache.org> AuthorDate: Sat Nov 16 11:47:53 2019 +0200 [SSHD-955] Provide SSH command line option to control user authentication mechanisms in client/server --- .../main/java/org/apache/sshd/cli/CliSupport.java | 31 +++++- .../sshd/cli/client/SshClientCliSupport.java | 110 +++++++++++++-------- .../org/apache/sshd/cli/client/SshClientMain.java | 12 ++- .../org/apache/sshd/cli/server/SshServerMain.java | 8 +- .../sshd/common/auth/UserAuthFactoriesManager.java | 68 +++++++++++++ .../common/config/ConfigFileReaderSupport.java | 29 +++++- .../sshd/client/ClientAuthenticationManager.java | 40 ++------ .../sshd/client/session/ClientUserAuthService.java | 6 +- .../sshd/server/ServerAuthenticationManager.java | 43 +++----- 9 files changed, 229 insertions(+), 118 deletions(-) diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java b/sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java index 3b6e417..f3920a8 100644 --- a/sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java +++ b/sshd-cli/src/main/java/org/apache/sshd/cli/CliSupport.java @@ -30,6 +30,9 @@ import java.util.logging.Level; import org.apache.sshd.common.AttributeRepository; import org.apache.sshd.common.PropertyResolver; import org.apache.sshd.common.PropertyResolverUtils; +import org.apache.sshd.common.auth.UserAuthFactoriesManager; +import org.apache.sshd.common.auth.UserAuthInstance; +import org.apache.sshd.common.auth.UserAuthMethodFactory; import org.apache.sshd.common.config.ConfigFileReaderSupport; import org.apache.sshd.common.config.LogLevelValue; import org.apache.sshd.common.helpers.AbstractFactoryManager; @@ -40,6 +43,7 @@ import org.apache.sshd.common.io.IoServiceEventListener; import org.apache.sshd.common.io.IoServiceFactoryFactory; import org.apache.sshd.common.kex.KexProposalOption; import org.apache.sshd.common.session.Session; +import org.apache.sshd.common.session.SessionContext; import org.apache.sshd.common.session.SessionListener; import org.apache.sshd.common.util.GenericUtils; @@ -69,6 +73,18 @@ public abstract class CliSupport { return true; } + public static <S extends SessionContext, + M extends UserAuthInstance<S>, F extends UserAuthMethodFactory<S, M>, + I extends UserAuthFactoriesManager<S, M, F>> + void setupUserAuthFactories( + I manager, PropertyResolver options) { + String methods = options.getString(ConfigFileReaderSupport.PREFERRED_AUTHS_CONFIG_PROP); + if (GenericUtils.isNotEmpty(methods)) { + manager.setUserAuthFactoriesNameList(methods); + return; + } + } + /** * Scans the arguments for the "-io" command line option and sets the I/O * service accordingly. If no specific option specified then {@link #DEFAULT_IO_SERVICE_FACTORY} @@ -115,7 +131,8 @@ public abstract class CliSupport { public static BuiltinIoServiceFactoryFactories resolveBuiltinIoServiceFactory( PrintStream stderr, String argName, String provider) { - BuiltinIoServiceFactoryFactories factory = BuiltinIoServiceFactoryFactories.fromFactoryName(provider); + BuiltinIoServiceFactoryFactories factory = + BuiltinIoServiceFactoryFactories.fromFactoryName(provider); if (factory == null) { System.err.println(argName + " - unknown provider (" + provider + ")" + " should be one of " + BuiltinIoServiceFactoryFactories.VALUES); @@ -170,7 +187,8 @@ public abstract class CliSupport { @Override public void abortEstablishedConnection( - IoConnector connector, SocketAddress local, AttributeRepository context, SocketAddress remote, Throwable reason) + IoConnector connector, SocketAddress local, AttributeRepository context, + SocketAddress remote, Throwable reason) throws IOException { out.append("Abort established connection ").append(Objects.toString(connector)) .append(" - local=").append(Objects.toString(local)) @@ -182,8 +200,10 @@ public abstract class CliSupport { } @Override - public void connectionAccepted(IoAcceptor acceptor, SocketAddress local, SocketAddress remote, SocketAddress service) - throws IOException { + public void connectionAccepted( + IoAcceptor acceptor, SocketAddress local, + SocketAddress remote, SocketAddress service) + throws IOException { out.append("Connection accepted via ").append(Objects.toString(acceptor)) .append(" - local=").append(Objects.toString(local)) .append(", remote=").append(Objects.toString(remote)) @@ -193,7 +213,8 @@ public abstract class CliSupport { @Override public void abortAcceptedConnection( - IoAcceptor acceptor, SocketAddress local, SocketAddress remote, SocketAddress service, Throwable reason) + IoAcceptor acceptor, SocketAddress local, SocketAddress remote, + SocketAddress service, Throwable reason) throws IOException { out.append("Abort accepted connection ").append(Objects.toString(acceptor)) .append(" - local=").append(Objects.toString(local)) 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 0116ea8..3b7c0e4 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 @@ -116,7 +116,8 @@ public abstract class SshClientCliSupport extends CliSupport { // NOTE: ClientSession#getFactoryManager is the SshClient public static ClientSession setupClientSession( - String portOption, BufferedReader stdin, Level level, PrintStream stdout, PrintStream stderr, String... args) + String portOption, BufferedReader stdin, Level level, + PrintStream stdout, PrintStream stderr, String... args) throws Exception { int port = -1; String host = null; @@ -178,7 +179,9 @@ public abstract class SshClientCliSupport extends CliSupport { compressions = setupCompressions(argName, GenericUtils.join( Arrays.asList( - BuiltinCompressions.Constants.ZLIB, BuiltinCompressions.Constants.DELAYED_ZLIB), ','), + BuiltinCompressions.Constants.ZLIB, + BuiltinCompressions.Constants.DELAYED_ZLIB), + ','), compressions, stderr); if (GenericUtils.isEmpty(compressions)) { error = true; @@ -239,7 +242,8 @@ public abstract class SshClientCliSupport extends CliSupport { return null; } - PropertyResolver resolver = PropertyResolverUtils.toPropertyResolver(options); + PropertyResolver resolver = + PropertyResolverUtils.toPropertyResolver(options); SshClient client = setupClient( resolver, ciphers, macs, compressions, identities, stdin, stdout, stderr, level, args); @@ -259,7 +263,9 @@ public abstract class SshClientCliSupport extends CliSupport { } // TODO use a configurable wait time - ClientSession session = client.connect(login, host, port).verify().getSession(); + ClientSession session = client.connect(login, host, port) + .verify() + .getSession(); try { if (GenericUtils.length(password) > 0) { session.addPasswordIdentity(password); @@ -288,7 +294,8 @@ public abstract class SshClientCliSupport extends CliSupport { } public static Map<String, ?> resolveClientEnvironment(PropertyResolver resolver) { - return resolveClientEnvironment((resolver == null) ? Collections.emptyMap() : resolver.getProperties()); + return resolveClientEnvironment( + (resolver == null) ? Collections.emptyMap() : resolver.getProperties()); } public static Map<String, ?> resolveClientEnvironment(Map<String, ?> options) { @@ -325,12 +332,15 @@ public abstract class SshClientCliSupport extends CliSupport { public static PtyChannelConfiguration resolveClientPtyOptions(PropertyResolver resolver) throws IOException, InterruptedException { - return resolveClientPtyOptions((resolver == null) ? Collections.emptyMap() : resolver.getProperties()); + return resolveClientPtyOptions( + (resolver == null) ? Collections.emptyMap() : resolver.getProperties()); } public static PtyChannelConfiguration resolveClientPtyOptions(Map<String, ?> options) throws IOException, InterruptedException { - Object v = GenericUtils.isEmpty(options) ? null : options.get(SshClientConfigFileReader.REQUEST_TTY_OPTION); + Object v = GenericUtils.isEmpty(options) + ? null + : options.get(SshClientConfigFileReader.REQUEST_TTY_OPTION); String s = Objects.toString(v, "auto"); boolean autoDetect = "auto".equalsIgnoreCase(s); Boolean ptyEnabled = autoDetect ? Boolean.TRUE : PropertyResolverUtils.parseBoolean(s); @@ -365,7 +375,8 @@ public abstract class SshClientCliSupport extends CliSupport { for (String kve : kvp) { int pos = kve.indexOf('='); String key = (pos >= 0) ? kve.substring(0, pos) : kve; - PtyMode mode = ValidateUtils.checkNotNull(PtyMode.fromName(key), "Unknown PTY mode: %s", key); + PtyMode mode = ValidateUtils.checkNotNull( + PtyMode.fromName(key), "Unknown PTY mode: %s", key); s = (pos >= 0) ? kve.substring(pos + 1) : ""; Integer value = GenericUtils.isEmpty(s) ? Integer.valueOf(1) : Integer.valueOf(s); Integer prev = ptyModes.put(mode, value); @@ -378,8 +389,10 @@ public abstract class SshClientCliSupport extends CliSupport { } public static SshClient setupDefaultClient( - PropertyResolver resolver, Level level, PrintStream stdout, PrintStream stderr, String... args) { - SshClient client = setupIoServiceFactory(SshClient.setUpDefaultClient(), resolver, level, stdout, stderr, args); + PropertyResolver resolver, Level level, + PrintStream stdout, PrintStream stderr, String... args) { + SshClient client = setupIoServiceFactory( + SshClient.setUpDefaultClient(), resolver, level, stdout, stderr, args); SshClientConfigFileReader.setupClientHeartbeat(client, resolver); return client; } @@ -441,6 +454,7 @@ public abstract class SshClientCliSupport extends CliSupport { } setupServerKeyVerifier(client, resolver, stdin, stdout, stderr); + setupUserAuthFactories(client, resolver); setupSessionUserInteraction(client, stdin, stdout, stderr); setupSessionExtensions(client, resolver, stdin, stdout, stderr); @@ -536,10 +550,12 @@ public abstract class SshClientCliSupport extends CliSupport { } public static void setupSessionExtensions( - KexFactoryManager manager, PropertyResolver resolver, BufferedReader stdin, PrintStream stdout, PrintStream stderr) + KexFactoryManager manager, PropertyResolver resolver, + BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception { Map<String, ?> options = resolver.getProperties(); - String kexExtension = Objects.toString(options.remove(KexExtensionHandler.class.getSimpleName()), null); + String kexExtension = Objects.toString( + options.remove(KexExtensionHandler.class.getSimpleName()), null); if (GenericUtils.isEmpty(kexExtension)) { return; } @@ -551,7 +567,8 @@ public abstract class SshClientCliSupport extends CliSupport { ClassLoader cl = ThreadUtils.resolveDefaultClassLoader(KexExtensionHandler.class); try { Class<?> clazz = cl.loadClass(kexExtension); - KexExtensionHandler handler = KexExtensionHandler.class.cast(clazz.newInstance()); + KexExtensionHandler handler = + KexExtensionHandler.class.cast(clazz.newInstance()); manager.setKexExtensionHandler(handler); } catch (Exception e) { stderr.append("ERROR: Failed (").append(e.getClass().getSimpleName()).append(')') @@ -576,43 +593,49 @@ public abstract class SshClientCliSupport extends CliSupport { Map<String, ?> options = resolver.getProperties(); String strictValue = - Objects.toString(options.remove(KnownHostsServerKeyVerifier.STRICT_CHECKING_OPTION), "true"); + Objects.toString( + options.remove(KnownHostsServerKeyVerifier.STRICT_CHECKING_OPTION), "true"); if (!ConfigFileReaderSupport.parseBooleanValue(strictValue)) { return current; } - String filePath = Objects.toString(options.remove(KnownHostsServerKeyVerifier.KNOWN_HOSTS_FILE_OPTION), null); + String filePath = Objects.toString( + options.remove(KnownHostsServerKeyVerifier.KNOWN_HOSTS_FILE_OPTION), null); if (GenericUtils.isEmpty(filePath)) { current = new DefaultKnownHostsServerKeyVerifier(current); } else { // if user specifies a different location than default be lenient current = new DefaultKnownHostsServerKeyVerifier(current, false, Paths.get(filePath)); } - ((KnownHostsServerKeyVerifier) current).setModifiedServerKeyAcceptor((clientSession, remoteAddress, entry, expected, actual) -> { - stderr.append("WARNING: Mismatched keys presented by ").append(Objects.toString(remoteAddress)) - .append(" for entry=").println(entry); - stderr.append(" ").append("Expected=").append(KeyUtils.getKeyType(expected)) - .append('-').println(KeyUtils.getFingerPrint(expected)); - stderr.append(" ").append("Actual=").append(KeyUtils.getKeyType(actual)) - .append('-').println(KeyUtils.getFingerPrint(actual)); - stderr.flush(); // just making sure + ((KnownHostsServerKeyVerifier) current).setModifiedServerKeyAcceptor( + (clientSession, remoteAddress, entry, expected, actual) -> { + stderr.append("WARNING: Mismatched keys presented by ").append(Objects.toString(remoteAddress)) + .append(" for entry=").println(entry); + stderr.append(" ").append("Expected=").append(KeyUtils.getKeyType(expected)) + .append('-').println(KeyUtils.getFingerPrint(expected)); + stderr.append(" ").append("Actual=").append(KeyUtils.getKeyType(actual)) + .append('-').println(KeyUtils.getFingerPrint(actual)); + stderr.flush(); // just making sure - stdout.append("Accept key and update known hosts: y/[N]"); - stdout.flush(); // just making sure + stdout.append("Accept key and update known hosts: y/[N]"); + stdout.flush(); // just making sure - String ans = GenericUtils.trimToEmpty(stdin.readLine()); - return (GenericUtils.length(ans) > 0) && (Character.toLowerCase(ans.charAt(0)) == 'y'); - }); + String ans = GenericUtils.trimToEmpty(stdin.readLine()); + return (GenericUtils.length(ans) > 0) + && (Character.toLowerCase(ans.charAt(0)) == 'y'); + }); manager.setServerKeyVerifier(current); return current; } - public static OutputStream resolveLoggingTargetStream(PrintStream stdout, PrintStream stderr, String... args) { + public static OutputStream resolveLoggingTargetStream( + PrintStream stdout, PrintStream stderr, String... args) { return resolveLoggingTargetStream(stdout, stderr, args, GenericUtils.length(args)); } - public static OutputStream resolveLoggingTargetStream(PrintStream stdout, PrintStream stderr, String[] args, int maxIndex) { + public static OutputStream resolveLoggingTargetStream( + PrintStream stdout, PrintStream stderr, String[] args, int maxIndex) { for (int index = 0; index < maxIndex; index++) { String argName = args[index]; if ("-E".equals(argName)) { @@ -640,7 +663,8 @@ public abstract class SshClientCliSupport extends CliSupport { } public static List<NamedFactory<Compression>> setupCompressions(PropertyResolver options, PrintStream stderr) { - String argVal = PropertyResolverUtils.getString(options, ConfigFileReaderSupport.COMPRESSION_PROP); + String argVal = PropertyResolverUtils.getString( + options, ConfigFileReaderSupport.COMPRESSION_PROP); if (GenericUtils.isEmpty(argVal)) { return Collections.emptyList(); } @@ -661,7 +685,8 @@ public abstract class SshClientCliSupport extends CliSupport { return null; } - BuiltinCompressions.ParseResult result = BuiltinCompressions.parseCompressionsList(argVal); + BuiltinCompressions.ParseResult result = + BuiltinCompressions.parseCompressionsList(argVal); Collection<? extends NamedFactory<Compression>> available = result.getParsedFactories(); if (GenericUtils.isEmpty(available)) { showError(stderr, "No known compressions in " + argVal); @@ -670,14 +695,16 @@ public abstract class SshClientCliSupport extends CliSupport { Collection<String> unsupported = result.getUnsupportedFactories(); if (GenericUtils.size(unsupported) > 0) { - stderr.append("WARNING: Ignored unsupported compressions: ").println(GenericUtils.join(unsupported, ',')); + stderr.append("WARNING: Ignored unsupported compressions: ") + .println(GenericUtils.join(unsupported, ',')); } return new ArrayList<>(available); } public static List<NamedFactory<Mac>> setupMacs(PropertyResolver options, PrintStream stderr) { - String argVal = PropertyResolverUtils.getString(options, ConfigFileReaderSupport.MACS_CONFIG_PROP); + String argVal = PropertyResolverUtils.getString( + options, ConfigFileReaderSupport.MACS_CONFIG_PROP); return GenericUtils.isEmpty(argVal) ? Collections.emptyList() : setupMacs(ConfigFileReaderSupport.MACS_CONFIG_PROP, argVal, null, stderr); @@ -699,21 +726,24 @@ public abstract class SshClientCliSupport extends CliSupport { Collection<String> unsupported = result.getUnsupportedFactories(); if (GenericUtils.size(unsupported) > 0) { - stderr.append("WARNING: Ignored unsupported MACs: ").println(GenericUtils.join(unsupported, ',')); + stderr.append("WARNING: Ignored unsupported MACs: ") + .println(GenericUtils.join(unsupported, ',')); } return new ArrayList<>(available); } public static List<NamedFactory<Cipher>> setupCiphers(PropertyResolver options, PrintStream stderr) { - String argVal = PropertyResolverUtils.getString(options, ConfigFileReaderSupport.CIPHERS_CONFIG_PROP); + String argVal = PropertyResolverUtils.getString( + options, ConfigFileReaderSupport.CIPHERS_CONFIG_PROP); return GenericUtils.isEmpty(argVal) ? Collections.emptyList() : setupCiphers(ConfigFileReaderSupport.CIPHERS_CONFIG_PROP, argVal, null, stderr); } // returns null - e.g., re-specified or no supported cipher found - public static List<NamedFactory<Cipher>> setupCiphers(String argName, String argVal, List<NamedFactory<Cipher>> current, PrintStream stderr) { + public static List<NamedFactory<Cipher>> setupCiphers( + String argName, String argVal, List<NamedFactory<Cipher>> current, PrintStream stderr) { if (GenericUtils.size(current) > 0) { showError(stderr, argName + " option value re-specified: " + NamedResource.getNames(current)); return null; @@ -728,13 +758,15 @@ public abstract class SshClientCliSupport extends CliSupport { Collection<String> unsupported = result.getUnsupportedFactories(); if (GenericUtils.size(unsupported) > 0) { - stderr.append("WARNING: Ignored unsupported ciphers: ").println(GenericUtils.join(unsupported, ',')); + stderr.append("WARNING: Ignored unsupported ciphers: ") + .println(GenericUtils.join(unsupported, ',')); } return new ArrayList<>(available); } - public static Handler setupLogging(Level level, PrintStream stdout, PrintStream stderr, OutputStream outputStream) { + public static Handler setupLogging( + Level level, PrintStream stdout, PrintStream stderr, OutputStream outputStream) { Handler fh = new ConsoleHandler() { { setOutputStream(outputStream); // override the default (stderr) diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientMain.java b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientMain.java index 8a3c3d2..e62b071 100644 --- a/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientMain.java +++ b/sshd-cli/src/main/java/org/apache/sshd/cli/client/SshClientMain.java @@ -121,11 +121,13 @@ public class SshClientMain extends SshClientCliSupport { ClientSession session = null; try (BufferedReader stdin = new BufferedReader( - new InputStreamReader(new NoCloseInputStream(System.in), Charset.defaultCharset()))) { + new InputStreamReader( + new NoCloseInputStream(System.in), Charset.defaultCharset()))) { if (!error) { setupLogging(level, stdout, stderr, logStream); - session = setupClientSession(SSH_CLIENT_PORT_OPTION, stdin, level, stdout, stderr, args); + session = setupClientSession( + SSH_CLIENT_PORT_OPTION, stdin, level, stdout, stderr, args); if (session == null) { error = true; } @@ -158,7 +160,8 @@ public class SshClientMain extends SshClientCliSupport { try { if (socksPort >= 0) { - session.startDynamicPortForwarding(new SshdSocketAddress(SshdSocketAddress.LOCALHOST_NAME, socksPort)); + session.startDynamicPortForwarding( + new SshdSocketAddress(SshdSocketAddress.LOCALHOST_NAME, socksPort)); Thread.sleep(Long.MAX_VALUE); } else { Map<String, ?> env = resolveClientEnvironment(client); @@ -169,7 +172,8 @@ public class SshClientMain extends SshClientCliSupport { ((ChannelShell) channel).setAgentForwarding(agentForward); channel.setIn(new NoCloseInputStream(System.in)); } else { - channel = session.createExecChannel(String.join(" ", command).trim(), ptyConfig, env); + channel = session.createExecChannel( + String.join(" ", command).trim(), ptyConfig, env); } try (OutputStream channelOut = new NoCloseOutputStream(System.out); diff --git a/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerMain.java b/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerMain.java index 99c4901..321ae1e 100644 --- a/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerMain.java +++ b/sshd-cli/src/main/java/org/apache/sshd/cli/server/SshServerMain.java @@ -152,7 +152,9 @@ public class SshServerMain extends SshServerCliSupport { Level level = resolveLoggingVerbosity(resolver, args); SshServer sshd = error ? null - : setupIoServiceFactory(SshServer.setUpDefaultServer(), resolver, level, System.out, System.err, args); + : setupIoServiceFactory( + SshServer.setUpDefaultServer(), resolver, + level, System.out, System.err, args); if (sshd == null) { error = true; } @@ -166,7 +168,8 @@ public class SshServerMain extends SshServerCliSupport { props.putAll(options); SshServerConfigFileReader.setupServerHeartbeat(sshd, resolver); - KeyPairProvider hostKeyProvider = resolveServerKeys(System.err, hostKeyType, hostKeySize, keyFiles); + KeyPairProvider hostKeyProvider = + resolveServerKeys(System.err, hostKeyType, hostKeySize, keyFiles); sshd.setKeyPairProvider(hostKeyProvider); // Should come AFTER key pair provider setup so auto-welcome can be generated if needed setupServerBanner(sshd, resolver); @@ -185,6 +188,7 @@ public class SshServerMain extends SshServerCliSupport { sshd.setPasswordAuthenticator((username, password, session) -> Objects.equals(username, password)); sshd.setPublickeyAuthenticator(AcceptAllPublickeyAuthenticator.INSTANCE); + setupUserAuthFactories(sshd, resolver); setupServerForwarding(sshd, level, System.out, System.err, resolver); sshd.setCommandFactory(new ScpCommandFactory.Builder() .withDelegate(ProcessShellCommandFactory.INSTANCE) diff --git a/sshd-common/src/main/java/org/apache/sshd/common/auth/UserAuthFactoriesManager.java b/sshd-common/src/main/java/org/apache/sshd/common/auth/UserAuthFactoriesManager.java new file mode 100644 index 0000000..c64c1d8 --- /dev/null +++ b/sshd-common/src/main/java/org/apache/sshd/common/auth/UserAuthFactoriesManager.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.sshd.common.auth; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.apache.sshd.common.NamedResource; +import org.apache.sshd.common.session.SessionContext; +import org.apache.sshd.common.util.GenericUtils; + +/** + * @param <S> Type of session being managed + * @param <M> Type of {@code UserAuth} being used + * @param <F> Type of user authentication mechanism factory + * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +public interface UserAuthFactoriesManager<S extends SessionContext, + M extends UserAuthInstance<S>, F extends UserAuthMethodFactory<S, M>> { + /** + * Retrieve the list of named factories for <code>UserAuth</code> objects. + * + * @return a list of named <code>UserAuth</code> factories, never {@code null}/empty + */ + List<F> getUserAuthFactories(); + + default String getUserAuthFactoriesNameList() { + return NamedResource.getNames(getUserAuthFactories()); + } + + default List<String> getUserAuthFactoriesNames() { + return NamedResource.getNameList(getUserAuthFactories()); + } + + void setUserAuthFactories(List<F> userAuthFactories); + + default void setUserAuthFactoriesNameList(String names) { + setUserAuthFactoriesNames(GenericUtils.split(names, ',')); + } + + default void setUserAuthFactoriesNames(String... names) { + setUserAuthFactoriesNames( + GenericUtils.isEmpty((Object[]) names) + ? Collections.emptyList() + : Arrays.asList(names)); + } + + void setUserAuthFactoriesNames(Collection<String> names); +} 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 05a107b..ec6c114 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 @@ -30,10 +30,14 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.OpenOption; import java.nio.file.Path; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.Properties; import java.util.concurrent.TimeUnit; import org.apache.sshd.common.PropertyResolverUtils; +import org.apache.sshd.common.auth.UserAuthMethodFactory; import org.apache.sshd.common.keyprovider.KeyPairProvider; import org.apache.sshd.common.util.GenericUtils; import org.apache.sshd.common.util.io.NoCloseInputStream; @@ -52,9 +56,29 @@ public final class ConfigFileReaderSupport { public static final String DEFAULT_COMPRESSION = CompressionConfigValue.NO.getName(); public static final String MAX_SESSIONS_CONFIG_PROP = "MaxSessions"; public static final int DEFAULT_MAX_SESSIONS = 10; + + public static final String PUBKEY_AUTH_CONFIG_PROP = "PubkeyAuthentication"; + public static final String DEFAULT_PUBKEY_AUTH = "yes"; + public static final boolean DEFAULT_PUBKEY_AUTH_VALUE = parseBooleanValue(DEFAULT_PUBKEY_AUTH); + public static final String PASSWORD_AUTH_CONFIG_PROP = "PasswordAuthentication"; - public static final String DEFAULT_PASSWORD_AUTH = "no"; + public static final String DEFAULT_PASSWORD_AUTH = "yes"; public static final boolean DEFAULT_PASSWORD_AUTH_VALUE = parseBooleanValue(DEFAULT_PASSWORD_AUTH); + + public static final String KBD_INTERACTIVE_CONFIG_PROP = "KbdInteractiveAuthentication"; + public static final String DEFAULT_KBD_INTERACTIVE_AUTH = "yes"; + public static final boolean DEFAULT_KBD_INTERACTIVE_AUTH_VALUE = parseBooleanValue(DEFAULT_KBD_INTERACTIVE_AUTH); + + public static final String PREFERRED_AUTHS_CONFIG_PROP = "PreferredAuthentications"; + public static final List<String> DEFAULT_PREFERRED_AUTHS = + Collections.unmodifiableList( + Arrays.asList( + UserAuthMethodFactory.PUBLIC_KEY, + UserAuthMethodFactory.KB_INTERACTIVE, + UserAuthMethodFactory.PASSWORD)); + public static final String DEFAULT_PREFERRED_AUTHS_VALUE = + GenericUtils.join(DEFAULT_PREFERRED_AUTHS, ','); + public static final String LISTEN_ADDRESS_CONFIG_PROP = "ListenAddress"; public static final String DEFAULT_BIND_ADDRESS = SshdSocketAddress.IPV4_ANYADDR; public static final String PORT_CONFIG_PROP = "Port"; @@ -63,9 +87,6 @@ public final class ConfigFileReaderSupport { public static final String USE_DNS_CONFIG_PROP = "UseDNS"; // NOTE: the usual default is TRUE public static final boolean DEFAULT_USE_DNS = true; - public static final String PUBKEY_AUTH_CONFIG_PROP = "PubkeyAuthentication"; - public static final String DEFAULT_PUBKEY_AUTH = "yes"; - public static final boolean DEFAULT_PUBKEY_AUTH_VALUE = parseBooleanValue(DEFAULT_PUBKEY_AUTH); public static final String AUTH_KEYS_FILE_CONFIG_PROP = "AuthorizedKeysFile"; public static final String MAX_AUTH_TRIES_CONFIG_PROP = "MaxAuthTries"; public static final int DEFAULT_MAX_AUTH_TRIES = 6; diff --git a/sshd-core/src/main/java/org/apache/sshd/client/ClientAuthenticationManager.java b/sshd-core/src/main/java/org/apache/sshd/client/ClientAuthenticationManager.java index b1fedc0..102d97c 100644 --- a/sshd-core/src/main/java/org/apache/sshd/client/ClientAuthenticationManager.java +++ b/sshd-core/src/main/java/org/apache/sshd/client/ClientAuthenticationManager.java @@ -20,18 +20,18 @@ package org.apache.sshd.client; import java.security.KeyPair; -import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.List; import org.apache.sshd.client.auth.AuthenticationIdentitiesProvider; import org.apache.sshd.client.auth.BuiltinUserAuthFactories; +import org.apache.sshd.client.auth.UserAuth; import org.apache.sshd.client.auth.UserAuthFactory; import org.apache.sshd.client.auth.keyboard.UserInteraction; import org.apache.sshd.client.auth.password.PasswordIdentityProvider; import org.apache.sshd.client.keyverifier.ServerKeyVerifier; -import org.apache.sshd.common.NamedResource; +import org.apache.sshd.client.session.ClientSession; +import org.apache.sshd.common.auth.UserAuthFactoriesManager; import org.apache.sshd.common.keyprovider.KeyIdentityProviderHolder; import org.apache.sshd.common.util.GenericUtils; import org.apache.sshd.common.util.ValidateUtils; @@ -40,7 +40,9 @@ import org.apache.sshd.common.util.ValidateUtils; * Holds information required for the client to perform authentication with the server * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> */ -public interface ClientAuthenticationManager extends KeyIdentityProviderHolder { +public interface ClientAuthenticationManager + extends UserAuthFactoriesManager<ClientSession, UserAuth, UserAuthFactory>, + KeyIdentityProviderHolder { /** * Ordered comma separated list of authentications methods. @@ -128,40 +130,16 @@ public interface ClientAuthenticationManager extends KeyIdentityProviderHolder { void setUserInteraction(UserInteraction userInteraction); - /** - * @return a {@link List} of {@link UserAuthFactory}-ies - never - * {@code null}/empty - */ - List<UserAuthFactory> getUserAuthFactories(); - - default String getUserAuthFactoriesNameList() { - return NamedResource.getNames(getUserAuthFactories()); - } - - default List<String> getUserAuthFactoriesNames() { - return NamedResource.getNameList(getUserAuthFactories()); - } - - void setUserAuthFactories(List<UserAuthFactory> userAuthFactories); - - default void setUserAuthFactoriesNameList(String names) { - setUserAuthFactoriesNames(GenericUtils.split(names, ',')); - } - - default void setUserAuthFactoriesNames(String... names) { - setUserAuthFactoriesNames( - GenericUtils.isEmpty((Object[]) names) ? Collections.emptyList() : Arrays.asList(names)); - } - + @Override default void setUserAuthFactoriesNames(Collection<String> names) { BuiltinUserAuthFactories.ParseResult result = BuiltinUserAuthFactories.parseFactoriesList(names); List<UserAuthFactory> factories = ValidateUtils.checkNotNullAndNotEmpty( - result.getParsedFactories(), "No supported cipher factories: %s", names); + result.getParsedFactories(), "No supported user authentication factories: %s", names); Collection<String> unsupported = result.getUnsupportedFactories(); ValidateUtils.checkTrue( - GenericUtils.isEmpty(unsupported), "Unsupported cipher factories found: %s", unsupported); + GenericUtils.isEmpty(unsupported), "Unsupported user authentication factories found: %s", unsupported); setUserAuthFactories(factories); } } diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientUserAuthService.java b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientUserAuthService.java index 5c00c6a..86d2a4e 100644 --- a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientUserAuthService.java +++ b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientUserAuthService.java @@ -307,7 +307,8 @@ public class ClientUserAuthService extends AbstractCloseable implements Service, currentMethod++; } else { if (debugEnabled) { - log.debug("tryNext({}) successfully processed initial buffer by method={}", session, userAuth.getName()); + log.debug("tryNext({}) successfully processed initial buffer by method={}", + session, userAuth.getName()); } return; } @@ -337,7 +338,8 @@ public class ClientUserAuthService extends AbstractCloseable implements Service, userAuth = UserAuthMethodFactory.createUserAuth(session, authFactories, method); if (userAuth == null) { - throw new UnsupportedOperationException("Failed to find a user-auth factory for method=" + method); + throw new UnsupportedOperationException( + "Failed to find a user-auth factory for method=" + method); } if (debugEnabled) { diff --git a/sshd-core/src/main/java/org/apache/sshd/server/ServerAuthenticationManager.java b/sshd-core/src/main/java/org/apache/sshd/server/ServerAuthenticationManager.java index ad4406e..72877e6 100644 --- a/sshd-core/src/main/java/org/apache/sshd/server/ServerAuthenticationManager.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/ServerAuthenticationManager.java @@ -20,13 +20,12 @@ package org.apache.sshd.server; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import org.apache.sshd.common.NamedFactory; -import org.apache.sshd.common.NamedResource; +import org.apache.sshd.common.auth.UserAuthFactoriesManager; import org.apache.sshd.common.keyprovider.KeyPairProviderHolder; import org.apache.sshd.common.util.GenericUtils; import org.apache.sshd.common.util.ValidateUtils; @@ -43,12 +42,15 @@ import org.apache.sshd.server.auth.password.PasswordAuthenticator; import org.apache.sshd.server.auth.password.UserAuthPasswordFactory; import org.apache.sshd.server.auth.pubkey.PublickeyAuthenticator; import org.apache.sshd.server.auth.pubkey.UserAuthPublicKeyFactory; +import org.apache.sshd.server.session.ServerSession; /** * Holds providers and helpers related to the server side authentication process * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> */ -public interface ServerAuthenticationManager extends KeyPairProviderHolder { +public interface ServerAuthenticationManager + extends UserAuthFactoriesManager<ServerSession, UserAuth, UserAuthFactory>, + KeyPairProviderHolder { /** * Key used to retrieve the value in the configuration properties map * of the maximum number of failed authentication requests before the @@ -153,37 +155,16 @@ public interface ServerAuthenticationManager extends KeyPairProviderHolder { UserAuthKeyboardInteractiveFactory DEFAULT_USER_AUTH_KB_INTERACTIVE_FACTORY = UserAuthKeyboardInteractiveFactory.INSTANCE; - /** - * Retrieve the list of named factories for <code>UserAuth</code> objects. - * - * @return a list of named <code>UserAuth</code> factories, never {@code null}/empty - */ - List<UserAuthFactory> getUserAuthFactories(); - - default String getUserAuthFactoriesNameList() { - return NamedResource.getNames(getUserAuthFactories()); - } - - default List<String> getUserAuthFactoriesNames() { - return NamedResource.getNameList(getUserAuthFactories()); - } - - void setUserAuthFactories(List<UserAuthFactory> userAuthFactories); - - default void setUserAuthFactoriesNameList(String names) { - setUserAuthFactoriesNames(GenericUtils.split(names, ',')); - } - - default void setUserAuthFactoriesNames(String... names) { - setUserAuthFactoriesNames(GenericUtils.isEmpty((Object[]) names) ? Collections.emptyList() : Arrays.asList(names)); - } - + @Override default void setUserAuthFactoriesNames(Collection<String> names) { - BuiltinUserAuthFactories.ParseResult result = BuiltinUserAuthFactories.parseFactoriesList(names); + BuiltinUserAuthFactories.ParseResult result = + BuiltinUserAuthFactories.parseFactoriesList(names); List<UserAuthFactory> factories = - ValidateUtils.checkNotNullAndNotEmpty(result.getParsedFactories(), "No supported cipher factories: %s", names); + ValidateUtils.checkNotNullAndNotEmpty( + result.getParsedFactories(), "No supported cipher factories: %s", names); Collection<String> unsupported = result.getUnsupportedFactories(); - ValidateUtils.checkTrue(GenericUtils.isEmpty(unsupported), "Unsupported cipher factories found: %s", unsupported); + ValidateUtils.checkTrue( + GenericUtils.isEmpty(unsupported), "Unsupported cipher factories found: %s", unsupported); setUserAuthFactories(factories); }