1 |
|
|
/* $OpenBSD: clientloop.c,v 1.306 2017/10/23 05:08:00 djm Exp $ */ |
2 |
|
|
/* |
3 |
|
|
* Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 |
|
|
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
5 |
|
|
* All rights reserved |
6 |
|
|
* The main loop for the interactive session (client side). |
7 |
|
|
* |
8 |
|
|
* As far as I am concerned, the code I have written for this software |
9 |
|
|
* can be used freely for any purpose. Any derived versions of this |
10 |
|
|
* software must be clearly marked as such, and if the derived work is |
11 |
|
|
* incompatible with the protocol description in the RFC file, it must be |
12 |
|
|
* called by a name other than "ssh" or "Secure Shell". |
13 |
|
|
* |
14 |
|
|
* |
15 |
|
|
* Copyright (c) 1999 Theo de Raadt. All rights reserved. |
16 |
|
|
* |
17 |
|
|
* Redistribution and use in source and binary forms, with or without |
18 |
|
|
* modification, are permitted provided that the following conditions |
19 |
|
|
* are met: |
20 |
|
|
* 1. Redistributions of source code must retain the above copyright |
21 |
|
|
* notice, this list of conditions and the following disclaimer. |
22 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
23 |
|
|
* notice, this list of conditions and the following disclaimer in the |
24 |
|
|
* documentation and/or other materials provided with the distribution. |
25 |
|
|
* |
26 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
27 |
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
28 |
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
29 |
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
30 |
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
31 |
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
32 |
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
33 |
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
34 |
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
35 |
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
36 |
|
|
* |
37 |
|
|
* |
38 |
|
|
* SSH2 support added by Markus Friedl. |
39 |
|
|
* Copyright (c) 1999, 2000, 2001 Markus Friedl. All rights reserved. |
40 |
|
|
* |
41 |
|
|
* Redistribution and use in source and binary forms, with or without |
42 |
|
|
* modification, are permitted provided that the following conditions |
43 |
|
|
* are met: |
44 |
|
|
* 1. Redistributions of source code must retain the above copyright |
45 |
|
|
* notice, this list of conditions and the following disclaimer. |
46 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
47 |
|
|
* notice, this list of conditions and the following disclaimer in the |
48 |
|
|
* documentation and/or other materials provided with the distribution. |
49 |
|
|
* |
50 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
51 |
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
52 |
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
53 |
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
54 |
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
55 |
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
56 |
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
57 |
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
58 |
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
59 |
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
60 |
|
|
*/ |
61 |
|
|
|
62 |
|
|
|
63 |
|
|
#include <sys/types.h> |
64 |
|
|
#include <sys/ioctl.h> |
65 |
|
|
#include <sys/stat.h> |
66 |
|
|
#include <sys/socket.h> |
67 |
|
|
#include <sys/time.h> |
68 |
|
|
#include <sys/queue.h> |
69 |
|
|
|
70 |
|
|
#include <ctype.h> |
71 |
|
|
#include <errno.h> |
72 |
|
|
#include <paths.h> |
73 |
|
|
#include <signal.h> |
74 |
|
|
#include <stdio.h> |
75 |
|
|
#include <stdlib.h> |
76 |
|
|
#include <string.h> |
77 |
|
|
#include <termios.h> |
78 |
|
|
#include <pwd.h> |
79 |
|
|
#include <unistd.h> |
80 |
|
|
#include <limits.h> |
81 |
|
|
|
82 |
|
|
#include "xmalloc.h" |
83 |
|
|
#include "ssh.h" |
84 |
|
|
#include "ssh2.h" |
85 |
|
|
#include "packet.h" |
86 |
|
|
#include "buffer.h" |
87 |
|
|
#include "compat.h" |
88 |
|
|
#include "channels.h" |
89 |
|
|
#include "dispatch.h" |
90 |
|
|
#include "key.h" |
91 |
|
|
#include "cipher.h" |
92 |
|
|
#include "kex.h" |
93 |
|
|
#include "myproposal.h" |
94 |
|
|
#include "log.h" |
95 |
|
|
#include "misc.h" |
96 |
|
|
#include "readconf.h" |
97 |
|
|
#include "clientloop.h" |
98 |
|
|
#include "sshconnect.h" |
99 |
|
|
#include "authfd.h" |
100 |
|
|
#include "atomicio.h" |
101 |
|
|
#include "sshpty.h" |
102 |
|
|
#include "match.h" |
103 |
|
|
#include "msg.h" |
104 |
|
|
#include "ssherr.h" |
105 |
|
|
#include "hostfile.h" |
106 |
|
|
|
107 |
|
|
/* import options */ |
108 |
|
|
extern Options options; |
109 |
|
|
|
110 |
|
|
/* Flag indicating that stdin should be redirected from /dev/null. */ |
111 |
|
|
extern int stdin_null_flag; |
112 |
|
|
|
113 |
|
|
/* Flag indicating that no shell has been requested */ |
114 |
|
|
extern int no_shell_flag; |
115 |
|
|
|
116 |
|
|
/* Flag indicating that ssh should daemonise after authentication is complete */ |
117 |
|
|
extern int fork_after_authentication_flag; |
118 |
|
|
|
119 |
|
|
/* Control socket */ |
120 |
|
|
extern int muxserver_sock; /* XXX use mux_client_cleanup() instead */ |
121 |
|
|
|
122 |
|
|
/* |
123 |
|
|
* Name of the host we are connecting to. This is the name given on the |
124 |
|
|
* command line, or the HostName specified for the user-supplied name in a |
125 |
|
|
* configuration file. |
126 |
|
|
*/ |
127 |
|
|
extern char *host; |
128 |
|
|
|
129 |
|
|
/* |
130 |
|
|
* Flag to indicate that we have received a window change signal which has |
131 |
|
|
* not yet been processed. This will cause a message indicating the new |
132 |
|
|
* window size to be sent to the server a little later. This is volatile |
133 |
|
|
* because this is updated in a signal handler. |
134 |
|
|
*/ |
135 |
|
|
static volatile sig_atomic_t received_window_change_signal = 0; |
136 |
|
|
static volatile sig_atomic_t received_signal = 0; |
137 |
|
|
|
138 |
|
|
/* Flag indicating whether the user's terminal is in non-blocking mode. */ |
139 |
|
|
static int in_non_blocking_mode = 0; |
140 |
|
|
|
141 |
|
|
/* Time when backgrounded control master using ControlPersist should exit */ |
142 |
|
|
static time_t control_persist_exit_time = 0; |
143 |
|
|
|
144 |
|
|
/* Common data for the client loop code. */ |
145 |
|
|
volatile sig_atomic_t quit_pending; /* Set non-zero to quit the loop. */ |
146 |
|
|
static int last_was_cr; /* Last character was a newline. */ |
147 |
|
|
static int exit_status; /* Used to store the command exit status. */ |
148 |
|
|
static Buffer stderr_buffer; /* Used for final exit message. */ |
149 |
|
|
static int connection_in; /* Connection to server (input). */ |
150 |
|
|
static int connection_out; /* Connection to server (output). */ |
151 |
|
|
static int need_rekeying; /* Set to non-zero if rekeying is requested. */ |
152 |
|
|
static int session_closed; /* In SSH2: login session closed. */ |
153 |
|
|
static u_int x11_refuse_time; /* If >0, refuse x11 opens after this time. */ |
154 |
|
|
|
155 |
|
|
static void client_init_dispatch(void); |
156 |
|
|
int session_ident = -1; |
157 |
|
|
|
158 |
|
|
/* Track escape per proto2 channel */ |
159 |
|
|
struct escape_filter_ctx { |
160 |
|
|
int escape_pending; |
161 |
|
|
int escape_char; |
162 |
|
|
}; |
163 |
|
|
|
164 |
|
|
/* Context for channel confirmation replies */ |
165 |
|
|
struct channel_reply_ctx { |
166 |
|
|
const char *request_type; |
167 |
|
|
int id; |
168 |
|
|
enum confirm_action action; |
169 |
|
|
}; |
170 |
|
|
|
171 |
|
|
/* Global request success/failure callbacks */ |
172 |
|
|
/* XXX move to struct ssh? */ |
173 |
|
|
struct global_confirm { |
174 |
|
|
TAILQ_ENTRY(global_confirm) entry; |
175 |
|
|
global_confirm_cb *cb; |
176 |
|
|
void *ctx; |
177 |
|
|
int ref_count; |
178 |
|
|
}; |
179 |
|
|
TAILQ_HEAD(global_confirms, global_confirm); |
180 |
|
|
static struct global_confirms global_confirms = |
181 |
|
|
TAILQ_HEAD_INITIALIZER(global_confirms); |
182 |
|
|
|
183 |
|
|
void ssh_process_session2_setup(int, int, int, Buffer *); |
184 |
|
|
|
185 |
|
|
/* Restores stdin to blocking mode. */ |
186 |
|
|
|
187 |
|
|
static void |
188 |
|
|
leave_non_blocking(void) |
189 |
|
|
{ |
190 |
|
|
if (in_non_blocking_mode) { |
191 |
|
|
unset_nonblock(fileno(stdin)); |
192 |
|
|
in_non_blocking_mode = 0; |
193 |
|
|
} |
194 |
|
|
} |
195 |
|
|
|
196 |
|
|
/* |
197 |
|
|
* Signal handler for the window change signal (SIGWINCH). This just sets a |
198 |
|
|
* flag indicating that the window has changed. |
199 |
|
|
*/ |
200 |
|
|
/*ARGSUSED */ |
201 |
|
|
static void |
202 |
|
|
window_change_handler(int sig) |
203 |
|
|
{ |
204 |
|
|
received_window_change_signal = 1; |
205 |
|
|
signal(SIGWINCH, window_change_handler); |
206 |
|
|
} |
207 |
|
|
|
208 |
|
|
/* |
209 |
|
|
* Signal handler for signals that cause the program to terminate. These |
210 |
|
|
* signals must be trapped to restore terminal modes. |
211 |
|
|
*/ |
212 |
|
|
/*ARGSUSED */ |
213 |
|
|
static void |
214 |
|
|
signal_handler(int sig) |
215 |
|
|
{ |
216 |
|
|
received_signal = sig; |
217 |
|
|
quit_pending = 1; |
218 |
|
|
} |
219 |
|
|
|
220 |
|
|
/* |
221 |
|
|
* Returns current time in seconds from Jan 1, 1970 with the maximum |
222 |
|
|
* available resolution. |
223 |
|
|
*/ |
224 |
|
|
|
225 |
|
|
static double |
226 |
|
|
get_current_time(void) |
227 |
|
|
{ |
228 |
|
|
struct timeval tv; |
229 |
|
|
gettimeofday(&tv, NULL); |
230 |
|
|
return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0; |
231 |
|
|
} |
232 |
|
|
|
233 |
|
|
/* |
234 |
|
|
* Sets control_persist_exit_time to the absolute time when the |
235 |
|
|
* backgrounded control master should exit due to expiry of the |
236 |
|
|
* ControlPersist timeout. Sets it to 0 if we are not a backgrounded |
237 |
|
|
* control master process, or if there is no ControlPersist timeout. |
238 |
|
|
*/ |
239 |
|
|
static void |
240 |
|
|
set_control_persist_exit_time(struct ssh *ssh) |
241 |
|
|
{ |
242 |
|
|
if (muxserver_sock == -1 || !options.control_persist |
243 |
|
|
|| options.control_persist_timeout == 0) { |
244 |
|
|
/* not using a ControlPersist timeout */ |
245 |
|
|
control_persist_exit_time = 0; |
246 |
|
|
} else if (channel_still_open(ssh)) { |
247 |
|
|
/* some client connections are still open */ |
248 |
|
|
if (control_persist_exit_time > 0) |
249 |
|
|
debug2("%s: cancel scheduled exit", __func__); |
250 |
|
|
control_persist_exit_time = 0; |
251 |
|
|
} else if (control_persist_exit_time <= 0) { |
252 |
|
|
/* a client connection has recently closed */ |
253 |
|
|
control_persist_exit_time = monotime() + |
254 |
|
|
(time_t)options.control_persist_timeout; |
255 |
|
|
debug2("%s: schedule exit in %d seconds", __func__, |
256 |
|
|
options.control_persist_timeout); |
257 |
|
|
} |
258 |
|
|
/* else we are already counting down to the timeout */ |
259 |
|
|
} |
260 |
|
|
|
261 |
|
|
#define SSH_X11_VALID_DISPLAY_CHARS ":/.-_" |
262 |
|
|
static int |
263 |
|
|
client_x11_display_valid(const char *display) |
264 |
|
|
{ |
265 |
|
|
size_t i, dlen; |
266 |
|
|
|
267 |
|
|
if (display == NULL) |
268 |
|
|
return 0; |
269 |
|
|
|
270 |
|
|
dlen = strlen(display); |
271 |
|
|
for (i = 0; i < dlen; i++) { |
272 |
|
|
if (!isalnum((u_char)display[i]) && |
273 |
|
|
strchr(SSH_X11_VALID_DISPLAY_CHARS, display[i]) == NULL) { |
274 |
|
|
debug("Invalid character '%c' in DISPLAY", display[i]); |
275 |
|
|
return 0; |
276 |
|
|
} |
277 |
|
|
} |
278 |
|
|
return 1; |
279 |
|
|
} |
280 |
|
|
|
281 |
|
|
#define SSH_X11_PROTO "MIT-MAGIC-COOKIE-1" |
282 |
|
|
#define X11_TIMEOUT_SLACK 60 |
283 |
|
|
int |
284 |
|
|
client_x11_get_proto(struct ssh *ssh, const char *display, |
285 |
|
|
const char *xauth_path, u_int trusted, u_int timeout, |
286 |
|
|
char **_proto, char **_data) |
287 |
|
|
{ |
288 |
|
|
char cmd[1024], line[512], xdisplay[512]; |
289 |
|
|
char xauthfile[PATH_MAX], xauthdir[PATH_MAX]; |
290 |
|
|
static char proto[512], data[512]; |
291 |
|
|
FILE *f; |
292 |
|
|
int got_data = 0, generated = 0, do_unlink = 0, r; |
293 |
|
|
struct stat st; |
294 |
|
|
u_int now, x11_timeout_real; |
295 |
|
|
|
296 |
|
|
*_proto = proto; |
297 |
|
|
*_data = data; |
298 |
|
|
proto[0] = data[0] = xauthfile[0] = xauthdir[0] = '\0'; |
299 |
|
|
|
300 |
|
|
if (!client_x11_display_valid(display)) { |
301 |
|
|
if (display != NULL) |
302 |
|
|
logit("DISPLAY \"%s\" invalid; disabling X11 forwarding", |
303 |
|
|
display); |
304 |
|
|
return -1; |
305 |
|
|
} |
306 |
|
|
if (xauth_path != NULL && stat(xauth_path, &st) == -1) { |
307 |
|
|
debug("No xauth program."); |
308 |
|
|
xauth_path = NULL; |
309 |
|
|
} |
310 |
|
|
|
311 |
|
|
if (xauth_path != NULL) { |
312 |
|
|
/* |
313 |
|
|
* Handle FamilyLocal case where $DISPLAY does |
314 |
|
|
* not match an authorization entry. For this we |
315 |
|
|
* just try "xauth list unix:displaynum.screennum". |
316 |
|
|
* XXX: "localhost" match to determine FamilyLocal |
317 |
|
|
* is not perfect. |
318 |
|
|
*/ |
319 |
|
|
if (strncmp(display, "localhost:", 10) == 0) { |
320 |
|
|
if ((r = snprintf(xdisplay, sizeof(xdisplay), "unix:%s", |
321 |
|
|
display + 10)) < 0 || |
322 |
|
|
(size_t)r >= sizeof(xdisplay)) { |
323 |
|
|
error("%s: display name too long", __func__); |
324 |
|
|
return -1; |
325 |
|
|
} |
326 |
|
|
display = xdisplay; |
327 |
|
|
} |
328 |
|
|
if (trusted == 0) { |
329 |
|
|
/* |
330 |
|
|
* Generate an untrusted X11 auth cookie. |
331 |
|
|
* |
332 |
|
|
* The authentication cookie should briefly outlive |
333 |
|
|
* ssh's willingness to forward X11 connections to |
334 |
|
|
* avoid nasty fail-open behaviour in the X server. |
335 |
|
|
*/ |
336 |
|
|
mktemp_proto(xauthdir, sizeof(xauthdir)); |
337 |
|
|
if (mkdtemp(xauthdir) == NULL) { |
338 |
|
|
error("%s: mkdtemp: %s", |
339 |
|
|
__func__, strerror(errno)); |
340 |
|
|
return -1; |
341 |
|
|
} |
342 |
|
|
do_unlink = 1; |
343 |
|
|
if ((r = snprintf(xauthfile, sizeof(xauthfile), |
344 |
|
|
"%s/xauthfile", xauthdir)) < 0 || |
345 |
|
|
(size_t)r >= sizeof(xauthfile)) { |
346 |
|
|
error("%s: xauthfile path too long", __func__); |
347 |
|
|
unlink(xauthfile); |
348 |
|
|
rmdir(xauthdir); |
349 |
|
|
return -1; |
350 |
|
|
} |
351 |
|
|
|
352 |
|
|
if (timeout >= UINT_MAX - X11_TIMEOUT_SLACK) |
353 |
|
|
x11_timeout_real = UINT_MAX; |
354 |
|
|
else |
355 |
|
|
x11_timeout_real = timeout + X11_TIMEOUT_SLACK; |
356 |
|
|
if ((r = snprintf(cmd, sizeof(cmd), |
357 |
|
|
"%s -f %s generate %s " SSH_X11_PROTO |
358 |
|
|
" untrusted timeout %u 2>" _PATH_DEVNULL, |
359 |
|
|
xauth_path, xauthfile, display, |
360 |
|
|
x11_timeout_real)) < 0 || |
361 |
|
|
(size_t)r >= sizeof(cmd)) |
362 |
|
|
fatal("%s: cmd too long", __func__); |
363 |
|
|
debug2("%s: %s", __func__, cmd); |
364 |
|
|
if (x11_refuse_time == 0) { |
365 |
|
|
now = monotime() + 1; |
366 |
|
|
if (UINT_MAX - timeout < now) |
367 |
|
|
x11_refuse_time = UINT_MAX; |
368 |
|
|
else |
369 |
|
|
x11_refuse_time = now + timeout; |
370 |
|
|
channel_set_x11_refuse_time(ssh, |
371 |
|
|
x11_refuse_time); |
372 |
|
|
} |
373 |
|
|
if (system(cmd) == 0) |
374 |
|
|
generated = 1; |
375 |
|
|
} |
376 |
|
|
|
377 |
|
|
/* |
378 |
|
|
* When in untrusted mode, we read the cookie only if it was |
379 |
|
|
* successfully generated as an untrusted one in the step |
380 |
|
|
* above. |
381 |
|
|
*/ |
382 |
|
|
if (trusted || generated) { |
383 |
|
|
snprintf(cmd, sizeof(cmd), |
384 |
|
|
"%s %s%s list %s 2>" _PATH_DEVNULL, |
385 |
|
|
xauth_path, |
386 |
|
|
generated ? "-f " : "" , |
387 |
|
|
generated ? xauthfile : "", |
388 |
|
|
display); |
389 |
|
|
debug2("x11_get_proto: %s", cmd); |
390 |
|
|
f = popen(cmd, "r"); |
391 |
|
|
if (f && fgets(line, sizeof(line), f) && |
392 |
|
|
sscanf(line, "%*s %511s %511s", proto, data) == 2) |
393 |
|
|
got_data = 1; |
394 |
|
|
if (f) |
395 |
|
|
pclose(f); |
396 |
|
|
} |
397 |
|
|
} |
398 |
|
|
|
399 |
|
|
if (do_unlink) { |
400 |
|
|
unlink(xauthfile); |
401 |
|
|
rmdir(xauthdir); |
402 |
|
|
} |
403 |
|
|
|
404 |
|
|
/* Don't fall back to fake X11 data for untrusted forwarding */ |
405 |
|
|
if (!trusted && !got_data) { |
406 |
|
|
error("Warning: untrusted X11 forwarding setup failed: " |
407 |
|
|
"xauth key data not generated"); |
408 |
|
|
return -1; |
409 |
|
|
} |
410 |
|
|
|
411 |
|
|
/* |
412 |
|
|
* If we didn't get authentication data, just make up some |
413 |
|
|
* data. The forwarding code will check the validity of the |
414 |
|
|
* response anyway, and substitute this data. The X11 |
415 |
|
|
* server, however, will ignore this fake data and use |
416 |
|
|
* whatever authentication mechanisms it was using otherwise |
417 |
|
|
* for the local connection. |
418 |
|
|
*/ |
419 |
|
|
if (!got_data) { |
420 |
|
|
u_int8_t rnd[16]; |
421 |
|
|
u_int i; |
422 |
|
|
|
423 |
|
|
logit("Warning: No xauth data; " |
424 |
|
|
"using fake authentication data for X11 forwarding."); |
425 |
|
|
strlcpy(proto, SSH_X11_PROTO, sizeof proto); |
426 |
|
|
arc4random_buf(rnd, sizeof(rnd)); |
427 |
|
|
for (i = 0; i < sizeof(rnd); i++) { |
428 |
|
|
snprintf(data + 2 * i, sizeof data - 2 * i, "%02x", |
429 |
|
|
rnd[i]); |
430 |
|
|
} |
431 |
|
|
} |
432 |
|
|
|
433 |
|
|
return 0; |
434 |
|
|
} |
435 |
|
|
|
436 |
|
|
/* |
437 |
|
|
* Checks if the client window has changed, and sends a packet about it to |
438 |
|
|
* the server if so. The actual change is detected elsewhere (by a software |
439 |
|
|
* interrupt on Unix); this just checks the flag and sends a message if |
440 |
|
|
* appropriate. |
441 |
|
|
*/ |
442 |
|
|
|
443 |
|
|
static void |
444 |
|
|
client_check_window_change(struct ssh *ssh) |
445 |
|
|
{ |
446 |
|
|
if (!received_window_change_signal) |
447 |
|
|
return; |
448 |
|
|
/** XXX race */ |
449 |
|
|
received_window_change_signal = 0; |
450 |
|
|
|
451 |
|
|
debug2("%s: changed", __func__); |
452 |
|
|
|
453 |
|
|
channel_send_window_changes(ssh); |
454 |
|
|
} |
455 |
|
|
|
456 |
|
|
static int |
457 |
|
|
client_global_request_reply(int type, u_int32_t seq, struct ssh *ssh) |
458 |
|
|
{ |
459 |
|
|
struct global_confirm *gc; |
460 |
|
|
|
461 |
|
|
if ((gc = TAILQ_FIRST(&global_confirms)) == NULL) |
462 |
|
|
return 0; |
463 |
|
|
if (gc->cb != NULL) |
464 |
|
|
gc->cb(ssh, type, seq, gc->ctx); |
465 |
|
|
if (--gc->ref_count <= 0) { |
466 |
|
|
TAILQ_REMOVE(&global_confirms, gc, entry); |
467 |
|
|
explicit_bzero(gc, sizeof(*gc)); |
468 |
|
|
free(gc); |
469 |
|
|
} |
470 |
|
|
|
471 |
|
|
packet_set_alive_timeouts(0); |
472 |
|
|
return 0; |
473 |
|
|
} |
474 |
|
|
|
475 |
|
|
static void |
476 |
|
|
server_alive_check(void) |
477 |
|
|
{ |
478 |
|
|
if (packet_inc_alive_timeouts() > options.server_alive_count_max) { |
479 |
|
|
logit("Timeout, server %s not responding.", host); |
480 |
|
|
cleanup_exit(255); |
481 |
|
|
} |
482 |
|
|
packet_start(SSH2_MSG_GLOBAL_REQUEST); |
483 |
|
|
packet_put_cstring("keepalive@openssh.com"); |
484 |
|
|
packet_put_char(1); /* boolean: want reply */ |
485 |
|
|
packet_send(); |
486 |
|
|
/* Insert an empty placeholder to maintain ordering */ |
487 |
|
|
client_register_global_confirm(NULL, NULL); |
488 |
|
|
} |
489 |
|
|
|
490 |
|
|
/* |
491 |
|
|
* Waits until the client can do something (some data becomes available on |
492 |
|
|
* one of the file descriptors). |
493 |
|
|
*/ |
494 |
|
|
static void |
495 |
|
|
client_wait_until_can_do_something(struct ssh *ssh, |
496 |
|
|
fd_set **readsetp, fd_set **writesetp, |
497 |
|
|
int *maxfdp, u_int *nallocp, int rekeying) |
498 |
|
|
{ |
499 |
|
|
struct timeval tv, *tvp; |
500 |
|
|
int timeout_secs; |
501 |
|
|
time_t minwait_secs = 0, server_alive_time = 0, now = monotime(); |
502 |
|
|
int ret; |
503 |
|
|
|
504 |
|
|
/* Add any selections by the channel mechanism. */ |
505 |
|
|
channel_prepare_select(active_state, readsetp, writesetp, maxfdp, |
506 |
|
|
nallocp, &minwait_secs); |
507 |
|
|
|
508 |
|
|
/* channel_prepare_select could have closed the last channel */ |
509 |
|
|
if (session_closed && !channel_still_open(ssh) && |
510 |
|
|
!packet_have_data_to_write()) { |
511 |
|
|
/* clear mask since we did not call select() */ |
512 |
|
|
memset(*readsetp, 0, *nallocp); |
513 |
|
|
memset(*writesetp, 0, *nallocp); |
514 |
|
|
return; |
515 |
|
|
} |
516 |
|
|
|
517 |
|
|
FD_SET(connection_in, *readsetp); |
518 |
|
|
|
519 |
|
|
/* Select server connection if have data to write to the server. */ |
520 |
|
|
if (packet_have_data_to_write()) |
521 |
|
|
FD_SET(connection_out, *writesetp); |
522 |
|
|
|
523 |
|
|
/* |
524 |
|
|
* Wait for something to happen. This will suspend the process until |
525 |
|
|
* some selected descriptor can be read, written, or has some other |
526 |
|
|
* event pending, or a timeout expires. |
527 |
|
|
*/ |
528 |
|
|
|
529 |
|
|
timeout_secs = INT_MAX; /* we use INT_MAX to mean no timeout */ |
530 |
|
|
if (options.server_alive_interval > 0) { |
531 |
|
|
timeout_secs = options.server_alive_interval; |
532 |
|
|
server_alive_time = now + options.server_alive_interval; |
533 |
|
|
} |
534 |
|
|
if (options.rekey_interval > 0 && !rekeying) |
535 |
|
|
timeout_secs = MINIMUM(timeout_secs, packet_get_rekey_timeout()); |
536 |
|
|
set_control_persist_exit_time(ssh); |
537 |
|
|
if (control_persist_exit_time > 0) { |
538 |
|
|
timeout_secs = MINIMUM(timeout_secs, |
539 |
|
|
control_persist_exit_time - now); |
540 |
|
|
if (timeout_secs < 0) |
541 |
|
|
timeout_secs = 0; |
542 |
|
|
} |
543 |
|
|
if (minwait_secs != 0) |
544 |
|
|
timeout_secs = MINIMUM(timeout_secs, (int)minwait_secs); |
545 |
|
|
if (timeout_secs == INT_MAX) |
546 |
|
|
tvp = NULL; |
547 |
|
|
else { |
548 |
|
|
tv.tv_sec = timeout_secs; |
549 |
|
|
tv.tv_usec = 0; |
550 |
|
|
tvp = &tv; |
551 |
|
|
} |
552 |
|
|
|
553 |
|
|
ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp); |
554 |
|
|
if (ret < 0) { |
555 |
|
|
char buf[100]; |
556 |
|
|
|
557 |
|
|
/* |
558 |
|
|
* We have to clear the select masks, because we return. |
559 |
|
|
* We have to return, because the mainloop checks for the flags |
560 |
|
|
* set by the signal handlers. |
561 |
|
|
*/ |
562 |
|
|
memset(*readsetp, 0, *nallocp); |
563 |
|
|
memset(*writesetp, 0, *nallocp); |
564 |
|
|
|
565 |
|
|
if (errno == EINTR) |
566 |
|
|
return; |
567 |
|
|
/* Note: we might still have data in the buffers. */ |
568 |
|
|
snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno)); |
569 |
|
|
buffer_append(&stderr_buffer, buf, strlen(buf)); |
570 |
|
|
quit_pending = 1; |
571 |
|
|
} else if (ret == 0) { |
572 |
|
|
/* |
573 |
|
|
* Timeout. Could have been either keepalive or rekeying. |
574 |
|
|
* Keepalive we check here, rekeying is checked in clientloop. |
575 |
|
|
*/ |
576 |
|
|
if (server_alive_time != 0 && server_alive_time <= monotime()) |
577 |
|
|
server_alive_check(); |
578 |
|
|
} |
579 |
|
|
|
580 |
|
|
} |
581 |
|
|
|
582 |
|
|
static void |
583 |
|
|
client_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr) |
584 |
|
|
{ |
585 |
|
|
/* Flush stdout and stderr buffers. */ |
586 |
|
|
if (buffer_len(bout) > 0) |
587 |
|
|
atomicio(vwrite, fileno(stdout), buffer_ptr(bout), |
588 |
|
|
buffer_len(bout)); |
589 |
|
|
if (buffer_len(berr) > 0) |
590 |
|
|
atomicio(vwrite, fileno(stderr), buffer_ptr(berr), |
591 |
|
|
buffer_len(berr)); |
592 |
|
|
|
593 |
|
|
leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); |
594 |
|
|
|
595 |
|
|
sshbuf_reset(bin); |
596 |
|
|
sshbuf_reset(bout); |
597 |
|
|
sshbuf_reset(berr); |
598 |
|
|
|
599 |
|
|
/* Send the suspend signal to the program itself. */ |
600 |
|
|
kill(getpid(), SIGTSTP); |
601 |
|
|
|
602 |
|
|
/* Reset window sizes in case they have changed */ |
603 |
|
|
received_window_change_signal = 1; |
604 |
|
|
|
605 |
|
|
enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); |
606 |
|
|
} |
607 |
|
|
|
608 |
|
|
static void |
609 |
|
|
client_process_net_input(fd_set *readset) |
610 |
|
|
{ |
611 |
|
|
int len; |
612 |
|
|
char buf[8192]; |
613 |
|
|
|
614 |
|
|
/* |
615 |
|
|
* Read input from the server, and add any such data to the buffer of |
616 |
|
|
* the packet subsystem. |
617 |
|
|
*/ |
618 |
|
|
if (FD_ISSET(connection_in, readset)) { |
619 |
|
|
/* Read as much as possible. */ |
620 |
|
|
len = read(connection_in, buf, sizeof(buf)); |
621 |
|
|
if (len == 0) { |
622 |
|
|
/* |
623 |
|
|
* Received EOF. The remote host has closed the |
624 |
|
|
* connection. |
625 |
|
|
*/ |
626 |
|
|
snprintf(buf, sizeof buf, |
627 |
|
|
"Connection to %.300s closed by remote host.\r\n", |
628 |
|
|
host); |
629 |
|
|
buffer_append(&stderr_buffer, buf, strlen(buf)); |
630 |
|
|
quit_pending = 1; |
631 |
|
|
return; |
632 |
|
|
} |
633 |
|
|
/* |
634 |
|
|
* There is a kernel bug on Solaris that causes select to |
635 |
|
|
* sometimes wake up even though there is no data available. |
636 |
|
|
*/ |
637 |
|
|
if (len < 0 && (errno == EAGAIN || errno == EINTR)) |
638 |
|
|
len = 0; |
639 |
|
|
|
640 |
|
|
if (len < 0) { |
641 |
|
|
/* |
642 |
|
|
* An error has encountered. Perhaps there is a |
643 |
|
|
* network problem. |
644 |
|
|
*/ |
645 |
|
|
snprintf(buf, sizeof buf, |
646 |
|
|
"Read from remote host %.300s: %.100s\r\n", |
647 |
|
|
host, strerror(errno)); |
648 |
|
|
buffer_append(&stderr_buffer, buf, strlen(buf)); |
649 |
|
|
quit_pending = 1; |
650 |
|
|
return; |
651 |
|
|
} |
652 |
|
|
packet_process_incoming(buf, len); |
653 |
|
|
} |
654 |
|
|
} |
655 |
|
|
|
656 |
|
|
static void |
657 |
|
|
client_status_confirm(struct ssh *ssh, int type, Channel *c, void *ctx) |
658 |
|
|
{ |
659 |
|
|
struct channel_reply_ctx *cr = (struct channel_reply_ctx *)ctx; |
660 |
|
|
char errmsg[256]; |
661 |
|
|
int tochan; |
662 |
|
|
|
663 |
|
|
/* |
664 |
|
|
* If a TTY was explicitly requested, then a failure to allocate |
665 |
|
|
* one is fatal. |
666 |
|
|
*/ |
667 |
|
|
if (cr->action == CONFIRM_TTY && |
668 |
|
|
(options.request_tty == REQUEST_TTY_FORCE || |
669 |
|
|
options.request_tty == REQUEST_TTY_YES)) |
670 |
|
|
cr->action = CONFIRM_CLOSE; |
671 |
|
|
|
672 |
|
|
/* XXX supress on mux _client_ quietmode */ |
673 |
|
|
tochan = options.log_level >= SYSLOG_LEVEL_ERROR && |
674 |
|
|
c->ctl_chan != -1 && c->extended_usage == CHAN_EXTENDED_WRITE; |
675 |
|
|
|
676 |
|
|
if (type == SSH2_MSG_CHANNEL_SUCCESS) { |
677 |
|
|
debug2("%s request accepted on channel %d", |
678 |
|
|
cr->request_type, c->self); |
679 |
|
|
} else if (type == SSH2_MSG_CHANNEL_FAILURE) { |
680 |
|
|
if (tochan) { |
681 |
|
|
snprintf(errmsg, sizeof(errmsg), |
682 |
|
|
"%s request failed\r\n", cr->request_type); |
683 |
|
|
} else { |
684 |
|
|
snprintf(errmsg, sizeof(errmsg), |
685 |
|
|
"%s request failed on channel %d", |
686 |
|
|
cr->request_type, c->self); |
687 |
|
|
} |
688 |
|
|
/* If error occurred on primary session channel, then exit */ |
689 |
|
|
if (cr->action == CONFIRM_CLOSE && c->self == session_ident) |
690 |
|
|
fatal("%s", errmsg); |
691 |
|
|
/* |
692 |
|
|
* If error occurred on mux client, append to |
693 |
|
|
* their stderr. |
694 |
|
|
*/ |
695 |
|
|
if (tochan) { |
696 |
|
|
buffer_append(c->extended, errmsg, strlen(errmsg)); |
697 |
|
|
} else |
698 |
|
|
error("%s", errmsg); |
699 |
|
|
if (cr->action == CONFIRM_TTY) { |
700 |
|
|
/* |
701 |
|
|
* If a TTY allocation error occurred, then arrange |
702 |
|
|
* for the correct TTY to leave raw mode. |
703 |
|
|
*/ |
704 |
|
|
if (c->self == session_ident) |
705 |
|
|
leave_raw_mode(0); |
706 |
|
|
else |
707 |
|
|
mux_tty_alloc_failed(ssh, c); |
708 |
|
|
} else if (cr->action == CONFIRM_CLOSE) { |
709 |
|
|
chan_read_failed(ssh, c); |
710 |
|
|
chan_write_failed(ssh, c); |
711 |
|
|
} |
712 |
|
|
} |
713 |
|
|
free(cr); |
714 |
|
|
} |
715 |
|
|
|
716 |
|
|
static void |
717 |
|
|
client_abandon_status_confirm(struct ssh *ssh, Channel *c, void *ctx) |
718 |
|
|
{ |
719 |
|
|
free(ctx); |
720 |
|
|
} |
721 |
|
|
|
722 |
|
|
void |
723 |
|
|
client_expect_confirm(struct ssh *ssh, int id, const char *request, |
724 |
|
|
enum confirm_action action) |
725 |
|
|
{ |
726 |
|
|
struct channel_reply_ctx *cr = xcalloc(1, sizeof(*cr)); |
727 |
|
|
|
728 |
|
|
cr->request_type = request; |
729 |
|
|
cr->action = action; |
730 |
|
|
|
731 |
|
|
channel_register_status_confirm(ssh, id, client_status_confirm, |
732 |
|
|
client_abandon_status_confirm, cr); |
733 |
|
|
} |
734 |
|
|
|
735 |
|
|
void |
736 |
|
|
client_register_global_confirm(global_confirm_cb *cb, void *ctx) |
737 |
|
|
{ |
738 |
|
|
struct global_confirm *gc, *last_gc; |
739 |
|
|
|
740 |
|
|
/* Coalesce identical callbacks */ |
741 |
|
|
last_gc = TAILQ_LAST(&global_confirms, global_confirms); |
742 |
|
|
if (last_gc && last_gc->cb == cb && last_gc->ctx == ctx) { |
743 |
|
|
if (++last_gc->ref_count >= INT_MAX) |
744 |
|
|
fatal("%s: last_gc->ref_count = %d", |
745 |
|
|
__func__, last_gc->ref_count); |
746 |
|
|
return; |
747 |
|
|
} |
748 |
|
|
|
749 |
|
|
gc = xcalloc(1, sizeof(*gc)); |
750 |
|
|
gc->cb = cb; |
751 |
|
|
gc->ctx = ctx; |
752 |
|
|
gc->ref_count = 1; |
753 |
|
|
TAILQ_INSERT_TAIL(&global_confirms, gc, entry); |
754 |
|
|
} |
755 |
|
|
|
756 |
|
|
static void |
757 |
|
|
process_cmdline(struct ssh *ssh) |
758 |
|
|
{ |
759 |
|
|
void (*handler)(int); |
760 |
|
|
char *s, *cmd; |
761 |
|
|
int ok, delete = 0, local = 0, remote = 0, dynamic = 0; |
762 |
|
|
struct Forward fwd; |
763 |
|
|
|
764 |
|
|
memset(&fwd, 0, sizeof(fwd)); |
765 |
|
|
|
766 |
|
|
leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); |
767 |
|
|
handler = signal(SIGINT, SIG_IGN); |
768 |
|
|
cmd = s = read_passphrase("\r\nssh> ", RP_ECHO); |
769 |
|
|
if (s == NULL) |
770 |
|
|
goto out; |
771 |
|
|
while (isspace((u_char)*s)) |
772 |
|
|
s++; |
773 |
|
|
if (*s == '-') |
774 |
|
|
s++; /* Skip cmdline '-', if any */ |
775 |
|
|
if (*s == '\0') |
776 |
|
|
goto out; |
777 |
|
|
|
778 |
|
|
if (*s == 'h' || *s == 'H' || *s == '?') { |
779 |
|
|
logit("Commands:"); |
780 |
|
|
logit(" -L[bind_address:]port:host:hostport " |
781 |
|
|
"Request local forward"); |
782 |
|
|
logit(" -R[bind_address:]port:host:hostport " |
783 |
|
|
"Request remote forward"); |
784 |
|
|
logit(" -D[bind_address:]port " |
785 |
|
|
"Request dynamic forward"); |
786 |
|
|
logit(" -KL[bind_address:]port " |
787 |
|
|
"Cancel local forward"); |
788 |
|
|
logit(" -KR[bind_address:]port " |
789 |
|
|
"Cancel remote forward"); |
790 |
|
|
logit(" -KD[bind_address:]port " |
791 |
|
|
"Cancel dynamic forward"); |
792 |
|
|
if (!options.permit_local_command) |
793 |
|
|
goto out; |
794 |
|
|
logit(" !args " |
795 |
|
|
"Execute local command"); |
796 |
|
|
goto out; |
797 |
|
|
} |
798 |
|
|
|
799 |
|
|
if (*s == '!' && options.permit_local_command) { |
800 |
|
|
s++; |
801 |
|
|
ssh_local_cmd(s); |
802 |
|
|
goto out; |
803 |
|
|
} |
804 |
|
|
|
805 |
|
|
if (*s == 'K') { |
806 |
|
|
delete = 1; |
807 |
|
|
s++; |
808 |
|
|
} |
809 |
|
|
if (*s == 'L') |
810 |
|
|
local = 1; |
811 |
|
|
else if (*s == 'R') |
812 |
|
|
remote = 1; |
813 |
|
|
else if (*s == 'D') |
814 |
|
|
dynamic = 1; |
815 |
|
|
else { |
816 |
|
|
logit("Invalid command."); |
817 |
|
|
goto out; |
818 |
|
|
} |
819 |
|
|
|
820 |
|
|
while (isspace((u_char)*++s)) |
821 |
|
|
; |
822 |
|
|
|
823 |
|
|
/* XXX update list of forwards in options */ |
824 |
|
|
if (delete) { |
825 |
|
|
/* We pass 1 for dynamicfwd to restrict to 1 or 2 fields. */ |
826 |
|
|
if (!parse_forward(&fwd, s, 1, 0)) { |
827 |
|
|
logit("Bad forwarding close specification."); |
828 |
|
|
goto out; |
829 |
|
|
} |
830 |
|
|
if (remote) |
831 |
|
|
ok = channel_request_rforward_cancel(ssh, &fwd) == 0; |
832 |
|
|
else if (dynamic) |
833 |
|
|
ok = channel_cancel_lport_listener(ssh, &fwd, |
834 |
|
|
0, &options.fwd_opts) > 0; |
835 |
|
|
else |
836 |
|
|
ok = channel_cancel_lport_listener(ssh, &fwd, |
837 |
|
|
CHANNEL_CANCEL_PORT_STATIC, |
838 |
|
|
&options.fwd_opts) > 0; |
839 |
|
|
if (!ok) { |
840 |
|
|
logit("Unknown port forwarding."); |
841 |
|
|
goto out; |
842 |
|
|
} |
843 |
|
|
logit("Canceled forwarding."); |
844 |
|
|
} else { |
845 |
|
|
if (!parse_forward(&fwd, s, dynamic, remote)) { |
846 |
|
|
logit("Bad forwarding specification."); |
847 |
|
|
goto out; |
848 |
|
|
} |
849 |
|
|
if (local || dynamic) { |
850 |
|
|
if (!channel_setup_local_fwd_listener(ssh, &fwd, |
851 |
|
|
&options.fwd_opts)) { |
852 |
|
|
logit("Port forwarding failed."); |
853 |
|
|
goto out; |
854 |
|
|
} |
855 |
|
|
} else { |
856 |
|
|
if (channel_request_remote_forwarding(ssh, &fwd) < 0) { |
857 |
|
|
logit("Port forwarding failed."); |
858 |
|
|
goto out; |
859 |
|
|
} |
860 |
|
|
} |
861 |
|
|
logit("Forwarding port."); |
862 |
|
|
} |
863 |
|
|
|
864 |
|
|
out: |
865 |
|
|
signal(SIGINT, handler); |
866 |
|
|
enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); |
867 |
|
|
free(cmd); |
868 |
|
|
free(fwd.listen_host); |
869 |
|
|
free(fwd.listen_path); |
870 |
|
|
free(fwd.connect_host); |
871 |
|
|
free(fwd.connect_path); |
872 |
|
|
} |
873 |
|
|
|
874 |
|
|
/* reasons to suppress output of an escape command in help output */ |
875 |
|
|
#define SUPPRESS_NEVER 0 /* never suppress, always show */ |
876 |
|
|
#define SUPPRESS_MUXCLIENT 1 /* don't show in mux client sessions */ |
877 |
|
|
#define SUPPRESS_MUXMASTER 2 /* don't show in mux master sessions */ |
878 |
|
|
#define SUPPRESS_SYSLOG 4 /* don't show when logging to syslog */ |
879 |
|
|
struct escape_help_text { |
880 |
|
|
const char *cmd; |
881 |
|
|
const char *text; |
882 |
|
|
unsigned int flags; |
883 |
|
|
}; |
884 |
|
|
static struct escape_help_text esc_txt[] = { |
885 |
|
|
{".", "terminate session", SUPPRESS_MUXMASTER}, |
886 |
|
|
{".", "terminate connection (and any multiplexed sessions)", |
887 |
|
|
SUPPRESS_MUXCLIENT}, |
888 |
|
|
{"B", "send a BREAK to the remote system", SUPPRESS_NEVER}, |
889 |
|
|
{"C", "open a command line", SUPPRESS_MUXCLIENT}, |
890 |
|
|
{"R", "request rekey", SUPPRESS_NEVER}, |
891 |
|
|
{"V/v", "decrease/increase verbosity (LogLevel)", SUPPRESS_MUXCLIENT}, |
892 |
|
|
{"^Z", "suspend ssh", SUPPRESS_MUXCLIENT}, |
893 |
|
|
{"#", "list forwarded connections", SUPPRESS_NEVER}, |
894 |
|
|
{"&", "background ssh (when waiting for connections to terminate)", |
895 |
|
|
SUPPRESS_MUXCLIENT}, |
896 |
|
|
{"?", "this message", SUPPRESS_NEVER}, |
897 |
|
|
}; |
898 |
|
|
|
899 |
|
|
static void |
900 |
|
|
print_escape_help(Buffer *b, int escape_char, int mux_client, int using_stderr) |
901 |
|
|
{ |
902 |
|
|
unsigned int i, suppress_flags; |
903 |
|
|
char string[1024]; |
904 |
|
|
|
905 |
|
|
snprintf(string, sizeof string, "%c?\r\n" |
906 |
|
|
"Supported escape sequences:\r\n", escape_char); |
907 |
|
|
buffer_append(b, string, strlen(string)); |
908 |
|
|
|
909 |
|
|
suppress_flags = |
910 |
|
|
(mux_client ? SUPPRESS_MUXCLIENT : 0) | |
911 |
|
|
(mux_client ? 0 : SUPPRESS_MUXMASTER) | |
912 |
|
|
(using_stderr ? 0 : SUPPRESS_SYSLOG); |
913 |
|
|
|
914 |
|
|
for (i = 0; i < sizeof(esc_txt)/sizeof(esc_txt[0]); i++) { |
915 |
|
|
if (esc_txt[i].flags & suppress_flags) |
916 |
|
|
continue; |
917 |
|
|
snprintf(string, sizeof string, " %c%-3s - %s\r\n", |
918 |
|
|
escape_char, esc_txt[i].cmd, esc_txt[i].text); |
919 |
|
|
buffer_append(b, string, strlen(string)); |
920 |
|
|
} |
921 |
|
|
|
922 |
|
|
snprintf(string, sizeof string, |
923 |
|
|
" %c%c - send the escape character by typing it twice\r\n" |
924 |
|
|
"(Note that escapes are only recognized immediately after " |
925 |
|
|
"newline.)\r\n", escape_char, escape_char); |
926 |
|
|
buffer_append(b, string, strlen(string)); |
927 |
|
|
} |
928 |
|
|
|
929 |
|
|
/* |
930 |
|
|
* Process the characters one by one. |
931 |
|
|
*/ |
932 |
|
|
static int |
933 |
|
|
process_escapes(struct ssh *ssh, Channel *c, |
934 |
|
|
Buffer *bin, Buffer *bout, Buffer *berr, |
935 |
|
|
char *buf, int len) |
936 |
|
|
{ |
937 |
|
|
char string[1024]; |
938 |
|
|
pid_t pid; |
939 |
|
|
int bytes = 0; |
940 |
|
|
u_int i; |
941 |
|
|
u_char ch; |
942 |
|
|
char *s; |
943 |
|
|
struct escape_filter_ctx *efc = c->filter_ctx == NULL ? |
944 |
|
|
NULL : (struct escape_filter_ctx *)c->filter_ctx; |
945 |
|
|
|
946 |
|
|
if (c->filter_ctx == NULL) |
947 |
|
|
return 0; |
948 |
|
|
|
949 |
|
|
if (len <= 0) |
950 |
|
|
return (0); |
951 |
|
|
|
952 |
|
|
for (i = 0; i < (u_int)len; i++) { |
953 |
|
|
/* Get one character at a time. */ |
954 |
|
|
ch = buf[i]; |
955 |
|
|
|
956 |
|
|
if (efc->escape_pending) { |
957 |
|
|
/* We have previously seen an escape character. */ |
958 |
|
|
/* Clear the flag now. */ |
959 |
|
|
efc->escape_pending = 0; |
960 |
|
|
|
961 |
|
|
/* Process the escaped character. */ |
962 |
|
|
switch (ch) { |
963 |
|
|
case '.': |
964 |
|
|
/* Terminate the connection. */ |
965 |
|
|
snprintf(string, sizeof string, "%c.\r\n", |
966 |
|
|
efc->escape_char); |
967 |
|
|
buffer_append(berr, string, strlen(string)); |
968 |
|
|
|
969 |
|
|
if (c && c->ctl_chan != -1) { |
970 |
|
|
chan_read_failed(ssh, c); |
971 |
|
|
chan_write_failed(ssh, c); |
972 |
|
|
if (c->detach_user) { |
973 |
|
|
c->detach_user(ssh, |
974 |
|
|
c->self, NULL); |
975 |
|
|
} |
976 |
|
|
c->type = SSH_CHANNEL_ABANDONED; |
977 |
|
|
buffer_clear(c->input); |
978 |
|
|
chan_ibuf_empty(ssh, c); |
979 |
|
|
return 0; |
980 |
|
|
} else |
981 |
|
|
quit_pending = 1; |
982 |
|
|
return -1; |
983 |
|
|
|
984 |
|
|
case 'Z' - 64: |
985 |
|
|
/* XXX support this for mux clients */ |
986 |
|
|
if (c && c->ctl_chan != -1) { |
987 |
|
|
char b[16]; |
988 |
|
|
noescape: |
989 |
|
|
if (ch == 'Z' - 64) |
990 |
|
|
snprintf(b, sizeof b, "^Z"); |
991 |
|
|
else |
992 |
|
|
snprintf(b, sizeof b, "%c", ch); |
993 |
|
|
snprintf(string, sizeof string, |
994 |
|
|
"%c%s escape not available to " |
995 |
|
|
"multiplexed sessions\r\n", |
996 |
|
|
efc->escape_char, b); |
997 |
|
|
buffer_append(berr, string, |
998 |
|
|
strlen(string)); |
999 |
|
|
continue; |
1000 |
|
|
} |
1001 |
|
|
/* Suspend the program. Inform the user */ |
1002 |
|
|
snprintf(string, sizeof string, |
1003 |
|
|
"%c^Z [suspend ssh]\r\n", efc->escape_char); |
1004 |
|
|
buffer_append(berr, string, strlen(string)); |
1005 |
|
|
|
1006 |
|
|
/* Restore terminal modes and suspend. */ |
1007 |
|
|
client_suspend_self(bin, bout, berr); |
1008 |
|
|
|
1009 |
|
|
/* We have been continued. */ |
1010 |
|
|
continue; |
1011 |
|
|
|
1012 |
|
|
case 'B': |
1013 |
|
|
snprintf(string, sizeof string, |
1014 |
|
|
"%cB\r\n", efc->escape_char); |
1015 |
|
|
buffer_append(berr, string, strlen(string)); |
1016 |
|
|
channel_request_start(ssh, c->self, "break", 0); |
1017 |
|
|
packet_put_int(1000); |
1018 |
|
|
packet_send(); |
1019 |
|
|
continue; |
1020 |
|
|
|
1021 |
|
|
case 'R': |
1022 |
|
|
if (datafellows & SSH_BUG_NOREKEY) |
1023 |
|
|
logit("Server does not " |
1024 |
|
|
"support re-keying"); |
1025 |
|
|
else |
1026 |
|
|
need_rekeying = 1; |
1027 |
|
|
continue; |
1028 |
|
|
|
1029 |
|
|
case 'V': |
1030 |
|
|
/* FALLTHROUGH */ |
1031 |
|
|
case 'v': |
1032 |
|
|
if (c && c->ctl_chan != -1) |
1033 |
|
|
goto noescape; |
1034 |
|
|
if (!log_is_on_stderr()) { |
1035 |
|
|
snprintf(string, sizeof string, |
1036 |
|
|
"%c%c [Logging to syslog]\r\n", |
1037 |
|
|
efc->escape_char, ch); |
1038 |
|
|
buffer_append(berr, string, |
1039 |
|
|
strlen(string)); |
1040 |
|
|
continue; |
1041 |
|
|
} |
1042 |
|
|
if (ch == 'V' && options.log_level > |
1043 |
|
|
SYSLOG_LEVEL_QUIET) |
1044 |
|
|
log_change_level(--options.log_level); |
1045 |
|
|
if (ch == 'v' && options.log_level < |
1046 |
|
|
SYSLOG_LEVEL_DEBUG3) |
1047 |
|
|
log_change_level(++options.log_level); |
1048 |
|
|
snprintf(string, sizeof string, |
1049 |
|
|
"%c%c [LogLevel %s]\r\n", |
1050 |
|
|
efc->escape_char, ch, |
1051 |
|
|
log_level_name(options.log_level)); |
1052 |
|
|
buffer_append(berr, string, strlen(string)); |
1053 |
|
|
continue; |
1054 |
|
|
|
1055 |
|
|
case '&': |
1056 |
|
|
if (c && c->ctl_chan != -1) |
1057 |
|
|
goto noescape; |
1058 |
|
|
/* |
1059 |
|
|
* Detach the program (continue to serve |
1060 |
|
|
* connections, but put in background and no |
1061 |
|
|
* more new connections). |
1062 |
|
|
*/ |
1063 |
|
|
/* Restore tty modes. */ |
1064 |
|
|
leave_raw_mode( |
1065 |
|
|
options.request_tty == REQUEST_TTY_FORCE); |
1066 |
|
|
|
1067 |
|
|
/* Stop listening for new connections. */ |
1068 |
|
|
channel_stop_listening(ssh); |
1069 |
|
|
|
1070 |
|
|
snprintf(string, sizeof string, |
1071 |
|
|
"%c& [backgrounded]\n", efc->escape_char); |
1072 |
|
|
buffer_append(berr, string, strlen(string)); |
1073 |
|
|
|
1074 |
|
|
/* Fork into background. */ |
1075 |
|
|
pid = fork(); |
1076 |
|
|
if (pid < 0) { |
1077 |
|
|
error("fork: %.100s", strerror(errno)); |
1078 |
|
|
continue; |
1079 |
|
|
} |
1080 |
|
|
if (pid != 0) { /* This is the parent. */ |
1081 |
|
|
/* The parent just exits. */ |
1082 |
|
|
exit(0); |
1083 |
|
|
} |
1084 |
|
|
/* The child continues serving connections. */ |
1085 |
|
|
buffer_append(bin, "\004", 1); |
1086 |
|
|
/* fake EOF on stdin */ |
1087 |
|
|
return -1; |
1088 |
|
|
case '?': |
1089 |
|
|
print_escape_help(berr, efc->escape_char, |
1090 |
|
|
(c && c->ctl_chan != -1), |
1091 |
|
|
log_is_on_stderr()); |
1092 |
|
|
continue; |
1093 |
|
|
|
1094 |
|
|
case '#': |
1095 |
|
|
snprintf(string, sizeof string, "%c#\r\n", |
1096 |
|
|
efc->escape_char); |
1097 |
|
|
buffer_append(berr, string, strlen(string)); |
1098 |
|
|
s = channel_open_message(ssh); |
1099 |
|
|
buffer_append(berr, s, strlen(s)); |
1100 |
|
|
free(s); |
1101 |
|
|
continue; |
1102 |
|
|
|
1103 |
|
|
case 'C': |
1104 |
|
|
if (c && c->ctl_chan != -1) |
1105 |
|
|
goto noescape; |
1106 |
|
|
process_cmdline(ssh); |
1107 |
|
|
continue; |
1108 |
|
|
|
1109 |
|
|
default: |
1110 |
|
|
if (ch != efc->escape_char) { |
1111 |
|
|
buffer_put_char(bin, efc->escape_char); |
1112 |
|
|
bytes++; |
1113 |
|
|
} |
1114 |
|
|
/* Escaped characters fall through here */ |
1115 |
|
|
break; |
1116 |
|
|
} |
1117 |
|
|
} else { |
1118 |
|
|
/* |
1119 |
|
|
* The previous character was not an escape char. |
1120 |
|
|
* Check if this is an escape. |
1121 |
|
|
*/ |
1122 |
|
|
if (last_was_cr && ch == efc->escape_char) { |
1123 |
|
|
/* |
1124 |
|
|
* It is. Set the flag and continue to |
1125 |
|
|
* next character. |
1126 |
|
|
*/ |
1127 |
|
|
efc->escape_pending = 1; |
1128 |
|
|
continue; |
1129 |
|
|
} |
1130 |
|
|
} |
1131 |
|
|
|
1132 |
|
|
/* |
1133 |
|
|
* Normal character. Record whether it was a newline, |
1134 |
|
|
* and append it to the buffer. |
1135 |
|
|
*/ |
1136 |
|
|
last_was_cr = (ch == '\r' || ch == '\n'); |
1137 |
|
|
buffer_put_char(bin, ch); |
1138 |
|
|
bytes++; |
1139 |
|
|
} |
1140 |
|
|
return bytes; |
1141 |
|
|
} |
1142 |
|
|
|
1143 |
|
|
/* |
1144 |
|
|
* Get packets from the connection input buffer, and process them as long as |
1145 |
|
|
* there are packets available. |
1146 |
|
|
* |
1147 |
|
|
* Any unknown packets received during the actual |
1148 |
|
|
* session cause the session to terminate. This is |
1149 |
|
|
* intended to make debugging easier since no |
1150 |
|
|
* confirmations are sent. Any compatible protocol |
1151 |
|
|
* extensions must be negotiated during the |
1152 |
|
|
* preparatory phase. |
1153 |
|
|
*/ |
1154 |
|
|
|
1155 |
|
|
static void |
1156 |
|
|
client_process_buffered_input_packets(void) |
1157 |
|
|
{ |
1158 |
|
|
ssh_dispatch_run_fatal(active_state, DISPATCH_NONBLOCK, &quit_pending); |
1159 |
|
|
} |
1160 |
|
|
|
1161 |
|
|
/* scan buf[] for '~' before sending data to the peer */ |
1162 |
|
|
|
1163 |
|
|
/* Helper: allocate a new escape_filter_ctx and fill in its escape char */ |
1164 |
|
|
void * |
1165 |
|
|
client_new_escape_filter_ctx(int escape_char) |
1166 |
|
|
{ |
1167 |
|
|
struct escape_filter_ctx *ret; |
1168 |
|
|
|
1169 |
|
|
ret = xcalloc(1, sizeof(*ret)); |
1170 |
|
|
ret->escape_pending = 0; |
1171 |
|
|
ret->escape_char = escape_char; |
1172 |
|
|
return (void *)ret; |
1173 |
|
|
} |
1174 |
|
|
|
1175 |
|
|
/* Free the escape filter context on channel free */ |
1176 |
|
|
void |
1177 |
|
|
client_filter_cleanup(struct ssh *ssh, int cid, void *ctx) |
1178 |
|
|
{ |
1179 |
|
|
free(ctx); |
1180 |
|
|
} |
1181 |
|
|
|
1182 |
|
|
int |
1183 |
|
|
client_simple_escape_filter(struct ssh *ssh, Channel *c, char *buf, int len) |
1184 |
|
|
{ |
1185 |
|
|
if (c->extended_usage != CHAN_EXTENDED_WRITE) |
1186 |
|
|
return 0; |
1187 |
|
|
|
1188 |
|
|
return process_escapes(ssh, c, c->input, c->output, c->extended, |
1189 |
|
|
buf, len); |
1190 |
|
|
} |
1191 |
|
|
|
1192 |
|
|
static void |
1193 |
|
|
client_channel_closed(struct ssh *ssh, int id, void *arg) |
1194 |
|
|
{ |
1195 |
|
|
channel_cancel_cleanup(ssh, id); |
1196 |
|
|
session_closed = 1; |
1197 |
|
|
leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); |
1198 |
|
|
} |
1199 |
|
|
|
1200 |
|
|
/* |
1201 |
|
|
* Implements the interactive session with the server. This is called after |
1202 |
|
|
* the user has been authenticated, and a command has been started on the |
1203 |
|
|
* remote host. If escape_char != SSH_ESCAPECHAR_NONE, it is the character |
1204 |
|
|
* used as an escape character for terminating or suspending the session. |
1205 |
|
|
*/ |
1206 |
|
|
int |
1207 |
|
|
client_loop(struct ssh *ssh, int have_pty, int escape_char_arg, |
1208 |
|
|
int ssh2_chan_id) |
1209 |
|
|
{ |
1210 |
|
|
fd_set *readset = NULL, *writeset = NULL; |
1211 |
|
|
double start_time, total_time; |
1212 |
|
|
int r, max_fd = 0, max_fd2 = 0, len; |
1213 |
|
|
u_int64_t ibytes, obytes; |
1214 |
|
|
u_int nalloc = 0; |
1215 |
|
|
char buf[100]; |
1216 |
|
|
|
1217 |
|
|
debug("Entering interactive session."); |
1218 |
|
|
|
1219 |
|
|
if (options.control_master && |
1220 |
|
|
!option_clear_or_none(options.control_path)) { |
1221 |
|
|
debug("pledge: id"); |
1222 |
|
|
if (pledge("stdio rpath wpath cpath unix inet dns recvfd proc exec id tty flock rpath cpath wpath", |
1223 |
|
|
NULL) == -1) |
1224 |
|
|
fatal("%s pledge(): %s", __func__, strerror(errno)); |
1225 |
|
|
|
1226 |
|
|
} else if (options.forward_x11 || options.permit_local_command) { |
1227 |
|
|
debug("pledge: exec"); |
1228 |
|
|
if (pledge("stdio rpath wpath cpath unix inet dns proc exec tty flock rpath cpath wpath", |
1229 |
|
|
NULL) == -1) |
1230 |
|
|
fatal("%s pledge(): %s", __func__, strerror(errno)); |
1231 |
|
|
|
1232 |
|
|
} else if (options.update_hostkeys) { |
1233 |
|
|
debug("pledge: filesystem full"); |
1234 |
|
|
if (pledge("stdio rpath wpath cpath unix inet dns proc tty flock rpath cpath wpath", |
1235 |
|
|
NULL) == -1) |
1236 |
|
|
fatal("%s pledge(): %s", __func__, strerror(errno)); |
1237 |
|
|
|
1238 |
|
|
} else if (!option_clear_or_none(options.proxy_command) || |
1239 |
|
|
fork_after_authentication_flag) { |
1240 |
|
|
debug("pledge: proc"); |
1241 |
|
|
if (pledge("stdio cpath unix inet dns proc tty flock rpath wpath", NULL) == -1) |
1242 |
|
|
fatal("%s pledge(): %s", __func__, strerror(errno)); |
1243 |
|
|
|
1244 |
|
|
} else { |
1245 |
|
|
debug("pledge: network"); |
1246 |
|
|
if (pledge("stdio unix inet dns proc tty flock rpath cpath wpath", NULL) == -1) |
1247 |
|
|
fatal("%s pledge(): %s", __func__, strerror(errno)); |
1248 |
|
|
} |
1249 |
|
|
|
1250 |
|
|
start_time = get_current_time(); |
1251 |
|
|
|
1252 |
|
|
/* Initialize variables. */ |
1253 |
|
|
last_was_cr = 1; |
1254 |
|
|
exit_status = -1; |
1255 |
|
|
connection_in = packet_get_connection_in(); |
1256 |
|
|
connection_out = packet_get_connection_out(); |
1257 |
|
|
max_fd = MAXIMUM(connection_in, connection_out); |
1258 |
|
|
|
1259 |
|
|
quit_pending = 0; |
1260 |
|
|
|
1261 |
|
|
/* Initialize buffers. */ |
1262 |
|
|
buffer_init(&stderr_buffer); |
1263 |
|
|
|
1264 |
|
|
client_init_dispatch(); |
1265 |
|
|
|
1266 |
|
|
/* |
1267 |
|
|
* Set signal handlers, (e.g. to restore non-blocking mode) |
1268 |
|
|
* but don't overwrite SIG_IGN, matches behaviour from rsh(1) |
1269 |
|
|
*/ |
1270 |
|
|
if (signal(SIGHUP, SIG_IGN) != SIG_IGN) |
1271 |
|
|
signal(SIGHUP, signal_handler); |
1272 |
|
|
if (signal(SIGINT, SIG_IGN) != SIG_IGN) |
1273 |
|
|
signal(SIGINT, signal_handler); |
1274 |
|
|
if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) |
1275 |
|
|
signal(SIGQUIT, signal_handler); |
1276 |
|
|
if (signal(SIGTERM, SIG_IGN) != SIG_IGN) |
1277 |
|
|
signal(SIGTERM, signal_handler); |
1278 |
|
|
signal(SIGWINCH, window_change_handler); |
1279 |
|
|
|
1280 |
|
|
if (have_pty) |
1281 |
|
|
enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); |
1282 |
|
|
|
1283 |
|
|
session_ident = ssh2_chan_id; |
1284 |
|
|
if (session_ident != -1) { |
1285 |
|
|
if (escape_char_arg != SSH_ESCAPECHAR_NONE) { |
1286 |
|
|
channel_register_filter(ssh, session_ident, |
1287 |
|
|
client_simple_escape_filter, NULL, |
1288 |
|
|
client_filter_cleanup, |
1289 |
|
|
client_new_escape_filter_ctx( |
1290 |
|
|
escape_char_arg)); |
1291 |
|
|
} |
1292 |
|
|
channel_register_cleanup(ssh, session_ident, |
1293 |
|
|
client_channel_closed, 0); |
1294 |
|
|
} |
1295 |
|
|
|
1296 |
|
|
/* Main loop of the client for the interactive session mode. */ |
1297 |
|
|
while (!quit_pending) { |
1298 |
|
|
|
1299 |
|
|
/* Process buffered packets sent by the server. */ |
1300 |
|
|
client_process_buffered_input_packets(); |
1301 |
|
|
|
1302 |
|
|
if (session_closed && !channel_still_open(ssh)) |
1303 |
|
|
break; |
1304 |
|
|
|
1305 |
|
|
if (ssh_packet_is_rekeying(ssh)) { |
1306 |
|
|
debug("rekeying in progress"); |
1307 |
|
|
} else if (need_rekeying) { |
1308 |
|
|
/* manual rekey request */ |
1309 |
|
|
debug("need rekeying"); |
1310 |
|
|
if ((r = kex_start_rekex(ssh)) != 0) |
1311 |
|
|
fatal("%s: kex_start_rekex: %s", __func__, |
1312 |
|
|
ssh_err(r)); |
1313 |
|
|
need_rekeying = 0; |
1314 |
|
|
} else { |
1315 |
|
|
/* |
1316 |
|
|
* Make packets from buffered channel data, and |
1317 |
|
|
* enqueue them for sending to the server. |
1318 |
|
|
*/ |
1319 |
|
|
if (packet_not_very_much_data_to_write()) |
1320 |
|
|
channel_output_poll(ssh); |
1321 |
|
|
|
1322 |
|
|
/* |
1323 |
|
|
* Check if the window size has changed, and buffer a |
1324 |
|
|
* message about it to the server if so. |
1325 |
|
|
*/ |
1326 |
|
|
client_check_window_change(ssh); |
1327 |
|
|
|
1328 |
|
|
if (quit_pending) |
1329 |
|
|
break; |
1330 |
|
|
} |
1331 |
|
|
/* |
1332 |
|
|
* Wait until we have something to do (something becomes |
1333 |
|
|
* available on one of the descriptors). |
1334 |
|
|
*/ |
1335 |
|
|
max_fd2 = max_fd; |
1336 |
|
|
client_wait_until_can_do_something(ssh, &readset, &writeset, |
1337 |
|
|
&max_fd2, &nalloc, ssh_packet_is_rekeying(ssh)); |
1338 |
|
|
|
1339 |
|
|
if (quit_pending) |
1340 |
|
|
break; |
1341 |
|
|
|
1342 |
|
|
/* Do channel operations unless rekeying in progress. */ |
1343 |
|
|
if (!ssh_packet_is_rekeying(ssh)) |
1344 |
|
|
channel_after_select(ssh, readset, writeset); |
1345 |
|
|
|
1346 |
|
|
/* Buffer input from the connection. */ |
1347 |
|
|
client_process_net_input(readset); |
1348 |
|
|
|
1349 |
|
|
if (quit_pending) |
1350 |
|
|
break; |
1351 |
|
|
|
1352 |
|
|
/* |
1353 |
|
|
* Send as much buffered packet data as possible to the |
1354 |
|
|
* sender. |
1355 |
|
|
*/ |
1356 |
|
|
if (FD_ISSET(connection_out, writeset)) |
1357 |
|
|
packet_write_poll(); |
1358 |
|
|
|
1359 |
|
|
/* |
1360 |
|
|
* If we are a backgrounded control master, and the |
1361 |
|
|
* timeout has expired without any active client |
1362 |
|
|
* connections, then quit. |
1363 |
|
|
*/ |
1364 |
|
|
if (control_persist_exit_time > 0) { |
1365 |
|
|
if (monotime() >= control_persist_exit_time) { |
1366 |
|
|
debug("ControlPersist timeout expired"); |
1367 |
|
|
break; |
1368 |
|
|
} |
1369 |
|
|
} |
1370 |
|
|
} |
1371 |
|
|
free(readset); |
1372 |
|
|
free(writeset); |
1373 |
|
|
|
1374 |
|
|
/* Terminate the session. */ |
1375 |
|
|
|
1376 |
|
|
/* Stop watching for window change. */ |
1377 |
|
|
signal(SIGWINCH, SIG_DFL); |
1378 |
|
|
|
1379 |
|
|
packet_start(SSH2_MSG_DISCONNECT); |
1380 |
|
|
packet_put_int(SSH2_DISCONNECT_BY_APPLICATION); |
1381 |
|
|
packet_put_cstring("disconnected by user"); |
1382 |
|
|
packet_put_cstring(""); /* language tag */ |
1383 |
|
|
packet_send(); |
1384 |
|
|
packet_write_wait(); |
1385 |
|
|
|
1386 |
|
|
channel_free_all(ssh); |
1387 |
|
|
|
1388 |
|
|
if (have_pty) |
1389 |
|
|
leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); |
1390 |
|
|
|
1391 |
|
|
/* restore blocking io */ |
1392 |
|
|
if (!isatty(fileno(stdin))) |
1393 |
|
|
unset_nonblock(fileno(stdin)); |
1394 |
|
|
if (!isatty(fileno(stdout))) |
1395 |
|
|
unset_nonblock(fileno(stdout)); |
1396 |
|
|
if (!isatty(fileno(stderr))) |
1397 |
|
|
unset_nonblock(fileno(stderr)); |
1398 |
|
|
|
1399 |
|
|
/* |
1400 |
|
|
* If there was no shell or command requested, there will be no remote |
1401 |
|
|
* exit status to be returned. In that case, clear error code if the |
1402 |
|
|
* connection was deliberately terminated at this end. |
1403 |
|
|
*/ |
1404 |
|
|
if (no_shell_flag && received_signal == SIGTERM) { |
1405 |
|
|
received_signal = 0; |
1406 |
|
|
exit_status = 0; |
1407 |
|
|
} |
1408 |
|
|
|
1409 |
|
|
if (received_signal) { |
1410 |
|
|
verbose("Killed by signal %d.", (int) received_signal); |
1411 |
|
|
cleanup_exit(0); |
1412 |
|
|
} |
1413 |
|
|
|
1414 |
|
|
/* |
1415 |
|
|
* In interactive mode (with pseudo tty) display a message indicating |
1416 |
|
|
* that the connection has been closed. |
1417 |
|
|
*/ |
1418 |
|
|
if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) { |
1419 |
|
|
snprintf(buf, sizeof buf, |
1420 |
|
|
"Connection to %.64s closed.\r\n", host); |
1421 |
|
|
buffer_append(&stderr_buffer, buf, strlen(buf)); |
1422 |
|
|
} |
1423 |
|
|
|
1424 |
|
|
/* Output any buffered data for stderr. */ |
1425 |
|
|
if (buffer_len(&stderr_buffer) > 0) { |
1426 |
|
|
len = atomicio(vwrite, fileno(stderr), |
1427 |
|
|
buffer_ptr(&stderr_buffer), buffer_len(&stderr_buffer)); |
1428 |
|
|
if (len < 0 || (u_int)len != buffer_len(&stderr_buffer)) |
1429 |
|
|
error("Write failed flushing stderr buffer."); |
1430 |
|
|
else |
1431 |
|
|
buffer_consume(&stderr_buffer, len); |
1432 |
|
|
} |
1433 |
|
|
|
1434 |
|
|
/* Clear and free any buffers. */ |
1435 |
|
|
explicit_bzero(buf, sizeof(buf)); |
1436 |
|
|
buffer_free(&stderr_buffer); |
1437 |
|
|
|
1438 |
|
|
/* Report bytes transferred, and transfer rates. */ |
1439 |
|
|
total_time = get_current_time() - start_time; |
1440 |
|
|
packet_get_bytes(&ibytes, &obytes); |
1441 |
|
|
verbose("Transferred: sent %llu, received %llu bytes, in %.1f seconds", |
1442 |
|
|
(unsigned long long)obytes, (unsigned long long)ibytes, total_time); |
1443 |
|
|
if (total_time > 0) |
1444 |
|
|
verbose("Bytes per second: sent %.1f, received %.1f", |
1445 |
|
|
obytes / total_time, ibytes / total_time); |
1446 |
|
|
/* Return the exit status of the program. */ |
1447 |
|
|
debug("Exit status %d", exit_status); |
1448 |
|
|
return exit_status; |
1449 |
|
|
} |
1450 |
|
|
|
1451 |
|
|
/*********/ |
1452 |
|
|
|
1453 |
|
|
static Channel * |
1454 |
|
|
client_request_forwarded_tcpip(struct ssh *ssh, const char *request_type, |
1455 |
|
|
int rchan, u_int rwindow, u_int rmaxpack) |
1456 |
|
|
{ |
1457 |
|
|
Channel *c = NULL; |
1458 |
|
|
struct sshbuf *b = NULL; |
1459 |
|
|
char *listen_address, *originator_address; |
1460 |
|
|
u_short listen_port, originator_port; |
1461 |
|
|
int r; |
1462 |
|
|
|
1463 |
|
|
/* Get rest of the packet */ |
1464 |
|
|
listen_address = packet_get_string(NULL); |
1465 |
|
|
listen_port = packet_get_int(); |
1466 |
|
|
originator_address = packet_get_string(NULL); |
1467 |
|
|
originator_port = packet_get_int(); |
1468 |
|
|
packet_check_eom(); |
1469 |
|
|
|
1470 |
|
|
debug("%s: listen %s port %d, originator %s port %d", __func__, |
1471 |
|
|
listen_address, listen_port, originator_address, originator_port); |
1472 |
|
|
|
1473 |
|
|
c = channel_connect_by_listen_address(ssh, listen_address, listen_port, |
1474 |
|
|
"forwarded-tcpip", originator_address); |
1475 |
|
|
|
1476 |
|
|
if (c != NULL && c->type == SSH_CHANNEL_MUX_CLIENT) { |
1477 |
|
|
if ((b = sshbuf_new()) == NULL) { |
1478 |
|
|
error("%s: alloc reply", __func__); |
1479 |
|
|
goto out; |
1480 |
|
|
} |
1481 |
|
|
/* reconstruct and send to muxclient */ |
1482 |
|
|
if ((r = sshbuf_put_u8(b, 0)) != 0 || /* padlen */ |
1483 |
|
|
(r = sshbuf_put_u8(b, SSH2_MSG_CHANNEL_OPEN)) != 0 || |
1484 |
|
|
(r = sshbuf_put_cstring(b, request_type)) != 0 || |
1485 |
|
|
(r = sshbuf_put_u32(b, rchan)) != 0 || |
1486 |
|
|
(r = sshbuf_put_u32(b, rwindow)) != 0 || |
1487 |
|
|
(r = sshbuf_put_u32(b, rmaxpack)) != 0 || |
1488 |
|
|
(r = sshbuf_put_cstring(b, listen_address)) != 0 || |
1489 |
|
|
(r = sshbuf_put_u32(b, listen_port)) != 0 || |
1490 |
|
|
(r = sshbuf_put_cstring(b, originator_address)) != 0 || |
1491 |
|
|
(r = sshbuf_put_u32(b, originator_port)) != 0 || |
1492 |
|
|
(r = sshbuf_put_stringb(c->output, b)) != 0) { |
1493 |
|
|
error("%s: compose for muxclient %s", __func__, |
1494 |
|
|
ssh_err(r)); |
1495 |
|
|
goto out; |
1496 |
|
|
} |
1497 |
|
|
} |
1498 |
|
|
|
1499 |
|
|
out: |
1500 |
|
|
sshbuf_free(b); |
1501 |
|
|
free(originator_address); |
1502 |
|
|
free(listen_address); |
1503 |
|
|
return c; |
1504 |
|
|
} |
1505 |
|
|
|
1506 |
|
|
static Channel * |
1507 |
|
|
client_request_forwarded_streamlocal(struct ssh *ssh, |
1508 |
|
|
const char *request_type, int rchan) |
1509 |
|
|
{ |
1510 |
|
|
Channel *c = NULL; |
1511 |
|
|
char *listen_path; |
1512 |
|
|
|
1513 |
|
|
/* Get the remote path. */ |
1514 |
|
|
listen_path = packet_get_string(NULL); |
1515 |
|
|
/* XXX: Skip reserved field for now. */ |
1516 |
|
|
if (packet_get_string_ptr(NULL) == NULL) |
1517 |
|
|
fatal("%s: packet_get_string_ptr failed", __func__); |
1518 |
|
|
packet_check_eom(); |
1519 |
|
|
|
1520 |
|
|
debug("%s: %s", __func__, listen_path); |
1521 |
|
|
|
1522 |
|
|
c = channel_connect_by_listen_path(ssh, listen_path, |
1523 |
|
|
"forwarded-streamlocal@openssh.com", "forwarded-streamlocal"); |
1524 |
|
|
free(listen_path); |
1525 |
|
|
return c; |
1526 |
|
|
} |
1527 |
|
|
|
1528 |
|
|
static Channel * |
1529 |
|
|
client_request_x11(struct ssh *ssh, const char *request_type, int rchan) |
1530 |
|
|
{ |
1531 |
|
|
Channel *c = NULL; |
1532 |
|
|
char *originator; |
1533 |
|
|
u_short originator_port; |
1534 |
|
|
int sock; |
1535 |
|
|
|
1536 |
|
|
if (!options.forward_x11) { |
1537 |
|
|
error("Warning: ssh server tried X11 forwarding."); |
1538 |
|
|
error("Warning: this is probably a break-in attempt by a " |
1539 |
|
|
"malicious server."); |
1540 |
|
|
return NULL; |
1541 |
|
|
} |
1542 |
|
|
if (x11_refuse_time != 0 && (u_int)monotime() >= x11_refuse_time) { |
1543 |
|
|
verbose("Rejected X11 connection after ForwardX11Timeout " |
1544 |
|
|
"expired"); |
1545 |
|
|
return NULL; |
1546 |
|
|
} |
1547 |
|
|
originator = packet_get_string(NULL); |
1548 |
|
|
if (datafellows & SSH_BUG_X11FWD) { |
1549 |
|
|
debug2("buggy server: x11 request w/o originator_port"); |
1550 |
|
|
originator_port = 0; |
1551 |
|
|
} else { |
1552 |
|
|
originator_port = packet_get_int(); |
1553 |
|
|
} |
1554 |
|
|
packet_check_eom(); |
1555 |
|
|
/* XXX check permission */ |
1556 |
|
|
debug("client_request_x11: request from %s %d", originator, |
1557 |
|
|
originator_port); |
1558 |
|
|
free(originator); |
1559 |
|
|
sock = x11_connect_display(ssh); |
1560 |
|
|
if (sock < 0) |
1561 |
|
|
return NULL; |
1562 |
|
|
c = channel_new(ssh, "x11", |
1563 |
|
|
SSH_CHANNEL_X11_OPEN, sock, sock, -1, |
1564 |
|
|
CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "x11", 1); |
1565 |
|
|
c->force_drain = 1; |
1566 |
|
|
return c; |
1567 |
|
|
} |
1568 |
|
|
|
1569 |
|
|
static Channel * |
1570 |
|
|
client_request_agent(struct ssh *ssh, const char *request_type, int rchan) |
1571 |
|
|
{ |
1572 |
|
|
Channel *c = NULL; |
1573 |
|
|
int r, sock; |
1574 |
|
|
|
1575 |
|
|
if (!options.forward_agent) { |
1576 |
|
|
error("Warning: ssh server tried agent forwarding."); |
1577 |
|
|
error("Warning: this is probably a break-in attempt by a " |
1578 |
|
|
"malicious server."); |
1579 |
|
|
return NULL; |
1580 |
|
|
} |
1581 |
|
|
if ((r = ssh_get_authentication_socket(&sock)) != 0) { |
1582 |
|
|
if (r != SSH_ERR_AGENT_NOT_PRESENT) |
1583 |
|
|
debug("%s: ssh_get_authentication_socket: %s", |
1584 |
|
|
__func__, ssh_err(r)); |
1585 |
|
|
return NULL; |
1586 |
|
|
} |
1587 |
|
|
c = channel_new(ssh, "authentication agent connection", |
1588 |
|
|
SSH_CHANNEL_OPEN, sock, sock, -1, |
1589 |
|
|
CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, |
1590 |
|
|
"authentication agent connection", 1); |
1591 |
|
|
c->force_drain = 1; |
1592 |
|
|
return c; |
1593 |
|
|
} |
1594 |
|
|
|
1595 |
|
|
char * |
1596 |
|
|
client_request_tun_fwd(struct ssh *ssh, int tun_mode, |
1597 |
|
|
int local_tun, int remote_tun) |
1598 |
|
|
{ |
1599 |
|
|
Channel *c; |
1600 |
|
|
int fd; |
1601 |
|
|
char *ifname = NULL; |
1602 |
|
|
|
1603 |
|
|
if (tun_mode == SSH_TUNMODE_NO) |
1604 |
|
|
return 0; |
1605 |
|
|
|
1606 |
|
|
debug("Requesting tun unit %d in mode %d", local_tun, tun_mode); |
1607 |
|
|
|
1608 |
|
|
/* Open local tunnel device */ |
1609 |
|
|
if ((fd = tun_open(local_tun, tun_mode, &ifname)) == -1) { |
1610 |
|
|
error("Tunnel device open failed."); |
1611 |
|
|
return NULL; |
1612 |
|
|
} |
1613 |
|
|
debug("Tunnel forwarding using interface %s", ifname); |
1614 |
|
|
|
1615 |
|
|
c = channel_new(ssh, "tun", SSH_CHANNEL_OPENING, fd, fd, -1, |
1616 |
|
|
CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1); |
1617 |
|
|
c->datagram = 1; |
1618 |
|
|
|
1619 |
|
|
packet_start(SSH2_MSG_CHANNEL_OPEN); |
1620 |
|
|
packet_put_cstring("tun@openssh.com"); |
1621 |
|
|
packet_put_int(c->self); |
1622 |
|
|
packet_put_int(c->local_window_max); |
1623 |
|
|
packet_put_int(c->local_maxpacket); |
1624 |
|
|
packet_put_int(tun_mode); |
1625 |
|
|
packet_put_int(remote_tun); |
1626 |
|
|
packet_send(); |
1627 |
|
|
|
1628 |
|
|
return ifname; |
1629 |
|
|
} |
1630 |
|
|
|
1631 |
|
|
/* XXXX move to generic input handler */ |
1632 |
|
|
static int |
1633 |
|
|
client_input_channel_open(int type, u_int32_t seq, struct ssh *ssh) |
1634 |
|
|
{ |
1635 |
|
|
Channel *c = NULL; |
1636 |
|
|
char *ctype; |
1637 |
|
|
int rchan; |
1638 |
|
|
u_int rmaxpack, rwindow, len; |
1639 |
|
|
|
1640 |
|
|
ctype = packet_get_string(&len); |
1641 |
|
|
rchan = packet_get_int(); |
1642 |
|
|
rwindow = packet_get_int(); |
1643 |
|
|
rmaxpack = packet_get_int(); |
1644 |
|
|
|
1645 |
|
|
debug("client_input_channel_open: ctype %s rchan %d win %d max %d", |
1646 |
|
|
ctype, rchan, rwindow, rmaxpack); |
1647 |
|
|
|
1648 |
|
|
if (strcmp(ctype, "forwarded-tcpip") == 0) { |
1649 |
|
|
c = client_request_forwarded_tcpip(ssh, ctype, rchan, rwindow, |
1650 |
|
|
rmaxpack); |
1651 |
|
|
} else if (strcmp(ctype, "forwarded-streamlocal@openssh.com") == 0) { |
1652 |
|
|
c = client_request_forwarded_streamlocal(ssh, ctype, rchan); |
1653 |
|
|
} else if (strcmp(ctype, "x11") == 0) { |
1654 |
|
|
c = client_request_x11(ssh, ctype, rchan); |
1655 |
|
|
} else if (strcmp(ctype, "auth-agent@openssh.com") == 0) { |
1656 |
|
|
c = client_request_agent(ssh, ctype, rchan); |
1657 |
|
|
} |
1658 |
|
|
if (c != NULL && c->type == SSH_CHANNEL_MUX_CLIENT) { |
1659 |
|
|
debug3("proxied to downstream: %s", ctype); |
1660 |
|
|
} else if (c != NULL) { |
1661 |
|
|
debug("confirm %s", ctype); |
1662 |
|
|
c->remote_id = rchan; |
1663 |
|
|
c->have_remote_id = 1; |
1664 |
|
|
c->remote_window = rwindow; |
1665 |
|
|
c->remote_maxpacket = rmaxpack; |
1666 |
|
|
if (c->type != SSH_CHANNEL_CONNECTING) { |
1667 |
|
|
packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); |
1668 |
|
|
packet_put_int(c->remote_id); |
1669 |
|
|
packet_put_int(c->self); |
1670 |
|
|
packet_put_int(c->local_window); |
1671 |
|
|
packet_put_int(c->local_maxpacket); |
1672 |
|
|
packet_send(); |
1673 |
|
|
} |
1674 |
|
|
} else { |
1675 |
|
|
debug("failure %s", ctype); |
1676 |
|
|
packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); |
1677 |
|
|
packet_put_int(rchan); |
1678 |
|
|
packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED); |
1679 |
|
|
if (!(datafellows & SSH_BUG_OPENFAILURE)) { |
1680 |
|
|
packet_put_cstring("open failed"); |
1681 |
|
|
packet_put_cstring(""); |
1682 |
|
|
} |
1683 |
|
|
packet_send(); |
1684 |
|
|
} |
1685 |
|
|
free(ctype); |
1686 |
|
|
return 0; |
1687 |
|
|
} |
1688 |
|
|
|
1689 |
|
|
static int |
1690 |
|
|
client_input_channel_req(int type, u_int32_t seq, struct ssh *ssh) |
1691 |
|
|
{ |
1692 |
|
|
Channel *c = NULL; |
1693 |
|
|
int exitval, id, reply, success = 0; |
1694 |
|
|
char *rtype; |
1695 |
|
|
|
1696 |
|
|
id = packet_get_int(); |
1697 |
|
|
c = channel_lookup(ssh, id); |
1698 |
|
|
if (channel_proxy_upstream(c, type, seq, ssh)) |
1699 |
|
|
return 0; |
1700 |
|
|
rtype = packet_get_string(NULL); |
1701 |
|
|
reply = packet_get_char(); |
1702 |
|
|
|
1703 |
|
|
debug("client_input_channel_req: channel %d rtype %s reply %d", |
1704 |
|
|
id, rtype, reply); |
1705 |
|
|
|
1706 |
|
|
if (id == -1) { |
1707 |
|
|
error("client_input_channel_req: request for channel -1"); |
1708 |
|
|
} else if (c == NULL) { |
1709 |
|
|
error("client_input_channel_req: channel %d: " |
1710 |
|
|
"unknown channel", id); |
1711 |
|
|
} else if (strcmp(rtype, "eow@openssh.com") == 0) { |
1712 |
|
|
packet_check_eom(); |
1713 |
|
|
chan_rcvd_eow(ssh, c); |
1714 |
|
|
} else if (strcmp(rtype, "exit-status") == 0) { |
1715 |
|
|
exitval = packet_get_int(); |
1716 |
|
|
if (c->ctl_chan != -1) { |
1717 |
|
|
mux_exit_message(ssh, c, exitval); |
1718 |
|
|
success = 1; |
1719 |
|
|
} else if (id == session_ident) { |
1720 |
|
|
/* Record exit value of local session */ |
1721 |
|
|
success = 1; |
1722 |
|
|
exit_status = exitval; |
1723 |
|
|
} else { |
1724 |
|
|
/* Probably for a mux channel that has already closed */ |
1725 |
|
|
debug("%s: no sink for exit-status on channel %d", |
1726 |
|
|
__func__, id); |
1727 |
|
|
} |
1728 |
|
|
packet_check_eom(); |
1729 |
|
|
} |
1730 |
|
|
if (reply && c != NULL && !(c->flags & CHAN_CLOSE_SENT)) { |
1731 |
|
|
if (!c->have_remote_id) |
1732 |
|
|
fatal("%s: channel %d: no remote_id", |
1733 |
|
|
__func__, c->self); |
1734 |
|
|
packet_start(success ? |
1735 |
|
|
SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); |
1736 |
|
|
packet_put_int(c->remote_id); |
1737 |
|
|
packet_send(); |
1738 |
|
|
} |
1739 |
|
|
free(rtype); |
1740 |
|
|
return 0; |
1741 |
|
|
} |
1742 |
|
|
|
1743 |
|
|
struct hostkeys_update_ctx { |
1744 |
|
|
/* The hostname and (optionally) IP address string for the server */ |
1745 |
|
|
char *host_str, *ip_str; |
1746 |
|
|
|
1747 |
|
|
/* |
1748 |
|
|
* Keys received from the server and a flag for each indicating |
1749 |
|
|
* whether they already exist in known_hosts. |
1750 |
|
|
* keys_seen is filled in by hostkeys_find() and later (for new |
1751 |
|
|
* keys) by client_global_hostkeys_private_confirm(). |
1752 |
|
|
*/ |
1753 |
|
|
struct sshkey **keys; |
1754 |
|
|
int *keys_seen; |
1755 |
|
|
size_t nkeys, nnew; |
1756 |
|
|
|
1757 |
|
|
/* |
1758 |
|
|
* Keys that are in known_hosts, but were not present in the update |
1759 |
|
|
* from the server (i.e. scheduled to be deleted). |
1760 |
|
|
* Filled in by hostkeys_find(). |
1761 |
|
|
*/ |
1762 |
|
|
struct sshkey **old_keys; |
1763 |
|
|
size_t nold; |
1764 |
|
|
}; |
1765 |
|
|
|
1766 |
|
|
static void |
1767 |
|
|
hostkeys_update_ctx_free(struct hostkeys_update_ctx *ctx) |
1768 |
|
|
{ |
1769 |
|
|
size_t i; |
1770 |
|
|
|
1771 |
|
|
if (ctx == NULL) |
1772 |
|
|
return; |
1773 |
|
|
for (i = 0; i < ctx->nkeys; i++) |
1774 |
|
|
sshkey_free(ctx->keys[i]); |
1775 |
|
|
free(ctx->keys); |
1776 |
|
|
free(ctx->keys_seen); |
1777 |
|
|
for (i = 0; i < ctx->nold; i++) |
1778 |
|
|
sshkey_free(ctx->old_keys[i]); |
1779 |
|
|
free(ctx->old_keys); |
1780 |
|
|
free(ctx->host_str); |
1781 |
|
|
free(ctx->ip_str); |
1782 |
|
|
free(ctx); |
1783 |
|
|
} |
1784 |
|
|
|
1785 |
|
|
static int |
1786 |
|
|
hostkeys_find(struct hostkey_foreach_line *l, void *_ctx) |
1787 |
|
|
{ |
1788 |
|
|
struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; |
1789 |
|
|
size_t i; |
1790 |
|
|
struct sshkey **tmp; |
1791 |
|
|
|
1792 |
|
|
if (l->status != HKF_STATUS_MATCHED || l->key == NULL) |
1793 |
|
|
return 0; |
1794 |
|
|
|
1795 |
|
|
/* Mark off keys we've already seen for this host */ |
1796 |
|
|
for (i = 0; i < ctx->nkeys; i++) { |
1797 |
|
|
if (sshkey_equal(l->key, ctx->keys[i])) { |
1798 |
|
|
debug3("%s: found %s key at %s:%ld", __func__, |
1799 |
|
|
sshkey_ssh_name(ctx->keys[i]), l->path, l->linenum); |
1800 |
|
|
ctx->keys_seen[i] = 1; |
1801 |
|
|
return 0; |
1802 |
|
|
} |
1803 |
|
|
} |
1804 |
|
|
/* This line contained a key that not offered by the server */ |
1805 |
|
|
debug3("%s: deprecated %s key at %s:%ld", __func__, |
1806 |
|
|
sshkey_ssh_name(l->key), l->path, l->linenum); |
1807 |
|
|
if ((tmp = recallocarray(ctx->old_keys, ctx->nold, ctx->nold + 1, |
1808 |
|
|
sizeof(*ctx->old_keys))) == NULL) |
1809 |
|
|
fatal("%s: recallocarray failed nold = %zu", |
1810 |
|
|
__func__, ctx->nold); |
1811 |
|
|
ctx->old_keys = tmp; |
1812 |
|
|
ctx->old_keys[ctx->nold++] = l->key; |
1813 |
|
|
l->key = NULL; |
1814 |
|
|
|
1815 |
|
|
return 0; |
1816 |
|
|
} |
1817 |
|
|
|
1818 |
|
|
static void |
1819 |
|
|
update_known_hosts(struct hostkeys_update_ctx *ctx) |
1820 |
|
|
{ |
1821 |
|
|
int r, was_raw = 0; |
1822 |
|
|
int loglevel = options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK ? |
1823 |
|
|
SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_VERBOSE; |
1824 |
|
|
char *fp, *response; |
1825 |
|
|
size_t i; |
1826 |
|
|
|
1827 |
|
|
for (i = 0; i < ctx->nkeys; i++) { |
1828 |
|
|
if (ctx->keys_seen[i] != 2) |
1829 |
|
|
continue; |
1830 |
|
|
if ((fp = sshkey_fingerprint(ctx->keys[i], |
1831 |
|
|
options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) |
1832 |
|
|
fatal("%s: sshkey_fingerprint failed", __func__); |
1833 |
|
|
do_log2(loglevel, "Learned new hostkey: %s %s", |
1834 |
|
|
sshkey_type(ctx->keys[i]), fp); |
1835 |
|
|
free(fp); |
1836 |
|
|
} |
1837 |
|
|
for (i = 0; i < ctx->nold; i++) { |
1838 |
|
|
if ((fp = sshkey_fingerprint(ctx->old_keys[i], |
1839 |
|
|
options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) |
1840 |
|
|
fatal("%s: sshkey_fingerprint failed", __func__); |
1841 |
|
|
do_log2(loglevel, "Deprecating obsolete hostkey: %s %s", |
1842 |
|
|
sshkey_type(ctx->old_keys[i]), fp); |
1843 |
|
|
free(fp); |
1844 |
|
|
} |
1845 |
|
|
if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK) { |
1846 |
|
|
if (get_saved_tio() != NULL) { |
1847 |
|
|
leave_raw_mode(1); |
1848 |
|
|
was_raw = 1; |
1849 |
|
|
} |
1850 |
|
|
response = NULL; |
1851 |
|
|
for (i = 0; !quit_pending && i < 3; i++) { |
1852 |
|
|
free(response); |
1853 |
|
|
response = read_passphrase("Accept updated hostkeys? " |
1854 |
|
|
"(yes/no): ", RP_ECHO); |
1855 |
|
|
if (strcasecmp(response, "yes") == 0) |
1856 |
|
|
break; |
1857 |
|
|
else if (quit_pending || response == NULL || |
1858 |
|
|
strcasecmp(response, "no") == 0) { |
1859 |
|
|
options.update_hostkeys = 0; |
1860 |
|
|
break; |
1861 |
|
|
} else { |
1862 |
|
|
do_log2(loglevel, "Please enter " |
1863 |
|
|
"\"yes\" or \"no\""); |
1864 |
|
|
} |
1865 |
|
|
} |
1866 |
|
|
if (quit_pending || i >= 3 || response == NULL) |
1867 |
|
|
options.update_hostkeys = 0; |
1868 |
|
|
free(response); |
1869 |
|
|
if (was_raw) |
1870 |
|
|
enter_raw_mode(1); |
1871 |
|
|
} |
1872 |
|
|
|
1873 |
|
|
/* |
1874 |
|
|
* Now that all the keys are verified, we can go ahead and replace |
1875 |
|
|
* them in known_hosts (assuming SSH_UPDATE_HOSTKEYS_ASK didn't |
1876 |
|
|
* cancel the operation). |
1877 |
|
|
*/ |
1878 |
|
|
if (options.update_hostkeys != 0 && |
1879 |
|
|
(r = hostfile_replace_entries(options.user_hostfiles[0], |
1880 |
|
|
ctx->host_str, ctx->ip_str, ctx->keys, ctx->nkeys, |
1881 |
|
|
options.hash_known_hosts, 0, |
1882 |
|
|
options.fingerprint_hash)) != 0) |
1883 |
|
|
error("%s: hostfile_replace_entries failed: %s", |
1884 |
|
|
__func__, ssh_err(r)); |
1885 |
|
|
} |
1886 |
|
|
|
1887 |
|
|
static void |
1888 |
|
|
client_global_hostkeys_private_confirm(struct ssh *ssh, int type, |
1889 |
|
|
u_int32_t seq, void *_ctx) |
1890 |
|
|
{ |
1891 |
|
|
struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; |
1892 |
|
|
size_t i, ndone; |
1893 |
|
|
struct sshbuf *signdata; |
1894 |
|
|
int r; |
1895 |
|
|
const u_char *sig; |
1896 |
|
|
size_t siglen; |
1897 |
|
|
|
1898 |
|
|
if (ctx->nnew == 0) |
1899 |
|
|
fatal("%s: ctx->nnew == 0", __func__); /* sanity */ |
1900 |
|
|
if (type != SSH2_MSG_REQUEST_SUCCESS) { |
1901 |
|
|
error("Server failed to confirm ownership of " |
1902 |
|
|
"private host keys"); |
1903 |
|
|
hostkeys_update_ctx_free(ctx); |
1904 |
|
|
return; |
1905 |
|
|
} |
1906 |
|
|
if ((signdata = sshbuf_new()) == NULL) |
1907 |
|
|
fatal("%s: sshbuf_new failed", __func__); |
1908 |
|
|
/* Don't want to accidentally accept an unbound signature */ |
1909 |
|
|
if (ssh->kex->session_id_len == 0) |
1910 |
|
|
fatal("%s: ssh->kex->session_id_len == 0", __func__); |
1911 |
|
|
/* |
1912 |
|
|
* Expect a signature for each of the ctx->nnew private keys we |
1913 |
|
|
* haven't seen before. They will be in the same order as the |
1914 |
|
|
* ctx->keys where the corresponding ctx->keys_seen[i] == 0. |
1915 |
|
|
*/ |
1916 |
|
|
for (ndone = i = 0; i < ctx->nkeys; i++) { |
1917 |
|
|
if (ctx->keys_seen[i]) |
1918 |
|
|
continue; |
1919 |
|
|
/* Prepare data to be signed: session ID, unique string, key */ |
1920 |
|
|
sshbuf_reset(signdata); |
1921 |
|
|
if ( (r = sshbuf_put_cstring(signdata, |
1922 |
|
|
"hostkeys-prove-00@openssh.com")) != 0 || |
1923 |
|
|
(r = sshbuf_put_string(signdata, ssh->kex->session_id, |
1924 |
|
|
ssh->kex->session_id_len)) != 0 || |
1925 |
|
|
(r = sshkey_puts(ctx->keys[i], signdata)) != 0) |
1926 |
|
|
fatal("%s: failed to prepare signature: %s", |
1927 |
|
|
__func__, ssh_err(r)); |
1928 |
|
|
/* Extract and verify signature */ |
1929 |
|
|
if ((r = sshpkt_get_string_direct(ssh, &sig, &siglen)) != 0) { |
1930 |
|
|
error("%s: couldn't parse message: %s", |
1931 |
|
|
__func__, ssh_err(r)); |
1932 |
|
|
goto out; |
1933 |
|
|
} |
1934 |
|
|
if ((r = sshkey_verify(ctx->keys[i], sig, siglen, |
1935 |
|
|
sshbuf_ptr(signdata), sshbuf_len(signdata), 0)) != 0) { |
1936 |
|
|
error("%s: server gave bad signature for %s key %zu", |
1937 |
|
|
__func__, sshkey_type(ctx->keys[i]), i); |
1938 |
|
|
goto out; |
1939 |
|
|
} |
1940 |
|
|
/* Key is good. Mark it as 'seen' */ |
1941 |
|
|
ctx->keys_seen[i] = 2; |
1942 |
|
|
ndone++; |
1943 |
|
|
} |
1944 |
|
|
if (ndone != ctx->nnew) |
1945 |
|
|
fatal("%s: ndone != ctx->nnew (%zu / %zu)", __func__, |
1946 |
|
|
ndone, ctx->nnew); /* Shouldn't happen */ |
1947 |
|
|
ssh_packet_check_eom(ssh); |
1948 |
|
|
|
1949 |
|
|
/* Make the edits to known_hosts */ |
1950 |
|
|
update_known_hosts(ctx); |
1951 |
|
|
out: |
1952 |
|
|
hostkeys_update_ctx_free(ctx); |
1953 |
|
|
} |
1954 |
|
|
|
1955 |
|
|
/* |
1956 |
|
|
* Returns non-zero if the key is accepted by HostkeyAlgorithms. |
1957 |
|
|
* Made slightly less trivial by the multiple RSA signature algorithm names. |
1958 |
|
|
*/ |
1959 |
|
|
static int |
1960 |
|
|
key_accepted_by_hostkeyalgs(const struct sshkey *key) |
1961 |
|
|
{ |
1962 |
|
|
const char *ktype = sshkey_ssh_name(key); |
1963 |
|
|
const char *hostkeyalgs = options.hostkeyalgorithms != NULL ? |
1964 |
|
|
options.hostkeyalgorithms : KEX_DEFAULT_PK_ALG; |
1965 |
|
|
|
1966 |
|
|
if (key == NULL || key->type == KEY_UNSPEC) |
1967 |
|
|
return 0; |
1968 |
|
|
if (key->type == KEY_RSA && |
1969 |
|
|
(match_pattern_list("rsa-sha2-256", hostkeyalgs, 0) == 1 || |
1970 |
|
|
match_pattern_list("rsa-sha2-512", hostkeyalgs, 0) == 1)) |
1971 |
|
|
return 1; |
1972 |
|
|
return match_pattern_list(ktype, hostkeyalgs, 0) == 1; |
1973 |
|
|
} |
1974 |
|
|
|
1975 |
|
|
/* |
1976 |
|
|
* Handle hostkeys-00@openssh.com global request to inform the client of all |
1977 |
|
|
* the server's hostkeys. The keys are checked against the user's |
1978 |
|
|
* HostkeyAlgorithms preference before they are accepted. |
1979 |
|
|
*/ |
1980 |
|
|
static int |
1981 |
|
|
client_input_hostkeys(void) |
1982 |
|
|
{ |
1983 |
|
|
struct ssh *ssh = active_state; /* XXX */ |
1984 |
|
|
const u_char *blob = NULL; |
1985 |
|
|
size_t i, len = 0; |
1986 |
|
|
struct sshbuf *buf = NULL; |
1987 |
|
|
struct sshkey *key = NULL, **tmp; |
1988 |
|
|
int r; |
1989 |
|
|
char *fp; |
1990 |
|
|
static int hostkeys_seen = 0; /* XXX use struct ssh */ |
1991 |
|
|
extern struct sockaddr_storage hostaddr; /* XXX from ssh.c */ |
1992 |
|
|
struct hostkeys_update_ctx *ctx = NULL; |
1993 |
|
|
|
1994 |
|
|
if (hostkeys_seen) |
1995 |
|
|
fatal("%s: server already sent hostkeys", __func__); |
1996 |
|
|
if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK && |
1997 |
|
|
options.batch_mode) |
1998 |
|
|
return 1; /* won't ask in batchmode, so don't even try */ |
1999 |
|
|
if (!options.update_hostkeys || options.num_user_hostfiles <= 0) |
2000 |
|
|
return 1; |
2001 |
|
|
|
2002 |
|
|
ctx = xcalloc(1, sizeof(*ctx)); |
2003 |
|
|
while (ssh_packet_remaining(ssh) > 0) { |
2004 |
|
|
sshkey_free(key); |
2005 |
|
|
key = NULL; |
2006 |
|
|
if ((r = sshpkt_get_string_direct(ssh, &blob, &len)) != 0) { |
2007 |
|
|
error("%s: couldn't parse message: %s", |
2008 |
|
|
__func__, ssh_err(r)); |
2009 |
|
|
goto out; |
2010 |
|
|
} |
2011 |
|
|
if ((r = sshkey_from_blob(blob, len, &key)) != 0) { |
2012 |
|
|
error("%s: parse key: %s", __func__, ssh_err(r)); |
2013 |
|
|
goto out; |
2014 |
|
|
} |
2015 |
|
|
fp = sshkey_fingerprint(key, options.fingerprint_hash, |
2016 |
|
|
SSH_FP_DEFAULT); |
2017 |
|
|
debug3("%s: received %s key %s", __func__, |
2018 |
|
|
sshkey_type(key), fp); |
2019 |
|
|
free(fp); |
2020 |
|
|
|
2021 |
|
|
if (!key_accepted_by_hostkeyalgs(key)) { |
2022 |
|
|
debug3("%s: %s key not permitted by HostkeyAlgorithms", |
2023 |
|
|
__func__, sshkey_ssh_name(key)); |
2024 |
|
|
continue; |
2025 |
|
|
} |
2026 |
|
|
/* Skip certs */ |
2027 |
|
|
if (sshkey_is_cert(key)) { |
2028 |
|
|
debug3("%s: %s key is a certificate; skipping", |
2029 |
|
|
__func__, sshkey_ssh_name(key)); |
2030 |
|
|
continue; |
2031 |
|
|
} |
2032 |
|
|
/* Ensure keys are unique */ |
2033 |
|
|
for (i = 0; i < ctx->nkeys; i++) { |
2034 |
|
|
if (sshkey_equal(key, ctx->keys[i])) { |
2035 |
|
|
error("%s: received duplicated %s host key", |
2036 |
|
|
__func__, sshkey_ssh_name(key)); |
2037 |
|
|
goto out; |
2038 |
|
|
} |
2039 |
|
|
} |
2040 |
|
|
/* Key is good, record it */ |
2041 |
|
|
if ((tmp = recallocarray(ctx->keys, ctx->nkeys, ctx->nkeys + 1, |
2042 |
|
|
sizeof(*ctx->keys))) == NULL) |
2043 |
|
|
fatal("%s: recallocarray failed nkeys = %zu", |
2044 |
|
|
__func__, ctx->nkeys); |
2045 |
|
|
ctx->keys = tmp; |
2046 |
|
|
ctx->keys[ctx->nkeys++] = key; |
2047 |
|
|
key = NULL; |
2048 |
|
|
} |
2049 |
|
|
|
2050 |
|
|
if (ctx->nkeys == 0) { |
2051 |
|
|
debug("%s: server sent no hostkeys", __func__); |
2052 |
|
|
goto out; |
2053 |
|
|
} |
2054 |
|
|
|
2055 |
|
|
if ((ctx->keys_seen = calloc(ctx->nkeys, |
2056 |
|
|
sizeof(*ctx->keys_seen))) == NULL) |
2057 |
|
|
fatal("%s: calloc failed", __func__); |
2058 |
|
|
|
2059 |
|
|
get_hostfile_hostname_ipaddr(host, |
2060 |
|
|
options.check_host_ip ? (struct sockaddr *)&hostaddr : NULL, |
2061 |
|
|
options.port, &ctx->host_str, |
2062 |
|
|
options.check_host_ip ? &ctx->ip_str : NULL); |
2063 |
|
|
|
2064 |
|
|
/* Find which keys we already know about. */ |
2065 |
|
|
if ((r = hostkeys_foreach(options.user_hostfiles[0], hostkeys_find, |
2066 |
|
|
ctx, ctx->host_str, ctx->ip_str, |
2067 |
|
|
HKF_WANT_PARSE_KEY|HKF_WANT_MATCH)) != 0) { |
2068 |
|
|
error("%s: hostkeys_foreach failed: %s", __func__, ssh_err(r)); |
2069 |
|
|
goto out; |
2070 |
|
|
} |
2071 |
|
|
|
2072 |
|
|
/* Figure out if we have any new keys to add */ |
2073 |
|
|
ctx->nnew = 0; |
2074 |
|
|
for (i = 0; i < ctx->nkeys; i++) { |
2075 |
|
|
if (!ctx->keys_seen[i]) |
2076 |
|
|
ctx->nnew++; |
2077 |
|
|
} |
2078 |
|
|
|
2079 |
|
|
debug3("%s: %zu keys from server: %zu new, %zu retained. %zu to remove", |
2080 |
|
|
__func__, ctx->nkeys, ctx->nnew, ctx->nkeys - ctx->nnew, ctx->nold); |
2081 |
|
|
|
2082 |
|
|
if (ctx->nnew == 0 && ctx->nold != 0) { |
2083 |
|
|
/* We have some keys to remove. Just do it. */ |
2084 |
|
|
update_known_hosts(ctx); |
2085 |
|
|
} else if (ctx->nnew != 0) { |
2086 |
|
|
/* |
2087 |
|
|
* We have received hitherto-unseen keys from the server. |
2088 |
|
|
* Ask the server to confirm ownership of the private halves. |
2089 |
|
|
*/ |
2090 |
|
|
debug3("%s: asking server to prove ownership for %zu keys", |
2091 |
|
|
__func__, ctx->nnew); |
2092 |
|
|
if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || |
2093 |
|
|
(r = sshpkt_put_cstring(ssh, |
2094 |
|
|
"hostkeys-prove-00@openssh.com")) != 0 || |
2095 |
|
|
(r = sshpkt_put_u8(ssh, 1)) != 0) /* bool: want reply */ |
2096 |
|
|
fatal("%s: cannot prepare packet: %s", |
2097 |
|
|
__func__, ssh_err(r)); |
2098 |
|
|
if ((buf = sshbuf_new()) == NULL) |
2099 |
|
|
fatal("%s: sshbuf_new", __func__); |
2100 |
|
|
for (i = 0; i < ctx->nkeys; i++) { |
2101 |
|
|
if (ctx->keys_seen[i]) |
2102 |
|
|
continue; |
2103 |
|
|
sshbuf_reset(buf); |
2104 |
|
|
if ((r = sshkey_putb(ctx->keys[i], buf)) != 0) |
2105 |
|
|
fatal("%s: sshkey_putb: %s", |
2106 |
|
|
__func__, ssh_err(r)); |
2107 |
|
|
if ((r = sshpkt_put_stringb(ssh, buf)) != 0) |
2108 |
|
|
fatal("%s: sshpkt_put_string: %s", |
2109 |
|
|
__func__, ssh_err(r)); |
2110 |
|
|
} |
2111 |
|
|
if ((r = sshpkt_send(ssh)) != 0) |
2112 |
|
|
fatal("%s: sshpkt_send: %s", __func__, ssh_err(r)); |
2113 |
|
|
client_register_global_confirm( |
2114 |
|
|
client_global_hostkeys_private_confirm, ctx); |
2115 |
|
|
ctx = NULL; /* will be freed in callback */ |
2116 |
|
|
} |
2117 |
|
|
|
2118 |
|
|
/* Success */ |
2119 |
|
|
out: |
2120 |
|
|
hostkeys_update_ctx_free(ctx); |
2121 |
|
|
sshkey_free(key); |
2122 |
|
|
sshbuf_free(buf); |
2123 |
|
|
/* |
2124 |
|
|
* NB. Return success for all cases. The server doesn't need to know |
2125 |
|
|
* what the client does with its hosts file. |
2126 |
|
|
*/ |
2127 |
|
|
return 1; |
2128 |
|
|
} |
2129 |
|
|
|
2130 |
|
|
static int |
2131 |
|
|
client_input_global_request(int type, u_int32_t seq, struct ssh *ssh) |
2132 |
|
|
{ |
2133 |
|
|
char *rtype; |
2134 |
|
|
int want_reply; |
2135 |
|
|
int success = 0; |
2136 |
|
|
|
2137 |
|
|
rtype = packet_get_cstring(NULL); |
2138 |
|
|
want_reply = packet_get_char(); |
2139 |
|
|
debug("client_input_global_request: rtype %s want_reply %d", |
2140 |
|
|
rtype, want_reply); |
2141 |
|
|
if (strcmp(rtype, "hostkeys-00@openssh.com") == 0) |
2142 |
|
|
success = client_input_hostkeys(); |
2143 |
|
|
if (want_reply) { |
2144 |
|
|
packet_start(success ? |
2145 |
|
|
SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE); |
2146 |
|
|
packet_send(); |
2147 |
|
|
packet_write_wait(); |
2148 |
|
|
} |
2149 |
|
|
free(rtype); |
2150 |
|
|
return 0; |
2151 |
|
|
} |
2152 |
|
|
|
2153 |
|
|
void |
2154 |
|
|
client_session2_setup(struct ssh *ssh, int id, int want_tty, int want_subsystem, |
2155 |
|
|
const char *term, struct termios *tiop, int in_fd, Buffer *cmd, char **env) |
2156 |
|
|
{ |
2157 |
|
|
int len; |
2158 |
|
|
Channel *c = NULL; |
2159 |
|
|
|
2160 |
|
|
debug2("%s: id %d", __func__, id); |
2161 |
|
|
|
2162 |
|
|
if ((c = channel_lookup(ssh, id)) == NULL) |
2163 |
|
|
fatal("%s: channel %d: unknown channel", __func__, id); |
2164 |
|
|
|
2165 |
|
|
packet_set_interactive(want_tty, |
2166 |
|
|
options.ip_qos_interactive, options.ip_qos_bulk); |
2167 |
|
|
|
2168 |
|
|
if (want_tty) { |
2169 |
|
|
struct winsize ws; |
2170 |
|
|
|
2171 |
|
|
/* Store window size in the packet. */ |
2172 |
|
|
if (ioctl(in_fd, TIOCGWINSZ, &ws) < 0) |
2173 |
|
|
memset(&ws, 0, sizeof(ws)); |
2174 |
|
|
|
2175 |
|
|
channel_request_start(ssh, id, "pty-req", 1); |
2176 |
|
|
client_expect_confirm(ssh, id, "PTY allocation", CONFIRM_TTY); |
2177 |
|
|
packet_put_cstring(term != NULL ? term : ""); |
2178 |
|
|
packet_put_int((u_int)ws.ws_col); |
2179 |
|
|
packet_put_int((u_int)ws.ws_row); |
2180 |
|
|
packet_put_int((u_int)ws.ws_xpixel); |
2181 |
|
|
packet_put_int((u_int)ws.ws_ypixel); |
2182 |
|
|
if (tiop == NULL) |
2183 |
|
|
tiop = get_saved_tio(); |
2184 |
|
|
tty_make_modes(-1, tiop); |
2185 |
|
|
packet_send(); |
2186 |
|
|
/* XXX wait for reply */ |
2187 |
|
|
c->client_tty = 1; |
2188 |
|
|
} |
2189 |
|
|
|
2190 |
|
|
/* Transfer any environment variables from client to server */ |
2191 |
|
|
if (options.num_send_env != 0 && env != NULL) { |
2192 |
|
|
int i, j, matched; |
2193 |
|
|
char *name, *val; |
2194 |
|
|
|
2195 |
|
|
debug("Sending environment."); |
2196 |
|
|
for (i = 0; env[i] != NULL; i++) { |
2197 |
|
|
/* Split */ |
2198 |
|
|
name = xstrdup(env[i]); |
2199 |
|
|
if ((val = strchr(name, '=')) == NULL) { |
2200 |
|
|
free(name); |
2201 |
|
|
continue; |
2202 |
|
|
} |
2203 |
|
|
*val++ = '\0'; |
2204 |
|
|
|
2205 |
|
|
matched = 0; |
2206 |
|
|
for (j = 0; j < options.num_send_env; j++) { |
2207 |
|
|
if (match_pattern(name, options.send_env[j])) { |
2208 |
|
|
matched = 1; |
2209 |
|
|
break; |
2210 |
|
|
} |
2211 |
|
|
} |
2212 |
|
|
if (!matched) { |
2213 |
|
|
debug3("Ignored env %s", name); |
2214 |
|
|
free(name); |
2215 |
|
|
continue; |
2216 |
|
|
} |
2217 |
|
|
|
2218 |
|
|
debug("Sending env %s = %s", name, val); |
2219 |
|
|
channel_request_start(ssh, id, "env", 0); |
2220 |
|
|
packet_put_cstring(name); |
2221 |
|
|
packet_put_cstring(val); |
2222 |
|
|
packet_send(); |
2223 |
|
|
free(name); |
2224 |
|
|
} |
2225 |
|
|
} |
2226 |
|
|
|
2227 |
|
|
len = buffer_len(cmd); |
2228 |
|
|
if (len > 0) { |
2229 |
|
|
if (len > 900) |
2230 |
|
|
len = 900; |
2231 |
|
|
if (want_subsystem) { |
2232 |
|
|
debug("Sending subsystem: %.*s", |
2233 |
|
|
len, (u_char*)buffer_ptr(cmd)); |
2234 |
|
|
channel_request_start(ssh, id, "subsystem", 1); |
2235 |
|
|
client_expect_confirm(ssh, id, "subsystem", |
2236 |
|
|
CONFIRM_CLOSE); |
2237 |
|
|
} else { |
2238 |
|
|
debug("Sending command: %.*s", |
2239 |
|
|
len, (u_char*)buffer_ptr(cmd)); |
2240 |
|
|
channel_request_start(ssh, id, "exec", 1); |
2241 |
|
|
client_expect_confirm(ssh, id, "exec", CONFIRM_CLOSE); |
2242 |
|
|
} |
2243 |
|
|
packet_put_string(buffer_ptr(cmd), buffer_len(cmd)); |
2244 |
|
|
packet_send(); |
2245 |
|
|
} else { |
2246 |
|
|
channel_request_start(ssh, id, "shell", 1); |
2247 |
|
|
client_expect_confirm(ssh, id, "shell", CONFIRM_CLOSE); |
2248 |
|
|
packet_send(); |
2249 |
|
|
} |
2250 |
|
|
} |
2251 |
|
|
|
2252 |
|
|
static void |
2253 |
|
|
client_init_dispatch(void) |
2254 |
|
|
{ |
2255 |
|
|
dispatch_init(&dispatch_protocol_error); |
2256 |
|
|
|
2257 |
|
|
dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); |
2258 |
|
|
dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data); |
2259 |
|
|
dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); |
2260 |
|
|
dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); |
2261 |
|
|
dispatch_set(SSH2_MSG_CHANNEL_OPEN, &client_input_channel_open); |
2262 |
|
|
dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); |
2263 |
|
|
dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); |
2264 |
|
|
dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &client_input_channel_req); |
2265 |
|
|
dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); |
2266 |
|
|
dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, &channel_input_status_confirm); |
2267 |
|
|
dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &channel_input_status_confirm); |
2268 |
|
|
dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &client_input_global_request); |
2269 |
|
|
|
2270 |
|
|
/* rekeying */ |
2271 |
|
|
dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit); |
2272 |
|
|
|
2273 |
|
|
/* global request reply messages */ |
2274 |
|
|
dispatch_set(SSH2_MSG_REQUEST_FAILURE, &client_global_request_reply); |
2275 |
|
|
dispatch_set(SSH2_MSG_REQUEST_SUCCESS, &client_global_request_reply); |
2276 |
|
|
} |
2277 |
|
|
|
2278 |
|
|
void |
2279 |
|
|
client_stop_mux(void) |
2280 |
|
|
{ |
2281 |
|
|
if (options.control_path != NULL && muxserver_sock != -1) |
2282 |
|
|
unlink(options.control_path); |
2283 |
|
|
/* |
2284 |
|
|
* If we are in persist mode, or don't have a shell, signal that we |
2285 |
|
|
* should close when all active channels are closed. |
2286 |
|
|
*/ |
2287 |
|
|
if (options.control_persist || no_shell_flag) { |
2288 |
|
|
session_closed = 1; |
2289 |
|
|
setproctitle("[stopped mux]"); |
2290 |
|
|
} |
2291 |
|
|
} |
2292 |
|
|
|
2293 |
|
|
/* client specific fatal cleanup */ |
2294 |
|
|
void |
2295 |
|
|
cleanup_exit(int i) |
2296 |
|
|
{ |
2297 |
|
|
leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); |
2298 |
|
|
leave_non_blocking(); |
2299 |
|
|
if (options.control_path != NULL && muxserver_sock != -1) |
2300 |
|
|
unlink(options.control_path); |
2301 |
|
|
ssh_kill_proxy_command(); |
2302 |
|
|
_exit(i); |
2303 |
|
|
} |