This is an automated email from the ASF dual-hosted git repository. lgoldstein pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/mina-sshd.git
commit 0de75cef20317326501f2ea54593b6e766eaa2f5 Author: Lyor Goldstein <lgoldst...@apache.org> AuthorDate: Fri Nov 20 09:47:47 2020 +0200 [SSHD-1104] Take into account possible key type aliases when using public key authentication --- CHANGES.md | 2 +- .../java/org/apache/sshd/common/NamedResource.java | 13 ++++++- .../common/signature/SignatureFactoriesHolder.java | 44 ++++++++++++++++++++++ .../signature/SignatureFactoriesManager.java | 16 +------- .../sshd/common/signature/SignatureFactory.java | 14 +++++++ .../sshd/client/auth/pubkey/KeyPairIdentity.java | 34 ++++++++++------- .../sshd/client/auth/pubkey/UserAuthPublicKey.java | 40 ++++++++++++++++---- 7 files changed, 126 insertions(+), 37 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 0c8196a..4375d17 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -61,4 +61,4 @@ or `-key-file` command line option. * [SSHD-1070](https://issues.apache.org/jira/browse/SSHD-1070) OutOfMemoryError when use async port forwarding * [SSHD-1100](https://issues.apache.org/jira/browse/SSHD-1100) Updated used moduli for DH group KEX * [SSHD-1102](https://issues.apache.org/jira/browse/SSHD-1102) Provide filter support for SftpDirectoryStream - +* [SSHD-1104](https://issues.apache.org/jira/browse/SSHD-1104) Take into account possible key type aliases when using public key authentication \ No newline at end of file diff --git a/sshd-common/src/main/java/org/apache/sshd/common/NamedResource.java b/sshd-common/src/main/java/org/apache/sshd/common/NamedResource.java index 256ecfd..b124579 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/NamedResource.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/NamedResource.java @@ -95,7 +95,7 @@ public interface NamedResource { */ static <R extends NamedResource> R findByName( String name, Comparator<? super String> c, Collection<? extends R> resources) { - return GenericUtils.isEmpty(name) + return (GenericUtils.isEmpty(name) || GenericUtils.isEmpty(resources)) ? null : GenericUtils.stream(resources) .filter(r -> c.compare(name, r.getName()) == 0) @@ -103,6 +103,17 @@ public interface NamedResource { .orElse(null); } + static <R extends NamedResource> R findFirstMatchByName( + Collection<String> names, Comparator<? super String> c, Collection<? extends R> resources) { + return (GenericUtils.isEmpty(names) || GenericUtils.isEmpty(resources)) + ? null + : GenericUtils.stream(resources) + .filter(r -> GenericUtils.findFirstMatchingMember(n -> c.compare(n, r.getName()) == 0, names) != null) + .findFirst() + .orElse(null); + + } + /** * Wraps a name value inside a {@link NamedResource} * diff --git a/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactoriesHolder.java b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactoriesHolder.java new file mode 100644 index 0000000..eeb4a5a --- /dev/null +++ b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactoriesHolder.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.sshd.common.signature; + +import java.util.List; + +import org.apache.sshd.common.NamedFactory; +import org.apache.sshd.common.NamedResource; + +/** + * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +@FunctionalInterface +public interface SignatureFactoriesHolder { + /** + * @return The list of named <code>Signature</code> factories + */ + List<NamedFactory<Signature>> getSignatureFactories(); + + default String getSignatureFactoriesNameList() { + return NamedResource.getNames(getSignatureFactories()); + } + + default List<String> getSignatureFactoriesNames() { + return NamedResource.getNameList(getSignatureFactories()); + } +} diff --git a/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactoriesManager.java b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactoriesManager.java index 6ef4352..86b8cbd 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactoriesManager.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactoriesManager.java @@ -25,7 +25,6 @@ import java.util.Collections; import java.util.List; import org.apache.sshd.common.NamedFactory; -import org.apache.sshd.common.NamedResource; import org.apache.sshd.common.util.GenericUtils; import org.apache.sshd.common.util.ValidateUtils; @@ -34,20 +33,7 @@ import org.apache.sshd.common.util.ValidateUtils; * * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> */ -public interface SignatureFactoriesManager { - /** - * @return The list of named <code>Signature</code> factories - */ - List<NamedFactory<Signature>> getSignatureFactories(); - - default String getSignatureFactoriesNameList() { - return NamedResource.getNames(getSignatureFactories()); - } - - default List<String> getSignatureFactoriesNames() { - return NamedResource.getNameList(getSignatureFactories()); - } - +public interface SignatureFactoriesManager extends SignatureFactoriesHolder { void setSignatureFactories(List<NamedFactory<Signature>> factories); default void setSignatureFactoriesNameList(String names) { diff --git a/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java index 8abfb79..e3dcf4f 100644 --- a/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java +++ b/sshd-common/src/main/java/org/apache/sshd/common/signature/SignatureFactory.java @@ -187,4 +187,18 @@ public interface SignatureFactory extends BuiltinFactory<Signature> { return posValue; } + + static NamedFactory<? extends Signature> resolveSignatureFactory( + String keyType, Collection<? extends NamedFactory<? extends Signature>> factories) { + if (GenericUtils.isEmpty(keyType) || GenericUtils.isEmpty(factories)) { + return null; + } + + Collection<String> aliases = KeyUtils.getAllEquivalentKeyTypes(keyType); + if (GenericUtils.isEmpty(aliases)) { + return NamedResource.findByName(keyType, String.CASE_INSENSITIVE_ORDER, factories); + } else { + return NamedResource.findFirstMatchByName(aliases, String.CASE_INSENSITIVE_ORDER, factories); + } + } } diff --git a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/KeyPairIdentity.java b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/KeyPairIdentity.java index be38e41..65812e1 100644 --- a/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/KeyPairIdentity.java +++ b/sshd-core/src/main/java/org/apache/sshd/client/auth/pubkey/KeyPairIdentity.java @@ -20,15 +20,17 @@ package org.apache.sshd.client.auth.pubkey; import java.security.KeyPair; import java.security.PublicKey; -import java.util.Collection; +import java.util.Collections; +import java.util.List; import java.util.Objects; import org.apache.sshd.common.NamedFactory; -import org.apache.sshd.common.NamedResource; import org.apache.sshd.common.config.keys.KeyUtils; import org.apache.sshd.common.session.SessionContext; import org.apache.sshd.common.signature.Signature; +import org.apache.sshd.common.signature.SignatureFactoriesHolder; import org.apache.sshd.common.signature.SignatureFactoriesManager; +import org.apache.sshd.common.signature.SignatureFactory; import org.apache.sshd.common.util.ValidateUtils; /** @@ -36,14 +38,15 @@ import org.apache.sshd.common.util.ValidateUtils; * * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> */ -public class KeyPairIdentity implements PublicKeyIdentity { - private final KeyPair pair; - private final Collection<NamedFactory<Signature>> signatureFactories; +public class KeyPairIdentity implements PublicKeyIdentity, SignatureFactoriesHolder { + protected final KeyPair pair; + private final List<NamedFactory<Signature>> signatureFactories; public KeyPairIdentity(SignatureFactoriesManager primary, SignatureFactoriesManager secondary, KeyPair pair) { - this.signatureFactories = ValidateUtils.checkNotNullAndNotEmpty( - SignatureFactoriesManager.resolveSignatureFactories(primary, secondary), - "No available signature factories"); + this.signatureFactories = Collections.unmodifiableList( + ValidateUtils.checkNotNullAndNotEmpty( + SignatureFactoriesManager.resolveSignatureFactories(primary, secondary), + "No available signature factories")); this.pair = Objects.requireNonNull(pair, "No key pair"); } @@ -53,12 +56,17 @@ public class KeyPairIdentity implements PublicKeyIdentity { } @Override + public List<NamedFactory<Signature>> getSignatureFactories() { + return signatureFactories; + } + + @Override public byte[] sign(SessionContext session, byte[] data) throws Exception { String keyType = KeyUtils.getKeyType(getPublicKey()); - Signature verifier = ValidateUtils.checkNotNull( - NamedFactory.create(signatureFactories, keyType), - "No signer could be located for key type=%s", - keyType); + // SSHD-1104 check if the key type is aliased + NamedFactory<? extends Signature> factory = SignatureFactory.resolveSignatureFactory(keyType, getSignatureFactories()); + Signature verifier = (factory == null) ? null : factory.create(); + ValidateUtils.checkNotNull(verifier, "No signer could be located for key type=%s", keyType); verifier.initSigner(session, pair.getPrivate()); verifier.update(session, data); return verifier.sign(session); @@ -69,7 +77,7 @@ public class KeyPairIdentity implements PublicKeyIdentity { PublicKey pubKey = getPublicKey(); return getClass().getSimpleName() + " type=" + KeyUtils.getKeyType(pubKey) - + ", factories=" + NamedResource.getNames(signatureFactories) + + ", factories=" + getSignatureFactoriesNameList() + ", fingerprint=" + KeyUtils.getFingerPrint(pubKey); } } 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 26d7b65..01143e6 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 @@ -22,6 +22,7 @@ import java.io.Closeable; import java.io.IOException; import java.security.PublicKey; import java.security.spec.InvalidKeySpecException; +import java.util.Collection; import java.util.Iterator; import java.util.List; @@ -32,7 +33,9 @@ import org.apache.sshd.common.RuntimeSshException; import org.apache.sshd.common.SshConstants; import org.apache.sshd.common.config.keys.KeyUtils; import org.apache.sshd.common.signature.Signature; +import org.apache.sshd.common.signature.SignatureFactoriesHolder; import org.apache.sshd.common.signature.SignatureFactoriesManager; +import org.apache.sshd.common.signature.SignatureFactory; import org.apache.sshd.common.util.GenericUtils; import org.apache.sshd.common.util.buffer.Buffer; import org.apache.sshd.common.util.buffer.BufferUtils; @@ -40,7 +43,7 @@ import org.apache.sshd.common.util.buffer.ByteArrayBuffer; /** * Implements the "publickey" authentication mechanism - * + * * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> */ public class UserAuthPublicKey extends AbstractUserAuth implements SignatureFactoriesManager { @@ -115,7 +118,17 @@ public class UserAuthPublicKey extends AbstractUserAuth implements SignatureFact throw new RuntimeSshException(e); } - String algo = KeyUtils.getKeyType(key); + String keyType = KeyUtils.getKeyType(key); + NamedFactory<? extends Signature> factory; + // SSHD-1104 check if the key type is aliased + if (current instanceof SignatureFactoriesHolder) { + factory = SignatureFactory.resolveSignatureFactory( + keyType, ((SignatureFactoriesHolder) current).getSignatureFactories()); + } else { + factory = SignatureFactory.resolveSignatureFactory(keyType, getSignatureFactories()); + } + + String algo = (factory == null) ? keyType : factory.getName(); String name = getName(); if (debugEnabled) { log.debug("sendAuthDataRequest({})[{}] send SSH_MSG_USERAUTH_REQUEST request {} type={} - fingerprint={}", @@ -156,12 +169,25 @@ public class UserAuthPublicKey extends AbstractUserAuth implements SignatureFact throw new RuntimeSshException(e); } - String algo = KeyUtils.getKeyType(key); + String curKeyType = KeyUtils.getKeyType(key); String rspKeyType = buffer.getString(); - if (!rspKeyType.equals(algo)) { - throw new InvalidKeySpecException( - "processAuthDataRequest(" + session + ")[" + service + "][" + name + "]" - + " mismatched key types: expected=" + algo + ", actual=" + rspKeyType); + 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; } PublicKey rspKey = buffer.getPublicKey();