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
The following commit(s) were added to refs/heads/master by this push: new f82e0db [SSHD-943] Provide session instance when KEX factory is invoked in order to create a KeyExchange instance f82e0db is described below commit f82e0db2ca23f806d8e31ced9156132f76017483 Author: Lyor Goldstein <lgoldst...@apache.org> AuthorDate: Tue Sep 24 10:54:22 2019 +0300 [SSHD-943] Provide session instance when KEX factory is invoked in order to create a KeyExchange instance --- CHANGES.md | 11 ++-- .../java/org/apache/sshd/common/NamedFactory.java | 7 +-- .../keyprovider/MultiKeyIdentityIterator.java | 4 +- .../sshd/common/session/SessionContextHolder.java | 6 +-- .../java/org/apache/sshd/client/ClientBuilder.java | 29 +++++++--- .../client/kex/AbstractDHClientKeyExchange.java | 10 +--- .../java/org/apache/sshd/client/kex/DHGClient.java | 17 +++--- .../org/apache/sshd/client/kex/DHGEXClient.java | 29 +++++----- .../java/org/apache/sshd/common/BaseBuilder.java | 22 ++++---- .../sshd/common/config/SshConfigFileReader.java | 61 ++++++++++++++-------- .../sshd/common/kex/AbstractKexFactoryManager.java | 9 ++-- .../apache/sshd/common/kex/KexFactoryManager.java | 4 +- .../org/apache/sshd/common/kex/KeyExchange.java | 6 +-- .../apache/sshd/common/kex/KeyExchangeFactory.java | 15 ++++-- .../sshd/common/kex/dh/AbstractDHKeyExchange.java | 18 +++---- .../apache/sshd/common/session/SessionHolder.java | 7 ++- .../common/session/helpers/AbstractSession.java | 14 ++--- .../java/org/apache/sshd/server/ServerBuilder.java | 24 +++++++-- .../server/kex/AbstractDHServerKeyExchange.java | 10 +--- .../org/apache/sshd/server/kex/DHGEXServer.java | 11 ++-- .../java/org/apache/sshd/server/kex/DHGServer.java | 11 ++-- .../java/org/apache/sshd/KeyReExchangeTest.java | 20 +++---- .../sshd/client/ClientSessionListenerTest.java | 36 ++++++++----- .../java/org/apache/sshd/client/kex/KexTest.java | 12 +++-- .../sshd/common/kex/KexFactoryManagerTest.java | 4 +- .../sshd/server/ServerSessionListenerTest.java | 42 +++++++++------ 26 files changed, 265 insertions(+), 174 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 2ec72a5..10f493f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -31,6 +31,9 @@ the standard does not specifically specify the behavior regarding symbolic links * `ChannelFactory` is a proper interface and it has been refactored to contain a `createChannel` method that accepts the session instance through which the request is made. +* `KeyExchangeFactory` is a proper interface and it has been refactored to contain a +`createKeyExchange` method that accepts the session instance through which the request is made. + ## Minor code helpers * `SessionListener` supports `sessionPeerIdentificationReceived` that is invoked once successful @@ -43,9 +46,9 @@ initialized in the past. * The internal moduli used in Diffie-Hellman group exchange are **cached** - lazy-loaded the 1st time such an exchange occurs. The cache can be invalidated (and thus force a re-load) by invoking `Moduli#clearInternalModuliCache`. -* `DHGEXClient#init` implementation allows overriding the min./max. key sizes for a specific session Diffi-Helman group -exchange via properties - see `DHGEXClient#PROP_DHGEX_CLIENT_MIN/MAX_KEY`. Similar applies for `DHGEXServer` but only for -the message type=30. +* `DHGEXClient` implementation allows overriding the min./max. key sizes for a specific session Diffi-Helman group +exchange via properties - see `DHGEXClient#PROP_DHGEX_CLIENT_MIN/MAX/PRF_KEY`. Similar applies for `DHGEXServer` but only for +the message type=30 (old request). ## Behavioral changes and enhancements @@ -63,3 +66,5 @@ for the server's identification before sending its own. * [SSHD-941](https://issues.apache.org/jira/browse/SSHD-941) - Allow user to override min./max. key sizes for a specific session Diffi-Helman group exchange via properties. +* [SSHD-943](https://issues.apache.org/jira/browse/SSHD-943) - Provide session instance when KEX factory is invoked in order to create a KeyExchange instance. + diff --git a/sshd-common/src/main/java/org/apache/sshd/common/NamedFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/NamedFactory.java index 5386447..024c327 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/NamedFactory.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/NamedFactory.java @@ -41,7 +41,8 @@ public interface NamedFactory<T> extends Factory<T>, NamedResource { * @return a newly created object or {@code null} if the factory is not in the list */ static <T> T create(Collection<? extends NamedFactory<? extends T>> factories, String name) { - NamedFactory<? extends T> f = NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, factories); + NamedFactory<? extends T> f = + NamedResource.findByName(name, String.CASE_INSENSITIVE_ORDER, factories); if (f != null) { return f.create(); } else { @@ -49,7 +50,7 @@ public interface NamedFactory<T> extends Factory<T>, NamedResource { } } - static <S extends OptionalFeature, T, E extends NamedFactory<T>> List<NamedFactory<T>> setUpTransformedFactories( + static <S extends OptionalFeature, E extends NamedResource> List<E> setUpTransformedFactories( boolean ignoreUnsupported, Collection<? extends S> preferred, Function<? super S, ? extends E> xform) { return preferred.stream() .filter(f -> ignoreUnsupported || f.isSupported()) @@ -57,7 +58,7 @@ public interface NamedFactory<T> extends Factory<T>, NamedResource { .collect(Collectors.toList()); } - static <T, E extends NamedFactory<T> & OptionalFeature> List<NamedFactory<T>> setUpBuiltinFactories( + static <E extends NamedResource & OptionalFeature> List<E> setUpBuiltinFactories( boolean ignoreUnsupported, Collection<? extends E> preferred) { return preferred.stream() .filter(f -> ignoreUnsupported || f.isSupported()) diff --git a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityIterator.java b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityIterator.java index 09d5f29..3f01f18 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityIterator.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/keyprovider/MultiKeyIdentityIterator.java @@ -26,6 +26,7 @@ import java.util.Iterator; import java.util.NoSuchElementException; import org.apache.sshd.common.session.SessionContext; +import org.apache.sshd.common.session.SessionContextHolder; /** * Iterates over several {@link KeyIdentityProvider}-s exhausting their @@ -33,7 +34,7 @@ import org.apache.sshd.common.session.SessionContext; * * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> */ -public class MultiKeyIdentityIterator implements Iterator<KeyPair> { +public class MultiKeyIdentityIterator implements Iterator<KeyPair>, SessionContextHolder { protected Iterator<KeyPair> currentProvider; protected boolean finished; private final SessionContext sessionContext; @@ -48,6 +49,7 @@ public class MultiKeyIdentityIterator implements Iterator<KeyPair> { return providers; } + @Override public SessionContext getSessionContext() { return sessionContext; } diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/SessionHolder.java b/sshd-common/src/main/java/org/apache/sshd/common/session/SessionContextHolder.java similarity index 86% copy from sshd-core/src/main/java/org/apache/sshd/common/session/SessionHolder.java copy to sshd-common/src/main/java/org/apache/sshd/common/session/SessionContextHolder.java index 280bbf0..79edb7e 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/session/SessionHolder.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/session/SessionContextHolder.java @@ -20,10 +20,8 @@ package org.apache.sshd.common.session; /** - * @param <S> Type of {@link Session} being held * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> */ -@FunctionalInterface -public interface SessionHolder<S extends Session> { - S getSession(); +public interface SessionContextHolder { + SessionContext getSessionContext(); } diff --git a/sshd-core/src/main/java/org/apache/sshd/client/ClientBuilder.java b/sshd-core/src/main/java/org/apache/sshd/client/ClientBuilder.java index 7c60a59..46d7ba9 100644 --- a/sshd-core/src/main/java/org/apache/sshd/client/ClientBuilder.java +++ b/sshd-core/src/main/java/org/apache/sshd/client/ClientBuilder.java @@ -37,12 +37,15 @@ import org.apache.sshd.common.NamedFactory; import org.apache.sshd.common.channel.ChannelFactory; import org.apache.sshd.common.channel.RequestHandler; import org.apache.sshd.common.compression.BuiltinCompressions; +import org.apache.sshd.common.compression.Compression; import org.apache.sshd.common.compression.CompressionFactory; import org.apache.sshd.common.config.keys.FilePasswordProvider; import org.apache.sshd.common.kex.DHFactory; import org.apache.sshd.common.kex.KeyExchange; +import org.apache.sshd.common.kex.KeyExchangeFactory; import org.apache.sshd.common.session.ConnectionService; import org.apache.sshd.common.signature.BuiltinSignatures; +import org.apache.sshd.common.signature.Signature; import org.apache.sshd.server.forward.ForwardedTcpipFactory; /** @@ -81,7 +84,9 @@ public class ClientBuilder extends BaseBuilder<SshClient, ClientBuilder> { BuiltinSignatures.dsa )); - public static final Function<DHFactory, NamedFactory<KeyExchange>> DH2KEX = factory -> + @SuppressWarnings("checkstyle:Indentation") + public static final Function<DHFactory, KeyExchangeFactory> DH2KEX = + factory -> factory == null ? null : factory.isGroupExchange() @@ -136,11 +141,11 @@ public class ClientBuilder extends BaseBuilder<SshClient, ClientBuilder> { super.fillWithDefaultValues(); if (signatureFactories == null) { - signatureFactories = NamedFactory.setUpBuiltinFactories(false, DEFAULT_SIGNATURE_PREFERENCE); + signatureFactories = setUpDefaultSignatureFactories(false); } if (compressionFactories == null) { - compressionFactories = NamedFactory.setUpBuiltinFactories(false, DEFAULT_COMPRESSION_FACTORIES); + compressionFactories = setUpDefaultCompressionFactories(false); } if (keyExchangeFactories == null) { @@ -188,11 +193,21 @@ public class ClientBuilder extends BaseBuilder<SshClient, ClientBuilder> { return client; } + @SuppressWarnings({ "unchecked", "rawtypes" }) // safe due to the hierarchy + public static List<NamedFactory<Signature>> setUpDefaultSignatureFactories(boolean ignoreUnsupported) { + return (List) NamedFactory.setUpBuiltinFactories(ignoreUnsupported, DEFAULT_SIGNATURE_PREFERENCE); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) // safe due to the hierarchy + public static List<NamedFactory<Compression>> setUpDefaultCompressionFactories(boolean ignoreUnsupported) { + return (List) NamedFactory.setUpBuiltinFactories(ignoreUnsupported, DEFAULT_COMPRESSION_FACTORIES); + } + /** * @param ignoreUnsupported If {@code true} then all the default - * key exchanges are included, regardless of whether they are currently - * supported by the JCE. Otherwise, only the supported ones out of the - * list are included + * key exchanges are included, regardless of whether they are currently + * supported by the JCE. Otherwise, only the supported ones out of the + * list are included * @return A {@link List} of the default {@link NamedFactory} * instances of the {@link KeyExchange}s according to the preference * order defined by {@link #DEFAULT_KEX_PREFERENCE}. @@ -200,7 +215,7 @@ public class ClientBuilder extends BaseBuilder<SshClient, ClientBuilder> { * key exchanges according to the <tt>ignoreUnsupported</tt> parameter * @see org.apache.sshd.common.kex.BuiltinDHFactories#isSupported() */ - public static List<NamedFactory<KeyExchange>> setUpDefaultKeyExchanges(boolean ignoreUnsupported) { + public static List<KeyExchangeFactory> setUpDefaultKeyExchanges(boolean ignoreUnsupported) { return NamedFactory.setUpTransformedFactories(ignoreUnsupported, DEFAULT_KEX_PREFERENCE, DH2KEX); } diff --git a/sshd-core/src/main/java/org/apache/sshd/client/kex/AbstractDHClientKeyExchange.java b/sshd-core/src/main/java/org/apache/sshd/client/kex/AbstractDHClientKeyExchange.java index c654b65..3158a2a 100644 --- a/sshd-core/src/main/java/org/apache/sshd/client/kex/AbstractDHClientKeyExchange.java +++ b/sshd-core/src/main/java/org/apache/sshd/client/kex/AbstractDHClientKeyExchange.java @@ -33,8 +33,8 @@ import org.apache.sshd.common.util.ValidateUtils; public abstract class AbstractDHClientKeyExchange extends AbstractDHKeyExchange implements ClientSessionHolder { protected PublicKey serverKey; - protected AbstractDHClientKeyExchange() { - super(); + protected AbstractDHClientKeyExchange(Session session) { + super(ValidateUtils.checkInstanceOf(session, ClientSession.class, "Using a client side KeyExchange on a server: %s", session)); } @Override @@ -43,12 +43,6 @@ public abstract class AbstractDHClientKeyExchange extends AbstractDHKeyExchange } @Override - public void init(Session s, byte[] v_s, byte[] v_c, byte[] i_s, byte[] i_c) throws Exception { - super.init(s, v_s, v_c, i_s, i_c); - ValidateUtils.checkInstanceOf(s, ClientSession.class, "Using a client side KeyExchange on a server: %s", s); - } - - @Override public PublicKey getServerKey() { return serverKey; } diff --git a/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGClient.java b/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGClient.java index bed61f3..810e352 100644 --- a/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGClient.java +++ b/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGClient.java @@ -46,7 +46,9 @@ public class DHGClient extends AbstractDHClientKeyExchange { protected final DHFactory factory; protected AbstractDH dh; - protected DHGClient(DHFactory factory) { + protected DHGClient(DHFactory factory, Session session) { + super(session); + this.factory = Objects.requireNonNull(factory, "No factory"); } @@ -63,8 +65,8 @@ public class DHGClient extends AbstractDHClientKeyExchange { } @Override - public KeyExchange create() { - return new DHGClient(delegate); + public KeyExchange createKeyExchange(Session session) throws Exception { + return new DHGClient(delegate, session); } @Override @@ -77,17 +79,20 @@ public class DHGClient extends AbstractDHClientKeyExchange { } @Override - public void init(Session s, byte[] v_s, byte[] v_c, byte[] i_s, byte[] i_c) throws Exception { - super.init(s, v_s, v_c, i_s, i_c); + public void init(byte[] v_s, byte[] v_c, byte[] i_s, byte[] i_c) throws Exception { + super.init(v_s, v_c, i_s, i_c); + dh = getDH(); hash = dh.getHash(); hash.init(); e = dh.getE(); + Session s = getSession(); if (log.isDebugEnabled()) { log.debug("init({})[{}] Send SSH_MSG_KEXDH_INIT", this, s); } - Buffer buffer = s.createBuffer(SshConstants.SSH_MSG_KEXDH_INIT, e.length + Integer.SIZE); + Buffer buffer = + s.createBuffer(SshConstants.SSH_MSG_KEXDH_INIT, e.length + Integer.SIZE); buffer.putMPInt(e); s.writePacket(buffer); diff --git a/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGEXClient.java b/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGEXClient.java index 2e23683..c6dcc35 100644 --- a/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGEXClient.java +++ b/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGEXClient.java @@ -45,20 +45,28 @@ import org.apache.sshd.common.util.security.SecurityUtils; public class DHGEXClient extends AbstractDHClientKeyExchange { public static final String PROP_DHGEX_CLIENT_MIN_KEY = "dhgex-client-min"; public static final String PROP_DHGEX_CLIENT_MAX_KEY = "dhgex-client-max"; + public static final String PROP_DHGEX_CLIENT_PRF_KEY = "dhgex-client-prf"; protected final DHFactory factory; protected byte expected; - protected int min = SecurityUtils.MIN_DHGEX_KEY_SIZE; + protected int min; protected int prf; protected int max; protected AbstractDH dh; protected byte[] p; protected byte[] g; - protected DHGEXClient(DHFactory factory) { + protected DHGEXClient(DHFactory factory, Session session) { + super(session); this.factory = Objects.requireNonNull(factory, "No factory"); - this.max = SecurityUtils.getMaxDHGroupExchangeKeySize(); - this.prf = Math.min(SecurityUtils.PREFERRED_DHGEX_KEY_SIZE, max); + + // SSHD-941 give the user a chance to intervene in the choice + min = PropertyResolverUtils.getIntProperty( + session, PROP_DHGEX_CLIENT_MIN_KEY, SecurityUtils.MIN_DHGEX_KEY_SIZE); + max = PropertyResolverUtils.getIntProperty( + session, PROP_DHGEX_CLIENT_MAX_KEY, SecurityUtils.getMaxDHGroupExchangeKeySize()); + prf = PropertyResolverUtils.getIntProperty( + session, PROP_DHGEX_CLIENT_PRF_KEY, Math.min(SecurityUtils.PREFERRED_DHGEX_KEY_SIZE, max)); } @Override @@ -74,8 +82,8 @@ public class DHGEXClient extends AbstractDHClientKeyExchange { } @Override - public KeyExchange create() { - return new DHGEXClient(delegate); + public KeyExchange createKeyExchange(Session session) throws Exception { + return new DHGEXClient(delegate, session); } @Override @@ -88,13 +96,10 @@ public class DHGEXClient extends AbstractDHClientKeyExchange { } @Override - public void init(Session s, byte[] v_s, byte[] v_c, byte[] i_s, byte[] i_c) throws Exception { - super.init(s, v_s, v_c, i_s, i_c); + public void init(byte[] v_s, byte[] v_c, byte[] i_s, byte[] i_c) throws Exception { + super.init(v_s, v_c, i_s, i_c); - // SSHD-941 give the user a chance to intervene in the choice - min = PropertyResolverUtils.getIntProperty(s, PROP_DHGEX_CLIENT_MIN_KEY, min); - max = PropertyResolverUtils.getIntProperty(s, PROP_DHGEX_CLIENT_MAX_KEY, max); - prf = Math.min(SecurityUtils.PREFERRED_DHGEX_KEY_SIZE, max); + Session s = getSession(); if (log.isDebugEnabled()) { log.debug("init({})[{}] Send SSH_MSG_KEX_DH_GEX_REQUEST - min={}, prf={}, max={}", this, s, min, prf, max); diff --git a/sshd-core/src/main/java/org/apache/sshd/common/BaseBuilder.java b/sshd-core/src/main/java/org/apache/sshd/common/BaseBuilder.java index 5dc33ce..904726c 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/BaseBuilder.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/BaseBuilder.java @@ -35,7 +35,7 @@ import org.apache.sshd.common.forward.DefaultForwarderFactory; import org.apache.sshd.common.forward.ForwardingFilterFactory; import org.apache.sshd.common.helpers.AbstractFactoryManager; import org.apache.sshd.common.kex.BuiltinDHFactories; -import org.apache.sshd.common.kex.KeyExchange; +import org.apache.sshd.common.kex.KeyExchangeFactory; import org.apache.sshd.common.mac.BuiltinMacs; import org.apache.sshd.common.mac.Mac; import org.apache.sshd.common.random.Random; @@ -128,7 +128,7 @@ public class BaseBuilder<T extends AbstractFactoryManager, S extends BaseBuilder DefaultUnknownChannelReferenceHandler.INSTANCE; protected Factory<T> factory; - protected List<NamedFactory<KeyExchange>> keyExchangeFactories; + protected List<KeyExchangeFactory> keyExchangeFactories; protected List<NamedFactory<Cipher>> cipherFactories; protected List<NamedFactory<Compression>> compressionFactories; protected List<NamedFactory<Mac>> macFactories; @@ -178,7 +178,7 @@ public class BaseBuilder<T extends AbstractFactoryManager, S extends BaseBuilder return me(); } - public S keyExchangeFactories(List<NamedFactory<KeyExchange>> keyExchangeFactories) { + public S keyExchangeFactories(List<KeyExchangeFactory> keyExchangeFactories) { this.keyExchangeFactories = keyExchangeFactories; return me(); } @@ -283,9 +283,9 @@ public class BaseBuilder<T extends AbstractFactoryManager, S extends BaseBuilder /** * @param ignoreUnsupported If {@code true} then all the default - * ciphers are included, regardless of whether they are currently - * supported by the JCE. Otherwise, only the supported ones out of the - * list are included + * ciphers are included, regardless of whether they are currently + * supported by the JCE. Otherwise, only the supported ones out of the + * list are included * @return A {@link List} of the default {@link NamedFactory} * instances of the {@link Cipher}s according to the preference * order defined by {@link #DEFAULT_CIPHERS_PREFERENCE}. @@ -293,14 +293,15 @@ public class BaseBuilder<T extends AbstractFactoryManager, S extends BaseBuilder * ciphers according to the <tt>ignoreUnsupported</tt> parameter * @see BuiltinCiphers#isSupported() */ + @SuppressWarnings({ "unchecked", "rawtypes" }) // safe due to the hierarchy public static List<NamedFactory<Cipher>> setUpDefaultCiphers(boolean ignoreUnsupported) { - return NamedFactory.setUpBuiltinFactories(ignoreUnsupported, DEFAULT_CIPHERS_PREFERENCE); + return (List) NamedFactory.setUpBuiltinFactories(ignoreUnsupported, DEFAULT_CIPHERS_PREFERENCE); } /** * @param ignoreUnsupported If {@code true} all the available built-in - * {@link Mac} factories are added, otherwise only those that are supported - * by the current JDK setup + * {@link Mac} factories are added, otherwise only those that are supported + * by the current JDK setup * @return A {@link List} of the default {@link NamedFactory} * instances of the {@link Mac}s according to the preference * order defined by {@link #DEFAULT_MAC_PREFERENCE}. @@ -308,7 +309,8 @@ public class BaseBuilder<T extends AbstractFactoryManager, S extends BaseBuilder * MACs according to the <tt>ignoreUnsupported</tt> parameter * @see BuiltinMacs#isSupported() */ + @SuppressWarnings({ "unchecked", "rawtypes" }) // safe due to the hierarchy public static List<NamedFactory<Mac>> setUpDefaultMacs(boolean ignoreUnsupported) { - return NamedFactory.setUpBuiltinFactories(ignoreUnsupported, DEFAULT_MAC_PREFERENCE); + return (List) NamedFactory.setUpBuiltinFactories(ignoreUnsupported, DEFAULT_MAC_PREFERENCE); } } \ No newline at end of file diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java b/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java index 46f34a1..c0205c7 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java @@ -38,6 +38,7 @@ import org.apache.sshd.common.helpers.AbstractFactoryManager; import org.apache.sshd.common.kex.BuiltinDHFactories; import org.apache.sshd.common.kex.DHFactory; import org.apache.sshd.common.kex.KeyExchange; +import org.apache.sshd.common.kex.KeyExchangeFactory; import org.apache.sshd.common.mac.BuiltinMacs; import org.apache.sshd.common.mac.Mac; import org.apache.sshd.common.signature.BuiltinSignatures; @@ -162,11 +163,13 @@ public final class SshConfigFileReader { return manager; } - public static <M extends AbstractFactoryManager> M configureCiphers(M manager, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) { + public static <M extends AbstractFactoryManager> M configureCiphers( + M manager, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) { Objects.requireNonNull(props, "No properties to configure"); return configureCiphers(manager, - props.getStringProperty(ConfigFileReaderSupport.CIPHERS_CONFIG_PROP, ConfigFileReaderSupport.DEFAULT_CIPHERS), - lenient, ignoreUnsupported); + props.getStringProperty( + ConfigFileReaderSupport.CIPHERS_CONFIG_PROP, ConfigFileReaderSupport.DEFAULT_CIPHERS), + lenient, ignoreUnsupported); } public static <M extends AbstractFactoryManager> M configureCiphers( @@ -175,18 +178,22 @@ public final class SshConfigFileReader { BuiltinCiphers.ParseResult result = BuiltinCiphers.parseCiphersList(value); Collection<String> unsupported = result.getUnsupportedFactories(); - ValidateUtils.checkTrue(lenient || GenericUtils.isEmpty(unsupported), "Unsupported cipher(s) (%s) in %s", unsupported, value); + ValidateUtils.checkTrue(lenient || GenericUtils.isEmpty(unsupported), + "Unsupported cipher(s) (%s) in %s", unsupported, value); List<NamedFactory<Cipher>> factories = - BuiltinFactory.setUpFactories(ignoreUnsupported, result.getParsedFactories()); - manager.setCipherFactories(ValidateUtils.checkNotNullAndNotEmpty(factories, "No known/unsupported ciphers(s): %s", value)); + BuiltinFactory.setUpFactories(ignoreUnsupported, result.getParsedFactories()); + manager.setCipherFactories( + ValidateUtils.checkNotNullAndNotEmpty(factories, "No known/unsupported ciphers(s): %s", value)); return manager; } - public static <M extends AbstractFactoryManager> M configureSignatures(M manager, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) { + public static <M extends AbstractFactoryManager> M configureSignatures( + M manager, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) { Objects.requireNonNull(props, "No properties to configure"); return configureSignatures(manager, - props.getStringProperty(ConfigFileReaderSupport.HOST_KEY_ALGORITHMS_CONFIG_PROP, ConfigFileReaderSupport.DEFAULT_HOST_KEY_ALGORITHMS), + props.getStringProperty( + ConfigFileReaderSupport.HOST_KEY_ALGORITHMS_CONFIG_PROP, ConfigFileReaderSupport.DEFAULT_HOST_KEY_ALGORITHMS), lenient, ignoreUnsupported); } @@ -196,11 +203,13 @@ public final class SshConfigFileReader { BuiltinSignatures.ParseResult result = BuiltinSignatures.parseSignatureList(value); Collection<String> unsupported = result.getUnsupportedFactories(); - ValidateUtils.checkTrue(lenient || GenericUtils.isEmpty(unsupported), "Unsupported signatures (%s) in %s", unsupported, value); + ValidateUtils.checkTrue(lenient || GenericUtils.isEmpty(unsupported), + "Unsupported signatures (%s) in %s", unsupported, value); List<NamedFactory<Signature>> factories = - BuiltinFactory.setUpFactories(ignoreUnsupported, result.getParsedFactories()); - manager.setSignatureFactories(ValidateUtils.checkNotNullAndNotEmpty(factories, "No known/supported signatures: %s", value)); + BuiltinFactory.setUpFactories(ignoreUnsupported, result.getParsedFactories()); + manager.setSignatureFactories( + ValidateUtils.checkNotNullAndNotEmpty(factories, "No known/supported signatures: %s", value)); return manager; } @@ -208,7 +217,8 @@ public final class SshConfigFileReader { M manager, PropertyResolver resolver, boolean lenient, boolean ignoreUnsupported) { Objects.requireNonNull(resolver, "No properties to configure"); return configureMacs(manager, - resolver.getStringProperty(ConfigFileReaderSupport.MACS_CONFIG_PROP, ConfigFileReaderSupport.DEFAULT_MACS), + resolver.getStringProperty( + ConfigFileReaderSupport.MACS_CONFIG_PROP, ConfigFileReaderSupport.DEFAULT_MACS), lenient, ignoreUnsupported); } @@ -218,11 +228,13 @@ public final class SshConfigFileReader { BuiltinMacs.ParseResult result = BuiltinMacs.parseMacsList(value); Collection<String> unsupported = result.getUnsupportedFactories(); - ValidateUtils.checkTrue(lenient || GenericUtils.isEmpty(unsupported), "Unsupported MAC(s) (%s) in %s", unsupported, value); + ValidateUtils.checkTrue(lenient || GenericUtils.isEmpty(unsupported), + "Unsupported MAC(s) (%s) in %s", unsupported, value); List<NamedFactory<Mac>> factories = - BuiltinFactory.setUpFactories(ignoreUnsupported, result.getParsedFactories()); - manager.setMacFactories(ValidateUtils.checkNotNullAndNotEmpty(factories, "No known/supported MAC(s): %s", value)); + BuiltinFactory.setUpFactories(ignoreUnsupported, result.getParsedFactories()); + manager.setMacFactories( + ValidateUtils.checkNotNullAndNotEmpty(factories, "No known/supported MAC(s): %s", value)); return manager; } @@ -242,25 +254,30 @@ public final class SshConfigFileReader { * @see ConfigFileReaderSupport#DEFAULT_KEX_ALGORITHMS DEFAULT_KEX_ALGORITHMS */ public static <M extends AbstractFactoryManager> M configureKeyExchanges( - M manager, PropertyResolver props, boolean lenient, Function<? super DHFactory, ? extends NamedFactory<KeyExchange>> xformer, boolean ignoreUnsupported) { + M manager, PropertyResolver props, boolean lenient, + Function<? super DHFactory, ? extends KeyExchangeFactory> xformer, boolean ignoreUnsupported) { Objects.requireNonNull(props, "No properties to configure"); return configureKeyExchanges(manager, - props.getStringProperty(ConfigFileReaderSupport.KEX_ALGORITHMS_CONFIG_PROP, ConfigFileReaderSupport.DEFAULT_KEX_ALGORITHMS), + props.getStringProperty( + ConfigFileReaderSupport.KEX_ALGORITHMS_CONFIG_PROP, ConfigFileReaderSupport.DEFAULT_KEX_ALGORITHMS), lenient, xformer, ignoreUnsupported); } public static <M extends AbstractFactoryManager> M configureKeyExchanges( - M manager, String value, boolean lenient, Function<? super DHFactory, ? extends NamedFactory<KeyExchange>> xformer, boolean ignoreUnsupported) { + M manager, String value, boolean lenient, + Function<? super DHFactory, ? extends KeyExchangeFactory> xformer, boolean ignoreUnsupported) { Objects.requireNonNull(manager, "No manager to configure"); Objects.requireNonNull(xformer, "No DHFactory transformer"); BuiltinDHFactories.ParseResult result = BuiltinDHFactories.parseDHFactoriesList(value); Collection<String> unsupported = result.getUnsupportedFactories(); - ValidateUtils.checkTrue(lenient || GenericUtils.isEmpty(unsupported), "Unsupported KEX(s) (%s) in %s", unsupported, value); + ValidateUtils.checkTrue(lenient || GenericUtils.isEmpty(unsupported), + "Unsupported KEX(s) (%s) in %s", unsupported, value); - List<NamedFactory<KeyExchange>> factories = - NamedFactory.setUpTransformedFactories(ignoreUnsupported, result.getParsedFactories(), xformer); - manager.setKeyExchangeFactories(ValidateUtils.checkNotNullAndNotEmpty(factories, "No known/supported KEXS(s): %s", value)); + List<KeyExchangeFactory> factories = + NamedFactory.setUpTransformedFactories(ignoreUnsupported, result.getParsedFactories(), xformer); + manager.setKeyExchangeFactories( + ValidateUtils.checkNotNullAndNotEmpty(factories, "No known/supported KEXS(s): %s", value)); return manager; } diff --git a/sshd-core/src/main/java/org/apache/sshd/common/kex/AbstractKexFactoryManager.java b/sshd-core/src/main/java/org/apache/sshd/common/kex/AbstractKexFactoryManager.java index 8f1d159..b4cd952 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/kex/AbstractKexFactoryManager.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/kex/AbstractKexFactoryManager.java @@ -19,6 +19,7 @@ package org.apache.sshd.common.kex; +import java.util.Collection; import java.util.Collections; import java.util.List; @@ -38,7 +39,7 @@ public abstract class AbstractKexFactoryManager extends AbstractInnerCloseable implements KexFactoryManager { private final KexFactoryManager delegate; - private List<NamedFactory<KeyExchange>> keyExchangeFactories; + private List<KeyExchangeFactory> keyExchangeFactories; private List<NamedFactory<Cipher>> cipherFactories; private List<NamedFactory<Compression>> compressionFactories; private List<NamedFactory<Mac>> macFactories; @@ -58,14 +59,14 @@ public abstract class AbstractKexFactoryManager } @Override - public List<NamedFactory<KeyExchange>> getKeyExchangeFactories() { + public List<KeyExchangeFactory> getKeyExchangeFactories() { KexFactoryManager parent = getDelegate(); return resolveEffectiveFactories(keyExchangeFactories, (parent == null) ? Collections.emptyList() : parent.getKeyExchangeFactories()); } @Override - public void setKeyExchangeFactories(List<NamedFactory<KeyExchange>> keyExchangeFactories) { + public void setKeyExchangeFactories(List<KeyExchangeFactory> keyExchangeFactories) { this.keyExchangeFactories = keyExchangeFactories; } @@ -129,7 +130,7 @@ public abstract class AbstractKexFactoryManager this.kexExtensionHandler = kexExtensionHandler; } - protected <V> List<V> resolveEffectiveFactories(List<V> local, List<V> inherited) { + protected <V, C extends Collection<V>> C resolveEffectiveFactories(C local, C inherited) { if (GenericUtils.isEmpty(local)) { return inherited; } else { diff --git a/sshd-core/src/main/java/org/apache/sshd/common/kex/KexFactoryManager.java b/sshd-core/src/main/java/org/apache/sshd/common/kex/KexFactoryManager.java index 6c873d2..a537227 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/kex/KexFactoryManager.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/kex/KexFactoryManager.java @@ -47,9 +47,9 @@ public interface KexFactoryManager extends SignatureFactoriesManager, KexExtensi * * @return a list of named <code>KeyExchange</code> factories, never {@code null} */ - List<NamedFactory<KeyExchange>> getKeyExchangeFactories(); + List<KeyExchangeFactory> getKeyExchangeFactories(); - void setKeyExchangeFactories(List<NamedFactory<KeyExchange>> keyExchangeFactories); + void setKeyExchangeFactories(List<KeyExchangeFactory> keyExchangeFactories); /** * Retrieve the list of named factories for <code>Cipher</code>. diff --git a/sshd-core/src/main/java/org/apache/sshd/common/kex/KeyExchange.java b/sshd-core/src/main/java/org/apache/sshd/common/kex/KeyExchange.java index f1b23b6..b47f0bc 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/kex/KeyExchange.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/kex/KeyExchange.java @@ -26,6 +26,7 @@ import org.apache.sshd.common.NamedResource; import org.apache.sshd.common.SshConstants; import org.apache.sshd.common.digest.Digest; import org.apache.sshd.common.session.Session; +import org.apache.sshd.common.session.SessionHolder; import org.apache.sshd.common.util.GenericUtils; import org.apache.sshd.common.util.buffer.Buffer; import org.apache.sshd.common.util.logging.LoggingUtils; @@ -35,7 +36,7 @@ import org.apache.sshd.common.util.logging.LoggingUtils; * * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> */ -public interface KeyExchange extends NamedResource { +public interface KeyExchange extends NamedResource, SessionHolder<Session> { NavigableMap<Integer, String> GROUP_KEX_OPCODES_MAP = Collections.unmodifiableNavigableMap( LoggingUtils.generateMnemonicMap(SshConstants.class, "SSH_MSG_KEX_DH_GEX_")); @@ -47,14 +48,13 @@ public interface KeyExchange extends NamedResource { /** * Initialize the key exchange algorithm. * - * @param session the session using this algorithm * @param v_s the server identification string * @param v_c the client identification string * @param i_s the server key initialization packet * @param i_c the client key initialization packet * @throws Exception if an error occurs */ - void init(Session session, byte[] v_s, byte[] v_c, byte[] i_s, byte[] i_c) throws Exception; + void init(byte[] v_s, byte[] v_c, byte[] i_s, byte[] i_c) throws Exception; /** * Process the next packet diff --git a/sshd-core/src/main/java/org/apache/sshd/common/kex/KeyExchangeFactory.java b/sshd-core/src/main/java/org/apache/sshd/common/kex/KeyExchangeFactory.java index cea659d..f9288e2 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/kex/KeyExchangeFactory.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/kex/KeyExchangeFactory.java @@ -19,13 +19,18 @@ package org.apache.sshd.common.kex; -import org.apache.sshd.common.NamedFactory; +import org.apache.sshd.common.NamedResource; +import org.apache.sshd.common.session.Session; /** * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> */ -// CHECKSTYLE:OFF -public interface KeyExchangeFactory extends NamedFactory<KeyExchange> { - // nothing extra +public interface KeyExchangeFactory extends NamedResource { + /** + * @param session The {@link Session} for which the factory is invoked + * @return The {@link KeyExchange} instance to be used + * @throws Exception If failed to create + */ + KeyExchange createKeyExchange(Session session) throws Exception; } -//CHECKSTYLE:ON + diff --git a/sshd-core/src/main/java/org/apache/sshd/common/kex/dh/AbstractDHKeyExchange.java b/sshd-core/src/main/java/org/apache/sshd/common/kex/dh/AbstractDHKeyExchange.java index 8bcfdae..7f29782 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/kex/dh/AbstractDHKeyExchange.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/kex/dh/AbstractDHKeyExchange.java @@ -19,19 +19,18 @@ package org.apache.sshd.common.kex.dh; +import java.util.Objects; + import org.apache.sshd.common.digest.Digest; import org.apache.sshd.common.kex.KeyExchange; import org.apache.sshd.common.session.Session; -import org.apache.sshd.common.session.SessionHolder; -import org.apache.sshd.common.session.helpers.AbstractSession; import org.apache.sshd.common.util.ValidateUtils; import org.apache.sshd.common.util.logging.AbstractLoggingBean; /** * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> */ -public abstract class AbstractDHKeyExchange extends AbstractLoggingBean implements KeyExchange, SessionHolder<AbstractSession> { - +public abstract class AbstractDHKeyExchange extends AbstractLoggingBean implements KeyExchange { protected byte[] v_s; protected byte[] v_c; protected byte[] i_s; @@ -42,15 +41,14 @@ public abstract class AbstractDHKeyExchange extends AbstractLoggingBean implemen protected byte[] k; protected byte[] h; - private AbstractSession session; + private final Session session; - protected AbstractDHKeyExchange() { - super(); + protected AbstractDHKeyExchange(Session session) { + this.session = Objects.requireNonNull(session, "No session provided"); } @Override - public void init(Session s, byte[] v_s, byte[] v_c, byte[] i_s, byte[] i_c) throws Exception { - this.session = ValidateUtils.checkInstanceOf(s, AbstractSession.class, "Not an abstract session: %s", s); + public void init(byte[] v_s, byte[] v_c, byte[] i_s, byte[] i_c) throws Exception { this.v_s = ValidateUtils.checkNotNullAndNotEmpty(v_s, "No v_s value"); this.v_c = ValidateUtils.checkNotNullAndNotEmpty(v_c, "No v_c value"); this.i_s = ValidateUtils.checkNotNullAndNotEmpty(i_s, "No i_s value"); @@ -58,7 +56,7 @@ public abstract class AbstractDHKeyExchange extends AbstractLoggingBean implemen } @Override - public AbstractSession getSession() { + public Session getSession() { return session; } diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/SessionHolder.java b/sshd-core/src/main/java/org/apache/sshd/common/session/SessionHolder.java index 280bbf0..3a6491d 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/session/SessionHolder.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/session/SessionHolder.java @@ -24,6 +24,11 @@ package org.apache.sshd.common.session; * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> */ @FunctionalInterface -public interface SessionHolder<S extends Session> { +public interface SessionHolder<S extends Session> extends SessionContextHolder { + @Override + default SessionContext getSessionContext() { + return getSession(); + } + S getSession(); } diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java index cc5c926..4bb43e2 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/session/helpers/AbstractSession.java @@ -44,6 +44,7 @@ import org.apache.sshd.common.Closeable; import org.apache.sshd.common.Factory; import org.apache.sshd.common.FactoryManager; import org.apache.sshd.common.NamedFactory; +import org.apache.sshd.common.NamedResource; import org.apache.sshd.common.RuntimeSshException; import org.apache.sshd.common.Service; import org.apache.sshd.common.SshConstants; @@ -63,6 +64,7 @@ import org.apache.sshd.common.io.IoWriteFuture; import org.apache.sshd.common.kex.KexProposalOption; import org.apache.sshd.common.kex.KexState; import org.apache.sshd.common.kex.KeyExchange; +import org.apache.sshd.common.kex.KeyExchangeFactory; import org.apache.sshd.common.kex.extension.KexExtensionHandler; import org.apache.sshd.common.kex.extension.KexExtensionHandler.AvailabilityPhase; import org.apache.sshd.common.kex.extension.KexExtensionHandler.KexPhase; @@ -639,12 +641,12 @@ public abstract class AbstractSession extends SessionHelper { Map<KexProposalOption, String> result = negotiate(); String kexAlgorithm = result.get(KexProposalOption.ALGORITHMS); - Collection<? extends NamedFactory<KeyExchange>> kexFactories = getKeyExchangeFactories(); + Collection<? extends KeyExchangeFactory> kexFactories = getKeyExchangeFactories(); + KeyExchangeFactory kexFactory = NamedResource.findByName( + kexAlgorithm, String.CASE_INSENSITIVE_ORDER, kexFactories); + ValidateUtils.checkNotNull(kexFactory, "Unknown negotiated KEX algorithm: %s", kexAlgorithm); synchronized (pendingPackets) { - kex = ValidateUtils.checkNotNull( - NamedFactory.create(kexFactories, kexAlgorithm), - "Unknown negotiated KEX algorithm: %s", - kexAlgorithm); + kex = kexFactory.createKeyExchange(this); } byte[] v_s = serverVersion.getBytes(StandardCharsets.UTF_8); @@ -655,7 +657,7 @@ public abstract class AbstractSession extends SessionHelper { i_s = getServerKexData(); i_c = getClientKexData(); } - kex.init(this, v_s, v_c, i_s, i_c); + kex.init(v_s, v_c, i_s, i_c); signalSessionEvent(SessionListener.Event.KexCompleted); } diff --git a/sshd-core/src/main/java/org/apache/sshd/server/ServerBuilder.java b/sshd-core/src/main/java/org/apache/sshd/server/ServerBuilder.java index 0510141..9dbfac1 100644 --- a/sshd-core/src/main/java/org/apache/sshd/server/ServerBuilder.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/ServerBuilder.java @@ -29,11 +29,14 @@ import org.apache.sshd.common.NamedFactory; import org.apache.sshd.common.channel.ChannelFactory; import org.apache.sshd.common.channel.RequestHandler; import org.apache.sshd.common.compression.BuiltinCompressions; +import org.apache.sshd.common.compression.Compression; import org.apache.sshd.common.compression.CompressionFactory; import org.apache.sshd.common.kex.DHFactory; import org.apache.sshd.common.kex.KeyExchange; +import org.apache.sshd.common.kex.KeyExchangeFactory; import org.apache.sshd.common.session.ConnectionService; import org.apache.sshd.common.signature.BuiltinSignatures; +import org.apache.sshd.common.signature.Signature; import org.apache.sshd.server.auth.keyboard.DefaultKeyboardInteractiveAuthenticator; import org.apache.sshd.server.auth.keyboard.KeyboardInteractiveAuthenticator; import org.apache.sshd.server.auth.pubkey.PublickeyAuthenticator; @@ -52,8 +55,9 @@ import org.apache.sshd.server.kex.DHGServer; * SshServer builder */ public class ServerBuilder extends BaseBuilder<SshServer, ServerBuilder> { - - public static final Function<DHFactory, NamedFactory<KeyExchange>> DH2KEX = factory -> + @SuppressWarnings("checkstyle:Indentation") + public static final Function<DHFactory, KeyExchangeFactory> DH2KEX = + factory -> factory == null ? null : factory.isGroupExchange() @@ -126,11 +130,11 @@ public class ServerBuilder extends BaseBuilder<SshServer, ServerBuilder> { super.fillWithDefaultValues(); if (compressionFactories == null) { - compressionFactories = NamedFactory.setUpBuiltinFactories(false, DEFAULT_COMPRESSION_FACTORIES); + compressionFactories = setUpDefaultCompressionFactories(false); } if (signatureFactories == null) { - signatureFactories = NamedFactory.setUpBuiltinFactories(false, DEFAULT_SIGNATURE_PREFERENCE); + signatureFactories = setUpDefaultSignatureFactories(false); } if (keyExchangeFactories == null) { @@ -168,6 +172,16 @@ public class ServerBuilder extends BaseBuilder<SshServer, ServerBuilder> { return server; } + @SuppressWarnings({ "unchecked", "rawtypes" }) // safe due to the hierarchy + public static List<NamedFactory<Signature>> setUpDefaultSignatureFactories(boolean ignoreUnsupported) { + return (List) NamedFactory.setUpBuiltinFactories(ignoreUnsupported, DEFAULT_SIGNATURE_PREFERENCE); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) // safe due to the hierarchy + public static List<NamedFactory<Compression>> setUpDefaultCompressionFactories(boolean ignoreUnsupported) { + return (List) NamedFactory.setUpBuiltinFactories(ignoreUnsupported, DEFAULT_COMPRESSION_FACTORIES); + } + /** * @param ignoreUnsupported If {@code true} then all the default * key exchanges are included, regardless of whether they are currently @@ -180,7 +194,7 @@ public class ServerBuilder extends BaseBuilder<SshServer, ServerBuilder> { * key exchanges according to the <tt>ignoreUnsupported</tt> parameter * @see org.apache.sshd.common.kex.BuiltinDHFactories#isSupported() */ - public static List<NamedFactory<KeyExchange>> setUpDefaultKeyExchanges(boolean ignoreUnsupported) { + public static List<KeyExchangeFactory> setUpDefaultKeyExchanges(boolean ignoreUnsupported) { return NamedFactory.setUpTransformedFactories(ignoreUnsupported, DEFAULT_KEX_PREFERENCE, DH2KEX); } diff --git a/sshd-core/src/main/java/org/apache/sshd/server/kex/AbstractDHServerKeyExchange.java b/sshd-core/src/main/java/org/apache/sshd/server/kex/AbstractDHServerKeyExchange.java index 831086c..7c11ec5 100644 --- a/sshd-core/src/main/java/org/apache/sshd/server/kex/AbstractDHServerKeyExchange.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/kex/AbstractDHServerKeyExchange.java @@ -32,8 +32,8 @@ import org.apache.sshd.server.session.ServerSessionHolder; * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> */ public abstract class AbstractDHServerKeyExchange extends AbstractDHKeyExchange implements ServerSessionHolder { - protected AbstractDHServerKeyExchange() { - super(); + protected AbstractDHServerKeyExchange(Session s) { + super(ValidateUtils.checkInstanceOf(s, ServerSession.class, "Using a client side KeyExchange on a server: %s", s)); } @Override @@ -42,12 +42,6 @@ public abstract class AbstractDHServerKeyExchange extends AbstractDHKeyExchange } @Override - public void init(Session s, byte[] v_s, byte[] v_c, byte[] i_s, byte[] i_c) throws Exception { - super.init(s, v_s, v_c, i_s, i_c); - ValidateUtils.checkInstanceOf(s, ServerSession.class, "Using a server side KeyExchange on a client: %s", s); - } - - @Override public PublicKey getServerKey() { ServerSession session = getServerSession(); return Objects.requireNonNull(session.getHostKey(), "No server key pair available").getPublic(); diff --git a/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGEXServer.java b/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGEXServer.java index 7a4b8f2..27ed991 100644 --- a/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGEXServer.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGEXServer.java @@ -67,7 +67,8 @@ public class DHGEXServer extends AbstractDHServerKeyExchange { protected byte expected; protected boolean oldRequest; - protected DHGEXServer(DHFactory factory) { + protected DHGEXServer(DHFactory factory, Session session) { + super(session); this.factory = Objects.requireNonNull(factory, "No factory"); } @@ -79,8 +80,8 @@ public class DHGEXServer extends AbstractDHServerKeyExchange { public static KeyExchangeFactory newFactory(DHFactory factory) { return new KeyExchangeFactory() { @Override - public KeyExchange create() { - return new DHGEXServer(factory); + public KeyExchange createKeyExchange(Session session) throws Exception { + return new DHGEXServer(factory, session); } @Override @@ -98,8 +99,8 @@ public class DHGEXServer extends AbstractDHServerKeyExchange { } @Override - public void init(Session s, byte[] v_s, byte[] v_c, byte[] i_s, byte[] i_c) throws Exception { - super.init(s, v_s, v_c, i_s, i_c); + public void init(byte[] v_s, byte[] v_c, byte[] i_s, byte[] i_c) throws Exception { + super.init(v_s, v_c, i_s, i_c); expected = SshConstants.SSH_MSG_KEX_DH_GEX_REQUEST; } diff --git a/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGServer.java b/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGServer.java index d74353b..e5dad22 100644 --- a/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGServer.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGServer.java @@ -45,7 +45,8 @@ public class DHGServer extends AbstractDHServerKeyExchange { protected final DHFactory factory; protected AbstractDH dh; - protected DHGServer(DHFactory factory) { + protected DHGServer(DHFactory factory, Session session) { + super(session); this.factory = Objects.requireNonNull(factory, "No factory"); } @@ -57,8 +58,8 @@ public class DHGServer extends AbstractDHServerKeyExchange { public static KeyExchangeFactory newFactory(final DHFactory factory) { return new KeyExchangeFactory() { @Override - public KeyExchange create() { - return new DHGServer(factory); + public KeyExchange createKeyExchange(Session session) throws Exception { + return new DHGServer(factory, session); } @Override @@ -76,8 +77,8 @@ public class DHGServer extends AbstractDHServerKeyExchange { } @Override - public void init(Session s, byte[] v_s, byte[] v_c, byte[] i_s, byte[] i_c) throws Exception { - super.init(s, v_s, v_c, i_s, i_c); + public void init(byte[] v_s, byte[] v_c, byte[] i_s, byte[] i_c) throws Exception { + super.init(v_s, v_c, i_s, i_c); dh = factory.create(); hash = dh.getHash(); hash.init(); diff --git a/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java b/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java index 9390383..13bcd56 100644 --- a/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java +++ b/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java @@ -42,13 +42,13 @@ import org.apache.sshd.client.channel.ClientChannel; import org.apache.sshd.client.channel.ClientChannelEvent; import org.apache.sshd.client.session.ClientSession; import org.apache.sshd.common.FactoryManager; -import org.apache.sshd.common.NamedFactory; import org.apache.sshd.common.PropertyResolverUtils; import org.apache.sshd.common.channel.Channel; import org.apache.sshd.common.cipher.BuiltinCiphers; import org.apache.sshd.common.future.KeyExchangeFuture; import org.apache.sshd.common.kex.BuiltinDHFactories; import org.apache.sshd.common.kex.KeyExchange; +import org.apache.sshd.common.kex.KeyExchangeFactory; import org.apache.sshd.common.session.Session; import org.apache.sshd.common.session.SessionListener; import org.apache.sshd.common.util.io.NullOutputStream; @@ -150,21 +150,21 @@ public class KeyReExchangeTest extends BaseTestSupport { try (SshClient client = setupTestClient()) { client.getCipherFactories().add(BuiltinCiphers.none); // replace the original KEX factories with wrapped ones that we can fail intentionally - List<NamedFactory<KeyExchange>> kexFactories = new ArrayList<>(); - final AtomicBoolean successfulInit = new AtomicBoolean(true); - final AtomicBoolean successfulNext = new AtomicBoolean(true); - final ClassLoader loader = getClass().getClassLoader(); - final Class<?>[] interfaces = {KeyExchange.class}; - for (final NamedFactory<KeyExchange> factory : client.getKeyExchangeFactories()) { - kexFactories.add(new NamedFactory<KeyExchange>() { + List<KeyExchangeFactory> kexFactories = new ArrayList<>(); + AtomicBoolean successfulInit = new AtomicBoolean(true); + AtomicBoolean successfulNext = new AtomicBoolean(true); + ClassLoader loader = getClass().getClassLoader(); + Class<?>[] interfaces = {KeyExchange.class}; + for (KeyExchangeFactory factory : client.getKeyExchangeFactories()) { + kexFactories.add(new KeyExchangeFactory() { @Override public String getName() { return factory.getName(); } @Override - public KeyExchange create() { - final KeyExchange proxiedInstance = factory.create(); + public KeyExchange createKeyExchange(Session s) throws Exception { + KeyExchange proxiedInstance = factory.createKeyExchange(s); return (KeyExchange) Proxy.newProxyInstance(loader, interfaces, (proxy, method, args) -> { String name = method.getName(); if ("init".equals(name) && (!successfulInit.get())) { diff --git a/sshd-core/src/test/java/org/apache/sshd/client/ClientSessionListenerTest.java b/sshd-core/src/test/java/org/apache/sshd/client/ClientSessionListenerTest.java index e5e928a..8669ba8 100644 --- a/sshd-core/src/test/java/org/apache/sshd/client/ClientSessionListenerTest.java +++ b/sshd-core/src/test/java/org/apache/sshd/client/ClientSessionListenerTest.java @@ -31,10 +31,14 @@ import org.apache.sshd.client.auth.keyboard.UserInteraction; import org.apache.sshd.client.keyverifier.ServerKeyVerifier; import org.apache.sshd.client.session.ClientSession; import org.apache.sshd.common.NamedFactory; +import org.apache.sshd.common.NamedResource; import org.apache.sshd.common.cipher.Cipher; +import org.apache.sshd.common.cipher.CipherFactory; import org.apache.sshd.common.kex.KexProposalOption; import org.apache.sshd.common.kex.KeyExchange; +import org.apache.sshd.common.kex.KeyExchangeFactory; import org.apache.sshd.common.mac.Mac; +import org.apache.sshd.common.mac.MacFactory; import org.apache.sshd.common.session.Session; import org.apache.sshd.common.session.SessionListener; import org.apache.sshd.common.util.GenericUtils; @@ -91,18 +95,21 @@ public class ClientSessionListenerTest extends BaseTestSupport { @Test public void testSessionListenerCanModifyKEXNegotiation() throws Exception { - final Map<KexProposalOption, NamedFactory<?>> kexParams = new EnumMap<>(KexProposalOption.class); + Map<KexProposalOption, NamedResource> kexParams = new EnumMap<>(KexProposalOption.class); kexParams.put(KexProposalOption.ALGORITHMS, getLeastFavorite(KeyExchange.class, client.getKeyExchangeFactories())); - kexParams.put(KexProposalOption.C2SENC, getLeastFavorite(Cipher.class, client.getCipherFactories())); - kexParams.put(KexProposalOption.C2SMAC, getLeastFavorite(Mac.class, client.getMacFactories())); + kexParams.put(KexProposalOption.C2SENC, getLeastFavorite(CipherFactory.class, client.getCipherFactories())); + kexParams.put(KexProposalOption.C2SMAC, getLeastFavorite(MacFactory.class, client.getMacFactories())); SessionListener listener = new SessionListener() { @Override @SuppressWarnings("unchecked") public void sessionCreated(Session session) { - session.setKeyExchangeFactories(Collections.singletonList((NamedFactory<KeyExchange>) kexParams.get(KexProposalOption.ALGORITHMS))); - session.setCipherFactories(Collections.singletonList((NamedFactory<Cipher>) kexParams.get(KexProposalOption.C2SENC))); - session.setMacFactories(Collections.singletonList((NamedFactory<Mac>) kexParams.get(KexProposalOption.C2SMAC))); + session.setKeyExchangeFactories( + Collections.singletonList((KeyExchangeFactory) kexParams.get(KexProposalOption.ALGORITHMS))); + session.setCipherFactories( + Collections.singletonList((NamedFactory<Cipher>) kexParams.get(KexProposalOption.C2SENC))); + session.setMacFactories( + Collections.singletonList((NamedFactory<Mac>) kexParams.get(KexProposalOption.C2SMAC))); } }; client.addSessionListener(listener); @@ -120,16 +127,17 @@ public class ClientSessionListenerTest extends BaseTestSupport { @Test public void testSessionListenerCanInfluenceAuthentication() throws IOException { - final AtomicInteger verificationCount = new AtomicInteger(); - final ServerKeyVerifier verifier = (sshClientSession, remoteAddress, serverKey) -> { + AtomicInteger verificationCount = new AtomicInteger(); + ServerKeyVerifier verifier = (sshClientSession, remoteAddress, serverKey) -> { verificationCount.incrementAndGet(); return true; }; - SessionListener listener = new SessionListener() { @Override public void sessionEvent(Session session, Event event) { - if ((!session.isAuthenticated()) && (session instanceof ClientSession) && Event.KexCompleted.equals(event)) { + if ((!session.isAuthenticated()) + && (session instanceof ClientSession) + && Event.KexCompleted.equals(event)) { ClientSession clientSession = (ClientSession) session; clientSession.setServerKeyVerifier(verifier); clientSession.setUserInteraction(UserInteraction.NONE); @@ -149,14 +157,18 @@ public class ClientSessionListenerTest extends BaseTestSupport { } } - private static <V> NamedFactory<V> getLeastFavorite(Class<V> type, List<? extends NamedFactory<V>> factories) { + private static <V extends NamedResource> NamedResource getLeastFavorite( + Class<V> type, List<? extends NamedResource> factories) { int numFactories = GenericUtils.size(factories); assertTrue("No factories for " + type.getSimpleName(), numFactories > 0); return factories.get(numFactories - 1); } private ClientSession createTestClientSession() throws IOException { - ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession(); + ClientSession session = + client.connect(getCurrentTestName(), TEST_LOCALHOST, port) + .verify(7L, TimeUnit.SECONDS) + .getSession(); try { session.addPasswordIdentity(getCurrentTestName()); session.auth().verify(5L, TimeUnit.SECONDS); diff --git a/sshd-core/src/test/java/org/apache/sshd/client/kex/KexTest.java b/sshd-core/src/test/java/org/apache/sshd/client/kex/KexTest.java index 678521f..da599ba 100644 --- a/sshd-core/src/test/java/org/apache/sshd/client/kex/KexTest.java +++ b/sshd-core/src/test/java/org/apache/sshd/client/kex/KexTest.java @@ -34,10 +34,9 @@ import org.apache.sshd.client.SshClient; import org.apache.sshd.client.channel.ClientChannel; import org.apache.sshd.client.channel.ClientChannelEvent; import org.apache.sshd.client.session.ClientSession; -import org.apache.sshd.common.NamedFactory; import org.apache.sshd.common.channel.Channel; import org.apache.sshd.common.kex.BuiltinDHFactories; -import org.apache.sshd.common.kex.KeyExchange; +import org.apache.sshd.common.kex.KeyExchangeFactory; import org.apache.sshd.common.util.security.SecurityUtils; import org.apache.sshd.server.SshServer; import org.apache.sshd.util.test.BaseTestSupport; @@ -119,12 +118,15 @@ public class KexTest extends BaseTestSupport { testClient(ClientBuilder.DH2KEX.apply(factory)); } - private void testClient(NamedFactory<KeyExchange> kex) throws Exception { + private void testClient(KeyExchangeFactory kex) throws Exception { try (ByteArrayOutputStream sent = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream()) { client.setKeyExchangeFactories(Collections.singletonList(kex)); - 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); @@ -153,7 +155,7 @@ public class KexTest extends BaseTestSupport { teeOut.flush(); Collection<ClientChannelEvent> result = - channel.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), TimeUnit.SECONDS.toMillis(15L)); + channel.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), TimeUnit.SECONDS.toMillis(15L)); assertFalse("Timeout while waiting for channel closure", result.contains(ClientChannelEvent.TIMEOUT)); } } diff --git a/sshd-core/src/test/java/org/apache/sshd/common/kex/KexFactoryManagerTest.java b/sshd-core/src/test/java/org/apache/sshd/common/kex/KexFactoryManagerTest.java index 0832ab4..acfd70a 100644 --- a/sshd-core/src/test/java/org/apache/sshd/common/kex/KexFactoryManagerTest.java +++ b/sshd-core/src/test/java/org/apache/sshd/common/kex/KexFactoryManagerTest.java @@ -140,12 +140,12 @@ public class KexFactoryManagerTest extends BaseTestSupport { } @Override - public List<NamedFactory<KeyExchange>> getKeyExchangeFactories() { + public List<KeyExchangeFactory> getKeyExchangeFactories() { return null; } @Override - public void setKeyExchangeFactories(List<NamedFactory<KeyExchange>> keyExchangeFactories) { + public void setKeyExchangeFactories(List<KeyExchangeFactory> keyExchangeFactories) { throw new UnsupportedOperationException("N/A"); } diff --git a/sshd-core/src/test/java/org/apache/sshd/server/ServerSessionListenerTest.java b/sshd-core/src/test/java/org/apache/sshd/server/ServerSessionListenerTest.java index 50d83ed..ffa54a4 100644 --- a/sshd-core/src/test/java/org/apache/sshd/server/ServerSessionListenerTest.java +++ b/sshd-core/src/test/java/org/apache/sshd/server/ServerSessionListenerTest.java @@ -32,11 +32,14 @@ import java.util.concurrent.atomic.AtomicInteger; import org.apache.sshd.client.SshClient; import org.apache.sshd.client.session.ClientSession; import org.apache.sshd.common.NamedFactory; +import org.apache.sshd.common.NamedResource; import org.apache.sshd.common.cipher.Cipher; +import org.apache.sshd.common.cipher.CipherFactory; import org.apache.sshd.common.io.IoSession; import org.apache.sshd.common.kex.KexProposalOption; -import org.apache.sshd.common.kex.KeyExchange; +import org.apache.sshd.common.kex.KeyExchangeFactory; import org.apache.sshd.common.mac.Mac; +import org.apache.sshd.common.mac.MacFactory; import org.apache.sshd.common.session.Session; import org.apache.sshd.common.session.SessionListener; import org.apache.sshd.common.util.GenericUtils; @@ -165,18 +168,21 @@ public class ServerSessionListenerTest extends BaseTestSupport { @Test public void testSessionListenerCanModifyKEXNegotiation() throws Exception { - final Map<KexProposalOption, NamedFactory<?>> kexParams = new EnumMap<>(KexProposalOption.class); - kexParams.put(KexProposalOption.ALGORITHMS, getLeastFavorite(KeyExchange.class, sshd.getKeyExchangeFactories())); - kexParams.put(KexProposalOption.S2CENC, getLeastFavorite(Cipher.class, sshd.getCipherFactories())); - kexParams.put(KexProposalOption.S2CMAC, getLeastFavorite(Mac.class, sshd.getMacFactories())); + Map<KexProposalOption, NamedResource> kexParams = new EnumMap<>(KexProposalOption.class); + kexParams.put(KexProposalOption.ALGORITHMS, getLeastFavorite(KeyExchangeFactory.class, sshd.getKeyExchangeFactories())); + kexParams.put(KexProposalOption.S2CENC, getLeastFavorite(CipherFactory.class, sshd.getCipherFactories())); + kexParams.put(KexProposalOption.S2CMAC, getLeastFavorite(MacFactory.class, sshd.getMacFactories())); SessionListener listener = new SessionListener() { @Override @SuppressWarnings("unchecked") public void sessionCreated(Session session) { - session.setKeyExchangeFactories(Collections.singletonList((NamedFactory<KeyExchange>) kexParams.get(KexProposalOption.ALGORITHMS))); - session.setCipherFactories(Collections.singletonList((NamedFactory<Cipher>) kexParams.get(KexProposalOption.S2CENC))); - session.setMacFactories(Collections.singletonList((NamedFactory<Mac>) kexParams.get(KexProposalOption.S2CMAC))); + session.setKeyExchangeFactories( + Collections.singletonList((KeyExchangeFactory) kexParams.get(KexProposalOption.ALGORITHMS))); + session.setCipherFactories( + Collections.singletonList((NamedFactory<Cipher>) kexParams.get(KexProposalOption.S2CENC))); + session.setMacFactories( + Collections.singletonList((NamedFactory<Mac>) kexParams.get(KexProposalOption.S2CMAC))); } }; sshd.addSessionListener(listener); @@ -194,9 +200,9 @@ public class ServerSessionListenerTest extends BaseTestSupport { @Test public void testSessionListenerCanModifyAuthentication() throws Exception { - final AtomicInteger passCount = new AtomicInteger(0); - final PasswordAuthenticator defaultPassAuth = sshd.getPasswordAuthenticator(); - final PasswordAuthenticator passAuth = (username, password, session) -> { + AtomicInteger passCount = new AtomicInteger(0); + PasswordAuthenticator defaultPassAuth = sshd.getPasswordAuthenticator(); + PasswordAuthenticator passAuth = (username, password, session) -> { passCount.incrementAndGet(); return defaultPassAuth.authenticate(username, password, session); }; @@ -215,22 +221,28 @@ public class ServerSessionListenerTest extends BaseTestSupport { sshd.addSessionListener(listener); try (ClientSession session = createTestClientSession()) { - assertNotSame("Mismatched default password authenticator", passAuth, sshd.getPasswordAuthenticator()); - assertNotSame("Mismatched default kb authenticator", KeyboardInteractiveAuthenticator.NONE, sshd.getKeyboardInteractiveAuthenticator()); + assertNotSame("Mismatched default password authenticator", + passAuth, sshd.getPasswordAuthenticator()); + assertNotSame("Mismatched default kb authenticator", + KeyboardInteractiveAuthenticator.NONE, sshd.getKeyboardInteractiveAuthenticator()); assertEquals("Authenticator override not invoked", 1, passCount.get()); } finally { sshd.removeSessionListener(listener); } } - private static <V> NamedFactory<V> getLeastFavorite(Class<V> type, List<? extends NamedFactory<V>> factories) { + private static <V extends NamedResource> NamedResource getLeastFavorite( + Class<V> type, List<? extends NamedResource> factories) { int numFactories = GenericUtils.size(factories); assertTrue("No factories for " + type.getSimpleName(), numFactories > 0); return factories.get(numFactories - 1); } private ClientSession createTestClientSession() throws Exception { - ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession(); + ClientSession session = + client.connect(getCurrentTestName(), TEST_LOCALHOST, port) + .verify(7L, TimeUnit.SECONDS) + .getSession(); try { session.addPasswordIdentity(getCurrentTestName()); session.auth().verify(11L, TimeUnit.SECONDS);