ssh: check the declared public key algo against 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. 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>
This commit is contained in:
committed by
Gopher Robot
parent
0d375be9b6
commit
c5370d2cc6
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
+10
-1
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user