Debugging improvements. Started using Dave Hinton's dmemdump
[sgt/putty] / ssh.c
diff --git a/ssh.c b/ssh.c
index a03330e..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 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 */
@@ -342,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;
@@ -428,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);
@@ -447,14 +507,9 @@ next_packet:
     if (ssh1_compressing) {
        unsigned char *decompblk;
        int decomplen;
-#if 0
-        {
-            int i;
-            debug(("Packet payload pre-decompression:\n"));
-            for (i = -1; i < pktin.length; i++)
-                debug(("  %02x", (unsigned char)pktin.body[i]));
-            debug(("\r\n"));
-        }
+#ifdef DUMP_PACKETS
+       debug(("Packet payload pre-decompression:\n"));
+       dmemdump(pktin.body-1, pktin.length+1);
 #endif
        zlib_decompress_block(pktin.body-1, pktin.length+1,
                              &decompblk, &decomplen);
@@ -470,14 +525,9 @@ next_packet:
        memcpy(pktin.body-1, decompblk, decomplen);
        sfree(decompblk);
        pktin.length = decomplen-1;
-#if 0
-        {
-            int i;
-            debug(("Packet payload post-decompression:\n"));
-            for (i = -1; i < pktin.length; i++)
-                debug(("  %02x", (unsigned char)pktin.body[i]));
-            debug(("\r\n"));
-        }
+#ifdef DUMP_PACKETS
+       debug(("Packet payload post-decompression:\n"));
+       dmemdump(pktin.body-1, pktin.length+1);
 #endif
     }
 
@@ -510,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);
 }
 
@@ -599,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
 
     /*
@@ -632,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);
@@ -649,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);
 }
 
@@ -688,11 +770,9 @@ static int s_wrpkt_prepare(void) {
 
     pktout.body[-1] = pktout.type;
 
-#if 0
+#ifdef DUMP_PACKETS
     debug(("Packet payload pre-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
 
     if (ssh1_compressing) {
@@ -703,11 +783,9 @@ static int s_wrpkt_prepare(void) {
        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
     }
 
@@ -721,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);
@@ -928,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");
@@ -963,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;
@@ -999,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)
@@ -1061,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
@@ -1128,7 +1200,9 @@ 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;
 
@@ -1156,10 +1230,11 @@ static void ssh_detect_bugs(char *vstring) {
 }
 
 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;
@@ -1179,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';
@@ -1200,11 +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
@@ -1214,7 +1296,7 @@ static int do_ssh_init(unsigned char c) {
         /*
          * This is a v2 server. Begin v2 protocol.
          */
-        char verstring[80];
+        char verstring[80], vlog[100];
         sprintf(verstring, "SSH-2.0-%s", sshver);
         SHA_Init(&exhashbase);
         /*
@@ -1222,11 +1304,11 @@ static int do_ssh_init(unsigned char c) {
          */
         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;
@@ -1234,20 +1316,23 @@ static int do_ssh_init(unsigned char c) {
         /*
          * This is a v1 server. Begin v1 protocol.
          */
-        sprintf(vstring, "SSH-%s-%s\n",
+        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", vstring);
-        vlog[strcspn(vlog, "\r\n")] = '\0';
+        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);
 }
 
@@ -1497,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;
@@ -1676,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);
@@ -2127,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");
@@ -2157,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. */
@@ -2179,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);
@@ -2198,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) {
@@ -2208,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;
@@ -2234,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);
@@ -2683,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);
@@ -2979,9 +3053,19 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
            while (pktin.type == SSH2_MSG_USERAUTH_BANNER) {
                 char *banner;
                 int size;
-               ssh2_pkt_getstring(&banner, &size);
-                if (banner)
-                    c_write_untrusted(banner, 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) {
@@ -2993,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;
@@ -3054,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.
                 */
@@ -3110,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.
@@ -3421,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);
@@ -3446,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");
 
@@ -3578,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 */
@@ -3725,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)
@@ -3751,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);
@@ -3809,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();
@@ -3845,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);
         }
     }