summaryrefslogtreecommitdiff
path: root/usr.bin
diff options
context:
space:
mode:
authorDamien Miller <djm@cvs.openbsd.org>2008-06-12 03:40:53 +0000
committerDamien Miller <djm@cvs.openbsd.org>2008-06-12 03:40:53 +0000
commitbbfd157de560c4048f3286118986e4c0a84940c1 (patch)
tree990e599892354af865a2fc995a0394ab5c2cf667 /usr.bin
parente55424f9a9ee9b78265dc539f08e3f7ef91ab5e3 (diff)
Enable ~ escapes for multiplex slave sessions; give each channel
its own escape state and hook the escape filters up to muxed channels. bz #1331 Mux slaves do not currently support the ~^Z and ~& escapes. NB. this change cranks the mux protocol version, so a new ssh mux client will not be able to connect to a running old ssh mux master. ok dtucker@
Diffstat (limited to 'usr.bin')
-rw-r--r--usr.bin/ssh/channels.c5
-rw-r--r--usr.bin/ssh/channels.h5
-rw-r--r--usr.bin/ssh/clientloop.c154
-rw-r--r--usr.bin/ssh/clientloop.h22
-rw-r--r--usr.bin/ssh/mux.c93
5 files changed, 191 insertions, 88 deletions
diff --git a/usr.bin/ssh/channels.c b/usr.bin/ssh/channels.c
index d9a4817a115..f0c78ac0388 100644
--- a/usr.bin/ssh/channels.c
+++ b/usr.bin/ssh/channels.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: channels.c,v 1.278 2008/06/10 04:50:25 dtucker Exp $ */
+/* $OpenBSD: channels.c,v 1.279 2008/06/12 03:40:52 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -726,7 +726,7 @@ channel_cancel_cleanup(int id)
void
channel_register_filter(int id, channel_infilter_fn *ifn,
- channel_outfilter_fn *ofn)
+ channel_outfilter_fn *ofn, void *ctx)
{
Channel *c = channel_lookup(id);
@@ -736,6 +736,7 @@ channel_register_filter(int id, channel_infilter_fn *ifn,
}
c->input_filter = ifn;
c->output_filter = ofn;
+ c->filter_ctx = ctx;
}
void
diff --git a/usr.bin/ssh/channels.h b/usr.bin/ssh/channels.h
index e3d1aa7e662..7201bed9446 100644
--- a/usr.bin/ssh/channels.h
+++ b/usr.bin/ssh/channels.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: channels.h,v 1.93 2008/06/10 04:50:25 dtucker Exp $ */
+/* $OpenBSD: channels.h,v 1.94 2008/06/12 03:40:52 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -130,6 +130,7 @@ struct Channel {
/* filter */
channel_infilter_fn *input_filter;
channel_outfilter_fn *output_filter;
+ void *filter_ctx;
/* keep boundaries */
int datagram;
@@ -194,7 +195,7 @@ void channel_request_start(int, char *, int);
void channel_register_cleanup(int, channel_callback_fn *, int);
void channel_register_open_confirm(int, channel_callback_fn *, void *);
void channel_register_filter(int, channel_infilter_fn *,
- channel_outfilter_fn *);
+ channel_outfilter_fn *, void *);
void channel_register_status_confirm(int, channel_confirm_cb *,
channel_confirm_abandon_cb *, void *);
void channel_cancel_cleanup(int);
diff --git a/usr.bin/ssh/clientloop.c b/usr.bin/ssh/clientloop.c
index 3cc7a0ed690..63ba265c49e 100644
--- a/usr.bin/ssh/clientloop.c
+++ b/usr.bin/ssh/clientloop.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: clientloop.c,v 1.194 2008/05/19 20:53:52 djm Exp $ */
+/* $OpenBSD: clientloop.c,v 1.195 2008/06/12 03:40:52 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -136,8 +136,8 @@ static int in_non_blocking_mode = 0;
/* Common data for the client loop code. */
static volatile sig_atomic_t quit_pending; /* Set non-zero to quit the loop. */
-static int escape_char; /* Escape character. */
-static int escape_pending; /* Last character was the escape character */
+static int escape_char1; /* Escape character. (proto1 only) */
+static int escape_pending1; /* Last character was an escape (proto1 only) */
static int last_was_cr; /* Last character was a newline. */
static int exit_status; /* Used to store the exit status of the command. */
static int stdin_eof; /* EOF has been encountered on standard error. */
@@ -154,6 +154,13 @@ static int session_closed = 0; /* In SSH2: login session closed. */
static void client_init_dispatch(void);
int session_ident = -1;
+/* Track escape per proto2 channel */
+struct escape_filter_ctx {
+ int escape_pending;
+ int escape_char;
+};
+
+/* Context for channel confirmation replies */
struct channel_reply_ctx {
const char *request_type;
int id, do_close;
@@ -377,8 +384,8 @@ client_check_initial_eof_on_stdin(void)
* and also process it as an escape character if
* appropriate.
*/
- if ((u_char) buf[0] == escape_char)
- escape_pending = 1;
+ if ((u_char) buf[0] == escape_char1)
+ escape_pending1 = 1;
else
buffer_append(&stdin_buffer, buf, 1);
}
@@ -805,9 +812,12 @@ out:
xfree(fwd.connect_host);
}
-/* process the characters one by one */
+/*
+ * Process the characters one by one, call with c==NULL for proto1 case.
+ */
static int
-process_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len)
+process_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr,
+ char *buf, int len)
{
char string[1024];
pid_t pid;
@@ -815,7 +825,20 @@ process_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len)
u_int i;
u_char ch;
char *s;
+ int *escape_pendingp, escape_char;
+ struct escape_filter_ctx *efc;
+ if (c == NULL) {
+ escape_pendingp = &escape_pending1;
+ escape_char = escape_char1;
+ } else {
+ if (c->filter_ctx == NULL)
+ return 0;
+ efc = (struct escape_filter_ctx *)c->filter_ctx;
+ escape_pendingp = &efc->escape_pending;
+ escape_char = efc->escape_char;
+ }
+
if (len <= 0)
return (0);
@@ -823,25 +846,43 @@ process_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len)
/* Get one character at a time. */
ch = buf[i];
- if (escape_pending) {
+ if (*escape_pendingp) {
/* We have previously seen an escape character. */
/* Clear the flag now. */
- escape_pending = 0;
+ *escape_pendingp = 0;
/* Process the escaped character. */
switch (ch) {
case '.':
/* Terminate the connection. */
- snprintf(string, sizeof string, "%c.\r\n", escape_char);
+ snprintf(string, sizeof string, "%c.\r\n",
+ escape_char);
buffer_append(berr, string, strlen(string));
- quit_pending = 1;
+ if (c && c->ctl_fd != -1) {
+ chan_read_failed(c);
+ chan_write_failed(c);
+ return 0;
+ } else
+ quit_pending = 1;
return -1;
case 'Z' - 64:
+ /* XXX support this for mux clients */
+ if (c && c->ctl_fd != -1) {
+ noescape:
+ snprintf(string, sizeof string,
+ "%c%c escape not available to "
+ "multiplexed sessions\r\n",
+ escape_char, ch);
+ buffer_append(berr, string,
+ strlen(string));
+ continue;
+ }
/* Suspend the program. */
/* Print a message to that effect to the user. */
- snprintf(string, sizeof string, "%c^Z [suspend ssh]\r\n", escape_char);
+ snprintf(string, sizeof string,
+ "%c^Z [suspend ssh]\r\n", escape_char);
buffer_append(berr, string, strlen(string));
/* Restore terminal modes and suspend. */
@@ -873,6 +914,8 @@ process_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len)
continue;
case '&':
+ if (c && c->ctl_fd != -1)
+ goto noescape;
/*
* Detach the program (continue to serve connections,
* but put in background and no more new connections).
@@ -921,27 +964,50 @@ process_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len)
continue;
case '?':
- snprintf(string, sizeof string,
+ if (c && c->ctl_fd != -1) {
+ snprintf(string, sizeof string,
"%c?\r\n\
Supported escape sequences:\r\n\
-%c. - terminate connection\r\n\
-%cB - send a BREAK to the remote system\r\n\
-%cC - open a command line\r\n\
-%cR - Request rekey (SSH protocol 2 only)\r\n\
-%c^Z - suspend ssh\r\n\
-%c# - list forwarded connections\r\n\
-%c& - background ssh (when waiting for connections to terminate)\r\n\
-%c? - this message\r\n\
-%c%c - send the escape character by typing it twice\r\n\
+ %c. - terminate session\r\n\
+ %cB - send a BREAK to the remote system\r\n\
+ %cC - open a command line\r\n\
+ %cR - Request rekey (SSH protocol 2 only)\r\n\
+ %c# - list forwarded connections\r\n\
+ %c? - this message\r\n\
+ %c%c - send the escape character by typing it twice\r\n\
(Note that escapes are only recognized immediately after newline.)\r\n",
- escape_char, escape_char, escape_char, escape_char,
- escape_char, escape_char, escape_char, escape_char,
- escape_char, escape_char, escape_char);
+ escape_char, escape_char,
+ escape_char, escape_char,
+ escape_char, escape_char,
+ escape_char, escape_char,
+ escape_char);
+ } else {
+ snprintf(string, sizeof string,
+"%c?\r\n\
+Supported escape sequences:\r\n\
+ %c. - terminate connection (and any multiplexed sessions)\r\n\
+ %cB - send a BREAK to the remote system\r\n\
+ %cC - open a command line\r\n\
+ %cR - Request rekey (SSH protocol 2 only)\r\n\
+ %c^Z - suspend ssh\r\n\
+ %c# - list forwarded connections\r\n\
+ %c& - background ssh (when waiting for connections to terminate)\r\n\
+ %c? - this message\r\n\
+ %c%c - send the escape character by typing it twice\r\n\
+(Note that escapes are only recognized immediately after newline.)\r\n",
+ escape_char, escape_char,
+ escape_char, escape_char,
+ escape_char, escape_char,
+ escape_char, escape_char,
+ escape_char, escape_char,
+ escape_char);
+ }
buffer_append(berr, string, strlen(string));
continue;
case '#':
- snprintf(string, sizeof string, "%c#\r\n", escape_char);
+ snprintf(string, sizeof string, "%c#\r\n",
+ escape_char);
buffer_append(berr, string, strlen(string));
s = channel_open_message();
buffer_append(berr, s, strlen(s));
@@ -967,7 +1033,7 @@ Supported escape sequences:\r\n\
*/
if (last_was_cr && ch == escape_char) {
/* It is. Set the flag and continue to next character. */
- escape_pending = 1;
+ *escape_pendingp = 1;
continue;
}
}
@@ -1018,7 +1084,7 @@ client_process_input(fd_set *readset)
packet_start(SSH_CMSG_EOF);
packet_send();
}
- } else if (escape_char == SSH_ESCAPECHAR_NONE) {
+ } else if (escape_char1 == SSH_ESCAPECHAR_NONE) {
/*
* Normal successful read, and no escape character.
* Just append the data to buffer.
@@ -1029,8 +1095,8 @@ client_process_input(fd_set *readset)
* Normal, successful read. But we have an escape character
* and have to process the characters one by one.
*/
- if (process_escapes(&stdin_buffer, &stdout_buffer,
- &stderr_buffer, buf, len) == -1)
+ if (process_escapes(NULL, &stdin_buffer,
+ &stdout_buffer, &stderr_buffer, buf, len) == -1)
return;
}
}
@@ -1105,13 +1171,26 @@ client_process_buffered_input_packets(void)
/* scan buf[] for '~' before sending data to the peer */
-static int
-simple_escape_filter(Channel *c, char *buf, int len)
+/* Helper: allocate a new escape_filter_ctx and fill in its escape char */
+void *
+client_new_escape_filter_ctx(int escape_char)
+{
+ struct escape_filter_ctx *ret;
+
+ ret = xmalloc(sizeof(*ret));
+ ret->escape_pending = 0;
+ ret->escape_char = escape_char;
+ return (void *)ret;
+}
+
+int
+client_simple_escape_filter(Channel *c, char *buf, int len)
{
if (c->extended_usage != CHAN_EXTENDED_WRITE)
return 0;
- return process_escapes(&c->input, &c->output, &c->extended, buf, len);
+ return process_escapes(c, &c->input, &c->output, &c->extended,
+ buf, len);
}
static void
@@ -1143,7 +1222,7 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
start_time = get_current_time();
/* Initialize variables. */
- escape_pending = 0;
+ escape_pending1 = 0;
last_was_cr = 1;
exit_status = -1;
stdin_eof = 0;
@@ -1170,7 +1249,7 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
stdout_bytes = 0;
stderr_bytes = 0;
quit_pending = 0;
- escape_char = escape_char_arg;
+ escape_char1 = escape_char_arg;
/* Initialize buffers. */
buffer_init(&stdin_buffer);
@@ -1198,9 +1277,10 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
if (compat20) {
session_ident = ssh2_chan_id;
- if (escape_char != SSH_ESCAPECHAR_NONE)
+ if (escape_char_arg != SSH_ESCAPECHAR_NONE)
channel_register_filter(session_ident,
- simple_escape_filter, NULL);
+ client_simple_escape_filter, NULL,
+ client_new_escape_filter_ctx(escape_char_arg));
if (session_ident != -1)
channel_register_cleanup(session_ident,
client_channel_closed, 0);
diff --git a/usr.bin/ssh/clientloop.h b/usr.bin/ssh/clientloop.h
index 6f8e701233b..cecbfb1a878 100644
--- a/usr.bin/ssh/clientloop.h
+++ b/usr.bin/ssh/clientloop.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: clientloop.h,v 1.19 2008/05/09 14:18:44 djm Exp $ */
+/* $OpenBSD: clientloop.h,v 1.20 2008/06/12 03:40:52 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -46,8 +46,12 @@ void client_session2_setup(int, int, int, const char *, struct termios *,
int, Buffer *, char **);
int client_request_tun_fwd(int, int, int);
+/* Escape filter for protocol 2 sessions */
+void *client_new_escape_filter_ctx(int);
+int client_simple_escape_filter(Channel *, char *, int);
+
/* Multiplexing protocol version */
-#define SSHMUX_VER 1
+#define SSHMUX_VER 2
/* Multiplexing control protocol flags */
#define SSHMUX_COMMAND_OPEN 1 /* Open new connection */
@@ -59,20 +63,6 @@ int client_request_tun_fwd(int, int, int);
#define SSHMUX_FLAG_X11_FWD (1<<2) /* Request X11 forwarding */
#define SSHMUX_FLAG_AGENT_FWD (1<<3) /* Request agent forwarding */
-/* Multiplexing routines */
-
-struct mux_session_confirm_ctx {
- int want_tty;
- int want_subsys;
- int want_x_fwd;
- int want_agent_fwd;
- Buffer cmd;
- char *term;
- struct termios tio;
- char **env;
-};
-
-/* mux.c */
void muxserver_listen(void);
int muxserver_accept_control(void);
void muxclient(const char *);
diff --git a/usr.bin/ssh/mux.c b/usr.bin/ssh/mux.c
index 020d561c659..42493a9c6d9 100644
--- a/usr.bin/ssh/mux.c
+++ b/usr.bin/ssh/mux.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mux.c,v 1.1 2008/05/09 14:18:44 djm Exp $ */
+/* $OpenBSD: mux.c,v 1.2 2008/06/12 03:40:52 djm Exp $ */
/*
* Copyright (c) 2002-2008 Damien Miller <djm@openbsd.org>
*
@@ -17,6 +17,20 @@
/* ssh session multiplexing support */
+/*
+ * TODO:
+ * 1. partial reads in muxserver_accept_control (maybe make channels
+ * from accepted connections)
+ * 2. Better signalling from master to slave, especially passing of
+ * error messages
+ * 3. Better fall-back from mux slave error to new connection.
+ * 3. Add/delete forwardings via slave
+ * 4. ExitOnForwardingFailure (after #3 obviously)
+ * 5. Maybe extension mechanisms for multi-X11/multi-agent forwarding
+ * 6. Document the mux mini-protocol somewhere.
+ * 6. Support ~^Z in mux slaves.
+ */
+
#include <sys/types.h>
#include <sys/param.h>
#include <sys/queue.h>
@@ -60,6 +74,18 @@ extern char *host;
int subsystem_flag;
extern Buffer command;
+/* Context for session open confirmation callback */
+struct mux_session_confirm_ctx {
+ int want_tty;
+ int want_subsys;
+ int want_x_fwd;
+ int want_agent_fwd;
+ Buffer cmd;
+ char *term;
+ struct termios tio;
+ char **env;
+};
+
/* fd to control socket */
int muxserver_sock = -1;
@@ -119,7 +145,7 @@ muxserver_listen(void)
/* Callback on open confirmation in mux master for a mux client session. */
static void
-client_extra_session2_setup(int id, void *arg)
+mux_session_confirm(int id, void *arg)
{
struct mux_session_confirm_ctx *cctx = arg;
const char *display;
@@ -178,7 +204,7 @@ muxserver_accept_control(void)
struct sockaddr_storage addr;
struct mux_session_confirm_ctx *cctx;
char *cmd;
- u_int i, j, len, env_len, mux_command, flags;
+ u_int i, j, len, env_len, mux_command, flags, escape_char;
uid_t euid;
gid_t egid;
int start_close = 0;
@@ -305,6 +331,7 @@ muxserver_accept_control(void)
cctx->want_x_fwd = (flags & SSHMUX_FLAG_X11_FWD) != 0;
cctx->want_agent_fwd = (flags & SSHMUX_FLAG_AGENT_FWD) != 0;
cctx->term = buffer_get_string(&m, &len);
+ escape_char = buffer_get_int(&m);
cmd = buffer_get_string(&m, &len);
buffer_init(&cctx->cmd);
@@ -390,14 +417,17 @@ muxserver_accept_control(void)
new_fd[0], new_fd[1], new_fd[2], window, packetmax,
CHAN_EXTENDED_WRITE, "client-session", /*nonblock*/0);
- /* XXX */
c->ctl_fd = client_fd;
+ if (cctx->want_tty && escape_char != 0xffffffff) {
+ channel_register_filter(c->self,
+ client_simple_escape_filter, NULL,
+ client_new_escape_filter_ctx((int)escape_char));
+ }
debug3("%s: channel_new: %d", __func__, c->self);
channel_send_open(c->self);
- channel_register_open_confirm(c->self,
- client_extra_session2_setup, cctx);
+ channel_register_open_confirm(c->self, mux_session_confirm, cctx);
return 0;
}
@@ -549,33 +579,34 @@ muxclient(const char *path)
fprintf(stderr, "Exit request sent.\r\n");
exit(0);
case SSHMUX_COMMAND_OPEN:
- /* continue below */
+ buffer_put_cstring(&m, term ? term : "");
+ if (options.escape_char == SSH_ESCAPECHAR_NONE)
+ buffer_put_int(&m, 0xffffffff);
+ else
+ buffer_put_int(&m, options.escape_char);
+ buffer_append(&command, "\0", 1);
+ buffer_put_cstring(&m, buffer_ptr(&command));
+
+ if (options.num_send_env == 0 || environ == NULL) {
+ buffer_put_int(&m, 0);
+ } else {
+ /* Pass environment */
+ num_env = 0;
+ for (i = 0; environ[i] != NULL; i++) {
+ if (env_permitted(environ[i]))
+ num_env++; /* Count */
+ }
+ buffer_put_int(&m, num_env);
+ for (i = 0; environ[i] != NULL && num_env >= 0; i++) {
+ if (env_permitted(environ[i])) {
+ num_env--;
+ buffer_put_cstring(&m, environ[i]);
+ }
+ }
+ }
break;
default:
- fatal("silly muxclient_command %d", muxclient_command);
- }
-
- /* SSHMUX_COMMAND_OPEN */
- buffer_put_cstring(&m, term ? term : "");
- buffer_append(&command, "\0", 1);
- buffer_put_cstring(&m, buffer_ptr(&command));
-
- if (options.num_send_env == 0 || environ == NULL) {
- buffer_put_int(&m, 0);
- } else {
- /* Pass environment */
- num_env = 0;
- for (i = 0; environ[i] != NULL; i++)
- if (env_permitted(environ[i]))
- num_env++; /* Count */
-
- buffer_put_int(&m, num_env);
-
- for (i = 0; environ[i] != NULL && num_env >= 0; i++)
- if (env_permitted(environ[i])) {
- num_env--;
- buffer_put_cstring(&m, environ[i]);
- }
+ fatal("unrecognised muxclient_command %d", muxclient_command);
}
if (ssh_msg_send(sock, SSHMUX_VER, &m) == -1)