Skip to content

Commit 6fad3df

Browse files
FiloSottiledrakkanaphistic
committed
ssh: support rsa-sha2-256/512 on the server side
This lets clients know we support rsa-sha2-256/512 signatures from ssh-rsa public keys. OpenSSH prefers to break the connection rather than attempting trial and error, apparently. We don't enable support for the "ext-info-s" because we're not interested in any client->server extensions. This also replaces isAcceptableAlgo which was rejecting the rsa-sha2-256/[email protected] public key algorithms. Tested with OpenSSH 9.1 on macOS Ventura. Fixes golang/go#49269 Updates golang/go#49952 Co-authored-by: Nicola Murino <[email protected]> Co-authored-by: Kristin Davidson <[email protected]> Change-Id: I4955c3b12bb45575e9977ac657bb5805b49d00c3 Reviewed-on: https://go-review.googlesource.com/c/crypto/+/447757 Run-TryBot: Filippo Valsorda <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Roland Shoemaker <[email protected]> Reviewed-by: Nicola Murino <[email protected]> Reviewed-by: Michael Knyszek <[email protected]>
1 parent 21d60a1 commit 6fad3df

File tree

4 files changed

+38
-15
lines changed

4 files changed

+38
-15
lines changed

ssh/client_auth_test.go

+1-3
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,7 @@ func TestClientAuthPublicKey(t *testing.T) {
132132
if err := tryAuth(t, config); err != nil {
133133
t.Fatalf("unable to dial remote side: %s", err)
134134
}
135-
// Once the server implements the server-sig-algs extension, this will turn
136-
// into KeyAlgoRSASHA256.
137-
if len(signer.used) != 1 || signer.used[0] != KeyAlgoRSA {
135+
if len(signer.used) != 1 || signer.used[0] != KeyAlgoRSASHA256 {
138136
t.Errorf("unexpected Sign/SignWithAlgorithm calls: %q", signer.used)
139137
}
140138
}

ssh/common.go

+15
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"fmt"
1111
"io"
1212
"math"
13+
"strings"
1314
"sync"
1415

1516
_ "crypto/sha1"
@@ -118,6 +119,20 @@ func algorithmsForKeyFormat(keyFormat string) []string {
118119
}
119120
}
120121

122+
// supportedPubKeyAuthAlgos specifies the supported client public key
123+
// authentication algorithms. Note that this doesn't include certificate types
124+
// since those use the underlying algorithm. This list is sent to the client if
125+
// it supports the server-sig-algs extension. Order is irrelevant.
126+
var supportedPubKeyAuthAlgos = []string{
127+
KeyAlgoED25519,
128+
KeyAlgoSKED25519, KeyAlgoSKECDSA256,
129+
KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
130+
KeyAlgoRSASHA256, KeyAlgoRSASHA512, KeyAlgoRSA,
131+
KeyAlgoDSA,
132+
}
133+
134+
var supportedPubKeyAuthAlgosList = strings.Join(supportedPubKeyAuthAlgos, ",")
135+
121136
// unexpectedMessageError results when the SSH message that we received didn't
122137
// match what we wanted.
123138
func unexpectedMessageError(expected, got uint8) error {

ssh/handshake.go

+20-1
Original file line numberDiff line numberDiff line change
@@ -615,7 +615,8 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
615615
return err
616616
}
617617

618-
if t.sessionID == nil {
618+
firstKeyExchange := t.sessionID == nil
619+
if firstKeyExchange {
619620
t.sessionID = result.H
620621
}
621622
result.SessionID = t.sessionID
@@ -626,6 +627,24 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
626627
if err = t.conn.writePacket([]byte{msgNewKeys}); err != nil {
627628
return err
628629
}
630+
631+
// On the server side, after the first SSH_MSG_NEWKEYS, send a SSH_MSG_EXT_INFO
632+
// message with the server-sig-algs extension if the client supports it. See
633+
// RFC 8308, Sections 2.4 and 3.1.
634+
if !isClient && firstKeyExchange && contains(clientInit.KexAlgos, "ext-info-c") {
635+
extInfo := &extInfoMsg{
636+
NumExtensions: 1,
637+
Payload: make([]byte, 0, 4+15+4+len(supportedPubKeyAuthAlgosList)),
638+
}
639+
extInfo.Payload = appendInt(extInfo.Payload, len("server-sig-algs"))
640+
extInfo.Payload = append(extInfo.Payload, "server-sig-algs"...)
641+
extInfo.Payload = appendInt(extInfo.Payload, len(supportedPubKeyAuthAlgosList))
642+
extInfo.Payload = append(extInfo.Payload, supportedPubKeyAuthAlgosList...)
643+
if err := t.conn.writePacket(Marshal(extInfo)); err != nil {
644+
return err
645+
}
646+
}
647+
629648
if packet, err := t.conn.readPacket(); err != nil {
630649
return err
631650
} else if packet[0] != msgNewKeys {

ssh/server.go

+2-11
Original file line numberDiff line numberDiff line change
@@ -291,15 +291,6 @@ func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error)
291291
return perms, err
292292
}
293293

294-
func isAcceptableAlgo(algo string) bool {
295-
switch algo {
296-
case KeyAlgoRSA, KeyAlgoRSASHA256, KeyAlgoRSASHA512, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, KeyAlgoSKECDSA256, KeyAlgoED25519, KeyAlgoSKED25519,
297-
CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoSKECDSA256v01, CertAlgoED25519v01, CertAlgoSKED25519v01:
298-
return true
299-
}
300-
return false
301-
}
302-
303294
func checkSourceAddress(addr net.Addr, sourceAddrs string) error {
304295
if addr == nil {
305296
return errors.New("ssh: no address known for client, but source-address match required")
@@ -514,7 +505,7 @@ userAuthLoop:
514505
return nil, parseError(msgUserAuthRequest)
515506
}
516507
algo := string(algoBytes)
517-
if !isAcceptableAlgo(algo) {
508+
if !contains(supportedPubKeyAuthAlgos, underlyingAlgo(algo)) {
518509
authErr = fmt.Errorf("ssh: algorithm %q not accepted", algo)
519510
break
520511
}
@@ -572,7 +563,7 @@ userAuthLoop:
572563
// algorithm name that corresponds to algo with
573564
// sig.Format. This is usually the same, but
574565
// for certs, the names differ.
575-
if !isAcceptableAlgo(sig.Format) {
566+
if !contains(supportedPubKeyAuthAlgos, sig.Format) {
576567
authErr = fmt.Errorf("ssh: algorithm %q not accepted", sig.Format)
577568
break
578569
}

0 commit comments

Comments
 (0)