mirror of
https://github.com/tmate-io/tmate-ssh-server.git
synced 2025-03-14 12:22:46 +00:00
211 lines
5 KiB
C
211 lines
5 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);
|
|
|
|
}
|