From 3c6a04dd12805e3f29b5b603b3369a343162f698 Mon Sep 17 00:00:00 2001 From: djm Date: Thu, 6 Jan 2022 21:55:23 +0000 Subject: [PATCH] Fix signature algorithm selection logic for UpdateHostkeys on the server side. The previous code tried to prefer RSA/SHA2 for hostkey proofs of RSA keys, but missed some cases. This will use RSA/SHA2 signatures for RSA keys if the client proposed these algorithms in initial KEX. bz3375 Mostly by Dmitry Belyavskiy with some tweaks by me. ok markus@ --- usr.bin/ssh/kex.c | 24 +++++++++++++++++++++++- usr.bin/ssh/kex.h | 4 +++- usr.bin/ssh/serverloop.c | 27 ++++++++++++++++++--------- 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/usr.bin/ssh/kex.c b/usr.bin/ssh/kex.c index d3fe4ad828e..c3e13fe56a5 100644 --- a/usr.bin/ssh/kex.c +++ b/usr.bin/ssh/kex.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kex.c,v 1.170 2021/12/19 22:13:12 djm Exp $ */ +/* $OpenBSD: kex.c,v 1.171 2022/01/06 21:55:23 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * @@ -883,6 +883,18 @@ proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX]) 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) { @@ -918,6 +930,16 @@ kex_choose_conf(struct ssh *ssh) 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 --git a/usr.bin/ssh/kex.h b/usr.bin/ssh/kex.h index 638b02f3c71..8363a6ea286 100644 --- a/usr.bin/ssh/kex.h +++ b/usr.bin/ssh/kex.h @@ -1,4 +1,4 @@ -/* $OpenBSD: kex.h,v 1.116 2021/12/19 22:12:54 djm Exp $ */ +/* $OpenBSD: kex.h,v 1.117 2022/01/06 21:55:23 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. @@ -102,6 +102,8 @@ enum kex_exchange { #define KEX_INIT_SENT 0x0001 #define KEX_INITIAL 0x0002 #define KEX_HAS_PUBKEY_HOSTBOUND 0x0004 +#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 --git a/usr.bin/ssh/serverloop.c b/usr.bin/ssh/serverloop.c index 7963782ecba..82f8ee14633 100644 --- a/usr.bin/ssh/serverloop.c +++ b/usr.bin/ssh/serverloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: serverloop.c,v 1.229 2022/01/06 21:48:38 djm Exp $ */ +/* $OpenBSD: serverloop.c,v 1.230 2022/01/06 21:55:23 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -676,16 +676,17 @@ server_input_hostkeys_prove(struct ssh *ssh, struct sshbuf **respp) 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; @@ -718,16 +719,24 @@ server_input_hostkeys_prove(struct ssh *ssh, struct sshbuf **respp) * 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; -- 2.20.1