var h syscall.Handle
err := NtOpenFile(
&h,
- SYNCHRONIZE|FILE_READ_ATTRIBUTES|DELETE,
+ FILE_READ_ATTRIBUTES|DELETE,
objAttrs,
&IO_STATUS_BLOCK{},
FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE,
- FILE_OPEN_REPARSE_POINT|FILE_OPEN_FOR_BACKUP_INTENT|FILE_SYNCHRONOUS_IO_NONALERT|options,
+ FILE_OPEN_REPARSE_POINT|FILE_OPEN_FOR_BACKUP_INTENT|options,
)
if err != nil {
- return ntCreateFileError(err, 0)
+ if ntStatus, ok := err.(NTStatus); !ok || ntStatus != STATUS_ACCESS_DENIED {
+ return ntCreateFileError(err, 0)
+ }
+
+ // Access denied, try opening with DELETE only.
+ // This may succeed if the file has restrictive permissions
+ // but the caller has delete child permission on the parent directory.
+ err = NtOpenFile(
+ &h,
+ DELETE,
+ objAttrs,
+ &IO_STATUS_BLOCK{},
+ FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_OPEN_REPARSE_POINT|FILE_OPEN_FOR_BACKUP_INTENT|options,
+ )
+ if err != nil {
+ return ntCreateFileError(err, 0)
+ }
}
defer syscall.CloseHandle(h)
"path/filepath"
"syscall"
"testing"
+ "unsafe"
)
func TestOpen(t *testing.T) {
}
}
}
+
+func TestDeleteAt(t *testing.T) {
+ testCases := []struct {
+ name string
+ modifier func(t *testing.T, path string)
+ }{
+ {"DeleteAt removes normal file", func(t *testing.T, name string) {}},
+ {"DeleteAt removes file with no read permission", makeFileNotReadable},
+ {"DeleteAt removes readonly file", makeFileReadonly},
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ t.Parallel()
+
+ dir := t.TempDir()
+ file := filepath.Join(dir, "a")
+ f, err := os.Create(file)
+ if err != nil {
+ t.Fatal(err)
+ }
+ f.Close()
+
+ // Remove all permissions from the file.
+ // Do not use os.Chmod it sets only readonly attribute on Windows.
+ tc.modifier(t, file)
+
+ // delete file using DeleteAt
+ dirfd, err := syscall.Open(dir, syscall.O_RDONLY, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ base := filepath.Base(file)
+ err = windows.Deleteat(dirfd, base, 0)
+ syscall.CloseHandle(dirfd)
+ if err != nil {
+ t.Fatalf("Deleteat failed: %v", err)
+ }
+
+ // Verify the file has been deleted.
+ if _, err := os.Stat(file); !os.IsNotExist(err) {
+ t.Fatalf("file still exists after DeleteAt")
+ }
+ })
+ }
+}
+
+func makeFileReadonly(t *testing.T, name string) {
+ if err := os.Chmod(name, 0); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func makeFileNotReadable(t *testing.T, name string) {
+ creatorOwnerSID, err := syscall.StringToSid("S-1-3-0")
+ if err != nil {
+ t.Fatal(err)
+ }
+ creatorGroupSID, err := syscall.StringToSid("S-1-3-1")
+ if err != nil {
+ t.Fatal(err)
+ }
+ everyoneSID, err := syscall.StringToSid("S-1-1-0")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ entryForSid := func(sid *syscall.SID) windows.EXPLICIT_ACCESS {
+ return windows.EXPLICIT_ACCESS{
+ AccessPermissions: 0,
+ AccessMode: windows.GRANT_ACCESS,
+ Inheritance: windows.SUB_CONTAINERS_AND_OBJECTS_INHERIT,
+ Trustee: windows.TRUSTEE{
+ TrusteeForm: windows.TRUSTEE_IS_SID,
+ Name: (*uint16)(unsafe.Pointer(sid)),
+ },
+ }
+ }
+
+ entries := []windows.EXPLICIT_ACCESS{
+ entryForSid(creatorOwnerSID),
+ entryForSid(creatorGroupSID),
+ entryForSid(everyoneSID),
+ }
+
+ var oldAcl, newAcl syscall.Handle
+ if err := windows.SetEntriesInAcl(
+ uint32(len(entries)),
+ &entries[0],
+ oldAcl,
+ &newAcl,
+ ); err != nil {
+ t.Fatal(err)
+ }
+
+ defer syscall.LocalFree((syscall.Handle)(unsafe.Pointer(newAcl)))
+ if err := windows.SetNamedSecurityInfo(
+ name,
+ windows.SE_FILE_OBJECT,
+ windows.DACL_SECURITY_INFORMATION|windows.PROTECTED_DACL_SECURITY_INFORMATION,
+ nil,
+ nil,
+ newAcl,
+ 0,
+ ); err != nil {
+ t.Fatal(err)
+ }
+}
STATUS_NOT_SUPPORTED NTStatus = 0xC00000BB
STATUS_INVALID_PARAMETER NTStatus = 0xC000000D
STATUS_INVALID_INFO_CLASS NTStatus = 0xC0000003
+ STATUS_ACCESS_DENIED NTStatus = 0xC0000022
)
const (
//sys NtSetInformationFile(handle syscall.Handle, iosb *IO_STATUS_BLOCK, inBuffer unsafe.Pointer, inBufferLen uint32, class uint32) (ntstatus error) = ntdll.NtSetInformationFile
//sys RtlIsDosDeviceName_U(name *uint16) (ret uint32) = ntdll.RtlIsDosDeviceName_U
//sys NtQueryInformationFile(handle syscall.Handle, iosb *IO_STATUS_BLOCK, inBuffer unsafe.Pointer, inBufferLen uint32, class uint32) (ntstatus error) = ntdll.NtQueryInformationFile
+
+//sys SetEntriesInAcl(countExplicitEntries uint32, explicitEntries *EXPLICIT_ACCESS, oldACL syscall.Handle, newACL *syscall.Handle) (ret error) = advapi32.SetEntriesInAclW
+//sys SetNamedSecurityInfo(objectName string, objectType int32, securityInformation uint32, owner *syscall.SID, group *syscall.SID, dacl syscall.Handle, sacl syscall.Handle) (ret error) = advapi32.SetNamedSecurityInfoW
O_FILE_FLAG_NO_BUFFERING |
O_FILE_FLAG_RANDOM_ACCESS |
O_FILE_FLAG_WRITE_THROUGH
+
+// https://msdn.microsoft.com/en-us/library/windows/desktop/aa379636.aspx
+type TRUSTEE struct {
+ MultipleTrustee *TRUSTEE
+ MultipleTrusteeOperation int32
+ TrusteeForm int32
+ TrusteeType int32
+ Name *uint16
+}
+
+// https://msdn.microsoft.com/en-us/library/windows/desktop/aa379638.aspx
+const (
+ TRUSTEE_IS_SID = iota
+ TRUSTEE_IS_NAME
+ TRUSTEE_BAD_FORM
+ TRUSTEE_IS_OBJECTS_AND_SID
+ TRUSTEE_IS_OBJECTS_AND_NAME
+)
+
+// https://msdn.microsoft.com/en-us/library/windows/desktop/aa446627.aspx
+type EXPLICIT_ACCESS struct {
+ AccessPermissions uint32
+ AccessMode int32
+ Inheritance uint32
+ Trustee TRUSTEE
+}
+
+// https://msdn.microsoft.com/en-us/library/windows/desktop/aa374899.aspx
+const (
+ NOT_USED_ACCESS = iota
+ GRANT_ACCESS
+ SET_ACCESS
+ DENY_ACCESS
+ REVOKE_ACCESS
+ SET_AUDIT_SUCCESS
+ SET_AUDIT_FAILURE
+)
+
+// https://msdn.microsoft.com/en-us/library/windows/desktop/aa446627.aspx
+const (
+ NO_INHERITANCE = 0x0
+ SUB_OBJECTS_ONLY_INHERIT = 0x1
+ SUB_CONTAINERS_ONLY_INHERIT = 0x2
+ SUB_CONTAINERS_AND_OBJECTS_INHERIT = 0x3
+ INHERIT_NO_PROPAGATE = 0x4
+ INHERIT_ONLY = 0x8
+)
+
+// https://msdn.microsoft.com/en-us/library/windows/desktop/aa379593.aspx
+const (
+ SE_UNKNOWN_OBJECT_TYPE = iota
+ SE_FILE_OBJECT
+ SE_SERVICE
+ SE_PRINTER
+ SE_REGISTRY_KEY
+ SE_LMSHARE
+ SE_KERNEL_OBJECT
+ SE_WINDOW_OBJECT
+ SE_DS_OBJECT
+ SE_DS_OBJECT_ALL
+ SE_PROVIDER_DEFINED_OBJECT
+ SE_WMIGUID_OBJECT
+ SE_REGISTRY_WOW64_32KEY
+ SE_REGISTRY_WOW64_64KEY
+)
+
+// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/23e75ca3-98fd-4396-84e5-86cd9d40d343
+const (
+ OWNER_SECURITY_INFORMATION = 0x00000001
+ GROUP_SECURITY_INFORMATION = 0x00000002
+ DACL_SECURITY_INFORMATION = 0x00000004
+ SACL_SECURITY_INFORMATION = 0x00000008
+ LABEL_SECURITY_INFORMATION = 0x00000010
+ UNPROTECTED_SACL_SECURITY_INFORMATION = 0x10000000
+ UNPROTECTED_DACL_SECURITY_INFORMATION = 0x20000000
+ PROTECTED_SACL_SECURITY_INFORMATION = 0x40000000
+ PROTECTED_DACL_SECURITY_INFORMATION = 0x80000000
+ ATTRIBUTE_SECURITY_INFORMATION = 0x00000020
+ SCOPE_SECURITY_INFORMATION = 0x00000040
+ PROCESS_TRUST_LABEL_SECURITY_INFORMATION = 0x00000080
+ BACKUP_SECURITY_INFORMATION = 0x00010000
+)
procOpenThreadToken = modadvapi32.NewProc("OpenThreadToken")
procQueryServiceStatus = modadvapi32.NewProc("QueryServiceStatus")
procRevertToSelf = modadvapi32.NewProc("RevertToSelf")
+ procSetEntriesInAclW = modadvapi32.NewProc("SetEntriesInAclW")
+ procSetNamedSecurityInfoW = modadvapi32.NewProc("SetNamedSecurityInfoW")
procSetTokenInformation = modadvapi32.NewProc("SetTokenInformation")
procProcessPrng = modbcryptprimitives.NewProc("ProcessPrng")
procGetAdaptersAddresses = modiphlpapi.NewProc("GetAdaptersAddresses")
return
}
+func SetEntriesInAcl(countExplicitEntries uint32, explicitEntries *EXPLICIT_ACCESS, oldACL syscall.Handle, newACL *syscall.Handle) (ret error) {
+ r0, _, _ := syscall.SyscallN(procSetEntriesInAclW.Addr(), uintptr(countExplicitEntries), uintptr(unsafe.Pointer(explicitEntries)), uintptr(oldACL), uintptr(unsafe.Pointer(newACL)))
+ if r0 != 0 {
+ ret = syscall.Errno(r0)
+ }
+ return
+}
+
+func SetNamedSecurityInfo(objectName string, objectType int32, securityInformation uint32, owner *syscall.SID, group *syscall.SID, dacl syscall.Handle, sacl syscall.Handle) (ret error) {
+ var _p0 *uint16
+ _p0, ret = syscall.UTF16PtrFromString(objectName)
+ if ret != nil {
+ return
+ }
+ return _SetNamedSecurityInfo(_p0, objectType, securityInformation, owner, group, dacl, sacl)
+}
+
+func _SetNamedSecurityInfo(objectName *uint16, objectType int32, securityInformation uint32, owner *syscall.SID, group *syscall.SID, dacl syscall.Handle, sacl syscall.Handle) (ret error) {
+ r0, _, _ := syscall.SyscallN(procSetNamedSecurityInfoW.Addr(), uintptr(unsafe.Pointer(objectName)), uintptr(objectType), uintptr(securityInformation), uintptr(unsafe.Pointer(owner)), uintptr(unsafe.Pointer(group)), uintptr(dacl), uintptr(sacl))
+ if r0 != 0 {
+ ret = syscall.Errno(r0)
+ }
+ return
+}
+
func SetTokenInformation(tokenHandle syscall.Token, tokenInformationClass uint32, tokenInformation unsafe.Pointer, tokenInformationLength uint32) (err error) {
r1, _, e1 := syscall.SyscallN(procSetTokenInformation.Addr(), uintptr(tokenHandle), uintptr(tokenInformationClass), uintptr(tokenInformation), uintptr(tokenInformationLength))
if r1 == 0 {