Skip to content

Commit 630f719

Browse files
committed
ssh: relax RSA signature check in SSH_MSG_USERAUTH_REQUEST
Buggy SSH clients, such as gpg-agent v2.2.4 and OpenSSH v7.6 shipped in Ubuntu 18.04, may send `ssh-rsa-512` as the public key algorithm but actually include an `rsa-sha` signature. If RFC 3808 (extension negotiation) is implemented, these clients will fail to authenticate with the error: ``` ssh: signature "ssh-rsa" came in for selected algorithm "rsa-sha2-512", public key is type ssh-rsa ``` According to RFC 8332 section 3.2: 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. ...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. While the client is expected to do the right thing, in practice older clients may not fully support `ssh-rsa-256` and `ssh-rsa-512`. For example, gpg-agent v2.2.6 added support for these newer signature types. To accomodate these clients, relax the matching constraint: if the `SSH_MSG_USERAUTH_REQUEST` message specifies an RSA public key algorithm and includes an RSA public key, then allow any of the following signature types: - `rsa-sha-512` - `rsa-sha-256` - `rsa-sha` This emulates what OpenSSH does. OpenSSH only considers that the RSA family is specified and then verifies if the signature and public key match. Closes golang/go#53391
1 parent a3485e1 commit 630f719

File tree

2 files changed

+105
-1
lines changed

2 files changed

+105
-1
lines changed

ssh/client_auth_test.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -957,3 +957,87 @@ func TestAuthMethodGSSAPIWithMIC(t *testing.T) {
957957
}
958958
}
959959
}
960+
961+
func TestCompatiblePublicKeyAndSignatureAlgorithms(t *testing.T) {
962+
type testcase struct {
963+
algo string
964+
pubKeyFormat string
965+
sigFormat string
966+
compatible bool
967+
}
968+
969+
testcases := []*testcase{
970+
{
971+
KeyAlgoRSA,
972+
KeyAlgoRSA,
973+
KeyAlgoRSA,
974+
true,
975+
},
976+
{
977+
KeyAlgoRSA,
978+
KeyAlgoRSASHA256,
979+
KeyAlgoRSA,
980+
true,
981+
},
982+
{
983+
KeyAlgoRSA,
984+
KeyAlgoRSASHA256,
985+
KeyAlgoRSASHA512,
986+
false,
987+
},
988+
{
989+
KeyAlgoECDSA384,
990+
KeyAlgoECDSA256,
991+
KeyAlgoECDSA256,
992+
false,
993+
},
994+
{
995+
KeyAlgoDSA,
996+
KeyAlgoRSA,
997+
KeyAlgoRSA,
998+
false,
999+
},
1000+
{
1001+
CertAlgoRSAv01,
1002+
CertAlgoRSAv01,
1003+
KeyAlgoRSA,
1004+
true,
1005+
},
1006+
{
1007+
CertAlgoRSAv01,
1008+
CertAlgoRSASHA256v01,
1009+
KeyAlgoRSA,
1010+
true,
1011+
},
1012+
{
1013+
CertAlgoRSAv01,
1014+
CertAlgoRSASHA256v01,
1015+
KeyAlgoRSA,
1016+
true,
1017+
},
1018+
{
1019+
CertAlgoRSAv01,
1020+
CertAlgoRSASHA512v01,
1021+
KeyAlgoRSA,
1022+
true,
1023+
},
1024+
{
1025+
KeyAlgoDSA,
1026+
CertAlgoRSASHA512v01,
1027+
KeyAlgoRSA,
1028+
false,
1029+
},
1030+
{
1031+
KeyAlgoSKECDSA256,
1032+
CertAlgoRSASHA512v01,
1033+
KeyAlgoRSA,
1034+
false,
1035+
},
1036+
}
1037+
1038+
for _, c := range testcases {
1039+
if isAlgoCompatible(c.algo, c.pubKeyFormat, c.sigFormat) != c.compatible {
1040+
t.Errorf("algorithm %s, public key format %s, signature format %s, expected compatible to be %v", c.algo, c.pubKeyFormat, c.sigFormat, c.compatible)
1041+
}
1042+
}
1043+
}

ssh/server.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,26 @@ func (l ServerAuthError) Error() string {
404404
// It is returned in ServerAuthError.Errors from NewServerConn.
405405
var ErrNoAuth = errors.New("ssh: no auth passed yet")
406406

407+
func isAlgoCompatible(algo string, pubKeyFormat string, sigFormat string) bool {
408+
algo = underlyingAlgo(algo)
409+
if algo == sigFormat {
410+
return true
411+
}
412+
413+
// Buggy SSH clients may send ssh-rsa2-512 as the public key algorithm but
414+
// actually include a rsa-sha signature.
415+
// According to RFC 8332 Section 3.2:
416+
// A server MAY, but is not required to, accept this variant or another variant that
417+
// corresponds to a good-faith implementation and is considered safe to
418+
// accept.
419+
compatibleAlgos := algorithmsForKeyFormat(pubKeyFormat)
420+
if contains(compatibleAlgos, algo) && contains(compatibleAlgos, sigFormat) {
421+
return true
422+
}
423+
424+
return false
425+
}
426+
407427
func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, error) {
408428
sessionID := s.transport.getSessionID()
409429
var cache pubKeyCache
@@ -576,7 +596,7 @@ userAuthLoop:
576596
authErr = fmt.Errorf("ssh: algorithm %q not accepted", sig.Format)
577597
break
578598
}
579-
if underlyingAlgo(algo) != sig.Format {
599+
if !isAlgoCompatible(algo, pubKey.Type(), sig.Format) {
580600
authErr = fmt.Errorf("ssh: signature %q not compatible with selected algorithm %q", sig.Format, algo)
581601
break
582602
}

0 commit comments

Comments
 (0)