Get rid of all the MSVC warnings.
[u/mdw/putty] / ssh.c
diff --git a/ssh.c b/ssh.c
index 38eb2bb..953c69f 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -6,6 +6,7 @@
 #include <stdlib.h>
 #include <stdarg.h>
 #include <assert.h>
+#include <limits.h>
 
 #include "putty.h"
 #include "tree234.h"
@@ -728,6 +729,7 @@ struct ssh_tag {
 
     tree234 *channels;                /* indexed by local id */
     struct ssh_channel *mainchan;      /* primary session channel */
+    int ncmode;                               /* is primary channel direct-tcpip? */
     int exitcode;
     int close_expected;
     int clean_exit;
@@ -1050,7 +1052,7 @@ static void c_write_stderr(int trusted, const char *buf, int len)
 {
     int i;
     for (i = 0; i < len; i++)
-       if (buf[i] != '\r' && (trusted || buf[i] & 0x60))
+       if (buf[i] != '\r' && (trusted || buf[i] == '\n' || (buf[i] & 0x60)))
            fputc(buf[i], stderr);
 }
 
@@ -1440,11 +1442,17 @@ static int s_wrpkt_prepare(Ssh ssh, struct Packet *pkt, int *offset_p)
     return biglen + 4;         /* len(length+padding+type+data+CRC) */
 }
 
+static int s_write(Ssh ssh, void *data, int len)
+{
+    log_packet(ssh->logctx, PKT_OUTGOING, -1, NULL, data, len, 0, NULL);
+    return sk_write(ssh->s, (char *)data, len);
+}
+
 static void s_wrpkt(Ssh ssh, struct Packet *pkt)
 {
     int len, backlog, offset;
     len = s_wrpkt_prepare(ssh, pkt, &offset);
-    backlog = sk_write(ssh->s, (char *)pkt->data + offset, len);
+    backlog = s_write(ssh, pkt->data + offset, len);
     if (backlog > SSH_MAX_BACKLOG)
        ssh_throttle_all(ssh, 1, backlog);
     ssh_free_packet(pkt);
@@ -1481,6 +1489,7 @@ static struct Packet *construct_packet(Ssh ssh, int pkttype, va_list ap)
 
     while ((argtype = va_arg(ap, int)) != PKT_END) {
        unsigned char *argp, argchar;
+       char *sargp;
        unsigned long argint;
        int arglen;
        switch (argtype) {
@@ -1499,8 +1508,8 @@ static struct Packet *construct_packet(Ssh ssh, int pkttype, va_list ap)
            ssh_pkt_adddata(pkt, argp, arglen);
            break;
          case PKT_STR:
-           argp = va_arg(ap, unsigned char *);
-           ssh_pkt_addstring(pkt, argp);
+           sargp = va_arg(ap, char *);
+           ssh_pkt_addstring(pkt, sargp);
            break;
          case PKT_BIGNUM:
            bn = va_arg(ap, Bignum);
@@ -1646,7 +1655,7 @@ static void ssh_pkt_addstring(struct Packet *pkt, char *data)
 static void ssh1_pkt_addmp(struct Packet *pkt, Bignum b)
 {
     int len = ssh1_bignum_length(b);
-    unsigned char *data = snewn(len, char);
+    unsigned char *data = snewn(len, unsigned char);
     (void) ssh1_write_bignum(data, b);
     ssh_pkt_adddata(pkt, data, len);
     sfree(data);
@@ -1828,7 +1837,7 @@ static void ssh2_pkt_send_noqueue(Ssh ssh, struct Packet *pkt)
        return;
     }
     len = ssh2_pkt_construct(ssh, pkt);
-    backlog = sk_write(ssh->s, (char *)pkt->data, len);
+    backlog = s_write(ssh, pkt->data, len);
     if (backlog > SSH_MAX_BACKLOG)
        ssh_throttle_all(ssh, 1, backlog);
 
@@ -1896,7 +1905,6 @@ static void ssh2_pkt_send(Ssh ssh, struct Packet *pkt)
        ssh2_pkt_send_noqueue(ssh, pkt);
 }
 
-#if 0 /* disused */
 /*
  * Either queue or defer a packet, depending on whether queueing is
  * set.
@@ -1908,7 +1916,6 @@ static void ssh2_pkt_defer(Ssh ssh, struct Packet *pkt)
     else
        ssh2_pkt_defer_noqueue(ssh, pkt, FALSE);
 }
-#endif
 
 /*
  * Send the whole deferred data block constructed by
@@ -1926,8 +1933,7 @@ static void ssh2_pkt_defer(Ssh ssh, struct Packet *pkt)
 static void ssh_pkt_defersend(Ssh ssh)
 {
     int backlog;
-    backlog = sk_write(ssh->s, (char *)ssh->deferred_send_data,
-                      ssh->deferred_len);
+    backlog = s_write(ssh, ssh->deferred_send_data, ssh->deferred_len);
     ssh->deferred_len = ssh->deferred_size = 0;
     sfree(ssh->deferred_send_data);
     ssh->deferred_send_data = NULL;
@@ -1943,6 +1949,74 @@ static void ssh_pkt_defersend(Ssh ssh)
 }
 
 /*
+ * Send a packet whose length needs to be disguised (typically
+ * passwords or keyboard-interactive responses).
+ */
+static void ssh2_pkt_send_with_padding(Ssh ssh, struct Packet *pkt,
+                                      int padsize)
+{
+#if 0
+    if (0) {
+       /*
+        * The simplest way to do this is to adjust the
+        * variable-length padding field in the outgoing packet.
+        * 
+        * Currently compiled out, because some Cisco SSH servers
+        * don't like excessively padded packets (bah, why's it
+        * always Cisco?)
+        */
+       pkt->forcepad = padsize;
+       ssh2_pkt_send(ssh, pkt);
+    } else
+#endif
+    {
+       /*
+        * If we can't do that, however, an alternative approach is
+        * to use the pkt_defer mechanism to bundle the packet
+        * tightly together with an SSH_MSG_IGNORE such that their
+        * combined length is a constant. So first we construct the
+        * final form of this packet and defer its sending.
+        */
+       ssh2_pkt_defer(ssh, pkt);
+
+       /*
+        * Now construct an SSH_MSG_IGNORE which includes a string
+        * that's an exact multiple of the cipher block size. (If
+        * the cipher is NULL so that the block size is
+        * unavailable, we don't do this trick at all, because we
+        * gain nothing by it.)
+        */
+       if (ssh->cscipher) {
+           int stringlen, i;
+
+           stringlen = (256 - ssh->deferred_len);
+           stringlen += ssh->cscipher->blksize - 1;
+           stringlen -= (stringlen % ssh->cscipher->blksize);
+           if (ssh->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 -=
+                   ssh->cscomp->disable_compression(ssh->cs_comp_ctx);
+           }
+           pkt = ssh2_pkt_init(SSH2_MSG_IGNORE);
+           ssh2_pkt_addstring_start(pkt);
+           for (i = 0; i < stringlen; i++) {
+               char c = (char) random_byte();
+               ssh2_pkt_addstring_data(pkt, &c, 1);
+           }
+           ssh2_pkt_defer(ssh, pkt);
+       }
+       ssh_pkt_defersend(ssh);
+    }
+}
+
+/*
  * Send all queued SSH-2 packets. We send them by means of
  * ssh2_pkt_defer_noqueue(), in case they included a pair of
  * packets that needed to be lumped together.
@@ -2159,6 +2233,13 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
 
     ssh->remote_bugs = 0;
 
+    /*
+     * General notes on server version strings:
+     *  - Not all servers reporting "Cisco-1.25" have all the bugs listed
+     *    here -- in particular, we've heard of one that's perfectly happy
+     *    with SSH1_MSG_IGNOREs -- but this string never seems to change,
+     *    so we can't distinguish them.
+     */
     if (ssh->cfg.sshbug_ignore1 == FORCE_ON ||
        (ssh->cfg.sshbug_ignore1 == AUTO &&
         (!strcmp(imp, "1.2.18") || !strcmp(imp, "1.2.19") ||
@@ -2416,7 +2497,7 @@ static int do_ssh_init(Ssh ssh, unsigned char c)
         }
         logeventf(ssh, "We claim version: %.*s",
                   strcspn(verstring, "\015\012"), verstring);
-       sk_write(ssh->s, verstring, strlen(verstring));
+       s_write(ssh, verstring, strlen(verstring));
         sfree(verstring);
        if (ssh->version == 2)
            do_ssh2_transport(ssh, NULL, -1, NULL);
@@ -2436,7 +2517,9 @@ static int do_ssh_init(Ssh ssh, unsigned char c)
 static void ssh_process_incoming_data(Ssh ssh,
                                      unsigned char **data, int *datalen)
 {
-    struct Packet *pktin = ssh->s_rdpkt(ssh, data, datalen);
+    struct Packet *pktin;
+
+    pktin = ssh->s_rdpkt(ssh, data, datalen);
     if (pktin) {
        ssh->protocol(ssh, NULL, 0, pktin);
        ssh_free_packet(pktin);
@@ -2479,6 +2562,9 @@ static void ssh_set_frozen(Ssh ssh, int frozen)
 
 static void ssh_gotdata(Ssh ssh, unsigned char *data, int datalen)
 {
+    /* Log raw data, if we're in that mode. */
+    log_packet(ssh->logctx, PKT_INCOMING, -1, NULL, data, datalen, 0, NULL);
+
     crBegin(ssh->ssh_gotdata_crstate);
 
     /*
@@ -3691,19 +3777,19 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
             *    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.
+            * A few servers can't deal with SSH1_MSG_IGNORE, at
+            * least in this context. 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.
             * 
-            * One server (a Cisco one) can deal with neither
-            * SSH1_MSG_IGNORE _nor_ a padded password string.
-            * For this server we are left with no defences
+            * A few servers can deal with neither SSH1_MSG_IGNORE
+            * here _nor_ a padded password string.
+            * For these servers we are left with no defences
             * against password length sniffing.
             */
-           if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE)) {
+           if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE) &&
+               !(ssh->remote_bugs & BUG_NEEDS_SSH1_PLAIN_PASSWORD)) {
                /*
                 * The server can deal with SSH1_MSG_IGNORE, so
                 * we can use the primary defence.
@@ -3772,10 +3858,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
                            PKTT_OTHER, PKT_END);
            } else {
                /*
-                * The server has _both_
-                * BUG_CHOKES_ON_SSH1_IGNORE and
-                * BUG_NEEDS_SSH1_PLAIN_PASSWORD. There is
-                * therefore nothing we can do.
+                * The server is believed unable to cope with
+                * any of our password camouflage methods.
                 */
                int len;
                len = strlen(s->cur_prompt->prompts[0]->result);
@@ -4557,7 +4641,7 @@ static void ssh1_msg_channel_data(Ssh ssh, struct Packet *pktin)
            /* Data for an agent message. Buffer it. */
            while (len > 0) {
                if (c->u.a.lensofar < 4) {
-                   unsigned int l = min(4 - c->u.a.lensofar, len);
+                   unsigned int l = min(4 - c->u.a.lensofar, (unsigned)len);
                    memcpy(c->u.a.msglen + c->u.a.lensofar, p,
                           l);
                    p += l;
@@ -4574,7 +4658,7 @@ static void ssh1_msg_channel_data(Ssh ssh, struct Packet *pktin)
                if (c->u.a.lensofar >= 4 && len > 0) {
                    unsigned int l =
                        min(c->u.a.totallen - c->u.a.lensofar,
-                           len);
+                           (unsigned)len);
                    memcpy(c->u.a.message + c->u.a.lensofar, p,
                           l);
                    p += l;
@@ -5994,7 +6078,8 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin)
          case CHAN_AGENT:
            while (length > 0) {
                if (c->u.a.lensofar < 4) {
-                   unsigned int l = min(4 - c->u.a.lensofar, length);
+                   unsigned int l = min(4 - c->u.a.lensofar,
+                                        (unsigned)length);
                    memcpy(c->u.a.msglen + c->u.a.lensofar,
                           data, l);
                    data += l;
@@ -6011,7 +6096,7 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin)
                if (c->u.a.lensofar >= 4 && length > 0) {
                    unsigned int l =
                        min(c->u.a.totallen - c->u.a.lensofar,
-                           length);
+                           (unsigned)length);
                    memcpy(c->u.a.message + c->u.a.lensofar,
                           data, l);
                    data += l;
@@ -7356,7 +7441,6 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                     * Send the responses to the server.
                     */
                    s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_INFO_RESPONSE);
-                   s->pktout->forcepad = 256;
                    ssh2_pkt_adduint32(s->pktout, s->num_prompts);
                    for (i=0; i < s->num_prompts; i++) {
                        dont_log_password(ssh, s->pktout, PKTLOG_BLANK);
@@ -7364,7 +7448,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                                           s->cur_prompt->prompts[i]->result);
                        end_log_omission(ssh, s->pktout);
                    }
-                   ssh2_pkt_send(ssh, s->pktout);
+                   ssh2_pkt_send_with_padding(ssh, s->pktout, 256);
 
                    /*
                     * Get the next packet in case it's another
@@ -7434,7 +7518,6 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                 * people who find out how long their password is!
                 */
                s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
-               s->pktout->forcepad = 256;
                ssh2_pkt_addstring(s->pktout, s->username);
                ssh2_pkt_addstring(s->pktout, "ssh-connection");
                                                        /* service requested */
@@ -7443,7 +7526,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                dont_log_password(ssh, s->pktout, PKTLOG_BLANK);
                ssh2_pkt_addstring(s->pktout, s->password);
                end_log_omission(ssh, s->pktout);
-               ssh2_pkt_send(ssh, s->pktout);
+               ssh2_pkt_send_with_padding(ssh, s->pktout, 256);
                logevent("Sent password");
                s->type = AUTH_TYPE_PASSWORD;
 
@@ -7485,6 +7568,21 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                    s->cur_prompt->instruction =
                        dupprintf("%.*s", prompt_len, prompt);
                    s->cur_prompt->instr_reqd = TRUE;
+                   /*
+                    * There's no explicit requirement in the protocol
+                    * for the "old" passwords in the original and
+                    * password-change messages to be the same, and
+                    * apparently some Cisco kit supports password change
+                    * by the user entering a blank password originally
+                    * and the real password subsequently, so,
+                    * reluctantly, we prompt for the old password again.
+                    *
+                    * (On the other hand, some servers don't even bother
+                    * to check this field.)
+                    */
+                   add_prompt(s->cur_prompt,
+                              dupstr("Current password (blank for previously entered password): "),
+                              FALSE, SSH_MAX_PASSWORD_LEN);
                    add_prompt(s->cur_prompt, dupstr("Enter new password: "),
                               FALSE, SSH_MAX_PASSWORD_LEN);
                    add_prompt(s->cur_prompt, dupstr("Confirm new password: "),
@@ -7518,10 +7616,25 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                        }
 
                        /*
-                        * Check the two passwords match.
+                        * If the user specified a new original password
+                        * (IYSWIM), overwrite any previously specified
+                        * one.
+                        * (A side effect is that the user doesn't have to
+                        * re-enter it if they louse up the new password.)
+                        */
+                       if (s->cur_prompt->prompts[0]->result[0]) {
+                           memset(s->password, 0, strlen(s->password));
+                               /* burn the evidence */
+                           sfree(s->password);
+                           s->password =
+                               dupstr(s->cur_prompt->prompts[0]->result);
+                       }
+
+                       /*
+                        * Check the two new passwords match.
                         */
-                       got_new = (strcmp(s->cur_prompt->prompts[0]->result,
-                                         s->cur_prompt->prompts[1]->result)
+                       got_new = (strcmp(s->cur_prompt->prompts[1]->result,
+                                         s->cur_prompt->prompts[2]->result)
                                   == 0);
                        if (!got_new)
                            /* They don't. Silly user. */
@@ -7534,7 +7647,6 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                     * (see above for padding rationale)
                     */
                    s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
-                   s->pktout->forcepad = 256;
                    ssh2_pkt_addstring(s->pktout, s->username);
                    ssh2_pkt_addstring(s->pktout, "ssh-connection");
                                                        /* service requested */
@@ -7543,10 +7655,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                    dont_log_password(ssh, s->pktout, PKTLOG_BLANK);
                    ssh2_pkt_addstring(s->pktout, s->password);
                    ssh2_pkt_addstring(s->pktout,
-                                      s->cur_prompt->prompts[0]->result);
+                                      s->cur_prompt->prompts[1]->result);
                    free_prompts(s->cur_prompt);
                    end_log_omission(ssh, s->pktout);
-                   ssh2_pkt_send(ssh, s->pktout);
+                   ssh2_pkt_send_with_padding(ssh, s->pktout, 256);
                    logevent("Sent new password");
                    
                    /*
@@ -7621,7 +7733,58 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
     /*
      * Create the main session channel.
      */
-    if (!ssh->cfg.ssh_no_shell) {
+    if (ssh->cfg.ssh_no_shell) {
+       ssh->mainchan = NULL;
+    } else if (*ssh->cfg.ssh_nc_host) {
+       /*
+        * Just start a direct-tcpip channel and use it as the main
+        * channel.
+        */
+       ssh->mainchan = snew(struct ssh_channel);
+       ssh->mainchan->ssh = ssh;
+       ssh->mainchan->localid = alloc_channel_id(ssh);
+       logeventf(ssh,
+                 "Opening direct-tcpip channel to %s:%d in place of session",
+                 ssh->cfg.ssh_nc_host, ssh->cfg.ssh_nc_port);
+       s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN);
+       ssh2_pkt_addstring(s->pktout, "direct-tcpip");
+       ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid);
+       ssh->mainchan->v.v2.locwindow = OUR_V2_WINSIZE;
+       ssh2_pkt_adduint32(s->pktout, ssh->mainchan->v.v2.locwindow);/* our window size */
+       ssh2_pkt_adduint32(s->pktout, OUR_V2_MAXPKT);      /* our max pkt size */
+       ssh2_pkt_addstring(s->pktout, ssh->cfg.ssh_nc_host);
+       ssh2_pkt_adduint32(s->pktout, ssh->cfg.ssh_nc_port);
+       /*
+        * There's nothing meaningful to put in the originator
+        * fields, but some servers insist on syntactically correct
+        * information.
+        */
+       ssh2_pkt_addstring(s->pktout, "0.0.0.0");
+       ssh2_pkt_adduint32(s->pktout, 0);
+       ssh2_pkt_send(ssh, s->pktout);
+
+       crWaitUntilV(pktin);
+       if (pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) {
+           bombout(("Server refused to open a direct-tcpip channel"));
+           crStopV;
+           /* FIXME: error data comes back in FAILURE packet */
+       }
+       if (ssh_pkt_getuint32(pktin) != ssh->mainchan->localid) {
+           bombout(("Server's channel confirmation cited wrong channel"));
+           crStopV;
+       }
+       ssh->mainchan->remoteid = ssh_pkt_getuint32(pktin);
+       ssh->mainchan->halfopen = FALSE;
+       ssh->mainchan->type = CHAN_MAINSESSION;
+       ssh->mainchan->closes = 0;
+       ssh->mainchan->v.v2.remwindow = ssh_pkt_getuint32(pktin);
+       ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin);
+       bufchain_init(&ssh->mainchan->v.v2.outbuffer);
+       add234(ssh->channels, ssh->mainchan);
+       update_specials_menu(ssh->frontend);
+       logevent("Opened direct-tcpip channel");
+       ssh->ncmode = TRUE;
+    } else {
        ssh->mainchan = snew(struct ssh_channel);
        ssh->mainchan->ssh = ssh;
        ssh->mainchan->localid = alloc_channel_id(ssh);
@@ -7652,8 +7815,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
        add234(ssh->channels, ssh->mainchan);
        update_specials_menu(ssh->frontend);
        logevent("Opened channel for session");
-    } else
-       ssh->mainchan = NULL;
+       ssh->ncmode = FALSE;
+    }
 
     /*
      * Now we have a channel, make dispatch table entries for
@@ -7676,7 +7839,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
     /*
      * Potentially enable X11 forwarding.
      */
-    if (ssh->mainchan && ssh->cfg.x11_forward) {
+    if (ssh->mainchan && !ssh->ncmode && ssh->cfg.x11_forward) {
        char proto[20], data[64];
        logevent("Requesting X11 forwarding");
        ssh->x11auth = x11_invent_auth(proto, sizeof(proto),
@@ -7724,7 +7887,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
     /*
      * Potentially enable agent forwarding.
      */
-    if (ssh->mainchan && ssh->cfg.agentfwd && agent_exists()) {
+    if (ssh->mainchan && !ssh->ncmode && ssh->cfg.agentfwd && agent_exists()) {
        logevent("Requesting OpenSSH-style agent forwarding");
        s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
        ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid);
@@ -7750,7 +7913,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
     /*
      * Now allocate a pty for the session.
      */
-    if (ssh->mainchan && !ssh->cfg.nopty) {
+    if (ssh->mainchan && !ssh->ncmode && !ssh->cfg.nopty) {
        /* Unpick the terminal-speed string. */
        /* XXX perhaps we should allow no speeds to be sent. */
         ssh->ospeed = 38400; ssh->ispeed = 38400; /* last-resort defaults */
@@ -7800,7 +7963,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
      * Simplest thing here is to send all the requests at once, and
      * then wait for a whole bunch of successes or failures.
      */
-    if (ssh->mainchan && *ssh->cfg.environmt) {
+    if (ssh->mainchan && !ssh->ncmode && *ssh->cfg.environmt) {
        char *e = ssh->cfg.environmt;
        char *var, *varend, *val;
 
@@ -7865,7 +8028,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
      * this twice if the config data has provided a second choice
      * of command.
      */
-    if (ssh->mainchan) while (1) {
+    if (ssh->mainchan && !ssh->ncmode) while (1) {
        int subsys;
        char *cmd;
 
@@ -8724,10 +8887,10 @@ void ssh_send_port_open(void *channel, char *hostname, int port, char *org)
     }
 }
 
-static Socket ssh_socket(void *handle)
+static int ssh_connected(void *handle)
 {
     Ssh ssh = (Ssh) handle;
-    return ssh->s;
+    return ssh->s != NULL;
 }
 
 static int ssh_sendok(void *handle)
@@ -8764,7 +8927,7 @@ static int ssh_return_exitcode(void *handle)
     if (ssh->s != NULL)
         return -1;
     else
-        return (ssh->exitcode >= 0 ? ssh->exitcode : 0);
+        return (ssh->exitcode >= 0 ? ssh->exitcode : INT_MAX);
 }
 
 /*
@@ -8797,7 +8960,7 @@ Backend ssh_backend = {
     ssh_size,
     ssh_special,
     ssh_get_specials,
-    ssh_socket,
+    ssh_connected,
     ssh_return_exitcode,
     ssh_sendok,
     ssh_ldisc,