]> andersk Git - openssh.git/blobdiff - kex.c
- markus@cvs.openbsd.org 2002/11/21 22:45:31
[openssh.git] / kex.c
diff --git a/kex.c b/kex.c
index 1314270d4965d94790172c4989c8caeebc7c4432..113663598eff617c8ce57734363ff0badb442efa 100644 (file)
--- a/kex.c
+++ b/kex.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000 Markus Friedl.  All rights reserved.
+ * Copyright (c) 2000, 2001 Markus Friedl.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -23,7 +23,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: kex.c,v 1.28 2001/04/04 09:48:34 markus Exp $");
+RCSID("$OpenBSD: kex.c,v 1.52 2002/11/21 22:45:31 markus Exp $");
 
 #include <openssl/crypto.h>
 
@@ -40,26 +40,32 @@ RCSID("$OpenBSD: kex.c,v 1.28 2001/04/04 09:48:34 markus Exp $");
 #include "mac.h"
 #include "match.h"
 #include "dispatch.h"
+#include "monitor.h"
 
 #define KEX_COOKIE_LEN 16
 
-void   kex_kexinit_finish(Kex *kex);
-void   kex_choose_conf(Kex *k);
+/* Use privilege separation for sshd */
+int use_privsep;
+struct monitor *pmonitor;
+
+
+/* prototype */
+static void kex_kexinit_finish(Kex *);
+static void kex_choose_conf(Kex *);
 
 /* put algorithm proposal into buffer */
-void
+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 */
@@ -67,7 +73,7 @@ kex_prop2buf(Buffer *b, char *proposal[PROPOSAL_MAX])
 }
 
 /* parse buffer and return algorithm proposal */
-char **
+static char **
 kex_buf2prop(Buffer *raw)
 {
        Buffer b;
@@ -95,7 +101,7 @@ kex_buf2prop(Buffer *raw)
        return proposal;
 }
 
-void
+static void
 kex_prop_free(char **proposal)
 {
        int i;
@@ -105,40 +111,70 @@ kex_prop_free(char **proposal)
        xfree(proposal);
 }
 
-void
-kex_protocol_error(int type, int plen, void *ctxt)
+static void
+kex_protocol_error(int type, u_int32_t seq, void *ctxt)
 {
-        error("Hm, kex protocol error: type %d plen %d", type, plen);
+       error("Hm, kex protocol error: type %d seq %u", type, seq);
+}
+
+static void
+kex_reset_dispatch(void)
+{
+       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)
 {
-       int i, plen;
+       kex_reset_dispatch();
 
        packet_start(SSH2_MSG_NEWKEYS);
        packet_send();
        /* packet_write_wait(); */
        debug("SSH2_MSG_NEWKEYS sent");
 
-        debug("waiting for SSH2_MSG_NEWKEYS");
-        packet_read_expect(&plen, SSH2_MSG_NEWKEYS);
+       debug("expecting SSH2_MSG_NEWKEYS");
+       packet_read_expect(SSH2_MSG_NEWKEYS);
+       packet_check_eom();
        debug("SSH2_MSG_NEWKEYS received");
-       kex->newkeys = 1;
-       for (i = 30; i <= 49; i++)
-               dispatch_set(i, &kex_protocol_error);
+
+       kex->done = 1;
        buffer_clear(&kex->peer);
        /* buffer_clear(&kex->my); */
        kex->flags &= ~KEX_INIT_SENT;
+       xfree(kex->name);
+       kex->name = NULL;
 }
 
 void
 kex_send_kexinit(Kex *kex)
 {
+       u_int32_t rand = 0;
+       u_char *cookie;
+       int i;
+
+       if (kex == NULL) {
+               error("kex_send_kexinit: no kex, cannot rekey");
+               return;
+       }
        if (kex->flags & KEX_INIT_SENT) {
                debug("KEX_INIT_SENT");
                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)
+                       rand = arc4random();
+               cookie[i] = rand;
+               rand >>= 8;
+       }
        packet_start(SSH2_MSG_KEXINIT);
        packet_put_raw(buffer_ptr(&kex->my), buffer_len(&kex->my));
        packet_send();
@@ -147,17 +183,29 @@ kex_send_kexinit(Kex *kex)
 }
 
 void
-kex_input_kexinit(int type, int plen, void *ctxt)
+kex_input_kexinit(int type, u_int32_t seq, void *ctxt)
 {
        char *ptr;
        int dlen;
+       int i;
        Kex *kex = (Kex *)ctxt;
 
        debug("SSH2_MSG_KEXINIT received");
+       if (kex == NULL)
+               fatal("kex_input_kexinit: no kex, cannot rekey");
 
        ptr = packet_get_raw(&dlen);
        buffer_append(&kex->peer, ptr, dlen);
 
+       /* discard packet */
+       for (i = 0; i < KEX_COOKIE_LEN; i++)
+               packet_get_char();
+       for (i = 0; i < PROPOSAL_MAX; i++)
+               xfree(packet_get_string(NULL));
+       (void) packet_get_char();
+       (void) packet_get_int();
+       packet_check_eom();
+
        kex_kexinit_finish(kex);
 }
 
@@ -165,25 +213,21 @@ Kex *
 kex_setup(char *proposal[PROPOSAL_MAX])
 {
        Kex *kex;
-       int i;
 
        kex = xmalloc(sizeof(*kex));
        memset(kex, 0, sizeof(*kex));
        buffer_init(&kex->peer);
        buffer_init(&kex->my);
        kex_prop2buf(&kex->my, proposal);
-       kex->newkeys = 0;
+       kex->done = 0;
 
        kex_send_kexinit(kex);                                  /* we start */
-       /* Numbers 30-49 are used for kex packets */
-       for (i = 30; i <= 49; i++)
-               dispatch_set(i, kex_protocol_error);
+       kex_reset_dispatch();
 
-       dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit);
        return kex;
 }
 
-void
+static void
 kex_kexinit_finish(Kex *kex)
 {
        if (!(kex->flags & KEX_INIT_SENT))
@@ -191,7 +235,7 @@ kex_kexinit_finish(Kex *kex)
 
        kex_choose_conf(kex);
 
-       switch(kex->kex_type) {
+       switch (kex->kex_type) {
        case DH_GRP1_SHA1:
                kexdh(kex);
                break;
@@ -203,21 +247,22 @@ kex_kexinit_finish(Kex *kex)
        }
 }
 
-void
+static void
 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);
 }
-void
+static void
 choose_mac(Mac *mac, char *client, char *server)
 {
        char *name = match_list(client, server, NULL);
@@ -232,7 +277,7 @@ choose_mac(Mac *mac, char *client, char *server)
        mac->key = NULL;
        mac->enabled = 0;
 }
-void
+static void
 choose_comp(Comp *comp, char *client, char *server)
 {
        char *name = match_list(client, server, NULL);
@@ -247,7 +292,7 @@ choose_comp(Comp *comp, char *client, char *server)
        }
        comp->name = name;
 }
-void
+static void
 choose_kex(Kex *k, char *client, char *server)
 {
        k->name = match_list(client, server, NULL);
@@ -260,7 +305,7 @@ choose_kex(Kex *k, char *client, char *server)
        } else
                fatal("bad kex alg %s", k->name);
 }
-void
+static void
 choose_hostkeyalg(Kex *k, char *client, char *server)
 {
        char *hostkeyalg = match_list(client, server, NULL);
@@ -272,7 +317,7 @@ choose_hostkeyalg(Kex *k, char *client, char *server)
        xfree(hostkeyalg);
 }
 
-void
+static void
 kex_choose_conf(Kex *kex)
 {
        Newkeys *newkeys;
@@ -294,10 +339,11 @@ kex_choose_conf(Kex *kex)
                sprop=peer;
        }
 
+       /* Algorithm Negotiation */
        for (mode = 0; mode < MODE_MAX; mode++) {
                newkeys = xmalloc(sizeof(*newkeys));
                memset(newkeys, 0, sizeof(*newkeys));
-               kex->keys[mode] = newkeys;
+               kex->newkeys[mode] = newkeys;
                ctos = (!kex->server && mode == MODE_OUT) || (kex->server && mode == MODE_IN);
                nenc  = ctos ? PROPOSAL_ENC_ALGS_CTOS  : PROPOSAL_ENC_ALGS_STOC;
                nmac  = ctos ? PROPOSAL_MAC_ALGS_CTOS  : PROPOSAL_MAC_ALGS_STOC;
@@ -316,11 +362,11 @@ kex_choose_conf(Kex *kex)
            sprop[PROPOSAL_SERVER_HOST_KEY_ALGS]);
        need = 0;
        for (mode = 0; mode < MODE_MAX; mode++) {
-               newkeys = kex->keys[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;
+               newkeys = kex->newkeys[mode];
+               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;
        }
@@ -331,31 +377,38 @@ kex_choose_conf(Kex *kex)
        kex_prop_free(peer);
 }
 
-u_char *
+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;
-       u_char *digest = xmalloc(((need+mdsz-1)/mdsz)*mdsz);
+       int mdsz = EVP_MD_size(evp_md);
+       u_char *digest = xmalloc(roundup(need, mdsz));
 
        buffer_init(&b);
        buffer_put_bignum2(&b, shared_secret);
 
+       /* K1 = HASH(K || H || "A" || session_id) */
        EVP_DigestInit(&md, evp_md);
-       EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));  /* shared_secret K */
-       EVP_DigestUpdate(&md, hash, mdsz);              /* transport-06 */
-       EVP_DigestUpdate(&md, &c, 1);                   /* key id */
+       if (!(datafellows & SSH_BUG_DERIVEKEY))
+               EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
+       EVP_DigestUpdate(&md, hash, mdsz);
+       EVP_DigestUpdate(&md, &c, 1);
        EVP_DigestUpdate(&md, kex->session_id, kex->session_id_len);
        EVP_DigestFinal(&md, digest, NULL);
 
-       /* expand */
+       /*
+        * expand key:
+        * Kn = HASH(K || H || K1 || K2 || ... || Kn-1)
+        * Key = K1 || K2 || ... || Kn
+        */
        for (have = mdsz; need > have; have += mdsz) {
                EVP_DigestInit(&md, evp_md);
-               EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
+               if (!(datafellows & SSH_BUG_DERIVEKEY))
+                       EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
                EVP_DigestUpdate(&md, hash, mdsz);
                EVP_DigestUpdate(&md, digest, have);
                EVP_DigestFinal(&md, digest + have, NULL);
@@ -368,34 +421,37 @@ derive_key(Kex *kex, int id, int need, u_char *hash, BIGNUM *shared_secret)
        return digest;
 }
 
-Newkeys *x_newkeys[MODE_MAX];
+Newkeys *current_keys[MODE_MAX];
 
 #define NKEYS  6
 void
 kex_derive_keys(Kex *kex, u_char *hash, BIGNUM *shared_secret)
 {
-       Newkeys *newkeys;
        u_char *keys[NKEYS];
        int i, mode, ctos;
 
        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++) {
-               newkeys = kex->keys[mode];
+               current_keys[mode] = kex->newkeys[mode];
+               kex->newkeys[mode] = NULL;
                ctos = (!kex->server && mode == MODE_OUT) || (kex->server && mode == MODE_IN);
-               newkeys->enc.iv  = keys[ctos ? 0 : 1];
-               newkeys->enc.key = keys[ctos ? 2 : 3];
-               newkeys->mac.key = keys[ctos ? 4 : 5];
-               x_newkeys[mode] = newkeys;
+               current_keys[mode]->enc.iv  = keys[ctos ? 0 : 1];
+               current_keys[mode]->enc.key = keys[ctos ? 2 : 3];
+               current_keys[mode]->mac.key = keys[ctos ? 4 : 5];
        }
 }
 
 Newkeys *
 kex_get_newkeys(int mode)
 {
-       return x_newkeys[mode];
+       Newkeys *ret;
+
+       ret = current_keys[mode];
+       current_keys[mode] = NULL;
+       return ret;
 }
 
 #if defined(DEBUG_KEX) || defined(DEBUG_KEXDH)
@@ -405,7 +461,7 @@ dump_digest(char *msg, u_char *digest, int len)
        int i;
 
        fprintf(stderr, "%s\n", msg);
-       for (i = 0; i< len; i++){
+       for (i = 0; i< len; i++) {
                fprintf(stderr, "%02x", digest[i]);
                if (i%32 == 31)
                        fprintf(stderr, "\n");
This page took 0.119947 seconds and 4 git commands to generate.