mirror of
https://github.com/netbirdio/netbird.git
synced 2026-08-04 19:55:09 -04:00
88 lines
3.1 KiB
Go
88 lines
3.1 KiB
Go
//go:build windows
|
|
|
|
package ipcauth
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
"golang.org/x/sys/windows"
|
|
)
|
|
|
|
// PipeServerTrusted reports an error unless the pipe behind conn was created by a
|
|
// principal this client may hand secrets to. Clients call it for a pipe whose name
|
|
// carries no guarantee of its own, which is any name outside the
|
|
// ProtectedPrefix\Administrators namespace: that namespace already restricts
|
|
// creation to administrators and LocalSystem, while a plain name can be created by
|
|
// any local user before the daemon gets there.
|
|
//
|
|
// The decision is made from the pipe object's owner, not from the serving process,
|
|
// because a client cannot open a process running as another user at all, and the
|
|
// legitimate case is precisely an unprivileged client talking to a privileged
|
|
// daemon. Trusted owners are the service accounts, BUILTIN\Administrators, and
|
|
// this client's own user, the last of which is the daemon a user runs themselves
|
|
// as in netstack mode. A pipe owned by anyone else gets no setup key, pre-shared
|
|
// key or SSO prompt out of this client.
|
|
func PipeServerTrusted(conn net.Conn) error {
|
|
// go-winio's pipe connection embeds *win32File, which exposes Fd().
|
|
fdConn, ok := conn.(interface{ Fd() uintptr })
|
|
if !ok {
|
|
return fmt.Errorf("connection %T does not expose a pipe handle", conn)
|
|
}
|
|
|
|
owner, err := pipeOwnerSID(windows.Handle(fdConn.Fd()))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !trustedPipeOwner(owner) {
|
|
return fmt.Errorf("pipe owned by %s, which is neither an administrator nor this user", owner)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// PipeOwnedBySelf reports whether the pipe behind conn was created by this very
|
|
// user, which is how a client recognises a daemon running as itself. Ownership it
|
|
// cannot read is reported as false.
|
|
func PipeOwnedBySelf(conn net.Conn) bool {
|
|
fdConn, ok := conn.(interface{ Fd() uintptr })
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
owner, err := pipeOwnerSID(windows.Handle(fdConn.Fd()))
|
|
if err != nil {
|
|
log.Debugf("read daemon pipe owner: %v", err)
|
|
return false
|
|
}
|
|
return selfKnown && selfIdentity.SID != "" && owner == selfIdentity.SID
|
|
}
|
|
|
|
// pipeOwnerSID reads the owner of the pipe object a client is connected to. The
|
|
// handle was opened with GENERIC_READ, which includes READ_CONTROL, so no extra
|
|
// access is needed.
|
|
func pipeOwnerSID(handle windows.Handle) (string, error) {
|
|
sd, err := windows.GetSecurityInfo(handle, windows.SE_KERNEL_OBJECT, windows.OWNER_SECURITY_INFORMATION)
|
|
if err != nil {
|
|
return "", fmt.Errorf("read pipe security info: %w", err)
|
|
}
|
|
|
|
owner, _, err := sd.Owner()
|
|
if err != nil {
|
|
return "", fmt.Errorf("read pipe owner: %w", err)
|
|
}
|
|
return owner.String(), nil
|
|
}
|
|
|
|
// trustedPipeOwner reports whether a pipe's owner is a principal a client may
|
|
// speak to. An elevated process's objects are owned by BUILTIN\Administrators by
|
|
// default, an unelevated one's by the user, which is why both forms appear here.
|
|
func trustedPipeOwner(owner string) bool {
|
|
switch owner {
|
|
case sidLocalSystem, sidLocalService, sidNetworkService, sidAdministrators:
|
|
return true
|
|
}
|
|
return selfKnown && selfIdentity.SID != "" && owner == selfIdentity.SID
|
|
}
|