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