/src/dropbear/src/packet.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Dropbear - a SSH2 server |
3 | | * |
4 | | * Copyright (c) 2002,2003 Matt Johnston |
5 | | * All rights reserved. |
6 | | * |
7 | | * Permission is hereby granted, free of charge, to any person obtaining a copy |
8 | | * of this software and associated documentation files (the "Software"), to deal |
9 | | * in the Software without restriction, including without limitation the rights |
10 | | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
11 | | * copies of the Software, and to permit persons to whom the Software is |
12 | | * furnished to do so, subject to the following conditions: |
13 | | * |
14 | | * The above copyright notice and this permission notice shall be included in |
15 | | * all copies or substantial portions of the Software. |
16 | | * |
17 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
18 | | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
19 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
20 | | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
21 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
22 | | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
23 | | * SOFTWARE. */ |
24 | | |
25 | | #include "includes.h" |
26 | | #include "packet.h" |
27 | | #include "session.h" |
28 | | #include "dbutil.h" |
29 | | #include "ssh.h" |
30 | | #include "algo.h" |
31 | | #include "buffer.h" |
32 | | #include "kex.h" |
33 | | #include "dbrandom.h" |
34 | | #include "service.h" |
35 | | #include "auth.h" |
36 | | #include "channel.h" |
37 | | #include "netio.h" |
38 | | #include "runopts.h" |
39 | | |
40 | | static int read_packet_init(void); |
41 | | static void make_mac(unsigned int seqno, const struct key_context_directional * key_state, |
42 | | buffer * clear_buf, unsigned int clear_len, |
43 | | unsigned char *output_mac); |
44 | | static int checkmac(void); |
45 | | |
46 | | /* For exact details see http://www.zlib.net/zlib_tech.html |
47 | | * 5 bytes per 16kB block, plus 6 bytes for the stream. |
48 | | * We might allocate 5 unnecessary bytes here if it's an |
49 | | * exact multiple. */ |
50 | 0 | #define ZLIB_COMPRESS_EXPANSION (((RECV_MAX_PAYLOAD_LEN/16384)+1)*5 + 6) |
51 | | #define ZLIB_DECOMPRESS_INCR 1024 |
52 | | #ifndef DISABLE_ZLIB |
53 | | static buffer* buf_decompress(const buffer* buf, unsigned int len); |
54 | | static void buf_compress(buffer * dest, buffer * src, unsigned int len); |
55 | | #endif |
56 | | |
57 | | /* non-blocking function writing out a current encrypted packet */ |
58 | 0 | void write_packet() { |
59 | |
|
60 | 0 | ssize_t written; |
61 | 0 | #if defined(HAVE_WRITEV) && (defined(IOV_MAX) || defined(UIO_MAXIOV)) |
62 | | /* 50 is somewhat arbitrary */ |
63 | 0 | unsigned int iov_count = 50; |
64 | 0 | struct iovec iov[50]; |
65 | | #else |
66 | | int len; |
67 | | buffer* writebuf; |
68 | | #endif |
69 | | |
70 | 0 | TRACE2(("enter write_packet")) |
71 | 0 | dropbear_assert(!isempty(&ses.writequeue)); |
72 | | |
73 | 0 | #if defined(HAVE_WRITEV) && (defined(IOV_MAX) || defined(UIO_MAXIOV)) |
74 | | |
75 | 0 | packet_queue_to_iovec(&ses.writequeue, iov, &iov_count); |
76 | | /* This may return EAGAIN. The main loop sometimes |
77 | | calls write_packet() without bothering to test with select() since |
78 | | it's likely to be necessary */ |
79 | 0 | #if DROPBEAR_FUZZ |
80 | 0 | if (fuzz.fuzzing) { |
81 | | /* pretend to write one packet at a time */ |
82 | | /* TODO(fuzz): randomise amount written based on the fuzz input */ |
83 | 0 | written = iov[0].iov_len; |
84 | 0 | } |
85 | 0 | else |
86 | 0 | #endif |
87 | 0 | { |
88 | 0 | written = writev(ses.sock_out, iov, iov_count); |
89 | 0 | if (written < 0) { |
90 | 0 | if (errno == EINTR || errno == EAGAIN) { |
91 | 0 | TRACE2(("leave write_packet: EINTR")) |
92 | 0 | return; |
93 | 0 | } else { |
94 | 0 | dropbear_exit("Error writing: %s", strerror(errno)); |
95 | 0 | } |
96 | 0 | } |
97 | 0 | } |
98 | | |
99 | 0 | packet_queue_consume(&ses.writequeue, written); |
100 | 0 | ses.writequeue_len -= written; |
101 | |
|
102 | 0 | if (written == 0) { |
103 | 0 | ses.remoteclosed(); |
104 | 0 | } |
105 | |
|
106 | | #else /* No writev () */ |
107 | | #if DROPBEAR_FUZZ |
108 | | _Static_assert(0, "No fuzzing code for no-writev writes"); |
109 | | #endif |
110 | | /* Get the next buffer in the queue of encrypted packets to write*/ |
111 | | writebuf = (buffer*)examine(&ses.writequeue); |
112 | | |
113 | | len = writebuf->len - writebuf->pos; |
114 | | dropbear_assert(len > 0); |
115 | | /* Try to write as much as possible */ |
116 | | written = write(ses.sock_out, buf_getptr(writebuf, len), len); |
117 | | |
118 | | if (written < 0) { |
119 | | if (errno == EINTR || errno == EAGAIN) { |
120 | | TRACE2(("leave writepacket: EINTR")) |
121 | | return; |
122 | | } else { |
123 | | dropbear_exit("Error writing: %s", strerror(errno)); |
124 | | } |
125 | | } |
126 | | |
127 | | if (written == 0) { |
128 | | ses.remoteclosed(); |
129 | | } |
130 | | |
131 | | ses.writequeue_len -= written; |
132 | | |
133 | | if (written == len) { |
134 | | /* We've finished with the packet, free it */ |
135 | | dequeue(&ses.writequeue); |
136 | | buf_free(writebuf); |
137 | | writebuf = NULL; |
138 | | } else { |
139 | | /* More packet left to write, leave it in the queue for later */ |
140 | | buf_incrpos(writebuf, written); |
141 | | } |
142 | | #endif /* writev */ |
143 | |
|
144 | 0 | TRACE2(("leave write_packet")) |
145 | 0 | } |
146 | | |
147 | | /* Non-blocking function reading available portion of a packet into the |
148 | | * ses's buffer, decrypting the length if encrypted, decrypting the |
149 | | * full portion if possible */ |
150 | 0 | void read_packet() { |
151 | |
|
152 | 0 | int len; |
153 | 0 | unsigned int maxlen; |
154 | 0 | unsigned char blocksize; |
155 | |
|
156 | 0 | TRACE2(("enter read_packet")) |
157 | 0 | blocksize = ses.keys->recv.algo_crypt->blocksize; |
158 | | |
159 | 0 | if (ses.readbuf == NULL || ses.readbuf->len < blocksize) { |
160 | 0 | int ret; |
161 | | /* In the first blocksize of a packet */ |
162 | | |
163 | | /* Read the first blocksize of the packet, so we can decrypt it and |
164 | | * find the length of the whole packet */ |
165 | 0 | ret = read_packet_init(); |
166 | |
|
167 | 0 | if (ret == DROPBEAR_FAILURE) { |
168 | | /* didn't read enough to determine the length */ |
169 | 0 | TRACE2(("leave read_packet: packetinit done")) |
170 | 0 | return; |
171 | 0 | } |
172 | 0 | } |
173 | | |
174 | | /* Attempt to read the remainder of the packet, note that there |
175 | | * mightn't be any available (EAGAIN) */ |
176 | 0 | maxlen = ses.readbuf->len - ses.readbuf->pos; |
177 | 0 | if (maxlen == 0) { |
178 | | /* Occurs when the packet is only a single block long and has all |
179 | | * been read in read_packet_init(). Usually means that MAC is disabled |
180 | | */ |
181 | 0 | len = 0; |
182 | 0 | } else { |
183 | 0 | len = read(ses.sock_in, buf_getptr(ses.readbuf, maxlen), maxlen); |
184 | |
|
185 | 0 | if (len == 0) { |
186 | 0 | ses.remoteclosed(); |
187 | 0 | } |
188 | |
|
189 | 0 | if (len < 0) { |
190 | 0 | if (errno == EINTR || errno == EAGAIN) { |
191 | 0 | TRACE2(("leave read_packet: EINTR or EAGAIN")) |
192 | 0 | return; |
193 | 0 | } else { |
194 | 0 | dropbear_exit("Error reading: %s", strerror(errno)); |
195 | 0 | } |
196 | 0 | } |
197 | | |
198 | 0 | buf_incrpos(ses.readbuf, len); |
199 | 0 | } |
200 | | |
201 | 0 | if ((unsigned int)len == maxlen) { |
202 | | /* The whole packet has been read */ |
203 | 0 | decrypt_packet(); |
204 | | /* The main select() loop process_packet() to |
205 | | * handle the packet contents... */ |
206 | 0 | } |
207 | 0 | TRACE2(("leave read_packet")) |
208 | 0 | } |
209 | | |
210 | | /* Function used to read the initial portion of a packet, and determine the |
211 | | * length. Only called during the first BLOCKSIZE of a packet. */ |
212 | | /* Returns DROPBEAR_SUCCESS if the length is determined, |
213 | | * DROPBEAR_FAILURE otherwise */ |
214 | 0 | static int read_packet_init() { |
215 | |
|
216 | 0 | unsigned int maxlen; |
217 | 0 | int slen; |
218 | 0 | unsigned int len, plen; |
219 | 0 | unsigned int blocksize; |
220 | 0 | unsigned int macsize; |
221 | | |
222 | |
|
223 | 0 | blocksize = ses.keys->recv.algo_crypt->blocksize; |
224 | 0 | macsize = ses.keys->recv.algo_mac->hashsize; |
225 | |
|
226 | 0 | if (ses.readbuf == NULL) { |
227 | | /* start of a new packet */ |
228 | 0 | ses.readbuf = buf_new(INIT_READBUF); |
229 | 0 | } |
230 | |
|
231 | 0 | maxlen = blocksize - ses.readbuf->pos; |
232 | | |
233 | | /* read the rest of the packet if possible */ |
234 | 0 | slen = read(ses.sock_in, buf_getwriteptr(ses.readbuf, maxlen), |
235 | 0 | maxlen); |
236 | 0 | if (slen == 0) { |
237 | 0 | ses.remoteclosed(); |
238 | 0 | } |
239 | 0 | if (slen < 0) { |
240 | 0 | if (errno == EINTR || errno == EAGAIN) { |
241 | 0 | TRACE2(("leave read_packet_init: EINTR")) |
242 | 0 | return DROPBEAR_FAILURE; |
243 | 0 | } |
244 | 0 | dropbear_exit("Error reading: %s", strerror(errno)); |
245 | 0 | } |
246 | | |
247 | 0 | buf_incrwritepos(ses.readbuf, slen); |
248 | |
|
249 | 0 | if ((unsigned int)slen != maxlen) { |
250 | | /* don't have enough bytes to determine length, get next time */ |
251 | 0 | return DROPBEAR_FAILURE; |
252 | 0 | } |
253 | | |
254 | | /* now we have the first block, need to get packet length, so we decrypt |
255 | | * the first block (only need first 4 bytes) */ |
256 | 0 | buf_setpos(ses.readbuf, 0); |
257 | 0 | #if DROPBEAR_AEAD_MODE |
258 | 0 | if (ses.keys->recv.crypt_mode->aead_crypt) { |
259 | 0 | if (ses.keys->recv.crypt_mode->aead_getlength(ses.recvseq, |
260 | 0 | buf_getptr(ses.readbuf, blocksize), &plen, |
261 | 0 | blocksize, |
262 | 0 | &ses.keys->recv.cipher_state) != CRYPT_OK) { |
263 | 0 | dropbear_exit("Error decrypting"); |
264 | 0 | } |
265 | 0 | len = plen + 4 + macsize; |
266 | 0 | } else |
267 | 0 | #endif |
268 | 0 | { |
269 | 0 | if (ses.keys->recv.crypt_mode->decrypt(buf_getptr(ses.readbuf, blocksize), |
270 | 0 | buf_getwriteptr(ses.readbuf, blocksize), |
271 | 0 | blocksize, |
272 | 0 | &ses.keys->recv.cipher_state) != CRYPT_OK) { |
273 | 0 | dropbear_exit("Error decrypting"); |
274 | 0 | } |
275 | 0 | plen = buf_getint(ses.readbuf) + 4; |
276 | 0 | len = plen + macsize; |
277 | 0 | } |
278 | | |
279 | 0 | TRACE2(("packet size is %u, block %u mac %u", len, blocksize, macsize)) |
280 | | |
281 | | |
282 | | /* check packet length */ |
283 | 0 | if ((len > RECV_MAX_PACKET_LEN) || |
284 | 0 | (plen < blocksize) || |
285 | 0 | (plen % blocksize != 0)) { |
286 | 0 | dropbear_exit("Integrity error (bad packet size %u)", len); |
287 | 0 | } |
288 | | |
289 | 0 | if (len > ses.readbuf->size) { |
290 | 0 | ses.readbuf = buf_resize(ses.readbuf, len); |
291 | 0 | } |
292 | 0 | buf_setlen(ses.readbuf, len); |
293 | 0 | buf_setpos(ses.readbuf, blocksize); |
294 | 0 | return DROPBEAR_SUCCESS; |
295 | 0 | } |
296 | | |
297 | | /* handle the received packet */ |
298 | 0 | void decrypt_packet() { |
299 | |
|
300 | 0 | unsigned char blocksize; |
301 | 0 | unsigned char macsize; |
302 | 0 | unsigned int padlen; |
303 | 0 | unsigned int len; |
304 | |
|
305 | 0 | TRACE2(("enter decrypt_packet")) |
306 | 0 | blocksize = ses.keys->recv.algo_crypt->blocksize; |
307 | 0 | macsize = ses.keys->recv.algo_mac->hashsize; |
308 | |
|
309 | 0 | ses.kexstate.datarecv += ses.readbuf->len; |
310 | |
|
311 | 0 | #if DROPBEAR_AEAD_MODE |
312 | 0 | if (ses.keys->recv.crypt_mode->aead_crypt) { |
313 | | /* first blocksize is not decrypted yet */ |
314 | 0 | buf_setpos(ses.readbuf, 0); |
315 | | |
316 | | /* decrypt it in-place */ |
317 | 0 | len = ses.readbuf->len - macsize - ses.readbuf->pos; |
318 | 0 | if (ses.keys->recv.crypt_mode->aead_crypt(ses.recvseq, |
319 | 0 | buf_getptr(ses.readbuf, len + macsize), |
320 | 0 | buf_getwriteptr(ses.readbuf, len), |
321 | 0 | len, macsize, |
322 | 0 | &ses.keys->recv.cipher_state, LTC_DECRYPT) != CRYPT_OK) { |
323 | 0 | dropbear_exit("Error decrypting"); |
324 | 0 | } |
325 | 0 | buf_incrpos(ses.readbuf, len); |
326 | 0 | } else |
327 | 0 | #endif |
328 | 0 | { |
329 | | /* we've already decrypted the first blocksize in read_packet_init */ |
330 | 0 | buf_setpos(ses.readbuf, blocksize); |
331 | | |
332 | | /* decrypt it in-place */ |
333 | 0 | len = ses.readbuf->len - macsize - ses.readbuf->pos; |
334 | 0 | if (ses.keys->recv.crypt_mode->decrypt( |
335 | 0 | buf_getptr(ses.readbuf, len), |
336 | 0 | buf_getwriteptr(ses.readbuf, len), |
337 | 0 | len, |
338 | 0 | &ses.keys->recv.cipher_state) != CRYPT_OK) { |
339 | 0 | dropbear_exit("Error decrypting"); |
340 | 0 | } |
341 | 0 | buf_incrpos(ses.readbuf, len); |
342 | | |
343 | | /* check the hmac */ |
344 | 0 | if (checkmac() != DROPBEAR_SUCCESS) { |
345 | 0 | dropbear_exit("Integrity error"); |
346 | 0 | } |
347 | |
|
348 | 0 | } |
349 | | |
350 | 0 | #if DROPBEAR_FUZZ |
351 | 0 | fuzz_dump(ses.readbuf->data, ses.readbuf->len); |
352 | 0 | #endif |
353 | | |
354 | | /* get padding length */ |
355 | 0 | buf_setpos(ses.readbuf, PACKET_PADDING_OFF); |
356 | 0 | padlen = buf_getbyte(ses.readbuf); |
357 | | |
358 | | /* payload length */ |
359 | | /* - 4 - 1 is for LEN and PADLEN values */ |
360 | 0 | len = ses.readbuf->len - padlen - 4 - 1 - macsize; |
361 | 0 | if ((len > RECV_MAX_PAYLOAD_LEN+ZLIB_COMPRESS_EXPANSION) || (len < 1)) { |
362 | 0 | dropbear_exit("Bad packet size %u", len); |
363 | 0 | } |
364 | | |
365 | 0 | buf_setpos(ses.readbuf, PACKET_PAYLOAD_OFF); |
366 | |
|
367 | | #ifndef DISABLE_ZLIB |
368 | | if (is_compress_recv()) { |
369 | | /* decompress */ |
370 | | ses.payload = buf_decompress(ses.readbuf, len); |
371 | | buf_setpos(ses.payload, 0); |
372 | | ses.payload_beginning = 0; |
373 | | buf_free(ses.readbuf); |
374 | | } else |
375 | | #endif |
376 | 0 | { |
377 | 0 | ses.payload = ses.readbuf; |
378 | 0 | ses.payload_beginning = ses.payload->pos; |
379 | 0 | buf_setlen(ses.payload, ses.payload->pos + len); |
380 | 0 | } |
381 | 0 | ses.readbuf = NULL; |
382 | |
|
383 | 0 | ses.recvseq++; |
384 | |
|
385 | 0 | TRACE2(("leave decrypt_packet")) |
386 | 0 | } |
387 | | |
388 | | /* Checks the mac at the end of a decrypted readbuf. |
389 | | * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ |
390 | 0 | static int checkmac() { |
391 | |
|
392 | 0 | unsigned char mac_bytes[MAX_MAC_LEN]; |
393 | 0 | unsigned int mac_size, contents_len; |
394 | | |
395 | 0 | mac_size = ses.keys->recv.algo_mac->hashsize; |
396 | 0 | contents_len = ses.readbuf->len - mac_size; |
397 | |
|
398 | 0 | buf_setpos(ses.readbuf, 0); |
399 | 0 | make_mac(ses.recvseq, &ses.keys->recv, ses.readbuf, contents_len, mac_bytes); |
400 | |
|
401 | 0 | #if DROPBEAR_FUZZ |
402 | 0 | if (fuzz.fuzzing) { |
403 | | /* fail 1 in 2000 times to test error path. */ |
404 | 0 | unsigned int value = 0; |
405 | 0 | if (mac_size > sizeof(value)) { |
406 | 0 | memcpy(&value, mac_bytes, sizeof(value)); |
407 | 0 | } |
408 | 0 | if (value % 2000 == 99) { |
409 | 0 | return DROPBEAR_FAILURE; |
410 | 0 | } |
411 | 0 | return DROPBEAR_SUCCESS; |
412 | 0 | } |
413 | 0 | #endif |
414 | | |
415 | | /* compare the hash */ |
416 | 0 | buf_setpos(ses.readbuf, contents_len); |
417 | 0 | if (constant_time_memcmp(mac_bytes, buf_getptr(ses.readbuf, mac_size), mac_size) != 0) { |
418 | 0 | return DROPBEAR_FAILURE; |
419 | 0 | } else { |
420 | 0 | return DROPBEAR_SUCCESS; |
421 | 0 | } |
422 | 0 | } |
423 | | |
424 | | #ifndef DISABLE_ZLIB |
425 | | /* returns a pointer to a newly created buffer */ |
426 | | static buffer* buf_decompress(const buffer* buf, unsigned int len) { |
427 | | |
428 | | int result; |
429 | | buffer * ret; |
430 | | z_streamp zstream; |
431 | | |
432 | | zstream = ses.keys->recv.zstream; |
433 | | /* We use RECV_MAX_PAYLOAD_LEN+1 here to ensure that |
434 | | we can detect an oversized payload after inflate() */ |
435 | | ret = buf_new(RECV_MAX_PAYLOAD_LEN+1); |
436 | | |
437 | | zstream->avail_in = len; |
438 | | zstream->next_in = buf_getptr(buf, len); |
439 | | zstream->avail_out = ret->size; |
440 | | zstream->next_out = ret->data; |
441 | | |
442 | | result = inflate(zstream, Z_SYNC_FLUSH); |
443 | | if (result != Z_OK) { |
444 | | dropbear_exit("zlib error"); |
445 | | } |
446 | | |
447 | | buf_setlen(ret, ret->size - zstream->avail_out); |
448 | | |
449 | | if (zstream->avail_in > 0 || ret->len > RECV_MAX_PAYLOAD_LEN) { |
450 | | /* The remote side sent larger than a payload size |
451 | | * of uncompressed data. |
452 | | */ |
453 | | dropbear_exit("bad packet, oversized decompressed"); |
454 | | } |
455 | | |
456 | | /* Success. All input was consumed and avail_out > 0 */ |
457 | | return ret; |
458 | | |
459 | | } |
460 | | #endif |
461 | | |
462 | | |
463 | | /* returns 1 if the packet is a valid type during kex (see 7.1 of rfc4253) */ |
464 | 0 | static int packet_is_okay_kex(unsigned char type) { |
465 | 0 | if (type >= SSH_MSG_USERAUTH_REQUEST) { |
466 | 0 | return 0; |
467 | 0 | } |
468 | 0 | if (type == SSH_MSG_SERVICE_REQUEST || type == SSH_MSG_SERVICE_ACCEPT) { |
469 | 0 | return 0; |
470 | 0 | } |
471 | 0 | if (type == SSH_MSG_KEXINIT) { |
472 | | /* XXX should this die horribly if !dataallowed ?? */ |
473 | 0 | return 0; |
474 | 0 | } |
475 | 0 | return 1; |
476 | 0 | } |
477 | | |
478 | 0 | static void enqueue_reply_packet() { |
479 | 0 | struct packetlist * new_item = NULL; |
480 | 0 | new_item = m_malloc(sizeof(struct packetlist)); |
481 | 0 | new_item->next = NULL; |
482 | | |
483 | 0 | new_item->payload = buf_newcopy(ses.writepayload); |
484 | 0 | buf_setpos(ses.writepayload, 0); |
485 | 0 | buf_setlen(ses.writepayload, 0); |
486 | | |
487 | 0 | if (ses.reply_queue_tail) { |
488 | 0 | ses.reply_queue_tail->next = new_item; |
489 | 0 | } else { |
490 | 0 | ses.reply_queue_head = new_item; |
491 | 0 | } |
492 | 0 | ses.reply_queue_tail = new_item; |
493 | 0 | } |
494 | | |
495 | 0 | void maybe_flush_reply_queue() { |
496 | 0 | struct packetlist *tmp_item = NULL, *curr_item = NULL; |
497 | 0 | if (!ses.dataallowed) |
498 | 0 | { |
499 | 0 | TRACE(("maybe_empty_reply_queue - no data allowed")) |
500 | 0 | return; |
501 | 0 | } |
502 | | |
503 | 0 | for (curr_item = ses.reply_queue_head; curr_item; ) { |
504 | 0 | CHECKCLEARTOWRITE(); |
505 | 0 | buf_putbytes(ses.writepayload, |
506 | 0 | curr_item->payload->data, curr_item->payload->len); |
507 | | |
508 | 0 | buf_free(curr_item->payload); |
509 | 0 | tmp_item = curr_item; |
510 | 0 | curr_item = curr_item->next; |
511 | 0 | m_free(tmp_item); |
512 | 0 | encrypt_packet(); |
513 | 0 | } |
514 | 0 | ses.reply_queue_head = ses.reply_queue_tail = NULL; |
515 | 0 | } |
516 | | |
517 | | /* encrypt the writepayload, putting into writebuf, ready for write_packet() |
518 | | * to put on the wire */ |
519 | 0 | void encrypt_packet() { |
520 | |
|
521 | 0 | unsigned char padlen; |
522 | 0 | unsigned char blocksize, mac_size; |
523 | 0 | buffer * writebuf; /* the packet which will go on the wire. This is |
524 | | encrypted in-place. */ |
525 | 0 | unsigned char packet_type; |
526 | 0 | unsigned int len, encrypt_buf_size; |
527 | 0 | unsigned char mac_bytes[MAX_MAC_LEN]; |
528 | |
|
529 | 0 | time_t now; |
530 | | |
531 | 0 | TRACE2(("enter encrypt_packet()")) |
532 | |
|
533 | 0 | buf_setpos(ses.writepayload, 0); |
534 | 0 | packet_type = buf_getbyte(ses.writepayload); |
535 | 0 | buf_setpos(ses.writepayload, 0); |
536 | |
|
537 | 0 | TRACE2(("encrypt_packet type is %d", packet_type)) |
538 | | |
539 | 0 | if ((!ses.dataallowed && !packet_is_okay_kex(packet_type))) { |
540 | | /* During key exchange only particular packets are allowed. |
541 | | Since this packet_type isn't OK we just enqueue it to send |
542 | | after the KEX, see maybe_flush_reply_queue */ |
543 | 0 | enqueue_reply_packet(); |
544 | 0 | return; |
545 | 0 | } |
546 | | |
547 | 0 | blocksize = ses.keys->trans.algo_crypt->blocksize; |
548 | 0 | mac_size = ses.keys->trans.algo_mac->hashsize; |
549 | | |
550 | | /* Encrypted packet len is payload+5. We need to then make sure |
551 | | * there is enough space for padding or MIN_PACKET_LEN. |
552 | | * Add extra 3 since we need at least 4 bytes of padding */ |
553 | 0 | encrypt_buf_size = (ses.writepayload->len+4+1) |
554 | 0 | + MAX(MIN_PACKET_LEN, blocksize) + 3 |
555 | | /* add space for the MAC at the end */ |
556 | 0 | + mac_size |
557 | | #ifndef DISABLE_ZLIB |
558 | | /* some extra in case 'compression' makes it larger */ |
559 | | + ZLIB_COMPRESS_EXPANSION |
560 | | #endif |
561 | | /* and an extra cleartext (stripped before transmission) byte for the |
562 | | * packet type */ |
563 | 0 | + 1; |
564 | |
|
565 | 0 | writebuf = buf_new(encrypt_buf_size); |
566 | 0 | buf_setlen(writebuf, PACKET_PAYLOAD_OFF); |
567 | 0 | buf_setpos(writebuf, PACKET_PAYLOAD_OFF); |
568 | |
|
569 | | #ifndef DISABLE_ZLIB |
570 | | /* compression */ |
571 | | if (is_compress_trans()) { |
572 | | buf_compress(writebuf, ses.writepayload, ses.writepayload->len); |
573 | | } else |
574 | | #endif |
575 | 0 | { |
576 | 0 | memcpy(buf_getwriteptr(writebuf, ses.writepayload->len), |
577 | 0 | buf_getptr(ses.writepayload, ses.writepayload->len), |
578 | 0 | ses.writepayload->len); |
579 | 0 | buf_incrwritepos(writebuf, ses.writepayload->len); |
580 | 0 | } |
581 | | |
582 | | /* finished with payload */ |
583 | 0 | buf_setpos(ses.writepayload, 0); |
584 | 0 | buf_setlen(ses.writepayload, 0); |
585 | | |
586 | | /* length of padding - packet length excluding the packetlength uint32 |
587 | | * field in aead mode must be a multiple of blocksize, with a minimum of |
588 | | * 4 bytes of padding */ |
589 | 0 | len = writebuf->len; |
590 | 0 | #if DROPBEAR_AEAD_MODE |
591 | 0 | if (ses.keys->trans.crypt_mode->aead_crypt) { |
592 | 0 | len -= 4; |
593 | 0 | } |
594 | 0 | #endif |
595 | 0 | padlen = blocksize - len % blocksize; |
596 | 0 | if (padlen < 4) { |
597 | 0 | padlen += blocksize; |
598 | 0 | } |
599 | | /* check for min packet length */ |
600 | 0 | if (writebuf->len + padlen < MIN_PACKET_LEN) { |
601 | 0 | padlen += blocksize; |
602 | 0 | } |
603 | |
|
604 | 0 | buf_setpos(writebuf, 0); |
605 | | /* packet length excluding the packetlength uint32 */ |
606 | 0 | buf_putint(writebuf, writebuf->len + padlen - 4); |
607 | | |
608 | | /* padding len */ |
609 | 0 | buf_putbyte(writebuf, padlen); |
610 | | /* actual padding */ |
611 | 0 | buf_setpos(writebuf, writebuf->len); |
612 | 0 | buf_incrlen(writebuf, padlen); |
613 | 0 | genrandom(buf_getptr(writebuf, padlen), padlen); |
614 | |
|
615 | 0 | #if DROPBEAR_AEAD_MODE |
616 | 0 | if (ses.keys->trans.crypt_mode->aead_crypt) { |
617 | | /* do the actual encryption, in-place */ |
618 | 0 | buf_setpos(writebuf, 0); |
619 | | /* encrypt it in-place*/ |
620 | 0 | len = writebuf->len; |
621 | 0 | buf_incrlen(writebuf, mac_size); |
622 | 0 | if (ses.keys->trans.crypt_mode->aead_crypt(ses.transseq, |
623 | 0 | buf_getptr(writebuf, len), |
624 | 0 | buf_getwriteptr(writebuf, len + mac_size), |
625 | 0 | len, mac_size, |
626 | 0 | &ses.keys->trans.cipher_state, LTC_ENCRYPT) != CRYPT_OK) { |
627 | 0 | dropbear_exit("Error encrypting"); |
628 | 0 | } |
629 | 0 | buf_incrpos(writebuf, len + mac_size); |
630 | 0 | } else |
631 | 0 | #endif |
632 | 0 | { |
633 | 0 | make_mac(ses.transseq, &ses.keys->trans, writebuf, writebuf->len, mac_bytes); |
634 | | |
635 | | /* do the actual encryption, in-place */ |
636 | 0 | buf_setpos(writebuf, 0); |
637 | | /* encrypt it in-place*/ |
638 | 0 | len = writebuf->len; |
639 | 0 | if (ses.keys->trans.crypt_mode->encrypt( |
640 | 0 | buf_getptr(writebuf, len), |
641 | 0 | buf_getwriteptr(writebuf, len), |
642 | 0 | len, |
643 | 0 | &ses.keys->trans.cipher_state) != CRYPT_OK) { |
644 | 0 | dropbear_exit("Error encrypting"); |
645 | 0 | } |
646 | 0 | buf_incrpos(writebuf, len); |
647 | | |
648 | | /* stick the MAC on it */ |
649 | 0 | buf_putbytes(writebuf, mac_bytes, mac_size); |
650 | 0 | } |
651 | | |
652 | | /* Update counts */ |
653 | 0 | ses.kexstate.datatrans += writebuf->len; |
654 | |
|
655 | 0 | writebuf_enqueue(writebuf); |
656 | | |
657 | | /* Update counts */ |
658 | 0 | ses.transseq++; |
659 | |
|
660 | 0 | now = monotonic_now(); |
661 | 0 | ses.last_packet_time_any_sent = now; |
662 | | /* idle timeout shouldn't be affected by responses to keepalives. |
663 | | send_msg_keepalive() itself also does tricks with |
664 | | ses.last_packet_idle_time - read that if modifying this code */ |
665 | 0 | if (packet_type != SSH_MSG_REQUEST_FAILURE |
666 | 0 | && packet_type != SSH_MSG_UNIMPLEMENTED |
667 | 0 | && packet_type != SSH_MSG_IGNORE) { |
668 | 0 | ses.last_packet_time_idle = now; |
669 | |
|
670 | 0 | } |
671 | |
|
672 | 0 | TRACE2(("leave encrypt_packet()")) |
673 | 0 | } |
674 | | |
675 | 0 | void writebuf_enqueue(buffer * writebuf) { |
676 | | /* enqueue the packet for sending. It will get freed after transmission. */ |
677 | 0 | buf_setpos(writebuf, 0); |
678 | 0 | enqueue(&ses.writequeue, (void*)writebuf); |
679 | 0 | ses.writequeue_len += writebuf->len; |
680 | 0 | } |
681 | | |
682 | | |
683 | | /* Create the packet mac, and append H(seqno|clearbuf) to the output */ |
684 | | /* output_mac must have ses.keys->trans.algo_mac->hashsize bytes. */ |
685 | | static void make_mac(unsigned int seqno, const struct key_context_directional * key_state, |
686 | | buffer * clear_buf, unsigned int clear_len, |
687 | 0 | unsigned char *output_mac) { |
688 | 0 | unsigned char seqbuf[4]; |
689 | 0 | unsigned long bufsize; |
690 | 0 | hmac_state hmac; |
691 | |
|
692 | 0 | if (key_state->algo_mac->hashsize > 0) { |
693 | | /* calculate the mac */ |
694 | 0 | if (hmac_init(&hmac, |
695 | 0 | key_state->hash_index, |
696 | 0 | key_state->mackey, |
697 | 0 | key_state->algo_mac->keysize) != CRYPT_OK) { |
698 | 0 | dropbear_exit("HMAC error"); |
699 | 0 | } |
700 | | |
701 | | /* sequence number */ |
702 | 0 | STORE32H(seqno, seqbuf); |
703 | 0 | if (hmac_process(&hmac, seqbuf, 4) != CRYPT_OK) { |
704 | 0 | dropbear_exit("HMAC error"); |
705 | 0 | } |
706 | | |
707 | | /* the actual contents */ |
708 | 0 | buf_setpos(clear_buf, 0); |
709 | 0 | if (hmac_process(&hmac, |
710 | 0 | buf_getptr(clear_buf, clear_len), |
711 | 0 | clear_len) != CRYPT_OK) { |
712 | 0 | dropbear_exit("HMAC error"); |
713 | 0 | } |
714 | | |
715 | 0 | bufsize = MAX_MAC_LEN; |
716 | 0 | if (hmac_done(&hmac, output_mac, &bufsize) != CRYPT_OK) { |
717 | 0 | dropbear_exit("HMAC error"); |
718 | 0 | } |
719 | 0 | } |
720 | 0 | TRACE2(("leave writemac")) |
721 | 0 | } |
722 | | |
723 | | #ifndef DISABLE_ZLIB |
724 | | /* compresses len bytes from src, outputting to dest (starting from the |
725 | | * respective current positions. dest must have sufficient space, |
726 | | * len+ZLIB_COMPRESS_EXPANSION */ |
727 | | static void buf_compress(buffer * dest, buffer * src, unsigned int len) { |
728 | | |
729 | | unsigned int endpos = src->pos + len; |
730 | | int result; |
731 | | |
732 | | TRACE2(("enter buf_compress")) |
733 | | |
734 | | dropbear_assert(dest->size - dest->pos >= len+ZLIB_COMPRESS_EXPANSION); |
735 | | |
736 | | ses.keys->trans.zstream->avail_in = endpos - src->pos; |
737 | | ses.keys->trans.zstream->next_in = |
738 | | buf_getptr(src, ses.keys->trans.zstream->avail_in); |
739 | | |
740 | | ses.keys->trans.zstream->avail_out = dest->size - dest->pos; |
741 | | ses.keys->trans.zstream->next_out = |
742 | | buf_getwriteptr(dest, ses.keys->trans.zstream->avail_out); |
743 | | |
744 | | result = deflate(ses.keys->trans.zstream, Z_SYNC_FLUSH); |
745 | | |
746 | | buf_setpos(src, endpos - ses.keys->trans.zstream->avail_in); |
747 | | buf_setlen(dest, dest->size - ses.keys->trans.zstream->avail_out); |
748 | | buf_setpos(dest, dest->len); |
749 | | |
750 | | if (result != Z_OK) { |
751 | | dropbear_exit("zlib error"); |
752 | | } |
753 | | |
754 | | /* fails if destination buffer wasn't large enough */ |
755 | | dropbear_assert(ses.keys->trans.zstream->avail_in == 0); |
756 | | TRACE2(("leave buf_compress")) |
757 | | } |
758 | | #endif |