This is an automated email from the ASF dual-hosted git repository.

twolf pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git


The following commit(s) were added to refs/heads/master by this push:
     new cb38574  [SSHD-1141] Tolerate SSH_MSG_USERAUTH_PK_OK with wrong 
algorithm
     new 87d5fc4  Merge pull request #185 from tomaswolf/SSHD-1141
cb38574 is described below

commit cb385742bf0962cbfcda011d7c4a89aa6ab7716a
Author: Thomas Wolf <tw...@apache.org>
AuthorDate: Tue Mar 23 19:51:41 2021 +0100

    [SSHD-1141] Tolerate SSH_MSG_USERAUTH_PK_OK with wrong algorithm
    
    SSH_MSG_USERAUTH_PK_OK is supposed to say "the server will accept the
    key and signature algorithm the client sent in the previous
    SSH_MSG_USERAUTH_REQUEST message". It is _not_ intended as a mechanism
    for the server to override the client's choice of algorithm.
    
    See RFC 4252.[1]
    
    Just log a warning if a server sends back an unexpected "algorithm".
    (Github's SSH-2.0-babeld-383743ad was found to reply "ssh-rsa" even
    if the client requested "rsa-sha2-512" and the server had previously
    announced via server-sig-algs that it supports rsa-sha2-512. Subsequent
    authentication with rsa-sha2-512 then still works.)
    
    Of course the returned public key must still match exactly.
    
    The client always uses the signature algorithm that it had announced
    it would use.
    
    [1] https://tools.ietf.org/html/rfc4252#page-9
---
 .../sshd/client/auth/pubkey/UserAuthPublicKey.java | 53 ++++++-------
 .../common/auth/PublicKeyAuthenticationTest.java   | 90 +++++++++++++---------
 2 files changed, 76 insertions(+), 67 deletions(-)

diff --git 
a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKey.java
 
b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKey.java
index d34a3df..eec73ec 100644
--- 
a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKey.java
+++ 
b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/UserAuthPublicKey.java
@@ -23,7 +23,6 @@ import java.io.IOException;
 import java.security.KeyPair;
 import java.security.PublicKey;
 import java.security.spec.InvalidKeySpecException;
-import java.util.Collection;
 import java.util.Deque;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -61,6 +60,7 @@ public class UserAuthPublicKey extends AbstractUserAuth 
implements SignatureFact
     protected Iterator<PublicKeyIdentity> keys;
     protected PublicKeyIdentity current;
     protected List<NamedFactory<Signature>> factories;
+    protected String chosenAlgorithm;
 
     public UserAuthPublicKey() {
         this(null);
@@ -102,6 +102,7 @@ public class UserAuthPublicKey extends AbstractUserAuth 
implements SignatureFact
         if (current == null) {
             // Just to be safe. (Paranoia)
             currentAlgorithms.clear();
+            chosenAlgorithm = null;
         } else if (!currentAlgorithms.isEmpty()) {
             currentAlgorithm = currentAlgorithms.poll();
         }
@@ -114,7 +115,7 @@ public class UserAuthPublicKey extends AbstractUserAuth 
implements SignatureFact
                         session, service, e.getClass().getSimpleName(), 
e.getMessage(), e);
                 throw new RuntimeSshException(e);
             }
-
+            chosenAlgorithm = null;
             if (current == null) {
                 if (debugEnabled) {
                     log.debug("resolveAttemptedPublicKeyIdentity({})[{}] no 
more keys to send", session, service);
@@ -175,6 +176,7 @@ public class UserAuthPublicKey extends AbstractUserAuth 
implements SignatureFact
             reporter.signalAuthenticationAttempt(session, service, keyPair, 
currentAlgorithm);
         }
 
+        chosenAlgorithm = currentAlgorithm;
         Buffer buffer = 
session.createBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST);
         buffer.putString(session.getUsername());
         buffer.putString(service);
@@ -228,46 +230,31 @@ public class UserAuthPublicKey extends AbstractUserAuth 
implements SignatureFact
         }
 
         PublicKey pubKey = keyPair.getPublic();
-        String curKeyType = KeyUtils.getKeyType(pubKey);
-        String rspKeyType = buffer.getString();
-        Collection<String> aliases = 
KeyUtils.getAllEquivalentKeyTypes(curKeyType);
-        String algo;
-        // SSHD-1104 see if key aliases used
-        if (GenericUtils.isEmpty(aliases)) {
-            algo = curKeyType;
-            if (!rspKeyType.equals(algo)) {
-                throw new InvalidKeySpecException(
-                        "processAuthDataRequest(" + session + ")[" + service + 
"][" + name + "]"
-                                                  + " mismatched key types: 
expected=" + algo + ", actual=" + rspKeyType);
-            }
-        } else {
-            if (GenericUtils.findFirstMatchingMember(n -> 
n.equalsIgnoreCase(rspKeyType), aliases) == null) {
-                throw new InvalidKeySpecException(
-                        "processAuthDataRequest(" + session + ")[" + service + 
"][" + name + "]"
-                                                  + " unsupported key type: 
expected=" + aliases + ", actual=" + rspKeyType);
-            }
-            algo = rspKeyType;
-        }
 
+        String rspKeyType = buffer.getString();
         PublicKey rspKey = buffer.getPublicKey();
+
+        if (debugEnabled) {
+            log.debug("processAuthDataRequest({})[{}][{}] 
SSH_MSG_USERAUTH_PK_OK type={}, fingerprint={}",
+                    session, service, name, rspKeyType, 
KeyUtils.getFingerPrint(rspKey));
+        }
         if (!KeyUtils.compareKeys(rspKey, pubKey)) {
             throw new InvalidKeySpecException(
                     "processAuthDataRequest(" + session + ")[" + service + 
"][" + name + "]"
-                                              + " mismatched " + algo + " 
keys: expected=" + KeyUtils.getFingerPrint(pubKey)
+                                              + " mismatched " + rspKeyType + 
" keys: expected=" + KeyUtils.getFingerPrint(pubKey)
                                               + ", actual=" + 
KeyUtils.getFingerPrint(rspKey));
         }
 
-        if (debugEnabled) {
-            log.debug("processAuthDataRequest({})[{}][{}] 
SSH_MSG_USERAUTH_PK_OK type={}, fingerprint={}",
-                    session, service, name, rspKeyType, 
KeyUtils.getFingerPrint(rspKey));
+        if (!chosenAlgorithm.equalsIgnoreCase(rspKeyType)) {
+            log.warn("processAuthDataRequest({})[{}][{}] sent algorithm {} but 
got back {} from {}",
+                    session, service, name, chosenAlgorithm, rspKeyType, 
session.getServerVersion());
         }
 
         String username = session.getUsername();
+        String algo = chosenAlgorithm;
         buffer = session.createBuffer(SshConstants.SSH_MSG_USERAUTH_REQUEST,
-                GenericUtils.length(username) + GenericUtils.length(service)
-                                                                             + 
GenericUtils.length(name)
-                                                                             + 
GenericUtils.length(algo)
-                                                                             + 
ByteArrayBuffer.DEFAULT_SIZE + Long.SIZE);
+                GenericUtils.length(username) + GenericUtils.length(service) + 
GenericUtils.length(name)
+                + GenericUtils.length(algo) + ByteArrayBuffer.DEFAULT_SIZE + 
Long.SIZE);
         buffer.putString(username);
         buffer.putString(service);
         buffer.putString(name);
@@ -275,6 +262,11 @@ public class UserAuthPublicKey extends AbstractUserAuth 
implements SignatureFact
         buffer.putString(algo);
         buffer.putPublicKey(pubKey);
 
+        if (debugEnabled) {
+            log.debug(
+                    "processAuthDataRequest({})[{}][{}]: signing with 
algorithm {}", //$NON-NLS-1$
+                    session, service, name, algo);
+        }
         byte[] sig = appendSignature(session, service, name, username, algo, 
pubKey, buffer);
         PublicKeyAuthenticationReporter reporter = 
session.getPublicKeyAuthenticationReporter();
         if (reporter != null) {
@@ -363,6 +355,7 @@ public class UserAuthPublicKey extends AbstractUserAuth 
implements SignatureFact
     protected void releaseKeys() throws IOException {
         currentAlgorithms.clear();
         current = null;
+        chosenAlgorithm = null;
         try {
             if (keys instanceof Closeable) {
                 if (log.isTraceEnabled()) {
diff --git 
a/sshd-core/src/test/java/org/apache/sshd/common/auth/PublicKeyAuthenticationTest.java
 
b/sshd-core/src/test/java/org/apache/sshd/common/auth/PublicKeyAuthenticationTest.java
index 2f23152..aa0c2cf 100644
--- 
a/sshd-core/src/test/java/org/apache/sshd/common/auth/PublicKeyAuthenticationTest.java
+++ 
b/sshd-core/src/test/java/org/apache/sshd/common/auth/PublicKeyAuthenticationTest.java
@@ -138,8 +138,54 @@ public class PublicKeyAuthenticationTest extends 
AuthenticationTestSupport {
     }
 
     @Test // see SSHD-624
-    public void testMismatchedUserAuthPkOkData() throws Exception {
-        AtomicInteger challengeCounter = new AtomicInteger(0);
+    public void testUserAuthPkOkWrongKey() throws Exception {
+        sshd.setUserAuthFactories(Collections.singletonList(
+                new 
org.apache.sshd.server.auth.pubkey.UserAuthPublicKeyFactory() {
+                    @Override
+                    public 
org.apache.sshd.server.auth.pubkey.UserAuthPublicKey 
createUserAuth(ServerSession session)
+                            throws IOException {
+                        return new 
org.apache.sshd.server.auth.pubkey.UserAuthPublicKey() {
+                            @Override
+                            protected void sendPublicKeyResponse(
+                                ServerSession session, String username, String 
alg, PublicKey key,
+                                byte[] keyBlob, int offset, int blobLen, 
Buffer buffer)
+                                throws Exception {
+                                // send another key
+                                KeyPair otherPair = 
org.apache.sshd.util.test.CommonTestSupportUtils
+                                        
.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024);
+                                PublicKey otherKey = otherPair.getPublic();
+                                Buffer buf = 
session.createBuffer(SshConstants.SSH_MSG_USERAUTH_PK_OK,
+                                        blobLen + alg.length() + Long.SIZE);
+                                buf.putString(alg);
+                                buf.putPublicKey(otherKey);
+                                session.writePacket(buf);
+                            }
+                        };
+                    }
+
+                }));
+
+        try (SshClient client = setupTestClient()) {
+            KeyPair clientIdentity = CommonTestSupportUtils.generateKeyPair(
+                    
CommonTestSupportUtils.DEFAULT_TEST_HOST_KEY_PROVIDER_ALGORITHM,
+                    CommonTestSupportUtils.DEFAULT_TEST_HOST_KEY_SIZE);
+            client.start();
+
+            try (ClientSession s = client.connect(getCurrentTestName(), 
TEST_LOCALHOST, port)
+                    .verify(CONNECT_TIMEOUT)
+                    .getSession()) {
+                s.addPublicKeyIdentity(clientIdentity);
+                SshException e = assertThrows(SshException.class, () -> 
s.auth().verify(AUTH_TIMEOUT));
+                Throwable t = e.getCause();
+                assertObjectInstanceOf("Unexpected failure cause", 
InvalidKeySpecException.class, t);
+            } finally {
+                client.stop();
+            }
+        }
+    }
+
+    @Test // see SSHD-1141
+    public void testUserAuthPkOkWrongAlgorithm() throws Exception {
         sshd.setUserAuthFactories(Collections.singletonList(
                 new 
org.apache.sshd.server.auth.pubkey.UserAuthPublicKeyFactory() {
                     @Override
@@ -151,25 +197,7 @@ public class PublicKeyAuthenticationTest extends 
AuthenticationTestSupport {
                                     ServerSession session, String username, 
String alg, PublicKey key,
                                     byte[] keyBlob, int offset, int blobLen, 
Buffer buffer)
                                     throws Exception {
-                                int count = challengeCounter.incrementAndGet();
-                                
outputDebugMessage("sendPublicKeyChallenge(%s)[%s]: count=%d", session, alg, 
count);
-                                if (count == 1) {
-                                    // send wrong key type
-                                    super.sendPublicKeyResponse(session, 
username,
-                                            KeyPairProvider.SSH_DSS, key, 
keyBlob, offset, blobLen, buffer);
-                                } else if (count == 2) {
-                                    // send another key
-                                    KeyPair otherPair = 
org.apache.sshd.util.test.CommonTestSupportUtils
-                                            
.generateKeyPair(KeyUtils.RSA_ALGORITHM, 1024);
-                                    PublicKey otherKey = otherPair.getPublic();
-                                    Buffer buf = 
session.createBuffer(SshConstants.SSH_MSG_USERAUTH_PK_OK,
-                                            blobLen + alg.length() + 
Long.SIZE);
-                                    buf.putString(alg);
-                                    buf.putPublicKey(otherKey);
-                                    session.writePacket(buf);
-                                } else {
-                                    super.sendPublicKeyResponse(session, 
username, alg, key, keyBlob, offset, blobLen, buffer);
-                                }
+                                super.sendPublicKeyResponse(session, username, 
KeyPairProvider.SSH_DSS, key, keyBlob, offset, blobLen, buffer);
                             }
                         };
                     }
@@ -182,22 +210,10 @@ public class PublicKeyAuthenticationTest extends 
AuthenticationTestSupport {
                     CommonTestSupportUtils.DEFAULT_TEST_HOST_KEY_SIZE);
             client.start();
 
-            try {
-                for (int index = 1; index <= 4; index++) {
-                    try (ClientSession s = 
client.connect(getCurrentTestName(), TEST_LOCALHOST, port)
-                            .verify(CONNECT_TIMEOUT)
-                            .getSession()) {
-                        s.addPublicKeyIdentity(clientIdentity);
-                        s.auth().verify(AUTH_TIMEOUT);
-                        assertEquals("Mismatched number of challenges", 3, 
challengeCounter.get());
-                        break;
-                    } catch (SshException e) { // expected
-                        outputDebugMessage("%s on retry #%d: %s", 
e.getClass().getSimpleName(), index, e.getMessage());
-
-                        Throwable t = e.getCause();
-                        assertObjectInstanceOf("Unexpected failure cause at 
retry #" + index, InvalidKeySpecException.class, t);
-                    }
-                }
+            try (ClientSession s = client.connect(getCurrentTestName(), 
TEST_LOCALHOST, port)
+                    .verify(CONNECT_TIMEOUT).getSession()) {
+                s.addPublicKeyIdentity(clientIdentity);
+                assertTrue("Successful authentication expected", 
s.auth().verify(AUTH_TIMEOUT).isSuccess());
             } finally {
                 client.stop();
             }

Reply via email to