upstream commit

Host key rotation support.

Add a hostkeys@openssh.com protocol extension (global request) for
a server to inform a client of all its available host key after
authentication has completed. The client may record the keys in
known_hosts, allowing it to upgrade to better host key algorithms
and a server to gracefully rotate its keys.

The client side of this is controlled by a UpdateHostkeys config
option (default on).

ok markus@
diff --git a/sshd.c b/sshd.c
index ef63bd1..f2ee10d 100644
--- a/sshd.c
+++ b/sshd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshd.c,v 1.438 2015/01/20 23:14:00 deraadt Exp $ */
+/* $OpenBSD: sshd.c,v 1.439 2015/01/26 03:04:46 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -911,6 +911,42 @@
 	return (-1);
 }
 
+/* Inform the client of all hostkeys */
+static void
+notify_hostkeys(struct ssh *ssh)
+{
+	struct sshbuf *buf;
+	struct sshkey *key;
+	int i, nkeys, r;
+	char *fp;
+
+	if ((buf = sshbuf_new()) == NULL)
+		fatal("%s: sshbuf_new", __func__);
+	for (i = nkeys = 0; i < options.num_host_key_files; i++) {
+		key = get_hostkey_public_by_index(i, ssh);
+		if (key == NULL || key->type == KEY_UNSPEC ||
+		    key->type == KEY_RSA1 || sshkey_is_cert(key))
+			continue;
+		fp = sshkey_fingerprint(key, options.fingerprint_hash,
+		    SSH_FP_DEFAULT);
+		debug3("%s: key %d: %s %s", __func__, i,
+		    sshkey_ssh_name(key), fp);
+		free(fp);
+		if ((r = sshkey_puts(key, buf)) != 0)
+			fatal("%s: couldn't put hostkey %d: %s",
+			    __func__, i, ssh_err(r));
+		nkeys++;
+	}
+	if (nkeys == 0)
+		fatal("%s: no hostkeys", __func__);
+	debug3("%s: send %d hostkeys", __func__, nkeys);
+	packet_start(SSH2_MSG_GLOBAL_REQUEST);
+	packet_put_cstring("hostkeys@openssh.com");
+	packet_put_char(0); /* want-reply */
+	packet_put_string(sshbuf_ptr(buf), sshbuf_len(buf));
+	packet_send();
+}
+
 /*
  * returns 1 if connection should be dropped, 0 otherwise.
  * dropping starts at connection #max_startups_begin with a probability
@@ -1722,6 +1758,8 @@
 			continue;
 		key = key_load_private(options.host_key_files[i], "", NULL);
 		pubkey = key_load_public(options.host_key_files[i], NULL);
+		if (pubkey == NULL && key != NULL)
+			pubkey = key_demote(key);
 		sensitive_data.host_keys[i] = key;
 		sensitive_data.host_pubkeys[i] = pubkey;
 
@@ -2185,6 +2223,10 @@
 	packet_set_timeout(options.client_alive_interval,
 	    options.client_alive_count_max);
 
+	/* Try to send all our hostkeys to the client */
+	if (compat20)
+		notify_hostkeys(active_state);
+
 	/* Start session. */
 	do_authenticated(authctxt);