diff --git a/agentf.c b/agentf.c index 3b3407fa..fd1e8b72 100644 --- a/agentf.c +++ b/agentf.c @@ -156,6 +156,9 @@ static const struct ChannelVtable agentf_channelvt = { agentf_set_input_wanted, agentf_log_close_msg, chan_no_eager_close, + chan_no_exit_status, + chan_no_exit_signal, + chan_no_exit_signal_numeric, }; Channel *agentf_new(SshChannel *c) diff --git a/portfwd.c b/portfwd.c index 6165df15..f1ced29c 100644 --- a/portfwd.c +++ b/portfwd.c @@ -451,6 +451,9 @@ static const struct ChannelVtable PortForwarding_channelvt = { pfd_set_input_wanted, pfd_log_close_msg, chan_no_eager_close, + chan_no_exit_status, + chan_no_exit_signal, + chan_no_exit_signal_numeric, }; /* diff --git a/ssh2connection.c b/ssh2connection.c index c0fe4961..047e4041 100644 --- a/ssh2connection.c +++ b/ssh2connection.c @@ -444,7 +444,7 @@ static int ssh2_connection_filter_queue(struct ssh2_connection_state *s) struct ssh2_channel *c; struct outstanding_channel_request *ocr; unsigned localid, remid, winsize, pktsize, ext_type; - int want_reply, reply_type, expect_halfopen; + int want_reply, reply_success, expect_halfopen; const char *error; PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ @@ -466,13 +466,7 @@ static int ssh2_connection_filter_queue(struct ssh2_connection_state *s) /* type = */ get_string(pktin); want_reply = get_bool(pktin); - /* - * 'reply_type' is the message type we'll send in - * response, if want_reply is set. Initialise it to the - * default value of REQUEST_FAILURE, for any request we - * don't recognise and handle below. - */ - reply_type = SSH2_MSG_REQUEST_FAILURE; + reply_success = FALSE; /* * We currently don't support any incoming global requests @@ -481,7 +475,9 @@ static int ssh2_connection_filter_queue(struct ssh2_connection_state *s) */ if (want_reply) { - pktout = ssh_bpp_new_pktout(s->ppl.bpp, reply_type); + int type = (reply_success ? SSH2_MSG_REQUEST_SUCCESS : + SSH2_MSG_REQUEST_FAILURE); + pktout = ssh_bpp_new_pktout(s->ppl.bpp, type); pq_push(s->ppl.out_pq, pktout); } pq_pop(s->ppl.in_pq); @@ -773,13 +769,7 @@ static int ssh2_connection_filter_queue(struct ssh2_connection_state *s) type = get_string(pktin); want_reply = get_bool(pktin); - /* - * 'reply_type' is the message type we'll send in - * response, if want_reply is set. Initialise it to - * the default value of CHANNEL_FAILURE, for any - * request we don't recognise and handle below. - */ - reply_type = SSH2_MSG_CHANNEL_FAILURE; + reply_success = FALSE; if (c->closes & CLOSES_SENT_CLOSE) { /* @@ -793,141 +783,71 @@ static int ssh2_connection_filter_queue(struct ssh2_connection_state *s) } /* - * Having got the channel number, we now look at the - * request type string to see if it's something we - * recognise. + * Try every channel request name we recognise, no + * matter what the channel, and see if the Channel + * instance will accept it. */ - if (c == s->mainchan) { - int exitcode; + if (ptrlen_eq_string(type, "exit-status")) { + int exitcode = toint(get_uint32(pktin)); + reply_success = chan_rcvd_exit_status(c->chan, exitcode); + } else if (ptrlen_eq_string(type, "exit-signal")) { + ptrlen signame; + int signum; + int core = FALSE; + ptrlen errmsg; + int format; /* - * We recognise "exit-status" and "exit-signal" on - * the primary channel. + * ICK: older versions of OpenSSH (e.g. 3.4p1) + * provide an `int' for the signal, despite its + * having been a `string' in the drafts of RFC + * 4254 since at least 2001. (Fixed in session.c + * 1.147.) Try to infer which we can safely parse + * it as. */ - if (ptrlen_eq_string(type, "exit-status")) { - exitcode = toint(get_uint32(pktin)); - ssh_got_exitcode(s->ppl.ssh, exitcode); - ppl_logevent(("Server sent command exit status %d", - exitcode)); - reply_type = SSH2_MSG_CHANNEL_SUCCESS; - } else if (ptrlen_eq_string(type, "exit-signal")) { - char *fmt_sig = NULL, *fmt_msg = NULL; - ptrlen errmsg; - int core = FALSE; - int format; - /* - * ICK: older versions of OpenSSH (e.g. 3.4p1) - * provide an `int' for the signal, despite - * its having been a `string' in the drafts of - * RFC 4254 since at least 2001. (Fixed in - * session.c 1.147.) Try to infer which we can - * safely parse it as. - */ + size_t startpos = BinarySource_UPCAST(pktin)->pos; - size_t startpos = BinarySource_UPCAST(pktin)->pos; + for (format = 0; format < 2; format++) { + BinarySource_UPCAST(pktin)->pos = startpos; + BinarySource_UPCAST(pktin)->err = BSE_NO_ERROR; - for (format = 0; format < 2; format++) { - BinarySource_UPCAST(pktin)->pos = startpos; - BinarySource_UPCAST(pktin)->err = BSE_NO_ERROR; + /* placate compiler warnings about unin */ + signame = make_ptrlen(NULL, 0); + signum = 0; - if (format == 0) { - /* standard string-based format */ - ptrlen signame = get_string(pktin); - fmt_sig = dupprintf(" \"%.*s\"", - PTRLEN_PRINTF(signame)); + if (format == 0) /* standard string-based format */ + signame = get_string(pktin); + else /* nonstandard integer format */ + signum = toint(get_uint32(pktin)); - /* - * Really hideous method of translating the - * signal description back into a locally - * meaningful number. - */ + core = get_bool(pktin); + errmsg = get_string(pktin); /* error message */ + get_string(pktin); /* language tag */ - if (0) - ; -#define TRANSLATE_SIGNAL(s) \ - else if (ptrlen_eq_string(signame, #s)) \ - exitcode = 128 + SIG ## s -#ifdef SIGABRT - TRANSLATE_SIGNAL(ABRT); -#endif -#ifdef SIGALRM - TRANSLATE_SIGNAL(ALRM); -#endif -#ifdef SIGFPE - TRANSLATE_SIGNAL(FPE); -#endif -#ifdef SIGHUP - TRANSLATE_SIGNAL(HUP); -#endif -#ifdef SIGILL - TRANSLATE_SIGNAL(ILL); -#endif -#ifdef SIGINT - TRANSLATE_SIGNAL(INT); -#endif -#ifdef SIGKILL - TRANSLATE_SIGNAL(KILL); -#endif -#ifdef SIGPIPE - TRANSLATE_SIGNAL(PIPE); -#endif -#ifdef SIGQUIT - TRANSLATE_SIGNAL(QUIT); -#endif -#ifdef SIGSEGV - TRANSLATE_SIGNAL(SEGV); -#endif -#ifdef SIGTERM - TRANSLATE_SIGNAL(TERM); -#endif -#ifdef SIGUSR1 - TRANSLATE_SIGNAL(USR1); -#endif -#ifdef SIGUSR2 - TRANSLATE_SIGNAL(USR2); -#endif -#undef TRANSLATE_SIGNAL - else - exitcode = 128; - } else { - /* nonstandard integer format */ - unsigned signum = get_uint32(pktin); - fmt_sig = dupprintf(" %u", signum); - exitcode = 128 + signum; - } + if (!get_err(pktin) && get_avail(pktin) == 0) + break; /* successful parse */ + } - core = get_bool(pktin); - errmsg = get_string(pktin); /* error message */ - get_string(pktin); /* language tag */ - if (!get_err(pktin) && get_avail(pktin) == 0) - break; /* successful parse */ - - sfree(fmt_sig); - } - - if (format == 2) { - fmt_sig = NULL; - exitcode = 128; - } - - if (errmsg.len) { - fmt_msg = dupprintf(" (\"%.*s\")", - PTRLEN_PRINTF(errmsg)); - } - - ssh_got_exitcode(s->ppl.ssh, exitcode); - ppl_logevent(("Server exited on signal%s%s%s", - fmt_sig ? fmt_sig : "", - core ? " (core dumped)" : "", - fmt_msg ? fmt_msg : "")); - sfree(fmt_sig); - sfree(fmt_msg); - reply_type = SSH2_MSG_CHANNEL_SUCCESS; + switch (format) { + case 0: + reply_success = chan_rcvd_exit_signal( + c->chan, signame, core, errmsg); + break; + case 1: + reply_success = chan_rcvd_exit_signal_numeric( + c->chan, signum, core, errmsg); + break; + default: + /* Couldn't parse this message in either format */ + reply_success = FALSE; + break; } } if (want_reply) { - pktout = ssh_bpp_new_pktout(s->ppl.bpp, reply_type); + int type = (reply_success ? SSH2_MSG_CHANNEL_SUCCESS : + SSH2_MSG_CHANNEL_FAILURE); + pktout = ssh_bpp_new_pktout(s->ppl.bpp, type); put_uint32(pktout, c->remoteid); pq_push(s->ppl.out_pq, pktout); } @@ -2193,6 +2113,11 @@ static int mainchan_send(Channel *chan, int is_stderr, const void *, int); static void mainchan_send_eof(Channel *chan); static void mainchan_set_input_wanted(Channel *chan, int wanted); static char *mainchan_log_close_msg(Channel *chan); +static int mainchan_rcvd_exit_status(Channel *chan, int status); +static int mainchan_rcvd_exit_signal( + Channel *chan, ptrlen signame, int core_dumped, ptrlen msg); +static int mainchan_rcvd_exit_signal_numeric( + Channel *chan, int signum, int core_dumped, ptrlen msg); static const struct ChannelVtable mainchan_channelvt = { mainchan_free, @@ -2203,6 +2128,9 @@ static const struct ChannelVtable mainchan_channelvt = { mainchan_set_input_wanted, mainchan_log_close_msg, chan_no_eager_close, + mainchan_rcvd_exit_status, + mainchan_rcvd_exit_signal, + mainchan_rcvd_exit_signal_numeric, }; static mainchan *mainchan_new(struct ssh2_connection_state *s) @@ -2299,6 +2227,119 @@ static char *mainchan_log_close_msg(Channel *chan) return dupstr("Main session channel closed"); } +static int mainchan_rcvd_exit_status(Channel *chan, int status) +{ + assert(chan->vt == &mainchan_channelvt); + mainchan *mc = container_of(chan, mainchan, chan); + struct ssh2_connection_state *s = mc->connlayer; + PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ + + ssh_got_exitcode(s->ppl.ssh, status); + ppl_logevent(("Session sent command exit status %d", status)); + return TRUE; +} + +static void ssh2_log_exit_signal_common( + struct ssh2_connection_state *s, const char *sigdesc, + int core_dumped, ptrlen msg) +{ + PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ + + const char *core_msg = core_dumped ? " (core dumped)" : ""; + const char *msg_pre = (msg.len ? " (" : ""); + const char *msg_post = (msg.len ? ")" : ""); + ppl_logevent(("Session exited on %s%s%s%.*s%s", + sigdesc, core_msg, msg_pre, PTRLEN_PRINTF(msg), msg_post)); +} + +static int mainchan_rcvd_exit_signal( + Channel *chan, ptrlen signame, int core_dumped, ptrlen msg) +{ + assert(chan->vt == &mainchan_channelvt); + mainchan *mc = container_of(chan, mainchan, chan); + struct ssh2_connection_state *s = mc->connlayer; + int exitcode; + char *signame_str; + + /* + * Translate the signal description back into a locally + * meaningful number. + */ + + if (0) + ; +#define TRANSLATE_SIGNAL(s) \ + else if (ptrlen_eq_string(signame, #s)) \ + exitcode = 128 + SIG ## s +#ifdef SIGABRT + TRANSLATE_SIGNAL(ABRT); +#endif +#ifdef SIGALRM + TRANSLATE_SIGNAL(ALRM); +#endif +#ifdef SIGFPE + TRANSLATE_SIGNAL(FPE); +#endif +#ifdef SIGHUP + TRANSLATE_SIGNAL(HUP); +#endif +#ifdef SIGILL + TRANSLATE_SIGNAL(ILL); +#endif +#ifdef SIGINT + TRANSLATE_SIGNAL(INT); +#endif +#ifdef SIGKILL + TRANSLATE_SIGNAL(KILL); +#endif +#ifdef SIGPIPE + TRANSLATE_SIGNAL(PIPE); +#endif +#ifdef SIGQUIT + TRANSLATE_SIGNAL(QUIT); +#endif +#ifdef SIGSEGV + TRANSLATE_SIGNAL(SEGV); +#endif +#ifdef SIGTERM + TRANSLATE_SIGNAL(TERM); +#endif +#ifdef SIGUSR1 + TRANSLATE_SIGNAL(USR1); +#endif +#ifdef SIGUSR2 + TRANSLATE_SIGNAL(USR2); +#endif +#undef TRANSLATE_SIGNAL + else + exitcode = 128; + + ssh_got_exitcode(s->ppl.ssh, exitcode); + if (exitcode == 128) + signame_str = dupprintf("unrecognised signal \"%.*s\"", + PTRLEN_PRINTF(signame)); + else + signame_str = dupprintf("signal SIG%.*s", PTRLEN_PRINTF(signame)); + ssh2_log_exit_signal_common(s, signame_str, core_dumped, msg); + sfree(signame_str); + return TRUE; +} + +static int mainchan_rcvd_exit_signal_numeric( + Channel *chan, int signum, int core_dumped, ptrlen msg) +{ + assert(chan->vt == &mainchan_channelvt); + mainchan *mc = container_of(chan, mainchan, chan); + struct ssh2_connection_state *s = mc->connlayer; + char *signum_str; + + ssh_got_exitcode(s->ppl.ssh, 128 + signum); + signum_str = dupprintf("signal %d", signum); + ssh2_log_exit_signal_common(s, signum_str, core_dumped, msg); + sfree(signum_str); + return TRUE; +} + /* * List of signal names defined by RFC 4254. These include all the ISO * C signals, but are a subset of the POSIX required signals. diff --git a/sshchan.h b/sshchan.h index e2bc28b0..8cf8914a 100644 --- a/sshchan.h +++ b/sshchan.h @@ -26,6 +26,14 @@ struct ChannelVtable { char *(*log_close_msg)(Channel *); int (*want_close)(Channel *, int sent_local_eof, int rcvd_remote_eof); + + /* A method for every channel request we know of. All of these + * return TRUE for success or FALSE for failure. */ + int (*rcvd_exit_status)(Channel *, int status); + int (*rcvd_exit_signal)( + Channel *chan, ptrlen signame, int core_dumped, ptrlen msg); + int (*rcvd_exit_signal_numeric)( + Channel *chan, int signum, int core_dumped, ptrlen msg); }; struct Channel { @@ -42,6 +50,12 @@ struct Channel { ((ch)->vt->set_input_wanted(ch, wanted)) #define chan_log_close_msg(ch) ((ch)->vt->log_close_msg(ch)) #define chan_want_close(ch, leof, reof) ((ch)->vt->want_close(ch, leof, reof)) +#define chan_rcvd_exit_status(ch, status) \ + ((ch)->vt->rcvd_exit_status(ch, status)) +#define chan_rcvd_exit_signal(ch, sig, core, msg) \ + ((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)) /* * Reusable methods you can put in vtables to give default handling of @@ -56,6 +70,11 @@ void chan_remotely_opened_failure(Channel *chan, const char *errtext); * closing until both directions have had an EOF */ int chan_no_eager_close(Channel *, int, int); +/* default implementations that refuse all the channel requests */ +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); + /* * Constructor for a trivial do-nothing implementation of * ChannelVtable. Used for 'zombie' channels, i.e. channels whose diff --git a/sshcommon.c b/sshcommon.c index 6095930a..8a6f524c 100644 --- a/sshcommon.c +++ b/sshcommon.c @@ -277,6 +277,9 @@ static const struct ChannelVtable zombiechan_channelvt = { zombiechan_set_input_wanted, zombiechan_log_close_msg, zombiechan_want_close, + chan_no_exit_status, + chan_no_exit_signal, + chan_no_exit_signal_numeric, }; Channel *zombiechan_new(void) @@ -340,6 +343,23 @@ int chan_no_eager_close(Channel *chan, int sent_local_eof, int rcvd_remote_eof) return FALSE; /* default: never proactively ask for a close */ } +int chan_no_exit_status(Channel *chan, int status) +{ + return FALSE; +} + +int chan_no_exit_signal( + Channel *chan, ptrlen signame, int core_dumped, ptrlen msg) +{ + return FALSE; +} + +int chan_no_exit_signal_numeric( + Channel *chan, int signum, int core_dumped, ptrlen msg) +{ + return FALSE; +} + /* ---------------------------------------------------------------------- * Common routine to marshal tty modes into an SSH packet. */ diff --git a/unix/uxpgnt.c b/unix/uxpgnt.c index 06efb0bc..09c6892e 100644 --- a/unix/uxpgnt.c +++ b/unix/uxpgnt.c @@ -125,6 +125,11 @@ static int time_to_die = FALSE; void chan_remotely_opened_confirmation(Channel *chan) { } void chan_remotely_opened_failure(Channel *chan, const char *err) { } int chan_no_eager_close(Channel *chan, int s, int r) { return FALSE; } +int chan_no_exit_status(Channel *ch, int s) { return FALSE; } +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; } /* * These functions are part of the plug for our connection to the X diff --git a/x11fwd.c b/x11fwd.c index 0aaa3a60..75105eae 100644 --- a/x11fwd.c +++ b/x11fwd.c @@ -731,6 +731,9 @@ static const struct ChannelVtable X11Connection_channelvt = { x11_set_input_wanted, x11_log_close_msg, chan_no_eager_close, + chan_no_exit_status, + chan_no_exit_signal, + chan_no_exit_signal_numeric, }; /*