1*38a52bd3SEd Maste /* $OpenBSD: clientloop.c,v 1.380 2022/06/03 04:30:46 djm Exp $ */ 2511b41d2SMark Murray /* 3511b41d2SMark Murray * Author: Tatu Ylonen <ylo@cs.hut.fi> 4511b41d2SMark Murray * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 5511b41d2SMark Murray * All rights reserved 6511b41d2SMark Murray * The main loop for the interactive session (client side). 7511b41d2SMark Murray * 8b66f2d16SKris Kennaway * As far as I am concerned, the code I have written for this software 9b66f2d16SKris Kennaway * can be used freely for any purpose. Any derived versions of this 10b66f2d16SKris Kennaway * software must be clearly marked as such, and if the derived work is 11b66f2d16SKris Kennaway * incompatible with the protocol description in the RFC file, it must be 12b66f2d16SKris Kennaway * called by a name other than "ssh" or "Secure Shell". 13b66f2d16SKris Kennaway * 14b66f2d16SKris Kennaway * 15b66f2d16SKris Kennaway * Copyright (c) 1999 Theo de Raadt. All rights reserved. 16b66f2d16SKris Kennaway * 17b66f2d16SKris Kennaway * Redistribution and use in source and binary forms, with or without 18b66f2d16SKris Kennaway * modification, are permitted provided that the following conditions 19b66f2d16SKris Kennaway * are met: 20b66f2d16SKris Kennaway * 1. Redistributions of source code must retain the above copyright 21b66f2d16SKris Kennaway * notice, this list of conditions and the following disclaimer. 22b66f2d16SKris Kennaway * 2. Redistributions in binary form must reproduce the above copyright 23b66f2d16SKris Kennaway * notice, this list of conditions and the following disclaimer in the 24b66f2d16SKris Kennaway * documentation and/or other materials provided with the distribution. 25b66f2d16SKris Kennaway * 26b66f2d16SKris Kennaway * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 27b66f2d16SKris Kennaway * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 28b66f2d16SKris Kennaway * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 29b66f2d16SKris Kennaway * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 30b66f2d16SKris Kennaway * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 31b66f2d16SKris Kennaway * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32b66f2d16SKris Kennaway * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33b66f2d16SKris Kennaway * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34b66f2d16SKris Kennaway * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 35b66f2d16SKris Kennaway * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36b66f2d16SKris Kennaway * 37b66f2d16SKris Kennaway * 38a04a10f8SKris Kennaway * SSH2 support added by Markus Friedl. 39ae1f160dSDag-Erling Smørgrav * Copyright (c) 1999, 2000, 2001 Markus Friedl. All rights reserved. 40b66f2d16SKris Kennaway * 41b66f2d16SKris Kennaway * Redistribution and use in source and binary forms, with or without 42b66f2d16SKris Kennaway * modification, are permitted provided that the following conditions 43b66f2d16SKris Kennaway * are met: 44b66f2d16SKris Kennaway * 1. Redistributions of source code must retain the above copyright 45b66f2d16SKris Kennaway * notice, this list of conditions and the following disclaimer. 46b66f2d16SKris Kennaway * 2. Redistributions in binary form must reproduce the above copyright 47b66f2d16SKris Kennaway * notice, this list of conditions and the following disclaimer in the 48b66f2d16SKris Kennaway * documentation and/or other materials provided with the distribution. 49b66f2d16SKris Kennaway * 50b66f2d16SKris Kennaway * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 51b66f2d16SKris Kennaway * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 52b66f2d16SKris Kennaway * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 53b66f2d16SKris Kennaway * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 54b66f2d16SKris Kennaway * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 55b66f2d16SKris Kennaway * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 56b66f2d16SKris Kennaway * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 57b66f2d16SKris Kennaway * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 58b66f2d16SKris Kennaway * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 59b66f2d16SKris Kennaway * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 60511b41d2SMark Murray */ 61511b41d2SMark Murray 62511b41d2SMark Murray #include "includes.h" 63511b41d2SMark Murray 64761efaa7SDag-Erling Smørgrav #include <sys/types.h> 65761efaa7SDag-Erling Smørgrav #include <sys/ioctl.h> 66761efaa7SDag-Erling Smørgrav #ifdef HAVE_SYS_STAT_H 67761efaa7SDag-Erling Smørgrav # include <sys/stat.h> 68761efaa7SDag-Erling Smørgrav #endif 69761efaa7SDag-Erling Smørgrav #ifdef HAVE_SYS_TIME_H 70761efaa7SDag-Erling Smørgrav # include <sys/time.h> 71761efaa7SDag-Erling Smørgrav #endif 72761efaa7SDag-Erling Smørgrav #include <sys/socket.h> 73761efaa7SDag-Erling Smørgrav 74761efaa7SDag-Erling Smørgrav #include <ctype.h> 75761efaa7SDag-Erling Smørgrav #include <errno.h> 76761efaa7SDag-Erling Smørgrav #ifdef HAVE_PATHS_H 77761efaa7SDag-Erling Smørgrav #include <paths.h> 78761efaa7SDag-Erling Smørgrav #endif 791323ec57SEd Maste #ifdef HAVE_POLL_H 801323ec57SEd Maste #include <poll.h> 811323ec57SEd Maste #endif 82761efaa7SDag-Erling Smørgrav #include <signal.h> 83761efaa7SDag-Erling Smørgrav #include <stdio.h> 84761efaa7SDag-Erling Smørgrav #include <stdlib.h> 85761efaa7SDag-Erling Smørgrav #include <string.h> 8619261079SEd Maste #include <stdarg.h> 87761efaa7SDag-Erling Smørgrav #include <termios.h> 88761efaa7SDag-Erling Smørgrav #include <pwd.h> 89761efaa7SDag-Erling Smørgrav #include <unistd.h> 90bc5531deSDag-Erling Smørgrav #include <limits.h> 91761efaa7SDag-Erling Smørgrav 92d4af9e69SDag-Erling Smørgrav #include "openbsd-compat/sys-queue.h" 93761efaa7SDag-Erling Smørgrav #include "xmalloc.h" 94511b41d2SMark Murray #include "ssh.h" 951e8db6e2SBrian Feldman #include "ssh2.h" 96511b41d2SMark Murray #include "packet.h" 97190cef3dSDag-Erling Smørgrav #include "sshbuf.h" 98a04a10f8SKris Kennaway #include "compat.h" 99a04a10f8SKris Kennaway #include "channels.h" 100a04a10f8SKris Kennaway #include "dispatch.h" 101190cef3dSDag-Erling Smørgrav #include "sshkey.h" 102761efaa7SDag-Erling Smørgrav #include "cipher.h" 1031e8db6e2SBrian Feldman #include "kex.h" 104eccfee6eSDag-Erling Smørgrav #include "myproposal.h" 1051e8db6e2SBrian Feldman #include "log.h" 106a0ee8cc6SDag-Erling Smørgrav #include "misc.h" 1071e8db6e2SBrian Feldman #include "readconf.h" 1081e8db6e2SBrian Feldman #include "clientloop.h" 109021d409fSDag-Erling Smørgrav #include "sshconnect.h" 1101e8db6e2SBrian Feldman #include "authfd.h" 1111e8db6e2SBrian Feldman #include "atomicio.h" 112d74d50a8SDag-Erling Smørgrav #include "sshpty.h" 113d74d50a8SDag-Erling Smørgrav #include "match.h" 114d74d50a8SDag-Erling Smørgrav #include "msg.h" 115bc5531deSDag-Erling Smørgrav #include "ssherr.h" 116bc5531deSDag-Erling Smørgrav #include "hostfile.h" 1175b9b2fafSBrian Feldman 1181323ec57SEd Maste /* Permitted RSA signature algorithms for UpdateHostkeys proofs */ 1191323ec57SEd Maste #define HOSTKEY_PROOF_RSA_ALGS "rsa-sha2-512,rsa-sha2-256" 1201323ec57SEd Maste 1215b9b2fafSBrian Feldman /* import options */ 1224899dde7SBrian Feldman extern Options options; 1234899dde7SBrian Feldman 124d74d50a8SDag-Erling Smørgrav /* Control socket */ 125b15c8340SDag-Erling Smørgrav extern int muxserver_sock; /* XXX use mux_client_cleanup() instead */ 126d74d50a8SDag-Erling Smørgrav 127511b41d2SMark Murray /* 128511b41d2SMark Murray * Name of the host we are connecting to. This is the name given on the 12919261079SEd Maste * command line, or the Hostname specified for the user-supplied name in a 130511b41d2SMark Murray * configuration file. 131511b41d2SMark Murray */ 132511b41d2SMark Murray extern char *host; 133511b41d2SMark Murray 134511b41d2SMark Murray /* 13519261079SEd Maste * If this field is not NULL, the ForwardAgent socket is this path and different 13619261079SEd Maste * instead of SSH_AUTH_SOCK. 13719261079SEd Maste */ 13819261079SEd Maste extern char *forward_agent_sock_path; 13919261079SEd Maste 14019261079SEd Maste /* 141511b41d2SMark Murray * Flag to indicate that we have received a window change signal which has 142511b41d2SMark Murray * not yet been processed. This will cause a message indicating the new 143511b41d2SMark Murray * window size to be sent to the server a little later. This is volatile 144511b41d2SMark Murray * because this is updated in a signal handler. 145511b41d2SMark Murray */ 146ae1f160dSDag-Erling Smørgrav static volatile sig_atomic_t received_window_change_signal = 0; 147ae1f160dSDag-Erling Smørgrav static volatile sig_atomic_t received_signal = 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. */ 156190cef3dSDag-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. */ 16219261079SEd Maste static time_t server_alive_time; /* Time to do server_alive_check */ 163a04a10f8SKris Kennaway 16419261079SEd Maste static void client_init_dispatch(struct ssh *ssh); 165a04a10f8SKris Kennaway int session_ident = -1; 166a04a10f8SKris Kennaway 167d4af9e69SDag-Erling Smørgrav /* Track escape per proto2 channel */ 168d4af9e69SDag-Erling Smørgrav struct escape_filter_ctx { 169d4af9e69SDag-Erling Smørgrav int escape_pending; 170d4af9e69SDag-Erling Smørgrav int escape_char; 171d74d50a8SDag-Erling Smørgrav }; 172d74d50a8SDag-Erling Smørgrav 173d4af9e69SDag-Erling Smørgrav /* Context for channel confirmation replies */ 174d4af9e69SDag-Erling Smørgrav struct channel_reply_ctx { 175d4af9e69SDag-Erling Smørgrav const char *request_type; 176e146993eSDag-Erling Smørgrav int id; 177e146993eSDag-Erling Smørgrav enum confirm_action action; 178d4af9e69SDag-Erling Smørgrav }; 179d4af9e69SDag-Erling Smørgrav 180d4af9e69SDag-Erling Smørgrav /* Global request success/failure callbacks */ 1814f52dfbbSDag-Erling Smørgrav /* XXX move to struct ssh? */ 182d4af9e69SDag-Erling Smørgrav struct global_confirm { 183d4af9e69SDag-Erling Smørgrav TAILQ_ENTRY(global_confirm) entry; 184d4af9e69SDag-Erling Smørgrav global_confirm_cb *cb; 185d4af9e69SDag-Erling Smørgrav void *ctx; 186d4af9e69SDag-Erling Smørgrav int ref_count; 187d4af9e69SDag-Erling Smørgrav }; 188d4af9e69SDag-Erling Smørgrav TAILQ_HEAD(global_confirms, global_confirm); 189d4af9e69SDag-Erling Smørgrav static struct global_confirms global_confirms = 190d4af9e69SDag-Erling Smørgrav TAILQ_HEAD_INITIALIZER(global_confirms); 191d4af9e69SDag-Erling Smørgrav 192190cef3dSDag-Erling Smørgrav void ssh_process_session2_setup(int, int, int, struct sshbuf *); 1931323ec57SEd Maste static void quit_message(const char *fmt, ...) 1941323ec57SEd Maste __attribute__((__format__ (printf, 1, 2))); 1951323ec57SEd Maste 1961323ec57SEd Maste static void 1971323ec57SEd Maste quit_message(const char *fmt, ...) 1981323ec57SEd Maste { 1991323ec57SEd Maste char *msg; 2001323ec57SEd Maste va_list args; 2011323ec57SEd Maste int r; 2021323ec57SEd Maste 2031323ec57SEd Maste va_start(args, fmt); 2041323ec57SEd Maste xvasprintf(&msg, fmt, args); 2051323ec57SEd Maste va_end(args); 2061323ec57SEd Maste 2071323ec57SEd Maste if ((r = sshbuf_putf(stderr_buffer, "%s\r\n", msg)) != 0) 2081323ec57SEd Maste fatal_fr(r, "sshbuf_putf"); 2091323ec57SEd Maste quit_pending = 1; 2101323ec57SEd Maste } 211d74d50a8SDag-Erling Smørgrav 212511b41d2SMark Murray /* 213511b41d2SMark Murray * Signal handler for the window change signal (SIGWINCH). This just sets a 214511b41d2SMark Murray * flag indicating that the window has changed. 215511b41d2SMark Murray */ 216761efaa7SDag-Erling Smørgrav /*ARGSUSED */ 217ae1f160dSDag-Erling Smørgrav static void 218511b41d2SMark Murray window_change_handler(int sig) 219511b41d2SMark Murray { 220511b41d2SMark Murray received_window_change_signal = 1; 221511b41d2SMark Murray } 222511b41d2SMark Murray 223511b41d2SMark Murray /* 224511b41d2SMark Murray * Signal handler for signals that cause the program to terminate. These 225511b41d2SMark Murray * signals must be trapped to restore terminal modes. 226511b41d2SMark Murray */ 227761efaa7SDag-Erling Smørgrav /*ARGSUSED */ 228ae1f160dSDag-Erling Smørgrav static void 229511b41d2SMark Murray signal_handler(int sig) 230511b41d2SMark Murray { 231ae1f160dSDag-Erling Smørgrav received_signal = sig; 232ae1f160dSDag-Erling Smørgrav quit_pending = 1; 233511b41d2SMark Murray } 234511b41d2SMark Murray 235511b41d2SMark Murray /* 236e2f6069cSDag-Erling Smørgrav * Sets control_persist_exit_time to the absolute time when the 237e2f6069cSDag-Erling Smørgrav * backgrounded control master should exit due to expiry of the 238e2f6069cSDag-Erling Smørgrav * ControlPersist timeout. Sets it to 0 if we are not a backgrounded 239e2f6069cSDag-Erling Smørgrav * control master process, or if there is no ControlPersist timeout. 240e2f6069cSDag-Erling Smørgrav */ 241e2f6069cSDag-Erling Smørgrav static void 2424f52dfbbSDag-Erling Smørgrav set_control_persist_exit_time(struct ssh *ssh) 243e2f6069cSDag-Erling Smørgrav { 244e2f6069cSDag-Erling Smørgrav if (muxserver_sock == -1 || !options.control_persist 245e146993eSDag-Erling Smørgrav || options.control_persist_timeout == 0) { 246e2f6069cSDag-Erling Smørgrav /* not using a ControlPersist timeout */ 247e2f6069cSDag-Erling Smørgrav control_persist_exit_time = 0; 2484f52dfbbSDag-Erling Smørgrav } else if (channel_still_open(ssh)) { 249e2f6069cSDag-Erling Smørgrav /* some client connections are still open */ 250e2f6069cSDag-Erling Smørgrav if (control_persist_exit_time > 0) 25119261079SEd Maste debug2_f("cancel scheduled exit"); 252e2f6069cSDag-Erling Smørgrav control_persist_exit_time = 0; 253e2f6069cSDag-Erling Smørgrav } else if (control_persist_exit_time <= 0) { 254e2f6069cSDag-Erling Smørgrav /* a client connection has recently closed */ 255e4a9863fSDag-Erling Smørgrav control_persist_exit_time = monotime() + 256e2f6069cSDag-Erling Smørgrav (time_t)options.control_persist_timeout; 25719261079SEd Maste debug2_f("schedule exit in %d seconds", 258e2f6069cSDag-Erling Smørgrav options.control_persist_timeout); 259e2f6069cSDag-Erling Smørgrav } 260e2f6069cSDag-Erling Smørgrav /* else we are already counting down to the timeout */ 261e2f6069cSDag-Erling Smørgrav } 262e2f6069cSDag-Erling Smørgrav 263462c32cbSDag-Erling Smørgrav #define SSH_X11_VALID_DISPLAY_CHARS ":/.-_" 264462c32cbSDag-Erling Smørgrav static int 265462c32cbSDag-Erling Smørgrav client_x11_display_valid(const char *display) 266462c32cbSDag-Erling Smørgrav { 267462c32cbSDag-Erling Smørgrav size_t i, dlen; 268462c32cbSDag-Erling Smørgrav 269acc1a9efSDag-Erling Smørgrav if (display == NULL) 270acc1a9efSDag-Erling Smørgrav return 0; 271acc1a9efSDag-Erling Smørgrav 272462c32cbSDag-Erling Smørgrav dlen = strlen(display); 273462c32cbSDag-Erling Smørgrav for (i = 0; i < dlen; i++) { 274f7167e0eSDag-Erling Smørgrav if (!isalnum((u_char)display[i]) && 275462c32cbSDag-Erling Smørgrav strchr(SSH_X11_VALID_DISPLAY_CHARS, display[i]) == NULL) { 276462c32cbSDag-Erling Smørgrav debug("Invalid character '%c' in DISPLAY", display[i]); 277462c32cbSDag-Erling Smørgrav return 0; 278462c32cbSDag-Erling Smørgrav } 279462c32cbSDag-Erling Smørgrav } 280462c32cbSDag-Erling Smørgrav return 1; 281462c32cbSDag-Erling Smørgrav } 282462c32cbSDag-Erling Smørgrav 283043840dfSDag-Erling Smørgrav #define SSH_X11_PROTO "MIT-MAGIC-COOKIE-1" 284557f75e5SDag-Erling Smørgrav #define X11_TIMEOUT_SLACK 60 285acc1a9efSDag-Erling Smørgrav int 2864f52dfbbSDag-Erling Smørgrav client_x11_get_proto(struct ssh *ssh, const char *display, 2874f52dfbbSDag-Erling Smørgrav const char *xauth_path, u_int trusted, u_int timeout, 2884f52dfbbSDag-Erling Smørgrav char **_proto, char **_data) 289043840dfSDag-Erling Smørgrav { 2902f513db7SEd Maste char *cmd, line[512], xdisplay[512]; 291acc1a9efSDag-Erling Smørgrav char xauthfile[PATH_MAX], xauthdir[PATH_MAX]; 292043840dfSDag-Erling Smørgrav static char proto[512], data[512]; 293043840dfSDag-Erling Smørgrav FILE *f; 294ca86bcf2SDag-Erling Smørgrav int got_data = 0, generated = 0, do_unlink = 0, r; 295043840dfSDag-Erling Smørgrav struct stat st; 296557f75e5SDag-Erling Smørgrav u_int now, x11_timeout_real; 297043840dfSDag-Erling Smørgrav 298043840dfSDag-Erling Smørgrav *_proto = proto; 299043840dfSDag-Erling Smørgrav *_data = data; 300acc1a9efSDag-Erling Smørgrav proto[0] = data[0] = xauthfile[0] = xauthdir[0] = '\0'; 301043840dfSDag-Erling Smørgrav 302acc1a9efSDag-Erling Smørgrav if (!client_x11_display_valid(display)) { 303acc1a9efSDag-Erling Smørgrav if (display != NULL) 304acc1a9efSDag-Erling Smørgrav logit("DISPLAY \"%s\" invalid; disabling X11 forwarding", 305462c32cbSDag-Erling Smørgrav display); 306acc1a9efSDag-Erling Smørgrav return -1; 307043840dfSDag-Erling Smørgrav } 308acc1a9efSDag-Erling Smørgrav if (xauth_path != NULL && stat(xauth_path, &st) == -1) { 309acc1a9efSDag-Erling Smørgrav debug("No xauth program."); 310acc1a9efSDag-Erling Smørgrav xauth_path = NULL; 311acc1a9efSDag-Erling Smørgrav } 312acc1a9efSDag-Erling Smørgrav 313acc1a9efSDag-Erling Smørgrav if (xauth_path != NULL) { 314043840dfSDag-Erling Smørgrav /* 315043840dfSDag-Erling Smørgrav * Handle FamilyLocal case where $DISPLAY does 316043840dfSDag-Erling Smørgrav * not match an authorization entry. For this we 317043840dfSDag-Erling Smørgrav * just try "xauth list unix:displaynum.screennum". 318043840dfSDag-Erling Smørgrav * XXX: "localhost" match to determine FamilyLocal 319043840dfSDag-Erling Smørgrav * is not perfect. 320043840dfSDag-Erling Smørgrav */ 321043840dfSDag-Erling Smørgrav if (strncmp(display, "localhost:", 10) == 0) { 322acc1a9efSDag-Erling Smørgrav if ((r = snprintf(xdisplay, sizeof(xdisplay), "unix:%s", 323acc1a9efSDag-Erling Smørgrav display + 10)) < 0 || 324acc1a9efSDag-Erling Smørgrav (size_t)r >= sizeof(xdisplay)) { 32519261079SEd Maste error_f("display name too long"); 326acc1a9efSDag-Erling Smørgrav return -1; 327acc1a9efSDag-Erling Smørgrav } 328043840dfSDag-Erling Smørgrav display = xdisplay; 329043840dfSDag-Erling Smørgrav } 330043840dfSDag-Erling Smørgrav if (trusted == 0) { 331557f75e5SDag-Erling Smørgrav /* 332acc1a9efSDag-Erling Smørgrav * Generate an untrusted X11 auth cookie. 333acc1a9efSDag-Erling Smørgrav * 334557f75e5SDag-Erling Smørgrav * The authentication cookie should briefly outlive 335557f75e5SDag-Erling Smørgrav * ssh's willingness to forward X11 connections to 336557f75e5SDag-Erling Smørgrav * avoid nasty fail-open behaviour in the X server. 337557f75e5SDag-Erling Smørgrav */ 338acc1a9efSDag-Erling Smørgrav mktemp_proto(xauthdir, sizeof(xauthdir)); 339acc1a9efSDag-Erling Smørgrav if (mkdtemp(xauthdir) == NULL) { 34019261079SEd Maste error_f("mkdtemp: %s", strerror(errno)); 341acc1a9efSDag-Erling Smørgrav return -1; 342acc1a9efSDag-Erling Smørgrav } 343acc1a9efSDag-Erling Smørgrav do_unlink = 1; 344acc1a9efSDag-Erling Smørgrav if ((r = snprintf(xauthfile, sizeof(xauthfile), 345acc1a9efSDag-Erling Smørgrav "%s/xauthfile", xauthdir)) < 0 || 346acc1a9efSDag-Erling Smørgrav (size_t)r >= sizeof(xauthfile)) { 34719261079SEd Maste error_f("xauthfile path too long"); 348acc1a9efSDag-Erling Smørgrav rmdir(xauthdir); 349acc1a9efSDag-Erling Smørgrav return -1; 350acc1a9efSDag-Erling Smørgrav } 351acc1a9efSDag-Erling Smørgrav 3522f513db7SEd Maste if (timeout == 0) { 3532f513db7SEd Maste /* auth doesn't time out */ 3542f513db7SEd Maste xasprintf(&cmd, "%s -f %s generate %s %s " 3552f513db7SEd Maste "untrusted 2>%s", 356557f75e5SDag-Erling Smørgrav xauth_path, xauthfile, display, 3572f513db7SEd Maste SSH_X11_PROTO, _PATH_DEVNULL); 3582f513db7SEd Maste } else { 3592f513db7SEd Maste /* Add some slack to requested expiry */ 3602f513db7SEd Maste if (timeout < UINT_MAX - X11_TIMEOUT_SLACK) 3612f513db7SEd Maste x11_timeout_real = timeout + 3622f513db7SEd Maste X11_TIMEOUT_SLACK; 3632f513db7SEd Maste else { 3642f513db7SEd Maste /* Don't overflow on long timeouts */ 3652f513db7SEd Maste x11_timeout_real = UINT_MAX; 3662f513db7SEd Maste } 3672f513db7SEd Maste xasprintf(&cmd, "%s -f %s generate %s %s " 3682f513db7SEd Maste "untrusted timeout %u 2>%s", 3692f513db7SEd Maste xauth_path, xauthfile, display, 3702f513db7SEd Maste SSH_X11_PROTO, x11_timeout_real, 3712f513db7SEd Maste _PATH_DEVNULL); 3722f513db7SEd Maste } 37319261079SEd Maste debug2_f("xauth command: %s", cmd); 3742f513db7SEd Maste 3752f513db7SEd Maste if (timeout != 0 && x11_refuse_time == 0) { 376e4a9863fSDag-Erling Smørgrav now = monotime() + 1; 377e2f6069cSDag-Erling Smørgrav if (UINT_MAX - timeout < now) 378e2f6069cSDag-Erling Smørgrav x11_refuse_time = UINT_MAX; 379e2f6069cSDag-Erling Smørgrav else 380e2f6069cSDag-Erling Smørgrav x11_refuse_time = now + timeout; 3814f52dfbbSDag-Erling Smørgrav channel_set_x11_refuse_time(ssh, 3824f52dfbbSDag-Erling Smørgrav x11_refuse_time); 383e2f6069cSDag-Erling Smørgrav } 384557f75e5SDag-Erling Smørgrav if (system(cmd) == 0) 385557f75e5SDag-Erling Smørgrav generated = 1; 3862f513db7SEd Maste free(cmd); 387043840dfSDag-Erling Smørgrav } 388d4af9e69SDag-Erling Smørgrav 389d4af9e69SDag-Erling Smørgrav /* 390d4af9e69SDag-Erling Smørgrav * When in untrusted mode, we read the cookie only if it was 391d4af9e69SDag-Erling Smørgrav * successfully generated as an untrusted one in the step 392d4af9e69SDag-Erling Smørgrav * above. 393d4af9e69SDag-Erling Smørgrav */ 394d4af9e69SDag-Erling Smørgrav if (trusted || generated) { 3952f513db7SEd Maste xasprintf(&cmd, 396021d409fSDag-Erling Smørgrav "%s %s%s list %s 2>" _PATH_DEVNULL, 397043840dfSDag-Erling Smørgrav xauth_path, 398043840dfSDag-Erling Smørgrav generated ? "-f " : "" , 399043840dfSDag-Erling Smørgrav generated ? xauthfile : "", 400043840dfSDag-Erling Smørgrav display); 401043840dfSDag-Erling Smørgrav debug2("x11_get_proto: %s", cmd); 402043840dfSDag-Erling Smørgrav f = popen(cmd, "r"); 403043840dfSDag-Erling Smørgrav if (f && fgets(line, sizeof(line), f) && 404043840dfSDag-Erling Smørgrav sscanf(line, "%*s %511s %511s", proto, data) == 2) 405043840dfSDag-Erling Smørgrav got_data = 1; 406043840dfSDag-Erling Smørgrav if (f) 407043840dfSDag-Erling Smørgrav pclose(f); 4082f513db7SEd Maste free(cmd); 409acc1a9efSDag-Erling Smørgrav } 410043840dfSDag-Erling Smørgrav } 411043840dfSDag-Erling Smørgrav 412043840dfSDag-Erling Smørgrav if (do_unlink) { 413043840dfSDag-Erling Smørgrav unlink(xauthfile); 414043840dfSDag-Erling Smørgrav rmdir(xauthdir); 415043840dfSDag-Erling Smørgrav } 416acc1a9efSDag-Erling Smørgrav 417acc1a9efSDag-Erling Smørgrav /* Don't fall back to fake X11 data for untrusted forwarding */ 418acc1a9efSDag-Erling Smørgrav if (!trusted && !got_data) { 419acc1a9efSDag-Erling Smørgrav error("Warning: untrusted X11 forwarding setup failed: " 420acc1a9efSDag-Erling Smørgrav "xauth key data not generated"); 421acc1a9efSDag-Erling Smørgrav return -1; 422acc1a9efSDag-Erling Smørgrav } 423043840dfSDag-Erling Smørgrav 424043840dfSDag-Erling Smørgrav /* 425043840dfSDag-Erling Smørgrav * If we didn't get authentication data, just make up some 426043840dfSDag-Erling Smørgrav * data. The forwarding code will check the validity of the 427043840dfSDag-Erling Smørgrav * response anyway, and substitute this data. The X11 428043840dfSDag-Erling Smørgrav * server, however, will ignore this fake data and use 429043840dfSDag-Erling Smørgrav * whatever authentication mechanisms it was using otherwise 430043840dfSDag-Erling Smørgrav * for the local connection. 431043840dfSDag-Erling Smørgrav */ 432043840dfSDag-Erling Smørgrav if (!got_data) { 433ca86bcf2SDag-Erling Smørgrav u_int8_t rnd[16]; 434ca86bcf2SDag-Erling Smørgrav u_int i; 435043840dfSDag-Erling Smørgrav 436043840dfSDag-Erling Smørgrav logit("Warning: No xauth data; " 437043840dfSDag-Erling Smørgrav "using fake authentication data for X11 forwarding."); 438043840dfSDag-Erling Smørgrav strlcpy(proto, SSH_X11_PROTO, sizeof proto); 439ca86bcf2SDag-Erling Smørgrav arc4random_buf(rnd, sizeof(rnd)); 440ca86bcf2SDag-Erling Smørgrav for (i = 0; i < sizeof(rnd); i++) { 441043840dfSDag-Erling Smørgrav snprintf(data + 2 * i, sizeof data - 2 * i, "%02x", 442ca86bcf2SDag-Erling Smørgrav rnd[i]); 443043840dfSDag-Erling Smørgrav } 444043840dfSDag-Erling Smørgrav } 445acc1a9efSDag-Erling Smørgrav 446acc1a9efSDag-Erling Smørgrav return 0; 447043840dfSDag-Erling Smørgrav } 448043840dfSDag-Erling Smørgrav 449511b41d2SMark Murray /* 450511b41d2SMark Murray * Checks if the client window has changed, and sends a packet about it to 451511b41d2SMark Murray * the server if so. The actual change is detected elsewhere (by a software 452511b41d2SMark Murray * interrupt on Unix); this just checks the flag and sends a message if 453511b41d2SMark Murray * appropriate. 454511b41d2SMark Murray */ 455511b41d2SMark Murray 456ae1f160dSDag-Erling Smørgrav static void 4574f52dfbbSDag-Erling Smørgrav client_check_window_change(struct ssh *ssh) 458511b41d2SMark Murray { 459a04a10f8SKris Kennaway if (!received_window_change_signal) 460a04a10f8SKris Kennaway return; 461511b41d2SMark Murray received_window_change_signal = 0; 46219261079SEd Maste debug2_f("changed"); 4634f52dfbbSDag-Erling Smørgrav channel_send_window_changes(ssh); 464511b41d2SMark Murray } 465511b41d2SMark Murray 466bc5531deSDag-Erling Smørgrav static int 4674f52dfbbSDag-Erling Smørgrav client_global_request_reply(int type, u_int32_t seq, struct ssh *ssh) 468efcad6b7SDag-Erling Smørgrav { 469d4af9e69SDag-Erling Smørgrav struct global_confirm *gc; 470d4af9e69SDag-Erling Smørgrav 471d4af9e69SDag-Erling Smørgrav if ((gc = TAILQ_FIRST(&global_confirms)) == NULL) 472bc5531deSDag-Erling Smørgrav return 0; 473d4af9e69SDag-Erling Smørgrav if (gc->cb != NULL) 4744f52dfbbSDag-Erling Smørgrav gc->cb(ssh, type, seq, gc->ctx); 475d4af9e69SDag-Erling Smørgrav if (--gc->ref_count <= 0) { 476d4af9e69SDag-Erling Smørgrav TAILQ_REMOVE(&global_confirms, gc, entry); 47719261079SEd Maste freezero(gc, sizeof(*gc)); 478d4af9e69SDag-Erling Smørgrav } 479d4af9e69SDag-Erling Smørgrav 48019261079SEd Maste ssh_packet_set_alive_timeouts(ssh, 0); 481bc5531deSDag-Erling Smørgrav return 0; 482efcad6b7SDag-Erling Smørgrav } 483efcad6b7SDag-Erling Smørgrav 484efcad6b7SDag-Erling Smørgrav static void 48519261079SEd Maste schedule_server_alive_check(void) 486efcad6b7SDag-Erling Smørgrav { 48719261079SEd Maste if (options.server_alive_interval > 0) 48819261079SEd Maste server_alive_time = monotime() + options.server_alive_interval; 48919261079SEd Maste } 49019261079SEd Maste 49119261079SEd Maste static void 49219261079SEd Maste server_alive_check(struct ssh *ssh) 49319261079SEd Maste { 49419261079SEd Maste int r; 49519261079SEd Maste 49619261079SEd Maste if (ssh_packet_inc_alive_timeouts(ssh) > options.server_alive_count_max) { 4974a421b63SDag-Erling Smørgrav logit("Timeout, server %s not responding.", host); 49892eb0aa1SDag-Erling Smørgrav cleanup_exit(255); 49992eb0aa1SDag-Erling Smørgrav } 50019261079SEd Maste if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || 50119261079SEd Maste (r = sshpkt_put_cstring(ssh, "keepalive@openssh.com")) != 0 || 50219261079SEd Maste (r = sshpkt_put_u8(ssh, 1)) != 0 || /* boolean: want reply */ 50319261079SEd Maste (r = sshpkt_send(ssh)) != 0) 50419261079SEd Maste fatal_fr(r, "send packet"); 505d4af9e69SDag-Erling Smørgrav /* Insert an empty placeholder to maintain ordering */ 506d4af9e69SDag-Erling Smørgrav client_register_global_confirm(NULL, NULL); 50719261079SEd Maste schedule_server_alive_check(); 508efcad6b7SDag-Erling Smørgrav } 509efcad6b7SDag-Erling Smørgrav 510511b41d2SMark Murray /* 511511b41d2SMark Murray * Waits until the client can do something (some data becomes available on 512511b41d2SMark Murray * one of the file descriptors). 513511b41d2SMark Murray */ 514ae1f160dSDag-Erling Smørgrav static void 5151323ec57SEd Maste client_wait_until_can_do_something(struct ssh *ssh, struct pollfd **pfdp, 5161323ec57SEd Maste u_int *npfd_allocp, u_int *npfd_activep, int rekeying, 5171323ec57SEd Maste int *conn_in_readyp, int *conn_out_readyp) 518511b41d2SMark Murray { 5191323ec57SEd Maste int timeout_secs, pollwait; 52019261079SEd Maste time_t minwait_secs = 0, now = monotime(); 5211323ec57SEd Maste int ret; 5221323ec57SEd Maste u_int p; 523efcad6b7SDag-Erling Smørgrav 5241323ec57SEd Maste *conn_in_readyp = *conn_out_readyp = 0; 525511b41d2SMark Murray 5261323ec57SEd Maste /* Prepare channel poll. First two pollfd entries are reserved */ 5271323ec57SEd Maste channel_prepare_poll(ssh, pfdp, npfd_allocp, npfd_activep, 2, 5281323ec57SEd Maste &minwait_secs); 5291323ec57SEd Maste if (*npfd_activep < 2) 5301323ec57SEd Maste fatal_f("bad npfd %u", *npfd_activep); /* shouldn't happen */ 5311323ec57SEd Maste 5321323ec57SEd Maste /* channel_prepare_poll could have closed the last channel */ 5334f52dfbbSDag-Erling Smørgrav if (session_closed && !channel_still_open(ssh) && 53419261079SEd Maste !ssh_packet_have_data_to_write(ssh)) { 5351323ec57SEd Maste /* clear events since we did not call poll() */ 5361323ec57SEd Maste for (p = 0; p < *npfd_activep; p++) 5371323ec57SEd Maste (*pfdp)[p].revents = 0; 538ae1f160dSDag-Erling Smørgrav return; 5394f52dfbbSDag-Erling Smørgrav } 5404f52dfbbSDag-Erling Smørgrav 5411323ec57SEd Maste /* Monitor server connection on reserved pollfd entries */ 5421323ec57SEd Maste (*pfdp)[0].fd = connection_in; 5431323ec57SEd Maste (*pfdp)[0].events = POLLIN; 5441323ec57SEd Maste (*pfdp)[1].fd = connection_out; 5451323ec57SEd Maste (*pfdp)[1].events = ssh_packet_have_data_to_write(ssh) ? POLLOUT : 0; 546511b41d2SMark Murray 547511b41d2SMark Murray /* 548511b41d2SMark Murray * Wait for something to happen. This will suspend the process until 5491323ec57SEd Maste * some polled descriptor can be read, written, or has some other 550e2f6069cSDag-Erling Smørgrav * event pending, or a timeout expires. 551511b41d2SMark Murray */ 552511b41d2SMark Murray 553e2f6069cSDag-Erling Smørgrav timeout_secs = INT_MAX; /* we use INT_MAX to mean no timeout */ 55419261079SEd Maste if (options.server_alive_interval > 0) 55519261079SEd Maste timeout_secs = MAXIMUM(server_alive_time - now, 0); 5564f52dfbbSDag-Erling Smørgrav if (options.rekey_interval > 0 && !rekeying) 55719261079SEd Maste timeout_secs = MINIMUM(timeout_secs, 55819261079SEd Maste ssh_packet_get_rekey_timeout(ssh)); 5594f52dfbbSDag-Erling Smørgrav set_control_persist_exit_time(ssh); 560e2f6069cSDag-Erling Smørgrav if (control_persist_exit_time > 0) { 561ca86bcf2SDag-Erling Smørgrav timeout_secs = MINIMUM(timeout_secs, 562e4a9863fSDag-Erling Smørgrav control_persist_exit_time - now); 563e2f6069cSDag-Erling Smørgrav if (timeout_secs < 0) 564e2f6069cSDag-Erling Smørgrav timeout_secs = 0; 565e2f6069cSDag-Erling Smørgrav } 566462c32cbSDag-Erling Smørgrav if (minwait_secs != 0) 567ca86bcf2SDag-Erling Smørgrav timeout_secs = MINIMUM(timeout_secs, (int)minwait_secs); 568e2f6069cSDag-Erling Smørgrav if (timeout_secs == INT_MAX) 5691323ec57SEd Maste pollwait = -1; 5701323ec57SEd Maste else if (timeout_secs >= INT_MAX / 1000) 5711323ec57SEd Maste pollwait = INT_MAX; 5721323ec57SEd Maste else 5731323ec57SEd Maste pollwait = timeout_secs * 1000; 574e2f6069cSDag-Erling Smørgrav 5751323ec57SEd Maste ret = poll(*pfdp, *npfd_activep, pollwait); 5761323ec57SEd Maste 57719261079SEd Maste if (ret == -1) { 5781e8db6e2SBrian Feldman /* 5791323ec57SEd Maste * We have to clear the events because we return. 5801e8db6e2SBrian Feldman * We have to return, because the mainloop checks for the flags 5811e8db6e2SBrian Feldman * set by the signal handlers. 5821e8db6e2SBrian Feldman */ 5831323ec57SEd Maste for (p = 0; p < *npfd_activep; p++) 5841323ec57SEd Maste (*pfdp)[p].revents = 0; 585511b41d2SMark Murray if (errno == EINTR) 586511b41d2SMark Murray return; 587511b41d2SMark Murray /* Note: we might still have data in the buffers. */ 5881323ec57SEd Maste quit_message("poll: %s", strerror(errno)); 5891323ec57SEd Maste return; 5901323ec57SEd Maste } 5911323ec57SEd Maste 5921323ec57SEd Maste *conn_in_readyp = (*pfdp)[0].revents != 0; 5931323ec57SEd Maste *conn_out_readyp = (*pfdp)[1].revents != 0; 5941323ec57SEd Maste 5951323ec57SEd Maste if (options.server_alive_interval > 0 && !*conn_in_readyp && 5961323ec57SEd Maste monotime() >= server_alive_time) { 597e4a9863fSDag-Erling Smørgrav /* 5981323ec57SEd Maste * ServerAlive check is needed. We can't rely on the poll 59919261079SEd Maste * timing out since traffic on the client side such as port 60019261079SEd Maste * forwards can keep waking it up. 601e4a9863fSDag-Erling Smørgrav */ 60219261079SEd Maste server_alive_check(ssh); 603e4a9863fSDag-Erling Smørgrav } 6041323ec57SEd Maste } 605e4a9863fSDag-Erling Smørgrav 606ae1f160dSDag-Erling Smørgrav static void 607190cef3dSDag-Erling Smørgrav client_suspend_self(struct sshbuf *bin, struct sshbuf *bout, struct sshbuf *berr) 608511b41d2SMark Murray { 609511b41d2SMark Murray /* Flush stdout and stderr buffers. */ 610190cef3dSDag-Erling Smørgrav if (sshbuf_len(bout) > 0) 611190cef3dSDag-Erling Smørgrav atomicio(vwrite, fileno(stdout), sshbuf_mutable_ptr(bout), 612190cef3dSDag-Erling Smørgrav sshbuf_len(bout)); 613190cef3dSDag-Erling Smørgrav if (sshbuf_len(berr) > 0) 614190cef3dSDag-Erling Smørgrav atomicio(vwrite, fileno(stderr), sshbuf_mutable_ptr(berr), 615190cef3dSDag-Erling Smørgrav sshbuf_len(berr)); 616511b41d2SMark Murray 617e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 618511b41d2SMark Murray 6194f52dfbbSDag-Erling Smørgrav sshbuf_reset(bin); 6204f52dfbbSDag-Erling Smørgrav sshbuf_reset(bout); 6214f52dfbbSDag-Erling Smørgrav sshbuf_reset(berr); 622511b41d2SMark Murray 623511b41d2SMark Murray /* Send the suspend signal to the program itself. */ 624511b41d2SMark Murray kill(getpid(), SIGTSTP); 625511b41d2SMark Murray 6265e8dbd04SDag-Erling Smørgrav /* Reset window sizes in case they have changed */ 627511b41d2SMark Murray received_window_change_signal = 1; 628511b41d2SMark Murray 629e146993eSDag-Erling Smørgrav enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 630511b41d2SMark Murray } 631511b41d2SMark Murray 632ae1f160dSDag-Erling Smørgrav static void 6331323ec57SEd Maste client_process_net_input(struct ssh *ssh) 634511b41d2SMark Murray { 6351323ec57SEd Maste int r; 636511b41d2SMark Murray 637511b41d2SMark Murray /* 638511b41d2SMark Murray * Read input from the server, and add any such data to the buffer of 639511b41d2SMark Murray * the packet subsystem. 640511b41d2SMark Murray */ 64119261079SEd Maste schedule_server_alive_check(); 6421323ec57SEd Maste if ((r = ssh_packet_process_read(ssh, connection_in)) == 0) 6431323ec57SEd Maste return; /* success */ 6441323ec57SEd Maste if (r == SSH_ERR_SYSTEM_ERROR) { 6451323ec57SEd Maste if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK) 6461323ec57SEd Maste return; 6471323ec57SEd Maste if (errno == EPIPE) { 6481323ec57SEd Maste quit_message("Connection to %s closed by remote host.", 6491323ec57SEd Maste host); 650511b41d2SMark Murray return; 651511b41d2SMark Murray } 652511b41d2SMark Murray } 6531323ec57SEd Maste quit_message("Read from remote host %s: %s", host, ssh_err(r)); 654a04a10f8SKris Kennaway } 655a04a10f8SKris Kennaway 656545d5ecaSDag-Erling Smørgrav static void 6574f52dfbbSDag-Erling Smørgrav client_status_confirm(struct ssh *ssh, int type, Channel *c, void *ctx) 658d74d50a8SDag-Erling Smørgrav { 659d4af9e69SDag-Erling Smørgrav struct channel_reply_ctx *cr = (struct channel_reply_ctx *)ctx; 660d4af9e69SDag-Erling Smørgrav char errmsg[256]; 661190cef3dSDag-Erling Smørgrav int r, tochan; 662d74d50a8SDag-Erling Smørgrav 663e146993eSDag-Erling Smørgrav /* 664e146993eSDag-Erling Smørgrav * If a TTY was explicitly requested, then a failure to allocate 665e146993eSDag-Erling Smørgrav * one is fatal. 666e146993eSDag-Erling Smørgrav */ 667e146993eSDag-Erling Smørgrav if (cr->action == CONFIRM_TTY && 668e146993eSDag-Erling Smørgrav (options.request_tty == REQUEST_TTY_FORCE || 669e146993eSDag-Erling Smørgrav options.request_tty == REQUEST_TTY_YES)) 670e146993eSDag-Erling Smørgrav cr->action = CONFIRM_CLOSE; 671e146993eSDag-Erling Smørgrav 672190cef3dSDag-Erling Smørgrav /* XXX suppress on mux _client_ quietmode */ 673d4af9e69SDag-Erling Smørgrav tochan = options.log_level >= SYSLOG_LEVEL_ERROR && 674b15c8340SDag-Erling Smørgrav c->ctl_chan != -1 && c->extended_usage == CHAN_EXTENDED_WRITE; 675d74d50a8SDag-Erling Smørgrav 676d4af9e69SDag-Erling Smørgrav if (type == SSH2_MSG_CHANNEL_SUCCESS) { 677d4af9e69SDag-Erling Smørgrav debug2("%s request accepted on channel %d", 678d4af9e69SDag-Erling Smørgrav cr->request_type, c->self); 679d4af9e69SDag-Erling Smørgrav } else if (type == SSH2_MSG_CHANNEL_FAILURE) { 680d4af9e69SDag-Erling Smørgrav if (tochan) { 681d4af9e69SDag-Erling Smørgrav snprintf(errmsg, sizeof(errmsg), 682d4af9e69SDag-Erling Smørgrav "%s request failed\r\n", cr->request_type); 683d4af9e69SDag-Erling Smørgrav } else { 684d4af9e69SDag-Erling Smørgrav snprintf(errmsg, sizeof(errmsg), 685d4af9e69SDag-Erling Smørgrav "%s request failed on channel %d", 686d4af9e69SDag-Erling Smørgrav cr->request_type, c->self); 687d74d50a8SDag-Erling Smørgrav } 688d4af9e69SDag-Erling Smørgrav /* If error occurred on primary session channel, then exit */ 689e146993eSDag-Erling Smørgrav if (cr->action == CONFIRM_CLOSE && c->self == session_ident) 690d4af9e69SDag-Erling Smørgrav fatal("%s", errmsg); 691e146993eSDag-Erling Smørgrav /* 692e146993eSDag-Erling Smørgrav * If error occurred on mux client, append to 693e146993eSDag-Erling Smørgrav * their stderr. 694e146993eSDag-Erling Smørgrav */ 695e146993eSDag-Erling Smørgrav if (tochan) { 69619261079SEd Maste debug3_f("channel %d: mux request: %s", c->self, 69719261079SEd Maste cr->request_type); 698190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put(c->extended, errmsg, 699190cef3dSDag-Erling Smørgrav strlen(errmsg))) != 0) 70019261079SEd Maste fatal_fr(r, "sshbuf_put"); 701e146993eSDag-Erling Smørgrav } else 702d4af9e69SDag-Erling Smørgrav error("%s", errmsg); 703e146993eSDag-Erling Smørgrav if (cr->action == CONFIRM_TTY) { 704e146993eSDag-Erling Smørgrav /* 705e146993eSDag-Erling Smørgrav * If a TTY allocation error occurred, then arrange 706e146993eSDag-Erling Smørgrav * for the correct TTY to leave raw mode. 707e146993eSDag-Erling Smørgrav */ 708e146993eSDag-Erling Smørgrav if (c->self == session_ident) 709e146993eSDag-Erling Smørgrav leave_raw_mode(0); 710e146993eSDag-Erling Smørgrav else 7114f52dfbbSDag-Erling Smørgrav mux_tty_alloc_failed(ssh, c); 712e146993eSDag-Erling Smørgrav } else if (cr->action == CONFIRM_CLOSE) { 7134f52dfbbSDag-Erling Smørgrav chan_read_failed(ssh, c); 7144f52dfbbSDag-Erling Smørgrav chan_write_failed(ssh, c); 715d74d50a8SDag-Erling Smørgrav } 716d74d50a8SDag-Erling Smørgrav } 717e4a9863fSDag-Erling Smørgrav free(cr); 718d4af9e69SDag-Erling Smørgrav } 719d74d50a8SDag-Erling Smørgrav 720d74d50a8SDag-Erling Smørgrav static void 7214f52dfbbSDag-Erling Smørgrav client_abandon_status_confirm(struct ssh *ssh, Channel *c, void *ctx) 722d74d50a8SDag-Erling Smørgrav { 723e4a9863fSDag-Erling Smørgrav free(ctx); 724d74d50a8SDag-Erling Smørgrav } 725d74d50a8SDag-Erling Smørgrav 726e146993eSDag-Erling Smørgrav void 7274f52dfbbSDag-Erling Smørgrav client_expect_confirm(struct ssh *ssh, int id, const char *request, 728e146993eSDag-Erling Smørgrav enum confirm_action action) 729d74d50a8SDag-Erling Smørgrav { 7300a37d4a3SXin LI struct channel_reply_ctx *cr = xcalloc(1, sizeof(*cr)); 731d74d50a8SDag-Erling Smørgrav 732d4af9e69SDag-Erling Smørgrav cr->request_type = request; 733e146993eSDag-Erling Smørgrav cr->action = action; 734d74d50a8SDag-Erling Smørgrav 7354f52dfbbSDag-Erling Smørgrav channel_register_status_confirm(ssh, id, client_status_confirm, 736d4af9e69SDag-Erling Smørgrav client_abandon_status_confirm, cr); 737d4af9e69SDag-Erling Smørgrav } 738d4af9e69SDag-Erling Smørgrav 739d4af9e69SDag-Erling Smørgrav void 740d4af9e69SDag-Erling Smørgrav client_register_global_confirm(global_confirm_cb *cb, void *ctx) 741d4af9e69SDag-Erling Smørgrav { 742d4af9e69SDag-Erling Smørgrav struct global_confirm *gc, *last_gc; 743d4af9e69SDag-Erling Smørgrav 744d4af9e69SDag-Erling Smørgrav /* Coalesce identical callbacks */ 745d4af9e69SDag-Erling Smørgrav last_gc = TAILQ_LAST(&global_confirms, global_confirms); 746d4af9e69SDag-Erling Smørgrav if (last_gc && last_gc->cb == cb && last_gc->ctx == ctx) { 747d4af9e69SDag-Erling Smørgrav if (++last_gc->ref_count >= INT_MAX) 74819261079SEd Maste fatal_f("last_gc->ref_count = %d", 74919261079SEd Maste last_gc->ref_count); 750d74d50a8SDag-Erling Smørgrav return; 751d74d50a8SDag-Erling Smørgrav } 752d74d50a8SDag-Erling Smørgrav 7530a37d4a3SXin LI gc = xcalloc(1, sizeof(*gc)); 754d4af9e69SDag-Erling Smørgrav gc->cb = cb; 755d4af9e69SDag-Erling Smørgrav gc->ctx = ctx; 756d4af9e69SDag-Erling Smørgrav gc->ref_count = 1; 757d4af9e69SDag-Erling Smørgrav TAILQ_INSERT_TAIL(&global_confirms, gc, entry); 758d74d50a8SDag-Erling Smørgrav } 759d74d50a8SDag-Erling Smørgrav 760d74d50a8SDag-Erling Smørgrav static void 7614f52dfbbSDag-Erling Smørgrav process_cmdline(struct ssh *ssh) 762545d5ecaSDag-Erling Smørgrav { 763545d5ecaSDag-Erling Smørgrav void (*handler)(int); 764a0ee8cc6SDag-Erling Smørgrav char *s, *cmd; 765a0ee8cc6SDag-Erling Smørgrav int ok, delete = 0, local = 0, remote = 0, dynamic = 0; 766a0ee8cc6SDag-Erling Smørgrav struct Forward fwd; 767545d5ecaSDag-Erling Smørgrav 768b83788ffSDag-Erling Smørgrav memset(&fwd, 0, sizeof(fwd)); 769d4af9e69SDag-Erling Smørgrav 770e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 77119261079SEd Maste handler = ssh_signal(SIGINT, SIG_IGN); 772545d5ecaSDag-Erling Smørgrav cmd = s = read_passphrase("\r\nssh> ", RP_ECHO); 773545d5ecaSDag-Erling Smørgrav if (s == NULL) 774545d5ecaSDag-Erling Smørgrav goto out; 775f7167e0eSDag-Erling Smørgrav while (isspace((u_char)*s)) 776545d5ecaSDag-Erling Smørgrav s++; 777d74d50a8SDag-Erling Smørgrav if (*s == '-') 778d74d50a8SDag-Erling Smørgrav s++; /* Skip cmdline '-', if any */ 779d74d50a8SDag-Erling Smørgrav if (*s == '\0') 780545d5ecaSDag-Erling Smørgrav goto out; 781d74d50a8SDag-Erling Smørgrav 782d74d50a8SDag-Erling Smørgrav if (*s == 'h' || *s == 'H' || *s == '?') { 783d74d50a8SDag-Erling Smørgrav logit("Commands:"); 784761efaa7SDag-Erling Smørgrav logit(" -L[bind_address:]port:host:hostport " 785761efaa7SDag-Erling Smørgrav "Request local forward"); 786761efaa7SDag-Erling Smørgrav logit(" -R[bind_address:]port:host:hostport " 787761efaa7SDag-Erling Smørgrav "Request remote forward"); 788cce7d346SDag-Erling Smørgrav logit(" -D[bind_address:]port " 789cce7d346SDag-Erling Smørgrav "Request dynamic forward"); 790462c32cbSDag-Erling Smørgrav logit(" -KL[bind_address:]port " 791462c32cbSDag-Erling Smørgrav "Cancel local forward"); 792761efaa7SDag-Erling Smørgrav logit(" -KR[bind_address:]port " 793761efaa7SDag-Erling Smørgrav "Cancel remote forward"); 794462c32cbSDag-Erling Smørgrav logit(" -KD[bind_address:]port " 795462c32cbSDag-Erling Smørgrav "Cancel dynamic forward"); 796021d409fSDag-Erling Smørgrav if (!options.permit_local_command) 797021d409fSDag-Erling Smørgrav goto out; 798761efaa7SDag-Erling Smørgrav logit(" !args " 799761efaa7SDag-Erling Smørgrav "Execute local command"); 800021d409fSDag-Erling Smørgrav goto out; 801021d409fSDag-Erling Smørgrav } 802021d409fSDag-Erling Smørgrav 803021d409fSDag-Erling Smørgrav if (*s == '!' && options.permit_local_command) { 804021d409fSDag-Erling Smørgrav s++; 805021d409fSDag-Erling Smørgrav ssh_local_cmd(s); 806d74d50a8SDag-Erling Smørgrav goto out; 807d74d50a8SDag-Erling Smørgrav } 808d74d50a8SDag-Erling Smørgrav 809d74d50a8SDag-Erling Smørgrav if (*s == 'K') { 810d74d50a8SDag-Erling Smørgrav delete = 1; 811d74d50a8SDag-Erling Smørgrav s++; 812d74d50a8SDag-Erling Smørgrav } 813cce7d346SDag-Erling Smørgrav if (*s == 'L') 814cce7d346SDag-Erling Smørgrav local = 1; 815cce7d346SDag-Erling Smørgrav else if (*s == 'R') 816cce7d346SDag-Erling Smørgrav remote = 1; 817cce7d346SDag-Erling Smørgrav else if (*s == 'D') 818cce7d346SDag-Erling Smørgrav dynamic = 1; 819cce7d346SDag-Erling Smørgrav else { 820d95e11bfSDag-Erling Smørgrav logit("Invalid command."); 821545d5ecaSDag-Erling Smørgrav goto out; 822545d5ecaSDag-Erling Smørgrav } 823cce7d346SDag-Erling Smørgrav 824f7167e0eSDag-Erling Smørgrav while (isspace((u_char)*++s)) 825d4af9e69SDag-Erling Smørgrav ; 826545d5ecaSDag-Erling Smørgrav 827b15c8340SDag-Erling Smørgrav /* XXX update list of forwards in options */ 828d74d50a8SDag-Erling Smørgrav if (delete) { 829a0ee8cc6SDag-Erling Smørgrav /* We pass 1 for dynamicfwd to restrict to 1 or 2 fields. */ 830a0ee8cc6SDag-Erling Smørgrav if (!parse_forward(&fwd, s, 1, 0)) { 831a0ee8cc6SDag-Erling Smørgrav logit("Bad forwarding close specification."); 832545d5ecaSDag-Erling Smørgrav goto out; 833545d5ecaSDag-Erling Smørgrav } 834462c32cbSDag-Erling Smørgrav if (remote) 8354f52dfbbSDag-Erling Smørgrav ok = channel_request_rforward_cancel(ssh, &fwd) == 0; 836462c32cbSDag-Erling Smørgrav else if (dynamic) 8374f52dfbbSDag-Erling Smørgrav ok = channel_cancel_lport_listener(ssh, &fwd, 838a0ee8cc6SDag-Erling Smørgrav 0, &options.fwd_opts) > 0; 839462c32cbSDag-Erling Smørgrav else 8404f52dfbbSDag-Erling Smørgrav ok = channel_cancel_lport_listener(ssh, &fwd, 841a0ee8cc6SDag-Erling Smørgrav CHANNEL_CANCEL_PORT_STATIC, 842a0ee8cc6SDag-Erling Smørgrav &options.fwd_opts) > 0; 843462c32cbSDag-Erling Smørgrav if (!ok) { 844d93a896eSDag-Erling Smørgrav logit("Unknown port forwarding."); 845462c32cbSDag-Erling Smørgrav goto out; 846462c32cbSDag-Erling Smørgrav } 847462c32cbSDag-Erling Smørgrav logit("Canceled forwarding."); 8485e8dbd04SDag-Erling Smørgrav } else { 849cce7d346SDag-Erling Smørgrav if (!parse_forward(&fwd, s, dynamic, remote)) { 8505e8dbd04SDag-Erling Smørgrav logit("Bad forwarding specification."); 851545d5ecaSDag-Erling Smørgrav goto out; 852545d5ecaSDag-Erling Smørgrav } 853cce7d346SDag-Erling Smørgrav if (local || dynamic) { 8544f52dfbbSDag-Erling Smørgrav if (!channel_setup_local_fwd_listener(ssh, &fwd, 855a0ee8cc6SDag-Erling Smørgrav &options.fwd_opts)) { 856d95e11bfSDag-Erling Smørgrav logit("Port forwarding failed."); 857545d5ecaSDag-Erling Smørgrav goto out; 858545d5ecaSDag-Erling Smørgrav } 8595e8dbd04SDag-Erling Smørgrav } else { 8604f52dfbbSDag-Erling Smørgrav if (channel_request_remote_forwarding(ssh, &fwd) < 0) { 861761efaa7SDag-Erling Smørgrav logit("Port forwarding failed."); 862761efaa7SDag-Erling Smørgrav goto out; 863761efaa7SDag-Erling Smørgrav } 8645e8dbd04SDag-Erling Smørgrav } 865d95e11bfSDag-Erling Smørgrav logit("Forwarding port."); 866d74d50a8SDag-Erling Smørgrav } 867d74d50a8SDag-Erling Smørgrav 868545d5ecaSDag-Erling Smørgrav out: 86919261079SEd Maste ssh_signal(SIGINT, handler); 870e146993eSDag-Erling Smørgrav enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 871e4a9863fSDag-Erling Smørgrav free(cmd); 872e4a9863fSDag-Erling Smørgrav free(fwd.listen_host); 873a0ee8cc6SDag-Erling Smørgrav free(fwd.listen_path); 874e4a9863fSDag-Erling Smørgrav free(fwd.connect_host); 875a0ee8cc6SDag-Erling Smørgrav free(fwd.connect_path); 876545d5ecaSDag-Erling Smørgrav } 877545d5ecaSDag-Erling Smørgrav 8786888a9beSDag-Erling Smørgrav /* reasons to suppress output of an escape command in help output */ 8796888a9beSDag-Erling Smørgrav #define SUPPRESS_NEVER 0 /* never suppress, always show */ 8804f52dfbbSDag-Erling Smørgrav #define SUPPRESS_MUXCLIENT 1 /* don't show in mux client sessions */ 8814f52dfbbSDag-Erling Smørgrav #define SUPPRESS_MUXMASTER 2 /* don't show in mux master sessions */ 8824f52dfbbSDag-Erling Smørgrav #define SUPPRESS_SYSLOG 4 /* don't show when logging to syslog */ 8836888a9beSDag-Erling Smørgrav struct escape_help_text { 8846888a9beSDag-Erling Smørgrav const char *cmd; 8856888a9beSDag-Erling Smørgrav const char *text; 8866888a9beSDag-Erling Smørgrav unsigned int flags; 8876888a9beSDag-Erling Smørgrav }; 8886888a9beSDag-Erling Smørgrav static struct escape_help_text esc_txt[] = { 8896888a9beSDag-Erling Smørgrav {".", "terminate session", SUPPRESS_MUXMASTER}, 8906888a9beSDag-Erling Smørgrav {".", "terminate connection (and any multiplexed sessions)", 8916888a9beSDag-Erling Smørgrav SUPPRESS_MUXCLIENT}, 8924f52dfbbSDag-Erling Smørgrav {"B", "send a BREAK to the remote system", SUPPRESS_NEVER}, 8936888a9beSDag-Erling Smørgrav {"C", "open a command line", SUPPRESS_MUXCLIENT}, 8944f52dfbbSDag-Erling Smørgrav {"R", "request rekey", SUPPRESS_NEVER}, 8956888a9beSDag-Erling Smørgrav {"V/v", "decrease/increase verbosity (LogLevel)", SUPPRESS_MUXCLIENT}, 8966888a9beSDag-Erling Smørgrav {"^Z", "suspend ssh", SUPPRESS_MUXCLIENT}, 8976888a9beSDag-Erling Smørgrav {"#", "list forwarded connections", SUPPRESS_NEVER}, 8986888a9beSDag-Erling Smørgrav {"&", "background ssh (when waiting for connections to terminate)", 8996888a9beSDag-Erling Smørgrav SUPPRESS_MUXCLIENT}, 9006888a9beSDag-Erling Smørgrav {"?", "this message", SUPPRESS_NEVER}, 9016888a9beSDag-Erling Smørgrav }; 9026888a9beSDag-Erling Smørgrav 9036888a9beSDag-Erling Smørgrav static void 904190cef3dSDag-Erling Smørgrav print_escape_help(struct sshbuf *b, int escape_char, int mux_client, 905190cef3dSDag-Erling Smørgrav int using_stderr) 9066888a9beSDag-Erling Smørgrav { 9076888a9beSDag-Erling Smørgrav unsigned int i, suppress_flags; 908190cef3dSDag-Erling Smørgrav int r; 9096888a9beSDag-Erling Smørgrav 910190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(b, 911190cef3dSDag-Erling Smørgrav "%c?\r\nSupported escape sequences:\r\n", escape_char)) != 0) 91219261079SEd Maste fatal_fr(r, "sshbuf_putf"); 9136888a9beSDag-Erling Smørgrav 9144f52dfbbSDag-Erling Smørgrav suppress_flags = 9156888a9beSDag-Erling Smørgrav (mux_client ? SUPPRESS_MUXCLIENT : 0) | 9166888a9beSDag-Erling Smørgrav (mux_client ? 0 : SUPPRESS_MUXMASTER) | 9176888a9beSDag-Erling Smørgrav (using_stderr ? 0 : SUPPRESS_SYSLOG); 9186888a9beSDag-Erling Smørgrav 9196888a9beSDag-Erling Smørgrav for (i = 0; i < sizeof(esc_txt)/sizeof(esc_txt[0]); i++) { 9206888a9beSDag-Erling Smørgrav if (esc_txt[i].flags & suppress_flags) 9216888a9beSDag-Erling Smørgrav continue; 922190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(b, " %c%-3s - %s\r\n", 923190cef3dSDag-Erling Smørgrav escape_char, esc_txt[i].cmd, esc_txt[i].text)) != 0) 92419261079SEd Maste fatal_fr(r, "sshbuf_putf"); 9256888a9beSDag-Erling Smørgrav } 9266888a9beSDag-Erling Smørgrav 927190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(b, 9286888a9beSDag-Erling Smørgrav " %c%c - send the escape character by typing it twice\r\n" 9296888a9beSDag-Erling Smørgrav "(Note that escapes are only recognized immediately after " 930190cef3dSDag-Erling Smørgrav "newline.)\r\n", escape_char, escape_char)) != 0) 93119261079SEd Maste fatal_fr(r, "sshbuf_putf"); 9326888a9beSDag-Erling Smørgrav } 9336888a9beSDag-Erling Smørgrav 934d4af9e69SDag-Erling Smørgrav /* 9354f52dfbbSDag-Erling Smørgrav * Process the characters one by one. 936d4af9e69SDag-Erling Smørgrav */ 937ae1f160dSDag-Erling Smørgrav static int 9384f52dfbbSDag-Erling Smørgrav process_escapes(struct ssh *ssh, Channel *c, 939190cef3dSDag-Erling Smørgrav struct sshbuf *bin, struct sshbuf *bout, struct sshbuf *berr, 940d4af9e69SDag-Erling Smørgrav char *buf, int len) 941b66f2d16SKris Kennaway { 942b66f2d16SKris Kennaway pid_t pid; 943190cef3dSDag-Erling Smørgrav int r, bytes = 0; 9441e8db6e2SBrian Feldman u_int i; 9451e8db6e2SBrian Feldman u_char ch; 946b66f2d16SKris Kennaway char *s; 9474f52dfbbSDag-Erling Smørgrav struct escape_filter_ctx *efc = c->filter_ctx == NULL ? 9484f52dfbbSDag-Erling Smørgrav NULL : (struct escape_filter_ctx *)c->filter_ctx; 949d4af9e69SDag-Erling Smørgrav 950d4af9e69SDag-Erling Smørgrav if (c->filter_ctx == NULL) 951d4af9e69SDag-Erling Smørgrav return 0; 952b66f2d16SKris Kennaway 953043840dfSDag-Erling Smørgrav if (len <= 0) 954043840dfSDag-Erling Smørgrav return (0); 955043840dfSDag-Erling Smørgrav 956043840dfSDag-Erling Smørgrav for (i = 0; i < (u_int)len; i++) { 957b66f2d16SKris Kennaway /* Get one character at a time. */ 958b66f2d16SKris Kennaway ch = buf[i]; 959b66f2d16SKris Kennaway 9604f52dfbbSDag-Erling Smørgrav if (efc->escape_pending) { 961b66f2d16SKris Kennaway /* We have previously seen an escape character. */ 962b66f2d16SKris Kennaway /* Clear the flag now. */ 9634f52dfbbSDag-Erling Smørgrav efc->escape_pending = 0; 964b66f2d16SKris Kennaway 965b66f2d16SKris Kennaway /* Process the escaped character. */ 966b66f2d16SKris Kennaway switch (ch) { 967b66f2d16SKris Kennaway case '.': 968b66f2d16SKris Kennaway /* Terminate the connection. */ 969190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(berr, "%c.\r\n", 970190cef3dSDag-Erling Smørgrav efc->escape_char)) != 0) 97119261079SEd Maste fatal_fr(r, "sshbuf_putf"); 972b15c8340SDag-Erling Smørgrav if (c && c->ctl_chan != -1) { 9734f52dfbbSDag-Erling Smørgrav chan_read_failed(ssh, c); 9744f52dfbbSDag-Erling Smørgrav chan_write_failed(ssh, c); 9754f52dfbbSDag-Erling Smørgrav if (c->detach_user) { 9764f52dfbbSDag-Erling Smørgrav c->detach_user(ssh, 9774f52dfbbSDag-Erling Smørgrav c->self, NULL); 9784f52dfbbSDag-Erling Smørgrav } 979e4a9863fSDag-Erling Smørgrav c->type = SSH_CHANNEL_ABANDONED; 980190cef3dSDag-Erling Smørgrav sshbuf_reset(c->input); 9814f52dfbbSDag-Erling Smørgrav chan_ibuf_empty(ssh, c); 982d4af9e69SDag-Erling Smørgrav return 0; 983d4af9e69SDag-Erling Smørgrav } else 984b66f2d16SKris Kennaway quit_pending = 1; 985b66f2d16SKris Kennaway return -1; 986b66f2d16SKris Kennaway 987b66f2d16SKris Kennaway case 'Z' - 64: 988d4af9e69SDag-Erling Smørgrav /* XXX support this for mux clients */ 989b15c8340SDag-Erling Smørgrav if (c && c->ctl_chan != -1) { 9906888a9beSDag-Erling Smørgrav char b[16]; 991d4af9e69SDag-Erling Smørgrav noescape: 9926888a9beSDag-Erling Smørgrav if (ch == 'Z' - 64) 9936888a9beSDag-Erling Smørgrav snprintf(b, sizeof b, "^Z"); 9946888a9beSDag-Erling Smørgrav else 9956888a9beSDag-Erling Smørgrav snprintf(b, sizeof b, "%c", ch); 996190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(berr, 9976888a9beSDag-Erling Smørgrav "%c%s escape not available to " 998d4af9e69SDag-Erling Smørgrav "multiplexed sessions\r\n", 999190cef3dSDag-Erling Smørgrav efc->escape_char, b)) != 0) 100019261079SEd Maste fatal_fr(r, "sshbuf_putf"); 1001d4af9e69SDag-Erling Smørgrav continue; 1002d4af9e69SDag-Erling Smørgrav } 1003d4af9e69SDag-Erling Smørgrav /* Suspend the program. Inform the user */ 1004190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(berr, 1005190cef3dSDag-Erling Smørgrav "%c^Z [suspend ssh]\r\n", 1006190cef3dSDag-Erling Smørgrav efc->escape_char)) != 0) 100719261079SEd Maste fatal_fr(r, "sshbuf_putf"); 1008b66f2d16SKris Kennaway 1009b66f2d16SKris Kennaway /* Restore terminal modes and suspend. */ 1010b66f2d16SKris Kennaway client_suspend_self(bin, bout, berr); 1011b66f2d16SKris Kennaway 1012b66f2d16SKris Kennaway /* We have been continued. */ 1013b66f2d16SKris Kennaway continue; 1014b66f2d16SKris Kennaway 1015d95e11bfSDag-Erling Smørgrav case 'B': 1016190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(berr, 1017190cef3dSDag-Erling Smørgrav "%cB\r\n", efc->escape_char)) != 0) 101819261079SEd Maste fatal_fr(r, "sshbuf_putf"); 10194f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, c->self, "break", 0); 1020190cef3dSDag-Erling Smørgrav if ((r = sshpkt_put_u32(ssh, 1000)) != 0 || 1021190cef3dSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) 102219261079SEd Maste fatal_fr(r, "send packet"); 1023d95e11bfSDag-Erling Smørgrav continue; 1024d95e11bfSDag-Erling Smørgrav 10251e8db6e2SBrian Feldman case 'R': 102619261079SEd Maste if (ssh->compat & 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()) { 1039190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(berr, 10406888a9beSDag-Erling Smørgrav "%c%c [Logging to syslog]\r\n", 1041190cef3dSDag-Erling Smørgrav efc->escape_char, ch)) != 0) 104219261079SEd Maste fatal_fr(r, "sshbuf_putf"); 10436888a9beSDag-Erling Smørgrav continue; 10446888a9beSDag-Erling Smørgrav } 10456888a9beSDag-Erling Smørgrav if (ch == 'V' && options.log_level > 10466888a9beSDag-Erling Smørgrav SYSLOG_LEVEL_QUIET) 10476888a9beSDag-Erling Smørgrav log_change_level(--options.log_level); 10486888a9beSDag-Erling Smørgrav if (ch == 'v' && options.log_level < 10496888a9beSDag-Erling Smørgrav SYSLOG_LEVEL_DEBUG3) 10506888a9beSDag-Erling Smørgrav log_change_level(++options.log_level); 1051190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(berr, 10524f52dfbbSDag-Erling Smørgrav "%c%c [LogLevel %s]\r\n", 10534f52dfbbSDag-Erling Smørgrav efc->escape_char, ch, 1054190cef3dSDag-Erling Smørgrav log_level_name(options.log_level))) != 0) 105519261079SEd Maste fatal_fr(r, "sshbuf_putf"); 10566888a9beSDag-Erling Smørgrav continue; 10576888a9beSDag-Erling Smørgrav 1058b66f2d16SKris Kennaway case '&': 1059b15c8340SDag-Erling Smørgrav if (c && c->ctl_chan != -1) 1060d4af9e69SDag-Erling Smørgrav goto noescape; 1061b66f2d16SKris Kennaway /* 1062d4af9e69SDag-Erling Smørgrav * Detach the program (continue to serve 1063d4af9e69SDag-Erling Smørgrav * connections, but put in background and no 1064d4af9e69SDag-Erling Smørgrav * more new connections). 1065b66f2d16SKris Kennaway */ 1066ae1f160dSDag-Erling Smørgrav /* Restore tty modes. */ 1067e146993eSDag-Erling Smørgrav leave_raw_mode( 1068e146993eSDag-Erling Smørgrav options.request_tty == REQUEST_TTY_FORCE); 1069ae1f160dSDag-Erling Smørgrav 1070ae1f160dSDag-Erling Smørgrav /* Stop listening for new connections. */ 10714f52dfbbSDag-Erling Smørgrav channel_stop_listening(ssh); 1072ae1f160dSDag-Erling Smørgrav 107319261079SEd Maste if ((r = sshbuf_putf(berr, "%c& " 107419261079SEd Maste "[backgrounded]\n", efc->escape_char)) != 0) 107519261079SEd Maste fatal_fr(r, "sshbuf_putf"); 1076ae1f160dSDag-Erling Smørgrav 1077ae1f160dSDag-Erling Smørgrav /* Fork into background. */ 1078ae1f160dSDag-Erling Smørgrav pid = fork(); 107919261079SEd Maste if (pid == -1) { 1080ae1f160dSDag-Erling Smørgrav error("fork: %.100s", strerror(errno)); 1081ae1f160dSDag-Erling Smørgrav continue; 1082ae1f160dSDag-Erling Smørgrav } 1083ae1f160dSDag-Erling Smørgrav if (pid != 0) { /* This is the parent. */ 1084ae1f160dSDag-Erling Smørgrav /* The parent just exits. */ 1085ae1f160dSDag-Erling Smørgrav exit(0); 1086ae1f160dSDag-Erling Smørgrav } 1087ae1f160dSDag-Erling Smørgrav /* The child continues serving connections. */ 1088ae1f160dSDag-Erling Smørgrav /* fake EOF on stdin */ 1089190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u8(bin, 4)) != 0) 109019261079SEd Maste fatal_fr(r, "sshbuf_put_u8"); 1091ae1f160dSDag-Erling Smørgrav return -1; 1092b66f2d16SKris Kennaway case '?': 10934f52dfbbSDag-Erling Smørgrav print_escape_help(berr, efc->escape_char, 10946888a9beSDag-Erling Smørgrav (c && c->ctl_chan != -1), 10956888a9beSDag-Erling Smørgrav log_is_on_stderr()); 1096b66f2d16SKris Kennaway continue; 1097b66f2d16SKris Kennaway 1098b66f2d16SKris Kennaway case '#': 1099190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(berr, "%c#\r\n", 1100190cef3dSDag-Erling Smørgrav efc->escape_char)) != 0) 110119261079SEd Maste fatal_fr(r, "sshbuf_putf"); 11024f52dfbbSDag-Erling Smørgrav s = channel_open_message(ssh); 1103190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put(berr, s, strlen(s))) != 0) 110419261079SEd Maste fatal_fr(r, "sshbuf_put"); 1105e4a9863fSDag-Erling Smørgrav free(s); 1106b66f2d16SKris Kennaway continue; 1107b66f2d16SKris Kennaway 1108545d5ecaSDag-Erling Smørgrav case 'C': 1109b15c8340SDag-Erling Smørgrav if (c && c->ctl_chan != -1) 1110cce7d346SDag-Erling Smørgrav goto noescape; 11114f52dfbbSDag-Erling Smørgrav process_cmdline(ssh); 1112545d5ecaSDag-Erling Smørgrav continue; 1113545d5ecaSDag-Erling Smørgrav 1114b66f2d16SKris Kennaway default: 11154f52dfbbSDag-Erling Smørgrav if (ch != efc->escape_char) { 1116190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u8(bin, 1117190cef3dSDag-Erling Smørgrav efc->escape_char)) != 0) 111819261079SEd Maste fatal_fr(r, "sshbuf_put_u8"); 1119b66f2d16SKris Kennaway bytes++; 1120b66f2d16SKris Kennaway } 1121b66f2d16SKris Kennaway /* Escaped characters fall through here */ 1122b66f2d16SKris Kennaway break; 1123b66f2d16SKris Kennaway } 1124b66f2d16SKris Kennaway } else { 1125b66f2d16SKris Kennaway /* 1126d4af9e69SDag-Erling Smørgrav * The previous character was not an escape char. 1127d4af9e69SDag-Erling Smørgrav * Check if this is an escape. 1128b66f2d16SKris Kennaway */ 11294f52dfbbSDag-Erling Smørgrav if (last_was_cr && ch == efc->escape_char) { 1130d4af9e69SDag-Erling Smørgrav /* 1131d4af9e69SDag-Erling Smørgrav * It is. Set the flag and continue to 1132d4af9e69SDag-Erling Smørgrav * next character. 1133d4af9e69SDag-Erling Smørgrav */ 11344f52dfbbSDag-Erling Smørgrav efc->escape_pending = 1; 1135b66f2d16SKris Kennaway continue; 1136b66f2d16SKris Kennaway } 1137b66f2d16SKris Kennaway } 1138b66f2d16SKris Kennaway 1139b66f2d16SKris Kennaway /* 1140b66f2d16SKris Kennaway * Normal character. Record whether it was a newline, 1141b66f2d16SKris Kennaway * and append it to the buffer. 1142b66f2d16SKris Kennaway */ 1143b66f2d16SKris Kennaway last_was_cr = (ch == '\r' || ch == '\n'); 1144190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u8(bin, ch)) != 0) 114519261079SEd Maste fatal_fr(r, "sshbuf_put_u8"); 1146b66f2d16SKris Kennaway bytes++; 1147b66f2d16SKris Kennaway } 1148b66f2d16SKris Kennaway return bytes; 1149b66f2d16SKris Kennaway } 1150b66f2d16SKris Kennaway 1151511b41d2SMark Murray /* 1152a04a10f8SKris Kennaway * Get packets from the connection input buffer, and process them as long as 1153a04a10f8SKris Kennaway * there are packets available. 1154a04a10f8SKris Kennaway * 1155a04a10f8SKris Kennaway * Any unknown packets received during the actual 1156a04a10f8SKris Kennaway * session cause the session to terminate. This is 1157a04a10f8SKris Kennaway * intended to make debugging easier since no 1158a04a10f8SKris Kennaway * confirmations are sent. Any compatible protocol 1159a04a10f8SKris Kennaway * extensions must be negotiated during the 1160a04a10f8SKris Kennaway * preparatory phase. 1161a04a10f8SKris Kennaway */ 1162a04a10f8SKris Kennaway 1163ae1f160dSDag-Erling Smørgrav static void 116419261079SEd Maste client_process_buffered_input_packets(struct ssh *ssh) 1165a04a10f8SKris Kennaway { 116619261079SEd Maste ssh_dispatch_run_fatal(ssh, DISPATCH_NONBLOCK, &quit_pending); 1167a04a10f8SKris Kennaway } 1168a04a10f8SKris Kennaway 1169b66f2d16SKris Kennaway /* scan buf[] for '~' before sending data to the peer */ 1170b66f2d16SKris Kennaway 1171d4af9e69SDag-Erling Smørgrav /* Helper: allocate a new escape_filter_ctx and fill in its escape char */ 1172d4af9e69SDag-Erling Smørgrav void * 1173d4af9e69SDag-Erling Smørgrav client_new_escape_filter_ctx(int escape_char) 1174b66f2d16SKris Kennaway { 1175d4af9e69SDag-Erling Smørgrav struct escape_filter_ctx *ret; 1176d4af9e69SDag-Erling Smørgrav 11770a37d4a3SXin LI ret = xcalloc(1, sizeof(*ret)); 1178d4af9e69SDag-Erling Smørgrav ret->escape_pending = 0; 1179d4af9e69SDag-Erling Smørgrav ret->escape_char = escape_char; 1180d4af9e69SDag-Erling Smørgrav return (void *)ret; 1181d4af9e69SDag-Erling Smørgrav } 1182d4af9e69SDag-Erling Smørgrav 1183d4af9e69SDag-Erling Smørgrav /* Free the escape filter context on channel free */ 1184d4af9e69SDag-Erling Smørgrav void 11854f52dfbbSDag-Erling Smørgrav client_filter_cleanup(struct ssh *ssh, int cid, void *ctx) 1186d4af9e69SDag-Erling Smørgrav { 1187e4a9863fSDag-Erling Smørgrav free(ctx); 1188d4af9e69SDag-Erling Smørgrav } 1189d4af9e69SDag-Erling Smørgrav 1190d4af9e69SDag-Erling Smørgrav int 11914f52dfbbSDag-Erling Smørgrav client_simple_escape_filter(struct ssh *ssh, Channel *c, char *buf, int len) 1192d4af9e69SDag-Erling Smørgrav { 1193d4af9e69SDag-Erling Smørgrav if (c->extended_usage != CHAN_EXTENDED_WRITE) 1194d4af9e69SDag-Erling Smørgrav return 0; 1195d4af9e69SDag-Erling Smørgrav 11964f52dfbbSDag-Erling Smørgrav return process_escapes(ssh, c, c->input, c->output, c->extended, 1197d4af9e69SDag-Erling Smørgrav buf, len); 1198b66f2d16SKris Kennaway } 1199b66f2d16SKris Kennaway 1200ae1f160dSDag-Erling Smørgrav static void 12014f52dfbbSDag-Erling Smørgrav client_channel_closed(struct ssh *ssh, int id, void *arg) 12021e8db6e2SBrian Feldman { 12034f52dfbbSDag-Erling Smørgrav channel_cancel_cleanup(ssh, id); 12041e8db6e2SBrian Feldman session_closed = 1; 1205e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 12061e8db6e2SBrian Feldman } 12071e8db6e2SBrian Feldman 1208a04a10f8SKris Kennaway /* 1209511b41d2SMark Murray * Implements the interactive session with the server. This is called after 1210511b41d2SMark Murray * the user has been authenticated, and a command has been started on the 1211ae1f160dSDag-Erling Smørgrav * remote host. If escape_char != SSH_ESCAPECHAR_NONE, it is the character 1212ae1f160dSDag-Erling Smørgrav * used as an escape character for terminating or suspending the session. 1213511b41d2SMark Murray */ 1214511b41d2SMark Murray int 12154f52dfbbSDag-Erling Smørgrav client_loop(struct ssh *ssh, int have_pty, int escape_char_arg, 12164f52dfbbSDag-Erling Smørgrav int ssh2_chan_id) 1217511b41d2SMark Murray { 12181323ec57SEd Maste struct pollfd *pfd = NULL; 12191323ec57SEd Maste u_int npfd_alloc = 0, npfd_active = 0; 1220511b41d2SMark Murray double start_time, total_time; 12211323ec57SEd Maste int r, len; 1222d4af9e69SDag-Erling Smørgrav u_int64_t ibytes, obytes; 12231323ec57SEd Maste int conn_in_ready, conn_out_ready; 1224511b41d2SMark Murray 1225511b41d2SMark Murray debug("Entering interactive session."); 1226511b41d2SMark Murray 1227acc1a9efSDag-Erling Smørgrav if (options.control_master && 1228acc1a9efSDag-Erling Smørgrav !option_clear_or_none(options.control_path)) { 1229acc1a9efSDag-Erling Smørgrav debug("pledge: id"); 123019261079SEd Maste if (pledge("stdio rpath wpath cpath unix inet dns recvfd sendfd proc exec id tty", 1231acc1a9efSDag-Erling Smørgrav NULL) == -1) 123219261079SEd Maste fatal_f("pledge(): %s", strerror(errno)); 1233acc1a9efSDag-Erling Smørgrav 1234acc1a9efSDag-Erling Smørgrav } else if (options.forward_x11 || options.permit_local_command) { 1235acc1a9efSDag-Erling Smørgrav debug("pledge: exec"); 1236acc1a9efSDag-Erling Smørgrav if (pledge("stdio rpath wpath cpath unix inet dns proc exec tty", 1237acc1a9efSDag-Erling Smørgrav NULL) == -1) 123819261079SEd Maste fatal_f("pledge(): %s", strerror(errno)); 1239acc1a9efSDag-Erling Smørgrav 1240acc1a9efSDag-Erling Smørgrav } else if (options.update_hostkeys) { 12411323ec57SEd Maste debug("pledge: filesystem"); 1242acc1a9efSDag-Erling Smørgrav if (pledge("stdio rpath wpath cpath unix inet dns proc tty", 1243acc1a9efSDag-Erling Smørgrav NULL) == -1) 124419261079SEd Maste fatal_f("pledge(): %s", strerror(errno)); 1245acc1a9efSDag-Erling Smørgrav 1246076ad2f8SDag-Erling Smørgrav } else if (!option_clear_or_none(options.proxy_command) || 124719261079SEd Maste options.fork_after_authentication) { 1248acc1a9efSDag-Erling Smørgrav debug("pledge: proc"); 1249acc1a9efSDag-Erling Smørgrav if (pledge("stdio cpath unix inet dns proc tty", NULL) == -1) 125019261079SEd Maste fatal_f("pledge(): %s", strerror(errno)); 1251acc1a9efSDag-Erling Smørgrav 1252acc1a9efSDag-Erling Smørgrav } else { 1253acc1a9efSDag-Erling Smørgrav debug("pledge: network"); 12544f52dfbbSDag-Erling Smørgrav if (pledge("stdio unix inet dns proc tty", NULL) == -1) 125519261079SEd Maste fatal_f("pledge(): %s", strerror(errno)); 1256acc1a9efSDag-Erling Smørgrav } 1257acc1a9efSDag-Erling Smørgrav 125847dd1d1bSDag-Erling Smørgrav start_time = monotime_double(); 1259511b41d2SMark Murray 1260511b41d2SMark Murray /* Initialize variables. */ 1261511b41d2SMark Murray last_was_cr = 1; 1262511b41d2SMark Murray exit_status = -1; 126319261079SEd Maste connection_in = ssh_packet_get_connection_in(ssh); 126419261079SEd Maste connection_out = ssh_packet_get_connection_out(ssh); 12651e8db6e2SBrian Feldman 1266511b41d2SMark Murray quit_pending = 0; 1267511b41d2SMark Murray 1268190cef3dSDag-Erling Smørgrav /* Initialize buffer. */ 1269190cef3dSDag-Erling Smørgrav if ((stderr_buffer = sshbuf_new()) == NULL) 127019261079SEd Maste fatal_f("sshbuf_new failed"); 1271511b41d2SMark Murray 127219261079SEd Maste client_init_dispatch(ssh); 1273a04a10f8SKris Kennaway 1274d0c8c0bcSDag-Erling Smørgrav /* 1275d0c8c0bcSDag-Erling Smørgrav * Set signal handlers, (e.g. to restore non-blocking mode) 1276d0c8c0bcSDag-Erling Smørgrav * but don't overwrite SIG_IGN, matches behaviour from rsh(1) 1277d0c8c0bcSDag-Erling Smørgrav */ 127819261079SEd Maste if (ssh_signal(SIGHUP, SIG_IGN) != SIG_IGN) 127919261079SEd Maste ssh_signal(SIGHUP, signal_handler); 128019261079SEd Maste if (ssh_signal(SIGINT, SIG_IGN) != SIG_IGN) 128119261079SEd Maste ssh_signal(SIGINT, signal_handler); 128219261079SEd Maste if (ssh_signal(SIGQUIT, SIG_IGN) != SIG_IGN) 128319261079SEd Maste ssh_signal(SIGQUIT, signal_handler); 128419261079SEd Maste if (ssh_signal(SIGTERM, SIG_IGN) != SIG_IGN) 128519261079SEd Maste ssh_signal(SIGTERM, signal_handler); 128619261079SEd Maste ssh_signal(SIGWINCH, window_change_handler); 1287511b41d2SMark Murray 1288511b41d2SMark Murray if (have_pty) 1289e146993eSDag-Erling Smørgrav enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 1290511b41d2SMark Murray 12911e8db6e2SBrian Feldman session_ident = ssh2_chan_id; 1292e146993eSDag-Erling Smørgrav if (session_ident != -1) { 1293e146993eSDag-Erling Smørgrav if (escape_char_arg != SSH_ESCAPECHAR_NONE) { 12944f52dfbbSDag-Erling Smørgrav channel_register_filter(ssh, session_ident, 1295d4af9e69SDag-Erling Smørgrav client_simple_escape_filter, NULL, 1296d4af9e69SDag-Erling Smørgrav client_filter_cleanup, 1297e146993eSDag-Erling Smørgrav client_new_escape_filter_ctx( 1298e146993eSDag-Erling Smørgrav escape_char_arg)); 1299e146993eSDag-Erling Smørgrav } 13004f52dfbbSDag-Erling Smørgrav channel_register_cleanup(ssh, session_ident, 1301021d409fSDag-Erling Smørgrav client_channel_closed, 0); 1302e146993eSDag-Erling Smørgrav } 1303b66f2d16SKris Kennaway 130419261079SEd Maste schedule_server_alive_check(); 130519261079SEd Maste 1306511b41d2SMark Murray /* Main loop of the client for the interactive session mode. */ 1307511b41d2SMark Murray while (!quit_pending) { 1308511b41d2SMark Murray 1309511b41d2SMark Murray /* Process buffered packets sent by the server. */ 131019261079SEd Maste client_process_buffered_input_packets(ssh); 1311511b41d2SMark Murray 13124f52dfbbSDag-Erling Smørgrav if (session_closed && !channel_still_open(ssh)) 1313a04a10f8SKris Kennaway break; 1314a04a10f8SKris Kennaway 13154f52dfbbSDag-Erling Smørgrav if (ssh_packet_is_rekeying(ssh)) { 13161e8db6e2SBrian Feldman debug("rekeying in progress"); 1317acc1a9efSDag-Erling Smørgrav } else if (need_rekeying) { 1318acc1a9efSDag-Erling Smørgrav /* manual rekey request */ 1319acc1a9efSDag-Erling Smørgrav debug("need rekeying"); 13204f52dfbbSDag-Erling Smørgrav if ((r = kex_start_rekex(ssh)) != 0) 132119261079SEd Maste fatal_fr(r, "kex_start_rekex"); 1322acc1a9efSDag-Erling Smørgrav need_rekeying = 0; 13231e8db6e2SBrian Feldman } else { 1324511b41d2SMark Murray /* 13251e8db6e2SBrian Feldman * Make packets from buffered channel data, and 13261e8db6e2SBrian Feldman * enqueue them for sending to the server. 1327511b41d2SMark Murray */ 132819261079SEd Maste if (ssh_packet_not_very_much_data_to_write(ssh)) 13294f52dfbbSDag-Erling Smørgrav channel_output_poll(ssh); 1330511b41d2SMark Murray 1331511b41d2SMark Murray /* 13321e8db6e2SBrian Feldman * Check if the window size has changed, and buffer a 13331e8db6e2SBrian Feldman * message about it to the server if so. 1334511b41d2SMark Murray */ 13354f52dfbbSDag-Erling Smørgrav client_check_window_change(ssh); 1336511b41d2SMark Murray 1337511b41d2SMark Murray if (quit_pending) 1338511b41d2SMark Murray break; 13391e8db6e2SBrian Feldman } 1340511b41d2SMark Murray /* 1341511b41d2SMark Murray * Wait until we have something to do (something becomes 1342511b41d2SMark Murray * available on one of the descriptors). 1343511b41d2SMark Murray */ 13441323ec57SEd Maste client_wait_until_can_do_something(ssh, &pfd, &npfd_alloc, 13451323ec57SEd Maste &npfd_active, ssh_packet_is_rekeying(ssh), 13461323ec57SEd Maste &conn_in_ready, &conn_out_ready); 1347511b41d2SMark Murray 1348511b41d2SMark Murray if (quit_pending) 1349511b41d2SMark Murray break; 1350511b41d2SMark Murray 1351*38a52bd3SEd Maste /* Do channel operations. */ 13521323ec57SEd Maste channel_after_poll(ssh, pfd, npfd_active); 1353511b41d2SMark Murray 1354a04a10f8SKris Kennaway /* Buffer input from the connection. */ 13551323ec57SEd Maste if (conn_in_ready) 13561323ec57SEd Maste client_process_net_input(ssh); 1357511b41d2SMark Murray 1358a04a10f8SKris Kennaway if (quit_pending) 1359a04a10f8SKris Kennaway break; 1360a04a10f8SKris Kennaway 136119261079SEd Maste /* A timeout may have triggered rekeying */ 136219261079SEd Maste if ((r = ssh_packet_check_rekey(ssh)) != 0) 136319261079SEd Maste fatal_fr(r, "cannot start rekeying"); 136419261079SEd Maste 1365d4af9e69SDag-Erling Smørgrav /* 1366d4af9e69SDag-Erling Smørgrav * Send as much buffered packet data as possible to the 1367d4af9e69SDag-Erling Smørgrav * sender. 1368d4af9e69SDag-Erling Smørgrav */ 13691323ec57SEd Maste if (conn_out_ready) { 137019261079SEd Maste if ((r = ssh_packet_write_poll(ssh)) != 0) { 137119261079SEd Maste sshpkt_fatal(ssh, r, 137219261079SEd Maste "%s: ssh_packet_write_poll", __func__); 137319261079SEd Maste } 137419261079SEd Maste } 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 } 13881323ec57SEd Maste free(pfd); 1389511b41d2SMark Murray 1390511b41d2SMark Murray /* Terminate the session. */ 1391511b41d2SMark Murray 1392511b41d2SMark Murray /* Stop watching for window change. */ 139319261079SEd Maste ssh_signal(SIGWINCH, SIG_DFL); 1394511b41d2SMark Murray 139519261079SEd Maste if ((r = sshpkt_start(ssh, SSH2_MSG_DISCONNECT)) != 0 || 139619261079SEd Maste (r = sshpkt_put_u32(ssh, SSH2_DISCONNECT_BY_APPLICATION)) != 0 || 139719261079SEd Maste (r = sshpkt_put_cstring(ssh, "disconnected by user")) != 0 || 139819261079SEd Maste (r = sshpkt_put_cstring(ssh, "")) != 0 || /* language tag */ 139919261079SEd Maste (r = sshpkt_send(ssh)) != 0 || 140019261079SEd Maste (r = ssh_packet_write_wait(ssh)) != 0) 140119261079SEd Maste fatal_fr(r, "send disconnect"); 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 1408efcad6b7SDag-Erling Smørgrav /* 1409efcad6b7SDag-Erling Smørgrav * If there was no shell or command requested, there will be no remote 1410efcad6b7SDag-Erling Smørgrav * exit status to be returned. In that case, clear error code if the 1411efcad6b7SDag-Erling Smørgrav * connection was deliberately terminated at this end. 1412efcad6b7SDag-Erling Smørgrav */ 1413e9e8876aSEd Maste if (options.session_type == SESSION_TYPE_NONE && 1414e9e8876aSEd Maste received_signal == SIGTERM) { 1415efcad6b7SDag-Erling Smørgrav received_signal = 0; 1416efcad6b7SDag-Erling Smørgrav exit_status = 0; 1417ae1f160dSDag-Erling Smørgrav } 1418511b41d2SMark Murray 14194f52dfbbSDag-Erling Smørgrav if (received_signal) { 14204f52dfbbSDag-Erling Smørgrav verbose("Killed by signal %d.", (int) received_signal); 142119261079SEd Maste cleanup_exit(255); 14224f52dfbbSDag-Erling Smørgrav } 1423efcad6b7SDag-Erling Smørgrav 1424511b41d2SMark Murray /* 1425511b41d2SMark Murray * In interactive mode (with pseudo tty) display a message indicating 1426511b41d2SMark Murray * that the connection has been closed. 1427511b41d2SMark Murray */ 14281323ec57SEd Maste if (have_pty && options.log_level >= SYSLOG_LEVEL_INFO) 14291323ec57SEd Maste quit_message("Connection to %s closed.", host); 1430ae1f160dSDag-Erling Smørgrav 1431511b41d2SMark Murray /* Output any buffered data for stderr. */ 1432190cef3dSDag-Erling Smørgrav if (sshbuf_len(stderr_buffer) > 0) { 14334a421b63SDag-Erling Smørgrav len = atomicio(vwrite, fileno(stderr), 1434190cef3dSDag-Erling Smørgrav (u_char *)sshbuf_ptr(stderr_buffer), 1435190cef3dSDag-Erling Smørgrav sshbuf_len(stderr_buffer)); 1436190cef3dSDag-Erling Smørgrav if (len < 0 || (u_int)len != sshbuf_len(stderr_buffer)) 1437511b41d2SMark Murray error("Write failed flushing stderr buffer."); 1438190cef3dSDag-Erling Smørgrav else if ((r = sshbuf_consume(stderr_buffer, len)) != 0) 143919261079SEd Maste fatal_fr(r, "sshbuf_consume"); 1440511b41d2SMark Murray } 1441511b41d2SMark Murray 1442511b41d2SMark Murray /* Clear and free any buffers. */ 1443190cef3dSDag-Erling Smørgrav sshbuf_free(stderr_buffer); 1444511b41d2SMark Murray 1445511b41d2SMark Murray /* Report bytes transferred, and transfer rates. */ 144647dd1d1bSDag-Erling Smørgrav total_time = monotime_double() - start_time; 144719261079SEd Maste ssh_packet_get_bytes(ssh, &ibytes, &obytes); 1448d4af9e69SDag-Erling Smørgrav verbose("Transferred: sent %llu, received %llu bytes, in %.1f seconds", 14494a421b63SDag-Erling Smørgrav (unsigned long long)obytes, (unsigned long long)ibytes, total_time); 1450511b41d2SMark Murray if (total_time > 0) 1451d4af9e69SDag-Erling Smørgrav verbose("Bytes per second: sent %.1f, received %.1f", 1452d4af9e69SDag-Erling Smørgrav obytes / total_time, ibytes / total_time); 1453511b41d2SMark Murray /* Return the exit status of the program. */ 1454511b41d2SMark Murray debug("Exit status %d", exit_status); 1455511b41d2SMark Murray return exit_status; 1456511b41d2SMark Murray } 1457a04a10f8SKris Kennaway 1458a04a10f8SKris Kennaway /*********/ 1459a04a10f8SKris Kennaway 1460ae1f160dSDag-Erling Smørgrav static Channel * 14614f52dfbbSDag-Erling Smørgrav client_request_forwarded_tcpip(struct ssh *ssh, const char *request_type, 14624f52dfbbSDag-Erling Smørgrav int rchan, u_int rwindow, u_int rmaxpack) 14631e8db6e2SBrian Feldman { 14641e8db6e2SBrian Feldman Channel *c = NULL; 1465ca86bcf2SDag-Erling Smørgrav struct sshbuf *b = NULL; 14661e8db6e2SBrian Feldman char *listen_address, *originator_address; 146719261079SEd Maste u_int listen_port, originator_port; 1468ca86bcf2SDag-Erling Smørgrav int r; 14691e8db6e2SBrian Feldman 14701e8db6e2SBrian Feldman /* Get rest of the packet */ 147119261079SEd Maste if ((r = sshpkt_get_cstring(ssh, &listen_address, NULL)) != 0 || 147219261079SEd Maste (r = sshpkt_get_u32(ssh, &listen_port)) != 0 || 147319261079SEd Maste (r = sshpkt_get_cstring(ssh, &originator_address, NULL)) != 0 || 147419261079SEd Maste (r = sshpkt_get_u32(ssh, &originator_port)) != 0 || 147519261079SEd Maste (r = sshpkt_get_end(ssh)) != 0) 147619261079SEd Maste fatal_fr(r, "parse packet"); 14771e8db6e2SBrian Feldman 147819261079SEd Maste debug_f("listen %s port %d, originator %s port %d", 1479a0ee8cc6SDag-Erling Smørgrav listen_address, listen_port, originator_address, originator_port); 14801e8db6e2SBrian Feldman 148119261079SEd Maste if (listen_port > 0xffff) 148219261079SEd Maste error_f("invalid listen port"); 148319261079SEd Maste else if (originator_port > 0xffff) 148419261079SEd Maste error_f("invalid originator port"); 148519261079SEd Maste else { 148619261079SEd Maste c = channel_connect_by_listen_address(ssh, 148719261079SEd Maste listen_address, listen_port, "forwarded-tcpip", 148819261079SEd Maste originator_address); 148919261079SEd Maste } 1490d4af9e69SDag-Erling Smørgrav 1491ca86bcf2SDag-Erling Smørgrav if (c != NULL && c->type == SSH_CHANNEL_MUX_CLIENT) { 1492ca86bcf2SDag-Erling Smørgrav if ((b = sshbuf_new()) == NULL) { 149319261079SEd Maste error_f("alloc reply"); 1494ca86bcf2SDag-Erling Smørgrav goto out; 1495ca86bcf2SDag-Erling Smørgrav } 1496ca86bcf2SDag-Erling Smørgrav /* reconstruct and send to muxclient */ 1497ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_put_u8(b, 0)) != 0 || /* padlen */ 1498ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u8(b, SSH2_MSG_CHANNEL_OPEN)) != 0 || 1499ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_cstring(b, request_type)) != 0 || 1500ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, rchan)) != 0 || 1501ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, rwindow)) != 0 || 1502ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, rmaxpack)) != 0 || 1503ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_cstring(b, listen_address)) != 0 || 1504ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, listen_port)) != 0 || 1505ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_cstring(b, originator_address)) != 0 || 1506ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, originator_port)) != 0 || 15074f52dfbbSDag-Erling Smørgrav (r = sshbuf_put_stringb(c->output, b)) != 0) { 150819261079SEd Maste error_fr(r, "compose for muxclient"); 1509ca86bcf2SDag-Erling Smørgrav goto out; 1510ca86bcf2SDag-Erling Smørgrav } 1511ca86bcf2SDag-Erling Smørgrav } 1512ca86bcf2SDag-Erling Smørgrav 1513ca86bcf2SDag-Erling Smørgrav out: 1514ca86bcf2SDag-Erling Smørgrav sshbuf_free(b); 1515e4a9863fSDag-Erling Smørgrav free(originator_address); 1516e4a9863fSDag-Erling Smørgrav free(listen_address); 15171e8db6e2SBrian Feldman return c; 15181e8db6e2SBrian Feldman } 15191e8db6e2SBrian Feldman 1520ae1f160dSDag-Erling Smørgrav static Channel * 15214f52dfbbSDag-Erling Smørgrav client_request_forwarded_streamlocal(struct ssh *ssh, 15224f52dfbbSDag-Erling Smørgrav const char *request_type, int rchan) 1523a0ee8cc6SDag-Erling Smørgrav { 1524a0ee8cc6SDag-Erling Smørgrav Channel *c = NULL; 1525a0ee8cc6SDag-Erling Smørgrav char *listen_path; 152619261079SEd Maste int r; 1527a0ee8cc6SDag-Erling Smørgrav 1528a0ee8cc6SDag-Erling Smørgrav /* Get the remote path. */ 152919261079SEd Maste if ((r = sshpkt_get_cstring(ssh, &listen_path, NULL)) != 0 || 153019261079SEd Maste (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* reserved */ 153119261079SEd Maste (r = sshpkt_get_end(ssh)) != 0) 153219261079SEd Maste fatal_fr(r, "parse packet"); 1533a0ee8cc6SDag-Erling Smørgrav 153419261079SEd Maste debug_f("request: %s", listen_path); 1535a0ee8cc6SDag-Erling Smørgrav 15364f52dfbbSDag-Erling Smørgrav c = channel_connect_by_listen_path(ssh, listen_path, 1537a0ee8cc6SDag-Erling Smørgrav "forwarded-streamlocal@openssh.com", "forwarded-streamlocal"); 1538a0ee8cc6SDag-Erling Smørgrav free(listen_path); 1539a0ee8cc6SDag-Erling Smørgrav return c; 1540a0ee8cc6SDag-Erling Smørgrav } 1541a0ee8cc6SDag-Erling Smørgrav 1542a0ee8cc6SDag-Erling Smørgrav static Channel * 15434f52dfbbSDag-Erling Smørgrav client_request_x11(struct ssh *ssh, const char *request_type, int rchan) 15441e8db6e2SBrian Feldman { 15451e8db6e2SBrian Feldman Channel *c = NULL; 15461e8db6e2SBrian Feldman char *originator; 154719261079SEd Maste u_int originator_port; 154819261079SEd Maste int r, sock; 15491e8db6e2SBrian Feldman 15501e8db6e2SBrian Feldman if (!options.forward_x11) { 15511e8db6e2SBrian Feldman error("Warning: ssh server tried X11 forwarding."); 1552d4af9e69SDag-Erling Smørgrav error("Warning: this is probably a break-in attempt by a " 1553d4af9e69SDag-Erling Smørgrav "malicious server."); 15541e8db6e2SBrian Feldman return NULL; 15551e8db6e2SBrian Feldman } 1556557f75e5SDag-Erling Smørgrav if (x11_refuse_time != 0 && (u_int)monotime() >= x11_refuse_time) { 1557e2f6069cSDag-Erling Smørgrav verbose("Rejected X11 connection after ForwardX11Timeout " 1558e2f6069cSDag-Erling Smørgrav "expired"); 1559e2f6069cSDag-Erling Smørgrav return NULL; 1560e2f6069cSDag-Erling Smørgrav } 156119261079SEd Maste if ((r = sshpkt_get_cstring(ssh, &originator, NULL)) != 0 || 156219261079SEd Maste (r = sshpkt_get_u32(ssh, &originator_port)) != 0 || 156319261079SEd Maste (r = sshpkt_get_end(ssh)) != 0) 156419261079SEd Maste fatal_fr(r, "parse packet"); 15651e8db6e2SBrian Feldman /* XXX check permission */ 156619261079SEd Maste /* XXX range check originator port? */ 156719261079SEd Maste debug("client_request_x11: request from %s %u", originator, 15681e8db6e2SBrian Feldman originator_port); 1569e4a9863fSDag-Erling Smørgrav free(originator); 15704f52dfbbSDag-Erling Smørgrav sock = x11_connect_display(ssh); 1571ae1f160dSDag-Erling Smørgrav if (sock < 0) 1572ae1f160dSDag-Erling Smørgrav return NULL; 15734f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, "x11", 157460c59fadSDag-Erling Smørgrav SSH_CHANNEL_X11_OPEN, sock, sock, -1, 157560c59fadSDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "x11", 1); 1576ae1f160dSDag-Erling Smørgrav c->force_drain = 1; 15771e8db6e2SBrian Feldman return c; 15781e8db6e2SBrian Feldman } 15791e8db6e2SBrian Feldman 1580ae1f160dSDag-Erling Smørgrav static Channel * 15814f52dfbbSDag-Erling Smørgrav client_request_agent(struct ssh *ssh, const char *request_type, int rchan) 15821e8db6e2SBrian Feldman { 15831e8db6e2SBrian Feldman Channel *c = NULL; 1584bc5531deSDag-Erling Smørgrav int r, sock; 15851e8db6e2SBrian Feldman 15861e8db6e2SBrian Feldman if (!options.forward_agent) { 15871e8db6e2SBrian Feldman error("Warning: ssh server tried agent forwarding."); 1588d4af9e69SDag-Erling Smørgrav error("Warning: this is probably a break-in attempt by a " 1589d4af9e69SDag-Erling Smørgrav "malicious server."); 15901e8db6e2SBrian Feldman return NULL; 15911e8db6e2SBrian Feldman } 159219261079SEd Maste if (forward_agent_sock_path == NULL) { 159319261079SEd Maste r = ssh_get_authentication_socket(&sock); 159419261079SEd Maste } else { 159519261079SEd Maste r = ssh_get_authentication_socket_path(forward_agent_sock_path, &sock); 159619261079SEd Maste } 159719261079SEd Maste if (r != 0) { 1598bc5531deSDag-Erling Smørgrav if (r != SSH_ERR_AGENT_NOT_PRESENT) 159919261079SEd Maste debug_fr(r, "ssh_get_authentication_socket"); 1600ae1f160dSDag-Erling Smørgrav return NULL; 1601bc5531deSDag-Erling Smørgrav } 16021323ec57SEd Maste if ((r = ssh_agent_bind_hostkey(sock, ssh->kex->initial_hostkey, 16031323ec57SEd Maste ssh->kex->session_id, ssh->kex->initial_sig, 1)) == 0) 16041323ec57SEd Maste debug_f("bound agent to hostkey"); 16051323ec57SEd Maste else 16061323ec57SEd Maste debug2_fr(r, "ssh_agent_bind_hostkey"); 16071323ec57SEd Maste 16084f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, "authentication agent connection", 16091e8db6e2SBrian Feldman SSH_CHANNEL_OPEN, sock, sock, -1, 1610e3bd730fSBryan Drewery CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, 161189986192SBrooks Davis "authentication agent connection", 1); 1612ae1f160dSDag-Erling Smørgrav c->force_drain = 1; 16131e8db6e2SBrian Feldman return c; 16141e8db6e2SBrian Feldman } 16151e8db6e2SBrian Feldman 161647dd1d1bSDag-Erling Smørgrav char * 16174f52dfbbSDag-Erling Smørgrav client_request_tun_fwd(struct ssh *ssh, int tun_mode, 161819261079SEd Maste int local_tun, int remote_tun, channel_open_fn *cb, void *cbctx) 1619d4af9e69SDag-Erling Smørgrav { 1620d4af9e69SDag-Erling Smørgrav Channel *c; 162119261079SEd Maste int r, fd; 162247dd1d1bSDag-Erling Smørgrav char *ifname = NULL; 1623d4af9e69SDag-Erling Smørgrav 1624d4af9e69SDag-Erling Smørgrav if (tun_mode == SSH_TUNMODE_NO) 1625d4af9e69SDag-Erling Smørgrav return 0; 1626d4af9e69SDag-Erling Smørgrav 1627d4af9e69SDag-Erling Smørgrav debug("Requesting tun unit %d in mode %d", local_tun, tun_mode); 1628d4af9e69SDag-Erling Smørgrav 1629d4af9e69SDag-Erling Smørgrav /* Open local tunnel device */ 163047dd1d1bSDag-Erling Smørgrav if ((fd = tun_open(local_tun, tun_mode, &ifname)) == -1) { 1631d4af9e69SDag-Erling Smørgrav error("Tunnel device open failed."); 163247dd1d1bSDag-Erling Smørgrav return NULL; 1633d4af9e69SDag-Erling Smørgrav } 163447dd1d1bSDag-Erling Smørgrav debug("Tunnel forwarding using interface %s", ifname); 1635d4af9e69SDag-Erling Smørgrav 16364f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, "tun", SSH_CHANNEL_OPENING, fd, fd, -1, 163760c59fadSDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1); 1638d4af9e69SDag-Erling Smørgrav c->datagram = 1; 1639d4af9e69SDag-Erling Smørgrav 1640d4af9e69SDag-Erling Smørgrav #if defined(SSH_TUN_FILTER) 1641d4af9e69SDag-Erling Smørgrav if (options.tun_open == SSH_TUNMODE_POINTOPOINT) 16424f52dfbbSDag-Erling Smørgrav channel_register_filter(ssh, c->self, sys_tun_infilter, 1643d4af9e69SDag-Erling Smørgrav sys_tun_outfilter, NULL, NULL); 1644d4af9e69SDag-Erling Smørgrav #endif 1645d4af9e69SDag-Erling Smørgrav 164619261079SEd Maste if (cb != NULL) 164719261079SEd Maste channel_register_open_confirm(ssh, c->self, cb, cbctx); 164819261079SEd Maste 164919261079SEd Maste if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN)) != 0 || 165019261079SEd Maste (r = sshpkt_put_cstring(ssh, "tun@openssh.com")) != 0 || 165119261079SEd Maste (r = sshpkt_put_u32(ssh, c->self)) != 0 || 165219261079SEd Maste (r = sshpkt_put_u32(ssh, c->local_window_max)) != 0 || 165319261079SEd Maste (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0 || 165419261079SEd Maste (r = sshpkt_put_u32(ssh, tun_mode)) != 0 || 165519261079SEd Maste (r = sshpkt_put_u32(ssh, remote_tun)) != 0 || 165619261079SEd Maste (r = sshpkt_send(ssh)) != 0) 165719261079SEd Maste sshpkt_fatal(ssh, r, "%s: send reply", __func__); 1658d4af9e69SDag-Erling Smørgrav 165947dd1d1bSDag-Erling Smørgrav return ifname; 1660d4af9e69SDag-Erling Smørgrav } 1661d4af9e69SDag-Erling Smørgrav 1662a04a10f8SKris Kennaway /* XXXX move to generic input handler */ 1663bc5531deSDag-Erling Smørgrav static int 16644f52dfbbSDag-Erling Smørgrav client_input_channel_open(int type, u_int32_t seq, struct ssh *ssh) 1665a04a10f8SKris Kennaway { 1666a04a10f8SKris Kennaway Channel *c = NULL; 166719261079SEd Maste char *ctype = NULL; 166819261079SEd Maste int r; 166919261079SEd Maste u_int rchan; 167019261079SEd Maste size_t len; 167119261079SEd Maste u_int rmaxpack, rwindow; 1672a04a10f8SKris Kennaway 167319261079SEd Maste if ((r = sshpkt_get_cstring(ssh, &ctype, &len)) != 0 || 167419261079SEd Maste (r = sshpkt_get_u32(ssh, &rchan)) != 0 || 167519261079SEd Maste (r = sshpkt_get_u32(ssh, &rwindow)) != 0 || 167619261079SEd Maste (r = sshpkt_get_u32(ssh, &rmaxpack)) != 0) 167719261079SEd Maste goto out; 1678a04a10f8SKris Kennaway 1679a04a10f8SKris Kennaway debug("client_input_channel_open: ctype %s rchan %d win %d max %d", 1680a04a10f8SKris Kennaway ctype, rchan, rwindow, rmaxpack); 1681a04a10f8SKris Kennaway 16821e8db6e2SBrian Feldman if (strcmp(ctype, "forwarded-tcpip") == 0) { 16834f52dfbbSDag-Erling Smørgrav c = client_request_forwarded_tcpip(ssh, ctype, rchan, rwindow, 1684ca86bcf2SDag-Erling Smørgrav rmaxpack); 1685a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(ctype, "forwarded-streamlocal@openssh.com") == 0) { 16864f52dfbbSDag-Erling Smørgrav c = client_request_forwarded_streamlocal(ssh, ctype, rchan); 16871e8db6e2SBrian Feldman } else if (strcmp(ctype, "x11") == 0) { 16884f52dfbbSDag-Erling Smørgrav c = client_request_x11(ssh, ctype, rchan); 16891e8db6e2SBrian Feldman } else if (strcmp(ctype, "auth-agent@openssh.com") == 0) { 16904f52dfbbSDag-Erling Smørgrav c = client_request_agent(ssh, ctype, rchan); 1691a04a10f8SKris Kennaway } 1692ca86bcf2SDag-Erling Smørgrav if (c != NULL && c->type == SSH_CHANNEL_MUX_CLIENT) { 1693ca86bcf2SDag-Erling Smørgrav debug3("proxied to downstream: %s", ctype); 1694ca86bcf2SDag-Erling Smørgrav } else if (c != NULL) { 1695a04a10f8SKris Kennaway debug("confirm %s", ctype); 1696a04a10f8SKris Kennaway c->remote_id = rchan; 16974f52dfbbSDag-Erling Smørgrav c->have_remote_id = 1; 1698a04a10f8SKris Kennaway c->remote_window = rwindow; 1699a04a10f8SKris Kennaway c->remote_maxpacket = rmaxpack; 1700ae1f160dSDag-Erling Smørgrav if (c->type != SSH_CHANNEL_CONNECTING) { 170119261079SEd Maste if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 || 170219261079SEd Maste (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 170319261079SEd Maste (r = sshpkt_put_u32(ssh, c->self)) != 0 || 170419261079SEd Maste (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || 170519261079SEd Maste (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0 || 170619261079SEd Maste (r = sshpkt_send(ssh)) != 0) 170719261079SEd Maste sshpkt_fatal(ssh, r, "%s: send reply", __func__); 1708ae1f160dSDag-Erling Smørgrav } 1709a04a10f8SKris Kennaway } else { 1710a04a10f8SKris Kennaway debug("failure %s", ctype); 171119261079SEd Maste if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_FAILURE)) != 0 || 171219261079SEd Maste (r = sshpkt_put_u32(ssh, rchan)) != 0 || 171319261079SEd Maste (r = sshpkt_put_u32(ssh, SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED)) != 0 || 171419261079SEd Maste (r = sshpkt_put_cstring(ssh, "open failed")) != 0 || 171519261079SEd Maste (r = sshpkt_put_cstring(ssh, "")) != 0 || 171619261079SEd Maste (r = sshpkt_send(ssh)) != 0) 171719261079SEd Maste sshpkt_fatal(ssh, r, "%s: send failure", __func__); 1718a04a10f8SKris Kennaway } 171919261079SEd Maste r = 0; 172019261079SEd Maste out: 1721e4a9863fSDag-Erling Smørgrav free(ctype); 172219261079SEd Maste return r; 1723a04a10f8SKris Kennaway } 1724bc5531deSDag-Erling Smørgrav 1725bc5531deSDag-Erling Smørgrav static int 17264f52dfbbSDag-Erling Smørgrav client_input_channel_req(int type, u_int32_t seq, struct ssh *ssh) 17271e8db6e2SBrian Feldman { 17281e8db6e2SBrian Feldman Channel *c = NULL; 172919261079SEd Maste char *rtype = NULL; 173019261079SEd Maste u_char reply; 173119261079SEd Maste u_int id, exitval; 173219261079SEd Maste int r, success = 0; 17331e8db6e2SBrian Feldman 173419261079SEd Maste if ((r = sshpkt_get_u32(ssh, &id)) != 0) 173519261079SEd Maste return r; 173619261079SEd Maste if (id <= INT_MAX) 17374f52dfbbSDag-Erling Smørgrav c = channel_lookup(ssh, id); 17384f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 1739ca86bcf2SDag-Erling Smørgrav return 0; 174019261079SEd Maste if ((r = sshpkt_get_cstring(ssh, &rtype, NULL)) != 0 || 174119261079SEd Maste (r = sshpkt_get_u8(ssh, &reply)) != 0) 174219261079SEd Maste goto out; 17431e8db6e2SBrian Feldman 174419261079SEd Maste debug("client_input_channel_req: channel %u rtype %s reply %d", 17451e8db6e2SBrian Feldman id, rtype, reply); 17461e8db6e2SBrian Feldman 174719261079SEd Maste if (c == NULL) { 1748d4af9e69SDag-Erling Smørgrav error("client_input_channel_req: channel %d: " 1749d4af9e69SDag-Erling Smørgrav "unknown channel", id); 1750d4af9e69SDag-Erling Smørgrav } else if (strcmp(rtype, "eow@openssh.com") == 0) { 175119261079SEd Maste if ((r = sshpkt_get_end(ssh)) != 0) 175219261079SEd Maste goto out; 17534f52dfbbSDag-Erling Smørgrav chan_rcvd_eow(ssh, c); 17541e8db6e2SBrian Feldman } else if (strcmp(rtype, "exit-status") == 0) { 175519261079SEd Maste if ((r = sshpkt_get_u32(ssh, &exitval)) != 0) 175619261079SEd Maste goto out; 1757b15c8340SDag-Erling Smørgrav if (c->ctl_chan != -1) { 17584f52dfbbSDag-Erling Smørgrav mux_exit_message(ssh, c, exitval); 1759b15c8340SDag-Erling Smørgrav success = 1; 176019261079SEd Maste } else if ((int)id == session_ident) { 1761b15c8340SDag-Erling Smørgrav /* Record exit value of local session */ 17621e8db6e2SBrian Feldman success = 1; 1763d74d50a8SDag-Erling Smørgrav exit_status = exitval; 1764d74d50a8SDag-Erling Smørgrav } else { 1765b15c8340SDag-Erling Smørgrav /* Probably for a mux channel that has already closed */ 176619261079SEd Maste debug_f("no sink for exit-status on channel %d", 176719261079SEd Maste id); 1768d74d50a8SDag-Erling Smørgrav } 176919261079SEd Maste if ((r = sshpkt_get_end(ssh)) != 0) 177019261079SEd Maste goto out; 17711e8db6e2SBrian Feldman } 1772a0ee8cc6SDag-Erling Smørgrav if (reply && c != NULL && !(c->flags & CHAN_CLOSE_SENT)) { 17734f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id) 177419261079SEd Maste fatal_f("channel %d: no remote_id", c->self); 177519261079SEd Maste if ((r = sshpkt_start(ssh, success ? 177619261079SEd Maste SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE)) != 0 || 177719261079SEd Maste (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 177819261079SEd Maste (r = sshpkt_send(ssh)) != 0) 177919261079SEd Maste sshpkt_fatal(ssh, r, "%s: send failure", __func__); 17801e8db6e2SBrian Feldman } 178119261079SEd Maste r = 0; 178219261079SEd Maste out: 1783e4a9863fSDag-Erling Smørgrav free(rtype); 178419261079SEd Maste return r; 17851e8db6e2SBrian Feldman } 1786bc5531deSDag-Erling Smørgrav 1787bc5531deSDag-Erling Smørgrav struct hostkeys_update_ctx { 1788bc5531deSDag-Erling Smørgrav /* The hostname and (optionally) IP address string for the server */ 1789bc5531deSDag-Erling Smørgrav char *host_str, *ip_str; 1790bc5531deSDag-Erling Smørgrav 1791bc5531deSDag-Erling Smørgrav /* 1792bc5531deSDag-Erling Smørgrav * Keys received from the server and a flag for each indicating 1793bc5531deSDag-Erling Smørgrav * whether they already exist in known_hosts. 179419261079SEd Maste * keys_match is filled in by hostkeys_find() and later (for new 1795bc5531deSDag-Erling Smørgrav * keys) by client_global_hostkeys_private_confirm(). 1796bc5531deSDag-Erling Smørgrav */ 1797bc5531deSDag-Erling Smørgrav struct sshkey **keys; 179819261079SEd Maste u_int *keys_match; /* mask of HKF_MATCH_* from hostfile.h */ 179919261079SEd Maste int *keys_verified; /* flag for new keys verified by server */ 180019261079SEd Maste size_t nkeys, nnew, nincomplete; /* total, new keys, incomplete match */ 1801bc5531deSDag-Erling Smørgrav 1802bc5531deSDag-Erling Smørgrav /* 1803bc5531deSDag-Erling Smørgrav * Keys that are in known_hosts, but were not present in the update 1804bc5531deSDag-Erling Smørgrav * from the server (i.e. scheduled to be deleted). 1805bc5531deSDag-Erling Smørgrav * Filled in by hostkeys_find(). 1806bc5531deSDag-Erling Smørgrav */ 1807bc5531deSDag-Erling Smørgrav struct sshkey **old_keys; 1808bc5531deSDag-Erling Smørgrav size_t nold; 180919261079SEd Maste 181019261079SEd Maste /* Various special cases. */ 181119261079SEd Maste int complex_hostspec; /* wildcard or manual pattern-list host name */ 181219261079SEd Maste int ca_available; /* saw CA key for this host */ 181319261079SEd Maste int old_key_seen; /* saw old key with other name/addr */ 181419261079SEd Maste int other_name_seen; /* saw key with other name/addr */ 1815bc5531deSDag-Erling Smørgrav }; 1816bc5531deSDag-Erling Smørgrav 1817ae1f160dSDag-Erling Smørgrav static void 1818bc5531deSDag-Erling Smørgrav hostkeys_update_ctx_free(struct hostkeys_update_ctx *ctx) 1819bc5531deSDag-Erling Smørgrav { 1820bc5531deSDag-Erling Smørgrav size_t i; 1821bc5531deSDag-Erling Smørgrav 1822bc5531deSDag-Erling Smørgrav if (ctx == NULL) 1823bc5531deSDag-Erling Smørgrav return; 1824bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) 1825bc5531deSDag-Erling Smørgrav sshkey_free(ctx->keys[i]); 1826bc5531deSDag-Erling Smørgrav free(ctx->keys); 182719261079SEd Maste free(ctx->keys_match); 182819261079SEd Maste free(ctx->keys_verified); 1829bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nold; i++) 1830bc5531deSDag-Erling Smørgrav sshkey_free(ctx->old_keys[i]); 1831bc5531deSDag-Erling Smørgrav free(ctx->old_keys); 1832bc5531deSDag-Erling Smørgrav free(ctx->host_str); 1833bc5531deSDag-Erling Smørgrav free(ctx->ip_str); 1834bc5531deSDag-Erling Smørgrav free(ctx); 1835bc5531deSDag-Erling Smørgrav } 1836bc5531deSDag-Erling Smørgrav 183719261079SEd Maste /* 183819261079SEd Maste * Returns non-zero if a known_hosts hostname list is not of a form that 183919261079SEd Maste * can be handled by UpdateHostkeys. These include wildcard hostnames and 184019261079SEd Maste * hostnames lists that do not follow the form host[,ip]. 184119261079SEd Maste */ 184219261079SEd Maste static int 184319261079SEd Maste hostspec_is_complex(const char *hosts) 184419261079SEd Maste { 184519261079SEd Maste char *cp; 184619261079SEd Maste 184719261079SEd Maste /* wildcard */ 184819261079SEd Maste if (strchr(hosts, '*') != NULL || strchr(hosts, '?') != NULL) 184919261079SEd Maste return 1; 185019261079SEd Maste /* single host/ip = ok */ 185119261079SEd Maste if ((cp = strchr(hosts, ',')) == NULL) 185219261079SEd Maste return 0; 185319261079SEd Maste /* more than two entries on the line */ 185419261079SEd Maste if (strchr(cp + 1, ',') != NULL) 185519261079SEd Maste return 1; 185619261079SEd Maste /* XXX maybe parse cp+1 and ensure it is an IP? */ 185719261079SEd Maste return 0; 185819261079SEd Maste } 185919261079SEd Maste 186019261079SEd Maste /* callback to search for ctx->keys in known_hosts */ 1861bc5531deSDag-Erling Smørgrav static int 1862bc5531deSDag-Erling Smørgrav hostkeys_find(struct hostkey_foreach_line *l, void *_ctx) 1863bc5531deSDag-Erling Smørgrav { 1864bc5531deSDag-Erling Smørgrav struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; 1865bc5531deSDag-Erling Smørgrav size_t i; 1866bc5531deSDag-Erling Smørgrav struct sshkey **tmp; 1867bc5531deSDag-Erling Smørgrav 186819261079SEd Maste if (l->key == NULL) 1869bc5531deSDag-Erling Smørgrav return 0; 187019261079SEd Maste if (l->status != HKF_STATUS_MATCHED) { 187119261079SEd Maste /* Record if one of the keys appears on a non-matching line */ 187219261079SEd Maste for (i = 0; i < ctx->nkeys; i++) { 187319261079SEd Maste if (sshkey_equal(l->key, ctx->keys[i])) { 187419261079SEd Maste ctx->other_name_seen = 1; 187519261079SEd Maste debug3_f("found %s key under different " 187619261079SEd Maste "name/addr at %s:%ld", 187719261079SEd Maste sshkey_ssh_name(ctx->keys[i]), 187819261079SEd Maste l->path, l->linenum); 187919261079SEd Maste return 0; 188019261079SEd Maste } 188119261079SEd Maste } 188219261079SEd Maste return 0; 188319261079SEd Maste } 188419261079SEd Maste /* Don't proceed if revocation or CA markers are present */ 188519261079SEd Maste /* XXX relax this */ 188619261079SEd Maste if (l->marker != MRK_NONE) { 188719261079SEd Maste debug3_f("hostkeys file %s:%ld has CA/revocation marker", 188819261079SEd Maste l->path, l->linenum); 188919261079SEd Maste ctx->complex_hostspec = 1; 189019261079SEd Maste return 0; 189119261079SEd Maste } 189219261079SEd Maste 189319261079SEd Maste /* If CheckHostIP is enabled, then check for mismatched hostname/addr */ 189419261079SEd Maste if (ctx->ip_str != NULL && strchr(l->hosts, ',') != NULL) { 189519261079SEd Maste if ((l->match & HKF_MATCH_HOST) == 0) { 189619261079SEd Maste /* Record if address matched a different hostname. */ 189719261079SEd Maste ctx->other_name_seen = 1; 189819261079SEd Maste debug3_f("found address %s against different hostname " 189919261079SEd Maste "at %s:%ld", ctx->ip_str, l->path, l->linenum); 190019261079SEd Maste return 0; 190119261079SEd Maste } else if ((l->match & HKF_MATCH_IP) == 0) { 190219261079SEd Maste /* Record if hostname matched a different address. */ 190319261079SEd Maste ctx->other_name_seen = 1; 190419261079SEd Maste debug3_f("found hostname %s against different address " 190519261079SEd Maste "at %s:%ld", ctx->host_str, l->path, l->linenum); 190619261079SEd Maste } 190719261079SEd Maste } 190819261079SEd Maste 190919261079SEd Maste /* 191019261079SEd Maste * UpdateHostkeys is skipped for wildcard host names and hostnames 191119261079SEd Maste * that contain more than two entries (ssh never writes these). 191219261079SEd Maste */ 191319261079SEd Maste if (hostspec_is_complex(l->hosts)) { 191419261079SEd Maste debug3_f("hostkeys file %s:%ld complex host specification", 191519261079SEd Maste l->path, l->linenum); 191619261079SEd Maste ctx->complex_hostspec = 1; 191719261079SEd Maste return 0; 191819261079SEd Maste } 1919bc5531deSDag-Erling Smørgrav 1920bc5531deSDag-Erling Smørgrav /* Mark off keys we've already seen for this host */ 1921bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) { 192219261079SEd Maste if (!sshkey_equal(l->key, ctx->keys[i])) 192319261079SEd Maste continue; 192419261079SEd Maste debug3_f("found %s key at %s:%ld", 1925bc5531deSDag-Erling Smørgrav sshkey_ssh_name(ctx->keys[i]), l->path, l->linenum); 192619261079SEd Maste ctx->keys_match[i] |= l->match; 1927bc5531deSDag-Erling Smørgrav return 0; 1928bc5531deSDag-Erling Smørgrav } 1929bc5531deSDag-Erling Smørgrav /* This line contained a key that not offered by the server */ 193019261079SEd Maste debug3_f("deprecated %s key at %s:%ld", sshkey_ssh_name(l->key), 193119261079SEd Maste l->path, l->linenum); 19324f52dfbbSDag-Erling Smørgrav if ((tmp = recallocarray(ctx->old_keys, ctx->nold, ctx->nold + 1, 1933bc5531deSDag-Erling Smørgrav sizeof(*ctx->old_keys))) == NULL) 193419261079SEd Maste fatal_f("recallocarray failed nold = %zu", ctx->nold); 1935bc5531deSDag-Erling Smørgrav ctx->old_keys = tmp; 1936bc5531deSDag-Erling Smørgrav ctx->old_keys[ctx->nold++] = l->key; 1937bc5531deSDag-Erling Smørgrav l->key = NULL; 1938bc5531deSDag-Erling Smørgrav 1939bc5531deSDag-Erling Smørgrav return 0; 1940bc5531deSDag-Erling Smørgrav } 1941bc5531deSDag-Erling Smørgrav 194219261079SEd Maste /* callback to search for ctx->old_keys in known_hosts under other names */ 194319261079SEd Maste static int 194419261079SEd Maste hostkeys_check_old(struct hostkey_foreach_line *l, void *_ctx) 194519261079SEd Maste { 194619261079SEd Maste struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; 194719261079SEd Maste size_t i; 194819261079SEd Maste int hashed; 194919261079SEd Maste 195019261079SEd Maste /* only care about lines that *don't* match the active host spec */ 195119261079SEd Maste if (l->status == HKF_STATUS_MATCHED || l->key == NULL) 195219261079SEd Maste return 0; 195319261079SEd Maste 195419261079SEd Maste hashed = l->match & (HKF_MATCH_HOST_HASHED|HKF_MATCH_IP_HASHED); 195519261079SEd Maste for (i = 0; i < ctx->nold; i++) { 195619261079SEd Maste if (!sshkey_equal(l->key, ctx->old_keys[i])) 195719261079SEd Maste continue; 195819261079SEd Maste debug3_f("found deprecated %s key at %s:%ld as %s", 195919261079SEd Maste sshkey_ssh_name(ctx->old_keys[i]), l->path, l->linenum, 196019261079SEd Maste hashed ? "[HASHED]" : l->hosts); 196119261079SEd Maste ctx->old_key_seen = 1; 196219261079SEd Maste break; 196319261079SEd Maste } 196419261079SEd Maste return 0; 196519261079SEd Maste } 196619261079SEd Maste 196719261079SEd Maste /* 196819261079SEd Maste * Check known_hosts files for deprecated keys under other names. Returns 0 196919261079SEd Maste * on success or -1 on failure. Updates ctx->old_key_seen if deprecated keys 197019261079SEd Maste * exist under names other than the active hostname/IP. 197119261079SEd Maste */ 197219261079SEd Maste static int 197319261079SEd Maste check_old_keys_othernames(struct hostkeys_update_ctx *ctx) 197419261079SEd Maste { 197519261079SEd Maste size_t i; 197619261079SEd Maste int r; 197719261079SEd Maste 197819261079SEd Maste debug2_f("checking for %zu deprecated keys", ctx->nold); 197919261079SEd Maste for (i = 0; i < options.num_user_hostfiles; i++) { 198019261079SEd Maste debug3_f("searching %s for %s / %s", 198119261079SEd Maste options.user_hostfiles[i], ctx->host_str, 198219261079SEd Maste ctx->ip_str ? ctx->ip_str : "(none)"); 198319261079SEd Maste if ((r = hostkeys_foreach(options.user_hostfiles[i], 198419261079SEd Maste hostkeys_check_old, ctx, ctx->host_str, ctx->ip_str, 198519261079SEd Maste HKF_WANT_PARSE_KEY, 0)) != 0) { 198619261079SEd Maste if (r == SSH_ERR_SYSTEM_ERROR && errno == ENOENT) { 198719261079SEd Maste debug_f("hostkeys file %s does not exist", 198819261079SEd Maste options.user_hostfiles[i]); 198919261079SEd Maste continue; 199019261079SEd Maste } 199119261079SEd Maste error_fr(r, "hostkeys_foreach failed for %s", 199219261079SEd Maste options.user_hostfiles[i]); 199319261079SEd Maste return -1; 199419261079SEd Maste } 199519261079SEd Maste } 199619261079SEd Maste return 0; 199719261079SEd Maste } 199819261079SEd Maste 199919261079SEd Maste static void 200019261079SEd Maste hostkey_change_preamble(LogLevel loglevel) 200119261079SEd Maste { 200219261079SEd Maste do_log2(loglevel, "The server has updated its host keys."); 200319261079SEd Maste do_log2(loglevel, "These changes were verified by the server's " 200419261079SEd Maste "existing trusted key."); 200519261079SEd Maste } 200619261079SEd Maste 2007bc5531deSDag-Erling Smørgrav static void 2008bc5531deSDag-Erling Smørgrav update_known_hosts(struct hostkeys_update_ctx *ctx) 2009bc5531deSDag-Erling Smørgrav { 201019261079SEd Maste int r, was_raw = 0, first = 1; 201119261079SEd Maste int asking = options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK; 201219261079SEd Maste LogLevel loglevel = asking ? SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_VERBOSE; 2013bc5531deSDag-Erling Smørgrav char *fp, *response; 2014bc5531deSDag-Erling Smørgrav size_t i; 201519261079SEd Maste struct stat sb; 2016bc5531deSDag-Erling Smørgrav 2017bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) { 201819261079SEd Maste if (!ctx->keys_verified[i]) 2019bc5531deSDag-Erling Smørgrav continue; 2020bc5531deSDag-Erling Smørgrav if ((fp = sshkey_fingerprint(ctx->keys[i], 2021bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) 202219261079SEd Maste fatal_f("sshkey_fingerprint failed"); 202319261079SEd Maste if (first && asking) 202419261079SEd Maste hostkey_change_preamble(loglevel); 2025bc5531deSDag-Erling Smørgrav do_log2(loglevel, "Learned new hostkey: %s %s", 2026bc5531deSDag-Erling Smørgrav sshkey_type(ctx->keys[i]), fp); 202719261079SEd Maste first = 0; 2028bc5531deSDag-Erling Smørgrav free(fp); 2029bc5531deSDag-Erling Smørgrav } 2030bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nold; i++) { 2031bc5531deSDag-Erling Smørgrav if ((fp = sshkey_fingerprint(ctx->old_keys[i], 2032bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) 203319261079SEd Maste fatal_f("sshkey_fingerprint failed"); 203419261079SEd Maste if (first && asking) 203519261079SEd Maste hostkey_change_preamble(loglevel); 2036bc5531deSDag-Erling Smørgrav do_log2(loglevel, "Deprecating obsolete hostkey: %s %s", 2037bc5531deSDag-Erling Smørgrav sshkey_type(ctx->old_keys[i]), fp); 203819261079SEd Maste first = 0; 2039bc5531deSDag-Erling Smørgrav free(fp); 2040bc5531deSDag-Erling Smørgrav } 2041bc5531deSDag-Erling Smørgrav if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK) { 2042bc5531deSDag-Erling Smørgrav if (get_saved_tio() != NULL) { 2043bc5531deSDag-Erling Smørgrav leave_raw_mode(1); 2044bc5531deSDag-Erling Smørgrav was_raw = 1; 2045bc5531deSDag-Erling Smørgrav } 2046bc5531deSDag-Erling Smørgrav response = NULL; 2047bc5531deSDag-Erling Smørgrav for (i = 0; !quit_pending && i < 3; i++) { 2048bc5531deSDag-Erling Smørgrav free(response); 2049bc5531deSDag-Erling Smørgrav response = read_passphrase("Accept updated hostkeys? " 2050bc5531deSDag-Erling Smørgrav "(yes/no): ", RP_ECHO); 2051bc5531deSDag-Erling Smørgrav if (strcasecmp(response, "yes") == 0) 2052bc5531deSDag-Erling Smørgrav break; 2053bc5531deSDag-Erling Smørgrav else if (quit_pending || response == NULL || 2054bc5531deSDag-Erling Smørgrav strcasecmp(response, "no") == 0) { 2055bc5531deSDag-Erling Smørgrav options.update_hostkeys = 0; 2056bc5531deSDag-Erling Smørgrav break; 2057bc5531deSDag-Erling Smørgrav } else { 2058bc5531deSDag-Erling Smørgrav do_log2(loglevel, "Please enter " 2059bc5531deSDag-Erling Smørgrav "\"yes\" or \"no\""); 2060bc5531deSDag-Erling Smørgrav } 2061bc5531deSDag-Erling Smørgrav } 2062bc5531deSDag-Erling Smørgrav if (quit_pending || i >= 3 || response == NULL) 2063bc5531deSDag-Erling Smørgrav options.update_hostkeys = 0; 2064bc5531deSDag-Erling Smørgrav free(response); 2065bc5531deSDag-Erling Smørgrav if (was_raw) 2066bc5531deSDag-Erling Smørgrav enter_raw_mode(1); 2067bc5531deSDag-Erling Smørgrav } 206819261079SEd Maste if (options.update_hostkeys == 0) 206919261079SEd Maste return; 2070bc5531deSDag-Erling Smørgrav /* 2071bc5531deSDag-Erling Smørgrav * Now that all the keys are verified, we can go ahead and replace 2072bc5531deSDag-Erling Smørgrav * them in known_hosts (assuming SSH_UPDATE_HOSTKEYS_ASK didn't 2073bc5531deSDag-Erling Smørgrav * cancel the operation). 2074bc5531deSDag-Erling Smørgrav */ 207519261079SEd Maste for (i = 0; i < options.num_user_hostfiles; i++) { 207619261079SEd Maste /* 207719261079SEd Maste * NB. keys are only added to hostfiles[0], for the rest we 207819261079SEd Maste * just delete the hostname entries. 207919261079SEd Maste */ 208019261079SEd Maste if (stat(options.user_hostfiles[i], &sb) != 0) { 208119261079SEd Maste if (errno == ENOENT) { 208219261079SEd Maste debug_f("known hosts file %s does not " 208319261079SEd Maste "exist", options.user_hostfiles[i]); 208419261079SEd Maste } else { 208519261079SEd Maste error_f("known hosts file %s " 208619261079SEd Maste "inaccessible: %s", 208719261079SEd Maste options.user_hostfiles[i], strerror(errno)); 208819261079SEd Maste } 208919261079SEd Maste continue; 209019261079SEd Maste } 209119261079SEd Maste if ((r = hostfile_replace_entries(options.user_hostfiles[i], 209219261079SEd Maste ctx->host_str, ctx->ip_str, 209319261079SEd Maste i == 0 ? ctx->keys : NULL, i == 0 ? ctx->nkeys : 0, 2094bc5531deSDag-Erling Smørgrav options.hash_known_hosts, 0, 209519261079SEd Maste options.fingerprint_hash)) != 0) { 209619261079SEd Maste error_fr(r, "hostfile_replace_entries failed for %s", 209719261079SEd Maste options.user_hostfiles[i]); 209819261079SEd Maste } 209919261079SEd Maste } 2100bc5531deSDag-Erling Smørgrav } 2101bc5531deSDag-Erling Smørgrav 2102bc5531deSDag-Erling Smørgrav static void 21034f52dfbbSDag-Erling Smørgrav client_global_hostkeys_private_confirm(struct ssh *ssh, int type, 21044f52dfbbSDag-Erling Smørgrav u_int32_t seq, void *_ctx) 2105bc5531deSDag-Erling Smørgrav { 2106bc5531deSDag-Erling Smørgrav struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; 2107bc5531deSDag-Erling Smørgrav size_t i, ndone; 2108bc5531deSDag-Erling Smørgrav struct sshbuf *signdata; 21091323ec57SEd Maste int r, plaintype; 2110bc5531deSDag-Erling Smørgrav const u_char *sig; 21111323ec57SEd Maste const char *rsa_kexalg = NULL; 21121323ec57SEd Maste char *alg = NULL; 2113bc5531deSDag-Erling Smørgrav size_t siglen; 2114bc5531deSDag-Erling Smørgrav 2115bc5531deSDag-Erling Smørgrav if (ctx->nnew == 0) 211619261079SEd Maste fatal_f("ctx->nnew == 0"); /* sanity */ 2117bc5531deSDag-Erling Smørgrav if (type != SSH2_MSG_REQUEST_SUCCESS) { 2118bc5531deSDag-Erling Smørgrav error("Server failed to confirm ownership of " 2119bc5531deSDag-Erling Smørgrav "private host keys"); 2120bc5531deSDag-Erling Smørgrav hostkeys_update_ctx_free(ctx); 2121bc5531deSDag-Erling Smørgrav return; 2122bc5531deSDag-Erling Smørgrav } 21231323ec57SEd Maste if (sshkey_type_plain(sshkey_type_from_name( 21241323ec57SEd Maste ssh->kex->hostkey_alg)) == KEY_RSA) 21251323ec57SEd Maste rsa_kexalg = ssh->kex->hostkey_alg; 2126bc5531deSDag-Erling Smørgrav if ((signdata = sshbuf_new()) == NULL) 212719261079SEd Maste fatal_f("sshbuf_new failed"); 2128bc5531deSDag-Erling Smørgrav /* 2129bc5531deSDag-Erling Smørgrav * Expect a signature for each of the ctx->nnew private keys we 2130bc5531deSDag-Erling Smørgrav * haven't seen before. They will be in the same order as the 213119261079SEd Maste * ctx->keys where the corresponding ctx->keys_match[i] == 0. 2132bc5531deSDag-Erling Smørgrav */ 2133bc5531deSDag-Erling Smørgrav for (ndone = i = 0; i < ctx->nkeys; i++) { 213419261079SEd Maste if (ctx->keys_match[i]) 2135bc5531deSDag-Erling Smørgrav continue; 21361323ec57SEd Maste plaintype = sshkey_type_plain(ctx->keys[i]->type); 2137bc5531deSDag-Erling Smørgrav /* Prepare data to be signed: session ID, unique string, key */ 2138bc5531deSDag-Erling Smørgrav sshbuf_reset(signdata); 2139bc5531deSDag-Erling Smørgrav if ( (r = sshbuf_put_cstring(signdata, 2140bc5531deSDag-Erling Smørgrav "hostkeys-prove-00@openssh.com")) != 0 || 214119261079SEd Maste (r = sshbuf_put_stringb(signdata, 214219261079SEd Maste ssh->kex->session_id)) != 0 || 2143bc5531deSDag-Erling Smørgrav (r = sshkey_puts(ctx->keys[i], signdata)) != 0) 214419261079SEd Maste fatal_fr(r, "compose signdata"); 2145bc5531deSDag-Erling Smørgrav /* Extract and verify signature */ 2146bc5531deSDag-Erling Smørgrav if ((r = sshpkt_get_string_direct(ssh, &sig, &siglen)) != 0) { 214719261079SEd Maste error_fr(r, "parse sig"); 2148bc5531deSDag-Erling Smørgrav goto out; 2149bc5531deSDag-Erling Smørgrav } 21501323ec57SEd Maste if ((r = sshkey_get_sigtype(sig, siglen, &alg)) != 0) { 21511323ec57SEd Maste error_fr(r, "server gave unintelligible signature " 21521323ec57SEd Maste "for %s key %zu", sshkey_type(ctx->keys[i]), i); 21531323ec57SEd Maste goto out; 21541323ec57SEd Maste } 215547dd1d1bSDag-Erling Smørgrav /* 21561323ec57SEd Maste * Special case for RSA keys: if a RSA hostkey was negotiated, 21571323ec57SEd Maste * then use its signature type for verification of RSA hostkey 21581323ec57SEd Maste * proofs. Otherwise, accept only RSA-SHA256/512 signatures. 215947dd1d1bSDag-Erling Smørgrav */ 21601323ec57SEd Maste if (plaintype == KEY_RSA && rsa_kexalg == NULL && 21611323ec57SEd Maste match_pattern_list(alg, HOSTKEY_PROOF_RSA_ALGS, 0) != 1) { 21621323ec57SEd Maste debug_f("server used untrusted RSA signature algorithm " 21631323ec57SEd Maste "%s for key %zu, disregarding", alg, i); 21641323ec57SEd Maste free(alg); 21651323ec57SEd Maste /* zap the key from the list */ 21661323ec57SEd Maste sshkey_free(ctx->keys[i]); 21671323ec57SEd Maste ctx->keys[i] = NULL; 21681323ec57SEd Maste ndone++; 21691323ec57SEd Maste continue; 21701323ec57SEd Maste } 21711323ec57SEd Maste debug3_f("verify %s key %zu using sigalg %s", 21721323ec57SEd Maste sshkey_type(ctx->keys[i]), i, alg); 21731323ec57SEd Maste free(alg); 2174bc5531deSDag-Erling Smørgrav if ((r = sshkey_verify(ctx->keys[i], sig, siglen, 217547dd1d1bSDag-Erling Smørgrav sshbuf_ptr(signdata), sshbuf_len(signdata), 21761323ec57SEd Maste plaintype == KEY_RSA ? rsa_kexalg : NULL, 0, NULL)) != 0) { 217719261079SEd Maste error_fr(r, "server gave bad signature for %s key %zu", 217819261079SEd Maste sshkey_type(ctx->keys[i]), i); 2179bc5531deSDag-Erling Smørgrav goto out; 2180bc5531deSDag-Erling Smørgrav } 2181bc5531deSDag-Erling Smørgrav /* Key is good. Mark it as 'seen' */ 218219261079SEd Maste ctx->keys_verified[i] = 1; 2183bc5531deSDag-Erling Smørgrav ndone++; 2184bc5531deSDag-Erling Smørgrav } 218519261079SEd Maste /* Shouldn't happen */ 2186bc5531deSDag-Erling Smørgrav if (ndone != ctx->nnew) 218719261079SEd Maste fatal_f("ndone != ctx->nnew (%zu / %zu)", ndone, ctx->nnew); 218819261079SEd Maste if ((r = sshpkt_get_end(ssh)) != 0) { 218919261079SEd Maste error_f("protocol error"); 219019261079SEd Maste goto out; 219119261079SEd Maste } 2192bc5531deSDag-Erling Smørgrav 2193bc5531deSDag-Erling Smørgrav /* Make the edits to known_hosts */ 2194bc5531deSDag-Erling Smørgrav update_known_hosts(ctx); 2195bc5531deSDag-Erling Smørgrav out: 2196bc5531deSDag-Erling Smørgrav hostkeys_update_ctx_free(ctx); 2197bc5531deSDag-Erling Smørgrav } 2198bc5531deSDag-Erling Smørgrav 2199bc5531deSDag-Erling Smørgrav /* 2200d93a896eSDag-Erling Smørgrav * Returns non-zero if the key is accepted by HostkeyAlgorithms. 2201d93a896eSDag-Erling Smørgrav * Made slightly less trivial by the multiple RSA signature algorithm names. 2202d93a896eSDag-Erling Smørgrav */ 2203d93a896eSDag-Erling Smørgrav static int 2204d93a896eSDag-Erling Smørgrav key_accepted_by_hostkeyalgs(const struct sshkey *key) 2205d93a896eSDag-Erling Smørgrav { 2206d93a896eSDag-Erling Smørgrav const char *ktype = sshkey_ssh_name(key); 220719261079SEd Maste const char *hostkeyalgs = options.hostkeyalgorithms; 2208d93a896eSDag-Erling Smørgrav 2209d93a896eSDag-Erling Smørgrav if (key == NULL || key->type == KEY_UNSPEC) 2210d93a896eSDag-Erling Smørgrav return 0; 2211d93a896eSDag-Erling Smørgrav if (key->type == KEY_RSA && 2212d93a896eSDag-Erling Smørgrav (match_pattern_list("rsa-sha2-256", hostkeyalgs, 0) == 1 || 2213d93a896eSDag-Erling Smørgrav match_pattern_list("rsa-sha2-512", hostkeyalgs, 0) == 1)) 2214d93a896eSDag-Erling Smørgrav return 1; 2215d93a896eSDag-Erling Smørgrav return match_pattern_list(ktype, hostkeyalgs, 0) == 1; 2216d93a896eSDag-Erling Smørgrav } 2217d93a896eSDag-Erling Smørgrav 2218d93a896eSDag-Erling Smørgrav /* 2219bc5531deSDag-Erling Smørgrav * Handle hostkeys-00@openssh.com global request to inform the client of all 2220bc5531deSDag-Erling Smørgrav * the server's hostkeys. The keys are checked against the user's 2221bc5531deSDag-Erling Smørgrav * HostkeyAlgorithms preference before they are accepted. 2222bc5531deSDag-Erling Smørgrav */ 2223bc5531deSDag-Erling Smørgrav static int 222419261079SEd Maste client_input_hostkeys(struct ssh *ssh) 2225bc5531deSDag-Erling Smørgrav { 2226bc5531deSDag-Erling Smørgrav const u_char *blob = NULL; 2227bc5531deSDag-Erling Smørgrav size_t i, len = 0; 2228bc5531deSDag-Erling Smørgrav struct sshbuf *buf = NULL; 2229bc5531deSDag-Erling Smørgrav struct sshkey *key = NULL, **tmp; 2230bc5531deSDag-Erling Smørgrav int r; 2231bc5531deSDag-Erling Smørgrav char *fp; 2232bc5531deSDag-Erling Smørgrav static int hostkeys_seen = 0; /* XXX use struct ssh */ 2233bc5531deSDag-Erling Smørgrav extern struct sockaddr_storage hostaddr; /* XXX from ssh.c */ 2234bc5531deSDag-Erling Smørgrav struct hostkeys_update_ctx *ctx = NULL; 223519261079SEd Maste u_int want; 2236bc5531deSDag-Erling Smørgrav 2237bc5531deSDag-Erling Smørgrav if (hostkeys_seen) 223819261079SEd Maste fatal_f("server already sent hostkeys"); 2239bc5531deSDag-Erling Smørgrav if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK && 2240bc5531deSDag-Erling Smørgrav options.batch_mode) 2241bc5531deSDag-Erling Smørgrav return 1; /* won't ask in batchmode, so don't even try */ 2242bc5531deSDag-Erling Smørgrav if (!options.update_hostkeys || options.num_user_hostfiles <= 0) 2243bc5531deSDag-Erling Smørgrav return 1; 2244bc5531deSDag-Erling Smørgrav 2245bc5531deSDag-Erling Smørgrav ctx = xcalloc(1, sizeof(*ctx)); 2246bc5531deSDag-Erling Smørgrav while (ssh_packet_remaining(ssh) > 0) { 2247bc5531deSDag-Erling Smørgrav sshkey_free(key); 2248bc5531deSDag-Erling Smørgrav key = NULL; 2249bc5531deSDag-Erling Smørgrav if ((r = sshpkt_get_string_direct(ssh, &blob, &len)) != 0) { 225019261079SEd Maste error_fr(r, "parse key"); 2251bc5531deSDag-Erling Smørgrav goto out; 2252bc5531deSDag-Erling Smørgrav } 2253bc5531deSDag-Erling Smørgrav if ((r = sshkey_from_blob(blob, len, &key)) != 0) { 225419261079SEd Maste do_log2_fr(r, r == SSH_ERR_KEY_TYPE_UNKNOWN ? 225519261079SEd Maste SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_ERROR, 225619261079SEd Maste "convert key"); 225719261079SEd Maste continue; 2258bc5531deSDag-Erling Smørgrav } 2259bc5531deSDag-Erling Smørgrav fp = sshkey_fingerprint(key, options.fingerprint_hash, 2260bc5531deSDag-Erling Smørgrav SSH_FP_DEFAULT); 226119261079SEd Maste debug3_f("received %s key %s", sshkey_type(key), fp); 2262bc5531deSDag-Erling Smørgrav free(fp); 2263eccfee6eSDag-Erling Smørgrav 2264d93a896eSDag-Erling Smørgrav if (!key_accepted_by_hostkeyalgs(key)) { 226519261079SEd Maste debug3_f("%s key not permitted by " 226619261079SEd Maste "HostkeyAlgorithms", sshkey_ssh_name(key)); 2267bc5531deSDag-Erling Smørgrav continue; 2268bc5531deSDag-Erling Smørgrav } 2269bc5531deSDag-Erling Smørgrav /* Skip certs */ 2270bc5531deSDag-Erling Smørgrav if (sshkey_is_cert(key)) { 227119261079SEd Maste debug3_f("%s key is a certificate; skipping", 227219261079SEd Maste sshkey_ssh_name(key)); 2273bc5531deSDag-Erling Smørgrav continue; 2274bc5531deSDag-Erling Smørgrav } 2275bc5531deSDag-Erling Smørgrav /* Ensure keys are unique */ 2276bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) { 2277bc5531deSDag-Erling Smørgrav if (sshkey_equal(key, ctx->keys[i])) { 227819261079SEd Maste error_f("received duplicated %s host key", 227919261079SEd Maste sshkey_ssh_name(key)); 2280bc5531deSDag-Erling Smørgrav goto out; 2281bc5531deSDag-Erling Smørgrav } 2282bc5531deSDag-Erling Smørgrav } 2283bc5531deSDag-Erling Smørgrav /* Key is good, record it */ 22844f52dfbbSDag-Erling Smørgrav if ((tmp = recallocarray(ctx->keys, ctx->nkeys, ctx->nkeys + 1, 2285bc5531deSDag-Erling Smørgrav sizeof(*ctx->keys))) == NULL) 228619261079SEd Maste fatal_f("recallocarray failed nkeys = %zu", 228719261079SEd Maste ctx->nkeys); 2288bc5531deSDag-Erling Smørgrav ctx->keys = tmp; 2289bc5531deSDag-Erling Smørgrav ctx->keys[ctx->nkeys++] = key; 2290bc5531deSDag-Erling Smørgrav key = NULL; 2291bc5531deSDag-Erling Smørgrav } 2292bc5531deSDag-Erling Smørgrav 2293bc5531deSDag-Erling Smørgrav if (ctx->nkeys == 0) { 229419261079SEd Maste debug_f("server sent no hostkeys"); 2295bc5531deSDag-Erling Smørgrav goto out; 2296bc5531deSDag-Erling Smørgrav } 2297bc5531deSDag-Erling Smørgrav 229819261079SEd Maste if ((ctx->keys_match = calloc(ctx->nkeys, 229919261079SEd Maste sizeof(*ctx->keys_match))) == NULL || 230019261079SEd Maste (ctx->keys_verified = calloc(ctx->nkeys, 230119261079SEd Maste sizeof(*ctx->keys_verified))) == NULL) 230219261079SEd Maste fatal_f("calloc failed"); 2303bc5531deSDag-Erling Smørgrav 2304bc5531deSDag-Erling Smørgrav get_hostfile_hostname_ipaddr(host, 2305bc5531deSDag-Erling Smørgrav options.check_host_ip ? (struct sockaddr *)&hostaddr : NULL, 2306bc5531deSDag-Erling Smørgrav options.port, &ctx->host_str, 2307bc5531deSDag-Erling Smørgrav options.check_host_ip ? &ctx->ip_str : NULL); 2308bc5531deSDag-Erling Smørgrav 2309bc5531deSDag-Erling Smørgrav /* Find which keys we already know about. */ 231019261079SEd Maste for (i = 0; i < options.num_user_hostfiles; i++) { 231119261079SEd Maste debug_f("searching %s for %s / %s", 231219261079SEd Maste options.user_hostfiles[i], ctx->host_str, 231319261079SEd Maste ctx->ip_str ? ctx->ip_str : "(none)"); 231419261079SEd Maste if ((r = hostkeys_foreach(options.user_hostfiles[i], 231519261079SEd Maste hostkeys_find, ctx, ctx->host_str, ctx->ip_str, 231619261079SEd Maste HKF_WANT_PARSE_KEY, 0)) != 0) { 231719261079SEd Maste if (r == SSH_ERR_SYSTEM_ERROR && errno == ENOENT) { 231819261079SEd Maste debug_f("hostkeys file %s does not exist", 231919261079SEd Maste options.user_hostfiles[i]); 232019261079SEd Maste continue; 232119261079SEd Maste } 232219261079SEd Maste error_fr(r, "hostkeys_foreach failed for %s", 232319261079SEd Maste options.user_hostfiles[i]); 2324bc5531deSDag-Erling Smørgrav goto out; 2325bc5531deSDag-Erling Smørgrav } 232619261079SEd Maste } 2327bc5531deSDag-Erling Smørgrav 2328bc5531deSDag-Erling Smørgrav /* Figure out if we have any new keys to add */ 232919261079SEd Maste ctx->nnew = ctx->nincomplete = 0; 233019261079SEd Maste want = HKF_MATCH_HOST | ( options.check_host_ip ? HKF_MATCH_IP : 0); 2331bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) { 233219261079SEd Maste if (ctx->keys_match[i] == 0) 2333bc5531deSDag-Erling Smørgrav ctx->nnew++; 233419261079SEd Maste if ((ctx->keys_match[i] & want) != want) 233519261079SEd Maste ctx->nincomplete++; 2336bc5531deSDag-Erling Smørgrav } 2337bc5531deSDag-Erling Smørgrav 233819261079SEd Maste debug3_f("%zu server keys: %zu new, %zu retained, " 233919261079SEd Maste "%zu incomplete match. %zu to remove", ctx->nkeys, ctx->nnew, 234019261079SEd Maste ctx->nkeys - ctx->nnew - ctx->nincomplete, 234119261079SEd Maste ctx->nincomplete, ctx->nold); 2342bc5531deSDag-Erling Smørgrav 234319261079SEd Maste if (ctx->nnew == 0 && ctx->nold == 0) { 234419261079SEd Maste debug_f("no new or deprecated keys from server"); 234519261079SEd Maste goto out; 234619261079SEd Maste } 234719261079SEd Maste 234819261079SEd Maste /* Various reasons why we cannot proceed with the update */ 234919261079SEd Maste if (ctx->complex_hostspec) { 235019261079SEd Maste debug_f("CA/revocation marker, manual host list or wildcard " 235119261079SEd Maste "host pattern found, skipping UserKnownHostsFile update"); 235219261079SEd Maste goto out; 235319261079SEd Maste } 235419261079SEd Maste if (ctx->other_name_seen) { 235519261079SEd Maste debug_f("host key found matching a different name/address, " 235619261079SEd Maste "skipping UserKnownHostsFile update"); 235719261079SEd Maste goto out; 235819261079SEd Maste } 2359bc5531deSDag-Erling Smørgrav /* 236019261079SEd Maste * If removing keys, check whether they appear under different 236119261079SEd Maste * names/addresses and refuse to proceed if they do. This avoids 236219261079SEd Maste * cases such as hosts with multiple names becoming inconsistent 236319261079SEd Maste * with regards to CheckHostIP entries. 236419261079SEd Maste * XXX UpdateHostkeys=force to override this (and other) checks? 236519261079SEd Maste */ 236619261079SEd Maste if (ctx->nold != 0) { 236719261079SEd Maste if (check_old_keys_othernames(ctx) != 0) 236819261079SEd Maste goto out; /* error already logged */ 236919261079SEd Maste if (ctx->old_key_seen) { 237019261079SEd Maste debug_f("key(s) for %s%s%s exist under other names; " 237119261079SEd Maste "skipping UserKnownHostsFile update", 237219261079SEd Maste ctx->host_str, ctx->ip_str == NULL ? "" : ",", 237319261079SEd Maste ctx->ip_str == NULL ? "" : ctx->ip_str); 237419261079SEd Maste goto out; 237519261079SEd Maste } 237619261079SEd Maste } 237719261079SEd Maste 237819261079SEd Maste if (ctx->nnew == 0) { 237919261079SEd Maste /* 238019261079SEd Maste * We have some keys to remove or fix matching for. 238119261079SEd Maste * We can proceed to do this without requiring a fresh proof 238219261079SEd Maste * from the server. 238319261079SEd Maste */ 238419261079SEd Maste update_known_hosts(ctx); 238519261079SEd Maste goto out; 238619261079SEd Maste } 238719261079SEd Maste /* 238819261079SEd Maste * We have received previously-unseen keys from the server. 2389bc5531deSDag-Erling Smørgrav * Ask the server to confirm ownership of the private halves. 2390bc5531deSDag-Erling Smørgrav */ 239119261079SEd Maste debug3_f("asking server to prove ownership for %zu keys", ctx->nnew); 2392bc5531deSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || 2393bc5531deSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, 2394bc5531deSDag-Erling Smørgrav "hostkeys-prove-00@openssh.com")) != 0 || 2395bc5531deSDag-Erling Smørgrav (r = sshpkt_put_u8(ssh, 1)) != 0) /* bool: want reply */ 239619261079SEd Maste fatal_fr(r, "prepare hostkeys-prove"); 2397bc5531deSDag-Erling Smørgrav if ((buf = sshbuf_new()) == NULL) 239819261079SEd Maste fatal_f("sshbuf_new"); 2399bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) { 240019261079SEd Maste if (ctx->keys_match[i]) 2401bc5531deSDag-Erling Smørgrav continue; 2402bc5531deSDag-Erling Smørgrav sshbuf_reset(buf); 240319261079SEd Maste if ((r = sshkey_putb(ctx->keys[i], buf)) != 0 || 240419261079SEd Maste (r = sshpkt_put_stringb(ssh, buf)) != 0) 240519261079SEd Maste fatal_fr(r, "assemble hostkeys-prove"); 2406bc5531deSDag-Erling Smørgrav } 2407bc5531deSDag-Erling Smørgrav if ((r = sshpkt_send(ssh)) != 0) 240819261079SEd Maste fatal_fr(r, "send hostkeys-prove"); 2409bc5531deSDag-Erling Smørgrav client_register_global_confirm( 2410bc5531deSDag-Erling Smørgrav client_global_hostkeys_private_confirm, ctx); 2411bc5531deSDag-Erling Smørgrav ctx = NULL; /* will be freed in callback */ 2412bc5531deSDag-Erling Smørgrav 2413bc5531deSDag-Erling Smørgrav /* Success */ 2414bc5531deSDag-Erling Smørgrav out: 2415bc5531deSDag-Erling Smørgrav hostkeys_update_ctx_free(ctx); 2416bc5531deSDag-Erling Smørgrav sshkey_free(key); 2417bc5531deSDag-Erling Smørgrav sshbuf_free(buf); 2418bc5531deSDag-Erling Smørgrav /* 2419bc5531deSDag-Erling Smørgrav * NB. Return success for all cases. The server doesn't need to know 2420bc5531deSDag-Erling Smørgrav * what the client does with its hosts file. 2421bc5531deSDag-Erling Smørgrav */ 2422bc5531deSDag-Erling Smørgrav return 1; 2423bc5531deSDag-Erling Smørgrav } 2424bc5531deSDag-Erling Smørgrav 2425bc5531deSDag-Erling Smørgrav static int 24264f52dfbbSDag-Erling Smørgrav client_input_global_request(int type, u_int32_t seq, struct ssh *ssh) 2427ae1f160dSDag-Erling Smørgrav { 2428ae1f160dSDag-Erling Smørgrav char *rtype; 242919261079SEd Maste u_char want_reply; 243019261079SEd Maste int r, success = 0; 2431a04a10f8SKris Kennaway 243219261079SEd Maste if ((r = sshpkt_get_cstring(ssh, &rtype, NULL)) != 0 || 243319261079SEd Maste (r = sshpkt_get_u8(ssh, &want_reply)) != 0) 243419261079SEd Maste goto out; 2435efcad6b7SDag-Erling Smørgrav debug("client_input_global_request: rtype %s want_reply %d", 2436efcad6b7SDag-Erling Smørgrav rtype, want_reply); 2437bc5531deSDag-Erling Smørgrav if (strcmp(rtype, "hostkeys-00@openssh.com") == 0) 243819261079SEd Maste success = client_input_hostkeys(ssh); 2439ae1f160dSDag-Erling Smørgrav if (want_reply) { 244019261079SEd Maste if ((r = sshpkt_start(ssh, success ? SSH2_MSG_REQUEST_SUCCESS : 244119261079SEd Maste SSH2_MSG_REQUEST_FAILURE)) != 0 || 244219261079SEd Maste (r = sshpkt_send(ssh)) != 0 || 244319261079SEd Maste (r = ssh_packet_write_wait(ssh)) != 0) 244419261079SEd Maste goto out; 2445ae1f160dSDag-Erling Smørgrav } 244619261079SEd Maste r = 0; 244719261079SEd Maste out: 2448e4a9863fSDag-Erling Smørgrav free(rtype); 244919261079SEd Maste return r; 245019261079SEd Maste } 245119261079SEd Maste 245219261079SEd Maste static void 245319261079SEd Maste client_send_env(struct ssh *ssh, int id, const char *name, const char *val) 245419261079SEd Maste { 245519261079SEd Maste int r; 245619261079SEd Maste 245719261079SEd Maste debug("channel %d: setting env %s = \"%s\"", id, name, val); 245819261079SEd Maste channel_request_start(ssh, id, "env", 0); 245919261079SEd Maste if ((r = sshpkt_put_cstring(ssh, name)) != 0 || 246019261079SEd Maste (r = sshpkt_put_cstring(ssh, val)) != 0 || 246119261079SEd Maste (r = sshpkt_send(ssh)) != 0) 246219261079SEd Maste fatal_fr(r, "send setenv"); 2463ae1f160dSDag-Erling Smørgrav } 2464ae1f160dSDag-Erling Smørgrav 2465d74d50a8SDag-Erling Smørgrav void 24664f52dfbbSDag-Erling Smørgrav client_session2_setup(struct ssh *ssh, int id, int want_tty, int want_subsystem, 2467190cef3dSDag-Erling Smørgrav const char *term, struct termios *tiop, int in_fd, struct sshbuf *cmd, 2468190cef3dSDag-Erling Smørgrav char **env) 2469d74d50a8SDag-Erling Smørgrav { 2470*38a52bd3SEd Maste size_t i, j, len; 2471*38a52bd3SEd Maste int matched, r; 2472190cef3dSDag-Erling Smørgrav char *name, *val; 24735e8dbd04SDag-Erling Smørgrav Channel *c = NULL; 2474d74d50a8SDag-Erling Smørgrav 247519261079SEd Maste debug2_f("id %d", id); 2476d74d50a8SDag-Erling Smørgrav 24774f52dfbbSDag-Erling Smørgrav if ((c = channel_lookup(ssh, id)) == NULL) 247819261079SEd Maste fatal_f("channel %d: unknown channel", id); 24795e8dbd04SDag-Erling Smørgrav 248019261079SEd Maste ssh_packet_set_interactive(ssh, want_tty, 24814a421b63SDag-Erling Smørgrav options.ip_qos_interactive, options.ip_qos_bulk); 24824a421b63SDag-Erling Smørgrav 2483d74d50a8SDag-Erling Smørgrav if (want_tty) { 2484d74d50a8SDag-Erling Smørgrav struct winsize ws; 2485d74d50a8SDag-Erling Smørgrav 2486d74d50a8SDag-Erling Smørgrav /* Store window size in the packet. */ 248719261079SEd Maste if (ioctl(in_fd, TIOCGWINSZ, &ws) == -1) 2488d74d50a8SDag-Erling Smørgrav memset(&ws, 0, sizeof(ws)); 2489d74d50a8SDag-Erling Smørgrav 24904f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, id, "pty-req", 1); 24914f52dfbbSDag-Erling Smørgrav client_expect_confirm(ssh, id, "PTY allocation", CONFIRM_TTY); 249219261079SEd Maste if ((r = sshpkt_put_cstring(ssh, term != NULL ? term : "")) 249319261079SEd Maste != 0 || 249419261079SEd Maste (r = sshpkt_put_u32(ssh, (u_int)ws.ws_col)) != 0 || 249519261079SEd Maste (r = sshpkt_put_u32(ssh, (u_int)ws.ws_row)) != 0 || 249619261079SEd Maste (r = sshpkt_put_u32(ssh, (u_int)ws.ws_xpixel)) != 0 || 249719261079SEd Maste (r = sshpkt_put_u32(ssh, (u_int)ws.ws_ypixel)) != 0) 249819261079SEd Maste fatal_fr(r, "build pty-req"); 2499d4af9e69SDag-Erling Smørgrav if (tiop == NULL) 2500d4af9e69SDag-Erling Smørgrav tiop = get_saved_tio(); 2501190cef3dSDag-Erling Smørgrav ssh_tty_make_modes(ssh, -1, tiop); 250219261079SEd Maste if ((r = sshpkt_send(ssh)) != 0) 250319261079SEd Maste fatal_fr(r, "send pty-req"); 2504d74d50a8SDag-Erling Smørgrav /* XXX wait for reply */ 25055e8dbd04SDag-Erling Smørgrav c->client_tty = 1; 2506d74d50a8SDag-Erling Smørgrav } 2507d74d50a8SDag-Erling Smørgrav 2508d74d50a8SDag-Erling Smørgrav /* Transfer any environment variables from client to server */ 2509d74d50a8SDag-Erling Smørgrav if (options.num_send_env != 0 && env != NULL) { 2510d74d50a8SDag-Erling Smørgrav debug("Sending environment."); 2511d74d50a8SDag-Erling Smørgrav for (i = 0; env[i] != NULL; i++) { 2512d74d50a8SDag-Erling Smørgrav /* Split */ 2513d74d50a8SDag-Erling Smørgrav name = xstrdup(env[i]); 2514d74d50a8SDag-Erling Smørgrav if ((val = strchr(name, '=')) == NULL) { 2515e4a9863fSDag-Erling Smørgrav free(name); 2516d74d50a8SDag-Erling Smørgrav continue; 2517d74d50a8SDag-Erling Smørgrav } 2518d74d50a8SDag-Erling Smørgrav *val++ = '\0'; 2519d74d50a8SDag-Erling Smørgrav 2520d74d50a8SDag-Erling Smørgrav matched = 0; 2521d74d50a8SDag-Erling Smørgrav for (j = 0; j < options.num_send_env; j++) { 2522d74d50a8SDag-Erling Smørgrav if (match_pattern(name, options.send_env[j])) { 2523d74d50a8SDag-Erling Smørgrav matched = 1; 2524d74d50a8SDag-Erling Smørgrav break; 2525d74d50a8SDag-Erling Smørgrav } 2526d74d50a8SDag-Erling Smørgrav } 2527d74d50a8SDag-Erling Smørgrav if (!matched) { 2528d74d50a8SDag-Erling Smørgrav debug3("Ignored env %s", name); 2529e4a9863fSDag-Erling Smørgrav free(name); 2530d74d50a8SDag-Erling Smørgrav continue; 2531d74d50a8SDag-Erling Smørgrav } 253219261079SEd Maste client_send_env(ssh, id, name, val); 2533e4a9863fSDag-Erling Smørgrav free(name); 2534d74d50a8SDag-Erling Smørgrav } 2535d74d50a8SDag-Erling Smørgrav } 2536190cef3dSDag-Erling Smørgrav for (i = 0; i < options.num_setenv; i++) { 2537190cef3dSDag-Erling Smørgrav /* Split */ 2538190cef3dSDag-Erling Smørgrav name = xstrdup(options.setenv[i]); 2539190cef3dSDag-Erling Smørgrav if ((val = strchr(name, '=')) == NULL) { 2540190cef3dSDag-Erling Smørgrav free(name); 2541190cef3dSDag-Erling Smørgrav continue; 2542190cef3dSDag-Erling Smørgrav } 2543190cef3dSDag-Erling Smørgrav *val++ = '\0'; 254419261079SEd Maste client_send_env(ssh, id, name, val); 2545190cef3dSDag-Erling Smørgrav free(name); 2546190cef3dSDag-Erling Smørgrav } 2547190cef3dSDag-Erling Smørgrav 2548190cef3dSDag-Erling Smørgrav len = sshbuf_len(cmd); 2549d74d50a8SDag-Erling Smørgrav if (len > 0) { 2550d74d50a8SDag-Erling Smørgrav if (len > 900) 2551d74d50a8SDag-Erling Smørgrav len = 900; 2552d74d50a8SDag-Erling Smørgrav if (want_subsystem) { 2553d4af9e69SDag-Erling Smørgrav debug("Sending subsystem: %.*s", 2554*38a52bd3SEd Maste (int)len, (const u_char*)sshbuf_ptr(cmd)); 25554f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, id, "subsystem", 1); 25564f52dfbbSDag-Erling Smørgrav client_expect_confirm(ssh, id, "subsystem", 25574f52dfbbSDag-Erling Smørgrav CONFIRM_CLOSE); 2558d74d50a8SDag-Erling Smørgrav } else { 2559d4af9e69SDag-Erling Smørgrav debug("Sending command: %.*s", 2560*38a52bd3SEd Maste (int)len, (const u_char*)sshbuf_ptr(cmd)); 25614f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, id, "exec", 1); 25624f52dfbbSDag-Erling Smørgrav client_expect_confirm(ssh, id, "exec", CONFIRM_CLOSE); 2563d74d50a8SDag-Erling Smørgrav } 256419261079SEd Maste if ((r = sshpkt_put_stringb(ssh, cmd)) != 0 || 256519261079SEd Maste (r = sshpkt_send(ssh)) != 0) 256619261079SEd Maste fatal_fr(r, "send command"); 2567d74d50a8SDag-Erling Smørgrav } else { 25684f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, id, "shell", 1); 25694f52dfbbSDag-Erling Smørgrav client_expect_confirm(ssh, id, "shell", CONFIRM_CLOSE); 257019261079SEd Maste if ((r = sshpkt_send(ssh)) != 0) 257119261079SEd Maste fatal_fr(r, "send shell"); 2572d74d50a8SDag-Erling Smørgrav } 2573d74d50a8SDag-Erling Smørgrav } 2574d74d50a8SDag-Erling Smørgrav 2575ae1f160dSDag-Erling Smørgrav static void 257619261079SEd Maste client_init_dispatch(struct ssh *ssh) 2577a04a10f8SKris Kennaway { 257819261079SEd Maste ssh_dispatch_init(ssh, &dispatch_protocol_error); 2579545d5ecaSDag-Erling Smørgrav 258019261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); 258119261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_DATA, &channel_input_data); 258219261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); 258319261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); 258419261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_OPEN, &client_input_channel_open); 258519261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); 258619261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); 258719261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_REQUEST, &client_input_channel_req); 258819261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); 258919261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_SUCCESS, &channel_input_status_confirm); 259019261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_FAILURE, &channel_input_status_confirm); 259119261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_GLOBAL_REQUEST, &client_input_global_request); 25921e8db6e2SBrian Feldman 25931e8db6e2SBrian Feldman /* rekeying */ 259419261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit); 2595545d5ecaSDag-Erling Smørgrav 2596545d5ecaSDag-Erling Smørgrav /* global request reply messages */ 259719261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_REQUEST_FAILURE, &client_global_request_reply); 259819261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_REQUEST_SUCCESS, &client_global_request_reply); 2599a04a10f8SKris Kennaway } 2600d4af9e69SDag-Erling Smørgrav 2601e146993eSDag-Erling Smørgrav void 2602e146993eSDag-Erling Smørgrav client_stop_mux(void) 2603e146993eSDag-Erling Smørgrav { 2604e146993eSDag-Erling Smørgrav if (options.control_path != NULL && muxserver_sock != -1) 2605e146993eSDag-Erling Smørgrav unlink(options.control_path); 2606e146993eSDag-Erling Smørgrav /* 26076888a9beSDag-Erling Smørgrav * If we are in persist mode, or don't have a shell, signal that we 26086888a9beSDag-Erling Smørgrav * should close when all active channels are closed. 2609e146993eSDag-Erling Smørgrav */ 261019261079SEd Maste if (options.control_persist || options.session_type == SESSION_TYPE_NONE) { 2611e146993eSDag-Erling Smørgrav session_closed = 1; 2612e146993eSDag-Erling Smørgrav setproctitle("[stopped mux]"); 2613e146993eSDag-Erling Smørgrav } 2614e146993eSDag-Erling Smørgrav } 2615e146993eSDag-Erling Smørgrav 2616efcad6b7SDag-Erling Smørgrav /* client specific fatal cleanup */ 2617efcad6b7SDag-Erling Smørgrav void 2618efcad6b7SDag-Erling Smørgrav cleanup_exit(int i) 2619efcad6b7SDag-Erling Smørgrav { 2620e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 2621d4af9e69SDag-Erling Smørgrav if (options.control_path != NULL && muxserver_sock != -1) 2622d74d50a8SDag-Erling Smørgrav unlink(options.control_path); 26234a421b63SDag-Erling Smørgrav ssh_kill_proxy_command(); 2624efcad6b7SDag-Erling Smørgrav _exit(i); 2625efcad6b7SDag-Erling Smørgrav } 2626