50 Commits
Author SHA1 Message Date
Bjoern Jacke 47792184ca server: set IPTOS_LOWDELAY in TCP connections
this helps edge routers to prioritize our interactive network traffic.
2021-02-20 16:56:55 -05:00
Anderson Sasaki 97d20249be Explicitly parse config to avoid options override (#74)
Since libssh 0.9.0 the global server configuration file is automatically
parsed when new connections are accepted, unless
ssh_bind_options_parse_config() is explicitly called or the automatic
parsing is disabled by setting the SSH_BIND_OPTIONS_PROCESS_CONFIG
option with false.

This adds an explicit call to ssh_bind_options_parse_config() before
setting the options to avoid them to be overridden.

Fixes #72

Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
2020-04-27 19:54:26 -04:00
Klemens Nanni f0c32a14a4 Include missing <signal.h>
At least on OpenBSD, signal(3) requires this.
2020-04-14 18:13:49 -04:00
Klemens Nanni 54ea774265 Fix libssh set_options_set() usage
SSH_OPTIONS_TIMEOUT requires a long value as per upstream documentation.

Pointing to an integer instead causes it to crash with SIBGUS on
OpenBSD/sparc64.
2020-04-14 18:13:35 -04:00
Nicolas Viennot cb4c7a874d Quotes can be a bad idea (utf8 quotes) 2020-04-14 18:07:21 -04:00
Nicolas Viennot 43214648f2 Forgot the letter i in the valid_digits 2019-11-17 18:44:59 -05:00
Nicolas Viennot e1a5e08471 Rename account-key -> api-key 2019-11-10 22:28:08 -05:00
Nicolas Viennot ecc15f6022 Unify tmate-debug.c with tmate 2019-11-10 15:52:31 -05:00
Nicolas Viennot f132989294 nits 2019-11-10 15:52:31 -05:00
Nicolas Viennot 90c3b571d3 Log uname 2019-11-10 15:52:31 -05:00
Nicolas Viennot 1d26957b3b Incoming messages should be dispatched first before being forwarded
It's a cheap sanity check
2019-11-09 23:28:07 -05:00
Nicolas Viennot b97f8dc7d6 Cleanup logging 2019-11-09 23:28:07 -05:00
Nicolas Viennot 7f26d860b5 Cleanup warnings 2019-11-08 23:54:36 -05:00
Nicolas Viennot 975f60d2ac Rename TMATE_SLAVE -> TMATE 2019-11-08 23:54:36 -05:00
Nicolas Viennot 4b79571357 Display warnings when trying to use named sessions without the websocket server 2019-11-08 23:54:36 -05:00
Nicolas Viennot 6c41248dad Be more persmissive for session tokens (useful for named sessions) 2019-11-08 23:54:36 -05:00
Nicolas Viennot 7c1bc239af Shorten connection grace period to 20s 2019-11-07 13:18:25 -05:00
Nicolas Viennot e71bd675b5 Allow tmate daemon to specify a list of authorized SSH keys 2019-11-07 13:18:25 -05:00
Nicolas Viennot 48884c95c9 Take out the -a authorized_keys option 2019-11-07 08:03:38 -05:00
Nicolas Viennot cd3ee3ee4d Refactor code 2019-11-07 08:01:49 -05:00
Nicolas Viennot 6a951aa9f7 Improve debugging traces 2019-11-06 16:45:18 -05:00
Nicolas Viennot 975ba3323b Fix bug of SIG unset if the keys already exists
Fixes #66
2019-10-31 20:45:19 -04:00
Nicolas Viennot 566a8bc412 Show better explaination of invalid session tokens 2019-10-22 05:22:58 -04:00
Nicolas Viennot dc05cbc4f5 Allow non authentication method for SSH 2019-10-15 03:36:15 -04:00
Nicolas Viennot 48b5c6afba readme notes 2019-10-14 23:21:11 -04:00
Nicolas Viennot 383b69f04c Take out some annoying letters to speak over the phone
Session key entropy goes from 149 to 144 bits. It's acceptable

Fixes #31
2019-10-13 05:23:45 -04:00
Nicolas Viennot fde93450dc Add readme for the docker image 2019-10-13 05:01:11 -04:00
Nicolas Viennot eb08daec79 README is todo 2019-10-13 02:54:29 -04:00
Nicolas Viennot 314bf91299 Remove latency measurements 2019-10-09 18:59:38 -04:00
Nicolas Viennot 9c3cc0e264 Renable keepalive now that libssh allows it 2019-10-09 18:47:01 -04:00
Nicolas Viennot 5c2c1c2584 logging improvement 2019-10-09 17:36:23 -04:00
Nicolas Viennot 2bd232c982 Avoid disconnection warning if fin packet is received 2019-10-05 04:11:32 -04:00
Nicolas Viennot 9df2e6d6fb nits 2019-09-18 23:03:55 -04:00
Nicolas Viennot 6c5497df4b Only load keys that are accessible 2019-08-31 23:42:21 -04:00
Nicolas Viennot 07dc84de98 avoid logging early failures 2019-08-31 22:09:19 -04:00
Nicolas Viennot 7f9378de43 Allow to advertize a specific SSH port (for load balancer) 2019-08-31 18:52:35 -04:00
Nicolas Viennot 080be74a75 Hide session token in ps and logs 2019-08-31 17:35:34 -04:00
Nicolas Viennot ebbafd794f Adjust verbose levels 2019-08-31 16:49:07 -04:00
Nicolas Viennot 6397e825bd Dockerfile update 2019-08-25 04:41:38 -04:00
Nicolas Viennot e568b91f04 Improved messages 2019-08-25 03:53:26 -04:00
Nicolas Viennot 4555d8d6fb Add PROXY protocol support 2019-08-24 18:31:07 -04:00
Nicolas Viennot 07bea82ab2 Docker: libssh update to 0.9 2019-08-24 18:12:40 -04:00
Nicolas Viennot 6a271c5a89 ssh_bind_accept -> ssh_bind_accept_fd 2019-08-24 18:01:00 -04:00
Nicolas Viennot 004fe379f6 Rename proxy -> websocket 2019-08-24 15:13:04 -04:00
Nicolas Viennot 4b20aaf66e SSH port is configurable in docker 2019-08-20 02:40:28 -04:00
Nicolas Viennot ba964e279c nits 2019-08-19 23:21:50 -04:00
Nicolas Viennot b2dc743aa6 Allow hostname detection in docker 2019-08-11 00:54:56 -04:00
Nicolas Viennot bd65eb3c48 cleanup dockerfile 2019-08-10 23:32:46 -04:00
Nicolas Viennot 46f821f88c Docker image use environement variables to get their settings 2019-08-10 21:37:51 -04:00
Nicolas Viennot 55d481a633 Dockerfile.dev 2019-08-04 12:30:21 -04:00
65 changed files with 1462 additions and 1040 deletions
+28 -18
View File
@@ -1,31 +1,41 @@
FROM alpine:3.9
FROM alpine:3.9 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 git wget cmake make gcc g++ linux-headers zlib-dev openssl-dev \
automake autoconf libevent-dev ncurses-dev msgpack-c-dev libexecinfo-dev
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; \
wget -O libssh.tar.xz https://www.libssh.org/files/0.9/libssh-0.9.0.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; \
make install
RUN mkdir -p /src/tmate-ssh-server
COPY . /src/tmate-ssh-server
RUN set -ex; \
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.9
RUN apk add --no-cache msgpack-c ncurses-libs libevent libexecinfo openssl zlib gdb bash
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"]
+31
View File
@@ -0,0 +1,31 @@
FROM alpine:3.9
RUN apk add --no-cache msgpack-c ncurses-libs libevent libexecinfo openssl zlib
RUN set -ex; \
apk add --no-cache --virtual .build-deps \
git wget cmake make gcc g++ linux-headers zlib-dev openssl-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.9/libssh-0.9.0.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
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
View File
@@ -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 \
+20 -4
View File
@@ -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
--------
+20 -24
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+4 -4
View File
@@ -7,8 +7,9 @@ gen_key() {
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)
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"
+20
View File
@@ -0,0 +1,20 @@
#!/bin/sh
set -e
if [ "${USE_PROXY_PROTOCOL}" == "1" ]; then
set -- -x "$@"
fi
if [ "${HAS_WEBSOCKET}" == "1" ]; then
set -- -w localhost "$@"
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} "$@"
+5 -5
View File
@@ -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
View File
@@ -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
+2 -2
View File
@@ -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;
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -22,7 +22,7 @@
#include "tmux.h"
#ifndef TMATE_SLAVE
#ifndef TMATE
/*
* Set window layouts - predefined methods to arrange windows. These are
+1 -1
View File
@@ -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
+44 -66
View File
@@ -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);
}
+1 -1
View File
@@ -25,7 +25,7 @@
#include "tmux.h"
#ifdef TMATE_SLAVE
#ifdef TMATE
char *
default_window_name(struct window *w)
+2
View File
@@ -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)
+5 -3
View File
@@ -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
+8 -8
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+14 -10
View File
@@ -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
}
}
+2 -1
View File
@@ -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(;;);
}
/*
+1 -1
View File
@@ -24,7 +24,7 @@
#include "tmux.h"
#ifndef TMATE_SLAVE
#ifndef TMATE
struct event ev_sighup;
struct event ev_sigchld;
+3 -3
View File
@@ -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
+225
View File
@@ -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;
}
+61 -31
View File
@@ -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,39 +36,50 @@ 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,
@@ -210,10 +234,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 +269,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 +432,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 +529,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 +549,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);
}
}
+3 -3
View File
@@ -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
View File
@@ -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
+53 -323
View File
@@ -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);
}
@@ -156,7 +102,7 @@ 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 +113,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 +137,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,16 +148,15 @@ int main(int argc, char **argv, char **envp)
cmdline_end = *envp;
tmate_preload_trace_lib();
if ((dev_urandom_fd = open("/dev/urandom", O_RDONLY)) < 0)
tmate_fatal("Cannot open /dev/urandom");
tmate_catch_sigsegv();
tmate_init_rand();
if ((mkdir(TMATE_WORKDIR, 0701) < 0 && errno != EEXIST) ||
(mkdir(TMATE_WORKDIR "/sessions", 0703) < 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 */
/* The websocket server needs to access the /session dir to rename sockets */
if ((chmod(TMATE_WORKDIR, 0701) < 0) ||
(chmod(TMATE_WORKDIR "/sessions", 0703) < 0) ||
(chmod(TMATE_WORKDIR "/jail", 0700) < 0))
@@ -223,113 +167,43 @@ int main(int argc, char **argv, char **envp)
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 +218,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 +233,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 +273,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;
}
}
+3
View File
@@ -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 {
+47
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+209 -130
View File
@@ -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_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 */
}
}
+88 -86
View File
@@ -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);
}
+74 -57
View File
@@ -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;
@@ -121,27 +118,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 +141,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 +168,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 +181,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 +195,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 +213,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 +248,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
+3 -3
View File
@@ -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);
+26 -21
View File
@@ -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
View File
@@ -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.
+1 -1
View File
@@ -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
View File
@@ -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
+12 -12
View File
@@ -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;
@@ -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
+2
View File
@@ -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)
{