mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +00:00
Factor KEXINIT analysis out into its own function.
The function takes the two KEXINIT packets in their string form, together with a list of mappings from names to known algorithm implementations, and returns the selected one of each kind, along with all the other necessary auxiliary stuff.
This commit is contained in:
parent
3df80af868
commit
7de8801e73
1
ssh.h
1
ssh.h
@ -1479,6 +1479,7 @@ int first_in_commasep_string(char const *needle, char const *haystack,
|
||||
int haylen);
|
||||
int in_commasep_string(char const *needle, char const *haystack, int haylen);
|
||||
void add_to_commasep(strbuf *buf, const char *data);
|
||||
int get_commasep_word(ptrlen *list, ptrlen *word);
|
||||
|
||||
int verify_ssh_manual_host_key(
|
||||
Conf *conf, const char *fingerprint, ssh_key *key);
|
||||
|
543
ssh2transport.c
543
ssh2transport.c
@ -146,6 +146,14 @@ typedef enum RekeyClass {
|
||||
RK_GSS_UPDATE
|
||||
} RekeyClass;
|
||||
|
||||
typedef struct transport_direction {
|
||||
const struct ssh2_cipheralg *cipher;
|
||||
const struct ssh2_macalg *mac;
|
||||
int etm_mode;
|
||||
const struct ssh_compression_alg *comp;
|
||||
int comp_delayed;
|
||||
} transport_direction;
|
||||
|
||||
struct ssh2_transport_state {
|
||||
int crState;
|
||||
|
||||
@ -201,13 +209,7 @@ struct ssh2_transport_state {
|
||||
Bignum p, g, e, f, K;
|
||||
strbuf *client_kexinit, *server_kexinit;
|
||||
int kex_init_value, kex_reply_value;
|
||||
struct {
|
||||
const struct ssh2_cipheralg *cipher;
|
||||
const struct ssh2_macalg *mac;
|
||||
int etm_mode;
|
||||
const struct ssh_compression_alg *comp;
|
||||
int comp_delayed;
|
||||
} in, out;
|
||||
transport_direction in, out;
|
||||
ptrlen hostkeydata, sigdata;
|
||||
char *keystr, *fingerprint;
|
||||
ssh_key *hkey; /* actual host key */
|
||||
@ -901,6 +903,217 @@ static void ssh2_write_kexinit_lists(
|
||||
put_stringz(pktout, "");
|
||||
}
|
||||
|
||||
static int ssh2_scan_kexinits(
|
||||
ptrlen client_kexinit, ptrlen server_kexinit,
|
||||
struct kexinit_algorithm kexlists[NKEXLIST][MAXKEXLIST],
|
||||
const struct ssh_kex **kex_alg, const ssh_keyalg **hostkey_alg,
|
||||
transport_direction *cs, transport_direction *sc,
|
||||
int *warn_kex, int *warn_hk, int *warn_cscipher, int *warn_sccipher,
|
||||
Ssh *ssh, int *ignore_guess_cs_packet, int *ignore_guess_sc_packet,
|
||||
int *n_server_hostkeys, int server_hostkeys[MAXKEXLIST])
|
||||
{
|
||||
BinarySource client[1], server[1];
|
||||
int i;
|
||||
int guess_correct;
|
||||
ptrlen clists[NKEXLIST], slists[NKEXLIST];
|
||||
const struct kexinit_algorithm *selected[NKEXLIST];
|
||||
|
||||
BinarySource_BARE_INIT(client, client_kexinit.ptr, client_kexinit.len);
|
||||
BinarySource_BARE_INIT(server, server_kexinit.ptr, server_kexinit.len);
|
||||
|
||||
/* Skip packet type bytes and random cookies. */
|
||||
get_data(client, 1 + 16);
|
||||
get_data(server, 1 + 16);
|
||||
|
||||
guess_correct = TRUE;
|
||||
|
||||
/* Find the matching string in each list, and map it to its
|
||||
* kexinit_algorithm structure. */
|
||||
for (i = 0; i < NKEXLIST; i++) {
|
||||
ptrlen clist, slist, cword, sword, found;
|
||||
int cfirst, sfirst, j;
|
||||
|
||||
clists[i] = get_string(client);
|
||||
slists[i] = get_string(server);
|
||||
if (get_err(client) || get_err(server)) {
|
||||
/* Report a better error than the spurious "Couldn't
|
||||
* agree" that we'd generate if we pressed on regardless
|
||||
* and treated the empty get_string() result as genuine */
|
||||
ssh_proto_error(ssh, "KEXINIT packet was incomplete");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for (cfirst = TRUE, clist = clists[i];
|
||||
get_commasep_word(&clist, &cword); cfirst = FALSE)
|
||||
for (sfirst = TRUE, slist = slists[i];
|
||||
get_commasep_word(&slist, &sword); sfirst = FALSE)
|
||||
if (ptrlen_eq_ptrlen(cword, sword)) {
|
||||
found = cword;
|
||||
goto found_match;
|
||||
}
|
||||
|
||||
/* No matching string found in the two lists. Delay reporting
|
||||
* a fatal error until below, because sometimes it turns out
|
||||
* not to be fatal. */
|
||||
selected[i] = NULL;
|
||||
|
||||
/*
|
||||
* However, even if a failure to agree on any algorithm at all
|
||||
* is not completely fatal (e.g. because it's the MAC
|
||||
* negotiation for a cipher that comes with a built-in MAC),
|
||||
* it still invalidates the guessed key exchange packet. (RFC
|
||||
* 4253 section 7, not contradicted by OpenSSH's
|
||||
* PROTOCOL.chacha20poly1305 or as far as I can see by their
|
||||
* code.)
|
||||
*/
|
||||
guess_correct = FALSE;
|
||||
|
||||
continue;
|
||||
|
||||
found_match:
|
||||
|
||||
selected[i] = NULL;
|
||||
for (j = 0; j < MAXKEXLIST; j++) {
|
||||
if (ptrlen_eq_string(found, kexlists[i][j].name)) {
|
||||
selected[i] = &kexlists[i][j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(selected[i]); /* kexlists[] must cover one of the inputs */
|
||||
|
||||
/*
|
||||
* If the kex or host key algorithm is not the first one in
|
||||
* both sides' lists, that means the guessed key exchange
|
||||
* packet (if any) is officially wrong.
|
||||
*/
|
||||
if ((i == KEXLIST_KEX || i == KEXLIST_HOSTKEY) && !(cfirst || sfirst))
|
||||
guess_correct = FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Skip language strings in both KEXINITs, and read the flags
|
||||
* saying whether a guessed KEX packet follows.
|
||||
*/
|
||||
get_string(client);
|
||||
get_string(client);
|
||||
get_string(server);
|
||||
get_string(server);
|
||||
if (ignore_guess_cs_packet)
|
||||
*ignore_guess_cs_packet = get_bool(client) && !guess_correct;
|
||||
if (ignore_guess_sc_packet)
|
||||
*ignore_guess_sc_packet = get_bool(server) && !guess_correct;
|
||||
|
||||
/*
|
||||
* Now transcribe the selected algorithm set into the output data.
|
||||
*/
|
||||
for (i = 0; i < NKEXLIST; i++) {
|
||||
const struct kexinit_algorithm *alg;
|
||||
|
||||
/*
|
||||
* If we've already selected a cipher which requires a
|
||||
* particular MAC, then just select that. This is the case in
|
||||
* which it's not a fatal error if the actual MAC string lists
|
||||
* didn't include any matching error.
|
||||
*/
|
||||
if (i == KEXLIST_CSMAC && cs->cipher &&
|
||||
cs->cipher->required_mac) {
|
||||
cs->mac = cs->cipher->required_mac;
|
||||
cs->etm_mode = !!(cs->mac->etm_name);
|
||||
continue;
|
||||
}
|
||||
if (i == KEXLIST_SCMAC && sc->cipher &&
|
||||
sc->cipher->required_mac) {
|
||||
sc->mac = sc->cipher->required_mac;
|
||||
sc->etm_mode = !!(sc->mac->etm_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
alg = selected[i];
|
||||
if (!alg) {
|
||||
/*
|
||||
* Otherwise, any match failure _is_ a fatal error.
|
||||
*/
|
||||
ssh_sw_abort(ssh, "Couldn't agree a %s (available: %.*s)",
|
||||
kexlist_descr[i], PTRLEN_PRINTF(slists[i]));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
switch (i) {
|
||||
case KEXLIST_KEX:
|
||||
*kex_alg = alg->u.kex.kex;
|
||||
*warn_kex = alg->u.kex.warn;
|
||||
break;
|
||||
|
||||
case KEXLIST_HOSTKEY:
|
||||
/*
|
||||
* Ignore an unexpected/inappropriate offer of "null",
|
||||
* we offer "null" when we're willing to use GSS KEX,
|
||||
* but it is only acceptable when GSSKEX is actually
|
||||
* selected.
|
||||
*/
|
||||
if (alg->u.hk.hostkey == NULL &&
|
||||
(*kex_alg)->main_type != KEXTYPE_GSS)
|
||||
continue;
|
||||
|
||||
*hostkey_alg = alg->u.hk.hostkey;
|
||||
*warn_hk = alg->u.hk.warn;
|
||||
break;
|
||||
|
||||
case KEXLIST_CSCIPHER:
|
||||
cs->cipher = alg->u.cipher.cipher;
|
||||
*warn_cscipher = alg->u.cipher.warn;
|
||||
break;
|
||||
|
||||
case KEXLIST_SCCIPHER:
|
||||
sc->cipher = alg->u.cipher.cipher;
|
||||
*warn_sccipher = alg->u.cipher.warn;
|
||||
break;
|
||||
|
||||
case KEXLIST_CSMAC:
|
||||
cs->mac = alg->u.mac.mac;
|
||||
cs->etm_mode = alg->u.mac.etm;
|
||||
break;
|
||||
|
||||
case KEXLIST_SCMAC:
|
||||
sc->mac = alg->u.mac.mac;
|
||||
sc->etm_mode = alg->u.mac.etm;
|
||||
break;
|
||||
|
||||
case KEXLIST_CSCOMP:
|
||||
cs->comp = alg->u.comp.comp;
|
||||
cs->comp_delayed = alg->u.comp.delayed;
|
||||
break;
|
||||
|
||||
case KEXLIST_SCCOMP:
|
||||
sc->comp = alg->u.comp.comp;
|
||||
sc->comp_delayed = alg->u.comp.delayed;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(FALSE && "Bad list index in scan_kexinits");
|
||||
}
|
||||
}
|
||||
|
||||
if (server_hostkeys) {
|
||||
/*
|
||||
* Finally, make an auxiliary pass over the server's host key
|
||||
* list to find all the host key algorithms offered by the
|
||||
* server which we know about at all, whether we selected each
|
||||
* one or not. We return these as a list of indices into the
|
||||
* constant hostkey_algs[] array.
|
||||
*/
|
||||
*n_server_hostkeys = 0;
|
||||
|
||||
for (i = 0; i < lenof(hostkey_algs); i++)
|
||||
if (in_commasep_string(hostkey_algs[i].alg->ssh_id,
|
||||
slists[KEXLIST_HOSTKEY].ptr,
|
||||
slists[KEXLIST_HOSTKEY].len))
|
||||
server_hostkeys[(*n_server_hostkeys)++] = i;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
|
||||
{
|
||||
struct ssh2_transport_state *s =
|
||||
@ -1026,238 +1239,146 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
|
||||
put_data(s->server_kexinit, get_ptr(pktin), get_avail(pktin));
|
||||
|
||||
/*
|
||||
* Now examine the other side's KEXINIT to see what we're up
|
||||
* to.
|
||||
* Work through the two KEXINIT packets in parallel to find the
|
||||
* selected algorithm identifiers.
|
||||
*/
|
||||
{
|
||||
ptrlen str;
|
||||
int i, j;
|
||||
int nhk, hks[MAXKEXLIST], i, j;
|
||||
|
||||
s->kex_alg = NULL;
|
||||
s->hostkey_alg = NULL;
|
||||
s->in.cipher = s->out.cipher = NULL;
|
||||
s->in.mac = s->out.mac = NULL;
|
||||
s->in.comp = s->out.comp = NULL;
|
||||
s->in.comp_delayed = s->out.comp_delayed = FALSE;
|
||||
s->warn_kex = s->warn_hk = FALSE;
|
||||
s->warn_cscipher = s->warn_sccipher = FALSE;
|
||||
if (!ssh2_scan_kexinits(
|
||||
ptrlen_from_strbuf(s->client_kexinit),
|
||||
ptrlen_from_strbuf(s->server_kexinit),
|
||||
s->kexlists, &s->kex_alg, &s->hostkey_alg, &s->out, &s->in,
|
||||
&s->warn_kex, &s->warn_hk, &s->warn_cscipher,
|
||||
&s->warn_sccipher, s->ppl.ssh, NULL, &s->ignorepkt, &nhk, hks))
|
||||
return; /* FALSE means a fatal error function was called */
|
||||
|
||||
get_data(pktin, 16); /* skip garbage cookie */
|
||||
/*
|
||||
* In addition to deciding which host key we're actually going
|
||||
* to use, we should make a list of the host keys offered by
|
||||
* the server which we _don't_ have cached. These will be
|
||||
* offered as cross-certification options by ssh_get_specials.
|
||||
*
|
||||
* We also count the key we're currently using for KEX as one
|
||||
* we've already got, because by the time this menu becomes
|
||||
* visible, it will be.
|
||||
*/
|
||||
s->n_uncert_hostkeys = 0;
|
||||
|
||||
s->guessok = FALSE;
|
||||
for (i = 0; i < NKEXLIST; i++) {
|
||||
str = get_string(pktin);
|
||||
if (get_err(pktin)) {
|
||||
ssh_proto_error(s->ppl.ssh, "KEXINIT packet was incomplete");
|
||||
return;
|
||||
for (i = 0; i < nhk; i++) {
|
||||
j = hks[i];
|
||||
if (hostkey_algs[j].alg != s->hostkey_alg &&
|
||||
!have_ssh_host_key(s->savedhost, s->savedport,
|
||||
hostkey_algs[j].alg->cache_id)) {
|
||||
s->uncert_hostkeys[s->n_uncert_hostkeys++] = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If we've already selected a cipher which requires a
|
||||
* particular MAC, then just select that, and don't even
|
||||
* bother looking through the server's KEXINIT string for
|
||||
* MACs. */
|
||||
if (i == KEXLIST_CSMAC && s->out.cipher &&
|
||||
s->out.cipher->required_mac) {
|
||||
s->out.mac = s->out.cipher->required_mac;
|
||||
s->out.etm_mode = !!(s->out.mac->etm_name);
|
||||
goto matched;
|
||||
}
|
||||
if (i == KEXLIST_SCMAC && s->in.cipher &&
|
||||
s->in.cipher->required_mac) {
|
||||
s->in.mac = s->in.cipher->required_mac;
|
||||
s->in.etm_mode = !!(s->in.mac->etm_name);
|
||||
goto matched;
|
||||
}
|
||||
s->exhash = ssh_hash_new(s->kex_alg->hash);
|
||||
put_stringz(s->exhash, s->client_greeting);
|
||||
put_stringz(s->exhash, s->server_greeting);
|
||||
put_string(s->exhash, s->client_kexinit->u, s->client_kexinit->len);
|
||||
put_string(s->exhash, s->server_kexinit->u, s->server_kexinit->len);
|
||||
|
||||
for (j = 0; j < MAXKEXLIST; j++) {
|
||||
struct kexinit_algorithm *alg = &s->kexlists[i][j];
|
||||
if (alg->name == NULL) break;
|
||||
if (in_commasep_string(alg->name, str.ptr, str.len)) {
|
||||
/* We've found a matching algorithm. */
|
||||
if (i == KEXLIST_KEX || i == KEXLIST_HOSTKEY) {
|
||||
/* Check if we might need to ignore first kex pkt */
|
||||
if (j != 0 ||
|
||||
!first_in_commasep_string(alg->name,
|
||||
str.ptr, str.len))
|
||||
s->guessok = FALSE;
|
||||
}
|
||||
if (i == KEXLIST_KEX) {
|
||||
s->kex_alg = alg->u.kex.kex;
|
||||
s->warn_kex = alg->u.kex.warn;
|
||||
} else if (i == KEXLIST_HOSTKEY) {
|
||||
/*
|
||||
* Ignore an unexpected/inappropriate offer of "null",
|
||||
* we offer "null" when we're willing to use GSS KEX,
|
||||
* but it is only acceptable when GSSKEX is actually
|
||||
* selected.
|
||||
*/
|
||||
if (alg->u.hk.hostkey == NULL &&
|
||||
s->kex_alg->main_type != KEXTYPE_GSS)
|
||||
continue;
|
||||
s->hostkey_alg = alg->u.hk.hostkey;
|
||||
s->warn_hk = alg->u.hk.warn;
|
||||
} else if (i == KEXLIST_CSCIPHER) {
|
||||
s->out.cipher = alg->u.cipher.cipher;
|
||||
s->warn_cscipher = alg->u.cipher.warn;
|
||||
} else if (i == KEXLIST_SCCIPHER) {
|
||||
s->in.cipher = alg->u.cipher.cipher;
|
||||
s->warn_sccipher = alg->u.cipher.warn;
|
||||
} else if (i == KEXLIST_CSMAC) {
|
||||
s->out.mac = alg->u.mac.mac;
|
||||
s->out.etm_mode = alg->u.mac.etm;
|
||||
} else if (i == KEXLIST_SCMAC) {
|
||||
s->in.mac = alg->u.mac.mac;
|
||||
s->in.etm_mode = alg->u.mac.etm;
|
||||
} else if (i == KEXLIST_CSCOMP) {
|
||||
s->out.comp = alg->u.comp.comp;
|
||||
s->out.comp_delayed = alg->u.comp.delayed;
|
||||
} else if (i == KEXLIST_SCCOMP) {
|
||||
s->in.comp = alg->u.comp.comp;
|
||||
s->in.comp_delayed = alg->u.comp.delayed;
|
||||
}
|
||||
goto matched;
|
||||
}
|
||||
}
|
||||
ssh_sw_abort(s->ppl.ssh, "Couldn't agree a %s (available: %.*s)",
|
||||
kexlist_descr[i], PTRLEN_PRINTF(str));
|
||||
if (s->warn_kex) {
|
||||
s->dlgret = seat_confirm_weak_crypto_primitive(
|
||||
s->ppl.seat, "key-exchange algorithm", s->kex_alg->name,
|
||||
ssh2_transport_dialog_callback, s);
|
||||
crMaybeWaitUntilV(s->dlgret >= 0);
|
||||
if (s->dlgret == 0) {
|
||||
ssh_user_close(s->ppl.ssh, "User aborted at kex warning");
|
||||
return;
|
||||
matched:;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == KEXLIST_HOSTKEY &&
|
||||
!s->gss_kex_used &&
|
||||
s->kex_alg->main_type != KEXTYPE_GSS) {
|
||||
int j;
|
||||
if (s->warn_hk) {
|
||||
int j, k;
|
||||
char *betteralgs;
|
||||
|
||||
/*
|
||||
* In addition to deciding which host key we're
|
||||
* actually going to use, we should make a list of the
|
||||
* host keys offered by the server which we _don't_
|
||||
* have cached. These will be offered as cross-
|
||||
* certification options by ssh_get_specials.
|
||||
*
|
||||
* We also count the key we're currently using for KEX
|
||||
* as one we've already got, because by the time this
|
||||
* menu becomes visible, it will be.
|
||||
*/
|
||||
s->n_uncert_hostkeys = 0;
|
||||
|
||||
for (j = 0; j < lenof(hostkey_algs); j++) {
|
||||
if (hostkey_algs[j].alg != s->hostkey_alg &&
|
||||
in_commasep_string(hostkey_algs[j].alg->ssh_id,
|
||||
str.ptr, str.len) &&
|
||||
!have_ssh_host_key(s->savedhost, s->savedport,
|
||||
hostkey_algs[j].alg->cache_id)) {
|
||||
s->uncert_hostkeys[s->n_uncert_hostkeys++] = j;
|
||||
}
|
||||
/*
|
||||
* Change warning box wording depending on why we chose a
|
||||
* warning-level host key algorithm. If it's because
|
||||
* that's all we have *cached*, list the host keys we
|
||||
* could usefully cross-certify. Otherwise, use the same
|
||||
* standard wording as any other weak crypto primitive.
|
||||
*/
|
||||
betteralgs = NULL;
|
||||
for (j = 0; j < s->n_uncert_hostkeys; j++) {
|
||||
const struct ssh_signkey_with_user_pref_id *hktype =
|
||||
&hostkey_algs[s->uncert_hostkeys[j]];
|
||||
int better = FALSE;
|
||||
for (k = 0; k < HK_MAX; k++) {
|
||||
int id = conf_get_int_int(s->conf, CONF_ssh_hklist, k);
|
||||
if (id == HK_WARN) {
|
||||
break;
|
||||
} else if (id == hktype->id) {
|
||||
better = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (better) {
|
||||
if (betteralgs) {
|
||||
char *old_ba = betteralgs;
|
||||
betteralgs = dupcat(betteralgs, ",",
|
||||
hktype->alg->ssh_id,
|
||||
(const char *)NULL);
|
||||
sfree(old_ba);
|
||||
} else {
|
||||
betteralgs = dupstr(hktype->alg->ssh_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get_string(pktin); /* client->server language */
|
||||
get_string(pktin); /* server->client language */
|
||||
s->ignorepkt = get_bool(pktin) && !s->guessok;
|
||||
|
||||
s->exhash = ssh_hash_new(s->kex_alg->hash);
|
||||
put_stringz(s->exhash, s->client_greeting);
|
||||
put_stringz(s->exhash, s->server_greeting);
|
||||
put_string(s->exhash, s->client_kexinit->u, s->client_kexinit->len);
|
||||
put_string(s->exhash, s->server_kexinit->u, s->server_kexinit->len);
|
||||
|
||||
if (s->warn_kex) {
|
||||
s->dlgret = seat_confirm_weak_crypto_primitive(
|
||||
s->ppl.seat, "key-exchange algorithm", s->kex_alg->name,
|
||||
ssh2_transport_dialog_callback, s);
|
||||
crMaybeWaitUntilV(s->dlgret >= 0);
|
||||
if (s->dlgret == 0) {
|
||||
ssh_user_close(s->ppl.ssh, "User aborted at kex warning");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (s->warn_hk) {
|
||||
int j, k;
|
||||
char *betteralgs;
|
||||
|
||||
/*
|
||||
* Change warning box wording depending on why we chose a
|
||||
* warning-level host key algorithm. If it's because
|
||||
* that's all we have *cached*, list the host keys we
|
||||
* could usefully cross-certify. Otherwise, use the same
|
||||
* standard wording as any other weak crypto primitive.
|
||||
*/
|
||||
betteralgs = NULL;
|
||||
for (j = 0; j < s->n_uncert_hostkeys; j++) {
|
||||
const struct ssh_signkey_with_user_pref_id *hktype =
|
||||
&hostkey_algs[s->uncert_hostkeys[j]];
|
||||
int better = FALSE;
|
||||
for (k = 0; k < HK_MAX; k++) {
|
||||
int id = conf_get_int_int(s->conf, CONF_ssh_hklist, k);
|
||||
if (id == HK_WARN) {
|
||||
break;
|
||||
} else if (id == hktype->id) {
|
||||
better = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (better) {
|
||||
if (betteralgs) {
|
||||
char *old_ba = betteralgs;
|
||||
betteralgs = dupcat(betteralgs, ",",
|
||||
hktype->alg->ssh_id,
|
||||
(const char *)NULL);
|
||||
sfree(old_ba);
|
||||
} else {
|
||||
betteralgs = dupstr(hktype->alg->ssh_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (betteralgs) {
|
||||
if (betteralgs) {
|
||||
/* Use the special warning prompt that lets us provide
|
||||
* a list of better algorithms */
|
||||
s->dlgret = seat_confirm_weak_cached_hostkey(
|
||||
s->ppl.seat, s->hostkey_alg->ssh_id, betteralgs,
|
||||
ssh2_transport_dialog_callback, s);
|
||||
sfree(betteralgs);
|
||||
} else {
|
||||
sfree(betteralgs);
|
||||
} else {
|
||||
/* If none exist, use the more general 'weak crypto'
|
||||
* warning prompt */
|
||||
s->dlgret = seat_confirm_weak_crypto_primitive(
|
||||
s->ppl.seat, "host key type", s->hostkey_alg->ssh_id,
|
||||
ssh2_transport_dialog_callback, s);
|
||||
}
|
||||
crMaybeWaitUntilV(s->dlgret >= 0);
|
||||
if (s->dlgret == 0) {
|
||||
ssh_user_close(s->ppl.ssh, "User aborted at host key warning");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (s->warn_cscipher) {
|
||||
s->dlgret = seat_confirm_weak_crypto_primitive(
|
||||
s->ppl.seat, "client-to-server cipher", s->out.cipher->name,
|
||||
ssh2_transport_dialog_callback, s);
|
||||
crMaybeWaitUntilV(s->dlgret >= 0);
|
||||
if (s->dlgret == 0) {
|
||||
ssh_user_close(s->ppl.ssh, "User aborted at cipher warning");
|
||||
return;
|
||||
}
|
||||
crMaybeWaitUntilV(s->dlgret >= 0);
|
||||
if (s->dlgret == 0) {
|
||||
ssh_user_close(s->ppl.ssh, "User aborted at host key warning");
|
||||
return;
|
||||
}
|
||||
|
||||
if (s->warn_sccipher) {
|
||||
s->dlgret = seat_confirm_weak_crypto_primitive(
|
||||
s->ppl.seat, "server-to-client cipher", s->in.cipher->name,
|
||||
ssh2_transport_dialog_callback, s);
|
||||
crMaybeWaitUntilV(s->dlgret >= 0);
|
||||
if (s->dlgret == 0) {
|
||||
ssh_user_close(s->ppl.ssh, "User aborted at cipher warning");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (s->ignorepkt) /* first_kex_packet_follows */
|
||||
crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL);
|
||||
}
|
||||
|
||||
if (s->warn_cscipher) {
|
||||
s->dlgret = seat_confirm_weak_crypto_primitive(
|
||||
s->ppl.seat, "client-to-server cipher", s->out.cipher->name,
|
||||
ssh2_transport_dialog_callback, s);
|
||||
crMaybeWaitUntilV(s->dlgret >= 0);
|
||||
if (s->dlgret == 0) {
|
||||
ssh_user_close(s->ppl.ssh, "User aborted at cipher warning");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (s->warn_sccipher) {
|
||||
s->dlgret = seat_confirm_weak_crypto_primitive(
|
||||
s->ppl.seat, "server-to-client cipher", s->in.cipher->name,
|
||||
ssh2_transport_dialog_callback, s);
|
||||
crMaybeWaitUntilV(s->dlgret >= 0);
|
||||
if (s->dlgret == 0) {
|
||||
ssh_user_close(s->ppl.ssh, "User aborted at cipher warning");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If the other side has sent an initial key exchange packet that
|
||||
* we must treat as a wrong guess, wait for it, and discard it.
|
||||
*/
|
||||
if (s->ignorepkt)
|
||||
crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL);
|
||||
|
||||
if (s->kex_alg->main_type == KEXTYPE_DH) {
|
||||
/*
|
||||
* Work out the number of bits of key we will need from the
|
||||
|
32
sshcommon.c
32
sshcommon.c
@ -596,6 +596,38 @@ void add_to_commasep(strbuf *buf, const char *data)
|
||||
put_data(buf, data, strlen(data));
|
||||
}
|
||||
|
||||
int get_commasep_word(ptrlen *list, ptrlen *word)
|
||||
{
|
||||
const char *comma;
|
||||
|
||||
/*
|
||||
* Discard empty list elements, should there be any, because we
|
||||
* never want to return one as if it was a real string. (This
|
||||
* introduces a mild tolerance of badly formatted data in lists we
|
||||
* receive, but I think that's acceptable.)
|
||||
*/
|
||||
while (list->len > 0 && *(const char *)list->ptr == ',') {
|
||||
list->ptr = (const char *)list->ptr + 1;
|
||||
list->len--;
|
||||
}
|
||||
|
||||
if (!list->len)
|
||||
return FALSE;
|
||||
|
||||
comma = memchr(list->ptr, ',', list->len);
|
||||
if (!comma) {
|
||||
*word = *list;
|
||||
list->len = 0;
|
||||
} else {
|
||||
size_t wordlen = comma - (const char *)list->ptr;
|
||||
word->ptr = list->ptr;
|
||||
word->len = wordlen;
|
||||
list->ptr = (const char *)list->ptr + wordlen + 1;
|
||||
list->len -= wordlen + 1;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------
|
||||
* Functions for translating SSH packet type codes into their symbolic
|
||||
* string names.
|
||||
|
Loading…
Reference in New Issue
Block a user