Fix signature algorithm selection logic for UpdateHostkeys on the
authordjm <djm@openbsd.org>
Thu, 6 Jan 2022 21:55:23 +0000 (21:55 +0000)
committerdjm <djm@openbsd.org>
Thu, 6 Jan 2022 21:55:23 +0000 (21:55 +0000)
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
usr.bin/ssh/kex.h
usr.bin/ssh/serverloop.c

index d3fe4ad..c3e13fe 100644 (file)
@@ -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) {
index 638b02f..8363a6e 100644 (file)
@@ -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;
index 7963782..82f8ee1 100644 (file)
@@ -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 <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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;