-
Notifications
You must be signed in to change notification settings - Fork 18.4k
Closed
Labels
NeedsFixThe path to resolution is known, but the work has not been done.The path to resolution is known, but the work has not been done.OS-Windows
Milestone
Description
Go version
go version go1.22.3 linux/amd64
Output of go env
in your module/workspace:
GO111MODULE=''
GOARCH='amd64'
GOBIN=''
GOCACHE='/home/xxx/.cache/go-build'
GOENV='/home/xxx/.config/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFLAGS=''
GOHOSTARCH='amd64'
GOHOSTOS='linux'
GOINSECURE=''
GOMODCACHE='/home/xxx/go/pkg/mod'
GONOPROXY=''
GONOSUMDB=''
GOOS='linux'
GOPATH='/home/xxx/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.3'
GCCGO='gccgo'
GOAMD64='v1'
AR='ar'
CC='gcc'
CXX='g++'
CGO_ENABLED='1'
GOMOD='/home/xxx/go/src/github.com/samiponkanen/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-build2434574384=/tmp/go-build -gno-record-gcc-switches'
What did you do?
It seems that Windows OpenSSH server behaves incorrectly w.r.t keyboard-interactive authentication:
$ ssh -vvv -o "PubkeyAuthentication no" -o "PasswordAuthentication no" user@10.1.102.148
OpenSSH_9.1p1, OpenSSL 3.0.2 15 Mar 2022
...
debug1: Local version string SSH-2.0-OpenSSH_9.1
debug1: Remote protocol version 2.0, remote software version OpenSSH_for_Windows_8.1
debug1: compat_banner: match: OpenSSH_for_Windows_8.1 pat OpenSSH* compat 0x04000000
...
debug3: receive packet: type 6
debug2: service_accept: ssh-userauth
debug1: SSH2_MSG_SERVICE_ACCEPT received
debug3: send packet: type 50
debug3: receive packet: type 51
debug1: Authentications that can continue: publickey,password,keyboard-interactive
debug3: start over, passed a different list publickey,password,keyboard-interactive
debug3: preferred keyboard-interactive
debug3: authmethod_lookup keyboard-interactive
debug3: remaining preferred:
debug3: authmethod_is_enabled keyboard-interactive
debug1: Next authentication method: keyboard-interactive
debug2: userauth_kbdint
debug3: send packet: type 50
debug2: we sent a keyboard-interactive packet, wait for reply
debug3: receive packet: type 51
debug1: Authentications that can continue: publickey,password,keyboard-interactive
debug3: userauth_kbdint: disable: no info_req_seen
debug2: we did not send a packet, disable method
debug1: No more authentication methods to try.
user@10.1.102.148: Permission denied (publickey,password,keyboard-interactive).
When trying to connect to such host using a golang client that uses ssh.KeyboardInteractive() wrapped a into ssh.RetryableAuthMethod(), then ssh.RetryableAuthMethod() will retry ssh.KeyboardInteractive() even if the failure happens so early that password is never prompted from the user.
What did you see happen?
package main
import (
"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)
cfg := &ssh.ClientConfig{
User: user,
Auth: []ssh.AuthMethod{
ssh.RetryableAuthMethod(ssh.KeyboardInteractive(func(name, instruction string, questions []string, echos []bool) ([]string, error) {
log.Printf("KeyboardInteractive()")
return []string{"notaverysecretpassword"}, nil
}), 6),
ssh.RetryableAuthMethod(ssh.PasswordCallback(func() (secret string, err error) {
log.Printf("PasswordCallback()")
return "notaverysecretpassword", nil
}), 6),
},
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()
}
Running this test client against a Windows OpenSSH server (and assuming MaxAuthTries is 6) reveals that neither KeyboardInteractive nor PasswordCallback is called:
$ ./testclient user@10.1.102.148
2024/06/06 12:02:05 connecting to user@10.1.102.148:22
ssh: handshake failed: ssh: disconnect, reason 2: Too many authentication failures
What did you expect to see?
Expected result is that PasswordCallback gets called:
$ ./testclient user@10.1.102.148
2024/06/06 12:02:42 connecting to user@10.1.102.148:22
2024/06/06 12:02:42 PasswordCallback()
2024/06/06 12:02:42 PasswordCallback()
2024/06/06 12:02:42 PasswordCallback()
2024/06/06 12:02:43 PasswordCallback()
2024/06/06 12:02:43 PasswordCallback()
ssh: handshake failed: ssh: disconnect, reason 2: Too many authentication failures
Metadata
Metadata
Assignees
Labels
NeedsFixThe path to resolution is known, but the work has not been done.The path to resolution is known, but the work has not been done.OS-Windows