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