Skip to content

proposal: x/crypto/ssh: verified public key callback and arbitrary data in Permissions #70795

Open
@espadolini

Description

@espadolini

Proposal Details

The ServerConfig.PublicKeyCallback callback gets called as part of the server-side SSH handshake whenever the client tries to authenticate with a public key or when the client asks whether or not a public key would be accepted by the server. No matter the order in which keys are presented, and notwithstanding the mitigation for CVE-2024-45337 (#70779), the callback is called before any signature from the client is verified (or potentially even received), which means that it's not practical or advisable to have the server make decisions on whether or not to continue with the authentication (potentially with a partial success) based on data and computations that require more effort than what can be allowed for an unauthenticated and untrusted client.

I propose the addition of a new, optional callback to ssh.ServerConfig:

type ServerConfig struct {
	...
	VerifiedPublicKeyCallback func(ConnMetadata, PublicKey, *Permissions) (*Permissions, error)
}

If PublicKeyCallback returns no error for a given public key, the client provides a SSH_MSG_USERAUTH_REQUEST message with a valid signature for that public key, and VerifiedPublicKeyCallback is non-nil, the VerifiedPublicKeyCallback will be called before the server sends a response to the client, passing in the same ConnMetadata and PublicKey that was passed to the PublicKeyCallback, and the *Permissions object that was returned by PublicKeyCallback.

The VerifiedPublicKeyCallback can then return the same or a new *Permissions to complete the handshake, a *PartialSuccessError to continue the handshake with a partial success and new authentication callbacks, or a different non-nil error to fail the authentication and let the client attempt to use a different authentication method (I believe that according to RFC 4252 the server is allowed to fail the authentication for a key that it has acknowledged as acceptable earlier in the handshake, but it might be worth checking the behavior of popular SSH clients in such a situation). VerifiedPublicKeyCallback is guaranteed to not be called again after returning success - in case of a *PartialSuccessError that includes PublicKeyCallback, it might be called again once more for a different public key.

To avoid ambiguous behavior, PublicKeyCallback is not allowed to return a *PartialSuccessError if VerifiedPublicKeyCallback is non-nil.

To better support passing data between the two callbacks - and to allow for extracting richer authentication data without risking the sort of misuse that led to CVE-2024-45337 - I also propose that we extend Permissions to include a single any field, for use by the authentication callbacks:

type Permissions struct {
	CriticalOptions map[string]string

	Extensions map[string]string

	ExtraData any
}

The ExtraData field will be available for inspection in ServerConn.Permissions after the handshake, and it can be used to pass some arbitrary data from PublicKeyCallback to VerifiedPublicKeyCallback.

Metadata

Metadata

Assignees

No one assigned

    Labels

    ProposalProposal-CryptoProposal related to crypto packages or other security issues

    Type

    No type

    Projects

    Status

    Active

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions