]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - ssh.c
Removing one bug, and hunting another
[PuTTY.git] / ssh.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #ifndef macintosh
4 #include <winsock.h>
5 #endif
6
7 #include "putty.h"
8
9 #ifndef FALSE
10 #define FALSE 0
11 #endif
12 #ifndef TRUE
13 #define TRUE 1
14 #endif
15
16 #include "ssh.h"
17
18 /* Coroutine mechanics for the sillier bits of the code */
19 #define crBegin1(l)     (l) = 0;
20 #define crBegin2(l)     switch(l) { case 0:;
21 #define crBegin(l)              crBegin1(l); crBegin2(l);
22 #define crFinish(l,z)   } (l) = 0; return (l)
23 #define foolemacs {
24 #define crFinishV(l)    } (l) = 0; return
25 #define crReturn(l,z)   \
26         do {\
27             (l)=__LINE__; return (z); case __LINE__:;\
28         } while (0)
29 #define crReturnV(l)    \
30         do {\
31             (l)=__LINE__; return; case __LINE__:;\
32         } while (0)
33 #define crStop(l,z)     do{ (l) = 0; return (z); }while(0)
34 #define crStopV(l)              do{ (l) = 0; return; }while(0)
35
36 #ifndef FALSE
37 #define FALSE 0
38 #endif
39 #ifndef TRUE
40 #define TRUE 1
41 #endif
42
43 struct Packet {
44     long length;
45     int type;
46     unsigned long crc;
47     unsigned char *data;
48     unsigned char *body;
49     long maxlen;
50 };
51
52 struct ssh_private {
53     SOCKET s;
54     unsigned char session_key[32];
55     struct ssh_cipher *cipher;
56     char *savedhost;
57     enum {
58         SSH_STATE_BEFORE_SIZE,
59         SSH_STATE_INTERMED,
60         SSH_STATE_SESSION,
61         SSH_STATE_CLOSED
62     } ssh_state;
63     int size_needed;
64 #ifdef FWHACK
65     char *FWhost;
66     int FWport;
67 #endif
68     struct Packet pktin;
69     struct Packet pktout;
70     /* State for ssh_gotdata coroutine */
71     int gd_line;
72     long len, biglen, to_read;
73     unsigned char *p;
74     int i, pad;
75     int chunk;
76     char *sversion;
77     size_t sversion_space, sversion_len;
78     /* State for ssh_protocol coroutine */
79     int pr_line;
80 };
81
82 static void s_write (Session *sess, unsigned char *buf, int len) {
83     struct ssh_private *sp = (struct ssh_private*)sess->back_priv;
84
85     while (len > 0) {
86         int i = net_send (sp->s, buf, len, 0);
87         if (i > 0)
88             len -= i, buf += i;
89     }
90 }
91
92 static int s_read (Session *sess, unsigned char *buf, int len) {
93     struct ssh_private *sp = (struct ssh_private*)sess->back_priv;
94
95     int ret = 0;
96     while (len > 0) {
97         int i = net_recv (sp->s, buf, len, 0);
98         if (i > 0)
99             len -= i, buf += i, ret += i;
100         else
101             return i;
102     }
103     return ret;
104 }
105
106 static void c_write (Session *sess, char *buf, int len) {
107
108     while (len--) {
109         int new_head = (sess->inbuf_head + 1) & INBUF_MASK;
110         if (new_head != sess->inbuf_reap) {
111             sess->inbuf[sess->inbuf_head] = *buf++;
112             sess->inbuf_head = new_head;
113         }
114     }
115 }
116
117 static void ssh_protocol(Session *,unsigned char *in, int inlen, int ispkt);
118 static void ssh_size(void);
119
120 static void ssh_gotdata(Session *sess, unsigned char *data, int datalen) {
121     struct ssh_private *sp = (struct ssh_private *)sess->back_priv;
122     unsigned char *eol;
123     size_t fraglen = datalen;
124     static char vstring[] = "SSH-1.5-PuTTY\n";
125
126     crBegin(sp->gd_line);
127
128 #ifdef FWHACK
129     /* Should wait for "SSH-" */
130 #endif
131     sp->sversion = smalloc(16); /* enough for "SSH-1.5-1.2.27\n" */
132     sp->sversion_space = 16;
133     sp->sversion_len = 0;
134     do {
135
136         while (datalen == 0)
137             crReturnV(sp->gd_line);
138         eol = memchr(data, '\n', datalen);
139         if (eol != NULL)
140             fraglen = eol + 1 - data; /* include \n */
141         if (sp->sversion_len + fraglen > sp->sversion_space) {
142             /* FIXME Sanity-check length */
143             srealloc(sp->sversion, sp->sversion_len + fraglen);
144             sp->sversion_space = sp->sversion_len + fraglen;
145         }
146         memcpy(sp->sversion + sp->sversion_len, data, fraglen);
147         data += fraglen; datalen -= fraglen;
148     } while (eol == NULL);
149     sp->sversion[sp->sversion_len - 1] = '\0';
150
151     if (sp->sversion_len < 4 || memcmp(sp->sversion, "SSH-", 4) != 0)
152         /* XXX explode! */;
153
154     /*
155      * We ignore the rest of the banner on the grounds that we only
156      * speak SSH 1.5 anyway.  If the server can't, that's its problem.
157      */
158
159     s_write(sess, (unsigned char *)vstring, strlen(vstring));
160
161     /* End of do_ssh_init */
162
163     while (1) {
164         for (sp->i = sp->len = 0; sp->i < 4; sp->i++) {
165             while (datalen == 0)
166                 crReturnV(sp->gd_line);
167             sp->len = (sp->len << 8) + *data;
168             data++, datalen--;
169         }
170
171 #ifdef FWHACK
172         if (sp->len == 0x52656d6f) {       /* "Remo"te server has closed ... */
173             sp->len = 0x300;               /* big enough to carry to end */
174         }
175 #endif
176
177         sp->pad = 8 - (sp->len%8);
178
179         sp->biglen = sp->len + sp->pad;
180
181         sp->len -= 5;                  /* type and CRC */
182
183         sp->pktin.length = sp->len;
184         if (sp->pktin.maxlen < sp->biglen) {
185             sp->pktin.maxlen = sp->biglen;
186             sp->pktin.data = (sp->pktin.data == NULL ? smalloc(sp->biglen) :
187                         srealloc(sp->pktin.data, sp->biglen));
188         }
189
190         sp->p = sp->pktin.data, sp->to_read = sp->biglen;
191         while (sp->to_read > 0) {
192             sp->chunk = sp->to_read;
193             while (datalen == 0)
194                 crReturnV(sp->gd_line);
195             if (sp->chunk > datalen)
196                 sp->chunk = datalen;
197             memcpy(sp->p, data, sp->chunk);
198             data += sp->chunk;
199             datalen -= sp->chunk;
200             sp->p += sp->chunk;
201             sp->to_read -= sp->chunk;
202         }
203
204         if (sp->cipher)
205             sp->cipher->decrypt(sp->pktin.data, sp->biglen);
206
207         sp->pktin.type = sp->pktin.data[sp->pad];
208         sp->pktin.body = sp->pktin.data+sp->pad+1;
209
210         if (sp->pktin.type == SSH_MSG_DEBUG) {         /* SSH_MSG_DEBUG */
211             /* FIXME: log it */
212         } else
213             ssh_protocol(sess, NULL, 0, 1);
214     }
215     crFinishV(sp->gd_line);
216 }
217
218 static void s_wrpkt_start(Session *sess, int type, int len) {
219     struct ssh_private *sp = (struct ssh_private *)sess->back_priv;
220     int pad, biglen;
221
222     len += 5;                          /* type and CRC */
223     pad = 8 - (len%8);
224     biglen = len + pad;
225
226     sp->pktout.length = len-5;
227     if (sp->pktout.maxlen < biglen) {
228         sp->pktout.maxlen = biglen;
229         sp->pktout.data = (sp->pktout.data == NULL ? smalloc(biglen+4) :
230                        srealloc(sp->pktout.data, biglen+4));
231     }
232
233     sp->pktout.type = type;
234     sp->pktout.body = sp->pktout.data+4+pad+1;
235 }
236
237 static void s_wrpkt(Session *sess) {
238     struct ssh_private *sp = (struct ssh_private *)sess->back_priv;
239
240     int pad, len, biglen, i;
241     unsigned long crc;
242
243     len = sp->pktout.length + 5;               /* type and CRC */
244     pad = 8 - (len%8);
245     biglen = len + pad;
246
247     sp->pktout.body[-1] = sp->pktout.type;
248     for (i=0; i<pad; i++)
249         sp->pktout.data[i+4] = random_byte();
250     crc = crc32(sp->pktout.data+4, biglen-4);
251
252     sp->pktout.data[biglen+0] = (unsigned char) ((crc >> 24) & 0xFF);
253     sp->pktout.data[biglen+1] = (unsigned char) ((crc >> 16) & 0xFF);
254     sp->pktout.data[biglen+2] = (unsigned char) ((crc >> 8) & 0xFF);
255     sp->pktout.data[biglen+3] = (unsigned char) (crc & 0xFF);
256
257     sp->pktout.data[0] = (len >> 24) & 0xFF;
258     sp->pktout.data[1] = (len >> 16) & 0xFF;
259     sp->pktout.data[2] = (len >> 8) & 0xFF;
260     sp->pktout.data[3] = len & 0xFF;
261
262     if (sp->cipher)
263         sp->cipher->encrypt(sp->pktout.data+4, biglen);
264
265     s_write(sess, sp->pktout.data, biglen+4);
266 }
267
268 static int do_ssh_init(Session *sess) {
269 }
270
271 static void ssh_protocol(Session *sess, unsigned char *in, int inlen,
272                          int ispkt) {
273     struct ssh_private *sp = (struct ssh_private *)sess->back_priv;
274     int i, j, len;
275     unsigned char session_id[16];
276     unsigned char *rsabuf, *keystr1, *keystr2;
277     unsigned char cookie[8];
278     struct RSAKey servkey, hostkey;
279     struct MD5Context md5c;
280     unsigned long supported_ciphers_mask;
281     int cipher_type;
282
283     extern struct ssh_cipher ssh_3des;
284     extern struct ssh_cipher ssh_blowfish;
285
286     crBegin(sp->pr_line);
287
288     random_init();
289
290     while (!ispkt)
291         crReturnV(sp->pr_line);
292
293     if (sp->pktin.type != SSH_SMSG_PUBLIC_KEY)
294         fatalbox("Public key packet not received");
295
296     memcpy(cookie, sp->pktin.body, 8);
297
298     MD5Init(&md5c);
299
300     i = makekey(sp->pktin.body+8, &servkey, &keystr1);
301
302     j = makekey(sp->pktin.body+8+i, &hostkey, &keystr2);
303
304     supported_ciphers_mask = (sp->pktin.body[12+i+j] << 24) |
305                              (sp->pktin.body[13+i+j] << 16) |
306                              (sp->pktin.body[14+i+j] << 8) |
307                              (sp->pktin.body[15+i+j]);
308
309     MD5Update(&md5c, keystr2, hostkey.bytes);
310     MD5Update(&md5c, keystr1, servkey.bytes);
311     MD5Update(&md5c, sp->pktin.body, 8);
312
313     MD5Final(session_id, &md5c);
314
315     for (i=0; i<32; i++)
316         session_key[i] = random_byte();
317
318     len = (hostkey.bytes > servkey.bytes ? hostkey.bytes : servkey.bytes);
319
320     rsabuf = malloc(len);
321     if (!rsabuf)
322         fatalbox("Out of memory");
323
324     verify_ssh_host_key(savedhost, &hostkey);
325
326     for (i=0; i<32; i++) {
327         rsabuf[i] = session_key[i];
328         if (i < 16)
329             rsabuf[i] ^= session_id[i];
330     }
331
332     if (hostkey.bytes > servkey.bytes) {
333         rsaencrypt(rsabuf, 32, &servkey);
334         rsaencrypt(rsabuf, servkey.bytes, &hostkey);
335     } else {
336         rsaencrypt(rsabuf, 32, &hostkey);
337         rsaencrypt(rsabuf, hostkey.bytes, &servkey);
338     }
339
340     cipher_type = cfg.cipher == CIPHER_BLOWFISH ? SSH_CIPHER_BLOWFISH :
341                   SSH_CIPHER_3DES;
342     if ((supported_ciphers_mask & (1 << cipher_type)) == 0) {
343         c_write("Selected cipher not supported, falling back to 3DES\r\n", 53);
344         cipher_type = SSH_CIPHER_3DES;
345     }
346
347     s_wrpkt_start(SSH_CMSG_SESSION_KEY, len+15);
348     sp->pktout.body[0] = cipher_type;
349     memcpy(sp->pktout.body+1, cookie, 8);
350     sp->pktout.body[9] = (len*8) >> 8;
351     sp->pktout.body[10] = (len*8) & 0xFF;
352     memcpy(sp->pktout.body+11, rsabuf, len);
353     sp->pktout.body[len+11] = sp->pktout.body[len+12] = 0;   /* protocol flags */
354     sp->pktout.body[len+13] = sp->pktout.body[len+14] = 0;
355     s_wrpkt();
356
357     free(rsabuf);
358
359     cipher = cipher_type == SSH_CIPHER_BLOWFISH ? &ssh_blowfish :
360              &ssh_3des;
361     cipher->sesskey(session_key);
362
363     do { crReturnV(sp->pr_line); } while (!ispkt);
364
365     if (sp->pktin.type != SSH_SMSG_SUCCESS)
366         fatalbox("Encryption not successfully enabled");
367
368     fflush(stdout);
369     {
370         static char username[100];
371         static int pos = 0;
372         static char c;
373         if (!*cfg.username) {
374             c_write("login as: ", 10);
375             while (pos >= 0) {
376                 do { crReturnV(sp->pr_line); } while (ispkt);
377                 while (inlen--) switch (c = *in++) {
378                   case 10: case 13:
379                     username[pos] = 0;
380                     pos = -1;
381                     break;
382                   case 8: case 127:
383                     if (pos > 0) {
384                         c_write("\b \b", 3);
385                         pos--;
386                     }
387                     break;
388                   case 21: case 27:
389                     while (pos > 0) {
390                         c_write("\b \b", 3);
391                         pos--;
392                     }
393                     break;
394                   case 3: case 4:
395                     random_save_seed();
396                     exit(0);
397                     break;
398                   default:
399                     if (c >= ' ' && c <= '~' && pos < 40) {
400                         username[pos++] = c;
401                         c_write(&c, 1);
402                     }
403                     break;
404                 }
405             }
406             c_write("\r\n", 2);
407             username[strcspn(username, "\n\r")] = '\0';
408         } else {
409             char stuff[200];
410             strncpy(username, cfg.username, 99);
411             username[99] = '\0';
412             sprintf(stuff, "Sent username \"%s\".\r\n", username);
413             c_write(stuff, strlen(stuff));
414         }
415         s_wrpkt_start(SSH_CMSG_USER, 4+strlen(username));
416         sp->pktout.body[0] = sp->pktout.body[1] = sp->pktout.body[2] = 0;
417         sp->pktout.body[3] = strlen(username);
418         memcpy(sp->pktout.body+4, username, strlen(username));
419         s_wrpkt();
420     }
421
422     do { crReturnV(sp->pr_line); } while (!ispkt);
423
424     while (sp->pktin.type == SSH_SMSG_FAILURE) {
425         static char password[100];
426         static int pos;
427         static char c;
428         c_write("password: ", 10);
429         pos = 0;
430         while (pos >= 0) {
431             do { crReturnV(sp->pr_line); } while (ispkt);
432             while (inlen--) switch (c = *in++) {
433               case 10: case 13:
434                 password[pos] = 0;
435                 pos = -1;
436                 break;
437               case 8: case 127:
438                 if (pos > 0)
439                     pos--;
440                 break;
441               case 21: case 27:
442                 pos = 0;
443                 break;
444               case 3: case 4:
445                 random_save_seed();
446                 exit(0);
447                 break;
448               default:
449                 if (c >= ' ' && c <= '~' && pos < 40)
450                     password[pos++] = c;
451                 break;
452             }
453         }
454         c_write("\r\n", 2);
455         s_wrpkt_start(SSH_CMSG_AUTH_PASSWORD, 4+strlen(password));
456         sp->pktout.body[0] = sp->pktout.body[1] = sp->pktout.body[2] = 0;
457         sp->pktout.body[3] = strlen(password);
458         memcpy(sp->pktout.body+4, password, strlen(password));
459         s_wrpkt();
460         memset(password, 0, strlen(password));
461         do { crReturnV(sp->pr_line); } while (!ispkt);
462         if (sp->pktin.type == SSH_SMSG_FAILURE) {
463             c_write("Access denied\r\n", 15);
464         } else if (sp->pktin.type != SSH_SMSG_SUCCESS) {
465             fatalbox("Strange packet received, type %d", sp->pktin.type);
466         }
467     }
468
469     if (!cfg.nopty) {
470         i = strlen(cfg.termtype);
471         s_wrpkt_start(SSH_CMSG_REQUEST_PTY, i+5*4+1);
472         sp->pktout.body[0] = (i >> 24) & 0xFF;
473         sp->pktout.body[1] = (i >> 16) & 0xFF;
474         sp->pktout.body[2] = (i >> 8) & 0xFF;
475         sp->pktout.body[3] = i & 0xFF;
476         memcpy(sp->pktout.body+4, cfg.termtype, i);
477         i += 4;
478         sp->pktout.body[i++] = (rows >> 24) & 0xFF;
479         sp->pktout.body[i++] = (rows >> 16) & 0xFF;
480         sp->pktout.body[i++] = (rows >> 8) & 0xFF;
481         sp->pktout.body[i++] = rows & 0xFF;
482         sp->pktout.body[i++] = (cols >> 24) & 0xFF;
483         sp->pktout.body[i++] = (cols >> 16) & 0xFF;
484         sp->pktout.body[i++] = (cols >> 8) & 0xFF;
485         sp->pktout.body[i++] = cols & 0xFF;
486         memset(sp->pktout.body+i, 0, 9);       /* 0 pixwidth, 0 pixheight, 0.b endofopt */
487         s_wrpkt();
488         ssh_state = SSH_STATE_INTERMED;
489         do { crReturnV(sp->pr_line); } while (!ispkt);
490         if (sp->pktin.type != SSH_SMSG_SUCCESS
491             && sp->pktin.type != SSH_SMSG_FAILURE) {
492             fatalbox("Protocol confusion");
493         } else if (sp->pktin.type == SSH_SMSG_FAILURE) {
494             c_write("Server refused to allocate pty\r\n", 32);
495         }
496     }
497
498     s_wrpkt_start(SSH_CMSG_EXEC_SHELL, 0);
499     s_wrpkt();
500
501     ssh_state = SSH_STATE_SESSION;
502     if (size_needed)
503         ssh_size();
504
505     while (1) {
506         crReturnV(sp->pr_line);
507         if (ispkt) {
508             if (sp->pktin.type == SSH_SMSG_STDOUT_DATA ||
509                 sp->pktin.type == SSH_SMSG_STDERR_DATA) {
510                 long len = 0;
511                 for (i = 0; i < 4; i++)
512                     len = (len << 8) + sp->pktin.body[i];
513                 c_write(sp->pktin.body+4, len);
514             } else if (sp->pktin.type == SSH_MSG_DISCONNECT) {
515                 /* SSH_MSG_DISCONNECT */
516                 ssh_state = SSH_STATE_CLOSED;
517             } else if (sp->pktin.type == SSH_SMSG_SUCCESS) {
518                 /* SSH_MSG_SUCCESS: may be from EXEC_SHELL on some servers */
519             } else if (sp->pktin.type == SSH_SMSG_FAILURE) {
520                 /* SSH_MSG_FAILURE: may be from EXEC_SHELL on some servers
521                  * if no pty is available or in other odd cases. Ignore */
522             } else if (sp->pktin.type == SSH_SMSG_EXITSTATUS) {
523                 /* EXITSTATUS */
524                 s_wrpkt_start(SSH_CMSG_EXIT_CONFIRMATION, 0);
525                 s_wrpkt();
526             } else {
527                 fatalbox("Strange packet received: type %d", sp->pktin.type);
528             }
529         } else {
530             s_wrpkt_start(SSH_CMSG_STDIN_DATA, 4+inlen);
531             sp->pktout.body[0] = (inlen >> 24) & 0xFF;
532             sp->pktout.body[1] = (inlen >> 16) & 0xFF;
533             sp->pktout.body[2] = (inlen >> 8) & 0xFF;
534             sp->pktout.body[3] = inlen & 0xFF;
535             memcpy(sp->pktout.body+4, in, inlen);
536             s_wrpkt();
537         }
538     }
539
540     crFinishV;
541 }
542
543 /*
544  * Called to set up the connection. Will arrange for WM_NETEVENT
545  * messages to be passed to the specified window, whose window
546  * procedure should then call telnet_msg().
547  *
548  * Returns an error message, or NULL on success.
549  *
550  * Also places the canonical host name into `realhost'.
551  */
552 static char *ssh_init(Session *sess) {
553     struct ssh_private *sp;
554     char *host = sess->cfg.host;
555     int port = sess->cfg.port;
556
557     sess->back_priv = smalloc(sizeof(struct ssh_private));
558     sp = (struct ssh_private *)sess->back_priv;
559     memset(*sp, 0, sizeof(*sp));
560     sp->s = INVALID_SOCKET;
561     sp->ssh_state = SSH_STATE_BEFORE_SIZE;
562
563     sp->savedhost = smalloc(1+strlen(host));
564     strcpy(sp->savedhost, host);
565
566 #ifdef FWHACK
567     sp->FWhost = host;
568     sp->FWport = port;
569     host = FWSTR;
570     port = 23;
571 #endif
572
573     if (port < 0)
574         port = 22;                     /* default ssh port */
575     sp->s = net_open(sess, host, port); 
576
577     return NULL;
578 }
579
580 static void ssh_opened(Session *sess) {
581     struct ssh_private *sp = (struct ssh_private *)sess->back_priv;
582
583 #ifdef FWHACK
584     send(sp->s, "connect ", 8, 0);
585     send(sp->s, FWhost, strlen(FWhost), 0);
586     {
587         char buf[20];
588         sprintf(buf, " %d\n", FWport);
589         send (s, buf, strlen(buf), 0);
590     }
591 #endif
592
593     if (!do_ssh_init())
594         return "Protocol initialisation error";
595
596     if (WSAAsyncSelect (s, hwnd, WM_NETEVENT, FD_READ | FD_CLOSE) == SOCKET_ERROR)
597         switch (WSAGetLastError()) {
598           case WSAENETDOWN: return "Network is down";
599           default: return "WSAAsyncSelect(): unknown error";
600         }
601
602     return NULL;
603 }
604
605 /*
606  * Process a WM_NETEVENT message. Will return 0 if the connection
607  * has closed, or <0 for a socket error.
608  */
609 static int ssh_msg (Session *sess, SOCKET sock, Net_Event_Type ne) {
610     struct ssh_private *sp = (struct ssh_private *)sess->back_priv;
611     int ret;
612     char buf[256];
613
614     if (s == INVALID_SOCKET)           /* how the hell did we get here?! */
615         return -5000;
616
617     switch (ne) {
618       case NE_OPEN:
619         ssh_opened(sess);
620         return 1;
621       case NE_DATA:
622         ret = net_recv(sock, buf, sizeof(buf), 0);
623         if (ret < 0)                   /* any _other_ error */
624             return -1;
625         if (ret == 0) {
626             sp->s = INVALID_SOCKET;
627             return 0;                  /* can't happen, in theory */
628         }
629         ssh_gotdata(sess, buf, ret);
630         return 1;
631       case NE_CLOSING:
632         sp->s = INVALID_SOCKET;
633         sp->ssh_state = SSH_STATE_CLOSED;
634         return 0;
635       case NE_NOHOST:
636         fatalbox("Host not found");
637       case NE_REFUSED:
638         fatalbox("Connection refused");
639       case NE_NOOPEN:
640         fatalbox("Unable to open connection");
641       case NE_TIMEOUT:
642         fatalbox("Connection timed out");
643       case NE_ABORT:
644         fatalbox("Connection reset by peer");
645       case NE_DIED:
646         fatalbox("Connection died");
647     }
648     return 1;                          /* shouldn't happen, but WTF */
649 }
650
651 /*
652  * Called to send data down the Telnet connection.
653  */
654 static void ssh_send(Session *sess, char *buf, int len) {
655     if (s == INVALID_SOCKET)
656         return;
657
658     ssh_protocol(sess, buf, len, 0);
659 }
660
661 /*
662  * Called to set the size of the window from Telnet's POV.
663  */
664 static void ssh_size(Session *sess) {
665     struct ssh_private *sp = (struct ssh_private *sp)sess->back_priv;
666
667     switch (sp->ssh_state) {
668       case SSH_STATE_BEFORE_SIZE:
669       case SSH_STATE_CLOSED:
670         break;                         /* do nothing */
671       case SSH_STATE_INTERMED:
672         sp->size_needed = TRUE;        /* buffer for later */
673         break;
674       case SSH_STATE_SESSION:
675         if (!sess->cfg.nopty) {
676             s_wrpkt_start(SSH_CMSG_WINDOW_SIZE, 16);
677             sp->pktout.body[0] = (rows >> 24) & 0xFF;
678             sp->pktout.body[1] = (rows >> 16) & 0xFF;
679             sp->pktout.body[2] = (rows >> 8) & 0xFF;
680             sp->pktout.body[3] = rows & 0xFF;
681             sp->pktout.body[4] = (cols >> 24) & 0xFF;
682             sp->pktout.body[5] = (cols >> 16) & 0xFF;
683             sp->pktout.body[6] = (cols >> 8) & 0xFF;
684             sp->pktout.body[7] = cols & 0xFF;
685             memset(sp->pktout.body+8, 0, 8);
686             s_wrpkt();
687         }
688     }
689 }
690
691 /*
692  * (Send Telnet special codes)
693  */
694 static void ssh_special (Telnet_Special code) {
695     /* do nothing */
696 }
697
698 Backend ssh_backend = {
699     ssh_init,
700     ssh_msg,
701     ssh_send,
702     ssh_size,
703     ssh_special
704 };