Files
privatae-bastion/client-go/privilege_windows.go
T
Privatae e4c9b69b46
ci / personal-data (push) Successful in 5s
ci / test (push) Successful in 3m4s
ci / fuzz (push) Successful in 3m45s
bastion 0.53.2
2026-10-20 04:42:14 +00:00

93 lines
4.1 KiB
Go

//go:build windows
package main
// WHO STANDS BEHIND THE ANCHOR, ON WINDOWS.
//
// Every machine-authority decision in this binary asks one question: does the
// caller administer this box? On Unix that is euid 0. On Windows there is no
// euid at all — Go's syscall package answers `Geteuid() (euid int) { return -1 }`
// unconditionally (syscall_windows.go), so the historic `os.Geteuid() != 0` gate
// was not "needs admin" on Windows, it was PERMANENTLY CLOSED. Running as
// Administrator made no difference; nothing could ever satisfy it.
//
// That silently made three things impossible on every Windows install:
// `set-mode networked`, `claim-anchor`, and `claim-owner`. A Windows machine
// could therefore never be a networked bastion and never be anchored — so the
// whole anchor-governed model (a machine several hosts share, later pairings
// held pending until the anchor approves) was Linux-only by accident rather than
// by decision.
//
// The Windows equivalent of "root on this machine" is a process that is BOTH
// running elevated AND whose token carries the local Administrators group. Both
// halves are needed and neither implies the other:
//
// - IsElevated alone would accept a token that was elevated in some other
// context but is not an administrator of THIS machine.
// - IsMember alone would accept an un-elevated admin user. On such a token the
// Administrators group is present but DENY-only, which is exactly the UAC
// split-token state — the user COULD administer the box but this process is
// not doing so. Treating that as authority would let any admin-account
// process anchor a machine without ever passing a UAC prompt, which is the
// consent step that makes "root stands behind the anchor" mean anything.
//
// LocalSystem (a service) satisfies both, which is correct: a machine-wide
// service is exactly the networked bastion's shape.
import (
"fmt"
"golang.org/x/sys/windows"
)
// processIsRoot reports whether this process administers the machine.
//
// Fails CLOSED: any error asking the question answers "no". An authority check
// that cannot be completed has not been passed.
func processIsRoot() bool {
ok, err := machineAuthority()
if err != nil {
// SAY SO. A silent "no" here is the exact bug this file exists to fix,
// one layer down: an administrator would read "run as Administrator"
// while already in an elevated terminal, with nothing anywhere saying
// the check itself failed to run.
fmt.Printf("privilege: cannot determine machine authority on this system: %v\n", err)
return false
}
return ok
}
// machineAuthority is the check itself, with its failure kept distinguishable
// from a plain "no".
func machineAuthority() (bool, error) {
// TokenElevation off the process pseudo-handle is a GetTokenInformation
// read, which a PRIMARY token answers fine.
if !windows.GetCurrentProcessToken().IsElevated() {
return false, nil
}
sid, err := windows.CreateWellKnownSid(windows.WinBuiltinAdministratorsSid)
if err != nil {
return false, fmt.Errorf("cannot build the Administrators SID: %w", err)
}
// Token(0) — a NULL handle — NOT GetCurrentProcessToken().
//
// IsMember hands its receiver straight to CheckTokenMembership
// (x/sys@v0.46.0/windows/security_windows.go:832-838), and that API takes an
// IMPERSONATION token; a primary token is refused with
// ERROR_NO_IMPERSONATION_TOKEN. GetCurrentProcessToken returns the PRIMARY
// token's pseudo-handle (:664-669) — so asking it this way would have
// errored for a genuine elevated administrator and, under the fail-closed
// rule above, answered "no". The gate this whole change exists to OPEN would
// have stayed shut, indistinguishably from a real refusal.
//
// Passing NULL is the documented way to avoid that: the API duplicates the
// calling thread's token into an impersonation token itself. It is also what
// .NET's WindowsIdentity.IsInRole does internally, which is why the
// PowerShell check in install.ps1 works while this one would not have.
member, err := windows.Token(0).IsMember(sid)
if err != nil {
return false, fmt.Errorf("CheckTokenMembership failed: %w", err)
}
return member, nil
}