bastion 0.53.2
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
package main
|
||||
|
||||
// The decisions behind the Windows drop (freeagent_drop_windows.go), kept in an
|
||||
// untagged file so the suite checks them on every platform. The Windows calls
|
||||
// that gather their inputs are in the tagged file.
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf16"
|
||||
)
|
||||
|
||||
// winSidTypeUser is SidTypeUser: the SID names a person's account rather than
|
||||
// a group, an alias or a well-known identity.
|
||||
const winSidTypeUser = 1
|
||||
|
||||
// faWinOwnerRefusal says why a Windows account may NOT run a folder's agent,
|
||||
// or "" when it may. It is the Windows reading of the unix rule (root, nobody,
|
||||
// no account): the agent runs as the person who owns the folder, never as one
|
||||
// of the machine's own identities, a group, or an account anyone can use.
|
||||
//
|
||||
// - SYSTEM, LOCAL SERVICE, NETWORK SERVICE, the service and virtual accounts
|
||||
// (S-1-5-80, -82, -83, -90, -96) and the BUILTIN groups (S-1-5-32, which is
|
||||
// where Administrators is, and the usual owner of a folder an elevated
|
||||
// administrator made) are the machine's identities, not a person.
|
||||
// - Only a user account issued by the machine or its domain (S-1-5-21-) or by
|
||||
// Microsoft Entra ID (S-1-12-1-) is a person.
|
||||
// - The built-in Administrator (RID 500) runs with full rights and no UAC
|
||||
// split by default, so an agent under its token would be the machine's
|
||||
// administrator: the root case. The built-in Guest (RID 501) is shared.
|
||||
// Every RID below 1000 is reserved for a built-in account the machine or
|
||||
// its domain made (krbtgt 502, DefaultAccount 503, WDAGUtilityAccount 504,
|
||||
// and the rest); a person's account is issued a RID of 1000 or more. None
|
||||
// of the reserved ones is a person, so all are refused.
|
||||
func faWinOwnerRefusal(sid string, sidType uint32, name string) string {
|
||||
grant := " — grant a folder owned by the user who should read it"
|
||||
switch sid {
|
||||
case "S-1-5-18":
|
||||
return "refusing to run the agent as SYSTEM: the granted folder is owned by the system" + grant
|
||||
case "S-1-5-19", "S-1-5-20":
|
||||
return "refusing to run the agent as a service account: the granted folder is owned by " + name + grant
|
||||
}
|
||||
for _, p := range []string{"S-1-5-32-", "S-1-5-80-", "S-1-5-82-", "S-1-5-83-", "S-1-5-90-", "S-1-5-96-"} {
|
||||
if strings.HasPrefix(sid, p) {
|
||||
return "refusing to run the agent as " + name + ": the granted folder is owned by one of the machine's own identities, not a person" + grant
|
||||
}
|
||||
}
|
||||
person := strings.HasPrefix(sid, "S-1-5-21-") || strings.HasPrefix(sid, "S-1-12-1-")
|
||||
if !person || sidType != winSidTypeUser {
|
||||
return "refusing to run the agent as " + name + ": the granted folder's owner is not a person's account" + grant
|
||||
}
|
||||
if strings.HasPrefix(sid, "S-1-5-21-") {
|
||||
if rid, err := strconv.Atoi(sid[strings.LastIndex(sid, "-")+1:]); err == nil && rid < 1000 {
|
||||
switch rid {
|
||||
case 500:
|
||||
return "refusing to run the agent as the built-in Administrator: it runs with the machine's full rights" + grant
|
||||
case 501:
|
||||
return "refusing to run the agent as the built-in Guest: anyone may use it" + grant
|
||||
default:
|
||||
return "refusing to run the agent as " + name + ": the granted folder is owned by a built-in account, not a person" + grant
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// winJoin joins Windows path parts with a backslash, whatever platform this
|
||||
// runs on, so the environment below is tested off Windows.
|
||||
func winJoin(parts ...string) string {
|
||||
out := strings.TrimRight(parts[0], `\`)
|
||||
for _, p := range parts[1:] {
|
||||
out += `\` + strings.Trim(p, `\`)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// faWinDropEnv is what a Windows child is told about who it is, and which of
|
||||
// the service's variables it keeps. The service runs as LocalSystem, so its
|
||||
// USERPROFILE, APPDATA, LOCALAPPDATA and TEMP all name LocalSystem's profile,
|
||||
// which the owner cannot open; they are replaced with the owner's own. A
|
||||
// Python child on Windows also needs SystemRoot to open a socket, so that is
|
||||
// kept, with the other machine-wide variables that say nothing about the
|
||||
// account.
|
||||
func faWinDropEnv(user, profile, stateDir string) (set []string, passthrough []string) {
|
||||
home := profile
|
||||
temp := stateDir
|
||||
if profile != "" {
|
||||
temp = winJoin(profile, `AppData\Local\Temp`)
|
||||
} else {
|
||||
home = stateDir
|
||||
}
|
||||
set = []string{"USERNAME=" + user, "HOME=" + home, "USERPROFILE=" + home, "TEMP=" + temp, "TMP=" + temp}
|
||||
if profile != "" {
|
||||
set = append(set, "APPDATA="+winJoin(profile, `AppData\Roaming`), "LOCALAPPDATA="+winJoin(profile, `AppData\Local`))
|
||||
}
|
||||
passthrough = []string{"PATH", "SystemRoot", "SYSTEMROOT", "windir", "WINDIR", "ComSpec", "PATHEXT",
|
||||
"PROCESSOR_ARCHITECTURE", "NUMBER_OF_PROCESSORS", "LANG", "LC_ALL"}
|
||||
return set, passthrough
|
||||
}
|
||||
|
||||
// faWinEnvBlock is the environment block CreateProcessAsUser takes: each
|
||||
// NAME=value NUL-terminated, the block ended by one more NUL, sorted by name
|
||||
// without regard to case as Windows requires. A later duplicate of a name
|
||||
// (in any case) wins, and the drive-letter entries that begin with '=' are
|
||||
// left out.
|
||||
func faWinEnvBlock(env []string) []uint16 {
|
||||
byName := map[string]string{}
|
||||
for _, kv := range env {
|
||||
i := strings.IndexByte(kv, '=')
|
||||
if i <= 0 {
|
||||
continue
|
||||
}
|
||||
byName[strings.ToUpper(kv[:i])] = kv
|
||||
}
|
||||
keys := make([]string, 0, len(byName))
|
||||
for k := range byName {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
var out []uint16
|
||||
for _, k := range keys {
|
||||
out = append(out, utf16.Encode([]rune(byName[k]))...)
|
||||
out = append(out, 0)
|
||||
}
|
||||
if len(out) == 0 {
|
||||
out = append(out, 0)
|
||||
}
|
||||
return append(out, 0)
|
||||
}
|
||||
Reference in New Issue
Block a user