Major destabilisation, phase 1. In this phase I've moved (I think)
[u/mdw/putty] / ssh.c
diff --git a/ssh.c b/ssh.c
index a3ac60b..31d4cc4 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -5,6 +5,7 @@
 #include <assert.h>
 
 #include "putty.h"
+#include "terminal.h"
 #include "tree234.h"
 #include "ssh.h"
 
@@ -182,6 +183,8 @@ static const char *const ssh2_disconnect_reasons[] = {
 #define BUG_NEEDS_SSH1_PLAIN_PASSWORD            4
 #define BUG_CHOKES_ON_RSA                        8
 #define BUG_SSH2_RSA_PADDING                    16
+#define BUG_SSH2_DERIVEKEY                       32
+#define BUG_SSH2_DH_GEX                          64
 
 static int ssh_pkt_ctx = 0;
 
@@ -425,6 +428,16 @@ enum {                                    /* channel types */
 struct ssh_channel {
     unsigned remoteid, localid;
     int type;
+    /*
+     * In SSH1, this value contains four bits:
+     * 
+     *   1   We have sent SSH1_MSG_CHANNEL_CLOSE.
+     *   2   We have sent SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION.
+     *   4   We have received SSH1_MSG_CHANNEL_CLOSE.
+     *   8   We have received SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION.
+     * 
+     * A channel is completely finished with when all four bits are set.
+     */
     int closes;
     union {
        struct ssh1_data_channel {
@@ -521,6 +534,8 @@ static int savedport;
 static int ssh_send_ok;
 static int ssh_echoing, ssh_editing;
 
+static void *frontend;
+
 static tree234 *ssh_channels;         /* indexed by local id */
 static struct ssh_channel *mainchan;   /* primary session channel */
 static int ssh_exitcode = -1;
@@ -676,7 +691,7 @@ static void c_write(char *buf, int len)
                fputc(buf[i], stderr);
        return;
     }
-    from_backend(1, buf, len);
+    from_backend(frontend, 1, buf, len);
 }
 
 static void c_write_untrusted(char *buf, int len)
@@ -803,11 +818,11 @@ static int ssh1_rdpkt(unsigned char **data, int *datalen)
 
     if (pktin.type == SSH1_MSG_DEBUG) {
        /* log debug message */
-       char buf[80];
+       char buf[512];
        int stringlen = GET_32BIT(pktin.body);
-       strcpy(buf, "Remote: ");
-       if (stringlen > 70)
-           stringlen = 70;
+       strcpy(buf, "Remote debug message: ");
+       if (stringlen > 480)
+           stringlen = 480;
        memcpy(buf + 8, pktin.body + 4, stringlen);
        buf[8 + stringlen] = '\0';
        logevent(buf);
@@ -885,6 +900,15 @@ static int ssh2_rdpkt(unsigned char **data, int *datalen)
     st->pad = pktin.data[4];
 
     /*
+     * _Completely_ silly lengths should be stomped on before they
+     * do us any more damage.
+     */
+    if (st->len < 0 || st->pad < 0 || st->len + st->pad < 0) {
+       bombout(("Incoming packet was garbled on decryption"));
+       crReturn(0);
+    }
+
+    /*
      * This enables us to deduce the payload length.
      */
     st->payload = st->len - st->pad - 1;
@@ -1650,9 +1674,11 @@ static void ssh_detect_bugs(char *vstring)
 
     ssh_remote_bugs = 0;
 
-    if (!strcmp(imp, "1.2.18") || !strcmp(imp, "1.2.19") ||
-       !strcmp(imp, "1.2.20") || !strcmp(imp, "1.2.21") ||
-       !strcmp(imp, "1.2.22") || !strcmp(imp, "Cisco-1.25")) {
+    if (cfg.sshbug_ignore1 == BUG_ON ||
+       (cfg.sshbug_ignore1 == BUG_AUTO &&
+        (!strcmp(imp, "1.2.18") || !strcmp(imp, "1.2.19") ||
+         !strcmp(imp, "1.2.20") || !strcmp(imp, "1.2.21") ||
+         !strcmp(imp, "1.2.22") || !strcmp(imp, "Cisco-1.25")))) {
        /*
         * These versions don't support SSH1_MSG_IGNORE, so we have
         * to use a different defence against password length
@@ -1662,7 +1688,9 @@ static void ssh_detect_bugs(char *vstring)
        logevent("We believe remote version has SSH1 ignore bug");
     }
 
-    if (!strcmp(imp, "Cisco-1.25")) {
+    if (cfg.sshbug_plainpw1 == BUG_ON ||
+       (cfg.sshbug_plainpw1 == BUG_AUTO &&
+        (!strcmp(imp, "Cisco-1.25")))) {
        /*
         * These versions need a plain password sent; they can't
         * handle having a null and a random length of data after
@@ -1672,7 +1700,9 @@ static void ssh_detect_bugs(char *vstring)
        logevent("We believe remote version needs a plain SSH1 password");
     }
 
-    if (!strcmp(imp, "Cisco-1.25")) {
+    if (cfg.sshbug_rsa1 == BUG_ON ||
+       (cfg.sshbug_rsa1 == BUG_AUTO &&
+        (!strcmp(imp, "Cisco-1.25")))) {
        /*
         * These versions apparently have no clue whatever about
         * RSA authentication and will panic and die if they see
@@ -1682,9 +1712,11 @@ static void ssh_detect_bugs(char *vstring)
        logevent("We believe remote version can't handle RSA authentication");
     }
 
-    if (!strncmp(imp, "2.1.0", 5) || !strncmp(imp, "2.0.", 4) ||
-       !strncmp(imp, "2.2.0", 5) || !strncmp(imp, "2.3.0", 5) ||
-       !strncmp(imp, "2.1 ", 4)) {
+    if (cfg.sshbug_hmac2 == BUG_ON ||
+       (cfg.sshbug_hmac2 == BUG_AUTO &&
+        (!strncmp(imp, "2.1.0", 5) || !strncmp(imp, "2.0.", 4) ||
+         !strncmp(imp, "2.2.0", 5) || !strncmp(imp, "2.3.0", 5) ||
+         !strncmp(imp, "2.1 ", 4)))) {
        /*
         * These versions have the HMAC bug.
         */
@@ -1692,14 +1724,36 @@ static void ssh_detect_bugs(char *vstring)
        logevent("We believe remote version has SSH2 HMAC bug");
     }
 
-    if ((!strncmp(imp, "OpenSSH_2.", 10) && imp[10]>='5' && imp[10]<='9') ||
-       (!strncmp(imp, "OpenSSH_3.", 10) && imp[10]>='0' && imp[10]<='2')) {
+    if (cfg.sshbug_derivekey2 == BUG_ON ||
+       (cfg.sshbug_derivekey2 == BUG_AUTO &&
+        (!strncmp(imp, "2.0.", 4)))) {
+       /*
+        * These versions have the key-derivation bug (failing to
+        * include the literal shared secret in the hashes that
+        * generate the keys).
+        */
+       ssh_remote_bugs |= BUG_SSH2_DERIVEKEY;
+       logevent("We believe remote version has SSH2 key-derivation bug");
+    }
+
+    if (cfg.sshbug_rsapad2 == BUG_ON ||
+       (cfg.sshbug_rsapad2 == BUG_AUTO &&
+        ((!strncmp(imp, "OpenSSH_2.", 10) && imp[10]>='5' && imp[10]<='9') ||
+         (!strncmp(imp, "OpenSSH_3.", 10) && imp[10]>='0' && imp[10]<='2')))){
        /*
         * These versions have the SSH2 RSA padding bug.
         */
        ssh_remote_bugs |= BUG_SSH2_RSA_PADDING;
        logevent("We believe remote version has SSH2 RSA padding bug");
     }
+
+    if (cfg.sshbug_dhgex2 == BUG_ON) {
+       /*
+        * These versions have the SSH2 DH GEX bug.
+        */
+       ssh_remote_bugs |= BUG_SSH2_DH_GEX;
+       logevent("We believe remote version has SSH2 DH group exchange bug");
+    }
 }
 
 static int do_ssh_init(unsigned char c)
@@ -1773,7 +1827,7 @@ static int do_ssh_init(unsigned char c)
     /* Anything strictly below "2.0" means protocol 1 is supported. */
     proto1 = ssh_versioncmp(version, "2.0") < 0;
     /* Anything greater or equal to "1.99" means protocol 2 is supported. */
-    proto2 = ssh_versioncmp(version, "1.99") > 0;
+    proto2 = ssh_versioncmp(version, "1.99") >= 0;
 
     if (cfg.sshprot == 0 && !proto1) {
        bombout(("SSH protocol version 1 required by user but not provided by server"));
@@ -1784,7 +1838,7 @@ static int do_ssh_init(unsigned char c)
        crReturn(0);
     }
 
-    if (proto2 && (cfg.sshprot == 2 || !proto1)) {
+    if (proto2 && (cfg.sshprot >= 2 || !proto1)) {
        /*
         * Use v2 protocol.
         */
@@ -2049,6 +2103,76 @@ static void ssh_throttle_all(int enable, int bufsize)
 }
 
 /*
+ * Username and password input, abstracted off into reusable
+ * routines (hopefully even reusable between SSH1 and SSH2!).
+ */
+static char *ssh_userpass_input_buffer;
+static int ssh_userpass_input_buflen;
+static int ssh_userpass_input_bufpos;
+static int ssh_userpass_input_echo;
+
+/* Set up a username or password input loop on a given buffer. */
+void setup_userpass_input(char *buffer, int buflen, int echo)
+{
+    ssh_userpass_input_buffer = buffer;
+    ssh_userpass_input_buflen = buflen;
+    ssh_userpass_input_bufpos = 0;
+    ssh_userpass_input_echo = echo;
+}
+
+/*
+ * Process some terminal data in the course of username/password
+ * input. Returns >0 for success (line of input returned in
+ * buffer), <0 for failure (user hit ^C/^D, bomb out and exit), 0
+ * for inconclusive (keep waiting for more input please).
+ */
+int process_userpass_input(unsigned char *in, int inlen)
+{
+    char c;
+
+    while (inlen--) {
+       switch (c = *in++) {
+         case 10:
+         case 13:
+           ssh_userpass_input_buffer[ssh_userpass_input_bufpos] = 0;
+           ssh_userpass_input_buffer[ssh_userpass_input_buflen-1] = 0;
+           return +1;
+           break;
+         case 8:
+         case 127:
+           if (ssh_userpass_input_bufpos > 0) {
+               if (ssh_userpass_input_echo)
+                   c_write_str("\b \b");
+               ssh_userpass_input_bufpos--;
+           }
+           break;
+         case 21:
+         case 27:
+           while (ssh_userpass_input_bufpos > 0) {
+               if (ssh_userpass_input_echo)
+                   c_write_str("\b \b");
+               ssh_userpass_input_bufpos--;
+           }
+           break;
+         case 3:
+         case 4:
+           return -1;
+           break;
+         default:
+           if (((c >= ' ' && c <= '~') ||
+                ((unsigned char) c >= 160))
+               && ssh_userpass_input_bufpos < ssh_userpass_input_buflen-1) {
+               ssh_userpass_input_buffer[ssh_userpass_input_bufpos++] = c;
+               if (ssh_userpass_input_echo)
+                   c_write(&c, 1);
+           }
+           break;
+       }
+    }
+    return 0;
+}
+
+/*
  * Handle the key exchange and user authentication phases.
  */
 static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
@@ -2233,8 +2357,6 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
 
     fflush(stdout);
     {
-       static int pos = 0;
-       static char c;
        if ((flags & FLAG_INTERACTIVE) && !*cfg.username) {
            if (ssh_get_line && !ssh_getline_pw_only) {
                if (!ssh_get_line("login as: ",
@@ -2248,51 +2370,22 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
                    crReturn(1);
                }
            } else {
+               static int ret;
                c_write_str("login as: ");
                ssh_send_ok = 1;
-               while (pos >= 0) {
+
+               setup_userpass_input(username, sizeof(username), 1);
+               do {
                    crWaitUntil(!ispkt);
-                   while (inlen--)
-                       switch (c = *in++) {
-                         case 10:
-                         case 13:
-                           username[pos] = 0;
-                           pos = -1;
-                           break;
-                         case 8:
-                         case 127:
-                           if (pos > 0) {
-                               c_write_str("\b \b");
-                               pos--;
-                           }
-                           break;
-                         case 21:
-                         case 27:
-                           while (pos > 0) {
-                               c_write_str("\b \b");
-                               pos--;
-                           }
-                           break;
-                         case 3:
-                         case 4:
-                           cleanup_exit(0);
-                           break;
-                         default:
-                           if (((c >= ' ' && c <= '~') ||
-                                ((unsigned char) c >= 160))
-                               && pos < sizeof(username)-1) {
-                               username[pos++] = c;
-                               c_write(&c, 1);
-                           }
-                           break;
-                       }
-               }
+                   ret = process_userpass_input(in, inlen);
+               } while (ret == 0);
+               if (ret < 0)
+                   cleanup_exit(0);
                c_write_str("\r\n");
-               username[strcspn(username, "\n\r")] = '\0';
            }
        } else {
-           strncpy(username, cfg.username, 99);
-           username[99] = '\0';
+           strncpy(username, cfg.username, sizeof(username));
+           username[sizeof(username)-1] = '\0';
        }
 
        send_packet(SSH1_CMSG_USER, PKT_STR, username, PKT_END);
@@ -2572,37 +2665,17 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
        } else {
            /* Prompt may have come from server. We've munged it a bit, so
             * we know it to be zero-terminated at least once. */
+           static int ret;
            c_write_untrusted(prompt, strlen(prompt));
            pos = 0;
-           ssh_send_ok = 1;
-           while (pos >= 0) {
+
+           setup_userpass_input(password, sizeof(password), 0);
+           do {
                crWaitUntil(!ispkt);
-               while (inlen--)
-                   switch (c = *in++) {
-                     case 10:
-                     case 13:
-                       password[pos] = 0;
-                       pos = -1;
-                       break;
-                     case 8:
-                     case 127:
-                       if (pos > 0)
-                           pos--;
-                       break;
-                     case 21:
-                     case 27:
-                       pos = 0;
-                       break;
-                     case 3:
-                     case 4:
-                       cleanup_exit(0);
-                       break;
-                     default:
-                       if (pos < sizeof(password)-1)
-                           password[pos++] = c;
-                       break;
-                   }
-           }
+               ret = process_userpass_input(in, inlen);
+           } while (ret == 0);
+           if (ret < 0)
+               cleanup_exit(0);
            c_write_str("\r\n");
        }
 
@@ -2837,7 +2910,7 @@ void sshfwd_close(struct ssh_channel *c)
                ssh2_pkt_send();
            }
        }
-       c->closes = 1;
+       c->closes = 1;                 /* sent MSG_CLOSE */
        if (c->type == CHAN_X11) {
            c->u.x11.s = NULL;
            logevent("Forwarded X11 connection terminated");
@@ -3054,7 +3127,8 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt)
     if (!cfg.nopty) {
        send_packet(SSH1_CMSG_REQUEST_PTY,
                    PKT_STR, cfg.termtype,
-                   PKT_INT, rows, PKT_INT, cols,
+                   PKT_INT, term ? term->rows : 24,
+                   PKT_INT, term ? term->cols : 80,
                    PKT_INT, 0, PKT_INT, 0, PKT_CHAR, 0, PKT_END);
        ssh_state = SSH_STATE_INTERMED;
        do {
@@ -3128,7 +3202,7 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt)
                pktin.type == SSH1_SMSG_STDERR_DATA) {
                long len = GET_32BIT(pktin.body);
                int bufsize =
-                   from_backend(pktin.type == SSH1_SMSG_STDERR_DATA,
+                   from_backend(frontend, pktin.type == SSH1_SMSG_STDERR_DATA,
                                 pktin.body + 4, len);
                if (!ssh1_stdout_throttling && bufsize > SSH1_BUFFER_LIMIT) {
                    ssh1_stdout_throttling = 1;
@@ -3293,13 +3367,11 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt)
                unsigned i = GET_32BIT(pktin.body);
                struct ssh_channel *c;
                c = find234(ssh_channels, &i, ssh_channelfind);
-               if (c) {
+               if (c && ((int)c->remoteid) != -1) {
                    int closetype;
                    closetype =
                        (pktin.type == SSH1_MSG_CHANNEL_CLOSE ? 1 : 2);
-                   if (!(c->closes & closetype))
-                       send_packet(pktin.type, PKT_INT, c->remoteid,
-                                   PKT_END);
+
                    if ((c->closes == 0) && (c->type == CHAN_X11)) {
                        logevent("Forwarded X11 connection terminated");
                        assert(c->u.x11.s != NULL);
@@ -3312,11 +3384,23 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt)
                        pfd_close(c->u.pfd.s);
                        c->u.pfd.s = NULL;
                    }
-                   c->closes |= closetype;
-                   if (c->closes == 3) {
+
+                   c->closes |= (closetype << 2);   /* seen this message */
+                   if (!(c->closes & closetype)) {
+                       send_packet(pktin.type, PKT_INT, c->remoteid,
+                                   PKT_END);
+                       c->closes |= closetype;      /* sent it too */
+                   }
+
+                   if (c->closes == 15) {
                        del234(ssh_channels, c);
                        sfree(c);
                    }
+               } else {
+                   bombout(("Received CHANNEL_CLOSE%s for %s channel %d\n",
+                            pktin.type == SSH1_MSG_CHANNEL_CLOSE ? "" :
+                            "_CONFIRMATION", c ? "half-open" : "nonexistent",
+                            i));
                }
            } else if (pktin.type == SSH1_MSG_CHANNEL_DATA) {
                /* Data sent down one of our channels. */
@@ -3469,14 +3553,16 @@ static void ssh2_mkkey(Bignum K, char *H, char *sessid, char chr,
     SHA_State s;
     /* First 20 bytes. */
     SHA_Init(&s);
-    sha_mpint(&s, K);
+    if (!(ssh_remote_bugs & BUG_SSH2_DERIVEKEY))
+       sha_mpint(&s, K);
     SHA_Bytes(&s, H, 20);
     SHA_Bytes(&s, &chr, 1);
     SHA_Bytes(&s, sessid, 20);
     SHA_Final(&s, keyspace);
     /* Next 20 bytes. */
     SHA_Init(&s);
-    sha_mpint(&s, K);
+    if (!(ssh_remote_bugs & BUG_SSH2_DERIVEKEY))
+       sha_mpint(&s, K);
     SHA_Bytes(&s, H, 20);
     SHA_Bytes(&s, keyspace, 20);
     SHA_Final(&s, keyspace + 20);
@@ -3560,7 +3646,7 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
     /*
      * Be prepared to work around the buggy MAC problem.
      */
-    if (cfg.buggymac || (ssh_remote_bugs & BUG_SSH2_HMAC))
+    if (ssh_remote_bugs & BUG_SSH2_HMAC)
        maclist = buggymacs, nmacs = lenof(buggymacs);
     else
        maclist = macs, nmacs = lenof(macs);
@@ -3575,6 +3661,9 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
     /* List key exchange algorithms. */
     ssh2_pkt_addstring_start();
     for (i = 0; i < lenof(kex_algs); i++) {
+       if (kex_algs[i] == &ssh_diffiehellman_gex &&
+           (ssh_remote_bugs & BUG_SSH2_DH_GEX))
+           continue;
        ssh2_pkt_addstring_str(kex_algs[i]->name);
        if (i < lenof(kex_algs) - 1)
            ssh2_pkt_addstring_str(",");
@@ -3681,6 +3770,9 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
     pktin.savedpos += 16;             /* skip garbage cookie */
     ssh2_pkt_getstring(&str, &len);    /* key exchange algorithms */
     for (i = 0; i < lenof(kex_algs); i++) {
+       if (kex_algs[i] == &ssh_diffiehellman_gex &&
+           (ssh_remote_bugs & BUG_SSH2_DH_GEX))
+           continue;
        if (in_commasep_string(kex_algs[i]->name, str, len)) {
            kex = kex_algs[i];
            break;
@@ -4092,13 +4184,9 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
     username[0] = '\0';
     got_username = FALSE;
     do {
-       static int pos;
-       static char c;
-
        /*
         * Get a username.
         */
-       pos = 0;
        if (got_username && !cfg.change_username) {
            /*
             * We got a username last time round this loop, and
@@ -4118,52 +4206,23 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                    crReturnV;
                }
            } else {
+               static int ret;
                c_write_str("login as: ");
                ssh_send_ok = 1;
-               while (pos >= 0) {
+               setup_userpass_input(username, sizeof(username), 1);
+               do {
                    crWaitUntilV(!ispkt);
-                   while (inlen--)
-                       switch (c = *in++) {
-                         case 10:
-                         case 13:
-                           username[pos] = 0;
-                           pos = -1;
-                           break;
-                         case 8:
-                         case 127:
-                           if (pos > 0) {
-                               c_write_str("\b \b");
-                               pos--;
-                           }
-                           break;
-                         case 21:
-                         case 27:
-                           while (pos > 0) {
-                               c_write_str("\b \b");
-                               pos--;
-                           }
-                           break;
-                         case 3:
-                         case 4:
-                           cleanup_exit(0);
-                           break;
-                         default:
-                           if (((c >= ' ' && c <= '~') ||
-                                ((unsigned char) c >= 160))
-                               && pos < sizeof(username)-1) {
-                               username[pos++] = c;
-                               c_write(&c, 1);
-                           }
-                           break;
-                       }
-               }
+                   ret = process_userpass_input(in, inlen);
+               } while (ret == 0);
+               if (ret < 0)
+                   cleanup_exit(0);
            }
            c_write_str("\r\n");
            username[strcspn(username, "\n\r")] = '\0';
        } else {
            char stuff[200];
-           strncpy(username, cfg.username, 99);
-           username[99] = '\0';
+           strncpy(username, cfg.username, sizeof(username));
+           username[sizeof(username)-1] = '\0';
            if ((flags & FLAG_VERBOSE) || (flags & FLAG_INTERACTIVE)) {
                sprintf(stuff, "Using username \"%s\".\r\n", username);
                c_write_str(stuff);
@@ -4325,6 +4384,14 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
            method = 0;
            ssh_pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
 
+           /*
+            * Most password/passphrase prompts will be
+            * non-echoing, so we set this to 0 by default.
+            * Exception is that some keyboard-interactive prompts
+            * can be echoing, in which case we'll set this to 1.
+            */
+           echo = 0;
+
            if (!method && can_pubkey && agent_exists() && !tried_agent) {
                /*
                 * Attempt public-key authentication using Pageant.
@@ -4579,11 +4646,11 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                    ssh2_pkt_getstring(&lang, &lang_len);
                    if (name_len > 0) {
                        c_write_untrusted(name, name_len);
-                       c_write_str("\n");
+                       c_write_str("\r\n");
                    }
                    if (inst_len > 0) {
                        c_write_untrusted(inst, inst_len);
-                       c_write_str("\n");
+                       c_write_str("\r\n");
                    }
                    num_prompts = ssh2_pkt_getuint32();
                }
@@ -4642,41 +4709,17 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                        crReturnV;
                    }
                } else {
-                   static int pos = 0;
-                   static char c;
-
+                   static int ret;
                    c_write_untrusted(pwprompt, strlen(pwprompt));
                    ssh_send_ok = 1;
 
-                   pos = 0;
-                   while (pos >= 0) {
+                   setup_userpass_input(password, sizeof(password), echo);
+                   do {
                        crWaitUntilV(!ispkt);
-                       while (inlen--)
-                           switch (c = *in++) {
-                             case 10:
-                             case 13:
-                               password[pos] = 0;
-                               pos = -1;
-                               break;
-                             case 8:
-                             case 127:
-                               if (pos > 0)
-                                   pos--;
-                               break;
-                             case 21:
-                             case 27:
-                               pos = 0;
-                               break;
-                             case 3:
-                             case 4:
-                               cleanup_exit(0);
-                               break;
-                             default:
-                               if (pos < sizeof(password)-1)
-                                   password[pos++] = c;
-                               break;
-                           }
-                   }
+                       ret = process_userpass_input(in, inlen);
+                   } while (ret == 0);
+                   if (ret < 0)
+                       cleanup_exit(0);
                    c_write_str("\r\n");
                }
            }
@@ -5103,8 +5146,8 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
        ssh2_pkt_addstring("pty-req");
        ssh2_pkt_addbool(1);           /* want reply */
        ssh2_pkt_addstring(cfg.termtype);
-       ssh2_pkt_adduint32(cols);
-       ssh2_pkt_adduint32(rows);
+       ssh2_pkt_adduint32(term ? term->cols : 80);
+       ssh2_pkt_adduint32(term ? term->rows : 24);
        ssh2_pkt_adduint32(0);         /* pixel width */
        ssh2_pkt_adduint32(0);         /* pixel height */
        ssh2_pkt_addstring_start();
@@ -5242,7 +5285,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                    switch (c->type) {
                      case CHAN_MAINSESSION:
                        bufsize =
-                           from_backend(pktin.type ==
+                           from_backend(frontend, pktin.type ==
                                         SSH2_MSG_CHANNEL_EXTENDED_DATA,
                                         data, length);
                        break;
@@ -5340,8 +5383,10 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                struct ssh_channel *c;
 
                c = find234(ssh_channels, &i, ssh_channelfind);
-               if (!c)
-                   continue;          /* nonexistent channel */
+               if (!c || ((int)c->remoteid) == -1) {
+                   bombout(("Received CHANNEL_CLOSE for %s channel %d\n",
+                            c ? "half-open" : "nonexistent", i));
+               }
                /* Do pre-close processing on the channel. */
                switch (c->type) {
                  case CHAN_MAINSESSION:
@@ -5665,7 +5710,8 @@ static void ssh2_protocol(unsigned char *in, int inlen, int ispkt)
  *
  * Returns an error message, or NULL on success.
  */
-static char *ssh_init(char *host, int port, char **realhost, int nodelay)
+static char *ssh_init(void *frontend_handle,
+                     char *host, int port, char **realhost, int nodelay)
 {
     char *p;
 
@@ -5674,6 +5720,8 @@ static char *ssh_init(char *host, int port, char **realhost, int nodelay)
        return "Microsoft high encryption pack not installed!";
 #endif
 
+    frontend = frontend_handle;
+
     ssh_send_ok = 0;
     ssh_editing = 0;
     ssh_echoing = 0;
@@ -5746,17 +5794,19 @@ static void ssh_size(void)
        break;
       case SSH_STATE_SESSION:
        if (!cfg.nopty) {
+           if (!term)
+               return;
            if (ssh_version == 1) {
                send_packet(SSH1_CMSG_WINDOW_SIZE,
-                           PKT_INT, rows, PKT_INT, cols,
+                           PKT_INT, term->rows, PKT_INT, term->cols,
                            PKT_INT, 0, PKT_INT, 0, PKT_END);
            } else {
                ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
                ssh2_pkt_adduint32(mainchan->remoteid);
                ssh2_pkt_addstring("window-change");
                ssh2_pkt_addbool(0);
-               ssh2_pkt_adduint32(cols);
-               ssh2_pkt_adduint32(rows);
+               ssh2_pkt_adduint32(term->cols);
+               ssh2_pkt_adduint32(term->rows);
                ssh2_pkt_adduint32(0);
                ssh2_pkt_adduint32(0);
                ssh2_pkt_send();
@@ -5795,7 +5845,8 @@ static void ssh_special(Telnet_Special code)
        if (ssh_state == SSH_STATE_CLOSED
            || ssh_state == SSH_STATE_PREPACKET) return;
        if (ssh_version == 1) {
-           send_packet(SSH1_MSG_IGNORE, PKT_STR, "", PKT_END);
+           if (!(ssh_remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE))
+               send_packet(SSH1_MSG_IGNORE, PKT_STR, "", PKT_END);
        } else {
            ssh2_pkt_init(SSH2_MSG_IGNORE);
            ssh2_pkt_addstring_start();