diff options
Diffstat (limited to 'ssh/sesschan.c')
-rw-r--r-- | ssh/sesschan.c | 798 |
1 files changed, 798 insertions, 0 deletions
diff --git a/ssh/sesschan.c b/ssh/sesschan.c new file mode 100644 index 00000000..9776eaf2 --- /dev/null +++ b/ssh/sesschan.c @@ -0,0 +1,798 @@ +/* + * Implement the "session" channel type for the SSH server. + */ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> + +#include "putty.h" +#include "ssh.h" +#include "channel.h" +#include "server.h" +#include "sftp.h" + +struct agentfwd { + ConnectionLayer *cl; + Socket *socket; + Plug plug; +}; + +typedef struct sesschan { + SshChannel *c; + + LogContext *parent_logctx, *child_logctx; + Conf *conf; + const SftpServerVtable *sftpserver_vt; + + LogPolicy logpolicy; + Seat seat; + + bool want_pty; + struct ssh_ttymodes ttymodes; + int wc, hc, wp, hp; + strbuf *termtype; + + bool ignoring_input; + bool seen_eof, seen_exit; + + Plug xfwd_plug; + int n_x11_sockets; + Socket *x11_sockets[MAX_X11_SOCKETS]; + + agentfwd *agent; + + Backend *backend; + + bufchain subsys_input; + SftpServer *sftpsrv; + ScpServer *scpsrv; + const SshServerConfig *ssc; + + Channel chan; +} sesschan; + +static void sesschan_free(Channel *chan); +static size_t sesschan_send( + Channel *chan, bool is_stderr, const void *, size_t); +static void sesschan_send_eof(Channel *chan); +static char *sesschan_log_close_msg(Channel *chan); +static bool sesschan_want_close(Channel *, bool, bool); +static void sesschan_set_input_wanted(Channel *chan, bool wanted); +static bool sesschan_run_shell(Channel *chan); +static bool sesschan_run_command(Channel *chan, ptrlen command); +static bool sesschan_run_subsystem(Channel *chan, ptrlen subsys); +static bool sesschan_enable_x11_forwarding( + Channel *chan, bool oneshot, ptrlen authproto, ptrlen authdata, + unsigned screen_number); +static bool sesschan_enable_agent_forwarding(Channel *chan); +static bool sesschan_allocate_pty( + Channel *chan, ptrlen termtype, unsigned width, unsigned height, + unsigned pixwidth, unsigned pixheight, struct ssh_ttymodes modes); +static bool sesschan_set_env(Channel *chan, ptrlen var, ptrlen value); +static bool sesschan_send_break(Channel *chan, unsigned length); +static bool sesschan_send_signal(Channel *chan, ptrlen signame); +static bool sesschan_change_window_size( + Channel *chan, unsigned width, unsigned height, + unsigned pixwidth, unsigned pixheight); + +static const ChannelVtable sesschan_channelvt = { + .free = sesschan_free, + .open_confirmation = chan_remotely_opened_confirmation, + .open_failed = chan_remotely_opened_failure, + .send = sesschan_send, + .send_eof = sesschan_send_eof, + .set_input_wanted = sesschan_set_input_wanted, + .log_close_msg = sesschan_log_close_msg, + .want_close = sesschan_want_close, + .rcvd_exit_status = chan_no_exit_status, + .rcvd_exit_signal = chan_no_exit_signal, + .rcvd_exit_signal_numeric = chan_no_exit_signal_numeric, + .run_shell = sesschan_run_shell, + .run_command = sesschan_run_command, + .run_subsystem = sesschan_run_subsystem, + .enable_x11_forwarding = sesschan_enable_x11_forwarding, + .enable_agent_forwarding = sesschan_enable_agent_forwarding, + .allocate_pty = sesschan_allocate_pty, + .set_env = sesschan_set_env, + .send_break = sesschan_send_break, + .send_signal = sesschan_send_signal, + .change_window_size = sesschan_change_window_size, + .request_response = chan_no_request_response, +}; + +static size_t sftp_chan_send( + Channel *chan, bool is_stderr, const void *, size_t); +static void sftp_chan_send_eof(Channel *chan); +static char *sftp_log_close_msg(Channel *chan); + +static const ChannelVtable sftp_channelvt = { + .free = sesschan_free, + .open_confirmation = chan_remotely_opened_confirmation, + .open_failed = chan_remotely_opened_failure, + .send = sftp_chan_send, + .send_eof = sftp_chan_send_eof, + .set_input_wanted = sesschan_set_input_wanted, + .log_close_msg = sftp_log_close_msg, + .want_close = chan_default_want_close, + .rcvd_exit_status = chan_no_exit_status, + .rcvd_exit_signal = chan_no_exit_signal, + .rcvd_exit_signal_numeric = chan_no_exit_signal_numeric, + .run_shell = chan_no_run_shell, + .run_command = chan_no_run_command, + .run_subsystem = chan_no_run_subsystem, + .enable_x11_forwarding = chan_no_enable_x11_forwarding, + .enable_agent_forwarding = chan_no_enable_agent_forwarding, + .allocate_pty = chan_no_allocate_pty, + .set_env = chan_no_set_env, + .send_break = chan_no_send_break, + .send_signal = chan_no_send_signal, + .change_window_size = chan_no_change_window_size, + .request_response = chan_no_request_response, +}; + +static size_t scp_chan_send( + Channel *chan, bool is_stderr, const void *, size_t); +static void scp_chan_send_eof(Channel *chan); +static void scp_set_input_wanted(Channel *chan, bool wanted); +static char *scp_log_close_msg(Channel *chan); + +static const ChannelVtable scp_channelvt = { + .free = sesschan_free, + .open_confirmation = chan_remotely_opened_confirmation, + .open_failed = chan_remotely_opened_failure, + .send = scp_chan_send, + .send_eof = scp_chan_send_eof, + .set_input_wanted = scp_set_input_wanted, + .log_close_msg = scp_log_close_msg, + .want_close = chan_default_want_close, + .rcvd_exit_status = chan_no_exit_status, + .rcvd_exit_signal = chan_no_exit_signal, + .rcvd_exit_signal_numeric = chan_no_exit_signal_numeric, + .run_shell = chan_no_run_shell, + .run_command = chan_no_run_command, + .run_subsystem = chan_no_run_subsystem, + .enable_x11_forwarding = chan_no_enable_x11_forwarding, + .enable_agent_forwarding = chan_no_enable_agent_forwarding, + .allocate_pty = chan_no_allocate_pty, + .set_env = chan_no_set_env, + .send_break = chan_no_send_break, + .send_signal = chan_no_send_signal, + .change_window_size = chan_no_change_window_size, + .request_response = chan_no_request_response, +}; + +static void sesschan_eventlog(LogPolicy *lp, const char *event) {} +static void sesschan_logging_error(LogPolicy *lp, const char *event) {} +static int sesschan_askappend( + LogPolicy *lp, Filename *filename, + void (*callback)(void *ctx, int result), void *ctx) { return 2; } + +static const LogPolicyVtable sesschan_logpolicy_vt = { + .eventlog = sesschan_eventlog, + .askappend = sesschan_askappend, + .logging_error = sesschan_logging_error, + .verbose = null_lp_verbose_no, +}; + +static size_t sesschan_seat_output( + Seat *, SeatOutputType type, const void *, size_t); +static bool sesschan_seat_eof(Seat *); +static void sesschan_notify_remote_exit(Seat *seat); +static void sesschan_connection_fatal(Seat *seat, const char *message); +static bool sesschan_get_window_pixel_size(Seat *seat, int *w, int *h); + +static const SeatVtable sesschan_seat_vt = { + .output = sesschan_seat_output, + .eof = sesschan_seat_eof, + .sent = nullseat_sent, + .banner = nullseat_banner, + .get_userpass_input = nullseat_get_userpass_input, + .notify_session_started = nullseat_notify_session_started, + .notify_remote_exit = sesschan_notify_remote_exit, + .notify_remote_disconnect = nullseat_notify_remote_disconnect, + .connection_fatal = sesschan_connection_fatal, + .update_specials_menu = nullseat_update_specials_menu, + .get_ttymode = nullseat_get_ttymode, + .set_busy_status = nullseat_set_busy_status, + .confirm_ssh_host_key = nullseat_confirm_ssh_host_key, + .confirm_weak_crypto_primitive = nullseat_confirm_weak_crypto_primitive, + .confirm_weak_cached_hostkey = nullseat_confirm_weak_cached_hostkey, + .prompt_descriptions = nullseat_prompt_descriptions, + .is_utf8 = nullseat_is_never_utf8, + .echoedit_update = nullseat_echoedit_update, + .get_x_display = nullseat_get_x_display, + .get_windowid = nullseat_get_windowid, + .get_window_pixel_size = sesschan_get_window_pixel_size, + .stripctrl_new = nullseat_stripctrl_new, + .set_trust_status = nullseat_set_trust_status, + .can_set_trust_status = nullseat_can_set_trust_status_no, + .has_mixed_input_stream = nullseat_has_mixed_input_stream_no, + .verbose = nullseat_verbose_no, + .interactive = nullseat_interactive_no, + .get_cursor_position = nullseat_get_cursor_position, +}; + +Channel *sesschan_new(SshChannel *c, LogContext *logctx, + const SftpServerVtable *sftpserver_vt, + const SshServerConfig *ssc) +{ + sesschan *sess = snew(sesschan); + memset(sess, 0, sizeof(sesschan)); + + sess->c = c; + sess->chan.vt = &sesschan_channelvt; + sess->chan.initial_fixed_window_size = 0; + sess->parent_logctx = logctx; + sess->ssc = ssc; + + /* Start with a completely default Conf */ + sess->conf = conf_new(); + load_open_settings(NULL, sess->conf); + + /* Set close-on-exit = true to suppress pty.c's "[pterm: process + * terminated with status x]" message */ + conf_set_int(sess->conf, CONF_close_on_exit, FORCE_ON); + + sess->seat.vt = &sesschan_seat_vt; + sess->logpolicy.vt = &sesschan_logpolicy_vt; + sess->child_logctx = log_init(&sess->logpolicy, sess->conf); + + sess->sftpserver_vt = sftpserver_vt; + + bufchain_init(&sess->subsys_input); + + return &sess->chan; +} + +static void sesschan_free(Channel *chan) +{ + sesschan *sess = container_of(chan, sesschan, chan); + int i; + + delete_callbacks_for_context(sess); + conf_free(sess->conf); + if (sess->backend) + backend_free(sess->backend); + bufchain_clear(&sess->subsys_input); + if (sess->sftpsrv) + sftpsrv_free(sess->sftpsrv); + for (i = 0; i < sess->n_x11_sockets; i++) + sk_close(sess->x11_sockets[i]); + if (sess->agent) + agentfwd_free(sess->agent); + + sfree(sess); +} + +static size_t sesschan_send(Channel *chan, bool is_stderr, + const void *data, size_t length) +{ + sesschan *sess = container_of(chan, sesschan, chan); + + if (!sess->backend || sess->ignoring_input) + return 0; + + backend_send(sess->backend, data, length); + return backend_sendbuffer(sess->backend); +} + +static void sesschan_send_eof(Channel *chan) +{ + sesschan *sess = container_of(chan, sesschan, chan); + if (sess->backend) + backend_special(sess->backend, SS_EOF, 0); +} + +static char *sesschan_log_close_msg(Channel *chan) +{ + return dupstr("Session channel closed"); +} + +static void sesschan_set_input_wanted(Channel *chan, bool wanted) +{ + sesschan *sess = container_of(chan, sesschan, chan); + /* Request the back end to resume sending input, if it had become + * throttled by the channel window shortening */ + if (wanted && sess->backend) + backend_unthrottle(sess->backend, 0); +} + +static void sesschan_start_backend(sesschan *sess, const char *cmd) +{ + /* + * List of environment variables that we should not pass through + * from the login session Uppity was run in (which, it being a + * test server, there will usually be one of). These variables + * will be set as part of X or agent forwarding, and shouldn't be + * confusingly set in the absence of that. + * + * (DISPLAY must also be cleared, but pty.c will do that anyway + * when our get_x_display method returns NULL.) + */ + static const char *const env_to_unset[] = { + "XAUTHORITY", "SSH_AUTH_SOCK", "SSH_AGENT_PID", + NULL /* terminator */ + }; + + sess->backend = pty_backend_create( + &sess->seat, sess->child_logctx, sess->conf, NULL, cmd, + sess->ttymodes, !sess->want_pty, sess->ssc->session_starting_dir, + env_to_unset); + backend_size(sess->backend, sess->wc, sess->hc); +} + +bool sesschan_run_shell(Channel *chan) +{ + sesschan *sess = container_of(chan, sesschan, chan); + + if (sess->backend) + return false; + + sesschan_start_backend(sess, NULL); + return true; +} + +bool sesschan_run_command(Channel *chan, ptrlen command) +{ + sesschan *sess = container_of(chan, sesschan, chan); + + if (sess->backend) + return false; + + /* FIXME: make this possible to configure off */ + if ((sess->scpsrv = scp_recognise_exec(sess->c, sess->sftpserver_vt, + command)) != NULL) { + sess->chan.vt = &scp_channelvt; + logevent(sess->parent_logctx, "Starting built-in SCP server"); + return true; + } + + char *command_str = mkstr(command); + sesschan_start_backend(sess, command_str); + sfree(command_str); + + return true; +} + +bool sesschan_run_subsystem(Channel *chan, ptrlen subsys) +{ + sesschan *sess = container_of(chan, sesschan, chan); + + if (ptrlen_eq_string(subsys, "sftp") && sess->sftpserver_vt) { + sess->sftpsrv = sftpsrv_new(sess->sftpserver_vt); + sess->chan.vt = &sftp_channelvt; + logevent(sess->parent_logctx, "Starting built-in SFTP subsystem"); + return true; + } + + return false; +} + +static void fwd_log(Plug *plug, PlugLogType type, SockAddr *addr, int port, + const char *error_msg, int error_code) +{ /* don't expect any weirdnesses from a listening socket */ } +static void fwd_closing(Plug *plug, PlugCloseType type, const char *error_msg) +{ /* not here, either */ } + +static int xfwd_accepting(Plug *p, accept_fn_t constructor, accept_ctx_t ctx) +{ + sesschan *sess = container_of(p, sesschan, xfwd_plug); + Plug *plug; + Channel *chan; + Socket *s; + SocketPeerInfo *pi; + const char *err; + + chan = portfwd_raw_new(sess->c->cl, &plug, false); + s = constructor(ctx, plug); + if ((err = sk_socket_error(s)) != NULL) { + portfwd_raw_free(chan); + return 1; + } + pi = sk_peer_info(s); + portfwd_raw_setup(chan, s, ssh_serverside_x11_open(sess->c->cl, chan, pi)); + sk_free_peer_info(pi); + + return 0; +} + +static const PlugVtable xfwd_plugvt = { + .log = fwd_log, + .closing = fwd_closing, + .accepting = xfwd_accepting, +}; + +bool sesschan_enable_x11_forwarding( + Channel *chan, bool oneshot, ptrlen authproto, ptrlen authdata_hex, + unsigned screen_number) +{ + sesschan *sess = container_of(chan, sesschan, chan); + strbuf *authdata_bin; + size_t i; + + if (oneshot) + return false; /* not supported */ + + /* + * Decode the authorisation data from ASCII hex into binary. + */ + if (authdata_hex.len % 2) + return false; /* expected an even number of digits */ + authdata_bin = strbuf_new_nm(); + for (i = 0; i < authdata_hex.len; i += 2) { + const unsigned char *hex = authdata_hex.ptr; + char hexbuf[3]; + + if (!isxdigit(hex[i]) || !isxdigit(hex[i+1])) { + strbuf_free(authdata_bin); + return false; /* not hex */ + } + + hexbuf[0] = hex[i]; + hexbuf[1] = hex[i+1]; + hexbuf[2] = '\0'; + put_byte(authdata_bin, strtoul(hexbuf, NULL, 16)); + } + + sess->xfwd_plug.vt = &xfwd_plugvt; + + char *screensuffix = dupprintf(".%u", screen_number); + + sess->n_x11_sockets = platform_make_x11_server( + &sess->xfwd_plug, appname, 10, screensuffix, + authproto, ptrlen_from_strbuf(authdata_bin), + sess->x11_sockets, sess->conf); + + sfree(screensuffix); + strbuf_free(authdata_bin); + return sess->n_x11_sockets != 0; +} + +static int agentfwd_accepting( + Plug *p, accept_fn_t constructor, accept_ctx_t ctx) +{ + agentfwd *agent = container_of(p, agentfwd, plug); + Plug *plug; + Channel *chan; + Socket *s; + const char *err; + + chan = portfwd_raw_new(agent->cl, &plug, false); + s = constructor(ctx, plug); + if ((err = sk_socket_error(s)) != NULL) { + portfwd_raw_free(chan); + return 1; + } + portfwd_raw_setup(chan, s, ssh_serverside_agent_open(agent->cl, chan)); + + return 0; +} + +static const PlugVtable agentfwd_plugvt = { + .log = fwd_log, + .closing = fwd_closing, + .accepting = agentfwd_accepting, +}; + +agentfwd *agentfwd_new(ConnectionLayer *cl, char **socketname_out) +{ + agentfwd *agent = snew(agentfwd); + agent->cl = cl; + agent->plug.vt = &agentfwd_plugvt; + + char *dir_prefix = dupprintf("/tmp/%s-agentfwd", appname); + char *error = NULL, *socketname = NULL; + agent->socket = platform_make_agent_socket( + &agent->plug, dir_prefix, &error, &socketname); + sfree(dir_prefix); + sfree(error); + + if (!agent->socket) { + sfree(agent); + sfree(socketname); + return NULL; + } + + *socketname_out = socketname; + return agent; +} + +void agentfwd_free(agentfwd *agent) +{ + if (agent->socket) + sk_close(agent->socket); + sfree(agent); +} + +bool sesschan_enable_agent_forwarding(Channel *chan) +{ + sesschan *sess = container_of(chan, sesschan, chan); + char *socketname; + + assert(!sess->agent); + + sess->agent = agentfwd_new(sess->c->cl, &socketname); + + if (!sess->agent) + return false; + + conf_set_str_str(sess->conf, CONF_environmt, "SSH_AUTH_SOCK", socketname); + sfree(socketname); + return true; +} + +bool sesschan_allocate_pty( + Channel *chan, ptrlen termtype, unsigned width, unsigned height, + unsigned pixwidth, unsigned pixheight, struct ssh_ttymodes modes) +{ + sesschan *sess = container_of(chan, sesschan, chan); + char *s; + + if (sess->want_pty) + return false; + + s = mkstr(termtype); + conf_set_str(sess->conf, CONF_termtype, s); + sfree(s); + + sess->want_pty = true; + sess->ttymodes = modes; + sess->wc = width; + sess->hc = height; + sess->wp = pixwidth; + sess->hp = pixheight; + + return true; +} + +bool sesschan_set_env(Channel *chan, ptrlen var, ptrlen value) +{ + sesschan *sess = container_of(chan, sesschan, chan); + + char *svar = mkstr(var), *svalue = mkstr(value); + conf_set_str_str(sess->conf, CONF_environmt, svar, svalue); + sfree(svar); + sfree(svalue); + + return true; +} + +bool sesschan_send_break(Channel *chan, unsigned length) +{ + sesschan *sess = container_of(chan, sesschan, chan); + + if (sess->backend) { + /* We ignore the break length. We could pass it through as the + * 'arg' parameter, and have pty.c collect it and pass it on + * to tcsendbreak, but since tcsendbreak in turn assigns + * implementation-defined semantics to _its_ duration + * parameter, this all just sounds too difficult. */ + backend_special(sess->backend, SS_BRK, 0); + return true; + } + return false; +} + +bool sesschan_send_signal(Channel *chan, ptrlen signame) +{ + sesschan *sess = container_of(chan, sesschan, chan); + + /* Start with a code that definitely isn't a signal (or indeed a + * special command at all), to indicate 'nothing matched'. */ + SessionSpecialCode code = SS_EXITMENU; + + #define SIGNAL_SUB(name) \ + if (ptrlen_eq_string(signame, #name)) code = SS_SIG ## name; + #define SIGNAL_MAIN(name, text) SIGNAL_SUB(name) + #include "signal-list.h" + #undef SIGNAL_MAIN + #undef SIGNAL_SUB + + if (code == SS_EXITMENU) + return false; + + backend_special(sess->backend, code, 0); + return true; +} + +bool sesschan_change_window_size( + Channel *chan, unsigned width, unsigned height, + unsigned pixwidth, unsigned pixheight) +{ + sesschan *sess = container_of(chan, sesschan, chan); + + if (!sess->want_pty) + return false; + + sess->wc = width; + sess->hc = height; + sess->wp = pixwidth; + sess->hp = pixheight; + + if (sess->backend) + backend_size(sess->backend, sess->wc, sess->hc); + + return true; +} + +static size_t sesschan_seat_output( + Seat *seat, SeatOutputType type, const void *data, size_t len) +{ + sesschan *sess = container_of(seat, sesschan, seat); + return sshfwd_write_ext(sess->c, type == SEAT_OUTPUT_STDERR, data, len); +} + +static void sesschan_check_close_callback(void *vctx) +{ + sesschan *sess = (sesschan *)vctx; + + /* + * Once we've seen incoming EOF from the backend (aka EIO from the + * pty master) and also passed on the process's exit status, we + * should proactively initiate closure of the session channel. + */ + if (sess->seen_eof && sess->seen_exit) + sshfwd_initiate_close(sess->c, NULL); +} + +static bool sesschan_want_close(Channel *chan, bool seen_eof, bool rcvd_eof) +{ + sesschan *sess = container_of(chan, sesschan, chan); + + /* + * Similarly to above, we don't want to initiate channel closure + * until we've sent the process's exit status, _even_ if EOF of + * the actual data stream has happened in both directions. + */ + return (sess->seen_eof && sess->seen_exit); +} + +static bool sesschan_seat_eof(Seat *seat) +{ + sesschan *sess = container_of(seat, sesschan, seat); + + sshfwd_write_eof(sess->c); + sess->seen_eof = true; + + queue_toplevel_callback(sesschan_check_close_callback, sess); + return true; +} + +static void sesschan_notify_remote_exit(Seat *seat) +{ + sesschan *sess = container_of(seat, sesschan, seat); + + if (!sess->backend) + return; + + bool got_signal = false; + if (!sess->ssc->exit_signal_numeric) { + char *sigmsg; + ptrlen signame = pty_backend_exit_signame(sess->backend, &sigmsg); + + if (signame.len) { + if (!sigmsg) + sigmsg = dupstr(""); + + sshfwd_send_exit_signal( + sess->c, signame, false, ptrlen_from_asciz(sigmsg)); + + got_signal = true; + } + + sfree(sigmsg); + } else { + int signum = pty_backend_exit_signum(sess->backend); + + if (signum >= 0) { + sshfwd_send_exit_signal_numeric(sess->c, signum, false, + PTRLEN_LITERAL("")); + got_signal = true; + } + } + + if (!got_signal) + sshfwd_send_exit_status(sess->c, backend_exitcode(sess->backend)); + + sess->seen_exit = true; + queue_toplevel_callback(sesschan_check_close_callback, sess); +} + +static void sesschan_connection_fatal(Seat *seat, const char *message) +{ + sesschan *sess = container_of(seat, sesschan, seat); + + /* Closest translation I can think of */ + sshfwd_send_exit_signal( + sess->c, PTRLEN_LITERAL("HUP"), false, ptrlen_from_asciz(message)); + + sess->ignoring_input = true; +} + +static bool sesschan_get_window_pixel_size(Seat *seat, int *width, int *height) +{ + sesschan *sess = container_of(seat, sesschan, seat); + + *width = sess->wp; + *height = sess->hp; + + return true; +} + +/* ---------------------------------------------------------------------- + * Built-in SFTP subsystem. + */ + +static size_t sftp_chan_send(Channel *chan, bool is_stderr, + const void *data, size_t length) +{ + sesschan *sess = container_of(chan, sesschan, chan); + + bufchain_add(&sess->subsys_input, data, length); + + while (bufchain_size(&sess->subsys_input) >= 4) { + char lenbuf[4]; + unsigned pktlen; + struct sftp_packet *pkt, *reply; + + bufchain_fetch(&sess->subsys_input, lenbuf, 4); + pktlen = GET_32BIT_MSB_FIRST(lenbuf); + + if (bufchain_size(&sess->subsys_input) - 4 < pktlen) + break; /* wait for more data */ + + bufchain_consume(&sess->subsys_input, 4); + pkt = sftp_recv_prepare(pktlen); + bufchain_fetch_consume(&sess->subsys_input, pkt->data, pktlen); + sftp_recv_finish(pkt); + reply = sftp_handle_request(sess->sftpsrv, pkt); + sftp_pkt_free(pkt); + + sftp_send_prepare(reply); + sshfwd_write(sess->c, reply->data, reply->length); + sftp_pkt_free(reply); + } + + return 0; +} + +static void sftp_chan_send_eof(Channel *chan) +{ + sesschan *sess = container_of(chan, sesschan, chan); + sshfwd_write_eof(sess->c); +} + +static char *sftp_log_close_msg(Channel *chan) +{ + return dupstr("Session channel (SFTP) closed"); +} + +/* ---------------------------------------------------------------------- + * Built-in SCP subsystem. + */ + +static size_t scp_chan_send(Channel *chan, bool is_stderr, + const void *data, size_t length) +{ + sesschan *sess = container_of(chan, sesschan, chan); + return scp_send(sess->scpsrv, data, length); +} + +static void scp_chan_send_eof(Channel *chan) +{ + sesschan *sess = container_of(chan, sesschan, chan); + scp_eof(sess->scpsrv); +} + +static char *scp_log_close_msg(Channel *chan) +{ + return dupstr("Session channel (SCP) closed"); +} + +static void scp_set_input_wanted(Channel *chan, bool wanted) +{ + sesschan *sess = container_of(chan, sesschan, chan); + scp_throttle(sess->scpsrv, !wanted); +} |