]> andersk Git - openssh.git/blobdiff - kex.c
- avsm@cvs.openbsd.org 2004/06/21 17:36:31
[openssh.git] / kex.c
diff --git a/kex.c b/kex.c
index 255cc7431dae4bb042cbc605fdcdae922f880707..a668346c393411efac7a27978ff029e4a194bb83 100644 (file)
--- a/kex.c
+++ b/kex.c
@@ -23,7 +23,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: kex.c,v 1.41 2001/12/28 15:06:00 markus Exp $");
+RCSID("$OpenBSD: kex.c,v 1.60 2004/06/21 17:36:31 avsm Exp $");
 
 #include <openssl/crypto.h>
 
@@ -40,6 +40,7 @@ RCSID("$OpenBSD: kex.c,v 1.41 2001/12/28 15:06:00 markus Exp $");
 #include "mac.h"
 #include "match.h"
 #include "dispatch.h"
+#include "monitor.h"
 
 #define KEX_COOKIE_LEN 16
 
@@ -51,16 +52,15 @@ static void kex_choose_conf(Kex *);
 static void
 kex_prop2buf(Buffer *b, char *proposal[PROPOSAL_MAX])
 {
-       u_int32_t rand = 0;
        int i;
 
        buffer_clear(b);
-       for (i = 0; i < KEX_COOKIE_LEN; i++) {
-               if (i % 4 == 0)
-                       rand = arc4random();
-               buffer_put_char(b, rand & 0xff);
-               rand >>= 8;
-       }
+       /*
+        * add a dummy cookie, the cookie will be overwritten by
+        * kex_send_kexinit(), each time a kexinit is set
+        */
+       for (i = 0; i < KEX_COOKIE_LEN; i++)
+               buffer_put_char(b, 0);
        for (i = 0; i < PROPOSAL_MAX; i++)
                buffer_put_cstring(b, proposal[i]);
        buffer_put_char(b, 0);                  /* first_kex_packet_follows */
@@ -69,7 +69,7 @@ kex_prop2buf(Buffer *b, char *proposal[PROPOSAL_MAX])
 
 /* parse buffer and return algorithm proposal */
 static char **
-kex_buf2prop(Buffer *raw)
+kex_buf2prop(Buffer *raw, int *first_kex_follows)
 {
        Buffer b;
        int i;
@@ -89,6 +89,8 @@ kex_buf2prop(Buffer *raw)
        }
        /* first kex follows / reserved */
        i = buffer_get_char(&b);
+       if (first_kex_follows != NULL)
+               *first_kex_follows = i;
        debug2("kex_parse_kexinit: first_kex_follows %d ", i);
        i = buffer_get_int(&b);
        debug2("kex_parse_kexinit: reserved %d ", i);
@@ -113,27 +115,26 @@ kex_protocol_error(int type, u_int32_t seq, void *ctxt)
 }
 
 static void
-kex_clear_dispatch(void)
+kex_reset_dispatch(void)
 {
-       int i;
-
-       /* Numbers 30-49 are used for kex packets */
-       for (i = 30; i <= 49; i++)
-               dispatch_set(i, &kex_protocol_error);
+       dispatch_range(SSH2_MSG_TRANSPORT_MIN,
+           SSH2_MSG_TRANSPORT_MAX, &kex_protocol_error);
+       dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit);
 }
 
 void
 kex_finish(Kex *kex)
 {
-       kex_clear_dispatch();
+       kex_reset_dispatch();
 
        packet_start(SSH2_MSG_NEWKEYS);
        packet_send();
        /* packet_write_wait(); */
        debug("SSH2_MSG_NEWKEYS sent");
 
-       debug("waiting for SSH2_MSG_NEWKEYS");
+       debug("expecting SSH2_MSG_NEWKEYS");
        packet_read_expect(SSH2_MSG_NEWKEYS);
+       packet_check_eom();
        debug("SSH2_MSG_NEWKEYS received");
 
        kex->done = 1;
@@ -147,6 +148,10 @@ kex_finish(Kex *kex)
 void
 kex_send_kexinit(Kex *kex)
 {
+       u_int32_t rnd = 0;
+       u_char *cookie;
+       int i;
+
        if (kex == NULL) {
                error("kex_send_kexinit: no kex, cannot rekey");
                return;
@@ -156,6 +161,17 @@ kex_send_kexinit(Kex *kex)
                return;
        }
        kex->done = 0;
+
+       /* generate a random cookie */
+       if (buffer_len(&kex->my) < KEX_COOKIE_LEN)
+               fatal("kex_send_kexinit: kex proposal too short");
+       cookie = buffer_ptr(&kex->my);
+       for (i = 0; i < KEX_COOKIE_LEN; i++) {
+               if (i % 4 == 0)
+                       rnd = arc4random();
+               cookie[i] = rnd;
+               rnd >>= 8;
+       }
        packet_start(SSH2_MSG_KEXINIT);
        packet_put_raw(buffer_ptr(&kex->my), buffer_len(&kex->my));
        packet_send();
@@ -183,8 +199,8 @@ kex_input_kexinit(int type, u_int32_t seq, void *ctxt)
                packet_get_char();
        for (i = 0; i < PROPOSAL_MAX; i++)
                xfree(packet_get_string(NULL));
-       packet_get_char();
-       packet_get_int();
+       (void) packet_get_char();
+       (void) packet_get_int();
        packet_check_eom();
 
        kex_kexinit_finish(kex);
@@ -203,8 +219,7 @@ kex_setup(char *proposal[PROPOSAL_MAX])
        kex->done = 0;
 
        kex_send_kexinit(kex);                                  /* we start */
-       kex_clear_dispatch();
-       dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit);
+       kex_reset_dispatch();
 
        return kex;
 }
@@ -217,14 +232,10 @@ kex_kexinit_finish(Kex *kex)
 
        kex_choose_conf(kex);
 
-       switch (kex->kex_type) {
-       case DH_GRP1_SHA1:
-               kexdh(kex);
-               break;
-       case DH_GEX_SHA1:
-               kexgex(kex);
-               break;
-       default:
+       if (kex->kex_type >= 0 && kex->kex_type < KEX_MAX &&
+           kex->kex[kex->kex_type] != NULL) {
+               (kex->kex[kex->kex_type])(kex);
+       } else {
                fatal("Unsupported key exchange %d", kex->kex_type);
        }
 }
@@ -235,13 +246,14 @@ choose_enc(Enc *enc, char *client, char *server)
        char *name = match_list(client, server, NULL);
        if (name == NULL)
                fatal("no matching cipher found: client %s server %s", client, server);
-       enc->cipher = cipher_by_name(name);
-       if (enc->cipher == NULL)
+       if ((enc->cipher = cipher_by_name(name)) == NULL)
                fatal("matching cipher is not supported: %s", name);
        enc->name = name;
        enc->enabled = 0;
        enc->iv = NULL;
        enc->key = NULL;
+       enc->key_len = cipher_keylen(enc->cipher);
+       enc->block_size = cipher_blocksize(enc->cipher);
 }
 static void
 choose_mac(Mac *mac, char *client, char *server)
@@ -280,9 +292,11 @@ choose_kex(Kex *k, char *client, char *server)
        if (k->name == NULL)
                fatal("no kex alg");
        if (strcmp(k->name, KEX_DH1) == 0) {
-               k->kex_type = DH_GRP1_SHA1;
+               k->kex_type = KEX_DH_GRP1_SHA1;
+       } else if (strcmp(k->name, KEX_DH14) == 0) {
+               k->kex_type = KEX_DH_GRP14_SHA1;
        } else if (strcmp(k->name, KEX_DHGEX) == 0) {
-               k->kex_type = DH_GEX_SHA1;
+               k->kex_type = KEX_DH_GEX_SHA1;
        } else
                fatal("bad kex alg %s", k->name);
 }
@@ -298,6 +312,30 @@ choose_hostkeyalg(Kex *k, char *client, char *server)
        xfree(hostkeyalg);
 }
 
+static int
+proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX])
+{
+       static int check[] = {
+               PROPOSAL_KEX_ALGS, PROPOSAL_SERVER_HOST_KEY_ALGS, -1
+       };
+       int *idx;
+       char *p;
+
+       for (idx = &check[0]; *idx != -1; idx++) {
+               if ((p = strchr(my[*idx], ',')) != NULL)
+                       *p = '\0';
+               if ((p = strchr(peer[*idx], ',')) != NULL)
+                       *p = '\0';
+               if (strcmp(my[*idx], peer[*idx]) != 0) {
+                       debug2("proposal mismatch: my %s peer %s",
+                           my[*idx], peer[*idx]);
+                       return (0);
+               }
+       }
+       debug2("proposals match");
+       return (1);
+}
+
 static void
 kex_choose_conf(Kex *kex)
 {
@@ -308,9 +346,10 @@ kex_choose_conf(Kex *kex)
        int mode;
        int ctos;                               /* direction: if true client-to-server */
        int need;
+       int first_kex_follows, type;
 
-       my   = kex_buf2prop(&kex->my);
-       peer = kex_buf2prop(&kex->peer);
+       my   = kex_buf2prop(&kex->my, NULL);
+       peer = kex_buf2prop(&kex->peer, &first_kex_follows);
 
        if (kex->server) {
                cprop=peer;
@@ -344,16 +383,23 @@ kex_choose_conf(Kex *kex)
        need = 0;
        for (mode = 0; mode < MODE_MAX; mode++) {
                newkeys = kex->newkeys[mode];
-               if (need < newkeys->enc.cipher->key_len)
-                       need = newkeys->enc.cipher->key_len;
-               if (need < newkeys->enc.cipher->block_size)
-                       need = newkeys->enc.cipher->block_size;
+               if (need < newkeys->enc.key_len)
+                       need = newkeys->enc.key_len;
+               if (need < newkeys->enc.block_size)
+                       need = newkeys->enc.block_size;
                if (need < newkeys->mac.key_len)
                        need = newkeys->mac.key_len;
        }
        /* XXX need runden? */
        kex->we_need = need;
 
+       /* ignore the next message if the proposals do not match */
+       if (first_kex_follows && !proposals_match(my, peer) &&
+          !(datafellows & SSH_BUG_FIRSTKEX)) {
+               type = packet_read();
+               debug2("skipping next packet (type %u)", type);
+       }
+
        kex_prop_free(my);
        kex_prop_free(peer);
 }
@@ -362,11 +408,11 @@ static u_char *
 derive_key(Kex *kex, int id, int need, u_char *hash, BIGNUM *shared_secret)
 {
        Buffer b;
-       EVP_MD *evp_md = EVP_sha1();
+       const EVP_MD *evp_md = EVP_sha1();
        EVP_MD_CTX md;
        char c = id;
        int have;
-       int mdsz = evp_md->md_size;
+       int mdsz = EVP_MD_size(evp_md);
        u_char *digest = xmalloc(roundup(need, mdsz));
 
        buffer_init(&b);
@@ -414,7 +460,7 @@ kex_derive_keys(Kex *kex, u_char *hash, BIGNUM *shared_secret)
        for (i = 0; i < NKEYS; i++)
                keys[i] = derive_key(kex, 'A'+i, kex->we_need, hash, shared_secret);
 
-       debug("kex_derive_keys");
+       debug2("kex_derive_keys");
        for (mode = 0; mode < MODE_MAX; mode++) {
                current_keys[mode] = kex->newkeys[mode];
                kex->newkeys[mode] = NULL;
@@ -435,6 +481,39 @@ kex_get_newkeys(int mode)
        return ret;
 }
 
+void
+derive_ssh1_session_id(BIGNUM *host_modulus, BIGNUM *server_modulus,
+    u_int8_t cookie[8], u_int8_t id[16])
+{
+       const EVP_MD *evp_md = EVP_md5();
+       EVP_MD_CTX md;
+       u_int8_t nbuf[2048], obuf[EVP_MAX_MD_SIZE];
+       int len;
+
+       EVP_DigestInit(&md, evp_md);
+
+       len = BN_num_bytes(host_modulus);
+       if (len < (512 / 8) || len > sizeof(nbuf))
+               fatal("%s: bad host modulus (len %d)", __func__, len);
+       BN_bn2bin(host_modulus, nbuf);
+       EVP_DigestUpdate(&md, nbuf, len);
+
+       len = BN_num_bytes(server_modulus);
+       if (len < (512 / 8) || len > sizeof(nbuf))
+               fatal("%s: bad server modulus (len %d)", __func__, len);
+       BN_bn2bin(server_modulus, nbuf);
+       EVP_DigestUpdate(&md, nbuf, len);
+
+       EVP_DigestUpdate(&md, cookie, 8);
+
+       EVP_DigestFinal(&md, obuf, NULL);
+       memcpy(id, obuf, 16);
+
+       memset(nbuf, 0, sizeof(nbuf));
+       memset(obuf, 0, sizeof(obuf));
+       memset(&md, 0, sizeof(md));
+}
+
 #if defined(DEBUG_KEX) || defined(DEBUG_KEXDH)
 void
 dump_digest(char *msg, u_char *digest, int len)
This page took 0.050878 seconds and 4 git commands to generate.