1*a91a2465SEd Maste /* $OpenBSD: clientloop.c,v 1.403 2024/02/21 05:57:34 djm Exp $ */ 2511b41d2SMark Murray /* 3511b41d2SMark Murray * Author: Tatu Ylonen <ylo@cs.hut.fi> 4511b41d2SMark Murray * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 5511b41d2SMark Murray * All rights reserved 6511b41d2SMark Murray * The main loop for the interactive session (client side). 7511b41d2SMark Murray * 8b66f2d16SKris Kennaway * As far as I am concerned, the code I have written for this software 9b66f2d16SKris Kennaway * can be used freely for any purpose. Any derived versions of this 10b66f2d16SKris Kennaway * software must be clearly marked as such, and if the derived work is 11b66f2d16SKris Kennaway * incompatible with the protocol description in the RFC file, it must be 12b66f2d16SKris Kennaway * called by a name other than "ssh" or "Secure Shell". 13b66f2d16SKris Kennaway * 14b66f2d16SKris Kennaway * 15b66f2d16SKris Kennaway * Copyright (c) 1999 Theo de Raadt. All rights reserved. 16b66f2d16SKris Kennaway * 17b66f2d16SKris Kennaway * Redistribution and use in source and binary forms, with or without 18b66f2d16SKris Kennaway * modification, are permitted provided that the following conditions 19b66f2d16SKris Kennaway * are met: 20b66f2d16SKris Kennaway * 1. Redistributions of source code must retain the above copyright 21b66f2d16SKris Kennaway * notice, this list of conditions and the following disclaimer. 22b66f2d16SKris Kennaway * 2. Redistributions in binary form must reproduce the above copyright 23b66f2d16SKris Kennaway * notice, this list of conditions and the following disclaimer in the 24b66f2d16SKris Kennaway * documentation and/or other materials provided with the distribution. 25b66f2d16SKris Kennaway * 26b66f2d16SKris Kennaway * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 27b66f2d16SKris Kennaway * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 28b66f2d16SKris Kennaway * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 29b66f2d16SKris Kennaway * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 30b66f2d16SKris Kennaway * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 31b66f2d16SKris Kennaway * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32b66f2d16SKris Kennaway * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33b66f2d16SKris Kennaway * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34b66f2d16SKris Kennaway * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 35b66f2d16SKris Kennaway * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36b66f2d16SKris Kennaway * 37b66f2d16SKris Kennaway * 38a04a10f8SKris Kennaway * SSH2 support added by Markus Friedl. 39ae1f160dSDag-Erling Smørgrav * Copyright (c) 1999, 2000, 2001 Markus Friedl. All rights reserved. 40b66f2d16SKris Kennaway * 41b66f2d16SKris Kennaway * Redistribution and use in source and binary forms, with or without 42b66f2d16SKris Kennaway * modification, are permitted provided that the following conditions 43b66f2d16SKris Kennaway * are met: 44b66f2d16SKris Kennaway * 1. Redistributions of source code must retain the above copyright 45b66f2d16SKris Kennaway * notice, this list of conditions and the following disclaimer. 46b66f2d16SKris Kennaway * 2. Redistributions in binary form must reproduce the above copyright 47b66f2d16SKris Kennaway * notice, this list of conditions and the following disclaimer in the 48b66f2d16SKris Kennaway * documentation and/or other materials provided with the distribution. 49b66f2d16SKris Kennaway * 50b66f2d16SKris Kennaway * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 51b66f2d16SKris Kennaway * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 52b66f2d16SKris Kennaway * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 53b66f2d16SKris Kennaway * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 54b66f2d16SKris Kennaway * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 55b66f2d16SKris Kennaway * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 56b66f2d16SKris Kennaway * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 57b66f2d16SKris Kennaway * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 58b66f2d16SKris Kennaway * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 59b66f2d16SKris Kennaway * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 60511b41d2SMark Murray */ 61511b41d2SMark Murray 62511b41d2SMark Murray #include "includes.h" 63511b41d2SMark Murray 64761efaa7SDag-Erling Smørgrav #include <sys/types.h> 65761efaa7SDag-Erling Smørgrav #include <sys/ioctl.h> 66761efaa7SDag-Erling Smørgrav #ifdef HAVE_SYS_STAT_H 67761efaa7SDag-Erling Smørgrav # include <sys/stat.h> 68761efaa7SDag-Erling Smørgrav #endif 69761efaa7SDag-Erling Smørgrav #ifdef HAVE_SYS_TIME_H 70761efaa7SDag-Erling Smørgrav # include <sys/time.h> 71761efaa7SDag-Erling Smørgrav #endif 72761efaa7SDag-Erling Smørgrav #include <sys/socket.h> 73761efaa7SDag-Erling Smørgrav 74761efaa7SDag-Erling Smørgrav #include <ctype.h> 75761efaa7SDag-Erling Smørgrav #include <errno.h> 76761efaa7SDag-Erling Smørgrav #ifdef HAVE_PATHS_H 77761efaa7SDag-Erling Smørgrav #include <paths.h> 78761efaa7SDag-Erling Smørgrav #endif 791323ec57SEd Maste #ifdef HAVE_POLL_H 801323ec57SEd Maste #include <poll.h> 811323ec57SEd Maste #endif 82761efaa7SDag-Erling Smørgrav #include <signal.h> 83761efaa7SDag-Erling Smørgrav #include <stdio.h> 84761efaa7SDag-Erling Smørgrav #include <stdlib.h> 85761efaa7SDag-Erling Smørgrav #include <string.h> 8619261079SEd Maste #include <stdarg.h> 87761efaa7SDag-Erling Smørgrav #include <termios.h> 88761efaa7SDag-Erling Smørgrav #include <pwd.h> 89761efaa7SDag-Erling Smørgrav #include <unistd.h> 90bc5531deSDag-Erling Smørgrav #include <limits.h> 91761efaa7SDag-Erling Smørgrav 92d4af9e69SDag-Erling Smørgrav #include "openbsd-compat/sys-queue.h" 93761efaa7SDag-Erling Smørgrav #include "xmalloc.h" 94511b41d2SMark Murray #include "ssh.h" 951e8db6e2SBrian Feldman #include "ssh2.h" 96511b41d2SMark Murray #include "packet.h" 97190cef3dSDag-Erling Smørgrav #include "sshbuf.h" 98a04a10f8SKris Kennaway #include "compat.h" 99a04a10f8SKris Kennaway #include "channels.h" 100a04a10f8SKris Kennaway #include "dispatch.h" 101190cef3dSDag-Erling Smørgrav #include "sshkey.h" 102761efaa7SDag-Erling Smørgrav #include "cipher.h" 1031e8db6e2SBrian Feldman #include "kex.h" 104eccfee6eSDag-Erling Smørgrav #include "myproposal.h" 1051e8db6e2SBrian Feldman #include "log.h" 106a0ee8cc6SDag-Erling Smørgrav #include "misc.h" 1071e8db6e2SBrian Feldman #include "readconf.h" 1081e8db6e2SBrian Feldman #include "clientloop.h" 109021d409fSDag-Erling Smørgrav #include "sshconnect.h" 1101e8db6e2SBrian Feldman #include "authfd.h" 1111e8db6e2SBrian Feldman #include "atomicio.h" 112d74d50a8SDag-Erling Smørgrav #include "sshpty.h" 113d74d50a8SDag-Erling Smørgrav #include "match.h" 114d74d50a8SDag-Erling Smørgrav #include "msg.h" 115bc5531deSDag-Erling Smørgrav #include "ssherr.h" 116bc5531deSDag-Erling Smørgrav #include "hostfile.h" 1175b9b2fafSBrian Feldman 1181323ec57SEd Maste /* Permitted RSA signature algorithms for UpdateHostkeys proofs */ 1191323ec57SEd Maste #define HOSTKEY_PROOF_RSA_ALGS "rsa-sha2-512,rsa-sha2-256" 1201323ec57SEd Maste 121edf85781SEd Maste /* Uncertainty (in percent) of keystroke timing intervals */ 122edf85781SEd Maste #define SSH_KEYSTROKE_TIMING_FUZZ 10 123edf85781SEd Maste 1245b9b2fafSBrian Feldman /* import options */ 1254899dde7SBrian Feldman extern Options options; 1264899dde7SBrian Feldman 127d74d50a8SDag-Erling Smørgrav /* Control socket */ 128b15c8340SDag-Erling Smørgrav extern int muxserver_sock; /* XXX use mux_client_cleanup() instead */ 129d74d50a8SDag-Erling Smørgrav 130511b41d2SMark Murray /* 131511b41d2SMark Murray * Name of the host we are connecting to. This is the name given on the 13219261079SEd Maste * command line, or the Hostname specified for the user-supplied name in a 133511b41d2SMark Murray * configuration file. 134511b41d2SMark Murray */ 135511b41d2SMark Murray extern char *host; 136511b41d2SMark Murray 137511b41d2SMark Murray /* 13819261079SEd Maste * If this field is not NULL, the ForwardAgent socket is this path and different 13919261079SEd Maste * instead of SSH_AUTH_SOCK. 14019261079SEd Maste */ 14119261079SEd Maste extern char *forward_agent_sock_path; 14219261079SEd Maste 14319261079SEd Maste /* 144511b41d2SMark Murray * Flag to indicate that we have received a window change signal which has 145511b41d2SMark Murray * not yet been processed. This will cause a message indicating the new 146511b41d2SMark Murray * window size to be sent to the server a little later. This is volatile 147511b41d2SMark Murray * because this is updated in a signal handler. 148511b41d2SMark Murray */ 149ae1f160dSDag-Erling Smørgrav static volatile sig_atomic_t received_window_change_signal = 0; 150ae1f160dSDag-Erling Smørgrav static volatile sig_atomic_t received_signal = 0; 151511b41d2SMark Murray 152e2f6069cSDag-Erling Smørgrav /* Time when backgrounded control master using ControlPersist should exit */ 153e2f6069cSDag-Erling Smørgrav static time_t control_persist_exit_time = 0; 154e2f6069cSDag-Erling Smørgrav 155511b41d2SMark Murray /* Common data for the client loop code. */ 156b15c8340SDag-Erling Smørgrav volatile sig_atomic_t quit_pending; /* Set non-zero to quit the loop. */ 157511b41d2SMark Murray static int last_was_cr; /* Last character was a newline. */ 158d4af9e69SDag-Erling Smørgrav static int exit_status; /* Used to store the command exit status. */ 159190cef3dSDag-Erling Smørgrav static struct sshbuf *stderr_buffer; /* Used for final exit message. */ 160511b41d2SMark Murray static int connection_in; /* Connection to server (input). */ 161511b41d2SMark Murray static int connection_out; /* Connection to server (output). */ 1621e8db6e2SBrian Feldman static int need_rekeying; /* Set to non-zero if rekeying is requested. */ 163e2f6069cSDag-Erling Smørgrav static int session_closed; /* In SSH2: login session closed. */ 1644d3fc8b0SEd Maste static time_t x11_refuse_time; /* If >0, refuse x11 opens after this time. */ 16519261079SEd Maste static time_t server_alive_time; /* Time to do server_alive_check */ 166f374ba41SEd Maste static int hostkeys_update_complete; 167f374ba41SEd Maste static int session_setup_complete; 168a04a10f8SKris Kennaway 16919261079SEd Maste static void client_init_dispatch(struct ssh *ssh); 170a04a10f8SKris Kennaway int session_ident = -1; 171a04a10f8SKris Kennaway 172d4af9e69SDag-Erling Smørgrav /* Track escape per proto2 channel */ 173d4af9e69SDag-Erling Smørgrav struct escape_filter_ctx { 174d4af9e69SDag-Erling Smørgrav int escape_pending; 175d4af9e69SDag-Erling Smørgrav int escape_char; 176d74d50a8SDag-Erling Smørgrav }; 177d74d50a8SDag-Erling Smørgrav 178d4af9e69SDag-Erling Smørgrav /* Context for channel confirmation replies */ 179d4af9e69SDag-Erling Smørgrav struct channel_reply_ctx { 180d4af9e69SDag-Erling Smørgrav const char *request_type; 181e146993eSDag-Erling Smørgrav int id; 182e146993eSDag-Erling Smørgrav enum confirm_action action; 183d4af9e69SDag-Erling Smørgrav }; 184d4af9e69SDag-Erling Smørgrav 185d4af9e69SDag-Erling Smørgrav /* Global request success/failure callbacks */ 1864f52dfbbSDag-Erling Smørgrav /* XXX move to struct ssh? */ 187d4af9e69SDag-Erling Smørgrav struct global_confirm { 188d4af9e69SDag-Erling Smørgrav TAILQ_ENTRY(global_confirm) entry; 189d4af9e69SDag-Erling Smørgrav global_confirm_cb *cb; 190d4af9e69SDag-Erling Smørgrav void *ctx; 191d4af9e69SDag-Erling Smørgrav int ref_count; 192d4af9e69SDag-Erling Smørgrav }; 193d4af9e69SDag-Erling Smørgrav TAILQ_HEAD(global_confirms, global_confirm); 194d4af9e69SDag-Erling Smørgrav static struct global_confirms global_confirms = 195d4af9e69SDag-Erling Smørgrav TAILQ_HEAD_INITIALIZER(global_confirms); 196d4af9e69SDag-Erling Smørgrav 197190cef3dSDag-Erling Smørgrav void ssh_process_session2_setup(int, int, int, struct sshbuf *); 1981323ec57SEd Maste static void quit_message(const char *fmt, ...) 1991323ec57SEd Maste __attribute__((__format__ (printf, 1, 2))); 2001323ec57SEd Maste 2011323ec57SEd Maste static void 2021323ec57SEd Maste quit_message(const char *fmt, ...) 2031323ec57SEd Maste { 2041323ec57SEd Maste char *msg; 2051323ec57SEd Maste va_list args; 2061323ec57SEd Maste int r; 2071323ec57SEd Maste 2081323ec57SEd Maste va_start(args, fmt); 2091323ec57SEd Maste xvasprintf(&msg, fmt, args); 2101323ec57SEd Maste va_end(args); 2111323ec57SEd Maste 2121323ec57SEd Maste if ((r = sshbuf_putf(stderr_buffer, "%s\r\n", msg)) != 0) 2131323ec57SEd Maste fatal_fr(r, "sshbuf_putf"); 214069ac184SEd Maste free(msg); 2151323ec57SEd Maste quit_pending = 1; 2161323ec57SEd Maste } 217d74d50a8SDag-Erling Smørgrav 218511b41d2SMark Murray /* 219511b41d2SMark Murray * Signal handler for the window change signal (SIGWINCH). This just sets a 220511b41d2SMark Murray * flag indicating that the window has changed. 221511b41d2SMark Murray */ 222ae1f160dSDag-Erling Smørgrav static void 223511b41d2SMark Murray window_change_handler(int sig) 224511b41d2SMark Murray { 225511b41d2SMark Murray received_window_change_signal = 1; 226511b41d2SMark Murray } 227511b41d2SMark Murray 228511b41d2SMark Murray /* 229511b41d2SMark Murray * Signal handler for signals that cause the program to terminate. These 230511b41d2SMark Murray * signals must be trapped to restore terminal modes. 231511b41d2SMark Murray */ 232ae1f160dSDag-Erling Smørgrav static void 233511b41d2SMark Murray signal_handler(int sig) 234511b41d2SMark Murray { 235ae1f160dSDag-Erling Smørgrav received_signal = sig; 236ae1f160dSDag-Erling Smørgrav quit_pending = 1; 237511b41d2SMark Murray } 238511b41d2SMark Murray 239511b41d2SMark Murray /* 240e2f6069cSDag-Erling Smørgrav * Sets control_persist_exit_time to the absolute time when the 241e2f6069cSDag-Erling Smørgrav * backgrounded control master should exit due to expiry of the 242e2f6069cSDag-Erling Smørgrav * ControlPersist timeout. Sets it to 0 if we are not a backgrounded 243e2f6069cSDag-Erling Smørgrav * control master process, or if there is no ControlPersist timeout. 244e2f6069cSDag-Erling Smørgrav */ 245e2f6069cSDag-Erling Smørgrav static void 2464f52dfbbSDag-Erling Smørgrav set_control_persist_exit_time(struct ssh *ssh) 247e2f6069cSDag-Erling Smørgrav { 248e2f6069cSDag-Erling Smørgrav if (muxserver_sock == -1 || !options.control_persist 249e146993eSDag-Erling Smørgrav || options.control_persist_timeout == 0) { 250e2f6069cSDag-Erling Smørgrav /* not using a ControlPersist timeout */ 251e2f6069cSDag-Erling Smørgrav control_persist_exit_time = 0; 2524f52dfbbSDag-Erling Smørgrav } else if (channel_still_open(ssh)) { 253e2f6069cSDag-Erling Smørgrav /* some client connections are still open */ 254e2f6069cSDag-Erling Smørgrav if (control_persist_exit_time > 0) 25519261079SEd Maste debug2_f("cancel scheduled exit"); 256e2f6069cSDag-Erling Smørgrav control_persist_exit_time = 0; 257e2f6069cSDag-Erling Smørgrav } else if (control_persist_exit_time <= 0) { 258e2f6069cSDag-Erling Smørgrav /* a client connection has recently closed */ 259e4a9863fSDag-Erling Smørgrav control_persist_exit_time = monotime() + 260e2f6069cSDag-Erling Smørgrav (time_t)options.control_persist_timeout; 26119261079SEd Maste debug2_f("schedule exit in %d seconds", 262e2f6069cSDag-Erling Smørgrav options.control_persist_timeout); 263e2f6069cSDag-Erling Smørgrav } 264e2f6069cSDag-Erling Smørgrav /* else we are already counting down to the timeout */ 265e2f6069cSDag-Erling Smørgrav } 266e2f6069cSDag-Erling Smørgrav 267462c32cbSDag-Erling Smørgrav #define SSH_X11_VALID_DISPLAY_CHARS ":/.-_" 268462c32cbSDag-Erling Smørgrav static int 269462c32cbSDag-Erling Smørgrav client_x11_display_valid(const char *display) 270462c32cbSDag-Erling Smørgrav { 271462c32cbSDag-Erling Smørgrav size_t i, dlen; 272462c32cbSDag-Erling Smørgrav 273acc1a9efSDag-Erling Smørgrav if (display == NULL) 274acc1a9efSDag-Erling Smørgrav return 0; 275acc1a9efSDag-Erling Smørgrav 276462c32cbSDag-Erling Smørgrav dlen = strlen(display); 277462c32cbSDag-Erling Smørgrav for (i = 0; i < dlen; i++) { 278f7167e0eSDag-Erling Smørgrav if (!isalnum((u_char)display[i]) && 279462c32cbSDag-Erling Smørgrav strchr(SSH_X11_VALID_DISPLAY_CHARS, display[i]) == NULL) { 280462c32cbSDag-Erling Smørgrav debug("Invalid character '%c' in DISPLAY", display[i]); 281462c32cbSDag-Erling Smørgrav return 0; 282462c32cbSDag-Erling Smørgrav } 283462c32cbSDag-Erling Smørgrav } 284462c32cbSDag-Erling Smørgrav return 1; 285462c32cbSDag-Erling Smørgrav } 286462c32cbSDag-Erling Smørgrav 287043840dfSDag-Erling Smørgrav #define SSH_X11_PROTO "MIT-MAGIC-COOKIE-1" 288557f75e5SDag-Erling Smørgrav #define X11_TIMEOUT_SLACK 60 289acc1a9efSDag-Erling Smørgrav int 2904f52dfbbSDag-Erling Smørgrav client_x11_get_proto(struct ssh *ssh, const char *display, 2914f52dfbbSDag-Erling Smørgrav const char *xauth_path, u_int trusted, u_int timeout, 2924f52dfbbSDag-Erling Smørgrav char **_proto, char **_data) 293043840dfSDag-Erling Smørgrav { 2942f513db7SEd Maste char *cmd, line[512], xdisplay[512]; 295acc1a9efSDag-Erling Smørgrav char xauthfile[PATH_MAX], xauthdir[PATH_MAX]; 296043840dfSDag-Erling Smørgrav static char proto[512], data[512]; 297043840dfSDag-Erling Smørgrav FILE *f; 298ca86bcf2SDag-Erling Smørgrav int got_data = 0, generated = 0, do_unlink = 0, r; 299043840dfSDag-Erling Smørgrav struct stat st; 300557f75e5SDag-Erling Smørgrav u_int now, x11_timeout_real; 301043840dfSDag-Erling Smørgrav 302043840dfSDag-Erling Smørgrav *_proto = proto; 303043840dfSDag-Erling Smørgrav *_data = data; 304acc1a9efSDag-Erling Smørgrav proto[0] = data[0] = xauthfile[0] = xauthdir[0] = '\0'; 305043840dfSDag-Erling Smørgrav 306acc1a9efSDag-Erling Smørgrav if (!client_x11_display_valid(display)) { 307acc1a9efSDag-Erling Smørgrav if (display != NULL) 308acc1a9efSDag-Erling Smørgrav logit("DISPLAY \"%s\" invalid; disabling X11 forwarding", 309462c32cbSDag-Erling Smørgrav display); 310acc1a9efSDag-Erling Smørgrav return -1; 311043840dfSDag-Erling Smørgrav } 312acc1a9efSDag-Erling Smørgrav if (xauth_path != NULL && stat(xauth_path, &st) == -1) { 313acc1a9efSDag-Erling Smørgrav debug("No xauth program."); 314acc1a9efSDag-Erling Smørgrav xauth_path = NULL; 315acc1a9efSDag-Erling Smørgrav } 316acc1a9efSDag-Erling Smørgrav 317acc1a9efSDag-Erling Smørgrav if (xauth_path != NULL) { 318043840dfSDag-Erling Smørgrav /* 319043840dfSDag-Erling Smørgrav * Handle FamilyLocal case where $DISPLAY does 320043840dfSDag-Erling Smørgrav * not match an authorization entry. For this we 321043840dfSDag-Erling Smørgrav * just try "xauth list unix:displaynum.screennum". 322043840dfSDag-Erling Smørgrav * XXX: "localhost" match to determine FamilyLocal 323043840dfSDag-Erling Smørgrav * is not perfect. 324043840dfSDag-Erling Smørgrav */ 325043840dfSDag-Erling Smørgrav if (strncmp(display, "localhost:", 10) == 0) { 326acc1a9efSDag-Erling Smørgrav if ((r = snprintf(xdisplay, sizeof(xdisplay), "unix:%s", 327acc1a9efSDag-Erling Smørgrav display + 10)) < 0 || 328acc1a9efSDag-Erling Smørgrav (size_t)r >= sizeof(xdisplay)) { 32919261079SEd Maste error_f("display name too long"); 330acc1a9efSDag-Erling Smørgrav return -1; 331acc1a9efSDag-Erling Smørgrav } 332043840dfSDag-Erling Smørgrav display = xdisplay; 333043840dfSDag-Erling Smørgrav } 334043840dfSDag-Erling Smørgrav if (trusted == 0) { 335557f75e5SDag-Erling Smørgrav /* 336acc1a9efSDag-Erling Smørgrav * Generate an untrusted X11 auth cookie. 337acc1a9efSDag-Erling Smørgrav * 338557f75e5SDag-Erling Smørgrav * The authentication cookie should briefly outlive 339557f75e5SDag-Erling Smørgrav * ssh's willingness to forward X11 connections to 340557f75e5SDag-Erling Smørgrav * avoid nasty fail-open behaviour in the X server. 341557f75e5SDag-Erling Smørgrav */ 342acc1a9efSDag-Erling Smørgrav mktemp_proto(xauthdir, sizeof(xauthdir)); 343acc1a9efSDag-Erling Smørgrav if (mkdtemp(xauthdir) == NULL) { 34419261079SEd Maste error_f("mkdtemp: %s", strerror(errno)); 345acc1a9efSDag-Erling Smørgrav return -1; 346acc1a9efSDag-Erling Smørgrav } 347acc1a9efSDag-Erling Smørgrav do_unlink = 1; 348acc1a9efSDag-Erling Smørgrav if ((r = snprintf(xauthfile, sizeof(xauthfile), 349acc1a9efSDag-Erling Smørgrav "%s/xauthfile", xauthdir)) < 0 || 350acc1a9efSDag-Erling Smørgrav (size_t)r >= sizeof(xauthfile)) { 35119261079SEd Maste error_f("xauthfile path too long"); 352acc1a9efSDag-Erling Smørgrav rmdir(xauthdir); 353acc1a9efSDag-Erling Smørgrav return -1; 354acc1a9efSDag-Erling Smørgrav } 355acc1a9efSDag-Erling Smørgrav 3562f513db7SEd Maste if (timeout == 0) { 3572f513db7SEd Maste /* auth doesn't time out */ 3582f513db7SEd Maste xasprintf(&cmd, "%s -f %s generate %s %s " 3592f513db7SEd Maste "untrusted 2>%s", 360557f75e5SDag-Erling Smørgrav xauth_path, xauthfile, display, 3612f513db7SEd Maste SSH_X11_PROTO, _PATH_DEVNULL); 3622f513db7SEd Maste } else { 3632f513db7SEd Maste /* Add some slack to requested expiry */ 3642f513db7SEd Maste if (timeout < UINT_MAX - X11_TIMEOUT_SLACK) 3652f513db7SEd Maste x11_timeout_real = timeout + 3662f513db7SEd Maste X11_TIMEOUT_SLACK; 3672f513db7SEd Maste else { 3682f513db7SEd Maste /* Don't overflow on long timeouts */ 3692f513db7SEd Maste x11_timeout_real = UINT_MAX; 3702f513db7SEd Maste } 3712f513db7SEd Maste xasprintf(&cmd, "%s -f %s generate %s %s " 3722f513db7SEd Maste "untrusted timeout %u 2>%s", 3732f513db7SEd Maste xauth_path, xauthfile, display, 3742f513db7SEd Maste SSH_X11_PROTO, x11_timeout_real, 3752f513db7SEd Maste _PATH_DEVNULL); 3762f513db7SEd Maste } 37719261079SEd Maste debug2_f("xauth command: %s", cmd); 3782f513db7SEd Maste 3792f513db7SEd Maste if (timeout != 0 && x11_refuse_time == 0) { 380e4a9863fSDag-Erling Smørgrav now = monotime() + 1; 3814d3fc8b0SEd Maste if (SSH_TIME_T_MAX - timeout < now) 3824d3fc8b0SEd Maste x11_refuse_time = SSH_TIME_T_MAX; 383e2f6069cSDag-Erling Smørgrav else 384e2f6069cSDag-Erling Smørgrav x11_refuse_time = now + timeout; 3854f52dfbbSDag-Erling Smørgrav channel_set_x11_refuse_time(ssh, 3864f52dfbbSDag-Erling Smørgrav x11_refuse_time); 387e2f6069cSDag-Erling Smørgrav } 388557f75e5SDag-Erling Smørgrav if (system(cmd) == 0) 389557f75e5SDag-Erling Smørgrav generated = 1; 3902f513db7SEd Maste free(cmd); 391043840dfSDag-Erling Smørgrav } 392d4af9e69SDag-Erling Smørgrav 393d4af9e69SDag-Erling Smørgrav /* 394d4af9e69SDag-Erling Smørgrav * When in untrusted mode, we read the cookie only if it was 395d4af9e69SDag-Erling Smørgrav * successfully generated as an untrusted one in the step 396d4af9e69SDag-Erling Smørgrav * above. 397d4af9e69SDag-Erling Smørgrav */ 398d4af9e69SDag-Erling Smørgrav if (trusted || generated) { 3992f513db7SEd Maste xasprintf(&cmd, 400021d409fSDag-Erling Smørgrav "%s %s%s list %s 2>" _PATH_DEVNULL, 401043840dfSDag-Erling Smørgrav xauth_path, 402043840dfSDag-Erling Smørgrav generated ? "-f " : "" , 403043840dfSDag-Erling Smørgrav generated ? xauthfile : "", 404043840dfSDag-Erling Smørgrav display); 405043840dfSDag-Erling Smørgrav debug2("x11_get_proto: %s", cmd); 406043840dfSDag-Erling Smørgrav f = popen(cmd, "r"); 407043840dfSDag-Erling Smørgrav if (f && fgets(line, sizeof(line), f) && 408043840dfSDag-Erling Smørgrav sscanf(line, "%*s %511s %511s", proto, data) == 2) 409043840dfSDag-Erling Smørgrav got_data = 1; 410043840dfSDag-Erling Smørgrav if (f) 411043840dfSDag-Erling Smørgrav pclose(f); 4122f513db7SEd Maste free(cmd); 413acc1a9efSDag-Erling Smørgrav } 414043840dfSDag-Erling Smørgrav } 415043840dfSDag-Erling Smørgrav 416043840dfSDag-Erling Smørgrav if (do_unlink) { 417043840dfSDag-Erling Smørgrav unlink(xauthfile); 418043840dfSDag-Erling Smørgrav rmdir(xauthdir); 419043840dfSDag-Erling Smørgrav } 420acc1a9efSDag-Erling Smørgrav 421acc1a9efSDag-Erling Smørgrav /* Don't fall back to fake X11 data for untrusted forwarding */ 422acc1a9efSDag-Erling Smørgrav if (!trusted && !got_data) { 423acc1a9efSDag-Erling Smørgrav error("Warning: untrusted X11 forwarding setup failed: " 424acc1a9efSDag-Erling Smørgrav "xauth key data not generated"); 425acc1a9efSDag-Erling Smørgrav return -1; 426acc1a9efSDag-Erling Smørgrav } 427043840dfSDag-Erling Smørgrav 428043840dfSDag-Erling Smørgrav /* 429043840dfSDag-Erling Smørgrav * If we didn't get authentication data, just make up some 430043840dfSDag-Erling Smørgrav * data. The forwarding code will check the validity of the 431043840dfSDag-Erling Smørgrav * response anyway, and substitute this data. The X11 432043840dfSDag-Erling Smørgrav * server, however, will ignore this fake data and use 433043840dfSDag-Erling Smørgrav * whatever authentication mechanisms it was using otherwise 434043840dfSDag-Erling Smørgrav * for the local connection. 435043840dfSDag-Erling Smørgrav */ 436043840dfSDag-Erling Smørgrav if (!got_data) { 437ca86bcf2SDag-Erling Smørgrav u_int8_t rnd[16]; 438ca86bcf2SDag-Erling Smørgrav u_int i; 439043840dfSDag-Erling Smørgrav 440043840dfSDag-Erling Smørgrav logit("Warning: No xauth data; " 441043840dfSDag-Erling Smørgrav "using fake authentication data for X11 forwarding."); 442043840dfSDag-Erling Smørgrav strlcpy(proto, SSH_X11_PROTO, sizeof proto); 443ca86bcf2SDag-Erling Smørgrav arc4random_buf(rnd, sizeof(rnd)); 444ca86bcf2SDag-Erling Smørgrav for (i = 0; i < sizeof(rnd); i++) { 445043840dfSDag-Erling Smørgrav snprintf(data + 2 * i, sizeof data - 2 * i, "%02x", 446ca86bcf2SDag-Erling Smørgrav rnd[i]); 447043840dfSDag-Erling Smørgrav } 448043840dfSDag-Erling Smørgrav } 449acc1a9efSDag-Erling Smørgrav 450acc1a9efSDag-Erling Smørgrav return 0; 451043840dfSDag-Erling Smørgrav } 452043840dfSDag-Erling Smørgrav 453511b41d2SMark Murray /* 454511b41d2SMark Murray * Checks if the client window has changed, and sends a packet about it to 455511b41d2SMark Murray * the server if so. The actual change is detected elsewhere (by a software 456511b41d2SMark Murray * interrupt on Unix); this just checks the flag and sends a message if 457511b41d2SMark Murray * appropriate. 458511b41d2SMark Murray */ 459511b41d2SMark Murray 460ae1f160dSDag-Erling Smørgrav static void 4614f52dfbbSDag-Erling Smørgrav client_check_window_change(struct ssh *ssh) 462511b41d2SMark Murray { 463a04a10f8SKris Kennaway if (!received_window_change_signal) 464a04a10f8SKris Kennaway return; 465511b41d2SMark Murray received_window_change_signal = 0; 46619261079SEd Maste debug2_f("changed"); 4674f52dfbbSDag-Erling Smørgrav channel_send_window_changes(ssh); 468511b41d2SMark Murray } 469511b41d2SMark Murray 470bc5531deSDag-Erling Smørgrav static int 4714f52dfbbSDag-Erling Smørgrav client_global_request_reply(int type, u_int32_t seq, struct ssh *ssh) 472efcad6b7SDag-Erling Smørgrav { 473d4af9e69SDag-Erling Smørgrav struct global_confirm *gc; 474d4af9e69SDag-Erling Smørgrav 475d4af9e69SDag-Erling Smørgrav if ((gc = TAILQ_FIRST(&global_confirms)) == NULL) 476bc5531deSDag-Erling Smørgrav return 0; 477d4af9e69SDag-Erling Smørgrav if (gc->cb != NULL) 4784f52dfbbSDag-Erling Smørgrav gc->cb(ssh, type, seq, gc->ctx); 479d4af9e69SDag-Erling Smørgrav if (--gc->ref_count <= 0) { 480d4af9e69SDag-Erling Smørgrav TAILQ_REMOVE(&global_confirms, gc, entry); 48119261079SEd Maste freezero(gc, sizeof(*gc)); 482d4af9e69SDag-Erling Smørgrav } 483d4af9e69SDag-Erling Smørgrav 48419261079SEd Maste ssh_packet_set_alive_timeouts(ssh, 0); 485bc5531deSDag-Erling Smørgrav return 0; 486efcad6b7SDag-Erling Smørgrav } 487efcad6b7SDag-Erling Smørgrav 488efcad6b7SDag-Erling Smørgrav static void 48919261079SEd Maste schedule_server_alive_check(void) 490efcad6b7SDag-Erling Smørgrav { 49119261079SEd Maste if (options.server_alive_interval > 0) 49219261079SEd Maste server_alive_time = monotime() + options.server_alive_interval; 49319261079SEd Maste } 49419261079SEd Maste 49519261079SEd Maste static void 49619261079SEd Maste server_alive_check(struct ssh *ssh) 49719261079SEd Maste { 49819261079SEd Maste int r; 49919261079SEd Maste 50019261079SEd Maste if (ssh_packet_inc_alive_timeouts(ssh) > options.server_alive_count_max) { 5014a421b63SDag-Erling Smørgrav logit("Timeout, server %s not responding.", host); 50292eb0aa1SDag-Erling Smørgrav cleanup_exit(255); 50392eb0aa1SDag-Erling Smørgrav } 50419261079SEd Maste if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || 50519261079SEd Maste (r = sshpkt_put_cstring(ssh, "keepalive@openssh.com")) != 0 || 50619261079SEd Maste (r = sshpkt_put_u8(ssh, 1)) != 0 || /* boolean: want reply */ 50719261079SEd Maste (r = sshpkt_send(ssh)) != 0) 50819261079SEd Maste fatal_fr(r, "send packet"); 509d4af9e69SDag-Erling Smørgrav /* Insert an empty placeholder to maintain ordering */ 510d4af9e69SDag-Erling Smørgrav client_register_global_confirm(NULL, NULL); 51119261079SEd Maste schedule_server_alive_check(); 512efcad6b7SDag-Erling Smørgrav } 513efcad6b7SDag-Erling Smørgrav 514edf85781SEd Maste /* Try to send a dummy keystroke */ 515edf85781SEd Maste static int 516edf85781SEd Maste send_chaff(struct ssh *ssh) 517edf85781SEd Maste { 518edf85781SEd Maste int r; 519edf85781SEd Maste 520*a91a2465SEd Maste if (ssh->kex == NULL || (ssh->kex->flags & KEX_HAS_PING) == 0) 521edf85781SEd Maste return 0; 522edf85781SEd Maste /* XXX probabilistically send chaff? */ 523edf85781SEd Maste /* 524edf85781SEd Maste * a SSH2_MSG_CHANNEL_DATA payload is 9 bytes: 525edf85781SEd Maste * 4 bytes channel ID + 4 bytes string length + 1 byte string data 526edf85781SEd Maste * simulate that here. 527edf85781SEd Maste */ 528edf85781SEd Maste if ((r = sshpkt_start(ssh, SSH2_MSG_PING)) != 0 || 529edf85781SEd Maste (r = sshpkt_put_cstring(ssh, "PING!")) != 0 || 530edf85781SEd Maste (r = sshpkt_send(ssh)) != 0) 531edf85781SEd Maste fatal_fr(r, "send packet"); 532edf85781SEd Maste return 1; 533edf85781SEd Maste } 534edf85781SEd Maste 535edf85781SEd Maste /* Sets the next interval to send a keystroke or chaff packet */ 536edf85781SEd Maste static void 537edf85781SEd Maste set_next_interval(const struct timespec *now, struct timespec *next_interval, 538edf85781SEd Maste u_int interval_ms, int starting) 539edf85781SEd Maste { 540edf85781SEd Maste struct timespec tmp; 541edf85781SEd Maste long long interval_ns, fuzz_ns; 542edf85781SEd Maste static long long rate_fuzz; 543edf85781SEd Maste 544edf85781SEd Maste interval_ns = interval_ms * (1000LL * 1000); 545edf85781SEd Maste fuzz_ns = (interval_ns * SSH_KEYSTROKE_TIMING_FUZZ) / 100; 546edf85781SEd Maste /* Center fuzz around requested interval */ 547edf85781SEd Maste if (fuzz_ns > INT_MAX) 548edf85781SEd Maste fuzz_ns = INT_MAX; 549edf85781SEd Maste if (fuzz_ns > interval_ns) { 550edf85781SEd Maste /* Shouldn't happen */ 551edf85781SEd Maste fatal_f("internal error: fuzz %u%% %lldns > interval %lldns", 552edf85781SEd Maste SSH_KEYSTROKE_TIMING_FUZZ, fuzz_ns, interval_ns); 553edf85781SEd Maste } 554edf85781SEd Maste /* 555edf85781SEd Maste * Randomise the keystroke/chaff intervals in two ways: 556edf85781SEd Maste * 1. Each interval has some random jitter applied to make the 557edf85781SEd Maste * interval-to-interval time unpredictable. 558edf85781SEd Maste * 2. The overall interval rate is also randomly perturbed for each 559edf85781SEd Maste * chaffing session to make the average rate unpredictable. 560edf85781SEd Maste */ 561edf85781SEd Maste if (starting) 562edf85781SEd Maste rate_fuzz = arc4random_uniform(fuzz_ns); 563edf85781SEd Maste interval_ns -= fuzz_ns; 564edf85781SEd Maste interval_ns += arc4random_uniform(fuzz_ns) + rate_fuzz; 565edf85781SEd Maste 566edf85781SEd Maste tmp.tv_sec = interval_ns / (1000 * 1000 * 1000); 567edf85781SEd Maste tmp.tv_nsec = interval_ns % (1000 * 1000 * 1000); 568edf85781SEd Maste 569edf85781SEd Maste timespecadd(now, &tmp, next_interval); 570edf85781SEd Maste } 571edf85781SEd Maste 572edf85781SEd Maste /* 573edf85781SEd Maste * Performs keystroke timing obfuscation. Returns non-zero if the 574edf85781SEd Maste * output fd should be polled. 575edf85781SEd Maste */ 576edf85781SEd Maste static int 577edf85781SEd Maste obfuscate_keystroke_timing(struct ssh *ssh, struct timespec *timeout, 578edf85781SEd Maste int channel_did_enqueue) 579edf85781SEd Maste { 580edf85781SEd Maste static int active; 581edf85781SEd Maste static struct timespec next_interval, chaff_until; 582edf85781SEd Maste struct timespec now, tmp; 583edf85781SEd Maste int just_started = 0, had_keystroke = 0; 584edf85781SEd Maste static unsigned long long nchaff; 585edf85781SEd Maste char *stop_reason = NULL; 586edf85781SEd Maste long long n; 587edf85781SEd Maste 588edf85781SEd Maste monotime_ts(&now); 589edf85781SEd Maste 590edf85781SEd Maste if (options.obscure_keystroke_timing_interval <= 0) 591edf85781SEd Maste return 1; /* disabled in config */ 592edf85781SEd Maste 593069ac184SEd Maste if (!channel_tty_open(ssh) || quit_pending) { 594edf85781SEd Maste /* Stop if no channels left of we're waiting for one to close */ 595edf85781SEd Maste stop_reason = "no active channels"; 596edf85781SEd Maste } else if (ssh_packet_is_rekeying(ssh)) { 597edf85781SEd Maste /* Stop if we're rekeying */ 598edf85781SEd Maste stop_reason = "rekeying started"; 599edf85781SEd Maste } else if (!ssh_packet_interactive_data_to_write(ssh) && 600edf85781SEd Maste ssh_packet_have_data_to_write(ssh)) { 601edf85781SEd Maste /* Stop if the output buffer has more than a few keystrokes */ 602edf85781SEd Maste stop_reason = "output buffer filling"; 603edf85781SEd Maste } else if (active && channel_did_enqueue && 604edf85781SEd Maste ssh_packet_have_data_to_write(ssh)) { 605edf85781SEd Maste /* Still in active mode and have a keystroke queued. */ 606edf85781SEd Maste had_keystroke = 1; 607edf85781SEd Maste } else if (active) { 608edf85781SEd Maste if (timespeccmp(&now, &chaff_until, >=)) { 609edf85781SEd Maste /* Stop if there have been no keystrokes for a while */ 610edf85781SEd Maste stop_reason = "chaff time expired"; 611edf85781SEd Maste } else if (timespeccmp(&now, &next_interval, >=)) { 612edf85781SEd Maste /* Otherwise if we were due to send, then send chaff */ 613edf85781SEd Maste if (send_chaff(ssh)) 614edf85781SEd Maste nchaff++; 615edf85781SEd Maste } 616edf85781SEd Maste } 617edf85781SEd Maste 618edf85781SEd Maste if (stop_reason != NULL) { 619edf85781SEd Maste if (active) { 620edf85781SEd Maste debug3_f("stopping: %s (%llu chaff packets sent)", 621edf85781SEd Maste stop_reason, nchaff); 622edf85781SEd Maste active = 0; 623edf85781SEd Maste } 624edf85781SEd Maste return 1; 625edf85781SEd Maste } 626edf85781SEd Maste 627edf85781SEd Maste /* 628edf85781SEd Maste * If we're in interactive mode, and only have a small amount 629edf85781SEd Maste * of outbound data, then we assume that the user is typing 630edf85781SEd Maste * interactively. In this case, start quantising outbound packets to 631edf85781SEd Maste * fixed time intervals to hide inter-keystroke timing. 632edf85781SEd Maste */ 633edf85781SEd Maste if (!active && ssh_packet_interactive_data_to_write(ssh) && 634edf85781SEd Maste channel_did_enqueue && ssh_packet_have_data_to_write(ssh)) { 635edf85781SEd Maste debug3_f("starting: interval ~%dms", 636edf85781SEd Maste options.obscure_keystroke_timing_interval); 637edf85781SEd Maste just_started = had_keystroke = active = 1; 638edf85781SEd Maste nchaff = 0; 639edf85781SEd Maste set_next_interval(&now, &next_interval, 640edf85781SEd Maste options.obscure_keystroke_timing_interval, 1); 641edf85781SEd Maste } 642edf85781SEd Maste 643edf85781SEd Maste /* Don't hold off if obfuscation inactive */ 644edf85781SEd Maste if (!active) 645edf85781SEd Maste return 1; 646edf85781SEd Maste 647edf85781SEd Maste if (had_keystroke) { 648edf85781SEd Maste /* 649edf85781SEd Maste * Arrange to send chaff packets for a random interval after 650edf85781SEd Maste * the last keystroke was sent. 651edf85781SEd Maste */ 652edf85781SEd Maste ms_to_timespec(&tmp, SSH_KEYSTROKE_CHAFF_MIN_MS + 653edf85781SEd Maste arc4random_uniform(SSH_KEYSTROKE_CHAFF_RNG_MS)); 654edf85781SEd Maste timespecadd(&now, &tmp, &chaff_until); 655edf85781SEd Maste } 656edf85781SEd Maste 657edf85781SEd Maste ptimeout_deadline_monotime_tsp(timeout, &next_interval); 658edf85781SEd Maste 659edf85781SEd Maste if (just_started) 660edf85781SEd Maste return 1; 661edf85781SEd Maste 662edf85781SEd Maste /* Don't arm output fd for poll until the timing interval has elapsed */ 663edf85781SEd Maste if (timespeccmp(&now, &next_interval, <)) 664edf85781SEd Maste return 0; 665edf85781SEd Maste 666edf85781SEd Maste /* Calculate number of intervals missed since the last check */ 667edf85781SEd Maste n = (now.tv_sec - next_interval.tv_sec) * 1000LL * 1000 * 1000; 668edf85781SEd Maste n += now.tv_nsec - next_interval.tv_nsec; 669edf85781SEd Maste n /= options.obscure_keystroke_timing_interval * 1000LL * 1000; 670edf85781SEd Maste n = (n < 0) ? 1 : n + 1; 671edf85781SEd Maste 672edf85781SEd Maste /* Advance to the next interval */ 673edf85781SEd Maste set_next_interval(&now, &next_interval, 674edf85781SEd Maste options.obscure_keystroke_timing_interval * n, 0); 675edf85781SEd Maste return 1; 676edf85781SEd Maste } 677edf85781SEd Maste 678511b41d2SMark Murray /* 679511b41d2SMark Murray * Waits until the client can do something (some data becomes available on 680511b41d2SMark Murray * one of the file descriptors). 681511b41d2SMark Murray */ 682ae1f160dSDag-Erling Smørgrav static void 6831323ec57SEd Maste client_wait_until_can_do_something(struct ssh *ssh, struct pollfd **pfdp, 684edf85781SEd Maste u_int *npfd_allocp, u_int *npfd_activep, int channel_did_enqueue, 685069ac184SEd Maste sigset_t *sigsetp, int *conn_in_readyp, int *conn_out_readyp) 686511b41d2SMark Murray { 687f374ba41SEd Maste struct timespec timeout; 688edf85781SEd Maste int ret, oready; 6891323ec57SEd Maste u_int p; 690efcad6b7SDag-Erling Smørgrav 6911323ec57SEd Maste *conn_in_readyp = *conn_out_readyp = 0; 692511b41d2SMark Murray 6931323ec57SEd Maste /* Prepare channel poll. First two pollfd entries are reserved */ 694f374ba41SEd Maste ptimeout_init(&timeout); 695f374ba41SEd Maste channel_prepare_poll(ssh, pfdp, npfd_allocp, npfd_activep, 2, &timeout); 6961323ec57SEd Maste if (*npfd_activep < 2) 6971323ec57SEd Maste fatal_f("bad npfd %u", *npfd_activep); /* shouldn't happen */ 6981323ec57SEd Maste 6991323ec57SEd Maste /* channel_prepare_poll could have closed the last channel */ 7004f52dfbbSDag-Erling Smørgrav if (session_closed && !channel_still_open(ssh) && 70119261079SEd Maste !ssh_packet_have_data_to_write(ssh)) { 7021323ec57SEd Maste /* clear events since we did not call poll() */ 7031323ec57SEd Maste for (p = 0; p < *npfd_activep; p++) 7041323ec57SEd Maste (*pfdp)[p].revents = 0; 705ae1f160dSDag-Erling Smørgrav return; 7064f52dfbbSDag-Erling Smørgrav } 7074f52dfbbSDag-Erling Smørgrav 708edf85781SEd Maste oready = obfuscate_keystroke_timing(ssh, &timeout, channel_did_enqueue); 709edf85781SEd Maste 7101323ec57SEd Maste /* Monitor server connection on reserved pollfd entries */ 7111323ec57SEd Maste (*pfdp)[0].fd = connection_in; 7121323ec57SEd Maste (*pfdp)[0].events = POLLIN; 7131323ec57SEd Maste (*pfdp)[1].fd = connection_out; 714edf85781SEd Maste (*pfdp)[1].events = (oready && ssh_packet_have_data_to_write(ssh)) ? 715edf85781SEd Maste POLLOUT : 0; 716511b41d2SMark Murray 717511b41d2SMark Murray /* 718511b41d2SMark Murray * Wait for something to happen. This will suspend the process until 7191323ec57SEd Maste * some polled descriptor can be read, written, or has some other 720e2f6069cSDag-Erling Smørgrav * event pending, or a timeout expires. 721511b41d2SMark Murray */ 7224f52dfbbSDag-Erling Smørgrav set_control_persist_exit_time(ssh); 723f374ba41SEd Maste if (control_persist_exit_time > 0) 724f374ba41SEd Maste ptimeout_deadline_monotime(&timeout, control_persist_exit_time); 725f374ba41SEd Maste if (options.server_alive_interval > 0) 726f374ba41SEd Maste ptimeout_deadline_monotime(&timeout, server_alive_time); 727edf85781SEd Maste if (options.rekey_interval > 0 && !ssh_packet_is_rekeying(ssh)) { 728f374ba41SEd Maste ptimeout_deadline_sec(&timeout, 729f374ba41SEd Maste ssh_packet_get_rekey_timeout(ssh)); 730e2f6069cSDag-Erling Smørgrav } 731e2f6069cSDag-Erling Smørgrav 732069ac184SEd Maste ret = ppoll(*pfdp, *npfd_activep, ptimeout_get_tsp(&timeout), sigsetp); 7331323ec57SEd Maste 73419261079SEd Maste if (ret == -1) { 7351e8db6e2SBrian Feldman /* 7361323ec57SEd Maste * We have to clear the events because we return. 7371e8db6e2SBrian Feldman * We have to return, because the mainloop checks for the flags 7381e8db6e2SBrian Feldman * set by the signal handlers. 7391e8db6e2SBrian Feldman */ 7401323ec57SEd Maste for (p = 0; p < *npfd_activep; p++) 7411323ec57SEd Maste (*pfdp)[p].revents = 0; 742511b41d2SMark Murray if (errno == EINTR) 743511b41d2SMark Murray return; 744511b41d2SMark Murray /* Note: we might still have data in the buffers. */ 7451323ec57SEd Maste quit_message("poll: %s", strerror(errno)); 7461323ec57SEd Maste return; 7471323ec57SEd Maste } 7481323ec57SEd Maste 7491323ec57SEd Maste *conn_in_readyp = (*pfdp)[0].revents != 0; 7501323ec57SEd Maste *conn_out_readyp = (*pfdp)[1].revents != 0; 7511323ec57SEd Maste 7521323ec57SEd Maste if (options.server_alive_interval > 0 && !*conn_in_readyp && 7531323ec57SEd Maste monotime() >= server_alive_time) { 754e4a9863fSDag-Erling Smørgrav /* 7551323ec57SEd Maste * ServerAlive check is needed. We can't rely on the poll 75619261079SEd Maste * timing out since traffic on the client side such as port 75719261079SEd Maste * forwards can keep waking it up. 758e4a9863fSDag-Erling Smørgrav */ 75919261079SEd Maste server_alive_check(ssh); 760e4a9863fSDag-Erling Smørgrav } 7611323ec57SEd Maste } 762e4a9863fSDag-Erling Smørgrav 763ae1f160dSDag-Erling Smørgrav static void 764190cef3dSDag-Erling Smørgrav client_suspend_self(struct sshbuf *bin, struct sshbuf *bout, struct sshbuf *berr) 765511b41d2SMark Murray { 766511b41d2SMark Murray /* Flush stdout and stderr buffers. */ 767190cef3dSDag-Erling Smørgrav if (sshbuf_len(bout) > 0) 768190cef3dSDag-Erling Smørgrav atomicio(vwrite, fileno(stdout), sshbuf_mutable_ptr(bout), 769190cef3dSDag-Erling Smørgrav sshbuf_len(bout)); 770190cef3dSDag-Erling Smørgrav if (sshbuf_len(berr) > 0) 771190cef3dSDag-Erling Smørgrav atomicio(vwrite, fileno(stderr), sshbuf_mutable_ptr(berr), 772190cef3dSDag-Erling Smørgrav sshbuf_len(berr)); 773511b41d2SMark Murray 774e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 775511b41d2SMark Murray 7764f52dfbbSDag-Erling Smørgrav sshbuf_reset(bin); 7774f52dfbbSDag-Erling Smørgrav sshbuf_reset(bout); 7784f52dfbbSDag-Erling Smørgrav sshbuf_reset(berr); 779511b41d2SMark Murray 780511b41d2SMark Murray /* Send the suspend signal to the program itself. */ 781511b41d2SMark Murray kill(getpid(), SIGTSTP); 782511b41d2SMark Murray 7835e8dbd04SDag-Erling Smørgrav /* Reset window sizes in case they have changed */ 784511b41d2SMark Murray received_window_change_signal = 1; 785511b41d2SMark Murray 786e146993eSDag-Erling Smørgrav enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 787511b41d2SMark Murray } 788511b41d2SMark Murray 789ae1f160dSDag-Erling Smørgrav static void 7901323ec57SEd Maste client_process_net_input(struct ssh *ssh) 791511b41d2SMark Murray { 7921323ec57SEd Maste int r; 793511b41d2SMark Murray 794511b41d2SMark Murray /* 795511b41d2SMark Murray * Read input from the server, and add any such data to the buffer of 796511b41d2SMark Murray * the packet subsystem. 797511b41d2SMark Murray */ 79819261079SEd Maste schedule_server_alive_check(); 7991323ec57SEd Maste if ((r = ssh_packet_process_read(ssh, connection_in)) == 0) 8001323ec57SEd Maste return; /* success */ 8011323ec57SEd Maste if (r == SSH_ERR_SYSTEM_ERROR) { 8021323ec57SEd Maste if (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK) 8031323ec57SEd Maste return; 8041323ec57SEd Maste if (errno == EPIPE) { 8051323ec57SEd Maste quit_message("Connection to %s closed by remote host.", 8061323ec57SEd Maste host); 807511b41d2SMark Murray return; 808511b41d2SMark Murray } 809511b41d2SMark Murray } 8101323ec57SEd Maste quit_message("Read from remote host %s: %s", host, ssh_err(r)); 811a04a10f8SKris Kennaway } 812a04a10f8SKris Kennaway 813545d5ecaSDag-Erling Smørgrav static void 8144f52dfbbSDag-Erling Smørgrav client_status_confirm(struct ssh *ssh, int type, Channel *c, void *ctx) 815d74d50a8SDag-Erling Smørgrav { 816d4af9e69SDag-Erling Smørgrav struct channel_reply_ctx *cr = (struct channel_reply_ctx *)ctx; 817d4af9e69SDag-Erling Smørgrav char errmsg[256]; 818190cef3dSDag-Erling Smørgrav int r, tochan; 819d74d50a8SDag-Erling Smørgrav 820e146993eSDag-Erling Smørgrav /* 821e146993eSDag-Erling Smørgrav * If a TTY was explicitly requested, then a failure to allocate 822e146993eSDag-Erling Smørgrav * one is fatal. 823e146993eSDag-Erling Smørgrav */ 824e146993eSDag-Erling Smørgrav if (cr->action == CONFIRM_TTY && 825e146993eSDag-Erling Smørgrav (options.request_tty == REQUEST_TTY_FORCE || 826e146993eSDag-Erling Smørgrav options.request_tty == REQUEST_TTY_YES)) 827e146993eSDag-Erling Smørgrav cr->action = CONFIRM_CLOSE; 828e146993eSDag-Erling Smørgrav 829190cef3dSDag-Erling Smørgrav /* XXX suppress on mux _client_ quietmode */ 830d4af9e69SDag-Erling Smørgrav tochan = options.log_level >= SYSLOG_LEVEL_ERROR && 831b15c8340SDag-Erling Smørgrav c->ctl_chan != -1 && c->extended_usage == CHAN_EXTENDED_WRITE; 832d74d50a8SDag-Erling Smørgrav 833d4af9e69SDag-Erling Smørgrav if (type == SSH2_MSG_CHANNEL_SUCCESS) { 834d4af9e69SDag-Erling Smørgrav debug2("%s request accepted on channel %d", 835d4af9e69SDag-Erling Smørgrav cr->request_type, c->self); 836d4af9e69SDag-Erling Smørgrav } else if (type == SSH2_MSG_CHANNEL_FAILURE) { 837d4af9e69SDag-Erling Smørgrav if (tochan) { 838d4af9e69SDag-Erling Smørgrav snprintf(errmsg, sizeof(errmsg), 839d4af9e69SDag-Erling Smørgrav "%s request failed\r\n", cr->request_type); 840d4af9e69SDag-Erling Smørgrav } else { 841d4af9e69SDag-Erling Smørgrav snprintf(errmsg, sizeof(errmsg), 842d4af9e69SDag-Erling Smørgrav "%s request failed on channel %d", 843d4af9e69SDag-Erling Smørgrav cr->request_type, c->self); 844d74d50a8SDag-Erling Smørgrav } 845d4af9e69SDag-Erling Smørgrav /* If error occurred on primary session channel, then exit */ 846e146993eSDag-Erling Smørgrav if (cr->action == CONFIRM_CLOSE && c->self == session_ident) 847d4af9e69SDag-Erling Smørgrav fatal("%s", errmsg); 848e146993eSDag-Erling Smørgrav /* 849e146993eSDag-Erling Smørgrav * If error occurred on mux client, append to 850e146993eSDag-Erling Smørgrav * their stderr. 851e146993eSDag-Erling Smørgrav */ 852e146993eSDag-Erling Smørgrav if (tochan) { 85319261079SEd Maste debug3_f("channel %d: mux request: %s", c->self, 85419261079SEd Maste cr->request_type); 855190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put(c->extended, errmsg, 856190cef3dSDag-Erling Smørgrav strlen(errmsg))) != 0) 85719261079SEd Maste fatal_fr(r, "sshbuf_put"); 858e146993eSDag-Erling Smørgrav } else 859d4af9e69SDag-Erling Smørgrav error("%s", errmsg); 860e146993eSDag-Erling Smørgrav if (cr->action == CONFIRM_TTY) { 861e146993eSDag-Erling Smørgrav /* 862e146993eSDag-Erling Smørgrav * If a TTY allocation error occurred, then arrange 863e146993eSDag-Erling Smørgrav * for the correct TTY to leave raw mode. 864e146993eSDag-Erling Smørgrav */ 865e146993eSDag-Erling Smørgrav if (c->self == session_ident) 866e146993eSDag-Erling Smørgrav leave_raw_mode(0); 867e146993eSDag-Erling Smørgrav else 8684f52dfbbSDag-Erling Smørgrav mux_tty_alloc_failed(ssh, c); 869e146993eSDag-Erling Smørgrav } else if (cr->action == CONFIRM_CLOSE) { 8704f52dfbbSDag-Erling Smørgrav chan_read_failed(ssh, c); 8714f52dfbbSDag-Erling Smørgrav chan_write_failed(ssh, c); 872d74d50a8SDag-Erling Smørgrav } 873d74d50a8SDag-Erling Smørgrav } 874e4a9863fSDag-Erling Smørgrav free(cr); 875d4af9e69SDag-Erling Smørgrav } 876d74d50a8SDag-Erling Smørgrav 877d74d50a8SDag-Erling Smørgrav static void 8784f52dfbbSDag-Erling Smørgrav client_abandon_status_confirm(struct ssh *ssh, Channel *c, void *ctx) 879d74d50a8SDag-Erling Smørgrav { 880e4a9863fSDag-Erling Smørgrav free(ctx); 881d74d50a8SDag-Erling Smørgrav } 882d74d50a8SDag-Erling Smørgrav 883e146993eSDag-Erling Smørgrav void 8844f52dfbbSDag-Erling Smørgrav client_expect_confirm(struct ssh *ssh, int id, const char *request, 885e146993eSDag-Erling Smørgrav enum confirm_action action) 886d74d50a8SDag-Erling Smørgrav { 8870a37d4a3SXin LI struct channel_reply_ctx *cr = xcalloc(1, sizeof(*cr)); 888d74d50a8SDag-Erling Smørgrav 889d4af9e69SDag-Erling Smørgrav cr->request_type = request; 890e146993eSDag-Erling Smørgrav cr->action = action; 891d74d50a8SDag-Erling Smørgrav 8924f52dfbbSDag-Erling Smørgrav channel_register_status_confirm(ssh, id, client_status_confirm, 893d4af9e69SDag-Erling Smørgrav client_abandon_status_confirm, cr); 894d4af9e69SDag-Erling Smørgrav } 895d4af9e69SDag-Erling Smørgrav 896d4af9e69SDag-Erling Smørgrav void 897d4af9e69SDag-Erling Smørgrav client_register_global_confirm(global_confirm_cb *cb, void *ctx) 898d4af9e69SDag-Erling Smørgrav { 899d4af9e69SDag-Erling Smørgrav struct global_confirm *gc, *last_gc; 900d4af9e69SDag-Erling Smørgrav 901d4af9e69SDag-Erling Smørgrav /* Coalesce identical callbacks */ 902d4af9e69SDag-Erling Smørgrav last_gc = TAILQ_LAST(&global_confirms, global_confirms); 903d4af9e69SDag-Erling Smørgrav if (last_gc && last_gc->cb == cb && last_gc->ctx == ctx) { 904d4af9e69SDag-Erling Smørgrav if (++last_gc->ref_count >= INT_MAX) 90519261079SEd Maste fatal_f("last_gc->ref_count = %d", 90619261079SEd Maste last_gc->ref_count); 907d74d50a8SDag-Erling Smørgrav return; 908d74d50a8SDag-Erling Smørgrav } 909d74d50a8SDag-Erling Smørgrav 9100a37d4a3SXin LI gc = xcalloc(1, sizeof(*gc)); 911d4af9e69SDag-Erling Smørgrav gc->cb = cb; 912d4af9e69SDag-Erling Smørgrav gc->ctx = ctx; 913d4af9e69SDag-Erling Smørgrav gc->ref_count = 1; 914d4af9e69SDag-Erling Smørgrav TAILQ_INSERT_TAIL(&global_confirms, gc, entry); 915d74d50a8SDag-Erling Smørgrav } 916d74d50a8SDag-Erling Smørgrav 917f374ba41SEd Maste /* 918f374ba41SEd Maste * Returns non-zero if the client is able to handle a hostkeys-00@openssh.com 919f374ba41SEd Maste * hostkey update request. 920f374ba41SEd Maste */ 921f374ba41SEd Maste static int 922f374ba41SEd Maste can_update_hostkeys(void) 923f374ba41SEd Maste { 924f374ba41SEd Maste if (hostkeys_update_complete) 925f374ba41SEd Maste return 0; 926f374ba41SEd Maste if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK && 927f374ba41SEd Maste options.batch_mode) 928f374ba41SEd Maste return 0; /* won't ask in batchmode, so don't even try */ 929f374ba41SEd Maste if (!options.update_hostkeys || options.num_user_hostfiles <= 0) 930f374ba41SEd Maste return 0; 931f374ba41SEd Maste return 1; 932f374ba41SEd Maste } 933f374ba41SEd Maste 934f374ba41SEd Maste static void 935f374ba41SEd Maste client_repledge(void) 936f374ba41SEd Maste { 937f374ba41SEd Maste debug3_f("enter"); 938f374ba41SEd Maste 939f374ba41SEd Maste /* Might be able to tighten pledge now that session is established */ 940f374ba41SEd Maste if (options.control_master || options.control_path != NULL || 941f374ba41SEd Maste options.forward_x11 || options.fork_after_authentication || 942f374ba41SEd Maste can_update_hostkeys() || 943f374ba41SEd Maste (session_ident != -1 && !session_setup_complete)) { 944f374ba41SEd Maste /* Can't tighten */ 945f374ba41SEd Maste return; 946f374ba41SEd Maste } 947f374ba41SEd Maste /* 948f374ba41SEd Maste * LocalCommand and UpdateHostkeys have finished, so can get rid of 949f374ba41SEd Maste * filesystem. 950f374ba41SEd Maste * 951f374ba41SEd Maste * XXX protocol allows a server can to change hostkeys during the 952f374ba41SEd Maste * connection at rekey time that could trigger a hostkeys update 953f374ba41SEd Maste * but AFAIK no implementations support this. Could improve by 954f374ba41SEd Maste * forcing known_hosts to be read-only or via unveil(2). 955f374ba41SEd Maste */ 956f374ba41SEd Maste if (options.num_local_forwards != 0 || 957f374ba41SEd Maste options.num_remote_forwards != 0 || 958f374ba41SEd Maste options.num_permitted_remote_opens != 0 || 959f374ba41SEd Maste options.enable_escape_commandline != 0) { 960f374ba41SEd Maste /* rfwd needs inet */ 961f374ba41SEd Maste debug("pledge: network"); 962f374ba41SEd Maste if (pledge("stdio unix inet dns proc tty", NULL) == -1) 963f374ba41SEd Maste fatal_f("pledge(): %s", strerror(errno)); 964f374ba41SEd Maste } else if (options.forward_agent != 0) { 965f374ba41SEd Maste /* agent forwarding needs to open $SSH_AUTH_SOCK at will */ 966f374ba41SEd Maste debug("pledge: agent"); 967f374ba41SEd Maste if (pledge("stdio unix proc tty", NULL) == -1) 968f374ba41SEd Maste fatal_f("pledge(): %s", strerror(errno)); 969f374ba41SEd Maste } else { 970f374ba41SEd Maste debug("pledge: fork"); 971f374ba41SEd Maste if (pledge("stdio proc tty", NULL) == -1) 972f374ba41SEd Maste fatal_f("pledge(): %s", strerror(errno)); 973f374ba41SEd Maste } 974f374ba41SEd Maste /* XXX further things to do: 975f374ba41SEd Maste * 976f374ba41SEd Maste * - might be able to get rid of proc if we kill ~^Z 977f374ba41SEd Maste * - ssh -N (no session) 978f374ba41SEd Maste * - stdio forwarding 979f374ba41SEd Maste * - sessions without tty 980f374ba41SEd Maste */ 981f374ba41SEd Maste } 982f374ba41SEd Maste 983d74d50a8SDag-Erling Smørgrav static void 9844f52dfbbSDag-Erling Smørgrav process_cmdline(struct ssh *ssh) 985545d5ecaSDag-Erling Smørgrav { 986545d5ecaSDag-Erling Smørgrav void (*handler)(int); 987a0ee8cc6SDag-Erling Smørgrav char *s, *cmd; 988a0ee8cc6SDag-Erling Smørgrav int ok, delete = 0, local = 0, remote = 0, dynamic = 0; 989a0ee8cc6SDag-Erling Smørgrav struct Forward fwd; 990545d5ecaSDag-Erling Smørgrav 991b83788ffSDag-Erling Smørgrav memset(&fwd, 0, sizeof(fwd)); 992d4af9e69SDag-Erling Smørgrav 993e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 99419261079SEd Maste handler = ssh_signal(SIGINT, SIG_IGN); 995545d5ecaSDag-Erling Smørgrav cmd = s = read_passphrase("\r\nssh> ", RP_ECHO); 996545d5ecaSDag-Erling Smørgrav if (s == NULL) 997545d5ecaSDag-Erling Smørgrav goto out; 998f7167e0eSDag-Erling Smørgrav while (isspace((u_char)*s)) 999545d5ecaSDag-Erling Smørgrav s++; 1000d74d50a8SDag-Erling Smørgrav if (*s == '-') 1001d74d50a8SDag-Erling Smørgrav s++; /* Skip cmdline '-', if any */ 1002d74d50a8SDag-Erling Smørgrav if (*s == '\0') 1003545d5ecaSDag-Erling Smørgrav goto out; 1004d74d50a8SDag-Erling Smørgrav 1005d74d50a8SDag-Erling Smørgrav if (*s == 'h' || *s == 'H' || *s == '?') { 1006d74d50a8SDag-Erling Smørgrav logit("Commands:"); 1007761efaa7SDag-Erling Smørgrav logit(" -L[bind_address:]port:host:hostport " 1008761efaa7SDag-Erling Smørgrav "Request local forward"); 1009761efaa7SDag-Erling Smørgrav logit(" -R[bind_address:]port:host:hostport " 1010761efaa7SDag-Erling Smørgrav "Request remote forward"); 1011cce7d346SDag-Erling Smørgrav logit(" -D[bind_address:]port " 1012cce7d346SDag-Erling Smørgrav "Request dynamic forward"); 1013462c32cbSDag-Erling Smørgrav logit(" -KL[bind_address:]port " 1014462c32cbSDag-Erling Smørgrav "Cancel local forward"); 1015761efaa7SDag-Erling Smørgrav logit(" -KR[bind_address:]port " 1016761efaa7SDag-Erling Smørgrav "Cancel remote forward"); 1017462c32cbSDag-Erling Smørgrav logit(" -KD[bind_address:]port " 1018462c32cbSDag-Erling Smørgrav "Cancel dynamic forward"); 1019021d409fSDag-Erling Smørgrav if (!options.permit_local_command) 1020021d409fSDag-Erling Smørgrav goto out; 1021761efaa7SDag-Erling Smørgrav logit(" !args " 1022761efaa7SDag-Erling Smørgrav "Execute local command"); 1023021d409fSDag-Erling Smørgrav goto out; 1024021d409fSDag-Erling Smørgrav } 1025021d409fSDag-Erling Smørgrav 1026021d409fSDag-Erling Smørgrav if (*s == '!' && options.permit_local_command) { 1027021d409fSDag-Erling Smørgrav s++; 1028021d409fSDag-Erling Smørgrav ssh_local_cmd(s); 1029d74d50a8SDag-Erling Smørgrav goto out; 1030d74d50a8SDag-Erling Smørgrav } 1031d74d50a8SDag-Erling Smørgrav 1032d74d50a8SDag-Erling Smørgrav if (*s == 'K') { 1033d74d50a8SDag-Erling Smørgrav delete = 1; 1034d74d50a8SDag-Erling Smørgrav s++; 1035d74d50a8SDag-Erling Smørgrav } 1036cce7d346SDag-Erling Smørgrav if (*s == 'L') 1037cce7d346SDag-Erling Smørgrav local = 1; 1038cce7d346SDag-Erling Smørgrav else if (*s == 'R') 1039cce7d346SDag-Erling Smørgrav remote = 1; 1040cce7d346SDag-Erling Smørgrav else if (*s == 'D') 1041cce7d346SDag-Erling Smørgrav dynamic = 1; 1042cce7d346SDag-Erling Smørgrav else { 1043d95e11bfSDag-Erling Smørgrav logit("Invalid command."); 1044545d5ecaSDag-Erling Smørgrav goto out; 1045545d5ecaSDag-Erling Smørgrav } 1046cce7d346SDag-Erling Smørgrav 1047f7167e0eSDag-Erling Smørgrav while (isspace((u_char)*++s)) 1048d4af9e69SDag-Erling Smørgrav ; 1049545d5ecaSDag-Erling Smørgrav 1050b15c8340SDag-Erling Smørgrav /* XXX update list of forwards in options */ 1051d74d50a8SDag-Erling Smørgrav if (delete) { 1052a0ee8cc6SDag-Erling Smørgrav /* We pass 1 for dynamicfwd to restrict to 1 or 2 fields. */ 1053a0ee8cc6SDag-Erling Smørgrav if (!parse_forward(&fwd, s, 1, 0)) { 1054a0ee8cc6SDag-Erling Smørgrav logit("Bad forwarding close specification."); 1055545d5ecaSDag-Erling Smørgrav goto out; 1056545d5ecaSDag-Erling Smørgrav } 1057462c32cbSDag-Erling Smørgrav if (remote) 10584f52dfbbSDag-Erling Smørgrav ok = channel_request_rforward_cancel(ssh, &fwd) == 0; 1059462c32cbSDag-Erling Smørgrav else if (dynamic) 10604f52dfbbSDag-Erling Smørgrav ok = channel_cancel_lport_listener(ssh, &fwd, 1061a0ee8cc6SDag-Erling Smørgrav 0, &options.fwd_opts) > 0; 1062462c32cbSDag-Erling Smørgrav else 10634f52dfbbSDag-Erling Smørgrav ok = channel_cancel_lport_listener(ssh, &fwd, 1064a0ee8cc6SDag-Erling Smørgrav CHANNEL_CANCEL_PORT_STATIC, 1065a0ee8cc6SDag-Erling Smørgrav &options.fwd_opts) > 0; 1066462c32cbSDag-Erling Smørgrav if (!ok) { 1067d93a896eSDag-Erling Smørgrav logit("Unknown port forwarding."); 1068462c32cbSDag-Erling Smørgrav goto out; 1069462c32cbSDag-Erling Smørgrav } 1070462c32cbSDag-Erling Smørgrav logit("Canceled forwarding."); 10715e8dbd04SDag-Erling Smørgrav } else { 1072f374ba41SEd Maste /* -R specs can be both dynamic or not, so check both. */ 1073f374ba41SEd Maste if (remote) { 1074f374ba41SEd Maste if (!parse_forward(&fwd, s, 0, remote) && 1075f374ba41SEd Maste !parse_forward(&fwd, s, 1, remote)) { 1076f374ba41SEd Maste logit("Bad remote forwarding specification."); 1077f374ba41SEd Maste goto out; 1078f374ba41SEd Maste } 1079f374ba41SEd Maste } else if (!parse_forward(&fwd, s, dynamic, remote)) { 1080f374ba41SEd Maste logit("Bad local forwarding specification."); 1081545d5ecaSDag-Erling Smørgrav goto out; 1082545d5ecaSDag-Erling Smørgrav } 1083cce7d346SDag-Erling Smørgrav if (local || dynamic) { 10844f52dfbbSDag-Erling Smørgrav if (!channel_setup_local_fwd_listener(ssh, &fwd, 1085a0ee8cc6SDag-Erling Smørgrav &options.fwd_opts)) { 1086d95e11bfSDag-Erling Smørgrav logit("Port forwarding failed."); 1087545d5ecaSDag-Erling Smørgrav goto out; 1088545d5ecaSDag-Erling Smørgrav } 10895e8dbd04SDag-Erling Smørgrav } else { 10904f52dfbbSDag-Erling Smørgrav if (channel_request_remote_forwarding(ssh, &fwd) < 0) { 1091761efaa7SDag-Erling Smørgrav logit("Port forwarding failed."); 1092761efaa7SDag-Erling Smørgrav goto out; 1093761efaa7SDag-Erling Smørgrav } 10945e8dbd04SDag-Erling Smørgrav } 1095d95e11bfSDag-Erling Smørgrav logit("Forwarding port."); 1096d74d50a8SDag-Erling Smørgrav } 1097d74d50a8SDag-Erling Smørgrav 1098545d5ecaSDag-Erling Smørgrav out: 109919261079SEd Maste ssh_signal(SIGINT, handler); 1100e146993eSDag-Erling Smørgrav enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 1101e4a9863fSDag-Erling Smørgrav free(cmd); 1102e4a9863fSDag-Erling Smørgrav free(fwd.listen_host); 1103a0ee8cc6SDag-Erling Smørgrav free(fwd.listen_path); 1104e4a9863fSDag-Erling Smørgrav free(fwd.connect_host); 1105a0ee8cc6SDag-Erling Smørgrav free(fwd.connect_path); 1106545d5ecaSDag-Erling Smørgrav } 1107545d5ecaSDag-Erling Smørgrav 11086888a9beSDag-Erling Smørgrav /* reasons to suppress output of an escape command in help output */ 11096888a9beSDag-Erling Smørgrav #define SUPPRESS_NEVER 0 /* never suppress, always show */ 11104f52dfbbSDag-Erling Smørgrav #define SUPPRESS_MUXCLIENT 1 /* don't show in mux client sessions */ 11114f52dfbbSDag-Erling Smørgrav #define SUPPRESS_MUXMASTER 2 /* don't show in mux master sessions */ 11124f52dfbbSDag-Erling Smørgrav #define SUPPRESS_SYSLOG 4 /* don't show when logging to syslog */ 1113f374ba41SEd Maste #define SUPPRESS_NOCMDLINE 8 /* don't show when cmdline disabled*/ 11146888a9beSDag-Erling Smørgrav struct escape_help_text { 11156888a9beSDag-Erling Smørgrav const char *cmd; 11166888a9beSDag-Erling Smørgrav const char *text; 11176888a9beSDag-Erling Smørgrav unsigned int flags; 11186888a9beSDag-Erling Smørgrav }; 11196888a9beSDag-Erling Smørgrav static struct escape_help_text esc_txt[] = { 11206888a9beSDag-Erling Smørgrav {".", "terminate session", SUPPRESS_MUXMASTER}, 11216888a9beSDag-Erling Smørgrav {".", "terminate connection (and any multiplexed sessions)", 11226888a9beSDag-Erling Smørgrav SUPPRESS_MUXCLIENT}, 11234f52dfbbSDag-Erling Smørgrav {"B", "send a BREAK to the remote system", SUPPRESS_NEVER}, 1124f374ba41SEd Maste {"C", "open a command line", SUPPRESS_MUXCLIENT|SUPPRESS_NOCMDLINE}, 11254f52dfbbSDag-Erling Smørgrav {"R", "request rekey", SUPPRESS_NEVER}, 11266888a9beSDag-Erling Smørgrav {"V/v", "decrease/increase verbosity (LogLevel)", SUPPRESS_MUXCLIENT}, 11276888a9beSDag-Erling Smørgrav {"^Z", "suspend ssh", SUPPRESS_MUXCLIENT}, 11286888a9beSDag-Erling Smørgrav {"#", "list forwarded connections", SUPPRESS_NEVER}, 11296888a9beSDag-Erling Smørgrav {"&", "background ssh (when waiting for connections to terminate)", 11306888a9beSDag-Erling Smørgrav SUPPRESS_MUXCLIENT}, 11316888a9beSDag-Erling Smørgrav {"?", "this message", SUPPRESS_NEVER}, 11326888a9beSDag-Erling Smørgrav }; 11336888a9beSDag-Erling Smørgrav 11346888a9beSDag-Erling Smørgrav static void 1135190cef3dSDag-Erling Smørgrav print_escape_help(struct sshbuf *b, int escape_char, int mux_client, 1136190cef3dSDag-Erling Smørgrav int using_stderr) 11376888a9beSDag-Erling Smørgrav { 11386888a9beSDag-Erling Smørgrav unsigned int i, suppress_flags; 1139190cef3dSDag-Erling Smørgrav int r; 11406888a9beSDag-Erling Smørgrav 1141190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(b, 1142190cef3dSDag-Erling Smørgrav "%c?\r\nSupported escape sequences:\r\n", escape_char)) != 0) 114319261079SEd Maste fatal_fr(r, "sshbuf_putf"); 11446888a9beSDag-Erling Smørgrav 11454f52dfbbSDag-Erling Smørgrav suppress_flags = 11466888a9beSDag-Erling Smørgrav (mux_client ? SUPPRESS_MUXCLIENT : 0) | 11476888a9beSDag-Erling Smørgrav (mux_client ? 0 : SUPPRESS_MUXMASTER) | 1148f374ba41SEd Maste (using_stderr ? 0 : SUPPRESS_SYSLOG) | 1149f374ba41SEd Maste (options.enable_escape_commandline == 0 ? SUPPRESS_NOCMDLINE : 0); 11506888a9beSDag-Erling Smørgrav 11516888a9beSDag-Erling Smørgrav for (i = 0; i < sizeof(esc_txt)/sizeof(esc_txt[0]); i++) { 11526888a9beSDag-Erling Smørgrav if (esc_txt[i].flags & suppress_flags) 11536888a9beSDag-Erling Smørgrav continue; 1154190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(b, " %c%-3s - %s\r\n", 1155190cef3dSDag-Erling Smørgrav escape_char, esc_txt[i].cmd, esc_txt[i].text)) != 0) 115619261079SEd Maste fatal_fr(r, "sshbuf_putf"); 11576888a9beSDag-Erling Smørgrav } 11586888a9beSDag-Erling Smørgrav 1159190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(b, 11606888a9beSDag-Erling Smørgrav " %c%c - send the escape character by typing it twice\r\n" 11616888a9beSDag-Erling Smørgrav "(Note that escapes are only recognized immediately after " 1162190cef3dSDag-Erling Smørgrav "newline.)\r\n", escape_char, escape_char)) != 0) 116319261079SEd Maste fatal_fr(r, "sshbuf_putf"); 11646888a9beSDag-Erling Smørgrav } 11656888a9beSDag-Erling Smørgrav 1166d4af9e69SDag-Erling Smørgrav /* 11674f52dfbbSDag-Erling Smørgrav * Process the characters one by one. 1168d4af9e69SDag-Erling Smørgrav */ 1169ae1f160dSDag-Erling Smørgrav static int 11704f52dfbbSDag-Erling Smørgrav process_escapes(struct ssh *ssh, Channel *c, 1171190cef3dSDag-Erling Smørgrav struct sshbuf *bin, struct sshbuf *bout, struct sshbuf *berr, 1172d4af9e69SDag-Erling Smørgrav char *buf, int len) 1173b66f2d16SKris Kennaway { 1174b66f2d16SKris Kennaway pid_t pid; 1175190cef3dSDag-Erling Smørgrav int r, bytes = 0; 11761e8db6e2SBrian Feldman u_int i; 11771e8db6e2SBrian Feldman u_char ch; 1178b66f2d16SKris Kennaway char *s; 1179535af610SEd Maste struct escape_filter_ctx *efc; 1180d4af9e69SDag-Erling Smørgrav 1181535af610SEd Maste if (c == NULL || c->filter_ctx == NULL || len <= 0) 1182d4af9e69SDag-Erling Smørgrav return 0; 1183b66f2d16SKris Kennaway 1184535af610SEd Maste efc = (struct escape_filter_ctx *)c->filter_ctx; 1185043840dfSDag-Erling Smørgrav 1186043840dfSDag-Erling Smørgrav for (i = 0; i < (u_int)len; i++) { 1187b66f2d16SKris Kennaway /* Get one character at a time. */ 1188b66f2d16SKris Kennaway ch = buf[i]; 1189b66f2d16SKris Kennaway 11904f52dfbbSDag-Erling Smørgrav if (efc->escape_pending) { 1191b66f2d16SKris Kennaway /* We have previously seen an escape character. */ 1192b66f2d16SKris Kennaway /* Clear the flag now. */ 11934f52dfbbSDag-Erling Smørgrav efc->escape_pending = 0; 1194b66f2d16SKris Kennaway 1195b66f2d16SKris Kennaway /* Process the escaped character. */ 1196b66f2d16SKris Kennaway switch (ch) { 1197b66f2d16SKris Kennaway case '.': 1198b66f2d16SKris Kennaway /* Terminate the connection. */ 1199190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(berr, "%c.\r\n", 1200190cef3dSDag-Erling Smørgrav efc->escape_char)) != 0) 120119261079SEd Maste fatal_fr(r, "sshbuf_putf"); 1202b15c8340SDag-Erling Smørgrav if (c && c->ctl_chan != -1) { 1203f374ba41SEd Maste channel_force_close(ssh, c, 1); 1204d4af9e69SDag-Erling Smørgrav return 0; 1205d4af9e69SDag-Erling Smørgrav } else 1206b66f2d16SKris Kennaway quit_pending = 1; 1207b66f2d16SKris Kennaway return -1; 1208b66f2d16SKris Kennaway 1209b66f2d16SKris Kennaway case 'Z' - 64: 1210d4af9e69SDag-Erling Smørgrav /* XXX support this for mux clients */ 1211b15c8340SDag-Erling Smørgrav if (c && c->ctl_chan != -1) { 12126888a9beSDag-Erling Smørgrav char b[16]; 1213d4af9e69SDag-Erling Smørgrav noescape: 12146888a9beSDag-Erling Smørgrav if (ch == 'Z' - 64) 12156888a9beSDag-Erling Smørgrav snprintf(b, sizeof b, "^Z"); 12166888a9beSDag-Erling Smørgrav else 12176888a9beSDag-Erling Smørgrav snprintf(b, sizeof b, "%c", ch); 1218190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(berr, 12196888a9beSDag-Erling Smørgrav "%c%s escape not available to " 1220d4af9e69SDag-Erling Smørgrav "multiplexed sessions\r\n", 1221190cef3dSDag-Erling Smørgrav efc->escape_char, b)) != 0) 122219261079SEd Maste fatal_fr(r, "sshbuf_putf"); 1223d4af9e69SDag-Erling Smørgrav continue; 1224d4af9e69SDag-Erling Smørgrav } 1225d4af9e69SDag-Erling Smørgrav /* Suspend the program. Inform the user */ 1226190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(berr, 1227190cef3dSDag-Erling Smørgrav "%c^Z [suspend ssh]\r\n", 1228190cef3dSDag-Erling Smørgrav efc->escape_char)) != 0) 122919261079SEd Maste fatal_fr(r, "sshbuf_putf"); 1230b66f2d16SKris Kennaway 1231b66f2d16SKris Kennaway /* Restore terminal modes and suspend. */ 1232b66f2d16SKris Kennaway client_suspend_self(bin, bout, berr); 1233b66f2d16SKris Kennaway 1234b66f2d16SKris Kennaway /* We have been continued. */ 1235b66f2d16SKris Kennaway continue; 1236b66f2d16SKris Kennaway 1237d95e11bfSDag-Erling Smørgrav case 'B': 1238190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(berr, 1239190cef3dSDag-Erling Smørgrav "%cB\r\n", efc->escape_char)) != 0) 124019261079SEd Maste fatal_fr(r, "sshbuf_putf"); 12414f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, c->self, "break", 0); 1242190cef3dSDag-Erling Smørgrav if ((r = sshpkt_put_u32(ssh, 1000)) != 0 || 1243190cef3dSDag-Erling Smørgrav (r = sshpkt_send(ssh)) != 0) 124419261079SEd Maste fatal_fr(r, "send packet"); 1245d95e11bfSDag-Erling Smørgrav continue; 1246d95e11bfSDag-Erling Smørgrav 12471e8db6e2SBrian Feldman case 'R': 124819261079SEd Maste if (ssh->compat & SSH_BUG_NOREKEY) 1249d4af9e69SDag-Erling Smørgrav logit("Server does not " 1250d4af9e69SDag-Erling Smørgrav "support re-keying"); 12511e8db6e2SBrian Feldman else 12521e8db6e2SBrian Feldman need_rekeying = 1; 12531e8db6e2SBrian Feldman continue; 12541e8db6e2SBrian Feldman 12556888a9beSDag-Erling Smørgrav case 'V': 12566888a9beSDag-Erling Smørgrav /* FALLTHROUGH */ 12576888a9beSDag-Erling Smørgrav case 'v': 12586888a9beSDag-Erling Smørgrav if (c && c->ctl_chan != -1) 12596888a9beSDag-Erling Smørgrav goto noescape; 12606888a9beSDag-Erling Smørgrav if (!log_is_on_stderr()) { 1261190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(berr, 12626888a9beSDag-Erling Smørgrav "%c%c [Logging to syslog]\r\n", 1263190cef3dSDag-Erling Smørgrav efc->escape_char, ch)) != 0) 126419261079SEd Maste fatal_fr(r, "sshbuf_putf"); 12656888a9beSDag-Erling Smørgrav continue; 12666888a9beSDag-Erling Smørgrav } 12676888a9beSDag-Erling Smørgrav if (ch == 'V' && options.log_level > 12686888a9beSDag-Erling Smørgrav SYSLOG_LEVEL_QUIET) 12696888a9beSDag-Erling Smørgrav log_change_level(--options.log_level); 12706888a9beSDag-Erling Smørgrav if (ch == 'v' && options.log_level < 12716888a9beSDag-Erling Smørgrav SYSLOG_LEVEL_DEBUG3) 12726888a9beSDag-Erling Smørgrav log_change_level(++options.log_level); 1273190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(berr, 12744f52dfbbSDag-Erling Smørgrav "%c%c [LogLevel %s]\r\n", 12754f52dfbbSDag-Erling Smørgrav efc->escape_char, ch, 1276190cef3dSDag-Erling Smørgrav log_level_name(options.log_level))) != 0) 127719261079SEd Maste fatal_fr(r, "sshbuf_putf"); 12786888a9beSDag-Erling Smørgrav continue; 12796888a9beSDag-Erling Smørgrav 1280b66f2d16SKris Kennaway case '&': 1281535af610SEd Maste if (c->ctl_chan != -1) 1282d4af9e69SDag-Erling Smørgrav goto noescape; 1283b66f2d16SKris Kennaway /* 1284d4af9e69SDag-Erling Smørgrav * Detach the program (continue to serve 1285d4af9e69SDag-Erling Smørgrav * connections, but put in background and no 1286d4af9e69SDag-Erling Smørgrav * more new connections). 1287b66f2d16SKris Kennaway */ 1288ae1f160dSDag-Erling Smørgrav /* Restore tty modes. */ 1289e146993eSDag-Erling Smørgrav leave_raw_mode( 1290e146993eSDag-Erling Smørgrav options.request_tty == REQUEST_TTY_FORCE); 1291ae1f160dSDag-Erling Smørgrav 1292ae1f160dSDag-Erling Smørgrav /* Stop listening for new connections. */ 12934f52dfbbSDag-Erling Smørgrav channel_stop_listening(ssh); 1294ae1f160dSDag-Erling Smørgrav 129519261079SEd Maste if ((r = sshbuf_putf(berr, "%c& " 129619261079SEd Maste "[backgrounded]\n", efc->escape_char)) != 0) 129719261079SEd Maste fatal_fr(r, "sshbuf_putf"); 1298ae1f160dSDag-Erling Smørgrav 1299ae1f160dSDag-Erling Smørgrav /* Fork into background. */ 1300ae1f160dSDag-Erling Smørgrav pid = fork(); 130119261079SEd Maste if (pid == -1) { 1302ae1f160dSDag-Erling Smørgrav error("fork: %.100s", strerror(errno)); 1303ae1f160dSDag-Erling Smørgrav continue; 1304ae1f160dSDag-Erling Smørgrav } 1305ae1f160dSDag-Erling Smørgrav if (pid != 0) { /* This is the parent. */ 1306ae1f160dSDag-Erling Smørgrav /* The parent just exits. */ 1307ae1f160dSDag-Erling Smørgrav exit(0); 1308ae1f160dSDag-Erling Smørgrav } 1309ae1f160dSDag-Erling Smørgrav /* The child continues serving connections. */ 1310ae1f160dSDag-Erling Smørgrav /* fake EOF on stdin */ 1311190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u8(bin, 4)) != 0) 131219261079SEd Maste fatal_fr(r, "sshbuf_put_u8"); 1313ae1f160dSDag-Erling Smørgrav return -1; 1314b66f2d16SKris Kennaway case '?': 13154f52dfbbSDag-Erling Smørgrav print_escape_help(berr, efc->escape_char, 13166888a9beSDag-Erling Smørgrav (c && c->ctl_chan != -1), 13176888a9beSDag-Erling Smørgrav log_is_on_stderr()); 1318b66f2d16SKris Kennaway continue; 1319b66f2d16SKris Kennaway 1320b66f2d16SKris Kennaway case '#': 1321190cef3dSDag-Erling Smørgrav if ((r = sshbuf_putf(berr, "%c#\r\n", 1322190cef3dSDag-Erling Smørgrav efc->escape_char)) != 0) 132319261079SEd Maste fatal_fr(r, "sshbuf_putf"); 13244f52dfbbSDag-Erling Smørgrav s = channel_open_message(ssh); 1325190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put(berr, s, strlen(s))) != 0) 132619261079SEd Maste fatal_fr(r, "sshbuf_put"); 1327e4a9863fSDag-Erling Smørgrav free(s); 1328b66f2d16SKris Kennaway continue; 1329b66f2d16SKris Kennaway 1330545d5ecaSDag-Erling Smørgrav case 'C': 1331b15c8340SDag-Erling Smørgrav if (c && c->ctl_chan != -1) 1332cce7d346SDag-Erling Smørgrav goto noescape; 1333f374ba41SEd Maste if (options.enable_escape_commandline == 0) { 1334f374ba41SEd Maste if ((r = sshbuf_putf(berr, 1335f374ba41SEd Maste "commandline disabled\r\n")) != 0) 1336f374ba41SEd Maste fatal_fr(r, "sshbuf_putf"); 1337f374ba41SEd Maste continue; 1338f374ba41SEd Maste } 13394f52dfbbSDag-Erling Smørgrav process_cmdline(ssh); 1340545d5ecaSDag-Erling Smørgrav continue; 1341545d5ecaSDag-Erling Smørgrav 1342b66f2d16SKris Kennaway default: 13434f52dfbbSDag-Erling Smørgrav if (ch != efc->escape_char) { 1344190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u8(bin, 1345190cef3dSDag-Erling Smørgrav efc->escape_char)) != 0) 134619261079SEd Maste fatal_fr(r, "sshbuf_put_u8"); 1347b66f2d16SKris Kennaway bytes++; 1348b66f2d16SKris Kennaway } 1349b66f2d16SKris Kennaway /* Escaped characters fall through here */ 1350b66f2d16SKris Kennaway break; 1351b66f2d16SKris Kennaway } 1352b66f2d16SKris Kennaway } else { 1353b66f2d16SKris Kennaway /* 1354d4af9e69SDag-Erling Smørgrav * The previous character was not an escape char. 1355d4af9e69SDag-Erling Smørgrav * Check if this is an escape. 1356b66f2d16SKris Kennaway */ 13574f52dfbbSDag-Erling Smørgrav if (last_was_cr && ch == efc->escape_char) { 1358d4af9e69SDag-Erling Smørgrav /* 1359d4af9e69SDag-Erling Smørgrav * It is. Set the flag and continue to 1360d4af9e69SDag-Erling Smørgrav * next character. 1361d4af9e69SDag-Erling Smørgrav */ 13624f52dfbbSDag-Erling Smørgrav efc->escape_pending = 1; 1363b66f2d16SKris Kennaway continue; 1364b66f2d16SKris Kennaway } 1365b66f2d16SKris Kennaway } 1366b66f2d16SKris Kennaway 1367b66f2d16SKris Kennaway /* 1368b66f2d16SKris Kennaway * Normal character. Record whether it was a newline, 1369b66f2d16SKris Kennaway * and append it to the buffer. 1370b66f2d16SKris Kennaway */ 1371b66f2d16SKris Kennaway last_was_cr = (ch == '\r' || ch == '\n'); 1372190cef3dSDag-Erling Smørgrav if ((r = sshbuf_put_u8(bin, ch)) != 0) 137319261079SEd Maste fatal_fr(r, "sshbuf_put_u8"); 1374b66f2d16SKris Kennaway bytes++; 1375b66f2d16SKris Kennaway } 1376b66f2d16SKris Kennaway return bytes; 1377b66f2d16SKris Kennaway } 1378b66f2d16SKris Kennaway 1379511b41d2SMark Murray /* 1380a04a10f8SKris Kennaway * Get packets from the connection input buffer, and process them as long as 1381a04a10f8SKris Kennaway * there are packets available. 1382a04a10f8SKris Kennaway * 1383a04a10f8SKris Kennaway * Any unknown packets received during the actual 1384a04a10f8SKris Kennaway * session cause the session to terminate. This is 1385a04a10f8SKris Kennaway * intended to make debugging easier since no 1386a04a10f8SKris Kennaway * confirmations are sent. Any compatible protocol 1387a04a10f8SKris Kennaway * extensions must be negotiated during the 1388a04a10f8SKris Kennaway * preparatory phase. 1389a04a10f8SKris Kennaway */ 1390a04a10f8SKris Kennaway 1391ae1f160dSDag-Erling Smørgrav static void 139219261079SEd Maste client_process_buffered_input_packets(struct ssh *ssh) 1393a04a10f8SKris Kennaway { 139419261079SEd Maste ssh_dispatch_run_fatal(ssh, DISPATCH_NONBLOCK, &quit_pending); 1395a04a10f8SKris Kennaway } 1396a04a10f8SKris Kennaway 1397b66f2d16SKris Kennaway /* scan buf[] for '~' before sending data to the peer */ 1398b66f2d16SKris Kennaway 1399d4af9e69SDag-Erling Smørgrav /* Helper: allocate a new escape_filter_ctx and fill in its escape char */ 1400d4af9e69SDag-Erling Smørgrav void * 1401d4af9e69SDag-Erling Smørgrav client_new_escape_filter_ctx(int escape_char) 1402b66f2d16SKris Kennaway { 1403d4af9e69SDag-Erling Smørgrav struct escape_filter_ctx *ret; 1404d4af9e69SDag-Erling Smørgrav 14050a37d4a3SXin LI ret = xcalloc(1, sizeof(*ret)); 1406d4af9e69SDag-Erling Smørgrav ret->escape_pending = 0; 1407d4af9e69SDag-Erling Smørgrav ret->escape_char = escape_char; 1408d4af9e69SDag-Erling Smørgrav return (void *)ret; 1409d4af9e69SDag-Erling Smørgrav } 1410d4af9e69SDag-Erling Smørgrav 1411d4af9e69SDag-Erling Smørgrav /* Free the escape filter context on channel free */ 1412d4af9e69SDag-Erling Smørgrav void 14134f52dfbbSDag-Erling Smørgrav client_filter_cleanup(struct ssh *ssh, int cid, void *ctx) 1414d4af9e69SDag-Erling Smørgrav { 1415e4a9863fSDag-Erling Smørgrav free(ctx); 1416d4af9e69SDag-Erling Smørgrav } 1417d4af9e69SDag-Erling Smørgrav 1418d4af9e69SDag-Erling Smørgrav int 14194f52dfbbSDag-Erling Smørgrav client_simple_escape_filter(struct ssh *ssh, Channel *c, char *buf, int len) 1420d4af9e69SDag-Erling Smørgrav { 1421d4af9e69SDag-Erling Smørgrav if (c->extended_usage != CHAN_EXTENDED_WRITE) 1422d4af9e69SDag-Erling Smørgrav return 0; 1423d4af9e69SDag-Erling Smørgrav 14244f52dfbbSDag-Erling Smørgrav return process_escapes(ssh, c, c->input, c->output, c->extended, 1425d4af9e69SDag-Erling Smørgrav buf, len); 1426b66f2d16SKris Kennaway } 1427b66f2d16SKris Kennaway 1428ae1f160dSDag-Erling Smørgrav static void 1429f374ba41SEd Maste client_channel_closed(struct ssh *ssh, int id, int force, void *arg) 14301e8db6e2SBrian Feldman { 14314f52dfbbSDag-Erling Smørgrav channel_cancel_cleanup(ssh, id); 14321e8db6e2SBrian Feldman session_closed = 1; 1433e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 14341e8db6e2SBrian Feldman } 14351e8db6e2SBrian Feldman 1436a04a10f8SKris Kennaway /* 1437511b41d2SMark Murray * Implements the interactive session with the server. This is called after 1438511b41d2SMark Murray * the user has been authenticated, and a command has been started on the 1439ae1f160dSDag-Erling Smørgrav * remote host. If escape_char != SSH_ESCAPECHAR_NONE, it is the character 1440ae1f160dSDag-Erling Smørgrav * used as an escape character for terminating or suspending the session. 1441511b41d2SMark Murray */ 1442511b41d2SMark Murray int 14434f52dfbbSDag-Erling Smørgrav client_loop(struct ssh *ssh, int have_pty, int escape_char_arg, 14444f52dfbbSDag-Erling Smørgrav int ssh2_chan_id) 1445511b41d2SMark Murray { 14461323ec57SEd Maste struct pollfd *pfd = NULL; 14471323ec57SEd Maste u_int npfd_alloc = 0, npfd_active = 0; 1448511b41d2SMark Murray double start_time, total_time; 1449edf85781SEd Maste int channel_did_enqueue = 0, r, len; 1450d4af9e69SDag-Erling Smørgrav u_int64_t ibytes, obytes; 14511323ec57SEd Maste int conn_in_ready, conn_out_ready; 1452069ac184SEd Maste sigset_t bsigset, osigset; 1453511b41d2SMark Murray 1454511b41d2SMark Murray debug("Entering interactive session."); 1455f374ba41SEd Maste session_ident = ssh2_chan_id; 1456511b41d2SMark Murray 1457acc1a9efSDag-Erling Smørgrav if (options.control_master && 1458acc1a9efSDag-Erling Smørgrav !option_clear_or_none(options.control_path)) { 1459acc1a9efSDag-Erling Smørgrav debug("pledge: id"); 146019261079SEd Maste if (pledge("stdio rpath wpath cpath unix inet dns recvfd sendfd proc exec id tty", 1461acc1a9efSDag-Erling Smørgrav NULL) == -1) 146219261079SEd Maste fatal_f("pledge(): %s", strerror(errno)); 1463acc1a9efSDag-Erling Smørgrav 1464acc1a9efSDag-Erling Smørgrav } else if (options.forward_x11 || options.permit_local_command) { 1465acc1a9efSDag-Erling Smørgrav debug("pledge: exec"); 1466acc1a9efSDag-Erling Smørgrav if (pledge("stdio rpath wpath cpath unix inet dns proc exec tty", 1467acc1a9efSDag-Erling Smørgrav NULL) == -1) 146819261079SEd Maste fatal_f("pledge(): %s", strerror(errno)); 1469acc1a9efSDag-Erling Smørgrav 1470acc1a9efSDag-Erling Smørgrav } else if (options.update_hostkeys) { 14711323ec57SEd Maste debug("pledge: filesystem"); 1472acc1a9efSDag-Erling Smørgrav if (pledge("stdio rpath wpath cpath unix inet dns proc tty", 1473acc1a9efSDag-Erling Smørgrav NULL) == -1) 147419261079SEd Maste fatal_f("pledge(): %s", strerror(errno)); 1475acc1a9efSDag-Erling Smørgrav 1476076ad2f8SDag-Erling Smørgrav } else if (!option_clear_or_none(options.proxy_command) || 147719261079SEd Maste options.fork_after_authentication) { 1478acc1a9efSDag-Erling Smørgrav debug("pledge: proc"); 1479acc1a9efSDag-Erling Smørgrav if (pledge("stdio cpath unix inet dns proc tty", NULL) == -1) 148019261079SEd Maste fatal_f("pledge(): %s", strerror(errno)); 1481acc1a9efSDag-Erling Smørgrav 1482acc1a9efSDag-Erling Smørgrav } else { 1483acc1a9efSDag-Erling Smørgrav debug("pledge: network"); 14844f52dfbbSDag-Erling Smørgrav if (pledge("stdio unix inet dns proc tty", NULL) == -1) 148519261079SEd Maste fatal_f("pledge(): %s", strerror(errno)); 1486acc1a9efSDag-Erling Smørgrav } 1487acc1a9efSDag-Erling Smørgrav 1488f374ba41SEd Maste /* might be able to tighten now */ 1489f374ba41SEd Maste client_repledge(); 1490f374ba41SEd Maste 149147dd1d1bSDag-Erling Smørgrav start_time = monotime_double(); 1492511b41d2SMark Murray 1493511b41d2SMark Murray /* Initialize variables. */ 1494511b41d2SMark Murray last_was_cr = 1; 1495511b41d2SMark Murray exit_status = -1; 149619261079SEd Maste connection_in = ssh_packet_get_connection_in(ssh); 149719261079SEd Maste connection_out = ssh_packet_get_connection_out(ssh); 14981e8db6e2SBrian Feldman 1499511b41d2SMark Murray quit_pending = 0; 1500511b41d2SMark Murray 1501190cef3dSDag-Erling Smørgrav /* Initialize buffer. */ 1502190cef3dSDag-Erling Smørgrav if ((stderr_buffer = sshbuf_new()) == NULL) 150319261079SEd Maste fatal_f("sshbuf_new failed"); 1504511b41d2SMark Murray 150519261079SEd Maste client_init_dispatch(ssh); 1506a04a10f8SKris Kennaway 1507d0c8c0bcSDag-Erling Smørgrav /* 1508d0c8c0bcSDag-Erling Smørgrav * Set signal handlers, (e.g. to restore non-blocking mode) 1509d0c8c0bcSDag-Erling Smørgrav * but don't overwrite SIG_IGN, matches behaviour from rsh(1) 1510d0c8c0bcSDag-Erling Smørgrav */ 151119261079SEd Maste if (ssh_signal(SIGHUP, SIG_IGN) != SIG_IGN) 151219261079SEd Maste ssh_signal(SIGHUP, signal_handler); 151319261079SEd Maste if (ssh_signal(SIGINT, SIG_IGN) != SIG_IGN) 151419261079SEd Maste ssh_signal(SIGINT, signal_handler); 151519261079SEd Maste if (ssh_signal(SIGQUIT, SIG_IGN) != SIG_IGN) 151619261079SEd Maste ssh_signal(SIGQUIT, signal_handler); 151719261079SEd Maste if (ssh_signal(SIGTERM, SIG_IGN) != SIG_IGN) 151819261079SEd Maste ssh_signal(SIGTERM, signal_handler); 151919261079SEd Maste ssh_signal(SIGWINCH, window_change_handler); 1520511b41d2SMark Murray 1521511b41d2SMark Murray if (have_pty) 1522e146993eSDag-Erling Smørgrav enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 1523511b41d2SMark Murray 1524e146993eSDag-Erling Smørgrav if (session_ident != -1) { 1525e146993eSDag-Erling Smørgrav if (escape_char_arg != SSH_ESCAPECHAR_NONE) { 15264f52dfbbSDag-Erling Smørgrav channel_register_filter(ssh, session_ident, 1527d4af9e69SDag-Erling Smørgrav client_simple_escape_filter, NULL, 1528d4af9e69SDag-Erling Smørgrav client_filter_cleanup, 1529e146993eSDag-Erling Smørgrav client_new_escape_filter_ctx( 1530e146993eSDag-Erling Smørgrav escape_char_arg)); 1531e146993eSDag-Erling Smørgrav } 15324f52dfbbSDag-Erling Smørgrav channel_register_cleanup(ssh, session_ident, 1533021d409fSDag-Erling Smørgrav client_channel_closed, 0); 1534e146993eSDag-Erling Smørgrav } 1535b66f2d16SKris Kennaway 153619261079SEd Maste schedule_server_alive_check(); 153719261079SEd Maste 1538069ac184SEd Maste if (sigemptyset(&bsigset) == -1 || 1539069ac184SEd Maste sigaddset(&bsigset, SIGHUP) == -1 || 1540069ac184SEd Maste sigaddset(&bsigset, SIGINT) == -1 || 1541069ac184SEd Maste sigaddset(&bsigset, SIGQUIT) == -1 || 1542069ac184SEd Maste sigaddset(&bsigset, SIGTERM) == -1) 1543069ac184SEd Maste error_f("bsigset setup: %s", strerror(errno)); 1544069ac184SEd Maste 1545511b41d2SMark Murray /* Main loop of the client for the interactive session mode. */ 1546511b41d2SMark Murray while (!quit_pending) { 1547edf85781SEd Maste channel_did_enqueue = 0; 1548511b41d2SMark Murray 1549511b41d2SMark Murray /* Process buffered packets sent by the server. */ 155019261079SEd Maste client_process_buffered_input_packets(ssh); 1551511b41d2SMark Murray 15524f52dfbbSDag-Erling Smørgrav if (session_closed && !channel_still_open(ssh)) 1553a04a10f8SKris Kennaway break; 1554a04a10f8SKris Kennaway 15554f52dfbbSDag-Erling Smørgrav if (ssh_packet_is_rekeying(ssh)) { 15561e8db6e2SBrian Feldman debug("rekeying in progress"); 1557acc1a9efSDag-Erling Smørgrav } else if (need_rekeying) { 1558acc1a9efSDag-Erling Smørgrav /* manual rekey request */ 1559acc1a9efSDag-Erling Smørgrav debug("need rekeying"); 15604f52dfbbSDag-Erling Smørgrav if ((r = kex_start_rekex(ssh)) != 0) 156119261079SEd Maste fatal_fr(r, "kex_start_rekex"); 1562acc1a9efSDag-Erling Smørgrav need_rekeying = 0; 15631e8db6e2SBrian Feldman } else { 1564511b41d2SMark Murray /* 15651e8db6e2SBrian Feldman * Make packets from buffered channel data, and 15661e8db6e2SBrian Feldman * enqueue them for sending to the server. 1567511b41d2SMark Murray */ 156819261079SEd Maste if (ssh_packet_not_very_much_data_to_write(ssh)) 1569edf85781SEd Maste channel_did_enqueue = channel_output_poll(ssh); 1570511b41d2SMark Murray 1571511b41d2SMark Murray /* 15721e8db6e2SBrian Feldman * Check if the window size has changed, and buffer a 15731e8db6e2SBrian Feldman * message about it to the server if so. 1574511b41d2SMark Murray */ 15754f52dfbbSDag-Erling Smørgrav client_check_window_change(ssh); 15761e8db6e2SBrian Feldman } 1577511b41d2SMark Murray /* 1578511b41d2SMark Murray * Wait until we have something to do (something becomes 1579511b41d2SMark Murray * available on one of the descriptors). 1580511b41d2SMark Murray */ 1581069ac184SEd Maste if (sigprocmask(SIG_BLOCK, &bsigset, &osigset) == -1) 1582069ac184SEd Maste error_f("bsigset sigprocmask: %s", strerror(errno)); 1583069ac184SEd Maste if (quit_pending) 1584069ac184SEd Maste break; 15851323ec57SEd Maste client_wait_until_can_do_something(ssh, &pfd, &npfd_alloc, 1586069ac184SEd Maste &npfd_active, channel_did_enqueue, &osigset, 15871323ec57SEd Maste &conn_in_ready, &conn_out_ready); 1588069ac184SEd Maste if (sigprocmask(SIG_UNBLOCK, &bsigset, &osigset) == -1) 1589069ac184SEd Maste error_f("osigset sigprocmask: %s", strerror(errno)); 1590511b41d2SMark Murray 1591511b41d2SMark Murray if (quit_pending) 1592511b41d2SMark Murray break; 1593511b41d2SMark Murray 159438a52bd3SEd Maste /* Do channel operations. */ 15951323ec57SEd Maste channel_after_poll(ssh, pfd, npfd_active); 1596511b41d2SMark Murray 1597a04a10f8SKris Kennaway /* Buffer input from the connection. */ 15981323ec57SEd Maste if (conn_in_ready) 15991323ec57SEd Maste client_process_net_input(ssh); 1600511b41d2SMark Murray 1601a04a10f8SKris Kennaway if (quit_pending) 1602a04a10f8SKris Kennaway break; 1603a04a10f8SKris Kennaway 160419261079SEd Maste /* A timeout may have triggered rekeying */ 160519261079SEd Maste if ((r = ssh_packet_check_rekey(ssh)) != 0) 160619261079SEd Maste fatal_fr(r, "cannot start rekeying"); 160719261079SEd Maste 1608d4af9e69SDag-Erling Smørgrav /* 1609d4af9e69SDag-Erling Smørgrav * Send as much buffered packet data as possible to the 1610d4af9e69SDag-Erling Smørgrav * sender. 1611d4af9e69SDag-Erling Smørgrav */ 16121323ec57SEd Maste if (conn_out_ready) { 161319261079SEd Maste if ((r = ssh_packet_write_poll(ssh)) != 0) { 161419261079SEd Maste sshpkt_fatal(ssh, r, 161519261079SEd Maste "%s: ssh_packet_write_poll", __func__); 161619261079SEd Maste } 161719261079SEd Maste } 1618e2f6069cSDag-Erling Smørgrav 1619e2f6069cSDag-Erling Smørgrav /* 1620e2f6069cSDag-Erling Smørgrav * If we are a backgrounded control master, and the 1621e2f6069cSDag-Erling Smørgrav * timeout has expired without any active client 1622e2f6069cSDag-Erling Smørgrav * connections, then quit. 1623e2f6069cSDag-Erling Smørgrav */ 1624e2f6069cSDag-Erling Smørgrav if (control_persist_exit_time > 0) { 1625e4a9863fSDag-Erling Smørgrav if (monotime() >= control_persist_exit_time) { 1626e2f6069cSDag-Erling Smørgrav debug("ControlPersist timeout expired"); 1627e2f6069cSDag-Erling Smørgrav break; 1628e2f6069cSDag-Erling Smørgrav } 1629e2f6069cSDag-Erling Smørgrav } 1630511b41d2SMark Murray } 16311323ec57SEd Maste free(pfd); 1632511b41d2SMark Murray 1633511b41d2SMark Murray /* Terminate the session. */ 1634511b41d2SMark Murray 1635511b41d2SMark Murray /* Stop watching for window change. */ 163619261079SEd Maste ssh_signal(SIGWINCH, SIG_DFL); 1637511b41d2SMark Murray 163819261079SEd Maste if ((r = sshpkt_start(ssh, SSH2_MSG_DISCONNECT)) != 0 || 163919261079SEd Maste (r = sshpkt_put_u32(ssh, SSH2_DISCONNECT_BY_APPLICATION)) != 0 || 164019261079SEd Maste (r = sshpkt_put_cstring(ssh, "disconnected by user")) != 0 || 164119261079SEd Maste (r = sshpkt_put_cstring(ssh, "")) != 0 || /* language tag */ 164219261079SEd Maste (r = sshpkt_send(ssh)) != 0 || 164319261079SEd Maste (r = ssh_packet_write_wait(ssh)) != 0) 164419261079SEd Maste fatal_fr(r, "send disconnect"); 16457aee6ffeSDag-Erling Smørgrav 16464f52dfbbSDag-Erling Smørgrav channel_free_all(ssh); 1647ae1f160dSDag-Erling Smørgrav 1648ae1f160dSDag-Erling Smørgrav if (have_pty) 1649e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 1650ae1f160dSDag-Erling Smørgrav 1651efcad6b7SDag-Erling Smørgrav /* 1652efcad6b7SDag-Erling Smørgrav * If there was no shell or command requested, there will be no remote 1653efcad6b7SDag-Erling Smørgrav * exit status to be returned. In that case, clear error code if the 1654efcad6b7SDag-Erling Smørgrav * connection was deliberately terminated at this end. 1655efcad6b7SDag-Erling Smørgrav */ 1656e9e8876aSEd Maste if (options.session_type == SESSION_TYPE_NONE && 1657e9e8876aSEd Maste received_signal == SIGTERM) { 1658efcad6b7SDag-Erling Smørgrav received_signal = 0; 1659efcad6b7SDag-Erling Smørgrav exit_status = 0; 1660ae1f160dSDag-Erling Smørgrav } 1661511b41d2SMark Murray 16624f52dfbbSDag-Erling Smørgrav if (received_signal) { 16634f52dfbbSDag-Erling Smørgrav verbose("Killed by signal %d.", (int) received_signal); 166419261079SEd Maste cleanup_exit(255); 16654f52dfbbSDag-Erling Smørgrav } 1666efcad6b7SDag-Erling Smørgrav 1667511b41d2SMark Murray /* 1668511b41d2SMark Murray * In interactive mode (with pseudo tty) display a message indicating 1669511b41d2SMark Murray * that the connection has been closed. 1670511b41d2SMark Murray */ 16711323ec57SEd Maste if (have_pty && options.log_level >= SYSLOG_LEVEL_INFO) 16721323ec57SEd Maste quit_message("Connection to %s closed.", host); 1673ae1f160dSDag-Erling Smørgrav 1674511b41d2SMark Murray /* Output any buffered data for stderr. */ 1675190cef3dSDag-Erling Smørgrav if (sshbuf_len(stderr_buffer) > 0) { 16764a421b63SDag-Erling Smørgrav len = atomicio(vwrite, fileno(stderr), 1677190cef3dSDag-Erling Smørgrav (u_char *)sshbuf_ptr(stderr_buffer), 1678190cef3dSDag-Erling Smørgrav sshbuf_len(stderr_buffer)); 1679190cef3dSDag-Erling Smørgrav if (len < 0 || (u_int)len != sshbuf_len(stderr_buffer)) 1680511b41d2SMark Murray error("Write failed flushing stderr buffer."); 1681190cef3dSDag-Erling Smørgrav else if ((r = sshbuf_consume(stderr_buffer, len)) != 0) 168219261079SEd Maste fatal_fr(r, "sshbuf_consume"); 1683511b41d2SMark Murray } 1684511b41d2SMark Murray 1685511b41d2SMark Murray /* Clear and free any buffers. */ 1686190cef3dSDag-Erling Smørgrav sshbuf_free(stderr_buffer); 1687511b41d2SMark Murray 1688511b41d2SMark Murray /* Report bytes transferred, and transfer rates. */ 168947dd1d1bSDag-Erling Smørgrav total_time = monotime_double() - start_time; 169019261079SEd Maste ssh_packet_get_bytes(ssh, &ibytes, &obytes); 1691d4af9e69SDag-Erling Smørgrav verbose("Transferred: sent %llu, received %llu bytes, in %.1f seconds", 16924a421b63SDag-Erling Smørgrav (unsigned long long)obytes, (unsigned long long)ibytes, total_time); 1693511b41d2SMark Murray if (total_time > 0) 1694d4af9e69SDag-Erling Smørgrav verbose("Bytes per second: sent %.1f, received %.1f", 1695d4af9e69SDag-Erling Smørgrav obytes / total_time, ibytes / total_time); 1696511b41d2SMark Murray /* Return the exit status of the program. */ 1697511b41d2SMark Murray debug("Exit status %d", exit_status); 1698511b41d2SMark Murray return exit_status; 1699511b41d2SMark Murray } 1700a04a10f8SKris Kennaway 1701a04a10f8SKris Kennaway /*********/ 1702a04a10f8SKris Kennaway 1703ae1f160dSDag-Erling Smørgrav static Channel * 17044f52dfbbSDag-Erling Smørgrav client_request_forwarded_tcpip(struct ssh *ssh, const char *request_type, 17054f52dfbbSDag-Erling Smørgrav int rchan, u_int rwindow, u_int rmaxpack) 17061e8db6e2SBrian Feldman { 17071e8db6e2SBrian Feldman Channel *c = NULL; 1708ca86bcf2SDag-Erling Smørgrav struct sshbuf *b = NULL; 17091e8db6e2SBrian Feldman char *listen_address, *originator_address; 171019261079SEd Maste u_int listen_port, originator_port; 1711ca86bcf2SDag-Erling Smørgrav int r; 17121e8db6e2SBrian Feldman 17131e8db6e2SBrian Feldman /* Get rest of the packet */ 171419261079SEd Maste if ((r = sshpkt_get_cstring(ssh, &listen_address, NULL)) != 0 || 171519261079SEd Maste (r = sshpkt_get_u32(ssh, &listen_port)) != 0 || 171619261079SEd Maste (r = sshpkt_get_cstring(ssh, &originator_address, NULL)) != 0 || 171719261079SEd Maste (r = sshpkt_get_u32(ssh, &originator_port)) != 0 || 171819261079SEd Maste (r = sshpkt_get_end(ssh)) != 0) 171919261079SEd Maste fatal_fr(r, "parse packet"); 17201e8db6e2SBrian Feldman 172119261079SEd Maste debug_f("listen %s port %d, originator %s port %d", 1722a0ee8cc6SDag-Erling Smørgrav listen_address, listen_port, originator_address, originator_port); 17231e8db6e2SBrian Feldman 172419261079SEd Maste if (listen_port > 0xffff) 172519261079SEd Maste error_f("invalid listen port"); 172619261079SEd Maste else if (originator_port > 0xffff) 172719261079SEd Maste error_f("invalid originator port"); 172819261079SEd Maste else { 172919261079SEd Maste c = channel_connect_by_listen_address(ssh, 173019261079SEd Maste listen_address, listen_port, "forwarded-tcpip", 173119261079SEd Maste originator_address); 173219261079SEd Maste } 1733d4af9e69SDag-Erling Smørgrav 1734ca86bcf2SDag-Erling Smørgrav if (c != NULL && c->type == SSH_CHANNEL_MUX_CLIENT) { 1735ca86bcf2SDag-Erling Smørgrav if ((b = sshbuf_new()) == NULL) { 173619261079SEd Maste error_f("alloc reply"); 1737ca86bcf2SDag-Erling Smørgrav goto out; 1738ca86bcf2SDag-Erling Smørgrav } 1739ca86bcf2SDag-Erling Smørgrav /* reconstruct and send to muxclient */ 1740ca86bcf2SDag-Erling Smørgrav if ((r = sshbuf_put_u8(b, 0)) != 0 || /* padlen */ 1741ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u8(b, SSH2_MSG_CHANNEL_OPEN)) != 0 || 1742ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_cstring(b, request_type)) != 0 || 1743ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, rchan)) != 0 || 1744ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, rwindow)) != 0 || 1745ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, rmaxpack)) != 0 || 1746ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_cstring(b, listen_address)) != 0 || 1747ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, listen_port)) != 0 || 1748ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_cstring(b, originator_address)) != 0 || 1749ca86bcf2SDag-Erling Smørgrav (r = sshbuf_put_u32(b, originator_port)) != 0 || 17504f52dfbbSDag-Erling Smørgrav (r = sshbuf_put_stringb(c->output, b)) != 0) { 175119261079SEd Maste error_fr(r, "compose for muxclient"); 1752ca86bcf2SDag-Erling Smørgrav goto out; 1753ca86bcf2SDag-Erling Smørgrav } 1754ca86bcf2SDag-Erling Smørgrav } 1755ca86bcf2SDag-Erling Smørgrav 1756ca86bcf2SDag-Erling Smørgrav out: 1757ca86bcf2SDag-Erling Smørgrav sshbuf_free(b); 1758e4a9863fSDag-Erling Smørgrav free(originator_address); 1759e4a9863fSDag-Erling Smørgrav free(listen_address); 17601e8db6e2SBrian Feldman return c; 17611e8db6e2SBrian Feldman } 17621e8db6e2SBrian Feldman 1763ae1f160dSDag-Erling Smørgrav static Channel * 17644f52dfbbSDag-Erling Smørgrav client_request_forwarded_streamlocal(struct ssh *ssh, 17654f52dfbbSDag-Erling Smørgrav const char *request_type, int rchan) 1766a0ee8cc6SDag-Erling Smørgrav { 1767a0ee8cc6SDag-Erling Smørgrav Channel *c = NULL; 1768a0ee8cc6SDag-Erling Smørgrav char *listen_path; 176919261079SEd Maste int r; 1770a0ee8cc6SDag-Erling Smørgrav 1771a0ee8cc6SDag-Erling Smørgrav /* Get the remote path. */ 177219261079SEd Maste if ((r = sshpkt_get_cstring(ssh, &listen_path, NULL)) != 0 || 177319261079SEd Maste (r = sshpkt_get_string(ssh, NULL, NULL)) != 0 || /* reserved */ 177419261079SEd Maste (r = sshpkt_get_end(ssh)) != 0) 177519261079SEd Maste fatal_fr(r, "parse packet"); 1776a0ee8cc6SDag-Erling Smørgrav 177719261079SEd Maste debug_f("request: %s", listen_path); 1778a0ee8cc6SDag-Erling Smørgrav 17794f52dfbbSDag-Erling Smørgrav c = channel_connect_by_listen_path(ssh, listen_path, 1780a0ee8cc6SDag-Erling Smørgrav "forwarded-streamlocal@openssh.com", "forwarded-streamlocal"); 1781a0ee8cc6SDag-Erling Smørgrav free(listen_path); 1782a0ee8cc6SDag-Erling Smørgrav return c; 1783a0ee8cc6SDag-Erling Smørgrav } 1784a0ee8cc6SDag-Erling Smørgrav 1785a0ee8cc6SDag-Erling Smørgrav static Channel * 17864f52dfbbSDag-Erling Smørgrav client_request_x11(struct ssh *ssh, const char *request_type, int rchan) 17871e8db6e2SBrian Feldman { 17881e8db6e2SBrian Feldman Channel *c = NULL; 17891e8db6e2SBrian Feldman char *originator; 179019261079SEd Maste u_int originator_port; 179119261079SEd Maste int r, sock; 17921e8db6e2SBrian Feldman 17931e8db6e2SBrian Feldman if (!options.forward_x11) { 17941e8db6e2SBrian Feldman error("Warning: ssh server tried X11 forwarding."); 1795d4af9e69SDag-Erling Smørgrav error("Warning: this is probably a break-in attempt by a " 1796d4af9e69SDag-Erling Smørgrav "malicious server."); 17971e8db6e2SBrian Feldman return NULL; 17981e8db6e2SBrian Feldman } 17994d3fc8b0SEd Maste if (x11_refuse_time != 0 && monotime() >= x11_refuse_time) { 1800e2f6069cSDag-Erling Smørgrav verbose("Rejected X11 connection after ForwardX11Timeout " 1801e2f6069cSDag-Erling Smørgrav "expired"); 1802e2f6069cSDag-Erling Smørgrav return NULL; 1803e2f6069cSDag-Erling Smørgrav } 180419261079SEd Maste if ((r = sshpkt_get_cstring(ssh, &originator, NULL)) != 0 || 180519261079SEd Maste (r = sshpkt_get_u32(ssh, &originator_port)) != 0 || 180619261079SEd Maste (r = sshpkt_get_end(ssh)) != 0) 180719261079SEd Maste fatal_fr(r, "parse packet"); 18081e8db6e2SBrian Feldman /* XXX check permission */ 180919261079SEd Maste /* XXX range check originator port? */ 181019261079SEd Maste debug("client_request_x11: request from %s %u", originator, 18111e8db6e2SBrian Feldman originator_port); 1812e4a9863fSDag-Erling Smørgrav free(originator); 18134f52dfbbSDag-Erling Smørgrav sock = x11_connect_display(ssh); 1814ae1f160dSDag-Erling Smørgrav if (sock < 0) 1815ae1f160dSDag-Erling Smørgrav return NULL; 1816069ac184SEd Maste c = channel_new(ssh, "x11-connection", 181760c59fadSDag-Erling Smørgrav SSH_CHANNEL_X11_OPEN, sock, sock, -1, 181860c59fadSDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "x11", 1); 1819ae1f160dSDag-Erling Smørgrav c->force_drain = 1; 18201e8db6e2SBrian Feldman return c; 18211e8db6e2SBrian Feldman } 18221e8db6e2SBrian Feldman 1823ae1f160dSDag-Erling Smørgrav static Channel * 18244f52dfbbSDag-Erling Smørgrav client_request_agent(struct ssh *ssh, const char *request_type, int rchan) 18251e8db6e2SBrian Feldman { 18261e8db6e2SBrian Feldman Channel *c = NULL; 1827bc5531deSDag-Erling Smørgrav int r, sock; 18281e8db6e2SBrian Feldman 18291e8db6e2SBrian Feldman if (!options.forward_agent) { 18301e8db6e2SBrian Feldman error("Warning: ssh server tried agent forwarding."); 1831d4af9e69SDag-Erling Smørgrav error("Warning: this is probably a break-in attempt by a " 1832d4af9e69SDag-Erling Smørgrav "malicious server."); 18331e8db6e2SBrian Feldman return NULL; 18341e8db6e2SBrian Feldman } 183519261079SEd Maste if (forward_agent_sock_path == NULL) { 183619261079SEd Maste r = ssh_get_authentication_socket(&sock); 183719261079SEd Maste } else { 183819261079SEd Maste r = ssh_get_authentication_socket_path(forward_agent_sock_path, &sock); 183919261079SEd Maste } 184019261079SEd Maste if (r != 0) { 1841bc5531deSDag-Erling Smørgrav if (r != SSH_ERR_AGENT_NOT_PRESENT) 184219261079SEd Maste debug_fr(r, "ssh_get_authentication_socket"); 1843ae1f160dSDag-Erling Smørgrav return NULL; 1844bc5531deSDag-Erling Smørgrav } 18451323ec57SEd Maste if ((r = ssh_agent_bind_hostkey(sock, ssh->kex->initial_hostkey, 18461323ec57SEd Maste ssh->kex->session_id, ssh->kex->initial_sig, 1)) == 0) 18471323ec57SEd Maste debug_f("bound agent to hostkey"); 18481323ec57SEd Maste else 18491323ec57SEd Maste debug2_fr(r, "ssh_agent_bind_hostkey"); 18501323ec57SEd Maste 1851069ac184SEd Maste c = channel_new(ssh, "agent-connection", 18521e8db6e2SBrian Feldman SSH_CHANNEL_OPEN, sock, sock, -1, 1853e3bd730fSBryan Drewery CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, 185489986192SBrooks Davis "authentication agent connection", 1); 1855ae1f160dSDag-Erling Smørgrav c->force_drain = 1; 18561e8db6e2SBrian Feldman return c; 18571e8db6e2SBrian Feldman } 18581e8db6e2SBrian Feldman 185947dd1d1bSDag-Erling Smørgrav char * 18604f52dfbbSDag-Erling Smørgrav client_request_tun_fwd(struct ssh *ssh, int tun_mode, 186119261079SEd Maste int local_tun, int remote_tun, channel_open_fn *cb, void *cbctx) 1862d4af9e69SDag-Erling Smørgrav { 1863d4af9e69SDag-Erling Smørgrav Channel *c; 186419261079SEd Maste int r, fd; 186547dd1d1bSDag-Erling Smørgrav char *ifname = NULL; 1866d4af9e69SDag-Erling Smørgrav 1867d4af9e69SDag-Erling Smørgrav if (tun_mode == SSH_TUNMODE_NO) 1868d4af9e69SDag-Erling Smørgrav return 0; 1869d4af9e69SDag-Erling Smørgrav 1870d4af9e69SDag-Erling Smørgrav debug("Requesting tun unit %d in mode %d", local_tun, tun_mode); 1871d4af9e69SDag-Erling Smørgrav 1872d4af9e69SDag-Erling Smørgrav /* Open local tunnel device */ 187347dd1d1bSDag-Erling Smørgrav if ((fd = tun_open(local_tun, tun_mode, &ifname)) == -1) { 1874d4af9e69SDag-Erling Smørgrav error("Tunnel device open failed."); 187547dd1d1bSDag-Erling Smørgrav return NULL; 1876d4af9e69SDag-Erling Smørgrav } 187747dd1d1bSDag-Erling Smørgrav debug("Tunnel forwarding using interface %s", ifname); 1878d4af9e69SDag-Erling Smørgrav 1879069ac184SEd Maste c = channel_new(ssh, "tun-connection", SSH_CHANNEL_OPENING, fd, fd, -1, 188060c59fadSDag-Erling Smørgrav CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1); 1881d4af9e69SDag-Erling Smørgrav c->datagram = 1; 1882d4af9e69SDag-Erling Smørgrav 1883d4af9e69SDag-Erling Smørgrav #if defined(SSH_TUN_FILTER) 1884d4af9e69SDag-Erling Smørgrav if (options.tun_open == SSH_TUNMODE_POINTOPOINT) 18854f52dfbbSDag-Erling Smørgrav channel_register_filter(ssh, c->self, sys_tun_infilter, 1886d4af9e69SDag-Erling Smørgrav sys_tun_outfilter, NULL, NULL); 1887d4af9e69SDag-Erling Smørgrav #endif 1888d4af9e69SDag-Erling Smørgrav 188919261079SEd Maste if (cb != NULL) 189019261079SEd Maste channel_register_open_confirm(ssh, c->self, cb, cbctx); 189119261079SEd Maste 189219261079SEd Maste if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN)) != 0 || 189319261079SEd Maste (r = sshpkt_put_cstring(ssh, "tun@openssh.com")) != 0 || 189419261079SEd Maste (r = sshpkt_put_u32(ssh, c->self)) != 0 || 189519261079SEd Maste (r = sshpkt_put_u32(ssh, c->local_window_max)) != 0 || 189619261079SEd Maste (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0 || 189719261079SEd Maste (r = sshpkt_put_u32(ssh, tun_mode)) != 0 || 189819261079SEd Maste (r = sshpkt_put_u32(ssh, remote_tun)) != 0 || 189919261079SEd Maste (r = sshpkt_send(ssh)) != 0) 190019261079SEd Maste sshpkt_fatal(ssh, r, "%s: send reply", __func__); 1901d4af9e69SDag-Erling Smørgrav 190247dd1d1bSDag-Erling Smørgrav return ifname; 1903d4af9e69SDag-Erling Smørgrav } 1904d4af9e69SDag-Erling Smørgrav 1905a04a10f8SKris Kennaway /* XXXX move to generic input handler */ 1906bc5531deSDag-Erling Smørgrav static int 19074f52dfbbSDag-Erling Smørgrav client_input_channel_open(int type, u_int32_t seq, struct ssh *ssh) 1908a04a10f8SKris Kennaway { 1909a04a10f8SKris Kennaway Channel *c = NULL; 191019261079SEd Maste char *ctype = NULL; 191119261079SEd Maste int r; 191219261079SEd Maste u_int rchan; 191319261079SEd Maste size_t len; 191419261079SEd Maste u_int rmaxpack, rwindow; 1915a04a10f8SKris Kennaway 191619261079SEd Maste if ((r = sshpkt_get_cstring(ssh, &ctype, &len)) != 0 || 191719261079SEd Maste (r = sshpkt_get_u32(ssh, &rchan)) != 0 || 191819261079SEd Maste (r = sshpkt_get_u32(ssh, &rwindow)) != 0 || 191919261079SEd Maste (r = sshpkt_get_u32(ssh, &rmaxpack)) != 0) 192019261079SEd Maste goto out; 1921a04a10f8SKris Kennaway 1922a04a10f8SKris Kennaway debug("client_input_channel_open: ctype %s rchan %d win %d max %d", 1923a04a10f8SKris Kennaway ctype, rchan, rwindow, rmaxpack); 1924a04a10f8SKris Kennaway 19251e8db6e2SBrian Feldman if (strcmp(ctype, "forwarded-tcpip") == 0) { 19264f52dfbbSDag-Erling Smørgrav c = client_request_forwarded_tcpip(ssh, ctype, rchan, rwindow, 1927ca86bcf2SDag-Erling Smørgrav rmaxpack); 1928a0ee8cc6SDag-Erling Smørgrav } else if (strcmp(ctype, "forwarded-streamlocal@openssh.com") == 0) { 19294f52dfbbSDag-Erling Smørgrav c = client_request_forwarded_streamlocal(ssh, ctype, rchan); 19301e8db6e2SBrian Feldman } else if (strcmp(ctype, "x11") == 0) { 19314f52dfbbSDag-Erling Smørgrav c = client_request_x11(ssh, ctype, rchan); 19321e8db6e2SBrian Feldman } else if (strcmp(ctype, "auth-agent@openssh.com") == 0) { 19334f52dfbbSDag-Erling Smørgrav c = client_request_agent(ssh, ctype, rchan); 1934a04a10f8SKris Kennaway } 1935ca86bcf2SDag-Erling Smørgrav if (c != NULL && c->type == SSH_CHANNEL_MUX_CLIENT) { 1936ca86bcf2SDag-Erling Smørgrav debug3("proxied to downstream: %s", ctype); 1937ca86bcf2SDag-Erling Smørgrav } else if (c != NULL) { 1938a04a10f8SKris Kennaway debug("confirm %s", ctype); 1939a04a10f8SKris Kennaway c->remote_id = rchan; 19404f52dfbbSDag-Erling Smørgrav c->have_remote_id = 1; 1941a04a10f8SKris Kennaway c->remote_window = rwindow; 1942a04a10f8SKris Kennaway c->remote_maxpacket = rmaxpack; 1943ae1f160dSDag-Erling Smørgrav if (c->type != SSH_CHANNEL_CONNECTING) { 194419261079SEd Maste if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION)) != 0 || 194519261079SEd Maste (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 194619261079SEd Maste (r = sshpkt_put_u32(ssh, c->self)) != 0 || 194719261079SEd Maste (r = sshpkt_put_u32(ssh, c->local_window)) != 0 || 194819261079SEd Maste (r = sshpkt_put_u32(ssh, c->local_maxpacket)) != 0 || 194919261079SEd Maste (r = sshpkt_send(ssh)) != 0) 195019261079SEd Maste sshpkt_fatal(ssh, r, "%s: send reply", __func__); 1951ae1f160dSDag-Erling Smørgrav } 1952a04a10f8SKris Kennaway } else { 1953a04a10f8SKris Kennaway debug("failure %s", ctype); 195419261079SEd Maste if ((r = sshpkt_start(ssh, SSH2_MSG_CHANNEL_OPEN_FAILURE)) != 0 || 195519261079SEd Maste (r = sshpkt_put_u32(ssh, rchan)) != 0 || 195619261079SEd Maste (r = sshpkt_put_u32(ssh, SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED)) != 0 || 195719261079SEd Maste (r = sshpkt_put_cstring(ssh, "open failed")) != 0 || 195819261079SEd Maste (r = sshpkt_put_cstring(ssh, "")) != 0 || 195919261079SEd Maste (r = sshpkt_send(ssh)) != 0) 196019261079SEd Maste sshpkt_fatal(ssh, r, "%s: send failure", __func__); 1961a04a10f8SKris Kennaway } 196219261079SEd Maste r = 0; 196319261079SEd Maste out: 1964e4a9863fSDag-Erling Smørgrav free(ctype); 196519261079SEd Maste return r; 1966a04a10f8SKris Kennaway } 1967bc5531deSDag-Erling Smørgrav 1968bc5531deSDag-Erling Smørgrav static int 19694f52dfbbSDag-Erling Smørgrav client_input_channel_req(int type, u_int32_t seq, struct ssh *ssh) 19701e8db6e2SBrian Feldman { 19711e8db6e2SBrian Feldman Channel *c = NULL; 197219261079SEd Maste char *rtype = NULL; 197319261079SEd Maste u_char reply; 197419261079SEd Maste u_int id, exitval; 197519261079SEd Maste int r, success = 0; 19761e8db6e2SBrian Feldman 197719261079SEd Maste if ((r = sshpkt_get_u32(ssh, &id)) != 0) 197819261079SEd Maste return r; 197919261079SEd Maste if (id <= INT_MAX) 19804f52dfbbSDag-Erling Smørgrav c = channel_lookup(ssh, id); 19814f52dfbbSDag-Erling Smørgrav if (channel_proxy_upstream(c, type, seq, ssh)) 1982ca86bcf2SDag-Erling Smørgrav return 0; 198319261079SEd Maste if ((r = sshpkt_get_cstring(ssh, &rtype, NULL)) != 0 || 198419261079SEd Maste (r = sshpkt_get_u8(ssh, &reply)) != 0) 198519261079SEd Maste goto out; 19861e8db6e2SBrian Feldman 198719261079SEd Maste debug("client_input_channel_req: channel %u rtype %s reply %d", 19881e8db6e2SBrian Feldman id, rtype, reply); 19891e8db6e2SBrian Feldman 199019261079SEd Maste if (c == NULL) { 1991d4af9e69SDag-Erling Smørgrav error("client_input_channel_req: channel %d: " 1992d4af9e69SDag-Erling Smørgrav "unknown channel", id); 1993d4af9e69SDag-Erling Smørgrav } else if (strcmp(rtype, "eow@openssh.com") == 0) { 199419261079SEd Maste if ((r = sshpkt_get_end(ssh)) != 0) 199519261079SEd Maste goto out; 19964f52dfbbSDag-Erling Smørgrav chan_rcvd_eow(ssh, c); 19971e8db6e2SBrian Feldman } else if (strcmp(rtype, "exit-status") == 0) { 199819261079SEd Maste if ((r = sshpkt_get_u32(ssh, &exitval)) != 0) 199919261079SEd Maste goto out; 2000b15c8340SDag-Erling Smørgrav if (c->ctl_chan != -1) { 20014f52dfbbSDag-Erling Smørgrav mux_exit_message(ssh, c, exitval); 2002b15c8340SDag-Erling Smørgrav success = 1; 200319261079SEd Maste } else if ((int)id == session_ident) { 2004b15c8340SDag-Erling Smørgrav /* Record exit value of local session */ 20051e8db6e2SBrian Feldman success = 1; 2006d74d50a8SDag-Erling Smørgrav exit_status = exitval; 2007d74d50a8SDag-Erling Smørgrav } else { 2008b15c8340SDag-Erling Smørgrav /* Probably for a mux channel that has already closed */ 200919261079SEd Maste debug_f("no sink for exit-status on channel %d", 201019261079SEd Maste id); 2011d74d50a8SDag-Erling Smørgrav } 201219261079SEd Maste if ((r = sshpkt_get_end(ssh)) != 0) 201319261079SEd Maste goto out; 20141e8db6e2SBrian Feldman } 2015a0ee8cc6SDag-Erling Smørgrav if (reply && c != NULL && !(c->flags & CHAN_CLOSE_SENT)) { 20164f52dfbbSDag-Erling Smørgrav if (!c->have_remote_id) 201719261079SEd Maste fatal_f("channel %d: no remote_id", c->self); 201819261079SEd Maste if ((r = sshpkt_start(ssh, success ? 201919261079SEd Maste SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE)) != 0 || 202019261079SEd Maste (r = sshpkt_put_u32(ssh, c->remote_id)) != 0 || 202119261079SEd Maste (r = sshpkt_send(ssh)) != 0) 202219261079SEd Maste sshpkt_fatal(ssh, r, "%s: send failure", __func__); 20231e8db6e2SBrian Feldman } 202419261079SEd Maste r = 0; 202519261079SEd Maste out: 2026e4a9863fSDag-Erling Smørgrav free(rtype); 202719261079SEd Maste return r; 20281e8db6e2SBrian Feldman } 2029bc5531deSDag-Erling Smørgrav 2030bc5531deSDag-Erling Smørgrav struct hostkeys_update_ctx { 2031bc5531deSDag-Erling Smørgrav /* The hostname and (optionally) IP address string for the server */ 2032bc5531deSDag-Erling Smørgrav char *host_str, *ip_str; 2033bc5531deSDag-Erling Smørgrav 2034bc5531deSDag-Erling Smørgrav /* 2035bc5531deSDag-Erling Smørgrav * Keys received from the server and a flag for each indicating 2036bc5531deSDag-Erling Smørgrav * whether they already exist in known_hosts. 203719261079SEd Maste * keys_match is filled in by hostkeys_find() and later (for new 2038f374ba41SEd Maste * keys) by client_global_hostkeys_prove_confirm(). 2039bc5531deSDag-Erling Smørgrav */ 2040bc5531deSDag-Erling Smørgrav struct sshkey **keys; 204119261079SEd Maste u_int *keys_match; /* mask of HKF_MATCH_* from hostfile.h */ 204219261079SEd Maste int *keys_verified; /* flag for new keys verified by server */ 204319261079SEd Maste size_t nkeys, nnew, nincomplete; /* total, new keys, incomplete match */ 2044bc5531deSDag-Erling Smørgrav 2045bc5531deSDag-Erling Smørgrav /* 2046bc5531deSDag-Erling Smørgrav * Keys that are in known_hosts, but were not present in the update 2047bc5531deSDag-Erling Smørgrav * from the server (i.e. scheduled to be deleted). 2048bc5531deSDag-Erling Smørgrav * Filled in by hostkeys_find(). 2049bc5531deSDag-Erling Smørgrav */ 2050bc5531deSDag-Erling Smørgrav struct sshkey **old_keys; 2051bc5531deSDag-Erling Smørgrav size_t nold; 205219261079SEd Maste 205319261079SEd Maste /* Various special cases. */ 205419261079SEd Maste int complex_hostspec; /* wildcard or manual pattern-list host name */ 205519261079SEd Maste int ca_available; /* saw CA key for this host */ 205619261079SEd Maste int old_key_seen; /* saw old key with other name/addr */ 205719261079SEd Maste int other_name_seen; /* saw key with other name/addr */ 2058bc5531deSDag-Erling Smørgrav }; 2059bc5531deSDag-Erling Smørgrav 2060ae1f160dSDag-Erling Smørgrav static void 2061bc5531deSDag-Erling Smørgrav hostkeys_update_ctx_free(struct hostkeys_update_ctx *ctx) 2062bc5531deSDag-Erling Smørgrav { 2063bc5531deSDag-Erling Smørgrav size_t i; 2064bc5531deSDag-Erling Smørgrav 2065bc5531deSDag-Erling Smørgrav if (ctx == NULL) 2066bc5531deSDag-Erling Smørgrav return; 2067bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) 2068bc5531deSDag-Erling Smørgrav sshkey_free(ctx->keys[i]); 2069bc5531deSDag-Erling Smørgrav free(ctx->keys); 207019261079SEd Maste free(ctx->keys_match); 207119261079SEd Maste free(ctx->keys_verified); 2072bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nold; i++) 2073bc5531deSDag-Erling Smørgrav sshkey_free(ctx->old_keys[i]); 2074bc5531deSDag-Erling Smørgrav free(ctx->old_keys); 2075bc5531deSDag-Erling Smørgrav free(ctx->host_str); 2076bc5531deSDag-Erling Smørgrav free(ctx->ip_str); 2077bc5531deSDag-Erling Smørgrav free(ctx); 2078bc5531deSDag-Erling Smørgrav } 2079bc5531deSDag-Erling Smørgrav 208019261079SEd Maste /* 208119261079SEd Maste * Returns non-zero if a known_hosts hostname list is not of a form that 208219261079SEd Maste * can be handled by UpdateHostkeys. These include wildcard hostnames and 208319261079SEd Maste * hostnames lists that do not follow the form host[,ip]. 208419261079SEd Maste */ 208519261079SEd Maste static int 208619261079SEd Maste hostspec_is_complex(const char *hosts) 208719261079SEd Maste { 208819261079SEd Maste char *cp; 208919261079SEd Maste 209019261079SEd Maste /* wildcard */ 209119261079SEd Maste if (strchr(hosts, '*') != NULL || strchr(hosts, '?') != NULL) 209219261079SEd Maste return 1; 209319261079SEd Maste /* single host/ip = ok */ 209419261079SEd Maste if ((cp = strchr(hosts, ',')) == NULL) 209519261079SEd Maste return 0; 209619261079SEd Maste /* more than two entries on the line */ 209719261079SEd Maste if (strchr(cp + 1, ',') != NULL) 209819261079SEd Maste return 1; 209919261079SEd Maste /* XXX maybe parse cp+1 and ensure it is an IP? */ 210019261079SEd Maste return 0; 210119261079SEd Maste } 210219261079SEd Maste 210319261079SEd Maste /* callback to search for ctx->keys in known_hosts */ 2104bc5531deSDag-Erling Smørgrav static int 2105bc5531deSDag-Erling Smørgrav hostkeys_find(struct hostkey_foreach_line *l, void *_ctx) 2106bc5531deSDag-Erling Smørgrav { 2107bc5531deSDag-Erling Smørgrav struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; 2108bc5531deSDag-Erling Smørgrav size_t i; 2109bc5531deSDag-Erling Smørgrav struct sshkey **tmp; 2110bc5531deSDag-Erling Smørgrav 211119261079SEd Maste if (l->key == NULL) 2112bc5531deSDag-Erling Smørgrav return 0; 211319261079SEd Maste if (l->status != HKF_STATUS_MATCHED) { 211419261079SEd Maste /* Record if one of the keys appears on a non-matching line */ 211519261079SEd Maste for (i = 0; i < ctx->nkeys; i++) { 211619261079SEd Maste if (sshkey_equal(l->key, ctx->keys[i])) { 211719261079SEd Maste ctx->other_name_seen = 1; 211819261079SEd Maste debug3_f("found %s key under different " 211919261079SEd Maste "name/addr at %s:%ld", 212019261079SEd Maste sshkey_ssh_name(ctx->keys[i]), 212119261079SEd Maste l->path, l->linenum); 212219261079SEd Maste return 0; 212319261079SEd Maste } 212419261079SEd Maste } 212519261079SEd Maste return 0; 212619261079SEd Maste } 212719261079SEd Maste /* Don't proceed if revocation or CA markers are present */ 212819261079SEd Maste /* XXX relax this */ 212919261079SEd Maste if (l->marker != MRK_NONE) { 213019261079SEd Maste debug3_f("hostkeys file %s:%ld has CA/revocation marker", 213119261079SEd Maste l->path, l->linenum); 213219261079SEd Maste ctx->complex_hostspec = 1; 213319261079SEd Maste return 0; 213419261079SEd Maste } 213519261079SEd Maste 213619261079SEd Maste /* If CheckHostIP is enabled, then check for mismatched hostname/addr */ 213719261079SEd Maste if (ctx->ip_str != NULL && strchr(l->hosts, ',') != NULL) { 213819261079SEd Maste if ((l->match & HKF_MATCH_HOST) == 0) { 213919261079SEd Maste /* Record if address matched a different hostname. */ 214019261079SEd Maste ctx->other_name_seen = 1; 214119261079SEd Maste debug3_f("found address %s against different hostname " 214219261079SEd Maste "at %s:%ld", ctx->ip_str, l->path, l->linenum); 214319261079SEd Maste return 0; 214419261079SEd Maste } else if ((l->match & HKF_MATCH_IP) == 0) { 214519261079SEd Maste /* Record if hostname matched a different address. */ 214619261079SEd Maste ctx->other_name_seen = 1; 214719261079SEd Maste debug3_f("found hostname %s against different address " 214819261079SEd Maste "at %s:%ld", ctx->host_str, l->path, l->linenum); 214919261079SEd Maste } 215019261079SEd Maste } 215119261079SEd Maste 215219261079SEd Maste /* 215319261079SEd Maste * UpdateHostkeys is skipped for wildcard host names and hostnames 215419261079SEd Maste * that contain more than two entries (ssh never writes these). 215519261079SEd Maste */ 215619261079SEd Maste if (hostspec_is_complex(l->hosts)) { 215719261079SEd Maste debug3_f("hostkeys file %s:%ld complex host specification", 215819261079SEd Maste l->path, l->linenum); 215919261079SEd Maste ctx->complex_hostspec = 1; 216019261079SEd Maste return 0; 216119261079SEd Maste } 2162bc5531deSDag-Erling Smørgrav 2163bc5531deSDag-Erling Smørgrav /* Mark off keys we've already seen for this host */ 2164bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) { 216519261079SEd Maste if (!sshkey_equal(l->key, ctx->keys[i])) 216619261079SEd Maste continue; 216719261079SEd Maste debug3_f("found %s key at %s:%ld", 2168bc5531deSDag-Erling Smørgrav sshkey_ssh_name(ctx->keys[i]), l->path, l->linenum); 216919261079SEd Maste ctx->keys_match[i] |= l->match; 2170bc5531deSDag-Erling Smørgrav return 0; 2171bc5531deSDag-Erling Smørgrav } 2172bc5531deSDag-Erling Smørgrav /* This line contained a key that not offered by the server */ 217319261079SEd Maste debug3_f("deprecated %s key at %s:%ld", sshkey_ssh_name(l->key), 217419261079SEd Maste l->path, l->linenum); 21754f52dfbbSDag-Erling Smørgrav if ((tmp = recallocarray(ctx->old_keys, ctx->nold, ctx->nold + 1, 2176bc5531deSDag-Erling Smørgrav sizeof(*ctx->old_keys))) == NULL) 217719261079SEd Maste fatal_f("recallocarray failed nold = %zu", ctx->nold); 2178bc5531deSDag-Erling Smørgrav ctx->old_keys = tmp; 2179bc5531deSDag-Erling Smørgrav ctx->old_keys[ctx->nold++] = l->key; 2180bc5531deSDag-Erling Smørgrav l->key = NULL; 2181bc5531deSDag-Erling Smørgrav 2182bc5531deSDag-Erling Smørgrav return 0; 2183bc5531deSDag-Erling Smørgrav } 2184bc5531deSDag-Erling Smørgrav 218519261079SEd Maste /* callback to search for ctx->old_keys in known_hosts under other names */ 218619261079SEd Maste static int 218719261079SEd Maste hostkeys_check_old(struct hostkey_foreach_line *l, void *_ctx) 218819261079SEd Maste { 218919261079SEd Maste struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; 219019261079SEd Maste size_t i; 219119261079SEd Maste int hashed; 219219261079SEd Maste 219319261079SEd Maste /* only care about lines that *don't* match the active host spec */ 219419261079SEd Maste if (l->status == HKF_STATUS_MATCHED || l->key == NULL) 219519261079SEd Maste return 0; 219619261079SEd Maste 219719261079SEd Maste hashed = l->match & (HKF_MATCH_HOST_HASHED|HKF_MATCH_IP_HASHED); 219819261079SEd Maste for (i = 0; i < ctx->nold; i++) { 219919261079SEd Maste if (!sshkey_equal(l->key, ctx->old_keys[i])) 220019261079SEd Maste continue; 220119261079SEd Maste debug3_f("found deprecated %s key at %s:%ld as %s", 220219261079SEd Maste sshkey_ssh_name(ctx->old_keys[i]), l->path, l->linenum, 220319261079SEd Maste hashed ? "[HASHED]" : l->hosts); 220419261079SEd Maste ctx->old_key_seen = 1; 220519261079SEd Maste break; 220619261079SEd Maste } 220719261079SEd Maste return 0; 220819261079SEd Maste } 220919261079SEd Maste 221019261079SEd Maste /* 221119261079SEd Maste * Check known_hosts files for deprecated keys under other names. Returns 0 221219261079SEd Maste * on success or -1 on failure. Updates ctx->old_key_seen if deprecated keys 221319261079SEd Maste * exist under names other than the active hostname/IP. 221419261079SEd Maste */ 221519261079SEd Maste static int 221619261079SEd Maste check_old_keys_othernames(struct hostkeys_update_ctx *ctx) 221719261079SEd Maste { 221819261079SEd Maste size_t i; 221919261079SEd Maste int r; 222019261079SEd Maste 222119261079SEd Maste debug2_f("checking for %zu deprecated keys", ctx->nold); 222219261079SEd Maste for (i = 0; i < options.num_user_hostfiles; i++) { 222319261079SEd Maste debug3_f("searching %s for %s / %s", 222419261079SEd Maste options.user_hostfiles[i], ctx->host_str, 222519261079SEd Maste ctx->ip_str ? ctx->ip_str : "(none)"); 222619261079SEd Maste if ((r = hostkeys_foreach(options.user_hostfiles[i], 222719261079SEd Maste hostkeys_check_old, ctx, ctx->host_str, ctx->ip_str, 222819261079SEd Maste HKF_WANT_PARSE_KEY, 0)) != 0) { 222919261079SEd Maste if (r == SSH_ERR_SYSTEM_ERROR && errno == ENOENT) { 223019261079SEd Maste debug_f("hostkeys file %s does not exist", 223119261079SEd Maste options.user_hostfiles[i]); 223219261079SEd Maste continue; 223319261079SEd Maste } 223419261079SEd Maste error_fr(r, "hostkeys_foreach failed for %s", 223519261079SEd Maste options.user_hostfiles[i]); 223619261079SEd Maste return -1; 223719261079SEd Maste } 223819261079SEd Maste } 223919261079SEd Maste return 0; 224019261079SEd Maste } 224119261079SEd Maste 224219261079SEd Maste static void 224319261079SEd Maste hostkey_change_preamble(LogLevel loglevel) 224419261079SEd Maste { 224519261079SEd Maste do_log2(loglevel, "The server has updated its host keys."); 224619261079SEd Maste do_log2(loglevel, "These changes were verified by the server's " 224719261079SEd Maste "existing trusted key."); 224819261079SEd Maste } 224919261079SEd Maste 2250bc5531deSDag-Erling Smørgrav static void 2251bc5531deSDag-Erling Smørgrav update_known_hosts(struct hostkeys_update_ctx *ctx) 2252bc5531deSDag-Erling Smørgrav { 225319261079SEd Maste int r, was_raw = 0, first = 1; 225419261079SEd Maste int asking = options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK; 225519261079SEd Maste LogLevel loglevel = asking ? SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_VERBOSE; 2256bc5531deSDag-Erling Smørgrav char *fp, *response; 2257bc5531deSDag-Erling Smørgrav size_t i; 225819261079SEd Maste struct stat sb; 2259bc5531deSDag-Erling Smørgrav 2260bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) { 226119261079SEd Maste if (!ctx->keys_verified[i]) 2262bc5531deSDag-Erling Smørgrav continue; 2263bc5531deSDag-Erling Smørgrav if ((fp = sshkey_fingerprint(ctx->keys[i], 2264bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) 226519261079SEd Maste fatal_f("sshkey_fingerprint failed"); 226619261079SEd Maste if (first && asking) 226719261079SEd Maste hostkey_change_preamble(loglevel); 2268bc5531deSDag-Erling Smørgrav do_log2(loglevel, "Learned new hostkey: %s %s", 2269bc5531deSDag-Erling Smørgrav sshkey_type(ctx->keys[i]), fp); 227019261079SEd Maste first = 0; 2271bc5531deSDag-Erling Smørgrav free(fp); 2272bc5531deSDag-Erling Smørgrav } 2273bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nold; i++) { 2274bc5531deSDag-Erling Smørgrav if ((fp = sshkey_fingerprint(ctx->old_keys[i], 2275bc5531deSDag-Erling Smørgrav options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) 227619261079SEd Maste fatal_f("sshkey_fingerprint failed"); 227719261079SEd Maste if (first && asking) 227819261079SEd Maste hostkey_change_preamble(loglevel); 2279bc5531deSDag-Erling Smørgrav do_log2(loglevel, "Deprecating obsolete hostkey: %s %s", 2280bc5531deSDag-Erling Smørgrav sshkey_type(ctx->old_keys[i]), fp); 228119261079SEd Maste first = 0; 2282bc5531deSDag-Erling Smørgrav free(fp); 2283bc5531deSDag-Erling Smørgrav } 2284bc5531deSDag-Erling Smørgrav if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK) { 2285bc5531deSDag-Erling Smørgrav if (get_saved_tio() != NULL) { 2286bc5531deSDag-Erling Smørgrav leave_raw_mode(1); 2287bc5531deSDag-Erling Smørgrav was_raw = 1; 2288bc5531deSDag-Erling Smørgrav } 2289bc5531deSDag-Erling Smørgrav response = NULL; 2290bc5531deSDag-Erling Smørgrav for (i = 0; !quit_pending && i < 3; i++) { 2291bc5531deSDag-Erling Smørgrav free(response); 2292bc5531deSDag-Erling Smørgrav response = read_passphrase("Accept updated hostkeys? " 2293bc5531deSDag-Erling Smørgrav "(yes/no): ", RP_ECHO); 22944d3fc8b0SEd Maste if (response != NULL && strcasecmp(response, "yes") == 0) 2295bc5531deSDag-Erling Smørgrav break; 2296bc5531deSDag-Erling Smørgrav else if (quit_pending || response == NULL || 2297bc5531deSDag-Erling Smørgrav strcasecmp(response, "no") == 0) { 2298bc5531deSDag-Erling Smørgrav options.update_hostkeys = 0; 2299bc5531deSDag-Erling Smørgrav break; 2300bc5531deSDag-Erling Smørgrav } else { 2301bc5531deSDag-Erling Smørgrav do_log2(loglevel, "Please enter " 2302bc5531deSDag-Erling Smørgrav "\"yes\" or \"no\""); 2303bc5531deSDag-Erling Smørgrav } 2304bc5531deSDag-Erling Smørgrav } 2305bc5531deSDag-Erling Smørgrav if (quit_pending || i >= 3 || response == NULL) 2306bc5531deSDag-Erling Smørgrav options.update_hostkeys = 0; 2307bc5531deSDag-Erling Smørgrav free(response); 2308bc5531deSDag-Erling Smørgrav if (was_raw) 2309bc5531deSDag-Erling Smørgrav enter_raw_mode(1); 2310bc5531deSDag-Erling Smørgrav } 231119261079SEd Maste if (options.update_hostkeys == 0) 231219261079SEd Maste return; 2313bc5531deSDag-Erling Smørgrav /* 2314bc5531deSDag-Erling Smørgrav * Now that all the keys are verified, we can go ahead and replace 2315bc5531deSDag-Erling Smørgrav * them in known_hosts (assuming SSH_UPDATE_HOSTKEYS_ASK didn't 2316bc5531deSDag-Erling Smørgrav * cancel the operation). 2317bc5531deSDag-Erling Smørgrav */ 231819261079SEd Maste for (i = 0; i < options.num_user_hostfiles; i++) { 231919261079SEd Maste /* 232019261079SEd Maste * NB. keys are only added to hostfiles[0], for the rest we 232119261079SEd Maste * just delete the hostname entries. 232219261079SEd Maste */ 232319261079SEd Maste if (stat(options.user_hostfiles[i], &sb) != 0) { 232419261079SEd Maste if (errno == ENOENT) { 232519261079SEd Maste debug_f("known hosts file %s does not " 232619261079SEd Maste "exist", options.user_hostfiles[i]); 232719261079SEd Maste } else { 232819261079SEd Maste error_f("known hosts file %s " 232919261079SEd Maste "inaccessible: %s", 233019261079SEd Maste options.user_hostfiles[i], strerror(errno)); 233119261079SEd Maste } 233219261079SEd Maste continue; 233319261079SEd Maste } 233419261079SEd Maste if ((r = hostfile_replace_entries(options.user_hostfiles[i], 233519261079SEd Maste ctx->host_str, ctx->ip_str, 233619261079SEd Maste i == 0 ? ctx->keys : NULL, i == 0 ? ctx->nkeys : 0, 2337bc5531deSDag-Erling Smørgrav options.hash_known_hosts, 0, 233819261079SEd Maste options.fingerprint_hash)) != 0) { 233919261079SEd Maste error_fr(r, "hostfile_replace_entries failed for %s", 234019261079SEd Maste options.user_hostfiles[i]); 234119261079SEd Maste } 234219261079SEd Maste } 2343bc5531deSDag-Erling Smørgrav } 2344bc5531deSDag-Erling Smørgrav 2345bc5531deSDag-Erling Smørgrav static void 2346f374ba41SEd Maste client_global_hostkeys_prove_confirm(struct ssh *ssh, int type, 23474f52dfbbSDag-Erling Smørgrav u_int32_t seq, void *_ctx) 2348bc5531deSDag-Erling Smørgrav { 2349bc5531deSDag-Erling Smørgrav struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; 2350bc5531deSDag-Erling Smørgrav size_t i, ndone; 2351bc5531deSDag-Erling Smørgrav struct sshbuf *signdata; 23521323ec57SEd Maste int r, plaintype; 2353bc5531deSDag-Erling Smørgrav const u_char *sig; 23541323ec57SEd Maste const char *rsa_kexalg = NULL; 23551323ec57SEd Maste char *alg = NULL; 2356bc5531deSDag-Erling Smørgrav size_t siglen; 2357bc5531deSDag-Erling Smørgrav 2358bc5531deSDag-Erling Smørgrav if (ctx->nnew == 0) 235919261079SEd Maste fatal_f("ctx->nnew == 0"); /* sanity */ 2360bc5531deSDag-Erling Smørgrav if (type != SSH2_MSG_REQUEST_SUCCESS) { 2361bc5531deSDag-Erling Smørgrav error("Server failed to confirm ownership of " 2362bc5531deSDag-Erling Smørgrav "private host keys"); 2363bc5531deSDag-Erling Smørgrav hostkeys_update_ctx_free(ctx); 2364bc5531deSDag-Erling Smørgrav return; 2365bc5531deSDag-Erling Smørgrav } 23661323ec57SEd Maste if (sshkey_type_plain(sshkey_type_from_name( 23671323ec57SEd Maste ssh->kex->hostkey_alg)) == KEY_RSA) 23681323ec57SEd Maste rsa_kexalg = ssh->kex->hostkey_alg; 2369bc5531deSDag-Erling Smørgrav if ((signdata = sshbuf_new()) == NULL) 237019261079SEd Maste fatal_f("sshbuf_new failed"); 2371bc5531deSDag-Erling Smørgrav /* 2372bc5531deSDag-Erling Smørgrav * Expect a signature for each of the ctx->nnew private keys we 2373bc5531deSDag-Erling Smørgrav * haven't seen before. They will be in the same order as the 237419261079SEd Maste * ctx->keys where the corresponding ctx->keys_match[i] == 0. 2375bc5531deSDag-Erling Smørgrav */ 2376bc5531deSDag-Erling Smørgrav for (ndone = i = 0; i < ctx->nkeys; i++) { 237719261079SEd Maste if (ctx->keys_match[i]) 2378bc5531deSDag-Erling Smørgrav continue; 23791323ec57SEd Maste plaintype = sshkey_type_plain(ctx->keys[i]->type); 2380bc5531deSDag-Erling Smørgrav /* Prepare data to be signed: session ID, unique string, key */ 2381bc5531deSDag-Erling Smørgrav sshbuf_reset(signdata); 2382bc5531deSDag-Erling Smørgrav if ( (r = sshbuf_put_cstring(signdata, 2383bc5531deSDag-Erling Smørgrav "hostkeys-prove-00@openssh.com")) != 0 || 238419261079SEd Maste (r = sshbuf_put_stringb(signdata, 238519261079SEd Maste ssh->kex->session_id)) != 0 || 2386bc5531deSDag-Erling Smørgrav (r = sshkey_puts(ctx->keys[i], signdata)) != 0) 238719261079SEd Maste fatal_fr(r, "compose signdata"); 2388bc5531deSDag-Erling Smørgrav /* Extract and verify signature */ 2389bc5531deSDag-Erling Smørgrav if ((r = sshpkt_get_string_direct(ssh, &sig, &siglen)) != 0) { 239019261079SEd Maste error_fr(r, "parse sig"); 2391bc5531deSDag-Erling Smørgrav goto out; 2392bc5531deSDag-Erling Smørgrav } 23931323ec57SEd Maste if ((r = sshkey_get_sigtype(sig, siglen, &alg)) != 0) { 23941323ec57SEd Maste error_fr(r, "server gave unintelligible signature " 23951323ec57SEd Maste "for %s key %zu", sshkey_type(ctx->keys[i]), i); 23961323ec57SEd Maste goto out; 23971323ec57SEd Maste } 239847dd1d1bSDag-Erling Smørgrav /* 23991323ec57SEd Maste * Special case for RSA keys: if a RSA hostkey was negotiated, 24001323ec57SEd Maste * then use its signature type for verification of RSA hostkey 24011323ec57SEd Maste * proofs. Otherwise, accept only RSA-SHA256/512 signatures. 240247dd1d1bSDag-Erling Smørgrav */ 24031323ec57SEd Maste if (plaintype == KEY_RSA && rsa_kexalg == NULL && 24041323ec57SEd Maste match_pattern_list(alg, HOSTKEY_PROOF_RSA_ALGS, 0) != 1) { 24051323ec57SEd Maste debug_f("server used untrusted RSA signature algorithm " 24061323ec57SEd Maste "%s for key %zu, disregarding", alg, i); 24071323ec57SEd Maste free(alg); 24081323ec57SEd Maste /* zap the key from the list */ 24091323ec57SEd Maste sshkey_free(ctx->keys[i]); 24101323ec57SEd Maste ctx->keys[i] = NULL; 24111323ec57SEd Maste ndone++; 24121323ec57SEd Maste continue; 24131323ec57SEd Maste } 24141323ec57SEd Maste debug3_f("verify %s key %zu using sigalg %s", 24151323ec57SEd Maste sshkey_type(ctx->keys[i]), i, alg); 24161323ec57SEd Maste free(alg); 2417bc5531deSDag-Erling Smørgrav if ((r = sshkey_verify(ctx->keys[i], sig, siglen, 241847dd1d1bSDag-Erling Smørgrav sshbuf_ptr(signdata), sshbuf_len(signdata), 24191323ec57SEd Maste plaintype == KEY_RSA ? rsa_kexalg : NULL, 0, NULL)) != 0) { 242019261079SEd Maste error_fr(r, "server gave bad signature for %s key %zu", 242119261079SEd Maste sshkey_type(ctx->keys[i]), i); 2422bc5531deSDag-Erling Smørgrav goto out; 2423bc5531deSDag-Erling Smørgrav } 2424bc5531deSDag-Erling Smørgrav /* Key is good. Mark it as 'seen' */ 242519261079SEd Maste ctx->keys_verified[i] = 1; 2426bc5531deSDag-Erling Smørgrav ndone++; 2427bc5531deSDag-Erling Smørgrav } 242819261079SEd Maste /* Shouldn't happen */ 2429bc5531deSDag-Erling Smørgrav if (ndone != ctx->nnew) 243019261079SEd Maste fatal_f("ndone != ctx->nnew (%zu / %zu)", ndone, ctx->nnew); 243119261079SEd Maste if ((r = sshpkt_get_end(ssh)) != 0) { 243219261079SEd Maste error_f("protocol error"); 243319261079SEd Maste goto out; 243419261079SEd Maste } 2435bc5531deSDag-Erling Smørgrav 2436bc5531deSDag-Erling Smørgrav /* Make the edits to known_hosts */ 2437bc5531deSDag-Erling Smørgrav update_known_hosts(ctx); 2438bc5531deSDag-Erling Smørgrav out: 2439bc5531deSDag-Erling Smørgrav hostkeys_update_ctx_free(ctx); 2440f374ba41SEd Maste hostkeys_update_complete = 1; 2441f374ba41SEd Maste client_repledge(); 2442bc5531deSDag-Erling Smørgrav } 2443bc5531deSDag-Erling Smørgrav 2444bc5531deSDag-Erling Smørgrav /* 2445d93a896eSDag-Erling Smørgrav * Returns non-zero if the key is accepted by HostkeyAlgorithms. 2446d93a896eSDag-Erling Smørgrav * Made slightly less trivial by the multiple RSA signature algorithm names. 2447d93a896eSDag-Erling Smørgrav */ 2448d93a896eSDag-Erling Smørgrav static int 2449d93a896eSDag-Erling Smørgrav key_accepted_by_hostkeyalgs(const struct sshkey *key) 2450d93a896eSDag-Erling Smørgrav { 2451d93a896eSDag-Erling Smørgrav const char *ktype = sshkey_ssh_name(key); 245219261079SEd Maste const char *hostkeyalgs = options.hostkeyalgorithms; 2453d93a896eSDag-Erling Smørgrav 2454535af610SEd Maste if (key->type == KEY_UNSPEC) 2455d93a896eSDag-Erling Smørgrav return 0; 2456d93a896eSDag-Erling Smørgrav if (key->type == KEY_RSA && 2457d93a896eSDag-Erling Smørgrav (match_pattern_list("rsa-sha2-256", hostkeyalgs, 0) == 1 || 2458d93a896eSDag-Erling Smørgrav match_pattern_list("rsa-sha2-512", hostkeyalgs, 0) == 1)) 2459d93a896eSDag-Erling Smørgrav return 1; 2460d93a896eSDag-Erling Smørgrav return match_pattern_list(ktype, hostkeyalgs, 0) == 1; 2461d93a896eSDag-Erling Smørgrav } 2462d93a896eSDag-Erling Smørgrav 2463d93a896eSDag-Erling Smørgrav /* 2464bc5531deSDag-Erling Smørgrav * Handle hostkeys-00@openssh.com global request to inform the client of all 2465bc5531deSDag-Erling Smørgrav * the server's hostkeys. The keys are checked against the user's 2466bc5531deSDag-Erling Smørgrav * HostkeyAlgorithms preference before they are accepted. 2467bc5531deSDag-Erling Smørgrav */ 2468bc5531deSDag-Erling Smørgrav static int 246919261079SEd Maste client_input_hostkeys(struct ssh *ssh) 2470bc5531deSDag-Erling Smørgrav { 2471bc5531deSDag-Erling Smørgrav const u_char *blob = NULL; 2472bc5531deSDag-Erling Smørgrav size_t i, len = 0; 2473bc5531deSDag-Erling Smørgrav struct sshbuf *buf = NULL; 2474bc5531deSDag-Erling Smørgrav struct sshkey *key = NULL, **tmp; 2475f374ba41SEd Maste int r, prove_sent = 0; 2476bc5531deSDag-Erling Smørgrav char *fp; 2477bc5531deSDag-Erling Smørgrav static int hostkeys_seen = 0; /* XXX use struct ssh */ 2478bc5531deSDag-Erling Smørgrav extern struct sockaddr_storage hostaddr; /* XXX from ssh.c */ 2479bc5531deSDag-Erling Smørgrav struct hostkeys_update_ctx *ctx = NULL; 248019261079SEd Maste u_int want; 2481bc5531deSDag-Erling Smørgrav 2482bc5531deSDag-Erling Smørgrav if (hostkeys_seen) 248319261079SEd Maste fatal_f("server already sent hostkeys"); 2484f374ba41SEd Maste if (!can_update_hostkeys()) 2485bc5531deSDag-Erling Smørgrav return 1; 2486f374ba41SEd Maste hostkeys_seen = 1; 2487bc5531deSDag-Erling Smørgrav 2488bc5531deSDag-Erling Smørgrav ctx = xcalloc(1, sizeof(*ctx)); 2489bc5531deSDag-Erling Smørgrav while (ssh_packet_remaining(ssh) > 0) { 2490bc5531deSDag-Erling Smørgrav sshkey_free(key); 2491bc5531deSDag-Erling Smørgrav key = NULL; 2492bc5531deSDag-Erling Smørgrav if ((r = sshpkt_get_string_direct(ssh, &blob, &len)) != 0) { 249319261079SEd Maste error_fr(r, "parse key"); 2494bc5531deSDag-Erling Smørgrav goto out; 2495bc5531deSDag-Erling Smørgrav } 2496bc5531deSDag-Erling Smørgrav if ((r = sshkey_from_blob(blob, len, &key)) != 0) { 249719261079SEd Maste do_log2_fr(r, r == SSH_ERR_KEY_TYPE_UNKNOWN ? 249819261079SEd Maste SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_ERROR, 249919261079SEd Maste "convert key"); 250019261079SEd Maste continue; 2501bc5531deSDag-Erling Smørgrav } 2502bc5531deSDag-Erling Smørgrav fp = sshkey_fingerprint(key, options.fingerprint_hash, 2503bc5531deSDag-Erling Smørgrav SSH_FP_DEFAULT); 250419261079SEd Maste debug3_f("received %s key %s", sshkey_type(key), fp); 2505bc5531deSDag-Erling Smørgrav free(fp); 2506eccfee6eSDag-Erling Smørgrav 2507d93a896eSDag-Erling Smørgrav if (!key_accepted_by_hostkeyalgs(key)) { 250819261079SEd Maste debug3_f("%s key not permitted by " 250919261079SEd Maste "HostkeyAlgorithms", sshkey_ssh_name(key)); 2510bc5531deSDag-Erling Smørgrav continue; 2511bc5531deSDag-Erling Smørgrav } 2512bc5531deSDag-Erling Smørgrav /* Skip certs */ 2513bc5531deSDag-Erling Smørgrav if (sshkey_is_cert(key)) { 251419261079SEd Maste debug3_f("%s key is a certificate; skipping", 251519261079SEd Maste sshkey_ssh_name(key)); 2516bc5531deSDag-Erling Smørgrav continue; 2517bc5531deSDag-Erling Smørgrav } 2518bc5531deSDag-Erling Smørgrav /* Ensure keys are unique */ 2519bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) { 2520bc5531deSDag-Erling Smørgrav if (sshkey_equal(key, ctx->keys[i])) { 252119261079SEd Maste error_f("received duplicated %s host key", 252219261079SEd Maste sshkey_ssh_name(key)); 2523bc5531deSDag-Erling Smørgrav goto out; 2524bc5531deSDag-Erling Smørgrav } 2525bc5531deSDag-Erling Smørgrav } 2526bc5531deSDag-Erling Smørgrav /* Key is good, record it */ 25274f52dfbbSDag-Erling Smørgrav if ((tmp = recallocarray(ctx->keys, ctx->nkeys, ctx->nkeys + 1, 2528bc5531deSDag-Erling Smørgrav sizeof(*ctx->keys))) == NULL) 252919261079SEd Maste fatal_f("recallocarray failed nkeys = %zu", 253019261079SEd Maste ctx->nkeys); 2531bc5531deSDag-Erling Smørgrav ctx->keys = tmp; 2532bc5531deSDag-Erling Smørgrav ctx->keys[ctx->nkeys++] = key; 2533bc5531deSDag-Erling Smørgrav key = NULL; 2534bc5531deSDag-Erling Smørgrav } 2535bc5531deSDag-Erling Smørgrav 2536bc5531deSDag-Erling Smørgrav if (ctx->nkeys == 0) { 253719261079SEd Maste debug_f("server sent no hostkeys"); 2538bc5531deSDag-Erling Smørgrav goto out; 2539bc5531deSDag-Erling Smørgrav } 2540bc5531deSDag-Erling Smørgrav 254119261079SEd Maste if ((ctx->keys_match = calloc(ctx->nkeys, 254219261079SEd Maste sizeof(*ctx->keys_match))) == NULL || 254319261079SEd Maste (ctx->keys_verified = calloc(ctx->nkeys, 254419261079SEd Maste sizeof(*ctx->keys_verified))) == NULL) 254519261079SEd Maste fatal_f("calloc failed"); 2546bc5531deSDag-Erling Smørgrav 2547bc5531deSDag-Erling Smørgrav get_hostfile_hostname_ipaddr(host, 2548bc5531deSDag-Erling Smørgrav options.check_host_ip ? (struct sockaddr *)&hostaddr : NULL, 2549bc5531deSDag-Erling Smørgrav options.port, &ctx->host_str, 2550bc5531deSDag-Erling Smørgrav options.check_host_ip ? &ctx->ip_str : NULL); 2551bc5531deSDag-Erling Smørgrav 2552bc5531deSDag-Erling Smørgrav /* Find which keys we already know about. */ 255319261079SEd Maste for (i = 0; i < options.num_user_hostfiles; i++) { 255419261079SEd Maste debug_f("searching %s for %s / %s", 255519261079SEd Maste options.user_hostfiles[i], ctx->host_str, 255619261079SEd Maste ctx->ip_str ? ctx->ip_str : "(none)"); 255719261079SEd Maste if ((r = hostkeys_foreach(options.user_hostfiles[i], 255819261079SEd Maste hostkeys_find, ctx, ctx->host_str, ctx->ip_str, 255919261079SEd Maste HKF_WANT_PARSE_KEY, 0)) != 0) { 256019261079SEd Maste if (r == SSH_ERR_SYSTEM_ERROR && errno == ENOENT) { 256119261079SEd Maste debug_f("hostkeys file %s does not exist", 256219261079SEd Maste options.user_hostfiles[i]); 256319261079SEd Maste continue; 256419261079SEd Maste } 256519261079SEd Maste error_fr(r, "hostkeys_foreach failed for %s", 256619261079SEd Maste options.user_hostfiles[i]); 2567bc5531deSDag-Erling Smørgrav goto out; 2568bc5531deSDag-Erling Smørgrav } 256919261079SEd Maste } 2570bc5531deSDag-Erling Smørgrav 2571bc5531deSDag-Erling Smørgrav /* Figure out if we have any new keys to add */ 257219261079SEd Maste ctx->nnew = ctx->nincomplete = 0; 257319261079SEd Maste want = HKF_MATCH_HOST | ( options.check_host_ip ? HKF_MATCH_IP : 0); 2574bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) { 257519261079SEd Maste if (ctx->keys_match[i] == 0) 2576bc5531deSDag-Erling Smørgrav ctx->nnew++; 257719261079SEd Maste if ((ctx->keys_match[i] & want) != want) 257819261079SEd Maste ctx->nincomplete++; 2579bc5531deSDag-Erling Smørgrav } 2580bc5531deSDag-Erling Smørgrav 258119261079SEd Maste debug3_f("%zu server keys: %zu new, %zu retained, " 258219261079SEd Maste "%zu incomplete match. %zu to remove", ctx->nkeys, ctx->nnew, 258319261079SEd Maste ctx->nkeys - ctx->nnew - ctx->nincomplete, 258419261079SEd Maste ctx->nincomplete, ctx->nold); 2585bc5531deSDag-Erling Smørgrav 258619261079SEd Maste if (ctx->nnew == 0 && ctx->nold == 0) { 258719261079SEd Maste debug_f("no new or deprecated keys from server"); 258819261079SEd Maste goto out; 258919261079SEd Maste } 259019261079SEd Maste 259119261079SEd Maste /* Various reasons why we cannot proceed with the update */ 259219261079SEd Maste if (ctx->complex_hostspec) { 259319261079SEd Maste debug_f("CA/revocation marker, manual host list or wildcard " 259419261079SEd Maste "host pattern found, skipping UserKnownHostsFile update"); 259519261079SEd Maste goto out; 259619261079SEd Maste } 259719261079SEd Maste if (ctx->other_name_seen) { 259819261079SEd Maste debug_f("host key found matching a different name/address, " 259919261079SEd Maste "skipping UserKnownHostsFile update"); 260019261079SEd Maste goto out; 260119261079SEd Maste } 2602bc5531deSDag-Erling Smørgrav /* 260319261079SEd Maste * If removing keys, check whether they appear under different 260419261079SEd Maste * names/addresses and refuse to proceed if they do. This avoids 260519261079SEd Maste * cases such as hosts with multiple names becoming inconsistent 260619261079SEd Maste * with regards to CheckHostIP entries. 260719261079SEd Maste * XXX UpdateHostkeys=force to override this (and other) checks? 260819261079SEd Maste */ 260919261079SEd Maste if (ctx->nold != 0) { 261019261079SEd Maste if (check_old_keys_othernames(ctx) != 0) 261119261079SEd Maste goto out; /* error already logged */ 261219261079SEd Maste if (ctx->old_key_seen) { 261319261079SEd Maste debug_f("key(s) for %s%s%s exist under other names; " 261419261079SEd Maste "skipping UserKnownHostsFile update", 261519261079SEd Maste ctx->host_str, ctx->ip_str == NULL ? "" : ",", 261619261079SEd Maste ctx->ip_str == NULL ? "" : ctx->ip_str); 261719261079SEd Maste goto out; 261819261079SEd Maste } 261919261079SEd Maste } 262019261079SEd Maste 262119261079SEd Maste if (ctx->nnew == 0) { 262219261079SEd Maste /* 262319261079SEd Maste * We have some keys to remove or fix matching for. 262419261079SEd Maste * We can proceed to do this without requiring a fresh proof 262519261079SEd Maste * from the server. 262619261079SEd Maste */ 262719261079SEd Maste update_known_hosts(ctx); 262819261079SEd Maste goto out; 262919261079SEd Maste } 263019261079SEd Maste /* 263119261079SEd Maste * We have received previously-unseen keys from the server. 2632bc5531deSDag-Erling Smørgrav * Ask the server to confirm ownership of the private halves. 2633bc5531deSDag-Erling Smørgrav */ 263419261079SEd Maste debug3_f("asking server to prove ownership for %zu keys", ctx->nnew); 2635bc5531deSDag-Erling Smørgrav if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || 2636bc5531deSDag-Erling Smørgrav (r = sshpkt_put_cstring(ssh, 2637bc5531deSDag-Erling Smørgrav "hostkeys-prove-00@openssh.com")) != 0 || 2638bc5531deSDag-Erling Smørgrav (r = sshpkt_put_u8(ssh, 1)) != 0) /* bool: want reply */ 263919261079SEd Maste fatal_fr(r, "prepare hostkeys-prove"); 2640bc5531deSDag-Erling Smørgrav if ((buf = sshbuf_new()) == NULL) 264119261079SEd Maste fatal_f("sshbuf_new"); 2642bc5531deSDag-Erling Smørgrav for (i = 0; i < ctx->nkeys; i++) { 264319261079SEd Maste if (ctx->keys_match[i]) 2644bc5531deSDag-Erling Smørgrav continue; 2645bc5531deSDag-Erling Smørgrav sshbuf_reset(buf); 264619261079SEd Maste if ((r = sshkey_putb(ctx->keys[i], buf)) != 0 || 264719261079SEd Maste (r = sshpkt_put_stringb(ssh, buf)) != 0) 264819261079SEd Maste fatal_fr(r, "assemble hostkeys-prove"); 2649bc5531deSDag-Erling Smørgrav } 2650bc5531deSDag-Erling Smørgrav if ((r = sshpkt_send(ssh)) != 0) 265119261079SEd Maste fatal_fr(r, "send hostkeys-prove"); 2652bc5531deSDag-Erling Smørgrav client_register_global_confirm( 2653f374ba41SEd Maste client_global_hostkeys_prove_confirm, ctx); 2654bc5531deSDag-Erling Smørgrav ctx = NULL; /* will be freed in callback */ 2655f374ba41SEd Maste prove_sent = 1; 2656bc5531deSDag-Erling Smørgrav 2657bc5531deSDag-Erling Smørgrav /* Success */ 2658bc5531deSDag-Erling Smørgrav out: 2659bc5531deSDag-Erling Smørgrav hostkeys_update_ctx_free(ctx); 2660bc5531deSDag-Erling Smørgrav sshkey_free(key); 2661bc5531deSDag-Erling Smørgrav sshbuf_free(buf); 2662f374ba41SEd Maste if (!prove_sent) { 2663f374ba41SEd Maste /* UpdateHostkeys handling completed */ 2664f374ba41SEd Maste hostkeys_update_complete = 1; 2665f374ba41SEd Maste client_repledge(); 2666f374ba41SEd Maste } 2667bc5531deSDag-Erling Smørgrav /* 2668bc5531deSDag-Erling Smørgrav * NB. Return success for all cases. The server doesn't need to know 2669bc5531deSDag-Erling Smørgrav * what the client does with its hosts file. 2670bc5531deSDag-Erling Smørgrav */ 2671bc5531deSDag-Erling Smørgrav return 1; 2672bc5531deSDag-Erling Smørgrav } 2673bc5531deSDag-Erling Smørgrav 2674bc5531deSDag-Erling Smørgrav static int 26754f52dfbbSDag-Erling Smørgrav client_input_global_request(int type, u_int32_t seq, struct ssh *ssh) 2676ae1f160dSDag-Erling Smørgrav { 2677ae1f160dSDag-Erling Smørgrav char *rtype; 267819261079SEd Maste u_char want_reply; 267919261079SEd Maste int r, success = 0; 2680a04a10f8SKris Kennaway 268119261079SEd Maste if ((r = sshpkt_get_cstring(ssh, &rtype, NULL)) != 0 || 268219261079SEd Maste (r = sshpkt_get_u8(ssh, &want_reply)) != 0) 268319261079SEd Maste goto out; 2684efcad6b7SDag-Erling Smørgrav debug("client_input_global_request: rtype %s want_reply %d", 2685efcad6b7SDag-Erling Smørgrav rtype, want_reply); 2686bc5531deSDag-Erling Smørgrav if (strcmp(rtype, "hostkeys-00@openssh.com") == 0) 268719261079SEd Maste success = client_input_hostkeys(ssh); 2688ae1f160dSDag-Erling Smørgrav if (want_reply) { 268919261079SEd Maste if ((r = sshpkt_start(ssh, success ? SSH2_MSG_REQUEST_SUCCESS : 269019261079SEd Maste SSH2_MSG_REQUEST_FAILURE)) != 0 || 269119261079SEd Maste (r = sshpkt_send(ssh)) != 0 || 269219261079SEd Maste (r = ssh_packet_write_wait(ssh)) != 0) 269319261079SEd Maste goto out; 2694ae1f160dSDag-Erling Smørgrav } 269519261079SEd Maste r = 0; 269619261079SEd Maste out: 2697e4a9863fSDag-Erling Smørgrav free(rtype); 269819261079SEd Maste return r; 269919261079SEd Maste } 270019261079SEd Maste 270119261079SEd Maste static void 270219261079SEd Maste client_send_env(struct ssh *ssh, int id, const char *name, const char *val) 270319261079SEd Maste { 270419261079SEd Maste int r; 270519261079SEd Maste 270619261079SEd Maste debug("channel %d: setting env %s = \"%s\"", id, name, val); 270719261079SEd Maste channel_request_start(ssh, id, "env", 0); 270819261079SEd Maste if ((r = sshpkt_put_cstring(ssh, name)) != 0 || 270919261079SEd Maste (r = sshpkt_put_cstring(ssh, val)) != 0 || 271019261079SEd Maste (r = sshpkt_send(ssh)) != 0) 271119261079SEd Maste fatal_fr(r, "send setenv"); 2712ae1f160dSDag-Erling Smørgrav } 2713ae1f160dSDag-Erling Smørgrav 2714d74d50a8SDag-Erling Smørgrav void 27154f52dfbbSDag-Erling Smørgrav client_session2_setup(struct ssh *ssh, int id, int want_tty, int want_subsystem, 2716190cef3dSDag-Erling Smørgrav const char *term, struct termios *tiop, int in_fd, struct sshbuf *cmd, 2717190cef3dSDag-Erling Smørgrav char **env) 2718d74d50a8SDag-Erling Smørgrav { 271938a52bd3SEd Maste size_t i, j, len; 272038a52bd3SEd Maste int matched, r; 2721190cef3dSDag-Erling Smørgrav char *name, *val; 27225e8dbd04SDag-Erling Smørgrav Channel *c = NULL; 2723d74d50a8SDag-Erling Smørgrav 272419261079SEd Maste debug2_f("id %d", id); 2725d74d50a8SDag-Erling Smørgrav 27264f52dfbbSDag-Erling Smørgrav if ((c = channel_lookup(ssh, id)) == NULL) 272719261079SEd Maste fatal_f("channel %d: unknown channel", id); 27285e8dbd04SDag-Erling Smørgrav 272919261079SEd Maste ssh_packet_set_interactive(ssh, want_tty, 27304a421b63SDag-Erling Smørgrav options.ip_qos_interactive, options.ip_qos_bulk); 27314a421b63SDag-Erling Smørgrav 2732d74d50a8SDag-Erling Smørgrav if (want_tty) { 2733d74d50a8SDag-Erling Smørgrav struct winsize ws; 2734d74d50a8SDag-Erling Smørgrav 2735d74d50a8SDag-Erling Smørgrav /* Store window size in the packet. */ 273619261079SEd Maste if (ioctl(in_fd, TIOCGWINSZ, &ws) == -1) 2737d74d50a8SDag-Erling Smørgrav memset(&ws, 0, sizeof(ws)); 2738d74d50a8SDag-Erling Smørgrav 27394f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, id, "pty-req", 1); 27404f52dfbbSDag-Erling Smørgrav client_expect_confirm(ssh, id, "PTY allocation", CONFIRM_TTY); 274119261079SEd Maste if ((r = sshpkt_put_cstring(ssh, term != NULL ? term : "")) 274219261079SEd Maste != 0 || 274319261079SEd Maste (r = sshpkt_put_u32(ssh, (u_int)ws.ws_col)) != 0 || 274419261079SEd Maste (r = sshpkt_put_u32(ssh, (u_int)ws.ws_row)) != 0 || 274519261079SEd Maste (r = sshpkt_put_u32(ssh, (u_int)ws.ws_xpixel)) != 0 || 274619261079SEd Maste (r = sshpkt_put_u32(ssh, (u_int)ws.ws_ypixel)) != 0) 274719261079SEd Maste fatal_fr(r, "build pty-req"); 2748d4af9e69SDag-Erling Smørgrav if (tiop == NULL) 2749d4af9e69SDag-Erling Smørgrav tiop = get_saved_tio(); 2750190cef3dSDag-Erling Smørgrav ssh_tty_make_modes(ssh, -1, tiop); 275119261079SEd Maste if ((r = sshpkt_send(ssh)) != 0) 275219261079SEd Maste fatal_fr(r, "send pty-req"); 2753d74d50a8SDag-Erling Smørgrav /* XXX wait for reply */ 27545e8dbd04SDag-Erling Smørgrav c->client_tty = 1; 2755d74d50a8SDag-Erling Smørgrav } 2756d74d50a8SDag-Erling Smørgrav 2757d74d50a8SDag-Erling Smørgrav /* Transfer any environment variables from client to server */ 2758d74d50a8SDag-Erling Smørgrav if (options.num_send_env != 0 && env != NULL) { 2759d74d50a8SDag-Erling Smørgrav debug("Sending environment."); 2760d74d50a8SDag-Erling Smørgrav for (i = 0; env[i] != NULL; i++) { 2761d74d50a8SDag-Erling Smørgrav /* Split */ 2762d74d50a8SDag-Erling Smørgrav name = xstrdup(env[i]); 2763d74d50a8SDag-Erling Smørgrav if ((val = strchr(name, '=')) == NULL) { 2764e4a9863fSDag-Erling Smørgrav free(name); 2765d74d50a8SDag-Erling Smørgrav continue; 2766d74d50a8SDag-Erling Smørgrav } 2767d74d50a8SDag-Erling Smørgrav *val++ = '\0'; 2768d74d50a8SDag-Erling Smørgrav 2769d74d50a8SDag-Erling Smørgrav matched = 0; 2770d74d50a8SDag-Erling Smørgrav for (j = 0; j < options.num_send_env; j++) { 2771d74d50a8SDag-Erling Smørgrav if (match_pattern(name, options.send_env[j])) { 2772d74d50a8SDag-Erling Smørgrav matched = 1; 2773d74d50a8SDag-Erling Smørgrav break; 2774d74d50a8SDag-Erling Smørgrav } 2775d74d50a8SDag-Erling Smørgrav } 2776d74d50a8SDag-Erling Smørgrav if (!matched) { 2777d74d50a8SDag-Erling Smørgrav debug3("Ignored env %s", name); 2778e4a9863fSDag-Erling Smørgrav free(name); 2779d74d50a8SDag-Erling Smørgrav continue; 2780d74d50a8SDag-Erling Smørgrav } 278119261079SEd Maste client_send_env(ssh, id, name, val); 2782e4a9863fSDag-Erling Smørgrav free(name); 2783d74d50a8SDag-Erling Smørgrav } 2784d74d50a8SDag-Erling Smørgrav } 2785190cef3dSDag-Erling Smørgrav for (i = 0; i < options.num_setenv; i++) { 2786190cef3dSDag-Erling Smørgrav /* Split */ 2787190cef3dSDag-Erling Smørgrav name = xstrdup(options.setenv[i]); 2788190cef3dSDag-Erling Smørgrav if ((val = strchr(name, '=')) == NULL) { 2789190cef3dSDag-Erling Smørgrav free(name); 2790190cef3dSDag-Erling Smørgrav continue; 2791190cef3dSDag-Erling Smørgrav } 2792190cef3dSDag-Erling Smørgrav *val++ = '\0'; 279319261079SEd Maste client_send_env(ssh, id, name, val); 2794190cef3dSDag-Erling Smørgrav free(name); 2795190cef3dSDag-Erling Smørgrav } 2796190cef3dSDag-Erling Smørgrav 2797190cef3dSDag-Erling Smørgrav len = sshbuf_len(cmd); 2798d74d50a8SDag-Erling Smørgrav if (len > 0) { 2799d74d50a8SDag-Erling Smørgrav if (len > 900) 2800d74d50a8SDag-Erling Smørgrav len = 900; 2801d74d50a8SDag-Erling Smørgrav if (want_subsystem) { 2802d4af9e69SDag-Erling Smørgrav debug("Sending subsystem: %.*s", 280338a52bd3SEd Maste (int)len, (const u_char*)sshbuf_ptr(cmd)); 28044f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, id, "subsystem", 1); 28054f52dfbbSDag-Erling Smørgrav client_expect_confirm(ssh, id, "subsystem", 28064f52dfbbSDag-Erling Smørgrav CONFIRM_CLOSE); 2807d74d50a8SDag-Erling Smørgrav } else { 2808d4af9e69SDag-Erling Smørgrav debug("Sending command: %.*s", 280938a52bd3SEd Maste (int)len, (const u_char*)sshbuf_ptr(cmd)); 28104f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, id, "exec", 1); 28114f52dfbbSDag-Erling Smørgrav client_expect_confirm(ssh, id, "exec", CONFIRM_CLOSE); 2812d74d50a8SDag-Erling Smørgrav } 281319261079SEd Maste if ((r = sshpkt_put_stringb(ssh, cmd)) != 0 || 281419261079SEd Maste (r = sshpkt_send(ssh)) != 0) 281519261079SEd Maste fatal_fr(r, "send command"); 2816d74d50a8SDag-Erling Smørgrav } else { 28174f52dfbbSDag-Erling Smørgrav channel_request_start(ssh, id, "shell", 1); 28184f52dfbbSDag-Erling Smørgrav client_expect_confirm(ssh, id, "shell", CONFIRM_CLOSE); 281919261079SEd Maste if ((r = sshpkt_send(ssh)) != 0) 282019261079SEd Maste fatal_fr(r, "send shell"); 2821d74d50a8SDag-Erling Smørgrav } 2822f374ba41SEd Maste 2823f374ba41SEd Maste session_setup_complete = 1; 2824f374ba41SEd Maste client_repledge(); 2825d74d50a8SDag-Erling Smørgrav } 2826d74d50a8SDag-Erling Smørgrav 2827ae1f160dSDag-Erling Smørgrav static void 282819261079SEd Maste client_init_dispatch(struct ssh *ssh) 2829a04a10f8SKris Kennaway { 283019261079SEd Maste ssh_dispatch_init(ssh, &dispatch_protocol_error); 2831545d5ecaSDag-Erling Smørgrav 283219261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); 283319261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_DATA, &channel_input_data); 283419261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); 283519261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); 283619261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_OPEN, &client_input_channel_open); 283719261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); 283819261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); 283919261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_REQUEST, &client_input_channel_req); 284019261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); 284119261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_SUCCESS, &channel_input_status_confirm); 284219261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_CHANNEL_FAILURE, &channel_input_status_confirm); 284319261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_GLOBAL_REQUEST, &client_input_global_request); 28441e8db6e2SBrian Feldman 28451e8db6e2SBrian Feldman /* rekeying */ 284619261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit); 2847545d5ecaSDag-Erling Smørgrav 2848545d5ecaSDag-Erling Smørgrav /* global request reply messages */ 284919261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_REQUEST_FAILURE, &client_global_request_reply); 285019261079SEd Maste ssh_dispatch_set(ssh, SSH2_MSG_REQUEST_SUCCESS, &client_global_request_reply); 2851a04a10f8SKris Kennaway } 2852d4af9e69SDag-Erling Smørgrav 2853e146993eSDag-Erling Smørgrav void 2854e146993eSDag-Erling Smørgrav client_stop_mux(void) 2855e146993eSDag-Erling Smørgrav { 2856e146993eSDag-Erling Smørgrav if (options.control_path != NULL && muxserver_sock != -1) 2857e146993eSDag-Erling Smørgrav unlink(options.control_path); 2858e146993eSDag-Erling Smørgrav /* 28596888a9beSDag-Erling Smørgrav * If we are in persist mode, or don't have a shell, signal that we 28606888a9beSDag-Erling Smørgrav * should close when all active channels are closed. 2861e146993eSDag-Erling Smørgrav */ 286219261079SEd Maste if (options.control_persist || options.session_type == SESSION_TYPE_NONE) { 2863e146993eSDag-Erling Smørgrav session_closed = 1; 2864e146993eSDag-Erling Smørgrav setproctitle("[stopped mux]"); 2865e146993eSDag-Erling Smørgrav } 2866e146993eSDag-Erling Smørgrav } 2867e146993eSDag-Erling Smørgrav 2868efcad6b7SDag-Erling Smørgrav /* client specific fatal cleanup */ 2869efcad6b7SDag-Erling Smørgrav void 2870efcad6b7SDag-Erling Smørgrav cleanup_exit(int i) 2871efcad6b7SDag-Erling Smørgrav { 2872e146993eSDag-Erling Smørgrav leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); 2873d4af9e69SDag-Erling Smørgrav if (options.control_path != NULL && muxserver_sock != -1) 2874d74d50a8SDag-Erling Smørgrav unlink(options.control_path); 28754a421b63SDag-Erling Smørgrav ssh_kill_proxy_command(); 2876efcad6b7SDag-Erling Smørgrav _exit(i); 2877efcad6b7SDag-Erling Smørgrav } 2878