ssh: close net.Conn on all NewServerConn errors

This PR ensures that the net.Conn passed to ssh.NewServerConn is closed
on all error return paths, not just after a failed handshake. This matches
the behavior of ssh.NewClientConn.

Change-Id: Id8a51d10ae8d575cbbe26f2ef6b37de7cca840ec
GitHub-Last-Rev: 81bb2e58a881a9a85935740bda06b034b32a8ce3
GitHub-Pull-Request: golang/crypto#279
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/549095
Run-TryBot: Nicola Murino <nicola.murino@gmail.com>
Auto-Submit: Nicola Murino <nicola.murino@gmail.com>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Reviewed-by: Nicola Murino <nicola.murino@gmail.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
Edoardo Spadolini
2023-12-14 18:23:28 +00:00
committed by Gopher Robot
parent 152cdb1503
commit 4e5a26183e
2 changed files with 66 additions and 9 deletions
+2
View File
@@ -213,6 +213,7 @@ func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewCha
} else { } else {
for _, algo := range fullConf.PublicKeyAuthAlgorithms { for _, algo := range fullConf.PublicKeyAuthAlgorithms {
if !contains(supportedPubKeyAuthAlgos, algo) { if !contains(supportedPubKeyAuthAlgos, algo) {
c.Close()
return nil, nil, nil, fmt.Errorf("ssh: unsupported public key authentication algorithm %s", algo) return nil, nil, nil, fmt.Errorf("ssh: unsupported public key authentication algorithm %s", algo)
} }
} }
@@ -220,6 +221,7 @@ func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewCha
// Check if the config contains any unsupported key exchanges // Check if the config contains any unsupported key exchanges
for _, kex := range fullConf.KeyExchanges { for _, kex := range fullConf.KeyExchanges {
if _, ok := serverForbiddenKexAlgos[kex]; ok { if _, ok := serverForbiddenKexAlgos[kex]; ok {
c.Close()
return nil, nil, nil, fmt.Errorf("ssh: unsupported key exchange %s for server", kex) return nil, nil, nil, fmt.Errorf("ssh: unsupported key exchange %s for server", kex)
} }
} }
+64 -9
View File
@@ -5,7 +5,11 @@
package ssh package ssh
import ( import (
"io"
"net"
"sync/atomic"
"testing" "testing"
"time"
) )
func TestClientAuthRestrictedPublicKeyAlgos(t *testing.T) { func TestClientAuthRestrictedPublicKeyAlgos(t *testing.T) {
@@ -59,27 +63,78 @@ func TestClientAuthRestrictedPublicKeyAlgos(t *testing.T) {
} }
func TestNewServerConnValidationErrors(t *testing.T) { func TestNewServerConnValidationErrors(t *testing.T) {
c1, c2, err := netPipe()
if err != nil {
t.Fatalf("netPipe: %v", err)
}
defer c1.Close()
defer c2.Close()
serverConf := &ServerConfig{ serverConf := &ServerConfig{
PublicKeyAuthAlgorithms: []string{CertAlgoRSAv01}, PublicKeyAuthAlgorithms: []string{CertAlgoRSAv01},
} }
_, _, _, err = NewServerConn(c1, serverConf) c := &markerConn{}
_, _, _, err := NewServerConn(c, serverConf)
if err == nil { if err == nil {
t.Fatal("NewServerConn with invalid public key auth algorithms succeeded") t.Fatal("NewServerConn with invalid public key auth algorithms succeeded")
} }
if !c.isClosed() {
t.Fatal("NewServerConn with invalid public key auth algorithms left connection open")
}
if c.isUsed() {
t.Fatal("NewServerConn with invalid public key auth algorithms used connection")
}
serverConf = &ServerConfig{ serverConf = &ServerConfig{
Config: Config{ Config: Config{
KeyExchanges: []string{kexAlgoDHGEXSHA256}, KeyExchanges: []string{kexAlgoDHGEXSHA256},
}, },
} }
_, _, _, err = NewServerConn(c1, serverConf) c = &markerConn{}
_, _, _, err = NewServerConn(c, serverConf)
if err == nil { if err == nil {
t.Fatal("NewServerConn with unsupported key exchange succeeded") t.Fatal("NewServerConn with unsupported key exchange succeeded")
} }
if !c.isClosed() {
t.Fatal("NewServerConn with unsupported key exchange left connection open")
}
if c.isUsed() {
t.Fatal("NewServerConn with unsupported key exchange used connection")
}
} }
type markerConn struct {
closed uint32
used uint32
}
func (c *markerConn) isClosed() bool {
return atomic.LoadUint32(&c.closed) != 0
}
func (c *markerConn) isUsed() bool {
return atomic.LoadUint32(&c.used) != 0
}
func (c *markerConn) Close() error {
atomic.StoreUint32(&c.closed, 1)
return nil
}
func (c *markerConn) Read(b []byte) (n int, err error) {
atomic.StoreUint32(&c.used, 1)
if atomic.LoadUint32(&c.closed) != 0 {
return 0, net.ErrClosed
} else {
return 0, io.EOF
}
}
func (c *markerConn) Write(b []byte) (n int, err error) {
atomic.StoreUint32(&c.used, 1)
if atomic.LoadUint32(&c.closed) != 0 {
return 0, net.ErrClosed
} else {
return 0, io.ErrClosedPipe
}
}
func (*markerConn) LocalAddr() net.Addr { return nil }
func (*markerConn) RemoteAddr() net.Addr { return nil }
func (*markerConn) SetDeadline(t time.Time) error { return nil }
func (*markerConn) SetReadDeadline(t time.Time) error { return nil }
func (*markerConn) SetWriteDeadline(t time.Time) error { return nil }