From 496cc44f643ab863735ea54113379c526239c950 Mon Sep 17 00:00:00 2001 From: Andrew Thornton Date: Sat, 9 Oct 2021 21:06:11 +0100 Subject: [PATCH 1/2] Offer rsa-sha2-512 and rsa-sha2-256 algorithms in internal SSH There is a subtle bug in the SSH library x/crypto/ssh which makes the incorrect assumption that the public key type is the same as the signature algorithm type. This means that only ssh-rsa signatures are offered by default. This PR adds a workaround around this problem. Fix #17175 Signed-off-by: Andrew Thornton --- modules/ssh/ssh.go | 56 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/modules/ssh/ssh.go b/modules/ssh/ssh.go index efe952534551..bd50e061459a 100644 --- a/modules/ssh/ssh.go +++ b/modules/ssh/ssh.go @@ -316,10 +316,66 @@ func Listen(host string, port int, ciphers []string, keyExchanges []string, macs } } + // Workaround slightly broken behaviour in x/crypto/ssh/handshake.go:458-463 + // + // Fundamentally the issue here is that HostKeyAlgos make the incorrect assumption + // that the PublicKey().Type() matches the signature algorithm. + // + // Therefore we need to add duplicates for the RSA with different signing algorithms. + for i, signer := range srv.HostSigners { + if signer.PublicKey().Type() == "ssh-rsa" { + front := append(srv.HostSigners[:i], + &wrapSigner{ + Signer: signer, + algorithm: gossh.SigAlgoRSASHA2512, + }, + &wrapSigner{ + Signer: signer, + algorithm: gossh.SigAlgoRSASHA2256, + }, + ) + srv.HostSigners = append(front, + srv.HostSigners[i:]..., + ) + } + } + go listen(&srv) } +// wrapSigner wraps a signer and overrides its public key type with the provided algorithm +type wrapSigner struct { + ssh.Signer + algorithm string +} + +// PublicKey returns an associated PublicKey instance. +func (s *wrapSigner) PublicKey() gossh.PublicKey { + return &wrapPublicKey{ + PublicKey: s.Signer.PublicKey(), + algorithm: s.algorithm, + } +} + +// Sign returns raw signature for the given data. This method +// will apply the hash specified for the keytype to the data using +// the algorithm assigned for this key +func (s *wrapSigner) Sign(rand io.Reader, data []byte) (*gossh.Signature, error) { + return s.Signer.(gossh.AlgorithmSigner).SignWithAlgorithm(rand, data, s.algorithm) +} + +// wrapPublicKey wraps a PublicKey and overrides its type +type wrapPublicKey struct { + gossh.PublicKey + algorithm string +} + +// Type returns the algorithm +func (k *wrapPublicKey) Type() string { + return k.algorithm +} + // GenKeyPair make a pair of public and private keys for SSH access. // Public key is encoded in the format for inclusion in an OpenSSH authorized_keys file. // Private Key generated is PEM encoded From ab7b12e1408cbe30ba1af11241a11af2e125fea9 Mon Sep 17 00:00:00 2001 From: Andrew Thornton Date: Tue, 12 Oct 2021 19:57:07 +0100 Subject: [PATCH 2/2] as per review Signed-off-by: Andrew Thornton --- modules/ssh/ssh.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/ssh/ssh.go b/modules/ssh/ssh.go index bd50e061459a..909ed32c5ecf 100644 --- a/modules/ssh/ssh.go +++ b/modules/ssh/ssh.go @@ -322,9 +322,10 @@ func Listen(host string, port int, ciphers []string, keyExchanges []string, macs // that the PublicKey().Type() matches the signature algorithm. // // Therefore we need to add duplicates for the RSA with different signing algorithms. - for i, signer := range srv.HostSigners { + signers := make([]ssh.Signer, 0, len(srv.HostSigners)) + for _, signer := range srv.HostSigners { if signer.PublicKey().Type() == "ssh-rsa" { - front := append(srv.HostSigners[:i], + signers = append(signers, &wrapSigner{ Signer: signer, algorithm: gossh.SigAlgoRSASHA2512, @@ -334,11 +335,10 @@ func Listen(host string, port int, ciphers []string, keyExchanges []string, macs algorithm: gossh.SigAlgoRSASHA2256, }, ) - srv.HostSigners = append(front, - srv.HostSigners[i:]..., - ) } + signers = append(signers, signer) } + srv.HostSigners = signers go listen(&srv)