Skip to content

Commit aa4048a

Browse files
committed
internal/lsp/lsprpc: don't connect to sockets owned by different users
When running gopls as a forwarder it attempts to forward the LSP to a remote daemon. On posix systems, by default this uses a unix domain socket at a predictable filesystem location. As an extra precaution, attempt to verify that the remote socket is in fact owned by the current user. Also, change the default TCP listen address used on windows to bind to localhost. Updates golang/go#34111 Change-Id: Ib24886d290089a773851c5439586c3ddc9eb797d Reviewed-on: https://go-review.googlesource.com/c/tools/+/222246 Run-TryBot: Robert Findley <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Heschi Kreinick <[email protected]>
1 parent 270c59d commit aa4048a

File tree

3 files changed

+51
-4
lines changed

3 files changed

+51
-4
lines changed

internal/lsp/lsprpc/autostart_default.go

+8-3
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ import (
1010
)
1111

1212
var (
13-
startRemote = startRemoteDefault
14-
autoNetworkAddress = autoNetworkAddressDefault
13+
startRemote = startRemoteDefault
14+
autoNetworkAddress = autoNetworkAddressDefault
15+
verifyRemoteOwnership = verifyRemoteOwnershipDefault
1516
)
1617

1718
func startRemoteDefault(goplsPath string, args ...string) error {
@@ -29,5 +30,9 @@ func autoNetworkAddressDefault(goplsPath, id string) (network string, address st
2930
if id != "" {
3031
panic("identified remotes are not supported on windows")
3132
}
32-
return "tcp", ":37374"
33+
return "tcp", "localhost:37374"
34+
}
35+
36+
func verifyRemoteOwnershipDefault(network, address string) (bool, error) {
37+
return true, nil
3338
}

internal/lsp/lsprpc/autostart_posix.go

+31-1
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,27 @@
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

5-
// +build darwin dragonfly freebsd linux netbsd solaris openbsd
5+
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
66

77
package lsprpc
88

99
import (
1010
"crypto/sha1"
11+
"errors"
1112
"fmt"
1213
"log"
1314
"os"
1415
"os/exec"
16+
"os/user"
1517
"path/filepath"
18+
"strconv"
1619
"syscall"
1720
)
1821

1922
func init() {
2023
startRemote = startRemotePosix
2124
autoNetworkAddress = autoNetworkAddressPosix
25+
verifyRemoteOwnership = verifyRemoteOwnershipPosix
2226
}
2327

2428
func startRemotePosix(goplsPath string, args ...string) error {
@@ -64,3 +68,29 @@ func autoNetworkAddressPosix(goplsPath, id string) (network string, address stri
6468
}
6569
return "unix", filepath.Join(os.TempDir(), fmt.Sprintf("%s-%s-daemon.%s%s", basename, shortHash, user, idComponent))
6670
}
71+
72+
func verifyRemoteOwnershipPosix(network, address string) (bool, error) {
73+
if network != "unix" {
74+
return true, nil
75+
}
76+
fi, err := os.Stat(address)
77+
if err != nil {
78+
if os.IsNotExist(err) {
79+
return true, nil
80+
}
81+
return false, fmt.Errorf("checking socket owner: %v", err)
82+
}
83+
stat, ok := fi.Sys().(*syscall.Stat_t)
84+
if !ok {
85+
return false, errors.New("fi.Sys() is not a Stat_t")
86+
}
87+
user, err := user.Current()
88+
if err != nil {
89+
return false, fmt.Errorf("checking current user: %v", err)
90+
}
91+
uid, err := strconv.ParseUint(user.Uid, 10, 32)
92+
if err != nil {
93+
return false, fmt.Errorf("parsing current UID: %v", err)
94+
}
95+
return stat.Uid == uint32(uid), nil
96+
}

internal/lsp/lsprpc/lsprpc.go

+12
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,18 @@ func (f *Forwarder) connectToRemote(ctx context.Context) (net.Conn, error) {
268268
// So we need to resolve a real network and address here.
269269
network, address = autoNetworkAddress(f.goplsPath, f.addr)
270270
}
271+
// Attempt to verify that we own the remote. This is imperfect, but if we can
272+
// determine that the remote is owned by a different user, we should fail.
273+
ok, err := verifyRemoteOwnership(network, address)
274+
if err != nil {
275+
// If the ownership check itself failed, we fail open but log an error to
276+
// the user.
277+
log.Error(ctx, "unable to check daemon socket owner, failing open: %v", err)
278+
} else if !ok {
279+
// We succesfully checked that the socket is not owned by us, we fail
280+
// closed.
281+
return nil, fmt.Errorf("socket %q is owned by a different user", address)
282+
}
271283
// Try dialing our remote once, in case it is already running.
272284
netConn, err = net.DialTimeout(network, address, f.dialTimeout)
273285
if err == nil {

0 commit comments

Comments
 (0)