Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adds Windows GetAce syscall wrapper #191

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
40 changes: 40 additions & 0 deletions windows/security_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -1086,6 +1086,27 @@ type EXPLICIT_ACCESS struct {
Trustee TRUSTEE
}

// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-ace_header
type ACE_HEADER struct {
AceType uint8
AceFlags uint8
AceSize uint16
}

// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-access_allowed_ace
type ACCESS_ALLOWED_ACE struct {
Header ACE_HEADER
Mask ACCESS_MASK
Sid SID
}

const (
// Constants for AceType
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-ace_header
ACCESS_ALLOWED_ACE_TYPE = 0
ACCESS_DENIED_ACE_TYPE = 1
)

// This type is the union inside of TRUSTEE and must be created using one of the TrusteeValueFrom* functions.
type TrusteeValue uintptr

Expand Down Expand Up @@ -1157,6 +1178,12 @@ type OBJECTS_AND_NAME struct {
//sys makeSelfRelativeSD(absoluteSD *SECURITY_DESCRIPTOR, selfRelativeSD *SECURITY_DESCRIPTOR, selfRelativeSDSize *uint32) (err error) = advapi32.MakeSelfRelativeSD

//sys setEntriesInAcl(countExplicitEntries uint32, explicitEntries *EXPLICIT_ACCESS, oldACL *ACL, newACL **ACL) (ret error) = advapi32.SetEntriesInAclW
//sys getAceFromAcl(acl *ACL, aceIndex uint32, pAce **ACCESS_ALLOWED_ACE) (ret error) = advapi32.GetAce

// ACECount returns the number of ACEs an ACL has.
func (acl *ACL) ACECount() uint16 {
return acl.aceCount
}

// Control returns the security descriptor control bits.
func (sd *SECURITY_DESCRIPTOR) Control() (control SECURITY_DESCRIPTOR_CONTROL, revision uint32, err error) {
Expand Down Expand Up @@ -1433,3 +1460,16 @@ func ACLFromEntries(explicitEntries []EXPLICIT_ACCESS, mergedACL *ACL) (acl *ACL
copy(aclBytes, (*[(1 << 31) - 1]byte)(unsafe.Pointer(winHeapACL))[:len(aclBytes):len(aclBytes)])
return (*ACL)(unsafe.Pointer(&aclBytes[0])), nil
}

// GetACEFromACL returns the access control entry from the given index associated with the given ACL.
// https://learn.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-getace
func GetACEFromACL(acl *ACL, index uint32) (ace *ACCESS_ALLOWED_ACE, err error) {
err = getAceFromAcl(acl, index, &ace)
if err != nil {
return nil, err
}

aceBytes := make([]byte, ace.Header.AceSize)
copy(aceBytes, (*[(1 << 31) - 1]byte)(unsafe.Pointer(ace))[:len(aceBytes):len(aceBytes)])
return (*ACCESS_ALLOWED_ACE)(unsafe.Pointer(&aceBytes[0])), nil
}
165 changes: 165 additions & 0 deletions windows/syscall_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,171 @@ func TestBuildSecurityDescriptor(t *testing.T) {
}
}

// getEntriesFromACL returns a list of explicit access control entries associated with the given ACL.
func getEntriesFromACL(acl *windows.ACL) (aces []*windows.ACCESS_ALLOWED_ACE, err error) {
aces = make([]*windows.ACCESS_ALLOWED_ACE, acl.ACECount())

for i := uint16(0); i < acl.AceCount(); i++ {
// aceCount is a word, but aceIndex is a double word.
aces[i], err = windows.GetACEFromACL(acl, uint32(i))
if err != nil {
return []*windows.ACCESS_ALLOWED_ACE{}, err
}
}

return aces, nil
}

func TestGetACEsFromACL(t *testing.T) {
// Create a temporary file to set ACLs on and test getting the ACEs from the ACL.
f, err := os.CreateTemp("", "foo.lish")
defer os.Remove(f.Name())

f.Close()

// Well-known SID Strings:
// https://support.microsoft.com/en-us/help/243330/well-known-security-identifiers-in-windows-operating-systems
ownerSid, err := windows.StringToSid("S-1-3-2")
if err != nil {
t.Fatal(err)
}
groupSid, err := windows.StringToSid("S-1-3-3")
if err != nil {
t.Fatal(err)
}
worldSid, err := windows.StringToSid("S-1-1-0")
if err != nil {
t.Fatal(err)
}

ownerPermissions := windows.ACCESS_MASK(windows.GENERIC_ALL)
groupPermissions := windows.ACCESS_MASK(windows.GENERIC_READ | windows.GENERIC_EXECUTE)
worldPermissions := windows.ACCESS_MASK(windows.GENERIC_READ)

access := []windows.EXPLICIT_ACCESS{
{
AccessPermissions: ownerPermissions,
AccessMode: windows.GRANT_ACCESS,
Trustee: windows.TRUSTEE{
TrusteeForm: windows.TRUSTEE_IS_SID,
TrusteeValue: windows.TrusteeValueFromSID(ownerSid),
},
},
{
AccessPermissions: groupPermissions,
AccessMode: windows.GRANT_ACCESS,
Trustee: windows.TRUSTEE{
TrusteeForm: windows.TRUSTEE_IS_SID,
TrusteeType: windows.TRUSTEE_IS_GROUP,
TrusteeValue: windows.TrusteeValueFromSID(groupSid),
},
},
{
AccessPermissions: worldPermissions,
AccessMode: windows.GRANT_ACCESS,
Trustee: windows.TRUSTEE{
TrusteeForm: windows.TRUSTEE_IS_SID,
TrusteeType: windows.TRUSTEE_IS_GROUP,
TrusteeValue: windows.TrusteeValueFromSID(worldSid),
},
},
}

acl, err := windows.ACLFromEntries(access, nil)
if err != nil {
t.Fatal(err)
}

// Set new ACL.
err = windows.SetNamedSecurityInfo(
f.Name(),
windows.SE_FILE_OBJECT,
windows.DACL_SECURITY_INFORMATION|windows.PROTECTED_DACL_SECURITY_INFORMATION,
nil,
nil,
acl,
nil,
)
if err != nil {
t.Fatal(err)
}

descriptor, err := windows.GetNamedSecurityInfo(
f.Name(),
windows.SE_FILE_OBJECT,
windows.DACL_SECURITY_INFORMATION|windows.PROTECTED_DACL_SECURITY_INFORMATION|windows.OWNER_SECURITY_INFORMATION|windows.GROUP_SECURITY_INFORMATION,
)
if err != nil {
t.Fatal(err)
}

dacl, _, err := descriptor.DACL()
if err != nil {
t.Fatal(err)
}

owner, _, err := descriptor.Owner()
if err != nil {
t.Fatal(err)
}

group, _, err := descriptor.Group()
if err != nil {
t.Fatal(err)
}

entries, err := getEntriesFromACL(dacl)
if err != nil {
t.Fatal(err)
}

if len(entries) != 3 {
t.Fatalf("Expected newly set ACL to only have 3 entries.")
}

// https://docs.microsoft.com/en-us/windows/win32/fileio/file-access-rights-constants
// read = read data | read attributes
read := 0x0001 | 0x0080

// write = write data | append data | write attributes | write EA
write := 0x0002 | 0x0004 | 0x0100 | 0x0010

// execute = read data | file execute
execute := 0x0001 | 0x0020

// Check the set ACEs. We should have the equivalent of 754.
for _, entry := range entries {
mask := int(entry.Mask)
actual := 0

if mask&read == read {
actual |= 4
}
if mask&write == write {
actual |= 2
}
if mask&execute == execute {
actual |= 1
}

if owner.Equals(&entry.Sid) {
if actual != 7 {
t.Fatalf("Expected owner to have FullAccess permissions.")
}
} else if group.Equals(&entry.Sid) {
if actual != 5 {
t.Fatalf("Expected group to have only Read and Execute permissions.")
}
} else if worldSid.Equals(&entry.Sid) {
if actual != 4 {
t.Fatalf("Expected the World to have only Read permissions.")
}
} else {
t.Fatalf("Unexpected SID in ACEs: %s", (&entry.Sid).String())
}
}
}

func TestGetDiskFreeSpaceEx(t *testing.T) {
cwd, err := windows.UTF16PtrFromString(".")
if err != nil {
Expand Down
9 changes: 9 additions & 0 deletions windows/zsyscall_windows.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.