Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f314123df | ||
|
|
b41672b634 | ||
|
|
36f073b4cc | ||
|
|
1c020d1f5c | ||
|
|
9dd7f0779e | ||
|
|
befd49f4e8 | ||
|
|
511fd2bd85 | ||
|
|
fb373b6c2f | ||
|
|
afd81e9521 | ||
|
|
a693c27ef4 | ||
|
|
1d0743c5d5 | ||
|
|
ae28014643 | ||
|
|
d7a7c271dc | ||
|
|
43b390b704 | ||
|
|
97d20249be | ||
|
|
f0c32a14a4 | ||
|
|
54ea774265 | ||
|
|
cb4c7a874d | ||
|
|
43214648f2 | ||
|
|
e1a5e08471 | ||
|
|
ecc15f6022 | ||
|
|
f132989294 | ||
|
|
90c3b571d3 | ||
|
|
1d26957b3b | ||
|
|
b97f8dc7d6 | ||
|
|
7f26d860b5 | ||
|
|
975f60d2ac | ||
|
|
4b79571357 | ||
|
|
6c41248dad | ||
|
|
7c1bc239af | ||
|
|
e71bd675b5 | ||
|
|
48884c95c9 | ||
|
|
cd3ee3ee4d | ||
|
|
6a951aa9f7 | ||
|
|
975ba3323b | ||
|
|
566a8bc412 | ||
|
|
dc05cbc4f5 | ||
|
|
48b5c6afba | ||
|
|
383b69f04c | ||
|
|
fde93450dc | ||
|
|
eb08daec79 | ||
|
|
314bf91299 | ||
|
|
9c3cc0e264 | ||
|
|
5c2c1c2584 | ||
|
|
2bd232c982 | ||
|
|
9df2e6d6fb | ||
|
|
6c5497df4b | ||
|
|
07dc84de98 | ||
|
|
7f9378de43 | ||
|
|
080be74a75 | ||
|
|
ebbafd794f | ||
|
|
6397e825bd | ||
|
|
e568b91f04 | ||
|
|
4555d8d6fb | ||
|
|
07bea82ab2 | ||
|
|
6a271c5a89 | ||
|
|
004fe379f6 | ||
|
|
4b20aaf66e | ||
|
|
ba964e279c | ||
|
|
b2dc743aa6 | ||
|
|
bd65eb3c48 | ||
|
|
46f821f88c | ||
|
|
55d481a633 |
+48
-24
@@ -1,31 +1,55 @@
|
||||
FROM alpine:3.9
|
||||
FROM alpine:3.13 AS build
|
||||
|
||||
RUN apk add --no-cache msgpack-c ncurses-libs libevent libexecinfo libressl zlib
|
||||
RUN apk add --no-cache msgpack-c ncurses-libs libevent libexecinfo openssl zlib
|
||||
|
||||
RUN apk add --no-cache \
|
||||
autoconf \
|
||||
automake \
|
||||
cmake \
|
||||
g++ \
|
||||
gcc \
|
||||
git \
|
||||
libevent-dev \
|
||||
libexecinfo-dev \
|
||||
linux-headers \
|
||||
make \
|
||||
msgpack-c-dev \
|
||||
ncurses-dev \
|
||||
openssl-dev \
|
||||
zlib-dev
|
||||
|
||||
RUN apk add --no-cache libssh-dev
|
||||
|
||||
RUN mkdir -p /src/tmate-ssh-server
|
||||
COPY . /src/tmate-ssh-server
|
||||
|
||||
RUN set -ex; \
|
||||
apk add --no-cache --virtual .build-deps \
|
||||
git wget cmake make gcc g++ linux-headers zlib-dev libressl-dev \
|
||||
automake autoconf libevent-dev ncurses-dev msgpack-c-dev libexecinfo-dev; \
|
||||
\
|
||||
mkdir -p /src/libssh/build; \
|
||||
cd /src; \
|
||||
wget -O libssh.tar.xz https://www.libssh.org/files/0.8/libssh-0.8.7.tar.xz; \
|
||||
tar -xf libssh.tar.xz -C /src/libssh --strip-components=1; \
|
||||
cd /src/libssh/build; \
|
||||
cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr -DWITH_SFTP=OFF ..; \
|
||||
make -j "$(nproc)"; \
|
||||
make install ;\
|
||||
\
|
||||
mkdir -p /src/; \
|
||||
cd /src/; \
|
||||
git clone https://github.com/tmate-io/tmate-ssh-server.git; \
|
||||
cd tmate-ssh-server; \
|
||||
cd /src/tmate-ssh-server; \
|
||||
./autogen.sh; \
|
||||
./configure --prefix=/usr CFLAGS="-D_GNU_SOURCE"; \
|
||||
make -j "$(nproc)"; \
|
||||
make install ;\
|
||||
rm -rf /src; \
|
||||
apk del .build-deps
|
||||
make install
|
||||
|
||||
EXPOSE 22
|
||||
ENTRYPOINT ["tmate-ssh-server"]
|
||||
### Minimal run-time image
|
||||
FROM alpine:3.13
|
||||
|
||||
RUN apk add --no-cache \
|
||||
bash \
|
||||
gdb \
|
||||
libevent \
|
||||
libexecinfo \
|
||||
libssh \
|
||||
msgpack-c \
|
||||
ncurses-libs \
|
||||
openssl \
|
||||
zlib
|
||||
|
||||
COPY --from=build /usr/lib/libssh.so.* /usr/lib/
|
||||
COPY --from=build /usr/bin/tmate-ssh-server /usr/bin/
|
||||
|
||||
# TODO not run as root. Instead, use capabilities.
|
||||
|
||||
COPY docker-entrypoint.sh /usr/local/bin
|
||||
|
||||
EXPOSE 2200
|
||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
FROM alpine:3.13
|
||||
|
||||
RUN apk add --no-cache msgpack-c ncurses-libs libevent libexecinfo openssl zlib
|
||||
|
||||
RUN set -ex; \
|
||||
apk add --no-cache --virtual .build-deps \
|
||||
autoconf \
|
||||
automake \
|
||||
cmake \
|
||||
g++ \
|
||||
gcc \
|
||||
git \
|
||||
libevent-dev \
|
||||
libexecinfo-dev \
|
||||
linux-headers \
|
||||
make \
|
||||
msgpack-c-dev \
|
||||
ncurses-dev \
|
||||
openssl-dev \
|
||||
zlib-dev
|
||||
|
||||
RUN set -ex; \
|
||||
apk add --no-cache libssh-dev
|
||||
|
||||
WORKDIR /src/tmate-ssh-server
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN set -ex; \
|
||||
./autogen.sh; \
|
||||
./configure --prefix=/usr CFLAGS="-D_GNU_SOURCE"; \
|
||||
make -j "$(nproc)"; \
|
||||
ln -s /src/tmate-ssh-server/tmate-ssh-server /usr/bin
|
||||
|
||||
COPY docker-entrypoint.sh /usr/local/bin
|
||||
|
||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||
+10
-9
@@ -25,10 +25,6 @@ if IS_DEVENV
|
||||
CFLAGS += -DDEVENV
|
||||
endif
|
||||
|
||||
if IS_LATENCY
|
||||
CFLAGS += -DENABLE_LATENCY
|
||||
endif
|
||||
|
||||
if IS_LINUX
|
||||
CFLAGS += -rdynamic # for stack traces
|
||||
endif
|
||||
@@ -41,8 +37,8 @@ CFLAGS += -g
|
||||
CFLAGS += -Wno-long-long -Wall -W -Wnested-externs -Wformat=2
|
||||
CFLAGS += -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations
|
||||
CFLAGS += -Wwrite-strings -Wshadow -Wpointer-arith -Wsign-compare
|
||||
CFLAGS += -Wundef -Wbad-function-cast -Winline -Wcast-align
|
||||
CFLAGS += -Wdeclaration-after-statement -Wno-pointer-sign -Wno-attributes
|
||||
CFLAGS += -Wundef -Wbad-function-cast -Winline
|
||||
CFLAGS += -Wno-pointer-sign -Wno-attributes
|
||||
CPPFLAGS += -DDEBUG
|
||||
endif
|
||||
if IS_COVERAGE
|
||||
@@ -52,6 +48,9 @@ endif
|
||||
CPPFLAGS += -iquote.
|
||||
endif
|
||||
|
||||
CFLAGS += -Wno-unused-parameter -Wno-unused-variable -Wno-null-pointer-arithmetic
|
||||
CFLAGS += -Wno-format-nonliteral
|
||||
|
||||
# Set flags for Solaris.
|
||||
if IS_SUNOS
|
||||
if IS_GCC
|
||||
@@ -179,18 +178,20 @@ dist_tmate_ssh_server_SOURCES = \
|
||||
signal.c \
|
||||
status.c \
|
||||
style.c \
|
||||
tmate-debug.c \
|
||||
tmate-auth-keys.c \
|
||||
tmate-daemon-decoder.c \
|
||||
tmate-daemon-encoder.c \
|
||||
tmate-daemon-legacy.c \
|
||||
tmate-msgpack.c \
|
||||
tmate-proxy.c \
|
||||
tmate-debug.c \
|
||||
tmate-main.c \
|
||||
tmate-msgpack.c \
|
||||
tmate-rand.c \
|
||||
tmate-ssh-client-pty.c \
|
||||
tmate-ssh-daemon.c \
|
||||
tmate-ssh-exec.c \
|
||||
tmate-ssh-latency.c \
|
||||
tmate-ssh-server.c \
|
||||
tmate-websocket.c \
|
||||
tmux.c \
|
||||
tty-acs.c \
|
||||
tty-keys.c \
|
||||
|
||||
@@ -8,11 +8,27 @@ Usage
|
||||
|
||||
See on [tmate.io](http://tmate.io/).
|
||||
|
||||
Contributions
|
||||
-------------
|
||||
Docker image
|
||||
-----------
|
||||
|
||||
* Chef cookbook by [JJ Asghar](https://github.com/jjasghar): [https://github.com/jjasghar/chef-tmate-slave](https://github.com/jjasghar/chef-tmate-slave)
|
||||
* Custom host:port support by [Paolo Mainardi](https://github.com/paolomainardi)
|
||||
A docker image is provided:
|
||||
[`tmate/tmate-ssh-server`](https://hub.docker.com/r/tmate/tmate-ssh-server)
|
||||
|
||||
The following environment variables are used to configure the server:
|
||||
|
||||
* `SSH_KEYS_PATH` (mandatory): The path where the ssh keys are located.
|
||||
* `HAS_WEBSOCKET`: set to `1` if the tmate-websocket server exists (for HTML5
|
||||
clients).
|
||||
* `USE_PROXY_PROTOCOL`: set to `1` if the ssh server is behind a load balancer
|
||||
that uses the [proxy protocol](http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) enabled.
|
||||
This is useful to get client real IPs.
|
||||
* `SSH_HOSTNAME`: configures the SSH hostname to advertise to tmate hosts.
|
||||
* `SSH_PORT_LISTEN`: port on which the SSH server should listen on.
|
||||
* `SSH_PORT_ADVERTISE`: configures the SSH port to advertise to tmate hosts.
|
||||
Defaults to `SSH_PORT_LISTEN`.
|
||||
|
||||
Note that you need to add the *SYS_ADMIN* capability to the container.
|
||||
This is needed to create nested containers (namespaces) to secure sessions.
|
||||
|
||||
License
|
||||
--------
|
||||
|
||||
@@ -64,9 +64,6 @@ void client_dispatch(struct imsg *, void *);
|
||||
void client_dispatch_attached(struct imsg *);
|
||||
void client_dispatch_wait(struct imsg *, const char *);
|
||||
const char *client_exit_message(void);
|
||||
#ifdef TMATE_SLAVE
|
||||
void client_report_latency(int latency_ms);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Get server create lock. If already held then server start is happening in
|
||||
@@ -230,7 +227,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags,
|
||||
struct termios tio, saved_tio;
|
||||
size_t size;
|
||||
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
/* Ignore SIGCHLD now or daemon() in the server will leave a zombie. */
|
||||
signal(SIGCHLD, SIG_IGN);
|
||||
#endif
|
||||
@@ -267,7 +264,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags,
|
||||
cmd_list_free(cmdlist);
|
||||
}
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
fd = tmate_session->tmux_socket_fd;
|
||||
|
||||
/* Create client process structure (starts logging). */
|
||||
@@ -311,7 +308,7 @@ client_main(struct event_base *base, int argc, char **argv, int flags,
|
||||
fatal("pledge failed");
|
||||
#endif
|
||||
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
/* Free stuff that is not used in the client. */
|
||||
options_free(global_options);
|
||||
options_free(global_s_options);
|
||||
@@ -404,21 +401,25 @@ client_send_identify(const char *ttynam, const char *cwd)
|
||||
int fd, flags = client_flags;
|
||||
pid_t pid;
|
||||
|
||||
proc_send(client_peer, MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags);
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
proc_send(client_peer, MSG_IDENTIFY_TMATE_IP_ADDRESS, -1,
|
||||
tmate_session->ssh_client.ip_address,
|
||||
strlen(tmate_session->ssh_client.ip_address) + 1);
|
||||
|
||||
proc_send(client_peer, MSG_IDENTIFY_TMATE_PUBKEY, -1,
|
||||
tmate_session->ssh_client.pubkey,
|
||||
strlen(tmate_session->ssh_client.pubkey) + 1);
|
||||
#ifdef TMATE
|
||||
if (tmate_session->ssh_client.pubkey) {
|
||||
proc_send(client_peer, MSG_IDENTIFY_TMATE_AUTH_PUBKEY, -1,
|
||||
tmate_session->ssh_client.pubkey,
|
||||
strlen(tmate_session->ssh_client.pubkey) + 1);
|
||||
} else {
|
||||
proc_send(client_peer, MSG_IDENTIFY_TMATE_AUTH_NONE, -1, NULL, 0);
|
||||
}
|
||||
|
||||
proc_send(client_peer, MSG_IDENTIFY_TMATE_READONLY,
|
||||
-1, &tmate_session->readonly, 1);
|
||||
|
||||
proc_send(client_peer, MSG_IDENTIFY_TMATE_IP_ADDRESS, -1,
|
||||
tmate_session->ssh_client.ip_address,
|
||||
strlen(tmate_session->ssh_client.ip_address) + 1);
|
||||
#endif
|
||||
|
||||
proc_send(client_peer, MSG_IDENTIFY_FLAGS, -1, &flags, sizeof flags);
|
||||
|
||||
if ((s = getenv("TERM")) == NULL)
|
||||
s = "";
|
||||
proc_send(client_peer, MSG_IDENTIFY_TERM, -1, s, strlen(s) + 1);
|
||||
@@ -647,7 +648,7 @@ client_dispatch_wait(struct imsg *imsg, const char *shellcmd)
|
||||
if (datalen == 0 || data[datalen - 1] != '\0')
|
||||
fatalx("bad MSG_SHELL string");
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
exit(1);
|
||||
#else
|
||||
clear_signals(0);
|
||||
@@ -661,6 +662,8 @@ client_dispatch_wait(struct imsg *imsg, const char *shellcmd)
|
||||
case MSG_EXITED:
|
||||
proc_exit(client_proc);
|
||||
break;
|
||||
case MSG_TMATE_AUTH_STATUS:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -731,10 +734,3 @@ client_dispatch_attached(struct imsg *imsg)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
void client_report_latency(int latency_ms)
|
||||
{
|
||||
proc_send(client_peer, MSG_LATENCY, -1, &latency_ms, sizeof(latency_ms));
|
||||
}
|
||||
#endif
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ const struct cmd_entry cmd_break_pane_entry = {
|
||||
enum cmd_retval
|
||||
cmd_break_pane_exec(struct cmd *self, struct cmd_q *cmdq)
|
||||
{
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
return (CMD_RETURN_ERROR);
|
||||
#else
|
||||
struct args *args = self->args;
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmd_q *cmdq)
|
||||
enum cmd_retval
|
||||
join_pane(struct cmd *self, struct cmd_q *cmdq, int not_same_window)
|
||||
{
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
return (CMD_RETURN_ERROR);
|
||||
#else
|
||||
struct args *args = self->args;
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@ const struct cmd_entry cmd_kill_pane_entry = {
|
||||
enum cmd_retval
|
||||
cmd_kill_pane_exec(struct cmd *self, struct cmd_q *cmdq)
|
||||
{
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
return (CMD_RETURN_ERROR);
|
||||
#else
|
||||
struct winlink *wl = cmdq->state.tflag.wl;
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@ const struct cmd_entry cmd_paste_buffer_entry = {
|
||||
enum cmd_retval
|
||||
cmd_paste_buffer_exec(struct cmd *self, struct cmd_q *cmdq)
|
||||
{
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
return (CMD_RETURN_ERROR);
|
||||
#else
|
||||
struct args *args = self->args;
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ const struct cmd_entry cmd_pipe_pane_entry = {
|
||||
enum cmd_retval
|
||||
cmd_pipe_pane_exec(struct cmd *self, struct cmd_q *cmdq)
|
||||
{
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
return (CMD_RETURN_ERROR);
|
||||
#else
|
||||
struct args *args = self->args;
|
||||
|
||||
+1
-1
@@ -191,7 +191,7 @@ cmdq_continue_one(struct cmd_q *cmdq)
|
||||
char *tmp;
|
||||
int flags = !!(cmd->flags & CMD_CONTROL);
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
int client_id;
|
||||
if (!tmate_should_exec_cmd_locally(cmd->entry)) {
|
||||
client_id = cmdq->client ? cmdq->client->id : -1;
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ const struct cmd_entry cmd_resize_pane_entry = {
|
||||
enum cmd_retval
|
||||
cmd_resize_pane_exec(struct cmd *self, struct cmd_q *cmdq)
|
||||
{
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
return (CMD_RETURN_ERROR);
|
||||
#else
|
||||
struct args *args = self->args;
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ const struct cmd_entry cmd_respawn_pane_entry = {
|
||||
enum cmd_retval
|
||||
cmd_respawn_pane_exec(struct cmd *self, struct cmd_q *cmdq)
|
||||
{
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
return (CMD_RETURN_ERROR);
|
||||
#else
|
||||
struct args *args = self->args;
|
||||
|
||||
@@ -45,7 +45,7 @@ const struct cmd_entry cmd_respawn_window_entry = {
|
||||
enum cmd_retval
|
||||
cmd_respawn_window_exec(struct cmd *self, struct cmd_q *cmdq)
|
||||
{
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
return (CMD_RETURN_ERROR);
|
||||
#else
|
||||
struct args *args = self->args;
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ const struct cmd_entry cmd_rotate_window_entry = {
|
||||
enum cmd_retval
|
||||
cmd_rotate_window_exec(struct cmd *self, struct cmd_q *cmdq)
|
||||
{
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
return (CMD_RETURN_ERROR);
|
||||
#else
|
||||
struct winlink *wl = cmdq->state.tflag.wl;
|
||||
|
||||
+1
-1
@@ -70,7 +70,7 @@ const struct cmd_entry cmd_previous_layout_entry = {
|
||||
enum cmd_retval
|
||||
cmd_select_layout_exec(struct cmd *self, struct cmd_q *cmdq)
|
||||
{
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
return (CMD_RETURN_ERROR);
|
||||
#else
|
||||
struct args *args = self->args;
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@ const struct cmd_entry cmd_send_prefix_entry = {
|
||||
enum cmd_retval
|
||||
cmd_send_keys_exec(struct cmd *self, struct cmd_q *cmdq)
|
||||
{
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
return (CMD_RETURN_ERROR);
|
||||
#else
|
||||
struct args *args = self->args;
|
||||
|
||||
+7
-1
@@ -22,6 +22,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "tmux.h"
|
||||
#include "tmate.h"
|
||||
|
||||
/*
|
||||
* Set an option.
|
||||
@@ -113,6 +114,11 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq)
|
||||
else
|
||||
valstr = args->argv[1];
|
||||
|
||||
#ifdef TMATE
|
||||
if (!args_has(args, 'u'))
|
||||
tmate_hook_set_option(optstr, valstr);
|
||||
#endif
|
||||
|
||||
/* Is this a user option? */
|
||||
if (*optstr == '@')
|
||||
return (cmd_set_option_user(self, cmdq, optstr, valstr));
|
||||
@@ -185,7 +191,7 @@ cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq)
|
||||
}
|
||||
|
||||
/* Start or stop timers if necessary. */
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
if (strcmp(oe->name, "automatic-rename") == 0) {
|
||||
RB_FOREACH(w, windows, &windows) {
|
||||
if (options_get_number(w->options, "automatic-rename"))
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ const struct cmd_entry cmd_split_window_entry = {
|
||||
enum cmd_retval
|
||||
cmd_split_window_exec(struct cmd *self, struct cmd_q *cmdq)
|
||||
{
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
return (CMD_RETURN_ERROR);
|
||||
#else
|
||||
struct args *args = self->args;
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ const struct cmd_entry cmd_swap_pane_entry = {
|
||||
enum cmd_retval
|
||||
cmd_swap_pane_exec(struct cmd *self, struct cmd_q *cmdq)
|
||||
{
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
return (CMD_RETURN_ERROR);
|
||||
#else
|
||||
struct winlink *src_wl, *dst_wl;
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ const struct cmd_entry cmd_switch_client_entry = {
|
||||
enum cmd_retval
|
||||
cmd_switch_client_exec(struct cmd *self, struct cmd_q *cmdq)
|
||||
{
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
cmdq_error(cmdq, "client switch is not supported");
|
||||
return (CMD_RETURN_ERROR);
|
||||
#else
|
||||
|
||||
+3
-1
@@ -52,6 +52,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "compat.h"
|
||||
|
||||
#define Assert(Cond) if (!(Cond)) abort()
|
||||
|
||||
static const char Base64[] =
|
||||
@@ -122,7 +124,7 @@ static const char Pad64 = '=';
|
||||
*/
|
||||
|
||||
int
|
||||
b64_ntop(uint8_t const *src, size_t srclength, char *target, size_t targsize) {
|
||||
b64_ntop(const char *src, size_t srclength, char *target, size_t targsize) {
|
||||
size_t datalength = 0;
|
||||
uint8_t input[3];
|
||||
uint8_t output[4];
|
||||
|
||||
+3
-7
@@ -2,6 +2,7 @@
|
||||
|
||||
AC_INIT(tmate-ssh-server, 2.3.0)
|
||||
|
||||
AM_SILENT_RULES([yes])
|
||||
AC_CONFIG_AUX_DIR(etc)
|
||||
AM_INIT_AUTOMAKE([foreign subdir-objects])
|
||||
|
||||
@@ -23,13 +24,6 @@ PKG_PROG_PKG_CONFIG
|
||||
# Default tmux.conf goes in /etc not ${prefix}/etc.
|
||||
test "$sysconfdir" = '${prefix}/etc' && sysconfdir=/etc
|
||||
|
||||
AC_ARG_ENABLE(
|
||||
latency,
|
||||
AC_HELP_STRING(--enable-latency, "enable latency (requires libssh head)"),
|
||||
found_latency=$enable_latency
|
||||
)
|
||||
AM_CONDITIONAL(IS_LATENCY, test "x$found_latency" = xyes)
|
||||
|
||||
AC_ARG_ENABLE(
|
||||
devenv,
|
||||
AC_HELP_STRING(--enable-devenv, "dev env (port 2200, no random tokens)"),
|
||||
@@ -107,6 +101,7 @@ AC_CHECK_HEADERS(
|
||||
inttypes.h \
|
||||
libutil.h \
|
||||
ncurses.h \
|
||||
execinfo.h \
|
||||
ndir.h \
|
||||
paths.h \
|
||||
pty.h \
|
||||
@@ -128,6 +123,7 @@ AC_CHECK_FUNCS(
|
||||
[ \
|
||||
dirfd \
|
||||
flock \
|
||||
backtrace \
|
||||
setproctitle \
|
||||
sysconf \
|
||||
cfmakeraw \
|
||||
|
||||
+1
-1
@@ -80,7 +80,7 @@ control_notify_window_layout_changed(struct window *w)
|
||||
if (winlink_find_by_window_id(&s->windows, w->id) == NULL)
|
||||
continue;
|
||||
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
/*
|
||||
* When the last pane in a window is closed it won't have a
|
||||
* layout root and we don't need to inform the client about the
|
||||
|
||||
+6
-6
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
set -eu
|
||||
|
||||
gen_key() {
|
||||
@@ -6,9 +6,10 @@ gen_key() {
|
||||
ks="${keytype}_"
|
||||
key="keys/ssh_host_${ks}key"
|
||||
if [ ! -e "${key}" ] ; then
|
||||
ssh-keygen -t ${keytype} -f "${key}" -N ''
|
||||
SIG=$(ssh-keygen -l -E SHA256 -f $key.pub | cut -d ' ' -f 2)
|
||||
ssh-keygen -t "${keytype}" -f "${key}" -N ''
|
||||
echo ""
|
||||
fi
|
||||
SIG=$(ssh-keygen -l -E SHA256 -f "$key.pub" | cut -d ' ' -f 2)
|
||||
}
|
||||
|
||||
mkdir -p keys
|
||||
@@ -18,10 +19,9 @@ gen_key ed25519
|
||||
ED25519_SIG=$SIG
|
||||
|
||||
|
||||
echo ""
|
||||
echo "You may use the following settings this in your .tmate.conf:"
|
||||
echo ""
|
||||
echo "set -g tmate-server-host localhost"
|
||||
echo "set -g tmate-server-port 22"
|
||||
echo "set -g tmate-server-rsa-fingerprint \"$RSA_SIG\""
|
||||
echo "set -g tmate-server-ed25519-fingerprint \"$ED25519_SIG\""
|
||||
echo "set -g tmate-server-rsa-fingerprint $RSA_SIG"
|
||||
echo "set -g tmate-server-ed25519-fingerprint $ED25519_SIG"
|
||||
|
||||
Executable
+24
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
if [ "${USE_PROXY_PROTOCOL}" -eq "1" ]; then
|
||||
set -- -x "$@"
|
||||
fi
|
||||
|
||||
if [ "${HAS_WEBSOCKET}" -eq "1" ]; then
|
||||
set -- -w localhost "$@"
|
||||
fi
|
||||
|
||||
if [ -n "${WEBSOCKET_HOSTNAME}" ]; then
|
||||
set -- -w "${WEBSOCKET_HOSTNAME}" "$@"
|
||||
fi
|
||||
|
||||
if [ ! -z "${SSH_HOSTNAME}" ]; then
|
||||
set -- -h "${SSH_HOSTNAME}" "$@"
|
||||
fi
|
||||
|
||||
SSH_PORT_LISTEN=${SSH_PORT_LISTEN:-2200}
|
||||
SSH_PORT_ADVERTIZE=${SSH_PORT_ADVERTIZE:-${SSH_PORT_LISTEN}}
|
||||
SSH_PORT_ADVERTISE=${SSH_PORT_ADVERTISE:-${SSH_PORT_ADVERTIZE}}
|
||||
|
||||
exec tmate-ssh-server -p ${SSH_PORT_LISTEN} -q ${SSH_PORT_ADVERTISE} -k ${SSH_KEYS_PATH} "$@"
|
||||
@@ -361,7 +361,7 @@ format_cb_window_layout(struct format_tree *ft, struct format_entry *fe)
|
||||
if (w == NULL)
|
||||
return;
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
fe->value = xstrdup("no layout");
|
||||
#else
|
||||
if (w->saved_layout_root != NULL)
|
||||
@@ -380,14 +380,14 @@ format_cb_window_visible_layout(struct format_tree *ft, struct format_entry *fe)
|
||||
if (w == NULL)
|
||||
return;
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
fe->value = xstrdup("no layout");
|
||||
#else
|
||||
fe->value = layout_dump(w->layout_root);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
/* Callback for pane_start_command. */
|
||||
void
|
||||
format_cb_start_command(struct format_tree *ft, struct format_entry *fe)
|
||||
@@ -1187,7 +1187,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
|
||||
format_add(ft, "pane_active", "%d", wp == wp->window->active);
|
||||
format_add(ft, "pane_input_off", "%d", !!(wp->flags & PANE_INPUTOFF));
|
||||
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
status = wp->status;
|
||||
if (wp->fd == -1 && WIFEXITED(status))
|
||||
format_add(ft, "pane_dead_status", "%d", WEXITSTATUS(status));
|
||||
@@ -1207,7 +1207,7 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
|
||||
|
||||
format_add(ft, "pane_tty", "%s", wp->tty);
|
||||
format_add(ft, "pane_pid", "%ld", (long) wp->pid);
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
format_add_cb(ft, "pane_start_command", format_cb_start_command);
|
||||
format_add_cb(ft, "pane_current_command", format_cb_current_command);
|
||||
format_add_cb(ft, "pane_current_path", format_cb_current_path);
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
|
||||
/*
|
||||
* This file is rather misleadingly named, it contains the code which takes a
|
||||
|
||||
@@ -839,7 +839,7 @@ input_parse(struct window_pane *wp)
|
||||
{
|
||||
struct input_ctx *ictx = wp->ictx;
|
||||
const struct input_transition *itr;
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
struct evbuffer *evb = wp->event_input;
|
||||
#else
|
||||
struct evbuffer *evb = wp->event->input;
|
||||
@@ -961,7 +961,7 @@ input_get(struct input_ctx *ictx, u_int validx, int minval, int defval)
|
||||
void
|
||||
input_reply(struct input_ctx *ictx, const char *fmt, ...)
|
||||
{
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
va_list ap;
|
||||
char *reply;
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ job_run(const char *cmd, struct session *s, const char *cwd,
|
||||
int nullfd, out[2];
|
||||
const char *home;
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
return NULL;
|
||||
#endif
|
||||
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
|
||||
struct layout_cell *layout_find_bottomright(struct layout_cell *);
|
||||
u_short layout_checksum(const char *);
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
|
||||
/*
|
||||
* Set window layouts - predefined methods to arrange windows. These are
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
|
||||
/*
|
||||
* The window layout is a tree of cells each of which can be one of: a
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -31,16 +30,11 @@
|
||||
|
||||
FILE *log_file;
|
||||
|
||||
struct logging_settings {
|
||||
const char *program_name;
|
||||
bool use_syslog;
|
||||
int log_level;
|
||||
};
|
||||
|
||||
static struct logging_settings log_settings;
|
||||
|
||||
static void log_event_cb(int, const char *);
|
||||
static void log_vwrite(int, const char *, va_list);
|
||||
static void log_vwrite(const char *, va_list);
|
||||
|
||||
static int log_level;
|
||||
char *log_prefix;
|
||||
|
||||
static void
|
||||
log_event_cb(__unused int severity, const char *msg)
|
||||
@@ -52,75 +46,74 @@ log_event_cb(__unused int severity, const char *msg)
|
||||
void
|
||||
log_add_level(void)
|
||||
{
|
||||
log_settings.log_level++;
|
||||
log_level++;
|
||||
}
|
||||
|
||||
/* Get log level. */
|
||||
int
|
||||
log_get_level(void)
|
||||
{
|
||||
return (log_settings.log_level);
|
||||
return log_level;
|
||||
}
|
||||
|
||||
void init_logging(const char *program_name, bool use_syslog, int log_level)
|
||||
void init_logging(int _log_level)
|
||||
{
|
||||
log_settings.log_level = log_level;
|
||||
log_settings.use_syslog = use_syslog;
|
||||
log_settings.program_name = xstrdup(program_name);
|
||||
log_level = _log_level;
|
||||
log_prefix = xstrdup("");
|
||||
|
||||
if (use_syslog) {
|
||||
openlog(program_name, LOG_CONS | LOG_PID, LOG_USER);
|
||||
setlogmask(LOG_UPTO(log_level));
|
||||
} else {
|
||||
log_file = fdopen(dup(STDERR_FILENO), "a");
|
||||
if (!log_file)
|
||||
exit(1);
|
||||
}
|
||||
log_file = fdopen(dup(STDERR_FILENO), "a");
|
||||
if (!log_file)
|
||||
exit(1);
|
||||
|
||||
event_set_log_callback(log_event_cb);
|
||||
}
|
||||
|
||||
/* Write a log message. */
|
||||
static void
|
||||
log_vwrite(int level, const char *msg, va_list ap)
|
||||
void set_log_prefix(char *_log_prefix)
|
||||
{
|
||||
char *fmt = NULL;
|
||||
free(log_prefix);
|
||||
log_prefix = xstrdup(_log_prefix);
|
||||
}
|
||||
|
||||
const char *token = tmate_session->session_token;
|
||||
/* Write a log message. */
|
||||
__attribute__((__format__(__printf__, 1, 0)))
|
||||
static void
|
||||
log_vwrite(const char *msg, va_list ap)
|
||||
{
|
||||
char *fmt, *out;
|
||||
struct timeval tv;
|
||||
|
||||
if (log_settings.log_level < level)
|
||||
if (log_file == NULL)
|
||||
return;
|
||||
|
||||
if (token) {
|
||||
if (asprintf(&fmt, "[%s] %s", token, msg) < 0)
|
||||
exit(1);
|
||||
msg = fmt;
|
||||
}
|
||||
if (vasprintf(&fmt, msg, ap) == -1)
|
||||
exit(1);
|
||||
if (stravis(&out, fmt, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL) == -1)
|
||||
exit(1);
|
||||
|
||||
if (log_settings.use_syslog) {
|
||||
vsyslog(level, msg, ap);
|
||||
} else {
|
||||
fprintf(log_file, "<%d> ", level);
|
||||
vfprintf(log_file, msg, ap);
|
||||
fprintf(log_file, "\n");
|
||||
fflush(log_file);
|
||||
}
|
||||
if (fprintf(log_file, "%s%s\n", log_prefix, out) == -1)
|
||||
exit(1);
|
||||
|
||||
fflush(log_file);
|
||||
|
||||
free(out);
|
||||
free(fmt);
|
||||
}
|
||||
|
||||
/* Log a debug message. */
|
||||
void
|
||||
log_debug(const char *msg, ...)
|
||||
log_emit(int level, const char *msg, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (log_level < level)
|
||||
return;
|
||||
|
||||
va_start(ap, msg);
|
||||
log_vwrite(LOG_DEBUG, msg, ap);
|
||||
log_vwrite(msg, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/* Log a critical error with error string and die. */
|
||||
__attribute__((__format__(__printf__, 1, 0)))
|
||||
__dead void
|
||||
fatal(const char *msg, ...)
|
||||
{
|
||||
@@ -130,11 +123,13 @@ fatal(const char *msg, ...)
|
||||
va_start(ap, msg);
|
||||
if (asprintf(&fmt, "fatal: %s: %s", msg, strerror(errno)) == -1)
|
||||
exit(1);
|
||||
log_vwrite(LOG_CRIT, fmt, ap);
|
||||
msg = fmt;
|
||||
log_vwrite(msg, ap);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Log a critical error and die. */
|
||||
__attribute__((__format__(__printf__, 1, 0)))
|
||||
__dead void
|
||||
fatalx(const char *msg, ...)
|
||||
{
|
||||
@@ -144,24 +139,7 @@ fatalx(const char *msg, ...)
|
||||
va_start(ap, msg);
|
||||
if (asprintf(&fmt, "fatal: %s", msg) == -1)
|
||||
exit(1);
|
||||
log_vwrite(LOG_CRIT, fmt, ap);
|
||||
msg = fmt;
|
||||
log_vwrite(msg, ap);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void tmate_log(int level, const char *msg, ...)
|
||||
{
|
||||
char *fmt;
|
||||
va_list ap;
|
||||
|
||||
if (log_settings.log_level < level)
|
||||
return;
|
||||
|
||||
va_start(ap, msg);
|
||||
|
||||
if (asprintf(&fmt, "(tmate) %s", msg) < 0)
|
||||
exit(1);
|
||||
log_vwrite(level, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
free(fmt);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
|
||||
char *
|
||||
default_window_name(struct window *w)
|
||||
|
||||
@@ -28,7 +28,9 @@ char *osdep_get_name(int, char *);
|
||||
char *osdep_get_cwd(int);
|
||||
struct event_base *osdep_event_init(void);
|
||||
|
||||
#ifndef __unused
|
||||
#define __unused __attribute__ ((__unused__))
|
||||
#endif
|
||||
|
||||
char *
|
||||
osdep_get_name(int fd, __unused char *tty)
|
||||
|
||||
@@ -100,6 +100,7 @@ proc_event_cb(__unused int fd, short events, void *arg)
|
||||
proc_update_event(peer);
|
||||
}
|
||||
|
||||
#ifndef TMATE
|
||||
static void
|
||||
proc_signal_cb(int signo, __unused short events, void *arg)
|
||||
{
|
||||
@@ -107,6 +108,7 @@ proc_signal_cb(int signo, __unused short events, void *arg)
|
||||
|
||||
tp->signalcb(signo);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
peer_check_version(struct tmuxpeer *peer, struct imsg *imsg)
|
||||
@@ -172,7 +174,7 @@ proc_start(const char *name, struct event_base *base, int forkflag,
|
||||
struct tmuxproc *tp;
|
||||
struct utsname u;
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
if (forkflag)
|
||||
fatal("can't fork");
|
||||
#else
|
||||
@@ -210,7 +212,7 @@ proc_start(const char *name, struct event_base *base, int forkflag,
|
||||
tp = xcalloc(1, sizeof *tp);
|
||||
tp->name = xstrdup(name);
|
||||
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
tp->signalcb = signalcb;
|
||||
set_signals(proc_signal_cb, tp);
|
||||
#endif
|
||||
@@ -226,7 +228,7 @@ proc_loop(struct tmuxproc *tp, int (*loopcb)(void))
|
||||
event_loop(EVLOOP_ONCE);
|
||||
while (!tp->exit && (loopcb == NULL || !loopcb ()));
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
/* flush data on sockets */
|
||||
event_loop(EVLOOP_ONCE | EVLOOP_NONBLOCK);
|
||||
#endif
|
||||
|
||||
@@ -61,7 +61,7 @@ recalculate_sizes(void)
|
||||
TAILQ_FOREACH(c, &clients, entry) {
|
||||
if (c->flags & CLIENT_SUSPENDED)
|
||||
continue;
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
if (c->flags & CLIENT_READONLY)
|
||||
continue;
|
||||
#endif
|
||||
@@ -78,12 +78,12 @@ recalculate_sizes(void)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
if (tmate_has_proxy()) {
|
||||
if (tmate_session->proxy_sy < ssy)
|
||||
ssy = tmate_session->proxy_sy;
|
||||
if (tmate_session->proxy_sx < ssx)
|
||||
ssx = tmate_session->proxy_sx;
|
||||
#ifdef TMATE
|
||||
if (tmate_has_websocket()) {
|
||||
if (tmate_session->websocket_sy < ssy)
|
||||
ssy = tmate_session->websocket_sy;
|
||||
if (tmate_session->websocket_sx < ssx)
|
||||
ssx = tmate_session->websocket_sx;
|
||||
}
|
||||
|
||||
tmate_client_resize(ssx, ssy);
|
||||
@@ -155,7 +155,7 @@ recalculate_sizes(void)
|
||||
is_zoomed = w->flags & WINDOW_ZOOMED;
|
||||
if (is_zoomed)
|
||||
window_unzoom(w);
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
layout_resize(w, ssx, ssy);
|
||||
#endif
|
||||
window_resize(w, ssx, ssy);
|
||||
|
||||
+69
-27
@@ -96,7 +96,7 @@ server_client_get_key_table(struct client *c)
|
||||
return (name);
|
||||
}
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
u_int next_client_id;
|
||||
#endif
|
||||
|
||||
@@ -110,7 +110,7 @@ server_client_create(int fd)
|
||||
|
||||
c = xcalloc(1, sizeof *c);
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
c->id = next_client_id++;
|
||||
c->ip_address = NULL;
|
||||
c->pubkey = NULL;
|
||||
@@ -204,7 +204,7 @@ server_client_lost(struct client *c)
|
||||
TAILQ_REMOVE(&clients, c, entry);
|
||||
log_debug("lost client %p", c);
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
tmate_notify_client_left(tmate_session, c);
|
||||
#endif
|
||||
|
||||
@@ -252,7 +252,7 @@ server_client_lost(struct client *c)
|
||||
cmdq_free(c->cmdq);
|
||||
c->cmdq = NULL;
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
free(c->ip_address);
|
||||
c->ip_address = NULL;
|
||||
free(c->pubkey);
|
||||
@@ -266,7 +266,9 @@ server_client_lost(struct client *c)
|
||||
|
||||
server_client_unref(c);
|
||||
|
||||
#ifndef TMATE
|
||||
server_add_accept(0); /* may be more file descriptors now */
|
||||
#endif
|
||||
|
||||
recalculate_sizes();
|
||||
server_check_unattached();
|
||||
@@ -630,7 +632,7 @@ server_client_handle_key(struct client *c, key_code key)
|
||||
window_unzoom(w);
|
||||
wp = window_pane_at_index(w, key - '0');
|
||||
if (wp != NULL && window_pane_visible(wp))
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
tmate_client_set_active_pane(c->id, key - '0', wp->id);
|
||||
#else
|
||||
window_set_active_pane(w, wp);
|
||||
@@ -781,7 +783,7 @@ server_client_loop(void)
|
||||
RB_FOREACH(w, windows, &windows) {
|
||||
w->flags &= ~WINDOW_REDRAW;
|
||||
TAILQ_FOREACH(wp, &w->panes, entry) {
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
if (wp->fd != -1) {
|
||||
server_client_check_focus(wp);
|
||||
server_client_check_resize(wp);
|
||||
@@ -793,7 +795,7 @@ server_client_loop(void)
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
/* Check if pane should be resized. */
|
||||
void
|
||||
server_client_check_resize(struct window_pane *wp)
|
||||
@@ -1057,6 +1059,11 @@ server_client_dispatch(struct imsg *imsg, void *arg)
|
||||
if (c->flags & CLIENT_DEAD)
|
||||
return;
|
||||
|
||||
#ifdef TMATE
|
||||
if (c->flags & CLIENT_EXIT)
|
||||
return;
|
||||
#endif
|
||||
|
||||
if (imsg == NULL) {
|
||||
server_client_lost(c);
|
||||
return;
|
||||
@@ -1065,6 +1072,24 @@ server_client_dispatch(struct imsg *imsg, void *arg)
|
||||
data = imsg->data;
|
||||
datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
|
||||
|
||||
#ifdef TMATE
|
||||
switch (imsg->hdr.type) {
|
||||
case MSG_IDENTIFY_TMATE_IP_ADDRESS:
|
||||
case MSG_IDENTIFY_TMATE_AUTH_NONE:
|
||||
case MSG_IDENTIFY_TMATE_AUTH_PUBKEY:
|
||||
case MSG_IDENTIFY_TMATE_READONLY:
|
||||
server_client_dispatch_identify(c, imsg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(c->flags & CLIENT_TMATE_AUTHENTICATED)) {
|
||||
control_write(c, "Authentication needed");
|
||||
tmate_info("Dropping unauthenticated client");
|
||||
c->flags |= CLIENT_EXIT;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (imsg->hdr.type) {
|
||||
case MSG_IDENTIFY_FLAGS:
|
||||
case MSG_IDENTIFY_TERM:
|
||||
@@ -1074,13 +1099,18 @@ server_client_dispatch(struct imsg *imsg, void *arg)
|
||||
case MSG_IDENTIFY_ENVIRON:
|
||||
case MSG_IDENTIFY_CLIENTPID:
|
||||
case MSG_IDENTIFY_DONE:
|
||||
#ifdef TMATE_SLAVE
|
||||
case MSG_IDENTIFY_TMATE_IP_ADDRESS:
|
||||
case MSG_IDENTIFY_TMATE_PUBKEY:
|
||||
case MSG_IDENTIFY_TMATE_READONLY:
|
||||
#endif
|
||||
server_client_dispatch_identify(c, imsg);
|
||||
break;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef TMATE
|
||||
if (!(c->flags & CLIENT_IDENTIFIED)) {
|
||||
tmate_info("dropping unidentified client message: %d", imsg->hdr.type);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (imsg->hdr.type) {
|
||||
case MSG_COMMAND:
|
||||
server_client_dispatch_command(c, imsg);
|
||||
break;
|
||||
@@ -1149,17 +1179,6 @@ server_client_dispatch(struct imsg *imsg, void *arg)
|
||||
|
||||
server_client_dispatch_shell(c);
|
||||
break;
|
||||
#ifdef TMATE_SLAVE
|
||||
case MSG_LATENCY:
|
||||
{
|
||||
int latency_ms;
|
||||
if (datalen != sizeof(latency_ms))
|
||||
fatalx("bad MSG_LATENCY size");
|
||||
memcpy(&latency_ms, data, sizeof(latency_ms));
|
||||
tmate_notify_latency(tmate_session, c, latency_ms);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1216,6 +1235,15 @@ error:
|
||||
c->flags |= CLIENT_EXIT;
|
||||
}
|
||||
|
||||
static void handle_tmate_auth(struct client *c)
|
||||
{
|
||||
bool allow = tmate_allow_auth(c->pubkey);
|
||||
if (allow)
|
||||
c->flags |= CLIENT_TMATE_AUTHENTICATED;
|
||||
|
||||
proc_send(c->peer, MSG_TMATE_AUTH_STATUS, -1, &allow, sizeof(allow));
|
||||
}
|
||||
|
||||
/* Handle identify message. */
|
||||
void
|
||||
server_client_dispatch_identify(struct client *c, struct imsg *imsg)
|
||||
@@ -1280,17 +1308,25 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg)
|
||||
memcpy(&c->pid, data, sizeof c->pid);
|
||||
log_debug("client %p IDENTIFY_CLIENTPID %ld", c, (long)c->pid);
|
||||
break;
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
case MSG_IDENTIFY_TMATE_IP_ADDRESS:
|
||||
if (datalen == 0 || data[datalen - 1] != '\0')
|
||||
fatalx("bad MSG_IDENTIFY_TMATE_IP_ADDRESS string");
|
||||
c->ip_address = xstrdup(data);
|
||||
break;
|
||||
case MSG_IDENTIFY_TMATE_PUBKEY:
|
||||
|
||||
case MSG_IDENTIFY_TMATE_AUTH_NONE:
|
||||
assert(!c->pubkey);
|
||||
handle_tmate_auth(c);
|
||||
break;
|
||||
|
||||
case MSG_IDENTIFY_TMATE_AUTH_PUBKEY:
|
||||
if (datalen == 0 || data[datalen - 1] != '\0')
|
||||
fatalx("bad MSG_IDENTIFY_TMATE_PUBKEY string");
|
||||
c->pubkey = xstrdup(data);
|
||||
handle_tmate_auth(c);
|
||||
break;
|
||||
|
||||
case MSG_IDENTIFY_TMATE_READONLY:
|
||||
if (datalen != 1)
|
||||
fatalx("bad MSG_IDENTIFY_TMATE_READONLY size");
|
||||
@@ -1344,7 +1380,7 @@ server_client_dispatch_identify(struct client *c, struct imsg *imsg)
|
||||
if (!(c->flags & CLIENT_CONTROL))
|
||||
c->flags |= CLIENT_TERMINAL;
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
tmate_notify_client_join(tmate_session, c);
|
||||
#endif
|
||||
}
|
||||
@@ -1381,6 +1417,9 @@ server_client_push_stdout(struct client *c)
|
||||
struct msg_stdout_data data;
|
||||
size_t sent, left;
|
||||
|
||||
if (!(c->flags & CLIENT_TMATE_AUTHENTICATED))
|
||||
return;
|
||||
|
||||
left = EVBUFFER_LENGTH(c->stdout_data);
|
||||
while (left != 0) {
|
||||
sent = left;
|
||||
@@ -1422,6 +1461,9 @@ server_client_push_stderr(struct client *c)
|
||||
struct msg_stderr_data data;
|
||||
size_t sent, left;
|
||||
|
||||
if (!(c->flags & CLIENT_TMATE_AUTHENTICATED))
|
||||
return;
|
||||
|
||||
if (c->stderr_data == c->stdout_data) {
|
||||
server_client_push_stdout(c);
|
||||
return;
|
||||
|
||||
+2
-2
@@ -294,7 +294,7 @@ void
|
||||
server_destroy_pane(struct window_pane *wp, int hooks)
|
||||
{
|
||||
struct window *w = wp->window;
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
int old_fd;
|
||||
struct screen_write_ctx ctx;
|
||||
struct grid_cell gc;
|
||||
@@ -330,7 +330,7 @@ server_destroy_pane(struct window_pane *wp, int hooks)
|
||||
#endif
|
||||
|
||||
server_unzoom_window(w);
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
layout_close_pane(wp);
|
||||
window_remove_pane(w, wp);
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ server_create_socket(void)
|
||||
int
|
||||
server_start(struct event_base *base, int lockfd, char *lockfile)
|
||||
{
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
server_proc = proc_start("server", base, 0, server_signal);
|
||||
#else
|
||||
int pair[2];
|
||||
@@ -170,7 +170,7 @@ server_start(struct event_base *base, int lockfd, char *lockfile)
|
||||
|
||||
gettimeofday(&start_time, NULL);
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
server_fd = tmate_session->tmux_socket_fd;
|
||||
#else
|
||||
server_fd = server_create_socket();
|
||||
@@ -188,20 +188,19 @@ server_start(struct event_base *base, int lockfd, char *lockfile)
|
||||
|
||||
start_cfg();
|
||||
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
status_prompt_load_history();
|
||||
server_add_accept(0);
|
||||
#endif
|
||||
|
||||
server_add_accept(0);
|
||||
|
||||
proc_loop(server_proc, server_loop);
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
status_prompt_save_history();
|
||||
#endif
|
||||
exit(0);
|
||||
}
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
static int tmate_server_request_exit;
|
||||
#endif
|
||||
|
||||
@@ -213,7 +212,7 @@ server_loop(void)
|
||||
|
||||
server_client_loop();
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
if (!ssh_is_connected(tmate_session->ssh_client.session) &&
|
||||
!tmate_server_request_exit) {
|
||||
/*
|
||||
@@ -223,7 +222,8 @@ server_loop(void)
|
||||
* This causes an infinite loop.
|
||||
* The following check is a workaround to avoid the infinite loop.
|
||||
*/
|
||||
tmate_fatal("SSH socket not connected, but socket close wasn't detected");
|
||||
tmate_info("SSH socket not connected, but socket close wasn't detected");
|
||||
request_server_termination();
|
||||
}
|
||||
|
||||
if (!tmate_server_request_exit)
|
||||
@@ -248,6 +248,8 @@ server_loop(void)
|
||||
if (!TAILQ_EMPTY(&clients))
|
||||
return (0);
|
||||
|
||||
tmate_info("Session terminated");
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
@@ -258,7 +260,7 @@ server_send_exit(void)
|
||||
struct client *c, *c1;
|
||||
struct session *s, *s1;
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
tmate_server_request_exit = 1;
|
||||
#endif
|
||||
|
||||
@@ -379,6 +381,7 @@ server_signal(int sig)
|
||||
case SIGCHLD:
|
||||
server_child_signal();
|
||||
break;
|
||||
#ifndef TMATE
|
||||
case SIGUSR1:
|
||||
event_del(&server_ev_accept);
|
||||
fd = server_create_socket();
|
||||
@@ -389,6 +392,7 @@ server_signal(int sig)
|
||||
}
|
||||
server_add_accept(0);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -344,7 +344,7 @@ session_new(struct session *s, const char *name, int argc, char **argv,
|
||||
shell = _PATH_BSHELL;
|
||||
|
||||
hlimit = options_get_number(s->options, "history-limit");
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
hlimit = hlimit > TMATE_HLIMIT ? TMATE_HLIMIT : hlimit;
|
||||
#endif
|
||||
w = window_create(name, argc, argv, path, shell, cwd, env, s->tio,
|
||||
@@ -564,6 +564,7 @@ session_group_index(struct session_group *sg)
|
||||
}
|
||||
|
||||
fatalx("session group not found");
|
||||
for(;;);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
#include "tmux.h"
|
||||
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
|
||||
struct event ev_sighup;
|
||||
struct event ev_sigchld;
|
||||
|
||||
@@ -217,7 +217,7 @@ status_redraw_get_left(struct client *c, time_t t, struct grid_cell *gc,
|
||||
char *left;
|
||||
size_t leftlen;
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
left = xstrdup(tmate_left_status ?: "");
|
||||
#else
|
||||
style_apply_update(gc, s->options, "status-left-style");
|
||||
@@ -243,7 +243,7 @@ status_redraw_get_right(struct client *c, time_t t, struct grid_cell *gc,
|
||||
char *right;
|
||||
size_t rightlen;
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
right = xstrdup(tmate_right_status ?: "");
|
||||
#else
|
||||
style_apply_update(gc, s->options, "status-right-style");
|
||||
@@ -530,7 +530,7 @@ status_print(struct client *c, struct winlink *wl, time_t t,
|
||||
const char *fmt;
|
||||
char *text;
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
/* TODO use the host winlink status */
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include "tmate.h"
|
||||
|
||||
static void reset_and_enable_authorized_keys(void)
|
||||
{
|
||||
ssh_key *keys = tmate_session->authorized_keys;
|
||||
if (keys) {
|
||||
for (ssh_key *k = keys; *k; k++)
|
||||
ssh_key_free(*k);
|
||||
free(keys);
|
||||
}
|
||||
|
||||
keys = xreallocarray(NULL, sizeof(ssh_key), 1);
|
||||
keys[0] = NULL;
|
||||
|
||||
tmate_session->authorized_keys = keys;
|
||||
}
|
||||
|
||||
static ssh_key import_ssh_pubkey64(const char *_keystr)
|
||||
{
|
||||
/* key is formatted as "type base64_key" */
|
||||
|
||||
char * const keystr = xstrdup(_keystr);
|
||||
char *s = keystr;
|
||||
ssh_key ret = NULL;
|
||||
|
||||
char *key_type = strsep(&s, " ");
|
||||
char *key_content = strsep(&s, " ");
|
||||
|
||||
if (!key_content)
|
||||
goto out;
|
||||
|
||||
enum ssh_keytypes_e type = ssh_key_type_from_name(key_type);
|
||||
if (type == SSH_KEYTYPE_UNKNOWN)
|
||||
goto out;
|
||||
|
||||
if (ssh_pki_import_pubkey_base64(key_content, type, &ret) != SSH_OK) {
|
||||
ret = NULL;
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
free(keystr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int get_num_authorized_keys(ssh_key *keys)
|
||||
{
|
||||
if (!keys)
|
||||
return 0;
|
||||
|
||||
int count = 0;
|
||||
for (ssh_key *k = keys; *k; k++)
|
||||
count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
static void append_authorized_key(const char *keystr)
|
||||
{
|
||||
if (!tmate_session->authorized_keys)
|
||||
reset_and_enable_authorized_keys();
|
||||
|
||||
ssh_key pkey = import_ssh_pubkey64(keystr);
|
||||
if (!pkey)
|
||||
return;
|
||||
|
||||
ssh_key *keys = tmate_session->authorized_keys;
|
||||
int count = get_num_authorized_keys(keys);
|
||||
keys = xreallocarray(keys, sizeof(ssh_key), count+2);
|
||||
tmate_session->authorized_keys = keys;
|
||||
|
||||
keys[count++] = pkey;
|
||||
keys[count] = NULL;
|
||||
}
|
||||
|
||||
static void tmate_set(char *key, char *value)
|
||||
{
|
||||
if (!strcmp(key, "authorized_keys"))
|
||||
append_authorized_key(value);
|
||||
}
|
||||
|
||||
void tmate_hook_set_option_auth(const char *name, const char *val)
|
||||
{
|
||||
if (!strcmp(name, "tmate-authorized-keys")) {
|
||||
reset_and_enable_authorized_keys();
|
||||
} else if (!strcmp(name, "tmate-set")) {
|
||||
char *key_value = xstrdup(val);
|
||||
char *s = key_value;
|
||||
|
||||
char *key = strsep(&s, "=");
|
||||
char *value = s;
|
||||
if (value)
|
||||
tmate_set(key, value);
|
||||
|
||||
free(key_value);
|
||||
}
|
||||
}
|
||||
|
||||
bool tmate_allow_auth(const char *pubkey)
|
||||
{
|
||||
/*
|
||||
* Note that we don't accept connections on the tmux socket until we
|
||||
* get the tmate ready message.
|
||||
*/
|
||||
if (!tmate_session->authorized_keys)
|
||||
return true;
|
||||
|
||||
if (!pubkey)
|
||||
return false;
|
||||
|
||||
ssh_key client_pkey = import_ssh_pubkey64(pubkey);
|
||||
if (!client_pkey)
|
||||
return false;
|
||||
|
||||
bool ret = false;
|
||||
for (ssh_key *k = tmate_session->authorized_keys; *k; k++) {
|
||||
if (!ssh_key_cmp(client_pkey, *k, SSH_KEY_CMP_PUBLIC)) {
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ssh_key_free(client_pkey);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int write_all(int fd, const char *buf, size_t len)
|
||||
{
|
||||
for (size_t i = 0; i < len;) {
|
||||
size_t ret = write(fd, buf+i, len-i);
|
||||
if (ret <= 0)
|
||||
return -1;
|
||||
i += ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_all(int fd, char *buf, size_t len)
|
||||
{
|
||||
for (size_t i = 0; i < len;) {
|
||||
size_t ret = read(fd, buf+i, len-i);
|
||||
if (ret <= 0)
|
||||
return -1;
|
||||
i += ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The following is executed in the context of the SSH server
|
||||
*/
|
||||
bool would_tmate_session_allow_auth(const char *token, const char *pubkey)
|
||||
{
|
||||
/*
|
||||
* The existance of this function is a bit unpleasant:
|
||||
* In order to have the right SSH public key from the SSH client,
|
||||
* we need to ask the tmate session for a match. Denying the key
|
||||
* to the SSH client will make it cycle through its keys.
|
||||
* We briefly connect to the session to get an answer.
|
||||
*
|
||||
* Note that the client will get reauthenticated later (see
|
||||
* server-client.c when identifying the client).
|
||||
*/
|
||||
int sock_fd = -1;
|
||||
int ret = true;
|
||||
|
||||
if (tmate_validate_session_token(token) < 0)
|
||||
goto out;
|
||||
|
||||
char *sock_path = get_socket_path(token);
|
||||
|
||||
struct sockaddr_un sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sun_family = AF_UNIX;
|
||||
size_t size = strlcpy(sa.sun_path, sock_path, sizeof(sa.sun_path));
|
||||
free(sock_path);
|
||||
if (size >= sizeof sa.sun_path)
|
||||
goto out;
|
||||
|
||||
sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (sock_fd < 0)
|
||||
goto out;
|
||||
|
||||
if (connect(sock_fd, (struct sockaddr *)&sa, sizeof sa) == -1)
|
||||
goto out;
|
||||
|
||||
struct imsg_hdr hdr = {
|
||||
.type = pubkey ? MSG_IDENTIFY_TMATE_AUTH_PUBKEY :
|
||||
MSG_IDENTIFY_TMATE_AUTH_NONE,
|
||||
.len = IMSG_HEADER_SIZE + (pubkey ? strlen(pubkey)+1 : 0),
|
||||
.flags = 0,
|
||||
.peerid = PROTOCOL_VERSION,
|
||||
.pid = -1,
|
||||
};
|
||||
|
||||
if (write_all(sock_fd, (void*)&hdr, sizeof(hdr)) < 0)
|
||||
goto out;
|
||||
|
||||
if (pubkey) {
|
||||
if (write_all(sock_fd, pubkey, strlen(pubkey)+1) < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
struct {
|
||||
struct imsg_hdr hdr;
|
||||
bool allow;
|
||||
} __packed recv_msg;
|
||||
|
||||
if (read_all(sock_fd, (void*)&recv_msg, sizeof(recv_msg)) < 0)
|
||||
goto out;
|
||||
|
||||
if (recv_msg.hdr.type == MSG_TMATE_AUTH_STATUS &&
|
||||
recv_msg.hdr.len == sizeof(recv_msg))
|
||||
ret = recv_msg.allow;
|
||||
|
||||
tmate_debug("(preauth) allow=%d", ret);
|
||||
|
||||
out:
|
||||
if (sock_fd != -1)
|
||||
close(sock_fd);
|
||||
return ret;
|
||||
}
|
||||
+70
-33
@@ -6,16 +6,29 @@
|
||||
|
||||
char *tmate_left_status, *tmate_right_status;
|
||||
|
||||
static void tmate_ready(struct tmate_session *session,
|
||||
__unused struct tmate_unpacker *uk)
|
||||
{
|
||||
/* This message is also used by the websocket server */
|
||||
|
||||
/*
|
||||
* We only start accepting connections once the host is ready, this
|
||||
* way we have the authorized keys loaded correctly
|
||||
*/
|
||||
|
||||
if (session->authorized_keys) {
|
||||
int count = get_num_authorized_keys(session->authorized_keys);
|
||||
tmate_info("Restricting ssh access, num_keys=%d", count);
|
||||
}
|
||||
server_add_accept(0);
|
||||
}
|
||||
|
||||
static void tmate_header(struct tmate_session *session,
|
||||
struct tmate_unpacker *uk)
|
||||
{
|
||||
char port_arg[16] = {0};
|
||||
char tmp[512];
|
||||
char *ssh_conn_str;
|
||||
|
||||
session->client_protocol_version = unpack_int(uk);
|
||||
if (session->client_protocol_version <= 4) {
|
||||
session->daemon_encoder.mpac_version = 4;
|
||||
}
|
||||
|
||||
if (session->client_protocol_version >= 3) {
|
||||
session->client_version = unpack_string(uk);
|
||||
@@ -23,43 +36,55 @@ static void tmate_header(struct tmate_session *session,
|
||||
session->client_version = xstrdup("1.8.5");
|
||||
}
|
||||
|
||||
tmate_notice("Daemon header: client version: %s, protocol version: %d",
|
||||
session->client_version, session->client_protocol_version);
|
||||
if (session->client_protocol_version < 6) {
|
||||
/* older clients don't send a ready message */
|
||||
tmate_ready(session, NULL);
|
||||
}
|
||||
|
||||
if (tmate_has_proxy()) {
|
||||
/* If we have a proxy, it takes care of all the following notificatons */
|
||||
tmate_send_proxy_header(session);
|
||||
if (session->client_protocol_version < 5) {
|
||||
session->daemon_encoder.mpac_version = 4;
|
||||
}
|
||||
|
||||
tmate_info("tmate version=%s, protocol=%d",
|
||||
session->client_version, session->client_protocol_version);
|
||||
|
||||
if (tmate_has_websocket()) {
|
||||
/* If we have a websocket server, it takes care of all the following notificatons */
|
||||
tmate_send_websocket_header(session);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tmate_settings->ssh_port != 22)
|
||||
sprintf(port_arg, " -p%d", tmate_settings->ssh_port);
|
||||
|
||||
sprintf(tmp, "ssh%s %s@%s", port_arg, session->session_token_ro, tmate_settings->tmate_host);
|
||||
|
||||
ssh_conn_str = get_ssh_conn_string(session->session_token_ro);
|
||||
tmate_notify("Note: clear your terminal before sharing readonly access");
|
||||
tmate_notify("ssh session read only: %s", tmp);
|
||||
tmate_notify("ssh session read only: %s", ssh_conn_str);
|
||||
tmate_set_env("tmate_ssh_ro", ssh_conn_str);
|
||||
free(ssh_conn_str);
|
||||
|
||||
sprintf(tmp, "ssh%s %s@%s", port_arg, session->session_token, tmate_settings->tmate_host);
|
||||
tmate_notify("ssh session: %s", tmp);
|
||||
|
||||
tmate_set_env("tmate_ssh_ro", tmp);
|
||||
tmate_set_env("tmate_ssh", tmp);
|
||||
ssh_conn_str = get_ssh_conn_string(session->session_token);
|
||||
tmate_notify("ssh session: %s", ssh_conn_str);
|
||||
tmate_set_env("tmate_ssh", ssh_conn_str);
|
||||
free(ssh_conn_str);
|
||||
|
||||
tmate_send_client_ready();
|
||||
}
|
||||
|
||||
static void tmate_ready(__unused struct tmate_session *session,
|
||||
__unused struct tmate_unpacker *uk)
|
||||
static void tmate_uname(struct tmate_session *session,
|
||||
struct tmate_unpacker *uk)
|
||||
{
|
||||
/* used by the proxy */
|
||||
char *sysname = unpack_string(uk);
|
||||
char *nodename = unpack_string(uk);
|
||||
char *release = unpack_string(uk);
|
||||
char *version = unpack_string(uk);
|
||||
char *machine = unpack_string(uk);
|
||||
tmate_info("sysname=%s machine=%s release=%s version=%s nodename=%s",
|
||||
sysname, machine, release, version, nodename);
|
||||
}
|
||||
|
||||
|
||||
extern u_int next_window_pane_id;
|
||||
|
||||
static void tmate_sync_window_panes(struct window *w,
|
||||
struct tmate_unpacker *w_uk)
|
||||
struct tmate_unpacker *w_uk,
|
||||
int *num_panes)
|
||||
{
|
||||
struct tmate_unpacker uk, tmp_uk;
|
||||
struct window_pane *wp, *wp_tmp;
|
||||
@@ -69,6 +94,9 @@ static void tmate_sync_window_panes(struct window *w,
|
||||
wp->flags |= PANE_KILL;
|
||||
|
||||
unpack_each(&uk, &tmp_uk, w_uk) {
|
||||
if (++(*num_panes) > TMATE_MAX_PANES)
|
||||
tmate_fatal("Too many opened panes (max=%d)", TMATE_MAX_PANES);
|
||||
|
||||
int id = unpack_int(&uk);
|
||||
u_int sx = unpack_int(&uk);
|
||||
u_int sy = unpack_int(&uk);
|
||||
@@ -105,6 +133,8 @@ static void tmate_sync_window_panes(struct window *w,
|
||||
|
||||
active_pane_id = unpack_int(w_uk);
|
||||
wp = window_pane_find_by_id(active_pane_id);
|
||||
if (!wp || wp->window != w)
|
||||
tmate_fatal("Invalid active_pane_id recevied");
|
||||
window_set_active_pane(w, wp);
|
||||
}
|
||||
|
||||
@@ -115,6 +145,7 @@ static void tmate_sync_windows(struct session *s,
|
||||
struct winlink *wl, *wl_tmp;
|
||||
struct window *w;
|
||||
int active_window_idx;
|
||||
int num_panes = 0;
|
||||
char *cause;
|
||||
|
||||
RB_FOREACH(wl, winlinks, &s->windows)
|
||||
@@ -139,7 +170,7 @@ static void tmate_sync_windows(struct session *s,
|
||||
w->sx = s->sx;
|
||||
w->sy = s->sy;
|
||||
|
||||
tmate_sync_window_panes(w, &uk);
|
||||
tmate_sync_window_panes(w, &uk, &num_panes);
|
||||
}
|
||||
|
||||
RB_FOREACH_SAFE(wl, winlinks, &s->windows, wl_tmp) {
|
||||
@@ -210,10 +241,10 @@ static void tmate_exec_cmd_str(__unused struct tmate_session *session,
|
||||
|
||||
cmd_str = unpack_string(uk);
|
||||
|
||||
tmate_info("Local cmd: %s", cmd_str);
|
||||
tmate_debug("Local cmd: %s", cmd_str);
|
||||
|
||||
if (cmd_string_parse(cmd_str, &cmdlist, NULL, 0, &cause) != 0) {
|
||||
tmate_info("parse error: %s", cause);
|
||||
tmate_debug("parse error: %s", cause);
|
||||
free(cause);
|
||||
goto out;
|
||||
}
|
||||
@@ -245,13 +276,13 @@ static void tmate_exec_cmd(__unused struct tmate_session *session,
|
||||
|
||||
cmd = cmd_parse(argc, argv, NULL, 0, &cause);
|
||||
if (!cmd) {
|
||||
tmate_info("parse error: %s", cause);
|
||||
tmate_debug("parse error: %s", cause);
|
||||
free(cause);
|
||||
goto out;
|
||||
}
|
||||
|
||||
cmd_str = cmd_print(cmd);
|
||||
tmate_info("Local cmd: %s", cmd_str);
|
||||
tmate_debug("Local cmd: %s", cmd_str);
|
||||
free(cmd_str);
|
||||
|
||||
cmdlist = xcalloc(1, sizeof *cmdlist);
|
||||
@@ -408,14 +439,15 @@ static void tmate_write_copy_mode(__unused struct tmate_session *session,
|
||||
static void tmate_fin(__unused struct tmate_session *session,
|
||||
__unused struct tmate_unpacker *uk)
|
||||
{
|
||||
session->fin_received = true;
|
||||
request_server_termination();
|
||||
}
|
||||
|
||||
static void tmate_reconnect(__unused struct tmate_session *session,
|
||||
__unused struct tmate_unpacker *uk)
|
||||
{
|
||||
if (!tmate_has_proxy())
|
||||
tmate_fatal("Cannot do reconnections without the proxy");
|
||||
if (!tmate_has_websocket())
|
||||
tmate_fatal("Cannot do reconnections without the websocket server");
|
||||
}
|
||||
|
||||
static void restore_snapshot_grid(struct grid *grid, struct tmate_unpacker *uk)
|
||||
@@ -504,6 +536,10 @@ static void tmate_snapshot(__unused struct tmate_session *session,
|
||||
void tmate_dispatch_daemon_message(struct tmate_session *session,
|
||||
struct tmate_unpacker *uk)
|
||||
{
|
||||
/* We make a copy because we are mutating it */
|
||||
struct tmate_unpacker uk_copy = *uk;
|
||||
uk = &uk_copy;
|
||||
|
||||
int cmd = unpack_int(uk);
|
||||
switch (cmd) {
|
||||
#define dispatch(c, f) case c: f(session, uk); break
|
||||
@@ -520,6 +556,7 @@ void tmate_dispatch_daemon_message(struct tmate_session *session,
|
||||
dispatch(TMATE_OUT_RECONNECT, tmate_reconnect);
|
||||
dispatch(TMATE_OUT_SNAPSHOT, tmate_snapshot);
|
||||
dispatch(TMATE_OUT_EXEC_CMD, tmate_exec_cmd);
|
||||
dispatch(TMATE_OUT_UNAME, tmate_uname);
|
||||
default: tmate_fatal("Bad message type: %d", cmd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "tmate.h"
|
||||
#include "tmate-protocol.h"
|
||||
|
||||
#define pack(what, ...) _pack(&tmate_session->daemon_encoder, what, __VA_ARGS__)
|
||||
#define pack(what, ...) _pack(&tmate_session->daemon_encoder, what, ##__VA_ARGS__)
|
||||
|
||||
static void __tmate_notify(const char *msg)
|
||||
{
|
||||
@@ -147,7 +147,7 @@ int tmate_should_exec_cmd_locally(const struct cmd_entry *cmd)
|
||||
|
||||
void tmate_client_cmd_str(int client_id, const char *cmd)
|
||||
{
|
||||
tmate_info("Remote cmd (cid=%d): %s", client_id, cmd);
|
||||
tmate_debug("Remote cmd (cid=%d): %s", client_id, cmd);
|
||||
|
||||
pack(array, 3);
|
||||
pack(int, TMATE_IN_EXEC_CMD_STR);
|
||||
@@ -217,7 +217,7 @@ void tmate_client_cmd(int client_id, struct cmd *cmd)
|
||||
free(cmd_str);
|
||||
return;
|
||||
}
|
||||
tmate_info("Remote cmd (cid=%d): %s", client_id, cmd_str);
|
||||
tmate_debug("Remote cmd (cid=%d): %s", client_id, cmd_str);
|
||||
free(cmd_str);
|
||||
|
||||
extract_cmd(cmd, &argc, &argv);
|
||||
|
||||
+38
-7
@@ -1,9 +1,20 @@
|
||||
#ifdef HAVE_EXECINFO_H
|
||||
#include <execinfo.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <regex.h>
|
||||
#include <signal.h>
|
||||
#include "tmate.h"
|
||||
|
||||
#ifndef HAVE_BACKTRACE
|
||||
|
||||
void tmate_print_stack_trace(void) {}
|
||||
void tmate_catch_sigsegv(void) {}
|
||||
void tmate_preload_trace_lib(void) {}
|
||||
|
||||
#else
|
||||
|
||||
#if DEBUG
|
||||
|
||||
static int print_resolved_stack_frame(const char *frame)
|
||||
@@ -47,7 +58,7 @@ static int print_resolved_stack_frame(const char *frame)
|
||||
return -1;
|
||||
|
||||
line[strlen(line)-1] = 0; /* remove \n */
|
||||
tmate_info("%s(%s) [%s]", file, line, address);
|
||||
tmate_debug("%s(%s) [%s]", file, line, address);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -59,10 +70,10 @@ void tmate_print_stack_trace(void)
|
||||
char **strings;
|
||||
size_t i;
|
||||
|
||||
size = backtrace(array, 20);
|
||||
strings = backtrace_symbols(array, size);
|
||||
size = backtrace (array, 20);
|
||||
strings = backtrace_symbols (array, size);
|
||||
|
||||
tmate_info("============ %zd stack frames ============", size);
|
||||
tmate_info ("============ %zd stack frames ============", size);
|
||||
|
||||
for (i = 1; i < size; i++) {
|
||||
#if DEBUG
|
||||
@@ -71,11 +82,31 @@ void tmate_print_stack_trace(void)
|
||||
tmate_info("%s", strings[i]);
|
||||
}
|
||||
|
||||
free(strings);
|
||||
free (strings);
|
||||
}
|
||||
|
||||
static void handle_crash(int sig)
|
||||
{
|
||||
/* TODO send stack trace to server */
|
||||
const char *what = sig == SIGSEGV ? "SIGSEGV" : "SIGABRT";
|
||||
tmate_info("%s printing stack trace", what);
|
||||
tmate_print_stack_trace();
|
||||
|
||||
/* Reraise */
|
||||
signal(sig, NULL);
|
||||
kill(getpid(), sig);
|
||||
}
|
||||
|
||||
void tmate_catch_sigsegv(void)
|
||||
{
|
||||
signal(SIGSEGV, handle_crash);
|
||||
signal(SIGABRT, handle_crash);
|
||||
}
|
||||
|
||||
void tmate_preload_trace_lib(void)
|
||||
{
|
||||
void *array[1];
|
||||
backtrace(array, 1);
|
||||
void *array[1];
|
||||
backtrace(array, 1);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+80
-327
@@ -16,81 +16,27 @@
|
||||
#include <term.h>
|
||||
#include <time.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/syslog.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include "tmate.h"
|
||||
|
||||
struct tmate_session _tmate_session, *tmate_session = &_tmate_session;
|
||||
|
||||
extern FILE *log_file;
|
||||
|
||||
static char *cmdline;
|
||||
static char *cmdline_end;
|
||||
static int dev_urandom_fd;
|
||||
|
||||
extern int server_create_socket(void);
|
||||
extern int client_connect(struct event_base *base, const char *path, int start_server);
|
||||
|
||||
struct tmate_settings _tmate_settings = {
|
||||
.keys_dir = TMATE_SSH_DEFAULT_KEYS_DIR,
|
||||
.authorized_keys_path = NULL,
|
||||
.ssh_port = TMATE_SSH_DEFAULT_PORT,
|
||||
.proxy_hostname = NULL,
|
||||
.ssh_port_advertized = -1,
|
||||
.websocket_hostname = NULL,
|
||||
.bind_addr = NULL,
|
||||
.proxy_port = TMATE_DEFAULT_PROXY_PORT,
|
||||
.websocket_port = TMATE_DEFAULT_WEBSOCKET_PORT,
|
||||
.tmate_host = NULL,
|
||||
.log_level = LOG_NOTICE,
|
||||
.use_syslog = false,
|
||||
.log_level = LOG_INFO,
|
||||
.use_proxy_protocol = false,
|
||||
};
|
||||
|
||||
struct tmate_settings *tmate_settings = &_tmate_settings;
|
||||
|
||||
void tmate_get_random_bytes(void *buffer, ssize_t len)
|
||||
{
|
||||
if (read(dev_urandom_fd, buffer, len) != len)
|
||||
tmate_fatal("Cannot read from /dev/urandom");
|
||||
}
|
||||
|
||||
long tmate_get_random_long(void)
|
||||
{
|
||||
long val;
|
||||
tmate_get_random_bytes(&val, sizeof(val));
|
||||
return val;
|
||||
}
|
||||
|
||||
#define RS_BUF_SIZE 256
|
||||
|
||||
struct random_stream {
|
||||
char bytes[RS_BUF_SIZE];
|
||||
off_t pos;
|
||||
};
|
||||
|
||||
static void random_stream_init(struct random_stream *rs)
|
||||
{
|
||||
rs->pos = RS_BUF_SIZE;
|
||||
}
|
||||
|
||||
static char *random_stream_get(struct random_stream *rs, size_t count)
|
||||
{
|
||||
char *ret;
|
||||
|
||||
if (count > RS_BUF_SIZE) {
|
||||
tmate_fatal("buffer too small");
|
||||
}
|
||||
|
||||
if (rs->pos + count > RS_BUF_SIZE) {
|
||||
tmate_get_random_bytes(rs->bytes, RS_BUF_SIZE);
|
||||
rs->pos = 0;
|
||||
}
|
||||
|
||||
|
||||
ret = &rs->bytes[rs->pos];
|
||||
rs->pos += count;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
extern int server_fd;
|
||||
extern void server_send_exit(void);
|
||||
void request_server_termination(void)
|
||||
@@ -103,7 +49,7 @@ void request_server_termination(void)
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
fprintf(stderr, "usage: tmate-ssh-server [-b ip] [-h hostname] [-k keys_dir] [-a authorized_keys_path] [-p port] [-x proxy_hostname] [-q proxy_port] [-s] [-v]\n");
|
||||
fprintf(stderr, "usage: tmate-ssh-server [-b ip] [-h hostname] [-k keys_dir] [-p listen_port] [-q ssh_port_advertized] [-w websocket_hostname] [-z websocket_port] [-x] [-v]\n");
|
||||
}
|
||||
|
||||
static char* get_full_hostname(void)
|
||||
@@ -123,7 +69,7 @@ static char* get_full_hostname(void)
|
||||
hints.ai_flags = AI_CANONNAME;
|
||||
|
||||
if ((gai_result = getaddrinfo(hostname, NULL, &hints, &info)) != 0) {
|
||||
tmate_warn("cannot lookup hostname: %s", gai_strerror(gai_result));
|
||||
tmate_info("cannot lookup hostname: %s", gai_strerror(gai_result));
|
||||
return xstrdup(hostname);
|
||||
}
|
||||
|
||||
@@ -152,11 +98,29 @@ static void setup_locale(void)
|
||||
tzset();
|
||||
}
|
||||
|
||||
static int check_owned_directory_mode(const char *path, mode_t expected_mode)
|
||||
{
|
||||
struct stat stat;
|
||||
if (lstat(path, &stat))
|
||||
return -1;
|
||||
|
||||
if (!S_ISDIR(stat.st_mode))
|
||||
return -1;
|
||||
|
||||
if (stat.st_uid != getuid())
|
||||
return -1;
|
||||
|
||||
if ((stat.st_mode & 07777) != expected_mode)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv, char **envp)
|
||||
{
|
||||
int opt;
|
||||
|
||||
while ((opt = getopt(argc, argv, "b:h:k:a:p:x:q:sv")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "b:h:k:p:q:w:z:xv")) != -1) {
|
||||
switch (opt) {
|
||||
case 'b':
|
||||
tmate_settings->bind_addr = xstrdup(optarg);
|
||||
@@ -167,20 +131,20 @@ int main(int argc, char **argv, char **envp)
|
||||
case 'k':
|
||||
tmate_settings->keys_dir = xstrdup(optarg);
|
||||
break;
|
||||
case 'a':
|
||||
tmate_settings->authorized_keys_path = xstrdup(optarg);
|
||||
break;
|
||||
case 'p':
|
||||
tmate_settings->ssh_port = atoi(optarg);
|
||||
break;
|
||||
case 'x':
|
||||
tmate_settings->proxy_hostname = xstrdup(optarg);
|
||||
break;
|
||||
case 'q':
|
||||
tmate_settings->proxy_port = atoi(optarg);
|
||||
tmate_settings->ssh_port_advertized = atoi(optarg);
|
||||
break;
|
||||
case 's':
|
||||
tmate_settings->use_syslog = true;
|
||||
case 'w':
|
||||
tmate_settings->websocket_hostname = xstrdup(optarg);
|
||||
break;
|
||||
case 'z':
|
||||
tmate_settings->websocket_port = atoi(optarg);
|
||||
break;
|
||||
case 'x':
|
||||
tmate_settings->use_proxy_protocol = true;
|
||||
break;
|
||||
case 'v':
|
||||
tmate_settings->log_level++;
|
||||
@@ -191,8 +155,7 @@ int main(int argc, char **argv, char **envp)
|
||||
}
|
||||
}
|
||||
|
||||
init_logging("tmate-remote-tmux",
|
||||
tmate_settings->use_syslog, tmate_settings->log_level);
|
||||
init_logging(tmate_settings->log_level);
|
||||
|
||||
setup_locale();
|
||||
|
||||
@@ -203,133 +166,67 @@ int main(int argc, char **argv, char **envp)
|
||||
cmdline_end = *envp;
|
||||
|
||||
tmate_preload_trace_lib();
|
||||
tmate_catch_sigsegv();
|
||||
tmate_init_rand();
|
||||
|
||||
if ((dev_urandom_fd = open("/dev/urandom", O_RDONLY)) < 0)
|
||||
tmate_fatal("Cannot open /dev/urandom");
|
||||
|
||||
if ((mkdir(TMATE_WORKDIR, 0701) < 0 && errno != EEXIST) ||
|
||||
(mkdir(TMATE_WORKDIR "/sessions", 0703) < 0 && errno != EEXIST) ||
|
||||
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);
|
||||
|
||||
/* The proxy needs to access the /session dir to rename sockets */
|
||||
if ((chmod(TMATE_WORKDIR, 0701) < 0) ||
|
||||
(chmod(TMATE_WORKDIR "/sessions", 0703) < 0) ||
|
||||
if ((chmod(TMATE_WORKDIR, 0700) < 0) ||
|
||||
(chmod(TMATE_WORKDIR "/sessions", 0700) < 0) ||
|
||||
(chmod(TMATE_WORKDIR "/jail", 0700) < 0))
|
||||
tmate_fatal("Cannot prepare session in " TMATE_WORKDIR);
|
||||
|
||||
if (check_owned_directory_mode(TMATE_WORKDIR, 0700) ||
|
||||
check_owned_directory_mode(TMATE_WORKDIR "/sessions", 0700) ||
|
||||
check_owned_directory_mode(TMATE_WORKDIR "/jail", 0700))
|
||||
tmate_fatal(TMATE_WORKDIR " and subdirectories has incorrect ownership/mode. "
|
||||
"Try deleting " TMATE_WORKDIR " and try again");
|
||||
|
||||
tmate_ssh_server_main(tmate_session,
|
||||
tmate_settings->keys_dir, tmate_settings->bind_addr, tmate_settings->ssh_port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char tmate_token_digits[] = "abcdefghijklmnopqrstuvwxyz"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"0123456789";
|
||||
#define NUM_DIGITS (sizeof(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] = tmate_token_digits[c];
|
||||
}
|
||||
|
||||
token[i] = 0;
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
void set_session_token(struct tmate_session *session,
|
||||
const char *token)
|
||||
char *get_socket_path(const char *_token)
|
||||
{
|
||||
char *path;
|
||||
session->session_token = xstrdup(token);
|
||||
xasprintf(&path, TMATE_WORKDIR "/sessions/%s", token);
|
||||
socket_path = path;
|
||||
char *token = xstrdup(_token);
|
||||
|
||||
memset(cmdline, 0, cmdline_end - cmdline);
|
||||
sprintf(cmdline, "tmate-ssh-server [%s] %s %s",
|
||||
session->session_token,
|
||||
session->ssh_client.role == TMATE_ROLE_DAEMON ? "(daemon)" : "(pty client)",
|
||||
session->ssh_client.ip_address);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static int validate_token(const char *token)
|
||||
{
|
||||
int len;
|
||||
int i;
|
||||
|
||||
if (!memcmp("ro-", token, 3))
|
||||
token += 3;
|
||||
|
||||
len = strlen(token);
|
||||
|
||||
if (len != TMATE_TOKEN_LEN)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!strchr(tmate_token_digits, token[i]))
|
||||
return -1;
|
||||
for (char *c = token; *c; c++) {
|
||||
if (*c == '/' || *c == '.')
|
||||
*c = '=';
|
||||
}
|
||||
|
||||
return 0;
|
||||
xasprintf(&path, TMATE_WORKDIR "/sessions/%s", token);
|
||||
free(token);
|
||||
return path;
|
||||
}
|
||||
|
||||
static void random_sleep(void)
|
||||
void set_session_token(struct tmate_session *session, const char *token)
|
||||
{
|
||||
struct timespec ts;
|
||||
ts.tv_sec = 0;
|
||||
ts.tv_nsec = 50000000 + (tmate_get_random_long() % 150000000);
|
||||
nanosleep(&ts, NULL);
|
||||
session->session_token = xstrdup(token);
|
||||
socket_path = get_socket_path(token);
|
||||
|
||||
xasprintf((char **)&session->obfuscated_session_token, "%.4s...",
|
||||
session->session_token);
|
||||
|
||||
size_t size = cmdline_end - cmdline;
|
||||
memset(cmdline, 0, size);
|
||||
snprintf(cmdline, size-1, "tmate-ssh-server [%s] %s %s",
|
||||
tmate_session->obfuscated_session_token,
|
||||
session->ssh_client.role == TMATE_ROLE_DAEMON ? "(daemon)" : "(pty client)",
|
||||
session->ssh_client.ip_address);
|
||||
|
||||
char *log_prefix;
|
||||
xasprintf(&log_prefix, "[%s] ", session->obfuscated_session_token);
|
||||
set_log_prefix(log_prefix);
|
||||
free(log_prefix);
|
||||
}
|
||||
|
||||
static void ssh_echo(struct tmate_ssh_client *ssh_client,
|
||||
const char *str)
|
||||
{
|
||||
ssh_channel_write(ssh_client->channel, str, strlen(str));
|
||||
}
|
||||
|
||||
#define BAD_TOKEN_ERROR_STR \
|
||||
"Invalid session token" "\r\n"
|
||||
|
||||
#define EXPIRED_TOKEN_ERROR_STR \
|
||||
"Invalid or expired session token" "\r\n"
|
||||
|
||||
static void close_fds_except(int *fd_to_preserve, int num_fds)
|
||||
void close_fds_except(int *fd_to_preserve, int num_fds)
|
||||
{
|
||||
int fd, i, preserve;
|
||||
|
||||
@@ -344,7 +241,7 @@ static void close_fds_except(int *fd_to_preserve, int num_fds)
|
||||
}
|
||||
}
|
||||
|
||||
static void jail(void)
|
||||
void get_in_jail(void)
|
||||
{
|
||||
struct passwd *pw;
|
||||
uid_t uid;
|
||||
@@ -359,7 +256,7 @@ static void jail(void)
|
||||
gid = pw->pw_gid;
|
||||
|
||||
if (getuid() != 0)
|
||||
tmate_fatal("Need root privileges");
|
||||
tmate_fatal("Need root privileges to create the jail");
|
||||
|
||||
if (chroot(TMATE_WORKDIR "/jail") < 0)
|
||||
tmate_fatal("Cannot chroot()");
|
||||
@@ -399,158 +296,14 @@ static void jail(void)
|
||||
|
||||
nice(1);
|
||||
|
||||
tmate_info("Dropped priviledges to %s (%d,%d), jailed in %s",
|
||||
TMATE_JAIL_USER, uid, gid, TMATE_WORKDIR "/jail");
|
||||
tmate_debug("Dropped priviledges to %s (%d,%d), jailed in %s",
|
||||
TMATE_JAIL_USER, uid, gid, TMATE_WORKDIR "/jail");
|
||||
}
|
||||
|
||||
static void setup_ncurse(int fd, const char *name)
|
||||
void setup_ncurse(int fd, const char *name)
|
||||
{
|
||||
int error;
|
||||
if (setupterm((char *)name, fd, &error) != OK)
|
||||
tmate_fatal("Cannot setup terminal");
|
||||
}
|
||||
|
||||
static void handle_sigterm(__unused int sig)
|
||||
{
|
||||
request_server_termination();
|
||||
}
|
||||
|
||||
static 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_notice("Spawning daemon for %s at %s (%s)",
|
||||
client->username, client->ip_address, client->pubkey);
|
||||
|
||||
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->proxy_fd}, 4);
|
||||
|
||||
jail();
|
||||
event_reinit(session->ev_base);
|
||||
|
||||
tmux_server_init();
|
||||
signal(SIGTERM, handle_sigterm);
|
||||
server_start(session->ev_base, -1, NULL);
|
||||
/* never reached */
|
||||
}
|
||||
|
||||
static void tmate_spawn_pty_client(struct tmate_session *session)
|
||||
{
|
||||
struct tmate_ssh_client *client = &session->ssh_client;
|
||||
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;
|
||||
|
||||
if (validate_token(token) < 0) {
|
||||
ssh_echo(client, BAD_TOKEN_ERROR_STR);
|
||||
tmate_fatal("Invalid token");
|
||||
}
|
||||
|
||||
set_session_token(session, token);
|
||||
|
||||
tmate_notice("Spawning pty client for %s (%s)",
|
||||
client->ip_address, client->pubkey);
|
||||
|
||||
session->tmux_socket_fd = client_connect(session->ev_base, socket_path, 0);
|
||||
if (session->tmux_socket_fd < 0) {
|
||||
random_sleep(); /* for making timing attacks harder */
|
||||
ssh_echo(client, EXPIRED_TOKEN_ERROR_STR);
|
||||
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.
|
||||
*/
|
||||
session->readonly = false;
|
||||
if (lstat(socket_path, &fstat) < 0)
|
||||
tmate_fatal("Cannot fstat()");
|
||||
if (S_ISLNK(fstat.st_mode)) {
|
||||
session->readonly = true;
|
||||
argv = argv_ro;
|
||||
argc = 2;
|
||||
}
|
||||
|
||||
if (openpty(&session->pty, &slave_pty, NULL, NULL, NULL) < 0)
|
||||
tmate_fatal("Cannot allocate pty");
|
||||
|
||||
dup2(slave_pty, STDIN_FILENO);
|
||||
dup2(slave_pty, STDOUT_FILENO);
|
||||
dup2(slave_pty, STDERR_FILENO);
|
||||
|
||||
setup_ncurse(slave_pty, "screen-256color");
|
||||
|
||||
tmate_client_pty_init(session);
|
||||
|
||||
/* the unused session->proxy_fd will get closed automatically */
|
||||
|
||||
close_fds_except((int[]){STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO,
|
||||
session->tmux_socket_fd,
|
||||
ssh_get_fd(session->ssh_client.session),
|
||||
session->pty, log_file ? fileno(log_file) : -1}, 7);
|
||||
jail();
|
||||
event_reinit(session->ev_base);
|
||||
|
||||
ret = client_main(session->ev_base, argc, argv,
|
||||
CLIENT_UTF8 | CLIENT_256COLOURS, NULL);
|
||||
tmate_flush_pty(session);
|
||||
exit(ret);
|
||||
}
|
||||
|
||||
static void tmate_spawn_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(session->ev_base);
|
||||
|
||||
tmate_client_exec_init(session);
|
||||
|
||||
if (event_base_dispatch(session->ev_base) < 0)
|
||||
tmate_fatal("Cannot run event loop");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void tmate_spawn(struct tmate_session *session)
|
||||
{
|
||||
switch (session->ssh_client.role) {
|
||||
case TMATE_ROLE_DAEMON: tmate_spawn_daemon(session); break;
|
||||
case TMATE_ROLE_PTY_CLIENT: tmate_spawn_pty_client(session); break;
|
||||
case TMATE_ROLE_EXEC: tmate_spawn_exec(session); break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,6 +285,11 @@ void tmate_decoder_init(struct tmate_decoder *decoder, tmate_decoder_reader *rea
|
||||
void tmate_decoder_get_buffer(struct tmate_decoder *decoder,
|
||||
char **buf, size_t *len)
|
||||
{
|
||||
if (msgpack_unpacker_message_size(&decoder->unpacker) > TMATE_MAX_MESSAGE_SIZE) {
|
||||
tmate_fatal("Incoming message is too large");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!msgpack_unpacker_reserve_buffer(&decoder->unpacker, UNPACKER_RESERVE_SIZE))
|
||||
tmate_fatal("cannot expand decoder buffer");
|
||||
|
||||
|
||||
@@ -56,6 +56,7 @@ enum tmate_daemon_out_msg_types {
|
||||
TMATE_OUT_RECONNECT,
|
||||
TMATE_OUT_SNAPSHOT,
|
||||
TMATE_OUT_EXEC_CMD,
|
||||
TMATE_OUT_UNAME,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -77,6 +78,8 @@ enum tmate_daemon_out_msg_types {
|
||||
[TMATE_OUT_RECONNECT, string: reconnection_data]
|
||||
[TMATE_OUT_SNAPSHOT, ...]
|
||||
[TMATE_OUT_EXEC_CMD, string: cmd_name, ...string: args]
|
||||
[TMATE_OUT_UNAME, string: name.sysname, string: name.nodename,
|
||||
string: name.release, string: name.version, string: name.machine]
|
||||
*/
|
||||
|
||||
enum tmate_daemon_in_msg_types {
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
#include "tmate.h"
|
||||
#include <fcntl.h>
|
||||
|
||||
static int dev_urandom_fd;
|
||||
|
||||
void tmate_init_rand(void)
|
||||
{
|
||||
if ((dev_urandom_fd = open("/dev/urandom", O_RDONLY)) < 0)
|
||||
tmate_fatal("Cannot open /dev/urandom");
|
||||
}
|
||||
|
||||
void tmate_get_random_bytes(void *buffer, ssize_t len)
|
||||
{
|
||||
if (read(dev_urandom_fd, buffer, len) != len)
|
||||
tmate_fatal("Cannot read from /dev/urandom");
|
||||
}
|
||||
|
||||
long tmate_get_random_long(void)
|
||||
{
|
||||
long val;
|
||||
tmate_get_random_bytes(&val, sizeof(val));
|
||||
return val;
|
||||
}
|
||||
|
||||
void random_stream_init(struct random_stream *rs)
|
||||
{
|
||||
rs->pos = RS_BUF_SIZE;
|
||||
}
|
||||
|
||||
char *random_stream_get(struct random_stream *rs, size_t count)
|
||||
{
|
||||
char *ret;
|
||||
|
||||
if (count > RS_BUF_SIZE) {
|
||||
tmate_fatal("buffer too small");
|
||||
}
|
||||
|
||||
if (rs->pos + count > RS_BUF_SIZE) {
|
||||
tmate_get_random_bytes(rs->bytes, RS_BUF_SIZE);
|
||||
rs->pos = 0;
|
||||
}
|
||||
|
||||
|
||||
ret = &rs->bytes[rs->pos];
|
||||
rs->pos += count;
|
||||
return ret;
|
||||
}
|
||||
+124
-11
@@ -1,17 +1,10 @@
|
||||
#include <libssh/server.h>
|
||||
#include <errno.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <signal.h>
|
||||
#include "tmate.h"
|
||||
|
||||
extern void client_signal(int sig);
|
||||
extern void client_report_latency(int latency_ms);
|
||||
|
||||
static void on_latency_callback(__unused void *userdata, int latency_ms)
|
||||
{
|
||||
client_report_latency(latency_ms);
|
||||
}
|
||||
|
||||
static int on_ssh_channel_read(__unused ssh_session _session,
|
||||
__unused ssh_channel channel,
|
||||
void *_data, uint32_t total_len,
|
||||
@@ -91,13 +84,13 @@ static void __on_pty_event(__unused evutil_socket_t fd, __unused short what, voi
|
||||
on_pty_event(arg);
|
||||
}
|
||||
|
||||
void tmate_flush_pty(struct tmate_session *session)
|
||||
static void tmate_flush_pty(struct tmate_session *session)
|
||||
{
|
||||
on_pty_event(session);
|
||||
close(session->pty);
|
||||
}
|
||||
|
||||
void tmate_client_pty_init(struct tmate_session *session)
|
||||
static void tmate_client_pty_init(struct tmate_session *session)
|
||||
{
|
||||
struct tmate_ssh_client *client = &session->ssh_client;
|
||||
|
||||
@@ -116,6 +109,126 @@ void tmate_client_pty_init(struct tmate_session *session)
|
||||
event_set(&session->ev_pty, session->pty,
|
||||
EV_READ | EV_PERSIST, __on_pty_event, session);
|
||||
event_add(&session->ev_pty, NULL);
|
||||
}
|
||||
|
||||
tmate_add_ssh_latency_callback(client, on_latency_callback, session);
|
||||
static void random_sleep(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
ts.tv_sec = 0;
|
||||
ts.tv_nsec = 50000000 + (tmate_get_random_long() % 150000000);
|
||||
nanosleep(&ts, NULL);
|
||||
}
|
||||
|
||||
#define BAD_TOKEN_ERROR_STR \
|
||||
"Invalid session token" "\r\n"
|
||||
|
||||
#define EXPIRED_TOKEN_ERROR_STR \
|
||||
"Invalid or expired session token" "\r\n"
|
||||
|
||||
static void ssh_echo(struct tmate_ssh_client *ssh_client,
|
||||
const char *str)
|
||||
{
|
||||
ssh_channel_write(ssh_client->channel, str, strlen(str));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Note: get_socket_path() replaces '/' and '.' by '_' to
|
||||
* avoid wondering around the file system.
|
||||
*/
|
||||
static char valid_digits[] = "abcdefghijklmnopqrstuvwxyz"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"0123456789-_/";
|
||||
|
||||
int tmate_validate_session_token(const char *token)
|
||||
{
|
||||
int len;
|
||||
int i;
|
||||
|
||||
len = strlen(token);
|
||||
if (len <= 2)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!strchr(valid_digits, token[i]))
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tmate_spawn_pty_client(struct tmate_session *session)
|
||||
{
|
||||
struct tmate_ssh_client *client = &session->ssh_client;
|
||||
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;
|
||||
|
||||
if (tmate_validate_session_token(token) < 0) {
|
||||
ssh_echo(client, BAD_TOKEN_ERROR_STR);
|
||||
tmate_fatal("Invalid token");
|
||||
}
|
||||
|
||||
set_session_token(session, token);
|
||||
|
||||
tmate_info("Spawning pty client ip=%s", client->ip_address);
|
||||
|
||||
session->tmux_socket_fd = client_connect(session->ev_base, socket_path, 0);
|
||||
if (session->tmux_socket_fd < 0) {
|
||||
if (tmate_has_websocket()) {
|
||||
/* Turn the response into an exec to show a better error */
|
||||
client->exec_command = xstrdup("explain-session-not-found");
|
||||
tmate_spawn_exec(session);
|
||||
/* No return */
|
||||
}
|
||||
|
||||
random_sleep(); /* for making timing attacks harder */
|
||||
ssh_echo(client, EXPIRED_TOKEN_ERROR_STR);
|
||||
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.
|
||||
*/
|
||||
session->readonly = false;
|
||||
if (lstat(socket_path, &fstat) < 0)
|
||||
tmate_fatal("Cannot fstat()");
|
||||
if (S_ISLNK(fstat.st_mode)) {
|
||||
session->readonly = true;
|
||||
argv = argv_ro;
|
||||
argc = 2;
|
||||
}
|
||||
|
||||
if (openpty(&session->pty, &slave_pty, NULL, NULL, NULL) < 0)
|
||||
tmate_fatal("Cannot allocate pty");
|
||||
|
||||
dup2(slave_pty, STDIN_FILENO);
|
||||
dup2(slave_pty, STDOUT_FILENO);
|
||||
dup2(slave_pty, STDERR_FILENO);
|
||||
|
||||
setup_ncurse(slave_pty, "screen-256color");
|
||||
|
||||
tmate_client_pty_init(session);
|
||||
|
||||
/* the unused session->websocket_fd will get closed automatically */
|
||||
close_fds_except((int[]){STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO,
|
||||
session->tmux_socket_fd,
|
||||
ssh_get_fd(session->ssh_client.session),
|
||||
session->pty, log_file ? fileno(log_file) : -1}, 7);
|
||||
get_in_jail();
|
||||
event_reinit(session->ev_base);
|
||||
|
||||
ret = client_main(session->ev_base, argc, argv,
|
||||
CLIENT_UTF8 | CLIENT_256COLOURS, NULL);
|
||||
tmate_flush_pty(session);
|
||||
exit(ret);
|
||||
}
|
||||
|
||||
+131
-11
@@ -1,18 +1,15 @@
|
||||
#include "tmate.h"
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
|
||||
static void on_latency_callback(void *userdata, int latency_ms)
|
||||
{
|
||||
struct tmate_session *session = userdata;
|
||||
tmate_notify_latency(session, NULL, latency_ms);
|
||||
}
|
||||
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_send_proxy_daemon_msg(session, uk);
|
||||
tmate_dispatch_daemon_message(session, uk);
|
||||
tmate_send_websocket_daemon_msg(session, uk);
|
||||
}
|
||||
|
||||
static int on_ssh_channel_read(__unused ssh_session _session,
|
||||
@@ -62,8 +59,8 @@ static void on_daemon_encoder_write(void *userdata, struct evbuffer *buffer)
|
||||
|
||||
written = ssh_channel_write(session->ssh_client.channel, buf, len);
|
||||
if (written < 0) {
|
||||
tmate_warn("Error writing to channel: %s",
|
||||
ssh_get_error(session->ssh_client.session));
|
||||
tmate_info("Error writing to channel: %s",
|
||||
ssh_get_error(session->ssh_client.session));
|
||||
request_server_termination();
|
||||
break;
|
||||
}
|
||||
@@ -72,7 +69,7 @@ static void on_daemon_encoder_write(void *userdata, struct evbuffer *buffer)
|
||||
}
|
||||
}
|
||||
|
||||
void tmate_daemon_init(struct tmate_session *session)
|
||||
static void tmate_daemon_init(struct tmate_session *session)
|
||||
{
|
||||
struct tmate_ssh_client *client = &session->ssh_client;
|
||||
|
||||
@@ -85,7 +82,130 @@ 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);
|
||||
|
||||
tmate_init_proxy(session, NULL);
|
||||
tmate_init_websocket(session, NULL);
|
||||
}
|
||||
|
||||
tmate_add_ssh_latency_callback(client, on_latency_callback, session);
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
+25
-6
@@ -14,16 +14,16 @@ void tmate_dump_exec_response(struct tmate_session *session,
|
||||
ssh_channel_close(client->channel);
|
||||
|
||||
if (event_base_loopexit(session->ev_base, NULL) < 0)
|
||||
tmate_fatal("cannot stop event loop");
|
||||
tmate_fatal("Cannot stop event loop");
|
||||
}
|
||||
|
||||
static void on_proxy_error(struct tmate_session *session, __unused short events)
|
||||
static void on_websocket_error(struct tmate_session *session, __unused short events)
|
||||
{
|
||||
tmate_warn("Lost proxy connection");
|
||||
tmate_info("Lost websocket server connection");
|
||||
tmate_dump_exec_response(session, 1, "Internal Error\r\n");
|
||||
}
|
||||
|
||||
void tmate_client_exec_init(struct tmate_session *session)
|
||||
static void tmate_client_exec_init(struct tmate_session *session)
|
||||
{
|
||||
struct tmate_ssh_client *client = &session->ssh_client;
|
||||
|
||||
@@ -31,7 +31,26 @@ void tmate_client_exec_init(struct tmate_session *session)
|
||||
ssh_callbacks_init(&client->channel_cb);
|
||||
ssh_set_channel_callbacks(client->channel, &client->channel_cb);
|
||||
|
||||
tmate_init_proxy(session, on_proxy_error);
|
||||
tmate_init_websocket(session, on_websocket_error);
|
||||
|
||||
tmate_proxy_exec(session, client->exec_command);
|
||||
tmate_websocket_exec(session, client->exec_command);
|
||||
}
|
||||
|
||||
void tmate_spawn_exec(struct tmate_session *session)
|
||||
{
|
||||
struct tmate_ssh_client *client = &session->ssh_client;
|
||||
|
||||
tmate_info("Spawning exec client ip=%s", client->ip_address);
|
||||
|
||||
close_fds_except((int[]){ssh_get_fd(session->ssh_client.session),
|
||||
log_file ? fileno(log_file) : -1,
|
||||
session->websocket_fd}, 3);
|
||||
get_in_jail();
|
||||
event_reinit(session->ev_base);
|
||||
|
||||
tmate_client_exec_init(session);
|
||||
|
||||
if (event_base_dispatch(session->ev_base) < 0)
|
||||
tmate_fatal("Cannot run event loop");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
+7
-115
@@ -13,13 +13,14 @@
|
||||
|
||||
static void on_keepalive_timer(evutil_socket_t fd, short what, void *arg);
|
||||
|
||||
static void start_keepalive_timer(struct tmate_ssh_client *client,
|
||||
int timeout_ms)
|
||||
void start_keepalive_timer(struct tmate_ssh_client *client, int timeout_ms)
|
||||
{
|
||||
struct timeval tv;
|
||||
tv.tv_sec = timeout_ms / 1000;
|
||||
tv.tv_usec = (timeout_ms % 1000)*1000;
|
||||
|
||||
client->keepalive_interval_ms = timeout_ms;
|
||||
|
||||
evtimer_set(&client->ev_keepalive_timer, on_keepalive_timer, client);
|
||||
evtimer_add(&client->ev_keepalive_timer, &tv);
|
||||
}
|
||||
@@ -30,124 +31,15 @@ static void on_keepalive_timer(__unused evutil_socket_t fd,
|
||||
struct tmate_ssh_client *client = arg;
|
||||
|
||||
/*
|
||||
* libssh-0.8.4 or higher have a broken keepalive implementation due
|
||||
* to packet filtering.
|
||||
* libssh 0.8.4, 0.8.5, and 0.8.6 can't handle the response of the
|
||||
* keepalives due to packet filtering.
|
||||
*/
|
||||
if (ssh_version(SSH_VERSION_INT(0,8,4)))
|
||||
|
||||
if (ssh_version(SSH_VERSION_INT(0,8,4)) && !ssh_version(SSH_VERSION_INT(0,8,7)))
|
||||
return;
|
||||
|
||||
if (ssh_send_keepalive(client->session) == SSH_ERROR)
|
||||
return;
|
||||
|
||||
#ifdef ENABLE_LATENCY
|
||||
if (client->keepalive_sent_at.tv_sec == 0) {
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &client->keepalive_sent_at) < 0)
|
||||
tmate_fatal("cannot clock_gettime()");
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* We restart the timer here as opposed to the ssh_pong callback,
|
||||
* because some clients may be broken and our callback may not be
|
||||
* called, and we must ensure that we send keepalives periodically
|
||||
* because some connections may get closed for inactivity due to the
|
||||
* presence of hostile routers.
|
||||
*/
|
||||
start_keepalive_timer(client, client->keepalive_interval_ms);
|
||||
}
|
||||
|
||||
|
||||
#ifdef ENABLE_LATENCY
|
||||
static void timespec_subtract(struct timespec *result,
|
||||
struct timespec *x, struct timespec *y);
|
||||
|
||||
static unsigned long long timespec_to_millisec(struct timespec *ts);
|
||||
|
||||
static void ssh_pong(struct tmate_ssh_client *client)
|
||||
{
|
||||
struct timespec now, tmp;
|
||||
int latency_ms;
|
||||
|
||||
if (!client->latency_cb)
|
||||
return;
|
||||
|
||||
if (!client->keepalive_sent_at.tv_sec)
|
||||
return;
|
||||
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &now) < 0)
|
||||
tmate_fatal("cannot clock_gettime()");
|
||||
|
||||
timespec_subtract(&tmp, &now, &client->keepalive_sent_at);
|
||||
latency_ms = timespec_to_millisec(&tmp);
|
||||
client->latency_cb(client->latency_cb_userdata, latency_ms);
|
||||
|
||||
client->keepalive_sent_at.tv_sec = 0;
|
||||
}
|
||||
|
||||
static void ssh_request_denied_callback(__unused ssh_session session, void *userdata)
|
||||
{
|
||||
ssh_pong(userdata);
|
||||
}
|
||||
|
||||
static void ssh_unimplemented_packet_callback(__unused ssh_session session,
|
||||
__unused uint32_t seq, void *userdata)
|
||||
{
|
||||
ssh_pong(userdata);
|
||||
}
|
||||
|
||||
void tmate_start_ssh_latency_probes(struct tmate_ssh_client *client,
|
||||
struct ssh_server_callbacks_struct *server_callbacks,
|
||||
int keepalive_interval_ms)
|
||||
{
|
||||
client->keepalive_interval_ms = keepalive_interval_ms;
|
||||
client->latency_cb = NULL;
|
||||
server_callbacks->client_unimplemented_packet_function = ssh_unimplemented_packet_callback;
|
||||
server_callbacks->client_request_denied_function = ssh_request_denied_callback;
|
||||
client->keepalive_sent_at.tv_sec = 0;
|
||||
start_keepalive_timer(client, 3000);
|
||||
}
|
||||
|
||||
void tmate_add_ssh_latency_callback(struct tmate_ssh_client *client,
|
||||
ssh_client_latency_cb cb, void *userdata)
|
||||
{
|
||||
if (client->latency_cb)
|
||||
tmate_fatal("only one latency callback for now");
|
||||
|
||||
client->latency_cb = cb;
|
||||
client->latency_cb_userdata = userdata;
|
||||
}
|
||||
|
||||
static void timespec_subtract(struct timespec *result,
|
||||
struct timespec *x, struct timespec *y)
|
||||
{
|
||||
if (x->tv_nsec < y->tv_nsec) {
|
||||
result->tv_sec = x->tv_sec - y->tv_sec - 1;
|
||||
result->tv_nsec = x->tv_nsec - y->tv_nsec + 1000000000;
|
||||
} else {
|
||||
result->tv_sec = x->tv_sec - y->tv_sec;
|
||||
result->tv_nsec = x->tv_nsec - y->tv_nsec;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned long long timespec_to_millisec(struct timespec *ts)
|
||||
{
|
||||
return ts->tv_sec * 1000ULL + ts->tv_nsec / 1000000ULL;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void tmate_start_ssh_latency_probes(struct tmate_ssh_client *client,
|
||||
__unused struct ssh_server_callbacks_struct *server_callbacks,
|
||||
int keepalive_interval_ms)
|
||||
{
|
||||
client->keepalive_interval_ms = keepalive_interval_ms;
|
||||
start_keepalive_timer(client, 3000);
|
||||
}
|
||||
|
||||
void tmate_add_ssh_latency_callback(__unused struct tmate_ssh_client *client,
|
||||
__unused ssh_client_latency_cb cb, __unused void *userdata)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
+210
-131
@@ -6,6 +6,7 @@
|
||||
#include <sys/wait.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <event.h>
|
||||
#include <arpa/inet.h>
|
||||
#ifndef IPPROTO_TCP
|
||||
@@ -14,6 +15,21 @@
|
||||
|
||||
#include "tmate.h"
|
||||
|
||||
char *get_ssh_conn_string(const char *session_token)
|
||||
{
|
||||
char port_arg[16] = {0};
|
||||
char *ret;
|
||||
|
||||
int ssh_port_advertized = tmate_settings->ssh_port_advertized == -1 ?
|
||||
tmate_settings->ssh_port :
|
||||
tmate_settings->ssh_port_advertized;
|
||||
|
||||
if (ssh_port_advertized != 22)
|
||||
sprintf(port_arg, " -p%d", ssh_port_advertized);
|
||||
xasprintf(&ret, "ssh%s %s@%s", port_arg, session_token, tmate_settings->tmate_host);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int pty_request(__unused ssh_session session,
|
||||
__unused ssh_channel channel,
|
||||
__unused const char *term,
|
||||
@@ -67,7 +83,7 @@ static int exec_request(__unused ssh_session session,
|
||||
if (client->role)
|
||||
return 1;
|
||||
|
||||
if (!tmate_has_proxy())
|
||||
if (!tmate_has_websocket())
|
||||
return 1;
|
||||
|
||||
client->role = TMATE_ROLE_EXEC;
|
||||
@@ -110,64 +126,9 @@ static ssh_channel channel_open_request_cb(ssh_session session, void *userdata)
|
||||
return client->channel;
|
||||
}
|
||||
|
||||
static int check_authorized_keys(struct ssh_key_struct *client_pubkey) {
|
||||
#define MAX_PUBKEY_SIZE 0x4000
|
||||
|
||||
const char *authorized_keys_path = tmate_settings->authorized_keys_path;
|
||||
const char *token_delim = " ";
|
||||
|
||||
FILE *file;
|
||||
char key_buf[MAX_PUBKEY_SIZE], *key_type, *key_content;
|
||||
enum ssh_keytypes_e type;
|
||||
ssh_key pkey;
|
||||
|
||||
if (authorized_keys_path == NULL)
|
||||
return SSH_AUTH_SUCCESS;
|
||||
|
||||
file = fopen(authorized_keys_path, "rb");
|
||||
if (file == NULL) {
|
||||
tmate_fatal("Could not open authorized_keys file: \"%s\"", authorized_keys_path);
|
||||
return SSH_AUTH_DENIED;
|
||||
}
|
||||
|
||||
while (fgets(key_buf, MAX_PUBKEY_SIZE, file)) {
|
||||
if (key_buf[0] == '#' || key_buf[0] == '\0')
|
||||
continue;
|
||||
|
||||
key_type = strtok(key_buf, token_delim);
|
||||
if (key_type == NULL)
|
||||
continue;
|
||||
|
||||
type = ssh_key_type_from_name(key_type);
|
||||
if (type == SSH_KEYTYPE_UNKNOWN)
|
||||
continue;
|
||||
|
||||
key_content = strtok(NULL, token_delim);
|
||||
if (key_content == NULL)
|
||||
continue;
|
||||
|
||||
pkey = ssh_key_new();
|
||||
if (ssh_pki_import_pubkey_base64(key_content, type, &pkey) != SSH_OK) {
|
||||
ssh_key_free(pkey);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ssh_key_cmp(pkey, client_pubkey, SSH_KEY_CMP_PUBLIC)) {
|
||||
ssh_key_free(pkey);
|
||||
fclose(file);
|
||||
return SSH_AUTH_SUCCESS;
|
||||
}
|
||||
|
||||
ssh_key_free(pkey);
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
return SSH_AUTH_DENIED;
|
||||
}
|
||||
|
||||
static int auth_pubkey_cb(__unused ssh_session session,
|
||||
const char *user,
|
||||
struct ssh_key_struct *pubkey,
|
||||
ssh_key pubkey,
|
||||
char signature_state, void *userdata)
|
||||
{
|
||||
struct tmate_ssh_client *client = userdata;
|
||||
@@ -175,10 +136,25 @@ static int auth_pubkey_cb(__unused ssh_session session,
|
||||
switch (signature_state) {
|
||||
case SSH_PUBLICKEY_STATE_VALID:
|
||||
client->username = xstrdup(user);
|
||||
if (ssh_pki_export_pubkey_base64(pubkey, &client->pubkey) != SSH_OK)
|
||||
|
||||
const char *key_type = ssh_key_type_to_char(ssh_key_type(pubkey));
|
||||
|
||||
char *b64_key;
|
||||
if (ssh_pki_export_pubkey_base64(pubkey, &b64_key) != SSH_OK)
|
||||
tmate_fatal("error getting public key");
|
||||
|
||||
return check_authorized_keys(pubkey);
|
||||
char *pubkey64;
|
||||
xasprintf(&pubkey64, "%s %s", key_type, b64_key);
|
||||
free(b64_key);
|
||||
|
||||
if (!would_tmate_session_allow_auth(user, pubkey64)) {
|
||||
free(pubkey64);
|
||||
return SSH_AUTH_DENIED;
|
||||
}
|
||||
|
||||
client->pubkey = pubkey64;
|
||||
|
||||
return SSH_AUTH_SUCCESS;
|
||||
case SSH_PUBLICKEY_STATE_NONE:
|
||||
return SSH_AUTH_SUCCESS;
|
||||
default:
|
||||
@@ -186,8 +162,22 @@ static int auth_pubkey_cb(__unused ssh_session session,
|
||||
}
|
||||
}
|
||||
|
||||
static int auth_none_cb(__unused ssh_session session, const char *user, void *userdata)
|
||||
{
|
||||
struct tmate_ssh_client *client = userdata;
|
||||
|
||||
if (!would_tmate_session_allow_auth(user, NULL))
|
||||
return SSH_AUTH_DENIED;
|
||||
|
||||
client->username = xstrdup(user);
|
||||
client->pubkey = NULL;
|
||||
|
||||
return SSH_AUTH_SUCCESS;
|
||||
}
|
||||
|
||||
static struct ssh_server_callbacks_struct ssh_server_cb = {
|
||||
.auth_pubkey_function = auth_pubkey_cb,
|
||||
.auth_none_function = auth_none_cb,
|
||||
.channel_open_request_session_function = channel_open_request_cb,
|
||||
};
|
||||
|
||||
@@ -197,7 +187,7 @@ static void on_ssh_read(__unused evutil_socket_t fd, __unused short what, void *
|
||||
ssh_execute_message_callbacks(client->session);
|
||||
|
||||
if (!ssh_is_connected(client->session)) {
|
||||
tmate_warn("SSH Disconnected");
|
||||
tmate_debug("ssh disconnected");
|
||||
|
||||
event_del(&client->ev_ssh);
|
||||
|
||||
@@ -215,17 +205,17 @@ static void register_on_ssh_read(struct tmate_ssh_client *client)
|
||||
|
||||
static void handle_sigalrm(__unused int sig)
|
||||
{
|
||||
tmate_fatal("Connection grace period (%d) passed", TMATE_SSH_GRACE_PERIOD);
|
||||
tmate_fatal_quiet("Connection grace period (%ds) passed", TMATE_SSH_GRACE_PERIOD);
|
||||
}
|
||||
|
||||
static void client_bootstrap(struct tmate_session *_session)
|
||||
{
|
||||
struct tmate_ssh_client *client = &_session->ssh_client;
|
||||
int grace_period = TMATE_SSH_GRACE_PERIOD;
|
||||
long grace_period = TMATE_SSH_GRACE_PERIOD;
|
||||
ssh_event mainloop;
|
||||
ssh_session session = client->session;
|
||||
|
||||
tmate_notice("Bootstrapping ssh client ip=%s", client->ip_address);
|
||||
tmate_debug("Bootstrapping ssh client ip=%s", client->ip_address);
|
||||
|
||||
_session->ev_base = osdep_event_init();
|
||||
|
||||
@@ -234,20 +224,19 @@ static void client_bootstrap(struct tmate_session *_session)
|
||||
|
||||
{
|
||||
int flag = 1;
|
||||
setsockopt(ssh_get_fd(session), IPPROTO_TCP, TCP_NODELAY,
|
||||
&flag, sizeof(flag));
|
||||
int fd = ssh_get_fd(session);
|
||||
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag));
|
||||
flag = 0x10; /* IPTOS_LOWDELAY */
|
||||
setsockopt(fd, IPPROTO_IP, IP_TOS, &flag, sizeof(flag));
|
||||
}
|
||||
|
||||
signal(SIGALRM, handle_sigalrm);
|
||||
alarm(grace_period);
|
||||
|
||||
/*
|
||||
* We should die early if we can't connect to proxy. This way the
|
||||
* tmate daemon will pick another server to work on.
|
||||
* We should die early if we can't connect to websocket server. This
|
||||
* way the tmate daemon will pick another server to work on.
|
||||
*/
|
||||
_session->proxy_fd = -1;
|
||||
if (tmate_has_proxy())
|
||||
_session->proxy_fd = tmate_connect_to_proxy();
|
||||
_session->websocket_fd = -1;
|
||||
if (tmate_has_websocket())
|
||||
_session->websocket_fd = tmate_connect_to_websocket();
|
||||
|
||||
ssh_server_cb.userdata = client;
|
||||
ssh_callbacks_init(&ssh_server_cb);
|
||||
@@ -256,32 +245,36 @@ static void client_bootstrap(struct tmate_session *_session)
|
||||
ssh_options_set(session, SSH_OPTIONS_TIMEOUT, &grace_period);
|
||||
ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "yes");
|
||||
|
||||
ssh_set_auth_methods(client->session, SSH_AUTH_METHOD_PUBLICKEY);
|
||||
ssh_set_auth_methods(client->session, SSH_AUTH_METHOD_NONE |
|
||||
SSH_AUTH_METHOD_PUBLICKEY);
|
||||
|
||||
tmate_info("Exchanging DH keys");
|
||||
tmate_debug("Exchanging DH keys");
|
||||
if (ssh_handle_key_exchange(session) < 0)
|
||||
tmate_fatal("Error doing the key exchange: %s",
|
||||
ssh_get_error(session));
|
||||
tmate_fatal_quiet("Error doing the key exchange: %s", ssh_get_error(session));
|
||||
|
||||
mainloop = ssh_event_new();
|
||||
ssh_event_add_session(mainloop, session);
|
||||
|
||||
while (!client->role) {
|
||||
if (ssh_event_dopoll(mainloop, -1) == SSH_ERROR)
|
||||
tmate_fatal("Error polling ssh socket: %s", ssh_get_error(session));
|
||||
tmate_fatal_quiet("Error polling ssh socket: %s", ssh_get_error(session));
|
||||
}
|
||||
|
||||
alarm(0);
|
||||
|
||||
/* The latency is callback set later */
|
||||
tmate_start_ssh_latency_probes(client, &ssh_server_cb, TMATE_SSH_KEEPALIVE * 1000);
|
||||
/* The latency callback is set later */
|
||||
start_keepalive_timer(client, TMATE_SSH_KEEPALIVE_SEC * 1000);
|
||||
register_on_ssh_read(client);
|
||||
|
||||
tmate_spawn(_session);
|
||||
switch (client->role) {
|
||||
case TMATE_ROLE_DAEMON: tmate_spawn_daemon(_session); break;
|
||||
case TMATE_ROLE_PTY_CLIENT: tmate_spawn_pty_client(_session); break;
|
||||
case TMATE_ROLE_EXEC: tmate_spawn_exec(_session); break;
|
||||
}
|
||||
/* never reached */
|
||||
}
|
||||
|
||||
static int get_ip(int fd, char *dst, size_t len)
|
||||
static int get_client_ip_socket(int fd, char *dst, size_t len)
|
||||
{
|
||||
struct sockaddr sa;
|
||||
socklen_t sa_len = sizeof(sa);
|
||||
@@ -308,10 +301,74 @@ static int get_ip(int fd, char *dst, size_t len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_single_line(int fd, char *dst, size_t len)
|
||||
{
|
||||
/*
|
||||
* This reads exactly one line from fd.
|
||||
* We cannot read bytes after the new line.
|
||||
* We could use recv() with MSG_PEEK to do this more efficiently.
|
||||
*/
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (read(fd, &dst[i], 1) <= 0)
|
||||
break;
|
||||
|
||||
if (dst[i] == '\r')
|
||||
i--;
|
||||
|
||||
if (dst[i] == '\n') {
|
||||
dst[i] = '\0';
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int get_client_ip_proxy_protocol(int fd, char *dst, size_t len)
|
||||
{
|
||||
char header[110];
|
||||
int tok_num;
|
||||
|
||||
#define SIGNATURE "PROXY "
|
||||
ssize_t ret = read(fd, header, sizeof(SIGNATURE)-1);
|
||||
if (ret <= 0)
|
||||
tmate_fatal_quiet("Disconnected, health checker?");
|
||||
if (ret != sizeof(SIGNATURE)-1)
|
||||
return -1;
|
||||
if (memcmp(header, SIGNATURE, sizeof(SIGNATURE)-1))
|
||||
return -1;
|
||||
#undef SIGNATURE
|
||||
|
||||
if (read_single_line(fd, header, sizeof(header)) < 0)
|
||||
return -1;
|
||||
|
||||
tmate_debug("proxy header: %s", header);
|
||||
|
||||
tok_num = 0;
|
||||
for (char *tok = strtok(header, " "); tok; tok = strtok(NULL, " "), tok_num++) {
|
||||
if (tok_num == 1)
|
||||
strlcpy(dst, tok, len);
|
||||
}
|
||||
|
||||
if (tok_num != 5)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_client_ip(int fd, char *dst, size_t len)
|
||||
{
|
||||
if (tmate_settings->use_proxy_protocol)
|
||||
return get_client_ip_proxy_protocol(fd, dst, len);
|
||||
else
|
||||
return get_client_ip_socket(fd, dst, len);
|
||||
}
|
||||
|
||||
static void ssh_log_function(int priority, const char *function,
|
||||
const char *buffer, __unused void *userdata)
|
||||
{
|
||||
tmate_log(LOG_NOTICE + priority, "[%s] %s", function, buffer);
|
||||
/* loglevel already applied */
|
||||
log_emit(LOG_DEBUG, "[%s] %s", function, buffer);
|
||||
}
|
||||
|
||||
static inline int max(int a, int b)
|
||||
@@ -321,15 +378,28 @@ static inline int max(int a, int b)
|
||||
return a;
|
||||
}
|
||||
|
||||
static void ssh_import_key(ssh_bind bind, const char *keys_dir, const char *name)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
ssh_key key = NULL;
|
||||
|
||||
sprintf(path, "%s/%s", keys_dir, name);
|
||||
|
||||
if (access(path, F_OK) < 0)
|
||||
return;
|
||||
|
||||
tmate_info("Loading key %s", path);
|
||||
|
||||
ssh_pki_import_privkey_file(path, NULL, NULL, NULL, &key);
|
||||
ssh_bind_options_set(bind, SSH_BIND_OPTIONS_IMPORT_KEY, key);
|
||||
}
|
||||
|
||||
static ssh_bind prepare_ssh(const char *keys_dir, const char *bind_addr, int port)
|
||||
{
|
||||
ssh_bind bind;
|
||||
char buffer[PATH_MAX];
|
||||
int ssh_log_level;
|
||||
ssh_key rsakey = NULL;
|
||||
ssh_key ed25519key = NULL;
|
||||
|
||||
ssh_log_level = SSH_LOG_WARNING + max(log_get_level() - LOG_NOTICE, 0);
|
||||
ssh_log_level = SSH_LOG_WARNING + max(log_get_level() - LOG_INFO, 0);
|
||||
|
||||
ssh_set_log_callback(ssh_log_function);
|
||||
|
||||
@@ -337,24 +407,25 @@ static ssh_bind prepare_ssh(const char *keys_dir, const char *bind_addr, int por
|
||||
if (!bind)
|
||||
tmate_fatal("Cannot initialize ssh");
|
||||
|
||||
#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
|
||||
/* Explicitly parse configuration to avoid automatic configuration file
|
||||
* loading which could override options */
|
||||
ssh_bind_options_parse_config(bind, NULL);
|
||||
#endif
|
||||
|
||||
if (bind_addr)
|
||||
ssh_bind_options_set(bind, SSH_BIND_OPTIONS_BINDADDR, bind_addr);
|
||||
ssh_bind_options_set(bind, SSH_BIND_OPTIONS_BINDPORT, &port);
|
||||
ssh_bind_options_set(bind, SSH_BIND_OPTIONS_BANNER, TMATE_SSH_BANNER);
|
||||
ssh_bind_options_set(bind, SSH_BIND_OPTIONS_LOG_VERBOSITY, &ssh_log_level);
|
||||
|
||||
sprintf(buffer, "%s/ssh_host_rsa_key", keys_dir);
|
||||
ssh_pki_import_privkey_file(buffer, NULL, NULL, NULL, &rsakey);
|
||||
ssh_bind_options_set(bind, SSH_BIND_OPTIONS_IMPORT_KEY, rsakey);
|
||||
|
||||
sprintf(buffer, "%s/ssh_host_ed25519_key", keys_dir);
|
||||
ssh_pki_import_privkey_file(buffer, NULL, NULL, NULL, &ed25519key);
|
||||
ssh_bind_options_set(bind, SSH_BIND_OPTIONS_IMPORT_KEY, ed25519key);
|
||||
ssh_import_key(bind, keys_dir, "ssh_host_rsa_key");
|
||||
ssh_import_key(bind, keys_dir, "ssh_host_ed25519_key");
|
||||
|
||||
if (ssh_bind_listen(bind) < 0)
|
||||
tmate_fatal("Error listening to socket: %s\n", ssh_get_error(bind));
|
||||
tmate_fatal("Error listening to socket: %s", ssh_get_error(bind));
|
||||
|
||||
tmate_notice("Accepting connections on %s:%d", bind_addr ?: "", port);
|
||||
tmate_info("Accepting connections on %s:%d", bind_addr ?: "", port);
|
||||
|
||||
return bind;
|
||||
}
|
||||
@@ -367,67 +438,75 @@ static void handle_sigchld(__unused int sig)
|
||||
while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) {
|
||||
/*
|
||||
* It's not safe to call indirectly malloc() here, because
|
||||
* of potential deadlocks with ssh_bind_accept() which also
|
||||
* calls malloc(). (And we can't even block singals because
|
||||
* the accept() call is blocking.
|
||||
* of potential deadlocks with other malloc() calls.
|
||||
*/
|
||||
#if 0
|
||||
if (WIFEXITED(status))
|
||||
tmate_info("Child %d exited (%d)", pid, WEXITSTATUS(status));
|
||||
tmate_debug("Child %d exited (%d)", pid, WEXITSTATUS(status));
|
||||
if (WIFSIGNALED(status))
|
||||
tmate_info("Child %d killed (%d)", pid, WTERMSIG(status));
|
||||
tmate_debug("Child %d killed (%d)", pid, WTERMSIG(status));
|
||||
if (WIFSTOPPED(status))
|
||||
tmate_info("Child %d stopped (%d)", pid, WSTOPSIG(status));
|
||||
tmate_debug("Child %d stopped (%d)", pid, WSTOPSIG(status));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_sigsegv(__unused int sig)
|
||||
{
|
||||
tmate_info("CRASH, printing stack trace");
|
||||
tmate_print_stack_trace();
|
||||
tmate_fatal("CRASHED");
|
||||
}
|
||||
|
||||
void tmate_ssh_server_main(struct tmate_session *session, const char *keys_dir,
|
||||
const char *bind_addr, int port)
|
||||
{
|
||||
struct tmate_ssh_client *client = &session->ssh_client;
|
||||
ssh_bind bind;
|
||||
pid_t pid;
|
||||
int fd;
|
||||
|
||||
signal(SIGSEGV, handle_sigsegv);
|
||||
tmate_catch_sigsegv();
|
||||
signal(SIGCHLD, handle_sigchld);
|
||||
|
||||
bind = prepare_ssh(keys_dir, bind_addr, port);
|
||||
|
||||
client->session = ssh_new();
|
||||
client->channel = NULL;
|
||||
client->winsize_pty.ws_col = 80;
|
||||
client->winsize_pty.ws_row = 24;
|
||||
session->session_token = "init";
|
||||
|
||||
if (!client->session)
|
||||
tmate_fatal("Cannot initialize session");
|
||||
|
||||
for (;;) {
|
||||
client->session = ssh_new();
|
||||
client->channel = NULL;
|
||||
client->winsize_pty.ws_col = 80;
|
||||
client->winsize_pty.ws_row = 24;
|
||||
|
||||
if (!client->session)
|
||||
tmate_fatal("Cannot initialize session");
|
||||
|
||||
if (ssh_bind_accept(bind, client->session) < 0)
|
||||
tmate_fatal("Error accepting connection: %s", ssh_get_error(bind));
|
||||
|
||||
if (get_ip(ssh_get_fd(client->session),
|
||||
client->ip_address, sizeof(client->ip_address)) < 0)
|
||||
tmate_fatal("Error getting IP address from connection");
|
||||
fd = accept(ssh_bind_get_fd(bind), NULL, NULL);
|
||||
if (fd < 0)
|
||||
tmate_fatal("Error accepting connection");
|
||||
|
||||
if ((pid = fork()) < 0)
|
||||
tmate_fatal("Can't fork");
|
||||
|
||||
if (pid) {
|
||||
tmate_info("Child spawned pid=%d, ip=%s",
|
||||
pid, client->ip_address);
|
||||
ssh_free(client->session);
|
||||
} else {
|
||||
ssh_bind_free(bind);
|
||||
session->session_token = "init";
|
||||
client_bootstrap(session);
|
||||
/* Parent process */
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Child process */
|
||||
|
||||
signal(SIGALRM, handle_sigalrm);
|
||||
alarm(TMATE_SSH_GRACE_PERIOD);
|
||||
|
||||
if (get_client_ip(fd, client->ip_address, sizeof(client->ip_address)) < 0) {
|
||||
if (tmate_settings->use_proxy_protocol)
|
||||
tmate_fatal("Proxy header invalid. Load balancer may be misconfigured");
|
||||
else
|
||||
tmate_fatal("Error getting client IP from connection");
|
||||
}
|
||||
|
||||
tmate_debug("Connection accepted ip=%s", client->ip_address);
|
||||
|
||||
if (ssh_bind_accept_fd(bind, client->session, fd) < 0)
|
||||
tmate_fatal("Error accepting connection: %s", ssh_get_error(bind));
|
||||
|
||||
ssh_bind_free(bind);
|
||||
|
||||
client_bootstrap(session);
|
||||
/* never reached */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,9 +9,21 @@
|
||||
#include "tmate.h"
|
||||
#include "tmate-protocol.h"
|
||||
|
||||
/*
|
||||
* The websocket refers to the websocket server.
|
||||
* (https://github.com/tmate-io/tmate-websocket)
|
||||
*/
|
||||
|
||||
#define CONTROL_PROTOCOL_VERSION 2
|
||||
|
||||
#define pack(what, ...) _pack(&tmate_session->proxy_encoder, what, __VA_ARGS__)
|
||||
#define pack(what, ...) _pack(&tmate_session->websocket_encoder, what, ##__VA_ARGS__)
|
||||
|
||||
#define pack_string_or_nil(str) ({ \
|
||||
if (str) \
|
||||
pack(string, str); \
|
||||
else \
|
||||
pack(nil); \
|
||||
})
|
||||
|
||||
static void ctl_daemon_fwd_msg(__unused struct tmate_session *session,
|
||||
struct tmate_unpacker *uk)
|
||||
@@ -143,8 +155,8 @@ static void ctl_pane_keys(__unused struct tmate_session *session,
|
||||
static void ctl_resize(struct tmate_session *session,
|
||||
struct tmate_unpacker *uk)
|
||||
{
|
||||
session->proxy_sx = (u_int)unpack_int(uk);
|
||||
session->proxy_sy = (u_int)unpack_int(uk);
|
||||
session->websocket_sx = (u_int)unpack_int(uk);
|
||||
session->websocket_sy = (u_int)unpack_int(uk);
|
||||
recalculate_sizes();
|
||||
}
|
||||
|
||||
@@ -173,8 +185,8 @@ static void ctl_rename_session(struct tmate_session *session,
|
||||
free(stoken_ro);
|
||||
}
|
||||
|
||||
static void tmate_dispatch_proxy_message(struct tmate_session *session,
|
||||
struct tmate_unpacker *uk)
|
||||
static void tmate_dispatch_websocket_message(struct tmate_session *session,
|
||||
struct tmate_unpacker *uk)
|
||||
{
|
||||
int cmd = unpack_int(uk);
|
||||
switch (cmd) {
|
||||
@@ -185,31 +197,31 @@ static void tmate_dispatch_proxy_message(struct tmate_session *session,
|
||||
dispatch(TMATE_CTL_RESIZE, ctl_resize);
|
||||
dispatch(TMATE_CTL_EXEC_RESPONSE, ctl_ssh_exec_response);
|
||||
dispatch(TMATE_CTL_RENAME_SESSION, ctl_rename_session);
|
||||
default: tmate_warn("Bad proxy message type: %d", cmd);
|
||||
default: tmate_info("Bad websocket server message type: %d", cmd);
|
||||
}
|
||||
}
|
||||
|
||||
void tmate_proxy_exec(struct tmate_session *session, const char *command)
|
||||
void tmate_websocket_exec(struct tmate_session *session, const char *command)
|
||||
{
|
||||
struct tmate_ssh_client *client = &session->ssh_client;
|
||||
|
||||
if (!tmate_has_proxy())
|
||||
if (!tmate_has_websocket())
|
||||
return;
|
||||
|
||||
pack(array, 5);
|
||||
pack(int, TMATE_CTL_EXEC);
|
||||
pack(string, client->username);
|
||||
pack(string, client->ip_address);
|
||||
pack(string, client->pubkey);
|
||||
pack_string_or_nil(client->pubkey);
|
||||
pack(string, command);
|
||||
}
|
||||
|
||||
void tmate_notify_client_join(__unused struct tmate_session *session,
|
||||
struct client *c)
|
||||
{
|
||||
tmate_notice("Client joined (cid=%d)", c->id);
|
||||
tmate_info("Client joined (cid=%d)", c->id);
|
||||
|
||||
if (!tmate_has_proxy())
|
||||
if (!tmate_has_websocket())
|
||||
return;
|
||||
|
||||
c->flags |= CLIENT_TMATE_NOTIFIED_JOIN;
|
||||
@@ -218,16 +230,19 @@ void tmate_notify_client_join(__unused struct tmate_session *session,
|
||||
pack(int, TMATE_CTL_CLIENT_JOIN);
|
||||
pack(int, c->id);
|
||||
pack(string, c->ip_address);
|
||||
pack(string, c->pubkey);
|
||||
pack_string_or_nil(c->pubkey);
|
||||
pack(boolean, c->readonly);
|
||||
}
|
||||
|
||||
void tmate_notify_client_left(__unused struct tmate_session *session,
|
||||
struct client *c)
|
||||
{
|
||||
tmate_notice("Client left (cid=%d)", c->id);
|
||||
if (!(c->flags & CLIENT_IDENTIFIED))
|
||||
return;
|
||||
|
||||
if (!tmate_has_proxy())
|
||||
tmate_info("Client left (cid=%d)", c->id);
|
||||
|
||||
if (!tmate_has_websocket())
|
||||
return;
|
||||
|
||||
if (!(c->flags & CLIENT_TMATE_NOTIFIED_JOIN))
|
||||
@@ -240,29 +255,12 @@ void tmate_notify_client_left(__unused struct tmate_session *session,
|
||||
pack(int, c->id);
|
||||
}
|
||||
|
||||
void tmate_notify_latency(__unused struct tmate_session *session,
|
||||
struct client *c, int latency_ms)
|
||||
{
|
||||
int cid;
|
||||
|
||||
if (!tmate_has_proxy())
|
||||
return;
|
||||
|
||||
cid = c ? c->id : -1;
|
||||
tmate_debug("Client latency (cid=%d): %dms", cid, latency_ms);
|
||||
|
||||
pack(array, 3);
|
||||
pack(int, TMATE_CTL_LATENCY);
|
||||
pack(int, cid);
|
||||
pack(int, latency_ms);
|
||||
}
|
||||
|
||||
void tmate_send_proxy_daemon_msg(__unused struct tmate_session *session,
|
||||
struct tmate_unpacker *uk)
|
||||
void tmate_send_websocket_daemon_msg(__unused struct tmate_session *session,
|
||||
struct tmate_unpacker *uk)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!tmate_has_proxy())
|
||||
if (!tmate_has_websocket())
|
||||
return;
|
||||
|
||||
pack(array, 2);
|
||||
@@ -273,114 +271,118 @@ void tmate_send_proxy_daemon_msg(__unused struct tmate_session *session,
|
||||
pack(object, uk->argv[i]);
|
||||
}
|
||||
|
||||
void tmate_send_proxy_header(struct tmate_session *session)
|
||||
void tmate_send_websocket_header(struct tmate_session *session)
|
||||
{
|
||||
char port_arg[16] = {0};
|
||||
char ssh_cmd_fmt[512];
|
||||
|
||||
if (!tmate_has_proxy())
|
||||
if (!tmate_has_websocket())
|
||||
return;
|
||||
|
||||
pack(array, 9);
|
||||
pack(int, TMATE_CTL_HEADER);
|
||||
pack(int, CONTROL_PROTOCOL_VERSION);
|
||||
pack(string, session->ssh_client.ip_address);
|
||||
pack(string, session->ssh_client.pubkey);
|
||||
pack_string_or_nil(session->ssh_client.pubkey);
|
||||
pack(string, session->session_token);
|
||||
pack(string, session->session_token_ro);
|
||||
|
||||
if (tmate_settings->ssh_port != 22)
|
||||
sprintf(port_arg, " -p%d", tmate_settings->ssh_port);
|
||||
sprintf(ssh_cmd_fmt, "ssh%s %%s@%s", port_arg, tmate_settings->tmate_host);
|
||||
char *ssh_cmd_fmt = get_ssh_conn_string("%s");
|
||||
pack(string, ssh_cmd_fmt);
|
||||
free(ssh_cmd_fmt);
|
||||
|
||||
pack(string, session->client_version);
|
||||
pack(int, session->client_protocol_version);
|
||||
}
|
||||
|
||||
static void on_proxy_decoder_read(void *userdata, struct tmate_unpacker *uk)
|
||||
static void on_websocket_decoder_read(void *userdata, struct tmate_unpacker *uk)
|
||||
{
|
||||
struct tmate_session *session = userdata;
|
||||
tmate_dispatch_proxy_message(session, uk);
|
||||
tmate_dispatch_websocket_message(session, uk);
|
||||
}
|
||||
|
||||
static void on_proxy_read(__unused struct bufferevent *bev, void *_session)
|
||||
static void on_websocket_read(__unused struct bufferevent *bev, void *_session)
|
||||
{
|
||||
struct tmate_session *session = _session;
|
||||
struct evbuffer *proxy_in;
|
||||
struct evbuffer *websocket_in;
|
||||
ssize_t written;
|
||||
char *buf;
|
||||
size_t len;
|
||||
|
||||
proxy_in = bufferevent_get_input(session->bev_proxy);
|
||||
websocket_in = bufferevent_get_input(session->bev_websocket);
|
||||
|
||||
while (evbuffer_get_length(proxy_in)) {
|
||||
tmate_decoder_get_buffer(&session->proxy_decoder, &buf, &len);
|
||||
while (evbuffer_get_length(websocket_in)) {
|
||||
tmate_decoder_get_buffer(&session->websocket_decoder, &buf, &len);
|
||||
|
||||
if (len == 0)
|
||||
tmate_fatal("No more room in client decoder. Message too big?");
|
||||
|
||||
written = evbuffer_remove(proxy_in, buf, len);
|
||||
written = evbuffer_remove(websocket_in, buf, len);
|
||||
if (written < 0)
|
||||
tmate_fatal("Cannot read proxy buffer");
|
||||
tmate_fatal("Cannot read websocket buffer");
|
||||
|
||||
tmate_decoder_commit(&session->proxy_decoder, written);
|
||||
tmate_decoder_commit(&session->websocket_decoder, written);
|
||||
}
|
||||
}
|
||||
|
||||
static void on_proxy_encoder_write(void *userdata, struct evbuffer *buffer)
|
||||
static void on_websocket_encoder_write(void *userdata, struct evbuffer *buffer)
|
||||
{
|
||||
struct tmate_session *session = userdata;
|
||||
struct evbuffer *proxy_out;
|
||||
struct evbuffer *websocket_out;
|
||||
|
||||
proxy_out = bufferevent_get_output(session->bev_proxy);
|
||||
websocket_out = bufferevent_get_output(session->bev_websocket);
|
||||
|
||||
if (evbuffer_add_buffer(proxy_out, buffer) < 0)
|
||||
tmate_fatal("Cannot write to proxy buffer");
|
||||
if (evbuffer_add_buffer(websocket_out, buffer) < 0)
|
||||
tmate_fatal("Cannot write to websocket server buffer");
|
||||
}
|
||||
|
||||
static void on_proxy_event_default(__unused struct tmate_session *session, short events)
|
||||
static void on_websocket_event_default(__unused struct tmate_session *session, short events)
|
||||
{
|
||||
if (events & BEV_EVENT_EOF)
|
||||
tmate_fatal("Connection to proxy closed");
|
||||
if (events & BEV_EVENT_EOF) {
|
||||
if (session->fin_received) {
|
||||
/*
|
||||
* This is expected. The websocket will close the
|
||||
* connection upon receiving the fin message.
|
||||
*/
|
||||
exit(0);
|
||||
}
|
||||
tmate_fatal("Connection to websocket server closed");
|
||||
}
|
||||
|
||||
if (events & BEV_EVENT_ERROR)
|
||||
tmate_fatal("Connection to proxy error: %s",
|
||||
tmate_fatal("Connection to websocket server error: %s",
|
||||
evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR()));
|
||||
}
|
||||
|
||||
static void on_proxy_event(__unused struct bufferevent *bev, short events, void *_session)
|
||||
static void on_websocket_event(__unused struct bufferevent *bev, short events, void *_session)
|
||||
{
|
||||
struct tmate_session *session = _session;
|
||||
session->on_proxy_error(session, events);
|
||||
session->on_websocket_error(session, events);
|
||||
}
|
||||
|
||||
void tmate_init_proxy(struct tmate_session *session,
|
||||
on_proxy_error_cb on_proxy_error)
|
||||
void tmate_init_websocket(struct tmate_session *session,
|
||||
on_websocket_error_cb on_websocket_error)
|
||||
{
|
||||
if (!tmate_has_proxy())
|
||||
if (!tmate_has_websocket())
|
||||
return;
|
||||
|
||||
session->proxy_sx = -1;
|
||||
session->proxy_sy = -1;
|
||||
session->websocket_sx = -1;
|
||||
session->websocket_sy = -1;
|
||||
|
||||
/* session->proxy_fd is already connected */
|
||||
session->bev_proxy = bufferevent_socket_new(session->ev_base, session->proxy_fd,
|
||||
/* session->websocket_fd is already connected */
|
||||
session->bev_websocket = bufferevent_socket_new(session->ev_base, session->websocket_fd,
|
||||
BEV_OPT_CLOSE_ON_FREE);
|
||||
if (!session->bev_proxy)
|
||||
if (!session->bev_websocket)
|
||||
tmate_fatal("Cannot setup socket bufferevent");
|
||||
|
||||
session->on_proxy_error = on_proxy_error ?: on_proxy_event_default;
|
||||
session->on_websocket_error = on_websocket_error ?: on_websocket_event_default;
|
||||
|
||||
bufferevent_setcb(session->bev_proxy,
|
||||
on_proxy_read, NULL, on_proxy_event, session);
|
||||
bufferevent_enable(session->bev_proxy, EV_READ | EV_WRITE);
|
||||
bufferevent_setcb(session->bev_websocket,
|
||||
on_websocket_read, NULL, on_websocket_event, session);
|
||||
bufferevent_enable(session->bev_websocket, 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_encoder_init(&session->websocket_encoder, on_websocket_encoder_write, session);
|
||||
tmate_decoder_init(&session->websocket_decoder, on_websocket_decoder_read, session);
|
||||
}
|
||||
|
||||
static int _tmate_connect_to_proxy(const char *hostname, int port)
|
||||
static int _tmate_connect_to_websocket(const char *hostname, int port)
|
||||
{
|
||||
int sockfd = -1;
|
||||
struct sockaddr_in servaddr;
|
||||
@@ -400,24 +402,24 @@ static int _tmate_connect_to_proxy(const char *hostname, int port)
|
||||
servaddr.sin_port = htons(port);
|
||||
|
||||
if (connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
|
||||
tmate_fatal("Cannot connect to proxy at %s:%d", hostname, port);
|
||||
tmate_fatal("Cannot connect to websocket server at %s:%d", hostname, port);
|
||||
|
||||
{
|
||||
int flag = 1;
|
||||
if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag)) < 0)
|
||||
tmate_fatal("Can't set proxy socket to TCP_NODELAY");
|
||||
tmate_fatal("Can't set websocket server socket to TCP_NODELAY");
|
||||
}
|
||||
|
||||
if (fcntl(sockfd, F_SETFL, O_NONBLOCK) < 0)
|
||||
tmate_fatal("Can't set proxy socket to non-blocking");
|
||||
tmate_fatal("Can't set websocket server socket to non-blocking");
|
||||
|
||||
tmate_notice("Connected to proxy at %s:%d", hostname, port);
|
||||
tmate_debug("Connected to websocket server at %s:%d", hostname, port);
|
||||
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
int tmate_connect_to_proxy(void)
|
||||
int tmate_connect_to_websocket(void)
|
||||
{
|
||||
return _tmate_connect_to_proxy(tmate_settings->proxy_hostname,
|
||||
tmate_settings->proxy_port);
|
||||
return _tmate_connect_to_websocket(tmate_settings->websocket_hostname,
|
||||
tmate_settings->websocket_port);
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
#ifndef TMATE_H
|
||||
#define TMATE_H
|
||||
|
||||
#include <sys/syslog.h>
|
||||
#include <sys/types.h>
|
||||
#include <msgpack.h>
|
||||
#include <libssh/libssh.h>
|
||||
@@ -14,18 +13,16 @@ struct tmate_session;
|
||||
|
||||
/* log.c */
|
||||
|
||||
extern void init_logging(const char *program_name, bool use_syslog, int log_level);
|
||||
extern void printflike(2, 3) tmate_log(int level, const char *msg, ...);
|
||||
#define tmate_debug(...) log_emit(LOG_DEBUG, __VA_ARGS__)
|
||||
#define tmate_info(...) log_emit(LOG_INFO, __VA_ARGS__)
|
||||
#define tmate_fatal(...) fatalx( __VA_ARGS__)
|
||||
#define tmate_fatal_quiet(...) ({tmate_debug(__VA_ARGS__); exit(1);})
|
||||
|
||||
#define tmate_debug(str, ...) tmate_log(LOG_DEBUG, str, ##__VA_ARGS__)
|
||||
#define tmate_info(str, ...) tmate_log(LOG_INFO, str, ##__VA_ARGS__)
|
||||
#define tmate_notice(str, ...) tmate_log(LOG_NOTICE, str, ##__VA_ARGS__)
|
||||
#define tmate_warn(str, ...) tmate_log(LOG_WARNING, str, ##__VA_ARGS__)
|
||||
#define tmate_fatal(str, ...) \
|
||||
({ \
|
||||
tmate_log(LOG_CRIT, "fatal: " str, ##__VA_ARGS__); \
|
||||
exit(1); \
|
||||
})
|
||||
/* tmate-auth-keys.c */
|
||||
extern void tmate_hook_set_option_auth(const char *name, const char *val);
|
||||
extern bool tmate_allow_auth(const char *pubkey);
|
||||
extern bool would_tmate_session_allow_auth(const char *token, const char *pubkey);
|
||||
extern int get_num_authorized_keys(ssh_key *keys);
|
||||
|
||||
/* tmate-msgpack.c */
|
||||
|
||||
@@ -51,7 +48,7 @@ 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
|
||||
|
||||
#define _pack(enc, what, ...) msgpack_pack_##what(&(enc)->pk, __VA_ARGS__)
|
||||
#define _pack(enc, what, ...) msgpack_pack_##what(&(enc)->pk, ##__VA_ARGS__)
|
||||
|
||||
struct tmate_unpacker;
|
||||
struct tmate_decoder;
|
||||
@@ -111,6 +108,8 @@ extern void tmate_translate_legacy_key(int pane_id, key_code key);
|
||||
|
||||
#define TMATE_HLIMIT 2000
|
||||
#define TMATE_PANE_ACTIVE 1
|
||||
#define TMATE_MAX_MESSAGE_SIZE (17*1024)
|
||||
#define TMATE_MAX_PANES 1000
|
||||
|
||||
extern char *tmate_left_status, *tmate_right_status;
|
||||
extern void tmate_dispatch_daemon_message(struct tmate_session *session,
|
||||
@@ -121,27 +120,22 @@ extern void tmate_dispatch_daemon_message(struct tmate_session *session,
|
||||
#define TMATE_KEYFRAME_INTERVAL_SEC 10
|
||||
#define TMATE_KEYFRAME_MAX_SIZE 1024*1024
|
||||
|
||||
extern void tmate_daemon_init(struct tmate_session *session);
|
||||
extern void tmate_spawn_daemon(struct tmate_session *session);
|
||||
extern void tmate_hook_set_option(const char *name, const char *val);
|
||||
|
||||
/* tmate-ssh-exec.c */
|
||||
|
||||
extern void tmate_spawn_exec(struct tmate_session *session);
|
||||
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);
|
||||
extern void tmate_flush_pty(struct tmate_session *session);
|
||||
extern void tmate_spawn_pty_client(struct tmate_session *session);
|
||||
extern int tmate_validate_session_token(const char *token);
|
||||
|
||||
/* tmate-ssh-server.c */
|
||||
|
||||
#define TMATE_SSH_BANNER "tmate"
|
||||
#ifdef ENABLE_LATENCY
|
||||
#define TMATE_SSH_KEEPALIVE 10
|
||||
#else
|
||||
#define TMATE_SSH_KEEPALIVE 50
|
||||
#endif
|
||||
#define TMATE_SSH_KEEPALIVE_SEC 300
|
||||
|
||||
#define TMATE_ROLE_DAEMON 1
|
||||
#define TMATE_ROLE_PTY_CLIENT 2
|
||||
@@ -149,11 +143,8 @@ extern void tmate_flush_pty(struct tmate_session *session);
|
||||
|
||||
struct tmate_ssh_client;
|
||||
typedef void ssh_client_latency_cb(void *userdata, int latency_ms);
|
||||
extern void tmate_start_ssh_latency_probes(struct tmate_ssh_client *client,
|
||||
struct ssh_server_callbacks_struct *server_callbacks,
|
||||
int keepalive_interval_ms);
|
||||
extern void tmate_add_ssh_latency_callback(struct tmate_ssh_client *client,
|
||||
ssh_client_latency_cb cb, void *userdata);
|
||||
extern char *get_ssh_conn_string(const char *session_token);
|
||||
extern void start_keepalive_timer(struct tmate_ssh_client *client, int timeout_ms);
|
||||
|
||||
struct tmate_ssh_client {
|
||||
char ip_address[64];
|
||||
@@ -179,11 +170,6 @@ struct tmate_ssh_client {
|
||||
|
||||
struct event ev_keepalive_timer;
|
||||
int keepalive_interval_ms;
|
||||
#ifdef ENABLE_LATENCY
|
||||
ssh_client_latency_cb *latency_cb;
|
||||
void *latency_cb_userdata;
|
||||
struct timespec keepalive_sent_at;
|
||||
#endif
|
||||
};
|
||||
|
||||
extern void tmate_ssh_server_main(struct tmate_session *session,
|
||||
@@ -197,11 +183,11 @@ extern void tmate_ssh_server_main(struct tmate_session *session,
|
||||
#define TMATE_SSH_DEFAULT_PORT 22
|
||||
#endif
|
||||
|
||||
#define TMATE_SSH_GRACE_PERIOD 60
|
||||
#define TMATE_SSH_GRACE_PERIOD 20
|
||||
|
||||
#define TMATE_SSH_DEFAULT_KEYS_DIR "keys"
|
||||
|
||||
#define TMATE_DEFAULT_PROXY_PORT 4002
|
||||
#define TMATE_DEFAULT_WEBSOCKET_PORT 4002
|
||||
|
||||
#define TMATE_TOKEN_LEN 25
|
||||
#define TMATE_WORKDIR "/tmp/tmate"
|
||||
@@ -211,16 +197,17 @@ struct tmate_settings {
|
||||
const char *keys_dir;
|
||||
const char *authorized_keys_path;
|
||||
int ssh_port;
|
||||
const char *proxy_hostname;
|
||||
int proxy_port;
|
||||
int ssh_port_advertized;
|
||||
const char *websocket_hostname;
|
||||
int websocket_port;
|
||||
const char *tmate_host;
|
||||
const char *bind_addr;
|
||||
int log_level;
|
||||
bool use_syslog;
|
||||
bool use_proxy_protocol;
|
||||
};
|
||||
extern struct tmate_settings *tmate_settings;
|
||||
|
||||
typedef void on_proxy_error_cb(struct tmate_session *session, short events);
|
||||
typedef void on_websocket_error_cb(struct tmate_session *session, short events);
|
||||
|
||||
struct tmate_session {
|
||||
struct event_base *ev_base;
|
||||
@@ -228,21 +215,25 @@ struct tmate_session {
|
||||
int tmux_socket_fd;
|
||||
|
||||
/* only for role deamon */
|
||||
ssh_key *authorized_keys; /* array with NULL as last element */
|
||||
|
||||
const char *session_token;
|
||||
const char *session_token_ro;
|
||||
const char *obfuscated_session_token; /* for logging purposes */
|
||||
|
||||
struct tmate_encoder daemon_encoder;
|
||||
struct tmate_decoder daemon_decoder;
|
||||
const char *client_version;
|
||||
int client_protocol_version;
|
||||
struct event ev_notify_timer;
|
||||
bool fin_received;
|
||||
|
||||
int proxy_fd;
|
||||
struct bufferevent *bev_proxy;
|
||||
struct tmate_encoder proxy_encoder;
|
||||
struct tmate_decoder proxy_decoder;
|
||||
u_int proxy_sx, proxy_sy;
|
||||
on_proxy_error_cb *on_proxy_error;
|
||||
int websocket_fd;
|
||||
struct bufferevent *bev_websocket;
|
||||
struct tmate_encoder websocket_encoder;
|
||||
struct tmate_decoder websocket_decoder;
|
||||
u_int websocket_sx, websocket_sy;
|
||||
on_websocket_error_cb *on_websocket_error;
|
||||
|
||||
/* only for role client-pty */
|
||||
int pty;
|
||||
@@ -259,35 +250,63 @@ extern struct tmate_session *tmate_session;
|
||||
extern void tmate_get_random_bytes(void *buffer, ssize_t len);
|
||||
extern long tmate_get_random_long(void);
|
||||
extern void request_server_termination(void);
|
||||
extern void tmate_spawn(struct tmate_session *session);
|
||||
extern char *get_socket_path(const char *_token);
|
||||
extern void set_session_token(struct tmate_session *session, const char *token);
|
||||
|
||||
/* tmate-proxy.c */
|
||||
extern void close_fds_except(int *fd_to_preserve, int num_fds);
|
||||
extern void get_in_jail(void);
|
||||
|
||||
extern void tmate_proxy_exec(struct tmate_session *session, const char *command);
|
||||
/* tmate-rand.c */
|
||||
#define RS_BUF_SIZE 256
|
||||
|
||||
struct random_stream {
|
||||
char bytes[RS_BUF_SIZE];
|
||||
off_t pos;
|
||||
};
|
||||
|
||||
extern void tmate_init_rand(void);
|
||||
extern void tmate_get_random_bytes(void *buffer, ssize_t len);
|
||||
extern long tmate_get_random_long(void);
|
||||
extern void random_stream_init(struct random_stream *rs);
|
||||
extern char *random_stream_get(struct random_stream *rs, size_t count);
|
||||
extern void setup_ncurse(int fd, const char *name);
|
||||
|
||||
/* tmate-websocket.c */
|
||||
|
||||
extern void tmate_websocket_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_notify_latency(struct tmate_session *session, struct client *c, int latency_ms);
|
||||
|
||||
extern void tmate_send_proxy_daemon_msg(struct tmate_session *session,
|
||||
extern void tmate_send_websocket_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(struct tmate_session *session,
|
||||
on_proxy_error_cb on_proxy_error);
|
||||
extern void tmate_send_websocket_header(struct tmate_session *session);
|
||||
extern void tmate_init_websocket(struct tmate_session *session,
|
||||
on_websocket_error_cb on_websocket_error);
|
||||
|
||||
extern int tmate_connect_to_proxy(void);
|
||||
static inline bool tmate_has_proxy(void)
|
||||
extern int tmate_connect_to_websocket(void);
|
||||
static inline bool tmate_has_websocket(void)
|
||||
{
|
||||
return !!tmate_settings->proxy_hostname;
|
||||
return !!tmate_settings->websocket_hostname;
|
||||
}
|
||||
|
||||
/* tmate-debug.c */
|
||||
|
||||
extern void tmate_preload_trace_lib(void);
|
||||
extern void tmate_print_stack_trace(void);
|
||||
extern void tmate_catch_sigsegv(void);
|
||||
|
||||
/* tmux.c */
|
||||
|
||||
extern void tmux_server_init(void);
|
||||
|
||||
/* server.c */
|
||||
extern int server_create_socket(void);
|
||||
|
||||
/* log.c */
|
||||
extern FILE *log_file;
|
||||
|
||||
/* client.c */
|
||||
extern void client_signal(int sig);
|
||||
extern int client_connect(struct event_base *base, const char *path, int start_server);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -44,7 +44,7 @@ struct hooks *global_hooks;
|
||||
struct timeval start_time;
|
||||
const char *socket_path;
|
||||
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
__dead void usage(void);
|
||||
static char *make_label(const char *);
|
||||
|
||||
@@ -167,7 +167,7 @@ setblocking(int fd, int state)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
const char * find_home(void) { return NULL; }
|
||||
int areshell(__unused const char *shell) { return 0; }
|
||||
#else
|
||||
@@ -350,7 +350,7 @@ main(int argc, char **argv)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
void tmux_server_init(void)
|
||||
{
|
||||
global_hooks = hooks_create(NULL);
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#ifndef TMUX_H
|
||||
#define TMUX_H
|
||||
|
||||
#define TMATE_SLAVE
|
||||
#define TMATE
|
||||
|
||||
#define PROTOCOL_VERSION 8
|
||||
|
||||
@@ -414,13 +414,6 @@ enum msgtype {
|
||||
MSG_IDENTIFY_CLIENTPID,
|
||||
MSG_IDENTIFY_CWD,
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
/* Next time put this after TMATE_LATENCY */
|
||||
MSG_IDENTIFY_TMATE_IP_ADDRESS,
|
||||
MSG_IDENTIFY_TMATE_PUBKEY,
|
||||
MSG_IDENTIFY_TMATE_READONLY,
|
||||
#endif
|
||||
|
||||
MSG_COMMAND = 200,
|
||||
MSG_DETACH,
|
||||
MSG_DETACHKILL,
|
||||
@@ -439,8 +432,13 @@ enum msgtype {
|
||||
MSG_UNLOCK,
|
||||
MSG_WAKEUP,
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
MSG_LATENCY = 300
|
||||
#ifdef TMATE
|
||||
MSG_LATENCY = 300,
|
||||
MSG_IDENTIFY_TMATE_IP_ADDRESS,
|
||||
MSG_IDENTIFY_TMATE_AUTH_NONE,
|
||||
MSG_IDENTIFY_TMATE_AUTH_PUBKEY,
|
||||
MSG_IDENTIFY_TMATE_READONLY,
|
||||
MSG_TMATE_AUTH_STATUS,
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -873,7 +871,7 @@ struct window_pane {
|
||||
|
||||
struct window *window;
|
||||
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
struct layout_cell *layout_cell;
|
||||
struct layout_cell *saved_layout_cell;
|
||||
#endif
|
||||
@@ -892,7 +890,7 @@ struct window_pane {
|
||||
#define PANE_FOCUSPUSH 0x10
|
||||
#define PANE_INPUTOFF 0x20
|
||||
#define PANE_CHANGED 0x40
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
#define PANE_KILL 0x80
|
||||
#endif
|
||||
|
||||
@@ -905,7 +903,7 @@ struct window_pane {
|
||||
char tty[TTY_NAME_MAX];
|
||||
int status;
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
struct evbuffer *event_input;
|
||||
#else
|
||||
int fd;
|
||||
@@ -957,7 +955,7 @@ struct window {
|
||||
|
||||
int lastlayout;
|
||||
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
struct layout_cell *layout_root;
|
||||
struct layout_cell *saved_layout_root;
|
||||
char *old_layout;
|
||||
@@ -975,7 +973,7 @@ struct window {
|
||||
#define WINDOW_FORCEWIDTH 0x2000
|
||||
#define WINDOW_FORCEHEIGHT 0x4000
|
||||
#define WINDOW_ALERTFLAGS (WINDOW_BELL|WINDOW_ACTIVITY|WINDOW_SILENCE)
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
#define WINDOW_KILL 0x8000
|
||||
#endif
|
||||
|
||||
@@ -1001,7 +999,7 @@ struct winlink {
|
||||
#define WINLINK_ACTIVITY 0x2
|
||||
#define WINLINK_SILENCE 0x4
|
||||
#define WINLINK_ALERTFLAGS (WINLINK_BELL|WINLINK_ACTIVITY|WINLINK_SILENCE)
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
#define WINLINK_KILL 0x80
|
||||
#endif
|
||||
|
||||
@@ -1251,7 +1249,7 @@ struct message_entry {
|
||||
|
||||
/* Client connection. */
|
||||
struct client {
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
int id;
|
||||
#endif
|
||||
struct tmuxpeer *peer;
|
||||
@@ -1305,8 +1303,9 @@ struct client {
|
||||
#define CLIENT_256COLOURS 0x20000
|
||||
#define CLIENT_IDENTIFIED 0x40000
|
||||
#define CLIENT_STATUSFORCE 0x80000
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
#define CLIENT_TMATE_NOTIFIED_JOIN 0x10000000
|
||||
#define CLIENT_TMATE_AUTHENTICATED 0x20000000
|
||||
#endif
|
||||
int flags;
|
||||
struct key_table *keytable;
|
||||
@@ -1339,7 +1338,7 @@ struct client {
|
||||
struct cmd_q *cmdq;
|
||||
int references;
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
char *ip_address;
|
||||
char *pubkey;
|
||||
int readonly;
|
||||
@@ -2012,7 +2011,7 @@ void input_reset(struct window_pane *, int);
|
||||
struct evbuffer *input_pending(struct window_pane *);
|
||||
void input_parse(struct window_pane *);
|
||||
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
/* input-key.c */
|
||||
void input_key(struct window_pane *, key_code, struct mouse_event *);
|
||||
#endif
|
||||
@@ -2387,7 +2386,13 @@ void log_add_level(void);
|
||||
int log_get_level(void);
|
||||
void log_open(const char *);
|
||||
void log_close(void);
|
||||
void printflike(1, 2) log_debug(const char *, ...);
|
||||
#define LOG_ERROR 0
|
||||
#define LOG_INFO 1
|
||||
#define LOG_DEBUG 2
|
||||
#define log_debug(...) log_emit(LOG_DEBUG+1, __VA_ARGS__)
|
||||
void init_logging(int log_level);
|
||||
void set_log_prefix(char *_log_prefix);
|
||||
void printflike(2, 3) log_emit(int level, const char *, ...);
|
||||
__dead void printflike(1, 2) fatal(const char *, ...);
|
||||
__dead void printflike(1, 2) fatalx(const char *, ...);
|
||||
|
||||
|
||||
+2
-2
@@ -407,7 +407,7 @@ tty_term_find(char *name, int fd, char **cause)
|
||||
LIST_INSERT_HEAD(&tty_terms, term, entry);
|
||||
|
||||
/* we are in a jail, no access to files */
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
/* Set up curses terminal. */
|
||||
if (setupterm(name, fd, &error) != OK) {
|
||||
switch (error) {
|
||||
@@ -537,7 +537,7 @@ error:
|
||||
void
|
||||
tty_term_free(struct tty_term *term)
|
||||
{
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
/*
|
||||
* We need to keep the term in memory, because we are in a jail, with
|
||||
* no file system, so we can't reload them.
|
||||
|
||||
@@ -166,7 +166,7 @@ tty_init(struct tty *tty, struct client *c, int fd, char *term)
|
||||
tty->fd = fd;
|
||||
tty->client = c;
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
tty->path = NULL;
|
||||
#else
|
||||
if ((path = ttyname(fd)) == NULL)
|
||||
|
||||
+3
-3
@@ -61,7 +61,7 @@ window_copy_init(struct window_pane *wp)
|
||||
data->searchtype = WINDOW_COPY_OFF;
|
||||
data->searchstr = NULL;
|
||||
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
if (wp->fd != -1)
|
||||
bufferevent_disable(wp->event, EV_READ|EV_WRITE);
|
||||
#endif
|
||||
@@ -125,7 +125,7 @@ window_copy_free(struct window_pane *wp)
|
||||
{
|
||||
struct window_copy_mode_data *data = wp->modedata;
|
||||
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
if (wp->fd != -1)
|
||||
bufferevent_enable(wp->event, EV_READ|EV_WRITE);
|
||||
#endif
|
||||
@@ -1407,7 +1407,7 @@ window_copy_copy_pipe(struct window_pane *wp, struct session *sess,
|
||||
struct format_tree *ft;
|
||||
char *expanded;
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
/* that job_run() is not going to end well */
|
||||
return;
|
||||
#endif
|
||||
|
||||
@@ -297,7 +297,7 @@ window_create1(u_int sx, u_int sy)
|
||||
TAILQ_INIT(&w->panes);
|
||||
w->active = NULL;
|
||||
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
w->lastlayout = -1;
|
||||
w->layout_root = NULL;
|
||||
#endif
|
||||
@@ -327,7 +327,7 @@ window_create(const char *name, int argc, char **argv, const char *path,
|
||||
|
||||
w = window_create1(sx, sy);
|
||||
wp = window_add_pane(w, hlimit);
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
layout_init(w, wp);
|
||||
#endif
|
||||
|
||||
@@ -352,7 +352,7 @@ window_destroy(struct window *w)
|
||||
{
|
||||
RB_REMOVE(windows, &windows, w);
|
||||
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
if (w->layout_root != NULL)
|
||||
layout_free_cell(w->layout_root);
|
||||
if (w->saved_layout_root != NULL)
|
||||
@@ -523,7 +523,7 @@ window_zoom(struct window_pane *wp)
|
||||
if (w->active != wp)
|
||||
window_set_active_pane(w, wp);
|
||||
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
TAILQ_FOREACH(wp1, &w->panes, entry) {
|
||||
wp1->saved_layout_cell = wp1->layout_cell;
|
||||
wp1->layout_cell = NULL;
|
||||
@@ -548,7 +548,7 @@ window_unzoom(struct window *w)
|
||||
|
||||
w->flags &= ~WINDOW_ZOOMED;
|
||||
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
layout_free(w);
|
||||
w->layout_root = w->saved_layout_root;
|
||||
w->saved_layout_root = NULL;
|
||||
@@ -685,7 +685,7 @@ window_destroy_panes(struct window *w)
|
||||
}
|
||||
}
|
||||
|
||||
/* Retuns the printable flags on a window, empty string if no flags set. */
|
||||
/* Returns the printable flags on a window, empty string if no flags set. */
|
||||
char *
|
||||
window_printable_flags(struct session *s, struct winlink *wl)
|
||||
{
|
||||
@@ -752,7 +752,7 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
|
||||
wp->shell = NULL;
|
||||
wp->cwd = NULL;
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
wp->event_input = evbuffer_new();
|
||||
#else
|
||||
wp->fd = -1;
|
||||
@@ -761,7 +761,7 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
|
||||
|
||||
wp->mode = NULL;
|
||||
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
wp->layout_cell = NULL;
|
||||
#endif
|
||||
|
||||
@@ -798,7 +798,7 @@ window_pane_destroy(struct window_pane *wp)
|
||||
if (event_initialized(&wp->timer))
|
||||
evtimer_del(&wp->timer);
|
||||
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
evbuffer_free(wp->event_input);
|
||||
#else
|
||||
if (wp->fd != -1) {
|
||||
@@ -834,7 +834,7 @@ window_pane_spawn(struct window_pane *wp, int argc, char **argv,
|
||||
const char *path, const char *shell, const char *cwd, struct environ *env,
|
||||
struct termios *tio, char **cause)
|
||||
{
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
return 0;
|
||||
#else
|
||||
struct winsize ws;
|
||||
@@ -967,7 +967,7 @@ void
|
||||
window_pane_read_callback(__unused struct bufferevent *bufev, void *data)
|
||||
{
|
||||
struct window_pane *wp = data;
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
struct evbuffer *evb = wp->event_input;
|
||||
#else
|
||||
struct evbuffer *evb = wp->event->input;
|
||||
@@ -1154,7 +1154,7 @@ void
|
||||
window_pane_key(struct window_pane *wp, struct client *c, struct session *s,
|
||||
key_code key, struct mouse_event *m)
|
||||
{
|
||||
#ifdef TMATE_SLAVE
|
||||
#ifdef TMATE
|
||||
/*
|
||||
* pane_id == -1, which means that we should send the keys to
|
||||
* the active pane. This is better due to latency issues in
|
||||
@@ -1199,7 +1199,7 @@ window_pane_visible(struct window_pane *wp)
|
||||
{
|
||||
struct window *w = wp->window;
|
||||
|
||||
#ifndef TMATE_SLAVE
|
||||
#ifndef TMATE
|
||||
if (wp->layout_cell == NULL)
|
||||
return (0);
|
||||
#endif
|
||||
|
||||
@@ -94,6 +94,7 @@ xasprintf(char **ret, const char *fmt, ...)
|
||||
return i;
|
||||
}
|
||||
|
||||
__attribute__((__format__(__printf__, 2, 0)))
|
||||
int
|
||||
xvasprintf(char **ret, const char *fmt, va_list ap)
|
||||
{
|
||||
@@ -120,6 +121,7 @@ xsnprintf(char *str, size_t len, const char *fmt, ...)
|
||||
return i;
|
||||
}
|
||||
|
||||
__attribute__((__format__(__printf__, 3, 0)))
|
||||
int
|
||||
xvsnprintf(char *str, size_t len, const char *fmt, va_list ap)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user