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: regression: no longer able to authenticate using rsa-sha2-512 key #56342

Closed
kprav33n opened this issue Oct 20, 2022 · 14 comments
Closed
Labels
NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Milestone

Comments

@kprav33n
Copy link
Contributor

kprav33n commented Oct 20, 2022

What version of Go are you using (go version)?

$ go version
go version go1.19.2 linux/amd64

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GO111MODULE="on"
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/praveen/.cache/go-build"
GOENV="/home/praveen/.config/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/local/praveen/go/pkg/mod"
GONOPROXY="redacted-host"
GONOSUMDB="redacted-host"
GOOS="linux"
GOPATH="/local/praveen/go"
GOPRIVATE="redacted-host"
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/home/praveen/sdk/go1.19.2"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/home/praveen/sdk/go1.19.2/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.19.2"
GCCGO="gccgo"
GOAMD64="v1"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/local/praveen/src/crypto/go.mod"
GOWORK=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build3315967625=/tmp/go-build -gno-record-gcc-switches"

What did you do?

Authenticating to SSH server using rsa-sha2-512 used to work fine with in my Go program when I was using golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed. However, after upgrading to the latest (v0.1.0) crypto, the same program is unable to authenticate successfully anymore. I narrowed down the regression to a particular commit 5d542ad81a58c89581d596f49d0ba5d435481bcf. Key was generated using ssh-keygen -t rsa-sha2-512. (openssh version 8.9p1-3 on Ubuntu jammy). SSH server used was Github enterprise edition (SSH-2.0-babeld-5f3a6bb).

I have provided a test program to reproduce the issue below.

What did you expect to see?

Successful connection to the SSH server using the provided rsa-sha2-512 key.

What did you see instead?

Following error returned from the crypto library.

ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey], no supported methods remain

Go Program to Reproduce/Bisect

Placed the following program in cmd/sshauthtest/main.go under the local crypto git repo checked out based on v0.1.0 tag.

Note: I have redacted the username, hostname and key file path. Needs to be populated by the developer when reproducing the issue.

package main

import (
	"fmt"
	"net"
	"os"

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

func main() {
	user := "***REDACTED***"
	host := "***REDACTED***"
	keyFilePath := "***REDACTED***"

	keyBytes, err := os.ReadFile(keyFilePath)
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to read private key file %s: %s", keyFilePath, err)
		os.Exit(1)
	}

	signer, err := ssh.ParsePrivateKey(keyBytes)
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to parse private key file %s: %s", keyFilePath, err)
		os.Exit(1)
	}

	config := &ssh.ClientConfig{
		User: user,
		Auth: []ssh.AuthMethod{
			ssh.PublicKeys(signer),
		},
		HostKeyCallback: func(string, net.Addr, ssh.PublicKey) error {
			return nil
		},
	}

	if _, err := ssh.Dial("tcp", host, config); err != nil {
		fmt.Fprintf(os.Stderr, "failed to connect to %s: %s", host, err)
		os.Exit(1)
	}

	fmt.Printf("successfully connected to %s\n", host)
}

Git Bisection Result

$ git show --stat
commit 642fcc37f5043eadb2509c84b2769e729e7d27ef (HEAD -> master, tag: v0.1.0, origin/master, origin/HEAD)
Author: Gopher Robot <gobot@golang.org>
Date:   Wed Oct 19 15:40:49 2022 +0000

    go.mod: update golang.org/x dependencies

    Update golang.org/x dependencies to their latest tagged versions.
    Once this CL is submitted, and post-submit testing succeeds on all
    first-class ports across all supported Go versions, this repository
    will be tagged with its next minor version.

    Change-Id: If840eea1cadc749ce55efd88eb7d9fc38472839e
    Reviewed-on: https://go-review.googlesource.com/c/crypto/+/443996
    Auto-Submit: Gopher Robot <gobot@golang.org>
    Reviewed-by: Roland Shoemaker <roland@golang.org>
    Reviewed-by: Heschi Kreinick <heschi@google.com>
    TryBot-Result: Gopher Robot <gobot@golang.org>
    Run-TryBot: Gopher Robot <gobot@golang.org>

 go.mod |  8 ++++----
 go.sum | 34 +++++++++++++++++++++++++++-------
 2 files changed, 31 insertions(+), 11 deletions(-)

$ git bisect start HEAD 198e4374d7ed
Bisecting: 23 revisions left to test after this (roughly 5 steps)
[5352b09029215197cc109b46f0560d05ffab29db] acme/autocert: support External Account Binding (EAB) tokens

$ git bisect run go run ./cmd/sshauthtest
running  'go' 'run' './cmd/sshauthtest'
failed to connect to redacted-host:22: ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey], no supported methods remainexit status 1
Bisecting: 11 revisions left to test after this (roughly 4 steps)
[6068a2e6cfdc895ce524b6d2bdc8ea0cea8ea0e8] ssh: ignore MAC if AEAD ciphers negotiated
running  'go' 'run' './cmd/sshauthtest'
successfully connected to redacted-host:22
Bisecting: 5 revisions left to test after this (roughly 3 steps)
[1baeb1ce4c0b006eff0f294c47cb7617598dfb3d] ssh: don't advertise rsa-sha2 algorithms if we can't use them
running  'go' 'run' './cmd/sshauthtest'
successfully connected to redacted-host:22
Bisecting: 2 revisions left to test after this (roughly 2 steps)
[3147a52a75dda54ac3a611ef8978640d85188a2a] ssh: support rsa-sha2-256/512 for client certificates
running  'go' 'run' './cmd/sshauthtest'
failed to connect to redacted-host:22: ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey], no supported methods remainexit status 1
Bisecting: 0 revisions left to test after this (roughly 1 step)
[5d542ad81a58c89581d596f49d0ba5d435481bcf] ssh: support rsa-sha2-256/512 for client authentication
running  'go' 'run' './cmd/sshauthtest'
failed to connect to redacted-host:22: ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey], no supported methods remainexit status 1
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[a5774263c1e06050d4c71b5794b2b5a321289a8f] ssh: send (and rename) keyboard-interactive name field to the client
running  'go' 'run' './cmd/sshauthtest'
successfully connected to redacted-host:22
5d542ad81a58c89581d596f49d0ba5d435481bcf is the first bad commit
commit 5d542ad81a58c89581d596f49d0ba5d435481bcf
Author: Filippo Valsorda <filippo@golang.org>
Date:   Mon Mar 14 10:48:13 2022 -0400

    ssh: support rsa-sha2-256/512 for client authentication

    CL 220037 had implemented support for host authentication using
    rsa-sha2-256/512, but not client public key authentication. OpenSSH
    disabled the SHA-1 based ssh-rsa by default in version 8.8 (after
    pre-announcing it in versions 8.2, 8.3, 8.4, 8.5, 8.6, and 8.7) although
    some distributions re-enable it. GitHub will start rejecting ssh-rsa for
    keys uploaded before November 2, 2021 on March 15, 2022.

    https://github.blog/2021-09-01-improving-git-protocol-security-github/

    The server side already worked, as long as the client selected one of
    the SHA-2 algorithms, because the signature flowed freely to Verify.
    There was however nothing verifying that the signature algorithm matched
    the advertised one. The comment suggested the check was being performed,
    but it got lost back in CL 86190043. Not a security issue because the
    signature had to pass the callback's Verify method regardless, and both
    values were checked to be acceptable.

    Tested with OpenSSH 8.8 configured with "PubkeyAcceptedKeyTypes -ssh-rsa"
    and no application-side changes.

    The Signers returned by ssh/agent (when backed by an agent client)
    didn't actually implement AlgorithmSigner but ParameterizedSigner, an
    interface defined in an earlier version of CL 123955.

    Updates golang/go#49269
    Fixes golang/go#39885
    For golang/go#49952

    Change-Id: I13b41db8041f1112a70f106c55f077b904b12cb8
    Reviewed-on: https://go-review.googlesource.com/c/crypto/+/392394
    Trust: Filippo Valsorda <filippo@golang.org>
    Run-TryBot: Filippo Valsorda <filippo@golang.org>
    TryBot-Result: Gopher Robot <gobot@golang.org>
    Reviewed-by: Roland Shoemaker <roland@golang.org>

 ssh/agent/client.go     |  24 ++++++----
 ssh/client_auth.go      | 116 +++++++++++++++++++++++++++++++++++++++---------
 ssh/client_auth_test.go |  54 +++++++++++++++++++++-
 ssh/common.go           |   7 +--
 ssh/handshake.go        |   7 +++
 ssh/messages.go         |  11 +++++
 ssh/server.go           |   8 +++-
 7 files changed, 192 insertions(+), 35 deletions(-)
bisect found first bad commit

Additional Debug Information

CC: @FiloSottile

I tried to debug further and narrowed down the difference in behavior to x/crypto/ssh.confirmKeyAck function, which thinks that there is a mismatch between the algorithm used by the client and responded by the server. Note the additional fmt.Printf that I added under case msgUserAuthPubKeyOk.

func confirmKeyAck(key PublicKey, algo string, c packetConn) (bool, error) {
	pubKey := key.Marshal()

	for {
		packet, err := c.readPacket()
		if err != nil {
			return false, err
		}
		switch packet[0] {
		case msgUserAuthBanner:
			if err := handleBannerResponse(c, packet); err != nil {
				return false, err
			}
		case msgUserAuthPubKeyOk:
			var msg userAuthPubKeyOkMsg
			if err := Unmarshal(packet, &msg); err != nil {
				return false, err
			}
			fmt.Printf("msg.Algo: %s, algo: %s\n", msg.Algo, algo)
			if msg.Algo != algo || !bytes.Equal(msg.PubKey, pubKey) {
				return false, nil
			}
			return true, nil
		case msgUserAuthFailure:
			return false, nil
		default:
			return false, unexpectedMessageError(msgUserAuthPubKeyOk, packet[0])
		}
	}
}

In the failing case, it produces the following output.

msg.Algo: ssh-rsa, algo: rsa-sha2-256

What is even more interesting is that the algo was reported as rsa-sha2-256 even though it was generated as rsa-sha2-512!

In the succeeding case, both are of value ssh-rsa.

ssh-ed25519 works fine with both the new and old versions of crypto library. As a workaround for now, we are recommending our users to stick with ed25519 keys because of this issue.

@gopherbot gopherbot added this to the Unreleased milestone Oct 20, 2022
@kprav33n
Copy link
Contributor Author

Probably related to #53391

@dr2chase
Copy link
Contributor

@golang/security can you have a look at this? It smells like a dupe but IANAE.

@dr2chase dr2chase added the NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. label Oct 20, 2022
@idcmp
Copy link

idcmp commented Nov 15, 2022

FWIW this is only somewhat related - and not dupe of - #53391 as this is the client side (cc @FiloSottile ).

@idcmp
Copy link

idcmp commented Nov 17, 2022

For my use case, if I patch pickSignatureAlgorithm in client_auth.go to ignore server-sig-algs I seem to be able to get things to work again. (I forced ok=false after it reads extPayload).

I can't speak to the security of doing this though, but it does unblock me until the code is fixed.

@cschelcher
Copy link

Indeed, this seems to be a mismatch between key algorithm sent by the client and acknowledged by the server during authentication phase (pubkey).

Checking the RFC 4252 §7 on that point, the public key authentication request sent by the client is:

 byte      SSH_MSG_USERAUTH_REQUEST
 string    user name in ISO-10646 UTF-8 encoding [RFC3629]
 string    service name in US-ASCII
 string    "publickey"
 boolean   FALSE
 string    public key algorithm name
 string    public key blob

In our case the public key algorithm name field is set to rsa-sha2-256 or rsa-sha2-512 by the client.

The server reply as per RFC 4252 §7:

Any public key algorithm may be offered for use in authentication.
In particular, the list is not constrained by what was negotiated
during key exchange. If the server does not support some algorithm,
it MUST simply reject the request.

The server MUST respond to this message with either
SSH_MSG_USERAUTH_FAILURE or with the following:

 byte      SSH_MSG_USERAUTH_PK_OK
 string    public key algorithm name from the request
 string    public key blob from the request

In our case, the public key algorithm name from the request is set to ssh-rsa which is not what the client sent. So, the server seems not being RFC compliant.

However, there is no mention in the RFC that the client MUST, MAY or SHOULD verify these fields from the SSH_MSG_USERAUTH_PK_OK message. In other words, as the server replied with such message, it then indicate that it supports the algorithm sent by the client and accepts the public key.

As a consequence, in the x/crypto/ssh.confirmKeyAck function, skipping the algorithm match check on SSH_MSG_USERAUTH_PK_OK would be a valuable fix to this issue:

func confirmKeyAck(key PublicKey, algo string, c packetConn) (bool, error) {
	pubKey := key.Marshal()

	for {
		packet, err := c.readPacket()
		if err != nil {
			return false, err
		}
		switch packet[0] {
		case msgUserAuthBanner:
			if err := handleBannerResponse(c, packet); err != nil {
				return false, err
			}
		case msgUserAuthPubKeyOk:
			var msg userAuthPubKeyOkMsg
			if err := Unmarshal(packet, &msg); err != nil {
				return false, err
			}
			// if msg.Algo != algo || !bytes.Equal(msg.PubKey, pubKey) {
			if !bytes.Equal(msg.PubKey, pubKey) {
				return false, nil
			}
			return true, nil
		case msgUserAuthFailure:
			return false, nil
		default:
			return false, unexpectedMessageError(msgUserAuthPubKeyOk, packet[0])
		}
	}
}

Removing the algorithm match check improves client interoperability with servers that supports rsa-sha2-256 and rsa-sha2-512 (as per RFC 8303) but their implementation still reports them as ssh-rsa. Most probably a transition phase.

As far as I understand, the algorithm match check here does not improve security and removing it does not degrade it either. The client will then proceed with a SSH_MSG_USERAUTH_REQUEST message including its signature. If the server after all does not support the algorithm, the authentication will fail.

Did I miss something ?
I may be wrong, of course, what do you think ?

@drakkan
Copy link
Member

drakkan commented Jul 11, 2023

Hello,

I don't have GitHub Enterprise, I used my personal GitHub account to test. I cannot reproduce the issue.

msg.Algo and algo match in my tests:

msg algo "rsa-sha2-256", algo "rsa-sha2-256"

I also tried to set a different algorithm using the patch here like this

        signer, err := ssh.ParsePrivateKey(keyBytes)
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to parse private key file %s: %s", keyFilePath, err)
		os.Exit(1)
	}
	as, err := ssh.NewSignerWithAlgorithms(signer.(ssh.AlgorithmSigner), []string{ssh.KeyAlgoRSASHA512})
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to create multi algorithms signer: %s", err)
		os.Exit(1)
	}
	signer = as

works with all supported algorithms and msg.Algo and algo always match. Is this problem still current? Or does it only happen with GitHub Enterprise accounts?

tobischo added a commit to tobischo/gocrypto that referenced this issue Nov 20, 2023
tobischo added a commit to tobischo/gocrypto that referenced this issue Jan 9, 2024
tobischo added a commit to tobischo/gocrypto that referenced this issue Feb 8, 2024
tobischo added a commit to tobischo/gocrypto that referenced this issue Feb 27, 2024
tobischo added a commit to tobischo/gocrypto that referenced this issue Mar 6, 2024
@tobischo
Copy link

tobischo commented Mar 6, 2024

Is this problem still current?

I would say the problem is still current.

We keep seeing this issue with 2 servers that some of our partners are using.
Login works flawlessly via sftp (openssh) via CLI to both of those servers.

We first noticed it to start failing at v0.15.0 of golang.org/x/crypto.
It has been continuously an issue since then, which is why I resorted to fork+applying a patch and a replace directive on new versions released - which is probably not the best, but a working version for us for now.

I can try to gather more details about this. At least one of the servers in question is using HiDrive from Strato.
I have not gone through all the other fixes that might solve this, so will also do this to best case get rid of having to apply the patch before every update.

@drakkan
Copy link
Member

drakkan commented Mar 7, 2024

Hello,

I'd like to have access to a server with this behavior to test with OpenSSH and better understand what OpenSSH does so we can do something similar in crypto/ssh.

If this is not possible it would be great to have debug logs from the OpenSSH CLI connecting to this server, probably we need to add some additional logs here and in other relevant code paths. Thank you

@tobischo
Copy link

tobischo commented Mar 7, 2024

Unfortunately I haven't figured out what the exact setup is and I cannot really provide the specific credentials for the server.
I can check if I can get something from the same provider for you to use for some time.

In the meantime, some logs and code:

openssh connection Log
$ sftp -vvvv -i id_key_example specific-user@sftp.hidrive.strato.com
OpenSSH_7.9p1 Debian-10+deb10u4, OpenSSL 1.1.1n  15 Mar 2022
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: /etc/ssh/ssh_config line 19: Applying options for *
debug2: resolving "sftp.hidrive.strato.com" port 22
debug2: ssh_connect_direct
debug1: Connecting to sftp.hidrive.strato.com [85.214.3.70] port 22.
debug1: Connection established.
debug1: identity file id_key_example type -1
debug1: identity file id_key_example-cert type -1
debug1: Local version string SSH-2.0-OpenSSH_7.9p1 Debian-10+deb10u4
debug1: Remote protocol version 2.0, remote software version OpenSSH-7.5p1
debug1: match: OpenSSH-7.5p1 pat OpenSSH* compat 0x04000000
debug2: fd 4 setting O_NONBLOCK
debug1: Authenticating to sftp.hidrive.strato.com:22 as 'specific-user'
debug3: hostkeys_foreach: reading file "/home/ts/.ssh/known_hosts"
debug3: record_hostkey: found key type ECDSA in file /home/ts/.ssh/known_hosts:1
debug3: load_hostkeys: loaded 1 keys from sftp.hidrive.strato.com
debug3: order_hostkeyalgs: prefer hostkeyalgs: ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521-cert-v01@openssh.com,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521
debug3: send packet: type 20
debug1: SSH2_MSG_KEXINIT sent
debug3: receive packet: type 20
debug1: SSH2_MSG_KEXINIT received
debug2: local client KEXINIT proposal
debug2: KEX algorithms: curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256,diffie-hellman-group14-sha1,ext-info-c,kex-strict-c-v00@openssh.com
debug2: host key algorithms: ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521-cert-v01@openssh.com,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa
debug2: ciphers ctos: chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com
debug2: ciphers stoc: chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com
debug2: MACs ctos: umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1
debug2: MACs stoc: umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1
debug2: compression ctos: none,zlib@openssh.com,zlib
debug2: compression stoc: none,zlib@openssh.com,zlib
debug2: languages ctos:
debug2: languages stoc:
debug2: first_kex_follows 0
debug2: reserved 0
debug2: peer server KEXINIT proposal
debug2: KEX algorithms: curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha256,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1
debug2: host key algorithms: ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa,ssh-dss
debug2: ciphers ctos: chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com
debug2: ciphers stoc: chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com
debug2: MACs ctos: umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1
debug2: MACs stoc: umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1
debug2: compression ctos: none,zlib@openssh.com,zlib
debug2: compression stoc: none,zlib@openssh.com,zlib
debug2: languages ctos:
debug2: languages stoc:
debug2: first_kex_follows 0
debug2: reserved 0
debug1: kex: algorithm: curve25519-sha256
debug1: kex: host key algorithm: ecdsa-sha2-nistp256
debug1: kex: server->client cipher: chacha20-poly1305@openssh.com MAC: <implicit> compression: none
debug1: kex: client->server cipher: chacha20-poly1305@openssh.com MAC: <implicit> compression: none
debug3: send packet: type 30
debug1: expecting SSH2_MSG_KEX_ECDH_REPLY
debug3: receive packet: type 31
debug1: Server host key: ecdsa-sha2-nistp256 SHA256:nMzqtMicxgSJMPkDASkKzWebzGNnT200ipaAxwER03k
debug3: hostkeys_foreach: reading file "/home/ts/.ssh/known_hosts"
debug3: record_hostkey: found key type ECDSA in file /home/ts/.ssh/known_hosts:1
debug3: load_hostkeys: loaded 1 keys from sftp.hidrive.strato.com
debug3: hostkeys_foreach: reading file "/home/ts/.ssh/known_hosts"
debug3: record_hostkey: found key type ECDSA in file /home/ts/.ssh/known_hosts:2
debug3: load_hostkeys: loaded 1 keys from 85.214.3.70
debug1: Host 'sftp.hidrive.strato.com' is known and matches the ECDSA host key.
debug1: Found key in /home/ts/.ssh/known_hosts:1
debug3: send packet: type 21
debug2: set_newkeys: mode 1
debug1: rekey after 134217728 blocks
debug1: SSH2_MSG_NEWKEYS sent
debug1: expecting SSH2_MSG_NEWKEYS
debug3: receive packet: type 21
debug1: SSH2_MSG_NEWKEYS received
debug2: set_newkeys: mode 0
debug1: rekey after 134217728 blocks
debug1: Will attempt key: id_key_example  explicit
debug2: pubkey_prepare: done
debug3: send packet: type 5
debug3: receive packet: type 7
debug1: SSH2_MSG_EXT_INFO received
debug1: kex_input_ext_info: server-sig-algs=<rsa-sha2-256,rsa-sha2-512>
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
debug3: start over, passed a different list publickey,password
debug3: preferred gssapi-keyex,gssapi-with-mic,publickey,keyboard-interactive,password
debug3: authmethod_lookup publickey
debug3: remaining preferred: keyboard-interactive,password
debug3: authmethod_is_enabled publickey
debug1: Next authentication method: publickey
debug1: Trying private key: id_key_example
debug3: sign_and_send_pubkey: RSA SHA256:<key>
debug3: sign_and_send_pubkey: signing using rsa-sha2-512
debug3: send packet: type 50
debug2: we sent a publickey packet, wait for reply
debug3: receive packet: type 52
debug1: Authentication succeeded (publickey).
Authenticated to sftp.hidrive.strato.com ([85.214.3.70]:22).
debug2: fd 5 setting O_NONBLOCK
debug3: fd 6 is O_NONBLOCK
debug1: channel 0: new [client-session]
debug3: ssh_session2_open: channel_new: 0
debug2: channel 0: send open
debug3: send packet: type 90
debug1: Requesting no-more-sessions@openssh.com
debug3: send packet: type 80
debug1: Entering interactive session.
debug1: pledge: network
debug3: receive packet: type 91
debug2: channel_input_open_confirmation: channel 0: callback start
debug2: fd 4 setting TCP_NODELAY
debug3: ssh_packet_set_tos: set IP_TOS 0x08
debug2: client_session2_setup: id 0
debug1: Sending environment.
debug3: Ignored env SHELL
debug3: Ignored env PWD
debug3: Ignored env LOGNAME
debug3: Ignored env HOME
debug1: Sending env LANG = de_DE.UTF-8
debug2: channel 0: request env confirm 0
debug3: send packet: type 98
debug3: Ignored env LS_COLORS
debug3: Ignored env SSH_CONNECTION
debug3: Ignored env TERM
debug3: Ignored env USER
debug3: Ignored env SHLVL
debug3: Ignored env SSH_CLIENT
debug3: Ignored env PATH
debug3: Ignored env MAIL
debug3: Ignored env SSH_TTY
debug3: Ignored env _
debug1: Sending subsystem: sftp
debug2: channel 0: request subsystem confirm 1
debug3: send packet: type 98
debug2: channel_input_open_confirmation: channel 0: callback done
debug2: channel 0: open confirm rwindow 0 rmax 32768
debug2: channel 0: rcvd adjust 2097152
debug3: receive packet: type 99
debug2: channel_input_status_confirm: type 99 id 0
debug2: subsystem request accepted on channel 0
debug2: Remote version: 3
debug2: Server supports extension "statvfs@openssh.com" revision 2
debug2: Server supports extension "fstatvfs@openssh.com" revision 2
debug2: Server supports extension "posix-rename@openssh.com" revision 1
Connected to specific-user@sftp.hidrive.strato.com.
debug3: Sent message fd 3 T:16 I:1
debug3: SSH_FXP_REALPATH . -> / size 0
sftp> exit
debug2: channel 0: read<=0 rfd 5 len 0
debug2: channel 0: read failed
debug2: channel 0: chan_shutdown_read (i0 o0 sock -1 wfd 5 efd 7 [write])
debug2: channel 0: input open -> drain
debug2: channel 0: ibuf empty
debug2: channel 0: send eof
debug3: send packet: type 96
debug2: channel 0: input drain -> closed
debug3: receive packet: type 96
debug2: channel 0: rcvd eof
debug2: channel 0: output open -> drain
debug2: channel 0: obuf empty
debug2: channel 0: chan_shutdown_write (i3 o1 sock -1 wfd 6 efd 7 [write])
debug2: channel 0: output drain -> closed
debug3: receive packet: type 98
debug1: client_input_channel_req: channel 0 rtype exit-status reply 0
debug3: receive packet: type 97
debug2: channel 0: rcvd close
debug3: channel 0: will not send data after close
debug2: channel 0: almost dead
debug2: channel 0: gc: notify user
debug2: channel 0: gc: user detached
debug2: channel 0: send close
debug3: send packet: type 97
debug2: channel 0: is dead
debug2: channel 0: garbage collecting
debug1: channel 0: free: client-session, nchannels 1
debug3: channel 0: status: The following connections are open:
  #0 client-session (t4 r0 i3/0 o3/0 e[write]/0 fd -1/-1/7 sock -1 cc -1)

debug3: send packet: type 1
debug1: fd 0 clearing O_NONBLOCK
debug3: fd 1 is not O_NONBLOCK
Transferred: sent 3032, received 1944 bytes, in 1.6 seconds
Bytes per second: sent 1871.6, received 1200.0
debug1: Exit status 0

I prepared a simplified code version for go to show that we are not doing anything really beyond the basics for the connection:

main.go
package main

import (
  "fmt"
  "log"
  "net"
  "os"
  "time"

  "github.com/joho/godotenv"
  "golang.org/x/crypto/ssh"
)

func main() {
  err := godotenv.Load(".env")
  if err != nil {
    log.Fatal(err)
  }

  authMethods := make([]ssh.AuthMethod, 0)

  signer, err := ssh.ParsePrivateKey([]byte(os.Getenv("TEST_KEY")))
  if err != nil {
    log.Fatal(err)
  }

  authMethods = append(authMethods, ssh.PublicKeys(signer))

  sshConfig := &ssh.ClientConfig{
    User:    os.Getenv("TEST_USERNAME"),
    Auth:    authMethods,
    Timeout: 5 * time.Second,
    HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
      return nil
    },
  }
  sshConfig.SetDefaults()

  hostAndPort := os.Getenv("TEST_HOST_AND_PORT")

  sshClient, err := ssh.Dial(
    "tcp",
    hostAndPort,
    sshConfig,
  )
  if err != nil {
    log.Fatal(err)
  }
  defer sshClient.Close()

  fmt.Println("Connection success")
}
go.mod
module main

go 1.22.0

require (
  github.com/joho/godotenv v1.5.1
  golang.org/x/crypto v0.21.0
)

require golang.org/x/sys v0.18.0 // indirect

Now, with my patch it works

go.mod with replace
module main

go 1.22.0

require (
  github.com/joho/godotenv v1.5.1
  golang.org/x/crypto v0.21.1
)

require golang.org/x/sys v0.18.0 // indirect

replace golang.org/x/crypto v0.21.1 => github.com/tobischo/gocrypto v0.21.1

and when I add log statements where to golang.org/x/ssh where I added my patch, then I would see the following:

$ go run main.go
Msg Algo: ssh-rsa
Algo: rsa-sha2-256
2024/03/07 21:59:21 ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey], no supported methods remain
exit status 1

@drakkan
Copy link
Member

drakkan commented Mar 8, 2024

@tobischo thanks for the logs.

From your logs I see

debug1: Remote protocol version 2.0, remote software version OpenSSH-7.5p1

so I downloaded and compiled from source OpenSSH-7.5p1 but I cannot reproduce the issue. I have rsa-sha2-256 for both algo and msg.Algo.

The banner is slightly different, if I connect with ssh cli I have this

debug1: Remote protocol version 2.0, remote software version OpenSSH_7.5

@tobischo
Copy link

tobischo commented Mar 8, 2024

When I run ssh instead of sftp to the server I still get the following:

debug1: Remote protocol version 2.0, remote software version OpenSSH-7.5p1

and I see that part whether I use a valid username / key combination or not 🤔

obviously I am not getting to the relevant bit with invalid credentials

@gopherbot
Copy link
Contributor

Change https://go.dev/cl/573360 mentions this issue: ssh: validate key type in SSH_MSG_USERAUTH_PK_OK response

@drakkan
Copy link
Member

drakkan commented Mar 23, 2024

@tobischo can you please confirm that the above CL fixes the reported issue? Thank you

@tobischo
Copy link

@drakkan I can confirm that the change you prepared is working with the 2 servers where I observed the issues
Thank you for spending the time to look into this 😃

tobischo added a commit to tobischo/gocrypto that referenced this issue Apr 22, 2024
drakkan added a commit to drakkan/crypto that referenced this issue May 11, 2024
According to RFC 4252 Section 7 the algorithm in SSH_MSG_USERAUTH_PK_OK
should match that of the request but some servers send the key type instead.
OpenSSH checks for the key type, so we do the same.

Fixes golang/go#66438
Fixes golang/go#64785
Fixes golang/go#56342
Fixes golang/go#54027

Change-Id: I2f733f0faece097e44ba7a97c868d30a53e21d79
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/573360
Auto-Submit: Nicola Murino <nicola.murino@gmail.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Run-TryBot: Nicola Murino <nicola.murino@gmail.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Joedian Reid <joedian@google.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Projects
None yet
Development

No branches or pull requests

7 participants