diff options
-rw-r--r-- | ChangeLog | 12 | ||||
-rw-r--r-- | PROTOCOL.mux | 11 | ||||
-rw-r--r-- | clientloop.c | 53 | ||||
-rw-r--r-- | clientloop.h | 4 | ||||
-rw-r--r-- | mux.c | 60 |
5 files changed, 111 insertions, 29 deletions
@@ -50,6 +50,18 @@ | |||
50 | - jmc@cvs.openbsd.org 2011/05/07 23:20:25 | 50 | - jmc@cvs.openbsd.org 2011/05/07 23:20:25 |
51 | [ssh.1] | 51 | [ssh.1] |
52 | +.It RequestTTY | 52 | +.It RequestTTY |
53 | - djm@cvs.openbsd.org 2011/05/08 12:52:01 | ||
54 | [PROTOCOL.mux clientloop.c clientloop.h mux.c] | ||
55 | improve our behaviour when TTY allocation fails: if we are in | ||
56 | RequestTTY=auto mode (the default), then do not treat at TTY | ||
57 | allocation error as fatal but rather just restore the local TTY | ||
58 | to cooked mode and continue. This is more graceful on devices that | ||
59 | never allocate TTYs. | ||
60 | |||
61 | If RequestTTY is set to "yes" or "force", then failure to allocate | ||
62 | a TTY is fatal. | ||
63 | |||
64 | ok markus@ | ||
53 | 65 | ||
54 | 20110510 | 66 | 20110510 |
55 | - (dtucker) [openbsd-compat/openssl-compat.{c,h}] Bug #1882: fix | 67 | - (dtucker) [openbsd-compat/openssl-compat.{c,h}] Bug #1882: fix |
diff --git a/PROTOCOL.mux b/PROTOCOL.mux index 3da9e37ae..9ad256602 100644 --- a/PROTOCOL.mux +++ b/PROTOCOL.mux | |||
@@ -73,6 +73,13 @@ non-multiplexed ssh(1) connection. Two additional cases that the | |||
73 | client must cope with are it receiving a signal itself and the | 73 | client must cope with are it receiving a signal itself and the |
74 | server disconnecting without sending an exit message. | 74 | server disconnecting without sending an exit message. |
75 | 75 | ||
76 | A master may also send a MUX_S_TTY_ALLOC_FAIL before MUX_S_EXIT_MESSAGE | ||
77 | if remote TTY allocation was unsuccessful. The client may use this to | ||
78 | return its local tty to "cooked" mode. | ||
79 | |||
80 | uint32 MUX_S_TTY_ALLOC_FAIL | ||
81 | uint32 session id | ||
82 | |||
76 | 3. Health checks | 83 | 3. Health checks |
77 | 84 | ||
78 | The client may request a health check/PID report from a server: | 85 | The client may request a health check/PID report from a server: |
@@ -197,6 +204,7 @@ The MUX_S_PERMISSION_DENIED and MUX_S_FAILURE include a reason: | |||
197 | #define MUX_S_ALIVE 0x80000005 | 204 | #define MUX_S_ALIVE 0x80000005 |
198 | #define MUX_S_SESSION_OPENED 0x80000006 | 205 | #define MUX_S_SESSION_OPENED 0x80000006 |
199 | #define MUX_S_REMOTE_PORT 0x80000007 | 206 | #define MUX_S_REMOTE_PORT 0x80000007 |
207 | #define MUX_S_TTY_ALLOC_FAIL 0x80000008 | ||
200 | 208 | ||
201 | #define MUX_FWD_LOCAL 1 | 209 | #define MUX_FWD_LOCAL 1 |
202 | #define MUX_FWD_REMOTE 2 | 210 | #define MUX_FWD_REMOTE 2 |
@@ -208,7 +216,6 @@ XXX lock (maybe) | |||
208 | XXX watch in/out traffic (pre/post crypto) | 216 | XXX watch in/out traffic (pre/post crypto) |
209 | XXX inject packet (what about replies) | 217 | XXX inject packet (what about replies) |
210 | XXX server->client error/warning notifications | 218 | XXX server->client error/warning notifications |
211 | XXX port0 rfwd (need custom response message) | ||
212 | XXX send signals via mux | 219 | XXX send signals via mux |
213 | 220 | ||
214 | $OpenBSD: PROTOCOL.mux,v 1.6 2011/05/06 22:20:10 djm Exp $ | 221 | $OpenBSD: PROTOCOL.mux,v 1.7 2011/05/08 12:52:01 djm Exp $ |
diff --git a/clientloop.c b/clientloop.c index 5bd757dfb..ed1d8a238 100644 --- a/clientloop.c +++ b/clientloop.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: clientloop.c,v 1.233 2011/05/06 21:34:32 djm Exp $ */ | 1 | /* $OpenBSD: clientloop.c,v 1.234 2011/05/08 12:52:01 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -174,9 +174,11 @@ struct escape_filter_ctx { | |||
174 | }; | 174 | }; |
175 | 175 | ||
176 | /* Context for channel confirmation replies */ | 176 | /* Context for channel confirmation replies */ |
177 | enum confirm_action { CONFIRM_WARN = 0, CONFIRM_CLOSE, CONFIRM_TTY }; | ||
177 | struct channel_reply_ctx { | 178 | struct channel_reply_ctx { |
178 | const char *request_type; | 179 | const char *request_type; |
179 | int id, do_close; | 180 | int id; |
181 | enum confirm_action action; | ||
180 | }; | 182 | }; |
181 | 183 | ||
182 | /* Global request success/failure callbacks */ | 184 | /* Global request success/failure callbacks */ |
@@ -739,6 +741,15 @@ client_status_confirm(int type, Channel *c, void *ctx) | |||
739 | char errmsg[256]; | 741 | char errmsg[256]; |
740 | int tochan; | 742 | int tochan; |
741 | 743 | ||
744 | /* | ||
745 | * If a TTY was explicitly requested, then a failure to allocate | ||
746 | * one is fatal. | ||
747 | */ | ||
748 | if (cr->action == CONFIRM_TTY && | ||
749 | (options.request_tty == REQUEST_TTY_FORCE || | ||
750 | options.request_tty == REQUEST_TTY_YES)) | ||
751 | cr->action = CONFIRM_CLOSE; | ||
752 | |||
742 | /* XXX supress on mux _client_ quietmode */ | 753 | /* XXX supress on mux _client_ quietmode */ |
743 | tochan = options.log_level >= SYSLOG_LEVEL_ERROR && | 754 | tochan = options.log_level >= SYSLOG_LEVEL_ERROR && |
744 | c->ctl_chan != -1 && c->extended_usage == CHAN_EXTENDED_WRITE; | 755 | c->ctl_chan != -1 && c->extended_usage == CHAN_EXTENDED_WRITE; |
@@ -756,14 +767,27 @@ client_status_confirm(int type, Channel *c, void *ctx) | |||
756 | cr->request_type, c->self); | 767 | cr->request_type, c->self); |
757 | } | 768 | } |
758 | /* If error occurred on primary session channel, then exit */ | 769 | /* If error occurred on primary session channel, then exit */ |
759 | if (cr->do_close && c->self == session_ident) | 770 | if (cr->action == CONFIRM_CLOSE && c->self == session_ident) |
760 | fatal("%s", errmsg); | 771 | fatal("%s", errmsg); |
761 | /* If error occurred on mux client, append to their stderr */ | 772 | /* |
762 | if (tochan) | 773 | * If error occurred on mux client, append to |
763 | buffer_append(&c->extended, errmsg, strlen(errmsg)); | 774 | * their stderr. |
764 | else | 775 | */ |
776 | if (tochan) { | ||
777 | buffer_append(&c->extended, errmsg, | ||
778 | strlen(errmsg)); | ||
779 | } else | ||
765 | error("%s", errmsg); | 780 | error("%s", errmsg); |
766 | if (cr->do_close) { | 781 | if (cr->action == CONFIRM_TTY) { |
782 | /* | ||
783 | * If a TTY allocation error occurred, then arrange | ||
784 | * for the correct TTY to leave raw mode. | ||
785 | */ | ||
786 | if (c->self == session_ident) | ||
787 | leave_raw_mode(0); | ||
788 | else | ||
789 | mux_tty_alloc_failed(c); | ||
790 | } else if (cr->action == CONFIRM_CLOSE) { | ||
767 | chan_read_failed(c); | 791 | chan_read_failed(c); |
768 | chan_write_failed(c); | 792 | chan_write_failed(c); |
769 | } | 793 | } |
@@ -778,12 +802,13 @@ client_abandon_status_confirm(Channel *c, void *ctx) | |||
778 | } | 802 | } |
779 | 803 | ||
780 | static void | 804 | static void |
781 | client_expect_confirm(int id, const char *request, int do_close) | 805 | client_expect_confirm(int id, const char *request, |
806 | enum confirm_action action) | ||
782 | { | 807 | { |
783 | struct channel_reply_ctx *cr = xmalloc(sizeof(*cr)); | 808 | struct channel_reply_ctx *cr = xmalloc(sizeof(*cr)); |
784 | 809 | ||
785 | cr->request_type = request; | 810 | cr->request_type = request; |
786 | cr->do_close = do_close; | 811 | cr->action = action; |
787 | 812 | ||
788 | channel_register_status_confirm(id, client_status_confirm, | 813 | channel_register_status_confirm(id, client_status_confirm, |
789 | client_abandon_status_confirm, cr); | 814 | client_abandon_status_confirm, cr); |
@@ -1983,7 +2008,7 @@ client_session2_setup(int id, int want_tty, int want_subsystem, | |||
1983 | memset(&ws, 0, sizeof(ws)); | 2008 | memset(&ws, 0, sizeof(ws)); |
1984 | 2009 | ||
1985 | channel_request_start(id, "pty-req", 1); | 2010 | channel_request_start(id, "pty-req", 1); |
1986 | client_expect_confirm(id, "PTY allocation", 1); | 2011 | client_expect_confirm(id, "PTY allocation", CONFIRM_TTY); |
1987 | packet_put_cstring(term != NULL ? term : ""); | 2012 | packet_put_cstring(term != NULL ? term : ""); |
1988 | packet_put_int((u_int)ws.ws_col); | 2013 | packet_put_int((u_int)ws.ws_col); |
1989 | packet_put_int((u_int)ws.ws_row); | 2014 | packet_put_int((u_int)ws.ws_row); |
@@ -2042,18 +2067,18 @@ client_session2_setup(int id, int want_tty, int want_subsystem, | |||
2042 | debug("Sending subsystem: %.*s", | 2067 | debug("Sending subsystem: %.*s", |
2043 | len, (u_char*)buffer_ptr(cmd)); | 2068 | len, (u_char*)buffer_ptr(cmd)); |
2044 | channel_request_start(id, "subsystem", 1); | 2069 | channel_request_start(id, "subsystem", 1); |
2045 | client_expect_confirm(id, "subsystem", 1); | 2070 | client_expect_confirm(id, "subsystem", CONFIRM_CLOSE); |
2046 | } else { | 2071 | } else { |
2047 | debug("Sending command: %.*s", | 2072 | debug("Sending command: %.*s", |
2048 | len, (u_char*)buffer_ptr(cmd)); | 2073 | len, (u_char*)buffer_ptr(cmd)); |
2049 | channel_request_start(id, "exec", 1); | 2074 | channel_request_start(id, "exec", 1); |
2050 | client_expect_confirm(id, "exec", 1); | 2075 | client_expect_confirm(id, "exec", CONFIRM_CLOSE); |
2051 | } | 2076 | } |
2052 | packet_put_string(buffer_ptr(cmd), buffer_len(cmd)); | 2077 | packet_put_string(buffer_ptr(cmd), buffer_len(cmd)); |
2053 | packet_send(); | 2078 | packet_send(); |
2054 | } else { | 2079 | } else { |
2055 | channel_request_start(id, "shell", 1); | 2080 | channel_request_start(id, "shell", 1); |
2056 | client_expect_confirm(id, "shell", 1); | 2081 | client_expect_confirm(id, "shell", CONFIRM_CLOSE); |
2057 | packet_send(); | 2082 | packet_send(); |
2058 | } | 2083 | } |
2059 | } | 2084 | } |
diff --git a/clientloop.h b/clientloop.h index 37d072906..ad588d14d 100644 --- a/clientloop.h +++ b/clientloop.h | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: clientloop.h,v 1.26 2011/04/17 22:42:41 djm Exp $ */ | 1 | /* $OpenBSD: clientloop.h,v 1.27 2011/05/08 12:52:01 djm Exp $ */ |
2 | 2 | ||
3 | /* | 3 | /* |
4 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 4 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
@@ -70,3 +70,5 @@ void client_register_global_confirm(global_confirm_cb *, void *); | |||
70 | void muxserver_listen(void); | 70 | void muxserver_listen(void); |
71 | void muxclient(const char *); | 71 | void muxclient(const char *); |
72 | void mux_exit_message(Channel *, int); | 72 | void mux_exit_message(Channel *, int); |
73 | void mux_tty_alloc_failed(Channel *); | ||
74 | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: mux.c,v 1.27 2011/05/06 21:34:32 djm Exp $ */ | 1 | /* $OpenBSD: mux.c,v 1.28 2011/05/08 12:52:01 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2002-2008 Damien Miller <djm@openbsd.org> | 3 | * Copyright (c) 2002-2008 Damien Miller <djm@openbsd.org> |
4 | * | 4 | * |
@@ -153,6 +153,7 @@ struct mux_master_state { | |||
153 | #define MUX_S_ALIVE 0x80000005 | 153 | #define MUX_S_ALIVE 0x80000005 |
154 | #define MUX_S_SESSION_OPENED 0x80000006 | 154 | #define MUX_S_SESSION_OPENED 0x80000006 |
155 | #define MUX_S_REMOTE_PORT 0x80000007 | 155 | #define MUX_S_REMOTE_PORT 0x80000007 |
156 | #define MUX_S_TTY_ALLOC_FAIL 0x80000008 | ||
156 | 157 | ||
157 | /* type codes for MUX_C_OPEN_FWD and MUX_C_CLOSE_FWD */ | 158 | /* type codes for MUX_C_OPEN_FWD and MUX_C_CLOSE_FWD */ |
158 | #define MUX_FWD_LOCAL 1 | 159 | #define MUX_FWD_LOCAL 1 |
@@ -1054,6 +1055,27 @@ mux_exit_message(Channel *c, int exitval) | |||
1054 | buffer_free(&m); | 1055 | buffer_free(&m); |
1055 | } | 1056 | } |
1056 | 1057 | ||
1058 | void | ||
1059 | mux_tty_alloc_failed(Channel *c) | ||
1060 | { | ||
1061 | Buffer m; | ||
1062 | Channel *mux_chan; | ||
1063 | |||
1064 | debug3("%s: channel %d: TTY alloc failed", __func__, c->self); | ||
1065 | |||
1066 | if ((mux_chan = channel_by_id(c->ctl_chan)) == NULL) | ||
1067 | fatal("%s: channel %d missing mux channel %d", | ||
1068 | __func__, c->self, c->ctl_chan); | ||
1069 | |||
1070 | /* Append exit message packet to control socket output queue */ | ||
1071 | buffer_init(&m); | ||
1072 | buffer_put_int(&m, MUX_S_TTY_ALLOC_FAIL); | ||
1073 | buffer_put_int(&m, c->self); | ||
1074 | |||
1075 | buffer_put_string(&mux_chan->output, buffer_ptr(&m), buffer_len(&m)); | ||
1076 | buffer_free(&m); | ||
1077 | } | ||
1078 | |||
1057 | /* Prepare a mux master to listen on a Unix domain socket. */ | 1079 | /* Prepare a mux master to listen on a Unix domain socket. */ |
1058 | void | 1080 | void |
1059 | muxserver_listen(void) | 1081 | muxserver_listen(void) |
@@ -1612,7 +1634,7 @@ mux_client_request_session(int fd) | |||
1612 | char *e, *term; | 1634 | char *e, *term; |
1613 | u_int i, rid, sid, esid, exitval, type, exitval_seen; | 1635 | u_int i, rid, sid, esid, exitval, type, exitval_seen; |
1614 | extern char **environ; | 1636 | extern char **environ; |
1615 | int devnull; | 1637 | int devnull, rawmode; |
1616 | 1638 | ||
1617 | debug3("%s: entering", __func__); | 1639 | debug3("%s: entering", __func__); |
1618 | 1640 | ||
@@ -1708,6 +1730,7 @@ mux_client_request_session(int fd) | |||
1708 | signal(SIGTERM, control_client_sighandler); | 1730 | signal(SIGTERM, control_client_sighandler); |
1709 | signal(SIGWINCH, control_client_sigrelay); | 1731 | signal(SIGWINCH, control_client_sigrelay); |
1710 | 1732 | ||
1733 | rawmode = tty_flag; | ||
1711 | if (tty_flag) | 1734 | if (tty_flag) |
1712 | enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); | 1735 | enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); |
1713 | 1736 | ||
@@ -1723,22 +1746,35 @@ mux_client_request_session(int fd) | |||
1723 | if (mux_client_read_packet(fd, &m) != 0) | 1746 | if (mux_client_read_packet(fd, &m) != 0) |
1724 | break; | 1747 | break; |
1725 | type = buffer_get_int(&m); | 1748 | type = buffer_get_int(&m); |
1726 | if (type != MUX_S_EXIT_MESSAGE) { | 1749 | switch (type) { |
1750 | case MUX_S_TTY_ALLOC_FAIL: | ||
1751 | if ((esid = buffer_get_int(&m)) != sid) | ||
1752 | fatal("%s: tty alloc fail on unknown session: " | ||
1753 | "my id %u theirs %u", | ||
1754 | __func__, sid, esid); | ||
1755 | leave_raw_mode(options.request_tty == | ||
1756 | REQUEST_TTY_FORCE); | ||
1757 | rawmode = 0; | ||
1758 | continue; | ||
1759 | case MUX_S_EXIT_MESSAGE: | ||
1760 | if ((esid = buffer_get_int(&m)) != sid) | ||
1761 | fatal("%s: exit on unknown session: " | ||
1762 | "my id %u theirs %u", | ||
1763 | __func__, sid, esid); | ||
1764 | if (exitval_seen) | ||
1765 | fatal("%s: exitval sent twice", __func__); | ||
1766 | exitval = buffer_get_int(&m); | ||
1767 | exitval_seen = 1; | ||
1768 | continue; | ||
1769 | default: | ||
1727 | e = buffer_get_string(&m, NULL); | 1770 | e = buffer_get_string(&m, NULL); |
1728 | fatal("%s: master returned error: %s", __func__, e); | 1771 | fatal("%s: master returned error: %s", __func__, e); |
1729 | } | 1772 | } |
1730 | if ((esid = buffer_get_int(&m)) != sid) | ||
1731 | fatal("%s: exit on unknown session: my id %u theirs %u", | ||
1732 | __func__, sid, esid); | ||
1733 | debug("%s: master session id: %u", __func__, sid); | ||
1734 | if (exitval_seen) | ||
1735 | fatal("%s: exitval sent twice", __func__); | ||
1736 | exitval = buffer_get_int(&m); | ||
1737 | exitval_seen = 1; | ||
1738 | } | 1773 | } |
1739 | 1774 | ||
1740 | close(fd); | 1775 | close(fd); |
1741 | leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); | 1776 | if (rawmode) |
1777 | leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); | ||
1742 | 1778 | ||
1743 | if (muxclient_terminate) { | 1779 | if (muxclient_terminate) { |
1744 | debug2("Exiting on signal %d", muxclient_terminate); | 1780 | debug2("Exiting on signal %d", muxclient_terminate); |