tmate-io_tmate-ssh-server/tmate-ssh-daemon.c
2019-11-10 22:28:08 -05:00

212 lines
5.0 KiB
C

#include "tmate.h"
#include <errno.h>
#include <signal.h>
struct tmate_session _tmate_session, *tmate_session = &_tmate_session;
static void on_daemon_decoder_read(void *userdata, struct tmate_unpacker *uk)
{
struct tmate_session *session = userdata;
tmate_dispatch_daemon_message(session, uk);
tmate_send_websocket_daemon_msg(session, uk);
}
static int on_ssh_channel_read(__unused ssh_session _session,
__unused ssh_channel channel,
void *_data, uint32_t total_len,
__unused int is_stderr, void *userdata)
{
struct tmate_session *session = userdata;
char *data = _data;
size_t written = 0;
char *buf;
size_t len;
while (total_len) {
tmate_decoder_get_buffer(&session->daemon_decoder, &buf, &len);
if (len == 0)
tmate_fatal("No more room in client decoder. Message too big?");
if (len > total_len)
len = total_len;
memcpy(buf, data, len);
tmate_decoder_commit(&session->daemon_decoder, len);
total_len -= len;
written += len;
data += len;
}
return written;
}
static void on_daemon_encoder_write(void *userdata, struct evbuffer *buffer)
{
struct tmate_session *session = userdata;
ssize_t len, written;
unsigned char *buf;
for(;;) {
len = evbuffer_get_length(buffer);
if (!len)
break;
buf = evbuffer_pullup(buffer, -1);
written = ssh_channel_write(session->ssh_client.channel, buf, len);
if (written < 0) {
tmate_info("Error writing to channel: %s",
ssh_get_error(session->ssh_client.session));
request_server_termination();
break;
}
evbuffer_drain(buffer, written);
}
}
static void tmate_daemon_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);
client->channel_cb.userdata = session;
client->channel_cb.channel_data_function = on_ssh_channel_read,
ssh_set_channel_callbacks(client->channel, &client->channel_cb);
tmate_encoder_init(&session->daemon_encoder, on_daemon_encoder_write, session);
tmate_decoder_init(&session->daemon_decoder, on_daemon_decoder_read, session);
tmate_init_websocket(session, NULL);
}
static void handle_sigterm(__unused int sig)
{
request_server_termination();
}
/* We skip letters that are hard to distinguish when reading */
static char rand_tmate_token_digits[] = "abcdefghjkmnpqrstuvwxyz"
"ABCDEFGHJKLMNPQRSTUVWXYZ"
"23456789";
#define NUM_DIGITS (sizeof(rand_tmate_token_digits) - 1)
static char *get_random_token(void)
{
struct random_stream rs;
char *token = xmalloc(TMATE_TOKEN_LEN + 1);
int i;
unsigned char c;
random_stream_init(&rs);
for (i = 0; i < TMATE_TOKEN_LEN; i++) {
do {
c = *random_stream_get(&rs, 1);
} while (c >= NUM_DIGITS);
token[i] = rand_tmate_token_digits[c];
}
token[i] = 0;
return token;
}
static void create_session_ro_symlink(struct tmate_session *session)
{
char *tmp, *token, *session_ro_path;
#ifdef DEVENV
tmp = xstrdup("READONLYTOKENFORDEVENV000");
#else
tmp = get_random_token();
#endif
xasprintf(&token, "ro-%s", tmp);
free(tmp);
session->session_token_ro = token;
xasprintf(&session_ro_path, TMATE_WORKDIR "/sessions/%s",
session->session_token_ro);
unlink(session_ro_path);
if (symlink(session->session_token, session_ro_path) < 0)
tmate_fatal("Cannot create read-only symlink");
free(session_ro_path);
}
void tmate_spawn_daemon(struct tmate_session *session)
{
struct tmate_ssh_client *client = &session->ssh_client;
char *token;
#ifdef DEVENV
token = xstrdup("SUPERSECURETOKENFORDEVENV");
#else
token = get_random_token();
#endif
set_session_token(session, token);
free(token);
tmate_info("Spawning daemon ip=%s", client->ip_address);
session->tmux_socket_fd = server_create_socket();
if (session->tmux_socket_fd < 0)
tmate_fatal("Cannot create to the tmux socket");
create_session_ro_symlink(session);
/*
* Needed to initialize the database used in tty-term.c.
* We won't have access to it once in the jail.
*/
setup_ncurse(STDOUT_FILENO, "screen-256color");
tmate_daemon_init(session);
close_fds_except((int[]){session->tmux_socket_fd,
ssh_get_fd(session->ssh_client.session),
log_file ? fileno(log_file) : -1,
session->websocket_fd}, 4);
get_in_jail();
event_reinit(session->ev_base);
tmux_server_init();
signal(SIGTERM, handle_sigterm);
server_start(session->ev_base, -1, NULL);
/* never reached */
}
static void handle_session_name_options(const char *name, __unused const char *val)
{
if (tmate_has_websocket())
return;
if (!strcmp(name, "tmate-api-key") ||
!strcmp(name, "tmate-session-name") ||
!strcmp(name, "tmate-session-name-ro")) {
static bool warned;
if (!warned) {
tmate_info("Named sessions are not supported (no websocket server)");
tmate_notify("Named sessions are not supported (no websocket server)");
}
warned = true;
}
}
void tmate_hook_set_option(const char *name, const char *val)
{
tmate_hook_set_option_auth(name, val);
handle_session_name_options(name, val);
}