1 /*
2  * Implement the "session" channel type for the SSH server.
3  */
4 
5 #include <assert.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <ctype.h>
9 
10 #include "putty.h"
11 #include "ssh.h"
12 #include "sshchan.h"
13 #include "sshserver.h"
14 #include "sftp.h"
15 
16 struct agentfwd {
17     ConnectionLayer *cl;
18     Socket *socket;
19     Plug plug;
20 };
21 
22 typedef struct sesschan {
23     SshChannel *c;
24 
25     LogContext *parent_logctx, *child_logctx;
26     Conf *conf;
27     const SftpServerVtable *sftpserver_vt;
28 
29     LogPolicy logpolicy;
30     Seat seat;
31 
32     bool want_pty;
33     struct ssh_ttymodes ttymodes;
34     int wc, hc, wp, hp;
35     strbuf *termtype;
36 
37     bool ignoring_input;
38     bool seen_eof, seen_exit;
39 
40     Plug xfwd_plug;
41     int n_x11_sockets;
42     Socket *x11_sockets[MAX_X11_SOCKETS];
43 
44     agentfwd *agent;
45 
46     Backend *backend;
47 
48     bufchain subsys_input;
49     SftpServer *sftpsrv;
50     ScpServer *scpsrv;
51     const SshServerConfig *ssc;
52 
53     Channel chan;
54 } sesschan;
55 
56 static void sesschan_free(Channel *chan);
57 static size_t sesschan_send(
58     Channel *chan, bool is_stderr, const void *, size_t);
59 static void sesschan_send_eof(Channel *chan);
60 static char *sesschan_log_close_msg(Channel *chan);
61 static bool sesschan_want_close(Channel *, bool, bool);
62 static void sesschan_set_input_wanted(Channel *chan, bool wanted);
63 static bool sesschan_run_shell(Channel *chan);
64 static bool sesschan_run_command(Channel *chan, ptrlen command);
65 static bool sesschan_run_subsystem(Channel *chan, ptrlen subsys);
66 static bool sesschan_enable_x11_forwarding(
67     Channel *chan, bool oneshot, ptrlen authproto, ptrlen authdata,
68     unsigned screen_number);
69 static bool sesschan_enable_agent_forwarding(Channel *chan);
70 static bool sesschan_allocate_pty(
71     Channel *chan, ptrlen termtype, unsigned width, unsigned height,
72     unsigned pixwidth, unsigned pixheight, struct ssh_ttymodes modes);
73 static bool sesschan_set_env(Channel *chan, ptrlen var, ptrlen value);
74 static bool sesschan_send_break(Channel *chan, unsigned length);
75 static bool sesschan_send_signal(Channel *chan, ptrlen signame);
76 static bool sesschan_change_window_size(
77     Channel *chan, unsigned width, unsigned height,
78     unsigned pixwidth, unsigned pixheight);
79 
80 static const ChannelVtable sesschan_channelvt = {
81     .free = sesschan_free,
82     .open_confirmation = chan_remotely_opened_confirmation,
83     .open_failed = chan_remotely_opened_failure,
84     .send = sesschan_send,
85     .send_eof = sesschan_send_eof,
86     .set_input_wanted = sesschan_set_input_wanted,
87     .log_close_msg = sesschan_log_close_msg,
88     .want_close = sesschan_want_close,
89     .rcvd_exit_status = chan_no_exit_status,
90     .rcvd_exit_signal = chan_no_exit_signal,
91     .rcvd_exit_signal_numeric = chan_no_exit_signal_numeric,
92     .run_shell = sesschan_run_shell,
93     .run_command = sesschan_run_command,
94     .run_subsystem = sesschan_run_subsystem,
95     .enable_x11_forwarding = sesschan_enable_x11_forwarding,
96     .enable_agent_forwarding = sesschan_enable_agent_forwarding,
97     .allocate_pty = sesschan_allocate_pty,
98     .set_env = sesschan_set_env,
99     .send_break = sesschan_send_break,
100     .send_signal = sesschan_send_signal,
101     .change_window_size = sesschan_change_window_size,
102     .request_response = chan_no_request_response,
103 };
104 
105 static size_t sftp_chan_send(
106     Channel *chan, bool is_stderr, const void *, size_t);
107 static void sftp_chan_send_eof(Channel *chan);
108 static char *sftp_log_close_msg(Channel *chan);
109 
110 static const ChannelVtable sftp_channelvt = {
111     .free = sesschan_free,
112     .open_confirmation = chan_remotely_opened_confirmation,
113     .open_failed = chan_remotely_opened_failure,
114     .send = sftp_chan_send,
115     .send_eof = sftp_chan_send_eof,
116     .set_input_wanted = sesschan_set_input_wanted,
117     .log_close_msg = sftp_log_close_msg,
118     .want_close = chan_default_want_close,
119     .rcvd_exit_status = chan_no_exit_status,
120     .rcvd_exit_signal = chan_no_exit_signal,
121     .rcvd_exit_signal_numeric = chan_no_exit_signal_numeric,
122     .run_shell = chan_no_run_shell,
123     .run_command = chan_no_run_command,
124     .run_subsystem = chan_no_run_subsystem,
125     .enable_x11_forwarding = chan_no_enable_x11_forwarding,
126     .enable_agent_forwarding = chan_no_enable_agent_forwarding,
127     .allocate_pty = chan_no_allocate_pty,
128     .set_env = chan_no_set_env,
129     .send_break = chan_no_send_break,
130     .send_signal = chan_no_send_signal,
131     .change_window_size = chan_no_change_window_size,
132     .request_response = chan_no_request_response,
133 };
134 
135 static size_t scp_chan_send(
136     Channel *chan, bool is_stderr, const void *, size_t);
137 static void scp_chan_send_eof(Channel *chan);
138 static void scp_set_input_wanted(Channel *chan, bool wanted);
139 static char *scp_log_close_msg(Channel *chan);
140 
141 static const ChannelVtable scp_channelvt = {
142     .free = sesschan_free,
143     .open_confirmation = chan_remotely_opened_confirmation,
144     .open_failed = chan_remotely_opened_failure,
145     .send = scp_chan_send,
146     .send_eof = scp_chan_send_eof,
147     .set_input_wanted = scp_set_input_wanted,
148     .log_close_msg = scp_log_close_msg,
149     .want_close = chan_default_want_close,
150     .rcvd_exit_status = chan_no_exit_status,
151     .rcvd_exit_signal = chan_no_exit_signal,
152     .rcvd_exit_signal_numeric = chan_no_exit_signal_numeric,
153     .run_shell = chan_no_run_shell,
154     .run_command = chan_no_run_command,
155     .run_subsystem = chan_no_run_subsystem,
156     .enable_x11_forwarding = chan_no_enable_x11_forwarding,
157     .enable_agent_forwarding = chan_no_enable_agent_forwarding,
158     .allocate_pty = chan_no_allocate_pty,
159     .set_env = chan_no_set_env,
160     .send_break = chan_no_send_break,
161     .send_signal = chan_no_send_signal,
162     .change_window_size = chan_no_change_window_size,
163     .request_response = chan_no_request_response,
164 };
165 
sesschan_eventlog(LogPolicy * lp,const char * event)166 static void sesschan_eventlog(LogPolicy *lp, const char *event) {}
sesschan_logging_error(LogPolicy * lp,const char * event)167 static void sesschan_logging_error(LogPolicy *lp, const char *event) {}
sesschan_askappend(LogPolicy * lp,Filename * filename,void (* callback)(void * ctx,int result),void * ctx)168 static int sesschan_askappend(
169     LogPolicy *lp, Filename *filename,
170     void (*callback)(void *ctx, int result), void *ctx) { return 2; }
171 
172 static const LogPolicyVtable sesschan_logpolicy_vt = {
173     .eventlog = sesschan_eventlog,
174     .askappend = sesschan_askappend,
175     .logging_error = sesschan_logging_error,
176     .verbose = null_lp_verbose_no,
177 };
178 
179 static size_t sesschan_seat_output(
180     Seat *, bool is_stderr, const void *, size_t);
181 static bool sesschan_seat_eof(Seat *);
182 static void sesschan_notify_remote_exit(Seat *seat);
183 static void sesschan_connection_fatal(Seat *seat, const char *message);
184 static bool sesschan_get_window_pixel_size(Seat *seat, int *w, int *h);
185 
186 static const SeatVtable sesschan_seat_vt = {
187     .output = sesschan_seat_output,
188     .eof = sesschan_seat_eof,
189     .get_userpass_input = nullseat_get_userpass_input,
190     .notify_remote_exit = sesschan_notify_remote_exit,
191     .connection_fatal = sesschan_connection_fatal,
192     .update_specials_menu = nullseat_update_specials_menu,
193     .get_ttymode = nullseat_get_ttymode,
194     .set_busy_status = nullseat_set_busy_status,
195     .verify_ssh_host_key = nullseat_verify_ssh_host_key,
196     .confirm_weak_crypto_primitive = nullseat_confirm_weak_crypto_primitive,
197     .confirm_weak_cached_hostkey = nullseat_confirm_weak_cached_hostkey,
198     .is_utf8 = nullseat_is_never_utf8,
199     .echoedit_update = nullseat_echoedit_update,
200     .get_x_display = nullseat_get_x_display,
201     .get_windowid = nullseat_get_windowid,
202     .get_window_pixel_size = sesschan_get_window_pixel_size,
203     .stripctrl_new = nullseat_stripctrl_new,
204     .set_trust_status = nullseat_set_trust_status,
205     .verbose = nullseat_verbose_no,
206     .interactive = nullseat_interactive_no,
207     .get_cursor_position = nullseat_get_cursor_position,
208 };
209 
sesschan_new(SshChannel * c,LogContext * logctx,const SftpServerVtable * sftpserver_vt,const SshServerConfig * ssc)210 Channel *sesschan_new(SshChannel *c, LogContext *logctx,
211                       const SftpServerVtable *sftpserver_vt,
212                       const SshServerConfig *ssc)
213 {
214     sesschan *sess = snew(sesschan);
215     memset(sess, 0, sizeof(sesschan));
216 
217     sess->c = c;
218     sess->chan.vt = &sesschan_channelvt;
219     sess->chan.initial_fixed_window_size = 0;
220     sess->parent_logctx = logctx;
221     sess->ssc = ssc;
222 
223     /* Start with a completely default Conf */
224     sess->conf = conf_new();
225     load_open_settings(NULL, sess->conf);
226 
227     /* Set close-on-exit = true to suppress uxpty.c's "[pterm: process
228      * terminated with status x]" message */
229     conf_set_int(sess->conf, CONF_close_on_exit, FORCE_ON);
230 
231     sess->seat.vt = &sesschan_seat_vt;
232     sess->logpolicy.vt = &sesschan_logpolicy_vt;
233     sess->child_logctx = log_init(&sess->logpolicy, sess->conf);
234 
235     sess->sftpserver_vt = sftpserver_vt;
236 
237     bufchain_init(&sess->subsys_input);
238 
239     return &sess->chan;
240 }
241 
sesschan_free(Channel * chan)242 static void sesschan_free(Channel *chan)
243 {
244     sesschan *sess = container_of(chan, sesschan, chan);
245     int i;
246 
247     delete_callbacks_for_context(sess);
248     conf_free(sess->conf);
249     if (sess->backend)
250         backend_free(sess->backend);
251     bufchain_clear(&sess->subsys_input);
252     if (sess->sftpsrv)
253         sftpsrv_free(sess->sftpsrv);
254     for (i = 0; i < sess->n_x11_sockets; i++)
255         sk_close(sess->x11_sockets[i]);
256     if (sess->agent)
257         agentfwd_free(sess->agent);
258 
259     sfree(sess);
260 }
261 
sesschan_send(Channel * chan,bool is_stderr,const void * data,size_t length)262 static size_t sesschan_send(Channel *chan, bool is_stderr,
263                             const void *data, size_t length)
264 {
265     sesschan *sess = container_of(chan, sesschan, chan);
266 
267     if (!sess->backend || sess->ignoring_input)
268         return 0;
269 
270     return backend_send(sess->backend, data, length);
271 }
272 
sesschan_send_eof(Channel * chan)273 static void sesschan_send_eof(Channel *chan)
274 {
275     sesschan *sess = container_of(chan, sesschan, chan);
276     if (sess->backend)
277         backend_special(sess->backend, SS_EOF, 0);
278 }
279 
sesschan_log_close_msg(Channel * chan)280 static char *sesschan_log_close_msg(Channel *chan)
281 {
282     return dupstr("Session channel closed");
283 }
284 
sesschan_set_input_wanted(Channel * chan,bool wanted)285 static void sesschan_set_input_wanted(Channel *chan, bool wanted)
286 {
287     /* I don't think we need to do anything here */
288 }
289 
sesschan_start_backend(sesschan * sess,const char * cmd)290 static void sesschan_start_backend(sesschan *sess, const char *cmd)
291 {
292     /*
293      * List of environment variables that we should not pass through
294      * from the login session Uppity was run in (which, it being a
295      * test server, there will usually be one of). These variables
296      * will be set as part of X or agent forwarding, and shouldn't be
297      * confusingly set in the absence of that.
298      *
299      * (DISPLAY must also be cleared, but uxpty.c will do that anyway
300      * when our get_x_display method returns NULL.)
301      */
302     static const char *const env_to_unset[] = {
303         "XAUTHORITY", "SSH_AUTH_SOCK", "SSH_AGENT_PID",
304         NULL /* terminator */
305     };
306 
307     sess->backend = pty_backend_create(
308         &sess->seat, sess->child_logctx, sess->conf, NULL, cmd,
309         sess->ttymodes, !sess->want_pty, sess->ssc->session_starting_dir,
310         env_to_unset);
311     backend_size(sess->backend, sess->wc, sess->hc);
312 }
313 
sesschan_run_shell(Channel * chan)314 bool sesschan_run_shell(Channel *chan)
315 {
316     sesschan *sess = container_of(chan, sesschan, chan);
317 
318     if (sess->backend)
319         return false;
320 
321     sesschan_start_backend(sess, NULL);
322     return true;
323 }
324 
sesschan_run_command(Channel * chan,ptrlen command)325 bool sesschan_run_command(Channel *chan, ptrlen command)
326 {
327     sesschan *sess = container_of(chan, sesschan, chan);
328 
329     if (sess->backend)
330         return false;
331 
332     /* FIXME: make this possible to configure off */
333     if ((sess->scpsrv = scp_recognise_exec(sess->c, sess->sftpserver_vt,
334                                            command)) != NULL) {
335         sess->chan.vt = &scp_channelvt;
336         logevent(sess->parent_logctx, "Starting built-in SCP server");
337         return true;
338     }
339 
340     char *command_str = mkstr(command);
341     sesschan_start_backend(sess, command_str);
342     sfree(command_str);
343 
344     return true;
345 }
346 
sesschan_run_subsystem(Channel * chan,ptrlen subsys)347 bool sesschan_run_subsystem(Channel *chan, ptrlen subsys)
348 {
349     sesschan *sess = container_of(chan, sesschan, chan);
350 
351     if (ptrlen_eq_string(subsys, "sftp") && sess->sftpserver_vt) {
352         sess->sftpsrv = sftpsrv_new(sess->sftpserver_vt);
353         sess->chan.vt = &sftp_channelvt;
354         logevent(sess->parent_logctx, "Starting built-in SFTP subsystem");
355         return true;
356     }
357 
358     return false;
359 }
360 
fwd_log(Plug * plug,PlugLogType type,SockAddr * addr,int port,const char * error_msg,int error_code)361 static void fwd_log(Plug *plug, PlugLogType type, SockAddr *addr, int port,
362                     const char *error_msg, int error_code)
363 { /* don't expect any weirdnesses from a listening socket */ }
fwd_closing(Plug * plug,const char * error_msg,int error_code,bool calling_back)364 static void fwd_closing(Plug *plug, const char *error_msg, int error_code,
365                         bool calling_back)
366 { /* not here, either */ }
367 
xfwd_accepting(Plug * p,accept_fn_t constructor,accept_ctx_t ctx)368 static int xfwd_accepting(Plug *p, accept_fn_t constructor, accept_ctx_t ctx)
369 {
370     sesschan *sess = container_of(p, sesschan, xfwd_plug);
371     Plug *plug;
372     Channel *chan;
373     Socket *s;
374     SocketPeerInfo *pi;
375     const char *err;
376 
377     chan = portfwd_raw_new(sess->c->cl, &plug, false);
378     s = constructor(ctx, plug);
379     if ((err = sk_socket_error(s)) != NULL) {
380         portfwd_raw_free(chan);
381         return 1;
382     }
383     pi = sk_peer_info(s);
384     portfwd_raw_setup(chan, s, ssh_serverside_x11_open(sess->c->cl, chan, pi));
385     sk_free_peer_info(pi);
386 
387     return 0;
388 }
389 
390 static const PlugVtable xfwd_plugvt = {
391     .log = fwd_log,
392     .closing = fwd_closing,
393     .accepting = xfwd_accepting,
394 };
395 
sesschan_enable_x11_forwarding(Channel * chan,bool oneshot,ptrlen authproto,ptrlen authdata_hex,unsigned screen_number)396 bool sesschan_enable_x11_forwarding(
397     Channel *chan, bool oneshot, ptrlen authproto, ptrlen authdata_hex,
398     unsigned screen_number)
399 {
400     sesschan *sess = container_of(chan, sesschan, chan);
401     strbuf *authdata_bin;
402     size_t i;
403     char screensuffix[32];
404 
405     if (oneshot)
406         return false;                  /* not supported */
407 
408     snprintf(screensuffix, sizeof(screensuffix), ".%u", screen_number);
409 
410     /*
411      * Decode the authorisation data from ASCII hex into binary.
412      */
413     if (authdata_hex.len % 2)
414         return false;                  /* expected an even number of digits */
415     authdata_bin = strbuf_new_nm();
416     for (i = 0; i < authdata_hex.len; i += 2) {
417         const unsigned char *hex = authdata_hex.ptr;
418         char hexbuf[3];
419 
420         if (!isxdigit(hex[i]) || !isxdigit(hex[i+1])) {
421             strbuf_free(authdata_bin);
422             return false;              /* not hex */
423         }
424 
425         hexbuf[0] = hex[i];
426         hexbuf[1] = hex[i+1];
427         hexbuf[2] = '\0';
428         put_byte(authdata_bin, strtoul(hexbuf, NULL, 16));
429     }
430 
431     sess->xfwd_plug.vt = &xfwd_plugvt;
432 
433     sess->n_x11_sockets = platform_make_x11_server(
434         &sess->xfwd_plug, appname, 10, screensuffix,
435         authproto, ptrlen_from_strbuf(authdata_bin),
436         sess->x11_sockets, sess->conf);
437 
438     strbuf_free(authdata_bin);
439     return sess->n_x11_sockets != 0;
440 }
441 
agentfwd_accepting(Plug * p,accept_fn_t constructor,accept_ctx_t ctx)442 static int agentfwd_accepting(
443     Plug *p, accept_fn_t constructor, accept_ctx_t ctx)
444 {
445     agentfwd *agent = container_of(p, agentfwd, plug);
446     Plug *plug;
447     Channel *chan;
448     Socket *s;
449     const char *err;
450 
451     chan = portfwd_raw_new(agent->cl, &plug, false);
452     s = constructor(ctx, plug);
453     if ((err = sk_socket_error(s)) != NULL) {
454         portfwd_raw_free(chan);
455         return 1;
456     }
457     portfwd_raw_setup(chan, s, ssh_serverside_agent_open(agent->cl, chan));
458 
459     return 0;
460 }
461 
462 static const PlugVtable agentfwd_plugvt = {
463     .log = fwd_log,
464     .closing = fwd_closing,
465     .accepting = agentfwd_accepting,
466 };
467 
agentfwd_new(ConnectionLayer * cl,char ** socketname_out)468 agentfwd *agentfwd_new(ConnectionLayer *cl, char **socketname_out)
469 {
470     agentfwd *agent = snew(agentfwd);
471     agent->cl = cl;
472     agent->plug.vt = &agentfwd_plugvt;
473 
474     char *dir_prefix = dupprintf("/tmp/%s-agentfwd", appname);
475     char *error = NULL, *socketname = NULL;
476     agent->socket = platform_make_agent_socket(
477         &agent->plug, dir_prefix, &error, &socketname);
478     sfree(dir_prefix);
479     sfree(error);
480 
481     if (!agent->socket) {
482         sfree(agent);
483         sfree(socketname);
484         return NULL;
485     }
486 
487     *socketname_out = socketname;
488     return agent;
489 }
490 
agentfwd_free(agentfwd * agent)491 void agentfwd_free(agentfwd *agent)
492 {
493     if (agent->socket)
494         sk_close(agent->socket);
495     sfree(agent);
496 }
497 
sesschan_enable_agent_forwarding(Channel * chan)498 bool sesschan_enable_agent_forwarding(Channel *chan)
499 {
500     sesschan *sess = container_of(chan, sesschan, chan);
501     char *socketname;
502 
503     assert(!sess->agent);
504 
505     sess->agent = agentfwd_new(sess->c->cl, &socketname);
506 
507     if (!sess->agent)
508         return false;
509 
510     conf_set_str_str(sess->conf, CONF_environmt, "SSH_AUTH_SOCK", socketname);
511     sfree(socketname);
512     return true;
513 }
514 
sesschan_allocate_pty(Channel * chan,ptrlen termtype,unsigned width,unsigned height,unsigned pixwidth,unsigned pixheight,struct ssh_ttymodes modes)515 bool sesschan_allocate_pty(
516     Channel *chan, ptrlen termtype, unsigned width, unsigned height,
517     unsigned pixwidth, unsigned pixheight, struct ssh_ttymodes modes)
518 {
519     sesschan *sess = container_of(chan, sesschan, chan);
520     char *s;
521 
522     if (sess->want_pty)
523         return false;
524 
525     s = mkstr(termtype);
526     conf_set_str(sess->conf, CONF_termtype, s);
527     sfree(s);
528 
529     sess->want_pty = true;
530     sess->ttymodes  = modes;
531     sess->wc = width;
532     sess->hc = height;
533     sess->wp = pixwidth;
534     sess->hp = pixheight;
535 
536     return true;
537 }
538 
sesschan_set_env(Channel * chan,ptrlen var,ptrlen value)539 bool sesschan_set_env(Channel *chan, ptrlen var, ptrlen value)
540 {
541     sesschan *sess = container_of(chan, sesschan, chan);
542 
543     char *svar = mkstr(var), *svalue = mkstr(value);
544     conf_set_str_str(sess->conf, CONF_environmt, svar, svalue);
545     sfree(svar);
546     sfree(svalue);
547 
548     return true;
549 }
550 
sesschan_send_break(Channel * chan,unsigned length)551 bool sesschan_send_break(Channel *chan, unsigned length)
552 {
553     sesschan *sess = container_of(chan, sesschan, chan);
554 
555     if (sess->backend) {
556         /* We ignore the break length. We could pass it through as the
557          * 'arg' parameter, and have uxpty.c collect it and pass it on
558          * to tcsendbreak, but since tcsendbreak in turn assigns
559          * implementation-defined semantics to _its_ duration
560          * parameter, this all just sounds too difficult. */
561         backend_special(sess->backend, SS_BRK, 0);
562         return true;
563     }
564     return false;
565 }
566 
sesschan_send_signal(Channel * chan,ptrlen signame)567 bool sesschan_send_signal(Channel *chan, ptrlen signame)
568 {
569     sesschan *sess = container_of(chan, sesschan, chan);
570 
571     /* Start with a code that definitely isn't a signal (or indeed a
572      * special command at all), to indicate 'nothing matched'. */
573     SessionSpecialCode code = SS_EXITMENU;
574 
575     #define SIGNAL_SUB(name) \
576         if (ptrlen_eq_string(signame, #name)) code = SS_SIG ## name;
577     #define SIGNAL_MAIN(name, text) SIGNAL_SUB(name)
578     #include "sshsignals.h"
579     #undef SIGNAL_MAIN
580     #undef SIGNAL_SUB
581 
582     if (code == SS_EXITMENU)
583         return false;
584 
585     backend_special(sess->backend, code, 0);
586     return true;
587 }
588 
sesschan_change_window_size(Channel * chan,unsigned width,unsigned height,unsigned pixwidth,unsigned pixheight)589 bool sesschan_change_window_size(
590     Channel *chan, unsigned width, unsigned height,
591     unsigned pixwidth, unsigned pixheight)
592 {
593     sesschan *sess = container_of(chan, sesschan, chan);
594 
595     if (!sess->want_pty)
596         return false;
597 
598     sess->wc = width;
599     sess->hc = height;
600     sess->wp = pixwidth;
601     sess->hp = pixheight;
602 
603     if (sess->backend)
604         backend_size(sess->backend, sess->wc, sess->hc);
605 
606     return true;
607 }
608 
sesschan_seat_output(Seat * seat,bool is_stderr,const void * data,size_t len)609 static size_t sesschan_seat_output(
610     Seat *seat, bool is_stderr, const void *data, size_t len)
611 {
612     sesschan *sess = container_of(seat, sesschan, seat);
613     return sshfwd_write_ext(sess->c, is_stderr, data, len);
614 }
615 
sesschan_check_close_callback(void * vctx)616 static void sesschan_check_close_callback(void *vctx)
617 {
618     sesschan *sess = (sesschan *)vctx;
619 
620     /*
621      * Once we've seen incoming EOF from the backend (aka EIO from the
622      * pty master) and also passed on the process's exit status, we
623      * should proactively initiate closure of the session channel.
624      */
625     if (sess->seen_eof && sess->seen_exit)
626         sshfwd_initiate_close(sess->c, NULL);
627 }
628 
sesschan_want_close(Channel * chan,bool seen_eof,bool rcvd_eof)629 static bool sesschan_want_close(Channel *chan, bool seen_eof, bool rcvd_eof)
630 {
631     sesschan *sess = container_of(chan, sesschan, chan);
632 
633     /*
634      * Similarly to above, we don't want to initiate channel closure
635      * until we've sent the process's exit status, _even_ if EOF of
636      * the actual data stream has happened in both directions.
637      */
638     return (sess->seen_eof && sess->seen_exit);
639 }
640 
sesschan_seat_eof(Seat * seat)641 static bool sesschan_seat_eof(Seat *seat)
642 {
643     sesschan *sess = container_of(seat, sesschan, seat);
644 
645     sshfwd_write_eof(sess->c);
646     sess->seen_eof = true;
647 
648     queue_toplevel_callback(sesschan_check_close_callback, sess);
649     return true;
650 }
651 
sesschan_notify_remote_exit(Seat * seat)652 static void sesschan_notify_remote_exit(Seat *seat)
653 {
654     sesschan *sess = container_of(seat, sesschan, seat);
655 
656     if (!sess->backend)
657         return;
658 
659     bool got_signal = false;
660     if (!sess->ssc->exit_signal_numeric) {
661         char *sigmsg;
662         ptrlen signame = pty_backend_exit_signame(sess->backend, &sigmsg);
663 
664         if (signame.len) {
665             if (!sigmsg)
666                 sigmsg = dupstr("");
667 
668             sshfwd_send_exit_signal(
669                 sess->c, signame, false, ptrlen_from_asciz(sigmsg));
670 
671             got_signal = true;
672         }
673 
674         sfree(sigmsg);
675     } else {
676         int signum = pty_backend_exit_signum(sess->backend);
677 
678         if (signum >= 0) {
679             sshfwd_send_exit_signal_numeric(sess->c, signum, false,
680                                             PTRLEN_LITERAL(""));
681             got_signal = true;
682         }
683     }
684 
685     if (!got_signal)
686         sshfwd_send_exit_status(sess->c, backend_exitcode(sess->backend));
687 
688     sess->seen_exit = true;
689     queue_toplevel_callback(sesschan_check_close_callback, sess);
690 }
691 
sesschan_connection_fatal(Seat * seat,const char * message)692 static void sesschan_connection_fatal(Seat *seat, const char *message)
693 {
694     sesschan *sess = container_of(seat, sesschan, seat);
695 
696     /* Closest translation I can think of */
697     sshfwd_send_exit_signal(
698         sess->c, PTRLEN_LITERAL("HUP"), false, ptrlen_from_asciz(message));
699 
700     sess->ignoring_input = true;
701 }
702 
sesschan_get_window_pixel_size(Seat * seat,int * width,int * height)703 static bool sesschan_get_window_pixel_size(Seat *seat, int *width, int *height)
704 {
705     sesschan *sess = container_of(seat, sesschan, seat);
706 
707     *width = sess->wp;
708     *height = sess->hp;
709 
710     return true;
711 }
712 
713 /* ----------------------------------------------------------------------
714  * Built-in SFTP subsystem.
715  */
716 
sftp_chan_send(Channel * chan,bool is_stderr,const void * data,size_t length)717 static size_t sftp_chan_send(Channel *chan, bool is_stderr,
718                              const void *data, size_t length)
719 {
720     sesschan *sess = container_of(chan, sesschan, chan);
721 
722     bufchain_add(&sess->subsys_input, data, length);
723 
724     while (bufchain_size(&sess->subsys_input) >= 4) {
725         char lenbuf[4];
726         unsigned pktlen;
727         struct sftp_packet *pkt, *reply;
728 
729         bufchain_fetch(&sess->subsys_input, lenbuf, 4);
730         pktlen = GET_32BIT_MSB_FIRST(lenbuf);
731 
732         if (bufchain_size(&sess->subsys_input) - 4 < pktlen)
733             break;                     /* wait for more data */
734 
735         bufchain_consume(&sess->subsys_input, 4);
736         pkt = sftp_recv_prepare(pktlen);
737         bufchain_fetch_consume(&sess->subsys_input, pkt->data, pktlen);
738         sftp_recv_finish(pkt);
739         reply = sftp_handle_request(sess->sftpsrv, pkt);
740         sftp_pkt_free(pkt);
741 
742         sftp_send_prepare(reply);
743         sshfwd_write(sess->c, reply->data, reply->length);
744         sftp_pkt_free(reply);
745     }
746 
747     return 0;
748 }
749 
sftp_chan_send_eof(Channel * chan)750 static void sftp_chan_send_eof(Channel *chan)
751 {
752     sesschan *sess = container_of(chan, sesschan, chan);
753     sshfwd_write_eof(sess->c);
754 }
755 
sftp_log_close_msg(Channel * chan)756 static char *sftp_log_close_msg(Channel *chan)
757 {
758     return dupstr("Session channel (SFTP) closed");
759 }
760 
761 /* ----------------------------------------------------------------------
762  * Built-in SCP subsystem.
763  */
764 
scp_chan_send(Channel * chan,bool is_stderr,const void * data,size_t length)765 static size_t scp_chan_send(Channel *chan, bool is_stderr,
766                             const void *data, size_t length)
767 {
768     sesschan *sess = container_of(chan, sesschan, chan);
769     return scp_send(sess->scpsrv, data, length);
770 }
771 
scp_chan_send_eof(Channel * chan)772 static void scp_chan_send_eof(Channel *chan)
773 {
774     sesschan *sess = container_of(chan, sesschan, chan);
775     scp_eof(sess->scpsrv);
776 }
777 
scp_log_close_msg(Channel * chan)778 static char *scp_log_close_msg(Channel *chan)
779 {
780     return dupstr("Session channel (SCP) closed");
781 }
782 
scp_set_input_wanted(Channel * chan,bool wanted)783 static void scp_set_input_wanted(Channel *chan, bool wanted)
784 {
785     sesschan *sess = container_of(chan, sesschan, chan);
786     scp_throttle(sess->scpsrv, !wanted);
787 }
788