Miscellaneous fixes for better interoperation with commercial SSH 2
[u/mdw/putty] / ssh.c
diff --git a/ssh.c b/ssh.c
index 8497815..29cddc4 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -25,7 +25,7 @@
                       if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)) \
                       fprintf(stderr, "%s\n", s); }
 
-#define bombout(msg) ( ssh_state == SSH_STATE_CLOSED, closesocket(s), \
+#define bombout(msg) ( ssh_state = SSH_STATE_CLOSED, closesocket(s), \
                        s = INVALID_SOCKET, connection_fatal msg )
 
 #define SSH1_MSG_DISCONNECT                       1    /* 0x1 */
@@ -167,7 +167,7 @@ const static struct ssh_kex *kex_algs[] = { &ssh_diffiehellman };
 extern const struct ssh_hostkey ssh_dss;
 const static struct ssh_hostkey *hostkey_algs[] = { &ssh_dss };
 
-extern const struct ssh_mac ssh_sha1;
+extern const struct ssh_mac ssh_sha1, ssh_sha1_buggy;
 
 static void nullmac_key(unsigned char *key) { }
 static void nullmac_generate(unsigned char *blk, int len, unsigned long seq) { }
@@ -176,6 +176,7 @@ const static struct ssh_mac ssh_mac_none = {
     nullmac_key, nullmac_key, nullmac_generate, nullmac_verify, "none", 0
 };
 const static struct ssh_mac *macs[] = { &ssh_sha1, &ssh_mac_none };
+const static struct ssh_mac *buggymacs[] = { &ssh_sha1_buggy, &ssh_mac_none };
 
 const static struct ssh_compress ssh_comp_none = {
     "none"
@@ -810,7 +811,7 @@ static int ssh_versioncmp(char *a, char *b) {
  * state.
  */
 #include <stdio.h>
-void sha_string(SHA_State *s, void *str, int len) {
+static void sha_string(SHA_State *s, void *str, int len) {
     unsigned char lenblk[4];
     PUT_32BIT(lenblk, len);
     SHA_Bytes(s, lenblk, 4);
@@ -820,7 +821,7 @@ void sha_string(SHA_State *s, void *str, int len) {
 /*
  * SSH2 packet construction functions.
  */
-void ssh2_pkt_adddata(void *data, int len) {
+static void ssh2_pkt_adddata(void *data, int len) {
     pktout.length += len;
     if (pktout.maxlen < pktout.length) {
         pktout.maxlen = pktout.length + 256;
@@ -831,40 +832,40 @@ void ssh2_pkt_adddata(void *data, int len) {
     }
     memcpy(pktout.data+pktout.length-len, data, len);
 }
-void ssh2_pkt_addbyte(unsigned char byte) {
+static void ssh2_pkt_addbyte(unsigned char byte) {
     ssh2_pkt_adddata(&byte, 1);
 }
-void ssh2_pkt_init(int pkt_type) {
+static void ssh2_pkt_init(int pkt_type) {
     pktout.length = 5;
     ssh2_pkt_addbyte((unsigned char)pkt_type);
 }
-void ssh2_pkt_addbool(unsigned char value) {
+static void ssh2_pkt_addbool(unsigned char value) {
     ssh2_pkt_adddata(&value, 1);
 }
-void ssh2_pkt_adduint32(unsigned long value) {
+static void ssh2_pkt_adduint32(unsigned long value) {
     unsigned char x[4];
     PUT_32BIT(x, value);
     ssh2_pkt_adddata(x, 4);
 }
-void ssh2_pkt_addstring_start(void) {
+static void ssh2_pkt_addstring_start(void) {
     ssh2_pkt_adduint32(0);
     pktout.savedpos = pktout.length;
 }
-void ssh2_pkt_addstring_str(char *data) {
+static void ssh2_pkt_addstring_str(char *data) {
     ssh2_pkt_adddata(data, strlen(data));
     PUT_32BIT(pktout.data + pktout.savedpos - 4,
               pktout.length - pktout.savedpos);
 }
-void ssh2_pkt_addstring_data(char *data, int len) {
+static void ssh2_pkt_addstring_data(char *data, int len) {
     ssh2_pkt_adddata(data, len);
     PUT_32BIT(pktout.data + pktout.savedpos - 4,
               pktout.length - pktout.savedpos);
 }
-void ssh2_pkt_addstring(char *data) {
+static void ssh2_pkt_addstring(char *data) {
     ssh2_pkt_addstring_start();
     ssh2_pkt_addstring_str(data);
 }
-char *ssh2_mpint_fmt(Bignum b, int *len) {
+static char *ssh2_mpint_fmt(Bignum b, int *len) {
     unsigned char *p;
     int i, n = b[0];
     p = malloc(n * 2 + 1);
@@ -882,7 +883,7 @@ char *ssh2_mpint_fmt(Bignum b, int *len) {
     *len = n*2+1-i;
     return p;
 }
-void ssh2_pkt_addmp(Bignum b) {
+static void ssh2_pkt_addmp(Bignum b) {
     unsigned char *p;
     int len;
     p = ssh2_mpint_fmt(b, &len);
@@ -890,7 +891,7 @@ void ssh2_pkt_addmp(Bignum b) {
     ssh2_pkt_addstring_data(p, len);
     free(p);
 }
-void ssh2_pkt_send(void) {
+static void ssh2_pkt_send(void) {
     int cipherblk, maclen, padding, i;
     static unsigned long outgoing_sequence = 0;
 
@@ -938,7 +939,7 @@ void bndebug(char *string, Bignum b) {
 }
 #endif
 
-void sha_mpint(SHA_State *s, Bignum b) {
+static void sha_mpint(SHA_State *s, Bignum b) {
     unsigned char *p;
     int len;
     p = ssh2_mpint_fmt(b, &len);
@@ -949,7 +950,7 @@ void sha_mpint(SHA_State *s, Bignum b) {
 /*
  * SSH2 packet decode functions.
  */
-unsigned long ssh2_pkt_getuint32(void) {
+static unsigned long ssh2_pkt_getuint32(void) {
     unsigned long value;
     if (pktin.length - pktin.savedpos < 4)
         return 0;                      /* arrgh, no way to decline (FIXME?) */
@@ -957,7 +958,7 @@ unsigned long ssh2_pkt_getuint32(void) {
     pktin.savedpos += 4;
     return value;
 }
-void ssh2_pkt_getstring(char **p, int *length) {
+static void ssh2_pkt_getstring(char **p, int *length) {
     *p = NULL;
     if (pktin.length - pktin.savedpos < 4)
         return;
@@ -968,7 +969,7 @@ void ssh2_pkt_getstring(char **p, int *length) {
     *p = pktin.data+pktin.savedpos;
     pktin.savedpos += *length;
 }
-Bignum ssh2_pkt_getmp(void) {
+static Bignum ssh2_pkt_getmp(void) {
     char *p;
     int i, j, length;
     Bignum b;
@@ -1776,7 +1777,7 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt) {
 /*
  * Utility routine for decoding comma-separated strings in KEXINIT.
  */
-int in_commasep_string(char *needle, char *haystack, int haylen) {
+static int in_commasep_string(char *needle, char *haystack, int haylen) {
     int needlen = strlen(needle);
     while (1) {
         /*
@@ -1803,7 +1804,7 @@ int in_commasep_string(char *needle, char *haystack, int haylen) {
 /*
  * SSH2 key creation method.
  */
-void ssh2_mkkey(Bignum K, char *H, char chr, char *keyspace) {
+static void ssh2_mkkey(Bignum K, char *H, char chr, char *keyspace) {
     SHA_State s;
     /* First 20 bytes. */
     SHA_Init(&s);
@@ -1828,6 +1829,7 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
     static int i, len;
     static char *str;
     static Bignum e, f, K;
+    static const struct ssh_mac **maclist;
     static const struct ssh_cipher *cscipher_tobe = NULL;
     static const struct ssh_cipher *sccipher_tobe = NULL;
     static const struct ssh_mac *csmac_tobe = NULL;
@@ -1858,6 +1860,14 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
         preferred_cipher = &ssh_3des_ssh2;
     }
 
+    /*
+     * Be prepared to work around the buggy MAC problem.
+     */
+    if (cfg.buggymac)
+        maclist = buggymacs;
+    else
+        maclist = macs;
+
     begin_key_exchange:
     /*
      * Construct and send our key exchange packet.
@@ -1897,16 +1907,16 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
     }
     /* List client->server MAC algorithms. */
     ssh2_pkt_addstring_start();
-    for (i = 0; i < lenof(macs); i++) {
-        ssh2_pkt_addstring_str(macs[i]->name);
-        if (i < lenof(macs)-1)
+    for (i = 0; i < lenof(maclist); i++) {
+        ssh2_pkt_addstring_str(maclist[i]->name);
+        if (i < lenof(maclist)-1)
             ssh2_pkt_addstring_str(",");
     }
     /* List server->client MAC algorithms. */
     ssh2_pkt_addstring_start();
-    for (i = 0; i < lenof(macs); i++) {
-        ssh2_pkt_addstring_str(macs[i]->name);
-        if (i < lenof(macs)-1)
+    for (i = 0; i < lenof(maclist); i++) {
+        ssh2_pkt_addstring_str(maclist[i]->name);
+        if (i < lenof(maclist)-1)
             ssh2_pkt_addstring_str(",");
     }
     /* List client->server compression algorithms. */
@@ -1979,16 +1989,16 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
         }
     }
     ssh2_pkt_getstring(&str, &len);    /* client->server mac */
-    for (i = 0; i < lenof(macs); i++) {
-        if (in_commasep_string(macs[i]->name, str, len)) {
-            csmac_tobe = macs[i];
+    for (i = 0; i < lenof(maclist); i++) {
+        if (in_commasep_string(maclist[i]->name, str, len)) {
+            csmac_tobe = maclist[i];
             break;
         }
     }
     ssh2_pkt_getstring(&str, &len);    /* server->client mac */
-    for (i = 0; i < lenof(macs); i++) {
-        if (in_commasep_string(macs[i]->name, str, len)) {
-            scmac_tobe = macs[i];
+    for (i = 0; i < lenof(maclist); i++) {
+        if (in_commasep_string(maclist[i]->name, str, len)) {
+            scmac_tobe = maclist[i];
             break;
         }
     }
@@ -2275,7 +2285,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
     ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN);
     ssh2_pkt_addstring("session");
     ssh2_pkt_adduint32(mainchan->localid);
-    ssh2_pkt_adduint32(0x7FFFFFFFUL);  /* our window size */
+    ssh2_pkt_adduint32(0x8000UL);  /* our window size */
     ssh2_pkt_adduint32(0x4000UL);  /* our max pkt size */
     ssh2_pkt_send();
     crWaitUntilV(ispkt);
@@ -2419,6 +2429,9 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                     logevent("All channels closed. Disconnecting");
                     ssh2_pkt_init(SSH2_MSG_DISCONNECT);
                     ssh2_pkt_send();
+                    ssh_state = SSH_STATE_CLOSED;
+                    closesocket(s);
+                    s = INVALID_SOCKET;
                 }
                 continue;              /* remote sends close; ignore (FIXME) */
            } else if (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) {
@@ -2611,6 +2624,14 @@ static void ssh_special (Telnet_Special code) {
             ssh2_pkt_send();
         }
         logevent("Sent EOF message");
+    } else if (code == TS_PING) {
+        if (ssh_version == 1) {
+            send_packet(SSH1_MSG_IGNORE, PKT_STR, "", PKT_END);
+        } else {
+            ssh2_pkt_init(SSH2_MSG_IGNORE);
+            ssh2_pkt_addstring_start();
+            ssh2_pkt_send();
+        }
     } else {
         /* do nothing */
     }
@@ -2627,5 +2648,6 @@ Backend ssh_backend = {
     ssh_size,
     ssh_special,
     ssh_socket,
-    ssh_sendok
+    ssh_sendok,
+    22
 };