Connection hang, can't stop SSH

Damien Miller djm at mindrot.org
Thu Oct 15 11:31:40 AEDT 2020


On Wed, 14 Oct 2020, Philipp Marek wrote:

> Using OpenSSH_8.3p1 I had an open (working) connection to some other
> box; after a bit of inactivity, some device in the middle seems to have
> forgotten about the TCP connection (NAT) and broke it.
> 
> I've got an EscapeChar defined, though; so first I tried to send a BREAK
> and, when that didn't help (TCP already gone, packets get lost!), I
> tried (just out of curiosity) a Rekey.
> 
> Now I can see my TCP side hanging with about 4KB in the socket buffers -
> but the SSH process (that previously reacted fine upon my EscapeChar)
> now is completely dead.
> 
> - EscapeChar doesn't work
> - Ctrl-Z doesn't work
> - Ctrl-C doesn't work
> 
> 
> strace shows ssh hanging in
>     pselect6(8, [3], [], NULL, NULL, NULL...
> 
> 
> So yeah, my fault for trying a rekey on a broken connection... but I'd
> have thought that my EscapeChar stays available, so that I can easily
> get my terminal back!
> (Yeah, I can just kill ssh from another. But what if it was my last
> terminal after breaking the sshd setup?!?!)
> 
> 
> Is it easy to get EscapeChar working all of the time?

no, not easy unfortunately. While key exchange is in progress, the input
channels are not polled. Fixing this would involve some fiddly plumbing
as the IO handling is fairly intimately tied to the channel state
machine, and the channel state machine drives all sorts of events that
will try to send packets to the server, and we cannot emit packets while
in rekeying.

Attached is a stab at it, completely untested :)

-d
-------------- next part --------------
commit d3bbb19bda45df77a8a3e2a90f2511f254d35be2
Author: Damien Miller <djm at mindrot.org>
Date:   Thu Oct 15 11:30:38 2020 +1100

    continue processing input during rekeying

diff --git a/channels.c b/channels.c
index b678f97..9dce034 100644
--- a/channels.c
+++ b/channels.c
@@ -1063,6 +1063,8 @@ static void
 channel_pre_listener(struct ssh *ssh, Channel *c,
     fd_set *readset, fd_set *writeset)
 {
+	if (ssh_packet_is_rekeying(ssh))
+		return;
 	FD_SET(c->sock, readset);
 }
 
@@ -1070,6 +1072,8 @@ static void
 channel_pre_connecting(struct ssh *ssh, Channel *c,
     fd_set *readset, fd_set *writeset)
 {
+	if (ssh_packet_is_rekeying(ssh))
+		return;
 	debug3("channel %d: waiting for connection", c->self);
 	FD_SET(c->sock, writeset);
 }
@@ -1083,6 +1087,8 @@ channel_pre_open(struct ssh *ssh, Channel *c,
 	    sshbuf_len(c->input) < c->remote_window &&
 	    sshbuf_check_reserve(c->input, CHAN_RBUF) == 0)
 		FD_SET(c->rfd, readset);
+	if (ssh_packet_is_rekeying(ssh))
+		return;
 	if (c->ostate == CHAN_OUTPUT_OPEN ||
 	    c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
 		if (sshbuf_len(c->output) > 0) {
@@ -1191,14 +1197,16 @@ static void
 channel_pre_x11_open(struct ssh *ssh, Channel *c,
     fd_set *readset, fd_set *writeset)
 {
-	int ret = x11_open_helper(ssh, c->output);
-
 	/* c->force_drain = 1; */
 
-	if (ret == 1) {
+	if (ssh_packet_is_rekeying(ssh))
+		return;
+	switch (x11_open_helper(ssh, c->output)) {
+	case 1:
 		c->type = SSH_CHANNEL_OPEN;
 		channel_pre_open(ssh, c, readset, writeset);
-	} else if (ret == -1) {
+		break;
+	case -1:
 		logit("X11 connection rejected because of wrong authentication.");
 		debug2("X11 rejected %d i%d/o%d",
 		    c->self, c->istate, c->ostate);
@@ -1215,6 +1223,8 @@ static void
 channel_pre_mux_client(struct ssh *ssh,
     Channel *c, fd_set *readset, fd_set *writeset)
 {
+	if (ssh_packet_is_rekeying(ssh))
+		return;
 	if (c->istate == CHAN_INPUT_OPEN && !c->mux_pause &&
 	    sshbuf_check_reserve(c->input, CHAN_RBUF) == 0)
 		FD_SET(c->rfd, readset);
@@ -1529,6 +1539,8 @@ channel_pre_dynamic(struct ssh *ssh, Channel *c,
 	u_int have;
 	int ret;
 
+	if (ssh_packet_is_rekeying(ssh))
+		return;
 	have = sshbuf_len(c->input);
 	debug2("channel %d: pre_dynamic: have %d", c->self, have);
 	/* sshbuf_dump(c->input, stderr); */
@@ -1653,6 +1665,8 @@ channel_post_x11_listener(struct ssh *ssh, Channel *c,
 	socklen_t addrlen;
 	char buf[16384], *remote_ipaddr;
 
+	if (ssh_packet_is_rekeying(ssh))
+		return;
 	if (!FD_ISSET(c->sock, readset))
 		return;
 
@@ -1784,6 +1798,8 @@ channel_post_port_listener(struct ssh *ssh, Channel *c,
 	socklen_t addrlen;
 	char *rtype;
 
+	if (ssh_packet_is_rekeying(ssh))
+		return;
 	if (!FD_ISSET(c->sock, readset))
 		return;
 
@@ -1843,6 +1859,8 @@ channel_post_auth_listener(struct ssh *ssh, Channel *c,
 	struct sockaddr_storage addr;
 	socklen_t addrlen;
 
+	if (ssh_packet_is_rekeying(ssh))
+		return;
 	if (!FD_ISSET(c->sock, readset))
 		return;
 
@@ -1870,6 +1888,8 @@ channel_post_connecting(struct ssh *ssh, Channel *c,
 	int err = 0, sock, isopen, r;
 	socklen_t sz = sizeof(err);
 
+	if (ssh_packet_is_rekeying(ssh))
+		return;
 	if (!FD_ISSET(c->sock, writeset))
 		return;
 	if (!c->have_remote_id)
@@ -2175,9 +2195,11 @@ channel_post_open(struct ssh *ssh, Channel *c,
     fd_set *readset, fd_set *writeset)
 {
 	channel_handle_rfd(ssh, c, readset, writeset);
-	channel_handle_wfd(ssh, c, readset, writeset);
 	channel_handle_efd(ssh, c, readset, writeset);
-	channel_check_window(ssh, c);
+	if (!ssh_packet_is_rekeying(ssh)) {
+		channel_handle_wfd(ssh, c, readset, writeset);
+		channel_check_window(ssh, c);
+	}
 }
 
 static u_int
@@ -2270,6 +2292,8 @@ static void
 channel_post_mux_client(struct ssh *ssh, Channel *c,
     fd_set *readset, fd_set *writeset)
 {
+	if (ssh_packet_is_rekeying(ssh))
+		return;
 	channel_post_mux_client_read(ssh, c, readset, writeset);
 	channel_post_mux_client_write(ssh, c, readset, writeset);
 }
@@ -2285,6 +2309,8 @@ channel_post_mux_listener(struct ssh *ssh, Channel *c,
 	uid_t euid;
 	gid_t egid;
 
+	if (ssh_packet_is_rekeying(ssh))
+		return;
 	if (!FD_ISSET(c->sock, readset))
 		return;
 
@@ -2491,9 +2517,7 @@ channel_prepare_select(struct ssh *ssh, fd_set **readsetp, fd_set **writesetp,
 	memset(*readsetp, 0, sz);
 	memset(*writesetp, 0, sz);
 
-	if (!ssh_packet_is_rekeying(ssh))
-		channel_handler(ssh, CHAN_PRE, *readsetp, *writesetp,
-		    minwait_secs);
+	channel_handler(ssh, CHAN_PRE, *readsetp, *writesetp, minwait_secs);
 }
 
 /*
diff --git a/clientloop.c b/clientloop.c
index fa240b1..9bb3b4e 100644
--- a/clientloop.c
+++ b/clientloop.c
@@ -1360,8 +1360,7 @@ client_loop(struct ssh *ssh, int have_pty, int escape_char_arg,
 			break;
 
 		/* Do channel operations unless rekeying in progress. */
-		if (!ssh_packet_is_rekeying(ssh))
-			channel_after_select(ssh, readset, writeset);
+		channel_after_select(ssh, readset, writeset);
 
 		/* Buffer input from the connection.  */
 		client_process_net_input(ssh, readset);


More information about the openssh-unix-dev mailing list