diff options
author | djm@openbsd.org <djm@openbsd.org> | 2015-01-26 03:04:45 +0000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2015-01-27 00:00:57 +1100 |
commit | 8d4f87258f31cb6def9b3b55b6a7321d84728ff2 (patch) | |
tree | c98e66c1c0824f0b0e312d7b44d8eeac46265362 /clientloop.c | |
parent | 60b1825262b1f1e24fc72050b907189c92daf18e (diff) |
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@
Diffstat (limited to 'clientloop.c')
-rw-r--r-- | clientloop.c | 94 |
1 files changed, 92 insertions, 2 deletions
diff --git a/clientloop.c b/clientloop.c index 4522a6332..7b54b6eb0 100644 --- a/clientloop.c +++ b/clientloop.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: clientloop.c,v 1.266 2015/01/20 23:14:00 deraadt Exp $ */ | 1 | /* $OpenBSD: clientloop.c,v 1.267 2015/01/26 03:04:45 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -112,6 +112,7 @@ | |||
112 | #include "msg.h" | 112 | #include "msg.h" |
113 | #include "roaming.h" | 113 | #include "roaming.h" |
114 | #include "ssherr.h" | 114 | #include "ssherr.h" |
115 | #include "hostfile.h" | ||
115 | 116 | ||
116 | /* import options */ | 117 | /* import options */ |
117 | extern Options options; | 118 | extern Options options; |
@@ -1781,6 +1782,7 @@ client_input_exit_status(int type, u_int32_t seq, void *ctxt) | |||
1781 | quit_pending = 1; | 1782 | quit_pending = 1; |
1782 | return 0; | 1783 | return 0; |
1783 | } | 1784 | } |
1785 | |||
1784 | static int | 1786 | static int |
1785 | client_input_agent_open(int type, u_int32_t seq, void *ctxt) | 1787 | client_input_agent_open(int type, u_int32_t seq, void *ctxt) |
1786 | { | 1788 | { |
@@ -2038,6 +2040,7 @@ client_input_channel_open(int type, u_int32_t seq, void *ctxt) | |||
2038 | free(ctype); | 2040 | free(ctype); |
2039 | return 0; | 2041 | return 0; |
2040 | } | 2042 | } |
2043 | |||
2041 | static int | 2044 | static int |
2042 | client_input_channel_req(int type, u_int32_t seq, void *ctxt) | 2045 | client_input_channel_req(int type, u_int32_t seq, void *ctxt) |
2043 | { | 2046 | { |
@@ -2085,6 +2088,91 @@ client_input_channel_req(int type, u_int32_t seq, void *ctxt) | |||
2085 | free(rtype); | 2088 | free(rtype); |
2086 | return 0; | 2089 | return 0; |
2087 | } | 2090 | } |
2091 | |||
2092 | /* | ||
2093 | * Handle hostkeys@openssh.com global request to inform the client of all | ||
2094 | * the server's hostkeys. The keys are checked against the user's | ||
2095 | * HostkeyAlgorithms preference before they are accepted. | ||
2096 | */ | ||
2097 | static int | ||
2098 | client_input_hostkeys(void) | ||
2099 | { | ||
2100 | const u_char *blob = NULL; | ||
2101 | u_int i, len = 0, nkeys = 0; | ||
2102 | struct sshbuf *buf = NULL; | ||
2103 | struct sshkey *key = NULL, **tmp, **keys = NULL; | ||
2104 | int r, success = 1; | ||
2105 | char *fp, *host_str = NULL; | ||
2106 | static int hostkeys_seen = 0; /* XXX use struct ssh */ | ||
2107 | |||
2108 | /* | ||
2109 | * NB. Return success for all cases other than protocol error. The | ||
2110 | * server doesn't need to know what the client does with its hosts | ||
2111 | * file. | ||
2112 | */ | ||
2113 | |||
2114 | blob = packet_get_string_ptr(&len); | ||
2115 | packet_check_eom(); | ||
2116 | |||
2117 | if (hostkeys_seen) | ||
2118 | fatal("%s: server already sent hostkeys", __func__); | ||
2119 | if (!options.update_hostkeys || options.num_user_hostfiles <= 0) | ||
2120 | return 1; | ||
2121 | if ((buf = sshbuf_from(blob, len)) == NULL) | ||
2122 | fatal("%s: sshbuf_from failed", __func__); | ||
2123 | while (sshbuf_len(buf) > 0) { | ||
2124 | sshkey_free(key); | ||
2125 | key = NULL; | ||
2126 | if ((r = sshkey_froms(buf, &key)) != 0) | ||
2127 | fatal("%s: parse key: %s", __func__, ssh_err(r)); | ||
2128 | fp = sshkey_fingerprint(key, options.fingerprint_hash, | ||
2129 | SSH_FP_DEFAULT); | ||
2130 | debug3("%s: received %s key %s", __func__, | ||
2131 | sshkey_type(key), fp); | ||
2132 | free(fp); | ||
2133 | /* Check that the key is accepted in HostkeyAlgorithms */ | ||
2134 | if (options.hostkeyalgorithms != NULL && | ||
2135 | match_pattern_list(sshkey_ssh_name(key), | ||
2136 | options.hostkeyalgorithms, | ||
2137 | strlen(options.hostkeyalgorithms), 0) != 1) { | ||
2138 | debug3("%s: %s key not permitted by HostkeyAlgorithms", | ||
2139 | __func__, sshkey_ssh_name(key)); | ||
2140 | continue; | ||
2141 | } | ||
2142 | if ((tmp = reallocarray(keys, nkeys + 1, | ||
2143 | sizeof(*keys))) == NULL) | ||
2144 | fatal("%s: reallocarray failed nkeys = %u", | ||
2145 | __func__, nkeys); | ||
2146 | keys = tmp; | ||
2147 | keys[nkeys++] = key; | ||
2148 | key = NULL; | ||
2149 | } | ||
2150 | |||
2151 | debug3("%s: received %u keys from server", __func__, nkeys); | ||
2152 | if (nkeys == 0) { | ||
2153 | error("%s: server sent no hostkeys", __func__); | ||
2154 | goto out; | ||
2155 | } | ||
2156 | |||
2157 | get_hostfile_hostname_ipaddr(host, NULL, options.port, &host_str, NULL); | ||
2158 | |||
2159 | if ((r = hostfile_replace_entries(options.user_hostfiles[0], host_str, | ||
2160 | keys, nkeys, options.hash_known_hosts, 1)) != 0) { | ||
2161 | error("%s: hostfile_replace_entries failed: %s", | ||
2162 | __func__, ssh_err(r)); | ||
2163 | goto out; | ||
2164 | } | ||
2165 | |||
2166 | /* Success */ | ||
2167 | out: | ||
2168 | free(host_str); | ||
2169 | sshkey_free(key); | ||
2170 | for (i = 0; i < nkeys; i++) | ||
2171 | sshkey_free(keys[i]); | ||
2172 | sshbuf_free(buf); | ||
2173 | return success; | ||
2174 | } | ||
2175 | |||
2088 | static int | 2176 | static int |
2089 | client_input_global_request(int type, u_int32_t seq, void *ctxt) | 2177 | client_input_global_request(int type, u_int32_t seq, void *ctxt) |
2090 | { | 2178 | { |
@@ -2092,10 +2180,12 @@ client_input_global_request(int type, u_int32_t seq, void *ctxt) | |||
2092 | int want_reply; | 2180 | int want_reply; |
2093 | int success = 0; | 2181 | int success = 0; |
2094 | 2182 | ||
2095 | rtype = packet_get_string(NULL); | 2183 | rtype = packet_get_cstring(NULL); |
2096 | want_reply = packet_get_char(); | 2184 | want_reply = packet_get_char(); |
2097 | debug("client_input_global_request: rtype %s want_reply %d", | 2185 | debug("client_input_global_request: rtype %s want_reply %d", |
2098 | rtype, want_reply); | 2186 | rtype, want_reply); |
2187 | if (strcmp(rtype, "hostkeys@openssh.com") == 0) | ||
2188 | success = client_input_hostkeys(); | ||
2099 | if (want_reply) { | 2189 | if (want_reply) { |
2100 | packet_start(success ? | 2190 | packet_start(success ? |
2101 | SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE); | 2191 | SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE); |