* UPDATE *
The answers have been very helpful and now my code is returning ERROR_SUCCESS
. The key change seemed to be switching to use SetKernelObjectSecurity()
. However, now I'm seeing a different problem; my code succeeds, but if I look on the file system or check the file in code, it still has previous owner.
This has been reported before on SO, but without a satisfactory answer.
Here is a public gist with my code. It adds some output so you can see what I'm talking about. You should be able to add it to an empty Visual Studio C++ Console project and debug through it. Be sure to open Visual Studio using "Run As Administrator".
* 2nd UPDATE *
I just found this note on MSDN for SetKernelObjectSecurity()
.
Note This function should not be used when setting a security descriptor on file system objects. Instead, use the
SetSecurityInfo
orSetNamedSecurityInfo
functions.
I'm not sure how I missed that... it's right at the top.
* ORIGINAL QUESTION *
I need to implement the equivalent functionality of fchown()
on Windows, but after quite a bit of research and effort I have been unable to make it work. fchown()
changes the ownership of a file that is specified via an open file descriptor. In the case of Windows, this could be either an open file descriptor or a HANDLE
(you can create one from the other). It seems that no matter what I try, I get ERROR_ACCESS_DENIED
.
I have tried both SetSecurityInfo()
and SetUserObjectInfo()
. I can get the ownership information from the open file descriptor using the corresponding Get* functions: GetSecurityInfo()
and GetUserObjectSecurity()
.
When I rework my code to use either SetNamedSecurityInfo()
or SetFileSecurity()
, where you specify a name for the file rather than an open HANDLE, everything works fine.
Have I run into the low-level file system access control rules of the operating system?
Is it impossible to change the ownership of an open file on Windows?
From what I could tell, I couldn't even change the DACLs when the HANDLE was open. Is Windows just trying to prevent me from a false sense of security when I think I have secured the file but someone still has an open HANDLE?
It seems to me that if I am missing something, it might be how I am calling CreateFile()
.
In anticipation of some of the answers that might be posted:
(Also, please bear in mind I got this working by simply replacing the object versions of the Win32 APIs with those that take file name)
AdjustTokenPrivileges()
to give myself SE_TAKE_OWNERSHIP_NAME
CreateFile()
for dwShareMode
.Here is some code. I've removed all the error handling so that this question isn't too long:
wchar_t* filename = L"test.txt";
HANDLE hFile = CreateFile(filename, GENERIC_READ, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
HANDLE hToken = NULL;
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken);
SetPrivilege(hToken, SE_BACKUP_NAME, TRUE);
SetPrivilege(hToken, SE_RESTORE_NAME, TRUE);
SetPrivilege(hToken, SE_SECURITY_NAME, TRUE);
SetPrivilege(hToken, SE_TAKE_OWNERSHIP_NAME, TRUE);
DWORD bufSize = 0;
SECURITY_INFORMATION info = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;
GetUserObjectSecurity(hFile, &info, NULL, 0, &bufSize); /* get buffer size */
PSECURITY_DESCRIPTOR desc = (PSECURITY_DESCRIPTOR)calloc(bufSize, sizeof(BYTE));
GetUserObjectSecurity(hFile, &info, desc, bufSize, &bufSize);
TRUSTEE trustee = { 0 };
BuildTrusteeWithSid(&trustee, newOwnerSid);
PSECURITY_DESCRIPTOR newdesc = NULL;
BuildSecurityDescriptor(&trustee, NULL, 0, NULL, 0, NULL, desc, &bufSize, &newdesc);
SetUserObjectSecurity(hFile, &info, newdesc);
free(desc);
LocalFree(newdesc);
CloseHandle(hToken);
CloseHandle(hFile);
You'll need these headers:
#include <Windows.h>
#include <AclAPI.h>
#include <Sddl.h>
#include <stdio.h>
The SetPrivilege() function is:
static
BOOL
SetPrivilege(HANDLE hToken, LPCTSTR lpszPrivilege, BOOL bEnablePrivilege) {
TOKEN_PRIVILEGES newState = { 0 };
LUID luid;
if (!LookupPrivilegeValue(NULL, lpszPrivilege, &luid)) {
return FALSE;
}
newState.PrivilegeCount = 1;
newState.Privileges[0].Luid = luid;
if (bEnablePrivilege) {
newState.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
}
else {
newState.Privileges[0].Attributes = 0;
}
/* If this returns a failure then your process does not have the ability to grant the privilege. */
if (!AdjustTokenPrivileges(hToken, FALSE, &newState, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) {
return FALSE;
}
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) {
return FALSE;
}
return TRUE;
}
You've opened the handle with GENERIC_READ
access. Windows enforces this; having opened the handle that way, you can only use the handle in read operations. (This means that Windows need only check your access to the object when you open the handle; from then on, access is granted or denied based entirely on the handle's access rights.)
The documentation on SECURITY_INFORMATION
shows which access rights you need on the handle in order to query and set the various information. In your case you will need WRITE_OWNER
to assign the ownership and primary group, WRITE_DAC
to assign the DACL, and READ_CONTROL
to read the ownership, primary group and DACL.
Note that GENERIC_WRITE
does not include either WRITE_OWNER
or WRITE_DAC
so you will have to specify them explicitly.
(I can't find any documentation on what file permissions are included in GENERIC_ALL
but even if it works it would be preferable to explicitly request the permissions you will be using.)
SetUserObjectInfo
and GetUserObjectSecurity
work on "user" objects which, broadly speaking, are window manager objects. Files are kernel objects so you need the functions with Kernel
in the name like SetKernelObjectSecurity
. See Object Categories.
That said, SetSecurityInfo
should work.
SetFileSecurity
is working for you so you must have appropriate permissions.
Most likely, you aren't requesting the correct access permissions in the call to CreateFile
. Harry Johnston wrote more on this so see his answer for details.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With