diff -up openssh-8.0p1/hostfile.c.cve-2020-14145 openssh-8.0p1/hostfile.c --- openssh-8.0p1/hostfile.c.cve-2020-14145 2019-04-18 00:52:57.000000000 +0200 +++ openssh-8.0p1/hostfile.c 2021-05-17 16:53:38.694577251 +0200 @@ -409,6 +409,18 @@ lookup_key_in_hostkeys_by_type(struct ho found) == HOST_FOUND); } +int +lookup_marker_in_hostkeys(struct hostkeys *hostkeys, int want_marker) +{ + u_int i; + + for (i = 0; i < hostkeys->num_entries; i++) { + if (hostkeys->entries[i].marker == (HostkeyMarker)want_marker) + return 1; + } + return 0; +} + static int write_host_entry(FILE *f, const char *host, const char *ip, const struct sshkey *key, int store_hash) diff -up openssh-8.0p1/hostfile.h.cve-2020-14145 openssh-8.0p1/hostfile.h --- openssh-8.0p1/hostfile.h.cve-2020-14145 2019-04-18 00:52:57.000000000 +0200 +++ openssh-8.0p1/hostfile.h 2021-05-17 16:53:38.694577251 +0200 @@ -39,6 +39,7 @@ HostStatus check_key_in_hostkeys(struct const struct hostkey_entry **); int lookup_key_in_hostkeys_by_type(struct hostkeys *, int, const struct hostkey_entry **); +int lookup_marker_in_hostkeys(struct hostkeys *, int); int hostfile_read_key(char **, u_int *, struct sshkey *); int add_host_to_hostfile(const char *, const char *, diff -up openssh-8.0p1/sshconnect2.c.cve-2020-14145 openssh-8.0p1/sshconnect2.c --- openssh-8.0p1/sshconnect2.c.cve-2020-14145 2021-05-17 16:53:38.610576561 +0200 +++ openssh-8.0p1/sshconnect2.c 2021-05-17 16:54:58.169230103 +0200 @@ -98,12 +98,25 @@ verify_host_key_callback(struct sshkey * return 0; } +/* Returns the first item from a comma-separated algorithm list */ +static char * +first_alg(const char *algs) +{ + char *ret, *cp; + + ret = xstrdup(algs); + if ((cp = strchr(ret, ',')) != NULL) + *cp = '\0'; + return ret; +} + static char * order_hostkeyalgs(char *host, struct sockaddr *hostaddr, u_short port) { - char *oavail, *avail, *first, *last, *alg, *hostname, *ret; + char *oavail = NULL, *avail = NULL, *first = NULL, *last = NULL; + char *alg = NULL, *hostname = NULL, *ret = NULL, *best = NULL; size_t maxlen; - struct hostkeys *hostkeys; + struct hostkeys *hostkeys = NULL; int ktype; u_int i; @@ -115,6 +128,26 @@ order_hostkeyalgs(char *host, struct soc for (i = 0; i < options.num_system_hostfiles; i++) load_hostkeys(hostkeys, hostname, options.system_hostfiles[i]); + /* + * If a plain public key exists that matches the type of the best + * preference HostkeyAlgorithms, then use the whole list as is. + * Note that we ignore whether the best preference algorithm is a + * certificate type, as sshconnect.c will downgrade certs to + * plain keys if necessary. + */ + best = first_alg(options.hostkeyalgorithms); + if (lookup_key_in_hostkeys_by_type(hostkeys, + sshkey_type_plain(sshkey_type_from_name(best)), NULL)) { + debug3("%s: have matching best-preference key type %s, " + "using HostkeyAlgorithms verbatim", __func__, best); + ret = xstrdup(options.hostkeyalgorithms); + goto out; + } + + /* + * Otherwise, prefer the host key algorithms that match known keys + * while keeping the ordering of HostkeyAlgorithms as much as possible. + */ oavail = avail = xstrdup(KEX_DEFAULT_PK_ALG); maxlen = strlen(avail) + 1; first = xmalloc(maxlen); @@ -131,11 +164,23 @@ order_hostkeyalgs(char *host, struct soc while ((alg = strsep(&avail, ",")) && *alg != '\0') { if ((ktype = sshkey_type_from_name(alg)) == KEY_UNSPEC) fatal("%s: unknown alg %s", __func__, alg); + /* + * If we have a @cert-authority marker in known_hosts then + * prefer all certificate algorithms. + */ + if (sshkey_type_is_cert(ktype) && + lookup_marker_in_hostkeys(hostkeys, MRK_CA)) { + ALG_APPEND(first, alg); + continue; + } + /* If the key appears in known_hosts then prefer it */ if (lookup_key_in_hostkeys_by_type(hostkeys, - sshkey_type_plain(ktype), NULL)) + sshkey_type_plain(ktype), NULL)) { ALG_APPEND(first, alg); - else - ALG_APPEND(last, alg); + continue; + } + /* Otherwise, put it last */ + ALG_APPEND(last, alg); } #undef ALG_APPEND xasprintf(&ret, "%s%s%s", first, @@ -143,6 +188,8 @@ order_hostkeyalgs(char *host, struct soc if (*first != '\0') debug3("%s: prefer hostkeyalgs: %s", __func__, first); + out: + free(best); free(first); free(last); free(hostname);