Add input-focus support for System 7, where the Control Manager can't do
[u/mdw/putty] / ssh.c
diff --git a/ssh.c b/ssh.c
index 1b2e7e1..25dee28 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -163,6 +163,7 @@ static const char *const ssh2_disconnect_reasons[] = {
 #define BUG_SSH2_RSA_PADDING                    16
 #define BUG_SSH2_DERIVEKEY                       32
 #define BUG_SSH2_DH_GEX                          64
+#define BUG_SSH2_PK_SESSIONID                   128
 
 #define translate(x) if (type == x) return #x
 #define translatec(x,ctx) if (type == x && (pkt_ctx & ctx)) return #x
@@ -268,11 +269,30 @@ static char *ssh2_pkt_type(int pkt_ctx, int type)
 
 enum { PKT_END, PKT_INT, PKT_CHAR, PKT_DATA, PKT_STR, PKT_BIGNUM };
 
-/* Coroutine mechanics for the sillier bits of the code */
+/*
+ * Coroutine mechanics for the sillier bits of the code. If these
+ * macros look impenetrable to you, you might find it helpful to
+ * read
+ * 
+ *   http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html
+ * 
+ * which explains the theory behind these macros.
+ * 
+ * In particular, if you are getting `case expression not constant'
+ * errors when building with MS Visual Studio, this is because MS's
+ * Edit and Continue debugging feature causes their compiler to
+ * violate ANSI C. To disable Edit and Continue debugging:
+ * 
+ *  - right-click ssh.c in the FileView
+ *  - click Settings
+ *  - select the C/C++ tab and the General category
+ *  - under `Debug info:', select anything _other_ than `Program
+ *    Database for Edit and Continue'.
+ */
 #define crBegin(v)     { int *crLine = &v; switch(v) { case 0:;
 #define crState(t) \
     struct t *s; \
-    if (!ssh->t) ssh->t = smalloc(sizeof(struct t)); \
+    if (!ssh->t) ssh->t = snew(struct t); \
     s = ssh->t;
 #define crFinish(z)    } *crLine = 0; return (z); }
 #define crFinishV      } *crLine = 0; return; }
@@ -734,7 +754,7 @@ static int alloc_channel_id(Ssh ssh)
     return low + 1 + CHANNEL_NUMBER_OFFSET;
 }
 
-static void c_write(Ssh ssh, char *buf, int len)
+static void c_write(Ssh ssh, const char *buf, int len)
 {
     if ((flags & FLAG_STDERR)) {
        int i;
@@ -746,7 +766,7 @@ static void c_write(Ssh ssh, char *buf, int len)
     from_backend(ssh->frontend, 1, buf, len);
 }
 
-static void c_write_untrusted(Ssh ssh, char *buf, int len)
+static void c_write_untrusted(Ssh ssh, const char *buf, int len)
 {
     int i;
     for (i = 0; i < len; i++) {
@@ -757,7 +777,7 @@ static void c_write_untrusted(Ssh ssh, char *buf, int len)
     }
 }
 
-static void c_write_str(Ssh ssh, char *buf)
+static void c_write_str(Ssh ssh, const char *buf)
 {
     c_write(ssh, buf, strlen(buf));
 }
@@ -794,7 +814,8 @@ static int ssh1_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
 
     if (ssh->pktin.maxlen < st->biglen) {
        ssh->pktin.maxlen = st->biglen;
-       ssh->pktin.data = srealloc(ssh->pktin.data, st->biglen + APIEXTRA);
+       ssh->pktin.data = sresize(ssh->pktin.data, st->biglen + APIEXTRA,
+                                 unsigned char);
     }
 
     st->to_read = st->biglen;
@@ -839,8 +860,9 @@ static int ssh1_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
 
        if (ssh->pktin.maxlen < st->pad + decomplen) {
            ssh->pktin.maxlen = st->pad + decomplen;
-           ssh->pktin.data = srealloc(ssh->pktin.data,
-                                      ssh->pktin.maxlen + APIEXTRA);
+           ssh->pktin.data = sresize(ssh->pktin.data,
+                                     ssh->pktin.maxlen + APIEXTRA,
+                                     unsigned char);
            ssh->pktin.body = ssh->pktin.data + st->pad + 1;
        }
 
@@ -922,7 +944,8 @@ static int ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
 
     if (ssh->pktin.maxlen < st->cipherblk) {
        ssh->pktin.maxlen = st->cipherblk;
-       ssh->pktin.data = srealloc(ssh->pktin.data, st->cipherblk + APIEXTRA);
+       ssh->pktin.data = sresize(ssh->pktin.data, st->cipherblk + APIEXTRA,
+                                 unsigned char);
     }
 
     /*
@@ -973,8 +996,9 @@ static int ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
      */
     if (ssh->pktin.maxlen < st->packetlen + st->maclen) {
        ssh->pktin.maxlen = st->packetlen + st->maclen;
-       ssh->pktin.data = srealloc(ssh->pktin.data,
-                                  ssh->pktin.maxlen + APIEXTRA);
+       ssh->pktin.data = sresize(ssh->pktin.data,
+                                 ssh->pktin.maxlen + APIEXTRA,
+                                 unsigned char);
     }
 
     /*
@@ -1016,8 +1040,9 @@ static int ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
                                    &newpayload, &newlen)) {
            if (ssh->pktin.maxlen < newlen + 5) {
                ssh->pktin.maxlen = newlen + 5;
-               ssh->pktin.data = srealloc(ssh->pktin.data,
-                                          ssh->pktin.maxlen + APIEXTRA);
+               ssh->pktin.data = sresize(ssh->pktin.data,
+                                         ssh->pktin.maxlen + APIEXTRA,
+                                         unsigned char);
            }
            ssh->pktin.length = 5 + newlen;
            memcpy(ssh->pktin.data + 5, newpayload, newlen);
@@ -1150,9 +1175,11 @@ static void ssh1_pktout_size(Ssh ssh, int len)
 #ifdef MSCRYPTOAPI
        /* Allocate enough buffer space for extra block
         * for MS CryptEncrypt() */
-       ssh->pktout.data = srealloc(ssh->pktout.data, biglen + 12);
+       ssh->pktout.data = sresize(ssh->pktout.data, biglen + 12,
+                                  unsigned char);
 #else
-       ssh->pktout.data = srealloc(ssh->pktout.data, biglen + 4);
+       ssh->pktout.data = sresize(ssh->pktout.data, biglen + 4,
+                                  unsigned char);
 #endif
     }
     ssh->pktout.body = ssh->pktout.data + 4 + pad + 1;
@@ -1166,8 +1193,18 @@ static void s_wrpkt_start(Ssh ssh, int type, int len)
 
 static int s_wrpkt_prepare(Ssh ssh)
 {
-    int pad, len, biglen, i;
+    int pad, biglen, i;
     unsigned long crc;
+#ifdef __SC__
+    /*
+     * XXX various versions of SC (including 8.8.4) screw up the
+     * register allocation in this function and use the same register
+     * (D6) for len and as a temporary, with predictable results.  The
+     * following sledgehammer prevents this.
+     */
+    volatile
+#endif
+    int len;
 
     ssh->pktout.body[-1] = ssh->pktout.type;
 
@@ -1218,8 +1255,9 @@ static void s_wrpkt_defer(Ssh ssh)
     len = s_wrpkt_prepare(ssh);
     if (ssh->deferred_len + len > ssh->deferred_size) {
        ssh->deferred_size = ssh->deferred_len + len + 128;
-       ssh->deferred_send_data = srealloc(ssh->deferred_send_data,
-                                          ssh->deferred_size);
+       ssh->deferred_send_data = sresize(ssh->deferred_send_data,
+                                         ssh->deferred_size,
+                                         unsigned char);
     }
     memcpy(ssh->deferred_send_data + ssh->deferred_len, ssh->pktout.data, len);
     ssh->deferred_len += len;
@@ -1366,8 +1404,9 @@ static void ssh2_pkt_ensure(Ssh ssh, int length)
 {
     if (ssh->pktout.maxlen < length) {
        ssh->pktout.maxlen = length + 256;
-       ssh->pktout.data = srealloc(ssh->pktout.data,
-                                   ssh->pktout.maxlen + APIEXTRA);
+       ssh->pktout.data = sresize(ssh->pktout.data,
+                                  ssh->pktout.maxlen + APIEXTRA,
+                                  unsigned char);
        if (!ssh->pktout.data)
            fatalbox("Out of memory");
     }
@@ -1423,7 +1462,7 @@ static unsigned char *ssh2_mpint_fmt(Bignum b, int *len)
 {
     unsigned char *p;
     int i, n = (bignum_bitcount(b) + 7) / 8;
-    p = smalloc(n + 1);
+    p = snewn(n + 1, unsigned char);
     if (!p)
        fatalbox("out of memory");
     p[0] = 0;
@@ -1534,8 +1573,9 @@ static void ssh2_pkt_defer(Ssh ssh)
     int len = ssh2_pkt_construct(ssh);
     if (ssh->deferred_len + len > ssh->deferred_size) {
        ssh->deferred_size = ssh->deferred_len + len + 128;
-       ssh->deferred_send_data = srealloc(ssh->deferred_send_data,
-                                          ssh->deferred_size);
+       ssh->deferred_send_data = sresize(ssh->deferred_send_data,
+                                         ssh->deferred_size,
+                                         unsigned char);
     }
     memcpy(ssh->deferred_send_data + ssh->deferred_len, ssh->pktout.data, len);
     ssh->deferred_len += len;
@@ -1719,8 +1759,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
 
     ssh->remote_bugs = 0;
 
-    if (ssh->cfg.sshbug_ignore1 == BUG_ON ||
-       (ssh->cfg.sshbug_ignore1 == BUG_AUTO &&
+    if (ssh->cfg.sshbug_ignore1 == FORCE_ON ||
+       (ssh->cfg.sshbug_ignore1 == AUTO &&
         (!strcmp(imp, "1.2.18") || !strcmp(imp, "1.2.19") ||
          !strcmp(imp, "1.2.20") || !strcmp(imp, "1.2.21") ||
          !strcmp(imp, "1.2.22") || !strcmp(imp, "Cisco-1.25")))) {
@@ -1733,8 +1773,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
        logevent("We believe remote version has SSH1 ignore bug");
     }
 
-    if (ssh->cfg.sshbug_plainpw1 == BUG_ON ||
-       (ssh->cfg.sshbug_plainpw1 == BUG_AUTO &&
+    if (ssh->cfg.sshbug_plainpw1 == FORCE_ON ||
+       (ssh->cfg.sshbug_plainpw1 == AUTO &&
         (!strcmp(imp, "Cisco-1.25")))) {
        /*
         * These versions need a plain password sent; they can't
@@ -1745,8 +1785,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
        logevent("We believe remote version needs a plain SSH1 password");
     }
 
-    if (ssh->cfg.sshbug_rsa1 == BUG_ON ||
-       (ssh->cfg.sshbug_rsa1 == BUG_AUTO &&
+    if (ssh->cfg.sshbug_rsa1 == FORCE_ON ||
+       (ssh->cfg.sshbug_rsa1 == AUTO &&
         (!strcmp(imp, "Cisco-1.25")))) {
        /*
         * These versions apparently have no clue whatever about
@@ -1757,8 +1797,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
        logevent("We believe remote version can't handle RSA authentication");
     }
 
-    if (ssh->cfg.sshbug_hmac2 == BUG_ON ||
-       (ssh->cfg.sshbug_hmac2 == BUG_AUTO &&
+    if (ssh->cfg.sshbug_hmac2 == FORCE_ON ||
+       (ssh->cfg.sshbug_hmac2 == AUTO &&
         (wc_match("2.1.0*", imp) || wc_match("2.0.*", imp) ||
          wc_match("2.2.0*", imp) || wc_match("2.3.0*", imp) ||
          wc_match("2.1 *", imp)))) {
@@ -1769,9 +1809,9 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
        logevent("We believe remote version has SSH2 HMAC bug");
     }
 
-    if (ssh->cfg.sshbug_derivekey2 == BUG_ON ||
-       (ssh->cfg.sshbug_derivekey2 == BUG_AUTO &&
-        (wc_match("2.0.0*", imp) || wc_match("2.0.1[01]*", imp) ))) {
+    if (ssh->cfg.sshbug_derivekey2 == FORCE_ON ||
+       (ssh->cfg.sshbug_derivekey2 == AUTO &&
+        (wc_match("2.0.0*", imp) || wc_match("2.0.10*", imp) ))) {
        /*
         * These versions have the key-derivation bug (failing to
         * include the literal shared secret in the hashes that
@@ -1781,8 +1821,8 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
        logevent("We believe remote version has SSH2 key-derivation bug");
     }
 
-    if (ssh->cfg.sshbug_rsapad2 == BUG_ON ||
-       (ssh->cfg.sshbug_rsapad2 == BUG_AUTO &&
+    if (ssh->cfg.sshbug_rsapad2 == FORCE_ON ||
+       (ssh->cfg.sshbug_rsapad2 == AUTO &&
         (wc_match("OpenSSH_2.[5-9]*", imp) ||
          wc_match("OpenSSH_3.[0-2]*", imp)))) {
        /*
@@ -1792,7 +1832,18 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
        logevent("We believe remote version has SSH2 RSA padding bug");
     }
 
-    if (ssh->cfg.sshbug_dhgex2 == BUG_ON) {
+    if (ssh->cfg.sshbug_pksessid2 == FORCE_ON ||
+       (ssh->cfg.sshbug_pksessid2 == AUTO &&
+        wc_match("OpenSSH_2.[0-2]*", imp))) {
+       /*
+        * These versions have the SSH2 session-ID bug in
+        * public-key authentication.
+        */
+       ssh->remote_bugs |= BUG_SSH2_PK_SESSIONID;
+       logevent("We believe remote version has SSH2 public-key-session-ID bug");
+    }
+
+    if (ssh->cfg.sshbug_dhgex2 == FORCE_ON) {
        /*
         * User specified the SSH2 DH GEX bug.
         */
@@ -1835,7 +1886,7 @@ static int do_ssh_init(Ssh ssh, unsigned char c)
     }
 
     s->vstrsize = 16;
-    s->vstring = smalloc(s->vstrsize);
+    s->vstring = snewn(s->vstrsize, char);
     strcpy(s->vstring, "SSH-");
     s->vslen = 4;
     s->i = 0;
@@ -1843,7 +1894,7 @@ static int do_ssh_init(Ssh ssh, unsigned char c)
        crReturn(1);                   /* get another char */
        if (s->vslen >= s->vstrsize - 1) {
            s->vstrsize += 16;
-           s->vstring = srealloc(s->vstring, s->vstrsize);
+           s->vstring = sresize(s->vstring, s->vstrsize, char);
        }
        s->vstring[s->vslen++] = c;
        if (s->i >= 0) {
@@ -1863,7 +1914,7 @@ static int do_ssh_init(Ssh ssh, unsigned char c)
     s->vstring[strcspn(s->vstring, "\r\n")] = '\0';/* remove EOL chars */
     {
        char *vlog;
-       vlog = smalloc(20 + s->vslen);
+       vlog = snewn(20 + s->vslen, char);
        sprintf(vlog, "Server version: %s", s->vstring);
        logevent(vlog);
        sfree(vlog);
@@ -2042,7 +2093,7 @@ static char *connect_to_host(Ssh ssh, char *host, int port,
     SockAddr addr;
     char *err;
 
-    ssh->savedhost = smalloc(1 + strlen(host));
+    ssh->savedhost = snewn(1 + strlen(host), char);
     if (!ssh->savedhost)
        fatalbox("Out of memory");
     strcpy(ssh->savedhost, host);
@@ -2181,7 +2232,13 @@ static int process_userpass_input(Ssh ssh, unsigned char *in, int inlen)
            return -1;
            break;
          default:
-           if (((c >= ' ' && c <= '~') ||
+           /*
+            * This simplistic check for printability is disabled
+            * when we're doing password input, because some people
+            * have control characters in their passwords.o
+            */
+           if ((!ssh->userpass_input_echo ||
+                (c >= ' ' && c <= '~') ||
                 ((unsigned char) c >= 160))
                && ssh->userpass_input_bufpos < ssh->userpass_input_buflen-1) {
                ssh->userpass_input_buffer[ssh->userpass_input_bufpos++] = c;
@@ -2279,7 +2336,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, int ispkt)
 
     s->len = (hostkey.bytes > servkey.bytes ? hostkey.bytes : servkey.bytes);
 
-    s->rsabuf = smalloc(s->len);
+    s->rsabuf = snewn(s->len, unsigned char);
     if (!s->rsabuf)
        fatalbox("Out of memory");
 
@@ -2292,7 +2349,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, int ispkt)
         */
        int len = rsastr_len(&hostkey);
        char fingerprint[100];
-       char *keystr = smalloc(len);
+       char *keystr = snewn(len, char);
        if (!keystr)
            fatalbox("Out of memory");
        rsastr_fmt(keystr, &hostkey);
@@ -2457,8 +2514,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, int ispkt)
     }
     s->tis_auth_refused = s->ccard_auth_refused = 0;
     /* Load the public half of ssh->cfg.keyfile so we notice if it's in Pageant */
-    if (*ssh->cfg.keyfile) {
-       if (!rsakey_pubblob(ssh->cfg.keyfile,
+    if (!filename_is_null(ssh->cfg.keyfile)) {
+       if (!rsakey_pubblob(&ssh->cfg.keyfile,
                            &s->publickey_blob, &s->publickey_bloblen))
            s->publickey_blob = NULL;
     } else
@@ -2530,7 +2587,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, int ispkt)
                        len += ssh1_bignum_length(s->challenge);
                        len += 16;     /* session id */
                        len += 4;      /* response format */
-                       agentreq = smalloc(4 + len);
+                       agentreq = snewn(4 + len, char);
                        PUT_32BIT(agentreq, len);
                        q = agentreq + 4;
                        *q++ = SSH1_AGENTC_RSA_CHALLENGE;
@@ -2586,7 +2643,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, int ispkt)
            if (s->authed)
                break;
        }
-       if (*ssh->cfg.keyfile && !s->tried_publickey)
+       if (!filename_is_null(ssh->cfg.keyfile) && !s->tried_publickey)
            s->pwpkt_type = SSH1_CMSG_AUTH_RSA;
 
        if (ssh->cfg.try_tis_auth &&
@@ -2651,8 +2708,9 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, int ispkt)
            char msgbuf[256];
            if (flags & FLAG_VERBOSE)
                c_write_str(ssh, "Trying public key authentication.\r\n");
-           logeventf(ssh, "Trying public key \"%s\"", ssh->cfg.keyfile);
-           type = key_type(ssh->cfg.keyfile);
+           logeventf(ssh, "Trying public key \"%s\"",
+                     filename_to_str(&ssh->cfg.keyfile));
+           type = key_type(&ssh->cfg.keyfile);
            if (type != SSH_KEYTYPE_SSH1) {
                sprintf(msgbuf, "Key is of wrong type (%s)",
                        key_type_to_str(type));
@@ -2662,7 +2720,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, int ispkt)
                s->tried_publickey = 1;
                continue;
            }
-           if (!rsakey_encrypted(ssh->cfg.keyfile, &comment)) {
+           if (!rsakey_encrypted(&ssh->cfg.keyfile, &comment)) {
                if (flags & FLAG_VERBOSE)
                    c_write_str(ssh, "No passphrase required.\r\n");
                goto tryauth;
@@ -2718,10 +2776,10 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, int ispkt)
            s->tried_publickey = 1;
            
            {
-               int ret = loadrsakey(ssh->cfg.keyfile, &s->key, s->password);
+               int ret = loadrsakey(&ssh->cfg.keyfile, &s->key, s->password);
                if (ret == 0) {
                    c_write_str(ssh, "Couldn't load private key from ");
-                   c_write_str(ssh, ssh->cfg.keyfile);
+                   c_write_str(ssh, filename_to_str(&ssh->cfg.keyfile));
                    c_write_str(ssh, ".\r\n");
                    continue;          /* go and try password */
                }
@@ -2844,7 +2902,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, int ispkt)
 
                    assert(pwlen >= bottom && pwlen <= top);
 
-                   randomstr = smalloc(top + 1);
+                   randomstr = snewn(top + 1, char);
 
                    for (i = bottom; i <= top; i++) {
                        if (i == pwlen)
@@ -3134,7 +3192,7 @@ static void ssh1_protocol(Ssh ssh, unsigned char *in, int inlen, int ispkt)
                              dserv, "(", dport, dserv, ")");
                } else {
                    struct ssh_rportfwd *pf;
-                   pf = smalloc(sizeof(*pf));
+                   pf = snew(struct ssh_rportfwd);
                    strcpy(pf->dhost, host);
                    pf->dport = dport;
                    if (saddr) {
@@ -3282,7 +3340,7 @@ static void ssh1_protocol(Ssh ssh, unsigned char *in, int inlen, int ispkt)
                                PKT_INT, GET_32BIT(ssh->pktin.body), PKT_END);
                    logevent("Rejected X11 connect request");
                } else {
-                   c = smalloc(sizeof(struct ssh_channel));
+                   c = snew(struct ssh_channel);
                    c->ssh = ssh;
 
                    if (x11_init(&c->u.x11.s, ssh->cfg.x11_display, c,
@@ -3317,7 +3375,7 @@ static void ssh1_protocol(Ssh ssh, unsigned char *in, int inlen, int ispkt)
                    send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,
                                PKT_INT, GET_32BIT(ssh->pktin.body), PKT_END);
                } else {
-                   c = smalloc(sizeof(struct ssh_channel));
+                   c = snew(struct ssh_channel);
                    c->ssh = ssh;
                    c->remoteid = GET_32BIT(ssh->pktin.body);
                    c->localid = alloc_channel_id(ssh);
@@ -3338,7 +3396,7 @@ static void ssh1_protocol(Ssh ssh, unsigned char *in, int inlen, int ispkt)
                int hostsize, port;
                char host[256], buf[1024];
                char *p, *h, *e;
-               c = smalloc(sizeof(struct ssh_channel));
+               c = snew(struct ssh_channel);
                c->ssh = ssh;
 
                hostsize = GET_32BIT(ssh->pktin.body+4);
@@ -3494,7 +3552,8 @@ static void ssh1_protocol(Ssh ssh, unsigned char *in, int inlen, int ispkt)
                            if (c->u.a.lensofar == 4) {
                                c->u.a.totallen =
                                    4 + GET_32BIT(c->u.a.msglen);
-                               c->u.a.message = smalloc(c->u.a.totallen);
+                               c->u.a.message = snewn(c->u.a.totallen,
+                                                      unsigned char);
                                memcpy(c->u.a.message, c->u.a.msglen, 4);
                            }
                            if (c->u.a.lensofar >= 4 && len > 0) {
@@ -4031,6 +4090,7 @@ static int do_ssh2_transport(Ssh ssh, unsigned char *in, int inlen, int ispkt)
     SHA_Final(&ssh->exhash, s->exchange_hash);
 
     dh_cleanup(ssh->kex_ctx);
+    ssh->kex_ctx = NULL;
 
 #if 0
     debug(("Exchange hash is:\n"));
@@ -4387,20 +4447,22 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
        s->tried_keyb_inter = FALSE;
        s->kbd_inter_running = FALSE;
        /* Load the pub half of ssh->cfg.keyfile so we notice if it's in Pageant */
-       if (*ssh->cfg.keyfile) {
+       if (!filename_is_null(ssh->cfg.keyfile)) {
            int keytype;
-           logeventf(ssh, "Reading private key file \"%.150s\"", ssh->cfg.keyfile);
-           keytype = key_type(ssh->cfg.keyfile);
+           logeventf(ssh, "Reading private key file \"%.150s\"",
+                     filename_to_str(&ssh->cfg.keyfile));
+           keytype = key_type(&ssh->cfg.keyfile);
            if (keytype == SSH_KEYTYPE_SSH2) {
                s->publickey_blob =
-                   ssh2_userkey_loadpub(ssh->cfg.keyfile, NULL,
+                   ssh2_userkey_loadpub(&ssh->cfg.keyfile, NULL,
                                         &s->publickey_bloblen);
            } else {
                char *msgbuf;
                logeventf(ssh, "Unable to use this key file (%s)",
                          key_type_to_str(keytype));
                msgbuf = dupprintf("Unable to use key file \"%.150s\""
-                                  " (%s)\r\n", ssh->cfg.keyfile,
+                                  " (%s)\r\n",
+                                  filename_to_str(&ssh->cfg.keyfile),
                                   key_type_to_str(keytype));
                c_write_str(ssh, msgbuf);
                sfree(msgbuf);
@@ -4625,11 +4687,13 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
                        ssh2_pkt_addstring_data(ssh, s->pkblob, s->pklen);
 
                        s->siglen = ssh->pktout.length - 5 + 4 + 20;
+                        if (ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)
+                            s->siglen -= 4;
                        s->len = 1;       /* message type */
                        s->len += 4 + s->pklen; /* key blob */
                        s->len += 4 + s->siglen;        /* data to sign */
                        s->len += 4;      /* flags */
-                       s->agentreq = smalloc(4 + s->len);
+                       s->agentreq = snewn(4 + s->len, char);
                        PUT_32BIT(s->agentreq, s->len);
                        s->q = s->agentreq + 4;
                        *s->q++ = SSH2_AGENTC_SIGN_REQUEST;
@@ -4640,8 +4704,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
                        PUT_32BIT(s->q, s->siglen);
                        s->q += 4;
                        /* Now the data to be signed... */
-                       PUT_32BIT(s->q, 20);
-                       s->q += 4;
+                        if (!(ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)) {
+                            PUT_32BIT(s->q, 20);
+                            s->q += 4;
+                        }
                        memcpy(s->q, ssh->v2_session_id, 20);
                        s->q += 20;
                        memcpy(s->q, ssh->pktout.data + 5,
@@ -4691,7 +4757,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
                 * willing to accept it.
                 */
                pub_blob =
-                   (unsigned char *)ssh2_userkey_loadpub(ssh->cfg.keyfile,
+                   (unsigned char *)ssh2_userkey_loadpub(&ssh->cfg.keyfile,
                                                          &algorithm,
                                                          &pub_blob_len);
                if (pub_blob) {
@@ -4719,7 +4785,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
                     * Actually attempt a serious authentication using
                     * the key.
                     */
-                   if (ssh2_userkey_encrypted(ssh->cfg.keyfile, &comment)) {
+                   if (ssh2_userkey_encrypted(&ssh->cfg.keyfile, &comment)) {
                        sprintf(s->pwprompt,
                                "Passphrase for key \"%.100s\": ",
                                comment);
@@ -4871,7 +4937,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
                 */
                struct ssh2_userkey *key;
 
-               key = ssh2_load_userkey(ssh->cfg.keyfile, s->password);
+               key = ssh2_load_userkey(&ssh->cfg.keyfile, s->password);
                if (key == SSH2_WRONG_PASSPHRASE || key == NULL) {
                    if (key == SSH2_WRONG_PASSPHRASE) {
                        c_write_str(ssh, "Wrong passphrase\r\n");
@@ -4890,6 +4956,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
                } else {
                    unsigned char *pkblob, *sigblob, *sigdata;
                    int pkblob_len, sigblob_len, sigdata_len;
+                    int p;
 
                    /*
                     * We have loaded the private key and the server
@@ -4915,11 +4982,19 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
                     * outgoing packet.
                     */
                    sigdata_len = ssh->pktout.length - 5 + 4 + 20;
-                   sigdata = smalloc(sigdata_len);
-                   PUT_32BIT(sigdata, 20);
-                   memcpy(sigdata + 4, ssh->v2_session_id, 20);
-                   memcpy(sigdata + 24, ssh->pktout.data + 5,
+                    if (ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)
+                        sigdata_len -= 4;
+                   sigdata = snewn(sigdata_len, char);
+                    p = 0;
+                    if (!(ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)) {
+                        PUT_32BIT(sigdata+p, 20);
+                        p += 4;
+                    }
+                   memcpy(sigdata+p, ssh->v2_session_id, 20); p += 20;
+                   memcpy(sigdata+p, ssh->pktout.data + 5,
                           ssh->pktout.length - 5);
+                    p += ssh->pktout.length - 5;
+                    assert(p == sigdata_len);
                    sigblob = key->alg->sign(key->data, (char *)sigdata,
                                             sigdata_len, &sigblob_len);
                    ssh2_add_sigblob(ssh, pkblob, pkblob_len,
@@ -5041,7 +5116,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
      * So now create a channel with a session in it.
      */
     ssh->channels = newtree234(ssh_channelcmp);
-    ssh->mainchan = smalloc(sizeof(struct ssh_channel));
+    ssh->mainchan = snew(struct ssh_channel);
     ssh->mainchan->ssh = ssh;
     ssh->mainchan->localid = alloc_channel_id(ssh);
     ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_OPEN);
@@ -5198,7 +5273,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
                              dserv, "(", dport, dserv, ")");
                } else {
                    struct ssh_rportfwd *pf;
-                   pf = smalloc(sizeof(*pf));
+                   pf = snew(struct ssh_rportfwd);
                    strcpy(pf->dhost, host);
                    pf->dport = dport;
                    pf->sport = sport;
@@ -5466,7 +5541,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
                            if (c->u.a.lensofar == 4) {
                                c->u.a.totallen =
                                    4 + GET_32BIT(c->u.a.msglen);
-                               c->u.a.message = smalloc(c->u.a.totallen);
+                               c->u.a.message = snewn(c->u.a.totallen,
+                                                      unsigned char);
                                memcpy(c->u.a.message, c->u.a.msglen, 4);
                            }
                            if (c->u.a.lensofar >= 4 && length > 0) {
@@ -5730,7 +5806,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
                struct ssh_channel *c;
                unsigned remid, winsize, pktsize;
                ssh2_pkt_getstring(ssh, &type, &typelen);
-               c = smalloc(sizeof(struct ssh_channel));
+               c = snew(struct ssh_channel);
                c->ssh = ssh;
 
                remid = ssh2_pkt_getuint32(ssh);
@@ -5740,7 +5816,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
                port = ssh2_pkt_getuint32(ssh);
 
                if (typelen == 3 && !memcmp(type, "x11", 3)) {
-                   char *addrstr = smalloc(peeraddrlen+1);
+                   char *addrstr = snewn(peeraddrlen+1, char);
                    memcpy(addrstr, peeraddr, peeraddrlen);
                    peeraddr[peeraddrlen] = '\0';
 
@@ -5883,7 +5959,7 @@ static char *ssh_init(void *frontend_handle, void **backend_handle,
     char *p;
     Ssh ssh;
 
-    ssh = smalloc(sizeof(*ssh));
+    ssh = snew(struct ssh_tag);
     ssh->cfg = *cfg;                  /* STRUCTURE COPY */
     ssh->s = NULL;
     ssh->cipher = NULL;
@@ -5902,6 +5978,7 @@ static char *ssh_init(void *frontend_handle, void **backend_handle,
     ssh->sccomp = NULL;
     ssh->sc_comp_ctx = NULL;
     ssh->kex = NULL;
+    ssh->kex_ctx = NULL;
     ssh->hostkey = NULL;
     ssh->exitcode = -1;
     ssh->state = SSH_STATE_PREPACKET;
@@ -5948,6 +6025,9 @@ static char *ssh_init(void *frontend_handle, void **backend_handle,
     ssh->term_width = ssh->cfg.width;
     ssh->term_height = ssh->cfg.height;
 
+    ssh->channels = NULL;
+    ssh->rportfwds = NULL;
+
     ssh->send_ok = 0;
     ssh->editing = 0;
     ssh->echoing = 0;
@@ -5964,6 +6044,65 @@ static char *ssh_init(void *frontend_handle, void **backend_handle,
     return NULL;
 }
 
+static void ssh_free(void *handle)
+{
+    Ssh ssh = (Ssh) handle;
+    struct ssh_channel *c;
+    struct ssh_rportfwd *pf;
+
+    if (ssh->v1_cipher_ctx)
+       ssh->cipher->free_context(ssh->v1_cipher_ctx);
+    if (ssh->cs_cipher_ctx)
+       ssh->cscipher->free_context(ssh->cs_cipher_ctx);
+    if (ssh->sc_cipher_ctx)
+       ssh->sccipher->free_context(ssh->sc_cipher_ctx);
+    if (ssh->cs_mac_ctx)
+       ssh->csmac->free_context(ssh->cs_mac_ctx);
+    if (ssh->sc_mac_ctx)
+       ssh->scmac->free_context(ssh->sc_mac_ctx);
+    if (ssh->cs_comp_ctx)
+       ssh->cscomp->compress_cleanup(ssh->cs_comp_ctx);
+    if (ssh->sc_comp_ctx)
+       ssh->sccomp->compress_cleanup(ssh->sc_comp_ctx);
+    if (ssh->kex_ctx)
+       dh_cleanup(ssh->kex_ctx);
+    sfree(ssh->savedhost);
+
+    if (ssh->channels) {
+       while ((c = delpos234(ssh->channels, 0)) != NULL) {
+           switch (c->type) {
+             case CHAN_X11:
+               if (c->u.x11.s != NULL)
+                   x11_close(c->u.x11.s);
+               break;
+             case CHAN_SOCKDATA:
+               if (c->u.pfd.s != NULL)
+                   pfd_close(c->u.pfd.s);
+               break;
+           }
+           sfree(c);
+       }
+       freetree234(ssh->channels);
+    }
+
+    if (ssh->rportfwds) {
+       while ((pf = delpos234(ssh->rportfwds, 0)) != NULL)
+           sfree(pf);
+       freetree234(ssh->rportfwds);
+    }
+    sfree(ssh->deferred_send_data);
+    if (ssh->x11auth)
+       x11_free_auth(ssh->x11auth);
+    sfree(ssh->do_ssh_init_state);
+    sfree(ssh->do_ssh1_login_state);
+    sfree(ssh->do_ssh2_transport_state);
+    sfree(ssh->do_ssh2_authconn_state);
+    
+    if (ssh->s)
+       sk_close(ssh->s);
+    sfree(ssh);
+}
+
 /*
  * Reconfigure the SSH backend.
  * 
@@ -6114,7 +6253,7 @@ void *new_sock_channel(void *handle, Socket s)
 {
     Ssh ssh = (Ssh) handle;
     struct ssh_channel *c;
-    c = smalloc(sizeof(struct ssh_channel));
+    c = snew(struct ssh_channel);
     c->ssh = ssh;
 
     if (c) {
@@ -6236,6 +6375,7 @@ extern int ssh_fallback_cmd(void *handle)
 
 Backend ssh_backend = {
     ssh_init,
+    ssh_free,
     ssh_reconfig,
     ssh_send,
     ssh_sendbuffer,