Fix the `SERIOUS NETWORK INTERNAL ERROR' oversight in winnet.c. See
[sgt/putty] / ssh.c
diff --git a/ssh.c b/ssh.c
index 4a6db9f..cc6ee30 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -159,6 +159,8 @@ static const char *const ssh2_disconnect_reasons[] = {
  */
 #define BUG_CHOKES_ON_SSH1_IGNORE                 1
 #define BUG_SSH2_HMAC                             2
+#define BUG_NEEDS_SSH1_PLAIN_PASSWORD            4
+
 
 #define GET_32BIT(cp) \
     (((unsigned long)(unsigned char)(cp)[0] << 24) | \
@@ -1418,17 +1420,15 @@ static void ssh_detect_bugs(char *vstring)
     char *imp;                        /* pointer to implementation part */
     imp = vstring;
     imp += strcspn(imp, "-");
-    if (*imp)
-       imp++;
+    if (*imp) imp++;
     imp += strcspn(imp, "-");
-    if (*imp)
-       imp++;
+    if (*imp) imp++;
 
     ssh_remote_bugs = 0;
 
     if (!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, "1.2.22") || !strcmp(imp, "Cisco-1.25")) {
        /*
         * These versions don't support SSH1_MSG_IGNORE, so we have
         * to use a different defence against password length
@@ -1438,6 +1438,16 @@ static void ssh_detect_bugs(char *vstring)
        logevent("We believe remote version has SSH1 ignore bug");
     }
 
+    if (!strcmp(imp, "Cisco-1.25")) {
+       /*
+        * These versions need a plain password sent; they can't
+        * handle having a null and a random length of data after
+        * the password.
+        */
+       ssh_remote_bugs |= BUG_NEEDS_SSH1_PLAIN_PASSWORD;
+       logevent("We believe remote version needs a plain SSH1 password");
+    }
+
     if (!strncmp(imp, "2.1.0", 5) || !strncmp(imp, "2.0.", 4) ||
        !strncmp(imp, "2.2.0", 5) || !strncmp(imp, "2.3.0", 5) ||
        !strncmp(imp, "2.1 ", 4)) {
@@ -1479,8 +1489,8 @@ static int do_ssh_init(unsigned char c)
        crReturn(1);                   /* get another character */
     }
 
-    vstring = smalloc(16);
     vstrsize = 16;
+    vstring = smalloc(vstrsize);
     strcpy(vstring, "SSH-");
     vslen = 4;
     i = 0;
@@ -1506,10 +1516,10 @@ static int do_ssh_init(unsigned char c)
 
     vstring[vslen] = 0;
     vlog = smalloc(20 + vslen);
+    vstring[strcspn (vstring, "\r\n")] = '\0'; /* remove end-of-line chars */
     sprintf(vlog, "Server version: %s", vstring);
-    ssh_detect_bugs(vstring);
-    vlog[strcspn(vlog, "\r\n")] = '\0';
     logevent(vlog);
+    ssh_detect_bugs(vstring);
     sfree(vlog);
 
     /*
@@ -2405,26 +2415,17 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
                 * 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
+                * against password length sniffing.
                 */
-               if (ssh_remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE) {
-                   char string[64];
-                   char *s;
-                   int len;
-
-                   len = strlen(password);
-                   if (len < sizeof(string)) {
-                       s = string;
-                       strcpy(string, password);
-                       len++;         /* cover the zero byte */
-                       while (len < sizeof(string)) {
-                           string[len++] = (char) random_byte();
-                       }
-                   } else {
-                       s = password;
-                   }
-                   send_packet(pwpkt_type, PKT_INT, len,
-                               PKT_DATA, s, len, PKT_END);
-               } else {
+               if (!(ssh_remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE)) {
+                   /*
+                    * The server can deal with SSH1_MSG_IGNORE, so
+                    * we can use the primary defence.
+                    */
                    int bottom, top, pwlen, i;
                    char *randomstr;
 
@@ -2456,7 +2457,45 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
                                         PKT_STR, randomstr, PKT_END);
                        }
                    }
+                   logevent("Sending password with camouflage packets");
                    ssh_pkt_defersend();
+               } 
+               else if (!(ssh_remote_bugs & BUG_NEEDS_SSH1_PLAIN_PASSWORD)) {
+                   /*
+                    * The server can't deal with SSH1_MSG_IGNORE
+                    * but can deal with padded passwords, so we
+                    * can use the secondary defence.
+                    */
+                   char string[64];
+                   char *s;
+                   int len;
+
+                   len = strlen(password);
+                   if (len < sizeof(string)) {
+                       s = string;
+                       strcpy(string, password);
+                       len++;         /* cover the zero byte */
+                       while (len < sizeof(string)) {
+                           string[len++] = (char) random_byte();
+                       }
+                   } else {
+                       s = password;
+                   }
+                   logevent("Sending length-padded password");
+                   send_packet(pwpkt_type, PKT_INT, len,
+                               PKT_DATA, s, len, 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.
+                    */
+                   int len;
+                   len = strlen(password);
+                   logevent("Sending unpadded password");
+                   send_packet(pwpkt_type, PKT_INT, len,
+                               PKT_DATA, password, len, PKT_END);
                }
            } else {
                send_packet(pwpkt_type, PKT_STR, password, PKT_END);
@@ -2725,7 +2764,7 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt)
     if (eof_needed)
        ssh_special(TS_EOF);
 
-    ldisc_send(NULL, 0);              /* cause ldisc to notice changes */
+    ldisc_send(NULL, 0, 0);           /* cause ldisc to notice changes */
     ssh_send_ok = 1;
     ssh_channels = newtree234(ssh_channelcmp);
     while (1) {
@@ -4379,7 +4418,8 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
 
        if (pktin.type != SSH2_MSG_CHANNEL_SUCCESS) {
            if (pktin.type != SSH2_MSG_CHANNEL_FAILURE) {
-               bombout(("Server got confused by X11 forwarding request"));
+               bombout(("Unexpected response to X11 forwarding request:"
+                        " packet type %d", pktin.type));
                crReturnV;
            }
            logevent("X11 forwarding refused");
@@ -4467,7 +4507,9 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
 
                        if (pktin.type != SSH2_MSG_REQUEST_SUCCESS) {
                            if (pktin.type != SSH2_MSG_REQUEST_FAILURE) {
-                               bombout(("Server got confused by port forwarding request"));
+                               bombout(("Unexpected response to port "
+                                        "forwarding request: packet type %d",
+                                        pktin.type));
                                crReturnV;
                            }
                            logevent("Server refused this port forwarding");
@@ -4505,8 +4547,8 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
 
        if (pktin.type != SSH2_MSG_CHANNEL_SUCCESS) {
            if (pktin.type != SSH2_MSG_CHANNEL_FAILURE) {
-               bombout(
-                       ("Server got confused by agent forwarding request"));
+               bombout(("Unexpected response to agent forwarding request:"
+                        " packet type %d", pktin.type));
                crReturnV;
            }
            logevent("Agent forwarding refused");
@@ -4548,7 +4590,8 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
 
        if (pktin.type != SSH2_MSG_CHANNEL_SUCCESS) {
            if (pktin.type != SSH2_MSG_CHANNEL_FAILURE) {
-               bombout(("Server got confused by pty request"));
+               bombout(("Unexpected response to pty request:"
+                        " packet type %d", pktin.type));
                crReturnV;
            }
            c_write_str("Server refused to allocate pty\r\n");
@@ -4605,7 +4648,8 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
        } while (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST);
        if (pktin.type != SSH2_MSG_CHANNEL_SUCCESS) {
            if (pktin.type != SSH2_MSG_CHANNEL_FAILURE) {
-               bombout(("Server got confused by shell/command request"));
+               bombout(("Unexpected response to shell/command request:"
+                        " packet type %d", pktin.type));
                crReturnV;
            }
            /*
@@ -4636,7 +4680,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
     /*
      * Transfer data!
      */
-    ldisc_send(NULL, 0);              /* cause ldisc to notice changes */
+    ldisc_send(NULL, 0, 0);           /* cause ldisc to notice changes */
     ssh_send_ok = 1;
     while (1) {
        static int try_send;