ssh: don't err out on channel request msgs to unknown channels

rfc4254 section 5.4 states that channel request messages sent to an
unrecognized channel should be replied with a `SSH_MSG_CHANNEL_FAILURE`,
rather than erring out and closing the mux. This can occur with servers
like openssh-portable, which can begin to close a channel and also use
that channel for keepalives before it has received a closed response
from the client.

Fixes golang/go#38908

Change-Id: Id68b77e16b2889d3a21d505ed8018f9f457e067a
GitHub-Last-Rev: 8a92f87dc30697d9e3805af695efdf1b1dc8409b
GitHub-Pull-Request: golang/crypto#136
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/232659
Run-TryBot: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
diff --git a/ssh/mux.go b/ssh/mux.go
index f190162..9654c01 100644
--- a/ssh/mux.go
+++ b/ssh/mux.go
@@ -240,7 +240,7 @@
 	id := binary.BigEndian.Uint32(packet[1:])
 	ch := m.chanList.getChan(id)
 	if ch == nil {
-		return fmt.Errorf("ssh: invalid channel %d", id)
+		return m.handleUnknownChannelPacket(id, packet)
 	}
 
 	return ch.handlePacket(packet)
@@ -328,3 +328,24 @@
 		return nil, fmt.Errorf("ssh: unexpected packet in response to channel open: %T", msg)
 	}
 }
+
+func (m *mux) handleUnknownChannelPacket(id uint32, packet []byte) error {
+	msg, err := decode(packet)
+	if err != nil {
+		return err
+	}
+
+	switch msg := msg.(type) {
+	// RFC 4254 section 5.4 says unrecognized channel requests should
+	// receive a failure response.
+	case *channelRequestMsg:
+		if msg.WantReply {
+			return m.sendMessage(channelRequestFailureMsg{
+				PeersID: msg.PeersID,
+			})
+		}
+		return nil
+	default:
+		return fmt.Errorf("ssh: invalid channel %d", id)
+	}
+}
diff --git a/ssh/mux_test.go b/ssh/mux_test.go
index 94596ec..0b6b74d 100644
--- a/ssh/mux_test.go
+++ b/ssh/mux_test.go
@@ -9,6 +9,7 @@
 	"io/ioutil"
 	"sync"
 	"testing"
+	"time"
 )
 
 func muxPair() (*mux, *mux) {
@@ -288,6 +289,219 @@
 	}
 }
 
+func TestMuxUnknownChannelRequests(t *testing.T) {
+	clientPipe, serverPipe := memPipe()
+	client := newMux(clientPipe)
+	defer serverPipe.Close()
+	defer client.Close()
+
+	kDone := make(chan struct{})
+	go func() {
+		// Ignore unknown channel messages that don't want a reply.
+		err := serverPipe.writePacket(Marshal(channelRequestMsg{
+			PeersID:             1,
+			Request:             "keepalive@openssh.com",
+			WantReply:           false,
+			RequestSpecificData: []byte{},
+		}))
+		if err != nil {
+			t.Fatalf("send: %v", err)
+		}
+
+		// Send a keepalive, which should get a channel failure message
+		// in response.
+		err = serverPipe.writePacket(Marshal(channelRequestMsg{
+			PeersID:             2,
+			Request:             "keepalive@openssh.com",
+			WantReply:           true,
+			RequestSpecificData: []byte{},
+		}))
+		if err != nil {
+			t.Fatalf("send: %v", err)
+		}
+
+		packet, err := serverPipe.readPacket()
+		if err != nil {
+			t.Fatalf("read packet: %v", err)
+		}
+		decoded, err := decode(packet)
+		if err != nil {
+			t.Fatalf("decode failed: %v", err)
+		}
+
+		switch msg := decoded.(type) {
+		case *channelRequestFailureMsg:
+			if msg.PeersID != 2 {
+				t.Fatalf("received response to wrong message: %v", msg)
+			}
+		default:
+			t.Fatalf("unexpected channel message: %v", msg)
+		}
+
+		kDone <- struct{}{}
+
+		// Receive and respond to the keepalive to confirm the mux is
+		// still processing requests.
+		packet, err = serverPipe.readPacket()
+		if err != nil {
+			t.Fatalf("read packet: %v", err)
+		}
+		if packet[0] != msgGlobalRequest {
+			t.Fatalf("expected global request")
+		}
+
+		err = serverPipe.writePacket(Marshal(globalRequestFailureMsg{
+			Data: []byte{},
+		}))
+		if err != nil {
+			t.Fatalf("failed to send failure msg: %v", err)
+		}
+
+		close(kDone)
+	}()
+
+	// Wait for the server to send the keepalive message and receive back a
+	// response.
+	select {
+	case <-kDone:
+	case <-time.After(10 * time.Second):
+		t.Fatalf("server never received ack")
+	}
+
+	// Confirm client hasn't closed.
+	if _, _, err := client.SendRequest("keepalive@golang.org", true, nil); err != nil {
+		t.Fatalf("failed to send keepalive: %v", err)
+	}
+
+	select {
+	case <-kDone:
+	case <-time.After(10 * time.Second):
+		t.Fatalf("server never shut down")
+	}
+}
+
+func TestMuxClosedChannel(t *testing.T) {
+	clientPipe, serverPipe := memPipe()
+	client := newMux(clientPipe)
+	defer serverPipe.Close()
+	defer client.Close()
+
+	kDone := make(chan struct{})
+	go func() {
+		// Open the channel.
+		packet, err := serverPipe.readPacket()
+		if err != nil {
+			t.Fatalf("read packet: %v", err)
+		}
+		if packet[0] != msgChannelOpen {
+			t.Fatalf("expected chan open")
+		}
+
+		var openMsg channelOpenMsg
+		if err := Unmarshal(packet, &openMsg); err != nil {
+			t.Fatalf("unmarshal: %v", err)
+		}
+
+		// Send back the opened channel confirmation.
+		err = serverPipe.writePacket(Marshal(channelOpenConfirmMsg{
+			PeersID:       openMsg.PeersID,
+			MyID:          0,
+			MyWindow:      0,
+			MaxPacketSize: channelMaxPacket,
+		}))
+		if err != nil {
+			t.Fatalf("send: %v", err)
+		}
+
+		// Close the channel.
+		err = serverPipe.writePacket(Marshal(channelCloseMsg{
+			PeersID: openMsg.PeersID,
+		}))
+		if err != nil {
+			t.Fatalf("send: %v", err)
+		}
+
+		// Send a keepalive message on the channel we just closed.
+		err = serverPipe.writePacket(Marshal(channelRequestMsg{
+			PeersID:             openMsg.PeersID,
+			Request:             "keepalive@openssh.com",
+			WantReply:           true,
+			RequestSpecificData: []byte{},
+		}))
+		if err != nil {
+			t.Fatalf("send: %v", err)
+		}
+
+		// Receive the channel closed response.
+		packet, err = serverPipe.readPacket()
+		if err != nil {
+			t.Fatalf("read packet: %v", err)
+		}
+		if packet[0] != msgChannelClose {
+			t.Fatalf("expected channel close")
+		}
+
+		// Receive the keepalive response failure.
+		packet, err = serverPipe.readPacket()
+		if err != nil {
+			t.Fatalf("read packet: %v", err)
+		}
+		if packet[0] != msgChannelFailure {
+			t.Fatalf("expected channel close")
+		}
+		kDone <- struct{}{}
+
+		// Receive and respond to the keepalive to confirm the mux is
+		// still processing requests.
+		packet, err = serverPipe.readPacket()
+		if err != nil {
+			t.Fatalf("read packet: %v", err)
+		}
+		if packet[0] != msgGlobalRequest {
+			t.Fatalf("expected global request")
+		}
+
+		err = serverPipe.writePacket(Marshal(globalRequestFailureMsg{
+			Data: []byte{},
+		}))
+		if err != nil {
+			t.Fatalf("failed to send failure msg: %v", err)
+		}
+
+		close(kDone)
+	}()
+
+	// Open a channel.
+	ch, err := client.openChannel("chan", nil)
+	if err != nil {
+		t.Fatalf("OpenChannel: %v", err)
+	}
+	defer ch.Close()
+
+	// Wait for the server to close the channel and send the keepalive.
+	select {
+	case <-kDone:
+	case <-time.After(10 * time.Second):
+		t.Fatalf("server never received ack")
+	}
+
+	// Make sure the channel closed.
+	if _, ok := <-ch.incomingRequests; ok {
+		t.Fatalf("channel not closed")
+	}
+
+	// Confirm client hasn't closed
+	if _, _, err := client.SendRequest("keepalive@golang.org", true, nil); err != nil {
+		t.Fatalf("failed to send keepalive: %v", err)
+	}
+
+	select {
+	case <-kDone:
+	case <-time.After(10 * time.Second):
+		t.Fatalf("server never shut down")
+	}
+}
+
 func TestMuxGlobalRequest(t *testing.T) {
 	clientMux, serverMux := muxPair()
 	defer serverMux.Close()