diff options
author | Damien Miller <djm@mindrot.org> | 2010-09-24 22:07:55 +1000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2010-09-24 22:07:55 +1000 |
commit | 603134e077e667b4819effb0e121803842df621f (patch) | |
tree | 6a4a91c923103d9b6408ab6ff2be4773884aecbc /mux.c | |
parent | 18e1cab1a112052580bbd3f35fbaec15661d098d (diff) |
- djm@cvs.openbsd.org 2010/09/20 07:19:27
[mux.c]
"atomically" create the listening mux socket by binding it on a temorary
name and then linking it into position after listen() has succeeded.
this allows the mux clients to determine that the server socket is
either ready or stale without races. stale server sockets are now
automatically removed
ok deraadt
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 | } |