From 8d4f87258f31cb6def9b3b55b6a7321d84728ff2 Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Mon, 26 Jan 2015 03:04:45 +0000 Subject: 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@ --- clientloop.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 92 insertions(+), 2 deletions(-) (limited to 'clientloop.c') diff --git a/clientloop.c b/clientloop.c index 4522a6332..7b54b6eb0 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 * Copyright (c) 1995 Tatu Ylonen , 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 @@ client_input_exit_status(int type, u_int32_t seq, void *ctxt) quit_pending = 1; return 0; } + static int client_input_agent_open(int type, u_int32_t seq, void *ctxt) { @@ -2038,6 +2040,7 @@ client_input_channel_open(int type, u_int32_t seq, void *ctxt) free(ctype); return 0; } + static int client_input_channel_req(int type, u_int32_t seq, void *ctxt) { @@ -2085,6 +2088,91 @@ client_input_channel_req(int type, u_int32_t seq, void *ctxt) 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 @@ client_input_global_request(int type, u_int32_t seq, void *ctxt) 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); -- cgit v1.2.3