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

add PasswordAuthE func, which is like PasswordAuth but returns error instead of bool #187

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions example_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ssh_test

import (
"fmt"
"io"
"io/ioutil"

Expand All @@ -21,6 +22,17 @@ func ExamplePasswordAuth() {
)
}

func ExamplePasswordAuthE() {
ssh.ListenAndServe(":2222", nil,
ssh.PasswordAuthE(func(ctx ssh.Context, pass string) error {
if pass == "secret" {
return nil
}
return fmt.Errorf("password incorrect")
}),
)
}

func ExampleNoPty() {
ssh.ListenAndServe(":2222", nil, ssh.NoPty())
}
Expand Down
8 changes: 8 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ func PasswordAuth(fn PasswordHandler) Option {
}
}

// PasswordAuthE returns a functional option that sets PasswordHandlerE on the server.
func PasswordAuthE(fn PasswordHandlerE) Option {
return func(srv *Server) error {
srv.PasswordHandlerE = fn
return nil
}
}

// PublicKeyAuth returns a functional option that sets PublicKeyHandler on the server.
func PublicKeyAuth(fn PublicKeyHandler) Option {
return func(srv *Server) error {
Expand Down
51 changes: 51 additions & 0 deletions options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,57 @@ func TestPasswordAuthBadPass(t *testing.T) {
}
}

func TestPasswordAuthE(t *testing.T) {
t.Parallel()
testUser := "testuser"
testPass := "testpass"
session, _, cleanup := newTestSessionWithOptions(t, &Server{
Handler: func(s Session) {
// noop
},
}, &gossh.ClientConfig{
User: testUser,
Auth: []gossh.AuthMethod{
gossh.Password(testPass),
},
HostKeyCallback: gossh.InsecureIgnoreHostKey(),
}, PasswordAuthE(func(ctx Context, password string) error {
if ctx.User() != testUser {
t.Fatalf("user = %#v; want %#v", ctx.User(), testUser)
}
if password != testPass {
t.Fatalf("user = %#v; want %#v", password, testPass)
}
return nil
}))
defer cleanup()
if err := session.Run(""); err != nil {
t.Fatal(err)
}
}

func TestPasswordAuthEBadPass(t *testing.T) {
t.Parallel()
l := newLocalListener()
srv := &Server{Handler: func(s Session) {}}
srv.SetOption(PasswordAuthE(func(ctx Context, password string) error {
return nil
}))
go srv.serveOnce(l)
_, err := gossh.Dial("tcp", l.Addr().String(), &gossh.ClientConfig{
User: "testuser",
Auth: []gossh.AuthMethod{
gossh.Password("testpass"),
},
HostKeyCallback: gossh.InsecureIgnoreHostKey(),
})
if err != nil {
if !strings.Contains(err.Error(), "unable to authenticate") {
t.Fatal(err)
}
}
}

type wrappedConn struct {
net.Conn
written int32
Expand Down
10 changes: 9 additions & 1 deletion server.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ type Server struct {

KeyboardInteractiveHandler KeyboardInteractiveHandler // keyboard-interactive authentication handler
PasswordHandler PasswordHandler // password authentication handler
PasswordHandlerE PasswordHandlerE // password authentiication handler with error, if it is set, it overrides PasswordHandler
PublicKeyHandler PublicKeyHandler // public key authentication handler
PtyCallback PtyCallback // callback for allowing PTY sessions, allows all if nil
ConnCallback ConnCallback // optional callback for wrapping net.Conn before handling
Expand Down Expand Up @@ -126,7 +127,7 @@ func (srv *Server) config(ctx Context) *gossh.ServerConfig {
for _, signer := range srv.HostSigners {
config.AddHostKey(signer)
}
if srv.PasswordHandler == nil && srv.PublicKeyHandler == nil && srv.KeyboardInteractiveHandler == nil {
if srv.PasswordHandler == nil && srv.PasswordHandlerE == nil && srv.PublicKeyHandler == nil && srv.KeyboardInteractiveHandler == nil {
config.NoClientAuth = true
}
if srv.Version != "" {
Expand All @@ -141,6 +142,13 @@ func (srv *Server) config(ctx Context) *gossh.ServerConfig {
return ctx.Permissions().Permissions, nil
}
}
if srv.PasswordHandlerE != nil {
config.PasswordCallback = func(conn gossh.ConnMetadata, password []byte) (*gossh.Permissions, error) {
applyConnMetadata(ctx, conn)
err := srv.PasswordHandlerE(ctx, string(password))
return ctx.Permissions().Permissions, err
}
}
if srv.PublicKeyHandler != nil {
config.PublicKeyCallback = func(conn gossh.ConnMetadata, key gossh.PublicKey) (*gossh.Permissions, error) {
applyConnMetadata(ctx, conn)
Expand Down
3 changes: 3 additions & 0 deletions ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ type PublicKeyHandler func(ctx Context, key PublicKey) bool
// PasswordHandler is a callback for performing password authentication.
type PasswordHandler func(ctx Context, password string) bool

// PasswordHandlerE is like PasswordHandler, but returns error
type PasswordHandlerE func(ctx Context, password string) error

// KeyboardInteractiveHandler is a callback for performing keyboard-interactive authentication.
type KeyboardInteractiveHandler func(ctx Context, challenger gossh.KeyboardInteractiveChallenge) bool

Expand Down