Skip to content

Commit

Permalink
ssh: check the declared public key algo against decoded one
Browse files Browse the repository at this point in the history
This check will ensure we don't accept e.g. ssh-rsa-cert-v01@openssh.com
algorithm with ssh-rsa public key type.
The algorithm and public key type must be consistent: both must be
certificate algorithms, or neither.

Change-Id: I1d75074fb4d6db3a8796408e98ddffe577a96ab1
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/506836
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Run-TryBot: Filippo Valsorda <filippo@golang.org>
  • Loading branch information
drakkan authored and gopherbot committed Sep 5, 2023
1 parent 0d375be commit c5370d2
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 1 deletion.
91 changes: 91 additions & 0 deletions ssh/client_auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1076,3 +1076,94 @@ func TestCompatibleAlgoAndSignatures(t *testing.T) {
}
}
}

// configurablePublicKeyCallback is a public key callback that allows to
// configure the signature algorithm and format. This way we can emulate the
// behavior of buggy clients.
type configurablePublicKeyCallback struct {
signer AlgorithmSigner
signatureAlgo string
signatureFormat string
}

func (cb configurablePublicKeyCallback) method() string {
return "publickey"
}

func (cb configurablePublicKeyCallback) auth(session []byte, user string, c packetConn, rand io.Reader, extensions map[string][]byte) (authResult, []string, error) {
pub := cb.signer.PublicKey()

ok, err := validateKey(pub, cb.signatureAlgo, user, c)
if err != nil {
return authFailure, nil, err
}
if !ok {
return authFailure, nil, fmt.Errorf("invalid public key")
}

pubKey := pub.Marshal()
data := buildDataSignedForAuth(session, userAuthRequestMsg{
User: user,
Service: serviceSSH,
Method: cb.method(),
}, cb.signatureAlgo, pubKey)
sign, err := cb.signer.SignWithAlgorithm(rand, data, underlyingAlgo(cb.signatureFormat))
if err != nil {
return authFailure, nil, err
}

s := Marshal(sign)
sig := make([]byte, stringLength(len(s)))
marshalString(sig, s)
msg := publickeyAuthMsg{
User: user,
Service: serviceSSH,
Method: cb.method(),
HasSig: true,
Algoname: cb.signatureAlgo,
PubKey: pubKey,
Sig: sig,
}
p := Marshal(&msg)
if err := c.writePacket(p); err != nil {
return authFailure, nil, err
}
var success authResult
success, methods, err := handleAuthResponse(c)
if err != nil {
return authFailure, nil, err
}
if success == authSuccess || !containsMethod(methods, cb.method()) {
return success, methods, err
}

return authFailure, methods, nil
}

func TestPublicKeyAndAlgoCompatibility(t *testing.T) {
cert := &Certificate{
Key: testPublicKeys["rsa"],
ValidBefore: CertTimeInfinity,
CertType: UserCert,
}
cert.SignCert(rand.Reader, testSigners["ecdsa"])
certSigner, err := NewCertSigner(cert, testSigners["rsa"])
if err != nil {
t.Fatalf("NewCertSigner: %v", err)
}

clientConfig := &ClientConfig{
User: "user",
HostKeyCallback: InsecureIgnoreHostKey(),
Auth: []AuthMethod{
configurablePublicKeyCallback{
signer: certSigner.(AlgorithmSigner),
signatureAlgo: KeyAlgoRSASHA256,
signatureFormat: KeyAlgoRSASHA256,
},
},
}
if err := tryAuth(t, clientConfig); err == nil {
t.Error("cert login passed with incompatible public key type and algorithm")
}
}
11 changes: 10 additions & 1 deletion ssh/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,16 @@ userAuthLoop:
if !ok || len(payload) > 0 {
return nil, parseError(msgUserAuthRequest)
}

// Ensure the declared public key algo is compatible with the
// decoded one. This check will ensure we don't accept e.g.
// ssh-rsa-cert-v01@openssh.com algorithm with ssh-rsa public
// key type. The algorithm and public key type must be
// consistent: both must be certificate algorithms, or neither.
if !contains(algorithmsForKeyFormat(pubKey.Type()), algo) {
authErr = fmt.Errorf("ssh: public key type %q not compatible with selected algorithm %q",
pubKey.Type(), algo)
break
}
// Ensure the public key algo and signature algo
// are supported. Compare the private key
// algorithm name that corresponds to algo with
Expand Down

0 comments on commit c5370d2

Please sign in to comment.