From a747db88edb60516c29a000876e06a7ec55f42a6 Mon Sep 17 00:00:00 2001 From: David Kocher Date: Tue, 16 Jun 2015 15:42:01 +0200 Subject: [PATCH] Add option for client to verify negotiated key exchange algorithms. --- src/main/java/net/schmizz/sshj/SSHClient.java | 16 ++++++++--- .../schmizz/sshj/transport/KeyExchanger.java | 15 +++++++++++ .../net/schmizz/sshj/transport/Transport.java | 8 ++++++ .../schmizz/sshj/transport/TransportImpl.java | 6 +++++ .../verification/AlgorithmsVerifier.java | 27 +++++++++++++++++++ 5 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 src/main/java/net/schmizz/sshj/transport/verification/AlgorithmsVerifier.java diff --git a/src/main/java/net/schmizz/sshj/SSHClient.java b/src/main/java/net/schmizz/sshj/SSHClient.java index a59eaa431..d5667d54b 100644 --- a/src/main/java/net/schmizz/sshj/SSHClient.java +++ b/src/main/java/net/schmizz/sshj/SSHClient.java @@ -39,6 +39,7 @@ import net.schmizz.sshj.transport.compression.DelayedZlibCompression; import net.schmizz.sshj.transport.compression.NoneCompression; import net.schmizz.sshj.transport.compression.ZlibCompression; +import net.schmizz.sshj.transport.verification.AlgorithmsVerifier; import net.schmizz.sshj.transport.verification.HostKeyVerifier; import net.schmizz.sshj.transport.verification.OpenSSHKnownHosts; import net.schmizz.sshj.userauth.UserAuth; @@ -158,10 +159,19 @@ public SSHClient(Config config) { * Add a {@link HostKeyVerifier} which will be invoked for verifying host key during connection establishment and * future key exchanges. * - * @param hostKeyVerifier {@link HostKeyVerifier} instance + * @param verifier {@link HostKeyVerifier} instance */ - public void addHostKeyVerifier(HostKeyVerifier hostKeyVerifier) { - trans.addHostKeyVerifier(hostKeyVerifier); + public void addHostKeyVerifier(HostKeyVerifier verifier) { + trans.addHostKeyVerifier(verifier); + } + + /** + * Add a {@link AlgorithmsVerifier} which will be invoked for verifying negotiated algorithms. + * + * @param verifier {@link AlgorithmsVerifier} instance + */ + public void addAlgorithmsVerifier(AlgorithmsVerifier verifier) { + trans.addAlgorithmsVerifier(verifier); } /** diff --git a/src/main/java/net/schmizz/sshj/transport/KeyExchanger.java b/src/main/java/net/schmizz/sshj/transport/KeyExchanger.java index 9f92d14e9..781805e7b 100644 --- a/src/main/java/net/schmizz/sshj/transport/KeyExchanger.java +++ b/src/main/java/net/schmizz/sshj/transport/KeyExchanger.java @@ -33,6 +33,8 @@ import net.schmizz.sshj.transport.kex.KeyExchange; import net.schmizz.sshj.transport.mac.MAC; import net.schmizz.sshj.transport.verification.HostKeyVerifier; +import net.schmizz.sshj.transport.verification.AlgorithmsVerifier; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -68,6 +70,8 @@ private static enum Expected { */ private final Queue hostVerifiers = new LinkedList(); + private final Queue algorithmVerifiers = new LinkedList(); + private final AtomicBoolean kexOngoing = new AtomicBoolean(); /** What we are expecting from the next packet */ @@ -108,6 +112,10 @@ synchronized void addHostKeyVerifier(HostKeyVerifier hkv) { hostVerifiers.add(hkv); } + synchronized void addAlgorithmsVerifier(AlgorithmsVerifier verifier) { + algorithmVerifiers.add(verifier); + } + /** * Returns the session identifier computed during key exchange. * @@ -218,6 +226,13 @@ private void gotKexInit(SSHPacket buf) final Proposal serverProposal = new Proposal(buf); negotiatedAlgs = clientProposal.negotiate(serverProposal); log.debug("Negotiated algorithms: {}", negotiatedAlgs); + for(AlgorithmsVerifier v: algorithmVerifiers) { + log.debug("Trying to verify algorithms with {}", v); + if(!v.verify(negotiatedAlgs)) { + throw new TransportException(DisconnectReason.KEY_EXCHANGE_FAILED, + "Failed to verify negotiated algorithms `" + negotiatedAlgs + "`"); + } + } kex = Factory.Named.Util.create(transport.getConfig().getKeyExchangeFactories(), negotiatedAlgs.getKeyExchangeAlgorithm()); try { diff --git a/src/main/java/net/schmizz/sshj/transport/Transport.java b/src/main/java/net/schmizz/sshj/transport/Transport.java index 003941660..6992a52ae 100644 --- a/src/main/java/net/schmizz/sshj/transport/Transport.java +++ b/src/main/java/net/schmizz/sshj/transport/Transport.java @@ -20,6 +20,7 @@ import net.schmizz.sshj.common.DisconnectReason; import net.schmizz.sshj.common.SSHPacket; import net.schmizz.sshj.common.SSHPacketHandler; +import net.schmizz.sshj.transport.verification.AlgorithmsVerifier; import net.schmizz.sshj.transport.verification.HostKeyVerifier; import java.io.InputStream; @@ -51,6 +52,13 @@ void init(String host, int port, InputStream in, OutputStream out) */ void addHostKeyVerifier(HostKeyVerifier hkv); + /** + * Adds the specified verifier. + * + * @param verifier The verifier to call with negotiated algorithms + */ + void addAlgorithmsVerifier(AlgorithmsVerifier verifier); + /** * Do key exchange and algorithm negotiation. This can be the initial one or for algorithm renegotiation. * diff --git a/src/main/java/net/schmizz/sshj/transport/TransportImpl.java b/src/main/java/net/schmizz/sshj/transport/TransportImpl.java index 1e3b13bcc..80b2b01fb 100644 --- a/src/main/java/net/schmizz/sshj/transport/TransportImpl.java +++ b/src/main/java/net/schmizz/sshj/transport/TransportImpl.java @@ -27,6 +27,7 @@ import net.schmizz.sshj.common.Message; import net.schmizz.sshj.common.SSHException; import net.schmizz.sshj.common.SSHPacket; +import net.schmizz.sshj.transport.verification.AlgorithmsVerifier; import net.schmizz.sshj.transport.verification.HostKeyVerifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -235,6 +236,11 @@ public void addHostKeyVerifier(HostKeyVerifier hkv) { kexer.addHostKeyVerifier(hkv); } + @Override + public void addAlgorithmsVerifier(AlgorithmsVerifier verifier) { + kexer.addAlgorithmsVerifier(verifier); + } + @Override public void doKex() throws TransportException { diff --git a/src/main/java/net/schmizz/sshj/transport/verification/AlgorithmsVerifier.java b/src/main/java/net/schmizz/sshj/transport/verification/AlgorithmsVerifier.java new file mode 100644 index 000000000..7ea1f510e --- /dev/null +++ b/src/main/java/net/schmizz/sshj/transport/verification/AlgorithmsVerifier.java @@ -0,0 +1,27 @@ +/** + * Copyright 2009 sshj contributors + * + * Licensed 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 net.schmizz.sshj.transport.verification; + +import net.schmizz.sshj.transport.NegotiatedAlgorithms; + +public interface AlgorithmsVerifier { + + /** + * Callback is invoked when algorithms have been negotiated between client and server. + * @return False to interrupt the connection + */ + boolean verify(NegotiatedAlgorithms algorithms); +}