Skip to content

x/crypto/ssh: Consider relaxing public key and signature matching for RSA keys in SSH_MSG_USERAUTH_REQUEST #53391

Closed
@stanhu

Description

@stanhu

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

$ go version
go version go1.17.9 darwin/arm64

Does this issue reproduce with the latest release?

No, but if SSH server extension is implemented (golang/crypto#197), this issue will show up under certain circumstances for older clients.

What did you do?

I applied golang/crypto#197, which implements RFC 3808 (server extension negotiation), to the master branch. Then I attempted to log into a Go SSH server with an Yubikey RSA key on Ubuntu 18.04. The login failed:

ssh: signature "ssh-rsa" came in for selected algorithm "rsa-sha2-512", public key is type ssh-rsa

In https://github.com/golang/crypto/blob/793ad666bf5ec61392092b27061be9618e4e219b/ssh/server.go#L567, the SSH server performs this check:

if underlyingAlgo(algo) != sig.Format {
	authErr = fmt.Errorf("ssh: signature %q not compatible with selected algorithm %q", sig.Format, algo)
	break
}

This is a sensible check and works for most cases. However, in Ubuntu 18.04 with gpg-agent v2.2.4 and OpenSSH v7.6, logins that use an RSA key with gpg-agent will fail to login. This problem does not happen with an OpenSSH server, however.

What did you expect to see?

As the example from RFC8332 shows, here is an example SSH_MSG_USERAUTH_REQUEST message:

     byte      SSH_MSG_USERAUTH_REQUEST
     string    user name
     string    service name
     string    "publickey"
     boolean   TRUE
     string    "rsa-sha2-512"     <--- 1 - Public key algorithm name (algo) (https://datatracker.ietf.org/doc/html/rfc4252#page-8)
     string    public key blob:
         string    "ssh-rsa"      <--- 2 - Public key type (pubKey.Type())
         mpint     e
         mpint     n
     string    signature:
         string    "rsa-sha2-512" <--- 3 - Signature format (sig.Format)
         string    rsa_signature_blob

Why does this happen? Since the server advertises it supports rsa-sha2-512, the OpenSSH client will attempt to use it in the signature. However, gpg-agent v2.2.4 does not support rsa-sha2-512; this was added in v2.2.6 via gpg/gnupg@80b775b. As a result, it appears that the SSH_MSG_USERAUTH_REQUEST has rsa-sha2-512 in 1 but ssh-sha for 3.

The Go crypto library expects that 1 and 3 match. However, OpenSSH is a bit more lenient, and it only checks that 1 is an RSA algorithm (rsa-dsa, rsa-sha2-256, or rsa-sha2-512), and that this matches with an RSA key in 2. In more detail:

  1. In https://github.com/openssh/openssh-portable/blob/abd59663df37a42152e37980113ccaa405b9a282/auth2-pubkey.c#L112, sshkey_type_from_name(pkalg) returns KEY_RSA type (https://github.com/openssh/openssh-portable/blob/0f3455356bc284d7c6f4d3c1614d31161bd5dcc2/sshkey.c#L91-L94).
  2. The public key is of type KEY_RSA, so https://github.com/openssh/openssh-portable/blob/abd59663df37a42152e37980113ccaa405b9a282/auth2-pubkey.c#L127 matches.
  3. https://github.com/openssh/openssh-portable/blob/abd59663df37a42152e37980113ccaa405b9a282/auth2-pubkey.c#L200 attempts to verify the public key with the signature by calling ssh_rsa_verify in https://github.com/openssh/openssh-portable/blob/83fa3a044891887369ce8b487ce88d713a04df48/ssh-rsa.c#L222. It determines the signature format in 3.

https://datatracker.ietf.org/doc/html/rfc8332#section-3.2 speaks to the problem we're seeing here:

   If the client includes the signature field, the client MUST encode
   the same algorithm name in the signature as in
   SSH_MSG_USERAUTH_REQUEST -- either "rsa-sha2-256" or "rsa-sha2-512".
   If a server receives a mismatching request, it MAY apply arbitrary
   authentication penalties, including but not limited to authentication
   failure or disconnect.

   OpenSSH 7.2 (but not 7.2p2) incorrectly encodes the algorithm in the
   signature as "ssh-rsa" when the algorithm in SSH_MSG_USERAUTH_REQUEST
   is "rsa-sha2-256" or "rsa-sha2-512".  In this case, the signature
   does actually use either SHA-256 or SHA-512.  A server MAY, but is
   not required to, accept this variant or another variant that
   corresponds to a good-faith implementation and is considered safe to
   accept.

Technically, the spec says here that the strict match on the server side is acceptable, but the spec also says that a server may relax this match since we know that:

  1. The SSH_MSG_USERAUTH_REQUEST public key algorithm is ssh-rsa.
  2. The public key type is rsa-sha.
  3. The signature type can be rsa-sha, rsa-sha2-256, or rsa-sha2-512.
  4. The verification of the public key with the signature still must pass.

I realize this may just be covering up old bugs related to gpg-agent or OpenSSH, but that's what we're seeing in the wild.

Metadata

Metadata

Assignees

No one assigned

    Labels

    FrozenDueToAgeNeedsFixThe path to resolution is known, but the work has not been done.

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions