diff options
Diffstat (limited to 'clientloop.c')
-rw-r--r-- | clientloop.c | 137 |
1 files changed, 99 insertions, 38 deletions
diff --git a/clientloop.c b/clientloop.c index b5a1f7038..ebd0dbca1 100644 --- a/clientloop.c +++ b/clientloop.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: clientloop.c,v 1.327 2019/07/24 08:57:00 mestre Exp $ */ | 1 | /* $OpenBSD: clientloop.c,v 1.340 2020/02/02 09:45:34 dtucker 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 |
@@ -77,10 +77,10 @@ | |||
77 | #include <paths.h> | 77 | #include <paths.h> |
78 | #endif | 78 | #endif |
79 | #include <signal.h> | 79 | #include <signal.h> |
80 | #include <stdarg.h> | ||
81 | #include <stdio.h> | 80 | #include <stdio.h> |
82 | #include <stdlib.h> | 81 | #include <stdlib.h> |
83 | #include <string.h> | 82 | #include <string.h> |
83 | #include <stdarg.h> | ||
84 | #include <termios.h> | 84 | #include <termios.h> |
85 | #include <pwd.h> | 85 | #include <pwd.h> |
86 | #include <unistd.h> | 86 | #include <unistd.h> |
@@ -135,6 +135,12 @@ extern int muxserver_sock; /* XXX use mux_client_cleanup() instead */ | |||
135 | extern char *host; | 135 | extern char *host; |
136 | 136 | ||
137 | /* | 137 | /* |
138 | * If this field is not NULL, the ForwardAgent socket is this path and different | ||
139 | * instead of SSH_AUTH_SOCK. | ||
140 | */ | ||
141 | extern char *forward_agent_sock_path; | ||
142 | |||
143 | /* | ||
138 | * Flag to indicate that we have received a window change signal which has | 144 | * Flag to indicate that we have received a window change signal which has |
139 | * not yet been processed. This will cause a message indicating the new | 145 | * not yet been processed. This will cause a message indicating the new |
140 | * window size to be sent to the server a little later. This is volatile | 146 | * window size to be sent to the server a little later. This is volatile |
@@ -779,7 +785,7 @@ process_cmdline(struct ssh *ssh) | |||
779 | memset(&fwd, 0, sizeof(fwd)); | 785 | memset(&fwd, 0, sizeof(fwd)); |
780 | 786 | ||
781 | leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); | 787 | leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); |
782 | handler = signal(SIGINT, SIG_IGN); | 788 | handler = ssh_signal(SIGINT, SIG_IGN); |
783 | cmd = s = read_passphrase("\r\nssh> ", RP_ECHO); | 789 | cmd = s = read_passphrase("\r\nssh> ", RP_ECHO); |
784 | if (s == NULL) | 790 | if (s == NULL) |
785 | goto out; | 791 | goto out; |
@@ -877,7 +883,7 @@ process_cmdline(struct ssh *ssh) | |||
877 | } | 883 | } |
878 | 884 | ||
879 | out: | 885 | out: |
880 | signal(SIGINT, handler); | 886 | ssh_signal(SIGINT, handler); |
881 | enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); | 887 | enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); |
882 | free(cmd); | 888 | free(cmd); |
883 | free(fwd.listen_host); | 889 | free(fwd.listen_host); |
@@ -1300,15 +1306,15 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg, | |||
1300 | * Set signal handlers, (e.g. to restore non-blocking mode) | 1306 | * Set signal handlers, (e.g. to restore non-blocking mode) |
1301 | * but don't overwrite SIG_IGN, matches behaviour from rsh(1) | 1307 | * but don't overwrite SIG_IGN, matches behaviour from rsh(1) |
1302 | */ | 1308 | */ |
1303 | if (signal(SIGHUP, SIG_IGN) != SIG_IGN) | 1309 | if (ssh_signal(SIGHUP, SIG_IGN) != SIG_IGN) |
1304 | signal(SIGHUP, signal_handler); | 1310 | ssh_signal(SIGHUP, signal_handler); |
1305 | if (signal(SIGINT, SIG_IGN) != SIG_IGN) | 1311 | if (ssh_signal(SIGINT, SIG_IGN) != SIG_IGN) |
1306 | signal(SIGINT, signal_handler); | 1312 | ssh_signal(SIGINT, signal_handler); |
1307 | if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) | 1313 | if (ssh_signal(SIGQUIT, SIG_IGN) != SIG_IGN) |
1308 | signal(SIGQUIT, signal_handler); | 1314 | ssh_signal(SIGQUIT, signal_handler); |
1309 | if (signal(SIGTERM, SIG_IGN) != SIG_IGN) | 1315 | if (ssh_signal(SIGTERM, SIG_IGN) != SIG_IGN) |
1310 | signal(SIGTERM, signal_handler); | 1316 | ssh_signal(SIGTERM, signal_handler); |
1311 | signal(SIGWINCH, window_change_handler); | 1317 | ssh_signal(SIGWINCH, window_change_handler); |
1312 | 1318 | ||
1313 | if (have_pty) | 1319 | if (have_pty) |
1314 | enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); | 1320 | enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); |
@@ -1386,8 +1392,12 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg, | |||
1386 | * Send as much buffered packet data as possible to the | 1392 | * Send as much buffered packet data as possible to the |
1387 | * sender. | 1393 | * sender. |
1388 | */ | 1394 | */ |
1389 | if (FD_ISSET(connection_out, writeset)) | 1395 | if (FD_ISSET(connection_out, writeset)) { |
1390 | ssh_packet_write_poll(ssh); | 1396 | if ((r = ssh_packet_write_poll(ssh)) != 0) { |
1397 | sshpkt_fatal(ssh, r, | ||
1398 | "%s: ssh_packet_write_poll", __func__); | ||
1399 | } | ||
1400 | } | ||
1391 | 1401 | ||
1392 | /* | 1402 | /* |
1393 | * If we are a backgrounded control master, and the | 1403 | * If we are a backgrounded control master, and the |
@@ -1407,7 +1417,7 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg, | |||
1407 | /* Terminate the session. */ | 1417 | /* Terminate the session. */ |
1408 | 1418 | ||
1409 | /* Stop watching for window change. */ | 1419 | /* Stop watching for window change. */ |
1410 | signal(SIGWINCH, SIG_DFL); | 1420 | ssh_signal(SIGWINCH, SIG_DFL); |
1411 | 1421 | ||
1412 | if ((r = sshpkt_start(ssh, SSH2_MSG_DISCONNECT)) != 0 || | 1422 | if ((r = sshpkt_start(ssh, SSH2_MSG_DISCONNECT)) != 0 || |
1413 | (r = sshpkt_put_u32(ssh, SSH2_DISCONNECT_BY_APPLICATION)) != 0 || | 1423 | (r = sshpkt_put_u32(ssh, SSH2_DISCONNECT_BY_APPLICATION)) != 0 || |
@@ -1618,7 +1628,12 @@ client_request_agent(struct ssh *ssh, const char *request_type, int rchan) | |||
1618 | "malicious server."); | 1628 | "malicious server."); |
1619 | return NULL; | 1629 | return NULL; |
1620 | } | 1630 | } |
1621 | if ((r = ssh_get_authentication_socket(&sock)) != 0) { | 1631 | if (forward_agent_sock_path == NULL) { |
1632 | r = ssh_get_authentication_socket(&sock); | ||
1633 | } else { | ||
1634 | r = ssh_get_authentication_socket_path(forward_agent_sock_path, &sock); | ||
1635 | } | ||
1636 | if (r != 0) { | ||
1622 | if (r != SSH_ERR_AGENT_NOT_PRESENT) | 1637 | if (r != SSH_ERR_AGENT_NOT_PRESENT) |
1623 | debug("%s: ssh_get_authentication_socket: %s", | 1638 | debug("%s: ssh_get_authentication_socket: %s", |
1624 | __func__, ssh_err(r)); | 1639 | __func__, ssh_err(r)); |
@@ -1877,13 +1892,22 @@ hostkeys_find(struct hostkey_foreach_line *l, void *_ctx) | |||
1877 | } | 1892 | } |
1878 | 1893 | ||
1879 | static void | 1894 | static void |
1895 | hostkey_change_preamble(LogLevel loglevel) | ||
1896 | { | ||
1897 | do_log2(loglevel, "The server has updated its host keys."); | ||
1898 | do_log2(loglevel, "These changes were verified by the server's " | ||
1899 | "existing trusted key."); | ||
1900 | } | ||
1901 | |||
1902 | static void | ||
1880 | update_known_hosts(struct hostkeys_update_ctx *ctx) | 1903 | update_known_hosts(struct hostkeys_update_ctx *ctx) |
1881 | { | 1904 | { |
1882 | int r, was_raw = 0; | 1905 | int r, was_raw = 0, first = 1; |
1883 | LogLevel loglevel = options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK ? | 1906 | int asking = options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK; |
1884 | SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_VERBOSE; | 1907 | LogLevel loglevel = asking ? SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_VERBOSE; |
1885 | char *fp, *response; | 1908 | char *fp, *response; |
1886 | size_t i; | 1909 | size_t i; |
1910 | struct stat sb; | ||
1887 | 1911 | ||
1888 | for (i = 0; i < ctx->nkeys; i++) { | 1912 | for (i = 0; i < ctx->nkeys; i++) { |
1889 | if (ctx->keys_seen[i] != 2) | 1913 | if (ctx->keys_seen[i] != 2) |
@@ -1891,16 +1915,22 @@ update_known_hosts(struct hostkeys_update_ctx *ctx) | |||
1891 | if ((fp = sshkey_fingerprint(ctx->keys[i], | 1915 | if ((fp = sshkey_fingerprint(ctx->keys[i], |
1892 | options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) | 1916 | options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) |
1893 | fatal("%s: sshkey_fingerprint failed", __func__); | 1917 | fatal("%s: sshkey_fingerprint failed", __func__); |
1918 | if (first && asking) | ||
1919 | hostkey_change_preamble(loglevel); | ||
1894 | do_log2(loglevel, "Learned new hostkey: %s %s", | 1920 | do_log2(loglevel, "Learned new hostkey: %s %s", |
1895 | sshkey_type(ctx->keys[i]), fp); | 1921 | sshkey_type(ctx->keys[i]), fp); |
1922 | first = 0; | ||
1896 | free(fp); | 1923 | free(fp); |
1897 | } | 1924 | } |
1898 | for (i = 0; i < ctx->nold; i++) { | 1925 | for (i = 0; i < ctx->nold; i++) { |
1899 | if ((fp = sshkey_fingerprint(ctx->old_keys[i], | 1926 | if ((fp = sshkey_fingerprint(ctx->old_keys[i], |
1900 | options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) | 1927 | options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) |
1901 | fatal("%s: sshkey_fingerprint failed", __func__); | 1928 | fatal("%s: sshkey_fingerprint failed", __func__); |
1929 | if (first && asking) | ||
1930 | hostkey_change_preamble(loglevel); | ||
1902 | do_log2(loglevel, "Deprecating obsolete hostkey: %s %s", | 1931 | do_log2(loglevel, "Deprecating obsolete hostkey: %s %s", |
1903 | sshkey_type(ctx->old_keys[i]), fp); | 1932 | sshkey_type(ctx->old_keys[i]), fp); |
1933 | first = 0; | ||
1904 | free(fp); | 1934 | free(fp); |
1905 | } | 1935 | } |
1906 | if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK) { | 1936 | if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK) { |
@@ -1930,19 +1960,37 @@ update_known_hosts(struct hostkeys_update_ctx *ctx) | |||
1930 | if (was_raw) | 1960 | if (was_raw) |
1931 | enter_raw_mode(1); | 1961 | enter_raw_mode(1); |
1932 | } | 1962 | } |
1933 | 1963 | if (options.update_hostkeys == 0) | |
1964 | return; | ||
1934 | /* | 1965 | /* |
1935 | * Now that all the keys are verified, we can go ahead and replace | 1966 | * Now that all the keys are verified, we can go ahead and replace |
1936 | * them in known_hosts (assuming SSH_UPDATE_HOSTKEYS_ASK didn't | 1967 | * them in known_hosts (assuming SSH_UPDATE_HOSTKEYS_ASK didn't |
1937 | * cancel the operation). | 1968 | * cancel the operation). |
1938 | */ | 1969 | */ |
1939 | if (options.update_hostkeys != 0 && | 1970 | for (i = 0; i < options.num_user_hostfiles; i++) { |
1940 | (r = hostfile_replace_entries(options.user_hostfiles[0], | 1971 | /* |
1941 | ctx->host_str, ctx->ip_str, ctx->keys, ctx->nkeys, | 1972 | * NB. keys are only added to hostfiles[0], for the rest we |
1942 | options.hash_known_hosts, 0, | 1973 | * just delete the hostname entries. |
1943 | options.fingerprint_hash)) != 0) | 1974 | */ |
1944 | error("%s: hostfile_replace_entries failed: %s", | 1975 | if (stat(options.user_hostfiles[i], &sb) != 0) { |
1945 | __func__, ssh_err(r)); | 1976 | if (errno == ENOENT) { |
1977 | debug("%s: known hosts file %s does not exist", | ||
1978 | __func__, strerror(errno)); | ||
1979 | } else { | ||
1980 | error("%s: known hosts file %s inaccessible", | ||
1981 | __func__, strerror(errno)); | ||
1982 | } | ||
1983 | continue; | ||
1984 | } | ||
1985 | if ((r = hostfile_replace_entries(options.user_hostfiles[i], | ||
1986 | ctx->host_str, ctx->ip_str, | ||
1987 | i == 0 ? ctx->keys : NULL, i == 0 ? ctx->nkeys : 0, | ||
1988 | options.hash_known_hosts, 0, | ||
1989 | options.fingerprint_hash)) != 0) { | ||
1990 | error("%s: hostfile_replace_entries failed for %s: %s", | ||
1991 | __func__, options.user_hostfiles[i], ssh_err(r)); | ||
1992 | } | ||
1993 | } | ||
1946 | } | 1994 | } |
1947 | 1995 | ||
1948 | static void | 1996 | static void |
@@ -2003,7 +2051,8 @@ client_global_hostkeys_private_confirm(struct ssh *ssh, int type, | |||
2003 | sshkey_type_plain(ctx->keys[i]->type) == KEY_RSA; | 2051 | sshkey_type_plain(ctx->keys[i]->type) == KEY_RSA; |
2004 | if ((r = sshkey_verify(ctx->keys[i], sig, siglen, | 2052 | if ((r = sshkey_verify(ctx->keys[i], sig, siglen, |
2005 | sshbuf_ptr(signdata), sshbuf_len(signdata), | 2053 | sshbuf_ptr(signdata), sshbuf_len(signdata), |
2006 | use_kexsigtype ? ssh->kex->hostkey_alg : NULL, 0)) != 0) { | 2054 | use_kexsigtype ? ssh->kex->hostkey_alg : NULL, 0, |
2055 | NULL)) != 0) { | ||
2007 | error("%s: server gave bad signature for %s key %zu", | 2056 | error("%s: server gave bad signature for %s key %zu", |
2008 | __func__, sshkey_type(ctx->keys[i]), i); | 2057 | __func__, sshkey_type(ctx->keys[i]), i); |
2009 | goto out; | 2058 | goto out; |
@@ -2034,8 +2083,7 @@ static int | |||
2034 | key_accepted_by_hostkeyalgs(const struct sshkey *key) | 2083 | key_accepted_by_hostkeyalgs(const struct sshkey *key) |
2035 | { | 2084 | { |
2036 | const char *ktype = sshkey_ssh_name(key); | 2085 | const char *ktype = sshkey_ssh_name(key); |
2037 | const char *hostkeyalgs = options.hostkeyalgorithms != NULL ? | 2086 | const char *hostkeyalgs = options.hostkeyalgorithms; |
2038 | options.hostkeyalgorithms : KEX_DEFAULT_PK_ALG; | ||
2039 | 2087 | ||
2040 | if (key == NULL || key->type == KEY_UNSPEC) | 2088 | if (key == NULL || key->type == KEY_UNSPEC) |
2041 | return 0; | 2089 | return 0; |
@@ -2082,8 +2130,10 @@ client_input_hostkeys(struct ssh *ssh) | |||
2082 | goto out; | 2130 | goto out; |
2083 | } | 2131 | } |
2084 | if ((r = sshkey_from_blob(blob, len, &key)) != 0) { | 2132 | if ((r = sshkey_from_blob(blob, len, &key)) != 0) { |
2085 | error("%s: parse key: %s", __func__, ssh_err(r)); | 2133 | do_log2(r == SSH_ERR_KEY_TYPE_UNKNOWN ? |
2086 | goto out; | 2134 | SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_ERROR, |
2135 | "%s: parse key: %s", __func__, ssh_err(r)); | ||
2136 | continue; | ||
2087 | } | 2137 | } |
2088 | fp = sshkey_fingerprint(key, options.fingerprint_hash, | 2138 | fp = sshkey_fingerprint(key, options.fingerprint_hash, |
2089 | SSH_FP_DEFAULT); | 2139 | SSH_FP_DEFAULT); |
@@ -2135,11 +2185,22 @@ client_input_hostkeys(struct ssh *ssh) | |||
2135 | options.check_host_ip ? &ctx->ip_str : NULL); | 2185 | options.check_host_ip ? &ctx->ip_str : NULL); |
2136 | 2186 | ||
2137 | /* Find which keys we already know about. */ | 2187 | /* Find which keys we already know about. */ |
2138 | if ((r = hostkeys_foreach(options.user_hostfiles[0], hostkeys_find, | 2188 | for (i = 0; i < options.num_user_hostfiles; i++) { |
2139 | ctx, ctx->host_str, ctx->ip_str, | 2189 | debug("%s: searching %s for %s / %s", __func__, |
2140 | HKF_WANT_PARSE_KEY|HKF_WANT_MATCH)) != 0) { | 2190 | options.user_hostfiles[i], ctx->host_str, |
2141 | error("%s: hostkeys_foreach failed: %s", __func__, ssh_err(r)); | 2191 | ctx->ip_str ? ctx->ip_str : "(none)"); |
2142 | goto out; | 2192 | if ((r = hostkeys_foreach(options.user_hostfiles[i], |
2193 | hostkeys_find, ctx, ctx->host_str, ctx->ip_str, | ||
2194 | HKF_WANT_PARSE_KEY|HKF_WANT_MATCH)) != 0) { | ||
2195 | if (r == SSH_ERR_SYSTEM_ERROR && errno == ENOENT) { | ||
2196 | debug("%s: hostkeys file %s does not exist", | ||
2197 | __func__, options.user_hostfiles[i]); | ||
2198 | continue; | ||
2199 | } | ||
2200 | error("%s: hostkeys_foreach failed for %s: %s", | ||
2201 | __func__, options.user_hostfiles[i], ssh_err(r)); | ||
2202 | goto out; | ||
2203 | } | ||
2143 | } | 2204 | } |
2144 | 2205 | ||
2145 | /* Figure out if we have any new keys to add */ | 2206 | /* Figure out if we have any new keys to add */ |