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 }