mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-03-22 14:39:24 -05:00
Factor KEXINIT construction out into its own function.
This has nice effects on code tidiness (quite a few variables now become local to the new function instead of living permanently in the transport layer), but mostly, the idea is to add flexibility by introducing a convenient place to change the policy for how we write the negotiation lists in our KEXINIT.
This commit is contained in:
parent
fe26ddb1d9
commit
3df80af868
708
ssh2transport.c
708
ssh2transport.c
@ -199,11 +199,8 @@ struct ssh2_transport_state {
|
|||||||
|
|
||||||
int nbits, pbits, warn_kex, warn_hk, warn_cscipher, warn_sccipher;
|
int nbits, pbits, warn_kex, warn_hk, warn_cscipher, warn_sccipher;
|
||||||
Bignum p, g, e, f, K;
|
Bignum p, g, e, f, K;
|
||||||
void *our_kexinit;
|
strbuf *client_kexinit, *server_kexinit;
|
||||||
int our_kexinitlen;
|
|
||||||
int kex_init_value, kex_reply_value;
|
int kex_init_value, kex_reply_value;
|
||||||
const struct ssh2_macalg *const *maclist;
|
|
||||||
int nmacs;
|
|
||||||
struct {
|
struct {
|
||||||
const struct ssh2_cipheralg *cipher;
|
const struct ssh2_cipheralg *cipher;
|
||||||
const struct ssh2_macalg *mac;
|
const struct ssh2_macalg *mac;
|
||||||
@ -217,16 +214,9 @@ struct ssh2_transport_state {
|
|||||||
struct RSAKey *rsa_kex_key; /* for RSA kex */
|
struct RSAKey *rsa_kex_key; /* for RSA kex */
|
||||||
struct ec_key *ecdh_key; /* for ECDH kex */
|
struct ec_key *ecdh_key; /* for ECDH kex */
|
||||||
unsigned char exchange_hash[SSH2_KEX_MAX_HASH_LEN];
|
unsigned char exchange_hash[SSH2_KEX_MAX_HASH_LEN];
|
||||||
int n_preferred_kex;
|
|
||||||
int can_gssapi_keyex;
|
int can_gssapi_keyex;
|
||||||
int need_gss_transient_hostkey;
|
int need_gss_transient_hostkey;
|
||||||
int warned_about_no_gss_transient_hostkey;
|
int warned_about_no_gss_transient_hostkey;
|
||||||
const struct ssh_kexes *preferred_kex[KEX_MAX + 1]; /* +1 for GSSAPI */
|
|
||||||
int n_preferred_hk;
|
|
||||||
int preferred_hk[HK_MAX];
|
|
||||||
int n_preferred_ciphers;
|
|
||||||
const struct ssh2_ciphers *preferred_ciphers[CIPHER_MAX];
|
|
||||||
const struct ssh_compression_alg *preferred_comp;
|
|
||||||
int got_session_id;
|
int got_session_id;
|
||||||
int dlgret;
|
int dlgret;
|
||||||
int guessok;
|
int guessok;
|
||||||
@ -341,6 +331,9 @@ PacketProtocolLayer *ssh2_transport_new(
|
|||||||
s->thc = ssh_transient_hostkey_cache_new();
|
s->thc = ssh_transient_hostkey_cache_new();
|
||||||
s->gss_kex_used = FALSE;
|
s->gss_kex_used = FALSE;
|
||||||
|
|
||||||
|
s->client_kexinit = strbuf_new();
|
||||||
|
s->server_kexinit = strbuf_new();
|
||||||
|
|
||||||
ssh2_transport_set_max_data_size(s);
|
ssh2_transport_set_max_data_size(s);
|
||||||
|
|
||||||
return &s->ppl;
|
return &s->ppl;
|
||||||
@ -397,6 +390,8 @@ static void ssh2_transport_free(PacketProtocolLayer *ppl)
|
|||||||
ssh_ecdhkex_freekey(s->ecdh_key);
|
ssh_ecdhkex_freekey(s->ecdh_key);
|
||||||
if (s->exhash)
|
if (s->exhash)
|
||||||
ssh_hash_free(s->exhash);
|
ssh_hash_free(s->exhash);
|
||||||
|
strbuf_free(s->client_kexinit);
|
||||||
|
strbuf_free(s->server_kexinit);
|
||||||
ssh_transient_hostkey_cache_free(s->thc);
|
ssh_transient_hostkey_cache_free(s->thc);
|
||||||
sfree(s);
|
sfree(s);
|
||||||
}
|
}
|
||||||
@ -583,6 +578,329 @@ static PktIn *ssh2_transport_pop(struct ssh2_transport_state *s)
|
|||||||
return pq_pop(s->ppl.in_pq);
|
return pq_pop(s->ppl.in_pq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ssh2_write_kexinit_lists(
|
||||||
|
BinarySink *pktout,
|
||||||
|
struct kexinit_algorithm kexlists[NKEXLIST][MAXKEXLIST],
|
||||||
|
Conf *conf, int remote_bugs,
|
||||||
|
const char *hk_host, int hk_port, const ssh_keyalg *hk_prev,
|
||||||
|
ssh_transient_hostkey_cache *thc,
|
||||||
|
int first_time, int can_gssapi_keyex, int transient_hostkey_mode)
|
||||||
|
{
|
||||||
|
int i, j, k, warn;
|
||||||
|
|
||||||
|
int n_preferred_kex;
|
||||||
|
const struct ssh_kexes *preferred_kex[KEX_MAX + 1]; /* +1 for GSSAPI */
|
||||||
|
int n_preferred_hk;
|
||||||
|
int preferred_hk[HK_MAX];
|
||||||
|
int n_preferred_ciphers;
|
||||||
|
const struct ssh2_ciphers *preferred_ciphers[CIPHER_MAX];
|
||||||
|
const struct ssh_compression_alg *preferred_comp;
|
||||||
|
const struct ssh2_macalg *const *maclist;
|
||||||
|
int nmacs;
|
||||||
|
|
||||||
|
struct kexinit_algorithm *alg;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set up the preferred key exchange. (NULL => warn below here)
|
||||||
|
*/
|
||||||
|
n_preferred_kex = 0;
|
||||||
|
if (can_gssapi_keyex)
|
||||||
|
preferred_kex[n_preferred_kex++] = &ssh_gssk5_sha1_kex;
|
||||||
|
for (i = 0; i < KEX_MAX; i++) {
|
||||||
|
switch (conf_get_int_int(conf, CONF_ssh_kexlist, i)) {
|
||||||
|
case KEX_DHGEX:
|
||||||
|
preferred_kex[n_preferred_kex++] =
|
||||||
|
&ssh_diffiehellman_gex;
|
||||||
|
break;
|
||||||
|
case KEX_DHGROUP14:
|
||||||
|
preferred_kex[n_preferred_kex++] =
|
||||||
|
&ssh_diffiehellman_group14;
|
||||||
|
break;
|
||||||
|
case KEX_DHGROUP1:
|
||||||
|
preferred_kex[n_preferred_kex++] =
|
||||||
|
&ssh_diffiehellman_group1;
|
||||||
|
break;
|
||||||
|
case KEX_RSA:
|
||||||
|
preferred_kex[n_preferred_kex++] =
|
||||||
|
&ssh_rsa_kex;
|
||||||
|
break;
|
||||||
|
case KEX_ECDH:
|
||||||
|
preferred_kex[n_preferred_kex++] =
|
||||||
|
&ssh_ecdh_kex;
|
||||||
|
break;
|
||||||
|
case KEX_WARN:
|
||||||
|
/* Flag for later. Don't bother if it's the last in
|
||||||
|
* the list. */
|
||||||
|
if (i < KEX_MAX - 1) {
|
||||||
|
preferred_kex[n_preferred_kex++] = NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set up the preferred host key types. These are just the ids
|
||||||
|
* in the enum in putty.h, so 'warn below here' is indicated
|
||||||
|
* by HK_WARN.
|
||||||
|
*/
|
||||||
|
n_preferred_hk = 0;
|
||||||
|
for (i = 0; i < HK_MAX; i++) {
|
||||||
|
int id = conf_get_int_int(conf, CONF_ssh_hklist, i);
|
||||||
|
/* As above, don't bother with HK_WARN if it's last in the
|
||||||
|
* list */
|
||||||
|
if (id != HK_WARN || i < HK_MAX - 1)
|
||||||
|
preferred_hk[n_preferred_hk++] = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set up the preferred ciphers. (NULL => warn below here)
|
||||||
|
*/
|
||||||
|
n_preferred_ciphers = 0;
|
||||||
|
for (i = 0; i < CIPHER_MAX; i++) {
|
||||||
|
switch (conf_get_int_int(conf, CONF_ssh_cipherlist, i)) {
|
||||||
|
case CIPHER_BLOWFISH:
|
||||||
|
preferred_ciphers[n_preferred_ciphers++] = &ssh2_blowfish;
|
||||||
|
break;
|
||||||
|
case CIPHER_DES:
|
||||||
|
if (conf_get_int(conf, CONF_ssh2_des_cbc))
|
||||||
|
preferred_ciphers[n_preferred_ciphers++] = &ssh2_des;
|
||||||
|
break;
|
||||||
|
case CIPHER_3DES:
|
||||||
|
preferred_ciphers[n_preferred_ciphers++] = &ssh2_3des;
|
||||||
|
break;
|
||||||
|
case CIPHER_AES:
|
||||||
|
preferred_ciphers[n_preferred_ciphers++] = &ssh2_aes;
|
||||||
|
break;
|
||||||
|
case CIPHER_ARCFOUR:
|
||||||
|
preferred_ciphers[n_preferred_ciphers++] = &ssh2_arcfour;
|
||||||
|
break;
|
||||||
|
case CIPHER_CHACHA20:
|
||||||
|
preferred_ciphers[n_preferred_ciphers++] = &ssh2_ccp;
|
||||||
|
break;
|
||||||
|
case CIPHER_WARN:
|
||||||
|
/* Flag for later. Don't bother if it's the last in
|
||||||
|
* the list. */
|
||||||
|
if (i < CIPHER_MAX - 1) {
|
||||||
|
preferred_ciphers[n_preferred_ciphers++] = NULL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set up preferred compression.
|
||||||
|
*/
|
||||||
|
if (conf_get_int(conf, CONF_compression))
|
||||||
|
preferred_comp = &ssh_zlib;
|
||||||
|
else
|
||||||
|
preferred_comp = &ssh_comp_none;
|
||||||
|
|
||||||
|
for (i = 0; i < NKEXLIST; i++)
|
||||||
|
for (j = 0; j < MAXKEXLIST; j++)
|
||||||
|
kexlists[i][j].name = NULL;
|
||||||
|
/* List key exchange algorithms. */
|
||||||
|
warn = FALSE;
|
||||||
|
for (i = 0; i < n_preferred_kex; i++) {
|
||||||
|
const struct ssh_kexes *k = preferred_kex[i];
|
||||||
|
if (!k) warn = TRUE;
|
||||||
|
else for (j = 0; j < k->nkexes; j++) {
|
||||||
|
alg = ssh2_kexinit_addalg(kexlists[KEXLIST_KEX],
|
||||||
|
k->list[j]->name);
|
||||||
|
alg->u.kex.kex = k->list[j];
|
||||||
|
alg->u.kex.warn = warn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* List server host key algorithms. */
|
||||||
|
if (first_time) {
|
||||||
|
/*
|
||||||
|
* In the first key exchange, we list all the algorithms
|
||||||
|
* we're prepared to cope with, but prefer those algorithms
|
||||||
|
* for which we have a host key for this host.
|
||||||
|
*
|
||||||
|
* If the host key algorithm is below the warning
|
||||||
|
* threshold, we warn even if we did already have a key
|
||||||
|
* for it, on the basis that if the user has just
|
||||||
|
* reconfigured that host key type to be warned about,
|
||||||
|
* they surely _do_ want to be alerted that a server
|
||||||
|
* they're actually connecting to is using it.
|
||||||
|
*/
|
||||||
|
warn = FALSE;
|
||||||
|
for (i = 0; i < n_preferred_hk; i++) {
|
||||||
|
if (preferred_hk[i] == HK_WARN)
|
||||||
|
warn = TRUE;
|
||||||
|
for (j = 0; j < lenof(hostkey_algs); j++) {
|
||||||
|
if (hostkey_algs[j].id != preferred_hk[i])
|
||||||
|
continue;
|
||||||
|
if (have_ssh_host_key(hk_host, hk_port,
|
||||||
|
hostkey_algs[j].alg->cache_id)) {
|
||||||
|
alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY],
|
||||||
|
hostkey_algs[j].alg->ssh_id);
|
||||||
|
alg->u.hk.hostkey = hostkey_algs[j].alg;
|
||||||
|
alg->u.hk.warn = warn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
warn = FALSE;
|
||||||
|
for (i = 0; i < n_preferred_hk; i++) {
|
||||||
|
if (preferred_hk[i] == HK_WARN)
|
||||||
|
warn = TRUE;
|
||||||
|
for (j = 0; j < lenof(hostkey_algs); j++) {
|
||||||
|
if (hostkey_algs[j].id != preferred_hk[i])
|
||||||
|
continue;
|
||||||
|
alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY],
|
||||||
|
hostkey_algs[j].alg->ssh_id);
|
||||||
|
alg->u.hk.hostkey = hostkey_algs[j].alg;
|
||||||
|
alg->u.hk.warn = warn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifndef NO_GSSAPI
|
||||||
|
} else if (transient_hostkey_mode) {
|
||||||
|
/*
|
||||||
|
* If we've previously done a GSSAPI KEX, then we list
|
||||||
|
* precisely the algorithms for which a previous GSS key
|
||||||
|
* exchange has delivered us a host key, because we expect
|
||||||
|
* one of exactly those keys to be used in any subsequent
|
||||||
|
* non-GSS-based rekey.
|
||||||
|
*
|
||||||
|
* An exception is if this is the key exchange we
|
||||||
|
* triggered for the purposes of populating that cache -
|
||||||
|
* in which case the cache will currently be empty, which
|
||||||
|
* isn't helpful!
|
||||||
|
*/
|
||||||
|
warn = FALSE;
|
||||||
|
for (i = 0; i < n_preferred_hk; i++) {
|
||||||
|
if (preferred_hk[i] == HK_WARN)
|
||||||
|
warn = TRUE;
|
||||||
|
for (j = 0; j < lenof(hostkey_algs); j++) {
|
||||||
|
if (hostkey_algs[j].id != preferred_hk[i])
|
||||||
|
continue;
|
||||||
|
if (ssh_transient_hostkey_cache_has(
|
||||||
|
thc, hostkey_algs[j].alg)) {
|
||||||
|
alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY],
|
||||||
|
hostkey_algs[j].alg->ssh_id);
|
||||||
|
alg->u.hk.hostkey = hostkey_algs[j].alg;
|
||||||
|
alg->u.hk.warn = warn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* In subsequent key exchanges, we list only the host key
|
||||||
|
* algorithm that was selected in the first key exchange,
|
||||||
|
* so that we keep getting the same host key and hence
|
||||||
|
* don't have to interrupt the user's session to ask for
|
||||||
|
* reverification.
|
||||||
|
*/
|
||||||
|
assert(hk_prev);
|
||||||
|
alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY], hk_prev->ssh_id);
|
||||||
|
alg->u.hk.hostkey = hk_prev;
|
||||||
|
alg->u.hk.warn = FALSE;
|
||||||
|
}
|
||||||
|
if (can_gssapi_keyex) {
|
||||||
|
alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY], "null");
|
||||||
|
alg->u.hk.hostkey = NULL;
|
||||||
|
}
|
||||||
|
/* List encryption algorithms (client->server then server->client). */
|
||||||
|
for (k = KEXLIST_CSCIPHER; k <= KEXLIST_SCCIPHER; k++) {
|
||||||
|
warn = FALSE;
|
||||||
|
#ifdef FUZZING
|
||||||
|
alg = ssh2_kexinit_addalg(kexlists[k], "none");
|
||||||
|
alg->u.cipher.cipher = NULL;
|
||||||
|
alg->u.cipher.warn = warn;
|
||||||
|
#endif /* FUZZING */
|
||||||
|
for (i = 0; i < n_preferred_ciphers; i++) {
|
||||||
|
const struct ssh2_ciphers *c = preferred_ciphers[i];
|
||||||
|
if (!c) warn = TRUE;
|
||||||
|
else for (j = 0; j < c->nciphers; j++) {
|
||||||
|
alg = ssh2_kexinit_addalg(kexlists[k],
|
||||||
|
c->list[j]->name);
|
||||||
|
alg->u.cipher.cipher = c->list[j];
|
||||||
|
alg->u.cipher.warn = warn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Be prepared to work around the buggy MAC problem.
|
||||||
|
*/
|
||||||
|
if (remote_bugs & BUG_SSH2_HMAC) {
|
||||||
|
maclist = buggymacs;
|
||||||
|
nmacs = lenof(buggymacs);
|
||||||
|
} else {
|
||||||
|
maclist = macs;
|
||||||
|
nmacs = lenof(macs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* List MAC algorithms (client->server then server->client). */
|
||||||
|
for (j = KEXLIST_CSMAC; j <= KEXLIST_SCMAC; j++) {
|
||||||
|
#ifdef FUZZING
|
||||||
|
alg = ssh2_kexinit_addalg(kexlists[j], "none");
|
||||||
|
alg->u.mac.mac = NULL;
|
||||||
|
alg->u.mac.etm = FALSE;
|
||||||
|
#endif /* FUZZING */
|
||||||
|
for (i = 0; i < nmacs; i++) {
|
||||||
|
alg = ssh2_kexinit_addalg(kexlists[j], maclist[i]->name);
|
||||||
|
alg->u.mac.mac = maclist[i];
|
||||||
|
alg->u.mac.etm = FALSE;
|
||||||
|
}
|
||||||
|
for (i = 0; i < nmacs; i++) {
|
||||||
|
/* For each MAC, there may also be an ETM version,
|
||||||
|
* which we list second. */
|
||||||
|
if (maclist[i]->etm_name) {
|
||||||
|
alg = ssh2_kexinit_addalg(kexlists[j], maclist[i]->etm_name);
|
||||||
|
alg->u.mac.mac = maclist[i];
|
||||||
|
alg->u.mac.etm = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* List client->server compression algorithms,
|
||||||
|
* then server->client compression algorithms. (We use the
|
||||||
|
* same set twice.) */
|
||||||
|
for (j = KEXLIST_CSCOMP; j <= KEXLIST_SCCOMP; j++) {
|
||||||
|
assert(lenof(compressions) > 1);
|
||||||
|
/* Prefer non-delayed versions */
|
||||||
|
alg = ssh2_kexinit_addalg(kexlists[j], preferred_comp->name);
|
||||||
|
alg->u.comp.comp = preferred_comp;
|
||||||
|
alg->u.comp.delayed = FALSE;
|
||||||
|
if (preferred_comp->delayed_name) {
|
||||||
|
alg = ssh2_kexinit_addalg(kexlists[j],
|
||||||
|
preferred_comp->delayed_name);
|
||||||
|
alg->u.comp.comp = preferred_comp;
|
||||||
|
alg->u.comp.delayed = TRUE;
|
||||||
|
}
|
||||||
|
for (i = 0; i < lenof(compressions); i++) {
|
||||||
|
const struct ssh_compression_alg *c = compressions[i];
|
||||||
|
alg = ssh2_kexinit_addalg(kexlists[j], c->name);
|
||||||
|
alg->u.comp.comp = c;
|
||||||
|
alg->u.comp.delayed = FALSE;
|
||||||
|
if (c->delayed_name) {
|
||||||
|
alg = ssh2_kexinit_addalg(kexlists[j], c->delayed_name);
|
||||||
|
alg->u.comp.comp = c;
|
||||||
|
alg->u.comp.delayed = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Finally, format the lists into text and write them into the
|
||||||
|
* outgoing KEXINIT packet.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < NKEXLIST; i++) {
|
||||||
|
strbuf *list = strbuf_new();
|
||||||
|
for (j = 0; j < MAXKEXLIST; j++) {
|
||||||
|
if (kexlists[i][j].name == NULL) break;
|
||||||
|
add_to_commasep(list, kexlists[i][j].name);
|
||||||
|
}
|
||||||
|
put_stringsb(pktout, list);
|
||||||
|
}
|
||||||
|
/* List client->server languages. Empty list. */
|
||||||
|
put_stringz(pktout, "");
|
||||||
|
/* List server->client languages. Empty list. */
|
||||||
|
put_stringz(pktout, "");
|
||||||
|
}
|
||||||
|
|
||||||
static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
|
static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
|
||||||
{
|
{
|
||||||
struct ssh2_transport_state *s =
|
struct ssh2_transport_state *s =
|
||||||
@ -607,14 +925,6 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
|
|||||||
s->need_gss_transient_hostkey = FALSE;
|
s->need_gss_transient_hostkey = FALSE;
|
||||||
s->warned_about_no_gss_transient_hostkey = FALSE;
|
s->warned_about_no_gss_transient_hostkey = FALSE;
|
||||||
|
|
||||||
/*
|
|
||||||
* Be prepared to work around the buggy MAC problem.
|
|
||||||
*/
|
|
||||||
if (s->ppl.remote_bugs & BUG_SSH2_HMAC)
|
|
||||||
s->maclist = buggymacs, s->nmacs = lenof(buggymacs);
|
|
||||||
else
|
|
||||||
s->maclist = macs, s->nmacs = lenof(macs);
|
|
||||||
|
|
||||||
begin_key_exchange:
|
begin_key_exchange:
|
||||||
|
|
||||||
#ifndef NO_GSSAPI
|
#ifndef NO_GSSAPI
|
||||||
@ -665,316 +975,55 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
s->ppl.bpp->pls->kctx = SSH2_PKTCTX_NOKEX;
|
s->ppl.bpp->pls->kctx = SSH2_PKTCTX_NOKEX;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Construct our KEXINIT packet, in a strbuf so we can refer to it
|
||||||
|
* later.
|
||||||
|
*/
|
||||||
|
s->client_kexinit->len = 0;
|
||||||
|
put_byte(s->client_kexinit, SSH2_MSG_KEXINIT);
|
||||||
{
|
{
|
||||||
int i, j, k, warn;
|
int i;
|
||||||
struct kexinit_algorithm *alg;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set up the preferred key exchange. (NULL => warn below here)
|
|
||||||
*/
|
|
||||||
s->n_preferred_kex = 0;
|
|
||||||
if (s->can_gssapi_keyex)
|
|
||||||
s->preferred_kex[s->n_preferred_kex++] = &ssh_gssk5_sha1_kex;
|
|
||||||
for (i = 0; i < KEX_MAX; i++) {
|
|
||||||
switch (conf_get_int_int(s->conf, CONF_ssh_kexlist, i)) {
|
|
||||||
case KEX_DHGEX:
|
|
||||||
s->preferred_kex[s->n_preferred_kex++] =
|
|
||||||
&ssh_diffiehellman_gex;
|
|
||||||
break;
|
|
||||||
case KEX_DHGROUP14:
|
|
||||||
s->preferred_kex[s->n_preferred_kex++] =
|
|
||||||
&ssh_diffiehellman_group14;
|
|
||||||
break;
|
|
||||||
case KEX_DHGROUP1:
|
|
||||||
s->preferred_kex[s->n_preferred_kex++] =
|
|
||||||
&ssh_diffiehellman_group1;
|
|
||||||
break;
|
|
||||||
case KEX_RSA:
|
|
||||||
s->preferred_kex[s->n_preferred_kex++] =
|
|
||||||
&ssh_rsa_kex;
|
|
||||||
break;
|
|
||||||
case KEX_ECDH:
|
|
||||||
s->preferred_kex[s->n_preferred_kex++] =
|
|
||||||
&ssh_ecdh_kex;
|
|
||||||
break;
|
|
||||||
case KEX_WARN:
|
|
||||||
/* Flag for later. Don't bother if it's the last in
|
|
||||||
* the list. */
|
|
||||||
if (i < KEX_MAX - 1) {
|
|
||||||
s->preferred_kex[s->n_preferred_kex++] = NULL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set up the preferred host key types. These are just the ids
|
|
||||||
* in the enum in putty.h, so 'warn below here' is indicated
|
|
||||||
* by HK_WARN.
|
|
||||||
*/
|
|
||||||
s->n_preferred_hk = 0;
|
|
||||||
for (i = 0; i < HK_MAX; i++) {
|
|
||||||
int id = conf_get_int_int(s->conf, CONF_ssh_hklist, i);
|
|
||||||
/* As above, don't bother with HK_WARN if it's last in the
|
|
||||||
* list */
|
|
||||||
if (id != HK_WARN || i < HK_MAX - 1)
|
|
||||||
s->preferred_hk[s->n_preferred_hk++] = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set up the preferred ciphers. (NULL => warn below here)
|
|
||||||
*/
|
|
||||||
s->n_preferred_ciphers = 0;
|
|
||||||
for (i = 0; i < CIPHER_MAX; i++) {
|
|
||||||
switch (conf_get_int_int(s->conf, CONF_ssh_cipherlist, i)) {
|
|
||||||
case CIPHER_BLOWFISH:
|
|
||||||
s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_blowfish;
|
|
||||||
break;
|
|
||||||
case CIPHER_DES:
|
|
||||||
if (conf_get_int(s->conf, CONF_ssh2_des_cbc))
|
|
||||||
s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_des;
|
|
||||||
break;
|
|
||||||
case CIPHER_3DES:
|
|
||||||
s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_3des;
|
|
||||||
break;
|
|
||||||
case CIPHER_AES:
|
|
||||||
s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_aes;
|
|
||||||
break;
|
|
||||||
case CIPHER_ARCFOUR:
|
|
||||||
s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_arcfour;
|
|
||||||
break;
|
|
||||||
case CIPHER_CHACHA20:
|
|
||||||
s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_ccp;
|
|
||||||
break;
|
|
||||||
case CIPHER_WARN:
|
|
||||||
/* Flag for later. Don't bother if it's the last in
|
|
||||||
* the list. */
|
|
||||||
if (i < CIPHER_MAX - 1) {
|
|
||||||
s->preferred_ciphers[s->n_preferred_ciphers++] = NULL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set up preferred compression.
|
|
||||||
*/
|
|
||||||
if (conf_get_int(s->conf, CONF_compression))
|
|
||||||
s->preferred_comp = &ssh_zlib;
|
|
||||||
else
|
|
||||||
s->preferred_comp = &ssh_comp_none;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Flag that KEX is in progress.
|
|
||||||
*/
|
|
||||||
s->kex_in_progress = TRUE;
|
|
||||||
|
|
||||||
for (i = 0; i < NKEXLIST; i++)
|
|
||||||
for (j = 0; j < MAXKEXLIST; j++)
|
|
||||||
s->kexlists[i][j].name = NULL;
|
|
||||||
/* List key exchange algorithms. */
|
|
||||||
warn = FALSE;
|
|
||||||
for (i = 0; i < s->n_preferred_kex; i++) {
|
|
||||||
const struct ssh_kexes *k = s->preferred_kex[i];
|
|
||||||
if (!k) warn = TRUE;
|
|
||||||
else for (j = 0; j < k->nkexes; j++) {
|
|
||||||
alg = ssh2_kexinit_addalg(s->kexlists[KEXLIST_KEX],
|
|
||||||
k->list[j]->name);
|
|
||||||
alg->u.kex.kex = k->list[j];
|
|
||||||
alg->u.kex.warn = warn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* List server host key algorithms. */
|
|
||||||
if (!s->got_session_id) {
|
|
||||||
/*
|
|
||||||
* In the first key exchange, we list all the algorithms
|
|
||||||
* we're prepared to cope with, but prefer those algorithms
|
|
||||||
* for which we have a host key for this host.
|
|
||||||
*
|
|
||||||
* If the host key algorithm is below the warning
|
|
||||||
* threshold, we warn even if we did already have a key
|
|
||||||
* for it, on the basis that if the user has just
|
|
||||||
* reconfigured that host key type to be warned about,
|
|
||||||
* they surely _do_ want to be alerted that a server
|
|
||||||
* they're actually connecting to is using it.
|
|
||||||
*/
|
|
||||||
warn = FALSE;
|
|
||||||
for (i = 0; i < s->n_preferred_hk; i++) {
|
|
||||||
if (s->preferred_hk[i] == HK_WARN)
|
|
||||||
warn = TRUE;
|
|
||||||
for (j = 0; j < lenof(hostkey_algs); j++) {
|
|
||||||
if (hostkey_algs[j].id != s->preferred_hk[i])
|
|
||||||
continue;
|
|
||||||
if (have_ssh_host_key(s->savedhost, s->savedport,
|
|
||||||
hostkey_algs[j].alg->cache_id)) {
|
|
||||||
alg = ssh2_kexinit_addalg(s->kexlists[KEXLIST_HOSTKEY],
|
|
||||||
hostkey_algs[j].alg->ssh_id);
|
|
||||||
alg->u.hk.hostkey = hostkey_algs[j].alg;
|
|
||||||
alg->u.hk.warn = warn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
warn = FALSE;
|
|
||||||
for (i = 0; i < s->n_preferred_hk; i++) {
|
|
||||||
if (s->preferred_hk[i] == HK_WARN)
|
|
||||||
warn = TRUE;
|
|
||||||
for (j = 0; j < lenof(hostkey_algs); j++) {
|
|
||||||
if (hostkey_algs[j].id != s->preferred_hk[i])
|
|
||||||
continue;
|
|
||||||
alg = ssh2_kexinit_addalg(s->kexlists[KEXLIST_HOSTKEY],
|
|
||||||
hostkey_algs[j].alg->ssh_id);
|
|
||||||
alg->u.hk.hostkey = hostkey_algs[j].alg;
|
|
||||||
alg->u.hk.warn = warn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#ifndef NO_GSSAPI
|
|
||||||
} else if (s->gss_kex_used && !s->need_gss_transient_hostkey) {
|
|
||||||
/*
|
|
||||||
* If we've previously done a GSSAPI KEX, then we list
|
|
||||||
* precisely the algorithms for which a previous GSS key
|
|
||||||
* exchange has delivered us a host key, because we expect
|
|
||||||
* one of exactly those keys to be used in any subsequent
|
|
||||||
* non-GSS-based rekey.
|
|
||||||
*
|
|
||||||
* An exception is if this is the key exchange we
|
|
||||||
* triggered for the purposes of populating that cache -
|
|
||||||
* in which case the cache will currently be empty, which
|
|
||||||
* isn't helpful!
|
|
||||||
*/
|
|
||||||
warn = FALSE;
|
|
||||||
for (i = 0; i < s->n_preferred_hk; i++) {
|
|
||||||
if (s->preferred_hk[i] == HK_WARN)
|
|
||||||
warn = TRUE;
|
|
||||||
for (j = 0; j < lenof(hostkey_algs); j++) {
|
|
||||||
if (hostkey_algs[j].id != s->preferred_hk[i])
|
|
||||||
continue;
|
|
||||||
if (ssh_transient_hostkey_cache_has(
|
|
||||||
s->thc, hostkey_algs[j].alg)) {
|
|
||||||
alg = ssh2_kexinit_addalg(s->kexlists[KEXLIST_HOSTKEY],
|
|
||||||
hostkey_algs[j].alg->ssh_id);
|
|
||||||
alg->u.hk.hostkey = hostkey_algs[j].alg;
|
|
||||||
alg->u.hk.warn = warn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
* In subsequent key exchanges, we list only the kex
|
|
||||||
* algorithm that was selected in the first key exchange,
|
|
||||||
* so that we keep getting the same host key and hence
|
|
||||||
* don't have to interrupt the user's session to ask for
|
|
||||||
* reverification.
|
|
||||||
*/
|
|
||||||
assert(s->kex_alg);
|
|
||||||
alg = ssh2_kexinit_addalg(s->kexlists[KEXLIST_HOSTKEY],
|
|
||||||
s->hostkey_alg->ssh_id);
|
|
||||||
alg->u.hk.hostkey = s->hostkey_alg;
|
|
||||||
alg->u.hk.warn = FALSE;
|
|
||||||
}
|
|
||||||
if (s->can_gssapi_keyex) {
|
|
||||||
alg = ssh2_kexinit_addalg(s->kexlists[KEXLIST_HOSTKEY], "null");
|
|
||||||
alg->u.hk.hostkey = NULL;
|
|
||||||
}
|
|
||||||
/* List encryption algorithms (client->server then server->client). */
|
|
||||||
for (k = KEXLIST_CSCIPHER; k <= KEXLIST_SCCIPHER; k++) {
|
|
||||||
warn = FALSE;
|
|
||||||
#ifdef FUZZING
|
|
||||||
alg = ssh2_kexinit_addalg(s->kexlists[k], "none");
|
|
||||||
alg->u.cipher.cipher = NULL;
|
|
||||||
alg->u.cipher.warn = warn;
|
|
||||||
#endif /* FUZZING */
|
|
||||||
for (i = 0; i < s->n_preferred_ciphers; i++) {
|
|
||||||
const struct ssh2_ciphers *c = s->preferred_ciphers[i];
|
|
||||||
if (!c) warn = TRUE;
|
|
||||||
else for (j = 0; j < c->nciphers; j++) {
|
|
||||||
alg = ssh2_kexinit_addalg(s->kexlists[k],
|
|
||||||
c->list[j]->name);
|
|
||||||
alg->u.cipher.cipher = c->list[j];
|
|
||||||
alg->u.cipher.warn = warn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* List MAC algorithms (client->server then server->client). */
|
|
||||||
for (j = KEXLIST_CSMAC; j <= KEXLIST_SCMAC; j++) {
|
|
||||||
#ifdef FUZZING
|
|
||||||
alg = ssh2_kexinit_addalg(s->kexlists[j], "none");
|
|
||||||
alg->u.mac.mac = NULL;
|
|
||||||
alg->u.mac.etm = FALSE;
|
|
||||||
#endif /* FUZZING */
|
|
||||||
for (i = 0; i < s->nmacs; i++) {
|
|
||||||
alg = ssh2_kexinit_addalg(s->kexlists[j], s->maclist[i]->name);
|
|
||||||
alg->u.mac.mac = s->maclist[i];
|
|
||||||
alg->u.mac.etm = FALSE;
|
|
||||||
}
|
|
||||||
for (i = 0; i < s->nmacs; i++)
|
|
||||||
/* For each MAC, there may also be an ETM version,
|
|
||||||
* which we list second. */
|
|
||||||
if (s->maclist[i]->etm_name) {
|
|
||||||
alg = ssh2_kexinit_addalg(s->kexlists[j],
|
|
||||||
s->maclist[i]->etm_name);
|
|
||||||
alg->u.mac.mac = s->maclist[i];
|
|
||||||
alg->u.mac.etm = TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* List client->server compression algorithms,
|
|
||||||
* then server->client compression algorithms. (We use the
|
|
||||||
* same set twice.) */
|
|
||||||
for (j = KEXLIST_CSCOMP; j <= KEXLIST_SCCOMP; j++) {
|
|
||||||
assert(lenof(compressions) > 1);
|
|
||||||
/* Prefer non-delayed versions */
|
|
||||||
alg = ssh2_kexinit_addalg(s->kexlists[j], s->preferred_comp->name);
|
|
||||||
alg->u.comp.comp = s->preferred_comp;
|
|
||||||
alg->u.comp.delayed = FALSE;
|
|
||||||
if (s->preferred_comp->delayed_name) {
|
|
||||||
alg = ssh2_kexinit_addalg(s->kexlists[j],
|
|
||||||
s->preferred_comp->delayed_name);
|
|
||||||
alg->u.comp.comp = s->preferred_comp;
|
|
||||||
alg->u.comp.delayed = TRUE;
|
|
||||||
}
|
|
||||||
for (i = 0; i < lenof(compressions); i++) {
|
|
||||||
const struct ssh_compression_alg *c = compressions[i];
|
|
||||||
alg = ssh2_kexinit_addalg(s->kexlists[j], c->name);
|
|
||||||
alg->u.comp.comp = c;
|
|
||||||
alg->u.comp.delayed = FALSE;
|
|
||||||
if (c->delayed_name) {
|
|
||||||
alg = ssh2_kexinit_addalg(s->kexlists[j], c->delayed_name);
|
|
||||||
alg->u.comp.comp = c;
|
|
||||||
alg->u.comp.delayed = TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* Construct and send our key exchange packet.
|
|
||||||
*/
|
|
||||||
pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_KEXINIT);
|
|
||||||
for (i = 0; i < 16; i++)
|
for (i = 0; i < 16; i++)
|
||||||
put_byte(pktout, (unsigned char) random_byte());
|
put_byte(s->client_kexinit, (unsigned char) random_byte());
|
||||||
for (i = 0; i < NKEXLIST; i++) {
|
|
||||||
strbuf *list = strbuf_new();
|
|
||||||
for (j = 0; j < MAXKEXLIST; j++) {
|
|
||||||
if (s->kexlists[i][j].name == NULL) break;
|
|
||||||
add_to_commasep(list, s->kexlists[i][j].name);
|
|
||||||
}
|
|
||||||
put_stringsb(pktout, list);
|
|
||||||
}
|
|
||||||
/* List client->server languages. Empty list. */
|
|
||||||
put_stringz(pktout, "");
|
|
||||||
/* List server->client languages. Empty list. */
|
|
||||||
put_stringz(pktout, "");
|
|
||||||
/* First KEX packet does _not_ follow, because we're not that brave. */
|
|
||||||
put_bool(pktout, FALSE);
|
|
||||||
/* Reserved. */
|
|
||||||
put_uint32(pktout, 0);
|
|
||||||
}
|
}
|
||||||
|
ssh2_write_kexinit_lists(
|
||||||
|
BinarySink_UPCAST(s->client_kexinit), s->kexlists,
|
||||||
|
s->conf, s->ppl.remote_bugs,
|
||||||
|
s->savedhost, s->savedport, s->hostkey_alg, s->thc,
|
||||||
|
!s->got_session_id, s->can_gssapi_keyex,
|
||||||
|
s->gss_kex_used && !s->need_gss_transient_hostkey);
|
||||||
|
/* First KEX packet does _not_ follow, because we're not that brave. */
|
||||||
|
put_bool(s->client_kexinit, FALSE);
|
||||||
|
put_uint32(s->client_kexinit, 0); /* reserved */
|
||||||
|
|
||||||
s->our_kexinitlen = pktout->length - 5;
|
/*
|
||||||
s->our_kexinit = snewn(s->our_kexinitlen, unsigned char);
|
* Send our KEXINIT.
|
||||||
memcpy(s->our_kexinit, pktout->data + 5, s->our_kexinitlen);
|
*/
|
||||||
|
pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_KEXINIT);
|
||||||
|
put_data(pktout, s->client_kexinit->u + 1,
|
||||||
|
s->client_kexinit->len - 1); /* omit initial packet type byte */
|
||||||
pq_push(s->ppl.out_pq, pktout);
|
pq_push(s->ppl.out_pq, pktout);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Flag that KEX is in progress.
|
||||||
|
*/
|
||||||
|
s->kex_in_progress = TRUE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wait for the other side's KEXINIT, and save it.
|
||||||
|
*/
|
||||||
crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL);
|
crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL);
|
||||||
|
if (pktin->type != SSH2_MSG_KEXINIT) {
|
||||||
|
ssh_proto_error(s->ppl.ssh, "Received unexpected packet when "
|
||||||
|
"expecting KEXINIT, type %d (%s)", pktin->type,
|
||||||
|
ssh2_pkt_type(s->ppl.bpp->pls->kctx,
|
||||||
|
s->ppl.bpp->pls->actx, pktin->type));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
s->server_kexinit->len = 0;
|
||||||
|
put_byte(s->server_kexinit, SSH2_MSG_KEXINIT);
|
||||||
|
put_data(s->server_kexinit, get_ptr(pktin), get_avail(pktin));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now examine the other side's KEXINIT to see what we're up
|
* Now examine the other side's KEXINIT to see what we're up
|
||||||
@ -984,13 +1033,6 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
|
|||||||
ptrlen str;
|
ptrlen str;
|
||||||
int i, j;
|
int i, j;
|
||||||
|
|
||||||
if (pktin->type != SSH2_MSG_KEXINIT) {
|
|
||||||
ssh_proto_error(s->ppl.ssh, "Received unexpected packet when "
|
|
||||||
"expecting KEXINIT, type %d (%s)", pktin->type,
|
|
||||||
ssh2_pkt_type(s->ppl.bpp->pls->kctx,
|
|
||||||
s->ppl.bpp->pls->actx, pktin->type));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
s->kex_alg = NULL;
|
s->kex_alg = NULL;
|
||||||
s->hostkey_alg = NULL;
|
s->hostkey_alg = NULL;
|
||||||
s->in.cipher = s->out.cipher = NULL;
|
s->in.cipher = s->out.cipher = NULL;
|
||||||
@ -1118,12 +1160,8 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
|
|||||||
s->exhash = ssh_hash_new(s->kex_alg->hash);
|
s->exhash = ssh_hash_new(s->kex_alg->hash);
|
||||||
put_stringz(s->exhash, s->client_greeting);
|
put_stringz(s->exhash, s->client_greeting);
|
||||||
put_stringz(s->exhash, s->server_greeting);
|
put_stringz(s->exhash, s->server_greeting);
|
||||||
put_string(s->exhash, s->our_kexinit, s->our_kexinitlen);
|
put_string(s->exhash, s->client_kexinit->u, s->client_kexinit->len);
|
||||||
sfree(s->our_kexinit);
|
put_string(s->exhash, s->server_kexinit->u, s->server_kexinit->len);
|
||||||
/* Include the type byte in the hash of server's KEXINIT */
|
|
||||||
put_string(s->exhash,
|
|
||||||
(const char *)BinarySource_UPCAST(pktin)->data - 1,
|
|
||||||
BinarySource_UPCAST(pktin)->len + 1);
|
|
||||||
|
|
||||||
if (s->warn_kex) {
|
if (s->warn_kex) {
|
||||||
s->dlgret = seat_confirm_weak_crypto_primitive(
|
s->dlgret = seat_confirm_weak_crypto_primitive(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user