diff options
Diffstat (limited to 'mux.c')
-rw-r--r-- | mux.c | 50 |
1 files changed, 46 insertions, 4 deletions
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: mux.c,v 1.21 2010/06/25 23:15:36 djm Exp $ */ | 1 | /* $OpenBSD: mux.c,v 1.22 2010/09/20 07:19:27 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2002-2008 Damien Miller <djm@openbsd.org> | 3 | * Copyright (c) 2002-2008 Damien Miller <djm@openbsd.org> |
4 | * | 4 | * |
@@ -1026,6 +1026,9 @@ muxserver_listen(void) | |||
1026 | struct sockaddr_un addr; | 1026 | struct sockaddr_un addr; |
1027 | socklen_t sun_len; | 1027 | socklen_t sun_len; |
1028 | mode_t old_umask; | 1028 | mode_t old_umask; |
1029 | char *orig_control_path = options.control_path; | ||
1030 | char rbuf[16+1]; | ||
1031 | u_int i, r; | ||
1029 | 1032 | ||
1030 | if (options.control_path == NULL || | 1033 | if (options.control_path == NULL || |
1031 | options.control_master == SSHCTL_MASTER_NO) | 1034 | options.control_master == SSHCTL_MASTER_NO) |
@@ -1033,6 +1036,23 @@ muxserver_listen(void) | |||
1033 | 1036 | ||
1034 | debug("setting up multiplex master socket"); | 1037 | debug("setting up multiplex master socket"); |
1035 | 1038 | ||
1039 | /* | ||
1040 | * Use a temporary path before listen so we can pseudo-atomically | ||
1041 | * establish the listening socket in its final location to avoid | ||
1042 | * other processes racing in between bind() and listen() and hitting | ||
1043 | * an unready socket. | ||
1044 | */ | ||
1045 | for (i = 0; i < sizeof(rbuf) - 1; i++) { | ||
1046 | r = arc4random_uniform(26+26+10); | ||
1047 | rbuf[i] = (r < 26) ? 'a' + r : | ||
1048 | (r < 26*2) ? 'A' + r - 26 : | ||
1049 | '0' + r - 26 - 26; | ||
1050 | } | ||
1051 | rbuf[sizeof(rbuf) - 1] = '\0'; | ||
1052 | options.control_path = NULL; | ||
1053 | xasprintf(&options.control_path, "%s.%s", orig_control_path, rbuf); | ||
1054 | debug3("%s: temporary control path %s", __func__, options.control_path); | ||
1055 | |||
1036 | memset(&addr, '\0', sizeof(addr)); | 1056 | memset(&addr, '\0', sizeof(addr)); |
1037 | addr.sun_family = AF_UNIX; | 1057 | addr.sun_family = AF_UNIX; |
1038 | sun_len = offsetof(struct sockaddr_un, sun_path) + | 1058 | sun_len = offsetof(struct sockaddr_un, sun_path) + |
@@ -1051,6 +1071,7 @@ muxserver_listen(void) | |||
1051 | if (errno == EINVAL || errno == EADDRINUSE) { | 1071 | if (errno == EINVAL || errno == EADDRINUSE) { |
1052 | error("ControlSocket %s already exists, " | 1072 | error("ControlSocket %s already exists, " |
1053 | "disabling multiplexing", options.control_path); | 1073 | "disabling multiplexing", options.control_path); |
1074 | disable_mux_master: | ||
1054 | close(muxserver_sock); | 1075 | close(muxserver_sock); |
1055 | muxserver_sock = -1; | 1076 | muxserver_sock = -1; |
1056 | xfree(options.control_path); | 1077 | xfree(options.control_path); |
@@ -1065,12 +1086,29 @@ muxserver_listen(void) | |||
1065 | if (listen(muxserver_sock, 64) == -1) | 1086 | if (listen(muxserver_sock, 64) == -1) |
1066 | fatal("%s listen(): %s", __func__, strerror(errno)); | 1087 | fatal("%s listen(): %s", __func__, strerror(errno)); |
1067 | 1088 | ||
1089 | /* Now atomically "move" the mux socket into position */ | ||
1090 | if (link(options.control_path, orig_control_path) != 0) { | ||
1091 | if (errno != EEXIST) { | ||
1092 | fatal("%s: link mux listener %s => %s: %s", __func__, | ||
1093 | options.control_path, orig_control_path, | ||
1094 | strerror(errno)); | ||
1095 | } | ||
1096 | error("ControlSocket %s already exists, disabling multiplexing", | ||
1097 | orig_control_path); | ||
1098 | xfree(orig_control_path); | ||
1099 | unlink(options.control_path); | ||
1100 | goto disable_mux_master; | ||
1101 | } | ||
1102 | unlink(options.control_path); | ||
1103 | xfree(options.control_path); | ||
1104 | options.control_path = orig_control_path; | ||
1105 | |||
1068 | set_nonblock(muxserver_sock); | 1106 | set_nonblock(muxserver_sock); |
1069 | 1107 | ||
1070 | mux_listener_channel = channel_new("mux listener", | 1108 | mux_listener_channel = channel_new("mux listener", |
1071 | SSH_CHANNEL_MUX_LISTENER, muxserver_sock, muxserver_sock, -1, | 1109 | SSH_CHANNEL_MUX_LISTENER, muxserver_sock, muxserver_sock, -1, |
1072 | CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, | 1110 | CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, |
1073 | 0, addr.sun_path, 1); | 1111 | 0, options.control_path, 1); |
1074 | mux_listener_channel->mux_rcb = mux_master_read_cb; | 1112 | mux_listener_channel->mux_rcb = mux_master_read_cb; |
1075 | debug3("%s: mux listener channel %d fd %d", __func__, | 1113 | debug3("%s: mux listener channel %d fd %d", __func__, |
1076 | mux_listener_channel->self, mux_listener_channel->sock); | 1114 | mux_listener_channel->self, mux_listener_channel->sock); |
@@ -1823,9 +1861,13 @@ muxclient(const char *path) | |||
1823 | fatal("Control socket connect(%.100s): %s", path, | 1861 | fatal("Control socket connect(%.100s): %s", path, |
1824 | strerror(errno)); | 1862 | strerror(errno)); |
1825 | } | 1863 | } |
1826 | if (errno == ENOENT) | 1864 | if (errno == ECONNREFUSED && |
1865 | options.control_master != SSHCTL_MASTER_NO) { | ||
1866 | debug("Stale control socket %.100s, unlinking", path); | ||
1867 | unlink(path); | ||
1868 | } else if (errno == ENOENT) { | ||
1827 | debug("Control socket \"%.100s\" does not exist", path); | 1869 | debug("Control socket \"%.100s\" does not exist", path); |
1828 | else { | 1870 | } else { |
1829 | error("Control socket connect(%.100s): %s", path, | 1871 | error("Control socket connect(%.100s): %s", path, |
1830 | strerror(errno)); | 1872 | strerror(errno)); |
1831 | } | 1873 | } |