clientloop.c revision 207319
1207319Sdes/* $OpenBSD: clientloop.c,v 1.219 2010/03/13 21:10:38 djm 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 */ 124204917Sdesextern int muxserver_sock; /* XXX use mux_client_cleanup() instead */ 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 133204917Sdes/* Force TTY allocation */ 134204917Sdesextern int force_tty_flag; 135204917Sdes 13657429Smarkm/* 13757429Smarkm * Flag to indicate that we have received a window change signal which has 13857429Smarkm * not yet been processed. This will cause a message indicating the new 13957429Smarkm * window size to be sent to the server a little later. This is volatile 14057429Smarkm * because this is updated in a signal handler. 14157429Smarkm */ 14292555Sdesstatic volatile sig_atomic_t received_window_change_signal = 0; 14392555Sdesstatic volatile sig_atomic_t received_signal = 0; 14457429Smarkm 145157016Sdes/* Flag indicating whether the user's terminal is in non-blocking mode. */ 14657429Smarkmstatic int in_non_blocking_mode = 0; 14757429Smarkm 14857429Smarkm/* Common data for the client loop code. */ 149204917Sdesvolatile sig_atomic_t quit_pending; /* Set non-zero to quit the loop. */ 150181111Sdesstatic int escape_char1; /* Escape character. (proto1 only) */ 151181111Sdesstatic int escape_pending1; /* Last character was an escape (proto1 only) */ 15257429Smarkmstatic int last_was_cr; /* Last character was a newline. */ 153181111Sdesstatic int exit_status; /* Used to store the command exit status. */ 154181111Sdesstatic int stdin_eof; /* EOF has been encountered on stderr. */ 15557429Smarkmstatic Buffer stdin_buffer; /* Buffer for stdin data. */ 15657429Smarkmstatic Buffer stdout_buffer; /* Buffer for stdout data. */ 15757429Smarkmstatic Buffer stderr_buffer; /* Buffer for stderr data. */ 15876259Sgreenstatic u_int buffer_high;/* Soft max buffer size. */ 15957429Smarkmstatic int connection_in; /* Connection to server (input). */ 16057429Smarkmstatic int connection_out; /* Connection to server (output). */ 16176259Sgreenstatic int need_rekeying; /* Set to non-zero if rekeying is requested. */ 16276259Sgreenstatic int session_closed = 0; /* In SSH2: login session closed. */ 16357429Smarkm 16492555Sdesstatic void client_init_dispatch(void); 16560573Skrisint session_ident = -1; 16660573Skris 167204917Sdesint session_resumed = 0; 168204917Sdes 169181111Sdes/* Track escape per proto2 channel */ 170181111Sdesstruct escape_filter_ctx { 171181111Sdes int escape_pending; 172181111Sdes int escape_char; 173137015Sdes}; 174137015Sdes 175181111Sdes/* Context for channel confirmation replies */ 176181111Sdesstruct channel_reply_ctx { 177181111Sdes const char *request_type; 178181111Sdes int id, do_close; 179181111Sdes}; 180181111Sdes 181181111Sdes/* Global request success/failure callbacks */ 182181111Sdesstruct global_confirm { 183181111Sdes TAILQ_ENTRY(global_confirm) entry; 184181111Sdes global_confirm_cb *cb; 185181111Sdes void *ctx; 186181111Sdes int ref_count; 187181111Sdes}; 188181111SdesTAILQ_HEAD(global_confirms, global_confirm); 189181111Sdesstatic struct global_confirms global_confirms = 190181111Sdes TAILQ_HEAD_INITIALIZER(global_confirms); 191181111Sdes 19276259Sgreen/*XXX*/ 19376259Sgreenextern Kex *xxx_kex; 19457429Smarkm 195137015Sdesvoid ssh_process_session2_setup(int, int, int, Buffer *); 196137015Sdes 19757429Smarkm/* Restores stdin to blocking mode. */ 19857429Smarkm 19992555Sdesstatic void 20076259Sgreenleave_non_blocking(void) 20157429Smarkm{ 20257429Smarkm if (in_non_blocking_mode) { 203137015Sdes unset_nonblock(fileno(stdin)); 20457429Smarkm in_non_blocking_mode = 0; 20557429Smarkm } 20657429Smarkm} 20757429Smarkm 20857429Smarkm/* Puts stdin terminal in non-blocking mode. */ 20957429Smarkm 21092555Sdesstatic void 21176259Sgreenenter_non_blocking(void) 21257429Smarkm{ 21357429Smarkm in_non_blocking_mode = 1; 214137015Sdes set_nonblock(fileno(stdin)); 21557429Smarkm} 21657429Smarkm 21757429Smarkm/* 21857429Smarkm * Signal handler for the window change signal (SIGWINCH). This just sets a 21957429Smarkm * flag indicating that the window has changed. 22057429Smarkm */ 221162852Sdes/*ARGSUSED */ 22292555Sdesstatic void 22357429Smarkmwindow_change_handler(int sig) 22457429Smarkm{ 22557429Smarkm received_window_change_signal = 1; 22657429Smarkm signal(SIGWINCH, window_change_handler); 22757429Smarkm} 22857429Smarkm 22957429Smarkm/* 23057429Smarkm * Signal handler for signals that cause the program to terminate. These 23157429Smarkm * signals must be trapped to restore terminal modes. 23257429Smarkm */ 233162852Sdes/*ARGSUSED */ 23492555Sdesstatic void 23557429Smarkmsignal_handler(int sig) 23657429Smarkm{ 23792555Sdes received_signal = sig; 23892555Sdes quit_pending = 1; 23957429Smarkm} 24057429Smarkm 24157429Smarkm/* 24257429Smarkm * Returns current time in seconds from Jan 1, 1970 with the maximum 24357429Smarkm * available resolution. 24457429Smarkm */ 24557429Smarkm 24692555Sdesstatic double 24776259Sgreenget_current_time(void) 24857429Smarkm{ 24957429Smarkm struct timeval tv; 25057429Smarkm gettimeofday(&tv, NULL); 25157429Smarkm return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0; 25257429Smarkm} 25357429Smarkm 254149749Sdes#define SSH_X11_PROTO "MIT-MAGIC-COOKIE-1" 255149749Sdesvoid 256149749Sdesclient_x11_get_proto(const char *display, const char *xauth_path, 257149749Sdes u_int trusted, char **_proto, char **_data) 258149749Sdes{ 259149749Sdes char cmd[1024]; 260149749Sdes char line[512]; 261149749Sdes char xdisplay[512]; 262149749Sdes static char proto[512], data[512]; 263149749Sdes FILE *f; 264149749Sdes int got_data = 0, generated = 0, do_unlink = 0, i; 265149749Sdes char *xauthdir, *xauthfile; 266149749Sdes struct stat st; 267149749Sdes 268149749Sdes xauthdir = xauthfile = NULL; 269149749Sdes *_proto = proto; 270149749Sdes *_data = data; 271149749Sdes proto[0] = data[0] = '\0'; 272149749Sdes 273149749Sdes if (xauth_path == NULL ||(stat(xauth_path, &st) == -1)) { 274149749Sdes debug("No xauth program."); 275149749Sdes } else { 276149749Sdes if (display == NULL) { 277149749Sdes debug("x11_get_proto: DISPLAY not set"); 278149749Sdes return; 279149749Sdes } 280149749Sdes /* 281149749Sdes * Handle FamilyLocal case where $DISPLAY does 282149749Sdes * not match an authorization entry. For this we 283149749Sdes * just try "xauth list unix:displaynum.screennum". 284149749Sdes * XXX: "localhost" match to determine FamilyLocal 285149749Sdes * is not perfect. 286149749Sdes */ 287149749Sdes if (strncmp(display, "localhost:", 10) == 0) { 288149749Sdes snprintf(xdisplay, sizeof(xdisplay), "unix:%s", 289149749Sdes display + 10); 290149749Sdes display = xdisplay; 291149749Sdes } 292149749Sdes if (trusted == 0) { 293149749Sdes xauthdir = xmalloc(MAXPATHLEN); 294149749Sdes xauthfile = xmalloc(MAXPATHLEN); 295149749Sdes strlcpy(xauthdir, "/tmp/ssh-XXXXXXXXXX", MAXPATHLEN); 296149749Sdes if (mkdtemp(xauthdir) != NULL) { 297149749Sdes do_unlink = 1; 298149749Sdes snprintf(xauthfile, MAXPATHLEN, "%s/xauthfile", 299149749Sdes xauthdir); 300149749Sdes snprintf(cmd, sizeof(cmd), 301149749Sdes "%s -f %s generate %s " SSH_X11_PROTO 302149749Sdes " untrusted timeout 1200 2>" _PATH_DEVNULL, 303149749Sdes xauth_path, xauthfile, display); 304149749Sdes debug2("x11_get_proto: %s", cmd); 305149749Sdes if (system(cmd) == 0) 306149749Sdes generated = 1; 307149749Sdes } 308149749Sdes } 309181111Sdes 310181111Sdes /* 311181111Sdes * When in untrusted mode, we read the cookie only if it was 312181111Sdes * successfully generated as an untrusted one in the step 313181111Sdes * above. 314181111Sdes */ 315181111Sdes if (trusted || generated) { 316181111Sdes snprintf(cmd, sizeof(cmd), 317181111Sdes "%s %s%s list %s 2>" _PATH_DEVNULL, 318181111Sdes xauth_path, 319181111Sdes generated ? "-f " : "" , 320181111Sdes generated ? xauthfile : "", 321181111Sdes display); 322181111Sdes debug2("x11_get_proto: %s", cmd); 323181111Sdes f = popen(cmd, "r"); 324181111Sdes if (f && fgets(line, sizeof(line), f) && 325181111Sdes sscanf(line, "%*s %511s %511s", proto, data) == 2) 326181111Sdes got_data = 1; 327181111Sdes if (f) 328181111Sdes pclose(f); 329181111Sdes } else 330181111Sdes error("Warning: untrusted X11 forwarding setup failed: " 331181111Sdes "xauth key data not generated"); 332149749Sdes } 333149749Sdes 334149749Sdes if (do_unlink) { 335149749Sdes unlink(xauthfile); 336149749Sdes rmdir(xauthdir); 337149749Sdes } 338149749Sdes if (xauthdir) 339149749Sdes xfree(xauthdir); 340149749Sdes if (xauthfile) 341149749Sdes xfree(xauthfile); 342149749Sdes 343149749Sdes /* 344149749Sdes * If we didn't get authentication data, just make up some 345149749Sdes * data. The forwarding code will check the validity of the 346149749Sdes * response anyway, and substitute this data. The X11 347149749Sdes * server, however, will ignore this fake data and use 348149749Sdes * whatever authentication mechanisms it was using otherwise 349149749Sdes * for the local connection. 350149749Sdes */ 351149749Sdes if (!got_data) { 352149749Sdes u_int32_t rnd = 0; 353149749Sdes 354149749Sdes logit("Warning: No xauth data; " 355149749Sdes "using fake authentication data for X11 forwarding."); 356149749Sdes strlcpy(proto, SSH_X11_PROTO, sizeof proto); 357149749Sdes for (i = 0; i < 16; i++) { 358149749Sdes if (i % 4 == 0) 359149749Sdes rnd = arc4random(); 360149749Sdes snprintf(data + 2 * i, sizeof data - 2 * i, "%02x", 361149749Sdes rnd & 0xff); 362149749Sdes rnd >>= 8; 363149749Sdes } 364149749Sdes } 365149749Sdes} 366149749Sdes 36757429Smarkm/* 36857429Smarkm * This is called when the interactive is entered. This checks if there is 36957429Smarkm * an EOF coming on stdin. We must check this explicitly, as select() does 37057429Smarkm * not appear to wake up when redirecting from /dev/null. 37157429Smarkm */ 37257429Smarkm 37392555Sdesstatic void 37476259Sgreenclient_check_initial_eof_on_stdin(void) 37557429Smarkm{ 37657429Smarkm int len; 37757429Smarkm char buf[1]; 37857429Smarkm 37957429Smarkm /* 38057429Smarkm * If standard input is to be "redirected from /dev/null", we simply 38157429Smarkm * mark that we have seen an EOF and send an EOF message to the 38257429Smarkm * server. Otherwise, we try to read a single character; it appears 38357429Smarkm * that for some files, such /dev/null, select() never wakes up for 38457429Smarkm * read for this descriptor, which means that we never get EOF. This 38557429Smarkm * way we will get the EOF if stdin comes from /dev/null or similar. 38657429Smarkm */ 38757429Smarkm if (stdin_null_flag) { 38857429Smarkm /* Fake EOF on stdin. */ 38957429Smarkm debug("Sending eof."); 39057429Smarkm stdin_eof = 1; 39157429Smarkm packet_start(SSH_CMSG_EOF); 39257429Smarkm packet_send(); 39357429Smarkm } else { 39457429Smarkm enter_non_blocking(); 39557429Smarkm 39657429Smarkm /* Check for immediate EOF on stdin. */ 39757429Smarkm len = read(fileno(stdin), buf, 1); 39857429Smarkm if (len == 0) { 399181111Sdes /* 400181111Sdes * EOF. Record that we have seen it and send 401181111Sdes * EOF to server. 402181111Sdes */ 40357429Smarkm debug("Sending eof."); 40457429Smarkm stdin_eof = 1; 40557429Smarkm packet_start(SSH_CMSG_EOF); 40657429Smarkm packet_send(); 40757429Smarkm } else if (len > 0) { 40857429Smarkm /* 40957429Smarkm * Got data. We must store the data in the buffer, 41057429Smarkm * and also process it as an escape character if 41157429Smarkm * appropriate. 41257429Smarkm */ 413181111Sdes if ((u_char) buf[0] == escape_char1) 414181111Sdes escape_pending1 = 1; 41576259Sgreen else 41657429Smarkm buffer_append(&stdin_buffer, buf, 1); 41757429Smarkm } 41857429Smarkm leave_non_blocking(); 41957429Smarkm } 42057429Smarkm} 42157429Smarkm 42257429Smarkm 42357429Smarkm/* 42457429Smarkm * Make packets from buffered stdin data, and buffer them for sending to the 42557429Smarkm * connection. 42657429Smarkm */ 42757429Smarkm 42892555Sdesstatic void 42976259Sgreenclient_make_packets_from_stdin_data(void) 43057429Smarkm{ 43176259Sgreen u_int len; 43257429Smarkm 43357429Smarkm /* Send buffered stdin data to the server. */ 43457429Smarkm while (buffer_len(&stdin_buffer) > 0 && 43592555Sdes packet_not_very_much_data_to_write()) { 43657429Smarkm len = buffer_len(&stdin_buffer); 43757429Smarkm /* Keep the packets at reasonable size. */ 43857429Smarkm if (len > packet_get_maxsize()) 43957429Smarkm len = packet_get_maxsize(); 44057429Smarkm packet_start(SSH_CMSG_STDIN_DATA); 44157429Smarkm packet_put_string(buffer_ptr(&stdin_buffer), len); 44257429Smarkm packet_send(); 44357429Smarkm buffer_consume(&stdin_buffer, len); 44457429Smarkm /* If we have a pending EOF, send it now. */ 44557429Smarkm if (stdin_eof && buffer_len(&stdin_buffer) == 0) { 44657429Smarkm packet_start(SSH_CMSG_EOF); 44757429Smarkm packet_send(); 44857429Smarkm } 44957429Smarkm } 45057429Smarkm} 45157429Smarkm 45257429Smarkm/* 45357429Smarkm * Checks if the client window has changed, and sends a packet about it to 45457429Smarkm * the server if so. The actual change is detected elsewhere (by a software 45557429Smarkm * interrupt on Unix); this just checks the flag and sends a message if 45657429Smarkm * appropriate. 45757429Smarkm */ 45857429Smarkm 45992555Sdesstatic void 46076259Sgreenclient_check_window_change(void) 46157429Smarkm{ 46260573Skris struct winsize ws; 46357429Smarkm 46460573Skris if (! received_window_change_signal) 46560573Skris return; 46660573Skris /** XXX race */ 46760573Skris received_window_change_signal = 0; 46857429Smarkm 46969587Sgreen debug2("client_check_window_change: changed"); 47060573Skris 47160573Skris if (compat20) { 472137015Sdes channel_send_window_changes(); 47360573Skris } else { 474137015Sdes if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0) 475137015Sdes return; 47660573Skris packet_start(SSH_CMSG_WINDOW_SIZE); 477162852Sdes packet_put_int((u_int)ws.ws_row); 478162852Sdes packet_put_int((u_int)ws.ws_col); 479162852Sdes packet_put_int((u_int)ws.ws_xpixel); 480162852Sdes packet_put_int((u_int)ws.ws_ypixel); 48160573Skris packet_send(); 48257429Smarkm } 48357429Smarkm} 48457429Smarkm 485126274Sdesstatic void 486126274Sdesclient_global_request_reply(int type, u_int32_t seq, void *ctxt) 487126274Sdes{ 488181111Sdes struct global_confirm *gc; 489181111Sdes 490181111Sdes if ((gc = TAILQ_FIRST(&global_confirms)) == NULL) 491181111Sdes return; 492181111Sdes if (gc->cb != NULL) 493181111Sdes gc->cb(type, seq, gc->ctx); 494181111Sdes if (--gc->ref_count <= 0) { 495181111Sdes TAILQ_REMOVE(&global_confirms, gc, entry); 496181111Sdes bzero(gc, sizeof(*gc)); 497181111Sdes xfree(gc); 498181111Sdes } 499181111Sdes 500197679Sdes packet_set_alive_timeouts(0); 501126274Sdes} 502126274Sdes 503126274Sdesstatic void 504126274Sdesserver_alive_check(void) 505126274Sdes{ 506197679Sdes if (packet_inc_alive_timeouts() > options.server_alive_count_max) { 507164146Sdes logit("Timeout, server not responding."); 508164146Sdes cleanup_exit(255); 509164146Sdes } 510126274Sdes packet_start(SSH2_MSG_GLOBAL_REQUEST); 511126274Sdes packet_put_cstring("keepalive@openssh.com"); 512126274Sdes packet_put_char(1); /* boolean: want reply */ 513126274Sdes packet_send(); 514181111Sdes /* Insert an empty placeholder to maintain ordering */ 515181111Sdes client_register_global_confirm(NULL, NULL); 516126274Sdes} 517126274Sdes 51857429Smarkm/* 51957429Smarkm * Waits until the client can do something (some data becomes available on 52057429Smarkm * one of the file descriptors). 52157429Smarkm */ 52292555Sdesstatic void 52376259Sgreenclient_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, 524137015Sdes int *maxfdp, u_int *nallocp, int rekeying) 52557429Smarkm{ 526126274Sdes struct timeval tv, *tvp; 527126274Sdes int ret; 528126274Sdes 52976259Sgreen /* Add any selections by the channel mechanism. */ 53092555Sdes channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, rekeying); 53157429Smarkm 53260573Skris if (!compat20) { 53360573Skris /* Read from the connection, unless our buffers are full. */ 53460573Skris if (buffer_len(&stdout_buffer) < buffer_high && 53560573Skris buffer_len(&stderr_buffer) < buffer_high && 53660573Skris channel_not_very_much_buffered_data()) 53776259Sgreen FD_SET(connection_in, *readsetp); 53860573Skris /* 53960573Skris * Read from stdin, unless we have seen EOF or have very much 54060573Skris * buffered data to send to the server. 54160573Skris */ 54260573Skris if (!stdin_eof && packet_not_very_much_data_to_write()) 54376259Sgreen FD_SET(fileno(stdin), *readsetp); 54460573Skris 54560573Skris /* Select stdout/stderr if have data in buffer. */ 54660573Skris if (buffer_len(&stdout_buffer) > 0) 54776259Sgreen FD_SET(fileno(stdout), *writesetp); 54860573Skris if (buffer_len(&stderr_buffer) > 0) 54976259Sgreen FD_SET(fileno(stderr), *writesetp); 55060573Skris } else { 55192555Sdes /* channel_prepare_select could have closed the last channel */ 55292555Sdes if (session_closed && !channel_still_open() && 55392555Sdes !packet_have_data_to_write()) { 55492555Sdes /* clear mask since we did not call select() */ 55592555Sdes memset(*readsetp, 0, *nallocp); 55692555Sdes memset(*writesetp, 0, *nallocp); 55792555Sdes return; 55892555Sdes } else { 55992555Sdes FD_SET(connection_in, *readsetp); 56092555Sdes } 56160573Skris } 56257429Smarkm 56357429Smarkm /* Select server connection if have data to write to the server. */ 56457429Smarkm if (packet_have_data_to_write()) 56576259Sgreen FD_SET(connection_out, *writesetp); 56657429Smarkm 56757429Smarkm /* 56857429Smarkm * Wait for something to happen. This will suspend the process until 56957429Smarkm * some selected descriptor can be read, written, or has some other 570126274Sdes * event pending. 57157429Smarkm */ 57257429Smarkm 573126274Sdes if (options.server_alive_interval == 0 || !compat20) 574126274Sdes tvp = NULL; 575137015Sdes else { 576126274Sdes tv.tv_sec = options.server_alive_interval; 577126274Sdes tv.tv_usec = 0; 578126274Sdes tvp = &tv; 579126274Sdes } 580126274Sdes ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp); 581126274Sdes if (ret < 0) { 58257429Smarkm char buf[100]; 58376259Sgreen 58476259Sgreen /* 58576259Sgreen * We have to clear the select masks, because we return. 58676259Sgreen * We have to return, because the mainloop checks for the flags 58776259Sgreen * set by the signal handlers. 58876259Sgreen */ 58992555Sdes memset(*readsetp, 0, *nallocp); 59092555Sdes memset(*writesetp, 0, *nallocp); 59176259Sgreen 59257429Smarkm if (errno == EINTR) 59357429Smarkm return; 59457429Smarkm /* Note: we might still have data in the buffers. */ 59557429Smarkm snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno)); 59657429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 59757429Smarkm quit_pending = 1; 598126274Sdes } else if (ret == 0) 599126274Sdes server_alive_check(); 60057429Smarkm} 60157429Smarkm 60292555Sdesstatic void 60365668Skrisclient_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr) 60457429Smarkm{ 60557429Smarkm /* Flush stdout and stderr buffers. */ 60665668Skris if (buffer_len(bout) > 0) 607181111Sdes atomicio(vwrite, fileno(stdout), buffer_ptr(bout), 608181111Sdes buffer_len(bout)); 60965668Skris if (buffer_len(berr) > 0) 610181111Sdes atomicio(vwrite, fileno(stderr), buffer_ptr(berr), 611181111Sdes buffer_len(berr)); 61257429Smarkm 613204917Sdes leave_raw_mode(force_tty_flag); 61457429Smarkm 61557429Smarkm /* 61657429Smarkm * Free (and clear) the buffer to reduce the amount of data that gets 61757429Smarkm * written to swap. 61857429Smarkm */ 61965668Skris buffer_free(bin); 62065668Skris buffer_free(bout); 62165668Skris buffer_free(berr); 62257429Smarkm 62357429Smarkm /* Send the suspend signal to the program itself. */ 62457429Smarkm kill(getpid(), SIGTSTP); 62557429Smarkm 626146998Sdes /* Reset window sizes in case they have changed */ 627146998Sdes received_window_change_signal = 1; 62857429Smarkm 62957429Smarkm /* OK, we have been continued by the user. Reinitialize buffers. */ 63065668Skris buffer_init(bin); 63165668Skris buffer_init(bout); 63265668Skris buffer_init(berr); 63357429Smarkm 634204917Sdes enter_raw_mode(force_tty_flag); 63557429Smarkm} 63657429Smarkm 63792555Sdesstatic void 638162852Sdesclient_process_net_input(fd_set *readset) 63957429Smarkm{ 640197679Sdes int len, cont = 0; 641197679Sdes char buf[SSH_IOBUFSZ]; 64257429Smarkm 64357429Smarkm /* 64457429Smarkm * Read input from the server, and add any such data to the buffer of 64557429Smarkm * the packet subsystem. 64657429Smarkm */ 64757429Smarkm if (FD_ISSET(connection_in, readset)) { 64857429Smarkm /* Read as much as possible. */ 649197679Sdes len = roaming_read(connection_in, buf, sizeof(buf), &cont); 650197679Sdes if (len == 0 && cont == 0) { 651181111Sdes /* 652181111Sdes * Received EOF. The remote host has closed the 653181111Sdes * connection. 654181111Sdes */ 655181111Sdes snprintf(buf, sizeof buf, 656181111Sdes "Connection to %.300s closed by remote host.\r\n", 657181111Sdes host); 65857429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 65957429Smarkm quit_pending = 1; 66057429Smarkm return; 66157429Smarkm } 66257429Smarkm /* 66357429Smarkm * There is a kernel bug on Solaris that causes select to 66457429Smarkm * sometimes wake up even though there is no data available. 66557429Smarkm */ 666181111Sdes if (len < 0 && 667181111Sdes (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)) 66857429Smarkm len = 0; 66957429Smarkm 67057429Smarkm if (len < 0) { 671181111Sdes /* 672181111Sdes * An error has encountered. Perhaps there is a 673181111Sdes * network problem. 674181111Sdes */ 675181111Sdes snprintf(buf, sizeof buf, 676181111Sdes "Read from remote host %.300s: %.100s\r\n", 677181111Sdes host, strerror(errno)); 67857429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 67957429Smarkm quit_pending = 1; 68057429Smarkm return; 68157429Smarkm } 68257429Smarkm packet_process_incoming(buf, len); 68357429Smarkm } 68460573Skris} 68560573Skris 68698675Sdesstatic void 687181111Sdesclient_status_confirm(int type, Channel *c, void *ctx) 688137015Sdes{ 689181111Sdes struct channel_reply_ctx *cr = (struct channel_reply_ctx *)ctx; 690181111Sdes char errmsg[256]; 691181111Sdes int tochan; 692137015Sdes 693181111Sdes /* XXX supress on mux _client_ quietmode */ 694181111Sdes tochan = options.log_level >= SYSLOG_LEVEL_ERROR && 695204917Sdes c->ctl_chan != -1 && c->extended_usage == CHAN_EXTENDED_WRITE; 696137015Sdes 697181111Sdes if (type == SSH2_MSG_CHANNEL_SUCCESS) { 698181111Sdes debug2("%s request accepted on channel %d", 699181111Sdes cr->request_type, c->self); 700181111Sdes } else if (type == SSH2_MSG_CHANNEL_FAILURE) { 701181111Sdes if (tochan) { 702181111Sdes snprintf(errmsg, sizeof(errmsg), 703181111Sdes "%s request failed\r\n", cr->request_type); 704181111Sdes } else { 705181111Sdes snprintf(errmsg, sizeof(errmsg), 706181111Sdes "%s request failed on channel %d", 707181111Sdes cr->request_type, c->self); 708181111Sdes } 709181111Sdes /* If error occurred on primary session channel, then exit */ 710181111Sdes if (cr->do_close && c->self == session_ident) 711181111Sdes fatal("%s", errmsg); 712181111Sdes /* If error occurred on mux client, append to their stderr */ 713181111Sdes if (tochan) 714181111Sdes buffer_append(&c->extended, errmsg, strlen(errmsg)); 715181111Sdes else 716181111Sdes error("%s", errmsg); 717181111Sdes if (cr->do_close) { 718181111Sdes chan_read_failed(c); 719181111Sdes chan_write_failed(c); 720181111Sdes } 721137015Sdes } 722181111Sdes xfree(cr); 723137015Sdes} 724137015Sdes 725137015Sdesstatic void 726181111Sdesclient_abandon_status_confirm(Channel *c, void *ctx) 727137015Sdes{ 728181111Sdes xfree(ctx); 729137015Sdes} 730137015Sdes 731137015Sdesstatic void 732181111Sdesclient_expect_confirm(int id, const char *request, int do_close) 733137015Sdes{ 734181111Sdes struct channel_reply_ctx *cr = xmalloc(sizeof(*cr)); 735137015Sdes 736181111Sdes cr->request_type = request; 737181111Sdes cr->do_close = do_close; 738137015Sdes 739181111Sdes channel_register_status_confirm(id, client_status_confirm, 740181111Sdes client_abandon_status_confirm, cr); 741181111Sdes} 742137015Sdes 743181111Sdesvoid 744181111Sdesclient_register_global_confirm(global_confirm_cb *cb, void *ctx) 745181111Sdes{ 746181111Sdes struct global_confirm *gc, *last_gc; 747137015Sdes 748181111Sdes /* Coalesce identical callbacks */ 749181111Sdes last_gc = TAILQ_LAST(&global_confirms, global_confirms); 750181111Sdes if (last_gc && last_gc->cb == cb && last_gc->ctx == ctx) { 751181111Sdes if (++last_gc->ref_count >= INT_MAX) 752181111Sdes fatal("%s: last_gc->ref_count = %d", 753181111Sdes __func__, last_gc->ref_count); 754146998Sdes return; 755146998Sdes } 756146998Sdes 757181111Sdes gc = xmalloc(sizeof(*gc)); 758181111Sdes gc->cb = cb; 759181111Sdes gc->ctx = ctx; 760181111Sdes gc->ref_count = 1; 761181111Sdes TAILQ_INSERT_TAIL(&global_confirms, gc, entry); 762137015Sdes} 763137015Sdes 764137015Sdesstatic void 76598675Sdesprocess_cmdline(void) 76698675Sdes{ 76798675Sdes void (*handler)(int); 768146998Sdes char *s, *cmd, *cancel_host; 769137015Sdes int delete = 0; 770192595Sdes int local = 0, remote = 0, dynamic = 0; 771192595Sdes int cancel_port; 772146998Sdes Forward fwd; 77398675Sdes 774181111Sdes bzero(&fwd, sizeof(fwd)); 775181111Sdes fwd.listen_host = fwd.connect_host = NULL; 776181111Sdes 777204917Sdes leave_raw_mode(force_tty_flag); 77898675Sdes handler = signal(SIGINT, SIG_IGN); 77998675Sdes cmd = s = read_passphrase("\r\nssh> ", RP_ECHO); 78098675Sdes if (s == NULL) 78198675Sdes goto out; 782181111Sdes while (isspace(*s)) 78398675Sdes s++; 784137015Sdes if (*s == '-') 785137015Sdes s++; /* Skip cmdline '-', if any */ 786137015Sdes if (*s == '\0') 78798675Sdes goto out; 788137015Sdes 789137015Sdes if (*s == 'h' || *s == 'H' || *s == '?') { 790137015Sdes logit("Commands:"); 791162852Sdes logit(" -L[bind_address:]port:host:hostport " 792162852Sdes "Request local forward"); 793162852Sdes logit(" -R[bind_address:]port:host:hostport " 794162852Sdes "Request remote forward"); 795192595Sdes logit(" -D[bind_address:]port " 796192595Sdes "Request dynamic forward"); 797162852Sdes logit(" -KR[bind_address:]port " 798162852Sdes "Cancel remote forward"); 799157016Sdes if (!options.permit_local_command) 800157016Sdes goto out; 801162852Sdes logit(" !args " 802162852Sdes "Execute local command"); 803137015Sdes goto out; 804137015Sdes } 805137015Sdes 806157016Sdes if (*s == '!' && options.permit_local_command) { 807157016Sdes s++; 808157016Sdes ssh_local_cmd(s); 809157016Sdes goto out; 810157016Sdes } 811157016Sdes 812137015Sdes if (*s == 'K') { 813137015Sdes delete = 1; 814137015Sdes s++; 815137015Sdes } 816192595Sdes if (*s == 'L') 817192595Sdes local = 1; 818192595Sdes else if (*s == 'R') 819192595Sdes remote = 1; 820192595Sdes else if (*s == 'D') 821192595Sdes dynamic = 1; 822192595Sdes else { 823124208Sdes logit("Invalid command."); 82498675Sdes goto out; 82598675Sdes } 826192595Sdes 827192595Sdes if ((local || dynamic) && delete) { 828137015Sdes logit("Not supported."); 829137015Sdes goto out; 830137015Sdes } 831192595Sdes if (remote && delete && !compat20) { 832124208Sdes logit("Not supported for SSH protocol version 1."); 83398675Sdes goto out; 83498675Sdes } 835137015Sdes 836181111Sdes while (isspace(*++s)) 837181111Sdes ; 83898675Sdes 839204917Sdes /* XXX update list of forwards in options */ 840137015Sdes if (delete) { 841146998Sdes cancel_port = 0; 842146998Sdes cancel_host = hpdelim(&s); /* may be NULL */ 843146998Sdes if (s != NULL) { 844146998Sdes cancel_port = a2port(s); 845146998Sdes cancel_host = cleanhostname(cancel_host); 846146998Sdes } else { 847146998Sdes cancel_port = a2port(cancel_host); 848146998Sdes cancel_host = NULL; 84998675Sdes } 850192595Sdes if (cancel_port <= 0) { 851146998Sdes logit("Bad forwarding close port"); 852137015Sdes goto out; 853137015Sdes } 854146998Sdes channel_request_rforward_cancel(cancel_host, cancel_port); 855137015Sdes } else { 856192595Sdes if (!parse_forward(&fwd, s, dynamic, remote)) { 857137015Sdes logit("Bad forwarding specification."); 858137015Sdes goto out; 859137015Sdes } 860192595Sdes if (local || dynamic) { 861146998Sdes if (channel_setup_local_fwd_listener(fwd.listen_host, 862146998Sdes fwd.listen_port, fwd.connect_host, 863146998Sdes fwd.connect_port, options.gateway_ports) < 0) { 864137015Sdes logit("Port forwarding failed."); 865137015Sdes goto out; 866137015Sdes } 867146998Sdes } else { 868162852Sdes if (channel_request_remote_forwarding(fwd.listen_host, 869146998Sdes fwd.listen_port, fwd.connect_host, 870162852Sdes fwd.connect_port) < 0) { 871162852Sdes logit("Port forwarding failed."); 872162852Sdes goto out; 873162852Sdes } 874146998Sdes } 875146998Sdes 876137015Sdes logit("Forwarding port."); 877137015Sdes } 878137015Sdes 87998675Sdesout: 88098675Sdes signal(SIGINT, handler); 881204917Sdes enter_raw_mode(force_tty_flag); 88298675Sdes if (cmd) 88398675Sdes xfree(cmd); 884181111Sdes if (fwd.listen_host != NULL) 885181111Sdes xfree(fwd.listen_host); 886181111Sdes if (fwd.connect_host != NULL) 887181111Sdes xfree(fwd.connect_host); 88898675Sdes} 88998675Sdes 890181111Sdes/* 891181111Sdes * Process the characters one by one, call with c==NULL for proto1 case. 892181111Sdes */ 89392555Sdesstatic int 894181111Sdesprocess_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr, 895181111Sdes char *buf, int len) 89665668Skris{ 89765668Skris char string[1024]; 89865668Skris pid_t pid; 89965668Skris int bytes = 0; 90076259Sgreen u_int i; 90176259Sgreen u_char ch; 90265668Skris char *s; 903181111Sdes int *escape_pendingp, escape_char; 904181111Sdes struct escape_filter_ctx *efc; 90565668Skris 906181111Sdes if (c == NULL) { 907181111Sdes escape_pendingp = &escape_pending1; 908181111Sdes escape_char = escape_char1; 909181111Sdes } else { 910181111Sdes if (c->filter_ctx == NULL) 911181111Sdes return 0; 912181111Sdes efc = (struct escape_filter_ctx *)c->filter_ctx; 913181111Sdes escape_pendingp = &efc->escape_pending; 914181111Sdes escape_char = efc->escape_char; 915181111Sdes } 916181111Sdes 917149749Sdes if (len <= 0) 918149749Sdes return (0); 919149749Sdes 920149749Sdes for (i = 0; i < (u_int)len; i++) { 92165668Skris /* Get one character at a time. */ 92265668Skris ch = buf[i]; 92365668Skris 924181111Sdes if (*escape_pendingp) { 92565668Skris /* We have previously seen an escape character. */ 92665668Skris /* Clear the flag now. */ 927181111Sdes *escape_pendingp = 0; 92865668Skris 92965668Skris /* Process the escaped character. */ 93065668Skris switch (ch) { 93165668Skris case '.': 93265668Skris /* Terminate the connection. */ 933181111Sdes snprintf(string, sizeof string, "%c.\r\n", 934181111Sdes escape_char); 93565668Skris buffer_append(berr, string, strlen(string)); 93665668Skris 937204917Sdes if (c && c->ctl_chan != -1) { 938181111Sdes chan_read_failed(c); 939181111Sdes chan_write_failed(c); 940181111Sdes return 0; 941181111Sdes } else 942181111Sdes quit_pending = 1; 94365668Skris return -1; 94465668Skris 94565668Skris case 'Z' - 64: 946181111Sdes /* XXX support this for mux clients */ 947204917Sdes if (c && c->ctl_chan != -1) { 948181111Sdes noescape: 949181111Sdes snprintf(string, sizeof string, 950181111Sdes "%c%c escape not available to " 951181111Sdes "multiplexed sessions\r\n", 952181111Sdes escape_char, ch); 953181111Sdes buffer_append(berr, string, 954181111Sdes strlen(string)); 955181111Sdes continue; 956181111Sdes } 957181111Sdes /* Suspend the program. Inform the user */ 958181111Sdes snprintf(string, sizeof string, 959181111Sdes "%c^Z [suspend ssh]\r\n", escape_char); 96065668Skris buffer_append(berr, string, strlen(string)); 96165668Skris 96265668Skris /* Restore terminal modes and suspend. */ 96365668Skris client_suspend_self(bin, bout, berr); 96465668Skris 96565668Skris /* We have been continued. */ 96665668Skris continue; 96765668Skris 968124208Sdes case 'B': 969124208Sdes if (compat20) { 970124208Sdes snprintf(string, sizeof string, 971124208Sdes "%cB\r\n", escape_char); 972124208Sdes buffer_append(berr, string, 973124208Sdes strlen(string)); 974124208Sdes channel_request_start(session_ident, 975124208Sdes "break", 0); 976124208Sdes packet_put_int(1000); 977124208Sdes packet_send(); 978124208Sdes } 979124208Sdes continue; 980124208Sdes 98176259Sgreen case 'R': 98276259Sgreen if (compat20) { 98376259Sgreen if (datafellows & SSH_BUG_NOREKEY) 984181111Sdes logit("Server does not " 985181111Sdes "support re-keying"); 98676259Sgreen else 98776259Sgreen need_rekeying = 1; 98876259Sgreen } 98976259Sgreen continue; 99076259Sgreen 99165668Skris case '&': 992204917Sdes if (c && c->ctl_chan != -1) 993181111Sdes goto noescape; 99465668Skris /* 995181111Sdes * Detach the program (continue to serve 996181111Sdes * connections, but put in background and no 997181111Sdes * more new connections). 99865668Skris */ 99965668Skris /* Restore tty modes. */ 1000204917Sdes leave_raw_mode(force_tty_flag); 100165668Skris 100265668Skris /* Stop listening for new connections. */ 100365668Skris channel_stop_listening(); 100465668Skris 100592555Sdes snprintf(string, sizeof string, 100692555Sdes "%c& [backgrounded]\n", escape_char); 100792555Sdes buffer_append(berr, string, strlen(string)); 100865668Skris 100965668Skris /* Fork into background. */ 101065668Skris pid = fork(); 101165668Skris if (pid < 0) { 101265668Skris error("fork: %.100s", strerror(errno)); 101365668Skris continue; 101465668Skris } 101565668Skris if (pid != 0) { /* This is the parent. */ 101665668Skris /* The parent just exits. */ 101765668Skris exit(0); 101865668Skris } 101965668Skris /* The child continues serving connections. */ 102092555Sdes if (compat20) { 102192555Sdes buffer_append(bin, "\004", 1); 102292555Sdes /* fake EOF on stdin */ 102392555Sdes return -1; 102492555Sdes } else if (!stdin_eof) { 102592555Sdes /* 1026181111Sdes * Sending SSH_CMSG_EOF alone does not 1027181111Sdes * always appear to be enough. So we 1028181111Sdes * try to send an EOF character first. 102992555Sdes */ 103092555Sdes packet_start(SSH_CMSG_STDIN_DATA); 103192555Sdes packet_put_string("\004", 1); 103292555Sdes packet_send(); 103392555Sdes /* Close stdin. */ 103492555Sdes stdin_eof = 1; 103592555Sdes if (buffer_len(bin) == 0) { 103692555Sdes packet_start(SSH_CMSG_EOF); 103792555Sdes packet_send(); 103892555Sdes } 103992555Sdes } 104092555Sdes continue; 104165668Skris 104265668Skris case '?': 1043204917Sdes if (c && c->ctl_chan != -1) { 1044181111Sdes snprintf(string, sizeof string, 104565668Skris"%c?\r\n\ 104665668SkrisSupported escape sequences:\r\n\ 1047181111Sdes %c. - terminate session\r\n\ 1048181111Sdes %cB - send a BREAK to the remote system\r\n\ 1049181111Sdes %cR - Request rekey (SSH protocol 2 only)\r\n\ 1050181111Sdes %c# - list forwarded connections\r\n\ 1051181111Sdes %c? - this message\r\n\ 1052181111Sdes %c%c - send the escape character by typing it twice\r\n\ 105365668Skris(Note that escapes are only recognized immediately after newline.)\r\n", 1054181111Sdes escape_char, escape_char, 1055181111Sdes escape_char, escape_char, 1056181111Sdes escape_char, escape_char, 1057192595Sdes escape_char, escape_char); 1058181111Sdes } else { 1059181111Sdes snprintf(string, sizeof string, 1060181111Sdes"%c?\r\n\ 1061181111SdesSupported escape sequences:\r\n\ 1062181111Sdes %c. - terminate connection (and any multiplexed sessions)\r\n\ 1063181111Sdes %cB - send a BREAK to the remote system\r\n\ 1064181111Sdes %cC - open a command line\r\n\ 1065181111Sdes %cR - Request rekey (SSH protocol 2 only)\r\n\ 1066181111Sdes %c^Z - suspend ssh\r\n\ 1067181111Sdes %c# - list forwarded connections\r\n\ 1068181111Sdes %c& - background ssh (when waiting for connections to terminate)\r\n\ 1069181111Sdes %c? - this message\r\n\ 1070181111Sdes %c%c - send the escape character by typing it twice\r\n\ 1071181111Sdes(Note that escapes are only recognized immediately after newline.)\r\n", 1072181111Sdes escape_char, escape_char, 1073181111Sdes escape_char, escape_char, 1074181111Sdes escape_char, escape_char, 1075181111Sdes escape_char, escape_char, 1076181111Sdes escape_char, escape_char, 1077181111Sdes escape_char); 1078181111Sdes } 107965668Skris buffer_append(berr, string, strlen(string)); 108065668Skris continue; 108165668Skris 108265668Skris case '#': 1083181111Sdes snprintf(string, sizeof string, "%c#\r\n", 1084181111Sdes escape_char); 108565668Skris buffer_append(berr, string, strlen(string)); 108665668Skris s = channel_open_message(); 108765668Skris buffer_append(berr, s, strlen(s)); 108865668Skris xfree(s); 108965668Skris continue; 109065668Skris 109198675Sdes case 'C': 1092204917Sdes if (c && c->ctl_chan != -1) 1093192595Sdes goto noescape; 109498675Sdes process_cmdline(); 109598675Sdes continue; 109698675Sdes 109765668Skris default: 109865668Skris if (ch != escape_char) { 109965668Skris buffer_put_char(bin, escape_char); 110065668Skris bytes++; 110165668Skris } 110265668Skris /* Escaped characters fall through here */ 110365668Skris break; 110465668Skris } 110565668Skris } else { 110665668Skris /* 1107181111Sdes * The previous character was not an escape char. 1108181111Sdes * Check if this is an escape. 110965668Skris */ 111065668Skris if (last_was_cr && ch == escape_char) { 1111181111Sdes /* 1112181111Sdes * It is. Set the flag and continue to 1113181111Sdes * next character. 1114181111Sdes */ 1115181111Sdes *escape_pendingp = 1; 111665668Skris continue; 111765668Skris } 111865668Skris } 111965668Skris 112065668Skris /* 112165668Skris * Normal character. Record whether it was a newline, 112265668Skris * and append it to the buffer. 112365668Skris */ 112465668Skris last_was_cr = (ch == '\r' || ch == '\n'); 112565668Skris buffer_put_char(bin, ch); 112665668Skris bytes++; 112765668Skris } 112865668Skris return bytes; 112965668Skris} 113065668Skris 113192555Sdesstatic void 1132162852Sdesclient_process_input(fd_set *readset) 113360573Skris{ 113460573Skris int len; 1135197679Sdes char buf[SSH_IOBUFSZ]; 113660573Skris 113757429Smarkm /* Read input from stdin. */ 113857429Smarkm if (FD_ISSET(fileno(stdin), readset)) { 113957429Smarkm /* Read as much as possible. */ 114057429Smarkm len = read(fileno(stdin), buf, sizeof(buf)); 1141181111Sdes if (len < 0 && 1142181111Sdes (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)) 114376259Sgreen return; /* we'll try again later */ 114457429Smarkm if (len <= 0) { 114557429Smarkm /* 114657429Smarkm * Received EOF or error. They are treated 114757429Smarkm * similarly, except that an error message is printed 114857429Smarkm * if it was an error condition. 114957429Smarkm */ 115057429Smarkm if (len < 0) { 1151181111Sdes snprintf(buf, sizeof buf, "read: %.100s\r\n", 1152181111Sdes strerror(errno)); 115357429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 115457429Smarkm } 115557429Smarkm /* Mark that we have seen EOF. */ 115657429Smarkm stdin_eof = 1; 115757429Smarkm /* 115857429Smarkm * Send an EOF message to the server unless there is 115957429Smarkm * data in the buffer. If there is data in the 116057429Smarkm * buffer, no message will be sent now. Code 116157429Smarkm * elsewhere will send the EOF when the buffer 116257429Smarkm * becomes empty if stdin_eof is set. 116357429Smarkm */ 116457429Smarkm if (buffer_len(&stdin_buffer) == 0) { 116557429Smarkm packet_start(SSH_CMSG_EOF); 116657429Smarkm packet_send(); 116757429Smarkm } 1168181111Sdes } else if (escape_char1 == SSH_ESCAPECHAR_NONE) { 116957429Smarkm /* 117057429Smarkm * Normal successful read, and no escape character. 117157429Smarkm * Just append the data to buffer. 117257429Smarkm */ 117357429Smarkm buffer_append(&stdin_buffer, buf, len); 117457429Smarkm } else { 117557429Smarkm /* 1176181111Sdes * Normal, successful read. But we have an escape 1177181111Sdes * character and have to process the characters one 1178181111Sdes * by one. 117957429Smarkm */ 1180181111Sdes if (process_escapes(NULL, &stdin_buffer, 1181181111Sdes &stdout_buffer, &stderr_buffer, buf, len) == -1) 118265668Skris return; 118357429Smarkm } 118457429Smarkm } 118557429Smarkm} 118657429Smarkm 118792555Sdesstatic void 1188162852Sdesclient_process_output(fd_set *writeset) 118957429Smarkm{ 119057429Smarkm int len; 119157429Smarkm char buf[100]; 119257429Smarkm 119357429Smarkm /* Write buffered output to stdout. */ 119457429Smarkm if (FD_ISSET(fileno(stdout), writeset)) { 119557429Smarkm /* Write as much data as possible. */ 119657429Smarkm len = write(fileno(stdout), buffer_ptr(&stdout_buffer), 119757429Smarkm buffer_len(&stdout_buffer)); 119857429Smarkm if (len <= 0) { 1199181111Sdes if (errno == EINTR || errno == EAGAIN || 1200181111Sdes errno == EWOULDBLOCK) 120157429Smarkm len = 0; 120257429Smarkm else { 120357429Smarkm /* 120457429Smarkm * An error or EOF was encountered. Put an 120557429Smarkm * error message to stderr buffer. 120657429Smarkm */ 1207181111Sdes snprintf(buf, sizeof buf, 1208181111Sdes "write stdout: %.50s\r\n", strerror(errno)); 120957429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 121057429Smarkm quit_pending = 1; 121157429Smarkm return; 121257429Smarkm } 121357429Smarkm } 121457429Smarkm /* Consume printed data from the buffer. */ 121557429Smarkm buffer_consume(&stdout_buffer, len); 121657429Smarkm } 121757429Smarkm /* Write buffered output to stderr. */ 121857429Smarkm if (FD_ISSET(fileno(stderr), writeset)) { 121957429Smarkm /* Write as much data as possible. */ 122057429Smarkm len = write(fileno(stderr), buffer_ptr(&stderr_buffer), 122157429Smarkm buffer_len(&stderr_buffer)); 122257429Smarkm if (len <= 0) { 1223181111Sdes if (errno == EINTR || errno == EAGAIN || 1224181111Sdes errno == EWOULDBLOCK) 122557429Smarkm len = 0; 122657429Smarkm else { 1227181111Sdes /* 1228181111Sdes * EOF or error, but can't even print 1229181111Sdes * error message. 1230181111Sdes */ 123157429Smarkm quit_pending = 1; 123257429Smarkm return; 123357429Smarkm } 123457429Smarkm } 123557429Smarkm /* Consume printed characters from the buffer. */ 123657429Smarkm buffer_consume(&stderr_buffer, len); 123757429Smarkm } 123857429Smarkm} 123957429Smarkm 124057429Smarkm/* 124160573Skris * Get packets from the connection input buffer, and process them as long as 124260573Skris * there are packets available. 124360573Skris * 124460573Skris * Any unknown packets received during the actual 124560573Skris * session cause the session to terminate. This is 124660573Skris * intended to make debugging easier since no 124760573Skris * confirmations are sent. Any compatible protocol 124860573Skris * extensions must be negotiated during the 124960573Skris * preparatory phase. 125060573Skris */ 125160573Skris 125292555Sdesstatic void 125376259Sgreenclient_process_buffered_input_packets(void) 125460573Skris{ 1255181111Sdes dispatch_run(DISPATCH_NONBLOCK, &quit_pending, 1256181111Sdes compat20 ? xxx_kex : NULL); 125760573Skris} 125860573Skris 125965668Skris/* scan buf[] for '~' before sending data to the peer */ 126065668Skris 1261181111Sdes/* Helper: allocate a new escape_filter_ctx and fill in its escape char */ 1262181111Sdesvoid * 1263181111Sdesclient_new_escape_filter_ctx(int escape_char) 126465668Skris{ 1265181111Sdes struct escape_filter_ctx *ret; 1266181111Sdes 1267181111Sdes ret = xmalloc(sizeof(*ret)); 1268181111Sdes ret->escape_pending = 0; 1269181111Sdes ret->escape_char = escape_char; 1270181111Sdes return (void *)ret; 127165668Skris} 127265668Skris 1273181111Sdes/* Free the escape filter context on channel free */ 1274181111Sdesvoid 1275181111Sdesclient_filter_cleanup(int cid, void *ctx) 1276181111Sdes{ 1277181111Sdes xfree(ctx); 1278181111Sdes} 1279181111Sdes 1280181111Sdesint 1281181111Sdesclient_simple_escape_filter(Channel *c, char *buf, int len) 1282181111Sdes{ 1283181111Sdes if (c->extended_usage != CHAN_EXTENDED_WRITE) 1284181111Sdes return 0; 1285181111Sdes 1286181111Sdes return process_escapes(c, &c->input, &c->output, &c->extended, 1287181111Sdes buf, len); 1288181111Sdes} 1289181111Sdes 129092555Sdesstatic void 129176259Sgreenclient_channel_closed(int id, void *arg) 129276259Sgreen{ 129392555Sdes channel_cancel_cleanup(id); 129476259Sgreen session_closed = 1; 1295204917Sdes leave_raw_mode(force_tty_flag); 129676259Sgreen} 129776259Sgreen 129860573Skris/* 129957429Smarkm * Implements the interactive session with the server. This is called after 130057429Smarkm * the user has been authenticated, and a command has been started on the 130192555Sdes * remote host. If escape_char != SSH_ESCAPECHAR_NONE, it is the character 130292555Sdes * used as an escape character for terminating or suspending the session. 130357429Smarkm */ 130457429Smarkm 130560573Skrisint 130665668Skrisclient_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) 130757429Smarkm{ 130876259Sgreen fd_set *readset = NULL, *writeset = NULL; 130957429Smarkm double start_time, total_time; 1310137015Sdes int max_fd = 0, max_fd2 = 0, len, rekeying = 0; 1311181111Sdes u_int64_t ibytes, obytes; 1312137015Sdes u_int nalloc = 0; 131357429Smarkm char buf[100]; 131457429Smarkm 131557429Smarkm debug("Entering interactive session."); 131657429Smarkm 131757429Smarkm start_time = get_current_time(); 131857429Smarkm 131957429Smarkm /* Initialize variables. */ 1320181111Sdes escape_pending1 = 0; 132157429Smarkm last_was_cr = 1; 132257429Smarkm exit_status = -1; 132357429Smarkm stdin_eof = 0; 132457429Smarkm buffer_high = 64 * 1024; 132557429Smarkm connection_in = packet_get_connection_in(); 132657429Smarkm connection_out = packet_get_connection_out(); 132776259Sgreen max_fd = MAX(connection_in, connection_out); 132876259Sgreen 132976259Sgreen if (!compat20) { 133076259Sgreen /* enable nonblocking unless tty */ 133176259Sgreen if (!isatty(fileno(stdin))) 133276259Sgreen set_nonblock(fileno(stdin)); 133376259Sgreen if (!isatty(fileno(stdout))) 133476259Sgreen set_nonblock(fileno(stdout)); 133576259Sgreen if (!isatty(fileno(stderr))) 133676259Sgreen set_nonblock(fileno(stderr)); 133776259Sgreen max_fd = MAX(max_fd, fileno(stdin)); 133876259Sgreen max_fd = MAX(max_fd, fileno(stdout)); 133976259Sgreen max_fd = MAX(max_fd, fileno(stderr)); 134076259Sgreen } 134157429Smarkm quit_pending = 0; 1342181111Sdes escape_char1 = escape_char_arg; 134357429Smarkm 134457429Smarkm /* Initialize buffers. */ 134557429Smarkm buffer_init(&stdin_buffer); 134657429Smarkm buffer_init(&stdout_buffer); 134757429Smarkm buffer_init(&stderr_buffer); 134857429Smarkm 134960573Skris client_init_dispatch(); 135060573Skris 1351113908Sdes /* 1352113908Sdes * Set signal handlers, (e.g. to restore non-blocking mode) 1353113908Sdes * but don't overwrite SIG_IGN, matches behaviour from rsh(1) 1354113908Sdes */ 1355146998Sdes if (signal(SIGHUP, SIG_IGN) != SIG_IGN) 1356146998Sdes signal(SIGHUP, signal_handler); 1357113908Sdes if (signal(SIGINT, SIG_IGN) != SIG_IGN) 1358113908Sdes signal(SIGINT, signal_handler); 1359113908Sdes if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) 1360113908Sdes signal(SIGQUIT, signal_handler); 1361113908Sdes if (signal(SIGTERM, SIG_IGN) != SIG_IGN) 1362113908Sdes signal(SIGTERM, signal_handler); 1363146998Sdes signal(SIGWINCH, window_change_handler); 136457429Smarkm 136557429Smarkm if (have_pty) 1366204917Sdes enter_raw_mode(force_tty_flag); 136757429Smarkm 136876259Sgreen if (compat20) { 136976259Sgreen session_ident = ssh2_chan_id; 1370181111Sdes if (escape_char_arg != SSH_ESCAPECHAR_NONE) 137176259Sgreen channel_register_filter(session_ident, 1372181111Sdes client_simple_escape_filter, NULL, 1373181111Sdes client_filter_cleanup, 1374181111Sdes client_new_escape_filter_ctx(escape_char_arg)); 137576259Sgreen if (session_ident != -1) 137676259Sgreen channel_register_cleanup(session_ident, 1377157016Sdes client_channel_closed, 0); 137876259Sgreen } else { 137976259Sgreen /* Check if we should immediately send eof on stdin. */ 138060573Skris client_check_initial_eof_on_stdin(); 138176259Sgreen } 138257429Smarkm 138357429Smarkm /* Main loop of the client for the interactive session mode. */ 138457429Smarkm while (!quit_pending) { 138557429Smarkm 138657429Smarkm /* Process buffered packets sent by the server. */ 138757429Smarkm client_process_buffered_input_packets(); 138857429Smarkm 138976259Sgreen if (compat20 && session_closed && !channel_still_open()) 139060573Skris break; 139160573Skris 139276259Sgreen rekeying = (xxx_kex != NULL && !xxx_kex->done); 139357429Smarkm 139476259Sgreen if (rekeying) { 139576259Sgreen debug("rekeying in progress"); 139676259Sgreen } else { 139776259Sgreen /* 139876259Sgreen * Make packets of buffered stdin data, and buffer 139976259Sgreen * them for sending to the server. 140076259Sgreen */ 140176259Sgreen if (!compat20) 140276259Sgreen client_make_packets_from_stdin_data(); 140357429Smarkm 140476259Sgreen /* 140576259Sgreen * Make packets from buffered channel data, and 140676259Sgreen * enqueue them for sending to the server. 140776259Sgreen */ 140876259Sgreen if (packet_not_very_much_data_to_write()) 140976259Sgreen channel_output_poll(); 141057429Smarkm 141176259Sgreen /* 141276259Sgreen * Check if the window size has changed, and buffer a 141376259Sgreen * message about it to the server if so. 141476259Sgreen */ 141576259Sgreen client_check_window_change(); 141657429Smarkm 141776259Sgreen if (quit_pending) 141876259Sgreen break; 141976259Sgreen } 142057429Smarkm /* 142157429Smarkm * Wait until we have something to do (something becomes 142257429Smarkm * available on one of the descriptors). 142357429Smarkm */ 142492555Sdes max_fd2 = max_fd; 142576259Sgreen client_wait_until_can_do_something(&readset, &writeset, 142692555Sdes &max_fd2, &nalloc, rekeying); 142757429Smarkm 142857429Smarkm if (quit_pending) 142957429Smarkm break; 143057429Smarkm 143176259Sgreen /* Do channel operations unless rekeying in progress. */ 143276259Sgreen if (!rekeying) { 143376259Sgreen channel_after_select(readset, writeset); 1434124208Sdes if (need_rekeying || packet_need_rekeying()) { 1435124208Sdes debug("need rekeying"); 143676259Sgreen xxx_kex->done = 0; 143776259Sgreen kex_send_kexinit(xxx_kex); 143876259Sgreen need_rekeying = 0; 143976259Sgreen } 144076259Sgreen } 144176259Sgreen 144260573Skris /* Buffer input from the connection. */ 144376259Sgreen client_process_net_input(readset); 144457429Smarkm 144560573Skris if (quit_pending) 144660573Skris break; 144757429Smarkm 144860573Skris if (!compat20) { 144960573Skris /* Buffer data from stdin */ 145076259Sgreen client_process_input(readset); 145160573Skris /* 145260573Skris * Process output to stdout and stderr. Output to 145360573Skris * the connection is processed elsewhere (above). 145460573Skris */ 145576259Sgreen client_process_output(writeset); 145660573Skris } 145760573Skris 1458204917Sdes if (session_resumed) { 1459204917Sdes connection_in = packet_get_connection_in(); 1460204917Sdes connection_out = packet_get_connection_out(); 1461204917Sdes max_fd = MAX(max_fd, connection_out); 1462204917Sdes max_fd = MAX(max_fd, connection_in); 1463204917Sdes session_resumed = 0; 1464204917Sdes } 1465204917Sdes 1466181111Sdes /* 1467181111Sdes * Send as much buffered packet data as possible to the 1468181111Sdes * sender. 1469181111Sdes */ 147076259Sgreen if (FD_ISSET(connection_out, writeset)) 147157429Smarkm packet_write_poll(); 147257429Smarkm } 147376259Sgreen if (readset) 147476259Sgreen xfree(readset); 147576259Sgreen if (writeset) 147676259Sgreen xfree(writeset); 147757429Smarkm 147857429Smarkm /* Terminate the session. */ 147957429Smarkm 148057429Smarkm /* Stop watching for window change. */ 1481146998Sdes signal(SIGWINCH, SIG_DFL); 148257429Smarkm 1483197679Sdes if (compat20) { 1484197679Sdes packet_start(SSH2_MSG_DISCONNECT); 1485197679Sdes packet_put_int(SSH2_DISCONNECT_BY_APPLICATION); 1486197679Sdes packet_put_cstring("disconnected by user"); 1487207319Sdes packet_put_cstring(""); /* language tag */ 1488197679Sdes packet_send(); 1489197679Sdes packet_write_wait(); 1490197679Sdes } 1491197679Sdes 149292555Sdes channel_free_all(); 149357429Smarkm 149492555Sdes if (have_pty) 1495204917Sdes leave_raw_mode(force_tty_flag); 149692555Sdes 149792555Sdes /* restore blocking io */ 149892555Sdes if (!isatty(fileno(stdin))) 149992555Sdes unset_nonblock(fileno(stdin)); 150092555Sdes if (!isatty(fileno(stdout))) 150192555Sdes unset_nonblock(fileno(stdout)); 150292555Sdes if (!isatty(fileno(stderr))) 150392555Sdes unset_nonblock(fileno(stderr)); 150492555Sdes 1505126274Sdes /* 1506126274Sdes * If there was no shell or command requested, there will be no remote 1507126274Sdes * exit status to be returned. In that case, clear error code if the 1508126274Sdes * connection was deliberately terminated at this end. 1509126274Sdes */ 1510126274Sdes if (no_shell_flag && received_signal == SIGTERM) { 1511126274Sdes received_signal = 0; 1512126274Sdes exit_status = 0; 151392555Sdes } 151492555Sdes 1515126274Sdes if (received_signal) 1516126274Sdes fatal("Killed by signal %d.", (int) received_signal); 1517126274Sdes 151857429Smarkm /* 151957429Smarkm * In interactive mode (with pseudo tty) display a message indicating 152057429Smarkm * that the connection has been closed. 152157429Smarkm */ 152257429Smarkm if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) { 1523181111Sdes snprintf(buf, sizeof buf, 1524181111Sdes "Connection to %.64s closed.\r\n", host); 152557429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 152657429Smarkm } 152792555Sdes 152857429Smarkm /* Output any buffered data for stdout. */ 152957429Smarkm while (buffer_len(&stdout_buffer) > 0) { 153057429Smarkm len = write(fileno(stdout), buffer_ptr(&stdout_buffer), 153157429Smarkm buffer_len(&stdout_buffer)); 153257429Smarkm if (len <= 0) { 153357429Smarkm error("Write failed flushing stdout buffer."); 153457429Smarkm break; 153557429Smarkm } 153657429Smarkm buffer_consume(&stdout_buffer, len); 153757429Smarkm } 153857429Smarkm 153957429Smarkm /* Output any buffered data for stderr. */ 154057429Smarkm while (buffer_len(&stderr_buffer) > 0) { 154157429Smarkm len = write(fileno(stderr), buffer_ptr(&stderr_buffer), 154257429Smarkm buffer_len(&stderr_buffer)); 154357429Smarkm if (len <= 0) { 154457429Smarkm error("Write failed flushing stderr buffer."); 154557429Smarkm break; 154657429Smarkm } 154757429Smarkm buffer_consume(&stderr_buffer, len); 154857429Smarkm } 154957429Smarkm 155057429Smarkm /* Clear and free any buffers. */ 155157429Smarkm memset(buf, 0, sizeof(buf)); 155257429Smarkm buffer_free(&stdin_buffer); 155357429Smarkm buffer_free(&stdout_buffer); 155457429Smarkm buffer_free(&stderr_buffer); 155557429Smarkm 155657429Smarkm /* Report bytes transferred, and transfer rates. */ 155757429Smarkm total_time = get_current_time() - start_time; 1558181111Sdes packet_get_state(MODE_IN, NULL, NULL, NULL, &ibytes); 1559181111Sdes packet_get_state(MODE_OUT, NULL, NULL, NULL, &obytes); 1560181111Sdes verbose("Transferred: sent %llu, received %llu bytes, in %.1f seconds", 1561181111Sdes obytes, ibytes, total_time); 156257429Smarkm if (total_time > 0) 1563181111Sdes verbose("Bytes per second: sent %.1f, received %.1f", 1564181111Sdes obytes / total_time, ibytes / total_time); 156557429Smarkm /* Return the exit status of the program. */ 156657429Smarkm debug("Exit status %d", exit_status); 156757429Smarkm return exit_status; 156857429Smarkm} 156960573Skris 157060573Skris/*********/ 157160573Skris 157292555Sdesstatic void 157392555Sdesclient_input_stdout_data(int type, u_int32_t seq, void *ctxt) 157460573Skris{ 157576259Sgreen u_int data_len; 157660573Skris char *data = packet_get_string(&data_len); 157792555Sdes packet_check_eom(); 157860573Skris buffer_append(&stdout_buffer, data, data_len); 157960573Skris memset(data, 0, data_len); 158060573Skris xfree(data); 158160573Skris} 158292555Sdesstatic void 158392555Sdesclient_input_stderr_data(int type, u_int32_t seq, void *ctxt) 158460573Skris{ 158576259Sgreen u_int data_len; 158660573Skris char *data = packet_get_string(&data_len); 158792555Sdes packet_check_eom(); 158860573Skris buffer_append(&stderr_buffer, data, data_len); 158960573Skris memset(data, 0, data_len); 159060573Skris xfree(data); 159160573Skris} 159292555Sdesstatic void 159392555Sdesclient_input_exit_status(int type, u_int32_t seq, void *ctxt) 159460573Skris{ 159560573Skris exit_status = packet_get_int(); 159692555Sdes packet_check_eom(); 159760573Skris /* Acknowledge the exit. */ 159860573Skris packet_start(SSH_CMSG_EXIT_CONFIRMATION); 159960573Skris packet_send(); 160060573Skris /* 160160573Skris * Must wait for packet to be sent since we are 160260573Skris * exiting the loop. 160360573Skris */ 160460573Skris packet_write_wait(); 160560573Skris /* Flag that we want to exit. */ 160660573Skris quit_pending = 1; 160760573Skris} 1608126274Sdesstatic void 1609126274Sdesclient_input_agent_open(int type, u_int32_t seq, void *ctxt) 1610126274Sdes{ 1611126274Sdes Channel *c = NULL; 1612126274Sdes int remote_id, sock; 161360573Skris 1614126274Sdes /* Read the remote channel number from the message. */ 1615126274Sdes remote_id = packet_get_int(); 1616126274Sdes packet_check_eom(); 1617126274Sdes 1618126274Sdes /* 1619126274Sdes * Get a connection to the local authentication agent (this may again 1620126274Sdes * get forwarded). 1621126274Sdes */ 1622126274Sdes sock = ssh_get_authentication_socket(); 1623126274Sdes 1624126274Sdes /* 1625126274Sdes * If we could not connect the agent, send an error message back to 1626126274Sdes * the server. This should never happen unless the agent dies, 1627126274Sdes * because authentication forwarding is only enabled if we have an 1628126274Sdes * agent. 1629126274Sdes */ 1630126274Sdes if (sock >= 0) { 1631126274Sdes c = channel_new("", SSH_CHANNEL_OPEN, sock, sock, 1632126274Sdes -1, 0, 0, 0, "authentication agent connection", 1); 1633126274Sdes c->remote_id = remote_id; 1634126274Sdes c->force_drain = 1; 1635126274Sdes } 1636126274Sdes if (c == NULL) { 1637126274Sdes packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 1638126274Sdes packet_put_int(remote_id); 1639126274Sdes } else { 1640126274Sdes /* Send a confirmation to the remote host. */ 1641126274Sdes debug("Forwarding authentication connection."); 1642126274Sdes packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 1643126274Sdes packet_put_int(remote_id); 1644126274Sdes packet_put_int(c->self); 1645126274Sdes } 1646126274Sdes packet_send(); 1647126274Sdes} 1648126274Sdes 164992555Sdesstatic Channel * 165076259Sgreenclient_request_forwarded_tcpip(const char *request_type, int rchan) 165176259Sgreen{ 1652106121Sdes Channel *c = NULL; 165376259Sgreen char *listen_address, *originator_address; 1654192595Sdes u_short listen_port, originator_port; 165576259Sgreen 165676259Sgreen /* Get rest of the packet */ 165776259Sgreen listen_address = packet_get_string(NULL); 165876259Sgreen listen_port = packet_get_int(); 165976259Sgreen originator_address = packet_get_string(NULL); 166076259Sgreen originator_port = packet_get_int(); 166192555Sdes packet_check_eom(); 166276259Sgreen 1663181111Sdes debug("client_request_forwarded_tcpip: listen %s port %d, " 1664181111Sdes "originator %s port %d", listen_address, listen_port, 1665181111Sdes originator_address, originator_port); 166676259Sgreen 1667181111Sdes c = channel_connect_by_listen_address(listen_port, 1668181111Sdes "forwarded-tcpip", originator_address); 1669181111Sdes 167076259Sgreen xfree(originator_address); 167176259Sgreen xfree(listen_address); 167276259Sgreen return c; 167376259Sgreen} 167476259Sgreen 1675106121Sdesstatic Channel * 167676259Sgreenclient_request_x11(const char *request_type, int rchan) 167776259Sgreen{ 167876259Sgreen Channel *c = NULL; 167976259Sgreen char *originator; 1680192595Sdes u_short originator_port; 168192555Sdes int sock; 168276259Sgreen 168376259Sgreen if (!options.forward_x11) { 168476259Sgreen error("Warning: ssh server tried X11 forwarding."); 1685181111Sdes error("Warning: this is probably a break-in attempt by a " 1686181111Sdes "malicious server."); 168776259Sgreen return NULL; 168876259Sgreen } 168976259Sgreen originator = packet_get_string(NULL); 169076259Sgreen if (datafellows & SSH_BUG_X11FWD) { 169176259Sgreen debug2("buggy server: x11 request w/o originator_port"); 169276259Sgreen originator_port = 0; 169376259Sgreen } else { 169476259Sgreen originator_port = packet_get_int(); 169576259Sgreen } 169692555Sdes packet_check_eom(); 169776259Sgreen /* XXX check permission */ 169876259Sgreen debug("client_request_x11: request from %s %d", originator, 169976259Sgreen originator_port); 170092555Sdes xfree(originator); 170176259Sgreen sock = x11_connect_display(); 170292555Sdes if (sock < 0) 170392555Sdes return NULL; 170492555Sdes c = channel_new("x11", 170592555Sdes SSH_CHANNEL_X11_OPEN, sock, sock, -1, 1706124208Sdes CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "x11", 1); 170792555Sdes c->force_drain = 1; 170876259Sgreen return c; 170976259Sgreen} 171076259Sgreen 1711106121Sdesstatic Channel * 171276259Sgreenclient_request_agent(const char *request_type, int rchan) 171376259Sgreen{ 171476259Sgreen Channel *c = NULL; 171592555Sdes int sock; 171676259Sgreen 171776259Sgreen if (!options.forward_agent) { 171876259Sgreen error("Warning: ssh server tried agent forwarding."); 1719181111Sdes error("Warning: this is probably a break-in attempt by a " 1720181111Sdes "malicious server."); 172176259Sgreen return NULL; 172276259Sgreen } 1723181111Sdes sock = ssh_get_authentication_socket(); 172492555Sdes if (sock < 0) 172592555Sdes return NULL; 172692555Sdes c = channel_new("authentication agent connection", 172792555Sdes SSH_CHANNEL_OPEN, sock, sock, -1, 1728181111Sdes CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, 1729124208Sdes "authentication agent connection", 1); 173092555Sdes c->force_drain = 1; 173176259Sgreen return c; 173276259Sgreen} 173376259Sgreen 1734181111Sdesint 1735181111Sdesclient_request_tun_fwd(int tun_mode, int local_tun, int remote_tun) 1736181111Sdes{ 1737181111Sdes Channel *c; 1738181111Sdes int fd; 1739181111Sdes 1740181111Sdes if (tun_mode == SSH_TUNMODE_NO) 1741181111Sdes return 0; 1742181111Sdes 1743181111Sdes if (!compat20) { 1744192595Sdes error("Tunnel forwarding is not supported for protocol 1"); 1745181111Sdes return -1; 1746181111Sdes } 1747181111Sdes 1748181111Sdes debug("Requesting tun unit %d in mode %d", local_tun, tun_mode); 1749181111Sdes 1750181111Sdes /* Open local tunnel device */ 1751181111Sdes if ((fd = tun_open(local_tun, tun_mode)) == -1) { 1752181111Sdes error("Tunnel device open failed."); 1753181111Sdes return -1; 1754181111Sdes } 1755181111Sdes 1756181111Sdes c = channel_new("tun", SSH_CHANNEL_OPENING, fd, fd, -1, 1757181111Sdes CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1); 1758181111Sdes c->datagram = 1; 1759181111Sdes 1760181111Sdes#if defined(SSH_TUN_FILTER) 1761181111Sdes if (options.tun_open == SSH_TUNMODE_POINTOPOINT) 1762181111Sdes channel_register_filter(c->self, sys_tun_infilter, 1763181111Sdes sys_tun_outfilter, NULL, NULL); 1764181111Sdes#endif 1765181111Sdes 1766181111Sdes packet_start(SSH2_MSG_CHANNEL_OPEN); 1767181111Sdes packet_put_cstring("tun@openssh.com"); 1768181111Sdes packet_put_int(c->self); 1769181111Sdes packet_put_int(c->local_window_max); 1770181111Sdes packet_put_int(c->local_maxpacket); 1771181111Sdes packet_put_int(tun_mode); 1772181111Sdes packet_put_int(remote_tun); 1773181111Sdes packet_send(); 1774181111Sdes 1775181111Sdes return 0; 1776181111Sdes} 1777181111Sdes 177860573Skris/* XXXX move to generic input handler */ 177992555Sdesstatic void 178092555Sdesclient_input_channel_open(int type, u_int32_t seq, void *ctxt) 178160573Skris{ 178260573Skris Channel *c = NULL; 178360573Skris char *ctype; 178460573Skris int rchan; 178599060Sdes u_int rmaxpack, rwindow, len; 178660573Skris 178760573Skris ctype = packet_get_string(&len); 178860573Skris rchan = packet_get_int(); 178960573Skris rwindow = packet_get_int(); 179060573Skris rmaxpack = packet_get_int(); 179160573Skris 179260573Skris debug("client_input_channel_open: ctype %s rchan %d win %d max %d", 179360573Skris ctype, rchan, rwindow, rmaxpack); 179460573Skris 179576259Sgreen if (strcmp(ctype, "forwarded-tcpip") == 0) { 179676259Sgreen c = client_request_forwarded_tcpip(ctype, rchan); 179776259Sgreen } else if (strcmp(ctype, "x11") == 0) { 179876259Sgreen c = client_request_x11(ctype, rchan); 179976259Sgreen } else if (strcmp(ctype, "auth-agent@openssh.com") == 0) { 180076259Sgreen c = client_request_agent(ctype, rchan); 180160573Skris } 180260573Skris/* XXX duplicate : */ 180360573Skris if (c != NULL) { 180460573Skris debug("confirm %s", ctype); 180560573Skris c->remote_id = rchan; 180660573Skris c->remote_window = rwindow; 180760573Skris c->remote_maxpacket = rmaxpack; 180892555Sdes if (c->type != SSH_CHANNEL_CONNECTING) { 180992555Sdes packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); 181092555Sdes packet_put_int(c->remote_id); 181192555Sdes packet_put_int(c->self); 181292555Sdes packet_put_int(c->local_window); 181392555Sdes packet_put_int(c->local_maxpacket); 181492555Sdes packet_send(); 181592555Sdes } 181660573Skris } else { 181760573Skris debug("failure %s", ctype); 181860573Skris packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); 181960573Skris packet_put_int(rchan); 182060573Skris packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED); 182192555Sdes if (!(datafellows & SSH_BUG_OPENFAILURE)) { 182292555Sdes packet_put_cstring("open failed"); 182392555Sdes packet_put_cstring(""); 182492555Sdes } 182560573Skris packet_send(); 182660573Skris } 182760573Skris xfree(ctype); 182860573Skris} 182992555Sdesstatic void 183092555Sdesclient_input_channel_req(int type, u_int32_t seq, void *ctxt) 183176259Sgreen{ 183276259Sgreen Channel *c = NULL; 1833137015Sdes int exitval, id, reply, success = 0; 183476259Sgreen char *rtype; 183560573Skris 183676259Sgreen id = packet_get_int(); 183776259Sgreen rtype = packet_get_string(NULL); 183876259Sgreen reply = packet_get_char(); 183976259Sgreen 184076259Sgreen debug("client_input_channel_req: channel %d rtype %s reply %d", 184176259Sgreen id, rtype, reply); 184276259Sgreen 1843137015Sdes if (id == -1) { 1844137015Sdes error("client_input_channel_req: request for channel -1"); 1845137015Sdes } else if ((c = channel_lookup(id)) == NULL) { 1846181111Sdes error("client_input_channel_req: channel %d: " 1847181111Sdes "unknown channel", id); 1848181111Sdes } else if (strcmp(rtype, "eow@openssh.com") == 0) { 1849181111Sdes packet_check_eom(); 1850181111Sdes chan_rcvd_eow(c); 185176259Sgreen } else if (strcmp(rtype, "exit-status") == 0) { 1852137015Sdes exitval = packet_get_int(); 1853204917Sdes if (c->ctl_chan != -1) { 1854204917Sdes mux_exit_message(c, exitval); 1855137015Sdes success = 1; 1856204917Sdes } else if (id == session_ident) { 1857204917Sdes /* Record exit value of local session */ 1858204917Sdes success = 1; 1859137015Sdes exit_status = exitval; 1860137015Sdes } else { 1861204917Sdes /* Probably for a mux channel that has already closed */ 1862204917Sdes debug("%s: no sink for exit-status on channel %d", 1863204917Sdes __func__, id); 1864137015Sdes } 186592555Sdes packet_check_eom(); 186676259Sgreen } 186776259Sgreen if (reply) { 186876259Sgreen packet_start(success ? 186976259Sgreen SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); 1870192595Sdes packet_put_int(c->remote_id); 187176259Sgreen packet_send(); 187276259Sgreen } 187376259Sgreen xfree(rtype); 187476259Sgreen} 187592555Sdesstatic void 187692555Sdesclient_input_global_request(int type, u_int32_t seq, void *ctxt) 187792555Sdes{ 187892555Sdes char *rtype; 187992555Sdes int want_reply; 188092555Sdes int success = 0; 188176259Sgreen 188292555Sdes rtype = packet_get_string(NULL); 188392555Sdes want_reply = packet_get_char(); 1884126274Sdes debug("client_input_global_request: rtype %s want_reply %d", 1885126274Sdes rtype, want_reply); 188692555Sdes if (want_reply) { 188792555Sdes packet_start(success ? 188892555Sdes SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE); 188992555Sdes packet_send(); 189092555Sdes packet_write_wait(); 189192555Sdes } 189292555Sdes xfree(rtype); 189392555Sdes} 189492555Sdes 1895137015Sdesvoid 1896137015Sdesclient_session2_setup(int id, int want_tty, int want_subsystem, 1897181111Sdes const char *term, struct termios *tiop, int in_fd, Buffer *cmd, char **env) 1898137015Sdes{ 1899137015Sdes int len; 1900146998Sdes Channel *c = NULL; 1901137015Sdes 1902137015Sdes debug2("%s: id %d", __func__, id); 1903137015Sdes 1904146998Sdes if ((c = channel_lookup(id)) == NULL) 1905146998Sdes fatal("client_session2_setup: channel %d: unknown channel", id); 1906146998Sdes 1907137015Sdes if (want_tty) { 1908137015Sdes struct winsize ws; 1909137015Sdes 1910137015Sdes /* Store window size in the packet. */ 1911137015Sdes if (ioctl(in_fd, TIOCGWINSZ, &ws) < 0) 1912137015Sdes memset(&ws, 0, sizeof(ws)); 1913137015Sdes 1914181111Sdes channel_request_start(id, "pty-req", 1); 1915181111Sdes client_expect_confirm(id, "PTY allocation", 0); 1916137015Sdes packet_put_cstring(term != NULL ? term : ""); 1917162852Sdes packet_put_int((u_int)ws.ws_col); 1918162852Sdes packet_put_int((u_int)ws.ws_row); 1919162852Sdes packet_put_int((u_int)ws.ws_xpixel); 1920162852Sdes packet_put_int((u_int)ws.ws_ypixel); 1921181111Sdes if (tiop == NULL) 1922181111Sdes tiop = get_saved_tio(); 1923181111Sdes tty_make_modes(-1, tiop); 1924137015Sdes packet_send(); 1925137015Sdes /* XXX wait for reply */ 1926146998Sdes c->client_tty = 1; 1927137015Sdes } 1928137015Sdes 1929137015Sdes /* Transfer any environment variables from client to server */ 1930137015Sdes if (options.num_send_env != 0 && env != NULL) { 1931137015Sdes int i, j, matched; 1932137015Sdes char *name, *val; 1933137015Sdes 1934137015Sdes debug("Sending environment."); 1935137015Sdes for (i = 0; env[i] != NULL; i++) { 1936137015Sdes /* Split */ 1937137015Sdes name = xstrdup(env[i]); 1938137015Sdes if ((val = strchr(name, '=')) == NULL) { 1939157016Sdes xfree(name); 1940137015Sdes continue; 1941137015Sdes } 1942137015Sdes *val++ = '\0'; 1943137015Sdes 1944137015Sdes matched = 0; 1945137015Sdes for (j = 0; j < options.num_send_env; j++) { 1946137015Sdes if (match_pattern(name, options.send_env[j])) { 1947137015Sdes matched = 1; 1948137015Sdes break; 1949137015Sdes } 1950137015Sdes } 1951137015Sdes if (!matched) { 1952137015Sdes debug3("Ignored env %s", name); 1953157016Sdes xfree(name); 1954137015Sdes continue; 1955137015Sdes } 1956137015Sdes 1957137015Sdes debug("Sending env %s = %s", name, val); 1958137015Sdes channel_request_start(id, "env", 0); 1959137015Sdes packet_put_cstring(name); 1960137015Sdes packet_put_cstring(val); 1961137015Sdes packet_send(); 1962157016Sdes xfree(name); 1963137015Sdes } 1964137015Sdes } 1965137015Sdes 1966137015Sdes len = buffer_len(cmd); 1967137015Sdes if (len > 0) { 1968137015Sdes if (len > 900) 1969137015Sdes len = 900; 1970137015Sdes if (want_subsystem) { 1971181111Sdes debug("Sending subsystem: %.*s", 1972181111Sdes len, (u_char*)buffer_ptr(cmd)); 1973181111Sdes channel_request_start(id, "subsystem", 1); 1974181111Sdes client_expect_confirm(id, "subsystem", 1); 1975137015Sdes } else { 1976181111Sdes debug("Sending command: %.*s", 1977181111Sdes len, (u_char*)buffer_ptr(cmd)); 1978181111Sdes channel_request_start(id, "exec", 1); 1979181111Sdes client_expect_confirm(id, "exec", 1); 1980137015Sdes } 1981137015Sdes packet_put_string(buffer_ptr(cmd), buffer_len(cmd)); 1982137015Sdes packet_send(); 1983137015Sdes } else { 1984181111Sdes channel_request_start(id, "shell", 1); 1985181111Sdes client_expect_confirm(id, "shell", 1); 1986137015Sdes packet_send(); 1987137015Sdes } 1988137015Sdes} 1989137015Sdes 199092555Sdesstatic void 199176259Sgreenclient_init_dispatch_20(void) 199260573Skris{ 199360573Skris dispatch_init(&dispatch_protocol_error); 199498675Sdes 199560573Skris dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); 199660573Skris dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data); 199760573Skris dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); 199860573Skris dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); 199960573Skris dispatch_set(SSH2_MSG_CHANNEL_OPEN, &client_input_channel_open); 200060573Skris dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); 200160573Skris dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); 200276259Sgreen dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &client_input_channel_req); 200360573Skris dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); 2004181111Sdes dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, &channel_input_status_confirm); 2005181111Sdes dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &channel_input_status_confirm); 200692555Sdes dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &client_input_global_request); 200776259Sgreen 200876259Sgreen /* rekeying */ 200976259Sgreen dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit); 201098675Sdes 201198675Sdes /* global request reply messages */ 201298675Sdes dispatch_set(SSH2_MSG_REQUEST_FAILURE, &client_global_request_reply); 201398675Sdes dispatch_set(SSH2_MSG_REQUEST_SUCCESS, &client_global_request_reply); 201460573Skris} 2015181111Sdes 201692555Sdesstatic void 201776259Sgreenclient_init_dispatch_13(void) 201860573Skris{ 201960573Skris dispatch_init(NULL); 202060573Skris dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close); 202160573Skris dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation); 202260573Skris dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data); 202360573Skris dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); 202460573Skris dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); 202560573Skris dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open); 202660573Skris dispatch_set(SSH_SMSG_EXITSTATUS, &client_input_exit_status); 202760573Skris dispatch_set(SSH_SMSG_STDERR_DATA, &client_input_stderr_data); 202860573Skris dispatch_set(SSH_SMSG_STDOUT_DATA, &client_input_stdout_data); 202968700Sgreen 203068700Sgreen dispatch_set(SSH_SMSG_AGENT_OPEN, options.forward_agent ? 2031126274Sdes &client_input_agent_open : &deny_input_open); 203268700Sgreen dispatch_set(SSH_SMSG_X11_OPEN, options.forward_x11 ? 203369587Sgreen &x11_input_open : &deny_input_open); 203460573Skris} 2035181111Sdes 203692555Sdesstatic void 203776259Sgreenclient_init_dispatch_15(void) 203860573Skris{ 203960573Skris client_init_dispatch_13(); 204060573Skris dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof); 204160573Skris dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, & channel_input_oclose); 204260573Skris} 2043181111Sdes 204492555Sdesstatic void 204576259Sgreenclient_init_dispatch(void) 204660573Skris{ 204760573Skris if (compat20) 204860573Skris client_init_dispatch_20(); 204960573Skris else if (compat13) 205060573Skris client_init_dispatch_13(); 205160573Skris else 205260573Skris client_init_dispatch_15(); 205360573Skris} 2054126274Sdes 2055126274Sdes/* client specific fatal cleanup */ 2056126274Sdesvoid 2057126274Sdescleanup_exit(int i) 2058126274Sdes{ 2059204917Sdes leave_raw_mode(force_tty_flag); 2060126274Sdes leave_non_blocking(); 2061181111Sdes if (options.control_path != NULL && muxserver_sock != -1) 2062137015Sdes unlink(options.control_path); 2063126274Sdes _exit(i); 2064126274Sdes} 2065