Browse Source

Merge remote-tracking branch 'zb140/get-access-mode'

master
forest 2 months ago
parent
commit
99c155a464
4 changed files with 286 additions and 7 deletions
  1. +63
    -1
      api/acl.go
  2. +7
    -6
      chmod.go
  3. +49
    -0
      constants.go
  4. +167
    -0
      getaccessmode.go

+ 63
- 1
api/acl.go View File

@ -63,7 +63,9 @@ const (
)
var (
procSetEntriesInAclW = advapi32.MustFindProc("SetEntriesInAclW")
procSetEntriesInAclW = advapi32.MustFindProc("SetEntriesInAclW")
procGetEffectiveRightsFromAclW = advapi32.MustFindProc("GetEffectiveRightsFromAclW")
procGetExplicitEntriesFromAclW = advapi32.MustFindProc("GetExplicitEntriesFromAclW")
)
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa379636.aspx
@ -96,3 +98,63 @@ func SetEntriesInAcl(entries []ExplicitAccess, oldAcl windows.Handle, newAcl *wi
}
return nil
}
func GetEffectiveRightsFromAcl(oldAcl windows.Handle, sid *windows.SID) (uint32, error) {
trustee := Trustee{
TrusteeForm: TRUSTEE_IS_SID,
Name: (*uint16)(unsafe.Pointer(sid)),
}
var rights uint32
ret, _, err := procGetEffectiveRightsFromAclW.Call(
uintptr(oldAcl),
uintptr(unsafe.Pointer(&trustee)),
uintptr(unsafe.Pointer(&rights)),
)
if ret != 0 {
return 0, err
}
return rights, nil
}
func GetExplicitEntriesFromAcl(oldAcl windows.Handle) ([]ExplicitAccess, error) {
var (
count uint32
list uintptr
)
/* TODO: seems like I ought to be able to something like this:
var entries *[]ExplicitAccess
ret, _, err := procGetExplicitEntriesFromAclW.Call(
...,
uintptr(unsafe.Pointer(&entries)),
)
but I couldn't figure out how to make it work. I tried a whole
bunch of different combinations but I only ever managed to get an empty list
*/
ret, _, err := procGetExplicitEntriesFromAclW.Call(
uintptr(oldAcl),
uintptr(unsafe.Pointer(&count)),
uintptr(unsafe.Pointer(&list)),
)
if ret != 0 {
return []ExplicitAccess{}, err
}
defer windows.LocalFree(windows.Handle(unsafe.Pointer(list)))
explicitAccessSize := unsafe.Sizeof(ExplicitAccess{})
getEntryAtOffset := func(list uintptr, offset uint32) ExplicitAccess {
return *(*ExplicitAccess)(unsafe.Pointer(list + explicitAccessSize*uintptr(offset)))
}
output := make([]ExplicitAccess, count)
for i := uint32(0); i < count; i++ {
output[i] = getEntryAtOffset(list, i)
}
return output, nil
}

+ 7
- 6
chmod.go View File

@ -13,15 +13,15 @@ import (
// file's group, and everyone else to be explicitly controlled.
func Chmod(name string, fileMode os.FileMode) error {
// https://support.microsoft.com/en-us/help/243330/well-known-security-identifiers-in-windows-operating-systems
creatorOwnerSID, err := windows.StringToSid("S-1-3-0")
creatorOwnerSID, err := windows.StringToSid(SID_NAME_CREATOR_OWNER)
if err != nil {
return err
}
creatorGroupSID, err := windows.StringToSid("S-1-3-1")
creatorGroupSID, err := windows.StringToSid(SID_NAME_CREATOR_GROUP)
if err != nil {
return err
}
everyoneSID, err := windows.StringToSid("S-1-1-0")
everyoneSID, err := windows.StringToSid(SID_NAME_EVERYONE)
if err != nil {
return err
}
@ -31,8 +31,9 @@ func Chmod(name string, fileMode os.FileMode) error {
name,
true,
false,
GrantSid(((mode&0700)<<23)|((mode&0200)<<9), creatorOwnerSID),
GrantSid(((mode&0070)<<26)|((mode&0020)<<12), creatorGroupSID),
GrantSid(((mode&0007)<<29)|((mode&0002)<<15), everyoneSID),
// explicitly granting SYNCHRONIZE allows later detection of when a 0 mode was specified
GrantSid(((mode&0700)<<23)|((mode&0200)<<9)|SYNCHRONIZE, creatorOwnerSID),
GrantSid(((mode&0070)<<26)|((mode&0020)<<12)|SYNCHRONIZE, creatorGroupSID),
GrantSid(((mode&0007)<<29)|((mode&0002)<<15)|SYNCHRONIZE, everyoneSID),
)
}

+ 49
- 0
constants.go View File

@ -0,0 +1,49 @@
package acl
import (
"golang.org/x/sys/windows"
)
const (
SID_NAME_CREATOR_OWNER = "S-1-3-0"
SID_NAME_CREATOR_GROUP = "S-1-3-1"
SID_NAME_EVERYONE = "S-1-1-0"
)
// access mask constants from https://docs.microsoft.com/en-us/windows/desktop/wmisdk/file-and-directory-access-rights-constants
// the x/sys/windows package defines some but not all of these constants
const FILE_READ_DATA = windows.FILE_LIST_DIRECTORY // for a directory, the ability to list contents
// the windows package only has this by the "LIST_DIRECTORY" name
const FILE_WRITE_DATA = 0x02 // for a directory, the ability to add a file
const FILE_APPEND_DATA = windows.FILE_APPEND_DATA // for a directory, the ability to add a subdirectory
const FILE_READ_EA = 0x08
const FILE_WRITE_EA = 0x10
const FILE_EXECUTE = 0x20 // for a directory, the ability to traverse
const FILE_READ_ATTRIBUTES = 0x80
const FILE_WRITE_ATTRIBUTES = windows.FILE_WRITE_ATTRIBUTES
const DELETE = 0x10000
const SYNCHRONIZE = windows.SYNCHRONIZE
// these correspond to the GENERIC permissions from https://docs.microsoft.com/en-us/windows/desktop/FileIO/file-security-and-access-rights
// except that PERM_WRITE has DELETE added to it because otherwise it would be impossible to delete or rename a file.
const PERM_READ uint32 = 0 |
FILE_READ_ATTRIBUTES |
FILE_READ_DATA |
FILE_READ_EA |
windows.STANDARD_RIGHTS_READ |
SYNCHRONIZE
const PERM_WRITE uint32 = 0 |
FILE_APPEND_DATA |
FILE_WRITE_ATTRIBUTES |
FILE_WRITE_DATA |
FILE_WRITE_EA |
windows.STANDARD_RIGHTS_WRITE |
SYNCHRONIZE
const PERM_EXECUTE uint32 = 0 |
FILE_EXECUTE |
FILE_READ_ATTRIBUTES |
windows.STANDARD_RIGHTS_EXECUTE |
SYNCHRONIZE

+ 167
- 0
getaccessmode.go View File

@ -0,0 +1,167 @@
package acl
import (
"os"
"path/filepath"
"unsafe"
"github.com/hectane/go-acl/api"
"golang.org/x/sys/windows"
)
func getEffectiveRightsForSid(oldAcl windows.Handle, sid *windows.SID) (uint32, error) {
return api.GetEffectiveRightsFromAcl(oldAcl, sid)
}
func getEffectiveRightsForSidName(oldAcl windows.Handle, sidName string) (uint32, error) {
sid, err := windows.StringToSid(sidName)
if err != nil {
return 0, err
}
return getEffectiveRightsForSid(oldAcl, sid)
}
func getAccessModeForRights(rights uint32) uint32 {
var ret uint32
if rights&PERM_READ == PERM_READ {
ret |= 04
}
if rights&PERM_WRITE == PERM_WRITE {
ret |= 02
}
if rights&PERM_EXECUTE == PERM_EXECUTE {
ret |= 01
}
return ret
}
func GetExplicitAccessMode(name string) (os.FileMode, error) {
var (
oldAcl windows.Handle
secDesc windows.Handle
owner *windows.SID
group *windows.SID
)
path, err := filepath.Abs(name)
if err != nil {
return os.FileMode(0), err
}
err = api.GetNamedSecurityInfo(
path,
api.SE_FILE_OBJECT,
api.DACL_SECURITY_INFORMATION|
api.OWNER_SECURITY_INFORMATION|
api.GROUP_SECURITY_INFORMATION,
&owner,
&group,
&oldAcl,
nil,
&secDesc,
)
if err != nil {
return os.FileMode(0), err
}
defer windows.LocalFree(secDesc)
ownerName, err := owner.String()
if err != nil {
return os.FileMode(0), err
}
groupName, err := group.String()
if err != nil {
return os.FileMode(0), err
}
entries, err := api.GetExplicitEntriesFromAcl(oldAcl)
if err != nil {
return os.FileMode(0), err
}
var mode uint32
if len(entries) > 0 {
for _, item := range entries {
if item.AccessMode == api.GRANT_ACCESS && item.Trustee.TrusteeForm == api.TRUSTEE_IS_SID {
trustee := (*windows.SID)(unsafe.Pointer(item.Trustee.Name))
name, err := trustee.String()
if err != nil {
continue
}
switch name {
case ownerName:
mode |= (getAccessModeForRights(item.AccessPermissions) << 6)
case groupName:
mode |= (getAccessModeForRights(item.AccessPermissions) << 3)
case SID_NAME_EVERYONE:
mode |= getAccessModeForRights(item.AccessPermissions)
}
}
}
}
return os.FileMode(mode), nil
}
func GetEffectiveAccessMode(name string) (os.FileMode, error) {
// get the file's current ACL
var (
oldAcl windows.Handle
secDesc windows.Handle
owner *windows.SID
group *windows.SID
)
path, err := filepath.Abs(name)
if err != nil {
return os.FileMode(0), err
}
err = api.GetNamedSecurityInfo(
path,
api.SE_FILE_OBJECT,
api.DACL_SECURITY_INFORMATION|
api.OWNER_SECURITY_INFORMATION|
api.GROUP_SECURITY_INFORMATION,
&owner,
&group,
&oldAcl,
nil,
&secDesc,
)
if err != nil {
return os.FileMode(0), err
}
defer windows.LocalFree(secDesc)
ownerRights, err := getEffectiveRightsForSid(oldAcl, owner)
if err != nil {
return os.FileMode(0), err
}
groupRights, err := getEffectiveRightsForSid(oldAcl, group)
if err != nil {
return os.FileMode(0), err
}
everyoneRights, err := getEffectiveRightsForSidName(oldAcl, SID_NAME_EVERYONE)
if err != nil {
return os.FileMode(0), err
}
mode := os.FileMode(
getAccessModeForRights(ownerRights)<<6 |
getAccessModeForRights(groupRights)<<3 |
getAccessModeForRights(everyoneRights)<<0)
return mode, nil
}

Loading…
Cancel
Save