diff --git a/agentf.c b/agentf.c index 3fab01a9..e2a26065 100644 --- a/agentf.c +++ b/agentf.c @@ -159,6 +159,16 @@ static const struct ChannelVtable agentf_channelvt = { chan_no_exit_status, chan_no_exit_signal, chan_no_exit_signal_numeric, + chan_no_run_shell, + chan_no_run_command, + chan_no_run_subsystem, + chan_no_enable_x11_forwarding, + chan_no_enable_agent_forwarding, + chan_no_allocate_pty, + chan_no_set_env, + chan_no_send_break, + chan_no_send_signal, + chan_no_change_window_size, chan_no_request_response, }; diff --git a/mainchan.c b/mainchan.c index 1ee731a6..c196da52 100644 --- a/mainchan.c +++ b/mainchan.c @@ -37,6 +37,16 @@ static const struct ChannelVtable mainchan_channelvt = { mainchan_rcvd_exit_status, mainchan_rcvd_exit_signal, mainchan_rcvd_exit_signal_numeric, + chan_no_run_shell, + chan_no_run_command, + chan_no_run_subsystem, + chan_no_enable_x11_forwarding, + chan_no_enable_agent_forwarding, + chan_no_allocate_pty, + chan_no_set_env, + chan_no_send_break, + chan_no_send_signal, + chan_no_change_window_size, mainchan_request_response, }; diff --git a/portfwd.c b/portfwd.c index 6d8d1153..b7acc98b 100644 --- a/portfwd.c +++ b/portfwd.c @@ -454,6 +454,16 @@ static const struct ChannelVtable PortForwarding_channelvt = { chan_no_exit_status, chan_no_exit_signal, chan_no_exit_signal_numeric, + chan_no_run_shell, + chan_no_run_command, + chan_no_run_subsystem, + chan_no_enable_x11_forwarding, + chan_no_enable_agent_forwarding, + chan_no_allocate_pty, + chan_no_set_env, + chan_no_send_break, + chan_no_send_signal, + chan_no_change_window_size, chan_no_request_response, }; diff --git a/ssh.h b/ssh.h index bb99997e..c771604b 100644 --- a/ssh.h +++ b/ssh.h @@ -1435,6 +1435,8 @@ struct ssh_ttymodes { }; struct ssh_ttymodes get_ttymodes_from_conf(Seat *seat, Conf *conf); +struct ssh_ttymodes read_ttymodes_from_packet( + BinarySource *bs, int ssh_version); void write_ttymodes_to_packet(BinarySink *bs, int ssh_version, struct ssh_ttymodes modes); diff --git a/ssh1connection-client.c b/ssh1connection-client.c index 24a6df7d..3018b68d 100644 --- a/ssh1connection-client.c +++ b/ssh1connection-client.c @@ -429,6 +429,9 @@ static const struct SshChannelVtable ssh1mainchan_vtable = { NULL /* get_conf */, NULL /* window_override_removed is only used by SSH-2 sharing */, NULL /* x11_sharing_handover, likewise */, + NULL /* send_exit_status */, + NULL /* send_exit_signal */, + NULL /* send_exit_signal_numeric */, ssh1mainchan_request_x11_forwarding, ssh1mainchan_request_agent_forwarding, ssh1mainchan_request_pty, diff --git a/ssh1connection.c b/ssh1connection.c index 5461eec4..d7c9144d 100644 --- a/ssh1connection.c +++ b/ssh1connection.c @@ -105,6 +105,9 @@ static const struct SshChannelVtable ssh1channel_vtable = { ssh1channel_get_conf, ssh1channel_window_override_removed, NULL /* x11_sharing_handover is only used by SSH-2 connection sharing */, + NULL /* send_exit_status */, + NULL /* send_exit_signal */, + NULL /* send_exit_signal_numeric */, NULL /* request_x11_forwarding */, NULL /* request_agent_forwarding */, NULL /* request_pty */, diff --git a/ssh2connection-client.c b/ssh2connection-client.c index 8cef6aca..5df69fd8 100644 --- a/ssh2connection-client.c +++ b/ssh2connection-client.c @@ -343,6 +343,23 @@ int ssh2channel_start_subsystem( return TRUE; } +void ssh2channel_send_exit_status(SshChannel *sc, int status) +{ + assert(FALSE && "Should never be called in the client"); +} + +void ssh2channel_send_exit_signal( + SshChannel *sc, ptrlen signame, int core_dumped, ptrlen msg) +{ + assert(FALSE && "Should never be called in the client"); +} + +void ssh2channel_send_exit_signal_numeric( + SshChannel *sc, int signum, int core_dumped, ptrlen msg) +{ + assert(FALSE && "Should never be called in the client"); +} + void ssh2channel_request_x11_forwarding( SshChannel *sc, int want_reply, const char *authproto, const char *authdata, int screen_number, int oneshot) diff --git a/ssh2connection.c b/ssh2connection.c index fbda961f..9c27a715 100644 --- a/ssh2connection.c +++ b/ssh2connection.c @@ -137,6 +137,9 @@ static const struct SshChannelVtable ssh2channel_vtable = { ssh2channel_get_conf, ssh2channel_window_override_removed, ssh2channel_x11_sharing_handover, + ssh2channel_send_exit_status, + ssh2channel_send_exit_signal, + ssh2channel_send_exit_signal_numeric, ssh2channel_request_x11_forwarding, ssh2channel_request_agent_forwarding, ssh2channel_request_pty, @@ -655,6 +658,66 @@ static int ssh2_connection_filter_queue(struct ssh2_connection_state *s) reply_success = FALSE; break; } + } else if (ptrlen_eq_string(type, "shell")) { + reply_success = chan_run_shell(c->chan); + } else if (ptrlen_eq_string(type, "exec")) { + ptrlen command = get_string(pktin); + reply_success = chan_run_command(c->chan, command); + } else if (ptrlen_eq_string(type, "subsystem")) { + ptrlen subsys = get_string(pktin); + reply_success = chan_run_subsystem(c->chan, subsys); + } else if (ptrlen_eq_string(type, "x11-req")) { + int oneshot = get_bool(pktin); + ptrlen authproto = get_string(pktin); + ptrlen authdata = get_string(pktin); + unsigned screen_number = get_uint32(pktin); + reply_success = chan_enable_x11_forwarding( + c->chan, oneshot, authproto, authdata, screen_number); + } else if (ptrlen_eq_string(type, + "auth-agent-req@openssh.com")) { + reply_success = chan_enable_agent_forwarding(c->chan); + } else if (ptrlen_eq_string(type, "pty-req")) { + ptrlen termtype = get_string(pktin); + unsigned width = get_uint32(pktin); + unsigned height = get_uint32(pktin); + unsigned pixwidth = get_uint32(pktin); + unsigned pixheight = get_uint32(pktin); + ptrlen encoded_modes = get_string(pktin); + BinarySource bs_modes[1]; + struct ssh_ttymodes modes; + + BinarySource_BARE_INIT( + bs_modes, encoded_modes.ptr, encoded_modes.len); + modes = read_ttymodes_from_packet(bs_modes, 2); + if (get_err(bs_modes) || get_avail(bs_modes) > 0) { + ppl_logevent(("Unable to decode terminal mode " + "string")); + reply_success = FALSE; + } else { + reply_success = chan_allocate_pty( + c->chan, termtype, width, height, + pixwidth, pixheight, modes); + } + } else if (ptrlen_eq_string(type, "env")) { + ptrlen var = get_string(pktin); + ptrlen value = get_string(pktin); + + reply_success = chan_set_env(c->chan, var, value); + } else if (ptrlen_eq_string(type, "break")) { + unsigned length = get_uint32(pktin); + + reply_success = chan_send_break(c->chan, length); + } else if (ptrlen_eq_string(type, "signal")) { + ptrlen signame = get_string(pktin); + + reply_success = chan_send_signal(c->chan, signame); + } else if (ptrlen_eq_string(type, "window-change")) { + unsigned width = get_uint32(pktin); + unsigned height = get_uint32(pktin); + unsigned pixwidth = get_uint32(pktin); + unsigned pixheight = get_uint32(pktin); + reply_success = chan_change_window_size( + c->chan, width, height, pixwidth, pixheight); } if (want_reply) { int type = (reply_success ? SSH2_MSG_CHANNEL_SUCCESS : diff --git a/ssh2connection.h b/ssh2connection.h index c898d6bd..11e62419 100644 --- a/ssh2connection.h +++ b/ssh2connection.h @@ -166,6 +166,11 @@ void ssh2_rportfwd_remove( SshChannel *ssh2_session_open(ConnectionLayer *cl, Channel *chan); +void ssh2channel_send_exit_status(SshChannel *c, int status); +void ssh2channel_send_exit_signal( + SshChannel *c, ptrlen signame, int core_dumped, ptrlen msg); +void ssh2channel_send_exit_signal_numeric( + SshChannel *c, int signum, int core_dumped, ptrlen msg); void ssh2channel_request_x11_forwarding( SshChannel *c, int want_reply, const char *authproto, const char *authdata, int screen_number, int oneshot); diff --git a/sshchan.h b/sshchan.h index 382023ab..e01d6bb5 100644 --- a/sshchan.h +++ b/sshchan.h @@ -34,6 +34,22 @@ struct ChannelVtable { Channel *chan, ptrlen signame, int core_dumped, ptrlen msg); int (*rcvd_exit_signal_numeric)( Channel *chan, int signum, int core_dumped, ptrlen msg); + int (*run_shell)(Channel *chan); + int (*run_command)(Channel *chan, ptrlen command); + int (*run_subsystem)(Channel *chan, ptrlen subsys); + int (*enable_x11_forwarding)( + Channel *chan, int oneshot, ptrlen authproto, ptrlen authdata, + unsigned screen_number); + int (*enable_agent_forwarding)(Channel *chan); + int (*allocate_pty)( + Channel *chan, ptrlen termtype, unsigned width, unsigned height, + unsigned pixwidth, unsigned pixheight, struct ssh_ttymodes modes); + int (*set_env)(Channel *chan, ptrlen var, ptrlen value); + int (*send_break)(Channel *chan, unsigned length); + int (*send_signal)(Channel *chan, ptrlen signame); + int (*change_window_size)( + Channel *chan, unsigned width, unsigned height, + unsigned pixwidth, unsigned pixheight); /* A method for signalling success/failure responses to channel * requests initiated from the SshChannel vtable with want_reply @@ -61,6 +77,26 @@ struct Channel { ((ch)->vt->rcvd_exit_signal(ch, sig, core, msg)) #define chan_rcvd_exit_signal_numeric(ch, sig, core, msg) \ ((ch)->vt->rcvd_exit_signal_numeric(ch, sig, core, msg)) +#define chan_run_shell(ch) \ + ((ch)->vt->run_shell(ch)) +#define chan_run_command(ch, cmd) \ + ((ch)->vt->run_command(ch, cmd)) +#define chan_run_subsystem(ch, subsys) \ + ((ch)->vt->run_subsystem(ch, subsys)) +#define chan_enable_x11_forwarding(ch, oneshot, ap, ad, scr) \ + ((ch)->vt->enable_x11_forwarding(ch, oneshot, ap, ad, scr)) +#define chan_enable_agent_forwarding(ch) \ + ((ch)->vt->enable_agent_forwarding(ch)) +#define chan_allocate_pty(ch, termtype, w, h, pw, ph, modes) \ + ((ch)->vt->allocate_pty(ch, termtype, w, h, pw, ph, modes)) +#define chan_set_env(ch, var, value) \ + ((ch)->vt->set_env(ch, var, value)) +#define chan_send_break(ch, length) \ + ((ch)->vt->send_break(ch, length)) +#define chan_send_signal(ch, signame) \ + ((ch)->vt->send_signal(ch, signame)) +#define chan_change_window_size(ch, w, h, pw, ph) \ + ((ch)->vt->change_window_size(ch, w, h, pw, ph)) #define chan_request_response(ch, success) \ ((ch)->vt->request_response(ch, success)) @@ -81,6 +117,22 @@ int chan_default_want_close(Channel *, int, int); int chan_no_exit_status(Channel *, int); int chan_no_exit_signal(Channel *, ptrlen, int, ptrlen); int chan_no_exit_signal_numeric(Channel *, int, int, ptrlen); +int chan_no_run_shell(Channel *chan); +int chan_no_run_command(Channel *chan, ptrlen command); +int chan_no_run_subsystem(Channel *chan, ptrlen subsys); +int chan_no_enable_x11_forwarding( + Channel *chan, int oneshot, ptrlen authproto, ptrlen authdata, + unsigned screen_number); +int chan_no_enable_agent_forwarding(Channel *chan); +int chan_no_allocate_pty( + Channel *chan, ptrlen termtype, unsigned width, unsigned height, + unsigned pixwidth, unsigned pixheight, struct ssh_ttymodes modes); +int chan_no_set_env(Channel *chan, ptrlen var, ptrlen value); +int chan_no_send_break(Channel *chan, unsigned length); +int chan_no_send_signal(Channel *chan, ptrlen signame); +int chan_no_change_window_size( + Channel *chan, unsigned width, unsigned height, + unsigned pixwidth, unsigned pixheight); /* default implementation that never expects to receive a response */ void chan_no_request_response(Channel *, int); @@ -131,6 +183,11 @@ struct SshChannelVtable { * wouldn't do anything usefully different with the reply in any * case.) */ + void (*send_exit_status)(SshChannel *c, int status); + void (*send_exit_signal)( + SshChannel *c, ptrlen signame, int core_dumped, ptrlen msg); + void (*send_exit_signal_numeric)( + SshChannel *c, int signum, int core_dumped, ptrlen msg); void (*request_x11_forwarding)( SshChannel *c, int want_reply, const char *authproto, const char *authdata, int screen_number, int oneshot); @@ -170,6 +227,12 @@ struct SshChannel { #define sshfwd_window_override_removed(c) ((c)->vt->window_override_removed(c)) #define sshfwd_x11_sharing_handover(c, cs, ch, pa, pp, e, pmaj, pmin, d, l) \ ((c)->vt->x11_sharing_handover(c, cs, ch, pa, pp, e, pmaj, pmin, d, l)) +#define sshfwd_send_exit_status(c, status) \ + ((c)->vt->send_exit_status(c, status)) +#define sshfwd_send_exit_signal(c, sig, core, msg) \ + ((c)->vt->send_exit_signal(c, sig, core, msg)) +#define sshfwd_send_exit_signal_numeric(c, sig, core, msg) \ + ((c)->vt->send_exit_signal_numeric(c, sig, core, msg)) #define sshfwd_request_x11_forwarding(c, wr, ap, ad, scr, oneshot) \ ((c)->vt->request_x11_forwarding(c, wr, ap, ad, scr, oneshot)) #define sshfwd_request_agent_forwarding(c, wr) \ diff --git a/sshcommon.c b/sshcommon.c index f0ef7009..56f4b420 100644 --- a/sshcommon.c +++ b/sshcommon.c @@ -280,6 +280,16 @@ static const struct ChannelVtable zombiechan_channelvt = { chan_no_exit_status, chan_no_exit_signal, chan_no_exit_signal_numeric, + chan_no_run_shell, + chan_no_run_command, + chan_no_run_subsystem, + chan_no_enable_x11_forwarding, + chan_no_enable_agent_forwarding, + chan_no_allocate_pty, + chan_no_set_env, + chan_no_send_break, + chan_no_send_signal, + chan_no_change_window_size, chan_no_request_response, }; @@ -366,6 +376,62 @@ int chan_no_exit_signal_numeric( return FALSE; } +int chan_no_run_shell(Channel *chan) +{ + return FALSE; +} + +int chan_no_run_command(Channel *chan, ptrlen command) +{ + return FALSE; +} + +int chan_no_run_subsystem(Channel *chan, ptrlen subsys) +{ + return FALSE; +} + +int chan_no_enable_x11_forwarding( + Channel *chan, int oneshot, ptrlen authproto, ptrlen authdata, + unsigned screen_number) +{ + return FALSE; +} + +int chan_no_enable_agent_forwarding(Channel *chan) +{ + return FALSE; +} + +int chan_no_allocate_pty( + Channel *chan, ptrlen termtype, unsigned width, unsigned height, + unsigned pixwidth, unsigned pixheight, struct ssh_ttymodes modes) +{ + return FALSE; +} + +int chan_no_set_env(Channel *chan, ptrlen var, ptrlen value) +{ + return FALSE; +} + +int chan_no_send_break(Channel *chan, unsigned length) +{ + return FALSE; +} + +int chan_no_send_signal(Channel *chan, ptrlen signame) +{ + return FALSE; +} + +int chan_no_change_window_size( + Channel *chan, unsigned width, unsigned height, + unsigned pixwidth, unsigned pixheight) +{ + return FALSE; +} + void chan_no_request_response(Channel *chan, int success) { assert(0 && "this channel type should never send a want-reply request"); @@ -387,6 +453,29 @@ static unsigned real_ttymode_opcode(unsigned our_opcode, int ssh_version) } } +static unsigned our_ttymode_opcode(unsigned real_opcode, int ssh_version) +{ + if (ssh_version == 1) { + switch (real_opcode) { + case TTYMODE_ISPEED_SSH1: + return TTYMODE_ISPEED; + case TTYMODE_OSPEED_SSH1: + return TTYMODE_OSPEED; + default: + return real_opcode; + } + } else { + switch (real_opcode) { + case TTYMODE_ISPEED_SSH2: + return TTYMODE_ISPEED; + case TTYMODE_OSPEED_SSH2: + return TTYMODE_OSPEED; + default: + return real_opcode; + } + } +} + struct ssh_ttymodes get_ttymodes_from_conf(Seat *seat, Conf *conf) { struct ssh_ttymodes modes; @@ -492,6 +581,49 @@ struct ssh_ttymodes get_ttymodes_from_conf(Seat *seat, Conf *conf) return modes; } +struct ssh_ttymodes read_ttymodes_from_packet( + BinarySource *bs, int ssh_version) +{ + struct ssh_ttymodes modes; + memset(&modes, 0, sizeof(modes)); + + while (1) { + unsigned real_opcode, our_opcode; + + real_opcode = get_byte(bs); + if (real_opcode == TTYMODE_END_OF_LIST) + break; + if (real_opcode >= 160) { + /* + * RFC 4254 (and the SSH 1.5 spec): "Opcodes 160 to 255 + * are not yet defined, and cause parsing to stop (they + * should only be used after any other data)." + * + * My interpretation of this is that if one of these + * opcodes appears, it's not a parse _error_, but it is + * something that we don't know how to parse even well + * enough to step over it to find the next opcode, so we + * stop parsing now and assume that the rest of the string + * is composed entirely of things we don't understand and + * (as usual for unsupported terminal modes) silently + * ignore. + */ + return modes; + } + + our_opcode = our_ttymode_opcode(real_opcode, ssh_version); + assert(our_opcode < TTYMODE_LIMIT); + modes.have_mode[our_opcode] = TRUE; + + if (ssh_version == 1 && real_opcode >= 1 && real_opcode <= 127) + modes.mode_val[our_opcode] = get_byte(bs); + else + modes.mode_val[our_opcode] = get_uint32(bs); + } + + return modes; +} + void write_ttymodes_to_packet(BinarySink *bs, int ssh_version, struct ssh_ttymodes modes) { diff --git a/unix/uxpgnt.c b/unix/uxpgnt.c index 86d4e2c1..ca1be2fe 100644 --- a/unix/uxpgnt.c +++ b/unix/uxpgnt.c @@ -127,6 +127,23 @@ int chan_no_exit_signal(Channel *ch, ptrlen s, int c, ptrlen m) { return FALSE; } int chan_no_exit_signal_numeric(Channel *ch, int s, int c, ptrlen m) { return FALSE; } +int chan_no_run_shell(Channel *chan) { return FALSE; } +int chan_no_run_command(Channel *chan, ptrlen command) { return FALSE; } +int chan_no_run_subsystem(Channel *chan, ptrlen subsys) { return FALSE; } +int chan_no_enable_x11_forwarding( + Channel *chan, int oneshot, ptrlen authproto, ptrlen authdata, + unsigned screen_number) { return FALSE; } +int chan_no_enable_agent_forwarding(Channel *chan) { return FALSE; } +int chan_no_allocate_pty( + Channel *chan, ptrlen termtype, unsigned width, unsigned height, + unsigned pixwidth, unsigned pixheight, struct ssh_ttymodes modes) +{ return FALSE; } +int chan_no_set_env(Channel *chan, ptrlen var, ptrlen value) { return FALSE; } +int chan_no_send_break(Channel *chan, unsigned length) { return FALSE; } +int chan_no_send_signal(Channel *chan, ptrlen signame) { return FALSE; } +int chan_no_change_window_size( + Channel *chan, unsigned width, unsigned height, + unsigned pixwidth, unsigned pixheight) { return FALSE; } void chan_no_request_response(Channel *chan, int success) {} /* diff --git a/x11fwd.c b/x11fwd.c index 7feb2a63..287003fd 100644 --- a/x11fwd.c +++ b/x11fwd.c @@ -776,6 +776,16 @@ static const struct ChannelVtable X11Connection_channelvt = { chan_no_exit_status, chan_no_exit_signal, chan_no_exit_signal_numeric, + chan_no_run_shell, + chan_no_run_command, + chan_no_run_subsystem, + chan_no_enable_x11_forwarding, + chan_no_enable_agent_forwarding, + chan_no_allocate_pty, + chan_no_set_env, + chan_no_send_break, + chan_no_send_signal, + chan_no_change_window_size, chan_no_request_response, };