diff options
author | Damien Miller <djm@mindrot.org> | 2008-05-19 16:00:08 +1000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2008-05-19 16:00:08 +1000 |
commit | b1cbfa25f1618b277de670b943acb07ff5dbb1ac (patch) | |
tree | 159cc3fa99ba5c6a2b880ad216542b1262e186fa | |
parent | bd74025c7b08104828986d01e60f04372b3d5337 (diff) |
- djm@cvs.openbsd.org 2008/05/09 14:18:44
[clientloop.c clientloop.h ssh.c mux.c]
tidy up session multiplexing code, moving it into its own file and
making the function names more consistent - making ssh.c and
clientloop.c a fair bit more readable.
ok markus@
-rw-r--r-- | ChangeLog | 8 | ||||
-rw-r--r-- | Makefile.in | 4 | ||||
-rw-r--r-- | clientloop.c | 308 | ||||
-rw-r--r-- | clientloop.h | 20 | ||||
-rw-r--r-- | mux.c | 646 | ||||
-rw-r--r-- | ssh.c | 309 |
6 files changed, 699 insertions, 596 deletions
@@ -111,6 +111,12 @@ | |||
111 | was to try the first address and give up. | 111 | was to try the first address and give up. |
112 | Reported by stig AT venaas.com in bz#343 | 112 | Reported by stig AT venaas.com in bz#343 |
113 | great feedback and ok markus@ | 113 | great feedback and ok markus@ |
114 | - djm@cvs.openbsd.org 2008/05/09 14:18:44 | ||
115 | [clientloop.c clientloop.h ssh.c mux.c] | ||
116 | tidy up session multiplexing code, moving it into its own file and | ||
117 | making the function names more consistent - making ssh.c and | ||
118 | clientloop.c a fair bit more readable. | ||
119 | ok markus@ | ||
114 | 120 | ||
115 | 20080403 | 121 | 20080403 |
116 | - (djm) [openbsd-compat/bsd-poll.c] Include stdlib.h to avoid compile- | 122 | - (djm) [openbsd-compat/bsd-poll.c] Include stdlib.h to avoid compile- |
@@ -3971,4 +3977,4 @@ | |||
3971 | OpenServer 6 and add osr5bigcrypt support so when someone migrates | 3977 | OpenServer 6 and add osr5bigcrypt support so when someone migrates |
3972 | passwords between UnixWare and OpenServer they will still work. OK dtucker@ | 3978 | passwords between UnixWare and OpenServer they will still work. OK dtucker@ |
3973 | 3979 | ||
3974 | $Id: ChangeLog,v 1.4925 2008/05/19 05:37:09 djm Exp $ | 3980 | $Id: ChangeLog,v 1.4926 2008/05/19 06:00:08 djm Exp $ |
diff --git a/Makefile.in b/Makefile.in index 6b488feca..b57b9b72d 100644 --- a/Makefile.in +++ b/Makefile.in | |||
@@ -1,4 +1,4 @@ | |||
1 | # $Id: Makefile.in,v 1.289 2008/03/13 01:41:31 djm Exp $ | 1 | # $Id: Makefile.in,v 1.290 2008/05/19 06:00:08 djm Exp $ |
2 | 2 | ||
3 | # uncomment if you run a non bourne compatable shell. Ie. csh | 3 | # uncomment if you run a non bourne compatable shell. Ie. csh |
4 | #SHELL = @SH@ | 4 | #SHELL = @SH@ |
@@ -74,7 +74,7 @@ LIBSSH_OBJS=acss.o authfd.o authfile.o bufaux.o bufbn.o buffer.o \ | |||
74 | entropy.o scard-opensc.o gss-genr.o umac.o | 74 | entropy.o scard-opensc.o gss-genr.o umac.o |
75 | 75 | ||
76 | SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ | 76 | SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ |
77 | sshconnect.o sshconnect1.o sshconnect2.o | 77 | sshconnect.o sshconnect1.o sshconnect2.o mux.o |
78 | 78 | ||
79 | SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o \ | 79 | SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o auth-rsa.o auth-rh-rsa.o \ |
80 | sshpty.o sshlogin.o servconf.o serverloop.o \ | 80 | sshpty.o sshlogin.o servconf.o serverloop.o \ |
diff --git a/clientloop.c b/clientloop.c index 7bd1af60c..c87aa5a0a 100644 --- a/clientloop.c +++ b/clientloop.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: clientloop.c,v 1.191 2008/05/09 04:55:56 djm Exp $ */ | 1 | /* $OpenBSD: clientloop.c,v 1.192 2008/05/09 14:18:44 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 |
@@ -121,7 +121,7 @@ extern int stdin_null_flag; | |||
121 | extern int no_shell_flag; | 121 | extern int no_shell_flag; |
122 | 122 | ||
123 | /* Control socket */ | 123 | /* Control socket */ |
124 | extern int control_fd; | 124 | extern int muxserver_sock; |
125 | 125 | ||
126 | /* | 126 | /* |
127 | * Name of the host we are connecting to. This is the name given on the | 127 | * Name of the host we are connecting to. This is the name given on the |
@@ -162,17 +162,6 @@ static int session_closed = 0; /* In SSH2: login session closed. */ | |||
162 | static void client_init_dispatch(void); | 162 | static void client_init_dispatch(void); |
163 | int session_ident = -1; | 163 | int session_ident = -1; |
164 | 164 | ||
165 | struct confirm_ctx { | ||
166 | int want_tty; | ||
167 | int want_subsys; | ||
168 | int want_x_fwd; | ||
169 | int want_agent_fwd; | ||
170 | Buffer cmd; | ||
171 | char *term; | ||
172 | struct termios tio; | ||
173 | char **env; | ||
174 | }; | ||
175 | |||
176 | struct channel_reply_ctx { | 165 | struct channel_reply_ctx { |
177 | const char *request_type; | 166 | const char *request_type; |
178 | int id, do_close; | 167 | int id, do_close; |
@@ -538,8 +527,8 @@ client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, | |||
538 | if (packet_have_data_to_write()) | 527 | if (packet_have_data_to_write()) |
539 | FD_SET(connection_out, *writesetp); | 528 | FD_SET(connection_out, *writesetp); |
540 | 529 | ||
541 | if (control_fd != -1) | 530 | if (muxserver_sock != -1) |
542 | FD_SET(control_fd, *readsetp); | 531 | FD_SET(muxserver_sock, *readsetp); |
543 | 532 | ||
544 | /* | 533 | /* |
545 | * Wait for something to happen. This will suspend the process until | 534 | * Wait for something to happen. This will suspend the process until |
@@ -707,284 +696,6 @@ client_expect_confirm(int id, const char *request, int do_close) | |||
707 | } | 696 | } |
708 | 697 | ||
709 | static void | 698 | static void |
710 | client_extra_session2_setup(int id, void *arg) | ||
711 | { | ||
712 | struct confirm_ctx *cctx = arg; | ||
713 | const char *display; | ||
714 | Channel *c; | ||
715 | int i; | ||
716 | |||
717 | if (cctx == NULL) | ||
718 | fatal("%s: cctx == NULL", __func__); | ||
719 | if ((c = channel_lookup(id)) == NULL) | ||
720 | fatal("%s: no channel for id %d", __func__, id); | ||
721 | |||
722 | display = getenv("DISPLAY"); | ||
723 | if (cctx->want_x_fwd && options.forward_x11 && display != NULL) { | ||
724 | char *proto, *data; | ||
725 | /* Get reasonable local authentication information. */ | ||
726 | client_x11_get_proto(display, options.xauth_location, | ||
727 | options.forward_x11_trusted, &proto, &data); | ||
728 | /* Request forwarding with authentication spoofing. */ | ||
729 | debug("Requesting X11 forwarding with authentication spoofing."); | ||
730 | x11_request_forwarding_with_spoofing(id, display, proto, data); | ||
731 | /* XXX wait for reply */ | ||
732 | } | ||
733 | |||
734 | if (cctx->want_agent_fwd && options.forward_agent) { | ||
735 | debug("Requesting authentication agent forwarding."); | ||
736 | channel_request_start(id, "auth-agent-req@openssh.com", 0); | ||
737 | packet_send(); | ||
738 | } | ||
739 | |||
740 | client_session2_setup(id, cctx->want_tty, cctx->want_subsys, | ||
741 | cctx->term, &cctx->tio, c->rfd, &cctx->cmd, cctx->env); | ||
742 | |||
743 | c->open_confirm_ctx = NULL; | ||
744 | buffer_free(&cctx->cmd); | ||
745 | xfree(cctx->term); | ||
746 | if (cctx->env != NULL) { | ||
747 | for (i = 0; cctx->env[i] != NULL; i++) | ||
748 | xfree(cctx->env[i]); | ||
749 | xfree(cctx->env); | ||
750 | } | ||
751 | xfree(cctx); | ||
752 | } | ||
753 | |||
754 | static void | ||
755 | client_process_control(fd_set *readset) | ||
756 | { | ||
757 | Buffer m; | ||
758 | Channel *c; | ||
759 | int client_fd, new_fd[3], ver, allowed, window, packetmax; | ||
760 | socklen_t addrlen; | ||
761 | struct sockaddr_storage addr; | ||
762 | struct confirm_ctx *cctx; | ||
763 | char *cmd; | ||
764 | u_int i, j, len, env_len, command, flags; | ||
765 | uid_t euid; | ||
766 | gid_t egid; | ||
767 | |||
768 | /* | ||
769 | * Accept connection on control socket | ||
770 | */ | ||
771 | if (control_fd == -1 || !FD_ISSET(control_fd, readset)) | ||
772 | return; | ||
773 | |||
774 | memset(&addr, 0, sizeof(addr)); | ||
775 | addrlen = sizeof(addr); | ||
776 | if ((client_fd = accept(control_fd, | ||
777 | (struct sockaddr*)&addr, &addrlen)) == -1) { | ||
778 | error("%s accept: %s", __func__, strerror(errno)); | ||
779 | return; | ||
780 | } | ||
781 | |||
782 | if (getpeereid(client_fd, &euid, &egid) < 0) { | ||
783 | error("%s getpeereid failed: %s", __func__, strerror(errno)); | ||
784 | close(client_fd); | ||
785 | return; | ||
786 | } | ||
787 | if ((euid != 0) && (getuid() != euid)) { | ||
788 | error("control mode uid mismatch: peer euid %u != uid %u", | ||
789 | (u_int) euid, (u_int) getuid()); | ||
790 | close(client_fd); | ||
791 | return; | ||
792 | } | ||
793 | |||
794 | unset_nonblock(client_fd); | ||
795 | |||
796 | /* Read command */ | ||
797 | buffer_init(&m); | ||
798 | if (ssh_msg_recv(client_fd, &m) == -1) { | ||
799 | error("%s: client msg_recv failed", __func__); | ||
800 | close(client_fd); | ||
801 | buffer_free(&m); | ||
802 | return; | ||
803 | } | ||
804 | if ((ver = buffer_get_char(&m)) != SSHMUX_VER) { | ||
805 | error("%s: wrong client version %d", __func__, ver); | ||
806 | buffer_free(&m); | ||
807 | close(client_fd); | ||
808 | return; | ||
809 | } | ||
810 | |||
811 | allowed = 1; | ||
812 | command = buffer_get_int(&m); | ||
813 | flags = buffer_get_int(&m); | ||
814 | |||
815 | buffer_clear(&m); | ||
816 | |||
817 | switch (command) { | ||
818 | case SSHMUX_COMMAND_OPEN: | ||
819 | if (options.control_master == SSHCTL_MASTER_ASK || | ||
820 | options.control_master == SSHCTL_MASTER_AUTO_ASK) | ||
821 | allowed = ask_permission("Allow shared connection " | ||
822 | "to %s? ", host); | ||
823 | /* continue below */ | ||
824 | break; | ||
825 | case SSHMUX_COMMAND_TERMINATE: | ||
826 | if (options.control_master == SSHCTL_MASTER_ASK || | ||
827 | options.control_master == SSHCTL_MASTER_AUTO_ASK) | ||
828 | allowed = ask_permission("Terminate shared connection " | ||
829 | "to %s? ", host); | ||
830 | if (allowed) | ||
831 | quit_pending = 1; | ||
832 | /* FALLTHROUGH */ | ||
833 | case SSHMUX_COMMAND_ALIVE_CHECK: | ||
834 | /* Reply for SSHMUX_COMMAND_TERMINATE and ALIVE_CHECK */ | ||
835 | buffer_clear(&m); | ||
836 | buffer_put_int(&m, allowed); | ||
837 | buffer_put_int(&m, getpid()); | ||
838 | if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) { | ||
839 | error("%s: client msg_send failed", __func__); | ||
840 | close(client_fd); | ||
841 | buffer_free(&m); | ||
842 | return; | ||
843 | } | ||
844 | buffer_free(&m); | ||
845 | close(client_fd); | ||
846 | return; | ||
847 | default: | ||
848 | error("Unsupported command %d", command); | ||
849 | buffer_free(&m); | ||
850 | close(client_fd); | ||
851 | return; | ||
852 | } | ||
853 | |||
854 | /* Reply for SSHMUX_COMMAND_OPEN */ | ||
855 | buffer_clear(&m); | ||
856 | buffer_put_int(&m, allowed); | ||
857 | buffer_put_int(&m, getpid()); | ||
858 | if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) { | ||
859 | error("%s: client msg_send failed", __func__); | ||
860 | close(client_fd); | ||
861 | buffer_free(&m); | ||
862 | return; | ||
863 | } | ||
864 | |||
865 | if (!allowed) { | ||
866 | error("Refused control connection"); | ||
867 | close(client_fd); | ||
868 | buffer_free(&m); | ||
869 | return; | ||
870 | } | ||
871 | |||
872 | buffer_clear(&m); | ||
873 | if (ssh_msg_recv(client_fd, &m) == -1) { | ||
874 | error("%s: client msg_recv failed", __func__); | ||
875 | close(client_fd); | ||
876 | buffer_free(&m); | ||
877 | return; | ||
878 | } | ||
879 | if ((ver = buffer_get_char(&m)) != SSHMUX_VER) { | ||
880 | error("%s: wrong client version %d", __func__, ver); | ||
881 | buffer_free(&m); | ||
882 | close(client_fd); | ||
883 | return; | ||
884 | } | ||
885 | |||
886 | cctx = xcalloc(1, sizeof(*cctx)); | ||
887 | cctx->want_tty = (flags & SSHMUX_FLAG_TTY) != 0; | ||
888 | cctx->want_subsys = (flags & SSHMUX_FLAG_SUBSYS) != 0; | ||
889 | cctx->want_x_fwd = (flags & SSHMUX_FLAG_X11_FWD) != 0; | ||
890 | cctx->want_agent_fwd = (flags & SSHMUX_FLAG_AGENT_FWD) != 0; | ||
891 | cctx->term = buffer_get_string(&m, &len); | ||
892 | |||
893 | cmd = buffer_get_string(&m, &len); | ||
894 | buffer_init(&cctx->cmd); | ||
895 | buffer_append(&cctx->cmd, cmd, strlen(cmd)); | ||
896 | |||
897 | env_len = buffer_get_int(&m); | ||
898 | env_len = MIN(env_len, 4096); | ||
899 | debug3("%s: receiving %d env vars", __func__, env_len); | ||
900 | if (env_len != 0) { | ||
901 | cctx->env = xcalloc(env_len + 1, sizeof(*cctx->env)); | ||
902 | for (i = 0; i < env_len; i++) | ||
903 | cctx->env[i] = buffer_get_string(&m, &len); | ||
904 | cctx->env[i] = NULL; | ||
905 | } | ||
906 | |||
907 | debug2("%s: accepted tty %d, subsys %d, cmd %s", __func__, | ||
908 | cctx->want_tty, cctx->want_subsys, cmd); | ||
909 | xfree(cmd); | ||
910 | |||
911 | /* Gather fds from client */ | ||
912 | for(i = 0; i < 3; i++) { | ||
913 | if ((new_fd[i] = mm_receive_fd(client_fd)) == -1) { | ||
914 | error("%s: failed to receive fd %d from slave", | ||
915 | __func__, i); | ||
916 | for (j = 0; j < i; j++) | ||
917 | close(new_fd[j]); | ||
918 | for (j = 0; j < env_len; j++) | ||
919 | xfree(cctx->env[j]); | ||
920 | if (env_len > 0) | ||
921 | xfree(cctx->env); | ||
922 | xfree(cctx->term); | ||
923 | buffer_free(&cctx->cmd); | ||
924 | close(client_fd); | ||
925 | xfree(cctx); | ||
926 | return; | ||
927 | } | ||
928 | } | ||
929 | |||
930 | debug2("%s: got fds stdin %d, stdout %d, stderr %d", __func__, | ||
931 | new_fd[0], new_fd[1], new_fd[2]); | ||
932 | |||
933 | /* Try to pick up ttymodes from client before it goes raw */ | ||
934 | if (cctx->want_tty && tcgetattr(new_fd[0], &cctx->tio) == -1) | ||
935 | error("%s: tcgetattr: %s", __func__, strerror(errno)); | ||
936 | |||
937 | /* This roundtrip is just for synchronisation of ttymodes */ | ||
938 | buffer_clear(&m); | ||
939 | if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) { | ||
940 | error("%s: client msg_send failed", __func__); | ||
941 | close(client_fd); | ||
942 | close(new_fd[0]); | ||
943 | close(new_fd[1]); | ||
944 | close(new_fd[2]); | ||
945 | buffer_free(&m); | ||
946 | xfree(cctx->term); | ||
947 | if (env_len != 0) { | ||
948 | for (i = 0; i < env_len; i++) | ||
949 | xfree(cctx->env[i]); | ||
950 | xfree(cctx->env); | ||
951 | } | ||
952 | return; | ||
953 | } | ||
954 | buffer_free(&m); | ||
955 | |||
956 | /* enable nonblocking unless tty */ | ||
957 | if (!isatty(new_fd[0])) | ||
958 | set_nonblock(new_fd[0]); | ||
959 | if (!isatty(new_fd[1])) | ||
960 | set_nonblock(new_fd[1]); | ||
961 | if (!isatty(new_fd[2])) | ||
962 | set_nonblock(new_fd[2]); | ||
963 | |||
964 | set_nonblock(client_fd); | ||
965 | |||
966 | window = CHAN_SES_WINDOW_DEFAULT; | ||
967 | packetmax = CHAN_SES_PACKET_DEFAULT; | ||
968 | if (cctx->want_tty) { | ||
969 | window >>= 1; | ||
970 | packetmax >>= 1; | ||
971 | } | ||
972 | |||
973 | c = channel_new("session", SSH_CHANNEL_OPENING, | ||
974 | new_fd[0], new_fd[1], new_fd[2], window, packetmax, | ||
975 | CHAN_EXTENDED_WRITE, "client-session", /*nonblock*/0); | ||
976 | |||
977 | /* XXX */ | ||
978 | c->ctl_fd = client_fd; | ||
979 | |||
980 | debug3("%s: channel_new: %d", __func__, c->self); | ||
981 | |||
982 | channel_send_open(c->self); | ||
983 | channel_register_open_confirm(c->self, | ||
984 | client_extra_session2_setup, cctx); | ||
985 | } | ||
986 | |||
987 | static void | ||
988 | process_cmdline(void) | 699 | process_cmdline(void) |
989 | { | 700 | { |
990 | void (*handler)(int); | 701 | void (*handler)(int); |
@@ -1448,8 +1159,8 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) | |||
1448 | connection_in = packet_get_connection_in(); | 1159 | connection_in = packet_get_connection_in(); |
1449 | connection_out = packet_get_connection_out(); | 1160 | connection_out = packet_get_connection_out(); |
1450 | max_fd = MAX(connection_in, connection_out); | 1161 | max_fd = MAX(connection_in, connection_out); |
1451 | if (control_fd != -1) | 1162 | if (muxserver_sock != -1) |
1452 | max_fd = MAX(max_fd, control_fd); | 1163 | max_fd = MAX(max_fd, muxserver_sock); |
1453 | 1164 | ||
1454 | if (!compat20) { | 1165 | if (!compat20) { |
1455 | /* enable nonblocking unless tty */ | 1166 | /* enable nonblocking unless tty */ |
@@ -1569,7 +1280,10 @@ client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) | |||
1569 | client_process_net_input(readset); | 1280 | client_process_net_input(readset); |
1570 | 1281 | ||
1571 | /* Accept control connections. */ | 1282 | /* Accept control connections. */ |
1572 | client_process_control(readset); | 1283 | if (muxserver_sock != -1 &&FD_ISSET(muxserver_sock, readset)) { |
1284 | if (muxserver_accept_control()) | ||
1285 | quit_pending = 1; | ||
1286 | } | ||
1573 | 1287 | ||
1574 | if (quit_pending) | 1288 | if (quit_pending) |
1575 | break; | 1289 | break; |
@@ -2157,7 +1871,7 @@ cleanup_exit(int i) | |||
2157 | { | 1871 | { |
2158 | leave_raw_mode(); | 1872 | leave_raw_mode(); |
2159 | leave_non_blocking(); | 1873 | leave_non_blocking(); |
2160 | if (options.control_path != NULL && control_fd != -1) | 1874 | if (options.control_path != NULL && muxserver_sock != -1) |
2161 | unlink(options.control_path); | 1875 | unlink(options.control_path); |
2162 | _exit(i); | 1876 | _exit(i); |
2163 | } | 1877 | } |
diff --git a/clientloop.h b/clientloop.h index cb2d7c089..6f8e70123 100644 --- a/clientloop.h +++ b/clientloop.h | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: clientloop.h,v 1.18 2008/05/08 13:06:11 djm Exp $ */ | 1 | /* $OpenBSD: clientloop.h,v 1.19 2008/05/09 14:18:44 djm Exp $ */ |
2 | 2 | ||
3 | /* | 3 | /* |
4 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 4 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
@@ -58,3 +58,21 @@ int client_request_tun_fwd(int, int, int); | |||
58 | #define SSHMUX_FLAG_SUBSYS (1<<1) /* Subsystem request on open */ | 58 | #define SSHMUX_FLAG_SUBSYS (1<<1) /* Subsystem request on open */ |
59 | #define SSHMUX_FLAG_X11_FWD (1<<2) /* Request X11 forwarding */ | 59 | #define SSHMUX_FLAG_X11_FWD (1<<2) /* Request X11 forwarding */ |
60 | #define SSHMUX_FLAG_AGENT_FWD (1<<3) /* Request agent forwarding */ | 60 | #define SSHMUX_FLAG_AGENT_FWD (1<<3) /* Request agent forwarding */ |
61 | |||
62 | /* Multiplexing routines */ | ||
63 | |||
64 | struct mux_session_confirm_ctx { | ||
65 | int want_tty; | ||
66 | int want_subsys; | ||
67 | int want_x_fwd; | ||
68 | int want_agent_fwd; | ||
69 | Buffer cmd; | ||
70 | char *term; | ||
71 | struct termios tio; | ||
72 | char **env; | ||
73 | }; | ||
74 | |||
75 | /* mux.c */ | ||
76 | void muxserver_listen(void); | ||
77 | int muxserver_accept_control(void); | ||
78 | void muxclient(const char *); | ||
@@ -0,0 +1,646 @@ | |||
1 | /* $OpenBSD: mux.c,v 1.1 2008/05/09 14:18:44 djm Exp $ */ | ||
2 | /* | ||
3 | * Copyright (c) 2002-2008 Damien Miller <djm@openbsd.org> | ||
4 | * | ||
5 | * Permission to use, copy, modify, and distribute this software for any | ||
6 | * purpose with or without fee is hereby granted, provided that the above | ||
7 | * copyright notice and this permission notice appear in all copies. | ||
8 | * | ||
9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
16 | */ | ||
17 | |||
18 | /* ssh session multiplexing support */ | ||
19 | |||
20 | #include <sys/types.h> | ||
21 | #include <sys/param.h> | ||
22 | #include <sys/stat.h> | ||
23 | #include <sys/socket.h> | ||
24 | #include <sys/un.h> | ||
25 | |||
26 | #include <errno.h> | ||
27 | #include <fcntl.h> | ||
28 | #include <signal.h> | ||
29 | #include <stdarg.h> | ||
30 | #include <stddef.h> | ||
31 | #include <stdlib.h> | ||
32 | #include <stdio.h> | ||
33 | #include <string.h> | ||
34 | #include <unistd.h> | ||
35 | #include <util.h> | ||
36 | #include <paths.h> | ||
37 | |||
38 | #include "openbsd-compat/sys-queue.h" | ||
39 | #include "xmalloc.h" | ||
40 | #include "log.h" | ||
41 | #include "ssh.h" | ||
42 | #include "pathnames.h" | ||
43 | #include "misc.h" | ||
44 | #include "match.h" | ||
45 | #include "buffer.h" | ||
46 | #include "channels.h" | ||
47 | #include "msg.h" | ||
48 | #include "packet.h" | ||
49 | #include "monitor_fdpass.h" | ||
50 | #include "sshpty.h" | ||
51 | #include "key.h" | ||
52 | #include "readconf.h" | ||
53 | #include "clientloop.h" | ||
54 | |||
55 | /* from ssh.c */ | ||
56 | extern int tty_flag; | ||
57 | extern Options options; | ||
58 | extern int stdin_null_flag; | ||
59 | extern char *host; | ||
60 | int subsystem_flag; | ||
61 | extern Buffer command; | ||
62 | |||
63 | /* fd to control socket */ | ||
64 | int muxserver_sock = -1; | ||
65 | |||
66 | /* Multiplexing control command */ | ||
67 | u_int muxclient_command = 0; | ||
68 | |||
69 | /* Set when signalled. */ | ||
70 | static volatile sig_atomic_t muxclient_terminate = 0; | ||
71 | |||
72 | /* PID of multiplex server */ | ||
73 | static u_int muxserver_pid = 0; | ||
74 | |||
75 | |||
76 | /* ** Multiplexing master support */ | ||
77 | |||
78 | /* Prepare a mux master to listen on a Unix domain socket. */ | ||
79 | void | ||
80 | muxserver_listen(void) | ||
81 | { | ||
82 | struct sockaddr_un addr; | ||
83 | mode_t old_umask; | ||
84 | int addr_len; | ||
85 | |||
86 | if (options.control_path == NULL || | ||
87 | options.control_master == SSHCTL_MASTER_NO) | ||
88 | return; | ||
89 | |||
90 | debug("setting up multiplex master socket"); | ||
91 | |||
92 | memset(&addr, '\0', sizeof(addr)); | ||
93 | addr.sun_family = AF_UNIX; | ||
94 | addr_len = offsetof(struct sockaddr_un, sun_path) + | ||
95 | strlen(options.control_path) + 1; | ||
96 | |||
97 | if (strlcpy(addr.sun_path, options.control_path, | ||
98 | sizeof(addr.sun_path)) >= sizeof(addr.sun_path)) | ||
99 | fatal("ControlPath too long"); | ||
100 | |||
101 | if ((muxserver_sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) | ||
102 | fatal("%s socket(): %s", __func__, strerror(errno)); | ||
103 | |||
104 | old_umask = umask(0177); | ||
105 | if (bind(muxserver_sock, (struct sockaddr *)&addr, addr_len) == -1) { | ||
106 | muxserver_sock = -1; | ||
107 | if (errno == EINVAL || errno == EADDRINUSE) | ||
108 | fatal("ControlSocket %s already exists", | ||
109 | options.control_path); | ||
110 | else | ||
111 | fatal("%s bind(): %s", __func__, strerror(errno)); | ||
112 | } | ||
113 | umask(old_umask); | ||
114 | |||
115 | if (listen(muxserver_sock, 64) == -1) | ||
116 | fatal("%s listen(): %s", __func__, strerror(errno)); | ||
117 | |||
118 | set_nonblock(muxserver_sock); | ||
119 | } | ||
120 | |||
121 | /* Callback on open confirmation in mux master for a mux client session. */ | ||
122 | static void | ||
123 | client_extra_session2_setup(int id, void *arg) | ||
124 | { | ||
125 | struct mux_session_confirm_ctx *cctx = arg; | ||
126 | const char *display; | ||
127 | Channel *c; | ||
128 | int i; | ||
129 | |||
130 | if (cctx == NULL) | ||
131 | fatal("%s: cctx == NULL", __func__); | ||
132 | if ((c = channel_lookup(id)) == NULL) | ||
133 | fatal("%s: no channel for id %d", __func__, id); | ||
134 | |||
135 | display = getenv("DISPLAY"); | ||
136 | if (cctx->want_x_fwd && options.forward_x11 && display != NULL) { | ||
137 | char *proto, *data; | ||
138 | /* Get reasonable local authentication information. */ | ||
139 | client_x11_get_proto(display, options.xauth_location, | ||
140 | options.forward_x11_trusted, &proto, &data); | ||
141 | /* Request forwarding with authentication spoofing. */ | ||
142 | debug("Requesting X11 forwarding with authentication spoofing."); | ||
143 | x11_request_forwarding_with_spoofing(id, display, proto, data); | ||
144 | /* XXX wait for reply */ | ||
145 | } | ||
146 | |||
147 | if (cctx->want_agent_fwd && options.forward_agent) { | ||
148 | debug("Requesting authentication agent forwarding."); | ||
149 | channel_request_start(id, "auth-agent-req@openssh.com", 0); | ||
150 | packet_send(); | ||
151 | } | ||
152 | |||
153 | client_session2_setup(id, cctx->want_tty, cctx->want_subsys, | ||
154 | cctx->term, &cctx->tio, c->rfd, &cctx->cmd, cctx->env); | ||
155 | |||
156 | c->open_confirm_ctx = NULL; | ||
157 | buffer_free(&cctx->cmd); | ||
158 | xfree(cctx->term); | ||
159 | if (cctx->env != NULL) { | ||
160 | for (i = 0; cctx->env[i] != NULL; i++) | ||
161 | xfree(cctx->env[i]); | ||
162 | xfree(cctx->env); | ||
163 | } | ||
164 | xfree(cctx); | ||
165 | } | ||
166 | |||
167 | /* | ||
168 | * Accept a connection on the mux master socket and process the | ||
169 | * client's request. Returns flag indicating whether mux master should | ||
170 | * begin graceful close. | ||
171 | */ | ||
172 | int | ||
173 | muxserver_accept_control(void) | ||
174 | { | ||
175 | Buffer m; | ||
176 | Channel *c; | ||
177 | int client_fd, new_fd[3], ver, allowed, window, packetmax; | ||
178 | socklen_t addrlen; | ||
179 | struct sockaddr_storage addr; | ||
180 | struct mux_session_confirm_ctx *cctx; | ||
181 | char *cmd; | ||
182 | u_int i, j, len, env_len, mux_command, flags; | ||
183 | uid_t euid; | ||
184 | gid_t egid; | ||
185 | int start_close = 0; | ||
186 | |||
187 | /* | ||
188 | * Accept connection on control socket | ||
189 | */ | ||
190 | memset(&addr, 0, sizeof(addr)); | ||
191 | addrlen = sizeof(addr); | ||
192 | if ((client_fd = accept(muxserver_sock, | ||
193 | (struct sockaddr*)&addr, &addrlen)) == -1) { | ||
194 | error("%s accept: %s", __func__, strerror(errno)); | ||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | if (getpeereid(client_fd, &euid, &egid) < 0) { | ||
199 | error("%s getpeereid failed: %s", __func__, strerror(errno)); | ||
200 | close(client_fd); | ||
201 | return 0; | ||
202 | } | ||
203 | if ((euid != 0) && (getuid() != euid)) { | ||
204 | error("control mode uid mismatch: peer euid %u != uid %u", | ||
205 | (u_int) euid, (u_int) getuid()); | ||
206 | close(client_fd); | ||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | /* XXX handle asynchronously */ | ||
211 | unset_nonblock(client_fd); | ||
212 | |||
213 | /* Read command */ | ||
214 | buffer_init(&m); | ||
215 | if (ssh_msg_recv(client_fd, &m) == -1) { | ||
216 | error("%s: client msg_recv failed", __func__); | ||
217 | close(client_fd); | ||
218 | buffer_free(&m); | ||
219 | return 0; | ||
220 | } | ||
221 | if ((ver = buffer_get_char(&m)) != SSHMUX_VER) { | ||
222 | error("%s: wrong client version %d", __func__, ver); | ||
223 | buffer_free(&m); | ||
224 | close(client_fd); | ||
225 | return 0; | ||
226 | } | ||
227 | |||
228 | allowed = 1; | ||
229 | mux_command = buffer_get_int(&m); | ||
230 | flags = buffer_get_int(&m); | ||
231 | |||
232 | buffer_clear(&m); | ||
233 | |||
234 | switch (mux_command) { | ||
235 | case SSHMUX_COMMAND_OPEN: | ||
236 | if (options.control_master == SSHCTL_MASTER_ASK || | ||
237 | options.control_master == SSHCTL_MASTER_AUTO_ASK) | ||
238 | allowed = ask_permission("Allow shared connection " | ||
239 | "to %s? ", host); | ||
240 | /* continue below */ | ||
241 | break; | ||
242 | case SSHMUX_COMMAND_TERMINATE: | ||
243 | if (options.control_master == SSHCTL_MASTER_ASK || | ||
244 | options.control_master == SSHCTL_MASTER_AUTO_ASK) | ||
245 | allowed = ask_permission("Terminate shared connection " | ||
246 | "to %s? ", host); | ||
247 | if (allowed) | ||
248 | start_close = 1; | ||
249 | /* FALLTHROUGH */ | ||
250 | case SSHMUX_COMMAND_ALIVE_CHECK: | ||
251 | /* Reply for SSHMUX_COMMAND_TERMINATE and ALIVE_CHECK */ | ||
252 | buffer_clear(&m); | ||
253 | buffer_put_int(&m, allowed); | ||
254 | buffer_put_int(&m, getpid()); | ||
255 | if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) { | ||
256 | error("%s: client msg_send failed", __func__); | ||
257 | close(client_fd); | ||
258 | buffer_free(&m); | ||
259 | return start_close; | ||
260 | } | ||
261 | buffer_free(&m); | ||
262 | close(client_fd); | ||
263 | return start_close; | ||
264 | default: | ||
265 | error("Unsupported command %d", mux_command); | ||
266 | buffer_free(&m); | ||
267 | close(client_fd); | ||
268 | return 0; | ||
269 | } | ||
270 | |||
271 | /* Reply for SSHMUX_COMMAND_OPEN */ | ||
272 | buffer_clear(&m); | ||
273 | buffer_put_int(&m, allowed); | ||
274 | buffer_put_int(&m, getpid()); | ||
275 | if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) { | ||
276 | error("%s: client msg_send failed", __func__); | ||
277 | close(client_fd); | ||
278 | buffer_free(&m); | ||
279 | return 0; | ||
280 | } | ||
281 | |||
282 | if (!allowed) { | ||
283 | error("Refused control connection"); | ||
284 | close(client_fd); | ||
285 | buffer_free(&m); | ||
286 | return 0; | ||
287 | } | ||
288 | |||
289 | buffer_clear(&m); | ||
290 | if (ssh_msg_recv(client_fd, &m) == -1) { | ||
291 | error("%s: client msg_recv failed", __func__); | ||
292 | close(client_fd); | ||
293 | buffer_free(&m); | ||
294 | return 0; | ||
295 | } | ||
296 | if ((ver = buffer_get_char(&m)) != SSHMUX_VER) { | ||
297 | error("%s: wrong client version %d", __func__, ver); | ||
298 | buffer_free(&m); | ||
299 | close(client_fd); | ||
300 | return 0; | ||
301 | } | ||
302 | |||
303 | cctx = xcalloc(1, sizeof(*cctx)); | ||
304 | cctx->want_tty = (flags & SSHMUX_FLAG_TTY) != 0; | ||
305 | cctx->want_subsys = (flags & SSHMUX_FLAG_SUBSYS) != 0; | ||
306 | cctx->want_x_fwd = (flags & SSHMUX_FLAG_X11_FWD) != 0; | ||
307 | cctx->want_agent_fwd = (flags & SSHMUX_FLAG_AGENT_FWD) != 0; | ||
308 | cctx->term = buffer_get_string(&m, &len); | ||
309 | |||
310 | cmd = buffer_get_string(&m, &len); | ||
311 | buffer_init(&cctx->cmd); | ||
312 | buffer_append(&cctx->cmd, cmd, strlen(cmd)); | ||
313 | |||
314 | env_len = buffer_get_int(&m); | ||
315 | env_len = MIN(env_len, 4096); | ||
316 | debug3("%s: receiving %d env vars", __func__, env_len); | ||
317 | if (env_len != 0) { | ||
318 | cctx->env = xcalloc(env_len + 1, sizeof(*cctx->env)); | ||
319 | for (i = 0; i < env_len; i++) | ||
320 | cctx->env[i] = buffer_get_string(&m, &len); | ||
321 | cctx->env[i] = NULL; | ||
322 | } | ||
323 | |||
324 | debug2("%s: accepted tty %d, subsys %d, cmd %s", __func__, | ||
325 | cctx->want_tty, cctx->want_subsys, cmd); | ||
326 | xfree(cmd); | ||
327 | |||
328 | /* Gather fds from client */ | ||
329 | for(i = 0; i < 3; i++) { | ||
330 | if ((new_fd[i] = mm_receive_fd(client_fd)) == -1) { | ||
331 | error("%s: failed to receive fd %d from slave", | ||
332 | __func__, i); | ||
333 | for (j = 0; j < i; j++) | ||
334 | close(new_fd[j]); | ||
335 | for (j = 0; j < env_len; j++) | ||
336 | xfree(cctx->env[j]); | ||
337 | if (env_len > 0) | ||
338 | xfree(cctx->env); | ||
339 | xfree(cctx->term); | ||
340 | buffer_free(&cctx->cmd); | ||
341 | close(client_fd); | ||
342 | xfree(cctx); | ||
343 | return 0; | ||
344 | } | ||
345 | } | ||
346 | |||
347 | debug2("%s: got fds stdin %d, stdout %d, stderr %d", __func__, | ||
348 | new_fd[0], new_fd[1], new_fd[2]); | ||
349 | |||
350 | /* Try to pick up ttymodes from client before it goes raw */ | ||
351 | if (cctx->want_tty && tcgetattr(new_fd[0], &cctx->tio) == -1) | ||
352 | error("%s: tcgetattr: %s", __func__, strerror(errno)); | ||
353 | |||
354 | /* This roundtrip is just for synchronisation of ttymodes */ | ||
355 | buffer_clear(&m); | ||
356 | if (ssh_msg_send(client_fd, SSHMUX_VER, &m) == -1) { | ||
357 | error("%s: client msg_send failed", __func__); | ||
358 | close(client_fd); | ||
359 | close(new_fd[0]); | ||
360 | close(new_fd[1]); | ||
361 | close(new_fd[2]); | ||
362 | buffer_free(&m); | ||
363 | xfree(cctx->term); | ||
364 | if (env_len != 0) { | ||
365 | for (i = 0; i < env_len; i++) | ||
366 | xfree(cctx->env[i]); | ||
367 | xfree(cctx->env); | ||
368 | } | ||
369 | return 0; | ||
370 | } | ||
371 | buffer_free(&m); | ||
372 | |||
373 | /* enable nonblocking unless tty */ | ||
374 | if (!isatty(new_fd[0])) | ||
375 | set_nonblock(new_fd[0]); | ||
376 | if (!isatty(new_fd[1])) | ||
377 | set_nonblock(new_fd[1]); | ||
378 | if (!isatty(new_fd[2])) | ||
379 | set_nonblock(new_fd[2]); | ||
380 | |||
381 | set_nonblock(client_fd); | ||
382 | |||
383 | window = CHAN_SES_WINDOW_DEFAULT; | ||
384 | packetmax = CHAN_SES_PACKET_DEFAULT; | ||
385 | if (cctx->want_tty) { | ||
386 | window >>= 1; | ||
387 | packetmax >>= 1; | ||
388 | } | ||
389 | |||
390 | c = channel_new("session", SSH_CHANNEL_OPENING, | ||
391 | new_fd[0], new_fd[1], new_fd[2], window, packetmax, | ||
392 | CHAN_EXTENDED_WRITE, "client-session", /*nonblock*/0); | ||
393 | |||
394 | /* XXX */ | ||
395 | c->ctl_fd = client_fd; | ||
396 | |||
397 | debug3("%s: channel_new: %d", __func__, c->self); | ||
398 | |||
399 | channel_send_open(c->self); | ||
400 | channel_register_open_confirm(c->self, | ||
401 | client_extra_session2_setup, cctx); | ||
402 | return 0; | ||
403 | } | ||
404 | |||
405 | /* ** Multiplexing client support */ | ||
406 | |||
407 | /* Exit signal handler */ | ||
408 | static void | ||
409 | control_client_sighandler(int signo) | ||
410 | { | ||
411 | muxclient_terminate = signo; | ||
412 | } | ||
413 | |||
414 | /* | ||
415 | * Relay signal handler - used to pass some signals from mux client to | ||
416 | * mux master. | ||
417 | */ | ||
418 | static void | ||
419 | control_client_sigrelay(int signo) | ||
420 | { | ||
421 | int save_errno = errno; | ||
422 | |||
423 | if (muxserver_pid > 1) | ||
424 | kill(muxserver_pid, signo); | ||
425 | |||
426 | errno = save_errno; | ||
427 | } | ||
428 | |||
429 | /* Check mux client environment variables before passing them to mux master. */ | ||
430 | static int | ||
431 | env_permitted(char *env) | ||
432 | { | ||
433 | int i, ret; | ||
434 | char name[1024], *cp; | ||
435 | |||
436 | if ((cp = strchr(env, '=')) == NULL || cp == env) | ||
437 | return (0); | ||
438 | ret = snprintf(name, sizeof(name), "%.*s", (int)(cp - env), env); | ||
439 | if (ret <= 0 || (size_t)ret >= sizeof(name)) | ||
440 | fatal("env_permitted: name '%.100s...' too long", env); | ||
441 | |||
442 | for (i = 0; i < options.num_send_env; i++) | ||
443 | if (match_pattern(name, options.send_env[i])) | ||
444 | return (1); | ||
445 | |||
446 | return (0); | ||
447 | } | ||
448 | |||
449 | /* Multiplex client main loop. */ | ||
450 | void | ||
451 | muxclient(const char *path) | ||
452 | { | ||
453 | struct sockaddr_un addr; | ||
454 | int i, r, fd, sock, exitval[2], num_env, addr_len; | ||
455 | Buffer m; | ||
456 | char *term; | ||
457 | extern char **environ; | ||
458 | u_int flags; | ||
459 | |||
460 | if (muxclient_command == 0) | ||
461 | muxclient_command = SSHMUX_COMMAND_OPEN; | ||
462 | |||
463 | switch (options.control_master) { | ||
464 | case SSHCTL_MASTER_AUTO: | ||
465 | case SSHCTL_MASTER_AUTO_ASK: | ||
466 | debug("auto-mux: Trying existing master"); | ||
467 | /* FALLTHROUGH */ | ||
468 | case SSHCTL_MASTER_NO: | ||
469 | break; | ||
470 | default: | ||
471 | return; | ||
472 | } | ||
473 | |||
474 | memset(&addr, '\0', sizeof(addr)); | ||
475 | addr.sun_family = AF_UNIX; | ||
476 | addr_len = offsetof(struct sockaddr_un, sun_path) + | ||
477 | strlen(path) + 1; | ||
478 | |||
479 | if (strlcpy(addr.sun_path, path, | ||
480 | sizeof(addr.sun_path)) >= sizeof(addr.sun_path)) | ||
481 | fatal("ControlPath too long"); | ||
482 | |||
483 | if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) | ||
484 | fatal("%s socket(): %s", __func__, strerror(errno)); | ||
485 | |||
486 | if (connect(sock, (struct sockaddr *)&addr, addr_len) == -1) { | ||
487 | if (muxclient_command != SSHMUX_COMMAND_OPEN) { | ||
488 | fatal("Control socket connect(%.100s): %s", path, | ||
489 | strerror(errno)); | ||
490 | } | ||
491 | if (errno == ENOENT) | ||
492 | debug("Control socket \"%.100s\" does not exist", path); | ||
493 | else { | ||
494 | error("Control socket connect(%.100s): %s", path, | ||
495 | strerror(errno)); | ||
496 | } | ||
497 | close(sock); | ||
498 | return; | ||
499 | } | ||
500 | |||
501 | if (stdin_null_flag) { | ||
502 | if ((fd = open(_PATH_DEVNULL, O_RDONLY)) == -1) | ||
503 | fatal("open(/dev/null): %s", strerror(errno)); | ||
504 | if (dup2(fd, STDIN_FILENO) == -1) | ||
505 | fatal("dup2: %s", strerror(errno)); | ||
506 | if (fd > STDERR_FILENO) | ||
507 | close(fd); | ||
508 | } | ||
509 | |||
510 | term = getenv("TERM"); | ||
511 | |||
512 | flags = 0; | ||
513 | if (tty_flag) | ||
514 | flags |= SSHMUX_FLAG_TTY; | ||
515 | if (subsystem_flag) | ||
516 | flags |= SSHMUX_FLAG_SUBSYS; | ||
517 | if (options.forward_x11) | ||
518 | flags |= SSHMUX_FLAG_X11_FWD; | ||
519 | if (options.forward_agent) | ||
520 | flags |= SSHMUX_FLAG_AGENT_FWD; | ||
521 | |||
522 | signal(SIGPIPE, SIG_IGN); | ||
523 | |||
524 | buffer_init(&m); | ||
525 | |||
526 | /* Send our command to server */ | ||
527 | buffer_put_int(&m, muxclient_command); | ||
528 | buffer_put_int(&m, flags); | ||
529 | if (ssh_msg_send(sock, SSHMUX_VER, &m) == -1) | ||
530 | fatal("%s: msg_send", __func__); | ||
531 | buffer_clear(&m); | ||
532 | |||
533 | /* Get authorisation status and PID of controlee */ | ||
534 | if (ssh_msg_recv(sock, &m) == -1) | ||
535 | fatal("%s: msg_recv", __func__); | ||
536 | if (buffer_get_char(&m) != SSHMUX_VER) | ||
537 | fatal("%s: wrong version", __func__); | ||
538 | if (buffer_get_int(&m) != 1) | ||
539 | fatal("Connection to master denied"); | ||
540 | muxserver_pid = buffer_get_int(&m); | ||
541 | |||
542 | buffer_clear(&m); | ||
543 | |||
544 | switch (muxclient_command) { | ||
545 | case SSHMUX_COMMAND_ALIVE_CHECK: | ||
546 | fprintf(stderr, "Master running (pid=%d)\r\n", | ||
547 | muxserver_pid); | ||
548 | exit(0); | ||
549 | case SSHMUX_COMMAND_TERMINATE: | ||
550 | fprintf(stderr, "Exit request sent.\r\n"); | ||
551 | exit(0); | ||
552 | case SSHMUX_COMMAND_OPEN: | ||
553 | /* continue below */ | ||
554 | break; | ||
555 | default: | ||
556 | fatal("silly muxclient_command %d", muxclient_command); | ||
557 | } | ||
558 | |||
559 | /* SSHMUX_COMMAND_OPEN */ | ||
560 | buffer_put_cstring(&m, term ? term : ""); | ||
561 | buffer_append(&command, "\0", 1); | ||
562 | buffer_put_cstring(&m, buffer_ptr(&command)); | ||
563 | |||
564 | if (options.num_send_env == 0 || environ == NULL) { | ||
565 | buffer_put_int(&m, 0); | ||
566 | } else { | ||
567 | /* Pass environment */ | ||
568 | num_env = 0; | ||
569 | for (i = 0; environ[i] != NULL; i++) | ||
570 | if (env_permitted(environ[i])) | ||
571 | num_env++; /* Count */ | ||
572 | |||
573 | buffer_put_int(&m, num_env); | ||
574 | |||
575 | for (i = 0; environ[i] != NULL && num_env >= 0; i++) | ||
576 | if (env_permitted(environ[i])) { | ||
577 | num_env--; | ||
578 | buffer_put_cstring(&m, environ[i]); | ||
579 | } | ||
580 | } | ||
581 | |||
582 | if (ssh_msg_send(sock, SSHMUX_VER, &m) == -1) | ||
583 | fatal("%s: msg_send", __func__); | ||
584 | |||
585 | if (mm_send_fd(sock, STDIN_FILENO) == -1 || | ||
586 | mm_send_fd(sock, STDOUT_FILENO) == -1 || | ||
587 | mm_send_fd(sock, STDERR_FILENO) == -1) | ||
588 | fatal("%s: send fds failed", __func__); | ||
589 | |||
590 | /* Wait for reply, so master has a chance to gather ttymodes */ | ||
591 | buffer_clear(&m); | ||
592 | if (ssh_msg_recv(sock, &m) == -1) | ||
593 | fatal("%s: msg_recv", __func__); | ||
594 | if (buffer_get_char(&m) != SSHMUX_VER) | ||
595 | fatal("%s: wrong version", __func__); | ||
596 | buffer_free(&m); | ||
597 | |||
598 | signal(SIGHUP, control_client_sighandler); | ||
599 | signal(SIGINT, control_client_sighandler); | ||
600 | signal(SIGTERM, control_client_sighandler); | ||
601 | signal(SIGWINCH, control_client_sigrelay); | ||
602 | |||
603 | if (tty_flag) | ||
604 | enter_raw_mode(); | ||
605 | |||
606 | /* | ||
607 | * Stick around until the controlee closes the client_fd. | ||
608 | * Before it does, it is expected to write this process' exit | ||
609 | * value (one int). This process must read the value and wait for | ||
610 | * the closure of the client_fd; if this one closes early, the | ||
611 | * multiplex master will terminate early too (possibly losing data). | ||
612 | */ | ||
613 | exitval[0] = 0; | ||
614 | for (i = 0; !muxclient_terminate && i < (int)sizeof(exitval);) { | ||
615 | r = read(sock, (char *)exitval + i, sizeof(exitval) - i); | ||
616 | if (r == 0) { | ||
617 | debug2("Received EOF from master"); | ||
618 | break; | ||
619 | } | ||
620 | if (r == -1) { | ||
621 | if (errno == EINTR) | ||
622 | continue; | ||
623 | fatal("%s: read %s", __func__, strerror(errno)); | ||
624 | } | ||
625 | i += r; | ||
626 | } | ||
627 | |||
628 | close(sock); | ||
629 | leave_raw_mode(); | ||
630 | if (i > (int)sizeof(int)) | ||
631 | fatal("%s: master returned too much data (%d > %lu)", | ||
632 | __func__, i, sizeof(int)); | ||
633 | if (muxclient_terminate) { | ||
634 | debug2("Exiting on signal %d", muxclient_terminate); | ||
635 | exitval[0] = 255; | ||
636 | } else if (i < (int)sizeof(int)) { | ||
637 | debug2("Control master terminated unexpectedly"); | ||
638 | exitval[0] = 255; | ||
639 | } else | ||
640 | debug2("Received exit status from master %d", exitval[0]); | ||
641 | |||
642 | if (tty_flag && options.log_level != SYSLOG_LEVEL_QUIET) | ||
643 | fprintf(stderr, "Shared connection to %s closed.\r\n", host); | ||
644 | |||
645 | exit(exitval[0]); | ||
646 | } | ||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssh.c,v 1.311 2008/05/08 13:06:11 djm Exp $ */ | 1 | /* $OpenBSD: ssh.c,v 1.312 2008/05/09 14:18:44 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> |
@@ -99,7 +98,6 @@ | |||
99 | #include "sshpty.h" | 98 | #include "sshpty.h" |
100 | #include "match.h" | 99 | #include "match.h" |
101 | #include "msg.h" | 100 | #include "msg.h" |
102 | #include "monitor_fdpass.h" | ||
103 | #include "uidswap.h" | 101 | #include "uidswap.h" |
104 | #include "version.h" | 102 | #include "version.h" |
105 | 103 | ||
@@ -171,15 +169,9 @@ static int client_global_request_id = 0; | |||
171 | /* pid of proxycommand child process */ | 169 | /* pid of proxycommand child process */ |
172 | pid_t proxy_command_pid = 0; | 170 | pid_t proxy_command_pid = 0; |
173 | 171 | ||
174 | /* fd to control socket */ | 172 | /* mux.c */ |
175 | int control_fd = -1; | 173 | extern int muxserver_sock; |
176 | 174 | extern u_int muxclient_command; | |
177 | /* Multiplexing control command */ | ||
178 | static u_int mux_command = 0; | ||
179 | |||
180 | /* Only used in control client mode */ | ||
181 | volatile sig_atomic_t control_client_terminate = 0; | ||
182 | u_int control_server_pid = 0; | ||
183 | 175 | ||
184 | /* Prints a help message to the user. This function never returns. */ | 176 | /* Prints a help message to the user. This function never returns. */ |
185 | 177 | ||
@@ -200,7 +192,10 @@ usage(void) | |||
200 | static int ssh_session(void); | 192 | static int ssh_session(void); |
201 | static int ssh_session2(void); | 193 | static int ssh_session2(void); |
202 | static void load_public_identity_files(void); | 194 | static void load_public_identity_files(void); |
203 | static void control_client(const char *path); | 195 | |
196 | /* from muxclient.c */ | ||
197 | void muxclient(const char *); | ||
198 | void muxserver_listen(void); | ||
204 | 199 | ||
205 | /* | 200 | /* |
206 | * Main program for the ssh client. | 201 | * Main program for the ssh client. |
@@ -310,9 +305,9 @@ main(int ac, char **av) | |||
310 | break; | 305 | break; |
311 | case 'O': | 306 | case 'O': |
312 | if (strcmp(optarg, "check") == 0) | 307 | if (strcmp(optarg, "check") == 0) |
313 | mux_command = SSHMUX_COMMAND_ALIVE_CHECK; | 308 | muxclient_command = SSHMUX_COMMAND_ALIVE_CHECK; |
314 | else if (strcmp(optarg, "exit") == 0) | 309 | else if (strcmp(optarg, "exit") == 0) |
315 | mux_command = SSHMUX_COMMAND_TERMINATE; | 310 | muxclient_command = SSHMUX_COMMAND_TERMINATE; |
316 | else | 311 | else |
317 | fatal("Invalid multiplex command."); | 312 | fatal("Invalid multiplex command."); |
318 | break; | 313 | break; |
@@ -683,10 +678,10 @@ main(int ac, char **av) | |||
683 | "r", options.user, "l", thishost, (char *)NULL); | 678 | "r", options.user, "l", thishost, (char *)NULL); |
684 | xfree(cp); | 679 | xfree(cp); |
685 | } | 680 | } |
686 | if (mux_command != 0 && options.control_path == NULL) | 681 | if (muxclient_command != 0 && options.control_path == NULL) |
687 | fatal("No ControlPath specified for \"-O\" command"); | 682 | fatal("No ControlPath specified for \"-O\" command"); |
688 | if (options.control_path != NULL) | 683 | if (options.control_path != NULL) |
689 | control_client(options.control_path); | 684 | muxclient(options.control_path); |
690 | 685 | ||
691 | timeout_ms = options.connection_timeout * 1000; | 686 | timeout_ms = options.connection_timeout * 1000; |
692 | 687 | ||
@@ -809,7 +804,7 @@ main(int ac, char **av) | |||
809 | exit_status = compat20 ? ssh_session2() : ssh_session(); | 804 | exit_status = compat20 ? ssh_session2() : ssh_session(); |
810 | packet_close(); | 805 | packet_close(); |
811 | 806 | ||
812 | if (options.control_path != NULL && control_fd != -1) | 807 | if (options.control_path != NULL && muxserver_sock != -1) |
813 | unlink(options.control_path); | 808 | unlink(options.control_path); |
814 | 809 | ||
815 | /* | 810 | /* |
@@ -1064,48 +1059,6 @@ client_global_request_reply_fwd(int type, u_int32_t seq, void *ctxt) | |||
1064 | } | 1059 | } |
1065 | } | 1060 | } |
1066 | 1061 | ||
1067 | static void | ||
1068 | ssh_control_listener(void) | ||
1069 | { | ||
1070 | struct sockaddr_un addr; | ||
1071 | mode_t old_umask; | ||
1072 | int addr_len; | ||
1073 | |||
1074 | if (options.control_path == NULL || | ||
1075 | options.control_master == SSHCTL_MASTER_NO) | ||
1076 | return; | ||
1077 | |||
1078 | debug("setting up multiplex master socket"); | ||
1079 | |||
1080 | memset(&addr, '\0', sizeof(addr)); | ||
1081 | addr.sun_family = AF_UNIX; | ||
1082 | addr_len = offsetof(struct sockaddr_un, sun_path) + | ||
1083 | strlen(options.control_path) + 1; | ||
1084 | |||
1085 | if (strlcpy(addr.sun_path, options.control_path, | ||
1086 | sizeof(addr.sun_path)) >= sizeof(addr.sun_path)) | ||
1087 | fatal("ControlPath too long"); | ||
1088 | |||
1089 | if ((control_fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) | ||
1090 | fatal("%s socket(): %s", __func__, strerror(errno)); | ||
1091 | |||
1092 | old_umask = umask(0177); | ||
1093 | if (bind(control_fd, (struct sockaddr *)&addr, addr_len) == -1) { | ||
1094 | control_fd = -1; | ||
1095 | if (errno == EINVAL || errno == EADDRINUSE) | ||
1096 | fatal("ControlSocket %s already exists", | ||
1097 | options.control_path); | ||
1098 | else | ||
1099 | fatal("%s bind(): %s", __func__, strerror(errno)); | ||
1100 | } | ||
1101 | umask(old_umask); | ||
1102 | |||
1103 | if (listen(control_fd, 64) == -1) | ||
1104 | fatal("%s listen(): %s", __func__, strerror(errno)); | ||
1105 | |||
1106 | set_nonblock(control_fd); | ||
1107 | } | ||
1108 | |||
1109 | /* request pty/x11/agent/tcpfwd/shell for channel */ | 1062 | /* request pty/x11/agent/tcpfwd/shell for channel */ |
1110 | static void | 1063 | static void |
1111 | ssh_session2_setup(int id, void *arg) | 1064 | ssh_session2_setup(int id, void *arg) |
@@ -1204,7 +1157,7 @@ ssh_session2(void) | |||
1204 | ssh_local_cmd(options.local_command); | 1157 | ssh_local_cmd(options.local_command); |
1205 | 1158 | ||
1206 | /* Start listening for multiplex clients */ | 1159 | /* Start listening for multiplex clients */ |
1207 | ssh_control_listener(); | 1160 | muxserver_listen(); |
1208 | 1161 | ||
1209 | /* If requested, let ssh continue in the background. */ | 1162 | /* If requested, let ssh continue in the background. */ |
1210 | if (fork_after_authentication_flag) | 1163 | if (fork_after_authentication_flag) |
@@ -1272,237 +1225,3 @@ load_public_identity_files(void) | |||
1272 | bzero(pwdir, strlen(pwdir)); | 1225 | bzero(pwdir, strlen(pwdir)); |
1273 | xfree(pwdir); | 1226 | xfree(pwdir); |
1274 | } | 1227 | } |
1275 | |||
1276 | static void | ||
1277 | control_client_sighandler(int signo) | ||
1278 | { | ||
1279 | control_client_terminate = signo; | ||
1280 | } | ||
1281 | |||
1282 | static void | ||
1283 | control_client_sigrelay(int signo) | ||
1284 | { | ||
1285 | int save_errno = errno; | ||
1286 | |||
1287 | if (control_server_pid > 1) | ||
1288 | kill(control_server_pid, signo); | ||
1289 | |||
1290 | errno = save_errno; | ||
1291 | } | ||
1292 | |||
1293 | static int | ||
1294 | env_permitted(char *env) | ||
1295 | { | ||
1296 | int i, ret; | ||
1297 | char name[1024], *cp; | ||
1298 | |||
1299 | if ((cp = strchr(env, '=')) == NULL || cp == env) | ||
1300 | return (0); | ||
1301 | ret = snprintf(name, sizeof(name), "%.*s", (int)(cp - env), env); | ||
1302 | if (ret <= 0 || (size_t)ret >= sizeof(name)) | ||
1303 | fatal("env_permitted: name '%.100s...' too long", env); | ||
1304 | |||
1305 | for (i = 0; i < options.num_send_env; i++) | ||
1306 | if (match_pattern(name, options.send_env[i])) | ||
1307 | return (1); | ||
1308 | |||
1309 | return (0); | ||
1310 | } | ||
1311 | |||
1312 | static void | ||
1313 | control_client(const char *path) | ||
1314 | { | ||
1315 | struct sockaddr_un addr; | ||
1316 | int i, r, fd, sock, exitval[2], num_env, addr_len; | ||
1317 | Buffer m; | ||
1318 | char *term; | ||
1319 | extern char **environ; | ||
1320 | u_int flags; | ||
1321 | |||
1322 | if (mux_command == 0) | ||
1323 | mux_command = SSHMUX_COMMAND_OPEN; | ||
1324 | |||
1325 | switch (options.control_master) { | ||
1326 | case SSHCTL_MASTER_AUTO: | ||
1327 | case SSHCTL_MASTER_AUTO_ASK: | ||
1328 | debug("auto-mux: Trying existing master"); | ||
1329 | /* FALLTHROUGH */ | ||
1330 | case SSHCTL_MASTER_NO: | ||
1331 | break; | ||
1332 | default: | ||
1333 | return; | ||
1334 | } | ||
1335 | |||
1336 | memset(&addr, '\0', sizeof(addr)); | ||
1337 | addr.sun_family = AF_UNIX; | ||
1338 | addr_len = offsetof(struct sockaddr_un, sun_path) + | ||
1339 | strlen(path) + 1; | ||
1340 | |||
1341 | if (strlcpy(addr.sun_path, path, | ||
1342 | sizeof(addr.sun_path)) >= sizeof(addr.sun_path)) | ||
1343 | fatal("ControlPath too long"); | ||
1344 | |||
1345 | if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) | ||
1346 | fatal("%s socket(): %s", __func__, strerror(errno)); | ||
1347 | |||
1348 | if (connect(sock, (struct sockaddr *)&addr, addr_len) == -1) { | ||
1349 | if (mux_command != SSHMUX_COMMAND_OPEN) { | ||
1350 | fatal("Control socket connect(%.100s): %s", path, | ||
1351 | strerror(errno)); | ||
1352 | } | ||
1353 | if (errno == ENOENT) | ||
1354 | debug("Control socket \"%.100s\" does not exist", path); | ||
1355 | else { | ||
1356 | error("Control socket connect(%.100s): %s", path, | ||
1357 | strerror(errno)); | ||
1358 | } | ||
1359 | close(sock); | ||
1360 | return; | ||
1361 | } | ||
1362 | |||
1363 | if (stdin_null_flag) { | ||
1364 | if ((fd = open(_PATH_DEVNULL, O_RDONLY)) == -1) | ||
1365 | fatal("open(/dev/null): %s", strerror(errno)); | ||
1366 | if (dup2(fd, STDIN_FILENO) == -1) | ||
1367 | fatal("dup2: %s", strerror(errno)); | ||
1368 | if (fd > STDERR_FILENO) | ||
1369 | close(fd); | ||
1370 | } | ||
1371 | |||
1372 | term = getenv("TERM"); | ||
1373 | |||
1374 | flags = 0; | ||
1375 | if (tty_flag) | ||
1376 | flags |= SSHMUX_FLAG_TTY; | ||
1377 | if (subsystem_flag) | ||
1378 | flags |= SSHMUX_FLAG_SUBSYS; | ||
1379 | if (options.forward_x11) | ||
1380 | flags |= SSHMUX_FLAG_X11_FWD; | ||
1381 | if (options.forward_agent) | ||
1382 | flags |= SSHMUX_FLAG_AGENT_FWD; | ||
1383 | |||
1384 | signal(SIGPIPE, SIG_IGN); | ||
1385 | |||
1386 | buffer_init(&m); | ||
1387 | |||
1388 | /* Send our command to server */ | ||
1389 | buffer_put_int(&m, mux_command); | ||
1390 | buffer_put_int(&m, flags); | ||
1391 | if (ssh_msg_send(sock, SSHMUX_VER, &m) == -1) | ||
1392 | fatal("%s: msg_send", __func__); | ||
1393 | buffer_clear(&m); | ||
1394 | |||
1395 | /* Get authorisation status and PID of controlee */ | ||
1396 | if (ssh_msg_recv(sock, &m) == -1) | ||
1397 | fatal("%s: msg_recv", __func__); | ||
1398 | if (buffer_get_char(&m) != SSHMUX_VER) | ||
1399 | fatal("%s: wrong version", __func__); | ||
1400 | if (buffer_get_int(&m) != 1) | ||
1401 | fatal("Connection to master denied"); | ||
1402 | control_server_pid = buffer_get_int(&m); | ||
1403 | |||
1404 | buffer_clear(&m); | ||
1405 | |||
1406 | switch (mux_command) { | ||
1407 | case SSHMUX_COMMAND_ALIVE_CHECK: | ||
1408 | fprintf(stderr, "Master running (pid=%d)\r\n", | ||
1409 | control_server_pid); | ||
1410 | exit(0); | ||
1411 | case SSHMUX_COMMAND_TERMINATE: | ||
1412 | fprintf(stderr, "Exit request sent.\r\n"); | ||
1413 | exit(0); | ||
1414 | case SSHMUX_COMMAND_OPEN: | ||
1415 | /* continue below */ | ||
1416 | break; | ||
1417 | default: | ||
1418 | fatal("silly mux_command %d", mux_command); | ||
1419 | } | ||
1420 | |||
1421 | /* SSHMUX_COMMAND_OPEN */ | ||
1422 | buffer_put_cstring(&m, term ? term : ""); | ||
1423 | buffer_append(&command, "\0", 1); | ||
1424 | buffer_put_cstring(&m, buffer_ptr(&command)); | ||
1425 | |||
1426 | if (options.num_send_env == 0 || environ == NULL) { | ||
1427 | buffer_put_int(&m, 0); | ||
1428 | } else { | ||
1429 | /* Pass environment */ | ||
1430 | num_env = 0; | ||
1431 | for (i = 0; environ[i] != NULL; i++) | ||
1432 | if (env_permitted(environ[i])) | ||
1433 | num_env++; /* Count */ | ||
1434 | |||
1435 | buffer_put_int(&m, num_env); | ||
1436 | |||
1437 | for (i = 0; environ[i] != NULL && num_env >= 0; i++) | ||
1438 | if (env_permitted(environ[i])) { | ||
1439 | num_env--; | ||
1440 | buffer_put_cstring(&m, environ[i]); | ||
1441 | } | ||
1442 | } | ||
1443 | |||
1444 | if (ssh_msg_send(sock, SSHMUX_VER, &m) == -1) | ||
1445 | fatal("%s: msg_send", __func__); | ||
1446 | |||
1447 | if (mm_send_fd(sock, STDIN_FILENO) == -1 || | ||
1448 | mm_send_fd(sock, STDOUT_FILENO) == -1 || | ||
1449 | mm_send_fd(sock, STDERR_FILENO) == -1) | ||
1450 | fatal("%s: send fds failed", __func__); | ||
1451 | |||
1452 | /* Wait for reply, so master has a chance to gather ttymodes */ | ||
1453 | buffer_clear(&m); | ||
1454 | if (ssh_msg_recv(sock, &m) == -1) | ||
1455 | fatal("%s: msg_recv", __func__); | ||
1456 | if (buffer_get_char(&m) != SSHMUX_VER) | ||
1457 | fatal("%s: wrong version", __func__); | ||
1458 | buffer_free(&m); | ||
1459 | |||
1460 | signal(SIGHUP, control_client_sighandler); | ||
1461 | signal(SIGINT, control_client_sighandler); | ||
1462 | signal(SIGTERM, control_client_sighandler); | ||
1463 | signal(SIGWINCH, control_client_sigrelay); | ||
1464 | |||
1465 | if (tty_flag) | ||
1466 | enter_raw_mode(); | ||
1467 | |||
1468 | /* | ||
1469 | * Stick around until the controlee closes the client_fd. | ||
1470 | * Before it does, it is expected to write this process' exit | ||
1471 | * value (one int). This process must read the value and wait for | ||
1472 | * the closure of the client_fd; if this one closes early, the | ||
1473 | * multiplex master will terminate early too (possibly losing data). | ||
1474 | */ | ||
1475 | exitval[0] = 0; | ||
1476 | for (i = 0; !control_client_terminate && i < (int)sizeof(exitval);) { | ||
1477 | r = read(sock, (char *)exitval + i, sizeof(exitval) - i); | ||
1478 | if (r == 0) { | ||
1479 | debug2("Received EOF from master"); | ||
1480 | break; | ||
1481 | } | ||
1482 | if (r == -1) { | ||
1483 | if (errno == EINTR) | ||
1484 | continue; | ||
1485 | fatal("%s: read %s", __func__, strerror(errno)); | ||
1486 | } | ||
1487 | i += r; | ||
1488 | } | ||
1489 | |||
1490 | close(sock); | ||
1491 | leave_raw_mode(); | ||
1492 | if (i > (int)sizeof(int)) | ||
1493 | fatal("%s: master returned too much data (%d > %lu)", | ||
1494 | __func__, i, sizeof(int)); | ||
1495 | if (control_client_terminate) { | ||
1496 | debug2("Exiting on signal %d", control_client_terminate); | ||
1497 | exitval[0] = 255; | ||
1498 | } else if (i < (int)sizeof(int)) { | ||
1499 | debug2("Control master terminated unexpectedly"); | ||
1500 | exitval[0] = 255; | ||
1501 | } else | ||
1502 | debug2("Received exit status from master %d", exitval[0]); | ||
1503 | |||
1504 | if (tty_flag && options.log_level != SYSLOG_LEVEL_QUIET) | ||
1505 | fprintf(stderr, "Shared connection to %s closed.\r\n", host); | ||
1506 | |||
1507 | exit(exitval[0]); | ||
1508 | } | ||