diff --git a/server.c b/server.c index b961faae..9c8254a4 100644 --- a/server.c +++ b/server.c @@ -84,12 +84,7 @@ server_create_socket(void) fatal("socket failed"); } -#ifdef TMATE_SLAVE - if (access(sa.sun_path, F_OK) == 0) - tmate_fatal("session exists"); -#else unlink(sa.sun_path); -#endif if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) fatal("socket failed"); diff --git a/tmate-slave.c b/tmate-slave.c index b28b7925..bdf56286 100644 --- a/tmate-slave.c +++ b/tmate-slave.c @@ -2,6 +2,16 @@ #include #include #include +#include +#include +#include +#include +#ifdef HAVE_CURSES_H +#include +#else +#include +#endif +#include #include "tmate.h" struct tmate_encoder *tmate_encoder; @@ -41,6 +51,12 @@ int main(int argc, char **argv) } log_open(debug_level, log_path); + + if ((mkdir(TMATE_WORKDIR, 0700) < 0 && errno != EEXIST) || + (mkdir(TMATE_WORKDIR "/sessions", 0700) < 0 && errno != EEXIST) || + (mkdir(TMATE_WORKDIR "/jail", 0700) < 0 && errno != EEXIST)) + tmate_fatal("Cannot prepare session in " TMATE_WORKDIR); + tmate_ssh_server_main(port); return 0; } @@ -48,7 +64,7 @@ int main(int argc, char **argv) static void set_session_token(const char *token) { tmate_session_token = xstrdup(token); - strcpy(socket_path, "/tmp/tmate-slave-"); + strcpy(socket_path, TMATE_WORKDIR "/sessions/"); strcat(socket_path, token); } @@ -62,6 +78,11 @@ static char *get_random_token(void) int i; char *token = xmalloc(TMATE_TOKEN_LEN + 1); +#if 0 + strcpy(token, "TOKENTOKENTOKENTOKENTOKEN"); + return token; +#endif + ssh_get_random(token, TMATE_TOKEN_LEN, 0); for (i = 0; i < TMATE_TOKEN_LEN; i++) token[i] = tmate_token_digits[token[i] % NUM_DIGITS]; @@ -134,6 +155,50 @@ static void close_fds_except(int *fd_to_preserve, int num_fds) } } +static void jail(void) +{ + struct passwd *pw; + uid_t uid; + gid_t gid; + + pw = getpwnam(TMATE_JAIL_USER); + if (!pw) { + tmate_fatal("Cannot get the /etc/passwd entry for %s", + TMATE_JAIL_USER); + } + uid = pw->pw_uid; + gid = pw->pw_gid; + + /* + * We are already in a new PID namespace (from the server fork). + */ + + if (chroot(TMATE_WORKDIR "/jail") < 0) + tmate_fatal("Cannot chroot()"); + + if (chdir("/") < 0) + tmate_fatal("Cannot chdir()"); + + if (setgroups(1, (gid_t[]){gid}) < 0) + tmate_fatal("Cannot setgroups()"); + + if (setresuid(uid, uid, uid) < 0) + tmate_fatal("Cannot setresuid()"); + + if (setresuid(gid, gid, gid) < 0) + tmate_fatal("Cannot setresgid()"); + + tmate_debug("Dropped priviledges to %s (%d,%d)", + TMATE_JAIL_USER, uid, gid); +} + +static void setup_ncurse(int fd, const char *name) +{ + int error; + if (setupterm(name, fd, &error) != OK) + tmate_fatal("Cannot setup terminal"); +} + static void tmate_spawn_slave_server(struct tmate_ssh_client *client) { char *token; @@ -150,8 +215,14 @@ static void tmate_spawn_slave_server(struct tmate_ssh_client *client) if (tmux_socket_fd < 0) tmate_fatal("Cannot create to the tmux socket"); + /* + * 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"); close_fds_except((int[]){tmux_socket_fd, ssh_get_fd(client->session), fileno(log_file)}, 7); + jail(); ev_base = osdep_event_init(); @@ -195,9 +266,11 @@ static void tmate_spawn_slave_client(struct tmate_ssh_client *client) dup2(slave_pty, STDOUT_FILENO); dup2(slave_pty, STDERR_FILENO); + setup_ncurse(slave_pty, "screen-256color"); close_fds_except((int[]){STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, tmux_socket_fd, ssh_get_fd(client->session), client->pty, fileno(log_file)}, 7); + jail(); ev_base = osdep_event_init(); diff --git a/tmate-ssh-client.c b/tmate-ssh-client.c index 6737b3d4..9a425d75 100644 --- a/tmate-ssh-client.c +++ b/tmate-ssh-client.c @@ -3,6 +3,8 @@ #include #include +extern int server_shutdown; + static void consume_channel(struct tmate_ssh_client *client) { char *buf; @@ -18,7 +20,8 @@ static void consume_channel(struct tmate_ssh_client *client) if (len < 0) { tmate_debug("Error reading from channel: %s", ssh_get_error(client->session)); - exit(1); + server_shutdown = 1; + break; } if (len == 0) break; @@ -35,7 +38,8 @@ static void on_session_event(struct tmate_ssh_client *client) consume_channel(client); if (!ssh_is_connected(session)) { tmate_debug("Disconnected"); - exit(1); + server_shutdown = 1; + return; } } @@ -68,7 +72,8 @@ static void flush_input_stream(struct tmate_ssh_client *client) if (written < 0) { tmate_debug("Error writing to channel: %s", ssh_get_error(client->session)); - exit(1); + server_shutdown = 1; + break; } evbuffer_drain(evb, written); diff --git a/tmate-ssh-server.c b/tmate-ssh-server.c index 3ac6866d..bb8b0d43 100644 --- a/tmate-ssh-server.c +++ b/tmate-ssh-server.c @@ -3,9 +3,11 @@ #include #include #include +#include +#include +#include #include #include -#include #include "tmate.h" @@ -160,6 +162,8 @@ static void handle_sigchld(void) int status, child_dead, child_exit_status; pid_t pid; + /* TODO cleanup the socket when the client dies */ + while ((pid = waitpid(0, &status, WNOHANG)) > 0) { child_dead = 0; @@ -208,6 +212,14 @@ static struct ssh_callbacks_struct ssh_session_callbacks = { .log_function = ssh_log_cb }; +static pid_t namespace_fork(void) +{ + /* XXX we are breaking getpid() libc cache. Bad libc. */ + unsigned long flags; + flags = CLONE_NEWPID | CLONE_NEWIPC | CLONE_NEWNS | CLONE_NEWNET; + return syscall(SYS_clone, flags | SIGCHLD, NULL, NULL, NULL); +} + void tmate_ssh_server_main(int port) { struct tmate_ssh_client _client; @@ -245,8 +257,8 @@ void tmate_ssh_server_main(int port) if (ssh_bind_accept(bind, client->session) < 0) tmate_fatal("Error accepting connection: %s", ssh_get_error(bind)); - if ((pid = fork()) < 0) - tmate_fatal("Can't fork"); + if ((pid = namespace_fork()) < 0) + tmate_fatal("Can't fork in new namespace"); if (pid) { ssh_free(client->session); diff --git a/tmate.h b/tmate.h index d3fd422f..ea434f9e 100644 --- a/tmate.h +++ b/tmate.h @@ -101,6 +101,8 @@ extern void tmate_ssh_server_main(int port); /* tmate-slave.c */ #define TMATE_TOKEN_LEN 25 +#define TMATE_WORKDIR "/tmp/tmate" +#define TMATE_JAIL_USER "nobody" extern struct tmate_encoder *tmate_encoder; extern int tmux_socket_fd; diff --git a/tty-term.c b/tty-term.c index 95cb5db7..74cd2fb1 100644 --- a/tty-term.c +++ b/tty-term.c @@ -333,6 +333,8 @@ tty_term_find(char *name, int fd, const char *overrides, char **cause) memset(term->codes, 0, sizeof term->codes); LIST_INSERT_HEAD(&tty_terms, term, entry); + /* we are in a jail, no access to files */ +#ifndef TMATE_SLAVE /* Set up curses terminal. */ if (setupterm(name, fd, &error) != OK) { switch (error) { @@ -353,6 +355,7 @@ tty_term_find(char *name, int fd, const char *overrides, char **cause) } goto error; } +#endif /* Fill in codes. */ for (i = 0; i < NTTYCODE; i++) { diff --git a/tty.c b/tty.c index 9e6a88f1..4e51f697 100644 --- a/tty.c +++ b/tty.c @@ -72,9 +72,13 @@ tty_init(struct tty *tty, struct client *c, int fd, char *term) tty->fd = fd; tty->client = c; +#ifdef TMATE_SLAVE + tty->path = NULL; +#else if ((path = ttyname(fd)) == NULL) fatalx("ttyname failed"); tty->path = xstrdup(path); +#endif tty->cstyle = 0; tty->ccolour = xstrdup("");