grantvmgroupaccess.go 5.5 KB

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