//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))
}
