1 /*
2  * Binary packet protocol for SSH-1.
3  */
4 
5 #include <assert.h>
6 
7 #include "putty.h"
8 #include "ssh.h"
9 #include "sshbpp.h"
10 #include "sshcr.h"
11 
12 struct ssh1_bpp_state {
13     int crState;
14     long len, pad, biglen, length, maxlen;
15     unsigned char *data;
16     uint32_t realcrc, gotcrc;
17     int chunk;
18     PktIn *pktin;
19 
20     ssh_cipher *cipher_in, *cipher_out;
21 
22     struct crcda_ctx *crcda_ctx;
23     uint8_t iv[8];                     /* for crcda */
24 
25     bool pending_compression_request;
26     ssh_compressor *compctx;
27     ssh_decompressor *decompctx;
28 
29     BinaryPacketProtocol bpp;
30 };
31 
32 static void ssh1_bpp_free(BinaryPacketProtocol *bpp);
33 static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp);
34 static void ssh1_bpp_handle_output(BinaryPacketProtocol *bpp);
35 static void ssh1_bpp_queue_disconnect(BinaryPacketProtocol *bpp,
36                                       const char *msg, int category);
37 static PktOut *ssh1_bpp_new_pktout(int type);
38 
39 static const BinaryPacketProtocolVtable ssh1_bpp_vtable = {
40     .free = ssh1_bpp_free,
41     .handle_input = ssh1_bpp_handle_input,
42     .handle_output = ssh1_bpp_handle_output,
43     .new_pktout = ssh1_bpp_new_pktout,
44     .queue_disconnect = ssh1_bpp_queue_disconnect,
45     .packet_size_limit = 0xFFFFFFFF, /* no special limit for this bpp */
46 };
47 
ssh1_bpp_new(LogContext * logctx)48 BinaryPacketProtocol *ssh1_bpp_new(LogContext *logctx)
49 {
50     struct ssh1_bpp_state *s = snew(struct ssh1_bpp_state);
51     memset(s, 0, sizeof(*s));
52     s->bpp.vt = &ssh1_bpp_vtable;
53     s->bpp.logctx = logctx;
54     ssh_bpp_common_setup(&s->bpp);
55     return &s->bpp;
56 }
57 
ssh1_bpp_free(BinaryPacketProtocol * bpp)58 static void ssh1_bpp_free(BinaryPacketProtocol *bpp)
59 {
60     struct ssh1_bpp_state *s = container_of(bpp, struct ssh1_bpp_state, bpp);
61     if (s->cipher_in)
62         ssh_cipher_free(s->cipher_in);
63     if (s->cipher_out)
64         ssh_cipher_free(s->cipher_out);
65     if (s->compctx)
66         ssh_compressor_free(s->compctx);
67     if (s->decompctx)
68         ssh_decompressor_free(s->decompctx);
69     if (s->crcda_ctx)
70         crcda_free_context(s->crcda_ctx);
71     sfree(s->pktin);
72     sfree(s);
73 }
74 
ssh1_bpp_new_cipher(BinaryPacketProtocol * bpp,const ssh_cipheralg * cipher,const void * session_key)75 void ssh1_bpp_new_cipher(BinaryPacketProtocol *bpp,
76                          const ssh_cipheralg *cipher,
77                          const void *session_key)
78 {
79     struct ssh1_bpp_state *s;
80     assert(bpp->vt == &ssh1_bpp_vtable);
81     s = container_of(bpp, struct ssh1_bpp_state, bpp);
82 
83     assert(!s->cipher_in);
84     assert(!s->cipher_out);
85 
86     if (cipher) {
87         s->cipher_in = ssh_cipher_new(cipher);
88         s->cipher_out = ssh_cipher_new(cipher);
89         ssh_cipher_setkey(s->cipher_in, session_key);
90         ssh_cipher_setkey(s->cipher_out, session_key);
91 
92         assert(!s->crcda_ctx);
93         s->crcda_ctx = crcda_make_context();
94 
95         bpp_logevent("Initialised %s encryption", cipher->text_name);
96 
97         memset(s->iv, 0, sizeof(s->iv));
98 
99         assert(cipher->blksize <= sizeof(s->iv));
100         ssh_cipher_setiv(s->cipher_in, s->iv);
101         ssh_cipher_setiv(s->cipher_out, s->iv);
102     }
103 }
104 
ssh1_bpp_start_compression(BinaryPacketProtocol * bpp)105 void ssh1_bpp_start_compression(BinaryPacketProtocol *bpp)
106 {
107     struct ssh1_bpp_state *s;
108     assert(bpp->vt == &ssh1_bpp_vtable);
109     s = container_of(bpp, struct ssh1_bpp_state, bpp);
110 
111     assert(!s->compctx);
112     assert(!s->decompctx);
113 
114     s->compctx = ssh_compressor_new(&ssh_zlib);
115     s->decompctx = ssh_decompressor_new(&ssh_zlib);
116 
117     bpp_logevent("Started zlib (RFC1950) compression");
118 }
119 
120 #define BPP_READ(ptr, len) do                                           \
121     {                                                                   \
122         bool success;                                                   \
123         crMaybeWaitUntilV((success = bufchain_try_fetch_consume(        \
124                                s->bpp.in_raw, ptr, len)) ||             \
125                           s->bpp.input_eof);                            \
126         if (!success)                                                   \
127             goto eof;                                                   \
128         ssh_check_frozen(s->bpp.ssh);                                   \
129     } while (0)
130 
ssh1_bpp_handle_input(BinaryPacketProtocol * bpp)131 static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp)
132 {
133     struct ssh1_bpp_state *s = container_of(bpp, struct ssh1_bpp_state, bpp);
134 
135     crBegin(s->crState);
136 
137     while (1) {
138         s->maxlen = 0;
139         s->length = 0;
140 
141         {
142             unsigned char lenbuf[4];
143             BPP_READ(lenbuf, 4);
144             s->len = toint(GET_32BIT_MSB_FIRST(lenbuf));
145         }
146 
147         if (s->len < 5 || s->len > 262144) { /* SSH1.5-mandated max size */
148             ssh_sw_abort(s->bpp.ssh,
149                          "Out-of-range packet length from remote suggests"
150                          " data stream corruption");
151             crStopV;
152         }
153 
154         s->pad = 8 - (s->len % 8);
155         s->biglen = s->len + s->pad;
156         s->length = s->len - 5;
157 
158         /*
159          * Allocate the packet to return, now we know its length.
160          */
161         s->pktin = snew_plus(PktIn, s->biglen);
162         s->pktin->qnode.prev = s->pktin->qnode.next = NULL;
163         s->pktin->qnode.on_free_queue = false;
164         s->pktin->type = 0;
165 
166         s->maxlen = s->biglen;
167         s->data = snew_plus_get_aux(s->pktin);
168 
169         BPP_READ(s->data, s->biglen);
170 
171         if (s->cipher_in && detect_attack(s->crcda_ctx,
172                                           s->data, s->biglen, s->iv)) {
173             ssh_sw_abort(s->bpp.ssh,
174                          "Network attack (CRC compensation) detected!");
175             crStopV;
176         }
177         /* Save the last cipher block, to be passed to the next call
178          * to detect_attack */
179         assert(s->biglen >= 8);
180         memcpy(s->iv, s->data + s->biglen - 8, sizeof(s->iv));
181 
182         if (s->cipher_in)
183             ssh_cipher_decrypt(s->cipher_in, s->data, s->biglen);
184 
185         s->realcrc = crc32_ssh1(make_ptrlen(s->data, s->biglen - 4));
186         s->gotcrc = GET_32BIT_MSB_FIRST(s->data + s->biglen - 4);
187         if (s->gotcrc != s->realcrc) {
188             ssh_sw_abort(s->bpp.ssh, "Incorrect CRC received on packet");
189             crStopV;
190         }
191 
192         if (s->decompctx) {
193             unsigned char *decompblk;
194             int decomplen;
195             if (!ssh_decompressor_decompress(
196                     s->decompctx, s->data + s->pad, s->length + 1,
197                     &decompblk, &decomplen)) {
198                 ssh_sw_abort(s->bpp.ssh,
199                              "Zlib decompression encountered invalid data");
200                 crStopV;
201             }
202 
203             if (s->maxlen < s->pad + decomplen) {
204                 PktIn *old_pktin = s->pktin;
205 
206                 s->maxlen = s->pad + decomplen;
207                 s->pktin = snew_plus(PktIn, s->maxlen);
208                 *s->pktin = *old_pktin; /* structure copy */
209                 s->data = snew_plus_get_aux(s->pktin);
210 
211                 smemclr(old_pktin, s->biglen);
212                 sfree(old_pktin);
213             }
214 
215             memcpy(s->data + s->pad, decompblk, decomplen);
216             sfree(decompblk);
217             s->length = decomplen - 1;
218         }
219 
220         /*
221          * Now we can find the bounds of the semantic content of the
222          * packet, and the initial type byte.
223          */
224         s->data += s->pad;
225         s->pktin->type = *s->data++;
226         BinarySource_INIT(s->pktin, s->data, s->length);
227 
228         if (s->bpp.logctx) {
229             logblank_t blanks[MAX_BLANKS];
230             int nblanks = ssh1_censor_packet(
231                 s->bpp.pls, s->pktin->type, false,
232                 make_ptrlen(s->data, s->length), blanks);
233             log_packet(s->bpp.logctx, PKT_INCOMING, s->pktin->type,
234                        ssh1_pkt_type(s->pktin->type),
235                        get_ptr(s->pktin), get_avail(s->pktin), nblanks, blanks,
236                        NULL, 0, NULL);
237         }
238 
239         s->pktin->qnode.formal_size = get_avail(s->pktin);
240         pq_push(&s->bpp.in_pq, s->pktin);
241 
242         {
243             int type = s->pktin->type;
244             s->pktin = NULL;
245 
246             switch (type) {
247               case SSH1_SMSG_SUCCESS:
248               case SSH1_SMSG_FAILURE:
249                 if (s->pending_compression_request) {
250                     /*
251                      * This is the response to
252                      * SSH1_CMSG_REQUEST_COMPRESSION.
253                      */
254                     if (type == SSH1_SMSG_SUCCESS) {
255                         /*
256                          * If the response was positive, start
257                          * compression.
258                          */
259                         ssh1_bpp_start_compression(&s->bpp);
260                     }
261 
262                     /*
263                      * Either way, cancel the pending flag, and
264                      * schedule a run of our output side in case we
265                      * had any packets queued up in the meantime.
266                      */
267                     s->pending_compression_request = false;
268                     queue_idempotent_callback(&s->bpp.ic_out_pq);
269                 }
270                 break;
271             }
272         }
273     }
274 
275   eof:
276     if (!s->bpp.expect_close) {
277         ssh_remote_error(s->bpp.ssh,
278                          "Remote side unexpectedly closed network connection");
279     } else {
280         ssh_remote_eof(s->bpp.ssh, "Remote side closed network connection");
281     }
282     return;  /* avoid touching s now it's been freed */
283 
284     crFinishV;
285 }
286 
ssh1_bpp_new_pktout(int pkt_type)287 static PktOut *ssh1_bpp_new_pktout(int pkt_type)
288 {
289     PktOut *pkt = ssh_new_packet();
290     pkt->length = 4 + 8;            /* space for length + max padding */
291     put_byte(pkt, pkt_type);
292     pkt->prefix = pkt->length;
293     pkt->type = pkt_type;
294     pkt->downstream_id = 0;
295     pkt->additional_log_text = NULL;
296     return pkt;
297 }
298 
ssh1_bpp_format_packet(struct ssh1_bpp_state * s,PktOut * pkt)299 static void ssh1_bpp_format_packet(struct ssh1_bpp_state *s, PktOut *pkt)
300 {
301     int pad, biglen, pktoffs;
302     uint32_t crc;
303     int len;
304 
305     if (s->bpp.logctx) {
306         ptrlen pktdata = make_ptrlen(pkt->data + pkt->prefix,
307                                      pkt->length - pkt->prefix);
308         logblank_t blanks[MAX_BLANKS];
309         int nblanks = ssh1_censor_packet(
310             s->bpp.pls, pkt->type, true, pktdata, blanks);
311         log_packet(s->bpp.logctx, PKT_OUTGOING, pkt->type,
312                    ssh1_pkt_type(pkt->type),
313                    pktdata.ptr, pktdata.len, nblanks, blanks,
314                    NULL, 0, NULL);
315     }
316 
317     if (s->compctx) {
318         unsigned char *compblk;
319         int complen;
320         ssh_compressor_compress(s->compctx, pkt->data + 12, pkt->length - 12,
321                                 &compblk, &complen, 0);
322         /* Replace the uncompressed packet data with the compressed
323          * version. */
324         pkt->length = 12;
325         put_data(pkt, compblk, complen);
326         sfree(compblk);
327     }
328 
329     put_uint32(pkt, 0); /* space for CRC */
330     len = pkt->length - 4 - 8;  /* len(type+data+CRC) */
331     pad = 8 - (len % 8);
332     pktoffs = 8 - pad;
333     biglen = len + pad;         /* len(padding+type+data+CRC) */
334 
335     random_read(pkt->data + pktoffs, 4+8 - pktoffs);
336     crc = crc32_ssh1(
337         make_ptrlen(pkt->data + pktoffs + 4, biglen - 4)); /* all ex len */
338     PUT_32BIT_MSB_FIRST(pkt->data + pktoffs + 4 + biglen - 4, crc);
339     PUT_32BIT_MSB_FIRST(pkt->data + pktoffs, len);
340 
341     if (s->cipher_out)
342         ssh_cipher_encrypt(s->cipher_out, pkt->data + pktoffs + 4, biglen);
343 
344     bufchain_add(s->bpp.out_raw, pkt->data + pktoffs,
345                  biglen + 4); /* len(length+padding+type+data+CRC) */
346 }
347 
ssh1_bpp_handle_output(BinaryPacketProtocol * bpp)348 static void ssh1_bpp_handle_output(BinaryPacketProtocol *bpp)
349 {
350     struct ssh1_bpp_state *s = container_of(bpp, struct ssh1_bpp_state, bpp);
351     PktOut *pkt;
352 
353     if (s->pending_compression_request) {
354         /*
355          * Don't send any output packets while we're awaiting a
356          * response to SSH1_CMSG_REQUEST_COMPRESSION, because if they
357          * cross over in transit with the responding SSH1_CMSG_SUCCESS
358          * then the other end could decode them with the wrong
359          * compression settings.
360          */
361         return;
362     }
363 
364     while ((pkt = pq_pop(&s->bpp.out_pq)) != NULL) {
365         int type = pkt->type;
366         ssh1_bpp_format_packet(s, pkt);
367         ssh_free_pktout(pkt);
368 
369         if (type == SSH1_CMSG_REQUEST_COMPRESSION) {
370             /*
371              * When we see the actual compression request go past, set
372              * the pending flag, and stop processing packets this
373              * time.
374              */
375             s->pending_compression_request = true;
376             break;
377         }
378     }
379 }
380 
ssh1_bpp_queue_disconnect(BinaryPacketProtocol * bpp,const char * msg,int category)381 static void ssh1_bpp_queue_disconnect(BinaryPacketProtocol *bpp,
382                                       const char *msg, int category)
383 {
384     PktOut *pkt = ssh_bpp_new_pktout(bpp, SSH1_MSG_DISCONNECT);
385     put_stringz(pkt, msg);
386     pq_push(&bpp->out_pq, pkt);
387 }
388