clientloop.c revision 197679
1197679Sdes/* $OpenBSD: clientloop.c,v 1.213 2009/07/05 19:28:33 stevesk Exp $ */ 257429Smarkm/* 357429Smarkm * Author: Tatu Ylonen <ylo@cs.hut.fi> 457429Smarkm * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 557429Smarkm * All rights reserved 665668Skris * The main loop for the interactive session (client side). 760573Skris * 865668Skris * As far as I am concerned, the code I have written for this software 965668Skris * can be used freely for any purpose. Any derived versions of this 1065668Skris * software must be clearly marked as such, and if the derived work is 1165668Skris * incompatible with the protocol description in the RFC file, it must be 1265668Skris * called by a name other than "ssh" or "Secure Shell". 1360573Skris * 1460573Skris * 1565668Skris * Copyright (c) 1999 Theo de Raadt. All rights reserved. 1660573Skris * 1765668Skris * Redistribution and use in source and binary forms, with or without 1865668Skris * modification, are permitted provided that the following conditions 1965668Skris * are met: 2065668Skris * 1. Redistributions of source code must retain the above copyright 2165668Skris * notice, this list of conditions and the following disclaimer. 2265668Skris * 2. Redistributions in binary form must reproduce the above copyright 2365668Skris * notice, this list of conditions and the following disclaimer in the 2465668Skris * documentation and/or other materials provided with the distribution. 2565668Skris * 2665668Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2765668Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2865668Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2965668Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 3065668Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 3165668Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3265668Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3365668Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3465668Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3565668Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3665668Skris * 3765668Skris * 3860573Skris * SSH2 support added by Markus Friedl. 3992555Sdes * Copyright (c) 1999, 2000, 2001 Markus Friedl. All rights reserved. 4065668Skris * 4165668Skris * Redistribution and use in source and binary forms, with or without 4265668Skris * modification, are permitted provided that the following conditions 4365668Skris * are met: 4465668Skris * 1. Redistributions of source code must retain the above copyright 4565668Skris * notice, this list of conditions and the following disclaimer. 4665668Skris * 2. Redistributions in binary form must reproduce the above copyright 4765668Skris * notice, this list of conditions and the following disclaimer in the 4865668Skris * documentation and/or other materials provided with the distribution. 4965668Skris * 5065668Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 5165668Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 5265668Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 5365668Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 5465668Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 5565668Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 5665668Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 5765668Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 5865668Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 5965668Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 6057429Smarkm */ 6157429Smarkm 6257429Smarkm#include "includes.h" 6357429Smarkm 64162852Sdes#include <sys/types.h> 65162852Sdes#include <sys/ioctl.h> 66162852Sdes#include <sys/param.h> 67162852Sdes#ifdef HAVE_SYS_STAT_H 68162852Sdes# include <sys/stat.h> 69162852Sdes#endif 70162852Sdes#ifdef HAVE_SYS_TIME_H 71162852Sdes# include <sys/time.h> 72162852Sdes#endif 73162852Sdes#include <sys/socket.h> 74162852Sdes 75162852Sdes#include <ctype.h> 76162852Sdes#include <errno.h> 77162852Sdes#ifdef HAVE_PATHS_H 78162852Sdes#include <paths.h> 79162852Sdes#endif 80162852Sdes#include <signal.h> 81162852Sdes#include <stdarg.h> 82162852Sdes#include <stdio.h> 83162852Sdes#include <stdlib.h> 84162852Sdes#include <string.h> 85162852Sdes#include <termios.h> 86162852Sdes#include <pwd.h> 87162852Sdes#include <unistd.h> 88162852Sdes 89181111Sdes#include "openbsd-compat/sys-queue.h" 90162852Sdes#include "xmalloc.h" 9176259Sgreen#include "ssh.h" 9276259Sgreen#include "ssh1.h" 9376259Sgreen#include "ssh2.h" 9457429Smarkm#include "packet.h" 9557429Smarkm#include "buffer.h" 9660573Skris#include "compat.h" 9760573Skris#include "channels.h" 9860573Skris#include "dispatch.h" 9976259Sgreen#include "key.h" 100162852Sdes#include "cipher.h" 10176259Sgreen#include "kex.h" 10276259Sgreen#include "log.h" 10376259Sgreen#include "readconf.h" 10476259Sgreen#include "clientloop.h" 105157016Sdes#include "sshconnect.h" 10676259Sgreen#include "authfd.h" 10776259Sgreen#include "atomicio.h" 108137015Sdes#include "sshpty.h" 10976259Sgreen#include "misc.h" 110137015Sdes#include "match.h" 111137015Sdes#include "msg.h" 112197679Sdes#include "roaming.h" 11360573Skris 11469587Sgreen/* import options */ 11568700Sgreenextern Options options; 11668700Sgreen 11757429Smarkm/* Flag indicating that stdin should be redirected from /dev/null. */ 11857429Smarkmextern int stdin_null_flag; 11957429Smarkm 120126274Sdes/* Flag indicating that no shell has been requested */ 121126274Sdesextern int no_shell_flag; 122126274Sdes 123137015Sdes/* Control socket */ 124181111Sdesextern int muxserver_sock; 125137015Sdes 12657429Smarkm/* 12757429Smarkm * Name of the host we are connecting to. This is the name given on the 12857429Smarkm * command line, or the HostName specified for the user-supplied name in a 12957429Smarkm * configuration file. 13057429Smarkm */ 13157429Smarkmextern char *host; 13257429Smarkm 13357429Smarkm/* 13457429Smarkm * Flag to indicate that we have received a window change signal which has 13557429Smarkm * not yet been processed. This will cause a message indicating the new 13657429Smarkm * window size to be sent to the server a little later. This is volatile 13757429Smarkm * because this is updated in a signal handler. 13857429Smarkm */ 13992555Sdesstatic volatile sig_atomic_t received_window_change_signal = 0; 14092555Sdesstatic volatile sig_atomic_t received_signal = 0; 14157429Smarkm 142157016Sdes/* Flag indicating whether the user's terminal is in non-blocking mode. */ 14357429Smarkmstatic int in_non_blocking_mode = 0; 14457429Smarkm 14557429Smarkm/* Common data for the client loop code. */ 146162852Sdesstatic volatile sig_atomic_t quit_pending; /* Set non-zero to quit the loop. */ 147181111Sdesstatic int escape_char1; /* Escape character. (proto1 only) */ 148181111Sdesstatic int escape_pending1; /* Last character was an escape (proto1 only) */ 14957429Smarkmstatic int last_was_cr; /* Last character was a newline. */ 150181111Sdesstatic int exit_status; /* Used to store the command exit status. */ 151181111Sdesstatic int stdin_eof; /* EOF has been encountered on stderr. */ 15257429Smarkmstatic Buffer stdin_buffer; /* Buffer for stdin data. */ 15357429Smarkmstatic Buffer stdout_buffer; /* Buffer for stdout data. */ 15457429Smarkmstatic Buffer stderr_buffer; /* Buffer for stderr data. */ 15576259Sgreenstatic u_int buffer_high;/* Soft max buffer size. */ 15657429Smarkmstatic int connection_in; /* Connection to server (input). */ 15757429Smarkmstatic int connection_out; /* Connection to server (output). */ 15876259Sgreenstatic int need_rekeying; /* Set to non-zero if rekeying is requested. */ 15976259Sgreenstatic int session_closed = 0; /* In SSH2: login session closed. */ 16057429Smarkm 16192555Sdesstatic void client_init_dispatch(void); 16260573Skrisint session_ident = -1; 16360573Skris 164181111Sdes/* Track escape per proto2 channel */ 165181111Sdesstruct escape_filter_ctx { 166181111Sdes int escape_pending; 167181111Sdes int escape_char; 168137015Sdes}; 169137015Sdes 170181111Sdes/* Context for channel confirmation replies */ 171181111Sdesstruct channel_reply_ctx { 172181111Sdes const char *request_type; 173181111Sdes int id, do_close; 174181111Sdes}; 175181111Sdes 176181111Sdes/* Global request success/failure callbacks */ 177181111Sdesstruct global_confirm { 178181111Sdes TAILQ_ENTRY(global_confirm) entry; 179181111Sdes global_confirm_cb *cb; 180181111Sdes void *ctx; 181181111Sdes int ref_count; 182181111Sdes}; 183181111SdesTAILQ_HEAD(global_confirms, global_confirm); 184181111Sdesstatic struct global_confirms global_confirms = 185181111Sdes TAILQ_HEAD_INITIALIZER(global_confirms); 186181111Sdes 18776259Sgreen/*XXX*/ 18876259Sgreenextern Kex *xxx_kex; 18957429Smarkm 190137015Sdesvoid ssh_process_session2_setup(int, int, int, Buffer *); 191137015Sdes 19257429Smarkm/* Restores stdin to blocking mode. */ 19357429Smarkm 19492555Sdesstatic void 19576259Sgreenleave_non_blocking(void) 19657429Smarkm{ 19757429Smarkm if (in_non_blocking_mode) { 198137015Sdes unset_nonblock(fileno(stdin)); 19957429Smarkm in_non_blocking_mode = 0; 20057429Smarkm } 20157429Smarkm} 20257429Smarkm 20357429Smarkm/* Puts stdin terminal in non-blocking mode. */ 20457429Smarkm 20592555Sdesstatic void 20676259Sgreenenter_non_blocking(void) 20757429Smarkm{ 20857429Smarkm in_non_blocking_mode = 1; 209137015Sdes set_nonblock(fileno(stdin)); 21057429Smarkm} 21157429Smarkm 21257429Smarkm/* 21357429Smarkm * Signal handler for the window change signal (SIGWINCH). This just sets a 21457429Smarkm * flag indicating that the window has changed. 21557429Smarkm */ 216162852Sdes/*ARGSUSED */ 21792555Sdesstatic void 21857429Smarkmwindow_change_handler(int sig) 21957429Smarkm{ 22057429Smarkm received_window_change_signal = 1; 22157429Smarkm signal(SIGWINCH, window_change_handler); 22257429Smarkm} 22357429Smarkm 22457429Smarkm/* 22557429Smarkm * Signal handler for signals that cause the program to terminate. These 22657429Smarkm * signals must be trapped to restore terminal modes. 22757429Smarkm */ 228162852Sdes/*ARGSUSED */ 22992555Sdesstatic void 23057429Smarkmsignal_handler(int sig) 23157429Smarkm{ 23292555Sdes received_signal = sig; 23392555Sdes quit_pending = 1; 23457429Smarkm} 23557429Smarkm 23657429Smarkm/* 23757429Smarkm * Returns current time in seconds from Jan 1, 1970 with the maximum 23857429Smarkm * available resolution. 23957429Smarkm */ 24057429Smarkm 24192555Sdesstatic double 24276259Sgreenget_current_time(void) 24357429Smarkm{ 24457429Smarkm struct timeval tv; 24557429Smarkm gettimeofday(&tv, NULL); 24657429Smarkm return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0; 24757429Smarkm} 24857429Smarkm 249149749Sdes#define SSH_X11_PROTO "MIT-MAGIC-COOKIE-1" 250149749Sdesvoid 251149749Sdesclient_x11_get_proto(const char *display, const char *xauth_path, 252149749Sdes u_int trusted, char **_proto, char **_data) 253149749Sdes{ 254149749Sdes char cmd[1024]; 255149749Sdes char line[512]; 256149749Sdes char xdisplay[512]; 257149749Sdes static char proto[512], data[512]; 258149749Sdes FILE *f; 259149749Sdes int got_data = 0, generated = 0, do_unlink = 0, i; 260149749Sdes char *xauthdir, *xauthfile; 261149749Sdes struct stat st; 262149749Sdes 263149749Sdes xauthdir = xauthfile = NULL; 264149749Sdes *_proto = proto; 265149749Sdes *_data = data; 266149749Sdes proto[0] = data[0] = '\0'; 267149749Sdes 268149749Sdes if (xauth_path == NULL ||(stat(xauth_path, &st) == -1)) { 269149749Sdes debug("No xauth program."); 270149749Sdes } else { 271149749Sdes if (display == NULL) { 272149749Sdes debug("x11_get_proto: DISPLAY not set"); 273149749Sdes return; 274149749Sdes } 275149749Sdes /* 276149749Sdes * Handle FamilyLocal case where $DISPLAY does 277149749Sdes * not match an authorization entry. For this we 278149749Sdes * just try "xauth list unix:displaynum.screennum". 279149749Sdes * XXX: "localhost" match to determine FamilyLocal 280149749Sdes * is not perfect. 281149749Sdes */ 282149749Sdes if (strncmp(display, "localhost:", 10) == 0) { 283149749Sdes snprintf(xdisplay, sizeof(xdisplay), "unix:%s", 284149749Sdes display + 10); 285149749Sdes display = xdisplay; 286149749Sdes } 287149749Sdes if (trusted == 0) { 288149749Sdes xauthdir = xmalloc(MAXPATHLEN); 289149749Sdes xauthfile = xmalloc(MAXPATHLEN); 290149749Sdes strlcpy(xauthdir, "/tmp/ssh-XXXXXXXXXX", MAXPATHLEN); 291149749Sdes if (mkdtemp(xauthdir) != NULL) { 292149749Sdes do_unlink = 1; 293149749Sdes snprintf(xauthfile, MAXPATHLEN, "%s/xauthfile", 294149749Sdes xauthdir); 295149749Sdes snprintf(cmd, sizeof(cmd), 296149749Sdes "%s -f %s generate %s " SSH_X11_PROTO 297149749Sdes " untrusted timeout 1200 2>" _PATH_DEVNULL, 298149749Sdes xauth_path, xauthfile, display); 299149749Sdes debug2("x11_get_proto: %s", cmd); 300149749Sdes if (system(cmd) == 0) 301149749Sdes generated = 1; 302149749Sdes } 303149749Sdes } 304181111Sdes 305181111Sdes /* 306181111Sdes * When in untrusted mode, we read the cookie only if it was 307181111Sdes * successfully generated as an untrusted one in the step 308181111Sdes * above. 309181111Sdes */ 310181111Sdes if (trusted || generated) { 311181111Sdes snprintf(cmd, sizeof(cmd), 312181111Sdes "%s %s%s list %s 2>" _PATH_DEVNULL, 313181111Sdes xauth_path, 314181111Sdes generated ? "-f " : "" , 315181111Sdes generated ? xauthfile : "", 316181111Sdes display); 317181111Sdes debug2("x11_get_proto: %s", cmd); 318181111Sdes f = popen(cmd, "r"); 319181111Sdes if (f && fgets(line, sizeof(line), f) && 320181111Sdes sscanf(line, "%*s %511s %511s", proto, data) == 2) 321181111Sdes got_data = 1; 322181111Sdes if (f) 323181111Sdes pclose(f); 324181111Sdes } else 325181111Sdes error("Warning: untrusted X11 forwarding setup failed: " 326181111Sdes "xauth key data not generated"); 327149749Sdes } 328149749Sdes 329149749Sdes if (do_unlink) { 330149749Sdes unlink(xauthfile); 331149749Sdes rmdir(xauthdir); 332149749Sdes } 333149749Sdes if (xauthdir) 334149749Sdes xfree(xauthdir); 335149749Sdes if (xauthfile) 336149749Sdes xfree(xauthfile); 337149749Sdes 338149749Sdes /* 339149749Sdes * If we didn't get authentication data, just make up some 340149749Sdes * data. The forwarding code will check the validity of the 341149749Sdes * response anyway, and substitute this data. The X11 342149749Sdes * server, however, will ignore this fake data and use 343149749Sdes * whatever authentication mechanisms it was using otherwise 344149749Sdes * for the local connection. 345149749Sdes */ 346149749Sdes if (!got_data) { 347149749Sdes u_int32_t rnd = 0; 348149749Sdes 349149749Sdes logit("Warning: No xauth data; " 350149749Sdes "using fake authentication data for X11 forwarding."); 351149749Sdes strlcpy(proto, SSH_X11_PROTO, sizeof proto); 352149749Sdes for (i = 0; i < 16; i++) { 353149749Sdes if (i % 4 == 0) 354149749Sdes rnd = arc4random(); 355149749Sdes snprintf(data + 2 * i, sizeof data - 2 * i, "%02x", 356149749Sdes rnd & 0xff); 357149749Sdes rnd >>= 8; 358149749Sdes } 359149749Sdes } 360149749Sdes} 361149749Sdes 36257429Smarkm/* 36357429Smarkm * This is called when the interactive is entered. This checks if there is 36457429Smarkm * an EOF coming on stdin. We must check this explicitly, as select() does 36557429Smarkm * not appear to wake up when redirecting from /dev/null. 36657429Smarkm */ 36757429Smarkm 36892555Sdesstatic void 36976259Sgreenclient_check_initial_eof_on_stdin(void) 37057429Smarkm{ 37157429Smarkm int len; 37257429Smarkm char buf[1]; 37357429Smarkm 37457429Smarkm /* 37557429Smarkm * If standard input is to be "redirected from /dev/null", we simply 37657429Smarkm * mark that we have seen an EOF and send an EOF message to the 37757429Smarkm * server. Otherwise, we try to read a single character; it appears 37857429Smarkm * that for some files, such /dev/null, select() never wakes up for 37957429Smarkm * read for this descriptor, which means that we never get EOF. This 38057429Smarkm * way we will get the EOF if stdin comes from /dev/null or similar. 38157429Smarkm */ 38257429Smarkm if (stdin_null_flag) { 38357429Smarkm /* Fake EOF on stdin. */ 38457429Smarkm debug("Sending eof."); 38557429Smarkm stdin_eof = 1; 38657429Smarkm packet_start(SSH_CMSG_EOF); 38757429Smarkm packet_send(); 38857429Smarkm } else { 38957429Smarkm enter_non_blocking(); 39057429Smarkm 39157429Smarkm /* Check for immediate EOF on stdin. */ 39257429Smarkm len = read(fileno(stdin), buf, 1); 39357429Smarkm if (len == 0) { 394181111Sdes /* 395181111Sdes * EOF. Record that we have seen it and send 396181111Sdes * EOF to server. 397181111Sdes */ 39857429Smarkm debug("Sending eof."); 39957429Smarkm stdin_eof = 1; 40057429Smarkm packet_start(SSH_CMSG_EOF); 40157429Smarkm packet_send(); 40257429Smarkm } else if (len > 0) { 40357429Smarkm /* 40457429Smarkm * Got data. We must store the data in the buffer, 40557429Smarkm * and also process it as an escape character if 40657429Smarkm * appropriate. 40757429Smarkm */ 408181111Sdes if ((u_char) buf[0] == escape_char1) 409181111Sdes escape_pending1 = 1; 41076259Sgreen else 41157429Smarkm buffer_append(&stdin_buffer, buf, 1); 41257429Smarkm } 41357429Smarkm leave_non_blocking(); 41457429Smarkm } 41557429Smarkm} 41657429Smarkm 41757429Smarkm 41857429Smarkm/* 41957429Smarkm * Make packets from buffered stdin data, and buffer them for sending to the 42057429Smarkm * connection. 42157429Smarkm */ 42257429Smarkm 42392555Sdesstatic void 42476259Sgreenclient_make_packets_from_stdin_data(void) 42557429Smarkm{ 42676259Sgreen u_int len; 42757429Smarkm 42857429Smarkm /* Send buffered stdin data to the server. */ 42957429Smarkm while (buffer_len(&stdin_buffer) > 0 && 43092555Sdes packet_not_very_much_data_to_write()) { 43157429Smarkm len = buffer_len(&stdin_buffer); 43257429Smarkm /* Keep the packets at reasonable size. */ 43357429Smarkm if (len > packet_get_maxsize()) 43457429Smarkm len = packet_get_maxsize(); 43557429Smarkm packet_start(SSH_CMSG_STDIN_DATA); 43657429Smarkm packet_put_string(buffer_ptr(&stdin_buffer), len); 43757429Smarkm packet_send(); 43857429Smarkm buffer_consume(&stdin_buffer, len); 43957429Smarkm /* If we have a pending EOF, send it now. */ 44057429Smarkm if (stdin_eof && buffer_len(&stdin_buffer) == 0) { 44157429Smarkm packet_start(SSH_CMSG_EOF); 44257429Smarkm packet_send(); 44357429Smarkm } 44457429Smarkm } 44557429Smarkm} 44657429Smarkm 44757429Smarkm/* 44857429Smarkm * Checks if the client window has changed, and sends a packet about it to 44957429Smarkm * the server if so. The actual change is detected elsewhere (by a software 45057429Smarkm * interrupt on Unix); this just checks the flag and sends a message if 45157429Smarkm * appropriate. 45257429Smarkm */ 45357429Smarkm 45492555Sdesstatic void 45576259Sgreenclient_check_window_change(void) 45657429Smarkm{ 45760573Skris struct winsize ws; 45857429Smarkm 45960573Skris if (! received_window_change_signal) 46060573Skris return; 46160573Skris /** XXX race */ 46260573Skris received_window_change_signal = 0; 46357429Smarkm 46469587Sgreen debug2("client_check_window_change: changed"); 46560573Skris 46660573Skris if (compat20) { 467137015Sdes channel_send_window_changes(); 46860573Skris } else { 469137015Sdes if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0) 470137015Sdes return; 47160573Skris packet_start(SSH_CMSG_WINDOW_SIZE); 472162852Sdes packet_put_int((u_int)ws.ws_row); 473162852Sdes packet_put_int((u_int)ws.ws_col); 474162852Sdes packet_put_int((u_int)ws.ws_xpixel); 475162852Sdes packet_put_int((u_int)ws.ws_ypixel); 47660573Skris packet_send(); 47757429Smarkm } 47857429Smarkm} 47957429Smarkm 480126274Sdesstatic void 481126274Sdesclient_global_request_reply(int type, u_int32_t seq, void *ctxt) 482126274Sdes{ 483181111Sdes struct global_confirm *gc; 484181111Sdes 485181111Sdes if ((gc = TAILQ_FIRST(&global_confirms)) == NULL) 486181111Sdes return; 487181111Sdes if (gc->cb != NULL) 488181111Sdes gc->cb(type, seq, gc->ctx); 489181111Sdes if (--gc->ref_count <= 0) { 490181111Sdes TAILQ_REMOVE(&global_confirms, gc, entry); 491181111Sdes bzero(gc, sizeof(*gc)); 492181111Sdes xfree(gc); 493181111Sdes } 494181111Sdes 495197679Sdes packet_set_alive_timeouts(0); 496126274Sdes} 497126274Sdes 498126274Sdesstatic void 499126274Sdesserver_alive_check(void) 500126274Sdes{ 501197679Sdes if (packet_inc_alive_timeouts() > options.server_alive_count_max) { 502164146Sdes logit("Timeout, server not responding."); 503164146Sdes cleanup_exit(255); 504164146Sdes } 505126274Sdes packet_start(SSH2_MSG_GLOBAL_REQUEST); 506126274Sdes packet_put_cstring("keepalive@openssh.com"); 507126274Sdes packet_put_char(1); /* boolean: want reply */ 508126274Sdes packet_send(); 509181111Sdes /* Insert an empty placeholder to maintain ordering */ 510181111Sdes client_register_global_confirm(NULL, NULL); 511126274Sdes} 512126274Sdes 51357429Smarkm/* 51457429Smarkm * Waits until the client can do something (some data becomes available on 51557429Smarkm * one of the file descriptors). 51657429Smarkm */ 51792555Sdesstatic void 51876259Sgreenclient_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, 519137015Sdes int *maxfdp, u_int *nallocp, int rekeying) 52057429Smarkm{ 521126274Sdes struct timeval tv, *tvp; 522126274Sdes int ret; 523126274Sdes 52476259Sgreen /* Add any selections by the channel mechanism. */ 52592555Sdes channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, rekeying); 52657429Smarkm 52760573Skris if (!compat20) { 52860573Skris /* Read from the connection, unless our buffers are full. */ 52960573Skris if (buffer_len(&stdout_buffer) < buffer_high && 53060573Skris buffer_len(&stderr_buffer) < buffer_high && 53160573Skris channel_not_very_much_buffered_data()) 53276259Sgreen FD_SET(connection_in, *readsetp); 53360573Skris /* 53460573Skris * Read from stdin, unless we have seen EOF or have very much 53560573Skris * buffered data to send to the server. 53660573Skris */ 53760573Skris if (!stdin_eof && packet_not_very_much_data_to_write()) 53876259Sgreen FD_SET(fileno(stdin), *readsetp); 53960573Skris 54060573Skris /* Select stdout/stderr if have data in buffer. */ 54160573Skris if (buffer_len(&stdout_buffer) > 0) 54276259Sgreen FD_SET(fileno(stdout), *writesetp); 54360573Skris if (buffer_len(&stderr_buffer) > 0) 54476259Sgreen FD_SET(fileno(stderr), *writesetp); 54560573Skris } else { 54692555Sdes /* channel_prepare_select could have closed the last channel */ 54792555Sdes if (session_closed && !channel_still_open() && 54892555Sdes !packet_have_data_to_write()) { 54992555Sdes /* clear mask since we did not call select() */ 55092555Sdes memset(*readsetp, 0, *nallocp); 55192555Sdes memset(*writesetp, 0, *nallocp); 55292555Sdes return; 55392555Sdes } else { 55492555Sdes FD_SET(connection_in, *readsetp); 55592555Sdes } 55660573Skris } 55757429Smarkm 55857429Smarkm /* Select server connection if have data to write to the server. */ 55957429Smarkm if (packet_have_data_to_write()) 56076259Sgreen FD_SET(connection_out, *writesetp); 56157429Smarkm 562181111Sdes if (muxserver_sock != -1) 563181111Sdes FD_SET(muxserver_sock, *readsetp); 564137015Sdes 56557429Smarkm /* 56657429Smarkm * Wait for something to happen. This will suspend the process until 56757429Smarkm * some selected descriptor can be read, written, or has some other 568126274Sdes * event pending. 56957429Smarkm */ 57057429Smarkm 571126274Sdes if (options.server_alive_interval == 0 || !compat20) 572126274Sdes tvp = NULL; 573137015Sdes else { 574126274Sdes tv.tv_sec = options.server_alive_interval; 575126274Sdes tv.tv_usec = 0; 576126274Sdes tvp = &tv; 577126274Sdes } 578126274Sdes ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp); 579126274Sdes if (ret < 0) { 58057429Smarkm char buf[100]; 58176259Sgreen 58276259Sgreen /* 58376259Sgreen * We have to clear the select masks, because we return. 58476259Sgreen * We have to return, because the mainloop checks for the flags 58576259Sgreen * set by the signal handlers. 58676259Sgreen */ 58792555Sdes memset(*readsetp, 0, *nallocp); 58892555Sdes memset(*writesetp, 0, *nallocp); 58976259Sgreen 59057429Smarkm if (errno == EINTR) 59157429Smarkm return; 59257429Smarkm /* Note: we might still have data in the buffers. */ 59357429Smarkm snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno)); 59457429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 59557429Smarkm quit_pending = 1; 596126274Sdes } else if (ret == 0) 597126274Sdes server_alive_check(); 59857429Smarkm} 59957429Smarkm 60092555Sdesstatic void 60165668Skrisclient_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr) 60257429Smarkm{ 60357429Smarkm /* Flush stdout and stderr buffers. */ 60465668Skris if (buffer_len(bout) > 0) 605181111Sdes atomicio(vwrite, fileno(stdout), buffer_ptr(bout), 606181111Sdes buffer_len(bout)); 60765668Skris if (buffer_len(berr) > 0) 608181111Sdes atomicio(vwrite, fileno(stderr), buffer_ptr(berr), 609181111Sdes buffer_len(berr)); 61057429Smarkm 61157429Smarkm leave_raw_mode(); 61257429Smarkm 61357429Smarkm /* 61457429Smarkm * Free (and clear) the buffer to reduce the amount of data that gets 61557429Smarkm * written to swap. 61657429Smarkm */ 61765668Skris buffer_free(bin); 61865668Skris buffer_free(bout); 61965668Skris buffer_free(berr); 62057429Smarkm 62157429Smarkm /* Send the suspend signal to the program itself. */ 62257429Smarkm kill(getpid(), SIGTSTP); 62357429Smarkm 624146998Sdes /* Reset window sizes in case they have changed */ 625146998Sdes received_window_change_signal = 1; 62657429Smarkm 62757429Smarkm /* OK, we have been continued by the user. Reinitialize buffers. */ 62865668Skris buffer_init(bin); 62965668Skris buffer_init(bout); 63065668Skris buffer_init(berr); 63157429Smarkm 63257429Smarkm enter_raw_mode(); 63357429Smarkm} 63457429Smarkm 63592555Sdesstatic void 636162852Sdesclient_process_net_input(fd_set *readset) 63757429Smarkm{ 638197679Sdes int len, cont = 0; 639197679Sdes char buf[SSH_IOBUFSZ]; 64057429Smarkm 64157429Smarkm /* 64257429Smarkm * Read input from the server, and add any such data to the buffer of 64357429Smarkm * the packet subsystem. 64457429Smarkm */ 64557429Smarkm if (FD_ISSET(connection_in, readset)) { 64657429Smarkm /* Read as much as possible. */ 647197679Sdes len = roaming_read(connection_in, buf, sizeof(buf), &cont); 648197679Sdes if (len == 0 && cont == 0) { 649181111Sdes /* 650181111Sdes * Received EOF. The remote host has closed the 651181111Sdes * connection. 652181111Sdes */ 653181111Sdes snprintf(buf, sizeof buf, 654181111Sdes "Connection to %.300s closed by remote host.\r\n", 655181111Sdes host); 65657429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 65757429Smarkm quit_pending = 1; 65857429Smarkm return; 65957429Smarkm } 66057429Smarkm /* 66157429Smarkm * There is a kernel bug on Solaris that causes select to 66257429Smarkm * sometimes wake up even though there is no data available. 66357429Smarkm */ 664181111Sdes if (len < 0 && 665181111Sdes (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)) 66657429Smarkm len = 0; 66757429Smarkm 66857429Smarkm if (len < 0) { 669181111Sdes /* 670181111Sdes * An error has encountered. Perhaps there is a 671181111Sdes * network problem. 672181111Sdes */ 673181111Sdes snprintf(buf, sizeof buf, 674181111Sdes "Read from remote host %.300s: %.100s\r\n", 675181111Sdes host, strerror(errno)); 67657429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 67757429Smarkm quit_pending = 1; 67857429Smarkm return; 67957429Smarkm } 68057429Smarkm packet_process_incoming(buf, len); 68157429Smarkm } 68260573Skris} 68360573Skris 68498675Sdesstatic void 685181111Sdesclient_status_confirm(int type, Channel *c, void *ctx) 686137015Sdes{ 687181111Sdes struct channel_reply_ctx *cr = (struct channel_reply_ctx *)ctx; 688181111Sdes char errmsg[256]; 689181111Sdes int tochan; 690137015Sdes 691181111Sdes /* XXX supress on mux _client_ quietmode */ 692181111Sdes tochan = options.log_level >= SYSLOG_LEVEL_ERROR && 693181111Sdes c->ctl_fd != -1 && c->extended_usage == CHAN_EXTENDED_WRITE; 694137015Sdes 695181111Sdes if (type == SSH2_MSG_CHANNEL_SUCCESS) { 696181111Sdes debug2("%s request accepted on channel %d", 697181111Sdes cr->request_type, c->self); 698181111Sdes } else if (type == SSH2_MSG_CHANNEL_FAILURE) { 699181111Sdes if (tochan) { 700181111Sdes snprintf(errmsg, sizeof(errmsg), 701181111Sdes "%s request failed\r\n", cr->request_type); 702181111Sdes } else { 703181111Sdes snprintf(errmsg, sizeof(errmsg), 704181111Sdes "%s request failed on channel %d", 705181111Sdes cr->request_type, c->self); 706181111Sdes } 707181111Sdes /* If error occurred on primary session channel, then exit */ 708181111Sdes if (cr->do_close && c->self == session_ident) 709181111Sdes fatal("%s", errmsg); 710181111Sdes /* If error occurred on mux client, append to their stderr */ 711181111Sdes if (tochan) 712181111Sdes buffer_append(&c->extended, errmsg, strlen(errmsg)); 713181111Sdes else 714181111Sdes error("%s", errmsg); 715181111Sdes if (cr->do_close) { 716181111Sdes chan_read_failed(c); 717181111Sdes chan_write_failed(c); 718181111Sdes } 719137015Sdes } 720181111Sdes xfree(cr); 721137015Sdes} 722137015Sdes 723137015Sdesstatic void 724181111Sdesclient_abandon_status_confirm(Channel *c, void *ctx) 725137015Sdes{ 726181111Sdes xfree(ctx); 727137015Sdes} 728137015Sdes 729137015Sdesstatic void 730181111Sdesclient_expect_confirm(int id, const char *request, int do_close) 731137015Sdes{ 732181111Sdes struct channel_reply_ctx *cr = xmalloc(sizeof(*cr)); 733137015Sdes 734181111Sdes cr->request_type = request; 735181111Sdes cr->do_close = do_close; 736137015Sdes 737181111Sdes channel_register_status_confirm(id, client_status_confirm, 738181111Sdes client_abandon_status_confirm, cr); 739181111Sdes} 740137015Sdes 741181111Sdesvoid 742181111Sdesclient_register_global_confirm(global_confirm_cb *cb, void *ctx) 743181111Sdes{ 744181111Sdes struct global_confirm *gc, *last_gc; 745137015Sdes 746181111Sdes /* Coalesce identical callbacks */ 747181111Sdes last_gc = TAILQ_LAST(&global_confirms, global_confirms); 748181111Sdes if (last_gc && last_gc->cb == cb && last_gc->ctx == ctx) { 749181111Sdes if (++last_gc->ref_count >= INT_MAX) 750181111Sdes fatal("%s: last_gc->ref_count = %d", 751181111Sdes __func__, last_gc->ref_count); 752146998Sdes return; 753146998Sdes } 754146998Sdes 755181111Sdes gc = xmalloc(sizeof(*gc)); 756181111Sdes gc->cb = cb; 757181111Sdes gc->ctx = ctx; 758181111Sdes gc->ref_count = 1; 759181111Sdes TAILQ_INSERT_TAIL(&global_confirms, gc, entry); 760137015Sdes} 761137015Sdes 762137015Sdesstatic void 76398675Sdesprocess_cmdline(void) 76498675Sdes{ 76598675Sdes void (*handler)(int); 766146998Sdes char *s, *cmd, *cancel_host; 767137015Sdes int delete = 0; 768192595Sdes int local = 0, remote = 0, dynamic = 0; 769192595Sdes int cancel_port; 770146998Sdes Forward fwd; 77198675Sdes 772181111Sdes bzero(&fwd, sizeof(fwd)); 773181111Sdes fwd.listen_host = fwd.connect_host = NULL; 774181111Sdes 77598675Sdes leave_raw_mode(); 77698675Sdes handler = signal(SIGINT, SIG_IGN); 77798675Sdes cmd = s = read_passphrase("\r\nssh> ", RP_ECHO); 77898675Sdes if (s == NULL) 77998675Sdes goto out; 780181111Sdes while (isspace(*s)) 78198675Sdes s++; 782137015Sdes if (*s == '-') 783137015Sdes s++; /* Skip cmdline '-', if any */ 784137015Sdes if (*s == '\0') 78598675Sdes goto out; 786137015Sdes 787137015Sdes if (*s == 'h' || *s == 'H' || *s == '?') { 788137015Sdes logit("Commands:"); 789162852Sdes logit(" -L[bind_address:]port:host:hostport " 790162852Sdes "Request local forward"); 791162852Sdes logit(" -R[bind_address:]port:host:hostport " 792162852Sdes "Request remote forward"); 793192595Sdes logit(" -D[bind_address:]port " 794192595Sdes "Request dynamic forward"); 795162852Sdes logit(" -KR[bind_address:]port " 796162852Sdes "Cancel remote forward"); 797157016Sdes if (!options.permit_local_command) 798157016Sdes goto out; 799162852Sdes logit(" !args " 800162852Sdes "Execute local command"); 801137015Sdes goto out; 802137015Sdes } 803137015Sdes 804157016Sdes if (*s == '!' && options.permit_local_command) { 805157016Sdes s++; 806157016Sdes ssh_local_cmd(s); 807157016Sdes goto out; 808157016Sdes } 809157016Sdes 810137015Sdes if (*s == 'K') { 811137015Sdes delete = 1; 812137015Sdes s++; 813137015Sdes } 814192595Sdes if (*s == 'L') 815192595Sdes local = 1; 816192595Sdes else if (*s == 'R') 817192595Sdes remote = 1; 818192595Sdes else if (*s == 'D') 819192595Sdes dynamic = 1; 820192595Sdes else { 821124208Sdes logit("Invalid command."); 82298675Sdes goto out; 82398675Sdes } 824192595Sdes 825192595Sdes if ((local || dynamic) && delete) { 826137015Sdes logit("Not supported."); 827137015Sdes goto out; 828137015Sdes } 829192595Sdes if (remote && delete && !compat20) { 830124208Sdes logit("Not supported for SSH protocol version 1."); 83198675Sdes goto out; 83298675Sdes } 833137015Sdes 834181111Sdes while (isspace(*++s)) 835181111Sdes ; 83698675Sdes 837137015Sdes if (delete) { 838146998Sdes cancel_port = 0; 839146998Sdes cancel_host = hpdelim(&s); /* may be NULL */ 840146998Sdes if (s != NULL) { 841146998Sdes cancel_port = a2port(s); 842146998Sdes cancel_host = cleanhostname(cancel_host); 843146998Sdes } else { 844146998Sdes cancel_port = a2port(cancel_host); 845146998Sdes cancel_host = NULL; 84698675Sdes } 847192595Sdes if (cancel_port <= 0) { 848146998Sdes logit("Bad forwarding close port"); 849137015Sdes goto out; 850137015Sdes } 851146998Sdes channel_request_rforward_cancel(cancel_host, cancel_port); 852137015Sdes } else { 853192595Sdes if (!parse_forward(&fwd, s, dynamic, remote)) { 854137015Sdes logit("Bad forwarding specification."); 855137015Sdes goto out; 856137015Sdes } 857192595Sdes if (local || dynamic) { 858146998Sdes if (channel_setup_local_fwd_listener(fwd.listen_host, 859146998Sdes fwd.listen_port, fwd.connect_host, 860146998Sdes fwd.connect_port, options.gateway_ports) < 0) { 861137015Sdes logit("Port forwarding failed."); 862137015Sdes goto out; 863137015Sdes } 864146998Sdes } else { 865162852Sdes if (channel_request_remote_forwarding(fwd.listen_host, 866146998Sdes fwd.listen_port, fwd.connect_host, 867162852Sdes fwd.connect_port) < 0) { 868162852Sdes logit("Port forwarding failed."); 869162852Sdes goto out; 870162852Sdes } 871146998Sdes } 872146998Sdes 873137015Sdes logit("Forwarding port."); 874137015Sdes } 875137015Sdes 87698675Sdesout: 87798675Sdes signal(SIGINT, handler); 87898675Sdes enter_raw_mode(); 87998675Sdes if (cmd) 88098675Sdes xfree(cmd); 881181111Sdes if (fwd.listen_host != NULL) 882181111Sdes xfree(fwd.listen_host); 883181111Sdes if (fwd.connect_host != NULL) 884181111Sdes xfree(fwd.connect_host); 88598675Sdes} 88698675Sdes 887181111Sdes/* 888181111Sdes * Process the characters one by one, call with c==NULL for proto1 case. 889181111Sdes */ 89092555Sdesstatic int 891181111Sdesprocess_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr, 892181111Sdes char *buf, int len) 89365668Skris{ 89465668Skris char string[1024]; 89565668Skris pid_t pid; 89665668Skris int bytes = 0; 89776259Sgreen u_int i; 89876259Sgreen u_char ch; 89965668Skris char *s; 900181111Sdes int *escape_pendingp, escape_char; 901181111Sdes struct escape_filter_ctx *efc; 90265668Skris 903181111Sdes if (c == NULL) { 904181111Sdes escape_pendingp = &escape_pending1; 905181111Sdes escape_char = escape_char1; 906181111Sdes } else { 907181111Sdes if (c->filter_ctx == NULL) 908181111Sdes return 0; 909181111Sdes efc = (struct escape_filter_ctx *)c->filter_ctx; 910181111Sdes escape_pendingp = &efc->escape_pending; 911181111Sdes escape_char = efc->escape_char; 912181111Sdes } 913181111Sdes 914149749Sdes if (len <= 0) 915149749Sdes return (0); 916149749Sdes 917149749Sdes for (i = 0; i < (u_int)len; i++) { 91865668Skris /* Get one character at a time. */ 91965668Skris ch = buf[i]; 92065668Skris 921181111Sdes if (*escape_pendingp) { 92265668Skris /* We have previously seen an escape character. */ 92365668Skris /* Clear the flag now. */ 924181111Sdes *escape_pendingp = 0; 92565668Skris 92665668Skris /* Process the escaped character. */ 92765668Skris switch (ch) { 92865668Skris case '.': 92965668Skris /* Terminate the connection. */ 930181111Sdes snprintf(string, sizeof string, "%c.\r\n", 931181111Sdes escape_char); 93265668Skris buffer_append(berr, string, strlen(string)); 93365668Skris 934181111Sdes if (c && c->ctl_fd != -1) { 935181111Sdes chan_read_failed(c); 936181111Sdes chan_write_failed(c); 937181111Sdes return 0; 938181111Sdes } else 939181111Sdes quit_pending = 1; 94065668Skris return -1; 94165668Skris 94265668Skris case 'Z' - 64: 943181111Sdes /* XXX support this for mux clients */ 944181111Sdes if (c && c->ctl_fd != -1) { 945181111Sdes noescape: 946181111Sdes snprintf(string, sizeof string, 947181111Sdes "%c%c escape not available to " 948181111Sdes "multiplexed sessions\r\n", 949181111Sdes escape_char, ch); 950181111Sdes buffer_append(berr, string, 951181111Sdes strlen(string)); 952181111Sdes continue; 953181111Sdes } 954181111Sdes /* Suspend the program. Inform the user */ 955181111Sdes snprintf(string, sizeof string, 956181111Sdes "%c^Z [suspend ssh]\r\n", escape_char); 95765668Skris buffer_append(berr, string, strlen(string)); 95865668Skris 95965668Skris /* Restore terminal modes and suspend. */ 96065668Skris client_suspend_self(bin, bout, berr); 96165668Skris 96265668Skris /* We have been continued. */ 96365668Skris continue; 96465668Skris 965124208Sdes case 'B': 966124208Sdes if (compat20) { 967124208Sdes snprintf(string, sizeof string, 968124208Sdes "%cB\r\n", escape_char); 969124208Sdes buffer_append(berr, string, 970124208Sdes strlen(string)); 971124208Sdes channel_request_start(session_ident, 972124208Sdes "break", 0); 973124208Sdes packet_put_int(1000); 974124208Sdes packet_send(); 975124208Sdes } 976124208Sdes continue; 977124208Sdes 97876259Sgreen case 'R': 97976259Sgreen if (compat20) { 98076259Sgreen if (datafellows & SSH_BUG_NOREKEY) 981181111Sdes logit("Server does not " 982181111Sdes "support re-keying"); 98376259Sgreen else 98476259Sgreen need_rekeying = 1; 98576259Sgreen } 98676259Sgreen continue; 98776259Sgreen 98865668Skris case '&': 989181111Sdes if (c && c->ctl_fd != -1) 990181111Sdes goto noescape; 99165668Skris /* 992181111Sdes * Detach the program (continue to serve 993181111Sdes * connections, but put in background and no 994181111Sdes * more new connections). 99565668Skris */ 99665668Skris /* Restore tty modes. */ 99765668Skris leave_raw_mode(); 99865668Skris 99965668Skris /* Stop listening for new connections. */ 100065668Skris channel_stop_listening(); 100165668Skris 100292555Sdes snprintf(string, sizeof string, 100392555Sdes "%c& [backgrounded]\n", escape_char); 100492555Sdes buffer_append(berr, string, strlen(string)); 100565668Skris 100665668Skris /* Fork into background. */ 100765668Skris pid = fork(); 100865668Skris if (pid < 0) { 100965668Skris error("fork: %.100s", strerror(errno)); 101065668Skris continue; 101165668Skris } 101265668Skris if (pid != 0) { /* This is the parent. */ 101365668Skris /* The parent just exits. */ 101465668Skris exit(0); 101565668Skris } 101665668Skris /* The child continues serving connections. */ 101792555Sdes if (compat20) { 101892555Sdes buffer_append(bin, "\004", 1); 101992555Sdes /* fake EOF on stdin */ 102092555Sdes return -1; 102192555Sdes } else if (!stdin_eof) { 102292555Sdes /* 1023181111Sdes * Sending SSH_CMSG_EOF alone does not 1024181111Sdes * always appear to be enough. So we 1025181111Sdes * try to send an EOF character first. 102692555Sdes */ 102792555Sdes packet_start(SSH_CMSG_STDIN_DATA); 102892555Sdes packet_put_string("\004", 1); 102992555Sdes packet_send(); 103092555Sdes /* Close stdin. */ 103192555Sdes stdin_eof = 1; 103292555Sdes if (buffer_len(bin) == 0) { 103392555Sdes packet_start(SSH_CMSG_EOF); 103492555Sdes packet_send(); 103592555Sdes } 103692555Sdes } 103792555Sdes continue; 103865668Skris 103965668Skris case '?': 1040181111Sdes if (c && c->ctl_fd != -1) { 1041181111Sdes snprintf(string, sizeof string, 104265668Skris"%c?\r\n\ 104365668SkrisSupported escape sequences:\r\n\ 1044181111Sdes %c. - terminate session\r\n\ 1045181111Sdes %cB - send a BREAK to the remote system\r\n\ 1046181111Sdes %cR - Request rekey (SSH protocol 2 only)\r\n\ 1047181111Sdes %c# - list forwarded connections\r\n\ 1048181111Sdes %c? - this message\r\n\ 1049181111Sdes %c%c - send the escape character by typing it twice\r\n\ 105065668Skris(Note that escapes are only recognized immediately after newline.)\r\n", 1051181111Sdes escape_char, escape_char, 1052181111Sdes escape_char, escape_char, 1053181111Sdes escape_char, escape_char, 1054192595Sdes escape_char, escape_char); 1055181111Sdes } else { 1056181111Sdes snprintf(string, sizeof string, 1057181111Sdes"%c?\r\n\ 1058181111SdesSupported escape sequences:\r\n\ 1059181111Sdes %c. - terminate connection (and any multiplexed sessions)\r\n\ 1060181111Sdes %cB - send a BREAK to the remote system\r\n\ 1061181111Sdes %cC - open a command line\r\n\ 1062181111Sdes %cR - Request rekey (SSH protocol 2 only)\r\n\ 1063181111Sdes %c^Z - suspend ssh\r\n\ 1064181111Sdes %c# - list forwarded connections\r\n\ 1065181111Sdes %c& - background ssh (when waiting for connections to terminate)\r\n\ 1066181111Sdes %c? - this message\r\n\ 1067181111Sdes %c%c - send the escape character by typing it twice\r\n\ 1068181111Sdes(Note that escapes are only recognized immediately after newline.)\r\n", 1069181111Sdes escape_char, escape_char, 1070181111Sdes escape_char, escape_char, 1071181111Sdes escape_char, escape_char, 1072181111Sdes escape_char, escape_char, 1073181111Sdes escape_char, escape_char, 1074181111Sdes escape_char); 1075181111Sdes } 107665668Skris buffer_append(berr, string, strlen(string)); 107765668Skris continue; 107865668Skris 107965668Skris case '#': 1080181111Sdes snprintf(string, sizeof string, "%c#\r\n", 1081181111Sdes escape_char); 108265668Skris buffer_append(berr, string, strlen(string)); 108365668Skris s = channel_open_message(); 108465668Skris buffer_append(berr, s, strlen(s)); 108565668Skris xfree(s); 108665668Skris continue; 108765668Skris 108898675Sdes case 'C': 1089192595Sdes if (c && c->ctl_fd != -1) 1090192595Sdes goto noescape; 109198675Sdes process_cmdline(); 109298675Sdes continue; 109398675Sdes 109465668Skris default: 109565668Skris if (ch != escape_char) { 109665668Skris buffer_put_char(bin, escape_char); 109765668Skris bytes++; 109865668Skris } 109965668Skris /* Escaped characters fall through here */ 110065668Skris break; 110165668Skris } 110265668Skris } else { 110365668Skris /* 1104181111Sdes * The previous character was not an escape char. 1105181111Sdes * Check if this is an escape. 110665668Skris */ 110765668Skris if (last_was_cr && ch == escape_char) { 1108181111Sdes /* 1109181111Sdes * It is. Set the flag and continue to 1110181111Sdes * next character. 1111181111Sdes */ 1112181111Sdes *escape_pendingp = 1; 111365668Skris continue; 111465668Skris } 111565668Skris } 111665668Skris 111765668Skris /* 111865668Skris * Normal character. Record whether it was a newline, 111965668Skris * and append it to the buffer. 112065668Skris */ 112165668Skris last_was_cr = (ch == '\r' || ch == '\n'); 112265668Skris buffer_put_char(bin, ch); 112365668Skris bytes++; 112465668Skris } 112565668Skris return bytes; 112665668Skris} 112765668Skris 112892555Sdesstatic void 1129162852Sdesclient_process_input(fd_set *readset) 113060573Skris{ 113160573Skris int len; 1132197679Sdes char buf[SSH_IOBUFSZ]; 113360573Skris 113457429Smarkm /* Read input from stdin. */ 113557429Smarkm if (FD_ISSET(fileno(stdin), readset)) { 113657429Smarkm /* Read as much as possible. */ 113757429Smarkm len = read(fileno(stdin), buf, sizeof(buf)); 1138181111Sdes if (len < 0 && 1139181111Sdes (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)) 114076259Sgreen return; /* we'll try again later */ 114157429Smarkm if (len <= 0) { 114257429Smarkm /* 114357429Smarkm * Received EOF or error. They are treated 114457429Smarkm * similarly, except that an error message is printed 114557429Smarkm * if it was an error condition. 114657429Smarkm */ 114757429Smarkm if (len < 0) { 1148181111Sdes snprintf(buf, sizeof buf, "read: %.100s\r\n", 1149181111Sdes strerror(errno)); 115057429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 115157429Smarkm } 115257429Smarkm /* Mark that we have seen EOF. */ 115357429Smarkm stdin_eof = 1; 115457429Smarkm /* 115557429Smarkm * Send an EOF message to the server unless there is 115657429Smarkm * data in the buffer. If there is data in the 115757429Smarkm * buffer, no message will be sent now. Code 115857429Smarkm * elsewhere will send the EOF when the buffer 115957429Smarkm * becomes empty if stdin_eof is set. 116057429Smarkm */ 116157429Smarkm if (buffer_len(&stdin_buffer) == 0) { 116257429Smarkm packet_start(SSH_CMSG_EOF); 116357429Smarkm packet_send(); 116457429Smarkm } 1165181111Sdes } else if (escape_char1 == SSH_ESCAPECHAR_NONE) { 116657429Smarkm /* 116757429Smarkm * Normal successful read, and no escape character. 116857429Smarkm * Just append the data to buffer. 116957429Smarkm */ 117057429Smarkm buffer_append(&stdin_buffer, buf, len); 117157429Smarkm } else { 117257429Smarkm /* 1173181111Sdes * Normal, successful read. But we have an escape 1174181111Sdes * character and have to process the characters one 1175181111Sdes * by one. 117657429Smarkm */ 1177181111Sdes if (process_escapes(NULL, &stdin_buffer, 1178181111Sdes &stdout_buffer, &stderr_buffer, buf, len) == -1) 117965668Skris return; 118057429Smarkm } 118157429Smarkm } 118257429Smarkm} 118357429Smarkm 118492555Sdesstatic void 1185162852Sdesclient_process_output(fd_set *writeset) 118657429Smarkm{ 118757429Smarkm int len; 118857429Smarkm char buf[100]; 118957429Smarkm 119057429Smarkm /* Write buffered output to stdout. */ 119157429Smarkm if (FD_ISSET(fileno(stdout), writeset)) { 119257429Smarkm /* Write as much data as possible. */ 119357429Smarkm len = write(fileno(stdout), buffer_ptr(&stdout_buffer), 119457429Smarkm buffer_len(&stdout_buffer)); 119557429Smarkm if (len <= 0) { 1196181111Sdes if (errno == EINTR || errno == EAGAIN || 1197181111Sdes errno == EWOULDBLOCK) 119857429Smarkm len = 0; 119957429Smarkm else { 120057429Smarkm /* 120157429Smarkm * An error or EOF was encountered. Put an 120257429Smarkm * error message to stderr buffer. 120357429Smarkm */ 1204181111Sdes snprintf(buf, sizeof buf, 1205181111Sdes "write stdout: %.50s\r\n", strerror(errno)); 120657429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 120757429Smarkm quit_pending = 1; 120857429Smarkm return; 120957429Smarkm } 121057429Smarkm } 121157429Smarkm /* Consume printed data from the buffer. */ 121257429Smarkm buffer_consume(&stdout_buffer, len); 121357429Smarkm } 121457429Smarkm /* Write buffered output to stderr. */ 121557429Smarkm if (FD_ISSET(fileno(stderr), writeset)) { 121657429Smarkm /* Write as much data as possible. */ 121757429Smarkm len = write(fileno(stderr), buffer_ptr(&stderr_buffer), 121857429Smarkm buffer_len(&stderr_buffer)); 121957429Smarkm if (len <= 0) { 1220181111Sdes if (errno == EINTR || errno == EAGAIN || 1221181111Sdes errno == EWOULDBLOCK) 122257429Smarkm len = 0; 122357429Smarkm else { 1224181111Sdes /* 1225181111Sdes * EOF or error, but can't even print 1226181111Sdes * error message. 1227181111Sdes */ 122857429Smarkm quit_pending = 1; 122957429Smarkm return; 123057429Smarkm } 123157429Smarkm } 123257429Smarkm /* Consume printed characters from the buffer. */ 123357429Smarkm buffer_consume(&stderr_buffer, len); 123457429Smarkm } 123557429Smarkm} 123657429Smarkm 123757429Smarkm/* 123860573Skris * Get packets from the connection input buffer, and process them as long as 123960573Skris * there are packets available. 124060573Skris * 124160573Skris * Any unknown packets received during the actual 124260573Skris * session cause the session to terminate. This is 124360573Skris * intended to make debugging easier since no 124460573Skris * confirmations are sent. Any compatible protocol 124560573Skris * extensions must be negotiated during the 124660573Skris * preparatory phase. 124760573Skris */ 124860573Skris 124992555Sdesstatic void 125076259Sgreenclient_process_buffered_input_packets(void) 125160573Skris{ 1252181111Sdes dispatch_run(DISPATCH_NONBLOCK, &quit_pending, 1253181111Sdes compat20 ? xxx_kex : NULL); 125460573Skris} 125560573Skris 125665668Skris/* scan buf[] for '~' before sending data to the peer */ 125765668Skris 1258181111Sdes/* Helper: allocate a new escape_filter_ctx and fill in its escape char */ 1259181111Sdesvoid * 1260181111Sdesclient_new_escape_filter_ctx(int escape_char) 126165668Skris{ 1262181111Sdes struct escape_filter_ctx *ret; 1263181111Sdes 1264181111Sdes ret = xmalloc(sizeof(*ret)); 1265181111Sdes ret->escape_pending = 0; 1266181111Sdes ret->escape_char = escape_char; 1267181111Sdes return (void *)ret; 126865668Skris} 126965668Skris 1270181111Sdes/* Free the escape filter context on channel free */ 1271181111Sdesvoid 1272181111Sdesclient_filter_cleanup(int cid, void *ctx) 1273181111Sdes{ 1274181111Sdes xfree(ctx); 1275181111Sdes} 1276181111Sdes 1277181111Sdesint 1278181111Sdesclient_simple_escape_filter(Channel *c, char *buf, int len) 1279181111Sdes{ 1280181111Sdes if (c->extended_usage != CHAN_EXTENDED_WRITE) 1281181111Sdes return 0; 1282181111Sdes 1283181111Sdes return process_escapes(c, &c->input, &c->output, &c->extended, 1284181111Sdes buf, len); 1285181111Sdes} 1286181111Sdes 128792555Sdesstatic void 128876259Sgreenclient_channel_closed(int id, void *arg) 128976259Sgreen{ 129092555Sdes channel_cancel_cleanup(id); 129176259Sgreen session_closed = 1; 1292126274Sdes leave_raw_mode(); 129376259Sgreen} 129476259Sgreen 129560573Skris/* 129657429Smarkm * Implements the interactive session with the server. This is called after 129757429Smarkm * the user has been authenticated, and a command has been started on the 129892555Sdes * remote host. If escape_char != SSH_ESCAPECHAR_NONE, it is the character 129992555Sdes * used as an escape character for terminating or suspending the session. 130057429Smarkm */ 130157429Smarkm 130260573Skrisint 130365668Skrisclient_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) 130457429Smarkm{ 130576259Sgreen fd_set *readset = NULL, *writeset = NULL; 130657429Smarkm double start_time, total_time; 1307137015Sdes int max_fd = 0, max_fd2 = 0, len, rekeying = 0; 1308181111Sdes u_int64_t ibytes, obytes; 1309137015Sdes u_int nalloc = 0; 131057429Smarkm char buf[100]; 131157429Smarkm 131257429Smarkm debug("Entering interactive session."); 131357429Smarkm 131457429Smarkm start_time = get_current_time(); 131557429Smarkm 131657429Smarkm /* Initialize variables. */ 1317181111Sdes escape_pending1 = 0; 131857429Smarkm last_was_cr = 1; 131957429Smarkm exit_status = -1; 132057429Smarkm stdin_eof = 0; 132157429Smarkm buffer_high = 64 * 1024; 132257429Smarkm connection_in = packet_get_connection_in(); 132357429Smarkm connection_out = packet_get_connection_out(); 132476259Sgreen max_fd = MAX(connection_in, connection_out); 1325181111Sdes if (muxserver_sock != -1) 1326181111Sdes max_fd = MAX(max_fd, muxserver_sock); 132776259Sgreen 132876259Sgreen if (!compat20) { 132976259Sgreen /* enable nonblocking unless tty */ 133076259Sgreen if (!isatty(fileno(stdin))) 133176259Sgreen set_nonblock(fileno(stdin)); 133276259Sgreen if (!isatty(fileno(stdout))) 133376259Sgreen set_nonblock(fileno(stdout)); 133476259Sgreen if (!isatty(fileno(stderr))) 133576259Sgreen set_nonblock(fileno(stderr)); 133676259Sgreen max_fd = MAX(max_fd, fileno(stdin)); 133776259Sgreen max_fd = MAX(max_fd, fileno(stdout)); 133876259Sgreen max_fd = MAX(max_fd, fileno(stderr)); 133976259Sgreen } 134057429Smarkm quit_pending = 0; 1341181111Sdes escape_char1 = escape_char_arg; 134257429Smarkm 134357429Smarkm /* Initialize buffers. */ 134457429Smarkm buffer_init(&stdin_buffer); 134557429Smarkm buffer_init(&stdout_buffer); 134657429Smarkm buffer_init(&stderr_buffer); 134757429Smarkm 134860573Skris client_init_dispatch(); 134960573Skris 1350113908Sdes /* 1351113908Sdes * Set signal handlers, (e.g. to restore non-blocking mode) 1352113908Sdes * but don't overwrite SIG_IGN, matches behaviour from rsh(1) 1353113908Sdes */ 1354146998Sdes if (signal(SIGHUP, SIG_IGN) != SIG_IGN) 1355146998Sdes signal(SIGHUP, signal_handler); 1356113908Sdes if (signal(SIGINT, SIG_IGN) != SIG_IGN) 1357113908Sdes signal(SIGINT, signal_handler); 1358113908Sdes if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) 1359113908Sdes signal(SIGQUIT, signal_handler); 1360113908Sdes if (signal(SIGTERM, SIG_IGN) != SIG_IGN) 1361113908Sdes signal(SIGTERM, signal_handler); 1362146998Sdes signal(SIGWINCH, window_change_handler); 136357429Smarkm 136457429Smarkm if (have_pty) 136557429Smarkm enter_raw_mode(); 136657429Smarkm 136776259Sgreen if (compat20) { 136876259Sgreen session_ident = ssh2_chan_id; 1369181111Sdes if (escape_char_arg != SSH_ESCAPECHAR_NONE) 137076259Sgreen channel_register_filter(session_ident, 1371181111Sdes client_simple_escape_filter, NULL, 1372181111Sdes client_filter_cleanup, 1373181111Sdes client_new_escape_filter_ctx(escape_char_arg)); 137476259Sgreen if (session_ident != -1) 137576259Sgreen channel_register_cleanup(session_ident, 1376157016Sdes client_channel_closed, 0); 137776259Sgreen } else { 137876259Sgreen /* Check if we should immediately send eof on stdin. */ 137960573Skris client_check_initial_eof_on_stdin(); 138076259Sgreen } 138157429Smarkm 138257429Smarkm /* Main loop of the client for the interactive session mode. */ 138357429Smarkm while (!quit_pending) { 138457429Smarkm 138557429Smarkm /* Process buffered packets sent by the server. */ 138657429Smarkm client_process_buffered_input_packets(); 138757429Smarkm 138876259Sgreen if (compat20 && session_closed && !channel_still_open()) 138960573Skris break; 139060573Skris 139176259Sgreen rekeying = (xxx_kex != NULL && !xxx_kex->done); 139257429Smarkm 139376259Sgreen if (rekeying) { 139476259Sgreen debug("rekeying in progress"); 139576259Sgreen } else { 139676259Sgreen /* 139776259Sgreen * Make packets of buffered stdin data, and buffer 139876259Sgreen * them for sending to the server. 139976259Sgreen */ 140076259Sgreen if (!compat20) 140176259Sgreen client_make_packets_from_stdin_data(); 140257429Smarkm 140376259Sgreen /* 140476259Sgreen * Make packets from buffered channel data, and 140576259Sgreen * enqueue them for sending to the server. 140676259Sgreen */ 140776259Sgreen if (packet_not_very_much_data_to_write()) 140876259Sgreen channel_output_poll(); 140957429Smarkm 141076259Sgreen /* 141176259Sgreen * Check if the window size has changed, and buffer a 141276259Sgreen * message about it to the server if so. 141376259Sgreen */ 141476259Sgreen client_check_window_change(); 141557429Smarkm 141676259Sgreen if (quit_pending) 141776259Sgreen break; 141876259Sgreen } 141957429Smarkm /* 142057429Smarkm * Wait until we have something to do (something becomes 142157429Smarkm * available on one of the descriptors). 142257429Smarkm */ 142392555Sdes max_fd2 = max_fd; 142476259Sgreen client_wait_until_can_do_something(&readset, &writeset, 142592555Sdes &max_fd2, &nalloc, rekeying); 142657429Smarkm 142757429Smarkm if (quit_pending) 142857429Smarkm break; 142957429Smarkm 143076259Sgreen /* Do channel operations unless rekeying in progress. */ 143176259Sgreen if (!rekeying) { 143276259Sgreen channel_after_select(readset, writeset); 1433124208Sdes if (need_rekeying || packet_need_rekeying()) { 1434124208Sdes debug("need rekeying"); 143576259Sgreen xxx_kex->done = 0; 143676259Sgreen kex_send_kexinit(xxx_kex); 143776259Sgreen need_rekeying = 0; 143876259Sgreen } 143976259Sgreen } 144076259Sgreen 144160573Skris /* Buffer input from the connection. */ 144276259Sgreen client_process_net_input(readset); 144357429Smarkm 1444137015Sdes /* Accept control connections. */ 1445181111Sdes if (muxserver_sock != -1 &&FD_ISSET(muxserver_sock, readset)) { 1446181111Sdes if (muxserver_accept_control()) 1447181111Sdes quit_pending = 1; 1448181111Sdes } 1449137015Sdes 145060573Skris if (quit_pending) 145160573Skris break; 145257429Smarkm 145360573Skris if (!compat20) { 145460573Skris /* Buffer data from stdin */ 145576259Sgreen client_process_input(readset); 145660573Skris /* 145760573Skris * Process output to stdout and stderr. Output to 145860573Skris * the connection is processed elsewhere (above). 145960573Skris */ 146076259Sgreen client_process_output(writeset); 146160573Skris } 146260573Skris 1463181111Sdes /* 1464181111Sdes * Send as much buffered packet data as possible to the 1465181111Sdes * sender. 1466181111Sdes */ 146776259Sgreen if (FD_ISSET(connection_out, writeset)) 146857429Smarkm packet_write_poll(); 146957429Smarkm } 147076259Sgreen if (readset) 147176259Sgreen xfree(readset); 147276259Sgreen if (writeset) 147376259Sgreen xfree(writeset); 147457429Smarkm 147557429Smarkm /* Terminate the session. */ 147657429Smarkm 147757429Smarkm /* Stop watching for window change. */ 1478146998Sdes signal(SIGWINCH, SIG_DFL); 147957429Smarkm 1480197679Sdes if (compat20) { 1481197679Sdes packet_start(SSH2_MSG_DISCONNECT); 1482197679Sdes packet_put_int(SSH2_DISCONNECT_BY_APPLICATION); 1483197679Sdes packet_put_cstring("disconnected by user"); 1484197679Sdes packet_send(); 1485197679Sdes packet_write_wait(); 1486197679Sdes } 1487197679Sdes 148892555Sdes channel_free_all(); 148957429Smarkm 149092555Sdes if (have_pty) 149192555Sdes leave_raw_mode(); 149292555Sdes 149392555Sdes /* restore blocking io */ 149492555Sdes if (!isatty(fileno(stdin))) 149592555Sdes unset_nonblock(fileno(stdin)); 149692555Sdes if (!isatty(fileno(stdout))) 149792555Sdes unset_nonblock(fileno(stdout)); 149892555Sdes if (!isatty(fileno(stderr))) 149992555Sdes unset_nonblock(fileno(stderr)); 150092555Sdes 1501126274Sdes /* 1502126274Sdes * If there was no shell or command requested, there will be no remote 1503126274Sdes * exit status to be returned. In that case, clear error code if the 1504126274Sdes * connection was deliberately terminated at this end. 1505126274Sdes */ 1506126274Sdes if (no_shell_flag && received_signal == SIGTERM) { 1507126274Sdes received_signal = 0; 1508126274Sdes exit_status = 0; 150992555Sdes } 151092555Sdes 1511126274Sdes if (received_signal) 1512126274Sdes fatal("Killed by signal %d.", (int) received_signal); 1513126274Sdes 151457429Smarkm /* 151557429Smarkm * In interactive mode (with pseudo tty) display a message indicating 151657429Smarkm * that the connection has been closed. 151757429Smarkm */ 151857429Smarkm if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) { 1519181111Sdes snprintf(buf, sizeof buf, 1520181111Sdes "Connection to %.64s closed.\r\n", host); 152157429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 152257429Smarkm } 152392555Sdes 152457429Smarkm /* Output any buffered data for stdout. */ 152557429Smarkm while (buffer_len(&stdout_buffer) > 0) { 152657429Smarkm len = write(fileno(stdout), buffer_ptr(&stdout_buffer), 152757429Smarkm buffer_len(&stdout_buffer)); 152857429Smarkm if (len <= 0) { 152957429Smarkm error("Write failed flushing stdout buffer."); 153057429Smarkm break; 153157429Smarkm } 153257429Smarkm buffer_consume(&stdout_buffer, len); 153357429Smarkm } 153457429Smarkm 153557429Smarkm /* Output any buffered data for stderr. */ 153657429Smarkm while (buffer_len(&stderr_buffer) > 0) { 153757429Smarkm len = write(fileno(stderr), buffer_ptr(&stderr_buffer), 153857429Smarkm buffer_len(&stderr_buffer)); 153957429Smarkm if (len <= 0) { 154057429Smarkm error("Write failed flushing stderr buffer."); 154157429Smarkm break; 154257429Smarkm } 154357429Smarkm buffer_consume(&stderr_buffer, len); 154457429Smarkm } 154557429Smarkm 154657429Smarkm /* Clear and free any buffers. */ 154757429Smarkm memset(buf, 0, sizeof(buf)); 154857429Smarkm buffer_free(&stdin_buffer); 154957429Smarkm buffer_free(&stdout_buffer); 155057429Smarkm buffer_free(&stderr_buffer); 155157429Smarkm 155257429Smarkm /* Report bytes transferred, and transfer rates. */ 155357429Smarkm total_time = get_current_time() - start_time; 1554181111Sdes packet_get_state(MODE_IN, NULL, NULL, NULL, &ibytes); 1555181111Sdes packet_get_state(MODE_OUT, NULL, NULL, NULL, &obytes); 1556181111Sdes verbose("Transferred: sent %llu, received %llu bytes, in %.1f seconds", 1557181111Sdes obytes, ibytes, total_time); 155857429Smarkm if (total_time > 0) 1559181111Sdes verbose("Bytes per second: sent %.1f, received %.1f", 1560181111Sdes obytes / total_time, ibytes / total_time); 156157429Smarkm /* Return the exit status of the program. */ 156257429Smarkm debug("Exit status %d", exit_status); 156357429Smarkm return exit_status; 156457429Smarkm} 156560573Skris 156660573Skris/*********/ 156760573Skris 156892555Sdesstatic void 156992555Sdesclient_input_stdout_data(int type, u_int32_t seq, void *ctxt) 157060573Skris{ 157176259Sgreen u_int data_len; 157260573Skris char *data = packet_get_string(&data_len); 157392555Sdes packet_check_eom(); 157460573Skris buffer_append(&stdout_buffer, data, data_len); 157560573Skris memset(data, 0, data_len); 157660573Skris xfree(data); 157760573Skris} 157892555Sdesstatic void 157992555Sdesclient_input_stderr_data(int type, u_int32_t seq, void *ctxt) 158060573Skris{ 158176259Sgreen u_int data_len; 158260573Skris char *data = packet_get_string(&data_len); 158392555Sdes packet_check_eom(); 158460573Skris buffer_append(&stderr_buffer, data, data_len); 158560573Skris memset(data, 0, data_len); 158660573Skris xfree(data); 158760573Skris} 158892555Sdesstatic void 158992555Sdesclient_input_exit_status(int type, u_int32_t seq, void *ctxt) 159060573Skris{ 159160573Skris exit_status = packet_get_int(); 159292555Sdes packet_check_eom(); 159360573Skris /* Acknowledge the exit. */ 159460573Skris packet_start(SSH_CMSG_EXIT_CONFIRMATION); 159560573Skris packet_send(); 159660573Skris /* 159760573Skris * Must wait for packet to be sent since we are 159860573Skris * exiting the loop. 159960573Skris */ 160060573Skris packet_write_wait(); 160160573Skris /* Flag that we want to exit. */ 160260573Skris quit_pending = 1; 160360573Skris} 1604126274Sdesstatic void 1605126274Sdesclient_input_agent_open(int type, u_int32_t seq, void *ctxt) 1606126274Sdes{ 1607126274Sdes Channel *c = NULL; 1608126274Sdes int remote_id, sock; 160960573Skris 1610126274Sdes /* Read the remote channel number from the message. */ 1611126274Sdes remote_id = packet_get_int(); 1612126274Sdes packet_check_eom(); 1613126274Sdes 1614126274Sdes /* 1615126274Sdes * Get a connection to the local authentication agent (this may again 1616126274Sdes * get forwarded). 1617126274Sdes */ 1618126274Sdes sock = ssh_get_authentication_socket(); 1619126274Sdes 1620126274Sdes /* 1621126274Sdes * If we could not connect the agent, send an error message back to 1622126274Sdes * the server. This should never happen unless the agent dies, 1623126274Sdes * because authentication forwarding is only enabled if we have an 1624126274Sdes * agent. 1625126274Sdes */ 1626126274Sdes if (sock >= 0) { 1627126274Sdes c = channel_new("", SSH_CHANNEL_OPEN, sock, sock, 1628126274Sdes -1, 0, 0, 0, "authentication agent connection", 1); 1629126274Sdes c->remote_id = remote_id; 1630126274Sdes c->force_drain = 1; 1631126274Sdes } 1632126274Sdes if (c == NULL) { 1633126274Sdes packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 1634126274Sdes packet_put_int(remote_id); 1635126274Sdes } else { 1636126274Sdes /* Send a confirmation to the remote host. */ 1637126274Sdes debug("Forwarding authentication connection."); 1638126274Sdes packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 1639126274Sdes packet_put_int(remote_id); 1640126274Sdes packet_put_int(c->self); 1641126274Sdes } 1642126274Sdes packet_send(); 1643126274Sdes} 1644126274Sdes 164592555Sdesstatic Channel * 164676259Sgreenclient_request_forwarded_tcpip(const char *request_type, int rchan) 164776259Sgreen{ 1648106121Sdes Channel *c = NULL; 164976259Sgreen char *listen_address, *originator_address; 1650192595Sdes u_short listen_port, originator_port; 165176259Sgreen 165276259Sgreen /* Get rest of the packet */ 165376259Sgreen listen_address = packet_get_string(NULL); 165476259Sgreen listen_port = packet_get_int(); 165576259Sgreen originator_address = packet_get_string(NULL); 165676259Sgreen originator_port = packet_get_int(); 165792555Sdes packet_check_eom(); 165876259Sgreen 1659181111Sdes debug("client_request_forwarded_tcpip: listen %s port %d, " 1660181111Sdes "originator %s port %d", listen_address, listen_port, 1661181111Sdes originator_address, originator_port); 166276259Sgreen 1663181111Sdes c = channel_connect_by_listen_address(listen_port, 1664181111Sdes "forwarded-tcpip", originator_address); 1665181111Sdes 166676259Sgreen xfree(originator_address); 166776259Sgreen xfree(listen_address); 166876259Sgreen return c; 166976259Sgreen} 167076259Sgreen 1671106121Sdesstatic Channel * 167276259Sgreenclient_request_x11(const char *request_type, int rchan) 167376259Sgreen{ 167476259Sgreen Channel *c = NULL; 167576259Sgreen char *originator; 1676192595Sdes u_short originator_port; 167792555Sdes int sock; 167876259Sgreen 167976259Sgreen if (!options.forward_x11) { 168076259Sgreen error("Warning: ssh server tried X11 forwarding."); 1681181111Sdes error("Warning: this is probably a break-in attempt by a " 1682181111Sdes "malicious server."); 168376259Sgreen return NULL; 168476259Sgreen } 168576259Sgreen originator = packet_get_string(NULL); 168676259Sgreen if (datafellows & SSH_BUG_X11FWD) { 168776259Sgreen debug2("buggy server: x11 request w/o originator_port"); 168876259Sgreen originator_port = 0; 168976259Sgreen } else { 169076259Sgreen originator_port = packet_get_int(); 169176259Sgreen } 169292555Sdes packet_check_eom(); 169376259Sgreen /* XXX check permission */ 169476259Sgreen debug("client_request_x11: request from %s %d", originator, 169576259Sgreen originator_port); 169692555Sdes xfree(originator); 169776259Sgreen sock = x11_connect_display(); 169892555Sdes if (sock < 0) 169992555Sdes return NULL; 170092555Sdes c = channel_new("x11", 170192555Sdes SSH_CHANNEL_X11_OPEN, sock, sock, -1, 1702124208Sdes CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "x11", 1); 170392555Sdes c->force_drain = 1; 170476259Sgreen return c; 170576259Sgreen} 170676259Sgreen 1707106121Sdesstatic Channel * 170876259Sgreenclient_request_agent(const char *request_type, int rchan) 170976259Sgreen{ 171076259Sgreen Channel *c = NULL; 171192555Sdes int sock; 171276259Sgreen 171376259Sgreen if (!options.forward_agent) { 171476259Sgreen error("Warning: ssh server tried agent forwarding."); 1715181111Sdes error("Warning: this is probably a break-in attempt by a " 1716181111Sdes "malicious server."); 171776259Sgreen return NULL; 171876259Sgreen } 1719181111Sdes sock = ssh_get_authentication_socket(); 172092555Sdes if (sock < 0) 172192555Sdes return NULL; 172292555Sdes c = channel_new("authentication agent connection", 172392555Sdes SSH_CHANNEL_OPEN, sock, sock, -1, 1724181111Sdes CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, 1725124208Sdes "authentication agent connection", 1); 172692555Sdes c->force_drain = 1; 172776259Sgreen return c; 172876259Sgreen} 172976259Sgreen 1730181111Sdesint 1731181111Sdesclient_request_tun_fwd(int tun_mode, int local_tun, int remote_tun) 1732181111Sdes{ 1733181111Sdes Channel *c; 1734181111Sdes int fd; 1735181111Sdes 1736181111Sdes if (tun_mode == SSH_TUNMODE_NO) 1737181111Sdes return 0; 1738181111Sdes 1739181111Sdes if (!compat20) { 1740192595Sdes error("Tunnel forwarding is not supported for protocol 1"); 1741181111Sdes return -1; 1742181111Sdes } 1743181111Sdes 1744181111Sdes debug("Requesting tun unit %d in mode %d", local_tun, tun_mode); 1745181111Sdes 1746181111Sdes /* Open local tunnel device */ 1747181111Sdes if ((fd = tun_open(local_tun, tun_mode)) == -1) { 1748181111Sdes error("Tunnel device open failed."); 1749181111Sdes return -1; 1750181111Sdes } 1751181111Sdes 1752181111Sdes c = channel_new("tun", SSH_CHANNEL_OPENING, fd, fd, -1, 1753181111Sdes CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1); 1754181111Sdes c->datagram = 1; 1755181111Sdes 1756181111Sdes#if defined(SSH_TUN_FILTER) 1757181111Sdes if (options.tun_open == SSH_TUNMODE_POINTOPOINT) 1758181111Sdes channel_register_filter(c->self, sys_tun_infilter, 1759181111Sdes sys_tun_outfilter, NULL, NULL); 1760181111Sdes#endif 1761181111Sdes 1762181111Sdes packet_start(SSH2_MSG_CHANNEL_OPEN); 1763181111Sdes packet_put_cstring("tun@openssh.com"); 1764181111Sdes packet_put_int(c->self); 1765181111Sdes packet_put_int(c->local_window_max); 1766181111Sdes packet_put_int(c->local_maxpacket); 1767181111Sdes packet_put_int(tun_mode); 1768181111Sdes packet_put_int(remote_tun); 1769181111Sdes packet_send(); 1770181111Sdes 1771181111Sdes return 0; 1772181111Sdes} 1773181111Sdes 177460573Skris/* XXXX move to generic input handler */ 177592555Sdesstatic void 177692555Sdesclient_input_channel_open(int type, u_int32_t seq, void *ctxt) 177760573Skris{ 177860573Skris Channel *c = NULL; 177960573Skris char *ctype; 178060573Skris int rchan; 178199060Sdes u_int rmaxpack, rwindow, len; 178260573Skris 178360573Skris ctype = packet_get_string(&len); 178460573Skris rchan = packet_get_int(); 178560573Skris rwindow = packet_get_int(); 178660573Skris rmaxpack = packet_get_int(); 178760573Skris 178860573Skris debug("client_input_channel_open: ctype %s rchan %d win %d max %d", 178960573Skris ctype, rchan, rwindow, rmaxpack); 179060573Skris 179176259Sgreen if (strcmp(ctype, "forwarded-tcpip") == 0) { 179276259Sgreen c = client_request_forwarded_tcpip(ctype, rchan); 179376259Sgreen } else if (strcmp(ctype, "x11") == 0) { 179476259Sgreen c = client_request_x11(ctype, rchan); 179576259Sgreen } else if (strcmp(ctype, "auth-agent@openssh.com") == 0) { 179676259Sgreen c = client_request_agent(ctype, rchan); 179760573Skris } 179860573Skris/* XXX duplicate : */ 179960573Skris if (c != NULL) { 180060573Skris debug("confirm %s", ctype); 180160573Skris c->remote_id = rchan; 180260573Skris c->remote_window = rwindow; 180360573Skris c->remote_maxpacket = rmaxpack; 180492555Sdes if (c->type != SSH_CHANNEL_CONNECTING) { 180592555Sdes packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); 180692555Sdes packet_put_int(c->remote_id); 180792555Sdes packet_put_int(c->self); 180892555Sdes packet_put_int(c->local_window); 180992555Sdes packet_put_int(c->local_maxpacket); 181092555Sdes packet_send(); 181192555Sdes } 181260573Skris } else { 181360573Skris debug("failure %s", ctype); 181460573Skris packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); 181560573Skris packet_put_int(rchan); 181660573Skris packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED); 181792555Sdes if (!(datafellows & SSH_BUG_OPENFAILURE)) { 181892555Sdes packet_put_cstring("open failed"); 181992555Sdes packet_put_cstring(""); 182092555Sdes } 182160573Skris packet_send(); 182260573Skris } 182360573Skris xfree(ctype); 182460573Skris} 182592555Sdesstatic void 182692555Sdesclient_input_channel_req(int type, u_int32_t seq, void *ctxt) 182776259Sgreen{ 182876259Sgreen Channel *c = NULL; 1829137015Sdes int exitval, id, reply, success = 0; 183076259Sgreen char *rtype; 183160573Skris 183276259Sgreen id = packet_get_int(); 183376259Sgreen rtype = packet_get_string(NULL); 183476259Sgreen reply = packet_get_char(); 183576259Sgreen 183676259Sgreen debug("client_input_channel_req: channel %d rtype %s reply %d", 183776259Sgreen id, rtype, reply); 183876259Sgreen 1839137015Sdes if (id == -1) { 1840137015Sdes error("client_input_channel_req: request for channel -1"); 1841137015Sdes } else if ((c = channel_lookup(id)) == NULL) { 1842181111Sdes error("client_input_channel_req: channel %d: " 1843181111Sdes "unknown channel", id); 1844181111Sdes } else if (strcmp(rtype, "eow@openssh.com") == 0) { 1845181111Sdes packet_check_eom(); 1846181111Sdes chan_rcvd_eow(c); 184776259Sgreen } else if (strcmp(rtype, "exit-status") == 0) { 1848137015Sdes exitval = packet_get_int(); 1849137015Sdes if (id == session_ident) { 1850137015Sdes success = 1; 1851137015Sdes exit_status = exitval; 1852137015Sdes } else if (c->ctl_fd == -1) { 1853137015Sdes error("client_input_channel_req: unexpected channel %d", 1854137015Sdes session_ident); 1855137015Sdes } else { 1856137015Sdes atomicio(vwrite, c->ctl_fd, &exitval, sizeof(exitval)); 1857137015Sdes success = 1; 1858137015Sdes } 185992555Sdes packet_check_eom(); 186076259Sgreen } 186176259Sgreen if (reply) { 186276259Sgreen packet_start(success ? 186376259Sgreen SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); 1864192595Sdes packet_put_int(c->remote_id); 186576259Sgreen packet_send(); 186676259Sgreen } 186776259Sgreen xfree(rtype); 186876259Sgreen} 186992555Sdesstatic void 187092555Sdesclient_input_global_request(int type, u_int32_t seq, void *ctxt) 187192555Sdes{ 187292555Sdes char *rtype; 187392555Sdes int want_reply; 187492555Sdes int success = 0; 187576259Sgreen 187692555Sdes rtype = packet_get_string(NULL); 187792555Sdes want_reply = packet_get_char(); 1878126274Sdes debug("client_input_global_request: rtype %s want_reply %d", 1879126274Sdes rtype, want_reply); 188092555Sdes if (want_reply) { 188192555Sdes packet_start(success ? 188292555Sdes SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE); 188392555Sdes packet_send(); 188492555Sdes packet_write_wait(); 188592555Sdes } 188692555Sdes xfree(rtype); 188792555Sdes} 188892555Sdes 1889137015Sdesvoid 1890137015Sdesclient_session2_setup(int id, int want_tty, int want_subsystem, 1891181111Sdes const char *term, struct termios *tiop, int in_fd, Buffer *cmd, char **env) 1892137015Sdes{ 1893137015Sdes int len; 1894146998Sdes Channel *c = NULL; 1895137015Sdes 1896137015Sdes debug2("%s: id %d", __func__, id); 1897137015Sdes 1898146998Sdes if ((c = channel_lookup(id)) == NULL) 1899146998Sdes fatal("client_session2_setup: channel %d: unknown channel", id); 1900146998Sdes 1901137015Sdes if (want_tty) { 1902137015Sdes struct winsize ws; 1903137015Sdes 1904137015Sdes /* Store window size in the packet. */ 1905137015Sdes if (ioctl(in_fd, TIOCGWINSZ, &ws) < 0) 1906137015Sdes memset(&ws, 0, sizeof(ws)); 1907137015Sdes 1908181111Sdes channel_request_start(id, "pty-req", 1); 1909181111Sdes client_expect_confirm(id, "PTY allocation", 0); 1910137015Sdes packet_put_cstring(term != NULL ? term : ""); 1911162852Sdes packet_put_int((u_int)ws.ws_col); 1912162852Sdes packet_put_int((u_int)ws.ws_row); 1913162852Sdes packet_put_int((u_int)ws.ws_xpixel); 1914162852Sdes packet_put_int((u_int)ws.ws_ypixel); 1915181111Sdes if (tiop == NULL) 1916181111Sdes tiop = get_saved_tio(); 1917181111Sdes tty_make_modes(-1, tiop); 1918137015Sdes packet_send(); 1919137015Sdes /* XXX wait for reply */ 1920146998Sdes c->client_tty = 1; 1921137015Sdes } 1922137015Sdes 1923137015Sdes /* Transfer any environment variables from client to server */ 1924137015Sdes if (options.num_send_env != 0 && env != NULL) { 1925137015Sdes int i, j, matched; 1926137015Sdes char *name, *val; 1927137015Sdes 1928137015Sdes debug("Sending environment."); 1929137015Sdes for (i = 0; env[i] != NULL; i++) { 1930137015Sdes /* Split */ 1931137015Sdes name = xstrdup(env[i]); 1932137015Sdes if ((val = strchr(name, '=')) == NULL) { 1933157016Sdes xfree(name); 1934137015Sdes continue; 1935137015Sdes } 1936137015Sdes *val++ = '\0'; 1937137015Sdes 1938137015Sdes matched = 0; 1939137015Sdes for (j = 0; j < options.num_send_env; j++) { 1940137015Sdes if (match_pattern(name, options.send_env[j])) { 1941137015Sdes matched = 1; 1942137015Sdes break; 1943137015Sdes } 1944137015Sdes } 1945137015Sdes if (!matched) { 1946137015Sdes debug3("Ignored env %s", name); 1947157016Sdes xfree(name); 1948137015Sdes continue; 1949137015Sdes } 1950137015Sdes 1951137015Sdes debug("Sending env %s = %s", name, val); 1952137015Sdes channel_request_start(id, "env", 0); 1953137015Sdes packet_put_cstring(name); 1954137015Sdes packet_put_cstring(val); 1955137015Sdes packet_send(); 1956157016Sdes xfree(name); 1957137015Sdes } 1958137015Sdes } 1959137015Sdes 1960137015Sdes len = buffer_len(cmd); 1961137015Sdes if (len > 0) { 1962137015Sdes if (len > 900) 1963137015Sdes len = 900; 1964137015Sdes if (want_subsystem) { 1965181111Sdes debug("Sending subsystem: %.*s", 1966181111Sdes len, (u_char*)buffer_ptr(cmd)); 1967181111Sdes channel_request_start(id, "subsystem", 1); 1968181111Sdes client_expect_confirm(id, "subsystem", 1); 1969137015Sdes } else { 1970181111Sdes debug("Sending command: %.*s", 1971181111Sdes len, (u_char*)buffer_ptr(cmd)); 1972181111Sdes channel_request_start(id, "exec", 1); 1973181111Sdes client_expect_confirm(id, "exec", 1); 1974137015Sdes } 1975137015Sdes packet_put_string(buffer_ptr(cmd), buffer_len(cmd)); 1976137015Sdes packet_send(); 1977137015Sdes } else { 1978181111Sdes channel_request_start(id, "shell", 1); 1979181111Sdes client_expect_confirm(id, "shell", 1); 1980137015Sdes packet_send(); 1981137015Sdes } 1982137015Sdes} 1983137015Sdes 198492555Sdesstatic void 198576259Sgreenclient_init_dispatch_20(void) 198660573Skris{ 198760573Skris dispatch_init(&dispatch_protocol_error); 198898675Sdes 198960573Skris dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); 199060573Skris dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data); 199160573Skris dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); 199260573Skris dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); 199360573Skris dispatch_set(SSH2_MSG_CHANNEL_OPEN, &client_input_channel_open); 199460573Skris dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); 199560573Skris dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); 199676259Sgreen dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &client_input_channel_req); 199760573Skris dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); 1998181111Sdes dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, &channel_input_status_confirm); 1999181111Sdes dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &channel_input_status_confirm); 200092555Sdes dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &client_input_global_request); 200176259Sgreen 200276259Sgreen /* rekeying */ 200376259Sgreen dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit); 200498675Sdes 200598675Sdes /* global request reply messages */ 200698675Sdes dispatch_set(SSH2_MSG_REQUEST_FAILURE, &client_global_request_reply); 200798675Sdes dispatch_set(SSH2_MSG_REQUEST_SUCCESS, &client_global_request_reply); 200860573Skris} 2009181111Sdes 201092555Sdesstatic void 201176259Sgreenclient_init_dispatch_13(void) 201260573Skris{ 201360573Skris dispatch_init(NULL); 201460573Skris dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close); 201560573Skris dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation); 201660573Skris dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data); 201760573Skris dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); 201860573Skris dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); 201960573Skris dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open); 202060573Skris dispatch_set(SSH_SMSG_EXITSTATUS, &client_input_exit_status); 202160573Skris dispatch_set(SSH_SMSG_STDERR_DATA, &client_input_stderr_data); 202260573Skris dispatch_set(SSH_SMSG_STDOUT_DATA, &client_input_stdout_data); 202368700Sgreen 202468700Sgreen dispatch_set(SSH_SMSG_AGENT_OPEN, options.forward_agent ? 2025126274Sdes &client_input_agent_open : &deny_input_open); 202668700Sgreen dispatch_set(SSH_SMSG_X11_OPEN, options.forward_x11 ? 202769587Sgreen &x11_input_open : &deny_input_open); 202860573Skris} 2029181111Sdes 203092555Sdesstatic void 203176259Sgreenclient_init_dispatch_15(void) 203260573Skris{ 203360573Skris client_init_dispatch_13(); 203460573Skris dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof); 203560573Skris dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, & channel_input_oclose); 203660573Skris} 2037181111Sdes 203892555Sdesstatic void 203976259Sgreenclient_init_dispatch(void) 204060573Skris{ 204160573Skris if (compat20) 204260573Skris client_init_dispatch_20(); 204360573Skris else if (compat13) 204460573Skris client_init_dispatch_13(); 204560573Skris else 204660573Skris client_init_dispatch_15(); 204760573Skris} 2048126274Sdes 2049126274Sdes/* client specific fatal cleanup */ 2050126274Sdesvoid 2051126274Sdescleanup_exit(int i) 2052126274Sdes{ 2053126274Sdes leave_raw_mode(); 2054126274Sdes leave_non_blocking(); 2055181111Sdes if (options.control_path != NULL && muxserver_sock != -1) 2056137015Sdes unlink(options.control_path); 2057126274Sdes _exit(i); 2058126274Sdes} 2059