github.com/Serizao/go-winio@v0.0.0-20230906082528-f02f7f4ad6e8/pkg/security/grantvmgroupaccess.go (about) 1 //go:build windows 2 // +build windows 3 4 package security 5 6 import ( 7 "fmt" 8 "os" 9 "unsafe" 10 11 "golang.org/x/sys/windows" 12 ) 13 14 type ( 15 accessMask uint32 16 accessMode uint32 17 desiredAccess uint32 18 inheritMode uint32 19 objectType uint32 20 shareMode uint32 21 securityInformation uint32 22 trusteeForm uint32 23 trusteeType uint32 24 25 explicitAccess struct { 26 accessPermissions accessMask 27 accessMode accessMode 28 inheritance inheritMode 29 trustee trustee 30 } 31 32 trustee struct { 33 multipleTrustee *trustee 34 multipleTrusteeOperation int32 35 trusteeForm trusteeForm 36 trusteeType trusteeType 37 name uintptr 38 } 39 ) 40 41 const ( 42 accessMaskDesiredPermission accessMask = 1 << 31 // GENERIC_READ 43 44 accessModeGrant accessMode = 1 45 46 desiredAccessReadControl desiredAccess = 0x20000 47 desiredAccessWriteDac desiredAccess = 0x40000 48 49 //cspell:disable-next-line 50 gvmga = "GrantVmGroupAccess:" 51 52 inheritModeNoInheritance inheritMode = 0x0 53 inheritModeSubContainersAndObjectsInherit inheritMode = 0x3 54 55 objectTypeFileObject objectType = 0x1 56 57 securityInformationDACL securityInformation = 0x4 58 59 shareModeRead shareMode = 0x1 60 shareModeWrite shareMode = 0x2 61 62 sidVMGroup = "S-1-5-83-0" 63 64 trusteeFormIsSID trusteeForm = 0 65 66 trusteeTypeWellKnownGroup trusteeType = 5 67 ) 68 69 // GrantVMGroupAccess sets the DACL for a specified file or directory to 70 // include Grant ACE entries for the VM Group SID. This is a golang re- 71 // implementation of the same function in vmcompute, just not exported in 72 // RS5. Which kind of sucks. Sucks a lot :/ 73 // 74 //revive:disable-next-line:var-naming VM, not Vm 75 func GrantVmGroupAccess(name string) error { 76 // Stat (to determine if `name` is a directory). 77 s, err := os.Stat(name) 78 if err != nil { 79 return fmt.Errorf("%s os.Stat %s: %w", gvmga, name, err) 80 } 81 82 // Get a handle to the file/directory. Must defer Close on success. 83 fd, err := createFile(name, s.IsDir()) 84 if err != nil { 85 return err // Already wrapped 86 } 87 defer windows.CloseHandle(fd) //nolint:errcheck 88 89 // Get the current DACL and Security Descriptor. Must defer LocalFree on success. 90 ot := objectTypeFileObject 91 si := securityInformationDACL 92 sd := uintptr(0) 93 origDACL := uintptr(0) 94 if err := getSecurityInfo(fd, uint32(ot), uint32(si), nil, nil, &origDACL, nil, &sd); err != nil { 95 return fmt.Errorf("%s GetSecurityInfo %s: %w", gvmga, name, err) 96 } 97 defer windows.LocalFree(windows.Handle(sd)) //nolint:errcheck 98 99 // Generate a new DACL which is the current DACL with the required ACEs added. 100 // Must defer LocalFree on success. 101 newDACL, err := generateDACLWithAcesAdded(name, s.IsDir(), origDACL) 102 if err != nil { 103 return err // Already wrapped 104 } 105 defer windows.LocalFree(windows.Handle(newDACL)) //nolint:errcheck 106 107 // And finally use SetSecurityInfo to apply the updated DACL. 108 if err := setSecurityInfo(fd, uint32(ot), uint32(si), uintptr(0), uintptr(0), newDACL, uintptr(0)); err != nil { 109 return fmt.Errorf("%s SetSecurityInfo %s: %w", gvmga, name, err) 110 } 111 112 return nil 113 } 114 115 // createFile is a helper function to call [Nt]CreateFile to get a handle to 116 // the file or directory. 117 func createFile(name string, isDir bool) (windows.Handle, error) { 118 namep, err := windows.UTF16FromString(name) 119 if err != nil { 120 return windows.InvalidHandle, fmt.Errorf("could not convernt name to UTF-16: %w", err) 121 } 122 da := uint32(desiredAccessReadControl | desiredAccessWriteDac) 123 sm := uint32(shareModeRead | shareModeWrite) 124 fa := uint32(windows.FILE_ATTRIBUTE_NORMAL) 125 if isDir { 126 fa |= windows.FILE_FLAG_BACKUP_SEMANTICS 127 } 128 fd, err := windows.CreateFile(&namep[0], da, sm, nil, windows.OPEN_EXISTING, fa, 0) 129 if err != nil { 130 return windows.InvalidHandle, fmt.Errorf("%s windows.CreateFile %s: %w", gvmga, name, err) 131 } 132 return fd, nil 133 } 134 135 // generateDACLWithAcesAdded generates a new DACL with the two needed ACEs added. 136 // The caller is responsible for LocalFree of the returned DACL on success. 137 func generateDACLWithAcesAdded(name string, isDir bool, origDACL uintptr) (uintptr, error) { 138 // Generate pointers to the SIDs based on the string SIDs 139 sid, err := windows.StringToSid(sidVMGroup) 140 if err != nil { 141 return 0, fmt.Errorf("%s windows.StringToSid %s %s: %w", gvmga, name, sidVMGroup, err) 142 } 143 144 inheritance := inheritModeNoInheritance 145 if isDir { 146 inheritance = inheritModeSubContainersAndObjectsInherit 147 } 148 149 eaArray := []explicitAccess{ 150 { 151 accessPermissions: accessMaskDesiredPermission, 152 accessMode: accessModeGrant, 153 inheritance: inheritance, 154 trustee: trustee{ 155 trusteeForm: trusteeFormIsSID, 156 trusteeType: trusteeTypeWellKnownGroup, 157 name: uintptr(unsafe.Pointer(sid)), 158 }, 159 }, 160 } 161 162 modifiedDACL := uintptr(0) 163 if err := setEntriesInAcl(uintptr(uint32(1)), uintptr(unsafe.Pointer(&eaArray[0])), origDACL, &modifiedDACL); err != nil { 164 return 0, fmt.Errorf("%s SetEntriesInAcl %s: %w", gvmga, name, err) 165 } 166 167 return modifiedDACL, nil 168 }