While I'm adding explanatory comments by the coroutine macros, it
[u/mdw/putty] / ssh.c
diff --git a/ssh.c b/ssh.c
index 88af248..662d521 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,7 +269,26 @@ 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; \
@@ -1166,8 +1186,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;
 
@@ -1771,7 +1801,7 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
 
     if (ssh->cfg.sshbug_derivekey2 == FORCE_ON ||
        (ssh->cfg.sshbug_derivekey2 == AUTO &&
-        (wc_match("2.0.0*", imp) || wc_match("2.0.1[01]*", imp) ))) {
+        (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
@@ -1792,6 +1822,17 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
        logevent("We believe remote version has SSH2 RSA padding bug");
     }
 
+    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.
@@ -2181,7 +2222,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;
@@ -4629,6 +4676,8 @@ 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 */
@@ -4644,8 +4693,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,
@@ -4894,6 +4945,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
@@ -4919,11 +4971,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;
+                    if (ssh->remote_bugs & BUG_SSH2_PK_SESSIONID)
+                        sigdata_len -= 4;
                    sigdata = smalloc(sigdata_len);
-                   PUT_32BIT(sigdata, 20);
-                   memcpy(sigdata + 4, ssh->v2_session_id, 20);
-                   memcpy(sigdata + 24, ssh->pktout.data + 5,
+                    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,