0
|
1
|
|
2 package com.trilead.ssh2.transport;
|
|
3
|
|
4 import java.io.IOException;
|
|
5 import java.io.InputStream;
|
|
6 import java.io.OutputStream;
|
|
7 import java.security.SecureRandom;
|
|
8
|
|
9 import com.trilead.ssh2.compression.ICompressor;
|
|
10 import com.trilead.ssh2.crypto.cipher.BlockCipher;
|
|
11 import com.trilead.ssh2.crypto.cipher.CipherInputStream;
|
|
12 import com.trilead.ssh2.crypto.cipher.CipherOutputStream;
|
|
13 import com.trilead.ssh2.crypto.cipher.NullCipher;
|
|
14 import com.trilead.ssh2.crypto.digest.MAC;
|
|
15 import com.trilead.ssh2.log.Logger;
|
|
16 import com.trilead.ssh2.packets.Packets;
|
|
17
|
|
18
|
|
19 /**
|
|
20 * TransportConnection.
|
|
21 *
|
|
22 * @author Christian Plattner, plattner@trilead.com
|
|
23 * @version $Id: TransportConnection.java,v 1.1 2007/10/15 12:49:56 cplattne Exp $
|
|
24 */
|
|
25 public class TransportConnection {
|
|
26 private static final Logger log = Logger.getLogger(TransportConnection.class);
|
|
27
|
|
28 int send_seq_number = 0;
|
|
29
|
|
30 int recv_seq_number = 0;
|
|
31
|
|
32 CipherInputStream cis;
|
|
33
|
|
34 CipherOutputStream cos;
|
|
35
|
|
36 boolean useRandomPadding = false;
|
|
37
|
|
38 /* Depends on current MAC and CIPHER */
|
|
39
|
|
40 MAC send_mac;
|
|
41
|
|
42 byte[] send_mac_buffer;
|
|
43
|
|
44 int send_padd_blocksize = 8;
|
|
45
|
|
46 MAC recv_mac;
|
|
47
|
|
48 byte[] recv_mac_buffer;
|
|
49
|
|
50 byte[] recv_mac_buffer_cmp;
|
|
51
|
|
52 int recv_padd_blocksize = 8;
|
|
53
|
|
54 ICompressor recv_comp = null;
|
|
55
|
|
56 ICompressor send_comp = null;
|
|
57
|
|
58 boolean can_recv_compress = false;
|
|
59
|
|
60 boolean can_send_compress = false;
|
|
61
|
|
62 byte[] recv_comp_buffer;
|
|
63
|
|
64 byte[] send_comp_buffer;
|
|
65
|
|
66 /* won't change */
|
|
67
|
|
68 final byte[] send_padding_buffer = new byte[256];
|
|
69
|
|
70 final byte[] send_packet_header_buffer = new byte[5];
|
|
71
|
|
72 final byte[] recv_padding_buffer = new byte[256];
|
|
73
|
|
74 final byte[] recv_packet_header_buffer = new byte[5];
|
|
75
|
|
76 boolean recv_packet_header_present = false;
|
|
77
|
|
78 ClientServerHello csh;
|
|
79
|
|
80 final SecureRandom rnd;
|
|
81
|
|
82 public TransportConnection(InputStream is, OutputStream os, SecureRandom rnd) {
|
|
83 this.cis = new CipherInputStream(new NullCipher(), is);
|
|
84 this.cos = new CipherOutputStream(new NullCipher(), os);
|
|
85 this.rnd = rnd;
|
|
86 }
|
|
87
|
|
88 public void changeRecvCipher(BlockCipher bc, MAC mac) {
|
|
89 cis.changeCipher(bc);
|
|
90 recv_mac = mac;
|
|
91 recv_mac_buffer = (mac != null) ? new byte[mac.size()] : null;
|
|
92 recv_mac_buffer_cmp = (mac != null) ? new byte[mac.size()] : null;
|
|
93 recv_padd_blocksize = bc.getBlockSize();
|
|
94
|
|
95 if (recv_padd_blocksize < 8)
|
|
96 recv_padd_blocksize = 8;
|
|
97 }
|
|
98
|
|
99 public void changeSendCipher(BlockCipher bc, MAC mac) {
|
|
100 if ((bc instanceof NullCipher) == false) {
|
|
101 /* Only use zero byte padding for the first few packets */
|
|
102 useRandomPadding = true;
|
|
103 /* Once we start encrypting, there is no way back */
|
|
104 }
|
|
105
|
|
106 cos.changeCipher(bc);
|
|
107 send_mac = mac;
|
|
108 send_mac_buffer = (mac != null) ? new byte[mac.size()] : null;
|
|
109 send_padd_blocksize = bc.getBlockSize();
|
|
110
|
|
111 if (send_padd_blocksize < 8)
|
|
112 send_padd_blocksize = 8;
|
|
113 }
|
|
114
|
|
115 public void changeRecvCompression(ICompressor comp) {
|
|
116 recv_comp = comp;
|
|
117
|
|
118 if (comp != null) {
|
|
119 recv_comp_buffer = new byte[comp.getBufferSize()];
|
|
120 can_recv_compress |= recv_comp.canCompressPreauth();
|
|
121 }
|
|
122 }
|
|
123
|
|
124 public void changeSendCompression(ICompressor comp) {
|
|
125 send_comp = comp;
|
|
126
|
|
127 if (comp != null) {
|
|
128 send_comp_buffer = new byte[comp.getBufferSize()];
|
|
129 can_send_compress |= send_comp.canCompressPreauth();
|
|
130 }
|
|
131 }
|
|
132
|
|
133 public void sendMessage(byte[] message) throws IOException {
|
|
134 sendMessage(message, 0, message.length, 0);
|
|
135 }
|
|
136
|
|
137 public void sendMessage(byte[] message, int off, int len) throws IOException {
|
|
138 sendMessage(message, off, len, 0);
|
|
139 }
|
|
140
|
|
141 public int getPacketOverheadEstimate() {
|
|
142 // return an estimate for the paket overhead (for send operations)
|
|
143 return 5 + 4 + (send_padd_blocksize - 1) + send_mac_buffer.length;
|
|
144 }
|
|
145
|
|
146 public void sendMessage(byte[] message, int off, int len, int padd) throws IOException {
|
|
147 if (padd < 4)
|
|
148 padd = 4;
|
|
149 else if (padd > 64)
|
|
150 padd = 64;
|
|
151
|
|
152 if (send_comp != null && can_send_compress) {
|
|
153 if (send_comp_buffer.length < message.length + 1024)
|
|
154 send_comp_buffer = new byte[message.length + 1024];
|
|
155
|
|
156 len = send_comp.compress(message, off, len, send_comp_buffer);
|
|
157 message = send_comp_buffer;
|
|
158 }
|
|
159
|
|
160 int packet_len = 5 + len + padd; /* Minimum allowed padding is 4 */
|
|
161 int slack = packet_len % send_padd_blocksize;
|
|
162
|
|
163 if (slack != 0) {
|
|
164 packet_len += (send_padd_blocksize - slack);
|
|
165 }
|
|
166
|
|
167 if (packet_len < 16)
|
|
168 packet_len = 16;
|
|
169
|
|
170 int padd_len = packet_len - (5 + len);
|
|
171
|
|
172 if (useRandomPadding) {
|
|
173 for (int i = 0; i < padd_len; i = i + 4) {
|
|
174 /*
|
|
175 * don't waste calls to rnd.nextInt() (by using only 8bit of the
|
|
176 * output). just believe me: even though we may write here up to 3
|
|
177 * bytes which won't be used, there is no "buffer overflow" (i.e.,
|
|
178 * arrayindexoutofbounds). the padding buffer is big enough =) (256
|
|
179 * bytes, and that is bigger than any current cipher block size + 64).
|
|
180 */
|
|
181 int r = rnd.nextInt();
|
|
182 send_padding_buffer[i] = (byte) r;
|
|
183 send_padding_buffer[i + 1] = (byte)(r >> 8);
|
|
184 send_padding_buffer[i + 2] = (byte)(r >> 16);
|
|
185 send_padding_buffer[i + 3] = (byte)(r >> 24);
|
|
186 }
|
|
187 }
|
|
188 else {
|
|
189 /* use zero padding for unencrypted traffic */
|
|
190 for (int i = 0; i < padd_len; i++)
|
|
191 send_padding_buffer[i] = 0;
|
|
192
|
|
193 /* Actually this code is paranoid: we never filled any
|
|
194 * bytes into the padding buffer so far, therefore it should
|
|
195 * consist of zeros only.
|
|
196 */
|
|
197 }
|
|
198
|
|
199 send_packet_header_buffer[0] = (byte)((packet_len - 4) >> 24);
|
|
200 send_packet_header_buffer[1] = (byte)((packet_len - 4) >> 16);
|
|
201 send_packet_header_buffer[2] = (byte)((packet_len - 4) >> 8);
|
|
202 send_packet_header_buffer[3] = (byte)((packet_len - 4));
|
|
203 send_packet_header_buffer[4] = (byte) padd_len;
|
|
204 cos.write(send_packet_header_buffer, 0, 5);
|
|
205 cos.write(message, off, len);
|
|
206 cos.write(send_padding_buffer, 0, padd_len);
|
|
207
|
|
208 if (send_mac != null) {
|
|
209 send_mac.initMac(send_seq_number);
|
|
210 send_mac.update(send_packet_header_buffer, 0, 5);
|
|
211 send_mac.update(message, off, len);
|
|
212 send_mac.update(send_padding_buffer, 0, padd_len);
|
|
213 send_mac.getMac(send_mac_buffer, 0);
|
|
214 cos.writePlain(send_mac_buffer, 0, send_mac_buffer.length);
|
|
215 }
|
|
216
|
|
217 cos.flush();
|
|
218
|
|
219 if (log.isEnabled()) {
|
|
220 log.log(90, "Sent " + Packets.getMessageName(message[off] & 0xff) + " " + len + " bytes payload");
|
|
221 }
|
|
222
|
|
223 send_seq_number++;
|
|
224 }
|
|
225
|
|
226 public int peekNextMessageLength() throws IOException {
|
|
227 if (recv_packet_header_present == false) {
|
|
228 cis.read(recv_packet_header_buffer, 0, 5);
|
|
229 recv_packet_header_present = true;
|
|
230 }
|
|
231
|
|
232 int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24)
|
|
233 | ((recv_packet_header_buffer[1] & 0xff) << 16) | ((recv_packet_header_buffer[2] & 0xff) << 8)
|
|
234 | ((recv_packet_header_buffer[3] & 0xff));
|
|
235 int padding_length = recv_packet_header_buffer[4] & 0xff;
|
|
236
|
|
237 if (packet_length > 35000 || packet_length < 12)
|
|
238 throw new IOException("Illegal packet size! (" + packet_length + ")");
|
|
239
|
|
240 int payload_length = packet_length - padding_length - 1;
|
|
241
|
|
242 if (payload_length < 0)
|
|
243 throw new IOException("Illegal padding_length in packet from remote (" + padding_length + ")");
|
|
244
|
|
245 return payload_length;
|
|
246 }
|
|
247
|
|
248 public int receiveMessage(byte buffer[], int off, int len) throws IOException {
|
|
249 if (recv_packet_header_present == false) {
|
|
250 cis.read(recv_packet_header_buffer, 0, 5);
|
|
251 }
|
|
252 else
|
|
253 recv_packet_header_present = false;
|
|
254
|
|
255 int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24)
|
|
256 | ((recv_packet_header_buffer[1] & 0xff) << 16) | ((recv_packet_header_buffer[2] & 0xff) << 8)
|
|
257 | ((recv_packet_header_buffer[3] & 0xff));
|
|
258 int padding_length = recv_packet_header_buffer[4] & 0xff;
|
|
259
|
|
260 if (packet_length > 35000 || packet_length < 12)
|
|
261 throw new IOException("Illegal packet size! (" + packet_length + ")");
|
|
262
|
|
263 int payload_length = packet_length - padding_length - 1;
|
|
264
|
|
265 if (payload_length < 0)
|
|
266 throw new IOException("Illegal padding_length in packet from remote (" + padding_length + ")");
|
|
267
|
|
268 if (payload_length >= len)
|
|
269 throw new IOException("Receive buffer too small (" + len + ", need " + payload_length + ")");
|
|
270
|
|
271 cis.read(buffer, off, payload_length);
|
|
272 cis.read(recv_padding_buffer, 0, padding_length);
|
|
273
|
|
274 if (recv_mac != null) {
|
|
275 cis.readPlain(recv_mac_buffer, 0, recv_mac_buffer.length);
|
|
276 recv_mac.initMac(recv_seq_number);
|
|
277 recv_mac.update(recv_packet_header_buffer, 0, 5);
|
|
278 recv_mac.update(buffer, off, payload_length);
|
|
279 recv_mac.update(recv_padding_buffer, 0, padding_length);
|
|
280 recv_mac.getMac(recv_mac_buffer_cmp, 0);
|
|
281
|
|
282 for (int i = 0; i < recv_mac_buffer.length; i++) {
|
|
283 if (recv_mac_buffer[i] != recv_mac_buffer_cmp[i])
|
|
284 throw new IOException("Remote sent corrupt MAC.");
|
|
285 }
|
|
286 }
|
|
287
|
|
288 recv_seq_number++;
|
|
289
|
|
290 if (log.isEnabled()) {
|
|
291 log.log(90, "Received " + Packets.getMessageName(buffer[off] & 0xff) + " " + payload_length
|
|
292 + " bytes payload");
|
|
293 }
|
|
294
|
|
295 if (recv_comp != null && can_recv_compress) {
|
|
296 int[] uncomp_len = new int[] { payload_length };
|
|
297 buffer = recv_comp.uncompress(buffer, off, uncomp_len);
|
|
298
|
|
299 if (buffer == null) {
|
|
300 throw new IOException("Error while inflating remote data");
|
|
301 }
|
|
302 else {
|
|
303 return uncomp_len[0];
|
|
304 }
|
|
305 }
|
|
306 else {
|
|
307 return payload_length;
|
|
308 }
|
|
309 }
|
|
310
|
|
311 /**
|
|
312 *
|
|
313 */
|
|
314 public void startCompression() {
|
|
315 can_recv_compress = true;
|
|
316 can_send_compress = true;
|
|
317 }
|
|
318 }
|