123 lines
3.8 KiB
Go
123 lines
3.8 KiB
Go
//go:build windows
|
|
|
|
package main
|
|
|
|
import (
|
|
"os"
|
|
|
|
"golang.org/x/sys/windows"
|
|
"golang.org/x/sys/windows/registry"
|
|
)
|
|
|
|
// The decisions are in mcpconfig_winopen.go; this file makes the calls.
|
|
|
|
// The values mcpconfig_winopen.go spells out must be Windows' own: each line
|
|
// fails to compile if one differs.
|
|
var (
|
|
_ = [1]struct{}{}[mcpWinFileTypeDisk^windows.FILE_TYPE_DISK]
|
|
_ = [1]struct{}{}[mcpWinAttrDirectory^windows.FILE_ATTRIBUTE_DIRECTORY]
|
|
_ = [1]struct{}{}[mcpWinAttrDevice^windows.FILE_ATTRIBUTE_DEVICE]
|
|
_ = [1]struct{}{}[mcpWinAttrReparse^windows.FILE_ATTRIBUTE_REPARSE_POINT]
|
|
)
|
|
|
|
var mcpAdminOwners = mcpWinAdminOwners
|
|
|
|
// mcpOpenRegular opens path with CreateFile and keeps it only when the
|
|
// handle is a regular file on disk (mcpWinOpenWith). It shares read, write
|
|
// and delete with everyone else: the agent client that owns the file must be
|
|
// able to save it, including by replacing it, while this reads it.
|
|
func mcpOpenRegular(path string) (*os.File, string, error) {
|
|
return mcpWinOpenWith(mcpWinSystemOps, path)
|
|
}
|
|
|
|
var mcpWinSystemOps = mcpWinOps{
|
|
openPath: mcpWinCreateFile,
|
|
handleInfo: mcpWinHandleFacts,
|
|
closeHandle: func(h uintptr) { windows.CloseHandle(windows.Handle(h)) },
|
|
asFile: func(h uintptr, path string) *os.File { return os.NewFile(h, path) },
|
|
}
|
|
|
|
func mcpWinCreateFile(path string) (uintptr, error) {
|
|
p, err := windows.UTF16PtrFromString(path)
|
|
if err != nil {
|
|
return 0, &os.PathError{Op: "open", Path: path, Err: err}
|
|
}
|
|
h, err := windows.CreateFile(p, windows.GENERIC_READ,
|
|
windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE|windows.FILE_SHARE_DELETE, nil,
|
|
windows.OPEN_EXISTING, windows.FILE_FLAG_OPEN_REPARSE_POINT, 0)
|
|
if err != nil {
|
|
return 0, &os.PathError{Op: "open", Path: path, Err: err}
|
|
}
|
|
return uintptr(h), nil
|
|
}
|
|
|
|
// mcpWinHandleFacts reads the type, the attributes and the owner from the
|
|
// handle. GENERIC_READ carries READ_CONTROL, which GetSecurityInfo needs for
|
|
// the owner; an owner that cannot be read is left "" and the file is then
|
|
// read only where no owner check applies.
|
|
func mcpWinHandleFacts(h uintptr) (mcpWinHandleInfo, error) {
|
|
wh := windows.Handle(h)
|
|
var i mcpWinHandleInfo
|
|
t, err := windows.GetFileType(wh)
|
|
if err != nil {
|
|
return i, err
|
|
}
|
|
i.FileType = t
|
|
if t != windows.FILE_TYPE_DISK {
|
|
return i, nil // refused by mcpWinRegular; nothing more to ask it
|
|
}
|
|
var bi windows.ByHandleFileInformation
|
|
if err := windows.GetFileInformationByHandle(wh, &bi); err != nil {
|
|
return i, err
|
|
}
|
|
i.Attributes = bi.FileAttributes
|
|
if sd, err := windows.GetSecurityInfo(wh, windows.SE_FILE_OBJECT, windows.OWNER_SECURITY_INFORMATION); err == nil {
|
|
if o, _, err := sd.Owner(); err == nil && o != nil {
|
|
i.Owner = o.String()
|
|
}
|
|
}
|
|
return i, nil
|
|
}
|
|
|
|
// mcpPathOwner is the SID whose profile dir is, from the ProfileList the
|
|
// User Profile Service keeps
|
|
// (HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\<SID>,
|
|
// value ProfileImagePath). A directory it does not name, or names twice,
|
|
// gives mcpOwnerUnknown, so only SYSTEM's and the Administrators group's
|
|
// files are read there.
|
|
func mcpPathOwner(dir string) string {
|
|
return mcpWinProfileSID(dir, mcpWinProfiles())
|
|
}
|
|
|
|
func mcpWinProfiles() map[string]string {
|
|
out := map[string]string{}
|
|
k, err := registry.OpenKey(registry.LOCAL_MACHINE,
|
|
`SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList`, registry.ENUMERATE_SUB_KEYS)
|
|
if err != nil {
|
|
return out
|
|
}
|
|
defer k.Close()
|
|
sids, err := k.ReadSubKeyNames(-1)
|
|
if err != nil {
|
|
return out
|
|
}
|
|
for _, sid := range sids {
|
|
sk, err := registry.OpenKey(k, sid, registry.QUERY_VALUE)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
p, typ, err := sk.GetStringValue("ProfileImagePath")
|
|
sk.Close()
|
|
if err != nil {
|
|
continue
|
|
}
|
|
if typ == registry.EXPAND_SZ {
|
|
if x, err := registry.ExpandString(p); err == nil {
|
|
p = x
|
|
}
|
|
}
|
|
out[sid] = p
|
|
}
|
|
return out
|
|
}
|