You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by lg...@apache.org on 2015/12/09 11:07:04 UTC

[1/6] mina-sshd git commit: [SSHD-474] Null pointer exception when connecting with latest Dropbear's dbclient

Repository: mina-sshd
Updated Branches:
  refs/heads/master 28faad412 -> 0371baefc


[SSHD-474] Null pointer exception when connecting with latest Dropbear's dbclient

* Alleviate the problem - there is now a data stream corruption...


Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/56b5f0e8
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/56b5f0e8
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/56b5f0e8

Branch: refs/heads/master
Commit: 56b5f0e888d5983c7dc0b0b0776d6cbd2be97c64
Parents: 28faad4
Author: Lyor Goldstein <lg...@vmware.com>
Authored: Wed Dec 9 11:34:36 2015 +0200
Committer: Lyor Goldstein <lg...@vmware.com>
Committed: Wed Dec 9 11:34:36 2015 +0200

----------------------------------------------------------------------
 .../org/apache/sshd/client/kex/DHGClient.java   |  3 +-
 .../org/apache/sshd/client/kex/DHGEXClient.java |  3 +-
 .../org/apache/sshd/common/kex/KeyExchange.java |  5 +-
 .../sshd/common/session/AbstractSession.java    | 87 ++++++++++++++++----
 .../org/apache/sshd/server/kex/DHGEXServer.java |  3 +-
 .../org/apache/sshd/server/kex/DHGServer.java   |  3 +-
 6 files changed, 76 insertions(+), 28 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/56b5f0e8/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGClient.java
----------------------------------------------------------------------
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 430c296..c676c35 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
@@ -98,8 +98,7 @@ public class DHGClient extends AbstractDHClientKeyExchange {
     }
 
     @Override
-    public boolean next(Buffer buffer) throws Exception {
-        int cmd = buffer.getUByte();
+    public boolean next(int cmd, Buffer buffer) throws Exception {
         Session session = getSession();
         if (log.isDebugEnabled()) {
             log.debug("next({})[{}] process command={}", this, session, KeyExchange.Utils.getSimpleKexOpcodeName(cmd));

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/56b5f0e8/sshd-core/src/main/java/org/apache/sshd/client/kex/DHGEXClient.java
----------------------------------------------------------------------
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 dca86aa..c521749 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
@@ -102,8 +102,7 @@ public class DHGEXClient extends AbstractDHClientKeyExchange {
     }
 
     @Override
-    public boolean next(Buffer buffer) throws Exception {
-        int cmd = buffer.getUByte();
+    public boolean next(int cmd, Buffer buffer) throws Exception {
         Session session = getSession();
         if (log.isDebugEnabled()) {
             log.debug("next({})[{}] process command={}", this, session, KeyExchange.Utils.getGroupKexOpcodeName(cmd));

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/56b5f0e8/sshd-core/src/main/java/org/apache/sshd/common/kex/KeyExchange.java
----------------------------------------------------------------------
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 24fcd57..a3364a9 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
@@ -51,11 +51,12 @@ public interface KeyExchange extends NamedResource {
     /**
      * Process the next packet
      *
-     * @param buffer the packet
+     * @param cmd the command
+     * @param buffer the packet contents positioned after the command
      * @return a boolean indicating if the processing is complete or if more packets are to be received
      * @throws Exception if an error occurs
      */
-    boolean next(Buffer buffer) throws Exception;
+    boolean next(int cmd, Buffer buffer) throws Exception;
 
     /**
      * The message digest used by this key exchange algorithm.

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/56b5f0e8/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java b/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
index 1347245..74d275f 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
@@ -144,6 +144,7 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
     protected byte[] i_c; // the payload of the client's SSH_MSG_KEXINIT
     protected byte[] i_s; // the payload of the factoryManager's SSH_MSG_KEXINIT
     protected KeyExchange kex;
+    protected Boolean firstKexPacketFollows;
     protected final AtomicReference<KexState> kexState = new AtomicReference<>(KexState.UNKNOWN);
     protected final AtomicReference<DefaultKeyExchangeFuture> kexFutureHolder = new AtomicReference<>(null);
 
@@ -416,22 +417,18 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
         }
 
         switch (cmd) {
-            case SshConstants.SSH_MSG_DISCONNECT: {
+            case SshConstants.SSH_MSG_DISCONNECT:
                 handleDisconnect(buffer);
                 break;
-            }
-            case SshConstants.SSH_MSG_IGNORE: {
+            case SshConstants.SSH_MSG_IGNORE:
                 handleIgnore(buffer);
                 break;
-            }
-            case SshConstants.SSH_MSG_UNIMPLEMENTED: {
+            case SshConstants.SSH_MSG_UNIMPLEMENTED:
                 handleUnimplented(buffer);
                 break;
-            }
-            case SshConstants.SSH_MSG_DEBUG: {
+            case SshConstants.SSH_MSG_DEBUG:
                 handleDebug(buffer);
                 break;
-            }
             case SshConstants.SSH_MSG_SERVICE_REQUEST:
                 handleServiceRequest(buffer);
                 break;
@@ -446,13 +443,17 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
                 break;
             default:
                 if ((cmd >= SshConstants.SSH_MSG_KEX_FIRST) && (cmd <= SshConstants.SSH_MSG_KEX_LAST)) {
-                    validateKexState(cmd, KexState.RUN);
-                    buffer.rpos(buffer.rpos() - 1);
-                    if (kex.next(buffer)) {
-                        checkKeys();
-                        sendNewKeys();
-                        kexState.set(KexState.KEYS);
+                    if (firstKexPacketFollows != null) {
+                        try {
+                            if (!handleFirstKexPacketFollows(cmd, buffer, firstKexPacketFollows.booleanValue())) {
+                                break;
+                            }
+                        } finally {
+                            firstKexPacketFollows = null;   // avoid re-checking
+                        }
                     }
+
+                    handleKexMessage(cmd, buffer);
                 } else if (currentService != null) {
                     currentService.process(cmd, buffer);
                     resetIdleTimeout();
@@ -464,6 +465,56 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
         checkRekey();
     }
 
+    protected boolean handleFirstKexPacketFollows(int cmd, Buffer buffer, boolean followFlag) {
+        if (!followFlag) {
+            return true; // if 1st KEX packet does not follow then process the command
+        }
+
+        /*
+         * According to RFC4253 section 7.1:
+         *
+         *      If the other party's guess was wrong, and this field was TRUE,
+         *      the next packet MUST be silently ignored
+         */
+        for (KexProposalOption option : new KexProposalOption[]{KexProposalOption.ALGORITHMS, KexProposalOption.SERVERKEYS}) {
+            Pair<String, String> result = comparePreferredKexProposalOption(option);
+            if (result != null) {
+                if (log.isDebugEnabled()) {
+                    log.debug("handleKexMessage({})[{}] 1st follow KEX packet {} option mismatch: client={}, server={}",
+                              this, SshConstants.getCommandMessageName(cmd), option, result.getFirst(), result.getSecond());
+                }
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    protected Pair<String, String> comparePreferredKexProposalOption(KexProposalOption option) {
+        String[] clientPreferences = GenericUtils.split(clientProposal.get(option), ',');
+        String clientValue = clientPreferences[0];
+        String[] serverPreferences = GenericUtils.split(serverProposal.get(option), ',');
+        String serverValue = serverPreferences[0];
+        return clientValue.equals(serverValue) ? null : new Pair<>(clientValue, serverValue);
+    }
+
+    protected void handleKexMessage(int cmd, Buffer buffer) throws Exception {
+        validateKexState(cmd, KexState.RUN);
+
+        if (kex.next(cmd, buffer)) {
+            if (log.isDebugEnabled()) {
+                log.debug("handleKexMessage({})[{}] KEX processing complete after cmd={}", this, kex.getName(), cmd);
+            }
+            checkKeys();
+            sendNewKeys();
+            kexState.set(KexState.KEYS);
+        } else {
+            if (log.isDebugEnabled()) {
+                log.debug("handleKexMessage({})[{}] more KEX packets expected after cmd={}", this, kex.getName(), cmd);
+            }
+        }
+    }
+
     protected void handleIgnore(Buffer buffer) throws Exception {
         if (log.isDebugEnabled()) {
             log.debug("handleIgnore({}) SSH_MSG_IGNORE", this);
@@ -551,7 +602,6 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
                 "Unknown negotiated KEX algorithm: %s",
                 kexAlgorithm);
         kex.init(this, serverVersion.getBytes(StandardCharsets.UTF_8), clientVersion.getBytes(StandardCharsets.UTF_8), i_s, i_c);
-
         sendSessionEvent(SessionListener.Event.KexCompleted);
     }
 
@@ -954,7 +1004,7 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
                 assert decoderBuffer.rpos() == 4;
                 int macSize = inMac != null ? inMac.getBlockSize() : 0;
                 // Check if the packet has been fully received
-                if (decoderBuffer.available() >= decoderLength + macSize) {
+                if (decoderBuffer.available() >= (decoderLength + macSize)) {
                     byte[] data = decoderBuffer.array();
                     // Decrypt the remaining of the packet
                     if (inCipher != null) {
@@ -1000,7 +1050,7 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
                     }
 
                     if (log.isTraceEnabled()) {
-                        log.trace("decode({}) Received packet #{}: {}", this, Long.valueOf(seqi), buf.printHex());
+                        log.trace("decode({}) Received packet #{}: {}", this, seqi, buf.printHex());
                     }
 
                     // Update stats
@@ -1201,7 +1251,7 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
             size += readLen;
         }
 
-        boolean firstKexPacketFollows = buffer.getBoolean();
+        firstKexPacketFollows = buffer.getBoolean();
         if (log.isTraceEnabled()) {
             log.trace("receiveKexInit(" + toString() + ") first kex packet follows: " + firstKexPacketFollows);
         }
@@ -1362,6 +1412,7 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
         inBlocksCount.set(0L);
         outBlocksCount.set(0L);
         lastKeyTimeValue.set(System.currentTimeMillis());
+        firstKexPacketFollows = null;
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/56b5f0e8/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGEXServer.java
----------------------------------------------------------------------
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 4694de4..b427c08 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
@@ -101,8 +101,7 @@ public class DHGEXServer extends AbstractDHServerKeyExchange {
     }
 
     @Override
-    public boolean next(Buffer buffer) throws Exception {
-        int cmd = buffer.getUByte();
+    public boolean next(int cmd, Buffer buffer) throws Exception {
         ServerSession session = getServerSession();
         if (log.isDebugEnabled()) {
             log.debug("next({})[{}] process command={}", this, session, KeyExchange.Utils.getGroupKexOpcodeName(cmd));

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/56b5f0e8/sshd-core/src/main/java/org/apache/sshd/server/kex/DHGServer.java
----------------------------------------------------------------------
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 8a9704a..e6a9ef2 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
@@ -84,8 +84,7 @@ public class DHGServer extends AbstractDHServerKeyExchange {
     }
 
     @Override
-    public boolean next(Buffer buffer) throws Exception {
-        int cmd = buffer.getUByte();
+    public boolean next(int cmd, Buffer buffer) throws Exception {
         ServerSession session = getServerSession();
         if (log.isDebugEnabled()) {
             log.debug("next({})[{}] process command={}", this, session, KeyExchange.Utils.getSimpleKexOpcodeName(cmd));


[2/6] mina-sshd git commit: Specify SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE as the disconnect reason if failed to verify server key

Posted by lg...@apache.org.
Specify SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE as the disconnect reason if failed to verify server key


Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/2a3d0fda
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/2a3d0fda
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/2a3d0fda

Branch: refs/heads/master
Commit: 2a3d0fda13008a875496772c0bb06f97d383861c
Parents: 56b5f0e
Author: Lyor Goldstein <lg...@vmware.com>
Authored: Wed Dec 9 11:35:17 2015 +0200
Committer: Lyor Goldstein <lg...@vmware.com>
Committed: Wed Dec 9 11:35:17 2015 +0200

----------------------------------------------------------------------
 .../java/org/apache/sshd/client/session/ClientSessionImpl.java     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/2a3d0fda/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
index 103925f..d10e8cd 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
@@ -364,7 +364,7 @@ public class ClientSessionImpl extends AbstractClientSession {
         SocketAddress remoteAddress = ioSession.getRemoteAddress();
 
         if (!serverKeyVerifier.verifyServerKey(this, remoteAddress, kex.getServerKey())) {
-            throw new SshException("Server key did not validate");
+            throw new SshException(SshConstants.SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE, "Server key did not validate");
         }
     }
 


[4/6] mina-sshd git commit: Fixed keys re-exchange request logic in case of automatic re-exchange due to thresholds

Posted by lg...@apache.org.
Fixed keys re-exchange request logic in case of automatic re-exchange due to thresholds


Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/d99d4806
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/d99d4806
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/d99d4806

Branch: refs/heads/master
Commit: d99d4806e00a68a5dcbe5d634fe8b3113c360360
Parents: 05f5d36
Author: Lyor Goldstein <lg...@vmware.com>
Authored: Wed Dec 9 12:00:06 2015 +0200
Committer: Lyor Goldstein <lg...@vmware.com>
Committed: Wed Dec 9 12:00:06 2015 +0200

----------------------------------------------------------------------
 .../sshd/common/session/AbstractSession.java    | 85 +++++++++++++-------
 .../org/apache/sshd/common/session/Session.java |  4 +-
 .../java/org/apache/sshd/KeyReExchangeTest.java | 36 +++++++++
 .../java/org/apache/sshd/server/ServerTest.java |  3 +-
 .../test/OutputCountTrackingOutputStream.java   |  4 +-
 5 files changed, 96 insertions(+), 36 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d99d4806/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java b/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
index 74d275f..c7fddef 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/AbstractSession.java
@@ -424,7 +424,7 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
                 handleIgnore(buffer);
                 break;
             case SshConstants.SSH_MSG_UNIMPLEMENTED:
-                handleUnimplented(buffer);
+                handleUnimplemented(buffer);
                 break;
             case SshConstants.SSH_MSG_DEBUG:
                 handleDebug(buffer);
@@ -439,7 +439,7 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
                 handleKexInit(buffer);
                 break;
             case SshConstants.SSH_MSG_NEWKEYS:
-                handleNewKeys(cmd);
+                handleNewKeys(cmd, buffer);
                 break;
             default:
                 if ((cmd >= SshConstants.SSH_MSG_KEX_FIRST) && (cmd <= SshConstants.SSH_MSG_KEX_LAST)) {
@@ -521,17 +521,21 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
         }
     }
 
-    protected void handleUnimplented(Buffer buffer) throws Exception {
-        int seqNo = buffer.getInt();
+    protected void handleUnimplemented(Buffer buffer) throws Exception {
+        handleUnimplemented(buffer.getInt(), buffer);
+    }
+
+    protected void handleUnimplemented(int seqNo, Buffer buffer) throws Exception {
         if (log.isDebugEnabled()) {
             log.debug("handleUnimplented({}) SSH_MSG_UNIMPLEMENTED #{}", this, seqNo);
         }
     }
 
     protected void handleDebug(Buffer buffer) throws Exception {
-        boolean display = buffer.getBoolean();
-        String msg = buffer.getString();
-        String lang = buffer.getString();
+        handleDebug(buffer.getBoolean(), buffer.getString(), buffer.getString(), buffer);
+    }
+
+    protected void handleDebug(boolean display, String msg, String lang, Buffer buffer) throws Exception {
         if (log.isDebugEnabled()) {
             log.debug("handleDebug({}) SSH_MSG_DEBUG (display={}) [lang={}] '{}'",
                       this, display, lang, msg);
@@ -539,18 +543,23 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
     }
 
     protected void handleDisconnect(Buffer buffer) throws Exception  {
-        int code = buffer.getInt();
-        String msg = buffer.getString();
-        String lang = buffer.getString();
+        handleDisconnect(buffer.getInt(), buffer.getString(), buffer.getString(), buffer);
+    }
+
+    protected void handleDisconnect(int code, String msg, String lang, Buffer buffer) throws Exception {
         if (log.isDebugEnabled()) {
             log.debug("handleDisconnect({}) SSH_MSG_DISCONNECT reason={}, [lang={}] msg={}",
                       this, SshConstants.getDisconnectReasonName(code), lang, msg);
         }
+
         close(true);
     }
 
-    protected void handleServiceRequest(Buffer buffer) throws IOException {
-        String serviceName = buffer.getString();
+    protected void handleServiceRequest(Buffer buffer) throws Exception {
+        handleServiceRequest(buffer.getString(), buffer);
+    }
+
+    protected void handleServiceRequest(String serviceName, Buffer buffer) throws Exception {
         if (log.isDebugEnabled()) {
             log.debug("handleServiceRequest({}) SSH_MSG_SERVICE_REQUEST '{}'", this, serviceName);
         }
@@ -569,16 +578,21 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
             disconnect(SshConstants.SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE, "Bad service request: " + serviceName);
             return;
         }
+
         if (log.isDebugEnabled()) {
             log.debug("handleServiceRequest({}) Accepted service {}", this, serviceName);
         }
+
         Buffer response = prepareBuffer(SshConstants.SSH_MSG_SERVICE_ACCEPT, BufferUtils.clear(buffer));
         response.putString(serviceName);
         writePacket(response);
     }
 
-    protected void handleServiceAccept(Buffer buffer) throws IOException {
-        String serviceName = buffer.getString();
+    protected void handleServiceAccept(Buffer buffer) throws Exception {
+        handleServiceAccept(buffer.getString(), buffer);
+    }
+
+    protected void handleServiceAccept(String serviceName, Buffer buffer) throws Exception {
         if (log.isDebugEnabled()) {
             log.debug("handleServiceAccept({}) SSH_MSG_SERVICE_ACCEPT service={}", this, serviceName);
         }
@@ -605,7 +619,7 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
         sendSessionEvent(SessionListener.Event.KexCompleted);
     }
 
-    protected void handleNewKeys(int cmd) throws Exception {
+    protected void handleNewKeys(int cmd, Buffer buffer) throws Exception {
         if (log.isDebugEnabled()) {
             log.debug("handleNewKeys({}) SSH_MSG_NEWKEYS command={}", this, SshConstants.getCommandMessageName(cmd));
         }
@@ -1692,27 +1706,36 @@ public abstract class AbstractSession extends AbstractKexFactoryManager implemen
 
     @Override
     public KeyExchangeFuture reExchangeKeys() throws IOException {
-        if (kexState.compareAndSet(KexState.DONE, KexState.INIT)) {
-            log.info("reExchangeKeys({}) Initiating key re-exchange", this);
-            sendKexInit();
-
-            DefaultKeyExchangeFuture kexFuture = kexFutureHolder.getAndSet(new DefaultKeyExchangeFuture(null));
-            if (kexFuture != null) {
-                synchronized (kexFuture) {
-                    Object value = kexFuture.getValue();
-                    if (value == null) {
-                        kexFuture.setValue(new SshException("New KEX started while previous one still ongoing"));
-                    }
-                }
-            }
-        }
-
+        requestNewKeysExchange();
         return ValidateUtils.checkNotNull(kexFutureHolder.get(), "No current KEX future on state=%s", kexState.get());
     }
 
     protected void checkRekey() throws IOException {
         if (isRekeyRequired()) {
-            reExchangeKeys();
+            requestNewKeysExchange();
+        }
+    }
+
+    protected void requestNewKeysExchange() throws IOException {
+        if (!kexState.compareAndSet(KexState.DONE, KexState.INIT)) {
+            if (log.isDebugEnabled()) {
+                log.debug("requestNewKeysExchange({}) KEX state not DONE: {}", this, kexState.get());
+            }
+
+            return;
+        }
+
+        log.info("requestNewKeysExchange({}) Initiating key re-exchange", this);
+        sendKexInit();
+
+        DefaultKeyExchangeFuture kexFuture = kexFutureHolder.getAndSet(new DefaultKeyExchangeFuture(null));
+        if (kexFuture != null) {
+            synchronized (kexFuture) {
+                Object value = kexFuture.getValue();
+                if (value == null) {
+                    kexFuture.setValue(new SshException("New KEX started while previous one still ongoing"));
+                }
+            }
         }
     }
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d99d4806/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java b/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
index db9de87..c31d589 100644
--- a/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
+++ b/sshd-core/src/main/java/org/apache/sshd/common/session/Session.java
@@ -226,8 +226,8 @@ public interface Session
     /**
      * Initiate a new key exchange.
      *
-     * @return An {@link KeyExchangeFuture} for awaiting the completion of the exchange
-     * @throws IOException If failed to negotiate keys
+     * @return A {@link KeyExchangeFuture} for awaiting the completion of the exchange
+     * @throws IOException If failed to request keys re-negotiation
      */
     KeyExchangeFuture reExchangeKeys() throws IOException;
 

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d99d4806/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java
----------------------------------------------------------------------
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 34d1a40..242e3b4 100644
--- a/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/KeyReExchangeTest.java
@@ -264,17 +264,26 @@ public class KeyReExchangeTest extends BaseTestSupport {
                      InputStream inPipe = new PipedInputStream(pipedIn);
                      OutputStream teeOut = new TeeOutputStream(sent, pipedIn);
                      ByteArrayOutputStream out = new ByteArrayOutputStream() {
+                         private long writeCount = 0L;
+
                          @Override
                          public void write(int b) {
                              super.write(b);
+                             updateWriteCount(1L);
                              pipedCount.release(1);
                          }
 
                          @Override
                          public void write(byte[] b, int off, int len) {
                              super.write(b, off, len);
+                             updateWriteCount(len);
                              pipedCount.release(len);
                          }
+
+                         private void updateWriteCount(long delta) {
+                             writeCount += delta;
+                             outputDebugMessage("OUT write count=%d", writeCount);
+                         }
                      };
                      ByteArrayOutputStream err = new ByteArrayOutputStream()) {
 
@@ -333,17 +342,26 @@ public class KeyReExchangeTest extends BaseTestSupport {
             try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession();
                  ByteArrayOutputStream sent = new ByteArrayOutputStream();
                  ByteArrayOutputStream out = new ByteArrayOutputStream() {
+                     private long writeCount = 0L;
+
                      @Override
                      public void write(int b) {
                          super.write(b);
+                         updateWriteCount(1L);
                          pipedCount.release(1);
                      }
 
                      @Override
                      public void write(byte[] b, int off, int len) {
                          super.write(b, off, len);
+                         updateWriteCount(len);
                          pipedCount.release(len);
                      }
+
+                     private void updateWriteCount(long delta) {
+                         writeCount += delta;
+                         outputDebugMessage("OUT write count=%d", writeCount);
+                     }
                  }) {
                 session.addPasswordIdentity(getCurrentTestName());
                 session.auth().verify(5L, TimeUnit.SECONDS);
@@ -436,17 +454,26 @@ public class KeyReExchangeTest extends BaseTestSupport {
             try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession();
                  ByteArrayOutputStream sent = new ByteArrayOutputStream();
                  ByteArrayOutputStream out = new ByteArrayOutputStream() {
+                     private long writeCount = 0L;
+
                      @Override
                      public void write(int b) {
                          super.write(b);
+                         updateWriteCount(1L);
                          pipedCount.release(1);
                      }
 
                      @Override
                      public void write(byte[] b, int off, int len) {
                          super.write(b, off, len);
+                         updateWriteCount(len);
                          pipedCount.release(len);
                      }
+
+                     private void updateWriteCount(long delta) {
+                         writeCount += delta;
+                         outputDebugMessage("OUT write count=%d", writeCount);
+                     }
                  }) {
                 session.addPasswordIdentity(getCurrentTestName());
                 session.auth().verify(5L, TimeUnit.SECONDS);
@@ -557,17 +584,26 @@ public class KeyReExchangeTest extends BaseTestSupport {
             try (ClientSession session = client.connect(getCurrentTestName(), TEST_LOCALHOST, port).verify(7L, TimeUnit.SECONDS).getSession();
                  ByteArrayOutputStream sent = new ByteArrayOutputStream();
                  ByteArrayOutputStream out = new ByteArrayOutputStream() {
+                     private long writeCount = 0L;
+
                      @Override
                      public void write(int b) {
                          super.write(b);
+                         updateWriteCount(1L);
                          pipedCount.release(1);
                      }
 
                      @Override
                      public void write(byte[] b, int off, int len) {
                          super.write(b, off, len);
+                         updateWriteCount(len);
                          pipedCount.release(len);
                      }
+
+                     private void updateWriteCount(long delta) {
+                         writeCount += delta;
+                         outputDebugMessage("OUT write count=%d", writeCount);
+                     }
                  }) {
                 session.addPasswordIdentity(getCurrentTestName());
                 session.auth().verify(5L, TimeUnit.SECONDS);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d99d4806/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java b/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
index e45c51c..8bf16db 100644
--- a/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
+++ b/sshd-core/src/test/java/org/apache/sshd/server/ServerTest.java
@@ -167,7 +167,8 @@ public class ServerTest extends BaseTestSupport {
                 assertFalse("Authentication unexpectedly successful", authFuture.isSuccess());
             } while (authFuture.getException() == null);
 
-            assertNotNull("Missing auth future exception", authFuture.getException());
+            Throwable t = authFuture.getException();
+            assertNotNull("Missing auth future exception", t);
             assertTrue("Number trials (" + nbTrials + ") below min.=" + MAX_AUTH_REQUESTS, nbTrials > MAX_AUTH_REQUESTS);
         } finally {
             client.stop();

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/d99d4806/sshd-core/src/test/java/org/apache/sshd/util/test/OutputCountTrackingOutputStream.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/test/java/org/apache/sshd/util/test/OutputCountTrackingOutputStream.java b/sshd-core/src/test/java/org/apache/sshd/util/test/OutputCountTrackingOutputStream.java
index 0cb6ec4..b4eb84c 100644
--- a/sshd-core/src/test/java/org/apache/sshd/util/test/OutputCountTrackingOutputStream.java
+++ b/sshd-core/src/test/java/org/apache/sshd/util/test/OutputCountTrackingOutputStream.java
@@ -35,13 +35,13 @@ public class OutputCountTrackingOutputStream extends FilterOutputStream {
 
     @Override
     public void write(int b) throws IOException {
-        super.write(b);
+        out.write(b);
         updateWriteCount(1L);
     }
 
     @Override
     public void write(byte[] b, int off, int len) throws IOException {
-        super.write(b, off, len);
+        out.write(b, off, len); // don't call super since it calls the single 'write'
         updateWriteCount(len);
     }
 


[3/6] mina-sshd git commit: Send SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE if failed to create requested service

Posted by lg...@apache.org.
Send SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE if failed to create requested service

* As sanctioned by RFC4253 section 10


Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/05f5d36c
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/05f5d36c
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/05f5d36c

Branch: refs/heads/master
Commit: 05f5d36c073a3b0ec789ef4a77835b75fc0d6455
Parents: 2a3d0fd
Author: Lyor Goldstein <lg...@vmware.com>
Authored: Wed Dec 9 11:57:04 2015 +0200
Committer: Lyor Goldstein <lg...@vmware.com>
Committed: Wed Dec 9 11:57:04 2015 +0200

----------------------------------------------------------------------
 .../apache/sshd/server/session/ServerSessionImpl.java | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/05f5d36c/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSessionImpl.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSessionImpl.java b/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSessionImpl.java
index f506924..32ae420 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSessionImpl.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/session/ServerSessionImpl.java
@@ -66,7 +66,19 @@ public class ServerSessionImpl extends AbstractServerSession {
 
     @Override
     public void startService(String name) throws Exception {
-        currentService = ServiceFactory.Utils.create(getFactoryManager().getServiceFactories(), name, this);
+        currentService = ServiceFactory.Utils.create(
+                        getFactoryManager().getServiceFactories(),
+                        ValidateUtils.checkNotNullAndNotEmpty(name, "No service name"),
+                        this);
+        /*
+         * According to RFC4253:
+         *
+         *      If the server rejects the service request, it SHOULD send an
+         *      appropriate SSH_MSG_DISCONNECT message and MUST disconnect.
+         */
+        if (currentService == null) {
+            throw new SshException(SshConstants.SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE, "Unknown service: " + name);
+        }
     }
 
     @Override


[6/6] mina-sshd git commit: Added more detailed log messages as to the authentication progress

Posted by lg...@apache.org.
Added more detailed log messages as to the authentication progress


Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/0371baef
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/0371baef
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/0371baef

Branch: refs/heads/master
Commit: 0371baefc52c55961a2df43af5f86b5add5c71b8
Parents: 4cdbefc
Author: Lyor Goldstein <lg...@vmware.com>
Authored: Wed Dec 9 12:02:16 2015 +0200
Committer: Lyor Goldstein <lg...@vmware.com>
Committed: Wed Dec 9 12:02:16 2015 +0200

----------------------------------------------------------------------
 .../auth/UserAuthKeyboardInteractive.java       | 36 ++++++---
 .../client/session/ClientUserAuthService.java   | 78 +++++++++++++-------
 .../auth/UserAuthKeyboardInteractive.java       | 32 ++++++--
 .../apache/sshd/server/auth/UserAuthNone.java   |  3 +
 .../sshd/server/auth/UserAuthPassword.java      |  6 +-
 .../sshd/server/auth/UserAuthPublicKey.java     | 68 +++++++++++------
 .../server/session/ServerUserAuthService.java   | 76 +++++++++++++------
 7 files changed, 213 insertions(+), 86 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0371baef/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthKeyboardInteractive.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthKeyboardInteractive.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthKeyboardInteractive.java
index 553a544..203c3d1 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthKeyboardInteractive.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthKeyboardInteractive.java
@@ -160,6 +160,9 @@ public class UserAuthKeyboardInteractive extends AbstractUserAuth {
 
             String[] rep = getUserResponses(name, instruction, language_tag, prompt, echo);
             if (rep == null) {
+                if (log.isDebugEnabled()) {
+                    log.debug("process({})[{}] no responses for {}", session, service, name);
+                }
                 return false;
             }
 
@@ -173,19 +176,24 @@ public class UserAuthKeyboardInteractive extends AbstractUserAuth {
              * However it is the server's (!) responsibility to fail, so we only warn...
              */
             if (num != rep.length) {
-                log.warn("process({}) Mismatched prompts ({}) vs. responses count ({})", session, num, rep.length);
+                log.warn("process({})[{}] Mismatched prompts ({}) vs. responses count ({})",
+                         session, service, num, rep.length);
             }
 
             buffer = session.prepareBuffer(SshConstants.SSH_MSG_USERAUTH_INFO_RESPONSE, BufferUtils.clear(buffer));
             buffer.putInt(rep.length);
-            for (String r : rep) {
+            for (int index = 0; index < rep.length; index++) {
+                String r = rep[index];
+                if (log.isTraceEnabled()) {
+                    log.trace("process({})[{}] response #{}: {}", session, service, index + 1, r);
+                }
                 buffer.putString(r);
             }
             session.writePacket(buffer);
             return true;
         }
 
-        throw new IllegalStateException("process(" + session + ")[" + service + ")"
+        throw new IllegalStateException("process(" + session + ")[" + service + "]"
                 + " received unknown packet: cmd=" + SshConstants.getCommandMessageName(cmd));
     }
 
@@ -221,20 +229,30 @@ public class UserAuthKeyboardInteractive extends AbstractUserAuth {
      * a warning...)
      */
     protected String[] getUserResponses(String name, String instruction, String lang, String[] prompt, boolean[] echo) {
+        ClientSession session = getClientSession();
         int num = GenericUtils.length(prompt);
         if (num == 0) {
+            if (log.isDebugEnabled()) {
+                log.debug("getUserResponses({}) no prompts for interaction={}", session, name);
+            }
             return GenericUtils.EMPTY_STRING_ARRAY;
         }
 
         String candidate = getCurrentPasswordCandidate();
         if (useCurrentPassword(candidate, name, instruction, lang, prompt, echo)) {
-            return new String[]{candidate};
-        } else {
-            ClientSession session = getClientSession();
-            UserInteraction ui = session.getUserInteraction();
-            if ((ui != null) && ui.isInteractionAllowed(session)) {
-                return ui.interactive(session, name, instruction, lang, prompt, echo);
+            if (log.isDebugEnabled()) {
+                log.debug("getUserResponses({}) use password candidate for interaction={}", session, name);
             }
+            return new String[]{candidate};
+        }
+
+        UserInteraction ui = session.getUserInteraction();
+        if ((ui != null) && ui.isInteractionAllowed(session)) {
+            return ui.interactive(session, name, instruction, lang, prompt, echo);
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug("getUserResponses({}) no user interaction for name={}", session, name);
         }
 
         return null;

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0371baef/sshd-core/src/main/java/org/apache/sshd/client/session/ClientUserAuthService.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientUserAuthService.java b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientUserAuthService.java
index 0e2fe5c..1e3ca06 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientUserAuthService.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientUserAuthService.java
@@ -52,7 +52,7 @@ public class ClientUserAuthService extends AbstractCloseable implements Service,
      */
     private final AuthFuture authFuture;
 
-    private final ClientSessionImpl session;
+    private final ClientSessionImpl clientSession;
 
     private List<Object> identities;
     private String service;
@@ -68,27 +68,35 @@ public class ClientUserAuthService extends AbstractCloseable implements Service,
         if (!(s instanceof ClientSessionImpl)) {
             throw new IllegalStateException("Client side service used on server side");
         }
-        session = (ClientSessionImpl) s;
-        authFuture = new DefaultAuthFuture(session.getLock());
-        authFactories = session.getUserAuthFactories();
+        clientSession = (ClientSessionImpl) s;
+        authFuture = new DefaultAuthFuture(clientSession.getLock());
+        authFactories = clientSession.getUserAuthFactories();
         clientMethods = new ArrayList<>();
 
-        String prefs = PropertyResolverUtils.getString(session, ClientAuthenticationManager.PREFERRED_AUTHS);
-        if (!GenericUtils.isEmpty(prefs)) {
-            for (String pref : prefs.split(",")) {
+        String prefs = PropertyResolverUtils.getString(s, ClientAuthenticationManager.PREFERRED_AUTHS);
+        if (GenericUtils.isEmpty(prefs)) {
+            for (NamedFactory<UserAuth> factory : authFactories) {
+                clientMethods.add(factory.getName());
+            }
+        } else {
+            if (log.isDebugEnabled()) {
+                log.debug("ClientUserAuthService({}) use configured preferrences: {}", s, prefs);
+            }
+
+            for (String pref : GenericUtils.split(prefs, ',')) {
                 NamedFactory<UserAuth> factory = NamedResource.Utils.findByName(pref, String.CASE_INSENSITIVE_ORDER, authFactories);
                 if (factory != null) {
                     clientMethods.add(pref);
                 } else {
                     if (log.isDebugEnabled()) {
-                        log.debug("ClientUserAuthService({}) skip unknown prefered authentication method: {}", s, pref);
+                        log.debug("ClientUserAuthService({}) skip unknown preferred authentication method: {}", s, pref);
                     }
                 }
             }
-        } else {
-            for (NamedFactory<UserAuth> factory : authFactories) {
-                clientMethods.add(factory.getName());
-            }
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug("ClientUserAuthService({}) client methods: {}", s, clientMethods);
         }
     }
 
@@ -99,7 +107,7 @@ public class ClientUserAuthService extends AbstractCloseable implements Service,
 
     @Override
     public ClientSession getClientSession() {
-        return session;
+        return clientSession;
     }
 
     @Override
@@ -111,12 +119,14 @@ public class ClientUserAuthService extends AbstractCloseable implements Service,
         this.identities = new ArrayList<>(identities);
         this.service = service;
 
+        ClientSession session = getClientSession();
+        String username = session.getUsername();
         if (log.isDebugEnabled()) {
-            log.debug("auth({})[{}] Send SSH_MSG_USERAUTH_REQUEST for 'none'", getClientSession(), service);
+            log.debug("auth({})[{}] Send SSH_MSG_USERAUTH_REQUEST for 'none'", session, service);
         }
-        String username = session.getUsername();
+
         Buffer buffer = session.createBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST, username.length() + service.length() + Integer.SIZE);
-        buffer.putString(session.getUsername());
+        buffer.putString(username);
         buffer.putString(service);
         buffer.putString("none");
         session.writePacket(buffer);
@@ -126,6 +136,7 @@ public class ClientUserAuthService extends AbstractCloseable implements Service,
 
     @Override
     public void process(int cmd, Buffer buffer) throws Exception {
+        ClientSession session = getClientSession();
         if (this.authFuture.isSuccess()) {
             throw new IllegalStateException("UserAuth message delivered to authenticated client");
         } else if (this.authFuture.isDone()) {
@@ -159,41 +170,51 @@ public class ClientUserAuthService extends AbstractCloseable implements Service,
      */
     protected void processUserAuth(Buffer buffer) throws Exception {
         int cmd = buffer.getUByte();
+        ClientSession session = getClientSession();
         if (cmd == SshConstants.SSH_MSG_USERAUTH_SUCCESS) {
             if (log.isDebugEnabled()) {
                 log.debug("processUserAuth({}) SSH_MSG_USERAUTH_SUCCESS Succeeded with {}",
-                          getClientSession(), userAuth);
+                          session, userAuth);
             }
             if (userAuth != null) {
-                userAuth.destroy();
-                userAuth = null;
+                try {
+                    userAuth.destroy();
+                } finally {
+                    userAuth = null;
+                }
             }
             session.setAuthenticated();
-            session.switchToNextService();
+            ((ClientSessionImpl) session).switchToNextService();
             // Will wake up anyone sitting in waitFor
             authFuture.setAuthed(true);
             return;
         }
+
         if (cmd == SshConstants.SSH_MSG_USERAUTH_FAILURE) {
             String mths = buffer.getString();
             boolean partial = buffer.getBoolean();
             if (log.isDebugEnabled()) {
                 log.debug("processUserAuth({}) Received SSH_MSG_USERAUTH_FAILURE - partial={}, methods={}",
-                          getClientSession(), partial, mths);
+                          session, partial, mths);
             }
             if (partial || (serverMethods == null)) {
                 serverMethods = Arrays.asList(GenericUtils.split(mths, ','));
                 if (userAuth != null) {
-                    userAuth.destroy();
-                    userAuth = null;
+                    try {
+                        userAuth.destroy();
+                    } finally {
+                        userAuth = null;
+                    }
                 }
             }
             tryNext();
             return;
         }
+
         if (userAuth == null) {
-            throw new IllegalStateException("Received unknown packet");
+            throw new IllegalStateException("Received unknown packet: " + SshConstants.getCommandMessageName(cmd));
         }
+
         buffer.rpos(buffer.rpos() - 1);
         if (!userAuth.process(buffer)) {
             tryNext();
@@ -201,6 +222,7 @@ public class ClientUserAuthService extends AbstractCloseable implements Service,
     }
 
     protected void tryNext() throws Exception {
+        ClientSession session = getClientSession();
         // Loop until we find something to try
         while (true) {
             if (userAuth == null) {
@@ -218,10 +240,12 @@ public class ClientUserAuthService extends AbstractCloseable implements Service,
 
             if (currentMethod >= clientMethods.size()) {
                 if (log.isDebugEnabled()) {
-                    log.debug("tryNext({}) exhausted all methods", getClientSession());
+                    log.debug("tryNext({}) exhausted all methods - client={}, server={}",
+                              session, clientMethods, serverMethods);
                 }
 
-                authFuture.setAuthed(false);
+                // also wake up anyone sitting in waitFor
+                authFuture.setException(new SshException(SshConstants.SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE, "No more authentication methods available"));
                 return;
             }
 
@@ -231,7 +255,7 @@ public class ClientUserAuthService extends AbstractCloseable implements Service,
                 throw new UnsupportedOperationException("Failed to find a user-auth factory for method=" + method);
             }
             if (log.isDebugEnabled()) {
-                log.debug("tryNext({}) attempting method={}", getClientSession(), method);
+                log.debug("tryNext({}) attempting method={}", session, method);
             }
 
             userAuth.init(session, service, identities);

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0371baef/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthKeyboardInteractive.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthKeyboardInteractive.java b/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthKeyboardInteractive.java
index a384520..9639929 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthKeyboardInteractive.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthKeyboardInteractive.java
@@ -24,6 +24,7 @@ import java.util.List;
 
 import org.apache.sshd.common.SshConstants;
 import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.util.GenericUtils;
 import org.apache.sshd.common.util.buffer.Buffer;
 import org.apache.sshd.common.util.buffer.BufferUtils;
 import org.apache.sshd.server.auth.keyboard.InteractiveChallenge;
@@ -45,13 +46,15 @@ public class UserAuthKeyboardInteractive extends AbstractUserAuth {
     @Override
     protected Boolean doAuth(Buffer buffer, boolean init) throws Exception {
         ServerSession session = getServerSession();
+        String username = getUsername();
         KeyboardInteractiveAuthenticator auth = session.getKeyboardInteractiveAuthenticator();
         if (init) {
             String lang = buffer.getString();
             String subMethods = buffer.getString();
             if (auth == null) {
                 if (log.isDebugEnabled()) {
-                    log.debug("doAuth({}) no interactive authenticator to generate challenge", session);
+                    log.debug("doAuth({}@{})[methods={}, lang={}] - no interactive authenticator to generate challenge",
+                              username, session, subMethods, lang);
                 }
                 return false;
             }
@@ -59,11 +62,19 @@ public class UserAuthKeyboardInteractive extends AbstractUserAuth {
             InteractiveChallenge challenge = auth.generateChallenge(session, getUsername(), lang, subMethods);
             if (challenge == null) {
                 if (log.isDebugEnabled()) {
-                    log.debug("doAuth({}) no interactive challenge generated", session);
+                    log.debug("doAuth({}@{})[methods={}, lang={}] - no interactive challenge generated",
+                              username, session, subMethods, lang);
                 }
                 return false;
             }
 
+            if (log.isDebugEnabled()) {
+                log.debug("doAuth({}@{})[methods={}, lang={}] challenge name={}, instruction={}, lang={}, num. prompts={}",
+                          username, session, subMethods, lang,
+                          challenge.getInteractionName(), challenge.getInteractionInstruction(),
+                          challenge.getLanguageTag(), GenericUtils.size(challenge.getPrompts()));
+            }
+
             // Prompt for password
             buffer = session.prepareBuffer(SshConstants.SSH_MSG_USERAUTH_INFO_REQUEST, BufferUtils.clear(buffer));
             challenge.append(buffer);
@@ -78,17 +89,28 @@ public class UserAuthKeyboardInteractive extends AbstractUserAuth {
             int num = buffer.getInt();
             List<String> responses = (num <= 0) ? Collections.<String>emptyList() : new ArrayList<String>(num);
             for (int index = 0; index < num; index++) {
-                responses.add(buffer.getString());
+                String value = buffer.getString();
+                if (log.isTraceEnabled()) {
+                    log.trace("doAuth({}@{}) response #{}: {}", username, session, index + 1, value);
+                }
+                responses.add(value);
             }
 
             if (auth == null) {
                 if (log.isDebugEnabled()) {
-                    log.debug("doAuth({}) no interactive authenticator to validate responses", session);
+                    log.debug("doAuth({}@{}) no interactive authenticator to validate {} responses",
+                              username, session, num);
                 }
                 return false;
             }
 
-            return auth.authenticate(session, getUsername(), responses);
+            boolean authed = auth.authenticate(session, username, responses);
+            if (log.isDebugEnabled()) {
+                log.debug("doAuth({}@{}) authenticate {} responses result: {}",
+                          username, session, num, authed);
+            }
+
+            return authed;
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0371baef/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthNone.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthNone.java b/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthNone.java
index 8cc708e..8b20eed 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthNone.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthNone.java
@@ -34,6 +34,9 @@ public class UserAuthNone extends AbstractUserAuth {
 
     @Override
     public Boolean doAuth(Buffer buffer, boolean init) {
+        if (log.isDebugEnabled()) {
+            log.debug("doAuth({}@{}) init={}", getUsername(), getServerSession(), init);
+        }
         return Boolean.TRUE;
     }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0371baef/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthPassword.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthPassword.java b/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthPassword.java
index d59720b..b0e72d4 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthPassword.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthPassword.java
@@ -77,7 +77,11 @@ public class UserAuthPassword extends AbstractUserAuth {
         }
 
         try {
-            return auth.authenticate(username, password, session);
+            boolean authed = auth.authenticate(username, password, session);
+            if (log.isDebugEnabled()) {
+                log.debug("checkPassword({}) authentication result: {}", session, authed);
+            }
+            return authed;
         } catch (PasswordChangeRequiredException e) {
             if (log.isDebugEnabled()) {
                 log.debug("checkPassword({}) password change required: {}", session, e.getMessage());

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0371baef/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthPublicKey.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthPublicKey.java b/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthPublicKey.java
index 5299a84..08b05b7 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthPublicKey.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/auth/UserAuthPublicKey.java
@@ -55,10 +55,11 @@ public class UserAuthPublicKey extends AbstractUserAuth {
         buffer.wpos(buffer.rpos() + len);
 
         ServerSession session = getServerSession();
+        String username = getUsername();
         PublicKey key = buffer.getRawPublicKey();
         if (log.isDebugEnabled()) {
-            log.debug("doAuth({}) verify key type={}, fingerprint={}",
-                      session, alg, KeyUtils.getFingerPrint(key));
+            log.debug("doAuth({}@{}) verify key type={}, fingerprint={}",
+                      username, session, alg, KeyUtils.getFingerPrint(key));
         }
 
         Signature verifier = ValidateUtils.checkNotNull(
@@ -72,38 +73,63 @@ public class UserAuthPublicKey extends AbstractUserAuth {
         PublickeyAuthenticator authenticator = session.getPublickeyAuthenticator();
         if (authenticator == null) {
             if (log.isDebugEnabled()) {
-                log.debug("doAuth({}) no authenticator", session);
+                log.debug("doAuth({}@{}) key type={}, fingerprint={} - no authenticator",
+                          username, session, alg, KeyUtils.getFingerPrint(key));
             }
             return false;
         }
 
-        if (!authenticator.authenticate(getUsername(), key, session)) {
+        boolean authed = authenticator.authenticate(username, key, session);
+        if (log.isDebugEnabled()) {
+            log.debug("doAuth({}@{}) key type={}, fingerprint={} - authentication result: {}",
+                      username, session, alg, KeyUtils.getFingerPrint(key), authed);
+        }
+        if (!authed) {
             return Boolean.FALSE;
         }
 
         if (!hasSig) {
+            if (log.isDebugEnabled()) {
+                log.debug("doAuth({}@{}) send SSH_MSG_USERAUTH_PK_OK for key type={}, fingerprint={}",
+                           username, session, alg, KeyUtils.getFingerPrint(key));
+            }
+
             Buffer buf = session.prepareBuffer(SshConstants.SSH_MSG_USERAUTH_PK_OK, BufferUtils.clear(buffer));
             buf.putString(alg);
             buf.putRawBytes(buffer.array(), oldPos, 4 + len);
             session.writePacket(buf);
             return null;
-        } else {
-            Buffer buf = new ByteArrayBuffer();
-            buf.putBytes(session.getKex().getH());
-            buf.putByte(SshConstants.SSH_MSG_USERAUTH_REQUEST);
-            buf.putString(getUsername());
-            buf.putString(getService());
-            buf.putString(getName());
-            buf.putBoolean(true);
-            buf.putString(alg);
-            buffer.rpos(oldPos);
-            buffer.wpos(oldPos + 4 + len);
-            buf.putBuffer(buffer);
-            verifier.update(buf.array(), buf.rpos(), buf.available());
-            if (!verifier.verify(sig)) {
-                throw new Exception("Key verification failed");
-            }
-            return Boolean.TRUE;
         }
+
+        // verify signature
+        Buffer buf = new ByteArrayBuffer();
+        buf.putBytes(session.getKex().getH());
+        buf.putByte(SshConstants.SSH_MSG_USERAUTH_REQUEST);
+        buf.putString(username);
+        buf.putString(getService());
+        buf.putString(getName());
+        buf.putBoolean(true);
+        buf.putString(alg);
+        buffer.rpos(oldPos);
+        buffer.wpos(oldPos + 4 + len);
+        buf.putBuffer(buffer);
+        verifier.update(buf.array(), buf.rpos(), buf.available());
+        if (log.isTraceEnabled()) {
+            log.trace("doAuth({}@{}) key type={}, fingerprint={} - verification data={}",
+                      username, session, alg, KeyUtils.getFingerPrint(key), buf.printHex());
+            log.trace("doAuth({}@{}) key type={}, fingerprint={} - expected signature={}",
+                    username, session, alg, KeyUtils.getFingerPrint(key), BufferUtils.printHex(sig));
+        }
+
+        if (!verifier.verify(sig)) {
+            throw new Exception("Key verification failed");
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug("doAuth({}@{}) key type={}, fingerprint={} - verified",
+                      username, session, alg, KeyUtils.getFingerPrint(key));
+        }
+
+        return Boolean.TRUE;
     }
 }

http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/0371baef/sshd-core/src/main/java/org/apache/sshd/server/session/ServerUserAuthService.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/server/session/ServerUserAuthService.java b/sshd-core/src/main/java/org/apache/sshd/server/session/ServerUserAuthService.java
index 977bdc4..a67b40f 100644
--- a/sshd-core/src/main/java/org/apache/sshd/server/session/ServerUserAuthService.java
+++ b/sshd-core/src/main/java/org/apache/sshd/server/session/ServerUserAuthService.java
@@ -45,7 +45,7 @@ import org.apache.sshd.server.auth.UserAuthNoneFactory;
  */
 public class ServerUserAuthService extends AbstractCloseable implements Service, ServerSessionHolder {
 
-    private final ServerSession session;
+    private final ServerSession serverSession;
     private List<NamedFactory<UserAuth>> userAuthFactories;
     private List<List<String>> authMethods;
     private String authUserName;
@@ -62,22 +62,25 @@ public class ServerUserAuthService extends AbstractCloseable implements Service,
             throw new SshException("Session already authenticated");
         }
 
-        this.session = (ServerSession) s;
-        maxAuthRequests = PropertyResolverUtils.getIntProperty(session, ServerAuthenticationManager.MAX_AUTH_REQUESTS, ServerAuthenticationManager.DEFAULT_MAX_AUTH_REQUESTS);
+        serverSession = (ServerSession) s;
+        maxAuthRequests = PropertyResolverUtils.getIntProperty(s, ServerAuthenticationManager.MAX_AUTH_REQUESTS, ServerAuthenticationManager.DEFAULT_MAX_AUTH_REQUESTS);
 
-        List<NamedFactory<UserAuth>> factories = session.getUserAuthFactories();
+        List<NamedFactory<UserAuth>> factories = serverSession.getUserAuthFactories();
         userAuthFactories = new ArrayList<>(factories);
         // Get authentication methods
         authMethods = new ArrayList<>();
 
-        String mths = PropertyResolverUtils.getString(session, ServerFactoryManager.AUTH_METHODS);
+        String mths = PropertyResolverUtils.getString(s, ServerFactoryManager.AUTH_METHODS);
         if (GenericUtils.isEmpty(mths)) {
             for (NamedFactory<UserAuth> uaf : factories) {
                 authMethods.add(new ArrayList<>(Collections.singletonList(uaf.getName())));
             }
         } else {
+            if (log.isDebugEnabled()) {
+                log.debug("ServerUserAuthService({}) using configured methods={}", s, mths);
+            }
             for (String mthl : mths.split("\\s")) {
-                authMethods.add(new ArrayList<>(Arrays.asList(mthl.split(","))));
+                authMethods.add(new ArrayList<>(Arrays.asList(GenericUtils.split(mthl, ','))));
             }
         }
         // Verify all required methods are supported
@@ -91,7 +94,8 @@ public class ServerUserAuthService extends AbstractCloseable implements Service,
         }
 
         if (log.isDebugEnabled()) {
-            log.debug("Authorized authentication methods: {}", NamedResource.Utils.getNames(userAuthFactories));
+            log.debug("ServerUserAuthService({}) authorized authentication methods: {}",
+                      s, NamedResource.Utils.getNames(userAuthFactories));
         }
     }
 
@@ -107,17 +111,21 @@ public class ServerUserAuthService extends AbstractCloseable implements Service,
 
     @Override
     public ServerSession getServerSession() {
-        return session;
+        return serverSession;
     }
 
     @Override
     public void process(int cmd, Buffer buffer) throws Exception {
         Boolean authed = Boolean.FALSE;
+        ServerSession session = getServerSession();
 
         if (cmd == SshConstants.SSH_MSG_USERAUTH_REQUEST) {
-            if (this.currentAuth != null) {
-                this.currentAuth.destroy();
-                this.currentAuth = null;
+            if (currentAuth != null) {
+                try {
+                    currentAuth.destroy();
+                } finally {
+                    currentAuth = null;
+                }
             }
 
             String username = buffer.getString();
@@ -125,7 +133,7 @@ public class ServerUserAuthService extends AbstractCloseable implements Service,
             String method = buffer.getString();
             if (log.isDebugEnabled()) {
                 log.debug("process({}) Received SSH_MSG_USERAUTH_REQUEST user={}, service={}, method={}",
-                          getServerSession(), username, service, method);
+                          session, username, service, method);
             }
 
             if (this.authUserName == null || this.authService == null) {
@@ -141,26 +149,30 @@ public class ServerUserAuthService extends AbstractCloseable implements Service,
             // TODO: verify that the service is supported
             this.authMethod = method;
             if (nbAuthRequests++ > maxAuthRequests) {
-                session.disconnect(SshConstants.SSH2_DISCONNECT_PROTOCOL_ERROR, "Too many authentication failures");
+                session.disconnect(SshConstants.SSH2_DISCONNECT_PROTOCOL_ERROR, "Too many authentication failures: " + nbAuthRequests);
                 return;
             }
 
             if (log.isDebugEnabled()) {
-                log.debug("process({}) Authenticating user '{}' with service '{}' and method '{}'",
-                          getServerSession(), username, service, method);
+                log.debug("process({}) Authenticating user '{}' with service '{}' and method '{}' (attempt {} / {})",
+                          session, username, service, method, nbAuthRequests, maxAuthRequests);
             }
 
             NamedFactory<UserAuth> factory = NamedResource.Utils.findByName(method, String.CASE_INSENSITIVE_ORDER, userAuthFactories);
             if (factory != null) {
-                currentAuth = factory.create();
+                currentAuth = ValidateUtils.checkNotNull(factory.create(), "No authenticator created for method=%s", method);
                 try {
                     authed = currentAuth.auth(session, username, service, buffer);
                 } catch (Exception e) {
                     if (log.isDebugEnabled()) {
                         log.debug("process({}) Failed ({}) to authenticate using method={}: {}",
-                                  getServerSession(), e.getClass().getSimpleName(), method, e.getMessage());
+                                  session, e.getClass().getSimpleName(), method, e.getMessage());
                     }
                 }
+            } else {
+                if (log.isDebugEnabled()) {
+                    log.debug("process({}) no authentication factory for method={}", session, method);
+                }
             }
         } else {
             if (this.currentAuth == null) {
@@ -169,8 +181,9 @@ public class ServerUserAuthService extends AbstractCloseable implements Service,
             }
             if (log.isDebugEnabled()) {
                 log.debug("process({}) Received authentication message={} for mechanism={}",
-                          getServerSession(), SshConstants.getCommandMessageName(cmd), currentAuth.getName());
+                          session, SshConstants.getCommandMessageName(cmd), currentAuth.getName());
             }
+
             buffer.rpos(buffer.rpos() - 1);
             try {
                 authed = currentAuth.next(buffer);
@@ -178,7 +191,7 @@ public class ServerUserAuthService extends AbstractCloseable implements Service,
                 // Continue
                 if (log.isDebugEnabled()) {
                     log.debug("process({}) Failed ({}) to authenticate using method={}: {}",
-                              getServerSession(), e.getClass().getSimpleName(), currentAuth.getName(), e.getMessage());
+                              session, e.getClass().getSimpleName(), currentAuth.getName(), e.getMessage());
                 }
             }
         }
@@ -196,12 +209,13 @@ public class ServerUserAuthService extends AbstractCloseable implements Service,
         String username = (currentAuth == null) ? null : currentAuth.getUsername();
         if (log.isDebugEnabled()) {
             log.debug("handleAuthenticationInProgress({}@{}) {}",
-                      username, session, SshConstants.getCommandMessageName(cmd));
+                      username, getServerSession(), SshConstants.getCommandMessageName(cmd));
         }
     }
 
     protected void handleAuthenticationSuccess(int cmd, Buffer buffer) throws Exception {
         String username = ValidateUtils.checkNotNull(currentAuth, "No current auth").getUsername();
+        ServerSession session = getServerSession();
         if (log.isDebugEnabled()) {
             log.debug("handleAuthenticationSuccess({}@{}) {}",
                       username, session, SshConstants.getCommandMessageName(cmd));
@@ -242,6 +256,11 @@ public class ServerUserAuthService extends AbstractCloseable implements Service,
                 buffer = session.createBuffer(SshConstants.SSH_MSG_USERAUTH_BANNER, welcomeBanner.length() + lang.length() + Long.SIZE);
                 buffer.putString(welcomeBanner);
                 buffer.putString(lang);
+
+                if (log.isDebugEnabled()) {
+                    log.debug("handleAuthenticationSuccess({}@{}) send banner (length={}, lang={})",
+                              username, session, welcomeBanner.length(), lang);
+                }
                 session.writePacket(buffer);
             }
 
@@ -263,7 +282,12 @@ public class ServerUserAuthService extends AbstractCloseable implements Service,
                     sb.append(l.get(0));
                 }
             }
-            buffer.putString(sb.toString());
+
+            String remaining = sb.toString();
+            if (log.isDebugEnabled()) {
+                log.debug("handleAuthenticationSuccess({}@{}) remaining methods={}", username, session, remaining);
+            }
+            buffer.putString(remaining);
             buffer.putBoolean(true);    // partial success ...
             session.writePacket(buffer);
         }
@@ -277,6 +301,7 @@ public class ServerUserAuthService extends AbstractCloseable implements Service,
 
     protected void handleAuthenticationFailure(int cmd, Buffer buffer) throws Exception {
         String username = (currentAuth == null) ? null : currentAuth.getUsername();
+        ServerSession session = getServerSession();
         if (log.isDebugEnabled()) {
             log.debug("handleAuthenticationFailure({}@{}) {}",
                       username, session, SshConstants.getCommandMessageName(cmd));
@@ -295,7 +320,12 @@ public class ServerUserAuthService extends AbstractCloseable implements Service,
                 }
             }
         }
-        buffer.putString(sb.toString());
+
+        String remaining = sb.toString();
+        if (log.isDebugEnabled()) {
+            log.debug("handleAuthenticationFailure({}@{}) remaining methods: {}", username, session, remaining);
+        }
+        buffer.putString(remaining);
         buffer.putBoolean(false);   // no partial success ...
         session.writePacket(buffer);
 
@@ -309,6 +339,6 @@ public class ServerUserAuthService extends AbstractCloseable implements Service,
     }
 
     public ServerFactoryManager getFactoryManager() {
-        return session.getFactoryManager();
+        return serverSession.getFactoryManager();
     }
 }


[5/6] mina-sshd git commit: [SSHD-610] Disconnect reason should be propagated as error message

Posted by lg...@apache.org.
[SSHD-610] Disconnect reason should be propagated as error message


Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/4cdbefc6
Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/4cdbefc6
Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/4cdbefc6

Branch: refs/heads/master
Commit: 4cdbefc61c80f4d089dfb8c48fe19258cfa2ef24
Parents: d99d480
Author: Lyor Goldstein <lg...@vmware.com>
Authored: Wed Dec 9 12:01:15 2015 +0200
Committer: Lyor Goldstein <lg...@vmware.com>
Committed: Wed Dec 9 12:01:15 2015 +0200

----------------------------------------------------------------------
 .../apache/sshd/client/session/ClientSessionImpl.java    | 11 +++++++++++
 1 file changed, 11 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/4cdbefc6/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
----------------------------------------------------------------------
diff --git a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
index d10e8cd..5a0a0f5 100644
--- a/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
+++ b/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
@@ -146,6 +146,17 @@ public class ClientSessionImpl extends AbstractClientSession {
         super.exceptionCaught(t);
     }
 
+    @Override
+    protected void handleDisconnect(int code, String msg, String lang, Buffer buffer) throws Exception {
+        synchronized (lock) {
+            if (!authFuture.isDone()) {
+                authFuture.setException(new SshException(code, msg));
+            }
+        }
+
+        super.handleDisconnect(code, msg, lang, buffer);
+    }
+
     protected String nextServiceName() {
         synchronized (lock) {
             return nextServiceFactory.getName();