diff --git a/build.gradle b/build.gradle index 9fd32081..5b06609a 100644 --- a/build.gradle +++ b/build.gradle @@ -35,8 +35,8 @@ project.version = scmVersion.version configurations.implementation.transitive = false -def bouncycastleVersion = "1.69" -def sshdVersion = "2.1.0" +def bouncycastleVersion = "1.70" +def sshdVersion = "2.8.0" dependencies { implementation "org.slf4j:slf4j-api:1.7.32" @@ -47,15 +47,15 @@ dependencies { implementation "net.i2p.crypto:eddsa:0.3.0" - testImplementation "junit:junit:4.12" + testImplementation "junit:junit:4.13.2" testImplementation 'org.spockframework:spock-core:1.3-groovy-2.4' - testImplementation "org.mockito:mockito-core:2.28.2" + testImplementation "org.mockito:mockito-core:4.2.0" testImplementation "org.apache.sshd:sshd-core:$sshdVersion" testImplementation "org.apache.sshd:sshd-sftp:$sshdVersion" testImplementation "org.apache.sshd:sshd-scp:$sshdVersion" - testImplementation "ch.qos.logback:logback-classic:1.2.6" + testImplementation "ch.qos.logback:logback-classic:1.2.9" testImplementation 'org.glassfish.grizzly:grizzly-http-server:2.4.4' - testImplementation 'org.apache.httpcomponents:httpclient:4.5.9' + testImplementation 'org.apache.httpcomponents:httpclient:4.5.13' testImplementation 'org.testcontainers:testcontainers:1.16.2' } diff --git a/src/main/java/net/schmizz/sshj/connection/channel/forwarded/RemotePortForwarder.java b/src/main/java/net/schmizz/sshj/connection/channel/forwarded/RemotePortForwarder.java index 01465763..bb86f9ac 100644 --- a/src/main/java/net/schmizz/sshj/connection/channel/forwarded/RemotePortForwarder.java +++ b/src/main/java/net/schmizz/sshj/connection/channel/forwarded/RemotePortForwarder.java @@ -142,6 +142,10 @@ public class RemotePortForwarder // Listen on all IPv4 return true; } + if ("0.0.0.0".equals(address) && "0:0:0:0:0:0:0:0".equals(channelForward.address)) { + // Handle IPv4 requests on IPv6 channel forward + return true; + } return false; } diff --git a/src/test/java/com/hierynomus/sshj/connection/channel/forwarded/RemotePortForwarderTest.java b/src/test/java/com/hierynomus/sshj/connection/channel/forwarded/RemotePortForwarderTest.java index eda13fab..b3738b8a 100644 --- a/src/test/java/com/hierynomus/sshj/connection/channel/forwarded/RemotePortForwarderTest.java +++ b/src/test/java/com/hierynomus/sshj/connection/channel/forwarded/RemotePortForwarderTest.java @@ -37,14 +37,14 @@ import java.io.File; import java.io.IOException; import java.net.InetSocketAddress; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertEquals; public class RemotePortForwarderTest { private static final Logger log = LoggerFactory.getLogger(RemotePortForwarderTest.class); private static final PortRange RANGE = new PortRange(9000, 9999); - private static final InetSocketAddress HTTP_SERVER_SOCKET_ADDR = new InetSocketAddress("127.0.0.1", 8080); + private static final String LOCALHOST = "127.0.0.1"; + private static final InetSocketAddress HTTP_SERVER_SOCKET_ADDR = new InetSocketAddress(LOCALHOST, 8080); @Rule public SshFixture fixture = new SshFixture(); @@ -62,61 +62,63 @@ public class RemotePortForwarderTest { @Test public void shouldHaveWorkingHttpServer() throws IOException { // Just to check that we have a working http server... - assertThat(httpGet("127.0.0.1", 8080), equalTo(200)); + assertEquals(200, httpGet( 8080)); } @Test public void shouldDynamicallyForwardPortForLocalhost() throws IOException { SSHClient sshClient = getFixtureClient(); RemotePortForwarder.Forward bind = forwardPort(sshClient, "127.0.0.1", new SinglePort(0)); - assertThat(httpGet("127.0.0.1", bind.getPort()), equalTo(200)); + assertHttpGetSuccess(bind); } @Test public void shouldDynamicallyForwardPortForAllIPv4() throws IOException { SSHClient sshClient = getFixtureClient(); RemotePortForwarder.Forward bind = forwardPort(sshClient, "0.0.0.0", new SinglePort(0)); - assertThat(httpGet("127.0.0.1", bind.getPort()), equalTo(200)); + assertHttpGetSuccess(bind); } @Test public void shouldDynamicallyForwardPortForAllProtocols() throws IOException { SSHClient sshClient = getFixtureClient(); RemotePortForwarder.Forward bind = forwardPort(sshClient, "", new SinglePort(0)); - assertThat(httpGet("127.0.0.1", bind.getPort()), equalTo(200)); + assertHttpGetSuccess(bind); } @Test public void shouldForwardPortForLocalhost() throws IOException { SSHClient sshClient = getFixtureClient(); RemotePortForwarder.Forward bind = forwardPort(sshClient, "127.0.0.1", RANGE); - assertThat(httpGet("127.0.0.1", bind.getPort()), equalTo(200)); + assertHttpGetSuccess(bind); } @Test public void shouldForwardPortForAllIPv4() throws IOException { SSHClient sshClient = getFixtureClient(); RemotePortForwarder.Forward bind = forwardPort(sshClient, "0.0.0.0", RANGE); - assertThat(httpGet("127.0.0.1", bind.getPort()), equalTo(200)); + assertHttpGetSuccess(bind); } @Test public void shouldForwardPortForAllProtocols() throws IOException { SSHClient sshClient = getFixtureClient(); RemotePortForwarder.Forward bind = forwardPort(sshClient, "", RANGE); - assertThat(httpGet("127.0.0.1", bind.getPort()), equalTo(200)); + assertHttpGetSuccess(bind); + } + + private void assertHttpGetSuccess(final RemotePortForwarder.Forward bind) throws IOException { + assertEquals(200, httpGet(bind.getPort())); } private RemotePortForwarder.Forward forwardPort(SSHClient sshClient, String address, PortRange portRange) throws IOException { while (true) { try { - RemotePortForwarder.Forward forward = sshClient.getRemotePortForwarder().bind( + return sshClient.getRemotePortForwarder().bind( // where the server should listen new RemotePortForwarder.Forward(address, portRange.nextPort()), // what we do with incoming connections that are forwarded to us new SocketForwardingConnectListener(HTTP_SERVER_SOCKET_ADDR)); - - return forward; } catch (ConnectionException ce) { if (!portRange.hasNext()) { throw ce; @@ -125,9 +127,9 @@ public class RemotePortForwarderTest { } } - private int httpGet(String server, int port) throws IOException { + private int httpGet(int port) throws IOException { HttpClient client = HttpClientBuilder.create().build(); - String urlString = "http://" + server + ":" + port; + String urlString = "http://" + LOCALHOST + ":" + port; log.info("Trying: GET " + urlString); HttpResponse execute = client.execute(new HttpGet(urlString)); return execute.getStatusLine().getStatusCode(); @@ -140,7 +142,7 @@ public class RemotePortForwarderTest { } private static class PortRange { - private int upper; + private final int upper; private int current; public PortRange(int lower, int upper) { diff --git a/src/test/java/com/hierynomus/sshj/test/SshFixture.java b/src/test/java/com/hierynomus/sshj/test/SshFixture.java index 0db60596..cbf559c3 100644 --- a/src/test/java/com/hierynomus/sshj/test/SshFixture.java +++ b/src/test/java/com/hierynomus/sshj/test/SshFixture.java @@ -19,21 +19,17 @@ import net.schmizz.sshj.Config; import net.schmizz.sshj.DefaultConfig; import net.schmizz.sshj.SSHClient; import net.schmizz.sshj.util.gss.BogusGSSAuthenticator; -import org.apache.sshd.common.NamedFactory; import org.apache.sshd.common.keyprovider.ClassLoadableResourceKeyPairProvider; +import org.apache.sshd.scp.server.ScpCommandFactory; import org.apache.sshd.server.SshServer; -import org.apache.sshd.server.auth.password.PasswordAuthenticator; -import org.apache.sshd.server.command.Command; -import org.apache.sshd.server.command.CommandFactory; -import org.apache.sshd.server.scp.ScpCommandFactory; -import org.apache.sshd.server.session.ServerSession; import org.apache.sshd.server.shell.ProcessShellFactory; -import org.apache.sshd.server.subsystem.sftp.SftpSubsystemFactory; +import org.apache.sshd.sftp.server.SftpSubsystemFactory; import org.junit.rules.ExternalResource; import java.io.IOException; +import java.io.UncheckedIOException; import java.net.ServerSocket; -import java.util.Arrays; +import java.util.Collections; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -43,9 +39,9 @@ public class SshFixture extends ExternalResource { public static final String hostkey = "hostkey.pem"; public static final String fingerprint = "ce:a7:c1:cf:17:3f:96:49:6a:53:1a:05:0b:ba:90:db"; - private SshServer server = defaultSshServer(); + private final SshServer server = defaultSshServer(); private SSHClient client = null; - private AtomicBoolean started = new AtomicBoolean(false); + private final AtomicBoolean started = new AtomicBoolean(false); private boolean autoStart = true; public SshFixture(boolean autoStart) { @@ -108,38 +104,23 @@ public class SshFixture extends ExternalResource { sshServer.setPort(randomPort()); ClassLoadableResourceKeyPairProvider fileKeyPairProvider = new ClassLoadableResourceKeyPairProvider(hostkey); sshServer.setKeyPairProvider(fileKeyPairProvider); - sshServer.setPasswordAuthenticator(new PasswordAuthenticator() { - @Override - public boolean authenticate(String username, String password, ServerSession session) { - return username.equals(password); - } - }); + sshServer.setPasswordAuthenticator((username, password, session) -> username.equals(password)); sshServer.setGSSAuthenticator(new BogusGSSAuthenticator()); - sshServer.setSubsystemFactories(Arrays.>asList(new SftpSubsystemFactory())); + sshServer.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory())); ScpCommandFactory commandFactory = new ScpCommandFactory(); - commandFactory.setDelegateCommandFactory(new CommandFactory() { - @Override - public Command createCommand(String command) { - return new ProcessShellFactory(command.split(" ")).create(); - } - }); + commandFactory.setDelegateCommandFactory((session, command) -> new ProcessShellFactory(command, command.split(" ")).createShell(session)); sshServer.setCommandFactory(commandFactory); - sshServer.setShellFactory(new ProcessShellFactory("ls")); + sshServer.setShellFactory(new ProcessShellFactory("ls", "ls")); return sshServer; } private int randomPort() { try { - ServerSocket s = null; - try { - s = new ServerSocket(0); + try (final ServerSocket s = new ServerSocket(0)) { return s.getLocalPort(); - } finally { - if (s != null) - s.close(); } } catch (IOException e) { - throw new RuntimeException(e); + throw new UncheckedIOException(e); } } diff --git a/src/test/java/com/hierynomus/sshj/transport/kex/KeyExchangeTest.java b/src/test/java/com/hierynomus/sshj/transport/kex/KeyExchangeTest.java index ec8c6eb4..e82ec077 100644 --- a/src/test/java/com/hierynomus/sshj/transport/kex/KeyExchangeTest.java +++ b/src/test/java/com/hierynomus/sshj/transport/kex/KeyExchangeTest.java @@ -22,9 +22,8 @@ import net.schmizz.sshj.common.Factory; import net.schmizz.sshj.transport.kex.DHGexSHA1; import net.schmizz.sshj.transport.kex.DHGexSHA256; import net.schmizz.sshj.transport.kex.ECDHNistP; -import org.apache.sshd.common.NamedFactory; 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.server.SshServer; import org.apache.sshd.server.kex.DHGEXServer; import org.apache.sshd.server.kex.DHGServer; @@ -38,6 +37,7 @@ import java.util.Collections; @RunWith(Parameterized.class) public class KeyExchangeTest extends BaseAlgorithmTest { + @SuppressWarnings("deprecation") @Parameterized.Parameters(name = "algorithm={0}") public static Collection getParameters() { return Arrays.asList(new Object[][]{ @@ -56,10 +56,10 @@ public class KeyExchangeTest extends BaseAlgorithmTest { }); } - private Factory.Named clientFactory; - private NamedFactory serverFactory; + private final Factory.Named clientFactory; + private final KeyExchangeFactory serverFactory; - public KeyExchangeTest(NamedFactory serverFactory, Factory.Named clientFactory) { + public KeyExchangeTest(KeyExchangeFactory serverFactory, Factory.Named clientFactory) { this.clientFactory = clientFactory; this.serverFactory = serverFactory; } diff --git a/src/test/java/com/hierynomus/sshj/userauth/method/AuthKeyboardInteractiveTest.java b/src/test/java/com/hierynomus/sshj/userauth/method/AuthKeyboardInteractiveTest.java index dbf81b0d..305afd9e 100644 --- a/src/test/java/com/hierynomus/sshj/userauth/method/AuthKeyboardInteractiveTest.java +++ b/src/test/java/com/hierynomus/sshj/userauth/method/AuthKeyboardInteractiveTest.java @@ -20,12 +20,8 @@ import net.schmizz.sshj.SSHClient; import net.schmizz.sshj.userauth.method.AuthKeyboardInteractive; import net.schmizz.sshj.userauth.method.ChallengeResponseProvider; import net.schmizz.sshj.userauth.password.Resource; -import org.apache.sshd.client.auth.keyboard.UserAuthKeyboardInteractiveFactory; -import org.apache.sshd.common.NamedFactory; -import org.apache.sshd.server.auth.UserAuth; -import org.apache.sshd.server.auth.keyboard.UserAuthKeyboardInteractive; -import org.apache.sshd.server.auth.password.PasswordAuthenticator; -import org.apache.sshd.server.session.ServerSession; +import org.apache.sshd.server.auth.keyboard.UserAuthKeyboardInteractiveFactory; +import org.apache.sshd.server.auth.password.AcceptAllPasswordAuthenticator; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -43,28 +39,8 @@ public class AuthKeyboardInteractiveTest { @Before public void setKeyboardInteractiveAuthenticator() throws IOException { - fixture.getServer().setUserAuthFactories(Collections.>singletonList(new NamedFactory() { - @Override - public String getName() { - return UserAuthKeyboardInteractiveFactory.NAME; - } - - @Override - public UserAuth get() { - return new UserAuthKeyboardInteractive(); - } - - @Override - public UserAuth create() { - return get(); - } - })); - fixture.getServer().setPasswordAuthenticator(new PasswordAuthenticator() { - @Override - public boolean authenticate(String username, String password, ServerSession session) { - return password.equals(username); - } - }); + fixture.getServer().setUserAuthFactories(Collections.singletonList(new UserAuthKeyboardInteractiveFactory())); + fixture.getServer().setPasswordAuthenticator(AcceptAllPasswordAuthenticator.INSTANCE); fixture.getServer().start(); } @@ -75,7 +51,7 @@ public class AuthKeyboardInteractiveTest { sshClient.auth(userAndPassword, new AuthKeyboardInteractive(new ChallengeResponseProvider() { @Override public List getSubmethods() { - return new ArrayList(); + return new ArrayList<>(); } @Override diff --git a/src/test/java/com/hierynomus/sshj/userauth/method/AuthPasswordTest.java b/src/test/java/com/hierynomus/sshj/userauth/method/AuthPasswordTest.java index 33ba2c79..49a900d1 100644 --- a/src/test/java/com/hierynomus/sshj/userauth/method/AuthPasswordTest.java +++ b/src/test/java/com/hierynomus/sshj/userauth/method/AuthPasswordTest.java @@ -21,56 +21,29 @@ import net.schmizz.sshj.userauth.UserAuthException; import net.schmizz.sshj.userauth.password.PasswordFinder; import net.schmizz.sshj.userauth.password.PasswordUpdateProvider; import net.schmizz.sshj.userauth.password.Resource; -import org.apache.sshd.common.NamedFactory; -import org.apache.sshd.common.util.buffer.Buffer; -import org.apache.sshd.server.auth.UserAuth; import org.apache.sshd.server.auth.password.PasswordAuthenticator; import org.apache.sshd.server.auth.password.PasswordChangeRequiredException; -import org.apache.sshd.server.auth.password.UserAuthPassword; import org.apache.sshd.server.auth.password.UserAuthPasswordFactory; import org.apache.sshd.server.session.ServerSession; import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import java.io.IOException; import java.util.Collections; import java.util.Stack; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertThrows; public class AuthPasswordTest { @Rule public SshFixture fixture = new SshFixture(false); - @Rule - public ExpectedException expectedException = ExpectedException.none(); - @Before public void setPasswordAuthenticator() throws IOException { - fixture.getServer().setUserAuthFactories(Collections.>singletonList(new NamedFactory() { - @Override - public String getName() { - return UserAuthPasswordFactory.NAME; - } - - @Override - public UserAuth get() { - return new UserAuthPassword() { - @Override - protected Boolean handleClientPasswordChangeRequest(Buffer buffer, ServerSession session, String username, String oldPassword, String newPassword) throws Exception { - return session.getPasswordAuthenticator().authenticate(username, newPassword, session); - } - }; - } - - @Override - public UserAuth create() { - return get(); - } - })); + fixture.getServer().setUserAuthFactories(Collections.singletonList(new UserAuthPasswordFactory())); fixture.getServer().setPasswordAuthenticator(new PasswordAuthenticator() { @Override public boolean authenticate(String username, String password, ServerSession session) { @@ -80,6 +53,12 @@ public class AuthPasswordTest { return password.equals(username); } } + + @Override + public boolean handleClientPasswordChangeRequest( + ServerSession session, String username, String oldPassword, String newPassword) { + return username.equals(newPassword); + } }); fixture.getServer().start(); } @@ -87,8 +66,7 @@ public class AuthPasswordTest { @Test public void shouldNotHandlePasswordChangeIfNoPasswordUpdateProviderSet() throws IOException { SSHClient sshClient = fixture.setupConnectedDefaultClient(); - expectedException.expect(UserAuthException.class); - sshClient.authPassword("jeroen", "changeme"); + assertThrows(UserAuthException.class, () -> sshClient.authPassword("jeroen", "changeme")); assertThat("Should not have authenticated", !sshClient.isAuthenticated()); } @@ -112,8 +90,7 @@ public class AuthPasswordTest { @Test public void shouldHandlePasswordChangeWithWrongPassword() throws IOException { SSHClient sshClient = fixture.setupConnectedDefaultClient(); - expectedException.expect(UserAuthException.class); - sshClient.authPassword("jeroen", new PasswordFinder() { + assertThrows(UserAuthException.class, () -> sshClient.authPassword("jeroen", new PasswordFinder() { @Override public char[] reqPassword(Resource resource) { return "changeme".toCharArray(); @@ -123,7 +100,7 @@ public class AuthPasswordTest { public boolean shouldRetry(Resource resource) { return false; } - }, new StaticPasswordUpdateProvider("bad")); + }, new StaticPasswordUpdateProvider("bad"))); assertThat("Should not have authenticated", !sshClient.isAuthenticated()); } @@ -153,7 +130,7 @@ public class AuthPasswordTest { } private static class StaticPasswordUpdateProvider implements PasswordUpdateProvider { - private Stack newPasswords = new Stack(); + private final Stack newPasswords = new Stack<>(); public StaticPasswordUpdateProvider(String... newPasswords) { for (int i = newPasswords.length - 1; i >= 0; i--) {