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