| package security |
| |
| import ( |
| "os" |
| "syscall" |
| "unsafe" |
| |
| "github.com/pkg/errors" |
| ) |
| |
| type ( |
| accessMask uint32 |
| accessMode uint32 |
| desiredAccess uint32 |
| inheritMode uint32 |
| objectType uint32 |
| shareMode uint32 |
| securityInformation uint32 |
| trusteeForm uint32 |
| trusteeType uint32 |
| |
| explicitAccess struct { |
| accessPermissions accessMask |
| accessMode accessMode |
| inheritance inheritMode |
| trustee trustee |
| } |
| |
| trustee struct { |
| multipleTrustee *trustee |
| multipleTrusteeOperation int32 |
| trusteeForm trusteeForm |
| trusteeType trusteeType |
| name uintptr |
| } |
| ) |
| |
| const ( |
| accessMaskDesiredPermission accessMask = 1 << 31 // GENERIC_READ |
| |
| 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 |
| |
| 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 { |
| // Stat (to determine if `name` is a directory). |
| s, err := os.Stat(name) |
| if err != nil { |
| return errors.Wrapf(err, "%s os.Stat %s", gvmga, name) |
| } |
| |
| // 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 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 errors.Wrapf(err, "%s GetSecurityInfo %s", gvmga, name) |
| } |
| defer 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(), origDACL) |
| if err != nil { |
| return err // Already wrapped |
| } |
| defer 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 errors.Wrapf(err, "%s SetSecurityInfo %s", gvmga, name) |
| } |
| |
| 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 := syscall.StringToUTF16(name) |
| 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, errors.Wrapf(err, "%s syscall.CreateFile %s", gvmga, name) |
| } |
| 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, origDACL uintptr) (uintptr, error) { |
| // Generate pointers to the SIDs based on the string SIDs |
| sid, err := syscall.StringToSid(sidVmGroup) |
| if err != nil { |
| return 0, errors.Wrapf(err, "%s syscall.StringToSid %s %s", gvmga, name, sidVmGroup) |
| } |
| |
| inheritance := inheritModeNoInheritance |
| if isDir { |
| inheritance = inheritModeSubContainersAndObjectsInherit |
| } |
| |
| eaArray := []explicitAccess{ |
| explicitAccess{ |
| accessPermissions: accessMaskDesiredPermission, |
| 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, errors.Wrapf(err, "%s SetEntriesInAcl %s", gvmga, name) |
| } |
| |
| return modifiedDACL, nil |
| } |