On Thu, 6 Feb 2020, Damien Miller wrote: > That being said, the HostKeyAlgorithms knob is a bit subtle because > it is treated specially already because of its interaction with > known_hosts - by default ssh prefers algorithms in known_hosts to > ones that aren't. If the user overrides HostKeyAlgorithms then this > preference order is lost and the user may be presented with new/changed > host key warnings. > > I think I can improve this situation; let me go write a patch :) I think this patch improves the situation. It does two things: 1) When dumping the configuration ("ssh -G"), it will expand +/- specifiers in HostkeyAlgorithms. I.e. before: $ ssh -F none -oHostkeyAlgorithms=-ssh-rsa* -G ::1 | grep ^hostkeyalg hostkeyalgorithms -ssh-rsa* after: $ ssh -F none -oHostkeyAlgorithms=-ssh-rsa* -G ::1 | grep ^hostkeyalg hostkeyalgorithms ecdsa-sha2-nistp256-cert-v01@xxxxxxxxxxx,ecdsa-sha2-nistp384-cert-v01@xxxxxxxxxxx,ecdsa-sha2-nistp521-cert-v01@xxxxxxxxxxx,sk-ecdsa-sha2-nistp256-cert-v01@xxxxxxxxxxx,ssh-ed25519-cert-v01@xxxxxxxxxxx,sk-ssh-ed25519-cert-v01@xxxxxxxxxxx,rsa-sha2-512-cert-v01@xxxxxxxxxxx,rsa-sha2-256-cert-v01@xxxxxxxxxxx,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,sk-ecdsa-sha2-nistp256@xxxxxxxxxxx,ssh-ed25519,sk-ssh-ed25519@xxxxxxxxxxx,rsa-sha2-512,rsa-sha2-256 2) Retain the default behaviour of preferring algorithms that appear in known_hosts when the user is just adding to or removing from the default HostkeyAlgorithms (i.e. HostkeyAlgorithms=+/-...). In these cases, the preferred algorithms will be the intersection of those specified in HostkeyAlgorithms and those found in known_hosts. This should let users set HostkeyAlgorithms=-ssh-rsa* without hitting new/changes hostkey warnings. diff --git a/readconf.c b/readconf.c index 0dfa776..e6b7863 100644 --- a/readconf.c +++ b/readconf.c @@ -2642,8 +2642,20 @@ dump_cfg_forwards(OpCodes code, u_int count, const struct Forward *fwds) void dump_client_config(Options *o, const char *host) { - int i; - char buf[8]; + int i, r; + char buf[8], *all_key; + + /* + * Expand HostKeyAlgorithms name lists. This isn't handled in + * fill_default_options() like the other algorithm lists because + * the host key algorithms are by default dynamically chosen based + * on the host's keys found in known_hosts. + */ + all_key = sshkey_alg_list(0, 0, 1, ','); + if ((r = kex_assemble_names(&o->hostkeyalgorithms, kex_default_pk_alg(), + all_key)) != 0) + fatal("%s: expand HostKeyAlgorithms: %s", __func__, ssh_err(r)); + free(all_key); /* Most interesting options first: user, host, port */ dump_cfg_string(oUser, o->user); diff --git a/sshconnect2.c b/sshconnect2.c index 93ac1ac..5b98dd0 100644 --- a/sshconnect2.c +++ b/sshconnect2.c @@ -114,7 +114,7 @@ order_hostkeyalgs(char *host, struct sockaddr *hostaddr, u_short port) for (i = 0; i < options.num_system_hostfiles; i++) load_hostkeys(hostkeys, hostname, options.system_hostfiles[i]); - oavail = avail = xstrdup(kex_default_pk_alg()); + oavail = avail = xstrdup(options.hostkeyalgorithms); maxlen = strlen(avail) + 1; first = xmalloc(maxlen); last = xmalloc(maxlen); @@ -156,11 +156,28 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port) { char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT }; char *s, *all_key; - int r; + int r, use_known_hosts_order = 0; xxx_host = host; xxx_hostaddr = hostaddr; + /* + * If the user has not specified HostkeyAlgorithms, or has only + * appended or removed algorithms from that list then prefer algorithms + * that are in the list that are supported by known_hosts keys. + */ + if (options.hostkeyalgorithms == NULL || + options.hostkeyalgorithms[0] == '-' || + options.hostkeyalgorithms[0] == '+') + use_known_hosts_order = 1; + + /* Expand or fill in HostkeyAlgorithms */ + all_key = sshkey_alg_list(0, 0, 1, ','); + if (kex_assemble_names(&options.hostkeyalgorithms, + kex_default_pk_alg(), all_key) != 0) + fatal("%s: kex_assemble_namelist", __func__); + free(all_key); + if ((s = kex_names_cat(options.kex_algorithms, "ext-info-c")) == NULL) fatal("%s: kex_names_cat", __func__); myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(s); @@ -173,21 +190,15 @@ ssh_kex2(struct ssh *ssh, char *host, struct sockaddr *hostaddr, u_short port) (char *)compression_alg_list(options.compression); myproposal[PROPOSAL_MAC_ALGS_CTOS] = myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs; - if (options.hostkeyalgorithms != NULL) { - all_key = sshkey_alg_list(0, 0, 1, ','); - if (kex_assemble_names(&options.hostkeyalgorithms, - kex_default_pk_alg(), all_key) != 0) - fatal("%s: kex_assemble_namelist", __func__); - free(all_key); - myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = - compat_pkalg_proposal(options.hostkeyalgorithms); - } else { - /* Enforce default */ - options.hostkeyalgorithms = xstrdup(kex_default_pk_alg()); - /* Prefer algorithms that we already have keys for */ + if (use_known_hosts_order) { + /* Query known_hosts and prefer algorithms that appear there */ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( order_hostkeyalgs(host, hostaddr, port)); + } else { + /* Use specified HostkeyAlgorithms exactly */ + myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = + compat_pkalg_proposal(options.hostkeyalgorithms); } if (options.rekey_limit || options.rekey_interval) _______________________________________________ openssh-unix-dev mailing list openssh-unix-dev@xxxxxxxxxxx https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev