...

Source file src/github.com/Microsoft/go-winio/pkg/security/grantvmgroupaccess.go

Documentation: github.com/Microsoft/go-winio/pkg/security

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

View as plain text