1296853Sdes/* $OpenBSD: clientloop.c,v 1.284 2016/02/08 10:57:07 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 64295367Sdes#include <sys/param.h> /* MIN MAX */ 65162852Sdes#include <sys/types.h> 66162852Sdes#include <sys/ioctl.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> 88295367Sdes#include <limits.h> 89162852Sdes 90181111Sdes#include "openbsd-compat/sys-queue.h" 91162852Sdes#include "xmalloc.h" 9276259Sgreen#include "ssh.h" 9376259Sgreen#include "ssh1.h" 9476259Sgreen#include "ssh2.h" 9557429Smarkm#include "packet.h" 9657429Smarkm#include "buffer.h" 9760573Skris#include "compat.h" 9860573Skris#include "channels.h" 9960573Skris#include "dispatch.h" 10076259Sgreen#include "key.h" 101162852Sdes#include "cipher.h" 10276259Sgreen#include "kex.h" 103295367Sdes#include "myproposal.h" 10476259Sgreen#include "log.h" 105295367Sdes#include "misc.h" 10676259Sgreen#include "readconf.h" 10776259Sgreen#include "clientloop.h" 108157016Sdes#include "sshconnect.h" 10976259Sgreen#include "authfd.h" 11076259Sgreen#include "atomicio.h" 111137015Sdes#include "sshpty.h" 112137015Sdes#include "match.h" 113137015Sdes#include "msg.h" 114295367Sdes#include "ssherr.h" 115295367Sdes#include "hostfile.h" 11660573Skris 11769587Sgreen/* import options */ 11868700Sgreenextern Options options; 11968700Sgreen 12057429Smarkm/* Flag indicating that stdin should be redirected from /dev/null. */ 12157429Smarkmextern int stdin_null_flag; 12257429Smarkm 123126274Sdes/* Flag indicating that no shell has been requested */ 124126274Sdesextern int no_shell_flag; 125126274Sdes 126137015Sdes/* Control socket */ 127204917Sdesextern int muxserver_sock; /* XXX use mux_client_cleanup() instead */ 128137015Sdes 12957429Smarkm/* 13057429Smarkm * Name of the host we are connecting to. This is the name given on the 13157429Smarkm * command line, or the HostName specified for the user-supplied name in a 13257429Smarkm * configuration file. 13357429Smarkm */ 13457429Smarkmextern char *host; 13557429Smarkm 13657429Smarkm/* 13757429Smarkm * Flag to indicate that we have received a window change signal which has 13857429Smarkm * not yet been processed. This will cause a message indicating the new 13957429Smarkm * window size to be sent to the server a little later. This is volatile 14057429Smarkm * because this is updated in a signal handler. 14157429Smarkm */ 14292555Sdesstatic volatile sig_atomic_t received_window_change_signal = 0; 14392555Sdesstatic volatile sig_atomic_t received_signal = 0; 14457429Smarkm 145157016Sdes/* Flag indicating whether the user's terminal is in non-blocking mode. */ 14657429Smarkmstatic int in_non_blocking_mode = 0; 14757429Smarkm 148215116Sdes/* Time when backgrounded control master using ControlPersist should exit */ 149215116Sdesstatic time_t control_persist_exit_time = 0; 150215116Sdes 15157429Smarkm/* Common data for the client loop code. */ 152204917Sdesvolatile sig_atomic_t quit_pending; /* Set non-zero to quit the loop. */ 153181111Sdesstatic int escape_char1; /* Escape character. (proto1 only) */ 154181111Sdesstatic int escape_pending1; /* Last character was an escape (proto1 only) */ 15557429Smarkmstatic int last_was_cr; /* Last character was a newline. */ 156181111Sdesstatic int exit_status; /* Used to store the command exit status. */ 157181111Sdesstatic int stdin_eof; /* EOF has been encountered on stderr. */ 15857429Smarkmstatic Buffer stdin_buffer; /* Buffer for stdin data. */ 15957429Smarkmstatic Buffer stdout_buffer; /* Buffer for stdout data. */ 16057429Smarkmstatic Buffer stderr_buffer; /* Buffer for stderr data. */ 161215116Sdesstatic u_int buffer_high; /* Soft max buffer size. */ 16257429Smarkmstatic int connection_in; /* Connection to server (input). */ 16357429Smarkmstatic int connection_out; /* Connection to server (output). */ 16476259Sgreenstatic int need_rekeying; /* Set to non-zero if rekeying is requested. */ 165215116Sdesstatic int session_closed; /* In SSH2: login session closed. */ 166295367Sdesstatic u_int x11_refuse_time; /* If >0, refuse x11 opens after this time. */ 16757429Smarkm 16892555Sdesstatic void client_init_dispatch(void); 16960573Skrisint session_ident = -1; 17060573Skris 171181111Sdes/* Track escape per proto2 channel */ 172181111Sdesstruct escape_filter_ctx { 173181111Sdes int escape_pending; 174181111Sdes int escape_char; 175137015Sdes}; 176137015Sdes 177181111Sdes/* Context for channel confirmation replies */ 178181111Sdesstruct channel_reply_ctx { 179181111Sdes const char *request_type; 180226046Sdes int id; 181226046Sdes enum confirm_action action; 182181111Sdes}; 183181111Sdes 184181111Sdes/* Global request success/failure callbacks */ 185181111Sdesstruct global_confirm { 186181111Sdes TAILQ_ENTRY(global_confirm) entry; 187181111Sdes global_confirm_cb *cb; 188181111Sdes void *ctx; 189181111Sdes int ref_count; 190181111Sdes}; 191181111SdesTAILQ_HEAD(global_confirms, global_confirm); 192181111Sdesstatic struct global_confirms global_confirms = 193181111Sdes TAILQ_HEAD_INITIALIZER(global_confirms); 194181111Sdes 195137015Sdesvoid ssh_process_session2_setup(int, int, int, Buffer *); 196137015Sdes 19757429Smarkm/* Restores stdin to blocking mode. */ 19857429Smarkm 19992555Sdesstatic void 20076259Sgreenleave_non_blocking(void) 20157429Smarkm{ 20257429Smarkm if (in_non_blocking_mode) { 203137015Sdes unset_nonblock(fileno(stdin)); 20457429Smarkm in_non_blocking_mode = 0; 20557429Smarkm } 20657429Smarkm} 20757429Smarkm 20857429Smarkm/* Puts stdin terminal in non-blocking mode. */ 20957429Smarkm 21092555Sdesstatic void 21176259Sgreenenter_non_blocking(void) 21257429Smarkm{ 21357429Smarkm in_non_blocking_mode = 1; 214137015Sdes set_nonblock(fileno(stdin)); 21557429Smarkm} 21657429Smarkm 21757429Smarkm/* 21857429Smarkm * Signal handler for the window change signal (SIGWINCH). This just sets a 21957429Smarkm * flag indicating that the window has changed. 22057429Smarkm */ 221162852Sdes/*ARGSUSED */ 22292555Sdesstatic void 22357429Smarkmwindow_change_handler(int sig) 22457429Smarkm{ 22557429Smarkm received_window_change_signal = 1; 22657429Smarkm signal(SIGWINCH, window_change_handler); 22757429Smarkm} 22857429Smarkm 22957429Smarkm/* 23057429Smarkm * Signal handler for signals that cause the program to terminate. These 23157429Smarkm * signals must be trapped to restore terminal modes. 23257429Smarkm */ 233162852Sdes/*ARGSUSED */ 23492555Sdesstatic void 23557429Smarkmsignal_handler(int sig) 23657429Smarkm{ 23792555Sdes received_signal = sig; 23892555Sdes quit_pending = 1; 23957429Smarkm} 24057429Smarkm 24157429Smarkm/* 24257429Smarkm * Returns current time in seconds from Jan 1, 1970 with the maximum 24357429Smarkm * available resolution. 24457429Smarkm */ 24557429Smarkm 24692555Sdesstatic double 24776259Sgreenget_current_time(void) 24857429Smarkm{ 24957429Smarkm struct timeval tv; 25057429Smarkm gettimeofday(&tv, NULL); 25157429Smarkm return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0; 25257429Smarkm} 25357429Smarkm 254215116Sdes/* 255215116Sdes * Sets control_persist_exit_time to the absolute time when the 256215116Sdes * backgrounded control master should exit due to expiry of the 257215116Sdes * ControlPersist timeout. Sets it to 0 if we are not a backgrounded 258215116Sdes * control master process, or if there is no ControlPersist timeout. 259215116Sdes */ 260215116Sdesstatic void 261215116Sdesset_control_persist_exit_time(void) 262215116Sdes{ 263215116Sdes if (muxserver_sock == -1 || !options.control_persist 264226046Sdes || options.control_persist_timeout == 0) { 265215116Sdes /* not using a ControlPersist timeout */ 266215116Sdes control_persist_exit_time = 0; 267226046Sdes } else if (channel_still_open()) { 268215116Sdes /* some client connections are still open */ 269215116Sdes if (control_persist_exit_time > 0) 270215116Sdes debug2("%s: cancel scheduled exit", __func__); 271215116Sdes control_persist_exit_time = 0; 272215116Sdes } else if (control_persist_exit_time <= 0) { 273215116Sdes /* a client connection has recently closed */ 274255767Sdes control_persist_exit_time = monotime() + 275215116Sdes (time_t)options.control_persist_timeout; 276215116Sdes debug2("%s: schedule exit in %d seconds", __func__, 277215116Sdes options.control_persist_timeout); 278215116Sdes } 279215116Sdes /* else we are already counting down to the timeout */ 280215116Sdes} 281215116Sdes 282240075Sdes#define SSH_X11_VALID_DISPLAY_CHARS ":/.-_" 283240075Sdesstatic int 284240075Sdesclient_x11_display_valid(const char *display) 285240075Sdes{ 286240075Sdes size_t i, dlen; 287240075Sdes 288296853Sdes if (display == NULL) 289296853Sdes return 0; 290296853Sdes 291240075Sdes dlen = strlen(display); 292240075Sdes for (i = 0; i < dlen; i++) { 293262566Sdes if (!isalnum((u_char)display[i]) && 294240075Sdes strchr(SSH_X11_VALID_DISPLAY_CHARS, display[i]) == NULL) { 295240075Sdes debug("Invalid character '%c' in DISPLAY", display[i]); 296240075Sdes return 0; 297240075Sdes } 298240075Sdes } 299240075Sdes return 1; 300240075Sdes} 301240075Sdes 302295367Sdes#define SSH_X11_PROTO "MIT-MAGIC-COOKIE-1" 303295367Sdes#define X11_TIMEOUT_SLACK 60 304296853Sdesint 305149749Sdesclient_x11_get_proto(const char *display, const char *xauth_path, 306215116Sdes u_int trusted, u_int timeout, char **_proto, char **_data) 307149749Sdes{ 308296853Sdes char cmd[1024], line[512], xdisplay[512]; 309296853Sdes char xauthfile[PATH_MAX], xauthdir[PATH_MAX]; 310149749Sdes static char proto[512], data[512]; 311149749Sdes FILE *f; 312296853Sdes int got_data = 0, generated = 0, do_unlink = 0, i, r; 313149749Sdes struct stat st; 314295367Sdes u_int now, x11_timeout_real; 315149749Sdes 316149749Sdes *_proto = proto; 317149749Sdes *_data = data; 318296853Sdes proto[0] = data[0] = xauthfile[0] = xauthdir[0] = '\0'; 319149749Sdes 320296853Sdes if (!client_x11_display_valid(display)) { 321296853Sdes if (display != NULL) 322296853Sdes logit("DISPLAY \"%s\" invalid; disabling X11 forwarding", 323296853Sdes display); 324296853Sdes return -1; 325296853Sdes } 326296853Sdes if (xauth_path != NULL && stat(xauth_path, &st) == -1) { 327149749Sdes debug("No xauth program."); 328296853Sdes xauth_path = NULL; 329296853Sdes } 330296853Sdes 331296853Sdes if (xauth_path != NULL) { 332149749Sdes /* 333149749Sdes * Handle FamilyLocal case where $DISPLAY does 334149749Sdes * not match an authorization entry. For this we 335149749Sdes * just try "xauth list unix:displaynum.screennum". 336149749Sdes * XXX: "localhost" match to determine FamilyLocal 337149749Sdes * is not perfect. 338149749Sdes */ 339149749Sdes if (strncmp(display, "localhost:", 10) == 0) { 340296853Sdes if ((r = snprintf(xdisplay, sizeof(xdisplay), "unix:%s", 341296853Sdes display + 10)) < 0 || 342296853Sdes (size_t)r >= sizeof(xdisplay)) { 343296853Sdes error("%s: display name too long", __func__); 344296853Sdes return -1; 345296853Sdes } 346149749Sdes display = xdisplay; 347149749Sdes } 348149749Sdes if (trusted == 0) { 349295367Sdes /* 350296853Sdes * Generate an untrusted X11 auth cookie. 351296853Sdes * 352295367Sdes * The authentication cookie should briefly outlive 353295367Sdes * ssh's willingness to forward X11 connections to 354295367Sdes * avoid nasty fail-open behaviour in the X server. 355295367Sdes */ 356296853Sdes mktemp_proto(xauthdir, sizeof(xauthdir)); 357296853Sdes if (mkdtemp(xauthdir) == NULL) { 358296853Sdes error("%s: mkdtemp: %s", 359296853Sdes __func__, strerror(errno)); 360296853Sdes return -1; 361296853Sdes } 362296853Sdes do_unlink = 1; 363296853Sdes if ((r = snprintf(xauthfile, sizeof(xauthfile), 364296853Sdes "%s/xauthfile", xauthdir)) < 0 || 365296853Sdes (size_t)r >= sizeof(xauthfile)) { 366296853Sdes error("%s: xauthfile path too long", __func__); 367296853Sdes unlink(xauthfile); 368296853Sdes rmdir(xauthdir); 369296853Sdes return -1; 370296853Sdes } 371296853Sdes 372295367Sdes if (timeout >= UINT_MAX - X11_TIMEOUT_SLACK) 373295367Sdes x11_timeout_real = UINT_MAX; 374295367Sdes else 375295367Sdes x11_timeout_real = timeout + X11_TIMEOUT_SLACK; 376296853Sdes if ((r = snprintf(cmd, sizeof(cmd), 377296853Sdes "%s -f %s generate %s " SSH_X11_PROTO 378296853Sdes " untrusted timeout %u 2>" _PATH_DEVNULL, 379296853Sdes xauth_path, xauthfile, display, 380296853Sdes x11_timeout_real)) < 0 || 381296853Sdes (size_t)r >= sizeof(cmd)) 382296853Sdes fatal("%s: cmd too long", __func__); 383296853Sdes debug2("%s: %s", __func__, cmd); 384296853Sdes if (x11_refuse_time == 0) { 385296853Sdes now = monotime() + 1; 386296853Sdes if (UINT_MAX - timeout < now) 387296853Sdes x11_refuse_time = UINT_MAX; 388296853Sdes else 389296853Sdes x11_refuse_time = now + timeout; 390296853Sdes channel_set_x11_refuse_time(x11_refuse_time); 391149749Sdes } 392296853Sdes if (system(cmd) == 0) 393296853Sdes generated = 1; 394149749Sdes } 395181111Sdes 396181111Sdes /* 397181111Sdes * When in untrusted mode, we read the cookie only if it was 398181111Sdes * successfully generated as an untrusted one in the step 399181111Sdes * above. 400181111Sdes */ 401181111Sdes if (trusted || generated) { 402181111Sdes snprintf(cmd, sizeof(cmd), 403181111Sdes "%s %s%s list %s 2>" _PATH_DEVNULL, 404181111Sdes xauth_path, 405181111Sdes generated ? "-f " : "" , 406181111Sdes generated ? xauthfile : "", 407181111Sdes display); 408181111Sdes debug2("x11_get_proto: %s", cmd); 409181111Sdes f = popen(cmd, "r"); 410181111Sdes if (f && fgets(line, sizeof(line), f) && 411181111Sdes sscanf(line, "%*s %511s %511s", proto, data) == 2) 412181111Sdes got_data = 1; 413181111Sdes if (f) 414181111Sdes pclose(f); 415296853Sdes } 416149749Sdes } 417149749Sdes 418149749Sdes if (do_unlink) { 419149749Sdes unlink(xauthfile); 420149749Sdes rmdir(xauthdir); 421149749Sdes } 422149749Sdes 423296853Sdes /* Don't fall back to fake X11 data for untrusted forwarding */ 424296853Sdes if (!trusted && !got_data) { 425296853Sdes error("Warning: untrusted X11 forwarding setup failed: " 426296853Sdes "xauth key data not generated"); 427296853Sdes return -1; 428296853Sdes } 429296853Sdes 430149749Sdes /* 431149749Sdes * If we didn't get authentication data, just make up some 432149749Sdes * data. The forwarding code will check the validity of the 433149749Sdes * response anyway, and substitute this data. The X11 434149749Sdes * server, however, will ignore this fake data and use 435149749Sdes * whatever authentication mechanisms it was using otherwise 436149749Sdes * for the local connection. 437149749Sdes */ 438149749Sdes if (!got_data) { 439149749Sdes u_int32_t rnd = 0; 440149749Sdes 441149749Sdes logit("Warning: No xauth data; " 442149749Sdes "using fake authentication data for X11 forwarding."); 443149749Sdes strlcpy(proto, SSH_X11_PROTO, sizeof proto); 444149749Sdes for (i = 0; i < 16; i++) { 445149749Sdes if (i % 4 == 0) 446149749Sdes rnd = arc4random(); 447149749Sdes snprintf(data + 2 * i, sizeof data - 2 * i, "%02x", 448149749Sdes rnd & 0xff); 449149749Sdes rnd >>= 8; 450149749Sdes } 451149749Sdes } 452296853Sdes 453296853Sdes return 0; 454149749Sdes} 455149749Sdes 45657429Smarkm/* 45757429Smarkm * This is called when the interactive is entered. This checks if there is 45857429Smarkm * an EOF coming on stdin. We must check this explicitly, as select() does 45957429Smarkm * not appear to wake up when redirecting from /dev/null. 46057429Smarkm */ 46157429Smarkm 46292555Sdesstatic void 46376259Sgreenclient_check_initial_eof_on_stdin(void) 46457429Smarkm{ 46557429Smarkm int len; 46657429Smarkm char buf[1]; 46757429Smarkm 46857429Smarkm /* 46957429Smarkm * If standard input is to be "redirected from /dev/null", we simply 47057429Smarkm * mark that we have seen an EOF and send an EOF message to the 47157429Smarkm * server. Otherwise, we try to read a single character; it appears 47257429Smarkm * that for some files, such /dev/null, select() never wakes up for 47357429Smarkm * read for this descriptor, which means that we never get EOF. This 47457429Smarkm * way we will get the EOF if stdin comes from /dev/null or similar. 47557429Smarkm */ 47657429Smarkm if (stdin_null_flag) { 47757429Smarkm /* Fake EOF on stdin. */ 47857429Smarkm debug("Sending eof."); 47957429Smarkm stdin_eof = 1; 48057429Smarkm packet_start(SSH_CMSG_EOF); 48157429Smarkm packet_send(); 48257429Smarkm } else { 48357429Smarkm enter_non_blocking(); 48457429Smarkm 48557429Smarkm /* Check for immediate EOF on stdin. */ 48657429Smarkm len = read(fileno(stdin), buf, 1); 48757429Smarkm if (len == 0) { 488181111Sdes /* 489181111Sdes * EOF. Record that we have seen it and send 490181111Sdes * EOF to server. 491181111Sdes */ 49257429Smarkm debug("Sending eof."); 49357429Smarkm stdin_eof = 1; 49457429Smarkm packet_start(SSH_CMSG_EOF); 49557429Smarkm packet_send(); 49657429Smarkm } else if (len > 0) { 49757429Smarkm /* 49857429Smarkm * Got data. We must store the data in the buffer, 49957429Smarkm * and also process it as an escape character if 50057429Smarkm * appropriate. 50157429Smarkm */ 502181111Sdes if ((u_char) buf[0] == escape_char1) 503181111Sdes escape_pending1 = 1; 50476259Sgreen else 50557429Smarkm buffer_append(&stdin_buffer, buf, 1); 50657429Smarkm } 50757429Smarkm leave_non_blocking(); 50857429Smarkm } 50957429Smarkm} 51057429Smarkm 51157429Smarkm 51257429Smarkm/* 51357429Smarkm * Make packets from buffered stdin data, and buffer them for sending to the 51457429Smarkm * connection. 51557429Smarkm */ 51657429Smarkm 51792555Sdesstatic void 51876259Sgreenclient_make_packets_from_stdin_data(void) 51957429Smarkm{ 52076259Sgreen u_int len; 52157429Smarkm 52257429Smarkm /* Send buffered stdin data to the server. */ 52357429Smarkm while (buffer_len(&stdin_buffer) > 0 && 52492555Sdes packet_not_very_much_data_to_write()) { 52557429Smarkm len = buffer_len(&stdin_buffer); 52657429Smarkm /* Keep the packets at reasonable size. */ 52757429Smarkm if (len > packet_get_maxsize()) 52857429Smarkm len = packet_get_maxsize(); 52957429Smarkm packet_start(SSH_CMSG_STDIN_DATA); 53057429Smarkm packet_put_string(buffer_ptr(&stdin_buffer), len); 53157429Smarkm packet_send(); 53257429Smarkm buffer_consume(&stdin_buffer, len); 53357429Smarkm /* If we have a pending EOF, send it now. */ 53457429Smarkm if (stdin_eof && buffer_len(&stdin_buffer) == 0) { 53557429Smarkm packet_start(SSH_CMSG_EOF); 53657429Smarkm packet_send(); 53757429Smarkm } 53857429Smarkm } 53957429Smarkm} 54057429Smarkm 54157429Smarkm/* 54257429Smarkm * Checks if the client window has changed, and sends a packet about it to 54357429Smarkm * the server if so. The actual change is detected elsewhere (by a software 54457429Smarkm * interrupt on Unix); this just checks the flag and sends a message if 54557429Smarkm * appropriate. 54657429Smarkm */ 54757429Smarkm 54892555Sdesstatic void 54976259Sgreenclient_check_window_change(void) 55057429Smarkm{ 55160573Skris struct winsize ws; 55257429Smarkm 55360573Skris if (! received_window_change_signal) 55460573Skris return; 55560573Skris /** XXX race */ 55660573Skris received_window_change_signal = 0; 55757429Smarkm 55869587Sgreen debug2("client_check_window_change: changed"); 55960573Skris 56060573Skris if (compat20) { 561137015Sdes channel_send_window_changes(); 56260573Skris } else { 563137015Sdes if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0) 564137015Sdes return; 56560573Skris packet_start(SSH_CMSG_WINDOW_SIZE); 566162852Sdes packet_put_int((u_int)ws.ws_row); 567162852Sdes packet_put_int((u_int)ws.ws_col); 568162852Sdes packet_put_int((u_int)ws.ws_xpixel); 569162852Sdes packet_put_int((u_int)ws.ws_ypixel); 57060573Skris packet_send(); 57157429Smarkm } 57257429Smarkm} 57357429Smarkm 574295367Sdesstatic int 575126274Sdesclient_global_request_reply(int type, u_int32_t seq, void *ctxt) 576126274Sdes{ 577181111Sdes struct global_confirm *gc; 578181111Sdes 579181111Sdes if ((gc = TAILQ_FIRST(&global_confirms)) == NULL) 580295367Sdes return 0; 581181111Sdes if (gc->cb != NULL) 582181111Sdes gc->cb(type, seq, gc->ctx); 583181111Sdes if (--gc->ref_count <= 0) { 584181111Sdes TAILQ_REMOVE(&global_confirms, gc, entry); 585264377Sdes explicit_bzero(gc, sizeof(*gc)); 586255767Sdes free(gc); 587181111Sdes } 588181111Sdes 589197679Sdes packet_set_alive_timeouts(0); 590295367Sdes return 0; 591126274Sdes} 592126274Sdes 593126274Sdesstatic void 594126274Sdesserver_alive_check(void) 595126274Sdes{ 596197679Sdes if (packet_inc_alive_timeouts() > options.server_alive_count_max) { 597221420Sdes logit("Timeout, server %s not responding.", host); 598164146Sdes cleanup_exit(255); 599164146Sdes } 600126274Sdes packet_start(SSH2_MSG_GLOBAL_REQUEST); 601126274Sdes packet_put_cstring("keepalive@openssh.com"); 602126274Sdes packet_put_char(1); /* boolean: want reply */ 603126274Sdes packet_send(); 604181111Sdes /* Insert an empty placeholder to maintain ordering */ 605181111Sdes client_register_global_confirm(NULL, NULL); 606126274Sdes} 607126274Sdes 60857429Smarkm/* 60957429Smarkm * Waits until the client can do something (some data becomes available on 61057429Smarkm * one of the file descriptors). 61157429Smarkm */ 61292555Sdesstatic void 61376259Sgreenclient_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, 614137015Sdes int *maxfdp, u_int *nallocp, int rekeying) 61557429Smarkm{ 616126274Sdes struct timeval tv, *tvp; 617215116Sdes int timeout_secs; 618255767Sdes time_t minwait_secs = 0, server_alive_time = 0, now = monotime(); 619126274Sdes int ret; 620126274Sdes 62176259Sgreen /* Add any selections by the channel mechanism. */ 622240075Sdes channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, 623240075Sdes &minwait_secs, rekeying); 62457429Smarkm 62560573Skris if (!compat20) { 62660573Skris /* Read from the connection, unless our buffers are full. */ 62760573Skris if (buffer_len(&stdout_buffer) < buffer_high && 62860573Skris buffer_len(&stderr_buffer) < buffer_high && 62960573Skris channel_not_very_much_buffered_data()) 63076259Sgreen FD_SET(connection_in, *readsetp); 63160573Skris /* 63260573Skris * Read from stdin, unless we have seen EOF or have very much 63360573Skris * buffered data to send to the server. 63460573Skris */ 63560573Skris if (!stdin_eof && packet_not_very_much_data_to_write()) 63676259Sgreen FD_SET(fileno(stdin), *readsetp); 63760573Skris 63860573Skris /* Select stdout/stderr if have data in buffer. */ 63960573Skris if (buffer_len(&stdout_buffer) > 0) 64076259Sgreen FD_SET(fileno(stdout), *writesetp); 64160573Skris if (buffer_len(&stderr_buffer) > 0) 64276259Sgreen FD_SET(fileno(stderr), *writesetp); 64360573Skris } else { 64492555Sdes /* channel_prepare_select could have closed the last channel */ 64592555Sdes if (session_closed && !channel_still_open() && 64692555Sdes !packet_have_data_to_write()) { 64792555Sdes /* clear mask since we did not call select() */ 64892555Sdes memset(*readsetp, 0, *nallocp); 64992555Sdes memset(*writesetp, 0, *nallocp); 65092555Sdes return; 65192555Sdes } else { 65292555Sdes FD_SET(connection_in, *readsetp); 65392555Sdes } 65460573Skris } 65557429Smarkm 65657429Smarkm /* Select server connection if have data to write to the server. */ 65757429Smarkm if (packet_have_data_to_write()) 65876259Sgreen FD_SET(connection_out, *writesetp); 65957429Smarkm 66057429Smarkm /* 66157429Smarkm * Wait for something to happen. This will suspend the process until 66257429Smarkm * some selected descriptor can be read, written, or has some other 663215116Sdes * event pending, or a timeout expires. 66457429Smarkm */ 66557429Smarkm 666215116Sdes timeout_secs = INT_MAX; /* we use INT_MAX to mean no timeout */ 667255767Sdes if (options.server_alive_interval > 0 && compat20) { 668215116Sdes timeout_secs = options.server_alive_interval; 669255767Sdes server_alive_time = now + options.server_alive_interval; 670255767Sdes } 671255767Sdes if (options.rekey_interval > 0 && compat20 && !rekeying) 672255767Sdes timeout_secs = MIN(timeout_secs, packet_get_rekey_timeout()); 673215116Sdes set_control_persist_exit_time(); 674215116Sdes if (control_persist_exit_time > 0) { 675215116Sdes timeout_secs = MIN(timeout_secs, 676255767Sdes control_persist_exit_time - now); 677215116Sdes if (timeout_secs < 0) 678215116Sdes timeout_secs = 0; 679215116Sdes } 680240075Sdes if (minwait_secs != 0) 681240075Sdes timeout_secs = MIN(timeout_secs, (int)minwait_secs); 682215116Sdes if (timeout_secs == INT_MAX) 683126274Sdes tvp = NULL; 684137015Sdes else { 685215116Sdes tv.tv_sec = timeout_secs; 686126274Sdes tv.tv_usec = 0; 687126274Sdes tvp = &tv; 688126274Sdes } 689215116Sdes 690126274Sdes ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp); 691126274Sdes if (ret < 0) { 69257429Smarkm char buf[100]; 69376259Sgreen 69476259Sgreen /* 69576259Sgreen * We have to clear the select masks, because we return. 69676259Sgreen * We have to return, because the mainloop checks for the flags 69776259Sgreen * set by the signal handlers. 69876259Sgreen */ 69992555Sdes memset(*readsetp, 0, *nallocp); 70092555Sdes memset(*writesetp, 0, *nallocp); 70176259Sgreen 70257429Smarkm if (errno == EINTR) 70357429Smarkm return; 70457429Smarkm /* Note: we might still have data in the buffers. */ 70557429Smarkm snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno)); 70657429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 70757429Smarkm quit_pending = 1; 708255767Sdes } else if (ret == 0) { 709255767Sdes /* 710255767Sdes * Timeout. Could have been either keepalive or rekeying. 711255767Sdes * Keepalive we check here, rekeying is checked in clientloop. 712255767Sdes */ 713255767Sdes if (server_alive_time != 0 && server_alive_time <= monotime()) 714255767Sdes server_alive_check(); 715255767Sdes } 716255767Sdes 71757429Smarkm} 71857429Smarkm 71992555Sdesstatic void 72065668Skrisclient_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr) 72157429Smarkm{ 72257429Smarkm /* Flush stdout and stderr buffers. */ 72365668Skris if (buffer_len(bout) > 0) 724181111Sdes atomicio(vwrite, fileno(stdout), buffer_ptr(bout), 725181111Sdes buffer_len(bout)); 72665668Skris if (buffer_len(berr) > 0) 727181111Sdes atomicio(vwrite, fileno(stderr), buffer_ptr(berr), 728181111Sdes buffer_len(berr)); 72957429Smarkm 730226046Sdes leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 73157429Smarkm 73257429Smarkm /* 73357429Smarkm * Free (and clear) the buffer to reduce the amount of data that gets 73457429Smarkm * written to swap. 73557429Smarkm */ 73665668Skris buffer_free(bin); 73765668Skris buffer_free(bout); 73865668Skris buffer_free(berr); 73957429Smarkm 74057429Smarkm /* Send the suspend signal to the program itself. */ 74157429Smarkm kill(getpid(), SIGTSTP); 74257429Smarkm 743146998Sdes /* Reset window sizes in case they have changed */ 744146998Sdes received_window_change_signal = 1; 74557429Smarkm 74657429Smarkm /* OK, we have been continued by the user. Reinitialize buffers. */ 74765668Skris buffer_init(bin); 74865668Skris buffer_init(bout); 74965668Skris buffer_init(berr); 75057429Smarkm 751226046Sdes enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 75257429Smarkm} 75357429Smarkm 75492555Sdesstatic void 755162852Sdesclient_process_net_input(fd_set *readset) 75657429Smarkm{ 757296853Sdes int len; 758197679Sdes char buf[SSH_IOBUFSZ]; 75957429Smarkm 76057429Smarkm /* 76157429Smarkm * Read input from the server, and add any such data to the buffer of 76257429Smarkm * the packet subsystem. 76357429Smarkm */ 76457429Smarkm if (FD_ISSET(connection_in, readset)) { 76557429Smarkm /* Read as much as possible. */ 766296853Sdes len = read(connection_in, buf, sizeof(buf)); 767296853Sdes if (len == 0) { 768181111Sdes /* 769181111Sdes * Received EOF. The remote host has closed the 770181111Sdes * connection. 771181111Sdes */ 772181111Sdes snprintf(buf, sizeof buf, 773181111Sdes "Connection to %.300s closed by remote host.\r\n", 774181111Sdes host); 77557429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 77657429Smarkm quit_pending = 1; 77757429Smarkm return; 77857429Smarkm } 77957429Smarkm /* 78057429Smarkm * There is a kernel bug on Solaris that causes select to 78157429Smarkm * sometimes wake up even though there is no data available. 78257429Smarkm */ 783181111Sdes if (len < 0 && 784181111Sdes (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)) 78557429Smarkm len = 0; 78657429Smarkm 78757429Smarkm if (len < 0) { 788181111Sdes /* 789181111Sdes * An error has encountered. Perhaps there is a 790181111Sdes * network problem. 791181111Sdes */ 792181111Sdes snprintf(buf, sizeof buf, 793181111Sdes "Read from remote host %.300s: %.100s\r\n", 794181111Sdes host, strerror(errno)); 79557429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 79657429Smarkm quit_pending = 1; 79757429Smarkm return; 79857429Smarkm } 79957429Smarkm packet_process_incoming(buf, len); 80057429Smarkm } 80160573Skris} 80260573Skris 80398675Sdesstatic void 804181111Sdesclient_status_confirm(int type, Channel *c, void *ctx) 805137015Sdes{ 806181111Sdes struct channel_reply_ctx *cr = (struct channel_reply_ctx *)ctx; 807181111Sdes char errmsg[256]; 808181111Sdes int tochan; 809137015Sdes 810226046Sdes /* 811226046Sdes * If a TTY was explicitly requested, then a failure to allocate 812226046Sdes * one is fatal. 813226046Sdes */ 814226046Sdes if (cr->action == CONFIRM_TTY && 815226046Sdes (options.request_tty == REQUEST_TTY_FORCE || 816226046Sdes options.request_tty == REQUEST_TTY_YES)) 817226046Sdes cr->action = CONFIRM_CLOSE; 818226046Sdes 819181111Sdes /* XXX supress on mux _client_ quietmode */ 820181111Sdes tochan = options.log_level >= SYSLOG_LEVEL_ERROR && 821204917Sdes c->ctl_chan != -1 && c->extended_usage == CHAN_EXTENDED_WRITE; 822137015Sdes 823181111Sdes if (type == SSH2_MSG_CHANNEL_SUCCESS) { 824181111Sdes debug2("%s request accepted on channel %d", 825181111Sdes cr->request_type, c->self); 826181111Sdes } else if (type == SSH2_MSG_CHANNEL_FAILURE) { 827181111Sdes if (tochan) { 828181111Sdes snprintf(errmsg, sizeof(errmsg), 829181111Sdes "%s request failed\r\n", cr->request_type); 830181111Sdes } else { 831181111Sdes snprintf(errmsg, sizeof(errmsg), 832181111Sdes "%s request failed on channel %d", 833181111Sdes cr->request_type, c->self); 834181111Sdes } 835181111Sdes /* If error occurred on primary session channel, then exit */ 836226046Sdes if (cr->action == CONFIRM_CLOSE && c->self == session_ident) 837181111Sdes fatal("%s", errmsg); 838226046Sdes /* 839226046Sdes * If error occurred on mux client, append to 840226046Sdes * their stderr. 841226046Sdes */ 842226046Sdes if (tochan) { 843226046Sdes buffer_append(&c->extended, errmsg, 844226046Sdes strlen(errmsg)); 845226046Sdes } else 846181111Sdes error("%s", errmsg); 847226046Sdes if (cr->action == CONFIRM_TTY) { 848226046Sdes /* 849226046Sdes * If a TTY allocation error occurred, then arrange 850226046Sdes * for the correct TTY to leave raw mode. 851226046Sdes */ 852226046Sdes if (c->self == session_ident) 853226046Sdes leave_raw_mode(0); 854226046Sdes else 855226046Sdes mux_tty_alloc_failed(c); 856226046Sdes } else if (cr->action == CONFIRM_CLOSE) { 857181111Sdes chan_read_failed(c); 858181111Sdes chan_write_failed(c); 859181111Sdes } 860137015Sdes } 861255767Sdes free(cr); 862137015Sdes} 863137015Sdes 864137015Sdesstatic void 865181111Sdesclient_abandon_status_confirm(Channel *c, void *ctx) 866137015Sdes{ 867255767Sdes free(ctx); 868137015Sdes} 869137015Sdes 870226046Sdesvoid 871226046Sdesclient_expect_confirm(int id, const char *request, 872226046Sdes enum confirm_action action) 873137015Sdes{ 874258343Sdes struct channel_reply_ctx *cr = xcalloc(1, sizeof(*cr)); 875137015Sdes 876181111Sdes cr->request_type = request; 877226046Sdes cr->action = action; 878137015Sdes 879181111Sdes channel_register_status_confirm(id, client_status_confirm, 880181111Sdes client_abandon_status_confirm, cr); 881181111Sdes} 882137015Sdes 883181111Sdesvoid 884181111Sdesclient_register_global_confirm(global_confirm_cb *cb, void *ctx) 885181111Sdes{ 886181111Sdes struct global_confirm *gc, *last_gc; 887137015Sdes 888181111Sdes /* Coalesce identical callbacks */ 889181111Sdes last_gc = TAILQ_LAST(&global_confirms, global_confirms); 890181111Sdes if (last_gc && last_gc->cb == cb && last_gc->ctx == ctx) { 891181111Sdes if (++last_gc->ref_count >= INT_MAX) 892181111Sdes fatal("%s: last_gc->ref_count = %d", 893181111Sdes __func__, last_gc->ref_count); 894146998Sdes return; 895146998Sdes } 896146998Sdes 897258343Sdes gc = xcalloc(1, sizeof(*gc)); 898181111Sdes gc->cb = cb; 899181111Sdes gc->ctx = ctx; 900181111Sdes gc->ref_count = 1; 901181111Sdes TAILQ_INSERT_TAIL(&global_confirms, gc, entry); 902137015Sdes} 903137015Sdes 904137015Sdesstatic void 90598675Sdesprocess_cmdline(void) 90698675Sdes{ 90798675Sdes void (*handler)(int); 908295367Sdes char *s, *cmd; 909295367Sdes int ok, delete = 0, local = 0, remote = 0, dynamic = 0; 910295367Sdes struct Forward fwd; 91198675Sdes 912264377Sdes memset(&fwd, 0, sizeof(fwd)); 913181111Sdes 914226046Sdes leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 91598675Sdes handler = signal(SIGINT, SIG_IGN); 91698675Sdes cmd = s = read_passphrase("\r\nssh> ", RP_ECHO); 91798675Sdes if (s == NULL) 91898675Sdes goto out; 919262566Sdes while (isspace((u_char)*s)) 92098675Sdes s++; 921137015Sdes if (*s == '-') 922137015Sdes s++; /* Skip cmdline '-', if any */ 923137015Sdes if (*s == '\0') 92498675Sdes goto out; 925137015Sdes 926137015Sdes if (*s == 'h' || *s == 'H' || *s == '?') { 927137015Sdes logit("Commands:"); 928162852Sdes logit(" -L[bind_address:]port:host:hostport " 929162852Sdes "Request local forward"); 930162852Sdes logit(" -R[bind_address:]port:host:hostport " 931162852Sdes "Request remote forward"); 932192595Sdes logit(" -D[bind_address:]port " 933192595Sdes "Request dynamic forward"); 934240075Sdes logit(" -KL[bind_address:]port " 935240075Sdes "Cancel local forward"); 936162852Sdes logit(" -KR[bind_address:]port " 937162852Sdes "Cancel remote forward"); 938240075Sdes logit(" -KD[bind_address:]port " 939240075Sdes "Cancel dynamic forward"); 940157016Sdes if (!options.permit_local_command) 941157016Sdes goto out; 942162852Sdes logit(" !args " 943162852Sdes "Execute local command"); 944137015Sdes goto out; 945137015Sdes } 946137015Sdes 947157016Sdes if (*s == '!' && options.permit_local_command) { 948157016Sdes s++; 949157016Sdes ssh_local_cmd(s); 950157016Sdes goto out; 951157016Sdes } 952157016Sdes 953137015Sdes if (*s == 'K') { 954137015Sdes delete = 1; 955137015Sdes s++; 956137015Sdes } 957192595Sdes if (*s == 'L') 958192595Sdes local = 1; 959192595Sdes else if (*s == 'R') 960192595Sdes remote = 1; 961192595Sdes else if (*s == 'D') 962192595Sdes dynamic = 1; 963192595Sdes else { 964124208Sdes logit("Invalid command."); 96598675Sdes goto out; 96698675Sdes } 967192595Sdes 968240075Sdes if (delete && !compat20) { 969124208Sdes logit("Not supported for SSH protocol version 1."); 97098675Sdes goto out; 97198675Sdes } 972137015Sdes 973262566Sdes while (isspace((u_char)*++s)) 974181111Sdes ; 97598675Sdes 976204917Sdes /* XXX update list of forwards in options */ 977137015Sdes if (delete) { 978295367Sdes /* We pass 1 for dynamicfwd to restrict to 1 or 2 fields. */ 979295367Sdes if (!parse_forward(&fwd, s, 1, 0)) { 980295367Sdes logit("Bad forwarding close specification."); 981137015Sdes goto out; 982137015Sdes } 983240075Sdes if (remote) 984295367Sdes ok = channel_request_rforward_cancel(&fwd) == 0; 985240075Sdes else if (dynamic) 986295367Sdes ok = channel_cancel_lport_listener(&fwd, 987295367Sdes 0, &options.fwd_opts) > 0; 988240075Sdes else 989295367Sdes ok = channel_cancel_lport_listener(&fwd, 990295367Sdes CHANNEL_CANCEL_PORT_STATIC, 991295367Sdes &options.fwd_opts) > 0; 992240075Sdes if (!ok) { 993240075Sdes logit("Unkown port forwarding."); 994240075Sdes goto out; 995240075Sdes } 996240075Sdes logit("Canceled forwarding."); 997137015Sdes } else { 998192595Sdes if (!parse_forward(&fwd, s, dynamic, remote)) { 999137015Sdes logit("Bad forwarding specification."); 1000137015Sdes goto out; 1001137015Sdes } 1002192595Sdes if (local || dynamic) { 1003295367Sdes if (!channel_setup_local_fwd_listener(&fwd, 1004295367Sdes &options.fwd_opts)) { 1005137015Sdes logit("Port forwarding failed."); 1006137015Sdes goto out; 1007137015Sdes } 1008146998Sdes } else { 1009295367Sdes if (channel_request_remote_forwarding(&fwd) < 0) { 1010162852Sdes logit("Port forwarding failed."); 1011162852Sdes goto out; 1012162852Sdes } 1013146998Sdes } 1014137015Sdes logit("Forwarding port."); 1015137015Sdes } 1016137015Sdes 101798675Sdesout: 101898675Sdes signal(SIGINT, handler); 1019226046Sdes enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 1020255767Sdes free(cmd); 1021255767Sdes free(fwd.listen_host); 1022295367Sdes free(fwd.listen_path); 1023255767Sdes free(fwd.connect_host); 1024295367Sdes free(fwd.connect_path); 102598675Sdes} 102698675Sdes 1027248619Sdes/* reasons to suppress output of an escape command in help output */ 1028248619Sdes#define SUPPRESS_NEVER 0 /* never suppress, always show */ 1029248619Sdes#define SUPPRESS_PROTO1 1 /* don't show in protocol 1 sessions */ 1030248619Sdes#define SUPPRESS_MUXCLIENT 2 /* don't show in mux client sessions */ 1031248619Sdes#define SUPPRESS_MUXMASTER 4 /* don't show in mux master sessions */ 1032248619Sdes#define SUPPRESS_SYSLOG 8 /* don't show when logging to syslog */ 1033248619Sdesstruct escape_help_text { 1034248619Sdes const char *cmd; 1035248619Sdes const char *text; 1036248619Sdes unsigned int flags; 1037248619Sdes}; 1038248619Sdesstatic struct escape_help_text esc_txt[] = { 1039248619Sdes {".", "terminate session", SUPPRESS_MUXMASTER}, 1040248619Sdes {".", "terminate connection (and any multiplexed sessions)", 1041248619Sdes SUPPRESS_MUXCLIENT}, 1042248619Sdes {"B", "send a BREAK to the remote system", SUPPRESS_PROTO1}, 1043248619Sdes {"C", "open a command line", SUPPRESS_MUXCLIENT}, 1044248619Sdes {"R", "request rekey", SUPPRESS_PROTO1}, 1045248619Sdes {"V/v", "decrease/increase verbosity (LogLevel)", SUPPRESS_MUXCLIENT}, 1046248619Sdes {"^Z", "suspend ssh", SUPPRESS_MUXCLIENT}, 1047248619Sdes {"#", "list forwarded connections", SUPPRESS_NEVER}, 1048248619Sdes {"&", "background ssh (when waiting for connections to terminate)", 1049248619Sdes SUPPRESS_MUXCLIENT}, 1050248619Sdes {"?", "this message", SUPPRESS_NEVER}, 1051248619Sdes}; 1052248619Sdes 1053248619Sdesstatic void 1054248619Sdesprint_escape_help(Buffer *b, int escape_char, int protocol2, int mux_client, 1055248619Sdes int using_stderr) 1056248619Sdes{ 1057248619Sdes unsigned int i, suppress_flags; 1058248619Sdes char string[1024]; 1059248619Sdes 1060248619Sdes snprintf(string, sizeof string, "%c?\r\n" 1061248619Sdes "Supported escape sequences:\r\n", escape_char); 1062248619Sdes buffer_append(b, string, strlen(string)); 1063248619Sdes 1064248619Sdes suppress_flags = (protocol2 ? 0 : SUPPRESS_PROTO1) | 1065248619Sdes (mux_client ? SUPPRESS_MUXCLIENT : 0) | 1066248619Sdes (mux_client ? 0 : SUPPRESS_MUXMASTER) | 1067248619Sdes (using_stderr ? 0 : SUPPRESS_SYSLOG); 1068248619Sdes 1069248619Sdes for (i = 0; i < sizeof(esc_txt)/sizeof(esc_txt[0]); i++) { 1070248619Sdes if (esc_txt[i].flags & suppress_flags) 1071248619Sdes continue; 1072248619Sdes snprintf(string, sizeof string, " %c%-3s - %s\r\n", 1073248619Sdes escape_char, esc_txt[i].cmd, esc_txt[i].text); 1074248619Sdes buffer_append(b, string, strlen(string)); 1075248619Sdes } 1076248619Sdes 1077248619Sdes snprintf(string, sizeof string, 1078248619Sdes " %c%c - send the escape character by typing it twice\r\n" 1079248619Sdes "(Note that escapes are only recognized immediately after " 1080248619Sdes "newline.)\r\n", escape_char, escape_char); 1081248619Sdes buffer_append(b, string, strlen(string)); 1082248619Sdes} 1083248619Sdes 1084181111Sdes/* 1085181111Sdes * Process the characters one by one, call with c==NULL for proto1 case. 1086181111Sdes */ 108792555Sdesstatic int 1088181111Sdesprocess_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr, 1089181111Sdes char *buf, int len) 109065668Skris{ 109165668Skris char string[1024]; 109265668Skris pid_t pid; 109365668Skris int bytes = 0; 109476259Sgreen u_int i; 109576259Sgreen u_char ch; 109665668Skris char *s; 1097181111Sdes int *escape_pendingp, escape_char; 1098181111Sdes struct escape_filter_ctx *efc; 109965668Skris 1100181111Sdes if (c == NULL) { 1101181111Sdes escape_pendingp = &escape_pending1; 1102181111Sdes escape_char = escape_char1; 1103181111Sdes } else { 1104181111Sdes if (c->filter_ctx == NULL) 1105181111Sdes return 0; 1106181111Sdes efc = (struct escape_filter_ctx *)c->filter_ctx; 1107181111Sdes escape_pendingp = &efc->escape_pending; 1108181111Sdes escape_char = efc->escape_char; 1109181111Sdes } 1110181111Sdes 1111149749Sdes if (len <= 0) 1112149749Sdes return (0); 1113149749Sdes 1114149749Sdes for (i = 0; i < (u_int)len; i++) { 111565668Skris /* Get one character at a time. */ 111665668Skris ch = buf[i]; 111765668Skris 1118181111Sdes if (*escape_pendingp) { 111965668Skris /* We have previously seen an escape character. */ 112065668Skris /* Clear the flag now. */ 1121181111Sdes *escape_pendingp = 0; 112265668Skris 112365668Skris /* Process the escaped character. */ 112465668Skris switch (ch) { 112565668Skris case '.': 112665668Skris /* Terminate the connection. */ 1127181111Sdes snprintf(string, sizeof string, "%c.\r\n", 1128181111Sdes escape_char); 112965668Skris buffer_append(berr, string, strlen(string)); 113065668Skris 1131204917Sdes if (c && c->ctl_chan != -1) { 1132181111Sdes chan_read_failed(c); 1133181111Sdes chan_write_failed(c); 1134255767Sdes if (c->detach_user) 1135255767Sdes c->detach_user(c->self, NULL); 1136255767Sdes c->type = SSH_CHANNEL_ABANDONED; 1137255767Sdes buffer_clear(&c->input); 1138255767Sdes chan_ibuf_empty(c); 1139181111Sdes return 0; 1140181111Sdes } else 1141181111Sdes quit_pending = 1; 114265668Skris return -1; 114365668Skris 114465668Skris case 'Z' - 64: 1145181111Sdes /* XXX support this for mux clients */ 1146204917Sdes if (c && c->ctl_chan != -1) { 1147248619Sdes char b[16]; 1148181111Sdes noescape: 1149248619Sdes if (ch == 'Z' - 64) 1150248619Sdes snprintf(b, sizeof b, "^Z"); 1151248619Sdes else 1152248619Sdes snprintf(b, sizeof b, "%c", ch); 1153181111Sdes snprintf(string, sizeof string, 1154248619Sdes "%c%s escape not available to " 1155181111Sdes "multiplexed sessions\r\n", 1156248619Sdes escape_char, b); 1157181111Sdes buffer_append(berr, string, 1158181111Sdes strlen(string)); 1159181111Sdes continue; 1160181111Sdes } 1161181111Sdes /* Suspend the program. Inform the user */ 1162181111Sdes snprintf(string, sizeof string, 1163181111Sdes "%c^Z [suspend ssh]\r\n", escape_char); 116465668Skris buffer_append(berr, string, strlen(string)); 116565668Skris 116665668Skris /* Restore terminal modes and suspend. */ 116765668Skris client_suspend_self(bin, bout, berr); 116865668Skris 116965668Skris /* We have been continued. */ 117065668Skris continue; 117165668Skris 1172124208Sdes case 'B': 1173124208Sdes if (compat20) { 1174124208Sdes snprintf(string, sizeof string, 1175124208Sdes "%cB\r\n", escape_char); 1176124208Sdes buffer_append(berr, string, 1177124208Sdes strlen(string)); 1178262566Sdes channel_request_start(c->self, 1179124208Sdes "break", 0); 1180124208Sdes packet_put_int(1000); 1181124208Sdes packet_send(); 1182124208Sdes } 1183124208Sdes continue; 1184124208Sdes 118576259Sgreen case 'R': 118676259Sgreen if (compat20) { 118776259Sgreen if (datafellows & SSH_BUG_NOREKEY) 1188181111Sdes logit("Server does not " 1189181111Sdes "support re-keying"); 119076259Sgreen else 119176259Sgreen need_rekeying = 1; 119276259Sgreen } 119376259Sgreen continue; 119476259Sgreen 1195248619Sdes case 'V': 1196248619Sdes /* FALLTHROUGH */ 1197248619Sdes case 'v': 1198248619Sdes if (c && c->ctl_chan != -1) 1199248619Sdes goto noescape; 1200248619Sdes if (!log_is_on_stderr()) { 1201248619Sdes snprintf(string, sizeof string, 1202248619Sdes "%c%c [Logging to syslog]\r\n", 1203248619Sdes escape_char, ch); 1204248619Sdes buffer_append(berr, string, 1205248619Sdes strlen(string)); 1206248619Sdes continue; 1207248619Sdes } 1208248619Sdes if (ch == 'V' && options.log_level > 1209248619Sdes SYSLOG_LEVEL_QUIET) 1210248619Sdes log_change_level(--options.log_level); 1211248619Sdes if (ch == 'v' && options.log_level < 1212248619Sdes SYSLOG_LEVEL_DEBUG3) 1213248619Sdes log_change_level(++options.log_level); 1214248619Sdes snprintf(string, sizeof string, 1215248619Sdes "%c%c [LogLevel %s]\r\n", escape_char, ch, 1216248619Sdes log_level_name(options.log_level)); 1217248619Sdes buffer_append(berr, string, strlen(string)); 1218248619Sdes continue; 1219248619Sdes 122065668Skris case '&': 1221204917Sdes if (c && c->ctl_chan != -1) 1222181111Sdes goto noescape; 122365668Skris /* 1224181111Sdes * Detach the program (continue to serve 1225181111Sdes * connections, but put in background and no 1226181111Sdes * more new connections). 122765668Skris */ 122865668Skris /* Restore tty modes. */ 1229226046Sdes leave_raw_mode( 1230226046Sdes options.request_tty == REQUEST_TTY_FORCE); 123165668Skris 123265668Skris /* Stop listening for new connections. */ 123365668Skris channel_stop_listening(); 123465668Skris 123592555Sdes snprintf(string, sizeof string, 123692555Sdes "%c& [backgrounded]\n", escape_char); 123792555Sdes buffer_append(berr, string, strlen(string)); 123865668Skris 123965668Skris /* Fork into background. */ 124065668Skris pid = fork(); 124165668Skris if (pid < 0) { 124265668Skris error("fork: %.100s", strerror(errno)); 124365668Skris continue; 124465668Skris } 124565668Skris if (pid != 0) { /* This is the parent. */ 124665668Skris /* The parent just exits. */ 124765668Skris exit(0); 124865668Skris } 124965668Skris /* The child continues serving connections. */ 125092555Sdes if (compat20) { 125192555Sdes buffer_append(bin, "\004", 1); 125292555Sdes /* fake EOF on stdin */ 125392555Sdes return -1; 125492555Sdes } else if (!stdin_eof) { 125592555Sdes /* 1256181111Sdes * Sending SSH_CMSG_EOF alone does not 1257181111Sdes * always appear to be enough. So we 1258181111Sdes * try to send an EOF character first. 125992555Sdes */ 126092555Sdes packet_start(SSH_CMSG_STDIN_DATA); 126192555Sdes packet_put_string("\004", 1); 126292555Sdes packet_send(); 126392555Sdes /* Close stdin. */ 126492555Sdes stdin_eof = 1; 126592555Sdes if (buffer_len(bin) == 0) { 126692555Sdes packet_start(SSH_CMSG_EOF); 126792555Sdes packet_send(); 126892555Sdes } 126992555Sdes } 127092555Sdes continue; 127165668Skris 127265668Skris case '?': 1273248619Sdes print_escape_help(berr, escape_char, compat20, 1274248619Sdes (c && c->ctl_chan != -1), 1275248619Sdes log_is_on_stderr()); 127665668Skris continue; 127765668Skris 127865668Skris case '#': 1279181111Sdes snprintf(string, sizeof string, "%c#\r\n", 1280181111Sdes escape_char); 128165668Skris buffer_append(berr, string, strlen(string)); 128265668Skris s = channel_open_message(); 128365668Skris buffer_append(berr, s, strlen(s)); 1284255767Sdes free(s); 128565668Skris continue; 128665668Skris 128798675Sdes case 'C': 1288204917Sdes if (c && c->ctl_chan != -1) 1289192595Sdes goto noescape; 129098675Sdes process_cmdline(); 129198675Sdes continue; 129298675Sdes 129365668Skris default: 129465668Skris if (ch != escape_char) { 129565668Skris buffer_put_char(bin, escape_char); 129665668Skris bytes++; 129765668Skris } 129865668Skris /* Escaped characters fall through here */ 129965668Skris break; 130065668Skris } 130165668Skris } else { 130265668Skris /* 1303181111Sdes * The previous character was not an escape char. 1304181111Sdes * Check if this is an escape. 130565668Skris */ 130665668Skris if (last_was_cr && ch == escape_char) { 1307181111Sdes /* 1308181111Sdes * It is. Set the flag and continue to 1309181111Sdes * next character. 1310181111Sdes */ 1311181111Sdes *escape_pendingp = 1; 131265668Skris continue; 131365668Skris } 131465668Skris } 131565668Skris 131665668Skris /* 131765668Skris * Normal character. Record whether it was a newline, 131865668Skris * and append it to the buffer. 131965668Skris */ 132065668Skris last_was_cr = (ch == '\r' || ch == '\n'); 132165668Skris buffer_put_char(bin, ch); 132265668Skris bytes++; 132365668Skris } 132465668Skris return bytes; 132565668Skris} 132665668Skris 132792555Sdesstatic void 1328162852Sdesclient_process_input(fd_set *readset) 132960573Skris{ 133060573Skris int len; 1331197679Sdes char buf[SSH_IOBUFSZ]; 133260573Skris 133357429Smarkm /* Read input from stdin. */ 133457429Smarkm if (FD_ISSET(fileno(stdin), readset)) { 133557429Smarkm /* Read as much as possible. */ 133657429Smarkm len = read(fileno(stdin), buf, sizeof(buf)); 1337181111Sdes if (len < 0 && 1338181111Sdes (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)) 133976259Sgreen return; /* we'll try again later */ 134057429Smarkm if (len <= 0) { 134157429Smarkm /* 134257429Smarkm * Received EOF or error. They are treated 134357429Smarkm * similarly, except that an error message is printed 134457429Smarkm * if it was an error condition. 134557429Smarkm */ 134657429Smarkm if (len < 0) { 1347181111Sdes snprintf(buf, sizeof buf, "read: %.100s\r\n", 1348181111Sdes strerror(errno)); 134957429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 135057429Smarkm } 135157429Smarkm /* Mark that we have seen EOF. */ 135257429Smarkm stdin_eof = 1; 135357429Smarkm /* 135457429Smarkm * Send an EOF message to the server unless there is 135557429Smarkm * data in the buffer. If there is data in the 135657429Smarkm * buffer, no message will be sent now. Code 135757429Smarkm * elsewhere will send the EOF when the buffer 135857429Smarkm * becomes empty if stdin_eof is set. 135957429Smarkm */ 136057429Smarkm if (buffer_len(&stdin_buffer) == 0) { 136157429Smarkm packet_start(SSH_CMSG_EOF); 136257429Smarkm packet_send(); 136357429Smarkm } 1364181111Sdes } else if (escape_char1 == SSH_ESCAPECHAR_NONE) { 136557429Smarkm /* 136657429Smarkm * Normal successful read, and no escape character. 136757429Smarkm * Just append the data to buffer. 136857429Smarkm */ 136957429Smarkm buffer_append(&stdin_buffer, buf, len); 137057429Smarkm } else { 137157429Smarkm /* 1372181111Sdes * Normal, successful read. But we have an escape 1373181111Sdes * character and have to process the characters one 1374181111Sdes * by one. 137557429Smarkm */ 1376181111Sdes if (process_escapes(NULL, &stdin_buffer, 1377181111Sdes &stdout_buffer, &stderr_buffer, buf, len) == -1) 137865668Skris return; 137957429Smarkm } 138057429Smarkm } 138157429Smarkm} 138257429Smarkm 138392555Sdesstatic void 1384162852Sdesclient_process_output(fd_set *writeset) 138557429Smarkm{ 138657429Smarkm int len; 138757429Smarkm char buf[100]; 138857429Smarkm 138957429Smarkm /* Write buffered output to stdout. */ 139057429Smarkm if (FD_ISSET(fileno(stdout), writeset)) { 139157429Smarkm /* Write as much data as possible. */ 139257429Smarkm len = write(fileno(stdout), buffer_ptr(&stdout_buffer), 139357429Smarkm buffer_len(&stdout_buffer)); 139457429Smarkm if (len <= 0) { 1395181111Sdes if (errno == EINTR || errno == EAGAIN || 1396181111Sdes errno == EWOULDBLOCK) 139757429Smarkm len = 0; 139857429Smarkm else { 139957429Smarkm /* 140057429Smarkm * An error or EOF was encountered. Put an 140157429Smarkm * error message to stderr buffer. 140257429Smarkm */ 1403181111Sdes snprintf(buf, sizeof buf, 1404181111Sdes "write stdout: %.50s\r\n", strerror(errno)); 140557429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 140657429Smarkm quit_pending = 1; 140757429Smarkm return; 140857429Smarkm } 140957429Smarkm } 141057429Smarkm /* Consume printed data from the buffer. */ 141157429Smarkm buffer_consume(&stdout_buffer, len); 141257429Smarkm } 141357429Smarkm /* Write buffered output to stderr. */ 141457429Smarkm if (FD_ISSET(fileno(stderr), writeset)) { 141557429Smarkm /* Write as much data as possible. */ 141657429Smarkm len = write(fileno(stderr), buffer_ptr(&stderr_buffer), 141757429Smarkm buffer_len(&stderr_buffer)); 141857429Smarkm if (len <= 0) { 1419181111Sdes if (errno == EINTR || errno == EAGAIN || 1420181111Sdes errno == EWOULDBLOCK) 142157429Smarkm len = 0; 142257429Smarkm else { 1423181111Sdes /* 1424181111Sdes * EOF or error, but can't even print 1425181111Sdes * error message. 1426181111Sdes */ 142757429Smarkm quit_pending = 1; 142857429Smarkm return; 142957429Smarkm } 143057429Smarkm } 143157429Smarkm /* Consume printed characters from the buffer. */ 143257429Smarkm buffer_consume(&stderr_buffer, len); 143357429Smarkm } 143457429Smarkm} 143557429Smarkm 143657429Smarkm/* 143760573Skris * Get packets from the connection input buffer, and process them as long as 143860573Skris * there are packets available. 143960573Skris * 144060573Skris * Any unknown packets received during the actual 144160573Skris * session cause the session to terminate. This is 144260573Skris * intended to make debugging easier since no 144360573Skris * confirmations are sent. Any compatible protocol 144460573Skris * extensions must be negotiated during the 144560573Skris * preparatory phase. 144660573Skris */ 144760573Skris 144892555Sdesstatic void 144976259Sgreenclient_process_buffered_input_packets(void) 145060573Skris{ 1451295367Sdes dispatch_run(DISPATCH_NONBLOCK, &quit_pending, active_state); 145260573Skris} 145360573Skris 145465668Skris/* scan buf[] for '~' before sending data to the peer */ 145565668Skris 1456181111Sdes/* Helper: allocate a new escape_filter_ctx and fill in its escape char */ 1457181111Sdesvoid * 1458181111Sdesclient_new_escape_filter_ctx(int escape_char) 145965668Skris{ 1460181111Sdes struct escape_filter_ctx *ret; 1461181111Sdes 1462258343Sdes ret = xcalloc(1, sizeof(*ret)); 1463181111Sdes ret->escape_pending = 0; 1464181111Sdes ret->escape_char = escape_char; 1465181111Sdes return (void *)ret; 146665668Skris} 146765668Skris 1468181111Sdes/* Free the escape filter context on channel free */ 1469181111Sdesvoid 1470181111Sdesclient_filter_cleanup(int cid, void *ctx) 1471181111Sdes{ 1472255767Sdes free(ctx); 1473181111Sdes} 1474181111Sdes 1475181111Sdesint 1476181111Sdesclient_simple_escape_filter(Channel *c, char *buf, int len) 1477181111Sdes{ 1478181111Sdes if (c->extended_usage != CHAN_EXTENDED_WRITE) 1479181111Sdes return 0; 1480181111Sdes 1481181111Sdes return process_escapes(c, &c->input, &c->output, &c->extended, 1482181111Sdes buf, len); 1483181111Sdes} 1484181111Sdes 148592555Sdesstatic void 148676259Sgreenclient_channel_closed(int id, void *arg) 148776259Sgreen{ 148892555Sdes channel_cancel_cleanup(id); 148976259Sgreen session_closed = 1; 1490226046Sdes leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 149176259Sgreen} 149276259Sgreen 149360573Skris/* 149457429Smarkm * Implements the interactive session with the server. This is called after 149557429Smarkm * the user has been authenticated, and a command has been started on the 149692555Sdes * remote host. If escape_char != SSH_ESCAPECHAR_NONE, it is the character 149792555Sdes * used as an escape character for terminating or suspending the session. 149857429Smarkm */ 149957429Smarkm 150060573Skrisint 150165668Skrisclient_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) 150257429Smarkm{ 150376259Sgreen fd_set *readset = NULL, *writeset = NULL; 150457429Smarkm double start_time, total_time; 1505296853Sdes int r, max_fd = 0, max_fd2 = 0, len; 1506181111Sdes u_int64_t ibytes, obytes; 1507137015Sdes u_int nalloc = 0; 150857429Smarkm char buf[100]; 150957429Smarkm 151057429Smarkm debug("Entering interactive session."); 151157429Smarkm 1512296853Sdes if (options.control_master && 1513296853Sdes ! option_clear_or_none(options.control_path)) { 1514296853Sdes debug("pledge: id"); 1515296853Sdes if (pledge("stdio rpath wpath cpath unix inet dns proc exec id tty", 1516296853Sdes NULL) == -1) 1517296853Sdes fatal("%s pledge(): %s", __func__, strerror(errno)); 1518296853Sdes 1519296853Sdes } else if (options.forward_x11 || options.permit_local_command) { 1520296853Sdes debug("pledge: exec"); 1521296853Sdes if (pledge("stdio rpath wpath cpath unix inet dns proc exec tty", 1522296853Sdes NULL) == -1) 1523296853Sdes fatal("%s pledge(): %s", __func__, strerror(errno)); 1524296853Sdes 1525296853Sdes } else if (options.update_hostkeys) { 1526296853Sdes debug("pledge: filesystem full"); 1527296853Sdes if (pledge("stdio rpath wpath cpath unix inet dns proc tty", 1528296853Sdes NULL) == -1) 1529296853Sdes fatal("%s pledge(): %s", __func__, strerror(errno)); 1530296853Sdes 1531296853Sdes } else if (! option_clear_or_none(options.proxy_command)) { 1532296853Sdes debug("pledge: proc"); 1533296853Sdes if (pledge("stdio cpath unix inet dns proc tty", NULL) == -1) 1534296853Sdes fatal("%s pledge(): %s", __func__, strerror(errno)); 1535296853Sdes 1536296853Sdes } else { 1537296853Sdes debug("pledge: network"); 1538296853Sdes if (pledge("stdio unix inet dns tty", NULL) == -1) 1539296853Sdes fatal("%s pledge(): %s", __func__, strerror(errno)); 1540296853Sdes } 1541296853Sdes 154257429Smarkm start_time = get_current_time(); 154357429Smarkm 154457429Smarkm /* Initialize variables. */ 1545181111Sdes escape_pending1 = 0; 154657429Smarkm last_was_cr = 1; 154757429Smarkm exit_status = -1; 154857429Smarkm stdin_eof = 0; 154957429Smarkm buffer_high = 64 * 1024; 155057429Smarkm connection_in = packet_get_connection_in(); 155157429Smarkm connection_out = packet_get_connection_out(); 155276259Sgreen max_fd = MAX(connection_in, connection_out); 155376259Sgreen 155476259Sgreen if (!compat20) { 155576259Sgreen /* enable nonblocking unless tty */ 155676259Sgreen if (!isatty(fileno(stdin))) 155776259Sgreen set_nonblock(fileno(stdin)); 155876259Sgreen if (!isatty(fileno(stdout))) 155976259Sgreen set_nonblock(fileno(stdout)); 156076259Sgreen if (!isatty(fileno(stderr))) 156176259Sgreen set_nonblock(fileno(stderr)); 156276259Sgreen max_fd = MAX(max_fd, fileno(stdin)); 156376259Sgreen max_fd = MAX(max_fd, fileno(stdout)); 156476259Sgreen max_fd = MAX(max_fd, fileno(stderr)); 156576259Sgreen } 156657429Smarkm quit_pending = 0; 1567181111Sdes escape_char1 = escape_char_arg; 156857429Smarkm 156957429Smarkm /* Initialize buffers. */ 157057429Smarkm buffer_init(&stdin_buffer); 157157429Smarkm buffer_init(&stdout_buffer); 157257429Smarkm buffer_init(&stderr_buffer); 157357429Smarkm 157460573Skris client_init_dispatch(); 157560573Skris 1576113908Sdes /* 1577113908Sdes * Set signal handlers, (e.g. to restore non-blocking mode) 1578113908Sdes * but don't overwrite SIG_IGN, matches behaviour from rsh(1) 1579113908Sdes */ 1580146998Sdes if (signal(SIGHUP, SIG_IGN) != SIG_IGN) 1581146998Sdes signal(SIGHUP, signal_handler); 1582113908Sdes if (signal(SIGINT, SIG_IGN) != SIG_IGN) 1583113908Sdes signal(SIGINT, signal_handler); 1584113908Sdes if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) 1585113908Sdes signal(SIGQUIT, signal_handler); 1586113908Sdes if (signal(SIGTERM, SIG_IGN) != SIG_IGN) 1587113908Sdes signal(SIGTERM, signal_handler); 1588146998Sdes signal(SIGWINCH, window_change_handler); 158957429Smarkm 159057429Smarkm if (have_pty) 1591226046Sdes enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 159257429Smarkm 159376259Sgreen if (compat20) { 159476259Sgreen session_ident = ssh2_chan_id; 1595226046Sdes if (session_ident != -1) { 1596226046Sdes if (escape_char_arg != SSH_ESCAPECHAR_NONE) { 1597226046Sdes channel_register_filter(session_ident, 1598226046Sdes client_simple_escape_filter, NULL, 1599226046Sdes client_filter_cleanup, 1600226046Sdes client_new_escape_filter_ctx( 1601226046Sdes escape_char_arg)); 1602226046Sdes } 160376259Sgreen channel_register_cleanup(session_ident, 1604157016Sdes client_channel_closed, 0); 1605226046Sdes } 160676259Sgreen } else { 160776259Sgreen /* Check if we should immediately send eof on stdin. */ 160860573Skris client_check_initial_eof_on_stdin(); 160976259Sgreen } 161057429Smarkm 161157429Smarkm /* Main loop of the client for the interactive session mode. */ 161257429Smarkm while (!quit_pending) { 161357429Smarkm 161457429Smarkm /* Process buffered packets sent by the server. */ 161557429Smarkm client_process_buffered_input_packets(); 161657429Smarkm 161776259Sgreen if (compat20 && session_closed && !channel_still_open()) 161860573Skris break; 161960573Skris 1620296853Sdes if (ssh_packet_is_rekeying(active_state)) { 162176259Sgreen debug("rekeying in progress"); 1622296853Sdes } else if (need_rekeying) { 1623296853Sdes /* manual rekey request */ 1624296853Sdes debug("need rekeying"); 1625296853Sdes if ((r = kex_start_rekex(active_state)) != 0) 1626296853Sdes fatal("%s: kex_start_rekex: %s", __func__, 1627296853Sdes ssh_err(r)); 1628296853Sdes need_rekeying = 0; 162976259Sgreen } else { 163076259Sgreen /* 163176259Sgreen * Make packets of buffered stdin data, and buffer 163276259Sgreen * them for sending to the server. 163376259Sgreen */ 163476259Sgreen if (!compat20) 163576259Sgreen client_make_packets_from_stdin_data(); 163657429Smarkm 163776259Sgreen /* 163876259Sgreen * Make packets from buffered channel data, and 163976259Sgreen * enqueue them for sending to the server. 164076259Sgreen */ 164176259Sgreen if (packet_not_very_much_data_to_write()) 164276259Sgreen channel_output_poll(); 164357429Smarkm 164476259Sgreen /* 164576259Sgreen * Check if the window size has changed, and buffer a 164676259Sgreen * message about it to the server if so. 164776259Sgreen */ 164876259Sgreen client_check_window_change(); 164957429Smarkm 165076259Sgreen if (quit_pending) 165176259Sgreen break; 165276259Sgreen } 165357429Smarkm /* 165457429Smarkm * Wait until we have something to do (something becomes 165557429Smarkm * available on one of the descriptors). 165657429Smarkm */ 165792555Sdes max_fd2 = max_fd; 165876259Sgreen client_wait_until_can_do_something(&readset, &writeset, 1659296853Sdes &max_fd2, &nalloc, ssh_packet_is_rekeying(active_state)); 166057429Smarkm 166157429Smarkm if (quit_pending) 166257429Smarkm break; 166357429Smarkm 166476259Sgreen /* Do channel operations unless rekeying in progress. */ 1665296853Sdes if (!ssh_packet_is_rekeying(active_state)) 166676259Sgreen channel_after_select(readset, writeset); 166776259Sgreen 166860573Skris /* Buffer input from the connection. */ 166976259Sgreen client_process_net_input(readset); 167057429Smarkm 167160573Skris if (quit_pending) 167260573Skris break; 167357429Smarkm 167460573Skris if (!compat20) { 167560573Skris /* Buffer data from stdin */ 167676259Sgreen client_process_input(readset); 167760573Skris /* 167860573Skris * Process output to stdout and stderr. Output to 167960573Skris * the connection is processed elsewhere (above). 168060573Skris */ 168176259Sgreen client_process_output(writeset); 168260573Skris } 168360573Skris 1684181111Sdes /* 1685181111Sdes * Send as much buffered packet data as possible to the 1686181111Sdes * sender. 1687181111Sdes */ 168876259Sgreen if (FD_ISSET(connection_out, writeset)) 168957429Smarkm packet_write_poll(); 1690215116Sdes 1691215116Sdes /* 1692215116Sdes * If we are a backgrounded control master, and the 1693215116Sdes * timeout has expired without any active client 1694215116Sdes * connections, then quit. 1695215116Sdes */ 1696215116Sdes if (control_persist_exit_time > 0) { 1697255767Sdes if (monotime() >= control_persist_exit_time) { 1698215116Sdes debug("ControlPersist timeout expired"); 1699215116Sdes break; 1700215116Sdes } 1701215116Sdes } 170257429Smarkm } 1703255767Sdes free(readset); 1704255767Sdes free(writeset); 170557429Smarkm 170657429Smarkm /* Terminate the session. */ 170757429Smarkm 170857429Smarkm /* Stop watching for window change. */ 1709146998Sdes signal(SIGWINCH, SIG_DFL); 171057429Smarkm 1711197679Sdes if (compat20) { 1712197679Sdes packet_start(SSH2_MSG_DISCONNECT); 1713197679Sdes packet_put_int(SSH2_DISCONNECT_BY_APPLICATION); 1714197679Sdes packet_put_cstring("disconnected by user"); 1715207319Sdes packet_put_cstring(""); /* language tag */ 1716197679Sdes packet_send(); 1717197679Sdes packet_write_wait(); 1718197679Sdes } 1719197679Sdes 172092555Sdes channel_free_all(); 172157429Smarkm 172292555Sdes if (have_pty) 1723226046Sdes leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 172492555Sdes 172592555Sdes /* restore blocking io */ 172692555Sdes if (!isatty(fileno(stdin))) 172792555Sdes unset_nonblock(fileno(stdin)); 172892555Sdes if (!isatty(fileno(stdout))) 172992555Sdes unset_nonblock(fileno(stdout)); 173092555Sdes if (!isatty(fileno(stderr))) 173192555Sdes unset_nonblock(fileno(stderr)); 173292555Sdes 1733126274Sdes /* 1734126274Sdes * If there was no shell or command requested, there will be no remote 1735126274Sdes * exit status to be returned. In that case, clear error code if the 1736126274Sdes * connection was deliberately terminated at this end. 1737126274Sdes */ 1738126274Sdes if (no_shell_flag && received_signal == SIGTERM) { 1739126274Sdes received_signal = 0; 1740126274Sdes exit_status = 0; 174192555Sdes } 174292555Sdes 1743126274Sdes if (received_signal) 1744126274Sdes fatal("Killed by signal %d.", (int) received_signal); 1745126274Sdes 174657429Smarkm /* 174757429Smarkm * In interactive mode (with pseudo tty) display a message indicating 174857429Smarkm * that the connection has been closed. 174957429Smarkm */ 175057429Smarkm if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) { 1751181111Sdes snprintf(buf, sizeof buf, 1752181111Sdes "Connection to %.64s closed.\r\n", host); 175357429Smarkm buffer_append(&stderr_buffer, buf, strlen(buf)); 175457429Smarkm } 175592555Sdes 175657429Smarkm /* Output any buffered data for stdout. */ 1757221420Sdes if (buffer_len(&stdout_buffer) > 0) { 1758221420Sdes len = atomicio(vwrite, fileno(stdout), 1759221420Sdes buffer_ptr(&stdout_buffer), buffer_len(&stdout_buffer)); 1760221420Sdes if (len < 0 || (u_int)len != buffer_len(&stdout_buffer)) 176157429Smarkm error("Write failed flushing stdout buffer."); 1762221420Sdes else 1763221420Sdes buffer_consume(&stdout_buffer, len); 176457429Smarkm } 176557429Smarkm 176657429Smarkm /* Output any buffered data for stderr. */ 1767221420Sdes if (buffer_len(&stderr_buffer) > 0) { 1768221420Sdes len = atomicio(vwrite, fileno(stderr), 1769221420Sdes buffer_ptr(&stderr_buffer), buffer_len(&stderr_buffer)); 1770221420Sdes if (len < 0 || (u_int)len != buffer_len(&stderr_buffer)) 177157429Smarkm error("Write failed flushing stderr buffer."); 1772221420Sdes else 1773221420Sdes buffer_consume(&stderr_buffer, len); 177457429Smarkm } 177557429Smarkm 177657429Smarkm /* Clear and free any buffers. */ 1777296853Sdes explicit_bzero(buf, sizeof(buf)); 177857429Smarkm buffer_free(&stdin_buffer); 177957429Smarkm buffer_free(&stdout_buffer); 178057429Smarkm buffer_free(&stderr_buffer); 178157429Smarkm 178257429Smarkm /* Report bytes transferred, and transfer rates. */ 178357429Smarkm total_time = get_current_time() - start_time; 1784295367Sdes packet_get_bytes(&ibytes, &obytes); 1785181111Sdes verbose("Transferred: sent %llu, received %llu bytes, in %.1f seconds", 1786221420Sdes (unsigned long long)obytes, (unsigned long long)ibytes, total_time); 178757429Smarkm if (total_time > 0) 1788181111Sdes verbose("Bytes per second: sent %.1f, received %.1f", 1789181111Sdes obytes / total_time, ibytes / total_time); 179057429Smarkm /* Return the exit status of the program. */ 179157429Smarkm debug("Exit status %d", exit_status); 179257429Smarkm return exit_status; 179357429Smarkm} 179460573Skris 179560573Skris/*********/ 179660573Skris 1797295367Sdesstatic int 179892555Sdesclient_input_stdout_data(int type, u_int32_t seq, void *ctxt) 179960573Skris{ 180076259Sgreen u_int data_len; 180160573Skris char *data = packet_get_string(&data_len); 180292555Sdes packet_check_eom(); 180360573Skris buffer_append(&stdout_buffer, data, data_len); 1804264377Sdes explicit_bzero(data, data_len); 1805255767Sdes free(data); 1806295367Sdes return 0; 180760573Skris} 1808295367Sdesstatic int 180992555Sdesclient_input_stderr_data(int type, u_int32_t seq, void *ctxt) 181060573Skris{ 181176259Sgreen u_int data_len; 181260573Skris char *data = packet_get_string(&data_len); 181392555Sdes packet_check_eom(); 181460573Skris buffer_append(&stderr_buffer, data, data_len); 1815264377Sdes explicit_bzero(data, data_len); 1816255767Sdes free(data); 1817295367Sdes return 0; 181860573Skris} 1819295367Sdesstatic int 182092555Sdesclient_input_exit_status(int type, u_int32_t seq, void *ctxt) 182160573Skris{ 182260573Skris exit_status = packet_get_int(); 182392555Sdes packet_check_eom(); 182460573Skris /* Acknowledge the exit. */ 182560573Skris packet_start(SSH_CMSG_EXIT_CONFIRMATION); 182660573Skris packet_send(); 182760573Skris /* 182860573Skris * Must wait for packet to be sent since we are 182960573Skris * exiting the loop. 183060573Skris */ 183160573Skris packet_write_wait(); 183260573Skris /* Flag that we want to exit. */ 183360573Skris quit_pending = 1; 1834295367Sdes return 0; 183560573Skris} 1836295367Sdes 1837295367Sdesstatic int 1838126274Sdesclient_input_agent_open(int type, u_int32_t seq, void *ctxt) 1839126274Sdes{ 1840126274Sdes Channel *c = NULL; 1841295367Sdes int r, remote_id, sock; 184260573Skris 1843126274Sdes /* Read the remote channel number from the message. */ 1844126274Sdes remote_id = packet_get_int(); 1845126274Sdes packet_check_eom(); 1846126274Sdes 1847126274Sdes /* 1848126274Sdes * Get a connection to the local authentication agent (this may again 1849126274Sdes * get forwarded). 1850126274Sdes */ 1851295367Sdes if ((r = ssh_get_authentication_socket(&sock)) != 0 && 1852295367Sdes r != SSH_ERR_AGENT_NOT_PRESENT) 1853295367Sdes debug("%s: ssh_get_authentication_socket: %s", 1854295367Sdes __func__, ssh_err(r)); 1855126274Sdes 1856295367Sdes 1857126274Sdes /* 1858126274Sdes * If we could not connect the agent, send an error message back to 1859126274Sdes * the server. This should never happen unless the agent dies, 1860126274Sdes * because authentication forwarding is only enabled if we have an 1861126274Sdes * agent. 1862126274Sdes */ 1863126274Sdes if (sock >= 0) { 1864126274Sdes c = channel_new("", SSH_CHANNEL_OPEN, sock, sock, 1865126274Sdes -1, 0, 0, 0, "authentication agent connection", 1); 1866126274Sdes c->remote_id = remote_id; 1867126274Sdes c->force_drain = 1; 1868126274Sdes } 1869126274Sdes if (c == NULL) { 1870126274Sdes packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); 1871126274Sdes packet_put_int(remote_id); 1872126274Sdes } else { 1873126274Sdes /* Send a confirmation to the remote host. */ 1874126274Sdes debug("Forwarding authentication connection."); 1875126274Sdes packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); 1876126274Sdes packet_put_int(remote_id); 1877126274Sdes packet_put_int(c->self); 1878126274Sdes } 1879126274Sdes packet_send(); 1880295367Sdes return 0; 1881126274Sdes} 1882126274Sdes 188392555Sdesstatic Channel * 188476259Sgreenclient_request_forwarded_tcpip(const char *request_type, int rchan) 188576259Sgreen{ 1886106121Sdes Channel *c = NULL; 188776259Sgreen char *listen_address, *originator_address; 1888192595Sdes u_short listen_port, originator_port; 188976259Sgreen 189076259Sgreen /* Get rest of the packet */ 189176259Sgreen listen_address = packet_get_string(NULL); 189276259Sgreen listen_port = packet_get_int(); 189376259Sgreen originator_address = packet_get_string(NULL); 189476259Sgreen originator_port = packet_get_int(); 189592555Sdes packet_check_eom(); 189676259Sgreen 1897295367Sdes debug("%s: listen %s port %d, originator %s port %d", __func__, 1898295367Sdes listen_address, listen_port, originator_address, originator_port); 189976259Sgreen 1900295367Sdes c = channel_connect_by_listen_address(listen_address, listen_port, 1901181111Sdes "forwarded-tcpip", originator_address); 1902181111Sdes 1903255767Sdes free(originator_address); 1904255767Sdes free(listen_address); 190576259Sgreen return c; 190676259Sgreen} 190776259Sgreen 1908106121Sdesstatic Channel * 1909295367Sdesclient_request_forwarded_streamlocal(const char *request_type, int rchan) 1910295367Sdes{ 1911295367Sdes Channel *c = NULL; 1912295367Sdes char *listen_path; 1913295367Sdes 1914295367Sdes /* Get the remote path. */ 1915295367Sdes listen_path = packet_get_string(NULL); 1916295367Sdes /* XXX: Skip reserved field for now. */ 1917295367Sdes if (packet_get_string_ptr(NULL) == NULL) 1918295367Sdes fatal("%s: packet_get_string_ptr failed", __func__); 1919295367Sdes packet_check_eom(); 1920295367Sdes 1921295367Sdes debug("%s: %s", __func__, listen_path); 1922295367Sdes 1923295367Sdes c = channel_connect_by_listen_path(listen_path, 1924295367Sdes "forwarded-streamlocal@openssh.com", "forwarded-streamlocal"); 1925295367Sdes free(listen_path); 1926295367Sdes return c; 1927295367Sdes} 1928295367Sdes 1929295367Sdesstatic Channel * 193076259Sgreenclient_request_x11(const char *request_type, int rchan) 193176259Sgreen{ 193276259Sgreen Channel *c = NULL; 193376259Sgreen char *originator; 1934192595Sdes u_short originator_port; 193592555Sdes int sock; 193676259Sgreen 193776259Sgreen if (!options.forward_x11) { 193876259Sgreen error("Warning: ssh server tried X11 forwarding."); 1939181111Sdes error("Warning: this is probably a break-in attempt by a " 1940181111Sdes "malicious server."); 194176259Sgreen return NULL; 194276259Sgreen } 1943295367Sdes if (x11_refuse_time != 0 && (u_int)monotime() >= x11_refuse_time) { 1944215116Sdes verbose("Rejected X11 connection after ForwardX11Timeout " 1945215116Sdes "expired"); 1946215116Sdes return NULL; 1947215116Sdes } 194876259Sgreen originator = packet_get_string(NULL); 194976259Sgreen if (datafellows & SSH_BUG_X11FWD) { 195076259Sgreen debug2("buggy server: x11 request w/o originator_port"); 195176259Sgreen originator_port = 0; 195276259Sgreen } else { 195376259Sgreen originator_port = packet_get_int(); 195476259Sgreen } 195592555Sdes packet_check_eom(); 195676259Sgreen /* XXX check permission */ 195776259Sgreen debug("client_request_x11: request from %s %d", originator, 195876259Sgreen originator_port); 1959255767Sdes free(originator); 196076259Sgreen sock = x11_connect_display(); 196192555Sdes if (sock < 0) 196292555Sdes return NULL; 1963294693Sdes c = channel_new("x11", 1964294693Sdes SSH_CHANNEL_X11_OPEN, sock, sock, -1, 1965294693Sdes CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "x11", 1); 196692555Sdes c->force_drain = 1; 196776259Sgreen return c; 196876259Sgreen} 196976259Sgreen 1970106121Sdesstatic Channel * 197176259Sgreenclient_request_agent(const char *request_type, int rchan) 197276259Sgreen{ 197376259Sgreen Channel *c = NULL; 1974295367Sdes int r, sock; 197576259Sgreen 197676259Sgreen if (!options.forward_agent) { 197776259Sgreen error("Warning: ssh server tried agent forwarding."); 1978181111Sdes error("Warning: this is probably a break-in attempt by a " 1979181111Sdes "malicious server."); 198076259Sgreen return NULL; 198176259Sgreen } 1982295367Sdes if ((r = ssh_get_authentication_socket(&sock)) != 0) { 1983295367Sdes if (r != SSH_ERR_AGENT_NOT_PRESENT) 1984295367Sdes debug("%s: ssh_get_authentication_socket: %s", 1985295367Sdes __func__, ssh_err(r)); 198692555Sdes return NULL; 1987295367Sdes } 1988294693Sdes c = channel_new("authentication agent connection", 1989294693Sdes SSH_CHANNEL_OPEN, sock, sock, -1, 1990294693Sdes CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, 1991294693Sdes "authentication agent connection", 1); 199292555Sdes c->force_drain = 1; 199376259Sgreen return c; 199476259Sgreen} 199576259Sgreen 1996181111Sdesint 1997181111Sdesclient_request_tun_fwd(int tun_mode, int local_tun, int remote_tun) 1998181111Sdes{ 1999181111Sdes Channel *c; 2000181111Sdes int fd; 2001181111Sdes 2002181111Sdes if (tun_mode == SSH_TUNMODE_NO) 2003181111Sdes return 0; 2004181111Sdes 2005181111Sdes if (!compat20) { 2006192595Sdes error("Tunnel forwarding is not supported for protocol 1"); 2007181111Sdes return -1; 2008181111Sdes } 2009181111Sdes 2010181111Sdes debug("Requesting tun unit %d in mode %d", local_tun, tun_mode); 2011181111Sdes 2012181111Sdes /* Open local tunnel device */ 2013181111Sdes if ((fd = tun_open(local_tun, tun_mode)) == -1) { 2014181111Sdes error("Tunnel device open failed."); 2015181111Sdes return -1; 2016181111Sdes } 2017181111Sdes 2018294693Sdes c = channel_new("tun", SSH_CHANNEL_OPENING, fd, fd, -1, 2019294693Sdes CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1); 2020181111Sdes c->datagram = 1; 2021181111Sdes 2022181111Sdes#if defined(SSH_TUN_FILTER) 2023181111Sdes if (options.tun_open == SSH_TUNMODE_POINTOPOINT) 2024181111Sdes channel_register_filter(c->self, sys_tun_infilter, 2025181111Sdes sys_tun_outfilter, NULL, NULL); 2026181111Sdes#endif 2027181111Sdes 2028181111Sdes packet_start(SSH2_MSG_CHANNEL_OPEN); 2029181111Sdes packet_put_cstring("tun@openssh.com"); 2030181111Sdes packet_put_int(c->self); 2031181111Sdes packet_put_int(c->local_window_max); 2032181111Sdes packet_put_int(c->local_maxpacket); 2033181111Sdes packet_put_int(tun_mode); 2034181111Sdes packet_put_int(remote_tun); 2035181111Sdes packet_send(); 2036181111Sdes 2037181111Sdes return 0; 2038181111Sdes} 2039181111Sdes 204060573Skris/* XXXX move to generic input handler */ 2041295367Sdesstatic int 204292555Sdesclient_input_channel_open(int type, u_int32_t seq, void *ctxt) 204360573Skris{ 204460573Skris Channel *c = NULL; 204560573Skris char *ctype; 204660573Skris int rchan; 204799060Sdes u_int rmaxpack, rwindow, len; 204860573Skris 204960573Skris ctype = packet_get_string(&len); 205060573Skris rchan = packet_get_int(); 205160573Skris rwindow = packet_get_int(); 205260573Skris rmaxpack = packet_get_int(); 205360573Skris 205460573Skris debug("client_input_channel_open: ctype %s rchan %d win %d max %d", 205560573Skris ctype, rchan, rwindow, rmaxpack); 205660573Skris 205776259Sgreen if (strcmp(ctype, "forwarded-tcpip") == 0) { 205876259Sgreen c = client_request_forwarded_tcpip(ctype, rchan); 2059295367Sdes } else if (strcmp(ctype, "forwarded-streamlocal@openssh.com") == 0) { 2060295367Sdes c = client_request_forwarded_streamlocal(ctype, rchan); 206176259Sgreen } else if (strcmp(ctype, "x11") == 0) { 206276259Sgreen c = client_request_x11(ctype, rchan); 206376259Sgreen } else if (strcmp(ctype, "auth-agent@openssh.com") == 0) { 206476259Sgreen c = client_request_agent(ctype, rchan); 206560573Skris } 206660573Skris/* XXX duplicate : */ 206760573Skris if (c != NULL) { 206860573Skris debug("confirm %s", ctype); 206960573Skris c->remote_id = rchan; 207060573Skris c->remote_window = rwindow; 207160573Skris c->remote_maxpacket = rmaxpack; 207292555Sdes if (c->type != SSH_CHANNEL_CONNECTING) { 207392555Sdes packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); 207492555Sdes packet_put_int(c->remote_id); 207592555Sdes packet_put_int(c->self); 207692555Sdes packet_put_int(c->local_window); 207792555Sdes packet_put_int(c->local_maxpacket); 207892555Sdes packet_send(); 207992555Sdes } 208060573Skris } else { 208160573Skris debug("failure %s", ctype); 208260573Skris packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); 208360573Skris packet_put_int(rchan); 208460573Skris packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED); 208592555Sdes if (!(datafellows & SSH_BUG_OPENFAILURE)) { 208692555Sdes packet_put_cstring("open failed"); 208792555Sdes packet_put_cstring(""); 208892555Sdes } 208960573Skris packet_send(); 209060573Skris } 2091255767Sdes free(ctype); 2092295367Sdes return 0; 209360573Skris} 2094295367Sdes 2095295367Sdesstatic int 209692555Sdesclient_input_channel_req(int type, u_int32_t seq, void *ctxt) 209776259Sgreen{ 209876259Sgreen Channel *c = NULL; 2099137015Sdes int exitval, id, reply, success = 0; 210076259Sgreen char *rtype; 210160573Skris 210276259Sgreen id = packet_get_int(); 210376259Sgreen rtype = packet_get_string(NULL); 210476259Sgreen reply = packet_get_char(); 210576259Sgreen 210676259Sgreen debug("client_input_channel_req: channel %d rtype %s reply %d", 210776259Sgreen id, rtype, reply); 210876259Sgreen 2109137015Sdes if (id == -1) { 2110137015Sdes error("client_input_channel_req: request for channel -1"); 2111137015Sdes } else if ((c = channel_lookup(id)) == NULL) { 2112181111Sdes error("client_input_channel_req: channel %d: " 2113181111Sdes "unknown channel", id); 2114181111Sdes } else if (strcmp(rtype, "eow@openssh.com") == 0) { 2115181111Sdes packet_check_eom(); 2116181111Sdes chan_rcvd_eow(c); 211776259Sgreen } else if (strcmp(rtype, "exit-status") == 0) { 2118137015Sdes exitval = packet_get_int(); 2119204917Sdes if (c->ctl_chan != -1) { 2120204917Sdes mux_exit_message(c, exitval); 2121137015Sdes success = 1; 2122204917Sdes } else if (id == session_ident) { 2123204917Sdes /* Record exit value of local session */ 2124204917Sdes success = 1; 2125137015Sdes exit_status = exitval; 2126137015Sdes } else { 2127204917Sdes /* Probably for a mux channel that has already closed */ 2128204917Sdes debug("%s: no sink for exit-status on channel %d", 2129204917Sdes __func__, id); 2130137015Sdes } 213192555Sdes packet_check_eom(); 213276259Sgreen } 2133295367Sdes if (reply && c != NULL && !(c->flags & CHAN_CLOSE_SENT)) { 213476259Sgreen packet_start(success ? 213576259Sgreen SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); 2136192595Sdes packet_put_int(c->remote_id); 213776259Sgreen packet_send(); 213876259Sgreen } 2139255767Sdes free(rtype); 2140295367Sdes return 0; 214176259Sgreen} 2142295367Sdes 2143295367Sdesstruct hostkeys_update_ctx { 2144295367Sdes /* The hostname and (optionally) IP address string for the server */ 2145295367Sdes char *host_str, *ip_str; 2146295367Sdes 2147295367Sdes /* 2148295367Sdes * Keys received from the server and a flag for each indicating 2149295367Sdes * whether they already exist in known_hosts. 2150295367Sdes * keys_seen is filled in by hostkeys_find() and later (for new 2151295367Sdes * keys) by client_global_hostkeys_private_confirm(). 2152295367Sdes */ 2153295367Sdes struct sshkey **keys; 2154295367Sdes int *keys_seen; 2155295367Sdes size_t nkeys; 2156295367Sdes 2157295367Sdes size_t nnew; 2158295367Sdes 2159295367Sdes /* 2160295367Sdes * Keys that are in known_hosts, but were not present in the update 2161295367Sdes * from the server (i.e. scheduled to be deleted). 2162295367Sdes * Filled in by hostkeys_find(). 2163295367Sdes */ 2164295367Sdes struct sshkey **old_keys; 2165295367Sdes size_t nold; 2166295367Sdes}; 2167295367Sdes 216892555Sdesstatic void 2169295367Sdeshostkeys_update_ctx_free(struct hostkeys_update_ctx *ctx) 2170295367Sdes{ 2171295367Sdes size_t i; 2172295367Sdes 2173295367Sdes if (ctx == NULL) 2174295367Sdes return; 2175295367Sdes for (i = 0; i < ctx->nkeys; i++) 2176295367Sdes sshkey_free(ctx->keys[i]); 2177295367Sdes free(ctx->keys); 2178295367Sdes free(ctx->keys_seen); 2179295367Sdes for (i = 0; i < ctx->nold; i++) 2180295367Sdes sshkey_free(ctx->old_keys[i]); 2181295367Sdes free(ctx->old_keys); 2182295367Sdes free(ctx->host_str); 2183295367Sdes free(ctx->ip_str); 2184295367Sdes free(ctx); 2185295367Sdes} 2186295367Sdes 2187295367Sdesstatic int 2188295367Sdeshostkeys_find(struct hostkey_foreach_line *l, void *_ctx) 2189295367Sdes{ 2190295367Sdes struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; 2191295367Sdes size_t i; 2192295367Sdes struct sshkey **tmp; 2193295367Sdes 2194295367Sdes if (l->status != HKF_STATUS_MATCHED || l->key == NULL || 2195295367Sdes l->key->type == KEY_RSA1) 2196295367Sdes return 0; 2197295367Sdes 2198295367Sdes /* Mark off keys we've already seen for this host */ 2199295367Sdes for (i = 0; i < ctx->nkeys; i++) { 2200295367Sdes if (sshkey_equal(l->key, ctx->keys[i])) { 2201295367Sdes debug3("%s: found %s key at %s:%ld", __func__, 2202295367Sdes sshkey_ssh_name(ctx->keys[i]), l->path, l->linenum); 2203295367Sdes ctx->keys_seen[i] = 1; 2204295367Sdes return 0; 2205295367Sdes } 2206295367Sdes } 2207295367Sdes /* This line contained a key that not offered by the server */ 2208295367Sdes debug3("%s: deprecated %s key at %s:%ld", __func__, 2209295367Sdes sshkey_ssh_name(l->key), l->path, l->linenum); 2210295367Sdes if ((tmp = reallocarray(ctx->old_keys, ctx->nold + 1, 2211295367Sdes sizeof(*ctx->old_keys))) == NULL) 2212295367Sdes fatal("%s: reallocarray failed nold = %zu", 2213295367Sdes __func__, ctx->nold); 2214295367Sdes ctx->old_keys = tmp; 2215295367Sdes ctx->old_keys[ctx->nold++] = l->key; 2216295367Sdes l->key = NULL; 2217295367Sdes 2218295367Sdes return 0; 2219295367Sdes} 2220295367Sdes 2221295367Sdesstatic void 2222295367Sdesupdate_known_hosts(struct hostkeys_update_ctx *ctx) 2223295367Sdes{ 2224295367Sdes int r, was_raw = 0; 2225295367Sdes int loglevel = options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK ? 2226295367Sdes SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_VERBOSE; 2227295367Sdes char *fp, *response; 2228295367Sdes size_t i; 2229295367Sdes 2230295367Sdes for (i = 0; i < ctx->nkeys; i++) { 2231295367Sdes if (ctx->keys_seen[i] != 2) 2232295367Sdes continue; 2233295367Sdes if ((fp = sshkey_fingerprint(ctx->keys[i], 2234295367Sdes options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) 2235295367Sdes fatal("%s: sshkey_fingerprint failed", __func__); 2236295367Sdes do_log2(loglevel, "Learned new hostkey: %s %s", 2237295367Sdes sshkey_type(ctx->keys[i]), fp); 2238295367Sdes free(fp); 2239295367Sdes } 2240295367Sdes for (i = 0; i < ctx->nold; i++) { 2241295367Sdes if ((fp = sshkey_fingerprint(ctx->old_keys[i], 2242295367Sdes options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) 2243295367Sdes fatal("%s: sshkey_fingerprint failed", __func__); 2244295367Sdes do_log2(loglevel, "Deprecating obsolete hostkey: %s %s", 2245295367Sdes sshkey_type(ctx->old_keys[i]), fp); 2246295367Sdes free(fp); 2247295367Sdes } 2248295367Sdes if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK) { 2249295367Sdes if (get_saved_tio() != NULL) { 2250295367Sdes leave_raw_mode(1); 2251295367Sdes was_raw = 1; 2252295367Sdes } 2253295367Sdes response = NULL; 2254295367Sdes for (i = 0; !quit_pending && i < 3; i++) { 2255295367Sdes free(response); 2256295367Sdes response = read_passphrase("Accept updated hostkeys? " 2257295367Sdes "(yes/no): ", RP_ECHO); 2258295367Sdes if (strcasecmp(response, "yes") == 0) 2259295367Sdes break; 2260295367Sdes else if (quit_pending || response == NULL || 2261295367Sdes strcasecmp(response, "no") == 0) { 2262295367Sdes options.update_hostkeys = 0; 2263295367Sdes break; 2264295367Sdes } else { 2265295367Sdes do_log2(loglevel, "Please enter " 2266295367Sdes "\"yes\" or \"no\""); 2267295367Sdes } 2268295367Sdes } 2269295367Sdes if (quit_pending || i >= 3 || response == NULL) 2270295367Sdes options.update_hostkeys = 0; 2271295367Sdes free(response); 2272295367Sdes if (was_raw) 2273295367Sdes enter_raw_mode(1); 2274295367Sdes } 2275295367Sdes 2276295367Sdes /* 2277295367Sdes * Now that all the keys are verified, we can go ahead and replace 2278295367Sdes * them in known_hosts (assuming SSH_UPDATE_HOSTKEYS_ASK didn't 2279295367Sdes * cancel the operation). 2280295367Sdes */ 2281295367Sdes if (options.update_hostkeys != 0 && 2282295367Sdes (r = hostfile_replace_entries(options.user_hostfiles[0], 2283295367Sdes ctx->host_str, ctx->ip_str, ctx->keys, ctx->nkeys, 2284295367Sdes options.hash_known_hosts, 0, 2285295367Sdes options.fingerprint_hash)) != 0) 2286295367Sdes error("%s: hostfile_replace_entries failed: %s", 2287295367Sdes __func__, ssh_err(r)); 2288295367Sdes} 2289295367Sdes 2290295367Sdesstatic void 2291295367Sdesclient_global_hostkeys_private_confirm(int type, u_int32_t seq, void *_ctx) 2292295367Sdes{ 2293295367Sdes struct ssh *ssh = active_state; /* XXX */ 2294295367Sdes struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; 2295295367Sdes size_t i, ndone; 2296295367Sdes struct sshbuf *signdata; 2297295367Sdes int r; 2298295367Sdes const u_char *sig; 2299295367Sdes size_t siglen; 2300295367Sdes 2301295367Sdes if (ctx->nnew == 0) 2302295367Sdes fatal("%s: ctx->nnew == 0", __func__); /* sanity */ 2303295367Sdes if (type != SSH2_MSG_REQUEST_SUCCESS) { 2304295367Sdes error("Server failed to confirm ownership of " 2305295367Sdes "private host keys"); 2306295367Sdes hostkeys_update_ctx_free(ctx); 2307295367Sdes return; 2308295367Sdes } 2309295367Sdes if ((signdata = sshbuf_new()) == NULL) 2310295367Sdes fatal("%s: sshbuf_new failed", __func__); 2311295367Sdes /* Don't want to accidentally accept an unbound signature */ 2312295367Sdes if (ssh->kex->session_id_len == 0) 2313295367Sdes fatal("%s: ssh->kex->session_id_len == 0", __func__); 2314295367Sdes /* 2315295367Sdes * Expect a signature for each of the ctx->nnew private keys we 2316295367Sdes * haven't seen before. They will be in the same order as the 2317295367Sdes * ctx->keys where the corresponding ctx->keys_seen[i] == 0. 2318295367Sdes */ 2319295367Sdes for (ndone = i = 0; i < ctx->nkeys; i++) { 2320295367Sdes if (ctx->keys_seen[i]) 2321295367Sdes continue; 2322295367Sdes /* Prepare data to be signed: session ID, unique string, key */ 2323295367Sdes sshbuf_reset(signdata); 2324295367Sdes if ( (r = sshbuf_put_cstring(signdata, 2325295367Sdes "hostkeys-prove-00@openssh.com")) != 0 || 2326295367Sdes (r = sshbuf_put_string(signdata, ssh->kex->session_id, 2327295367Sdes ssh->kex->session_id_len)) != 0 || 2328295367Sdes (r = sshkey_puts(ctx->keys[i], signdata)) != 0) 2329295367Sdes fatal("%s: failed to prepare signature: %s", 2330295367Sdes __func__, ssh_err(r)); 2331295367Sdes /* Extract and verify signature */ 2332295367Sdes if ((r = sshpkt_get_string_direct(ssh, &sig, &siglen)) != 0) { 2333295367Sdes error("%s: couldn't parse message: %s", 2334295367Sdes __func__, ssh_err(r)); 2335295367Sdes goto out; 2336295367Sdes } 2337295367Sdes if ((r = sshkey_verify(ctx->keys[i], sig, siglen, 2338295367Sdes sshbuf_ptr(signdata), sshbuf_len(signdata), 0)) != 0) { 2339295367Sdes error("%s: server gave bad signature for %s key %zu", 2340295367Sdes __func__, sshkey_type(ctx->keys[i]), i); 2341295367Sdes goto out; 2342295367Sdes } 2343295367Sdes /* Key is good. Mark it as 'seen' */ 2344295367Sdes ctx->keys_seen[i] = 2; 2345295367Sdes ndone++; 2346295367Sdes } 2347295367Sdes if (ndone != ctx->nnew) 2348295367Sdes fatal("%s: ndone != ctx->nnew (%zu / %zu)", __func__, 2349295367Sdes ndone, ctx->nnew); /* Shouldn't happen */ 2350295367Sdes ssh_packet_check_eom(ssh); 2351295367Sdes 2352295367Sdes /* Make the edits to known_hosts */ 2353295367Sdes update_known_hosts(ctx); 2354295367Sdes out: 2355295367Sdes hostkeys_update_ctx_free(ctx); 2356295367Sdes} 2357295367Sdes 2358295367Sdes/* 2359295367Sdes * Handle hostkeys-00@openssh.com global request to inform the client of all 2360295367Sdes * the server's hostkeys. The keys are checked against the user's 2361295367Sdes * HostkeyAlgorithms preference before they are accepted. 2362295367Sdes */ 2363295367Sdesstatic int 2364295367Sdesclient_input_hostkeys(void) 2365295367Sdes{ 2366295367Sdes struct ssh *ssh = active_state; /* XXX */ 2367295367Sdes const u_char *blob = NULL; 2368295367Sdes size_t i, len = 0; 2369295367Sdes struct sshbuf *buf = NULL; 2370295367Sdes struct sshkey *key = NULL, **tmp; 2371295367Sdes int r; 2372295367Sdes char *fp; 2373295367Sdes static int hostkeys_seen = 0; /* XXX use struct ssh */ 2374295367Sdes extern struct sockaddr_storage hostaddr; /* XXX from ssh.c */ 2375295367Sdes struct hostkeys_update_ctx *ctx = NULL; 2376295367Sdes 2377295367Sdes if (hostkeys_seen) 2378295367Sdes fatal("%s: server already sent hostkeys", __func__); 2379295367Sdes if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK && 2380295367Sdes options.batch_mode) 2381295367Sdes return 1; /* won't ask in batchmode, so don't even try */ 2382295367Sdes if (!options.update_hostkeys || options.num_user_hostfiles <= 0) 2383295367Sdes return 1; 2384295367Sdes 2385295367Sdes ctx = xcalloc(1, sizeof(*ctx)); 2386295367Sdes while (ssh_packet_remaining(ssh) > 0) { 2387295367Sdes sshkey_free(key); 2388295367Sdes key = NULL; 2389295367Sdes if ((r = sshpkt_get_string_direct(ssh, &blob, &len)) != 0) { 2390295367Sdes error("%s: couldn't parse message: %s", 2391295367Sdes __func__, ssh_err(r)); 2392295367Sdes goto out; 2393295367Sdes } 2394295367Sdes if ((r = sshkey_from_blob(blob, len, &key)) != 0) { 2395295367Sdes error("%s: parse key: %s", __func__, ssh_err(r)); 2396295367Sdes goto out; 2397295367Sdes } 2398295367Sdes fp = sshkey_fingerprint(key, options.fingerprint_hash, 2399295367Sdes SSH_FP_DEFAULT); 2400295367Sdes debug3("%s: received %s key %s", __func__, 2401295367Sdes sshkey_type(key), fp); 2402295367Sdes free(fp); 2403295367Sdes 2404295367Sdes /* Check that the key is accepted in HostkeyAlgorithms */ 2405295367Sdes if (match_pattern_list(sshkey_ssh_name(key), 2406295367Sdes options.hostkeyalgorithms ? options.hostkeyalgorithms : 2407295367Sdes KEX_DEFAULT_PK_ALG, 0) != 1) { 2408295367Sdes debug3("%s: %s key not permitted by HostkeyAlgorithms", 2409295367Sdes __func__, sshkey_ssh_name(key)); 2410295367Sdes continue; 2411295367Sdes } 2412295367Sdes /* Skip certs */ 2413295367Sdes if (sshkey_is_cert(key)) { 2414295367Sdes debug3("%s: %s key is a certificate; skipping", 2415295367Sdes __func__, sshkey_ssh_name(key)); 2416295367Sdes continue; 2417295367Sdes } 2418295367Sdes /* Ensure keys are unique */ 2419295367Sdes for (i = 0; i < ctx->nkeys; i++) { 2420295367Sdes if (sshkey_equal(key, ctx->keys[i])) { 2421295367Sdes error("%s: received duplicated %s host key", 2422295367Sdes __func__, sshkey_ssh_name(key)); 2423295367Sdes goto out; 2424295367Sdes } 2425295367Sdes } 2426295367Sdes /* Key is good, record it */ 2427295367Sdes if ((tmp = reallocarray(ctx->keys, ctx->nkeys + 1, 2428295367Sdes sizeof(*ctx->keys))) == NULL) 2429295367Sdes fatal("%s: reallocarray failed nkeys = %zu", 2430295367Sdes __func__, ctx->nkeys); 2431295367Sdes ctx->keys = tmp; 2432295367Sdes ctx->keys[ctx->nkeys++] = key; 2433295367Sdes key = NULL; 2434295367Sdes } 2435295367Sdes 2436295367Sdes if (ctx->nkeys == 0) { 2437295367Sdes debug("%s: server sent no hostkeys", __func__); 2438295367Sdes goto out; 2439295367Sdes } 2440295367Sdes 2441295367Sdes if ((ctx->keys_seen = calloc(ctx->nkeys, 2442295367Sdes sizeof(*ctx->keys_seen))) == NULL) 2443295367Sdes fatal("%s: calloc failed", __func__); 2444295367Sdes 2445295367Sdes get_hostfile_hostname_ipaddr(host, 2446295367Sdes options.check_host_ip ? (struct sockaddr *)&hostaddr : NULL, 2447295367Sdes options.port, &ctx->host_str, 2448295367Sdes options.check_host_ip ? &ctx->ip_str : NULL); 2449295367Sdes 2450295367Sdes /* Find which keys we already know about. */ 2451295367Sdes if ((r = hostkeys_foreach(options.user_hostfiles[0], hostkeys_find, 2452295367Sdes ctx, ctx->host_str, ctx->ip_str, 2453295367Sdes HKF_WANT_PARSE_KEY|HKF_WANT_MATCH)) != 0) { 2454295367Sdes error("%s: hostkeys_foreach failed: %s", __func__, ssh_err(r)); 2455295367Sdes goto out; 2456295367Sdes } 2457295367Sdes 2458295367Sdes /* Figure out if we have any new keys to add */ 2459295367Sdes ctx->nnew = 0; 2460295367Sdes for (i = 0; i < ctx->nkeys; i++) { 2461295367Sdes if (!ctx->keys_seen[i]) 2462295367Sdes ctx->nnew++; 2463295367Sdes } 2464295367Sdes 2465295367Sdes debug3("%s: %zu keys from server: %zu new, %zu retained. %zu to remove", 2466295367Sdes __func__, ctx->nkeys, ctx->nnew, ctx->nkeys - ctx->nnew, ctx->nold); 2467295367Sdes 2468295367Sdes if (ctx->nnew == 0 && ctx->nold != 0) { 2469295367Sdes /* We have some keys to remove. Just do it. */ 2470295367Sdes update_known_hosts(ctx); 2471295367Sdes } else if (ctx->nnew != 0) { 2472295367Sdes /* 2473295367Sdes * We have received hitherto-unseen keys from the server. 2474295367Sdes * Ask the server to confirm ownership of the private halves. 2475295367Sdes */ 2476295367Sdes debug3("%s: asking server to prove ownership for %zu keys", 2477295367Sdes __func__, ctx->nnew); 2478295367Sdes if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || 2479295367Sdes (r = sshpkt_put_cstring(ssh, 2480295367Sdes "hostkeys-prove-00@openssh.com")) != 0 || 2481295367Sdes (r = sshpkt_put_u8(ssh, 1)) != 0) /* bool: want reply */ 2482295367Sdes fatal("%s: cannot prepare packet: %s", 2483295367Sdes __func__, ssh_err(r)); 2484295367Sdes if ((buf = sshbuf_new()) == NULL) 2485295367Sdes fatal("%s: sshbuf_new", __func__); 2486295367Sdes for (i = 0; i < ctx->nkeys; i++) { 2487295367Sdes if (ctx->keys_seen[i]) 2488295367Sdes continue; 2489295367Sdes sshbuf_reset(buf); 2490295367Sdes if ((r = sshkey_putb(ctx->keys[i], buf)) != 0) 2491295367Sdes fatal("%s: sshkey_putb: %s", 2492295367Sdes __func__, ssh_err(r)); 2493295367Sdes if ((r = sshpkt_put_stringb(ssh, buf)) != 0) 2494295367Sdes fatal("%s: sshpkt_put_string: %s", 2495295367Sdes __func__, ssh_err(r)); 2496295367Sdes } 2497295367Sdes if ((r = sshpkt_send(ssh)) != 0) 2498295367Sdes fatal("%s: sshpkt_send: %s", __func__, ssh_err(r)); 2499295367Sdes client_register_global_confirm( 2500295367Sdes client_global_hostkeys_private_confirm, ctx); 2501295367Sdes ctx = NULL; /* will be freed in callback */ 2502295367Sdes } 2503295367Sdes 2504295367Sdes /* Success */ 2505295367Sdes out: 2506295367Sdes hostkeys_update_ctx_free(ctx); 2507295367Sdes sshkey_free(key); 2508295367Sdes sshbuf_free(buf); 2509295367Sdes /* 2510295367Sdes * NB. Return success for all cases. The server doesn't need to know 2511295367Sdes * what the client does with its hosts file. 2512295367Sdes */ 2513295367Sdes return 1; 2514295367Sdes} 2515295367Sdes 2516295367Sdesstatic int 251792555Sdesclient_input_global_request(int type, u_int32_t seq, void *ctxt) 251892555Sdes{ 251992555Sdes char *rtype; 252092555Sdes int want_reply; 252192555Sdes int success = 0; 252276259Sgreen 2523295367Sdes rtype = packet_get_cstring(NULL); 252492555Sdes want_reply = packet_get_char(); 2525126274Sdes debug("client_input_global_request: rtype %s want_reply %d", 2526126274Sdes rtype, want_reply); 2527295367Sdes if (strcmp(rtype, "hostkeys-00@openssh.com") == 0) 2528295367Sdes success = client_input_hostkeys(); 252992555Sdes if (want_reply) { 253092555Sdes packet_start(success ? 253192555Sdes SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE); 253292555Sdes packet_send(); 253392555Sdes packet_write_wait(); 253492555Sdes } 2535255767Sdes free(rtype); 2536295367Sdes return 0; 253792555Sdes} 253892555Sdes 2539137015Sdesvoid 2540137015Sdesclient_session2_setup(int id, int want_tty, int want_subsystem, 2541181111Sdes const char *term, struct termios *tiop, int in_fd, Buffer *cmd, char **env) 2542137015Sdes{ 2543137015Sdes int len; 2544146998Sdes Channel *c = NULL; 2545137015Sdes 2546137015Sdes debug2("%s: id %d", __func__, id); 2547137015Sdes 2548146998Sdes if ((c = channel_lookup(id)) == NULL) 2549146998Sdes fatal("client_session2_setup: channel %d: unknown channel", id); 2550146998Sdes 2551221420Sdes packet_set_interactive(want_tty, 2552221420Sdes options.ip_qos_interactive, options.ip_qos_bulk); 2553221420Sdes 2554137015Sdes if (want_tty) { 2555137015Sdes struct winsize ws; 2556137015Sdes 2557137015Sdes /* Store window size in the packet. */ 2558137015Sdes if (ioctl(in_fd, TIOCGWINSZ, &ws) < 0) 2559137015Sdes memset(&ws, 0, sizeof(ws)); 2560137015Sdes 2561181111Sdes channel_request_start(id, "pty-req", 1); 2562226046Sdes client_expect_confirm(id, "PTY allocation", CONFIRM_TTY); 2563137015Sdes packet_put_cstring(term != NULL ? term : ""); 2564162852Sdes packet_put_int((u_int)ws.ws_col); 2565162852Sdes packet_put_int((u_int)ws.ws_row); 2566162852Sdes packet_put_int((u_int)ws.ws_xpixel); 2567162852Sdes packet_put_int((u_int)ws.ws_ypixel); 2568181111Sdes if (tiop == NULL) 2569181111Sdes tiop = get_saved_tio(); 2570181111Sdes tty_make_modes(-1, tiop); 2571137015Sdes packet_send(); 2572137015Sdes /* XXX wait for reply */ 2573146998Sdes c->client_tty = 1; 2574137015Sdes } 2575137015Sdes 2576137015Sdes /* Transfer any environment variables from client to server */ 2577137015Sdes if (options.num_send_env != 0 && env != NULL) { 2578137015Sdes int i, j, matched; 2579137015Sdes char *name, *val; 2580137015Sdes 2581137015Sdes debug("Sending environment."); 2582137015Sdes for (i = 0; env[i] != NULL; i++) { 2583137015Sdes /* Split */ 2584137015Sdes name = xstrdup(env[i]); 2585137015Sdes if ((val = strchr(name, '=')) == NULL) { 2586255767Sdes free(name); 2587137015Sdes continue; 2588137015Sdes } 2589137015Sdes *val++ = '\0'; 2590137015Sdes 2591137015Sdes matched = 0; 2592137015Sdes for (j = 0; j < options.num_send_env; j++) { 2593137015Sdes if (match_pattern(name, options.send_env[j])) { 2594137015Sdes matched = 1; 2595137015Sdes break; 2596137015Sdes } 2597137015Sdes } 2598137015Sdes if (!matched) { 2599137015Sdes debug3("Ignored env %s", name); 2600255767Sdes free(name); 2601137015Sdes continue; 2602137015Sdes } 2603137015Sdes 2604137015Sdes debug("Sending env %s = %s", name, val); 2605137015Sdes channel_request_start(id, "env", 0); 2606137015Sdes packet_put_cstring(name); 2607137015Sdes packet_put_cstring(val); 2608137015Sdes packet_send(); 2609255767Sdes free(name); 2610137015Sdes } 2611137015Sdes } 2612137015Sdes 2613137015Sdes len = buffer_len(cmd); 2614137015Sdes if (len > 0) { 2615137015Sdes if (len > 900) 2616137015Sdes len = 900; 2617137015Sdes if (want_subsystem) { 2618181111Sdes debug("Sending subsystem: %.*s", 2619181111Sdes len, (u_char*)buffer_ptr(cmd)); 2620181111Sdes channel_request_start(id, "subsystem", 1); 2621226046Sdes client_expect_confirm(id, "subsystem", CONFIRM_CLOSE); 2622137015Sdes } else { 2623181111Sdes debug("Sending command: %.*s", 2624181111Sdes len, (u_char*)buffer_ptr(cmd)); 2625181111Sdes channel_request_start(id, "exec", 1); 2626226046Sdes client_expect_confirm(id, "exec", CONFIRM_CLOSE); 2627137015Sdes } 2628137015Sdes packet_put_string(buffer_ptr(cmd), buffer_len(cmd)); 2629137015Sdes packet_send(); 2630137015Sdes } else { 2631181111Sdes channel_request_start(id, "shell", 1); 2632226046Sdes client_expect_confirm(id, "shell", CONFIRM_CLOSE); 2633137015Sdes packet_send(); 2634137015Sdes } 2635137015Sdes} 2636137015Sdes 263792555Sdesstatic void 263876259Sgreenclient_init_dispatch_20(void) 263960573Skris{ 264060573Skris dispatch_init(&dispatch_protocol_error); 264198675Sdes 264260573Skris dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); 264360573Skris dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data); 264460573Skris dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); 264560573Skris dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); 264660573Skris dispatch_set(SSH2_MSG_CHANNEL_OPEN, &client_input_channel_open); 264760573Skris dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); 264860573Skris dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); 264976259Sgreen dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &client_input_channel_req); 265060573Skris dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); 2651181111Sdes dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, &channel_input_status_confirm); 2652181111Sdes dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &channel_input_status_confirm); 265392555Sdes dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &client_input_global_request); 265476259Sgreen 265576259Sgreen /* rekeying */ 265676259Sgreen dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit); 265798675Sdes 265898675Sdes /* global request reply messages */ 265998675Sdes dispatch_set(SSH2_MSG_REQUEST_FAILURE, &client_global_request_reply); 266098675Sdes dispatch_set(SSH2_MSG_REQUEST_SUCCESS, &client_global_request_reply); 266160573Skris} 2662181111Sdes 266392555Sdesstatic void 266476259Sgreenclient_init_dispatch_13(void) 266560573Skris{ 266660573Skris dispatch_init(NULL); 266760573Skris dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close); 266860573Skris dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation); 266960573Skris dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data); 267060573Skris dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); 267160573Skris dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); 267260573Skris dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open); 267360573Skris dispatch_set(SSH_SMSG_EXITSTATUS, &client_input_exit_status); 267460573Skris dispatch_set(SSH_SMSG_STDERR_DATA, &client_input_stderr_data); 267560573Skris dispatch_set(SSH_SMSG_STDOUT_DATA, &client_input_stdout_data); 267668700Sgreen 267768700Sgreen dispatch_set(SSH_SMSG_AGENT_OPEN, options.forward_agent ? 2678126274Sdes &client_input_agent_open : &deny_input_open); 267968700Sgreen dispatch_set(SSH_SMSG_X11_OPEN, options.forward_x11 ? 268069587Sgreen &x11_input_open : &deny_input_open); 268160573Skris} 2682181111Sdes 268392555Sdesstatic void 268476259Sgreenclient_init_dispatch_15(void) 268560573Skris{ 268660573Skris client_init_dispatch_13(); 268760573Skris dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof); 268860573Skris dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, & channel_input_oclose); 268960573Skris} 2690181111Sdes 269192555Sdesstatic void 269276259Sgreenclient_init_dispatch(void) 269360573Skris{ 269460573Skris if (compat20) 269560573Skris client_init_dispatch_20(); 269660573Skris else if (compat13) 269760573Skris client_init_dispatch_13(); 269860573Skris else 269960573Skris client_init_dispatch_15(); 270060573Skris} 2701126274Sdes 2702226046Sdesvoid 2703226046Sdesclient_stop_mux(void) 2704226046Sdes{ 2705226046Sdes if (options.control_path != NULL && muxserver_sock != -1) 2706226046Sdes unlink(options.control_path); 2707226046Sdes /* 2708248619Sdes * If we are in persist mode, or don't have a shell, signal that we 2709248619Sdes * should close when all active channels are closed. 2710226046Sdes */ 2711248619Sdes if (options.control_persist || no_shell_flag) { 2712226046Sdes session_closed = 1; 2713226046Sdes setproctitle("[stopped mux]"); 2714226046Sdes } 2715226046Sdes} 2716226046Sdes 2717126274Sdes/* client specific fatal cleanup */ 2718126274Sdesvoid 2719126274Sdescleanup_exit(int i) 2720126274Sdes{ 2721226046Sdes leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 2722126274Sdes leave_non_blocking(); 2723181111Sdes if (options.control_path != NULL && muxserver_sock != -1) 2724137015Sdes unlink(options.control_path); 2725221420Sdes ssh_kill_proxy_command(); 2726126274Sdes _exit(i); 2727126274Sdes} 2728