At long last: PuTTY will now report its version to the server
[u/mdw/putty] / ssh.c
diff --git a/ssh.c b/ssh.c
index ea1fe02..1077b33 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -179,9 +179,7 @@ const static struct ssh2_ciphers *ciphers[] = {
 };
 
 const static struct ssh_kex *kex_algs[] = {
-#ifdef DO_DIFFIE_HELLMAN_GEX
     &ssh_diffiehellman_gex,
-#endif
     &ssh_diffiehellman };
 
 const static struct ssh_signkey *hostkey_algs[] = { &ssh_rsa, &ssh_dss };
@@ -271,7 +269,8 @@ static const struct ssh_compress *sccomp = NULL;
 static const struct ssh_kex *kex = NULL;
 static const struct ssh_signkey *hostkey = NULL;
 static unsigned char ssh2_session_id[20];
-int (*ssh_get_password)(const char *prompt, char *str, int maxlen) = NULL;
+int (*ssh_get_line)(const char *prompt, char *str, int maxlen,
+                    int is_pw) = NULL;
 
 static char *savedhost;
 static int savedport;
@@ -1194,7 +1193,8 @@ static int do_ssh_init(unsigned char c) {
         /*
          * This is a v2 server. Begin v2 protocol.
          */
-        char *verstring = "SSH-2.0-PuTTY";
+        char verstring[80];
+        sprintf(verstring, "SSH-2.0-%s", sshver);
         SHA_Init(&exhashbase);
         /*
          * Hash our version string and their version string.
@@ -1213,8 +1213,9 @@ static int do_ssh_init(unsigned char c) {
         /*
          * This is a v1 server. Begin v1 protocol.
          */
-        sprintf(vstring, "SSH-%s-PuTTY\n",
-                (ssh_versioncmp(version, "1.5") <= 0 ? version : "1.5"));
+        sprintf(vstring, "SSH-%s-%s\n",
+                (ssh_versioncmp(version, "1.5") <= 0 ? version : "1.5"),
+                sshver);
         sprintf(vlog, "We claim version: %s", vstring);
         vlog[strcspn(vlog, "\r\n")] = '\0';
         logevent(vlog);
@@ -1271,21 +1272,20 @@ static void ssh_gotdata(unsigned char *data, int datalen)
     crFinishV;
 }
 
-static int ssh_receive(Socket skt, int urgent, char *data, int len) {
-    if (urgent==3) {
+static int ssh_closing (Plug plug, char *error_msg, int error_code, int calling_back) {
+    ssh_state = SSH_STATE_CLOSED;
+    sk_close(s);
+    s = NULL;
+    if (error_msg) {
         /* A socket error has occurred. */
-        ssh_state = SSH_STATE_CLOSED;
-        sk_close(s);
-        s = NULL;
-        connection_fatal(data);
-        return 0;
-    } else if (!len) {
-       /* Connection has closed. */
-       ssh_state = SSH_STATE_CLOSED;
-       sk_close(s);
-       s = NULL;
-       return 0;
+        connection_fatal (error_msg);
+    } else {
+       /* Otherwise, the remote side closed the connection normally. */
     }
+    return 0;
+}
+
+static int ssh_receive(Plug plug, int urgent, char *data, int len) {
     ssh_gotdata (data, len);
     if (ssh_state == SSH_STATE_CLOSED) {
         if (s) {
@@ -1304,6 +1304,11 @@ static int ssh_receive(Socket skt, int urgent, char *data, int len) {
  */
 static char *connect_to_host(char *host, int port, char **realhost)
 {
+    static struct plug_function_table fn_table = {
+       ssh_closing,
+       ssh_receive
+    }, *fn_table_ptr = &fn_table;
+
     SockAddr addr;
     char *err;
 #ifdef FWHACK
@@ -1341,7 +1346,7 @@ static char *connect_to_host(char *host, int port, char **realhost)
     /*
      * Open socket.
      */
-    s = sk_new(addr, port, 0, 1, ssh_receive);
+    s = sk_new(addr, port, 0, 1, &fn_table_ptr);
     if ( (err = sk_socket_error(s)) )
        return err;
 
@@ -1505,43 +1510,56 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
        static int pos = 0;
        static char c;
        if ((flags & FLAG_INTERACTIVE) && !*cfg.username) {
-           c_write_str("login as: ");
-            ssh_send_ok = 1;
-           while (pos >= 0) {
-               crWaitUntil(!ispkt);
-               while (inlen--) switch (c = *in++) {
-                 case 10: case 13:
-                   username[pos] = 0;
-                   pos = -1;
-                   break;
-                 case 8: case 127:
-                   if (pos > 0) {
-                       c_write_str("\b \b");
-                       pos--;
-                   }
-                   break;
-                 case 21: case 27:
-                   while (pos > 0) {
-                       c_write_str("\b \b");
-                       pos--;
-                   }
-                   break;
-                 case 3: case 4:
-                   random_save_seed();
-                   exit(0);
-                   break;
-                 default:
-                   if (((c >= ' ' && c <= '~') ||
-                         ((unsigned char)c >= 160)) && pos < 40) {
-                       username[pos++] = c;
-                       c_write(&c, 1);
-                   }
-                   break;
-               }
-           }
-           c_write_str("\r\n");
-           username[strcspn(username, "\n\r")] = '\0';
-       } else {
+            if (ssh_get_line) {
+                if (!ssh_get_line("login as: ",
+                                  username, sizeof(username), FALSE)) {
+                    /*
+                     * get_line failed to get a username.
+                     * Terminate.
+                     */
+                    logevent("No username provided. Abandoning session.");
+                    ssh_state = SSH_STATE_CLOSED;
+                    crReturn(1);
+                }
+            } else {
+                c_write_str("login as: ");
+                ssh_send_ok = 1;
+                while (pos >= 0) {
+                    crWaitUntil(!ispkt);
+                    while (inlen--) switch (c = *in++) {
+                      case 10: case 13:
+                        username[pos] = 0;
+                        pos = -1;
+                        break;
+                      case 8: case 127:
+                        if (pos > 0) {
+                            c_write_str("\b \b");
+                            pos--;
+                        }
+                        break;
+                      case 21: case 27:
+                        while (pos > 0) {
+                            c_write_str("\b \b");
+                            pos--;
+                        }
+                        break;
+                      case 3: case 4:
+                        random_save_seed();
+                        exit(0);
+                        break;
+                      default:
+                        if (((c >= ' ' && c <= '~') ||
+                             ((unsigned char)c >= 160)) && pos < 40) {
+                            username[pos++] = c;
+                            c_write(&c, 1);
+                        }
+                        break;
+                    }
+                }
+                c_write_str("\r\n");
+                username[strcspn(username, "\n\r")] = '\0';
+            }
+        } else {
            strncpy(username, cfg.username, 99);
            username[99] = '\0';
        }
@@ -1592,7 +1610,8 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
             request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES;
             agent_query(request, 5, &r, &responselen);
             response = (unsigned char *)r;
-            if (response) {
+            if (response && responselen >= 5 &&
+                response[4] == SSH1_AGENT_RSA_IDENTITIES_ANSWER) {
                 p = response + 5;
                 nkeys = GET_32BIT(p); p += 4;
                 { char buf[64]; sprintf(buf, "Pageant has %d SSH1 keys", nkeys);
@@ -1743,13 +1762,12 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
             sfree(comment);
         }
 
-       if (ssh_get_password) {
-           if (!ssh_get_password(prompt, password, sizeof(password))) {
+       if (ssh_get_line) {
+           if (!ssh_get_line(prompt, password, sizeof(password), TRUE)) {
                 /*
-                 * get_password failed to get a password (for
-                 * example because one was supplied on the command
-                 * line which has already failed to work).
-                 * Terminate.
+                 * get_line failed to get a password (for example
+                 * because one was supplied on the command line
+                 * which has already failed to work). Terminate.
                  */
                 logevent("No more passwords to try");
                 ssh_state = SSH_STATE_CLOSED;
@@ -1913,7 +1931,7 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
 
                     pwlen = strlen(password);
                     if (pwlen < 16) {
-                        bottom = 1;
+                        bottom = 0;    /* zero length passwords are OK! :-) */
                         top = 15;
                     } else {
                         bottom = pwlen &~ 7;
@@ -1967,7 +1985,7 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
 }
 
 void sshfwd_close(struct ssh_channel *c) {
-    if (c) {
+    if (c && !c->closes) {
         if (ssh_version == 1) {
             send_packet(SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid, PKT_END);
         } else {
@@ -2331,7 +2349,7 @@ static void ssh2_mkkey(Bignum K, char *H, char *sessid, char chr, char *keyspace
  */
 static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
 {
-    static int i, j, len, nbits;
+    static int i, j, len, nbits, pbits;
     static char *str;
     static Bignum p, g, e, f, K;
     static int kex_init_value, kex_reply_value;
@@ -2554,30 +2572,34 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
     }
 
     /*
+     * Work out the number of bits of key we will need from the key
+     * exchange. We start with the maximum key length of either
+     * cipher...
+     */
+    {
+        int csbits, scbits;
+
+       csbits = cscipher_tobe->keylen;
+       scbits = sccipher_tobe->keylen;
+       nbits = (csbits > scbits ? csbits : scbits);
+    }
+    /* The keys only have 160-bit entropy, since they're based on
+     * a SHA-1 hash. So cap the key size at 160 bits. */
+    if (nbits > 160) nbits = 160;
+
+    /*
      * If we're doing Diffie-Hellman group exchange, start by
      * requesting a group.
      */
     if (kex == &ssh_diffiehellman_gex) {
-        int csbits, scbits;
-
         logevent("Doing Diffie-Hellman group exchange");
         /*
-         * Work out number of bits. We start with the maximum key
-         * length of either cipher...
-         */
-        csbits = cscipher_tobe->keylen;
-        scbits = sccipher_tobe->keylen;
-        nbits = (csbits > scbits ? csbits : scbits);
-        /* The keys only have 160-bit entropy, since they're based on
-         * a SHA-1 hash. So cap the key size at 160 bits. */
-        if (nbits > 160) nbits = 160;
-        /*
-         * ... and then work out how big a DH group we will need to
-         * allow that much data.
-         */
-        nbits = 512 << ((nbits-1) / 64);
+         * Work out how big a DH group we will need to allow that
+         * much data.
+        */
+        pbits = 512 << ((nbits-1) / 64);
         ssh2_pkt_init(SSH2_MSG_KEX_DH_GEX_REQUEST);
-        ssh2_pkt_adduint32(nbits);
+        ssh2_pkt_adduint32(pbits);
         ssh2_pkt_send();
 
         crWaitUntil(ispkt);
@@ -2600,7 +2622,7 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
     /*
      * Now generate and send e for Diffie-Hellman.
      */
-    e = dh_create_e();
+    e = dh_create_e(nbits*2);
     ssh2_pkt_init(kex_init_value);
     ssh2_pkt_addmp(e);
     ssh2_pkt_send();
@@ -2618,7 +2640,7 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
 
     sha_string(&exhash, hostkeydata, hostkeylen);
     if (kex == &ssh_diffiehellman_gex) {
-        sha_uint32(&exhash, nbits);
+        sha_uint32(&exhash, pbits);
         sha_mpint(&exhash, p);
         sha_mpint(&exhash, g);
     }
@@ -2841,40 +2863,53 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
         */
        pos = 0;
        if ((flags & FLAG_INTERACTIVE) && !*cfg.username) {
-           c_write_str("login as: ");
-           ssh_send_ok = 1;
-           while (pos >= 0) {
-               crWaitUntilV(!ispkt);
-               while (inlen--) switch (c = *in++) {
-                 case 10: case 13:
-                   username[pos] = 0;
-                   pos = -1;
-                   break;
-                 case 8: case 127:
-                   if (pos > 0) {
-                       c_write_str("\b \b");
-                       pos--;
-                   }
-                   break;
-                 case 21: case 27:
-                   while (pos > 0) {
-                       c_write_str("\b \b");
-                       pos--;
-                   }
-                   break;
-                 case 3: case 4:
-                   random_save_seed();
-                   exit(0);
-                   break;
-                 default:
-                   if (((c >= ' ' && c <= '~') ||
-                        ((unsigned char)c >= 160)) && pos < 40) {
-                       username[pos++] = c;
-                       c_write(&c, 1);
-                   }
-                   break;
-               }
-           }
+            if (ssh_get_line) {
+                if (!ssh_get_line("login as: ",
+                                  username, sizeof(username), FALSE)) {
+                    /*
+                     * get_line failed to get a username.
+                     * Terminate.
+                     */
+                    logevent("No username provided. Abandoning session.");
+                    ssh_state = SSH_STATE_CLOSED;
+                    crReturnV;
+                }
+            } else {
+                c_write_str("login as: ");
+                ssh_send_ok = 1;
+                while (pos >= 0) {
+                    crWaitUntilV(!ispkt);
+                    while (inlen--) switch (c = *in++) {
+                      case 10: case 13:
+                        username[pos] = 0;
+                        pos = -1;
+                        break;
+                      case 8: case 127:
+                        if (pos > 0) {
+                            c_write_str("\b \b");
+                            pos--;
+                        }
+                        break;
+                      case 21: case 27:
+                        while (pos > 0) {
+                            c_write_str("\b \b");
+                            pos--;
+                        }
+                        break;
+                      case 3: case 4:
+                        random_save_seed();
+                        exit(0);
+                        break;
+                      default:
+                        if (((c >= ' ' && c <= '~') ||
+                             ((unsigned char)c >= 160)) && pos < 40) {
+                            username[pos++] = c;
+                            c_write(&c, 1);
+                        }
+                        break;
+                    }
+                }
+            }
            c_write_str("\r\n");
            username[strcspn(username, "\n\r")] = '\0';
        } else {
@@ -3003,7 +3038,8 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                request[4] = SSH2_AGENTC_REQUEST_IDENTITIES;
                agent_query(request, 5, &r, &responselen);
                response = (unsigned char *)r;
-               if (response) {
+               if (response && responselen >= 5 &&
+                    response[4] == SSH2_AGENT_IDENTITIES_ANSWER) {
                    p = response + 5;
                    nkeys = GET_32BIT(p); p += 4;
                    { char buf[64]; sprintf(buf, "Pageant has %d SSH2 keys", nkeys);
@@ -3156,13 +3192,14 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
            }
 
            if (need_pw) {
-               if (ssh_get_password) {
-                   if (!ssh_get_password(pwprompt, password, sizeof(password))) {
+               if (ssh_get_line) {
+                   if (!ssh_get_line(pwprompt, password,
+                                      sizeof(password), TRUE)) {
                        /*
-                        * get_password failed to get a password (for
-                        * example because one was supplied on the command
-                        * line which has already failed to work).
-                        * Terminate.
+                        * get_line failed to get a password (for
+                        * example because one was supplied on the
+                        * command line which has already failed to
+                        * work). Terminate.
                         */
                        logevent("No more passwords to try");
                        ssh_state = SSH_STATE_CLOSED;
@@ -3423,9 +3460,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
      * Potentially enable agent forwarding.
      */
     if (cfg.agentfwd && agent_exists()) {
-        char proto[20], data[64];
         logevent("Requesting OpenSSH-style agent forwarding");
-        x11_invent_auth(proto, sizeof(proto), data, sizeof(data));
         ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
         ssh2_pkt_adduint32(mainchan->remoteid);
         ssh2_pkt_addstring("auth-agent-req@openssh.com");