1*4d3fc8b0SEd Maste /* $OpenBSD: clientloop.c,v 1.390 2023/03/08 04:43:12 guenther 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. */ 161*4d3fc8b0SEd Maste static time_t 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 */ 163f374ba41SEd Maste static int hostkeys_update_complete; 164f374ba41SEd Maste static int session_setup_complete; 165a04a10f8SKris Kennaway 16619261079SEd Maste static void client_init_dispatch(struct ssh *ssh); 167a04a10f8SKris Kennaway int session_ident = -1; 168a04a10f8SKris Kennaway 169d4af9e69SDag-Erling Smørgrav /* Track escape per proto2 channel */ 170d4af9e69SDag-Erling Smørgrav struct escape_filter_ctx { 171d4af9e69SDag-Erling Smørgrav int escape_pending; 172d4af9e69SDag-Erling Smørgrav int escape_char; 173d74d50a8SDag-Erling Smørgrav }; 174d74d50a8SDag-Erling Smørgrav 175d4af9e69SDag-Erling Smørgrav /* Context for channel confirmation replies */ 176d4af9e69SDag-Erling Smørgrav struct channel_reply_ctx { 177d4af9e69SDag-Erling Smørgrav const char *request_type; 178e146993eSDag-Erling Smørgrav int id; 179e146993eSDag-Erling Smørgrav enum confirm_action action; 180d4af9e69SDag-Erling Smørgrav }; 181d4af9e69SDag-Erling Smørgrav 182d4af9e69SDag-Erling Smørgrav /* Global request success/failure callbacks */ 1834f52dfbbSDag-Erling Smørgrav /* XXX move to struct ssh? */ 184d4af9e69SDag-Erling Smørgrav struct global_confirm { 185d4af9e69SDag-Erling Smørgrav TAILQ_ENTRY(global_confirm) entry; 186d4af9e69SDag-Erling Smørgrav global_confirm_cb *cb; 187d4af9e69SDag-Erling Smørgrav void *ctx; 188d4af9e69SDag-Erling Smørgrav int ref_count; 189d4af9e69SDag-Erling Smørgrav }; 190d4af9e69SDag-Erling Smørgrav TAILQ_HEAD(global_confirms, global_confirm); 191d4af9e69SDag-Erling Smørgrav static struct global_confirms global_confirms = 192d4af9e69SDag-Erling Smørgrav TAILQ_HEAD_INITIALIZER(global_confirms); 193d4af9e69SDag-Erling Smørgrav 194190cef3dSDag-Erling Smørgrav void ssh_process_session2_setup(int, int, int, struct sshbuf *); 1951323ec57SEd Maste static void quit_message(const char *fmt, ...) 1961323ec57SEd Maste __attribute__((__format__ (printf, 1, 2))); 1971323ec57SEd Maste 1981323ec57SEd Maste static void 1991323ec57SEd Maste quit_message(const char *fmt, ...) 2001323ec57SEd Maste { 2011323ec57SEd Maste char *msg; 2021323ec57SEd Maste va_list args; 2031323ec57SEd Maste int r; 2041323ec57SEd Maste 2051323ec57SEd Maste va_start(args, fmt); 2061323ec57SEd Maste xvasprintf(&msg, fmt, args); 2071323ec57SEd Maste va_end(args); 2081323ec57SEd Maste 2091323ec57SEd Maste if ((r = sshbuf_putf(stderr_buffer, "%s\r\n", msg)) != 0) 2101323ec57SEd Maste fatal_fr(r, "sshbuf_putf"); 2111323ec57SEd Maste quit_pending = 1; 2121323ec57SEd Maste } 213d74d50a8SDag-Erling Smørgrav 214511b41d2SMark Murray /* 215511b41d2SMark Murray * Signal handler for the window change signal (SIGWINCH). This just sets a 216511b41d2SMark Murray * flag indicating that the window has changed. 217511b41d2SMark Murray */ 218ae1f160dSDag-Erling Smørgrav static void 219511b41d2SMark Murray window_change_handler(int sig) 220511b41d2SMark Murray { 221511b41d2SMark Murray received_window_change_signal = 1; 222511b41d2SMark Murray } 223511b41d2SMark Murray 224511b41d2SMark Murray /* 225511b41d2SMark Murray * Signal handler for signals that cause the program to terminate. These 226511b41d2SMark Murray * signals must be trapped to restore terminal modes. 227511b41d2SMark Murray */ 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; 377*4d3fc8b0SEd Maste if (SSH_TIME_T_MAX - timeout < now) 378*4d3fc8b0SEd Maste x11_refuse_time = SSH_TIME_T_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 { 519f374ba41SEd Maste struct timespec timeout; 5201323ec57SEd Maste int ret; 5211323ec57SEd Maste u_int p; 522efcad6b7SDag-Erling Smørgrav 5231323ec57SEd Maste *conn_in_readyp = *conn_out_readyp = 0; 524511b41d2SMark Murray 5251323ec57SEd Maste /* Prepare channel poll. First two pollfd entries are reserved */ 526f374ba41SEd Maste ptimeout_init(&timeout); 527f374ba41SEd Maste channel_prepare_poll(ssh, pfdp, npfd_allocp, npfd_activep, 2, &timeout); 5281323ec57SEd Maste if (*npfd_activep < 2) 5291323ec57SEd Maste fatal_f("bad npfd %u", *npfd_activep); /* shouldn't happen */ 5301323ec57SEd Maste 5311323ec57SEd Maste /* channel_prepare_poll could have closed the last channel */ 5324f52dfbbSDag-Erling Smørgrav if (session_closed && !channel_still_open(ssh) && 53319261079SEd Maste !ssh_packet_have_data_to_write(ssh)) { 5341323ec57SEd Maste /* clear events since we did not call poll() */ 5351323ec57SEd Maste for (p = 0; p < *npfd_activep; p++) 5361323ec57SEd Maste (*pfdp)[p].revents = 0; 537ae1f160dSDag-Erling Smørgrav return; 5384f52dfbbSDag-Erling Smørgrav } 5394f52dfbbSDag-Erling Smørgrav 5401323ec57SEd Maste /* Monitor server connection on reserved pollfd entries */ 5411323ec57SEd Maste (*pfdp)[0].fd = connection_in; 5421323ec57SEd Maste (*pfdp)[0].events = POLLIN; 5431323ec57SEd Maste (*pfdp)[1].fd = connection_out; 5441323ec57SEd Maste (*pfdp)[1].events = ssh_packet_have_data_to_write(ssh) ? POLLOUT : 0; 545511b41d2SMark Murray 546511b41d2SMark Murray /* 547511b41d2SMark Murray * Wait for something to happen. This will suspend the process until 5481323ec57SEd Maste * some polled descriptor can be read, written, or has some other 549e2f6069cSDag-Erling Smørgrav * event pending, or a timeout expires. 550511b41d2SMark Murray */ 5514f52dfbbSDag-Erling Smørgrav set_control_persist_exit_time(ssh); 552f374ba41SEd Maste if (control_persist_exit_time > 0) 553f374ba41SEd Maste ptimeout_deadline_monotime(&timeout, control_persist_exit_time); 554f374ba41SEd Maste if (options.server_alive_interval > 0) 555f374ba41SEd Maste ptimeout_deadline_monotime(&timeout, server_alive_time); 556f374ba41SEd Maste if (options.rekey_interval > 0 && !rekeying) { 557f374ba41SEd Maste ptimeout_deadline_sec(&timeout, 558f374ba41SEd Maste ssh_packet_get_rekey_timeout(ssh)); 559e2f6069cSDag-Erling Smørgrav } 560e2f6069cSDag-Erling Smørgrav 561f374ba41SEd Maste ret = poll(*pfdp, *npfd_activep, ptimeout_get_ms(&timeout)); 5621323ec57SEd Maste 56319261079SEd Maste if (ret == -1) { 5641e8db6e2SBrian Feldman /* 5651323ec57SEd Maste * We have to clear the events because we return. 5661e8db6e2SBrian Feldman * We have to return, because the mainloop checks for the flags 5671e8db6e2SBrian Feldman * set by the signal handlers. 5681e8db6e2SBrian Feldman */ 5691323ec57SEd Maste for (p = 0; p < *npfd_activep; p++) 5701323ec57SEd Maste (*pfdp)[p].revents = 0; 571511b41d2SMark Murray if (errno == EINTR) 572511b41d2SMark Murray return; 573511b41d2SMark Murray /* Note: we might still have data in the buffers. */ 5741323ec57SEd Maste quit_message("poll: %s", strerror(errno)); 5751323ec57SEd Maste return; 5761323ec57SEd Maste } 5771323ec57SEd Maste 5781323ec57SEd Maste *conn_in_readyp = (*pfdp)[0].revents != 0; 5791323ec57SEd Maste *conn_out_readyp = (*pfdp)[1].revents != 0; 5801323ec57SEd Maste 5811323ec57SEd Maste if (options.server_alive_interval > 0 && !*conn_in_readyp && 5821323ec57SEd Maste monotime() >= server_alive_time) { 583e4a9863fSDag-Erling Smørgrav /* 5841323ec57SEd Maste * ServerAlive check is needed. We can't rely on the poll 58519261079SEd Maste * timing out since traffic on the client side such as port 58619261079SEd Maste * forwards can keep waking it up. 587e4a9863fSDag-Erling Smørgrav */ 58819261079SEd Maste server_alive_check(ssh); 589e4a9863fSDag-Erling Smørgrav } 5901323ec57SEd Maste } 591e4a9863fSDag-Erling Smørgrav 592ae1f160dSDag-Erling Smørgrav static void 593190cef3dSDag-Erling Smørgrav client_suspend_self(struct sshbuf *bin, struct sshbuf *bout, struct sshbuf *berr) 594511b41d2SMark Murray { 595511b41d2SMark Murray /* Flush stdout and stderr buffers. */ 596190cef3dSDag-Erling Smørgrav if (sshbuf_len(bout) > 0) 597190cef3dSDag-Erling Smørgrav atomicio(vwrite, fileno(stdout), sshbuf_mutable_ptr(bout), 598190cef3dSDag-Erling Smørgrav sshbuf_len(bout)); 599190cef3dSDag-Erling Smørgrav if (sshbuf_len(berr) > 0) 600190cef3dSDag-Erling Smørgrav atomicio(vwrite, fileno(stderr), sshbuf_mutable_ptr(berr), 601190cef3dSDag-Erling Smørgrav sshbuf_len(berr)); 602511b41d2SMark Murray 603e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 604511b41d2SMark Murray 6054f52dfbbSDag-Erling Smørgrav sshbuf_reset(bin); 6064f52dfbbSDag-Erling Smørgrav sshbuf_reset(bout); 6074f52dfbbSDag-Erling Smørgrav sshbuf_reset(berr); 608511b41d2SMark Murray 609511b41d2SMark Murray /* Send the suspend signal to the program itself. */ 610511b41d2SMark Murray kill(getpid(), SIGTSTP); 611511b41d2SMark Murray 6125e8dbd04SDag-Erling Smørgrav /* Reset window sizes in case they have changed */ 613511b41d2SMark Murray received_window_change_signal = 1; 614511b41d2SMark Murray 615e146993eSDag-Erling Smørgrav enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 616511b41d2SMark Murray } 617511b41d2SMark Murray 618ae1f160dSDag-Erling Smørgrav static void 6191323ec57SEd Maste client_process_net_input(struct ssh *ssh) 620511b41d2SMark Murray { 6211323ec57SEd Maste int r; 622511b41d2SMark Murray 623511b41d2SMark Murray /* 624511b41d2SMark Murray * Read input from the server, and add any such data to the buffer of 625511b41d2SMark Murray * the packet subsystem. 626511b41d2SMark Murray */ 62719261079SEd Maste schedule_server_alive_check(); 6281323ec57SEd Maste if ((r = ssh_packet_process_read(ssh, connection_in)) == 0) 6291323ec57SEd Maste return; /* success */ 6301323ec57SEd Maste if (r == SSH_ERR_SYSTEM_ERROR) { 6311323ec57SEd Maste if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK) 6321323ec57SEd Maste return; 6331323ec57SEd Maste if (errno == EPIPE) { 6341323ec57SEd Maste quit_message("Connection to %s closed by remote host.", 6351323ec57SEd Maste host); 636511b41d2SMark Murray return; 637511b41d2SMark Murray } 638511b41d2SMark Murray } 6391323ec57SEd Maste quit_message("Read from remote host %s: %s", host, ssh_err(r)); 640a04a10f8SKris Kennaway } 641a04a10f8SKris Kennaway 642545d5ecaSDag-Erling Smørgrav static void 6434f52dfbbSDag-Erling Smørgrav client_status_confirm(struct ssh *ssh, int type, Channel *c, void *ctx) 644d74d50a8SDag-Erling Smørgrav { 645d4af9e69SDag-Erling Smørgrav struct channel_reply_ctx *cr = (struct channel_reply_ctx *)ctx; 646d4af9e69SDag-Erling Smørgrav char errmsg[256]; 647190cef3dSDag-Erling Smørgrav int r, tochan; 648d74d50a8SDag-Erling Smørgrav 649e146993eSDag-Erling Smørgrav /* 650e146993eSDag-Erling Smørgrav * If a TTY was explicitly requested, then a failure to allocate 651e146993eSDag-Erling Smørgrav * one is fatal. 652e146993eSDag-Erling Smørgrav */ 653e146993eSDag-Erling Smørgrav if (cr->action == CONFIRM_TTY && 654e146993eSDag-Erling Smørgrav (options.request_tty == REQUEST_TTY_FORCE || 655e146993eSDag-Erling Smørgrav options.request_tty == REQUEST_TTY_YES)) 656e146993eSDag-Erling Smørgrav cr->action = CONFIRM_CLOSE; 657e146993eSDag-Erling Smørgrav 658190cef3dSDag-Erling Smørgrav /* XXX suppress on mux _client_ quietmode */ 659d4af9e69SDag-Erling Smørgrav tochan = options.log_level >= SYSLOG_LEVEL_ERROR && 660b15c8340SDag-Erling Smørgrav c->ctl_chan != -1 && c->extended_usage == CHAN_EXTENDED_WRITE; 661d74d50a8SDag-Erling Smørgrav 662d4af9e69SDag-Erling Smørgrav if (type == SSH2_MSG_CHANNEL_SUCCESS) { 663d4af9e69SDag-Erling Smørgrav debug2("%s request accepted on channel %d", 664d4af9e69SDag-Erling Smørgrav cr->request_type, c->self); 665d4af9e69SDag-Erling Smørgrav } else if (type == SSH2_MSG_CHANNEL_FAILURE) { 666d4af9e69SDag-Erling Smørgrav if (tochan) { 667d4af9e69SDag-Erling Smørgrav snprintf(errmsg, sizeof(errmsg), 668d4af9e69SDag-Erling Smørgrav "%s request failed\r\n", cr->request_type); 669d4af9e69SDag-Erling Smørgrav } else { 670d4af9e69SDag-Erling Smørgrav snprintf(errmsg, sizeof(errmsg), 671d4af9e69SDag-Erling Smørgrav "%s request failed on channel %d", 672d4af9e69SDag-Erling Smørgrav cr->request_type, c->self); 673d74d50a8SDag-Erling Smørgrav } 674d4af9e69SDag-Erling Smørgrav /* If error occurred on primary session channel, then exit */ 675e146993eSDag-Erling Smørgrav if (cr->action == CONFIRM_CLOSE && c->self == session_ident) 676d4af9e69SDag-Erling Smørgrav fatal("%s", errmsg); 677e146993eSDag-Erling Smørgrav /* 678e146993eSDag-Erling Smørgrav * If error occurred on mux client, append to 679e146993eSDag-Erling Smørgrav * their stderr. 680e146993eSDag-Erling Smørgrav */ 681e146993eSDag-Erling Smørgrav if (tochan) { 68219261079SEd Maste debug3_f("channel %d: mux request: %s", c->self, 68319261079SEd Maste cr->request_type); 684190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put(c->extended, errmsg, 685190cef3dSDag-Erling Smørgrav strlen(errmsg))) != 0) 68619261079SEd Maste fatal_fr(r, "sshbuf_put"); 687e146993eSDag-Erling Smørgrav } else 688d4af9e69SDag-Erling Smørgrav error("%s", errmsg); 689e146993eSDag-Erling Smørgrav if (cr->action == CONFIRM_TTY) { 690e146993eSDag-Erling Smørgrav /* 691e146993eSDag-Erling Smørgrav * If a TTY allocation error occurred, then arrange 692e146993eSDag-Erling Smørgrav * for the correct TTY to leave raw mode. 693e146993eSDag-Erling Smørgrav */ 694e146993eSDag-Erling Smørgrav if (c->self == session_ident) 695e146993eSDag-Erling Smørgrav leave_raw_mode(0); 696e146993eSDag-Erling Smørgrav else 6974f52dfbbSDag-Erling Smørgrav mux_tty_alloc_failed(ssh, c); 698e146993eSDag-Erling Smørgrav } else if (cr->action == CONFIRM_CLOSE) { 6994f52dfbbSDag-Erling Smørgrav chan_read_failed(ssh, c); 7004f52dfbbSDag-Erling Smørgrav chan_write_failed(ssh, c); 701d74d50a8SDag-Erling Smørgrav } 702d74d50a8SDag-Erling Smørgrav } 703e4a9863fSDag-Erling Smørgrav free(cr); 704d4af9e69SDag-Erling Smørgrav } 705d74d50a8SDag-Erling Smørgrav 706d74d50a8SDag-Erling Smørgrav static void 7074f52dfbbSDag-Erling Smørgrav client_abandon_status_confirm(struct ssh *ssh, Channel *c, void *ctx) 708d74d50a8SDag-Erling Smørgrav { 709e4a9863fSDag-Erling Smørgrav free(ctx); 710d74d50a8SDag-Erling Smørgrav } 711d74d50a8SDag-Erling Smørgrav 712e146993eSDag-Erling Smørgrav void 7134f52dfbbSDag-Erling Smørgrav client_expect_confirm(struct ssh *ssh, int id, const char *request, 714e146993eSDag-Erling Smørgrav enum confirm_action action) 715d74d50a8SDag-Erling Smørgrav { 7160a37d4a3SXin LI struct channel_reply_ctx *cr = xcalloc(1, sizeof(*cr)); 717d74d50a8SDag-Erling Smørgrav 718d4af9e69SDag-Erling Smørgrav cr->request_type = request; 719e146993eSDag-Erling Smørgrav cr->action = action; 720d74d50a8SDag-Erling Smørgrav 7214f52dfbbSDag-Erling Smørgrav channel_register_status_confirm(ssh, id, client_status_confirm, 722d4af9e69SDag-Erling Smørgrav client_abandon_status_confirm, cr); 723d4af9e69SDag-Erling Smørgrav } 724d4af9e69SDag-Erling Smørgrav 725d4af9e69SDag-Erling Smørgrav void 726d4af9e69SDag-Erling Smørgrav client_register_global_confirm(global_confirm_cb *cb, void *ctx) 727d4af9e69SDag-Erling Smørgrav { 728d4af9e69SDag-Erling Smørgrav struct global_confirm *gc, *last_gc; 729d4af9e69SDag-Erling Smørgrav 730d4af9e69SDag-Erling Smørgrav /* Coalesce identical callbacks */ 731d4af9e69SDag-Erling Smørgrav last_gc = TAILQ_LAST(&global_confirms, global_confirms); 732d4af9e69SDag-Erling Smørgrav if (last_gc && last_gc->cb == cb && last_gc->ctx == ctx) { 733d4af9e69SDag-Erling Smørgrav if (++last_gc->ref_count >= INT_MAX) 73419261079SEd Maste fatal_f("last_gc->ref_count = %d", 73519261079SEd Maste last_gc->ref_count); 736d74d50a8SDag-Erling Smørgrav return; 737d74d50a8SDag-Erling Smørgrav } 738d74d50a8SDag-Erling Smørgrav 7390a37d4a3SXin LI gc = xcalloc(1, sizeof(*gc)); 740d4af9e69SDag-Erling Smørgrav gc->cb = cb; 741d4af9e69SDag-Erling Smørgrav gc->ctx = ctx; 742d4af9e69SDag-Erling Smørgrav gc->ref_count = 1; 743d4af9e69SDag-Erling Smørgrav TAILQ_INSERT_TAIL(&global_confirms, gc, entry); 744d74d50a8SDag-Erling Smørgrav } 745d74d50a8SDag-Erling Smørgrav 746f374ba41SEd Maste /* 747f374ba41SEd Maste * Returns non-zero if the client is able to handle a hostkeys-00@openssh.com 748f374ba41SEd Maste * hostkey update request. 749f374ba41SEd Maste */ 750f374ba41SEd Maste static int 751f374ba41SEd Maste can_update_hostkeys(void) 752f374ba41SEd Maste { 753f374ba41SEd Maste if (hostkeys_update_complete) 754f374ba41SEd Maste return 0; 755f374ba41SEd Maste if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK && 756f374ba41SEd Maste options.batch_mode) 757f374ba41SEd Maste return 0; /* won't ask in batchmode, so don't even try */ 758f374ba41SEd Maste if (!options.update_hostkeys || options.num_user_hostfiles <= 0) 759f374ba41SEd Maste return 0; 760f374ba41SEd Maste return 1; 761f374ba41SEd Maste } 762f374ba41SEd Maste 763f374ba41SEd Maste static void 764f374ba41SEd Maste client_repledge(void) 765f374ba41SEd Maste { 766f374ba41SEd Maste debug3_f("enter"); 767f374ba41SEd Maste 768f374ba41SEd Maste /* Might be able to tighten pledge now that session is established */ 769f374ba41SEd Maste if (options.control_master || options.control_path != NULL || 770f374ba41SEd Maste options.forward_x11 || options.fork_after_authentication || 771f374ba41SEd Maste can_update_hostkeys() || 772f374ba41SEd Maste (session_ident != -1 && !session_setup_complete)) { 773f374ba41SEd Maste /* Can't tighten */ 774f374ba41SEd Maste return; 775f374ba41SEd Maste } 776f374ba41SEd Maste /* 777f374ba41SEd Maste * LocalCommand and UpdateHostkeys have finished, so can get rid of 778f374ba41SEd Maste * filesystem. 779f374ba41SEd Maste * 780f374ba41SEd Maste * XXX protocol allows a server can to change hostkeys during the 781f374ba41SEd Maste * connection at rekey time that could trigger a hostkeys update 782f374ba41SEd Maste * but AFAIK no implementations support this. Could improve by 783f374ba41SEd Maste * forcing known_hosts to be read-only or via unveil(2). 784f374ba41SEd Maste */ 785f374ba41SEd Maste if (options.num_local_forwards != 0 || 786f374ba41SEd Maste options.num_remote_forwards != 0 || 787f374ba41SEd Maste options.num_permitted_remote_opens != 0 || 788f374ba41SEd Maste options.enable_escape_commandline != 0) { 789f374ba41SEd Maste /* rfwd needs inet */ 790f374ba41SEd Maste debug("pledge: network"); 791f374ba41SEd Maste if (pledge("stdio unix inet dns proc tty", NULL) == -1) 792f374ba41SEd Maste fatal_f("pledge(): %s", strerror(errno)); 793f374ba41SEd Maste } else if (options.forward_agent != 0) { 794f374ba41SEd Maste /* agent forwarding needs to open $SSH_AUTH_SOCK at will */ 795f374ba41SEd Maste debug("pledge: agent"); 796f374ba41SEd Maste if (pledge("stdio unix proc tty", NULL) == -1) 797f374ba41SEd Maste fatal_f("pledge(): %s", strerror(errno)); 798f374ba41SEd Maste } else { 799f374ba41SEd Maste debug("pledge: fork"); 800f374ba41SEd Maste if (pledge("stdio proc tty", NULL) == -1) 801f374ba41SEd Maste fatal_f("pledge(): %s", strerror(errno)); 802f374ba41SEd Maste } 803f374ba41SEd Maste /* XXX further things to do: 804f374ba41SEd Maste * 805f374ba41SEd Maste * - might be able to get rid of proc if we kill ~^Z 806f374ba41SEd Maste * - ssh -N (no session) 807f374ba41SEd Maste * - stdio forwarding 808f374ba41SEd Maste * - sessions without tty 809f374ba41SEd Maste */ 810f374ba41SEd Maste } 811f374ba41SEd Maste 812d74d50a8SDag-Erling Smørgrav static void 8134f52dfbbSDag-Erling Smørgrav process_cmdline(struct ssh *ssh) 814545d5ecaSDag-Erling Smørgrav { 815545d5ecaSDag-Erling Smørgrav void (*handler)(int); 816a0ee8cc6SDag-Erling Smørgrav char *s, *cmd; 817a0ee8cc6SDag-Erling Smørgrav int ok, delete = 0, local = 0, remote = 0, dynamic = 0; 818a0ee8cc6SDag-Erling Smørgrav struct Forward fwd; 819545d5ecaSDag-Erling Smørgrav 820b83788ffSDag-Erling Smørgrav memset(&fwd, 0, sizeof(fwd)); 821d4af9e69SDag-Erling Smørgrav 822e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 82319261079SEd Maste handler = ssh_signal(SIGINT, SIG_IGN); 824545d5ecaSDag-Erling Smørgrav cmd = s = read_passphrase("\r\nssh> ", RP_ECHO); 825545d5ecaSDag-Erling Smørgrav if (s == NULL) 826545d5ecaSDag-Erling Smørgrav goto out; 827f7167e0eSDag-Erling Smørgrav while (isspace((u_char)*s)) 828545d5ecaSDag-Erling Smørgrav s++; 829d74d50a8SDag-Erling Smørgrav if (*s == '-') 830d74d50a8SDag-Erling Smørgrav s++; /* Skip cmdline '-', if any */ 831d74d50a8SDag-Erling Smørgrav if (*s == '\0') 832545d5ecaSDag-Erling Smørgrav goto out; 833d74d50a8SDag-Erling Smørgrav 834d74d50a8SDag-Erling Smørgrav if (*s == 'h' || *s == 'H' || *s == '?') { 835d74d50a8SDag-Erling Smørgrav logit("Commands:"); 836761efaa7SDag-Erling Smørgrav logit(" -L[bind_address:]port:host:hostport " 837761efaa7SDag-Erling Smørgrav "Request local forward"); 838761efaa7SDag-Erling Smørgrav logit(" -R[bind_address:]port:host:hostport " 839761efaa7SDag-Erling Smørgrav "Request remote forward"); 840cce7d346SDag-Erling Smørgrav logit(" -D[bind_address:]port " 841cce7d346SDag-Erling Smørgrav "Request dynamic forward"); 842462c32cbSDag-Erling Smørgrav logit(" -KL[bind_address:]port " 843462c32cbSDag-Erling Smørgrav "Cancel local forward"); 844761efaa7SDag-Erling Smørgrav logit(" -KR[bind_address:]port " 845761efaa7SDag-Erling Smørgrav "Cancel remote forward"); 846462c32cbSDag-Erling Smørgrav logit(" -KD[bind_address:]port " 847462c32cbSDag-Erling Smørgrav "Cancel dynamic forward"); 848021d409fSDag-Erling Smørgrav if (!options.permit_local_command) 849021d409fSDag-Erling Smørgrav goto out; 850761efaa7SDag-Erling Smørgrav logit(" !args " 851761efaa7SDag-Erling Smørgrav "Execute local command"); 852021d409fSDag-Erling Smørgrav goto out; 853021d409fSDag-Erling Smørgrav } 854021d409fSDag-Erling Smørgrav 855021d409fSDag-Erling Smørgrav if (*s == '!' && options.permit_local_command) { 856021d409fSDag-Erling Smørgrav s++; 857021d409fSDag-Erling Smørgrav ssh_local_cmd(s); 858d74d50a8SDag-Erling Smørgrav goto out; 859d74d50a8SDag-Erling Smørgrav } 860d74d50a8SDag-Erling Smørgrav 861d74d50a8SDag-Erling Smørgrav if (*s == 'K') { 862d74d50a8SDag-Erling Smørgrav delete = 1; 863d74d50a8SDag-Erling Smørgrav s++; 864d74d50a8SDag-Erling Smørgrav } 865cce7d346SDag-Erling Smørgrav if (*s == 'L') 866cce7d346SDag-Erling Smørgrav local = 1; 867cce7d346SDag-Erling Smørgrav else if (*s == 'R') 868cce7d346SDag-Erling Smørgrav remote = 1; 869cce7d346SDag-Erling Smørgrav else if (*s == 'D') 870cce7d346SDag-Erling Smørgrav dynamic = 1; 871cce7d346SDag-Erling Smørgrav else { 872d95e11bfSDag-Erling Smørgrav logit("Invalid command."); 873545d5ecaSDag-Erling Smørgrav goto out; 874545d5ecaSDag-Erling Smørgrav } 875cce7d346SDag-Erling Smørgrav 876f7167e0eSDag-Erling Smørgrav while (isspace((u_char)*++s)) 877d4af9e69SDag-Erling Smørgrav ; 878545d5ecaSDag-Erling Smørgrav 879b15c8340SDag-Erling Smørgrav /* XXX update list of forwards in options */ 880d74d50a8SDag-Erling Smørgrav if (delete) { 881a0ee8cc6SDag-Erling Smørgrav /* We pass 1 for dynamicfwd to restrict to 1 or 2 fields. */ 882a0ee8cc6SDag-Erling Smørgrav if (!parse_forward(&fwd, s, 1, 0)) { 883a0ee8cc6SDag-Erling Smørgrav logit("Bad forwarding close specification."); 884545d5ecaSDag-Erling Smørgrav goto out; 885545d5ecaSDag-Erling Smørgrav } 886462c32cbSDag-Erling Smørgrav if (remote) 8874f52dfbbSDag-Erling Smørgrav ok = channel_request_rforward_cancel(ssh, &fwd) == 0; 888462c32cbSDag-Erling Smørgrav else if (dynamic) 8894f52dfbbSDag-Erling Smørgrav ok = channel_cancel_lport_listener(ssh, &fwd, 890a0ee8cc6SDag-Erling Smørgrav 0, &options.fwd_opts) > 0; 891462c32cbSDag-Erling Smørgrav else 8924f52dfbbSDag-Erling Smørgrav ok = channel_cancel_lport_listener(ssh, &fwd, 893a0ee8cc6SDag-Erling Smørgrav CHANNEL_CANCEL_PORT_STATIC, 894a0ee8cc6SDag-Erling Smørgrav &options.fwd_opts) > 0; 895462c32cbSDag-Erling Smørgrav if (!ok) { 896d93a896eSDag-Erling Smørgrav logit("Unknown port forwarding."); 897462c32cbSDag-Erling Smørgrav goto out; 898462c32cbSDag-Erling Smørgrav } 899462c32cbSDag-Erling Smørgrav logit("Canceled forwarding."); 9005e8dbd04SDag-Erling Smørgrav } else { 901f374ba41SEd Maste /* -R specs can be both dynamic or not, so check both. */ 902f374ba41SEd Maste if (remote) { 903f374ba41SEd Maste if (!parse_forward(&fwd, s, 0, remote) && 904f374ba41SEd Maste !parse_forward(&fwd, s, 1, remote)) { 905f374ba41SEd Maste logit("Bad remote forwarding specification."); 906f374ba41SEd Maste goto out; 907f374ba41SEd Maste } 908f374ba41SEd Maste } else if (!parse_forward(&fwd, s, dynamic, remote)) { 909f374ba41SEd Maste logit("Bad local forwarding specification."); 910545d5ecaSDag-Erling Smørgrav goto out; 911545d5ecaSDag-Erling Smørgrav } 912cce7d346SDag-Erling Smørgrav if (local || dynamic) { 9134f52dfbbSDag-Erling Smørgrav if (!channel_setup_local_fwd_listener(ssh, &fwd, 914a0ee8cc6SDag-Erling Smørgrav &options.fwd_opts)) { 915d95e11bfSDag-Erling Smørgrav logit("Port forwarding failed."); 916545d5ecaSDag-Erling Smørgrav goto out; 917545d5ecaSDag-Erling Smørgrav } 9185e8dbd04SDag-Erling Smørgrav } else { 9194f52dfbbSDag-Erling Smørgrav if (channel_request_remote_forwarding(ssh, &fwd) < 0) { 920761efaa7SDag-Erling Smørgrav logit("Port forwarding failed."); 921761efaa7SDag-Erling Smørgrav goto out; 922761efaa7SDag-Erling Smørgrav } 9235e8dbd04SDag-Erling Smørgrav } 924d95e11bfSDag-Erling Smørgrav logit("Forwarding port."); 925d74d50a8SDag-Erling Smørgrav } 926d74d50a8SDag-Erling Smørgrav 927545d5ecaSDag-Erling Smørgrav out: 92819261079SEd Maste ssh_signal(SIGINT, handler); 929e146993eSDag-Erling Smørgrav enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 930e4a9863fSDag-Erling Smørgrav free(cmd); 931e4a9863fSDag-Erling Smørgrav free(fwd.listen_host); 932a0ee8cc6SDag-Erling Smørgrav free(fwd.listen_path); 933e4a9863fSDag-Erling Smørgrav free(fwd.connect_host); 934a0ee8cc6SDag-Erling Smørgrav free(fwd.connect_path); 935545d5ecaSDag-Erling Smørgrav } 936545d5ecaSDag-Erling Smørgrav 9376888a9beSDag-Erling Smørgrav /* reasons to suppress output of an escape command in help output */ 9386888a9beSDag-Erling Smørgrav #define SUPPRESS_NEVER 0 /* never suppress, always show */ 9394f52dfbbSDag-Erling Smørgrav #define SUPPRESS_MUXCLIENT 1 /* don't show in mux client sessions */ 9404f52dfbbSDag-Erling Smørgrav #define SUPPRESS_MUXMASTER 2 /* don't show in mux master sessions */ 9414f52dfbbSDag-Erling Smørgrav #define SUPPRESS_SYSLOG 4 /* don't show when logging to syslog */ 942f374ba41SEd Maste #define SUPPRESS_NOCMDLINE 8 /* don't show when cmdline disabled*/ 9436888a9beSDag-Erling Smørgrav struct escape_help_text { 9446888a9beSDag-Erling Smørgrav const char *cmd; 9456888a9beSDag-Erling Smørgrav const char *text; 9466888a9beSDag-Erling Smørgrav unsigned int flags; 9476888a9beSDag-Erling Smørgrav }; 9486888a9beSDag-Erling Smørgrav static struct escape_help_text esc_txt[] = { 9496888a9beSDag-Erling Smørgrav {".", "terminate session", SUPPRESS_MUXMASTER}, 9506888a9beSDag-Erling Smørgrav {".", "terminate connection (and any multiplexed sessions)", 9516888a9beSDag-Erling Smørgrav SUPPRESS_MUXCLIENT}, 9524f52dfbbSDag-Erling Smørgrav {"B", "send a BREAK to the remote system", SUPPRESS_NEVER}, 953f374ba41SEd Maste {"C", "open a command line", SUPPRESS_MUXCLIENT|SUPPRESS_NOCMDLINE}, 9544f52dfbbSDag-Erling Smørgrav {"R", "request rekey", SUPPRESS_NEVER}, 9556888a9beSDag-Erling Smørgrav {"V/v", "decrease/increase verbosity (LogLevel)", SUPPRESS_MUXCLIENT}, 9566888a9beSDag-Erling Smørgrav {"^Z", "suspend ssh", SUPPRESS_MUXCLIENT}, 9576888a9beSDag-Erling Smørgrav {"#", "list forwarded connections", SUPPRESS_NEVER}, 9586888a9beSDag-Erling Smørgrav {"&", "background ssh (when waiting for connections to terminate)", 9596888a9beSDag-Erling Smørgrav SUPPRESS_MUXCLIENT}, 9606888a9beSDag-Erling Smørgrav {"?", "this message", SUPPRESS_NEVER}, 9616888a9beSDag-Erling Smørgrav }; 9626888a9beSDag-Erling Smørgrav 9636888a9beSDag-Erling Smørgrav static void 964190cef3dSDag-Erling Smørgrav print_escape_help(struct sshbuf *b, int escape_char, int mux_client, 965190cef3dSDag-Erling Smørgrav int using_stderr) 9666888a9beSDag-Erling Smørgrav { 9676888a9beSDag-Erling Smørgrav unsigned int i, suppress_flags; 968190cef3dSDag-Erling Smørgrav int r; 9696888a9beSDag-Erling Smørgrav 970190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(b, 971190cef3dSDag-Erling Smørgrav "%c?\r\nSupported escape sequences:\r\n", escape_char)) != 0) 97219261079SEd Maste fatal_fr(r, "sshbuf_putf"); 9736888a9beSDag-Erling Smørgrav 9744f52dfbbSDag-Erling Smørgrav suppress_flags = 9756888a9beSDag-Erling Smørgrav (mux_client ? SUPPRESS_MUXCLIENT : 0) | 9766888a9beSDag-Erling Smørgrav (mux_client ? 0 : SUPPRESS_MUXMASTER) | 977f374ba41SEd Maste (using_stderr ? 0 : SUPPRESS_SYSLOG) | 978f374ba41SEd Maste (options.enable_escape_commandline == 0 ? SUPPRESS_NOCMDLINE : 0); 9796888a9beSDag-Erling Smørgrav 9806888a9beSDag-Erling Smørgrav for (i = 0; i < sizeof(esc_txt)/sizeof(esc_txt[0]); i++) { 9816888a9beSDag-Erling Smørgrav if (esc_txt[i].flags & suppress_flags) 9826888a9beSDag-Erling Smørgrav continue; 983190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(b, " %c%-3s - %s\r\n", 984190cef3dSDag-Erling Smørgrav escape_char, esc_txt[i].cmd, esc_txt[i].text)) != 0) 98519261079SEd Maste fatal_fr(r, "sshbuf_putf"); 9866888a9beSDag-Erling Smørgrav } 9876888a9beSDag-Erling Smørgrav 988190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(b, 9896888a9beSDag-Erling Smørgrav " %c%c - send the escape character by typing it twice\r\n" 9906888a9beSDag-Erling Smørgrav "(Note that escapes are only recognized immediately after " 991190cef3dSDag-Erling Smørgrav "newline.)\r\n", escape_char, escape_char)) != 0) 99219261079SEd Maste fatal_fr(r, "sshbuf_putf"); 9936888a9beSDag-Erling Smørgrav } 9946888a9beSDag-Erling Smørgrav 995d4af9e69SDag-Erling Smørgrav /* 9964f52dfbbSDag-Erling Smørgrav * Process the characters one by one. 997d4af9e69SDag-Erling Smørgrav */ 998ae1f160dSDag-Erling Smørgrav static int 9994f52dfbbSDag-Erling Smørgrav process_escapes(struct ssh *ssh, Channel *c, 1000190cef3dSDag-Erling Smørgrav struct sshbuf *bin, struct sshbuf *bout, struct sshbuf *berr, 1001d4af9e69SDag-Erling Smørgrav char *buf, int len) 1002b66f2d16SKris Kennaway { 1003b66f2d16SKris Kennaway pid_t pid; 1004190cef3dSDag-Erling Smørgrav int r, bytes = 0; 10051e8db6e2SBrian Feldman u_int i; 10061e8db6e2SBrian Feldman u_char ch; 1007b66f2d16SKris Kennaway char *s; 10084f52dfbbSDag-Erling Smørgrav struct escape_filter_ctx *efc = c->filter_ctx == NULL ? 10094f52dfbbSDag-Erling Smørgrav NULL : (struct escape_filter_ctx *)c->filter_ctx; 1010d4af9e69SDag-Erling Smørgrav 1011d4af9e69SDag-Erling Smørgrav if (c->filter_ctx == NULL) 1012d4af9e69SDag-Erling Smørgrav return 0; 1013b66f2d16SKris Kennaway 1014043840dfSDag-Erling Smørgrav if (len <= 0) 1015043840dfSDag-Erling Smørgrav return (0); 1016043840dfSDag-Erling Smørgrav 1017043840dfSDag-Erling Smørgrav for (i = 0; i < (u_int)len; i++) { 1018b66f2d16SKris Kennaway /* Get one character at a time. */ 1019b66f2d16SKris Kennaway ch = buf[i]; 1020b66f2d16SKris Kennaway 10214f52dfbbSDag-Erling Smørgrav if (efc->escape_pending) { 1022b66f2d16SKris Kennaway /* We have previously seen an escape character. */ 1023b66f2d16SKris Kennaway /* Clear the flag now. */ 10244f52dfbbSDag-Erling Smørgrav efc->escape_pending = 0; 1025b66f2d16SKris Kennaway 1026b66f2d16SKris Kennaway /* Process the escaped character. */ 1027b66f2d16SKris Kennaway switch (ch) { 1028b66f2d16SKris Kennaway case '.': 1029b66f2d16SKris Kennaway /* Terminate the connection. */ 1030190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(berr, "%c.\r\n", 1031190cef3dSDag-Erling Smørgrav efc->escape_char)) != 0) 103219261079SEd Maste fatal_fr(r, "sshbuf_putf"); 1033b15c8340SDag-Erling Smørgrav if (c && c->ctl_chan != -1) { 1034f374ba41SEd Maste channel_force_close(ssh, c, 1); 1035d4af9e69SDag-Erling Smørgrav return 0; 1036d4af9e69SDag-Erling Smørgrav } else 1037b66f2d16SKris Kennaway quit_pending = 1; 1038b66f2d16SKris Kennaway return -1; 1039b66f2d16SKris Kennaway 1040b66f2d16SKris Kennaway case 'Z' - 64: 1041d4af9e69SDag-Erling Smørgrav /* XXX support this for mux clients */ 1042b15c8340SDag-Erling Smørgrav if (c && c->ctl_chan != -1) { 10436888a9beSDag-Erling Smørgrav char b[16]; 1044d4af9e69SDag-Erling Smørgrav noescape: 10456888a9beSDag-Erling Smørgrav if (ch == 'Z' - 64) 10466888a9beSDag-Erling Smørgrav snprintf(b, sizeof b, "^Z"); 10476888a9beSDag-Erling Smørgrav else 10486888a9beSDag-Erling Smørgrav snprintf(b, sizeof b, "%c", ch); 1049190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(berr, 10506888a9beSDag-Erling Smørgrav "%c%s escape not available to " 1051d4af9e69SDag-Erling Smørgrav "multiplexed sessions\r\n", 1052190cef3dSDag-Erling Smørgrav efc->escape_char, b)) != 0) 105319261079SEd Maste fatal_fr(r, "sshbuf_putf"); 1054d4af9e69SDag-Erling Smørgrav continue; 1055d4af9e69SDag-Erling Smørgrav } 1056d4af9e69SDag-Erling Smørgrav /* Suspend the program. Inform the user */ 1057190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(berr, 1058190cef3dSDag-Erling Smørgrav "%c^Z [suspend ssh]\r\n", 1059190cef3dSDag-Erling Smørgrav efc->escape_char)) != 0) 106019261079SEd Maste fatal_fr(r, "sshbuf_putf"); 1061b66f2d16SKris Kennaway 1062b66f2d16SKris Kennaway /* Restore terminal modes and suspend. */ 1063b66f2d16SKris Kennaway client_suspend_self(bin, bout, berr); 1064b66f2d16SKris Kennaway 1065b66f2d16SKris Kennaway /* We have been continued. */ 1066b66f2d16SKris Kennaway continue; 1067b66f2d16SKris Kennaway 1068d95e11bfSDag-Erling Smørgrav case 'B': 1069190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(berr, 1070190cef3dSDag-Erling Smørgrav "%cB\r\n", efc->escape_char)) != 0) 107119261079SEd Maste fatal_fr(r, "sshbuf_putf"); 10724f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, c->self, "break", 0); 1073190cef3dSDag-Erling Smørgrav if ((r = sshpkt_put_u32(ssh, 1000)) != 0 || 1074190cef3dSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) 107519261079SEd Maste fatal_fr(r, "send packet"); 1076d95e11bfSDag-Erling Smørgrav continue; 1077d95e11bfSDag-Erling Smørgrav 10781e8db6e2SBrian Feldman case 'R': 107919261079SEd Maste if (ssh->compat & SSH_BUG_NOREKEY) 1080d4af9e69SDag-Erling Smørgrav logit("Server does not " 1081d4af9e69SDag-Erling Smørgrav "support re-keying"); 10821e8db6e2SBrian Feldman else 10831e8db6e2SBrian Feldman need_rekeying = 1; 10841e8db6e2SBrian Feldman continue; 10851e8db6e2SBrian Feldman 10866888a9beSDag-Erling Smørgrav case 'V': 10876888a9beSDag-Erling Smørgrav /* FALLTHROUGH */ 10886888a9beSDag-Erling Smørgrav case 'v': 10896888a9beSDag-Erling Smørgrav if (c && c->ctl_chan != -1) 10906888a9beSDag-Erling Smørgrav goto noescape; 10916888a9beSDag-Erling Smørgrav if (!log_is_on_stderr()) { 1092190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(berr, 10936888a9beSDag-Erling Smørgrav "%c%c [Logging to syslog]\r\n", 1094190cef3dSDag-Erling Smørgrav efc->escape_char, ch)) != 0) 109519261079SEd Maste fatal_fr(r, "sshbuf_putf"); 10966888a9beSDag-Erling Smørgrav continue; 10976888a9beSDag-Erling Smørgrav } 10986888a9beSDag-Erling Smørgrav if (ch == 'V' && options.log_level > 10996888a9beSDag-Erling Smørgrav SYSLOG_LEVEL_QUIET) 11006888a9beSDag-Erling Smørgrav log_change_level(--options.log_level); 11016888a9beSDag-Erling Smørgrav if (ch == 'v' && options.log_level < 11026888a9beSDag-Erling Smørgrav SYSLOG_LEVEL_DEBUG3) 11036888a9beSDag-Erling Smørgrav log_change_level(++options.log_level); 1104190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(berr, 11054f52dfbbSDag-Erling Smørgrav "%c%c [LogLevel %s]\r\n", 11064f52dfbbSDag-Erling Smørgrav efc->escape_char, ch, 1107190cef3dSDag-Erling Smørgrav log_level_name(options.log_level))) != 0) 110819261079SEd Maste fatal_fr(r, "sshbuf_putf"); 11096888a9beSDag-Erling Smørgrav continue; 11106888a9beSDag-Erling Smørgrav 1111b66f2d16SKris Kennaway case '&': 1112b15c8340SDag-Erling Smørgrav if (c && c->ctl_chan != -1) 1113d4af9e69SDag-Erling Smørgrav goto noescape; 1114b66f2d16SKris Kennaway /* 1115d4af9e69SDag-Erling Smørgrav * Detach the program (continue to serve 1116d4af9e69SDag-Erling Smørgrav * connections, but put in background and no 1117d4af9e69SDag-Erling Smørgrav * more new connections). 1118b66f2d16SKris Kennaway */ 1119ae1f160dSDag-Erling Smørgrav /* Restore tty modes. */ 1120e146993eSDag-Erling Smørgrav leave_raw_mode( 1121e146993eSDag-Erling Smørgrav options.request_tty == REQUEST_TTY_FORCE); 1122ae1f160dSDag-Erling Smørgrav 1123ae1f160dSDag-Erling Smørgrav /* Stop listening for new connections. */ 11244f52dfbbSDag-Erling Smørgrav channel_stop_listening(ssh); 1125ae1f160dSDag-Erling Smørgrav 112619261079SEd Maste if ((r = sshbuf_putf(berr, "%c& " 112719261079SEd Maste "[backgrounded]\n", efc->escape_char)) != 0) 112819261079SEd Maste fatal_fr(r, "sshbuf_putf"); 1129ae1f160dSDag-Erling Smørgrav 1130ae1f160dSDag-Erling Smørgrav /* Fork into background. */ 1131ae1f160dSDag-Erling Smørgrav pid = fork(); 113219261079SEd Maste if (pid == -1) { 1133ae1f160dSDag-Erling Smørgrav error("fork: %.100s", strerror(errno)); 1134ae1f160dSDag-Erling Smørgrav continue; 1135ae1f160dSDag-Erling Smørgrav } 1136ae1f160dSDag-Erling Smørgrav if (pid != 0) { /* This is the parent. */ 1137ae1f160dSDag-Erling Smørgrav /* The parent just exits. */ 1138ae1f160dSDag-Erling Smørgrav exit(0); 1139ae1f160dSDag-Erling Smørgrav } 1140ae1f160dSDag-Erling Smørgrav /* The child continues serving connections. */ 1141ae1f160dSDag-Erling Smørgrav /* fake EOF on stdin */ 1142190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u8(bin, 4)) != 0) 114319261079SEd Maste fatal_fr(r, "sshbuf_put_u8"); 1144ae1f160dSDag-Erling Smørgrav return -1; 1145b66f2d16SKris Kennaway case '?': 11464f52dfbbSDag-Erling Smørgrav print_escape_help(berr, efc->escape_char, 11476888a9beSDag-Erling Smørgrav (c && c->ctl_chan != -1), 11486888a9beSDag-Erling Smørgrav log_is_on_stderr()); 1149b66f2d16SKris Kennaway continue; 1150b66f2d16SKris Kennaway 1151b66f2d16SKris Kennaway case '#': 1152190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(berr, "%c#\r\n", 1153190cef3dSDag-Erling Smørgrav efc->escape_char)) != 0) 115419261079SEd Maste fatal_fr(r, "sshbuf_putf"); 11554f52dfbbSDag-Erling Smørgrav s = channel_open_message(ssh); 1156190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put(berr, s, strlen(s))) != 0) 115719261079SEd Maste fatal_fr(r, "sshbuf_put"); 1158e4a9863fSDag-Erling Smørgrav free(s); 1159b66f2d16SKris Kennaway continue; 1160b66f2d16SKris Kennaway 1161545d5ecaSDag-Erling Smørgrav case 'C': 1162b15c8340SDag-Erling Smørgrav if (c && c->ctl_chan != -1) 1163cce7d346SDag-Erling Smørgrav goto noescape; 1164f374ba41SEd Maste if (options.enable_escape_commandline == 0) { 1165f374ba41SEd Maste if ((r = sshbuf_putf(berr, 1166f374ba41SEd Maste "commandline disabled\r\n")) != 0) 1167f374ba41SEd Maste fatal_fr(r, "sshbuf_putf"); 1168f374ba41SEd Maste continue; 1169f374ba41SEd Maste } 11704f52dfbbSDag-Erling Smørgrav process_cmdline(ssh); 1171545d5ecaSDag-Erling Smørgrav continue; 1172545d5ecaSDag-Erling Smørgrav 1173b66f2d16SKris Kennaway default: 11744f52dfbbSDag-Erling Smørgrav if (ch != efc->escape_char) { 1175190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u8(bin, 1176190cef3dSDag-Erling Smørgrav efc->escape_char)) != 0) 117719261079SEd Maste fatal_fr(r, "sshbuf_put_u8"); 1178b66f2d16SKris Kennaway bytes++; 1179b66f2d16SKris Kennaway } 1180b66f2d16SKris Kennaway /* Escaped characters fall through here */ 1181b66f2d16SKris Kennaway break; 1182b66f2d16SKris Kennaway } 1183b66f2d16SKris Kennaway } else { 1184b66f2d16SKris Kennaway /* 1185d4af9e69SDag-Erling Smørgrav * The previous character was not an escape char. 1186d4af9e69SDag-Erling Smørgrav * Check if this is an escape. 1187b66f2d16SKris Kennaway */ 11884f52dfbbSDag-Erling Smørgrav if (last_was_cr && ch == efc->escape_char) { 1189d4af9e69SDag-Erling Smørgrav /* 1190d4af9e69SDag-Erling Smørgrav * It is. Set the flag and continue to 1191d4af9e69SDag-Erling Smørgrav * next character. 1192d4af9e69SDag-Erling Smørgrav */ 11934f52dfbbSDag-Erling Smørgrav efc->escape_pending = 1; 1194b66f2d16SKris Kennaway continue; 1195b66f2d16SKris Kennaway } 1196b66f2d16SKris Kennaway } 1197b66f2d16SKris Kennaway 1198b66f2d16SKris Kennaway /* 1199b66f2d16SKris Kennaway * Normal character. Record whether it was a newline, 1200b66f2d16SKris Kennaway * and append it to the buffer. 1201b66f2d16SKris Kennaway */ 1202b66f2d16SKris Kennaway last_was_cr = (ch == '\r' || ch == '\n'); 1203190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u8(bin, ch)) != 0) 120419261079SEd Maste fatal_fr(r, "sshbuf_put_u8"); 1205b66f2d16SKris Kennaway bytes++; 1206b66f2d16SKris Kennaway } 1207b66f2d16SKris Kennaway return bytes; 1208b66f2d16SKris Kennaway } 1209b66f2d16SKris Kennaway 1210511b41d2SMark Murray /* 1211a04a10f8SKris Kennaway * Get packets from the connection input buffer, and process them as long as 1212a04a10f8SKris Kennaway * there are packets available. 1213a04a10f8SKris Kennaway * 1214a04a10f8SKris Kennaway * Any unknown packets received during the actual 1215a04a10f8SKris Kennaway * session cause the session to terminate. This is 1216a04a10f8SKris Kennaway * intended to make debugging easier since no 1217a04a10f8SKris Kennaway * confirmations are sent. Any compatible protocol 1218a04a10f8SKris Kennaway * extensions must be negotiated during the 1219a04a10f8SKris Kennaway * preparatory phase. 1220a04a10f8SKris Kennaway */ 1221a04a10f8SKris Kennaway 1222ae1f160dSDag-Erling Smørgrav static void 122319261079SEd Maste client_process_buffered_input_packets(struct ssh *ssh) 1224a04a10f8SKris Kennaway { 122519261079SEd Maste ssh_dispatch_run_fatal(ssh, DISPATCH_NONBLOCK, &quit_pending); 1226a04a10f8SKris Kennaway } 1227a04a10f8SKris Kennaway 1228b66f2d16SKris Kennaway /* scan buf[] for '~' before sending data to the peer */ 1229b66f2d16SKris Kennaway 1230d4af9e69SDag-Erling Smørgrav /* Helper: allocate a new escape_filter_ctx and fill in its escape char */ 1231d4af9e69SDag-Erling Smørgrav void * 1232d4af9e69SDag-Erling Smørgrav client_new_escape_filter_ctx(int escape_char) 1233b66f2d16SKris Kennaway { 1234d4af9e69SDag-Erling Smørgrav struct escape_filter_ctx *ret; 1235d4af9e69SDag-Erling Smørgrav 12360a37d4a3SXin LI ret = xcalloc(1, sizeof(*ret)); 1237d4af9e69SDag-Erling Smørgrav ret->escape_pending = 0; 1238d4af9e69SDag-Erling Smørgrav ret->escape_char = escape_char; 1239d4af9e69SDag-Erling Smørgrav return (void *)ret; 1240d4af9e69SDag-Erling Smørgrav } 1241d4af9e69SDag-Erling Smørgrav 1242d4af9e69SDag-Erling Smørgrav /* Free the escape filter context on channel free */ 1243d4af9e69SDag-Erling Smørgrav void 12444f52dfbbSDag-Erling Smørgrav client_filter_cleanup(struct ssh *ssh, int cid, void *ctx) 1245d4af9e69SDag-Erling Smørgrav { 1246e4a9863fSDag-Erling Smørgrav free(ctx); 1247d4af9e69SDag-Erling Smørgrav } 1248d4af9e69SDag-Erling Smørgrav 1249d4af9e69SDag-Erling Smørgrav int 12504f52dfbbSDag-Erling Smørgrav client_simple_escape_filter(struct ssh *ssh, Channel *c, char *buf, int len) 1251d4af9e69SDag-Erling Smørgrav { 1252d4af9e69SDag-Erling Smørgrav if (c->extended_usage != CHAN_EXTENDED_WRITE) 1253d4af9e69SDag-Erling Smørgrav return 0; 1254d4af9e69SDag-Erling Smørgrav 12554f52dfbbSDag-Erling Smørgrav return process_escapes(ssh, c, c->input, c->output, c->extended, 1256d4af9e69SDag-Erling Smørgrav buf, len); 1257b66f2d16SKris Kennaway } 1258b66f2d16SKris Kennaway 1259ae1f160dSDag-Erling Smørgrav static void 1260f374ba41SEd Maste client_channel_closed(struct ssh *ssh, int id, int force, void *arg) 12611e8db6e2SBrian Feldman { 12624f52dfbbSDag-Erling Smørgrav channel_cancel_cleanup(ssh, id); 12631e8db6e2SBrian Feldman session_closed = 1; 1264e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 12651e8db6e2SBrian Feldman } 12661e8db6e2SBrian Feldman 1267a04a10f8SKris Kennaway /* 1268511b41d2SMark Murray * Implements the interactive session with the server. This is called after 1269511b41d2SMark Murray * the user has been authenticated, and a command has been started on the 1270ae1f160dSDag-Erling Smørgrav * remote host. If escape_char != SSH_ESCAPECHAR_NONE, it is the character 1271ae1f160dSDag-Erling Smørgrav * used as an escape character for terminating or suspending the session. 1272511b41d2SMark Murray */ 1273511b41d2SMark Murray int 12744f52dfbbSDag-Erling Smørgrav client_loop(struct ssh *ssh, int have_pty, int escape_char_arg, 12754f52dfbbSDag-Erling Smørgrav int ssh2_chan_id) 1276511b41d2SMark Murray { 12771323ec57SEd Maste struct pollfd *pfd = NULL; 12781323ec57SEd Maste u_int npfd_alloc = 0, npfd_active = 0; 1279511b41d2SMark Murray double start_time, total_time; 12801323ec57SEd Maste int r, len; 1281d4af9e69SDag-Erling Smørgrav u_int64_t ibytes, obytes; 12821323ec57SEd Maste int conn_in_ready, conn_out_ready; 1283511b41d2SMark Murray 1284511b41d2SMark Murray debug("Entering interactive session."); 1285f374ba41SEd Maste session_ident = ssh2_chan_id; 1286511b41d2SMark Murray 1287acc1a9efSDag-Erling Smørgrav if (options.control_master && 1288acc1a9efSDag-Erling Smørgrav !option_clear_or_none(options.control_path)) { 1289acc1a9efSDag-Erling Smørgrav debug("pledge: id"); 129019261079SEd Maste if (pledge("stdio rpath wpath cpath unix inet dns recvfd sendfd proc exec id tty", 1291acc1a9efSDag-Erling Smørgrav NULL) == -1) 129219261079SEd Maste fatal_f("pledge(): %s", strerror(errno)); 1293acc1a9efSDag-Erling Smørgrav 1294acc1a9efSDag-Erling Smørgrav } else if (options.forward_x11 || options.permit_local_command) { 1295acc1a9efSDag-Erling Smørgrav debug("pledge: exec"); 1296acc1a9efSDag-Erling Smørgrav if (pledge("stdio rpath wpath cpath unix inet dns proc exec tty", 1297acc1a9efSDag-Erling Smørgrav NULL) == -1) 129819261079SEd Maste fatal_f("pledge(): %s", strerror(errno)); 1299acc1a9efSDag-Erling Smørgrav 1300acc1a9efSDag-Erling Smørgrav } else if (options.update_hostkeys) { 13011323ec57SEd Maste debug("pledge: filesystem"); 1302acc1a9efSDag-Erling Smørgrav if (pledge("stdio rpath wpath cpath unix inet dns proc tty", 1303acc1a9efSDag-Erling Smørgrav NULL) == -1) 130419261079SEd Maste fatal_f("pledge(): %s", strerror(errno)); 1305acc1a9efSDag-Erling Smørgrav 1306076ad2f8SDag-Erling Smørgrav } else if (!option_clear_or_none(options.proxy_command) || 130719261079SEd Maste options.fork_after_authentication) { 1308acc1a9efSDag-Erling Smørgrav debug("pledge: proc"); 1309acc1a9efSDag-Erling Smørgrav if (pledge("stdio cpath unix inet dns proc tty", NULL) == -1) 131019261079SEd Maste fatal_f("pledge(): %s", strerror(errno)); 1311acc1a9efSDag-Erling Smørgrav 1312acc1a9efSDag-Erling Smørgrav } else { 1313acc1a9efSDag-Erling Smørgrav debug("pledge: network"); 13144f52dfbbSDag-Erling Smørgrav if (pledge("stdio unix inet dns proc tty", NULL) == -1) 131519261079SEd Maste fatal_f("pledge(): %s", strerror(errno)); 1316acc1a9efSDag-Erling Smørgrav } 1317acc1a9efSDag-Erling Smørgrav 1318f374ba41SEd Maste /* might be able to tighten now */ 1319f374ba41SEd Maste client_repledge(); 1320f374ba41SEd Maste 132147dd1d1bSDag-Erling Smørgrav start_time = monotime_double(); 1322511b41d2SMark Murray 1323511b41d2SMark Murray /* Initialize variables. */ 1324511b41d2SMark Murray last_was_cr = 1; 1325511b41d2SMark Murray exit_status = -1; 132619261079SEd Maste connection_in = ssh_packet_get_connection_in(ssh); 132719261079SEd Maste connection_out = ssh_packet_get_connection_out(ssh); 13281e8db6e2SBrian Feldman 1329511b41d2SMark Murray quit_pending = 0; 1330511b41d2SMark Murray 1331190cef3dSDag-Erling Smørgrav /* Initialize buffer. */ 1332190cef3dSDag-Erling Smørgrav if ((stderr_buffer = sshbuf_new()) == NULL) 133319261079SEd Maste fatal_f("sshbuf_new failed"); 1334511b41d2SMark Murray 133519261079SEd Maste client_init_dispatch(ssh); 1336a04a10f8SKris Kennaway 1337d0c8c0bcSDag-Erling Smørgrav /* 1338d0c8c0bcSDag-Erling Smørgrav * Set signal handlers, (e.g. to restore non-blocking mode) 1339d0c8c0bcSDag-Erling Smørgrav * but don't overwrite SIG_IGN, matches behaviour from rsh(1) 1340d0c8c0bcSDag-Erling Smørgrav */ 134119261079SEd Maste if (ssh_signal(SIGHUP, SIG_IGN) != SIG_IGN) 134219261079SEd Maste ssh_signal(SIGHUP, signal_handler); 134319261079SEd Maste if (ssh_signal(SIGINT, SIG_IGN) != SIG_IGN) 134419261079SEd Maste ssh_signal(SIGINT, signal_handler); 134519261079SEd Maste if (ssh_signal(SIGQUIT, SIG_IGN) != SIG_IGN) 134619261079SEd Maste ssh_signal(SIGQUIT, signal_handler); 134719261079SEd Maste if (ssh_signal(SIGTERM, SIG_IGN) != SIG_IGN) 134819261079SEd Maste ssh_signal(SIGTERM, signal_handler); 134919261079SEd Maste ssh_signal(SIGWINCH, window_change_handler); 1350511b41d2SMark Murray 1351511b41d2SMark Murray if (have_pty) 1352e146993eSDag-Erling Smørgrav enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 1353511b41d2SMark Murray 1354e146993eSDag-Erling Smørgrav if (session_ident != -1) { 1355e146993eSDag-Erling Smørgrav if (escape_char_arg != SSH_ESCAPECHAR_NONE) { 13564f52dfbbSDag-Erling Smørgrav channel_register_filter(ssh, session_ident, 1357d4af9e69SDag-Erling Smørgrav client_simple_escape_filter, NULL, 1358d4af9e69SDag-Erling Smørgrav client_filter_cleanup, 1359e146993eSDag-Erling Smørgrav client_new_escape_filter_ctx( 1360e146993eSDag-Erling Smørgrav escape_char_arg)); 1361e146993eSDag-Erling Smørgrav } 13624f52dfbbSDag-Erling Smørgrav channel_register_cleanup(ssh, session_ident, 1363021d409fSDag-Erling Smørgrav client_channel_closed, 0); 1364e146993eSDag-Erling Smørgrav } 1365b66f2d16SKris Kennaway 136619261079SEd Maste schedule_server_alive_check(); 136719261079SEd Maste 1368511b41d2SMark Murray /* Main loop of the client for the interactive session mode. */ 1369511b41d2SMark Murray while (!quit_pending) { 1370511b41d2SMark Murray 1371511b41d2SMark Murray /* Process buffered packets sent by the server. */ 137219261079SEd Maste client_process_buffered_input_packets(ssh); 1373511b41d2SMark Murray 13744f52dfbbSDag-Erling Smørgrav if (session_closed && !channel_still_open(ssh)) 1375a04a10f8SKris Kennaway break; 1376a04a10f8SKris Kennaway 13774f52dfbbSDag-Erling Smørgrav if (ssh_packet_is_rekeying(ssh)) { 13781e8db6e2SBrian Feldman debug("rekeying in progress"); 1379acc1a9efSDag-Erling Smørgrav } else if (need_rekeying) { 1380acc1a9efSDag-Erling Smørgrav /* manual rekey request */ 1381acc1a9efSDag-Erling Smørgrav debug("need rekeying"); 13824f52dfbbSDag-Erling Smørgrav if ((r = kex_start_rekex(ssh)) != 0) 138319261079SEd Maste fatal_fr(r, "kex_start_rekex"); 1384acc1a9efSDag-Erling Smørgrav need_rekeying = 0; 13851e8db6e2SBrian Feldman } else { 1386511b41d2SMark Murray /* 13871e8db6e2SBrian Feldman * Make packets from buffered channel data, and 13881e8db6e2SBrian Feldman * enqueue them for sending to the server. 1389511b41d2SMark Murray */ 139019261079SEd Maste if (ssh_packet_not_very_much_data_to_write(ssh)) 13914f52dfbbSDag-Erling Smørgrav channel_output_poll(ssh); 1392511b41d2SMark Murray 1393511b41d2SMark Murray /* 13941e8db6e2SBrian Feldman * Check if the window size has changed, and buffer a 13951e8db6e2SBrian Feldman * message about it to the server if so. 1396511b41d2SMark Murray */ 13974f52dfbbSDag-Erling Smørgrav client_check_window_change(ssh); 1398511b41d2SMark Murray 1399511b41d2SMark Murray if (quit_pending) 1400511b41d2SMark Murray break; 14011e8db6e2SBrian Feldman } 1402511b41d2SMark Murray /* 1403511b41d2SMark Murray * Wait until we have something to do (something becomes 1404511b41d2SMark Murray * available on one of the descriptors). 1405511b41d2SMark Murray */ 14061323ec57SEd Maste client_wait_until_can_do_something(ssh, &pfd, &npfd_alloc, 14071323ec57SEd Maste &npfd_active, ssh_packet_is_rekeying(ssh), 14081323ec57SEd Maste &conn_in_ready, &conn_out_ready); 1409511b41d2SMark Murray 1410511b41d2SMark Murray if (quit_pending) 1411511b41d2SMark Murray break; 1412511b41d2SMark Murray 141338a52bd3SEd Maste /* Do channel operations. */ 14141323ec57SEd Maste channel_after_poll(ssh, pfd, npfd_active); 1415511b41d2SMark Murray 1416a04a10f8SKris Kennaway /* Buffer input from the connection. */ 14171323ec57SEd Maste if (conn_in_ready) 14181323ec57SEd Maste client_process_net_input(ssh); 1419511b41d2SMark Murray 1420a04a10f8SKris Kennaway if (quit_pending) 1421a04a10f8SKris Kennaway break; 1422a04a10f8SKris Kennaway 142319261079SEd Maste /* A timeout may have triggered rekeying */ 142419261079SEd Maste if ((r = ssh_packet_check_rekey(ssh)) != 0) 142519261079SEd Maste fatal_fr(r, "cannot start rekeying"); 142619261079SEd Maste 1427d4af9e69SDag-Erling Smørgrav /* 1428d4af9e69SDag-Erling Smørgrav * Send as much buffered packet data as possible to the 1429d4af9e69SDag-Erling Smørgrav * sender. 1430d4af9e69SDag-Erling Smørgrav */ 14311323ec57SEd Maste if (conn_out_ready) { 143219261079SEd Maste if ((r = ssh_packet_write_poll(ssh)) != 0) { 143319261079SEd Maste sshpkt_fatal(ssh, r, 143419261079SEd Maste "%s: ssh_packet_write_poll", __func__); 143519261079SEd Maste } 143619261079SEd Maste } 1437e2f6069cSDag-Erling Smørgrav 1438e2f6069cSDag-Erling Smørgrav /* 1439e2f6069cSDag-Erling Smørgrav * If we are a backgrounded control master, and the 1440e2f6069cSDag-Erling Smørgrav * timeout has expired without any active client 1441e2f6069cSDag-Erling Smørgrav * connections, then quit. 1442e2f6069cSDag-Erling Smørgrav */ 1443e2f6069cSDag-Erling Smørgrav if (control_persist_exit_time > 0) { 1444e4a9863fSDag-Erling Smørgrav if (monotime() >= control_persist_exit_time) { 1445e2f6069cSDag-Erling Smørgrav debug("ControlPersist timeout expired"); 1446e2f6069cSDag-Erling Smørgrav break; 1447e2f6069cSDag-Erling Smørgrav } 1448e2f6069cSDag-Erling Smørgrav } 1449511b41d2SMark Murray } 14501323ec57SEd Maste free(pfd); 1451511b41d2SMark Murray 1452511b41d2SMark Murray /* Terminate the session. */ 1453511b41d2SMark Murray 1454511b41d2SMark Murray /* Stop watching for window change. */ 145519261079SEd Maste ssh_signal(SIGWINCH, SIG_DFL); 1456511b41d2SMark Murray 145719261079SEd Maste if ((r = sshpkt_start(ssh, SSH2_MSG_DISCONNECT)) != 0 || 145819261079SEd Maste (r = sshpkt_put_u32(ssh, SSH2_DISCONNECT_BY_APPLICATION)) != 0 || 145919261079SEd Maste (r = sshpkt_put_cstring(ssh, "disconnected by user")) != 0 || 146019261079SEd Maste (r = sshpkt_put_cstring(ssh, "")) != 0 || /* language tag */ 146119261079SEd Maste (r = sshpkt_send(ssh)) != 0 || 146219261079SEd Maste (r = ssh_packet_write_wait(ssh)) != 0) 146319261079SEd Maste fatal_fr(r, "send disconnect"); 14647aee6ffeSDag-Erling Smørgrav 14654f52dfbbSDag-Erling Smørgrav channel_free_all(ssh); 1466ae1f160dSDag-Erling Smørgrav 1467ae1f160dSDag-Erling Smørgrav if (have_pty) 1468e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 1469ae1f160dSDag-Erling Smørgrav 1470efcad6b7SDag-Erling Smørgrav /* 1471efcad6b7SDag-Erling Smørgrav * If there was no shell or command requested, there will be no remote 1472efcad6b7SDag-Erling Smørgrav * exit status to be returned. In that case, clear error code if the 1473efcad6b7SDag-Erling Smørgrav * connection was deliberately terminated at this end. 1474efcad6b7SDag-Erling Smørgrav */ 1475e9e8876aSEd Maste if (options.session_type == SESSION_TYPE_NONE && 1476e9e8876aSEd Maste received_signal == SIGTERM) { 1477efcad6b7SDag-Erling Smørgrav received_signal = 0; 1478efcad6b7SDag-Erling Smørgrav exit_status = 0; 1479ae1f160dSDag-Erling Smørgrav } 1480511b41d2SMark Murray 14814f52dfbbSDag-Erling Smørgrav if (received_signal) { 14824f52dfbbSDag-Erling Smørgrav verbose("Killed by signal %d.", (int) received_signal); 148319261079SEd Maste cleanup_exit(255); 14844f52dfbbSDag-Erling Smørgrav } 1485efcad6b7SDag-Erling Smørgrav 1486511b41d2SMark Murray /* 1487511b41d2SMark Murray * In interactive mode (with pseudo tty) display a message indicating 1488511b41d2SMark Murray * that the connection has been closed. 1489511b41d2SMark Murray */ 14901323ec57SEd Maste if (have_pty && options.log_level >= SYSLOG_LEVEL_INFO) 14911323ec57SEd Maste quit_message("Connection to %s closed.", host); 1492ae1f160dSDag-Erling Smørgrav 1493511b41d2SMark Murray /* Output any buffered data for stderr. */ 1494190cef3dSDag-Erling Smørgrav if (sshbuf_len(stderr_buffer) > 0) { 14954a421b63SDag-Erling Smørgrav len = atomicio(vwrite, fileno(stderr), 1496190cef3dSDag-Erling Smørgrav (u_char *)sshbuf_ptr(stderr_buffer), 1497190cef3dSDag-Erling Smørgrav sshbuf_len(stderr_buffer)); 1498190cef3dSDag-Erling Smørgrav if (len < 0 || (u_int)len != sshbuf_len(stderr_buffer)) 1499511b41d2SMark Murray error("Write failed flushing stderr buffer."); 1500190cef3dSDag-Erling Smørgrav else if ((r = sshbuf_consume(stderr_buffer, len)) != 0) 150119261079SEd Maste fatal_fr(r, "sshbuf_consume"); 1502511b41d2SMark Murray } 1503511b41d2SMark Murray 1504511b41d2SMark Murray /* Clear and free any buffers. */ 1505190cef3dSDag-Erling Smørgrav sshbuf_free(stderr_buffer); 1506511b41d2SMark Murray 1507511b41d2SMark Murray /* Report bytes transferred, and transfer rates. */ 150847dd1d1bSDag-Erling Smørgrav total_time = monotime_double() - start_time; 150919261079SEd Maste ssh_packet_get_bytes(ssh, &ibytes, &obytes); 1510d4af9e69SDag-Erling Smørgrav verbose("Transferred: sent %llu, received %llu bytes, in %.1f seconds", 15114a421b63SDag-Erling Smørgrav (unsigned long long)obytes, (unsigned long long)ibytes, total_time); 1512511b41d2SMark Murray if (total_time > 0) 1513d4af9e69SDag-Erling Smørgrav verbose("Bytes per second: sent %.1f, received %.1f", 1514d4af9e69SDag-Erling Smørgrav obytes / total_time, ibytes / total_time); 1515511b41d2SMark Murray /* Return the exit status of the program. */ 1516511b41d2SMark Murray debug("Exit status %d", exit_status); 1517511b41d2SMark Murray return exit_status; 1518511b41d2SMark Murray } 1519a04a10f8SKris Kennaway 1520a04a10f8SKris Kennaway /*********/ 1521a04a10f8SKris Kennaway 1522ae1f160dSDag-Erling Smørgrav static Channel * 15234f52dfbbSDag-Erling Smørgrav client_request_forwarded_tcpip(struct ssh *ssh, const char *request_type, 15244f52dfbbSDag-Erling Smørgrav int rchan, u_int rwindow, u_int rmaxpack) 15251e8db6e2SBrian Feldman { 15261e8db6e2SBrian Feldman Channel *c = NULL; 1527ca86bcf2SDag-Erling Smørgrav struct sshbuf *b = NULL; 15281e8db6e2SBrian Feldman char *listen_address, *originator_address; 152919261079SEd Maste u_int listen_port, originator_port; 1530ca86bcf2SDag-Erling Smørgrav int r; 15311e8db6e2SBrian Feldman 15321e8db6e2SBrian Feldman /* Get rest of the packet */ 153319261079SEd Maste if ((r = sshpkt_get_cstring(ssh, &listen_address, NULL)) != 0 || 153419261079SEd Maste (r = sshpkt_get_u32(ssh, &listen_port)) != 0 || 153519261079SEd Maste (r = sshpkt_get_cstring(ssh, &originator_address, NULL)) != 0 || 153619261079SEd Maste (r = sshpkt_get_u32(ssh, &originator_port)) != 0 || 153719261079SEd Maste (r = sshpkt_get_end(ssh)) != 0) 153819261079SEd Maste fatal_fr(r, "parse packet"); 15391e8db6e2SBrian Feldman 154019261079SEd Maste debug_f("listen %s port %d, originator %s port %d", 1541a0ee8cc6SDag-Erling Smørgrav listen_address, listen_port, originator_address, originator_port); 15421e8db6e2SBrian Feldman 154319261079SEd Maste if (listen_port > 0xffff) 154419261079SEd Maste error_f("invalid listen port"); 154519261079SEd Maste else if (originator_port > 0xffff) 154619261079SEd Maste error_f("invalid originator port"); 154719261079SEd Maste else { 154819261079SEd Maste c = channel_connect_by_listen_address(ssh, 154919261079SEd Maste listen_address, listen_port, "forwarded-tcpip", 155019261079SEd Maste originator_address); 155119261079SEd Maste } 1552d4af9e69SDag-Erling Smørgrav 1553ca86bcf2SDag-Erling Smørgrav if (c != NULL && c->type == SSH_CHANNEL_MUX_CLIENT) { 1554ca86bcf2SDag-Erling Smørgrav if ((b = sshbuf_new()) == NULL) { 155519261079SEd Maste error_f("alloc reply"); 1556ca86bcf2SDag-Erling Smørgrav goto out; 1557ca86bcf2SDag-Erling Smørgrav } 1558ca86bcf2SDag-Erling Smørgrav /* reconstruct and send to muxclient */ 1559ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_put_u8(b, 0)) != 0 || /* padlen */ 1560ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u8(b, SSH2_MSG_CHANNEL_OPEN)) != 0 || 1561ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_cstring(b, request_type)) != 0 || 1562ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, rchan)) != 0 || 1563ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, rwindow)) != 0 || 1564ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, rmaxpack)) != 0 || 1565ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_cstring(b, listen_address)) != 0 || 1566ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, listen_port)) != 0 || 1567ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_cstring(b, originator_address)) != 0 || 1568ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, originator_port)) != 0 || 15694f52dfbbSDag-Erling Smørgrav (r = sshbuf_put_stringb(c->output, b)) != 0) { 157019261079SEd Maste error_fr(r, "compose for muxclient"); 1571ca86bcf2SDag-Erling Smørgrav goto out; 1572ca86bcf2SDag-Erling Smørgrav } 1573ca86bcf2SDag-Erling Smørgrav } 1574ca86bcf2SDag-Erling Smørgrav 1575ca86bcf2SDag-Erling Smørgrav out: 1576ca86bcf2SDag-Erling Smørgrav sshbuf_free(b); 1577e4a9863fSDag-Erling Smørgrav free(originator_address); 1578e4a9863fSDag-Erling Smørgrav free(listen_address); 15791e8db6e2SBrian Feldman return c; 15801e8db6e2SBrian Feldman } 15811e8db6e2SBrian Feldman 1582ae1f160dSDag-Erling Smørgrav static Channel * 15834f52dfbbSDag-Erling Smørgrav client_request_forwarded_streamlocal(struct ssh *ssh, 15844f52dfbbSDag-Erling Smørgrav const char *request_type, int rchan) 1585a0ee8cc6SDag-Erling Smørgrav { 1586a0ee8cc6SDag-Erling Smørgrav Channel *c = NULL; 1587a0ee8cc6SDag-Erling Smørgrav char *listen_path; 158819261079SEd Maste int r; 1589a0ee8cc6SDag-Erling Smørgrav 1590a0ee8cc6SDag-Erling Smørgrav /* Get the remote path. */ 159119261079SEd Maste if ((r = sshpkt_get_cstring(ssh, &listen_path, NULL)) != 0 || 159219261079SEd Maste (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* reserved */ 159319261079SEd Maste (r = sshpkt_get_end(ssh)) != 0) 159419261079SEd Maste fatal_fr(r, "parse packet"); 1595a0ee8cc6SDag-Erling Smørgrav 159619261079SEd Maste debug_f("request: %s", listen_path); 1597a0ee8cc6SDag-Erling Smørgrav 15984f52dfbbSDag-Erling Smørgrav c = channel_connect_by_listen_path(ssh, listen_path, 1599a0ee8cc6SDag-Erling Smørgrav "forwarded-streamlocal@openssh.com", "forwarded-streamlocal"); 1600a0ee8cc6SDag-Erling Smørgrav free(listen_path); 1601a0ee8cc6SDag-Erling Smørgrav return c; 1602a0ee8cc6SDag-Erling Smørgrav } 1603a0ee8cc6SDag-Erling Smørgrav 1604a0ee8cc6SDag-Erling Smørgrav static Channel * 16054f52dfbbSDag-Erling Smørgrav client_request_x11(struct ssh *ssh, const char *request_type, int rchan) 16061e8db6e2SBrian Feldman { 16071e8db6e2SBrian Feldman Channel *c = NULL; 16081e8db6e2SBrian Feldman char *originator; 160919261079SEd Maste u_int originator_port; 161019261079SEd Maste int r, sock; 16111e8db6e2SBrian Feldman 16121e8db6e2SBrian Feldman if (!options.forward_x11) { 16131e8db6e2SBrian Feldman error("Warning: ssh server tried X11 forwarding."); 1614d4af9e69SDag-Erling Smørgrav error("Warning: this is probably a break-in attempt by a " 1615d4af9e69SDag-Erling Smørgrav "malicious server."); 16161e8db6e2SBrian Feldman return NULL; 16171e8db6e2SBrian Feldman } 1618*4d3fc8b0SEd Maste if (x11_refuse_time != 0 && monotime() >= x11_refuse_time) { 1619e2f6069cSDag-Erling Smørgrav verbose("Rejected X11 connection after ForwardX11Timeout " 1620e2f6069cSDag-Erling Smørgrav "expired"); 1621e2f6069cSDag-Erling Smørgrav return NULL; 1622e2f6069cSDag-Erling Smørgrav } 162319261079SEd Maste if ((r = sshpkt_get_cstring(ssh, &originator, NULL)) != 0 || 162419261079SEd Maste (r = sshpkt_get_u32(ssh, &originator_port)) != 0 || 162519261079SEd Maste (r = sshpkt_get_end(ssh)) != 0) 162619261079SEd Maste fatal_fr(r, "parse packet"); 16271e8db6e2SBrian Feldman /* XXX check permission */ 162819261079SEd Maste /* XXX range check originator port? */ 162919261079SEd Maste debug("client_request_x11: request from %s %u", originator, 16301e8db6e2SBrian Feldman originator_port); 1631e4a9863fSDag-Erling Smørgrav free(originator); 16324f52dfbbSDag-Erling Smørgrav sock = x11_connect_display(ssh); 1633ae1f160dSDag-Erling Smørgrav if (sock < 0) 1634ae1f160dSDag-Erling Smørgrav return NULL; 16354f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, "x11", 163660c59fadSDag-Erling Smørgrav SSH_CHANNEL_X11_OPEN, sock, sock, -1, 163760c59fadSDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "x11", 1); 1638ae1f160dSDag-Erling Smørgrav c->force_drain = 1; 16391e8db6e2SBrian Feldman return c; 16401e8db6e2SBrian Feldman } 16411e8db6e2SBrian Feldman 1642ae1f160dSDag-Erling Smørgrav static Channel * 16434f52dfbbSDag-Erling Smørgrav client_request_agent(struct ssh *ssh, const char *request_type, int rchan) 16441e8db6e2SBrian Feldman { 16451e8db6e2SBrian Feldman Channel *c = NULL; 1646bc5531deSDag-Erling Smørgrav int r, sock; 16471e8db6e2SBrian Feldman 16481e8db6e2SBrian Feldman if (!options.forward_agent) { 16491e8db6e2SBrian Feldman error("Warning: ssh server tried agent forwarding."); 1650d4af9e69SDag-Erling Smørgrav error("Warning: this is probably a break-in attempt by a " 1651d4af9e69SDag-Erling Smørgrav "malicious server."); 16521e8db6e2SBrian Feldman return NULL; 16531e8db6e2SBrian Feldman } 165419261079SEd Maste if (forward_agent_sock_path == NULL) { 165519261079SEd Maste r = ssh_get_authentication_socket(&sock); 165619261079SEd Maste } else { 165719261079SEd Maste r = ssh_get_authentication_socket_path(forward_agent_sock_path, &sock); 165819261079SEd Maste } 165919261079SEd Maste if (r != 0) { 1660bc5531deSDag-Erling Smørgrav if (r != SSH_ERR_AGENT_NOT_PRESENT) 166119261079SEd Maste debug_fr(r, "ssh_get_authentication_socket"); 1662ae1f160dSDag-Erling Smørgrav return NULL; 1663bc5531deSDag-Erling Smørgrav } 16641323ec57SEd Maste if ((r = ssh_agent_bind_hostkey(sock, ssh->kex->initial_hostkey, 16651323ec57SEd Maste ssh->kex->session_id, ssh->kex->initial_sig, 1)) == 0) 16661323ec57SEd Maste debug_f("bound agent to hostkey"); 16671323ec57SEd Maste else 16681323ec57SEd Maste debug2_fr(r, "ssh_agent_bind_hostkey"); 16691323ec57SEd Maste 16704f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, "authentication agent connection", 16711e8db6e2SBrian Feldman SSH_CHANNEL_OPEN, sock, sock, -1, 1672e3bd730fSBryan Drewery CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, 167389986192SBrooks Davis "authentication agent connection", 1); 1674ae1f160dSDag-Erling Smørgrav c->force_drain = 1; 16751e8db6e2SBrian Feldman return c; 16761e8db6e2SBrian Feldman } 16771e8db6e2SBrian Feldman 167847dd1d1bSDag-Erling Smørgrav char * 16794f52dfbbSDag-Erling Smørgrav client_request_tun_fwd(struct ssh *ssh, int tun_mode, 168019261079SEd Maste int local_tun, int remote_tun, channel_open_fn *cb, void *cbctx) 1681d4af9e69SDag-Erling Smørgrav { 1682d4af9e69SDag-Erling Smørgrav Channel *c; 168319261079SEd Maste int r, fd; 168447dd1d1bSDag-Erling Smørgrav char *ifname = NULL; 1685d4af9e69SDag-Erling Smørgrav 1686d4af9e69SDag-Erling Smørgrav if (tun_mode == SSH_TUNMODE_NO) 1687d4af9e69SDag-Erling Smørgrav return 0; 1688d4af9e69SDag-Erling Smørgrav 1689d4af9e69SDag-Erling Smørgrav debug("Requesting tun unit %d in mode %d", local_tun, tun_mode); 1690d4af9e69SDag-Erling Smørgrav 1691d4af9e69SDag-Erling Smørgrav /* Open local tunnel device */ 169247dd1d1bSDag-Erling Smørgrav if ((fd = tun_open(local_tun, tun_mode, &ifname)) == -1) { 1693d4af9e69SDag-Erling Smørgrav error("Tunnel device open failed."); 169447dd1d1bSDag-Erling Smørgrav return NULL; 1695d4af9e69SDag-Erling Smørgrav } 169647dd1d1bSDag-Erling Smørgrav debug("Tunnel forwarding using interface %s", ifname); 1697d4af9e69SDag-Erling Smørgrav 16984f52dfbbSDag-Erling Smørgrav c = channel_new(ssh, "tun", SSH_CHANNEL_OPENING, fd, fd, -1, 169960c59fadSDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1); 1700d4af9e69SDag-Erling Smørgrav c->datagram = 1; 1701d4af9e69SDag-Erling Smørgrav 1702d4af9e69SDag-Erling Smørgrav #if defined(SSH_TUN_FILTER) 1703d4af9e69SDag-Erling Smørgrav if (options.tun_open == SSH_TUNMODE_POINTOPOINT) 17044f52dfbbSDag-Erling Smørgrav channel_register_filter(ssh, c->self, sys_tun_infilter, 1705d4af9e69SDag-Erling Smørgrav sys_tun_outfilter, NULL, NULL); 1706d4af9e69SDag-Erling Smørgrav #endif 1707d4af9e69SDag-Erling Smørgrav 170819261079SEd Maste if (cb != NULL) 170919261079SEd Maste channel_register_open_confirm(ssh, c->self, cb, cbctx); 171019261079SEd Maste 171119261079SEd Maste if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN)) != 0 || 171219261079SEd Maste (r = sshpkt_put_cstring(ssh, "tun@openssh.com")) != 0 || 171319261079SEd Maste (r = sshpkt_put_u32(ssh, c->self)) != 0 || 171419261079SEd Maste (r = sshpkt_put_u32(ssh, c->local_window_max)) != 0 || 171519261079SEd Maste (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0 || 171619261079SEd Maste (r = sshpkt_put_u32(ssh, tun_mode)) != 0 || 171719261079SEd Maste (r = sshpkt_put_u32(ssh, remote_tun)) != 0 || 171819261079SEd Maste (r = sshpkt_send(ssh)) != 0) 171919261079SEd Maste sshpkt_fatal(ssh, r, "%s: send reply", __func__); 1720d4af9e69SDag-Erling Smørgrav 172147dd1d1bSDag-Erling Smørgrav return ifname; 1722d4af9e69SDag-Erling Smørgrav } 1723d4af9e69SDag-Erling Smørgrav 1724a04a10f8SKris Kennaway /* XXXX move to generic input handler */ 1725bc5531deSDag-Erling Smørgrav static int 17264f52dfbbSDag-Erling Smørgrav client_input_channel_open(int type, u_int32_t seq, struct ssh *ssh) 1727a04a10f8SKris Kennaway { 1728a04a10f8SKris Kennaway Channel *c = NULL; 172919261079SEd Maste char *ctype = NULL; 173019261079SEd Maste int r; 173119261079SEd Maste u_int rchan; 173219261079SEd Maste size_t len; 173319261079SEd Maste u_int rmaxpack, rwindow; 1734a04a10f8SKris Kennaway 173519261079SEd Maste if ((r = sshpkt_get_cstring(ssh, &ctype, &len)) != 0 || 173619261079SEd Maste (r = sshpkt_get_u32(ssh, &rchan)) != 0 || 173719261079SEd Maste (r = sshpkt_get_u32(ssh, &rwindow)) != 0 || 173819261079SEd Maste (r = sshpkt_get_u32(ssh, &rmaxpack)) != 0) 173919261079SEd Maste goto out; 1740a04a10f8SKris Kennaway 1741a04a10f8SKris Kennaway debug("client_input_channel_open: ctype %s rchan %d win %d max %d", 1742a04a10f8SKris Kennaway ctype, rchan, rwindow, rmaxpack); 1743a04a10f8SKris Kennaway 17441e8db6e2SBrian Feldman if (strcmp(ctype, "forwarded-tcpip") == 0) { 17454f52dfbbSDag-Erling Smørgrav c = client_request_forwarded_tcpip(ssh, ctype, rchan, rwindow, 1746ca86bcf2SDag-Erling Smørgrav rmaxpack); 1747a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(ctype, "forwarded-streamlocal@openssh.com") == 0) { 17484f52dfbbSDag-Erling Smørgrav c = client_request_forwarded_streamlocal(ssh, ctype, rchan); 17491e8db6e2SBrian Feldman } else if (strcmp(ctype, "x11") == 0) { 17504f52dfbbSDag-Erling Smørgrav c = client_request_x11(ssh, ctype, rchan); 17511e8db6e2SBrian Feldman } else if (strcmp(ctype, "auth-agent@openssh.com") == 0) { 17524f52dfbbSDag-Erling Smørgrav c = client_request_agent(ssh, ctype, rchan); 1753a04a10f8SKris Kennaway } 1754ca86bcf2SDag-Erling Smørgrav if (c != NULL && c->type == SSH_CHANNEL_MUX_CLIENT) { 1755ca86bcf2SDag-Erling Smørgrav debug3("proxied to downstream: %s", ctype); 1756ca86bcf2SDag-Erling Smørgrav } else if (c != NULL) { 1757a04a10f8SKris Kennaway debug("confirm %s", ctype); 1758a04a10f8SKris Kennaway c->remote_id = rchan; 17594f52dfbbSDag-Erling Smørgrav c->have_remote_id = 1; 1760a04a10f8SKris Kennaway c->remote_window = rwindow; 1761a04a10f8SKris Kennaway c->remote_maxpacket = rmaxpack; 1762ae1f160dSDag-Erling Smørgrav if (c->type != SSH_CHANNEL_CONNECTING) { 176319261079SEd Maste if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 || 176419261079SEd Maste (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 176519261079SEd Maste (r = sshpkt_put_u32(ssh, c->self)) != 0 || 176619261079SEd Maste (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || 176719261079SEd Maste (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0 || 176819261079SEd Maste (r = sshpkt_send(ssh)) != 0) 176919261079SEd Maste sshpkt_fatal(ssh, r, "%s: send reply", __func__); 1770ae1f160dSDag-Erling Smørgrav } 1771a04a10f8SKris Kennaway } else { 1772a04a10f8SKris Kennaway debug("failure %s", ctype); 177319261079SEd Maste if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_FAILURE)) != 0 || 177419261079SEd Maste (r = sshpkt_put_u32(ssh, rchan)) != 0 || 177519261079SEd Maste (r = sshpkt_put_u32(ssh, SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED)) != 0 || 177619261079SEd Maste (r = sshpkt_put_cstring(ssh, "open failed")) != 0 || 177719261079SEd Maste (r = sshpkt_put_cstring(ssh, "")) != 0 || 177819261079SEd Maste (r = sshpkt_send(ssh)) != 0) 177919261079SEd Maste sshpkt_fatal(ssh, r, "%s: send failure", __func__); 1780a04a10f8SKris Kennaway } 178119261079SEd Maste r = 0; 178219261079SEd Maste out: 1783e4a9863fSDag-Erling Smørgrav free(ctype); 178419261079SEd Maste return r; 1785a04a10f8SKris Kennaway } 1786bc5531deSDag-Erling Smørgrav 1787bc5531deSDag-Erling Smørgrav static int 17884f52dfbbSDag-Erling Smørgrav client_input_channel_req(int type, u_int32_t seq, struct ssh *ssh) 17891e8db6e2SBrian Feldman { 17901e8db6e2SBrian Feldman Channel *c = NULL; 179119261079SEd Maste char *rtype = NULL; 179219261079SEd Maste u_char reply; 179319261079SEd Maste u_int id, exitval; 179419261079SEd Maste int r, success = 0; 17951e8db6e2SBrian Feldman 179619261079SEd Maste if ((r = sshpkt_get_u32(ssh, &id)) != 0) 179719261079SEd Maste return r; 179819261079SEd Maste if (id <= INT_MAX) 17994f52dfbbSDag-Erling Smørgrav c = channel_lookup(ssh, id); 18004f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 1801ca86bcf2SDag-Erling Smørgrav return 0; 180219261079SEd Maste if ((r = sshpkt_get_cstring(ssh, &rtype, NULL)) != 0 || 180319261079SEd Maste (r = sshpkt_get_u8(ssh, &reply)) != 0) 180419261079SEd Maste goto out; 18051e8db6e2SBrian Feldman 180619261079SEd Maste debug("client_input_channel_req: channel %u rtype %s reply %d", 18071e8db6e2SBrian Feldman id, rtype, reply); 18081e8db6e2SBrian Feldman 180919261079SEd Maste if (c == NULL) { 1810d4af9e69SDag-Erling Smørgrav error("client_input_channel_req: channel %d: " 1811d4af9e69SDag-Erling Smørgrav "unknown channel", id); 1812d4af9e69SDag-Erling Smørgrav } else if (strcmp(rtype, "eow@openssh.com") == 0) { 181319261079SEd Maste if ((r = sshpkt_get_end(ssh)) != 0) 181419261079SEd Maste goto out; 18154f52dfbbSDag-Erling Smørgrav chan_rcvd_eow(ssh, c); 18161e8db6e2SBrian Feldman } else if (strcmp(rtype, "exit-status") == 0) { 181719261079SEd Maste if ((r = sshpkt_get_u32(ssh, &exitval)) != 0) 181819261079SEd Maste goto out; 1819b15c8340SDag-Erling Smørgrav if (c->ctl_chan != -1) { 18204f52dfbbSDag-Erling Smørgrav mux_exit_message(ssh, c, exitval); 1821b15c8340SDag-Erling Smørgrav success = 1; 182219261079SEd Maste } else if ((int)id == session_ident) { 1823b15c8340SDag-Erling Smørgrav /* Record exit value of local session */ 18241e8db6e2SBrian Feldman success = 1; 1825d74d50a8SDag-Erling Smørgrav exit_status = exitval; 1826d74d50a8SDag-Erling Smørgrav } else { 1827b15c8340SDag-Erling Smørgrav /* Probably for a mux channel that has already closed */ 182819261079SEd Maste debug_f("no sink for exit-status on channel %d", 182919261079SEd Maste id); 1830d74d50a8SDag-Erling Smørgrav } 183119261079SEd Maste if ((r = sshpkt_get_end(ssh)) != 0) 183219261079SEd Maste goto out; 18331e8db6e2SBrian Feldman } 1834a0ee8cc6SDag-Erling Smørgrav if (reply && c != NULL && !(c->flags & CHAN_CLOSE_SENT)) { 18354f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id) 183619261079SEd Maste fatal_f("channel %d: no remote_id", c->self); 183719261079SEd Maste if ((r = sshpkt_start(ssh, success ? 183819261079SEd Maste SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE)) != 0 || 183919261079SEd Maste (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 184019261079SEd Maste (r = sshpkt_send(ssh)) != 0) 184119261079SEd Maste sshpkt_fatal(ssh, r, "%s: send failure", __func__); 18421e8db6e2SBrian Feldman } 184319261079SEd Maste r = 0; 184419261079SEd Maste out: 1845e4a9863fSDag-Erling Smørgrav free(rtype); 184619261079SEd Maste return r; 18471e8db6e2SBrian Feldman } 1848bc5531deSDag-Erling Smørgrav 1849bc5531deSDag-Erling Smørgrav struct hostkeys_update_ctx { 1850bc5531deSDag-Erling Smørgrav /* The hostname and (optionally) IP address string for the server */ 1851bc5531deSDag-Erling Smørgrav char *host_str, *ip_str; 1852bc5531deSDag-Erling Smørgrav 1853bc5531deSDag-Erling Smørgrav /* 1854bc5531deSDag-Erling Smørgrav * Keys received from the server and a flag for each indicating 1855bc5531deSDag-Erling Smørgrav * whether they already exist in known_hosts. 185619261079SEd Maste * keys_match is filled in by hostkeys_find() and later (for new 1857f374ba41SEd Maste * keys) by client_global_hostkeys_prove_confirm(). 1858bc5531deSDag-Erling Smørgrav */ 1859bc5531deSDag-Erling Smørgrav struct sshkey **keys; 186019261079SEd Maste u_int *keys_match; /* mask of HKF_MATCH_* from hostfile.h */ 186119261079SEd Maste int *keys_verified; /* flag for new keys verified by server */ 186219261079SEd Maste size_t nkeys, nnew, nincomplete; /* total, new keys, incomplete match */ 1863bc5531deSDag-Erling Smørgrav 1864bc5531deSDag-Erling Smørgrav /* 1865bc5531deSDag-Erling Smørgrav * Keys that are in known_hosts, but were not present in the update 1866bc5531deSDag-Erling Smørgrav * from the server (i.e. scheduled to be deleted). 1867bc5531deSDag-Erling Smørgrav * Filled in by hostkeys_find(). 1868bc5531deSDag-Erling Smørgrav */ 1869bc5531deSDag-Erling Smørgrav struct sshkey **old_keys; 1870bc5531deSDag-Erling Smørgrav size_t nold; 187119261079SEd Maste 187219261079SEd Maste /* Various special cases. */ 187319261079SEd Maste int complex_hostspec; /* wildcard or manual pattern-list host name */ 187419261079SEd Maste int ca_available; /* saw CA key for this host */ 187519261079SEd Maste int old_key_seen; /* saw old key with other name/addr */ 187619261079SEd Maste int other_name_seen; /* saw key with other name/addr */ 1877bc5531deSDag-Erling Smørgrav }; 1878bc5531deSDag-Erling Smørgrav 1879ae1f160dSDag-Erling Smørgrav static void 1880bc5531deSDag-Erling Smørgrav hostkeys_update_ctx_free(struct hostkeys_update_ctx *ctx) 1881bc5531deSDag-Erling Smørgrav { 1882bc5531deSDag-Erling Smørgrav size_t i; 1883bc5531deSDag-Erling Smørgrav 1884bc5531deSDag-Erling Smørgrav if (ctx == NULL) 1885bc5531deSDag-Erling Smørgrav return; 1886bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) 1887bc5531deSDag-Erling Smørgrav sshkey_free(ctx->keys[i]); 1888bc5531deSDag-Erling Smørgrav free(ctx->keys); 188919261079SEd Maste free(ctx->keys_match); 189019261079SEd Maste free(ctx->keys_verified); 1891bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nold; i++) 1892bc5531deSDag-Erling Smørgrav sshkey_free(ctx->old_keys[i]); 1893bc5531deSDag-Erling Smørgrav free(ctx->old_keys); 1894bc5531deSDag-Erling Smørgrav free(ctx->host_str); 1895bc5531deSDag-Erling Smørgrav free(ctx->ip_str); 1896bc5531deSDag-Erling Smørgrav free(ctx); 1897bc5531deSDag-Erling Smørgrav } 1898bc5531deSDag-Erling Smørgrav 189919261079SEd Maste /* 190019261079SEd Maste * Returns non-zero if a known_hosts hostname list is not of a form that 190119261079SEd Maste * can be handled by UpdateHostkeys. These include wildcard hostnames and 190219261079SEd Maste * hostnames lists that do not follow the form host[,ip]. 190319261079SEd Maste */ 190419261079SEd Maste static int 190519261079SEd Maste hostspec_is_complex(const char *hosts) 190619261079SEd Maste { 190719261079SEd Maste char *cp; 190819261079SEd Maste 190919261079SEd Maste /* wildcard */ 191019261079SEd Maste if (strchr(hosts, '*') != NULL || strchr(hosts, '?') != NULL) 191119261079SEd Maste return 1; 191219261079SEd Maste /* single host/ip = ok */ 191319261079SEd Maste if ((cp = strchr(hosts, ',')) == NULL) 191419261079SEd Maste return 0; 191519261079SEd Maste /* more than two entries on the line */ 191619261079SEd Maste if (strchr(cp + 1, ',') != NULL) 191719261079SEd Maste return 1; 191819261079SEd Maste /* XXX maybe parse cp+1 and ensure it is an IP? */ 191919261079SEd Maste return 0; 192019261079SEd Maste } 192119261079SEd Maste 192219261079SEd Maste /* callback to search for ctx->keys in known_hosts */ 1923bc5531deSDag-Erling Smørgrav static int 1924bc5531deSDag-Erling Smørgrav hostkeys_find(struct hostkey_foreach_line *l, void *_ctx) 1925bc5531deSDag-Erling Smørgrav { 1926bc5531deSDag-Erling Smørgrav struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; 1927bc5531deSDag-Erling Smørgrav size_t i; 1928bc5531deSDag-Erling Smørgrav struct sshkey **tmp; 1929bc5531deSDag-Erling Smørgrav 193019261079SEd Maste if (l->key == NULL) 1931bc5531deSDag-Erling Smørgrav return 0; 193219261079SEd Maste if (l->status != HKF_STATUS_MATCHED) { 193319261079SEd Maste /* Record if one of the keys appears on a non-matching line */ 193419261079SEd Maste for (i = 0; i < ctx->nkeys; i++) { 193519261079SEd Maste if (sshkey_equal(l->key, ctx->keys[i])) { 193619261079SEd Maste ctx->other_name_seen = 1; 193719261079SEd Maste debug3_f("found %s key under different " 193819261079SEd Maste "name/addr at %s:%ld", 193919261079SEd Maste sshkey_ssh_name(ctx->keys[i]), 194019261079SEd Maste l->path, l->linenum); 194119261079SEd Maste return 0; 194219261079SEd Maste } 194319261079SEd Maste } 194419261079SEd Maste return 0; 194519261079SEd Maste } 194619261079SEd Maste /* Don't proceed if revocation or CA markers are present */ 194719261079SEd Maste /* XXX relax this */ 194819261079SEd Maste if (l->marker != MRK_NONE) { 194919261079SEd Maste debug3_f("hostkeys file %s:%ld has CA/revocation marker", 195019261079SEd Maste l->path, l->linenum); 195119261079SEd Maste ctx->complex_hostspec = 1; 195219261079SEd Maste return 0; 195319261079SEd Maste } 195419261079SEd Maste 195519261079SEd Maste /* If CheckHostIP is enabled, then check for mismatched hostname/addr */ 195619261079SEd Maste if (ctx->ip_str != NULL && strchr(l->hosts, ',') != NULL) { 195719261079SEd Maste if ((l->match & HKF_MATCH_HOST) == 0) { 195819261079SEd Maste /* Record if address matched a different hostname. */ 195919261079SEd Maste ctx->other_name_seen = 1; 196019261079SEd Maste debug3_f("found address %s against different hostname " 196119261079SEd Maste "at %s:%ld", ctx->ip_str, l->path, l->linenum); 196219261079SEd Maste return 0; 196319261079SEd Maste } else if ((l->match & HKF_MATCH_IP) == 0) { 196419261079SEd Maste /* Record if hostname matched a different address. */ 196519261079SEd Maste ctx->other_name_seen = 1; 196619261079SEd Maste debug3_f("found hostname %s against different address " 196719261079SEd Maste "at %s:%ld", ctx->host_str, l->path, l->linenum); 196819261079SEd Maste } 196919261079SEd Maste } 197019261079SEd Maste 197119261079SEd Maste /* 197219261079SEd Maste * UpdateHostkeys is skipped for wildcard host names and hostnames 197319261079SEd Maste * that contain more than two entries (ssh never writes these). 197419261079SEd Maste */ 197519261079SEd Maste if (hostspec_is_complex(l->hosts)) { 197619261079SEd Maste debug3_f("hostkeys file %s:%ld complex host specification", 197719261079SEd Maste l->path, l->linenum); 197819261079SEd Maste ctx->complex_hostspec = 1; 197919261079SEd Maste return 0; 198019261079SEd Maste } 1981bc5531deSDag-Erling Smørgrav 1982bc5531deSDag-Erling Smørgrav /* Mark off keys we've already seen for this host */ 1983bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) { 198419261079SEd Maste if (!sshkey_equal(l->key, ctx->keys[i])) 198519261079SEd Maste continue; 198619261079SEd Maste debug3_f("found %s key at %s:%ld", 1987bc5531deSDag-Erling Smørgrav sshkey_ssh_name(ctx->keys[i]), l->path, l->linenum); 198819261079SEd Maste ctx->keys_match[i] |= l->match; 1989bc5531deSDag-Erling Smørgrav return 0; 1990bc5531deSDag-Erling Smørgrav } 1991bc5531deSDag-Erling Smørgrav /* This line contained a key that not offered by the server */ 199219261079SEd Maste debug3_f("deprecated %s key at %s:%ld", sshkey_ssh_name(l->key), 199319261079SEd Maste l->path, l->linenum); 19944f52dfbbSDag-Erling Smørgrav if ((tmp = recallocarray(ctx->old_keys, ctx->nold, ctx->nold + 1, 1995bc5531deSDag-Erling Smørgrav sizeof(*ctx->old_keys))) == NULL) 199619261079SEd Maste fatal_f("recallocarray failed nold = %zu", ctx->nold); 1997bc5531deSDag-Erling Smørgrav ctx->old_keys = tmp; 1998bc5531deSDag-Erling Smørgrav ctx->old_keys[ctx->nold++] = l->key; 1999bc5531deSDag-Erling Smørgrav l->key = NULL; 2000bc5531deSDag-Erling Smørgrav 2001bc5531deSDag-Erling Smørgrav return 0; 2002bc5531deSDag-Erling Smørgrav } 2003bc5531deSDag-Erling Smørgrav 200419261079SEd Maste /* callback to search for ctx->old_keys in known_hosts under other names */ 200519261079SEd Maste static int 200619261079SEd Maste hostkeys_check_old(struct hostkey_foreach_line *l, void *_ctx) 200719261079SEd Maste { 200819261079SEd Maste struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; 200919261079SEd Maste size_t i; 201019261079SEd Maste int hashed; 201119261079SEd Maste 201219261079SEd Maste /* only care about lines that *don't* match the active host spec */ 201319261079SEd Maste if (l->status == HKF_STATUS_MATCHED || l->key == NULL) 201419261079SEd Maste return 0; 201519261079SEd Maste 201619261079SEd Maste hashed = l->match & (HKF_MATCH_HOST_HASHED|HKF_MATCH_IP_HASHED); 201719261079SEd Maste for (i = 0; i < ctx->nold; i++) { 201819261079SEd Maste if (!sshkey_equal(l->key, ctx->old_keys[i])) 201919261079SEd Maste continue; 202019261079SEd Maste debug3_f("found deprecated %s key at %s:%ld as %s", 202119261079SEd Maste sshkey_ssh_name(ctx->old_keys[i]), l->path, l->linenum, 202219261079SEd Maste hashed ? "[HASHED]" : l->hosts); 202319261079SEd Maste ctx->old_key_seen = 1; 202419261079SEd Maste break; 202519261079SEd Maste } 202619261079SEd Maste return 0; 202719261079SEd Maste } 202819261079SEd Maste 202919261079SEd Maste /* 203019261079SEd Maste * Check known_hosts files for deprecated keys under other names. Returns 0 203119261079SEd Maste * on success or -1 on failure. Updates ctx->old_key_seen if deprecated keys 203219261079SEd Maste * exist under names other than the active hostname/IP. 203319261079SEd Maste */ 203419261079SEd Maste static int 203519261079SEd Maste check_old_keys_othernames(struct hostkeys_update_ctx *ctx) 203619261079SEd Maste { 203719261079SEd Maste size_t i; 203819261079SEd Maste int r; 203919261079SEd Maste 204019261079SEd Maste debug2_f("checking for %zu deprecated keys", ctx->nold); 204119261079SEd Maste for (i = 0; i < options.num_user_hostfiles; i++) { 204219261079SEd Maste debug3_f("searching %s for %s / %s", 204319261079SEd Maste options.user_hostfiles[i], ctx->host_str, 204419261079SEd Maste ctx->ip_str ? ctx->ip_str : "(none)"); 204519261079SEd Maste if ((r = hostkeys_foreach(options.user_hostfiles[i], 204619261079SEd Maste hostkeys_check_old, ctx, ctx->host_str, ctx->ip_str, 204719261079SEd Maste HKF_WANT_PARSE_KEY, 0)) != 0) { 204819261079SEd Maste if (r == SSH_ERR_SYSTEM_ERROR && errno == ENOENT) { 204919261079SEd Maste debug_f("hostkeys file %s does not exist", 205019261079SEd Maste options.user_hostfiles[i]); 205119261079SEd Maste continue; 205219261079SEd Maste } 205319261079SEd Maste error_fr(r, "hostkeys_foreach failed for %s", 205419261079SEd Maste options.user_hostfiles[i]); 205519261079SEd Maste return -1; 205619261079SEd Maste } 205719261079SEd Maste } 205819261079SEd Maste return 0; 205919261079SEd Maste } 206019261079SEd Maste 206119261079SEd Maste static void 206219261079SEd Maste hostkey_change_preamble(LogLevel loglevel) 206319261079SEd Maste { 206419261079SEd Maste do_log2(loglevel, "The server has updated its host keys."); 206519261079SEd Maste do_log2(loglevel, "These changes were verified by the server's " 206619261079SEd Maste "existing trusted key."); 206719261079SEd Maste } 206819261079SEd Maste 2069bc5531deSDag-Erling Smørgrav static void 2070bc5531deSDag-Erling Smørgrav update_known_hosts(struct hostkeys_update_ctx *ctx) 2071bc5531deSDag-Erling Smørgrav { 207219261079SEd Maste int r, was_raw = 0, first = 1; 207319261079SEd Maste int asking = options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK; 207419261079SEd Maste LogLevel loglevel = asking ? SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_VERBOSE; 2075bc5531deSDag-Erling Smørgrav char *fp, *response; 2076bc5531deSDag-Erling Smørgrav size_t i; 207719261079SEd Maste struct stat sb; 2078bc5531deSDag-Erling Smørgrav 2079bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) { 208019261079SEd Maste if (!ctx->keys_verified[i]) 2081bc5531deSDag-Erling Smørgrav continue; 2082bc5531deSDag-Erling Smørgrav if ((fp = sshkey_fingerprint(ctx->keys[i], 2083bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) 208419261079SEd Maste fatal_f("sshkey_fingerprint failed"); 208519261079SEd Maste if (first && asking) 208619261079SEd Maste hostkey_change_preamble(loglevel); 2087bc5531deSDag-Erling Smørgrav do_log2(loglevel, "Learned new hostkey: %s %s", 2088bc5531deSDag-Erling Smørgrav sshkey_type(ctx->keys[i]), fp); 208919261079SEd Maste first = 0; 2090bc5531deSDag-Erling Smørgrav free(fp); 2091bc5531deSDag-Erling Smørgrav } 2092bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nold; i++) { 2093bc5531deSDag-Erling Smørgrav if ((fp = sshkey_fingerprint(ctx->old_keys[i], 2094bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) 209519261079SEd Maste fatal_f("sshkey_fingerprint failed"); 209619261079SEd Maste if (first && asking) 209719261079SEd Maste hostkey_change_preamble(loglevel); 2098bc5531deSDag-Erling Smørgrav do_log2(loglevel, "Deprecating obsolete hostkey: %s %s", 2099bc5531deSDag-Erling Smørgrav sshkey_type(ctx->old_keys[i]), fp); 210019261079SEd Maste first = 0; 2101bc5531deSDag-Erling Smørgrav free(fp); 2102bc5531deSDag-Erling Smørgrav } 2103bc5531deSDag-Erling Smørgrav if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK) { 2104bc5531deSDag-Erling Smørgrav if (get_saved_tio() != NULL) { 2105bc5531deSDag-Erling Smørgrav leave_raw_mode(1); 2106bc5531deSDag-Erling Smørgrav was_raw = 1; 2107bc5531deSDag-Erling Smørgrav } 2108bc5531deSDag-Erling Smørgrav response = NULL; 2109bc5531deSDag-Erling Smørgrav for (i = 0; !quit_pending && i < 3; i++) { 2110bc5531deSDag-Erling Smørgrav free(response); 2111bc5531deSDag-Erling Smørgrav response = read_passphrase("Accept updated hostkeys? " 2112bc5531deSDag-Erling Smørgrav "(yes/no): ", RP_ECHO); 2113*4d3fc8b0SEd Maste if (response != NULL && strcasecmp(response, "yes") == 0) 2114bc5531deSDag-Erling Smørgrav break; 2115bc5531deSDag-Erling Smørgrav else if (quit_pending || response == NULL || 2116bc5531deSDag-Erling Smørgrav strcasecmp(response, "no") == 0) { 2117bc5531deSDag-Erling Smørgrav options.update_hostkeys = 0; 2118bc5531deSDag-Erling Smørgrav break; 2119bc5531deSDag-Erling Smørgrav } else { 2120bc5531deSDag-Erling Smørgrav do_log2(loglevel, "Please enter " 2121bc5531deSDag-Erling Smørgrav "\"yes\" or \"no\""); 2122bc5531deSDag-Erling Smørgrav } 2123bc5531deSDag-Erling Smørgrav } 2124bc5531deSDag-Erling Smørgrav if (quit_pending || i >= 3 || response == NULL) 2125bc5531deSDag-Erling Smørgrav options.update_hostkeys = 0; 2126bc5531deSDag-Erling Smørgrav free(response); 2127bc5531deSDag-Erling Smørgrav if (was_raw) 2128bc5531deSDag-Erling Smørgrav enter_raw_mode(1); 2129bc5531deSDag-Erling Smørgrav } 213019261079SEd Maste if (options.update_hostkeys == 0) 213119261079SEd Maste return; 2132bc5531deSDag-Erling Smørgrav /* 2133bc5531deSDag-Erling Smørgrav * Now that all the keys are verified, we can go ahead and replace 2134bc5531deSDag-Erling Smørgrav * them in known_hosts (assuming SSH_UPDATE_HOSTKEYS_ASK didn't 2135bc5531deSDag-Erling Smørgrav * cancel the operation). 2136bc5531deSDag-Erling Smørgrav */ 213719261079SEd Maste for (i = 0; i < options.num_user_hostfiles; i++) { 213819261079SEd Maste /* 213919261079SEd Maste * NB. keys are only added to hostfiles[0], for the rest we 214019261079SEd Maste * just delete the hostname entries. 214119261079SEd Maste */ 214219261079SEd Maste if (stat(options.user_hostfiles[i], &sb) != 0) { 214319261079SEd Maste if (errno == ENOENT) { 214419261079SEd Maste debug_f("known hosts file %s does not " 214519261079SEd Maste "exist", options.user_hostfiles[i]); 214619261079SEd Maste } else { 214719261079SEd Maste error_f("known hosts file %s " 214819261079SEd Maste "inaccessible: %s", 214919261079SEd Maste options.user_hostfiles[i], strerror(errno)); 215019261079SEd Maste } 215119261079SEd Maste continue; 215219261079SEd Maste } 215319261079SEd Maste if ((r = hostfile_replace_entries(options.user_hostfiles[i], 215419261079SEd Maste ctx->host_str, ctx->ip_str, 215519261079SEd Maste i == 0 ? ctx->keys : NULL, i == 0 ? ctx->nkeys : 0, 2156bc5531deSDag-Erling Smørgrav options.hash_known_hosts, 0, 215719261079SEd Maste options.fingerprint_hash)) != 0) { 215819261079SEd Maste error_fr(r, "hostfile_replace_entries failed for %s", 215919261079SEd Maste options.user_hostfiles[i]); 216019261079SEd Maste } 216119261079SEd Maste } 2162bc5531deSDag-Erling Smørgrav } 2163bc5531deSDag-Erling Smørgrav 2164bc5531deSDag-Erling Smørgrav static void 2165f374ba41SEd Maste client_global_hostkeys_prove_confirm(struct ssh *ssh, int type, 21664f52dfbbSDag-Erling Smørgrav u_int32_t seq, void *_ctx) 2167bc5531deSDag-Erling Smørgrav { 2168bc5531deSDag-Erling Smørgrav struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; 2169bc5531deSDag-Erling Smørgrav size_t i, ndone; 2170bc5531deSDag-Erling Smørgrav struct sshbuf *signdata; 21711323ec57SEd Maste int r, plaintype; 2172bc5531deSDag-Erling Smørgrav const u_char *sig; 21731323ec57SEd Maste const char *rsa_kexalg = NULL; 21741323ec57SEd Maste char *alg = NULL; 2175bc5531deSDag-Erling Smørgrav size_t siglen; 2176bc5531deSDag-Erling Smørgrav 2177bc5531deSDag-Erling Smørgrav if (ctx->nnew == 0) 217819261079SEd Maste fatal_f("ctx->nnew == 0"); /* sanity */ 2179bc5531deSDag-Erling Smørgrav if (type != SSH2_MSG_REQUEST_SUCCESS) { 2180bc5531deSDag-Erling Smørgrav error("Server failed to confirm ownership of " 2181bc5531deSDag-Erling Smørgrav "private host keys"); 2182bc5531deSDag-Erling Smørgrav hostkeys_update_ctx_free(ctx); 2183bc5531deSDag-Erling Smørgrav return; 2184bc5531deSDag-Erling Smørgrav } 21851323ec57SEd Maste if (sshkey_type_plain(sshkey_type_from_name( 21861323ec57SEd Maste ssh->kex->hostkey_alg)) == KEY_RSA) 21871323ec57SEd Maste rsa_kexalg = ssh->kex->hostkey_alg; 2188bc5531deSDag-Erling Smørgrav if ((signdata = sshbuf_new()) == NULL) 218919261079SEd Maste fatal_f("sshbuf_new failed"); 2190bc5531deSDag-Erling Smørgrav /* 2191bc5531deSDag-Erling Smørgrav * Expect a signature for each of the ctx->nnew private keys we 2192bc5531deSDag-Erling Smørgrav * haven't seen before. They will be in the same order as the 219319261079SEd Maste * ctx->keys where the corresponding ctx->keys_match[i] == 0. 2194bc5531deSDag-Erling Smørgrav */ 2195bc5531deSDag-Erling Smørgrav for (ndone = i = 0; i < ctx->nkeys; i++) { 219619261079SEd Maste if (ctx->keys_match[i]) 2197bc5531deSDag-Erling Smørgrav continue; 21981323ec57SEd Maste plaintype = sshkey_type_plain(ctx->keys[i]->type); 2199bc5531deSDag-Erling Smørgrav /* Prepare data to be signed: session ID, unique string, key */ 2200bc5531deSDag-Erling Smørgrav sshbuf_reset(signdata); 2201bc5531deSDag-Erling Smørgrav if ( (r = sshbuf_put_cstring(signdata, 2202bc5531deSDag-Erling Smørgrav "hostkeys-prove-00@openssh.com")) != 0 || 220319261079SEd Maste (r = sshbuf_put_stringb(signdata, 220419261079SEd Maste ssh->kex->session_id)) != 0 || 2205bc5531deSDag-Erling Smørgrav (r = sshkey_puts(ctx->keys[i], signdata)) != 0) 220619261079SEd Maste fatal_fr(r, "compose signdata"); 2207bc5531deSDag-Erling Smørgrav /* Extract and verify signature */ 2208bc5531deSDag-Erling Smørgrav if ((r = sshpkt_get_string_direct(ssh, &sig, &siglen)) != 0) { 220919261079SEd Maste error_fr(r, "parse sig"); 2210bc5531deSDag-Erling Smørgrav goto out; 2211bc5531deSDag-Erling Smørgrav } 22121323ec57SEd Maste if ((r = sshkey_get_sigtype(sig, siglen, &alg)) != 0) { 22131323ec57SEd Maste error_fr(r, "server gave unintelligible signature " 22141323ec57SEd Maste "for %s key %zu", sshkey_type(ctx->keys[i]), i); 22151323ec57SEd Maste goto out; 22161323ec57SEd Maste } 221747dd1d1bSDag-Erling Smørgrav /* 22181323ec57SEd Maste * Special case for RSA keys: if a RSA hostkey was negotiated, 22191323ec57SEd Maste * then use its signature type for verification of RSA hostkey 22201323ec57SEd Maste * proofs. Otherwise, accept only RSA-SHA256/512 signatures. 222147dd1d1bSDag-Erling Smørgrav */ 22221323ec57SEd Maste if (plaintype == KEY_RSA && rsa_kexalg == NULL && 22231323ec57SEd Maste match_pattern_list(alg, HOSTKEY_PROOF_RSA_ALGS, 0) != 1) { 22241323ec57SEd Maste debug_f("server used untrusted RSA signature algorithm " 22251323ec57SEd Maste "%s for key %zu, disregarding", alg, i); 22261323ec57SEd Maste free(alg); 22271323ec57SEd Maste /* zap the key from the list */ 22281323ec57SEd Maste sshkey_free(ctx->keys[i]); 22291323ec57SEd Maste ctx->keys[i] = NULL; 22301323ec57SEd Maste ndone++; 22311323ec57SEd Maste continue; 22321323ec57SEd Maste } 22331323ec57SEd Maste debug3_f("verify %s key %zu using sigalg %s", 22341323ec57SEd Maste sshkey_type(ctx->keys[i]), i, alg); 22351323ec57SEd Maste free(alg); 2236bc5531deSDag-Erling Smørgrav if ((r = sshkey_verify(ctx->keys[i], sig, siglen, 223747dd1d1bSDag-Erling Smørgrav sshbuf_ptr(signdata), sshbuf_len(signdata), 22381323ec57SEd Maste plaintype == KEY_RSA ? rsa_kexalg : NULL, 0, NULL)) != 0) { 223919261079SEd Maste error_fr(r, "server gave bad signature for %s key %zu", 224019261079SEd Maste sshkey_type(ctx->keys[i]), i); 2241bc5531deSDag-Erling Smørgrav goto out; 2242bc5531deSDag-Erling Smørgrav } 2243bc5531deSDag-Erling Smørgrav /* Key is good. Mark it as 'seen' */ 224419261079SEd Maste ctx->keys_verified[i] = 1; 2245bc5531deSDag-Erling Smørgrav ndone++; 2246bc5531deSDag-Erling Smørgrav } 224719261079SEd Maste /* Shouldn't happen */ 2248bc5531deSDag-Erling Smørgrav if (ndone != ctx->nnew) 224919261079SEd Maste fatal_f("ndone != ctx->nnew (%zu / %zu)", ndone, ctx->nnew); 225019261079SEd Maste if ((r = sshpkt_get_end(ssh)) != 0) { 225119261079SEd Maste error_f("protocol error"); 225219261079SEd Maste goto out; 225319261079SEd Maste } 2254bc5531deSDag-Erling Smørgrav 2255bc5531deSDag-Erling Smørgrav /* Make the edits to known_hosts */ 2256bc5531deSDag-Erling Smørgrav update_known_hosts(ctx); 2257bc5531deSDag-Erling Smørgrav out: 2258bc5531deSDag-Erling Smørgrav hostkeys_update_ctx_free(ctx); 2259f374ba41SEd Maste hostkeys_update_complete = 1; 2260f374ba41SEd Maste client_repledge(); 2261bc5531deSDag-Erling Smørgrav } 2262bc5531deSDag-Erling Smørgrav 2263bc5531deSDag-Erling Smørgrav /* 2264d93a896eSDag-Erling Smørgrav * Returns non-zero if the key is accepted by HostkeyAlgorithms. 2265d93a896eSDag-Erling Smørgrav * Made slightly less trivial by the multiple RSA signature algorithm names. 2266d93a896eSDag-Erling Smørgrav */ 2267d93a896eSDag-Erling Smørgrav static int 2268d93a896eSDag-Erling Smørgrav key_accepted_by_hostkeyalgs(const struct sshkey *key) 2269d93a896eSDag-Erling Smørgrav { 2270d93a896eSDag-Erling Smørgrav const char *ktype = sshkey_ssh_name(key); 227119261079SEd Maste const char *hostkeyalgs = options.hostkeyalgorithms; 2272d93a896eSDag-Erling Smørgrav 2273d93a896eSDag-Erling Smørgrav if (key == NULL || key->type == KEY_UNSPEC) 2274d93a896eSDag-Erling Smørgrav return 0; 2275d93a896eSDag-Erling Smørgrav if (key->type == KEY_RSA && 2276d93a896eSDag-Erling Smørgrav (match_pattern_list("rsa-sha2-256", hostkeyalgs, 0) == 1 || 2277d93a896eSDag-Erling Smørgrav match_pattern_list("rsa-sha2-512", hostkeyalgs, 0) == 1)) 2278d93a896eSDag-Erling Smørgrav return 1; 2279d93a896eSDag-Erling Smørgrav return match_pattern_list(ktype, hostkeyalgs, 0) == 1; 2280d93a896eSDag-Erling Smørgrav } 2281d93a896eSDag-Erling Smørgrav 2282d93a896eSDag-Erling Smørgrav /* 2283bc5531deSDag-Erling Smørgrav * Handle hostkeys-00@openssh.com global request to inform the client of all 2284bc5531deSDag-Erling Smørgrav * the server's hostkeys. The keys are checked against the user's 2285bc5531deSDag-Erling Smørgrav * HostkeyAlgorithms preference before they are accepted. 2286bc5531deSDag-Erling Smørgrav */ 2287bc5531deSDag-Erling Smørgrav static int 228819261079SEd Maste client_input_hostkeys(struct ssh *ssh) 2289bc5531deSDag-Erling Smørgrav { 2290bc5531deSDag-Erling Smørgrav const u_char *blob = NULL; 2291bc5531deSDag-Erling Smørgrav size_t i, len = 0; 2292bc5531deSDag-Erling Smørgrav struct sshbuf *buf = NULL; 2293bc5531deSDag-Erling Smørgrav struct sshkey *key = NULL, **tmp; 2294f374ba41SEd Maste int r, prove_sent = 0; 2295bc5531deSDag-Erling Smørgrav char *fp; 2296bc5531deSDag-Erling Smørgrav static int hostkeys_seen = 0; /* XXX use struct ssh */ 2297bc5531deSDag-Erling Smørgrav extern struct sockaddr_storage hostaddr; /* XXX from ssh.c */ 2298bc5531deSDag-Erling Smørgrav struct hostkeys_update_ctx *ctx = NULL; 229919261079SEd Maste u_int want; 2300bc5531deSDag-Erling Smørgrav 2301bc5531deSDag-Erling Smørgrav if (hostkeys_seen) 230219261079SEd Maste fatal_f("server already sent hostkeys"); 2303f374ba41SEd Maste if (!can_update_hostkeys()) 2304bc5531deSDag-Erling Smørgrav return 1; 2305f374ba41SEd Maste hostkeys_seen = 1; 2306bc5531deSDag-Erling Smørgrav 2307bc5531deSDag-Erling Smørgrav ctx = xcalloc(1, sizeof(*ctx)); 2308bc5531deSDag-Erling Smørgrav while (ssh_packet_remaining(ssh) > 0) { 2309bc5531deSDag-Erling Smørgrav sshkey_free(key); 2310bc5531deSDag-Erling Smørgrav key = NULL; 2311bc5531deSDag-Erling Smørgrav if ((r = sshpkt_get_string_direct(ssh, &blob, &len)) != 0) { 231219261079SEd Maste error_fr(r, "parse key"); 2313bc5531deSDag-Erling Smørgrav goto out; 2314bc5531deSDag-Erling Smørgrav } 2315bc5531deSDag-Erling Smørgrav if ((r = sshkey_from_blob(blob, len, &key)) != 0) { 231619261079SEd Maste do_log2_fr(r, r == SSH_ERR_KEY_TYPE_UNKNOWN ? 231719261079SEd Maste SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_ERROR, 231819261079SEd Maste "convert key"); 231919261079SEd Maste continue; 2320bc5531deSDag-Erling Smørgrav } 2321bc5531deSDag-Erling Smørgrav fp = sshkey_fingerprint(key, options.fingerprint_hash, 2322bc5531deSDag-Erling Smørgrav SSH_FP_DEFAULT); 232319261079SEd Maste debug3_f("received %s key %s", sshkey_type(key), fp); 2324bc5531deSDag-Erling Smørgrav free(fp); 2325eccfee6eSDag-Erling Smørgrav 2326d93a896eSDag-Erling Smørgrav if (!key_accepted_by_hostkeyalgs(key)) { 232719261079SEd Maste debug3_f("%s key not permitted by " 232819261079SEd Maste "HostkeyAlgorithms", sshkey_ssh_name(key)); 2329bc5531deSDag-Erling Smørgrav continue; 2330bc5531deSDag-Erling Smørgrav } 2331bc5531deSDag-Erling Smørgrav /* Skip certs */ 2332bc5531deSDag-Erling Smørgrav if (sshkey_is_cert(key)) { 233319261079SEd Maste debug3_f("%s key is a certificate; skipping", 233419261079SEd Maste sshkey_ssh_name(key)); 2335bc5531deSDag-Erling Smørgrav continue; 2336bc5531deSDag-Erling Smørgrav } 2337bc5531deSDag-Erling Smørgrav /* Ensure keys are unique */ 2338bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) { 2339bc5531deSDag-Erling Smørgrav if (sshkey_equal(key, ctx->keys[i])) { 234019261079SEd Maste error_f("received duplicated %s host key", 234119261079SEd Maste sshkey_ssh_name(key)); 2342bc5531deSDag-Erling Smørgrav goto out; 2343bc5531deSDag-Erling Smørgrav } 2344bc5531deSDag-Erling Smørgrav } 2345bc5531deSDag-Erling Smørgrav /* Key is good, record it */ 23464f52dfbbSDag-Erling Smørgrav if ((tmp = recallocarray(ctx->keys, ctx->nkeys, ctx->nkeys + 1, 2347bc5531deSDag-Erling Smørgrav sizeof(*ctx->keys))) == NULL) 234819261079SEd Maste fatal_f("recallocarray failed nkeys = %zu", 234919261079SEd Maste ctx->nkeys); 2350bc5531deSDag-Erling Smørgrav ctx->keys = tmp; 2351bc5531deSDag-Erling Smørgrav ctx->keys[ctx->nkeys++] = key; 2352bc5531deSDag-Erling Smørgrav key = NULL; 2353bc5531deSDag-Erling Smørgrav } 2354bc5531deSDag-Erling Smørgrav 2355bc5531deSDag-Erling Smørgrav if (ctx->nkeys == 0) { 235619261079SEd Maste debug_f("server sent no hostkeys"); 2357bc5531deSDag-Erling Smørgrav goto out; 2358bc5531deSDag-Erling Smørgrav } 2359bc5531deSDag-Erling Smørgrav 236019261079SEd Maste if ((ctx->keys_match = calloc(ctx->nkeys, 236119261079SEd Maste sizeof(*ctx->keys_match))) == NULL || 236219261079SEd Maste (ctx->keys_verified = calloc(ctx->nkeys, 236319261079SEd Maste sizeof(*ctx->keys_verified))) == NULL) 236419261079SEd Maste fatal_f("calloc failed"); 2365bc5531deSDag-Erling Smørgrav 2366bc5531deSDag-Erling Smørgrav get_hostfile_hostname_ipaddr(host, 2367bc5531deSDag-Erling Smørgrav options.check_host_ip ? (struct sockaddr *)&hostaddr : NULL, 2368bc5531deSDag-Erling Smørgrav options.port, &ctx->host_str, 2369bc5531deSDag-Erling Smørgrav options.check_host_ip ? &ctx->ip_str : NULL); 2370bc5531deSDag-Erling Smørgrav 2371bc5531deSDag-Erling Smørgrav /* Find which keys we already know about. */ 237219261079SEd Maste for (i = 0; i < options.num_user_hostfiles; i++) { 237319261079SEd Maste debug_f("searching %s for %s / %s", 237419261079SEd Maste options.user_hostfiles[i], ctx->host_str, 237519261079SEd Maste ctx->ip_str ? ctx->ip_str : "(none)"); 237619261079SEd Maste if ((r = hostkeys_foreach(options.user_hostfiles[i], 237719261079SEd Maste hostkeys_find, ctx, ctx->host_str, ctx->ip_str, 237819261079SEd Maste HKF_WANT_PARSE_KEY, 0)) != 0) { 237919261079SEd Maste if (r == SSH_ERR_SYSTEM_ERROR && errno == ENOENT) { 238019261079SEd Maste debug_f("hostkeys file %s does not exist", 238119261079SEd Maste options.user_hostfiles[i]); 238219261079SEd Maste continue; 238319261079SEd Maste } 238419261079SEd Maste error_fr(r, "hostkeys_foreach failed for %s", 238519261079SEd Maste options.user_hostfiles[i]); 2386bc5531deSDag-Erling Smørgrav goto out; 2387bc5531deSDag-Erling Smørgrav } 238819261079SEd Maste } 2389bc5531deSDag-Erling Smørgrav 2390bc5531deSDag-Erling Smørgrav /* Figure out if we have any new keys to add */ 239119261079SEd Maste ctx->nnew = ctx->nincomplete = 0; 239219261079SEd Maste want = HKF_MATCH_HOST | ( options.check_host_ip ? HKF_MATCH_IP : 0); 2393bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) { 239419261079SEd Maste if (ctx->keys_match[i] == 0) 2395bc5531deSDag-Erling Smørgrav ctx->nnew++; 239619261079SEd Maste if ((ctx->keys_match[i] & want) != want) 239719261079SEd Maste ctx->nincomplete++; 2398bc5531deSDag-Erling Smørgrav } 2399bc5531deSDag-Erling Smørgrav 240019261079SEd Maste debug3_f("%zu server keys: %zu new, %zu retained, " 240119261079SEd Maste "%zu incomplete match. %zu to remove", ctx->nkeys, ctx->nnew, 240219261079SEd Maste ctx->nkeys - ctx->nnew - ctx->nincomplete, 240319261079SEd Maste ctx->nincomplete, ctx->nold); 2404bc5531deSDag-Erling Smørgrav 240519261079SEd Maste if (ctx->nnew == 0 && ctx->nold == 0) { 240619261079SEd Maste debug_f("no new or deprecated keys from server"); 240719261079SEd Maste goto out; 240819261079SEd Maste } 240919261079SEd Maste 241019261079SEd Maste /* Various reasons why we cannot proceed with the update */ 241119261079SEd Maste if (ctx->complex_hostspec) { 241219261079SEd Maste debug_f("CA/revocation marker, manual host list or wildcard " 241319261079SEd Maste "host pattern found, skipping UserKnownHostsFile update"); 241419261079SEd Maste goto out; 241519261079SEd Maste } 241619261079SEd Maste if (ctx->other_name_seen) { 241719261079SEd Maste debug_f("host key found matching a different name/address, " 241819261079SEd Maste "skipping UserKnownHostsFile update"); 241919261079SEd Maste goto out; 242019261079SEd Maste } 2421bc5531deSDag-Erling Smørgrav /* 242219261079SEd Maste * If removing keys, check whether they appear under different 242319261079SEd Maste * names/addresses and refuse to proceed if they do. This avoids 242419261079SEd Maste * cases such as hosts with multiple names becoming inconsistent 242519261079SEd Maste * with regards to CheckHostIP entries. 242619261079SEd Maste * XXX UpdateHostkeys=force to override this (and other) checks? 242719261079SEd Maste */ 242819261079SEd Maste if (ctx->nold != 0) { 242919261079SEd Maste if (check_old_keys_othernames(ctx) != 0) 243019261079SEd Maste goto out; /* error already logged */ 243119261079SEd Maste if (ctx->old_key_seen) { 243219261079SEd Maste debug_f("key(s) for %s%s%s exist under other names; " 243319261079SEd Maste "skipping UserKnownHostsFile update", 243419261079SEd Maste ctx->host_str, ctx->ip_str == NULL ? "" : ",", 243519261079SEd Maste ctx->ip_str == NULL ? "" : ctx->ip_str); 243619261079SEd Maste goto out; 243719261079SEd Maste } 243819261079SEd Maste } 243919261079SEd Maste 244019261079SEd Maste if (ctx->nnew == 0) { 244119261079SEd Maste /* 244219261079SEd Maste * We have some keys to remove or fix matching for. 244319261079SEd Maste * We can proceed to do this without requiring a fresh proof 244419261079SEd Maste * from the server. 244519261079SEd Maste */ 244619261079SEd Maste update_known_hosts(ctx); 244719261079SEd Maste goto out; 244819261079SEd Maste } 244919261079SEd Maste /* 245019261079SEd Maste * We have received previously-unseen keys from the server. 2451bc5531deSDag-Erling Smørgrav * Ask the server to confirm ownership of the private halves. 2452bc5531deSDag-Erling Smørgrav */ 245319261079SEd Maste debug3_f("asking server to prove ownership for %zu keys", ctx->nnew); 2454bc5531deSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || 2455bc5531deSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, 2456bc5531deSDag-Erling Smørgrav "hostkeys-prove-00@openssh.com")) != 0 || 2457bc5531deSDag-Erling Smørgrav (r = sshpkt_put_u8(ssh, 1)) != 0) /* bool: want reply */ 245819261079SEd Maste fatal_fr(r, "prepare hostkeys-prove"); 2459bc5531deSDag-Erling Smørgrav if ((buf = sshbuf_new()) == NULL) 246019261079SEd Maste fatal_f("sshbuf_new"); 2461bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) { 246219261079SEd Maste if (ctx->keys_match[i]) 2463bc5531deSDag-Erling Smørgrav continue; 2464bc5531deSDag-Erling Smørgrav sshbuf_reset(buf); 246519261079SEd Maste if ((r = sshkey_putb(ctx->keys[i], buf)) != 0 || 246619261079SEd Maste (r = sshpkt_put_stringb(ssh, buf)) != 0) 246719261079SEd Maste fatal_fr(r, "assemble hostkeys-prove"); 2468bc5531deSDag-Erling Smørgrav } 2469bc5531deSDag-Erling Smørgrav if ((r = sshpkt_send(ssh)) != 0) 247019261079SEd Maste fatal_fr(r, "send hostkeys-prove"); 2471bc5531deSDag-Erling Smørgrav client_register_global_confirm( 2472f374ba41SEd Maste client_global_hostkeys_prove_confirm, ctx); 2473bc5531deSDag-Erling Smørgrav ctx = NULL; /* will be freed in callback */ 2474f374ba41SEd Maste prove_sent = 1; 2475bc5531deSDag-Erling Smørgrav 2476bc5531deSDag-Erling Smørgrav /* Success */ 2477bc5531deSDag-Erling Smørgrav out: 2478bc5531deSDag-Erling Smørgrav hostkeys_update_ctx_free(ctx); 2479bc5531deSDag-Erling Smørgrav sshkey_free(key); 2480bc5531deSDag-Erling Smørgrav sshbuf_free(buf); 2481f374ba41SEd Maste if (!prove_sent) { 2482f374ba41SEd Maste /* UpdateHostkeys handling completed */ 2483f374ba41SEd Maste hostkeys_update_complete = 1; 2484f374ba41SEd Maste client_repledge(); 2485f374ba41SEd Maste } 2486bc5531deSDag-Erling Smørgrav /* 2487bc5531deSDag-Erling Smørgrav * NB. Return success for all cases. The server doesn't need to know 2488bc5531deSDag-Erling Smørgrav * what the client does with its hosts file. 2489bc5531deSDag-Erling Smørgrav */ 2490bc5531deSDag-Erling Smørgrav return 1; 2491bc5531deSDag-Erling Smørgrav } 2492bc5531deSDag-Erling Smørgrav 2493bc5531deSDag-Erling Smørgrav static int 24944f52dfbbSDag-Erling Smørgrav client_input_global_request(int type, u_int32_t seq, struct ssh *ssh) 2495ae1f160dSDag-Erling Smørgrav { 2496ae1f160dSDag-Erling Smørgrav char *rtype; 249719261079SEd Maste u_char want_reply; 249819261079SEd Maste int r, success = 0; 2499a04a10f8SKris Kennaway 250019261079SEd Maste if ((r = sshpkt_get_cstring(ssh, &rtype, NULL)) != 0 || 250119261079SEd Maste (r = sshpkt_get_u8(ssh, &want_reply)) != 0) 250219261079SEd Maste goto out; 2503efcad6b7SDag-Erling Smørgrav debug("client_input_global_request: rtype %s want_reply %d", 2504efcad6b7SDag-Erling Smørgrav rtype, want_reply); 2505bc5531deSDag-Erling Smørgrav if (strcmp(rtype, "hostkeys-00@openssh.com") == 0) 250619261079SEd Maste success = client_input_hostkeys(ssh); 2507ae1f160dSDag-Erling Smørgrav if (want_reply) { 250819261079SEd Maste if ((r = sshpkt_start(ssh, success ? SSH2_MSG_REQUEST_SUCCESS : 250919261079SEd Maste SSH2_MSG_REQUEST_FAILURE)) != 0 || 251019261079SEd Maste (r = sshpkt_send(ssh)) != 0 || 251119261079SEd Maste (r = ssh_packet_write_wait(ssh)) != 0) 251219261079SEd Maste goto out; 2513ae1f160dSDag-Erling Smørgrav } 251419261079SEd Maste r = 0; 251519261079SEd Maste out: 2516e4a9863fSDag-Erling Smørgrav free(rtype); 251719261079SEd Maste return r; 251819261079SEd Maste } 251919261079SEd Maste 252019261079SEd Maste static void 252119261079SEd Maste client_send_env(struct ssh *ssh, int id, const char *name, const char *val) 252219261079SEd Maste { 252319261079SEd Maste int r; 252419261079SEd Maste 252519261079SEd Maste debug("channel %d: setting env %s = \"%s\"", id, name, val); 252619261079SEd Maste channel_request_start(ssh, id, "env", 0); 252719261079SEd Maste if ((r = sshpkt_put_cstring(ssh, name)) != 0 || 252819261079SEd Maste (r = sshpkt_put_cstring(ssh, val)) != 0 || 252919261079SEd Maste (r = sshpkt_send(ssh)) != 0) 253019261079SEd Maste fatal_fr(r, "send setenv"); 2531ae1f160dSDag-Erling Smørgrav } 2532ae1f160dSDag-Erling Smørgrav 2533d74d50a8SDag-Erling Smørgrav void 25344f52dfbbSDag-Erling Smørgrav client_session2_setup(struct ssh *ssh, int id, int want_tty, int want_subsystem, 2535190cef3dSDag-Erling Smørgrav const char *term, struct termios *tiop, int in_fd, struct sshbuf *cmd, 2536190cef3dSDag-Erling Smørgrav char **env) 2537d74d50a8SDag-Erling Smørgrav { 253838a52bd3SEd Maste size_t i, j, len; 253938a52bd3SEd Maste int matched, r; 2540190cef3dSDag-Erling Smørgrav char *name, *val; 25415e8dbd04SDag-Erling Smørgrav Channel *c = NULL; 2542d74d50a8SDag-Erling Smørgrav 254319261079SEd Maste debug2_f("id %d", id); 2544d74d50a8SDag-Erling Smørgrav 25454f52dfbbSDag-Erling Smørgrav if ((c = channel_lookup(ssh, id)) == NULL) 254619261079SEd Maste fatal_f("channel %d: unknown channel", id); 25475e8dbd04SDag-Erling Smørgrav 254819261079SEd Maste ssh_packet_set_interactive(ssh, want_tty, 25494a421b63SDag-Erling Smørgrav options.ip_qos_interactive, options.ip_qos_bulk); 25504a421b63SDag-Erling Smørgrav 2551d74d50a8SDag-Erling Smørgrav if (want_tty) { 2552d74d50a8SDag-Erling Smørgrav struct winsize ws; 2553d74d50a8SDag-Erling Smørgrav 2554d74d50a8SDag-Erling Smørgrav /* Store window size in the packet. */ 255519261079SEd Maste if (ioctl(in_fd, TIOCGWINSZ, &ws) == -1) 2556d74d50a8SDag-Erling Smørgrav memset(&ws, 0, sizeof(ws)); 2557d74d50a8SDag-Erling Smørgrav 25584f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, id, "pty-req", 1); 25594f52dfbbSDag-Erling Smørgrav client_expect_confirm(ssh, id, "PTY allocation", CONFIRM_TTY); 256019261079SEd Maste if ((r = sshpkt_put_cstring(ssh, term != NULL ? term : "")) 256119261079SEd Maste != 0 || 256219261079SEd Maste (r = sshpkt_put_u32(ssh, (u_int)ws.ws_col)) != 0 || 256319261079SEd Maste (r = sshpkt_put_u32(ssh, (u_int)ws.ws_row)) != 0 || 256419261079SEd Maste (r = sshpkt_put_u32(ssh, (u_int)ws.ws_xpixel)) != 0 || 256519261079SEd Maste (r = sshpkt_put_u32(ssh, (u_int)ws.ws_ypixel)) != 0) 256619261079SEd Maste fatal_fr(r, "build pty-req"); 2567d4af9e69SDag-Erling Smørgrav if (tiop == NULL) 2568d4af9e69SDag-Erling Smørgrav tiop = get_saved_tio(); 2569190cef3dSDag-Erling Smørgrav ssh_tty_make_modes(ssh, -1, tiop); 257019261079SEd Maste if ((r = sshpkt_send(ssh)) != 0) 257119261079SEd Maste fatal_fr(r, "send pty-req"); 2572d74d50a8SDag-Erling Smørgrav /* XXX wait for reply */ 25735e8dbd04SDag-Erling Smørgrav c->client_tty = 1; 2574d74d50a8SDag-Erling Smørgrav } 2575d74d50a8SDag-Erling Smørgrav 2576d74d50a8SDag-Erling Smørgrav /* Transfer any environment variables from client to server */ 2577d74d50a8SDag-Erling Smørgrav if (options.num_send_env != 0 && env != NULL) { 2578d74d50a8SDag-Erling Smørgrav debug("Sending environment."); 2579d74d50a8SDag-Erling Smørgrav for (i = 0; env[i] != NULL; i++) { 2580d74d50a8SDag-Erling Smørgrav /* Split */ 2581d74d50a8SDag-Erling Smørgrav name = xstrdup(env[i]); 2582d74d50a8SDag-Erling Smørgrav if ((val = strchr(name, '=')) == NULL) { 2583e4a9863fSDag-Erling Smørgrav free(name); 2584d74d50a8SDag-Erling Smørgrav continue; 2585d74d50a8SDag-Erling Smørgrav } 2586d74d50a8SDag-Erling Smørgrav *val++ = '\0'; 2587d74d50a8SDag-Erling Smørgrav 2588d74d50a8SDag-Erling Smørgrav matched = 0; 2589d74d50a8SDag-Erling Smørgrav for (j = 0; j < options.num_send_env; j++) { 2590d74d50a8SDag-Erling Smørgrav if (match_pattern(name, options.send_env[j])) { 2591d74d50a8SDag-Erling Smørgrav matched = 1; 2592d74d50a8SDag-Erling Smørgrav break; 2593d74d50a8SDag-Erling Smørgrav } 2594d74d50a8SDag-Erling Smørgrav } 2595d74d50a8SDag-Erling Smørgrav if (!matched) { 2596d74d50a8SDag-Erling Smørgrav debug3("Ignored env %s", name); 2597e4a9863fSDag-Erling Smørgrav free(name); 2598d74d50a8SDag-Erling Smørgrav continue; 2599d74d50a8SDag-Erling Smørgrav } 260019261079SEd Maste client_send_env(ssh, id, name, val); 2601e4a9863fSDag-Erling Smørgrav free(name); 2602d74d50a8SDag-Erling Smørgrav } 2603d74d50a8SDag-Erling Smørgrav } 2604190cef3dSDag-Erling Smørgrav for (i = 0; i < options.num_setenv; i++) { 2605190cef3dSDag-Erling Smørgrav /* Split */ 2606190cef3dSDag-Erling Smørgrav name = xstrdup(options.setenv[i]); 2607190cef3dSDag-Erling Smørgrav if ((val = strchr(name, '=')) == NULL) { 2608190cef3dSDag-Erling Smørgrav free(name); 2609190cef3dSDag-Erling Smørgrav continue; 2610190cef3dSDag-Erling Smørgrav } 2611190cef3dSDag-Erling Smørgrav *val++ = '\0'; 261219261079SEd Maste client_send_env(ssh, id, name, val); 2613190cef3dSDag-Erling Smørgrav free(name); 2614190cef3dSDag-Erling Smørgrav } 2615190cef3dSDag-Erling Smørgrav 2616190cef3dSDag-Erling Smørgrav len = sshbuf_len(cmd); 2617d74d50a8SDag-Erling Smørgrav if (len > 0) { 2618d74d50a8SDag-Erling Smørgrav if (len > 900) 2619d74d50a8SDag-Erling Smørgrav len = 900; 2620d74d50a8SDag-Erling Smørgrav if (want_subsystem) { 2621d4af9e69SDag-Erling Smørgrav debug("Sending subsystem: %.*s", 262238a52bd3SEd Maste (int)len, (const u_char*)sshbuf_ptr(cmd)); 26234f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, id, "subsystem", 1); 26244f52dfbbSDag-Erling Smørgrav client_expect_confirm(ssh, id, "subsystem", 26254f52dfbbSDag-Erling Smørgrav CONFIRM_CLOSE); 2626d74d50a8SDag-Erling Smørgrav } else { 2627d4af9e69SDag-Erling Smørgrav debug("Sending command: %.*s", 262838a52bd3SEd Maste (int)len, (const u_char*)sshbuf_ptr(cmd)); 26294f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, id, "exec", 1); 26304f52dfbbSDag-Erling Smørgrav client_expect_confirm(ssh, id, "exec", CONFIRM_CLOSE); 2631d74d50a8SDag-Erling Smørgrav } 263219261079SEd Maste if ((r = sshpkt_put_stringb(ssh, cmd)) != 0 || 263319261079SEd Maste (r = sshpkt_send(ssh)) != 0) 263419261079SEd Maste fatal_fr(r, "send command"); 2635d74d50a8SDag-Erling Smørgrav } else { 26364f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, id, "shell", 1); 26374f52dfbbSDag-Erling Smørgrav client_expect_confirm(ssh, id, "shell", CONFIRM_CLOSE); 263819261079SEd Maste if ((r = sshpkt_send(ssh)) != 0) 263919261079SEd Maste fatal_fr(r, "send shell"); 2640d74d50a8SDag-Erling Smørgrav } 2641f374ba41SEd Maste 2642f374ba41SEd Maste session_setup_complete = 1; 2643f374ba41SEd Maste client_repledge(); 2644d74d50a8SDag-Erling Smørgrav } 2645d74d50a8SDag-Erling Smørgrav 2646ae1f160dSDag-Erling Smørgrav static void 264719261079SEd Maste client_init_dispatch(struct ssh *ssh) 2648a04a10f8SKris Kennaway { 264919261079SEd Maste ssh_dispatch_init(ssh, &dispatch_protocol_error); 2650545d5ecaSDag-Erling Smørgrav 265119261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); 265219261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_DATA, &channel_input_data); 265319261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); 265419261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); 265519261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_OPEN, &client_input_channel_open); 265619261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); 265719261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); 265819261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_REQUEST, &client_input_channel_req); 265919261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); 266019261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_SUCCESS, &channel_input_status_confirm); 266119261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_FAILURE, &channel_input_status_confirm); 266219261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_GLOBAL_REQUEST, &client_input_global_request); 26631e8db6e2SBrian Feldman 26641e8db6e2SBrian Feldman /* rekeying */ 266519261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit); 2666545d5ecaSDag-Erling Smørgrav 2667545d5ecaSDag-Erling Smørgrav /* global request reply messages */ 266819261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_REQUEST_FAILURE, &client_global_request_reply); 266919261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_REQUEST_SUCCESS, &client_global_request_reply); 2670a04a10f8SKris Kennaway } 2671d4af9e69SDag-Erling Smørgrav 2672e146993eSDag-Erling Smørgrav void 2673e146993eSDag-Erling Smørgrav client_stop_mux(void) 2674e146993eSDag-Erling Smørgrav { 2675e146993eSDag-Erling Smørgrav if (options.control_path != NULL && muxserver_sock != -1) 2676e146993eSDag-Erling Smørgrav unlink(options.control_path); 2677e146993eSDag-Erling Smørgrav /* 26786888a9beSDag-Erling Smørgrav * If we are in persist mode, or don't have a shell, signal that we 26796888a9beSDag-Erling Smørgrav * should close when all active channels are closed. 2680e146993eSDag-Erling Smørgrav */ 268119261079SEd Maste if (options.control_persist || options.session_type == SESSION_TYPE_NONE) { 2682e146993eSDag-Erling Smørgrav session_closed = 1; 2683e146993eSDag-Erling Smørgrav setproctitle("[stopped mux]"); 2684e146993eSDag-Erling Smørgrav } 2685e146993eSDag-Erling Smørgrav } 2686e146993eSDag-Erling Smørgrav 2687efcad6b7SDag-Erling Smørgrav /* client specific fatal cleanup */ 2688efcad6b7SDag-Erling Smørgrav void 2689efcad6b7SDag-Erling Smørgrav cleanup_exit(int i) 2690efcad6b7SDag-Erling Smørgrav { 2691e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 2692d4af9e69SDag-Erling Smørgrav if (options.control_path != NULL && muxserver_sock != -1) 2693d74d50a8SDag-Erling Smørgrav unlink(options.control_path); 26944a421b63SDag-Erling Smørgrav ssh_kill_proxy_command(); 2695efcad6b7SDag-Erling Smørgrav _exit(i); 2696efcad6b7SDag-Erling Smørgrav } 2697