diff options
author | Kenny Root <kenny@the-b.org> | 2014-10-01 23:04:51 +0100 |
---|---|---|
committer | Kenny Root <kenny@the-b.org> | 2014-10-01 12:48:19 +0100 |
commit | 49b779dcaf03e3598d2709b321e20ea029b25163 (patch) | |
tree | 05af547b1f1433d7dd6f7373d0b25a455e053a03 /src/com/trilead/ssh2/transport | |
parent | d64786d9197090c74072b648e487e3d34817bb57 (diff) | |
download | connectbot-49b779dcaf03e3598d2709b321e20ea029b25163.tar.gz connectbot-49b779dcaf03e3598d2709b321e20ea029b25163.tar.bz2 connectbot-49b779dcaf03e3598d2709b321e20ea029b25163.zip |
Convert to gradle build system
Diffstat (limited to 'src/com/trilead/ssh2/transport')
-rw-r--r-- | src/com/trilead/ssh2/transport/ClientServerHello.java | 125 | ||||
-rw-r--r-- | src/com/trilead/ssh2/transport/KexManager.java | 671 | ||||
-rw-r--r-- | src/com/trilead/ssh2/transport/KexParameters.java | 24 | ||||
-rw-r--r-- | src/com/trilead/ssh2/transport/KexState.java | 33 | ||||
-rw-r--r-- | src/com/trilead/ssh2/transport/MessageHandler.java | 14 | ||||
-rw-r--r-- | src/com/trilead/ssh2/transport/NegotiateException.java | 12 | ||||
-rw-r--r-- | src/com/trilead/ssh2/transport/NegotiatedParameters.java | 22 | ||||
-rw-r--r-- | src/com/trilead/ssh2/transport/TransportConnection.java | 343 | ||||
-rw-r--r-- | src/com/trilead/ssh2/transport/TransportManager.java | 802 |
9 files changed, 0 insertions, 2046 deletions
diff --git a/src/com/trilead/ssh2/transport/ClientServerHello.java b/src/com/trilead/ssh2/transport/ClientServerHello.java deleted file mode 100644 index d7a5ee5..0000000 --- a/src/com/trilead/ssh2/transport/ClientServerHello.java +++ /dev/null @@ -1,125 +0,0 @@ - -package com.trilead.ssh2.transport; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.UnsupportedEncodingException; - -import com.trilead.ssh2.Connection; - -/** - * ClientServerHello. - * - * @author Christian Plattner, plattner@trilead.com - * @version $Id: ClientServerHello.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $ - */ -public class ClientServerHello -{ - String server_line; - String client_line; - - String server_versioncomment; - - public final static int readLineRN(InputStream is, byte[] buffer) throws IOException - { - int pos = 0; - boolean need10 = false; - int len = 0; - while (true) - { - int c = is.read(); - if (c == -1) - throw new IOException("Premature connection close"); - - buffer[pos++] = (byte) c; - - if (c == 13) - { - need10 = true; - continue; - } - - if (c == 10) - break; - - if (need10 == true) - throw new IOException("Malformed line sent by the server, the line does not end correctly."); - - len++; - if (pos >= buffer.length) - throw new IOException("The server sent a too long line."); - } - - return len; - } - - public ClientServerHello(InputStream bi, OutputStream bo) throws IOException - { - client_line = "SSH-2.0-" + Connection.identification; - - bo.write((client_line + "\r\n").getBytes("ISO-8859-1")); - bo.flush(); - - byte[] serverVersion = new byte[512]; - - for (int i = 0; i < 50; i++) - { - int len = readLineRN(bi, serverVersion); - - server_line = new String(serverVersion, 0, len, "ISO-8859-1"); - - if (server_line.startsWith("SSH-")) - break; - } - - if (server_line.startsWith("SSH-") == false) - throw new IOException( - "Malformed server identification string. There was no line starting with 'SSH-' amongst the first 50 lines."); - - if (server_line.startsWith("SSH-1.99-")) - server_versioncomment = server_line.substring(9); - else if (server_line.startsWith("SSH-2.0-")) - server_versioncomment = server_line.substring(8); - else - throw new IOException("Server uses incompatible protocol, it is not SSH-2 compatible."); - } - - /** - * @return Returns the client_versioncomment. - */ - public byte[] getClientString() - { - byte[] result; - - try - { - result = client_line.getBytes("ISO-8859-1"); - } - catch (UnsupportedEncodingException ign) - { - result = client_line.getBytes(); - } - - return result; - } - - /** - * @return Returns the server_versioncomment. - */ - public byte[] getServerString() - { - byte[] result; - - try - { - result = server_line.getBytes("ISO-8859-1"); - } - catch (UnsupportedEncodingException ign) - { - result = server_line.getBytes(); - } - - return result; - } -} diff --git a/src/com/trilead/ssh2/transport/KexManager.java b/src/com/trilead/ssh2/transport/KexManager.java deleted file mode 100644 index cd26530..0000000 --- a/src/com/trilead/ssh2/transport/KexManager.java +++ /dev/null @@ -1,671 +0,0 @@ - -package com.trilead.ssh2.transport; - -import java.io.IOException; -import java.security.SecureRandom; -import java.security.interfaces.DSAPublicKey; -import java.security.interfaces.ECPublicKey; -import java.security.interfaces.RSAPublicKey; -import java.util.Set; -import java.util.TreeSet; - -import com.trilead.ssh2.ConnectionInfo; -import com.trilead.ssh2.DHGexParameters; -import com.trilead.ssh2.ServerHostKeyVerifier; -import com.trilead.ssh2.compression.CompressionFactory; -import com.trilead.ssh2.compression.ICompressor; -import com.trilead.ssh2.crypto.CryptoWishList; -import com.trilead.ssh2.crypto.KeyMaterial; -import com.trilead.ssh2.crypto.cipher.BlockCipher; -import com.trilead.ssh2.crypto.cipher.BlockCipherFactory; -import com.trilead.ssh2.crypto.dh.DhGroupExchange; -import com.trilead.ssh2.crypto.dh.GenericDhExchange; -import com.trilead.ssh2.crypto.digest.MAC; -import com.trilead.ssh2.log.Logger; -import com.trilead.ssh2.packets.PacketKexDHInit; -import com.trilead.ssh2.packets.PacketKexDHReply; -import com.trilead.ssh2.packets.PacketKexDhGexGroup; -import com.trilead.ssh2.packets.PacketKexDhGexInit; -import com.trilead.ssh2.packets.PacketKexDhGexReply; -import com.trilead.ssh2.packets.PacketKexDhGexRequest; -import com.trilead.ssh2.packets.PacketKexDhGexRequestOld; -import com.trilead.ssh2.packets.PacketKexInit; -import com.trilead.ssh2.packets.PacketNewKeys; -import com.trilead.ssh2.packets.Packets; -import com.trilead.ssh2.signature.DSASHA1Verify; -import com.trilead.ssh2.signature.ECDSASHA2Verify; -import com.trilead.ssh2.signature.RSASHA1Verify; - - -/** - * KexManager. - * - * @author Christian Plattner, plattner@trilead.com - * @version $Id: KexManager.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ - */ -public class KexManager -{ - private static final Logger log = Logger.getLogger(KexManager.class); - - private static final Set<String> HOSTKEY_ALGS = new TreeSet<String>(); - static { - HOSTKEY_ALGS.add("ecdsa-sha2-nistp256"); - HOSTKEY_ALGS.add("ecdsa-sha2-nistp384"); - HOSTKEY_ALGS.add("ecdsa-sha2-nistp521"); - HOSTKEY_ALGS.add("ssh-rsa"); - HOSTKEY_ALGS.add("ssh-dsa"); - } - - private static final Set<String> KEX_ALGS = new TreeSet<String>(); - static { - KEX_ALGS.add("ecdh-sha2-nistp256"); - KEX_ALGS.add("ecdh-sha2-nistp384"); - KEX_ALGS.add("ecdh-sha2-nistp521"); - KEX_ALGS.add("diffie-hellman-group-exchange-sha256"); - KEX_ALGS.add("diffie-hellman-group-exchange-sha1"); - KEX_ALGS.add("diffie-hellman-group14-sha1"); - KEX_ALGS.add("diffie-hellman-group1-sha1"); - } - - KexState kxs; - int kexCount = 0; - KeyMaterial km; - byte[] sessionId; - ClientServerHello csh; - - final Object accessLock = new Object(); - ConnectionInfo lastConnInfo = null; - - boolean connectionClosed = false; - - boolean ignore_next_kex_packet = false; - - final TransportManager tm; - - CryptoWishList nextKEXcryptoWishList; - DHGexParameters nextKEXdhgexParameters; - - ServerHostKeyVerifier verifier; - final String hostname; - final int port; - final SecureRandom rnd; - - public KexManager(TransportManager tm, ClientServerHello csh, CryptoWishList initialCwl, String hostname, int port, - ServerHostKeyVerifier keyVerifier, SecureRandom rnd) - { - this.tm = tm; - this.csh = csh; - this.nextKEXcryptoWishList = initialCwl; - this.nextKEXdhgexParameters = new DHGexParameters(); - this.hostname = hostname; - this.port = port; - this.verifier = keyVerifier; - this.rnd = rnd; - } - - public ConnectionInfo getOrWaitForConnectionInfo(int minKexCount) throws IOException - { - synchronized (accessLock) - { - while (true) - { - if ((lastConnInfo != null) && (lastConnInfo.keyExchangeCounter >= minKexCount)) - return lastConnInfo; - - if (connectionClosed) - throw (IOException) new IOException("Key exchange was not finished, connection is closed.") - .initCause(tm.getReasonClosedCause()); - - try - { - accessLock.wait(); - } - catch (InterruptedException e) - { - } - } - } - } - - private String getFirstMatch(String[] client, String[] server) throws NegotiateException - { - if (client == null || server == null) - throw new IllegalArgumentException(); - - if (client.length == 0) - return null; - - for (int i = 0; i < client.length; i++) - { - for (int j = 0; j < server.length; j++) - { - if (client[i].equals(server[j])) - return client[i]; - } - } - throw new NegotiateException(); - } - - private boolean compareFirstOfNameList(String[] a, String[] b) - { - if (a == null || b == null) - throw new IllegalArgumentException(); - - if ((a.length == 0) && (b.length == 0)) - return true; - - if ((a.length == 0) || (b.length == 0)) - return false; - - return (a[0].equals(b[0])); - } - - private boolean isGuessOK(KexParameters cpar, KexParameters spar) - { - if (cpar == null || spar == null) - throw new IllegalArgumentException(); - - if (compareFirstOfNameList(cpar.kex_algorithms, spar.kex_algorithms) == false) - { - return false; - } - - if (compareFirstOfNameList(cpar.server_host_key_algorithms, spar.server_host_key_algorithms) == false) - { - return false; - } - - /* - * We do NOT check here if the other algorithms can be agreed on, this - * is just a check if kex_algorithms and server_host_key_algorithms were - * guessed right! - */ - - return true; - } - - private NegotiatedParameters mergeKexParameters(KexParameters client, KexParameters server) - { - NegotiatedParameters np = new NegotiatedParameters(); - - try - { - np.kex_algo = getFirstMatch(client.kex_algorithms, server.kex_algorithms); - - log.log(20, "kex_algo=" + np.kex_algo); - - np.server_host_key_algo = getFirstMatch(client.server_host_key_algorithms, - server.server_host_key_algorithms); - - log.log(20, "server_host_key_algo=" + np.server_host_key_algo); - - np.enc_algo_client_to_server = getFirstMatch(client.encryption_algorithms_client_to_server, - server.encryption_algorithms_client_to_server); - np.enc_algo_server_to_client = getFirstMatch(client.encryption_algorithms_server_to_client, - server.encryption_algorithms_server_to_client); - - log.log(20, "enc_algo_client_to_server=" + np.enc_algo_client_to_server); - log.log(20, "enc_algo_server_to_client=" + np.enc_algo_server_to_client); - - np.mac_algo_client_to_server = getFirstMatch(client.mac_algorithms_client_to_server, - server.mac_algorithms_client_to_server); - np.mac_algo_server_to_client = getFirstMatch(client.mac_algorithms_server_to_client, - server.mac_algorithms_server_to_client); - - log.log(20, "mac_algo_client_to_server=" + np.mac_algo_client_to_server); - log.log(20, "mac_algo_server_to_client=" + np.mac_algo_server_to_client); - - np.comp_algo_client_to_server = getFirstMatch(client.compression_algorithms_client_to_server, - server.compression_algorithms_client_to_server); - np.comp_algo_server_to_client = getFirstMatch(client.compression_algorithms_server_to_client, - server.compression_algorithms_server_to_client); - - log.log(20, "comp_algo_client_to_server=" + np.comp_algo_client_to_server); - log.log(20, "comp_algo_server_to_client=" + np.comp_algo_server_to_client); - - } - catch (NegotiateException e) - { - return null; - } - - try - { - np.lang_client_to_server = getFirstMatch(client.languages_client_to_server, - server.languages_client_to_server); - } - catch (NegotiateException e1) - { - np.lang_client_to_server = null; - } - - try - { - np.lang_server_to_client = getFirstMatch(client.languages_server_to_client, - server.languages_server_to_client); - } - catch (NegotiateException e2) - { - np.lang_server_to_client = null; - } - - if (isGuessOK(client, server)) - np.guessOK = true; - - return np; - } - - public synchronized void initiateKEX(CryptoWishList cwl, DHGexParameters dhgex) throws IOException - { - nextKEXcryptoWishList = cwl; - nextKEXdhgexParameters = dhgex; - - if (kxs == null) - { - kxs = new KexState(); - - kxs.dhgexParameters = nextKEXdhgexParameters; - PacketKexInit kp = new PacketKexInit(nextKEXcryptoWishList); - kxs.localKEX = kp; - tm.sendKexMessage(kp.getPayload()); - } - } - - private boolean establishKeyMaterial() - { - try - { - int mac_cs_key_len = MAC.getKeyLen(kxs.np.mac_algo_client_to_server); - int enc_cs_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_client_to_server); - int enc_cs_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_client_to_server); - - int mac_sc_key_len = MAC.getKeyLen(kxs.np.mac_algo_server_to_client); - int enc_sc_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_server_to_client); - int enc_sc_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_server_to_client); - - km = KeyMaterial.create(kxs.hashAlgo, kxs.H, kxs.K, sessionId, enc_cs_key_len, enc_cs_block_len, mac_cs_key_len, - enc_sc_key_len, enc_sc_block_len, mac_sc_key_len); - } - catch (IllegalArgumentException e) - { - return false; - } - return true; - } - - private void finishKex() throws IOException - { - if (sessionId == null) - sessionId = kxs.H; - - establishKeyMaterial(); - - /* Tell the other side that we start using the new material */ - - PacketNewKeys ign = new PacketNewKeys(); - tm.sendKexMessage(ign.getPayload()); - - BlockCipher cbc; - MAC mac; - ICompressor comp; - - try - { - cbc = BlockCipherFactory.createCipher(kxs.np.enc_algo_client_to_server, true, km.enc_key_client_to_server, - km.initial_iv_client_to_server); - - mac = new MAC(kxs.np.mac_algo_client_to_server, km.integrity_key_client_to_server); - - comp = CompressionFactory.createCompressor(kxs.np.comp_algo_client_to_server); - - } - catch (IllegalArgumentException e1) - { - throw new IOException("Fatal error during MAC startup!"); - } - - tm.changeSendCipher(cbc, mac); - tm.changeSendCompression(comp); - tm.kexFinished(); - } - - public static final String[] getDefaultServerHostkeyAlgorithmList() - { - return HOSTKEY_ALGS.toArray(new String[HOSTKEY_ALGS.size()]); - } - - public static final void checkServerHostkeyAlgorithmsList(String[] algos) - { - for (int i = 0; i < algos.length; i++) - { - if (!HOSTKEY_ALGS.contains(algos[i])) - throw new IllegalArgumentException("Unknown server host key algorithm '" + algos[i] + "'"); - } - } - - public static final String[] getDefaultKexAlgorithmList() - { - return KEX_ALGS.toArray(new String[KEX_ALGS.size()]); - } - - public static final void checkKexAlgorithmList(String[] algos) - { - for (int i = 0; i < algos.length; i++) - { - if (!KEX_ALGS.contains(algos[i])) - throw new IllegalArgumentException("Unknown kex algorithm '" + algos[i] + "'"); - } - } - - private boolean verifySignature(byte[] sig, byte[] hostkey) throws IOException - { - if (kxs.np.server_host_key_algo.startsWith("ecdsa-sha2-")) - { - byte[] rs = ECDSASHA2Verify.decodeSSHECDSASignature(sig); - ECPublicKey epk = ECDSASHA2Verify.decodeSSHECDSAPublicKey(hostkey); - - log.log(50, "Verifying ecdsa signature"); - - return ECDSASHA2Verify.verifySignature(kxs.H, rs, epk); - } - - if (kxs.np.server_host_key_algo.equals("ssh-rsa")) - { - byte[] rs = RSASHA1Verify.decodeSSHRSASignature(sig); - RSAPublicKey rpk = RSASHA1Verify.decodeSSHRSAPublicKey(hostkey); - - log.log(50, "Verifying ssh-rsa signature"); - - return RSASHA1Verify.verifySignature(kxs.H, rs, rpk); - } - - if (kxs.np.server_host_key_algo.equals("ssh-dss")) - { - byte[] ds = DSASHA1Verify.decodeSSHDSASignature(sig); - DSAPublicKey dpk = DSASHA1Verify.decodeSSHDSAPublicKey(hostkey); - - log.log(50, "Verifying ssh-dss signature"); - - return DSASHA1Verify.verifySignature(kxs.H, ds, dpk); - } - - throw new IOException("Unknown server host key algorithm '" + kxs.np.server_host_key_algo + "'"); - } - - public synchronized void handleMessage(byte[] msg, int msglen) throws IOException - { - PacketKexInit kip; - - if (msg == null) - { - synchronized (accessLock) - { - connectionClosed = true; - accessLock.notifyAll(); - return; - } - } - - if ((kxs == null) && (msg[0] != Packets.SSH_MSG_KEXINIT)) - throw new IOException("Unexpected KEX message (type " + msg[0] + ")"); - - if (ignore_next_kex_packet) - { - ignore_next_kex_packet = false; - return; - } - - if (msg[0] == Packets.SSH_MSG_KEXINIT) - { - if ((kxs != null) && (kxs.state != 0)) - throw new IOException("Unexpected SSH_MSG_KEXINIT message during on-going kex exchange!"); - - if (kxs == null) - { - /* - * Ah, OK, peer wants to do KEX. Let's be nice and play - * together. - */ - kxs = new KexState(); - kxs.dhgexParameters = nextKEXdhgexParameters; - kip = new PacketKexInit(nextKEXcryptoWishList); - kxs.localKEX = kip; - tm.sendKexMessage(kip.getPayload()); - } - - kip = new PacketKexInit(msg, 0, msglen); - kxs.remoteKEX = kip; - - kxs.np = mergeKexParameters(kxs.localKEX.getKexParameters(), kxs.remoteKEX.getKexParameters()); - - if (kxs.np == null) - throw new IOException("Cannot negotiate, proposals do not match."); - - if (kxs.remoteKEX.isFirst_kex_packet_follows() && (kxs.np.guessOK == false)) - { - /* - * Guess was wrong, we need to ignore the next kex packet. - */ - - ignore_next_kex_packet = true; - } - - if (kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha1") - || kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha256")) - { - if (kxs.dhgexParameters.getMin_group_len() == 0 || csh.server_versioncomment.matches("OpenSSH_2\\.([0-4]\\.|5\\.[0-2]).*")) - { - PacketKexDhGexRequestOld dhgexreq = new PacketKexDhGexRequestOld(kxs.dhgexParameters); - tm.sendKexMessage(dhgexreq.getPayload()); - } - else - { - PacketKexDhGexRequest dhgexreq = new PacketKexDhGexRequest(kxs.dhgexParameters); - tm.sendKexMessage(dhgexreq.getPayload()); - } - if (kxs.np.kex_algo.endsWith("sha1")) { - kxs.hashAlgo = "SHA1"; - } else { - kxs.hashAlgo = "SHA-256"; - } - kxs.state = 1; - return; - } - - if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1") - || kxs.np.kex_algo.equals("diffie-hellman-group14-sha1") - || kxs.np.kex_algo.equals("ecdh-sha2-nistp256") - || kxs.np.kex_algo.equals("ecdh-sha2-nistp384") - || kxs.np.kex_algo.equals("ecdh-sha2-nistp521")) { - kxs.dhx = GenericDhExchange.getInstance(kxs.np.kex_algo); - - kxs.dhx.init(kxs.np.kex_algo); - kxs.hashAlgo = kxs.dhx.getHashAlgo(); - - PacketKexDHInit kp = new PacketKexDHInit(kxs.dhx.getE()); - tm.sendKexMessage(kp.getPayload()); - kxs.state = 1; - return; - } - - throw new IllegalStateException("Unknown KEX method!"); - } - - if (msg[0] == Packets.SSH_MSG_NEWKEYS) - { - if (km == null) - throw new IOException("Peer sent SSH_MSG_NEWKEYS, but I have no key material ready!"); - - BlockCipher cbc; - MAC mac; - ICompressor comp; - - try - { - cbc = BlockCipherFactory.createCipher(kxs.np.enc_algo_server_to_client, false, - km.enc_key_server_to_client, km.initial_iv_server_to_client); - - mac = new MAC(kxs.np.mac_algo_server_to_client, km.integrity_key_server_to_client); - - comp = CompressionFactory.createCompressor(kxs.np.comp_algo_server_to_client); - } - catch (IllegalArgumentException e1) - { - throw new IOException("Fatal error during MAC startup!"); - } - - tm.changeRecvCipher(cbc, mac); - tm.changeRecvCompression(comp); - - ConnectionInfo sci = new ConnectionInfo(); - - kexCount++; - - sci.keyExchangeAlgorithm = kxs.np.kex_algo; - sci.keyExchangeCounter = kexCount; - sci.clientToServerCryptoAlgorithm = kxs.np.enc_algo_client_to_server; - sci.serverToClientCryptoAlgorithm = kxs.np.enc_algo_server_to_client; - sci.clientToServerMACAlgorithm = kxs.np.mac_algo_client_to_server; - sci.serverToClientMACAlgorithm = kxs.np.mac_algo_server_to_client; - sci.serverHostKeyAlgorithm = kxs.np.server_host_key_algo; - sci.serverHostKey = kxs.hostkey; - - synchronized (accessLock) - { - lastConnInfo = sci; - accessLock.notifyAll(); - } - - kxs = null; - return; - } - - if ((kxs == null) || (kxs.state == 0)) - throw new IOException("Unexpected Kex submessage!"); - - if (kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha1") - || kxs.np.kex_algo.equals("diffie-hellman-group-exchange-sha256")) - { - if (kxs.state == 1) - { - PacketKexDhGexGroup dhgexgrp = new PacketKexDhGexGroup(msg, 0, msglen); - kxs.dhgx = new DhGroupExchange(dhgexgrp.getP(), dhgexgrp.getG()); - kxs.dhgx.init(rnd); - PacketKexDhGexInit dhgexinit = new PacketKexDhGexInit(kxs.dhgx.getE()); - tm.sendKexMessage(dhgexinit.getPayload()); - kxs.state = 2; - return; - } - - if (kxs.state == 2) - { - PacketKexDhGexReply dhgexrpl = new PacketKexDhGexReply(msg, 0, msglen); - - kxs.hostkey = dhgexrpl.getHostKey(); - - if (verifier != null) - { - boolean vres = false; - - try - { - vres = verifier.verifyServerHostKey(hostname, port, kxs.np.server_host_key_algo, kxs.hostkey); - } - catch (Exception e) - { - throw (IOException) new IOException( - "The server hostkey was not accepted by the verifier callback.").initCause(e); - } - - if (vres == false) - throw new IOException("The server hostkey was not accepted by the verifier callback"); - } - - kxs.dhgx.setF(dhgexrpl.getF()); - - try - { - kxs.H = kxs.dhgx.calculateH(kxs.hashAlgo, - csh.getClientString(), csh.getServerString(), - kxs.localKEX.getPayload(), kxs.remoteKEX.getPayload(), - dhgexrpl.getHostKey(), kxs.dhgexParameters); - } - catch (IllegalArgumentException e) - { - throw (IOException) new IOException("KEX error.").initCause(e); - } - - boolean res = verifySignature(dhgexrpl.getSignature(), kxs.hostkey); - - if (res == false) - throw new IOException("Hostkey signature sent by remote is wrong!"); - - kxs.K = kxs.dhgx.getK(); - - finishKex(); - kxs.state = -1; - return; - } - - throw new IllegalStateException("Illegal State in KEX Exchange!"); - } - - if (kxs.np.kex_algo.equals("diffie-hellman-group1-sha1") - || kxs.np.kex_algo.equals("diffie-hellman-group14-sha1") - || kxs.np.kex_algo.equals("ecdh-sha2-nistp256") - || kxs.np.kex_algo.equals("ecdh-sha2-nistp384") - || kxs.np.kex_algo.equals("ecdh-sha2-nistp521")) - { - if (kxs.state == 1) - { - - PacketKexDHReply dhr = new PacketKexDHReply(msg, 0, msglen); - - kxs.hostkey = dhr.getHostKey(); - - if (verifier != null) - { - boolean vres = false; - - try - { - vres = verifier.verifyServerHostKey(hostname, port, kxs.np.server_host_key_algo, kxs.hostkey); - } - catch (Exception e) - { - throw (IOException) new IOException( - "The server hostkey was not accepted by the verifier callback.").initCause(e); - } - - if (vres == false) - throw new IOException("The server hostkey was not accepted by the verifier callback"); - } - - kxs.dhx.setF(dhr.getF()); - - try - { - kxs.H = kxs.dhx.calculateH(csh.getClientString(), csh.getServerString(), kxs.localKEX.getPayload(), - kxs.remoteKEX.getPayload(), dhr.getHostKey()); - } - catch (IllegalArgumentException e) - { - throw (IOException) new IOException("KEX error.").initCause(e); - } - - boolean res = verifySignature(dhr.getSignature(), kxs.hostkey); - - if (res == false) - throw new IOException("Hostkey signature sent by remote is wrong!"); - - kxs.K = kxs.dhx.getK(); - - finishKex(); - kxs.state = -1; - return; - } - } - - throw new IllegalStateException("Unkown KEX method! (" + kxs.np.kex_algo + ")"); - } -} diff --git a/src/com/trilead/ssh2/transport/KexParameters.java b/src/com/trilead/ssh2/transport/KexParameters.java deleted file mode 100644 index 70bcf3e..0000000 --- a/src/com/trilead/ssh2/transport/KexParameters.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.trilead.ssh2.transport; - -/** - * KexParameters. - * - * @author Christian Plattner, plattner@trilead.com - * @version $Id: KexParameters.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ - */ -public class KexParameters -{ - public byte[] cookie; - public String[] kex_algorithms; - public String[] server_host_key_algorithms; - public String[] encryption_algorithms_client_to_server; - public String[] encryption_algorithms_server_to_client; - public String[] mac_algorithms_client_to_server; - public String[] mac_algorithms_server_to_client; - public String[] compression_algorithms_client_to_server; - public String[] compression_algorithms_server_to_client; - public String[] languages_client_to_server; - public String[] languages_server_to_client; - public boolean first_kex_packet_follows; - public int reserved_field1; -} diff --git a/src/com/trilead/ssh2/transport/KexState.java b/src/com/trilead/ssh2/transport/KexState.java deleted file mode 100644 index 8611f3f..0000000 --- a/src/com/trilead/ssh2/transport/KexState.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.trilead.ssh2.transport; - - -import java.math.BigInteger; - -import com.trilead.ssh2.DHGexParameters; -import com.trilead.ssh2.crypto.dh.DhGroupExchange; -import com.trilead.ssh2.crypto.dh.GenericDhExchange; -import com.trilead.ssh2.packets.PacketKexInit; - -/** - * KexState. - * - * @author Christian Plattner, plattner@trilead.com - * @version $Id: KexState.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $ - */ -public class KexState -{ - public PacketKexInit localKEX; - public PacketKexInit remoteKEX; - public NegotiatedParameters np; - public int state = 0; - - public BigInteger K; - public byte[] H; - - public byte[] hostkey; - - public String hashAlgo; - public GenericDhExchange dhx; - public DhGroupExchange dhgx; - public DHGexParameters dhgexParameters; -} diff --git a/src/com/trilead/ssh2/transport/MessageHandler.java b/src/com/trilead/ssh2/transport/MessageHandler.java deleted file mode 100644 index 039d473..0000000 --- a/src/com/trilead/ssh2/transport/MessageHandler.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.trilead.ssh2.transport; - -import java.io.IOException; - -/** - * MessageHandler. - * - * @author Christian Plattner, plattner@trilead.com - * @version $Id: MessageHandler.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ - */ -public interface MessageHandler -{ - public void handleMessage(byte[] msg, int msglen) throws IOException; -} diff --git a/src/com/trilead/ssh2/transport/NegotiateException.java b/src/com/trilead/ssh2/transport/NegotiateException.java deleted file mode 100644 index ff53097..0000000 --- a/src/com/trilead/ssh2/transport/NegotiateException.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.trilead.ssh2.transport; - -/** - * NegotiateException. - * - * @author Christian Plattner, plattner@trilead.com - * @version $Id: NegotiateException.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ - */ -public class NegotiateException extends Exception -{ - private static final long serialVersionUID = 3689910669428143157L; -} diff --git a/src/com/trilead/ssh2/transport/NegotiatedParameters.java b/src/com/trilead/ssh2/transport/NegotiatedParameters.java deleted file mode 100644 index e9f3a0a..0000000 --- a/src/com/trilead/ssh2/transport/NegotiatedParameters.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.trilead.ssh2.transport; - -/** - * NegotiatedParameters. - * - * @author Christian Plattner, plattner@trilead.com - * @version $Id: NegotiatedParameters.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $ - */ -public class NegotiatedParameters -{ - public boolean guessOK; - public String kex_algo; - public String server_host_key_algo; - public String enc_algo_client_to_server; - public String enc_algo_server_to_client; - public String mac_algo_client_to_server; - public String mac_algo_server_to_client; - public String comp_algo_client_to_server; - public String comp_algo_server_to_client; - public String lang_client_to_server; - public String lang_server_to_client; -} diff --git a/src/com/trilead/ssh2/transport/TransportConnection.java b/src/com/trilead/ssh2/transport/TransportConnection.java deleted file mode 100644 index 906c3c9..0000000 --- a/src/com/trilead/ssh2/transport/TransportConnection.java +++ /dev/null @@ -1,343 +0,0 @@ - -package com.trilead.ssh2.transport; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.security.SecureRandom; - -import com.trilead.ssh2.compression.ICompressor; -import com.trilead.ssh2.crypto.cipher.BlockCipher; -import com.trilead.ssh2.crypto.cipher.CipherInputStream; -import com.trilead.ssh2.crypto.cipher.CipherOutputStream; -import com.trilead.ssh2.crypto.cipher.NullCipher; -import com.trilead.ssh2.crypto.digest.MAC; -import com.trilead.ssh2.log.Logger; -import com.trilead.ssh2.packets.Packets; - - -/** - * TransportConnection. - * - * @author Christian Plattner, plattner@trilead.com - * @version $Id: TransportConnection.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $ - */ -public class TransportConnection -{ - private static final Logger log = Logger.getLogger(TransportConnection.class); - - int send_seq_number = 0; - - int recv_seq_number = 0; - - CipherInputStream cis; - - CipherOutputStream cos; - - boolean useRandomPadding = false; - - /* Depends on current MAC and CIPHER */ - - MAC send_mac; - - byte[] send_mac_buffer; - - int send_padd_blocksize = 8; - - MAC recv_mac; - - byte[] recv_mac_buffer; - - byte[] recv_mac_buffer_cmp; - - int recv_padd_blocksize = 8; - - ICompressor recv_comp = null; - - ICompressor send_comp = null; - - boolean can_recv_compress = false; - - boolean can_send_compress = false; - - byte[] recv_comp_buffer; - - byte[] send_comp_buffer; - - /* won't change */ - - final byte[] send_padding_buffer = new byte[256]; - - final byte[] send_packet_header_buffer = new byte[5]; - - final byte[] recv_padding_buffer = new byte[256]; - - final byte[] recv_packet_header_buffer = new byte[5]; - - boolean recv_packet_header_present = false; - - ClientServerHello csh; - - final SecureRandom rnd; - - public TransportConnection(InputStream is, OutputStream os, SecureRandom rnd) - { - this.cis = new CipherInputStream(new NullCipher(), is); - this.cos = new CipherOutputStream(new NullCipher(), os); - this.rnd = rnd; - } - - public void changeRecvCipher(BlockCipher bc, MAC mac) - { - cis.changeCipher(bc); - recv_mac = mac; - recv_mac_buffer = (mac != null) ? new byte[mac.size()] : null; - recv_mac_buffer_cmp = (mac != null) ? new byte[mac.size()] : null; - recv_padd_blocksize = bc.getBlockSize(); - if (recv_padd_blocksize < 8) - recv_padd_blocksize = 8; - } - - public void changeSendCipher(BlockCipher bc, MAC mac) - { - if ((bc instanceof NullCipher) == false) - { - /* Only use zero byte padding for the first few packets */ - useRandomPadding = true; - /* Once we start encrypting, there is no way back */ - } - - cos.changeCipher(bc); - send_mac = mac; - send_mac_buffer = (mac != null) ? new byte[mac.size()] : null; - send_padd_blocksize = bc.getBlockSize(); - if (send_padd_blocksize < 8) - send_padd_blocksize = 8; - } - - public void changeRecvCompression(ICompressor comp) - { - recv_comp = comp; - - if (comp != null) { - recv_comp_buffer = new byte[comp.getBufferSize()]; - can_recv_compress |= recv_comp.canCompressPreauth(); - } - } - - public void changeSendCompression(ICompressor comp) - { - send_comp = comp; - - if (comp != null) { - send_comp_buffer = new byte[comp.getBufferSize()]; - can_send_compress |= send_comp.canCompressPreauth(); - } - } - - public void sendMessage(byte[] message) throws IOException - { - sendMessage(message, 0, message.length, 0); - } - - public void sendMessage(byte[] message, int off, int len) throws IOException - { - sendMessage(message, off, len, 0); - } - - public int getPacketOverheadEstimate() - { - // return an estimate for the paket overhead (for send operations) - return 5 + 4 + (send_padd_blocksize - 1) + send_mac_buffer.length; - } - - public void sendMessage(byte[] message, int off, int len, int padd) throws IOException - { - if (padd < 4) - padd = 4; - else if (padd > 64) - padd = 64; - - if (send_comp != null && can_send_compress) { - if (send_comp_buffer.length < message.length + 1024) - send_comp_buffer = new byte[message.length + 1024]; - len = send_comp.compress(message, off, len, send_comp_buffer); - message = send_comp_buffer; - } - - int packet_len = 5 + len + padd; /* Minimum allowed padding is 4 */ - - int slack = packet_len % send_padd_blocksize; - - if (slack != 0) - { - packet_len += (send_padd_blocksize - slack); - } - - if (packet_len < 16) - packet_len = 16; - - int padd_len = packet_len - (5 + len); - - if (useRandomPadding) - { - for (int i = 0; i < padd_len; i = i + 4) - { - /* - * don't waste calls to rnd.nextInt() (by using only 8bit of the - * output). just believe me: even though we may write here up to 3 - * bytes which won't be used, there is no "buffer overflow" (i.e., - * arrayindexoutofbounds). the padding buffer is big enough =) (256 - * bytes, and that is bigger than any current cipher block size + 64). - */ - - int r = rnd.nextInt(); - send_padding_buffer[i] = (byte) r; - send_padding_buffer[i + 1] = (byte) (r >> 8); - send_padding_buffer[i + 2] = (byte) (r >> 16); - send_padding_buffer[i + 3] = (byte) (r >> 24); - } - } - else - { - /* use zero padding for unencrypted traffic */ - for (int i = 0; i < padd_len; i++) - send_padding_buffer[i] = 0; - /* Actually this code is paranoid: we never filled any - * bytes into the padding buffer so far, therefore it should - * consist of zeros only. - */ - } - - send_packet_header_buffer[0] = (byte) ((packet_len - 4) >> 24); - send_packet_header_buffer[1] = (byte) ((packet_len - 4) >> 16); - send_packet_header_buffer[2] = (byte) ((packet_len - 4) >> 8); - send_packet_header_buffer[3] = (byte) ((packet_len - 4)); - send_packet_header_buffer[4] = (byte) padd_len; - - cos.write(send_packet_header_buffer, 0, 5); - cos.write(message, off, len); - cos.write(send_padding_buffer, 0, padd_len); - - if (send_mac != null) - { - send_mac.initMac(send_seq_number); - send_mac.update(send_packet_header_buffer, 0, 5); - send_mac.update(message, off, len); - send_mac.update(send_padding_buffer, 0, padd_len); - - send_mac.getMac(send_mac_buffer, 0); - cos.writePlain(send_mac_buffer, 0, send_mac_buffer.length); - } - - cos.flush(); - - if (log.isEnabled()) - { - log.log(90, "Sent " + Packets.getMessageName(message[off] & 0xff) + " " + len + " bytes payload"); - } - - send_seq_number++; - } - - public int peekNextMessageLength() throws IOException - { - if (recv_packet_header_present == false) - { - cis.read(recv_packet_header_buffer, 0, 5); - recv_packet_header_present = true; - } - - int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24) - | ((recv_packet_header_buffer[1] & 0xff) << 16) | ((recv_packet_header_buffer[2] & 0xff) << 8) - | ((recv_packet_header_buffer[3] & 0xff)); - - int padding_length = recv_packet_header_buffer[4] & 0xff; - - if (packet_length > 35000 || packet_length < 12) - throw new IOException("Illegal packet size! (" + packet_length + ")"); - - int payload_length = packet_length - padding_length - 1; - - if (payload_length < 0) - throw new IOException("Illegal padding_length in packet from remote (" + padding_length + ")"); - - return payload_length; - } - - public int receiveMessage(byte buffer[], int off, int len) throws IOException - { - if (recv_packet_header_present == false) - { - cis.read(recv_packet_header_buffer, 0, 5); - } - else - recv_packet_header_present = false; - - int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24) - | ((recv_packet_header_buffer[1] & 0xff) << 16) | ((recv_packet_header_buffer[2] & 0xff) << 8) - | ((recv_packet_header_buffer[3] & 0xff)); - - int padding_length = recv_packet_header_buffer[4] & 0xff; - - if (packet_length > 35000 || packet_length < 12) - throw new IOException("Illegal packet size! (" + packet_length + ")"); - - int payload_length = packet_length - padding_length - 1; - - if (payload_length < 0) - throw new IOException("Illegal padding_length in packet from remote (" + padding_length + ")"); - - if (payload_length >= len) - throw new IOException("Receive buffer too small (" + len + ", need " + payload_length + ")"); - - cis.read(buffer, off, payload_length); - cis.read(recv_padding_buffer, 0, padding_length); - - if (recv_mac != null) - { - cis.readPlain(recv_mac_buffer, 0, recv_mac_buffer.length); - - recv_mac.initMac(recv_seq_number); - recv_mac.update(recv_packet_header_buffer, 0, 5); - recv_mac.update(buffer, off, payload_length); - recv_mac.update(recv_padding_buffer, 0, padding_length); - recv_mac.getMac(recv_mac_buffer_cmp, 0); - - for (int i = 0; i < recv_mac_buffer.length; i++) - { - if (recv_mac_buffer[i] != recv_mac_buffer_cmp[i]) - throw new IOException("Remote sent corrupt MAC."); - } - } - - recv_seq_number++; - - if (log.isEnabled()) - { - log.log(90, "Received " + Packets.getMessageName(buffer[off] & 0xff) + " " + payload_length - + " bytes payload"); - } - - if (recv_comp != null && can_recv_compress) { - int[] uncomp_len = new int[] { payload_length }; - buffer = recv_comp.uncompress(buffer, off, uncomp_len); - - if (buffer == null) { - throw new IOException("Error while inflating remote data"); - } else { - return uncomp_len[0]; - } - } else { - return payload_length; - } - } - - /** - * - */ - public void startCompression() { - can_recv_compress = true; - can_send_compress = true; - } -} diff --git a/src/com/trilead/ssh2/transport/TransportManager.java b/src/com/trilead/ssh2/transport/TransportManager.java deleted file mode 100644 index 2e88126..0000000 --- a/src/com/trilead/ssh2/transport/TransportManager.java +++ /dev/null @@ -1,802 +0,0 @@ - -package com.trilead.ssh2.transport; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.net.UnknownHostException; -import java.security.SecureRandom; -import java.util.Vector; - -import com.trilead.ssh2.ConnectionInfo; -import com.trilead.ssh2.ConnectionMonitor; -import com.trilead.ssh2.DHGexParameters; -import com.trilead.ssh2.HTTPProxyData; -import com.trilead.ssh2.HTTPProxyException; -import com.trilead.ssh2.ProxyData; -import com.trilead.ssh2.ServerHostKeyVerifier; -import com.trilead.ssh2.compression.ICompressor; -import com.trilead.ssh2.crypto.Base64; -import com.trilead.ssh2.crypto.CryptoWishList; -import com.trilead.ssh2.crypto.cipher.BlockCipher; -import com.trilead.ssh2.crypto.digest.MAC; -import com.trilead.ssh2.log.Logger; -import com.trilead.ssh2.packets.PacketDisconnect; -import com.trilead.ssh2.packets.Packets; -import com.trilead.ssh2.packets.TypesReader; -import com.trilead.ssh2.util.Tokenizer; - - -/* - * Yes, the "standard" is a big mess. On one side, the say that arbitary channel - * packets are allowed during kex exchange, on the other side we need to blindly - * ignore the next _packet_ if the KEX guess was wrong. Where do we know from that - * the next packet is not a channel data packet? Yes, we could check if it is in - * the KEX range. But the standard says nothing about this. The OpenSSH guys - * block local "normal" traffic during KEX. That's fine - however, they assume - * that the other side is doing the same. During re-key, if they receive traffic - * other than KEX, they become horribly irritated and kill the connection. Since - * we are very likely going to communicate with OpenSSH servers, we have to play - * the same game - even though we could do better. - * - * btw: having stdout and stderr on the same channel, with a shared window, is - * also a VERY good idea... =( - */ - -/** - * TransportManager. - * - * @author Christian Plattner, plattner@trilead.com - * @version $Id: TransportManager.java,v 1.2 2008/04/01 12:38:09 cplattne Exp $ - */ -public class TransportManager -{ - private static final Logger log = Logger.getLogger(TransportManager.class); - - class HandlerEntry - { - MessageHandler mh; - int low; - int high; - } - - private final Vector<byte[]> asynchronousQueue = new Vector<byte[]>(); - private Thread asynchronousThread = null; - - class AsynchronousWorker extends Thread - { - public void run() - { - while (true) - { - byte[] msg = null; - - synchronized (asynchronousQueue) - { - if (asynchronousQueue.size() == 0) - { - /* After the queue is empty for about 2 seconds, stop this thread */ - - try - { - asynchronousQueue.wait(2000); - } - catch (InterruptedException e) - { - /* OKOK, if somebody interrupts us, then we may die earlier. */ - } - - if (asynchronousQueue.size() == 0) - { - asynchronousThread = null; - return; - } - } - - msg = asynchronousQueue.remove(0); - } - - /* The following invocation may throw an IOException. - * There is no point in handling it - it simply means - * that the connection has a problem and we should stop - * sending asynchronously messages. We do not need to signal that - * we have exited (asynchronousThread = null): further - * messages in the queue cannot be sent by this or any - * other thread. - * Other threads will sooner or later (when receiving or - * sending the next message) get the same IOException and - * get to the same conclusion. - */ - - try - { - sendMessage(msg); - } - catch (IOException e) - { - return; - } - } - } - } - - String hostname; - int port; - final Socket sock = new Socket(); - - Object connectionSemaphore = new Object(); - - boolean flagKexOngoing = false; - boolean connectionClosed = false; - - Throwable reasonClosedCause = null; - - TransportConnection tc; - KexManager km; - - Vector<HandlerEntry> messageHandlers = new Vector<HandlerEntry>(); - - Thread receiveThread; - - Vector connectionMonitors = new Vector(); - boolean monitorsWereInformed = false; - - /** - * There were reports that there are JDKs which use - * the resolver even though one supplies a dotted IP - * address in the Socket constructor. That is why we - * try to generate the InetAdress "by hand". - * - * @param host - * @return the InetAddress - * @throws UnknownHostException - */ - private InetAddress createInetAddress(String host) throws UnknownHostException - { - /* Check if it is a dotted IP4 address */ - - InetAddress addr = parseIPv4Address(host); - - if (addr != null) - return addr; - - return InetAddress.getByName(host); - } - - private InetAddress parseIPv4Address(String host) throws UnknownHostException - { - if (host == null) - return null; - - String[] quad = Tokenizer.parseTokens(host, '.'); - - if ((quad == null) || (quad.length != 4)) - return null; - - byte[] addr = new byte[4]; - - for (int i = 0; i < 4; i++) - { - int part = 0; - - if ((quad[i].length() == 0) || (quad[i].length() > 3)) - return null; - - for (int k = 0; k < quad[i].length(); k++) - { - char c = quad[i].charAt(k); - - /* No, Character.isDigit is not the same */ - if ((c < '0') || (c > '9')) - return null; - - part = part * 10 + (c - '0'); - } - - if (part > 255) /* 300.1.2.3 is invalid =) */ - return null; - - addr[i] = (byte) part; - } - - return InetAddress.getByAddress(host, addr); - } - - public TransportManager(String host, int port) throws IOException - { - this.hostname = host; - this.port = port; - } - - public int getPacketOverheadEstimate() - { - return tc.getPacketOverheadEstimate(); - } - - public void setTcpNoDelay(boolean state) throws IOException - { - sock.setTcpNoDelay(state); - } - - public void setSoTimeout(int timeout) throws IOException - { - sock.setSoTimeout(timeout); - } - - public ConnectionInfo getConnectionInfo(int kexNumber) throws IOException - { - return km.getOrWaitForConnectionInfo(kexNumber); - } - - public Throwable getReasonClosedCause() - { - synchronized (connectionSemaphore) - { - return reasonClosedCause; - } - } - - public byte[] getSessionIdentifier() - { - return km.sessionId; - } - - public void close(Throwable cause, boolean useDisconnectPacket) - { - if (useDisconnectPacket == false) - { - /* OK, hard shutdown - do not aquire the semaphore, - * perhaps somebody is inside (and waits until the remote - * side is ready to accept new data). */ - - try - { - sock.close(); - } - catch (IOException ignore) - { - } - - /* OK, whoever tried to send data, should now agree that - * there is no point in further waiting =) - * It is safe now to aquire the semaphore. - */ - } - - synchronized (connectionSemaphore) - { - if (connectionClosed == false) - { - if (useDisconnectPacket == true) - { - try - { - byte[] msg = new PacketDisconnect(Packets.SSH_DISCONNECT_BY_APPLICATION, cause.getMessage(), "") - .getPayload(); - if (tc != null) - tc.sendMessage(msg); - } - catch (IOException ignore) - { - } - - try - { - sock.close(); - } - catch (IOException ignore) - { - } - } - - connectionClosed = true; - reasonClosedCause = cause; /* may be null */ - } - connectionSemaphore.notifyAll(); - } - - /* No check if we need to inform the monitors */ - - Vector monitors = null; - - synchronized (this) - { - /* Short term lock to protect "connectionMonitors" - * and "monitorsWereInformed" - * (they may be modified concurrently) - */ - - if (monitorsWereInformed == false) - { - monitorsWereInformed = true; - monitors = (Vector) connectionMonitors.clone(); - } - } - - if (monitors != null) - { - for (int i = 0; i < monitors.size(); i++) - { - try - { - ConnectionMonitor cmon = (ConnectionMonitor) monitors.elementAt(i); - cmon.connectionLost(reasonClosedCause); - } - catch (Exception ignore) - { - } - } - } - } - - private void establishConnection(ProxyData proxyData, int connectTimeout) throws IOException - { - /* See the comment for createInetAddress() */ - - if (proxyData == null) - { - InetAddress addr = createInetAddress(hostname); - sock.connect(new InetSocketAddress(addr, port), connectTimeout); - sock.setSoTimeout(0); - return; - } - - if (proxyData instanceof HTTPProxyData) - { - HTTPProxyData pd = (HTTPProxyData) proxyData; - - /* At the moment, we only support HTTP proxies */ - - InetAddress addr = createInetAddress(pd.proxyHost); - sock.connect(new InetSocketAddress(addr, pd.proxyPort), connectTimeout); - sock.setSoTimeout(0); - - /* OK, now tell the proxy where we actually want to connect to */ - - StringBuffer sb = new StringBuffer(); - - sb.append("CONNECT "); - sb.append(hostname); - sb.append(':'); - sb.append(port); - sb.append(" HTTP/1.0\r\n"); - - if ((pd.proxyUser != null) && (pd.proxyPass != null)) - { - String credentials = pd.proxyUser + ":" + pd.proxyPass; - char[] encoded = Base64.encode(credentials.getBytes("ISO-8859-1")); - sb.append("Proxy-Authorization: Basic "); - sb.append(encoded); - sb.append("\r\n"); - } - - if (pd.requestHeaderLines != null) - { - for (int i = 0; i < pd.requestHeaderLines.length; i++) - { - if (pd.requestHeaderLines[i] != null) - { - sb.append(pd.requestHeaderLines[i]); - sb.append("\r\n"); - } - } - } - - sb.append("\r\n"); - - OutputStream out = sock.getOutputStream(); - - out.write(sb.toString().getBytes("ISO-8859-1")); - out.flush(); - - /* Now parse the HTTP response */ - - byte[] buffer = new byte[1024]; - InputStream in = sock.getInputStream(); - - int len = ClientServerHello.readLineRN(in, buffer); - - String httpReponse = new String(buffer, 0, len, "ISO-8859-1"); - - if (httpReponse.startsWith("HTTP/") == false) - throw new IOException("The proxy did not send back a valid HTTP response."); - - /* "HTTP/1.X XYZ X" => 14 characters minimum */ - - if ((httpReponse.length() < 14) || (httpReponse.charAt(8) != ' ') || (httpReponse.charAt(12) != ' ')) - throw new IOException("The proxy did not send back a valid HTTP response."); - - int errorCode = 0; - - try - { - errorCode = Integer.parseInt(httpReponse.substring(9, 12)); - } - catch (NumberFormatException ignore) - { - throw new IOException("The proxy did not send back a valid HTTP response."); - } - - if ((errorCode < 0) || (errorCode > 999)) - throw new IOException("The proxy did not send back a valid HTTP response."); - - if (errorCode != 200) - { - throw new HTTPProxyException(httpReponse.substring(13), errorCode); - } - - /* OK, read until empty line */ - - while (true) - { - len = ClientServerHello.readLineRN(in, buffer); - if (len == 0) - break; - } - return; - } - - throw new IOException("Unsupported ProxyData"); - } - - public void initialize(CryptoWishList cwl, ServerHostKeyVerifier verifier, DHGexParameters dhgex, - int connectTimeout, SecureRandom rnd, ProxyData proxyData) throws IOException - { - /* First, establish the TCP connection to the SSH-2 server */ - - establishConnection(proxyData, connectTimeout); - - /* Parse the server line and say hello - important: this information is later needed for the - * key exchange (to stop man-in-the-middle attacks) - that is why we wrap it into an object - * for later use. - */ - - ClientServerHello csh = new ClientServerHello(sock.getInputStream(), sock.getOutputStream()); - - tc = new TransportConnection(sock.getInputStream(), sock.getOutputStream(), rnd); - - km = new KexManager(this, csh, cwl, hostname, port, verifier, rnd); - km.initiateKEX(cwl, dhgex); - - receiveThread = new Thread(new Runnable() - { - public void run() - { - try - { - receiveLoop(); - } - catch (IOException e) - { - close(e, false); - - if (log.isEnabled()) - log.log(10, "Receive thread: error in receiveLoop: " + e.getMessage()); - } - - if (log.isEnabled()) - log.log(50, "Receive thread: back from receiveLoop"); - - /* Tell all handlers that it is time to say goodbye */ - - if (km != null) - { - try - { - km.handleMessage(null, 0); - } - catch (IOException e) - { - } - } - - for (int i = 0; i < messageHandlers.size(); i++) - { - HandlerEntry he = messageHandlers.elementAt(i); - try - { - he.mh.handleMessage(null, 0); - } - catch (Exception ignore) - { - } - } - } - }); - - receiveThread.setDaemon(true); - receiveThread.start(); - } - - public void registerMessageHandler(MessageHandler mh, int low, int high) - { - HandlerEntry he = new HandlerEntry(); - he.mh = mh; - he.low = low; - he.high = high; - - synchronized (messageHandlers) - { - messageHandlers.addElement(he); - } - } - - public void removeMessageHandler(MessageHandler mh, int low, int high) - { - synchronized (messageHandlers) - { - for (int i = 0; i < messageHandlers.size(); i++) - { - HandlerEntry he = messageHandlers.elementAt(i); - if ((he.mh == mh) && (he.low == low) && (he.high == high)) - { - messageHandlers.removeElementAt(i); - break; - } - } - } - } - - public void sendKexMessage(byte[] msg) throws IOException - { - synchronized (connectionSemaphore) - { - if (connectionClosed) - { - throw (IOException) new IOException("Sorry, this connection is closed.").initCause(reasonClosedCause); - } - - flagKexOngoing = true; - - try - { - tc.sendMessage(msg); - } - catch (IOException e) - { - close(e, false); - throw e; - } - } - } - - public void kexFinished() throws IOException - { - synchronized (connectionSemaphore) - { - flagKexOngoing = false; - connectionSemaphore.notifyAll(); - } - } - - public void forceKeyExchange(CryptoWishList cwl, DHGexParameters dhgex) throws IOException - { - km.initiateKEX(cwl, dhgex); - } - - public void changeRecvCipher(BlockCipher bc, MAC mac) - { - tc.changeRecvCipher(bc, mac); - } - - public void changeSendCipher(BlockCipher bc, MAC mac) - { - tc.changeSendCipher(bc, mac); - } - - /** - * @param comp - */ - public void changeRecvCompression(ICompressor comp) { - tc.changeRecvCompression(comp); - } - - /** - * @param comp - */ - public void changeSendCompression(ICompressor comp) { - tc.changeSendCompression(comp); - } - - /** - * - */ - public void startCompression() { - tc.startCompression(); - } - - public void sendAsynchronousMessage(byte[] msg) throws IOException - { - synchronized (asynchronousQueue) - { - asynchronousQueue.addElement(msg); - - /* This limit should be flexible enough. We need this, otherwise the peer - * can flood us with global requests (and other stuff where we have to reply - * with an asynchronous message) and (if the server just sends data and does not - * read what we send) this will probably put us in a low memory situation - * (our send queue would grow and grow and...) */ - - if (asynchronousQueue.size() > 100) - throw new IOException("Error: the peer is not consuming our asynchronous replies."); - - /* Check if we have an asynchronous sending thread */ - - if (asynchronousThread == null) - { - asynchronousThread = new AsynchronousWorker(); - asynchronousThread.setDaemon(true); - asynchronousThread.start(); - - /* The thread will stop after 2 seconds of inactivity (i.e., empty queue) */ - } - } - } - - public void setConnectionMonitors(Vector monitors) - { - synchronized (this) - { - connectionMonitors = (Vector) monitors.clone(); - } - } - - public void sendMessage(byte[] msg) throws IOException - { - if (Thread.currentThread() == receiveThread) - throw new IOException("Assertion error: sendMessage may never be invoked by the receiver thread!"); - - synchronized (connectionSemaphore) - { - while (true) - { - if (connectionClosed) - { - throw (IOException) new IOException("Sorry, this connection is closed.") - .initCause(reasonClosedCause); - } - - if (flagKexOngoing == false) - break; - - try - { - connectionSemaphore.wait(); - } - catch (InterruptedException e) - { - } - } - - try - { - tc.sendMessage(msg); - } - catch (IOException e) - { - close(e, false); - throw e; - } - } - } - - public void receiveLoop() throws IOException - { - byte[] msg = new byte[35000]; - - while (true) - { - int msglen = tc.receiveMessage(msg, 0, msg.length); - - int type = msg[0] & 0xff; - - if (type == Packets.SSH_MSG_IGNORE) - continue; - - if (type == Packets.SSH_MSG_DEBUG) - { - if (log.isEnabled()) - { - TypesReader tr = new TypesReader(msg, 0, msglen); - tr.readByte(); - tr.readBoolean(); - StringBuffer debugMessageBuffer = new StringBuffer(); - debugMessageBuffer.append(tr.readString("UTF-8")); - - for (int i = 0; i < debugMessageBuffer.length(); i++) - { - char c = debugMessageBuffer.charAt(i); - - if ((c >= 32) && (c <= 126)) - continue; - debugMessageBuffer.setCharAt(i, '\uFFFD'); - } - - log.log(50, "DEBUG Message from remote: '" + debugMessageBuffer.toString() + "'"); - } - continue; - } - - if (type == Packets.SSH_MSG_UNIMPLEMENTED) - { - throw new IOException("Peer sent UNIMPLEMENTED message, that should not happen."); - } - - if (type == Packets.SSH_MSG_DISCONNECT) - { - TypesReader tr = new TypesReader(msg, 0, msglen); - tr.readByte(); - int reason_code = tr.readUINT32(); - StringBuffer reasonBuffer = new StringBuffer(); - reasonBuffer.append(tr.readString("UTF-8")); - - /* - * Do not get fooled by servers that send abnormal long error - * messages - */ - - if (reasonBuffer.length() > 255) - { - reasonBuffer.setLength(255); - reasonBuffer.setCharAt(254, '.'); - reasonBuffer.setCharAt(253, '.'); - reasonBuffer.setCharAt(252, '.'); - } - - /* - * Also, check that the server did not send charcaters that may - * screw up the receiver -> restrict to reasonable US-ASCII - * subset -> "printable characters" (ASCII 32 - 126). Replace - * all others with 0xFFFD (UNICODE replacement character). - */ - - for (int i = 0; i < reasonBuffer.length(); i++) - { - char c = reasonBuffer.charAt(i); - - if ((c >= 32) && (c <= 126)) - continue; - reasonBuffer.setCharAt(i, '\uFFFD'); - } - - throw new IOException("Peer sent DISCONNECT message (reason code " + reason_code + "): " - + reasonBuffer.toString()); - } - - /* - * Is it a KEX Packet? - */ - - if ((type == Packets.SSH_MSG_KEXINIT) || (type == Packets.SSH_MSG_NEWKEYS) - || ((type >= 30) && (type <= 49))) - { - km.handleMessage(msg, msglen); - continue; - } - - if (type == Packets.SSH_MSG_USERAUTH_SUCCESS) { - tc.startCompression(); - } - - MessageHandler mh = null; - - for (int i = 0; i < messageHandlers.size(); i++) - { - HandlerEntry he = messageHandlers.elementAt(i); - if ((he.low <= type) && (type <= he.high)) - { - mh = he.mh; - break; - } - } - - if (mh == null) - throw new IOException("Unexpected SSH message (type " + type + ")"); - - mh.handleMessage(msg, msglen); - } - } -} |