mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-07-01 03:22:48 -05:00
Support elliptic-curve Diffie-Hellman GSS KEX.
This is surprisingly simple, because it wasn't necessary to touch the GSS parts at all. Nothing changes about the message formats between integer DH and ECDH in GSS KEX, except that the mpints sent back and forth as part of integer DH are replaced by the opaque strings used in ECDH. So I've invented a new KEXTYPE and made it control a bunch of small conditionals in the middle of the GSS KEX code, leaving the rest unchanged.
This commit is contained in:
@ -248,7 +248,7 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted)
|
||||
ecdh_key_free(s->ecdh_key);
|
||||
s->ecdh_key = NULL;
|
||||
#ifndef NO_GSSAPI
|
||||
} else if (s->kex_alg->main_type == KEXTYPE_GSS) {
|
||||
} else if (kex_is_gss(s->kex_alg)) {
|
||||
ptrlen data;
|
||||
|
||||
s->ppl.bpp->pls->kctx = SSH2_PKTCTX_GSSKEX;
|
||||
@ -276,14 +276,25 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted)
|
||||
if (s->nbits > s->kex_alg->hash->hlen * 8)
|
||||
s->nbits = s->kex_alg->hash->hlen * 8;
|
||||
|
||||
if (dh_is_gex(s->kex_alg)) {
|
||||
assert(!s->ecdh_key);
|
||||
assert(!s->dh_ctx);
|
||||
|
||||
if (s->kex_alg->main_type == KEXTYPE_GSS_ECDH) {
|
||||
s->ecdh_key = ecdh_key_new(s->kex_alg, false);
|
||||
|
||||
char *desc = ecdh_keyalg_description(s->kex_alg);
|
||||
ppl_logevent("Doing GSSAPI (with Kerberos V5) %s with hash %s",
|
||||
desc, ssh_hash_alg(s->exhash)->text_name);
|
||||
sfree(desc);
|
||||
} else if (dh_is_gex(s->kex_alg)) {
|
||||
/*
|
||||
* Work out how big a DH group we will need to allow that
|
||||
* much data.
|
||||
*/
|
||||
s->pbits = 512 << ((s->nbits - 1) / 64);
|
||||
ppl_logevent("Doing GSSAPI (with Kerberos V5) Diffie-Hellman "
|
||||
"group exchange, with minimum %d bits", s->pbits);
|
||||
"group exchange, with minimum %d bits, and hash %s",
|
||||
s->pbits, ssh_hash_alg(s->exhash)->text_name);
|
||||
pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_KEXGSS_GROUPREQ);
|
||||
put_uint32(pktout, s->pbits); /* min */
|
||||
put_uint32(pktout, s->pbits); /* preferred */
|
||||
@ -314,14 +325,19 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted)
|
||||
} else {
|
||||
s->dh_ctx = dh_setup_group(s->kex_alg);
|
||||
ppl_logevent("Using GSSAPI (with Kerberos V5) Diffie-Hellman with"
|
||||
" standard group \"%s\"", s->kex_alg->groupname);
|
||||
" standard group \"%s\" and hash %s",
|
||||
s->kex_alg->groupname,
|
||||
ssh_hash_alg(s->exhash)->text_name);
|
||||
}
|
||||
|
||||
ppl_logevent("Doing GSSAPI (with Kerberos V5) Diffie-Hellman key "
|
||||
"exchange with hash %s", ssh_hash_alg(s->exhash)->text_name);
|
||||
/* Now generate e for Diffie-Hellman. */
|
||||
seat_set_busy_status(s->ppl.seat, BUSY_CPU);
|
||||
s->e = dh_create_e(s->dh_ctx);
|
||||
if (s->ecdh_key) {
|
||||
s->ebuf = strbuf_new_nm();
|
||||
ecdh_key_getpublic(s->ecdh_key, BinarySink_UPCAST(s->ebuf));
|
||||
} else {
|
||||
s->e = dh_create_e(s->dh_ctx);
|
||||
}
|
||||
|
||||
if (s->shgss->lib->gsslogmsg)
|
||||
ppl_logevent("%s", s->shgss->lib->gsslogmsg);
|
||||
@ -385,7 +401,11 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted)
|
||||
}
|
||||
put_string(pktout,
|
||||
s->gss_sndtok.value, s->gss_sndtok.length);
|
||||
put_mp_ssh2(pktout, s->e);
|
||||
if (s->ecdh_key) {
|
||||
put_stringpl(pktout, ptrlen_from_strbuf(s->ebuf));
|
||||
} else {
|
||||
put_mp_ssh2(pktout, s->e);
|
||||
}
|
||||
pq_push(s->ppl.out_pq, pktout);
|
||||
s->shgss->lib->free_tok(s->shgss->lib, &s->gss_sndtok);
|
||||
ppl_logevent("GSSAPI key exchange initialised");
|
||||
@ -412,7 +432,11 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted)
|
||||
continue;
|
||||
case SSH2_MSG_KEXGSS_COMPLETE:
|
||||
s->complete_rcvd = true;
|
||||
s->f = get_mp_ssh2(pktin);
|
||||
if (s->ecdh_key) {
|
||||
s->fbuf = strbuf_dup_nm(get_string(pktin));
|
||||
} else {
|
||||
s->f = get_mp_ssh2(pktin);
|
||||
}
|
||||
data = get_string(pktin);
|
||||
s->mic.value = (char *)data.ptr;
|
||||
s->mic.length = data.len;
|
||||
@ -475,7 +499,16 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted)
|
||||
s->gss_stat == SSH_GSS_S_CONTINUE_NEEDED ||
|
||||
!s->complete_rcvd);
|
||||
|
||||
{
|
||||
if (s->ecdh_key) {
|
||||
bool ok = ecdh_key_getkey(s->ecdh_key, ptrlen_from_strbuf(s->fbuf),
|
||||
BinarySink_UPCAST(s->kex_shared_secret));
|
||||
if (!ok) {
|
||||
ssh_proto_error(s->ppl.ssh, "Received invalid elliptic curve "
|
||||
"point in GSSAPI ECDH reply");
|
||||
*aborted = true;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
const char *err = dh_validate_f(s->dh_ctx, s->f);
|
||||
if (err) {
|
||||
ssh_proto_error(s->ppl.ssh, "GSSAPI reply failed "
|
||||
@ -483,10 +516,10 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted)
|
||||
*aborted = true;
|
||||
return;
|
||||
}
|
||||
mp_int *K = dh_find_K(s->dh_ctx, s->f);
|
||||
put_mp_ssh2(s->kex_shared_secret, K);
|
||||
mp_free(K);
|
||||
}
|
||||
mp_int *K = dh_find_K(s->dh_ctx, s->f);
|
||||
put_mp_ssh2(s->kex_shared_secret, K);
|
||||
mp_free(K);
|
||||
|
||||
/* We assume everything from now on will be quick, and it might
|
||||
* involve user interaction. */
|
||||
@ -494,26 +527,39 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted)
|
||||
|
||||
if (!s->hkey)
|
||||
put_stringz(s->exhash, "");
|
||||
if (dh_is_gex(s->kex_alg)) {
|
||||
/* min, preferred, max */
|
||||
put_uint32(s->exhash, s->pbits);
|
||||
put_uint32(s->exhash, s->pbits);
|
||||
put_uint32(s->exhash, s->pbits * 2);
|
||||
|
||||
put_mp_ssh2(s->exhash, s->p);
|
||||
put_mp_ssh2(s->exhash, s->g);
|
||||
if (s->ecdh_key) {
|
||||
put_stringpl(s->exhash, ptrlen_from_strbuf(s->ebuf));
|
||||
put_stringpl(s->exhash, ptrlen_from_strbuf(s->fbuf));
|
||||
} else {
|
||||
if (dh_is_gex(s->kex_alg)) {
|
||||
/* min, preferred, max */
|
||||
put_uint32(s->exhash, s->pbits);
|
||||
put_uint32(s->exhash, s->pbits);
|
||||
put_uint32(s->exhash, s->pbits * 2);
|
||||
|
||||
put_mp_ssh2(s->exhash, s->p);
|
||||
put_mp_ssh2(s->exhash, s->g);
|
||||
}
|
||||
put_mp_ssh2(s->exhash, s->e);
|
||||
put_mp_ssh2(s->exhash, s->f);
|
||||
}
|
||||
put_mp_ssh2(s->exhash, s->e);
|
||||
put_mp_ssh2(s->exhash, s->f);
|
||||
|
||||
/*
|
||||
* MIC verification is done below, after we compute the hash
|
||||
* used as the MIC input.
|
||||
*/
|
||||
|
||||
dh_cleanup(s->dh_ctx);
|
||||
s->dh_ctx = NULL;
|
||||
mp_free(s->f); s->f = NULL;
|
||||
if (s->ecdh_key) {
|
||||
ecdh_key_free(s->ecdh_key);
|
||||
s->ecdh_key = NULL;
|
||||
strbuf_free(s->ebuf); s->ebuf = NULL;
|
||||
strbuf_free(s->fbuf); s->fbuf = NULL;
|
||||
} else {
|
||||
dh_cleanup(s->dh_ctx);
|
||||
s->dh_ctx = NULL;
|
||||
mp_free(s->f); s->f = NULL;
|
||||
}
|
||||
if (dh_is_gex(s->kex_alg)) {
|
||||
mp_free(s->g); s->g = NULL;
|
||||
mp_free(s->p); s->p = NULL;
|
||||
@ -646,7 +692,7 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted)
|
||||
ssh2transport_finalise_exhash(s);
|
||||
|
||||
#ifndef NO_GSSAPI
|
||||
if (s->kex_alg->main_type == KEXTYPE_GSS) {
|
||||
if (kex_is_gss(s->kex_alg)) {
|
||||
Ssh_gss_buf gss_buf;
|
||||
SSH_GSS_CLEAR_BUF(&s->gss_buf);
|
||||
|
||||
@ -694,7 +740,7 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted)
|
||||
s->dh_ctx = NULL;
|
||||
|
||||
/* In GSS keyex there's no hostkey signature to verify */
|
||||
if (s->kex_alg->main_type != KEXTYPE_GSS) {
|
||||
if (!kex_is_gss(s->kex_alg)) {
|
||||
if (!s->hkey) {
|
||||
ssh_proto_error(s->ppl.ssh, "Server's host key is invalid");
|
||||
*aborted = true;
|
||||
@ -720,7 +766,7 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted)
|
||||
* In a GSS-based session, check the host key (if any) against
|
||||
* the transient host key cache.
|
||||
*/
|
||||
if (s->kex_alg->main_type == KEXTYPE_GSS) {
|
||||
if (kex_is_gss(s->kex_alg)) {
|
||||
|
||||
/*
|
||||
* We've just done a GSS key exchange. If it gave us a
|
||||
@ -785,7 +831,7 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted)
|
||||
* An exception is if this was the non-GSS key exchange we
|
||||
* triggered on purpose to populate the transient cache.
|
||||
*/
|
||||
assert(s->hkey); /* only KEXTYPE_GSS lets this be null */
|
||||
assert(s->hkey); /* only KEXTYPE_GSS* lets this be null */
|
||||
char *fingerprint = ssh2_double_fingerprint(
|
||||
s->hkey, SSH_FPTYPE_DEFAULT);
|
||||
|
||||
|
@ -228,6 +228,8 @@ static void ssh2_transport_free(PacketProtocolLayer *ppl)
|
||||
if (s->f) mp_free(s->f);
|
||||
if (s->p) mp_free(s->p);
|
||||
if (s->g) mp_free(s->g);
|
||||
if (s->ebuf) strbuf_free(s->ebuf);
|
||||
if (s->fbuf) strbuf_free(s->fbuf);
|
||||
if (s->kex_shared_secret) strbuf_free(s->kex_shared_secret);
|
||||
if (s->dh_ctx)
|
||||
dh_cleanup(s->dh_ctx);
|
||||
@ -508,7 +510,7 @@ static void ssh2_write_kexinit_lists(
|
||||
bool warn;
|
||||
|
||||
int n_preferred_kex;
|
||||
const ssh_kexes *preferred_kex[KEX_MAX + 2]; /* +2 for GSSAPI */
|
||||
const ssh_kexes *preferred_kex[KEX_MAX + 3]; /* +3 for GSSAPI */
|
||||
int n_preferred_hk;
|
||||
int preferred_hk[HK_MAX];
|
||||
int n_preferred_ciphers;
|
||||
@ -524,6 +526,7 @@ static void ssh2_write_kexinit_lists(
|
||||
*/
|
||||
n_preferred_kex = 0;
|
||||
if (can_gssapi_keyex) {
|
||||
preferred_kex[n_preferred_kex++] = &ssh_gssk5_ecdh_kex;
|
||||
preferred_kex[n_preferred_kex++] = &ssh_gssk5_sha2_kex;
|
||||
preferred_kex[n_preferred_kex++] = &ssh_gssk5_sha1_kex;
|
||||
}
|
||||
|
@ -181,6 +181,7 @@ struct ssh2_transport_state {
|
||||
int nbits, pbits;
|
||||
bool warn_kex, warn_hk, warn_cscipher, warn_sccipher;
|
||||
mp_int *p, *g, *e, *f;
|
||||
strbuf *ebuf, *fbuf;
|
||||
strbuf *kex_shared_secret;
|
||||
strbuf *outgoing_kexinit, *incoming_kexinit;
|
||||
strbuf *client_kexinit, *server_kexinit; /* aliases to the above */
|
||||
|
Reference in New Issue
Block a user