diff options
author | Colin Watson <cjwatson@debian.org> | 2008-07-22 19:45:18 +0000 |
---|---|---|
committer | Colin Watson <cjwatson@debian.org> | 2008-07-22 19:45:18 +0000 |
commit | 137d76ba65883aa8143af1fcad83b57e7badef0c (patch) | |
tree | f426e804bb5248ceafedfab7bb78ae6e6752942c /ssh.c | |
parent | dac7d049dad31f5f84d421d4eb628a7e13f977d7 (diff) | |
parent | ef94e5613d37bcbf880f21ee6094e4b1c7683a4c (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 'ssh.c')
-rw-r--r-- | ssh.c | 541 |
1 files changed, 170 insertions, 371 deletions
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh.c,v 1.301 2007/08/07 07:32:53 djm Exp $ */ | 1 | /* $OpenBSD: ssh.c,v 1.318 2008/07/02 13:47:39 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -49,7 +49,6 @@ | |||
49 | #include <sys/resource.h> | 49 | #include <sys/resource.h> |
50 | #include <sys/ioctl.h> | 50 | #include <sys/ioctl.h> |
51 | #include <sys/socket.h> | 51 | #include <sys/socket.h> |
52 | #include <sys/un.h> | ||
53 | 52 | ||
54 | #include <ctype.h> | 53 | #include <ctype.h> |
55 | #include <errno.h> | 54 | #include <errno.h> |
@@ -72,6 +71,8 @@ | |||
72 | 71 | ||
73 | #include <openssl/evp.h> | 72 | #include <openssl/evp.h> |
74 | #include <openssl/err.h> | 73 | #include <openssl/err.h> |
74 | #include "openbsd-compat/openssl-compat.h" | ||
75 | #include "openbsd-compat/sys-queue.h" | ||
75 | 76 | ||
76 | #include "xmalloc.h" | 77 | #include "xmalloc.h" |
77 | #include "ssh.h" | 78 | #include "ssh.h" |
@@ -97,7 +98,6 @@ | |||
97 | #include "sshpty.h" | 98 | #include "sshpty.h" |
98 | #include "match.h" | 99 | #include "match.h" |
99 | #include "msg.h" | 100 | #include "msg.h" |
100 | #include "monitor_fdpass.h" | ||
101 | #include "uidswap.h" | 101 | #include "uidswap.h" |
102 | #include "version.h" | 102 | #include "version.h" |
103 | 103 | ||
@@ -107,7 +107,7 @@ | |||
107 | 107 | ||
108 | extern char *__progname; | 108 | extern char *__progname; |
109 | 109 | ||
110 | /* Flag indicating whether debug mode is on. This can be set on the command line. */ | 110 | /* Flag indicating whether debug mode is on. May be set on the command line. */ |
111 | int debug_flag = 0; | 111 | int debug_flag = 0; |
112 | 112 | ||
113 | /* Flag indicating whether a tty should be allocated */ | 113 | /* Flag indicating whether a tty should be allocated */ |
@@ -164,20 +164,14 @@ Buffer command; | |||
164 | int subsystem_flag = 0; | 164 | int subsystem_flag = 0; |
165 | 165 | ||
166 | /* # of replies received for global requests */ | 166 | /* # of replies received for global requests */ |
167 | static int client_global_request_id = 0; | 167 | static int remote_forward_confirms_received = 0; |
168 | 168 | ||
169 | /* pid of proxycommand child process */ | 169 | /* pid of proxycommand child process */ |
170 | pid_t proxy_command_pid = 0; | 170 | pid_t proxy_command_pid = 0; |
171 | 171 | ||
172 | /* fd to control socket */ | 172 | /* mux.c */ |
173 | int control_fd = -1; | 173 | extern int muxserver_sock; |
174 | 174 | extern u_int muxclient_command; | |
175 | /* Multiplexing control command */ | ||
176 | static u_int mux_command = 0; | ||
177 | |||
178 | /* Only used in control client mode */ | ||
179 | volatile sig_atomic_t control_client_terminate = 0; | ||
180 | u_int control_server_pid = 0; | ||
181 | 175 | ||
182 | /* Prints a help message to the user. This function never returns. */ | 176 | /* Prints a help message to the user. This function never returns. */ |
183 | 177 | ||
@@ -198,7 +192,10 @@ usage(void) | |||
198 | static int ssh_session(void); | 192 | static int ssh_session(void); |
199 | static int ssh_session2(void); | 193 | static int ssh_session2(void); |
200 | static void load_public_identity_files(void); | 194 | static void load_public_identity_files(void); |
201 | static void control_client(const char *path); | 195 | |
196 | /* from muxclient.c */ | ||
197 | void muxclient(const char *); | ||
198 | void muxserver_listen(void); | ||
202 | 199 | ||
203 | /* | 200 | /* |
204 | * Main program for the ssh client. | 201 | * Main program for the ssh client. |
@@ -210,7 +207,7 @@ main(int ac, char **av) | |||
210 | char *p, *cp, *line, buf[256]; | 207 | char *p, *cp, *line, buf[256]; |
211 | struct stat st; | 208 | struct stat st; |
212 | struct passwd *pw; | 209 | struct passwd *pw; |
213 | int dummy; | 210 | int dummy, timeout_ms; |
214 | extern int optind, optreset; | 211 | extern int optind, optreset; |
215 | extern char *optarg; | 212 | extern char *optarg; |
216 | struct servent *sp; | 213 | struct servent *sp; |
@@ -264,15 +261,18 @@ main(int ac, char **av) | |||
264 | */ | 261 | */ |
265 | umask(022); | 262 | umask(022); |
266 | 263 | ||
267 | /* Initialize option structure to indicate that no values have been set. */ | 264 | /* |
265 | * Initialize option structure to indicate that no values have been | ||
266 | * set. | ||
267 | */ | ||
268 | initialize_options(&options); | 268 | initialize_options(&options); |
269 | 269 | ||
270 | /* Parse command-line arguments. */ | 270 | /* Parse command-line arguments. */ |
271 | host = NULL; | 271 | host = NULL; |
272 | 272 | ||
273 | again: | 273 | again: |
274 | while ((opt = getopt(ac, av, | 274 | while ((opt = getopt(ac, av, "1246ab:c:e:fgi:kl:m:no:p:qstvx" |
275 | "1246ab:c:e:fgi:kl:m:no:p:qstvxACD:F:I:KL:MNO:PR:S:TVw:XY")) != -1) { | 275 | "ACD:F:I:KL:MNO:PR:S:TVw:XY")) != -1) { |
276 | switch (opt) { | 276 | switch (opt) { |
277 | case '1': | 277 | case '1': |
278 | options.protocol = SSH_PROTO_1; | 278 | options.protocol = SSH_PROTO_1; |
@@ -308,9 +308,9 @@ main(int ac, char **av) | |||
308 | break; | 308 | break; |
309 | case 'O': | 309 | case 'O': |
310 | if (strcmp(optarg, "check") == 0) | 310 | if (strcmp(optarg, "check") == 0) |
311 | mux_command = SSHMUX_COMMAND_ALIVE_CHECK; | 311 | muxclient_command = SSHMUX_COMMAND_ALIVE_CHECK; |
312 | else if (strcmp(optarg, "exit") == 0) | 312 | else if (strcmp(optarg, "exit") == 0) |
313 | mux_command = SSHMUX_COMMAND_TERMINATE; | 313 | muxclient_command = SSHMUX_COMMAND_TERMINATE; |
314 | else | 314 | else |
315 | fatal("Invalid multiplex command."); | 315 | fatal("Invalid multiplex command."); |
316 | break; | 316 | break; |
@@ -377,7 +377,8 @@ main(int ac, char **av) | |||
377 | options.tun_open = SSH_TUNMODE_DEFAULT; | 377 | options.tun_open = SSH_TUNMODE_DEFAULT; |
378 | options.tun_local = a2tun(optarg, &options.tun_remote); | 378 | options.tun_local = a2tun(optarg, &options.tun_remote); |
379 | if (options.tun_local == SSH_TUNID_ERR) { | 379 | if (options.tun_local == SSH_TUNID_ERR) { |
380 | fprintf(stderr, "Bad tun device '%s'\n", optarg); | 380 | fprintf(stderr, |
381 | "Bad tun device '%s'\n", optarg); | ||
381 | exit(255); | 382 | exit(255); |
382 | } | 383 | } |
383 | break; | 384 | break; |
@@ -485,7 +486,8 @@ main(int ac, char **av) | |||
485 | } | 486 | } |
486 | if (cp != NULL) { | 487 | if (cp != NULL) { |
487 | fwd.listen_port = a2port(cp); | 488 | fwd.listen_port = a2port(cp); |
488 | fwd.listen_host = cleanhostname(fwd.listen_host); | 489 | fwd.listen_host = |
490 | cleanhostname(fwd.listen_host); | ||
489 | } else { | 491 | } else { |
490 | fwd.listen_port = a2port(fwd.listen_host); | 492 | fwd.listen_port = a2port(fwd.listen_host); |
491 | fwd.listen_host = NULL; | 493 | fwd.listen_host = NULL; |
@@ -591,8 +593,10 @@ main(int ac, char **av) | |||
591 | } | 593 | } |
592 | 594 | ||
593 | /* Cannot fork to background if no command. */ | 595 | /* Cannot fork to background if no command. */ |
594 | if (fork_after_authentication_flag && buffer_len(&command) == 0 && !no_shell_flag) | 596 | if (fork_after_authentication_flag && buffer_len(&command) == 0 && |
595 | fatal("Cannot fork into background without a command to execute."); | 597 | !no_shell_flag) |
598 | fatal("Cannot fork into background without a command " | ||
599 | "to execute."); | ||
596 | 600 | ||
597 | /* Allocate a tty by default if no command specified. */ | 601 | /* Allocate a tty by default if no command specified. */ |
598 | if (buffer_len(&command) == 0) | 602 | if (buffer_len(&command) == 0) |
@@ -604,7 +608,8 @@ main(int ac, char **av) | |||
604 | /* Do not allocate a tty if stdin is not a tty. */ | 608 | /* Do not allocate a tty if stdin is not a tty. */ |
605 | if ((!isatty(fileno(stdin)) || stdin_null_flag) && !force_tty_flag) { | 609 | if ((!isatty(fileno(stdin)) || stdin_null_flag) && !force_tty_flag) { |
606 | if (tty_flag && options.log_level > SYSLOG_LEVEL_QUIET) | 610 | if (tty_flag && options.log_level > SYSLOG_LEVEL_QUIET) |
607 | logit("Pseudo-terminal will not be allocated because stdin is not a terminal."); | 611 | logit("Pseudo-terminal will not be allocated because " |
612 | "stdin is not a terminal."); | ||
608 | tty_flag = 0; | 613 | tty_flag = 0; |
609 | } | 614 | } |
610 | 615 | ||
@@ -612,7 +617,8 @@ main(int ac, char **av) | |||
612 | * Initialize "log" output. Since we are the client all output | 617 | * Initialize "log" output. Since we are the client all output |
613 | * actually goes to stderr. | 618 | * actually goes to stderr. |
614 | */ | 619 | */ |
615 | log_init(av[0], options.log_level == -1 ? SYSLOG_LEVEL_INFO : options.log_level, | 620 | log_init(av[0], |
621 | options.log_level == -1 ? SYSLOG_LEVEL_INFO : options.log_level, | ||
616 | SYSLOG_FACILITY_USER, 1); | 622 | SYSLOG_FACILITY_USER, 1); |
617 | 623 | ||
618 | /* | 624 | /* |
@@ -646,6 +652,28 @@ main(int ac, char **av) | |||
646 | if (options.user == NULL) | 652 | if (options.user == NULL) |
647 | options.user = xstrdup(pw->pw_name); | 653 | options.user = xstrdup(pw->pw_name); |
648 | 654 | ||
655 | /* Get default port if port has not been set. */ | ||
656 | if (options.port == 0) { | ||
657 | sp = getservbyname(SSH_SERVICE_NAME, "tcp"); | ||
658 | options.port = sp ? ntohs(sp->s_port) : SSH_DEFAULT_PORT; | ||
659 | } | ||
660 | |||
661 | if (options.local_command != NULL) { | ||
662 | char thishost[NI_MAXHOST]; | ||
663 | |||
664 | if (gethostname(thishost, sizeof(thishost)) == -1) | ||
665 | fatal("gethostname: %s", strerror(errno)); | ||
666 | snprintf(buf, sizeof(buf), "%d", options.port); | ||
667 | debug3("expanding LocalCommand: %s", options.local_command); | ||
668 | cp = options.local_command; | ||
669 | options.local_command = percent_expand(cp, "d", pw->pw_dir, | ||
670 | "h", options.hostname? options.hostname : host, | ||
671 | "l", thishost, "n", host, "r", options.user, "p", buf, | ||
672 | "u", pw->pw_name, (char *)NULL); | ||
673 | debug3("expanded LocalCommand: %s", options.local_command); | ||
674 | xfree(cp); | ||
675 | } | ||
676 | |||
649 | if (options.hostname != NULL) | 677 | if (options.hostname != NULL) |
650 | host = options.hostname; | 678 | host = options.hostname; |
651 | 679 | ||
@@ -656,18 +684,16 @@ main(int ac, char **av) | |||
656 | *p = (char)tolower(*p); | 684 | *p = (char)tolower(*p); |
657 | } | 685 | } |
658 | 686 | ||
659 | /* Get default port if port has not been set. */ | ||
660 | if (options.port == 0) { | ||
661 | sp = getservbyname(SSH_SERVICE_NAME, "tcp"); | ||
662 | options.port = sp ? ntohs(sp->s_port) : SSH_DEFAULT_PORT; | ||
663 | } | ||
664 | |||
665 | if (options.proxy_command != NULL && | 687 | if (options.proxy_command != NULL && |
666 | strcmp(options.proxy_command, "none") == 0) | 688 | strcmp(options.proxy_command, "none") == 0) { |
689 | xfree(options.proxy_command); | ||
667 | options.proxy_command = NULL; | 690 | options.proxy_command = NULL; |
691 | } | ||
668 | if (options.control_path != NULL && | 692 | if (options.control_path != NULL && |
669 | strcmp(options.control_path, "none") == 0) | 693 | strcmp(options.control_path, "none") == 0) { |
694 | xfree(options.control_path); | ||
670 | options.control_path = NULL; | 695 | options.control_path = NULL; |
696 | } | ||
671 | 697 | ||
672 | if (options.control_path != NULL) { | 698 | if (options.control_path != NULL) { |
673 | char thishost[NI_MAXHOST]; | 699 | char thishost[NI_MAXHOST]; |
@@ -677,18 +703,22 @@ main(int ac, char **av) | |||
677 | snprintf(buf, sizeof(buf), "%d", options.port); | 703 | snprintf(buf, sizeof(buf), "%d", options.port); |
678 | cp = tilde_expand_filename(options.control_path, | 704 | cp = tilde_expand_filename(options.control_path, |
679 | original_real_uid); | 705 | original_real_uid); |
706 | xfree(options.control_path); | ||
680 | options.control_path = percent_expand(cp, "p", buf, "h", host, | 707 | options.control_path = percent_expand(cp, "p", buf, "h", host, |
681 | "r", options.user, "l", thishost, (char *)NULL); | 708 | "r", options.user, "l", thishost, (char *)NULL); |
682 | xfree(cp); | 709 | xfree(cp); |
683 | } | 710 | } |
684 | if (mux_command != 0 && options.control_path == NULL) | 711 | if (muxclient_command != 0 && options.control_path == NULL) |
685 | fatal("No ControlPath specified for \"-O\" command"); | 712 | fatal("No ControlPath specified for \"-O\" command"); |
686 | if (options.control_path != NULL) | 713 | if (options.control_path != NULL) |
687 | control_client(options.control_path); | 714 | muxclient(options.control_path); |
715 | |||
716 | timeout_ms = options.connection_timeout * 1000; | ||
688 | 717 | ||
689 | /* Open a connection to the remote host. */ | 718 | /* Open a connection to the remote host. */ |
690 | if (ssh_connect(host, &hostaddr, options.port, | 719 | if (ssh_connect(host, &hostaddr, options.port, |
691 | options.address_family, options.connection_attempts, | 720 | options.address_family, options.connection_attempts, &timeout_ms, |
721 | options.tcp_keep_alive, | ||
692 | #ifdef HAVE_CYGWIN | 722 | #ifdef HAVE_CYGWIN |
693 | options.use_privileged_port, | 723 | options.use_privileged_port, |
694 | #else | 724 | #else |
@@ -697,6 +727,9 @@ main(int ac, char **av) | |||
697 | options.proxy_command) != 0) | 727 | options.proxy_command) != 0) |
698 | exit(255); | 728 | exit(255); |
699 | 729 | ||
730 | if (timeout_ms > 0) | ||
731 | debug3("timeout: %d ms remain after connect", timeout_ms); | ||
732 | |||
700 | /* | 733 | /* |
701 | * If we successfully made the connection, load the host private key | 734 | * If we successfully made the connection, load the host private key |
702 | * in case we will need it later for combined rsa-rhosts | 735 | * in case we will need it later for combined rsa-rhosts |
@@ -750,7 +783,8 @@ main(int ac, char **av) | |||
750 | * Now that we are back to our own permissions, create ~/.ssh | 783 | * Now that we are back to our own permissions, create ~/.ssh |
751 | * directory if it doesn't already exist. | 784 | * directory if it doesn't already exist. |
752 | */ | 785 | */ |
753 | snprintf(buf, sizeof buf, "%.100s%s%.100s", pw->pw_dir, strcmp(pw->pw_dir, "/") ? "/" : "", _PATH_SSH_USER_DIR); | 786 | snprintf(buf, sizeof buf, "%.100s%s%.100s", pw->pw_dir, |
787 | strcmp(pw->pw_dir, "/") ? "/" : "", _PATH_SSH_USER_DIR); | ||
754 | if (stat(buf, &st) < 0) | 788 | if (stat(buf, &st) < 0) |
755 | if (mkdir(buf, 0700) < 0) | 789 | if (mkdir(buf, 0700) < 0) |
756 | error("Could not create directory '%.200s'.", buf); | 790 | error("Could not create directory '%.200s'.", buf); |
@@ -771,8 +805,9 @@ main(int ac, char **av) | |||
771 | 805 | ||
772 | signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE early */ | 806 | signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE early */ |
773 | 807 | ||
774 | /* Log into the remote system. This never returns if the login fails. */ | 808 | /* Log into the remote system. Never returns if the login fails. */ |
775 | ssh_login(&sensitive_data, host, (struct sockaddr *)&hostaddr, pw); | 809 | ssh_login(&sensitive_data, host, (struct sockaddr *)&hostaddr, |
810 | pw, timeout_ms); | ||
776 | 811 | ||
777 | /* We no longer need the private host keys. Clear them now. */ | 812 | /* We no longer need the private host keys. Clear them now. */ |
778 | if (sensitive_data.nkeys != 0) { | 813 | if (sensitive_data.nkeys != 0) { |
@@ -800,7 +835,7 @@ main(int ac, char **av) | |||
800 | exit_status = compat20 ? ssh_session2() : ssh_session(); | 835 | exit_status = compat20 ? ssh_session2() : ssh_session(); |
801 | packet_close(); | 836 | packet_close(); |
802 | 837 | ||
803 | if (options.control_path != NULL && control_fd != -1) | 838 | if (options.control_path != NULL && muxserver_sock != -1) |
804 | unlink(options.control_path); | 839 | unlink(options.control_path); |
805 | 840 | ||
806 | /* | 841 | /* |
@@ -813,6 +848,34 @@ main(int ac, char **av) | |||
813 | return exit_status; | 848 | return exit_status; |
814 | } | 849 | } |
815 | 850 | ||
851 | /* Callback for remote forward global requests */ | ||
852 | static void | ||
853 | ssh_confirm_remote_forward(int type, u_int32_t seq, void *ctxt) | ||
854 | { | ||
855 | Forward *rfwd = (Forward *)ctxt; | ||
856 | |||
857 | debug("remote forward %s for: listen %d, connect %s:%d", | ||
858 | type == SSH2_MSG_REQUEST_SUCCESS ? "success" : "failure", | ||
859 | rfwd->listen_port, rfwd->connect_host, rfwd->connect_port); | ||
860 | if (type == SSH2_MSG_REQUEST_FAILURE) { | ||
861 | if (options.exit_on_forward_failure) | ||
862 | fatal("Error: remote port forwarding failed for " | ||
863 | "listen port %d", rfwd->listen_port); | ||
864 | else | ||
865 | logit("Warning: remote port forwarding failed for " | ||
866 | "listen port %d", rfwd->listen_port); | ||
867 | } | ||
868 | if (++remote_forward_confirms_received == options.num_remote_forwards) { | ||
869 | debug("All remote forwarding requests processed"); | ||
870 | if (fork_after_authentication_flag) { | ||
871 | fork_after_authentication_flag = 0; | ||
872 | if (daemon(1, 1) < 0) | ||
873 | fatal("daemon() failed: %.200s", | ||
874 | strerror(errno)); | ||
875 | } | ||
876 | } | ||
877 | } | ||
878 | |||
816 | static void | 879 | static void |
817 | ssh_init_forwarding(void) | 880 | ssh_init_forwarding(void) |
818 | { | 881 | { |
@@ -861,6 +924,8 @@ ssh_init_forwarding(void) | |||
861 | logit("Warning: Could not request remote " | 924 | logit("Warning: Could not request remote " |
862 | "forwarding."); | 925 | "forwarding."); |
863 | } | 926 | } |
927 | client_register_global_confirm(ssh_confirm_remote_forward, | ||
928 | &options.remote_forwards[i]); | ||
864 | } | 929 | } |
865 | 930 | ||
866 | /* Initiate tunnel forwarding. */ | 931 | /* Initiate tunnel forwarding. */ |
@@ -897,10 +962,13 @@ ssh_session(void) | |||
897 | 962 | ||
898 | /* Enable compression if requested. */ | 963 | /* Enable compression if requested. */ |
899 | if (options.compression) { | 964 | if (options.compression) { |
900 | debug("Requesting compression at level %d.", options.compression_level); | 965 | debug("Requesting compression at level %d.", |
966 | options.compression_level); | ||
901 | 967 | ||
902 | if (options.compression_level < 1 || options.compression_level > 9) | 968 | if (options.compression_level < 1 || |
903 | fatal("Compression level must be from 1 (fast) to 9 (slow, best)."); | 969 | options.compression_level > 9) |
970 | fatal("Compression level must be from 1 (fast) to " | ||
971 | "9 (slow, best)."); | ||
904 | 972 | ||
905 | /* Send the request. */ | 973 | /* Send the request. */ |
906 | packet_start(SSH_CMSG_REQUEST_COMPRESSION); | 974 | packet_start(SSH_CMSG_REQUEST_COMPRESSION); |
@@ -913,7 +981,8 @@ ssh_session(void) | |||
913 | else if (type == SSH_SMSG_FAILURE) | 981 | else if (type == SSH_SMSG_FAILURE) |
914 | logit("Warning: Remote host refused compression."); | 982 | logit("Warning: Remote host refused compression."); |
915 | else | 983 | else |
916 | packet_disconnect("Protocol error waiting for compression response."); | 984 | packet_disconnect("Protocol error waiting for " |
985 | "compression response."); | ||
917 | } | 986 | } |
918 | /* Allocate a pseudo tty if appropriate. */ | 987 | /* Allocate a pseudo tty if appropriate. */ |
919 | if (tty_flag) { | 988 | if (tty_flag) { |
@@ -950,9 +1019,11 @@ ssh_session(void) | |||
950 | interactive = 1; | 1019 | interactive = 1; |
951 | have_tty = 1; | 1020 | have_tty = 1; |
952 | } else if (type == SSH_SMSG_FAILURE) | 1021 | } else if (type == SSH_SMSG_FAILURE) |
953 | logit("Warning: Remote host failed or refused to allocate a pseudo tty."); | 1022 | logit("Warning: Remote host failed or refused to " |
1023 | "allocate a pseudo tty."); | ||
954 | else | 1024 | else |
955 | packet_disconnect("Protocol error waiting for pty request response."); | 1025 | packet_disconnect("Protocol error waiting for pty " |
1026 | "request response."); | ||
956 | } | 1027 | } |
957 | /* Request X11 forwarding if enabled and DISPLAY is set. */ | 1028 | /* Request X11 forwarding if enabled and DISPLAY is set. */ |
958 | display = getenv("DISPLAY"); | 1029 | display = getenv("DISPLAY"); |
@@ -962,7 +1033,8 @@ ssh_session(void) | |||
962 | client_x11_get_proto(display, options.xauth_location, | 1033 | client_x11_get_proto(display, options.xauth_location, |
963 | options.forward_x11_trusted, &proto, &data); | 1034 | options.forward_x11_trusted, &proto, &data); |
964 | /* Request forwarding with authentication spoofing. */ | 1035 | /* Request forwarding with authentication spoofing. */ |
965 | debug("Requesting X11 forwarding with authentication spoofing."); | 1036 | debug("Requesting X11 forwarding with authentication " |
1037 | "spoofing."); | ||
966 | x11_request_forwarding_with_spoofing(0, display, proto, data); | 1038 | x11_request_forwarding_with_spoofing(0, display, proto, data); |
967 | 1039 | ||
968 | /* Read response from the server. */ | 1040 | /* Read response from the server. */ |
@@ -972,7 +1044,8 @@ ssh_session(void) | |||
972 | } else if (type == SSH_SMSG_FAILURE) { | 1044 | } else if (type == SSH_SMSG_FAILURE) { |
973 | logit("Warning: Remote host denied X11 forwarding."); | 1045 | logit("Warning: Remote host denied X11 forwarding."); |
974 | } else { | 1046 | } else { |
975 | packet_disconnect("Protocol error waiting for X11 forwarding"); | 1047 | packet_disconnect("Protocol error waiting for X11 " |
1048 | "forwarding"); | ||
976 | } | 1049 | } |
977 | } | 1050 | } |
978 | /* Tell the packet module whether this is an interactive session. */ | 1051 | /* Tell the packet module whether this is an interactive session. */ |
@@ -995,10 +1068,22 @@ ssh_session(void) | |||
995 | /* Initiate port forwardings. */ | 1068 | /* Initiate port forwardings. */ |
996 | ssh_init_forwarding(); | 1069 | ssh_init_forwarding(); |
997 | 1070 | ||
998 | /* If requested, let ssh continue in the background. */ | 1071 | /* Execute a local command */ |
999 | if (fork_after_authentication_flag) | 1072 | if (options.local_command != NULL && |
1073 | options.permit_local_command) | ||
1074 | ssh_local_cmd(options.local_command); | ||
1075 | |||
1076 | /* | ||
1077 | * If requested and we are not interested in replies to remote | ||
1078 | * forwarding requests, then let ssh continue in the background. | ||
1079 | */ | ||
1080 | if (fork_after_authentication_flag && | ||
1081 | (!options.exit_on_forward_failure || | ||
1082 | options.num_remote_forwards == 0)) { | ||
1083 | fork_after_authentication_flag = 0; | ||
1000 | if (daemon(1, 1) < 0) | 1084 | if (daemon(1, 1) < 0) |
1001 | fatal("daemon() failed: %.200s", strerror(errno)); | 1085 | fatal("daemon() failed: %.200s", strerror(errno)); |
1086 | } | ||
1002 | 1087 | ||
1003 | /* | 1088 | /* |
1004 | * If a command was specified on the command line, execute the | 1089 | * If a command was specified on the command line, execute the |
@@ -1008,7 +1093,8 @@ ssh_session(void) | |||
1008 | int len = buffer_len(&command); | 1093 | int len = buffer_len(&command); |
1009 | if (len > 900) | 1094 | if (len > 900) |
1010 | len = 900; | 1095 | len = 900; |
1011 | debug("Sending command: %.*s", len, (u_char *)buffer_ptr(&command)); | 1096 | debug("Sending command: %.*s", len, |
1097 | (u_char *)buffer_ptr(&command)); | ||
1012 | packet_start(SSH_CMSG_EXEC_CMD); | 1098 | packet_start(SSH_CMSG_EXEC_CMD); |
1013 | packet_put_string(buffer_ptr(&command), buffer_len(&command)); | 1099 | packet_put_string(buffer_ptr(&command), buffer_len(&command)); |
1014 | packet_send(); | 1100 | packet_send(); |
@@ -1025,88 +1111,6 @@ ssh_session(void) | |||
1025 | options.escape_char : SSH_ESCAPECHAR_NONE, 0); | 1111 | options.escape_char : SSH_ESCAPECHAR_NONE, 0); |
1026 | } | 1112 | } |
1027 | 1113 | ||
1028 | static void | ||
1029 | ssh_subsystem_reply(int type, u_int32_t seq, void *ctxt) | ||
1030 | { | ||
1031 | int id, len; | ||
1032 | |||
1033 | id = packet_get_int(); | ||
1034 | len = buffer_len(&command); | ||
1035 | if (len > 900) | ||
1036 | len = 900; | ||
1037 | packet_check_eom(); | ||
1038 | if (type == SSH2_MSG_CHANNEL_FAILURE) | ||
1039 | fatal("Request for subsystem '%.*s' failed on channel %d", | ||
1040 | len, (u_char *)buffer_ptr(&command), id); | ||
1041 | } | ||
1042 | |||
1043 | void | ||
1044 | client_global_request_reply_fwd(int type, u_int32_t seq, void *ctxt) | ||
1045 | { | ||
1046 | int i; | ||
1047 | |||
1048 | i = client_global_request_id++; | ||
1049 | if (i >= options.num_remote_forwards) | ||
1050 | return; | ||
1051 | debug("remote forward %s for: listen %d, connect %s:%d", | ||
1052 | type == SSH2_MSG_REQUEST_SUCCESS ? "success" : "failure", | ||
1053 | options.remote_forwards[i].listen_port, | ||
1054 | options.remote_forwards[i].connect_host, | ||
1055 | options.remote_forwards[i].connect_port); | ||
1056 | if (type == SSH2_MSG_REQUEST_FAILURE) { | ||
1057 | if (options.exit_on_forward_failure) | ||
1058 | fatal("Error: remote port forwarding failed for " | ||
1059 | "listen port %d", | ||
1060 | options.remote_forwards[i].listen_port); | ||
1061 | else | ||
1062 | logit("Warning: remote port forwarding failed for " | ||
1063 | "listen port %d", | ||
1064 | options.remote_forwards[i].listen_port); | ||
1065 | } | ||
1066 | } | ||
1067 | |||
1068 | static void | ||
1069 | ssh_control_listener(void) | ||
1070 | { | ||
1071 | struct sockaddr_un addr; | ||
1072 | mode_t old_umask; | ||
1073 | int addr_len; | ||
1074 | |||
1075 | if (options.control_path == NULL || | ||
1076 | options.control_master == SSHCTL_MASTER_NO) | ||
1077 | return; | ||
1078 | |||
1079 | debug("setting up multiplex master socket"); | ||
1080 | |||
1081 | memset(&addr, '\0', sizeof(addr)); | ||
1082 | addr.sun_family = AF_UNIX; | ||
1083 | addr_len = offsetof(struct sockaddr_un, sun_path) + | ||
1084 | strlen(options.control_path) + 1; | ||
1085 | |||
1086 | if (strlcpy(addr.sun_path, options.control_path, | ||
1087 | sizeof(addr.sun_path)) >= sizeof(addr.sun_path)) | ||
1088 | fatal("ControlPath too long"); | ||
1089 | |||
1090 | if ((control_fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) | ||
1091 | fatal("%s socket(): %s", __func__, strerror(errno)); | ||
1092 | |||
1093 | old_umask = umask(0177); | ||
1094 | if (bind(control_fd, (struct sockaddr *)&addr, addr_len) == -1) { | ||
1095 | control_fd = -1; | ||
1096 | if (errno == EINVAL || errno == EADDRINUSE) | ||
1097 | fatal("ControlSocket %s already exists", | ||
1098 | options.control_path); | ||
1099 | else | ||
1100 | fatal("%s bind(): %s", __func__, strerror(errno)); | ||
1101 | } | ||
1102 | umask(old_umask); | ||
1103 | |||
1104 | if (listen(control_fd, 64) == -1) | ||
1105 | fatal("%s listen(): %s", __func__, strerror(errno)); | ||
1106 | |||
1107 | set_nonblock(control_fd); | ||
1108 | } | ||
1109 | |||
1110 | /* request pty/x11/agent/tcpfwd/shell for channel */ | 1114 | /* request pty/x11/agent/tcpfwd/shell for channel */ |
1111 | static void | 1115 | static void |
1112 | ssh_session2_setup(int id, void *arg) | 1116 | ssh_session2_setup(int id, void *arg) |
@@ -1122,7 +1126,8 @@ ssh_session2_setup(int id, void *arg) | |||
1122 | client_x11_get_proto(display, options.xauth_location, | 1126 | client_x11_get_proto(display, options.xauth_location, |
1123 | options.forward_x11_trusted, &proto, &data); | 1127 | options.forward_x11_trusted, &proto, &data); |
1124 | /* Request forwarding with authentication spoofing. */ | 1128 | /* Request forwarding with authentication spoofing. */ |
1125 | debug("Requesting X11 forwarding with authentication spoofing."); | 1129 | debug("Requesting X11 forwarding with authentication " |
1130 | "spoofing."); | ||
1126 | x11_request_forwarding_with_spoofing(id, display, proto, data); | 1131 | x11_request_forwarding_with_spoofing(id, display, proto, data); |
1127 | interactive = 1; | 1132 | interactive = 1; |
1128 | /* XXX wait for reply */ | 1133 | /* XXX wait for reply */ |
@@ -1136,7 +1141,7 @@ ssh_session2_setup(int id, void *arg) | |||
1136 | } | 1141 | } |
1137 | 1142 | ||
1138 | client_session2_setup(id, tty_flag, subsystem_flag, getenv("TERM"), | 1143 | client_session2_setup(id, tty_flag, subsystem_flag, getenv("TERM"), |
1139 | NULL, fileno(stdin), &command, environ, &ssh_subsystem_reply); | 1144 | NULL, fileno(stdin), &command, environ); |
1140 | 1145 | ||
1141 | packet_set_interactive(interactive); | 1146 | packet_set_interactive(interactive); |
1142 | } | 1147 | } |
@@ -1182,7 +1187,8 @@ ssh_session2_open(void) | |||
1182 | 1187 | ||
1183 | channel_send_open(c->self); | 1188 | channel_send_open(c->self); |
1184 | if (!no_shell_flag) | 1189 | if (!no_shell_flag) |
1185 | channel_register_confirm(c->self, ssh_session2_setup, NULL); | 1190 | channel_register_open_confirm(c->self, |
1191 | ssh_session2_setup, NULL); | ||
1186 | 1192 | ||
1187 | return c->self; | 1193 | return c->self; |
1188 | } | 1194 | } |
@@ -1198,18 +1204,29 @@ ssh_session2(void) | |||
1198 | if (!no_shell_flag || (datafellows & SSH_BUG_DUMMYCHAN)) | 1204 | if (!no_shell_flag || (datafellows & SSH_BUG_DUMMYCHAN)) |
1199 | id = ssh_session2_open(); | 1205 | id = ssh_session2_open(); |
1200 | 1206 | ||
1207 | /* If we don't expect to open a new session, then disallow it */ | ||
1208 | if (options.control_master == SSHCTL_MASTER_NO) { | ||
1209 | debug("Requesting no-more-sessions@openssh.com"); | ||
1210 | packet_start(SSH2_MSG_GLOBAL_REQUEST); | ||
1211 | packet_put_cstring("no-more-sessions@openssh.com"); | ||
1212 | packet_put_char(0); | ||
1213 | packet_send(); | ||
1214 | } | ||
1215 | |||
1201 | /* Execute a local command */ | 1216 | /* Execute a local command */ |
1202 | if (options.local_command != NULL && | 1217 | if (options.local_command != NULL && |
1203 | options.permit_local_command) | 1218 | options.permit_local_command) |
1204 | ssh_local_cmd(options.local_command); | 1219 | ssh_local_cmd(options.local_command); |
1205 | 1220 | ||
1206 | /* Start listening for multiplex clients */ | 1221 | /* Start listening for multiplex clients */ |
1207 | ssh_control_listener(); | 1222 | muxserver_listen(); |
1208 | 1223 | ||
1209 | /* If requested, let ssh continue in the background. */ | 1224 | /* If requested, let ssh continue in the background. */ |
1210 | if (fork_after_authentication_flag) | 1225 | if (fork_after_authentication_flag) { |
1226 | fork_after_authentication_flag = 0; | ||
1211 | if (daemon(1, 1) < 0) | 1227 | if (daemon(1, 1) < 0) |
1212 | fatal("daemon() failed: %.200s", strerror(errno)); | 1228 | fatal("daemon() failed: %.200s", strerror(errno)); |
1229 | } | ||
1213 | 1230 | ||
1214 | return client_loop(tty_flag, tty_flag ? | 1231 | return client_loop(tty_flag, tty_flag ? |
1215 | options.escape_char : SSH_ESCAPECHAR_NONE, id); | 1232 | options.escape_char : SSH_ESCAPECHAR_NONE, id); |
@@ -1219,6 +1236,7 @@ static void | |||
1219 | load_public_identity_files(void) | 1236 | load_public_identity_files(void) |
1220 | { | 1237 | { |
1221 | char *filename, *cp, thishost[NI_MAXHOST], *fp; | 1238 | char *filename, *cp, thishost[NI_MAXHOST], *fp; |
1239 | char *pwdir = NULL, *pwname = NULL; | ||
1222 | int i = 0; | 1240 | int i = 0; |
1223 | Key *public; | 1241 | Key *public; |
1224 | struct passwd *pw; | 1242 | struct passwd *pw; |
@@ -1231,9 +1249,11 @@ load_public_identity_files(void) | |||
1231 | int count = 0; | 1249 | int count = 0; |
1232 | for (i = 0; keys[i] != NULL; i++) { | 1250 | for (i = 0; keys[i] != NULL; i++) { |
1233 | count++; | 1251 | count++; |
1234 | memmove(&options.identity_files[1], &options.identity_files[0], | 1252 | memmove(&options.identity_files[1], |
1253 | &options.identity_files[0], | ||
1235 | sizeof(char *) * (SSH_MAX_IDENTITY_FILES - 1)); | 1254 | sizeof(char *) * (SSH_MAX_IDENTITY_FILES - 1)); |
1236 | memmove(&options.identity_keys[1], &options.identity_keys[0], | 1255 | memmove(&options.identity_keys[1], |
1256 | &options.identity_keys[0], | ||
1237 | sizeof(Key *) * (SSH_MAX_IDENTITY_FILES - 1)); | 1257 | sizeof(Key *) * (SSH_MAX_IDENTITY_FILES - 1)); |
1238 | options.num_identity_files++; | 1258 | options.num_identity_files++; |
1239 | options.identity_keys[0] = keys[i]; | 1259 | options.identity_keys[0] = keys[i]; |
@@ -1247,14 +1267,16 @@ load_public_identity_files(void) | |||
1247 | #endif /* SMARTCARD */ | 1267 | #endif /* SMARTCARD */ |
1248 | if ((pw = getpwuid(original_real_uid)) == NULL) | 1268 | if ((pw = getpwuid(original_real_uid)) == NULL) |
1249 | fatal("load_public_identity_files: getpwuid failed"); | 1269 | fatal("load_public_identity_files: getpwuid failed"); |
1270 | pwname = xstrdup(pw->pw_name); | ||
1271 | pwdir = xstrdup(pw->pw_dir); | ||
1250 | if (gethostname(thishost, sizeof(thishost)) == -1) | 1272 | if (gethostname(thishost, sizeof(thishost)) == -1) |
1251 | fatal("load_public_identity_files: gethostname: %s", | 1273 | fatal("load_public_identity_files: gethostname: %s", |
1252 | strerror(errno)); | 1274 | strerror(errno)); |
1253 | for (; i < options.num_identity_files; i++) { | 1275 | for (; i < options.num_identity_files; i++) { |
1254 | cp = tilde_expand_filename(options.identity_files[i], | 1276 | cp = tilde_expand_filename(options.identity_files[i], |
1255 | original_real_uid); | 1277 | original_real_uid); |
1256 | filename = percent_expand(cp, "d", pw->pw_dir, | 1278 | filename = percent_expand(cp, "d", pwdir, |
1257 | "u", pw->pw_name, "l", thishost, "h", host, | 1279 | "u", pwname, "l", thishost, "h", host, |
1258 | "r", options.user, (char *)NULL); | 1280 | "r", options.user, (char *)NULL); |
1259 | xfree(cp); | 1281 | xfree(cp); |
1260 | public = key_load_public(filename, NULL); | 1282 | public = key_load_public(filename, NULL); |
@@ -1280,231 +1302,8 @@ load_public_identity_files(void) | |||
1280 | options.identity_files[i] = filename; | 1302 | options.identity_files[i] = filename; |
1281 | options.identity_keys[i] = public; | 1303 | options.identity_keys[i] = public; |
1282 | } | 1304 | } |
1283 | } | 1305 | bzero(pwname, strlen(pwname)); |
1284 | 1306 | xfree(pwname); | |
1285 | static void | 1307 | bzero(pwdir, strlen(pwdir)); |
1286 | control_client_sighandler(int signo) | 1308 | xfree(pwdir); |
1287 | { | ||
1288 | control_client_terminate = signo; | ||
1289 | } | ||
1290 | |||
1291 | static void | ||
1292 | control_client_sigrelay(int signo) | ||
1293 | { | ||
1294 | if (control_server_pid > 1) | ||
1295 | kill(control_server_pid, signo); | ||
1296 | } | ||
1297 | |||
1298 | static int | ||
1299 | env_permitted(char *env) | ||
1300 | { | ||
1301 | int i, ret; | ||
1302 | char name[1024], *cp; | ||
1303 | |||
1304 | if ((cp = strchr(env, '=')) == NULL || cp == env) | ||
1305 | return (0); | ||
1306 | ret = snprintf(name, sizeof(name), "%.*s", (int)(cp - env), env); | ||
1307 | if (ret <= 0 || (size_t)ret >= sizeof(name)) | ||
1308 | fatal("env_permitted: name '%.100s...' too long", env); | ||
1309 | |||
1310 | for (i = 0; i < options.num_send_env; i++) | ||
1311 | if (match_pattern(name, options.send_env[i])) | ||
1312 | return (1); | ||
1313 | |||
1314 | return (0); | ||
1315 | } | ||
1316 | |||
1317 | static void | ||
1318 | control_client(const char *path) | ||
1319 | { | ||
1320 | struct sockaddr_un addr; | ||
1321 | int i, r, fd, sock, exitval[2], num_env, addr_len; | ||
1322 | Buffer m; | ||
1323 | char *term; | ||
1324 | extern char **environ; | ||
1325 | u_int flags; | ||
1326 | |||
1327 | if (mux_command == 0) | ||
1328 | mux_command = SSHMUX_COMMAND_OPEN; | ||
1329 | |||
1330 | switch (options.control_master) { | ||
1331 | case SSHCTL_MASTER_AUTO: | ||
1332 | case SSHCTL_MASTER_AUTO_ASK: | ||
1333 | debug("auto-mux: Trying existing master"); | ||
1334 | /* FALLTHROUGH */ | ||
1335 | case SSHCTL_MASTER_NO: | ||
1336 | break; | ||
1337 | default: | ||
1338 | return; | ||
1339 | } | ||
1340 | |||
1341 | memset(&addr, '\0', sizeof(addr)); | ||
1342 | addr.sun_family = AF_UNIX; | ||
1343 | addr_len = offsetof(struct sockaddr_un, sun_path) + | ||
1344 | strlen(path) + 1; | ||
1345 | |||
1346 | if (strlcpy(addr.sun_path, path, | ||
1347 | sizeof(addr.sun_path)) >= sizeof(addr.sun_path)) | ||
1348 | fatal("ControlPath too long"); | ||
1349 | |||
1350 | if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) | ||
1351 | fatal("%s socket(): %s", __func__, strerror(errno)); | ||
1352 | |||
1353 | if (connect(sock, (struct sockaddr *)&addr, addr_len) == -1) { | ||
1354 | if (mux_command != SSHMUX_COMMAND_OPEN) { | ||
1355 | fatal("Control socket connect(%.100s): %s", path, | ||
1356 | strerror(errno)); | ||
1357 | } | ||
1358 | if (errno == ENOENT) | ||
1359 | debug("Control socket \"%.100s\" does not exist", path); | ||
1360 | else { | ||
1361 | error("Control socket connect(%.100s): %s", path, | ||
1362 | strerror(errno)); | ||
1363 | } | ||
1364 | close(sock); | ||
1365 | return; | ||
1366 | } | ||
1367 | |||
1368 | if (stdin_null_flag) { | ||
1369 | if ((fd = open(_PATH_DEVNULL, O_RDONLY)) == -1) | ||
1370 | fatal("open(/dev/null): %s", strerror(errno)); | ||
1371 | if (dup2(fd, STDIN_FILENO) == -1) | ||
1372 | fatal("dup2: %s", strerror(errno)); | ||
1373 | if (fd > STDERR_FILENO) | ||
1374 | close(fd); | ||
1375 | } | ||
1376 | |||
1377 | term = getenv("TERM"); | ||
1378 | |||
1379 | flags = 0; | ||
1380 | if (tty_flag) | ||
1381 | flags |= SSHMUX_FLAG_TTY; | ||
1382 | if (subsystem_flag) | ||
1383 | flags |= SSHMUX_FLAG_SUBSYS; | ||
1384 | if (options.forward_x11) | ||
1385 | flags |= SSHMUX_FLAG_X11_FWD; | ||
1386 | if (options.forward_agent) | ||
1387 | flags |= SSHMUX_FLAG_AGENT_FWD; | ||
1388 | |||
1389 | buffer_init(&m); | ||
1390 | |||
1391 | /* Send our command to server */ | ||
1392 | buffer_put_int(&m, mux_command); | ||
1393 | buffer_put_int(&m, flags); | ||
1394 | if (ssh_msg_send(sock, SSHMUX_VER, &m) == -1) | ||
1395 | fatal("%s: msg_send", __func__); | ||
1396 | buffer_clear(&m); | ||
1397 | |||
1398 | /* Get authorisation status and PID of controlee */ | ||
1399 | if (ssh_msg_recv(sock, &m) == -1) | ||
1400 | fatal("%s: msg_recv", __func__); | ||
1401 | if (buffer_get_char(&m) != SSHMUX_VER) | ||
1402 | fatal("%s: wrong version", __func__); | ||
1403 | if (buffer_get_int(&m) != 1) | ||
1404 | fatal("Connection to master denied"); | ||
1405 | control_server_pid = buffer_get_int(&m); | ||
1406 | |||
1407 | buffer_clear(&m); | ||
1408 | |||
1409 | switch (mux_command) { | ||
1410 | case SSHMUX_COMMAND_ALIVE_CHECK: | ||
1411 | fprintf(stderr, "Master running (pid=%d)\r\n", | ||
1412 | control_server_pid); | ||
1413 | exit(0); | ||
1414 | case SSHMUX_COMMAND_TERMINATE: | ||
1415 | fprintf(stderr, "Exit request sent.\r\n"); | ||
1416 | exit(0); | ||
1417 | case SSHMUX_COMMAND_OPEN: | ||
1418 | /* continue below */ | ||
1419 | break; | ||
1420 | default: | ||
1421 | fatal("silly mux_command %d", mux_command); | ||
1422 | } | ||
1423 | |||
1424 | /* SSHMUX_COMMAND_OPEN */ | ||
1425 | buffer_put_cstring(&m, term ? term : ""); | ||
1426 | buffer_append(&command, "\0", 1); | ||
1427 | buffer_put_cstring(&m, buffer_ptr(&command)); | ||
1428 | |||
1429 | if (options.num_send_env == 0 || environ == NULL) { | ||
1430 | buffer_put_int(&m, 0); | ||
1431 | } else { | ||
1432 | /* Pass environment */ | ||
1433 | num_env = 0; | ||
1434 | for (i = 0; environ[i] != NULL; i++) | ||
1435 | if (env_permitted(environ[i])) | ||
1436 | num_env++; /* Count */ | ||
1437 | |||
1438 | buffer_put_int(&m, num_env); | ||
1439 | |||
1440 | for (i = 0; environ[i] != NULL && num_env >= 0; i++) | ||
1441 | if (env_permitted(environ[i])) { | ||
1442 | num_env--; | ||
1443 | buffer_put_cstring(&m, environ[i]); | ||
1444 | } | ||
1445 | } | ||
1446 | |||
1447 | if (ssh_msg_send(sock, SSHMUX_VER, &m) == -1) | ||
1448 | fatal("%s: msg_send", __func__); | ||
1449 | |||
1450 | mm_send_fd(sock, STDIN_FILENO); | ||
1451 | mm_send_fd(sock, STDOUT_FILENO); | ||
1452 | mm_send_fd(sock, STDERR_FILENO); | ||
1453 | |||
1454 | /* Wait for reply, so master has a chance to gather ttymodes */ | ||
1455 | buffer_clear(&m); | ||
1456 | if (ssh_msg_recv(sock, &m) == -1) | ||
1457 | fatal("%s: msg_recv", __func__); | ||
1458 | if (buffer_get_char(&m) != SSHMUX_VER) | ||
1459 | fatal("%s: wrong version", __func__); | ||
1460 | buffer_free(&m); | ||
1461 | |||
1462 | signal(SIGHUP, control_client_sighandler); | ||
1463 | signal(SIGINT, control_client_sighandler); | ||
1464 | signal(SIGTERM, control_client_sighandler); | ||
1465 | signal(SIGWINCH, control_client_sigrelay); | ||
1466 | |||
1467 | if (tty_flag) | ||
1468 | enter_raw_mode(); | ||
1469 | |||
1470 | /* | ||
1471 | * Stick around until the controlee closes the client_fd. | ||
1472 | * Before it does, it is expected to write this process' exit | ||
1473 | * value (one int). This process must read the value and wait for | ||
1474 | * the closure of the client_fd; if this one closes early, the | ||
1475 | * multiplex master will terminate early too (possibly losing data). | ||
1476 | */ | ||
1477 | exitval[0] = 0; | ||
1478 | for (i = 0; !control_client_terminate && i < (int)sizeof(exitval);) { | ||
1479 | r = read(sock, (char *)exitval + i, sizeof(exitval) - i); | ||
1480 | if (r == 0) { | ||
1481 | debug2("Received EOF from master"); | ||
1482 | break; | ||
1483 | } | ||
1484 | if (r == -1) { | ||
1485 | if (errno == EINTR) | ||
1486 | continue; | ||
1487 | fatal("%s: read %s", __func__, strerror(errno)); | ||
1488 | } | ||
1489 | i += r; | ||
1490 | } | ||
1491 | |||
1492 | close(sock); | ||
1493 | leave_raw_mode(); | ||
1494 | if (i > (int)sizeof(int)) | ||
1495 | fatal("%s: master returned too much data (%d > %lu)", | ||
1496 | __func__, i, sizeof(int)); | ||
1497 | if (control_client_terminate) { | ||
1498 | debug2("Exiting on signal %d", control_client_terminate); | ||
1499 | exitval[0] = 255; | ||
1500 | } else if (i < (int)sizeof(int)) { | ||
1501 | debug2("Control master terminated unexpectedly"); | ||
1502 | exitval[0] = 255; | ||
1503 | } else | ||
1504 | debug2("Received exit status from master %d", exitval[0]); | ||
1505 | |||
1506 | if (tty_flag && options.log_level > SYSLOG_LEVEL_QUIET) | ||
1507 | fprintf(stderr, "Shared connection to %s closed.\r\n", host); | ||
1508 | |||
1509 | exit(exitval[0]); | ||
1510 | } | 1309 | } |