grantvmgroupaccess.go 4.7 KB

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