1
0
mirror of https://github.com/tmate-io/tmate-ssh-server.git synced 2020-11-18 19:53:51 -08:00

Passthrough for ssh commands to proxy

This commit is contained in:
Nicolas Viennot 2015-11-15 15:57:02 -05:00
parent 6172129f6f
commit e26cb1aca4
12 changed files with 253 additions and 88 deletions

View File

@ -187,6 +187,7 @@ dist_tmate_slave_SOURCES = \
tmate-slave.c \
tmate-ssh-client-pty.c \
tmate-ssh-daemon.c \
tmate-ssh-exec.c \
tmate-ssh-server.c \
tmux-bare.c \
tty-acs.c \

View File

@ -329,6 +329,7 @@ client_send_identify(int flags)
sizeof(data.ip_address));
strncpy(data.pubkey, tmate_session->ssh_client.pubkey,
sizeof(data.pubkey));
data.readonly = tmate_session->readonly;
#endif
data.flags = flags;

View File

@ -1033,6 +1033,7 @@ server_client_msg_identify(
#ifdef TMATE_SLAVE
c->ip_address = xstrdup(data->ip_address);
c->pubkey = xstrdup(data->pubkey);
c->readonly = data->readonly;
tmate_notify_client_join(tmate_session, c);
#endif
}

View File

@ -40,6 +40,14 @@ void msgpack_pack_string(msgpack_packer *pk, const char *str)
}
}
void msgpack_pack_boolean(msgpack_packer *pk, bool value)
{
if (value)
msgpack_pack_true(pk);
else
msgpack_pack_false(pk);
}
/* Copy/pasted from msgpack sources, except we include the v4 support */
int _msgpack_pack_object(msgpack_packer* pk, msgpack_object d)
{
@ -195,6 +203,24 @@ int64_t unpack_int(struct tmate_unpacker *uk)
return val;
}
bool unpack_bool(struct tmate_unpacker *uk)
{
bool val;
if (uk->argc == 0)
tmate_decoder_error();
if (uk->argv[0].type != MSGPACK_OBJECT_BOOLEAN)
tmate_decoder_error();
val = uk->argv[0].via.boolean;
uk->argv++;
uk->argc--;
return val;
}
void unpack_buffer(struct tmate_unpacker *uk, const char **buf, size_t *len)
{
if (uk->argc == 0)

View File

@ -10,6 +10,7 @@ enum tmate_control_out_msg_types {
TMATE_CTL_SNAPSHOT,
TMATE_CTL_CLIENT_JOIN,
TMATE_CTL_CLIENT_LEFT,
TMATE_CTL_EXEC,
};
/*
@ -18,8 +19,9 @@ enum tmate_control_out_msg_types {
[TMATE_CTL_DEAMON_OUT_MSG, object: msg]
[TMATE_CTL_SNAPSHOT, [[int: pane_id, [int: cur_x, int: cur_y], int: mode,
[[string: line_utf8, [int: char_attr, ...]], ...], ...], ...]]
[TMATE_CTL_CLIENT_JOIN, int: client_id, string: ip_address, string: pubkey]
[TMATE_CTL_CLIENT_JOIN, int: client_id, string: ip_address, string: pubkey, boolean: readonly]
[TMATE_CTL_CLIENT_LEFT, int: client_id]
[TMATE_CTL_EXEC, string: username, string: ip_address, string: pubkey, string: command]
*/
enum tmate_control_in_msg_types {
@ -27,6 +29,7 @@ enum tmate_control_in_msg_types {
TMATE_CTL_REQUEST_SNAPSHOT,
TMATE_CTL_PANE_KEYS,
TMATE_CTL_RESIZE,
TMATE_CTL_EXEC_RESPONSE,
};
/*
@ -34,6 +37,7 @@ enum tmate_control_in_msg_types {
[TMATE_CTL_REQUEST_SNAPSHOT, int: max_history_lines]
[TMATE_CTL_PANE_KEYS, int: pane_id, string: keys]
[TMATE_CTL_RESIZE, int: sx, int: sy] // sx == -1: no clients
[TMATE_CTL_EXEC_RESPONSE, int: exit_code, string: message]
*/
enum tmate_daemon_out_msg_types {

View File

@ -1,6 +1,7 @@
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <fcntl.h>
#include <errno.h>
#include "tmate.h"
#include "tmate-protocol.h"
@ -147,8 +148,21 @@ static void ctl_resize(struct tmate_session *session,
recalculate_sizes();
}
void tmate_dispatch_proxy_message(struct tmate_session *session,
static void ctl_ssh_exec_response(struct tmate_session *session,
struct tmate_unpacker *uk)
{
int exit_code;
char *message;
exit_code = unpack_int(uk);
message = unpack_string(uk);
tmate_dump_exec_response(session, exit_code, message);
free(message);
}
static void tmate_dispatch_proxy_message(struct tmate_session *session,
struct tmate_unpacker *uk)
{
int cmd = unpack_int(uk);
switch (cmd) {
@ -157,21 +171,38 @@ void tmate_dispatch_proxy_message(struct tmate_session *session,
dispatch(TMATE_CTL_REQUEST_SNAPSHOT, ctl_daemon_request_snapshot);
dispatch(TMATE_CTL_PANE_KEYS, ctl_pane_keys);
dispatch(TMATE_CTL_RESIZE, ctl_resize);
dispatch(TMATE_CTL_EXEC_RESPONSE, ctl_ssh_exec_response);
default: tmate_warn("Bad proxy message type: %d", cmd);
}
}
void tmate_proxy_exec(struct tmate_session *session, const char *command)
{
struct tmate_ssh_client *client = &session->ssh_client;
if (!tmate_has_proxy())
return;
pack(array, 5);
pack(int, TMATE_CTL_EXEC);
pack(string, client->username);
pack(string, client->ip_address);
pack(string, client->pubkey);
pack(string, command);
}
void tmate_notify_client_join(struct tmate_session *session,
struct client *c)
{
if (!tmate_has_proxy())
return;
pack(array, 4);
pack(array, 5);
pack(int, TMATE_CTL_CLIENT_JOIN);
pack(int, c->id);
pack(string, c->ip_address);
pack(string, c->pubkey);
pack(boolean, c->readonly);
}
void tmate_notify_client_left(struct tmate_session *session,
@ -216,13 +247,87 @@ void tmate_send_proxy_header(struct tmate_session *session)
pack(string, session->session_token_ro);
}
void tmate_init_proxy_session(struct tmate_session *session)
static void on_proxy_decoder_read(void *userdata, struct tmate_unpacker *uk)
{
struct tmate_session *session = userdata;
tmate_dispatch_proxy_message(session, uk);
}
static void on_proxy_read(struct bufferevent *bev, void *_session)
{
struct tmate_session *session = _session;
struct evbuffer *proxy_in;
ssize_t written;
char *buf;
size_t len;
proxy_in = bufferevent_get_input(session->bev_proxy);
while (evbuffer_get_length(proxy_in)) {
tmate_decoder_get_buffer(&session->proxy_decoder, &buf, &len);
if (len == 0)
tmate_fatal("No more room in client decoder. Message too big?");
written = evbuffer_remove(proxy_in, buf, len);
if (written < 0)
tmate_fatal("Cannot read proxy buffer");
tmate_decoder_commit(&session->proxy_decoder, written);
}
}
static void on_proxy_encoder_write(void *userdata, struct evbuffer *buffer)
{
struct tmate_session *session = userdata;
struct evbuffer *proxy_out;
size_t len;
proxy_out = bufferevent_get_output(session->bev_proxy);
if (evbuffer_add_buffer(proxy_out, buffer) < 0)
tmate_fatal("Cannot write to proxy buffer");
}
static void on_proxy_event_default(struct tmate_session *session, short events)
{
if (events & BEV_EVENT_EOF)
tmate_fatal("Connection to proxy closed");
if (events & BEV_EVENT_ERROR)
tmate_fatal("Connection to proxy error: %s",
evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()));
}
static void on_proxy_event(struct bufferevent *bev, short events, void *_session)
{
struct tmate_session *session = _session;
session->on_proxy_error(session, events);
}
void tmate_init_proxy(struct tmate_session *session,
on_proxy_error_cb on_proxy_error)
{
if (!tmate_has_proxy())
return;
session->proxy_sx = -1;
session->proxy_sy = -1;
/* session->proxy_fd is already connected */
session->bev_proxy = bufferevent_socket_new(ev_base, session->proxy_fd,
BEV_OPT_CLOSE_ON_FREE);
if (!session->bev_proxy)
tmate_fatal("Cannot setup socket bufferevent");
session->on_proxy_error = on_proxy_error ?: on_proxy_event_default;
bufferevent_setcb(session->bev_proxy,
on_proxy_read, NULL, on_proxy_event, session);
bufferevent_enable(session->bev_proxy, EV_READ | EV_WRITE);
tmate_encoder_init(&session->proxy_encoder, on_proxy_encoder_write, session);
tmate_decoder_init(&session->proxy_decoder, on_proxy_decoder_read, session);
}
static int _tmate_connect_to_proxy(const char *hostname, int port)

View File

@ -434,10 +434,26 @@ static void tmate_spawn_slave_pty_client(struct tmate_session *session)
exit(ret);
}
static void tmate_spawn_slave_exec(struct tmate_session *session)
{
close_fds_except((int[]){ssh_get_fd(session->ssh_client.session),
log_file ? fileno(log_file) : -1,
session->proxy_fd}, 3);
jail();
event_reinit(ev_base);
tmate_client_exec_init(session);
if (event_base_dispatch(ev_base) < 0)
tmate_fatal("Cannot run event loop");
exit(0);
}
void tmate_spawn_slave(struct tmate_session *session)
{
switch (session->ssh_client.role) {
case TMATE_ROLE_DAEMON: tmate_spawn_slave_daemon(session); break;
case TMATE_ROLE_PTY_CLIENT: tmate_spawn_slave_pty_client(session); break;
case TMATE_ROLE_EXEC: tmate_spawn_slave_exec(session); break;
}
}

View File

@ -1,58 +1,6 @@
#include "tmate.h"
#include <errno.h>
static void on_proxy_decoder_read(void *userdata, struct tmate_unpacker *uk)
{
struct tmate_session *session = userdata;
tmate_dispatch_proxy_message(session, uk);
}
static void on_proxy_read(struct bufferevent *bev, void *_session)
{
struct tmate_session *session = _session;
struct evbuffer *proxy_in;
ssize_t written;
char *buf;
size_t len;
proxy_in = bufferevent_get_input(session->bev_proxy);
while (evbuffer_get_length(proxy_in)) {
tmate_decoder_get_buffer(&session->proxy_decoder, &buf, &len);
if (len == 0)
tmate_fatal("No more room in client decoder. Message too big?");
written = evbuffer_remove(proxy_in, buf, len);
if (written < 0)
tmate_fatal("Cannot read proxy buffer");
tmate_decoder_commit(&session->proxy_decoder, written);
}
}
static void on_proxy_encoder_write(void *userdata, struct evbuffer *buffer)
{
struct tmate_session *session = userdata;
struct evbuffer *proxy_out;
size_t len;
proxy_out = bufferevent_get_output(session->bev_proxy);
if (evbuffer_add_buffer(proxy_out, buffer) < 0)
tmate_fatal("Cannot write to proxy buffer");
}
static void on_proxy_event(struct bufferevent *bev, short events, void *_session)
{
if (events & BEV_EVENT_EOF)
tmate_fatal("Connection to proxy closed");
if (events & BEV_EVENT_ERROR)
tmate_fatal("Connection to proxy error: %s",
evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()));
}
static void on_daemon_decoder_read(void *userdata, struct tmate_unpacker *uk)
{
struct tmate_session *session = userdata;
@ -117,25 +65,6 @@ static void on_daemon_encoder_write(void *userdata, struct evbuffer *buffer)
}
}
static void init_proxy(struct tmate_session *session)
{
/* session->proxy_fd is already connected */
session->bev_proxy = bufferevent_socket_new(ev_base, session->proxy_fd,
BEV_OPT_CLOSE_ON_FREE);
if (!session->bev_proxy)
tmate_fatal("Cannot setup socket bufferevent");
bufferevent_setcb(session->bev_proxy,
on_proxy_read, NULL, on_proxy_event, session);
bufferevent_enable(session->bev_proxy, EV_READ | EV_WRITE);
tmate_encoder_init(&session->proxy_encoder, on_proxy_encoder_write, session);
tmate_decoder_init(&session->proxy_decoder, on_proxy_decoder_read, session);
tmate_init_proxy_session(session);
tmate_send_proxy_header(session);
}
void tmate_daemon_init(struct tmate_session *session)
{
struct tmate_ssh_client *client = &session->ssh_client;
@ -149,6 +78,6 @@ void tmate_daemon_init(struct tmate_session *session)
tmate_encoder_init(&session->daemon_encoder, on_daemon_encoder_write, session);
tmate_decoder_init(&session->daemon_decoder, on_daemon_decoder_read, session);
if (tmate_has_proxy())
init_proxy(session);
tmate_init_proxy(session, NULL);
tmate_send_proxy_header(session);
}

37
tmate-ssh-exec.c Normal file
View File

@ -0,0 +1,37 @@
#include "tmate.h"
#include <errno.h>
#include <libssh/server.h>
void tmate_dump_exec_response(struct tmate_session *session,
int exit_code, const char *message)
{
struct tmate_ssh_client *client = &session->ssh_client;
ssh_channel_write(client->channel, message, strlen(message));
ssh_channel_request_send_exit_status(client->channel, exit_code);
ssh_channel_send_eof(client->channel);
ssh_channel_close(client->channel);
if (event_base_loopexit(ev_base, NULL) < 0)
tmate_fatal("cannot stop event loop");
}
static void on_proxy_error(struct tmate_session *session, short events)
{
tmate_warn("Lost proxy connection");
tmate_dump_exec_response(session, 1, "Internal Error\r\n");
}
void tmate_client_exec_init(struct tmate_session *session)
{
struct tmate_ssh_client *client = &session->ssh_client;
memset(&client->channel_cb, 0, sizeof(client->channel_cb));
ssh_callbacks_init(&client->channel_cb);
ssh_set_channel_callbacks(client->channel, &client->channel_cb);
tmate_init_proxy(session, on_proxy_error);
tmate_proxy_exec(session, client->exec_command);
}

View File

@ -45,6 +45,9 @@ static int shell_request(ssh_session session, ssh_channel channel,
{
struct tmate_ssh_client *client = userdata;
if (client->role)
return 1;
client->role = TMATE_ROLE_PTY_CLIENT;
return 0;
@ -55,12 +58,32 @@ static int subsystem_request(ssh_session session, ssh_channel channel,
{
struct tmate_ssh_client *client = userdata;
if (client->role)
return 1;
if (!strcmp(subsystem, "tmate"))
client->role = TMATE_ROLE_DAEMON;
return 0;
}
static int exec_request(ssh_session session, ssh_channel channel,
const char *command, void *userdata)
{
struct tmate_ssh_client *client = userdata;
if (client->role)
return 1;
if (!tmate_has_proxy())
return 1;
client->role = TMATE_ROLE_EXEC;
client->exec_command = xstrdup(command);
return 0;
}
static ssh_channel channel_open_request_cb(ssh_session session, void *userdata)
{
struct tmate_ssh_client *client = userdata;
@ -82,9 +105,10 @@ static ssh_channel channel_open_request_cb(ssh_session session, void *userdata)
memset(&client->channel_cb, 0, sizeof(client->channel_cb));
ssh_callbacks_init(&client->channel_cb);
client->channel_cb.userdata = client;
client->channel_cb.channel_pty_request_function = pty_request,
client->channel_cb.channel_shell_request_function = shell_request,
client->channel_cb.channel_subsystem_request_function = subsystem_request,
client->channel_cb.channel_pty_request_function = pty_request;
client->channel_cb.channel_shell_request_function = shell_request;
client->channel_cb.channel_subsystem_request_function = subsystem_request;
client->channel_cb.channel_exec_request_function = exec_request;
ssh_set_channel_callbacks(client->channel, &client->channel_cb);
return client->channel;

35
tmate.h
View File

@ -46,6 +46,7 @@ extern void tmate_encoder_init(struct tmate_encoder *encoder,
/* These functions deal with dual v4/v5 support through mpac_version */
extern void msgpack_pack_string(msgpack_packer *pk, const char *str);
extern void msgpack_pack_boolean(msgpack_packer *pk, bool value);
extern int _msgpack_pack_object(msgpack_packer *pk, msgpack_object d);
#define msgpack_pack_object _msgpack_pack_object
@ -73,6 +74,7 @@ struct tmate_unpacker {
extern void init_unpacker(struct tmate_unpacker *uk, msgpack_object obj);
extern void tmate_decoder_error(void);
extern int64_t unpack_int(struct tmate_unpacker *uk);
extern bool unpack_bool(struct tmate_unpacker *uk);
extern void unpack_buffer(struct tmate_unpacker *uk, const char **buf, size_t *len);
extern char *unpack_string(struct tmate_unpacker *uk);
extern void unpack_array(struct tmate_unpacker *uk, struct tmate_unpacker *nested);
@ -88,8 +90,6 @@ extern void unpack_array(struct tmate_unpacker *uk, struct tmate_unpacker *neste
extern void printflike1 tmate_notify(const char *fmt, ...);
extern void printflike2 tmate_notify_later(int timeout, const char *fmt, ...);
extern void tmate_notify_client_join(struct tmate_session *s, struct client *c);
extern void tmate_notify_client_left(struct tmate_session *s, struct client *c);
extern void tmate_client_resize(u_int sx, u_int sy);
extern void tmate_client_pane_key(int pane_id, int key);
@ -109,13 +109,19 @@ extern char *tmate_left_status, *tmate_right_status;
extern void tmate_dispatch_daemon_message(struct tmate_session *session,
struct tmate_unpacker *uk);
/* tmate-ssh-client.c */
/* tmate-ssh-daemon.c */
#define TMATE_KEYFRAME_INTERVAL_SEC 10
#define TMATE_KEYFRAME_MAX_SIZE 1024*1024
extern void tmate_daemon_init(struct tmate_session *session);
/* tmate-ssh-exec.c */
extern void tmate_dump_exec_response(struct tmate_session *session,
int exit_code, const char *message);
extern void tmate_client_exec_init(struct tmate_session *session);
/* tmate-ssh-client-pty.c */
extern void tmate_client_pty_init(struct tmate_session *session);
@ -128,6 +134,7 @@ extern void tmate_flush_pty(struct tmate_session *session);
#define TMATE_ROLE_DAEMON 1
#define TMATE_ROLE_PTY_CLIENT 2
#define TMATE_ROLE_EXEC 3
struct tmate_ssh_client {
char ip_address[64];
@ -145,6 +152,8 @@ struct tmate_ssh_client {
char *username;
char *pubkey;
char *exec_command;
struct winsize winsize_pty;
struct event ev_ssh;
@ -183,11 +192,13 @@ struct tmate_settings {
};
extern struct tmate_settings *tmate_settings;
typedef void on_proxy_error_cb(struct tmate_session *session, short events);
struct tmate_session {
struct tmate_ssh_client ssh_client;
int tmux_socket_fd;
/* only for deamon */
/* only for role deamon */
const char *session_token;
const char *session_token_ro;
@ -201,11 +212,17 @@ struct tmate_session {
struct tmate_encoder proxy_encoder;
struct tmate_decoder proxy_decoder;
u_int proxy_sx, proxy_sy;
on_proxy_error_cb *on_proxy_error;
/* only for client-pty */
/* only for role client-pty */
int pty;
struct event ev_pty;
bool readonly;
/* only for role-exec */
bool response_received;
bool response_status;
const char *response_message;
};
extern struct tmate_session *tmate_session;
@ -216,13 +233,15 @@ extern void tmate_spawn_slave(struct tmate_session *session);
/* tmate-proxy.c */
extern void tmate_dispatch_proxy_message(struct tmate_session *session,
struct tmate_unpacker *uk);
extern void tmate_proxy_exec(struct tmate_session *session, const char *command);
extern void tmate_notify_client_join(struct tmate_session *s, struct client *c);
extern void tmate_notify_client_left(struct tmate_session *s, struct client *c);
extern void tmate_send_proxy_daemon_msg(struct tmate_session *session,
struct tmate_unpacker *uk);
extern void tmate_send_proxy_header(struct tmate_session *session);
extern void tmate_init_proxy_session(struct tmate_session *session);
extern void tmate_init_proxy(struct tmate_session *session,
on_proxy_error_cb on_proxy_error);
extern int tmate_connect_to_proxy(void);
static inline bool tmate_has_proxy(void)

2
tmux.h
View File

@ -480,6 +480,7 @@ struct msg_identify_data {
#ifdef TMATE_SLAVE
char ip_address[64];
char pubkey[2048]; /* hopefully enough :) */
int readonly;
#endif
#define IDENTIFY_UTF8 0x1
@ -1389,6 +1390,7 @@ struct client {
#ifdef TMATE_SLAVE
char *ip_address;
char *pubkey;
int readonly;
#endif
};
ARRAY_DECL(clients, struct client *);