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: client auth loop calls PasswordCallback after receiving disconnect msg from server #66991

Open
samiponkanenssh opened this issue Apr 23, 2024 · 1 comment · May be fixed by golang/crypto#293
Labels
NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made.
Milestone

Comments

@samiponkanenssh
Copy link

Go version

go version go1.22.2 linux/amd64

Output of go env in your module/workspace:

GO111MODULE=''
GOARCH='amd64'
GOBIN=''
GOCACHE='/home/xxxx/.cache/go-build'
GOENV='/home/xxxx/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMODCACHE='/home/xxxx/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/home/xxxx/go'
GOPRIVATE=''
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/usr/local/lib/go'
GOSUMDB='sum.golang.org'
GOTMPDIR=''
GOTOOLCHAIN='auto'
GOTOOLDIR='/usr/local/lib/go/pkg/tool/linux_amd64'
GOVCS=''
GOVERSION='go1.22.2'
GCCGO='gccgo'
GOAMD64='v1'
AR='ar'
CC='gcc'
CXX='g++'
CGO_ENABLED='1'
GOMOD='/home/xxxx/go/src/github.com/samiponkanenssh/crypto/go.mod'
GOWORK=''
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
PKG_CONFIG='pkg-config'
GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build1562252138=/tmp/go-build -gno-record-gcc-switches'

What did you do?

When connecting with ssh.ClientConfig having publickey and password authentication enabled, and a OpenSSH server not accepting the client's publickey, and server using MaxAuthTries 1, then the golang ssh client still calls PasswordCallback() unexpectedly.

This happens because client auth loop does not exit immediately (in ssh/client_auth.go:74) when receiving a disconnect msg from server, but instead continues to try the next auth method. If that auth method is PasswordCallback, then the callback is called even though the server has already disconnected. This leads to weird UX because where the end user may get prompted for password followed by immediate failure.

The following code highlights the problem:

package main

import (
	"crypto/rand"
	"crypto/rsa"
	"log"
	"net"
	"os"
	"strings"

	"golang.org/x/crypto/ssh"
)

func main() {
	exit := func(v interface{}) {
		l := log.New(os.Stderr, "", 0)
		l.Printf("%v\n", v)
		os.Exit(-1)
	}

	args := os.Args[1:]
	if len(args) != 1 {
		exit("missing destination")
	}
	idx := strings.LastIndex(args[0], "@")
	if idx == -1 {
		exit("destination does not contain username")
	}
	user := args[0][:idx]
	dst := args[0][idx+1:]
	host, port, err := net.SplitHostPort(dst)
	if err != nil || port == "" {
		host = dst
		port = "22"
	}
	dst = net.JoinHostPort(host, port)

	key, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		exit(err)
	}

	signer, err := ssh.NewSignerFromKey(key)
	if err != nil {
		exit(err)
	}

	cfg := &ssh.ClientConfig{
		User: user,
		Auth: []ssh.AuthMethod{
			//ssh.PublicKeys(signer),
			ssh.PublicKeysCallback(func() ([]ssh.Signer, error) {
				log.Printf("PublicKeysCallback()")
				return []ssh.Signer{signer}, nil
			}),
			ssh.PasswordCallback(func() (secret string, err error) {
				log.Printf("PasswordCallback()")
				return "notaverysecretpassword", nil
			}),
			ssh.KeyboardInteractive(func(name, instruction string, questions []string, echos []bool) ([]string, error) {
				log.Printf("KeyboardInteractive()")
				return []string{"notaverysecretpassword"}, nil
			}),
		},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
	}

	log.Printf("connecting to %s@%s", user, dst)
	conn, err := ssh.Dial("tcp", dst, cfg)
	if err != nil {
		exit(err)
	}
	conn.Close()
}

What did you see happen?

$ ./sshclient root@192.168.200.105
2024/04/23 14:12:58 connecting to root@192.168.200.105:22
2024/04/23 14:12:58 PublicKeysCallback()
2024/04/23 14:12:58 PasswordCallback()
ssh: handshake failed: ssh: disconnect, reason 2: Too many authentication failures

What did you expect to see?

$ ./sshclient root@192.168.200.105
2024/04/23 14:13:49 connecting to root@192.168.200.105:22
2024/04/23 14:13:49 PublicKeysCallback()
ssh: handshake failed: ssh: disconnect, reason 2: Too many authentication failures
@gopherbot
Copy link

Change https://go.dev/cl/581075 mentions this issue: ssh: fail client auth immediately on receiving disconnect message fro…

@joedian joedian added the NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made. label Apr 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants