diff --git a/cmd-attach-session.c b/cmd-attach-session.c index 07185737..a434d69c 100644 --- a/cmd-attach-session.c +++ b/cmd-attach-session.c @@ -21,6 +21,7 @@ #include #include "tmux.h" +#include "tmate.h" /* * Attach existing session to the current terminal. diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 9adb2146..ea9b7f4a 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -60,6 +60,10 @@ cmd_switch_client_key_binding(struct cmd *self, int key) enum cmd_retval cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) { +#ifdef TMATE_SLAVE + cmdq_error(cmdq, "client switch is not supported"); + return (CMD_RETURN_ERROR); +#else struct args *args = self->args; struct client *c; struct session *s; @@ -111,4 +115,5 @@ cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq) s->curw->flags &= ~WINLINK_ALERTFLAGS; return (CMD_RETURN_NORMAL); +#endif } diff --git a/resize.c b/resize.c index f0e9dfe0..07861081 100644 --- a/resize.c +++ b/resize.c @@ -61,6 +61,10 @@ recalculate_sizes(void) c = ARRAY_ITEM(&clients, j); if (c == NULL || c->flags & CLIENT_SUSPENDED) continue; +#ifdef TMATE_SLAVE + if (c->flags & CLIENT_READONLY) + continue; +#endif if (c->session == s) { if (c->tty.sx < ssx) ssx = c->tty.sx; diff --git a/tmate-decoder.c b/tmate-decoder.c index cd4065ac..7beb562b 100644 --- a/tmate-decoder.c +++ b/tmate-decoder.c @@ -109,6 +109,9 @@ static void tmate_header(struct tmate_unpacker *uk) if (gethostname(hostname, sizeof(hostname)) < 0) tmate_fatal("cannot get hostname"); + tmate_notify("Remote session read only: ssh ro-%s@%s.%s (clear your screen if you share this)", + tmate_session_token_ro, hostname, TMATE_DOMAIN); + tmate_notify("Remote session: ssh %s@%s.%s", tmate_session_token, hostname, TMATE_DOMAIN); } diff --git a/tmate-slave.c b/tmate-slave.c index 484ddb2a..3da63b4d 100644 --- a/tmate-slave.c +++ b/tmate-slave.c @@ -20,6 +20,7 @@ struct tmate_encoder *tmate_encoder; int tmux_socket_fd; const char *tmate_session_token = "main"; +const char *tmate_session_token_ro = "ro-main"; static char *log_path; /* NULL means stderr */ static char *cmdline; @@ -97,19 +98,6 @@ int main(int argc, char **argv, char **envp) tmate_ssh_server_main(keys_dir, port); return 0; } -static void set_session_token(struct tmate_ssh_client *client, - const char *token) -{ - tmate_session_token = xstrdup(token); - strcpy(socket_path, TMATE_WORKDIR "/sessions/"); - strcat(socket_path, token); - - memset(cmdline, 0, cmdline_end - cmdline); - sprintf(cmdline, "tmate-slave [%s] %s %s", - tmate_session_token, - client->role == TMATE_ROLE_SERVER ? "(server)" : "(client)", - client->ip_address); -} static char tmate_token_digits[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" @@ -126,11 +114,37 @@ static char *get_random_token(void) token[i] = tmate_token_digits[token[i] % NUM_DIGITS]; token[i] = 0; + return token; +} + +static void set_session_token(struct tmate_ssh_client *client, + const char *token) +{ + tmate_session_token = xstrdup(token); + strcpy(socket_path, TMATE_WORKDIR "/sessions/"); + strcat(socket_path, token); + + memset(cmdline, 0, cmdline_end - cmdline); + sprintf(cmdline, "tmate-slave [%s] %s %s", + tmate_session_token, + client->role == TMATE_ROLE_SERVER ? "(server)" : "(client)", + client->ip_address); +} +static void create_session_ro_symlink(void) +{ + char session_ro_path[MAXPATHLEN]; + + tmate_session_token_ro = get_random_token(); #ifdef DEVENV - strcpy(token, "SUPERSECURETOKENFORDEVENV"); + strcpy((char *)tmate_session_token_ro, "READONLYTOKENFORDEVENV000"); #endif - return token; + strcpy(session_ro_path, TMATE_WORKDIR "/sessions/"); + strcat(session_ro_path, tmate_session_token_ro); + + unlink(session_ro_path); + if (symlink(tmate_session_token, session_ro_path) < 0) + tmate_fatal("Cannot create read-only symlink"); } static int validate_token(const char *token) @@ -254,6 +268,10 @@ static void tmate_spawn_slave_server(struct tmate_ssh_client *client) struct tmate_decoder decoder; token = get_random_token(); +#ifdef DEVENV + strcpy(token, "SUPERSECURETOKENFORDEVENV"); +#endif + set_session_token(client, token); free(token); @@ -263,6 +281,8 @@ static void tmate_spawn_slave_server(struct tmate_ssh_client *client) if (tmux_socket_fd < 0) tmate_fatal("Cannot create to the tmux socket"); + create_session_ro_symlink(); + /* * Needed to initialize the database used in tty-term.c. * We won't have access to it once in the jail. @@ -286,11 +306,19 @@ static void tmate_spawn_slave_server(struct tmate_ssh_client *client) static void tmate_spawn_slave_client(struct tmate_ssh_client *client) { - char *argv[] = {(char *)"attach", NULL}; + char *argv_rw[] = {(char *)"attach", NULL}; + char *argv_ro[] = {(char *)"attach", (char *)"-r", NULL}; + char **argv = argv_rw; + int argc = 1; char *token = client->username; + struct stat fstat; int slave_pty; int ret; + /* the "ro-" part is just sugar, we don't care about it */ + if (!memcmp("ro-", token, 3)) + token += 3; + if (validate_token(token) < 0) { ssh_echo(client, BAD_TOKEN_ERROR_STR); tmate_fatal("Bad token"); @@ -307,6 +335,22 @@ static void tmate_spawn_slave_client(struct tmate_ssh_client *client) tmate_fatal("Expired token"); } + /* + * If we are connecting through a symlink, it means that we are a + * readonly client. + * 1) We mark the client as CLIENT_READONLY on the server + * 2) We prevent any input (aside from the window size) to go through + * to the server. + */ + client->readonly = 0; + if (lstat(socket_path, &fstat) < 0) + tmate_fatal("Cannot fstat()"); + if (S_ISLNK(fstat.st_mode)) { + client->readonly = 1; + argv = argv_ro; + argc = 2; + } + if (openpty(&client->pty, &slave_pty, NULL, NULL, NULL) < 0) tmate_fatal("Cannot allocate pty"); @@ -324,7 +368,7 @@ static void tmate_spawn_slave_client(struct tmate_ssh_client *client) tmate_ssh_client_pty_init(client); - ret = client_main(1, argv, IDENTIFY_UTF8 | IDENTIFY_256COLOURS); + ret = client_main(argc, argv, IDENTIFY_UTF8 | IDENTIFY_256COLOURS); tmate_flush_pty(client); exit(ret); } diff --git a/tmate-ssh-client-pty.c b/tmate-ssh-client-pty.c index 8277a94e..dde02ae9 100644 --- a/tmate-ssh-client-pty.c +++ b/tmate-ssh-client-pty.c @@ -26,6 +26,9 @@ static void consume_channel(struct tmate_ssh_client *client) if (len == 0) return; + if (client->readonly) + continue; + ptr = buf; setblocking(client->pty, 1); while (len > 0) { diff --git a/tmate.h b/tmate.h index ac99ccf7..aacaf701 100644 --- a/tmate.h +++ b/tmate.h @@ -102,6 +102,7 @@ struct tmate_ssh_client { /* only for client-pty */ int pty; struct event ev_pty; + int readonly; }; extern void tmate_ssh_client_init(struct tmate_ssh_client *client, struct tmate_encoder *encoder, @@ -136,6 +137,7 @@ extern void tmate_ssh_server_main(const char *keys_dir, int port); extern struct tmate_encoder *tmate_encoder; extern int tmux_socket_fd; extern const char *tmate_session_token; +extern const char *tmate_session_token_ro; extern void tmate_get_random_bytes(void *buffer, ssize_t len); extern long tmate_get_random_long(void);