1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <libssh/libssh.h>
5 #include <sys/stat.h>
6 #include <sys/param.h>
7 #include <errno.h>
8 #include <grp.h>
9 #include <pwd.h>
10 #include <unistd.h>
11 #ifdef HAVE_CURSES_H
12 #include <curses.h>
13 #else
14 #include <ncurses.h>
15 #endif
16 #include <term.h>
17 #include <time.h>
18 #include <fcntl.h>
19 #include <sched.h>
20 #include <signal.h>
21 #include "tmate.h"
22 
23 static char *cmdline;
24 static char *cmdline_end;
25 
26 struct tmate_settings _tmate_settings = {
27 	.keys_dir        	= TMATE_SSH_DEFAULT_KEYS_DIR,
28 	.ssh_port        	= TMATE_SSH_DEFAULT_PORT,
29 	.ssh_port_advertized    = -1,
30 	.websocket_hostname  	= NULL,
31 	.bind_addr	 	= NULL,
32 	.websocket_port      	= TMATE_DEFAULT_WEBSOCKET_PORT,
33 	.tmate_host      	= NULL,
34 	.log_level      	= LOG_INFO,
35 	.use_proxy_protocol	= false,
36 };
37 
38 struct tmate_settings *tmate_settings = &_tmate_settings;
39 
40 extern int server_fd;
41 extern void server_send_exit(void);
request_server_termination(void)42 void request_server_termination(void)
43 {
44 	if (server_fd) {
45 		server_send_exit();
46 	} else
47 		exit(1);
48 }
49 
usage(void)50 static void usage(void)
51 {
52 	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");
53 }
54 
get_full_hostname(void)55 static char* get_full_hostname(void)
56 {
57 	struct addrinfo hints, *info;
58 	char hostname[1024];
59 	int gai_result;
60 	char *ret;
61 
62 	if (gethostname(hostname, sizeof(hostname)) < 0)
63 		tmate_fatal("cannot get hostname");
64 	hostname[1023] = '\0';
65 
66 	memset(&hints, 0, sizeof hints);
67 	hints.ai_family = AF_UNSPEC; /*either IPV4 or IPV6*/
68 	hints.ai_socktype = SOCK_STREAM;
69 	hints.ai_flags = AI_CANONNAME;
70 
71 	if ((gai_result = getaddrinfo(hostname, NULL, &hints, &info)) != 0) {
72 		tmate_info("cannot lookup hostname: %s", gai_strerror(gai_result));
73 		return xstrdup(hostname);
74 	}
75 
76 	ret = xstrdup(info->ai_canonname);
77 
78 	freeaddrinfo(info);
79 	return ret;
80 }
81 #include <langinfo.h>
82 #include <locale.h>
83 
setup_locale(void)84 static void setup_locale(void)
85 {
86 	const char *s;
87 
88 	if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL) {
89 		if (setlocale(LC_CTYPE, "") == NULL)
90 			tmate_fatal("invalid LC_ALL, LC_CTYPE or LANG");
91 		s = nl_langinfo(CODESET);
92 		if (strcasecmp(s, "UTF-8") != 0 &&
93 		    strcasecmp(s, "UTF8") != 0)
94 			tmate_fatal("need UTF-8 locale (LC_CTYPE) but have %s", s);
95 	}
96 
97 	setlocale(LC_TIME, "");
98 	tzset();
99 }
100 
main(int argc,char ** argv,char ** envp)101 int main(int argc, char **argv, char **envp)
102 {
103 	int opt;
104 
105 	while ((opt = getopt(argc, argv, "b:h:k:p:q:w:z:xv")) != -1) {
106 		switch (opt) {
107 		case 'b':
108 			tmate_settings->bind_addr = xstrdup(optarg);
109 			break;
110 		case 'h':
111 			tmate_settings->tmate_host = xstrdup(optarg);
112 			break;
113 		case 'k':
114 			tmate_settings->keys_dir = xstrdup(optarg);
115 			break;
116 		case 'p':
117 			tmate_settings->ssh_port = atoi(optarg);
118 			break;
119 		case 'q':
120 			tmate_settings->ssh_port_advertized = atoi(optarg);
121 			break;
122 		case 'w':
123 			tmate_settings->websocket_hostname = xstrdup(optarg);
124 			break;
125 		case 'z':
126 			tmate_settings->websocket_port = atoi(optarg);
127 			break;
128 		case 'x':
129 			tmate_settings->use_proxy_protocol = true;
130 			break;
131 		case 'v':
132 			tmate_settings->log_level++;
133 			break;
134 		default:
135 			usage();
136 			return 1;
137 		}
138 	}
139 
140 	init_logging(tmate_settings->log_level);
141 
142 	setup_locale();
143 
144 	if (!tmate_settings->tmate_host)
145 		tmate_settings->tmate_host = get_full_hostname();
146 
147 	cmdline = *argv;
148 	cmdline_end = *envp;
149 
150 	tmate_preload_trace_lib();
151 	tmate_catch_sigsegv();
152 	tmate_init_rand();
153 
154 	if ((mkdir(TMATE_WORKDIR, 0701)             < 0 && errno != EEXIST) ||
155 	    (mkdir(TMATE_WORKDIR "/sessions", 0703) < 0 && errno != EEXIST) ||
156 	    (mkdir(TMATE_WORKDIR "/jail", 0700)     < 0 && errno != EEXIST))
157 		tmate_fatal("Cannot prepare session in " TMATE_WORKDIR);
158 
159 	/* The websocket server needs to access the /session dir to rename sockets */
160 	if ((chmod(TMATE_WORKDIR, 0701)             < 0) ||
161 	    (chmod(TMATE_WORKDIR "/sessions", 0703) < 0) ||
162 	    (chmod(TMATE_WORKDIR "/jail", 0700)     < 0))
163 		tmate_fatal("Cannot prepare session in " TMATE_WORKDIR);
164 
165 	tmate_ssh_server_main(tmate_session,
166 			      tmate_settings->keys_dir, tmate_settings->bind_addr, tmate_settings->ssh_port);
167 	return 0;
168 }
169 
get_socket_path(const char * _token)170 char *get_socket_path(const char *_token)
171 {
172 	char *path;
173 	char *token = xstrdup(_token);
174 
175 	for (char *c = token; *c; c++) {
176 		if (*c == '/' || *c == '.')
177 			*c = '=';
178 	}
179 
180 	xasprintf(&path, TMATE_WORKDIR "/sessions/%s", token);
181 	free(token);
182 	return path;
183 }
184 
set_session_token(struct tmate_session * session,const char * token)185 void set_session_token(struct tmate_session *session, const char *token)
186 {
187 	session->session_token = xstrdup(token);
188 	socket_path = get_socket_path(token);
189 
190 	xasprintf((char **)&session->obfuscated_session_token, "%.4s...",
191 		  session->session_token);
192 
193 	size_t size = cmdline_end - cmdline;
194 	memset(cmdline, 0, size);
195 	snprintf(cmdline, size-1, "tmate-ssh-server [%s] %s %s",
196 		tmate_session->obfuscated_session_token,
197 		session->ssh_client.role == TMATE_ROLE_DAEMON ? "(daemon)" : "(pty client)",
198 		session->ssh_client.ip_address);
199 
200 	char *log_prefix;
201 	xasprintf(&log_prefix, "[%s] ", session->obfuscated_session_token);
202 	set_log_prefix(log_prefix);
203 	free(log_prefix);
204 }
205 
close_fds_except(int * fd_to_preserve,int num_fds)206 void close_fds_except(int *fd_to_preserve, int num_fds)
207 {
208 	int fd, i, preserve;
209 
210 	for (fd = 0; fd < 1024; fd++) {
211 		preserve = 0;
212 		for (i = 0; i < num_fds; i++)
213 			if (fd_to_preserve[i] == fd)
214 				preserve = 1;
215 
216 		if (!preserve)
217 			close(fd);
218 	}
219 }
220 
get_in_jail(void)221 void get_in_jail(void)
222 {
223 	struct passwd *pw;
224 	uid_t uid;
225 	gid_t gid;
226 
227 	pw = getpwnam(TMATE_JAIL_USER);
228 	if (!pw) {
229 		tmate_fatal("Cannot get the /etc/passwd entry for %s",
230 			    TMATE_JAIL_USER);
231 	}
232 	uid = pw->pw_uid;
233 	gid = pw->pw_gid;
234 
235 	if (getuid() != 0)
236 		tmate_fatal("Need root privileges to create the jail");
237 
238 	if (chroot(TMATE_WORKDIR "/jail") < 0)
239 		tmate_fatal("Cannot chroot()");
240 
241 	if (chdir("/") < 0)
242 		tmate_fatal("Cannot chdir()");
243 
244 #ifdef IS_LINUX
245 	if (unshare(CLONE_NEWPID | CLONE_NEWIPC | CLONE_NEWNS | CLONE_NEWNET) < 0)
246 		tmate_fatal("Cannot create new namespace");
247 #endif
248 
249 	if (setgroups(1, (gid_t[]){gid}) < 0)
250 		tmate_fatal("Cannot setgroups()");
251 
252 #if defined(HAVE_SETRESGID)
253 	if (setresgid(gid, gid, gid) < 0)
254 		tmate_fatal("Cannot setresgid() %d", gid);
255 #elif defined(HAVE_SETREGID)
256 	if (setregid(gid, gid) < 0)
257 		tmate_fatal("Cannot setregid()");
258 #else
259 	if (setgid(gid) < 0)
260 		tmate_fatal("Cannot setgid()");
261 #endif
262 
263 #if defined(HAVE_SETRESUID)
264 	if (setresuid(uid, uid, uid) < 0)
265 		tmate_fatal("Cannot setresuid()");
266 #elif defined(HAVE_SETREUID)
267 	if (setreuid(uid, uid) < 0)
268 		tmate_fatal("Cannot setreuid()");
269 #else
270 	if (setuid(uid) < 0)
271 		tmate_fatal("Cannot setuid()");
272 #endif
273 
274 	nice(1);
275 
276 	tmate_debug("Dropped priviledges to %s (%d,%d), jailed in %s",
277 		    TMATE_JAIL_USER, uid, gid, TMATE_WORKDIR "/jail");
278 }
279 
setup_ncurse(int fd,const char * name)280 void setup_ncurse(int fd, const char *name)
281 {
282 	int error;
283 	if (setupterm((char *)name, fd, &error) != OK)
284 		tmate_fatal("Cannot setup terminal");
285 }
286 
287