Skip to content

Commit a462277

Browse files
committed
Add subsystem support
1 parent f5cb472 commit a462277

File tree

2 files changed

+77
-19
lines changed

2 files changed

+77
-19
lines changed

server.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ import (
1515
// and ListenAndServeTLS methods after a call to Shutdown or Close.
1616
var ErrServerClosed = errors.New("ssh: Server closed")
1717

18+
type SubsystemHandler func(s Session)
19+
20+
var DefaultSubsystemHandlers = map[string]SubsystemHandler{}
21+
1822
type RequestHandler func(ctx Context, srv *Server, req *gossh.Request) (ok bool, payload []byte)
1923

2024
var DefaultRequestHandlers = map[string]RequestHandler{}
@@ -57,6 +61,10 @@ type Server struct {
5761
// no handlers are enabled.
5862
RequestHandlers map[string]RequestHandler
5963

64+
// SubsystemHandlers are handlers which are similar to the usual SSH command
65+
// handlers, but handle named subsystems.
66+
SubsystemHandlers map[string]SubsystemHandler
67+
6068
listenerWg sync.WaitGroup
6169
mu sync.RWMutex
6270
listeners map[net.Listener]struct{}
@@ -95,6 +103,12 @@ func (srv *Server) ensureHandlers() {
95103
srv.ChannelHandlers[k] = v
96104
}
97105
}
106+
if srv.SubsystemHandlers == nil {
107+
srv.SubsystemHandlers = map[string]SubsystemHandler{}
108+
for k, v := range DefaultSubsystemHandlers {
109+
srv.SubsystemHandlers[k] = v
110+
}
111+
}
98112
}
99113

100114
func (srv *Server) config(ctx Context) *gossh.ServerConfig {

session.go

Lines changed: 63 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ type Session interface {
4747
// RawCommand returns the exact command that was provided by the user.
4848
RawCommand() string
4949

50+
// Subsystem returns the subsystem requested by the user.
51+
Subsystem() string
52+
5053
// PublicKey returns the PublicKey used to authenticate. If a public key was not
5154
// used it will return nil.
5255
PublicKey() PublicKey
@@ -87,32 +90,35 @@ func DefaultSessionHandler(srv *Server, conn *gossh.ServerConn, newChan gossh.Ne
8790
return
8891
}
8992
sess := &session{
90-
Channel: ch,
91-
conn: conn,
92-
handler: srv.Handler,
93-
ptyCb: srv.PtyCallback,
94-
sessReqCb: srv.SessionRequestCallback,
95-
ctx: ctx,
93+
Channel: ch,
94+
conn: conn,
95+
handler: srv.Handler,
96+
ptyCb: srv.PtyCallback,
97+
sessReqCb: srv.SessionRequestCallback,
98+
subsystemHandlers: srv.SubsystemHandlers,
99+
ctx: ctx,
96100
}
97101
sess.handleRequests(reqs)
98102
}
99103

100104
type session struct {
101105
sync.Mutex
102106
gossh.Channel
103-
conn *gossh.ServerConn
104-
handler Handler
105-
handled bool
106-
exited bool
107-
pty *Pty
108-
winch chan Window
109-
env []string
110-
ptyCb PtyCallback
111-
sessReqCb SessionRequestCallback
112-
rawCmd string
113-
ctx Context
114-
sigCh chan<- Signal
115-
sigBuf []Signal
107+
conn *gossh.ServerConn
108+
handler Handler
109+
subsystemHandlers map[string]SubsystemHandler
110+
handled bool
111+
exited bool
112+
pty *Pty
113+
winch chan Window
114+
env []string
115+
ptyCb PtyCallback
116+
sessReqCb SessionRequestCallback
117+
rawCmd string
118+
subsystem string
119+
ctx Context
120+
sigCh chan<- Signal
121+
sigBuf []Signal
116122
}
117123

118124
func (sess *session) Write(p []byte) (n int, err error) {
@@ -191,6 +197,10 @@ func (sess *session) Command() []string {
191197
return append([]string(nil), cmd...)
192198
}
193199

200+
func (sess *session) Subsystem() string {
201+
return sess.subsystem
202+
}
203+
194204
func (sess *session) Pty() (Pty, <-chan Window, bool) {
195205
if sess.pty != nil {
196206
return *sess.pty, sess.winch, true
@@ -239,6 +249,40 @@ func (sess *session) handleRequests(reqs <-chan *gossh.Request) {
239249
sess.handler(sess)
240250
sess.Exit(0)
241251
}()
252+
case "subsystem":
253+
if sess.handled {
254+
req.Reply(false, nil)
255+
continue
256+
}
257+
258+
var payload = struct{ Value string }{}
259+
gossh.Unmarshal(req.Payload, &payload)
260+
sess.subsystem = payload.Value
261+
262+
// If there's a session policy callback, we need to confirm before
263+
// accepting the session.
264+
if sess.sessReqCb != nil && !sess.sessReqCb(sess, req.Type) {
265+
sess.rawCmd = ""
266+
req.Reply(false, nil)
267+
continue
268+
}
269+
270+
handler := sess.subsystemHandlers[payload.Value]
271+
if handler == nil {
272+
handler = sess.subsystemHandlers["default"]
273+
}
274+
if handler == nil {
275+
req.Reply(false, nil)
276+
continue
277+
}
278+
279+
sess.handled = true
280+
req.Reply(true, nil)
281+
282+
go func() {
283+
handler(sess)
284+
sess.Exit(0)
285+
}()
242286
case "env":
243287
if sess.handled {
244288
req.Reply(false, nil)

0 commit comments

Comments
 (0)