diff options
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 | } |