mirror of
https://github.com/PowerShell/openssh-portable.git
synced 2025-07-27 07:44:29 +02:00
- markus@cvs.openbsd.org 2001/04/03 19:53:29
[dh.c dh.h kex.c kex.h sshconnect2.c sshd.c] move kex to kex*.c, used dispatch_set() callbacks for kex. should make rekeying easier.
This commit is contained in:
parent
86ebcb6cf5
commit
20d7c7b02c
@ -6,6 +6,10 @@
|
|||||||
- stevesk@cvs.openbsd.org 2001/04/03 13:56:11
|
- stevesk@cvs.openbsd.org 2001/04/03 13:56:11
|
||||||
[sftp-glob.c ssh-agent.c ssh-keygen.c]
|
[sftp-glob.c ssh-agent.c ssh-keygen.c]
|
||||||
free() -> xfree()
|
free() -> xfree()
|
||||||
|
- markus@cvs.openbsd.org 2001/04/03 19:53:29
|
||||||
|
[dh.c dh.h kex.c kex.h sshconnect2.c sshd.c]
|
||||||
|
move kex to kex*.c, used dispatch_set() callbacks for kex. should
|
||||||
|
make rekeying easier.
|
||||||
|
|
||||||
20010403
|
20010403
|
||||||
- OpenBSD CVS Sync
|
- OpenBSD CVS Sync
|
||||||
@ -4816,4 +4820,4 @@
|
|||||||
- Wrote replacements for strlcpy and mkdtemp
|
- Wrote replacements for strlcpy and mkdtemp
|
||||||
- Released 1.0pre1
|
- Released 1.0pre1
|
||||||
|
|
||||||
$Id: ChangeLog,v 1.1049 2001/04/04 01:53:20 mouring Exp $
|
$Id: ChangeLog,v 1.1050 2001/04/04 01:56:17 mouring Exp $
|
||||||
|
21
dh.c
21
dh.c
@ -23,7 +23,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "includes.h"
|
#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"
|
#include "xmalloc.h"
|
||||||
|
|
||||||
@ -273,3 +273,22 @@ dh_new_group1(void)
|
|||||||
|
|
||||||
return (dh_new_group_asc(gen, group1));
|
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) */
|
||||||
|
}
|
||||||
|
4
dh.h
4
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.
|
* Copyright (c) 2000 Niels Provos. All rights reserved.
|
||||||
@ -40,6 +40,8 @@ DH *dh_new_group1(void);
|
|||||||
void dh_gen_key(DH *, int);
|
void dh_gen_key(DH *, int);
|
||||||
int dh_pub_is_valid(DH *dh, BIGNUM *dh_pub);
|
int dh_pub_is_valid(DH *dh, BIGNUM *dh_pub);
|
||||||
|
|
||||||
|
int dh_estimate(int bits);
|
||||||
|
|
||||||
#define DH_GRP_MIN 1024
|
#define DH_GRP_MIN 1024
|
||||||
#define DH_GRP_MAX 8192
|
#define DH_GRP_MAX 8192
|
||||||
|
|
||||||
|
400
kex.c
400
kex.c
@ -23,12 +23,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "includes.h"
|
#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/crypto.h>
|
||||||
#include <openssl/bio.h>
|
|
||||||
#include <openssl/bn.h>
|
|
||||||
#include <openssl/pem.h>
|
|
||||||
|
|
||||||
#include "ssh2.h"
|
#include "ssh2.h"
|
||||||
#include "xmalloc.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 "log.h"
|
||||||
#include "mac.h"
|
#include "mac.h"
|
||||||
#include "match.h"
|
#include "match.h"
|
||||||
|
#include "dispatch.h"
|
||||||
|
|
||||||
#define KEX_COOKIE_LEN 16
|
#define KEX_COOKIE_LEN 16
|
||||||
|
|
||||||
Buffer *
|
void kex_kexinit_finish(Kex *kex);
|
||||||
kex_init(char *myproposal[PROPOSAL_MAX])
|
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;
|
u_int32_t rand = 0;
|
||||||
int i;
|
int i;
|
||||||
Buffer *ki = xmalloc(sizeof(*ki));
|
|
||||||
|
buffer_clear(b);
|
||||||
for (i = 0; i < KEX_COOKIE_LEN; i++) {
|
for (i = 0; i < KEX_COOKIE_LEN; i++) {
|
||||||
if (i % 4 == 0)
|
if (i % 4 == 0)
|
||||||
rand = arc4random();
|
rand = arc4random();
|
||||||
cookie[i] = rand & 0xff;
|
buffer_put_char(b, rand & 0xff);
|
||||||
rand >>= 8;
|
rand >>= 8;
|
||||||
}
|
}
|
||||||
buffer_init(ki);
|
|
||||||
buffer_append(ki, (char *)cookie, sizeof cookie);
|
|
||||||
for (i = 0; i < PROPOSAL_MAX; i++)
|
for (i = 0; i < PROPOSAL_MAX; i++)
|
||||||
buffer_put_cstring(ki, myproposal[i]);
|
buffer_put_cstring(b, proposal[i]);
|
||||||
buffer_put_char(ki, first_kex_packet_follows);
|
buffer_put_char(b, 0); /* first_kex_packet_follows */
|
||||||
buffer_put_int(ki, 0); /* uint32 reserved */
|
buffer_put_int(b, 0); /* uint32 reserved */
|
||||||
return ki;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* send kexinit, parse and save reply */
|
/* parse buffer and return algorithm proposal */
|
||||||
void
|
char **
|
||||||
kex_exchange_kexinit(
|
kex_buf2prop(Buffer *raw)
|
||||||
Buffer *my_kexinit, Buffer *peer_kexint,
|
|
||||||
char *peer_proposal[PROPOSAL_MAX])
|
|
||||||
{
|
{
|
||||||
|
Buffer b;
|
||||||
int i;
|
int i;
|
||||||
char *ptr;
|
char **proposal;
|
||||||
int plen;
|
|
||||||
|
|
||||||
debug("send KEXINIT");
|
proposal = xmalloc(PROPOSAL_MAX * sizeof(char *));
|
||||||
packet_start(SSH2_MSG_KEXINIT);
|
|
||||||
packet_put_raw(buffer_ptr(my_kexinit), buffer_len(my_kexinit));
|
|
||||||
packet_send();
|
|
||||||
packet_write_wait();
|
|
||||||
debug("done");
|
|
||||||
|
|
||||||
/*
|
buffer_init(&b);
|
||||||
* read and save raw KEXINIT payload in buffer. this is used during
|
buffer_append(&b, buffer_ptr(raw), buffer_len(raw));
|
||||||
* 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 */
|
|
||||||
/* skip cookie */
|
/* skip cookie */
|
||||||
for (i = 0; i < KEX_COOKIE_LEN; i++)
|
for (i = 0; i < KEX_COOKIE_LEN; i++)
|
||||||
packet_get_char();
|
buffer_get_char(&b);
|
||||||
/* extract kex init proposal strings */
|
/* extract kex init proposal strings */
|
||||||
for (i = 0; i < PROPOSAL_MAX; i++) {
|
for (i = 0; i < PROPOSAL_MAX; i++) {
|
||||||
peer_proposal[i] = packet_get_string(NULL);
|
proposal[i] = buffer_get_string(&b,NULL);
|
||||||
debug("got kexinit: %s", peer_proposal[i]);
|
debug2("kex_parse_kexinit: %s", proposal[i]);
|
||||||
}
|
}
|
||||||
/* first kex follow / reserved */
|
/* first kex follows / reserved */
|
||||||
i = packet_get_char();
|
i = buffer_get_char(&b);
|
||||||
debug("first kex follow: %d ", i);
|
debug2("kex_parse_kexinit: first_kex_follows %d ", i);
|
||||||
i = packet_get_int();
|
i = buffer_get_int(&b);
|
||||||
debug("reserved: %d ", i);
|
debug2("kex_parse_kexinit: reserved %d ", i);
|
||||||
packet_done();
|
buffer_free(&b);
|
||||||
debug("done");
|
return proposal;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_KEX
|
|
||||||
void
|
void
|
||||||
dump_digest(u_char *digest, int len)
|
kex_prop_free(char **proposal)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i< len; i++){
|
|
||||||
fprintf(stderr, "%02x", digest[i]);
|
|
||||||
if(i%2!=0)
|
|
||||||
fprintf(stderr, " ");
|
|
||||||
}
|
|
||||||
fprintf(stderr, "\n");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
u_char *
|
for (i = 0; i < PROPOSAL_MAX; i++)
|
||||||
kex_hash(
|
xfree(proposal[i]);
|
||||||
char *client_version_string,
|
xfree(proposal);
|
||||||
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(digest, evp_md->md_size);
|
|
||||||
#endif
|
|
||||||
return digest;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u_char *
|
void
|
||||||
kex_hash_gex(
|
kex_protocol_error(int type, int plen, void *ctxt)
|
||||||
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;
|
error("Hm, kex protocol error: type %d plen %d", type, plen);
|
||||||
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_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(digest, evp_md->md_size);
|
|
||||||
#endif
|
|
||||||
return digest;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u_char *
|
void
|
||||||
derive_key(int id, int need, u_char *hash, BIGNUM *shared_secret)
|
kex_send_newkeys(void)
|
||||||
{
|
{
|
||||||
Buffer b;
|
packet_start(SSH2_MSG_NEWKEYS);
|
||||||
EVP_MD *evp_md = EVP_sha1();
|
packet_send();
|
||||||
EVP_MD_CTX md;
|
/* packet_write_wait(); */
|
||||||
char c = id;
|
debug("SSH2_MSG_NEWKEYS sent");
|
||||||
int have;
|
}
|
||||||
int mdsz = evp_md->md_size;
|
|
||||||
u_char *digest = xmalloc(((need+mdsz-1)/mdsz)*mdsz);
|
|
||||||
|
|
||||||
buffer_init(&b);
|
void
|
||||||
buffer_put_bignum2(&b, shared_secret);
|
kex_input_newkeys(int type, int plen, void *ctxt)
|
||||||
|
{
|
||||||
|
Kex *kex = ctxt;
|
||||||
|
int i;
|
||||||
|
|
||||||
EVP_DigestInit(&md, evp_md);
|
debug("SSH2_MSG_NEWKEYS received");
|
||||||
EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b)); /* shared_secret K */
|
kex->newkeys = 1;
|
||||||
EVP_DigestUpdate(&md, hash, mdsz); /* transport-06 */
|
for (i = 30; i <= 49; i++)
|
||||||
EVP_DigestUpdate(&md, &c, 1); /* key id */
|
dispatch_set(i, &kex_protocol_error);
|
||||||
EVP_DigestUpdate(&md, hash, mdsz); /* session id */
|
buffer_clear(&kex->peer);
|
||||||
EVP_DigestFinal(&md, digest, NULL);
|
buffer_clear(&kex->my);
|
||||||
|
kex->flags &= ~KEX_INIT_SENT;
|
||||||
|
}
|
||||||
|
|
||||||
/* expand */
|
void
|
||||||
for (have = mdsz; need > have; have += mdsz) {
|
kex_send_kexinit(Kex *kex)
|
||||||
EVP_DigestInit(&md, evp_md);
|
{
|
||||||
EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
|
packet_start(SSH2_MSG_KEXINIT);
|
||||||
EVP_DigestUpdate(&md, hash, mdsz);
|
packet_put_raw(buffer_ptr(&kex->my), buffer_len(&kex->my));
|
||||||
EVP_DigestUpdate(&md, digest, have);
|
packet_send();
|
||||||
EVP_DigestFinal(&md, digest + have, NULL);
|
debug("SSH2_MSG_KEXINIT sent");
|
||||||
|
kex->flags |= KEX_INIT_SENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
kex_input_kexinit(int type, int plen, void *ctxt)
|
||||||
|
{
|
||||||
|
char *ptr;
|
||||||
|
int dlen;
|
||||||
|
Kex *kex = (Kex *)ctxt;
|
||||||
|
|
||||||
|
dispatch_set(SSH2_MSG_KEXINIT, &kex_protocol_error);
|
||||||
|
debug("SSH2_MSG_KEXINIT received");
|
||||||
|
|
||||||
|
ptr = packet_get_raw(&dlen);
|
||||||
|
buffer_append(&kex->peer, ptr, dlen);
|
||||||
|
|
||||||
|
kex_kexinit_finish(kex);
|
||||||
|
}
|
||||||
|
|
||||||
|
Kex *
|
||||||
|
kex_start(char *proposal[PROPOSAL_MAX])
|
||||||
|
{
|
||||||
|
Kex *kex;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
kex = xmalloc(sizeof(*kex));
|
||||||
|
memset(kex, 0, sizeof(*kex));
|
||||||
|
buffer_init(&kex->peer);
|
||||||
|
buffer_init(&kex->my);
|
||||||
|
kex_prop2buf(&kex->my, proposal);
|
||||||
|
kex->newkeys = 0;
|
||||||
|
|
||||||
|
kex_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
void
|
||||||
@ -340,17 +273,25 @@ choose_hostkeyalg(Kex *k, char *client, char *server)
|
|||||||
xfree(hostkeyalg);
|
xfree(hostkeyalg);
|
||||||
}
|
}
|
||||||
|
|
||||||
Kex *
|
void
|
||||||
kex_choose_conf(char *cprop[PROPOSAL_MAX], char *sprop[PROPOSAL_MAX], int server)
|
kex_choose_conf(Kex *k)
|
||||||
{
|
{
|
||||||
|
char **my, **peer;
|
||||||
|
char **cprop, **sprop;
|
||||||
int mode;
|
int mode;
|
||||||
int ctos; /* direction: if true client-to-server */
|
int ctos; /* direction: if true client-to-server */
|
||||||
int need;
|
int need;
|
||||||
Kex *k;
|
|
||||||
|
|
||||||
k = xmalloc(sizeof(*k));
|
my = kex_buf2prop(&k->my);
|
||||||
memset(k, 0, sizeof(*k));
|
peer = kex_buf2prop(&k->peer);
|
||||||
k->server = server;
|
|
||||||
|
if (k->server) {
|
||||||
|
cprop=peer;
|
||||||
|
sprop=my;
|
||||||
|
} else {
|
||||||
|
cprop=my;
|
||||||
|
sprop=peer;
|
||||||
|
}
|
||||||
|
|
||||||
for (mode = 0; mode < MODE_MAX; mode++) {
|
for (mode = 0; mode < MODE_MAX; mode++) {
|
||||||
int nenc, nmac, ncomp;
|
int nenc, nmac, ncomp;
|
||||||
@ -381,11 +322,51 @@ kex_choose_conf(char *cprop[PROPOSAL_MAX], char *sprop[PROPOSAL_MAX], int server
|
|||||||
}
|
}
|
||||||
/* XXX need runden? */
|
/* XXX need runden? */
|
||||||
k->we_need = need;
|
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
|
#define NKEYS 6
|
||||||
int
|
void
|
||||||
kex_derive_keys(Kex *k, u_char *hash, BIGNUM *shared_secret)
|
kex_derive_keys(Kex *k, u_char *hash, BIGNUM *shared_secret)
|
||||||
{
|
{
|
||||||
int i;
|
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->enc[mode].key = keys[ctos ? 2 : 3];
|
||||||
k->mac[mode].key = keys[ctos ? 4 : 5];
|
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
|
||||||
|
58
kex.h
58
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.
|
* Copyright (c) 2000 Markus Friedl. All rights reserved.
|
||||||
@ -28,6 +28,8 @@
|
|||||||
|
|
||||||
#include <openssl/evp.h>
|
#include <openssl/evp.h>
|
||||||
#include "buffer.h"
|
#include "buffer.h"
|
||||||
|
#include "cipher.h"
|
||||||
|
#include "key.h"
|
||||||
|
|
||||||
#define KEX_DH1 "diffie-hellman-group1-sha1"
|
#define KEX_DH1 "diffie-hellman-group1-sha1"
|
||||||
#define KEX_DHGEX "diffie-hellman-group-exchange-sha1"
|
#define KEX_DHGEX "diffie-hellman-group-exchange-sha1"
|
||||||
@ -82,6 +84,7 @@ struct Comp {
|
|||||||
int enabled;
|
int enabled;
|
||||||
char *name;
|
char *name;
|
||||||
};
|
};
|
||||||
|
#define KEX_INIT_SENT 0x0001
|
||||||
struct Kex {
|
struct Kex {
|
||||||
Enc enc [MODE_MAX];
|
Enc enc [MODE_MAX];
|
||||||
Mac mac [MODE_MAX];
|
Mac mac [MODE_MAX];
|
||||||
@ -91,40 +94,31 @@ struct Kex {
|
|||||||
char *name;
|
char *name;
|
||||||
int hostkey_type;
|
int hostkey_type;
|
||||||
int kex_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_derive_keys(Kex *k, u_char *hash, BIGNUM *shared_secret);
|
||||||
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 packet_set_kex(Kex *k);
|
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 *
|
void kexdh(Kex *);
|
||||||
kex_hash(
|
void kexgex(Kex *);
|
||||||
char *client_version_string,
|
|
||||||
char *server_version_string,
|
#if defined(DEBUG_KEX) || defined(DEBUG_KEXDH)
|
||||||
char *ckexinit, int ckexinitlen,
|
void dump_digest(char *msg, u_char *digest, int len);
|
||||||
char *skexinit, int skexinitlen,
|
#endif
|
||||||
char *serverhostkeyblob, int sbloblen,
|
|
||||||
BIGNUM *client_dh_pub,
|
|
||||||
BIGNUM *server_dh_pub,
|
|
||||||
BIGNUM *shared_secret);
|
|
||||||
|
|
||||||
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
|
#endif
|
||||||
|
409
sshconnect2.c
409
sshconnect2.c
@ -23,7 +23,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "includes.h"
|
#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/bn.h>
|
||||||
#include <openssl/md5.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 "authfile.h"
|
||||||
#include "cli.h"
|
#include "cli.h"
|
||||||
#include "dh.h"
|
#include "dh.h"
|
||||||
#include "dispatch.h"
|
|
||||||
#include "authfd.h"
|
#include "authfd.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "readconf.h"
|
#include "readconf.h"
|
||||||
#include "readpass.h"
|
#include "readpass.h"
|
||||||
#include "match.h"
|
#include "match.h"
|
||||||
|
#include "dispatch.h"
|
||||||
void ssh_dh1_client(Kex *, char *, struct sockaddr *, Buffer *, Buffer *);
|
|
||||||
void ssh_dhgex_client(Kex *, char *, struct sockaddr *, Buffer *, Buffer *);
|
|
||||||
|
|
||||||
/* import */
|
/* import */
|
||||||
extern char *client_version_string;
|
extern char *client_version_string;
|
||||||
@ -69,13 +66,24 @@ extern Options options;
|
|||||||
u_char *session_id2 = NULL;
|
u_char *session_id2 = NULL;
|
||||||
int session_id2_len = 0;
|
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
|
void
|
||||||
ssh_kex2(char *host, struct sockaddr *hostaddr)
|
ssh_kex2(char *host, struct sockaddr *hostaddr)
|
||||||
{
|
{
|
||||||
int i, plen;
|
|
||||||
Kex *kex;
|
Kex *kex;
|
||||||
Buffer *client_kexinit, *server_kexinit;
|
|
||||||
char *sprop[PROPOSAL_MAX];
|
xxx_host = host;
|
||||||
|
xxx_hostaddr = hostaddr;
|
||||||
|
|
||||||
if (options.ciphers == (char *)-1) {
|
if (options.ciphers == (char *)-1) {
|
||||||
log("No valid ciphers for protocol version 2 given, using defaults.");
|
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;
|
myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* buffers with raw kexinit messages */
|
kex = kex_start(myproposal);
|
||||||
server_kexinit = xmalloc(sizeof(*server_kexinit));
|
kex->client_version_string=client_version_string;
|
||||||
buffer_init(server_kexinit);
|
kex->server_version_string=server_version_string;
|
||||||
client_kexinit = kex_init(myproposal);
|
kex->check_host_key=&check_host_key_callback;
|
||||||
|
|
||||||
/* algorithm negotiation */
|
/* start key exchange */
|
||||||
kex_exchange_kexinit(client_kexinit, server_kexinit, sprop);
|
dispatch_run(DISPATCH_BLOCK, &kex->newkeys, kex);
|
||||||
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);
|
|
||||||
|
|
||||||
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.");
|
|
||||||
|
|
||||||
#ifdef DEBUG_KEXDH
|
#ifdef DEBUG_KEXDH
|
||||||
/* send 1st encrypted/maced/compressed message */
|
/* send 1st encrypted/maced/compressed message */
|
||||||
@ -149,339 +124,7 @@ ssh_kex2(char *host, struct sockaddr *hostaddr)
|
|||||||
packet_send();
|
packet_send();
|
||||||
packet_write_wait();
|
packet_write_wait();
|
||||||
#endif
|
#endif
|
||||||
debug("done: KEX2.");
|
debug("done: ssh_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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -563,6 +206,7 @@ ssh_userauth2(const char *server_user, char *host)
|
|||||||
Authctxt authctxt;
|
Authctxt authctxt;
|
||||||
int type;
|
int type;
|
||||||
int plen;
|
int plen;
|
||||||
|
int i;
|
||||||
|
|
||||||
if (options.challenge_reponse_authentication)
|
if (options.challenge_reponse_authentication)
|
||||||
options.kbd_interactive_authentication = 1;
|
options.kbd_interactive_authentication = 1;
|
||||||
@ -603,7 +247,10 @@ ssh_userauth2(const char *server_user, char *host)
|
|||||||
/* initial userauth request */
|
/* initial userauth request */
|
||||||
userauth_none(&authctxt);
|
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_SUCCESS, &input_userauth_success);
|
||||||
dispatch_set(SSH2_MSG_USERAUTH_FAILURE, &input_userauth_failure);
|
dispatch_set(SSH2_MSG_USERAUTH_FAILURE, &input_userauth_failure);
|
||||||
dispatch_set(SSH2_MSG_USERAUTH_BANNER, &input_userauth_banner);
|
dispatch_set(SSH2_MSG_USERAUTH_BANNER, &input_userauth_banner);
|
||||||
|
359
sshd.c
359
sshd.c
@ -40,7 +40,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "includes.h"
|
#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/dh.h>
|
||||||
#include <openssl/bn.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 "canohost.h"
|
||||||
#include "auth.h"
|
#include "auth.h"
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
|
#include "dispatch.h"
|
||||||
|
|
||||||
#ifdef LIBWRAP
|
#ifdef LIBWRAP
|
||||||
#include <tcpd.h>
|
#include <tcpd.h>
|
||||||
@ -1407,14 +1408,7 @@ do_ssh1_kex(void)
|
|||||||
void
|
void
|
||||||
do_ssh2_kex(void)
|
do_ssh2_kex(void)
|
||||||
{
|
{
|
||||||
Buffer *server_kexinit;
|
|
||||||
Buffer *client_kexinit;
|
|
||||||
int payload_len;
|
|
||||||
int i;
|
|
||||||
Kex *kex;
|
Kex *kex;
|
||||||
char *cprop[PROPOSAL_MAX];
|
|
||||||
|
|
||||||
/* KEXINIT */
|
|
||||||
|
|
||||||
if (options.ciphers != NULL) {
|
if (options.ciphers != NULL) {
|
||||||
myproposal[PROPOSAL_ENC_ALGS_CTOS] =
|
myproposal[PROPOSAL_ENC_ALGS_CTOS] =
|
||||||
@ -1431,36 +1425,14 @@ do_ssh2_kex(void)
|
|||||||
}
|
}
|
||||||
myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = list_hostkey_types();
|
myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = list_hostkey_types();
|
||||||
|
|
||||||
server_kexinit = kex_init(myproposal);
|
kex = kex_start(myproposal);
|
||||||
client_kexinit = xmalloc(sizeof(*client_kexinit));
|
kex->server = 1;
|
||||||
buffer_init(client_kexinit);
|
kex->client_version_string=client_version_string;
|
||||||
|
kex->server_version_string=server_version_string;
|
||||||
|
kex->load_host_key=&get_hostkey_by_type;
|
||||||
|
|
||||||
/* algorithm negotiation */
|
/* start key exchange */
|
||||||
kex_exchange_kexinit(server_kexinit, client_kexinit, cprop);
|
dispatch_run(DISPATCH_BLOCK, &kex->newkeys, kex);
|
||||||
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.");
|
|
||||||
|
|
||||||
debug("Wait SSH2_MSG_NEWKEYS.");
|
|
||||||
packet_read_expect(&payload_len, SSH2_MSG_NEWKEYS);
|
|
||||||
debug("GOT SSH2_MSG_NEWKEYS.");
|
|
||||||
|
|
||||||
#ifdef DEBUG_KEXDH
|
#ifdef DEBUG_KEXDH
|
||||||
/* send 1st encrypted/maced/compressed message */
|
/* send 1st encrypted/maced/compressed message */
|
||||||
@ -1469,316 +1441,5 @@ do_ssh2_kex(void)
|
|||||||
packet_send();
|
packet_send();
|
||||||
packet_write_wait();
|
packet_write_wait();
|
||||||
#endif
|
#endif
|
||||||
|
debug("KEX done");
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user