Skip to content

Commit 9cd77c1

Browse files
committed
windows: add GetAce Windows API
GetAce obtains a pointer to an access control entry (ACE) in an discretionary access control list (DACL), which controls access to an object. Adds the ACE_HEADER and ACCESS_ALLOWED_ACE structs. Adds GetEntriesFromACL function which returns an array of ACEs from the given ACL if no errors have been encountered. References: - https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-ace_header - https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-access_allowed_ace - https://learn.microsoft.com/en-us/windows/win32/api/securitybaseapi/nf-securitybaseapi-getace Fixes golang/go#66850
1 parent 27dc90b commit 9cd77c1

File tree

3 files changed

+197
-1
lines changed

3 files changed

+197
-1
lines changed

windows/security_windows.go

+23-1
Original file line numberDiff line numberDiff line change
@@ -893,7 +893,7 @@ type ACL struct {
893893
aclRevision byte
894894
sbz1 byte
895895
aclSize uint16
896-
aceCount uint16
896+
ACECount uint16
897897
sbz2 uint16
898898
}
899899

@@ -1086,6 +1086,27 @@ type EXPLICIT_ACCESS struct {
10861086
Trustee TRUSTEE
10871087
}
10881088

1089+
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-ace_header
1090+
type ACE_HEADER struct {
1091+
ACEType uint8
1092+
ACEFlags uint8
1093+
ACESize uint16
1094+
}
1095+
1096+
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-access_allowed_ace
1097+
type ACCESS_ALLOWED_ACE struct {
1098+
Header ACE_HEADER
1099+
Mask ACCESS_MASK
1100+
Sid SID
1101+
}
1102+
1103+
const (
1104+
// Constants for AceType
1105+
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-ace_header
1106+
ACCESS_ALLOWED_ACE_TYPE = 0
1107+
ACCESS_DENIED_ACE_TYPE = 1
1108+
)
1109+
10891110
// This type is the union inside of TRUSTEE and must be created using one of the TrusteeValueFrom* functions.
10901111
type TrusteeValue uintptr
10911112

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

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

11611183
// Control returns the security descriptor control bits.
11621184
func (sd *SECURITY_DESCRIPTOR) Control() (control SECURITY_DESCRIPTOR_CONTROL, revision uint32, err error) {

windows/syscall_windows_test.go

+165
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,171 @@ func TestBuildSecurityDescriptor(t *testing.T) {
359359
}
360360
}
361361

362+
// getEntriesFromACL returns a list of explicit access control entries associated with the given ACL.
363+
func getEntriesFromACL(acl *windows.ACL) (aces []*windows.ACCESS_ALLOWED_ACE, err error) {
364+
aces = make([]*windows.ACCESS_ALLOWED_ACE, acl.ACECount)
365+
366+
for i := uint16(0); i < acl.ACECount; i++ {
367+
// ACECount is a word, but aceIndex is a double word.
368+
err = windows.GetAce(acl, uint32(i), &aces[i])
369+
if err != nil {
370+
return []*windows.ACCESS_ALLOWED_ACE{}, err
371+
}
372+
}
373+
374+
return aces, nil
375+
}
376+
377+
func TestGetACEsFromACL(t *testing.T) {
378+
// Create a temporary file to set ACLs on and test getting the ACEs from the ACL.
379+
f, err := os.CreateTemp("", "foo.lish")
380+
defer os.Remove(f.Name())
381+
382+
f.Close()
383+
384+
// Well-known SID Strings:
385+
// https://support.microsoft.com/en-us/help/243330/well-known-security-identifiers-in-windows-operating-systems
386+
ownerSid, err := windows.StringToSid("S-1-3-2")
387+
if err != nil {
388+
t.Fatal(err)
389+
}
390+
groupSid, err := windows.StringToSid("S-1-3-3")
391+
if err != nil {
392+
t.Fatal(err)
393+
}
394+
worldSid, err := windows.StringToSid("S-1-1-0")
395+
if err != nil {
396+
t.Fatal(err)
397+
}
398+
399+
ownerPermissions := windows.ACCESS_MASK(windows.GENERIC_ALL)
400+
groupPermissions := windows.ACCESS_MASK(windows.GENERIC_READ | windows.GENERIC_EXECUTE)
401+
worldPermissions := windows.ACCESS_MASK(windows.GENERIC_READ)
402+
403+
access := []windows.EXPLICIT_ACCESS{
404+
{
405+
AccessPermissions: ownerPermissions,
406+
AccessMode: windows.GRANT_ACCESS,
407+
Trustee: windows.TRUSTEE{
408+
TrusteeForm: windows.TRUSTEE_IS_SID,
409+
TrusteeValue: windows.TrusteeValueFromSID(ownerSid),
410+
},
411+
},
412+
{
413+
AccessPermissions: groupPermissions,
414+
AccessMode: windows.GRANT_ACCESS,
415+
Trustee: windows.TRUSTEE{
416+
TrusteeForm: windows.TRUSTEE_IS_SID,
417+
TrusteeType: windows.TRUSTEE_IS_GROUP,
418+
TrusteeValue: windows.TrusteeValueFromSID(groupSid),
419+
},
420+
},
421+
{
422+
AccessPermissions: worldPermissions,
423+
AccessMode: windows.GRANT_ACCESS,
424+
Trustee: windows.TRUSTEE{
425+
TrusteeForm: windows.TRUSTEE_IS_SID,
426+
TrusteeType: windows.TRUSTEE_IS_GROUP,
427+
TrusteeValue: windows.TrusteeValueFromSID(worldSid),
428+
},
429+
},
430+
}
431+
432+
acl, err := windows.ACLFromEntries(access, nil)
433+
if err != nil {
434+
t.Fatal(err)
435+
}
436+
437+
// Set new ACL.
438+
err = windows.SetNamedSecurityInfo(
439+
f.Name(),
440+
windows.SE_FILE_OBJECT,
441+
windows.DACL_SECURITY_INFORMATION|windows.PROTECTED_DACL_SECURITY_INFORMATION,
442+
nil,
443+
nil,
444+
acl,
445+
nil,
446+
)
447+
if err != nil {
448+
t.Fatal(err)
449+
}
450+
451+
descriptor, err := windows.GetNamedSecurityInfo(
452+
f.Name(),
453+
windows.SE_FILE_OBJECT,
454+
windows.DACL_SECURITY_INFORMATION|windows.PROTECTED_DACL_SECURITY_INFORMATION|windows.OWNER_SECURITY_INFORMATION|windows.GROUP_SECURITY_INFORMATION,
455+
)
456+
if err != nil {
457+
t.Fatal(err)
458+
}
459+
460+
dacl, _, err := descriptor.DACL()
461+
if err != nil {
462+
t.Fatal(err)
463+
}
464+
465+
owner, _, err := descriptor.Owner()
466+
if err != nil {
467+
t.Fatal(err)
468+
}
469+
470+
group, _, err := descriptor.Group()
471+
if err != nil {
472+
t.Fatal(err)
473+
}
474+
475+
entries, err := getEntriesFromACL(dacl)
476+
if err != nil {
477+
t.Fatal(err)
478+
}
479+
480+
if len(entries) != 3 {
481+
t.Fatalf("Expected newly set ACL to only have 3 entries.")
482+
}
483+
484+
// https://docs.microsoft.com/en-us/windows/win32/fileio/file-access-rights-constants
485+
// read = read data | read attributes
486+
read := 0x0001 | 0x0080
487+
488+
// write = write data | append data | write attributes | write EA
489+
write := 0x0002 | 0x0004 | 0x0100 | 0x0010
490+
491+
// execute = read data | file execute
492+
execute := 0x0001 | 0x0020
493+
494+
// Check the set ACEs. We should have the equivalent of 754.
495+
for _, entry := range entries {
496+
mask := int(entry.Mask)
497+
actual := 0
498+
499+
if mask&read == read {
500+
actual |= 4
501+
}
502+
if mask&write == write {
503+
actual |= 2
504+
}
505+
if mask&execute == execute {
506+
actual |= 1
507+
}
508+
509+
if owner.Equals(&entry.Sid) {
510+
if actual != 7 {
511+
t.Fatalf("Expected owner to have FullAccess permissions.")
512+
}
513+
} else if group.Equals(&entry.Sid) {
514+
if actual != 5 {
515+
t.Fatalf("Expected group to have only Read and Execute permissions.")
516+
}
517+
} else if worldSid.Equals(&entry.Sid) {
518+
if actual != 4 {
519+
t.Fatalf("Expected the World to have only Read permissions.")
520+
}
521+
} else {
522+
t.Fatalf("Unexpected SID in ACEs: %s", (&entry.Sid).String())
523+
}
524+
}
525+
}
526+
362527
func TestGetDiskFreeSpaceEx(t *testing.T) {
363528
cwd, err := windows.UTF16PtrFromString(".")
364529
if err != nil {

windows/zsyscall_windows.go

+9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)