all: use named pipes on windows

Signed-off-by: Maisem Ali <maisem@tailscale.com>
This commit is contained in:
Maisem Ali
2022-11-21 09:00:20 -08:00
committed by Maisem Ali
parent 45042a76cd
commit adc302f428
12 changed files with 159 additions and 82 deletions
-51
View File
@@ -6,7 +6,6 @@
package ipnauth
import (
"errors"
"fmt"
"net"
"net/netip"
@@ -23,7 +22,6 @@ import (
"tailscale.com/types/logger"
"tailscale.com/util/clientmetric"
"tailscale.com/util/groupmember"
"tailscale.com/util/pidowner"
"tailscale.com/util/winutil"
"tailscale.com/version/distro"
)
@@ -73,55 +71,6 @@ func (ci *ConnIdentity) Pid() int { return ci.pid }
func (ci *ConnIdentity) IsUnixSock() bool { return ci.isUnixSock }
func (ci *ConnIdentity) Creds() *peercred.Creds { return ci.creds }
// GetConnIdentity returns the localhost TCP connection's identity information
// (pid, userid, user). If it's not Windows (for now), it returns a nil error
// and a ConnIdentity with NotWindows set true. It's only an error if we expected
// to be able to map it and couldn't.
func GetConnIdentity(logf logger.Logf, c net.Conn) (ci *ConnIdentity, err error) {
ci = &ConnIdentity{conn: c}
if runtime.GOOS != "windows" { // for now; TODO: expand to other OSes
ci.notWindows = true
_, ci.isUnixSock = c.(*net.UnixConn)
ci.creds, _ = peercred.Get(c)
return ci, nil
}
la, err := netip.ParseAddrPort(c.LocalAddr().String())
if err != nil {
return ci, fmt.Errorf("parsing local address: %w", err)
}
ra, err := netip.ParseAddrPort(c.RemoteAddr().String())
if err != nil {
return ci, fmt.Errorf("parsing local remote: %w", err)
}
if !la.Addr().IsLoopback() || !ra.Addr().IsLoopback() {
return ci, errors.New("non-loopback connection")
}
tab, err := netstat.Get()
if err != nil {
return ci, fmt.Errorf("failed to get local connection table: %w", err)
}
pid := peerPid(tab.Entries, la, ra)
if pid == 0 {
return ci, errors.New("no local process found matching localhost connection")
}
ci.pid = pid
uid, err := pidowner.OwnerOfPID(pid)
if err != nil {
var hint string
if runtime.GOOS == "windows" {
hint = " (WSL?)"
}
return ci, fmt.Errorf("failed to map connection's pid to a user%s: %w", hint, err)
}
ci.userID = ipn.WindowsUserID(uid)
u, err := LookupUserFromID(logf, uid)
if err != nil {
return ci, fmt.Errorf("failed to look up user from userid: %w", err)
}
ci.user = u
return ci, nil
}
var metricIssue869Workaround = clientmetric.NewCounter("issue_869_workaround")
// LookupUserFromID is a wrapper around os/user.LookupId that works around some
+24
View File
@@ -0,0 +1,24 @@
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !windows
package ipnauth
import (
"net"
"inet.af/peercred"
"tailscale.com/types/logger"
)
// GetConnIdentity extracts the identity information from the connection
// based on the user who owns the other end of the connection.
// and couldn't. The returned connIdentity has NotWindows set to true.
func GetConnIdentity(_ logger.Logf, c net.Conn) (ci *ConnIdentity, err error) {
ci = &ConnIdentity{conn: c, notWindows: true}
_, ci.isUnixSock = c.(*net.UnixConn)
ci.creds, _ = peercred.Get(c)
return ci, nil
}
+59
View File
@@ -0,0 +1,59 @@
// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ipnauth
import (
"fmt"
"net"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
"tailscale.com/ipn"
"tailscale.com/types/logger"
"tailscale.com/util/pidowner"
)
var (
kernel32 = syscall.NewLazyDLL("kernel32.dll")
procGetNamedPipeClientProcessId = kernel32.NewProc("GetNamedPipeClientProcessId")
)
func getNamedPipeClientProcessId(h windows.Handle) (pid uint32, err error) {
r1, _, err := procGetNamedPipeClientProcessId.Call(uintptr(h), uintptr(unsafe.Pointer(&pid)))
if r1 > 0 {
return pid, nil
}
return 0, err
}
// GetConnIdentity extracts the identity information from the connection
// based on the user who owns the other end of the connection.
// If c is not backed by a named pipe, an error is returned.
func GetConnIdentity(logf logger.Logf, c net.Conn) (ci *ConnIdentity, err error) {
ci = &ConnIdentity{conn: c}
h, ok := c.(interface {
Fd() uintptr
})
if !ok {
return ci, fmt.Errorf("not a windows handle: %T", c)
}
pid, err := getNamedPipeClientProcessId(windows.Handle(h.Fd()))
if err != nil {
return ci, fmt.Errorf("getNamedPipeClientProcessId: %v", err)
}
ci.pid = int(pid)
uid, err := pidowner.OwnerOfPID(ci.pid)
if err != nil {
return ci, fmt.Errorf("failed to map connection's pid to a user (WSL?): %w", err)
}
ci.userID = ipn.WindowsUserID(uid)
u, err := LookupUserFromID(logf, uid)
if err != nil {
return ci, fmt.Errorf("failed to look up user from userid: %w", err)
}
ci.user = u
return ci, nil
}