Add subsystem support
This commit is contained in:
parent
f5cb472d2a
commit
a462277fdd
14
server.go
14
server.go
@ -15,6 +15,10 @@ import (
|
|||||||
// and ListenAndServeTLS methods after a call to Shutdown or Close.
|
// and ListenAndServeTLS methods after a call to Shutdown or Close.
|
||||||
var ErrServerClosed = errors.New("ssh: Server closed")
|
var ErrServerClosed = errors.New("ssh: Server closed")
|
||||||
|
|
||||||
|
type SubsystemHandler func(s Session)
|
||||||
|
|
||||||
|
var DefaultSubsystemHandlers = map[string]SubsystemHandler{}
|
||||||
|
|
||||||
type RequestHandler func(ctx Context, srv *Server, req *gossh.Request) (ok bool, payload []byte)
|
type RequestHandler func(ctx Context, srv *Server, req *gossh.Request) (ok bool, payload []byte)
|
||||||
|
|
||||||
var DefaultRequestHandlers = map[string]RequestHandler{}
|
var DefaultRequestHandlers = map[string]RequestHandler{}
|
||||||
@ -57,6 +61,10 @@ type Server struct {
|
|||||||
// no handlers are enabled.
|
// no handlers are enabled.
|
||||||
RequestHandlers map[string]RequestHandler
|
RequestHandlers map[string]RequestHandler
|
||||||
|
|
||||||
|
// SubsystemHandlers are handlers which are similar to the usual SSH command
|
||||||
|
// handlers, but handle named subsystems.
|
||||||
|
SubsystemHandlers map[string]SubsystemHandler
|
||||||
|
|
||||||
listenerWg sync.WaitGroup
|
listenerWg sync.WaitGroup
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
listeners map[net.Listener]struct{}
|
listeners map[net.Listener]struct{}
|
||||||
@ -95,6 +103,12 @@ func (srv *Server) ensureHandlers() {
|
|||||||
srv.ChannelHandlers[k] = v
|
srv.ChannelHandlers[k] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if srv.SubsystemHandlers == nil {
|
||||||
|
srv.SubsystemHandlers = map[string]SubsystemHandler{}
|
||||||
|
for k, v := range DefaultSubsystemHandlers {
|
||||||
|
srv.SubsystemHandlers[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *Server) config(ctx Context) *gossh.ServerConfig {
|
func (srv *Server) config(ctx Context) *gossh.ServerConfig {
|
||||||
|
82
session.go
82
session.go
@ -47,6 +47,9 @@ type Session interface {
|
|||||||
// RawCommand returns the exact command that was provided by the user.
|
// RawCommand returns the exact command that was provided by the user.
|
||||||
RawCommand() string
|
RawCommand() string
|
||||||
|
|
||||||
|
// Subsystem returns the subsystem requested by the user.
|
||||||
|
Subsystem() string
|
||||||
|
|
||||||
// PublicKey returns the PublicKey used to authenticate. If a public key was not
|
// PublicKey returns the PublicKey used to authenticate. If a public key was not
|
||||||
// used it will return nil.
|
// used it will return nil.
|
||||||
PublicKey() PublicKey
|
PublicKey() PublicKey
|
||||||
@ -87,12 +90,13 @@ func DefaultSessionHandler(srv *Server, conn *gossh.ServerConn, newChan gossh.Ne
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
sess := &session{
|
sess := &session{
|
||||||
Channel: ch,
|
Channel: ch,
|
||||||
conn: conn,
|
conn: conn,
|
||||||
handler: srv.Handler,
|
handler: srv.Handler,
|
||||||
ptyCb: srv.PtyCallback,
|
ptyCb: srv.PtyCallback,
|
||||||
sessReqCb: srv.SessionRequestCallback,
|
sessReqCb: srv.SessionRequestCallback,
|
||||||
ctx: ctx,
|
subsystemHandlers: srv.SubsystemHandlers,
|
||||||
|
ctx: ctx,
|
||||||
}
|
}
|
||||||
sess.handleRequests(reqs)
|
sess.handleRequests(reqs)
|
||||||
}
|
}
|
||||||
@ -100,19 +104,21 @@ func DefaultSessionHandler(srv *Server, conn *gossh.ServerConn, newChan gossh.Ne
|
|||||||
type session struct {
|
type session struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
gossh.Channel
|
gossh.Channel
|
||||||
conn *gossh.ServerConn
|
conn *gossh.ServerConn
|
||||||
handler Handler
|
handler Handler
|
||||||
handled bool
|
subsystemHandlers map[string]SubsystemHandler
|
||||||
exited bool
|
handled bool
|
||||||
pty *Pty
|
exited bool
|
||||||
winch chan Window
|
pty *Pty
|
||||||
env []string
|
winch chan Window
|
||||||
ptyCb PtyCallback
|
env []string
|
||||||
sessReqCb SessionRequestCallback
|
ptyCb PtyCallback
|
||||||
rawCmd string
|
sessReqCb SessionRequestCallback
|
||||||
ctx Context
|
rawCmd string
|
||||||
sigCh chan<- Signal
|
subsystem string
|
||||||
sigBuf []Signal
|
ctx Context
|
||||||
|
sigCh chan<- Signal
|
||||||
|
sigBuf []Signal
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sess *session) Write(p []byte) (n int, err error) {
|
func (sess *session) Write(p []byte) (n int, err error) {
|
||||||
@ -191,6 +197,10 @@ func (sess *session) Command() []string {
|
|||||||
return append([]string(nil), cmd...)
|
return append([]string(nil), cmd...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sess *session) Subsystem() string {
|
||||||
|
return sess.subsystem
|
||||||
|
}
|
||||||
|
|
||||||
func (sess *session) Pty() (Pty, <-chan Window, bool) {
|
func (sess *session) Pty() (Pty, <-chan Window, bool) {
|
||||||
if sess.pty != nil {
|
if sess.pty != nil {
|
||||||
return *sess.pty, sess.winch, true
|
return *sess.pty, sess.winch, true
|
||||||
@ -239,6 +249,40 @@ func (sess *session) handleRequests(reqs <-chan *gossh.Request) {
|
|||||||
sess.handler(sess)
|
sess.handler(sess)
|
||||||
sess.Exit(0)
|
sess.Exit(0)
|
||||||
}()
|
}()
|
||||||
|
case "subsystem":
|
||||||
|
if sess.handled {
|
||||||
|
req.Reply(false, nil)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var payload = struct{ Value string }{}
|
||||||
|
gossh.Unmarshal(req.Payload, &payload)
|
||||||
|
sess.subsystem = payload.Value
|
||||||
|
|
||||||
|
// If there's a session policy callback, we need to confirm before
|
||||||
|
// accepting the session.
|
||||||
|
if sess.sessReqCb != nil && !sess.sessReqCb(sess, req.Type) {
|
||||||
|
sess.rawCmd = ""
|
||||||
|
req.Reply(false, nil)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
handler := sess.subsystemHandlers[payload.Value]
|
||||||
|
if handler == nil {
|
||||||
|
handler = sess.subsystemHandlers["default"]
|
||||||
|
}
|
||||||
|
if handler == nil {
|
||||||
|
req.Reply(false, nil)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
sess.handled = true
|
||||||
|
req.Reply(true, nil)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
handler(sess)
|
||||||
|
sess.Exit(0)
|
||||||
|
}()
|
||||||
case "env":
|
case "env":
|
||||||
if sess.handled {
|
if sess.handled {
|
||||||
req.Reply(false, nil)
|
req.Reply(false, nil)
|
||||||
|
Loading…
Reference in New Issue
Block a user