summaryrefslogtreecommitdiff
path: root/mux.c
diff options
context:
space:
mode:
authorColin Watson <cjwatson@debian.org>2008-07-22 19:45:18 +0000
committerColin Watson <cjwatson@debian.org>2008-07-22 19:45:18 +0000
commit137d76ba65883aa8143af1fcad83b57e7badef0c (patch)
treef426e804bb5248ceafedfab7bb78ae6e6752942c /mux.c
parentdac7d049dad31f5f84d421d4eb628a7e13f977d7 (diff)
parentef94e5613d37bcbf880f21ee6094e4b1c7683a4c (diff)
* New upstream release (closes: #474301). Important changes not previously
backported to 4.7p1: - 4.9/4.9p1 (http://www.openssh.com/txt/release-4.9): + Added chroot(2) support for sshd(8), controlled by a new option "ChrootDirectory" (closes: #139047, LP: #24777). + Linked sftp-server(8) into sshd(8). The internal sftp server is used when the command "internal-sftp" is specified in a Subsystem or ForceCommand declaration. When used with ChrootDirectory, the internal sftp server requires no special configuration of files inside the chroot environment. + Added a protocol extension method "posix-rename@openssh.com" for sftp-server(8) to perform POSIX atomic rename() operations; sftp(1) prefers this if available (closes: #308561). + Removed the fixed limit of 100 file handles in sftp-server(8). + ssh(8) will now skip generation of SSH protocol 1 ephemeral server keys when in inetd mode and protocol 2 connections are negotiated. This speeds up protocol 2 connections to inetd-mode servers that also allow Protocol 1. + Accept the PermitRootLogin directive in a sshd_config(5) Match block. Allows for, e.g. permitting root only from the local network. + Reworked sftp(1) argument splitting and escaping to be more internally consistent (i.e. between sftp commands) and more consistent with sh(1). Please note that this will change the interpretation of some quoted strings, especially those with embedded backslash escape sequences. + Support "Banner=none" in sshd_config(5) to disable sending of a pre-login banner (e.g. in a Match block). + ssh(1) ProxyCommands are now executed with $SHELL rather than /bin/sh. + ssh(1)'s ConnectTimeout option is now applied to both the TCP connection and the SSH banner exchange (previously it just covered the TCP connection). This allows callers of ssh(1) to better detect and deal with stuck servers that accept a TCP connection but don't progress the protocol, and also makes ConnectTimeout useful for connections via a ProxyCommand. + scp(1) incorrectly reported "stalled" on slow copies (closes: #140828). + scp(1) date underflow for timestamps before epoch. + ssh(1) used the obsolete SIG DNS RRtype for host keys in DNS, instead of the current standard RRSIG. + Correctly drain ACKs when a sftp(1) upload write fails midway, avoids a fatal() exit from what should be a recoverable condition. + Fixed ssh-keygen(1) selective host key hashing (i.e. "ssh-keygen -HF hostname") to not include any IP address in the data to be hashed. + Make ssh(1) skip listening on the IPv6 wildcard address when a binding address of 0.0.0.0 is used against an old SSH server that does not support the RFC4254 syntax for wildcard bind addresses. + Enable IPV6_V6ONLY socket option on sshd(8) listen socket, as is already done for X11/TCP forwarding sockets (closes: #439661). + Fix FD leak that could hang a ssh(1) connection multiplexing master. + Make ssh(1) -q option documentation consistent with reality. + Fixed sshd(8) PAM support not calling pam_session_close(), or failing to call it with root privileges (closes: #372680). + Fix activation of OpenSSL engine support when requested in configure (LP: #119295). - 5.1/5.1p1 (http://www.openssh.com/txt/release-5.1): + Introduce experimental SSH Fingerprint ASCII Visualisation to ssh(1) and ssh-keygen(1). Visual fingerprint display is controlled by a new ssh_config(5) option "VisualHostKey". The intent is to render SSH host keys in a visual form that is amenable to easy recall and rejection of changed host keys. + sshd_config(5) now supports CIDR address/masklen matching in "Match address" blocks, with a fallback to classic wildcard matching. + sshd(8) now supports CIDR matching in ~/.ssh/authorized_keys from="..." restrictions, also with a fallback to classic wildcard matching. + Added an extended test mode (-T) to sshd(8) to request that it write its effective configuration to stdout and exit. Extended test mode also supports the specification of connection parameters (username, source address and hostname) to test the application of sshd_config(5) Match rules. + ssh(1) now prints the number of bytes transferred and the overall connection throughput for SSH protocol 2 sessions when in verbose mode (previously these statistics were displayed for protocol 1 connections only). + sftp-server(8) now supports extension methods statvfs@openssh.com and fstatvfs@openssh.com that implement statvfs(2)-like operations. + sftp(1) now has a "df" command to the sftp client that uses the statvfs@openssh.com to produce a df(1)-like display of filesystem space and inode utilisation (requires statvfs@openssh.com support on the server). + Added a MaxSessions option to sshd_config(5) to allow control of the number of multiplexed sessions supported over a single TCP connection. This allows increasing the number of allowed sessions above the previous default of 10, disabling connection multiplexing (MaxSessions=1) or disallowing login/shell/subsystem sessions entirely (MaxSessions=0). + Added a no-more-sessions@openssh.com global request extension that is sent from ssh(1) to sshd(8) when the client knows that it will never request another session (i.e. when session multiplexing is disabled). This allows a server to disallow further session requests and terminate the session in cases where the client has been hijacked. + ssh-keygen(1) now supports the use of the -l option in combination with -F to search for a host in ~/.ssh/known_hosts and display its fingerprint. + ssh-keyscan(1) now defaults to "rsa" (protocol 2) keys, instead of "rsa1". + Added an AllowAgentForwarding option to sshd_config(8) to control whether authentication agent forwarding is permitted. Note that this is a loose control, as a client may install their own unofficial forwarder. + ssh(1) and sshd(8): avoid unnecessary malloc/copy/free when receiving network data, resulting in a ~10% speedup. + ssh(1) and sshd(8) will now try additional addresses when connecting to a port forward destination whose DNS name resolves to more than one address. The previous behaviour was to try the only first address and give up if that failed. + ssh(1) and sshd(8) now support signalling that channels are half-closed for writing, through a channel protocol extension notification "eow@openssh.com". This allows propagation of closed file descriptors, so that commands such as "ssh -2 localhost od /bin/ls | true" do not send unnecessary data over the wire. + sshd(8): increased the default size of ssh protocol 1 ephemeral keys from 768 to 1024 bits. + When ssh(1) has been requested to fork after authentication ("ssh -f") with ExitOnForwardFailure enabled, delay the fork until after replies for any -R forwards have been seen. Allows for robust detection of -R forward failure when using -f. + "Match group" blocks in sshd_config(5) now support negation of groups. E.g. "Match group staff,!guests". + sftp(1) and sftp-server(8) now allow chmod-like operations to set set[ug]id/sticky bits. + The MaxAuthTries option is now permitted in sshd_config(5) match blocks. + Multiplexed ssh(1) sessions now support a subset of the ~ escapes that are available to a primary connection. + ssh(1) connection multiplexing will now fall back to creating a new connection in most error cases (closes: #352830). + Make ssh(1) deal more gracefully with channel requests that fail. Previously it would optimistically assume that requests would always succeed, which could cause hangs if they did not (e.g. when the server runs out of file descriptors). + ssh(1) now reports multiplexing errors via the multiplex slave's stderr where possible (subject to LogLevel in the mux master). + Prevent sshd(8) from erroneously applying public key restrictions leaned from ~/.ssh/authorized_keys to other authentication methods when public key authentication subsequently fails (LP: #161047). + Fixed an UMAC alignment problem that manifested on Itanium platforms.
Diffstat (limited to 'mux.c')
-rw-r--r--mux.c728
1 files changed, 728 insertions, 0 deletions
diff --git a/mux.c b/mux.c
new file mode 100644
index 000000000..4669e5328
--- /dev/null
+++ b/mux.c
@@ -0,0 +1,728 @@
1/* $OpenBSD: mux.c,v 1.7 2008/06/13 17:21:20 dtucker 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 "includes.h"
21
22/*
23 * TODO:
24 * 1. partial reads in muxserver_accept_control (maybe make channels
25 * from accepted connections)
26 * 2. Better signalling from master to slave, especially passing of
27 * error messages
28 * 3. Better fall-back from mux slave error to new connection.
29 * 3. Add/delete forwardings via slave
30 * 4. ExitOnForwardingFailure (after #3 obviously)
31 * 5. Maybe extension mechanisms for multi-X11/multi-agent forwarding
32 * 6. Document the mux mini-protocol somewhere.
33 * 7. Support ~^Z in mux slaves.
34 * 8. Inspect or control sessions in master.
35 * 9. If we ever support the "signal" channel request, send signals on
36 * sessions in master.
37 */
38
39#include <sys/types.h>
40#include <sys/param.h>
41#include <sys/stat.h>
42#include <sys/socket.h>
43#include <sys/un.h>
44
45#include <errno.h>
46#include <fcntl.h>
47#include <signal.h>
48#include <stdarg.h>
49#include <stddef.h>
50#include <stdlib.h>
51#include <stdio.h>
52#include <string.h>
53#include <unistd.h>
54#ifdef HAVE_PATHS_H
55#include <paths.h>
56#endif
57
58#ifdef HAVE_UTIL_H
59# include <util.h>
60#endif
61
62#ifdef HAVE_LIBUTIL_H
63# include <libutil.h>
64#endif
65
66#include "openbsd-compat/sys-queue.h"
67#include "xmalloc.h"
68#include "log.h"
69#include "ssh.h"
70#include "pathnames.h"
71#include "misc.h"
72#include "match.h"
73#include "buffer.h"
74#include "channels.h"
75#include "msg.h"
76#include "packet.h"
77#include "monitor_fdpass.h"
78#include "sshpty.h"
79#include "key.h"
80#include "readconf.h"
81#include "clientloop.h"
82
83/* from ssh.c */
84extern int tty_flag;
85extern Options options;
86extern int stdin_null_flag;
87extern char *host;
88int subsystem_flag;
89extern Buffer command;
90
91/* Context for session open confirmation callback */
92struct mux_session_confirm_ctx {
93 int want_tty;
94 int want_subsys;
95 int want_x_fwd;
96 int want_agent_fwd;
97 Buffer cmd;
98 char *term;
99 struct termios tio;
100 char **env;
101};
102
103/* fd to control socket */
104int muxserver_sock = -1;
105
106/* Multiplexing control command */
107u_int muxclient_command = 0;
108
109/* Set when signalled. */
110static volatile sig_atomic_t muxclient_terminate = 0;
111
112/* PID of multiplex server */
113static u_int muxserver_pid = 0;
114
115
116/* ** Multiplexing master support */
117
118/* Prepare a mux master to listen on a Unix domain socket. */
119void
120muxserver_listen(void)
121{
122 struct sockaddr_un addr;
123 mode_t old_umask;
124 int addr_len;
125
126 if (options.control_path == NULL ||
127 options.control_master == SSHCTL_MASTER_NO)
128 return;
129
130 debug("setting up multiplex master socket");
131
132 memset(&addr, '\0', sizeof(addr));
133 addr.sun_family = AF_UNIX;
134 addr_len = offsetof(struct sockaddr_un, sun_path) +
135 strlen(options.control_path) + 1;
136
137 if (strlcpy(addr.sun_path, options.control_path,
138 sizeof(addr.sun_path)) >= sizeof(addr.sun_path))
139 fatal("ControlPath too long");
140
141 if ((muxserver_sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
142 fatal("%s socket(): %s", __func__, strerror(errno));
143
144 old_umask = umask(0177);
145 if (bind(muxserver_sock, (struct sockaddr *)&addr, addr_len) == -1) {
146 muxserver_sock = -1;
147 if (errno == EINVAL || errno == EADDRINUSE) {
148 error("ControlSocket %s already exists, "
149 "disabling multiplexing", options.control_path);
150 close(muxserver_sock);
151 muxserver_sock = -1;
152 xfree(options.control_path);
153 options.control_path = NULL;
154 options.control_master = SSHCTL_MASTER_NO;
155 return;
156 } else
157 fatal("%s bind(): %s", __func__, strerror(errno));
158 }
159 umask(old_umask);
160
161 if (listen(muxserver_sock, 64) == -1)
162 fatal("%s listen(): %s", __func__, strerror(errno));
163
164 set_nonblock(muxserver_sock);
165}
166
167/* Callback on open confirmation in mux master for a mux client session. */
168static void
169mux_session_confirm(int id, void *arg)
170{
171 struct mux_session_confirm_ctx *cctx = arg;
172 const char *display;
173 Channel *c;
174 int i;
175
176 if (cctx == NULL)
177 fatal("%s: cctx == NULL", __func__);
178 if ((c = channel_lookup(id)) == NULL)
179 fatal("%s: no channel for id %d", __func__, id);
180
181 display = getenv("DISPLAY");
182 if (cctx->want_x_fwd && options.forward_x11 && display != NULL) {
183 char *proto, *data;
184 /* Get reasonable local authentication information. */
185 client_x11_get_proto(display, options.xauth_location,
186 options.forward_x11_trusted, &proto, &data);
187 /* Request forwarding with authentication spoofing. */
188 debug("Requesting X11 forwarding with authentication spoofing.");
189 x11_request_forwarding_with_spoofing(id, display, proto, data);
190 /* XXX wait for reply */
191 }
192
193 if (cctx->want_agent_fwd && options.forward_agent) {
194 debug("Requesting authentication agent forwarding.");
195 channel_request_start(id, "auth-agent-req@openssh.com", 0);
196 packet_send();
197 }
198
199 client_session2_setup(id, cctx->want_tty, cctx->want_subsys,
200 cctx->term, &cctx->tio, c->rfd, &cctx->cmd, cctx->env);
201
202 c->open_confirm_ctx = NULL;
203 buffer_free(&cctx->cmd);
204 xfree(cctx->term);
205 if (cctx->env != NULL) {
206 for (i = 0; cctx->env[i] != NULL; i++)
207 xfree(cctx->env[i]);
208 xfree(cctx->env);
209 }
210 xfree(cctx);
211}
212
213/*
214 * Accept a connection on the mux master socket and process the
215 * client's request. Returns flag indicating whether mux master should
216 * begin graceful close.
217 */
218int
219muxserver_accept_control(void)
220{
221 Buffer m;
222 Channel *c;
223 int client_fd, new_fd[3], ver, allowed, window, packetmax;
224 socklen_t addrlen;
225 struct sockaddr_storage addr;
226 struct mux_session_confirm_ctx *cctx;
227 char *cmd;
228 u_int i, j, len, env_len, mux_command, flags, escape_char;
229 uid_t euid;
230 gid_t egid;
231 int start_close = 0;
232
233 /*
234 * Accept connection on control socket
235 */
236 memset(&addr, 0, sizeof(addr));
237 addrlen = sizeof(addr);
238 if ((client_fd = accept(muxserver_sock,
239 (struct sockaddr*)&addr, &addrlen)) == -1) {
240 error("%s accept: %s", __func__, strerror(errno));
241 return 0;
242 }
243
244 if (getpeereid(client_fd, &euid, &egid) < 0) {
245 error("%s getpeereid failed: %s", __func__, strerror(errno));
246 close(client_fd);
247 return 0;
248 }
249 if ((euid != 0) && (getuid() != euid)) {
250 error("control mode uid mismatch: peer euid %u != uid %u",
251 (u_int) euid, (u_int) getuid());
252 close(client_fd);
253 return 0;
254 }
255
256 /* XXX handle asynchronously */
257 unset_nonblock(client_fd);
258
259 /* Read command */
260 buffer_init(&m);
261 if (ssh_msg_recv(client_fd, &m) == -1) {
262 error("%s: client msg_recv failed", __func__);
263 close(client_fd);
264 buffer_free(&m);
265 return 0;
266 }
267 if ((ver = buffer_get_char(&m)) != SSHMUX_VER) {
268 error("%s: wrong client version %d", __func__, ver);
269 buffer_free(&m);
270 close(client_fd);
271 return 0;
272 }
273
274 allowed = 1;
275 mux_command = buffer_get_int(&m);
276 flags = buffer_get_int(&m);
277
278 buffer_clear(&m);
279
280 switch (mux_command) {
281 case SSHMUX_COMMAND_OPEN:
282 if (options.control_master == SSHCTL_MASTER_ASK ||
283 options.control_master == SSHCTL_MASTER_AUTO_ASK)
284 allowed = ask_permission("Allow shared connection "
285 "to %s? ", host);
286 /* continue below */
287 break;
288 case SSHMUX_COMMAND_TERMINATE:
289 if (options.control_master == SSHCTL_MASTER_ASK ||
290 options.control_master == SSHCTL_MASTER_AUTO_ASK)
291 allowed = ask_permission("Terminate shared connection "
292 "to %s? ", host);
293 if (allowed)
294 start_close = 1;
295 /* FALLTHROUGH */
296 case SSHMUX_COMMAND_ALIVE_CHECK:
297 /* Reply for SSHMUX_COMMAND_TERMINATE and ALIVE_CHECK */
298 buffer_clear(&m);
299 buffer_put_int(&m, allowed);
300 buffer_put_int(&m, getpid());
301 if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) {
302 error("%s: client msg_send failed", __func__);
303 close(client_fd);
304 buffer_free(&m);
305 return start_close;
306 }
307 buffer_free(&m);
308 close(client_fd);
309 return start_close;
310 default:
311 error("Unsupported command %d", mux_command);
312 buffer_free(&m);
313 close(client_fd);
314 return 0;
315 }
316
317 /* Reply for SSHMUX_COMMAND_OPEN */
318 buffer_clear(&m);
319 buffer_put_int(&m, allowed);
320 buffer_put_int(&m, getpid());
321 if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) {
322 error("%s: client msg_send failed", __func__);
323 close(client_fd);
324 buffer_free(&m);
325 return 0;
326 }
327
328 if (!allowed) {
329 error("Refused control connection");
330 close(client_fd);
331 buffer_free(&m);
332 return 0;
333 }
334
335 buffer_clear(&m);
336 if (ssh_msg_recv(client_fd, &m) == -1) {
337 error("%s: client msg_recv failed", __func__);
338 close(client_fd);
339 buffer_free(&m);
340 return 0;
341 }
342 if ((ver = buffer_get_char(&m)) != SSHMUX_VER) {
343 error("%s: wrong client version %d", __func__, ver);
344 buffer_free(&m);
345 close(client_fd);
346 return 0;
347 }
348
349 cctx = xcalloc(1, sizeof(*cctx));
350 cctx->want_tty = (flags & SSHMUX_FLAG_TTY) != 0;
351 cctx->want_subsys = (flags & SSHMUX_FLAG_SUBSYS) != 0;
352 cctx->want_x_fwd = (flags & SSHMUX_FLAG_X11_FWD) != 0;
353 cctx->want_agent_fwd = (flags & SSHMUX_FLAG_AGENT_FWD) != 0;
354 cctx->term = buffer_get_string(&m, &len);
355 escape_char = buffer_get_int(&m);
356
357 cmd = buffer_get_string(&m, &len);
358 buffer_init(&cctx->cmd);
359 buffer_append(&cctx->cmd, cmd, strlen(cmd));
360
361 env_len = buffer_get_int(&m);
362 env_len = MIN(env_len, 4096);
363 debug3("%s: receiving %d env vars", __func__, env_len);
364 if (env_len != 0) {
365 cctx->env = xcalloc(env_len + 1, sizeof(*cctx->env));
366 for (i = 0; i < env_len; i++)
367 cctx->env[i] = buffer_get_string(&m, &len);
368 cctx->env[i] = NULL;
369 }
370
371 debug2("%s: accepted tty %d, subsys %d, cmd %s", __func__,
372 cctx->want_tty, cctx->want_subsys, cmd);
373 xfree(cmd);
374
375 /* Gather fds from client */
376 for(i = 0; i < 3; i++) {
377 if ((new_fd[i] = mm_receive_fd(client_fd)) == -1) {
378 error("%s: failed to receive fd %d from slave",
379 __func__, i);
380 for (j = 0; j < i; j++)
381 close(new_fd[j]);
382 for (j = 0; j < env_len; j++)
383 xfree(cctx->env[j]);
384 if (env_len > 0)
385 xfree(cctx->env);
386 xfree(cctx->term);
387 buffer_free(&cctx->cmd);
388 close(client_fd);
389 xfree(cctx);
390 return 0;
391 }
392 }
393
394 debug2("%s: got fds stdin %d, stdout %d, stderr %d", __func__,
395 new_fd[0], new_fd[1], new_fd[2]);
396
397 /* Try to pick up ttymodes from client before it goes raw */
398 if (cctx->want_tty && tcgetattr(new_fd[0], &cctx->tio) == -1)
399 error("%s: tcgetattr: %s", __func__, strerror(errno));
400
401 /* This roundtrip is just for synchronisation of ttymodes */
402 buffer_clear(&m);
403 if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) {
404 error("%s: client msg_send failed", __func__);
405 close(client_fd);
406 close(new_fd[0]);
407 close(new_fd[1]);
408 close(new_fd[2]);
409 buffer_free(&m);
410 xfree(cctx->term);
411 if (env_len != 0) {
412 for (i = 0; i < env_len; i++)
413 xfree(cctx->env[i]);
414 xfree(cctx->env);
415 }
416 return 0;
417 }
418 buffer_free(&m);
419
420 /* enable nonblocking unless tty */
421 if (!isatty(new_fd[0]))
422 set_nonblock(new_fd[0]);
423 if (!isatty(new_fd[1]))
424 set_nonblock(new_fd[1]);
425 if (!isatty(new_fd[2]))
426 set_nonblock(new_fd[2]);
427
428 set_nonblock(client_fd);
429
430 window = CHAN_SES_WINDOW_DEFAULT;
431 packetmax = CHAN_SES_PACKET_DEFAULT;
432 if (cctx->want_tty) {
433 window >>= 1;
434 packetmax >>= 1;
435 }
436
437 c = channel_new("session", SSH_CHANNEL_OPENING,
438 new_fd[0], new_fd[1], new_fd[2], window, packetmax,
439 CHAN_EXTENDED_WRITE, "client-session", /*nonblock*/0);
440
441 c->ctl_fd = client_fd;
442 if (cctx->want_tty && escape_char != 0xffffffff) {
443 channel_register_filter(c->self,
444 client_simple_escape_filter, NULL,
445 client_filter_cleanup,
446 client_new_escape_filter_ctx((int)escape_char));
447 }
448
449 debug3("%s: channel_new: %d", __func__, c->self);
450
451 channel_send_open(c->self);
452 channel_register_open_confirm(c->self, mux_session_confirm, cctx);
453 return 0;
454}
455
456/* ** Multiplexing client support */
457
458/* Exit signal handler */
459static void
460control_client_sighandler(int signo)
461{
462 muxclient_terminate = signo;
463}
464
465/*
466 * Relay signal handler - used to pass some signals from mux client to
467 * mux master.
468 */
469static void
470control_client_sigrelay(int signo)
471{
472 int save_errno = errno;
473
474 if (muxserver_pid > 1)
475 kill(muxserver_pid, signo);
476
477 errno = save_errno;
478}
479
480/* Check mux client environment variables before passing them to mux master. */
481static int
482env_permitted(char *env)
483{
484 int i, ret;
485 char name[1024], *cp;
486
487 if ((cp = strchr(env, '=')) == NULL || cp == env)
488 return (0);
489 ret = snprintf(name, sizeof(name), "%.*s", (int)(cp - env), env);
490 if (ret <= 0 || (size_t)ret >= sizeof(name))
491 fatal("env_permitted: name '%.100s...' too long", env);
492
493 for (i = 0; i < options.num_send_env; i++)
494 if (match_pattern(name, options.send_env[i]))
495 return (1);
496
497 return (0);
498}
499
500/* Multiplex client main loop. */
501void
502muxclient(const char *path)
503{
504 struct sockaddr_un addr;
505 int i, r, fd, sock, exitval[2], num_env, addr_len;
506 Buffer m;
507 char *term;
508 extern char **environ;
509 u_int allowed, flags;
510
511 if (muxclient_command == 0)
512 muxclient_command = SSHMUX_COMMAND_OPEN;
513
514 switch (options.control_master) {
515 case SSHCTL_MASTER_AUTO:
516 case SSHCTL_MASTER_AUTO_ASK:
517 debug("auto-mux: Trying existing master");
518 /* FALLTHROUGH */
519 case SSHCTL_MASTER_NO:
520 break;
521 default:
522 return;
523 }
524
525 memset(&addr, '\0', sizeof(addr));
526 addr.sun_family = AF_UNIX;
527 addr_len = offsetof(struct sockaddr_un, sun_path) +
528 strlen(path) + 1;
529
530 if (strlcpy(addr.sun_path, path,
531 sizeof(addr.sun_path)) >= sizeof(addr.sun_path))
532 fatal("ControlPath too long");
533
534 if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
535 fatal("%s socket(): %s", __func__, strerror(errno));
536
537 if (connect(sock, (struct sockaddr *)&addr, addr_len) == -1) {
538 if (muxclient_command != SSHMUX_COMMAND_OPEN) {
539 fatal("Control socket connect(%.100s): %s", path,
540 strerror(errno));
541 }
542 if (errno == ENOENT)
543 debug("Control socket \"%.100s\" does not exist", path);
544 else {
545 error("Control socket connect(%.100s): %s", path,
546 strerror(errno));
547 }
548 close(sock);
549 return;
550 }
551
552 if (stdin_null_flag) {
553 if ((fd = open(_PATH_DEVNULL, O_RDONLY)) == -1)
554 fatal("open(/dev/null): %s", strerror(errno));
555 if (dup2(fd, STDIN_FILENO) == -1)
556 fatal("dup2: %s", strerror(errno));
557 if (fd > STDERR_FILENO)
558 close(fd);
559 }
560
561 term = getenv("TERM");
562
563 flags = 0;
564 if (tty_flag)
565 flags |= SSHMUX_FLAG_TTY;
566 if (subsystem_flag)
567 flags |= SSHMUX_FLAG_SUBSYS;
568 if (options.forward_x11)
569 flags |= SSHMUX_FLAG_X11_FWD;
570 if (options.forward_agent)
571 flags |= SSHMUX_FLAG_AGENT_FWD;
572
573 signal(SIGPIPE, SIG_IGN);
574
575 buffer_init(&m);
576
577 /* Send our command to server */
578 buffer_put_int(&m, muxclient_command);
579 buffer_put_int(&m, flags);
580 if (ssh_msg_send(sock, SSHMUX_VER, &m) == -1) {
581 error("%s: msg_send", __func__);
582 muxerr:
583 close(sock);
584 buffer_free(&m);
585 if (muxclient_command != SSHMUX_COMMAND_OPEN)
586 cleanup_exit(255);
587 logit("Falling back to non-multiplexed connection");
588 xfree(options.control_path);
589 options.control_path = NULL;
590 options.control_master = SSHCTL_MASTER_NO;
591 return;
592 }
593 buffer_clear(&m);
594
595 /* Get authorisation status and PID of controlee */
596 if (ssh_msg_recv(sock, &m) == -1) {
597 error("%s: Did not receive reply from master", __func__);
598 goto muxerr;
599 }
600 if (buffer_get_char(&m) != SSHMUX_VER) {
601 error("%s: Master replied with wrong version", __func__);
602 goto muxerr;
603 }
604 if (buffer_get_int_ret(&allowed, &m) != 0) {
605 error("%s: bad server reply", __func__);
606 goto muxerr;
607 }
608 if (allowed != 1) {
609 error("Connection to master denied");
610 goto muxerr;
611 }
612 muxserver_pid = buffer_get_int(&m);
613
614 buffer_clear(&m);
615
616 switch (muxclient_command) {
617 case SSHMUX_COMMAND_ALIVE_CHECK:
618 fprintf(stderr, "Master running (pid=%d)\r\n",
619 muxserver_pid);
620 exit(0);
621 case SSHMUX_COMMAND_TERMINATE:
622 fprintf(stderr, "Exit request sent.\r\n");
623 exit(0);
624 case SSHMUX_COMMAND_OPEN:
625 buffer_put_cstring(&m, term ? term : "");
626 if (options.escape_char == SSH_ESCAPECHAR_NONE)
627 buffer_put_int(&m, 0xffffffff);
628 else
629 buffer_put_int(&m, options.escape_char);
630 buffer_append(&command, "\0", 1);
631 buffer_put_cstring(&m, buffer_ptr(&command));
632
633 if (options.num_send_env == 0 || environ == NULL) {
634 buffer_put_int(&m, 0);
635 } else {
636 /* Pass environment */
637 num_env = 0;
638 for (i = 0; environ[i] != NULL; i++) {
639 if (env_permitted(environ[i]))
640 num_env++; /* Count */
641 }
642 buffer_put_int(&m, num_env);
643 for (i = 0; environ[i] != NULL && num_env >= 0; i++) {
644 if (env_permitted(environ[i])) {
645 num_env--;
646 buffer_put_cstring(&m, environ[i]);
647 }
648 }
649 }
650 break;
651 default:
652 fatal("unrecognised muxclient_command %d", muxclient_command);
653 }
654
655 if (ssh_msg_send(sock, SSHMUX_VER, &m) == -1) {
656 error("%s: msg_send", __func__);
657 goto muxerr;
658 }
659
660 if (mm_send_fd(sock, STDIN_FILENO) == -1 ||
661 mm_send_fd(sock, STDOUT_FILENO) == -1 ||
662 mm_send_fd(sock, STDERR_FILENO) == -1) {
663 error("%s: send fds failed", __func__);
664 goto muxerr;
665 }
666
667 /*
668 * Mux errors are non-recoverable from this point as the master
669 * has ownership of the session now.
670 */
671
672 /* Wait for reply, so master has a chance to gather ttymodes */
673 buffer_clear(&m);
674 if (ssh_msg_recv(sock, &m) == -1)
675 fatal("%s: msg_recv", __func__);
676 if (buffer_get_char(&m) != SSHMUX_VER)
677 fatal("%s: wrong version", __func__);
678 buffer_free(&m);
679
680 signal(SIGHUP, control_client_sighandler);
681 signal(SIGINT, control_client_sighandler);
682 signal(SIGTERM, control_client_sighandler);
683 signal(SIGWINCH, control_client_sigrelay);
684
685 if (tty_flag)
686 enter_raw_mode();
687
688 /*
689 * Stick around until the controlee closes the client_fd.
690 * Before it does, it is expected to write this process' exit
691 * value (one int). This process must read the value and wait for
692 * the closure of the client_fd; if this one closes early, the
693 * multiplex master will terminate early too (possibly losing data).
694 */
695 exitval[0] = 0;
696 for (i = 0; !muxclient_terminate && i < (int)sizeof(exitval);) {
697 r = read(sock, (char *)exitval + i, sizeof(exitval) - i);
698 if (r == 0) {
699 debug2("Received EOF from master");
700 break;
701 }
702 if (r == -1) {
703 if (errno == EINTR)
704 continue;
705 fatal("%s: read %s", __func__, strerror(errno));
706 }
707 i += r;
708 }
709
710 close(sock);
711 leave_raw_mode();
712 if (i > (int)sizeof(int))
713 fatal("%s: master returned too much data (%d > %lu)",
714 __func__, i, (u_long)sizeof(int));
715 if (muxclient_terminate) {
716 debug2("Exiting on signal %d", muxclient_terminate);
717 exitval[0] = 255;
718 } else if (i < (int)sizeof(int)) {
719 debug2("Control master terminated unexpectedly");
720 exitval[0] = 255;
721 } else
722 debug2("Received exit status from master %d", exitval[0]);
723
724 if (tty_flag && options.log_level > SYSLOG_LEVEL_QUIET)
725 fprintf(stderr, "Shared connection to %s closed.\r\n", host);
726
727 exit(exitval[0]);
728}