Skip to content

Commit c5370d2

Browse files
drakkangopherbot
authored andcommitted
ssh: check the declared public key algo against decoded one
This check will ensure we don't accept e.g. [email protected] 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 <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]> Reviewed-by: Cherry Mui <[email protected]> Auto-Submit: Filippo Valsorda <[email protected]> Reviewed-by: Filippo Valsorda <[email protected]> Run-TryBot: Filippo Valsorda <[email protected]>
1 parent 0d375be commit c5370d2

File tree

2 files changed

+101
-1
lines changed

2 files changed

+101
-1
lines changed

ssh/client_auth_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,3 +1076,94 @@ func TestCompatibleAlgoAndSignatures(t *testing.T) {
10761076
}
10771077
}
10781078
}
1079+
1080+
// configurablePublicKeyCallback is a public key callback that allows to
1081+
// configure the signature algorithm and format. This way we can emulate the
1082+
// behavior of buggy clients.
1083+
type configurablePublicKeyCallback struct {
1084+
signer AlgorithmSigner
1085+
signatureAlgo string
1086+
signatureFormat string
1087+
}
1088+
1089+
func (cb configurablePublicKeyCallback) method() string {
1090+
return "publickey"
1091+
}
1092+
1093+
func (cb configurablePublicKeyCallback) auth(session []byte, user string, c packetConn, rand io.Reader, extensions map[string][]byte) (authResult, []string, error) {
1094+
pub := cb.signer.PublicKey()
1095+
1096+
ok, err := validateKey(pub, cb.signatureAlgo, user, c)
1097+
if err != nil {
1098+
return authFailure, nil, err
1099+
}
1100+
if !ok {
1101+
return authFailure, nil, fmt.Errorf("invalid public key")
1102+
}
1103+
1104+
pubKey := pub.Marshal()
1105+
data := buildDataSignedForAuth(session, userAuthRequestMsg{
1106+
User: user,
1107+
Service: serviceSSH,
1108+
Method: cb.method(),
1109+
}, cb.signatureAlgo, pubKey)
1110+
sign, err := cb.signer.SignWithAlgorithm(rand, data, underlyingAlgo(cb.signatureFormat))
1111+
if err != nil {
1112+
return authFailure, nil, err
1113+
}
1114+
1115+
s := Marshal(sign)
1116+
sig := make([]byte, stringLength(len(s)))
1117+
marshalString(sig, s)
1118+
msg := publickeyAuthMsg{
1119+
User: user,
1120+
Service: serviceSSH,
1121+
Method: cb.method(),
1122+
HasSig: true,
1123+
Algoname: cb.signatureAlgo,
1124+
PubKey: pubKey,
1125+
Sig: sig,
1126+
}
1127+
p := Marshal(&msg)
1128+
if err := c.writePacket(p); err != nil {
1129+
return authFailure, nil, err
1130+
}
1131+
var success authResult
1132+
success, methods, err := handleAuthResponse(c)
1133+
if err != nil {
1134+
return authFailure, nil, err
1135+
}
1136+
if success == authSuccess || !containsMethod(methods, cb.method()) {
1137+
return success, methods, err
1138+
}
1139+
1140+
return authFailure, methods, nil
1141+
}
1142+
1143+
func TestPublicKeyAndAlgoCompatibility(t *testing.T) {
1144+
cert := &Certificate{
1145+
Key: testPublicKeys["rsa"],
1146+
ValidBefore: CertTimeInfinity,
1147+
CertType: UserCert,
1148+
}
1149+
cert.SignCert(rand.Reader, testSigners["ecdsa"])
1150+
certSigner, err := NewCertSigner(cert, testSigners["rsa"])
1151+
if err != nil {
1152+
t.Fatalf("NewCertSigner: %v", err)
1153+
}
1154+
1155+
clientConfig := &ClientConfig{
1156+
User: "user",
1157+
HostKeyCallback: InsecureIgnoreHostKey(),
1158+
Auth: []AuthMethod{
1159+
configurablePublicKeyCallback{
1160+
signer: certSigner.(AlgorithmSigner),
1161+
signatureAlgo: KeyAlgoRSASHA256,
1162+
signatureFormat: KeyAlgoRSASHA256,
1163+
},
1164+
},
1165+
}
1166+
if err := tryAuth(t, clientConfig); err == nil {
1167+
t.Error("cert login passed with incompatible public key type and algorithm")
1168+
}
1169+
}

ssh/server.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,16 @@ userAuthLoop:
576576
if !ok || len(payload) > 0 {
577577
return nil, parseError(msgUserAuthRequest)
578578
}
579-
579+
// Ensure the declared public key algo is compatible with the
580+
// decoded one. This check will ensure we don't accept e.g.
581+
// [email protected] algorithm with ssh-rsa public
582+
// key type. The algorithm and public key type must be
583+
// consistent: both must be certificate algorithms, or neither.
584+
if !contains(algorithmsForKeyFormat(pubKey.Type()), algo) {
585+
authErr = fmt.Errorf("ssh: public key type %q not compatible with selected algorithm %q",
586+
pubKey.Type(), algo)
587+
break
588+
}
580589
// Ensure the public key algo and signature algo
581590
// are supported. Compare the private key
582591
// algorithm name that corresponds to algo with

0 commit comments

Comments
 (0)