=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/mux.c,v retrieving revision 1.21 retrieving revision 1.22 diff -u -r1.21 -r1.22 --- src/usr.bin/ssh/mux.c 2010/06/25 23:15:36 1.21 +++ src/usr.bin/ssh/mux.c 2010/09/20 07:19:27 1.22 @@ -1,4 +1,4 @@ -/* $OpenBSD: mux.c,v 1.21 2010/06/25 23:15:36 djm Exp $ */ +/* $OpenBSD: mux.c,v 1.22 2010/09/20 07:19:27 djm Exp $ */ /* * Copyright (c) 2002-2008 Damien Miller * @@ -1008,6 +1008,9 @@ { struct sockaddr_un addr; mode_t old_umask; + char *orig_control_path = options.control_path; + char rbuf[16+1]; + u_int i, r; if (options.control_path == NULL || options.control_master == SSHCTL_MASTER_NO) @@ -1015,6 +1018,23 @@ debug("setting up multiplex master socket"); + /* + * Use a temporary path before listen so we can pseudo-atomically + * establish the listening socket in its final location to avoid + * other processes racing in between bind() and listen() and hitting + * an unready socket. + */ + for (i = 0; i < sizeof(rbuf) - 1; i++) { + r = arc4random_uniform(26+26+10); + rbuf[i] = (r < 26) ? 'a' + r : + (r < 26*2) ? 'A' + r - 26 : + '0' + r - 26 - 26; + } + rbuf[sizeof(rbuf) - 1] = '\0'; + options.control_path = NULL; + xasprintf(&options.control_path, "%s.%s", orig_control_path, rbuf); + debug3("%s: temporary control path %s", __func__, options.control_path); + memset(&addr, '\0', sizeof(addr)); addr.sun_family = AF_UNIX; addr.sun_len = offsetof(struct sockaddr_un, sun_path) + @@ -1033,6 +1053,7 @@ if (errno == EINVAL || errno == EADDRINUSE) { error("ControlSocket %s already exists, " "disabling multiplexing", options.control_path); + disable_mux_master: close(muxserver_sock); muxserver_sock = -1; xfree(options.control_path); @@ -1047,12 +1068,29 @@ if (listen(muxserver_sock, 64) == -1) fatal("%s listen(): %s", __func__, strerror(errno)); + /* Now atomically "move" the mux socket into position */ + if (link(options.control_path, orig_control_path) != 0) { + if (errno != EEXIST) { + fatal("%s: link mux listener %s => %s: %s", __func__, + options.control_path, orig_control_path, + strerror(errno)); + } + error("ControlSocket %s already exists, disabling multiplexing", + orig_control_path); + xfree(orig_control_path); + unlink(options.control_path); + goto disable_mux_master; + } + unlink(options.control_path); + xfree(options.control_path); + options.control_path = orig_control_path; + set_nonblock(muxserver_sock); mux_listener_channel = channel_new("mux listener", SSH_CHANNEL_MUX_LISTENER, muxserver_sock, muxserver_sock, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, - 0, addr.sun_path, 1); + 0, options.control_path, 1); mux_listener_channel->mux_rcb = mux_master_read_cb; debug3("%s: mux listener channel %d fd %d", __func__, mux_listener_channel->self, mux_listener_channel->sock); @@ -1798,9 +1836,13 @@ fatal("Control socket connect(%.100s): %s", path, strerror(errno)); } - if (errno == ENOENT) + if (errno == ECONNREFUSED && + options.control_master != SSHCTL_MASTER_NO) { + debug("Stale control socket %.100s, unlinking", path); + unlink(path); + } else if (errno == ENOENT) { debug("Control socket \"%.100s\" does not exist", path); - else { + } else { error("Control socket connect(%.100s): %s", path, strerror(errno)); }