ssh: document and test source-address behavior for non-IP transports

The source-address critical option is a comma-separated list of IP
addresses and CIDR blocks, so it can never match a connection whose
remote address is not IP-based, such as a Unix domain socket.

Change-Id: I45e09fe5c8c13922cb0735278acf33320857322c
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/797042
Reviewed-by: David Chase <drchase@google.com>
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Junyang Shao <shaojunyang@google.com>
This commit is contained in:
Nicola Murino
2026-07-12 08:19:47 -07:00
parent b9229ecc70
commit c1a3b97d70
2 changed files with 22 additions and 2 deletions
+10 -2
View File
@@ -28,7 +28,11 @@ type Permissions struct {
// the given address). The SSH package currently only enforces
// the "source-address" critical option: it is validated against
// the client's remote address whenever it is present in the
// Permissions returned by any authentication callback. It is up
// Permissions returned by any authentication callback. Its value
// is a comma-separated list of IP addresses and CIDR blocks;
// consistently with OpenSSH, a connection whose remote address is
// not an IP address, such as a Unix domain socket, never matches
// the list and is rejected when the option is present. It is up
// to server implementations to enforce other critical options,
// such as "force-command", by checking them after the SSH
// handshake is successful. In general, SSH servers should reject
@@ -444,6 +448,10 @@ func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error)
return perms, err
}
// checkSourceAddress matches addr against sourceAddrs, a comma-separated list
// of IP addresses and CIDR blocks. Consistently with OpenSSH, a remote address
// that is not IP-based, such as a Unix domain socket, never matches the list
// and is rejected.
func checkSourceAddress(addr net.Addr, sourceAddrs string) error {
if addr == nil {
return errors.New("ssh: no address known for client, but source-address match required")
@@ -451,7 +459,7 @@ func checkSourceAddress(addr net.Addr, sourceAddrs string) error {
tcpAddr, ok := addr.(*net.TCPAddr)
if !ok {
return fmt.Errorf("ssh: remote address %v is not an TCP address when checking source-address match", addr)
return fmt.Errorf("ssh: remote address %v is not a TCP address when checking source-address match", addr)
}
for _, sourceAddr := range strings.Split(sourceAddrs, ",") {
+12
View File
@@ -849,6 +849,18 @@ func TestAuthCallbacksSourceAddress(t *testing.T) {
}
}
func TestCheckSourceAddressNonIP(t *testing.T) {
for _, addr := range []net.Addr{
&net.UnixAddr{Name: "/run/test.sock", Net: "unix"},
&net.UnixAddr{},
nil,
} {
if err := checkSourceAddress(addr, "127.0.0.0/8,::1/128"); err == nil {
t.Errorf("source-address check succeeded for non-IP remote address %v", addr)
}
}
}
func TestVerifiedPublicCallbackPartialSuccessBadUsage(t *testing.T) {
c1, c2, err := netPipe()
if err != nil {