summaryrefslogtreecommitdiff
path: root/usr.bin
diff options
context:
space:
mode:
authorMarkus Friedl <markus@cvs.openbsd.org>2001-04-03 19:53:31 +0000
committerMarkus Friedl <markus@cvs.openbsd.org>2001-04-03 19:53:31 +0000
commit761025b6048dc983849b3b222a869aaa62696685 (patch)
tree79de704e176cacc5cc6f538cd00927ad7bb2250b /usr.bin
parent4b84b33636d9bab5adb97f749b1e7890faf73f19 (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.c21
-rw-r--r--usr.bin/ssh/dh.h4
-rw-r--r--usr.bin/ssh/kex.c386
-rw-r--r--usr.bin/ssh/kex.h58
-rw-r--r--usr.bin/ssh/kexdh.c344
-rw-r--r--usr.bin/ssh/kexgex.c480
-rw-r--r--usr.bin/ssh/lib/Makefile4
-rw-r--r--usr.bin/ssh/sshconnect2.c409
-rw-r--r--usr.bin/ssh/sshd.c359
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");
}