Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

fchown() on Windows seems to be impossible to implement in C

Tags:

c

windows

winapi

* 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 or SetNamedSecurityInfo 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)

  • I am running in an elevated process
  • I called AdjustTokenPrivileges() to give myself SE_TAKE_OWNERSHIP_NAME
  • I have been through the DACLs and the rights in my process token--I believe I am running as a user with sufficient rights (otherwise why would it work with file name)
  • I tried to open the file explicitly by passing 0 to 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;
}
like image 502
petrsnd Avatar asked Jul 28 '14 23:07

petrsnd


2 Answers

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.)

like image 149
Harry Johnston Avatar answered Nov 06 '22 12:11

Harry Johnston


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.

like image 41
arx Avatar answered Nov 06 '22 12:11

arx