1
0
mirror of https://github.com/golang/go synced 2024-10-01 07:38:32 -06:00
go/internal/lsp/lsprpc/autostart_posix.go
Rob Findley aa4048aca1 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 <rfindley@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
2020-03-09 18:08:59 +00:00

97 lines
2.6 KiB
Go

// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
package lsprpc
import (
"crypto/sha1"
"errors"
"fmt"
"log"
"os"
"os/exec"
"os/user"
"path/filepath"
"strconv"
"syscall"
)
func init() {
startRemote = startRemotePosix
autoNetworkAddress = autoNetworkAddressPosix
verifyRemoteOwnership = verifyRemoteOwnershipPosix
}
func startRemotePosix(goplsPath string, args ...string) error {
cmd := exec.Command(goplsPath, args...)
cmd.SysProcAttr = &syscall.SysProcAttr{
Setsid: true,
}
if err := cmd.Start(); err != nil {
return fmt.Errorf("starting remote gopls: %v", err)
}
return nil
}
// autoNetworkAddress resolves an id on the 'auto' pseduo-network to a
// real network and address. On unix, this uses unix domain sockets.
func autoNetworkAddressPosix(goplsPath, id string) (network string, address string) {
// Especially when doing local development or testing, it's important that
// the remote gopls instance we connect to is running the same binary as our
// forwarder. So we encode a short hash of the binary path into the daemon
// socket name. If possible, we also include the buildid in this hash, to
// account for long-running processes where the binary has been subsequently
// rebuilt.
h := sha1.New()
cmd := exec.Command("go", "tool", "buildid", goplsPath)
cmd.Stdout = h
var pathHash []byte
if err := cmd.Run(); err == nil {
pathHash = h.Sum(nil)
} else {
log.Printf("error getting current buildid: %v", err)
sum := sha1.Sum([]byte(goplsPath))
pathHash = sum[:]
}
shortHash := fmt.Sprintf("%x", pathHash)[:6]
user := os.Getenv("USER")
if user == "" {
user = "shared"
}
basename := filepath.Base(goplsPath)
idComponent := ""
if id != "" {
idComponent = "-" + id
}
return "unix", filepath.Join(os.TempDir(), fmt.Sprintf("%s-%s-daemon.%s%s", basename, shortHash, user, idComponent))
}
func verifyRemoteOwnershipPosix(network, address string) (bool, error) {
if network != "unix" {
return true, nil
}
fi, err := os.Stat(address)
if err != nil {
if os.IsNotExist(err) {
return true, nil
}
return false, fmt.Errorf("checking socket owner: %v", err)
}
stat, ok := fi.Sys().(*syscall.Stat_t)
if !ok {
return false, errors.New("fi.Sys() is not a Stat_t")
}
user, err := user.Current()
if err != nil {
return false, fmt.Errorf("checking current user: %v", err)
}
uid, err := strconv.ParseUint(user.Uid, 10, 32)
if err != nil {
return false, fmt.Errorf("parsing current UID: %v", err)
}
return stat.Uid == uint32(uid), nil
}