diff options
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 397c96532..a9c8a90f0 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 | /* import options */ | 117 | /* import options */ |
115 | extern Options options; | 118 | extern Options options; |
@@ -191,9 +194,6 @@ TAILQ_HEAD(global_confirms, global_confirm); | |||
191 | static struct global_confirms global_confirms = | 194 | static struct global_confirms global_confirms = |
192 | TAILQ_HEAD_INITIALIZER(global_confirms); | 195 | TAILQ_HEAD_INITIALIZER(global_confirms); |
193 | 196 | ||
194 | /*XXX*/ | ||
195 | extern Kex *xxx_kex; | ||
196 | |||
197 | void ssh_process_session2_setup(int, int, int, Buffer *); | 197 | void ssh_process_session2_setup(int, int, int, Buffer *); |
198 | 198 | ||
199 | /* Restores stdin to blocking mode. */ | 199 | /* Restores stdin to blocking mode. */ |
@@ -341,12 +341,12 @@ client_x11_get_proto(const char *display, const char *xauth_path, | |||
341 | display = xdisplay; | 341 | display = xdisplay; |
342 | } | 342 | } |
343 | if (trusted == 0) { | 343 | if (trusted == 0) { |
344 | xauthdir = xmalloc(MAXPATHLEN); | 344 | xauthdir = xmalloc(PATH_MAX); |
345 | xauthfile = xmalloc(MAXPATHLEN); | 345 | xauthfile = xmalloc(PATH_MAX); |
346 | mktemp_proto(xauthdir, MAXPATHLEN); | 346 | mktemp_proto(xauthdir, PATH_MAX); |
347 | if (mkdtemp(xauthdir) != NULL) { | 347 | if (mkdtemp(xauthdir) != NULL) { |
348 | do_unlink = 1; | 348 | do_unlink = 1; |
349 | snprintf(xauthfile, MAXPATHLEN, "%s/xauthfile", | 349 | snprintf(xauthfile, PATH_MAX, "%s/xauthfile", |
350 | xauthdir); | 350 | xauthdir); |
351 | snprintf(cmd, sizeof(cmd), | 351 | snprintf(cmd, sizeof(cmd), |
352 | "%s -f %s generate %s " SSH_X11_PROTO | 352 | "%s -f %s generate %s " SSH_X11_PROTO |
@@ -538,13 +538,13 @@ client_check_window_change(void) | |||
538 | } | 538 | } |
539 | } | 539 | } |
540 | 540 | ||
541 | static void | 541 | static int |
542 | client_global_request_reply(int type, u_int32_t seq, void *ctxt) | 542 | client_global_request_reply(int type, u_int32_t seq, void *ctxt) |
543 | { | 543 | { |
544 | struct global_confirm *gc; | 544 | struct global_confirm *gc; |
545 | 545 | ||
546 | if ((gc = TAILQ_FIRST(&global_confirms)) == NULL) | 546 | if ((gc = TAILQ_FIRST(&global_confirms)) == NULL) |
547 | return; | 547 | return 0; |
548 | if (gc->cb != NULL) | 548 | if (gc->cb != NULL) |
549 | gc->cb(type, seq, gc->ctx); | 549 | gc->cb(type, seq, gc->ctx); |
550 | if (--gc->ref_count <= 0) { | 550 | if (--gc->ref_count <= 0) { |
@@ -554,6 +554,7 @@ client_global_request_reply(int type, u_int32_t seq, void *ctxt) | |||
554 | } | 554 | } |
555 | 555 | ||
556 | packet_set_alive_timeouts(0); | 556 | packet_set_alive_timeouts(0); |
557 | return 0; | ||
557 | } | 558 | } |
558 | 559 | ||
559 | static void | 560 | static void |
@@ -1414,8 +1415,7 @@ client_process_output(fd_set *writeset) | |||
1414 | static void | 1415 | static void |
1415 | client_process_buffered_input_packets(void) | 1416 | client_process_buffered_input_packets(void) |
1416 | { | 1417 | { |
1417 | dispatch_run(DISPATCH_NONBLOCK, &quit_pending, | 1418 | dispatch_run(DISPATCH_NONBLOCK, &quit_pending, active_state); |
1418 | compat20 ? xxx_kex : NULL); | ||
1419 | } | 1419 | } |
1420 | 1420 | ||
1421 | /* scan buf[] for '~' before sending data to the peer */ | 1421 | /* scan buf[] for '~' before sending data to the peer */ |
@@ -1469,7 +1469,7 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) | |||
1469 | { | 1469 | { |
1470 | fd_set *readset = NULL, *writeset = NULL; | 1470 | fd_set *readset = NULL, *writeset = NULL; |
1471 | double start_time, total_time; | 1471 | double start_time, total_time; |
1472 | int max_fd = 0, max_fd2 = 0, len, rekeying = 0; | 1472 | int r, max_fd = 0, max_fd2 = 0, len, rekeying = 0; |
1473 | u_int64_t ibytes, obytes; | 1473 | u_int64_t ibytes, obytes; |
1474 | u_int nalloc = 0; | 1474 | u_int nalloc = 0; |
1475 | char buf[100]; | 1475 | char buf[100]; |
@@ -1554,7 +1554,7 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) | |||
1554 | if (compat20 && session_closed && !channel_still_open()) | 1554 | if (compat20 && session_closed && !channel_still_open()) |
1555 | break; | 1555 | break; |
1556 | 1556 | ||
1557 | rekeying = (xxx_kex != NULL && !xxx_kex->done); | 1557 | rekeying = (active_state->kex != NULL && !active_state->kex->done); |
1558 | 1558 | ||
1559 | if (rekeying) { | 1559 | if (rekeying) { |
1560 | debug("rekeying in progress"); | 1560 | debug("rekeying in progress"); |
@@ -1598,8 +1598,10 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) | |||
1598 | channel_after_select(readset, writeset); | 1598 | channel_after_select(readset, writeset); |
1599 | if (need_rekeying || packet_need_rekeying()) { | 1599 | if (need_rekeying || packet_need_rekeying()) { |
1600 | debug("need rekeying"); | 1600 | debug("need rekeying"); |
1601 | xxx_kex->done = 0; | 1601 | active_state->kex->done = 0; |
1602 | kex_send_kexinit(xxx_kex); | 1602 | if ((r = kex_send_kexinit(active_state)) != 0) |
1603 | fatal("%s: kex_send_kexinit: %s", | ||
1604 | __func__, ssh_err(r)); | ||
1603 | need_rekeying = 0; | 1605 | need_rekeying = 0; |
1604 | } | 1606 | } |
1605 | } | 1607 | } |
@@ -1728,8 +1730,7 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) | |||
1728 | 1730 | ||
1729 | /* Report bytes transferred, and transfer rates. */ | 1731 | /* Report bytes transferred, and transfer rates. */ |
1730 | total_time = get_current_time() - start_time; | 1732 | total_time = get_current_time() - start_time; |
1731 | packet_get_state(MODE_IN, NULL, NULL, NULL, &ibytes); | 1733 | packet_get_bytes(&ibytes, &obytes); |
1732 | packet_get_state(MODE_OUT, NULL, NULL, NULL, &obytes); | ||
1733 | verbose("Transferred: sent %llu, received %llu bytes, in %.1f seconds", | 1734 | verbose("Transferred: sent %llu, received %llu bytes, in %.1f seconds", |
1734 | (unsigned long long)obytes, (unsigned long long)ibytes, total_time); | 1735 | (unsigned long long)obytes, (unsigned long long)ibytes, total_time); |
1735 | if (total_time > 0) | 1736 | if (total_time > 0) |
@@ -1742,7 +1743,7 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) | |||
1742 | 1743 | ||
1743 | /*********/ | 1744 | /*********/ |
1744 | 1745 | ||
1745 | static void | 1746 | static int |
1746 | client_input_stdout_data(int type, u_int32_t seq, void *ctxt) | 1747 | client_input_stdout_data(int type, u_int32_t seq, void *ctxt) |
1747 | { | 1748 | { |
1748 | u_int data_len; | 1749 | u_int data_len; |
@@ -1751,8 +1752,9 @@ client_input_stdout_data(int type, u_int32_t seq, void *ctxt) | |||
1751 | buffer_append(&stdout_buffer, data, data_len); | 1752 | buffer_append(&stdout_buffer, data, data_len); |
1752 | explicit_bzero(data, data_len); | 1753 | explicit_bzero(data, data_len); |
1753 | free(data); | 1754 | free(data); |
1755 | return 0; | ||
1754 | } | 1756 | } |
1755 | static void | 1757 | static int |
1756 | client_input_stderr_data(int type, u_int32_t seq, void *ctxt) | 1758 | client_input_stderr_data(int type, u_int32_t seq, void *ctxt) |
1757 | { | 1759 | { |
1758 | u_int data_len; | 1760 | u_int data_len; |
@@ -1761,8 +1763,9 @@ client_input_stderr_data(int type, u_int32_t seq, void *ctxt) | |||
1761 | buffer_append(&stderr_buffer, data, data_len); | 1763 | buffer_append(&stderr_buffer, data, data_len); |
1762 | explicit_bzero(data, data_len); | 1764 | explicit_bzero(data, data_len); |
1763 | free(data); | 1765 | free(data); |
1766 | return 0; | ||
1764 | } | 1767 | } |
1765 | static void | 1768 | static int |
1766 | client_input_exit_status(int type, u_int32_t seq, void *ctxt) | 1769 | client_input_exit_status(int type, u_int32_t seq, void *ctxt) |
1767 | { | 1770 | { |
1768 | exit_status = packet_get_int(); | 1771 | exit_status = packet_get_int(); |
@@ -1777,12 +1780,14 @@ client_input_exit_status(int type, u_int32_t seq, void *ctxt) | |||
1777 | packet_write_wait(); | 1780 | packet_write_wait(); |
1778 | /* Flag that we want to exit. */ | 1781 | /* Flag that we want to exit. */ |
1779 | quit_pending = 1; | 1782 | quit_pending = 1; |
1783 | return 0; | ||
1780 | } | 1784 | } |
1781 | static void | 1785 | |
1786 | static int | ||
1782 | 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) |
1783 | { | 1788 | { |
1784 | Channel *c = NULL; | 1789 | Channel *c = NULL; |
1785 | int remote_id, sock; | 1790 | int r, remote_id, sock; |
1786 | 1791 | ||
1787 | /* Read the remote channel number from the message. */ | 1792 | /* Read the remote channel number from the message. */ |
1788 | remote_id = packet_get_int(); | 1793 | remote_id = packet_get_int(); |
@@ -1792,7 +1797,11 @@ client_input_agent_open(int type, u_int32_t seq, void *ctxt) | |||
1792 | * Get a connection to the local authentication agent (this may again | 1797 | * Get a connection to the local authentication agent (this may again |
1793 | * get forwarded). | 1798 | * get forwarded). |
1794 | */ | 1799 | */ |
1795 | sock = ssh_get_authentication_socket(); | 1800 | if ((r = ssh_get_authentication_socket(&sock)) != 0 && |
1801 | r != SSH_ERR_AGENT_NOT_PRESENT) | ||
1802 | debug("%s: ssh_get_authentication_socket: %s", | ||
1803 | __func__, ssh_err(r)); | ||
1804 | |||
1796 | 1805 | ||
1797 | /* | 1806 | /* |
1798 | * If we could not connect the agent, send an error message back to | 1807 | * If we could not connect the agent, send an error message back to |
@@ -1817,6 +1826,7 @@ client_input_agent_open(int type, u_int32_t seq, void *ctxt) | |||
1817 | packet_put_int(c->self); | 1826 | packet_put_int(c->self); |
1818 | } | 1827 | } |
1819 | packet_send(); | 1828 | packet_send(); |
1829 | return 0; | ||
1820 | } | 1830 | } |
1821 | 1831 | ||
1822 | static Channel * | 1832 | static Channel * |
@@ -1910,7 +1920,7 @@ static Channel * | |||
1910 | client_request_agent(const char *request_type, int rchan) | 1920 | client_request_agent(const char *request_type, int rchan) |
1911 | { | 1921 | { |
1912 | Channel *c = NULL; | 1922 | Channel *c = NULL; |
1913 | int sock; | 1923 | int r, sock; |
1914 | 1924 | ||
1915 | if (!options.forward_agent) { | 1925 | if (!options.forward_agent) { |
1916 | error("Warning: ssh server tried agent forwarding."); | 1926 | error("Warning: ssh server tried agent forwarding."); |
@@ -1918,9 +1928,12 @@ client_request_agent(const char *request_type, int rchan) | |||
1918 | "malicious server."); | 1928 | "malicious server."); |
1919 | return NULL; | 1929 | return NULL; |
1920 | } | 1930 | } |
1921 | sock = ssh_get_authentication_socket(); | 1931 | if ((r = ssh_get_authentication_socket(&sock)) != 0) { |
1922 | if (sock < 0) | 1932 | if (r != SSH_ERR_AGENT_NOT_PRESENT) |
1933 | debug("%s: ssh_get_authentication_socket: %s", | ||
1934 | __func__, ssh_err(r)); | ||
1923 | return NULL; | 1935 | return NULL; |
1936 | } | ||
1924 | c = channel_new("authentication agent connection", | 1937 | c = channel_new("authentication agent connection", |
1925 | SSH_CHANNEL_OPEN, sock, sock, -1, | 1938 | SSH_CHANNEL_OPEN, sock, sock, -1, |
1926 | CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, | 1939 | CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, |
@@ -1974,7 +1987,7 @@ client_request_tun_fwd(int tun_mode, int local_tun, int remote_tun) | |||
1974 | } | 1987 | } |
1975 | 1988 | ||
1976 | /* XXXX move to generic input handler */ | 1989 | /* XXXX move to generic input handler */ |
1977 | static void | 1990 | static int |
1978 | client_input_channel_open(int type, u_int32_t seq, void *ctxt) | 1991 | client_input_channel_open(int type, u_int32_t seq, void *ctxt) |
1979 | { | 1992 | { |
1980 | Channel *c = NULL; | 1993 | Channel *c = NULL; |
@@ -2025,8 +2038,10 @@ client_input_channel_open(int type, u_int32_t seq, void *ctxt) | |||
2025 | packet_send(); | 2038 | packet_send(); |
2026 | } | 2039 | } |
2027 | free(ctype); | 2040 | free(ctype); |
2041 | return 0; | ||
2028 | } | 2042 | } |
2029 | static void | 2043 | |
2044 | static int | ||
2030 | 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) |
2031 | { | 2046 | { |
2032 | Channel *c = NULL; | 2047 | Channel *c = NULL; |
@@ -2071,18 +2086,395 @@ client_input_channel_req(int type, u_int32_t seq, void *ctxt) | |||
2071 | packet_send(); | 2086 | packet_send(); |
2072 | } | 2087 | } |
2073 | free(rtype); | 2088 | free(rtype); |
2089 | return 0; | ||
2090 | } | ||
2091 | |||
2092 | struct hostkeys_update_ctx { | ||
2093 | /* The hostname and (optionally) IP address string for the server */ | ||
2094 | char *host_str, *ip_str; | ||
2095 | |||
2096 | /* | ||
2097 | * Keys received from the server and a flag for each indicating | ||
2098 | * whether they already exist in known_hosts. | ||
2099 | * keys_seen is filled in by hostkeys_find() and later (for new | ||
2100 | * keys) by client_global_hostkeys_private_confirm(). | ||
2101 | */ | ||
2102 | struct sshkey **keys; | ||
2103 | int *keys_seen; | ||
2104 | size_t nkeys; | ||
2105 | |||
2106 | size_t nnew; | ||
2107 | |||
2108 | /* | ||
2109 | * Keys that are in known_hosts, but were not present in the update | ||
2110 | * from the server (i.e. scheduled to be deleted). | ||
2111 | * Filled in by hostkeys_find(). | ||
2112 | */ | ||
2113 | struct sshkey **old_keys; | ||
2114 | size_t nold; | ||
2115 | }; | ||
2116 | |||
2117 | static void | ||
2118 | hostkeys_update_ctx_free(struct hostkeys_update_ctx *ctx) | ||
2119 | { | ||
2120 | size_t i; | ||
2121 | |||
2122 | if (ctx == NULL) | ||
2123 | return; | ||
2124 | for (i = 0; i < ctx->nkeys; i++) | ||
2125 | sshkey_free(ctx->keys[i]); | ||
2126 | free(ctx->keys); | ||
2127 | free(ctx->keys_seen); | ||
2128 | for (i = 0; i < ctx->nold; i++) | ||
2129 | sshkey_free(ctx->old_keys[i]); | ||
2130 | free(ctx->old_keys); | ||
2131 | free(ctx->host_str); | ||
2132 | free(ctx->ip_str); | ||
2133 | free(ctx); | ||
2134 | } | ||
2135 | |||
2136 | static int | ||
2137 | hostkeys_find(struct hostkey_foreach_line *l, void *_ctx) | ||
2138 | { | ||
2139 | struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; | ||
2140 | size_t i; | ||
2141 | struct sshkey **tmp; | ||
2142 | |||
2143 | if (l->status != HKF_STATUS_MATCHED || l->key == NULL || | ||
2144 | l->key->type == KEY_RSA1) | ||
2145 | return 0; | ||
2146 | |||
2147 | /* Mark off keys we've already seen for this host */ | ||
2148 | for (i = 0; i < ctx->nkeys; i++) { | ||
2149 | if (sshkey_equal(l->key, ctx->keys[i])) { | ||
2150 | debug3("%s: found %s key at %s:%ld", __func__, | ||
2151 | sshkey_ssh_name(ctx->keys[i]), l->path, l->linenum); | ||
2152 | ctx->keys_seen[i] = 1; | ||
2153 | return 0; | ||
2154 | } | ||
2155 | } | ||
2156 | /* This line contained a key that not offered by the server */ | ||
2157 | debug3("%s: deprecated %s key at %s:%ld", __func__, | ||
2158 | sshkey_ssh_name(l->key), l->path, l->linenum); | ||
2159 | if ((tmp = reallocarray(ctx->old_keys, ctx->nold + 1, | ||
2160 | sizeof(*ctx->old_keys))) == NULL) | ||
2161 | fatal("%s: reallocarray failed nold = %zu", | ||
2162 | __func__, ctx->nold); | ||
2163 | ctx->old_keys = tmp; | ||
2164 | ctx->old_keys[ctx->nold++] = l->key; | ||
2165 | l->key = NULL; | ||
2166 | |||
2167 | return 0; | ||
2168 | } | ||
2169 | |||
2170 | static void | ||
2171 | update_known_hosts(struct hostkeys_update_ctx *ctx) | ||
2172 | { | ||
2173 | int r, was_raw = 0; | ||
2174 | int loglevel = options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK ? | ||
2175 | SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_VERBOSE; | ||
2176 | char *fp, *response; | ||
2177 | size_t i; | ||
2178 | |||
2179 | for (i = 0; i < ctx->nkeys; i++) { | ||
2180 | if (ctx->keys_seen[i] != 2) | ||
2181 | continue; | ||
2182 | if ((fp = sshkey_fingerprint(ctx->keys[i], | ||
2183 | options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) | ||
2184 | fatal("%s: sshkey_fingerprint failed", __func__); | ||
2185 | do_log2(loglevel, "Learned new hostkey: %s %s", | ||
2186 | sshkey_type(ctx->keys[i]), fp); | ||
2187 | free(fp); | ||
2188 | } | ||
2189 | for (i = 0; i < ctx->nold; i++) { | ||
2190 | if ((fp = sshkey_fingerprint(ctx->old_keys[i], | ||
2191 | options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) | ||
2192 | fatal("%s: sshkey_fingerprint failed", __func__); | ||
2193 | do_log2(loglevel, "Deprecating obsolete hostkey: %s %s", | ||
2194 | sshkey_type(ctx->old_keys[i]), fp); | ||
2195 | free(fp); | ||
2196 | } | ||
2197 | if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK) { | ||
2198 | if (get_saved_tio() != NULL) { | ||
2199 | leave_raw_mode(1); | ||
2200 | was_raw = 1; | ||
2201 | } | ||
2202 | response = NULL; | ||
2203 | for (i = 0; !quit_pending && i < 3; i++) { | ||
2204 | free(response); | ||
2205 | response = read_passphrase("Accept updated hostkeys? " | ||
2206 | "(yes/no): ", RP_ECHO); | ||
2207 | if (strcasecmp(response, "yes") == 0) | ||
2208 | break; | ||
2209 | else if (quit_pending || response == NULL || | ||
2210 | strcasecmp(response, "no") == 0) { | ||
2211 | options.update_hostkeys = 0; | ||
2212 | break; | ||
2213 | } else { | ||
2214 | do_log2(loglevel, "Please enter " | ||
2215 | "\"yes\" or \"no\""); | ||
2216 | } | ||
2217 | } | ||
2218 | if (quit_pending || i >= 3 || response == NULL) | ||
2219 | options.update_hostkeys = 0; | ||
2220 | free(response); | ||
2221 | if (was_raw) | ||
2222 | enter_raw_mode(1); | ||
2223 | } | ||
2224 | |||
2225 | /* | ||
2226 | * Now that all the keys are verified, we can go ahead and replace | ||
2227 | * them in known_hosts (assuming SSH_UPDATE_HOSTKEYS_ASK didn't | ||
2228 | * cancel the operation). | ||
2229 | */ | ||
2230 | if (options.update_hostkeys != 0 && | ||
2231 | (r = hostfile_replace_entries(options.user_hostfiles[0], | ||
2232 | ctx->host_str, ctx->ip_str, ctx->keys, ctx->nkeys, | ||
2233 | options.hash_known_hosts, 0, | ||
2234 | options.fingerprint_hash)) != 0) | ||
2235 | error("%s: hostfile_replace_entries failed: %s", | ||
2236 | __func__, ssh_err(r)); | ||
2074 | } | 2237 | } |
2238 | |||
2075 | static void | 2239 | static void |
2240 | client_global_hostkeys_private_confirm(int type, u_int32_t seq, void *_ctx) | ||
2241 | { | ||
2242 | struct ssh *ssh = active_state; /* XXX */ | ||
2243 | struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; | ||
2244 | size_t i, ndone; | ||
2245 | struct sshbuf *signdata; | ||
2246 | int r; | ||
2247 | const u_char *sig; | ||
2248 | size_t siglen; | ||
2249 | |||
2250 | if (ctx->nnew == 0) | ||
2251 | fatal("%s: ctx->nnew == 0", __func__); /* sanity */ | ||
2252 | if (type != SSH2_MSG_REQUEST_SUCCESS) { | ||
2253 | error("Server failed to confirm ownership of " | ||
2254 | "private host keys"); | ||
2255 | hostkeys_update_ctx_free(ctx); | ||
2256 | return; | ||
2257 | } | ||
2258 | if ((signdata = sshbuf_new()) == NULL) | ||
2259 | fatal("%s: sshbuf_new failed", __func__); | ||
2260 | /* Don't want to accidentally accept an unbound signature */ | ||
2261 | if (ssh->kex->session_id_len == 0) | ||
2262 | fatal("%s: ssh->kex->session_id_len == 0", __func__); | ||
2263 | /* | ||
2264 | * Expect a signature for each of the ctx->nnew private keys we | ||
2265 | * haven't seen before. They will be in the same order as the | ||
2266 | * ctx->keys where the corresponding ctx->keys_seen[i] == 0. | ||
2267 | */ | ||
2268 | for (ndone = i = 0; i < ctx->nkeys; i++) { | ||
2269 | if (ctx->keys_seen[i]) | ||
2270 | continue; | ||
2271 | /* Prepare data to be signed: session ID, unique string, key */ | ||
2272 | sshbuf_reset(signdata); | ||
2273 | if ( (r = sshbuf_put_cstring(signdata, | ||
2274 | "hostkeys-prove-00@openssh.com")) != 0 || | ||
2275 | (r = sshbuf_put_string(signdata, ssh->kex->session_id, | ||
2276 | ssh->kex->session_id_len)) != 0 || | ||
2277 | (r = sshkey_puts(ctx->keys[i], signdata)) != 0) | ||
2278 | fatal("%s: failed to prepare signature: %s", | ||
2279 | __func__, ssh_err(r)); | ||
2280 | /* Extract and verify signature */ | ||
2281 | if ((r = sshpkt_get_string_direct(ssh, &sig, &siglen)) != 0) { | ||
2282 | error("%s: couldn't parse message: %s", | ||
2283 | __func__, ssh_err(r)); | ||
2284 | goto out; | ||
2285 | } | ||
2286 | if ((r = sshkey_verify(ctx->keys[i], sig, siglen, | ||
2287 | sshbuf_ptr(signdata), sshbuf_len(signdata), 0)) != 0) { | ||
2288 | error("%s: server gave bad signature for %s key %zu", | ||
2289 | __func__, sshkey_type(ctx->keys[i]), i); | ||
2290 | goto out; | ||
2291 | } | ||
2292 | /* Key is good. Mark it as 'seen' */ | ||
2293 | ctx->keys_seen[i] = 2; | ||
2294 | ndone++; | ||
2295 | } | ||
2296 | if (ndone != ctx->nnew) | ||
2297 | fatal("%s: ndone != ctx->nnew (%zu / %zu)", __func__, | ||
2298 | ndone, ctx->nnew); /* Shouldn't happen */ | ||
2299 | ssh_packet_check_eom(ssh); | ||
2300 | |||
2301 | /* Make the edits to known_hosts */ | ||
2302 | update_known_hosts(ctx); | ||
2303 | out: | ||
2304 | hostkeys_update_ctx_free(ctx); | ||
2305 | } | ||
2306 | |||
2307 | /* | ||
2308 | * Handle hostkeys-00@openssh.com global request to inform the client of all | ||
2309 | * the server's hostkeys. The keys are checked against the user's | ||
2310 | * HostkeyAlgorithms preference before they are accepted. | ||
2311 | */ | ||
2312 | static int | ||
2313 | client_input_hostkeys(void) | ||
2314 | { | ||
2315 | struct ssh *ssh = active_state; /* XXX */ | ||
2316 | const u_char *blob = NULL; | ||
2317 | size_t i, len = 0; | ||
2318 | struct sshbuf *buf = NULL; | ||
2319 | struct sshkey *key = NULL, **tmp; | ||
2320 | int r; | ||
2321 | char *fp; | ||
2322 | static int hostkeys_seen = 0; /* XXX use struct ssh */ | ||
2323 | extern struct sockaddr_storage hostaddr; /* XXX from ssh.c */ | ||
2324 | struct hostkeys_update_ctx *ctx = NULL; | ||
2325 | |||
2326 | if (hostkeys_seen) | ||
2327 | fatal("%s: server already sent hostkeys", __func__); | ||
2328 | if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK && | ||
2329 | options.batch_mode) | ||
2330 | return 1; /* won't ask in batchmode, so don't even try */ | ||
2331 | if (!options.update_hostkeys || options.num_user_hostfiles <= 0) | ||
2332 | return 1; | ||
2333 | |||
2334 | ctx = xcalloc(1, sizeof(*ctx)); | ||
2335 | while (ssh_packet_remaining(ssh) > 0) { | ||
2336 | sshkey_free(key); | ||
2337 | key = NULL; | ||
2338 | if ((r = sshpkt_get_string_direct(ssh, &blob, &len)) != 0) { | ||
2339 | error("%s: couldn't parse message: %s", | ||
2340 | __func__, ssh_err(r)); | ||
2341 | goto out; | ||
2342 | } | ||
2343 | if ((r = sshkey_from_blob(blob, len, &key)) != 0) { | ||
2344 | error("%s: parse key: %s", __func__, ssh_err(r)); | ||
2345 | goto out; | ||
2346 | } | ||
2347 | fp = sshkey_fingerprint(key, options.fingerprint_hash, | ||
2348 | SSH_FP_DEFAULT); | ||
2349 | debug3("%s: received %s key %s", __func__, | ||
2350 | sshkey_type(key), fp); | ||
2351 | free(fp); | ||
2352 | /* Check that the key is accepted in HostkeyAlgorithms */ | ||
2353 | if (options.hostkeyalgorithms != NULL && | ||
2354 | match_pattern_list(sshkey_ssh_name(key), | ||
2355 | options.hostkeyalgorithms, | ||
2356 | strlen(options.hostkeyalgorithms), 0) != 1) { | ||
2357 | debug3("%s: %s key not permitted by HostkeyAlgorithms", | ||
2358 | __func__, sshkey_ssh_name(key)); | ||
2359 | continue; | ||
2360 | } | ||
2361 | /* Skip certs */ | ||
2362 | if (sshkey_is_cert(key)) { | ||
2363 | debug3("%s: %s key is a certificate; skipping", | ||
2364 | __func__, sshkey_ssh_name(key)); | ||
2365 | continue; | ||
2366 | } | ||
2367 | /* Ensure keys are unique */ | ||
2368 | for (i = 0; i < ctx->nkeys; i++) { | ||
2369 | if (sshkey_equal(key, ctx->keys[i])) { | ||
2370 | error("%s: received duplicated %s host key", | ||
2371 | __func__, sshkey_ssh_name(key)); | ||
2372 | goto out; | ||
2373 | } | ||
2374 | } | ||
2375 | /* Key is good, record it */ | ||
2376 | if ((tmp = reallocarray(ctx->keys, ctx->nkeys + 1, | ||
2377 | sizeof(*ctx->keys))) == NULL) | ||
2378 | fatal("%s: reallocarray failed nkeys = %zu", | ||
2379 | __func__, ctx->nkeys); | ||
2380 | ctx->keys = tmp; | ||
2381 | ctx->keys[ctx->nkeys++] = key; | ||
2382 | key = NULL; | ||
2383 | } | ||
2384 | |||
2385 | if (ctx->nkeys == 0) { | ||
2386 | debug("%s: server sent no hostkeys", __func__); | ||
2387 | goto out; | ||
2388 | } | ||
2389 | |||
2390 | if ((ctx->keys_seen = calloc(ctx->nkeys, | ||
2391 | sizeof(*ctx->keys_seen))) == NULL) | ||
2392 | fatal("%s: calloc failed", __func__); | ||
2393 | |||
2394 | get_hostfile_hostname_ipaddr(host, | ||
2395 | options.check_host_ip ? (struct sockaddr *)&hostaddr : NULL, | ||
2396 | options.port, &ctx->host_str, | ||
2397 | options.check_host_ip ? &ctx->ip_str : NULL); | ||
2398 | |||
2399 | /* Find which keys we already know about. */ | ||
2400 | if ((r = hostkeys_foreach(options.user_hostfiles[0], hostkeys_find, | ||
2401 | ctx, ctx->host_str, ctx->ip_str, | ||
2402 | HKF_WANT_PARSE_KEY|HKF_WANT_MATCH)) != 0) { | ||
2403 | error("%s: hostkeys_foreach failed: %s", __func__, ssh_err(r)); | ||
2404 | goto out; | ||
2405 | } | ||
2406 | |||
2407 | /* Figure out if we have any new keys to add */ | ||
2408 | ctx->nnew = 0; | ||
2409 | for (i = 0; i < ctx->nkeys; i++) { | ||
2410 | if (!ctx->keys_seen[i]) | ||
2411 | ctx->nnew++; | ||
2412 | } | ||
2413 | |||
2414 | debug3("%s: %zu keys from server: %zu new, %zu retained. %zu to remove", | ||
2415 | __func__, ctx->nkeys, ctx->nnew, ctx->nkeys - ctx->nnew, ctx->nold); | ||
2416 | |||
2417 | if (ctx->nnew == 0 && ctx->nold != 0) { | ||
2418 | /* We have some keys to remove. Just do it. */ | ||
2419 | update_known_hosts(ctx); | ||
2420 | } else if (ctx->nnew != 0) { | ||
2421 | /* | ||
2422 | * We have received hitherto-unseen keys from the server. | ||
2423 | * Ask the server to confirm ownership of the private halves. | ||
2424 | */ | ||
2425 | debug3("%s: asking server to prove ownership for %zu keys", | ||
2426 | __func__, ctx->nnew); | ||
2427 | if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || | ||
2428 | (r = sshpkt_put_cstring(ssh, | ||
2429 | "hostkeys-prove-00@openssh.com")) != 0 || | ||
2430 | (r = sshpkt_put_u8(ssh, 1)) != 0) /* bool: want reply */ | ||
2431 | fatal("%s: cannot prepare packet: %s", | ||
2432 | __func__, ssh_err(r)); | ||
2433 | if ((buf = sshbuf_new()) == NULL) | ||
2434 | fatal("%s: sshbuf_new", __func__); | ||
2435 | for (i = 0; i < ctx->nkeys; i++) { | ||
2436 | if (ctx->keys_seen[i]) | ||
2437 | continue; | ||
2438 | sshbuf_reset(buf); | ||
2439 | if ((r = sshkey_putb(ctx->keys[i], buf)) != 0) | ||
2440 | fatal("%s: sshkey_putb: %s", | ||
2441 | __func__, ssh_err(r)); | ||
2442 | if ((r = sshpkt_put_stringb(ssh, buf)) != 0) | ||
2443 | fatal("%s: sshpkt_put_string: %s", | ||
2444 | __func__, ssh_err(r)); | ||
2445 | } | ||
2446 | if ((r = sshpkt_send(ssh)) != 0) | ||
2447 | fatal("%s: sshpkt_send: %s", __func__, ssh_err(r)); | ||
2448 | client_register_global_confirm( | ||
2449 | client_global_hostkeys_private_confirm, ctx); | ||
2450 | ctx = NULL; /* will be freed in callback */ | ||
2451 | } | ||
2452 | |||
2453 | /* Success */ | ||
2454 | out: | ||
2455 | hostkeys_update_ctx_free(ctx); | ||
2456 | sshkey_free(key); | ||
2457 | sshbuf_free(buf); | ||
2458 | /* | ||
2459 | * NB. Return success for all cases. The server doesn't need to know | ||
2460 | * what the client does with its hosts file. | ||
2461 | */ | ||
2462 | return 1; | ||
2463 | } | ||
2464 | |||
2465 | static int | ||
2076 | client_input_global_request(int type, u_int32_t seq, void *ctxt) | 2466 | client_input_global_request(int type, u_int32_t seq, void *ctxt) |
2077 | { | 2467 | { |
2078 | char *rtype; | 2468 | char *rtype; |
2079 | int want_reply; | 2469 | int want_reply; |
2080 | int success = 0; | 2470 | int success = 0; |
2081 | 2471 | ||
2082 | rtype = packet_get_string(NULL); | 2472 | rtype = packet_get_cstring(NULL); |
2083 | want_reply = packet_get_char(); | 2473 | want_reply = packet_get_char(); |
2084 | debug("client_input_global_request: rtype %s want_reply %d", | 2474 | debug("client_input_global_request: rtype %s want_reply %d", |
2085 | rtype, want_reply); | 2475 | rtype, want_reply); |
2476 | if (strcmp(rtype, "hostkeys-00@openssh.com") == 0) | ||
2477 | success = client_input_hostkeys(); | ||
2086 | if (want_reply) { | 2478 | if (want_reply) { |
2087 | packet_start(success ? | 2479 | packet_start(success ? |
2088 | SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE); | 2480 | SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE); |
@@ -2090,6 +2482,7 @@ client_input_global_request(int type, u_int32_t seq, void *ctxt) | |||
2090 | packet_write_wait(); | 2482 | packet_write_wait(); |
2091 | } | 2483 | } |
2092 | free(rtype); | 2484 | free(rtype); |
2485 | return 0; | ||
2093 | } | 2486 | } |
2094 | 2487 | ||
2095 | void | 2488 | void |