123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186 |
- //go:build windows
- // +build windows
- package security
- import (
- "fmt"
- "os"
- "syscall"
- "unsafe"
- )
- type (
- accessMask uint32
- accessMode uint32
- desiredAccess uint32
- inheritMode uint32
- objectType uint32
- shareMode uint32
- securityInformation uint32
- trusteeForm uint32
- trusteeType uint32
- )
- type explicitAccess struct {
- accessPermissions accessMask
- accessMode accessMode
- inheritance inheritMode
- trustee trustee
- }
- type trustee struct {
- multipleTrustee *trustee
- multipleTrusteeOperation int32
- trusteeForm trusteeForm
- trusteeType trusteeType
- name uintptr
- }
- const (
- AccessMaskNone accessMask = 0
- AccessMaskRead accessMask = 1 << 31 // GENERIC_READ
- AccessMaskWrite accessMask = 1 << 30 // GENERIC_WRITE
- AccessMaskExecute accessMask = 1 << 29 // GENERIC_EXECUTE
- AccessMaskAll accessMask = 1 << 28 // GENERIC_ALL
- accessMaskDesiredPermission = AccessMaskRead
- accessModeGrant accessMode = 1
- desiredAccessReadControl desiredAccess = 0x20000
- desiredAccessWriteDac desiredAccess = 0x40000
- gvmga = "GrantVmGroupAccess:"
- inheritModeNoInheritance inheritMode = 0x0
- inheritModeSubContainersAndObjectsInherit inheritMode = 0x3
- objectTypeFileObject objectType = 0x1
- securityInformationDACL securityInformation = 0x4
- shareModeRead shareMode = 0x1
- shareModeWrite shareMode = 0x2
- //nolint:stylecheck // ST1003
- sidVmGroup = "S-1-5-83-0"
- trusteeFormIsSid trusteeForm = 0
- trusteeTypeWellKnownGroup trusteeType = 5
- )
- // GrantVmGroupAccess sets the DACL for a specified file or directory to
- // include Grant ACE entries for the VM Group SID. This is a golang re-
- // implementation of the same function in vmcompute, just not exported in
- // RS5. Which kind of sucks. Sucks a lot :/
- func GrantVmGroupAccess(name string) error { //nolint:stylecheck // ST1003
- return GrantVmGroupAccessWithMask(name, accessMaskDesiredPermission)
- }
- // GrantVmGroupAccessWithMask sets the desired DACL for a specified file or
- // directory.
- func GrantVmGroupAccessWithMask(name string, access accessMask) error { //nolint:stylecheck // ST1003
- if access == 0 || access<<4 != 0 {
- return fmt.Errorf("invalid access mask: 0x%08x", access)
- }
- // Stat (to determine if `name` is a directory).
- s, err := os.Stat(name)
- if err != nil {
- return fmt.Errorf("%s os.Stat %s: %w", gvmga, name, err)
- }
- // Get a handle to the file/directory. Must defer Close on success.
- fd, err := createFile(name, s.IsDir())
- if err != nil {
- return err // Already wrapped
- }
- defer func() {
- _ = syscall.CloseHandle(fd)
- }()
- // Get the current DACL and Security Descriptor. Must defer LocalFree on success.
- ot := objectTypeFileObject
- si := securityInformationDACL
- sd := uintptr(0)
- origDACL := uintptr(0)
- if err := getSecurityInfo(fd, uint32(ot), uint32(si), nil, nil, &origDACL, nil, &sd); err != nil {
- return fmt.Errorf("%s GetSecurityInfo %s: %w", gvmga, name, err)
- }
- defer func() {
- _, _ = syscall.LocalFree((syscall.Handle)(unsafe.Pointer(sd)))
- }()
- // Generate a new DACL which is the current DACL with the required ACEs added.
- // Must defer LocalFree on success.
- newDACL, err := generateDACLWithAcesAdded(name, s.IsDir(), access, origDACL)
- if err != nil {
- return err // Already wrapped
- }
- defer func() {
- _, _ = syscall.LocalFree((syscall.Handle)(unsafe.Pointer(newDACL)))
- }()
- // And finally use SetSecurityInfo to apply the updated DACL.
- if err := setSecurityInfo(fd, uint32(ot), uint32(si), uintptr(0), uintptr(0), newDACL, uintptr(0)); err != nil {
- return fmt.Errorf("%s SetSecurityInfo %s: %w", gvmga, name, err)
- }
- return nil
- }
- // createFile is a helper function to call [Nt]CreateFile to get a handle to
- // the file or directory.
- func createFile(name string, isDir bool) (syscall.Handle, error) {
- namep, err := syscall.UTF16FromString(name)
- if err != nil {
- return 0, fmt.Errorf("syscall.UTF16FromString %s: %w", name, err)
- }
- da := uint32(desiredAccessReadControl | desiredAccessWriteDac)
- sm := uint32(shareModeRead | shareModeWrite)
- fa := uint32(syscall.FILE_ATTRIBUTE_NORMAL)
- if isDir {
- fa = uint32(fa | syscall.FILE_FLAG_BACKUP_SEMANTICS)
- }
- fd, err := syscall.CreateFile(&namep[0], da, sm, nil, syscall.OPEN_EXISTING, fa, 0)
- if err != nil {
- return 0, fmt.Errorf("%s syscall.CreateFile %s: %w", gvmga, name, err)
- }
- return fd, nil
- }
- // generateDACLWithAcesAdded generates a new DACL with the two needed ACEs added.
- // The caller is responsible for LocalFree of the returned DACL on success.
- func generateDACLWithAcesAdded(name string, isDir bool, desiredAccess accessMask, origDACL uintptr) (uintptr, error) {
- // Generate pointers to the SIDs based on the string SIDs
- sid, err := syscall.StringToSid(sidVmGroup)
- if err != nil {
- return 0, fmt.Errorf("%s syscall.StringToSid %s %s: %w", gvmga, name, sidVmGroup, err)
- }
- inheritance := inheritModeNoInheritance
- if isDir {
- inheritance = inheritModeSubContainersAndObjectsInherit
- }
- eaArray := []explicitAccess{
- {
- accessPermissions: desiredAccess,
- accessMode: accessModeGrant,
- inheritance: inheritance,
- trustee: trustee{
- trusteeForm: trusteeFormIsSid,
- trusteeType: trusteeTypeWellKnownGroup,
- name: uintptr(unsafe.Pointer(sid)),
- },
- },
- }
- modifiedDACL := uintptr(0)
- if err := setEntriesInAcl(uintptr(uint32(1)), uintptr(unsafe.Pointer(&eaArray[0])), origDACL, &modifiedDACL); err != nil {
- return 0, fmt.Errorf("%s SetEntriesInAcl %s: %w", gvmga, name, err)
- }
- return modifiedDACL, nil
- }
|