Pull request 2312: 7400 Windows permcheck
Updates #7400.
Squashed commit of the following:
commit f50d7c200de545dc6c8ef70b39208f522033fb90
Merge: 47040a14c 37b16bcf7
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date: Tue Dec 3 18:09:23 2024 +0300
Merge branch 'master' into 7400-chown-permcheck
commit 47040a14cd50bf50429f44eba0acdcf736412b61
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date: Tue Dec 3 14:26:43 2024 +0300
permcheck: fix nil entries
commit e1d21c576d75a903b88db3b7beb82348cdcf60c9
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date: Mon Dec 2 15:37:58 2024 +0300
permcheck: fix nil owner
commit b1fc67c4d189293d0aee90c1905f7f387840643b
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date: Fri Nov 29 18:07:15 2024 +0300
permcheck: imp doc
commit 0b6a71326e249f0923e389aa1f6f164b02802a24
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date: Fri Nov 29 17:16:24 2024 +0300
permcheck: imp code
commit 7dfbeda179d0ddb81db54fa4e0dcff189b400215
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date: Fri Nov 29 14:28:17 2024 +0300
permcheck: imp code
commit 3a5b6aced948a2d09fdae823fc986266c9984b3d
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date: Thu Nov 28 19:21:03 2024 +0300
all: imp code, docs
commit c076c9366934303fa8c5909bd13770e367dca72e
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date: Thu Nov 28 15:14:06 2024 +0300
permcheck: imp code, docs
commit 09e4ae1ba12e195454f1db11fa2f5c9e8e170f06
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date: Wed Nov 27 19:19:11 2024 +0300
all: implement windows permcheck
commit b75ed7d4d30e289b8a99e68e6a5e94ab74cf49cb
Author: Eugene Burkov <E.Burkov@AdGuard.COM>
Date: Mon Nov 25 18:01:47 2024 +0300
all: revert permissions
This commit is contained in:
167
internal/permcheck/security_windows.go
Normal file
167
internal/permcheck/security_windows.go
Normal file
@@ -0,0 +1,167 @@
|
||||
//go:build windows
|
||||
|
||||
package permcheck
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// objectType is the type of the object for directories in context of security
|
||||
// API.
|
||||
const objectType windows.SE_OBJECT_TYPE = windows.SE_FILE_OBJECT
|
||||
|
||||
// fileDeleteChildRight is the mask bit for the right to delete a child object.
|
||||
// It seems to be missing from the [windows] package.
|
||||
//
|
||||
// See https://learn.microsoft.com/en-us/windows-hardware/drivers/ifs/access-mask.
|
||||
const fileDeleteChildRight windows.ACCESS_MASK = 0b0100_0000
|
||||
|
||||
// fullControlMask is the mask for full control access rights.
|
||||
const fullControlMask windows.ACCESS_MASK = windows.FILE_LIST_DIRECTORY |
|
||||
windows.FILE_WRITE_DATA |
|
||||
windows.FILE_APPEND_DATA |
|
||||
windows.FILE_READ_EA |
|
||||
windows.FILE_WRITE_EA |
|
||||
windows.FILE_TRAVERSE |
|
||||
fileDeleteChildRight |
|
||||
windows.FILE_READ_ATTRIBUTES |
|
||||
windows.FILE_WRITE_ATTRIBUTES |
|
||||
windows.DELETE |
|
||||
windows.READ_CONTROL |
|
||||
windows.WRITE_DAC |
|
||||
windows.WRITE_OWNER |
|
||||
windows.SYNCHRONIZE
|
||||
|
||||
// aceFunc is a function that handles access control entries in the
|
||||
// discretionary access control list. It should return true to continue
|
||||
// iterating over the entries, or false to stop.
|
||||
type aceFunc = func(
|
||||
hdr windows.ACE_HEADER,
|
||||
mask windows.ACCESS_MASK,
|
||||
sid *windows.SID,
|
||||
) (cont bool)
|
||||
|
||||
// rangeACEs ranges over the access control entries in the discretionary access
|
||||
// control list of the specified security descriptor and calls f for each one.
|
||||
func rangeACEs(dacl *windows.ACL, f aceFunc) (err error) {
|
||||
var errs []error
|
||||
for i := range uint32(dacl.AceCount) {
|
||||
var ace *windows.ACCESS_ALLOWED_ACE
|
||||
err = windows.GetAce(dacl, i, &ace)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("getting entry at index %d: %w", i, err))
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
sid := (*windows.SID)(unsafe.Pointer(&ace.SidStart))
|
||||
if !f(ace.Header, ace.Mask, sid) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err = errors.Join(errs...); err != nil {
|
||||
return fmt.Errorf("checking access control entries: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// setSecurityInfo sets the security information on the specified file, using
|
||||
// ents to create a discretionary access control list. Either owner or ents can
|
||||
// be nil, in which case the corresponding information is not set, but at least
|
||||
// one of them should be specified.
|
||||
func setSecurityInfo(fname string, owner *windows.SID, ents []windows.EXPLICIT_ACCESS) (err error) {
|
||||
var secInfo windows.SECURITY_INFORMATION
|
||||
|
||||
var acl *windows.ACL
|
||||
if len(ents) > 0 {
|
||||
// TODO(e.burkov): Investigate if this whole set is necessary.
|
||||
secInfo |= windows.DACL_SECURITY_INFORMATION |
|
||||
windows.PROTECTED_DACL_SECURITY_INFORMATION |
|
||||
windows.UNPROTECTED_DACL_SECURITY_INFORMATION
|
||||
|
||||
acl, err = windows.ACLFromEntries(ents, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating access control list: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if owner != nil {
|
||||
secInfo |= windows.OWNER_SECURITY_INFORMATION
|
||||
}
|
||||
|
||||
if secInfo == 0 {
|
||||
return errors.Error("no security information to set")
|
||||
}
|
||||
|
||||
err = windows.SetNamedSecurityInfo(fname, objectType, secInfo, owner, nil, acl, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("setting security info: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getSecurityInfo retrieves the security information for the specified file.
|
||||
func getSecurityInfo(fname string) (dacl *windows.ACL, owner *windows.SID, err error) {
|
||||
// desiredSecInfo defines the parts of a security descriptor to retrieve.
|
||||
const desiredSecInfo windows.SECURITY_INFORMATION = windows.OWNER_SECURITY_INFORMATION |
|
||||
windows.DACL_SECURITY_INFORMATION |
|
||||
windows.PROTECTED_DACL_SECURITY_INFORMATION |
|
||||
windows.UNPROTECTED_DACL_SECURITY_INFORMATION
|
||||
|
||||
sd, err := windows.GetNamedSecurityInfo(fname, objectType, desiredSecInfo)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("getting security descriptor: %w", err)
|
||||
}
|
||||
|
||||
owner, _, err = sd.Owner()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("getting owner sid: %w", err)
|
||||
}
|
||||
|
||||
dacl, _, err = sd.DACL()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("getting discretionary access control list: %w", err)
|
||||
}
|
||||
|
||||
return dacl, owner, nil
|
||||
}
|
||||
|
||||
// newFullExplicitAccess creates a new explicit access entry with full control
|
||||
// permissions.
|
||||
func newFullExplicitAccess(sid *windows.SID) (accEnt windows.EXPLICIT_ACCESS) {
|
||||
return windows.EXPLICIT_ACCESS{
|
||||
AccessPermissions: fullControlMask,
|
||||
AccessMode: windows.GRANT_ACCESS,
|
||||
Inheritance: windows.SUB_CONTAINERS_AND_OBJECTS_INHERIT,
|
||||
Trustee: windows.TRUSTEE{
|
||||
TrusteeForm: windows.TRUSTEE_IS_SID,
|
||||
TrusteeType: windows.TRUSTEE_IS_UNKNOWN,
|
||||
TrusteeValue: windows.TrusteeValueFromSID(sid),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// newDenyExplicitAccess creates a new explicit access entry with specified deny
|
||||
// permissions.
|
||||
func newDenyExplicitAccess(
|
||||
sid *windows.SID,
|
||||
mask windows.ACCESS_MASK,
|
||||
) (accEnt windows.EXPLICIT_ACCESS) {
|
||||
return windows.EXPLICIT_ACCESS{
|
||||
AccessPermissions: mask,
|
||||
AccessMode: windows.DENY_ACCESS,
|
||||
Inheritance: windows.SUB_CONTAINERS_AND_OBJECTS_INHERIT,
|
||||
Trustee: windows.TRUSTEE{
|
||||
TrusteeForm: windows.TRUSTEE_IS_SID,
|
||||
TrusteeType: windows.TRUSTEE_IS_UNKNOWN,
|
||||
TrusteeValue: windows.TrusteeValueFromSID(sid),
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user