diff --color -ru a/clientloop.c b/clientloop.c --- a/clientloop.c 2022-06-29 16:35:06.677597259 +0200 +++ b/clientloop.c 2022-06-29 16:40:29.737926205 +0200 @@ -116,6 +116,9 @@ #include "ssh-gss.h" #endif +/* Permitted RSA signature algorithms for UpdateHostkeys proofs */ +#define HOSTKEY_PROOF_RSA_ALGS "rsa-sha2-512,rsa-sha2-256" + /* import options */ extern Options options; @@ -2110,8 +2113,10 @@ struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; size_t i, ndone; struct sshbuf *signdata; - int r, kexsigtype, use_kexsigtype; + int r, plaintype; const u_char *sig; + const char *rsa_kexalg = NULL; + char *alg = NULL; size_t siglen; if (ctx->nnew == 0) @@ -2122,9 +2127,9 @@ hostkeys_update_ctx_free(ctx); return; } - kexsigtype = sshkey_type_plain( - sshkey_type_from_name(ssh->kex->hostkey_alg)); - + if (sshkey_type_plain(sshkey_type_from_name( + ssh->kex->hostkey_alg)) == KEY_RSA) + rsa_kexalg = ssh->kex->hostkey_alg; if ((signdata = sshbuf_new()) == NULL) fatal_f("sshbuf_new failed"); /* @@ -2135,6 +2140,7 @@ for (ndone = i = 0; i < ctx->nkeys; i++) { if (ctx->keys_match[i]) continue; + plaintype = sshkey_type_plain(ctx->keys[i]->type); /* Prepare data to be signed: session ID, unique string, key */ sshbuf_reset(signdata); if ( (r = sshbuf_put_cstring(signdata, @@ -2148,19 +2154,33 @@ error_fr(r, "parse sig"); goto out; } + if ((r = sshkey_get_sigtype(sig, siglen, &alg)) != 0) { + error_fr(r, "server gave unintelligible signature " + "for %s key %zu", sshkey_type(ctx->keys[i]), i); + goto out; + } /* - * For RSA keys, prefer to use the signature type negotiated - * during KEX to the default (SHA1). + * Special case for RSA keys: if a RSA hostkey was negotiated, + * then use its signature type for verification of RSA hostkey + * proofs. Otherwise, accept only RSA-SHA256/512 signatures. */ - use_kexsigtype = kexsigtype == KEY_RSA && - sshkey_type_plain(ctx->keys[i]->type) == KEY_RSA; - debug3_f("verify %s key %zu using %s sigalg", - sshkey_type(ctx->keys[i]), i, - use_kexsigtype ? ssh->kex->hostkey_alg : "default"); + if (plaintype == KEY_RSA && rsa_kexalg == NULL && + match_pattern_list(alg, HOSTKEY_PROOF_RSA_ALGS, 0) != 1) { + debug_f("server used untrusted RSA signature algorithm " + "%s for key %zu, disregarding", alg, i); + free(alg); + /* zap the key from the list */ + sshkey_free(ctx->keys[i]); + ctx->keys[i] = NULL; + ndone++; + continue; + } + debug3_f("verify %s key %zu using sigalg %s", + sshkey_type(ctx->keys[i]), i, alg); + free(alg); if ((r = sshkey_verify(ctx->keys[i], sig, siglen, sshbuf_ptr(signdata), sshbuf_len(signdata), - use_kexsigtype ? ssh->kex->hostkey_alg : NULL, 0, - NULL)) != 0) { + plaintype == KEY_RSA ? rsa_kexalg : NULL, 0, NULL)) != 0) { error_fr(r, "server gave bad signature for %s key %zu", sshkey_type(ctx->keys[i]), i); goto out; diff --git a/hostfile.c b/hostfile.c index a035b381..bd49e3ac 100644 --- a/hostfile.c +++ b/hostfile.c @@ -642,7 +642,7 @@ hostfile_replace_entries(const char *filename, const char *host, const char *ip, /* Re-add the requested keys */ want = HKF_MATCH_HOST | (ip == NULL ? 0 : HKF_MATCH_IP); for (i = 0; i < nkeys; i++) { - if ((want & ctx.match_keys[i]) == want) + if (keys[i] == NULL || (want & ctx.match_keys[i]) == want) continue; if ((fp = sshkey_fingerprint(keys[i], hash_alg, SSH_FP_DEFAULT)) == NULL) { diff --color -ru a/kex.c b/kex.c --- a/kex.c 2022-06-29 16:35:06.775599179 +0200 +++ b/kex.c 2022-06-29 16:42:00.839710940 +0200 @@ -959,6 +959,18 @@ return (1); } +/* returns non-zero if proposal contains any algorithm from algs */ +static int +has_any_alg(const char *proposal, const char *algs) +{ + char *cp; + + if ((cp = match_list(proposal, algs, NULL)) == NULL) + return 0; + free(cp); + return 1; +} + static int kex_choose_conf(struct ssh *ssh) { @@ -994,6 +1006,16 @@ free(ext); } + /* Check whether client supports rsa-sha2 algorithms */ + if (kex->server && (kex->flags & KEX_INITIAL)) { + if (has_any_alg(peer[PROPOSAL_SERVER_HOST_KEY_ALGS], + "rsa-sha2-256,rsa-sha2-256-cert-v01@openssh.com")) + kex->flags |= KEX_RSA_SHA2_256_SUPPORTED; + if (has_any_alg(peer[PROPOSAL_SERVER_HOST_KEY_ALGS], + "rsa-sha2-512,rsa-sha2-512-cert-v01@openssh.com")) + kex->flags |= KEX_RSA_SHA2_512_SUPPORTED; + } + /* Algorithm Negotiation */ if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS], sprop[PROPOSAL_KEX_ALGS])) != 0) { diff --color -ru a/kex.h b/kex.h --- a/kex.h 2022-06-29 16:35:06.766599003 +0200 +++ b/kex.h 2022-06-29 16:42:24.199168567 +0200 @@ -116,6 +116,8 @@ #define KEX_INIT_SENT 0x0001 #define KEX_INITIAL 0x0002 +#define KEX_RSA_SHA2_256_SUPPORTED 0x0008 /* only set in server for now */ +#define KEX_RSA_SHA2_512_SUPPORTED 0x0010 /* only set in server for now */ struct sshenc { char *name; diff --color -ru a/serverloop.c b/serverloop.c --- a/serverloop.c 2021-08-20 06:03:49.000000000 +0200 +++ b/serverloop.c 2022-06-29 16:45:05.902336428 +0200 @@ -684,16 +684,18 @@ struct sshbuf *resp = NULL; struct sshbuf *sigbuf = NULL; struct sshkey *key = NULL, *key_pub = NULL, *key_prv = NULL; - int r, ndx, kexsigtype, use_kexsigtype, success = 0; + int r, ndx, success = 0; const u_char *blob; + const char *sigalg, *kex_rsa_sigalg = NULL; u_char *sig = 0; size_t blen, slen; if ((resp = sshbuf_new()) == NULL || (sigbuf = sshbuf_new()) == NULL) fatal_f("sshbuf_new"); - kexsigtype = sshkey_type_plain( - sshkey_type_from_name(ssh->kex->hostkey_alg)); + if (sshkey_type_plain(sshkey_type_from_name( + ssh->kex->hostkey_alg)) == KEY_RSA) + kex_rsa_sigalg = ssh->kex->hostkey_alg; while (ssh_packet_remaining(ssh) > 0) { sshkey_free(key); key = NULL; @@ -726,16 +728,24 @@ * For RSA keys, prefer to use the signature type negotiated * during KEX to the default (SHA1). */ - use_kexsigtype = kexsigtype == KEY_RSA && - sshkey_type_plain(key->type) == KEY_RSA; + sigalg = NULL; + if (sshkey_type_plain(key->type) == KEY_RSA) { + if (kex_rsa_sigalg != NULL) + sigalg = kex_rsa_sigalg; + else if (ssh->kex->flags & KEX_RSA_SHA2_512_SUPPORTED) + sigalg = "rsa-sha2-512"; + else if (ssh->kex->flags & KEX_RSA_SHA2_256_SUPPORTED) + sigalg = "rsa-sha2-256"; + } + debug3_f("sign %s key (index %d) using sigalg %s", + sshkey_type(key), ndx, sigalg == NULL ? "default" : sigalg); if ((r = sshbuf_put_cstring(sigbuf, "hostkeys-prove-00@openssh.com")) != 0 || (r = sshbuf_put_stringb(sigbuf, ssh->kex->session_id)) != 0 || (r = sshkey_puts(key, sigbuf)) != 0 || (r = ssh->kex->sign(ssh, key_prv, key_pub, &sig, &slen, - sshbuf_ptr(sigbuf), sshbuf_len(sigbuf), - use_kexsigtype ? ssh->kex->hostkey_alg : NULL)) != 0 || + sshbuf_ptr(sigbuf), sshbuf_len(sigbuf), sigalg)) != 0 || (r = sshbuf_put_string(resp, sig, slen)) != 0) { error_fr(r, "assemble signature"); goto out;