clientloop.c revision 162852
1162852Sdes/* $OpenBSD: clientloop.c,v 1.175 2006/08/03 03:34:42 deraadt 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 89162852Sdes#include "xmalloc.h" 9076259Sgreen#include "ssh.h" 9176259Sgreen#include "ssh1.h" 9276259Sgreen#include "ssh2.h" 9357429Smarkm#include "packet.h" 9457429Smarkm#include "buffer.h" 9560573Skris#include "compat.h" 9660573Skris#include "channels.h" 9760573Skris#include "dispatch.h" 9876259Sgreen#include "key.h" 99162852Sdes#include "cipher.h" 10076259Sgreen#include "kex.h" 10176259Sgreen#include "log.h" 10276259Sgreen#include "readconf.h" 10376259Sgreen#include "clientloop.h" 104157016Sdes#include "sshconnect.h" 10576259Sgreen#include "authfd.h" 10676259Sgreen#include "atomicio.h" 107137015Sdes#include "sshpty.h" 10876259Sgreen#include "misc.h" 109137015Sdes#include "monitor_fdpass.h" 110137015Sdes#include "match.h" 111137015Sdes#include "msg.h" 11260573Skris 11369587Sgreen/* import options */ 11468700Sgreenextern Options options; 11568700Sgreen 11657429Smarkm/* Flag indicating that stdin should be redirected from /dev/null. */ 11757429Smarkmextern int stdin_null_flag; 11857429Smarkm 119126274Sdes/* Flag indicating that no shell has been requested */ 120126274Sdesextern int no_shell_flag; 121126274Sdes 122137015Sdes/* Control socket */ 123137015Sdesextern int control_fd; 124137015Sdes 12557429Smarkm/* 12657429Smarkm * Name of the host we are connecting to. This is the name given on the 12757429Smarkm * command line, or the HostName specified for the user-supplied name in a 12857429Smarkm * configuration file. 12957429Smarkm */ 13057429Smarkmextern char *host; 13157429Smarkm 13257429Smarkm/* 13357429Smarkm * Flag to indicate that we have received a window change signal which has 13457429Smarkm * not yet been processed. This will cause a message indicating the new 13557429Smarkm * window size to be sent to the server a little later. This is volatile 13657429Smarkm * because this is updated in a signal handler. 13757429Smarkm */ 13892555Sdesstatic volatile sig_atomic_t received_window_change_signal = 0; 13992555Sdesstatic volatile sig_atomic_t received_signal = 0; 14057429Smarkm 141157016Sdes/* Flag indicating whether the user's terminal is in non-blocking mode. */ 14257429Smarkmstatic int in_non_blocking_mode = 0; 14357429Smarkm 14457429Smarkm/* Common data for the client loop code. */ 145162852Sdesstatic volatile sig_atomic_t quit_pending; /* Set non-zero to quit the loop. */ 14665668Skrisstatic int escape_char; /* Escape character. */ 14757429Smarkmstatic int escape_pending; /* Last character was the escape character */ 14857429Smarkmstatic int last_was_cr; /* Last character was a newline. */ 14957429Smarkmstatic int exit_status; /* Used to store the exit status of the command. */ 15057429Smarkmstatic int stdin_eof; /* EOF has been encountered on standard error. */ 15157429Smarkmstatic Buffer stdin_buffer; /* Buffer for stdin data. */ 15257429Smarkmstatic Buffer stdout_buffer; /* Buffer for stdout data. */ 15357429Smarkmstatic Buffer stderr_buffer; /* Buffer for stderr data. */ 15476259Sgreenstatic u_long stdin_bytes, stdout_bytes, stderr_bytes; 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. */ 160126274Sdesstatic int server_alive_timeouts = 0; 16157429Smarkm 16292555Sdesstatic void client_init_dispatch(void); 16360573Skrisint session_ident = -1; 16460573Skris 165137015Sdesstruct confirm_ctx { 166137015Sdes int want_tty; 167137015Sdes int want_subsys; 168149749Sdes int want_x_fwd; 169149749Sdes int want_agent_fwd; 170137015Sdes Buffer cmd; 171137015Sdes char *term; 172137015Sdes struct termios tio; 173137015Sdes char **env; 174137015Sdes}; 175137015Sdes 17676259Sgreen/*XXX*/ 17776259Sgreenextern Kex *xxx_kex; 17857429Smarkm 179137015Sdesvoid ssh_process_session2_setup(int, int, int, Buffer *); 180137015Sdes 18157429Smarkm/* Restores stdin to blocking mode. */ 18257429Smarkm 18392555Sdesstatic void 18476259Sgreenleave_non_blocking(void) 18557429Smarkm{ 18657429Smarkm if (in_non_blocking_mode) { 187137015Sdes unset_nonblock(fileno(stdin)); 18857429Smarkm in_non_blocking_mode = 0; 18957429Smarkm } 19057429Smarkm} 19157429Smarkm 19257429Smarkm/* Puts stdin terminal in non-blocking mode. */ 19357429Smarkm 19492555Sdesstatic void 19576259Sgreenenter_non_blocking(void) 19657429Smarkm{ 19757429Smarkm in_non_blocking_mode = 1; 198137015Sdes set_nonblock(fileno(stdin)); 19957429Smarkm} 20057429Smarkm 20157429Smarkm/* 20257429Smarkm * Signal handler for the window change signal (SIGWINCH). This just sets a 20357429Smarkm * flag indicating that the window has changed. 20457429Smarkm */ 205162852Sdes/*ARGSUSED */ 20692555Sdesstatic void 20757429Smarkmwindow_change_handler(int sig) 20857429Smarkm{ 20957429Smarkm received_window_change_signal = 1; 21057429Smarkm signal(SIGWINCH, window_change_handler); 21157429Smarkm} 21257429Smarkm 21357429Smarkm/* 21457429Smarkm * Signal handler for signals that cause the program to terminate. These 21557429Smarkm * signals must be trapped to restore terminal modes. 21657429Smarkm */ 217162852Sdes/*ARGSUSED */ 21892555Sdesstatic void 21957429Smarkmsignal_handler(int sig) 22057429Smarkm{ 22192555Sdes received_signal = sig; 22292555Sdes quit_pending = 1; 22357429Smarkm} 22457429Smarkm 22557429Smarkm/* 22657429Smarkm * Returns current time in seconds from Jan 1, 1970 with the maximum 22757429Smarkm * available resolution. 22857429Smarkm */ 22957429Smarkm 23092555Sdesstatic double 23176259Sgreenget_current_time(void) 23257429Smarkm{ 23357429Smarkm struct timeval tv; 23457429Smarkm gettimeofday(&tv, NULL); 23557429Smarkm return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0; 23657429Smarkm} 23757429Smarkm 238149749Sdes#define SSH_X11_PROTO "MIT-MAGIC-COOKIE-1" 239149749Sdesvoid 240149749Sdesclient_x11_get_proto(const char *display, const char *xauth_path, 241149749Sdes u_int trusted, char **_proto, char **_data) 242149749Sdes{ 243149749Sdes char cmd[1024]; 244149749Sdes char line[512]; 245149749Sdes char xdisplay[512]; 246149749Sdes static char proto[512], data[512]; 247149749Sdes FILE *f; 248149749Sdes int got_data = 0, generated = 0, do_unlink = 0, i; 249149749Sdes char *xauthdir, *xauthfile; 250149749Sdes struct stat st; 251149749Sdes 252149749Sdes xauthdir = xauthfile = NULL; 253149749Sdes *_proto = proto; 254149749Sdes *_data = data; 255149749Sdes proto[0] = data[0] = '\0'; 256149749Sdes 257149749Sdes if (xauth_path == NULL ||(stat(xauth_path, &st) == -1)) { 258149749Sdes debug("No xauth program."); 259149749Sdes } else { 260149749Sdes if (display == NULL) { 261149749Sdes debug("x11_get_proto: DISPLAY not set"); 262149749Sdes return; 263149749Sdes } 264149749Sdes /* 265149749Sdes * Handle FamilyLocal case where $DISPLAY does 266149749Sdes * not match an authorization entry. For this we 267149749Sdes * just try "xauth list unix:displaynum.screennum". 268149749Sdes * XXX: "localhost" match to determine FamilyLocal 269149749Sdes * is not perfect. 270149749Sdes */ 271149749Sdes if (strncmp(display, "localhost:", 10) == 0) { 272149749Sdes snprintf(xdisplay, sizeof(xdisplay), "unix:%s", 273149749Sdes display + 10); 274149749Sdes display = xdisplay; 275149749Sdes } 276149749Sdes if (trusted == 0) { 277149749Sdes xauthdir = xmalloc(MAXPATHLEN); 278149749Sdes xauthfile = xmalloc(MAXPATHLEN); 279149749Sdes strlcpy(xauthdir, "/tmp/ssh-XXXXXXXXXX", MAXPATHLEN); 280149749Sdes if (mkdtemp(xauthdir) != NULL) { 281149749Sdes do_unlink = 1; 282149749Sdes snprintf(xauthfile, MAXPATHLEN, "%s/xauthfile", 283149749Sdes xauthdir); 284149749Sdes snprintf(cmd, sizeof(cmd), 285149749Sdes "%s -f %s generate %s " SSH_X11_PROTO 286149749Sdes " untrusted timeout 1200 2>" _PATH_DEVNULL, 287149749Sdes xauth_path, xauthfile, display); 288149749Sdes debug2("x11_get_proto: %s", cmd); 289149749Sdes if (system(cmd) == 0) 290149749Sdes generated = 1; 291149749Sdes } 292149749Sdes } 293149749Sdes snprintf(cmd, sizeof(cmd), 294157016Sdes "%s %s%s list %s 2>" _PATH_DEVNULL, 295149749Sdes xauth_path, 296149749Sdes generated ? "-f " : "" , 297149749Sdes generated ? xauthfile : "", 298149749Sdes display); 299149749Sdes debug2("x11_get_proto: %s", cmd); 300149749Sdes f = popen(cmd, "r"); 301149749Sdes if (f && fgets(line, sizeof(line), f) && 302149749Sdes sscanf(line, "%*s %511s %511s", proto, data) == 2) 303149749Sdes got_data = 1; 304149749Sdes if (f) 305149749Sdes pclose(f); 306149749Sdes } 307149749Sdes 308149749Sdes if (do_unlink) { 309149749Sdes unlink(xauthfile); 310149749Sdes rmdir(xauthdir); 311149749Sdes } 312149749Sdes if (xauthdir) 313149749Sdes xfree(xauthdir); 314149749Sdes if (xauthfile) 315149749Sdes xfree(xauthfile); 316149749Sdes 317149749Sdes /* 318149749Sdes * If we didn't get authentication data, just make up some 319149749Sdes * data. The forwarding code will check the validity of the 320149749Sdes * response anyway, and substitute this data. The X11 321149749Sdes * server, however, will ignore this fake data and use 322149749Sdes * whatever authentication mechanisms it was using otherwise 323149749Sdes * for the local connection. 324149749Sdes */ 325149749Sdes if (!got_data) { 326149749Sdes u_int32_t rnd = 0; 327149749Sdes 328149749Sdes logit("Warning: No xauth data; " 329149749Sdes "using fake authentication data for X11 forwarding."); 330149749Sdes strlcpy(proto, SSH_X11_PROTO, sizeof proto); 331149749Sdes for (i = 0; i < 16; i++) { 332149749Sdes if (i % 4 == 0) 333149749Sdes rnd = arc4random(); 334149749Sdes snprintf(data + 2 * i, sizeof data - 2 * i, "%02x", 335149749Sdes rnd & 0xff); 336149749Sdes rnd >>= 8; 337149749Sdes } 338149749Sdes } 339149749Sdes} 340149749Sdes 34157429Smarkm/* 34257429Smarkm * This is called when the interactive is entered. This checks if there is 34357429Smarkm * an EOF coming on stdin. We must check this explicitly, as select() does 34457429Smarkm * not appear to wake up when redirecting from /dev/null. 34557429Smarkm */ 34657429Smarkm 34792555Sdesstatic void 34876259Sgreenclient_check_initial_eof_on_stdin(void) 34957429Smarkm{ 35057429Smarkm int len; 35157429Smarkm char buf[1]; 35257429Smarkm 35357429Smarkm /* 35457429Smarkm * If standard input is to be "redirected from /dev/null", we simply 35557429Smarkm * mark that we have seen an EOF and send an EOF message to the 35657429Smarkm * server. Otherwise, we try to read a single character; it appears 35757429Smarkm * that for some files, such /dev/null, select() never wakes up for 35857429Smarkm * read for this descriptor, which means that we never get EOF. This 35957429Smarkm * way we will get the EOF if stdin comes from /dev/null or similar. 36057429Smarkm */ 36157429Smarkm if (stdin_null_flag) { 36257429Smarkm /* Fake EOF on stdin. */ 36357429Smarkm debug("Sending eof."); 36457429Smarkm stdin_eof = 1; 36557429Smarkm packet_start(SSH_CMSG_EOF); 36657429Smarkm packet_send(); 36757429Smarkm } else { 36857429Smarkm enter_non_blocking(); 36957429Smarkm 37057429Smarkm /* Check for immediate EOF on stdin. */ 37157429Smarkm len = read(fileno(stdin), buf, 1); 37257429Smarkm if (len == 0) { 37357429Smarkm /* EOF. Record that we have seen it and send EOF to server. */ 37457429Smarkm debug("Sending eof."); 37557429Smarkm stdin_eof = 1; 37657429Smarkm packet_start(SSH_CMSG_EOF); 37757429Smarkm packet_send(); 37857429Smarkm } else if (len > 0) { 37957429Smarkm /* 38057429Smarkm * Got data. We must store the data in the buffer, 38157429Smarkm * and also process it as an escape character if 38257429Smarkm * appropriate. 38357429Smarkm */ 38476259Sgreen if ((u_char) buf[0] == escape_char) 38557429Smarkm escape_pending = 1; 38676259Sgreen else 38757429Smarkm buffer_append(&stdin_buffer, buf, 1); 38857429Smarkm } 38957429Smarkm leave_non_blocking(); 39057429Smarkm } 39157429Smarkm} 39257429Smarkm 39357429Smarkm 39457429Smarkm/* 39557429Smarkm * Make packets from buffered stdin data, and buffer them for sending to the 39657429Smarkm * connection. 39757429Smarkm */ 39857429Smarkm 39992555Sdesstatic void 40076259Sgreenclient_make_packets_from_stdin_data(void) 40157429Smarkm{ 40276259Sgreen u_int len; 40357429Smarkm 40457429Smarkm /* Send buffered stdin data to the server. */ 40557429Smarkm while (buffer_len(&stdin_buffer) > 0 && 40692555Sdes packet_not_very_much_data_to_write()) { 40757429Smarkm len = buffer_len(&stdin_buffer); 40857429Smarkm /* Keep the packets at reasonable size. */ 40957429Smarkm if (len > packet_get_maxsize()) 41057429Smarkm len = packet_get_maxsize(); 41157429Smarkm packet_start(SSH_CMSG_STDIN_DATA); 41257429Smarkm packet_put_string(buffer_ptr(&stdin_buffer), len); 41357429Smarkm packet_send(); 41457429Smarkm buffer_consume(&stdin_buffer, len); 41576259Sgreen stdin_bytes += len; 41657429Smarkm /* If we have a pending EOF, send it now. */ 41757429Smarkm if (stdin_eof && buffer_len(&stdin_buffer) == 0) { 41857429Smarkm packet_start(SSH_CMSG_EOF); 41957429Smarkm packet_send(); 42057429Smarkm } 42157429Smarkm } 42257429Smarkm} 42357429Smarkm 42457429Smarkm/* 42557429Smarkm * Checks if the client window has changed, and sends a packet about it to 42657429Smarkm * the server if so. The actual change is detected elsewhere (by a software 42757429Smarkm * interrupt on Unix); this just checks the flag and sends a message if 42857429Smarkm * appropriate. 42957429Smarkm */ 43057429Smarkm 43192555Sdesstatic void 43276259Sgreenclient_check_window_change(void) 43357429Smarkm{ 43460573Skris struct winsize ws; 43557429Smarkm 43660573Skris if (! received_window_change_signal) 43760573Skris return; 43860573Skris /** XXX race */ 43960573Skris received_window_change_signal = 0; 44057429Smarkm 44169587Sgreen debug2("client_check_window_change: changed"); 44260573Skris 44360573Skris if (compat20) { 444137015Sdes channel_send_window_changes(); 44560573Skris } else { 446137015Sdes if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0) 447137015Sdes return; 44860573Skris packet_start(SSH_CMSG_WINDOW_SIZE); 449162852Sdes packet_put_int((u_int)ws.ws_row); 450162852Sdes packet_put_int((u_int)ws.ws_col); 451162852Sdes packet_put_int((u_int)ws.ws_xpixel); 452162852Sdes packet_put_int((u_int)ws.ws_ypixel); 45360573Skris packet_send(); 45457429Smarkm } 45557429Smarkm} 45657429Smarkm 457126274Sdesstatic void 458126274Sdesclient_global_request_reply(int type, u_int32_t seq, void *ctxt) 459126274Sdes{ 460126274Sdes server_alive_timeouts = 0; 461126274Sdes client_global_request_reply_fwd(type, seq, ctxt); 462126274Sdes} 463126274Sdes 464126274Sdesstatic void 465126274Sdesserver_alive_check(void) 466126274Sdes{ 467126274Sdes if (++server_alive_timeouts > options.server_alive_count_max) 468126274Sdes packet_disconnect("Timeout, server not responding."); 469126274Sdes packet_start(SSH2_MSG_GLOBAL_REQUEST); 470126274Sdes packet_put_cstring("keepalive@openssh.com"); 471126274Sdes packet_put_char(1); /* boolean: want reply */ 472126274Sdes packet_send(); 473126274Sdes} 474126274Sdes 47557429Smarkm/* 47657429Smarkm * Waits until the client can do something (some data becomes available on 47757429Smarkm * one of the file descriptors). 47857429Smarkm */ 47992555Sdesstatic void 48076259Sgreenclient_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, 481137015Sdes int *maxfdp, u_int *nallocp, int rekeying) 48257429Smarkm{ 483126274Sdes struct timeval tv, *tvp; 484126274Sdes int ret; 485126274Sdes 48676259Sgreen /* Add any selections by the channel mechanism. */ 48792555Sdes channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, rekeying); 48857429Smarkm 48960573Skris if (!compat20) { 49060573Skris /* Read from the connection, unless our buffers are full. */ 49160573Skris if (buffer_len(&stdout_buffer) < buffer_high && 49260573Skris buffer_len(&stderr_buffer) < buffer_high && 49360573Skris channel_not_very_much_buffered_data()) 49476259Sgreen FD_SET(connection_in, *readsetp); 49560573Skris /* 49660573Skris * Read from stdin, unless we have seen EOF or have very much 49760573Skris * buffered data to send to the server. 49860573Skris */ 49960573Skris if (!stdin_eof && packet_not_very_much_data_to_write()) 50076259Sgreen FD_SET(fileno(stdin), *readsetp); 50160573Skris 50260573Skris /* Select stdout/stderr if have data in buffer. */ 50360573Skris if (buffer_len(&stdout_buffer) > 0) 50476259Sgreen FD_SET(fileno(stdout), *writesetp); 50560573Skris if (buffer_len(&stderr_buffer) > 0) 50676259Sgreen FD_SET(fileno(stderr), *writesetp); 50760573Skris } else { 50892555Sdes /* channel_prepare_select could have closed the last channel */ 50992555Sdes if (session_closed && !channel_still_open() && 51092555Sdes !packet_have_data_to_write()) { 51192555Sdes /* clear mask since we did not call select() */ 51292555Sdes memset(*readsetp, 0, *nallocp); 51392555Sdes memset(*writesetp, 0, *nallocp); 51492555Sdes return; 51592555Sdes } else { 51692555Sdes FD_SET(connection_in, *readsetp); 51792555Sdes } 51860573Skris } 51957429Smarkm 52057429Smarkm /* Select server connection if have data to write to the server. */ 52157429Smarkm if (packet_have_data_to_write()) 52276259Sgreen FD_SET(connection_out, *writesetp); 52357429Smarkm 524137015Sdes if (control_fd != -1) 525137015Sdes FD_SET(control_fd, *readsetp); 526137015Sdes 52757429Smarkm /* 52857429Smarkm * Wait for something to happen. This will suspend the process until 52957429Smarkm * some selected descriptor can be read, written, or has some other 530126274Sdes * event pending. 53157429Smarkm */ 53257429Smarkm 533126274Sdes if (options.server_alive_interval == 0 || !compat20) 534126274Sdes tvp = NULL; 535137015Sdes else { 536126274Sdes tv.tv_sec = options.server_alive_interval; 537126274Sdes tv.tv_usec = 0; 538126274Sdes tvp = &tv; 539126274Sdes } 540126274Sdes ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp); 541126274Sdes if (ret < 0) { 54257429Smarkm char buf[100]; 54376259Sgreen 54476259Sgreen /* 54576259Sgreen * We have to clear the select masks, because we return. 54676259Sgreen * We have to return, because the mainloop checks for the flags 54776259Sgreen * set by the signal handlers. 54876259Sgreen */ 54992555Sdes memset(*readsetp, 0, *nallocp); 55092555Sdes memset(*writesetp, 0, *nallocp); 55176259Sgreen 55257429Smarkm if (errno == EINTR) 55357429Smarkm return; 55457429Smarkm /* Note: we might still have data in the buffers. */ 55557429Smarkm snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno)); 55657429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 55757429Smarkm quit_pending = 1; 558126274Sdes } else if (ret == 0) 559126274Sdes server_alive_check(); 56057429Smarkm} 56157429Smarkm 56292555Sdesstatic void 56365668Skrisclient_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr) 56457429Smarkm{ 56557429Smarkm /* Flush stdout and stderr buffers. */ 56665668Skris if (buffer_len(bout) > 0) 567124208Sdes atomicio(vwrite, fileno(stdout), buffer_ptr(bout), buffer_len(bout)); 56865668Skris if (buffer_len(berr) > 0) 569124208Sdes atomicio(vwrite, fileno(stderr), buffer_ptr(berr), buffer_len(berr)); 57057429Smarkm 57157429Smarkm leave_raw_mode(); 57257429Smarkm 57357429Smarkm /* 57457429Smarkm * Free (and clear) the buffer to reduce the amount of data that gets 57557429Smarkm * written to swap. 57657429Smarkm */ 57765668Skris buffer_free(bin); 57865668Skris buffer_free(bout); 57965668Skris buffer_free(berr); 58057429Smarkm 58157429Smarkm /* Send the suspend signal to the program itself. */ 58257429Smarkm kill(getpid(), SIGTSTP); 58357429Smarkm 584146998Sdes /* Reset window sizes in case they have changed */ 585146998Sdes received_window_change_signal = 1; 58657429Smarkm 58757429Smarkm /* OK, we have been continued by the user. Reinitialize buffers. */ 58865668Skris buffer_init(bin); 58965668Skris buffer_init(bout); 59065668Skris buffer_init(berr); 59157429Smarkm 59257429Smarkm enter_raw_mode(); 59357429Smarkm} 59457429Smarkm 59592555Sdesstatic void 596162852Sdesclient_process_net_input(fd_set *readset) 59757429Smarkm{ 59860573Skris int len; 59960573Skris char buf[8192]; 60057429Smarkm 60157429Smarkm /* 60257429Smarkm * Read input from the server, and add any such data to the buffer of 60357429Smarkm * the packet subsystem. 60457429Smarkm */ 60557429Smarkm if (FD_ISSET(connection_in, readset)) { 60657429Smarkm /* Read as much as possible. */ 60757429Smarkm len = read(connection_in, buf, sizeof(buf)); 60857429Smarkm if (len == 0) { 60957429Smarkm /* Received EOF. The remote host has closed the connection. */ 61057429Smarkm snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n", 61157429Smarkm host); 61257429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 61357429Smarkm quit_pending = 1; 61457429Smarkm return; 61557429Smarkm } 61657429Smarkm /* 61757429Smarkm * There is a kernel bug on Solaris that causes select to 61857429Smarkm * sometimes wake up even though there is no data available. 61957429Smarkm */ 62076259Sgreen if (len < 0 && (errno == EAGAIN || errno == EINTR)) 62157429Smarkm len = 0; 62257429Smarkm 62357429Smarkm if (len < 0) { 62457429Smarkm /* An error has encountered. Perhaps there is a network problem. */ 62557429Smarkm snprintf(buf, sizeof buf, "Read from remote host %.300s: %.100s\r\n", 62657429Smarkm host, strerror(errno)); 62757429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 62857429Smarkm quit_pending = 1; 62957429Smarkm return; 63057429Smarkm } 63157429Smarkm packet_process_incoming(buf, len); 63257429Smarkm } 63360573Skris} 63460573Skris 63598675Sdesstatic void 636137015Sdesclient_subsystem_reply(int type, u_int32_t seq, void *ctxt) 637137015Sdes{ 638137015Sdes int id; 639137015Sdes Channel *c; 640137015Sdes 641137015Sdes id = packet_get_int(); 642137015Sdes packet_check_eom(); 643137015Sdes 644137015Sdes if ((c = channel_lookup(id)) == NULL) { 645137015Sdes error("%s: no channel for id %d", __func__, id); 646137015Sdes return; 647137015Sdes } 648137015Sdes 649137015Sdes if (type == SSH2_MSG_CHANNEL_SUCCESS) 650137015Sdes debug2("Request suceeded on channel %d", id); 651137015Sdes else if (type == SSH2_MSG_CHANNEL_FAILURE) { 652137015Sdes error("Request failed on channel %d", id); 653137015Sdes channel_free(c); 654137015Sdes } 655137015Sdes} 656137015Sdes 657137015Sdesstatic void 658137015Sdesclient_extra_session2_setup(int id, void *arg) 659137015Sdes{ 660137015Sdes struct confirm_ctx *cctx = arg; 661149749Sdes const char *display; 662137015Sdes Channel *c; 663137015Sdes int i; 664137015Sdes 665137015Sdes if (cctx == NULL) 666137015Sdes fatal("%s: cctx == NULL", __func__); 667137015Sdes if ((c = channel_lookup(id)) == NULL) 668137015Sdes fatal("%s: no channel for id %d", __func__, id); 669137015Sdes 670149749Sdes display = getenv("DISPLAY"); 671149749Sdes if (cctx->want_x_fwd && options.forward_x11 && display != NULL) { 672149749Sdes char *proto, *data; 673149749Sdes /* Get reasonable local authentication information. */ 674149749Sdes client_x11_get_proto(display, options.xauth_location, 675149749Sdes options.forward_x11_trusted, &proto, &data); 676149749Sdes /* Request forwarding with authentication spoofing. */ 677149749Sdes debug("Requesting X11 forwarding with authentication spoofing."); 678149749Sdes x11_request_forwarding_with_spoofing(id, display, proto, data); 679149749Sdes /* XXX wait for reply */ 680149749Sdes } 681149749Sdes 682149749Sdes if (cctx->want_agent_fwd && options.forward_agent) { 683149749Sdes debug("Requesting authentication agent forwarding."); 684149749Sdes channel_request_start(id, "auth-agent-req@openssh.com", 0); 685149749Sdes packet_send(); 686149749Sdes } 687149749Sdes 688137015Sdes client_session2_setup(id, cctx->want_tty, cctx->want_subsys, 689137015Sdes cctx->term, &cctx->tio, c->rfd, &cctx->cmd, cctx->env, 690137015Sdes client_subsystem_reply); 691137015Sdes 692137015Sdes c->confirm_ctx = NULL; 693137015Sdes buffer_free(&cctx->cmd); 694137015Sdes xfree(cctx->term); 695137015Sdes if (cctx->env != NULL) { 696137015Sdes for (i = 0; cctx->env[i] != NULL; i++) 697137015Sdes xfree(cctx->env[i]); 698137015Sdes xfree(cctx->env); 699137015Sdes } 700137015Sdes xfree(cctx); 701137015Sdes} 702137015Sdes 703137015Sdesstatic void 704162852Sdesclient_process_control(fd_set *readset) 705137015Sdes{ 706137015Sdes Buffer m; 707137015Sdes Channel *c; 708149749Sdes int client_fd, new_fd[3], ver, allowed; 709137015Sdes socklen_t addrlen; 710137015Sdes struct sockaddr_storage addr; 711137015Sdes struct confirm_ctx *cctx; 712137015Sdes char *cmd; 713149749Sdes u_int i, len, env_len, command, flags; 714137015Sdes uid_t euid; 715137015Sdes gid_t egid; 716137015Sdes 717137015Sdes /* 718137015Sdes * Accept connection on control socket 719137015Sdes */ 720137015Sdes if (control_fd == -1 || !FD_ISSET(control_fd, readset)) 721137015Sdes return; 722137015Sdes 723137015Sdes memset(&addr, 0, sizeof(addr)); 724137015Sdes addrlen = sizeof(addr); 725137015Sdes if ((client_fd = accept(control_fd, 726137015Sdes (struct sockaddr*)&addr, &addrlen)) == -1) { 727137015Sdes error("%s accept: %s", __func__, strerror(errno)); 728137015Sdes return; 729137015Sdes } 730137015Sdes 731137015Sdes if (getpeereid(client_fd, &euid, &egid) < 0) { 732137015Sdes error("%s getpeereid failed: %s", __func__, strerror(errno)); 733137015Sdes close(client_fd); 734137015Sdes return; 735137015Sdes } 736137015Sdes if ((euid != 0) && (getuid() != euid)) { 737137015Sdes error("control mode uid mismatch: peer euid %u != uid %u", 738137015Sdes (u_int) euid, (u_int) getuid()); 739137015Sdes close(client_fd); 740137015Sdes return; 741137015Sdes } 742137015Sdes 743146998Sdes unset_nonblock(client_fd); 744146998Sdes 745146998Sdes /* Read command */ 746146998Sdes buffer_init(&m); 747146998Sdes if (ssh_msg_recv(client_fd, &m) == -1) { 748146998Sdes error("%s: client msg_recv failed", __func__); 749146998Sdes close(client_fd); 750146998Sdes buffer_free(&m); 751146998Sdes return; 752146998Sdes } 753149749Sdes if ((ver = buffer_get_char(&m)) != SSHMUX_VER) { 754146998Sdes error("%s: wrong client version %d", __func__, ver); 755146998Sdes buffer_free(&m); 756146998Sdes close(client_fd); 757146998Sdes return; 758146998Sdes } 759146998Sdes 760137015Sdes allowed = 1; 761146998Sdes command = buffer_get_int(&m); 762146998Sdes flags = buffer_get_int(&m); 763137015Sdes 764146998Sdes buffer_clear(&m); 765146998Sdes 766146998Sdes switch (command) { 767146998Sdes case SSHMUX_COMMAND_OPEN: 768149749Sdes if (options.control_master == SSHCTL_MASTER_ASK || 769149749Sdes options.control_master == SSHCTL_MASTER_AUTO_ASK) 770146998Sdes allowed = ask_permission("Allow shared connection " 771146998Sdes "to %s? ", host); 772146998Sdes /* continue below */ 773146998Sdes break; 774146998Sdes case SSHMUX_COMMAND_TERMINATE: 775149749Sdes if (options.control_master == SSHCTL_MASTER_ASK || 776149749Sdes options.control_master == SSHCTL_MASTER_AUTO_ASK) 777146998Sdes allowed = ask_permission("Terminate shared connection " 778146998Sdes "to %s? ", host); 779146998Sdes if (allowed) 780146998Sdes quit_pending = 1; 781147001Sdes /* FALLTHROUGH */ 782146998Sdes case SSHMUX_COMMAND_ALIVE_CHECK: 783146998Sdes /* Reply for SSHMUX_COMMAND_TERMINATE and ALIVE_CHECK */ 784146998Sdes buffer_clear(&m); 785146998Sdes buffer_put_int(&m, allowed); 786146998Sdes buffer_put_int(&m, getpid()); 787149749Sdes if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) { 788146998Sdes error("%s: client msg_send failed", __func__); 789146998Sdes close(client_fd); 790146998Sdes buffer_free(&m); 791146998Sdes return; 792137015Sdes } 793146998Sdes buffer_free(&m); 794146998Sdes close(client_fd); 795146998Sdes return; 796146998Sdes default: 797146998Sdes error("Unsupported command %d", command); 798146998Sdes buffer_free(&m); 799146998Sdes close(client_fd); 800146998Sdes return; 801137015Sdes } 802137015Sdes 803146998Sdes /* Reply for SSHMUX_COMMAND_OPEN */ 804146998Sdes buffer_clear(&m); 805137015Sdes buffer_put_int(&m, allowed); 806137015Sdes buffer_put_int(&m, getpid()); 807149749Sdes if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) { 808137015Sdes error("%s: client msg_send failed", __func__); 809137015Sdes close(client_fd); 810137015Sdes buffer_free(&m); 811137015Sdes return; 812137015Sdes } 813137015Sdes 814137015Sdes if (!allowed) { 815137015Sdes error("Refused control connection"); 816137015Sdes close(client_fd); 817137015Sdes buffer_free(&m); 818137015Sdes return; 819137015Sdes } 820137015Sdes 821146998Sdes buffer_clear(&m); 822137015Sdes if (ssh_msg_recv(client_fd, &m) == -1) { 823137015Sdes error("%s: client msg_recv failed", __func__); 824137015Sdes close(client_fd); 825137015Sdes buffer_free(&m); 826137015Sdes return; 827137015Sdes } 828149749Sdes if ((ver = buffer_get_char(&m)) != SSHMUX_VER) { 829137015Sdes error("%s: wrong client version %d", __func__, ver); 830137015Sdes buffer_free(&m); 831137015Sdes close(client_fd); 832137015Sdes return; 833137015Sdes } 834137015Sdes 835162852Sdes cctx = xcalloc(1, sizeof(*cctx)); 836146998Sdes cctx->want_tty = (flags & SSHMUX_FLAG_TTY) != 0; 837146998Sdes cctx->want_subsys = (flags & SSHMUX_FLAG_SUBSYS) != 0; 838149749Sdes cctx->want_x_fwd = (flags & SSHMUX_FLAG_X11_FWD) != 0; 839149749Sdes cctx->want_agent_fwd = (flags & SSHMUX_FLAG_AGENT_FWD) != 0; 840137015Sdes cctx->term = buffer_get_string(&m, &len); 841137015Sdes 842137015Sdes cmd = buffer_get_string(&m, &len); 843137015Sdes buffer_init(&cctx->cmd); 844137015Sdes buffer_append(&cctx->cmd, cmd, strlen(cmd)); 845137015Sdes 846137015Sdes env_len = buffer_get_int(&m); 847137015Sdes env_len = MIN(env_len, 4096); 848137015Sdes debug3("%s: receiving %d env vars", __func__, env_len); 849137015Sdes if (env_len != 0) { 850162852Sdes cctx->env = xcalloc(env_len + 1, sizeof(*cctx->env)); 851137015Sdes for (i = 0; i < env_len; i++) 852137015Sdes cctx->env[i] = buffer_get_string(&m, &len); 853137015Sdes cctx->env[i] = NULL; 854137015Sdes } 855137015Sdes 856137015Sdes debug2("%s: accepted tty %d, subsys %d, cmd %s", __func__, 857137015Sdes cctx->want_tty, cctx->want_subsys, cmd); 858162852Sdes xfree(cmd); 859137015Sdes 860137015Sdes /* Gather fds from client */ 861137015Sdes new_fd[0] = mm_receive_fd(client_fd); 862137015Sdes new_fd[1] = mm_receive_fd(client_fd); 863137015Sdes new_fd[2] = mm_receive_fd(client_fd); 864137015Sdes 865137015Sdes debug2("%s: got fds stdin %d, stdout %d, stderr %d", __func__, 866137015Sdes new_fd[0], new_fd[1], new_fd[2]); 867137015Sdes 868137015Sdes /* Try to pick up ttymodes from client before it goes raw */ 869137015Sdes if (cctx->want_tty && tcgetattr(new_fd[0], &cctx->tio) == -1) 870137015Sdes error("%s: tcgetattr: %s", __func__, strerror(errno)); 871137015Sdes 872146998Sdes /* This roundtrip is just for synchronisation of ttymodes */ 873137015Sdes buffer_clear(&m); 874149749Sdes if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) { 875137015Sdes error("%s: client msg_send failed", __func__); 876137015Sdes close(client_fd); 877137015Sdes close(new_fd[0]); 878137015Sdes close(new_fd[1]); 879137015Sdes close(new_fd[2]); 880137015Sdes buffer_free(&m); 881146998Sdes xfree(cctx->term); 882146998Sdes if (env_len != 0) { 883146998Sdes for (i = 0; i < env_len; i++) 884146998Sdes xfree(cctx->env[i]); 885146998Sdes xfree(cctx->env); 886146998Sdes } 887137015Sdes return; 888137015Sdes } 889137015Sdes buffer_free(&m); 890137015Sdes 891137015Sdes /* enable nonblocking unless tty */ 892137015Sdes if (!isatty(new_fd[0])) 893137015Sdes set_nonblock(new_fd[0]); 894137015Sdes if (!isatty(new_fd[1])) 895137015Sdes set_nonblock(new_fd[1]); 896137015Sdes if (!isatty(new_fd[2])) 897137015Sdes set_nonblock(new_fd[2]); 898137015Sdes 899137015Sdes set_nonblock(client_fd); 900137015Sdes 901137015Sdes c = channel_new("session", SSH_CHANNEL_OPENING, 902137015Sdes new_fd[0], new_fd[1], new_fd[2], 903137015Sdes CHAN_SES_WINDOW_DEFAULT, CHAN_SES_PACKET_DEFAULT, 904137015Sdes CHAN_EXTENDED_WRITE, "client-session", /*nonblock*/0); 905137015Sdes 906137015Sdes /* XXX */ 907137015Sdes c->ctl_fd = client_fd; 908137015Sdes 909137015Sdes debug3("%s: channel_new: %d", __func__, c->self); 910137015Sdes 911137015Sdes channel_send_open(c->self); 912137015Sdes channel_register_confirm(c->self, client_extra_session2_setup, cctx); 913137015Sdes} 914137015Sdes 915137015Sdesstatic void 91698675Sdesprocess_cmdline(void) 91798675Sdes{ 91898675Sdes void (*handler)(int); 919146998Sdes char *s, *cmd, *cancel_host; 920137015Sdes int delete = 0; 92198675Sdes int local = 0; 922146998Sdes u_short cancel_port; 923146998Sdes Forward fwd; 92498675Sdes 92598675Sdes leave_raw_mode(); 92698675Sdes handler = signal(SIGINT, SIG_IGN); 92798675Sdes cmd = s = read_passphrase("\r\nssh> ", RP_ECHO); 92898675Sdes if (s == NULL) 92998675Sdes goto out; 93098675Sdes while (*s && isspace(*s)) 93198675Sdes s++; 932137015Sdes if (*s == '-') 933137015Sdes s++; /* Skip cmdline '-', if any */ 934137015Sdes if (*s == '\0') 93598675Sdes goto out; 936137015Sdes 937137015Sdes if (*s == 'h' || *s == 'H' || *s == '?') { 938137015Sdes logit("Commands:"); 939162852Sdes logit(" -L[bind_address:]port:host:hostport " 940162852Sdes "Request local forward"); 941162852Sdes logit(" -R[bind_address:]port:host:hostport " 942162852Sdes "Request remote forward"); 943162852Sdes logit(" -KR[bind_address:]port " 944162852Sdes "Cancel remote forward"); 945157016Sdes if (!options.permit_local_command) 946157016Sdes goto out; 947162852Sdes logit(" !args " 948162852Sdes "Execute local command"); 949137015Sdes goto out; 950137015Sdes } 951137015Sdes 952157016Sdes if (*s == '!' && options.permit_local_command) { 953157016Sdes s++; 954157016Sdes ssh_local_cmd(s); 955157016Sdes goto out; 956157016Sdes } 957157016Sdes 958137015Sdes if (*s == 'K') { 959137015Sdes delete = 1; 960137015Sdes s++; 961137015Sdes } 962137015Sdes if (*s != 'L' && *s != 'R') { 963124208Sdes logit("Invalid command."); 96498675Sdes goto out; 96598675Sdes } 966137015Sdes if (*s == 'L') 96798675Sdes local = 1; 968137015Sdes if (local && delete) { 969137015Sdes logit("Not supported."); 970137015Sdes goto out; 971137015Sdes } 972137015Sdes if ((!local || delete) && !compat20) { 973124208Sdes logit("Not supported for SSH protocol version 1."); 97498675Sdes goto out; 97598675Sdes } 976137015Sdes 977137015Sdes s++; 97898675Sdes while (*s && isspace(*s)) 97998675Sdes s++; 98098675Sdes 981137015Sdes if (delete) { 982146998Sdes cancel_port = 0; 983146998Sdes cancel_host = hpdelim(&s); /* may be NULL */ 984146998Sdes if (s != NULL) { 985146998Sdes cancel_port = a2port(s); 986146998Sdes cancel_host = cleanhostname(cancel_host); 987146998Sdes } else { 988146998Sdes cancel_port = a2port(cancel_host); 989146998Sdes cancel_host = NULL; 99098675Sdes } 991146998Sdes if (cancel_port == 0) { 992146998Sdes logit("Bad forwarding close port"); 993137015Sdes goto out; 994137015Sdes } 995146998Sdes channel_request_rforward_cancel(cancel_host, cancel_port); 996137015Sdes } else { 997146998Sdes if (!parse_forward(&fwd, s)) { 998137015Sdes logit("Bad forwarding specification."); 999137015Sdes goto out; 1000137015Sdes } 1001137015Sdes if (local) { 1002146998Sdes if (channel_setup_local_fwd_listener(fwd.listen_host, 1003146998Sdes fwd.listen_port, fwd.connect_host, 1004146998Sdes fwd.connect_port, options.gateway_ports) < 0) { 1005137015Sdes logit("Port forwarding failed."); 1006137015Sdes goto out; 1007137015Sdes } 1008146998Sdes } else { 1009162852Sdes if (channel_request_remote_forwarding(fwd.listen_host, 1010146998Sdes fwd.listen_port, fwd.connect_host, 1011162852Sdes fwd.connect_port) < 0) { 1012162852Sdes logit("Port forwarding failed."); 1013162852Sdes goto out; 1014162852Sdes } 1015146998Sdes } 1016146998Sdes 1017137015Sdes logit("Forwarding port."); 1018137015Sdes } 1019137015Sdes 102098675Sdesout: 102198675Sdes signal(SIGINT, handler); 102298675Sdes enter_raw_mode(); 102398675Sdes if (cmd) 102498675Sdes xfree(cmd); 102598675Sdes} 102698675Sdes 102765668Skris/* process the characters one by one */ 102892555Sdesstatic int 102965668Skrisprocess_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len) 103065668Skris{ 103165668Skris char string[1024]; 103265668Skris pid_t pid; 103365668Skris int bytes = 0; 103476259Sgreen u_int i; 103576259Sgreen u_char ch; 103665668Skris char *s; 103765668Skris 1038149749Sdes if (len <= 0) 1039149749Sdes return (0); 1040149749Sdes 1041149749Sdes for (i = 0; i < (u_int)len; i++) { 104265668Skris /* Get one character at a time. */ 104365668Skris ch = buf[i]; 104465668Skris 104565668Skris if (escape_pending) { 104665668Skris /* We have previously seen an escape character. */ 104765668Skris /* Clear the flag now. */ 104865668Skris escape_pending = 0; 104965668Skris 105065668Skris /* Process the escaped character. */ 105165668Skris switch (ch) { 105265668Skris case '.': 105365668Skris /* Terminate the connection. */ 105465668Skris snprintf(string, sizeof string, "%c.\r\n", escape_char); 105565668Skris buffer_append(berr, string, strlen(string)); 105665668Skris 105765668Skris quit_pending = 1; 105865668Skris return -1; 105965668Skris 106065668Skris case 'Z' - 64: 106165668Skris /* Suspend the program. */ 106265668Skris /* Print a message to that effect to the user. */ 106365668Skris snprintf(string, sizeof string, "%c^Z [suspend ssh]\r\n", escape_char); 106465668Skris buffer_append(berr, string, strlen(string)); 106565668Skris 106665668Skris /* Restore terminal modes and suspend. */ 106765668Skris client_suspend_self(bin, bout, berr); 106865668Skris 106965668Skris /* We have been continued. */ 107065668Skris continue; 107165668Skris 1072124208Sdes case 'B': 1073124208Sdes if (compat20) { 1074124208Sdes snprintf(string, sizeof string, 1075124208Sdes "%cB\r\n", escape_char); 1076124208Sdes buffer_append(berr, string, 1077124208Sdes strlen(string)); 1078124208Sdes channel_request_start(session_ident, 1079124208Sdes "break", 0); 1080124208Sdes packet_put_int(1000); 1081124208Sdes packet_send(); 1082124208Sdes } 1083124208Sdes continue; 1084124208Sdes 108576259Sgreen case 'R': 108676259Sgreen if (compat20) { 108776259Sgreen if (datafellows & SSH_BUG_NOREKEY) 1088124208Sdes logit("Server does not support re-keying"); 108976259Sgreen else 109076259Sgreen need_rekeying = 1; 109176259Sgreen } 109276259Sgreen continue; 109376259Sgreen 109465668Skris case '&': 109565668Skris /* 109665668Skris * Detach the program (continue to serve connections, 109765668Skris * but put in background and no more new connections). 109865668Skris */ 109965668Skris /* Restore tty modes. */ 110065668Skris leave_raw_mode(); 110165668Skris 110265668Skris /* Stop listening for new connections. */ 110365668Skris channel_stop_listening(); 110465668Skris 110592555Sdes snprintf(string, sizeof string, 110692555Sdes "%c& [backgrounded]\n", escape_char); 110792555Sdes buffer_append(berr, string, strlen(string)); 110865668Skris 110965668Skris /* Fork into background. */ 111065668Skris pid = fork(); 111165668Skris if (pid < 0) { 111265668Skris error("fork: %.100s", strerror(errno)); 111365668Skris continue; 111465668Skris } 111565668Skris if (pid != 0) { /* This is the parent. */ 111665668Skris /* The parent just exits. */ 111765668Skris exit(0); 111865668Skris } 111965668Skris /* The child continues serving connections. */ 112092555Sdes if (compat20) { 112192555Sdes buffer_append(bin, "\004", 1); 112292555Sdes /* fake EOF on stdin */ 112392555Sdes return -1; 112492555Sdes } else if (!stdin_eof) { 112592555Sdes /* 112692555Sdes * Sending SSH_CMSG_EOF alone does not always appear 112792555Sdes * to be enough. So we try to send an EOF character 112892555Sdes * first. 112992555Sdes */ 113092555Sdes packet_start(SSH_CMSG_STDIN_DATA); 113192555Sdes packet_put_string("\004", 1); 113292555Sdes packet_send(); 113392555Sdes /* Close stdin. */ 113492555Sdes stdin_eof = 1; 113592555Sdes if (buffer_len(bin) == 0) { 113692555Sdes packet_start(SSH_CMSG_EOF); 113792555Sdes packet_send(); 113892555Sdes } 113992555Sdes } 114092555Sdes continue; 114165668Skris 114265668Skris case '?': 114365668Skris snprintf(string, sizeof string, 114465668Skris"%c?\r\n\ 114565668SkrisSupported escape sequences:\r\n\ 1146106121Sdes%c. - terminate connection\r\n\ 1147124208Sdes%cB - send a BREAK to the remote system\r\n\ 1148106121Sdes%cC - open a command line\r\n\ 1149106121Sdes%cR - Request rekey (SSH protocol 2 only)\r\n\ 1150106121Sdes%c^Z - suspend ssh\r\n\ 1151106121Sdes%c# - list forwarded connections\r\n\ 1152106121Sdes%c& - background ssh (when waiting for connections to terminate)\r\n\ 1153106121Sdes%c? - this message\r\n\ 1154106121Sdes%c%c - send the escape character by typing it twice\r\n\ 115565668Skris(Note that escapes are only recognized immediately after newline.)\r\n", 1156106121Sdes escape_char, escape_char, escape_char, escape_char, 1157106121Sdes escape_char, escape_char, escape_char, escape_char, 1158124208Sdes escape_char, escape_char, escape_char); 115965668Skris buffer_append(berr, string, strlen(string)); 116065668Skris continue; 116165668Skris 116265668Skris case '#': 116365668Skris snprintf(string, sizeof string, "%c#\r\n", escape_char); 116465668Skris buffer_append(berr, string, strlen(string)); 116565668Skris s = channel_open_message(); 116665668Skris buffer_append(berr, s, strlen(s)); 116765668Skris xfree(s); 116865668Skris continue; 116965668Skris 117098675Sdes case 'C': 117198675Sdes process_cmdline(); 117298675Sdes continue; 117398675Sdes 117465668Skris default: 117565668Skris if (ch != escape_char) { 117665668Skris buffer_put_char(bin, escape_char); 117765668Skris bytes++; 117865668Skris } 117965668Skris /* Escaped characters fall through here */ 118065668Skris break; 118165668Skris } 118265668Skris } else { 118365668Skris /* 118465668Skris * The previous character was not an escape char. Check if this 118565668Skris * is an escape. 118665668Skris */ 118765668Skris if (last_was_cr && ch == escape_char) { 118865668Skris /* It is. Set the flag and continue to next character. */ 118965668Skris escape_pending = 1; 119065668Skris continue; 119165668Skris } 119265668Skris } 119365668Skris 119465668Skris /* 119565668Skris * Normal character. Record whether it was a newline, 119665668Skris * and append it to the buffer. 119765668Skris */ 119865668Skris last_was_cr = (ch == '\r' || ch == '\n'); 119965668Skris buffer_put_char(bin, ch); 120065668Skris bytes++; 120165668Skris } 120265668Skris return bytes; 120365668Skris} 120465668Skris 120592555Sdesstatic void 1206162852Sdesclient_process_input(fd_set *readset) 120760573Skris{ 120860573Skris int len; 120965668Skris char buf[8192]; 121060573Skris 121157429Smarkm /* Read input from stdin. */ 121257429Smarkm if (FD_ISSET(fileno(stdin), readset)) { 121357429Smarkm /* Read as much as possible. */ 121457429Smarkm len = read(fileno(stdin), buf, sizeof(buf)); 121576259Sgreen if (len < 0 && (errno == EAGAIN || errno == EINTR)) 121676259Sgreen return; /* we'll try again later */ 121757429Smarkm if (len <= 0) { 121857429Smarkm /* 121957429Smarkm * Received EOF or error. They are treated 122057429Smarkm * similarly, except that an error message is printed 122157429Smarkm * if it was an error condition. 122257429Smarkm */ 122357429Smarkm if (len < 0) { 122457429Smarkm snprintf(buf, sizeof buf, "read: %.100s\r\n", strerror(errno)); 122557429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 122657429Smarkm } 122757429Smarkm /* Mark that we have seen EOF. */ 122857429Smarkm stdin_eof = 1; 122957429Smarkm /* 123057429Smarkm * Send an EOF message to the server unless there is 123157429Smarkm * data in the buffer. If there is data in the 123257429Smarkm * buffer, no message will be sent now. Code 123357429Smarkm * elsewhere will send the EOF when the buffer 123457429Smarkm * becomes empty if stdin_eof is set. 123557429Smarkm */ 123657429Smarkm if (buffer_len(&stdin_buffer) == 0) { 123757429Smarkm packet_start(SSH_CMSG_EOF); 123857429Smarkm packet_send(); 123957429Smarkm } 124092555Sdes } else if (escape_char == SSH_ESCAPECHAR_NONE) { 124157429Smarkm /* 124257429Smarkm * Normal successful read, and no escape character. 124357429Smarkm * Just append the data to buffer. 124457429Smarkm */ 124557429Smarkm buffer_append(&stdin_buffer, buf, len); 124657429Smarkm } else { 124757429Smarkm /* 124857429Smarkm * Normal, successful read. But we have an escape character 124957429Smarkm * and have to process the characters one by one. 125057429Smarkm */ 125176259Sgreen if (process_escapes(&stdin_buffer, &stdout_buffer, 125276259Sgreen &stderr_buffer, buf, len) == -1) 125365668Skris return; 125457429Smarkm } 125557429Smarkm } 125657429Smarkm} 125757429Smarkm 125892555Sdesstatic void 1259162852Sdesclient_process_output(fd_set *writeset) 126057429Smarkm{ 126157429Smarkm int len; 126257429Smarkm char buf[100]; 126357429Smarkm 126457429Smarkm /* Write buffered output to stdout. */ 126557429Smarkm if (FD_ISSET(fileno(stdout), writeset)) { 126657429Smarkm /* Write as much data as possible. */ 126757429Smarkm len = write(fileno(stdout), buffer_ptr(&stdout_buffer), 126857429Smarkm buffer_len(&stdout_buffer)); 126957429Smarkm if (len <= 0) { 127076259Sgreen if (errno == EINTR || errno == EAGAIN) 127157429Smarkm len = 0; 127257429Smarkm else { 127357429Smarkm /* 127457429Smarkm * An error or EOF was encountered. Put an 127557429Smarkm * error message to stderr buffer. 127657429Smarkm */ 127757429Smarkm snprintf(buf, sizeof buf, "write stdout: %.50s\r\n", strerror(errno)); 127857429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 127957429Smarkm quit_pending = 1; 128057429Smarkm return; 128157429Smarkm } 128257429Smarkm } 128357429Smarkm /* Consume printed data from the buffer. */ 128457429Smarkm buffer_consume(&stdout_buffer, len); 128576259Sgreen stdout_bytes += len; 128657429Smarkm } 128757429Smarkm /* Write buffered output to stderr. */ 128857429Smarkm if (FD_ISSET(fileno(stderr), writeset)) { 128957429Smarkm /* Write as much data as possible. */ 129057429Smarkm len = write(fileno(stderr), buffer_ptr(&stderr_buffer), 129157429Smarkm buffer_len(&stderr_buffer)); 129257429Smarkm if (len <= 0) { 129376259Sgreen if (errno == EINTR || errno == EAGAIN) 129457429Smarkm len = 0; 129557429Smarkm else { 129657429Smarkm /* EOF or error, but can't even print error message. */ 129757429Smarkm quit_pending = 1; 129857429Smarkm return; 129957429Smarkm } 130057429Smarkm } 130157429Smarkm /* Consume printed characters from the buffer. */ 130257429Smarkm buffer_consume(&stderr_buffer, len); 130376259Sgreen stderr_bytes += len; 130457429Smarkm } 130557429Smarkm} 130657429Smarkm 130757429Smarkm/* 130860573Skris * Get packets from the connection input buffer, and process them as long as 130960573Skris * there are packets available. 131060573Skris * 131160573Skris * Any unknown packets received during the actual 131260573Skris * session cause the session to terminate. This is 131360573Skris * intended to make debugging easier since no 131460573Skris * confirmations are sent. Any compatible protocol 131560573Skris * extensions must be negotiated during the 131660573Skris * preparatory phase. 131760573Skris */ 131860573Skris 131992555Sdesstatic void 132076259Sgreenclient_process_buffered_input_packets(void) 132160573Skris{ 132276259Sgreen dispatch_run(DISPATCH_NONBLOCK, &quit_pending, compat20 ? xxx_kex : NULL); 132360573Skris} 132460573Skris 132565668Skris/* scan buf[] for '~' before sending data to the peer */ 132665668Skris 132792555Sdesstatic int 132865668Skrissimple_escape_filter(Channel *c, char *buf, int len) 132965668Skris{ 133065668Skris /* XXX we assume c->extended is writeable */ 133165668Skris return process_escapes(&c->input, &c->output, &c->extended, buf, len); 133265668Skris} 133365668Skris 133492555Sdesstatic void 133576259Sgreenclient_channel_closed(int id, void *arg) 133676259Sgreen{ 133792555Sdes channel_cancel_cleanup(id); 133876259Sgreen session_closed = 1; 1339126274Sdes leave_raw_mode(); 134076259Sgreen} 134176259Sgreen 134260573Skris/* 134357429Smarkm * Implements the interactive session with the server. This is called after 134457429Smarkm * the user has been authenticated, and a command has been started on the 134592555Sdes * remote host. If escape_char != SSH_ESCAPECHAR_NONE, it is the character 134692555Sdes * used as an escape character for terminating or suspending the session. 134757429Smarkm */ 134857429Smarkm 134960573Skrisint 135065668Skrisclient_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) 135157429Smarkm{ 135276259Sgreen fd_set *readset = NULL, *writeset = NULL; 135357429Smarkm double start_time, total_time; 1354137015Sdes int max_fd = 0, max_fd2 = 0, len, rekeying = 0; 1355137015Sdes u_int nalloc = 0; 135657429Smarkm char buf[100]; 135757429Smarkm 135857429Smarkm debug("Entering interactive session."); 135957429Smarkm 136057429Smarkm start_time = get_current_time(); 136157429Smarkm 136257429Smarkm /* Initialize variables. */ 136357429Smarkm escape_pending = 0; 136457429Smarkm last_was_cr = 1; 136557429Smarkm exit_status = -1; 136657429Smarkm stdin_eof = 0; 136757429Smarkm buffer_high = 64 * 1024; 136857429Smarkm connection_in = packet_get_connection_in(); 136957429Smarkm connection_out = packet_get_connection_out(); 137076259Sgreen max_fd = MAX(connection_in, connection_out); 1371137015Sdes if (control_fd != -1) 1372137015Sdes max_fd = MAX(max_fd, control_fd); 137376259Sgreen 137476259Sgreen if (!compat20) { 137576259Sgreen /* enable nonblocking unless tty */ 137676259Sgreen if (!isatty(fileno(stdin))) 137776259Sgreen set_nonblock(fileno(stdin)); 137876259Sgreen if (!isatty(fileno(stdout))) 137976259Sgreen set_nonblock(fileno(stdout)); 138076259Sgreen if (!isatty(fileno(stderr))) 138176259Sgreen set_nonblock(fileno(stderr)); 138276259Sgreen max_fd = MAX(max_fd, fileno(stdin)); 138376259Sgreen max_fd = MAX(max_fd, fileno(stdout)); 138476259Sgreen max_fd = MAX(max_fd, fileno(stderr)); 138576259Sgreen } 138657429Smarkm stdin_bytes = 0; 138757429Smarkm stdout_bytes = 0; 138857429Smarkm stderr_bytes = 0; 138957429Smarkm quit_pending = 0; 139057429Smarkm escape_char = escape_char_arg; 139157429Smarkm 139257429Smarkm /* Initialize buffers. */ 139357429Smarkm buffer_init(&stdin_buffer); 139457429Smarkm buffer_init(&stdout_buffer); 139557429Smarkm buffer_init(&stderr_buffer); 139657429Smarkm 139760573Skris client_init_dispatch(); 139860573Skris 1399113908Sdes /* 1400113908Sdes * Set signal handlers, (e.g. to restore non-blocking mode) 1401113908Sdes * but don't overwrite SIG_IGN, matches behaviour from rsh(1) 1402113908Sdes */ 1403146998Sdes if (signal(SIGHUP, SIG_IGN) != SIG_IGN) 1404146998Sdes signal(SIGHUP, signal_handler); 1405113908Sdes if (signal(SIGINT, SIG_IGN) != SIG_IGN) 1406113908Sdes signal(SIGINT, signal_handler); 1407113908Sdes if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) 1408113908Sdes signal(SIGQUIT, signal_handler); 1409113908Sdes if (signal(SIGTERM, SIG_IGN) != SIG_IGN) 1410113908Sdes signal(SIGTERM, signal_handler); 1411146998Sdes signal(SIGWINCH, window_change_handler); 141257429Smarkm 141357429Smarkm if (have_pty) 141457429Smarkm enter_raw_mode(); 141557429Smarkm 141676259Sgreen if (compat20) { 141776259Sgreen session_ident = ssh2_chan_id; 141892555Sdes if (escape_char != SSH_ESCAPECHAR_NONE) 141976259Sgreen channel_register_filter(session_ident, 1420157016Sdes simple_escape_filter, NULL); 142176259Sgreen if (session_ident != -1) 142276259Sgreen channel_register_cleanup(session_ident, 1423157016Sdes client_channel_closed, 0); 142476259Sgreen } else { 142576259Sgreen /* Check if we should immediately send eof on stdin. */ 142660573Skris client_check_initial_eof_on_stdin(); 142776259Sgreen } 142857429Smarkm 142957429Smarkm /* Main loop of the client for the interactive session mode. */ 143057429Smarkm while (!quit_pending) { 143157429Smarkm 143257429Smarkm /* Process buffered packets sent by the server. */ 143357429Smarkm client_process_buffered_input_packets(); 143457429Smarkm 143576259Sgreen if (compat20 && session_closed && !channel_still_open()) 143660573Skris break; 143760573Skris 143876259Sgreen rekeying = (xxx_kex != NULL && !xxx_kex->done); 143957429Smarkm 144076259Sgreen if (rekeying) { 144176259Sgreen debug("rekeying in progress"); 144276259Sgreen } else { 144376259Sgreen /* 144476259Sgreen * Make packets of buffered stdin data, and buffer 144576259Sgreen * them for sending to the server. 144676259Sgreen */ 144776259Sgreen if (!compat20) 144876259Sgreen client_make_packets_from_stdin_data(); 144957429Smarkm 145076259Sgreen /* 145176259Sgreen * Make packets from buffered channel data, and 145276259Sgreen * enqueue them for sending to the server. 145376259Sgreen */ 145476259Sgreen if (packet_not_very_much_data_to_write()) 145576259Sgreen channel_output_poll(); 145657429Smarkm 145776259Sgreen /* 145876259Sgreen * Check if the window size has changed, and buffer a 145976259Sgreen * message about it to the server if so. 146076259Sgreen */ 146176259Sgreen client_check_window_change(); 146257429Smarkm 146376259Sgreen if (quit_pending) 146476259Sgreen break; 146576259Sgreen } 146657429Smarkm /* 146757429Smarkm * Wait until we have something to do (something becomes 146857429Smarkm * available on one of the descriptors). 146957429Smarkm */ 147092555Sdes max_fd2 = max_fd; 147176259Sgreen client_wait_until_can_do_something(&readset, &writeset, 147292555Sdes &max_fd2, &nalloc, rekeying); 147357429Smarkm 147457429Smarkm if (quit_pending) 147557429Smarkm break; 147657429Smarkm 147776259Sgreen /* Do channel operations unless rekeying in progress. */ 147876259Sgreen if (!rekeying) { 147976259Sgreen channel_after_select(readset, writeset); 1480124208Sdes if (need_rekeying || packet_need_rekeying()) { 1481124208Sdes debug("need rekeying"); 148276259Sgreen xxx_kex->done = 0; 148376259Sgreen kex_send_kexinit(xxx_kex); 148476259Sgreen need_rekeying = 0; 148576259Sgreen } 148676259Sgreen } 148776259Sgreen 148860573Skris /* Buffer input from the connection. */ 148976259Sgreen client_process_net_input(readset); 149057429Smarkm 1491137015Sdes /* Accept control connections. */ 1492137015Sdes client_process_control(readset); 1493137015Sdes 149460573Skris if (quit_pending) 149560573Skris break; 149657429Smarkm 149760573Skris if (!compat20) { 149860573Skris /* Buffer data from stdin */ 149976259Sgreen client_process_input(readset); 150060573Skris /* 150160573Skris * Process output to stdout and stderr. Output to 150260573Skris * the connection is processed elsewhere (above). 150360573Skris */ 150476259Sgreen client_process_output(writeset); 150560573Skris } 150660573Skris 150757429Smarkm /* Send as much buffered packet data as possible to the sender. */ 150876259Sgreen if (FD_ISSET(connection_out, writeset)) 150957429Smarkm packet_write_poll(); 151057429Smarkm } 151176259Sgreen if (readset) 151276259Sgreen xfree(readset); 151376259Sgreen if (writeset) 151476259Sgreen xfree(writeset); 151557429Smarkm 151657429Smarkm /* Terminate the session. */ 151757429Smarkm 151857429Smarkm /* Stop watching for window change. */ 1519146998Sdes signal(SIGWINCH, SIG_DFL); 152057429Smarkm 152192555Sdes channel_free_all(); 152257429Smarkm 152392555Sdes if (have_pty) 152492555Sdes leave_raw_mode(); 152592555Sdes 152692555Sdes /* restore blocking io */ 152792555Sdes if (!isatty(fileno(stdin))) 152892555Sdes unset_nonblock(fileno(stdin)); 152992555Sdes if (!isatty(fileno(stdout))) 153092555Sdes unset_nonblock(fileno(stdout)); 153192555Sdes if (!isatty(fileno(stderr))) 153292555Sdes unset_nonblock(fileno(stderr)); 153392555Sdes 1534126274Sdes /* 1535126274Sdes * If there was no shell or command requested, there will be no remote 1536126274Sdes * exit status to be returned. In that case, clear error code if the 1537126274Sdes * connection was deliberately terminated at this end. 1538126274Sdes */ 1539126274Sdes if (no_shell_flag && received_signal == SIGTERM) { 1540126274Sdes received_signal = 0; 1541126274Sdes exit_status = 0; 154292555Sdes } 154392555Sdes 1544126274Sdes if (received_signal) 1545126274Sdes fatal("Killed by signal %d.", (int) received_signal); 1546126274Sdes 154757429Smarkm /* 154857429Smarkm * In interactive mode (with pseudo tty) display a message indicating 154957429Smarkm * that the connection has been closed. 155057429Smarkm */ 155157429Smarkm if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) { 155257429Smarkm snprintf(buf, sizeof buf, "Connection to %.64s closed.\r\n", host); 155357429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 155457429Smarkm } 155592555Sdes 155657429Smarkm /* Output any buffered data for stdout. */ 155757429Smarkm while (buffer_len(&stdout_buffer) > 0) { 155857429Smarkm len = write(fileno(stdout), buffer_ptr(&stdout_buffer), 155957429Smarkm buffer_len(&stdout_buffer)); 156057429Smarkm if (len <= 0) { 156157429Smarkm error("Write failed flushing stdout buffer."); 156257429Smarkm break; 156357429Smarkm } 156457429Smarkm buffer_consume(&stdout_buffer, len); 156576259Sgreen stdout_bytes += len; 156657429Smarkm } 156757429Smarkm 156857429Smarkm /* Output any buffered data for stderr. */ 156957429Smarkm while (buffer_len(&stderr_buffer) > 0) { 157057429Smarkm len = write(fileno(stderr), buffer_ptr(&stderr_buffer), 157157429Smarkm buffer_len(&stderr_buffer)); 157257429Smarkm if (len <= 0) { 157357429Smarkm error("Write failed flushing stderr buffer."); 157457429Smarkm break; 157557429Smarkm } 157657429Smarkm buffer_consume(&stderr_buffer, len); 157776259Sgreen stderr_bytes += len; 157857429Smarkm } 157957429Smarkm 158057429Smarkm /* Clear and free any buffers. */ 158157429Smarkm memset(buf, 0, sizeof(buf)); 158257429Smarkm buffer_free(&stdin_buffer); 158357429Smarkm buffer_free(&stdout_buffer); 158457429Smarkm buffer_free(&stderr_buffer); 158557429Smarkm 158657429Smarkm /* Report bytes transferred, and transfer rates. */ 158757429Smarkm total_time = get_current_time() - start_time; 158857429Smarkm debug("Transferred: stdin %lu, stdout %lu, stderr %lu bytes in %.1f seconds", 158992555Sdes stdin_bytes, stdout_bytes, stderr_bytes, total_time); 159057429Smarkm if (total_time > 0) 159157429Smarkm debug("Bytes per second: stdin %.1f, stdout %.1f, stderr %.1f", 159292555Sdes stdin_bytes / total_time, stdout_bytes / total_time, 159392555Sdes stderr_bytes / total_time); 159457429Smarkm 159557429Smarkm /* Return the exit status of the program. */ 159657429Smarkm debug("Exit status %d", exit_status); 159757429Smarkm return exit_status; 159857429Smarkm} 159960573Skris 160060573Skris/*********/ 160160573Skris 160292555Sdesstatic void 160392555Sdesclient_input_stdout_data(int type, u_int32_t seq, void *ctxt) 160460573Skris{ 160576259Sgreen u_int data_len; 160660573Skris char *data = packet_get_string(&data_len); 160792555Sdes packet_check_eom(); 160860573Skris buffer_append(&stdout_buffer, data, data_len); 160960573Skris memset(data, 0, data_len); 161060573Skris xfree(data); 161160573Skris} 161292555Sdesstatic void 161392555Sdesclient_input_stderr_data(int type, u_int32_t seq, void *ctxt) 161460573Skris{ 161576259Sgreen u_int data_len; 161660573Skris char *data = packet_get_string(&data_len); 161792555Sdes packet_check_eom(); 161860573Skris buffer_append(&stderr_buffer, data, data_len); 161960573Skris memset(data, 0, data_len); 162060573Skris xfree(data); 162160573Skris} 162292555Sdesstatic void 162392555Sdesclient_input_exit_status(int type, u_int32_t seq, void *ctxt) 162460573Skris{ 162560573Skris exit_status = packet_get_int(); 162692555Sdes packet_check_eom(); 162760573Skris /* Acknowledge the exit. */ 162860573Skris packet_start(SSH_CMSG_EXIT_CONFIRMATION); 162960573Skris packet_send(); 163060573Skris /* 163160573Skris * Must wait for packet to be sent since we are 163260573Skris * exiting the loop. 163360573Skris */ 163460573Skris packet_write_wait(); 163560573Skris /* Flag that we want to exit. */ 163660573Skris quit_pending = 1; 163760573Skris} 1638126274Sdesstatic void 1639126274Sdesclient_input_agent_open(int type, u_int32_t seq, void *ctxt) 1640126274Sdes{ 1641126274Sdes Channel *c = NULL; 1642126274Sdes int remote_id, sock; 164360573Skris 1644126274Sdes /* Read the remote channel number from the message. */ 1645126274Sdes remote_id = packet_get_int(); 1646126274Sdes packet_check_eom(); 1647126274Sdes 1648126274Sdes /* 1649126274Sdes * Get a connection to the local authentication agent (this may again 1650126274Sdes * get forwarded). 1651126274Sdes */ 1652126274Sdes sock = ssh_get_authentication_socket(); 1653126274Sdes 1654126274Sdes /* 1655126274Sdes * If we could not connect the agent, send an error message back to 1656126274Sdes * the server. This should never happen unless the agent dies, 1657126274Sdes * because authentication forwarding is only enabled if we have an 1658126274Sdes * agent. 1659126274Sdes */ 1660126274Sdes if (sock >= 0) { 1661126274Sdes c = channel_new("", SSH_CHANNEL_OPEN, sock, sock, 1662126274Sdes -1, 0, 0, 0, "authentication agent connection", 1); 1663126274Sdes c->remote_id = remote_id; 1664126274Sdes c->force_drain = 1; 1665126274Sdes } 1666126274Sdes if (c == NULL) { 1667126274Sdes packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 1668126274Sdes packet_put_int(remote_id); 1669126274Sdes } else { 1670126274Sdes /* Send a confirmation to the remote host. */ 1671126274Sdes debug("Forwarding authentication connection."); 1672126274Sdes packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 1673126274Sdes packet_put_int(remote_id); 1674126274Sdes packet_put_int(c->self); 1675126274Sdes } 1676126274Sdes packet_send(); 1677126274Sdes} 1678126274Sdes 167992555Sdesstatic Channel * 168076259Sgreenclient_request_forwarded_tcpip(const char *request_type, int rchan) 168176259Sgreen{ 1682106121Sdes Channel *c = NULL; 168376259Sgreen char *listen_address, *originator_address; 168476259Sgreen int listen_port, originator_port; 168592555Sdes int sock; 168676259Sgreen 168776259Sgreen /* Get rest of the packet */ 168876259Sgreen listen_address = packet_get_string(NULL); 168976259Sgreen listen_port = packet_get_int(); 169076259Sgreen originator_address = packet_get_string(NULL); 169176259Sgreen originator_port = packet_get_int(); 169292555Sdes packet_check_eom(); 169376259Sgreen 169476259Sgreen debug("client_request_forwarded_tcpip: listen %s port %d, originator %s port %d", 169576259Sgreen listen_address, listen_port, originator_address, originator_port); 169676259Sgreen 169792555Sdes sock = channel_connect_by_listen_address(listen_port); 169892555Sdes if (sock < 0) { 169992555Sdes xfree(originator_address); 170092555Sdes xfree(listen_address); 170192555Sdes return NULL; 170276259Sgreen } 170392555Sdes c = channel_new("forwarded-tcpip", 170492555Sdes SSH_CHANNEL_CONNECTING, sock, sock, -1, 170592555Sdes CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_WINDOW_DEFAULT, 0, 1706124208Sdes originator_address, 1); 170776259Sgreen xfree(originator_address); 170876259Sgreen xfree(listen_address); 170976259Sgreen return c; 171076259Sgreen} 171176259Sgreen 1712106121Sdesstatic Channel * 171376259Sgreenclient_request_x11(const char *request_type, int rchan) 171476259Sgreen{ 171576259Sgreen Channel *c = NULL; 171676259Sgreen char *originator; 171776259Sgreen int originator_port; 171892555Sdes int sock; 171976259Sgreen 172076259Sgreen if (!options.forward_x11) { 172176259Sgreen error("Warning: ssh server tried X11 forwarding."); 1722157016Sdes error("Warning: this is probably a break-in attempt by a malicious server."); 172376259Sgreen return NULL; 172476259Sgreen } 172576259Sgreen originator = packet_get_string(NULL); 172676259Sgreen if (datafellows & SSH_BUG_X11FWD) { 172776259Sgreen debug2("buggy server: x11 request w/o originator_port"); 172876259Sgreen originator_port = 0; 172976259Sgreen } else { 173076259Sgreen originator_port = packet_get_int(); 173176259Sgreen } 173292555Sdes packet_check_eom(); 173376259Sgreen /* XXX check permission */ 173476259Sgreen debug("client_request_x11: request from %s %d", originator, 173576259Sgreen originator_port); 173692555Sdes xfree(originator); 173776259Sgreen sock = x11_connect_display(); 173892555Sdes if (sock < 0) 173992555Sdes return NULL; 174092555Sdes c = channel_new("x11", 174192555Sdes SSH_CHANNEL_X11_OPEN, sock, sock, -1, 1742124208Sdes CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "x11", 1); 174392555Sdes c->force_drain = 1; 174476259Sgreen return c; 174576259Sgreen} 174676259Sgreen 1747106121Sdesstatic Channel * 174876259Sgreenclient_request_agent(const char *request_type, int rchan) 174976259Sgreen{ 175076259Sgreen Channel *c = NULL; 175192555Sdes int sock; 175276259Sgreen 175376259Sgreen if (!options.forward_agent) { 175476259Sgreen error("Warning: ssh server tried agent forwarding."); 1755157016Sdes error("Warning: this is probably a break-in attempt by a malicious server."); 175676259Sgreen return NULL; 175776259Sgreen } 175876259Sgreen sock = ssh_get_authentication_socket(); 175992555Sdes if (sock < 0) 176092555Sdes return NULL; 176192555Sdes c = channel_new("authentication agent connection", 176292555Sdes SSH_CHANNEL_OPEN, sock, sock, -1, 176392555Sdes CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_WINDOW_DEFAULT, 0, 1764124208Sdes "authentication agent connection", 1); 176592555Sdes c->force_drain = 1; 176676259Sgreen return c; 176776259Sgreen} 176876259Sgreen 176960573Skris/* XXXX move to generic input handler */ 177092555Sdesstatic void 177192555Sdesclient_input_channel_open(int type, u_int32_t seq, void *ctxt) 177260573Skris{ 177360573Skris Channel *c = NULL; 177460573Skris char *ctype; 177560573Skris int rchan; 177699060Sdes u_int rmaxpack, rwindow, len; 177760573Skris 177860573Skris ctype = packet_get_string(&len); 177960573Skris rchan = packet_get_int(); 178060573Skris rwindow = packet_get_int(); 178160573Skris rmaxpack = packet_get_int(); 178260573Skris 178360573Skris debug("client_input_channel_open: ctype %s rchan %d win %d max %d", 178460573Skris ctype, rchan, rwindow, rmaxpack); 178560573Skris 178676259Sgreen if (strcmp(ctype, "forwarded-tcpip") == 0) { 178776259Sgreen c = client_request_forwarded_tcpip(ctype, rchan); 178876259Sgreen } else if (strcmp(ctype, "x11") == 0) { 178976259Sgreen c = client_request_x11(ctype, rchan); 179076259Sgreen } else if (strcmp(ctype, "auth-agent@openssh.com") == 0) { 179176259Sgreen c = client_request_agent(ctype, rchan); 179260573Skris } 179360573Skris/* XXX duplicate : */ 179460573Skris if (c != NULL) { 179560573Skris debug("confirm %s", ctype); 179660573Skris c->remote_id = rchan; 179760573Skris c->remote_window = rwindow; 179860573Skris c->remote_maxpacket = rmaxpack; 179992555Sdes if (c->type != SSH_CHANNEL_CONNECTING) { 180092555Sdes packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); 180192555Sdes packet_put_int(c->remote_id); 180292555Sdes packet_put_int(c->self); 180392555Sdes packet_put_int(c->local_window); 180492555Sdes packet_put_int(c->local_maxpacket); 180592555Sdes packet_send(); 180692555Sdes } 180760573Skris } else { 180860573Skris debug("failure %s", ctype); 180960573Skris packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); 181060573Skris packet_put_int(rchan); 181160573Skris packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED); 181292555Sdes if (!(datafellows & SSH_BUG_OPENFAILURE)) { 181392555Sdes packet_put_cstring("open failed"); 181492555Sdes packet_put_cstring(""); 181592555Sdes } 181660573Skris packet_send(); 181760573Skris } 181860573Skris xfree(ctype); 181960573Skris} 182092555Sdesstatic void 182192555Sdesclient_input_channel_req(int type, u_int32_t seq, void *ctxt) 182276259Sgreen{ 182376259Sgreen Channel *c = NULL; 1824137015Sdes int exitval, id, reply, success = 0; 182576259Sgreen char *rtype; 182660573Skris 182776259Sgreen id = packet_get_int(); 182876259Sgreen rtype = packet_get_string(NULL); 182976259Sgreen reply = packet_get_char(); 183076259Sgreen 183176259Sgreen debug("client_input_channel_req: channel %d rtype %s reply %d", 183276259Sgreen id, rtype, reply); 183376259Sgreen 1834137015Sdes if (id == -1) { 1835137015Sdes error("client_input_channel_req: request for channel -1"); 1836137015Sdes } else if ((c = channel_lookup(id)) == NULL) { 183776259Sgreen error("client_input_channel_req: channel %d: unknown channel", id); 183876259Sgreen } else if (strcmp(rtype, "exit-status") == 0) { 1839137015Sdes exitval = packet_get_int(); 1840137015Sdes if (id == session_ident) { 1841137015Sdes success = 1; 1842137015Sdes exit_status = exitval; 1843137015Sdes } else if (c->ctl_fd == -1) { 1844137015Sdes error("client_input_channel_req: unexpected channel %d", 1845137015Sdes session_ident); 1846137015Sdes } else { 1847137015Sdes atomicio(vwrite, c->ctl_fd, &exitval, sizeof(exitval)); 1848137015Sdes success = 1; 1849137015Sdes } 185092555Sdes packet_check_eom(); 185176259Sgreen } 185276259Sgreen if (reply) { 185376259Sgreen packet_start(success ? 185476259Sgreen SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); 1855137015Sdes packet_put_int(id); 185676259Sgreen packet_send(); 185776259Sgreen } 185876259Sgreen xfree(rtype); 185976259Sgreen} 186092555Sdesstatic void 186192555Sdesclient_input_global_request(int type, u_int32_t seq, void *ctxt) 186292555Sdes{ 186392555Sdes char *rtype; 186492555Sdes int want_reply; 186592555Sdes int success = 0; 186676259Sgreen 186792555Sdes rtype = packet_get_string(NULL); 186892555Sdes want_reply = packet_get_char(); 1869126274Sdes debug("client_input_global_request: rtype %s want_reply %d", 1870126274Sdes rtype, want_reply); 187192555Sdes if (want_reply) { 187292555Sdes packet_start(success ? 187392555Sdes SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE); 187492555Sdes packet_send(); 187592555Sdes packet_write_wait(); 187692555Sdes } 187792555Sdes xfree(rtype); 187892555Sdes} 187992555Sdes 1880137015Sdesvoid 1881137015Sdesclient_session2_setup(int id, int want_tty, int want_subsystem, 1882137015Sdes const char *term, struct termios *tiop, int in_fd, Buffer *cmd, char **env, 1883137015Sdes dispatch_fn *subsys_repl) 1884137015Sdes{ 1885137015Sdes int len; 1886146998Sdes Channel *c = NULL; 1887137015Sdes 1888137015Sdes debug2("%s: id %d", __func__, id); 1889137015Sdes 1890146998Sdes if ((c = channel_lookup(id)) == NULL) 1891146998Sdes fatal("client_session2_setup: channel %d: unknown channel", id); 1892146998Sdes 1893137015Sdes if (want_tty) { 1894137015Sdes struct winsize ws; 1895137015Sdes struct termios tio; 1896137015Sdes 1897137015Sdes /* Store window size in the packet. */ 1898137015Sdes if (ioctl(in_fd, TIOCGWINSZ, &ws) < 0) 1899137015Sdes memset(&ws, 0, sizeof(ws)); 1900137015Sdes 1901137015Sdes channel_request_start(id, "pty-req", 0); 1902137015Sdes packet_put_cstring(term != NULL ? term : ""); 1903162852Sdes packet_put_int((u_int)ws.ws_col); 1904162852Sdes packet_put_int((u_int)ws.ws_row); 1905162852Sdes packet_put_int((u_int)ws.ws_xpixel); 1906162852Sdes packet_put_int((u_int)ws.ws_ypixel); 1907137015Sdes tio = get_saved_tio(); 1908137015Sdes tty_make_modes(-1, tiop != NULL ? tiop : &tio); 1909137015Sdes packet_send(); 1910137015Sdes /* XXX wait for reply */ 1911146998Sdes c->client_tty = 1; 1912137015Sdes } 1913137015Sdes 1914137015Sdes /* Transfer any environment variables from client to server */ 1915137015Sdes if (options.num_send_env != 0 && env != NULL) { 1916137015Sdes int i, j, matched; 1917137015Sdes char *name, *val; 1918137015Sdes 1919137015Sdes debug("Sending environment."); 1920137015Sdes for (i = 0; env[i] != NULL; i++) { 1921137015Sdes /* Split */ 1922137015Sdes name = xstrdup(env[i]); 1923137015Sdes if ((val = strchr(name, '=')) == NULL) { 1924157016Sdes xfree(name); 1925137015Sdes continue; 1926137015Sdes } 1927137015Sdes *val++ = '\0'; 1928137015Sdes 1929137015Sdes matched = 0; 1930137015Sdes for (j = 0; j < options.num_send_env; j++) { 1931137015Sdes if (match_pattern(name, options.send_env[j])) { 1932137015Sdes matched = 1; 1933137015Sdes break; 1934137015Sdes } 1935137015Sdes } 1936137015Sdes if (!matched) { 1937137015Sdes debug3("Ignored env %s", name); 1938157016Sdes xfree(name); 1939137015Sdes continue; 1940137015Sdes } 1941137015Sdes 1942137015Sdes debug("Sending env %s = %s", name, val); 1943137015Sdes channel_request_start(id, "env", 0); 1944137015Sdes packet_put_cstring(name); 1945137015Sdes packet_put_cstring(val); 1946137015Sdes packet_send(); 1947157016Sdes xfree(name); 1948137015Sdes } 1949137015Sdes } 1950137015Sdes 1951137015Sdes len = buffer_len(cmd); 1952137015Sdes if (len > 0) { 1953137015Sdes if (len > 900) 1954137015Sdes len = 900; 1955137015Sdes if (want_subsystem) { 1956137015Sdes debug("Sending subsystem: %.*s", len, (u_char*)buffer_ptr(cmd)); 1957137015Sdes channel_request_start(id, "subsystem", subsys_repl != NULL); 1958137015Sdes if (subsys_repl != NULL) { 1959137015Sdes /* register callback for reply */ 1960137015Sdes /* XXX we assume that client_loop has already been called */ 1961137015Sdes dispatch_set(SSH2_MSG_CHANNEL_FAILURE, subsys_repl); 1962137015Sdes dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, subsys_repl); 1963137015Sdes } 1964137015Sdes } else { 1965137015Sdes debug("Sending command: %.*s", len, (u_char*)buffer_ptr(cmd)); 1966137015Sdes channel_request_start(id, "exec", 0); 1967137015Sdes } 1968137015Sdes packet_put_string(buffer_ptr(cmd), buffer_len(cmd)); 1969137015Sdes packet_send(); 1970137015Sdes } else { 1971137015Sdes channel_request_start(id, "shell", 0); 1972137015Sdes packet_send(); 1973137015Sdes } 1974137015Sdes} 1975137015Sdes 197692555Sdesstatic void 197776259Sgreenclient_init_dispatch_20(void) 197860573Skris{ 197960573Skris dispatch_init(&dispatch_protocol_error); 198098675Sdes 198160573Skris dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); 198260573Skris dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data); 198360573Skris dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); 198460573Skris dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); 198560573Skris dispatch_set(SSH2_MSG_CHANNEL_OPEN, &client_input_channel_open); 198660573Skris dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); 198760573Skris dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); 198876259Sgreen dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &client_input_channel_req); 198960573Skris dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); 199092555Sdes dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &client_input_global_request); 199176259Sgreen 199276259Sgreen /* rekeying */ 199376259Sgreen dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit); 199498675Sdes 199598675Sdes /* global request reply messages */ 199698675Sdes dispatch_set(SSH2_MSG_REQUEST_FAILURE, &client_global_request_reply); 199798675Sdes dispatch_set(SSH2_MSG_REQUEST_SUCCESS, &client_global_request_reply); 199860573Skris} 199992555Sdesstatic void 200076259Sgreenclient_init_dispatch_13(void) 200160573Skris{ 200260573Skris dispatch_init(NULL); 200360573Skris dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close); 200460573Skris dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation); 200560573Skris dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data); 200660573Skris dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); 200760573Skris dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); 200860573Skris dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open); 200960573Skris dispatch_set(SSH_SMSG_EXITSTATUS, &client_input_exit_status); 201060573Skris dispatch_set(SSH_SMSG_STDERR_DATA, &client_input_stderr_data); 201160573Skris dispatch_set(SSH_SMSG_STDOUT_DATA, &client_input_stdout_data); 201268700Sgreen 201368700Sgreen dispatch_set(SSH_SMSG_AGENT_OPEN, options.forward_agent ? 2014126274Sdes &client_input_agent_open : &deny_input_open); 201568700Sgreen dispatch_set(SSH_SMSG_X11_OPEN, options.forward_x11 ? 201669587Sgreen &x11_input_open : &deny_input_open); 201760573Skris} 201892555Sdesstatic void 201976259Sgreenclient_init_dispatch_15(void) 202060573Skris{ 202160573Skris client_init_dispatch_13(); 202260573Skris dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof); 202360573Skris dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, & channel_input_oclose); 202460573Skris} 202592555Sdesstatic void 202676259Sgreenclient_init_dispatch(void) 202760573Skris{ 202860573Skris if (compat20) 202960573Skris client_init_dispatch_20(); 203060573Skris else if (compat13) 203160573Skris client_init_dispatch_13(); 203260573Skris else 203360573Skris client_init_dispatch_15(); 203460573Skris} 2035126274Sdes 2036126274Sdes/* client specific fatal cleanup */ 2037126274Sdesvoid 2038126274Sdescleanup_exit(int i) 2039126274Sdes{ 2040126274Sdes leave_raw_mode(); 2041126274Sdes leave_non_blocking(); 2042137015Sdes if (options.control_path != NULL && control_fd != -1) 2043137015Sdes unlink(options.control_path); 2044126274Sdes _exit(i); 2045126274Sdes} 2046