Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x/crypto/ssh: Unable to start subsystem without an exec or shell message #35025

Open
magisterquis opened this issue Oct 21, 2019 · 2 comments
Open
Labels
NeedsInvestigation
Milestone

Comments

@magisterquis
Copy link

@magisterquis magisterquis commented Oct 21, 2019

What version of Go are you using (go version)?

$ go version
go version go1.13.3 openbsd/amd64

Does this issue reproduce with the latest release?

It does.

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/stuart/.cache/go-build"
GOENV="/home/stuart/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="openbsd"
GONOPROXY=""
GONOSUMDB=""
GOOS="openbsd"
GOPATH="/home/stuart/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/home/stuart/.go/1.13.3"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/home/stuart/.go/1.13.3/pkg/tool/openbsd_amd64"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="0"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -fmessage-length=0"

What did you do?

Wrote a little server which implemented an SSH subsystem and tried to write a client to use that subsystem.
A minimal server: http://play.golang.org/p/HtAmPnyMpDl
A minimal client: http://play.golang.org/p/VqSuWARBHJT

What did you expect to see?

Output from the subsystem

What did you see instead?

That the session hadn't been started.

When starting a subsystem, a channel is opened and a subsystem request is sent with the name of the subsystem. According to RFC4254 exactly one of shell, exec, or subsystem requests can succeed per channel, though it doesn't say that only one may be sent. Currently, the subsystem request is sent with session.RequestSubsystem, but there is no way to call the non-exported session.start without calling session.Start (which sends an exec request) or session.Shell (which sends a shell request). This means that to start subsystem, the server will first receive a subsystem request and then an exec or shell request. Changing session.RequestSubsystem to call session.start (i.e. to parallel session.Shell and session.Start) causes the server to start the subsystem and delivers output to the client.

The OpenSSH client only sends a subsystem message. The OpenSSH server replies with false exec request following a subsystem request.

@gopherbot gopherbot added this to the Unreleased milestone Oct 21, 2019
@dmitshur dmitshur added the NeedsInvestigation label Oct 21, 2019
@dmitshur
Copy link
Contributor

@dmitshur dmitshur commented Oct 21, 2019

/cc @hanwen @FiloSottile per owners.

@skinowski
Copy link

@skinowski skinowski commented Jun 29, 2021

Due to this bug, we cannot obtain exit status or signal from subsystem as well. This is because we cannot Session.Wait() on the subsystem. Probably the fix is easy, add the forgotten call to Session.start() in session:

--- a/vendor/golang.org/x/crypto/ssh/session.go
+++ b/vendor/golang.org/x/crypto/ssh/session.go
@@ -221,6 +221,9 @@ type subsystemRequestMsg struct {
 // RequestSubsystem requests the association of a subsystem with the session on the remote host.
 // A subsystem is a predefined command that runs in the background when the ssh session is initiated
 func (s *Session) RequestSubsystem(subsystem string) error {
+       if s.started {
+               return errors.New("ssh: session already started")
+       }
        msg := subsystemRequestMsg{
                Subsystem: subsystem,
        }
@@ -228,7 +231,10 @@ func (s *Session) RequestSubsystem(subsystem string) error {
        if err == nil && !ok {
                err = errors.New("ssh: subsystem request failed")
        }
-       return err
+       if err != nil {
+               return err
+       }
+       return s.start()
 }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
NeedsInvestigation
Projects
None yet
Development

No branches or pull requests

4 participants