agent/fileutil/harden_windows.go (85 lines of code) (raw):
// Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may not
// use this file except in compliance with the License. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
// either express or implied. See the License for the specific language governing
// permissions and limitations under the License.
//go:build windows
// +build windows
package fileutil
import (
"fmt"
"os"
"unsafe"
acl "github.com/hectane/go-acl"
aclapi "github.com/hectane/go-acl/api"
"golang.org/x/sys/windows"
)
// access mask with full access, 4 most significant bits are set to 1.
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa374892%28v=vs.85%29.aspx
var fullAccessAccessMask = uint32(15) << 28
// Harden the provided path with non-inheriting ACL for admin access only.
// The above comment's non-inheriting only applies to parent directory
// TODO: Move away from hectane/go-acl and uses x/sys/windows to achieve
// the same functionality
func Harden(path string) (err error) {
if _, err = os.Stat(path); err != nil {
return
}
builtinAdministratorsSID, buildinAdministratorsSIDLen := mallocSID(aclapi.SECURITY_MAX_SID_SIZE)
if err = aclapi.CreateWellKnownSid(
aclapi.WinBuiltinAdministratorsSid,
nil, builtinAdministratorsSID,
&buildinAdministratorsSIDLen,
); err != nil {
return fmt.Errorf("Failed to create SID for Built-in Administrators. %v", err)
}
localSystemSID, localSystemSIDLen := mallocSID(aclapi.SECURITY_MAX_SID_SIZE)
if err = aclapi.CreateWellKnownSid(
aclapi.WinLocalSystemSid,
nil, localSystemSID,
&localSystemSIDLen,
); err != nil {
return fmt.Errorf("Failed to create SID for LOCALSYSTYEM. %v", err)
}
// Despite the inherit flag set to false, this function actually
// Recursively apply the DACL to all sub-directory and files.
// And only prevents inheritance from parent directory. This could
// Cause signficant delay when being applied directory with many children.
if err = acl.Apply(
path,
true, // replace current ACL
false, // disable inheritance from parent folder
acl.GrantSid(fullAccessAccessMask, builtinAdministratorsSID),
acl.GrantSid(fullAccessAccessMask, localSystemSID),
); err != nil {
return fmt.Errorf("Failed to apply ACL. %v", err)
}
return
}
// Allocate memory space for SID.
func mallocSID(sidSize int) (sidPtr *windows.SID, sidLen uint32) {
var sid = make([]byte, sidSize)
sidPtr = (*windows.SID)(unsafe.Pointer(&sid[0]))
sidLen = uint32(len(sid))
return
}
// This function checks for whether the path has DACL restricted to BA or SY.
// If it does, then we already have a hardened ACL, and could skip reapplying
// the DACL recursively to potentially large directories leading to delay.
func hasHardenedACL(path string) (result bool) {
sd, err := windows.GetNamedSecurityInfo(
path,
windows.SE_FILE_OBJECT,
windows.DACL_SECURITY_INFORMATION,
)
if err != nil {
return
}
dacl, _, err := sd.DACL()
if err != nil {
return
}
BASid, err := windows.CreateWellKnownSid(windows.WinBuiltinAdministratorsSid)
if err != nil {
return
}
SYSid, err := windows.CreateWellKnownSid(windows.WinLocalSystemSid)
if err != nil {
return
}
// There should be at least 2 ACE corresponding to BA & SY
aceCount := uint32(dacl.AceCount)
if aceCount < 2 {
return
}
// Check to ensure all ACE entries only have Administrator or System access
// If this is satisfied, then the ACL already have hardened access.
for i := uint32(0); i < aceCount; i++ {
var ace *windows.ACCESS_ALLOWED_ACE
err := windows.GetAce(dacl, i, &ace)
if err != nil {
return
}
sid := (*windows.SID)(unsafe.Pointer(&ace.SidStart))
if !windows.EqualSid(sid, BASid) && !windows.EqualSid(sid, SYSid) {
return
}
}
return true
}