I have a tool which outputs the Owner and ACL of folders for documentation purposes. I now want to add a flag that indicates if the Inheritance on the path is enabled or not.
So far i could only find a .net class that provides that functionality (DirectorySecurity -> AreAccessRulesProtected).
How can i do this in native c++? For the other functionality i use GetSecurityInfo but as far as i see i can´t get that information from it.
Thanks
Thanks to RbMm´s comment i have solved the problem. For future readers here is a code snippet (no error handling):
PSECURITY_DESCRIPTOR pSD = NULL;
HANDLE hFile = CreateFile(path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
...
GetSecurityInfo(hFile, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, NULL, NULL, NULL, NULL, &pSD);
...
SECURITY_DESCRIPTOR_CONTROL sdc;
DWORD dwRev = 0;
iRC = GetSecurityDescriptorControl(pSD, &sdc, &dwRev);
if (iRC== 0) {} //error
else {
if ((sdc & SE_DACL_PROTECTED) == SE_DACL_PROTECTED) {
daclProtected = true;
}
}
Related
Recently I posted this question and now I am trying to set a reparse point after a file modification with WordPad app. Currently I checked that the reparse point is a Microsoft tag and I can save the reparse point data in a REPARSE_DATA_BUFFER pointer before the file lose the reparse point. But when I try to set the reparse point to the file, after a file modification, I always get the 395 error with the GetLastError function after call to DeviceIoControl function using the control code FSCTL_SET_REPARSE_POINT. The error 395 doesn't appear in this System Error Codes although I found this error here that has sense. I also removed the discretionary access control list (DACL) from the file, to grants full access to the file by everyone, before try to set the reparse point but I got the same error. Here I put two fragments of my code to get and set the reparse point. I will appreciate any help.
Code to get the reparse point
HANDLE hDevice = CreateFile(
filePath.c_str(), // File path in the computer
0,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
NULL);
if (hDevice != INVALID_HANDLE_VALUE)
{
size_t maxReparse = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
rdb = (REPARSE_DATA_BUFFER*)malloc(maxReparse); // rdb was declared as: REPARSE_DATA_BUFFER* rdb;
DWORD outBufferSize = maxReparse;
DWORD bytesReturned = 0;
if (DeviceIoControl(hDevice, FSCTL_GET_REPARSE_POINT, NULL, 0, rdb, outBufferSize, &bytesReturned, NULL))
{
if (rdb != NULL)
{
if (IsReparseTagMicrosoft(rdb->ReparseTag))
{
wprintf(L"Is a Microsoft tag.\n");
}
Code to set the reparse point
LPTSTR pszObjName = const_cast<wchar_t*>(filePath.c_str()); // File path in the computer
PACL newDACL = NULL; // NULL discretionary access control list to grant full access to everyone
DWORD secInfo = SetNamedSecurityInfo(pszObjName, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, newDACL, NULL);
if (secInfo == ERROR_SUCCESS)
{
HANDLE hDevice = CreateFile(filePath.c_str(),
GENERIC_ALL, //GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL);
if (hDevice != INVALID_HANDLE_VALUE)
{
size_t maxReparse = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
DWORD inBufferSize = maxReparse;
if (rdb != NULL)
{
DWORD bytesReturned = 0;
if (!DeviceIoControl(hDevice, FSCTL_SET_REPARSE_POINT, rdb, inBufferSize, NULL, 0, &bytesReturned, NULL))
{
DWORD error = GetLastError(); // Error 395
unsigned long errorUL = error;
wprintf(L"Error %lu in DeviceIoControl method.\n", errorUL);
}
I would like to retrieve the effective rights of a given registry key as a parameter. I test with an existing registry key under Windows. To do that I use the methods CreateFile, GetSecurityInfo and GetEffectiveRightsFromAclA.
I wanted to know if this is the correct method, because I have an error for the CreateFile method that returns INVALID_HANDLE_VALUE. Moreover for the method GetEffectiveRightsFromAclA, I do not understand which parameter I must put in TRUSTEE_A?
LPCWSTR lpwRegistryKey = L"HKEY_CLASSES_ROOT\\.acc\\OpenWithProgids\\WMP11.AssocFile.ADTS";
HANDLE handleKey;
handleKey = CreateFile(lpwRegistryKey, GENERIC_READ, FILE_SHARED_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if(handleKey == INVALID_HANDLE_VALUE)
{
//qDebug()<<"error";
}
//Here is an user SID
PSID pUserSid;
QString sSid("S-4-5-12");
BOOL resultSidConvert = ConvertStringSidToSidA(sSid.toStdString().c_str(), &pUserSid);
//Here success
if(resultSidConvert != 0)
{
PACL pDacl;
PSECURITY_DESCRIPTOR pSecurityDescriptor;
DWORD dwResultSecurity = GetSecurityInfo(handleKey, SE_REGISTRY_KEY, DACL_SECURITY_INFORMATION, nullptr, &pUserSid, &pDacl, nullptr, &pSecurityDescritptor);
if(dwResultSecurity == ERROR_SUCCESS)
{
ACCESS_MASK pAccessMask;
TRUSTEE_A pTrustee; //How should I initialize pTrustee?
DWORD dwEffectiveRights = (GetEffectiveRightsFromAclA(pDacl, &pTrustee, &pAccessMask);
if(dwEffectiveRights == ERROR_SUCCESS)
{
if((pAccessMask & DELETE) == DELETE)
{
qDebug()<<"Delete";
}
if((pAccessMask & GENERIC_READ) == GENERIC_READ)
{
qDebug()<<"READ";
}
//etc ..........
}
}
}
A registry key is not a file!
Don't use CreateFile. Use RegOpenKeyEx instead.
First, you must run the application as admin.
Then, you can open the key successfully. After that, take a look at RegGetKeySecurity function.
I want to recover the effective rights on a shared folder. To do that I used the methods CreateFile, GetSecurityInfo and GetEffectiveRightsFromAclA. The problem is that when I test the rights it does not work while I set the SID of the administrator. Does this come from a bad implementation of my code? I specify that I have the rights to the file.
//stringName is the file path
HANDLE file = CreateFile(stringName.c_str(), GENERIC_READ,
FILE_SHARE_READ,
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
PSID pSid;
//The SID is that of the administrator
ConvertStringSidToSidA(stringSid.c_str(), &pSid);
PACL ppDacl;
PSECURITY_DESCRIPTOR ppSecurityDescriptor;
GetSecurityInfo(file, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, nullptr,
&pSid, &ppDacl, nullptr, &ppSecurityDescriptor);
ACCESS_MASK pAccesMask;
TRUSTEE_A pTrustee;
GetEffectiveRightsFromAclA(ppDacl, &pTrustee, &pAccesMask);
//here conditions are not respected
if((pAccesMask & GENERIC_READ) == GENERIC_READ)
{
std::cout<<"ok"<<std::endl;
}
if((pAccesMask & GENERIC_WRITE) == GENERIC_WRITE)
{
std::cout<<"ok"<<std::endl;
}
if((pAccesMask & GENERIC_EXECUTE) == GENERIC_EXECUTE)
{
std::cout<<"ok"<<std::endl;
}
if((pAccesMask & GENERIC_ALL) == GENERIC_ALL)
{
std::cout<<"ok"<<std::endl;
}
Before use the pAccesMask value make sure the GetEffectiveRightsFromAclA function return ERROR_SUCCESS.
And because a Windows file object maps the GENERIC_READ bit to the READ_CONTROL and SYNCHRONIZE standard access rights. So change the condition check logic for GENERIC_READ like this:
if ((pAccesMask & READ_CONTROL) == READ_CONTROL && (pAccesMask & SYNCHRONIZE) == SYNCHRONIZE) //GENERIC_READ
{
std::cout << "GENERIC_READ" << std::endl;
}
I was under the assumption that if you use CreateFileW requesting exclusive access, that if it was already opened I should receive a sharing violation. However, I've come across an example where this is not the case. If LoadLibraryEx is called to load a DLL with the LOAD_LIBRARY_AS_DATAFILE flag set, then CreateFileW returns successfully. However there are operations that will be rejected (such as setting attributes like end of file). Is there a better way to detect if these files are open? Am I using CreateFileW incorrectly? Loading a DLL without the LOAD_LIBRARY_AS_DATAFILE flag does result in CreateFileW failing indicating it can not access the file because it is in use by another process.
HMODULE hModule = LoadLibraryEx(L"\\Pathto\\module.dll", NULL, NULL);
DWORD access = GENERIC_READ | GENERIC_WRITE | WRITE_OWNER | WRITE_DAC | ACCESS_SYSTEM_SECURITY;
DWORD creation = OPEN_EXISTING;
DWORD flags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT;
HANDLE file = CreateFileW(L"\\Pathto\\module.dll", access, 0, NULL, creation, flags, 0);
The above will result in CreateFileW failing with error code 32 (can't access cause it is in use by another process) which is the expected result. If I add the flag to LoadLibraryEx:
HMODULE hModule = LoadLibraryEx(name, NULL, LOAD_LIBRARY_AS_DATAFILE);
And use the exact same call to CreateFileW then I'm told it's successful, although I will run into problems later when attempting to set end of file (as an example). Additionally, deleting the file will fail with an access denied error (without the application or any other having an open handle).
Other odd behavior involves hard links with loaded libraries. If I generate a new hard link to the loaded module, and try to delete the alternate newly created hard link, it also fails (NO open file handles, just loaded module). Why? Shouldn't it just remove the link and dereference the link count, or schedule deletion on module close since I could have obtained exclusive access previously?
Also of note, the process itself runs in a privileged user account with the following additional privileges enabled:
SE_SECURITY_NAME, SE_BACKUP_NAME, SE_RESTORE_NAME, SE_TAKE_OWNERSHIP_NAME
How would one determine if you truly have exclusive access to a file when libraries are loaded?
Edit: Just to doubly check, I verified no other open handles besides loading the module via the SysInternals tool "handle".
your case can be more clear shown in next test
ULONG TestImageAccess(PCWSTR name, BOOL bImage, BOOL bRequestWriteAccess)
{
SetLastError(0);
HANDLE hFile = CreateFileW(name, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
HANDLE hSection = CreateFileMappingW(hFile, 0,
bImage ? PAGE_READONLY|SEC_IMAGE : PAGE_READONLY|SEC_COMMIT, 0, 0, 0);
CloseHandle(hFile);
if (hSection)
{
hFile = CreateFileW(name,
bRequestWriteAccess ? GENERIC_WRITE : GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);
CloseHandle(hSection);
if (hFile != INVALID_HANDLE_VALUE)
{
CloseHandle(hFile);
}
}
}
return GetLastError();
}
void TestImageAccess(PCWSTR filename)
{
TestImageAccess(filename, TRUE, TRUE); // ERROR_SHARING_VIOLATION
TestImageAccess(filename, FALSE, TRUE); // NOERROR
TestImageAccess(filename, TRUE, FALSE); // NOERROR
TestImageAccess(filename, FALSE, FALSE);// NOERROR
}
we got ERROR_SHARING_VIOLATION when we request write access to file and file mapped as image
this case is described in Executable Images:
If the user wants write access to the file, make sure there is not a
process mapping this file as an image
the call to MmFlushImageSection with MmFlushForWrite fail and you got STATUS_SHARING_VIOLATION - exactly at this line.
LoadLibraryEx with flag LOAD_LIBRARY_AS_DATAFILE create section on file with SEC_COMMIT when with 0 in flag - with SEC_IMAGE - so as image section
the privileges here absolute not related
How would one determine if you truly have exclusive access to a file
when libraries are loaded?
simply open file with access which you need and do what you need. and yes, you can got error ERROR_USER_MAPPED_FILE when you try truncate mapped file. but here nothing can be done. example 2
ULONG TestImage2(PCWSTR name)
{
HANDLE hFile = CreateFileW(name, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
HANDLE hSection = CreateFileMappingW(hFile, 0, PAGE_READONLY|SEC_COMMIT, 0, 0, 0);
CloseHandle(hFile);
if (hSection)
{
PVOID pv = MapViewOfFile(hSection, FILE_MAP_READ, 0, 0, 0);
CloseHandle(hSection);
if (pv)
{
hFile = CreateFileW(name,GENERIC_WRITE|GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
FILE_END_OF_FILE_INFO eof = { };
// ERROR_USER_MAPPED_FILE will be here
SetFileInformationByHandle(hFile, FileEndOfFileInfo, &eof, sizeof(eof));
CloseHandle(hFile);
}
UnmapViewOfFile(pv);
}
}
}
return GetLastError();
}
The MSDN says that using ReadDirectoryChangesW implies the calling process having the Backup and Restore privileges.
Does this mean that only process launched under administrator account will work correctly?
I've tried the following code, it fails to enable the required privileges when running as a restricted user.
void enablePrivileges()
{
enablePrivilege(SE_BACKUP_NAME);
enablePrivilege(SE_RESTORE_NAME);
}
void enablePrivilege(LPCTSTR name)
{
HANDLE hToken;
DWORD status;
if (::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
{
TOKEN_PRIVILEGES tp = { 1 };
if( ::LookupPrivilegeValue(NULL, name, &tp.Privileges[0].Luid) )
{
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
BOOL result = ::AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL);
verify (result != FALSE);
status = ::GetLastError();
}
::CloseHandle(hToken);
}
}
Am I doing something wrong? Is there any workaround for using ReadDirectoryChangesW from a non-administrator user account? It seems that the .NET's FileSystemWatcher can do this. Thanks!
Update: Here is the full code of the class:
class DirectoryChangesWatcher
{
public:
DirectoryChangesWatcher(wstring directory)
{
enablePrivileges();
hDir = ::CreateFile(directory.c_str(),
FILE_LIST_DIRECTORY | FILE_FLAG_OVERLAPPED,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, NULL);
ensure (hDir != INVALID_HANDLE_VALUE, err::SystemException);
::ZeroMemory(&overlapped, sizeof(OVERLAPPED));
overlapped.hEvent = dirChangedEvent.getHandle();
}
~DirectoryChangesWatcher() { ::CloseHandle(hDir); }
public:
Event& getEvent() { return dirChangedEvent; }
FILE_NOTIFY_INFORMATION* getBuffer() { return buffer; }
public:
void startAsyncWatch()
{
DWORD bytesReturned;
const BOOL res = ::ReadDirectoryChangesW(
hDir,
&buffer,
sizeof(buffer),
TRUE,
FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_SIZE,
&bytesReturned,
&overlapped,
NULL);
ensure(res != FALSE, err::SystemException);
}
private:
void enablePrivileges()
{
enablePrivilege(SE_BACKUP_NAME);
enablePrivilege(SE_RESTORE_NAME);
}
void enablePrivilege(LPCTSTR name)
{
HANDLE hToken;
DWORD status;
if (::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
{
TOKEN_PRIVILEGES tp = { 1 };
if( ::LookupPrivilegeValue(NULL, name, &tp.Privileges[0].Luid) )
{
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
BOOL result = ::AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL);
verify (result != FALSE);
status = ::GetLastError();
}
::CloseHandle(hToken);
}
}
private:
HANDLE hDir;
OVERLAPPED overlapped;
Event dirChangedEvent;
FILE_NOTIFY_INFORMATION buffer[1024];
};
}
Update: Good news! It turned out the problem really was in the FILE_SHARE_WRITE flag in the call to CreateFile. The notifications did not come unless I was an admin. When I removed this flag, everything is now working ona non-admin account too.
I have used ReadDirectoryChangesW without requiring administrator rights, at least on Vista. I don't think you need to manually elevate the process in order to use it on a folder the user already has permissions to see.
It would be more helpful to see the actual code you are using to call ReadDirectoryChangesW, including how you create the handle you pass in.
I don't see where MSDN says you need either backup or restore privileges. It instructs you to call CreateFile with the File_Flag_Backup_Semantics flag set, and in that flag's description, MSDN says this:
The system ensures that the calling process overrides file security checks when the process has SE_BACKUP_NAME and SE_RESTORE_NAME privileges.
The way I read it, if you have those privileges, then the system will override the file security checks for you. So if you don't have those privileges, then the program will simply continue to be bound by whatever file security checks would ordinarily be in effect.
Alex, in your CreateFile() call you put FILE_FLAG_OVERLAPPED into wrong position. It should be moved from 2nd to 6th parameter.