diff options
author | Damien Miller <djm@mindrot.org> | 2008-05-19 16:00:08 +1000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2008-05-19 16:00:08 +1000 |
commit | b1cbfa25f1618b277de670b943acb07ff5dbb1ac (patch) | |
tree | 159cc3fa99ba5c6a2b880ad216542b1262e186fa /mux.c | |
parent | bd74025c7b08104828986d01e60f04372b3d5337 (diff) |
- djm@cvs.openbsd.org 2008/05/09 14:18:44
[clientloop.c clientloop.h ssh.c mux.c]
tidy up session multiplexing code, moving it into its own file and
making the function names more consistent - making ssh.c and
clientloop.c a fair bit more readable.
ok markus@
Diffstat (limited to 'mux.c')
-rw-r--r-- | mux.c | 646 |
1 files changed, 646 insertions, 0 deletions
@@ -0,0 +1,646 @@ | |||
1 | /* $OpenBSD: mux.c,v 1.1 2008/05/09 14:18:44 djm Exp $ */ | ||
2 | /* | ||
3 | * Copyright (c) 2002-2008 Damien Miller <djm@openbsd.org> | ||
4 | * | ||
5 | * Permission to use, copy, modify, and distribute this software for any | ||
6 | * purpose with or without fee is hereby granted, provided that the above | ||
7 | * copyright notice and this permission notice appear in all copies. | ||
8 | * | ||
9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
16 | */ | ||
17 | |||
18 | /* ssh session multiplexing support */ | ||
19 | |||
20 | #include <sys/types.h> | ||
21 | #include <sys/param.h> | ||
22 | #include <sys/stat.h> | ||
23 | #include <sys/socket.h> | ||
24 | #include <sys/un.h> | ||
25 | |||
26 | #include <errno.h> | ||
27 | #include <fcntl.h> | ||
28 | #include <signal.h> | ||
29 | #include <stdarg.h> | ||
30 | #include <stddef.h> | ||
31 | #include <stdlib.h> | ||
32 | #include <stdio.h> | ||
33 | #include <string.h> | ||
34 | #include <unistd.h> | ||
35 | #include <util.h> | ||
36 | #include <paths.h> | ||
37 | |||
38 | #include "openbsd-compat/sys-queue.h" | ||
39 | #include "xmalloc.h" | ||
40 | #include "log.h" | ||
41 | #include "ssh.h" | ||
42 | #include "pathnames.h" | ||
43 | #include "misc.h" | ||
44 | #include "match.h" | ||
45 | #include "buffer.h" | ||
46 | #include "channels.h" | ||
47 | #include "msg.h" | ||
48 | #include "packet.h" | ||
49 | #include "monitor_fdpass.h" | ||
50 | #include "sshpty.h" | ||
51 | #include "key.h" | ||
52 | #include "readconf.h" | ||
53 | #include "clientloop.h" | ||
54 | |||
55 | /* from ssh.c */ | ||
56 | extern int tty_flag; | ||
57 | extern Options options; | ||
58 | extern int stdin_null_flag; | ||
59 | extern char *host; | ||
60 | int subsystem_flag; | ||
61 | extern Buffer command; | ||
62 | |||
63 | /* fd to control socket */ | ||
64 | int muxserver_sock = -1; | ||
65 | |||
66 | /* Multiplexing control command */ | ||
67 | u_int muxclient_command = 0; | ||
68 | |||
69 | /* Set when signalled. */ | ||
70 | static volatile sig_atomic_t muxclient_terminate = 0; | ||
71 | |||
72 | /* PID of multiplex server */ | ||
73 | static u_int muxserver_pid = 0; | ||
74 | |||
75 | |||
76 | /* ** Multiplexing master support */ | ||
77 | |||
78 | /* Prepare a mux master to listen on a Unix domain socket. */ | ||
79 | void | ||
80 | muxserver_listen(void) | ||
81 | { | ||
82 | struct sockaddr_un addr; | ||
83 | mode_t old_umask; | ||
84 | int addr_len; | ||
85 | |||
86 | if (options.control_path == NULL || | ||
87 | options.control_master == SSHCTL_MASTER_NO) | ||
88 | return; | ||
89 | |||
90 | debug("setting up multiplex master socket"); | ||
91 | |||
92 | memset(&addr, '\0', sizeof(addr)); | ||
93 | addr.sun_family = AF_UNIX; | ||
94 | addr_len = offsetof(struct sockaddr_un, sun_path) + | ||
95 | strlen(options.control_path) + 1; | ||
96 | |||
97 | if (strlcpy(addr.sun_path, options.control_path, | ||
98 | sizeof(addr.sun_path)) >= sizeof(addr.sun_path)) | ||
99 | fatal("ControlPath too long"); | ||
100 | |||
101 | if ((muxserver_sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) | ||
102 | fatal("%s socket(): %s", __func__, strerror(errno)); | ||
103 | |||
104 | old_umask = umask(0177); | ||
105 | if (bind(muxserver_sock, (struct sockaddr *)&addr, addr_len) == -1) { | ||
106 | muxserver_sock = -1; | ||
107 | if (errno == EINVAL || errno == EADDRINUSE) | ||
108 | fatal("ControlSocket %s already exists", | ||
109 | options.control_path); | ||
110 | else | ||
111 | fatal("%s bind(): %s", __func__, strerror(errno)); | ||
112 | } | ||
113 | umask(old_umask); | ||
114 | |||
115 | if (listen(muxserver_sock, 64) == -1) | ||
116 | fatal("%s listen(): %s", __func__, strerror(errno)); | ||
117 | |||
118 | set_nonblock(muxserver_sock); | ||
119 | } | ||
120 | |||
121 | /* Callback on open confirmation in mux master for a mux client session. */ | ||
122 | static void | ||
123 | client_extra_session2_setup(int id, void *arg) | ||
124 | { | ||
125 | struct mux_session_confirm_ctx *cctx = arg; | ||
126 | const char *display; | ||
127 | Channel *c; | ||
128 | int i; | ||
129 | |||
130 | if (cctx == NULL) | ||
131 | fatal("%s: cctx == NULL", __func__); | ||
132 | if ((c = channel_lookup(id)) == NULL) | ||
133 | fatal("%s: no channel for id %d", __func__, id); | ||
134 | |||
135 | display = getenv("DISPLAY"); | ||
136 | if (cctx->want_x_fwd && options.forward_x11 && display != NULL) { | ||
137 | char *proto, *data; | ||
138 | /* Get reasonable local authentication information. */ | ||
139 | client_x11_get_proto(display, options.xauth_location, | ||
140 | options.forward_x11_trusted, &proto, &data); | ||
141 | /* Request forwarding with authentication spoofing. */ | ||
142 | debug("Requesting X11 forwarding with authentication spoofing."); | ||
143 | x11_request_forwarding_with_spoofing(id, display, proto, data); | ||
144 | /* XXX wait for reply */ | ||
145 | } | ||
146 | |||
147 | if (cctx->want_agent_fwd && options.forward_agent) { | ||
148 | debug("Requesting authentication agent forwarding."); | ||
149 | channel_request_start(id, "auth-agent-req@openssh.com", 0); | ||
150 | packet_send(); | ||
151 | } | ||
152 | |||
153 | client_session2_setup(id, cctx->want_tty, cctx->want_subsys, | ||
154 | cctx->term, &cctx->tio, c->rfd, &cctx->cmd, cctx->env); | ||
155 | |||
156 | c->open_confirm_ctx = NULL; | ||
157 | buffer_free(&cctx->cmd); | ||
158 | xfree(cctx->term); | ||
159 | if (cctx->env != NULL) { | ||
160 | for (i = 0; cctx->env[i] != NULL; i++) | ||
161 | xfree(cctx->env[i]); | ||
162 | xfree(cctx->env); | ||
163 | } | ||
164 | xfree(cctx); | ||
165 | } | ||
166 | |||
167 | /* | ||
168 | * Accept a connection on the mux master socket and process the | ||
169 | * client's request. Returns flag indicating whether mux master should | ||
170 | * begin graceful close. | ||
171 | */ | ||
172 | int | ||
173 | muxserver_accept_control(void) | ||
174 | { | ||
175 | Buffer m; | ||
176 | Channel *c; | ||
177 | int client_fd, new_fd[3], ver, allowed, window, packetmax; | ||
178 | socklen_t addrlen; | ||
179 | struct sockaddr_storage addr; | ||
180 | struct mux_session_confirm_ctx *cctx; | ||
181 | char *cmd; | ||
182 | u_int i, j, len, env_len, mux_command, flags; | ||
183 | uid_t euid; | ||
184 | gid_t egid; | ||
185 | int start_close = 0; | ||
186 | |||
187 | /* | ||
188 | * Accept connection on control socket | ||
189 | */ | ||
190 | memset(&addr, 0, sizeof(addr)); | ||
191 | addrlen = sizeof(addr); | ||
192 | if ((client_fd = accept(muxserver_sock, | ||
193 | (struct sockaddr*)&addr, &addrlen)) == -1) { | ||
194 | error("%s accept: %s", __func__, strerror(errno)); | ||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | if (getpeereid(client_fd, &euid, &egid) < 0) { | ||
199 | error("%s getpeereid failed: %s", __func__, strerror(errno)); | ||
200 | close(client_fd); | ||
201 | return 0; | ||
202 | } | ||
203 | if ((euid != 0) && (getuid() != euid)) { | ||
204 | error("control mode uid mismatch: peer euid %u != uid %u", | ||
205 | (u_int) euid, (u_int) getuid()); | ||
206 | close(client_fd); | ||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | /* XXX handle asynchronously */ | ||
211 | unset_nonblock(client_fd); | ||
212 | |||
213 | /* Read command */ | ||
214 | buffer_init(&m); | ||
215 | if (ssh_msg_recv(client_fd, &m) == -1) { | ||
216 | error("%s: client msg_recv failed", __func__); | ||
217 | close(client_fd); | ||
218 | buffer_free(&m); | ||
219 | return 0; | ||
220 | } | ||
221 | if ((ver = buffer_get_char(&m)) != SSHMUX_VER) { | ||
222 | error("%s: wrong client version %d", __func__, ver); | ||
223 | buffer_free(&m); | ||
224 | close(client_fd); | ||
225 | return 0; | ||
226 | } | ||
227 | |||
228 | allowed = 1; | ||
229 | mux_command = buffer_get_int(&m); | ||
230 | flags = buffer_get_int(&m); | ||
231 | |||
232 | buffer_clear(&m); | ||
233 | |||
234 | switch (mux_command) { | ||
235 | case SSHMUX_COMMAND_OPEN: | ||
236 | if (options.control_master == SSHCTL_MASTER_ASK || | ||
237 | options.control_master == SSHCTL_MASTER_AUTO_ASK) | ||
238 | allowed = ask_permission("Allow shared connection " | ||
239 | "to %s? ", host); | ||
240 | /* continue below */ | ||
241 | break; | ||
242 | case SSHMUX_COMMAND_TERMINATE: | ||
243 | if (options.control_master == SSHCTL_MASTER_ASK || | ||
244 | options.control_master == SSHCTL_MASTER_AUTO_ASK) | ||
245 | allowed = ask_permission("Terminate shared connection " | ||
246 | "to %s? ", host); | ||
247 | if (allowed) | ||
248 | start_close = 1; | ||
249 | /* FALLTHROUGH */ | ||
250 | case SSHMUX_COMMAND_ALIVE_CHECK: | ||
251 | /* Reply for SSHMUX_COMMAND_TERMINATE and ALIVE_CHECK */ | ||
252 | buffer_clear(&m); | ||
253 | buffer_put_int(&m, allowed); | ||
254 | buffer_put_int(&m, getpid()); | ||
255 | if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) { | ||
256 | error("%s: client msg_send failed", __func__); | ||
257 | close(client_fd); | ||
258 | buffer_free(&m); | ||
259 | return start_close; | ||
260 | } | ||
261 | buffer_free(&m); | ||
262 | close(client_fd); | ||
263 | return start_close; | ||
264 | default: | ||
265 | error("Unsupported command %d", mux_command); | ||
266 | buffer_free(&m); | ||
267 | close(client_fd); | ||
268 | return 0; | ||
269 | } | ||
270 | |||
271 | /* Reply for SSHMUX_COMMAND_OPEN */ | ||
272 | buffer_clear(&m); | ||
273 | buffer_put_int(&m, allowed); | ||
274 | buffer_put_int(&m, getpid()); | ||
275 | if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) { | ||
276 | error("%s: client msg_send failed", __func__); | ||
277 | close(client_fd); | ||
278 | buffer_free(&m); | ||
279 | return 0; | ||
280 | } | ||
281 | |||
282 | if (!allowed) { | ||
283 | error("Refused control connection"); | ||
284 | close(client_fd); | ||
285 | buffer_free(&m); | ||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | buffer_clear(&m); | ||
290 | if (ssh_msg_recv(client_fd, &m) == -1) { | ||
291 | error("%s: client msg_recv failed", __func__); | ||
292 | close(client_fd); | ||
293 | buffer_free(&m); | ||
294 | return 0; | ||
295 | } | ||
296 | if ((ver = buffer_get_char(&m)) != SSHMUX_VER) { | ||
297 | error("%s: wrong client version %d", __func__, ver); | ||
298 | buffer_free(&m); | ||
299 | close(client_fd); | ||
300 | return 0; | ||
301 | } | ||
302 | |||
303 | cctx = xcalloc(1, sizeof(*cctx)); | ||
304 | cctx->want_tty = (flags & SSHMUX_FLAG_TTY) != 0; | ||
305 | cctx->want_subsys = (flags & SSHMUX_FLAG_SUBSYS) != 0; | ||
306 | cctx->want_x_fwd = (flags & SSHMUX_FLAG_X11_FWD) != 0; | ||
307 | cctx->want_agent_fwd = (flags & SSHMUX_FLAG_AGENT_FWD) != 0; | ||
308 | cctx->term = buffer_get_string(&m, &len); | ||
309 | |||
310 | cmd = buffer_get_string(&m, &len); | ||
311 | buffer_init(&cctx->cmd); | ||
312 | buffer_append(&cctx->cmd, cmd, strlen(cmd)); | ||
313 | |||
314 | env_len = buffer_get_int(&m); | ||
315 | env_len = MIN(env_len, 4096); | ||
316 | debug3("%s: receiving %d env vars", __func__, env_len); | ||
317 | if (env_len != 0) { | ||
318 | cctx->env = xcalloc(env_len + 1, sizeof(*cctx->env)); | ||
319 | for (i = 0; i < env_len; i++) | ||
320 | cctx->env[i] = buffer_get_string(&m, &len); | ||
321 | cctx->env[i] = NULL; | ||
322 | } | ||
323 | |||
324 | debug2("%s: accepted tty %d, subsys %d, cmd %s", __func__, | ||
325 | cctx->want_tty, cctx->want_subsys, cmd); | ||
326 | xfree(cmd); | ||
327 | |||
328 | /* Gather fds from client */ | ||
329 | for(i = 0; i < 3; i++) { | ||
330 | if ((new_fd[i] = mm_receive_fd(client_fd)) == -1) { | ||
331 | error("%s: failed to receive fd %d from slave", | ||
332 | __func__, i); | ||
333 | for (j = 0; j < i; j++) | ||
334 | close(new_fd[j]); | ||
335 | for (j = 0; j < env_len; j++) | ||
336 | xfree(cctx->env[j]); | ||
337 | if (env_len > 0) | ||
338 | xfree(cctx->env); | ||
339 | xfree(cctx->term); | ||
340 | buffer_free(&cctx->cmd); | ||
341 | close(client_fd); | ||
342 | xfree(cctx); | ||
343 | return 0; | ||
344 | } | ||
345 | } | ||
346 | |||
347 | debug2("%s: got fds stdin %d, stdout %d, stderr %d", __func__, | ||
348 | new_fd[0], new_fd[1], new_fd[2]); | ||
349 | |||
350 | /* Try to pick up ttymodes from client before it goes raw */ | ||
351 | if (cctx->want_tty && tcgetattr(new_fd[0], &cctx->tio) == -1) | ||
352 | error("%s: tcgetattr: %s", __func__, strerror(errno)); | ||
353 | |||
354 | /* This roundtrip is just for synchronisation of ttymodes */ | ||
355 | buffer_clear(&m); | ||
356 | if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) { | ||
357 | error("%s: client msg_send failed", __func__); | ||
358 | close(client_fd); | ||
359 | close(new_fd[0]); | ||
360 | close(new_fd[1]); | ||
361 | close(new_fd[2]); | ||
362 | buffer_free(&m); | ||
363 | xfree(cctx->term); | ||
364 | if (env_len != 0) { | ||
365 | for (i = 0; i < env_len; i++) | ||
366 | xfree(cctx->env[i]); | ||
367 | xfree(cctx->env); | ||
368 | } | ||
369 | return 0; | ||
370 | } | ||
371 | buffer_free(&m); | ||
372 | |||
373 | /* enable nonblocking unless tty */ | ||
374 | if (!isatty(new_fd[0])) | ||
375 | set_nonblock(new_fd[0]); | ||
376 | if (!isatty(new_fd[1])) | ||
377 | set_nonblock(new_fd[1]); | ||
378 | if (!isatty(new_fd[2])) | ||
379 | set_nonblock(new_fd[2]); | ||
380 | |||
381 | set_nonblock(client_fd); | ||
382 | |||
383 | window = CHAN_SES_WINDOW_DEFAULT; | ||
384 | packetmax = CHAN_SES_PACKET_DEFAULT; | ||
385 | if (cctx->want_tty) { | ||
386 | window >>= 1; | ||
387 | packetmax >>= 1; | ||
388 | } | ||
389 | |||
390 | c = channel_new("session", SSH_CHANNEL_OPENING, | ||
391 | new_fd[0], new_fd[1], new_fd[2], window, packetmax, | ||
392 | CHAN_EXTENDED_WRITE, "client-session", /*nonblock*/0); | ||
393 | |||
394 | /* XXX */ | ||
395 | c->ctl_fd = client_fd; | ||
396 | |||
397 | debug3("%s: channel_new: %d", __func__, c->self); | ||
398 | |||
399 | channel_send_open(c->self); | ||
400 | channel_register_open_confirm(c->self, | ||
401 | client_extra_session2_setup, cctx); | ||
402 | return 0; | ||
403 | } | ||
404 | |||
405 | /* ** Multiplexing client support */ | ||
406 | |||
407 | /* Exit signal handler */ | ||
408 | static void | ||
409 | control_client_sighandler(int signo) | ||
410 | { | ||
411 | muxclient_terminate = signo; | ||
412 | } | ||
413 | |||
414 | /* | ||
415 | * Relay signal handler - used to pass some signals from mux client to | ||
416 | * mux master. | ||
417 | */ | ||
418 | static void | ||
419 | control_client_sigrelay(int signo) | ||
420 | { | ||
421 | int save_errno = errno; | ||
422 | |||
423 | if (muxserver_pid > 1) | ||
424 | kill(muxserver_pid, signo); | ||
425 | |||
426 | errno = save_errno; | ||
427 | } | ||
428 | |||
429 | /* Check mux client environment variables before passing them to mux master. */ | ||
430 | static int | ||
431 | env_permitted(char *env) | ||
432 | { | ||
433 | int i, ret; | ||
434 | char name[1024], *cp; | ||
435 | |||
436 | if ((cp = strchr(env, '=')) == NULL || cp == env) | ||
437 | return (0); | ||
438 | ret = snprintf(name, sizeof(name), "%.*s", (int)(cp - env), env); | ||
439 | if (ret <= 0 || (size_t)ret >= sizeof(name)) | ||
440 | fatal("env_permitted: name '%.100s...' too long", env); | ||
441 | |||
442 | for (i = 0; i < options.num_send_env; i++) | ||
443 | if (match_pattern(name, options.send_env[i])) | ||
444 | return (1); | ||
445 | |||
446 | return (0); | ||
447 | } | ||
448 | |||
449 | /* Multiplex client main loop. */ | ||
450 | void | ||
451 | muxclient(const char *path) | ||
452 | { | ||
453 | struct sockaddr_un addr; | ||
454 | int i, r, fd, sock, exitval[2], num_env, addr_len; | ||
455 | Buffer m; | ||
456 | char *term; | ||
457 | extern char **environ; | ||
458 | u_int flags; | ||
459 | |||
460 | if (muxclient_command == 0) | ||
461 | muxclient_command = SSHMUX_COMMAND_OPEN; | ||
462 | |||
463 | switch (options.control_master) { | ||
464 | case SSHCTL_MASTER_AUTO: | ||
465 | case SSHCTL_MASTER_AUTO_ASK: | ||
466 | debug("auto-mux: Trying existing master"); | ||
467 | /* FALLTHROUGH */ | ||
468 | case SSHCTL_MASTER_NO: | ||
469 | break; | ||
470 | default: | ||
471 | return; | ||
472 | } | ||
473 | |||
474 | memset(&addr, '\0', sizeof(addr)); | ||
475 | addr.sun_family = AF_UNIX; | ||
476 | addr_len = offsetof(struct sockaddr_un, sun_path) + | ||
477 | strlen(path) + 1; | ||
478 | |||
479 | if (strlcpy(addr.sun_path, path, | ||
480 | sizeof(addr.sun_path)) >= sizeof(addr.sun_path)) | ||
481 | fatal("ControlPath too long"); | ||
482 | |||
483 | if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) | ||
484 | fatal("%s socket(): %s", __func__, strerror(errno)); | ||
485 | |||
486 | if (connect(sock, (struct sockaddr *)&addr, addr_len) == -1) { | ||
487 | if (muxclient_command != SSHMUX_COMMAND_OPEN) { | ||
488 | fatal("Control socket connect(%.100s): %s", path, | ||
489 | strerror(errno)); | ||
490 | } | ||
491 | if (errno == ENOENT) | ||
492 | debug("Control socket \"%.100s\" does not exist", path); | ||
493 | else { | ||
494 | error("Control socket connect(%.100s): %s", path, | ||
495 | strerror(errno)); | ||
496 | } | ||
497 | close(sock); | ||
498 | return; | ||
499 | } | ||
500 | |||
501 | if (stdin_null_flag) { | ||
502 | if ((fd = open(_PATH_DEVNULL, O_RDONLY)) == -1) | ||
503 | fatal("open(/dev/null): %s", strerror(errno)); | ||
504 | if (dup2(fd, STDIN_FILENO) == -1) | ||
505 | fatal("dup2: %s", strerror(errno)); | ||
506 | if (fd > STDERR_FILENO) | ||
507 | close(fd); | ||
508 | } | ||
509 | |||
510 | term = getenv("TERM"); | ||
511 | |||
512 | flags = 0; | ||
513 | if (tty_flag) | ||
514 | flags |= SSHMUX_FLAG_TTY; | ||
515 | if (subsystem_flag) | ||
516 | flags |= SSHMUX_FLAG_SUBSYS; | ||
517 | if (options.forward_x11) | ||
518 | flags |= SSHMUX_FLAG_X11_FWD; | ||
519 | if (options.forward_agent) | ||
520 | flags |= SSHMUX_FLAG_AGENT_FWD; | ||
521 | |||
522 | signal(SIGPIPE, SIG_IGN); | ||
523 | |||
524 | buffer_init(&m); | ||
525 | |||
526 | /* Send our command to server */ | ||
527 | buffer_put_int(&m, muxclient_command); | ||
528 | buffer_put_int(&m, flags); | ||
529 | if (ssh_msg_send(sock, SSHMUX_VER, &m) == -1) | ||
530 | fatal("%s: msg_send", __func__); | ||
531 | buffer_clear(&m); | ||
532 | |||
533 | /* Get authorisation status and PID of controlee */ | ||
534 | if (ssh_msg_recv(sock, &m) == -1) | ||
535 | fatal("%s: msg_recv", __func__); | ||
536 | if (buffer_get_char(&m) != SSHMUX_VER) | ||
537 | fatal("%s: wrong version", __func__); | ||
538 | if (buffer_get_int(&m) != 1) | ||
539 | fatal("Connection to master denied"); | ||
540 | muxserver_pid = buffer_get_int(&m); | ||
541 | |||
542 | buffer_clear(&m); | ||
543 | |||
544 | switch (muxclient_command) { | ||
545 | case SSHMUX_COMMAND_ALIVE_CHECK: | ||
546 | fprintf(stderr, "Master running (pid=%d)\r\n", | ||
547 | muxserver_pid); | ||
548 | exit(0); | ||
549 | case SSHMUX_COMMAND_TERMINATE: | ||
550 | fprintf(stderr, "Exit request sent.\r\n"); | ||
551 | exit(0); | ||
552 | case SSHMUX_COMMAND_OPEN: | ||
553 | /* continue below */ | ||
554 | break; | ||
555 | default: | ||
556 | fatal("silly muxclient_command %d", muxclient_command); | ||
557 | } | ||
558 | |||
559 | /* SSHMUX_COMMAND_OPEN */ | ||
560 | buffer_put_cstring(&m, term ? term : ""); | ||
561 | buffer_append(&command, "\0", 1); | ||
562 | buffer_put_cstring(&m, buffer_ptr(&command)); | ||
563 | |||
564 | if (options.num_send_env == 0 || environ == NULL) { | ||
565 | buffer_put_int(&m, 0); | ||
566 | } else { | ||
567 | /* Pass environment */ | ||
568 | num_env = 0; | ||
569 | for (i = 0; environ[i] != NULL; i++) | ||
570 | if (env_permitted(environ[i])) | ||
571 | num_env++; /* Count */ | ||
572 | |||
573 | buffer_put_int(&m, num_env); | ||
574 | |||
575 | for (i = 0; environ[i] != NULL && num_env >= 0; i++) | ||
576 | if (env_permitted(environ[i])) { | ||
577 | num_env--; | ||
578 | buffer_put_cstring(&m, environ[i]); | ||
579 | } | ||
580 | } | ||
581 | |||
582 | if (ssh_msg_send(sock, SSHMUX_VER, &m) == -1) | ||
583 | fatal("%s: msg_send", __func__); | ||
584 | |||
585 | if (mm_send_fd(sock, STDIN_FILENO) == -1 || | ||
586 | mm_send_fd(sock, STDOUT_FILENO) == -1 || | ||
587 | mm_send_fd(sock, STDERR_FILENO) == -1) | ||
588 | fatal("%s: send fds failed", __func__); | ||
589 | |||
590 | /* Wait for reply, so master has a chance to gather ttymodes */ | ||
591 | buffer_clear(&m); | ||
592 | if (ssh_msg_recv(sock, &m) == -1) | ||
593 | fatal("%s: msg_recv", __func__); | ||
594 | if (buffer_get_char(&m) != SSHMUX_VER) | ||
595 | fatal("%s: wrong version", __func__); | ||
596 | buffer_free(&m); | ||
597 | |||
598 | signal(SIGHUP, control_client_sighandler); | ||
599 | signal(SIGINT, control_client_sighandler); | ||
600 | signal(SIGTERM, control_client_sighandler); | ||
601 | signal(SIGWINCH, control_client_sigrelay); | ||
602 | |||
603 | if (tty_flag) | ||
604 | enter_raw_mode(); | ||
605 | |||
606 | /* | ||
607 | * Stick around until the controlee closes the client_fd. | ||
608 | * Before it does, it is expected to write this process' exit | ||
609 | * value (one int). This process must read the value and wait for | ||
610 | * the closure of the client_fd; if this one closes early, the | ||
611 | * multiplex master will terminate early too (possibly losing data). | ||
612 | */ | ||
613 | exitval[0] = 0; | ||
614 | for (i = 0; !muxclient_terminate && i < (int)sizeof(exitval);) { | ||
615 | r = read(sock, (char *)exitval + i, sizeof(exitval) - i); | ||
616 | if (r == 0) { | ||
617 | debug2("Received EOF from master"); | ||
618 | break; | ||
619 | } | ||
620 | if (r == -1) { | ||
621 | if (errno == EINTR) | ||
622 | continue; | ||
623 | fatal("%s: read %s", __func__, strerror(errno)); | ||
624 | } | ||
625 | i += r; | ||
626 | } | ||
627 | |||
628 | close(sock); | ||
629 | leave_raw_mode(); | ||
630 | if (i > (int)sizeof(int)) | ||
631 | fatal("%s: master returned too much data (%d > %lu)", | ||
632 | __func__, i, sizeof(int)); | ||
633 | if (muxclient_terminate) { | ||
634 | debug2("Exiting on signal %d", muxclient_terminate); | ||
635 | exitval[0] = 255; | ||
636 | } else if (i < (int)sizeof(int)) { | ||
637 | debug2("Control master terminated unexpectedly"); | ||
638 | exitval[0] = 255; | ||
639 | } else | ||
640 | debug2("Received exit status from master %d", exitval[0]); | ||
641 | |||
642 | if (tty_flag && options.log_level != SYSLOG_LEVEL_QUIET) | ||
643 | fprintf(stderr, "Shared connection to %s closed.\r\n", host); | ||
644 | |||
645 | exit(exitval[0]); | ||
646 | } | ||