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 <[email protected]>
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();
}