AdGuardHome/internal/permcheck/security_windows.go

168 lines
5 KiB
Go
Raw Normal View History

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
2024-12-03 18:26:00 +03:00
//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),
},
}
}