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/clientloop.c b/clientloop.c
index 4522a63..7b54b6e 100644
--- a/clientloop.c
+++ b/clientloop.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: clientloop.c,v 1.266 2015/01/20 23:14:00 deraadt Exp $ */
+/* $OpenBSD: clientloop.c,v 1.267 2015/01/26 03:04:45 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -112,6 +112,7 @@
#include "msg.h"
#include "roaming.h"
#include "ssherr.h"
+#include "hostfile.h"
/* import options */
extern Options options;
@@ -1781,6 +1782,7 @@
quit_pending = 1;
return 0;
}
+
static int
client_input_agent_open(int type, u_int32_t seq, void *ctxt)
{
@@ -2038,6 +2040,7 @@
free(ctype);
return 0;
}
+
static int
client_input_channel_req(int type, u_int32_t seq, void *ctxt)
{
@@ -2085,6 +2088,91 @@
free(rtype);
return 0;
}
+
+/*
+ * Handle hostkeys@openssh.com global request to inform the client of all
+ * the server's hostkeys. The keys are checked against the user's
+ * HostkeyAlgorithms preference before they are accepted.
+ */
+static int
+client_input_hostkeys(void)
+{
+ const u_char *blob = NULL;
+ u_int i, len = 0, nkeys = 0;
+ struct sshbuf *buf = NULL;
+ struct sshkey *key = NULL, **tmp, **keys = NULL;
+ int r, success = 1;
+ char *fp, *host_str = NULL;
+ static int hostkeys_seen = 0; /* XXX use struct ssh */
+
+ /*
+ * NB. Return success for all cases other than protocol error. The
+ * server doesn't need to know what the client does with its hosts
+ * file.
+ */
+
+ blob = packet_get_string_ptr(&len);
+ packet_check_eom();
+
+ if (hostkeys_seen)
+ fatal("%s: server already sent hostkeys", __func__);
+ if (!options.update_hostkeys || options.num_user_hostfiles <= 0)
+ return 1;
+ if ((buf = sshbuf_from(blob, len)) == NULL)
+ fatal("%s: sshbuf_from failed", __func__);
+ while (sshbuf_len(buf) > 0) {
+ sshkey_free(key);
+ key = NULL;
+ if ((r = sshkey_froms(buf, &key)) != 0)
+ fatal("%s: parse key: %s", __func__, ssh_err(r));
+ fp = sshkey_fingerprint(key, options.fingerprint_hash,
+ SSH_FP_DEFAULT);
+ debug3("%s: received %s key %s", __func__,
+ sshkey_type(key), fp);
+ free(fp);
+ /* Check that the key is accepted in HostkeyAlgorithms */
+ if (options.hostkeyalgorithms != NULL &&
+ match_pattern_list(sshkey_ssh_name(key),
+ options.hostkeyalgorithms,
+ strlen(options.hostkeyalgorithms), 0) != 1) {
+ debug3("%s: %s key not permitted by HostkeyAlgorithms",
+ __func__, sshkey_ssh_name(key));
+ continue;
+ }
+ if ((tmp = reallocarray(keys, nkeys + 1,
+ sizeof(*keys))) == NULL)
+ fatal("%s: reallocarray failed nkeys = %u",
+ __func__, nkeys);
+ keys = tmp;
+ keys[nkeys++] = key;
+ key = NULL;
+ }
+
+ debug3("%s: received %u keys from server", __func__, nkeys);
+ if (nkeys == 0) {
+ error("%s: server sent no hostkeys", __func__);
+ goto out;
+ }
+
+ get_hostfile_hostname_ipaddr(host, NULL, options.port, &host_str, NULL);
+
+ if ((r = hostfile_replace_entries(options.user_hostfiles[0], host_str,
+ keys, nkeys, options.hash_known_hosts, 1)) != 0) {
+ error("%s: hostfile_replace_entries failed: %s",
+ __func__, ssh_err(r));
+ goto out;
+ }
+
+ /* Success */
+ out:
+ free(host_str);
+ sshkey_free(key);
+ for (i = 0; i < nkeys; i++)
+ sshkey_free(keys[i]);
+ sshbuf_free(buf);
+ return success;
+}
+
static int
client_input_global_request(int type, u_int32_t seq, void *ctxt)
{
@@ -2092,10 +2180,12 @@
int want_reply;
int success = 0;
- rtype = packet_get_string(NULL);
+ rtype = packet_get_cstring(NULL);
want_reply = packet_get_char();
debug("client_input_global_request: rtype %s want_reply %d",
rtype, want_reply);
+ if (strcmp(rtype, "hostkeys@openssh.com") == 0)
+ success = client_input_hostkeys();
if (want_reply) {
packet_start(success ?
SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE);