diff options
author | Colin Watson <cjwatson@debian.org> | 2015-08-19 14:23:51 +0100 |
---|---|---|
committer | Colin Watson <cjwatson@debian.org> | 2015-08-19 16:48:11 +0100 |
commit | 0f0841b2d28b7463267d4d91577e72e3340a1d3a (patch) | |
tree | ba55fcd2b6e2cc22b30f5afb561dbb3da4c8b6c7 /clientloop.c | |
parent | f2a5f5dae656759efb0b76c3d94890b65c197a02 (diff) | |
parent | 8698446b972003b63dfe5dcbdb86acfe986afb85 (diff) |
New upstream release (6.8p1).
Diffstat (limited to 'clientloop.c')
-rw-r--r-- | clientloop.c | 455 |
1 files changed, 424 insertions, 31 deletions
diff --git a/clientloop.c b/clientloop.c index 0180774bb..45cef8829 100644 --- a/clientloop.c +++ b/clientloop.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: clientloop.c,v 1.261 2014/07/15 15:54:14 millert Exp $ */ | 1 | /* $OpenBSD: clientloop.c,v 1.272 2015/02/25 19:54:02 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 |
@@ -61,9 +61,9 @@ | |||
61 | 61 | ||
62 | #include "includes.h" | 62 | #include "includes.h" |
63 | 63 | ||
64 | #include <sys/param.h> /* MIN MAX */ | ||
64 | #include <sys/types.h> | 65 | #include <sys/types.h> |
65 | #include <sys/ioctl.h> | 66 | #include <sys/ioctl.h> |
66 | #include <sys/param.h> | ||
67 | #ifdef HAVE_SYS_STAT_H | 67 | #ifdef HAVE_SYS_STAT_H |
68 | # include <sys/stat.h> | 68 | # include <sys/stat.h> |
69 | #endif | 69 | #endif |
@@ -85,6 +85,7 @@ | |||
85 | #include <termios.h> | 85 | #include <termios.h> |
86 | #include <pwd.h> | 86 | #include <pwd.h> |
87 | #include <unistd.h> | 87 | #include <unistd.h> |
88 | #include <limits.h> | ||
88 | 89 | ||
89 | #include "openbsd-compat/sys-queue.h" | 90 | #include "openbsd-compat/sys-queue.h" |
90 | #include "xmalloc.h" | 91 | #include "xmalloc.h" |
@@ -110,6 +111,8 @@ | |||
110 | #include "match.h" | 111 | #include "match.h" |
111 | #include "msg.h" | 112 | #include "msg.h" |
112 | #include "roaming.h" | 113 | #include "roaming.h" |
114 | #include "ssherr.h" | ||
115 | #include "hostfile.h" | ||
113 | 116 | ||
114 | #ifdef GSSAPI | 117 | #ifdef GSSAPI |
115 | #include "ssh-gss.h" | 118 | #include "ssh-gss.h" |
@@ -195,9 +198,6 @@ TAILQ_HEAD(global_confirms, global_confirm); | |||
195 | static struct global_confirms global_confirms = | 198 | static struct global_confirms global_confirms = |
196 | TAILQ_HEAD_INITIALIZER(global_confirms); | 199 | TAILQ_HEAD_INITIALIZER(global_confirms); |
197 | 200 | ||
198 | /*XXX*/ | ||
199 | extern Kex *xxx_kex; | ||
200 | |||
201 | void ssh_process_session2_setup(int, int, int, Buffer *); | 201 | void ssh_process_session2_setup(int, int, int, Buffer *); |
202 | 202 | ||
203 | /* Restores stdin to blocking mode. */ | 203 | /* Restores stdin to blocking mode. */ |
@@ -345,12 +345,12 @@ client_x11_get_proto(const char *display, const char *xauth_path, | |||
345 | display = xdisplay; | 345 | display = xdisplay; |
346 | } | 346 | } |
347 | if (trusted == 0) { | 347 | if (trusted == 0) { |
348 | xauthdir = xmalloc(MAXPATHLEN); | 348 | xauthdir = xmalloc(PATH_MAX); |
349 | xauthfile = xmalloc(MAXPATHLEN); | 349 | xauthfile = xmalloc(PATH_MAX); |
350 | mktemp_proto(xauthdir, MAXPATHLEN); | 350 | mktemp_proto(xauthdir, PATH_MAX); |
351 | if (mkdtemp(xauthdir) != NULL) { | 351 | if (mkdtemp(xauthdir) != NULL) { |
352 | do_unlink = 1; | 352 | do_unlink = 1; |
353 | snprintf(xauthfile, MAXPATHLEN, "%s/xauthfile", | 353 | snprintf(xauthfile, PATH_MAX, "%s/xauthfile", |
354 | xauthdir); | 354 | xauthdir); |
355 | snprintf(cmd, sizeof(cmd), | 355 | snprintf(cmd, sizeof(cmd), |
356 | "%s -f %s generate %s " SSH_X11_PROTO | 356 | "%s -f %s generate %s " SSH_X11_PROTO |
@@ -542,13 +542,13 @@ client_check_window_change(void) | |||
542 | } | 542 | } |
543 | } | 543 | } |
544 | 544 | ||
545 | static void | 545 | static int |
546 | client_global_request_reply(int type, u_int32_t seq, void *ctxt) | 546 | client_global_request_reply(int type, u_int32_t seq, void *ctxt) |
547 | { | 547 | { |
548 | struct global_confirm *gc; | 548 | struct global_confirm *gc; |
549 | 549 | ||
550 | if ((gc = TAILQ_FIRST(&global_confirms)) == NULL) | 550 | if ((gc = TAILQ_FIRST(&global_confirms)) == NULL) |
551 | return; | 551 | return 0; |
552 | if (gc->cb != NULL) | 552 | if (gc->cb != NULL) |
553 | gc->cb(type, seq, gc->ctx); | 553 | gc->cb(type, seq, gc->ctx); |
554 | if (--gc->ref_count <= 0) { | 554 | if (--gc->ref_count <= 0) { |
@@ -558,6 +558,7 @@ client_global_request_reply(int type, u_int32_t seq, void *ctxt) | |||
558 | } | 558 | } |
559 | 559 | ||
560 | packet_set_alive_timeouts(0); | 560 | packet_set_alive_timeouts(0); |
561 | return 0; | ||
561 | } | 562 | } |
562 | 563 | ||
563 | static void | 564 | static void |
@@ -1423,8 +1424,7 @@ client_process_output(fd_set *writeset) | |||
1423 | static void | 1424 | static void |
1424 | client_process_buffered_input_packets(void) | 1425 | client_process_buffered_input_packets(void) |
1425 | { | 1426 | { |
1426 | dispatch_run(DISPATCH_NONBLOCK, &quit_pending, | 1427 | dispatch_run(DISPATCH_NONBLOCK, &quit_pending, active_state); |
1427 | compat20 ? xxx_kex : NULL); | ||
1428 | } | 1428 | } |
1429 | 1429 | ||
1430 | /* scan buf[] for '~' before sending data to the peer */ | 1430 | /* scan buf[] for '~' before sending data to the peer */ |
@@ -1478,7 +1478,7 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) | |||
1478 | { | 1478 | { |
1479 | fd_set *readset = NULL, *writeset = NULL; | 1479 | fd_set *readset = NULL, *writeset = NULL; |
1480 | double start_time, total_time; | 1480 | double start_time, total_time; |
1481 | int max_fd = 0, max_fd2 = 0, len, rekeying = 0; | 1481 | int r, max_fd = 0, max_fd2 = 0, len, rekeying = 0; |
1482 | u_int64_t ibytes, obytes; | 1482 | u_int64_t ibytes, obytes; |
1483 | u_int nalloc = 0; | 1483 | u_int nalloc = 0; |
1484 | char buf[100]; | 1484 | char buf[100]; |
@@ -1563,7 +1563,7 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) | |||
1563 | if (compat20 && session_closed && !channel_still_open()) | 1563 | if (compat20 && session_closed && !channel_still_open()) |
1564 | break; | 1564 | break; |
1565 | 1565 | ||
1566 | rekeying = (xxx_kex != NULL && !xxx_kex->done); | 1566 | rekeying = (active_state->kex != NULL && !active_state->kex->done); |
1567 | 1567 | ||
1568 | if (rekeying) { | 1568 | if (rekeying) { |
1569 | debug("rekeying in progress"); | 1569 | debug("rekeying in progress"); |
@@ -1616,8 +1616,10 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) | |||
1616 | 1616 | ||
1617 | if (need_rekeying || packet_need_rekeying()) { | 1617 | if (need_rekeying || packet_need_rekeying()) { |
1618 | debug("need rekeying"); | 1618 | debug("need rekeying"); |
1619 | xxx_kex->done = 0; | 1619 | active_state->kex->done = 0; |
1620 | kex_send_kexinit(xxx_kex); | 1620 | if ((r = kex_send_kexinit(active_state)) != 0) |
1621 | fatal("%s: kex_send_kexinit: %s", | ||
1622 | __func__, ssh_err(r)); | ||
1621 | need_rekeying = 0; | 1623 | need_rekeying = 0; |
1622 | } | 1624 | } |
1623 | } | 1625 | } |
@@ -1748,8 +1750,7 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) | |||
1748 | 1750 | ||
1749 | /* Report bytes transferred, and transfer rates. */ | 1751 | /* Report bytes transferred, and transfer rates. */ |
1750 | total_time = get_current_time() - start_time; | 1752 | total_time = get_current_time() - start_time; |
1751 | packet_get_state(MODE_IN, NULL, NULL, NULL, &ibytes); | 1753 | packet_get_bytes(&ibytes, &obytes); |
1752 | packet_get_state(MODE_OUT, NULL, NULL, NULL, &obytes); | ||
1753 | verbose("Transferred: sent %llu, received %llu bytes, in %.1f seconds", | 1754 | verbose("Transferred: sent %llu, received %llu bytes, in %.1f seconds", |
1754 | (unsigned long long)obytes, (unsigned long long)ibytes, total_time); | 1755 | (unsigned long long)obytes, (unsigned long long)ibytes, total_time); |
1755 | if (total_time > 0) | 1756 | if (total_time > 0) |
@@ -1762,7 +1763,7 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) | |||
1762 | 1763 | ||
1763 | /*********/ | 1764 | /*********/ |
1764 | 1765 | ||
1765 | static void | 1766 | static int |
1766 | client_input_stdout_data(int type, u_int32_t seq, void *ctxt) | 1767 | client_input_stdout_data(int type, u_int32_t seq, void *ctxt) |
1767 | { | 1768 | { |
1768 | u_int data_len; | 1769 | u_int data_len; |
@@ -1771,8 +1772,9 @@ client_input_stdout_data(int type, u_int32_t seq, void *ctxt) | |||
1771 | buffer_append(&stdout_buffer, data, data_len); | 1772 | buffer_append(&stdout_buffer, data, data_len); |
1772 | explicit_bzero(data, data_len); | 1773 | explicit_bzero(data, data_len); |
1773 | free(data); | 1774 | free(data); |
1775 | return 0; | ||
1774 | } | 1776 | } |
1775 | static void | 1777 | static int |
1776 | client_input_stderr_data(int type, u_int32_t seq, void *ctxt) | 1778 | client_input_stderr_data(int type, u_int32_t seq, void *ctxt) |
1777 | { | 1779 | { |
1778 | u_int data_len; | 1780 | u_int data_len; |
@@ -1781,8 +1783,9 @@ client_input_stderr_data(int type, u_int32_t seq, void *ctxt) | |||
1781 | buffer_append(&stderr_buffer, data, data_len); | 1783 | buffer_append(&stderr_buffer, data, data_len); |
1782 | explicit_bzero(data, data_len); | 1784 | explicit_bzero(data, data_len); |
1783 | free(data); | 1785 | free(data); |
1786 | return 0; | ||
1784 | } | 1787 | } |
1785 | static void | 1788 | static int |
1786 | client_input_exit_status(int type, u_int32_t seq, void *ctxt) | 1789 | client_input_exit_status(int type, u_int32_t seq, void *ctxt) |
1787 | { | 1790 | { |
1788 | exit_status = packet_get_int(); | 1791 | exit_status = packet_get_int(); |
@@ -1797,12 +1800,14 @@ client_input_exit_status(int type, u_int32_t seq, void *ctxt) | |||
1797 | packet_write_wait(); | 1800 | packet_write_wait(); |
1798 | /* Flag that we want to exit. */ | 1801 | /* Flag that we want to exit. */ |
1799 | quit_pending = 1; | 1802 | quit_pending = 1; |
1803 | return 0; | ||
1800 | } | 1804 | } |
1801 | static void | 1805 | |
1806 | static int | ||
1802 | client_input_agent_open(int type, u_int32_t seq, void *ctxt) | 1807 | client_input_agent_open(int type, u_int32_t seq, void *ctxt) |
1803 | { | 1808 | { |
1804 | Channel *c = NULL; | 1809 | Channel *c = NULL; |
1805 | int remote_id, sock; | 1810 | int r, remote_id, sock; |
1806 | 1811 | ||
1807 | /* Read the remote channel number from the message. */ | 1812 | /* Read the remote channel number from the message. */ |
1808 | remote_id = packet_get_int(); | 1813 | remote_id = packet_get_int(); |
@@ -1812,7 +1817,11 @@ client_input_agent_open(int type, u_int32_t seq, void *ctxt) | |||
1812 | * Get a connection to the local authentication agent (this may again | 1817 | * Get a connection to the local authentication agent (this may again |
1813 | * get forwarded). | 1818 | * get forwarded). |
1814 | */ | 1819 | */ |
1815 | sock = ssh_get_authentication_socket(); | 1820 | if ((r = ssh_get_authentication_socket(&sock)) != 0 && |
1821 | r != SSH_ERR_AGENT_NOT_PRESENT) | ||
1822 | debug("%s: ssh_get_authentication_socket: %s", | ||
1823 | __func__, ssh_err(r)); | ||
1824 | |||
1816 | 1825 | ||
1817 | /* | 1826 | /* |
1818 | * If we could not connect the agent, send an error message back to | 1827 | * If we could not connect the agent, send an error message back to |
@@ -1837,6 +1846,7 @@ client_input_agent_open(int type, u_int32_t seq, void *ctxt) | |||
1837 | packet_put_int(c->self); | 1846 | packet_put_int(c->self); |
1838 | } | 1847 | } |
1839 | packet_send(); | 1848 | packet_send(); |
1849 | return 0; | ||
1840 | } | 1850 | } |
1841 | 1851 | ||
1842 | static Channel * | 1852 | static Channel * |
@@ -1930,7 +1940,7 @@ static Channel * | |||
1930 | client_request_agent(const char *request_type, int rchan) | 1940 | client_request_agent(const char *request_type, int rchan) |
1931 | { | 1941 | { |
1932 | Channel *c = NULL; | 1942 | Channel *c = NULL; |
1933 | int sock; | 1943 | int r, sock; |
1934 | 1944 | ||
1935 | if (!options.forward_agent) { | 1945 | if (!options.forward_agent) { |
1936 | error("Warning: ssh server tried agent forwarding."); | 1946 | error("Warning: ssh server tried agent forwarding."); |
@@ -1938,9 +1948,12 @@ client_request_agent(const char *request_type, int rchan) | |||
1938 | "malicious server."); | 1948 | "malicious server."); |
1939 | return NULL; | 1949 | return NULL; |
1940 | } | 1950 | } |
1941 | sock = ssh_get_authentication_socket(); | 1951 | if ((r = ssh_get_authentication_socket(&sock)) != 0) { |
1942 | if (sock < 0) | 1952 | if (r != SSH_ERR_AGENT_NOT_PRESENT) |
1953 | debug("%s: ssh_get_authentication_socket: %s", | ||
1954 | __func__, ssh_err(r)); | ||
1943 | return NULL; | 1955 | return NULL; |
1956 | } | ||
1944 | c = channel_new("authentication agent connection", | 1957 | c = channel_new("authentication agent connection", |
1945 | SSH_CHANNEL_OPEN, sock, sock, -1, | 1958 | SSH_CHANNEL_OPEN, sock, sock, -1, |
1946 | CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, | 1959 | CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, |
@@ -1994,7 +2007,7 @@ client_request_tun_fwd(int tun_mode, int local_tun, int remote_tun) | |||
1994 | } | 2007 | } |
1995 | 2008 | ||
1996 | /* XXXX move to generic input handler */ | 2009 | /* XXXX move to generic input handler */ |
1997 | static void | 2010 | static int |
1998 | client_input_channel_open(int type, u_int32_t seq, void *ctxt) | 2011 | client_input_channel_open(int type, u_int32_t seq, void *ctxt) |
1999 | { | 2012 | { |
2000 | Channel *c = NULL; | 2013 | Channel *c = NULL; |
@@ -2045,8 +2058,10 @@ client_input_channel_open(int type, u_int32_t seq, void *ctxt) | |||
2045 | packet_send(); | 2058 | packet_send(); |
2046 | } | 2059 | } |
2047 | free(ctype); | 2060 | free(ctype); |
2061 | return 0; | ||
2048 | } | 2062 | } |
2049 | static void | 2063 | |
2064 | static int | ||
2050 | client_input_channel_req(int type, u_int32_t seq, void *ctxt) | 2065 | client_input_channel_req(int type, u_int32_t seq, void *ctxt) |
2051 | { | 2066 | { |
2052 | Channel *c = NULL; | 2067 | Channel *c = NULL; |
@@ -2091,18 +2106,395 @@ client_input_channel_req(int type, u_int32_t seq, void *ctxt) | |||
2091 | packet_send(); | 2106 | packet_send(); |
2092 | } | 2107 | } |
2093 | free(rtype); | 2108 | free(rtype); |
2109 | return 0; | ||
2110 | } | ||
2111 | |||
2112 | struct hostkeys_update_ctx { | ||
2113 | /* The hostname and (optionally) IP address string for the server */ | ||
2114 | char *host_str, *ip_str; | ||
2115 | |||
2116 | /* | ||
2117 | * Keys received from the server and a flag for each indicating | ||
2118 | * whether they already exist in known_hosts. | ||
2119 | * keys_seen is filled in by hostkeys_find() and later (for new | ||
2120 | * keys) by client_global_hostkeys_private_confirm(). | ||
2121 | */ | ||
2122 | struct sshkey **keys; | ||
2123 | int *keys_seen; | ||
2124 | size_t nkeys; | ||
2125 | |||
2126 | size_t nnew; | ||
2127 | |||
2128 | /* | ||
2129 | * Keys that are in known_hosts, but were not present in the update | ||
2130 | * from the server (i.e. scheduled to be deleted). | ||
2131 | * Filled in by hostkeys_find(). | ||
2132 | */ | ||
2133 | struct sshkey **old_keys; | ||
2134 | size_t nold; | ||
2135 | }; | ||
2136 | |||
2137 | static void | ||
2138 | hostkeys_update_ctx_free(struct hostkeys_update_ctx *ctx) | ||
2139 | { | ||
2140 | size_t i; | ||
2141 | |||
2142 | if (ctx == NULL) | ||
2143 | return; | ||
2144 | for (i = 0; i < ctx->nkeys; i++) | ||
2145 | sshkey_free(ctx->keys[i]); | ||
2146 | free(ctx->keys); | ||
2147 | free(ctx->keys_seen); | ||
2148 | for (i = 0; i < ctx->nold; i++) | ||
2149 | sshkey_free(ctx->old_keys[i]); | ||
2150 | free(ctx->old_keys); | ||
2151 | free(ctx->host_str); | ||
2152 | free(ctx->ip_str); | ||
2153 | free(ctx); | ||
2154 | } | ||
2155 | |||
2156 | static int | ||
2157 | hostkeys_find(struct hostkey_foreach_line *l, void *_ctx) | ||
2158 | { | ||
2159 | struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; | ||
2160 | size_t i; | ||
2161 | struct sshkey **tmp; | ||
2162 | |||
2163 | if (l->status != HKF_STATUS_MATCHED || l->key == NULL || | ||
2164 | l->key->type == KEY_RSA1) | ||
2165 | return 0; | ||
2166 | |||
2167 | /* Mark off keys we've already seen for this host */ | ||
2168 | for (i = 0; i < ctx->nkeys; i++) { | ||
2169 | if (sshkey_equal(l->key, ctx->keys[i])) { | ||
2170 | debug3("%s: found %s key at %s:%ld", __func__, | ||
2171 | sshkey_ssh_name(ctx->keys[i]), l->path, l->linenum); | ||
2172 | ctx->keys_seen[i] = 1; | ||
2173 | return 0; | ||
2174 | } | ||
2175 | } | ||
2176 | /* This line contained a key that not offered by the server */ | ||
2177 | debug3("%s: deprecated %s key at %s:%ld", __func__, | ||
2178 | sshkey_ssh_name(l->key), l->path, l->linenum); | ||
2179 | if ((tmp = reallocarray(ctx->old_keys, ctx->nold + 1, | ||
2180 | sizeof(*ctx->old_keys))) == NULL) | ||
2181 | fatal("%s: reallocarray failed nold = %zu", | ||
2182 | __func__, ctx->nold); | ||
2183 | ctx->old_keys = tmp; | ||
2184 | ctx->old_keys[ctx->nold++] = l->key; | ||
2185 | l->key = NULL; | ||
2186 | |||
2187 | return 0; | ||
2188 | } | ||
2189 | |||
2190 | static void | ||
2191 | update_known_hosts(struct hostkeys_update_ctx *ctx) | ||
2192 | { | ||
2193 | int r, was_raw = 0; | ||
2194 | int loglevel = options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK ? | ||
2195 | SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_VERBOSE; | ||
2196 | char *fp, *response; | ||
2197 | size_t i; | ||
2198 | |||
2199 | for (i = 0; i < ctx->nkeys; i++) { | ||
2200 | if (ctx->keys_seen[i] != 2) | ||
2201 | continue; | ||
2202 | if ((fp = sshkey_fingerprint(ctx->keys[i], | ||
2203 | options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) | ||
2204 | fatal("%s: sshkey_fingerprint failed", __func__); | ||
2205 | do_log2(loglevel, "Learned new hostkey: %s %s", | ||
2206 | sshkey_type(ctx->keys[i]), fp); | ||
2207 | free(fp); | ||
2208 | } | ||
2209 | for (i = 0; i < ctx->nold; i++) { | ||
2210 | if ((fp = sshkey_fingerprint(ctx->old_keys[i], | ||
2211 | options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) | ||
2212 | fatal("%s: sshkey_fingerprint failed", __func__); | ||
2213 | do_log2(loglevel, "Deprecating obsolete hostkey: %s %s", | ||
2214 | sshkey_type(ctx->old_keys[i]), fp); | ||
2215 | free(fp); | ||
2216 | } | ||
2217 | if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK) { | ||
2218 | if (get_saved_tio() != NULL) { | ||
2219 | leave_raw_mode(1); | ||
2220 | was_raw = 1; | ||
2221 | } | ||
2222 | response = NULL; | ||
2223 | for (i = 0; !quit_pending && i < 3; i++) { | ||
2224 | free(response); | ||
2225 | response = read_passphrase("Accept updated hostkeys? " | ||
2226 | "(yes/no): ", RP_ECHO); | ||
2227 | if (strcasecmp(response, "yes") == 0) | ||
2228 | break; | ||
2229 | else if (quit_pending || response == NULL || | ||
2230 | strcasecmp(response, "no") == 0) { | ||
2231 | options.update_hostkeys = 0; | ||
2232 | break; | ||
2233 | } else { | ||
2234 | do_log2(loglevel, "Please enter " | ||
2235 | "\"yes\" or \"no\""); | ||
2236 | } | ||
2237 | } | ||
2238 | if (quit_pending || i >= 3 || response == NULL) | ||
2239 | options.update_hostkeys = 0; | ||
2240 | free(response); | ||
2241 | if (was_raw) | ||
2242 | enter_raw_mode(1); | ||
2243 | } | ||
2244 | |||
2245 | /* | ||
2246 | * Now that all the keys are verified, we can go ahead and replace | ||
2247 | * them in known_hosts (assuming SSH_UPDATE_HOSTKEYS_ASK didn't | ||
2248 | * cancel the operation). | ||
2249 | */ | ||
2250 | if (options.update_hostkeys != 0 && | ||
2251 | (r = hostfile_replace_entries(options.user_hostfiles[0], | ||
2252 | ctx->host_str, ctx->ip_str, ctx->keys, ctx->nkeys, | ||
2253 | options.hash_known_hosts, 0, | ||
2254 | options.fingerprint_hash)) != 0) | ||
2255 | error("%s: hostfile_replace_entries failed: %s", | ||
2256 | __func__, ssh_err(r)); | ||
2094 | } | 2257 | } |
2258 | |||
2095 | static void | 2259 | static void |
2260 | client_global_hostkeys_private_confirm(int type, u_int32_t seq, void *_ctx) | ||
2261 | { | ||
2262 | struct ssh *ssh = active_state; /* XXX */ | ||
2263 | struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; | ||
2264 | size_t i, ndone; | ||
2265 | struct sshbuf *signdata; | ||
2266 | int r; | ||
2267 | const u_char *sig; | ||
2268 | size_t siglen; | ||
2269 | |||
2270 | if (ctx->nnew == 0) | ||
2271 | fatal("%s: ctx->nnew == 0", __func__); /* sanity */ | ||
2272 | if (type != SSH2_MSG_REQUEST_SUCCESS) { | ||
2273 | error("Server failed to confirm ownership of " | ||
2274 | "private host keys"); | ||
2275 | hostkeys_update_ctx_free(ctx); | ||
2276 | return; | ||
2277 | } | ||
2278 | if ((signdata = sshbuf_new()) == NULL) | ||
2279 | fatal("%s: sshbuf_new failed", __func__); | ||
2280 | /* Don't want to accidentally accept an unbound signature */ | ||
2281 | if (ssh->kex->session_id_len == 0) | ||
2282 | fatal("%s: ssh->kex->session_id_len == 0", __func__); | ||
2283 | /* | ||
2284 | * Expect a signature for each of the ctx->nnew private keys we | ||
2285 | * haven't seen before. They will be in the same order as the | ||
2286 | * ctx->keys where the corresponding ctx->keys_seen[i] == 0. | ||
2287 | */ | ||
2288 | for (ndone = i = 0; i < ctx->nkeys; i++) { | ||
2289 | if (ctx->keys_seen[i]) | ||
2290 | continue; | ||
2291 | /* Prepare data to be signed: session ID, unique string, key */ | ||
2292 | sshbuf_reset(signdata); | ||
2293 | if ( (r = sshbuf_put_cstring(signdata, | ||
2294 | "hostkeys-prove-00@openssh.com")) != 0 || | ||
2295 | (r = sshbuf_put_string(signdata, ssh->kex->session_id, | ||
2296 | ssh->kex->session_id_len)) != 0 || | ||
2297 | (r = sshkey_puts(ctx->keys[i], signdata)) != 0) | ||
2298 | fatal("%s: failed to prepare signature: %s", | ||
2299 | __func__, ssh_err(r)); | ||
2300 | /* Extract and verify signature */ | ||
2301 | if ((r = sshpkt_get_string_direct(ssh, &sig, &siglen)) != 0) { | ||
2302 | error("%s: couldn't parse message: %s", | ||
2303 | __func__, ssh_err(r)); | ||
2304 | goto out; | ||
2305 | } | ||
2306 | if ((r = sshkey_verify(ctx->keys[i], sig, siglen, | ||
2307 | sshbuf_ptr(signdata), sshbuf_len(signdata), 0)) != 0) { | ||
2308 | error("%s: server gave bad signature for %s key %zu", | ||
2309 | __func__, sshkey_type(ctx->keys[i]), i); | ||
2310 | goto out; | ||
2311 | } | ||
2312 | /* Key is good. Mark it as 'seen' */ | ||
2313 | ctx->keys_seen[i] = 2; | ||
2314 | ndone++; | ||
2315 | } | ||
2316 | if (ndone != ctx->nnew) | ||
2317 | fatal("%s: ndone != ctx->nnew (%zu / %zu)", __func__, | ||
2318 | ndone, ctx->nnew); /* Shouldn't happen */ | ||
2319 | ssh_packet_check_eom(ssh); | ||
2320 | |||
2321 | /* Make the edits to known_hosts */ | ||
2322 | update_known_hosts(ctx); | ||
2323 | out: | ||
2324 | hostkeys_update_ctx_free(ctx); | ||
2325 | } | ||
2326 | |||
2327 | /* | ||
2328 | * Handle hostkeys-00@openssh.com global request to inform the client of all | ||
2329 | * the server's hostkeys. The keys are checked against the user's | ||
2330 | * HostkeyAlgorithms preference before they are accepted. | ||
2331 | */ | ||
2332 | static int | ||
2333 | client_input_hostkeys(void) | ||
2334 | { | ||
2335 | struct ssh *ssh = active_state; /* XXX */ | ||
2336 | const u_char *blob = NULL; | ||
2337 | size_t i, len = 0; | ||
2338 | struct sshbuf *buf = NULL; | ||
2339 | struct sshkey *key = NULL, **tmp; | ||
2340 | int r; | ||
2341 | char *fp; | ||
2342 | static int hostkeys_seen = 0; /* XXX use struct ssh */ | ||
2343 | extern struct sockaddr_storage hostaddr; /* XXX from ssh.c */ | ||
2344 | struct hostkeys_update_ctx *ctx = NULL; | ||
2345 | |||
2346 | if (hostkeys_seen) | ||
2347 | fatal("%s: server already sent hostkeys", __func__); | ||
2348 | if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK && | ||
2349 | options.batch_mode) | ||
2350 | return 1; /* won't ask in batchmode, so don't even try */ | ||
2351 | if (!options.update_hostkeys || options.num_user_hostfiles <= 0) | ||
2352 | return 1; | ||
2353 | |||
2354 | ctx = xcalloc(1, sizeof(*ctx)); | ||
2355 | while (ssh_packet_remaining(ssh) > 0) { | ||
2356 | sshkey_free(key); | ||
2357 | key = NULL; | ||
2358 | if ((r = sshpkt_get_string_direct(ssh, &blob, &len)) != 0) { | ||
2359 | error("%s: couldn't parse message: %s", | ||
2360 | __func__, ssh_err(r)); | ||
2361 | goto out; | ||
2362 | } | ||
2363 | if ((r = sshkey_from_blob(blob, len, &key)) != 0) { | ||
2364 | error("%s: parse key: %s", __func__, ssh_err(r)); | ||
2365 | goto out; | ||
2366 | } | ||
2367 | fp = sshkey_fingerprint(key, options.fingerprint_hash, | ||
2368 | SSH_FP_DEFAULT); | ||
2369 | debug3("%s: received %s key %s", __func__, | ||
2370 | sshkey_type(key), fp); | ||
2371 | free(fp); | ||
2372 | /* Check that the key is accepted in HostkeyAlgorithms */ | ||
2373 | if (options.hostkeyalgorithms != NULL && | ||
2374 | match_pattern_list(sshkey_ssh_name(key), | ||
2375 | options.hostkeyalgorithms, | ||
2376 | strlen(options.hostkeyalgorithms), 0) != 1) { | ||
2377 | debug3("%s: %s key not permitted by HostkeyAlgorithms", | ||
2378 | __func__, sshkey_ssh_name(key)); | ||
2379 | continue; | ||
2380 | } | ||
2381 | /* Skip certs */ | ||
2382 | if (sshkey_is_cert(key)) { | ||
2383 | debug3("%s: %s key is a certificate; skipping", | ||
2384 | __func__, sshkey_ssh_name(key)); | ||
2385 | continue; | ||
2386 | } | ||
2387 | /* Ensure keys are unique */ | ||
2388 | for (i = 0; i < ctx->nkeys; i++) { | ||
2389 | if (sshkey_equal(key, ctx->keys[i])) { | ||
2390 | error("%s: received duplicated %s host key", | ||
2391 | __func__, sshkey_ssh_name(key)); | ||
2392 | goto out; | ||
2393 | } | ||
2394 | } | ||
2395 | /* Key is good, record it */ | ||
2396 | if ((tmp = reallocarray(ctx->keys, ctx->nkeys + 1, | ||
2397 | sizeof(*ctx->keys))) == NULL) | ||
2398 | fatal("%s: reallocarray failed nkeys = %zu", | ||
2399 | __func__, ctx->nkeys); | ||
2400 | ctx->keys = tmp; | ||
2401 | ctx->keys[ctx->nkeys++] = key; | ||
2402 | key = NULL; | ||
2403 | } | ||
2404 | |||
2405 | if (ctx->nkeys == 0) { | ||
2406 | debug("%s: server sent no hostkeys", __func__); | ||
2407 | goto out; | ||
2408 | } | ||
2409 | |||
2410 | if ((ctx->keys_seen = calloc(ctx->nkeys, | ||
2411 | sizeof(*ctx->keys_seen))) == NULL) | ||
2412 | fatal("%s: calloc failed", __func__); | ||
2413 | |||
2414 | get_hostfile_hostname_ipaddr(host, | ||
2415 | options.check_host_ip ? (struct sockaddr *)&hostaddr : NULL, | ||
2416 | options.port, &ctx->host_str, | ||
2417 | options.check_host_ip ? &ctx->ip_str : NULL); | ||
2418 | |||
2419 | /* Find which keys we already know about. */ | ||
2420 | if ((r = hostkeys_foreach(options.user_hostfiles[0], hostkeys_find, | ||
2421 | ctx, ctx->host_str, ctx->ip_str, | ||
2422 | HKF_WANT_PARSE_KEY|HKF_WANT_MATCH)) != 0) { | ||
2423 | error("%s: hostkeys_foreach failed: %s", __func__, ssh_err(r)); | ||
2424 | goto out; | ||
2425 | } | ||
2426 | |||
2427 | /* Figure out if we have any new keys to add */ | ||
2428 | ctx->nnew = 0; | ||
2429 | for (i = 0; i < ctx->nkeys; i++) { | ||
2430 | if (!ctx->keys_seen[i]) | ||
2431 | ctx->nnew++; | ||
2432 | } | ||
2433 | |||
2434 | debug3("%s: %zu keys from server: %zu new, %zu retained. %zu to remove", | ||
2435 | __func__, ctx->nkeys, ctx->nnew, ctx->nkeys - ctx->nnew, ctx->nold); | ||
2436 | |||
2437 | if (ctx->nnew == 0 && ctx->nold != 0) { | ||
2438 | /* We have some keys to remove. Just do it. */ | ||
2439 | update_known_hosts(ctx); | ||
2440 | } else if (ctx->nnew != 0) { | ||
2441 | /* | ||
2442 | * We have received hitherto-unseen keys from the server. | ||
2443 | * Ask the server to confirm ownership of the private halves. | ||
2444 | */ | ||
2445 | debug3("%s: asking server to prove ownership for %zu keys", | ||
2446 | __func__, ctx->nnew); | ||
2447 | if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || | ||
2448 | (r = sshpkt_put_cstring(ssh, | ||
2449 | "hostkeys-prove-00@openssh.com")) != 0 || | ||
2450 | (r = sshpkt_put_u8(ssh, 1)) != 0) /* bool: want reply */ | ||
2451 | fatal("%s: cannot prepare packet: %s", | ||
2452 | __func__, ssh_err(r)); | ||
2453 | if ((buf = sshbuf_new()) == NULL) | ||
2454 | fatal("%s: sshbuf_new", __func__); | ||
2455 | for (i = 0; i < ctx->nkeys; i++) { | ||
2456 | if (ctx->keys_seen[i]) | ||
2457 | continue; | ||
2458 | sshbuf_reset(buf); | ||
2459 | if ((r = sshkey_putb(ctx->keys[i], buf)) != 0) | ||
2460 | fatal("%s: sshkey_putb: %s", | ||
2461 | __func__, ssh_err(r)); | ||
2462 | if ((r = sshpkt_put_stringb(ssh, buf)) != 0) | ||
2463 | fatal("%s: sshpkt_put_string: %s", | ||
2464 | __func__, ssh_err(r)); | ||
2465 | } | ||
2466 | if ((r = sshpkt_send(ssh)) != 0) | ||
2467 | fatal("%s: sshpkt_send: %s", __func__, ssh_err(r)); | ||
2468 | client_register_global_confirm( | ||
2469 | client_global_hostkeys_private_confirm, ctx); | ||
2470 | ctx = NULL; /* will be freed in callback */ | ||
2471 | } | ||
2472 | |||
2473 | /* Success */ | ||
2474 | out: | ||
2475 | hostkeys_update_ctx_free(ctx); | ||
2476 | sshkey_free(key); | ||
2477 | sshbuf_free(buf); | ||
2478 | /* | ||
2479 | * NB. Return success for all cases. The server doesn't need to know | ||
2480 | * what the client does with its hosts file. | ||
2481 | */ | ||
2482 | return 1; | ||
2483 | } | ||
2484 | |||
2485 | static int | ||
2096 | client_input_global_request(int type, u_int32_t seq, void *ctxt) | 2486 | client_input_global_request(int type, u_int32_t seq, void *ctxt) |
2097 | { | 2487 | { |
2098 | char *rtype; | 2488 | char *rtype; |
2099 | int want_reply; | 2489 | int want_reply; |
2100 | int success = 0; | 2490 | int success = 0; |
2101 | 2491 | ||
2102 | rtype = packet_get_string(NULL); | 2492 | rtype = packet_get_cstring(NULL); |
2103 | want_reply = packet_get_char(); | 2493 | want_reply = packet_get_char(); |
2104 | debug("client_input_global_request: rtype %s want_reply %d", | 2494 | debug("client_input_global_request: rtype %s want_reply %d", |
2105 | rtype, want_reply); | 2495 | rtype, want_reply); |
2496 | if (strcmp(rtype, "hostkeys-00@openssh.com") == 0) | ||
2497 | success = client_input_hostkeys(); | ||
2106 | if (want_reply) { | 2498 | if (want_reply) { |
2107 | packet_start(success ? | 2499 | packet_start(success ? |
2108 | SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE); | 2500 | SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE); |
@@ -2110,6 +2502,7 @@ client_input_global_request(int type, u_int32_t seq, void *ctxt) | |||
2110 | packet_write_wait(); | 2502 | packet_write_wait(); |
2111 | } | 2503 | } |
2112 | free(rtype); | 2504 | free(rtype); |
2505 | return 0; | ||
2113 | } | 2506 | } |
2114 | 2507 | ||
2115 | void | 2508 | void |