diff options
author | Alistair Delva <adelva@google.com> | 2020-08-20 16:14:23 -0700 |
---|---|---|
committer | Alistair Delva <adelva@google.com> | 2020-08-20 16:53:18 -0700 |
commit | d9da10d147d633fdb6ec65e17ff4b8447419d83e (patch) | |
tree | 8f93e8fdc2907f141e0924910bfec26669819f0b /clientloop.c | |
parent | 22246b08952d746a7cc5a292570636cf4277598f (diff) | |
parent | ecb2c02d994b3e21994f31a70ff911667c262f1f (diff) |
Merge upstream-master into master
Commit ecb2c02d994b3e21994f31a70ff911667c262f1f upstream
This nearly (but not quite) corresponds to V_8_3_P1; subsequent
cherry-picks will correct this.
Bug: 162492243
Change-Id: I3c079d86435b7c25aefff4538dc89a3002b1e25b
Diffstat (limited to 'clientloop.c')
-rw-r--r-- | clientloop.c | 1867 |
1 files changed, 821 insertions, 1046 deletions
diff --git a/clientloop.c b/clientloop.c index a9c8a90f..da396c72 100644 --- a/clientloop.c +++ b/clientloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: clientloop.c,v 1.272 2015/02/25 19:54:02 djm Exp $ */ +/* $OpenBSD: clientloop.c,v 1.344 2020/04/24 02:19:40 dtucker Exp $ */ /* * Author: Tatu Ylonen <ylo@cs.hut.fi> * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland @@ -61,7 +61,6 @@ #include "includes.h" -#include <sys/param.h> /* MIN MAX */ #include <sys/types.h> #include <sys/ioctl.h> #ifdef HAVE_SYS_STAT_H @@ -78,10 +77,10 @@ #include <paths.h> #endif #include <signal.h> -#include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <stdarg.h> #include <termios.h> #include <pwd.h> #include <unistd.h> @@ -90,16 +89,16 @@ #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" #include "ssh.h" -#include "ssh1.h" #include "ssh2.h" #include "packet.h" -#include "buffer.h" +#include "sshbuf.h" #include "compat.h" #include "channels.h" #include "dispatch.h" -#include "key.h" +#include "sshkey.h" #include "cipher.h" #include "kex.h" +#include "myproposal.h" #include "log.h" #include "misc.h" #include "readconf.h" @@ -110,7 +109,6 @@ #include "sshpty.h" #include "match.h" #include "msg.h" -#include "roaming.h" #include "ssherr.h" #include "hostfile.h" @@ -123,17 +121,26 @@ extern int stdin_null_flag; /* Flag indicating that no shell has been requested */ extern int no_shell_flag; +/* Flag indicating that ssh should daemonise after authentication is complete */ +extern int fork_after_authentication_flag; + /* Control socket */ extern int muxserver_sock; /* XXX use mux_client_cleanup() instead */ /* * Name of the host we are connecting to. This is the name given on the - * command line, or the HostName specified for the user-supplied name in a + * command line, or the Hostname specified for the user-supplied name in a * configuration file. */ extern char *host; /* + * If this field is not NULL, the ForwardAgent socket is this path and different + * instead of SSH_AUTH_SOCK. + */ +extern char *forward_agent_sock_path; + +/* * Flag to indicate that we have received a window change signal which has * not yet been processed. This will cause a message indicating the new * window size to be sent to the server a little later. This is volatile @@ -142,34 +149,23 @@ extern char *host; static volatile sig_atomic_t received_window_change_signal = 0; static volatile sig_atomic_t received_signal = 0; -/* Flag indicating whether the user's terminal is in non-blocking mode. */ -static int in_non_blocking_mode = 0; - /* Time when backgrounded control master using ControlPersist should exit */ static time_t control_persist_exit_time = 0; /* Common data for the client loop code. */ volatile sig_atomic_t quit_pending; /* Set non-zero to quit the loop. */ -static int escape_char1; /* Escape character. (proto1 only) */ -static int escape_pending1; /* Last character was an escape (proto1 only) */ static int last_was_cr; /* Last character was a newline. */ static int exit_status; /* Used to store the command exit status. */ -static int stdin_eof; /* EOF has been encountered on stderr. */ -static Buffer stdin_buffer; /* Buffer for stdin data. */ -static Buffer stdout_buffer; /* Buffer for stdout data. */ -static Buffer stderr_buffer; /* Buffer for stderr data. */ -static u_int buffer_high; /* Soft max buffer size. */ +static struct sshbuf *stderr_buffer; /* Used for final exit message. */ static int connection_in; /* Connection to server (input). */ static int connection_out; /* Connection to server (output). */ static int need_rekeying; /* Set to non-zero if rekeying is requested. */ static int session_closed; /* In SSH2: login session closed. */ -static int x11_refuse_time; /* If >0, refuse x11 opens after this time. */ +static u_int x11_refuse_time; /* If >0, refuse x11 opens after this time. */ -static void client_init_dispatch(void); +static void client_init_dispatch(struct ssh *ssh); int session_ident = -1; -int session_resumed = 0; - /* Track escape per proto2 channel */ struct escape_filter_ctx { int escape_pending; @@ -184,6 +180,7 @@ struct channel_reply_ctx { }; /* Global request success/failure callbacks */ +/* XXX move to struct ssh? */ struct global_confirm { TAILQ_ENTRY(global_confirm) entry; global_confirm_cb *cb; @@ -194,27 +191,7 @@ TAILQ_HEAD(global_confirms, global_confirm); static struct global_confirms global_confirms = TAILQ_HEAD_INITIALIZER(global_confirms); -void ssh_process_session2_setup(int, int, int, Buffer *); - -/* Restores stdin to blocking mode. */ - -static void -leave_non_blocking(void) -{ - if (in_non_blocking_mode) { - unset_nonblock(fileno(stdin)); - in_non_blocking_mode = 0; - } -} - -/* Puts stdin terminal in non-blocking mode. */ - -static void -enter_non_blocking(void) -{ - in_non_blocking_mode = 1; - set_nonblock(fileno(stdin)); -} +void ssh_process_session2_setup(int, int, int, struct sshbuf *); /* * Signal handler for the window change signal (SIGWINCH). This just sets a @@ -225,7 +202,6 @@ static void window_change_handler(int sig) { received_window_change_signal = 1; - signal(SIGWINCH, window_change_handler); } /* @@ -241,32 +217,19 @@ signal_handler(int sig) } /* - * Returns current time in seconds from Jan 1, 1970 with the maximum - * available resolution. - */ - -static double -get_current_time(void) -{ - struct timeval tv; - gettimeofday(&tv, NULL); - return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0; -} - -/* * Sets control_persist_exit_time to the absolute time when the * backgrounded control master should exit due to expiry of the * ControlPersist timeout. Sets it to 0 if we are not a backgrounded * control master process, or if there is no ControlPersist timeout. */ static void -set_control_persist_exit_time(void) +set_control_persist_exit_time(struct ssh *ssh) { if (muxserver_sock == -1 || !options.control_persist || options.control_persist_timeout == 0) { /* not using a ControlPersist timeout */ control_persist_exit_time = 0; - } else if (channel_still_open()) { + } else if (channel_still_open(ssh)) { /* some client connections are still open */ if (control_persist_exit_time > 0) debug2("%s: cancel scheduled exit", __func__); @@ -287,6 +250,9 @@ client_x11_display_valid(const char *display) { size_t i, dlen; + if (display == NULL) + return 0; + dlen = strlen(display); for (i = 0; i < dlen; i++) { if (!isalnum((u_char)display[i]) && @@ -298,36 +264,37 @@ client_x11_display_valid(const char *display) return 1; } -#define SSH_X11_PROTO "MIT-MAGIC-COOKIE-1" -void -client_x11_get_proto(const char *display, const char *xauth_path, - u_int trusted, u_int timeout, char **_proto, char **_data) +#define SSH_X11_PROTO "MIT-MAGIC-COOKIE-1" +#define X11_TIMEOUT_SLACK 60 +int +client_x11_get_proto(struct ssh *ssh, const char *display, + const char *xauth_path, u_int trusted, u_int timeout, + char **_proto, char **_data) { - char cmd[1024]; - char line[512]; - char xdisplay[512]; + char *cmd, line[512], xdisplay[512]; + char xauthfile[PATH_MAX], xauthdir[PATH_MAX]; static char proto[512], data[512]; FILE *f; - int got_data = 0, generated = 0, do_unlink = 0, i; - char *xauthdir, *xauthfile; + int got_data = 0, generated = 0, do_unlink = 0, r; struct stat st; - u_int now; + u_int now, x11_timeout_real; - xauthdir = xauthfile = NULL; *_proto = proto; *_data = data; - proto[0] = data[0] = '\0'; + proto[0] = data[0] = xauthfile[0] = xauthdir[0] = '\0'; - if (xauth_path == NULL ||(stat(xauth_path, &st) == -1)) { + if (!client_x11_display_valid(display)) { + if (display != NULL) + logit("DISPLAY \"%s\" invalid; disabling X11 forwarding", + display); + return -1; + } + if (xauth_path != NULL && stat(xauth_path, &st) == -1) { debug("No xauth program."); - } else if (!client_x11_display_valid(display)) { - logit("DISPLAY '%s' invalid, falling back to fake xauth data", - display); - } else { - if (display == NULL) { - debug("x11_get_proto: DISPLAY not set"); - return; - } + xauth_path = NULL; + } + + if (xauth_path != NULL) { /* * Handle FamilyLocal case where $DISPLAY does * not match an authorization entry. For this we @@ -336,33 +303,72 @@ client_x11_get_proto(const char *display, const char *xauth_path, * is not perfect. */ if (strncmp(display, "localhost:", 10) == 0) { - snprintf(xdisplay, sizeof(xdisplay), "unix:%s", - display + 10); + if ((r = snprintf(xdisplay, sizeof(xdisplay), "unix:%s", + display + 10)) < 0 || + (size_t)r >= sizeof(xdisplay)) { + error("%s: display name too long", __func__); + return -1; + } display = xdisplay; } if (trusted == 0) { - xauthdir = xmalloc(PATH_MAX); - xauthfile = xmalloc(PATH_MAX); - mktemp_proto(xauthdir, PATH_MAX); - if (mkdtemp(xauthdir) != NULL) { - do_unlink = 1; - snprintf(xauthfile, PATH_MAX, "%s/xauthfile", - xauthdir); - snprintf(cmd, sizeof(cmd), - "%s -f %s generate %s " SSH_X11_PROTO - " untrusted timeout %u 2>" _PATH_DEVNULL, - xauth_path, xauthfile, display, timeout); - debug2("x11_get_proto: %s", cmd); - if (system(cmd) == 0) - generated = 1; - if (x11_refuse_time == 0) { - now = monotime() + 1; - if (UINT_MAX - timeout < now) - x11_refuse_time = UINT_MAX; - else - x11_refuse_time = now + timeout; + /* + * Generate an untrusted X11 auth cookie. + * + * The authentication cookie should briefly outlive + * ssh's willingness to forward X11 connections to + * avoid nasty fail-open behaviour in the X server. + */ + mktemp_proto(xauthdir, sizeof(xauthdir)); + if (mkdtemp(xauthdir) == NULL) { + error("%s: mkdtemp: %s", + __func__, strerror(errno)); + return -1; + } + do_unlink = 1; + if ((r = snprintf(xauthfile, sizeof(xauthfile), + "%s/xauthfile", xauthdir)) < 0 || + (size_t)r >= sizeof(xauthfile)) { + error("%s: xauthfile path too long", __func__); + rmdir(xauthdir); + return -1; + } + + if (timeout == 0) { + /* auth doesn't time out */ + xasprintf(&cmd, "%s -f %s generate %s %s " + "untrusted 2>%s", + xauth_path, xauthfile, display, + SSH_X11_PROTO, _PATH_DEVNULL); + } else { + /* Add some slack to requested expiry */ + if (timeout < UINT_MAX - X11_TIMEOUT_SLACK) + x11_timeout_real = timeout + + X11_TIMEOUT_SLACK; + else { + /* Don't overflow on long timeouts */ + x11_timeout_real = UINT_MAX; } + xasprintf(&cmd, "%s -f %s generate %s %s " + "untrusted timeout %u 2>%s", + xauth_path, xauthfile, display, + SSH_X11_PROTO, x11_timeout_real, + _PATH_DEVNULL); + } + debug2("%s: xauth command: %s", __func__, cmd); + + if (timeout != 0 && x11_refuse_time == 0) { + now = monotime() + 1; + if (UINT_MAX - timeout < now) + x11_refuse_time = UINT_MAX; + else + x11_refuse_time = now + timeout; + channel_set_x11_refuse_time(ssh, + x11_refuse_time); } + if (system(cmd) == 0) + generated = 1; + free(cmd); } /* @@ -371,7 +377,7 @@ client_x11_get_proto(const char *display, const char *xauth_path, * above. */ if (trusted || generated) { - snprintf(cmd, sizeof(cmd), + xasprintf(&cmd, "%s %s%s list %s 2>" _PATH_DEVNULL, xauth_path, generated ? "-f " : "" , @@ -384,17 +390,21 @@ client_x11_get_proto(const char *display, const char *xauth_path, got_data = 1; if (f) pclose(f); - } else - error("Warning: untrusted X11 forwarding setup failed: " - "xauth key data not generated"); + free(cmd); + } } if (do_unlink) { unlink(xauthfile); rmdir(xauthdir); } - free(xauthdir); - free(xauthfile); + + /* Don't fall back to fake X11 data for untrusted forwarding */ + if (!trusted && !got_data) { + error("Warning: untrusted X11 forwarding setup failed: " + "xauth key data not generated"); + return -1; + } /* * If we didn't get authentication data, just make up some @@ -405,104 +415,20 @@ client_x11_get_proto(const char *display, const char *xauth_path, * for the local connection. */ if (!got_data) { - u_int32_t rnd = 0; + u_int8_t rnd[16]; + u_int i; logit("Warning: No xauth data; " "using fake authentication data for X11 forwarding."); strlcpy(proto, SSH_X11_PROTO, sizeof proto); - for (i = 0; i < 16; i++) { - if (i % 4 == 0) - rnd = arc4random(); + arc4random_buf(rnd, sizeof(rnd)); + for (i = 0; i < sizeof(rnd); i++) { snprintf(data + 2 * i, sizeof data - 2 * i, "%02x", - rnd & 0xff); - rnd >>= 8; - } - } -} - -/* - * This is called when the interactive is entered. This checks if there is - * an EOF coming on stdin. We must check this explicitly, as select() does - * not appear to wake up when redirecting from /dev/null. - */ - -static void -client_check_initial_eof_on_stdin(void) -{ - int len; - char buf[1]; - - /* - * If standard input is to be "redirected from /dev/null", we simply - * mark that we have seen an EOF and send an EOF message to the - * server. Otherwise, we try to read a single character; it appears - * that for some files, such /dev/null, select() never wakes up for - * read for this descriptor, which means that we never get EOF. This - * way we will get the EOF if stdin comes from /dev/null or similar. - */ - if (stdin_null_flag) { - /* Fake EOF on stdin. */ - debug("Sending eof."); - stdin_eof = 1; - packet_start(SSH_CMSG_EOF); - packet_send(); - } else { - enter_non_blocking(); - - /* Check for immediate EOF on stdin. */ - len = read(fileno(stdin), buf, 1); - if (len == 0) { - /* - * EOF. Record that we have seen it and send - * EOF to server. - */ - debug("Sending eof."); - stdin_eof = 1; - packet_start(SSH_CMSG_EOF); - packet_send(); - } else if (len > 0) { - /* - * Got data. We must store the data in the buffer, - * and also process it as an escape character if - * appropriate. - */ - if ((u_char) buf[0] == escape_char1) - escape_pending1 = 1; - else - buffer_append(&stdin_buffer, buf, 1); + rnd[i]); } - leave_non_blocking(); } -} - -/* - * Make packets from buffered stdin data, and buffer them for sending to the - * connection. - */ - -static void -client_make_packets_from_stdin_data(void) -{ - u_int len; - - /* Send buffered stdin data to the server. */ - while (buffer_len(&stdin_buffer) > 0 && - packet_not_very_much_data_to_write()) { - len = buffer_len(&stdin_buffer); - /* Keep the packets at reasonable size. */ - if (len > packet_get_maxsize()) - len = packet_get_maxsize(); - packet_start(SSH_CMSG_STDIN_DATA); - packet_put_string(buffer_ptr(&stdin_buffer), len); - packet_send(); - buffer_consume(&stdin_buffer, len); - /* If we have a pending EOF, send it now. */ - if (stdin_eof && buffer_len(&stdin_buffer) == 0) { - packet_start(SSH_CMSG_EOF); - packet_send(); - } - } + return 0; } /* @@ -513,61 +439,47 @@ client_make_packets_from_stdin_data(void) */ static void -client_check_window_change(void) +client_check_window_change(struct ssh *ssh) { - struct winsize ws; - - if (! received_window_change_signal) + if (!received_window_change_signal) return; - /** XXX race */ received_window_change_signal = 0; - - debug2("client_check_window_change: changed"); - - if (compat20) { - channel_send_window_changes(); - } else { - if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0) - return; - packet_start(SSH_CMSG_WINDOW_SIZE); - packet_put_int((u_int)ws.ws_row); - packet_put_int((u_int)ws.ws_col); - packet_put_int((u_int)ws.ws_xpixel); - packet_put_int((u_int)ws.ws_ypixel); - packet_send(); - } + debug2("%s: changed", __func__); + channel_send_window_changes(ssh); } static int -client_global_request_reply(int type, u_int32_t seq, void *ctxt) +client_global_request_reply(int type, u_int32_t seq, struct ssh *ssh) { struct global_confirm *gc; if ((gc = TAILQ_FIRST(&global_confirms)) == NULL) return 0; if (gc->cb != NULL) - gc->cb(type, seq, gc->ctx); + gc->cb(ssh, type, seq, gc->ctx); if (--gc->ref_count <= 0) { TAILQ_REMOVE(&global_confirms, gc, entry); - explicit_bzero(gc, sizeof(*gc)); - free(gc); + freezero(gc, sizeof(*gc)); } - packet_set_alive_timeouts(0); + ssh_packet_set_alive_timeouts(ssh, 0); return 0; } static void -server_alive_check(void) +server_alive_check(struct ssh *ssh) { - if (packet_inc_alive_timeouts() > options.server_alive_count_max) { + int r; + + if (ssh_packet_inc_alive_timeouts(ssh) > options.server_alive_count_max) { logit("Timeout, server %s not responding.", host); cleanup_exit(255); } - packet_start(SSH2_MSG_GLOBAL_REQUEST); - packet_put_cstring("keepalive@openssh.com"); - packet_put_char(1); /* boolean: want reply */ - packet_send(); + if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || + (r = sshpkt_put_cstring(ssh, "keepalive@openssh.com")) != 0 || + (r = sshpkt_put_u8(ssh, 1)) != 0 || /* boolean: want reply */ + (r = sshpkt_send(ssh)) != 0) + fatal("%s: send packet: %s", __func__, ssh_err(r)); /* Insert an empty placeholder to maintain ordering */ client_register_global_confirm(NULL, NULL); } @@ -577,51 +489,32 @@ server_alive_check(void) * one of the file descriptors). */ static void -client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, +client_wait_until_can_do_something(struct ssh *ssh, + fd_set **readsetp, fd_set **writesetp, int *maxfdp, u_int *nallocp, int rekeying) { struct timeval tv, *tvp; int timeout_secs; time_t minwait_secs = 0, server_alive_time = 0, now = monotime(); - int ret; + int r, ret; /* Add any selections by the channel mechanism. */ - channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, - &minwait_secs, rekeying); - - if (!compat20) { - /* Read from the connection, unless our buffers are full. */ - if (buffer_len(&stdout_buffer) < buffer_high && - buffer_len(&stderr_buffer) < buffer_high && - channel_not_very_much_buffered_data()) - FD_SET(connection_in, *readsetp); - /* - * Read from stdin, unless we have seen EOF or have very much - * buffered data to send to the server. - */ - if (!stdin_eof && packet_not_very_much_data_to_write()) - FD_SET(fileno(stdin), *readsetp); - - /* Select stdout/stderr if have data in buffer. */ - if (buffer_len(&stdout_buffer) > 0) - FD_SET(fileno(stdout), *writesetp); - if (buffer_len(&stderr_buffer) > 0) - FD_SET(fileno(stderr), *writesetp); - } else { - /* channel_prepare_select could have closed the last channel */ - if (session_closed && !channel_still_open() && - !packet_have_data_to_write()) { - /* clear mask since we did not call select() */ - memset(*readsetp, 0, *nallocp); - memset(*writesetp, 0, *nallocp); - return; - } else { - FD_SET(connection_in, *readsetp); - } + channel_prepare_select(ssh, readsetp, writesetp, maxfdp, + nallocp, &minwait_secs); + + /* channel_prepare_select could have closed the last channel */ + if (session_closed && !channel_still_open(ssh) && + !ssh_packet_have_data_to_write(ssh)) { + /* clear mask since we did not call select() */ + memset(*readsetp, 0, *nallocp); + memset(*writesetp, 0, *nallocp); + return; } + FD_SET(connection_in, *readsetp); + /* Select server connection if have data to write to the server. */ - if (packet_have_data_to_write()) + if (ssh_packet_have_data_to_write(ssh)) FD_SET(connection_out, *writesetp); /* @@ -631,21 +524,22 @@ client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, */ timeout_secs = INT_MAX; /* we use INT_MAX to mean no timeout */ - if (options.server_alive_interval > 0 && compat20) { + if (options.server_alive_interval > 0) { timeout_secs = options.server_alive_interval; server_alive_time = now + options.server_alive_interval; } - if (options.rekey_interval > 0 && compat20 && !rekeying) - timeout_secs = MIN(timeout_secs, packet_get_rekey_timeout()); - set_control_persist_exit_time(); + if (options.rekey_interval > 0 && !rekeying) + timeout_secs = MINIMUM(timeout_secs, + ssh_packet_get_rekey_timeout(ssh)); + set_control_persist_exit_time(ssh); if (control_persist_exit_time > 0) { - timeout_secs = MIN(timeout_secs, + timeout_secs = MINIMUM(timeout_secs, control_persist_exit_time - now); if (timeout_secs < 0) timeout_secs = 0; } if (minwait_secs != 0) - timeout_secs = MIN(timeout_secs, (int)minwait_secs); + timeout_secs = MINIMUM(timeout_secs, (int)minwait_secs); if (timeout_secs == INT_MAX) tvp = NULL; else { @@ -655,9 +549,7 @@ client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, } ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp); - if (ret < 0) { - char buf[100]; - + if (ret == -1) { /* * We have to clear the select masks, because we return. * We have to return, because the mainloop checks for the flags @@ -669,8 +561,9 @@ client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, if (errno == EINTR) return; /* Note: we might still have data in the buffers. */ - snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno)); - buffer_append(&stderr_buffer, buf, strlen(buf)); + if ((r = sshbuf_putf(stderr_buffer, + "select: %s\r\n", strerror(errno))) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); quit_pending = 1; } else if (ret == 0) { /* @@ -678,31 +571,27 @@ client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, * Keepalive we check here, rekeying is checked in clientloop. */ if (server_alive_time != 0 && server_alive_time <= monotime()) - server_alive_check(); + server_alive_check(ssh); } } static void -client_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr) +client_suspend_self(struct sshbuf *bin, struct sshbuf *bout, struct sshbuf *berr) { /* Flush stdout and stderr buffers. */ - if (buffer_len(bout) > 0) - atomicio(vwrite, fileno(stdout), buffer_ptr(bout), - buffer_len(bout)); - if (buffer_len(berr) > 0) - atomicio(vwrite, fileno(stderr), buffer_ptr(berr), - buffer_len(berr)); + if (sshbuf_len(bout) > 0) + atomicio(vwrite, fileno(stdout), sshbuf_mutable_ptr(bout), + sshbuf_len(bout)); + if (sshbuf_len(berr) > 0) + atomicio(vwrite, fileno(stderr), sshbuf_mutable_ptr(berr), + sshbuf_len(berr)); leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); - /* - * Free (and clear) the buffer to reduce the amount of data that gets - * written to swap. - */ - buffer_free(bin); - buffer_free(bout); - buffer_free(berr); + sshbuf_reset(bin); + sshbuf_reset(bout); + sshbuf_reset(berr); /* Send the suspend signal to the program itself. */ kill(getpid(), SIGTSTP); @@ -710,19 +599,14 @@ client_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr) /* Reset window sizes in case they have changed */ received_window_change_signal = 1; - /* OK, we have been continued by the user. Reinitialize buffers. */ - buffer_init(bin); - buffer_init(bout); - buffer_init(berr); - enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); } static void -client_process_net_input(fd_set *readset) +client_process_net_input(struct ssh *ssh, fd_set *readset) { - int len, cont = 0; char buf[SSH_IOBUFSZ]; + int r, len; /* * Read input from the server, and add any such data to the buffer of @@ -730,16 +614,17 @@ client_process_net_input(fd_set *readset) */ if (FD_ISSET(connection_in, readset)) { /* Read as much as possible. */ - len = roaming_read(connection_in, buf, sizeof(buf), &cont); - if (len == 0 && cont == 0) { + len = read(connection_in, buf, sizeof(buf)); + if (len == 0) { /* * Received EOF. The remote host has closed the * connection. */ - snprintf(buf, sizeof buf, + if ((r = sshbuf_putf(stderr_buffer, "Connection to %.300s closed by remote host.\r\n", - host); - buffer_append(&stderr_buffer, buf, strlen(buf)); + host)) != 0) + fatal("%s: buffer error: %s", + __func__, ssh_err(r)); quit_pending = 1; return; } @@ -747,32 +632,33 @@ client_process_net_input(fd_set *readset) * There is a kernel bug on Solaris that causes select to * sometimes wake up even though there is no data available. */ - if (len < 0 && + if (len == -1 && (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)) len = 0; - if (len < 0) { + if (len == -1) { /* * An error has encountered. Perhaps there is a * network problem. */ - snprintf(buf, sizeof buf, + if ((r = sshbuf_putf(stderr_buffer, "Read from remote host %.300s: %.100s\r\n", - host, strerror(errno)); - buffer_append(&stderr_buffer, buf, strlen(buf)); + host, strerror(errno))) != 0) + fatal("%s: buffer error: %s", + __func__, ssh_err(r)); quit_pending = 1; return; } - packet_process_incoming(buf, len); + ssh_packet_process_incoming(ssh, buf, len); } } static void -client_status_confirm(int type, Channel *c, void *ctx) +client_status_confirm(struct ssh *ssh, int type, Channel *c, void *ctx) { struct channel_reply_ctx *cr = (struct channel_reply_ctx *)ctx; char errmsg[256]; - int tochan; + int r, tochan; /* * If a TTY was explicitly requested, then a failure to allocate @@ -783,7 +669,7 @@ client_status_confirm(int type, Channel *c, void *ctx) options.request_tty == REQUEST_TTY_YES)) cr->action = CONFIRM_CLOSE; - /* XXX supress on mux _client_ quietmode */ + /* XXX suppress on mux _client_ quietmode */ tochan = options.log_level >= SYSLOG_LEVEL_ERROR && c->ctl_chan != -1 && c->extended_usage == CHAN_EXTENDED_WRITE; @@ -807,8 +693,10 @@ client_status_confirm(int type, Channel *c, void *ctx) * their stderr. */ if (tochan) { - buffer_append(&c->extended, errmsg, - strlen(errmsg)); + if ((r = sshbuf_put(c->extended, errmsg, + strlen(errmsg))) != 0) + fatal("%s: buffer error %s", __func__, + ssh_err(r)); } else error("%s", errmsg); if (cr->action == CONFIRM_TTY) { @@ -819,23 +707,23 @@ client_status_confirm(int type, Channel *c, void *ctx) if (c->self == session_ident) leave_raw_mode(0); else - mux_tty_alloc_failed(c); + mux_tty_alloc_failed(ssh, c); } else if (cr->action == CONFIRM_CLOSE) { - chan_read_failed(c); - chan_write_failed(c); + chan_read_failed(ssh, c); + chan_write_failed(ssh, c); } } free(cr); } static void -client_abandon_status_confirm(Channel *c, void *ctx) +client_abandon_status_confirm(struct ssh *ssh, Channel *c, void *ctx) { free(ctx); } void -client_expect_confirm(int id, const char *request, +client_expect_confirm(struct ssh *ssh, int id, const char *request, enum confirm_action action) { struct channel_reply_ctx *cr = xcalloc(1, sizeof(*cr)); @@ -843,7 +731,7 @@ client_expect_confirm(int id, const char *request, cr->request_type = request; cr->action = action; - channel_register_status_confirm(id, client_status_confirm, + channel_register_status_confirm(ssh, id, client_status_confirm, client_abandon_status_confirm, cr); } @@ -869,7 +757,7 @@ client_register_global_confirm(global_confirm_cb *cb, void *ctx) } static void -process_cmdline(void) +process_cmdline(struct ssh *ssh) { void (*handler)(int); char *s, *cmd; @@ -879,7 +767,7 @@ process_cmdline(void) memset(&fwd, 0, sizeof(fwd)); leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); - handler = signal(SIGINT, SIG_IGN); + handler = ssh_signal(SIGINT, SIG_IGN); cmd = s = read_passphrase("\r\nssh> ", RP_ECHO); if (s == NULL) goto out; @@ -932,11 +820,6 @@ process_cmdline(void) goto out; } - if (delete && !compat20) { - logit("Not supported for SSH protocol version 1."); - goto out; - } - while (isspace((u_char)*++s)) ; @@ -948,16 +831,16 @@ process_cmdline(void) goto out; } if (remote) - ok = channel_request_rforward_cancel(&fwd) == 0; + ok = channel_request_rforward_cancel(ssh, &fwd) == 0; else if (dynamic) - ok = channel_cancel_lport_listener(&fwd, + ok = channel_cancel_lport_listener(ssh, &fwd, 0, &options.fwd_opts) > 0; else - ok = channel_cancel_lport_listener(&fwd, + ok = channel_cancel_lport_listener(ssh, &fwd, CHANNEL_CANCEL_PORT_STATIC, &options.fwd_opts) > 0; if (!ok) { - logit("Unkown port forwarding."); + logit("Unknown port forwarding."); goto out; } logit("Canceled forwarding."); @@ -967,13 +850,13 @@ process_cmdline(void) goto out; } if (local || dynamic) { - if (!channel_setup_local_fwd_listener(&fwd, + if (!channel_setup_local_fwd_listener(ssh, &fwd, &options.fwd_opts)) { logit("Port forwarding failed."); goto out; } } else { - if (channel_request_remote_forwarding(&fwd) < 0) { + if (channel_request_remote_forwarding(ssh, &fwd) < 0) { logit("Port forwarding failed."); goto out; } @@ -982,7 +865,7 @@ process_cmdline(void) } out: - signal(SIGINT, handler); + ssh_signal(SIGINT, handler); enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); free(cmd); free(fwd.listen_host); @@ -993,10 +876,9 @@ out: /* reasons to suppress output of an escape command in help output */ #define SUPPRESS_NEVER 0 /* never suppress, always show */ -#define SUPPRESS_PROTO1 1 /* don't show in protocol 1 sessions */ -#define SUPPRESS_MUXCLIENT 2 /* don't show in mux client sessions */ -#define SUPPRESS_MUXMASTER 4 /* don't show in mux master sessions */ -#define SUPPRESS_SYSLOG 8 /* don't show when logging to syslog */ +#define SUPPRESS_MUXCLIENT 1 /* don't show in mux client sessions */ +#define SUPPRESS_MUXMASTER 2 /* don't show in mux master sessions */ +#define SUPPRESS_SYSLOG 4 /* don't show when logging to syslog */ struct escape_help_text { const char *cmd; const char *text; @@ -1006,9 +888,9 @@ static struct escape_help_text esc_txt[] = { {".", "terminate session", SUPPRESS_MUXMASTER}, {".", "terminate connection (and any multiplexed sessions)", SUPPRESS_MUXCLIENT}, - {"B", "send a BREAK to the remote system", SUPPRESS_PROTO1}, + {"B", "send a BREAK to the remote system", SUPPRESS_NEVER}, {"C", "open a command line", SUPPRESS_MUXCLIENT}, - {"R", "request rekey", SUPPRESS_PROTO1}, + {"R", "request rekey", SUPPRESS_NEVER}, {"V/v", "decrease/increase verbosity (LogLevel)", SUPPRESS_MUXCLIENT}, {"^Z", "suspend ssh", SUPPRESS_MUXCLIENT}, {"#", "list forwarded connections", SUPPRESS_NEVER}, @@ -1018,17 +900,17 @@ static struct escape_help_text esc_txt[] = { }; static void -print_escape_help(Buffer *b, int escape_char, int protocol2, int mux_client, +print_escape_help(struct sshbuf *b, int escape_char, int mux_client, int using_stderr) { unsigned int i, suppress_flags; - char string[1024]; + int r; - snprintf(string, sizeof string, "%c?\r\n" - "Supported escape sequences:\r\n", escape_char); - buffer_append(b, string, strlen(string)); + if ((r = sshbuf_putf(b, + "%c?\r\nSupported escape sequences:\r\n", escape_char)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); - suppress_flags = (protocol2 ? 0 : SUPPRESS_PROTO1) | + suppress_flags = (mux_client ? SUPPRESS_MUXCLIENT : 0) | (mux_client ? 0 : SUPPRESS_MUXMASTER) | (using_stderr ? 0 : SUPPRESS_SYSLOG); @@ -1036,45 +918,37 @@ print_escape_help(Buffer *b, int escape_char, int protocol2, int mux_client, for (i = 0; i < sizeof(esc_txt)/sizeof(esc_txt[0]); i++) { if (esc_txt[i].flags & suppress_flags) continue; - snprintf(string, sizeof string, " %c%-3s - %s\r\n", - escape_char, esc_txt[i].cmd, esc_txt[i].text); - buffer_append(b, string, strlen(string)); + if ((r = sshbuf_putf(b, " %c%-3s - %s\r\n", + escape_char, esc_txt[i].cmd, esc_txt[i].text)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); } - snprintf(string, sizeof string, + if ((r = sshbuf_putf(b, " %c%c - send the escape character by typing it twice\r\n" "(Note that escapes are only recognized immediately after " - "newline.)\r\n", escape_char, escape_char); - buffer_append(b, string, strlen(string)); + "newline.)\r\n", escape_char, escape_char)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); } -/* - * Process the characters one by one, call with c==NULL for proto1 case. +/* + * Process the characters one by one. */ static int -process_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr, +process_escapes(struct ssh *ssh, Channel *c, + struct sshbuf *bin, struct sshbuf *bout, struct sshbuf *berr, char *buf, int len) { - char string[1024]; pid_t pid; - int bytes = 0; + int r, bytes = 0; u_int i; u_char ch; char *s; - int *escape_pendingp, escape_char; - struct escape_filter_ctx *efc; + struct escape_filter_ctx *efc = c->filter_ctx == NULL ? + NULL : (struct escape_filter_ctx *)c->filter_ctx; + + if (c->filter_ctx == NULL) + return 0; - if (c == NULL) { - escape_pendingp = &escape_pending1; - escape_char = escape_char1; - } else { - if (c->filter_ctx == NULL) - return 0; - efc = (struct escape_filter_ctx *)c->filter_ctx; - escape_pendingp = &efc->escape_pending; - escape_char = efc->escape_char; - } - if (len <= 0) return (0); @@ -1082,27 +956,29 @@ process_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr, /* Get one character at a time. */ ch = buf[i]; - if (*escape_pendingp) { + if (efc->escape_pending) { /* We have previously seen an escape character. */ /* Clear the flag now. */ - *escape_pendingp = 0; + efc->escape_pending = 0; /* Process the escaped character. */ switch (ch) { case '.': /* Terminate the connection. */ - snprintf(string, sizeof string, "%c.\r\n", - escape_char); - buffer_append(berr, string, strlen(string)); - + if ((r = sshbuf_putf(berr, "%c.\r\n", + efc->escape_char)) != 0) + fatal("%s: buffer error: %s", + __func__, ssh_err(r)); if (c && c->ctl_chan != -1) { - chan_read_failed(c); - chan_write_failed(c); - if (c->detach_user) - c->detach_user(c->self, NULL); + chan_read_failed(ssh, c); + chan_write_failed(ssh, c); + if (c->detach_user) { + c->detach_user(ssh, + c->self, NULL); + } c->type = SSH_CHANNEL_ABANDONED; - buffer_clear(&c->input); - chan_ibuf_empty(c); + sshbuf_reset(c->input); + chan_ibuf_empty(ssh, c); return 0; } else quit_pending = 1; @@ -1117,18 +993,20 @@ process_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr, snprintf(b, sizeof b, "^Z"); else snprintf(b, sizeof b, "%c", ch); - snprintf(string, sizeof string, + if ((r = sshbuf_putf(berr, "%c%s escape not available to " "multiplexed sessions\r\n", - escape_char, b); - buffer_append(berr, string, - strlen(string)); + efc->escape_char, b)) != 0) + fatal("%s: buffer error: %s", + __func__, ssh_err(r)); continue; } /* Suspend the program. Inform the user */ - snprintf(string, sizeof string, - "%c^Z [suspend ssh]\r\n", escape_char); - buffer_append(berr, string, strlen(string)); + if ((r = sshbuf_putf(berr, + "%c^Z [suspend ssh]\r\n", + efc->escape_char)) != 0) + fatal("%s: buffer error: %s", + __func__, ssh_err(r)); /* Restore terminal modes and suspend. */ client_suspend_self(bin, bout, berr); @@ -1137,26 +1015,23 @@ process_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr, continue; case 'B': - if (compat20) { - snprintf(string, sizeof string, - "%cB\r\n", escape_char); - buffer_append(berr, string, - strlen(string)); - channel_request_start(c->self, - "break", 0); - packet_put_int(1000); - packet_send(); - } + if ((r = sshbuf_putf(berr, + "%cB\r\n", efc->escape_char)) != 0) + fatal("%s: buffer error: %s", + __func__, ssh_err(r)); + channel_request_start(ssh, c->self, "break", 0); + if ((r = sshpkt_put_u32(ssh, 1000)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal("%s: send packet: %s", __func__, + ssh_err(r)); continue; case 'R': - if (compat20) { - if (datafellows & SSH_BUG_NOREKEY) - logit("Server does not " - "support re-keying"); - else - need_rekeying = 1; - } + if (datafellows & SSH_BUG_NOREKEY) + logit("Server does not " + "support re-keying"); + else + need_rekeying = 1; continue; case 'V': @@ -1165,11 +1040,11 @@ process_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr, if (c && c->ctl_chan != -1) goto noescape; if (!log_is_on_stderr()) { - snprintf(string, sizeof string, + if ((r = sshbuf_putf(berr, "%c%c [Logging to syslog]\r\n", - escape_char, ch); - buffer_append(berr, string, - strlen(string)); + efc->escape_char, ch)) != 0) + fatal("%s: buffer error: %s", + __func__, ssh_err(r)); continue; } if (ch == 'V' && options.log_level > @@ -1178,10 +1053,12 @@ process_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr, if (ch == 'v' && options.log_level < SYSLOG_LEVEL_DEBUG3) log_change_level(++options.log_level); - snprintf(string, sizeof string, - "%c%c [LogLevel %s]\r\n", escape_char, ch, - log_level_name(options.log_level)); - buffer_append(berr, string, strlen(string)); + if ((r = sshbuf_putf(berr, + "%c%c [LogLevel %s]\r\n", + efc->escape_char, ch, + log_level_name(options.log_level))) != 0) + fatal("%s: buffer error: %s", + __func__, ssh_err(r)); continue; case '&': @@ -1197,15 +1074,17 @@ process_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr, options.request_tty == REQUEST_TTY_FORCE); /* Stop listening for new connections. */ - channel_stop_listening(); + channel_stop_listening(ssh); - snprintf(string, sizeof string, - "%c& [backgrounded]\n", escape_char); - buffer_append(berr, string, strlen(string)); + if ((r = sshbuf_putf(berr, + "%c& [backgrounded]\n", efc->escape_char)) + != 0) + fatal("%s: buffer error: %s", + __func__, ssh_err(r)); /* Fork into background. */ pid = fork(); - if (pid < 0) { + if (pid == -1) { error("fork: %.100s", strerror(errno)); continue; } @@ -1214,52 +1093,41 @@ process_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr, exit(0); } /* The child continues serving connections. */ - if (compat20) { - buffer_append(bin, "\004", 1); - /* fake EOF on stdin */ - return -1; - } else if (!stdin_eof) { - /* - * Sending SSH_CMSG_EOF alone does not - * always appear to be enough. So we - * try to send an EOF character first. - */ - packet_start(SSH_CMSG_STDIN_DATA); - packet_put_string("\004", 1); - packet_send(); - /* Close stdin. */ - stdin_eof = 1; - if (buffer_len(bin) == 0) { - packet_start(SSH_CMSG_EOF); - packet_send(); - } - } - continue; - + /* fake EOF on stdin */ + if ((r = sshbuf_put_u8(bin, 4)) != 0) + fatal("%s: buffer error: %s", + __func__, ssh_err(r)); + return -1; case '?': - print_escape_help(berr, escape_char, compat20, + print_escape_help(berr, efc->escape_char, (c && c->ctl_chan != -1), log_is_on_stderr()); continue; case '#': - snprintf(string, sizeof string, "%c#\r\n", - escape_char); - buffer_append(berr, string, strlen(string)); - s = channel_open_message(); - buffer_append(berr, s, strlen(s)); + if ((r = sshbuf_putf(berr, "%c#\r\n", + efc->escape_char)) != 0) + fatal("%s: buffer error: %s", + __func__, ssh_err(r)); + s = channel_open_message(ssh); + if ((r = sshbuf_put(berr, s, strlen(s))) != 0) + fatal("%s: buffer error: %s", + __func__, ssh_err(r)); free(s); continue; case 'C': if (c && c->ctl_chan != -1) goto noescape; - process_cmdline(); + process_cmdline(ssh); continue; default: - if (ch != escape_char) { - buffer_put_char(bin, escape_char); + if (ch != efc->escape_char) { + if ((r = sshbuf_put_u8(bin, + efc->escape_char)) != 0) + fatal("%s: buffer error: %s", + __func__, ssh_err(r)); bytes++; } /* Escaped characters fall through here */ @@ -1270,12 +1138,12 @@ process_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr, * The previous character was not an escape char. * Check if this is an escape. */ - if (last_was_cr && ch == escape_char) { + if (last_was_cr && ch == efc->escape_char) { /* * It is. Set the flag and continue to * next character. */ - *escape_pendingp = 1; + efc->escape_pending = 1; continue; } } @@ -1285,121 +1153,13 @@ process_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr, * and append it to the buffer. */ last_was_cr = (ch == '\r' || ch == '\n'); - buffer_put_char(bin, ch); + if ((r = sshbuf_put_u8(bin, ch)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); bytes++; } return bytes; } -static void -client_process_input(fd_set *readset) -{ - int len; - char buf[SSH_IOBUFSZ]; - - /* Read input from stdin. */ - if (FD_ISSET(fileno(stdin), readset)) { - /* Read as much as possible. */ - len = read(fileno(stdin), buf, sizeof(buf)); - if (len < 0 && - (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)) - return; /* we'll try again later */ - if (len <= 0) { - /* - * Received EOF or error. They are treated - * similarly, except that an error message is printed - * if it was an error condition. - */ - if (len < 0) { - snprintf(buf, sizeof buf, "read: %.100s\r\n", - strerror(errno)); - buffer_append(&stderr_buffer, buf, strlen(buf)); - } - /* Mark that we have seen EOF. */ - stdin_eof = 1; - /* - * Send an EOF message to the server unless there is - * data in the buffer. If there is data in the - * buffer, no message will be sent now. Code - * elsewhere will send the EOF when the buffer - * becomes empty if stdin_eof is set. - */ - if (buffer_len(&stdin_buffer) == 0) { - packet_start(SSH_CMSG_EOF); - packet_send(); - } - } else if (escape_char1 == SSH_ESCAPECHAR_NONE) { - /* - * Normal successful read, and no escape character. - * Just append the data to buffer. - */ - buffer_append(&stdin_buffer, buf, len); - } else { - /* - * Normal, successful read. But we have an escape - * character and have to process the characters one - * by one. - */ - if (process_escapes(NULL, &stdin_buffer, - &stdout_buffer, &stderr_buffer, buf, len) == -1) - return; - } - } -} - -static void -client_process_output(fd_set *writeset) -{ - int len; - char buf[100]; - - /* Write buffered output to stdout. */ - if (FD_ISSET(fileno(stdout), writeset)) { - /* Write as much data as possible. */ - len = write(fileno(stdout), buffer_ptr(&stdout_buffer), - buffer_len(&stdout_buffer)); - if (len <= 0) { - if (errno == EINTR || errno == EAGAIN || - errno == EWOULDBLOCK) - len = 0; - else { - /* - * An error or EOF was encountered. Put an - * error message to stderr buffer. - */ - snprintf(buf, sizeof buf, - "write stdout: %.50s\r\n", strerror(errno)); - buffer_append(&stderr_buffer, buf, strlen(buf)); - quit_pending = 1; - return; - } - } - /* Consume printed data from the buffer. */ - buffer_consume(&stdout_buffer, len); - } - /* Write buffered output to stderr. */ - if (FD_ISSET(fileno(stderr), writeset)) { - /* Write as much data as possible. */ - len = write(fileno(stderr), buffer_ptr(&stderr_buffer), - buffer_len(&stderr_buffer)); - if (len <= 0) { - if (errno == EINTR || errno == EAGAIN || - errno == EWOULDBLOCK) - len = 0; - else { - /* - * EOF or error, but can't even print - * error message. - */ - quit_pending = 1; - return; - } - } - /* Consume printed characters from the buffer. */ - buffer_consume(&stderr_buffer, len); - } -} - /* * Get packets from the connection input buffer, and process them as long as * there are packets available. @@ -1413,9 +1173,9 @@ client_process_output(fd_set *writeset) */ static void -client_process_buffered_input_packets(void) +client_process_buffered_input_packets(struct ssh *ssh) { - dispatch_run(DISPATCH_NONBLOCK, &quit_pending, active_state); + ssh_dispatch_run_fatal(ssh, DISPATCH_NONBLOCK, &quit_pending); } /* scan buf[] for '~' before sending data to the peer */ @@ -1434,25 +1194,25 @@ client_new_escape_filter_ctx(int escape_char) /* Free the escape filter context on channel free */ void -client_filter_cleanup(int cid, void *ctx) +client_filter_cleanup(struct ssh *ssh, int cid, void *ctx) { free(ctx); } int -client_simple_escape_filter(Channel *c, char *buf, int len) +client_simple_escape_filter(struct ssh *ssh, Channel *c, char *buf, int len) { if (c->extended_usage != CHAN_EXTENDED_WRITE) return 0; - return process_escapes(c, &c->input, &c->output, &c->extended, + return process_escapes(ssh, c, c->input, c->output, c->extended, buf, len); } static void -client_channel_closed(int id, void *arg) +client_channel_closed(struct ssh *ssh, int id, void *arg) { - channel_cancel_cleanup(id); + channel_cancel_cleanup(ssh, id); session_closed = 1; leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); } @@ -1463,121 +1223,128 @@ client_channel_closed(int id, void *arg) * remote host. If escape_char != SSH_ESCAPECHAR_NONE, it is the character * used as an escape character for terminating or suspending the session. */ - int -client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) +client_loop(struct ssh *ssh, int have_pty, int escape_char_arg, + int ssh2_chan_id) { fd_set *readset = NULL, *writeset = NULL; double start_time, total_time; - int r, max_fd = 0, max_fd2 = 0, len, rekeying = 0; + int r, max_fd = 0, max_fd2 = 0, len; u_int64_t ibytes, obytes; u_int nalloc = 0; char buf[100]; debug("Entering interactive session."); - start_time = get_current_time(); + if (options.control_master && + !option_clear_or_none(options.control_path)) { + debug("pledge: id"); + if (pledge("stdio rpath wpath cpath unix inet dns recvfd sendfd proc exec id tty", + NULL) == -1) + fatal("%s pledge(): %s", __func__, strerror(errno)); + + } else if (options.forward_x11 || options.permit_local_command) { + debug("pledge: exec"); + if (pledge("stdio rpath wpath cpath unix inet dns proc exec tty", + NULL) == -1) + fatal("%s pledge(): %s", __func__, strerror(errno)); + + } else if (options.update_hostkeys) { + debug("pledge: filesystem full"); + if (pledge("stdio rpath wpath cpath unix inet dns proc tty", + NULL) == -1) + fatal("%s pledge(): %s", __func__, strerror(errno)); + + } else if (!option_clear_or_none(options.proxy_command) || + fork_after_authentication_flag) { + debug("pledge: proc"); + if (pledge("stdio cpath unix inet dns proc tty", NULL) == -1) + fatal("%s pledge(): %s", __func__, strerror(errno)); + + } else { + debug("pledge: network"); + if (pledge("stdio unix inet dns proc tty", NULL) == -1) + fatal("%s pledge(): %s", __func__, strerror(errno)); + } + + start_time = monotime_double(); /* Initialize variables. */ - escape_pending1 = 0; last_was_cr = 1; exit_status = -1; - stdin_eof = 0; - buffer_high = 64 * 1024; - connection_in = packet_get_connection_in(); - connection_out = packet_get_connection_out(); - max_fd = MAX(connection_in, connection_out); - - if (!compat20) { - /* enable nonblocking unless tty */ - if (!isatty(fileno(stdin))) - set_nonblock(fileno(stdin)); - if (!isatty(fileno(stdout))) - set_nonblock(fileno(stdout)); - if (!isatty(fileno(stderr))) - set_nonblock(fileno(stderr)); - max_fd = MAX(max_fd, fileno(stdin)); - max_fd = MAX(max_fd, fileno(stdout)); - max_fd = MAX(max_fd, fileno(stderr)); - } + connection_in = ssh_packet_get_connection_in(ssh); + connection_out = ssh_packet_get_connection_out(ssh); + max_fd = MAXIMUM(connection_in, connection_out); + quit_pending = 0; - escape_char1 = escape_char_arg; - /* Initialize buffers. */ - buffer_init(&stdin_buffer); - buffer_init(&stdout_buffer); - buffer_init(&stderr_buffer); + /* Initialize buffer. */ + if ((stderr_buffer = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); - client_init_dispatch(); + client_init_dispatch(ssh); /* * Set signal handlers, (e.g. to restore non-blocking mode) * but don't overwrite SIG_IGN, matches behaviour from rsh(1) */ - if (signal(SIGHUP, SIG_IGN) != SIG_IGN) - signal(SIGHUP, signal_handler); - if (signal(SIGINT, SIG_IGN) != SIG_IGN) - signal(SIGINT, signal_handler); - if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) - signal(SIGQUIT, signal_handler); - if (signal(SIGTERM, SIG_IGN) != SIG_IGN) - signal(SIGTERM, signal_handler); - signal(SIGWINCH, window_change_handler); + if (ssh_signal(SIGHUP, SIG_IGN) != SIG_IGN) + ssh_signal(SIGHUP, signal_handler); + if (ssh_signal(SIGINT, SIG_IGN) != SIG_IGN) + ssh_signal(SIGINT, signal_handler); + if (ssh_signal(SIGQUIT, SIG_IGN) != SIG_IGN) + ssh_signal(SIGQUIT, signal_handler); + if (ssh_signal(SIGTERM, SIG_IGN) != SIG_IGN) + ssh_signal(SIGTERM, signal_handler); + ssh_signal(SIGWINCH, window_change_handler); if (have_pty) enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); - if (compat20) { - session_ident = ssh2_chan_id; - if (session_ident != -1) { - if (escape_char_arg != SSH_ESCAPECHAR_NONE) { - channel_register_filter(session_ident, - client_simple_escape_filter, NULL, - client_filter_cleanup, - client_new_escape_filter_ctx( - escape_char_arg)); - } - channel_register_cleanup(session_ident, - client_channel_closed, 0); + session_ident = ssh2_chan_id; + if (session_ident != -1) { + if (escape_char_arg != SSH_ESCAPECHAR_NONE) { + channel_register_filter(ssh, session_ident, + client_simple_escape_filter, NULL, + client_filter_cleanup, + client_new_escape_filter_ctx( + escape_char_arg)); } - } else { - /* Check if we should immediately send eof on stdin. */ - client_check_initial_eof_on_stdin(); + channel_register_cleanup(ssh, session_ident, + client_channel_closed, 0); } /* Main loop of the client for the interactive session mode. */ while (!quit_pending) { /* Process buffered packets sent by the server. */ - client_process_buffered_input_packets(); + client_process_buffered_input_packets(ssh); - if (compat20 && session_closed && !channel_still_open()) + if (session_closed && !channel_still_open(ssh)) break; - rekeying = (active_state->kex != NULL && !active_state->kex->done); - - if (rekeying) { + if (ssh_packet_is_rekeying(ssh)) { debug("rekeying in progress"); + } else if (need_rekeying) { + /* manual rekey request */ + debug("need rekeying"); + if ((r = kex_start_rekex(ssh)) != 0) + fatal("%s: kex_start_rekex: %s", __func__, + ssh_err(r)); + need_rekeying = 0; } else { /* - * Make packets of buffered stdin data, and buffer - * them for sending to the server. - */ - if (!compat20) - client_make_packets_from_stdin_data(); - - /* * Make packets from buffered channel data, and * enqueue them for sending to the server. */ - if (packet_not_very_much_data_to_write()) - channel_output_poll(); + if (ssh_packet_not_very_much_data_to_write(ssh)) + channel_output_poll(ssh); /* * Check if the window size has changed, and buffer a * message about it to the server if so. */ - client_check_window_change(); + client_check_window_change(ssh); if (quit_pending) break; @@ -1587,55 +1354,32 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) * available on one of the descriptors). */ max_fd2 = max_fd; - client_wait_until_can_do_something(&readset, &writeset, - &max_fd2, &nalloc, rekeying); + client_wait_until_can_do_something(ssh, &readset, &writeset, + &max_fd2, &nalloc, ssh_packet_is_rekeying(ssh)); if (quit_pending) break; /* Do channel operations unless rekeying in progress. */ - if (!rekeying) { - channel_after_select(readset, writeset); - if (need_rekeying || packet_need_rekeying()) { - debug("need rekeying"); - active_state->kex->done = 0; - if ((r = kex_send_kexinit(active_state)) != 0) - fatal("%s: kex_send_kexinit: %s", - __func__, ssh_err(r)); - need_rekeying = 0; - } - } + if (!ssh_packet_is_rekeying(ssh)) + channel_after_select(ssh, readset, writeset); /* Buffer input from the connection. */ - client_process_net_input(readset); + client_process_net_input(ssh, readset); if (quit_pending) break; - if (!compat20) { - /* Buffer data from stdin */ - client_process_input(readset); - /* - * Process output to stdout and stderr. Output to - * the connection is processed elsewhere (above). - */ - client_process_output(writeset); - } - - if (session_resumed) { - connection_in = packet_get_connection_in(); - connection_out = packet_get_connection_out(); - max_fd = MAX(max_fd, connection_out); - max_fd = MAX(max_fd, connection_in); - session_resumed = 0; - } - /* * Send as much buffered packet data as possible to the * sender. */ - if (FD_ISSET(connection_out, writeset)) - packet_write_poll(); + if (FD_ISSET(connection_out, writeset)) { + if ((r = ssh_packet_write_poll(ssh)) != 0) { + sshpkt_fatal(ssh, r, + "%s: ssh_packet_write_poll", __func__); + } + } /* * If we are a backgrounded control master, and the @@ -1655,18 +1399,17 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) /* Terminate the session. */ /* Stop watching for window change. */ - signal(SIGWINCH, SIG_DFL); - - if (compat20) { - packet_start(SSH2_MSG_DISCONNECT); - packet_put_int(SSH2_DISCONNECT_BY_APPLICATION); - packet_put_cstring("disconnected by user"); - packet_put_cstring(""); /* language tag */ - packet_send(); - packet_write_wait(); - } + ssh_signal(SIGWINCH, SIG_DFL); - channel_free_all(); + if ((r = sshpkt_start(ssh, SSH2_MSG_DISCONNECT)) != 0 || + (r = sshpkt_put_u32(ssh, SSH2_DISCONNECT_BY_APPLICATION)) != 0 || + (r = sshpkt_put_cstring(ssh, "disconnected by user")) != 0 || + (r = sshpkt_put_cstring(ssh, "")) != 0 || /* language tag */ + (r = sshpkt_send(ssh)) != 0 || + (r = ssh_packet_write_wait(ssh)) != 0) + fatal("%s: send disconnect: %s", __func__, ssh_err(r)); + + channel_free_all(ssh); if (have_pty) leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); @@ -1689,48 +1432,39 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) exit_status = 0; } - if (received_signal) - fatal("Killed by signal %d.", (int) received_signal); + if (received_signal) { + verbose("Killed by signal %d.", (int) received_signal); + cleanup_exit(0); + } /* * In interactive mode (with pseudo tty) display a message indicating * that the connection has been closed. */ if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) { - snprintf(buf, sizeof buf, - "Connection to %.64s closed.\r\n", host); - buffer_append(&stderr_buffer, buf, strlen(buf)); - } - - /* Output any buffered data for stdout. */ - if (buffer_len(&stdout_buffer) > 0) { - len = atomicio(vwrite, fileno(stdout), - buffer_ptr(&stdout_buffer), buffer_len(&stdout_buffer)); - if (len < 0 || (u_int)len != buffer_len(&stdout_buffer)) - error("Write failed flushing stdout buffer."); - else - buffer_consume(&stdout_buffer, len); + if ((r = sshbuf_putf(stderr_buffer, + "Connection to %.64s closed.\r\n", host)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); } /* Output any buffered data for stderr. */ - if (buffer_len(&stderr_buffer) > 0) { + if (sshbuf_len(stderr_buffer) > 0) { len = atomicio(vwrite, fileno(stderr), - buffer_ptr(&stderr_buffer), buffer_len(&stderr_buffer)); - if (len < 0 || (u_int)len != buffer_len(&stderr_buffer)) + (u_char *)sshbuf_ptr(stderr_buffer), + sshbuf_len(stderr_buffer)); + if (len < 0 || (u_int)len != sshbuf_len(stderr_buffer)) error("Write failed flushing stderr buffer."); - else - buffer_consume(&stderr_buffer, len); + else if ((r = sshbuf_consume(stderr_buffer, len)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); } /* Clear and free any buffers. */ - memset(buf, 0, sizeof(buf)); - buffer_free(&stdin_buffer); - buffer_free(&stdout_buffer); - buffer_free(&stderr_buffer); + explicit_bzero(buf, sizeof(buf)); + sshbuf_free(stderr_buffer); /* Report bytes transferred, and transfer rates. */ - total_time = get_current_time() - start_time; - packet_get_bytes(&ibytes, &obytes); + total_time = monotime_double() - start_time; + ssh_packet_get_bytes(ssh, &ibytes, &obytes); verbose("Transferred: sent %llu, received %llu bytes, in %.1f seconds", (unsigned long long)obytes, (unsigned long long)ibytes, total_time); if (total_time > 0) @@ -1743,145 +1477,96 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) /*********/ -static int -client_input_stdout_data(int type, u_int32_t seq, void *ctxt) -{ - u_int data_len; - char *data = packet_get_string(&data_len); - packet_check_eom(); - buffer_append(&stdout_buffer, data, data_len); - explicit_bzero(data, data_len); - free(data); - return 0; -} -static int -client_input_stderr_data(int type, u_int32_t seq, void *ctxt) -{ - u_int data_len; - char *data = packet_get_string(&data_len); - packet_check_eom(); - buffer_append(&stderr_buffer, data, data_len); - explicit_bzero(data, data_len); - free(data); - return 0; -} -static int -client_input_exit_status(int type, u_int32_t seq, void *ctxt) -{ - exit_status = packet_get_int(); - packet_check_eom(); - /* Acknowledge the exit. */ - packet_start(SSH_CMSG_EXIT_CONFIRMATION); - packet_send(); - /* - * Must wait for packet to be sent since we are - * exiting the loop. - */ - packet_write_wait(); - /* Flag that we want to exit. */ - quit_pending = 1; - return 0; -} - -static int -client_input_agent_open(int type, u_int32_t seq, void *ctxt) -{ - Channel *c = NULL; - int r, remote_id, sock; - - /* Read the remote channel number from the message. */ - remote_id = packet_get_int(); - packet_check_eom(); - - /* - * Get a connection to the local authentication agent (this may again - * get forwarded). - */ - if ((r = ssh_get_authentication_socket(&sock)) != 0 && - r != SSH_ERR_AGENT_NOT_PRESENT) - debug("%s: ssh_get_authentication_socket: %s", - __func__, ssh_err(r)); - - - /* - * If we could not connect the agent, send an error message back to - * the server. This should never happen unless the agent dies, - * because authentication forwarding is only enabled if we have an - * agent. - */ - if (sock >= 0) { - c = channel_new("", SSH_CHANNEL_OPEN, sock, sock, - -1, 0, 0, 0, "authentication agent connection", 1); - c->remote_id = remote_id; - c->force_drain = 1; - } - if (c == NULL) { - packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); - packet_put_int(remote_id); - } else { - /* Send a confirmation to the remote host. */ - debug("Forwarding authentication connection."); - packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); - packet_put_int(remote_id); - packet_put_int(c->self); - } - packet_send(); - return 0; -} - static Channel * -client_request_forwarded_tcpip(const char *request_type, int rchan) +client_request_forwarded_tcpip(struct ssh *ssh, const char *request_type, + int rchan, u_int rwindow, u_int rmaxpack) { Channel *c = NULL; + struct sshbuf *b = NULL; char *listen_address, *originator_address; - u_short listen_port, originator_port; + u_int listen_port, originator_port; + int r; /* Get rest of the packet */ - listen_address = packet_get_string(NULL); - listen_port = packet_get_int(); - originator_address = packet_get_string(NULL); - originator_port = packet_get_int(); - packet_check_eom(); + if ((r = sshpkt_get_cstring(ssh, &listen_address, NULL)) != 0 || + (r = sshpkt_get_u32(ssh, &listen_port)) != 0 || + (r = sshpkt_get_cstring(ssh, &originator_address, NULL)) != 0 || + (r = sshpkt_get_u32(ssh, &originator_port)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + fatal("%s: parse packet: %s", __func__, ssh_err(r)); debug("%s: listen %s port %d, originator %s port %d", __func__, listen_address, listen_port, originator_address, originator_port); - c = channel_connect_by_listen_address(listen_address, listen_port, - "forwarded-tcpip", originator_address); + if (listen_port > 0xffff) + error("%s: invalid listen port", __func__); + else if (originator_port > 0xffff) + error("%s: invalid originator port", __func__); + else { + c = channel_connect_by_listen_address(ssh, + listen_address, listen_port, "forwarded-tcpip", + originator_address); + } + + if (c != NULL && c->type == SSH_CHANNEL_MUX_CLIENT) { + if ((b = sshbuf_new()) == NULL) { + error("%s: alloc reply", __func__); + goto out; + } + /* reconstruct and send to muxclient */ + if ((r = sshbuf_put_u8(b, 0)) != 0 || /* padlen */ + (r = sshbuf_put_u8(b, SSH2_MSG_CHANNEL_OPEN)) != 0 || + (r = sshbuf_put_cstring(b, request_type)) != 0 || + (r = sshbuf_put_u32(b, rchan)) != 0 || + (r = sshbuf_put_u32(b, rwindow)) != 0 || + (r = sshbuf_put_u32(b, rmaxpack)) != 0 || + (r = sshbuf_put_cstring(b, listen_address)) != 0 || + (r = sshbuf_put_u32(b, listen_port)) != 0 || + (r = sshbuf_put_cstring(b, originator_address)) != 0 || + (r = sshbuf_put_u32(b, originator_port)) != 0 || + (r = sshbuf_put_stringb(c->output, b)) != 0) { + error("%s: compose for muxclient %s", __func__, + ssh_err(r)); + goto out; + } + } + out: + sshbuf_free(b); free(originator_address); free(listen_address); return c; } static Channel * -client_request_forwarded_streamlocal(const char *request_type, int rchan) +client_request_forwarded_streamlocal(struct ssh *ssh, + const char *request_type, int rchan) { Channel *c = NULL; char *listen_path; + int r; /* Get the remote path. */ - listen_path = packet_get_string(NULL); - /* XXX: Skip reserved field for now. */ - if (packet_get_string_ptr(NULL) == NULL) - fatal("%s: packet_get_string_ptr failed", __func__); - packet_check_eom(); + if ((r = sshpkt_get_cstring(ssh, &listen_path, NULL)) != 0 || + (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* reserved */ + (r = sshpkt_get_end(ssh)) != 0) + fatal("%s: parse packet: %s", __func__, ssh_err(r)); - debug("%s: %s", __func__, listen_path); + debug("%s: request: %s", __func__, listen_path); - c = channel_connect_by_listen_path(listen_path, + c = channel_connect_by_listen_path(ssh, listen_path, "forwarded-streamlocal@openssh.com", "forwarded-streamlocal"); free(listen_path); return c; } static Channel * -client_request_x11(const char *request_type, int rchan) +client_request_x11(struct ssh *ssh, const char *request_type, int rchan) { Channel *c = NULL; char *originator; - u_short originator_port; - int sock; + u_int originator_port; + int r, sock; if (!options.forward_x11) { error("Warning: ssh server tried X11 forwarding."); @@ -1889,27 +1574,24 @@ client_request_x11(const char *request_type, int rchan) "malicious server."); return NULL; } - if (x11_refuse_time != 0 && monotime() >= x11_refuse_time) { + if (x11_refuse_time != 0 && (u_int)monotime() >= x11_refuse_time) { verbose("Rejected X11 connection after ForwardX11Timeout " "expired"); return NULL; } - originator = packet_get_string(NULL); - if (datafellows & SSH_BUG_X11FWD) { - debug2("buggy server: x11 request w/o originator_port"); - originator_port = 0; - } else { - originator_port = packet_get_int(); - } - packet_check_eom(); + if ((r = sshpkt_get_cstring(ssh, &originator, NULL)) != 0 || + (r = sshpkt_get_u32(ssh, &originator_port)) != 0 || + (r = sshpkt_get_end(ssh)) != 0) + fatal("%s: parse packet: %s", __func__, ssh_err(r)); /* XXX check permission */ - debug("client_request_x11: request from %s %d", originator, + /* XXX range check originator port? */ + debug("client_request_x11: request from %s %u", originator, originator_port); free(originator); - sock = x11_connect_display(); + sock = x11_connect_display(ssh); if (sock < 0) return NULL; - c = channel_new("x11", + c = channel_new(ssh, "x11", SSH_CHANNEL_X11_OPEN, sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "x11", 1); c->force_drain = 1; @@ -1917,7 +1599,7 @@ client_request_x11(const char *request_type, int rchan) } static Channel * -client_request_agent(const char *request_type, int rchan) +client_request_agent(struct ssh *ssh, const char *request_type, int rchan) { Channel *c = NULL; int r, sock; @@ -1928,13 +1610,18 @@ client_request_agent(const char *request_type, int rchan) "malicious server."); return NULL; } - if ((r = ssh_get_authentication_socket(&sock)) != 0) { + if (forward_agent_sock_path == NULL) { + r = ssh_get_authentication_socket(&sock); + } else { + r = ssh_get_authentication_socket_path(forward_agent_sock_path, &sock); + } + if (r != 0) { if (r != SSH_ERR_AGENT_NOT_PRESENT) debug("%s: ssh_get_authentication_socket: %s", __func__, ssh_err(r)); return NULL; } - c = channel_new("authentication agent connection", + c = channel_new(ssh, "authentication agent connection", SSH_CHANNEL_OPEN, sock, sock, -1, CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "authentication agent connection", 1); @@ -1942,133 +1629,151 @@ client_request_agent(const char *request_type, int rchan) return c; } -int -client_request_tun_fwd(int tun_mode, int local_tun, int remote_tun) +char * +client_request_tun_fwd(struct ssh *ssh, int tun_mode, + int local_tun, int remote_tun, channel_open_fn *cb, void *cbctx) { Channel *c; - int fd; + int r, fd; + char *ifname = NULL; if (tun_mode == SSH_TUNMODE_NO) return 0; - if (!compat20) { - error("Tunnel forwarding is not supported for protocol 1"); - return -1; - } - debug("Requesting tun unit %d in mode %d", local_tun, tun_mode); /* Open local tunnel device */ - if ((fd = tun_open(local_tun, tun_mode)) == -1) { + if ((fd = tun_open(local_tun, tun_mode, &ifname)) == -1) { error("Tunnel device open failed."); - return -1; + return NULL; } + debug("Tunnel forwarding using interface %s", ifname); - c = channel_new("tun", SSH_CHANNEL_OPENING, fd, fd, -1, + c = channel_new(ssh, "tun", SSH_CHANNEL_OPENING, fd, fd, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1); c->datagram = 1; #if defined(SSH_TUN_FILTER) if (options.tun_open == SSH_TUNMODE_POINTOPOINT) - channel_register_filter(c->self, sys_tun_infilter, + channel_register_filter(ssh, c->self, sys_tun_infilter, sys_tun_outfilter, NULL, NULL); #endif - packet_start(SSH2_MSG_CHANNEL_OPEN); - packet_put_cstring("tun@openssh.com"); - packet_put_int(c->self); - packet_put_int(c->local_window_max); - packet_put_int(c->local_maxpacket); - packet_put_int(tun_mode); - packet_put_int(remote_tun); - packet_send(); + if (cb != NULL) + channel_register_open_confirm(ssh, c->self, cb, cbctx); - return 0; + if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN)) != 0 || + (r = sshpkt_put_cstring(ssh, "tun@openssh.com")) != 0 || + (r = sshpkt_put_u32(ssh, c->self)) != 0 || + (r = sshpkt_put_u32(ssh, c->local_window_max)) != 0 || + (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0 || + (r = sshpkt_put_u32(ssh, tun_mode)) != 0 || + (r = sshpkt_put_u32(ssh, remote_tun)) != 0 || + (r = sshpkt_send(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: send reply", __func__); + + return ifname; } /* XXXX move to generic input handler */ static int -client_input_channel_open(int type, u_int32_t seq, void *ctxt) +client_input_channel_open(int type, u_int32_t seq, struct ssh *ssh) { Channel *c = NULL; - char *ctype; - int rchan; - u_int rmaxpack, rwindow, len; - - ctype = packet_get_string(&len); - rchan = packet_get_int(); - rwindow = packet_get_int(); - rmaxpack = packet_get_int(); + char *ctype = NULL; + int r; + u_int rchan; + size_t len; + u_int rmaxpack, rwindow; + + if ((r = sshpkt_get_cstring(ssh, &ctype, &len)) != 0 || + (r = sshpkt_get_u32(ssh, &rchan)) != 0 || + (r = sshpkt_get_u32(ssh, &rwindow)) != 0 || + (r = sshpkt_get_u32(ssh, &rmaxpack)) != 0) + goto out; debug("client_input_channel_open: ctype %s rchan %d win %d max %d", ctype, rchan, rwindow, rmaxpack); if (strcmp(ctype, "forwarded-tcpip") == 0) { - c = client_request_forwarded_tcpip(ctype, rchan); + c = client_request_forwarded_tcpip(ssh, ctype, rchan, rwindow, + rmaxpack); } else if (strcmp(ctype, "forwarded-streamlocal@openssh.com") == 0) { - c = client_request_forwarded_streamlocal(ctype, rchan); + c = client_request_forwarded_streamlocal(ssh, ctype, rchan); } else if (strcmp(ctype, "x11") == 0) { - c = client_request_x11(ctype, rchan); + c = client_request_x11(ssh, ctype, rchan); } else if (strcmp(ctype, "auth-agent@openssh.com") == 0) { - c = client_request_agent(ctype, rchan); + c = client_request_agent(ssh, ctype, rchan); } -/* XXX duplicate : */ - if (c != NULL) { + if (c != NULL && c->type == SSH_CHANNEL_MUX_CLIENT) { + debug3("proxied to downstream: %s", ctype); + } else if (c != NULL) { debug("confirm %s", ctype); c->remote_id = rchan; + c->have_remote_id = 1; c->remote_window = rwindow; c->remote_maxpacket = rmaxpack; if (c->type != SSH_CHANNEL_CONNECTING) { - packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); - packet_put_int(c->remote_id); - packet_put_int(c->self); - packet_put_int(c->local_window); - packet_put_int(c->local_maxpacket); - packet_send(); + if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 || + (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || + (r = sshpkt_put_u32(ssh, c->self)) != 0 || + (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || + (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0 || + (r = sshpkt_send(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: send reply", __func__); } } else { debug("failure %s", ctype); - packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); - packet_put_int(rchan); - packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED); - if (!(datafellows & SSH_BUG_OPENFAILURE)) { - packet_put_cstring("open failed"); - packet_put_cstring(""); - } - packet_send(); - } + if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_FAILURE)) != 0 || + (r = sshpkt_put_u32(ssh, rchan)) != 0 || + (r = sshpkt_put_u32(ssh, SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED)) != 0 || + (r = sshpkt_put_cstring(ssh, "open failed")) != 0 || + (r = sshpkt_put_cstring(ssh, "")) != 0 || + (r = sshpkt_send(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: send failure", __func__); + } + r = 0; + out: free(ctype); - return 0; + return r; } static int -client_input_channel_req(int type, u_int32_t seq, void *ctxt) +client_input_channel_req(int type, u_int32_t seq, struct ssh *ssh) { Channel *c = NULL; - int exitval, id, reply, success = 0; - char *rtype; - - id = packet_get_int(); - rtype = packet_get_string(NULL); - reply = packet_get_char(); + char *rtype = NULL; + u_char reply; + u_int id, exitval; + int r, success = 0; + + if ((r = sshpkt_get_u32(ssh, &id)) != 0) + return r; + if (id <= INT_MAX) + c = channel_lookup(ssh, id); + if (channel_proxy_upstream(c, type, seq, ssh)) + return 0; + if ((r = sshpkt_get_cstring(ssh, &rtype, NULL)) != 0 || + (r = sshpkt_get_u8(ssh, &reply)) != 0) + goto out; - debug("client_input_channel_req: channel %d rtype %s reply %d", + debug("client_input_channel_req: channel %u rtype %s reply %d", id, rtype, reply); - if (id == -1) { - error("client_input_channel_req: request for channel -1"); - } else if ((c = channel_lookup(id)) == NULL) { + if (c == NULL) { error("client_input_channel_req: channel %d: " "unknown channel", id); } else if (strcmp(rtype, "eow@openssh.com") == 0) { - packet_check_eom(); - chan_rcvd_eow(c); + if ((r = sshpkt_get_end(ssh)) != 0) + goto out; + chan_rcvd_eow(ssh, c); } else if (strcmp(rtype, "exit-status") == 0) { - exitval = packet_get_int(); + if ((r = sshpkt_get_u32(ssh, &exitval)) != 0) + goto out; if (c->ctl_chan != -1) { - mux_exit_message(c, exitval); + mux_exit_message(ssh, c, exitval); success = 1; - } else if (id == session_ident) { + } else if ((int)id == session_ident) { /* Record exit value of local session */ success = 1; exit_status = exitval; @@ -2077,16 +1782,23 @@ client_input_channel_req(int type, u_int32_t seq, void *ctxt) debug("%s: no sink for exit-status on channel %d", __func__, id); } - packet_check_eom(); + if ((r = sshpkt_get_end(ssh)) != 0) + goto out; } if (reply && c != NULL && !(c->flags & CHAN_CLOSE_SENT)) { - packet_start(success ? - SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); - packet_put_int(c->remote_id); - packet_send(); - } + if (!c->have_remote_id) + fatal("%s: channel %d: no remote_id", + __func__, c->self); + if ((r = sshpkt_start(ssh, success ? + SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE)) != 0 || + (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || + (r = sshpkt_send(ssh)) != 0) + sshpkt_fatal(ssh, r, "%s: send failure", __func__); + } + r = 0; + out: free(rtype); - return 0; + return r; } struct hostkeys_update_ctx { @@ -2101,9 +1813,7 @@ struct hostkeys_update_ctx { */ struct sshkey **keys; int *keys_seen; - size_t nkeys; - - size_t nnew; + size_t nkeys, nnew; /* * Keys that are in known_hosts, but were not present in the update @@ -2140,8 +1850,7 @@ hostkeys_find(struct hostkey_foreach_line *l, void *_ctx) size_t i; struct sshkey **tmp; - if (l->status != HKF_STATUS_MATCHED || l->key == NULL || - l->key->type == KEY_RSA1) + if (l->status != HKF_STATUS_MATCHED || l->key == NULL) return 0; /* Mark off keys we've already seen for this host */ @@ -2156,9 +1865,9 @@ hostkeys_find(struct hostkey_foreach_line *l, void *_ctx) /* This line contained a key that not offered by the server */ debug3("%s: deprecated %s key at %s:%ld", __func__, sshkey_ssh_name(l->key), l->path, l->linenum); - if ((tmp = reallocarray(ctx->old_keys, ctx->nold + 1, + if ((tmp = recallocarray(ctx->old_keys, ctx->nold, ctx->nold + 1, sizeof(*ctx->old_keys))) == NULL) - fatal("%s: reallocarray failed nold = %zu", + fatal("%s: recallocarray failed nold = %zu", __func__, ctx->nold); ctx->old_keys = tmp; ctx->old_keys[ctx->nold++] = l->key; @@ -2168,13 +1877,22 @@ hostkeys_find(struct hostkey_foreach_line *l, void *_ctx) } static void +hostkey_change_preamble(LogLevel loglevel) +{ + do_log2(loglevel, "The server has updated its host keys."); + do_log2(loglevel, "These changes were verified by the server's " + "existing trusted key."); +} + +static void update_known_hosts(struct hostkeys_update_ctx *ctx) { - int r, was_raw = 0; - int loglevel = options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK ? - SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_VERBOSE; + int r, was_raw = 0, first = 1; + int asking = options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK; + LogLevel loglevel = asking ? SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_VERBOSE; char *fp, *response; size_t i; + struct stat sb; for (i = 0; i < ctx->nkeys; i++) { if (ctx->keys_seen[i] != 2) @@ -2182,16 +1900,22 @@ update_known_hosts(struct hostkeys_update_ctx *ctx) if ((fp = sshkey_fingerprint(ctx->keys[i], options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) fatal("%s: sshkey_fingerprint failed", __func__); + if (first && asking) + hostkey_change_preamble(loglevel); do_log2(loglevel, "Learned new hostkey: %s %s", sshkey_type(ctx->keys[i]), fp); + first = 0; free(fp); } for (i = 0; i < ctx->nold; i++) { if ((fp = sshkey_fingerprint(ctx->old_keys[i], options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) fatal("%s: sshkey_fingerprint failed", __func__); + if (first && asking) + hostkey_change_preamble(loglevel); do_log2(loglevel, "Deprecating obsolete hostkey: %s %s", sshkey_type(ctx->old_keys[i]), fp); + first = 0; free(fp); } if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK) { @@ -2221,29 +1945,47 @@ update_known_hosts(struct hostkeys_update_ctx *ctx) if (was_raw) enter_raw_mode(1); } - + if (options.update_hostkeys == 0) + return; /* * Now that all the keys are verified, we can go ahead and replace * them in known_hosts (assuming SSH_UPDATE_HOSTKEYS_ASK didn't * cancel the operation). */ - if (options.update_hostkeys != 0 && - (r = hostfile_replace_entries(options.user_hostfiles[0], - ctx->host_str, ctx->ip_str, ctx->keys, ctx->nkeys, - options.hash_known_hosts, 0, - options.fingerprint_hash)) != 0) - error("%s: hostfile_replace_entries failed: %s", - __func__, ssh_err(r)); + for (i = 0; i < options.num_user_hostfiles; i++) { + /* + * NB. keys are only added to hostfiles[0], for the rest we + * just delete the hostname entries. + */ + if (stat(options.user_hostfiles[i], &sb) != 0) { + if (errno == ENOENT) { + debug("%s: known hosts file %s does not exist", + __func__, strerror(errno)); + } else { + error("%s: known hosts file %s inaccessible", + __func__, strerror(errno)); + } + continue; + } + if ((r = hostfile_replace_entries(options.user_hostfiles[i], + ctx->host_str, ctx->ip_str, + i == 0 ? ctx->keys : NULL, i == 0 ? ctx->nkeys : 0, + options.hash_known_hosts, 0, + options.fingerprint_hash)) != 0) { + error("%s: hostfile_replace_entries failed for %s: %s", + __func__, options.user_hostfiles[i], ssh_err(r)); + } + } } static void -client_global_hostkeys_private_confirm(int type, u_int32_t seq, void *_ctx) +client_global_hostkeys_private_confirm(struct ssh *ssh, int type, + u_int32_t seq, void *_ctx) { - struct ssh *ssh = active_state; /* XXX */ struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; size_t i, ndone; struct sshbuf *signdata; - int r; + int r, kexsigtype, use_kexsigtype; const u_char *sig; size_t siglen; @@ -2255,6 +1997,9 @@ client_global_hostkeys_private_confirm(int type, u_int32_t seq, void *_ctx) hostkeys_update_ctx_free(ctx); return; } + kexsigtype = sshkey_type_plain( + sshkey_type_from_name(ssh->kex->hostkey_alg)); + if ((signdata = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); /* Don't want to accidentally accept an unbound signature */ @@ -2283,8 +2028,16 @@ client_global_hostkeys_private_confirm(int type, u_int32_t seq, void *_ctx) __func__, ssh_err(r)); goto out; } + /* + * For RSA keys, prefer to use the signature type negotiated + * during KEX to the default (SHA1). + */ + use_kexsigtype = kexsigtype == KEY_RSA && + sshkey_type_plain(ctx->keys[i]->type) == KEY_RSA; if ((r = sshkey_verify(ctx->keys[i], sig, siglen, - sshbuf_ptr(signdata), sshbuf_len(signdata), 0)) != 0) { + sshbuf_ptr(signdata), sshbuf_len(signdata), + use_kexsigtype ? ssh->kex->hostkey_alg : NULL, 0, + NULL)) != 0) { error("%s: server gave bad signature for %s key %zu", __func__, sshkey_type(ctx->keys[i]), i); goto out; @@ -2296,7 +2049,10 @@ client_global_hostkeys_private_confirm(int type, u_int32_t seq, void *_ctx) if (ndone != ctx->nnew) fatal("%s: ndone != ctx->nnew (%zu / %zu)", __func__, ndone, ctx->nnew); /* Shouldn't happen */ - ssh_packet_check_eom(ssh); + if ((r = sshpkt_get_end(ssh)) != 0) { + error("%s: protocol error", __func__); + goto out; + } /* Make the edits to known_hosts */ update_known_hosts(ctx); @@ -2305,14 +2061,32 @@ client_global_hostkeys_private_confirm(int type, u_int32_t seq, void *_ctx) } /* + * Returns non-zero if the key is accepted by HostkeyAlgorithms. + * Made slightly less trivial by the multiple RSA signature algorithm names. + */ +static int +key_accepted_by_hostkeyalgs(const struct sshkey *key) +{ + const char *ktype = sshkey_ssh_name(key); + const char *hostkeyalgs = options.hostkeyalgorithms; + + if (key == NULL || key->type == KEY_UNSPEC) + return 0; + if (key->type == KEY_RSA && + (match_pattern_list("rsa-sha2-256", hostkeyalgs, 0) == 1 || + match_pattern_list("rsa-sha2-512", hostkeyalgs, 0) == 1)) + return 1; + return match_pattern_list(ktype, hostkeyalgs, 0) == 1; +} + +/* * Handle hostkeys-00@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) +client_input_hostkeys(struct ssh *ssh) { - struct ssh *ssh = active_state; /* XXX */ const u_char *blob = NULL; size_t i, len = 0; struct sshbuf *buf = NULL; @@ -2341,19 +2115,18 @@ client_input_hostkeys(void) goto out; } if ((r = sshkey_from_blob(blob, len, &key)) != 0) { - error("%s: parse key: %s", __func__, ssh_err(r)); - goto out; + do_log2(r == SSH_ERR_KEY_TYPE_UNKNOWN ? + SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_ERROR, + "%s: parse key: %s", __func__, ssh_err(r)); + continue; } 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) { + + if (!key_accepted_by_hostkeyalgs(key)) { debug3("%s: %s key not permitted by HostkeyAlgorithms", __func__, sshkey_ssh_name(key)); continue; @@ -2373,9 +2146,9 @@ client_input_hostkeys(void) } } /* Key is good, record it */ - if ((tmp = reallocarray(ctx->keys, ctx->nkeys + 1, + if ((tmp = recallocarray(ctx->keys, ctx->nkeys, ctx->nkeys + 1, sizeof(*ctx->keys))) == NULL) - fatal("%s: reallocarray failed nkeys = %zu", + fatal("%s: recallocarray failed nkeys = %zu", __func__, ctx->nkeys); ctx->keys = tmp; ctx->keys[ctx->nkeys++] = key; @@ -2397,11 +2170,22 @@ client_input_hostkeys(void) options.check_host_ip ? &ctx->ip_str : NULL); /* Find which keys we already know about. */ - if ((r = hostkeys_foreach(options.user_hostfiles[0], hostkeys_find, - ctx, ctx->host_str, ctx->ip_str, - HKF_WANT_PARSE_KEY|HKF_WANT_MATCH)) != 0) { - error("%s: hostkeys_foreach failed: %s", __func__, ssh_err(r)); - goto out; + for (i = 0; i < options.num_user_hostfiles; i++) { + debug("%s: searching %s for %s / %s", __func__, + options.user_hostfiles[i], ctx->host_str, + ctx->ip_str ? ctx->ip_str : "(none)"); + if ((r = hostkeys_foreach(options.user_hostfiles[i], + hostkeys_find, ctx, ctx->host_str, ctx->ip_str, + HKF_WANT_PARSE_KEY|HKF_WANT_MATCH)) != 0) { + if (r == SSH_ERR_SYSTEM_ERROR && errno == ENOENT) { + debug("%s: hostkeys file %s does not exist", + __func__, options.user_hostfiles[i]); + continue; + } + error("%s: hostkeys_foreach failed for %s: %s", + __func__, options.user_hostfiles[i], ssh_err(r)); + goto out; + } } /* Figure out if we have any new keys to add */ @@ -2463,70 +2247,76 @@ client_input_hostkeys(void) } static int -client_input_global_request(int type, u_int32_t seq, void *ctxt) +client_input_global_request(int type, u_int32_t seq, struct ssh *ssh) { char *rtype; - int want_reply; - int success = 0; + u_char want_reply; + int r, success = 0; - rtype = packet_get_cstring(NULL); - want_reply = packet_get_char(); + if ((r = sshpkt_get_cstring(ssh, &rtype, NULL)) != 0 || + (r = sshpkt_get_u8(ssh, &want_reply)) != 0) + goto out; debug("client_input_global_request: rtype %s want_reply %d", rtype, want_reply); if (strcmp(rtype, "hostkeys-00@openssh.com") == 0) - success = client_input_hostkeys(); + success = client_input_hostkeys(ssh); if (want_reply) { - packet_start(success ? - SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE); - packet_send(); - packet_write_wait(); + if ((r = sshpkt_start(ssh, success ? SSH2_MSG_REQUEST_SUCCESS : + SSH2_MSG_REQUEST_FAILURE)) != 0 || + (r = sshpkt_send(ssh)) != 0 || + (r = ssh_packet_write_wait(ssh)) != 0) + goto out; } + r = 0; + out: free(rtype); - return 0; + return r; } void -client_session2_setup(int id, int want_tty, int want_subsystem, - const char *term, struct termios *tiop, int in_fd, Buffer *cmd, char **env) +client_session2_setup(struct ssh *ssh, int id, int want_tty, int want_subsystem, + const char *term, struct termios *tiop, int in_fd, struct sshbuf *cmd, + char **env) { - int len; + int i, j, matched, len, r; + char *name, *val; Channel *c = NULL; debug2("%s: id %d", __func__, id); - if ((c = channel_lookup(id)) == NULL) - fatal("client_session2_setup: channel %d: unknown channel", id); + if ((c = channel_lookup(ssh, id)) == NULL) + fatal("%s: channel %d: unknown channel", __func__, id); - packet_set_interactive(want_tty, + ssh_packet_set_interactive(ssh, want_tty, options.ip_qos_interactive, options.ip_qos_bulk); if (want_tty) { struct winsize ws; /* Store window size in the packet. */ - if (ioctl(in_fd, TIOCGWINSZ, &ws) < 0) + if (ioctl(in_fd, TIOCGWINSZ, &ws) == -1) memset(&ws, 0, sizeof(ws)); - channel_request_start(id, "pty-req", 1); - client_expect_confirm(id, "PTY allocation", CONFIRM_TTY); - packet_put_cstring(term != NULL ? term : ""); - packet_put_int((u_int)ws.ws_col); - packet_put_int((u_int)ws.ws_row); - packet_put_int((u_int)ws.ws_xpixel); - packet_put_int((u_int)ws.ws_ypixel); + channel_request_start(ssh, id, "pty-req", 1); + client_expect_confirm(ssh, id, "PTY allocation", CONFIRM_TTY); + if ((r = sshpkt_put_cstring(ssh, term != NULL ? term : "")) + != 0 || + (r = sshpkt_put_u32(ssh, (u_int)ws.ws_col)) != 0 || + (r = sshpkt_put_u32(ssh, (u_int)ws.ws_row)) != 0 || + (r = sshpkt_put_u32(ssh, (u_int)ws.ws_xpixel)) != 0 || + (r = sshpkt_put_u32(ssh, (u_int)ws.ws_ypixel)) != 0) + fatal("%s: build packet: %s", __func__, ssh_err(r)); if (tiop == NULL) tiop = get_saved_tio(); - tty_make_modes(-1, tiop); - packet_send(); + ssh_tty_make_modes(ssh, -1, tiop); + if ((r = sshpkt_send(ssh)) != 0) + fatal("%s: send packet: %s", __func__, ssh_err(r)); /* XXX wait for reply */ c->client_tty = 1; } /* Transfer any environment variables from client to server */ if (options.num_send_env != 0 && env != NULL) { - int i, j, matched; - char *name, *val; - debug("Sending environment."); for (i = 0; env[i] != NULL; i++) { /* Split */ @@ -2551,101 +2341,87 @@ client_session2_setup(int id, int want_tty, int want_subsystem, } debug("Sending env %s = %s", name, val); - channel_request_start(id, "env", 0); - packet_put_cstring(name); - packet_put_cstring(val); - packet_send(); + channel_request_start(ssh, id, "env", 0); + if ((r = sshpkt_put_cstring(ssh, name)) != 0 || + (r = sshpkt_put_cstring(ssh, val)) != 0 || + (r = sshpkt_send(ssh)) != 0) { + fatal("%s: send packet: %s", + __func__, ssh_err(r)); + } free(name); } } + for (i = 0; i < options.num_setenv; i++) { + /* Split */ + name = xstrdup(options.setenv[i]); + if ((val = strchr(name, '=')) == NULL) { + free(name); + continue; + } + *val++ = '\0'; - len = buffer_len(cmd); + debug("Setting env %s = %s", name, val); + channel_request_start(ssh, id, "env", 0); + if ((r = sshpkt_put_cstring(ssh, name)) != 0 || + (r = sshpkt_put_cstring(ssh, val)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal("%s: send packet: %s", __func__, ssh_err(r)); + free(name); + } + + len = sshbuf_len(cmd); if (len > 0) { if (len > 900) len = 900; if (want_subsystem) { debug("Sending subsystem: %.*s", - len, (u_char*)buffer_ptr(cmd)); - channel_request_start(id, "subsystem", 1); - client_expect_confirm(id, "subsystem", CONFIRM_CLOSE); + len, (const u_char*)sshbuf_ptr(cmd)); + channel_request_start(ssh, id, "subsystem", 1); + client_expect_confirm(ssh, id, "subsystem", + CONFIRM_CLOSE); } else { debug("Sending command: %.*s", - len, (u_char*)buffer_ptr(cmd)); - channel_request_start(id, "exec", 1); - client_expect_confirm(id, "exec", CONFIRM_CLOSE); + len, (const u_char*)sshbuf_ptr(cmd)); + channel_request_start(ssh, id, "exec", 1); + client_expect_confirm(ssh, id, "exec", CONFIRM_CLOSE); } - packet_put_string(buffer_ptr(cmd), buffer_len(cmd)); - packet_send(); + if ((r = sshpkt_put_stringb(ssh, cmd)) != 0 || + (r = sshpkt_send(ssh)) != 0) + fatal("%s: send command: %s", __func__, ssh_err(r)); } else { - channel_request_start(id, "shell", 1); - client_expect_confirm(id, "shell", CONFIRM_CLOSE); - packet_send(); + channel_request_start(ssh, id, "shell", 1); + client_expect_confirm(ssh, id, "shell", CONFIRM_CLOSE); + if ((r = sshpkt_send(ssh)) != 0) { + fatal("%s: send shell request: %s", + __func__, ssh_err(r)); + } } } static void -client_init_dispatch_20(void) +client_init_dispatch(struct ssh *ssh) { - dispatch_init(&dispatch_protocol_error); - - dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); - dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data); - dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); - dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); - dispatch_set(SSH2_MSG_CHANNEL_OPEN, &client_input_channel_open); - dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); - dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); - dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &client_input_channel_req); - dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); - dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, &channel_input_status_confirm); - dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &channel_input_status_confirm); - dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &client_input_global_request); + ssh_dispatch_init(ssh, &dispatch_protocol_error); + + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_DATA, &channel_input_data); + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_OPEN, &client_input_channel_open); + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_REQUEST, &client_input_channel_req); + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_SUCCESS, &channel_input_status_confirm); + ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_FAILURE, &channel_input_status_confirm); + ssh_dispatch_set(ssh, SSH2_MSG_GLOBAL_REQUEST, &client_input_global_request); /* rekeying */ - dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit); + ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit); /* global request reply messages */ - dispatch_set(SSH2_MSG_REQUEST_FAILURE, &client_global_request_reply); - dispatch_set(SSH2_MSG_REQUEST_SUCCESS, &client_global_request_reply); -} - -static void -client_init_dispatch_13(void) -{ - dispatch_init(NULL); - dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close); - dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation); - dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data); - dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); - dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); - dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open); - dispatch_set(SSH_SMSG_EXITSTATUS, &client_input_exit_status); - dispatch_set(SSH_SMSG_STDERR_DATA, &client_input_stderr_data); - dispatch_set(SSH_SMSG_STDOUT_DATA, &client_input_stdout_data); - - dispatch_set(SSH_SMSG_AGENT_OPEN, options.forward_agent ? - &client_input_agent_open : &deny_input_open); - dispatch_set(SSH_SMSG_X11_OPEN, options.forward_x11 ? - &x11_input_open : &deny_input_open); -} - -static void -client_init_dispatch_15(void) -{ - client_init_dispatch_13(); - dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof); - dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, & channel_input_oclose); -} - -static void -client_init_dispatch(void) -{ - if (compat20) - client_init_dispatch_20(); - else if (compat13) - client_init_dispatch_13(); - else - client_init_dispatch_15(); + ssh_dispatch_set(ssh, SSH2_MSG_REQUEST_FAILURE, &client_global_request_reply); + ssh_dispatch_set(ssh, SSH2_MSG_REQUEST_SUCCESS, &client_global_request_reply); } void @@ -2668,7 +2444,6 @@ void cleanup_exit(int i) { leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); - leave_non_blocking(); if (options.control_path != NULL && muxserver_sock != -1) unlink(options.control_path); ssh_kill_proxy_command(); |