clientloop.c revision 192595
1192595Sdes/* $OpenBSD: clientloop.c,v 1.209 2009/02/12 03:00:56 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" 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 */ 123181111Sdesextern int muxserver_sock; 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. */ 146181111Sdesstatic int escape_char1; /* Escape character. (proto1 only) */ 147181111Sdesstatic int escape_pending1; /* Last character was an escape (proto1 only) */ 14857429Smarkmstatic int last_was_cr; /* Last character was a newline. */ 149181111Sdesstatic int exit_status; /* Used to store the command exit status. */ 150181111Sdesstatic int stdin_eof; /* EOF has been encountered on stderr. */ 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_int buffer_high;/* Soft max buffer size. */ 15557429Smarkmstatic int connection_in; /* Connection to server (input). */ 15657429Smarkmstatic int connection_out; /* Connection to server (output). */ 15776259Sgreenstatic int need_rekeying; /* Set to non-zero if rekeying is requested. */ 15876259Sgreenstatic int session_closed = 0; /* In SSH2: login session closed. */ 15957429Smarkm 16092555Sdesstatic void client_init_dispatch(void); 16160573Skrisint session_ident = -1; 16260573Skris 163181111Sdes/* Track escape per proto2 channel */ 164181111Sdesstruct escape_filter_ctx { 165181111Sdes int escape_pending; 166181111Sdes int escape_char; 167137015Sdes}; 168137015Sdes 169181111Sdes/* Context for channel confirmation replies */ 170181111Sdesstruct channel_reply_ctx { 171181111Sdes const char *request_type; 172181111Sdes int id, do_close; 173181111Sdes}; 174181111Sdes 175181111Sdes/* Global request success/failure callbacks */ 176181111Sdesstruct global_confirm { 177181111Sdes TAILQ_ENTRY(global_confirm) entry; 178181111Sdes global_confirm_cb *cb; 179181111Sdes void *ctx; 180181111Sdes int ref_count; 181181111Sdes}; 182181111SdesTAILQ_HEAD(global_confirms, global_confirm); 183181111Sdesstatic struct global_confirms global_confirms = 184181111Sdes TAILQ_HEAD_INITIALIZER(global_confirms); 185181111Sdes 18676259Sgreen/*XXX*/ 18776259Sgreenextern Kex *xxx_kex; 18857429Smarkm 189137015Sdesvoid ssh_process_session2_setup(int, int, int, Buffer *); 190137015Sdes 19157429Smarkm/* Restores stdin to blocking mode. */ 19257429Smarkm 19392555Sdesstatic void 19476259Sgreenleave_non_blocking(void) 19557429Smarkm{ 19657429Smarkm if (in_non_blocking_mode) { 197137015Sdes unset_nonblock(fileno(stdin)); 19857429Smarkm in_non_blocking_mode = 0; 19957429Smarkm } 20057429Smarkm} 20157429Smarkm 20257429Smarkm/* Puts stdin terminal in non-blocking mode. */ 20357429Smarkm 20492555Sdesstatic void 20576259Sgreenenter_non_blocking(void) 20657429Smarkm{ 20757429Smarkm in_non_blocking_mode = 1; 208137015Sdes set_nonblock(fileno(stdin)); 20957429Smarkm} 21057429Smarkm 21157429Smarkm/* 21257429Smarkm * Signal handler for the window change signal (SIGWINCH). This just sets a 21357429Smarkm * flag indicating that the window has changed. 21457429Smarkm */ 215162852Sdes/*ARGSUSED */ 21692555Sdesstatic void 21757429Smarkmwindow_change_handler(int sig) 21857429Smarkm{ 21957429Smarkm received_window_change_signal = 1; 22057429Smarkm signal(SIGWINCH, window_change_handler); 22157429Smarkm} 22257429Smarkm 22357429Smarkm/* 22457429Smarkm * Signal handler for signals that cause the program to terminate. These 22557429Smarkm * signals must be trapped to restore terminal modes. 22657429Smarkm */ 227162852Sdes/*ARGSUSED */ 22892555Sdesstatic void 22957429Smarkmsignal_handler(int sig) 23057429Smarkm{ 23192555Sdes received_signal = sig; 23292555Sdes quit_pending = 1; 23357429Smarkm} 23457429Smarkm 23557429Smarkm/* 23657429Smarkm * Returns current time in seconds from Jan 1, 1970 with the maximum 23757429Smarkm * available resolution. 23857429Smarkm */ 23957429Smarkm 24092555Sdesstatic double 24176259Sgreenget_current_time(void) 24257429Smarkm{ 24357429Smarkm struct timeval tv; 24457429Smarkm gettimeofday(&tv, NULL); 24557429Smarkm return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0; 24657429Smarkm} 24757429Smarkm 248149749Sdes#define SSH_X11_PROTO "MIT-MAGIC-COOKIE-1" 249149749Sdesvoid 250149749Sdesclient_x11_get_proto(const char *display, const char *xauth_path, 251149749Sdes u_int trusted, char **_proto, char **_data) 252149749Sdes{ 253149749Sdes char cmd[1024]; 254149749Sdes char line[512]; 255149749Sdes char xdisplay[512]; 256149749Sdes static char proto[512], data[512]; 257149749Sdes FILE *f; 258149749Sdes int got_data = 0, generated = 0, do_unlink = 0, i; 259149749Sdes char *xauthdir, *xauthfile; 260149749Sdes struct stat st; 261149749Sdes 262149749Sdes xauthdir = xauthfile = NULL; 263149749Sdes *_proto = proto; 264149749Sdes *_data = data; 265149749Sdes proto[0] = data[0] = '\0'; 266149749Sdes 267149749Sdes if (xauth_path == NULL ||(stat(xauth_path, &st) == -1)) { 268149749Sdes debug("No xauth program."); 269149749Sdes } else { 270149749Sdes if (display == NULL) { 271149749Sdes debug("x11_get_proto: DISPLAY not set"); 272149749Sdes return; 273149749Sdes } 274149749Sdes /* 275149749Sdes * Handle FamilyLocal case where $DISPLAY does 276149749Sdes * not match an authorization entry. For this we 277149749Sdes * just try "xauth list unix:displaynum.screennum". 278149749Sdes * XXX: "localhost" match to determine FamilyLocal 279149749Sdes * is not perfect. 280149749Sdes */ 281149749Sdes if (strncmp(display, "localhost:", 10) == 0) { 282149749Sdes snprintf(xdisplay, sizeof(xdisplay), "unix:%s", 283149749Sdes display + 10); 284149749Sdes display = xdisplay; 285149749Sdes } 286149749Sdes if (trusted == 0) { 287149749Sdes xauthdir = xmalloc(MAXPATHLEN); 288149749Sdes xauthfile = xmalloc(MAXPATHLEN); 289149749Sdes strlcpy(xauthdir, "/tmp/ssh-XXXXXXXXXX", MAXPATHLEN); 290149749Sdes if (mkdtemp(xauthdir) != NULL) { 291149749Sdes do_unlink = 1; 292149749Sdes snprintf(xauthfile, MAXPATHLEN, "%s/xauthfile", 293149749Sdes xauthdir); 294149749Sdes snprintf(cmd, sizeof(cmd), 295149749Sdes "%s -f %s generate %s " SSH_X11_PROTO 296149749Sdes " untrusted timeout 1200 2>" _PATH_DEVNULL, 297149749Sdes xauth_path, xauthfile, display); 298149749Sdes debug2("x11_get_proto: %s", cmd); 299149749Sdes if (system(cmd) == 0) 300149749Sdes generated = 1; 301149749Sdes } 302149749Sdes } 303181111Sdes 304181111Sdes /* 305181111Sdes * When in untrusted mode, we read the cookie only if it was 306181111Sdes * successfully generated as an untrusted one in the step 307181111Sdes * above. 308181111Sdes */ 309181111Sdes if (trusted || generated) { 310181111Sdes snprintf(cmd, sizeof(cmd), 311181111Sdes "%s %s%s list %s 2>" _PATH_DEVNULL, 312181111Sdes xauth_path, 313181111Sdes generated ? "-f " : "" , 314181111Sdes generated ? xauthfile : "", 315181111Sdes display); 316181111Sdes debug2("x11_get_proto: %s", cmd); 317181111Sdes f = popen(cmd, "r"); 318181111Sdes if (f && fgets(line, sizeof(line), f) && 319181111Sdes sscanf(line, "%*s %511s %511s", proto, data) == 2) 320181111Sdes got_data = 1; 321181111Sdes if (f) 322181111Sdes pclose(f); 323181111Sdes } else 324181111Sdes error("Warning: untrusted X11 forwarding setup failed: " 325181111Sdes "xauth key data not generated"); 326149749Sdes } 327149749Sdes 328149749Sdes if (do_unlink) { 329149749Sdes unlink(xauthfile); 330149749Sdes rmdir(xauthdir); 331149749Sdes } 332149749Sdes if (xauthdir) 333149749Sdes xfree(xauthdir); 334149749Sdes if (xauthfile) 335149749Sdes xfree(xauthfile); 336149749Sdes 337149749Sdes /* 338149749Sdes * If we didn't get authentication data, just make up some 339149749Sdes * data. The forwarding code will check the validity of the 340149749Sdes * response anyway, and substitute this data. The X11 341149749Sdes * server, however, will ignore this fake data and use 342149749Sdes * whatever authentication mechanisms it was using otherwise 343149749Sdes * for the local connection. 344149749Sdes */ 345149749Sdes if (!got_data) { 346149749Sdes u_int32_t rnd = 0; 347149749Sdes 348149749Sdes logit("Warning: No xauth data; " 349149749Sdes "using fake authentication data for X11 forwarding."); 350149749Sdes strlcpy(proto, SSH_X11_PROTO, sizeof proto); 351149749Sdes for (i = 0; i < 16; i++) { 352149749Sdes if (i % 4 == 0) 353149749Sdes rnd = arc4random(); 354149749Sdes snprintf(data + 2 * i, sizeof data - 2 * i, "%02x", 355149749Sdes rnd & 0xff); 356149749Sdes rnd >>= 8; 357149749Sdes } 358149749Sdes } 359149749Sdes} 360149749Sdes 36157429Smarkm/* 36257429Smarkm * This is called when the interactive is entered. This checks if there is 36357429Smarkm * an EOF coming on stdin. We must check this explicitly, as select() does 36457429Smarkm * not appear to wake up when redirecting from /dev/null. 36557429Smarkm */ 36657429Smarkm 36792555Sdesstatic void 36876259Sgreenclient_check_initial_eof_on_stdin(void) 36957429Smarkm{ 37057429Smarkm int len; 37157429Smarkm char buf[1]; 37257429Smarkm 37357429Smarkm /* 37457429Smarkm * If standard input is to be "redirected from /dev/null", we simply 37557429Smarkm * mark that we have seen an EOF and send an EOF message to the 37657429Smarkm * server. Otherwise, we try to read a single character; it appears 37757429Smarkm * that for some files, such /dev/null, select() never wakes up for 37857429Smarkm * read for this descriptor, which means that we never get EOF. This 37957429Smarkm * way we will get the EOF if stdin comes from /dev/null or similar. 38057429Smarkm */ 38157429Smarkm if (stdin_null_flag) { 38257429Smarkm /* Fake EOF on stdin. */ 38357429Smarkm debug("Sending eof."); 38457429Smarkm stdin_eof = 1; 38557429Smarkm packet_start(SSH_CMSG_EOF); 38657429Smarkm packet_send(); 38757429Smarkm } else { 38857429Smarkm enter_non_blocking(); 38957429Smarkm 39057429Smarkm /* Check for immediate EOF on stdin. */ 39157429Smarkm len = read(fileno(stdin), buf, 1); 39257429Smarkm if (len == 0) { 393181111Sdes /* 394181111Sdes * EOF. Record that we have seen it and send 395181111Sdes * EOF to server. 396181111Sdes */ 39757429Smarkm debug("Sending eof."); 39857429Smarkm stdin_eof = 1; 39957429Smarkm packet_start(SSH_CMSG_EOF); 40057429Smarkm packet_send(); 40157429Smarkm } else if (len > 0) { 40257429Smarkm /* 40357429Smarkm * Got data. We must store the data in the buffer, 40457429Smarkm * and also process it as an escape character if 40557429Smarkm * appropriate. 40657429Smarkm */ 407181111Sdes if ((u_char) buf[0] == escape_char1) 408181111Sdes escape_pending1 = 1; 40976259Sgreen else 41057429Smarkm buffer_append(&stdin_buffer, buf, 1); 41157429Smarkm } 41257429Smarkm leave_non_blocking(); 41357429Smarkm } 41457429Smarkm} 41557429Smarkm 41657429Smarkm 41757429Smarkm/* 41857429Smarkm * Make packets from buffered stdin data, and buffer them for sending to the 41957429Smarkm * connection. 42057429Smarkm */ 42157429Smarkm 42292555Sdesstatic void 42376259Sgreenclient_make_packets_from_stdin_data(void) 42457429Smarkm{ 42576259Sgreen u_int len; 42657429Smarkm 42757429Smarkm /* Send buffered stdin data to the server. */ 42857429Smarkm while (buffer_len(&stdin_buffer) > 0 && 42992555Sdes packet_not_very_much_data_to_write()) { 43057429Smarkm len = buffer_len(&stdin_buffer); 43157429Smarkm /* Keep the packets at reasonable size. */ 43257429Smarkm if (len > packet_get_maxsize()) 43357429Smarkm len = packet_get_maxsize(); 43457429Smarkm packet_start(SSH_CMSG_STDIN_DATA); 43557429Smarkm packet_put_string(buffer_ptr(&stdin_buffer), len); 43657429Smarkm packet_send(); 43757429Smarkm buffer_consume(&stdin_buffer, len); 43857429Smarkm /* If we have a pending EOF, send it now. */ 43957429Smarkm if (stdin_eof && buffer_len(&stdin_buffer) == 0) { 44057429Smarkm packet_start(SSH_CMSG_EOF); 44157429Smarkm packet_send(); 44257429Smarkm } 44357429Smarkm } 44457429Smarkm} 44557429Smarkm 44657429Smarkm/* 44757429Smarkm * Checks if the client window has changed, and sends a packet about it to 44857429Smarkm * the server if so. The actual change is detected elsewhere (by a software 44957429Smarkm * interrupt on Unix); this just checks the flag and sends a message if 45057429Smarkm * appropriate. 45157429Smarkm */ 45257429Smarkm 45392555Sdesstatic void 45476259Sgreenclient_check_window_change(void) 45557429Smarkm{ 45660573Skris struct winsize ws; 45757429Smarkm 45860573Skris if (! received_window_change_signal) 45960573Skris return; 46060573Skris /** XXX race */ 46160573Skris received_window_change_signal = 0; 46257429Smarkm 46369587Sgreen debug2("client_check_window_change: changed"); 46460573Skris 46560573Skris if (compat20) { 466137015Sdes channel_send_window_changes(); 46760573Skris } else { 468137015Sdes if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0) 469137015Sdes return; 47060573Skris packet_start(SSH_CMSG_WINDOW_SIZE); 471162852Sdes packet_put_int((u_int)ws.ws_row); 472162852Sdes packet_put_int((u_int)ws.ws_col); 473162852Sdes packet_put_int((u_int)ws.ws_xpixel); 474162852Sdes packet_put_int((u_int)ws.ws_ypixel); 47560573Skris packet_send(); 47657429Smarkm } 47757429Smarkm} 47857429Smarkm 479126274Sdesstatic void 480126274Sdesclient_global_request_reply(int type, u_int32_t seq, void *ctxt) 481126274Sdes{ 482181111Sdes struct global_confirm *gc; 483181111Sdes 484181111Sdes if ((gc = TAILQ_FIRST(&global_confirms)) == NULL) 485181111Sdes return; 486181111Sdes if (gc->cb != NULL) 487181111Sdes gc->cb(type, seq, gc->ctx); 488181111Sdes if (--gc->ref_count <= 0) { 489181111Sdes TAILQ_REMOVE(&global_confirms, gc, entry); 490181111Sdes bzero(gc, sizeof(*gc)); 491181111Sdes xfree(gc); 492181111Sdes } 493181111Sdes 494181111Sdes keep_alive_timeouts = 0; 495126274Sdes} 496126274Sdes 497126274Sdesstatic void 498126274Sdesserver_alive_check(void) 499126274Sdes{ 500181111Sdes if (++keep_alive_timeouts > options.server_alive_count_max) { 501164146Sdes logit("Timeout, server not responding."); 502164146Sdes cleanup_exit(255); 503164146Sdes } 504126274Sdes packet_start(SSH2_MSG_GLOBAL_REQUEST); 505126274Sdes packet_put_cstring("keepalive@openssh.com"); 506126274Sdes packet_put_char(1); /* boolean: want reply */ 507126274Sdes packet_send(); 508181111Sdes /* Insert an empty placeholder to maintain ordering */ 509181111Sdes client_register_global_confirm(NULL, NULL); 510126274Sdes} 511126274Sdes 51257429Smarkm/* 51357429Smarkm * Waits until the client can do something (some data becomes available on 51457429Smarkm * one of the file descriptors). 51557429Smarkm */ 51692555Sdesstatic void 51776259Sgreenclient_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, 518137015Sdes int *maxfdp, u_int *nallocp, int rekeying) 51957429Smarkm{ 520126274Sdes struct timeval tv, *tvp; 521126274Sdes int ret; 522126274Sdes 52376259Sgreen /* Add any selections by the channel mechanism. */ 52492555Sdes channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, rekeying); 52557429Smarkm 52660573Skris if (!compat20) { 52760573Skris /* Read from the connection, unless our buffers are full. */ 52860573Skris if (buffer_len(&stdout_buffer) < buffer_high && 52960573Skris buffer_len(&stderr_buffer) < buffer_high && 53060573Skris channel_not_very_much_buffered_data()) 53176259Sgreen FD_SET(connection_in, *readsetp); 53260573Skris /* 53360573Skris * Read from stdin, unless we have seen EOF or have very much 53460573Skris * buffered data to send to the server. 53560573Skris */ 53660573Skris if (!stdin_eof && packet_not_very_much_data_to_write()) 53776259Sgreen FD_SET(fileno(stdin), *readsetp); 53860573Skris 53960573Skris /* Select stdout/stderr if have data in buffer. */ 54060573Skris if (buffer_len(&stdout_buffer) > 0) 54176259Sgreen FD_SET(fileno(stdout), *writesetp); 54260573Skris if (buffer_len(&stderr_buffer) > 0) 54376259Sgreen FD_SET(fileno(stderr), *writesetp); 54460573Skris } else { 54592555Sdes /* channel_prepare_select could have closed the last channel */ 54692555Sdes if (session_closed && !channel_still_open() && 54792555Sdes !packet_have_data_to_write()) { 54892555Sdes /* clear mask since we did not call select() */ 54992555Sdes memset(*readsetp, 0, *nallocp); 55092555Sdes memset(*writesetp, 0, *nallocp); 55192555Sdes return; 55292555Sdes } else { 55392555Sdes FD_SET(connection_in, *readsetp); 55492555Sdes } 55560573Skris } 55657429Smarkm 55757429Smarkm /* Select server connection if have data to write to the server. */ 55857429Smarkm if (packet_have_data_to_write()) 55976259Sgreen FD_SET(connection_out, *writesetp); 56057429Smarkm 561181111Sdes if (muxserver_sock != -1) 562181111Sdes FD_SET(muxserver_sock, *readsetp); 563137015Sdes 56457429Smarkm /* 56557429Smarkm * Wait for something to happen. This will suspend the process until 56657429Smarkm * some selected descriptor can be read, written, or has some other 567126274Sdes * event pending. 56857429Smarkm */ 56957429Smarkm 570126274Sdes if (options.server_alive_interval == 0 || !compat20) 571126274Sdes tvp = NULL; 572137015Sdes else { 573126274Sdes tv.tv_sec = options.server_alive_interval; 574126274Sdes tv.tv_usec = 0; 575126274Sdes tvp = &tv; 576126274Sdes } 577126274Sdes ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp); 578126274Sdes if (ret < 0) { 57957429Smarkm char buf[100]; 58076259Sgreen 58176259Sgreen /* 58276259Sgreen * We have to clear the select masks, because we return. 58376259Sgreen * We have to return, because the mainloop checks for the flags 58476259Sgreen * set by the signal handlers. 58576259Sgreen */ 58692555Sdes memset(*readsetp, 0, *nallocp); 58792555Sdes memset(*writesetp, 0, *nallocp); 58876259Sgreen 58957429Smarkm if (errno == EINTR) 59057429Smarkm return; 59157429Smarkm /* Note: we might still have data in the buffers. */ 59257429Smarkm snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno)); 59357429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 59457429Smarkm quit_pending = 1; 595126274Sdes } else if (ret == 0) 596126274Sdes server_alive_check(); 59757429Smarkm} 59857429Smarkm 59992555Sdesstatic void 60065668Skrisclient_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr) 60157429Smarkm{ 60257429Smarkm /* Flush stdout and stderr buffers. */ 60365668Skris if (buffer_len(bout) > 0) 604181111Sdes atomicio(vwrite, fileno(stdout), buffer_ptr(bout), 605181111Sdes buffer_len(bout)); 60665668Skris if (buffer_len(berr) > 0) 607181111Sdes atomicio(vwrite, fileno(stderr), buffer_ptr(berr), 608181111Sdes buffer_len(berr)); 60957429Smarkm 61057429Smarkm leave_raw_mode(); 61157429Smarkm 61257429Smarkm /* 61357429Smarkm * Free (and clear) the buffer to reduce the amount of data that gets 61457429Smarkm * written to swap. 61557429Smarkm */ 61665668Skris buffer_free(bin); 61765668Skris buffer_free(bout); 61865668Skris buffer_free(berr); 61957429Smarkm 62057429Smarkm /* Send the suspend signal to the program itself. */ 62157429Smarkm kill(getpid(), SIGTSTP); 62257429Smarkm 623146998Sdes /* Reset window sizes in case they have changed */ 624146998Sdes received_window_change_signal = 1; 62557429Smarkm 62657429Smarkm /* OK, we have been continued by the user. Reinitialize buffers. */ 62765668Skris buffer_init(bin); 62865668Skris buffer_init(bout); 62965668Skris buffer_init(berr); 63057429Smarkm 63157429Smarkm enter_raw_mode(); 63257429Smarkm} 63357429Smarkm 63492555Sdesstatic void 635162852Sdesclient_process_net_input(fd_set *readset) 63657429Smarkm{ 63760573Skris int len; 63860573Skris char buf[8192]; 63957429Smarkm 64057429Smarkm /* 64157429Smarkm * Read input from the server, and add any such data to the buffer of 64257429Smarkm * the packet subsystem. 64357429Smarkm */ 64457429Smarkm if (FD_ISSET(connection_in, readset)) { 64557429Smarkm /* Read as much as possible. */ 64657429Smarkm len = read(connection_in, buf, sizeof(buf)); 64757429Smarkm if (len == 0) { 648181111Sdes /* 649181111Sdes * Received EOF. The remote host has closed the 650181111Sdes * connection. 651181111Sdes */ 652181111Sdes snprintf(buf, sizeof buf, 653181111Sdes "Connection to %.300s closed by remote host.\r\n", 654181111Sdes host); 65557429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 65657429Smarkm quit_pending = 1; 65757429Smarkm return; 65857429Smarkm } 65957429Smarkm /* 66057429Smarkm * There is a kernel bug on Solaris that causes select to 66157429Smarkm * sometimes wake up even though there is no data available. 66257429Smarkm */ 663181111Sdes if (len < 0 && 664181111Sdes (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)) 66557429Smarkm len = 0; 66657429Smarkm 66757429Smarkm if (len < 0) { 668181111Sdes /* 669181111Sdes * An error has encountered. Perhaps there is a 670181111Sdes * network problem. 671181111Sdes */ 672181111Sdes snprintf(buf, sizeof buf, 673181111Sdes "Read from remote host %.300s: %.100s\r\n", 674181111Sdes host, strerror(errno)); 67557429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 67657429Smarkm quit_pending = 1; 67757429Smarkm return; 67857429Smarkm } 67957429Smarkm packet_process_incoming(buf, len); 68057429Smarkm } 68160573Skris} 68260573Skris 68398675Sdesstatic void 684181111Sdesclient_status_confirm(int type, Channel *c, void *ctx) 685137015Sdes{ 686181111Sdes struct channel_reply_ctx *cr = (struct channel_reply_ctx *)ctx; 687181111Sdes char errmsg[256]; 688181111Sdes int tochan; 689137015Sdes 690181111Sdes /* XXX supress on mux _client_ quietmode */ 691181111Sdes tochan = options.log_level >= SYSLOG_LEVEL_ERROR && 692181111Sdes c->ctl_fd != -1 && c->extended_usage == CHAN_EXTENDED_WRITE; 693137015Sdes 694181111Sdes if (type == SSH2_MSG_CHANNEL_SUCCESS) { 695181111Sdes debug2("%s request accepted on channel %d", 696181111Sdes cr->request_type, c->self); 697181111Sdes } else if (type == SSH2_MSG_CHANNEL_FAILURE) { 698181111Sdes if (tochan) { 699181111Sdes snprintf(errmsg, sizeof(errmsg), 700181111Sdes "%s request failed\r\n", cr->request_type); 701181111Sdes } else { 702181111Sdes snprintf(errmsg, sizeof(errmsg), 703181111Sdes "%s request failed on channel %d", 704181111Sdes cr->request_type, c->self); 705181111Sdes } 706181111Sdes /* If error occurred on primary session channel, then exit */ 707181111Sdes if (cr->do_close && c->self == session_ident) 708181111Sdes fatal("%s", errmsg); 709181111Sdes /* If error occurred on mux client, append to their stderr */ 710181111Sdes if (tochan) 711181111Sdes buffer_append(&c->extended, errmsg, strlen(errmsg)); 712181111Sdes else 713181111Sdes error("%s", errmsg); 714181111Sdes if (cr->do_close) { 715181111Sdes chan_read_failed(c); 716181111Sdes chan_write_failed(c); 717181111Sdes } 718137015Sdes } 719181111Sdes xfree(cr); 720137015Sdes} 721137015Sdes 722137015Sdesstatic void 723181111Sdesclient_abandon_status_confirm(Channel *c, void *ctx) 724137015Sdes{ 725181111Sdes xfree(ctx); 726137015Sdes} 727137015Sdes 728137015Sdesstatic void 729181111Sdesclient_expect_confirm(int id, const char *request, int do_close) 730137015Sdes{ 731181111Sdes struct channel_reply_ctx *cr = xmalloc(sizeof(*cr)); 732137015Sdes 733181111Sdes cr->request_type = request; 734181111Sdes cr->do_close = do_close; 735137015Sdes 736181111Sdes channel_register_status_confirm(id, client_status_confirm, 737181111Sdes client_abandon_status_confirm, cr); 738181111Sdes} 739137015Sdes 740181111Sdesvoid 741181111Sdesclient_register_global_confirm(global_confirm_cb *cb, void *ctx) 742181111Sdes{ 743181111Sdes struct global_confirm *gc, *last_gc; 744137015Sdes 745181111Sdes /* Coalesce identical callbacks */ 746181111Sdes last_gc = TAILQ_LAST(&global_confirms, global_confirms); 747181111Sdes if (last_gc && last_gc->cb == cb && last_gc->ctx == ctx) { 748181111Sdes if (++last_gc->ref_count >= INT_MAX) 749181111Sdes fatal("%s: last_gc->ref_count = %d", 750181111Sdes __func__, last_gc->ref_count); 751146998Sdes return; 752146998Sdes } 753146998Sdes 754181111Sdes gc = xmalloc(sizeof(*gc)); 755181111Sdes gc->cb = cb; 756181111Sdes gc->ctx = ctx; 757181111Sdes gc->ref_count = 1; 758181111Sdes TAILQ_INSERT_TAIL(&global_confirms, gc, entry); 759137015Sdes} 760137015Sdes 761137015Sdesstatic void 76298675Sdesprocess_cmdline(void) 76398675Sdes{ 76498675Sdes void (*handler)(int); 765146998Sdes char *s, *cmd, *cancel_host; 766137015Sdes int delete = 0; 767192595Sdes int local = 0, remote = 0, dynamic = 0; 768192595Sdes int cancel_port; 769146998Sdes Forward fwd; 77098675Sdes 771181111Sdes bzero(&fwd, sizeof(fwd)); 772181111Sdes fwd.listen_host = fwd.connect_host = NULL; 773181111Sdes 77498675Sdes leave_raw_mode(); 77598675Sdes handler = signal(SIGINT, SIG_IGN); 77698675Sdes cmd = s = read_passphrase("\r\nssh> ", RP_ECHO); 77798675Sdes if (s == NULL) 77898675Sdes goto out; 779181111Sdes while (isspace(*s)) 78098675Sdes s++; 781137015Sdes if (*s == '-') 782137015Sdes s++; /* Skip cmdline '-', if any */ 783137015Sdes if (*s == '\0') 78498675Sdes goto out; 785137015Sdes 786137015Sdes if (*s == 'h' || *s == 'H' || *s == '?') { 787137015Sdes logit("Commands:"); 788162852Sdes logit(" -L[bind_address:]port:host:hostport " 789162852Sdes "Request local forward"); 790162852Sdes logit(" -R[bind_address:]port:host:hostport " 791162852Sdes "Request remote forward"); 792192595Sdes logit(" -D[bind_address:]port " 793192595Sdes "Request dynamic forward"); 794162852Sdes logit(" -KR[bind_address:]port " 795162852Sdes "Cancel remote forward"); 796157016Sdes if (!options.permit_local_command) 797157016Sdes goto out; 798162852Sdes logit(" !args " 799162852Sdes "Execute local command"); 800137015Sdes goto out; 801137015Sdes } 802137015Sdes 803157016Sdes if (*s == '!' && options.permit_local_command) { 804157016Sdes s++; 805157016Sdes ssh_local_cmd(s); 806157016Sdes goto out; 807157016Sdes } 808157016Sdes 809137015Sdes if (*s == 'K') { 810137015Sdes delete = 1; 811137015Sdes s++; 812137015Sdes } 813192595Sdes if (*s == 'L') 814192595Sdes local = 1; 815192595Sdes else if (*s == 'R') 816192595Sdes remote = 1; 817192595Sdes else if (*s == 'D') 818192595Sdes dynamic = 1; 819192595Sdes else { 820124208Sdes logit("Invalid command."); 82198675Sdes goto out; 82298675Sdes } 823192595Sdes 824192595Sdes if ((local || dynamic) && delete) { 825137015Sdes logit("Not supported."); 826137015Sdes goto out; 827137015Sdes } 828192595Sdes if (remote && delete && !compat20) { 829124208Sdes logit("Not supported for SSH protocol version 1."); 83098675Sdes goto out; 83198675Sdes } 832137015Sdes 833181111Sdes while (isspace(*++s)) 834181111Sdes ; 83598675Sdes 836137015Sdes if (delete) { 837146998Sdes cancel_port = 0; 838146998Sdes cancel_host = hpdelim(&s); /* may be NULL */ 839146998Sdes if (s != NULL) { 840146998Sdes cancel_port = a2port(s); 841146998Sdes cancel_host = cleanhostname(cancel_host); 842146998Sdes } else { 843146998Sdes cancel_port = a2port(cancel_host); 844146998Sdes cancel_host = NULL; 84598675Sdes } 846192595Sdes if (cancel_port <= 0) { 847146998Sdes logit("Bad forwarding close port"); 848137015Sdes goto out; 849137015Sdes } 850146998Sdes channel_request_rforward_cancel(cancel_host, cancel_port); 851137015Sdes } else { 852192595Sdes if (!parse_forward(&fwd, s, dynamic, remote)) { 853137015Sdes logit("Bad forwarding specification."); 854137015Sdes goto out; 855137015Sdes } 856192595Sdes if (local || dynamic) { 857146998Sdes if (channel_setup_local_fwd_listener(fwd.listen_host, 858146998Sdes fwd.listen_port, fwd.connect_host, 859146998Sdes fwd.connect_port, options.gateway_ports) < 0) { 860137015Sdes logit("Port forwarding failed."); 861137015Sdes goto out; 862137015Sdes } 863146998Sdes } else { 864162852Sdes if (channel_request_remote_forwarding(fwd.listen_host, 865146998Sdes fwd.listen_port, fwd.connect_host, 866162852Sdes fwd.connect_port) < 0) { 867162852Sdes logit("Port forwarding failed."); 868162852Sdes goto out; 869162852Sdes } 870146998Sdes } 871146998Sdes 872137015Sdes logit("Forwarding port."); 873137015Sdes } 874137015Sdes 87598675Sdesout: 87698675Sdes signal(SIGINT, handler); 87798675Sdes enter_raw_mode(); 87898675Sdes if (cmd) 87998675Sdes xfree(cmd); 880181111Sdes if (fwd.listen_host != NULL) 881181111Sdes xfree(fwd.listen_host); 882181111Sdes if (fwd.connect_host != NULL) 883181111Sdes xfree(fwd.connect_host); 88498675Sdes} 88598675Sdes 886181111Sdes/* 887181111Sdes * Process the characters one by one, call with c==NULL for proto1 case. 888181111Sdes */ 88992555Sdesstatic int 890181111Sdesprocess_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr, 891181111Sdes char *buf, int len) 89265668Skris{ 89365668Skris char string[1024]; 89465668Skris pid_t pid; 89565668Skris int bytes = 0; 89676259Sgreen u_int i; 89776259Sgreen u_char ch; 89865668Skris char *s; 899181111Sdes int *escape_pendingp, escape_char; 900181111Sdes struct escape_filter_ctx *efc; 90165668Skris 902181111Sdes if (c == NULL) { 903181111Sdes escape_pendingp = &escape_pending1; 904181111Sdes escape_char = escape_char1; 905181111Sdes } else { 906181111Sdes if (c->filter_ctx == NULL) 907181111Sdes return 0; 908181111Sdes efc = (struct escape_filter_ctx *)c->filter_ctx; 909181111Sdes escape_pendingp = &efc->escape_pending; 910181111Sdes escape_char = efc->escape_char; 911181111Sdes } 912181111Sdes 913149749Sdes if (len <= 0) 914149749Sdes return (0); 915149749Sdes 916149749Sdes for (i = 0; i < (u_int)len; i++) { 91765668Skris /* Get one character at a time. */ 91865668Skris ch = buf[i]; 91965668Skris 920181111Sdes if (*escape_pendingp) { 92165668Skris /* We have previously seen an escape character. */ 92265668Skris /* Clear the flag now. */ 923181111Sdes *escape_pendingp = 0; 92465668Skris 92565668Skris /* Process the escaped character. */ 92665668Skris switch (ch) { 92765668Skris case '.': 92865668Skris /* Terminate the connection. */ 929181111Sdes snprintf(string, sizeof string, "%c.\r\n", 930181111Sdes escape_char); 93165668Skris buffer_append(berr, string, strlen(string)); 93265668Skris 933181111Sdes if (c && c->ctl_fd != -1) { 934181111Sdes chan_read_failed(c); 935181111Sdes chan_write_failed(c); 936181111Sdes return 0; 937181111Sdes } else 938181111Sdes quit_pending = 1; 93965668Skris return -1; 94065668Skris 94165668Skris case 'Z' - 64: 942181111Sdes /* XXX support this for mux clients */ 943181111Sdes if (c && c->ctl_fd != -1) { 944181111Sdes noescape: 945181111Sdes snprintf(string, sizeof string, 946181111Sdes "%c%c escape not available to " 947181111Sdes "multiplexed sessions\r\n", 948181111Sdes escape_char, ch); 949181111Sdes buffer_append(berr, string, 950181111Sdes strlen(string)); 951181111Sdes continue; 952181111Sdes } 953181111Sdes /* Suspend the program. Inform the user */ 954181111Sdes snprintf(string, sizeof string, 955181111Sdes "%c^Z [suspend ssh]\r\n", escape_char); 95665668Skris buffer_append(berr, string, strlen(string)); 95765668Skris 95865668Skris /* Restore terminal modes and suspend. */ 95965668Skris client_suspend_self(bin, bout, berr); 96065668Skris 96165668Skris /* We have been continued. */ 96265668Skris continue; 96365668Skris 964124208Sdes case 'B': 965124208Sdes if (compat20) { 966124208Sdes snprintf(string, sizeof string, 967124208Sdes "%cB\r\n", escape_char); 968124208Sdes buffer_append(berr, string, 969124208Sdes strlen(string)); 970124208Sdes channel_request_start(session_ident, 971124208Sdes "break", 0); 972124208Sdes packet_put_int(1000); 973124208Sdes packet_send(); 974124208Sdes } 975124208Sdes continue; 976124208Sdes 97776259Sgreen case 'R': 97876259Sgreen if (compat20) { 97976259Sgreen if (datafellows & SSH_BUG_NOREKEY) 980181111Sdes logit("Server does not " 981181111Sdes "support re-keying"); 98276259Sgreen else 98376259Sgreen need_rekeying = 1; 98476259Sgreen } 98576259Sgreen continue; 98676259Sgreen 98765668Skris case '&': 988181111Sdes if (c && c->ctl_fd != -1) 989181111Sdes goto noescape; 99065668Skris /* 991181111Sdes * Detach the program (continue to serve 992181111Sdes * connections, but put in background and no 993181111Sdes * more new connections). 99465668Skris */ 99565668Skris /* Restore tty modes. */ 99665668Skris leave_raw_mode(); 99765668Skris 99865668Skris /* Stop listening for new connections. */ 99965668Skris channel_stop_listening(); 100065668Skris 100192555Sdes snprintf(string, sizeof string, 100292555Sdes "%c& [backgrounded]\n", escape_char); 100392555Sdes buffer_append(berr, string, strlen(string)); 100465668Skris 100565668Skris /* Fork into background. */ 100665668Skris pid = fork(); 100765668Skris if (pid < 0) { 100865668Skris error("fork: %.100s", strerror(errno)); 100965668Skris continue; 101065668Skris } 101165668Skris if (pid != 0) { /* This is the parent. */ 101265668Skris /* The parent just exits. */ 101365668Skris exit(0); 101465668Skris } 101565668Skris /* The child continues serving connections. */ 101692555Sdes if (compat20) { 101792555Sdes buffer_append(bin, "\004", 1); 101892555Sdes /* fake EOF on stdin */ 101992555Sdes return -1; 102092555Sdes } else if (!stdin_eof) { 102192555Sdes /* 1022181111Sdes * Sending SSH_CMSG_EOF alone does not 1023181111Sdes * always appear to be enough. So we 1024181111Sdes * try to send an EOF character first. 102592555Sdes */ 102692555Sdes packet_start(SSH_CMSG_STDIN_DATA); 102792555Sdes packet_put_string("\004", 1); 102892555Sdes packet_send(); 102992555Sdes /* Close stdin. */ 103092555Sdes stdin_eof = 1; 103192555Sdes if (buffer_len(bin) == 0) { 103292555Sdes packet_start(SSH_CMSG_EOF); 103392555Sdes packet_send(); 103492555Sdes } 103592555Sdes } 103692555Sdes continue; 103765668Skris 103865668Skris case '?': 1039181111Sdes if (c && c->ctl_fd != -1) { 1040181111Sdes snprintf(string, sizeof string, 104165668Skris"%c?\r\n\ 104265668SkrisSupported escape sequences:\r\n\ 1043181111Sdes %c. - terminate session\r\n\ 1044181111Sdes %cB - send a BREAK to the remote system\r\n\ 1045181111Sdes %cR - Request rekey (SSH protocol 2 only)\r\n\ 1046181111Sdes %c# - list forwarded connections\r\n\ 1047181111Sdes %c? - this message\r\n\ 1048181111Sdes %c%c - send the escape character by typing it twice\r\n\ 104965668Skris(Note that escapes are only recognized immediately after newline.)\r\n", 1050181111Sdes escape_char, escape_char, 1051181111Sdes escape_char, escape_char, 1052181111Sdes escape_char, escape_char, 1053192595Sdes escape_char, escape_char); 1054181111Sdes } else { 1055181111Sdes snprintf(string, sizeof string, 1056181111Sdes"%c?\r\n\ 1057181111SdesSupported escape sequences:\r\n\ 1058181111Sdes %c. - terminate connection (and any multiplexed sessions)\r\n\ 1059181111Sdes %cB - send a BREAK to the remote system\r\n\ 1060181111Sdes %cC - open a command line\r\n\ 1061181111Sdes %cR - Request rekey (SSH protocol 2 only)\r\n\ 1062181111Sdes %c^Z - suspend ssh\r\n\ 1063181111Sdes %c# - list forwarded connections\r\n\ 1064181111Sdes %c& - background ssh (when waiting for connections to terminate)\r\n\ 1065181111Sdes %c? - this message\r\n\ 1066181111Sdes %c%c - send the escape character by typing it twice\r\n\ 1067181111Sdes(Note that escapes are only recognized immediately after newline.)\r\n", 1068181111Sdes escape_char, escape_char, 1069181111Sdes escape_char, escape_char, 1070181111Sdes escape_char, escape_char, 1071181111Sdes escape_char, escape_char, 1072181111Sdes escape_char, escape_char, 1073181111Sdes escape_char); 1074181111Sdes } 107565668Skris buffer_append(berr, string, strlen(string)); 107665668Skris continue; 107765668Skris 107865668Skris case '#': 1079181111Sdes snprintf(string, sizeof string, "%c#\r\n", 1080181111Sdes escape_char); 108165668Skris buffer_append(berr, string, strlen(string)); 108265668Skris s = channel_open_message(); 108365668Skris buffer_append(berr, s, strlen(s)); 108465668Skris xfree(s); 108565668Skris continue; 108665668Skris 108798675Sdes case 'C': 1088192595Sdes if (c && c->ctl_fd != -1) 1089192595Sdes goto noescape; 109098675Sdes process_cmdline(); 109198675Sdes continue; 109298675Sdes 109365668Skris default: 109465668Skris if (ch != escape_char) { 109565668Skris buffer_put_char(bin, escape_char); 109665668Skris bytes++; 109765668Skris } 109865668Skris /* Escaped characters fall through here */ 109965668Skris break; 110065668Skris } 110165668Skris } else { 110265668Skris /* 1103181111Sdes * The previous character was not an escape char. 1104181111Sdes * Check if this is an escape. 110565668Skris */ 110665668Skris if (last_was_cr && ch == escape_char) { 1107181111Sdes /* 1108181111Sdes * It is. Set the flag and continue to 1109181111Sdes * next character. 1110181111Sdes */ 1111181111Sdes *escape_pendingp = 1; 111265668Skris continue; 111365668Skris } 111465668Skris } 111565668Skris 111665668Skris /* 111765668Skris * Normal character. Record whether it was a newline, 111865668Skris * and append it to the buffer. 111965668Skris */ 112065668Skris last_was_cr = (ch == '\r' || ch == '\n'); 112165668Skris buffer_put_char(bin, ch); 112265668Skris bytes++; 112365668Skris } 112465668Skris return bytes; 112565668Skris} 112665668Skris 112792555Sdesstatic void 1128162852Sdesclient_process_input(fd_set *readset) 112960573Skris{ 113060573Skris int len; 113165668Skris char buf[8192]; 113260573Skris 113357429Smarkm /* Read input from stdin. */ 113457429Smarkm if (FD_ISSET(fileno(stdin), readset)) { 113557429Smarkm /* Read as much as possible. */ 113657429Smarkm len = read(fileno(stdin), buf, sizeof(buf)); 1137181111Sdes if (len < 0 && 1138181111Sdes (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)) 113976259Sgreen return; /* we'll try again later */ 114057429Smarkm if (len <= 0) { 114157429Smarkm /* 114257429Smarkm * Received EOF or error. They are treated 114357429Smarkm * similarly, except that an error message is printed 114457429Smarkm * if it was an error condition. 114557429Smarkm */ 114657429Smarkm if (len < 0) { 1147181111Sdes snprintf(buf, sizeof buf, "read: %.100s\r\n", 1148181111Sdes strerror(errno)); 114957429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 115057429Smarkm } 115157429Smarkm /* Mark that we have seen EOF. */ 115257429Smarkm stdin_eof = 1; 115357429Smarkm /* 115457429Smarkm * Send an EOF message to the server unless there is 115557429Smarkm * data in the buffer. If there is data in the 115657429Smarkm * buffer, no message will be sent now. Code 115757429Smarkm * elsewhere will send the EOF when the buffer 115857429Smarkm * becomes empty if stdin_eof is set. 115957429Smarkm */ 116057429Smarkm if (buffer_len(&stdin_buffer) == 0) { 116157429Smarkm packet_start(SSH_CMSG_EOF); 116257429Smarkm packet_send(); 116357429Smarkm } 1164181111Sdes } else if (escape_char1 == SSH_ESCAPECHAR_NONE) { 116557429Smarkm /* 116657429Smarkm * Normal successful read, and no escape character. 116757429Smarkm * Just append the data to buffer. 116857429Smarkm */ 116957429Smarkm buffer_append(&stdin_buffer, buf, len); 117057429Smarkm } else { 117157429Smarkm /* 1172181111Sdes * Normal, successful read. But we have an escape 1173181111Sdes * character and have to process the characters one 1174181111Sdes * by one. 117557429Smarkm */ 1176181111Sdes if (process_escapes(NULL, &stdin_buffer, 1177181111Sdes &stdout_buffer, &stderr_buffer, buf, len) == -1) 117865668Skris return; 117957429Smarkm } 118057429Smarkm } 118157429Smarkm} 118257429Smarkm 118392555Sdesstatic void 1184162852Sdesclient_process_output(fd_set *writeset) 118557429Smarkm{ 118657429Smarkm int len; 118757429Smarkm char buf[100]; 118857429Smarkm 118957429Smarkm /* Write buffered output to stdout. */ 119057429Smarkm if (FD_ISSET(fileno(stdout), writeset)) { 119157429Smarkm /* Write as much data as possible. */ 119257429Smarkm len = write(fileno(stdout), buffer_ptr(&stdout_buffer), 119357429Smarkm buffer_len(&stdout_buffer)); 119457429Smarkm if (len <= 0) { 1195181111Sdes if (errno == EINTR || errno == EAGAIN || 1196181111Sdes errno == EWOULDBLOCK) 119757429Smarkm len = 0; 119857429Smarkm else { 119957429Smarkm /* 120057429Smarkm * An error or EOF was encountered. Put an 120157429Smarkm * error message to stderr buffer. 120257429Smarkm */ 1203181111Sdes snprintf(buf, sizeof buf, 1204181111Sdes "write stdout: %.50s\r\n", strerror(errno)); 120557429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 120657429Smarkm quit_pending = 1; 120757429Smarkm return; 120857429Smarkm } 120957429Smarkm } 121057429Smarkm /* Consume printed data from the buffer. */ 121157429Smarkm buffer_consume(&stdout_buffer, len); 121257429Smarkm } 121357429Smarkm /* Write buffered output to stderr. */ 121457429Smarkm if (FD_ISSET(fileno(stderr), writeset)) { 121557429Smarkm /* Write as much data as possible. */ 121657429Smarkm len = write(fileno(stderr), buffer_ptr(&stderr_buffer), 121757429Smarkm buffer_len(&stderr_buffer)); 121857429Smarkm if (len <= 0) { 1219181111Sdes if (errno == EINTR || errno == EAGAIN || 1220181111Sdes errno == EWOULDBLOCK) 122157429Smarkm len = 0; 122257429Smarkm else { 1223181111Sdes /* 1224181111Sdes * EOF or error, but can't even print 1225181111Sdes * error message. 1226181111Sdes */ 122757429Smarkm quit_pending = 1; 122857429Smarkm return; 122957429Smarkm } 123057429Smarkm } 123157429Smarkm /* Consume printed characters from the buffer. */ 123257429Smarkm buffer_consume(&stderr_buffer, len); 123357429Smarkm } 123457429Smarkm} 123557429Smarkm 123657429Smarkm/* 123760573Skris * Get packets from the connection input buffer, and process them as long as 123860573Skris * there are packets available. 123960573Skris * 124060573Skris * Any unknown packets received during the actual 124160573Skris * session cause the session to terminate. This is 124260573Skris * intended to make debugging easier since no 124360573Skris * confirmations are sent. Any compatible protocol 124460573Skris * extensions must be negotiated during the 124560573Skris * preparatory phase. 124660573Skris */ 124760573Skris 124892555Sdesstatic void 124976259Sgreenclient_process_buffered_input_packets(void) 125060573Skris{ 1251181111Sdes dispatch_run(DISPATCH_NONBLOCK, &quit_pending, 1252181111Sdes compat20 ? xxx_kex : NULL); 125360573Skris} 125460573Skris 125565668Skris/* scan buf[] for '~' before sending data to the peer */ 125665668Skris 1257181111Sdes/* Helper: allocate a new escape_filter_ctx and fill in its escape char */ 1258181111Sdesvoid * 1259181111Sdesclient_new_escape_filter_ctx(int escape_char) 126065668Skris{ 1261181111Sdes struct escape_filter_ctx *ret; 1262181111Sdes 1263181111Sdes ret = xmalloc(sizeof(*ret)); 1264181111Sdes ret->escape_pending = 0; 1265181111Sdes ret->escape_char = escape_char; 1266181111Sdes return (void *)ret; 126765668Skris} 126865668Skris 1269181111Sdes/* Free the escape filter context on channel free */ 1270181111Sdesvoid 1271181111Sdesclient_filter_cleanup(int cid, void *ctx) 1272181111Sdes{ 1273181111Sdes xfree(ctx); 1274181111Sdes} 1275181111Sdes 1276181111Sdesint 1277181111Sdesclient_simple_escape_filter(Channel *c, char *buf, int len) 1278181111Sdes{ 1279181111Sdes if (c->extended_usage != CHAN_EXTENDED_WRITE) 1280181111Sdes return 0; 1281181111Sdes 1282181111Sdes return process_escapes(c, &c->input, &c->output, &c->extended, 1283181111Sdes buf, len); 1284181111Sdes} 1285181111Sdes 128692555Sdesstatic void 128776259Sgreenclient_channel_closed(int id, void *arg) 128876259Sgreen{ 128992555Sdes channel_cancel_cleanup(id); 129076259Sgreen session_closed = 1; 1291126274Sdes leave_raw_mode(); 129276259Sgreen} 129376259Sgreen 129460573Skris/* 129557429Smarkm * Implements the interactive session with the server. This is called after 129657429Smarkm * the user has been authenticated, and a command has been started on the 129792555Sdes * remote host. If escape_char != SSH_ESCAPECHAR_NONE, it is the character 129892555Sdes * used as an escape character for terminating or suspending the session. 129957429Smarkm */ 130057429Smarkm 130160573Skrisint 130265668Skrisclient_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) 130357429Smarkm{ 130476259Sgreen fd_set *readset = NULL, *writeset = NULL; 130557429Smarkm double start_time, total_time; 1306137015Sdes int max_fd = 0, max_fd2 = 0, len, rekeying = 0; 1307181111Sdes u_int64_t ibytes, obytes; 1308137015Sdes u_int nalloc = 0; 130957429Smarkm char buf[100]; 131057429Smarkm 131157429Smarkm debug("Entering interactive session."); 131257429Smarkm 131357429Smarkm start_time = get_current_time(); 131457429Smarkm 131557429Smarkm /* Initialize variables. */ 1316181111Sdes escape_pending1 = 0; 131757429Smarkm last_was_cr = 1; 131857429Smarkm exit_status = -1; 131957429Smarkm stdin_eof = 0; 132057429Smarkm buffer_high = 64 * 1024; 132157429Smarkm connection_in = packet_get_connection_in(); 132257429Smarkm connection_out = packet_get_connection_out(); 132376259Sgreen max_fd = MAX(connection_in, connection_out); 1324181111Sdes if (muxserver_sock != -1) 1325181111Sdes max_fd = MAX(max_fd, muxserver_sock); 132676259Sgreen 132776259Sgreen if (!compat20) { 132876259Sgreen /* enable nonblocking unless tty */ 132976259Sgreen if (!isatty(fileno(stdin))) 133076259Sgreen set_nonblock(fileno(stdin)); 133176259Sgreen if (!isatty(fileno(stdout))) 133276259Sgreen set_nonblock(fileno(stdout)); 133376259Sgreen if (!isatty(fileno(stderr))) 133476259Sgreen set_nonblock(fileno(stderr)); 133576259Sgreen max_fd = MAX(max_fd, fileno(stdin)); 133676259Sgreen max_fd = MAX(max_fd, fileno(stdout)); 133776259Sgreen max_fd = MAX(max_fd, fileno(stderr)); 133876259Sgreen } 133957429Smarkm quit_pending = 0; 1340181111Sdes escape_char1 = escape_char_arg; 134157429Smarkm 134257429Smarkm /* Initialize buffers. */ 134357429Smarkm buffer_init(&stdin_buffer); 134457429Smarkm buffer_init(&stdout_buffer); 134557429Smarkm buffer_init(&stderr_buffer); 134657429Smarkm 134760573Skris client_init_dispatch(); 134860573Skris 1349113908Sdes /* 1350113908Sdes * Set signal handlers, (e.g. to restore non-blocking mode) 1351113908Sdes * but don't overwrite SIG_IGN, matches behaviour from rsh(1) 1352113908Sdes */ 1353146998Sdes if (signal(SIGHUP, SIG_IGN) != SIG_IGN) 1354146998Sdes signal(SIGHUP, signal_handler); 1355113908Sdes if (signal(SIGINT, SIG_IGN) != SIG_IGN) 1356113908Sdes signal(SIGINT, signal_handler); 1357113908Sdes if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) 1358113908Sdes signal(SIGQUIT, signal_handler); 1359113908Sdes if (signal(SIGTERM, SIG_IGN) != SIG_IGN) 1360113908Sdes signal(SIGTERM, signal_handler); 1361146998Sdes signal(SIGWINCH, window_change_handler); 136257429Smarkm 136357429Smarkm if (have_pty) 136457429Smarkm enter_raw_mode(); 136557429Smarkm 136676259Sgreen if (compat20) { 136776259Sgreen session_ident = ssh2_chan_id; 1368181111Sdes if (escape_char_arg != SSH_ESCAPECHAR_NONE) 136976259Sgreen channel_register_filter(session_ident, 1370181111Sdes client_simple_escape_filter, NULL, 1371181111Sdes client_filter_cleanup, 1372181111Sdes client_new_escape_filter_ctx(escape_char_arg)); 137376259Sgreen if (session_ident != -1) 137476259Sgreen channel_register_cleanup(session_ident, 1375157016Sdes client_channel_closed, 0); 137676259Sgreen } else { 137776259Sgreen /* Check if we should immediately send eof on stdin. */ 137860573Skris client_check_initial_eof_on_stdin(); 137976259Sgreen } 138057429Smarkm 138157429Smarkm /* Main loop of the client for the interactive session mode. */ 138257429Smarkm while (!quit_pending) { 138357429Smarkm 138457429Smarkm /* Process buffered packets sent by the server. */ 138557429Smarkm client_process_buffered_input_packets(); 138657429Smarkm 138776259Sgreen if (compat20 && session_closed && !channel_still_open()) 138860573Skris break; 138960573Skris 139076259Sgreen rekeying = (xxx_kex != NULL && !xxx_kex->done); 139157429Smarkm 139276259Sgreen if (rekeying) { 139376259Sgreen debug("rekeying in progress"); 139476259Sgreen } else { 139576259Sgreen /* 139676259Sgreen * Make packets of buffered stdin data, and buffer 139776259Sgreen * them for sending to the server. 139876259Sgreen */ 139976259Sgreen if (!compat20) 140076259Sgreen client_make_packets_from_stdin_data(); 140157429Smarkm 140276259Sgreen /* 140376259Sgreen * Make packets from buffered channel data, and 140476259Sgreen * enqueue them for sending to the server. 140576259Sgreen */ 140676259Sgreen if (packet_not_very_much_data_to_write()) 140776259Sgreen channel_output_poll(); 140857429Smarkm 140976259Sgreen /* 141076259Sgreen * Check if the window size has changed, and buffer a 141176259Sgreen * message about it to the server if so. 141276259Sgreen */ 141376259Sgreen client_check_window_change(); 141457429Smarkm 141576259Sgreen if (quit_pending) 141676259Sgreen break; 141776259Sgreen } 141857429Smarkm /* 141957429Smarkm * Wait until we have something to do (something becomes 142057429Smarkm * available on one of the descriptors). 142157429Smarkm */ 142292555Sdes max_fd2 = max_fd; 142376259Sgreen client_wait_until_can_do_something(&readset, &writeset, 142492555Sdes &max_fd2, &nalloc, rekeying); 142557429Smarkm 142657429Smarkm if (quit_pending) 142757429Smarkm break; 142857429Smarkm 142976259Sgreen /* Do channel operations unless rekeying in progress. */ 143076259Sgreen if (!rekeying) { 143176259Sgreen channel_after_select(readset, writeset); 1432124208Sdes if (need_rekeying || packet_need_rekeying()) { 1433124208Sdes debug("need rekeying"); 143476259Sgreen xxx_kex->done = 0; 143576259Sgreen kex_send_kexinit(xxx_kex); 143676259Sgreen need_rekeying = 0; 143776259Sgreen } 143876259Sgreen } 143976259Sgreen 144060573Skris /* Buffer input from the connection. */ 144176259Sgreen client_process_net_input(readset); 144257429Smarkm 1443137015Sdes /* Accept control connections. */ 1444181111Sdes if (muxserver_sock != -1 &&FD_ISSET(muxserver_sock, readset)) { 1445181111Sdes if (muxserver_accept_control()) 1446181111Sdes quit_pending = 1; 1447181111Sdes } 1448137015Sdes 144960573Skris if (quit_pending) 145060573Skris break; 145157429Smarkm 145260573Skris if (!compat20) { 145360573Skris /* Buffer data from stdin */ 145476259Sgreen client_process_input(readset); 145560573Skris /* 145660573Skris * Process output to stdout and stderr. Output to 145760573Skris * the connection is processed elsewhere (above). 145860573Skris */ 145976259Sgreen client_process_output(writeset); 146060573Skris } 146160573Skris 1462181111Sdes /* 1463181111Sdes * Send as much buffered packet data as possible to the 1464181111Sdes * sender. 1465181111Sdes */ 146676259Sgreen if (FD_ISSET(connection_out, writeset)) 146757429Smarkm packet_write_poll(); 146857429Smarkm } 146976259Sgreen if (readset) 147076259Sgreen xfree(readset); 147176259Sgreen if (writeset) 147276259Sgreen xfree(writeset); 147357429Smarkm 147457429Smarkm /* Terminate the session. */ 147557429Smarkm 147657429Smarkm /* Stop watching for window change. */ 1477146998Sdes signal(SIGWINCH, SIG_DFL); 147857429Smarkm 147992555Sdes channel_free_all(); 148057429Smarkm 148192555Sdes if (have_pty) 148292555Sdes leave_raw_mode(); 148392555Sdes 148492555Sdes /* restore blocking io */ 148592555Sdes if (!isatty(fileno(stdin))) 148692555Sdes unset_nonblock(fileno(stdin)); 148792555Sdes if (!isatty(fileno(stdout))) 148892555Sdes unset_nonblock(fileno(stdout)); 148992555Sdes if (!isatty(fileno(stderr))) 149092555Sdes unset_nonblock(fileno(stderr)); 149192555Sdes 1492126274Sdes /* 1493126274Sdes * If there was no shell or command requested, there will be no remote 1494126274Sdes * exit status to be returned. In that case, clear error code if the 1495126274Sdes * connection was deliberately terminated at this end. 1496126274Sdes */ 1497126274Sdes if (no_shell_flag && received_signal == SIGTERM) { 1498126274Sdes received_signal = 0; 1499126274Sdes exit_status = 0; 150092555Sdes } 150192555Sdes 1502126274Sdes if (received_signal) 1503126274Sdes fatal("Killed by signal %d.", (int) received_signal); 1504126274Sdes 150557429Smarkm /* 150657429Smarkm * In interactive mode (with pseudo tty) display a message indicating 150757429Smarkm * that the connection has been closed. 150857429Smarkm */ 150957429Smarkm if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) { 1510181111Sdes snprintf(buf, sizeof buf, 1511181111Sdes "Connection to %.64s closed.\r\n", host); 151257429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 151357429Smarkm } 151492555Sdes 151557429Smarkm /* Output any buffered data for stdout. */ 151657429Smarkm while (buffer_len(&stdout_buffer) > 0) { 151757429Smarkm len = write(fileno(stdout), buffer_ptr(&stdout_buffer), 151857429Smarkm buffer_len(&stdout_buffer)); 151957429Smarkm if (len <= 0) { 152057429Smarkm error("Write failed flushing stdout buffer."); 152157429Smarkm break; 152257429Smarkm } 152357429Smarkm buffer_consume(&stdout_buffer, len); 152457429Smarkm } 152557429Smarkm 152657429Smarkm /* Output any buffered data for stderr. */ 152757429Smarkm while (buffer_len(&stderr_buffer) > 0) { 152857429Smarkm len = write(fileno(stderr), buffer_ptr(&stderr_buffer), 152957429Smarkm buffer_len(&stderr_buffer)); 153057429Smarkm if (len <= 0) { 153157429Smarkm error("Write failed flushing stderr buffer."); 153257429Smarkm break; 153357429Smarkm } 153457429Smarkm buffer_consume(&stderr_buffer, len); 153557429Smarkm } 153657429Smarkm 153757429Smarkm /* Clear and free any buffers. */ 153857429Smarkm memset(buf, 0, sizeof(buf)); 153957429Smarkm buffer_free(&stdin_buffer); 154057429Smarkm buffer_free(&stdout_buffer); 154157429Smarkm buffer_free(&stderr_buffer); 154257429Smarkm 154357429Smarkm /* Report bytes transferred, and transfer rates. */ 154457429Smarkm total_time = get_current_time() - start_time; 1545181111Sdes packet_get_state(MODE_IN, NULL, NULL, NULL, &ibytes); 1546181111Sdes packet_get_state(MODE_OUT, NULL, NULL, NULL, &obytes); 1547181111Sdes verbose("Transferred: sent %llu, received %llu bytes, in %.1f seconds", 1548181111Sdes obytes, ibytes, total_time); 154957429Smarkm if (total_time > 0) 1550181111Sdes verbose("Bytes per second: sent %.1f, received %.1f", 1551181111Sdes obytes / total_time, ibytes / total_time); 155257429Smarkm /* Return the exit status of the program. */ 155357429Smarkm debug("Exit status %d", exit_status); 155457429Smarkm return exit_status; 155557429Smarkm} 155660573Skris 155760573Skris/*********/ 155860573Skris 155992555Sdesstatic void 156092555Sdesclient_input_stdout_data(int type, u_int32_t seq, void *ctxt) 156160573Skris{ 156276259Sgreen u_int data_len; 156360573Skris char *data = packet_get_string(&data_len); 156492555Sdes packet_check_eom(); 156560573Skris buffer_append(&stdout_buffer, data, data_len); 156660573Skris memset(data, 0, data_len); 156760573Skris xfree(data); 156860573Skris} 156992555Sdesstatic void 157092555Sdesclient_input_stderr_data(int type, u_int32_t seq, void *ctxt) 157160573Skris{ 157276259Sgreen u_int data_len; 157360573Skris char *data = packet_get_string(&data_len); 157492555Sdes packet_check_eom(); 157560573Skris buffer_append(&stderr_buffer, data, data_len); 157660573Skris memset(data, 0, data_len); 157760573Skris xfree(data); 157860573Skris} 157992555Sdesstatic void 158092555Sdesclient_input_exit_status(int type, u_int32_t seq, void *ctxt) 158160573Skris{ 158260573Skris exit_status = packet_get_int(); 158392555Sdes packet_check_eom(); 158460573Skris /* Acknowledge the exit. */ 158560573Skris packet_start(SSH_CMSG_EXIT_CONFIRMATION); 158660573Skris packet_send(); 158760573Skris /* 158860573Skris * Must wait for packet to be sent since we are 158960573Skris * exiting the loop. 159060573Skris */ 159160573Skris packet_write_wait(); 159260573Skris /* Flag that we want to exit. */ 159360573Skris quit_pending = 1; 159460573Skris} 1595126274Sdesstatic void 1596126274Sdesclient_input_agent_open(int type, u_int32_t seq, void *ctxt) 1597126274Sdes{ 1598126274Sdes Channel *c = NULL; 1599126274Sdes int remote_id, sock; 160060573Skris 1601126274Sdes /* Read the remote channel number from the message. */ 1602126274Sdes remote_id = packet_get_int(); 1603126274Sdes packet_check_eom(); 1604126274Sdes 1605126274Sdes /* 1606126274Sdes * Get a connection to the local authentication agent (this may again 1607126274Sdes * get forwarded). 1608126274Sdes */ 1609126274Sdes sock = ssh_get_authentication_socket(); 1610126274Sdes 1611126274Sdes /* 1612126274Sdes * If we could not connect the agent, send an error message back to 1613126274Sdes * the server. This should never happen unless the agent dies, 1614126274Sdes * because authentication forwarding is only enabled if we have an 1615126274Sdes * agent. 1616126274Sdes */ 1617126274Sdes if (sock >= 0) { 1618126274Sdes c = channel_new("", SSH_CHANNEL_OPEN, sock, sock, 1619126274Sdes -1, 0, 0, 0, "authentication agent connection", 1); 1620126274Sdes c->remote_id = remote_id; 1621126274Sdes c->force_drain = 1; 1622126274Sdes } 1623126274Sdes if (c == NULL) { 1624126274Sdes packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 1625126274Sdes packet_put_int(remote_id); 1626126274Sdes } else { 1627126274Sdes /* Send a confirmation to the remote host. */ 1628126274Sdes debug("Forwarding authentication connection."); 1629126274Sdes packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 1630126274Sdes packet_put_int(remote_id); 1631126274Sdes packet_put_int(c->self); 1632126274Sdes } 1633126274Sdes packet_send(); 1634126274Sdes} 1635126274Sdes 163692555Sdesstatic Channel * 163776259Sgreenclient_request_forwarded_tcpip(const char *request_type, int rchan) 163876259Sgreen{ 1639106121Sdes Channel *c = NULL; 164076259Sgreen char *listen_address, *originator_address; 1641192595Sdes u_short listen_port, originator_port; 164276259Sgreen 164376259Sgreen /* Get rest of the packet */ 164476259Sgreen listen_address = packet_get_string(NULL); 164576259Sgreen listen_port = packet_get_int(); 164676259Sgreen originator_address = packet_get_string(NULL); 164776259Sgreen originator_port = packet_get_int(); 164892555Sdes packet_check_eom(); 164976259Sgreen 1650181111Sdes debug("client_request_forwarded_tcpip: listen %s port %d, " 1651181111Sdes "originator %s port %d", listen_address, listen_port, 1652181111Sdes originator_address, originator_port); 165376259Sgreen 1654181111Sdes c = channel_connect_by_listen_address(listen_port, 1655181111Sdes "forwarded-tcpip", originator_address); 1656181111Sdes 165776259Sgreen xfree(originator_address); 165876259Sgreen xfree(listen_address); 165976259Sgreen return c; 166076259Sgreen} 166176259Sgreen 1662106121Sdesstatic Channel * 166376259Sgreenclient_request_x11(const char *request_type, int rchan) 166476259Sgreen{ 166576259Sgreen Channel *c = NULL; 166676259Sgreen char *originator; 1667192595Sdes u_short originator_port; 166892555Sdes int sock; 166976259Sgreen 167076259Sgreen if (!options.forward_x11) { 167176259Sgreen error("Warning: ssh server tried X11 forwarding."); 1672181111Sdes error("Warning: this is probably a break-in attempt by a " 1673181111Sdes "malicious server."); 167476259Sgreen return NULL; 167576259Sgreen } 167676259Sgreen originator = packet_get_string(NULL); 167776259Sgreen if (datafellows & SSH_BUG_X11FWD) { 167876259Sgreen debug2("buggy server: x11 request w/o originator_port"); 167976259Sgreen originator_port = 0; 168076259Sgreen } else { 168176259Sgreen originator_port = packet_get_int(); 168276259Sgreen } 168392555Sdes packet_check_eom(); 168476259Sgreen /* XXX check permission */ 168576259Sgreen debug("client_request_x11: request from %s %d", originator, 168676259Sgreen originator_port); 168792555Sdes xfree(originator); 168876259Sgreen sock = x11_connect_display(); 168992555Sdes if (sock < 0) 169092555Sdes return NULL; 169192555Sdes c = channel_new("x11", 169292555Sdes SSH_CHANNEL_X11_OPEN, sock, sock, -1, 1693124208Sdes CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "x11", 1); 169492555Sdes c->force_drain = 1; 169576259Sgreen return c; 169676259Sgreen} 169776259Sgreen 1698106121Sdesstatic Channel * 169976259Sgreenclient_request_agent(const char *request_type, int rchan) 170076259Sgreen{ 170176259Sgreen Channel *c = NULL; 170292555Sdes int sock; 170376259Sgreen 170476259Sgreen if (!options.forward_agent) { 170576259Sgreen error("Warning: ssh server tried agent forwarding."); 1706181111Sdes error("Warning: this is probably a break-in attempt by a " 1707181111Sdes "malicious server."); 170876259Sgreen return NULL; 170976259Sgreen } 1710181111Sdes sock = ssh_get_authentication_socket(); 171192555Sdes if (sock < 0) 171292555Sdes return NULL; 171392555Sdes c = channel_new("authentication agent connection", 171492555Sdes SSH_CHANNEL_OPEN, sock, sock, -1, 1715181111Sdes CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, 1716124208Sdes "authentication agent connection", 1); 171792555Sdes c->force_drain = 1; 171876259Sgreen return c; 171976259Sgreen} 172076259Sgreen 1721181111Sdesint 1722181111Sdesclient_request_tun_fwd(int tun_mode, int local_tun, int remote_tun) 1723181111Sdes{ 1724181111Sdes Channel *c; 1725181111Sdes int fd; 1726181111Sdes 1727181111Sdes if (tun_mode == SSH_TUNMODE_NO) 1728181111Sdes return 0; 1729181111Sdes 1730181111Sdes if (!compat20) { 1731192595Sdes error("Tunnel forwarding is not supported for protocol 1"); 1732181111Sdes return -1; 1733181111Sdes } 1734181111Sdes 1735181111Sdes debug("Requesting tun unit %d in mode %d", local_tun, tun_mode); 1736181111Sdes 1737181111Sdes /* Open local tunnel device */ 1738181111Sdes if ((fd = tun_open(local_tun, tun_mode)) == -1) { 1739181111Sdes error("Tunnel device open failed."); 1740181111Sdes return -1; 1741181111Sdes } 1742181111Sdes 1743181111Sdes c = channel_new("tun", SSH_CHANNEL_OPENING, fd, fd, -1, 1744181111Sdes CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1); 1745181111Sdes c->datagram = 1; 1746181111Sdes 1747181111Sdes#if defined(SSH_TUN_FILTER) 1748181111Sdes if (options.tun_open == SSH_TUNMODE_POINTOPOINT) 1749181111Sdes channel_register_filter(c->self, sys_tun_infilter, 1750181111Sdes sys_tun_outfilter, NULL, NULL); 1751181111Sdes#endif 1752181111Sdes 1753181111Sdes packet_start(SSH2_MSG_CHANNEL_OPEN); 1754181111Sdes packet_put_cstring("tun@openssh.com"); 1755181111Sdes packet_put_int(c->self); 1756181111Sdes packet_put_int(c->local_window_max); 1757181111Sdes packet_put_int(c->local_maxpacket); 1758181111Sdes packet_put_int(tun_mode); 1759181111Sdes packet_put_int(remote_tun); 1760181111Sdes packet_send(); 1761181111Sdes 1762181111Sdes return 0; 1763181111Sdes} 1764181111Sdes 176560573Skris/* XXXX move to generic input handler */ 176692555Sdesstatic void 176792555Sdesclient_input_channel_open(int type, u_int32_t seq, void *ctxt) 176860573Skris{ 176960573Skris Channel *c = NULL; 177060573Skris char *ctype; 177160573Skris int rchan; 177299060Sdes u_int rmaxpack, rwindow, len; 177360573Skris 177460573Skris ctype = packet_get_string(&len); 177560573Skris rchan = packet_get_int(); 177660573Skris rwindow = packet_get_int(); 177760573Skris rmaxpack = packet_get_int(); 177860573Skris 177960573Skris debug("client_input_channel_open: ctype %s rchan %d win %d max %d", 178060573Skris ctype, rchan, rwindow, rmaxpack); 178160573Skris 178276259Sgreen if (strcmp(ctype, "forwarded-tcpip") == 0) { 178376259Sgreen c = client_request_forwarded_tcpip(ctype, rchan); 178476259Sgreen } else if (strcmp(ctype, "x11") == 0) { 178576259Sgreen c = client_request_x11(ctype, rchan); 178676259Sgreen } else if (strcmp(ctype, "auth-agent@openssh.com") == 0) { 178776259Sgreen c = client_request_agent(ctype, rchan); 178860573Skris } 178960573Skris/* XXX duplicate : */ 179060573Skris if (c != NULL) { 179160573Skris debug("confirm %s", ctype); 179260573Skris c->remote_id = rchan; 179360573Skris c->remote_window = rwindow; 179460573Skris c->remote_maxpacket = rmaxpack; 179592555Sdes if (c->type != SSH_CHANNEL_CONNECTING) { 179692555Sdes packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); 179792555Sdes packet_put_int(c->remote_id); 179892555Sdes packet_put_int(c->self); 179992555Sdes packet_put_int(c->local_window); 180092555Sdes packet_put_int(c->local_maxpacket); 180192555Sdes packet_send(); 180292555Sdes } 180360573Skris } else { 180460573Skris debug("failure %s", ctype); 180560573Skris packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); 180660573Skris packet_put_int(rchan); 180760573Skris packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED); 180892555Sdes if (!(datafellows & SSH_BUG_OPENFAILURE)) { 180992555Sdes packet_put_cstring("open failed"); 181092555Sdes packet_put_cstring(""); 181192555Sdes } 181260573Skris packet_send(); 181360573Skris } 181460573Skris xfree(ctype); 181560573Skris} 181692555Sdesstatic void 181792555Sdesclient_input_channel_req(int type, u_int32_t seq, void *ctxt) 181876259Sgreen{ 181976259Sgreen Channel *c = NULL; 1820137015Sdes int exitval, id, reply, success = 0; 182176259Sgreen char *rtype; 182260573Skris 182376259Sgreen id = packet_get_int(); 182476259Sgreen rtype = packet_get_string(NULL); 182576259Sgreen reply = packet_get_char(); 182676259Sgreen 182776259Sgreen debug("client_input_channel_req: channel %d rtype %s reply %d", 182876259Sgreen id, rtype, reply); 182976259Sgreen 1830137015Sdes if (id == -1) { 1831137015Sdes error("client_input_channel_req: request for channel -1"); 1832137015Sdes } else if ((c = channel_lookup(id)) == NULL) { 1833181111Sdes error("client_input_channel_req: channel %d: " 1834181111Sdes "unknown channel", id); 1835181111Sdes } else if (strcmp(rtype, "eow@openssh.com") == 0) { 1836181111Sdes packet_check_eom(); 1837181111Sdes chan_rcvd_eow(c); 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); 1855192595Sdes packet_put_int(c->remote_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, 1882181111Sdes const char *term, struct termios *tiop, int in_fd, Buffer *cmd, char **env) 1883137015Sdes{ 1884137015Sdes int len; 1885146998Sdes Channel *c = NULL; 1886137015Sdes 1887137015Sdes debug2("%s: id %d", __func__, id); 1888137015Sdes 1889146998Sdes if ((c = channel_lookup(id)) == NULL) 1890146998Sdes fatal("client_session2_setup: channel %d: unknown channel", id); 1891146998Sdes 1892137015Sdes if (want_tty) { 1893137015Sdes struct winsize ws; 1894137015Sdes 1895137015Sdes /* Store window size in the packet. */ 1896137015Sdes if (ioctl(in_fd, TIOCGWINSZ, &ws) < 0) 1897137015Sdes memset(&ws, 0, sizeof(ws)); 1898137015Sdes 1899181111Sdes channel_request_start(id, "pty-req", 1); 1900181111Sdes client_expect_confirm(id, "PTY allocation", 0); 1901137015Sdes packet_put_cstring(term != NULL ? term : ""); 1902162852Sdes packet_put_int((u_int)ws.ws_col); 1903162852Sdes packet_put_int((u_int)ws.ws_row); 1904162852Sdes packet_put_int((u_int)ws.ws_xpixel); 1905162852Sdes packet_put_int((u_int)ws.ws_ypixel); 1906181111Sdes if (tiop == NULL) 1907181111Sdes tiop = get_saved_tio(); 1908181111Sdes tty_make_modes(-1, tiop); 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) { 1956181111Sdes debug("Sending subsystem: %.*s", 1957181111Sdes len, (u_char*)buffer_ptr(cmd)); 1958181111Sdes channel_request_start(id, "subsystem", 1); 1959181111Sdes client_expect_confirm(id, "subsystem", 1); 1960137015Sdes } else { 1961181111Sdes debug("Sending command: %.*s", 1962181111Sdes len, (u_char*)buffer_ptr(cmd)); 1963181111Sdes channel_request_start(id, "exec", 1); 1964181111Sdes client_expect_confirm(id, "exec", 1); 1965137015Sdes } 1966137015Sdes packet_put_string(buffer_ptr(cmd), buffer_len(cmd)); 1967137015Sdes packet_send(); 1968137015Sdes } else { 1969181111Sdes channel_request_start(id, "shell", 1); 1970181111Sdes client_expect_confirm(id, "shell", 1); 1971137015Sdes packet_send(); 1972137015Sdes } 1973137015Sdes} 1974137015Sdes 197592555Sdesstatic void 197676259Sgreenclient_init_dispatch_20(void) 197760573Skris{ 197860573Skris dispatch_init(&dispatch_protocol_error); 197998675Sdes 198060573Skris dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); 198160573Skris dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data); 198260573Skris dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); 198360573Skris dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); 198460573Skris dispatch_set(SSH2_MSG_CHANNEL_OPEN, &client_input_channel_open); 198560573Skris dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); 198660573Skris dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); 198776259Sgreen dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &client_input_channel_req); 198860573Skris dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); 1989181111Sdes dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, &channel_input_status_confirm); 1990181111Sdes dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &channel_input_status_confirm); 199192555Sdes dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &client_input_global_request); 199276259Sgreen 199376259Sgreen /* rekeying */ 199476259Sgreen dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit); 199598675Sdes 199698675Sdes /* global request reply messages */ 199798675Sdes dispatch_set(SSH2_MSG_REQUEST_FAILURE, &client_global_request_reply); 199898675Sdes dispatch_set(SSH2_MSG_REQUEST_SUCCESS, &client_global_request_reply); 199960573Skris} 2000181111Sdes 200192555Sdesstatic void 200276259Sgreenclient_init_dispatch_13(void) 200360573Skris{ 200460573Skris dispatch_init(NULL); 200560573Skris dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close); 200660573Skris dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation); 200760573Skris dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data); 200860573Skris dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); 200960573Skris dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); 201060573Skris dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open); 201160573Skris dispatch_set(SSH_SMSG_EXITSTATUS, &client_input_exit_status); 201260573Skris dispatch_set(SSH_SMSG_STDERR_DATA, &client_input_stderr_data); 201360573Skris dispatch_set(SSH_SMSG_STDOUT_DATA, &client_input_stdout_data); 201468700Sgreen 201568700Sgreen dispatch_set(SSH_SMSG_AGENT_OPEN, options.forward_agent ? 2016126274Sdes &client_input_agent_open : &deny_input_open); 201768700Sgreen dispatch_set(SSH_SMSG_X11_OPEN, options.forward_x11 ? 201869587Sgreen &x11_input_open : &deny_input_open); 201960573Skris} 2020181111Sdes 202192555Sdesstatic void 202276259Sgreenclient_init_dispatch_15(void) 202360573Skris{ 202460573Skris client_init_dispatch_13(); 202560573Skris dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof); 202660573Skris dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, & channel_input_oclose); 202760573Skris} 2028181111Sdes 202992555Sdesstatic void 203076259Sgreenclient_init_dispatch(void) 203160573Skris{ 203260573Skris if (compat20) 203360573Skris client_init_dispatch_20(); 203460573Skris else if (compat13) 203560573Skris client_init_dispatch_13(); 203660573Skris else 203760573Skris client_init_dispatch_15(); 203860573Skris} 2039126274Sdes 2040126274Sdes/* client specific fatal cleanup */ 2041126274Sdesvoid 2042126274Sdescleanup_exit(int i) 2043126274Sdes{ 2044126274Sdes leave_raw_mode(); 2045126274Sdes leave_non_blocking(); 2046181111Sdes if (options.control_path != NULL && muxserver_sock != -1) 2047137015Sdes unlink(options.control_path); 2048126274Sdes _exit(i); 2049126274Sdes} 2050