1*4f52dfbbSDag-Erling Smørgrav /* $OpenBSD: clientloop.c,v 1.305 2017/09/19 04:24:22 djm Exp $ */ 2511b41d2SMark Murray /* 3511b41d2SMark Murray * Author: Tatu Ylonen <ylo@cs.hut.fi> 4511b41d2SMark Murray * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 5511b41d2SMark Murray * All rights reserved 6511b41d2SMark Murray * The main loop for the interactive session (client side). 7511b41d2SMark Murray * 8b66f2d16SKris Kennaway * As far as I am concerned, the code I have written for this software 9b66f2d16SKris Kennaway * can be used freely for any purpose. Any derived versions of this 10b66f2d16SKris Kennaway * software must be clearly marked as such, and if the derived work is 11b66f2d16SKris Kennaway * incompatible with the protocol description in the RFC file, it must be 12b66f2d16SKris Kennaway * called by a name other than "ssh" or "Secure Shell". 13b66f2d16SKris Kennaway * 14b66f2d16SKris Kennaway * 15b66f2d16SKris Kennaway * Copyright (c) 1999 Theo de Raadt. All rights reserved. 16b66f2d16SKris Kennaway * 17b66f2d16SKris Kennaway * Redistribution and use in source and binary forms, with or without 18b66f2d16SKris Kennaway * modification, are permitted provided that the following conditions 19b66f2d16SKris Kennaway * are met: 20b66f2d16SKris Kennaway * 1. Redistributions of source code must retain the above copyright 21b66f2d16SKris Kennaway * notice, this list of conditions and the following disclaimer. 22b66f2d16SKris Kennaway * 2. Redistributions in binary form must reproduce the above copyright 23b66f2d16SKris Kennaway * notice, this list of conditions and the following disclaimer in the 24b66f2d16SKris Kennaway * documentation and/or other materials provided with the distribution. 25b66f2d16SKris Kennaway * 26b66f2d16SKris Kennaway * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 27b66f2d16SKris Kennaway * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 28b66f2d16SKris Kennaway * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 29b66f2d16SKris Kennaway * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 30b66f2d16SKris Kennaway * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 31b66f2d16SKris Kennaway * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32b66f2d16SKris Kennaway * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33b66f2d16SKris Kennaway * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34b66f2d16SKris Kennaway * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 35b66f2d16SKris Kennaway * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36b66f2d16SKris Kennaway * 37b66f2d16SKris Kennaway * 38a04a10f8SKris Kennaway * SSH2 support added by Markus Friedl. 39ae1f160dSDag-Erling Smørgrav * Copyright (c) 1999, 2000, 2001 Markus Friedl. All rights reserved. 40b66f2d16SKris Kennaway * 41b66f2d16SKris Kennaway * Redistribution and use in source and binary forms, with or without 42b66f2d16SKris Kennaway * modification, are permitted provided that the following conditions 43b66f2d16SKris Kennaway * are met: 44b66f2d16SKris Kennaway * 1. Redistributions of source code must retain the above copyright 45b66f2d16SKris Kennaway * notice, this list of conditions and the following disclaimer. 46b66f2d16SKris Kennaway * 2. Redistributions in binary form must reproduce the above copyright 47b66f2d16SKris Kennaway * notice, this list of conditions and the following disclaimer in the 48b66f2d16SKris Kennaway * documentation and/or other materials provided with the distribution. 49b66f2d16SKris Kennaway * 50b66f2d16SKris Kennaway * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 51b66f2d16SKris Kennaway * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 52b66f2d16SKris Kennaway * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 53b66f2d16SKris Kennaway * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 54b66f2d16SKris Kennaway * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 55b66f2d16SKris Kennaway * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 56b66f2d16SKris Kennaway * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 57b66f2d16SKris Kennaway * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 58b66f2d16SKris Kennaway * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 59b66f2d16SKris Kennaway * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 60511b41d2SMark Murray */ 61511b41d2SMark Murray 62511b41d2SMark Murray #include "includes.h" 63511b41d2SMark Murray 64761efaa7SDag-Erling Smørgrav #include <sys/types.h> 65761efaa7SDag-Erling Smørgrav #include <sys/ioctl.h> 66761efaa7SDag-Erling Smørgrav #ifdef HAVE_SYS_STAT_H 67761efaa7SDag-Erling Smørgrav # include <sys/stat.h> 68761efaa7SDag-Erling Smørgrav #endif 69761efaa7SDag-Erling Smørgrav #ifdef HAVE_SYS_TIME_H 70761efaa7SDag-Erling Smørgrav # include <sys/time.h> 71761efaa7SDag-Erling Smørgrav #endif 72761efaa7SDag-Erling Smørgrav #include <sys/socket.h> 73761efaa7SDag-Erling Smørgrav 74761efaa7SDag-Erling Smørgrav #include <ctype.h> 75761efaa7SDag-Erling Smørgrav #include <errno.h> 76761efaa7SDag-Erling Smørgrav #ifdef HAVE_PATHS_H 77761efaa7SDag-Erling Smørgrav #include <paths.h> 78761efaa7SDag-Erling Smørgrav #endif 79761efaa7SDag-Erling Smørgrav #include <signal.h> 80761efaa7SDag-Erling Smørgrav #include <stdarg.h> 81761efaa7SDag-Erling Smørgrav #include <stdio.h> 82761efaa7SDag-Erling Smørgrav #include <stdlib.h> 83761efaa7SDag-Erling Smørgrav #include <string.h> 84761efaa7SDag-Erling Smørgrav #include <termios.h> 85761efaa7SDag-Erling Smørgrav #include <pwd.h> 86761efaa7SDag-Erling Smørgrav #include <unistd.h> 87bc5531deSDag-Erling Smørgrav #include <limits.h> 88761efaa7SDag-Erling Smørgrav 89d4af9e69SDag-Erling Smørgrav #include "openbsd-compat/sys-queue.h" 90761efaa7SDag-Erling Smørgrav #include "xmalloc.h" 91511b41d2SMark Murray #include "ssh.h" 921e8db6e2SBrian Feldman #include "ssh2.h" 93511b41d2SMark Murray #include "packet.h" 94511b41d2SMark Murray #include "buffer.h" 95a04a10f8SKris Kennaway #include "compat.h" 96a04a10f8SKris Kennaway #include "channels.h" 97a04a10f8SKris Kennaway #include "dispatch.h" 981e8db6e2SBrian Feldman #include "key.h" 99761efaa7SDag-Erling Smørgrav #include "cipher.h" 1001e8db6e2SBrian Feldman #include "kex.h" 101eccfee6eSDag-Erling Smørgrav #include "myproposal.h" 1021e8db6e2SBrian Feldman #include "log.h" 103a0ee8cc6SDag-Erling Smørgrav #include "misc.h" 1041e8db6e2SBrian Feldman #include "readconf.h" 1051e8db6e2SBrian Feldman #include "clientloop.h" 106021d409fSDag-Erling Smørgrav #include "sshconnect.h" 1071e8db6e2SBrian Feldman #include "authfd.h" 1081e8db6e2SBrian Feldman #include "atomicio.h" 109d74d50a8SDag-Erling Smørgrav #include "sshpty.h" 110d74d50a8SDag-Erling Smørgrav #include "match.h" 111d74d50a8SDag-Erling Smørgrav #include "msg.h" 112bc5531deSDag-Erling Smørgrav #include "ssherr.h" 113bc5531deSDag-Erling Smørgrav #include "hostfile.h" 1145b9b2fafSBrian Feldman 1155b9b2fafSBrian Feldman /* import options */ 1164899dde7SBrian Feldman extern Options options; 1174899dde7SBrian Feldman 118511b41d2SMark Murray /* Flag indicating that stdin should be redirected from /dev/null. */ 119511b41d2SMark Murray extern int stdin_null_flag; 120511b41d2SMark Murray 121efcad6b7SDag-Erling Smørgrav /* Flag indicating that no shell has been requested */ 122efcad6b7SDag-Erling Smørgrav extern int no_shell_flag; 123efcad6b7SDag-Erling Smørgrav 124076ad2f8SDag-Erling Smørgrav /* Flag indicating that ssh should daemonise after authentication is complete */ 125076ad2f8SDag-Erling Smørgrav extern int fork_after_authentication_flag; 126076ad2f8SDag-Erling Smørgrav 127d74d50a8SDag-Erling Smørgrav /* Control socket */ 128b15c8340SDag-Erling Smørgrav extern int muxserver_sock; /* XXX use mux_client_cleanup() instead */ 129d74d50a8SDag-Erling Smørgrav 130511b41d2SMark Murray /* 131511b41d2SMark Murray * Name of the host we are connecting to. This is the name given on the 132511b41d2SMark Murray * command line, or the HostName specified for the user-supplied name in a 133511b41d2SMark Murray * configuration file. 134511b41d2SMark Murray */ 135511b41d2SMark Murray extern char *host; 136511b41d2SMark Murray 137511b41d2SMark Murray /* 138511b41d2SMark Murray * Flag to indicate that we have received a window change signal which has 139511b41d2SMark Murray * not yet been processed. This will cause a message indicating the new 140511b41d2SMark Murray * window size to be sent to the server a little later. This is volatile 141511b41d2SMark Murray * because this is updated in a signal handler. 142511b41d2SMark Murray */ 143ae1f160dSDag-Erling Smørgrav static volatile sig_atomic_t received_window_change_signal = 0; 144ae1f160dSDag-Erling Smørgrav static volatile sig_atomic_t received_signal = 0; 145511b41d2SMark Murray 146021d409fSDag-Erling Smørgrav /* Flag indicating whether the user's terminal is in non-blocking mode. */ 147511b41d2SMark Murray static int in_non_blocking_mode = 0; 148511b41d2SMark Murray 149e2f6069cSDag-Erling Smørgrav /* Time when backgrounded control master using ControlPersist should exit */ 150e2f6069cSDag-Erling Smørgrav static time_t control_persist_exit_time = 0; 151e2f6069cSDag-Erling Smørgrav 152511b41d2SMark Murray /* Common data for the client loop code. */ 153b15c8340SDag-Erling Smørgrav volatile sig_atomic_t quit_pending; /* Set non-zero to quit the loop. */ 154511b41d2SMark Murray static int last_was_cr; /* Last character was a newline. */ 155d4af9e69SDag-Erling Smørgrav static int exit_status; /* Used to store the command exit status. */ 156*4f52dfbbSDag-Erling Smørgrav static Buffer stderr_buffer; /* Used for final exit message. */ 157511b41d2SMark Murray static int connection_in; /* Connection to server (input). */ 158511b41d2SMark Murray static int connection_out; /* Connection to server (output). */ 1591e8db6e2SBrian Feldman static int need_rekeying; /* Set to non-zero if rekeying is requested. */ 160e2f6069cSDag-Erling Smørgrav static int session_closed; /* In SSH2: login session closed. */ 161557f75e5SDag-Erling Smørgrav static u_int x11_refuse_time; /* If >0, refuse x11 opens after this time. */ 162a04a10f8SKris Kennaway 163ae1f160dSDag-Erling Smørgrav static void client_init_dispatch(void); 164a04a10f8SKris Kennaway int session_ident = -1; 165a04a10f8SKris Kennaway 166d4af9e69SDag-Erling Smørgrav /* Track escape per proto2 channel */ 167d4af9e69SDag-Erling Smørgrav struct escape_filter_ctx { 168d4af9e69SDag-Erling Smørgrav int escape_pending; 169d4af9e69SDag-Erling Smørgrav int escape_char; 170d74d50a8SDag-Erling Smørgrav }; 171d74d50a8SDag-Erling Smørgrav 172d4af9e69SDag-Erling Smørgrav /* Context for channel confirmation replies */ 173d4af9e69SDag-Erling Smørgrav struct channel_reply_ctx { 174d4af9e69SDag-Erling Smørgrav const char *request_type; 175e146993eSDag-Erling Smørgrav int id; 176e146993eSDag-Erling Smørgrav enum confirm_action action; 177d4af9e69SDag-Erling Smørgrav }; 178d4af9e69SDag-Erling Smørgrav 179d4af9e69SDag-Erling Smørgrav /* Global request success/failure callbacks */ 180*4f52dfbbSDag-Erling Smørgrav /* XXX move to struct ssh? */ 181d4af9e69SDag-Erling Smørgrav struct global_confirm { 182d4af9e69SDag-Erling Smørgrav TAILQ_ENTRY(global_confirm) entry; 183d4af9e69SDag-Erling Smørgrav global_confirm_cb *cb; 184d4af9e69SDag-Erling Smørgrav void *ctx; 185d4af9e69SDag-Erling Smørgrav int ref_count; 186d4af9e69SDag-Erling Smørgrav }; 187d4af9e69SDag-Erling Smørgrav TAILQ_HEAD(global_confirms, global_confirm); 188d4af9e69SDag-Erling Smørgrav static struct global_confirms global_confirms = 189d4af9e69SDag-Erling Smørgrav TAILQ_HEAD_INITIALIZER(global_confirms); 190d4af9e69SDag-Erling Smørgrav 191d74d50a8SDag-Erling Smørgrav void ssh_process_session2_setup(int, int, int, Buffer *); 192d74d50a8SDag-Erling Smørgrav 193511b41d2SMark Murray /* Restores stdin to blocking mode. */ 194511b41d2SMark Murray 195ae1f160dSDag-Erling Smørgrav static void 1961e8db6e2SBrian Feldman leave_non_blocking(void) 197511b41d2SMark Murray { 198511b41d2SMark Murray if (in_non_blocking_mode) { 199d74d50a8SDag-Erling Smørgrav unset_nonblock(fileno(stdin)); 200511b41d2SMark Murray in_non_blocking_mode = 0; 201511b41d2SMark Murray } 202511b41d2SMark Murray } 203511b41d2SMark Murray 204511b41d2SMark Murray /* 205511b41d2SMark Murray * Signal handler for the window change signal (SIGWINCH). This just sets a 206511b41d2SMark Murray * flag indicating that the window has changed. 207511b41d2SMark Murray */ 208761efaa7SDag-Erling Smørgrav /*ARGSUSED */ 209ae1f160dSDag-Erling Smørgrav static void 210511b41d2SMark Murray window_change_handler(int sig) 211511b41d2SMark Murray { 212511b41d2SMark Murray received_window_change_signal = 1; 213511b41d2SMark Murray signal(SIGWINCH, window_change_handler); 214511b41d2SMark Murray } 215511b41d2SMark Murray 216511b41d2SMark Murray /* 217511b41d2SMark Murray * Signal handler for signals that cause the program to terminate. These 218511b41d2SMark Murray * signals must be trapped to restore terminal modes. 219511b41d2SMark Murray */ 220761efaa7SDag-Erling Smørgrav /*ARGSUSED */ 221ae1f160dSDag-Erling Smørgrav static void 222511b41d2SMark Murray signal_handler(int sig) 223511b41d2SMark Murray { 224ae1f160dSDag-Erling Smørgrav received_signal = sig; 225ae1f160dSDag-Erling Smørgrav quit_pending = 1; 226511b41d2SMark Murray } 227511b41d2SMark Murray 228511b41d2SMark Murray /* 229511b41d2SMark Murray * Returns current time in seconds from Jan 1, 1970 with the maximum 230511b41d2SMark Murray * available resolution. 231511b41d2SMark Murray */ 232511b41d2SMark Murray 233ae1f160dSDag-Erling Smørgrav static double 2341e8db6e2SBrian Feldman get_current_time(void) 235511b41d2SMark Murray { 236511b41d2SMark Murray struct timeval tv; 237511b41d2SMark Murray gettimeofday(&tv, NULL); 238511b41d2SMark Murray return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0; 239511b41d2SMark Murray } 240511b41d2SMark Murray 241e2f6069cSDag-Erling Smørgrav /* 242e2f6069cSDag-Erling Smørgrav * Sets control_persist_exit_time to the absolute time when the 243e2f6069cSDag-Erling Smørgrav * backgrounded control master should exit due to expiry of the 244e2f6069cSDag-Erling Smørgrav * ControlPersist timeout. Sets it to 0 if we are not a backgrounded 245e2f6069cSDag-Erling Smørgrav * control master process, or if there is no ControlPersist timeout. 246e2f6069cSDag-Erling Smørgrav */ 247e2f6069cSDag-Erling Smørgrav static void 248*4f52dfbbSDag-Erling Smørgrav set_control_persist_exit_time(struct ssh *ssh) 249e2f6069cSDag-Erling Smørgrav { 250e2f6069cSDag-Erling Smørgrav if (muxserver_sock == -1 || !options.control_persist 251e146993eSDag-Erling Smørgrav || options.control_persist_timeout == 0) { 252e2f6069cSDag-Erling Smørgrav /* not using a ControlPersist timeout */ 253e2f6069cSDag-Erling Smørgrav control_persist_exit_time = 0; 254*4f52dfbbSDag-Erling Smørgrav } else if (channel_still_open(ssh)) { 255e2f6069cSDag-Erling Smørgrav /* some client connections are still open */ 256e2f6069cSDag-Erling Smørgrav if (control_persist_exit_time > 0) 257e2f6069cSDag-Erling Smørgrav debug2("%s: cancel scheduled exit", __func__); 258e2f6069cSDag-Erling Smørgrav control_persist_exit_time = 0; 259e2f6069cSDag-Erling Smørgrav } else if (control_persist_exit_time <= 0) { 260e2f6069cSDag-Erling Smørgrav /* a client connection has recently closed */ 261e4a9863fSDag-Erling Smørgrav control_persist_exit_time = monotime() + 262e2f6069cSDag-Erling Smørgrav (time_t)options.control_persist_timeout; 263e2f6069cSDag-Erling Smørgrav debug2("%s: schedule exit in %d seconds", __func__, 264e2f6069cSDag-Erling Smørgrav options.control_persist_timeout); 265e2f6069cSDag-Erling Smørgrav } 266e2f6069cSDag-Erling Smørgrav /* else we are already counting down to the timeout */ 267e2f6069cSDag-Erling Smørgrav } 268e2f6069cSDag-Erling Smørgrav 269462c32cbSDag-Erling Smørgrav #define SSH_X11_VALID_DISPLAY_CHARS ":/.-_" 270462c32cbSDag-Erling Smørgrav static int 271462c32cbSDag-Erling Smørgrav client_x11_display_valid(const char *display) 272462c32cbSDag-Erling Smørgrav { 273462c32cbSDag-Erling Smørgrav size_t i, dlen; 274462c32cbSDag-Erling Smørgrav 275acc1a9efSDag-Erling Smørgrav if (display == NULL) 276acc1a9efSDag-Erling Smørgrav return 0; 277acc1a9efSDag-Erling Smørgrav 278462c32cbSDag-Erling Smørgrav dlen = strlen(display); 279462c32cbSDag-Erling Smørgrav for (i = 0; i < dlen; i++) { 280f7167e0eSDag-Erling Smørgrav if (!isalnum((u_char)display[i]) && 281462c32cbSDag-Erling Smørgrav strchr(SSH_X11_VALID_DISPLAY_CHARS, display[i]) == NULL) { 282462c32cbSDag-Erling Smørgrav debug("Invalid character '%c' in DISPLAY", display[i]); 283462c32cbSDag-Erling Smørgrav return 0; 284462c32cbSDag-Erling Smørgrav } 285462c32cbSDag-Erling Smørgrav } 286462c32cbSDag-Erling Smørgrav return 1; 287462c32cbSDag-Erling Smørgrav } 288462c32cbSDag-Erling Smørgrav 289043840dfSDag-Erling Smørgrav #define SSH_X11_PROTO "MIT-MAGIC-COOKIE-1" 290557f75e5SDag-Erling Smørgrav #define X11_TIMEOUT_SLACK 60 291acc1a9efSDag-Erling Smørgrav int 292*4f52dfbbSDag-Erling Smørgrav client_x11_get_proto(struct ssh *ssh, const char *display, 293*4f52dfbbSDag-Erling Smørgrav const char *xauth_path, u_int trusted, u_int timeout, 294*4f52dfbbSDag-Erling Smørgrav char **_proto, char **_data) 295043840dfSDag-Erling Smørgrav { 296acc1a9efSDag-Erling Smørgrav char cmd[1024], line[512], xdisplay[512]; 297acc1a9efSDag-Erling Smørgrav char xauthfile[PATH_MAX], xauthdir[PATH_MAX]; 298043840dfSDag-Erling Smørgrav static char proto[512], data[512]; 299043840dfSDag-Erling Smørgrav FILE *f; 300ca86bcf2SDag-Erling Smørgrav int got_data = 0, generated = 0, do_unlink = 0, r; 301043840dfSDag-Erling Smørgrav struct stat st; 302557f75e5SDag-Erling Smørgrav u_int now, x11_timeout_real; 303043840dfSDag-Erling Smørgrav 304043840dfSDag-Erling Smørgrav *_proto = proto; 305043840dfSDag-Erling Smørgrav *_data = data; 306acc1a9efSDag-Erling Smørgrav proto[0] = data[0] = xauthfile[0] = xauthdir[0] = '\0'; 307043840dfSDag-Erling Smørgrav 308acc1a9efSDag-Erling Smørgrav if (!client_x11_display_valid(display)) { 309acc1a9efSDag-Erling Smørgrav if (display != NULL) 310acc1a9efSDag-Erling Smørgrav logit("DISPLAY \"%s\" invalid; disabling X11 forwarding", 311462c32cbSDag-Erling Smørgrav display); 312acc1a9efSDag-Erling Smørgrav return -1; 313043840dfSDag-Erling Smørgrav } 314acc1a9efSDag-Erling Smørgrav if (xauth_path != NULL && stat(xauth_path, &st) == -1) { 315acc1a9efSDag-Erling Smørgrav debug("No xauth program."); 316acc1a9efSDag-Erling Smørgrav xauth_path = NULL; 317acc1a9efSDag-Erling Smørgrav } 318acc1a9efSDag-Erling Smørgrav 319acc1a9efSDag-Erling Smørgrav if (xauth_path != NULL) { 320043840dfSDag-Erling Smørgrav /* 321043840dfSDag-Erling Smørgrav * Handle FamilyLocal case where $DISPLAY does 322043840dfSDag-Erling Smørgrav * not match an authorization entry. For this we 323043840dfSDag-Erling Smørgrav * just try "xauth list unix:displaynum.screennum". 324043840dfSDag-Erling Smørgrav * XXX: "localhost" match to determine FamilyLocal 325043840dfSDag-Erling Smørgrav * is not perfect. 326043840dfSDag-Erling Smørgrav */ 327043840dfSDag-Erling Smørgrav if (strncmp(display, "localhost:", 10) == 0) { 328acc1a9efSDag-Erling Smørgrav if ((r = snprintf(xdisplay, sizeof(xdisplay), "unix:%s", 329acc1a9efSDag-Erling Smørgrav display + 10)) < 0 || 330acc1a9efSDag-Erling Smørgrav (size_t)r >= sizeof(xdisplay)) { 331acc1a9efSDag-Erling Smørgrav error("%s: display name too long", __func__); 332acc1a9efSDag-Erling Smørgrav return -1; 333acc1a9efSDag-Erling Smørgrav } 334043840dfSDag-Erling Smørgrav display = xdisplay; 335043840dfSDag-Erling Smørgrav } 336043840dfSDag-Erling Smørgrav if (trusted == 0) { 337557f75e5SDag-Erling Smørgrav /* 338acc1a9efSDag-Erling Smørgrav * Generate an untrusted X11 auth cookie. 339acc1a9efSDag-Erling Smørgrav * 340557f75e5SDag-Erling Smørgrav * The authentication cookie should briefly outlive 341557f75e5SDag-Erling Smørgrav * ssh's willingness to forward X11 connections to 342557f75e5SDag-Erling Smørgrav * avoid nasty fail-open behaviour in the X server. 343557f75e5SDag-Erling Smørgrav */ 344acc1a9efSDag-Erling Smørgrav mktemp_proto(xauthdir, sizeof(xauthdir)); 345acc1a9efSDag-Erling Smørgrav if (mkdtemp(xauthdir) == NULL) { 346acc1a9efSDag-Erling Smørgrav error("%s: mkdtemp: %s", 347acc1a9efSDag-Erling Smørgrav __func__, strerror(errno)); 348acc1a9efSDag-Erling Smørgrav return -1; 349acc1a9efSDag-Erling Smørgrav } 350acc1a9efSDag-Erling Smørgrav do_unlink = 1; 351acc1a9efSDag-Erling Smørgrav if ((r = snprintf(xauthfile, sizeof(xauthfile), 352acc1a9efSDag-Erling Smørgrav "%s/xauthfile", xauthdir)) < 0 || 353acc1a9efSDag-Erling Smørgrav (size_t)r >= sizeof(xauthfile)) { 354acc1a9efSDag-Erling Smørgrav error("%s: xauthfile path too long", __func__); 355acc1a9efSDag-Erling Smørgrav unlink(xauthfile); 356acc1a9efSDag-Erling Smørgrav rmdir(xauthdir); 357acc1a9efSDag-Erling Smørgrav return -1; 358acc1a9efSDag-Erling Smørgrav } 359acc1a9efSDag-Erling Smørgrav 360557f75e5SDag-Erling Smørgrav if (timeout >= UINT_MAX - X11_TIMEOUT_SLACK) 361557f75e5SDag-Erling Smørgrav x11_timeout_real = UINT_MAX; 362557f75e5SDag-Erling Smørgrav else 363557f75e5SDag-Erling Smørgrav x11_timeout_real = timeout + X11_TIMEOUT_SLACK; 364acc1a9efSDag-Erling Smørgrav if ((r = snprintf(cmd, sizeof(cmd), 365043840dfSDag-Erling Smørgrav "%s -f %s generate %s " SSH_X11_PROTO 366e2f6069cSDag-Erling Smørgrav " untrusted timeout %u 2>" _PATH_DEVNULL, 367557f75e5SDag-Erling Smørgrav xauth_path, xauthfile, display, 368acc1a9efSDag-Erling Smørgrav x11_timeout_real)) < 0 || 369acc1a9efSDag-Erling Smørgrav (size_t)r >= sizeof(cmd)) 370acc1a9efSDag-Erling Smørgrav fatal("%s: cmd too long", __func__); 371acc1a9efSDag-Erling Smørgrav debug2("%s: %s", __func__, cmd); 372e2f6069cSDag-Erling Smørgrav if (x11_refuse_time == 0) { 373e4a9863fSDag-Erling Smørgrav now = monotime() + 1; 374e2f6069cSDag-Erling Smørgrav if (UINT_MAX - timeout < now) 375e2f6069cSDag-Erling Smørgrav x11_refuse_time = UINT_MAX; 376e2f6069cSDag-Erling Smørgrav else 377e2f6069cSDag-Erling Smørgrav x11_refuse_time = now + timeout; 378*4f52dfbbSDag-Erling Smørgrav channel_set_x11_refuse_time(ssh, 379*4f52dfbbSDag-Erling Smørgrav x11_refuse_time); 380e2f6069cSDag-Erling Smørgrav } 381557f75e5SDag-Erling Smørgrav if (system(cmd) == 0) 382557f75e5SDag-Erling Smørgrav generated = 1; 383043840dfSDag-Erling Smørgrav } 384d4af9e69SDag-Erling Smørgrav 385d4af9e69SDag-Erling Smørgrav /* 386d4af9e69SDag-Erling Smørgrav * When in untrusted mode, we read the cookie only if it was 387d4af9e69SDag-Erling Smørgrav * successfully generated as an untrusted one in the step 388d4af9e69SDag-Erling Smørgrav * above. 389d4af9e69SDag-Erling Smørgrav */ 390d4af9e69SDag-Erling Smørgrav if (trusted || generated) { 391043840dfSDag-Erling Smørgrav snprintf(cmd, sizeof(cmd), 392021d409fSDag-Erling Smørgrav "%s %s%s list %s 2>" _PATH_DEVNULL, 393043840dfSDag-Erling Smørgrav xauth_path, 394043840dfSDag-Erling Smørgrav generated ? "-f " : "" , 395043840dfSDag-Erling Smørgrav generated ? xauthfile : "", 396043840dfSDag-Erling Smørgrav display); 397043840dfSDag-Erling Smørgrav debug2("x11_get_proto: %s", cmd); 398043840dfSDag-Erling Smørgrav f = popen(cmd, "r"); 399043840dfSDag-Erling Smørgrav if (f && fgets(line, sizeof(line), f) && 400043840dfSDag-Erling Smørgrav sscanf(line, "%*s %511s %511s", proto, data) == 2) 401043840dfSDag-Erling Smørgrav got_data = 1; 402043840dfSDag-Erling Smørgrav if (f) 403043840dfSDag-Erling Smørgrav pclose(f); 404acc1a9efSDag-Erling Smørgrav } 405043840dfSDag-Erling Smørgrav } 406043840dfSDag-Erling Smørgrav 407043840dfSDag-Erling Smørgrav if (do_unlink) { 408043840dfSDag-Erling Smørgrav unlink(xauthfile); 409043840dfSDag-Erling Smørgrav rmdir(xauthdir); 410043840dfSDag-Erling Smørgrav } 411acc1a9efSDag-Erling Smørgrav 412acc1a9efSDag-Erling Smørgrav /* Don't fall back to fake X11 data for untrusted forwarding */ 413acc1a9efSDag-Erling Smørgrav if (!trusted && !got_data) { 414acc1a9efSDag-Erling Smørgrav error("Warning: untrusted X11 forwarding setup failed: " 415acc1a9efSDag-Erling Smørgrav "xauth key data not generated"); 416acc1a9efSDag-Erling Smørgrav return -1; 417acc1a9efSDag-Erling Smørgrav } 418043840dfSDag-Erling Smørgrav 419043840dfSDag-Erling Smørgrav /* 420043840dfSDag-Erling Smørgrav * If we didn't get authentication data, just make up some 421043840dfSDag-Erling Smørgrav * data. The forwarding code will check the validity of the 422043840dfSDag-Erling Smørgrav * response anyway, and substitute this data. The X11 423043840dfSDag-Erling Smørgrav * server, however, will ignore this fake data and use 424043840dfSDag-Erling Smørgrav * whatever authentication mechanisms it was using otherwise 425043840dfSDag-Erling Smørgrav * for the local connection. 426043840dfSDag-Erling Smørgrav */ 427043840dfSDag-Erling Smørgrav if (!got_data) { 428ca86bcf2SDag-Erling Smørgrav u_int8_t rnd[16]; 429ca86bcf2SDag-Erling Smørgrav u_int i; 430043840dfSDag-Erling Smørgrav 431043840dfSDag-Erling Smørgrav logit("Warning: No xauth data; " 432043840dfSDag-Erling Smørgrav "using fake authentication data for X11 forwarding."); 433043840dfSDag-Erling Smørgrav strlcpy(proto, SSH_X11_PROTO, sizeof proto); 434ca86bcf2SDag-Erling Smørgrav arc4random_buf(rnd, sizeof(rnd)); 435ca86bcf2SDag-Erling Smørgrav for (i = 0; i < sizeof(rnd); i++) { 436043840dfSDag-Erling Smørgrav snprintf(data + 2 * i, sizeof data - 2 * i, "%02x", 437ca86bcf2SDag-Erling Smørgrav rnd[i]); 438043840dfSDag-Erling Smørgrav } 439043840dfSDag-Erling Smørgrav } 440acc1a9efSDag-Erling Smørgrav 441acc1a9efSDag-Erling Smørgrav return 0; 442043840dfSDag-Erling Smørgrav } 443043840dfSDag-Erling Smørgrav 444511b41d2SMark Murray /* 445511b41d2SMark Murray * Checks if the client window has changed, and sends a packet about it to 446511b41d2SMark Murray * the server if so. The actual change is detected elsewhere (by a software 447511b41d2SMark Murray * interrupt on Unix); this just checks the flag and sends a message if 448511b41d2SMark Murray * appropriate. 449511b41d2SMark Murray */ 450511b41d2SMark Murray 451ae1f160dSDag-Erling Smørgrav static void 452*4f52dfbbSDag-Erling Smørgrav client_check_window_change(struct ssh *ssh) 453511b41d2SMark Murray { 454a04a10f8SKris Kennaway if (!received_window_change_signal) 455a04a10f8SKris Kennaway return; 456a04a10f8SKris Kennaway /** XXX race */ 457511b41d2SMark Murray received_window_change_signal = 0; 458511b41d2SMark Murray 459*4f52dfbbSDag-Erling Smørgrav debug2("%s: changed", __func__); 460a04a10f8SKris Kennaway 461*4f52dfbbSDag-Erling Smørgrav channel_send_window_changes(ssh); 462511b41d2SMark Murray } 463511b41d2SMark Murray 464bc5531deSDag-Erling Smørgrav static int 465*4f52dfbbSDag-Erling Smørgrav client_global_request_reply(int type, u_int32_t seq, struct ssh *ssh) 466efcad6b7SDag-Erling Smørgrav { 467d4af9e69SDag-Erling Smørgrav struct global_confirm *gc; 468d4af9e69SDag-Erling Smørgrav 469d4af9e69SDag-Erling Smørgrav if ((gc = TAILQ_FIRST(&global_confirms)) == NULL) 470bc5531deSDag-Erling Smørgrav return 0; 471d4af9e69SDag-Erling Smørgrav if (gc->cb != NULL) 472*4f52dfbbSDag-Erling Smørgrav gc->cb(ssh, type, seq, gc->ctx); 473d4af9e69SDag-Erling Smørgrav if (--gc->ref_count <= 0) { 474d4af9e69SDag-Erling Smørgrav TAILQ_REMOVE(&global_confirms, gc, entry); 475b83788ffSDag-Erling Smørgrav explicit_bzero(gc, sizeof(*gc)); 476e4a9863fSDag-Erling Smørgrav free(gc); 477d4af9e69SDag-Erling Smørgrav } 478d4af9e69SDag-Erling Smørgrav 4797aee6ffeSDag-Erling Smørgrav packet_set_alive_timeouts(0); 480bc5531deSDag-Erling Smørgrav return 0; 481efcad6b7SDag-Erling Smørgrav } 482efcad6b7SDag-Erling Smørgrav 483efcad6b7SDag-Erling Smørgrav static void 484efcad6b7SDag-Erling Smørgrav server_alive_check(void) 485efcad6b7SDag-Erling Smørgrav { 4867aee6ffeSDag-Erling Smørgrav if (packet_inc_alive_timeouts() > options.server_alive_count_max) { 4874a421b63SDag-Erling Smørgrav logit("Timeout, server %s not responding.", host); 48892eb0aa1SDag-Erling Smørgrav cleanup_exit(255); 48992eb0aa1SDag-Erling Smørgrav } 490efcad6b7SDag-Erling Smørgrav packet_start(SSH2_MSG_GLOBAL_REQUEST); 491efcad6b7SDag-Erling Smørgrav packet_put_cstring("keepalive@openssh.com"); 492efcad6b7SDag-Erling Smørgrav packet_put_char(1); /* boolean: want reply */ 493efcad6b7SDag-Erling Smørgrav packet_send(); 494d4af9e69SDag-Erling Smørgrav /* Insert an empty placeholder to maintain ordering */ 495d4af9e69SDag-Erling Smørgrav client_register_global_confirm(NULL, NULL); 496efcad6b7SDag-Erling Smørgrav } 497efcad6b7SDag-Erling Smørgrav 498511b41d2SMark Murray /* 499511b41d2SMark Murray * Waits until the client can do something (some data becomes available on 500511b41d2SMark Murray * one of the file descriptors). 501511b41d2SMark Murray */ 502ae1f160dSDag-Erling Smørgrav static void 503*4f52dfbbSDag-Erling Smørgrav client_wait_until_can_do_something(struct ssh *ssh, 504*4f52dfbbSDag-Erling Smørgrav fd_set **readsetp, fd_set **writesetp, 505d74d50a8SDag-Erling Smørgrav int *maxfdp, u_int *nallocp, int rekeying) 506511b41d2SMark Murray { 507efcad6b7SDag-Erling Smørgrav struct timeval tv, *tvp; 508e2f6069cSDag-Erling Smørgrav int timeout_secs; 509e4a9863fSDag-Erling Smørgrav time_t minwait_secs = 0, server_alive_time = 0, now = monotime(); 510efcad6b7SDag-Erling Smørgrav int ret; 511efcad6b7SDag-Erling Smørgrav 5121e8db6e2SBrian Feldman /* Add any selections by the channel mechanism. */ 513*4f52dfbbSDag-Erling Smørgrav channel_prepare_select(active_state, readsetp, writesetp, maxfdp, 514*4f52dfbbSDag-Erling Smørgrav nallocp, &minwait_secs); 515511b41d2SMark Murray 516ae1f160dSDag-Erling Smørgrav /* channel_prepare_select could have closed the last channel */ 517*4f52dfbbSDag-Erling Smørgrav if (session_closed && !channel_still_open(ssh) && 518ae1f160dSDag-Erling Smørgrav !packet_have_data_to_write()) { 519ae1f160dSDag-Erling Smørgrav /* clear mask since we did not call select() */ 520ae1f160dSDag-Erling Smørgrav memset(*readsetp, 0, *nallocp); 521ae1f160dSDag-Erling Smørgrav memset(*writesetp, 0, *nallocp); 522ae1f160dSDag-Erling Smørgrav return; 523*4f52dfbbSDag-Erling Smørgrav } 524*4f52dfbbSDag-Erling Smørgrav 5251e8db6e2SBrian Feldman FD_SET(connection_in, *readsetp); 526511b41d2SMark Murray 527511b41d2SMark Murray /* Select server connection if have data to write to the server. */ 528511b41d2SMark Murray if (packet_have_data_to_write()) 5291e8db6e2SBrian Feldman FD_SET(connection_out, *writesetp); 530511b41d2SMark Murray 531511b41d2SMark Murray /* 532511b41d2SMark Murray * Wait for something to happen. This will suspend the process until 533511b41d2SMark Murray * some selected descriptor can be read, written, or has some other 534e2f6069cSDag-Erling Smørgrav * event pending, or a timeout expires. 535511b41d2SMark Murray */ 536511b41d2SMark Murray 537e2f6069cSDag-Erling Smørgrav timeout_secs = INT_MAX; /* we use INT_MAX to mean no timeout */ 538*4f52dfbbSDag-Erling Smørgrav if (options.server_alive_interval > 0) { 539e2f6069cSDag-Erling Smørgrav timeout_secs = options.server_alive_interval; 540e4a9863fSDag-Erling Smørgrav server_alive_time = now + options.server_alive_interval; 541e4a9863fSDag-Erling Smørgrav } 542*4f52dfbbSDag-Erling Smørgrav if (options.rekey_interval > 0 && !rekeying) 543ca86bcf2SDag-Erling Smørgrav timeout_secs = MINIMUM(timeout_secs, packet_get_rekey_timeout()); 544*4f52dfbbSDag-Erling Smørgrav set_control_persist_exit_time(ssh); 545e2f6069cSDag-Erling Smørgrav if (control_persist_exit_time > 0) { 546ca86bcf2SDag-Erling Smørgrav timeout_secs = MINIMUM(timeout_secs, 547e4a9863fSDag-Erling Smørgrav control_persist_exit_time - now); 548e2f6069cSDag-Erling Smørgrav if (timeout_secs < 0) 549e2f6069cSDag-Erling Smørgrav timeout_secs = 0; 550e2f6069cSDag-Erling Smørgrav } 551462c32cbSDag-Erling Smørgrav if (minwait_secs != 0) 552ca86bcf2SDag-Erling Smørgrav timeout_secs = MINIMUM(timeout_secs, (int)minwait_secs); 553e2f6069cSDag-Erling Smørgrav if (timeout_secs == INT_MAX) 554efcad6b7SDag-Erling Smørgrav tvp = NULL; 555efcad6b7SDag-Erling Smørgrav else { 556e2f6069cSDag-Erling Smørgrav tv.tv_sec = timeout_secs; 557efcad6b7SDag-Erling Smørgrav tv.tv_usec = 0; 558efcad6b7SDag-Erling Smørgrav tvp = &tv; 559efcad6b7SDag-Erling Smørgrav } 560e2f6069cSDag-Erling Smørgrav 561efcad6b7SDag-Erling Smørgrav ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp); 562efcad6b7SDag-Erling Smørgrav if (ret < 0) { 563511b41d2SMark Murray char buf[100]; 5641e8db6e2SBrian Feldman 5651e8db6e2SBrian Feldman /* 5661e8db6e2SBrian Feldman * We have to clear the select masks, because we return. 5671e8db6e2SBrian Feldman * We have to return, because the mainloop checks for the flags 5681e8db6e2SBrian Feldman * set by the signal handlers. 5691e8db6e2SBrian Feldman */ 570ae1f160dSDag-Erling Smørgrav memset(*readsetp, 0, *nallocp); 571ae1f160dSDag-Erling Smørgrav memset(*writesetp, 0, *nallocp); 5721e8db6e2SBrian Feldman 573511b41d2SMark Murray if (errno == EINTR) 574511b41d2SMark Murray return; 575511b41d2SMark Murray /* Note: we might still have data in the buffers. */ 576511b41d2SMark Murray snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno)); 577511b41d2SMark Murray buffer_append(&stderr_buffer, buf, strlen(buf)); 578511b41d2SMark Murray quit_pending = 1; 579e4a9863fSDag-Erling Smørgrav } else if (ret == 0) { 580e4a9863fSDag-Erling Smørgrav /* 581e4a9863fSDag-Erling Smørgrav * Timeout. Could have been either keepalive or rekeying. 582e4a9863fSDag-Erling Smørgrav * Keepalive we check here, rekeying is checked in clientloop. 583e4a9863fSDag-Erling Smørgrav */ 584e4a9863fSDag-Erling Smørgrav if (server_alive_time != 0 && server_alive_time <= monotime()) 585efcad6b7SDag-Erling Smørgrav server_alive_check(); 586511b41d2SMark Murray } 587511b41d2SMark Murray 588e4a9863fSDag-Erling Smørgrav } 589e4a9863fSDag-Erling Smørgrav 590ae1f160dSDag-Erling Smørgrav static void 591b66f2d16SKris Kennaway client_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr) 592511b41d2SMark Murray { 593511b41d2SMark Murray /* Flush stdout and stderr buffers. */ 594b66f2d16SKris Kennaway if (buffer_len(bout) > 0) 595d4af9e69SDag-Erling Smørgrav atomicio(vwrite, fileno(stdout), buffer_ptr(bout), 596d4af9e69SDag-Erling Smørgrav buffer_len(bout)); 597b66f2d16SKris Kennaway if (buffer_len(berr) > 0) 598d4af9e69SDag-Erling Smørgrav atomicio(vwrite, fileno(stderr), buffer_ptr(berr), 599d4af9e69SDag-Erling Smørgrav buffer_len(berr)); 600511b41d2SMark Murray 601e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 602511b41d2SMark Murray 603*4f52dfbbSDag-Erling Smørgrav sshbuf_reset(bin); 604*4f52dfbbSDag-Erling Smørgrav sshbuf_reset(bout); 605*4f52dfbbSDag-Erling Smørgrav sshbuf_reset(berr); 606511b41d2SMark Murray 607511b41d2SMark Murray /* Send the suspend signal to the program itself. */ 608511b41d2SMark Murray kill(getpid(), SIGTSTP); 609511b41d2SMark Murray 6105e8dbd04SDag-Erling Smørgrav /* Reset window sizes in case they have changed */ 611511b41d2SMark Murray received_window_change_signal = 1; 612511b41d2SMark Murray 613e146993eSDag-Erling Smørgrav enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 614511b41d2SMark Murray } 615511b41d2SMark Murray 616ae1f160dSDag-Erling Smørgrav static void 617a04a10f8SKris Kennaway client_process_net_input(fd_set *readset) 618511b41d2SMark Murray { 619acc1a9efSDag-Erling Smørgrav int len; 6207aee6ffeSDag-Erling Smørgrav char buf[SSH_IOBUFSZ]; 621511b41d2SMark Murray 622511b41d2SMark Murray /* 623511b41d2SMark Murray * Read input from the server, and add any such data to the buffer of 624511b41d2SMark Murray * the packet subsystem. 625511b41d2SMark Murray */ 626511b41d2SMark Murray if (FD_ISSET(connection_in, readset)) { 627511b41d2SMark Murray /* Read as much as possible. */ 628acc1a9efSDag-Erling Smørgrav len = read(connection_in, buf, sizeof(buf)); 629acc1a9efSDag-Erling Smørgrav if (len == 0) { 630d4af9e69SDag-Erling Smørgrav /* 631d4af9e69SDag-Erling Smørgrav * Received EOF. The remote host has closed the 632d4af9e69SDag-Erling Smørgrav * connection. 633d4af9e69SDag-Erling Smørgrav */ 634d4af9e69SDag-Erling Smørgrav snprintf(buf, sizeof buf, 635d4af9e69SDag-Erling Smørgrav "Connection to %.300s closed by remote host.\r\n", 636511b41d2SMark Murray host); 637511b41d2SMark Murray buffer_append(&stderr_buffer, buf, strlen(buf)); 638511b41d2SMark Murray quit_pending = 1; 639511b41d2SMark Murray return; 640511b41d2SMark Murray } 641511b41d2SMark Murray /* 642511b41d2SMark Murray * There is a kernel bug on Solaris that causes select to 643511b41d2SMark Murray * sometimes wake up even though there is no data available. 644511b41d2SMark Murray */ 645d4af9e69SDag-Erling Smørgrav if (len < 0 && 646d4af9e69SDag-Erling Smørgrav (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)) 647511b41d2SMark Murray len = 0; 648511b41d2SMark Murray 649511b41d2SMark Murray if (len < 0) { 650d4af9e69SDag-Erling Smørgrav /* 651d4af9e69SDag-Erling Smørgrav * An error has encountered. Perhaps there is a 652d4af9e69SDag-Erling Smørgrav * network problem. 653d4af9e69SDag-Erling Smørgrav */ 654d4af9e69SDag-Erling Smørgrav snprintf(buf, sizeof buf, 655d4af9e69SDag-Erling Smørgrav "Read from remote host %.300s: %.100s\r\n", 656511b41d2SMark Murray host, strerror(errno)); 657511b41d2SMark Murray buffer_append(&stderr_buffer, buf, strlen(buf)); 658511b41d2SMark Murray quit_pending = 1; 659511b41d2SMark Murray return; 660511b41d2SMark Murray } 661511b41d2SMark Murray packet_process_incoming(buf, len); 662511b41d2SMark Murray } 663a04a10f8SKris Kennaway } 664a04a10f8SKris Kennaway 665545d5ecaSDag-Erling Smørgrav static void 666*4f52dfbbSDag-Erling Smørgrav client_status_confirm(struct ssh *ssh, int type, Channel *c, void *ctx) 667d74d50a8SDag-Erling Smørgrav { 668d4af9e69SDag-Erling Smørgrav struct channel_reply_ctx *cr = (struct channel_reply_ctx *)ctx; 669d4af9e69SDag-Erling Smørgrav char errmsg[256]; 670d4af9e69SDag-Erling Smørgrav int tochan; 671d74d50a8SDag-Erling Smørgrav 672e146993eSDag-Erling Smørgrav /* 673e146993eSDag-Erling Smørgrav * If a TTY was explicitly requested, then a failure to allocate 674e146993eSDag-Erling Smørgrav * one is fatal. 675e146993eSDag-Erling Smørgrav */ 676e146993eSDag-Erling Smørgrav if (cr->action == CONFIRM_TTY && 677e146993eSDag-Erling Smørgrav (options.request_tty == REQUEST_TTY_FORCE || 678e146993eSDag-Erling Smørgrav options.request_tty == REQUEST_TTY_YES)) 679e146993eSDag-Erling Smørgrav cr->action = CONFIRM_CLOSE; 680e146993eSDag-Erling Smørgrav 681d4af9e69SDag-Erling Smørgrav /* XXX supress on mux _client_ quietmode */ 682d4af9e69SDag-Erling Smørgrav tochan = options.log_level >= SYSLOG_LEVEL_ERROR && 683b15c8340SDag-Erling Smørgrav c->ctl_chan != -1 && c->extended_usage == CHAN_EXTENDED_WRITE; 684d74d50a8SDag-Erling Smørgrav 685d4af9e69SDag-Erling Smørgrav if (type == SSH2_MSG_CHANNEL_SUCCESS) { 686d4af9e69SDag-Erling Smørgrav debug2("%s request accepted on channel %d", 687d4af9e69SDag-Erling Smørgrav cr->request_type, c->self); 688d4af9e69SDag-Erling Smørgrav } else if (type == SSH2_MSG_CHANNEL_FAILURE) { 689d4af9e69SDag-Erling Smørgrav if (tochan) { 690d4af9e69SDag-Erling Smørgrav snprintf(errmsg, sizeof(errmsg), 691d4af9e69SDag-Erling Smørgrav "%s request failed\r\n", cr->request_type); 692d4af9e69SDag-Erling Smørgrav } else { 693d4af9e69SDag-Erling Smørgrav snprintf(errmsg, sizeof(errmsg), 694d4af9e69SDag-Erling Smørgrav "%s request failed on channel %d", 695d4af9e69SDag-Erling Smørgrav cr->request_type, c->self); 696d74d50a8SDag-Erling Smørgrav } 697d4af9e69SDag-Erling Smørgrav /* If error occurred on primary session channel, then exit */ 698e146993eSDag-Erling Smørgrav if (cr->action == CONFIRM_CLOSE && c->self == session_ident) 699d4af9e69SDag-Erling Smørgrav fatal("%s", errmsg); 700e146993eSDag-Erling Smørgrav /* 701e146993eSDag-Erling Smørgrav * If error occurred on mux client, append to 702e146993eSDag-Erling Smørgrav * their stderr. 703e146993eSDag-Erling Smørgrav */ 704e146993eSDag-Erling Smørgrav if (tochan) { 705*4f52dfbbSDag-Erling Smørgrav buffer_append(c->extended, errmsg, strlen(errmsg)); 706e146993eSDag-Erling Smørgrav } else 707d4af9e69SDag-Erling Smørgrav error("%s", errmsg); 708e146993eSDag-Erling Smørgrav if (cr->action == CONFIRM_TTY) { 709e146993eSDag-Erling Smørgrav /* 710e146993eSDag-Erling Smørgrav * If a TTY allocation error occurred, then arrange 711e146993eSDag-Erling Smørgrav * for the correct TTY to leave raw mode. 712e146993eSDag-Erling Smørgrav */ 713e146993eSDag-Erling Smørgrav if (c->self == session_ident) 714e146993eSDag-Erling Smørgrav leave_raw_mode(0); 715e146993eSDag-Erling Smørgrav else 716*4f52dfbbSDag-Erling Smørgrav mux_tty_alloc_failed(ssh, c); 717e146993eSDag-Erling Smørgrav } else if (cr->action == CONFIRM_CLOSE) { 718*4f52dfbbSDag-Erling Smørgrav chan_read_failed(ssh, c); 719*4f52dfbbSDag-Erling Smørgrav chan_write_failed(ssh, c); 720d74d50a8SDag-Erling Smørgrav } 721d74d50a8SDag-Erling Smørgrav } 722e4a9863fSDag-Erling Smørgrav free(cr); 723d4af9e69SDag-Erling Smørgrav } 724d74d50a8SDag-Erling Smørgrav 725d74d50a8SDag-Erling Smørgrav static void 726*4f52dfbbSDag-Erling Smørgrav client_abandon_status_confirm(struct ssh *ssh, Channel *c, void *ctx) 727d74d50a8SDag-Erling Smørgrav { 728e4a9863fSDag-Erling Smørgrav free(ctx); 729d74d50a8SDag-Erling Smørgrav } 730d74d50a8SDag-Erling Smørgrav 731e146993eSDag-Erling Smørgrav void 732*4f52dfbbSDag-Erling Smørgrav client_expect_confirm(struct ssh *ssh, int id, const char *request, 733e146993eSDag-Erling Smørgrav enum confirm_action action) 734d74d50a8SDag-Erling Smørgrav { 7350a37d4a3SXin LI struct channel_reply_ctx *cr = xcalloc(1, sizeof(*cr)); 736d74d50a8SDag-Erling Smørgrav 737d4af9e69SDag-Erling Smørgrav cr->request_type = request; 738e146993eSDag-Erling Smørgrav cr->action = action; 739d74d50a8SDag-Erling Smørgrav 740*4f52dfbbSDag-Erling Smørgrav channel_register_status_confirm(ssh, id, client_status_confirm, 741d4af9e69SDag-Erling Smørgrav client_abandon_status_confirm, cr); 742d4af9e69SDag-Erling Smørgrav } 743d4af9e69SDag-Erling Smørgrav 744d4af9e69SDag-Erling Smørgrav void 745d4af9e69SDag-Erling Smørgrav client_register_global_confirm(global_confirm_cb *cb, void *ctx) 746d4af9e69SDag-Erling Smørgrav { 747d4af9e69SDag-Erling Smørgrav struct global_confirm *gc, *last_gc; 748d4af9e69SDag-Erling Smørgrav 749d4af9e69SDag-Erling Smørgrav /* Coalesce identical callbacks */ 750d4af9e69SDag-Erling Smørgrav last_gc = TAILQ_LAST(&global_confirms, global_confirms); 751d4af9e69SDag-Erling Smørgrav if (last_gc && last_gc->cb == cb && last_gc->ctx == ctx) { 752d4af9e69SDag-Erling Smørgrav if (++last_gc->ref_count >= INT_MAX) 753d4af9e69SDag-Erling Smørgrav fatal("%s: last_gc->ref_count = %d", 754d4af9e69SDag-Erling Smørgrav __func__, last_gc->ref_count); 755d74d50a8SDag-Erling Smørgrav return; 756d74d50a8SDag-Erling Smørgrav } 757d74d50a8SDag-Erling Smørgrav 7580a37d4a3SXin LI gc = xcalloc(1, sizeof(*gc)); 759d4af9e69SDag-Erling Smørgrav gc->cb = cb; 760d4af9e69SDag-Erling Smørgrav gc->ctx = ctx; 761d4af9e69SDag-Erling Smørgrav gc->ref_count = 1; 762d4af9e69SDag-Erling Smørgrav TAILQ_INSERT_TAIL(&global_confirms, gc, entry); 763d74d50a8SDag-Erling Smørgrav } 764d74d50a8SDag-Erling Smørgrav 765d74d50a8SDag-Erling Smørgrav static void 766*4f52dfbbSDag-Erling Smørgrav process_cmdline(struct ssh *ssh) 767545d5ecaSDag-Erling Smørgrav { 768545d5ecaSDag-Erling Smørgrav void (*handler)(int); 769a0ee8cc6SDag-Erling Smørgrav char *s, *cmd; 770a0ee8cc6SDag-Erling Smørgrav int ok, delete = 0, local = 0, remote = 0, dynamic = 0; 771a0ee8cc6SDag-Erling Smørgrav struct Forward fwd; 772545d5ecaSDag-Erling Smørgrav 773b83788ffSDag-Erling Smørgrav memset(&fwd, 0, sizeof(fwd)); 774d4af9e69SDag-Erling Smørgrav 775e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 776545d5ecaSDag-Erling Smørgrav handler = signal(SIGINT, SIG_IGN); 777545d5ecaSDag-Erling Smørgrav cmd = s = read_passphrase("\r\nssh> ", RP_ECHO); 778545d5ecaSDag-Erling Smørgrav if (s == NULL) 779545d5ecaSDag-Erling Smørgrav goto out; 780f7167e0eSDag-Erling Smørgrav while (isspace((u_char)*s)) 781545d5ecaSDag-Erling Smørgrav s++; 782d74d50a8SDag-Erling Smørgrav if (*s == '-') 783d74d50a8SDag-Erling Smørgrav s++; /* Skip cmdline '-', if any */ 784d74d50a8SDag-Erling Smørgrav if (*s == '\0') 785545d5ecaSDag-Erling Smørgrav goto out; 786d74d50a8SDag-Erling Smørgrav 787d74d50a8SDag-Erling Smørgrav if (*s == 'h' || *s == 'H' || *s == '?') { 788d74d50a8SDag-Erling Smørgrav logit("Commands:"); 789761efaa7SDag-Erling Smørgrav logit(" -L[bind_address:]port:host:hostport " 790761efaa7SDag-Erling Smørgrav "Request local forward"); 791761efaa7SDag-Erling Smørgrav logit(" -R[bind_address:]port:host:hostport " 792761efaa7SDag-Erling Smørgrav "Request remote forward"); 793cce7d346SDag-Erling Smørgrav logit(" -D[bind_address:]port " 794cce7d346SDag-Erling Smørgrav "Request dynamic forward"); 795462c32cbSDag-Erling Smørgrav logit(" -KL[bind_address:]port " 796462c32cbSDag-Erling Smørgrav "Cancel local forward"); 797761efaa7SDag-Erling Smørgrav logit(" -KR[bind_address:]port " 798761efaa7SDag-Erling Smørgrav "Cancel remote forward"); 799462c32cbSDag-Erling Smørgrav logit(" -KD[bind_address:]port " 800462c32cbSDag-Erling Smørgrav "Cancel dynamic forward"); 801021d409fSDag-Erling Smørgrav if (!options.permit_local_command) 802021d409fSDag-Erling Smørgrav goto out; 803761efaa7SDag-Erling Smørgrav logit(" !args " 804761efaa7SDag-Erling Smørgrav "Execute local command"); 805021d409fSDag-Erling Smørgrav goto out; 806021d409fSDag-Erling Smørgrav } 807021d409fSDag-Erling Smørgrav 808021d409fSDag-Erling Smørgrav if (*s == '!' && options.permit_local_command) { 809021d409fSDag-Erling Smørgrav s++; 810021d409fSDag-Erling Smørgrav ssh_local_cmd(s); 811d74d50a8SDag-Erling Smørgrav goto out; 812d74d50a8SDag-Erling Smørgrav } 813d74d50a8SDag-Erling Smørgrav 814d74d50a8SDag-Erling Smørgrav if (*s == 'K') { 815d74d50a8SDag-Erling Smørgrav delete = 1; 816d74d50a8SDag-Erling Smørgrav s++; 817d74d50a8SDag-Erling Smørgrav } 818cce7d346SDag-Erling Smørgrav if (*s == 'L') 819cce7d346SDag-Erling Smørgrav local = 1; 820cce7d346SDag-Erling Smørgrav else if (*s == 'R') 821cce7d346SDag-Erling Smørgrav remote = 1; 822cce7d346SDag-Erling Smørgrav else if (*s == 'D') 823cce7d346SDag-Erling Smørgrav dynamic = 1; 824cce7d346SDag-Erling Smørgrav else { 825d95e11bfSDag-Erling Smørgrav logit("Invalid command."); 826545d5ecaSDag-Erling Smørgrav goto out; 827545d5ecaSDag-Erling Smørgrav } 828cce7d346SDag-Erling Smørgrav 829f7167e0eSDag-Erling Smørgrav while (isspace((u_char)*++s)) 830d4af9e69SDag-Erling Smørgrav ; 831545d5ecaSDag-Erling Smørgrav 832b15c8340SDag-Erling Smørgrav /* XXX update list of forwards in options */ 833d74d50a8SDag-Erling Smørgrav if (delete) { 834a0ee8cc6SDag-Erling Smørgrav /* We pass 1 for dynamicfwd to restrict to 1 or 2 fields. */ 835a0ee8cc6SDag-Erling Smørgrav if (!parse_forward(&fwd, s, 1, 0)) { 836a0ee8cc6SDag-Erling Smørgrav logit("Bad forwarding close specification."); 837545d5ecaSDag-Erling Smørgrav goto out; 838545d5ecaSDag-Erling Smørgrav } 839462c32cbSDag-Erling Smørgrav if (remote) 840*4f52dfbbSDag-Erling Smørgrav ok = channel_request_rforward_cancel(ssh, &fwd) == 0; 841462c32cbSDag-Erling Smørgrav else if (dynamic) 842*4f52dfbbSDag-Erling Smørgrav ok = channel_cancel_lport_listener(ssh, &fwd, 843a0ee8cc6SDag-Erling Smørgrav 0, &options.fwd_opts) > 0; 844462c32cbSDag-Erling Smørgrav else 845*4f52dfbbSDag-Erling Smørgrav ok = channel_cancel_lport_listener(ssh, &fwd, 846a0ee8cc6SDag-Erling Smørgrav CHANNEL_CANCEL_PORT_STATIC, 847a0ee8cc6SDag-Erling Smørgrav &options.fwd_opts) > 0; 848462c32cbSDag-Erling Smørgrav if (!ok) { 849d93a896eSDag-Erling Smørgrav logit("Unknown port forwarding."); 850462c32cbSDag-Erling Smørgrav goto out; 851462c32cbSDag-Erling Smørgrav } 852462c32cbSDag-Erling Smørgrav logit("Canceled forwarding."); 8535e8dbd04SDag-Erling Smørgrav } else { 854cce7d346SDag-Erling Smørgrav if (!parse_forward(&fwd, s, dynamic, remote)) { 8555e8dbd04SDag-Erling Smørgrav logit("Bad forwarding specification."); 856545d5ecaSDag-Erling Smørgrav goto out; 857545d5ecaSDag-Erling Smørgrav } 858cce7d346SDag-Erling Smørgrav if (local || dynamic) { 859*4f52dfbbSDag-Erling Smørgrav if (!channel_setup_local_fwd_listener(ssh, &fwd, 860a0ee8cc6SDag-Erling Smørgrav &options.fwd_opts)) { 861d95e11bfSDag-Erling Smørgrav logit("Port forwarding failed."); 862545d5ecaSDag-Erling Smørgrav goto out; 863545d5ecaSDag-Erling Smørgrav } 8645e8dbd04SDag-Erling Smørgrav } else { 865*4f52dfbbSDag-Erling Smørgrav if (channel_request_remote_forwarding(ssh, &fwd) < 0) { 866761efaa7SDag-Erling Smørgrav logit("Port forwarding failed."); 867761efaa7SDag-Erling Smørgrav goto out; 868761efaa7SDag-Erling Smørgrav } 8695e8dbd04SDag-Erling Smørgrav } 870d95e11bfSDag-Erling Smørgrav logit("Forwarding port."); 871d74d50a8SDag-Erling Smørgrav } 872d74d50a8SDag-Erling Smørgrav 873545d5ecaSDag-Erling Smørgrav out: 874545d5ecaSDag-Erling Smørgrav signal(SIGINT, handler); 875e146993eSDag-Erling Smørgrav enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 876e4a9863fSDag-Erling Smørgrav free(cmd); 877e4a9863fSDag-Erling Smørgrav free(fwd.listen_host); 878a0ee8cc6SDag-Erling Smørgrav free(fwd.listen_path); 879e4a9863fSDag-Erling Smørgrav free(fwd.connect_host); 880a0ee8cc6SDag-Erling Smørgrav free(fwd.connect_path); 881545d5ecaSDag-Erling Smørgrav } 882545d5ecaSDag-Erling Smørgrav 8836888a9beSDag-Erling Smørgrav /* reasons to suppress output of an escape command in help output */ 8846888a9beSDag-Erling Smørgrav #define SUPPRESS_NEVER 0 /* never suppress, always show */ 885*4f52dfbbSDag-Erling Smørgrav #define SUPPRESS_MUXCLIENT 1 /* don't show in mux client sessions */ 886*4f52dfbbSDag-Erling Smørgrav #define SUPPRESS_MUXMASTER 2 /* don't show in mux master sessions */ 887*4f52dfbbSDag-Erling Smørgrav #define SUPPRESS_SYSLOG 4 /* don't show when logging to syslog */ 8886888a9beSDag-Erling Smørgrav struct escape_help_text { 8896888a9beSDag-Erling Smørgrav const char *cmd; 8906888a9beSDag-Erling Smørgrav const char *text; 8916888a9beSDag-Erling Smørgrav unsigned int flags; 8926888a9beSDag-Erling Smørgrav }; 8936888a9beSDag-Erling Smørgrav static struct escape_help_text esc_txt[] = { 8946888a9beSDag-Erling Smørgrav {".", "terminate session", SUPPRESS_MUXMASTER}, 8956888a9beSDag-Erling Smørgrav {".", "terminate connection (and any multiplexed sessions)", 8966888a9beSDag-Erling Smørgrav SUPPRESS_MUXCLIENT}, 897*4f52dfbbSDag-Erling Smørgrav {"B", "send a BREAK to the remote system", SUPPRESS_NEVER}, 8986888a9beSDag-Erling Smørgrav {"C", "open a command line", SUPPRESS_MUXCLIENT}, 899*4f52dfbbSDag-Erling Smørgrav {"R", "request rekey", SUPPRESS_NEVER}, 9006888a9beSDag-Erling Smørgrav {"V/v", "decrease/increase verbosity (LogLevel)", SUPPRESS_MUXCLIENT}, 9016888a9beSDag-Erling Smørgrav {"^Z", "suspend ssh", SUPPRESS_MUXCLIENT}, 9026888a9beSDag-Erling Smørgrav {"#", "list forwarded connections", SUPPRESS_NEVER}, 9036888a9beSDag-Erling Smørgrav {"&", "background ssh (when waiting for connections to terminate)", 9046888a9beSDag-Erling Smørgrav SUPPRESS_MUXCLIENT}, 9056888a9beSDag-Erling Smørgrav {"?", "this message", SUPPRESS_NEVER}, 9066888a9beSDag-Erling Smørgrav }; 9076888a9beSDag-Erling Smørgrav 9086888a9beSDag-Erling Smørgrav static void 909*4f52dfbbSDag-Erling Smørgrav print_escape_help(Buffer *b, int escape_char, int mux_client, int using_stderr) 9106888a9beSDag-Erling Smørgrav { 9116888a9beSDag-Erling Smørgrav unsigned int i, suppress_flags; 9126888a9beSDag-Erling Smørgrav char string[1024]; 9136888a9beSDag-Erling Smørgrav 9146888a9beSDag-Erling Smørgrav snprintf(string, sizeof string, "%c?\r\n" 9156888a9beSDag-Erling Smørgrav "Supported escape sequences:\r\n", escape_char); 9166888a9beSDag-Erling Smørgrav buffer_append(b, string, strlen(string)); 9176888a9beSDag-Erling Smørgrav 918*4f52dfbbSDag-Erling Smørgrav suppress_flags = 9196888a9beSDag-Erling Smørgrav (mux_client ? SUPPRESS_MUXCLIENT : 0) | 9206888a9beSDag-Erling Smørgrav (mux_client ? 0 : SUPPRESS_MUXMASTER) | 9216888a9beSDag-Erling Smørgrav (using_stderr ? 0 : SUPPRESS_SYSLOG); 9226888a9beSDag-Erling Smørgrav 9236888a9beSDag-Erling Smørgrav for (i = 0; i < sizeof(esc_txt)/sizeof(esc_txt[0]); i++) { 9246888a9beSDag-Erling Smørgrav if (esc_txt[i].flags & suppress_flags) 9256888a9beSDag-Erling Smørgrav continue; 9266888a9beSDag-Erling Smørgrav snprintf(string, sizeof string, " %c%-3s - %s\r\n", 9276888a9beSDag-Erling Smørgrav escape_char, esc_txt[i].cmd, esc_txt[i].text); 9286888a9beSDag-Erling Smørgrav buffer_append(b, string, strlen(string)); 9296888a9beSDag-Erling Smørgrav } 9306888a9beSDag-Erling Smørgrav 9316888a9beSDag-Erling Smørgrav snprintf(string, sizeof string, 9326888a9beSDag-Erling Smørgrav " %c%c - send the escape character by typing it twice\r\n" 9336888a9beSDag-Erling Smørgrav "(Note that escapes are only recognized immediately after " 9346888a9beSDag-Erling Smørgrav "newline.)\r\n", escape_char, escape_char); 9356888a9beSDag-Erling Smørgrav buffer_append(b, string, strlen(string)); 9366888a9beSDag-Erling Smørgrav } 9376888a9beSDag-Erling Smørgrav 938d4af9e69SDag-Erling Smørgrav /* 939*4f52dfbbSDag-Erling Smørgrav * Process the characters one by one. 940d4af9e69SDag-Erling Smørgrav */ 941ae1f160dSDag-Erling Smørgrav static int 942*4f52dfbbSDag-Erling Smørgrav process_escapes(struct ssh *ssh, Channel *c, 943*4f52dfbbSDag-Erling Smørgrav Buffer *bin, Buffer *bout, Buffer *berr, 944d4af9e69SDag-Erling Smørgrav char *buf, int len) 945b66f2d16SKris Kennaway { 946b66f2d16SKris Kennaway char string[1024]; 947b66f2d16SKris Kennaway pid_t pid; 948b66f2d16SKris Kennaway int bytes = 0; 9491e8db6e2SBrian Feldman u_int i; 9501e8db6e2SBrian Feldman u_char ch; 951b66f2d16SKris Kennaway char *s; 952*4f52dfbbSDag-Erling Smørgrav struct escape_filter_ctx *efc = c->filter_ctx == NULL ? 953*4f52dfbbSDag-Erling Smørgrav NULL : (struct escape_filter_ctx *)c->filter_ctx; 954d4af9e69SDag-Erling Smørgrav 955d4af9e69SDag-Erling Smørgrav if (c->filter_ctx == NULL) 956d4af9e69SDag-Erling Smørgrav return 0; 957b66f2d16SKris Kennaway 958043840dfSDag-Erling Smørgrav if (len <= 0) 959043840dfSDag-Erling Smørgrav return (0); 960043840dfSDag-Erling Smørgrav 961043840dfSDag-Erling Smørgrav for (i = 0; i < (u_int)len; i++) { 962b66f2d16SKris Kennaway /* Get one character at a time. */ 963b66f2d16SKris Kennaway ch = buf[i]; 964b66f2d16SKris Kennaway 965*4f52dfbbSDag-Erling Smørgrav if (efc->escape_pending) { 966b66f2d16SKris Kennaway /* We have previously seen an escape character. */ 967b66f2d16SKris Kennaway /* Clear the flag now. */ 968*4f52dfbbSDag-Erling Smørgrav efc->escape_pending = 0; 969b66f2d16SKris Kennaway 970b66f2d16SKris Kennaway /* Process the escaped character. */ 971b66f2d16SKris Kennaway switch (ch) { 972b66f2d16SKris Kennaway case '.': 973b66f2d16SKris Kennaway /* Terminate the connection. */ 974d4af9e69SDag-Erling Smørgrav snprintf(string, sizeof string, "%c.\r\n", 975*4f52dfbbSDag-Erling Smørgrav efc->escape_char); 976b66f2d16SKris Kennaway buffer_append(berr, string, strlen(string)); 977b66f2d16SKris Kennaway 978b15c8340SDag-Erling Smørgrav if (c && c->ctl_chan != -1) { 979*4f52dfbbSDag-Erling Smørgrav chan_read_failed(ssh, c); 980*4f52dfbbSDag-Erling Smørgrav chan_write_failed(ssh, c); 981*4f52dfbbSDag-Erling Smørgrav if (c->detach_user) { 982*4f52dfbbSDag-Erling Smørgrav c->detach_user(ssh, 983*4f52dfbbSDag-Erling Smørgrav c->self, NULL); 984*4f52dfbbSDag-Erling Smørgrav } 985e4a9863fSDag-Erling Smørgrav c->type = SSH_CHANNEL_ABANDONED; 986*4f52dfbbSDag-Erling Smørgrav buffer_clear(c->input); 987*4f52dfbbSDag-Erling Smørgrav chan_ibuf_empty(ssh, c); 988d4af9e69SDag-Erling Smørgrav return 0; 989d4af9e69SDag-Erling Smørgrav } else 990b66f2d16SKris Kennaway quit_pending = 1; 991b66f2d16SKris Kennaway return -1; 992b66f2d16SKris Kennaway 993b66f2d16SKris Kennaway case 'Z' - 64: 994d4af9e69SDag-Erling Smørgrav /* XXX support this for mux clients */ 995b15c8340SDag-Erling Smørgrav if (c && c->ctl_chan != -1) { 9966888a9beSDag-Erling Smørgrav char b[16]; 997d4af9e69SDag-Erling Smørgrav noescape: 9986888a9beSDag-Erling Smørgrav if (ch == 'Z' - 64) 9996888a9beSDag-Erling Smørgrav snprintf(b, sizeof b, "^Z"); 10006888a9beSDag-Erling Smørgrav else 10016888a9beSDag-Erling Smørgrav snprintf(b, sizeof b, "%c", ch); 1002d4af9e69SDag-Erling Smørgrav snprintf(string, sizeof string, 10036888a9beSDag-Erling Smørgrav "%c%s escape not available to " 1004d4af9e69SDag-Erling Smørgrav "multiplexed sessions\r\n", 1005*4f52dfbbSDag-Erling Smørgrav efc->escape_char, b); 1006d4af9e69SDag-Erling Smørgrav buffer_append(berr, string, 1007d4af9e69SDag-Erling Smørgrav strlen(string)); 1008d4af9e69SDag-Erling Smørgrav continue; 1009d4af9e69SDag-Erling Smørgrav } 1010d4af9e69SDag-Erling Smørgrav /* Suspend the program. Inform the user */ 1011d4af9e69SDag-Erling Smørgrav snprintf(string, sizeof string, 1012*4f52dfbbSDag-Erling Smørgrav "%c^Z [suspend ssh]\r\n", efc->escape_char); 1013b66f2d16SKris Kennaway buffer_append(berr, string, strlen(string)); 1014b66f2d16SKris Kennaway 1015b66f2d16SKris Kennaway /* Restore terminal modes and suspend. */ 1016b66f2d16SKris Kennaway client_suspend_self(bin, bout, berr); 1017b66f2d16SKris Kennaway 1018b66f2d16SKris Kennaway /* We have been continued. */ 1019b66f2d16SKris Kennaway continue; 1020b66f2d16SKris Kennaway 1021d95e11bfSDag-Erling Smørgrav case 'B': 1022d95e11bfSDag-Erling Smørgrav snprintf(string, sizeof string, 1023*4f52dfbbSDag-Erling Smørgrav "%cB\r\n", efc->escape_char); 1024*4f52dfbbSDag-Erling Smørgrav buffer_append(berr, string, strlen(string)); 1025*4f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, c->self, "break", 0); 1026d95e11bfSDag-Erling Smørgrav packet_put_int(1000); 1027d95e11bfSDag-Erling Smørgrav packet_send(); 1028d95e11bfSDag-Erling Smørgrav continue; 1029d95e11bfSDag-Erling Smørgrav 10301e8db6e2SBrian Feldman case 'R': 10311e8db6e2SBrian Feldman if (datafellows & SSH_BUG_NOREKEY) 1032d4af9e69SDag-Erling Smørgrav logit("Server does not " 1033d4af9e69SDag-Erling Smørgrav "support re-keying"); 10341e8db6e2SBrian Feldman else 10351e8db6e2SBrian Feldman need_rekeying = 1; 10361e8db6e2SBrian Feldman continue; 10371e8db6e2SBrian Feldman 10386888a9beSDag-Erling Smørgrav case 'V': 10396888a9beSDag-Erling Smørgrav /* FALLTHROUGH */ 10406888a9beSDag-Erling Smørgrav case 'v': 10416888a9beSDag-Erling Smørgrav if (c && c->ctl_chan != -1) 10426888a9beSDag-Erling Smørgrav goto noescape; 10436888a9beSDag-Erling Smørgrav if (!log_is_on_stderr()) { 10446888a9beSDag-Erling Smørgrav snprintf(string, sizeof string, 10456888a9beSDag-Erling Smørgrav "%c%c [Logging to syslog]\r\n", 1046*4f52dfbbSDag-Erling Smørgrav efc->escape_char, ch); 10476888a9beSDag-Erling Smørgrav buffer_append(berr, string, 10486888a9beSDag-Erling Smørgrav strlen(string)); 10496888a9beSDag-Erling Smørgrav continue; 10506888a9beSDag-Erling Smørgrav } 10516888a9beSDag-Erling Smørgrav if (ch == 'V' && options.log_level > 10526888a9beSDag-Erling Smørgrav SYSLOG_LEVEL_QUIET) 10536888a9beSDag-Erling Smørgrav log_change_level(--options.log_level); 10546888a9beSDag-Erling Smørgrav if (ch == 'v' && options.log_level < 10556888a9beSDag-Erling Smørgrav SYSLOG_LEVEL_DEBUG3) 10566888a9beSDag-Erling Smørgrav log_change_level(++options.log_level); 10576888a9beSDag-Erling Smørgrav snprintf(string, sizeof string, 1058*4f52dfbbSDag-Erling Smørgrav "%c%c [LogLevel %s]\r\n", 1059*4f52dfbbSDag-Erling Smørgrav efc->escape_char, ch, 10606888a9beSDag-Erling Smørgrav log_level_name(options.log_level)); 10616888a9beSDag-Erling Smørgrav buffer_append(berr, string, strlen(string)); 10626888a9beSDag-Erling Smørgrav continue; 10636888a9beSDag-Erling Smørgrav 1064b66f2d16SKris Kennaway case '&': 1065b15c8340SDag-Erling Smørgrav if (c && c->ctl_chan != -1) 1066d4af9e69SDag-Erling Smørgrav goto noescape; 1067b66f2d16SKris Kennaway /* 1068d4af9e69SDag-Erling Smørgrav * Detach the program (continue to serve 1069d4af9e69SDag-Erling Smørgrav * connections, but put in background and no 1070d4af9e69SDag-Erling Smørgrav * more new connections). 1071b66f2d16SKris Kennaway */ 1072ae1f160dSDag-Erling Smørgrav /* Restore tty modes. */ 1073e146993eSDag-Erling Smørgrav leave_raw_mode( 1074e146993eSDag-Erling Smørgrav options.request_tty == REQUEST_TTY_FORCE); 1075ae1f160dSDag-Erling Smørgrav 1076ae1f160dSDag-Erling Smørgrav /* Stop listening for new connections. */ 1077*4f52dfbbSDag-Erling Smørgrav channel_stop_listening(ssh); 1078ae1f160dSDag-Erling Smørgrav 1079ae1f160dSDag-Erling Smørgrav snprintf(string, sizeof string, 1080*4f52dfbbSDag-Erling Smørgrav "%c& [backgrounded]\n", efc->escape_char); 1081ae1f160dSDag-Erling Smørgrav buffer_append(berr, string, strlen(string)); 1082ae1f160dSDag-Erling Smørgrav 1083ae1f160dSDag-Erling Smørgrav /* Fork into background. */ 1084ae1f160dSDag-Erling Smørgrav pid = fork(); 1085ae1f160dSDag-Erling Smørgrav if (pid < 0) { 1086ae1f160dSDag-Erling Smørgrav error("fork: %.100s", strerror(errno)); 1087ae1f160dSDag-Erling Smørgrav continue; 1088ae1f160dSDag-Erling Smørgrav } 1089ae1f160dSDag-Erling Smørgrav if (pid != 0) { /* This is the parent. */ 1090ae1f160dSDag-Erling Smørgrav /* The parent just exits. */ 1091ae1f160dSDag-Erling Smørgrav exit(0); 1092ae1f160dSDag-Erling Smørgrav } 1093ae1f160dSDag-Erling Smørgrav /* The child continues serving connections. */ 1094ae1f160dSDag-Erling Smørgrav buffer_append(bin, "\004", 1); 1095ae1f160dSDag-Erling Smørgrav /* fake EOF on stdin */ 1096ae1f160dSDag-Erling Smørgrav return -1; 1097b66f2d16SKris Kennaway case '?': 1098*4f52dfbbSDag-Erling Smørgrav print_escape_help(berr, efc->escape_char, 10996888a9beSDag-Erling Smørgrav (c && c->ctl_chan != -1), 11006888a9beSDag-Erling Smørgrav log_is_on_stderr()); 1101b66f2d16SKris Kennaway continue; 1102b66f2d16SKris Kennaway 1103b66f2d16SKris Kennaway case '#': 1104d4af9e69SDag-Erling Smørgrav snprintf(string, sizeof string, "%c#\r\n", 1105*4f52dfbbSDag-Erling Smørgrav efc->escape_char); 1106b66f2d16SKris Kennaway buffer_append(berr, string, strlen(string)); 1107*4f52dfbbSDag-Erling Smørgrav s = channel_open_message(ssh); 1108b66f2d16SKris Kennaway buffer_append(berr, s, strlen(s)); 1109e4a9863fSDag-Erling Smørgrav free(s); 1110b66f2d16SKris Kennaway continue; 1111b66f2d16SKris Kennaway 1112545d5ecaSDag-Erling Smørgrav case 'C': 1113b15c8340SDag-Erling Smørgrav if (c && c->ctl_chan != -1) 1114cce7d346SDag-Erling Smørgrav goto noescape; 1115*4f52dfbbSDag-Erling Smørgrav process_cmdline(ssh); 1116545d5ecaSDag-Erling Smørgrav continue; 1117545d5ecaSDag-Erling Smørgrav 1118b66f2d16SKris Kennaway default: 1119*4f52dfbbSDag-Erling Smørgrav if (ch != efc->escape_char) { 1120*4f52dfbbSDag-Erling Smørgrav buffer_put_char(bin, efc->escape_char); 1121b66f2d16SKris Kennaway bytes++; 1122b66f2d16SKris Kennaway } 1123b66f2d16SKris Kennaway /* Escaped characters fall through here */ 1124b66f2d16SKris Kennaway break; 1125b66f2d16SKris Kennaway } 1126b66f2d16SKris Kennaway } else { 1127b66f2d16SKris Kennaway /* 1128d4af9e69SDag-Erling Smørgrav * The previous character was not an escape char. 1129d4af9e69SDag-Erling Smørgrav * Check if this is an escape. 1130b66f2d16SKris Kennaway */ 1131*4f52dfbbSDag-Erling Smørgrav if (last_was_cr && ch == efc->escape_char) { 1132d4af9e69SDag-Erling Smørgrav /* 1133d4af9e69SDag-Erling Smørgrav * It is. Set the flag and continue to 1134d4af9e69SDag-Erling Smørgrav * next character. 1135d4af9e69SDag-Erling Smørgrav */ 1136*4f52dfbbSDag-Erling Smørgrav efc->escape_pending = 1; 1137b66f2d16SKris Kennaway continue; 1138b66f2d16SKris Kennaway } 1139b66f2d16SKris Kennaway } 1140b66f2d16SKris Kennaway 1141b66f2d16SKris Kennaway /* 1142b66f2d16SKris Kennaway * Normal character. Record whether it was a newline, 1143b66f2d16SKris Kennaway * and append it to the buffer. 1144b66f2d16SKris Kennaway */ 1145b66f2d16SKris Kennaway last_was_cr = (ch == '\r' || ch == '\n'); 1146b66f2d16SKris Kennaway buffer_put_char(bin, ch); 1147b66f2d16SKris Kennaway bytes++; 1148b66f2d16SKris Kennaway } 1149b66f2d16SKris Kennaway return bytes; 1150b66f2d16SKris Kennaway } 1151b66f2d16SKris Kennaway 1152511b41d2SMark Murray /* 1153a04a10f8SKris Kennaway * Get packets from the connection input buffer, and process them as long as 1154a04a10f8SKris Kennaway * there are packets available. 1155a04a10f8SKris Kennaway * 1156a04a10f8SKris Kennaway * Any unknown packets received during the actual 1157a04a10f8SKris Kennaway * session cause the session to terminate. This is 1158a04a10f8SKris Kennaway * intended to make debugging easier since no 1159a04a10f8SKris Kennaway * confirmations are sent. Any compatible protocol 1160a04a10f8SKris Kennaway * extensions must be negotiated during the 1161a04a10f8SKris Kennaway * preparatory phase. 1162a04a10f8SKris Kennaway */ 1163a04a10f8SKris Kennaway 1164ae1f160dSDag-Erling Smørgrav static void 11651e8db6e2SBrian Feldman client_process_buffered_input_packets(void) 1166a04a10f8SKris Kennaway { 1167*4f52dfbbSDag-Erling Smørgrav ssh_dispatch_run_fatal(active_state, DISPATCH_NONBLOCK, &quit_pending); 1168a04a10f8SKris Kennaway } 1169a04a10f8SKris Kennaway 1170b66f2d16SKris Kennaway /* scan buf[] for '~' before sending data to the peer */ 1171b66f2d16SKris Kennaway 1172d4af9e69SDag-Erling Smørgrav /* Helper: allocate a new escape_filter_ctx and fill in its escape char */ 1173d4af9e69SDag-Erling Smørgrav void * 1174d4af9e69SDag-Erling Smørgrav client_new_escape_filter_ctx(int escape_char) 1175b66f2d16SKris Kennaway { 1176d4af9e69SDag-Erling Smørgrav struct escape_filter_ctx *ret; 1177d4af9e69SDag-Erling Smørgrav 11780a37d4a3SXin LI ret = xcalloc(1, sizeof(*ret)); 1179d4af9e69SDag-Erling Smørgrav ret->escape_pending = 0; 1180d4af9e69SDag-Erling Smørgrav ret->escape_char = escape_char; 1181d4af9e69SDag-Erling Smørgrav return (void *)ret; 1182d4af9e69SDag-Erling Smørgrav } 1183d4af9e69SDag-Erling Smørgrav 1184d4af9e69SDag-Erling Smørgrav /* Free the escape filter context on channel free */ 1185d4af9e69SDag-Erling Smørgrav void 1186*4f52dfbbSDag-Erling Smørgrav client_filter_cleanup(struct ssh *ssh, int cid, void *ctx) 1187d4af9e69SDag-Erling Smørgrav { 1188e4a9863fSDag-Erling Smørgrav free(ctx); 1189d4af9e69SDag-Erling Smørgrav } 1190d4af9e69SDag-Erling Smørgrav 1191d4af9e69SDag-Erling Smørgrav int 1192*4f52dfbbSDag-Erling Smørgrav client_simple_escape_filter(struct ssh *ssh, Channel *c, char *buf, int len) 1193d4af9e69SDag-Erling Smørgrav { 1194d4af9e69SDag-Erling Smørgrav if (c->extended_usage != CHAN_EXTENDED_WRITE) 1195d4af9e69SDag-Erling Smørgrav return 0; 1196d4af9e69SDag-Erling Smørgrav 1197*4f52dfbbSDag-Erling Smørgrav return process_escapes(ssh, c, c->input, c->output, c->extended, 1198d4af9e69SDag-Erling Smørgrav buf, len); 1199b66f2d16SKris Kennaway } 1200b66f2d16SKris Kennaway 1201ae1f160dSDag-Erling Smørgrav static void 1202*4f52dfbbSDag-Erling Smørgrav client_channel_closed(struct ssh *ssh, int id, void *arg) 12031e8db6e2SBrian Feldman { 1204*4f52dfbbSDag-Erling Smørgrav channel_cancel_cleanup(ssh, id); 12051e8db6e2SBrian Feldman session_closed = 1; 1206e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 12071e8db6e2SBrian Feldman } 12081e8db6e2SBrian Feldman 1209a04a10f8SKris Kennaway /* 1210511b41d2SMark Murray * Implements the interactive session with the server. This is called after 1211511b41d2SMark Murray * the user has been authenticated, and a command has been started on the 1212ae1f160dSDag-Erling Smørgrav * remote host. If escape_char != SSH_ESCAPECHAR_NONE, it is the character 1213ae1f160dSDag-Erling Smørgrav * used as an escape character for terminating or suspending the session. 1214511b41d2SMark Murray */ 1215511b41d2SMark Murray int 1216*4f52dfbbSDag-Erling Smørgrav client_loop(struct ssh *ssh, int have_pty, int escape_char_arg, 1217*4f52dfbbSDag-Erling Smørgrav int ssh2_chan_id) 1218511b41d2SMark Murray { 12191e8db6e2SBrian Feldman fd_set *readset = NULL, *writeset = NULL; 1220511b41d2SMark Murray double start_time, total_time; 1221acc1a9efSDag-Erling Smørgrav int r, max_fd = 0, max_fd2 = 0, len; 1222d4af9e69SDag-Erling Smørgrav u_int64_t ibytes, obytes; 1223d74d50a8SDag-Erling Smørgrav u_int nalloc = 0; 1224511b41d2SMark Murray char buf[100]; 1225511b41d2SMark Murray 1226511b41d2SMark Murray debug("Entering interactive session."); 1227511b41d2SMark Murray 1228acc1a9efSDag-Erling Smørgrav if (options.control_master && 1229acc1a9efSDag-Erling Smørgrav !option_clear_or_none(options.control_path)) { 1230acc1a9efSDag-Erling Smørgrav debug("pledge: id"); 1231076ad2f8SDag-Erling Smørgrav if (pledge("stdio rpath wpath cpath unix inet dns recvfd proc exec id tty", 1232acc1a9efSDag-Erling Smørgrav NULL) == -1) 1233acc1a9efSDag-Erling Smørgrav fatal("%s pledge(): %s", __func__, strerror(errno)); 1234acc1a9efSDag-Erling Smørgrav 1235acc1a9efSDag-Erling Smørgrav } else if (options.forward_x11 || options.permit_local_command) { 1236acc1a9efSDag-Erling Smørgrav debug("pledge: exec"); 1237acc1a9efSDag-Erling Smørgrav if (pledge("stdio rpath wpath cpath unix inet dns proc exec tty", 1238acc1a9efSDag-Erling Smørgrav NULL) == -1) 1239acc1a9efSDag-Erling Smørgrav fatal("%s pledge(): %s", __func__, strerror(errno)); 1240acc1a9efSDag-Erling Smørgrav 1241acc1a9efSDag-Erling Smørgrav } else if (options.update_hostkeys) { 1242acc1a9efSDag-Erling Smørgrav debug("pledge: filesystem full"); 1243acc1a9efSDag-Erling Smørgrav if (pledge("stdio rpath wpath cpath unix inet dns proc tty", 1244acc1a9efSDag-Erling Smørgrav NULL) == -1) 1245acc1a9efSDag-Erling Smørgrav fatal("%s pledge(): %s", __func__, strerror(errno)); 1246acc1a9efSDag-Erling Smørgrav 1247076ad2f8SDag-Erling Smørgrav } else if (!option_clear_or_none(options.proxy_command) || 1248076ad2f8SDag-Erling Smørgrav fork_after_authentication_flag) { 1249acc1a9efSDag-Erling Smørgrav debug("pledge: proc"); 1250acc1a9efSDag-Erling Smørgrav if (pledge("stdio cpath unix inet dns proc tty", NULL) == -1) 1251acc1a9efSDag-Erling Smørgrav fatal("%s pledge(): %s", __func__, strerror(errno)); 1252acc1a9efSDag-Erling Smørgrav 1253acc1a9efSDag-Erling Smørgrav } else { 1254acc1a9efSDag-Erling Smørgrav debug("pledge: network"); 1255*4f52dfbbSDag-Erling Smørgrav if (pledge("stdio unix inet dns proc tty", NULL) == -1) 1256acc1a9efSDag-Erling Smørgrav fatal("%s pledge(): %s", __func__, strerror(errno)); 1257acc1a9efSDag-Erling Smørgrav } 1258acc1a9efSDag-Erling Smørgrav 1259511b41d2SMark Murray start_time = get_current_time(); 1260511b41d2SMark Murray 1261511b41d2SMark Murray /* Initialize variables. */ 1262511b41d2SMark Murray last_was_cr = 1; 1263511b41d2SMark Murray exit_status = -1; 1264511b41d2SMark Murray connection_in = packet_get_connection_in(); 1265511b41d2SMark Murray connection_out = packet_get_connection_out(); 1266ca86bcf2SDag-Erling Smørgrav max_fd = MAXIMUM(connection_in, connection_out); 12671e8db6e2SBrian Feldman 1268511b41d2SMark Murray quit_pending = 0; 1269511b41d2SMark Murray 1270511b41d2SMark Murray /* Initialize buffers. */ 1271511b41d2SMark Murray buffer_init(&stderr_buffer); 1272511b41d2SMark Murray 1273a04a10f8SKris Kennaway client_init_dispatch(); 1274a04a10f8SKris Kennaway 1275d0c8c0bcSDag-Erling Smørgrav /* 1276d0c8c0bcSDag-Erling Smørgrav * Set signal handlers, (e.g. to restore non-blocking mode) 1277d0c8c0bcSDag-Erling Smørgrav * but don't overwrite SIG_IGN, matches behaviour from rsh(1) 1278d0c8c0bcSDag-Erling Smørgrav */ 12795e8dbd04SDag-Erling Smørgrav if (signal(SIGHUP, SIG_IGN) != SIG_IGN) 12805e8dbd04SDag-Erling Smørgrav signal(SIGHUP, signal_handler); 1281d0c8c0bcSDag-Erling Smørgrav if (signal(SIGINT, SIG_IGN) != SIG_IGN) 1282511b41d2SMark Murray signal(SIGINT, signal_handler); 1283d0c8c0bcSDag-Erling Smørgrav if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) 1284511b41d2SMark Murray signal(SIGQUIT, signal_handler); 1285d0c8c0bcSDag-Erling Smørgrav if (signal(SIGTERM, SIG_IGN) != SIG_IGN) 1286511b41d2SMark Murray signal(SIGTERM, signal_handler); 1287511b41d2SMark Murray signal(SIGWINCH, window_change_handler); 1288511b41d2SMark Murray 1289511b41d2SMark Murray if (have_pty) 1290e146993eSDag-Erling Smørgrav enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 1291511b41d2SMark Murray 12921e8db6e2SBrian Feldman session_ident = ssh2_chan_id; 1293e146993eSDag-Erling Smørgrav if (session_ident != -1) { 1294e146993eSDag-Erling Smørgrav if (escape_char_arg != SSH_ESCAPECHAR_NONE) { 1295*4f52dfbbSDag-Erling Smørgrav channel_register_filter(ssh, session_ident, 1296d4af9e69SDag-Erling Smørgrav client_simple_escape_filter, NULL, 1297d4af9e69SDag-Erling Smørgrav client_filter_cleanup, 1298e146993eSDag-Erling Smørgrav client_new_escape_filter_ctx( 1299e146993eSDag-Erling Smørgrav escape_char_arg)); 1300e146993eSDag-Erling Smørgrav } 1301*4f52dfbbSDag-Erling Smørgrav channel_register_cleanup(ssh, session_ident, 1302021d409fSDag-Erling Smørgrav client_channel_closed, 0); 1303e146993eSDag-Erling Smørgrav } 1304b66f2d16SKris Kennaway 1305511b41d2SMark Murray /* Main loop of the client for the interactive session mode. */ 1306511b41d2SMark Murray while (!quit_pending) { 1307511b41d2SMark Murray 1308511b41d2SMark Murray /* Process buffered packets sent by the server. */ 1309511b41d2SMark Murray client_process_buffered_input_packets(); 1310511b41d2SMark Murray 1311*4f52dfbbSDag-Erling Smørgrav if (session_closed && !channel_still_open(ssh)) 1312a04a10f8SKris Kennaway break; 1313a04a10f8SKris Kennaway 1314*4f52dfbbSDag-Erling Smørgrav if (ssh_packet_is_rekeying(ssh)) { 13151e8db6e2SBrian Feldman debug("rekeying in progress"); 1316acc1a9efSDag-Erling Smørgrav } else if (need_rekeying) { 1317acc1a9efSDag-Erling Smørgrav /* manual rekey request */ 1318acc1a9efSDag-Erling Smørgrav debug("need rekeying"); 1319*4f52dfbbSDag-Erling Smørgrav if ((r = kex_start_rekex(ssh)) != 0) 1320acc1a9efSDag-Erling Smørgrav fatal("%s: kex_start_rekex: %s", __func__, 1321acc1a9efSDag-Erling Smørgrav ssh_err(r)); 1322acc1a9efSDag-Erling Smørgrav need_rekeying = 0; 13231e8db6e2SBrian Feldman } else { 1324511b41d2SMark Murray /* 13251e8db6e2SBrian Feldman * Make packets from buffered channel data, and 13261e8db6e2SBrian Feldman * enqueue them for sending to the server. 1327511b41d2SMark Murray */ 1328511b41d2SMark Murray if (packet_not_very_much_data_to_write()) 1329*4f52dfbbSDag-Erling Smørgrav channel_output_poll(ssh); 1330511b41d2SMark Murray 1331511b41d2SMark Murray /* 13321e8db6e2SBrian Feldman * Check if the window size has changed, and buffer a 13331e8db6e2SBrian Feldman * message about it to the server if so. 1334511b41d2SMark Murray */ 1335*4f52dfbbSDag-Erling Smørgrav client_check_window_change(ssh); 1336511b41d2SMark Murray 1337511b41d2SMark Murray if (quit_pending) 1338511b41d2SMark Murray break; 13391e8db6e2SBrian Feldman } 1340511b41d2SMark Murray /* 1341511b41d2SMark Murray * Wait until we have something to do (something becomes 1342511b41d2SMark Murray * available on one of the descriptors). 1343511b41d2SMark Murray */ 1344ae1f160dSDag-Erling Smørgrav max_fd2 = max_fd; 1345*4f52dfbbSDag-Erling Smørgrav client_wait_until_can_do_something(ssh, &readset, &writeset, 1346*4f52dfbbSDag-Erling Smørgrav &max_fd2, &nalloc, ssh_packet_is_rekeying(ssh)); 1347511b41d2SMark Murray 1348511b41d2SMark Murray if (quit_pending) 1349511b41d2SMark Murray break; 1350511b41d2SMark Murray 13511e8db6e2SBrian Feldman /* Do channel operations unless rekeying in progress. */ 1352*4f52dfbbSDag-Erling Smørgrav if (!ssh_packet_is_rekeying(ssh)) 1353*4f52dfbbSDag-Erling Smørgrav channel_after_select(ssh, readset, writeset); 1354511b41d2SMark Murray 1355a04a10f8SKris Kennaway /* Buffer input from the connection. */ 13561e8db6e2SBrian Feldman client_process_net_input(readset); 1357511b41d2SMark Murray 1358a04a10f8SKris Kennaway if (quit_pending) 1359a04a10f8SKris Kennaway break; 1360a04a10f8SKris Kennaway 1361d4af9e69SDag-Erling Smørgrav /* 1362d4af9e69SDag-Erling Smørgrav * Send as much buffered packet data as possible to the 1363d4af9e69SDag-Erling Smørgrav * sender. 1364d4af9e69SDag-Erling Smørgrav */ 13651e8db6e2SBrian Feldman if (FD_ISSET(connection_out, writeset)) 1366511b41d2SMark Murray packet_write_poll(); 1367e2f6069cSDag-Erling Smørgrav 1368e2f6069cSDag-Erling Smørgrav /* 1369e2f6069cSDag-Erling Smørgrav * If we are a backgrounded control master, and the 1370e2f6069cSDag-Erling Smørgrav * timeout has expired without any active client 1371e2f6069cSDag-Erling Smørgrav * connections, then quit. 1372e2f6069cSDag-Erling Smørgrav */ 1373e2f6069cSDag-Erling Smørgrav if (control_persist_exit_time > 0) { 1374e4a9863fSDag-Erling Smørgrav if (monotime() >= control_persist_exit_time) { 1375e2f6069cSDag-Erling Smørgrav debug("ControlPersist timeout expired"); 1376e2f6069cSDag-Erling Smørgrav break; 1377e2f6069cSDag-Erling Smørgrav } 1378e2f6069cSDag-Erling Smørgrav } 1379511b41d2SMark Murray } 1380e4a9863fSDag-Erling Smørgrav free(readset); 1381e4a9863fSDag-Erling Smørgrav free(writeset); 1382511b41d2SMark Murray 1383511b41d2SMark Murray /* Terminate the session. */ 1384511b41d2SMark Murray 1385511b41d2SMark Murray /* Stop watching for window change. */ 1386511b41d2SMark Murray signal(SIGWINCH, SIG_DFL); 1387511b41d2SMark Murray 13887aee6ffeSDag-Erling Smørgrav packet_start(SSH2_MSG_DISCONNECT); 13897aee6ffeSDag-Erling Smørgrav packet_put_int(SSH2_DISCONNECT_BY_APPLICATION); 13907aee6ffeSDag-Erling Smørgrav packet_put_cstring("disconnected by user"); 13918ad9b54aSDag-Erling Smørgrav packet_put_cstring(""); /* language tag */ 13927aee6ffeSDag-Erling Smørgrav packet_send(); 13937aee6ffeSDag-Erling Smørgrav packet_write_wait(); 13947aee6ffeSDag-Erling Smørgrav 1395*4f52dfbbSDag-Erling Smørgrav channel_free_all(ssh); 1396ae1f160dSDag-Erling Smørgrav 1397ae1f160dSDag-Erling Smørgrav if (have_pty) 1398e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 1399ae1f160dSDag-Erling Smørgrav 1400ae1f160dSDag-Erling Smørgrav /* restore blocking io */ 1401ae1f160dSDag-Erling Smørgrav if (!isatty(fileno(stdin))) 1402ae1f160dSDag-Erling Smørgrav unset_nonblock(fileno(stdin)); 1403ae1f160dSDag-Erling Smørgrav if (!isatty(fileno(stdout))) 1404ae1f160dSDag-Erling Smørgrav unset_nonblock(fileno(stdout)); 1405ae1f160dSDag-Erling Smørgrav if (!isatty(fileno(stderr))) 1406ae1f160dSDag-Erling Smørgrav unset_nonblock(fileno(stderr)); 1407ae1f160dSDag-Erling Smørgrav 1408efcad6b7SDag-Erling Smørgrav /* 1409efcad6b7SDag-Erling Smørgrav * If there was no shell or command requested, there will be no remote 1410efcad6b7SDag-Erling Smørgrav * exit status to be returned. In that case, clear error code if the 1411efcad6b7SDag-Erling Smørgrav * connection was deliberately terminated at this end. 1412efcad6b7SDag-Erling Smørgrav */ 1413efcad6b7SDag-Erling Smørgrav if (no_shell_flag && received_signal == SIGTERM) { 1414efcad6b7SDag-Erling Smørgrav received_signal = 0; 1415efcad6b7SDag-Erling Smørgrav exit_status = 0; 1416ae1f160dSDag-Erling Smørgrav } 1417511b41d2SMark Murray 1418*4f52dfbbSDag-Erling Smørgrav if (received_signal) { 1419*4f52dfbbSDag-Erling Smørgrav verbose("Killed by signal %d.", (int) received_signal); 1420*4f52dfbbSDag-Erling Smørgrav cleanup_exit(0); 1421*4f52dfbbSDag-Erling Smørgrav } 1422efcad6b7SDag-Erling Smørgrav 1423511b41d2SMark Murray /* 1424511b41d2SMark Murray * In interactive mode (with pseudo tty) display a message indicating 1425511b41d2SMark Murray * that the connection has been closed. 1426511b41d2SMark Murray */ 1427511b41d2SMark Murray if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) { 1428d4af9e69SDag-Erling Smørgrav snprintf(buf, sizeof buf, 1429d4af9e69SDag-Erling Smørgrav "Connection to %.64s closed.\r\n", host); 1430511b41d2SMark Murray buffer_append(&stderr_buffer, buf, strlen(buf)); 1431511b41d2SMark Murray } 1432ae1f160dSDag-Erling Smørgrav 1433511b41d2SMark Murray /* Output any buffered data for stderr. */ 14344a421b63SDag-Erling Smørgrav if (buffer_len(&stderr_buffer) > 0) { 14354a421b63SDag-Erling Smørgrav len = atomicio(vwrite, fileno(stderr), 14364a421b63SDag-Erling Smørgrav buffer_ptr(&stderr_buffer), buffer_len(&stderr_buffer)); 14374a421b63SDag-Erling Smørgrav if (len < 0 || (u_int)len != buffer_len(&stderr_buffer)) 1438511b41d2SMark Murray error("Write failed flushing stderr buffer."); 14394a421b63SDag-Erling Smørgrav else 1440511b41d2SMark Murray buffer_consume(&stderr_buffer, len); 1441511b41d2SMark Murray } 1442511b41d2SMark Murray 1443511b41d2SMark Murray /* Clear and free any buffers. */ 1444acc1a9efSDag-Erling Smørgrav explicit_bzero(buf, sizeof(buf)); 1445511b41d2SMark Murray buffer_free(&stderr_buffer); 1446511b41d2SMark Murray 1447511b41d2SMark Murray /* Report bytes transferred, and transfer rates. */ 1448511b41d2SMark Murray total_time = get_current_time() - start_time; 1449bc5531deSDag-Erling Smørgrav packet_get_bytes(&ibytes, &obytes); 1450d4af9e69SDag-Erling Smørgrav verbose("Transferred: sent %llu, received %llu bytes, in %.1f seconds", 14514a421b63SDag-Erling Smørgrav (unsigned long long)obytes, (unsigned long long)ibytes, total_time); 1452511b41d2SMark Murray if (total_time > 0) 1453d4af9e69SDag-Erling Smørgrav verbose("Bytes per second: sent %.1f, received %.1f", 1454d4af9e69SDag-Erling Smørgrav obytes / total_time, ibytes / total_time); 1455511b41d2SMark Murray /* Return the exit status of the program. */ 1456511b41d2SMark Murray debug("Exit status %d", exit_status); 1457511b41d2SMark Murray return exit_status; 1458511b41d2SMark Murray } 1459a04a10f8SKris Kennaway 1460a04a10f8SKris Kennaway /*********/ 1461a04a10f8SKris Kennaway 1462ae1f160dSDag-Erling Smørgrav static Channel * 1463*4f52dfbbSDag-Erling Smørgrav client_request_forwarded_tcpip(struct ssh *ssh, const char *request_type, 1464*4f52dfbbSDag-Erling Smørgrav int rchan, u_int rwindow, u_int rmaxpack) 14651e8db6e2SBrian Feldman { 14661e8db6e2SBrian Feldman Channel *c = NULL; 1467ca86bcf2SDag-Erling Smørgrav struct sshbuf *b = NULL; 14681e8db6e2SBrian Feldman char *listen_address, *originator_address; 1469cce7d346SDag-Erling Smørgrav u_short listen_port, originator_port; 1470ca86bcf2SDag-Erling Smørgrav int r; 14711e8db6e2SBrian Feldman 14721e8db6e2SBrian Feldman /* Get rest of the packet */ 14731e8db6e2SBrian Feldman listen_address = packet_get_string(NULL); 14741e8db6e2SBrian Feldman listen_port = packet_get_int(); 14751e8db6e2SBrian Feldman originator_address = packet_get_string(NULL); 14761e8db6e2SBrian Feldman originator_port = packet_get_int(); 1477ae1f160dSDag-Erling Smørgrav packet_check_eom(); 14781e8db6e2SBrian Feldman 1479a0ee8cc6SDag-Erling Smørgrav debug("%s: listen %s port %d, originator %s port %d", __func__, 1480a0ee8cc6SDag-Erling Smørgrav listen_address, listen_port, originator_address, originator_port); 14811e8db6e2SBrian Feldman 1482*4f52dfbbSDag-Erling Smørgrav c = channel_connect_by_listen_address(ssh, listen_address, listen_port, 1483d4af9e69SDag-Erling Smørgrav "forwarded-tcpip", originator_address); 1484d4af9e69SDag-Erling Smørgrav 1485ca86bcf2SDag-Erling Smørgrav if (c != NULL && c->type == SSH_CHANNEL_MUX_CLIENT) { 1486ca86bcf2SDag-Erling Smørgrav if ((b = sshbuf_new()) == NULL) { 1487ca86bcf2SDag-Erling Smørgrav error("%s: alloc reply", __func__); 1488ca86bcf2SDag-Erling Smørgrav goto out; 1489ca86bcf2SDag-Erling Smørgrav } 1490ca86bcf2SDag-Erling Smørgrav /* reconstruct and send to muxclient */ 1491ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_put_u8(b, 0)) != 0 || /* padlen */ 1492ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u8(b, SSH2_MSG_CHANNEL_OPEN)) != 0 || 1493ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_cstring(b, request_type)) != 0 || 1494ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, rchan)) != 0 || 1495ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, rwindow)) != 0 || 1496ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, rmaxpack)) != 0 || 1497ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_cstring(b, listen_address)) != 0 || 1498ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, listen_port)) != 0 || 1499ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_cstring(b, originator_address)) != 0 || 1500ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, originator_port)) != 0 || 1501*4f52dfbbSDag-Erling Smørgrav (r = sshbuf_put_stringb(c->output, b)) != 0) { 1502ca86bcf2SDag-Erling Smørgrav error("%s: compose for muxclient %s", __func__, 1503ca86bcf2SDag-Erling Smørgrav ssh_err(r)); 1504ca86bcf2SDag-Erling Smørgrav goto out; 1505ca86bcf2SDag-Erling Smørgrav } 1506ca86bcf2SDag-Erling Smørgrav } 1507ca86bcf2SDag-Erling Smørgrav 1508ca86bcf2SDag-Erling Smørgrav out: 1509ca86bcf2SDag-Erling Smørgrav sshbuf_free(b); 1510e4a9863fSDag-Erling Smørgrav free(originator_address); 1511e4a9863fSDag-Erling Smørgrav free(listen_address); 15121e8db6e2SBrian Feldman return c; 15131e8db6e2SBrian Feldman } 15141e8db6e2SBrian Feldman 1515ae1f160dSDag-Erling Smørgrav static Channel * 1516*4f52dfbbSDag-Erling Smørgrav client_request_forwarded_streamlocal(struct ssh *ssh, 1517*4f52dfbbSDag-Erling Smørgrav const char *request_type, int rchan) 1518a0ee8cc6SDag-Erling Smørgrav { 1519a0ee8cc6SDag-Erling Smørgrav Channel *c = NULL; 1520a0ee8cc6SDag-Erling Smørgrav char *listen_path; 1521a0ee8cc6SDag-Erling Smørgrav 1522a0ee8cc6SDag-Erling Smørgrav /* Get the remote path. */ 1523a0ee8cc6SDag-Erling Smørgrav listen_path = packet_get_string(NULL); 1524a0ee8cc6SDag-Erling Smørgrav /* XXX: Skip reserved field for now. */ 1525a0ee8cc6SDag-Erling Smørgrav if (packet_get_string_ptr(NULL) == NULL) 1526a0ee8cc6SDag-Erling Smørgrav fatal("%s: packet_get_string_ptr failed", __func__); 1527a0ee8cc6SDag-Erling Smørgrav packet_check_eom(); 1528a0ee8cc6SDag-Erling Smørgrav 1529a0ee8cc6SDag-Erling Smørgrav debug("%s: %s", __func__, listen_path); 1530a0ee8cc6SDag-Erling Smørgrav 1531*4f52dfbbSDag-Erling Smørgrav c = channel_connect_by_listen_path(ssh, listen_path, 1532a0ee8cc6SDag-Erling Smørgrav "forwarded-streamlocal@openssh.com", "forwarded-streamlocal"); 1533a0ee8cc6SDag-Erling Smørgrav free(listen_path); 1534a0ee8cc6SDag-Erling Smørgrav return c; 1535a0ee8cc6SDag-Erling Smørgrav } 1536a0ee8cc6SDag-Erling Smørgrav 1537a0ee8cc6SDag-Erling Smørgrav static Channel * 1538*4f52dfbbSDag-Erling Smørgrav client_request_x11(struct ssh *ssh, const char *request_type, int rchan) 15391e8db6e2SBrian Feldman { 15401e8db6e2SBrian Feldman Channel *c = NULL; 15411e8db6e2SBrian Feldman char *originator; 1542cce7d346SDag-Erling Smørgrav u_short originator_port; 1543ae1f160dSDag-Erling Smørgrav int sock; 15441e8db6e2SBrian Feldman 15451e8db6e2SBrian Feldman if (!options.forward_x11) { 15461e8db6e2SBrian Feldman error("Warning: ssh server tried X11 forwarding."); 1547d4af9e69SDag-Erling Smørgrav error("Warning: this is probably a break-in attempt by a " 1548d4af9e69SDag-Erling Smørgrav "malicious server."); 15491e8db6e2SBrian Feldman return NULL; 15501e8db6e2SBrian Feldman } 1551557f75e5SDag-Erling Smørgrav if (x11_refuse_time != 0 && (u_int)monotime() >= x11_refuse_time) { 1552e2f6069cSDag-Erling Smørgrav verbose("Rejected X11 connection after ForwardX11Timeout " 1553e2f6069cSDag-Erling Smørgrav "expired"); 1554e2f6069cSDag-Erling Smørgrav return NULL; 1555e2f6069cSDag-Erling Smørgrav } 15561e8db6e2SBrian Feldman originator = packet_get_string(NULL); 15571e8db6e2SBrian Feldman if (datafellows & SSH_BUG_X11FWD) { 15581e8db6e2SBrian Feldman debug2("buggy server: x11 request w/o originator_port"); 15591e8db6e2SBrian Feldman originator_port = 0; 15601e8db6e2SBrian Feldman } else { 15611e8db6e2SBrian Feldman originator_port = packet_get_int(); 15621e8db6e2SBrian Feldman } 1563ae1f160dSDag-Erling Smørgrav packet_check_eom(); 15641e8db6e2SBrian Feldman /* XXX check permission */ 15651e8db6e2SBrian Feldman debug("client_request_x11: request from %s %d", originator, 15661e8db6e2SBrian Feldman originator_port); 1567e4a9863fSDag-Erling Smørgrav free(originator); 1568*4f52dfbbSDag-Erling Smørgrav sock = x11_connect_display(ssh); 1569ae1f160dSDag-Erling Smørgrav if (sock < 0) 1570ae1f160dSDag-Erling Smørgrav return NULL; 1571*4f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, "x11", 157260c59fadSDag-Erling Smørgrav SSH_CHANNEL_X11_OPEN, sock, sock, -1, 157360c59fadSDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "x11", 1); 1574ae1f160dSDag-Erling Smørgrav c->force_drain = 1; 15751e8db6e2SBrian Feldman return c; 15761e8db6e2SBrian Feldman } 15771e8db6e2SBrian Feldman 1578ae1f160dSDag-Erling Smørgrav static Channel * 1579*4f52dfbbSDag-Erling Smørgrav client_request_agent(struct ssh *ssh, const char *request_type, int rchan) 15801e8db6e2SBrian Feldman { 15811e8db6e2SBrian Feldman Channel *c = NULL; 1582bc5531deSDag-Erling Smørgrav int r, sock; 15831e8db6e2SBrian Feldman 15841e8db6e2SBrian Feldman if (!options.forward_agent) { 15851e8db6e2SBrian Feldman error("Warning: ssh server tried agent forwarding."); 1586d4af9e69SDag-Erling Smørgrav error("Warning: this is probably a break-in attempt by a " 1587d4af9e69SDag-Erling Smørgrav "malicious server."); 15881e8db6e2SBrian Feldman return NULL; 15891e8db6e2SBrian Feldman } 1590bc5531deSDag-Erling Smørgrav if ((r = ssh_get_authentication_socket(&sock)) != 0) { 1591bc5531deSDag-Erling Smørgrav if (r != SSH_ERR_AGENT_NOT_PRESENT) 1592bc5531deSDag-Erling Smørgrav debug("%s: ssh_get_authentication_socket: %s", 1593bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 1594ae1f160dSDag-Erling Smørgrav return NULL; 1595bc5531deSDag-Erling Smørgrav } 1596*4f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, "authentication agent connection", 15971e8db6e2SBrian Feldman SSH_CHANNEL_OPEN, sock, sock, -1, 1598e3bd730fSBryan Drewery CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, 159989986192SBrooks Davis "authentication agent connection", 1); 1600ae1f160dSDag-Erling Smørgrav c->force_drain = 1; 16011e8db6e2SBrian Feldman return c; 16021e8db6e2SBrian Feldman } 16031e8db6e2SBrian Feldman 1604d4af9e69SDag-Erling Smørgrav int 1605*4f52dfbbSDag-Erling Smørgrav client_request_tun_fwd(struct ssh *ssh, int tun_mode, 1606*4f52dfbbSDag-Erling Smørgrav int local_tun, int remote_tun) 1607d4af9e69SDag-Erling Smørgrav { 1608d4af9e69SDag-Erling Smørgrav Channel *c; 1609d4af9e69SDag-Erling Smørgrav int fd; 1610d4af9e69SDag-Erling Smørgrav 1611d4af9e69SDag-Erling Smørgrav if (tun_mode == SSH_TUNMODE_NO) 1612d4af9e69SDag-Erling Smørgrav return 0; 1613d4af9e69SDag-Erling Smørgrav 1614d4af9e69SDag-Erling Smørgrav debug("Requesting tun unit %d in mode %d", local_tun, tun_mode); 1615d4af9e69SDag-Erling Smørgrav 1616d4af9e69SDag-Erling Smørgrav /* Open local tunnel device */ 1617d4af9e69SDag-Erling Smørgrav if ((fd = tun_open(local_tun, tun_mode)) == -1) { 1618d4af9e69SDag-Erling Smørgrav error("Tunnel device open failed."); 1619d4af9e69SDag-Erling Smørgrav return -1; 1620d4af9e69SDag-Erling Smørgrav } 1621d4af9e69SDag-Erling Smørgrav 1622*4f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, "tun", SSH_CHANNEL_OPENING, fd, fd, -1, 162360c59fadSDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1); 1624d4af9e69SDag-Erling Smørgrav c->datagram = 1; 1625d4af9e69SDag-Erling Smørgrav 1626d4af9e69SDag-Erling Smørgrav #if defined(SSH_TUN_FILTER) 1627d4af9e69SDag-Erling Smørgrav if (options.tun_open == SSH_TUNMODE_POINTOPOINT) 1628*4f52dfbbSDag-Erling Smørgrav channel_register_filter(ssh, c->self, sys_tun_infilter, 1629d4af9e69SDag-Erling Smørgrav sys_tun_outfilter, NULL, NULL); 1630d4af9e69SDag-Erling Smørgrav #endif 1631d4af9e69SDag-Erling Smørgrav 1632d4af9e69SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_OPEN); 1633d4af9e69SDag-Erling Smørgrav packet_put_cstring("tun@openssh.com"); 1634d4af9e69SDag-Erling Smørgrav packet_put_int(c->self); 1635d4af9e69SDag-Erling Smørgrav packet_put_int(c->local_window_max); 1636d4af9e69SDag-Erling Smørgrav packet_put_int(c->local_maxpacket); 1637d4af9e69SDag-Erling Smørgrav packet_put_int(tun_mode); 1638d4af9e69SDag-Erling Smørgrav packet_put_int(remote_tun); 1639d4af9e69SDag-Erling Smørgrav packet_send(); 1640d4af9e69SDag-Erling Smørgrav 1641d4af9e69SDag-Erling Smørgrav return 0; 1642d4af9e69SDag-Erling Smørgrav } 1643d4af9e69SDag-Erling Smørgrav 1644a04a10f8SKris Kennaway /* XXXX move to generic input handler */ 1645bc5531deSDag-Erling Smørgrav static int 1646*4f52dfbbSDag-Erling Smørgrav client_input_channel_open(int type, u_int32_t seq, struct ssh *ssh) 1647a04a10f8SKris Kennaway { 1648a04a10f8SKris Kennaway Channel *c = NULL; 1649a04a10f8SKris Kennaway char *ctype; 1650a04a10f8SKris Kennaway int rchan; 1651ee21a45fSDag-Erling Smørgrav u_int rmaxpack, rwindow, len; 1652a04a10f8SKris Kennaway 1653a04a10f8SKris Kennaway ctype = packet_get_string(&len); 1654a04a10f8SKris Kennaway rchan = packet_get_int(); 1655a04a10f8SKris Kennaway rwindow = packet_get_int(); 1656a04a10f8SKris Kennaway rmaxpack = packet_get_int(); 1657a04a10f8SKris Kennaway 1658a04a10f8SKris Kennaway debug("client_input_channel_open: ctype %s rchan %d win %d max %d", 1659a04a10f8SKris Kennaway ctype, rchan, rwindow, rmaxpack); 1660a04a10f8SKris Kennaway 16611e8db6e2SBrian Feldman if (strcmp(ctype, "forwarded-tcpip") == 0) { 1662*4f52dfbbSDag-Erling Smørgrav c = client_request_forwarded_tcpip(ssh, ctype, rchan, rwindow, 1663ca86bcf2SDag-Erling Smørgrav rmaxpack); 1664a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(ctype, "forwarded-streamlocal@openssh.com") == 0) { 1665*4f52dfbbSDag-Erling Smørgrav c = client_request_forwarded_streamlocal(ssh, ctype, rchan); 16661e8db6e2SBrian Feldman } else if (strcmp(ctype, "x11") == 0) { 1667*4f52dfbbSDag-Erling Smørgrav c = client_request_x11(ssh, ctype, rchan); 16681e8db6e2SBrian Feldman } else if (strcmp(ctype, "auth-agent@openssh.com") == 0) { 1669*4f52dfbbSDag-Erling Smørgrav c = client_request_agent(ssh, ctype, rchan); 1670a04a10f8SKris Kennaway } 1671ca86bcf2SDag-Erling Smørgrav if (c != NULL && c->type == SSH_CHANNEL_MUX_CLIENT) { 1672ca86bcf2SDag-Erling Smørgrav debug3("proxied to downstream: %s", ctype); 1673ca86bcf2SDag-Erling Smørgrav } else if (c != NULL) { 1674a04a10f8SKris Kennaway debug("confirm %s", ctype); 1675a04a10f8SKris Kennaway c->remote_id = rchan; 1676*4f52dfbbSDag-Erling Smørgrav c->have_remote_id = 1; 1677a04a10f8SKris Kennaway c->remote_window = rwindow; 1678a04a10f8SKris Kennaway c->remote_maxpacket = rmaxpack; 1679ae1f160dSDag-Erling Smørgrav if (c->type != SSH_CHANNEL_CONNECTING) { 1680a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); 1681a04a10f8SKris Kennaway packet_put_int(c->remote_id); 1682a04a10f8SKris Kennaway packet_put_int(c->self); 1683a04a10f8SKris Kennaway packet_put_int(c->local_window); 1684a04a10f8SKris Kennaway packet_put_int(c->local_maxpacket); 1685a04a10f8SKris Kennaway packet_send(); 1686ae1f160dSDag-Erling Smørgrav } 1687a04a10f8SKris Kennaway } else { 1688a04a10f8SKris Kennaway debug("failure %s", ctype); 1689a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); 1690a04a10f8SKris Kennaway packet_put_int(rchan); 1691a04a10f8SKris Kennaway packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED); 1692ae1f160dSDag-Erling Smørgrav if (!(datafellows & SSH_BUG_OPENFAILURE)) { 1693ae1f160dSDag-Erling Smørgrav packet_put_cstring("open failed"); 1694a04a10f8SKris Kennaway packet_put_cstring(""); 1695ae1f160dSDag-Erling Smørgrav } 1696a04a10f8SKris Kennaway packet_send(); 1697a04a10f8SKris Kennaway } 1698e4a9863fSDag-Erling Smørgrav free(ctype); 1699bc5531deSDag-Erling Smørgrav return 0; 1700a04a10f8SKris Kennaway } 1701bc5531deSDag-Erling Smørgrav 1702bc5531deSDag-Erling Smørgrav static int 1703*4f52dfbbSDag-Erling Smørgrav client_input_channel_req(int type, u_int32_t seq, struct ssh *ssh) 17041e8db6e2SBrian Feldman { 17051e8db6e2SBrian Feldman Channel *c = NULL; 1706d74d50a8SDag-Erling Smørgrav int exitval, id, reply, success = 0; 17071e8db6e2SBrian Feldman char *rtype; 17081e8db6e2SBrian Feldman 17091e8db6e2SBrian Feldman id = packet_get_int(); 1710*4f52dfbbSDag-Erling Smørgrav c = channel_lookup(ssh, id); 1711*4f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 1712ca86bcf2SDag-Erling Smørgrav return 0; 17131e8db6e2SBrian Feldman rtype = packet_get_string(NULL); 17141e8db6e2SBrian Feldman reply = packet_get_char(); 17151e8db6e2SBrian Feldman 17161e8db6e2SBrian Feldman debug("client_input_channel_req: channel %d rtype %s reply %d", 17171e8db6e2SBrian Feldman id, rtype, reply); 17181e8db6e2SBrian Feldman 1719d74d50a8SDag-Erling Smørgrav if (id == -1) { 1720d74d50a8SDag-Erling Smørgrav error("client_input_channel_req: request for channel -1"); 1721ca86bcf2SDag-Erling Smørgrav } else if (c == NULL) { 1722d4af9e69SDag-Erling Smørgrav error("client_input_channel_req: channel %d: " 1723d4af9e69SDag-Erling Smørgrav "unknown channel", id); 1724d4af9e69SDag-Erling Smørgrav } else if (strcmp(rtype, "eow@openssh.com") == 0) { 1725d4af9e69SDag-Erling Smørgrav packet_check_eom(); 1726*4f52dfbbSDag-Erling Smørgrav chan_rcvd_eow(ssh, c); 17271e8db6e2SBrian Feldman } else if (strcmp(rtype, "exit-status") == 0) { 1728d74d50a8SDag-Erling Smørgrav exitval = packet_get_int(); 1729b15c8340SDag-Erling Smørgrav if (c->ctl_chan != -1) { 1730*4f52dfbbSDag-Erling Smørgrav mux_exit_message(ssh, c, exitval); 1731b15c8340SDag-Erling Smørgrav success = 1; 1732b15c8340SDag-Erling Smørgrav } else if (id == session_ident) { 1733b15c8340SDag-Erling Smørgrav /* Record exit value of local session */ 17341e8db6e2SBrian Feldman success = 1; 1735d74d50a8SDag-Erling Smørgrav exit_status = exitval; 1736d74d50a8SDag-Erling Smørgrav } else { 1737b15c8340SDag-Erling Smørgrav /* Probably for a mux channel that has already closed */ 1738b15c8340SDag-Erling Smørgrav debug("%s: no sink for exit-status on channel %d", 1739b15c8340SDag-Erling Smørgrav __func__, id); 1740d74d50a8SDag-Erling Smørgrav } 1741ae1f160dSDag-Erling Smørgrav packet_check_eom(); 17421e8db6e2SBrian Feldman } 1743a0ee8cc6SDag-Erling Smørgrav if (reply && c != NULL && !(c->flags & CHAN_CLOSE_SENT)) { 1744*4f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id) 1745*4f52dfbbSDag-Erling Smørgrav fatal("%s: channel %d: no remote_id", 1746*4f52dfbbSDag-Erling Smørgrav __func__, c->self); 17471e8db6e2SBrian Feldman packet_start(success ? 17481e8db6e2SBrian Feldman SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); 1749cce7d346SDag-Erling Smørgrav packet_put_int(c->remote_id); 17501e8db6e2SBrian Feldman packet_send(); 17511e8db6e2SBrian Feldman } 1752e4a9863fSDag-Erling Smørgrav free(rtype); 1753bc5531deSDag-Erling Smørgrav return 0; 17541e8db6e2SBrian Feldman } 1755bc5531deSDag-Erling Smørgrav 1756bc5531deSDag-Erling Smørgrav struct hostkeys_update_ctx { 1757bc5531deSDag-Erling Smørgrav /* The hostname and (optionally) IP address string for the server */ 1758bc5531deSDag-Erling Smørgrav char *host_str, *ip_str; 1759bc5531deSDag-Erling Smørgrav 1760bc5531deSDag-Erling Smørgrav /* 1761bc5531deSDag-Erling Smørgrav * Keys received from the server and a flag for each indicating 1762bc5531deSDag-Erling Smørgrav * whether they already exist in known_hosts. 1763bc5531deSDag-Erling Smørgrav * keys_seen is filled in by hostkeys_find() and later (for new 1764bc5531deSDag-Erling Smørgrav * keys) by client_global_hostkeys_private_confirm(). 1765bc5531deSDag-Erling Smørgrav */ 1766bc5531deSDag-Erling Smørgrav struct sshkey **keys; 1767bc5531deSDag-Erling Smørgrav int *keys_seen; 1768*4f52dfbbSDag-Erling Smørgrav size_t nkeys, nnew; 1769bc5531deSDag-Erling Smørgrav 1770bc5531deSDag-Erling Smørgrav /* 1771bc5531deSDag-Erling Smørgrav * Keys that are in known_hosts, but were not present in the update 1772bc5531deSDag-Erling Smørgrav * from the server (i.e. scheduled to be deleted). 1773bc5531deSDag-Erling Smørgrav * Filled in by hostkeys_find(). 1774bc5531deSDag-Erling Smørgrav */ 1775bc5531deSDag-Erling Smørgrav struct sshkey **old_keys; 1776bc5531deSDag-Erling Smørgrav size_t nold; 1777bc5531deSDag-Erling Smørgrav }; 1778bc5531deSDag-Erling Smørgrav 1779ae1f160dSDag-Erling Smørgrav static void 1780bc5531deSDag-Erling Smørgrav hostkeys_update_ctx_free(struct hostkeys_update_ctx *ctx) 1781bc5531deSDag-Erling Smørgrav { 1782bc5531deSDag-Erling Smørgrav size_t i; 1783bc5531deSDag-Erling Smørgrav 1784bc5531deSDag-Erling Smørgrav if (ctx == NULL) 1785bc5531deSDag-Erling Smørgrav return; 1786bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) 1787bc5531deSDag-Erling Smørgrav sshkey_free(ctx->keys[i]); 1788bc5531deSDag-Erling Smørgrav free(ctx->keys); 1789bc5531deSDag-Erling Smørgrav free(ctx->keys_seen); 1790bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nold; i++) 1791bc5531deSDag-Erling Smørgrav sshkey_free(ctx->old_keys[i]); 1792bc5531deSDag-Erling Smørgrav free(ctx->old_keys); 1793bc5531deSDag-Erling Smørgrav free(ctx->host_str); 1794bc5531deSDag-Erling Smørgrav free(ctx->ip_str); 1795bc5531deSDag-Erling Smørgrav free(ctx); 1796bc5531deSDag-Erling Smørgrav } 1797bc5531deSDag-Erling Smørgrav 1798bc5531deSDag-Erling Smørgrav static int 1799bc5531deSDag-Erling Smørgrav hostkeys_find(struct hostkey_foreach_line *l, void *_ctx) 1800bc5531deSDag-Erling Smørgrav { 1801bc5531deSDag-Erling Smørgrav struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; 1802bc5531deSDag-Erling Smørgrav size_t i; 1803bc5531deSDag-Erling Smørgrav struct sshkey **tmp; 1804bc5531deSDag-Erling Smørgrav 1805*4f52dfbbSDag-Erling Smørgrav if (l->status != HKF_STATUS_MATCHED || l->key == NULL) 1806bc5531deSDag-Erling Smørgrav return 0; 1807bc5531deSDag-Erling Smørgrav 1808bc5531deSDag-Erling Smørgrav /* Mark off keys we've already seen for this host */ 1809bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) { 1810bc5531deSDag-Erling Smørgrav if (sshkey_equal(l->key, ctx->keys[i])) { 1811bc5531deSDag-Erling Smørgrav debug3("%s: found %s key at %s:%ld", __func__, 1812bc5531deSDag-Erling Smørgrav sshkey_ssh_name(ctx->keys[i]), l->path, l->linenum); 1813bc5531deSDag-Erling Smørgrav ctx->keys_seen[i] = 1; 1814bc5531deSDag-Erling Smørgrav return 0; 1815bc5531deSDag-Erling Smørgrav } 1816bc5531deSDag-Erling Smørgrav } 1817bc5531deSDag-Erling Smørgrav /* This line contained a key that not offered by the server */ 1818bc5531deSDag-Erling Smørgrav debug3("%s: deprecated %s key at %s:%ld", __func__, 1819bc5531deSDag-Erling Smørgrav sshkey_ssh_name(l->key), l->path, l->linenum); 1820*4f52dfbbSDag-Erling Smørgrav if ((tmp = recallocarray(ctx->old_keys, ctx->nold, ctx->nold + 1, 1821bc5531deSDag-Erling Smørgrav sizeof(*ctx->old_keys))) == NULL) 1822*4f52dfbbSDag-Erling Smørgrav fatal("%s: recallocarray failed nold = %zu", 1823bc5531deSDag-Erling Smørgrav __func__, ctx->nold); 1824bc5531deSDag-Erling Smørgrav ctx->old_keys = tmp; 1825bc5531deSDag-Erling Smørgrav ctx->old_keys[ctx->nold++] = l->key; 1826bc5531deSDag-Erling Smørgrav l->key = NULL; 1827bc5531deSDag-Erling Smørgrav 1828bc5531deSDag-Erling Smørgrav return 0; 1829bc5531deSDag-Erling Smørgrav } 1830bc5531deSDag-Erling Smørgrav 1831bc5531deSDag-Erling Smørgrav static void 1832bc5531deSDag-Erling Smørgrav update_known_hosts(struct hostkeys_update_ctx *ctx) 1833bc5531deSDag-Erling Smørgrav { 1834bc5531deSDag-Erling Smørgrav int r, was_raw = 0; 1835bc5531deSDag-Erling Smørgrav int loglevel = options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK ? 1836bc5531deSDag-Erling Smørgrav SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_VERBOSE; 1837bc5531deSDag-Erling Smørgrav char *fp, *response; 1838bc5531deSDag-Erling Smørgrav size_t i; 1839bc5531deSDag-Erling Smørgrav 1840bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) { 1841bc5531deSDag-Erling Smørgrav if (ctx->keys_seen[i] != 2) 1842bc5531deSDag-Erling Smørgrav continue; 1843bc5531deSDag-Erling Smørgrav if ((fp = sshkey_fingerprint(ctx->keys[i], 1844bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) 1845bc5531deSDag-Erling Smørgrav fatal("%s: sshkey_fingerprint failed", __func__); 1846bc5531deSDag-Erling Smørgrav do_log2(loglevel, "Learned new hostkey: %s %s", 1847bc5531deSDag-Erling Smørgrav sshkey_type(ctx->keys[i]), fp); 1848bc5531deSDag-Erling Smørgrav free(fp); 1849bc5531deSDag-Erling Smørgrav } 1850bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nold; i++) { 1851bc5531deSDag-Erling Smørgrav if ((fp = sshkey_fingerprint(ctx->old_keys[i], 1852bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) 1853bc5531deSDag-Erling Smørgrav fatal("%s: sshkey_fingerprint failed", __func__); 1854bc5531deSDag-Erling Smørgrav do_log2(loglevel, "Deprecating obsolete hostkey: %s %s", 1855bc5531deSDag-Erling Smørgrav sshkey_type(ctx->old_keys[i]), fp); 1856bc5531deSDag-Erling Smørgrav free(fp); 1857bc5531deSDag-Erling Smørgrav } 1858bc5531deSDag-Erling Smørgrav if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK) { 1859bc5531deSDag-Erling Smørgrav if (get_saved_tio() != NULL) { 1860bc5531deSDag-Erling Smørgrav leave_raw_mode(1); 1861bc5531deSDag-Erling Smørgrav was_raw = 1; 1862bc5531deSDag-Erling Smørgrav } 1863bc5531deSDag-Erling Smørgrav response = NULL; 1864bc5531deSDag-Erling Smørgrav for (i = 0; !quit_pending && i < 3; i++) { 1865bc5531deSDag-Erling Smørgrav free(response); 1866bc5531deSDag-Erling Smørgrav response = read_passphrase("Accept updated hostkeys? " 1867bc5531deSDag-Erling Smørgrav "(yes/no): ", RP_ECHO); 1868bc5531deSDag-Erling Smørgrav if (strcasecmp(response, "yes") == 0) 1869bc5531deSDag-Erling Smørgrav break; 1870bc5531deSDag-Erling Smørgrav else if (quit_pending || response == NULL || 1871bc5531deSDag-Erling Smørgrav strcasecmp(response, "no") == 0) { 1872bc5531deSDag-Erling Smørgrav options.update_hostkeys = 0; 1873bc5531deSDag-Erling Smørgrav break; 1874bc5531deSDag-Erling Smørgrav } else { 1875bc5531deSDag-Erling Smørgrav do_log2(loglevel, "Please enter " 1876bc5531deSDag-Erling Smørgrav "\"yes\" or \"no\""); 1877bc5531deSDag-Erling Smørgrav } 1878bc5531deSDag-Erling Smørgrav } 1879bc5531deSDag-Erling Smørgrav if (quit_pending || i >= 3 || response == NULL) 1880bc5531deSDag-Erling Smørgrav options.update_hostkeys = 0; 1881bc5531deSDag-Erling Smørgrav free(response); 1882bc5531deSDag-Erling Smørgrav if (was_raw) 1883bc5531deSDag-Erling Smørgrav enter_raw_mode(1); 1884bc5531deSDag-Erling Smørgrav } 1885bc5531deSDag-Erling Smørgrav 1886bc5531deSDag-Erling Smørgrav /* 1887bc5531deSDag-Erling Smørgrav * Now that all the keys are verified, we can go ahead and replace 1888bc5531deSDag-Erling Smørgrav * them in known_hosts (assuming SSH_UPDATE_HOSTKEYS_ASK didn't 1889bc5531deSDag-Erling Smørgrav * cancel the operation). 1890bc5531deSDag-Erling Smørgrav */ 1891bc5531deSDag-Erling Smørgrav if (options.update_hostkeys != 0 && 1892bc5531deSDag-Erling Smørgrav (r = hostfile_replace_entries(options.user_hostfiles[0], 1893bc5531deSDag-Erling Smørgrav ctx->host_str, ctx->ip_str, ctx->keys, ctx->nkeys, 1894bc5531deSDag-Erling Smørgrav options.hash_known_hosts, 0, 1895bc5531deSDag-Erling Smørgrav options.fingerprint_hash)) != 0) 1896bc5531deSDag-Erling Smørgrav error("%s: hostfile_replace_entries failed: %s", 1897bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 1898bc5531deSDag-Erling Smørgrav } 1899bc5531deSDag-Erling Smørgrav 1900bc5531deSDag-Erling Smørgrav static void 1901*4f52dfbbSDag-Erling Smørgrav client_global_hostkeys_private_confirm(struct ssh *ssh, int type, 1902*4f52dfbbSDag-Erling Smørgrav u_int32_t seq, void *_ctx) 1903bc5531deSDag-Erling Smørgrav { 1904bc5531deSDag-Erling Smørgrav struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; 1905bc5531deSDag-Erling Smørgrav size_t i, ndone; 1906bc5531deSDag-Erling Smørgrav struct sshbuf *signdata; 1907bc5531deSDag-Erling Smørgrav int r; 1908bc5531deSDag-Erling Smørgrav const u_char *sig; 1909bc5531deSDag-Erling Smørgrav size_t siglen; 1910bc5531deSDag-Erling Smørgrav 1911bc5531deSDag-Erling Smørgrav if (ctx->nnew == 0) 1912bc5531deSDag-Erling Smørgrav fatal("%s: ctx->nnew == 0", __func__); /* sanity */ 1913bc5531deSDag-Erling Smørgrav if (type != SSH2_MSG_REQUEST_SUCCESS) { 1914bc5531deSDag-Erling Smørgrav error("Server failed to confirm ownership of " 1915bc5531deSDag-Erling Smørgrav "private host keys"); 1916bc5531deSDag-Erling Smørgrav hostkeys_update_ctx_free(ctx); 1917bc5531deSDag-Erling Smørgrav return; 1918bc5531deSDag-Erling Smørgrav } 1919bc5531deSDag-Erling Smørgrav if ((signdata = sshbuf_new()) == NULL) 1920bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 1921bc5531deSDag-Erling Smørgrav /* Don't want to accidentally accept an unbound signature */ 1922bc5531deSDag-Erling Smørgrav if (ssh->kex->session_id_len == 0) 1923bc5531deSDag-Erling Smørgrav fatal("%s: ssh->kex->session_id_len == 0", __func__); 1924bc5531deSDag-Erling Smørgrav /* 1925bc5531deSDag-Erling Smørgrav * Expect a signature for each of the ctx->nnew private keys we 1926bc5531deSDag-Erling Smørgrav * haven't seen before. They will be in the same order as the 1927bc5531deSDag-Erling Smørgrav * ctx->keys where the corresponding ctx->keys_seen[i] == 0. 1928bc5531deSDag-Erling Smørgrav */ 1929bc5531deSDag-Erling Smørgrav for (ndone = i = 0; i < ctx->nkeys; i++) { 1930bc5531deSDag-Erling Smørgrav if (ctx->keys_seen[i]) 1931bc5531deSDag-Erling Smørgrav continue; 1932bc5531deSDag-Erling Smørgrav /* Prepare data to be signed: session ID, unique string, key */ 1933bc5531deSDag-Erling Smørgrav sshbuf_reset(signdata); 1934bc5531deSDag-Erling Smørgrav if ( (r = sshbuf_put_cstring(signdata, 1935bc5531deSDag-Erling Smørgrav "hostkeys-prove-00@openssh.com")) != 0 || 1936bc5531deSDag-Erling Smørgrav (r = sshbuf_put_string(signdata, ssh->kex->session_id, 1937bc5531deSDag-Erling Smørgrav ssh->kex->session_id_len)) != 0 || 1938bc5531deSDag-Erling Smørgrav (r = sshkey_puts(ctx->keys[i], signdata)) != 0) 1939bc5531deSDag-Erling Smørgrav fatal("%s: failed to prepare signature: %s", 1940bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 1941bc5531deSDag-Erling Smørgrav /* Extract and verify signature */ 1942bc5531deSDag-Erling Smørgrav if ((r = sshpkt_get_string_direct(ssh, &sig, &siglen)) != 0) { 1943bc5531deSDag-Erling Smørgrav error("%s: couldn't parse message: %s", 1944bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 1945bc5531deSDag-Erling Smørgrav goto out; 1946bc5531deSDag-Erling Smørgrav } 1947bc5531deSDag-Erling Smørgrav if ((r = sshkey_verify(ctx->keys[i], sig, siglen, 1948bc5531deSDag-Erling Smørgrav sshbuf_ptr(signdata), sshbuf_len(signdata), 0)) != 0) { 1949bc5531deSDag-Erling Smørgrav error("%s: server gave bad signature for %s key %zu", 1950bc5531deSDag-Erling Smørgrav __func__, sshkey_type(ctx->keys[i]), i); 1951bc5531deSDag-Erling Smørgrav goto out; 1952bc5531deSDag-Erling Smørgrav } 1953bc5531deSDag-Erling Smørgrav /* Key is good. Mark it as 'seen' */ 1954bc5531deSDag-Erling Smørgrav ctx->keys_seen[i] = 2; 1955bc5531deSDag-Erling Smørgrav ndone++; 1956bc5531deSDag-Erling Smørgrav } 1957bc5531deSDag-Erling Smørgrav if (ndone != ctx->nnew) 1958bc5531deSDag-Erling Smørgrav fatal("%s: ndone != ctx->nnew (%zu / %zu)", __func__, 1959bc5531deSDag-Erling Smørgrav ndone, ctx->nnew); /* Shouldn't happen */ 1960bc5531deSDag-Erling Smørgrav ssh_packet_check_eom(ssh); 1961bc5531deSDag-Erling Smørgrav 1962bc5531deSDag-Erling Smørgrav /* Make the edits to known_hosts */ 1963bc5531deSDag-Erling Smørgrav update_known_hosts(ctx); 1964bc5531deSDag-Erling Smørgrav out: 1965bc5531deSDag-Erling Smørgrav hostkeys_update_ctx_free(ctx); 1966bc5531deSDag-Erling Smørgrav } 1967bc5531deSDag-Erling Smørgrav 1968bc5531deSDag-Erling Smørgrav /* 1969d93a896eSDag-Erling Smørgrav * Returns non-zero if the key is accepted by HostkeyAlgorithms. 1970d93a896eSDag-Erling Smørgrav * Made slightly less trivial by the multiple RSA signature algorithm names. 1971d93a896eSDag-Erling Smørgrav */ 1972d93a896eSDag-Erling Smørgrav static int 1973d93a896eSDag-Erling Smørgrav key_accepted_by_hostkeyalgs(const struct sshkey *key) 1974d93a896eSDag-Erling Smørgrav { 1975d93a896eSDag-Erling Smørgrav const char *ktype = sshkey_ssh_name(key); 1976d93a896eSDag-Erling Smørgrav const char *hostkeyalgs = options.hostkeyalgorithms != NULL ? 1977d93a896eSDag-Erling Smørgrav options.hostkeyalgorithms : KEX_DEFAULT_PK_ALG; 1978d93a896eSDag-Erling Smørgrav 1979d93a896eSDag-Erling Smørgrav if (key == NULL || key->type == KEY_UNSPEC) 1980d93a896eSDag-Erling Smørgrav return 0; 1981d93a896eSDag-Erling Smørgrav if (key->type == KEY_RSA && 1982d93a896eSDag-Erling Smørgrav (match_pattern_list("rsa-sha2-256", hostkeyalgs, 0) == 1 || 1983d93a896eSDag-Erling Smørgrav match_pattern_list("rsa-sha2-512", hostkeyalgs, 0) == 1)) 1984d93a896eSDag-Erling Smørgrav return 1; 1985d93a896eSDag-Erling Smørgrav return match_pattern_list(ktype, hostkeyalgs, 0) == 1; 1986d93a896eSDag-Erling Smørgrav } 1987d93a896eSDag-Erling Smørgrav 1988d93a896eSDag-Erling Smørgrav /* 1989bc5531deSDag-Erling Smørgrav * Handle hostkeys-00@openssh.com global request to inform the client of all 1990bc5531deSDag-Erling Smørgrav * the server's hostkeys. The keys are checked against the user's 1991bc5531deSDag-Erling Smørgrav * HostkeyAlgorithms preference before they are accepted. 1992bc5531deSDag-Erling Smørgrav */ 1993bc5531deSDag-Erling Smørgrav static int 1994bc5531deSDag-Erling Smørgrav client_input_hostkeys(void) 1995bc5531deSDag-Erling Smørgrav { 1996bc5531deSDag-Erling Smørgrav struct ssh *ssh = active_state; /* XXX */ 1997bc5531deSDag-Erling Smørgrav const u_char *blob = NULL; 1998bc5531deSDag-Erling Smørgrav size_t i, len = 0; 1999bc5531deSDag-Erling Smørgrav struct sshbuf *buf = NULL; 2000bc5531deSDag-Erling Smørgrav struct sshkey *key = NULL, **tmp; 2001bc5531deSDag-Erling Smørgrav int r; 2002bc5531deSDag-Erling Smørgrav char *fp; 2003bc5531deSDag-Erling Smørgrav static int hostkeys_seen = 0; /* XXX use struct ssh */ 2004bc5531deSDag-Erling Smørgrav extern struct sockaddr_storage hostaddr; /* XXX from ssh.c */ 2005bc5531deSDag-Erling Smørgrav struct hostkeys_update_ctx *ctx = NULL; 2006bc5531deSDag-Erling Smørgrav 2007bc5531deSDag-Erling Smørgrav if (hostkeys_seen) 2008bc5531deSDag-Erling Smørgrav fatal("%s: server already sent hostkeys", __func__); 2009bc5531deSDag-Erling Smørgrav if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK && 2010bc5531deSDag-Erling Smørgrav options.batch_mode) 2011bc5531deSDag-Erling Smørgrav return 1; /* won't ask in batchmode, so don't even try */ 2012bc5531deSDag-Erling Smørgrav if (!options.update_hostkeys || options.num_user_hostfiles <= 0) 2013bc5531deSDag-Erling Smørgrav return 1; 2014bc5531deSDag-Erling Smørgrav 2015bc5531deSDag-Erling Smørgrav ctx = xcalloc(1, sizeof(*ctx)); 2016bc5531deSDag-Erling Smørgrav while (ssh_packet_remaining(ssh) > 0) { 2017bc5531deSDag-Erling Smørgrav sshkey_free(key); 2018bc5531deSDag-Erling Smørgrav key = NULL; 2019bc5531deSDag-Erling Smørgrav if ((r = sshpkt_get_string_direct(ssh, &blob, &len)) != 0) { 2020bc5531deSDag-Erling Smørgrav error("%s: couldn't parse message: %s", 2021bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 2022bc5531deSDag-Erling Smørgrav goto out; 2023bc5531deSDag-Erling Smørgrav } 2024bc5531deSDag-Erling Smørgrav if ((r = sshkey_from_blob(blob, len, &key)) != 0) { 2025bc5531deSDag-Erling Smørgrav error("%s: parse key: %s", __func__, ssh_err(r)); 2026bc5531deSDag-Erling Smørgrav goto out; 2027bc5531deSDag-Erling Smørgrav } 2028bc5531deSDag-Erling Smørgrav fp = sshkey_fingerprint(key, options.fingerprint_hash, 2029bc5531deSDag-Erling Smørgrav SSH_FP_DEFAULT); 2030bc5531deSDag-Erling Smørgrav debug3("%s: received %s key %s", __func__, 2031bc5531deSDag-Erling Smørgrav sshkey_type(key), fp); 2032bc5531deSDag-Erling Smørgrav free(fp); 2033eccfee6eSDag-Erling Smørgrav 2034d93a896eSDag-Erling Smørgrav if (!key_accepted_by_hostkeyalgs(key)) { 2035bc5531deSDag-Erling Smørgrav debug3("%s: %s key not permitted by HostkeyAlgorithms", 2036bc5531deSDag-Erling Smørgrav __func__, sshkey_ssh_name(key)); 2037bc5531deSDag-Erling Smørgrav continue; 2038bc5531deSDag-Erling Smørgrav } 2039bc5531deSDag-Erling Smørgrav /* Skip certs */ 2040bc5531deSDag-Erling Smørgrav if (sshkey_is_cert(key)) { 2041bc5531deSDag-Erling Smørgrav debug3("%s: %s key is a certificate; skipping", 2042bc5531deSDag-Erling Smørgrav __func__, sshkey_ssh_name(key)); 2043bc5531deSDag-Erling Smørgrav continue; 2044bc5531deSDag-Erling Smørgrav } 2045bc5531deSDag-Erling Smørgrav /* Ensure keys are unique */ 2046bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) { 2047bc5531deSDag-Erling Smørgrav if (sshkey_equal(key, ctx->keys[i])) { 2048bc5531deSDag-Erling Smørgrav error("%s: received duplicated %s host key", 2049bc5531deSDag-Erling Smørgrav __func__, sshkey_ssh_name(key)); 2050bc5531deSDag-Erling Smørgrav goto out; 2051bc5531deSDag-Erling Smørgrav } 2052bc5531deSDag-Erling Smørgrav } 2053bc5531deSDag-Erling Smørgrav /* Key is good, record it */ 2054*4f52dfbbSDag-Erling Smørgrav if ((tmp = recallocarray(ctx->keys, ctx->nkeys, ctx->nkeys + 1, 2055bc5531deSDag-Erling Smørgrav sizeof(*ctx->keys))) == NULL) 2056*4f52dfbbSDag-Erling Smørgrav fatal("%s: recallocarray failed nkeys = %zu", 2057bc5531deSDag-Erling Smørgrav __func__, ctx->nkeys); 2058bc5531deSDag-Erling Smørgrav ctx->keys = tmp; 2059bc5531deSDag-Erling Smørgrav ctx->keys[ctx->nkeys++] = key; 2060bc5531deSDag-Erling Smørgrav key = NULL; 2061bc5531deSDag-Erling Smørgrav } 2062bc5531deSDag-Erling Smørgrav 2063bc5531deSDag-Erling Smørgrav if (ctx->nkeys == 0) { 2064bc5531deSDag-Erling Smørgrav debug("%s: server sent no hostkeys", __func__); 2065bc5531deSDag-Erling Smørgrav goto out; 2066bc5531deSDag-Erling Smørgrav } 2067bc5531deSDag-Erling Smørgrav 2068bc5531deSDag-Erling Smørgrav if ((ctx->keys_seen = calloc(ctx->nkeys, 2069bc5531deSDag-Erling Smørgrav sizeof(*ctx->keys_seen))) == NULL) 2070bc5531deSDag-Erling Smørgrav fatal("%s: calloc failed", __func__); 2071bc5531deSDag-Erling Smørgrav 2072bc5531deSDag-Erling Smørgrav get_hostfile_hostname_ipaddr(host, 2073bc5531deSDag-Erling Smørgrav options.check_host_ip ? (struct sockaddr *)&hostaddr : NULL, 2074bc5531deSDag-Erling Smørgrav options.port, &ctx->host_str, 2075bc5531deSDag-Erling Smørgrav options.check_host_ip ? &ctx->ip_str : NULL); 2076bc5531deSDag-Erling Smørgrav 2077bc5531deSDag-Erling Smørgrav /* Find which keys we already know about. */ 2078bc5531deSDag-Erling Smørgrav if ((r = hostkeys_foreach(options.user_hostfiles[0], hostkeys_find, 2079bc5531deSDag-Erling Smørgrav ctx, ctx->host_str, ctx->ip_str, 2080bc5531deSDag-Erling Smørgrav HKF_WANT_PARSE_KEY|HKF_WANT_MATCH)) != 0) { 2081bc5531deSDag-Erling Smørgrav error("%s: hostkeys_foreach failed: %s", __func__, ssh_err(r)); 2082bc5531deSDag-Erling Smørgrav goto out; 2083bc5531deSDag-Erling Smørgrav } 2084bc5531deSDag-Erling Smørgrav 2085bc5531deSDag-Erling Smørgrav /* Figure out if we have any new keys to add */ 2086bc5531deSDag-Erling Smørgrav ctx->nnew = 0; 2087bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) { 2088bc5531deSDag-Erling Smørgrav if (!ctx->keys_seen[i]) 2089bc5531deSDag-Erling Smørgrav ctx->nnew++; 2090bc5531deSDag-Erling Smørgrav } 2091bc5531deSDag-Erling Smørgrav 2092bc5531deSDag-Erling Smørgrav debug3("%s: %zu keys from server: %zu new, %zu retained. %zu to remove", 2093bc5531deSDag-Erling Smørgrav __func__, ctx->nkeys, ctx->nnew, ctx->nkeys - ctx->nnew, ctx->nold); 2094bc5531deSDag-Erling Smørgrav 2095bc5531deSDag-Erling Smørgrav if (ctx->nnew == 0 && ctx->nold != 0) { 2096bc5531deSDag-Erling Smørgrav /* We have some keys to remove. Just do it. */ 2097bc5531deSDag-Erling Smørgrav update_known_hosts(ctx); 2098bc5531deSDag-Erling Smørgrav } else if (ctx->nnew != 0) { 2099bc5531deSDag-Erling Smørgrav /* 2100bc5531deSDag-Erling Smørgrav * We have received hitherto-unseen keys from the server. 2101bc5531deSDag-Erling Smørgrav * Ask the server to confirm ownership of the private halves. 2102bc5531deSDag-Erling Smørgrav */ 2103bc5531deSDag-Erling Smørgrav debug3("%s: asking server to prove ownership for %zu keys", 2104bc5531deSDag-Erling Smørgrav __func__, ctx->nnew); 2105bc5531deSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || 2106bc5531deSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, 2107bc5531deSDag-Erling Smørgrav "hostkeys-prove-00@openssh.com")) != 0 || 2108bc5531deSDag-Erling Smørgrav (r = sshpkt_put_u8(ssh, 1)) != 0) /* bool: want reply */ 2109bc5531deSDag-Erling Smørgrav fatal("%s: cannot prepare packet: %s", 2110bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 2111bc5531deSDag-Erling Smørgrav if ((buf = sshbuf_new()) == NULL) 2112bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_new", __func__); 2113bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) { 2114bc5531deSDag-Erling Smørgrav if (ctx->keys_seen[i]) 2115bc5531deSDag-Erling Smørgrav continue; 2116bc5531deSDag-Erling Smørgrav sshbuf_reset(buf); 2117bc5531deSDag-Erling Smørgrav if ((r = sshkey_putb(ctx->keys[i], buf)) != 0) 2118bc5531deSDag-Erling Smørgrav fatal("%s: sshkey_putb: %s", 2119bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 2120bc5531deSDag-Erling Smørgrav if ((r = sshpkt_put_stringb(ssh, buf)) != 0) 2121bc5531deSDag-Erling Smørgrav fatal("%s: sshpkt_put_string: %s", 2122bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 2123bc5531deSDag-Erling Smørgrav } 2124bc5531deSDag-Erling Smørgrav if ((r = sshpkt_send(ssh)) != 0) 2125bc5531deSDag-Erling Smørgrav fatal("%s: sshpkt_send: %s", __func__, ssh_err(r)); 2126bc5531deSDag-Erling Smørgrav client_register_global_confirm( 2127bc5531deSDag-Erling Smørgrav client_global_hostkeys_private_confirm, ctx); 2128bc5531deSDag-Erling Smørgrav ctx = NULL; /* will be freed in callback */ 2129bc5531deSDag-Erling Smørgrav } 2130bc5531deSDag-Erling Smørgrav 2131bc5531deSDag-Erling Smørgrav /* Success */ 2132bc5531deSDag-Erling Smørgrav out: 2133bc5531deSDag-Erling Smørgrav hostkeys_update_ctx_free(ctx); 2134bc5531deSDag-Erling Smørgrav sshkey_free(key); 2135bc5531deSDag-Erling Smørgrav sshbuf_free(buf); 2136bc5531deSDag-Erling Smørgrav /* 2137bc5531deSDag-Erling Smørgrav * NB. Return success for all cases. The server doesn't need to know 2138bc5531deSDag-Erling Smørgrav * what the client does with its hosts file. 2139bc5531deSDag-Erling Smørgrav */ 2140bc5531deSDag-Erling Smørgrav return 1; 2141bc5531deSDag-Erling Smørgrav } 2142bc5531deSDag-Erling Smørgrav 2143bc5531deSDag-Erling Smørgrav static int 2144*4f52dfbbSDag-Erling Smørgrav client_input_global_request(int type, u_int32_t seq, struct ssh *ssh) 2145ae1f160dSDag-Erling Smørgrav { 2146ae1f160dSDag-Erling Smørgrav char *rtype; 2147ae1f160dSDag-Erling Smørgrav int want_reply; 2148ae1f160dSDag-Erling Smørgrav int success = 0; 2149a04a10f8SKris Kennaway 2150bc5531deSDag-Erling Smørgrav rtype = packet_get_cstring(NULL); 2151ae1f160dSDag-Erling Smørgrav want_reply = packet_get_char(); 2152efcad6b7SDag-Erling Smørgrav debug("client_input_global_request: rtype %s want_reply %d", 2153efcad6b7SDag-Erling Smørgrav rtype, want_reply); 2154bc5531deSDag-Erling Smørgrav if (strcmp(rtype, "hostkeys-00@openssh.com") == 0) 2155bc5531deSDag-Erling Smørgrav success = client_input_hostkeys(); 2156ae1f160dSDag-Erling Smørgrav if (want_reply) { 2157ae1f160dSDag-Erling Smørgrav packet_start(success ? 2158ae1f160dSDag-Erling Smørgrav SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE); 2159ae1f160dSDag-Erling Smørgrav packet_send(); 2160ae1f160dSDag-Erling Smørgrav packet_write_wait(); 2161ae1f160dSDag-Erling Smørgrav } 2162e4a9863fSDag-Erling Smørgrav free(rtype); 2163bc5531deSDag-Erling Smørgrav return 0; 2164ae1f160dSDag-Erling Smørgrav } 2165ae1f160dSDag-Erling Smørgrav 2166d74d50a8SDag-Erling Smørgrav void 2167*4f52dfbbSDag-Erling Smørgrav client_session2_setup(struct ssh *ssh, int id, int want_tty, int want_subsystem, 2168d4af9e69SDag-Erling Smørgrav const char *term, struct termios *tiop, int in_fd, Buffer *cmd, char **env) 2169d74d50a8SDag-Erling Smørgrav { 2170d74d50a8SDag-Erling Smørgrav int len; 21715e8dbd04SDag-Erling Smørgrav Channel *c = NULL; 2172d74d50a8SDag-Erling Smørgrav 2173d74d50a8SDag-Erling Smørgrav debug2("%s: id %d", __func__, id); 2174d74d50a8SDag-Erling Smørgrav 2175*4f52dfbbSDag-Erling Smørgrav if ((c = channel_lookup(ssh, id)) == NULL) 2176*4f52dfbbSDag-Erling Smørgrav fatal("%s: channel %d: unknown channel", __func__, id); 21775e8dbd04SDag-Erling Smørgrav 21784a421b63SDag-Erling Smørgrav packet_set_interactive(want_tty, 21794a421b63SDag-Erling Smørgrav options.ip_qos_interactive, options.ip_qos_bulk); 21804a421b63SDag-Erling Smørgrav 2181d74d50a8SDag-Erling Smørgrav if (want_tty) { 2182d74d50a8SDag-Erling Smørgrav struct winsize ws; 2183d74d50a8SDag-Erling Smørgrav 2184d74d50a8SDag-Erling Smørgrav /* Store window size in the packet. */ 2185d74d50a8SDag-Erling Smørgrav if (ioctl(in_fd, TIOCGWINSZ, &ws) < 0) 2186d74d50a8SDag-Erling Smørgrav memset(&ws, 0, sizeof(ws)); 2187d74d50a8SDag-Erling Smørgrav 2188*4f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, id, "pty-req", 1); 2189*4f52dfbbSDag-Erling Smørgrav client_expect_confirm(ssh, id, "PTY allocation", CONFIRM_TTY); 2190d74d50a8SDag-Erling Smørgrav packet_put_cstring(term != NULL ? term : ""); 2191761efaa7SDag-Erling Smørgrav packet_put_int((u_int)ws.ws_col); 2192761efaa7SDag-Erling Smørgrav packet_put_int((u_int)ws.ws_row); 2193761efaa7SDag-Erling Smørgrav packet_put_int((u_int)ws.ws_xpixel); 2194761efaa7SDag-Erling Smørgrav packet_put_int((u_int)ws.ws_ypixel); 2195d4af9e69SDag-Erling Smørgrav if (tiop == NULL) 2196d4af9e69SDag-Erling Smørgrav tiop = get_saved_tio(); 2197d4af9e69SDag-Erling Smørgrav tty_make_modes(-1, tiop); 2198d74d50a8SDag-Erling Smørgrav packet_send(); 2199d74d50a8SDag-Erling Smørgrav /* XXX wait for reply */ 22005e8dbd04SDag-Erling Smørgrav c->client_tty = 1; 2201d74d50a8SDag-Erling Smørgrav } 2202d74d50a8SDag-Erling Smørgrav 2203d74d50a8SDag-Erling Smørgrav /* Transfer any environment variables from client to server */ 2204d74d50a8SDag-Erling Smørgrav if (options.num_send_env != 0 && env != NULL) { 2205d74d50a8SDag-Erling Smørgrav int i, j, matched; 2206d74d50a8SDag-Erling Smørgrav char *name, *val; 2207d74d50a8SDag-Erling Smørgrav 2208d74d50a8SDag-Erling Smørgrav debug("Sending environment."); 2209d74d50a8SDag-Erling Smørgrav for (i = 0; env[i] != NULL; i++) { 2210d74d50a8SDag-Erling Smørgrav /* Split */ 2211d74d50a8SDag-Erling Smørgrav name = xstrdup(env[i]); 2212d74d50a8SDag-Erling Smørgrav if ((val = strchr(name, '=')) == NULL) { 2213e4a9863fSDag-Erling Smørgrav free(name); 2214d74d50a8SDag-Erling Smørgrav continue; 2215d74d50a8SDag-Erling Smørgrav } 2216d74d50a8SDag-Erling Smørgrav *val++ = '\0'; 2217d74d50a8SDag-Erling Smørgrav 2218d74d50a8SDag-Erling Smørgrav matched = 0; 2219d74d50a8SDag-Erling Smørgrav for (j = 0; j < options.num_send_env; j++) { 2220d74d50a8SDag-Erling Smørgrav if (match_pattern(name, options.send_env[j])) { 2221d74d50a8SDag-Erling Smørgrav matched = 1; 2222d74d50a8SDag-Erling Smørgrav break; 2223d74d50a8SDag-Erling Smørgrav } 2224d74d50a8SDag-Erling Smørgrav } 2225d74d50a8SDag-Erling Smørgrav if (!matched) { 2226d74d50a8SDag-Erling Smørgrav debug3("Ignored env %s", name); 2227e4a9863fSDag-Erling Smørgrav free(name); 2228d74d50a8SDag-Erling Smørgrav continue; 2229d74d50a8SDag-Erling Smørgrav } 2230d74d50a8SDag-Erling Smørgrav 2231d74d50a8SDag-Erling Smørgrav debug("Sending env %s = %s", name, val); 2232*4f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, id, "env", 0); 2233d74d50a8SDag-Erling Smørgrav packet_put_cstring(name); 2234d74d50a8SDag-Erling Smørgrav packet_put_cstring(val); 2235d74d50a8SDag-Erling Smørgrav packet_send(); 2236e4a9863fSDag-Erling Smørgrav free(name); 2237d74d50a8SDag-Erling Smørgrav } 2238d74d50a8SDag-Erling Smørgrav } 2239d74d50a8SDag-Erling Smørgrav 2240d74d50a8SDag-Erling Smørgrav len = buffer_len(cmd); 2241d74d50a8SDag-Erling Smørgrav if (len > 0) { 2242d74d50a8SDag-Erling Smørgrav if (len > 900) 2243d74d50a8SDag-Erling Smørgrav len = 900; 2244d74d50a8SDag-Erling Smørgrav if (want_subsystem) { 2245d4af9e69SDag-Erling Smørgrav debug("Sending subsystem: %.*s", 2246d4af9e69SDag-Erling Smørgrav len, (u_char*)buffer_ptr(cmd)); 2247*4f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, id, "subsystem", 1); 2248*4f52dfbbSDag-Erling Smørgrav client_expect_confirm(ssh, id, "subsystem", 2249*4f52dfbbSDag-Erling Smørgrav CONFIRM_CLOSE); 2250d74d50a8SDag-Erling Smørgrav } else { 2251d4af9e69SDag-Erling Smørgrav debug("Sending command: %.*s", 2252d4af9e69SDag-Erling Smørgrav len, (u_char*)buffer_ptr(cmd)); 2253*4f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, id, "exec", 1); 2254*4f52dfbbSDag-Erling Smørgrav client_expect_confirm(ssh, id, "exec", CONFIRM_CLOSE); 2255d74d50a8SDag-Erling Smørgrav } 2256d74d50a8SDag-Erling Smørgrav packet_put_string(buffer_ptr(cmd), buffer_len(cmd)); 2257d74d50a8SDag-Erling Smørgrav packet_send(); 2258d74d50a8SDag-Erling Smørgrav } else { 2259*4f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, id, "shell", 1); 2260*4f52dfbbSDag-Erling Smørgrav client_expect_confirm(ssh, id, "shell", CONFIRM_CLOSE); 2261d74d50a8SDag-Erling Smørgrav packet_send(); 2262d74d50a8SDag-Erling Smørgrav } 2263d74d50a8SDag-Erling Smørgrav } 2264d74d50a8SDag-Erling Smørgrav 2265ae1f160dSDag-Erling Smørgrav static void 2266*4f52dfbbSDag-Erling Smørgrav client_init_dispatch(void) 2267a04a10f8SKris Kennaway { 2268a04a10f8SKris Kennaway dispatch_init(&dispatch_protocol_error); 2269545d5ecaSDag-Erling Smørgrav 2270a04a10f8SKris Kennaway dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); 2271a04a10f8SKris Kennaway dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data); 2272a04a10f8SKris Kennaway dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); 2273a04a10f8SKris Kennaway dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); 2274a04a10f8SKris Kennaway dispatch_set(SSH2_MSG_CHANNEL_OPEN, &client_input_channel_open); 2275a04a10f8SKris Kennaway dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); 2276a04a10f8SKris Kennaway dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); 22771e8db6e2SBrian Feldman dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &client_input_channel_req); 2278a04a10f8SKris Kennaway dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); 2279d4af9e69SDag-Erling Smørgrav dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, &channel_input_status_confirm); 2280d4af9e69SDag-Erling Smørgrav dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &channel_input_status_confirm); 2281ae1f160dSDag-Erling Smørgrav dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &client_input_global_request); 22821e8db6e2SBrian Feldman 22831e8db6e2SBrian Feldman /* rekeying */ 22841e8db6e2SBrian Feldman dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit); 2285545d5ecaSDag-Erling Smørgrav 2286545d5ecaSDag-Erling Smørgrav /* global request reply messages */ 2287545d5ecaSDag-Erling Smørgrav dispatch_set(SSH2_MSG_REQUEST_FAILURE, &client_global_request_reply); 2288545d5ecaSDag-Erling Smørgrav dispatch_set(SSH2_MSG_REQUEST_SUCCESS, &client_global_request_reply); 2289a04a10f8SKris Kennaway } 2290d4af9e69SDag-Erling Smørgrav 2291e146993eSDag-Erling Smørgrav void 2292e146993eSDag-Erling Smørgrav client_stop_mux(void) 2293e146993eSDag-Erling Smørgrav { 2294e146993eSDag-Erling Smørgrav if (options.control_path != NULL && muxserver_sock != -1) 2295e146993eSDag-Erling Smørgrav unlink(options.control_path); 2296e146993eSDag-Erling Smørgrav /* 22976888a9beSDag-Erling Smørgrav * If we are in persist mode, or don't have a shell, signal that we 22986888a9beSDag-Erling Smørgrav * should close when all active channels are closed. 2299e146993eSDag-Erling Smørgrav */ 23006888a9beSDag-Erling Smørgrav if (options.control_persist || no_shell_flag) { 2301e146993eSDag-Erling Smørgrav session_closed = 1; 2302e146993eSDag-Erling Smørgrav setproctitle("[stopped mux]"); 2303e146993eSDag-Erling Smørgrav } 2304e146993eSDag-Erling Smørgrav } 2305e146993eSDag-Erling Smørgrav 2306efcad6b7SDag-Erling Smørgrav /* client specific fatal cleanup */ 2307efcad6b7SDag-Erling Smørgrav void 2308efcad6b7SDag-Erling Smørgrav cleanup_exit(int i) 2309efcad6b7SDag-Erling Smørgrav { 2310e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 2311efcad6b7SDag-Erling Smørgrav leave_non_blocking(); 2312d4af9e69SDag-Erling Smørgrav if (options.control_path != NULL && muxserver_sock != -1) 2313d74d50a8SDag-Erling Smørgrav unlink(options.control_path); 23144a421b63SDag-Erling Smørgrav ssh_kill_proxy_command(); 2315efcad6b7SDag-Erling Smørgrav _exit(i); 2316efcad6b7SDag-Erling Smørgrav } 2317