1*47dd1d1bSDag-Erling Smørgrav /* $OpenBSD: clientloop.c,v 1.311 2018/02/11 21:16:56 dtucker 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. */ 1564f52dfbbSDag-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 */ 1804f52dfbbSDag-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 } 214511b41d2SMark Murray 215511b41d2SMark Murray /* 216511b41d2SMark Murray * Signal handler for signals that cause the program to terminate. These 217511b41d2SMark Murray * signals must be trapped to restore terminal modes. 218511b41d2SMark Murray */ 219761efaa7SDag-Erling Smørgrav /*ARGSUSED */ 220ae1f160dSDag-Erling Smørgrav static void 221511b41d2SMark Murray signal_handler(int sig) 222511b41d2SMark Murray { 223ae1f160dSDag-Erling Smørgrav received_signal = sig; 224ae1f160dSDag-Erling Smørgrav quit_pending = 1; 225511b41d2SMark Murray } 226511b41d2SMark Murray 227511b41d2SMark Murray /* 228e2f6069cSDag-Erling Smørgrav * Sets control_persist_exit_time to the absolute time when the 229e2f6069cSDag-Erling Smørgrav * backgrounded control master should exit due to expiry of the 230e2f6069cSDag-Erling Smørgrav * ControlPersist timeout. Sets it to 0 if we are not a backgrounded 231e2f6069cSDag-Erling Smørgrav * control master process, or if there is no ControlPersist timeout. 232e2f6069cSDag-Erling Smørgrav */ 233e2f6069cSDag-Erling Smørgrav static void 2344f52dfbbSDag-Erling Smørgrav set_control_persist_exit_time(struct ssh *ssh) 235e2f6069cSDag-Erling Smørgrav { 236e2f6069cSDag-Erling Smørgrav if (muxserver_sock == -1 || !options.control_persist 237e146993eSDag-Erling Smørgrav || options.control_persist_timeout == 0) { 238e2f6069cSDag-Erling Smørgrav /* not using a ControlPersist timeout */ 239e2f6069cSDag-Erling Smørgrav control_persist_exit_time = 0; 2404f52dfbbSDag-Erling Smørgrav } else if (channel_still_open(ssh)) { 241e2f6069cSDag-Erling Smørgrav /* some client connections are still open */ 242e2f6069cSDag-Erling Smørgrav if (control_persist_exit_time > 0) 243e2f6069cSDag-Erling Smørgrav debug2("%s: cancel scheduled exit", __func__); 244e2f6069cSDag-Erling Smørgrav control_persist_exit_time = 0; 245e2f6069cSDag-Erling Smørgrav } else if (control_persist_exit_time <= 0) { 246e2f6069cSDag-Erling Smørgrav /* a client connection has recently closed */ 247e4a9863fSDag-Erling Smørgrav control_persist_exit_time = monotime() + 248e2f6069cSDag-Erling Smørgrav (time_t)options.control_persist_timeout; 249e2f6069cSDag-Erling Smørgrav debug2("%s: schedule exit in %d seconds", __func__, 250e2f6069cSDag-Erling Smørgrav options.control_persist_timeout); 251e2f6069cSDag-Erling Smørgrav } 252e2f6069cSDag-Erling Smørgrav /* else we are already counting down to the timeout */ 253e2f6069cSDag-Erling Smørgrav } 254e2f6069cSDag-Erling Smørgrav 255462c32cbSDag-Erling Smørgrav #define SSH_X11_VALID_DISPLAY_CHARS ":/.-_" 256462c32cbSDag-Erling Smørgrav static int 257462c32cbSDag-Erling Smørgrav client_x11_display_valid(const char *display) 258462c32cbSDag-Erling Smørgrav { 259462c32cbSDag-Erling Smørgrav size_t i, dlen; 260462c32cbSDag-Erling Smørgrav 261acc1a9efSDag-Erling Smørgrav if (display == NULL) 262acc1a9efSDag-Erling Smørgrav return 0; 263acc1a9efSDag-Erling Smørgrav 264462c32cbSDag-Erling Smørgrav dlen = strlen(display); 265462c32cbSDag-Erling Smørgrav for (i = 0; i < dlen; i++) { 266f7167e0eSDag-Erling Smørgrav if (!isalnum((u_char)display[i]) && 267462c32cbSDag-Erling Smørgrav strchr(SSH_X11_VALID_DISPLAY_CHARS, display[i]) == NULL) { 268462c32cbSDag-Erling Smørgrav debug("Invalid character '%c' in DISPLAY", display[i]); 269462c32cbSDag-Erling Smørgrav return 0; 270462c32cbSDag-Erling Smørgrav } 271462c32cbSDag-Erling Smørgrav } 272462c32cbSDag-Erling Smørgrav return 1; 273462c32cbSDag-Erling Smørgrav } 274462c32cbSDag-Erling Smørgrav 275043840dfSDag-Erling Smørgrav #define SSH_X11_PROTO "MIT-MAGIC-COOKIE-1" 276557f75e5SDag-Erling Smørgrav #define X11_TIMEOUT_SLACK 60 277acc1a9efSDag-Erling Smørgrav int 2784f52dfbbSDag-Erling Smørgrav client_x11_get_proto(struct ssh *ssh, const char *display, 2794f52dfbbSDag-Erling Smørgrav const char *xauth_path, u_int trusted, u_int timeout, 2804f52dfbbSDag-Erling Smørgrav char **_proto, char **_data) 281043840dfSDag-Erling Smørgrav { 282acc1a9efSDag-Erling Smørgrav char cmd[1024], line[512], xdisplay[512]; 283acc1a9efSDag-Erling Smørgrav char xauthfile[PATH_MAX], xauthdir[PATH_MAX]; 284043840dfSDag-Erling Smørgrav static char proto[512], data[512]; 285043840dfSDag-Erling Smørgrav FILE *f; 286ca86bcf2SDag-Erling Smørgrav int got_data = 0, generated = 0, do_unlink = 0, r; 287043840dfSDag-Erling Smørgrav struct stat st; 288557f75e5SDag-Erling Smørgrav u_int now, x11_timeout_real; 289043840dfSDag-Erling Smørgrav 290043840dfSDag-Erling Smørgrav *_proto = proto; 291043840dfSDag-Erling Smørgrav *_data = data; 292acc1a9efSDag-Erling Smørgrav proto[0] = data[0] = xauthfile[0] = xauthdir[0] = '\0'; 293043840dfSDag-Erling Smørgrav 294acc1a9efSDag-Erling Smørgrav if (!client_x11_display_valid(display)) { 295acc1a9efSDag-Erling Smørgrav if (display != NULL) 296acc1a9efSDag-Erling Smørgrav logit("DISPLAY \"%s\" invalid; disabling X11 forwarding", 297462c32cbSDag-Erling Smørgrav display); 298acc1a9efSDag-Erling Smørgrav return -1; 299043840dfSDag-Erling Smørgrav } 300acc1a9efSDag-Erling Smørgrav if (xauth_path != NULL && stat(xauth_path, &st) == -1) { 301acc1a9efSDag-Erling Smørgrav debug("No xauth program."); 302acc1a9efSDag-Erling Smørgrav xauth_path = NULL; 303acc1a9efSDag-Erling Smørgrav } 304acc1a9efSDag-Erling Smørgrav 305acc1a9efSDag-Erling Smørgrav if (xauth_path != NULL) { 306043840dfSDag-Erling Smørgrav /* 307043840dfSDag-Erling Smørgrav * Handle FamilyLocal case where $DISPLAY does 308043840dfSDag-Erling Smørgrav * not match an authorization entry. For this we 309043840dfSDag-Erling Smørgrav * just try "xauth list unix:displaynum.screennum". 310043840dfSDag-Erling Smørgrav * XXX: "localhost" match to determine FamilyLocal 311043840dfSDag-Erling Smørgrav * is not perfect. 312043840dfSDag-Erling Smørgrav */ 313043840dfSDag-Erling Smørgrav if (strncmp(display, "localhost:", 10) == 0) { 314acc1a9efSDag-Erling Smørgrav if ((r = snprintf(xdisplay, sizeof(xdisplay), "unix:%s", 315acc1a9efSDag-Erling Smørgrav display + 10)) < 0 || 316acc1a9efSDag-Erling Smørgrav (size_t)r >= sizeof(xdisplay)) { 317acc1a9efSDag-Erling Smørgrav error("%s: display name too long", __func__); 318acc1a9efSDag-Erling Smørgrav return -1; 319acc1a9efSDag-Erling Smørgrav } 320043840dfSDag-Erling Smørgrav display = xdisplay; 321043840dfSDag-Erling Smørgrav } 322043840dfSDag-Erling Smørgrav if (trusted == 0) { 323557f75e5SDag-Erling Smørgrav /* 324acc1a9efSDag-Erling Smørgrav * Generate an untrusted X11 auth cookie. 325acc1a9efSDag-Erling Smørgrav * 326557f75e5SDag-Erling Smørgrav * The authentication cookie should briefly outlive 327557f75e5SDag-Erling Smørgrav * ssh's willingness to forward X11 connections to 328557f75e5SDag-Erling Smørgrav * avoid nasty fail-open behaviour in the X server. 329557f75e5SDag-Erling Smørgrav */ 330acc1a9efSDag-Erling Smørgrav mktemp_proto(xauthdir, sizeof(xauthdir)); 331acc1a9efSDag-Erling Smørgrav if (mkdtemp(xauthdir) == NULL) { 332acc1a9efSDag-Erling Smørgrav error("%s: mkdtemp: %s", 333acc1a9efSDag-Erling Smørgrav __func__, strerror(errno)); 334acc1a9efSDag-Erling Smørgrav return -1; 335acc1a9efSDag-Erling Smørgrav } 336acc1a9efSDag-Erling Smørgrav do_unlink = 1; 337acc1a9efSDag-Erling Smørgrav if ((r = snprintf(xauthfile, sizeof(xauthfile), 338acc1a9efSDag-Erling Smørgrav "%s/xauthfile", xauthdir)) < 0 || 339acc1a9efSDag-Erling Smørgrav (size_t)r >= sizeof(xauthfile)) { 340acc1a9efSDag-Erling Smørgrav error("%s: xauthfile path too long", __func__); 341acc1a9efSDag-Erling Smørgrav unlink(xauthfile); 342acc1a9efSDag-Erling Smørgrav rmdir(xauthdir); 343acc1a9efSDag-Erling Smørgrav return -1; 344acc1a9efSDag-Erling Smørgrav } 345acc1a9efSDag-Erling Smørgrav 346557f75e5SDag-Erling Smørgrav if (timeout >= UINT_MAX - X11_TIMEOUT_SLACK) 347557f75e5SDag-Erling Smørgrav x11_timeout_real = UINT_MAX; 348557f75e5SDag-Erling Smørgrav else 349557f75e5SDag-Erling Smørgrav x11_timeout_real = timeout + X11_TIMEOUT_SLACK; 350acc1a9efSDag-Erling Smørgrav if ((r = snprintf(cmd, sizeof(cmd), 351043840dfSDag-Erling Smørgrav "%s -f %s generate %s " SSH_X11_PROTO 352e2f6069cSDag-Erling Smørgrav " untrusted timeout %u 2>" _PATH_DEVNULL, 353557f75e5SDag-Erling Smørgrav xauth_path, xauthfile, display, 354acc1a9efSDag-Erling Smørgrav x11_timeout_real)) < 0 || 355acc1a9efSDag-Erling Smørgrav (size_t)r >= sizeof(cmd)) 356acc1a9efSDag-Erling Smørgrav fatal("%s: cmd too long", __func__); 357acc1a9efSDag-Erling Smørgrav debug2("%s: %s", __func__, cmd); 358e2f6069cSDag-Erling Smørgrav if (x11_refuse_time == 0) { 359e4a9863fSDag-Erling Smørgrav now = monotime() + 1; 360e2f6069cSDag-Erling Smørgrav if (UINT_MAX - timeout < now) 361e2f6069cSDag-Erling Smørgrav x11_refuse_time = UINT_MAX; 362e2f6069cSDag-Erling Smørgrav else 363e2f6069cSDag-Erling Smørgrav x11_refuse_time = now + timeout; 3644f52dfbbSDag-Erling Smørgrav channel_set_x11_refuse_time(ssh, 3654f52dfbbSDag-Erling Smørgrav x11_refuse_time); 366e2f6069cSDag-Erling Smørgrav } 367557f75e5SDag-Erling Smørgrav if (system(cmd) == 0) 368557f75e5SDag-Erling Smørgrav generated = 1; 369043840dfSDag-Erling Smørgrav } 370d4af9e69SDag-Erling Smørgrav 371d4af9e69SDag-Erling Smørgrav /* 372d4af9e69SDag-Erling Smørgrav * When in untrusted mode, we read the cookie only if it was 373d4af9e69SDag-Erling Smørgrav * successfully generated as an untrusted one in the step 374d4af9e69SDag-Erling Smørgrav * above. 375d4af9e69SDag-Erling Smørgrav */ 376d4af9e69SDag-Erling Smørgrav if (trusted || generated) { 377043840dfSDag-Erling Smørgrav snprintf(cmd, sizeof(cmd), 378021d409fSDag-Erling Smørgrav "%s %s%s list %s 2>" _PATH_DEVNULL, 379043840dfSDag-Erling Smørgrav xauth_path, 380043840dfSDag-Erling Smørgrav generated ? "-f " : "" , 381043840dfSDag-Erling Smørgrav generated ? xauthfile : "", 382043840dfSDag-Erling Smørgrav display); 383043840dfSDag-Erling Smørgrav debug2("x11_get_proto: %s", cmd); 384043840dfSDag-Erling Smørgrav f = popen(cmd, "r"); 385043840dfSDag-Erling Smørgrav if (f && fgets(line, sizeof(line), f) && 386043840dfSDag-Erling Smørgrav sscanf(line, "%*s %511s %511s", proto, data) == 2) 387043840dfSDag-Erling Smørgrav got_data = 1; 388043840dfSDag-Erling Smørgrav if (f) 389043840dfSDag-Erling Smørgrav pclose(f); 390acc1a9efSDag-Erling Smørgrav } 391043840dfSDag-Erling Smørgrav } 392043840dfSDag-Erling Smørgrav 393043840dfSDag-Erling Smørgrav if (do_unlink) { 394043840dfSDag-Erling Smørgrav unlink(xauthfile); 395043840dfSDag-Erling Smørgrav rmdir(xauthdir); 396043840dfSDag-Erling Smørgrav } 397acc1a9efSDag-Erling Smørgrav 398acc1a9efSDag-Erling Smørgrav /* Don't fall back to fake X11 data for untrusted forwarding */ 399acc1a9efSDag-Erling Smørgrav if (!trusted && !got_data) { 400acc1a9efSDag-Erling Smørgrav error("Warning: untrusted X11 forwarding setup failed: " 401acc1a9efSDag-Erling Smørgrav "xauth key data not generated"); 402acc1a9efSDag-Erling Smørgrav return -1; 403acc1a9efSDag-Erling Smørgrav } 404043840dfSDag-Erling Smørgrav 405043840dfSDag-Erling Smørgrav /* 406043840dfSDag-Erling Smørgrav * If we didn't get authentication data, just make up some 407043840dfSDag-Erling Smørgrav * data. The forwarding code will check the validity of the 408043840dfSDag-Erling Smørgrav * response anyway, and substitute this data. The X11 409043840dfSDag-Erling Smørgrav * server, however, will ignore this fake data and use 410043840dfSDag-Erling Smørgrav * whatever authentication mechanisms it was using otherwise 411043840dfSDag-Erling Smørgrav * for the local connection. 412043840dfSDag-Erling Smørgrav */ 413043840dfSDag-Erling Smørgrav if (!got_data) { 414ca86bcf2SDag-Erling Smørgrav u_int8_t rnd[16]; 415ca86bcf2SDag-Erling Smørgrav u_int i; 416043840dfSDag-Erling Smørgrav 417043840dfSDag-Erling Smørgrav logit("Warning: No xauth data; " 418043840dfSDag-Erling Smørgrav "using fake authentication data for X11 forwarding."); 419043840dfSDag-Erling Smørgrav strlcpy(proto, SSH_X11_PROTO, sizeof proto); 420ca86bcf2SDag-Erling Smørgrav arc4random_buf(rnd, sizeof(rnd)); 421ca86bcf2SDag-Erling Smørgrav for (i = 0; i < sizeof(rnd); i++) { 422043840dfSDag-Erling Smørgrav snprintf(data + 2 * i, sizeof data - 2 * i, "%02x", 423ca86bcf2SDag-Erling Smørgrav rnd[i]); 424043840dfSDag-Erling Smørgrav } 425043840dfSDag-Erling Smørgrav } 426acc1a9efSDag-Erling Smørgrav 427acc1a9efSDag-Erling Smørgrav return 0; 428043840dfSDag-Erling Smørgrav } 429043840dfSDag-Erling Smørgrav 430511b41d2SMark Murray /* 431511b41d2SMark Murray * Checks if the client window has changed, and sends a packet about it to 432511b41d2SMark Murray * the server if so. The actual change is detected elsewhere (by a software 433511b41d2SMark Murray * interrupt on Unix); this just checks the flag and sends a message if 434511b41d2SMark Murray * appropriate. 435511b41d2SMark Murray */ 436511b41d2SMark Murray 437ae1f160dSDag-Erling Smørgrav static void 4384f52dfbbSDag-Erling Smørgrav client_check_window_change(struct ssh *ssh) 439511b41d2SMark Murray { 440a04a10f8SKris Kennaway if (!received_window_change_signal) 441a04a10f8SKris Kennaway return; 442a04a10f8SKris Kennaway /** XXX race */ 443511b41d2SMark Murray received_window_change_signal = 0; 444511b41d2SMark Murray 4454f52dfbbSDag-Erling Smørgrav debug2("%s: changed", __func__); 446a04a10f8SKris Kennaway 4474f52dfbbSDag-Erling Smørgrav channel_send_window_changes(ssh); 448511b41d2SMark Murray } 449511b41d2SMark Murray 450bc5531deSDag-Erling Smørgrav static int 4514f52dfbbSDag-Erling Smørgrav client_global_request_reply(int type, u_int32_t seq, struct ssh *ssh) 452efcad6b7SDag-Erling Smørgrav { 453d4af9e69SDag-Erling Smørgrav struct global_confirm *gc; 454d4af9e69SDag-Erling Smørgrav 455d4af9e69SDag-Erling Smørgrav if ((gc = TAILQ_FIRST(&global_confirms)) == NULL) 456bc5531deSDag-Erling Smørgrav return 0; 457d4af9e69SDag-Erling Smørgrav if (gc->cb != NULL) 4584f52dfbbSDag-Erling Smørgrav gc->cb(ssh, type, seq, gc->ctx); 459d4af9e69SDag-Erling Smørgrav if (--gc->ref_count <= 0) { 460d4af9e69SDag-Erling Smørgrav TAILQ_REMOVE(&global_confirms, gc, entry); 461b83788ffSDag-Erling Smørgrav explicit_bzero(gc, sizeof(*gc)); 462e4a9863fSDag-Erling Smørgrav free(gc); 463d4af9e69SDag-Erling Smørgrav } 464d4af9e69SDag-Erling Smørgrav 4657aee6ffeSDag-Erling Smørgrav packet_set_alive_timeouts(0); 466bc5531deSDag-Erling Smørgrav return 0; 467efcad6b7SDag-Erling Smørgrav } 468efcad6b7SDag-Erling Smørgrav 469efcad6b7SDag-Erling Smørgrav static void 470efcad6b7SDag-Erling Smørgrav server_alive_check(void) 471efcad6b7SDag-Erling Smørgrav { 4727aee6ffeSDag-Erling Smørgrav if (packet_inc_alive_timeouts() > options.server_alive_count_max) { 4734a421b63SDag-Erling Smørgrav logit("Timeout, server %s not responding.", host); 47492eb0aa1SDag-Erling Smørgrav cleanup_exit(255); 47592eb0aa1SDag-Erling Smørgrav } 476efcad6b7SDag-Erling Smørgrav packet_start(SSH2_MSG_GLOBAL_REQUEST); 477efcad6b7SDag-Erling Smørgrav packet_put_cstring("keepalive@openssh.com"); 478efcad6b7SDag-Erling Smørgrav packet_put_char(1); /* boolean: want reply */ 479efcad6b7SDag-Erling Smørgrav packet_send(); 480d4af9e69SDag-Erling Smørgrav /* Insert an empty placeholder to maintain ordering */ 481d4af9e69SDag-Erling Smørgrav client_register_global_confirm(NULL, NULL); 482efcad6b7SDag-Erling Smørgrav } 483efcad6b7SDag-Erling Smørgrav 484511b41d2SMark Murray /* 485511b41d2SMark Murray * Waits until the client can do something (some data becomes available on 486511b41d2SMark Murray * one of the file descriptors). 487511b41d2SMark Murray */ 488ae1f160dSDag-Erling Smørgrav static void 4894f52dfbbSDag-Erling Smørgrav client_wait_until_can_do_something(struct ssh *ssh, 4904f52dfbbSDag-Erling Smørgrav fd_set **readsetp, fd_set **writesetp, 491d74d50a8SDag-Erling Smørgrav int *maxfdp, u_int *nallocp, int rekeying) 492511b41d2SMark Murray { 493efcad6b7SDag-Erling Smørgrav struct timeval tv, *tvp; 494e2f6069cSDag-Erling Smørgrav int timeout_secs; 495e4a9863fSDag-Erling Smørgrav time_t minwait_secs = 0, server_alive_time = 0, now = monotime(); 496efcad6b7SDag-Erling Smørgrav int ret; 497efcad6b7SDag-Erling Smørgrav 4981e8db6e2SBrian Feldman /* Add any selections by the channel mechanism. */ 4994f52dfbbSDag-Erling Smørgrav channel_prepare_select(active_state, readsetp, writesetp, maxfdp, 5004f52dfbbSDag-Erling Smørgrav nallocp, &minwait_secs); 501511b41d2SMark Murray 502ae1f160dSDag-Erling Smørgrav /* channel_prepare_select could have closed the last channel */ 5034f52dfbbSDag-Erling Smørgrav if (session_closed && !channel_still_open(ssh) && 504ae1f160dSDag-Erling Smørgrav !packet_have_data_to_write()) { 505ae1f160dSDag-Erling Smørgrav /* clear mask since we did not call select() */ 506ae1f160dSDag-Erling Smørgrav memset(*readsetp, 0, *nallocp); 507ae1f160dSDag-Erling Smørgrav memset(*writesetp, 0, *nallocp); 508ae1f160dSDag-Erling Smørgrav return; 5094f52dfbbSDag-Erling Smørgrav } 5104f52dfbbSDag-Erling Smørgrav 5111e8db6e2SBrian Feldman FD_SET(connection_in, *readsetp); 512511b41d2SMark Murray 513511b41d2SMark Murray /* Select server connection if have data to write to the server. */ 514511b41d2SMark Murray if (packet_have_data_to_write()) 5151e8db6e2SBrian Feldman FD_SET(connection_out, *writesetp); 516511b41d2SMark Murray 517511b41d2SMark Murray /* 518511b41d2SMark Murray * Wait for something to happen. This will suspend the process until 519511b41d2SMark Murray * some selected descriptor can be read, written, or has some other 520e2f6069cSDag-Erling Smørgrav * event pending, or a timeout expires. 521511b41d2SMark Murray */ 522511b41d2SMark Murray 523e2f6069cSDag-Erling Smørgrav timeout_secs = INT_MAX; /* we use INT_MAX to mean no timeout */ 5244f52dfbbSDag-Erling Smørgrav if (options.server_alive_interval > 0) { 525e2f6069cSDag-Erling Smørgrav timeout_secs = options.server_alive_interval; 526e4a9863fSDag-Erling Smørgrav server_alive_time = now + options.server_alive_interval; 527e4a9863fSDag-Erling Smørgrav } 5284f52dfbbSDag-Erling Smørgrav if (options.rekey_interval > 0 && !rekeying) 529ca86bcf2SDag-Erling Smørgrav timeout_secs = MINIMUM(timeout_secs, packet_get_rekey_timeout()); 5304f52dfbbSDag-Erling Smørgrav set_control_persist_exit_time(ssh); 531e2f6069cSDag-Erling Smørgrav if (control_persist_exit_time > 0) { 532ca86bcf2SDag-Erling Smørgrav timeout_secs = MINIMUM(timeout_secs, 533e4a9863fSDag-Erling Smørgrav control_persist_exit_time - now); 534e2f6069cSDag-Erling Smørgrav if (timeout_secs < 0) 535e2f6069cSDag-Erling Smørgrav timeout_secs = 0; 536e2f6069cSDag-Erling Smørgrav } 537462c32cbSDag-Erling Smørgrav if (minwait_secs != 0) 538ca86bcf2SDag-Erling Smørgrav timeout_secs = MINIMUM(timeout_secs, (int)minwait_secs); 539e2f6069cSDag-Erling Smørgrav if (timeout_secs == INT_MAX) 540efcad6b7SDag-Erling Smørgrav tvp = NULL; 541efcad6b7SDag-Erling Smørgrav else { 542e2f6069cSDag-Erling Smørgrav tv.tv_sec = timeout_secs; 543efcad6b7SDag-Erling Smørgrav tv.tv_usec = 0; 544efcad6b7SDag-Erling Smørgrav tvp = &tv; 545efcad6b7SDag-Erling Smørgrav } 546e2f6069cSDag-Erling Smørgrav 547efcad6b7SDag-Erling Smørgrav ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp); 548efcad6b7SDag-Erling Smørgrav if (ret < 0) { 549511b41d2SMark Murray char buf[100]; 5501e8db6e2SBrian Feldman 5511e8db6e2SBrian Feldman /* 5521e8db6e2SBrian Feldman * We have to clear the select masks, because we return. 5531e8db6e2SBrian Feldman * We have to return, because the mainloop checks for the flags 5541e8db6e2SBrian Feldman * set by the signal handlers. 5551e8db6e2SBrian Feldman */ 556ae1f160dSDag-Erling Smørgrav memset(*readsetp, 0, *nallocp); 557ae1f160dSDag-Erling Smørgrav memset(*writesetp, 0, *nallocp); 5581e8db6e2SBrian Feldman 559511b41d2SMark Murray if (errno == EINTR) 560511b41d2SMark Murray return; 561511b41d2SMark Murray /* Note: we might still have data in the buffers. */ 562511b41d2SMark Murray snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno)); 563511b41d2SMark Murray buffer_append(&stderr_buffer, buf, strlen(buf)); 564511b41d2SMark Murray quit_pending = 1; 565e4a9863fSDag-Erling Smørgrav } else if (ret == 0) { 566e4a9863fSDag-Erling Smørgrav /* 567e4a9863fSDag-Erling Smørgrav * Timeout. Could have been either keepalive or rekeying. 568e4a9863fSDag-Erling Smørgrav * Keepalive we check here, rekeying is checked in clientloop. 569e4a9863fSDag-Erling Smørgrav */ 570e4a9863fSDag-Erling Smørgrav if (server_alive_time != 0 && server_alive_time <= monotime()) 571efcad6b7SDag-Erling Smørgrav server_alive_check(); 572511b41d2SMark Murray } 573511b41d2SMark Murray 574e4a9863fSDag-Erling Smørgrav } 575e4a9863fSDag-Erling Smørgrav 576ae1f160dSDag-Erling Smørgrav static void 577b66f2d16SKris Kennaway client_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr) 578511b41d2SMark Murray { 579511b41d2SMark Murray /* Flush stdout and stderr buffers. */ 580b66f2d16SKris Kennaway if (buffer_len(bout) > 0) 581d4af9e69SDag-Erling Smørgrav atomicio(vwrite, fileno(stdout), buffer_ptr(bout), 582d4af9e69SDag-Erling Smørgrav buffer_len(bout)); 583b66f2d16SKris Kennaway if (buffer_len(berr) > 0) 584d4af9e69SDag-Erling Smørgrav atomicio(vwrite, fileno(stderr), buffer_ptr(berr), 585d4af9e69SDag-Erling Smørgrav buffer_len(berr)); 586511b41d2SMark Murray 587e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 588511b41d2SMark Murray 5894f52dfbbSDag-Erling Smørgrav sshbuf_reset(bin); 5904f52dfbbSDag-Erling Smørgrav sshbuf_reset(bout); 5914f52dfbbSDag-Erling Smørgrav sshbuf_reset(berr); 592511b41d2SMark Murray 593511b41d2SMark Murray /* Send the suspend signal to the program itself. */ 594511b41d2SMark Murray kill(getpid(), SIGTSTP); 595511b41d2SMark Murray 5965e8dbd04SDag-Erling Smørgrav /* Reset window sizes in case they have changed */ 597511b41d2SMark Murray received_window_change_signal = 1; 598511b41d2SMark Murray 599e146993eSDag-Erling Smørgrav enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 600511b41d2SMark Murray } 601511b41d2SMark Murray 602ae1f160dSDag-Erling Smørgrav static void 603a04a10f8SKris Kennaway client_process_net_input(fd_set *readset) 604511b41d2SMark Murray { 605acc1a9efSDag-Erling Smørgrav int len; 6067aee6ffeSDag-Erling Smørgrav char buf[SSH_IOBUFSZ]; 607511b41d2SMark Murray 608511b41d2SMark Murray /* 609511b41d2SMark Murray * Read input from the server, and add any such data to the buffer of 610511b41d2SMark Murray * the packet subsystem. 611511b41d2SMark Murray */ 612511b41d2SMark Murray if (FD_ISSET(connection_in, readset)) { 613511b41d2SMark Murray /* Read as much as possible. */ 614acc1a9efSDag-Erling Smørgrav len = read(connection_in, buf, sizeof(buf)); 615acc1a9efSDag-Erling Smørgrav if (len == 0) { 616d4af9e69SDag-Erling Smørgrav /* 617d4af9e69SDag-Erling Smørgrav * Received EOF. The remote host has closed the 618d4af9e69SDag-Erling Smørgrav * connection. 619d4af9e69SDag-Erling Smørgrav */ 620d4af9e69SDag-Erling Smørgrav snprintf(buf, sizeof buf, 621d4af9e69SDag-Erling Smørgrav "Connection to %.300s closed by remote host.\r\n", 622511b41d2SMark Murray host); 623511b41d2SMark Murray buffer_append(&stderr_buffer, buf, strlen(buf)); 624511b41d2SMark Murray quit_pending = 1; 625511b41d2SMark Murray return; 626511b41d2SMark Murray } 627511b41d2SMark Murray /* 628511b41d2SMark Murray * There is a kernel bug on Solaris that causes select to 629511b41d2SMark Murray * sometimes wake up even though there is no data available. 630511b41d2SMark Murray */ 631d4af9e69SDag-Erling Smørgrav if (len < 0 && 632d4af9e69SDag-Erling Smørgrav (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)) 633511b41d2SMark Murray len = 0; 634511b41d2SMark Murray 635511b41d2SMark Murray if (len < 0) { 636d4af9e69SDag-Erling Smørgrav /* 637d4af9e69SDag-Erling Smørgrav * An error has encountered. Perhaps there is a 638d4af9e69SDag-Erling Smørgrav * network problem. 639d4af9e69SDag-Erling Smørgrav */ 640d4af9e69SDag-Erling Smørgrav snprintf(buf, sizeof buf, 641d4af9e69SDag-Erling Smørgrav "Read from remote host %.300s: %.100s\r\n", 642511b41d2SMark Murray host, strerror(errno)); 643511b41d2SMark Murray buffer_append(&stderr_buffer, buf, strlen(buf)); 644511b41d2SMark Murray quit_pending = 1; 645511b41d2SMark Murray return; 646511b41d2SMark Murray } 647511b41d2SMark Murray packet_process_incoming(buf, len); 648511b41d2SMark Murray } 649a04a10f8SKris Kennaway } 650a04a10f8SKris Kennaway 651545d5ecaSDag-Erling Smørgrav static void 6524f52dfbbSDag-Erling Smørgrav client_status_confirm(struct ssh *ssh, int type, Channel *c, void *ctx) 653d74d50a8SDag-Erling Smørgrav { 654d4af9e69SDag-Erling Smørgrav struct channel_reply_ctx *cr = (struct channel_reply_ctx *)ctx; 655d4af9e69SDag-Erling Smørgrav char errmsg[256]; 656d4af9e69SDag-Erling Smørgrav int tochan; 657d74d50a8SDag-Erling Smørgrav 658e146993eSDag-Erling Smørgrav /* 659e146993eSDag-Erling Smørgrav * If a TTY was explicitly requested, then a failure to allocate 660e146993eSDag-Erling Smørgrav * one is fatal. 661e146993eSDag-Erling Smørgrav */ 662e146993eSDag-Erling Smørgrav if (cr->action == CONFIRM_TTY && 663e146993eSDag-Erling Smørgrav (options.request_tty == REQUEST_TTY_FORCE || 664e146993eSDag-Erling Smørgrav options.request_tty == REQUEST_TTY_YES)) 665e146993eSDag-Erling Smørgrav cr->action = CONFIRM_CLOSE; 666e146993eSDag-Erling Smørgrav 667d4af9e69SDag-Erling Smørgrav /* XXX supress on mux _client_ quietmode */ 668d4af9e69SDag-Erling Smørgrav tochan = options.log_level >= SYSLOG_LEVEL_ERROR && 669b15c8340SDag-Erling Smørgrav c->ctl_chan != -1 && c->extended_usage == CHAN_EXTENDED_WRITE; 670d74d50a8SDag-Erling Smørgrav 671d4af9e69SDag-Erling Smørgrav if (type == SSH2_MSG_CHANNEL_SUCCESS) { 672d4af9e69SDag-Erling Smørgrav debug2("%s request accepted on channel %d", 673d4af9e69SDag-Erling Smørgrav cr->request_type, c->self); 674d4af9e69SDag-Erling Smørgrav } else if (type == SSH2_MSG_CHANNEL_FAILURE) { 675d4af9e69SDag-Erling Smørgrav if (tochan) { 676d4af9e69SDag-Erling Smørgrav snprintf(errmsg, sizeof(errmsg), 677d4af9e69SDag-Erling Smørgrav "%s request failed\r\n", cr->request_type); 678d4af9e69SDag-Erling Smørgrav } else { 679d4af9e69SDag-Erling Smørgrav snprintf(errmsg, sizeof(errmsg), 680d4af9e69SDag-Erling Smørgrav "%s request failed on channel %d", 681d4af9e69SDag-Erling Smørgrav cr->request_type, c->self); 682d74d50a8SDag-Erling Smørgrav } 683d4af9e69SDag-Erling Smørgrav /* If error occurred on primary session channel, then exit */ 684e146993eSDag-Erling Smørgrav if (cr->action == CONFIRM_CLOSE && c->self == session_ident) 685d4af9e69SDag-Erling Smørgrav fatal("%s", errmsg); 686e146993eSDag-Erling Smørgrav /* 687e146993eSDag-Erling Smørgrav * If error occurred on mux client, append to 688e146993eSDag-Erling Smørgrav * their stderr. 689e146993eSDag-Erling Smørgrav */ 690e146993eSDag-Erling Smørgrav if (tochan) { 6914f52dfbbSDag-Erling Smørgrav buffer_append(c->extended, errmsg, strlen(errmsg)); 692e146993eSDag-Erling Smørgrav } else 693d4af9e69SDag-Erling Smørgrav error("%s", errmsg); 694e146993eSDag-Erling Smørgrav if (cr->action == CONFIRM_TTY) { 695e146993eSDag-Erling Smørgrav /* 696e146993eSDag-Erling Smørgrav * If a TTY allocation error occurred, then arrange 697e146993eSDag-Erling Smørgrav * for the correct TTY to leave raw mode. 698e146993eSDag-Erling Smørgrav */ 699e146993eSDag-Erling Smørgrav if (c->self == session_ident) 700e146993eSDag-Erling Smørgrav leave_raw_mode(0); 701e146993eSDag-Erling Smørgrav else 7024f52dfbbSDag-Erling Smørgrav mux_tty_alloc_failed(ssh, c); 703e146993eSDag-Erling Smørgrav } else if (cr->action == CONFIRM_CLOSE) { 7044f52dfbbSDag-Erling Smørgrav chan_read_failed(ssh, c); 7054f52dfbbSDag-Erling Smørgrav chan_write_failed(ssh, c); 706d74d50a8SDag-Erling Smørgrav } 707d74d50a8SDag-Erling Smørgrav } 708e4a9863fSDag-Erling Smørgrav free(cr); 709d4af9e69SDag-Erling Smørgrav } 710d74d50a8SDag-Erling Smørgrav 711d74d50a8SDag-Erling Smørgrav static void 7124f52dfbbSDag-Erling Smørgrav client_abandon_status_confirm(struct ssh *ssh, Channel *c, void *ctx) 713d74d50a8SDag-Erling Smørgrav { 714e4a9863fSDag-Erling Smørgrav free(ctx); 715d74d50a8SDag-Erling Smørgrav } 716d74d50a8SDag-Erling Smørgrav 717e146993eSDag-Erling Smørgrav void 7184f52dfbbSDag-Erling Smørgrav client_expect_confirm(struct ssh *ssh, int id, const char *request, 719e146993eSDag-Erling Smørgrav enum confirm_action action) 720d74d50a8SDag-Erling Smørgrav { 7210a37d4a3SXin LI struct channel_reply_ctx *cr = xcalloc(1, sizeof(*cr)); 722d74d50a8SDag-Erling Smørgrav 723d4af9e69SDag-Erling Smørgrav cr->request_type = request; 724e146993eSDag-Erling Smørgrav cr->action = action; 725d74d50a8SDag-Erling Smørgrav 7264f52dfbbSDag-Erling Smørgrav channel_register_status_confirm(ssh, id, client_status_confirm, 727d4af9e69SDag-Erling Smørgrav client_abandon_status_confirm, cr); 728d4af9e69SDag-Erling Smørgrav } 729d4af9e69SDag-Erling Smørgrav 730d4af9e69SDag-Erling Smørgrav void 731d4af9e69SDag-Erling Smørgrav client_register_global_confirm(global_confirm_cb *cb, void *ctx) 732d4af9e69SDag-Erling Smørgrav { 733d4af9e69SDag-Erling Smørgrav struct global_confirm *gc, *last_gc; 734d4af9e69SDag-Erling Smørgrav 735d4af9e69SDag-Erling Smørgrav /* Coalesce identical callbacks */ 736d4af9e69SDag-Erling Smørgrav last_gc = TAILQ_LAST(&global_confirms, global_confirms); 737d4af9e69SDag-Erling Smørgrav if (last_gc && last_gc->cb == cb && last_gc->ctx == ctx) { 738d4af9e69SDag-Erling Smørgrav if (++last_gc->ref_count >= INT_MAX) 739d4af9e69SDag-Erling Smørgrav fatal("%s: last_gc->ref_count = %d", 740d4af9e69SDag-Erling Smørgrav __func__, last_gc->ref_count); 741d74d50a8SDag-Erling Smørgrav return; 742d74d50a8SDag-Erling Smørgrav } 743d74d50a8SDag-Erling Smørgrav 7440a37d4a3SXin LI gc = xcalloc(1, sizeof(*gc)); 745d4af9e69SDag-Erling Smørgrav gc->cb = cb; 746d4af9e69SDag-Erling Smørgrav gc->ctx = ctx; 747d4af9e69SDag-Erling Smørgrav gc->ref_count = 1; 748d4af9e69SDag-Erling Smørgrav TAILQ_INSERT_TAIL(&global_confirms, gc, entry); 749d74d50a8SDag-Erling Smørgrav } 750d74d50a8SDag-Erling Smørgrav 751d74d50a8SDag-Erling Smørgrav static void 7524f52dfbbSDag-Erling Smørgrav process_cmdline(struct ssh *ssh) 753545d5ecaSDag-Erling Smørgrav { 754545d5ecaSDag-Erling Smørgrav void (*handler)(int); 755a0ee8cc6SDag-Erling Smørgrav char *s, *cmd; 756a0ee8cc6SDag-Erling Smørgrav int ok, delete = 0, local = 0, remote = 0, dynamic = 0; 757a0ee8cc6SDag-Erling Smørgrav struct Forward fwd; 758545d5ecaSDag-Erling Smørgrav 759b83788ffSDag-Erling Smørgrav memset(&fwd, 0, sizeof(fwd)); 760d4af9e69SDag-Erling Smørgrav 761e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 762545d5ecaSDag-Erling Smørgrav handler = signal(SIGINT, SIG_IGN); 763545d5ecaSDag-Erling Smørgrav cmd = s = read_passphrase("\r\nssh> ", RP_ECHO); 764545d5ecaSDag-Erling Smørgrav if (s == NULL) 765545d5ecaSDag-Erling Smørgrav goto out; 766f7167e0eSDag-Erling Smørgrav while (isspace((u_char)*s)) 767545d5ecaSDag-Erling Smørgrav s++; 768d74d50a8SDag-Erling Smørgrav if (*s == '-') 769d74d50a8SDag-Erling Smørgrav s++; /* Skip cmdline '-', if any */ 770d74d50a8SDag-Erling Smørgrav if (*s == '\0') 771545d5ecaSDag-Erling Smørgrav goto out; 772d74d50a8SDag-Erling Smørgrav 773d74d50a8SDag-Erling Smørgrav if (*s == 'h' || *s == 'H' || *s == '?') { 774d74d50a8SDag-Erling Smørgrav logit("Commands:"); 775761efaa7SDag-Erling Smørgrav logit(" -L[bind_address:]port:host:hostport " 776761efaa7SDag-Erling Smørgrav "Request local forward"); 777761efaa7SDag-Erling Smørgrav logit(" -R[bind_address:]port:host:hostport " 778761efaa7SDag-Erling Smørgrav "Request remote forward"); 779cce7d346SDag-Erling Smørgrav logit(" -D[bind_address:]port " 780cce7d346SDag-Erling Smørgrav "Request dynamic forward"); 781462c32cbSDag-Erling Smørgrav logit(" -KL[bind_address:]port " 782462c32cbSDag-Erling Smørgrav "Cancel local forward"); 783761efaa7SDag-Erling Smørgrav logit(" -KR[bind_address:]port " 784761efaa7SDag-Erling Smørgrav "Cancel remote forward"); 785462c32cbSDag-Erling Smørgrav logit(" -KD[bind_address:]port " 786462c32cbSDag-Erling Smørgrav "Cancel dynamic forward"); 787021d409fSDag-Erling Smørgrav if (!options.permit_local_command) 788021d409fSDag-Erling Smørgrav goto out; 789761efaa7SDag-Erling Smørgrav logit(" !args " 790761efaa7SDag-Erling Smørgrav "Execute local command"); 791021d409fSDag-Erling Smørgrav goto out; 792021d409fSDag-Erling Smørgrav } 793021d409fSDag-Erling Smørgrav 794021d409fSDag-Erling Smørgrav if (*s == '!' && options.permit_local_command) { 795021d409fSDag-Erling Smørgrav s++; 796021d409fSDag-Erling Smørgrav ssh_local_cmd(s); 797d74d50a8SDag-Erling Smørgrav goto out; 798d74d50a8SDag-Erling Smørgrav } 799d74d50a8SDag-Erling Smørgrav 800d74d50a8SDag-Erling Smørgrav if (*s == 'K') { 801d74d50a8SDag-Erling Smørgrav delete = 1; 802d74d50a8SDag-Erling Smørgrav s++; 803d74d50a8SDag-Erling Smørgrav } 804cce7d346SDag-Erling Smørgrav if (*s == 'L') 805cce7d346SDag-Erling Smørgrav local = 1; 806cce7d346SDag-Erling Smørgrav else if (*s == 'R') 807cce7d346SDag-Erling Smørgrav remote = 1; 808cce7d346SDag-Erling Smørgrav else if (*s == 'D') 809cce7d346SDag-Erling Smørgrav dynamic = 1; 810cce7d346SDag-Erling Smørgrav else { 811d95e11bfSDag-Erling Smørgrav logit("Invalid command."); 812545d5ecaSDag-Erling Smørgrav goto out; 813545d5ecaSDag-Erling Smørgrav } 814cce7d346SDag-Erling Smørgrav 815f7167e0eSDag-Erling Smørgrav while (isspace((u_char)*++s)) 816d4af9e69SDag-Erling Smørgrav ; 817545d5ecaSDag-Erling Smørgrav 818b15c8340SDag-Erling Smørgrav /* XXX update list of forwards in options */ 819d74d50a8SDag-Erling Smørgrav if (delete) { 820a0ee8cc6SDag-Erling Smørgrav /* We pass 1 for dynamicfwd to restrict to 1 or 2 fields. */ 821a0ee8cc6SDag-Erling Smørgrav if (!parse_forward(&fwd, s, 1, 0)) { 822a0ee8cc6SDag-Erling Smørgrav logit("Bad forwarding close specification."); 823545d5ecaSDag-Erling Smørgrav goto out; 824545d5ecaSDag-Erling Smørgrav } 825462c32cbSDag-Erling Smørgrav if (remote) 8264f52dfbbSDag-Erling Smørgrav ok = channel_request_rforward_cancel(ssh, &fwd) == 0; 827462c32cbSDag-Erling Smørgrav else if (dynamic) 8284f52dfbbSDag-Erling Smørgrav ok = channel_cancel_lport_listener(ssh, &fwd, 829a0ee8cc6SDag-Erling Smørgrav 0, &options.fwd_opts) > 0; 830462c32cbSDag-Erling Smørgrav else 8314f52dfbbSDag-Erling Smørgrav ok = channel_cancel_lport_listener(ssh, &fwd, 832a0ee8cc6SDag-Erling Smørgrav CHANNEL_CANCEL_PORT_STATIC, 833a0ee8cc6SDag-Erling Smørgrav &options.fwd_opts) > 0; 834462c32cbSDag-Erling Smørgrav if (!ok) { 835d93a896eSDag-Erling Smørgrav logit("Unknown port forwarding."); 836462c32cbSDag-Erling Smørgrav goto out; 837462c32cbSDag-Erling Smørgrav } 838462c32cbSDag-Erling Smørgrav logit("Canceled forwarding."); 8395e8dbd04SDag-Erling Smørgrav } else { 840cce7d346SDag-Erling Smørgrav if (!parse_forward(&fwd, s, dynamic, remote)) { 8415e8dbd04SDag-Erling Smørgrav logit("Bad forwarding specification."); 842545d5ecaSDag-Erling Smørgrav goto out; 843545d5ecaSDag-Erling Smørgrav } 844cce7d346SDag-Erling Smørgrav if (local || dynamic) { 8454f52dfbbSDag-Erling Smørgrav if (!channel_setup_local_fwd_listener(ssh, &fwd, 846a0ee8cc6SDag-Erling Smørgrav &options.fwd_opts)) { 847d95e11bfSDag-Erling Smørgrav logit("Port forwarding failed."); 848545d5ecaSDag-Erling Smørgrav goto out; 849545d5ecaSDag-Erling Smørgrav } 8505e8dbd04SDag-Erling Smørgrav } else { 8514f52dfbbSDag-Erling Smørgrav if (channel_request_remote_forwarding(ssh, &fwd) < 0) { 852761efaa7SDag-Erling Smørgrav logit("Port forwarding failed."); 853761efaa7SDag-Erling Smørgrav goto out; 854761efaa7SDag-Erling Smørgrav } 8555e8dbd04SDag-Erling Smørgrav } 856d95e11bfSDag-Erling Smørgrav logit("Forwarding port."); 857d74d50a8SDag-Erling Smørgrav } 858d74d50a8SDag-Erling Smørgrav 859545d5ecaSDag-Erling Smørgrav out: 860545d5ecaSDag-Erling Smørgrav signal(SIGINT, handler); 861e146993eSDag-Erling Smørgrav enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 862e4a9863fSDag-Erling Smørgrav free(cmd); 863e4a9863fSDag-Erling Smørgrav free(fwd.listen_host); 864a0ee8cc6SDag-Erling Smørgrav free(fwd.listen_path); 865e4a9863fSDag-Erling Smørgrav free(fwd.connect_host); 866a0ee8cc6SDag-Erling Smørgrav free(fwd.connect_path); 867545d5ecaSDag-Erling Smørgrav } 868545d5ecaSDag-Erling Smørgrav 8696888a9beSDag-Erling Smørgrav /* reasons to suppress output of an escape command in help output */ 8706888a9beSDag-Erling Smørgrav #define SUPPRESS_NEVER 0 /* never suppress, always show */ 8714f52dfbbSDag-Erling Smørgrav #define SUPPRESS_MUXCLIENT 1 /* don't show in mux client sessions */ 8724f52dfbbSDag-Erling Smørgrav #define SUPPRESS_MUXMASTER 2 /* don't show in mux master sessions */ 8734f52dfbbSDag-Erling Smørgrav #define SUPPRESS_SYSLOG 4 /* don't show when logging to syslog */ 8746888a9beSDag-Erling Smørgrav struct escape_help_text { 8756888a9beSDag-Erling Smørgrav const char *cmd; 8766888a9beSDag-Erling Smørgrav const char *text; 8776888a9beSDag-Erling Smørgrav unsigned int flags; 8786888a9beSDag-Erling Smørgrav }; 8796888a9beSDag-Erling Smørgrav static struct escape_help_text esc_txt[] = { 8806888a9beSDag-Erling Smørgrav {".", "terminate session", SUPPRESS_MUXMASTER}, 8816888a9beSDag-Erling Smørgrav {".", "terminate connection (and any multiplexed sessions)", 8826888a9beSDag-Erling Smørgrav SUPPRESS_MUXCLIENT}, 8834f52dfbbSDag-Erling Smørgrav {"B", "send a BREAK to the remote system", SUPPRESS_NEVER}, 8846888a9beSDag-Erling Smørgrav {"C", "open a command line", SUPPRESS_MUXCLIENT}, 8854f52dfbbSDag-Erling Smørgrav {"R", "request rekey", SUPPRESS_NEVER}, 8866888a9beSDag-Erling Smørgrav {"V/v", "decrease/increase verbosity (LogLevel)", SUPPRESS_MUXCLIENT}, 8876888a9beSDag-Erling Smørgrav {"^Z", "suspend ssh", SUPPRESS_MUXCLIENT}, 8886888a9beSDag-Erling Smørgrav {"#", "list forwarded connections", SUPPRESS_NEVER}, 8896888a9beSDag-Erling Smørgrav {"&", "background ssh (when waiting for connections to terminate)", 8906888a9beSDag-Erling Smørgrav SUPPRESS_MUXCLIENT}, 8916888a9beSDag-Erling Smørgrav {"?", "this message", SUPPRESS_NEVER}, 8926888a9beSDag-Erling Smørgrav }; 8936888a9beSDag-Erling Smørgrav 8946888a9beSDag-Erling Smørgrav static void 8954f52dfbbSDag-Erling Smørgrav print_escape_help(Buffer *b, int escape_char, int mux_client, int using_stderr) 8966888a9beSDag-Erling Smørgrav { 8976888a9beSDag-Erling Smørgrav unsigned int i, suppress_flags; 8986888a9beSDag-Erling Smørgrav char string[1024]; 8996888a9beSDag-Erling Smørgrav 9006888a9beSDag-Erling Smørgrav snprintf(string, sizeof string, "%c?\r\n" 9016888a9beSDag-Erling Smørgrav "Supported escape sequences:\r\n", escape_char); 9026888a9beSDag-Erling Smørgrav buffer_append(b, string, strlen(string)); 9036888a9beSDag-Erling Smørgrav 9044f52dfbbSDag-Erling Smørgrav suppress_flags = 9056888a9beSDag-Erling Smørgrav (mux_client ? SUPPRESS_MUXCLIENT : 0) | 9066888a9beSDag-Erling Smørgrav (mux_client ? 0 : SUPPRESS_MUXMASTER) | 9076888a9beSDag-Erling Smørgrav (using_stderr ? 0 : SUPPRESS_SYSLOG); 9086888a9beSDag-Erling Smørgrav 9096888a9beSDag-Erling Smørgrav for (i = 0; i < sizeof(esc_txt)/sizeof(esc_txt[0]); i++) { 9106888a9beSDag-Erling Smørgrav if (esc_txt[i].flags & suppress_flags) 9116888a9beSDag-Erling Smørgrav continue; 9126888a9beSDag-Erling Smørgrav snprintf(string, sizeof string, " %c%-3s - %s\r\n", 9136888a9beSDag-Erling Smørgrav escape_char, esc_txt[i].cmd, esc_txt[i].text); 9146888a9beSDag-Erling Smørgrav buffer_append(b, string, strlen(string)); 9156888a9beSDag-Erling Smørgrav } 9166888a9beSDag-Erling Smørgrav 9176888a9beSDag-Erling Smørgrav snprintf(string, sizeof string, 9186888a9beSDag-Erling Smørgrav " %c%c - send the escape character by typing it twice\r\n" 9196888a9beSDag-Erling Smørgrav "(Note that escapes are only recognized immediately after " 9206888a9beSDag-Erling Smørgrav "newline.)\r\n", escape_char, escape_char); 9216888a9beSDag-Erling Smørgrav buffer_append(b, string, strlen(string)); 9226888a9beSDag-Erling Smørgrav } 9236888a9beSDag-Erling Smørgrav 924d4af9e69SDag-Erling Smørgrav /* 9254f52dfbbSDag-Erling Smørgrav * Process the characters one by one. 926d4af9e69SDag-Erling Smørgrav */ 927ae1f160dSDag-Erling Smørgrav static int 9284f52dfbbSDag-Erling Smørgrav process_escapes(struct ssh *ssh, Channel *c, 9294f52dfbbSDag-Erling Smørgrav Buffer *bin, Buffer *bout, Buffer *berr, 930d4af9e69SDag-Erling Smørgrav char *buf, int len) 931b66f2d16SKris Kennaway { 932b66f2d16SKris Kennaway char string[1024]; 933b66f2d16SKris Kennaway pid_t pid; 934b66f2d16SKris Kennaway int bytes = 0; 9351e8db6e2SBrian Feldman u_int i; 9361e8db6e2SBrian Feldman u_char ch; 937b66f2d16SKris Kennaway char *s; 9384f52dfbbSDag-Erling Smørgrav struct escape_filter_ctx *efc = c->filter_ctx == NULL ? 9394f52dfbbSDag-Erling Smørgrav NULL : (struct escape_filter_ctx *)c->filter_ctx; 940d4af9e69SDag-Erling Smørgrav 941d4af9e69SDag-Erling Smørgrav if (c->filter_ctx == NULL) 942d4af9e69SDag-Erling Smørgrav return 0; 943b66f2d16SKris Kennaway 944043840dfSDag-Erling Smørgrav if (len <= 0) 945043840dfSDag-Erling Smørgrav return (0); 946043840dfSDag-Erling Smørgrav 947043840dfSDag-Erling Smørgrav for (i = 0; i < (u_int)len; i++) { 948b66f2d16SKris Kennaway /* Get one character at a time. */ 949b66f2d16SKris Kennaway ch = buf[i]; 950b66f2d16SKris Kennaway 9514f52dfbbSDag-Erling Smørgrav if (efc->escape_pending) { 952b66f2d16SKris Kennaway /* We have previously seen an escape character. */ 953b66f2d16SKris Kennaway /* Clear the flag now. */ 9544f52dfbbSDag-Erling Smørgrav efc->escape_pending = 0; 955b66f2d16SKris Kennaway 956b66f2d16SKris Kennaway /* Process the escaped character. */ 957b66f2d16SKris Kennaway switch (ch) { 958b66f2d16SKris Kennaway case '.': 959b66f2d16SKris Kennaway /* Terminate the connection. */ 960d4af9e69SDag-Erling Smørgrav snprintf(string, sizeof string, "%c.\r\n", 9614f52dfbbSDag-Erling Smørgrav efc->escape_char); 962b66f2d16SKris Kennaway buffer_append(berr, string, strlen(string)); 963b66f2d16SKris Kennaway 964b15c8340SDag-Erling Smørgrav if (c && c->ctl_chan != -1) { 9654f52dfbbSDag-Erling Smørgrav chan_read_failed(ssh, c); 9664f52dfbbSDag-Erling Smørgrav chan_write_failed(ssh, c); 9674f52dfbbSDag-Erling Smørgrav if (c->detach_user) { 9684f52dfbbSDag-Erling Smørgrav c->detach_user(ssh, 9694f52dfbbSDag-Erling Smørgrav c->self, NULL); 9704f52dfbbSDag-Erling Smørgrav } 971e4a9863fSDag-Erling Smørgrav c->type = SSH_CHANNEL_ABANDONED; 9724f52dfbbSDag-Erling Smørgrav buffer_clear(c->input); 9734f52dfbbSDag-Erling Smørgrav chan_ibuf_empty(ssh, c); 974d4af9e69SDag-Erling Smørgrav return 0; 975d4af9e69SDag-Erling Smørgrav } else 976b66f2d16SKris Kennaway quit_pending = 1; 977b66f2d16SKris Kennaway return -1; 978b66f2d16SKris Kennaway 979b66f2d16SKris Kennaway case 'Z' - 64: 980d4af9e69SDag-Erling Smørgrav /* XXX support this for mux clients */ 981b15c8340SDag-Erling Smørgrav if (c && c->ctl_chan != -1) { 9826888a9beSDag-Erling Smørgrav char b[16]; 983d4af9e69SDag-Erling Smørgrav noescape: 9846888a9beSDag-Erling Smørgrav if (ch == 'Z' - 64) 9856888a9beSDag-Erling Smørgrav snprintf(b, sizeof b, "^Z"); 9866888a9beSDag-Erling Smørgrav else 9876888a9beSDag-Erling Smørgrav snprintf(b, sizeof b, "%c", ch); 988d4af9e69SDag-Erling Smørgrav snprintf(string, sizeof string, 9896888a9beSDag-Erling Smørgrav "%c%s escape not available to " 990d4af9e69SDag-Erling Smørgrav "multiplexed sessions\r\n", 9914f52dfbbSDag-Erling Smørgrav efc->escape_char, b); 992d4af9e69SDag-Erling Smørgrav buffer_append(berr, string, 993d4af9e69SDag-Erling Smørgrav strlen(string)); 994d4af9e69SDag-Erling Smørgrav continue; 995d4af9e69SDag-Erling Smørgrav } 996d4af9e69SDag-Erling Smørgrav /* Suspend the program. Inform the user */ 997d4af9e69SDag-Erling Smørgrav snprintf(string, sizeof string, 9984f52dfbbSDag-Erling Smørgrav "%c^Z [suspend ssh]\r\n", efc->escape_char); 999b66f2d16SKris Kennaway buffer_append(berr, string, strlen(string)); 1000b66f2d16SKris Kennaway 1001b66f2d16SKris Kennaway /* Restore terminal modes and suspend. */ 1002b66f2d16SKris Kennaway client_suspend_self(bin, bout, berr); 1003b66f2d16SKris Kennaway 1004b66f2d16SKris Kennaway /* We have been continued. */ 1005b66f2d16SKris Kennaway continue; 1006b66f2d16SKris Kennaway 1007d95e11bfSDag-Erling Smørgrav case 'B': 1008d95e11bfSDag-Erling Smørgrav snprintf(string, sizeof string, 10094f52dfbbSDag-Erling Smørgrav "%cB\r\n", efc->escape_char); 10104f52dfbbSDag-Erling Smørgrav buffer_append(berr, string, strlen(string)); 10114f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, c->self, "break", 0); 1012d95e11bfSDag-Erling Smørgrav packet_put_int(1000); 1013d95e11bfSDag-Erling Smørgrav packet_send(); 1014d95e11bfSDag-Erling Smørgrav continue; 1015d95e11bfSDag-Erling Smørgrav 10161e8db6e2SBrian Feldman case 'R': 10171e8db6e2SBrian Feldman if (datafellows & SSH_BUG_NOREKEY) 1018d4af9e69SDag-Erling Smørgrav logit("Server does not " 1019d4af9e69SDag-Erling Smørgrav "support re-keying"); 10201e8db6e2SBrian Feldman else 10211e8db6e2SBrian Feldman need_rekeying = 1; 10221e8db6e2SBrian Feldman continue; 10231e8db6e2SBrian Feldman 10246888a9beSDag-Erling Smørgrav case 'V': 10256888a9beSDag-Erling Smørgrav /* FALLTHROUGH */ 10266888a9beSDag-Erling Smørgrav case 'v': 10276888a9beSDag-Erling Smørgrav if (c && c->ctl_chan != -1) 10286888a9beSDag-Erling Smørgrav goto noescape; 10296888a9beSDag-Erling Smørgrav if (!log_is_on_stderr()) { 10306888a9beSDag-Erling Smørgrav snprintf(string, sizeof string, 10316888a9beSDag-Erling Smørgrav "%c%c [Logging to syslog]\r\n", 10324f52dfbbSDag-Erling Smørgrav efc->escape_char, ch); 10336888a9beSDag-Erling Smørgrav buffer_append(berr, string, 10346888a9beSDag-Erling Smørgrav strlen(string)); 10356888a9beSDag-Erling Smørgrav continue; 10366888a9beSDag-Erling Smørgrav } 10376888a9beSDag-Erling Smørgrav if (ch == 'V' && options.log_level > 10386888a9beSDag-Erling Smørgrav SYSLOG_LEVEL_QUIET) 10396888a9beSDag-Erling Smørgrav log_change_level(--options.log_level); 10406888a9beSDag-Erling Smørgrav if (ch == 'v' && options.log_level < 10416888a9beSDag-Erling Smørgrav SYSLOG_LEVEL_DEBUG3) 10426888a9beSDag-Erling Smørgrav log_change_level(++options.log_level); 10436888a9beSDag-Erling Smørgrav snprintf(string, sizeof string, 10444f52dfbbSDag-Erling Smørgrav "%c%c [LogLevel %s]\r\n", 10454f52dfbbSDag-Erling Smørgrav efc->escape_char, ch, 10466888a9beSDag-Erling Smørgrav log_level_name(options.log_level)); 10476888a9beSDag-Erling Smørgrav buffer_append(berr, string, strlen(string)); 10486888a9beSDag-Erling Smørgrav continue; 10496888a9beSDag-Erling Smørgrav 1050b66f2d16SKris Kennaway case '&': 1051b15c8340SDag-Erling Smørgrav if (c && c->ctl_chan != -1) 1052d4af9e69SDag-Erling Smørgrav goto noescape; 1053b66f2d16SKris Kennaway /* 1054d4af9e69SDag-Erling Smørgrav * Detach the program (continue to serve 1055d4af9e69SDag-Erling Smørgrav * connections, but put in background and no 1056d4af9e69SDag-Erling Smørgrav * more new connections). 1057b66f2d16SKris Kennaway */ 1058ae1f160dSDag-Erling Smørgrav /* Restore tty modes. */ 1059e146993eSDag-Erling Smørgrav leave_raw_mode( 1060e146993eSDag-Erling Smørgrav options.request_tty == REQUEST_TTY_FORCE); 1061ae1f160dSDag-Erling Smørgrav 1062ae1f160dSDag-Erling Smørgrav /* Stop listening for new connections. */ 10634f52dfbbSDag-Erling Smørgrav channel_stop_listening(ssh); 1064ae1f160dSDag-Erling Smørgrav 1065ae1f160dSDag-Erling Smørgrav snprintf(string, sizeof string, 10664f52dfbbSDag-Erling Smørgrav "%c& [backgrounded]\n", efc->escape_char); 1067ae1f160dSDag-Erling Smørgrav buffer_append(berr, string, strlen(string)); 1068ae1f160dSDag-Erling Smørgrav 1069ae1f160dSDag-Erling Smørgrav /* Fork into background. */ 1070ae1f160dSDag-Erling Smørgrav pid = fork(); 1071ae1f160dSDag-Erling Smørgrav if (pid < 0) { 1072ae1f160dSDag-Erling Smørgrav error("fork: %.100s", strerror(errno)); 1073ae1f160dSDag-Erling Smørgrav continue; 1074ae1f160dSDag-Erling Smørgrav } 1075ae1f160dSDag-Erling Smørgrav if (pid != 0) { /* This is the parent. */ 1076ae1f160dSDag-Erling Smørgrav /* The parent just exits. */ 1077ae1f160dSDag-Erling Smørgrav exit(0); 1078ae1f160dSDag-Erling Smørgrav } 1079ae1f160dSDag-Erling Smørgrav /* The child continues serving connections. */ 1080ae1f160dSDag-Erling Smørgrav buffer_append(bin, "\004", 1); 1081ae1f160dSDag-Erling Smørgrav /* fake EOF on stdin */ 1082ae1f160dSDag-Erling Smørgrav return -1; 1083b66f2d16SKris Kennaway case '?': 10844f52dfbbSDag-Erling Smørgrav print_escape_help(berr, efc->escape_char, 10856888a9beSDag-Erling Smørgrav (c && c->ctl_chan != -1), 10866888a9beSDag-Erling Smørgrav log_is_on_stderr()); 1087b66f2d16SKris Kennaway continue; 1088b66f2d16SKris Kennaway 1089b66f2d16SKris Kennaway case '#': 1090d4af9e69SDag-Erling Smørgrav snprintf(string, sizeof string, "%c#\r\n", 10914f52dfbbSDag-Erling Smørgrav efc->escape_char); 1092b66f2d16SKris Kennaway buffer_append(berr, string, strlen(string)); 10934f52dfbbSDag-Erling Smørgrav s = channel_open_message(ssh); 1094b66f2d16SKris Kennaway buffer_append(berr, s, strlen(s)); 1095e4a9863fSDag-Erling Smørgrav free(s); 1096b66f2d16SKris Kennaway continue; 1097b66f2d16SKris Kennaway 1098545d5ecaSDag-Erling Smørgrav case 'C': 1099b15c8340SDag-Erling Smørgrav if (c && c->ctl_chan != -1) 1100cce7d346SDag-Erling Smørgrav goto noescape; 11014f52dfbbSDag-Erling Smørgrav process_cmdline(ssh); 1102545d5ecaSDag-Erling Smørgrav continue; 1103545d5ecaSDag-Erling Smørgrav 1104b66f2d16SKris Kennaway default: 11054f52dfbbSDag-Erling Smørgrav if (ch != efc->escape_char) { 11064f52dfbbSDag-Erling Smørgrav buffer_put_char(bin, efc->escape_char); 1107b66f2d16SKris Kennaway bytes++; 1108b66f2d16SKris Kennaway } 1109b66f2d16SKris Kennaway /* Escaped characters fall through here */ 1110b66f2d16SKris Kennaway break; 1111b66f2d16SKris Kennaway } 1112b66f2d16SKris Kennaway } else { 1113b66f2d16SKris Kennaway /* 1114d4af9e69SDag-Erling Smørgrav * The previous character was not an escape char. 1115d4af9e69SDag-Erling Smørgrav * Check if this is an escape. 1116b66f2d16SKris Kennaway */ 11174f52dfbbSDag-Erling Smørgrav if (last_was_cr && ch == efc->escape_char) { 1118d4af9e69SDag-Erling Smørgrav /* 1119d4af9e69SDag-Erling Smørgrav * It is. Set the flag and continue to 1120d4af9e69SDag-Erling Smørgrav * next character. 1121d4af9e69SDag-Erling Smørgrav */ 11224f52dfbbSDag-Erling Smørgrav efc->escape_pending = 1; 1123b66f2d16SKris Kennaway continue; 1124b66f2d16SKris Kennaway } 1125b66f2d16SKris Kennaway } 1126b66f2d16SKris Kennaway 1127b66f2d16SKris Kennaway /* 1128b66f2d16SKris Kennaway * Normal character. Record whether it was a newline, 1129b66f2d16SKris Kennaway * and append it to the buffer. 1130b66f2d16SKris Kennaway */ 1131b66f2d16SKris Kennaway last_was_cr = (ch == '\r' || ch == '\n'); 1132b66f2d16SKris Kennaway buffer_put_char(bin, ch); 1133b66f2d16SKris Kennaway bytes++; 1134b66f2d16SKris Kennaway } 1135b66f2d16SKris Kennaway return bytes; 1136b66f2d16SKris Kennaway } 1137b66f2d16SKris Kennaway 1138511b41d2SMark Murray /* 1139a04a10f8SKris Kennaway * Get packets from the connection input buffer, and process them as long as 1140a04a10f8SKris Kennaway * there are packets available. 1141a04a10f8SKris Kennaway * 1142a04a10f8SKris Kennaway * Any unknown packets received during the actual 1143a04a10f8SKris Kennaway * session cause the session to terminate. This is 1144a04a10f8SKris Kennaway * intended to make debugging easier since no 1145a04a10f8SKris Kennaway * confirmations are sent. Any compatible protocol 1146a04a10f8SKris Kennaway * extensions must be negotiated during the 1147a04a10f8SKris Kennaway * preparatory phase. 1148a04a10f8SKris Kennaway */ 1149a04a10f8SKris Kennaway 1150ae1f160dSDag-Erling Smørgrav static void 11511e8db6e2SBrian Feldman client_process_buffered_input_packets(void) 1152a04a10f8SKris Kennaway { 11534f52dfbbSDag-Erling Smørgrav ssh_dispatch_run_fatal(active_state, DISPATCH_NONBLOCK, &quit_pending); 1154a04a10f8SKris Kennaway } 1155a04a10f8SKris Kennaway 1156b66f2d16SKris Kennaway /* scan buf[] for '~' before sending data to the peer */ 1157b66f2d16SKris Kennaway 1158d4af9e69SDag-Erling Smørgrav /* Helper: allocate a new escape_filter_ctx and fill in its escape char */ 1159d4af9e69SDag-Erling Smørgrav void * 1160d4af9e69SDag-Erling Smørgrav client_new_escape_filter_ctx(int escape_char) 1161b66f2d16SKris Kennaway { 1162d4af9e69SDag-Erling Smørgrav struct escape_filter_ctx *ret; 1163d4af9e69SDag-Erling Smørgrav 11640a37d4a3SXin LI ret = xcalloc(1, sizeof(*ret)); 1165d4af9e69SDag-Erling Smørgrav ret->escape_pending = 0; 1166d4af9e69SDag-Erling Smørgrav ret->escape_char = escape_char; 1167d4af9e69SDag-Erling Smørgrav return (void *)ret; 1168d4af9e69SDag-Erling Smørgrav } 1169d4af9e69SDag-Erling Smørgrav 1170d4af9e69SDag-Erling Smørgrav /* Free the escape filter context on channel free */ 1171d4af9e69SDag-Erling Smørgrav void 11724f52dfbbSDag-Erling Smørgrav client_filter_cleanup(struct ssh *ssh, int cid, void *ctx) 1173d4af9e69SDag-Erling Smørgrav { 1174e4a9863fSDag-Erling Smørgrav free(ctx); 1175d4af9e69SDag-Erling Smørgrav } 1176d4af9e69SDag-Erling Smørgrav 1177d4af9e69SDag-Erling Smørgrav int 11784f52dfbbSDag-Erling Smørgrav client_simple_escape_filter(struct ssh *ssh, Channel *c, char *buf, int len) 1179d4af9e69SDag-Erling Smørgrav { 1180d4af9e69SDag-Erling Smørgrav if (c->extended_usage != CHAN_EXTENDED_WRITE) 1181d4af9e69SDag-Erling Smørgrav return 0; 1182d4af9e69SDag-Erling Smørgrav 11834f52dfbbSDag-Erling Smørgrav return process_escapes(ssh, c, c->input, c->output, c->extended, 1184d4af9e69SDag-Erling Smørgrav buf, len); 1185b66f2d16SKris Kennaway } 1186b66f2d16SKris Kennaway 1187ae1f160dSDag-Erling Smørgrav static void 11884f52dfbbSDag-Erling Smørgrav client_channel_closed(struct ssh *ssh, int id, void *arg) 11891e8db6e2SBrian Feldman { 11904f52dfbbSDag-Erling Smørgrav channel_cancel_cleanup(ssh, id); 11911e8db6e2SBrian Feldman session_closed = 1; 1192e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 11931e8db6e2SBrian Feldman } 11941e8db6e2SBrian Feldman 1195a04a10f8SKris Kennaway /* 1196511b41d2SMark Murray * Implements the interactive session with the server. This is called after 1197511b41d2SMark Murray * the user has been authenticated, and a command has been started on the 1198ae1f160dSDag-Erling Smørgrav * remote host. If escape_char != SSH_ESCAPECHAR_NONE, it is the character 1199ae1f160dSDag-Erling Smørgrav * used as an escape character for terminating or suspending the session. 1200511b41d2SMark Murray */ 1201511b41d2SMark Murray int 12024f52dfbbSDag-Erling Smørgrav client_loop(struct ssh *ssh, int have_pty, int escape_char_arg, 12034f52dfbbSDag-Erling Smørgrav int ssh2_chan_id) 1204511b41d2SMark Murray { 12051e8db6e2SBrian Feldman fd_set *readset = NULL, *writeset = NULL; 1206511b41d2SMark Murray double start_time, total_time; 1207acc1a9efSDag-Erling Smørgrav int r, max_fd = 0, max_fd2 = 0, len; 1208d4af9e69SDag-Erling Smørgrav u_int64_t ibytes, obytes; 1209d74d50a8SDag-Erling Smørgrav u_int nalloc = 0; 1210511b41d2SMark Murray char buf[100]; 1211511b41d2SMark Murray 1212511b41d2SMark Murray debug("Entering interactive session."); 1213511b41d2SMark Murray 1214acc1a9efSDag-Erling Smørgrav if (options.control_master && 1215acc1a9efSDag-Erling Smørgrav !option_clear_or_none(options.control_path)) { 1216acc1a9efSDag-Erling Smørgrav debug("pledge: id"); 1217076ad2f8SDag-Erling Smørgrav if (pledge("stdio rpath wpath cpath unix inet dns recvfd proc exec id tty", 1218acc1a9efSDag-Erling Smørgrav NULL) == -1) 1219acc1a9efSDag-Erling Smørgrav fatal("%s pledge(): %s", __func__, strerror(errno)); 1220acc1a9efSDag-Erling Smørgrav 1221acc1a9efSDag-Erling Smørgrav } else if (options.forward_x11 || options.permit_local_command) { 1222acc1a9efSDag-Erling Smørgrav debug("pledge: exec"); 1223acc1a9efSDag-Erling Smørgrav if (pledge("stdio rpath wpath cpath unix inet dns proc exec tty", 1224acc1a9efSDag-Erling Smørgrav NULL) == -1) 1225acc1a9efSDag-Erling Smørgrav fatal("%s pledge(): %s", __func__, strerror(errno)); 1226acc1a9efSDag-Erling Smørgrav 1227acc1a9efSDag-Erling Smørgrav } else if (options.update_hostkeys) { 1228acc1a9efSDag-Erling Smørgrav debug("pledge: filesystem full"); 1229acc1a9efSDag-Erling Smørgrav if (pledge("stdio rpath wpath cpath unix inet dns proc tty", 1230acc1a9efSDag-Erling Smørgrav NULL) == -1) 1231acc1a9efSDag-Erling Smørgrav fatal("%s pledge(): %s", __func__, strerror(errno)); 1232acc1a9efSDag-Erling Smørgrav 1233076ad2f8SDag-Erling Smørgrav } else if (!option_clear_or_none(options.proxy_command) || 1234076ad2f8SDag-Erling Smørgrav fork_after_authentication_flag) { 1235acc1a9efSDag-Erling Smørgrav debug("pledge: proc"); 1236acc1a9efSDag-Erling Smørgrav if (pledge("stdio cpath unix inet dns proc tty", NULL) == -1) 1237acc1a9efSDag-Erling Smørgrav fatal("%s pledge(): %s", __func__, strerror(errno)); 1238acc1a9efSDag-Erling Smørgrav 1239acc1a9efSDag-Erling Smørgrav } else { 1240acc1a9efSDag-Erling Smørgrav debug("pledge: network"); 12414f52dfbbSDag-Erling Smørgrav if (pledge("stdio unix inet dns proc tty", NULL) == -1) 1242acc1a9efSDag-Erling Smørgrav fatal("%s pledge(): %s", __func__, strerror(errno)); 1243acc1a9efSDag-Erling Smørgrav } 1244acc1a9efSDag-Erling Smørgrav 1245*47dd1d1bSDag-Erling Smørgrav start_time = monotime_double(); 1246511b41d2SMark Murray 1247511b41d2SMark Murray /* Initialize variables. */ 1248511b41d2SMark Murray last_was_cr = 1; 1249511b41d2SMark Murray exit_status = -1; 1250511b41d2SMark Murray connection_in = packet_get_connection_in(); 1251511b41d2SMark Murray connection_out = packet_get_connection_out(); 1252ca86bcf2SDag-Erling Smørgrav max_fd = MAXIMUM(connection_in, connection_out); 12531e8db6e2SBrian Feldman 1254511b41d2SMark Murray quit_pending = 0; 1255511b41d2SMark Murray 1256511b41d2SMark Murray /* Initialize buffers. */ 1257511b41d2SMark Murray buffer_init(&stderr_buffer); 1258511b41d2SMark Murray 1259a04a10f8SKris Kennaway client_init_dispatch(); 1260a04a10f8SKris Kennaway 1261d0c8c0bcSDag-Erling Smørgrav /* 1262d0c8c0bcSDag-Erling Smørgrav * Set signal handlers, (e.g. to restore non-blocking mode) 1263d0c8c0bcSDag-Erling Smørgrav * but don't overwrite SIG_IGN, matches behaviour from rsh(1) 1264d0c8c0bcSDag-Erling Smørgrav */ 12655e8dbd04SDag-Erling Smørgrav if (signal(SIGHUP, SIG_IGN) != SIG_IGN) 12665e8dbd04SDag-Erling Smørgrav signal(SIGHUP, signal_handler); 1267d0c8c0bcSDag-Erling Smørgrav if (signal(SIGINT, SIG_IGN) != SIG_IGN) 1268511b41d2SMark Murray signal(SIGINT, signal_handler); 1269d0c8c0bcSDag-Erling Smørgrav if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) 1270511b41d2SMark Murray signal(SIGQUIT, signal_handler); 1271d0c8c0bcSDag-Erling Smørgrav if (signal(SIGTERM, SIG_IGN) != SIG_IGN) 1272511b41d2SMark Murray signal(SIGTERM, signal_handler); 1273511b41d2SMark Murray signal(SIGWINCH, window_change_handler); 1274511b41d2SMark Murray 1275511b41d2SMark Murray if (have_pty) 1276e146993eSDag-Erling Smørgrav enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 1277511b41d2SMark Murray 12781e8db6e2SBrian Feldman session_ident = ssh2_chan_id; 1279e146993eSDag-Erling Smørgrav if (session_ident != -1) { 1280e146993eSDag-Erling Smørgrav if (escape_char_arg != SSH_ESCAPECHAR_NONE) { 12814f52dfbbSDag-Erling Smørgrav channel_register_filter(ssh, session_ident, 1282d4af9e69SDag-Erling Smørgrav client_simple_escape_filter, NULL, 1283d4af9e69SDag-Erling Smørgrav client_filter_cleanup, 1284e146993eSDag-Erling Smørgrav client_new_escape_filter_ctx( 1285e146993eSDag-Erling Smørgrav escape_char_arg)); 1286e146993eSDag-Erling Smørgrav } 12874f52dfbbSDag-Erling Smørgrav channel_register_cleanup(ssh, session_ident, 1288021d409fSDag-Erling Smørgrav client_channel_closed, 0); 1289e146993eSDag-Erling Smørgrav } 1290b66f2d16SKris Kennaway 1291511b41d2SMark Murray /* Main loop of the client for the interactive session mode. */ 1292511b41d2SMark Murray while (!quit_pending) { 1293511b41d2SMark Murray 1294511b41d2SMark Murray /* Process buffered packets sent by the server. */ 1295511b41d2SMark Murray client_process_buffered_input_packets(); 1296511b41d2SMark Murray 12974f52dfbbSDag-Erling Smørgrav if (session_closed && !channel_still_open(ssh)) 1298a04a10f8SKris Kennaway break; 1299a04a10f8SKris Kennaway 13004f52dfbbSDag-Erling Smørgrav if (ssh_packet_is_rekeying(ssh)) { 13011e8db6e2SBrian Feldman debug("rekeying in progress"); 1302acc1a9efSDag-Erling Smørgrav } else if (need_rekeying) { 1303acc1a9efSDag-Erling Smørgrav /* manual rekey request */ 1304acc1a9efSDag-Erling Smørgrav debug("need rekeying"); 13054f52dfbbSDag-Erling Smørgrav if ((r = kex_start_rekex(ssh)) != 0) 1306acc1a9efSDag-Erling Smørgrav fatal("%s: kex_start_rekex: %s", __func__, 1307acc1a9efSDag-Erling Smørgrav ssh_err(r)); 1308acc1a9efSDag-Erling Smørgrav need_rekeying = 0; 13091e8db6e2SBrian Feldman } else { 1310511b41d2SMark Murray /* 13111e8db6e2SBrian Feldman * Make packets from buffered channel data, and 13121e8db6e2SBrian Feldman * enqueue them for sending to the server. 1313511b41d2SMark Murray */ 1314511b41d2SMark Murray if (packet_not_very_much_data_to_write()) 13154f52dfbbSDag-Erling Smørgrav channel_output_poll(ssh); 1316511b41d2SMark Murray 1317511b41d2SMark Murray /* 13181e8db6e2SBrian Feldman * Check if the window size has changed, and buffer a 13191e8db6e2SBrian Feldman * message about it to the server if so. 1320511b41d2SMark Murray */ 13214f52dfbbSDag-Erling Smørgrav client_check_window_change(ssh); 1322511b41d2SMark Murray 1323511b41d2SMark Murray if (quit_pending) 1324511b41d2SMark Murray break; 13251e8db6e2SBrian Feldman } 1326511b41d2SMark Murray /* 1327511b41d2SMark Murray * Wait until we have something to do (something becomes 1328511b41d2SMark Murray * available on one of the descriptors). 1329511b41d2SMark Murray */ 1330ae1f160dSDag-Erling Smørgrav max_fd2 = max_fd; 13314f52dfbbSDag-Erling Smørgrav client_wait_until_can_do_something(ssh, &readset, &writeset, 13324f52dfbbSDag-Erling Smørgrav &max_fd2, &nalloc, ssh_packet_is_rekeying(ssh)); 1333511b41d2SMark Murray 1334511b41d2SMark Murray if (quit_pending) 1335511b41d2SMark Murray break; 1336511b41d2SMark Murray 13371e8db6e2SBrian Feldman /* Do channel operations unless rekeying in progress. */ 13384f52dfbbSDag-Erling Smørgrav if (!ssh_packet_is_rekeying(ssh)) 13394f52dfbbSDag-Erling Smørgrav channel_after_select(ssh, readset, writeset); 1340511b41d2SMark Murray 1341a04a10f8SKris Kennaway /* Buffer input from the connection. */ 13421e8db6e2SBrian Feldman client_process_net_input(readset); 1343511b41d2SMark Murray 1344a04a10f8SKris Kennaway if (quit_pending) 1345a04a10f8SKris Kennaway break; 1346a04a10f8SKris Kennaway 1347d4af9e69SDag-Erling Smørgrav /* 1348d4af9e69SDag-Erling Smørgrav * Send as much buffered packet data as possible to the 1349d4af9e69SDag-Erling Smørgrav * sender. 1350d4af9e69SDag-Erling Smørgrav */ 13511e8db6e2SBrian Feldman if (FD_ISSET(connection_out, writeset)) 1352511b41d2SMark Murray packet_write_poll(); 1353e2f6069cSDag-Erling Smørgrav 1354e2f6069cSDag-Erling Smørgrav /* 1355e2f6069cSDag-Erling Smørgrav * If we are a backgrounded control master, and the 1356e2f6069cSDag-Erling Smørgrav * timeout has expired without any active client 1357e2f6069cSDag-Erling Smørgrav * connections, then quit. 1358e2f6069cSDag-Erling Smørgrav */ 1359e2f6069cSDag-Erling Smørgrav if (control_persist_exit_time > 0) { 1360e4a9863fSDag-Erling Smørgrav if (monotime() >= control_persist_exit_time) { 1361e2f6069cSDag-Erling Smørgrav debug("ControlPersist timeout expired"); 1362e2f6069cSDag-Erling Smørgrav break; 1363e2f6069cSDag-Erling Smørgrav } 1364e2f6069cSDag-Erling Smørgrav } 1365511b41d2SMark Murray } 1366e4a9863fSDag-Erling Smørgrav free(readset); 1367e4a9863fSDag-Erling Smørgrav free(writeset); 1368511b41d2SMark Murray 1369511b41d2SMark Murray /* Terminate the session. */ 1370511b41d2SMark Murray 1371511b41d2SMark Murray /* Stop watching for window change. */ 1372511b41d2SMark Murray signal(SIGWINCH, SIG_DFL); 1373511b41d2SMark Murray 13747aee6ffeSDag-Erling Smørgrav packet_start(SSH2_MSG_DISCONNECT); 13757aee6ffeSDag-Erling Smørgrav packet_put_int(SSH2_DISCONNECT_BY_APPLICATION); 13767aee6ffeSDag-Erling Smørgrav packet_put_cstring("disconnected by user"); 13778ad9b54aSDag-Erling Smørgrav packet_put_cstring(""); /* language tag */ 13787aee6ffeSDag-Erling Smørgrav packet_send(); 13797aee6ffeSDag-Erling Smørgrav packet_write_wait(); 13807aee6ffeSDag-Erling Smørgrav 13814f52dfbbSDag-Erling Smørgrav channel_free_all(ssh); 1382ae1f160dSDag-Erling Smørgrav 1383ae1f160dSDag-Erling Smørgrav if (have_pty) 1384e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 1385ae1f160dSDag-Erling Smørgrav 1386ae1f160dSDag-Erling Smørgrav /* restore blocking io */ 1387ae1f160dSDag-Erling Smørgrav if (!isatty(fileno(stdin))) 1388ae1f160dSDag-Erling Smørgrav unset_nonblock(fileno(stdin)); 1389ae1f160dSDag-Erling Smørgrav if (!isatty(fileno(stdout))) 1390ae1f160dSDag-Erling Smørgrav unset_nonblock(fileno(stdout)); 1391ae1f160dSDag-Erling Smørgrav if (!isatty(fileno(stderr))) 1392ae1f160dSDag-Erling Smørgrav unset_nonblock(fileno(stderr)); 1393ae1f160dSDag-Erling Smørgrav 1394efcad6b7SDag-Erling Smørgrav /* 1395efcad6b7SDag-Erling Smørgrav * If there was no shell or command requested, there will be no remote 1396efcad6b7SDag-Erling Smørgrav * exit status to be returned. In that case, clear error code if the 1397efcad6b7SDag-Erling Smørgrav * connection was deliberately terminated at this end. 1398efcad6b7SDag-Erling Smørgrav */ 1399efcad6b7SDag-Erling Smørgrav if (no_shell_flag && received_signal == SIGTERM) { 1400efcad6b7SDag-Erling Smørgrav received_signal = 0; 1401efcad6b7SDag-Erling Smørgrav exit_status = 0; 1402ae1f160dSDag-Erling Smørgrav } 1403511b41d2SMark Murray 14044f52dfbbSDag-Erling Smørgrav if (received_signal) { 14054f52dfbbSDag-Erling Smørgrav verbose("Killed by signal %d.", (int) received_signal); 14064f52dfbbSDag-Erling Smørgrav cleanup_exit(0); 14074f52dfbbSDag-Erling Smørgrav } 1408efcad6b7SDag-Erling Smørgrav 1409511b41d2SMark Murray /* 1410511b41d2SMark Murray * In interactive mode (with pseudo tty) display a message indicating 1411511b41d2SMark Murray * that the connection has been closed. 1412511b41d2SMark Murray */ 1413511b41d2SMark Murray if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) { 1414d4af9e69SDag-Erling Smørgrav snprintf(buf, sizeof buf, 1415d4af9e69SDag-Erling Smørgrav "Connection to %.64s closed.\r\n", host); 1416511b41d2SMark Murray buffer_append(&stderr_buffer, buf, strlen(buf)); 1417511b41d2SMark Murray } 1418ae1f160dSDag-Erling Smørgrav 1419511b41d2SMark Murray /* Output any buffered data for stderr. */ 14204a421b63SDag-Erling Smørgrav if (buffer_len(&stderr_buffer) > 0) { 14214a421b63SDag-Erling Smørgrav len = atomicio(vwrite, fileno(stderr), 14224a421b63SDag-Erling Smørgrav buffer_ptr(&stderr_buffer), buffer_len(&stderr_buffer)); 14234a421b63SDag-Erling Smørgrav if (len < 0 || (u_int)len != buffer_len(&stderr_buffer)) 1424511b41d2SMark Murray error("Write failed flushing stderr buffer."); 14254a421b63SDag-Erling Smørgrav else 1426511b41d2SMark Murray buffer_consume(&stderr_buffer, len); 1427511b41d2SMark Murray } 1428511b41d2SMark Murray 1429511b41d2SMark Murray /* Clear and free any buffers. */ 1430acc1a9efSDag-Erling Smørgrav explicit_bzero(buf, sizeof(buf)); 1431511b41d2SMark Murray buffer_free(&stderr_buffer); 1432511b41d2SMark Murray 1433511b41d2SMark Murray /* Report bytes transferred, and transfer rates. */ 1434*47dd1d1bSDag-Erling Smørgrav total_time = monotime_double() - start_time; 1435bc5531deSDag-Erling Smørgrav packet_get_bytes(&ibytes, &obytes); 1436d4af9e69SDag-Erling Smørgrav verbose("Transferred: sent %llu, received %llu bytes, in %.1f seconds", 14374a421b63SDag-Erling Smørgrav (unsigned long long)obytes, (unsigned long long)ibytes, total_time); 1438511b41d2SMark Murray if (total_time > 0) 1439d4af9e69SDag-Erling Smørgrav verbose("Bytes per second: sent %.1f, received %.1f", 1440d4af9e69SDag-Erling Smørgrav obytes / total_time, ibytes / total_time); 1441511b41d2SMark Murray /* Return the exit status of the program. */ 1442511b41d2SMark Murray debug("Exit status %d", exit_status); 1443511b41d2SMark Murray return exit_status; 1444511b41d2SMark Murray } 1445a04a10f8SKris Kennaway 1446a04a10f8SKris Kennaway /*********/ 1447a04a10f8SKris Kennaway 1448ae1f160dSDag-Erling Smørgrav static Channel * 14494f52dfbbSDag-Erling Smørgrav client_request_forwarded_tcpip(struct ssh *ssh, const char *request_type, 14504f52dfbbSDag-Erling Smørgrav int rchan, u_int rwindow, u_int rmaxpack) 14511e8db6e2SBrian Feldman { 14521e8db6e2SBrian Feldman Channel *c = NULL; 1453ca86bcf2SDag-Erling Smørgrav struct sshbuf *b = NULL; 14541e8db6e2SBrian Feldman char *listen_address, *originator_address; 1455cce7d346SDag-Erling Smørgrav u_short listen_port, originator_port; 1456ca86bcf2SDag-Erling Smørgrav int r; 14571e8db6e2SBrian Feldman 14581e8db6e2SBrian Feldman /* Get rest of the packet */ 14591e8db6e2SBrian Feldman listen_address = packet_get_string(NULL); 14601e8db6e2SBrian Feldman listen_port = packet_get_int(); 14611e8db6e2SBrian Feldman originator_address = packet_get_string(NULL); 14621e8db6e2SBrian Feldman originator_port = packet_get_int(); 1463ae1f160dSDag-Erling Smørgrav packet_check_eom(); 14641e8db6e2SBrian Feldman 1465a0ee8cc6SDag-Erling Smørgrav debug("%s: listen %s port %d, originator %s port %d", __func__, 1466a0ee8cc6SDag-Erling Smørgrav listen_address, listen_port, originator_address, originator_port); 14671e8db6e2SBrian Feldman 14684f52dfbbSDag-Erling Smørgrav c = channel_connect_by_listen_address(ssh, listen_address, listen_port, 1469d4af9e69SDag-Erling Smørgrav "forwarded-tcpip", originator_address); 1470d4af9e69SDag-Erling Smørgrav 1471ca86bcf2SDag-Erling Smørgrav if (c != NULL && c->type == SSH_CHANNEL_MUX_CLIENT) { 1472ca86bcf2SDag-Erling Smørgrav if ((b = sshbuf_new()) == NULL) { 1473ca86bcf2SDag-Erling Smørgrav error("%s: alloc reply", __func__); 1474ca86bcf2SDag-Erling Smørgrav goto out; 1475ca86bcf2SDag-Erling Smørgrav } 1476ca86bcf2SDag-Erling Smørgrav /* reconstruct and send to muxclient */ 1477ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_put_u8(b, 0)) != 0 || /* padlen */ 1478ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u8(b, SSH2_MSG_CHANNEL_OPEN)) != 0 || 1479ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_cstring(b, request_type)) != 0 || 1480ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, rchan)) != 0 || 1481ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, rwindow)) != 0 || 1482ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, rmaxpack)) != 0 || 1483ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_cstring(b, listen_address)) != 0 || 1484ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, listen_port)) != 0 || 1485ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_cstring(b, originator_address)) != 0 || 1486ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, originator_port)) != 0 || 14874f52dfbbSDag-Erling Smørgrav (r = sshbuf_put_stringb(c->output, b)) != 0) { 1488ca86bcf2SDag-Erling Smørgrav error("%s: compose for muxclient %s", __func__, 1489ca86bcf2SDag-Erling Smørgrav ssh_err(r)); 1490ca86bcf2SDag-Erling Smørgrav goto out; 1491ca86bcf2SDag-Erling Smørgrav } 1492ca86bcf2SDag-Erling Smørgrav } 1493ca86bcf2SDag-Erling Smørgrav 1494ca86bcf2SDag-Erling Smørgrav out: 1495ca86bcf2SDag-Erling Smørgrav sshbuf_free(b); 1496e4a9863fSDag-Erling Smørgrav free(originator_address); 1497e4a9863fSDag-Erling Smørgrav free(listen_address); 14981e8db6e2SBrian Feldman return c; 14991e8db6e2SBrian Feldman } 15001e8db6e2SBrian Feldman 1501ae1f160dSDag-Erling Smørgrav static Channel * 15024f52dfbbSDag-Erling Smørgrav client_request_forwarded_streamlocal(struct ssh *ssh, 15034f52dfbbSDag-Erling Smørgrav const char *request_type, int rchan) 1504a0ee8cc6SDag-Erling Smørgrav { 1505a0ee8cc6SDag-Erling Smørgrav Channel *c = NULL; 1506a0ee8cc6SDag-Erling Smørgrav char *listen_path; 1507a0ee8cc6SDag-Erling Smørgrav 1508a0ee8cc6SDag-Erling Smørgrav /* Get the remote path. */ 1509a0ee8cc6SDag-Erling Smørgrav listen_path = packet_get_string(NULL); 1510a0ee8cc6SDag-Erling Smørgrav /* XXX: Skip reserved field for now. */ 1511a0ee8cc6SDag-Erling Smørgrav if (packet_get_string_ptr(NULL) == NULL) 1512a0ee8cc6SDag-Erling Smørgrav fatal("%s: packet_get_string_ptr failed", __func__); 1513a0ee8cc6SDag-Erling Smørgrav packet_check_eom(); 1514a0ee8cc6SDag-Erling Smørgrav 1515a0ee8cc6SDag-Erling Smørgrav debug("%s: %s", __func__, listen_path); 1516a0ee8cc6SDag-Erling Smørgrav 15174f52dfbbSDag-Erling Smørgrav c = channel_connect_by_listen_path(ssh, listen_path, 1518a0ee8cc6SDag-Erling Smørgrav "forwarded-streamlocal@openssh.com", "forwarded-streamlocal"); 1519a0ee8cc6SDag-Erling Smørgrav free(listen_path); 1520a0ee8cc6SDag-Erling Smørgrav return c; 1521a0ee8cc6SDag-Erling Smørgrav } 1522a0ee8cc6SDag-Erling Smørgrav 1523a0ee8cc6SDag-Erling Smørgrav static Channel * 15244f52dfbbSDag-Erling Smørgrav client_request_x11(struct ssh *ssh, const char *request_type, int rchan) 15251e8db6e2SBrian Feldman { 15261e8db6e2SBrian Feldman Channel *c = NULL; 15271e8db6e2SBrian Feldman char *originator; 1528cce7d346SDag-Erling Smørgrav u_short originator_port; 1529ae1f160dSDag-Erling Smørgrav int sock; 15301e8db6e2SBrian Feldman 15311e8db6e2SBrian Feldman if (!options.forward_x11) { 15321e8db6e2SBrian Feldman error("Warning: ssh server tried X11 forwarding."); 1533d4af9e69SDag-Erling Smørgrav error("Warning: this is probably a break-in attempt by a " 1534d4af9e69SDag-Erling Smørgrav "malicious server."); 15351e8db6e2SBrian Feldman return NULL; 15361e8db6e2SBrian Feldman } 1537557f75e5SDag-Erling Smørgrav if (x11_refuse_time != 0 && (u_int)monotime() >= x11_refuse_time) { 1538e2f6069cSDag-Erling Smørgrav verbose("Rejected X11 connection after ForwardX11Timeout " 1539e2f6069cSDag-Erling Smørgrav "expired"); 1540e2f6069cSDag-Erling Smørgrav return NULL; 1541e2f6069cSDag-Erling Smørgrav } 15421e8db6e2SBrian Feldman originator = packet_get_string(NULL); 15431e8db6e2SBrian Feldman originator_port = packet_get_int(); 1544ae1f160dSDag-Erling Smørgrav packet_check_eom(); 15451e8db6e2SBrian Feldman /* XXX check permission */ 15461e8db6e2SBrian Feldman debug("client_request_x11: request from %s %d", originator, 15471e8db6e2SBrian Feldman originator_port); 1548e4a9863fSDag-Erling Smørgrav free(originator); 15494f52dfbbSDag-Erling Smørgrav sock = x11_connect_display(ssh); 1550ae1f160dSDag-Erling Smørgrav if (sock < 0) 1551ae1f160dSDag-Erling Smørgrav return NULL; 15524f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, "x11", 155360c59fadSDag-Erling Smørgrav SSH_CHANNEL_X11_OPEN, sock, sock, -1, 155460c59fadSDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "x11", 1); 1555ae1f160dSDag-Erling Smørgrav c->force_drain = 1; 15561e8db6e2SBrian Feldman return c; 15571e8db6e2SBrian Feldman } 15581e8db6e2SBrian Feldman 1559ae1f160dSDag-Erling Smørgrav static Channel * 15604f52dfbbSDag-Erling Smørgrav client_request_agent(struct ssh *ssh, const char *request_type, int rchan) 15611e8db6e2SBrian Feldman { 15621e8db6e2SBrian Feldman Channel *c = NULL; 1563bc5531deSDag-Erling Smørgrav int r, sock; 15641e8db6e2SBrian Feldman 15651e8db6e2SBrian Feldman if (!options.forward_agent) { 15661e8db6e2SBrian Feldman error("Warning: ssh server tried agent forwarding."); 1567d4af9e69SDag-Erling Smørgrav error("Warning: this is probably a break-in attempt by a " 1568d4af9e69SDag-Erling Smørgrav "malicious server."); 15691e8db6e2SBrian Feldman return NULL; 15701e8db6e2SBrian Feldman } 1571bc5531deSDag-Erling Smørgrav if ((r = ssh_get_authentication_socket(&sock)) != 0) { 1572bc5531deSDag-Erling Smørgrav if (r != SSH_ERR_AGENT_NOT_PRESENT) 1573bc5531deSDag-Erling Smørgrav debug("%s: ssh_get_authentication_socket: %s", 1574bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 1575ae1f160dSDag-Erling Smørgrav return NULL; 1576bc5531deSDag-Erling Smørgrav } 15774f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, "authentication agent connection", 15781e8db6e2SBrian Feldman SSH_CHANNEL_OPEN, sock, sock, -1, 1579e3bd730fSBryan Drewery CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, 158089986192SBrooks Davis "authentication agent connection", 1); 1581ae1f160dSDag-Erling Smørgrav c->force_drain = 1; 15821e8db6e2SBrian Feldman return c; 15831e8db6e2SBrian Feldman } 15841e8db6e2SBrian Feldman 1585*47dd1d1bSDag-Erling Smørgrav char * 15864f52dfbbSDag-Erling Smørgrav client_request_tun_fwd(struct ssh *ssh, int tun_mode, 15874f52dfbbSDag-Erling Smørgrav int local_tun, int remote_tun) 1588d4af9e69SDag-Erling Smørgrav { 1589d4af9e69SDag-Erling Smørgrav Channel *c; 1590d4af9e69SDag-Erling Smørgrav int fd; 1591*47dd1d1bSDag-Erling Smørgrav char *ifname = NULL; 1592d4af9e69SDag-Erling Smørgrav 1593d4af9e69SDag-Erling Smørgrav if (tun_mode == SSH_TUNMODE_NO) 1594d4af9e69SDag-Erling Smørgrav return 0; 1595d4af9e69SDag-Erling Smørgrav 1596d4af9e69SDag-Erling Smørgrav debug("Requesting tun unit %d in mode %d", local_tun, tun_mode); 1597d4af9e69SDag-Erling Smørgrav 1598d4af9e69SDag-Erling Smørgrav /* Open local tunnel device */ 1599*47dd1d1bSDag-Erling Smørgrav if ((fd = tun_open(local_tun, tun_mode, &ifname)) == -1) { 1600d4af9e69SDag-Erling Smørgrav error("Tunnel device open failed."); 1601*47dd1d1bSDag-Erling Smørgrav return NULL; 1602d4af9e69SDag-Erling Smørgrav } 1603*47dd1d1bSDag-Erling Smørgrav debug("Tunnel forwarding using interface %s", ifname); 1604d4af9e69SDag-Erling Smørgrav 16054f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, "tun", SSH_CHANNEL_OPENING, fd, fd, -1, 160660c59fadSDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1); 1607d4af9e69SDag-Erling Smørgrav c->datagram = 1; 1608d4af9e69SDag-Erling Smørgrav 1609d4af9e69SDag-Erling Smørgrav #if defined(SSH_TUN_FILTER) 1610d4af9e69SDag-Erling Smørgrav if (options.tun_open == SSH_TUNMODE_POINTOPOINT) 16114f52dfbbSDag-Erling Smørgrav channel_register_filter(ssh, c->self, sys_tun_infilter, 1612d4af9e69SDag-Erling Smørgrav sys_tun_outfilter, NULL, NULL); 1613d4af9e69SDag-Erling Smørgrav #endif 1614d4af9e69SDag-Erling Smørgrav 1615d4af9e69SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_OPEN); 1616d4af9e69SDag-Erling Smørgrav packet_put_cstring("tun@openssh.com"); 1617d4af9e69SDag-Erling Smørgrav packet_put_int(c->self); 1618d4af9e69SDag-Erling Smørgrav packet_put_int(c->local_window_max); 1619d4af9e69SDag-Erling Smørgrav packet_put_int(c->local_maxpacket); 1620d4af9e69SDag-Erling Smørgrav packet_put_int(tun_mode); 1621d4af9e69SDag-Erling Smørgrav packet_put_int(remote_tun); 1622d4af9e69SDag-Erling Smørgrav packet_send(); 1623d4af9e69SDag-Erling Smørgrav 1624*47dd1d1bSDag-Erling Smørgrav return ifname; 1625d4af9e69SDag-Erling Smørgrav } 1626d4af9e69SDag-Erling Smørgrav 1627a04a10f8SKris Kennaway /* XXXX move to generic input handler */ 1628bc5531deSDag-Erling Smørgrav static int 16294f52dfbbSDag-Erling Smørgrav client_input_channel_open(int type, u_int32_t seq, struct ssh *ssh) 1630a04a10f8SKris Kennaway { 1631a04a10f8SKris Kennaway Channel *c = NULL; 1632a04a10f8SKris Kennaway char *ctype; 1633a04a10f8SKris Kennaway int rchan; 1634ee21a45fSDag-Erling Smørgrav u_int rmaxpack, rwindow, len; 1635a04a10f8SKris Kennaway 1636a04a10f8SKris Kennaway ctype = packet_get_string(&len); 1637a04a10f8SKris Kennaway rchan = packet_get_int(); 1638a04a10f8SKris Kennaway rwindow = packet_get_int(); 1639a04a10f8SKris Kennaway rmaxpack = packet_get_int(); 1640a04a10f8SKris Kennaway 1641a04a10f8SKris Kennaway debug("client_input_channel_open: ctype %s rchan %d win %d max %d", 1642a04a10f8SKris Kennaway ctype, rchan, rwindow, rmaxpack); 1643a04a10f8SKris Kennaway 16441e8db6e2SBrian Feldman if (strcmp(ctype, "forwarded-tcpip") == 0) { 16454f52dfbbSDag-Erling Smørgrav c = client_request_forwarded_tcpip(ssh, ctype, rchan, rwindow, 1646ca86bcf2SDag-Erling Smørgrav rmaxpack); 1647a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(ctype, "forwarded-streamlocal@openssh.com") == 0) { 16484f52dfbbSDag-Erling Smørgrav c = client_request_forwarded_streamlocal(ssh, ctype, rchan); 16491e8db6e2SBrian Feldman } else if (strcmp(ctype, "x11") == 0) { 16504f52dfbbSDag-Erling Smørgrav c = client_request_x11(ssh, ctype, rchan); 16511e8db6e2SBrian Feldman } else if (strcmp(ctype, "auth-agent@openssh.com") == 0) { 16524f52dfbbSDag-Erling Smørgrav c = client_request_agent(ssh, ctype, rchan); 1653a04a10f8SKris Kennaway } 1654ca86bcf2SDag-Erling Smørgrav if (c != NULL && c->type == SSH_CHANNEL_MUX_CLIENT) { 1655ca86bcf2SDag-Erling Smørgrav debug3("proxied to downstream: %s", ctype); 1656ca86bcf2SDag-Erling Smørgrav } else if (c != NULL) { 1657a04a10f8SKris Kennaway debug("confirm %s", ctype); 1658a04a10f8SKris Kennaway c->remote_id = rchan; 16594f52dfbbSDag-Erling Smørgrav c->have_remote_id = 1; 1660a04a10f8SKris Kennaway c->remote_window = rwindow; 1661a04a10f8SKris Kennaway c->remote_maxpacket = rmaxpack; 1662ae1f160dSDag-Erling Smørgrav if (c->type != SSH_CHANNEL_CONNECTING) { 1663a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); 1664a04a10f8SKris Kennaway packet_put_int(c->remote_id); 1665a04a10f8SKris Kennaway packet_put_int(c->self); 1666a04a10f8SKris Kennaway packet_put_int(c->local_window); 1667a04a10f8SKris Kennaway packet_put_int(c->local_maxpacket); 1668a04a10f8SKris Kennaway packet_send(); 1669ae1f160dSDag-Erling Smørgrav } 1670a04a10f8SKris Kennaway } else { 1671a04a10f8SKris Kennaway debug("failure %s", ctype); 1672a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); 1673a04a10f8SKris Kennaway packet_put_int(rchan); 1674a04a10f8SKris Kennaway packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED); 1675ae1f160dSDag-Erling Smørgrav packet_put_cstring("open failed"); 1676a04a10f8SKris Kennaway packet_put_cstring(""); 1677a04a10f8SKris Kennaway packet_send(); 1678a04a10f8SKris Kennaway } 1679e4a9863fSDag-Erling Smørgrav free(ctype); 1680bc5531deSDag-Erling Smørgrav return 0; 1681a04a10f8SKris Kennaway } 1682bc5531deSDag-Erling Smørgrav 1683bc5531deSDag-Erling Smørgrav static int 16844f52dfbbSDag-Erling Smørgrav client_input_channel_req(int type, u_int32_t seq, struct ssh *ssh) 16851e8db6e2SBrian Feldman { 16861e8db6e2SBrian Feldman Channel *c = NULL; 1687d74d50a8SDag-Erling Smørgrav int exitval, id, reply, success = 0; 16881e8db6e2SBrian Feldman char *rtype; 16891e8db6e2SBrian Feldman 16901e8db6e2SBrian Feldman id = packet_get_int(); 16914f52dfbbSDag-Erling Smørgrav c = channel_lookup(ssh, id); 16924f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 1693ca86bcf2SDag-Erling Smørgrav return 0; 16941e8db6e2SBrian Feldman rtype = packet_get_string(NULL); 16951e8db6e2SBrian Feldman reply = packet_get_char(); 16961e8db6e2SBrian Feldman 16971e8db6e2SBrian Feldman debug("client_input_channel_req: channel %d rtype %s reply %d", 16981e8db6e2SBrian Feldman id, rtype, reply); 16991e8db6e2SBrian Feldman 1700d74d50a8SDag-Erling Smørgrav if (id == -1) { 1701d74d50a8SDag-Erling Smørgrav error("client_input_channel_req: request for channel -1"); 1702ca86bcf2SDag-Erling Smørgrav } else if (c == NULL) { 1703d4af9e69SDag-Erling Smørgrav error("client_input_channel_req: channel %d: " 1704d4af9e69SDag-Erling Smørgrav "unknown channel", id); 1705d4af9e69SDag-Erling Smørgrav } else if (strcmp(rtype, "eow@openssh.com") == 0) { 1706d4af9e69SDag-Erling Smørgrav packet_check_eom(); 17074f52dfbbSDag-Erling Smørgrav chan_rcvd_eow(ssh, c); 17081e8db6e2SBrian Feldman } else if (strcmp(rtype, "exit-status") == 0) { 1709d74d50a8SDag-Erling Smørgrav exitval = packet_get_int(); 1710b15c8340SDag-Erling Smørgrav if (c->ctl_chan != -1) { 17114f52dfbbSDag-Erling Smørgrav mux_exit_message(ssh, c, exitval); 1712b15c8340SDag-Erling Smørgrav success = 1; 1713b15c8340SDag-Erling Smørgrav } else if (id == session_ident) { 1714b15c8340SDag-Erling Smørgrav /* Record exit value of local session */ 17151e8db6e2SBrian Feldman success = 1; 1716d74d50a8SDag-Erling Smørgrav exit_status = exitval; 1717d74d50a8SDag-Erling Smørgrav } else { 1718b15c8340SDag-Erling Smørgrav /* Probably for a mux channel that has already closed */ 1719b15c8340SDag-Erling Smørgrav debug("%s: no sink for exit-status on channel %d", 1720b15c8340SDag-Erling Smørgrav __func__, id); 1721d74d50a8SDag-Erling Smørgrav } 1722ae1f160dSDag-Erling Smørgrav packet_check_eom(); 17231e8db6e2SBrian Feldman } 1724a0ee8cc6SDag-Erling Smørgrav if (reply && c != NULL && !(c->flags & CHAN_CLOSE_SENT)) { 17254f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id) 17264f52dfbbSDag-Erling Smørgrav fatal("%s: channel %d: no remote_id", 17274f52dfbbSDag-Erling Smørgrav __func__, c->self); 17281e8db6e2SBrian Feldman packet_start(success ? 17291e8db6e2SBrian Feldman SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); 1730cce7d346SDag-Erling Smørgrav packet_put_int(c->remote_id); 17311e8db6e2SBrian Feldman packet_send(); 17321e8db6e2SBrian Feldman } 1733e4a9863fSDag-Erling Smørgrav free(rtype); 1734bc5531deSDag-Erling Smørgrav return 0; 17351e8db6e2SBrian Feldman } 1736bc5531deSDag-Erling Smørgrav 1737bc5531deSDag-Erling Smørgrav struct hostkeys_update_ctx { 1738bc5531deSDag-Erling Smørgrav /* The hostname and (optionally) IP address string for the server */ 1739bc5531deSDag-Erling Smørgrav char *host_str, *ip_str; 1740bc5531deSDag-Erling Smørgrav 1741bc5531deSDag-Erling Smørgrav /* 1742bc5531deSDag-Erling Smørgrav * Keys received from the server and a flag for each indicating 1743bc5531deSDag-Erling Smørgrav * whether they already exist in known_hosts. 1744bc5531deSDag-Erling Smørgrav * keys_seen is filled in by hostkeys_find() and later (for new 1745bc5531deSDag-Erling Smørgrav * keys) by client_global_hostkeys_private_confirm(). 1746bc5531deSDag-Erling Smørgrav */ 1747bc5531deSDag-Erling Smørgrav struct sshkey **keys; 1748bc5531deSDag-Erling Smørgrav int *keys_seen; 17494f52dfbbSDag-Erling Smørgrav size_t nkeys, nnew; 1750bc5531deSDag-Erling Smørgrav 1751bc5531deSDag-Erling Smørgrav /* 1752bc5531deSDag-Erling Smørgrav * Keys that are in known_hosts, but were not present in the update 1753bc5531deSDag-Erling Smørgrav * from the server (i.e. scheduled to be deleted). 1754bc5531deSDag-Erling Smørgrav * Filled in by hostkeys_find(). 1755bc5531deSDag-Erling Smørgrav */ 1756bc5531deSDag-Erling Smørgrav struct sshkey **old_keys; 1757bc5531deSDag-Erling Smørgrav size_t nold; 1758bc5531deSDag-Erling Smørgrav }; 1759bc5531deSDag-Erling Smørgrav 1760ae1f160dSDag-Erling Smørgrav static void 1761bc5531deSDag-Erling Smørgrav hostkeys_update_ctx_free(struct hostkeys_update_ctx *ctx) 1762bc5531deSDag-Erling Smørgrav { 1763bc5531deSDag-Erling Smørgrav size_t i; 1764bc5531deSDag-Erling Smørgrav 1765bc5531deSDag-Erling Smørgrav if (ctx == NULL) 1766bc5531deSDag-Erling Smørgrav return; 1767bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) 1768bc5531deSDag-Erling Smørgrav sshkey_free(ctx->keys[i]); 1769bc5531deSDag-Erling Smørgrav free(ctx->keys); 1770bc5531deSDag-Erling Smørgrav free(ctx->keys_seen); 1771bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nold; i++) 1772bc5531deSDag-Erling Smørgrav sshkey_free(ctx->old_keys[i]); 1773bc5531deSDag-Erling Smørgrav free(ctx->old_keys); 1774bc5531deSDag-Erling Smørgrav free(ctx->host_str); 1775bc5531deSDag-Erling Smørgrav free(ctx->ip_str); 1776bc5531deSDag-Erling Smørgrav free(ctx); 1777bc5531deSDag-Erling Smørgrav } 1778bc5531deSDag-Erling Smørgrav 1779bc5531deSDag-Erling Smørgrav static int 1780bc5531deSDag-Erling Smørgrav hostkeys_find(struct hostkey_foreach_line *l, void *_ctx) 1781bc5531deSDag-Erling Smørgrav { 1782bc5531deSDag-Erling Smørgrav struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; 1783bc5531deSDag-Erling Smørgrav size_t i; 1784bc5531deSDag-Erling Smørgrav struct sshkey **tmp; 1785bc5531deSDag-Erling Smørgrav 17864f52dfbbSDag-Erling Smørgrav if (l->status != HKF_STATUS_MATCHED || l->key == NULL) 1787bc5531deSDag-Erling Smørgrav return 0; 1788bc5531deSDag-Erling Smørgrav 1789bc5531deSDag-Erling Smørgrav /* Mark off keys we've already seen for this host */ 1790bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) { 1791bc5531deSDag-Erling Smørgrav if (sshkey_equal(l->key, ctx->keys[i])) { 1792bc5531deSDag-Erling Smørgrav debug3("%s: found %s key at %s:%ld", __func__, 1793bc5531deSDag-Erling Smørgrav sshkey_ssh_name(ctx->keys[i]), l->path, l->linenum); 1794bc5531deSDag-Erling Smørgrav ctx->keys_seen[i] = 1; 1795bc5531deSDag-Erling Smørgrav return 0; 1796bc5531deSDag-Erling Smørgrav } 1797bc5531deSDag-Erling Smørgrav } 1798bc5531deSDag-Erling Smørgrav /* This line contained a key that not offered by the server */ 1799bc5531deSDag-Erling Smørgrav debug3("%s: deprecated %s key at %s:%ld", __func__, 1800bc5531deSDag-Erling Smørgrav sshkey_ssh_name(l->key), l->path, l->linenum); 18014f52dfbbSDag-Erling Smørgrav if ((tmp = recallocarray(ctx->old_keys, ctx->nold, ctx->nold + 1, 1802bc5531deSDag-Erling Smørgrav sizeof(*ctx->old_keys))) == NULL) 18034f52dfbbSDag-Erling Smørgrav fatal("%s: recallocarray failed nold = %zu", 1804bc5531deSDag-Erling Smørgrav __func__, ctx->nold); 1805bc5531deSDag-Erling Smørgrav ctx->old_keys = tmp; 1806bc5531deSDag-Erling Smørgrav ctx->old_keys[ctx->nold++] = l->key; 1807bc5531deSDag-Erling Smørgrav l->key = NULL; 1808bc5531deSDag-Erling Smørgrav 1809bc5531deSDag-Erling Smørgrav return 0; 1810bc5531deSDag-Erling Smørgrav } 1811bc5531deSDag-Erling Smørgrav 1812bc5531deSDag-Erling Smørgrav static void 1813bc5531deSDag-Erling Smørgrav update_known_hosts(struct hostkeys_update_ctx *ctx) 1814bc5531deSDag-Erling Smørgrav { 1815bc5531deSDag-Erling Smørgrav int r, was_raw = 0; 1816bc5531deSDag-Erling Smørgrav int loglevel = options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK ? 1817bc5531deSDag-Erling Smørgrav SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_VERBOSE; 1818bc5531deSDag-Erling Smørgrav char *fp, *response; 1819bc5531deSDag-Erling Smørgrav size_t i; 1820bc5531deSDag-Erling Smørgrav 1821bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) { 1822bc5531deSDag-Erling Smørgrav if (ctx->keys_seen[i] != 2) 1823bc5531deSDag-Erling Smørgrav continue; 1824bc5531deSDag-Erling Smørgrav if ((fp = sshkey_fingerprint(ctx->keys[i], 1825bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) 1826bc5531deSDag-Erling Smørgrav fatal("%s: sshkey_fingerprint failed", __func__); 1827bc5531deSDag-Erling Smørgrav do_log2(loglevel, "Learned new hostkey: %s %s", 1828bc5531deSDag-Erling Smørgrav sshkey_type(ctx->keys[i]), fp); 1829bc5531deSDag-Erling Smørgrav free(fp); 1830bc5531deSDag-Erling Smørgrav } 1831bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nold; i++) { 1832bc5531deSDag-Erling Smørgrav if ((fp = sshkey_fingerprint(ctx->old_keys[i], 1833bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) 1834bc5531deSDag-Erling Smørgrav fatal("%s: sshkey_fingerprint failed", __func__); 1835bc5531deSDag-Erling Smørgrav do_log2(loglevel, "Deprecating obsolete hostkey: %s %s", 1836bc5531deSDag-Erling Smørgrav sshkey_type(ctx->old_keys[i]), fp); 1837bc5531deSDag-Erling Smørgrav free(fp); 1838bc5531deSDag-Erling Smørgrav } 1839bc5531deSDag-Erling Smørgrav if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK) { 1840bc5531deSDag-Erling Smørgrav if (get_saved_tio() != NULL) { 1841bc5531deSDag-Erling Smørgrav leave_raw_mode(1); 1842bc5531deSDag-Erling Smørgrav was_raw = 1; 1843bc5531deSDag-Erling Smørgrav } 1844bc5531deSDag-Erling Smørgrav response = NULL; 1845bc5531deSDag-Erling Smørgrav for (i = 0; !quit_pending && i < 3; i++) { 1846bc5531deSDag-Erling Smørgrav free(response); 1847bc5531deSDag-Erling Smørgrav response = read_passphrase("Accept updated hostkeys? " 1848bc5531deSDag-Erling Smørgrav "(yes/no): ", RP_ECHO); 1849bc5531deSDag-Erling Smørgrav if (strcasecmp(response, "yes") == 0) 1850bc5531deSDag-Erling Smørgrav break; 1851bc5531deSDag-Erling Smørgrav else if (quit_pending || response == NULL || 1852bc5531deSDag-Erling Smørgrav strcasecmp(response, "no") == 0) { 1853bc5531deSDag-Erling Smørgrav options.update_hostkeys = 0; 1854bc5531deSDag-Erling Smørgrav break; 1855bc5531deSDag-Erling Smørgrav } else { 1856bc5531deSDag-Erling Smørgrav do_log2(loglevel, "Please enter " 1857bc5531deSDag-Erling Smørgrav "\"yes\" or \"no\""); 1858bc5531deSDag-Erling Smørgrav } 1859bc5531deSDag-Erling Smørgrav } 1860bc5531deSDag-Erling Smørgrav if (quit_pending || i >= 3 || response == NULL) 1861bc5531deSDag-Erling Smørgrav options.update_hostkeys = 0; 1862bc5531deSDag-Erling Smørgrav free(response); 1863bc5531deSDag-Erling Smørgrav if (was_raw) 1864bc5531deSDag-Erling Smørgrav enter_raw_mode(1); 1865bc5531deSDag-Erling Smørgrav } 1866bc5531deSDag-Erling Smørgrav 1867bc5531deSDag-Erling Smørgrav /* 1868bc5531deSDag-Erling Smørgrav * Now that all the keys are verified, we can go ahead and replace 1869bc5531deSDag-Erling Smørgrav * them in known_hosts (assuming SSH_UPDATE_HOSTKEYS_ASK didn't 1870bc5531deSDag-Erling Smørgrav * cancel the operation). 1871bc5531deSDag-Erling Smørgrav */ 1872bc5531deSDag-Erling Smørgrav if (options.update_hostkeys != 0 && 1873bc5531deSDag-Erling Smørgrav (r = hostfile_replace_entries(options.user_hostfiles[0], 1874bc5531deSDag-Erling Smørgrav ctx->host_str, ctx->ip_str, ctx->keys, ctx->nkeys, 1875bc5531deSDag-Erling Smørgrav options.hash_known_hosts, 0, 1876bc5531deSDag-Erling Smørgrav options.fingerprint_hash)) != 0) 1877bc5531deSDag-Erling Smørgrav error("%s: hostfile_replace_entries failed: %s", 1878bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 1879bc5531deSDag-Erling Smørgrav } 1880bc5531deSDag-Erling Smørgrav 1881bc5531deSDag-Erling Smørgrav static void 18824f52dfbbSDag-Erling Smørgrav client_global_hostkeys_private_confirm(struct ssh *ssh, int type, 18834f52dfbbSDag-Erling Smørgrav u_int32_t seq, void *_ctx) 1884bc5531deSDag-Erling Smørgrav { 1885bc5531deSDag-Erling Smørgrav struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; 1886bc5531deSDag-Erling Smørgrav size_t i, ndone; 1887bc5531deSDag-Erling Smørgrav struct sshbuf *signdata; 1888*47dd1d1bSDag-Erling Smørgrav int r, kexsigtype, use_kexsigtype; 1889bc5531deSDag-Erling Smørgrav const u_char *sig; 1890bc5531deSDag-Erling Smørgrav size_t siglen; 1891bc5531deSDag-Erling Smørgrav 1892bc5531deSDag-Erling Smørgrav if (ctx->nnew == 0) 1893bc5531deSDag-Erling Smørgrav fatal("%s: ctx->nnew == 0", __func__); /* sanity */ 1894bc5531deSDag-Erling Smørgrav if (type != SSH2_MSG_REQUEST_SUCCESS) { 1895bc5531deSDag-Erling Smørgrav error("Server failed to confirm ownership of " 1896bc5531deSDag-Erling Smørgrav "private host keys"); 1897bc5531deSDag-Erling Smørgrav hostkeys_update_ctx_free(ctx); 1898bc5531deSDag-Erling Smørgrav return; 1899bc5531deSDag-Erling Smørgrav } 1900*47dd1d1bSDag-Erling Smørgrav kexsigtype = sshkey_type_plain( 1901*47dd1d1bSDag-Erling Smørgrav sshkey_type_from_name(ssh->kex->hostkey_alg)); 1902*47dd1d1bSDag-Erling Smørgrav 1903bc5531deSDag-Erling Smørgrav if ((signdata = sshbuf_new()) == NULL) 1904bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 1905bc5531deSDag-Erling Smørgrav /* Don't want to accidentally accept an unbound signature */ 1906bc5531deSDag-Erling Smørgrav if (ssh->kex->session_id_len == 0) 1907bc5531deSDag-Erling Smørgrav fatal("%s: ssh->kex->session_id_len == 0", __func__); 1908bc5531deSDag-Erling Smørgrav /* 1909bc5531deSDag-Erling Smørgrav * Expect a signature for each of the ctx->nnew private keys we 1910bc5531deSDag-Erling Smørgrav * haven't seen before. They will be in the same order as the 1911bc5531deSDag-Erling Smørgrav * ctx->keys where the corresponding ctx->keys_seen[i] == 0. 1912bc5531deSDag-Erling Smørgrav */ 1913bc5531deSDag-Erling Smørgrav for (ndone = i = 0; i < ctx->nkeys; i++) { 1914bc5531deSDag-Erling Smørgrav if (ctx->keys_seen[i]) 1915bc5531deSDag-Erling Smørgrav continue; 1916bc5531deSDag-Erling Smørgrav /* Prepare data to be signed: session ID, unique string, key */ 1917bc5531deSDag-Erling Smørgrav sshbuf_reset(signdata); 1918bc5531deSDag-Erling Smørgrav if ( (r = sshbuf_put_cstring(signdata, 1919bc5531deSDag-Erling Smørgrav "hostkeys-prove-00@openssh.com")) != 0 || 1920bc5531deSDag-Erling Smørgrav (r = sshbuf_put_string(signdata, ssh->kex->session_id, 1921bc5531deSDag-Erling Smørgrav ssh->kex->session_id_len)) != 0 || 1922bc5531deSDag-Erling Smørgrav (r = sshkey_puts(ctx->keys[i], signdata)) != 0) 1923bc5531deSDag-Erling Smørgrav fatal("%s: failed to prepare signature: %s", 1924bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 1925bc5531deSDag-Erling Smørgrav /* Extract and verify signature */ 1926bc5531deSDag-Erling Smørgrav if ((r = sshpkt_get_string_direct(ssh, &sig, &siglen)) != 0) { 1927bc5531deSDag-Erling Smørgrav error("%s: couldn't parse message: %s", 1928bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 1929bc5531deSDag-Erling Smørgrav goto out; 1930bc5531deSDag-Erling Smørgrav } 1931*47dd1d1bSDag-Erling Smørgrav /* 1932*47dd1d1bSDag-Erling Smørgrav * For RSA keys, prefer to use the signature type negotiated 1933*47dd1d1bSDag-Erling Smørgrav * during KEX to the default (SHA1). 1934*47dd1d1bSDag-Erling Smørgrav */ 1935*47dd1d1bSDag-Erling Smørgrav use_kexsigtype = kexsigtype == KEY_RSA && 1936*47dd1d1bSDag-Erling Smørgrav sshkey_type_plain(ctx->keys[i]->type) == KEY_RSA; 1937bc5531deSDag-Erling Smørgrav if ((r = sshkey_verify(ctx->keys[i], sig, siglen, 1938*47dd1d1bSDag-Erling Smørgrav sshbuf_ptr(signdata), sshbuf_len(signdata), 1939*47dd1d1bSDag-Erling Smørgrav use_kexsigtype ? ssh->kex->hostkey_alg : NULL, 0)) != 0) { 1940bc5531deSDag-Erling Smørgrav error("%s: server gave bad signature for %s key %zu", 1941bc5531deSDag-Erling Smørgrav __func__, sshkey_type(ctx->keys[i]), i); 1942bc5531deSDag-Erling Smørgrav goto out; 1943bc5531deSDag-Erling Smørgrav } 1944bc5531deSDag-Erling Smørgrav /* Key is good. Mark it as 'seen' */ 1945bc5531deSDag-Erling Smørgrav ctx->keys_seen[i] = 2; 1946bc5531deSDag-Erling Smørgrav ndone++; 1947bc5531deSDag-Erling Smørgrav } 1948bc5531deSDag-Erling Smørgrav if (ndone != ctx->nnew) 1949bc5531deSDag-Erling Smørgrav fatal("%s: ndone != ctx->nnew (%zu / %zu)", __func__, 1950bc5531deSDag-Erling Smørgrav ndone, ctx->nnew); /* Shouldn't happen */ 1951bc5531deSDag-Erling Smørgrav ssh_packet_check_eom(ssh); 1952bc5531deSDag-Erling Smørgrav 1953bc5531deSDag-Erling Smørgrav /* Make the edits to known_hosts */ 1954bc5531deSDag-Erling Smørgrav update_known_hosts(ctx); 1955bc5531deSDag-Erling Smørgrav out: 1956bc5531deSDag-Erling Smørgrav hostkeys_update_ctx_free(ctx); 1957bc5531deSDag-Erling Smørgrav } 1958bc5531deSDag-Erling Smørgrav 1959bc5531deSDag-Erling Smørgrav /* 1960d93a896eSDag-Erling Smørgrav * Returns non-zero if the key is accepted by HostkeyAlgorithms. 1961d93a896eSDag-Erling Smørgrav * Made slightly less trivial by the multiple RSA signature algorithm names. 1962d93a896eSDag-Erling Smørgrav */ 1963d93a896eSDag-Erling Smørgrav static int 1964d93a896eSDag-Erling Smørgrav key_accepted_by_hostkeyalgs(const struct sshkey *key) 1965d93a896eSDag-Erling Smørgrav { 1966d93a896eSDag-Erling Smørgrav const char *ktype = sshkey_ssh_name(key); 1967d93a896eSDag-Erling Smørgrav const char *hostkeyalgs = options.hostkeyalgorithms != NULL ? 1968d93a896eSDag-Erling Smørgrav options.hostkeyalgorithms : KEX_DEFAULT_PK_ALG; 1969d93a896eSDag-Erling Smørgrav 1970d93a896eSDag-Erling Smørgrav if (key == NULL || key->type == KEY_UNSPEC) 1971d93a896eSDag-Erling Smørgrav return 0; 1972d93a896eSDag-Erling Smørgrav if (key->type == KEY_RSA && 1973d93a896eSDag-Erling Smørgrav (match_pattern_list("rsa-sha2-256", hostkeyalgs, 0) == 1 || 1974d93a896eSDag-Erling Smørgrav match_pattern_list("rsa-sha2-512", hostkeyalgs, 0) == 1)) 1975d93a896eSDag-Erling Smørgrav return 1; 1976d93a896eSDag-Erling Smørgrav return match_pattern_list(ktype, hostkeyalgs, 0) == 1; 1977d93a896eSDag-Erling Smørgrav } 1978d93a896eSDag-Erling Smørgrav 1979d93a896eSDag-Erling Smørgrav /* 1980bc5531deSDag-Erling Smørgrav * Handle hostkeys-00@openssh.com global request to inform the client of all 1981bc5531deSDag-Erling Smørgrav * the server's hostkeys. The keys are checked against the user's 1982bc5531deSDag-Erling Smørgrav * HostkeyAlgorithms preference before they are accepted. 1983bc5531deSDag-Erling Smørgrav */ 1984bc5531deSDag-Erling Smørgrav static int 1985bc5531deSDag-Erling Smørgrav client_input_hostkeys(void) 1986bc5531deSDag-Erling Smørgrav { 1987bc5531deSDag-Erling Smørgrav struct ssh *ssh = active_state; /* XXX */ 1988bc5531deSDag-Erling Smørgrav const u_char *blob = NULL; 1989bc5531deSDag-Erling Smørgrav size_t i, len = 0; 1990bc5531deSDag-Erling Smørgrav struct sshbuf *buf = NULL; 1991bc5531deSDag-Erling Smørgrav struct sshkey *key = NULL, **tmp; 1992bc5531deSDag-Erling Smørgrav int r; 1993bc5531deSDag-Erling Smørgrav char *fp; 1994bc5531deSDag-Erling Smørgrav static int hostkeys_seen = 0; /* XXX use struct ssh */ 1995bc5531deSDag-Erling Smørgrav extern struct sockaddr_storage hostaddr; /* XXX from ssh.c */ 1996bc5531deSDag-Erling Smørgrav struct hostkeys_update_ctx *ctx = NULL; 1997bc5531deSDag-Erling Smørgrav 1998bc5531deSDag-Erling Smørgrav if (hostkeys_seen) 1999bc5531deSDag-Erling Smørgrav fatal("%s: server already sent hostkeys", __func__); 2000bc5531deSDag-Erling Smørgrav if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK && 2001bc5531deSDag-Erling Smørgrav options.batch_mode) 2002bc5531deSDag-Erling Smørgrav return 1; /* won't ask in batchmode, so don't even try */ 2003bc5531deSDag-Erling Smørgrav if (!options.update_hostkeys || options.num_user_hostfiles <= 0) 2004bc5531deSDag-Erling Smørgrav return 1; 2005bc5531deSDag-Erling Smørgrav 2006bc5531deSDag-Erling Smørgrav ctx = xcalloc(1, sizeof(*ctx)); 2007bc5531deSDag-Erling Smørgrav while (ssh_packet_remaining(ssh) > 0) { 2008bc5531deSDag-Erling Smørgrav sshkey_free(key); 2009bc5531deSDag-Erling Smørgrav key = NULL; 2010bc5531deSDag-Erling Smørgrav if ((r = sshpkt_get_string_direct(ssh, &blob, &len)) != 0) { 2011bc5531deSDag-Erling Smørgrav error("%s: couldn't parse message: %s", 2012bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 2013bc5531deSDag-Erling Smørgrav goto out; 2014bc5531deSDag-Erling Smørgrav } 2015bc5531deSDag-Erling Smørgrav if ((r = sshkey_from_blob(blob, len, &key)) != 0) { 2016bc5531deSDag-Erling Smørgrav error("%s: parse key: %s", __func__, ssh_err(r)); 2017bc5531deSDag-Erling Smørgrav goto out; 2018bc5531deSDag-Erling Smørgrav } 2019bc5531deSDag-Erling Smørgrav fp = sshkey_fingerprint(key, options.fingerprint_hash, 2020bc5531deSDag-Erling Smørgrav SSH_FP_DEFAULT); 2021bc5531deSDag-Erling Smørgrav debug3("%s: received %s key %s", __func__, 2022bc5531deSDag-Erling Smørgrav sshkey_type(key), fp); 2023bc5531deSDag-Erling Smørgrav free(fp); 2024eccfee6eSDag-Erling Smørgrav 2025d93a896eSDag-Erling Smørgrav if (!key_accepted_by_hostkeyalgs(key)) { 2026bc5531deSDag-Erling Smørgrav debug3("%s: %s key not permitted by HostkeyAlgorithms", 2027bc5531deSDag-Erling Smørgrav __func__, sshkey_ssh_name(key)); 2028bc5531deSDag-Erling Smørgrav continue; 2029bc5531deSDag-Erling Smørgrav } 2030bc5531deSDag-Erling Smørgrav /* Skip certs */ 2031bc5531deSDag-Erling Smørgrav if (sshkey_is_cert(key)) { 2032bc5531deSDag-Erling Smørgrav debug3("%s: %s key is a certificate; skipping", 2033bc5531deSDag-Erling Smørgrav __func__, sshkey_ssh_name(key)); 2034bc5531deSDag-Erling Smørgrav continue; 2035bc5531deSDag-Erling Smørgrav } 2036bc5531deSDag-Erling Smørgrav /* Ensure keys are unique */ 2037bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) { 2038bc5531deSDag-Erling Smørgrav if (sshkey_equal(key, ctx->keys[i])) { 2039bc5531deSDag-Erling Smørgrav error("%s: received duplicated %s host key", 2040bc5531deSDag-Erling Smørgrav __func__, sshkey_ssh_name(key)); 2041bc5531deSDag-Erling Smørgrav goto out; 2042bc5531deSDag-Erling Smørgrav } 2043bc5531deSDag-Erling Smørgrav } 2044bc5531deSDag-Erling Smørgrav /* Key is good, record it */ 20454f52dfbbSDag-Erling Smørgrav if ((tmp = recallocarray(ctx->keys, ctx->nkeys, ctx->nkeys + 1, 2046bc5531deSDag-Erling Smørgrav sizeof(*ctx->keys))) == NULL) 20474f52dfbbSDag-Erling Smørgrav fatal("%s: recallocarray failed nkeys = %zu", 2048bc5531deSDag-Erling Smørgrav __func__, ctx->nkeys); 2049bc5531deSDag-Erling Smørgrav ctx->keys = tmp; 2050bc5531deSDag-Erling Smørgrav ctx->keys[ctx->nkeys++] = key; 2051bc5531deSDag-Erling Smørgrav key = NULL; 2052bc5531deSDag-Erling Smørgrav } 2053bc5531deSDag-Erling Smørgrav 2054bc5531deSDag-Erling Smørgrav if (ctx->nkeys == 0) { 2055bc5531deSDag-Erling Smørgrav debug("%s: server sent no hostkeys", __func__); 2056bc5531deSDag-Erling Smørgrav goto out; 2057bc5531deSDag-Erling Smørgrav } 2058bc5531deSDag-Erling Smørgrav 2059bc5531deSDag-Erling Smørgrav if ((ctx->keys_seen = calloc(ctx->nkeys, 2060bc5531deSDag-Erling Smørgrav sizeof(*ctx->keys_seen))) == NULL) 2061bc5531deSDag-Erling Smørgrav fatal("%s: calloc failed", __func__); 2062bc5531deSDag-Erling Smørgrav 2063bc5531deSDag-Erling Smørgrav get_hostfile_hostname_ipaddr(host, 2064bc5531deSDag-Erling Smørgrav options.check_host_ip ? (struct sockaddr *)&hostaddr : NULL, 2065bc5531deSDag-Erling Smørgrav options.port, &ctx->host_str, 2066bc5531deSDag-Erling Smørgrav options.check_host_ip ? &ctx->ip_str : NULL); 2067bc5531deSDag-Erling Smørgrav 2068bc5531deSDag-Erling Smørgrav /* Find which keys we already know about. */ 2069bc5531deSDag-Erling Smørgrav if ((r = hostkeys_foreach(options.user_hostfiles[0], hostkeys_find, 2070bc5531deSDag-Erling Smørgrav ctx, ctx->host_str, ctx->ip_str, 2071bc5531deSDag-Erling Smørgrav HKF_WANT_PARSE_KEY|HKF_WANT_MATCH)) != 0) { 2072bc5531deSDag-Erling Smørgrav error("%s: hostkeys_foreach failed: %s", __func__, ssh_err(r)); 2073bc5531deSDag-Erling Smørgrav goto out; 2074bc5531deSDag-Erling Smørgrav } 2075bc5531deSDag-Erling Smørgrav 2076bc5531deSDag-Erling Smørgrav /* Figure out if we have any new keys to add */ 2077bc5531deSDag-Erling Smørgrav ctx->nnew = 0; 2078bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) { 2079bc5531deSDag-Erling Smørgrav if (!ctx->keys_seen[i]) 2080bc5531deSDag-Erling Smørgrav ctx->nnew++; 2081bc5531deSDag-Erling Smørgrav } 2082bc5531deSDag-Erling Smørgrav 2083bc5531deSDag-Erling Smørgrav debug3("%s: %zu keys from server: %zu new, %zu retained. %zu to remove", 2084bc5531deSDag-Erling Smørgrav __func__, ctx->nkeys, ctx->nnew, ctx->nkeys - ctx->nnew, ctx->nold); 2085bc5531deSDag-Erling Smørgrav 2086bc5531deSDag-Erling Smørgrav if (ctx->nnew == 0 && ctx->nold != 0) { 2087bc5531deSDag-Erling Smørgrav /* We have some keys to remove. Just do it. */ 2088bc5531deSDag-Erling Smørgrav update_known_hosts(ctx); 2089bc5531deSDag-Erling Smørgrav } else if (ctx->nnew != 0) { 2090bc5531deSDag-Erling Smørgrav /* 2091bc5531deSDag-Erling Smørgrav * We have received hitherto-unseen keys from the server. 2092bc5531deSDag-Erling Smørgrav * Ask the server to confirm ownership of the private halves. 2093bc5531deSDag-Erling Smørgrav */ 2094bc5531deSDag-Erling Smørgrav debug3("%s: asking server to prove ownership for %zu keys", 2095bc5531deSDag-Erling Smørgrav __func__, ctx->nnew); 2096bc5531deSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || 2097bc5531deSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, 2098bc5531deSDag-Erling Smørgrav "hostkeys-prove-00@openssh.com")) != 0 || 2099bc5531deSDag-Erling Smørgrav (r = sshpkt_put_u8(ssh, 1)) != 0) /* bool: want reply */ 2100bc5531deSDag-Erling Smørgrav fatal("%s: cannot prepare packet: %s", 2101bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 2102bc5531deSDag-Erling Smørgrav if ((buf = sshbuf_new()) == NULL) 2103bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_new", __func__); 2104bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) { 2105bc5531deSDag-Erling Smørgrav if (ctx->keys_seen[i]) 2106bc5531deSDag-Erling Smørgrav continue; 2107bc5531deSDag-Erling Smørgrav sshbuf_reset(buf); 2108bc5531deSDag-Erling Smørgrav if ((r = sshkey_putb(ctx->keys[i], buf)) != 0) 2109bc5531deSDag-Erling Smørgrav fatal("%s: sshkey_putb: %s", 2110bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 2111bc5531deSDag-Erling Smørgrav if ((r = sshpkt_put_stringb(ssh, buf)) != 0) 2112bc5531deSDag-Erling Smørgrav fatal("%s: sshpkt_put_string: %s", 2113bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 2114bc5531deSDag-Erling Smørgrav } 2115bc5531deSDag-Erling Smørgrav if ((r = sshpkt_send(ssh)) != 0) 2116bc5531deSDag-Erling Smørgrav fatal("%s: sshpkt_send: %s", __func__, ssh_err(r)); 2117bc5531deSDag-Erling Smørgrav client_register_global_confirm( 2118bc5531deSDag-Erling Smørgrav client_global_hostkeys_private_confirm, ctx); 2119bc5531deSDag-Erling Smørgrav ctx = NULL; /* will be freed in callback */ 2120bc5531deSDag-Erling Smørgrav } 2121bc5531deSDag-Erling Smørgrav 2122bc5531deSDag-Erling Smørgrav /* Success */ 2123bc5531deSDag-Erling Smørgrav out: 2124bc5531deSDag-Erling Smørgrav hostkeys_update_ctx_free(ctx); 2125bc5531deSDag-Erling Smørgrav sshkey_free(key); 2126bc5531deSDag-Erling Smørgrav sshbuf_free(buf); 2127bc5531deSDag-Erling Smørgrav /* 2128bc5531deSDag-Erling Smørgrav * NB. Return success for all cases. The server doesn't need to know 2129bc5531deSDag-Erling Smørgrav * what the client does with its hosts file. 2130bc5531deSDag-Erling Smørgrav */ 2131bc5531deSDag-Erling Smørgrav return 1; 2132bc5531deSDag-Erling Smørgrav } 2133bc5531deSDag-Erling Smørgrav 2134bc5531deSDag-Erling Smørgrav static int 21354f52dfbbSDag-Erling Smørgrav client_input_global_request(int type, u_int32_t seq, struct ssh *ssh) 2136ae1f160dSDag-Erling Smørgrav { 2137ae1f160dSDag-Erling Smørgrav char *rtype; 2138ae1f160dSDag-Erling Smørgrav int want_reply; 2139ae1f160dSDag-Erling Smørgrav int success = 0; 2140a04a10f8SKris Kennaway 2141bc5531deSDag-Erling Smørgrav rtype = packet_get_cstring(NULL); 2142ae1f160dSDag-Erling Smørgrav want_reply = packet_get_char(); 2143efcad6b7SDag-Erling Smørgrav debug("client_input_global_request: rtype %s want_reply %d", 2144efcad6b7SDag-Erling Smørgrav rtype, want_reply); 2145bc5531deSDag-Erling Smørgrav if (strcmp(rtype, "hostkeys-00@openssh.com") == 0) 2146bc5531deSDag-Erling Smørgrav success = client_input_hostkeys(); 2147ae1f160dSDag-Erling Smørgrav if (want_reply) { 2148ae1f160dSDag-Erling Smørgrav packet_start(success ? 2149ae1f160dSDag-Erling Smørgrav SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE); 2150ae1f160dSDag-Erling Smørgrav packet_send(); 2151ae1f160dSDag-Erling Smørgrav packet_write_wait(); 2152ae1f160dSDag-Erling Smørgrav } 2153e4a9863fSDag-Erling Smørgrav free(rtype); 2154bc5531deSDag-Erling Smørgrav return 0; 2155ae1f160dSDag-Erling Smørgrav } 2156ae1f160dSDag-Erling Smørgrav 2157d74d50a8SDag-Erling Smørgrav void 21584f52dfbbSDag-Erling Smørgrav client_session2_setup(struct ssh *ssh, int id, int want_tty, int want_subsystem, 2159d4af9e69SDag-Erling Smørgrav const char *term, struct termios *tiop, int in_fd, Buffer *cmd, char **env) 2160d74d50a8SDag-Erling Smørgrav { 2161d74d50a8SDag-Erling Smørgrav int len; 21625e8dbd04SDag-Erling Smørgrav Channel *c = NULL; 2163d74d50a8SDag-Erling Smørgrav 2164d74d50a8SDag-Erling Smørgrav debug2("%s: id %d", __func__, id); 2165d74d50a8SDag-Erling Smørgrav 21664f52dfbbSDag-Erling Smørgrav if ((c = channel_lookup(ssh, id)) == NULL) 21674f52dfbbSDag-Erling Smørgrav fatal("%s: channel %d: unknown channel", __func__, id); 21685e8dbd04SDag-Erling Smørgrav 21694a421b63SDag-Erling Smørgrav packet_set_interactive(want_tty, 21704a421b63SDag-Erling Smørgrav options.ip_qos_interactive, options.ip_qos_bulk); 21714a421b63SDag-Erling Smørgrav 2172d74d50a8SDag-Erling Smørgrav if (want_tty) { 2173d74d50a8SDag-Erling Smørgrav struct winsize ws; 2174d74d50a8SDag-Erling Smørgrav 2175d74d50a8SDag-Erling Smørgrav /* Store window size in the packet. */ 2176d74d50a8SDag-Erling Smørgrav if (ioctl(in_fd, TIOCGWINSZ, &ws) < 0) 2177d74d50a8SDag-Erling Smørgrav memset(&ws, 0, sizeof(ws)); 2178d74d50a8SDag-Erling Smørgrav 21794f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, id, "pty-req", 1); 21804f52dfbbSDag-Erling Smørgrav client_expect_confirm(ssh, id, "PTY allocation", CONFIRM_TTY); 2181d74d50a8SDag-Erling Smørgrav packet_put_cstring(term != NULL ? term : ""); 2182761efaa7SDag-Erling Smørgrav packet_put_int((u_int)ws.ws_col); 2183761efaa7SDag-Erling Smørgrav packet_put_int((u_int)ws.ws_row); 2184761efaa7SDag-Erling Smørgrav packet_put_int((u_int)ws.ws_xpixel); 2185761efaa7SDag-Erling Smørgrav packet_put_int((u_int)ws.ws_ypixel); 2186d4af9e69SDag-Erling Smørgrav if (tiop == NULL) 2187d4af9e69SDag-Erling Smørgrav tiop = get_saved_tio(); 2188d4af9e69SDag-Erling Smørgrav tty_make_modes(-1, tiop); 2189d74d50a8SDag-Erling Smørgrav packet_send(); 2190d74d50a8SDag-Erling Smørgrav /* XXX wait for reply */ 21915e8dbd04SDag-Erling Smørgrav c->client_tty = 1; 2192d74d50a8SDag-Erling Smørgrav } 2193d74d50a8SDag-Erling Smørgrav 2194d74d50a8SDag-Erling Smørgrav /* Transfer any environment variables from client to server */ 2195d74d50a8SDag-Erling Smørgrav if (options.num_send_env != 0 && env != NULL) { 2196d74d50a8SDag-Erling Smørgrav int i, j, matched; 2197d74d50a8SDag-Erling Smørgrav char *name, *val; 2198d74d50a8SDag-Erling Smørgrav 2199d74d50a8SDag-Erling Smørgrav debug("Sending environment."); 2200d74d50a8SDag-Erling Smørgrav for (i = 0; env[i] != NULL; i++) { 2201d74d50a8SDag-Erling Smørgrav /* Split */ 2202d74d50a8SDag-Erling Smørgrav name = xstrdup(env[i]); 2203d74d50a8SDag-Erling Smørgrav if ((val = strchr(name, '=')) == NULL) { 2204e4a9863fSDag-Erling Smørgrav free(name); 2205d74d50a8SDag-Erling Smørgrav continue; 2206d74d50a8SDag-Erling Smørgrav } 2207d74d50a8SDag-Erling Smørgrav *val++ = '\0'; 2208d74d50a8SDag-Erling Smørgrav 2209d74d50a8SDag-Erling Smørgrav matched = 0; 2210d74d50a8SDag-Erling Smørgrav for (j = 0; j < options.num_send_env; j++) { 2211d74d50a8SDag-Erling Smørgrav if (match_pattern(name, options.send_env[j])) { 2212d74d50a8SDag-Erling Smørgrav matched = 1; 2213d74d50a8SDag-Erling Smørgrav break; 2214d74d50a8SDag-Erling Smørgrav } 2215d74d50a8SDag-Erling Smørgrav } 2216d74d50a8SDag-Erling Smørgrav if (!matched) { 2217d74d50a8SDag-Erling Smørgrav debug3("Ignored env %s", name); 2218e4a9863fSDag-Erling Smørgrav free(name); 2219d74d50a8SDag-Erling Smørgrav continue; 2220d74d50a8SDag-Erling Smørgrav } 2221d74d50a8SDag-Erling Smørgrav 2222d74d50a8SDag-Erling Smørgrav debug("Sending env %s = %s", name, val); 22234f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, id, "env", 0); 2224d74d50a8SDag-Erling Smørgrav packet_put_cstring(name); 2225d74d50a8SDag-Erling Smørgrav packet_put_cstring(val); 2226d74d50a8SDag-Erling Smørgrav packet_send(); 2227e4a9863fSDag-Erling Smørgrav free(name); 2228d74d50a8SDag-Erling Smørgrav } 2229d74d50a8SDag-Erling Smørgrav } 2230d74d50a8SDag-Erling Smørgrav 2231d74d50a8SDag-Erling Smørgrav len = buffer_len(cmd); 2232d74d50a8SDag-Erling Smørgrav if (len > 0) { 2233d74d50a8SDag-Erling Smørgrav if (len > 900) 2234d74d50a8SDag-Erling Smørgrav len = 900; 2235d74d50a8SDag-Erling Smørgrav if (want_subsystem) { 2236d4af9e69SDag-Erling Smørgrav debug("Sending subsystem: %.*s", 2237d4af9e69SDag-Erling Smørgrav len, (u_char*)buffer_ptr(cmd)); 22384f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, id, "subsystem", 1); 22394f52dfbbSDag-Erling Smørgrav client_expect_confirm(ssh, id, "subsystem", 22404f52dfbbSDag-Erling Smørgrav CONFIRM_CLOSE); 2241d74d50a8SDag-Erling Smørgrav } else { 2242d4af9e69SDag-Erling Smørgrav debug("Sending command: %.*s", 2243d4af9e69SDag-Erling Smørgrav len, (u_char*)buffer_ptr(cmd)); 22444f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, id, "exec", 1); 22454f52dfbbSDag-Erling Smørgrav client_expect_confirm(ssh, id, "exec", CONFIRM_CLOSE); 2246d74d50a8SDag-Erling Smørgrav } 2247d74d50a8SDag-Erling Smørgrav packet_put_string(buffer_ptr(cmd), buffer_len(cmd)); 2248d74d50a8SDag-Erling Smørgrav packet_send(); 2249d74d50a8SDag-Erling Smørgrav } else { 22504f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, id, "shell", 1); 22514f52dfbbSDag-Erling Smørgrav client_expect_confirm(ssh, id, "shell", CONFIRM_CLOSE); 2252d74d50a8SDag-Erling Smørgrav packet_send(); 2253d74d50a8SDag-Erling Smørgrav } 2254d74d50a8SDag-Erling Smørgrav } 2255d74d50a8SDag-Erling Smørgrav 2256ae1f160dSDag-Erling Smørgrav static void 22574f52dfbbSDag-Erling Smørgrav client_init_dispatch(void) 2258a04a10f8SKris Kennaway { 2259a04a10f8SKris Kennaway dispatch_init(&dispatch_protocol_error); 2260545d5ecaSDag-Erling Smørgrav 2261a04a10f8SKris Kennaway dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); 2262a04a10f8SKris Kennaway dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data); 2263a04a10f8SKris Kennaway dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); 2264a04a10f8SKris Kennaway dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); 2265a04a10f8SKris Kennaway dispatch_set(SSH2_MSG_CHANNEL_OPEN, &client_input_channel_open); 2266a04a10f8SKris Kennaway dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); 2267a04a10f8SKris Kennaway dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); 22681e8db6e2SBrian Feldman dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &client_input_channel_req); 2269a04a10f8SKris Kennaway dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); 2270d4af9e69SDag-Erling Smørgrav dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, &channel_input_status_confirm); 2271d4af9e69SDag-Erling Smørgrav dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &channel_input_status_confirm); 2272ae1f160dSDag-Erling Smørgrav dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &client_input_global_request); 22731e8db6e2SBrian Feldman 22741e8db6e2SBrian Feldman /* rekeying */ 22751e8db6e2SBrian Feldman dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit); 2276545d5ecaSDag-Erling Smørgrav 2277545d5ecaSDag-Erling Smørgrav /* global request reply messages */ 2278545d5ecaSDag-Erling Smørgrav dispatch_set(SSH2_MSG_REQUEST_FAILURE, &client_global_request_reply); 2279545d5ecaSDag-Erling Smørgrav dispatch_set(SSH2_MSG_REQUEST_SUCCESS, &client_global_request_reply); 2280a04a10f8SKris Kennaway } 2281d4af9e69SDag-Erling Smørgrav 2282e146993eSDag-Erling Smørgrav void 2283e146993eSDag-Erling Smørgrav client_stop_mux(void) 2284e146993eSDag-Erling Smørgrav { 2285e146993eSDag-Erling Smørgrav if (options.control_path != NULL && muxserver_sock != -1) 2286e146993eSDag-Erling Smørgrav unlink(options.control_path); 2287e146993eSDag-Erling Smørgrav /* 22886888a9beSDag-Erling Smørgrav * If we are in persist mode, or don't have a shell, signal that we 22896888a9beSDag-Erling Smørgrav * should close when all active channels are closed. 2290e146993eSDag-Erling Smørgrav */ 22916888a9beSDag-Erling Smørgrav if (options.control_persist || no_shell_flag) { 2292e146993eSDag-Erling Smørgrav session_closed = 1; 2293e146993eSDag-Erling Smørgrav setproctitle("[stopped mux]"); 2294e146993eSDag-Erling Smørgrav } 2295e146993eSDag-Erling Smørgrav } 2296e146993eSDag-Erling Smørgrav 2297efcad6b7SDag-Erling Smørgrav /* client specific fatal cleanup */ 2298efcad6b7SDag-Erling Smørgrav void 2299efcad6b7SDag-Erling Smørgrav cleanup_exit(int i) 2300efcad6b7SDag-Erling Smørgrav { 2301e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 2302efcad6b7SDag-Erling Smørgrav leave_non_blocking(); 2303d4af9e69SDag-Erling Smørgrav if (options.control_path != NULL && muxserver_sock != -1) 2304d74d50a8SDag-Erling Smørgrav unlink(options.control_path); 23054a421b63SDag-Erling Smørgrav ssh_kill_proxy_command(); 2306efcad6b7SDag-Erling Smørgrav _exit(i); 2307efcad6b7SDag-Erling Smørgrav } 2308