Reduce the number of round-trips involved in opening an SSH-2 session
[sgt/putty] / ssh.c
diff --git a/ssh.c b/ssh.c
index ef503bf..f39894a 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -196,6 +196,7 @@ static const char *const ssh2_disconnect_reasons[] = {
 #define BUG_SSH2_PK_SESSIONID                   128
 #define BUG_SSH2_MAXPKT                                256
 #define BUG_CHOKES_ON_SSH2_IGNORE               512
+#define BUG_CHOKES_ON_WINADJ                   1024
 
 /*
  * Codes for terminal modes.
@@ -2580,6 +2581,15 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
        ssh->remote_bugs |= BUG_CHOKES_ON_SSH2_IGNORE;
        logevent("We believe remote version has SSH-2 ignore bug");
     }
+
+    if (conf_get_int(ssh->conf, CONF_sshbug_winadj) == FORCE_ON) {
+       /*
+        * Servers that don't support our winadj request for one
+        * reason or another. Currently, none detected automatically.
+        */
+       ssh->remote_bugs |= BUG_CHOKES_ON_WINADJ;
+       logevent("We believe remote version has winadj bug");
+    }
 }
 
 /*
@@ -3849,7 +3859,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
                ret = loadrsakey(s->keyfile, &s->key, passphrase,
                                 &error);
                if (passphrase) {
-                   memset(passphrase, 0, strlen(passphrase));
+                   smemclr(passphrase, strlen(passphrase));
                    sfree(passphrase);
                }
                if (ret == 1) {
@@ -6294,7 +6304,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
        assert(ssh->csmac->len <=
               ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);
        ssh->csmac->setkey(ssh->cs_mac_ctx, keyspace);
-       memset(keyspace, 0, sizeof(keyspace));
+       smemclr(keyspace, sizeof(keyspace));
     }
 
     logeventf(ssh, "Initialised %.200s client->server encryption",
@@ -6360,7 +6370,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
        assert(ssh->scmac->len <=
               ssh->kex->hash->hlen * SSH2_MKKEY_ITERS);
        ssh->scmac->setkey(ssh->sc_mac_ctx, keyspace);
-       memset(keyspace, 0, sizeof(keyspace));
+       smemclr(keyspace, sizeof(keyspace));
     }
     logeventf(ssh, "Initialised %.200s server->client encryption",
              ssh->sccipher->text_name);
@@ -6635,7 +6645,8 @@ static void ssh2_set_window(struct ssh_channel *c, int newwin)
         * unexpected CHANNEL_FAILUREs.
         */
        if (newwin == c->v.v2.locmaxwin &&
-           ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE]) {
+           ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] &&
+            !(ssh->remote_bugs & BUG_CHOKES_ON_WINADJ)) {
            pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
            ssh2_pkt_adduint32(pktout, c->remoteid);
            ssh2_pkt_addstring(pktout, "winadj@putty.projects.tartarus.org");
@@ -7501,6 +7512,9 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
        int siglen, retlen, len;
        char *q, *agentreq, *ret;
        int try_send;
+       int requested_x11;
+       int requested_agent;
+       int requested_tty;
        int num_env, env_left, env_ok;
        struct Packet *pktout;
        Filename *keyfile;
@@ -8151,7 +8165,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                    key = ssh2_load_userkey(s->keyfile, passphrase, &error);
                    if (passphrase) {
                        /* burn the evidence */
-                       memset(passphrase, 0, strlen(passphrase));
+                       smemclr(passphrase, strlen(passphrase));
                        sfree(passphrase);
                    }
                    if (key == SSH2_WRONG_PASSPHRASE || key == NULL) {
@@ -8730,7 +8744,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                             */
                            /* burn the evidence */
                            free_prompts(s->cur_prompt);
-                           memset(s->password, 0, strlen(s->password));
+                           smemclr(s->password, strlen(s->password));
                            sfree(s->password);
                            ssh_disconnect(ssh, NULL, "Unable to authenticate",
                                           SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER,
@@ -8746,7 +8760,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                         * re-enter it if they louse up the new password.)
                         */
                        if (s->cur_prompt->prompts[0]->result[0]) {
-                           memset(s->password, 0, strlen(s->password));
+                           smemclr(s->password, strlen(s->password));
                                /* burn the evidence */
                            sfree(s->password);
                            s->password =
@@ -8813,7 +8827,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                 * We don't need the old password any more, in any
                 * case. Burn the evidence.
                 */
-               memset(s->password, 0, strlen(s->password));
+               smemclr(s->password, strlen(s->password));
                sfree(s->password);
 
            } else {
@@ -8988,6 +9002,17 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
     }
 
     /*
+     * Enable port forwardings.
+     */
+    ssh_setup_portfwd(ssh, ssh->conf);
+
+    /*
+     * Send the CHANNEL_REQUESTS for the main channel.  We send them all
+     * and then start looking for responses, so it's important that the
+     * sending and receiving code below it is kept in sync.
+     */
+
+    /*
      * Potentially enable X11 forwarding.
      */
     if (ssh->mainchan && !ssh->ncmode && conf_get_int(ssh->conf, CONF_x11_forward) &&
@@ -9012,26 +9037,9 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
        end_log_omission(ssh, s->pktout);
        ssh2_pkt_adduint32(s->pktout, ssh->x11disp->screennum);
        ssh2_pkt_send(ssh, s->pktout);
-
-       crWaitUntilV(pktin);
-
-       if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) {
-           if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) {
-               bombout(("Unexpected response to X11 forwarding request:"
-                        " packet type %d", pktin->type));
-               crStopV;
-           }
-           logevent("X11 forwarding refused");
-       } else {
-           logevent("X11 forwarding enabled");
-           ssh->X11_fwd_enabled = TRUE;
-       }
-    }
-
-    /*
-     * Enable port forwardings.
-     */
-    ssh_setup_portfwd(ssh, ssh->conf);
+       s->requested_x11 = TRUE;
+    } else
+       s->requested_x11 = FALSE;
 
     /*
      * Potentially enable agent forwarding.
@@ -9043,21 +9051,9 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
        ssh2_pkt_addstring(s->pktout, "auth-agent-req@openssh.com");
        ssh2_pkt_addbool(s->pktout, 1);        /* want reply */
        ssh2_pkt_send(ssh, s->pktout);
-
-       crWaitUntilV(pktin);
-
-       if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) {
-           if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) {
-               bombout(("Unexpected response to agent forwarding request:"
-                        " packet type %d", pktin->type));
-               crStopV;
-           }
-           logevent("Agent forwarding refused");
-       } else {
-           logevent("Agent forwarding enabled");
-           ssh->agentfwd_enabled = TRUE;
-       }
-    }
+       s->requested_agent = TRUE;
+    } else
+       s->requested_agent = FALSE;
 
     /*
      * Now allocate a pty for the session.
@@ -9086,25 +9082,9 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
        ssh2_pkt_addstring_data(s->pktout, "\0", 1); /* TTY_OP_END */
        ssh2_pkt_send(ssh, s->pktout);
        ssh->state = SSH_STATE_INTERMED;
-
-       crWaitUntilV(pktin);
-
-       if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) {
-           if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) {
-               bombout(("Unexpected response to pty request:"
-                        " packet type %d", pktin->type));
-               crStopV;
-           }
-           c_write_str(ssh, "Server refused to allocate pty\r\n");
-           ssh->editing = ssh->echoing = 1;
-       } else {
-           logeventf(ssh, "Allocated pty (ospeed %dbps, ispeed %dbps)",
-                     ssh->ospeed, ssh->ispeed);
-            ssh->got_pty = TRUE;
-       }
-    } else {
-       ssh->editing = ssh->echoing = 1;
-    }
+       s->requested_tty = TRUE;
+    } else
+       s->requested_tty = FALSE;
 
     /*
      * Send environment variables.
@@ -9112,11 +9092,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
      * Simplest thing here is to send all the requests at once, and
      * then wait for a whole bunch of successes or failures.
      */
+    s->num_env = 0;
     if (ssh->mainchan && !ssh->ncmode) {
        char *key, *val;
 
-       s->num_env = 0;
-
        for (val = conf_get_str_strs(ssh->conf, CONF_environmt, NULL, &key);
             val != NULL;
             val = conf_get_str_strs(ssh->conf, CONF_environmt, key, &key)) {
@@ -9130,39 +9109,96 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
 
            s->num_env++;
        }
-
-       if (s->num_env) {
+       if (s->num_env)
            logeventf(ssh, "Sent %d environment variables", s->num_env);
+    }
 
-           s->env_ok = 0;
-           s->env_left = s->num_env;
+    /*
+     * All CHANNEL_REQUESTs sent.  Now collect up the replies.  These
+     * must be in precisely the same order as the requests.
+     */
 
-           while (s->env_left > 0) {
-               crWaitUntilV(pktin);
+    if (s->requested_x11) {
+       crWaitUntilV(pktin);
 
-               if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) {
-                   if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) {
-                       bombout(("Unexpected response to environment request:"
-                                " packet type %d", pktin->type));
-                       crStopV;
-                   }
-               } else {
-                   s->env_ok++;
-               }
+       if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) {
+           if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) {
+               bombout(("Unexpected response to X11 forwarding request:"
+                        " packet type %d", pktin->type));
+               crStopV;
+           }
+           logevent("X11 forwarding refused");
+       } else {
+           logevent("X11 forwarding enabled");
+           ssh->X11_fwd_enabled = TRUE;
+       }
+    }
+
+    if (s->requested_agent) {
+       crWaitUntilV(pktin);
+
+       if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) {
+           if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) {
+               bombout(("Unexpected response to agent forwarding request:"
+                        " packet type %d", pktin->type));
+               crStopV;
+           }
+           logevent("Agent forwarding refused");
+       } else {
+           logevent("Agent forwarding enabled");
+           ssh->agentfwd_enabled = TRUE;
+       }
+    }
 
-               s->env_left--;
+    if (s->requested_tty) {
+       crWaitUntilV(pktin);
+
+       if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) {
+           if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) {
+               bombout(("Unexpected response to pty request:"
+                        " packet type %d", pktin->type));
+               crStopV;
            }
+           c_write_str(ssh, "Server refused to allocate pty\r\n");
+           ssh->editing = ssh->echoing = 1;
+       } else {
+           logeventf(ssh, "Allocated pty (ospeed %dbps, ispeed %dbps)",
+                     ssh->ospeed, ssh->ispeed);
+            ssh->got_pty = TRUE;
+       }
+    } else {
+       ssh->editing = ssh->echoing = 1;
+    }
 
-           if (s->env_ok == s->num_env) {
-               logevent("All environment variables successfully set");
-           } else if (s->env_ok == 0) {
-               logevent("All environment variables refused");
-               c_write_str(ssh, "Server refused to set environment variables\r\n");
+    if (s->num_env) {
+       s->env_ok = 0;
+       s->env_left = s->num_env;
+
+       while (s->env_left > 0) {
+           crWaitUntilV(pktin);
+
+           if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) {
+               if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) {
+                   bombout(("Unexpected response to environment request:"
+                            " packet type %d", pktin->type));
+                   crStopV;
+               }
            } else {
-               logeventf(ssh, "%d environment variables refused",
-                         s->num_env - s->env_ok);
-               c_write_str(ssh, "Server refused to set all environment variables\r\n");
+               s->env_ok++;
            }
+
+           s->env_left--;
+       }
+
+       if (s->env_ok == s->num_env) {
+           logevent("All environment variables successfully set");
+       } else if (s->env_ok == 0) {
+           logevent("All environment variables refused");
+           c_write_str(ssh, "Server refused to set environment variables\r\n");
+       } else {
+           logeventf(ssh, "%d environment variables refused",
+                     s->num_env - s->env_ok);
+           c_write_str(ssh, "Server refused to set all environment variables\r\n");
        }
     }