Call for testing: OpenSSH 8.2

Damien Miller djm at mindrot.org
Thu Feb 6 15:08:38 AEDT 2020


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 at openssh.com,ecdsa-sha2-nistp384-cert-v01 at openssh.com,ecdsa-sha2-nistp521-cert-v01 at openssh.com,sk-ecdsa-sha2-nistp256-cert-v01 at openssh.com,ssh-ed25519-cert-v01 at openssh.com,sk-ssh-ed25519-cert-v01 at openssh.com,rsa-sha2-512-cert-v01 at openssh.com,rsa-sha2-256-cert-v01 at openssh.com,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,sk-ecdsa-sha2-nistp256 at openssh.com,ssh-ed25519,sk-ssh-ed25519 at openssh.com,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)


More information about the openssh-unix-dev mailing list