Tiny tinkerings with FAQ
[u/mdw/putty] / ssh.c
diff --git a/ssh.c b/ssh.c
index b0f1651..e913571 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -607,12 +607,6 @@ static int ssh1_rdpkt(unsigned char **data, int *datalen)
        (*data)++, (*datalen)--;
     }
 
-#ifdef FWHACK
-    if (st->len == 0x52656d6f) {       /* "Remo"te server has closed ... */
-       st->len = 0x300;               /* big enough to carry to end */
-    }
-#endif
-
     st->pad = 8 - (st->len % 8);
     st->biglen = st->len + st->pad;
     pktin.length = st->len - 5;
@@ -725,6 +719,8 @@ static int ssh1_rdpkt(unsigned char **data, int *datalen)
        memcpy(buf + nowlen, pktin.body + 4, msglen);
        buf[nowlen + msglen] = '\0';
        logevent(buf);
+       bombout(("Server sent disconnect message:\n\"%s\"", buf+nowlen));
+       crReturn(0);
     }
 
     crFinish(0);
@@ -768,11 +764,7 @@ static int ssh2_rdpkt(unsigned char **data, int *datalen)
        pktin.data[st->i] = *(*data)++;
        (*datalen)--;
     }
-#ifdef FWHACK
-    if (!memcmp(pktin.data, "Remo", 4)) {      /* "Remo"te server has closed ... */
-       /* FIXME */
-    }
-#endif
+
     if (sccipher)
        sccipher->decrypt(pktin.data, st->cipherblk);
 
@@ -898,6 +890,12 @@ static int ssh2_rdpkt(unsigned char **data, int *datalen)
        memcpy(buf + nowlen, pktin.data + 14, msglen);
        buf[nowlen + msglen] = '\0';
        logevent(buf);
+       bombout(("Server sent disconnect message\ntype %d (%s):\n\"%s\"",
+                reason,
+                (reason > 0 && reason < lenof(ssh2_disconnect_reasons)) ?
+                ssh2_disconnect_reasons[reason] : "unknown",
+                buf+nowlen));
+       crReturn(0);
     }
 
     crFinish(0);
@@ -1594,6 +1592,9 @@ static void ssh_gotdata(unsigned char *data, int datalen)
     while (1) {
        while (datalen > 0) {
            if (s_rdpkt(&data, &datalen) == 0) {
+               if (ssh_state == SSH_STATE_CLOSED) {
+                   return;
+               }
                ssh_protocol(NULL, 0, 1);
                if (ssh_state == SSH_STATE_CLOSED) {
                    return;
@@ -1686,6 +1687,11 @@ static char *connect_to_host(char *host, int port, char **realhost)
     /*
      * Try to find host.
      */
+    {
+       char buf[200];
+       sprintf(buf, "Looking up host \"%.170s\"", host);
+       logevent(buf);
+    }
     addr = sk_namelookup(host, realhost);
     if ((err = sk_addr_error(addr)))
        return err;
@@ -1697,6 +1703,12 @@ static char *connect_to_host(char *host, int port, char **realhost)
     /*
      * Open socket.
      */
+    {
+       char buf[200], addrbuf[100];
+       sk_getaddr(addr, addrbuf, 100);
+       sprintf(buf, "Connecting to %.100s port %d", addrbuf, port);
+       logevent(buf);
+    }
     s = sk_new(addr, port, 0, 1, &fn_table_ptr);
     if ((err = sk_socket_error(s)))
        return err;
@@ -1777,6 +1789,7 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
     struct MD5Context md5c;
     static unsigned long supported_ciphers_mask, supported_auths_mask;
     static int tried_publickey;
+    static int tis_auth_refused, ccard_auth_refused;
     static unsigned char session_id[16];
     static int cipher_type;
     static char username[100];
@@ -2025,6 +2038,7 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
     crWaitUntil(ispkt);
 
     tried_publickey = 0;
+    tis_auth_refused = ccard_auth_refused = 0;
 
     while (pktin.type == SSH1_SMSG_FAILURE) {
        static char password[100];
@@ -2032,12 +2046,8 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
        static int pos;
        static char c;
        static int pwpkt_type;
-       /*
-        * Show password prompt, having first obtained it via a TIS
-        * or CryptoCard exchange if we're doing TIS or CryptoCard
-        * authentication.
-        */
        pwpkt_type = SSH1_CMSG_AUTH_PASSWORD;
+
        if (agent_exists()) {
            /*
             * Attempt RSA authentication using Pageant.
@@ -2160,9 +2170,9 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
        if (*cfg.keyfile && !tried_publickey)
            pwpkt_type = SSH1_CMSG_AUTH_RSA;
 
-       if (pktin.type == SSH1_SMSG_FAILURE &&
-           cfg.try_tis_auth &&
-           (supported_auths_mask & (1 << SSH1_AUTH_TIS))) {
+       if (cfg.try_tis_auth &&
+           (supported_auths_mask & (1 << SSH1_AUTH_TIS)) &&
+           !tis_auth_refused) {
            pwpkt_type = SSH1_CMSG_AUTH_TIS_RESPONSE;
            logevent("Requested TIS authentication");
            send_packet(SSH1_CMSG_AUTH_TIS, PKT_END);
@@ -2171,6 +2181,8 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
                logevent("TIS authentication declined");
                if (flags & FLAG_INTERACTIVE)
                    c_write_str("TIS authentication refused.\r\n");
+               tis_auth_refused = 1;
+               continue;
            } else {
                int challengelen = ((pktin.body[0] << 24) |
                                    (pktin.body[1] << 16) |
@@ -2180,12 +2192,17 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
                if (challengelen > sizeof(prompt) - 1)
                    challengelen = sizeof(prompt) - 1;  /* prevent overrun */
                memcpy(prompt, pktin.body + 4, challengelen);
-               prompt[challengelen] = '\0';
+               /* Prompt heuristic comes from OpenSSH */
+               strncpy(prompt + challengelen,
+                       memchr(prompt, '\n', challengelen) ?
+                       "": "\r\nResponse: ",
+                       (sizeof prompt) - challengelen);
+               prompt[(sizeof prompt) - 1] = '\0';
            }
        }
-       if (pktin.type == SSH1_SMSG_FAILURE &&
-           cfg.try_tis_auth &&
-           (supported_auths_mask & (1 << SSH1_AUTH_CCARD))) {
+       if (cfg.try_tis_auth &&
+           (supported_auths_mask & (1 << SSH1_AUTH_CCARD)) &&
+           !ccard_auth_refused) {
            pwpkt_type = SSH1_CMSG_AUTH_CCARD_RESPONSE;
            logevent("Requested CryptoCard authentication");
            send_packet(SSH1_CMSG_AUTH_CCARD, PKT_END);
@@ -2193,6 +2210,8 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
            if (pktin.type != SSH1_SMSG_AUTH_CCARD_CHALLENGE) {
                logevent("CryptoCard authentication declined");
                c_write_str("CryptoCard authentication refused.\r\n");
+               ccard_auth_refused = 1;
+               continue;
            } else {
                int challengelen = ((pktin.body[0] << 24) |
                                    (pktin.body[1] << 16) |
@@ -2202,7 +2221,9 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
                if (challengelen > sizeof(prompt) - 1)
                    challengelen = sizeof(prompt) - 1;  /* prevent overrun */
                memcpy(prompt, pktin.body + 4, challengelen);
-               strncpy(prompt + challengelen, "\r\nResponse : ",
+               strncpy(prompt + challengelen,
+                       memchr(prompt, '\n', challengelen) ?
+                       "" : "\r\nResponse: ",
                        sizeof(prompt) - challengelen);
                prompt[sizeof(prompt) - 1] = '\0';
            }
@@ -2224,6 +2245,11 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
            sfree(comment);
        }
 
+       /*
+        * Show password prompt, having first obtained it via a TIS
+        * or CryptoCard exchange if we're doing TIS or CryptoCard
+        * authentication.
+        */
        if (ssh_get_line) {
            if (!ssh_get_line(prompt, password, sizeof(password), TRUE)) {
                /*
@@ -2239,7 +2265,9 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
                crReturn(1);
            }
        } else {
-           c_write_str(prompt);
+           /* Prompt may have come from server. We've munged it a bit, so
+            * we know it to be zero-terminated at least once. */
+           c_write_untrusted(prompt, strlen(prompt));
            pos = 0;
            ssh_send_ok = 1;
            while (pos >= 0) {
@@ -2288,7 +2316,7 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
            tried_publickey = 1;
            i = loadrsakey(cfg.keyfile, &pubkey, password);
            if (i == 0) {
-               c_write_str("Couldn't load public key from ");
+               c_write_str("Couldn't load private key from ");
                c_write_str(cfg.keyfile);
                c_write_str(".\r\n");
                continue;              /* go and try password */
@@ -2697,7 +2725,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) {
@@ -2709,7 +2737,7 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt)
                int bufsize =
                    from_backend(pktin.type == SSH1_SMSG_STDERR_DATA,
                                 pktin.body + 4, len);
-               if (bufsize > SSH1_BUFFER_LIMIT) {
+               if (!ssh1_stdout_throttling && bufsize > SSH1_BUFFER_LIMIT) {
                    ssh1_stdout_throttling = 1;
                    ssh1_throttle(+1);
                }
@@ -2955,7 +2983,7 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt)
                        bufsize = 0;   /* agent channels never back up */
                        break;
                    }
-                   if (bufsize > SSH1_BUFFER_LIMIT) {
+                   if (!c->v.v1.throttling && bufsize > SSH1_BUFFER_LIMIT) {
                        c->v.v1.throttling = 1;
                        ssh1_throttle(+1);
                    }
@@ -2967,6 +2995,15 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt)
                 * if no pty is available or in other odd cases. Ignore */
            } else if (pktin.type == SSH1_SMSG_EXIT_STATUS) {
                send_packet(SSH1_CMSG_EXIT_CONFIRMATION, PKT_END);
+                /*
+                 * In case `helpful' firewalls or proxies tack
+                 * extra human-readable text on the end of the
+                 * session which we might mistake for another
+                 * encrypted packet, we close the session once
+                 * we've sent EXIT_CONFIRMATION.
+                 */
+                ssh_state = SSH_STATE_CLOSED;
+                crReturnV;
            } else {
                bombout(("Strange packet received: type %d", pktin.type));
                crReturnV;
@@ -3806,66 +3843,12 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                    in_commasep_string("publickey", methods, methlen);
                can_passwd =
                    in_commasep_string("password", methods, methlen);
-               can_passwd =
-                   in_commasep_string("password", methods, methlen);
                can_keyb_inter = 
                    in_commasep_string("keyboard-interactive", methods, methlen);
            }
 
            method = 0;
 
-           if (!method && can_keyb_inter && !tried_keyb_inter) {
-               method = AUTH_KEYBOARD_INTERACTIVE;
-               type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
-               tried_keyb_inter = TRUE;
-
-               ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
-               ssh2_pkt_addstring(username);
-               ssh2_pkt_addstring("ssh-connection");   /* service requested */
-               ssh2_pkt_addstring("keyboard-interactive");     /* method */
-               ssh2_pkt_addstring(""); /* lang */
-               ssh2_pkt_addstring("");
-               ssh2_pkt_send();
-
-               crWaitUntilV(ispkt);
-               if (pktin.type != SSH2_MSG_USERAUTH_INFO_REQUEST) {
-                   if (pktin.type == SSH2_MSG_USERAUTH_FAILURE)
-                       gotit = TRUE;
-                   logevent("Keyboard-interactive authentication refused");
-                   type = AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET;
-                   continue;
-               }
-
-               kbd_inter_running = TRUE;
-           }
-
-           if (kbd_inter_running) {
-               method = AUTH_KEYBOARD_INTERACTIVE;
-               type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
-               tried_keyb_inter = TRUE;
-
-               /* We've got packet with that "interactive" info
-                  dump banners, and set its prompt as ours */
-               {
-                   char *name, *inst, *lang, *prompt;
-                   int name_len, inst_len, lang_len, prompt_len;
-                   ssh2_pkt_getstring(&name, &name_len);
-                   ssh2_pkt_getstring(&inst, &inst_len);
-                   ssh2_pkt_getstring(&lang, &lang_len);
-                   if (name_len > 0)
-                       c_write_untrusted(name, name_len);
-                   if (inst_len > 0)
-                       c_write_untrusted(inst, inst_len);
-                   num_prompts = ssh2_pkt_getuint32();
-
-                   ssh2_pkt_getstring(&prompt, &prompt_len);
-                   strncpy(pwprompt, prompt, sizeof(pwprompt));
-                   need_pw = TRUE;
-
-                   echo = ssh2_pkt_getbool();
-               }
-           }
-
            if (!method && can_pubkey && agent_exists() && !tried_agent) {
                /*
                 * Attempt public-key authentication using Pageant.
@@ -4059,6 +4042,58 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                }
            }
 
+           if (!method && can_keyb_inter && !tried_keyb_inter) {
+               method = AUTH_KEYBOARD_INTERACTIVE;
+               type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
+               tried_keyb_inter = TRUE;
+
+               ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
+               ssh2_pkt_addstring(username);
+               ssh2_pkt_addstring("ssh-connection");   /* service requested */
+               ssh2_pkt_addstring("keyboard-interactive");     /* method */
+               ssh2_pkt_addstring(""); /* lang */
+               ssh2_pkt_addstring("");
+               ssh2_pkt_send();
+
+               crWaitUntilV(ispkt);
+               if (pktin.type != SSH2_MSG_USERAUTH_INFO_REQUEST) {
+                   if (pktin.type == SSH2_MSG_USERAUTH_FAILURE)
+                       gotit = TRUE;
+                   logevent("Keyboard-interactive authentication refused");
+                   type = AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET;
+                   continue;
+               }
+
+               kbd_inter_running = TRUE;
+           }
+
+           if (kbd_inter_running) {
+               method = AUTH_KEYBOARD_INTERACTIVE;
+               type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
+               tried_keyb_inter = TRUE;
+
+               /* We've got packet with that "interactive" info
+                  dump banners, and set its prompt as ours */
+               {
+                   char *name, *inst, *lang, *prompt;
+                   int name_len, inst_len, lang_len, prompt_len;
+                   ssh2_pkt_getstring(&name, &name_len);
+                   ssh2_pkt_getstring(&inst, &inst_len);
+                   ssh2_pkt_getstring(&lang, &lang_len);
+                   if (name_len > 0)
+                       c_write_untrusted(name, name_len);
+                   if (inst_len > 0)
+                       c_write_untrusted(inst, inst_len);
+                   num_prompts = ssh2_pkt_getuint32();
+
+                   ssh2_pkt_getstring(&prompt, &prompt_len);
+                   strncpy(pwprompt, prompt, sizeof(pwprompt));
+                   need_pw = TRUE;
+
+                   echo = ssh2_pkt_getbool();
+               }
+           }
+
            if (!method && can_passwd) {
                method = AUTH_PASSWORD;
                sprintf(pwprompt, "%.90s@%.90s's password: ", username,
@@ -4344,7 +4379,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");
@@ -4432,7 +4468,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");
@@ -4470,8 +4508,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");
@@ -4513,7 +4551,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");
@@ -4570,7 +4609,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;
            }
            /*
@@ -4601,7 +4641,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;
@@ -5115,7 +5155,7 @@ void *new_sock_channel(Socket s)
 void ssh_unthrottle(int bufsize)
 {
     if (ssh_version == 1) {
-       if (bufsize < SSH1_BUFFER_LIMIT) {
+       if (ssh1_stdout_throttling && bufsize < SSH1_BUFFER_LIMIT) {
            ssh1_stdout_throttling = 0;
            ssh1_throttle(-1);
        }