1 /*
2 * SSH main session channel handling.
3 */
4
5 #include <assert.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8
9 #include "putty.h"
10 #include "ssh.h"
11 #include "sshppl.h"
12 #include "sshchan.h"
13
14 static void mainchan_free(Channel *chan);
15 static void mainchan_open_confirmation(Channel *chan);
16 static void mainchan_open_failure(Channel *chan, const char *errtext);
17 static size_t mainchan_send(
18 Channel *chan, bool is_stderr, const void *, size_t);
19 static void mainchan_send_eof(Channel *chan);
20 static void mainchan_set_input_wanted(Channel *chan, bool wanted);
21 static char *mainchan_log_close_msg(Channel *chan);
22 static bool mainchan_rcvd_exit_status(Channel *chan, int status);
23 static bool mainchan_rcvd_exit_signal(
24 Channel *chan, ptrlen signame, bool core_dumped, ptrlen msg);
25 static bool mainchan_rcvd_exit_signal_numeric(
26 Channel *chan, int signum, bool core_dumped, ptrlen msg);
27 static void mainchan_request_response(Channel *chan, bool success);
28
29 static const ChannelVtable mainchan_channelvt = {
30 .free = mainchan_free,
31 .open_confirmation = mainchan_open_confirmation,
32 .open_failed = mainchan_open_failure,
33 .send = mainchan_send,
34 .send_eof = mainchan_send_eof,
35 .set_input_wanted = mainchan_set_input_wanted,
36 .log_close_msg = mainchan_log_close_msg,
37 .want_close = chan_default_want_close,
38 .rcvd_exit_status = mainchan_rcvd_exit_status,
39 .rcvd_exit_signal = mainchan_rcvd_exit_signal,
40 .rcvd_exit_signal_numeric = mainchan_rcvd_exit_signal_numeric,
41 .run_shell = chan_no_run_shell,
42 .run_command = chan_no_run_command,
43 .run_subsystem = chan_no_run_subsystem,
44 .enable_x11_forwarding = chan_no_enable_x11_forwarding,
45 .enable_agent_forwarding = chan_no_enable_agent_forwarding,
46 .allocate_pty = chan_no_allocate_pty,
47 .set_env = chan_no_set_env,
48 .send_break = chan_no_send_break,
49 .send_signal = chan_no_send_signal,
50 .change_window_size = chan_no_change_window_size,
51 .request_response = mainchan_request_response,
52 };
53
54 typedef enum MainChanType {
55 MAINCHAN_SESSION, MAINCHAN_DIRECT_TCPIP
56 } MainChanType;
57
58 struct mainchan {
59 SshChannel *sc;
60 Conf *conf;
61 PacketProtocolLayer *ppl;
62 ConnectionLayer *cl;
63
64 MainChanType type;
65 bool is_simple;
66
67 bool req_x11, req_agent, req_pty, req_cmd_primary, req_cmd_fallback;
68 int n_req_env, n_env_replies, n_env_fails;
69 bool eof_pending, eof_sent, got_pty, ready;
70
71 int term_width, term_height;
72
73 Channel chan;
74 };
75
mainchan_new(PacketProtocolLayer * ppl,ConnectionLayer * cl,Conf * conf,int term_width,int term_height,bool is_simple,SshChannel ** sc_out)76 mainchan *mainchan_new(
77 PacketProtocolLayer *ppl, ConnectionLayer *cl, Conf *conf,
78 int term_width, int term_height, bool is_simple, SshChannel **sc_out)
79 {
80 mainchan *mc;
81
82 if (conf_get_bool(conf, CONF_ssh_no_shell))
83 return NULL; /* no main channel at all */
84
85 mc = snew(mainchan);
86 memset(mc, 0, sizeof(mainchan));
87 mc->ppl = ppl;
88 mc->cl = cl;
89 mc->conf = conf_copy(conf);
90 mc->term_width = term_width;
91 mc->term_height = term_height;
92 mc->is_simple = is_simple;
93
94 mc->sc = NULL;
95 mc->chan.vt = &mainchan_channelvt;
96 mc->chan.initial_fixed_window_size = 0;
97
98 if (*conf_get_str(mc->conf, CONF_ssh_nc_host)) {
99 const char *host = conf_get_str(mc->conf, CONF_ssh_nc_host);
100 int port = conf_get_int(mc->conf, CONF_ssh_nc_port);
101
102 mc->sc = ssh_lportfwd_open(cl, host, port, "main channel",
103 NULL, &mc->chan);
104 mc->type = MAINCHAN_DIRECT_TCPIP;
105 } else {
106 mc->sc = ssh_session_open(cl, &mc->chan);
107 mc->type = MAINCHAN_SESSION;
108 }
109
110 if (sc_out) *sc_out = mc->sc;
111 return mc;
112 }
113
mainchan_free(Channel * chan)114 static void mainchan_free(Channel *chan)
115 {
116 assert(chan->vt == &mainchan_channelvt);
117 mainchan *mc = container_of(chan, mainchan, chan);
118 conf_free(mc->conf);
119 sfree(mc);
120 }
121
122 static void mainchan_try_fallback_command(mainchan *mc);
123 static void mainchan_ready(mainchan *mc);
124
mainchan_open_confirmation(Channel * chan)125 static void mainchan_open_confirmation(Channel *chan)
126 {
127 mainchan *mc = container_of(chan, mainchan, chan);
128 PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */
129
130 seat_update_specials_menu(mc->ppl->seat);
131 ppl_logevent("Opened main channel");
132
133 if (mc->is_simple)
134 sshfwd_hint_channel_is_simple(mc->sc);
135
136 if (mc->type == MAINCHAN_SESSION) {
137 /*
138 * Send the CHANNEL_REQUESTS for the main session channel.
139 */
140 char *key, *val, *cmd;
141 struct X11Display *x11disp;
142 struct X11FakeAuth *x11auth;
143 bool retry_cmd_now = false;
144
145 if (conf_get_bool(mc->conf, CONF_x11_forward)) {
146 char *x11_setup_err;
147 if ((x11disp = x11_setup_display(
148 conf_get_str(mc->conf, CONF_x11_display),
149 mc->conf, &x11_setup_err)) == NULL) {
150 ppl_logevent("X11 forwarding not enabled: unable to"
151 " initialise X display: %s", x11_setup_err);
152 sfree(x11_setup_err);
153 } else {
154 x11auth = ssh_add_x11_display(
155 mc->cl, conf_get_int(mc->conf, CONF_x11_auth), x11disp);
156
157 sshfwd_request_x11_forwarding(
158 mc->sc, true, x11auth->protoname, x11auth->datastring,
159 x11disp->screennum, false);
160 mc->req_x11 = true;
161 }
162 }
163
164 if (ssh_agent_forwarding_permitted(mc->cl)) {
165 sshfwd_request_agent_forwarding(mc->sc, true);
166 mc->req_agent = true;
167 }
168
169 if (!conf_get_bool(mc->conf, CONF_nopty)) {
170 sshfwd_request_pty(
171 mc->sc, true, mc->conf, mc->term_width, mc->term_height);
172 mc->req_pty = true;
173 }
174
175 for (val = conf_get_str_strs(mc->conf, CONF_environmt, NULL, &key);
176 val != NULL;
177 val = conf_get_str_strs(mc->conf, CONF_environmt, key, &key)) {
178 sshfwd_send_env_var(mc->sc, true, key, val);
179 mc->n_req_env++;
180 }
181 if (mc->n_req_env)
182 ppl_logevent("Sent %d environment variables", mc->n_req_env);
183
184 cmd = conf_get_str(mc->conf, CONF_remote_cmd);
185 if (conf_get_bool(mc->conf, CONF_ssh_subsys)) {
186 retry_cmd_now = !sshfwd_start_subsystem(mc->sc, true, cmd);
187 } else if (*cmd) {
188 sshfwd_start_command(mc->sc, true, cmd);
189 } else {
190 sshfwd_start_shell(mc->sc, true);
191 }
192
193 if (retry_cmd_now)
194 mainchan_try_fallback_command(mc);
195 else
196 mc->req_cmd_primary = true;
197
198 } else {
199 ssh_set_ldisc_option(mc->cl, LD_ECHO, true);
200 ssh_set_ldisc_option(mc->cl, LD_EDIT, true);
201 mainchan_ready(mc);
202 }
203 }
204
mainchan_try_fallback_command(mainchan * mc)205 static void mainchan_try_fallback_command(mainchan *mc)
206 {
207 const char *cmd = conf_get_str(mc->conf, CONF_remote_cmd2);
208 if (conf_get_bool(mc->conf, CONF_ssh_subsys2)) {
209 sshfwd_start_subsystem(mc->sc, true, cmd);
210 } else {
211 sshfwd_start_command(mc->sc, true, cmd);
212 }
213 mc->req_cmd_fallback = true;
214 }
215
mainchan_request_response(Channel * chan,bool success)216 static void mainchan_request_response(Channel *chan, bool success)
217 {
218 assert(chan->vt == &mainchan_channelvt);
219 mainchan *mc = container_of(chan, mainchan, chan);
220 PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */
221
222 if (mc->req_x11) {
223 mc->req_x11 = false;
224
225 if (success) {
226 ppl_logevent("X11 forwarding enabled");
227 ssh_enable_x_fwd(mc->cl);
228 } else {
229 ppl_logevent("X11 forwarding refused");
230 }
231 return;
232 }
233
234 if (mc->req_agent) {
235 mc->req_agent = false;
236
237 if (success) {
238 ppl_logevent("Agent forwarding enabled");
239 } else {
240 ppl_logevent("Agent forwarding refused");
241 }
242 return;
243 }
244
245 if (mc->req_pty) {
246 mc->req_pty = false;
247
248 if (success) {
249 ppl_logevent("Allocated pty");
250 mc->got_pty = true;
251 } else {
252 ppl_logevent("Server refused to allocate pty");
253 ppl_printf("Server refused to allocate pty\r\n");
254 ssh_set_ldisc_option(mc->cl, LD_ECHO, true);
255 ssh_set_ldisc_option(mc->cl, LD_EDIT, true);
256 }
257 return;
258 }
259
260 if (mc->n_env_replies < mc->n_req_env) {
261 int j = mc->n_env_replies++;
262 if (!success) {
263 ppl_logevent("Server refused to set environment variable %s",
264 conf_get_str_nthstrkey(mc->conf,
265 CONF_environmt, j));
266 mc->n_env_fails++;
267 }
268
269 if (mc->n_env_replies == mc->n_req_env) {
270 if (mc->n_env_fails == 0) {
271 ppl_logevent("All environment variables successfully set");
272 } else if (mc->n_env_fails == mc->n_req_env) {
273 ppl_logevent("All environment variables refused");
274 ppl_printf("Server refused to set environment "
275 "variables\r\n");
276 } else {
277 ppl_printf("Server refused to set all environment "
278 "variables\r\n");
279 }
280 }
281 return;
282 }
283
284 if (mc->req_cmd_primary) {
285 mc->req_cmd_primary = false;
286
287 if (success) {
288 ppl_logevent("Started a shell/command");
289 mainchan_ready(mc);
290 } else if (*conf_get_str(mc->conf, CONF_remote_cmd2)) {
291 ppl_logevent("Primary command failed; attempting fallback");
292 mainchan_try_fallback_command(mc);
293 } else {
294 /*
295 * If there's no remote_cmd2 configured, then we have no
296 * fallback command, so we've run out of options.
297 */
298 ssh_sw_abort(mc->ppl->ssh,
299 "Server refused to start a shell/command");
300 }
301 return;
302 }
303
304 if (mc->req_cmd_fallback) {
305 mc->req_cmd_fallback = false;
306
307 if (success) {
308 ppl_logevent("Started a shell/command");
309 ssh_got_fallback_cmd(mc->ppl->ssh);
310 mainchan_ready(mc);
311 } else {
312 ssh_sw_abort(mc->ppl->ssh,
313 "Server refused to start a shell/command");
314 }
315 return;
316 }
317 }
318
mainchan_ready(mainchan * mc)319 static void mainchan_ready(mainchan *mc)
320 {
321 mc->ready = true;
322
323 ssh_set_wants_user_input(mc->cl, true);
324 ssh_ppl_got_user_input(mc->ppl); /* in case any is already queued */
325
326 /* If an EOF arrived before we were ready, handle it now. */
327 if (mc->eof_pending) {
328 mc->eof_pending = false;
329 mainchan_special_cmd(mc, SS_EOF, 0);
330 }
331
332 ssh_ldisc_update(mc->ppl->ssh);
333 queue_idempotent_callback(&mc->ppl->ic_process_queue);
334 }
335
mainchan_open_failure(Channel * chan,const char * errtext)336 static void mainchan_open_failure(Channel *chan, const char *errtext)
337 {
338 assert(chan->vt == &mainchan_channelvt);
339 mainchan *mc = container_of(chan, mainchan, chan);
340
341 ssh_sw_abort_deferred(mc->ppl->ssh,
342 "Server refused to open main channel: %s", errtext);
343 }
344
mainchan_send(Channel * chan,bool is_stderr,const void * data,size_t length)345 static size_t mainchan_send(Channel *chan, bool is_stderr,
346 const void *data, size_t length)
347 {
348 assert(chan->vt == &mainchan_channelvt);
349 mainchan *mc = container_of(chan, mainchan, chan);
350 return seat_output(mc->ppl->seat, is_stderr, data, length);
351 }
352
mainchan_send_eof(Channel * chan)353 static void mainchan_send_eof(Channel *chan)
354 {
355 assert(chan->vt == &mainchan_channelvt);
356 mainchan *mc = container_of(chan, mainchan, chan);
357 PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */
358
359 if (!mc->eof_sent && (seat_eof(mc->ppl->seat) || mc->got_pty)) {
360 /*
361 * Either seat_eof told us that the front end wants us to
362 * close the outgoing side of the connection as soon as we see
363 * EOF from the far end, or else we've unilaterally decided to
364 * do that because we've allocated a remote pty and hence EOF
365 * isn't a particularly meaningful concept.
366 */
367 sshfwd_write_eof(mc->sc);
368 ppl_logevent("Sent EOF message");
369 mc->eof_sent = true;
370 ssh_set_wants_user_input(mc->cl, false); /* stop reading from stdin */
371 }
372 }
373
mainchan_set_input_wanted(Channel * chan,bool wanted)374 static void mainchan_set_input_wanted(Channel *chan, bool wanted)
375 {
376 assert(chan->vt == &mainchan_channelvt);
377 mainchan *mc = container_of(chan, mainchan, chan);
378
379 /*
380 * This is the main channel of the SSH session, i.e. the one tied
381 * to the standard input (or GUI) of the primary SSH client user
382 * interface. So ssh->send_ok is how we control whether we're
383 * reading from that input.
384 */
385 ssh_set_wants_user_input(mc->cl, wanted);
386 }
387
mainchan_log_close_msg(Channel * chan)388 static char *mainchan_log_close_msg(Channel *chan)
389 {
390 return dupstr("Main session channel closed");
391 }
392
mainchan_rcvd_exit_status(Channel * chan,int status)393 static bool mainchan_rcvd_exit_status(Channel *chan, int status)
394 {
395 assert(chan->vt == &mainchan_channelvt);
396 mainchan *mc = container_of(chan, mainchan, chan);
397 PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */
398
399 ssh_got_exitcode(mc->ppl->ssh, status);
400 ppl_logevent("Session sent command exit status %d", status);
401 return true;
402 }
403
mainchan_log_exit_signal_common(mainchan * mc,const char * sigdesc,bool core_dumped,ptrlen msg)404 static void mainchan_log_exit_signal_common(
405 mainchan *mc, const char *sigdesc,
406 bool core_dumped, ptrlen msg)
407 {
408 PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */
409
410 const char *core_msg = core_dumped ? " (core dumped)" : "";
411 const char *msg_pre = (msg.len ? " (" : "");
412 const char *msg_post = (msg.len ? ")" : "");
413 ppl_logevent("Session exited on %s%s%s%.*s%s",
414 sigdesc, core_msg, msg_pre, PTRLEN_PRINTF(msg), msg_post);
415 }
416
mainchan_rcvd_exit_signal(Channel * chan,ptrlen signame,bool core_dumped,ptrlen msg)417 static bool mainchan_rcvd_exit_signal(
418 Channel *chan, ptrlen signame, bool core_dumped, ptrlen msg)
419 {
420 assert(chan->vt == &mainchan_channelvt);
421 mainchan *mc = container_of(chan, mainchan, chan);
422 int exitcode;
423 char *signame_str;
424
425 /*
426 * Translate the signal description back into a locally meaningful
427 * number, or 128 if the string didn't match any we recognise.
428 */
429 exitcode = 128;
430
431 #define SIGNAL_SUB(s) \
432 if (ptrlen_eq_string(signame, #s)) \
433 exitcode = 128 + SIG ## s;
434 #define SIGNAL_MAIN(s, text) SIGNAL_SUB(s)
435 #define SIGNALS_LOCAL_ONLY
436 #include "sshsignals.h"
437 #undef SIGNAL_SUB
438 #undef SIGNAL_MAIN
439 #undef SIGNALS_LOCAL_ONLY
440
441 ssh_got_exitcode(mc->ppl->ssh, exitcode);
442 if (exitcode == 128)
443 signame_str = dupprintf("unrecognised signal \"%.*s\"",
444 PTRLEN_PRINTF(signame));
445 else
446 signame_str = dupprintf("signal SIG%.*s", PTRLEN_PRINTF(signame));
447 mainchan_log_exit_signal_common(mc, signame_str, core_dumped, msg);
448 sfree(signame_str);
449 return true;
450 }
451
mainchan_rcvd_exit_signal_numeric(Channel * chan,int signum,bool core_dumped,ptrlen msg)452 static bool mainchan_rcvd_exit_signal_numeric(
453 Channel *chan, int signum, bool core_dumped, ptrlen msg)
454 {
455 assert(chan->vt == &mainchan_channelvt);
456 mainchan *mc = container_of(chan, mainchan, chan);
457 char *signum_str;
458
459 ssh_got_exitcode(mc->ppl->ssh, 128 + signum);
460 signum_str = dupprintf("signal %d", signum);
461 mainchan_log_exit_signal_common(mc, signum_str, core_dumped, msg);
462 sfree(signum_str);
463 return true;
464 }
465
mainchan_get_specials(mainchan * mc,add_special_fn_t add_special,void * ctx)466 void mainchan_get_specials(
467 mainchan *mc, add_special_fn_t add_special, void *ctx)
468 {
469 /* FIXME: this _does_ depend on whether these services are supported */
470
471 add_special(ctx, "Break", SS_BRK, 0);
472
473 #define SIGNAL_MAIN(name, desc) \
474 add_special(ctx, "SIG" #name " (" desc ")", SS_SIG ## name, 0);
475 #define SIGNAL_SUB(name)
476 #include "sshsignals.h"
477 #undef SIGNAL_MAIN
478 #undef SIGNAL_SUB
479
480 add_special(ctx, "More signals", SS_SUBMENU, 0);
481
482 #define SIGNAL_MAIN(name, desc)
483 #define SIGNAL_SUB(name) \
484 add_special(ctx, "SIG" #name, SS_SIG ## name, 0);
485 #include "sshsignals.h"
486 #undef SIGNAL_MAIN
487 #undef SIGNAL_SUB
488
489 add_special(ctx, NULL, SS_EXITMENU, 0);
490 }
491
ssh_signal_lookup(SessionSpecialCode code)492 static const char *ssh_signal_lookup(SessionSpecialCode code)
493 {
494 #define SIGNAL_SUB(name) \
495 if (code == SS_SIG ## name) return #name;
496 #define SIGNAL_MAIN(name, desc) SIGNAL_SUB(name)
497 #include "sshsignals.h"
498 #undef SIGNAL_MAIN
499 #undef SIGNAL_SUB
500
501 /* If none of those clauses matched, fail lookup. */
502 return NULL;
503 }
504
mainchan_special_cmd(mainchan * mc,SessionSpecialCode code,int arg)505 void mainchan_special_cmd(mainchan *mc, SessionSpecialCode code, int arg)
506 {
507 PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */
508 const char *signame;
509
510 if (code == SS_EOF) {
511 if (!mc->ready) {
512 /*
513 * Buffer the EOF to send as soon as the main channel is
514 * fully set up.
515 */
516 mc->eof_pending = true;
517 } else if (!mc->eof_sent) {
518 sshfwd_write_eof(mc->sc);
519 mc->eof_sent = true;
520 }
521 } else if (code == SS_BRK) {
522 sshfwd_send_serial_break(
523 mc->sc, false, 0 /* default break length */);
524 } else if ((signame = ssh_signal_lookup(code)) != NULL) {
525 /* It's a signal. */
526 sshfwd_send_signal(mc->sc, false, signame);
527 ppl_logevent("Sent signal SIG%s", signame);
528 }
529 }
530
mainchan_terminal_size(mainchan * mc,int width,int height)531 void mainchan_terminal_size(mainchan *mc, int width, int height)
532 {
533 mc->term_width = width;
534 mc->term_height = height;
535
536 if (mc->req_pty || mc->got_pty)
537 sshfwd_send_terminal_size_change(mc->sc, width, height);
538 }
539