83 lines
2.4 KiB
Go
83 lines
2.4 KiB
Go
//go:build windows
|
|
|
|
package credentiallease
|
|
|
|
import (
|
|
"os"
|
|
"unsafe"
|
|
|
|
"golang.org/x/sys/windows"
|
|
)
|
|
|
|
const (
|
|
accessAllowedObjectACEType = 5
|
|
accessAllowedCallbackACEType = 9
|
|
accessAllowedCallbackObjectACEType = 11
|
|
)
|
|
|
|
// privateKeyFileSecure validates Windows ownership and discretionary ACLs.
|
|
// FileMode permission bits do not represent Windows ACLs, so checking 0600
|
|
// would reject every normal Windows key file without proving confidentiality.
|
|
func privateKeyFileSecure(file *os.File, _ os.FileInfo) bool {
|
|
sd, err := windows.GetSecurityInfo(
|
|
windows.Handle(file.Fd()),
|
|
windows.SE_FILE_OBJECT,
|
|
windows.OWNER_SECURITY_INFORMATION|windows.DACL_SECURITY_INFORMATION,
|
|
)
|
|
if err != nil || sd == nil || !sd.IsValid() {
|
|
return false
|
|
}
|
|
|
|
current, err := windows.GetCurrentProcessToken().GetTokenUser()
|
|
if err != nil || current == nil || current.User.Sid == nil {
|
|
return false
|
|
}
|
|
owner, _, err := sd.Owner()
|
|
if err != nil || !trustedKeyFileSID(owner, current.User.Sid) {
|
|
return false
|
|
}
|
|
|
|
dacl, _, err := sd.DACL()
|
|
if err != nil || dacl == nil {
|
|
return false
|
|
}
|
|
for i := uint32(0); i < uint32(dacl.AceCount); i++ {
|
|
var ace *windows.ACCESS_ALLOWED_ACE
|
|
if err := windows.GetAce(dacl, i, &ace); err != nil || ace == nil {
|
|
return false
|
|
}
|
|
if ace.Header.AceFlags&windows.INHERIT_ONLY_ACE != 0 {
|
|
continue
|
|
}
|
|
switch ace.Header.AceType {
|
|
case windows.ACCESS_DENIED_ACE_TYPE:
|
|
continue
|
|
case windows.ACCESS_ALLOWED_ACE_TYPE:
|
|
if !aceGrantsFileRead(ace.Mask) {
|
|
continue
|
|
}
|
|
sid := (*windows.SID)(unsafe.Pointer(&ace.SidStart))
|
|
if !trustedKeyFileSID(sid, current.User.Sid) {
|
|
return false
|
|
}
|
|
case accessAllowedObjectACEType, accessAllowedCallbackACEType, accessAllowedCallbackObjectACEType:
|
|
// These layouts carry additional fields before the SID. Reject them
|
|
// instead of guessing whether an untrusted principal can read the key.
|
|
return false
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func aceGrantsFileRead(mask windows.ACCESS_MASK) bool {
|
|
const readMask = windows.FILE_READ_DATA | windows.GENERIC_READ | windows.GENERIC_ALL | windows.MAXIMUM_ALLOWED
|
|
return mask&readMask != 0
|
|
}
|
|
|
|
func trustedKeyFileSID(candidate, current *windows.SID) bool {
|
|
return candidate != nil && current != nil && (candidate.Equals(current) ||
|
|
candidate.IsWellKnown(windows.WinLocalSystemSid) ||
|
|
candidate.IsWellKnown(windows.WinBuiltinAdministratorsSid))
|
|
}
|