Support diffie-hellman-group14-sha1 group exchange. Tested against
[u/mdw/putty] / ssh.c
diff --git a/ssh.c b/ssh.c
index 07d06f1..a6fba8b 100644 (file)
--- a/ssh.c
+++ b/ssh.c
  * Packet type contexts, so that ssh2_pkt_type can correctly decode
  * the ambiguous type numbers back into the correct type strings.
  */
-#define SSH2_PKTCTX_DHGROUP1         0x0001
+#define SSH2_PKTCTX_DHGROUP          0x0001
 #define SSH2_PKTCTX_DHGEX            0x0002
 #define SSH2_PKTCTX_PUBLICKEY        0x0010
 #define SSH2_PKTCTX_PASSWORD         0x0020
@@ -222,8 +222,8 @@ static char *ssh2_pkt_type(int pkt_ctx, int type)
     translate(SSH2_MSG_SERVICE_ACCEPT);
     translate(SSH2_MSG_KEXINIT);
     translate(SSH2_MSG_NEWKEYS);
-    translatec(SSH2_MSG_KEXDH_INIT, SSH2_PKTCTX_DHGROUP1);
-    translatec(SSH2_MSG_KEXDH_REPLY, SSH2_PKTCTX_DHGROUP1);
+    translatec(SSH2_MSG_KEXDH_INIT, SSH2_PKTCTX_DHGROUP);
+    translatec(SSH2_MSG_KEXDH_REPLY, SSH2_PKTCTX_DHGROUP);
     translatec(SSH2_MSG_KEX_DH_GEX_REQUEST, SSH2_PKTCTX_DHGEX);
     translatec(SSH2_MSG_KEX_DH_GEX_GROUP, SSH2_PKTCTX_DHGEX);
     translatec(SSH2_MSG_KEX_DH_GEX_INIT, SSH2_PKTCTX_DHGEX);
@@ -362,7 +362,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
 
 const static struct ssh_kex *kex_algs[] = {
     &ssh_diffiehellman_gex,
-    &ssh_diffiehellman
+    &ssh_diffiehellman_group14,
+    &ssh_diffiehellman_group1,
 };
 
 const static struct ssh_signkey *hostkey_algs[] = { &ssh_rsa, &ssh_dss };
@@ -2067,6 +2068,29 @@ static void ssh_detect_bugs(Ssh ssh, char *vstring)
     }
 }
 
+/*
+ * The `software version' part of an SSH version string is required
+ * to contain no spaces or minus signs.
+ */
+static void ssh_fix_verstring(char *str)
+{
+    /* Eat "SSH-<protoversion>-". */
+    assert(*str == 'S'); str++;
+    assert(*str == 'S'); str++;
+    assert(*str == 'H'); str++;
+    assert(*str == '-'); str++;
+    while (*str && *str != '-') str++;
+    assert(*str == '-'); str++;
+
+    /* Convert minus signs and spaces in the remaining string into
+     * underscores. */
+    while (*str) {
+        if (*str == '-' || *str == ' ')
+            *str = '_';
+        str++;
+    }
+}
+
 static int do_ssh_init(Ssh ssh, unsigned char c)
 {
     struct do_ssh_init_state {
@@ -2126,7 +2150,7 @@ static int do_ssh_init(Ssh ssh, unsigned char c)
     ssh->rdpkt2_state.incoming_sequence = 0;
 
     s->vstring[s->vslen] = 0;
-    s->vstring[strcspn(s->vstring, "\r\n")] = '\0';/* remove EOL chars */
+    s->vstring[strcspn(s->vstring, "\015\012")] = '\0';/* remove EOL chars */
     {
        char *vlog;
        vlog = snewn(20 + s->vslen, char);
@@ -2154,46 +2178,60 @@ static int do_ssh_init(Ssh ssh, unsigned char c)
        crStop(0);
     }
 
-    if (s->proto2 && (ssh->cfg.sshprot >= 2 || !s->proto1)) {
-       /*
-        * Use v2 protocol.
-        */
-       char verstring[80], vlog[100];
-       sprintf(verstring, "SSH-2.0-%s", sshver);
-       SHA_Init(&ssh->exhashbase);
-       /*
-        * Hash our version string and their version string.
-        */
-       sha_string(&ssh->exhashbase, verstring, strlen(verstring));
-       sha_string(&ssh->exhashbase, s->vstring, strcspn(s->vstring, "\r\n"));
-       sprintf(vlog, "We claim version: %s", verstring);
-       logevent(vlog);
-       strcat(verstring, "\012");
-       logevent("Using SSH protocol version 2");
-       sk_write(ssh->s, verstring, strlen(verstring));
-       ssh->protocol = ssh2_protocol;
-       ssh2_protocol_setup(ssh);
-       ssh->version = 2;
-       ssh->s_rdpkt = ssh2_rdpkt;
-    } else {
-       /*
-        * Use v1 protocol.
-        */
-       char verstring[80], vlog[100];
-       sprintf(verstring, "SSH-%s-%s",
-               (ssh_versioncmp(s->version, "1.5") <= 0 ? s->version : "1.5"),
-               sshver);
-       sprintf(vlog, "We claim version: %s", verstring);
-       logevent(vlog);
-       strcat(verstring, "\012");
-
-       logevent("Using SSH protocol version 1");
+    {
+        char *verstring;
+
+        if (s->proto2 && (ssh->cfg.sshprot >= 2 || !s->proto1)) {
+            /*
+             * Construct a v2 version string.
+             */
+            verstring = dupprintf("SSH-2.0-%s\015\012", sshver);
+            ssh->version = 2;
+        } else {
+            /*
+             * Construct a v1 version string.
+             */
+            verstring = dupprintf("SSH-%s-%s\012",
+                                  (ssh_versioncmp(s->version, "1.5") <= 0 ?
+                                   s->version : "1.5"),
+                                  sshver);
+            ssh->version = 1;
+        }
+
+        ssh_fix_verstring(verstring);
+
+        if (ssh->version == 2) {
+            /*
+             * Hash our version string and their version string.
+             */
+            SHA_Init(&ssh->exhashbase);
+            sha_string(&ssh->exhashbase, verstring,
+                       strcspn(verstring, "\015\012"));
+            sha_string(&ssh->exhashbase, s->vstring,
+                       strcspn(s->vstring, "\015\012"));
+
+            /*
+             * Initialise SSHv2 protocol.
+             */
+            ssh->protocol = ssh2_protocol;
+            ssh2_protocol_setup(ssh);
+            ssh->s_rdpkt = ssh2_rdpkt;
+        } else {
+            /*
+             * Initialise SSHv1 protocol.
+             */
+            ssh->protocol = ssh1_protocol;
+            ssh1_protocol_setup(ssh);
+            ssh->s_rdpkt = ssh1_rdpkt;
+        }
+        logeventf(ssh, "We claim version: %.*s",
+                  strcspn(verstring, "\015\012"), verstring);
        sk_write(ssh->s, verstring, strlen(verstring));
-       ssh->protocol = ssh1_protocol;
-       ssh1_protocol_setup(ssh);
-       ssh->version = 1;
-       ssh->s_rdpkt = ssh1_rdpkt;
+        sfree(verstring);
     }
+
+    logeventf(ssh, "Using SSH protocol version %d", ssh->version);
+
     update_specials_menu(ssh->frontend);
     ssh->state = SSH_STATE_BEFORE_SIZE;
     ssh->pinger = pinger_new(&ssh->cfg, &ssh_backend, ssh);
@@ -4613,7 +4651,7 @@ static int do_ssh2_transport(Ssh ssh, unsigned char *in, int inlen,
      * If we're doing Diffie-Hellman group exchange, start by
      * requesting a group.
      */
-    if (ssh->kex == &ssh_diffiehellman_gex) {
+    if (!ssh->kex->pdata) {
        logevent("Doing Diffie-Hellman group exchange");
        ssh->pkt_ctx |= SSH2_PKTCTX_DHGEX;
        /*
@@ -4636,14 +4674,16 @@ static int do_ssh2_transport(Ssh ssh, unsigned char *in, int inlen,
            bombout(("unable to read mp-ints from incoming group packet"));
            crStop(0);
        }
-       ssh->kex_ctx = dh_setup_group(s->p, s->g);
+       ssh->kex_ctx = dh_setup_gex(s->p, s->g);
        s->kex_init_value = SSH2_MSG_KEX_DH_GEX_INIT;
        s->kex_reply_value = SSH2_MSG_KEX_DH_GEX_REPLY;
     } else {
-       ssh->pkt_ctx |= SSH2_PKTCTX_DHGROUP1;
-       ssh->kex_ctx = dh_setup_group1();
+       ssh->pkt_ctx |= SSH2_PKTCTX_DHGROUP;
+       ssh->kex_ctx = dh_setup_group(ssh->kex);
        s->kex_init_value = SSH2_MSG_KEXDH_INIT;
        s->kex_reply_value = SSH2_MSG_KEXDH_REPLY;
+       logeventf(ssh, "Using Diffie-Hellman with standard group \"%s\"",
+                 ssh->kex->groupname);
     }
 
     logevent("Doing Diffie-Hellman key exchange");
@@ -4732,7 +4772,7 @@ static int do_ssh2_transport(Ssh ssh, unsigned char *in, int inlen,
 
     /*
      * We've sent client NEWKEYS, so create and initialise
-     * client-to-servere session keys.
+     * client-to-server session keys.
      */
     if (ssh->cs_cipher_ctx)
        ssh->cscipher->free_context(ssh->cs_cipher_ctx);
@@ -7198,12 +7238,14 @@ static void ssh_free(void *handle)
            sfree(c);
        }
        freetree234(ssh->channels);
+       ssh->channels = NULL;
     }
 
     if (ssh->rportfwds) {
        while ((pf = delpos234(ssh->rportfwds, 0)) != NULL)
            sfree(pf);
        freetree234(ssh->rportfwds);
+       ssh->rportfwds = NULL;
     }
     sfree(ssh->deferred_send_data);
     if (ssh->x11auth)
@@ -7219,9 +7261,9 @@ static void ssh_free(void *handle)
     if (ssh->s)
        ssh_do_close(ssh);
     expire_timer_context(ssh);
-    sfree(ssh);
     if (ssh->pinger)
        pinger_free(ssh->pinger);
+    sfree(ssh);
 
     random_unref();
 }