diff --git a/ssh/src/main/java/ch/cyberduck/core/sftp/SFTPSession.java b/ssh/src/main/java/ch/cyberduck/core/sftp/SFTPSession.java index e7bcf4e7115..c66c1ed1ea0 100644 --- a/ssh/src/main/java/ch/cyberduck/core/sftp/SFTPSession.java +++ b/ssh/src/main/java/ch/cyberduck/core/sftp/SFTPSession.java @@ -46,6 +46,7 @@ import ch.cyberduck.core.sftp.openssh.OpenSSHIdentityAgentConfigurator; import ch.cyberduck.core.sftp.openssh.OpenSSHJumpHostConfigurator; import ch.cyberduck.core.sftp.openssh.OpenSSHPreferredAuthenticationsConfigurator; +import ch.cyberduck.core.sftp.openssh.WindowsOpenSSHAgentAuthenticator; import ch.cyberduck.core.sftp.putty.PageantAuthenticator; import ch.cyberduck.core.ssl.X509KeyManager; import ch.cyberduck.core.ssl.X509TrustManager; @@ -66,6 +67,7 @@ import java.util.LinkedHashMap; import java.util.List; +import com.jcraft.jsch.agentproxy.AgentProxyException; import net.schmizz.concurrent.Promise; import net.schmizz.keepalive.KeepAlive; import net.schmizz.keepalive.KeepAliveProvider; @@ -119,9 +121,9 @@ protected SSHClient connect(final Proxy proxy, final HostKeyCallback key, final final DefaultConfig configuration = new DefaultConfig(); if("zlib".equals(preferences.getProperty("ssh.compression"))) { configuration.setCompressionFactories(Arrays.asList( - new DelayedZlibCompression.Factory(), - new ZlibCompression.Factory(), - new NoneCompression.Factory())); + new DelayedZlibCompression.Factory(), + new ZlibCompression.Factory(), + new NoneCompression.Factory())); } else { configuration.setCompressionFactories(Collections.singletonList(new NoneCompression.Factory())); @@ -163,7 +165,7 @@ protected SSHClient connect(final HostKeyCallback key, final LoginCallback promp } } final DirectConnection tunnel = hop.newDirectConnection( - new OpenSSHHostnameConfigurator().getHostname(host.getHostname()), host.getPort()); + new OpenSSHHostnameConfigurator().getHostname(host.getHostname()), host.getPort()); // Connect to internal host connection.connectVia(tunnel); } @@ -245,13 +247,13 @@ public boolean alert(final ConnectionCallback prompt) throws BackgroundException private void alert(final ConnectionCallback prompt, final String algorithm) throws ConnectionCanceledException { prompt.warn(host, MessageFormat.format(LocaleFactory.localizedString("Insecure algorithm {0} negotiated with server", "Credentials"), - algorithm), - new StringAppender() - .append(LocaleFactory.localizedString("The algorithm is possibly too weak to meet current cryptography standards", "Credentials")) - .append(LocaleFactory.localizedString("Please contact your web hosting service provider for assistance", "Support")).toString(), - LocaleFactory.localizedString("Continue", "Credentials"), - LocaleFactory.localizedString("Disconnect", "Credentials"), - String.format("ssh.algorithm.whitelist.%s", host.getHostname())); + algorithm), + new StringAppender() + .append(LocaleFactory.localizedString("The algorithm is possibly too weak to meet current cryptography standards", "Credentials")) + .append(LocaleFactory.localizedString("Please contact your web hosting service provider for assistance", "Support")).toString(), + LocaleFactory.localizedString("Continue", "Credentials"), + LocaleFactory.localizedString("Disconnect", "Credentials"), + String.format("ssh.algorithm.whitelist.%s", host.getHostname())); } @Override @@ -286,13 +288,21 @@ private void authenticate(final SSHClient client, final Host host, final LoginCa switch(Factory.Platform.getDefault()) { case windows: defaultMethods.add(new SFTPAgentAuthentication(client, new PageantAuthenticator())); - - defaultMethods.add(new SFTPAgentAuthentication(client, new OpenSSHAgentAuthenticator( - new OpenSSHIdentityAgentConfigurator().getIdentityAgent(host.getHostname()), true))); + try { + defaultMethods.add(new SFTPAgentAuthentication(client, new WindowsOpenSSHAgentAuthenticator())); + } + catch(AgentProxyException e) { + log.warn(String.format("Agent proxy failed with %s", e)); + } break; default: - defaultMethods.add(new SFTPAgentAuthentication(client, new OpenSSHAgentAuthenticator( - new OpenSSHIdentityAgentConfigurator().getIdentityAgent(host.getHostname()), false))); + try { + defaultMethods.add(new SFTPAgentAuthentication(client, new OpenSSHAgentAuthenticator( + new OpenSSHIdentityAgentConfigurator().getIdentityAgent(host.getHostname())))); + } + catch(AgentProxyException e) { + log.warn(String.format("Agent proxy failed with %s", e)); + } break; } } @@ -366,13 +376,13 @@ private void authenticate(final SSHClient client, final Host host, final LoginCa } catch(IllegalStateException ignored) { log.warn(String.format("Server disconnected with %s while trying authentication method %s", - disconnectListener.getFailure(), auth)); + disconnectListener.getFailure(), auth)); try { if(null == disconnectListener.getFailure()) { throw new ConnectionRefusedException(LocaleFactory.localizedString("Login failed", "Credentials"), ignored); } throw new SFTPExceptionMappingService().map(LocaleFactory.localizedString("Login failed", "Credentials"), - disconnectListener.getFailure()); + disconnectListener.getFailure()); } catch(InteroperabilityException e) { throw new LoginFailureException(e.getDetail(false), e); @@ -385,7 +395,7 @@ private void authenticate(final SSHClient client, final Host host, final LoginCa if(!client.isAuthenticated()) { if(null == lastFailure) { throw new LoginFailureException(MessageFormat.format(LocaleFactory.localizedString( - "Login {0} with username and password", "Credentials"), BookmarkNameProvider.toString(host))); + "Login {0} with username and password", "Credentials"), BookmarkNameProvider.toString(host))); } throw lastFailure; } diff --git a/ssh/src/main/java/ch/cyberduck/core/sftp/openssh/OpenSSHAgentAuthenticator.java b/ssh/src/main/java/ch/cyberduck/core/sftp/openssh/OpenSSHAgentAuthenticator.java index 889da7fe34f..7d88694e6c8 100644 --- a/ssh/src/main/java/ch/cyberduck/core/sftp/openssh/OpenSSHAgentAuthenticator.java +++ b/ssh/src/main/java/ch/cyberduck/core/sftp/openssh/OpenSSHAgentAuthenticator.java @@ -23,9 +23,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.io.RandomAccessFile; -import java.io.EOFException; -import java.io.IOException; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -35,62 +32,19 @@ import com.jcraft.jsch.agentproxy.AgentProxyException; import com.jcraft.jsch.agentproxy.Identity; import com.jcraft.jsch.agentproxy.connector.SSHAgentConnector; -import com.jcraft.jsch.agentproxy.USocketFactory; import com.jcraft.jsch.agentproxy.usocket.JNAUSocketFactory; -// Implements a wrapper around RandomAccessFile for use with jsch's -// SSH connector to support Windows' OpenSSH fork. -class RandomAccessFileSocketFactory implements USocketFactory -{ - class WindowsSocket extends Socket - { - private RandomAccessFile raf; - WindowsSocket(String path) throws IOException - { - raf = new RandomAccessFile(path, "rw"); - } - - public int readFull(byte[] buf, int s, int len) throws IOException - { - try { - raf.readFully(buf, s, len); - } catch (EOFException e) { - return -1; - } - return len; - } - public void write(byte[] buf, int s, int len) throws IOException - { - raf.write(buf, s, len); - } - public void close() throws IOException - { - raf.close(); - } - } - - public Socket open(String path) throws IOException - { - return new WindowsSocket(path); - } -} - public class OpenSSHAgentAuthenticator extends AgentAuthenticator { private static final Logger log = LogManager.getLogger(OpenSSHAgentAuthenticator.class); - private AgentProxy proxy; + private final AgentProxy proxy; - public OpenSSHAgentAuthenticator(final String socket, final boolean windows) { - try { - if (windows) { - proxy = new AgentProxy(new SSHAgentConnector(new RandomAccessFileSocketFactory(), "\\\\.\\pipe\\openssh-ssh-agent")); - } else { - proxy = new AgentProxy(new SSHAgentConnector(new JNAUSocketFactory(), socket)); - } - } - catch(AgentProxyException e) { - log.warn(String.format("Agent proxy %s failed with %s", this, e)); - } + public OpenSSHAgentAuthenticator(final String socket) throws AgentProxyException { + this(new AgentProxy(new SSHAgentConnector(new JNAUSocketFactory(), socket))); + } + + public OpenSSHAgentAuthenticator(final AgentProxy proxy) { + this.proxy = proxy; } @Override diff --git a/ssh/src/main/java/ch/cyberduck/core/sftp/openssh/WindowsOpenSSHAgentAuthenticator.java b/ssh/src/main/java/ch/cyberduck/core/sftp/openssh/WindowsOpenSSHAgentAuthenticator.java new file mode 100644 index 00000000000..4dfb8d813a3 --- /dev/null +++ b/ssh/src/main/java/ch/cyberduck/core/sftp/openssh/WindowsOpenSSHAgentAuthenticator.java @@ -0,0 +1,67 @@ +package ch.cyberduck.core.sftp.openssh; + +/* + * Copyright (c) 2002-2022 iterate GmbH. All rights reserved. + * https://cyberduck.io/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +import java.io.EOFException; +import java.io.IOException; +import java.io.RandomAccessFile; + +import com.jcraft.jsch.agentproxy.AgentProxy; +import com.jcraft.jsch.agentproxy.AgentProxyException; +import com.jcraft.jsch.agentproxy.USocketFactory; +import com.jcraft.jsch.agentproxy.connector.SSHAgentConnector; + +public class WindowsOpenSSHAgentAuthenticator extends OpenSSHAgentAuthenticator { + + public WindowsOpenSSHAgentAuthenticator() throws AgentProxyException { + super(new AgentProxy(new SSHAgentConnector(new RandomAccessFileSocketFactory(), "\\\\.\\pipe\\openssh-ssh-agent"))); + } + + /** + * Implements a wrapper around RandomAccessFile for use with jsch's SSH connector to support Windows' OpenSSH fork. + */ + private static class RandomAccessFileSocketFactory implements USocketFactory { + static class WindowsSocket extends Socket { + private final RandomAccessFile raf; + + WindowsSocket(String path) throws IOException { + raf = new RandomAccessFile(path, "rw"); + } + + public int readFull(byte[] buf, int s, int len) throws IOException { + try { + raf.readFully(buf, s, len); + } + catch(EOFException e) { + return -1; + } + return len; + } + + public void write(byte[] buf, int s, int len) throws IOException { + raf.write(buf, s, len); + } + + public void close() throws IOException { + raf.close(); + } + } + + public Socket open(String path) throws IOException { + return new WindowsSocket(path); + } + } +} diff --git a/ssh/src/test/java/ch/cyberduck/core/sftp/openssh/OpenSSHAgentAuthenticatorTest.java b/ssh/src/test/java/ch/cyberduck/core/sftp/openssh/OpenSSHAgentAuthenticatorTest.java index 0ac909397ff..9665cb8cff9 100644 --- a/ssh/src/test/java/ch/cyberduck/core/sftp/openssh/OpenSSHAgentAuthenticatorTest.java +++ b/ssh/src/test/java/ch/cyberduck/core/sftp/openssh/OpenSSHAgentAuthenticatorTest.java @@ -18,22 +18,26 @@ * feedback@cyberduck.ch */ -import org.junit.Ignore; +import ch.cyberduck.core.Factory; + +import org.apache.commons.lang3.StringUtils; import org.junit.Test; import java.util.Collection; +import com.jcraft.jsch.agentproxy.AgentProxyException; import com.jcraft.jsch.agentproxy.Identity; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assume.assumeTrue; public class OpenSSHAgentAuthenticatorTest { - @Test - @Ignore - public void testGetIdentities() { - final OpenSSHAgentAuthenticator authenticator = new OpenSSHAgentAuthenticator(null, false); + @Test(expected = AgentProxyException.class) + public void testGetIdentities() throws Exception { + assumeTrue(Factory.Platform.getDefault().equals(Factory.Platform.Name.mac)); + final OpenSSHAgentAuthenticator authenticator = new OpenSSHAgentAuthenticator(StringUtils.EMPTY); final Collection identities = authenticator.getIdentities(); assertNotNull(authenticator.getProxy()); assertFalse(identities.isEmpty());