diff --git a/server.go b/server.go index 3b6cd20..18419b8 100644 --- a/server.go +++ b/server.go @@ -27,6 +27,7 @@ type Server struct { KeyboardInteractiveHandler KeyboardInteractiveHandler // keyboard-interactive authentication handler PasswordHandler PasswordHandler // password authentication handler PublicKeyHandler PublicKeyHandler // public key authentication handler + NextAuthMethodsHandler NextAuthMethodsHandler // next auth methods handler for 2 step auth PtyCallback PtyCallback // callback for allowing PTY sessions, allows all if nil ConnCallback ConnCallback // optional callback for wrapping net.Conn before handling LocalPortForwardingCallback LocalPortForwardingCallback // callback for allowing local port forwarding, denies all if nil @@ -90,37 +91,59 @@ func (srv *Server) config(ctx Context) *gossh.ServerConfig { for _, signer := range srv.HostSigners { config.AddHostKey(signer) } - if srv.PasswordHandler == nil && srv.PublicKeyHandler == nil { - config.NoClientAuth = true - } + //if srv.PasswordHandler == nil && srv.PublicKeyHandler == nil { + // config.NoClientAuth = true + //} if srv.Version != "" { config.ServerVersion = "SSH-2.0-" + srv.Version } if srv.PasswordHandler != nil { config.PasswordCallback = func(conn gossh.ConnMetadata, password []byte) (*gossh.Permissions, error) { applyConnMetadata(ctx, conn) - if ok := srv.PasswordHandler(ctx, string(password)); !ok { + res := srv.PasswordHandler(ctx, string(password)) + switch res { + case AuthSuccessful: + return ctx.Permissions().Permissions, nil + case AuthPartiallySuccessful: + return ctx.Permissions().Permissions, gossh.ErrPartialSuccess + default: return ctx.Permissions().Permissions, fmt.Errorf("permission denied") } - return ctx.Permissions().Permissions, nil } } if srv.PublicKeyHandler != nil { config.PublicKeyCallback = func(conn gossh.ConnMetadata, key gossh.PublicKey) (*gossh.Permissions, error) { applyConnMetadata(ctx, conn) - if ok := srv.PublicKeyHandler(ctx, key); !ok { + res := srv.PublicKeyHandler(ctx, key) + switch res { + case AuthSuccessful: + ctx.SetValue(ContextKeyPublicKey, key) + return ctx.Permissions().Permissions, nil + case AuthPartiallySuccessful: + ctx.SetValue(ContextKeyPublicKey, key) + return ctx.Permissions().Permissions, gossh.ErrPartialSuccess + default: return ctx.Permissions().Permissions, fmt.Errorf("permission denied") } - ctx.SetValue(ContextKeyPublicKey, key) - return ctx.Permissions().Permissions, nil } } if srv.KeyboardInteractiveHandler != nil { config.KeyboardInteractiveCallback = func(conn gossh.ConnMetadata, challenger gossh.KeyboardInteractiveChallenge) (*gossh.Permissions, error) { - if ok := srv.KeyboardInteractiveHandler(ctx, challenger); !ok { + res := srv.KeyboardInteractiveHandler(ctx, challenger) + switch res { + case AuthSuccessful: + return ctx.Permissions().Permissions, nil + case AuthPartiallySuccessful: + return ctx.Permissions().Permissions, gossh.ErrPartialSuccess + default: return ctx.Permissions().Permissions, fmt.Errorf("permission denied") } - return ctx.Permissions().Permissions, nil + } + } + if srv.NextAuthMethodsHandler != nil { + config.NextAuthMethodsCallback = func(conn gossh.ConnMetadata) []string { + applyConnMetadata(ctx, conn) + return srv.NextAuthMethodsHandler(ctx) } } return config diff --git a/session.go b/session.go index c105aae..86a69ce 100644 --- a/session.go +++ b/session.go @@ -301,6 +301,7 @@ func (sess *session) handleRequests(reqs <-chan *gossh.Request) { handler, ok := sess.subsystemHandlers[subname] if !ok { req.Reply(false, nil) + continue } sess.handled = true req.Reply(true, nil) diff --git a/ssh.go b/ssh.go index aefb681..f727b52 100644 --- a/ssh.go +++ b/ssh.go @@ -26,6 +26,16 @@ const ( SIGUSR2 Signal = "USR2" ) +// AuthResult, use some code except bool, to mark +// auth state +type AuthResult int + +const ( + AuthFailed AuthResult = iota + AuthSuccessful + AuthPartiallySuccessful +) + // DefaultHandler is the default Handler used by Serve. var DefaultHandler Handler @@ -39,13 +49,16 @@ type Handler func(Session) type SubsystemHandler func(Session) // PublicKeyHandler is a callback for performing public key authentication. -type PublicKeyHandler func(ctx Context, key PublicKey) bool +type PublicKeyHandler func(ctx Context, key PublicKey) AuthResult // PasswordHandler is a callback for performing password authentication. -type PasswordHandler func(ctx Context, password string) bool +type PasswordHandler func(ctx Context, password string) AuthResult // KeyboardInteractiveHandler is a callback for performing keyboard-interactive authentication. -type KeyboardInteractiveHandler func(ctx Context, challenger gossh.KeyboardInteractiveChallenge) bool +type KeyboardInteractiveHandler func(ctx Context, challenger gossh.KeyboardInteractiveChallenge) AuthResult + +// NextAuthMethodsHandler is a callback for performing 2 step auth +type NextAuthMethodsHandler func(ctx Context) []string // PtyCallback is a hook for allowing PTY sessions. type PtyCallback func(ctx Context, pty Pty) bool