diff options
author | Markus Friedl <markus@cvs.openbsd.org> | 2001-04-03 19:53:31 +0000 |
---|---|---|
committer | Markus Friedl <markus@cvs.openbsd.org> | 2001-04-03 19:53:31 +0000 |
commit | 761025b6048dc983849b3b222a869aaa62696685 (patch) | |
tree | 79de704e176cacc5cc6f538cd00927ad7bb2250b /usr.bin | |
parent | 4b84b33636d9bab5adb97f749b1e7890faf73f19 (diff) |
move kex to kex*.c, used dispatch_set() callbacks for kex. should
make rekeying easier.
Diffstat (limited to 'usr.bin')
-rw-r--r-- | usr.bin/ssh/dh.c | 21 | ||||
-rw-r--r-- | usr.bin/ssh/dh.h | 4 | ||||
-rw-r--r-- | usr.bin/ssh/kex.c | 386 | ||||
-rw-r--r-- | usr.bin/ssh/kex.h | 58 | ||||
-rw-r--r-- | usr.bin/ssh/kexdh.c | 344 | ||||
-rw-r--r-- | usr.bin/ssh/kexgex.c | 480 | ||||
-rw-r--r-- | usr.bin/ssh/lib/Makefile | 4 | ||||
-rw-r--r-- | usr.bin/ssh/sshconnect2.c | 409 | ||||
-rw-r--r-- | usr.bin/ssh/sshd.c | 359 |
9 files changed, 1105 insertions, 960 deletions
diff --git a/usr.bin/ssh/dh.c b/usr.bin/ssh/dh.c index 6c53b0038d0..03b9fd1b84e 100644 --- a/usr.bin/ssh/dh.c +++ b/usr.bin/ssh/dh.c @@ -23,7 +23,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: dh.c,v 1.11 2001/03/29 21:17:39 markus Exp $"); +RCSID("$OpenBSD: dh.c,v 1.12 2001/04/03 19:53:29 markus Exp $"); #include "xmalloc.h" @@ -273,3 +273,22 @@ dh_new_group1(void) return (dh_new_group_asc(gen, group1)); } + +/* + * Estimates the group order for a Diffie-Hellman group that has an + * attack complexity approximately the same as O(2**bits). Estimate + * with: O(exp(1.9223 * (ln q)^(1/3) (ln ln q)^(2/3))) + */ + +int +dh_estimate(int bits) +{ + + if (bits < 64) + return (512); /* O(2**63) */ + if (bits < 128) + return (1024); /* O(2**86) */ + if (bits < 192) + return (2048); /* O(2**116) */ + return (4096); /* O(2**156) */ +} diff --git a/usr.bin/ssh/dh.h b/usr.bin/ssh/dh.h index 13d2fa1620e..e8b2944ce9f 100644 --- a/usr.bin/ssh/dh.h +++ b/usr.bin/ssh/dh.h @@ -1,4 +1,4 @@ -/* $OpenBSD: dh.h,v 1.4 2001/03/29 21:17:39 markus Exp $ */ +/* $OpenBSD: dh.h,v 1.5 2001/04/03 19:53:29 markus Exp $ */ /* * Copyright (c) 2000 Niels Provos. All rights reserved. @@ -40,6 +40,8 @@ DH *dh_new_group1(void); void dh_gen_key(DH *, int); int dh_pub_is_valid(DH *dh, BIGNUM *dh_pub); +int dh_estimate(int bits); + #define DH_GRP_MIN 1024 #define DH_GRP_MAX 8192 diff --git a/usr.bin/ssh/kex.c b/usr.bin/ssh/kex.c index 576d4b56e77..a0a5b46fef8 100644 --- a/usr.bin/ssh/kex.c +++ b/usr.bin/ssh/kex.c @@ -23,12 +23,9 @@ */ #include "includes.h" -RCSID("$OpenBSD: kex.c,v 1.25 2001/03/29 21:17:39 markus Exp $"); +RCSID("$OpenBSD: kex.c,v 1.26 2001/04/03 19:53:29 markus Exp $"); #include <openssl/crypto.h> -#include <openssl/bio.h> -#include <openssl/bn.h> -#include <openssl/pem.h> #include "ssh2.h" #include "xmalloc.h" @@ -42,233 +39,169 @@ RCSID("$OpenBSD: kex.c,v 1.25 2001/03/29 21:17:39 markus Exp $"); #include "log.h" #include "mac.h" #include "match.h" +#include "dispatch.h" #define KEX_COOKIE_LEN 16 -Buffer * -kex_init(char *myproposal[PROPOSAL_MAX]) +void kex_kexinit_finish(Kex *kex); +void kex_choose_conf(Kex *k); + +/* put algorithm proposal into buffer */ +void +kex_prop2buf(Buffer *b, char *proposal[PROPOSAL_MAX]) { - int first_kex_packet_follows = 0; - u_char cookie[KEX_COOKIE_LEN]; u_int32_t rand = 0; int i; - Buffer *ki = xmalloc(sizeof(*ki)); + + buffer_clear(b); for (i = 0; i < KEX_COOKIE_LEN; i++) { if (i % 4 == 0) rand = arc4random(); - cookie[i] = rand & 0xff; + buffer_put_char(b, rand & 0xff); rand >>= 8; } - buffer_init(ki); - buffer_append(ki, (char *)cookie, sizeof cookie); for (i = 0; i < PROPOSAL_MAX; i++) - buffer_put_cstring(ki, myproposal[i]); - buffer_put_char(ki, first_kex_packet_follows); - buffer_put_int(ki, 0); /* uint32 reserved */ - return ki; + buffer_put_cstring(b, proposal[i]); + buffer_put_char(b, 0); /* first_kex_packet_follows */ + buffer_put_int(b, 0); /* uint32 reserved */ } -/* send kexinit, parse and save reply */ -void -kex_exchange_kexinit( - Buffer *my_kexinit, Buffer *peer_kexint, - char *peer_proposal[PROPOSAL_MAX]) +/* parse buffer and return algorithm proposal */ +char ** +kex_buf2prop(Buffer *raw) { + Buffer b; int i; - char *ptr; - int plen; + char **proposal; - debug("send KEXINIT"); - packet_start(SSH2_MSG_KEXINIT); - packet_put_raw(buffer_ptr(my_kexinit), buffer_len(my_kexinit)); - packet_send(); - packet_write_wait(); - debug("done"); - - /* - * read and save raw KEXINIT payload in buffer. this is used during - * computation of the session_id and the session keys. - */ - debug("wait KEXINIT"); - packet_read_expect(&plen, SSH2_MSG_KEXINIT); - ptr = packet_get_raw(&plen); - buffer_append(peer_kexint, ptr, plen); - - /* parse packet and save algorithm proposal */ + proposal = xmalloc(PROPOSAL_MAX * sizeof(char *)); + + buffer_init(&b); + buffer_append(&b, buffer_ptr(raw), buffer_len(raw)); /* skip cookie */ for (i = 0; i < KEX_COOKIE_LEN; i++) - packet_get_char(); + buffer_get_char(&b); /* extract kex init proposal strings */ for (i = 0; i < PROPOSAL_MAX; i++) { - peer_proposal[i] = packet_get_string(NULL); - debug("got kexinit: %s", peer_proposal[i]); + proposal[i] = buffer_get_string(&b,NULL); + debug2("kex_parse_kexinit: %s", proposal[i]); } - /* first kex follow / reserved */ - i = packet_get_char(); - debug("first kex follow: %d ", i); - i = packet_get_int(); - debug("reserved: %d ", i); - packet_done(); - debug("done"); + /* first kex follows / reserved */ + i = buffer_get_char(&b); + debug2("kex_parse_kexinit: first_kex_follows %d ", i); + i = buffer_get_int(&b); + debug2("kex_parse_kexinit: reserved %d ", i); + buffer_free(&b); + return proposal; } -#ifdef DEBUG_KEX void -dump_digest(u_char *digest, int len) +kex_prop_free(char **proposal) { int i; - for (i = 0; i< len; i++){ - fprintf(stderr, "%02x", digest[i]); - if(i%2!=0) - fprintf(stderr, " "); - } - fprintf(stderr, "\n"); + + for (i = 0; i < PROPOSAL_MAX; i++) + xfree(proposal[i]); + xfree(proposal); } -#endif -u_char * -kex_hash( - char *client_version_string, - char *server_version_string, - char *ckexinit, int ckexinitlen, - char *skexinit, int skexinitlen, - char *serverhostkeyblob, int sbloblen, - BIGNUM *client_dh_pub, - BIGNUM *server_dh_pub, - BIGNUM *shared_secret) +void +kex_protocol_error(int type, int plen, void *ctxt) { - Buffer b; - static u_char digest[EVP_MAX_MD_SIZE]; - EVP_MD *evp_md = EVP_sha1(); - EVP_MD_CTX md; - - buffer_init(&b); - buffer_put_string(&b, client_version_string, strlen(client_version_string)); - buffer_put_string(&b, server_version_string, strlen(server_version_string)); - - /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */ - buffer_put_int(&b, ckexinitlen+1); - buffer_put_char(&b, SSH2_MSG_KEXINIT); - buffer_append(&b, ckexinit, ckexinitlen); - buffer_put_int(&b, skexinitlen+1); - buffer_put_char(&b, SSH2_MSG_KEXINIT); - buffer_append(&b, skexinit, skexinitlen); - - buffer_put_string(&b, serverhostkeyblob, sbloblen); - buffer_put_bignum2(&b, client_dh_pub); - buffer_put_bignum2(&b, server_dh_pub); - buffer_put_bignum2(&b, shared_secret); - -#ifdef DEBUG_KEX - buffer_dump(&b); -#endif + error("Hm, kex protocol error: type %d plen %d", type, plen); +} - EVP_DigestInit(&md, evp_md); - EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); - EVP_DigestFinal(&md, digest, NULL); +void +kex_send_newkeys(void) +{ + packet_start(SSH2_MSG_NEWKEYS); + packet_send(); + /* packet_write_wait(); */ + debug("SSH2_MSG_NEWKEYS sent"); +} - buffer_free(&b); +void +kex_input_newkeys(int type, int plen, void *ctxt) +{ + Kex *kex = ctxt; + int i; -#ifdef DEBUG_KEX - dump_digest(digest, evp_md->md_size); -#endif - return digest; + debug("SSH2_MSG_NEWKEYS received"); + kex->newkeys = 1; + for (i = 30; i <= 49; i++) + dispatch_set(i, &kex_protocol_error); + buffer_clear(&kex->peer); + buffer_clear(&kex->my); + kex->flags &= ~KEX_INIT_SENT; } -u_char * -kex_hash_gex( - char *client_version_string, - char *server_version_string, - char *ckexinit, int ckexinitlen, - char *skexinit, int skexinitlen, - char *serverhostkeyblob, int sbloblen, - int min, int wantbits, int max, BIGNUM *prime, BIGNUM *gen, - BIGNUM *client_dh_pub, - BIGNUM *server_dh_pub, - BIGNUM *shared_secret) +void +kex_send_kexinit(Kex *kex) { - Buffer b; - static u_char digest[EVP_MAX_MD_SIZE]; - EVP_MD *evp_md = EVP_sha1(); - EVP_MD_CTX md; - - buffer_init(&b); - buffer_put_string(&b, client_version_string, strlen(client_version_string)); - buffer_put_string(&b, server_version_string, strlen(server_version_string)); - - /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */ - buffer_put_int(&b, ckexinitlen+1); - buffer_put_char(&b, SSH2_MSG_KEXINIT); - buffer_append(&b, ckexinit, ckexinitlen); - buffer_put_int(&b, skexinitlen+1); - buffer_put_char(&b, SSH2_MSG_KEXINIT); - buffer_append(&b, skexinit, skexinitlen); - - buffer_put_string(&b, serverhostkeyblob, sbloblen); - if (min == -1 || max == -1) - buffer_put_int(&b, wantbits); - else { - buffer_put_int(&b, min); - buffer_put_int(&b, wantbits); - buffer_put_int(&b, max); - } - buffer_put_bignum2(&b, prime); - buffer_put_bignum2(&b, gen); - buffer_put_bignum2(&b, client_dh_pub); - buffer_put_bignum2(&b, server_dh_pub); - buffer_put_bignum2(&b, shared_secret); + packet_start(SSH2_MSG_KEXINIT); + packet_put_raw(buffer_ptr(&kex->my), buffer_len(&kex->my)); + packet_send(); + debug("SSH2_MSG_KEXINIT sent"); + kex->flags |= KEX_INIT_SENT; +} -#ifdef DEBUG_KEX - buffer_dump(&b); -#endif +void +kex_input_kexinit(int type, int plen, void *ctxt) +{ + char *ptr; + int dlen; + Kex *kex = (Kex *)ctxt; - EVP_DigestInit(&md, evp_md); - EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); - EVP_DigestFinal(&md, digest, NULL); + dispatch_set(SSH2_MSG_KEXINIT, &kex_protocol_error); + debug("SSH2_MSG_KEXINIT received"); - buffer_free(&b); + ptr = packet_get_raw(&dlen); + buffer_append(&kex->peer, ptr, dlen); -#ifdef DEBUG_KEX - dump_digest(digest, evp_md->md_size); -#endif - return digest; + kex_kexinit_finish(kex); } -u_char * -derive_key(int id, int need, u_char *hash, BIGNUM *shared_secret) +Kex * +kex_start(char *proposal[PROPOSAL_MAX]) { - Buffer b; - 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); - - buffer_init(&b); - buffer_put_bignum2(&b, shared_secret); + Kex *kex; + int i; - 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 */ - EVP_DigestUpdate(&md, hash, mdsz); /* session id */ - EVP_DigestFinal(&md, digest, NULL); + 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_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); + + dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit); + dispatch_set(SSH2_MSG_NEWKEYS, &kex_input_newkeys); + return kex; +} - /* expand */ - for (have = mdsz; need > have; have += mdsz) { - EVP_DigestInit(&md, evp_md); - 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); +void +kex_kexinit_finish(Kex *kex) +{ + if (!(kex->flags & KEX_INIT_SENT)) + kex_send_kexinit(kex); + + kex_choose_conf(kex); + + switch(kex->kex_type) { + case DH_GRP1_SHA1: + kexdh(kex); + break; + case DH_GEX_SHA1: + kexgex(kex); + break; + default: + fatal("Unsupported key exchange %d", kex->kex_type); } - buffer_free(&b); -#ifdef DEBUG_KEX - fprintf(stderr, "Digest '%c'== ", c); - dump_digest(digest, need); -#endif - return digest; } void @@ -340,17 +273,25 @@ choose_hostkeyalg(Kex *k, char *client, char *server) xfree(hostkeyalg); } -Kex * -kex_choose_conf(char *cprop[PROPOSAL_MAX], char *sprop[PROPOSAL_MAX], int server) +void +kex_choose_conf(Kex *k) { + char **my, **peer; + char **cprop, **sprop; int mode; int ctos; /* direction: if true client-to-server */ int need; - Kex *k; - k = xmalloc(sizeof(*k)); - memset(k, 0, sizeof(*k)); - k->server = server; + my = kex_buf2prop(&k->my); + peer = kex_buf2prop(&k->peer); + + if (k->server) { + cprop=peer; + sprop=my; + } else { + cprop=my; + sprop=peer; + } for (mode = 0; mode < MODE_MAX; mode++) { int nenc, nmac, ncomp; @@ -381,11 +322,51 @@ kex_choose_conf(char *cprop[PROPOSAL_MAX], char *sprop[PROPOSAL_MAX], int server } /* XXX need runden? */ k->we_need = need; - return k; + + kex_prop_free(my); + kex_prop_free(peer); + +} + +u_char * +derive_key(int id, int need, u_char *hash, BIGNUM *shared_secret) +{ + Buffer b; + 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); + + buffer_init(&b); + buffer_put_bignum2(&b, shared_secret); + + 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 */ + EVP_DigestUpdate(&md, hash, mdsz); /* session id */ + EVP_DigestFinal(&md, digest, NULL); + + /* expand */ + for (have = mdsz; need > have; have += mdsz) { + EVP_DigestInit(&md, evp_md); + 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); + } + buffer_free(&b); +#ifdef DEBUG_KEX + fprintf(stderr, "key '%c'== ", c); + dump_digest("key", digest, need); +#endif + return digest; } #define NKEYS 6 -int +void kex_derive_keys(Kex *k, u_char *hash, BIGNUM *shared_secret) { int i; @@ -402,5 +383,22 @@ kex_derive_keys(Kex *k, u_char *hash, BIGNUM *shared_secret) k->enc[mode].key = keys[ctos ? 2 : 3]; k->mac[mode].key = keys[ctos ? 4 : 5]; } - return 0; } + +#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) +void +dump_digest(char *msg, u_char *digest, int len) +{ + int i; + + fprintf(stderr, "%s\n", msg); + for (i = 0; i< len; i++){ + fprintf(stderr, "%02x", digest[i]); + if (i%32 == 31) + fprintf(stderr, "\n"); + else if (i%8 == 7) + fprintf(stderr, " "); + } + fprintf(stderr, "\n"); +} +#endif diff --git a/usr.bin/ssh/kex.h b/usr.bin/ssh/kex.h index 4cc87d52707..58f6d82c0b2 100644 --- a/usr.bin/ssh/kex.h +++ b/usr.bin/ssh/kex.h @@ -1,4 +1,4 @@ -/* $OpenBSD: kex.h,v 1.17 2001/03/29 21:17:40 markus Exp $ */ +/* $OpenBSD: kex.h,v 1.18 2001/04/03 19:53:29 markus Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. @@ -28,6 +28,8 @@ #include <openssl/evp.h> #include "buffer.h" +#include "cipher.h" +#include "key.h" #define KEX_DH1 "diffie-hellman-group1-sha1" #define KEX_DHGEX "diffie-hellman-group-exchange-sha1" @@ -82,6 +84,7 @@ struct Comp { int enabled; char *name; }; +#define KEX_INIT_SENT 0x0001 struct Kex { Enc enc [MODE_MAX]; Mac mac [MODE_MAX]; @@ -91,40 +94,31 @@ struct Kex { char *name; int hostkey_type; int kex_type; + + /* used during kex */ + Buffer my; + Buffer peer; + int newkeys; + int flags; + void *state; + char *client_version_string; + char *server_version_string; + + int (*check_host_key)(Key *hostkey); + Key *(*load_host_key)(int type); }; -Buffer *kex_init(char *myproposal[PROPOSAL_MAX]); -void -kex_exchange_kexinit( - Buffer *my_kexinit, Buffer *peer_kexint, - char *peer_proposal[PROPOSAL_MAX]); -Kex * -kex_choose_conf(char *cprop[PROPOSAL_MAX], - char *sprop[PROPOSAL_MAX], int server); -int kex_derive_keys(Kex *k, u_char *hash, BIGNUM *shared_secret); +void kex_derive_keys(Kex *k, u_char *hash, BIGNUM *shared_secret); void packet_set_kex(Kex *k); +Kex *kex_start(char *proposal[PROPOSAL_MAX]); +void kex_send_newkeys(void); +void kex_protocol_error(int type, int plen, void *ctxt); -u_char * -kex_hash( - char *client_version_string, - char *server_version_string, - char *ckexinit, int ckexinitlen, - char *skexinit, int skexinitlen, - char *serverhostkeyblob, int sbloblen, - BIGNUM *client_dh_pub, - BIGNUM *server_dh_pub, - BIGNUM *shared_secret); +void kexdh(Kex *); +void kexgex(Kex *); + +#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) +void dump_digest(char *msg, u_char *digest, int len); +#endif -u_char * -kex_hash_gex( - char *client_version_string, - char *server_version_string, - char *ckexinit, int ckexinitlen, - char *skexinit, int skexinitlen, - char *serverhostkeyblob, int sbloblen, - int min, int wantbits, int max, - BIGNUM *prime, BIGNUM *gen, - BIGNUM *client_dh_pub, - BIGNUM *server_dh_pub, - BIGNUM *shared_secret); #endif diff --git a/usr.bin/ssh/kexdh.c b/usr.bin/ssh/kexdh.c new file mode 100644 index 00000000000..dcf4f123e1e --- /dev/null +++ b/usr.bin/ssh/kexdh.c @@ -0,0 +1,344 @@ +/* + * Copyright (c) 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 + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" +RCSID("$OpenBSD: kexdh.c,v 1.1 2001/04/03 19:53:29 markus Exp $"); + +#include <openssl/crypto.h> +#include <openssl/bn.h> + +#include "xmalloc.h" +#include "buffer.h" +#include "bufaux.h" +#include "key.h" +#include "kex.h" +#include "log.h" +#include "dispatch.h" +#include "packet.h" +#include "dh.h" +#include "ssh2.h" + +extern u_char *session_id2; +extern int session_id2_len; + +dispatch_fn kexdh_input_init; /* C -> S */ +dispatch_fn kexdh_input_reply; /* S -> C */ + +typedef struct State State; +struct State { + DH *dh; +}; + +u_char * +kex_dh_hash( + char *client_version_string, + char *server_version_string, + char *ckexinit, int ckexinitlen, + char *skexinit, int skexinitlen, + char *serverhostkeyblob, int sbloblen, + BIGNUM *client_dh_pub, + BIGNUM *server_dh_pub, + BIGNUM *shared_secret) +{ + Buffer b; + static u_char digest[EVP_MAX_MD_SIZE]; + EVP_MD *evp_md = EVP_sha1(); + EVP_MD_CTX md; + + buffer_init(&b); + buffer_put_string(&b, client_version_string, strlen(client_version_string)); + buffer_put_string(&b, server_version_string, strlen(server_version_string)); + + /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */ + buffer_put_int(&b, ckexinitlen+1); + buffer_put_char(&b, SSH2_MSG_KEXINIT); + buffer_append(&b, ckexinit, ckexinitlen); + buffer_put_int(&b, skexinitlen+1); + buffer_put_char(&b, SSH2_MSG_KEXINIT); + buffer_append(&b, skexinit, skexinitlen); + + buffer_put_string(&b, serverhostkeyblob, sbloblen); + buffer_put_bignum2(&b, client_dh_pub); + buffer_put_bignum2(&b, server_dh_pub); + buffer_put_bignum2(&b, shared_secret); + +#ifdef DEBUG_KEX + buffer_dump(&b); +#endif + EVP_DigestInit(&md, evp_md); + EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); + EVP_DigestFinal(&md, digest, NULL); + + buffer_free(&b); + +#ifdef DEBUG_KEX + dump_digest("hash", digest, evp_md->md_size); +#endif + return digest; +} + +/* client */ + +void +kexdh_client(Kex *kex) +{ + State *state; + + dispatch_set(SSH2_MSG_KEXDH_REPLY, &kexdh_input_reply); + + state = xmalloc(sizeof(State)); + kex->state = state; + + /* generate and send 'e', client DH public key */ + state->dh = dh_new_group1(); + dh_gen_key(state->dh, kex->we_need * 8); + packet_start(SSH2_MSG_KEXDH_INIT); + packet_put_bignum2(state->dh->pub_key); + packet_send(); + + debug("SSH2_MSG_KEXDH_INIT sent"); +#ifdef DEBUG_KEXDH + DHparams_print_fp(stderr, state->dh); + fprintf(stderr, "pub= "); + BN_print_fp(stderr, state->dh->pub_key); + fprintf(stderr, "\n"); +#endif +} + +void +kexdh_input_reply(int type, int plen, void *ctxt) +{ + BIGNUM *dh_server_pub = NULL, *shared_secret = NULL; + Key *server_host_key; + char *server_host_key_blob = NULL, *signature = NULL; + u_char *kbuf, *hash; + u_int klen, kout, slen, sbloblen; + int dlen; + Kex *kex = (Kex *)ctxt; + State *state = (State *)kex->state; + + debug("SSH2_MSG_KEXDH_REPLY received"); + dispatch_set(SSH2_MSG_KEXDH_REPLY, &kex_protocol_error); + + /* key, cert */ + server_host_key_blob = packet_get_string(&sbloblen); + server_host_key = key_from_blob(server_host_key_blob, sbloblen); + if (server_host_key == NULL) + fatal("cannot decode server_host_key_blob"); + + if (kex->check_host_key == NULL) + fatal("cannot check server_host_key"); + kex->check_host_key(server_host_key); + + /* DH paramter f, server public DH key */ + dh_server_pub = BN_new(); + if (dh_server_pub == NULL) + fatal("dh_server_pub == NULL"); + packet_get_bignum2(dh_server_pub, &dlen); + +#ifdef DEBUG_KEXDH + fprintf(stderr, "dh_server_pub= "); + BN_print_fp(stderr, dh_server_pub); + fprintf(stderr, "\n"); + debug("bits %d", BN_num_bits(dh_server_pub)); +#endif + + /* signed H */ + signature = packet_get_string(&slen); + packet_done(); + + if (!dh_pub_is_valid(state->dh, dh_server_pub)) + packet_disconnect("bad server public DH value"); + + klen = DH_size(state->dh); + kbuf = xmalloc(klen); + kout = DH_compute_key(kbuf, dh_server_pub, state->dh); +#ifdef DEBUG_KEXDH + dump_digest("shared secret", kbuf, kout); +#endif + shared_secret = BN_new(); + BN_bin2bn(kbuf, kout, shared_secret); + memset(kbuf, 0, klen); + xfree(kbuf); + + /* calc and verify H */ + hash = kex_dh_hash( + kex->client_version_string, + kex->server_version_string, + buffer_ptr(&kex->my), buffer_len(&kex->my), + buffer_ptr(&kex->peer), buffer_len(&kex->peer), + server_host_key_blob, sbloblen, + state->dh->pub_key, + dh_server_pub, + shared_secret + ); + xfree(server_host_key_blob); + DH_free(state->dh); + BN_free(dh_server_pub); + + if (key_verify(server_host_key, (u_char *)signature, slen, hash, 20) != 1) + fatal("key_verify failed for server_host_key"); + key_free(server_host_key); + xfree(signature); + + kex_derive_keys(kex, hash, shared_secret); + BN_clear_free(shared_secret); + packet_set_kex(kex); + kex_send_newkeys(); + + /* save session id */ + session_id2_len = 20; + session_id2 = xmalloc(session_id2_len); + memcpy(session_id2, hash, session_id2_len); + + xfree(state); + kex->state = NULL; +} + +/* server */ + +void +kexdh_server(Kex *kex) +{ + State *state; + + dispatch_set(SSH2_MSG_KEXDH_INIT, &kexdh_input_init); + + state = xmalloc(sizeof(*state)); + kex->state = state; + + /* generate server DH public key */ + state->dh = dh_new_group1(); + dh_gen_key(state->dh, kex->we_need * 8); +} + +void +kexdh_input_init(int type, int plen, void *ctxt) +{ + BIGNUM *shared_secret = NULL, *dh_client_pub = NULL; + Key *server_host_key; + u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL; + u_int sbloblen, klen, kout; + int dlen, slen; + Kex *kex = (Kex*) ctxt; + State *state = (State*) kex->state; + DH *dh = state->dh; + + debug("SSH2_MSG_KEXDH_INIT received"); + dispatch_set(SSH2_MSG_KEXDH_INIT, &kex_protocol_error); + + if (kex->load_host_key == NULL) + fatal("Cannot load hostkey"); + server_host_key = kex->load_host_key(kex->hostkey_type); + if (server_host_key == NULL) + fatal("Unsupported hostkey type %d", kex->hostkey_type); + + /* key, cert */ + dh_client_pub = BN_new(); + if (dh_client_pub == NULL) + fatal("dh_client_pub == NULL"); + packet_get_bignum2(dh_client_pub, &dlen); + +#ifdef DEBUG_KEXDH + fprintf(stderr, "dh_client_pub= "); + BN_print_fp(stderr, dh_client_pub); + fprintf(stderr, "\n"); + debug("bits %d", BN_num_bits(dh_client_pub)); +#endif + +#ifdef DEBUG_KEXDH + DHparams_print_fp(stderr, dh); + fprintf(stderr, "pub= "); + BN_print_fp(stderr, dh->pub_key); + fprintf(stderr, "\n"); +#endif + if (!dh_pub_is_valid(dh, dh_client_pub)) + packet_disconnect("bad client public DH value"); + + klen = DH_size(dh); + kbuf = xmalloc(klen); + kout = DH_compute_key(kbuf, dh_client_pub, dh); +#ifdef DEBUG_KEXDH + dump_digest("shared secret", kbuf, kout); +#endif + shared_secret = BN_new(); + BN_bin2bn(kbuf, kout, shared_secret); + memset(kbuf, 0, klen); + xfree(kbuf); + + key_to_blob(server_host_key, &server_host_key_blob, &sbloblen); + + /* calc H */ + hash = kex_dh_hash( + kex->client_version_string, + kex->server_version_string, + buffer_ptr(&kex->peer), buffer_len(&kex->peer), + buffer_ptr(&kex->my), buffer_len(&kex->my), + (char *)server_host_key_blob, sbloblen, + dh_client_pub, + dh->pub_key, + shared_secret + ); + BN_free(dh_client_pub); + + /* save session id := H */ + /* XXX hashlen depends on KEX */ + session_id2_len = 20; + session_id2 = xmalloc(session_id2_len); + memcpy(session_id2, hash, session_id2_len); + + /* sign H */ + /* XXX hashlen depends on KEX */ + key_sign(server_host_key, &signature, &slen, hash, 20); + + /* destroy_sensitive_data(); */ + + /* send server hostkey, DH pubkey 'f' and singed H */ + packet_start(SSH2_MSG_KEXDH_REPLY); + packet_put_string((char *)server_host_key_blob, sbloblen); + packet_put_bignum2(dh->pub_key); /* f */ + packet_put_string((char *)signature, slen); + packet_send(); + xfree(signature); + xfree(server_host_key_blob); + + kex_derive_keys(kex, hash, shared_secret); + BN_clear_free(shared_secret); + packet_set_kex(kex); + kex_send_newkeys(); + + /* have keys, free DH */ + DH_free(dh); + xfree(state); + kex->state = NULL; +} + +void +kexdh(Kex *kex) +{ + if (kex->server) + kexdh_server(kex); + else + kexdh_client(kex); +} diff --git a/usr.bin/ssh/kexgex.c b/usr.bin/ssh/kexgex.c new file mode 100644 index 00000000000..43fa8b9b7e2 --- /dev/null +++ b/usr.bin/ssh/kexgex.c @@ -0,0 +1,480 @@ +/* + * Copyright (c) 2000 Niels Provos. All rights reserved. + * Copyright (c) 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 + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "includes.h" +RCSID("$OpenBSD: kexgex.c,v 1.1 2001/04/03 19:53:29 markus Exp $"); + +#include <openssl/bn.h> + +#include "xmalloc.h" +#include "buffer.h" +#include "bufaux.h" +#include "key.h" +#include "kex.h" +#include "log.h" +#include "dispatch.h" +#include "packet.h" +#include "dh.h" +#include "ssh2.h" +#include "compat.h" + +extern u_char *session_id2; +extern int session_id2_len; + +typedef struct State State; +struct State { + DH *dh; + int min, nbits, max; +}; + +dispatch_fn kexgex_input_request; /* C -> S */ +dispatch_fn kexgex_input_group; /* S -> C */ +dispatch_fn kexgex_input_init; /* C -> S */ +dispatch_fn kexgex_input_reply; /* S -> C */ + +u_char * +kexgex_hash( + char *client_version_string, + char *server_version_string, + char *ckexinit, int ckexinitlen, + char *skexinit, int skexinitlen, + char *serverhostkeyblob, int sbloblen, + int min, int wantbits, int max, BIGNUM *prime, BIGNUM *gen, + BIGNUM *client_dh_pub, + BIGNUM *server_dh_pub, + BIGNUM *shared_secret) +{ + Buffer b; + static u_char digest[EVP_MAX_MD_SIZE]; + EVP_MD *evp_md = EVP_sha1(); + EVP_MD_CTX md; + + buffer_init(&b); + buffer_put_string(&b, client_version_string, strlen(client_version_string)); + buffer_put_string(&b, server_version_string, strlen(server_version_string)); + + /* kexinit messages: fake header: len+SSH2_MSG_KEXINIT */ + buffer_put_int(&b, ckexinitlen+1); + buffer_put_char(&b, SSH2_MSG_KEXINIT); + buffer_append(&b, ckexinit, ckexinitlen); + buffer_put_int(&b, skexinitlen+1); + buffer_put_char(&b, SSH2_MSG_KEXINIT); + buffer_append(&b, skexinit, skexinitlen); + + buffer_put_string(&b, serverhostkeyblob, sbloblen); + if (min == -1 || max == -1) + buffer_put_int(&b, wantbits); + else { + buffer_put_int(&b, min); + buffer_put_int(&b, wantbits); + buffer_put_int(&b, max); + } + buffer_put_bignum2(&b, prime); + buffer_put_bignum2(&b, gen); + buffer_put_bignum2(&b, client_dh_pub); + buffer_put_bignum2(&b, server_dh_pub); + buffer_put_bignum2(&b, shared_secret); + +#ifdef DEBUG_KEXDH + buffer_dump(&b); +#endif + EVP_DigestInit(&md, evp_md); + EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); + EVP_DigestFinal(&md, digest, NULL); + + buffer_free(&b); + +#ifdef DEBUG_KEXDH + dump_digest("hash", digest, evp_md->md_size); +#endif + return digest; +} + +/* client */ + +void +kexgex_client(Kex *kex) +{ + State *state; + + dispatch_set(SSH2_MSG_KEX_DH_GEX_GROUP, &kexgex_input_group); + + state = xmalloc(sizeof(*state)); + state->nbits = dh_estimate(kex->we_need * 8); + kex->state = state; + + if (datafellows & SSH_OLD_DHGEX) { + debug("SSH2_MSG_KEX_DH_GEX_REQUEST_OLD sent"); + + /* Old GEX request */ + packet_start(SSH2_MSG_KEX_DH_GEX_REQUEST_OLD); + packet_put_int(state->nbits); + state->min = DH_GRP_MIN; + state->max = DH_GRP_MAX; + } else { + debug("SSH2_MSG_KEX_DH_GEX_REQUEST sent"); + + /* New GEX request */ + state->min = DH_GRP_MIN; + state->max = DH_GRP_MAX; + packet_start(SSH2_MSG_KEX_DH_GEX_REQUEST); + packet_put_int(state->min); + packet_put_int(state->nbits); + packet_put_int(state->max); + } +#ifdef DEBUG_KEXDH + fprintf(stderr, "\nmin = %d, nbits = %d, max = %d\n", + state->min, state->nbits, state->max); +#endif + packet_send(); +} + +void +kexgex_input_group(int type, int plen, void *ctxt) +{ + DH *dh; + int dlen; + BIGNUM *p = 0, *g = 0; + Kex *kex = (Kex*) ctxt; + State *state = (State *) kex->state; + + debug("SSH2_MSG_KEX_DH_GEX_GROUP receivied"); + dispatch_set(SSH2_MSG_KEX_DH_GEX_GROUP, &kex_protocol_error); + dispatch_set(SSH2_MSG_KEX_DH_GEX_REPLY, &kexgex_input_reply); + + if ((p = BN_new()) == NULL) + fatal("BN_new"); + packet_get_bignum2(p, &dlen); + if ((g = BN_new()) == NULL) + fatal("BN_new"); + packet_get_bignum2(g, &dlen); + + if (BN_num_bits(p) < state->min || BN_num_bits(p) > state->max) + fatal("DH_GEX group out of range: %d !< %d !< %d", + state->min, BN_num_bits(p), state->max); + + dh = dh_new_group(g, p); + dh_gen_key(dh, kex->we_need * 8); + + state->dh = dh; + +#ifdef DEBUG_KEXDH + DHparams_print_fp(stderr, dh); + fprintf(stderr, "pub= "); + BN_print_fp(stderr, dh->pub_key); + fprintf(stderr, "\n"); +#endif + + debug("SSH2_MSG_KEX_DH_GEX_INIT sent"); + /* generate and send 'e', client DH public key */ + packet_start(SSH2_MSG_KEX_DH_GEX_INIT); + packet_put_bignum2(dh->pub_key); + packet_send(); +} + +void +kexgex_input_reply(int type, int plen, void *ctxt) +{ + BIGNUM *dh_server_pub = NULL, *shared_secret = NULL; + Key *server_host_key; + Kex *kex = (Kex*) ctxt; + State *state = (State *) kex->state; + DH *dh = state->dh; + u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL; + u_int klen, kout, slen, sbloblen; + int dlen, min, max; + + debug("SSH2_MSG_KEX_DH_GEX_REPLY received"); + dispatch_set(SSH2_MSG_KEX_DH_GEX_REPLY, &kex_protocol_error); + + /* key, cert */ + server_host_key_blob = packet_get_string(&sbloblen); + server_host_key = key_from_blob(server_host_key_blob, sbloblen); + if (server_host_key == NULL) + fatal("cannot decode server_host_key_blob"); + + if (kex->check_host_key == NULL) + fatal("cannot check server_host_key"); + kex->check_host_key(server_host_key); + + /* DH paramter f, server public DH key */ + dh_server_pub = BN_new(); + if (dh_server_pub == NULL) + fatal("dh_server_pub == NULL"); + packet_get_bignum2(dh_server_pub, &dlen); + +#ifdef DEBUG_KEXDH + fprintf(stderr, "dh_server_pub= "); + BN_print_fp(stderr, dh_server_pub); + fprintf(stderr, "\n"); + debug("bits %d", BN_num_bits(dh_server_pub)); +#endif + + /* signed H */ + signature = packet_get_string(&slen); + packet_done(); + + if (!dh_pub_is_valid(dh, dh_server_pub)) + packet_disconnect("bad server public DH value"); + + klen = DH_size(dh); + kbuf = xmalloc(klen); + kout = DH_compute_key(kbuf, dh_server_pub, dh); +#ifdef DEBUG_KEXDH + dump_digest("shared secret", kbuf, kout); +#endif + shared_secret = BN_new(); + BN_bin2bn(kbuf, kout, shared_secret); + memset(kbuf, 0, klen); + xfree(kbuf); + + if (datafellows & SSH_OLD_DHGEX) { + min = max = -1; + } else { + min = state->min; + max = state->max; + } + + /* calc and verify H */ + hash = kexgex_hash( + kex->client_version_string, + kex->server_version_string, + buffer_ptr(&kex->my), buffer_len(&kex->my), + buffer_ptr(&kex->peer), buffer_len(&kex->peer), + server_host_key_blob, sbloblen, + min, state->nbits, max, + dh->p, dh->g, + dh->pub_key, + dh_server_pub, + shared_secret + ); + xfree(server_host_key_blob); + BN_free(dh_server_pub); + + if (key_verify(server_host_key, (u_char *)signature, slen, hash, 20) != 1) + fatal("key_verify failed for server_host_key"); + key_free(server_host_key); + xfree(signature); + + kex_derive_keys(kex, hash, shared_secret); + BN_clear_free(shared_secret); + packet_set_kex(kex); + + /* save session id */ + session_id2_len = 20; + session_id2 = xmalloc(session_id2_len); + memcpy(session_id2, hash, session_id2_len); + + kex_send_newkeys(); + + /* have keys, free DH */ + DH_free(dh); + xfree(state); + kex->state = NULL; +} + +/* server */ + +void +kexgex_server(Kex *kex) +{ + State *state; + + state = xmalloc(sizeof(*state)); + kex->state = state; + + dispatch_set(SSH2_MSG_KEX_DH_GEX_REQUEST, &kexgex_input_request); + dispatch_set(SSH2_MSG_KEX_DH_GEX_REQUEST_OLD, &kexgex_input_request); +} + +void +kexgex_input_request(int type, int plen, void *ctxt) +{ + Kex *kex = (Kex*) ctxt; + State *state = (State *) kex->state; + int min = -1, max = -1; + + dispatch_set(SSH2_MSG_KEX_DH_GEX_REQUEST, &kex_protocol_error); + dispatch_set(SSH2_MSG_KEX_DH_GEX_REQUEST_OLD, &kex_protocol_error); + dispatch_set(SSH2_MSG_KEX_DH_GEX_INIT, &kexgex_input_init); + + switch(type){ + case SSH2_MSG_KEX_DH_GEX_REQUEST: + debug("SSH2_MSG_KEX_DH_GEX_REQUEST received"); + min = packet_get_int(); + state->nbits = packet_get_int(); + max = packet_get_int(); + min = MAX(DH_GRP_MIN, min); + max = MIN(DH_GRP_MAX, max); + state->min = min; + state->max = max; + break; + case SSH2_MSG_KEX_DH_GEX_REQUEST_OLD: + debug("SSH2_MSG_KEX_DH_GEX_REQUEST_OLD received"); + state->nbits = packet_get_int(); + min = DH_GRP_MIN; + max = DH_GRP_MAX; + /* unused for old GEX */ + state->min = -1; + state->max = -1; + break; + } + packet_done(); + + if (max < min || state->nbits < min || max < state->nbits) + fatal("DH_GEX_REQUEST, bad parameters: %d !< %d !< %d", + min, state->nbits, max); + + state->dh = choose_dh(min, state->nbits, max); + if (state->dh == NULL) + packet_disconnect("Protocol error: no matching DH grp found"); + + debug("SSH2_MSG_KEX_DH_GEX_GROUP sent"); + packet_start(SSH2_MSG_KEX_DH_GEX_GROUP); + packet_put_bignum2(state->dh->p); + packet_put_bignum2(state->dh->g); + packet_send(); + + /* flush */ + packet_write_wait(); + + /* Compute our exchange value in parallel with the client */ + dh_gen_key(state->dh, kex->we_need * 8); +} + +void +kexgex_input_init(int type, int plen, void *ctxt) +{ + BIGNUM *shared_secret = NULL, *dh_client_pub = NULL; + Key *server_host_key; + Kex *kex = (Kex*) ctxt; + State *state = (State *) kex->state; + DH *dh = state->dh; + u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL; + u_int sbloblen, klen, kout; + int dlen, slen; + + if (kex->load_host_key == NULL) + fatal("Cannot load hostkey"); + server_host_key = kex->load_host_key(kex->hostkey_type); + if (server_host_key == NULL) + fatal("Unsupported hostkey type %d", kex->hostkey_type); + + dispatch_set(SSH2_MSG_KEX_DH_GEX_INIT, &kex_protocol_error); + debug("SSH2_MSG_KEX_DH_GEX_INIT received"); + + /* key, cert */ + dh_client_pub = BN_new(); + if (dh_client_pub == NULL) + fatal("dh_client_pub == NULL"); + packet_get_bignum2(dh_client_pub, &dlen); + +#ifdef DEBUG_KEXDH + fprintf(stderr, "dh_client_pub= "); + BN_print_fp(stderr, dh_client_pub); + fprintf(stderr, "\n"); + debug("bits %d", BN_num_bits(dh_client_pub)); +#endif + +#ifdef DEBUG_KEXDH + DHparams_print_fp(stderr, dh); + fprintf(stderr, "pub= "); + BN_print_fp(stderr, dh->pub_key); + fprintf(stderr, "\n"); +#endif + if (!dh_pub_is_valid(dh, dh_client_pub)) + packet_disconnect("bad client public DH value"); + + klen = DH_size(dh); + kbuf = xmalloc(klen); + kout = DH_compute_key(kbuf, dh_client_pub, dh); +#ifdef DEBUG_KEXDH + dump_digest("shared secret", kbuf, kout); +#endif + shared_secret = BN_new(); + BN_bin2bn(kbuf, kout, shared_secret); + memset(kbuf, 0, klen); + xfree(kbuf); + + key_to_blob(server_host_key, &server_host_key_blob, &sbloblen); + + /* calc H */ /* XXX depends on 'kex' */ + hash = kexgex_hash( + kex->client_version_string, + kex->server_version_string, + buffer_ptr(&kex->peer), buffer_len(&kex->peer), + buffer_ptr(&kex->my), buffer_len(&kex->my), + (char *)server_host_key_blob, sbloblen, + state->min, state->nbits, state->max, + dh->p, dh->g, + dh_client_pub, + dh->pub_key, + shared_secret + ); + BN_free(dh_client_pub); + + /* save session id := H */ + /* XXX hashlen depends on KEX */ + session_id2_len = 20; + session_id2 = xmalloc(session_id2_len); + memcpy(session_id2, hash, session_id2_len); + + /* sign H */ + /* XXX hashlen depends on KEX */ + key_sign(server_host_key, &signature, &slen, hash, 20); + + /* destroy_sensitive_data(); */ + + /* send server hostkey, DH pubkey 'f' and singed H */ + debug("SSH2_MSG_KEX_DH_GEX_REPLY sent"); + packet_start(SSH2_MSG_KEX_DH_GEX_REPLY); + packet_put_string((char *)server_host_key_blob, sbloblen); + packet_put_bignum2(dh->pub_key); /* f */ + packet_put_string((char *)signature, slen); + packet_send(); + xfree(signature); + xfree(server_host_key_blob); + /* packet_write_wait(); */ + + kex_derive_keys(kex, hash, shared_secret); + BN_clear_free(shared_secret); + packet_set_kex(kex); + + kex_send_newkeys(); + + /* have keys, free DH */ + DH_free(dh); + xfree(state); + kex->state = NULL; +} + +void +kexgex(Kex *kex) +{ + if (kex->server) + kexgex_server(kex); + else + kexgex_client(kex); +} diff --git a/usr.bin/ssh/lib/Makefile b/usr.bin/ssh/lib/Makefile index ac05712d255..fbcd66f59ad 100644 --- a/usr.bin/ssh/lib/Makefile +++ b/usr.bin/ssh/lib/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.21 2001/03/29 21:17:40 markus Exp $ +# $OpenBSD: Makefile,v 1.22 2001/04/03 19:53:30 markus Exp $ .PATH: ${.CURDIR}/.. @@ -8,7 +8,7 @@ SRCS= authfd.c authfile.c bufaux.c buffer.c canohost.c channels.c \ hostfile.c log.c match.c mpaux.c nchan.c packet.c readpass.c \ rsa.c tildexpand.c ttymodes.c uidswap.c xmalloc.c atomicio.c \ key.c dispatch.c kex.c mac.c uuencode.c misc.c \ - cli.c rijndael.c ssh-dss.c ssh-rsa.c dh.c + cli.c rijndael.c ssh-dss.c ssh-rsa.c dh.c kexdh.c kexgex.c NOPROFILE= yes NOPIC= yes diff --git a/usr.bin/ssh/sshconnect2.c b/usr.bin/ssh/sshconnect2.c index 460d614f049..4ed39a23ee6 100644 --- a/usr.bin/ssh/sshconnect2.c +++ b/usr.bin/ssh/sshconnect2.c @@ -23,7 +23,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: sshconnect2.c,v 1.60 2001/03/29 21:06:21 stevesk Exp $"); +RCSID("$OpenBSD: sshconnect2.c,v 1.61 2001/04/03 19:53:29 markus Exp $"); #include <openssl/bn.h> #include <openssl/md5.h> @@ -47,15 +47,12 @@ RCSID("$OpenBSD: sshconnect2.c,v 1.60 2001/03/29 21:06:21 stevesk Exp $"); #include "authfile.h" #include "cli.h" #include "dh.h" -#include "dispatch.h" #include "authfd.h" #include "log.h" #include "readconf.h" #include "readpass.h" #include "match.h" - -void ssh_dh1_client(Kex *, char *, struct sockaddr *, Buffer *, Buffer *); -void ssh_dhgex_client(Kex *, char *, struct sockaddr *, Buffer *, Buffer *); +#include "dispatch.h" /* import */ extern char *client_version_string; @@ -69,13 +66,24 @@ extern Options options; u_char *session_id2 = NULL; int session_id2_len = 0; +char *xxx_host; +struct sockaddr *xxx_hostaddr; + +int +check_host_key_callback(Key *hostkey) +{ + check_host_key(xxx_host, xxx_hostaddr, hostkey, + options.user_hostfile2, options.system_hostfile2); + return 0; +} + void ssh_kex2(char *host, struct sockaddr *hostaddr) { - int i, plen; Kex *kex; - Buffer *client_kexinit, *server_kexinit; - char *sprop[PROPOSAL_MAX]; + + xxx_host = host; + xxx_hostaddr = hostaddr; if (options.ciphers == (char *)-1) { log("No valid ciphers for protocol version 2 given, using defaults."); @@ -101,46 +109,13 @@ ssh_kex2(char *host, struct sockaddr *hostaddr) myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs; } - /* buffers with raw kexinit messages */ - server_kexinit = xmalloc(sizeof(*server_kexinit)); - buffer_init(server_kexinit); - client_kexinit = kex_init(myproposal); - - /* algorithm negotiation */ - kex_exchange_kexinit(client_kexinit, server_kexinit, sprop); - kex = kex_choose_conf(myproposal, sprop, 0); - for (i = 0; i < PROPOSAL_MAX; i++) - xfree(sprop[i]); - - /* server authentication and session key agreement */ - switch(kex->kex_type) { - case DH_GRP1_SHA1: - ssh_dh1_client(kex, host, hostaddr, - client_kexinit, server_kexinit); - break; - case DH_GEX_SHA1: - ssh_dhgex_client(kex, host, hostaddr, client_kexinit, - server_kexinit); - break; - default: - fatal("Unsupported key exchange %d", kex->kex_type); - } - - buffer_free(client_kexinit); - buffer_free(server_kexinit); - xfree(client_kexinit); - xfree(server_kexinit); + kex = kex_start(myproposal); + kex->client_version_string=client_version_string; + kex->server_version_string=server_version_string; + kex->check_host_key=&check_host_key_callback; - debug("Wait SSH2_MSG_NEWKEYS."); - packet_read_expect(&plen, SSH2_MSG_NEWKEYS); - packet_done(); - debug("GOT SSH2_MSG_NEWKEYS."); - - debug("send SSH2_MSG_NEWKEYS."); - packet_start(SSH2_MSG_NEWKEYS); - packet_send(); - packet_write_wait(); - debug("done: send SSH2_MSG_NEWKEYS."); + /* start key exchange */ + dispatch_run(DISPATCH_BLOCK, &kex->newkeys, kex); #ifdef DEBUG_KEXDH /* send 1st encrypted/maced/compressed message */ @@ -149,339 +124,7 @@ ssh_kex2(char *host, struct sockaddr *hostaddr) packet_send(); packet_write_wait(); #endif - debug("done: KEX2."); -} - -/* diffie-hellman-group1-sha1 */ - -void -ssh_dh1_client(Kex *kex, char *host, struct sockaddr *hostaddr, - Buffer *client_kexinit, Buffer *server_kexinit) -{ -#ifdef DEBUG_KEXDH - int i; -#endif - int plen, dlen; - u_int klen, kout; - char *signature = NULL; - u_int slen; - char *server_host_key_blob = NULL; - Key *server_host_key; - u_int sbloblen; - DH *dh; - BIGNUM *dh_server_pub = 0; - BIGNUM *shared_secret = 0; - u_char *kbuf; - u_char *hash; - - debug("Sending SSH2_MSG_KEXDH_INIT."); - /* generate and send 'e', client DH public key */ - dh = dh_new_group1(); - dh_gen_key(dh, kex->we_need * 8); - packet_start(SSH2_MSG_KEXDH_INIT); - packet_put_bignum2(dh->pub_key); - packet_send(); - packet_write_wait(); - -#ifdef DEBUG_KEXDH - fprintf(stderr, "\np= "); - BN_print_fp(stderr, dh->p); - fprintf(stderr, "\ng= "); - BN_print_fp(stderr, dh->g); - fprintf(stderr, "\npub= "); - BN_print_fp(stderr, dh->pub_key); - fprintf(stderr, "\n"); - DHparams_print_fp(stderr, dh); -#endif - - debug("Wait SSH2_MSG_KEXDH_REPLY."); - - packet_read_expect(&plen, SSH2_MSG_KEXDH_REPLY); - - debug("Got SSH2_MSG_KEXDH_REPLY."); - - /* key, cert */ - server_host_key_blob = packet_get_string(&sbloblen); - server_host_key = key_from_blob(server_host_key_blob, sbloblen); - if (server_host_key == NULL) - fatal("cannot decode server_host_key_blob"); - - check_host_key(host, hostaddr, server_host_key, - options.user_hostfile2, options.system_hostfile2); - - /* DH paramter f, server public DH key */ - dh_server_pub = BN_new(); - if (dh_server_pub == NULL) - fatal("dh_server_pub == NULL"); - packet_get_bignum2(dh_server_pub, &dlen); - -#ifdef DEBUG_KEXDH - fprintf(stderr, "\ndh_server_pub= "); - BN_print_fp(stderr, dh_server_pub); - fprintf(stderr, "\n"); - debug("bits %d", BN_num_bits(dh_server_pub)); -#endif - - /* signed H */ - signature = packet_get_string(&slen); - packet_done(); - - if (!dh_pub_is_valid(dh, dh_server_pub)) - packet_disconnect("bad server public DH value"); - - klen = DH_size(dh); - kbuf = xmalloc(klen); - kout = DH_compute_key(kbuf, dh_server_pub, dh); -#ifdef DEBUG_KEXDH - debug("shared secret: len %d/%d", klen, kout); - fprintf(stderr, "shared secret == "); - for (i = 0; i< kout; i++) - fprintf(stderr, "%02x", (kbuf[i])&0xff); - fprintf(stderr, "\n"); -#endif - shared_secret = BN_new(); - - BN_bin2bn(kbuf, kout, shared_secret); - memset(kbuf, 0, klen); - xfree(kbuf); - - /* calc and verify H */ - hash = kex_hash( - client_version_string, - server_version_string, - buffer_ptr(client_kexinit), buffer_len(client_kexinit), - buffer_ptr(server_kexinit), buffer_len(server_kexinit), - server_host_key_blob, sbloblen, - dh->pub_key, - dh_server_pub, - shared_secret - ); - xfree(server_host_key_blob); - DH_free(dh); - BN_free(dh_server_pub); -#ifdef DEBUG_KEXDH - fprintf(stderr, "hash == "); - for (i = 0; i< 20; i++) - fprintf(stderr, "%02x", (hash[i])&0xff); - fprintf(stderr, "\n"); -#endif - if (key_verify(server_host_key, (u_char *)signature, slen, hash, 20) != 1) - fatal("key_verify failed for server_host_key"); - key_free(server_host_key); - xfree(signature); - - kex_derive_keys(kex, hash, shared_secret); - BN_clear_free(shared_secret); - packet_set_kex(kex); - - /* save session id */ - session_id2_len = 20; - session_id2 = xmalloc(session_id2_len); - memcpy(session_id2, hash, session_id2_len); -} - -/* diffie-hellman-group-exchange-sha1 */ - -/* - * Estimates the group order for a Diffie-Hellman group that has an - * attack complexity approximately the same as O(2**bits). Estimate - * with: O(exp(1.9223 * (ln q)^(1/3) (ln ln q)^(2/3))) - */ - -int -dh_estimate(int bits) -{ - - if (bits < 64) - return (512); /* O(2**63) */ - if (bits < 128) - return (1024); /* O(2**86) */ - if (bits < 192) - return (2048); /* O(2**116) */ - return (4096); /* O(2**156) */ -} - -void -ssh_dhgex_client(Kex *kex, char *host, struct sockaddr *hostaddr, - Buffer *client_kexinit, Buffer *server_kexinit) -{ -#ifdef DEBUG_KEXDH - int i; -#endif - int plen, dlen; - u_int klen, kout; - char *signature = NULL; - u_int slen, nbits, min, max; - char *server_host_key_blob = NULL; - Key *server_host_key; - u_int sbloblen; - DH *dh; - BIGNUM *dh_server_pub = 0; - BIGNUM *shared_secret = 0; - BIGNUM *p = 0, *g = 0; - u_char *kbuf; - u_char *hash; - - nbits = dh_estimate(kex->we_need * 8); - - if (datafellows & SSH_OLD_DHGEX) { - debug("Sending SSH2_MSG_KEX_DH_GEX_REQUEST_OLD."); - - /* Old GEX request */ - packet_start(SSH2_MSG_KEX_DH_GEX_REQUEST_OLD); - packet_put_int(nbits); - min = DH_GRP_MIN; - max = DH_GRP_MAX; - } else { - debug("Sending SSH2_MSG_KEX_DH_GEX_REQUEST."); - - /* New GEX request */ - min = DH_GRP_MIN; - max = DH_GRP_MAX; - - packet_start(SSH2_MSG_KEX_DH_GEX_REQUEST); - packet_put_int(min); - packet_put_int(nbits); - packet_put_int(max); - } - packet_send(); - packet_write_wait(); - -#ifdef DEBUG_KEXDH - fprintf(stderr, "\nmin = %d, nbits = %d, max = %d", min, nbits, max); -#endif - - debug("Wait SSH2_MSG_KEX_DH_GEX_GROUP."); - - packet_read_expect(&plen, SSH2_MSG_KEX_DH_GEX_GROUP); - - debug("Got SSH2_MSG_KEX_DH_GEX_GROUP."); - - if ((p = BN_new()) == NULL) - fatal("BN_new"); - packet_get_bignum2(p, &dlen); - if ((g = BN_new()) == NULL) - fatal("BN_new"); - packet_get_bignum2(g, &dlen); - - if (BN_num_bits(p) < min || BN_num_bits(p) > max) - fatal("DH_GEX group out of range: %d !< %d !< %d", - min, BN_num_bits(p), max); - - dh = dh_new_group(g, p); - - dh_gen_key(dh, kex->we_need * 8); - -#ifdef DEBUG_KEXDH - fprintf(stderr, "\np= "); - BN_print_fp(stderr, dh->p); - fprintf(stderr, "\ng= "); - BN_print_fp(stderr, dh->g); - fprintf(stderr, "\npub= "); - BN_print_fp(stderr, dh->pub_key); - fprintf(stderr, "\n"); - DHparams_print_fp(stderr, dh); -#endif - - debug("Sending SSH2_MSG_KEX_DH_GEX_INIT."); - /* generate and send 'e', client DH public key */ - packet_start(SSH2_MSG_KEX_DH_GEX_INIT); - packet_put_bignum2(dh->pub_key); - packet_send(); - packet_write_wait(); - - debug("Wait SSH2_MSG_KEX_DH_GEX_REPLY."); - - packet_read_expect(&plen, SSH2_MSG_KEX_DH_GEX_REPLY); - - debug("Got SSH2_MSG_KEXDH_REPLY."); - - /* key, cert */ - server_host_key_blob = packet_get_string(&sbloblen); - server_host_key = key_from_blob(server_host_key_blob, sbloblen); - if (server_host_key == NULL) - fatal("cannot decode server_host_key_blob"); - - check_host_key(host, hostaddr, server_host_key, - options.user_hostfile2, options.system_hostfile2); - - /* DH paramter f, server public DH key */ - dh_server_pub = BN_new(); - if (dh_server_pub == NULL) - fatal("dh_server_pub == NULL"); - packet_get_bignum2(dh_server_pub, &dlen); - -#ifdef DEBUG_KEXDH - fprintf(stderr, "\ndh_server_pub= "); - BN_print_fp(stderr, dh_server_pub); - fprintf(stderr, "\n"); - debug("bits %d", BN_num_bits(dh_server_pub)); -#endif - - /* signed H */ - signature = packet_get_string(&slen); - packet_done(); - - if (!dh_pub_is_valid(dh, dh_server_pub)) - packet_disconnect("bad server public DH value"); - - klen = DH_size(dh); - kbuf = xmalloc(klen); - kout = DH_compute_key(kbuf, dh_server_pub, dh); -#ifdef DEBUG_KEXDH - debug("shared secret: len %d/%d", klen, kout); - fprintf(stderr, "shared secret == "); - for (i = 0; i< kout; i++) - fprintf(stderr, "%02x", (kbuf[i])&0xff); - fprintf(stderr, "\n"); -#endif - shared_secret = BN_new(); - - BN_bin2bn(kbuf, kout, shared_secret); - memset(kbuf, 0, klen); - xfree(kbuf); - - if (datafellows & SSH_OLD_DHGEX) { - /* These values are not included in the hash */ - min = -1; - max = -1; - } - - /* calc and verify H */ - hash = kex_hash_gex( - client_version_string, - server_version_string, - buffer_ptr(client_kexinit), buffer_len(client_kexinit), - buffer_ptr(server_kexinit), buffer_len(server_kexinit), - server_host_key_blob, sbloblen, - min, nbits, max, - dh->p, dh->g, - dh->pub_key, - dh_server_pub, - shared_secret - ); - xfree(server_host_key_blob); - DH_free(dh); - BN_free(dh_server_pub); -#ifdef DEBUG_KEXDH - fprintf(stderr, "hash == "); - for (i = 0; i< 20; i++) - fprintf(stderr, "%02x", (hash[i])&0xff); - fprintf(stderr, "\n"); -#endif - if (key_verify(server_host_key, (u_char *)signature, slen, hash, 20) != 1) - fatal("key_verify failed for server_host_key"); - key_free(server_host_key); - xfree(signature); - - kex_derive_keys(kex, hash, shared_secret); - BN_clear_free(shared_secret); - packet_set_kex(kex); - - /* save session id */ - session_id2_len = 20; - session_id2 = xmalloc(session_id2_len); - memcpy(session_id2, hash, session_id2_len); + debug("done: ssh_kex2."); } /* @@ -563,6 +206,7 @@ ssh_userauth2(const char *server_user, char *host) Authctxt authctxt; int type; int plen; + int i; if (options.challenge_reponse_authentication) options.kbd_interactive_authentication = 1; @@ -603,7 +247,10 @@ ssh_userauth2(const char *server_user, char *host) /* initial userauth request */ userauth_none(&authctxt); - dispatch_init(&input_userauth_error); + //dispatch_init(&input_userauth_error); + for (i = 50; i <= 254; i++) { + dispatch_set(i, &input_userauth_error); + } dispatch_set(SSH2_MSG_USERAUTH_SUCCESS, &input_userauth_success); dispatch_set(SSH2_MSG_USERAUTH_FAILURE, &input_userauth_failure); dispatch_set(SSH2_MSG_USERAUTH_BANNER, &input_userauth_banner); diff --git a/usr.bin/ssh/sshd.c b/usr.bin/ssh/sshd.c index de58d0cffe0..e7a5755fdf0 100644 --- a/usr.bin/ssh/sshd.c +++ b/usr.bin/ssh/sshd.c @@ -40,7 +40,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: sshd.c,v 1.185 2001/03/29 23:42:01 djm Exp $"); +RCSID("$OpenBSD: sshd.c,v 1.186 2001/04/03 19:53:29 markus Exp $"); #include <openssl/dh.h> #include <openssl/bn.h> @@ -70,6 +70,7 @@ RCSID("$OpenBSD: sshd.c,v 1.185 2001/03/29 23:42:01 djm Exp $"); #include "canohost.h" #include "auth.h" #include "misc.h" +#include "dispatch.h" #ifdef LIBWRAP #include <tcpd.h> @@ -1381,14 +1382,7 @@ do_ssh1_kex(void) void do_ssh2_kex(void) { - Buffer *server_kexinit; - Buffer *client_kexinit; - int payload_len; - int i; Kex *kex; - char *cprop[PROPOSAL_MAX]; - -/* KEXINIT */ if (options.ciphers != NULL) { myproposal[PROPOSAL_ENC_ALGS_CTOS] = @@ -1405,36 +1399,14 @@ do_ssh2_kex(void) } myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = list_hostkey_types(); - server_kexinit = kex_init(myproposal); - client_kexinit = xmalloc(sizeof(*client_kexinit)); - buffer_init(client_kexinit); - - /* algorithm negotiation */ - kex_exchange_kexinit(server_kexinit, client_kexinit, cprop); - kex = kex_choose_conf(cprop, myproposal, 1); - for (i = 0; i < PROPOSAL_MAX; i++) - xfree(cprop[i]); - - switch (kex->kex_type) { - case DH_GRP1_SHA1: - ssh_dh1_server(kex, client_kexinit, server_kexinit); - break; - case DH_GEX_SHA1: - ssh_dhgex_server(kex, client_kexinit, server_kexinit); - break; - default: - fatal("Unsupported key exchange %d", kex->kex_type); - } - - debug("send SSH2_MSG_NEWKEYS."); - packet_start(SSH2_MSG_NEWKEYS); - packet_send(); - packet_write_wait(); - debug("done: send SSH2_MSG_NEWKEYS."); + kex = kex_start(myproposal); + kex->server = 1; + kex->client_version_string=client_version_string; + kex->server_version_string=server_version_string; + kex->load_host_key=&get_hostkey_by_type; - debug("Wait SSH2_MSG_NEWKEYS."); - packet_read_expect(&payload_len, SSH2_MSG_NEWKEYS); - debug("GOT SSH2_MSG_NEWKEYS."); + /* start key exchange */ + dispatch_run(DISPATCH_BLOCK, &kex->newkeys, kex); #ifdef DEBUG_KEXDH /* send 1st encrypted/maced/compressed message */ @@ -1443,316 +1415,5 @@ do_ssh2_kex(void) packet_send(); packet_write_wait(); #endif - - debug("done: KEX2."); -} - -/* - * SSH2 key exchange - */ - -/* diffie-hellman-group1-sha1 */ - -void -ssh_dh1_server(Kex *kex, Buffer *client_kexinit, Buffer *server_kexinit) -{ -#ifdef DEBUG_KEXDH - int i; -#endif - int payload_len, dlen; - int slen; - u_char *signature = NULL; - u_char *server_host_key_blob = NULL; - u_int sbloblen; - u_int klen, kout; - u_char *kbuf; - u_char *hash; - BIGNUM *shared_secret = 0; - DH *dh; - BIGNUM *dh_client_pub = 0; - Key *hostkey; - - hostkey = get_hostkey_by_type(kex->hostkey_type); - if (hostkey == NULL) - fatal("Unsupported hostkey type %d", kex->hostkey_type); - -/* KEXDH */ - /* generate DH key */ - dh = dh_new_group1(); /* XXX depends on 'kex' */ - dh_gen_key(dh, kex->we_need * 8); - - debug("Wait SSH2_MSG_KEXDH_INIT."); - packet_read_expect(&payload_len, SSH2_MSG_KEXDH_INIT); - - /* key, cert */ - dh_client_pub = BN_new(); - if (dh_client_pub == NULL) - fatal("dh_client_pub == NULL"); - packet_get_bignum2(dh_client_pub, &dlen); - -#ifdef DEBUG_KEXDH - fprintf(stderr, "\ndh_client_pub= "); - BN_print_fp(stderr, dh_client_pub); - fprintf(stderr, "\n"); - debug("bits %d", BN_num_bits(dh_client_pub)); -#endif - -#ifdef DEBUG_KEXDH - fprintf(stderr, "\np= "); - BN_print_fp(stderr, dh->p); - fprintf(stderr, "\ng= "); - bn_print(dh->g); - fprintf(stderr, "\npub= "); - BN_print_fp(stderr, dh->pub_key); - fprintf(stderr, "\n"); - DHparams_print_fp(stderr, dh); -#endif - if (!dh_pub_is_valid(dh, dh_client_pub)) - packet_disconnect("bad client public DH value"); - - klen = DH_size(dh); - kbuf = xmalloc(klen); - kout = DH_compute_key(kbuf, dh_client_pub, dh); - -#ifdef DEBUG_KEXDH - debug("shared secret: len %d/%d", klen, kout); - fprintf(stderr, "shared secret == "); - for (i = 0; i< kout; i++) - fprintf(stderr, "%02x", (kbuf[i])&0xff); - fprintf(stderr, "\n"); -#endif - shared_secret = BN_new(); - - BN_bin2bn(kbuf, kout, shared_secret); - memset(kbuf, 0, klen); - xfree(kbuf); - - /* XXX precompute? */ - key_to_blob(hostkey, &server_host_key_blob, &sbloblen); - - /* calc H */ /* XXX depends on 'kex' */ - hash = kex_hash( - client_version_string, - server_version_string, - buffer_ptr(client_kexinit), buffer_len(client_kexinit), - buffer_ptr(server_kexinit), buffer_len(server_kexinit), - (char *)server_host_key_blob, sbloblen, - dh_client_pub, - dh->pub_key, - shared_secret - ); - buffer_free(client_kexinit); - buffer_free(server_kexinit); - xfree(client_kexinit); - xfree(server_kexinit); - BN_free(dh_client_pub); -#ifdef DEBUG_KEXDH - fprintf(stderr, "hash == "); - for (i = 0; i< 20; i++) - fprintf(stderr, "%02x", (hash[i])&0xff); - fprintf(stderr, "\n"); -#endif - /* save session id := H */ - /* XXX hashlen depends on KEX */ - session_id2_len = 20; - session_id2 = xmalloc(session_id2_len); - memcpy(session_id2, hash, session_id2_len); - - /* sign H */ - /* XXX hashlen depends on KEX */ - key_sign(hostkey, &signature, &slen, hash, 20); - - destroy_sensitive_data(); - - /* send server hostkey, DH pubkey 'f' and singed H */ - packet_start(SSH2_MSG_KEXDH_REPLY); - packet_put_string((char *)server_host_key_blob, sbloblen); - packet_put_bignum2(dh->pub_key); /* f */ - packet_put_string((char *)signature, slen); - packet_send(); - xfree(signature); - xfree(server_host_key_blob); - packet_write_wait(); - - kex_derive_keys(kex, hash, shared_secret); - BN_clear_free(shared_secret); - packet_set_kex(kex); - - /* have keys, free DH */ - DH_free(dh); -} - -/* diffie-hellman-group-exchange-sha1 */ - -void -ssh_dhgex_server(Kex *kex, Buffer *client_kexinit, Buffer *server_kexinit) -{ -#ifdef DEBUG_KEXDH - int i; -#endif - int payload_len, dlen; - int slen, nbits, type, min, max; - u_char *signature = NULL; - u_char *server_host_key_blob = NULL; - u_int sbloblen; - u_int klen, kout; - u_char *kbuf; - u_char *hash; - BIGNUM *shared_secret = 0; - DH *dh; - BIGNUM *dh_client_pub = 0; - Key *hostkey; - - hostkey = get_hostkey_by_type(kex->hostkey_type); - if (hostkey == NULL) - fatal("Unsupported hostkey type %d", kex->hostkey_type); - -/* KEXDHGEX */ - debug("Wait SSH2_MSG_KEX_DH_GEX_REQUEST."); - type = packet_read(&payload_len); - if (type != SSH2_MSG_KEX_DH_GEX_REQUEST_OLD && - type != SSH2_MSG_KEX_DH_GEX_REQUEST) - packet_disconnect("Protocol error: expected type %d or %d, got %d", - SSH2_MSG_KEX_DH_GEX_REQUEST_OLD, - SSH2_MSG_KEX_DH_GEX_REQUEST, - type); - if (type == SSH2_MSG_KEX_DH_GEX_REQUEST_OLD) { - nbits = packet_get_int(); - min = DH_GRP_MIN; - max = DH_GRP_MAX; - } else { - min = packet_get_int(); - nbits = packet_get_int(); - max = packet_get_int(); - - min = MAX(DH_GRP_MIN, min); - max = MIN(DH_GRP_MAX, max); - } - - if (max < min || nbits < min || max < nbits) - fatal("DH_GEX_REQUEST, bad parameters: %d !< %d !< %d", - min, nbits, max); - - dh = choose_dh(min, nbits, max); - if (dh == NULL) - packet_disconnect("Protocol error: no matching DH grp found"); - - debug("Sending SSH2_MSG_KEX_DH_GEX_GROUP."); - packet_start(SSH2_MSG_KEX_DH_GEX_GROUP); - packet_put_bignum2(dh->p); - packet_put_bignum2(dh->g); - packet_send(); - packet_write_wait(); - - /* Compute our exchange value in parallel with the client */ - - dh_gen_key(dh, kex->we_need * 8); - - debug("Wait SSH2_MSG_KEX_DH_GEX_INIT."); - packet_read_expect(&payload_len, SSH2_MSG_KEX_DH_GEX_INIT); - - /* key, cert */ - dh_client_pub = BN_new(); - if (dh_client_pub == NULL) - fatal("dh_client_pub == NULL"); - packet_get_bignum2(dh_client_pub, &dlen); - -#ifdef DEBUG_KEXDH - fprintf(stderr, "\ndh_client_pub= "); - BN_print_fp(stderr, dh_client_pub); - fprintf(stderr, "\n"); - debug("bits %d", BN_num_bits(dh_client_pub)); -#endif - -#ifdef DEBUG_KEXDH - fprintf(stderr, "\np= "); - BN_print_fp(stderr, dh->p); - fprintf(stderr, "\ng= "); - bn_print(dh->g); - fprintf(stderr, "\npub= "); - BN_print_fp(stderr, dh->pub_key); - fprintf(stderr, "\n"); - DHparams_print_fp(stderr, dh); -#endif - if (!dh_pub_is_valid(dh, dh_client_pub)) - packet_disconnect("bad client public DH value"); - - klen = DH_size(dh); - kbuf = xmalloc(klen); - kout = DH_compute_key(kbuf, dh_client_pub, dh); - -#ifdef DEBUG_KEXDH - debug("shared secret: len %d/%d", klen, kout); - fprintf(stderr, "shared secret == "); - for (i = 0; i< kout; i++) - fprintf(stderr, "%02x", (kbuf[i])&0xff); - fprintf(stderr, "\n"); -#endif - shared_secret = BN_new(); - - BN_bin2bn(kbuf, kout, shared_secret); - memset(kbuf, 0, klen); - xfree(kbuf); - - /* XXX precompute? */ - key_to_blob(hostkey, &server_host_key_blob, &sbloblen); - - if (type == SSH2_MSG_KEX_DH_GEX_REQUEST_OLD) { - /* These values are not included in the hash */ - min = -1; - max = -1; - } - - /* calc H */ /* XXX depends on 'kex' */ - hash = kex_hash_gex( - client_version_string, - server_version_string, - buffer_ptr(client_kexinit), buffer_len(client_kexinit), - buffer_ptr(server_kexinit), buffer_len(server_kexinit), - (char *)server_host_key_blob, sbloblen, - min, nbits, max, - dh->p, dh->g, - dh_client_pub, - dh->pub_key, - shared_secret - ); - buffer_free(client_kexinit); - buffer_free(server_kexinit); - xfree(client_kexinit); - xfree(server_kexinit); - BN_free(dh_client_pub); -#ifdef DEBUG_KEXDH - fprintf(stderr, "hash == "); - for (i = 0; i< 20; i++) - fprintf(stderr, "%02x", (hash[i])&0xff); - fprintf(stderr, "\n"); -#endif - /* save session id := H */ - /* XXX hashlen depends on KEX */ - session_id2_len = 20; - session_id2 = xmalloc(session_id2_len); - memcpy(session_id2, hash, session_id2_len); - - /* sign H */ - /* XXX hashlen depends on KEX */ - key_sign(hostkey, &signature, &slen, hash, 20); - - destroy_sensitive_data(); - - /* send server hostkey, DH pubkey 'f' and singed H */ - packet_start(SSH2_MSG_KEX_DH_GEX_REPLY); - packet_put_string((char *)server_host_key_blob, sbloblen); - packet_put_bignum2(dh->pub_key); /* f */ - packet_put_string((char *)signature, slen); - packet_send(); - xfree(signature); - xfree(server_host_key_blob); - packet_write_wait(); - - kex_derive_keys(kex, hash, shared_secret); - BN_clear_free(shared_secret); - packet_set_kex(kex); - - /* have keys, free DH */ - DH_free(dh); + debug("KEX done"); } |