93 lines
4.1 KiB
Go
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
|
|
}
|