1 #include <libssh/libssh.h>
2 #include <libssh/server.h>
3 #include <libssh/callbacks.h>
4 #include <sys/socket.h>
5 #include <netinet/tcp.h>
6 #include <sys/wait.h>
7 #include <ctype.h>
8 #include <stdio.h>
9 #include <signal.h>
10 #include <event.h>
11 #include <arpa/inet.h>
12 #ifndef IPPROTO_TCP
13 #include <netinet/in.h>
14 #endif
15 
16 #include "tmate.h"
17 
get_ssh_conn_string(const char * session_token)18 char *get_ssh_conn_string(const char *session_token)
19 {
20 	char port_arg[16] = {0};
21 	char *ret;
22 
23 	int ssh_port_advertized = tmate_settings->ssh_port_advertized == -1 ?
24 		tmate_settings->ssh_port :
25 		tmate_settings->ssh_port_advertized;
26 
27 	if (ssh_port_advertized != 22)
28 		sprintf(port_arg, " -p%d", ssh_port_advertized);
29 	xasprintf(&ret, "ssh%s %s@%s", port_arg, session_token, tmate_settings->tmate_host);
30 	return ret;
31 }
32 
pty_request(__unused ssh_session session,__unused ssh_channel channel,__unused const char * term,int width,int height,__unused int pxwidth,__unused int pwheight,void * userdata)33 static int pty_request(__unused ssh_session session,
34 		       __unused ssh_channel channel,
35 		       __unused const char *term,
36 		       int width, int height,
37 		       __unused int pxwidth, __unused int pwheight,
38 		       void *userdata)
39 {
40 	struct tmate_ssh_client *client = userdata;
41 
42 	client->winsize_pty.ws_col = width;
43 	client->winsize_pty.ws_row = height;
44 
45 	return 0;
46 }
47 
shell_request(__unused ssh_session session,__unused ssh_channel channel,void * userdata)48 static int shell_request(__unused ssh_session session,
49 			 __unused ssh_channel channel,
50 			 void *userdata)
51 {
52 	struct tmate_ssh_client *client = userdata;
53 
54 	if (client->role)
55 		return 1;
56 
57 	client->role = TMATE_ROLE_PTY_CLIENT;
58 
59 	return 0;
60 }
61 
subsystem_request(__unused ssh_session session,__unused ssh_channel channel,const char * subsystem,void * userdata)62 static int subsystem_request(__unused ssh_session session,
63 			     __unused ssh_channel channel,
64 			     const char *subsystem, void *userdata)
65 {
66 	struct tmate_ssh_client *client = userdata;
67 
68 	if (client->role)
69 		return 1;
70 
71 	if (!strcmp(subsystem, "tmate"))
72 		client->role = TMATE_ROLE_DAEMON;
73 
74 	return 0;
75 }
76 
exec_request(__unused ssh_session session,__unused ssh_channel channel,const char * command,void * userdata)77 static int exec_request(__unused ssh_session session,
78 			__unused ssh_channel channel,
79 			const char *command, void *userdata)
80 {
81 	struct tmate_ssh_client *client = userdata;
82 
83 	if (client->role)
84 		return 1;
85 
86 	if (!tmate_has_websocket())
87 		return 1;
88 
89 	client->role = TMATE_ROLE_EXEC;
90 	client->exec_command = xstrdup(command);
91 
92 	return 0;
93 }
94 
channel_open_request_cb(ssh_session session,void * userdata)95 static ssh_channel channel_open_request_cb(ssh_session session, void *userdata)
96 {
97 	struct tmate_ssh_client *client = userdata;
98 
99 	if (!client->username) {
100 		/* The authentication did not go through yet */
101 		return NULL;
102 	}
103 
104 	if (client->channel) {
105 		/*
106 		 * We already have a channel, and we don't support multi
107 		 * channels yet. Returning NULL means the channel request will
108 		 * be denied.
109 		 */
110 		return NULL;
111 	}
112 
113 	client->channel = ssh_channel_new(session);
114 	if (!client->channel)
115 		tmate_fatal("Error getting channel");
116 
117 	memset(&client->channel_cb, 0, sizeof(client->channel_cb));
118 	ssh_callbacks_init(&client->channel_cb);
119 	client->channel_cb.userdata = client;
120 	client->channel_cb.channel_pty_request_function = pty_request;
121 	client->channel_cb.channel_shell_request_function = shell_request;
122 	client->channel_cb.channel_subsystem_request_function = subsystem_request;
123 	client->channel_cb.channel_exec_request_function = exec_request;
124 	ssh_set_channel_callbacks(client->channel, &client->channel_cb);
125 
126 	return client->channel;
127 }
128 
auth_pubkey_cb(__unused ssh_session session,const char * user,ssh_key pubkey,char signature_state,void * userdata)129 static int auth_pubkey_cb(__unused ssh_session session,
130 			  const char *user,
131 			  ssh_key pubkey,
132 			  char signature_state, void *userdata)
133 {
134 	struct tmate_ssh_client *client = userdata;
135 
136 	switch (signature_state) {
137 	case SSH_PUBLICKEY_STATE_VALID:
138 		client->username = xstrdup(user);
139 
140 		const char *key_type = ssh_key_type_to_char(ssh_key_type(pubkey));
141 
142 		char *b64_key;
143 		if (ssh_pki_export_pubkey_base64(pubkey, &b64_key) != SSH_OK)
144 			tmate_fatal("error getting public key");
145 
146 		char *pubkey64;
147 		xasprintf(&pubkey64, "%s %s", key_type, b64_key);
148 		free(b64_key);
149 
150 		if (!would_tmate_session_allow_auth(user, pubkey64)) {
151 			free(pubkey64);
152 			return SSH_AUTH_DENIED;
153 		}
154 
155 		client->pubkey = pubkey64;
156 
157 		return SSH_AUTH_SUCCESS;
158 	case SSH_PUBLICKEY_STATE_NONE:
159 		return SSH_AUTH_SUCCESS;
160 	default:
161 		return SSH_AUTH_DENIED;
162 	}
163 }
164 
auth_none_cb(__unused ssh_session session,const char * user,void * userdata)165 static int auth_none_cb(__unused ssh_session session, const char *user, void *userdata)
166 {
167 	struct tmate_ssh_client *client = userdata;
168 
169 	if (!would_tmate_session_allow_auth(user, NULL))
170 		return SSH_AUTH_DENIED;
171 
172 	client->username = xstrdup(user);
173 	client->pubkey = NULL;
174 
175 	return SSH_AUTH_SUCCESS;
176 }
177 
178 static struct ssh_server_callbacks_struct ssh_server_cb = {
179 	.auth_pubkey_function = auth_pubkey_cb,
180 	.auth_none_function = auth_none_cb,
181 	.channel_open_request_session_function = channel_open_request_cb,
182 };
183 
on_ssh_read(__unused evutil_socket_t fd,__unused short what,void * arg)184 static void on_ssh_read(__unused evutil_socket_t fd, __unused short what, void *arg)
185 {
186 	struct tmate_ssh_client *client = arg;
187 	ssh_execute_message_callbacks(client->session);
188 
189 	if (!ssh_is_connected(client->session)) {
190 		tmate_debug("ssh disconnected");
191 
192 		event_del(&client->ev_ssh);
193 
194 		/* For graceful tmux client termination */
195 		request_server_termination();
196 	}
197 }
198 
register_on_ssh_read(struct tmate_ssh_client * client)199 static void register_on_ssh_read(struct tmate_ssh_client *client)
200 {
201 	event_set(&client->ev_ssh, ssh_get_fd(client->session),
202 		  EV_READ | EV_PERSIST, on_ssh_read, client);
203 	event_add(&client->ev_ssh, NULL);
204 }
205 
handle_sigalrm(__unused int sig)206 static void handle_sigalrm(__unused int sig)
207 {
208 	tmate_fatal_quiet("Connection grace period (%ds) passed", TMATE_SSH_GRACE_PERIOD);
209 }
210 
client_bootstrap(struct tmate_session * _session)211 static void client_bootstrap(struct tmate_session *_session)
212 {
213 	struct tmate_ssh_client *client = &_session->ssh_client;
214 	long grace_period = TMATE_SSH_GRACE_PERIOD;
215 	ssh_event mainloop;
216 	ssh_session session = client->session;
217 
218 	tmate_debug("Bootstrapping ssh client ip=%s", client->ip_address);
219 
220 	_session->ev_base = osdep_event_init();
221 
222 	/* new process group, we don't want to die with our parent (upstart) */
223 	setpgid(0, 0);
224 
225 	{
226 	int flag = 1;
227 	setsockopt(ssh_get_fd(session), IPPROTO_TCP, TCP_NODELAY,
228 		   &flag, sizeof(flag));
229 	}
230 
231 	/*
232 	 * We should die early if we can't connect to websocket server. This
233 	 * way the tmate daemon will pick another server to work on.
234 	 */
235 	_session->websocket_fd = -1;
236 	if (tmate_has_websocket())
237 		_session->websocket_fd = tmate_connect_to_websocket();
238 
239 	ssh_server_cb.userdata = client;
240 	ssh_callbacks_init(&ssh_server_cb);
241 	ssh_set_server_callbacks(client->session, &ssh_server_cb);
242 
243 	ssh_options_set(session, SSH_OPTIONS_TIMEOUT, &grace_period);
244 	ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "yes");
245 	ssh_options_set(session, SSH_OPTIONS_KEY_EXCHANGE, "curve25519-sha256@libssh.org,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256,diffie-hellman-group16-sha512");
246 	ssh_options_set(session, SSH_OPTIONS_CIPHERS_C_S, "aes256-gcm@openssh.com,aes128-gcm@openssh.com,chacha20-poly1305@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr");
247 	ssh_options_set(session, SSH_OPTIONS_CIPHERS_S_C, "aes256-gcm@openssh.com,aes128-gcm@openssh.com,chacha20-poly1305@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr");
248 	ssh_options_set(session, SSH_OPTIONS_HMAC_S_C, "hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com");
249 	ssh_options_set(session, SSH_OPTIONS_HMAC_C_S, "hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com");
250 	ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-ed25519,ssh-rsa");
251 
252 	ssh_set_auth_methods(client->session, SSH_AUTH_METHOD_NONE |
253 					      SSH_AUTH_METHOD_PUBLICKEY);
254 
255 	tmate_debug("Exchanging DH keys");
256 	if (ssh_handle_key_exchange(session) < 0)
257 		tmate_fatal_quiet("Error doing the key exchange: %s", ssh_get_error(session));
258 
259 	mainloop = ssh_event_new();
260 	ssh_event_add_session(mainloop, session);
261 
262 	while (!client->role) {
263 		if (ssh_event_dopoll(mainloop, -1) == SSH_ERROR)
264 			tmate_fatal_quiet("Error polling ssh socket: %s", ssh_get_error(session));
265 	}
266 
267 	alarm(0);
268 
269 	/* The latency callback is set later */
270 	start_keepalive_timer(client, TMATE_SSH_KEEPALIVE_SEC * 1000);
271 	register_on_ssh_read(client);
272 
273 	switch (client->role) {
274 	case TMATE_ROLE_DAEMON:		tmate_spawn_daemon(_session);		break;
275 	case TMATE_ROLE_PTY_CLIENT:	tmate_spawn_pty_client(_session);	break;
276 	case TMATE_ROLE_EXEC:		tmate_spawn_exec(_session);		break;
277 	}
278 	/* never reached */
279 }
280 
get_client_ip_socket(int fd,char * dst,size_t len)281 static int get_client_ip_socket(int fd, char *dst, size_t len)
282 {
283 	struct sockaddr sa;
284 	socklen_t sa_len = sizeof(sa);
285 
286 	if (getpeername(fd, &sa, &sa_len) < 0)
287 		return -1;
288 
289 
290 	switch (sa.sa_family) {
291 	case AF_INET:
292 		if (!inet_ntop(AF_INET, &((struct sockaddr_in *)&sa)->sin_addr,
293 			       dst, len))
294 			return -1;
295 		break;
296 	case AF_INET6:
297 		if (!inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&sa)->sin6_addr,
298 			       dst, len))
299 			return -1;
300 		break;
301 	default:
302 		return -1;
303 	}
304 
305 	return 0;
306 }
307 
read_single_line(int fd,char * dst,size_t len)308 static int read_single_line(int fd, char *dst, size_t len)
309 {
310 	/*
311 	 * This reads exactly one line from fd.
312 	 * We cannot read bytes after the new line.
313 	 * We could use recv() with MSG_PEEK to do this more efficiently.
314 	 */
315 	for (size_t i = 0; i < len; i++) {
316 		if (read(fd, &dst[i], 1) <= 0)
317 			break;
318 
319 		if (dst[i] == '\r')
320 			i--;
321 
322 		if (dst[i] == '\n') {
323 			dst[i] = '\0';
324 			return i;
325 		}
326 	}
327 
328 	return -1;
329 }
330 
get_client_ip_proxy_protocol(int fd,char * dst,size_t len)331 static int get_client_ip_proxy_protocol(int fd, char *dst, size_t len)
332 {
333 	char header[110];
334 	int tok_num;
335 
336 #define SIGNATURE "PROXY "
337 	ssize_t ret = read(fd, header, sizeof(SIGNATURE)-1);
338 	if (ret <= 0)
339 		tmate_fatal_quiet("Disconnected, health checker?");
340 	if (ret != sizeof(SIGNATURE)-1)
341 		return -1;
342 	if (memcmp(header, SIGNATURE, sizeof(SIGNATURE)-1))
343 		return -1;
344 #undef SIGNATURE
345 
346 	if (read_single_line(fd, header, sizeof(header)) < 0)
347 		return -1;
348 
349 	tmate_debug("proxy header: %s", header);
350 
351 	tok_num = 0;
352 	for (char *tok = strtok(header, " "); tok; tok = strtok(NULL, " "), tok_num++) {
353 		if (tok_num == 1)
354 			strlcpy(dst, tok, len);
355 	}
356 
357 	if (tok_num != 5)
358 		return -1;
359 
360 	return 0;
361 }
362 
get_client_ip(int fd,char * dst,size_t len)363 static int get_client_ip(int fd, char *dst, size_t len)
364 {
365 	if (tmate_settings->use_proxy_protocol)
366 		return get_client_ip_proxy_protocol(fd, dst, len);
367 	else
368 		return get_client_ip_socket(fd, dst, len);
369 }
370 
ssh_log_function(int priority,const char * function,const char * buffer,__unused void * userdata)371 static void ssh_log_function(int priority, const char *function,
372 			     const char *buffer, __unused void *userdata)
373 {
374 	/* loglevel already applied */
375 	log_emit(LOG_DEBUG, "[%s] %s", function, buffer);
376 }
377 
max(int a,int b)378 static inline int max(int a, int b)
379 {
380 	if (a < b)
381 		return b;
382 	return a;
383 }
384 
ssh_import_key(ssh_bind bind,const char * keys_dir,const char * name)385 static void ssh_import_key(ssh_bind bind, const char *keys_dir, const char *name)
386 {
387 	char path[PATH_MAX];
388 	ssh_key key = NULL;
389 
390 	sprintf(path, "%s/%s", keys_dir, name);
391 
392 	if (access(path, F_OK) < 0)
393 		return;
394 
395 	tmate_info("Loading key %s", path);
396 
397 	ssh_pki_import_privkey_file(path, NULL, NULL, NULL, &key);
398 	ssh_bind_options_set(bind, SSH_BIND_OPTIONS_IMPORT_KEY, key);
399 }
400 
prepare_ssh(const char * keys_dir,const char * bind_addr,int port)401 static ssh_bind prepare_ssh(const char *keys_dir, const char *bind_addr, int port)
402 {
403 	ssh_bind bind;
404 	int ssh_log_level;
405 
406 	ssh_log_level = SSH_LOG_WARNING + max(log_get_level() - LOG_INFO, 0);
407 
408 	ssh_set_log_callback(ssh_log_function);
409 
410 	bind = ssh_bind_new();
411 	if (!bind)
412 		tmate_fatal("Cannot initialize ssh");
413 
414 #if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
415 	/* Explicitly parse configuration to avoid automatic configuration file
416 	 * loading which could override options */
417 	ssh_bind_options_parse_config(bind, NULL);
418 #endif
419 
420 	if (bind_addr)
421 		ssh_bind_options_set(bind, SSH_BIND_OPTIONS_BINDADDR, bind_addr);
422 	ssh_bind_options_set(bind, SSH_BIND_OPTIONS_BINDPORT, &port);
423 	ssh_bind_options_set(bind, SSH_BIND_OPTIONS_BANNER, TMATE_SSH_BANNER);
424 	ssh_bind_options_set(bind, SSH_BIND_OPTIONS_LOG_VERBOSITY, &ssh_log_level);
425 
426 	ssh_import_key(bind, keys_dir, "ssh_host_ed25519_key");
427 
428 	if (ssh_bind_listen(bind) < 0)
429 		tmate_fatal("Error listening to socket: %s\n", ssh_get_error(bind));
430 
431 	tmate_info("Accepting connections on %s:%d", bind_addr ?: "", port);
432 
433 	return bind;
434 }
435 
handle_sigchld(__unused int sig)436 static void handle_sigchld(__unused int sig)
437 {
438 	int status;
439 	pid_t pid;
440 
441 	while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) {
442 		/*
443 		 * It's not safe to call indirectly malloc() here, because
444 		 * of potential deadlocks with other malloc() calls.
445 		 */
446 #if 0
447 		if (WIFEXITED(status))
448 			tmate_debug("Child %d exited (%d)", pid, WEXITSTATUS(status));
449 		if (WIFSIGNALED(status))
450 			tmate_debug("Child %d killed (%d)", pid, WTERMSIG(status));
451 		if (WIFSTOPPED(status))
452 			tmate_debug("Child %d stopped (%d)", pid, WSTOPSIG(status));
453 #endif
454 	}
455 }
456 
tmate_ssh_server_main(struct tmate_session * session,const char * keys_dir,const char * bind_addr,int port)457 void tmate_ssh_server_main(struct tmate_session *session, const char *keys_dir,
458 			   const char *bind_addr, int port)
459 {
460 	struct tmate_ssh_client *client = &session->ssh_client;
461 	ssh_bind bind;
462 	pid_t pid;
463 	int fd;
464 
465 	tmate_catch_sigsegv();
466 	signal(SIGCHLD, handle_sigchld);
467 
468 	bind = prepare_ssh(keys_dir, bind_addr, port);
469 
470 	client->session = ssh_new();
471 	client->channel = NULL;
472 	client->winsize_pty.ws_col = 80;
473 	client->winsize_pty.ws_row = 24;
474 	session->session_token = "init";
475 
476 	if (!client->session)
477 		tmate_fatal("Cannot initialize session");
478 
479 	for (;;) {
480 		fd = accept(ssh_bind_get_fd(bind), NULL, NULL);
481 		if (fd < 0)
482 			tmate_fatal("Error accepting connection");
483 
484 		if ((pid = fork()) < 0)
485 			tmate_fatal("Can't fork");
486 
487 		if (pid) {
488 			/* Parent process */
489 			close(fd);
490 			continue;
491 		}
492 
493 		/* Child process */
494 
495 		signal(SIGALRM, handle_sigalrm);
496 		alarm(TMATE_SSH_GRACE_PERIOD);
497 
498 		if (get_client_ip(fd, client->ip_address, sizeof(client->ip_address)) < 0) {
499 			if (tmate_settings->use_proxy_protocol)
500 				tmate_fatal("Proxy header invalid. Load balancer may be misconfigured");
501 			else
502 				tmate_fatal("Error getting client IP from connection");
503 		}
504 
505 		tmate_debug("Connection accepted ip=%s", client->ip_address);
506 
507 		if (ssh_bind_accept_fd(bind, client->session, fd) < 0)
508 			tmate_fatal("Error accepting connection: %s", ssh_get_error(bind));
509 
510 		ssh_bind_free(bind);
511 
512 		client_bootstrap(session);
513 		/* never reached */
514 	}
515 }
516