Add support for RFC 4432 RSA key exchange, the patch for which has been
[u/mdw/putty] / ssh.c
diff --git a/ssh.c b/ssh.c
index a161604..64a069e 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -7,6 +7,7 @@
 #include <stdarg.h>
 #include <assert.h>
 #include <limits.h>
+#include <signal.h>
 
 #include "putty.h"
 #include "tree234.h"
@@ -82,6 +83,9 @@
 #define SSH2_MSG_KEX_DH_GEX_GROUP                 31   /* 0x1f */
 #define SSH2_MSG_KEX_DH_GEX_INIT                  32   /* 0x20 */
 #define SSH2_MSG_KEX_DH_GEX_REPLY                 33   /* 0x21 */
+#define SSH2_MSG_KEXRSA_PUBKEY                    30    /* 0x1e */
+#define SSH2_MSG_KEXRSA_SECRET                    31    /* 0x1f */
+#define SSH2_MSG_KEXRSA_DONE                      32    /* 0x20 */
 #define SSH2_MSG_USERAUTH_REQUEST                 50   /* 0x32 */
 #define SSH2_MSG_USERAUTH_FAILURE                 51   /* 0x33 */
 #define SSH2_MSG_USERAUTH_SUCCESS                 52   /* 0x34 */
  */
 #define SSH2_PKTCTX_DHGROUP          0x0001
 #define SSH2_PKTCTX_DHGEX            0x0002
+#define SSH2_PKTCTX_RSAKEX           0x0004
 #define SSH2_PKTCTX_KEX_MASK         0x000F
 #define SSH2_PKTCTX_PUBLICKEY        0x0010
 #define SSH2_PKTCTX_PASSWORD         0x0020
@@ -338,6 +343,9 @@ static char *ssh2_pkt_type(int pkt_ctx, int type)
     translatec(SSH2_MSG_KEX_DH_GEX_GROUP, SSH2_PKTCTX_DHGEX);
     translatec(SSH2_MSG_KEX_DH_GEX_INIT, SSH2_PKTCTX_DHGEX);
     translatec(SSH2_MSG_KEX_DH_GEX_REPLY, SSH2_PKTCTX_DHGEX);
+    translatec(SSH2_MSG_KEXRSA_PUBKEY, SSH2_PKTCTX_RSAKEX);
+    translatec(SSH2_MSG_KEXRSA_SECRET, SSH2_PKTCTX_RSAKEX);
+    translatec(SSH2_MSG_KEXRSA_DONE, SSH2_PKTCTX_RSAKEX);
     translate(SSH2_MSG_USERAUTH_REQUEST);
     translate(SSH2_MSG_USERAUTH_FAILURE);
     translate(SSH2_MSG_USERAUTH_SUCCESS);
@@ -1052,7 +1060,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);
 }
 
@@ -1442,11 +1450,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);
@@ -1483,6 +1497,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) {
@@ -1501,8 +1516,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);
@@ -1648,7 +1663,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);
@@ -1830,7 +1845,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);
 
@@ -1856,6 +1871,7 @@ static void ssh2_pkt_defer_noqueue(Ssh ssh, struct Packet *pkt, int noignore)
         * get encrypted with a known IV.
         */
        struct Packet *ipkt = ssh2_pkt_init(SSH2_MSG_IGNORE);
+       ssh2_pkt_addstring_start(ipkt);
        ssh2_pkt_defer_noqueue(ssh, ipkt, TRUE);
     }
     len = ssh2_pkt_construct(ssh, pkt);
@@ -1898,7 +1914,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.
@@ -1910,7 +1925,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
@@ -1928,8 +1942,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;
@@ -1945,6 +1958,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.
@@ -2161,6 +2242,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") ||
@@ -2418,7 +2506,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);
@@ -2438,7 +2526,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);
@@ -2481,6 +2571,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);
 
     /*
@@ -2617,6 +2710,9 @@ static int ssh_closing(Plug plug, const char *error_msg, int error_code,
            error_msg = "Server closed network connection";
     }
 
+    if (ssh->close_expected && ssh->clean_exit && ssh->exitcode < 0)
+       ssh->exitcode = 0;
+
     if (need_notify)
         notify_remote_exit(ssh->frontend);
 
@@ -3476,6 +3572,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
                    /* and try again */
                } else {
                    assert(0 && "unexpected return from loadrsakey()");
+                   got_passphrase = FALSE;   /* placate optimisers */
                }
            }
 
@@ -3693,19 +3790,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.
@@ -3774,10 +3871,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);
@@ -4559,7 +4654,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;
@@ -4576,7 +4671,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;
@@ -5017,9 +5112,10 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
        const struct ssh_mac *scmac_tobe;
        const struct ssh_compress *cscomp_tobe;
        const struct ssh_compress *sccomp_tobe;
-       char *hostkeydata, *sigdata, *keystr, *fingerprint;
-       int hostkeylen, siglen;
+       char *hostkeydata, *sigdata, *rsakeydata, *keystr, *fingerprint;
+       int hostkeylen, siglen, rsakeylen;
        void *hkey;                    /* actual host key */
+       void *rsakey;                  /* for RSA kex */
        unsigned char exchange_hash[SSH2_KEX_MAX_HASH_LEN];
        int n_preferred_kex;
        const struct ssh_kexes *preferred_kex[KEX_MAX];
@@ -5073,6 +5169,10 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
                s->preferred_kex[s->n_preferred_kex++] =
                    &ssh_diffiehellman_group1;
                break;
+             case KEX_RSA:
+               s->preferred_kex[s->n_preferred_kex++] =
+                   &ssh_rsa_kex;
+               break;
              case KEX_WARN:
                /* Flag for later. Don't bother if it's the last in
                 * the list. */
@@ -5472,6 +5572,8 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
            crWaitUntil(pktin);                /* Ignore packet */
     }
 
+    if (ssh->kex->main_type == KEXTYPE_DH) {
+       /* XXX The lines below should be reindented before this is committed.*/
     /*
      * Work out the number of bits of key we will need from the key
      * exchange. We start with the maximum key length of either
@@ -5547,6 +5649,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
     }
     set_busy_status(ssh->frontend, BUSY_CPU); /* cogitate */
     ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);
+    s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen);
     s->f = ssh2_pkt_getmp(pktin);
     if (!s->f) {
        bombout(("unable to parse key exchange reply packet"));
@@ -5568,11 +5671,120 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
     }
     hash_mpint(ssh->kex->hash, ssh->exhash, s->e);
     hash_mpint(ssh->kex->hash, ssh->exhash, s->f);
+
+    dh_cleanup(ssh->kex_ctx);
+    freebn(s->f);
+    if (!ssh->kex->pdata) {
+        freebn(s->g);
+       freebn(s->p);
+    }
+        /* XXX end incorrectly-indented section */
+    } else {
+       logeventf(ssh, "Doing RSA key exchange with hash %s",
+                 ssh->kex->hash->text_name);
+       ssh->pkt_ctx |= SSH2_PKTCTX_RSAKEX;
+        /*
+         * RSA key exchange. First expect a KEXRSA_PUBKEY packet
+         * from the server.
+         */
+        crWaitUntil(pktin);
+        if (pktin->type != SSH2_MSG_KEXRSA_PUBKEY) {
+            bombout(("expected RSA public key packet from server"));
+            crStop(0);
+        }
+
+        ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);
+        hash_string(ssh->kex->hash, ssh->exhash,
+                   s->hostkeydata, s->hostkeylen);
+       s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen);
+
+        {
+            char *keydata;
+            ssh_pkt_getstring(pktin, &keydata, &s->rsakeylen);
+            s->rsakeydata = snewn(s->rsakeylen, char);
+            memcpy(s->rsakeydata, keydata, s->rsakeylen);
+        }
+
+        s->rsakey = ssh_rsakex_newkey(s->rsakeydata, s->rsakeylen);
+        if (!s->rsakey) {
+            sfree(s->rsakeydata);
+            bombout(("unable to parse RSA public key from server"));
+            crStop(0);
+        }
+
+        hash_string(ssh->kex->hash, ssh->exhash, s->rsakeydata, s->rsakeylen);
+
+        /*
+         * Next, set up a shared secret K, of precisely KLEN -
+         * 2*HLEN - 49 bits, where KLEN is the bit length of the
+         * RSA key modulus and HLEN is the bit length of the hash
+         * we're using.
+         */
+        {
+            int klen = ssh_rsakex_klen(s->rsakey);
+            int nbits = klen - (2*ssh->kex->hash->hlen*8 + 49);
+            int i, byte = 0;
+            unsigned char *kstr1, *kstr2, *outstr;
+            int kstr1len, kstr2len, outstrlen;
+
+            s->K = bn_power_2(nbits - 1);
+
+            for (i = 0; i < nbits; i++) {
+                if ((i & 7) == 0) {
+                    byte = random_byte();
+                }
+                bignum_set_bit(s->K, i, (byte >> (i & 7)) & 1);
+            }
+
+            /*
+             * Encode this as an mpint.
+             */
+            kstr1 = ssh2_mpint_fmt(s->K, &kstr1len);
+            kstr2 = snewn(kstr2len = 4 + kstr1len, unsigned char);
+            PUT_32BIT(kstr2, kstr1len);
+            memcpy(kstr2 + 4, kstr1, kstr1len);
+
+            /*
+             * Encrypt it with the given RSA key.
+             */
+            outstrlen = (klen + 7) / 8;
+            outstr = snewn(outstrlen, unsigned char);
+            ssh_rsakex_encrypt(ssh->kex->hash, kstr2, kstr2len,
+                              outstr, outstrlen, s->rsakey);
+
+            /*
+             * And send it off in a return packet.
+             */
+            s->pktout = ssh2_pkt_init(SSH2_MSG_KEXRSA_SECRET);
+            ssh2_pkt_addstring_start(s->pktout);
+            ssh2_pkt_addstring_data(s->pktout, outstr, outstrlen);
+            ssh2_pkt_send_noqueue(ssh, s->pktout);
+
+           hash_string(ssh->kex->hash, ssh->exhash, outstr, outstrlen);
+
+            sfree(kstr2);
+            sfree(kstr1);
+            sfree(outstr);
+        }
+
+        ssh_rsakex_freekey(s->rsakey);
+
+        crWaitUntil(pktin);
+        if (pktin->type != SSH2_MSG_KEXRSA_DONE) {
+            sfree(s->rsakeydata);
+            bombout(("expected signature packet from server"));
+            crStop(0);
+        }
+
+        ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen);
+
+        sfree(s->rsakeydata);
+    }
+
     hash_mpint(ssh->kex->hash, ssh->exhash, s->K);
     assert(ssh->kex->hash->hlen <= sizeof(s->exchange_hash));
     ssh->kex->hash->final(ssh->exhash, s->exchange_hash);
 
-    dh_cleanup(ssh->kex_ctx);
     ssh->kex_ctx = NULL;
 
 #if 0
@@ -5580,7 +5792,6 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
     dmemdump(s->exchange_hash, ssh->kex->hash->hlen);
 #endif
 
-    s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen);
     if (!s->hkey ||
        !ssh->hostkey->verifysig(s->hkey, s->sigdata, s->siglen,
                                 (char *)s->exchange_hash,
@@ -5762,14 +5973,9 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
                  ssh->sccomp->text_name);
 
     /*
-     * Free key exchange data.
+     * Free shared secret.
      */
-    freebn(s->f);
     freebn(s->K);
-    if (!ssh->kex->pdata) {
-       freebn(s->g);
-       freebn(s->p);
-    }
 
     /*
      * Key exchange is over. Loop straight back round if we have a
@@ -5996,7 +6202,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;
@@ -6013,7 +6220,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;
@@ -6282,11 +6489,13 @@ static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin)
                        is_plausible = FALSE;
                }
            }
+           ssh->exitcode = 128;       /* means `unknown signal' */
            if (is_plausible) {
                if (is_int) {
                    /* Old non-standard OpenSSH. */
                    int signum = ssh_pkt_getuint32(pktin);
                    fmt_sig = dupprintf(" %d", signum);
+                   ssh->exitcode = 128 + signum;
                } else {
                    /* As per the drafts. */
                    char *sig;
@@ -6298,6 +6507,60 @@ static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin)
                        fmt_sig = dupprintf(" \"%.*s\"",
                                            siglen, sig);
                    }
+
+                   /*
+                    * Really hideous method of translating the
+                    * signal description back into a locally
+                    * meaningful number.
+                    */
+
+                   if (0)
+                       ;
+#define TRANSLATE_SIGNAL(s) \
+    else if (siglen == lenof(#s)-1 && !memcmp(sig, #s, siglen)) \
+        ssh->exitcode = 128 + SIG ## s
+#ifdef SIGABRT
+                   TRANSLATE_SIGNAL(ABRT);
+#endif
+#ifdef SIGALRM
+                   TRANSLATE_SIGNAL(ALRM);
+#endif
+#ifdef SIGFPE
+                   TRANSLATE_SIGNAL(FPE);
+#endif
+#ifdef SIGHUP
+                   TRANSLATE_SIGNAL(HUP);
+#endif
+#ifdef SIGILL
+                   TRANSLATE_SIGNAL(ILL);
+#endif
+#ifdef SIGINT
+                   TRANSLATE_SIGNAL(INT);
+#endif
+#ifdef SIGKILL
+                   TRANSLATE_SIGNAL(KILL);
+#endif
+#ifdef SIGPIPE
+                   TRANSLATE_SIGNAL(PIPE);
+#endif
+#ifdef SIGQUIT
+                   TRANSLATE_SIGNAL(QUIT);
+#endif
+#ifdef SIGSEGV
+                   TRANSLATE_SIGNAL(SEGV);
+#endif
+#ifdef SIGTERM
+                   TRANSLATE_SIGNAL(TERM);
+#endif
+#ifdef SIGUSR1
+                   TRANSLATE_SIGNAL(USR1);
+#endif
+#ifdef SIGUSR2
+                   TRANSLATE_SIGNAL(USR2);
+#endif
+#undef TRANSLATE_SIGNAL
+                   else
+                       ssh->exitcode = 128;
                }
                core = ssh2_pkt_getbool(pktin);
                ssh_pkt_getstring(pktin, &msg, &msglen);
@@ -7358,7 +7621,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);
@@ -7366,7 +7628,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
@@ -7436,7 +7698,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 */
@@ -7445,7 +7706,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;
 
@@ -7487,6 +7748,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: "),
@@ -7520,10 +7796,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.)
                         */
-                       got_new = (strcmp(s->cur_prompt->prompts[0]->result,
-                                         s->cur_prompt->prompts[1]->result)
+                       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[1]->result,
+                                         s->cur_prompt->prompts[2]->result)
                                   == 0);
                        if (!got_new)
                            /* They don't. Silly user. */
@@ -7536,7 +7827,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 */
@@ -7545,10 +7835,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");
                    
                    /*
@@ -8733,8 +9023,7 @@ static void ssh_unthrottle(void *handle, int bufsize)
            ssh1_throttle(ssh, -1);
        }
     } else {
-       if (ssh->mainchan && ssh->mainchan->closes == 0)
-           ssh2_set_window(ssh->mainchan, OUR_V2_WINSIZE - bufsize);
+       ssh2_set_window(ssh->mainchan, OUR_V2_WINSIZE - bufsize);
     }
 }