1*190cef3dSDag-Erling Smørgrav /* $OpenBSD: clientloop.c,v 1.317 2018/07/11 18:53:29 markus 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" 94*190cef3dSDag-Erling Smørgrav #include "sshbuf.h" 95a04a10f8SKris Kennaway #include "compat.h" 96a04a10f8SKris Kennaway #include "channels.h" 97a04a10f8SKris Kennaway #include "dispatch.h" 98*190cef3dSDag-Erling Smørgrav #include "sshkey.h" 99761efaa7SDag-Erling Smørgrav #include "cipher.h" 1001e8db6e2SBrian Feldman #include "kex.h" 101eccfee6eSDag-Erling Smørgrav #include "myproposal.h" 1021e8db6e2SBrian Feldman #include "log.h" 103a0ee8cc6SDag-Erling Smørgrav #include "misc.h" 1041e8db6e2SBrian Feldman #include "readconf.h" 1051e8db6e2SBrian Feldman #include "clientloop.h" 106021d409fSDag-Erling Smørgrav #include "sshconnect.h" 1071e8db6e2SBrian Feldman #include "authfd.h" 1081e8db6e2SBrian Feldman #include "atomicio.h" 109d74d50a8SDag-Erling Smørgrav #include "sshpty.h" 110d74d50a8SDag-Erling Smørgrav #include "match.h" 111d74d50a8SDag-Erling Smørgrav #include "msg.h" 112bc5531deSDag-Erling Smørgrav #include "ssherr.h" 113bc5531deSDag-Erling Smørgrav #include "hostfile.h" 1145b9b2fafSBrian Feldman 1155b9b2fafSBrian Feldman /* import options */ 1164899dde7SBrian Feldman extern Options options; 1174899dde7SBrian Feldman 118511b41d2SMark Murray /* Flag indicating that stdin should be redirected from /dev/null. */ 119511b41d2SMark Murray extern int stdin_null_flag; 120511b41d2SMark Murray 121efcad6b7SDag-Erling Smørgrav /* Flag indicating that no shell has been requested */ 122efcad6b7SDag-Erling Smørgrav extern int no_shell_flag; 123efcad6b7SDag-Erling Smørgrav 124076ad2f8SDag-Erling Smørgrav /* Flag indicating that ssh should daemonise after authentication is complete */ 125076ad2f8SDag-Erling Smørgrav extern int fork_after_authentication_flag; 126076ad2f8SDag-Erling Smørgrav 127d74d50a8SDag-Erling Smørgrav /* Control socket */ 128b15c8340SDag-Erling Smørgrav extern int muxserver_sock; /* XXX use mux_client_cleanup() instead */ 129d74d50a8SDag-Erling Smørgrav 130511b41d2SMark Murray /* 131511b41d2SMark Murray * Name of the host we are connecting to. This is the name given on the 132511b41d2SMark Murray * command line, or the HostName specified for the user-supplied name in a 133511b41d2SMark Murray * configuration file. 134511b41d2SMark Murray */ 135511b41d2SMark Murray extern char *host; 136511b41d2SMark Murray 137511b41d2SMark Murray /* 138511b41d2SMark Murray * Flag to indicate that we have received a window change signal which has 139511b41d2SMark Murray * not yet been processed. This will cause a message indicating the new 140511b41d2SMark Murray * window size to be sent to the server a little later. This is volatile 141511b41d2SMark Murray * because this is updated in a signal handler. 142511b41d2SMark Murray */ 143ae1f160dSDag-Erling Smørgrav static volatile sig_atomic_t received_window_change_signal = 0; 144ae1f160dSDag-Erling Smørgrav static volatile sig_atomic_t received_signal = 0; 145511b41d2SMark Murray 146021d409fSDag-Erling Smørgrav /* Flag indicating whether the user's terminal is in non-blocking mode. */ 147511b41d2SMark Murray static int in_non_blocking_mode = 0; 148511b41d2SMark Murray 149e2f6069cSDag-Erling Smørgrav /* Time when backgrounded control master using ControlPersist should exit */ 150e2f6069cSDag-Erling Smørgrav static time_t control_persist_exit_time = 0; 151e2f6069cSDag-Erling Smørgrav 152511b41d2SMark Murray /* Common data for the client loop code. */ 153b15c8340SDag-Erling Smørgrav volatile sig_atomic_t quit_pending; /* Set non-zero to quit the loop. */ 154511b41d2SMark Murray static int last_was_cr; /* Last character was a newline. */ 155d4af9e69SDag-Erling Smørgrav static int exit_status; /* Used to store the command exit status. */ 156*190cef3dSDag-Erling Smørgrav static struct sshbuf *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 191*190cef3dSDag-Erling Smørgrav void ssh_process_session2_setup(int, int, int, struct sshbuf *); 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(); 496*190cef3dSDag-Erling Smørgrav int r, 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) { 5491e8db6e2SBrian Feldman /* 5501e8db6e2SBrian Feldman * We have to clear the select masks, because we return. 5511e8db6e2SBrian Feldman * We have to return, because the mainloop checks for the flags 5521e8db6e2SBrian Feldman * set by the signal handlers. 5531e8db6e2SBrian Feldman */ 554ae1f160dSDag-Erling Smørgrav memset(*readsetp, 0, *nallocp); 555ae1f160dSDag-Erling Smørgrav memset(*writesetp, 0, *nallocp); 5561e8db6e2SBrian Feldman 557511b41d2SMark Murray if (errno == EINTR) 558511b41d2SMark Murray return; 559511b41d2SMark Murray /* Note: we might still have data in the buffers. */ 560*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(stderr_buffer, 561*190cef3dSDag-Erling Smørgrav "select: %s\r\n", strerror(errno))) != 0) 562*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 563511b41d2SMark Murray quit_pending = 1; 564e4a9863fSDag-Erling Smørgrav } else if (ret == 0) { 565e4a9863fSDag-Erling Smørgrav /* 566e4a9863fSDag-Erling Smørgrav * Timeout. Could have been either keepalive or rekeying. 567e4a9863fSDag-Erling Smørgrav * Keepalive we check here, rekeying is checked in clientloop. 568e4a9863fSDag-Erling Smørgrav */ 569e4a9863fSDag-Erling Smørgrav if (server_alive_time != 0 && server_alive_time <= monotime()) 570efcad6b7SDag-Erling Smørgrav server_alive_check(); 571511b41d2SMark Murray } 572511b41d2SMark Murray 573e4a9863fSDag-Erling Smørgrav } 574e4a9863fSDag-Erling Smørgrav 575ae1f160dSDag-Erling Smørgrav static void 576*190cef3dSDag-Erling Smørgrav client_suspend_self(struct sshbuf *bin, struct sshbuf *bout, struct sshbuf *berr) 577511b41d2SMark Murray { 578511b41d2SMark Murray /* Flush stdout and stderr buffers. */ 579*190cef3dSDag-Erling Smørgrav if (sshbuf_len(bout) > 0) 580*190cef3dSDag-Erling Smørgrav atomicio(vwrite, fileno(stdout), sshbuf_mutable_ptr(bout), 581*190cef3dSDag-Erling Smørgrav sshbuf_len(bout)); 582*190cef3dSDag-Erling Smørgrav if (sshbuf_len(berr) > 0) 583*190cef3dSDag-Erling Smørgrav atomicio(vwrite, fileno(stderr), sshbuf_mutable_ptr(berr), 584*190cef3dSDag-Erling Smørgrav sshbuf_len(berr)); 585511b41d2SMark Murray 586e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 587511b41d2SMark Murray 5884f52dfbbSDag-Erling Smørgrav sshbuf_reset(bin); 5894f52dfbbSDag-Erling Smørgrav sshbuf_reset(bout); 5904f52dfbbSDag-Erling Smørgrav sshbuf_reset(berr); 591511b41d2SMark Murray 592511b41d2SMark Murray /* Send the suspend signal to the program itself. */ 593511b41d2SMark Murray kill(getpid(), SIGTSTP); 594511b41d2SMark Murray 5955e8dbd04SDag-Erling Smørgrav /* Reset window sizes in case they have changed */ 596511b41d2SMark Murray received_window_change_signal = 1; 597511b41d2SMark Murray 598e146993eSDag-Erling Smørgrav enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 599511b41d2SMark Murray } 600511b41d2SMark Murray 601ae1f160dSDag-Erling Smørgrav static void 602a04a10f8SKris Kennaway client_process_net_input(fd_set *readset) 603511b41d2SMark Murray { 6047aee6ffeSDag-Erling Smørgrav char buf[SSH_IOBUFSZ]; 605*190cef3dSDag-Erling Smørgrav int r, len; 606511b41d2SMark Murray 607511b41d2SMark Murray /* 608511b41d2SMark Murray * Read input from the server, and add any such data to the buffer of 609511b41d2SMark Murray * the packet subsystem. 610511b41d2SMark Murray */ 611511b41d2SMark Murray if (FD_ISSET(connection_in, readset)) { 612511b41d2SMark Murray /* Read as much as possible. */ 613acc1a9efSDag-Erling Smørgrav len = read(connection_in, buf, sizeof(buf)); 614acc1a9efSDag-Erling Smørgrav if (len == 0) { 615d4af9e69SDag-Erling Smørgrav /* 616d4af9e69SDag-Erling Smørgrav * Received EOF. The remote host has closed the 617d4af9e69SDag-Erling Smørgrav * connection. 618d4af9e69SDag-Erling Smørgrav */ 619*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(stderr_buffer, 620d4af9e69SDag-Erling Smørgrav "Connection to %.300s closed by remote host.\r\n", 621*190cef3dSDag-Erling Smørgrav host)) != 0) 622*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", 623*190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 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 */ 640*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(stderr_buffer, 641d4af9e69SDag-Erling Smørgrav "Read from remote host %.300s: %.100s\r\n", 642*190cef3dSDag-Erling Smørgrav host, strerror(errno))) != 0) 643*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", 644*190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 645511b41d2SMark Murray quit_pending = 1; 646511b41d2SMark Murray return; 647511b41d2SMark Murray } 648511b41d2SMark Murray packet_process_incoming(buf, len); 649511b41d2SMark Murray } 650a04a10f8SKris Kennaway } 651a04a10f8SKris Kennaway 652545d5ecaSDag-Erling Smørgrav static void 6534f52dfbbSDag-Erling Smørgrav client_status_confirm(struct ssh *ssh, int type, Channel *c, void *ctx) 654d74d50a8SDag-Erling Smørgrav { 655d4af9e69SDag-Erling Smørgrav struct channel_reply_ctx *cr = (struct channel_reply_ctx *)ctx; 656d4af9e69SDag-Erling Smørgrav char errmsg[256]; 657*190cef3dSDag-Erling Smørgrav int r, tochan; 658d74d50a8SDag-Erling Smørgrav 659e146993eSDag-Erling Smørgrav /* 660e146993eSDag-Erling Smørgrav * If a TTY was explicitly requested, then a failure to allocate 661e146993eSDag-Erling Smørgrav * one is fatal. 662e146993eSDag-Erling Smørgrav */ 663e146993eSDag-Erling Smørgrav if (cr->action == CONFIRM_TTY && 664e146993eSDag-Erling Smørgrav (options.request_tty == REQUEST_TTY_FORCE || 665e146993eSDag-Erling Smørgrav options.request_tty == REQUEST_TTY_YES)) 666e146993eSDag-Erling Smørgrav cr->action = CONFIRM_CLOSE; 667e146993eSDag-Erling Smørgrav 668*190cef3dSDag-Erling Smørgrav /* XXX suppress on mux _client_ quietmode */ 669d4af9e69SDag-Erling Smørgrav tochan = options.log_level >= SYSLOG_LEVEL_ERROR && 670b15c8340SDag-Erling Smørgrav c->ctl_chan != -1 && c->extended_usage == CHAN_EXTENDED_WRITE; 671d74d50a8SDag-Erling Smørgrav 672d4af9e69SDag-Erling Smørgrav if (type == SSH2_MSG_CHANNEL_SUCCESS) { 673d4af9e69SDag-Erling Smørgrav debug2("%s request accepted on channel %d", 674d4af9e69SDag-Erling Smørgrav cr->request_type, c->self); 675d4af9e69SDag-Erling Smørgrav } else if (type == SSH2_MSG_CHANNEL_FAILURE) { 676d4af9e69SDag-Erling Smørgrav if (tochan) { 677d4af9e69SDag-Erling Smørgrav snprintf(errmsg, sizeof(errmsg), 678d4af9e69SDag-Erling Smørgrav "%s request failed\r\n", cr->request_type); 679d4af9e69SDag-Erling Smørgrav } else { 680d4af9e69SDag-Erling Smørgrav snprintf(errmsg, sizeof(errmsg), 681d4af9e69SDag-Erling Smørgrav "%s request failed on channel %d", 682d4af9e69SDag-Erling Smørgrav cr->request_type, c->self); 683d74d50a8SDag-Erling Smørgrav } 684d4af9e69SDag-Erling Smørgrav /* If error occurred on primary session channel, then exit */ 685e146993eSDag-Erling Smørgrav if (cr->action == CONFIRM_CLOSE && c->self == session_ident) 686d4af9e69SDag-Erling Smørgrav fatal("%s", errmsg); 687e146993eSDag-Erling Smørgrav /* 688e146993eSDag-Erling Smørgrav * If error occurred on mux client, append to 689e146993eSDag-Erling Smørgrav * their stderr. 690e146993eSDag-Erling Smørgrav */ 691e146993eSDag-Erling Smørgrav if (tochan) { 692*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put(c->extended, errmsg, 693*190cef3dSDag-Erling Smørgrav strlen(errmsg))) != 0) 694*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error %s", __func__, 695*190cef3dSDag-Erling Smørgrav ssh_err(r)); 696e146993eSDag-Erling Smørgrav } else 697d4af9e69SDag-Erling Smørgrav error("%s", errmsg); 698e146993eSDag-Erling Smørgrav if (cr->action == CONFIRM_TTY) { 699e146993eSDag-Erling Smørgrav /* 700e146993eSDag-Erling Smørgrav * If a TTY allocation error occurred, then arrange 701e146993eSDag-Erling Smørgrav * for the correct TTY to leave raw mode. 702e146993eSDag-Erling Smørgrav */ 703e146993eSDag-Erling Smørgrav if (c->self == session_ident) 704e146993eSDag-Erling Smørgrav leave_raw_mode(0); 705e146993eSDag-Erling Smørgrav else 7064f52dfbbSDag-Erling Smørgrav mux_tty_alloc_failed(ssh, c); 707e146993eSDag-Erling Smørgrav } else if (cr->action == CONFIRM_CLOSE) { 7084f52dfbbSDag-Erling Smørgrav chan_read_failed(ssh, c); 7094f52dfbbSDag-Erling Smørgrav chan_write_failed(ssh, c); 710d74d50a8SDag-Erling Smørgrav } 711d74d50a8SDag-Erling Smørgrav } 712e4a9863fSDag-Erling Smørgrav free(cr); 713d4af9e69SDag-Erling Smørgrav } 714d74d50a8SDag-Erling Smørgrav 715d74d50a8SDag-Erling Smørgrav static void 7164f52dfbbSDag-Erling Smørgrav client_abandon_status_confirm(struct ssh *ssh, Channel *c, void *ctx) 717d74d50a8SDag-Erling Smørgrav { 718e4a9863fSDag-Erling Smørgrav free(ctx); 719d74d50a8SDag-Erling Smørgrav } 720d74d50a8SDag-Erling Smørgrav 721e146993eSDag-Erling Smørgrav void 7224f52dfbbSDag-Erling Smørgrav client_expect_confirm(struct ssh *ssh, int id, const char *request, 723e146993eSDag-Erling Smørgrav enum confirm_action action) 724d74d50a8SDag-Erling Smørgrav { 7250a37d4a3SXin LI struct channel_reply_ctx *cr = xcalloc(1, sizeof(*cr)); 726d74d50a8SDag-Erling Smørgrav 727d4af9e69SDag-Erling Smørgrav cr->request_type = request; 728e146993eSDag-Erling Smørgrav cr->action = action; 729d74d50a8SDag-Erling Smørgrav 7304f52dfbbSDag-Erling Smørgrav channel_register_status_confirm(ssh, id, client_status_confirm, 731d4af9e69SDag-Erling Smørgrav client_abandon_status_confirm, cr); 732d4af9e69SDag-Erling Smørgrav } 733d4af9e69SDag-Erling Smørgrav 734d4af9e69SDag-Erling Smørgrav void 735d4af9e69SDag-Erling Smørgrav client_register_global_confirm(global_confirm_cb *cb, void *ctx) 736d4af9e69SDag-Erling Smørgrav { 737d4af9e69SDag-Erling Smørgrav struct global_confirm *gc, *last_gc; 738d4af9e69SDag-Erling Smørgrav 739d4af9e69SDag-Erling Smørgrav /* Coalesce identical callbacks */ 740d4af9e69SDag-Erling Smørgrav last_gc = TAILQ_LAST(&global_confirms, global_confirms); 741d4af9e69SDag-Erling Smørgrav if (last_gc && last_gc->cb == cb && last_gc->ctx == ctx) { 742d4af9e69SDag-Erling Smørgrav if (++last_gc->ref_count >= INT_MAX) 743d4af9e69SDag-Erling Smørgrav fatal("%s: last_gc->ref_count = %d", 744d4af9e69SDag-Erling Smørgrav __func__, last_gc->ref_count); 745d74d50a8SDag-Erling Smørgrav return; 746d74d50a8SDag-Erling Smørgrav } 747d74d50a8SDag-Erling Smørgrav 7480a37d4a3SXin LI gc = xcalloc(1, sizeof(*gc)); 749d4af9e69SDag-Erling Smørgrav gc->cb = cb; 750d4af9e69SDag-Erling Smørgrav gc->ctx = ctx; 751d4af9e69SDag-Erling Smørgrav gc->ref_count = 1; 752d4af9e69SDag-Erling Smørgrav TAILQ_INSERT_TAIL(&global_confirms, gc, entry); 753d74d50a8SDag-Erling Smørgrav } 754d74d50a8SDag-Erling Smørgrav 755d74d50a8SDag-Erling Smørgrav static void 7564f52dfbbSDag-Erling Smørgrav process_cmdline(struct ssh *ssh) 757545d5ecaSDag-Erling Smørgrav { 758545d5ecaSDag-Erling Smørgrav void (*handler)(int); 759a0ee8cc6SDag-Erling Smørgrav char *s, *cmd; 760a0ee8cc6SDag-Erling Smørgrav int ok, delete = 0, local = 0, remote = 0, dynamic = 0; 761a0ee8cc6SDag-Erling Smørgrav struct Forward fwd; 762545d5ecaSDag-Erling Smørgrav 763b83788ffSDag-Erling Smørgrav memset(&fwd, 0, sizeof(fwd)); 764d4af9e69SDag-Erling Smørgrav 765e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 766545d5ecaSDag-Erling Smørgrav handler = signal(SIGINT, SIG_IGN); 767545d5ecaSDag-Erling Smørgrav cmd = s = read_passphrase("\r\nssh> ", RP_ECHO); 768545d5ecaSDag-Erling Smørgrav if (s == NULL) 769545d5ecaSDag-Erling Smørgrav goto out; 770f7167e0eSDag-Erling Smørgrav while (isspace((u_char)*s)) 771545d5ecaSDag-Erling Smørgrav s++; 772d74d50a8SDag-Erling Smørgrav if (*s == '-') 773d74d50a8SDag-Erling Smørgrav s++; /* Skip cmdline '-', if any */ 774d74d50a8SDag-Erling Smørgrav if (*s == '\0') 775545d5ecaSDag-Erling Smørgrav goto out; 776d74d50a8SDag-Erling Smørgrav 777d74d50a8SDag-Erling Smørgrav if (*s == 'h' || *s == 'H' || *s == '?') { 778d74d50a8SDag-Erling Smørgrav logit("Commands:"); 779761efaa7SDag-Erling Smørgrav logit(" -L[bind_address:]port:host:hostport " 780761efaa7SDag-Erling Smørgrav "Request local forward"); 781761efaa7SDag-Erling Smørgrav logit(" -R[bind_address:]port:host:hostport " 782761efaa7SDag-Erling Smørgrav "Request remote forward"); 783cce7d346SDag-Erling Smørgrav logit(" -D[bind_address:]port " 784cce7d346SDag-Erling Smørgrav "Request dynamic forward"); 785462c32cbSDag-Erling Smørgrav logit(" -KL[bind_address:]port " 786462c32cbSDag-Erling Smørgrav "Cancel local forward"); 787761efaa7SDag-Erling Smørgrav logit(" -KR[bind_address:]port " 788761efaa7SDag-Erling Smørgrav "Cancel remote forward"); 789462c32cbSDag-Erling Smørgrav logit(" -KD[bind_address:]port " 790462c32cbSDag-Erling Smørgrav "Cancel dynamic forward"); 791021d409fSDag-Erling Smørgrav if (!options.permit_local_command) 792021d409fSDag-Erling Smørgrav goto out; 793761efaa7SDag-Erling Smørgrav logit(" !args " 794761efaa7SDag-Erling Smørgrav "Execute local command"); 795021d409fSDag-Erling Smørgrav goto out; 796021d409fSDag-Erling Smørgrav } 797021d409fSDag-Erling Smørgrav 798021d409fSDag-Erling Smørgrav if (*s == '!' && options.permit_local_command) { 799021d409fSDag-Erling Smørgrav s++; 800021d409fSDag-Erling Smørgrav ssh_local_cmd(s); 801d74d50a8SDag-Erling Smørgrav goto out; 802d74d50a8SDag-Erling Smørgrav } 803d74d50a8SDag-Erling Smørgrav 804d74d50a8SDag-Erling Smørgrav if (*s == 'K') { 805d74d50a8SDag-Erling Smørgrav delete = 1; 806d74d50a8SDag-Erling Smørgrav s++; 807d74d50a8SDag-Erling Smørgrav } 808cce7d346SDag-Erling Smørgrav if (*s == 'L') 809cce7d346SDag-Erling Smørgrav local = 1; 810cce7d346SDag-Erling Smørgrav else if (*s == 'R') 811cce7d346SDag-Erling Smørgrav remote = 1; 812cce7d346SDag-Erling Smørgrav else if (*s == 'D') 813cce7d346SDag-Erling Smørgrav dynamic = 1; 814cce7d346SDag-Erling Smørgrav else { 815d95e11bfSDag-Erling Smørgrav logit("Invalid command."); 816545d5ecaSDag-Erling Smørgrav goto out; 817545d5ecaSDag-Erling Smørgrav } 818cce7d346SDag-Erling Smørgrav 819f7167e0eSDag-Erling Smørgrav while (isspace((u_char)*++s)) 820d4af9e69SDag-Erling Smørgrav ; 821545d5ecaSDag-Erling Smørgrav 822b15c8340SDag-Erling Smørgrav /* XXX update list of forwards in options */ 823d74d50a8SDag-Erling Smørgrav if (delete) { 824a0ee8cc6SDag-Erling Smørgrav /* We pass 1 for dynamicfwd to restrict to 1 or 2 fields. */ 825a0ee8cc6SDag-Erling Smørgrav if (!parse_forward(&fwd, s, 1, 0)) { 826a0ee8cc6SDag-Erling Smørgrav logit("Bad forwarding close specification."); 827545d5ecaSDag-Erling Smørgrav goto out; 828545d5ecaSDag-Erling Smørgrav } 829462c32cbSDag-Erling Smørgrav if (remote) 8304f52dfbbSDag-Erling Smørgrav ok = channel_request_rforward_cancel(ssh, &fwd) == 0; 831462c32cbSDag-Erling Smørgrav else if (dynamic) 8324f52dfbbSDag-Erling Smørgrav ok = channel_cancel_lport_listener(ssh, &fwd, 833a0ee8cc6SDag-Erling Smørgrav 0, &options.fwd_opts) > 0; 834462c32cbSDag-Erling Smørgrav else 8354f52dfbbSDag-Erling Smørgrav ok = channel_cancel_lport_listener(ssh, &fwd, 836a0ee8cc6SDag-Erling Smørgrav CHANNEL_CANCEL_PORT_STATIC, 837a0ee8cc6SDag-Erling Smørgrav &options.fwd_opts) > 0; 838462c32cbSDag-Erling Smørgrav if (!ok) { 839d93a896eSDag-Erling Smørgrav logit("Unknown port forwarding."); 840462c32cbSDag-Erling Smørgrav goto out; 841462c32cbSDag-Erling Smørgrav } 842462c32cbSDag-Erling Smørgrav logit("Canceled forwarding."); 8435e8dbd04SDag-Erling Smørgrav } else { 844cce7d346SDag-Erling Smørgrav if (!parse_forward(&fwd, s, dynamic, remote)) { 8455e8dbd04SDag-Erling Smørgrav logit("Bad forwarding specification."); 846545d5ecaSDag-Erling Smørgrav goto out; 847545d5ecaSDag-Erling Smørgrav } 848cce7d346SDag-Erling Smørgrav if (local || dynamic) { 8494f52dfbbSDag-Erling Smørgrav if (!channel_setup_local_fwd_listener(ssh, &fwd, 850a0ee8cc6SDag-Erling Smørgrav &options.fwd_opts)) { 851d95e11bfSDag-Erling Smørgrav logit("Port forwarding failed."); 852545d5ecaSDag-Erling Smørgrav goto out; 853545d5ecaSDag-Erling Smørgrav } 8545e8dbd04SDag-Erling Smørgrav } else { 8554f52dfbbSDag-Erling Smørgrav if (channel_request_remote_forwarding(ssh, &fwd) < 0) { 856761efaa7SDag-Erling Smørgrav logit("Port forwarding failed."); 857761efaa7SDag-Erling Smørgrav goto out; 858761efaa7SDag-Erling Smørgrav } 8595e8dbd04SDag-Erling Smørgrav } 860d95e11bfSDag-Erling Smørgrav logit("Forwarding port."); 861d74d50a8SDag-Erling Smørgrav } 862d74d50a8SDag-Erling Smørgrav 863545d5ecaSDag-Erling Smørgrav out: 864545d5ecaSDag-Erling Smørgrav signal(SIGINT, handler); 865e146993eSDag-Erling Smørgrav enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 866e4a9863fSDag-Erling Smørgrav free(cmd); 867e4a9863fSDag-Erling Smørgrav free(fwd.listen_host); 868a0ee8cc6SDag-Erling Smørgrav free(fwd.listen_path); 869e4a9863fSDag-Erling Smørgrav free(fwd.connect_host); 870a0ee8cc6SDag-Erling Smørgrav free(fwd.connect_path); 871545d5ecaSDag-Erling Smørgrav } 872545d5ecaSDag-Erling Smørgrav 8736888a9beSDag-Erling Smørgrav /* reasons to suppress output of an escape command in help output */ 8746888a9beSDag-Erling Smørgrav #define SUPPRESS_NEVER 0 /* never suppress, always show */ 8754f52dfbbSDag-Erling Smørgrav #define SUPPRESS_MUXCLIENT 1 /* don't show in mux client sessions */ 8764f52dfbbSDag-Erling Smørgrav #define SUPPRESS_MUXMASTER 2 /* don't show in mux master sessions */ 8774f52dfbbSDag-Erling Smørgrav #define SUPPRESS_SYSLOG 4 /* don't show when logging to syslog */ 8786888a9beSDag-Erling Smørgrav struct escape_help_text { 8796888a9beSDag-Erling Smørgrav const char *cmd; 8806888a9beSDag-Erling Smørgrav const char *text; 8816888a9beSDag-Erling Smørgrav unsigned int flags; 8826888a9beSDag-Erling Smørgrav }; 8836888a9beSDag-Erling Smørgrav static struct escape_help_text esc_txt[] = { 8846888a9beSDag-Erling Smørgrav {".", "terminate session", SUPPRESS_MUXMASTER}, 8856888a9beSDag-Erling Smørgrav {".", "terminate connection (and any multiplexed sessions)", 8866888a9beSDag-Erling Smørgrav SUPPRESS_MUXCLIENT}, 8874f52dfbbSDag-Erling Smørgrav {"B", "send a BREAK to the remote system", SUPPRESS_NEVER}, 8886888a9beSDag-Erling Smørgrav {"C", "open a command line", SUPPRESS_MUXCLIENT}, 8894f52dfbbSDag-Erling Smørgrav {"R", "request rekey", SUPPRESS_NEVER}, 8906888a9beSDag-Erling Smørgrav {"V/v", "decrease/increase verbosity (LogLevel)", SUPPRESS_MUXCLIENT}, 8916888a9beSDag-Erling Smørgrav {"^Z", "suspend ssh", SUPPRESS_MUXCLIENT}, 8926888a9beSDag-Erling Smørgrav {"#", "list forwarded connections", SUPPRESS_NEVER}, 8936888a9beSDag-Erling Smørgrav {"&", "background ssh (when waiting for connections to terminate)", 8946888a9beSDag-Erling Smørgrav SUPPRESS_MUXCLIENT}, 8956888a9beSDag-Erling Smørgrav {"?", "this message", SUPPRESS_NEVER}, 8966888a9beSDag-Erling Smørgrav }; 8976888a9beSDag-Erling Smørgrav 8986888a9beSDag-Erling Smørgrav static void 899*190cef3dSDag-Erling Smørgrav print_escape_help(struct sshbuf *b, int escape_char, int mux_client, 900*190cef3dSDag-Erling Smørgrav int using_stderr) 9016888a9beSDag-Erling Smørgrav { 9026888a9beSDag-Erling Smørgrav unsigned int i, suppress_flags; 903*190cef3dSDag-Erling Smørgrav int r; 9046888a9beSDag-Erling Smørgrav 905*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(b, 906*190cef3dSDag-Erling Smørgrav "%c?\r\nSupported escape sequences:\r\n", escape_char)) != 0) 907*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 9086888a9beSDag-Erling Smørgrav 9094f52dfbbSDag-Erling Smørgrav suppress_flags = 9106888a9beSDag-Erling Smørgrav (mux_client ? SUPPRESS_MUXCLIENT : 0) | 9116888a9beSDag-Erling Smørgrav (mux_client ? 0 : SUPPRESS_MUXMASTER) | 9126888a9beSDag-Erling Smørgrav (using_stderr ? 0 : SUPPRESS_SYSLOG); 9136888a9beSDag-Erling Smørgrav 9146888a9beSDag-Erling Smørgrav for (i = 0; i < sizeof(esc_txt)/sizeof(esc_txt[0]); i++) { 9156888a9beSDag-Erling Smørgrav if (esc_txt[i].flags & suppress_flags) 9166888a9beSDag-Erling Smørgrav continue; 917*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(b, " %c%-3s - %s\r\n", 918*190cef3dSDag-Erling Smørgrav escape_char, esc_txt[i].cmd, esc_txt[i].text)) != 0) 919*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 9206888a9beSDag-Erling Smørgrav } 9216888a9beSDag-Erling Smørgrav 922*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(b, 9236888a9beSDag-Erling Smørgrav " %c%c - send the escape character by typing it twice\r\n" 9246888a9beSDag-Erling Smørgrav "(Note that escapes are only recognized immediately after " 925*190cef3dSDag-Erling Smørgrav "newline.)\r\n", escape_char, escape_char)) != 0) 926*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 9276888a9beSDag-Erling Smørgrav } 9286888a9beSDag-Erling Smørgrav 929d4af9e69SDag-Erling Smørgrav /* 9304f52dfbbSDag-Erling Smørgrav * Process the characters one by one. 931d4af9e69SDag-Erling Smørgrav */ 932ae1f160dSDag-Erling Smørgrav static int 9334f52dfbbSDag-Erling Smørgrav process_escapes(struct ssh *ssh, Channel *c, 934*190cef3dSDag-Erling Smørgrav struct sshbuf *bin, struct sshbuf *bout, struct sshbuf *berr, 935d4af9e69SDag-Erling Smørgrav char *buf, int len) 936b66f2d16SKris Kennaway { 937b66f2d16SKris Kennaway pid_t pid; 938*190cef3dSDag-Erling Smørgrav int r, bytes = 0; 9391e8db6e2SBrian Feldman u_int i; 9401e8db6e2SBrian Feldman u_char ch; 941b66f2d16SKris Kennaway char *s; 9424f52dfbbSDag-Erling Smørgrav struct escape_filter_ctx *efc = c->filter_ctx == NULL ? 9434f52dfbbSDag-Erling Smørgrav NULL : (struct escape_filter_ctx *)c->filter_ctx; 944d4af9e69SDag-Erling Smørgrav 945d4af9e69SDag-Erling Smørgrav if (c->filter_ctx == NULL) 946d4af9e69SDag-Erling Smørgrav return 0; 947b66f2d16SKris Kennaway 948043840dfSDag-Erling Smørgrav if (len <= 0) 949043840dfSDag-Erling Smørgrav return (0); 950043840dfSDag-Erling Smørgrav 951043840dfSDag-Erling Smørgrav for (i = 0; i < (u_int)len; i++) { 952b66f2d16SKris Kennaway /* Get one character at a time. */ 953b66f2d16SKris Kennaway ch = buf[i]; 954b66f2d16SKris Kennaway 9554f52dfbbSDag-Erling Smørgrav if (efc->escape_pending) { 956b66f2d16SKris Kennaway /* We have previously seen an escape character. */ 957b66f2d16SKris Kennaway /* Clear the flag now. */ 9584f52dfbbSDag-Erling Smørgrav efc->escape_pending = 0; 959b66f2d16SKris Kennaway 960b66f2d16SKris Kennaway /* Process the escaped character. */ 961b66f2d16SKris Kennaway switch (ch) { 962b66f2d16SKris Kennaway case '.': 963b66f2d16SKris Kennaway /* Terminate the connection. */ 964*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(berr, "%c.\r\n", 965*190cef3dSDag-Erling Smørgrav efc->escape_char)) != 0) 966*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", 967*190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 968b15c8340SDag-Erling Smørgrav if (c && c->ctl_chan != -1) { 9694f52dfbbSDag-Erling Smørgrav chan_read_failed(ssh, c); 9704f52dfbbSDag-Erling Smørgrav chan_write_failed(ssh, c); 9714f52dfbbSDag-Erling Smørgrav if (c->detach_user) { 9724f52dfbbSDag-Erling Smørgrav c->detach_user(ssh, 9734f52dfbbSDag-Erling Smørgrav c->self, NULL); 9744f52dfbbSDag-Erling Smørgrav } 975e4a9863fSDag-Erling Smørgrav c->type = SSH_CHANNEL_ABANDONED; 976*190cef3dSDag-Erling Smørgrav sshbuf_reset(c->input); 9774f52dfbbSDag-Erling Smørgrav chan_ibuf_empty(ssh, c); 978d4af9e69SDag-Erling Smørgrav return 0; 979d4af9e69SDag-Erling Smørgrav } else 980b66f2d16SKris Kennaway quit_pending = 1; 981b66f2d16SKris Kennaway return -1; 982b66f2d16SKris Kennaway 983b66f2d16SKris Kennaway case 'Z' - 64: 984d4af9e69SDag-Erling Smørgrav /* XXX support this for mux clients */ 985b15c8340SDag-Erling Smørgrav if (c && c->ctl_chan != -1) { 9866888a9beSDag-Erling Smørgrav char b[16]; 987d4af9e69SDag-Erling Smørgrav noescape: 9886888a9beSDag-Erling Smørgrav if (ch == 'Z' - 64) 9896888a9beSDag-Erling Smørgrav snprintf(b, sizeof b, "^Z"); 9906888a9beSDag-Erling Smørgrav else 9916888a9beSDag-Erling Smørgrav snprintf(b, sizeof b, "%c", ch); 992*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(berr, 9936888a9beSDag-Erling Smørgrav "%c%s escape not available to " 994d4af9e69SDag-Erling Smørgrav "multiplexed sessions\r\n", 995*190cef3dSDag-Erling Smørgrav efc->escape_char, b)) != 0) 996*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", 997*190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 998d4af9e69SDag-Erling Smørgrav continue; 999d4af9e69SDag-Erling Smørgrav } 1000d4af9e69SDag-Erling Smørgrav /* Suspend the program. Inform the user */ 1001*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(berr, 1002*190cef3dSDag-Erling Smørgrav "%c^Z [suspend ssh]\r\n", 1003*190cef3dSDag-Erling Smørgrav efc->escape_char)) != 0) 1004*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", 1005*190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 1006b66f2d16SKris Kennaway 1007b66f2d16SKris Kennaway /* Restore terminal modes and suspend. */ 1008b66f2d16SKris Kennaway client_suspend_self(bin, bout, berr); 1009b66f2d16SKris Kennaway 1010b66f2d16SKris Kennaway /* We have been continued. */ 1011b66f2d16SKris Kennaway continue; 1012b66f2d16SKris Kennaway 1013d95e11bfSDag-Erling Smørgrav case 'B': 1014*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(berr, 1015*190cef3dSDag-Erling Smørgrav "%cB\r\n", efc->escape_char)) != 0) 1016*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", 1017*190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 10184f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, c->self, "break", 0); 1019*190cef3dSDag-Erling Smørgrav if ((r = sshpkt_put_u32(ssh, 1000)) != 0 || 1020*190cef3dSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) 1021*190cef3dSDag-Erling Smørgrav fatal("%s: %s", __func__, 1022*190cef3dSDag-Erling Smørgrav ssh_err(r)); 1023d95e11bfSDag-Erling Smørgrav continue; 1024d95e11bfSDag-Erling Smørgrav 10251e8db6e2SBrian Feldman case 'R': 10261e8db6e2SBrian Feldman if (datafellows & SSH_BUG_NOREKEY) 1027d4af9e69SDag-Erling Smørgrav logit("Server does not " 1028d4af9e69SDag-Erling Smørgrav "support re-keying"); 10291e8db6e2SBrian Feldman else 10301e8db6e2SBrian Feldman need_rekeying = 1; 10311e8db6e2SBrian Feldman continue; 10321e8db6e2SBrian Feldman 10336888a9beSDag-Erling Smørgrav case 'V': 10346888a9beSDag-Erling Smørgrav /* FALLTHROUGH */ 10356888a9beSDag-Erling Smørgrav case 'v': 10366888a9beSDag-Erling Smørgrav if (c && c->ctl_chan != -1) 10376888a9beSDag-Erling Smørgrav goto noescape; 10386888a9beSDag-Erling Smørgrav if (!log_is_on_stderr()) { 1039*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(berr, 10406888a9beSDag-Erling Smørgrav "%c%c [Logging to syslog]\r\n", 1041*190cef3dSDag-Erling Smørgrav efc->escape_char, ch)) != 0) 1042*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", 1043*190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 10446888a9beSDag-Erling Smørgrav continue; 10456888a9beSDag-Erling Smørgrav } 10466888a9beSDag-Erling Smørgrav if (ch == 'V' && options.log_level > 10476888a9beSDag-Erling Smørgrav SYSLOG_LEVEL_QUIET) 10486888a9beSDag-Erling Smørgrav log_change_level(--options.log_level); 10496888a9beSDag-Erling Smørgrav if (ch == 'v' && options.log_level < 10506888a9beSDag-Erling Smørgrav SYSLOG_LEVEL_DEBUG3) 10516888a9beSDag-Erling Smørgrav log_change_level(++options.log_level); 1052*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(berr, 10534f52dfbbSDag-Erling Smørgrav "%c%c [LogLevel %s]\r\n", 10544f52dfbbSDag-Erling Smørgrav efc->escape_char, ch, 1055*190cef3dSDag-Erling Smørgrav log_level_name(options.log_level))) != 0) 1056*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", 1057*190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 10586888a9beSDag-Erling Smørgrav continue; 10596888a9beSDag-Erling Smørgrav 1060b66f2d16SKris Kennaway case '&': 1061b15c8340SDag-Erling Smørgrav if (c && c->ctl_chan != -1) 1062d4af9e69SDag-Erling Smørgrav goto noescape; 1063b66f2d16SKris Kennaway /* 1064d4af9e69SDag-Erling Smørgrav * Detach the program (continue to serve 1065d4af9e69SDag-Erling Smørgrav * connections, but put in background and no 1066d4af9e69SDag-Erling Smørgrav * more new connections). 1067b66f2d16SKris Kennaway */ 1068ae1f160dSDag-Erling Smørgrav /* Restore tty modes. */ 1069e146993eSDag-Erling Smørgrav leave_raw_mode( 1070e146993eSDag-Erling Smørgrav options.request_tty == REQUEST_TTY_FORCE); 1071ae1f160dSDag-Erling Smørgrav 1072ae1f160dSDag-Erling Smørgrav /* Stop listening for new connections. */ 10734f52dfbbSDag-Erling Smørgrav channel_stop_listening(ssh); 1074ae1f160dSDag-Erling Smørgrav 1075*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(berr, 1076*190cef3dSDag-Erling Smørgrav "%c& [backgrounded]\n", efc->escape_char)) 1077*190cef3dSDag-Erling Smørgrav != 0) 1078*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", 1079*190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 1080ae1f160dSDag-Erling Smørgrav 1081ae1f160dSDag-Erling Smørgrav /* Fork into background. */ 1082ae1f160dSDag-Erling Smørgrav pid = fork(); 1083ae1f160dSDag-Erling Smørgrav if (pid < 0) { 1084ae1f160dSDag-Erling Smørgrav error("fork: %.100s", strerror(errno)); 1085ae1f160dSDag-Erling Smørgrav continue; 1086ae1f160dSDag-Erling Smørgrav } 1087ae1f160dSDag-Erling Smørgrav if (pid != 0) { /* This is the parent. */ 1088ae1f160dSDag-Erling Smørgrav /* The parent just exits. */ 1089ae1f160dSDag-Erling Smørgrav exit(0); 1090ae1f160dSDag-Erling Smørgrav } 1091ae1f160dSDag-Erling Smørgrav /* The child continues serving connections. */ 1092ae1f160dSDag-Erling Smørgrav /* fake EOF on stdin */ 1093*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u8(bin, 4)) != 0) 1094*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", 1095*190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 1096ae1f160dSDag-Erling Smørgrav return -1; 1097b66f2d16SKris Kennaway case '?': 10984f52dfbbSDag-Erling Smørgrav print_escape_help(berr, efc->escape_char, 10996888a9beSDag-Erling Smørgrav (c && c->ctl_chan != -1), 11006888a9beSDag-Erling Smørgrav log_is_on_stderr()); 1101b66f2d16SKris Kennaway continue; 1102b66f2d16SKris Kennaway 1103b66f2d16SKris Kennaway case '#': 1104*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(berr, "%c#\r\n", 1105*190cef3dSDag-Erling Smørgrav efc->escape_char)) != 0) 1106*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", 1107*190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 11084f52dfbbSDag-Erling Smørgrav s = channel_open_message(ssh); 1109*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put(berr, s, strlen(s))) != 0) 1110*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", 1111*190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 1112e4a9863fSDag-Erling Smørgrav free(s); 1113b66f2d16SKris Kennaway continue; 1114b66f2d16SKris Kennaway 1115545d5ecaSDag-Erling Smørgrav case 'C': 1116b15c8340SDag-Erling Smørgrav if (c && c->ctl_chan != -1) 1117cce7d346SDag-Erling Smørgrav goto noescape; 11184f52dfbbSDag-Erling Smørgrav process_cmdline(ssh); 1119545d5ecaSDag-Erling Smørgrav continue; 1120545d5ecaSDag-Erling Smørgrav 1121b66f2d16SKris Kennaway default: 11224f52dfbbSDag-Erling Smørgrav if (ch != efc->escape_char) { 1123*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u8(bin, 1124*190cef3dSDag-Erling Smørgrav efc->escape_char)) != 0) 1125*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", 1126*190cef3dSDag-Erling Smørgrav __func__, ssh_err(r)); 1127b66f2d16SKris Kennaway bytes++; 1128b66f2d16SKris Kennaway } 1129b66f2d16SKris Kennaway /* Escaped characters fall through here */ 1130b66f2d16SKris Kennaway break; 1131b66f2d16SKris Kennaway } 1132b66f2d16SKris Kennaway } else { 1133b66f2d16SKris Kennaway /* 1134d4af9e69SDag-Erling Smørgrav * The previous character was not an escape char. 1135d4af9e69SDag-Erling Smørgrav * Check if this is an escape. 1136b66f2d16SKris Kennaway */ 11374f52dfbbSDag-Erling Smørgrav if (last_was_cr && ch == efc->escape_char) { 1138d4af9e69SDag-Erling Smørgrav /* 1139d4af9e69SDag-Erling Smørgrav * It is. Set the flag and continue to 1140d4af9e69SDag-Erling Smørgrav * next character. 1141d4af9e69SDag-Erling Smørgrav */ 11424f52dfbbSDag-Erling Smørgrav efc->escape_pending = 1; 1143b66f2d16SKris Kennaway continue; 1144b66f2d16SKris Kennaway } 1145b66f2d16SKris Kennaway } 1146b66f2d16SKris Kennaway 1147b66f2d16SKris Kennaway /* 1148b66f2d16SKris Kennaway * Normal character. Record whether it was a newline, 1149b66f2d16SKris Kennaway * and append it to the buffer. 1150b66f2d16SKris Kennaway */ 1151b66f2d16SKris Kennaway last_was_cr = (ch == '\r' || ch == '\n'); 1152*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u8(bin, ch)) != 0) 1153*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1154b66f2d16SKris Kennaway bytes++; 1155b66f2d16SKris Kennaway } 1156b66f2d16SKris Kennaway return bytes; 1157b66f2d16SKris Kennaway } 1158b66f2d16SKris Kennaway 1159511b41d2SMark Murray /* 1160a04a10f8SKris Kennaway * Get packets from the connection input buffer, and process them as long as 1161a04a10f8SKris Kennaway * there are packets available. 1162a04a10f8SKris Kennaway * 1163a04a10f8SKris Kennaway * Any unknown packets received during the actual 1164a04a10f8SKris Kennaway * session cause the session to terminate. This is 1165a04a10f8SKris Kennaway * intended to make debugging easier since no 1166a04a10f8SKris Kennaway * confirmations are sent. Any compatible protocol 1167a04a10f8SKris Kennaway * extensions must be negotiated during the 1168a04a10f8SKris Kennaway * preparatory phase. 1169a04a10f8SKris Kennaway */ 1170a04a10f8SKris Kennaway 1171ae1f160dSDag-Erling Smørgrav static void 11721e8db6e2SBrian Feldman client_process_buffered_input_packets(void) 1173a04a10f8SKris Kennaway { 11744f52dfbbSDag-Erling Smørgrav ssh_dispatch_run_fatal(active_state, DISPATCH_NONBLOCK, &quit_pending); 1175a04a10f8SKris Kennaway } 1176a04a10f8SKris Kennaway 1177b66f2d16SKris Kennaway /* scan buf[] for '~' before sending data to the peer */ 1178b66f2d16SKris Kennaway 1179d4af9e69SDag-Erling Smørgrav /* Helper: allocate a new escape_filter_ctx and fill in its escape char */ 1180d4af9e69SDag-Erling Smørgrav void * 1181d4af9e69SDag-Erling Smørgrav client_new_escape_filter_ctx(int escape_char) 1182b66f2d16SKris Kennaway { 1183d4af9e69SDag-Erling Smørgrav struct escape_filter_ctx *ret; 1184d4af9e69SDag-Erling Smørgrav 11850a37d4a3SXin LI ret = xcalloc(1, sizeof(*ret)); 1186d4af9e69SDag-Erling Smørgrav ret->escape_pending = 0; 1187d4af9e69SDag-Erling Smørgrav ret->escape_char = escape_char; 1188d4af9e69SDag-Erling Smørgrav return (void *)ret; 1189d4af9e69SDag-Erling Smørgrav } 1190d4af9e69SDag-Erling Smørgrav 1191d4af9e69SDag-Erling Smørgrav /* Free the escape filter context on channel free */ 1192d4af9e69SDag-Erling Smørgrav void 11934f52dfbbSDag-Erling Smørgrav client_filter_cleanup(struct ssh *ssh, int cid, void *ctx) 1194d4af9e69SDag-Erling Smørgrav { 1195e4a9863fSDag-Erling Smørgrav free(ctx); 1196d4af9e69SDag-Erling Smørgrav } 1197d4af9e69SDag-Erling Smørgrav 1198d4af9e69SDag-Erling Smørgrav int 11994f52dfbbSDag-Erling Smørgrav client_simple_escape_filter(struct ssh *ssh, Channel *c, char *buf, int len) 1200d4af9e69SDag-Erling Smørgrav { 1201d4af9e69SDag-Erling Smørgrav if (c->extended_usage != CHAN_EXTENDED_WRITE) 1202d4af9e69SDag-Erling Smørgrav return 0; 1203d4af9e69SDag-Erling Smørgrav 12044f52dfbbSDag-Erling Smørgrav return process_escapes(ssh, c, c->input, c->output, c->extended, 1205d4af9e69SDag-Erling Smørgrav buf, len); 1206b66f2d16SKris Kennaway } 1207b66f2d16SKris Kennaway 1208ae1f160dSDag-Erling Smørgrav static void 12094f52dfbbSDag-Erling Smørgrav client_channel_closed(struct ssh *ssh, int id, void *arg) 12101e8db6e2SBrian Feldman { 12114f52dfbbSDag-Erling Smørgrav channel_cancel_cleanup(ssh, id); 12121e8db6e2SBrian Feldman session_closed = 1; 1213e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 12141e8db6e2SBrian Feldman } 12151e8db6e2SBrian Feldman 1216a04a10f8SKris Kennaway /* 1217511b41d2SMark Murray * Implements the interactive session with the server. This is called after 1218511b41d2SMark Murray * the user has been authenticated, and a command has been started on the 1219ae1f160dSDag-Erling Smørgrav * remote host. If escape_char != SSH_ESCAPECHAR_NONE, it is the character 1220ae1f160dSDag-Erling Smørgrav * used as an escape character for terminating or suspending the session. 1221511b41d2SMark Murray */ 1222511b41d2SMark Murray int 12234f52dfbbSDag-Erling Smørgrav client_loop(struct ssh *ssh, int have_pty, int escape_char_arg, 12244f52dfbbSDag-Erling Smørgrav int ssh2_chan_id) 1225511b41d2SMark Murray { 12261e8db6e2SBrian Feldman fd_set *readset = NULL, *writeset = NULL; 1227511b41d2SMark Murray double start_time, total_time; 1228acc1a9efSDag-Erling Smørgrav int r, max_fd = 0, max_fd2 = 0, len; 1229d4af9e69SDag-Erling Smørgrav u_int64_t ibytes, obytes; 1230d74d50a8SDag-Erling Smørgrav u_int nalloc = 0; 1231511b41d2SMark Murray char buf[100]; 1232511b41d2SMark Murray 1233511b41d2SMark Murray debug("Entering interactive session."); 1234511b41d2SMark Murray 1235acc1a9efSDag-Erling Smørgrav if (options.control_master && 1236acc1a9efSDag-Erling Smørgrav !option_clear_or_none(options.control_path)) { 1237acc1a9efSDag-Erling Smørgrav debug("pledge: id"); 1238076ad2f8SDag-Erling Smørgrav if (pledge("stdio rpath wpath cpath unix inet dns recvfd proc exec id tty", 1239acc1a9efSDag-Erling Smørgrav NULL) == -1) 1240acc1a9efSDag-Erling Smørgrav fatal("%s pledge(): %s", __func__, strerror(errno)); 1241acc1a9efSDag-Erling Smørgrav 1242acc1a9efSDag-Erling Smørgrav } else if (options.forward_x11 || options.permit_local_command) { 1243acc1a9efSDag-Erling Smørgrav debug("pledge: exec"); 1244acc1a9efSDag-Erling Smørgrav if (pledge("stdio rpath wpath cpath unix inet dns proc exec tty", 1245acc1a9efSDag-Erling Smørgrav NULL) == -1) 1246acc1a9efSDag-Erling Smørgrav fatal("%s pledge(): %s", __func__, strerror(errno)); 1247acc1a9efSDag-Erling Smørgrav 1248acc1a9efSDag-Erling Smørgrav } else if (options.update_hostkeys) { 1249acc1a9efSDag-Erling Smørgrav debug("pledge: filesystem full"); 1250acc1a9efSDag-Erling Smørgrav if (pledge("stdio rpath wpath cpath unix inet dns proc tty", 1251acc1a9efSDag-Erling Smørgrav NULL) == -1) 1252acc1a9efSDag-Erling Smørgrav fatal("%s pledge(): %s", __func__, strerror(errno)); 1253acc1a9efSDag-Erling Smørgrav 1254076ad2f8SDag-Erling Smørgrav } else if (!option_clear_or_none(options.proxy_command) || 1255076ad2f8SDag-Erling Smørgrav fork_after_authentication_flag) { 1256acc1a9efSDag-Erling Smørgrav debug("pledge: proc"); 1257acc1a9efSDag-Erling Smørgrav if (pledge("stdio cpath unix inet dns proc tty", NULL) == -1) 1258acc1a9efSDag-Erling Smørgrav fatal("%s pledge(): %s", __func__, strerror(errno)); 1259acc1a9efSDag-Erling Smørgrav 1260acc1a9efSDag-Erling Smørgrav } else { 1261acc1a9efSDag-Erling Smørgrav debug("pledge: network"); 12624f52dfbbSDag-Erling Smørgrav if (pledge("stdio unix inet dns proc tty", NULL) == -1) 1263acc1a9efSDag-Erling Smørgrav fatal("%s pledge(): %s", __func__, strerror(errno)); 1264acc1a9efSDag-Erling Smørgrav } 1265acc1a9efSDag-Erling Smørgrav 126647dd1d1bSDag-Erling Smørgrav start_time = monotime_double(); 1267511b41d2SMark Murray 1268511b41d2SMark Murray /* Initialize variables. */ 1269511b41d2SMark Murray last_was_cr = 1; 1270511b41d2SMark Murray exit_status = -1; 1271511b41d2SMark Murray connection_in = packet_get_connection_in(); 1272511b41d2SMark Murray connection_out = packet_get_connection_out(); 1273ca86bcf2SDag-Erling Smørgrav max_fd = MAXIMUM(connection_in, connection_out); 12741e8db6e2SBrian Feldman 1275511b41d2SMark Murray quit_pending = 0; 1276511b41d2SMark Murray 1277*190cef3dSDag-Erling Smørgrav /* Initialize buffer. */ 1278*190cef3dSDag-Erling Smørgrav if ((stderr_buffer = sshbuf_new()) == NULL) 1279*190cef3dSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 1280511b41d2SMark Murray 1281a04a10f8SKris Kennaway client_init_dispatch(); 1282a04a10f8SKris Kennaway 1283d0c8c0bcSDag-Erling Smørgrav /* 1284d0c8c0bcSDag-Erling Smørgrav * Set signal handlers, (e.g. to restore non-blocking mode) 1285d0c8c0bcSDag-Erling Smørgrav * but don't overwrite SIG_IGN, matches behaviour from rsh(1) 1286d0c8c0bcSDag-Erling Smørgrav */ 12875e8dbd04SDag-Erling Smørgrav if (signal(SIGHUP, SIG_IGN) != SIG_IGN) 12885e8dbd04SDag-Erling Smørgrav signal(SIGHUP, signal_handler); 1289d0c8c0bcSDag-Erling Smørgrav if (signal(SIGINT, SIG_IGN) != SIG_IGN) 1290511b41d2SMark Murray signal(SIGINT, signal_handler); 1291d0c8c0bcSDag-Erling Smørgrav if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) 1292511b41d2SMark Murray signal(SIGQUIT, signal_handler); 1293d0c8c0bcSDag-Erling Smørgrav if (signal(SIGTERM, SIG_IGN) != SIG_IGN) 1294511b41d2SMark Murray signal(SIGTERM, signal_handler); 1295511b41d2SMark Murray signal(SIGWINCH, window_change_handler); 1296511b41d2SMark Murray 1297511b41d2SMark Murray if (have_pty) 1298e146993eSDag-Erling Smørgrav enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 1299511b41d2SMark Murray 13001e8db6e2SBrian Feldman session_ident = ssh2_chan_id; 1301e146993eSDag-Erling Smørgrav if (session_ident != -1) { 1302e146993eSDag-Erling Smørgrav if (escape_char_arg != SSH_ESCAPECHAR_NONE) { 13034f52dfbbSDag-Erling Smørgrav channel_register_filter(ssh, session_ident, 1304d4af9e69SDag-Erling Smørgrav client_simple_escape_filter, NULL, 1305d4af9e69SDag-Erling Smørgrav client_filter_cleanup, 1306e146993eSDag-Erling Smørgrav client_new_escape_filter_ctx( 1307e146993eSDag-Erling Smørgrav escape_char_arg)); 1308e146993eSDag-Erling Smørgrav } 13094f52dfbbSDag-Erling Smørgrav channel_register_cleanup(ssh, session_ident, 1310021d409fSDag-Erling Smørgrav client_channel_closed, 0); 1311e146993eSDag-Erling Smørgrav } 1312b66f2d16SKris Kennaway 1313511b41d2SMark Murray /* Main loop of the client for the interactive session mode. */ 1314511b41d2SMark Murray while (!quit_pending) { 1315511b41d2SMark Murray 1316511b41d2SMark Murray /* Process buffered packets sent by the server. */ 1317511b41d2SMark Murray client_process_buffered_input_packets(); 1318511b41d2SMark Murray 13194f52dfbbSDag-Erling Smørgrav if (session_closed && !channel_still_open(ssh)) 1320a04a10f8SKris Kennaway break; 1321a04a10f8SKris Kennaway 13224f52dfbbSDag-Erling Smørgrav if (ssh_packet_is_rekeying(ssh)) { 13231e8db6e2SBrian Feldman debug("rekeying in progress"); 1324acc1a9efSDag-Erling Smørgrav } else if (need_rekeying) { 1325acc1a9efSDag-Erling Smørgrav /* manual rekey request */ 1326acc1a9efSDag-Erling Smørgrav debug("need rekeying"); 13274f52dfbbSDag-Erling Smørgrav if ((r = kex_start_rekex(ssh)) != 0) 1328acc1a9efSDag-Erling Smørgrav fatal("%s: kex_start_rekex: %s", __func__, 1329acc1a9efSDag-Erling Smørgrav ssh_err(r)); 1330acc1a9efSDag-Erling Smørgrav need_rekeying = 0; 13311e8db6e2SBrian Feldman } else { 1332511b41d2SMark Murray /* 13331e8db6e2SBrian Feldman * Make packets from buffered channel data, and 13341e8db6e2SBrian Feldman * enqueue them for sending to the server. 1335511b41d2SMark Murray */ 1336511b41d2SMark Murray if (packet_not_very_much_data_to_write()) 13374f52dfbbSDag-Erling Smørgrav channel_output_poll(ssh); 1338511b41d2SMark Murray 1339511b41d2SMark Murray /* 13401e8db6e2SBrian Feldman * Check if the window size has changed, and buffer a 13411e8db6e2SBrian Feldman * message about it to the server if so. 1342511b41d2SMark Murray */ 13434f52dfbbSDag-Erling Smørgrav client_check_window_change(ssh); 1344511b41d2SMark Murray 1345511b41d2SMark Murray if (quit_pending) 1346511b41d2SMark Murray break; 13471e8db6e2SBrian Feldman } 1348511b41d2SMark Murray /* 1349511b41d2SMark Murray * Wait until we have something to do (something becomes 1350511b41d2SMark Murray * available on one of the descriptors). 1351511b41d2SMark Murray */ 1352ae1f160dSDag-Erling Smørgrav max_fd2 = max_fd; 13534f52dfbbSDag-Erling Smørgrav client_wait_until_can_do_something(ssh, &readset, &writeset, 13544f52dfbbSDag-Erling Smørgrav &max_fd2, &nalloc, ssh_packet_is_rekeying(ssh)); 1355511b41d2SMark Murray 1356511b41d2SMark Murray if (quit_pending) 1357511b41d2SMark Murray break; 1358511b41d2SMark Murray 13591e8db6e2SBrian Feldman /* Do channel operations unless rekeying in progress. */ 13604f52dfbbSDag-Erling Smørgrav if (!ssh_packet_is_rekeying(ssh)) 13614f52dfbbSDag-Erling Smørgrav channel_after_select(ssh, readset, writeset); 1362511b41d2SMark Murray 1363a04a10f8SKris Kennaway /* Buffer input from the connection. */ 13641e8db6e2SBrian Feldman client_process_net_input(readset); 1365511b41d2SMark Murray 1366a04a10f8SKris Kennaway if (quit_pending) 1367a04a10f8SKris Kennaway break; 1368a04a10f8SKris Kennaway 1369d4af9e69SDag-Erling Smørgrav /* 1370d4af9e69SDag-Erling Smørgrav * Send as much buffered packet data as possible to the 1371d4af9e69SDag-Erling Smørgrav * sender. 1372d4af9e69SDag-Erling Smørgrav */ 13731e8db6e2SBrian Feldman if (FD_ISSET(connection_out, writeset)) 1374511b41d2SMark Murray packet_write_poll(); 1375e2f6069cSDag-Erling Smørgrav 1376e2f6069cSDag-Erling Smørgrav /* 1377e2f6069cSDag-Erling Smørgrav * If we are a backgrounded control master, and the 1378e2f6069cSDag-Erling Smørgrav * timeout has expired without any active client 1379e2f6069cSDag-Erling Smørgrav * connections, then quit. 1380e2f6069cSDag-Erling Smørgrav */ 1381e2f6069cSDag-Erling Smørgrav if (control_persist_exit_time > 0) { 1382e4a9863fSDag-Erling Smørgrav if (monotime() >= control_persist_exit_time) { 1383e2f6069cSDag-Erling Smørgrav debug("ControlPersist timeout expired"); 1384e2f6069cSDag-Erling Smørgrav break; 1385e2f6069cSDag-Erling Smørgrav } 1386e2f6069cSDag-Erling Smørgrav } 1387511b41d2SMark Murray } 1388e4a9863fSDag-Erling Smørgrav free(readset); 1389e4a9863fSDag-Erling Smørgrav free(writeset); 1390511b41d2SMark Murray 1391511b41d2SMark Murray /* Terminate the session. */ 1392511b41d2SMark Murray 1393511b41d2SMark Murray /* Stop watching for window change. */ 1394511b41d2SMark Murray signal(SIGWINCH, SIG_DFL); 1395511b41d2SMark Murray 13967aee6ffeSDag-Erling Smørgrav packet_start(SSH2_MSG_DISCONNECT); 13977aee6ffeSDag-Erling Smørgrav packet_put_int(SSH2_DISCONNECT_BY_APPLICATION); 13987aee6ffeSDag-Erling Smørgrav packet_put_cstring("disconnected by user"); 13998ad9b54aSDag-Erling Smørgrav packet_put_cstring(""); /* language tag */ 14007aee6ffeSDag-Erling Smørgrav packet_send(); 14017aee6ffeSDag-Erling Smørgrav packet_write_wait(); 14027aee6ffeSDag-Erling Smørgrav 14034f52dfbbSDag-Erling Smørgrav channel_free_all(ssh); 1404ae1f160dSDag-Erling Smørgrav 1405ae1f160dSDag-Erling Smørgrav if (have_pty) 1406e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 1407ae1f160dSDag-Erling Smørgrav 1408ae1f160dSDag-Erling Smørgrav /* restore blocking io */ 1409ae1f160dSDag-Erling Smørgrav if (!isatty(fileno(stdin))) 1410ae1f160dSDag-Erling Smørgrav unset_nonblock(fileno(stdin)); 1411ae1f160dSDag-Erling Smørgrav if (!isatty(fileno(stdout))) 1412ae1f160dSDag-Erling Smørgrav unset_nonblock(fileno(stdout)); 1413ae1f160dSDag-Erling Smørgrav if (!isatty(fileno(stderr))) 1414ae1f160dSDag-Erling Smørgrav unset_nonblock(fileno(stderr)); 1415ae1f160dSDag-Erling Smørgrav 1416efcad6b7SDag-Erling Smørgrav /* 1417efcad6b7SDag-Erling Smørgrav * If there was no shell or command requested, there will be no remote 1418efcad6b7SDag-Erling Smørgrav * exit status to be returned. In that case, clear error code if the 1419efcad6b7SDag-Erling Smørgrav * connection was deliberately terminated at this end. 1420efcad6b7SDag-Erling Smørgrav */ 1421efcad6b7SDag-Erling Smørgrav if (no_shell_flag && received_signal == SIGTERM) { 1422efcad6b7SDag-Erling Smørgrav received_signal = 0; 1423efcad6b7SDag-Erling Smørgrav exit_status = 0; 1424ae1f160dSDag-Erling Smørgrav } 1425511b41d2SMark Murray 14264f52dfbbSDag-Erling Smørgrav if (received_signal) { 14274f52dfbbSDag-Erling Smørgrav verbose("Killed by signal %d.", (int) received_signal); 14284f52dfbbSDag-Erling Smørgrav cleanup_exit(0); 14294f52dfbbSDag-Erling Smørgrav } 1430efcad6b7SDag-Erling Smørgrav 1431511b41d2SMark Murray /* 1432511b41d2SMark Murray * In interactive mode (with pseudo tty) display a message indicating 1433511b41d2SMark Murray * that the connection has been closed. 1434511b41d2SMark Murray */ 1435511b41d2SMark Murray if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) { 1436*190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(stderr_buffer, 1437*190cef3dSDag-Erling Smørgrav "Connection to %.64s closed.\r\n", host)) != 0) 1438*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1439511b41d2SMark Murray } 1440ae1f160dSDag-Erling Smørgrav 1441511b41d2SMark Murray /* Output any buffered data for stderr. */ 1442*190cef3dSDag-Erling Smørgrav if (sshbuf_len(stderr_buffer) > 0) { 14434a421b63SDag-Erling Smørgrav len = atomicio(vwrite, fileno(stderr), 1444*190cef3dSDag-Erling Smørgrav (u_char *)sshbuf_ptr(stderr_buffer), 1445*190cef3dSDag-Erling Smørgrav sshbuf_len(stderr_buffer)); 1446*190cef3dSDag-Erling Smørgrav if (len < 0 || (u_int)len != sshbuf_len(stderr_buffer)) 1447511b41d2SMark Murray error("Write failed flushing stderr buffer."); 1448*190cef3dSDag-Erling Smørgrav else if ((r = sshbuf_consume(stderr_buffer, len)) != 0) 1449*190cef3dSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1450511b41d2SMark Murray } 1451511b41d2SMark Murray 1452511b41d2SMark Murray /* Clear and free any buffers. */ 1453acc1a9efSDag-Erling Smørgrav explicit_bzero(buf, sizeof(buf)); 1454*190cef3dSDag-Erling Smørgrav sshbuf_free(stderr_buffer); 1455511b41d2SMark Murray 1456511b41d2SMark Murray /* Report bytes transferred, and transfer rates. */ 145747dd1d1bSDag-Erling Smørgrav total_time = monotime_double() - start_time; 1458bc5531deSDag-Erling Smørgrav packet_get_bytes(&ibytes, &obytes); 1459d4af9e69SDag-Erling Smørgrav verbose("Transferred: sent %llu, received %llu bytes, in %.1f seconds", 14604a421b63SDag-Erling Smørgrav (unsigned long long)obytes, (unsigned long long)ibytes, total_time); 1461511b41d2SMark Murray if (total_time > 0) 1462d4af9e69SDag-Erling Smørgrav verbose("Bytes per second: sent %.1f, received %.1f", 1463d4af9e69SDag-Erling Smørgrav obytes / total_time, ibytes / total_time); 1464511b41d2SMark Murray /* Return the exit status of the program. */ 1465511b41d2SMark Murray debug("Exit status %d", exit_status); 1466511b41d2SMark Murray return exit_status; 1467511b41d2SMark Murray } 1468a04a10f8SKris Kennaway 1469a04a10f8SKris Kennaway /*********/ 1470a04a10f8SKris Kennaway 1471ae1f160dSDag-Erling Smørgrav static Channel * 14724f52dfbbSDag-Erling Smørgrav client_request_forwarded_tcpip(struct ssh *ssh, const char *request_type, 14734f52dfbbSDag-Erling Smørgrav int rchan, u_int rwindow, u_int rmaxpack) 14741e8db6e2SBrian Feldman { 14751e8db6e2SBrian Feldman Channel *c = NULL; 1476ca86bcf2SDag-Erling Smørgrav struct sshbuf *b = NULL; 14771e8db6e2SBrian Feldman char *listen_address, *originator_address; 1478cce7d346SDag-Erling Smørgrav u_short listen_port, originator_port; 1479ca86bcf2SDag-Erling Smørgrav int r; 14801e8db6e2SBrian Feldman 14811e8db6e2SBrian Feldman /* Get rest of the packet */ 14821e8db6e2SBrian Feldman listen_address = packet_get_string(NULL); 14831e8db6e2SBrian Feldman listen_port = packet_get_int(); 14841e8db6e2SBrian Feldman originator_address = packet_get_string(NULL); 14851e8db6e2SBrian Feldman originator_port = packet_get_int(); 1486ae1f160dSDag-Erling Smørgrav packet_check_eom(); 14871e8db6e2SBrian Feldman 1488a0ee8cc6SDag-Erling Smørgrav debug("%s: listen %s port %d, originator %s port %d", __func__, 1489a0ee8cc6SDag-Erling Smørgrav listen_address, listen_port, originator_address, originator_port); 14901e8db6e2SBrian Feldman 14914f52dfbbSDag-Erling Smørgrav c = channel_connect_by_listen_address(ssh, listen_address, listen_port, 1492d4af9e69SDag-Erling Smørgrav "forwarded-tcpip", originator_address); 1493d4af9e69SDag-Erling Smørgrav 1494ca86bcf2SDag-Erling Smørgrav if (c != NULL && c->type == SSH_CHANNEL_MUX_CLIENT) { 1495ca86bcf2SDag-Erling Smørgrav if ((b = sshbuf_new()) == NULL) { 1496ca86bcf2SDag-Erling Smørgrav error("%s: alloc reply", __func__); 1497ca86bcf2SDag-Erling Smørgrav goto out; 1498ca86bcf2SDag-Erling Smørgrav } 1499ca86bcf2SDag-Erling Smørgrav /* reconstruct and send to muxclient */ 1500ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_put_u8(b, 0)) != 0 || /* padlen */ 1501ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u8(b, SSH2_MSG_CHANNEL_OPEN)) != 0 || 1502ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_cstring(b, request_type)) != 0 || 1503ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, rchan)) != 0 || 1504ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, rwindow)) != 0 || 1505ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, rmaxpack)) != 0 || 1506ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_cstring(b, listen_address)) != 0 || 1507ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, listen_port)) != 0 || 1508ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_cstring(b, originator_address)) != 0 || 1509ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, originator_port)) != 0 || 15104f52dfbbSDag-Erling Smørgrav (r = sshbuf_put_stringb(c->output, b)) != 0) { 1511ca86bcf2SDag-Erling Smørgrav error("%s: compose for muxclient %s", __func__, 1512ca86bcf2SDag-Erling Smørgrav ssh_err(r)); 1513ca86bcf2SDag-Erling Smørgrav goto out; 1514ca86bcf2SDag-Erling Smørgrav } 1515ca86bcf2SDag-Erling Smørgrav } 1516ca86bcf2SDag-Erling Smørgrav 1517ca86bcf2SDag-Erling Smørgrav out: 1518ca86bcf2SDag-Erling Smørgrav sshbuf_free(b); 1519e4a9863fSDag-Erling Smørgrav free(originator_address); 1520e4a9863fSDag-Erling Smørgrav free(listen_address); 15211e8db6e2SBrian Feldman return c; 15221e8db6e2SBrian Feldman } 15231e8db6e2SBrian Feldman 1524ae1f160dSDag-Erling Smørgrav static Channel * 15254f52dfbbSDag-Erling Smørgrav client_request_forwarded_streamlocal(struct ssh *ssh, 15264f52dfbbSDag-Erling Smørgrav const char *request_type, int rchan) 1527a0ee8cc6SDag-Erling Smørgrav { 1528a0ee8cc6SDag-Erling Smørgrav Channel *c = NULL; 1529a0ee8cc6SDag-Erling Smørgrav char *listen_path; 1530a0ee8cc6SDag-Erling Smørgrav 1531a0ee8cc6SDag-Erling Smørgrav /* Get the remote path. */ 1532a0ee8cc6SDag-Erling Smørgrav listen_path = packet_get_string(NULL); 1533a0ee8cc6SDag-Erling Smørgrav /* XXX: Skip reserved field for now. */ 1534a0ee8cc6SDag-Erling Smørgrav if (packet_get_string_ptr(NULL) == NULL) 1535a0ee8cc6SDag-Erling Smørgrav fatal("%s: packet_get_string_ptr failed", __func__); 1536a0ee8cc6SDag-Erling Smørgrav packet_check_eom(); 1537a0ee8cc6SDag-Erling Smørgrav 1538a0ee8cc6SDag-Erling Smørgrav debug("%s: %s", __func__, listen_path); 1539a0ee8cc6SDag-Erling Smørgrav 15404f52dfbbSDag-Erling Smørgrav c = channel_connect_by_listen_path(ssh, listen_path, 1541a0ee8cc6SDag-Erling Smørgrav "forwarded-streamlocal@openssh.com", "forwarded-streamlocal"); 1542a0ee8cc6SDag-Erling Smørgrav free(listen_path); 1543a0ee8cc6SDag-Erling Smørgrav return c; 1544a0ee8cc6SDag-Erling Smørgrav } 1545a0ee8cc6SDag-Erling Smørgrav 1546a0ee8cc6SDag-Erling Smørgrav static Channel * 15474f52dfbbSDag-Erling Smørgrav client_request_x11(struct ssh *ssh, const char *request_type, int rchan) 15481e8db6e2SBrian Feldman { 15491e8db6e2SBrian Feldman Channel *c = NULL; 15501e8db6e2SBrian Feldman char *originator; 1551cce7d346SDag-Erling Smørgrav u_short originator_port; 1552ae1f160dSDag-Erling Smørgrav int sock; 15531e8db6e2SBrian Feldman 15541e8db6e2SBrian Feldman if (!options.forward_x11) { 15551e8db6e2SBrian Feldman error("Warning: ssh server tried X11 forwarding."); 1556d4af9e69SDag-Erling Smørgrav error("Warning: this is probably a break-in attempt by a " 1557d4af9e69SDag-Erling Smørgrav "malicious server."); 15581e8db6e2SBrian Feldman return NULL; 15591e8db6e2SBrian Feldman } 1560557f75e5SDag-Erling Smørgrav if (x11_refuse_time != 0 && (u_int)monotime() >= x11_refuse_time) { 1561e2f6069cSDag-Erling Smørgrav verbose("Rejected X11 connection after ForwardX11Timeout " 1562e2f6069cSDag-Erling Smørgrav "expired"); 1563e2f6069cSDag-Erling Smørgrav return NULL; 1564e2f6069cSDag-Erling Smørgrav } 15651e8db6e2SBrian Feldman originator = packet_get_string(NULL); 15661e8db6e2SBrian Feldman originator_port = packet_get_int(); 1567ae1f160dSDag-Erling Smørgrav packet_check_eom(); 15681e8db6e2SBrian Feldman /* XXX check permission */ 15691e8db6e2SBrian Feldman debug("client_request_x11: request from %s %d", originator, 15701e8db6e2SBrian Feldman originator_port); 1571e4a9863fSDag-Erling Smørgrav free(originator); 15724f52dfbbSDag-Erling Smørgrav sock = x11_connect_display(ssh); 1573ae1f160dSDag-Erling Smørgrav if (sock < 0) 1574ae1f160dSDag-Erling Smørgrav return NULL; 15754f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, "x11", 157660c59fadSDag-Erling Smørgrav SSH_CHANNEL_X11_OPEN, sock, sock, -1, 157760c59fadSDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "x11", 1); 1578ae1f160dSDag-Erling Smørgrav c->force_drain = 1; 15791e8db6e2SBrian Feldman return c; 15801e8db6e2SBrian Feldman } 15811e8db6e2SBrian Feldman 1582ae1f160dSDag-Erling Smørgrav static Channel * 15834f52dfbbSDag-Erling Smørgrav client_request_agent(struct ssh *ssh, const char *request_type, int rchan) 15841e8db6e2SBrian Feldman { 15851e8db6e2SBrian Feldman Channel *c = NULL; 1586bc5531deSDag-Erling Smørgrav int r, sock; 15871e8db6e2SBrian Feldman 15881e8db6e2SBrian Feldman if (!options.forward_agent) { 15891e8db6e2SBrian Feldman error("Warning: ssh server tried agent forwarding."); 1590d4af9e69SDag-Erling Smørgrav error("Warning: this is probably a break-in attempt by a " 1591d4af9e69SDag-Erling Smørgrav "malicious server."); 15921e8db6e2SBrian Feldman return NULL; 15931e8db6e2SBrian Feldman } 1594bc5531deSDag-Erling Smørgrav if ((r = ssh_get_authentication_socket(&sock)) != 0) { 1595bc5531deSDag-Erling Smørgrav if (r != SSH_ERR_AGENT_NOT_PRESENT) 1596bc5531deSDag-Erling Smørgrav debug("%s: ssh_get_authentication_socket: %s", 1597bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 1598ae1f160dSDag-Erling Smørgrav return NULL; 1599bc5531deSDag-Erling Smørgrav } 16004f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, "authentication agent connection", 16011e8db6e2SBrian Feldman SSH_CHANNEL_OPEN, sock, sock, -1, 1602e3bd730fSBryan Drewery CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, 160389986192SBrooks Davis "authentication agent connection", 1); 1604ae1f160dSDag-Erling Smørgrav c->force_drain = 1; 16051e8db6e2SBrian Feldman return c; 16061e8db6e2SBrian Feldman } 16071e8db6e2SBrian Feldman 160847dd1d1bSDag-Erling Smørgrav char * 16094f52dfbbSDag-Erling Smørgrav client_request_tun_fwd(struct ssh *ssh, int tun_mode, 16104f52dfbbSDag-Erling Smørgrav int local_tun, int remote_tun) 1611d4af9e69SDag-Erling Smørgrav { 1612d4af9e69SDag-Erling Smørgrav Channel *c; 1613d4af9e69SDag-Erling Smørgrav int fd; 161447dd1d1bSDag-Erling Smørgrav char *ifname = NULL; 1615d4af9e69SDag-Erling Smørgrav 1616d4af9e69SDag-Erling Smørgrav if (tun_mode == SSH_TUNMODE_NO) 1617d4af9e69SDag-Erling Smørgrav return 0; 1618d4af9e69SDag-Erling Smørgrav 1619d4af9e69SDag-Erling Smørgrav debug("Requesting tun unit %d in mode %d", local_tun, tun_mode); 1620d4af9e69SDag-Erling Smørgrav 1621d4af9e69SDag-Erling Smørgrav /* Open local tunnel device */ 162247dd1d1bSDag-Erling Smørgrav if ((fd = tun_open(local_tun, tun_mode, &ifname)) == -1) { 1623d4af9e69SDag-Erling Smørgrav error("Tunnel device open failed."); 162447dd1d1bSDag-Erling Smørgrav return NULL; 1625d4af9e69SDag-Erling Smørgrav } 162647dd1d1bSDag-Erling Smørgrav debug("Tunnel forwarding using interface %s", ifname); 1627d4af9e69SDag-Erling Smørgrav 16284f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, "tun", SSH_CHANNEL_OPENING, fd, fd, -1, 162960c59fadSDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1); 1630d4af9e69SDag-Erling Smørgrav c->datagram = 1; 1631d4af9e69SDag-Erling Smørgrav 1632d4af9e69SDag-Erling Smørgrav #if defined(SSH_TUN_FILTER) 1633d4af9e69SDag-Erling Smørgrav if (options.tun_open == SSH_TUNMODE_POINTOPOINT) 16344f52dfbbSDag-Erling Smørgrav channel_register_filter(ssh, c->self, sys_tun_infilter, 1635d4af9e69SDag-Erling Smørgrav sys_tun_outfilter, NULL, NULL); 1636d4af9e69SDag-Erling Smørgrav #endif 1637d4af9e69SDag-Erling Smørgrav 1638d4af9e69SDag-Erling Smørgrav packet_start(SSH2_MSG_CHANNEL_OPEN); 1639d4af9e69SDag-Erling Smørgrav packet_put_cstring("tun@openssh.com"); 1640d4af9e69SDag-Erling Smørgrav packet_put_int(c->self); 1641d4af9e69SDag-Erling Smørgrav packet_put_int(c->local_window_max); 1642d4af9e69SDag-Erling Smørgrav packet_put_int(c->local_maxpacket); 1643d4af9e69SDag-Erling Smørgrav packet_put_int(tun_mode); 1644d4af9e69SDag-Erling Smørgrav packet_put_int(remote_tun); 1645d4af9e69SDag-Erling Smørgrav packet_send(); 1646d4af9e69SDag-Erling Smørgrav 164747dd1d1bSDag-Erling Smørgrav return ifname; 1648d4af9e69SDag-Erling Smørgrav } 1649d4af9e69SDag-Erling Smørgrav 1650a04a10f8SKris Kennaway /* XXXX move to generic input handler */ 1651bc5531deSDag-Erling Smørgrav static int 16524f52dfbbSDag-Erling Smørgrav client_input_channel_open(int type, u_int32_t seq, struct ssh *ssh) 1653a04a10f8SKris Kennaway { 1654a04a10f8SKris Kennaway Channel *c = NULL; 1655a04a10f8SKris Kennaway char *ctype; 1656a04a10f8SKris Kennaway int rchan; 1657ee21a45fSDag-Erling Smørgrav u_int rmaxpack, rwindow, len; 1658a04a10f8SKris Kennaway 1659a04a10f8SKris Kennaway ctype = packet_get_string(&len); 1660a04a10f8SKris Kennaway rchan = packet_get_int(); 1661a04a10f8SKris Kennaway rwindow = packet_get_int(); 1662a04a10f8SKris Kennaway rmaxpack = packet_get_int(); 1663a04a10f8SKris Kennaway 1664a04a10f8SKris Kennaway debug("client_input_channel_open: ctype %s rchan %d win %d max %d", 1665a04a10f8SKris Kennaway ctype, rchan, rwindow, rmaxpack); 1666a04a10f8SKris Kennaway 16671e8db6e2SBrian Feldman if (strcmp(ctype, "forwarded-tcpip") == 0) { 16684f52dfbbSDag-Erling Smørgrav c = client_request_forwarded_tcpip(ssh, ctype, rchan, rwindow, 1669ca86bcf2SDag-Erling Smørgrav rmaxpack); 1670a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(ctype, "forwarded-streamlocal@openssh.com") == 0) { 16714f52dfbbSDag-Erling Smørgrav c = client_request_forwarded_streamlocal(ssh, ctype, rchan); 16721e8db6e2SBrian Feldman } else if (strcmp(ctype, "x11") == 0) { 16734f52dfbbSDag-Erling Smørgrav c = client_request_x11(ssh, ctype, rchan); 16741e8db6e2SBrian Feldman } else if (strcmp(ctype, "auth-agent@openssh.com") == 0) { 16754f52dfbbSDag-Erling Smørgrav c = client_request_agent(ssh, ctype, rchan); 1676a04a10f8SKris Kennaway } 1677ca86bcf2SDag-Erling Smørgrav if (c != NULL && c->type == SSH_CHANNEL_MUX_CLIENT) { 1678ca86bcf2SDag-Erling Smørgrav debug3("proxied to downstream: %s", ctype); 1679ca86bcf2SDag-Erling Smørgrav } else if (c != NULL) { 1680a04a10f8SKris Kennaway debug("confirm %s", ctype); 1681a04a10f8SKris Kennaway c->remote_id = rchan; 16824f52dfbbSDag-Erling Smørgrav c->have_remote_id = 1; 1683a04a10f8SKris Kennaway c->remote_window = rwindow; 1684a04a10f8SKris Kennaway c->remote_maxpacket = rmaxpack; 1685ae1f160dSDag-Erling Smørgrav if (c->type != SSH_CHANNEL_CONNECTING) { 1686a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); 1687a04a10f8SKris Kennaway packet_put_int(c->remote_id); 1688a04a10f8SKris Kennaway packet_put_int(c->self); 1689a04a10f8SKris Kennaway packet_put_int(c->local_window); 1690a04a10f8SKris Kennaway packet_put_int(c->local_maxpacket); 1691a04a10f8SKris Kennaway packet_send(); 1692ae1f160dSDag-Erling Smørgrav } 1693a04a10f8SKris Kennaway } else { 1694a04a10f8SKris Kennaway debug("failure %s", ctype); 1695a04a10f8SKris Kennaway packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); 1696a04a10f8SKris Kennaway packet_put_int(rchan); 1697a04a10f8SKris Kennaway packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED); 1698ae1f160dSDag-Erling Smørgrav packet_put_cstring("open failed"); 1699a04a10f8SKris Kennaway packet_put_cstring(""); 1700a04a10f8SKris Kennaway packet_send(); 1701a04a10f8SKris Kennaway } 1702e4a9863fSDag-Erling Smørgrav free(ctype); 1703bc5531deSDag-Erling Smørgrav return 0; 1704a04a10f8SKris Kennaway } 1705bc5531deSDag-Erling Smørgrav 1706bc5531deSDag-Erling Smørgrav static int 17074f52dfbbSDag-Erling Smørgrav client_input_channel_req(int type, u_int32_t seq, struct ssh *ssh) 17081e8db6e2SBrian Feldman { 17091e8db6e2SBrian Feldman Channel *c = NULL; 1710d74d50a8SDag-Erling Smørgrav int exitval, id, reply, success = 0; 17111e8db6e2SBrian Feldman char *rtype; 17121e8db6e2SBrian Feldman 17131e8db6e2SBrian Feldman id = packet_get_int(); 17144f52dfbbSDag-Erling Smørgrav c = channel_lookup(ssh, id); 17154f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 1716ca86bcf2SDag-Erling Smørgrav return 0; 17171e8db6e2SBrian Feldman rtype = packet_get_string(NULL); 17181e8db6e2SBrian Feldman reply = packet_get_char(); 17191e8db6e2SBrian Feldman 17201e8db6e2SBrian Feldman debug("client_input_channel_req: channel %d rtype %s reply %d", 17211e8db6e2SBrian Feldman id, rtype, reply); 17221e8db6e2SBrian Feldman 1723d74d50a8SDag-Erling Smørgrav if (id == -1) { 1724d74d50a8SDag-Erling Smørgrav error("client_input_channel_req: request for channel -1"); 1725ca86bcf2SDag-Erling Smørgrav } else if (c == NULL) { 1726d4af9e69SDag-Erling Smørgrav error("client_input_channel_req: channel %d: " 1727d4af9e69SDag-Erling Smørgrav "unknown channel", id); 1728d4af9e69SDag-Erling Smørgrav } else if (strcmp(rtype, "eow@openssh.com") == 0) { 1729d4af9e69SDag-Erling Smørgrav packet_check_eom(); 17304f52dfbbSDag-Erling Smørgrav chan_rcvd_eow(ssh, c); 17311e8db6e2SBrian Feldman } else if (strcmp(rtype, "exit-status") == 0) { 1732d74d50a8SDag-Erling Smørgrav exitval = packet_get_int(); 1733b15c8340SDag-Erling Smørgrav if (c->ctl_chan != -1) { 17344f52dfbbSDag-Erling Smørgrav mux_exit_message(ssh, c, exitval); 1735b15c8340SDag-Erling Smørgrav success = 1; 1736b15c8340SDag-Erling Smørgrav } else if (id == session_ident) { 1737b15c8340SDag-Erling Smørgrav /* Record exit value of local session */ 17381e8db6e2SBrian Feldman success = 1; 1739d74d50a8SDag-Erling Smørgrav exit_status = exitval; 1740d74d50a8SDag-Erling Smørgrav } else { 1741b15c8340SDag-Erling Smørgrav /* Probably for a mux channel that has already closed */ 1742b15c8340SDag-Erling Smørgrav debug("%s: no sink for exit-status on channel %d", 1743b15c8340SDag-Erling Smørgrav __func__, id); 1744d74d50a8SDag-Erling Smørgrav } 1745ae1f160dSDag-Erling Smørgrav packet_check_eom(); 17461e8db6e2SBrian Feldman } 1747a0ee8cc6SDag-Erling Smørgrav if (reply && c != NULL && !(c->flags & CHAN_CLOSE_SENT)) { 17484f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id) 17494f52dfbbSDag-Erling Smørgrav fatal("%s: channel %d: no remote_id", 17504f52dfbbSDag-Erling Smørgrav __func__, c->self); 17511e8db6e2SBrian Feldman packet_start(success ? 17521e8db6e2SBrian Feldman SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); 1753cce7d346SDag-Erling Smørgrav packet_put_int(c->remote_id); 17541e8db6e2SBrian Feldman packet_send(); 17551e8db6e2SBrian Feldman } 1756e4a9863fSDag-Erling Smørgrav free(rtype); 1757bc5531deSDag-Erling Smørgrav return 0; 17581e8db6e2SBrian Feldman } 1759bc5531deSDag-Erling Smørgrav 1760bc5531deSDag-Erling Smørgrav struct hostkeys_update_ctx { 1761bc5531deSDag-Erling Smørgrav /* The hostname and (optionally) IP address string for the server */ 1762bc5531deSDag-Erling Smørgrav char *host_str, *ip_str; 1763bc5531deSDag-Erling Smørgrav 1764bc5531deSDag-Erling Smørgrav /* 1765bc5531deSDag-Erling Smørgrav * Keys received from the server and a flag for each indicating 1766bc5531deSDag-Erling Smørgrav * whether they already exist in known_hosts. 1767bc5531deSDag-Erling Smørgrav * keys_seen is filled in by hostkeys_find() and later (for new 1768bc5531deSDag-Erling Smørgrav * keys) by client_global_hostkeys_private_confirm(). 1769bc5531deSDag-Erling Smørgrav */ 1770bc5531deSDag-Erling Smørgrav struct sshkey **keys; 1771bc5531deSDag-Erling Smørgrav int *keys_seen; 17724f52dfbbSDag-Erling Smørgrav size_t nkeys, nnew; 1773bc5531deSDag-Erling Smørgrav 1774bc5531deSDag-Erling Smørgrav /* 1775bc5531deSDag-Erling Smørgrav * Keys that are in known_hosts, but were not present in the update 1776bc5531deSDag-Erling Smørgrav * from the server (i.e. scheduled to be deleted). 1777bc5531deSDag-Erling Smørgrav * Filled in by hostkeys_find(). 1778bc5531deSDag-Erling Smørgrav */ 1779bc5531deSDag-Erling Smørgrav struct sshkey **old_keys; 1780bc5531deSDag-Erling Smørgrav size_t nold; 1781bc5531deSDag-Erling Smørgrav }; 1782bc5531deSDag-Erling Smørgrav 1783ae1f160dSDag-Erling Smørgrav static void 1784bc5531deSDag-Erling Smørgrav hostkeys_update_ctx_free(struct hostkeys_update_ctx *ctx) 1785bc5531deSDag-Erling Smørgrav { 1786bc5531deSDag-Erling Smørgrav size_t i; 1787bc5531deSDag-Erling Smørgrav 1788bc5531deSDag-Erling Smørgrav if (ctx == NULL) 1789bc5531deSDag-Erling Smørgrav return; 1790bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) 1791bc5531deSDag-Erling Smørgrav sshkey_free(ctx->keys[i]); 1792bc5531deSDag-Erling Smørgrav free(ctx->keys); 1793bc5531deSDag-Erling Smørgrav free(ctx->keys_seen); 1794bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nold; i++) 1795bc5531deSDag-Erling Smørgrav sshkey_free(ctx->old_keys[i]); 1796bc5531deSDag-Erling Smørgrav free(ctx->old_keys); 1797bc5531deSDag-Erling Smørgrav free(ctx->host_str); 1798bc5531deSDag-Erling Smørgrav free(ctx->ip_str); 1799bc5531deSDag-Erling Smørgrav free(ctx); 1800bc5531deSDag-Erling Smørgrav } 1801bc5531deSDag-Erling Smørgrav 1802bc5531deSDag-Erling Smørgrav static int 1803bc5531deSDag-Erling Smørgrav hostkeys_find(struct hostkey_foreach_line *l, void *_ctx) 1804bc5531deSDag-Erling Smørgrav { 1805bc5531deSDag-Erling Smørgrav struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; 1806bc5531deSDag-Erling Smørgrav size_t i; 1807bc5531deSDag-Erling Smørgrav struct sshkey **tmp; 1808bc5531deSDag-Erling Smørgrav 18094f52dfbbSDag-Erling Smørgrav if (l->status != HKF_STATUS_MATCHED || l->key == NULL) 1810bc5531deSDag-Erling Smørgrav return 0; 1811bc5531deSDag-Erling Smørgrav 1812bc5531deSDag-Erling Smørgrav /* Mark off keys we've already seen for this host */ 1813bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) { 1814bc5531deSDag-Erling Smørgrav if (sshkey_equal(l->key, ctx->keys[i])) { 1815bc5531deSDag-Erling Smørgrav debug3("%s: found %s key at %s:%ld", __func__, 1816bc5531deSDag-Erling Smørgrav sshkey_ssh_name(ctx->keys[i]), l->path, l->linenum); 1817bc5531deSDag-Erling Smørgrav ctx->keys_seen[i] = 1; 1818bc5531deSDag-Erling Smørgrav return 0; 1819bc5531deSDag-Erling Smørgrav } 1820bc5531deSDag-Erling Smørgrav } 1821bc5531deSDag-Erling Smørgrav /* This line contained a key that not offered by the server */ 1822bc5531deSDag-Erling Smørgrav debug3("%s: deprecated %s key at %s:%ld", __func__, 1823bc5531deSDag-Erling Smørgrav sshkey_ssh_name(l->key), l->path, l->linenum); 18244f52dfbbSDag-Erling Smørgrav if ((tmp = recallocarray(ctx->old_keys, ctx->nold, ctx->nold + 1, 1825bc5531deSDag-Erling Smørgrav sizeof(*ctx->old_keys))) == NULL) 18264f52dfbbSDag-Erling Smørgrav fatal("%s: recallocarray failed nold = %zu", 1827bc5531deSDag-Erling Smørgrav __func__, ctx->nold); 1828bc5531deSDag-Erling Smørgrav ctx->old_keys = tmp; 1829bc5531deSDag-Erling Smørgrav ctx->old_keys[ctx->nold++] = l->key; 1830bc5531deSDag-Erling Smørgrav l->key = NULL; 1831bc5531deSDag-Erling Smørgrav 1832bc5531deSDag-Erling Smørgrav return 0; 1833bc5531deSDag-Erling Smørgrav } 1834bc5531deSDag-Erling Smørgrav 1835bc5531deSDag-Erling Smørgrav static void 1836bc5531deSDag-Erling Smørgrav update_known_hosts(struct hostkeys_update_ctx *ctx) 1837bc5531deSDag-Erling Smørgrav { 1838bc5531deSDag-Erling Smørgrav int r, was_raw = 0; 1839bc5531deSDag-Erling Smørgrav int loglevel = options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK ? 1840bc5531deSDag-Erling Smørgrav SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_VERBOSE; 1841bc5531deSDag-Erling Smørgrav char *fp, *response; 1842bc5531deSDag-Erling Smørgrav size_t i; 1843bc5531deSDag-Erling Smørgrav 1844bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) { 1845bc5531deSDag-Erling Smørgrav if (ctx->keys_seen[i] != 2) 1846bc5531deSDag-Erling Smørgrav continue; 1847bc5531deSDag-Erling Smørgrav if ((fp = sshkey_fingerprint(ctx->keys[i], 1848bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) 1849bc5531deSDag-Erling Smørgrav fatal("%s: sshkey_fingerprint failed", __func__); 1850bc5531deSDag-Erling Smørgrav do_log2(loglevel, "Learned new hostkey: %s %s", 1851bc5531deSDag-Erling Smørgrav sshkey_type(ctx->keys[i]), fp); 1852bc5531deSDag-Erling Smørgrav free(fp); 1853bc5531deSDag-Erling Smørgrav } 1854bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nold; i++) { 1855bc5531deSDag-Erling Smørgrav if ((fp = sshkey_fingerprint(ctx->old_keys[i], 1856bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) 1857bc5531deSDag-Erling Smørgrav fatal("%s: sshkey_fingerprint failed", __func__); 1858bc5531deSDag-Erling Smørgrav do_log2(loglevel, "Deprecating obsolete hostkey: %s %s", 1859bc5531deSDag-Erling Smørgrav sshkey_type(ctx->old_keys[i]), fp); 1860bc5531deSDag-Erling Smørgrav free(fp); 1861bc5531deSDag-Erling Smørgrav } 1862bc5531deSDag-Erling Smørgrav if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK) { 1863bc5531deSDag-Erling Smørgrav if (get_saved_tio() != NULL) { 1864bc5531deSDag-Erling Smørgrav leave_raw_mode(1); 1865bc5531deSDag-Erling Smørgrav was_raw = 1; 1866bc5531deSDag-Erling Smørgrav } 1867bc5531deSDag-Erling Smørgrav response = NULL; 1868bc5531deSDag-Erling Smørgrav for (i = 0; !quit_pending && i < 3; i++) { 1869bc5531deSDag-Erling Smørgrav free(response); 1870bc5531deSDag-Erling Smørgrav response = read_passphrase("Accept updated hostkeys? " 1871bc5531deSDag-Erling Smørgrav "(yes/no): ", RP_ECHO); 1872bc5531deSDag-Erling Smørgrav if (strcasecmp(response, "yes") == 0) 1873bc5531deSDag-Erling Smørgrav break; 1874bc5531deSDag-Erling Smørgrav else if (quit_pending || response == NULL || 1875bc5531deSDag-Erling Smørgrav strcasecmp(response, "no") == 0) { 1876bc5531deSDag-Erling Smørgrav options.update_hostkeys = 0; 1877bc5531deSDag-Erling Smørgrav break; 1878bc5531deSDag-Erling Smørgrav } else { 1879bc5531deSDag-Erling Smørgrav do_log2(loglevel, "Please enter " 1880bc5531deSDag-Erling Smørgrav "\"yes\" or \"no\""); 1881bc5531deSDag-Erling Smørgrav } 1882bc5531deSDag-Erling Smørgrav } 1883bc5531deSDag-Erling Smørgrav if (quit_pending || i >= 3 || response == NULL) 1884bc5531deSDag-Erling Smørgrav options.update_hostkeys = 0; 1885bc5531deSDag-Erling Smørgrav free(response); 1886bc5531deSDag-Erling Smørgrav if (was_raw) 1887bc5531deSDag-Erling Smørgrav enter_raw_mode(1); 1888bc5531deSDag-Erling Smørgrav } 1889bc5531deSDag-Erling Smørgrav 1890bc5531deSDag-Erling Smørgrav /* 1891bc5531deSDag-Erling Smørgrav * Now that all the keys are verified, we can go ahead and replace 1892bc5531deSDag-Erling Smørgrav * them in known_hosts (assuming SSH_UPDATE_HOSTKEYS_ASK didn't 1893bc5531deSDag-Erling Smørgrav * cancel the operation). 1894bc5531deSDag-Erling Smørgrav */ 1895bc5531deSDag-Erling Smørgrav if (options.update_hostkeys != 0 && 1896bc5531deSDag-Erling Smørgrav (r = hostfile_replace_entries(options.user_hostfiles[0], 1897bc5531deSDag-Erling Smørgrav ctx->host_str, ctx->ip_str, ctx->keys, ctx->nkeys, 1898bc5531deSDag-Erling Smørgrav options.hash_known_hosts, 0, 1899bc5531deSDag-Erling Smørgrav options.fingerprint_hash)) != 0) 1900bc5531deSDag-Erling Smørgrav error("%s: hostfile_replace_entries failed: %s", 1901bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 1902bc5531deSDag-Erling Smørgrav } 1903bc5531deSDag-Erling Smørgrav 1904bc5531deSDag-Erling Smørgrav static void 19054f52dfbbSDag-Erling Smørgrav client_global_hostkeys_private_confirm(struct ssh *ssh, int type, 19064f52dfbbSDag-Erling Smørgrav u_int32_t seq, void *_ctx) 1907bc5531deSDag-Erling Smørgrav { 1908bc5531deSDag-Erling Smørgrav struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; 1909bc5531deSDag-Erling Smørgrav size_t i, ndone; 1910bc5531deSDag-Erling Smørgrav struct sshbuf *signdata; 191147dd1d1bSDag-Erling Smørgrav int r, kexsigtype, use_kexsigtype; 1912bc5531deSDag-Erling Smørgrav const u_char *sig; 1913bc5531deSDag-Erling Smørgrav size_t siglen; 1914bc5531deSDag-Erling Smørgrav 1915bc5531deSDag-Erling Smørgrav if (ctx->nnew == 0) 1916bc5531deSDag-Erling Smørgrav fatal("%s: ctx->nnew == 0", __func__); /* sanity */ 1917bc5531deSDag-Erling Smørgrav if (type != SSH2_MSG_REQUEST_SUCCESS) { 1918bc5531deSDag-Erling Smørgrav error("Server failed to confirm ownership of " 1919bc5531deSDag-Erling Smørgrav "private host keys"); 1920bc5531deSDag-Erling Smørgrav hostkeys_update_ctx_free(ctx); 1921bc5531deSDag-Erling Smørgrav return; 1922bc5531deSDag-Erling Smørgrav } 192347dd1d1bSDag-Erling Smørgrav kexsigtype = sshkey_type_plain( 192447dd1d1bSDag-Erling Smørgrav sshkey_type_from_name(ssh->kex->hostkey_alg)); 192547dd1d1bSDag-Erling Smørgrav 1926bc5531deSDag-Erling Smørgrav if ((signdata = sshbuf_new()) == NULL) 1927bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 1928bc5531deSDag-Erling Smørgrav /* Don't want to accidentally accept an unbound signature */ 1929bc5531deSDag-Erling Smørgrav if (ssh->kex->session_id_len == 0) 1930bc5531deSDag-Erling Smørgrav fatal("%s: ssh->kex->session_id_len == 0", __func__); 1931bc5531deSDag-Erling Smørgrav /* 1932bc5531deSDag-Erling Smørgrav * Expect a signature for each of the ctx->nnew private keys we 1933bc5531deSDag-Erling Smørgrav * haven't seen before. They will be in the same order as the 1934bc5531deSDag-Erling Smørgrav * ctx->keys where the corresponding ctx->keys_seen[i] == 0. 1935bc5531deSDag-Erling Smørgrav */ 1936bc5531deSDag-Erling Smørgrav for (ndone = i = 0; i < ctx->nkeys; i++) { 1937bc5531deSDag-Erling Smørgrav if (ctx->keys_seen[i]) 1938bc5531deSDag-Erling Smørgrav continue; 1939bc5531deSDag-Erling Smørgrav /* Prepare data to be signed: session ID, unique string, key */ 1940bc5531deSDag-Erling Smørgrav sshbuf_reset(signdata); 1941bc5531deSDag-Erling Smørgrav if ( (r = sshbuf_put_cstring(signdata, 1942bc5531deSDag-Erling Smørgrav "hostkeys-prove-00@openssh.com")) != 0 || 1943bc5531deSDag-Erling Smørgrav (r = sshbuf_put_string(signdata, ssh->kex->session_id, 1944bc5531deSDag-Erling Smørgrav ssh->kex->session_id_len)) != 0 || 1945bc5531deSDag-Erling Smørgrav (r = sshkey_puts(ctx->keys[i], signdata)) != 0) 1946bc5531deSDag-Erling Smørgrav fatal("%s: failed to prepare signature: %s", 1947bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 1948bc5531deSDag-Erling Smørgrav /* Extract and verify signature */ 1949bc5531deSDag-Erling Smørgrav if ((r = sshpkt_get_string_direct(ssh, &sig, &siglen)) != 0) { 1950bc5531deSDag-Erling Smørgrav error("%s: couldn't parse message: %s", 1951bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 1952bc5531deSDag-Erling Smørgrav goto out; 1953bc5531deSDag-Erling Smørgrav } 195447dd1d1bSDag-Erling Smørgrav /* 195547dd1d1bSDag-Erling Smørgrav * For RSA keys, prefer to use the signature type negotiated 195647dd1d1bSDag-Erling Smørgrav * during KEX to the default (SHA1). 195747dd1d1bSDag-Erling Smørgrav */ 195847dd1d1bSDag-Erling Smørgrav use_kexsigtype = kexsigtype == KEY_RSA && 195947dd1d1bSDag-Erling Smørgrav sshkey_type_plain(ctx->keys[i]->type) == KEY_RSA; 1960bc5531deSDag-Erling Smørgrav if ((r = sshkey_verify(ctx->keys[i], sig, siglen, 196147dd1d1bSDag-Erling Smørgrav sshbuf_ptr(signdata), sshbuf_len(signdata), 196247dd1d1bSDag-Erling Smørgrav use_kexsigtype ? ssh->kex->hostkey_alg : NULL, 0)) != 0) { 1963bc5531deSDag-Erling Smørgrav error("%s: server gave bad signature for %s key %zu", 1964bc5531deSDag-Erling Smørgrav __func__, sshkey_type(ctx->keys[i]), i); 1965bc5531deSDag-Erling Smørgrav goto out; 1966bc5531deSDag-Erling Smørgrav } 1967bc5531deSDag-Erling Smørgrav /* Key is good. Mark it as 'seen' */ 1968bc5531deSDag-Erling Smørgrav ctx->keys_seen[i] = 2; 1969bc5531deSDag-Erling Smørgrav ndone++; 1970bc5531deSDag-Erling Smørgrav } 1971bc5531deSDag-Erling Smørgrav if (ndone != ctx->nnew) 1972bc5531deSDag-Erling Smørgrav fatal("%s: ndone != ctx->nnew (%zu / %zu)", __func__, 1973bc5531deSDag-Erling Smørgrav ndone, ctx->nnew); /* Shouldn't happen */ 1974bc5531deSDag-Erling Smørgrav ssh_packet_check_eom(ssh); 1975bc5531deSDag-Erling Smørgrav 1976bc5531deSDag-Erling Smørgrav /* Make the edits to known_hosts */ 1977bc5531deSDag-Erling Smørgrav update_known_hosts(ctx); 1978bc5531deSDag-Erling Smørgrav out: 1979bc5531deSDag-Erling Smørgrav hostkeys_update_ctx_free(ctx); 1980bc5531deSDag-Erling Smørgrav } 1981bc5531deSDag-Erling Smørgrav 1982bc5531deSDag-Erling Smørgrav /* 1983d93a896eSDag-Erling Smørgrav * Returns non-zero if the key is accepted by HostkeyAlgorithms. 1984d93a896eSDag-Erling Smørgrav * Made slightly less trivial by the multiple RSA signature algorithm names. 1985d93a896eSDag-Erling Smørgrav */ 1986d93a896eSDag-Erling Smørgrav static int 1987d93a896eSDag-Erling Smørgrav key_accepted_by_hostkeyalgs(const struct sshkey *key) 1988d93a896eSDag-Erling Smørgrav { 1989d93a896eSDag-Erling Smørgrav const char *ktype = sshkey_ssh_name(key); 1990d93a896eSDag-Erling Smørgrav const char *hostkeyalgs = options.hostkeyalgorithms != NULL ? 1991d93a896eSDag-Erling Smørgrav options.hostkeyalgorithms : KEX_DEFAULT_PK_ALG; 1992d93a896eSDag-Erling Smørgrav 1993d93a896eSDag-Erling Smørgrav if (key == NULL || key->type == KEY_UNSPEC) 1994d93a896eSDag-Erling Smørgrav return 0; 1995d93a896eSDag-Erling Smørgrav if (key->type == KEY_RSA && 1996d93a896eSDag-Erling Smørgrav (match_pattern_list("rsa-sha2-256", hostkeyalgs, 0) == 1 || 1997d93a896eSDag-Erling Smørgrav match_pattern_list("rsa-sha2-512", hostkeyalgs, 0) == 1)) 1998d93a896eSDag-Erling Smørgrav return 1; 1999d93a896eSDag-Erling Smørgrav return match_pattern_list(ktype, hostkeyalgs, 0) == 1; 2000d93a896eSDag-Erling Smørgrav } 2001d93a896eSDag-Erling Smørgrav 2002d93a896eSDag-Erling Smørgrav /* 2003bc5531deSDag-Erling Smørgrav * Handle hostkeys-00@openssh.com global request to inform the client of all 2004bc5531deSDag-Erling Smørgrav * the server's hostkeys. The keys are checked against the user's 2005bc5531deSDag-Erling Smørgrav * HostkeyAlgorithms preference before they are accepted. 2006bc5531deSDag-Erling Smørgrav */ 2007bc5531deSDag-Erling Smørgrav static int 2008bc5531deSDag-Erling Smørgrav client_input_hostkeys(void) 2009bc5531deSDag-Erling Smørgrav { 2010bc5531deSDag-Erling Smørgrav struct ssh *ssh = active_state; /* XXX */ 2011bc5531deSDag-Erling Smørgrav const u_char *blob = NULL; 2012bc5531deSDag-Erling Smørgrav size_t i, len = 0; 2013bc5531deSDag-Erling Smørgrav struct sshbuf *buf = NULL; 2014bc5531deSDag-Erling Smørgrav struct sshkey *key = NULL, **tmp; 2015bc5531deSDag-Erling Smørgrav int r; 2016bc5531deSDag-Erling Smørgrav char *fp; 2017bc5531deSDag-Erling Smørgrav static int hostkeys_seen = 0; /* XXX use struct ssh */ 2018bc5531deSDag-Erling Smørgrav extern struct sockaddr_storage hostaddr; /* XXX from ssh.c */ 2019bc5531deSDag-Erling Smørgrav struct hostkeys_update_ctx *ctx = NULL; 2020bc5531deSDag-Erling Smørgrav 2021bc5531deSDag-Erling Smørgrav if (hostkeys_seen) 2022bc5531deSDag-Erling Smørgrav fatal("%s: server already sent hostkeys", __func__); 2023bc5531deSDag-Erling Smørgrav if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK && 2024bc5531deSDag-Erling Smørgrav options.batch_mode) 2025bc5531deSDag-Erling Smørgrav return 1; /* won't ask in batchmode, so don't even try */ 2026bc5531deSDag-Erling Smørgrav if (!options.update_hostkeys || options.num_user_hostfiles <= 0) 2027bc5531deSDag-Erling Smørgrav return 1; 2028bc5531deSDag-Erling Smørgrav 2029bc5531deSDag-Erling Smørgrav ctx = xcalloc(1, sizeof(*ctx)); 2030bc5531deSDag-Erling Smørgrav while (ssh_packet_remaining(ssh) > 0) { 2031bc5531deSDag-Erling Smørgrav sshkey_free(key); 2032bc5531deSDag-Erling Smørgrav key = NULL; 2033bc5531deSDag-Erling Smørgrav if ((r = sshpkt_get_string_direct(ssh, &blob, &len)) != 0) { 2034bc5531deSDag-Erling Smørgrav error("%s: couldn't parse message: %s", 2035bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 2036bc5531deSDag-Erling Smørgrav goto out; 2037bc5531deSDag-Erling Smørgrav } 2038bc5531deSDag-Erling Smørgrav if ((r = sshkey_from_blob(blob, len, &key)) != 0) { 2039bc5531deSDag-Erling Smørgrav error("%s: parse key: %s", __func__, ssh_err(r)); 2040bc5531deSDag-Erling Smørgrav goto out; 2041bc5531deSDag-Erling Smørgrav } 2042bc5531deSDag-Erling Smørgrav fp = sshkey_fingerprint(key, options.fingerprint_hash, 2043bc5531deSDag-Erling Smørgrav SSH_FP_DEFAULT); 2044bc5531deSDag-Erling Smørgrav debug3("%s: received %s key %s", __func__, 2045bc5531deSDag-Erling Smørgrav sshkey_type(key), fp); 2046bc5531deSDag-Erling Smørgrav free(fp); 2047eccfee6eSDag-Erling Smørgrav 2048d93a896eSDag-Erling Smørgrav if (!key_accepted_by_hostkeyalgs(key)) { 2049bc5531deSDag-Erling Smørgrav debug3("%s: %s key not permitted by HostkeyAlgorithms", 2050bc5531deSDag-Erling Smørgrav __func__, sshkey_ssh_name(key)); 2051bc5531deSDag-Erling Smørgrav continue; 2052bc5531deSDag-Erling Smørgrav } 2053bc5531deSDag-Erling Smørgrav /* Skip certs */ 2054bc5531deSDag-Erling Smørgrav if (sshkey_is_cert(key)) { 2055bc5531deSDag-Erling Smørgrav debug3("%s: %s key is a certificate; skipping", 2056bc5531deSDag-Erling Smørgrav __func__, sshkey_ssh_name(key)); 2057bc5531deSDag-Erling Smørgrav continue; 2058bc5531deSDag-Erling Smørgrav } 2059bc5531deSDag-Erling Smørgrav /* Ensure keys are unique */ 2060bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) { 2061bc5531deSDag-Erling Smørgrav if (sshkey_equal(key, ctx->keys[i])) { 2062bc5531deSDag-Erling Smørgrav error("%s: received duplicated %s host key", 2063bc5531deSDag-Erling Smørgrav __func__, sshkey_ssh_name(key)); 2064bc5531deSDag-Erling Smørgrav goto out; 2065bc5531deSDag-Erling Smørgrav } 2066bc5531deSDag-Erling Smørgrav } 2067bc5531deSDag-Erling Smørgrav /* Key is good, record it */ 20684f52dfbbSDag-Erling Smørgrav if ((tmp = recallocarray(ctx->keys, ctx->nkeys, ctx->nkeys + 1, 2069bc5531deSDag-Erling Smørgrav sizeof(*ctx->keys))) == NULL) 20704f52dfbbSDag-Erling Smørgrav fatal("%s: recallocarray failed nkeys = %zu", 2071bc5531deSDag-Erling Smørgrav __func__, ctx->nkeys); 2072bc5531deSDag-Erling Smørgrav ctx->keys = tmp; 2073bc5531deSDag-Erling Smørgrav ctx->keys[ctx->nkeys++] = key; 2074bc5531deSDag-Erling Smørgrav key = NULL; 2075bc5531deSDag-Erling Smørgrav } 2076bc5531deSDag-Erling Smørgrav 2077bc5531deSDag-Erling Smørgrav if (ctx->nkeys == 0) { 2078bc5531deSDag-Erling Smørgrav debug("%s: server sent no hostkeys", __func__); 2079bc5531deSDag-Erling Smørgrav goto out; 2080bc5531deSDag-Erling Smørgrav } 2081bc5531deSDag-Erling Smørgrav 2082bc5531deSDag-Erling Smørgrav if ((ctx->keys_seen = calloc(ctx->nkeys, 2083bc5531deSDag-Erling Smørgrav sizeof(*ctx->keys_seen))) == NULL) 2084bc5531deSDag-Erling Smørgrav fatal("%s: calloc failed", __func__); 2085bc5531deSDag-Erling Smørgrav 2086bc5531deSDag-Erling Smørgrav get_hostfile_hostname_ipaddr(host, 2087bc5531deSDag-Erling Smørgrav options.check_host_ip ? (struct sockaddr *)&hostaddr : NULL, 2088bc5531deSDag-Erling Smørgrav options.port, &ctx->host_str, 2089bc5531deSDag-Erling Smørgrav options.check_host_ip ? &ctx->ip_str : NULL); 2090bc5531deSDag-Erling Smørgrav 2091bc5531deSDag-Erling Smørgrav /* Find which keys we already know about. */ 2092bc5531deSDag-Erling Smørgrav if ((r = hostkeys_foreach(options.user_hostfiles[0], hostkeys_find, 2093bc5531deSDag-Erling Smørgrav ctx, ctx->host_str, ctx->ip_str, 2094bc5531deSDag-Erling Smørgrav HKF_WANT_PARSE_KEY|HKF_WANT_MATCH)) != 0) { 2095bc5531deSDag-Erling Smørgrav error("%s: hostkeys_foreach failed: %s", __func__, ssh_err(r)); 2096bc5531deSDag-Erling Smørgrav goto out; 2097bc5531deSDag-Erling Smørgrav } 2098bc5531deSDag-Erling Smørgrav 2099bc5531deSDag-Erling Smørgrav /* Figure out if we have any new keys to add */ 2100bc5531deSDag-Erling Smørgrav ctx->nnew = 0; 2101bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) { 2102bc5531deSDag-Erling Smørgrav if (!ctx->keys_seen[i]) 2103bc5531deSDag-Erling Smørgrav ctx->nnew++; 2104bc5531deSDag-Erling Smørgrav } 2105bc5531deSDag-Erling Smørgrav 2106bc5531deSDag-Erling Smørgrav debug3("%s: %zu keys from server: %zu new, %zu retained. %zu to remove", 2107bc5531deSDag-Erling Smørgrav __func__, ctx->nkeys, ctx->nnew, ctx->nkeys - ctx->nnew, ctx->nold); 2108bc5531deSDag-Erling Smørgrav 2109bc5531deSDag-Erling Smørgrav if (ctx->nnew == 0 && ctx->nold != 0) { 2110bc5531deSDag-Erling Smørgrav /* We have some keys to remove. Just do it. */ 2111bc5531deSDag-Erling Smørgrav update_known_hosts(ctx); 2112bc5531deSDag-Erling Smørgrav } else if (ctx->nnew != 0) { 2113bc5531deSDag-Erling Smørgrav /* 2114bc5531deSDag-Erling Smørgrav * We have received hitherto-unseen keys from the server. 2115bc5531deSDag-Erling Smørgrav * Ask the server to confirm ownership of the private halves. 2116bc5531deSDag-Erling Smørgrav */ 2117bc5531deSDag-Erling Smørgrav debug3("%s: asking server to prove ownership for %zu keys", 2118bc5531deSDag-Erling Smørgrav __func__, ctx->nnew); 2119bc5531deSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || 2120bc5531deSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, 2121bc5531deSDag-Erling Smørgrav "hostkeys-prove-00@openssh.com")) != 0 || 2122bc5531deSDag-Erling Smørgrav (r = sshpkt_put_u8(ssh, 1)) != 0) /* bool: want reply */ 2123bc5531deSDag-Erling Smørgrav fatal("%s: cannot prepare packet: %s", 2124bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 2125bc5531deSDag-Erling Smørgrav if ((buf = sshbuf_new()) == NULL) 2126bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_new", __func__); 2127bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) { 2128bc5531deSDag-Erling Smørgrav if (ctx->keys_seen[i]) 2129bc5531deSDag-Erling Smørgrav continue; 2130bc5531deSDag-Erling Smørgrav sshbuf_reset(buf); 2131bc5531deSDag-Erling Smørgrav if ((r = sshkey_putb(ctx->keys[i], buf)) != 0) 2132bc5531deSDag-Erling Smørgrav fatal("%s: sshkey_putb: %s", 2133bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 2134bc5531deSDag-Erling Smørgrav if ((r = sshpkt_put_stringb(ssh, buf)) != 0) 2135bc5531deSDag-Erling Smørgrav fatal("%s: sshpkt_put_string: %s", 2136bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 2137bc5531deSDag-Erling Smørgrav } 2138bc5531deSDag-Erling Smørgrav if ((r = sshpkt_send(ssh)) != 0) 2139bc5531deSDag-Erling Smørgrav fatal("%s: sshpkt_send: %s", __func__, ssh_err(r)); 2140bc5531deSDag-Erling Smørgrav client_register_global_confirm( 2141bc5531deSDag-Erling Smørgrav client_global_hostkeys_private_confirm, ctx); 2142bc5531deSDag-Erling Smørgrav ctx = NULL; /* will be freed in callback */ 2143bc5531deSDag-Erling Smørgrav } 2144bc5531deSDag-Erling Smørgrav 2145bc5531deSDag-Erling Smørgrav /* Success */ 2146bc5531deSDag-Erling Smørgrav out: 2147bc5531deSDag-Erling Smørgrav hostkeys_update_ctx_free(ctx); 2148bc5531deSDag-Erling Smørgrav sshkey_free(key); 2149bc5531deSDag-Erling Smørgrav sshbuf_free(buf); 2150bc5531deSDag-Erling Smørgrav /* 2151bc5531deSDag-Erling Smørgrav * NB. Return success for all cases. The server doesn't need to know 2152bc5531deSDag-Erling Smørgrav * what the client does with its hosts file. 2153bc5531deSDag-Erling Smørgrav */ 2154bc5531deSDag-Erling Smørgrav return 1; 2155bc5531deSDag-Erling Smørgrav } 2156bc5531deSDag-Erling Smørgrav 2157bc5531deSDag-Erling Smørgrav static int 21584f52dfbbSDag-Erling Smørgrav client_input_global_request(int type, u_int32_t seq, struct ssh *ssh) 2159ae1f160dSDag-Erling Smørgrav { 2160ae1f160dSDag-Erling Smørgrav char *rtype; 2161ae1f160dSDag-Erling Smørgrav int want_reply; 2162ae1f160dSDag-Erling Smørgrav int success = 0; 2163a04a10f8SKris Kennaway 2164bc5531deSDag-Erling Smørgrav rtype = packet_get_cstring(NULL); 2165ae1f160dSDag-Erling Smørgrav want_reply = packet_get_char(); 2166efcad6b7SDag-Erling Smørgrav debug("client_input_global_request: rtype %s want_reply %d", 2167efcad6b7SDag-Erling Smørgrav rtype, want_reply); 2168bc5531deSDag-Erling Smørgrav if (strcmp(rtype, "hostkeys-00@openssh.com") == 0) 2169bc5531deSDag-Erling Smørgrav success = client_input_hostkeys(); 2170ae1f160dSDag-Erling Smørgrav if (want_reply) { 2171ae1f160dSDag-Erling Smørgrav packet_start(success ? 2172ae1f160dSDag-Erling Smørgrav SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE); 2173ae1f160dSDag-Erling Smørgrav packet_send(); 2174ae1f160dSDag-Erling Smørgrav packet_write_wait(); 2175ae1f160dSDag-Erling Smørgrav } 2176e4a9863fSDag-Erling Smørgrav free(rtype); 2177bc5531deSDag-Erling Smørgrav return 0; 2178ae1f160dSDag-Erling Smørgrav } 2179ae1f160dSDag-Erling Smørgrav 2180d74d50a8SDag-Erling Smørgrav void 21814f52dfbbSDag-Erling Smørgrav client_session2_setup(struct ssh *ssh, int id, int want_tty, int want_subsystem, 2182*190cef3dSDag-Erling Smørgrav const char *term, struct termios *tiop, int in_fd, struct sshbuf *cmd, 2183*190cef3dSDag-Erling Smørgrav char **env) 2184d74d50a8SDag-Erling Smørgrav { 2185*190cef3dSDag-Erling Smørgrav int i, j, matched, len; 2186*190cef3dSDag-Erling Smørgrav char *name, *val; 21875e8dbd04SDag-Erling Smørgrav Channel *c = NULL; 2188d74d50a8SDag-Erling Smørgrav 2189d74d50a8SDag-Erling Smørgrav debug2("%s: id %d", __func__, id); 2190d74d50a8SDag-Erling Smørgrav 21914f52dfbbSDag-Erling Smørgrav if ((c = channel_lookup(ssh, id)) == NULL) 21924f52dfbbSDag-Erling Smørgrav fatal("%s: channel %d: unknown channel", __func__, id); 21935e8dbd04SDag-Erling Smørgrav 21944a421b63SDag-Erling Smørgrav packet_set_interactive(want_tty, 21954a421b63SDag-Erling Smørgrav options.ip_qos_interactive, options.ip_qos_bulk); 21964a421b63SDag-Erling Smørgrav 2197d74d50a8SDag-Erling Smørgrav if (want_tty) { 2198d74d50a8SDag-Erling Smørgrav struct winsize ws; 2199d74d50a8SDag-Erling Smørgrav 2200d74d50a8SDag-Erling Smørgrav /* Store window size in the packet. */ 2201d74d50a8SDag-Erling Smørgrav if (ioctl(in_fd, TIOCGWINSZ, &ws) < 0) 2202d74d50a8SDag-Erling Smørgrav memset(&ws, 0, sizeof(ws)); 2203d74d50a8SDag-Erling Smørgrav 22044f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, id, "pty-req", 1); 22054f52dfbbSDag-Erling Smørgrav client_expect_confirm(ssh, id, "PTY allocation", CONFIRM_TTY); 2206d74d50a8SDag-Erling Smørgrav packet_put_cstring(term != NULL ? term : ""); 2207761efaa7SDag-Erling Smørgrav packet_put_int((u_int)ws.ws_col); 2208761efaa7SDag-Erling Smørgrav packet_put_int((u_int)ws.ws_row); 2209761efaa7SDag-Erling Smørgrav packet_put_int((u_int)ws.ws_xpixel); 2210761efaa7SDag-Erling Smørgrav packet_put_int((u_int)ws.ws_ypixel); 2211d4af9e69SDag-Erling Smørgrav if (tiop == NULL) 2212d4af9e69SDag-Erling Smørgrav tiop = get_saved_tio(); 2213*190cef3dSDag-Erling Smørgrav ssh_tty_make_modes(ssh, -1, tiop); 2214d74d50a8SDag-Erling Smørgrav packet_send(); 2215d74d50a8SDag-Erling Smørgrav /* XXX wait for reply */ 22165e8dbd04SDag-Erling Smørgrav c->client_tty = 1; 2217d74d50a8SDag-Erling Smørgrav } 2218d74d50a8SDag-Erling Smørgrav 2219d74d50a8SDag-Erling Smørgrav /* Transfer any environment variables from client to server */ 2220d74d50a8SDag-Erling Smørgrav if (options.num_send_env != 0 && env != NULL) { 2221d74d50a8SDag-Erling Smørgrav debug("Sending environment."); 2222d74d50a8SDag-Erling Smørgrav for (i = 0; env[i] != NULL; i++) { 2223d74d50a8SDag-Erling Smørgrav /* Split */ 2224d74d50a8SDag-Erling Smørgrav name = xstrdup(env[i]); 2225d74d50a8SDag-Erling Smørgrav if ((val = strchr(name, '=')) == NULL) { 2226e4a9863fSDag-Erling Smørgrav free(name); 2227d74d50a8SDag-Erling Smørgrav continue; 2228d74d50a8SDag-Erling Smørgrav } 2229d74d50a8SDag-Erling Smørgrav *val++ = '\0'; 2230d74d50a8SDag-Erling Smørgrav 2231d74d50a8SDag-Erling Smørgrav matched = 0; 2232d74d50a8SDag-Erling Smørgrav for (j = 0; j < options.num_send_env; j++) { 2233d74d50a8SDag-Erling Smørgrav if (match_pattern(name, options.send_env[j])) { 2234d74d50a8SDag-Erling Smørgrav matched = 1; 2235d74d50a8SDag-Erling Smørgrav break; 2236d74d50a8SDag-Erling Smørgrav } 2237d74d50a8SDag-Erling Smørgrav } 2238d74d50a8SDag-Erling Smørgrav if (!matched) { 2239d74d50a8SDag-Erling Smørgrav debug3("Ignored env %s", name); 2240e4a9863fSDag-Erling Smørgrav free(name); 2241d74d50a8SDag-Erling Smørgrav continue; 2242d74d50a8SDag-Erling Smørgrav } 2243d74d50a8SDag-Erling Smørgrav 2244d74d50a8SDag-Erling Smørgrav debug("Sending env %s = %s", name, val); 22454f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, id, "env", 0); 2246d74d50a8SDag-Erling Smørgrav packet_put_cstring(name); 2247d74d50a8SDag-Erling Smørgrav packet_put_cstring(val); 2248d74d50a8SDag-Erling Smørgrav packet_send(); 2249e4a9863fSDag-Erling Smørgrav free(name); 2250d74d50a8SDag-Erling Smørgrav } 2251d74d50a8SDag-Erling Smørgrav } 2252*190cef3dSDag-Erling Smørgrav for (i = 0; i < options.num_setenv; i++) { 2253*190cef3dSDag-Erling Smørgrav /* Split */ 2254*190cef3dSDag-Erling Smørgrav name = xstrdup(options.setenv[i]); 2255*190cef3dSDag-Erling Smørgrav if ((val = strchr(name, '=')) == NULL) { 2256*190cef3dSDag-Erling Smørgrav free(name); 2257*190cef3dSDag-Erling Smørgrav continue; 2258*190cef3dSDag-Erling Smørgrav } 2259*190cef3dSDag-Erling Smørgrav *val++ = '\0'; 2260d74d50a8SDag-Erling Smørgrav 2261*190cef3dSDag-Erling Smørgrav debug("Setting env %s = %s", name, val); 2262*190cef3dSDag-Erling Smørgrav channel_request_start(ssh, id, "env", 0); 2263*190cef3dSDag-Erling Smørgrav packet_put_cstring(name); 2264*190cef3dSDag-Erling Smørgrav packet_put_cstring(val); 2265*190cef3dSDag-Erling Smørgrav packet_send(); 2266*190cef3dSDag-Erling Smørgrav free(name); 2267*190cef3dSDag-Erling Smørgrav } 2268*190cef3dSDag-Erling Smørgrav 2269*190cef3dSDag-Erling Smørgrav len = sshbuf_len(cmd); 2270d74d50a8SDag-Erling Smørgrav if (len > 0) { 2271d74d50a8SDag-Erling Smørgrav if (len > 900) 2272d74d50a8SDag-Erling Smørgrav len = 900; 2273d74d50a8SDag-Erling Smørgrav if (want_subsystem) { 2274d4af9e69SDag-Erling Smørgrav debug("Sending subsystem: %.*s", 2275*190cef3dSDag-Erling Smørgrav len, (const u_char*)sshbuf_ptr(cmd)); 22764f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, id, "subsystem", 1); 22774f52dfbbSDag-Erling Smørgrav client_expect_confirm(ssh, id, "subsystem", 22784f52dfbbSDag-Erling Smørgrav CONFIRM_CLOSE); 2279d74d50a8SDag-Erling Smørgrav } else { 2280d4af9e69SDag-Erling Smørgrav debug("Sending command: %.*s", 2281*190cef3dSDag-Erling Smørgrav len, (const u_char*)sshbuf_ptr(cmd)); 22824f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, id, "exec", 1); 22834f52dfbbSDag-Erling Smørgrav client_expect_confirm(ssh, id, "exec", CONFIRM_CLOSE); 2284d74d50a8SDag-Erling Smørgrav } 2285*190cef3dSDag-Erling Smørgrav packet_put_string(sshbuf_ptr(cmd), sshbuf_len(cmd)); 2286d74d50a8SDag-Erling Smørgrav packet_send(); 2287d74d50a8SDag-Erling Smørgrav } else { 22884f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, id, "shell", 1); 22894f52dfbbSDag-Erling Smørgrav client_expect_confirm(ssh, id, "shell", CONFIRM_CLOSE); 2290d74d50a8SDag-Erling Smørgrav packet_send(); 2291d74d50a8SDag-Erling Smørgrav } 2292d74d50a8SDag-Erling Smørgrav } 2293d74d50a8SDag-Erling Smørgrav 2294ae1f160dSDag-Erling Smørgrav static void 22954f52dfbbSDag-Erling Smørgrav client_init_dispatch(void) 2296a04a10f8SKris Kennaway { 2297a04a10f8SKris Kennaway dispatch_init(&dispatch_protocol_error); 2298545d5ecaSDag-Erling Smørgrav 2299a04a10f8SKris Kennaway dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); 2300a04a10f8SKris Kennaway dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data); 2301a04a10f8SKris Kennaway dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); 2302a04a10f8SKris Kennaway dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); 2303a04a10f8SKris Kennaway dispatch_set(SSH2_MSG_CHANNEL_OPEN, &client_input_channel_open); 2304a04a10f8SKris Kennaway dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); 2305a04a10f8SKris Kennaway dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); 23061e8db6e2SBrian Feldman dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &client_input_channel_req); 2307a04a10f8SKris Kennaway dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); 2308d4af9e69SDag-Erling Smørgrav dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, &channel_input_status_confirm); 2309d4af9e69SDag-Erling Smørgrav dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &channel_input_status_confirm); 2310ae1f160dSDag-Erling Smørgrav dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &client_input_global_request); 23111e8db6e2SBrian Feldman 23121e8db6e2SBrian Feldman /* rekeying */ 23131e8db6e2SBrian Feldman dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit); 2314545d5ecaSDag-Erling Smørgrav 2315545d5ecaSDag-Erling Smørgrav /* global request reply messages */ 2316545d5ecaSDag-Erling Smørgrav dispatch_set(SSH2_MSG_REQUEST_FAILURE, &client_global_request_reply); 2317545d5ecaSDag-Erling Smørgrav dispatch_set(SSH2_MSG_REQUEST_SUCCESS, &client_global_request_reply); 2318a04a10f8SKris Kennaway } 2319d4af9e69SDag-Erling Smørgrav 2320e146993eSDag-Erling Smørgrav void 2321e146993eSDag-Erling Smørgrav client_stop_mux(void) 2322e146993eSDag-Erling Smørgrav { 2323e146993eSDag-Erling Smørgrav if (options.control_path != NULL && muxserver_sock != -1) 2324e146993eSDag-Erling Smørgrav unlink(options.control_path); 2325e146993eSDag-Erling Smørgrav /* 23266888a9beSDag-Erling Smørgrav * If we are in persist mode, or don't have a shell, signal that we 23276888a9beSDag-Erling Smørgrav * should close when all active channels are closed. 2328e146993eSDag-Erling Smørgrav */ 23296888a9beSDag-Erling Smørgrav if (options.control_persist || no_shell_flag) { 2330e146993eSDag-Erling Smørgrav session_closed = 1; 2331e146993eSDag-Erling Smørgrav setproctitle("[stopped mux]"); 2332e146993eSDag-Erling Smørgrav } 2333e146993eSDag-Erling Smørgrav } 2334e146993eSDag-Erling Smørgrav 2335efcad6b7SDag-Erling Smørgrav /* client specific fatal cleanup */ 2336efcad6b7SDag-Erling Smørgrav void 2337efcad6b7SDag-Erling Smørgrav cleanup_exit(int i) 2338efcad6b7SDag-Erling Smørgrav { 2339e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 2340efcad6b7SDag-Erling Smørgrav leave_non_blocking(); 2341d4af9e69SDag-Erling Smørgrav if (options.control_path != NULL && muxserver_sock != -1) 2342d74d50a8SDag-Erling Smørgrav unlink(options.control_path); 23434a421b63SDag-Erling Smørgrav ssh_kill_proxy_command(); 2344efcad6b7SDag-Erling Smørgrav _exit(i); 2345efcad6b7SDag-Erling Smørgrav } 2346