Debugging improvements. Started using Dave Hinton's dmemdump
[sgt/putty] / ssh.c
diff --git a/ssh.c b/ssh.c
index e41176f..b3be8e4 100644 (file)
--- a/ssh.c
+++ b/ssh.c
 #define TRUE 1
 #endif
 
+/* uncomment this for packet level debugging */
+/* #define DUMP_PACKETS */
+
 #define logevent(s) { logevent(s); \
                       if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)) \
-                      fprintf(stderr, "%s\n", s); }
+                      { fprintf(stderr, "%s\n", s); fflush(stderr); } }
 
 #define bombout(msg) ( ssh_state = SSH_STATE_CLOSED, \
                           (s ? sk_close(s), s = NULL : 0), \
 #define SSH1_AUTH_TIS                             5    /* 0x5 */
 #define SSH1_AUTH_CCARD                           16   /* 0x10 */
 
+#define SSH1_PROTOFLAG_SCREEN_NUMBER              1    /* 0x1 */
+/* Mask for protoflags we will echo back to server if seen */
+#define SSH1_PROTOFLAGS_SUPPORTED                 0    /* 0x1 */
+
 #define SSH2_MSG_DISCONNECT                       1    /* 0x1 */
 #define SSH2_MSG_IGNORE                           2    /* 0x2 */
 #define SSH2_MSG_UNIMPLEMENTED                    3    /* 0x3 */
 #define SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE   9    /* 0x9 */
 #define SSH2_DISCONNECT_CONNECTION_LOST           10   /* 0xa */
 #define SSH2_DISCONNECT_BY_APPLICATION            11   /* 0xb */
+#define SSH2_DISCONNECT_TOO_MANY_CONNECTIONS      12   /* 0xc */
+#define SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER    13   /* 0xd */
+#define SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE 14 /* 0xe */
+#define SSH2_DISCONNECT_ILLEGAL_USER_NAME         15   /* 0xf */
+
+static const char *const ssh2_disconnect_reasons[] = {
+    NULL,
+    "SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT",
+    "SSH_DISCONNECT_PROTOCOL_ERROR",
+    "SSH_DISCONNECT_KEY_EXCHANGE_FAILED",
+    "SSH_DISCONNECT_HOST_AUTHENTICATION_FAILED",
+    "SSH_DISCONNECT_MAC_ERROR",
+    "SSH_DISCONNECT_COMPRESSION_ERROR",
+    "SSH_DISCONNECT_SERVICE_NOT_AVAILABLE",
+    "SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED",
+    "SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE",
+    "SSH_DISCONNECT_CONNECTION_LOST",
+    "SSH_DISCONNECT_BY_APPLICATION",
+    "SSH_DISCONNECT_TOO_MANY_CONNECTIONS",
+    "SSH_DISCONNECT_AUTH_CANCELLED_BY_USER",
+    "SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE",
+    "SSH_DISCONNECT_ILLEGAL_USER_NAME",
+};
 
 #define SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED     1    /* 0x1 */
 #define SSH2_OPEN_CONNECT_FAILED                  2    /* 0x2 */
 
 #define SSH2_EXTENDED_DATA_STDERR                 1    /* 0x1 */
 
+/*
+ * Various remote-bug flags.
+ */
+#define BUG_CHOKES_ON_SSH1_IGNORE                 1
+#define BUG_SSH2_HMAC                             2
+
 #define GET_32BIT(cp) \
     (((unsigned long)(unsigned char)(cp)[0] << 24) | \
     ((unsigned long)(unsigned char)(cp)[1] << 16) | \
@@ -173,9 +209,7 @@ const static struct ssh2_ciphers *ciphers[] = {
 };
 
 const static struct ssh_kex *kex_algs[] = {
-#ifdef DO_DIFFIE_HELLMAN_GEX
     &ssh_diffiehellman_gex,
-#endif
     &ssh_diffiehellman };
 
 const static struct ssh_signkey *hostkey_algs[] = { &ssh_rsa, &ssh_dss };
@@ -196,10 +230,12 @@ static int ssh_comp_none_block(unsigned char *block, int len,
                               unsigned char **outblock, int *outlen) {
     return 0;
 }
+static int ssh_comp_none_disable(void) { return 0; }
 const static struct ssh_compress ssh_comp_none = {
     "none",
     ssh_comp_none_init, ssh_comp_none_block,
-    ssh_comp_none_init, ssh_comp_none_block
+    ssh_comp_none_init, ssh_comp_none_block,
+    ssh_comp_none_disable
 };
 extern const struct ssh_compress ssh_zlib;
 const static struct ssh_compress *compressions[] = {
@@ -250,8 +286,11 @@ static Socket s = NULL;
 
 static unsigned char session_key[32];
 static int ssh1_compressing;
+static int ssh1_remote_protoflags;
+static int ssh1_local_protoflags;
 static int ssh_agentfwd_enabled;
 static int ssh_X11_fwd_enabled;
+static int ssh_remote_bugs;
 static const struct ssh_cipher *cipher = NULL;
 static const struct ssh2_cipher *cscipher = NULL;
 static const struct ssh2_cipher *sccipher = NULL;
@@ -262,7 +301,8 @@ static const struct ssh_compress *sccomp = NULL;
 static const struct ssh_kex *kex = NULL;
 static const struct ssh_signkey *hostkey = NULL;
 static unsigned char ssh2_session_id[20];
-int (*ssh_get_password)(const char *prompt, char *str, int maxlen) = NULL;
+int (*ssh_get_line)(const char *prompt, char *str, int maxlen,
+                    int is_pw) = NULL;
 
 static char *savedhost;
 static int savedport;
@@ -328,6 +368,42 @@ static int ssh_channelfind(void *av, void *bv) {
     return 0;
 }
 
+static int alloc_channel_id(void) {
+    const unsigned CHANNEL_NUMBER_OFFSET = 256;
+    unsigned low, high, mid;
+    int tsize;
+    struct ssh_channel *c;
+
+    /*
+     * First-fit allocation of channel numbers: always pick the
+     * lowest unused one. To do this, binary-search using the
+     * counted B-tree to find the largest channel ID which is in a
+     * contiguous sequence from the beginning. (Precisely
+     * everything in that sequence must have ID equal to its tree
+     * index plus CHANNEL_NUMBER_OFFSET.)
+     */
+    tsize = count234(ssh_channels);
+
+    low = -1; high = tsize;
+    while (high - low > 1) {
+       mid = (high + low) / 2;
+       c = index234(ssh_channels, mid);
+       if (c->localid == mid + CHANNEL_NUMBER_OFFSET)
+           low = mid;                 /* this one is fine */
+       else
+           high = mid;                /* this one is past it */
+    }
+    /*
+     * Now low points to either -1, or the tree index of the
+     * largest ID in the initial sequence.
+     */
+    {
+       unsigned i = low + 1 + CHANNEL_NUMBER_OFFSET;
+       assert(NULL == find234(ssh_channels, &i, ssh_channelfind));
+    }
+    return low + 1 + CHANNEL_NUMBER_OFFSET;
+}
+
 static void c_write (char *buf, int len) {
     if ((flags & FLAG_STDERR)) {
         int i;
@@ -339,6 +415,16 @@ static void c_write (char *buf, int len) {
     from_backend(1, buf, len);
 }
 
+static void c_write_untrusted(char *buf, int len) {
+    int i;
+    for (i = 0; i < len; i++) {
+        if (buf[i] == '\n')
+            c_write("\r\n", 2);
+        else if ((buf[i] & 0x60) || (buf[i] == '\r'))
+            c_write(buf+i, 1);
+    }
+}
+
 static void c_write_str (char *buf) {
     c_write(buf, strlen(buf));
 }
@@ -404,11 +490,9 @@ next_packet:
 
     if (cipher)
        cipher->decrypt(pktin.data, st->biglen);
-#if 0
-    debug(("Got packet len=%d pad=%d\r\n", st->len, st->pad));
-    for (st->i = 0; st->i < st->biglen; st->i++)
-        debug(("  %02x", (unsigned char)pktin.data[st->i]));
-    debug(("\r\n"));
+#ifdef DUMP_PACKETS
+    debug(("Got packet len=%d pad=%d\n", st->len, st->pad));
+    dmemdump(pktin.data, st->biglen);
 #endif
 
     st->realcrc = crc32(pktin.data, st->biglen-4);
@@ -423,12 +507,9 @@ next_packet:
     if (ssh1_compressing) {
        unsigned char *decompblk;
        int decomplen;
-#if 0
-       int i;
+#ifdef DUMP_PACKETS
        debug(("Packet payload pre-decompression:\n"));
-       for (i = -1; i < pktin.length; i++)
-           debug(("  %02x", (unsigned char)pktin.body[i]));
-       debug(("\r\n"));
+       dmemdump(pktin.body-1, pktin.length+1);
 #endif
        zlib_decompress_block(pktin.body-1, pktin.length+1,
                              &decompblk, &decomplen);
@@ -444,11 +525,9 @@ next_packet:
        memcpy(pktin.body-1, decompblk, decomplen);
        sfree(decompblk);
        pktin.length = decomplen-1;
-#if 0
+#ifdef DUMP_PACKETS
        debug(("Packet payload post-decompression:\n"));
-       for (i = -1; i < pktin.length; i++)
-           debug(("  %02x", (unsigned char)pktin.body[i]));
-       debug(("\r\n"));
+       dmemdump(pktin.body-1, pktin.length+1);
 #endif
     }
 
@@ -481,6 +560,20 @@ next_packet:
        goto next_packet;
     }
 
+    if (pktin.type == SSH1_MSG_DISCONNECT) {
+       /* log reason code in disconnect message */
+       char buf[256];
+       unsigned msglen = GET_32BIT(pktin.body);
+       unsigned nowlen;
+       strcpy(buf, "Remote sent disconnect: ");
+       nowlen = strlen(buf);
+       if (msglen > sizeof(buf)-nowlen-1)
+           msglen = sizeof(buf)-nowlen-1;
+       memcpy(buf+nowlen, pktin.body+4, msglen);
+       buf[nowlen+msglen] = '\0';
+       logevent(buf);
+    }
+
     crFinish(0);
 }
 
@@ -570,11 +663,9 @@ next_packet:
         sccipher->decrypt(pktin.data + st->cipherblk,
                           st->packetlen - st->cipherblk);
 
-#if 0
-    debug(("Got packet len=%d pad=%d\r\n", st->len, st->pad));
-    for (st->i = 0; st->i < st->packetlen; st->i++)
-        debug(("  %02x", (unsigned char)pktin.data[st->i]));
-    debug(("\r\n"));
+#ifdef DUMP_PACKETS
+    debug(("Got packet len=%d pad=%d\n", st->len, st->pad));
+    dmemdump(pktin.data, st->packetlen);
 #endif
 
     /*
@@ -603,11 +694,9 @@ next_packet:
            }
            pktin.length = 5 + newlen;
            memcpy(pktin.data+5, newpayload, newlen);
-#if 0
-           debug(("Post-decompression payload:\r\n"));
-           for (st->i = 0; st->i < newlen; st->i++)
-               debug(("  %02x", (unsigned char)pktin.data[5+st->i]));
-           debug(("\r\n"));
+#ifdef DUMP_PACKETS
+           debug(("Post-decompression payload:\n"));
+           dmemdump(pktin.data+5, newlen);
 #endif
 
            sfree(newpayload);
@@ -620,6 +709,28 @@ next_packet:
     if (pktin.type == SSH2_MSG_IGNORE || pktin.type == SSH2_MSG_DEBUG)
         goto next_packet;              /* FIXME: print DEBUG message */
 
+    if (pktin.type == SSH2_MSG_DISCONNECT) {
+       /* log reason code in disconnect message */
+       char buf[256];
+       int reason = GET_32BIT(pktin.data+6);
+       unsigned msglen = GET_32BIT(pktin.data+10);
+       unsigned nowlen;
+       if (reason > 0 && reason < lenof(ssh2_disconnect_reasons)) {
+           sprintf(buf, "Received disconnect message (%s)",
+                   ssh2_disconnect_reasons[reason]);
+       } else {
+           sprintf(buf, "Received disconnect message (unknown type %d)", reason);
+       }
+       logevent(buf);
+       strcpy(buf, "Disconnection message text: ");
+       nowlen = strlen(buf);
+       if (msglen > sizeof(buf)-nowlen-1)
+           msglen = sizeof(buf)-nowlen-1;
+       memcpy(buf+nowlen, pktin.data+14, msglen);
+       buf[nowlen+msglen] = '\0';
+       logevent(buf);
+    }
+
     crFinish(0);
 }
 
@@ -659,25 +770,22 @@ static int s_wrpkt_prepare(void) {
 
     pktout.body[-1] = pktout.type;
 
+#ifdef DUMP_PACKETS
+    debug(("Packet payload pre-compression:\n"));
+    dmemdump(pktout.body-1, pktout.length+1);
+#endif
+
     if (ssh1_compressing) {
        unsigned char *compblk;
        int complen;
-#if 0
-       debug(("Packet payload pre-compression:\n"));
-       for (i = -1; i < pktout.length; i++)
-           debug(("  %02x", (unsigned char)pktout.body[i]));
-       debug(("\r\n"));
-#endif
        zlib_compress_block(pktout.body-1, pktout.length+1,
                            &compblk, &complen);
        ssh1_pktout_size(complen-1);
        memcpy(pktout.body-1, compblk, complen);
        sfree(compblk);
-#if 0
+#ifdef DUMP_PACKETS
        debug(("Packet payload post-compression:\n"));
-       for (i = -1; i < pktout.length; i++)
-           debug(("  %02x", (unsigned char)pktout.body[i]));
-       debug(("\r\n"));
+       dmemdump(pktout.body-1, pktout.length+1);
 #endif
     }
 
@@ -691,11 +799,9 @@ static int s_wrpkt_prepare(void) {
     PUT_32BIT(pktout.data+biglen, crc);
     PUT_32BIT(pktout.data, len);
 
-#if 0
-    debug(("Sending packet len=%d\r\n", biglen+4));
-    for (i = 0; i < biglen+4; i++)
-        debug(("  %02x", (unsigned char)pktout.data[i]));
-    debug(("\r\n"));
+#ifdef DUMP_PACKETS
+    debug(("Sending packet len=%d\n", biglen+4));
+    dmemdump(pktout.data, biglen+4);
 #endif
     if (cipher)
        cipher->encrypt(pktout.data+4, biglen);
@@ -898,7 +1004,7 @@ static void ssh2_pkt_addstring(char *data) {
 }
 static char *ssh2_mpint_fmt(Bignum b, int *len) {
     unsigned char *p;
-    int i, n = (ssh1_bignum_bitcount(b)+7)/8;
+    int i, n = (bignum_bitcount(b)+7)/8;
     p = smalloc(n + 1);
     if (!p)
         fatalbox("out of memory");
@@ -933,11 +1039,9 @@ static int ssh2_pkt_construct(void) {
     /*
      * Compress packet payload.
      */
-#if 0
-    debug(("Pre-compression payload:\r\n"));
-    for (i = 5; i < pktout.length; i++)
-       debug(("  %02x", (unsigned char)pktout.data[i]));
-    debug(("\r\n"));
+#ifdef DUMP_PACKETS
+    debug(("Pre-compression payload:\n"));
+    dmemdump(pktout.data+5, pktout.length-5);
 #endif
     {
        unsigned char *newpayload;
@@ -969,11 +1073,9 @@ static int ssh2_pkt_construct(void) {
                         outgoing_sequence);
     outgoing_sequence++;               /* whether or not we MACed */
 
-#if 0
-    debug(("Sending packet len=%d\r\n", pktout.length+padding));
-    for (i = 0; i < pktout.length+padding; i++)
-        debug(("  %02x", (unsigned char)pktout.data[i]));
-    debug(("\r\n"));
+#ifdef DUMP_PACKETS
+    debug(("Sending packet len=%d\n", pktout.length+padding));
+    dmemdump(pktout.data, pktout.length+padding);
 #endif
 
     if (cscipher)
@@ -1031,7 +1133,7 @@ void bndebug(char *string, Bignum b) {
     debug(("%s", string));
     for (i = 0; i < len; i++)
         debug((" %02x", p[i]));
-    debug(("\r\n"));
+    debug(("\n"));
     sfree(p);
 }
 #endif
@@ -1090,11 +1192,49 @@ static Bignum ssh2_pkt_getmp(void) {
     return b;
 }
 
+/*
+ * Examine the remote side's version string and compare it against
+ * a list of known buggy implementations.
+ */
+static void ssh_detect_bugs(char *vstring) {
+    char *imp;                         /* pointer to implementation part */
+    imp = vstring;
+    imp += strcspn(imp, "-");
+    if (*imp) imp++;
+    imp += strcspn(imp, "-");
+    if (*imp) imp++;
+
+    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")) {
+        /*
+         * These versions don't support SSH1_MSG_IGNORE, so we have
+         * to use a different defence against password length
+         * sniffing.
+         */
+        ssh_remote_bugs |= BUG_CHOKES_ON_SSH1_IGNORE;
+        logevent("We believe remote version has SSH1 ignore bug");
+    }
+
+    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)) {
+        /*
+         * These versions have the HMAC bug.
+         */
+        ssh_remote_bugs |= BUG_SSH2_HMAC;
+        logevent("We believe remote version has SSH2 HMAC bug");
+    }
+}
+
 static int do_ssh_init(unsigned char c) {
-    static char *vsp;
+    static char vslen;
     static char version[10];
-    static char vstring[80];
-    static char vlog[sizeof(vstring)+20];
+    static char *vstring;
+    static int vstrsize;
+    static char *vlog;
     static int i;
 
     crBegin;
@@ -1114,13 +1254,18 @@ static int do_ssh_init(unsigned char c) {
        crReturn(1);                   /* get another character */
     }
 
+    vstring = smalloc(16);
+    vstrsize = 16;
     strcpy(vstring, "SSH-");
-    vsp = vstring+4;
+    vslen = 4;
     i = 0;
     while (1) {
        crReturn(1);                   /* get another char */
-       if (vsp < vstring+sizeof(vstring)-1)
-           *vsp++ = c;
+       if (vslen >= vstrsize-1) {
+            vstrsize += 16;
+            vstring = srealloc(vstring, vstrsize);
+        }
+        vstring[vslen++] = c;
        if (i >= 0) {
            if (c == '-') {
                version[i] = '\0';
@@ -1135,10 +1280,13 @@ static int do_ssh_init(unsigned char c) {
     ssh_agentfwd_enabled = FALSE;
     rdpkt2_state.incoming_sequence = 0;
 
-    *vsp = 0;
+    vstring[vslen] = 0;
+    vlog = smalloc(20 + vslen);
     sprintf(vlog, "Server version: %s", vstring);
+    ssh_detect_bugs(vstring);
     vlog[strcspn(vlog, "\r\n")] = '\0';
     logevent(vlog);
+    sfree(vlog);
 
     /*
      * Server version "1.99" means we can choose whether we use v1
@@ -1148,18 +1296,19 @@ static int do_ssh_init(unsigned char c) {
         /*
          * This is a v2 server. Begin v2 protocol.
          */
-        char *verstring = "SSH-2.0-PuTTY";
+        char verstring[80], vlog[100];
+        sprintf(verstring, "SSH-2.0-%s", sshver);
         SHA_Init(&exhashbase);
         /*
          * Hash our version string and their version string.
          */
         sha_string(&exhashbase, verstring, strlen(verstring));
         sha_string(&exhashbase, vstring, strcspn(vstring, "\r\n"));
-        sprintf(vstring, "%s\n", verstring);
         sprintf(vlog, "We claim version: %s", verstring);
         logevent(vlog);
+        strcat(verstring, "\n");
         logevent("Using SSH protocol version 2");
-        sk_write(s, vstring, strlen(vstring));
+        sk_write(s, verstring, strlen(verstring));
         ssh_protocol = ssh2_protocol;
         ssh_version = 2;
         s_rdpkt = ssh2_rdpkt;
@@ -1167,19 +1316,23 @@ static int do_ssh_init(unsigned char c) {
         /*
          * This is a v1 server. Begin v1 protocol.
          */
-        sprintf(vstring, "SSH-%s-PuTTY\n",
-                (ssh_versioncmp(version, "1.5") <= 0 ? version : "1.5"));
-        sprintf(vlog, "We claim version: %s", vstring);
-        vlog[strcspn(vlog, "\r\n")] = '\0';
+        char verstring[80], vlog[100];
+        sprintf(verstring, "SSH-%s-%s",
+                (ssh_versioncmp(version, "1.5") <= 0 ? version : "1.5"),
+                sshver);
+        sprintf(vlog, "We claim version: %s", verstring);
         logevent(vlog);
+        strcat(verstring, "\n");
         logevent("Using SSH protocol version 1");
-        sk_write(s, vstring, strlen(vstring));
+        sk_write(s, verstring, strlen(verstring));
         ssh_protocol = ssh1_protocol;
         ssh_version = 1;
         s_rdpkt = ssh1_rdpkt;
     }
     ssh_state = SSH_STATE_BEFORE_SIZE;
 
+    sfree(vstring);
+
     crFinish(0);
 }
 
@@ -1225,21 +1378,20 @@ static void ssh_gotdata(unsigned char *data, int datalen)
     crFinishV;
 }
 
-static int ssh_receive(Socket skt, int urgent, char *data, int len) {
-    if (urgent==3) {
+static int ssh_closing (Plug plug, char *error_msg, int error_code, int calling_back) {
+    ssh_state = SSH_STATE_CLOSED;
+    sk_close(s);
+    s = NULL;
+    if (error_msg) {
         /* A socket error has occurred. */
-        ssh_state = SSH_STATE_CLOSED;
-        sk_close(s);
-        s = NULL;
-        connection_fatal(data);
-        return 0;
-    } else if (!len) {
-       /* Connection has closed. */
-       ssh_state = SSH_STATE_CLOSED;
-       sk_close(s);
-       s = NULL;
-       return 0;
+        connection_fatal (error_msg);
+    } else {
+       /* Otherwise, the remote side closed the connection normally. */
     }
+    return 0;
+}
+
+static int ssh_receive(Plug plug, int urgent, char *data, int len) {
     ssh_gotdata (data, len);
     if (ssh_state == SSH_STATE_CLOSED) {
         if (s) {
@@ -1258,6 +1410,11 @@ static int ssh_receive(Socket skt, int urgent, char *data, int len) {
  */
 static char *connect_to_host(char *host, int port, char **realhost)
 {
+    static struct plug_function_table fn_table = {
+       ssh_closing,
+       ssh_receive
+    }, *fn_table_ptr = &fn_table;
+
     SockAddr addr;
     char *err;
 #ifdef FWHACK
@@ -1295,7 +1452,7 @@ static char *connect_to_host(char *host, int port, char **realhost)
     /*
      * Open socket.
      */
-    s = sk_new(addr, port, 0, 1, ssh_receive);
+    s = sk_new(addr, port, 0, 1, &fn_table_ptr);
     if ( (err = sk_socket_error(s)) )
        return err;
 
@@ -1357,9 +1514,13 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
        logevent(logmsg);
     }
 
+    ssh1_remote_protoflags = GET_32BIT(pktin.body+8+i+j);
     supported_ciphers_mask = GET_32BIT(pktin.body+12+i+j);
     supported_auths_mask = GET_32BIT(pktin.body+16+i+j);
 
+    ssh1_local_protoflags = ssh1_remote_protoflags & SSH1_PROTOFLAGS_SUPPORTED;
+    ssh1_local_protoflags |= SSH1_PROTOFLAG_SCREEN_NUMBER;
+
     MD5Init(&md5c);
     MD5Update(&md5c, keystr2, hostkey.bytes);
     MD5Update(&md5c, keystr1, servkey.bytes);
@@ -1421,6 +1582,11 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
     if ((supported_ciphers_mask & (1 << cipher_type)) == 0) {
        c_write_str("Selected cipher not supported, falling back to 3DES\r\n");
        cipher_type = SSH_CIPHER_3DES;
+       if ((supported_ciphers_mask & (1 << cipher_type)) == 0) {
+           bombout(("Server violates SSH 1 protocol by "
+                    "not supporting 3DES encryption"));
+           crReturn(0);
+       }
     }
     switch (cipher_type) {
       case SSH_CIPHER_3DES: logevent("Using 3DES encryption"); break;
@@ -1433,7 +1599,7 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
                 PKT_DATA, cookie, 8,
                 PKT_CHAR, (len*8) >> 8, PKT_CHAR, (len*8) & 0xFF,
                 PKT_DATA, rsabuf, len,
-                PKT_INT, 0,
+                PKT_INT, ssh1_local_protoflags,
                 PKT_END);
 
     logevent("Trying to enable encryption...");
@@ -1459,43 +1625,56 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
        static int pos = 0;
        static char c;
        if ((flags & FLAG_INTERACTIVE) && !*cfg.username) {
-           c_write_str("login as: ");
-            ssh_send_ok = 1;
-           while (pos >= 0) {
-               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:
-                   random_save_seed();
-                   exit(0);
-                   break;
-                 default:
-                   if (((c >= ' ' && c <= '~') ||
-                         ((unsigned char)c >= 160)) && pos < 40) {
-                       username[pos++] = c;
-                       c_write(&c, 1);
-                   }
-                   break;
-               }
-           }
-           c_write_str("\r\n");
-           username[strcspn(username, "\n\r")] = '\0';
-       } else {
+            if (ssh_get_line) {
+                if (!ssh_get_line("login as: ",
+                                  username, sizeof(username), FALSE)) {
+                    /*
+                     * get_line failed to get a username.
+                     * Terminate.
+                     */
+                    logevent("No username provided. Abandoning session.");
+                    ssh_state = SSH_STATE_CLOSED;
+                    crReturn(1);
+                }
+            } else {
+                c_write_str("login as: ");
+                ssh_send_ok = 1;
+                while (pos >= 0) {
+                    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:
+                        random_save_seed();
+                        exit(0);
+                        break;
+                      default:
+                        if (((c >= ' ' && c <= '~') ||
+                             ((unsigned char)c >= 160)) && pos < 40) {
+                            username[pos++] = c;
+                            c_write(&c, 1);
+                        }
+                        break;
+                    }
+                }
+                c_write_str("\r\n");
+                username[strcspn(username, "\n\r")] = '\0';
+            }
+        } else {
            strncpy(username, cfg.username, 99);
            username[99] = '\0';
        }
@@ -1546,10 +1725,11 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
             request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES;
             agent_query(request, 5, &r, &responselen);
             response = (unsigned char *)r;
-            if (response) {
+            if (response && responselen >= 5 &&
+                response[4] == SSH1_AGENT_RSA_IDENTITIES_ANSWER) {
                 p = response + 5;
                 nkeys = GET_32BIT(p); p += 4;
-                { char buf[64]; sprintf(buf, "Pageant has %d keys", nkeys);
+                { char buf[64]; sprintf(buf, "Pageant has %d SSH1 keys", nkeys);
                     logevent(buf); }
                 for (i = 0; i < nkeys; i++) {
                     static struct RSAKey key;
@@ -1586,7 +1766,7 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
                         PUT_32BIT(agentreq, len);
                         q = agentreq + 4;
                         *q++ = SSH1_AGENTC_RSA_CHALLENGE;
-                        PUT_32BIT(q, ssh1_bignum_bitcount(key.modulus));
+                        PUT_32BIT(q, bignum_bitcount(key.modulus));
                         q += 4;
                         q += ssh1_write_bignum(q, key.exponent);
                         q += ssh1_write_bignum(q, key.modulus);
@@ -1697,13 +1877,12 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
             sfree(comment);
         }
 
-       if (ssh_get_password) {
-           if (!ssh_get_password(prompt, password, sizeof(password))) {
+       if (ssh_get_line) {
+           if (!ssh_get_line(prompt, password, sizeof(password), TRUE)) {
                 /*
-                 * get_password failed to get a password (for
-                 * example because one was supplied on the command
-                 * line which has already failed to work).
-                 * Terminate.
+                 * get_line failed to get a password (for example
+                 * because one was supplied on the command line
+                 * which has already failed to work). Terminate.
                  */
                 logevent("No more passwords to try");
                 ssh_state = SSH_STATE_CLOSED;
@@ -1835,38 +2014,65 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
                  *    N+7. This won't obscure the order of
                  *    magnitude of the password length, but it will
                  *    introduce a bit of extra uncertainty.
+                 * 
+                 * A few servers (the old 1.2.18 through 1.2.22)
+                 * can't deal with SSH1_MSG_IGNORE. For these
+                 * servers, we need an alternative defence. We make
+                 * use of the fact that the password is interpreted
+                 * as a C string: so we can append a NUL, then some
+                 * random data.
                  */
-                int bottom, top, pwlen, i;
-                char *randomstr;
-
-                pwlen = strlen(password);
-                if (pwlen < 16) {
-                    bottom = 1;
-                    top = 15;
+                if (ssh_remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE) {
+                    char string[64];
+                    char *s;
+                    int len;
+
+                    len = strlen(password);
+                    if (len < sizeof(string)) {
+                        s = string;
+                        strcpy(string, password);
+                        len++;         /* cover the zero byte */
+                        while (len < sizeof(string)) {
+                            string[len++] = (char)random_byte();
+                        }
+                    } else {
+                        s = password;
+                    }
+                    send_packet(pwpkt_type, PKT_INT, len,
+                                PKT_DATA, s, len, PKT_END);
                 } else {
-                    bottom = pwlen &~ 7;
-                    top = bottom + 7;
-                }
+                    int bottom, top, pwlen, i;
+                    char *randomstr;
 
-                assert(pwlen >= bottom && pwlen <= top);
+                    pwlen = strlen(password);
+                    if (pwlen < 16) {
+                        bottom = 0;    /* zero length passwords are OK! :-) */
+                        top = 15;
+                    } else {
+                        bottom = pwlen &~ 7;
+                        top = bottom + 7;
+                    }
 
-                randomstr = smalloc(top+1);
+                    assert(pwlen >= bottom && pwlen <= top);
 
-                for (i = bottom; i <= top; i++) {
-                    if (i == pwlen)
-                        defer_packet(pwpkt_type, PKT_STR, password, PKT_END);
-                    else {
-                        for (j = 0; j < i; j++) {
-                            do {
-                                randomstr[j] = random_byte();
-                            } while (randomstr[j] == '\0');
+                    randomstr = smalloc(top+1);
+
+                    for (i = bottom; i <= top; i++) {
+                        if (i == pwlen)
+                            defer_packet(pwpkt_type, PKT_STR, password, PKT_END);
+                        else {
+                            for (j = 0; j < i; j++) {
+                                do {
+                                    randomstr[j] = random_byte();
+                                } while (randomstr[j] == '\0');
+                            }
+                            randomstr[i] = '\0';
+                            defer_packet(SSH1_MSG_IGNORE,
+                                         PKT_STR, randomstr, PKT_END);
                         }
-                        randomstr[i] = '\0';
-                        defer_packet(SSH1_MSG_IGNORE,
-                                     PKT_STR, randomstr, PKT_END);
                     }
+                    ssh_pkt_defersend();
                 }
-                ssh_pkt_defersend();
             } else {
                 send_packet(pwpkt_type, PKT_STR, password, PKT_END);
             }
@@ -1894,7 +2100,7 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
 }
 
 void sshfwd_close(struct ssh_channel *c) {
-    if (c) {
+    if (c && !c->closes) {
         if (ssh_version == 1) {
             send_packet(SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid, PKT_END);
         } else {
@@ -1953,10 +2159,16 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) {
         char proto[20], data[64];
         logevent("Requesting X11 forwarding");
         x11_invent_auth(proto, sizeof(proto), data, sizeof(data));
-        send_packet(SSH1_CMSG_X11_REQUEST_FORWARDING, 
-                   PKT_STR, proto, PKT_STR, data,
-                   PKT_INT, 0,
-                   PKT_END);
+        if (ssh1_local_protoflags & SSH1_PROTOFLAG_SCREEN_NUMBER) {
+            send_packet(SSH1_CMSG_X11_REQUEST_FORWARDING,
+                        PKT_STR, proto, PKT_STR, data,
+                        PKT_INT, 0,
+                        PKT_END);
+        } else {
+            send_packet(SSH1_CMSG_X11_REQUEST_FORWARDING,
+                        PKT_STR, proto, PKT_STR, data,
+                        PKT_END);
+        }
         do { crReturnV; } while (!ispkt);
         if (pktin.type != SSH1_SMSG_SUCCESS && pktin.type != SSH1_SMSG_FAILURE) {
             bombout(("Protocol confusion"));
@@ -2005,8 +2217,8 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) {
        zlib_decompress_init();
     }
 
-    if (*cfg.remote_cmd)
-        send_packet(SSH1_CMSG_EXEC_CMD, PKT_STR, cfg.remote_cmd, PKT_END);
+    if (*cfg.remote_cmd_ptr)
+        send_packet(SSH1_CMSG_EXEC_CMD, PKT_STR, cfg.remote_cmd_ptr, PKT_END);
     else
         send_packet(SSH1_CMSG_EXEC_SHELL, PKT_END);
     logevent("Started session");
@@ -2035,9 +2247,7 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) {
             } else if (pktin.type == SSH1_SMSG_X11_OPEN) {
                 /* Remote side is trying to open a channel to talk to our
                  * X-Server. Give them back a local channel number. */
-                unsigned i;
-                struct ssh_channel *c, *d;
-                enum234 e;
+                struct ssh_channel *c;
 
                logevent("Received X11 connect request");
                /* Refuse if X11 forwarding is disabled. */
@@ -2057,13 +2267,8 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) {
                                  PKT_END);
                    } else {
                      logevent("opening X11 forward connection succeeded");
-                     for (i=1, d = first234(ssh_channels, &e); d; d = next234(&e)) {
-                          if (d->localid > i)
-                              break;     /* found a free number */
-                          i = d->localid + 1;
-                     }
                      c->remoteid = GET_32BIT(pktin.body);
-                     c->localid = i;
+                     c->localid = alloc_channel_id();
                      c->closes = 0;
                      c->type = CHAN_X11;   /* identify channel type */
                      add234(ssh_channels, c);
@@ -2076,9 +2281,7 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) {
             } else if (pktin.type == SSH1_SMSG_AGENT_OPEN) {
                 /* Remote side is trying to open a channel to talk to our
                  * agent. Give them back a local channel number. */
-                unsigned i;
                 struct ssh_channel *c;
-                enum234 e;
 
                /* Refuse if agent forwarding is disabled. */
                if (!ssh_agentfwd_enabled) {
@@ -2086,15 +2289,9 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) {
                                PKT_INT, GET_32BIT(pktin.body),
                                PKT_END);
                } else {
-                   i = 1;
-                   for (c = first234(ssh_channels, &e); c; c = next234(&e)) {
-                       if (c->localid > i)
-                           break;     /* found a free number */
-                       i = c->localid + 1;
-                   }
                    c = smalloc(sizeof(struct ssh_channel));
                    c->remoteid = GET_32BIT(pktin.body);
-                   c->localid = i;
+                   c->localid = alloc_channel_id();
                    c->closes = 0;
                    c->type = CHAN_AGENT;   /* identify channel type */
                    c->u.a.lensofar = 0;
@@ -2112,7 +2309,8 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) {
                 if (c) {
                     int closetype;
                     closetype = (pktin.type == SSH1_MSG_CHANNEL_CLOSE ? 1 : 2);
-                    send_packet(pktin.type, PKT_INT, c->remoteid, PKT_END);
+                    if (!(c->closes & closetype))
+                        send_packet(pktin.type, PKT_INT, c->remoteid, PKT_END);
                    if ((c->closes == 0) && (c->type == CHAN_X11)) {
                        logevent("X11 connection closed");
                        assert(c->u.x11.s != NULL);
@@ -2258,7 +2456,7 @@ static void ssh2_mkkey(Bignum K, char *H, char *sessid, char chr, char *keyspace
  */
 static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
 {
-    static int i, j, len, nbits;
+    static int i, j, len, nbits, pbits;
     static char *str;
     static Bignum p, g, e, f, K;
     static int kex_init_value, kex_reply_value;
@@ -2307,7 +2505,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)
+    if (cfg.buggymac || (ssh_remote_bugs & BUG_SSH2_HMAC))
         maclist = buggymacs, nmacs = lenof(buggymacs);
     else
         maclist = macs, nmacs = lenof(macs);
@@ -2481,30 +2679,34 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
     }
 
     /*
+     * Work out the number of bits of key we will need from the key
+     * exchange. We start with the maximum key length of either
+     * cipher...
+     */
+    {
+        int csbits, scbits;
+
+       csbits = cscipher_tobe->keylen;
+       scbits = sccipher_tobe->keylen;
+       nbits = (csbits > scbits ? csbits : scbits);
+    }
+    /* The keys only have 160-bit entropy, since they're based on
+     * a SHA-1 hash. So cap the key size at 160 bits. */
+    if (nbits > 160) nbits = 160;
+
+    /*
      * If we're doing Diffie-Hellman group exchange, start by
      * requesting a group.
      */
     if (kex == &ssh_diffiehellman_gex) {
-        int csbits, scbits;
-
         logevent("Doing Diffie-Hellman group exchange");
         /*
-         * Work out number of bits. We start with the maximum key
-         * length of either cipher...
-         */
-        csbits = cscipher_tobe->keylen;
-        scbits = sccipher_tobe->keylen;
-        nbits = (csbits > scbits ? csbits : scbits);
-        /* The keys only have 160-bit entropy, since they're based on
-         * a SHA-1 hash. So cap the key size at 160 bits. */
-        if (nbits > 160) nbits = 160;
-        /*
-         * ... and then work out how big a DH group we will need to
-         * allow that much data.
-         */
-        nbits = 512 << ((nbits-1) / 64);
+         * Work out how big a DH group we will need to allow that
+         * much data.
+        */
+        pbits = 512 << ((nbits-1) / 64);
         ssh2_pkt_init(SSH2_MSG_KEX_DH_GEX_REQUEST);
-        ssh2_pkt_adduint32(nbits);
+        ssh2_pkt_adduint32(pbits);
         ssh2_pkt_send();
 
         crWaitUntil(ispkt);
@@ -2527,7 +2729,7 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
     /*
      * Now generate and send e for Diffie-Hellman.
      */
-    e = dh_create_e();
+    e = dh_create_e(nbits*2);
     ssh2_pkt_init(kex_init_value);
     ssh2_pkt_addmp(e);
     ssh2_pkt_send();
@@ -2545,7 +2747,7 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
 
     sha_string(&exhash, hostkeydata, hostkeylen);
     if (kex == &ssh_diffiehellman_gex) {
-        sha_uint32(&exhash, nbits);
+        sha_uint32(&exhash, pbits);
         sha_mpint(&exhash, p);
         sha_mpint(&exhash, g);
     }
@@ -2557,10 +2759,8 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
     dh_cleanup();
 
 #if 0
-    debug(("Exchange hash is:\r\n"));
-    for (i = 0; i < 20; i++)
-        debug((" %02x", exchange_hash[i]));
-    debug(("\r\n"));
+    debug(("Exchange hash is:\n"));
+    dmemdump(exchange_hash, 20);
 #endif
 
     hkey = hostkey->newkey(hostkeydata, hostkeylen);
@@ -2768,40 +2968,53 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
         */
        pos = 0;
        if ((flags & FLAG_INTERACTIVE) && !*cfg.username) {
-           c_write_str("login as: ");
-           ssh_send_ok = 1;
-           while (pos >= 0) {
-               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:
-                   random_save_seed();
-                   exit(0);
-                   break;
-                 default:
-                   if (((c >= ' ' && c <= '~') ||
-                        ((unsigned char)c >= 160)) && pos < 40) {
-                       username[pos++] = c;
-                       c_write(&c, 1);
-                   }
-                   break;
-               }
-           }
+            if (ssh_get_line) {
+                if (!ssh_get_line("login as: ",
+                                  username, sizeof(username), FALSE)) {
+                    /*
+                     * get_line failed to get a username.
+                     * Terminate.
+                     */
+                    logevent("No username provided. Abandoning session.");
+                    ssh_state = SSH_STATE_CLOSED;
+                    crReturnV;
+                }
+            } else {
+                c_write_str("login as: ");
+                ssh_send_ok = 1;
+                while (pos >= 0) {
+                    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:
+                        random_save_seed();
+                        exit(0);
+                        break;
+                      default:
+                        if (((c >= ' ' && c <= '~') ||
+                             ((unsigned char)c >= 160)) && pos < 40) {
+                            username[pos++] = c;
+                            c_write(&c, 1);
+                        }
+                        break;
+                    }
+                }
+            }
            c_write_str("\r\n");
            username[strcspn(username, "\n\r")] = '\0';
        } else {
@@ -2838,7 +3051,21 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
            if (!gotit)
                crWaitUntilV(ispkt);
            while (pktin.type == SSH2_MSG_USERAUTH_BANNER) {
-               /* FIXME: should support this */
+                char *banner;
+                int size;
+                /*
+                 * Don't show the banner if we're operating in
+                 * non-verbose non-interactive mode. (It's probably
+                 * a script, which means nobody will read the
+                 * banner _anyway_, and moreover the printing of
+                 * the banner will screw up processing on the
+                 * output of (say) plink.)
+                 */
+                if (flags & (FLAG_VERBOSE | FLAG_INTERACTIVE)) {
+                    ssh2_pkt_getstring(&banner, &size);
+                    if (banner)
+                        c_write_untrusted(banner, size);
+                }
                crWaitUntilV(ispkt);
            }
            if (pktin.type == SSH2_MSG_USERAUTH_SUCCESS) {
@@ -2850,6 +3077,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
            if (pktin.type != SSH2_MSG_USERAUTH_FAILURE) {
                bombout(("Strange packet received during authentication: type %d",
                         pktin.type));
+               crReturnV;
            }
 
            gotit = FALSE;
@@ -2911,7 +3139,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
 
            method = 0;
 
-           if (!method && can_pubkey && agent_exists && !tried_agent) {
+           if (!method && can_pubkey && agent_exists() && !tried_agent) {
                /*
                 * Attempt public-key authentication using Pageant.
                 */
@@ -2930,10 +3158,11 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                request[4] = SSH2_AGENTC_REQUEST_IDENTITIES;
                agent_query(request, 5, &r, &responselen);
                response = (unsigned char *)r;
-               if (response) {
+               if (response && responselen >= 5 &&
+                    response[4] == SSH2_AGENT_IDENTITIES_ANSWER) {
                    p = response + 5;
                    nkeys = GET_32BIT(p); p += 4;
-                   { char buf[64]; sprintf(buf, "Pageant has %d keys", nkeys);
+                   { char buf[64]; sprintf(buf, "Pageant has %d SSH2 keys", nkeys);
                        logevent(buf); }
                    for (i = 0; i < nkeys; i++) {
                        static char *pkblob, *alg, *commentp;
@@ -2966,9 +3195,11 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                            continue;
                        }
 
-                       c_write_str("Authenticating with public key \"");
-                       c_write(commentp, commentlen);
-                       c_write_str("\" from agent\r\n");
+                        if (flags & FLAG_VERBOSE) {
+                            c_write_str("Authenticating with public key \"");
+                            c_write(commentp, commentlen);
+                            c_write_str("\" from agent\r\n");
+                        }
 
                        /*
                         * Server is willing to accept the key.
@@ -3083,13 +3314,14 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
            }
 
            if (need_pw) {
-               if (ssh_get_password) {
-                   if (!ssh_get_password(pwprompt, password, sizeof(password))) {
+               if (ssh_get_line) {
+                   if (!ssh_get_line(pwprompt, password,
+                                      sizeof(password), TRUE)) {
                        /*
-                        * get_password failed to get a password (for
-                        * example because one was supplied on the command
-                        * line which has already failed to work).
-                        * Terminate.
+                        * get_line failed to get a password (for
+                        * example because one was supplied on the
+                        * command line which has already failed to
+                        * work). Terminate.
                         */
                        logevent("No more passwords to try");
                        ssh_state = SSH_STATE_CLOSED;
@@ -3224,15 +3456,29 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                 * reason, we don't do this trick at all because we gain
                 * nothing by it.
                 */
-               if (cscipher) {
-                   int i, j;
+                if (cscipher) {
+                    int stringlen, i;
+
+                    stringlen = (256 - deferred_len);
+                    stringlen += cscipher->blksize - 1;
+                    stringlen -= (stringlen % cscipher->blksize);
+                    if (cscomp) {
+                        /*
+                         * Temporarily disable actual compression,
+                         * so we can guarantee to get this string
+                         * exactly the length we want it. The
+                         * compression-disabling routine should
+                         * return an integer indicating how many
+                         * bytes we should adjust our string length
+                         * by.
+                         */
+                        stringlen -= cscomp->disable_compression();
+                    }
                    ssh2_pkt_init(SSH2_MSG_IGNORE);
                    ssh2_pkt_addstring_start();
-                   for (i = deferred_len; i <= 256; i += cscipher->blksize) {
-                       for (j = 0; j < cscipher->blksize; j++) {
-                           char c = (char)random_byte();
-                           ssh2_pkt_addstring_data(&c, 1);
-                       }
+                   for (i = 0; i < stringlen; i++) {
+                        char c = (char)random_byte();
+                        ssh2_pkt_addstring_data(&c, 1);
                    }
                    ssh2_pkt_defer();
                }
@@ -3262,8 +3508,9 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
     /*
      * So now create a channel with a session in it.
      */
+    ssh_channels = newtree234(ssh_channelcmp);
     mainchan = smalloc(sizeof(struct ssh_channel));
-    mainchan->localid = 100;           /* as good as any */
+    mainchan->localid = alloc_channel_id();
     ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN);
     ssh2_pkt_addstring("session");
     ssh2_pkt_adduint32(mainchan->localid);
@@ -3287,7 +3534,6 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
     mainchan->v2.remmaxpkt = ssh2_pkt_getuint32();
     mainchan->v2.outbuffer = NULL;
     mainchan->v2.outbuflen = mainchan->v2.outbufsize = 0;
-    ssh_channels = newtree234(ssh_channelcmp);
     add234(ssh_channels, mainchan);
     logevent("Opened channel for session");
 
@@ -3336,9 +3582,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
      * Potentially enable agent forwarding.
      */
     if (cfg.agentfwd && agent_exists()) {
-        char proto[20], data[64];
         logevent("Requesting OpenSSH-style agent forwarding");
-        x11_invent_auth(proto, sizeof(proto), data, sizeof(data));
         ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
         ssh2_pkt_adduint32(mainchan->remoteid);
         ssh2_pkt_addstring("auth-agent-req@openssh.com");
@@ -3421,11 +3665,11 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
     if (cfg.ssh_subsys) {
         ssh2_pkt_addstring("subsystem");
         ssh2_pkt_addbool(1);           /* want reply */
-        ssh2_pkt_addstring(cfg.remote_cmd);
-    } else if (*cfg.remote_cmd) {
+        ssh2_pkt_addstring(cfg.remote_cmd_ptr);
+    } else if (*cfg.remote_cmd_ptr) {
         ssh2_pkt_addstring("exec");
         ssh2_pkt_addbool(1);           /* want reply */
-        ssh2_pkt_addstring(cfg.remote_cmd);
+        ssh2_pkt_addstring(cfg.remote_cmd_ptr);
     } else {
         ssh2_pkt_addstring("shell");
         ssh2_pkt_addbool(1);           /* want reply */
@@ -3568,7 +3812,6 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
            } else if (pktin.type == SSH2_MSG_CHANNEL_CLOSE) {
                 unsigned i = ssh2_pkt_getuint32();
                 struct ssh_channel *c;
-                enum234 e;
 
                 c = find234(ssh_channels, &i, ssh_channelfind);
                 if (!c)
@@ -3594,8 +3837,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                 /*
                  * See if that was the last channel left open.
                  */
-                c = first234(ssh_channels, &e);
-                if (!c) {
+                if (count234(ssh_channels) == 0) {
                     logevent("All channels closed. Disconnecting");
                     ssh2_pkt_init(SSH2_MSG_DISCONNECT);
                     ssh2_pkt_adduint32(SSH2_DISCONNECT_BY_APPLICATION);
@@ -3612,7 +3854,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                 c = find234(ssh_channels, &i, ssh_channelfind);
                 if (!c)
                     continue;          /* nonexistent channel */
-                mainchan->v2.remwindow += ssh2_pkt_getuint32();
+                c->v2.remwindow += ssh2_pkt_getuint32();
                 try_send = TRUE;
            } else if (pktin.type == SSH2_MSG_CHANNEL_OPEN) {
                 char *type;
@@ -3652,17 +3894,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                     ssh2_pkt_send();
                     sfree(c);
                 } else {
-                    struct ssh_channel *d;
-                    unsigned i;
-                    enum234 e;
-
-                    for (i=1, d = first234(ssh_channels, &e); d;
-                         d = next234(&e)) {
-                       if (d->localid > i)
-                            break;     /* found a free number */
-                       i = d->localid + 1;
-                    }
-                    c->localid = i;
+                    c->localid = alloc_channel_id();
                     c->closes = 0;
                     c->v2.remwindow = ssh2_pkt_getuint32();
                     c->v2.remmaxpkt = ssh2_pkt_getuint32();
@@ -3688,12 +3920,12 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
             try_send = TRUE;
        }
         if (try_send) {
-            enum234 e;
+            int i;
             struct ssh_channel *c;
             /*
              * Try to send data on all channels if we can.
              */
-            for (c = first234(ssh_channels, &e); c; c = next234(&e))
+            for (i = 0; NULL != (c = index234(ssh_channels, i)); i++)
                 ssh2_try_send(c);
         }
     }