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.
Related
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 have created a folder with CreateDirectoryW to which I haven't got access. I used nullptr as a security descriptor, but for some reason it didn't copy the parent folder's ACLs, but instead made the folder inaccessible.
I can't view or change the owner. takeown, icacls, SetNamedSecurityInfoW, all from elevated processes or command prompts, fail with ERROR_ACCESS_DENIED.
Do I have any chance of deleting this folder in Windows (Shell or C++) before trying a Linux live CD which hopefully doesn't care about the ACLs?
You just need to enable backup (or restore) privilege:
#include <Windows.h>
#include <stdio.h>
int wmain(int argc, wchar_t ** argv)
{
// argv[1] must contain the directory to remove
HANDLE hToken;
struct
{
DWORD PrivilegeCount;
LUID_AND_ATTRIBUTES Privileges[1];
} tkp;
if (OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
{
LookupPrivilegeValue(NULL, SE_BACKUP_NAME, &tkp.Privileges[0].Luid);
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(hToken, FALSE,
(PTOKEN_PRIVILEGES)&tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0))
{
printf("AdjustTokenPrivileges: %u\n", GetLastError());
return 1;
}
if (GetLastError() != ERROR_SUCCESS)
{
// This happens if you don't have the privilege
printf("AdjustTokenPrivileges: %u\n", GetLastError());
return 1;
}
CloseHandle(hToken);
}
if (!RemoveDirectory(argv[1]))
{
printf("RemoveDirectory: %u\n", GetLastError());
return 1;
}
printf("OK\n");
return 0;
}
Note that in the interests of conciseness, some error handling has been omitted. Also note that AdjustTokenPrivileges() is one of the few special cases where calling GetLastError() is meaningful even though the call succeeded; it will return ERROR_SUCCESS or ERROR_NOT_ALL_ASSIGNED depending on whether you actually have all of the privileges you were attempting to enable.
This is a fairly general solution for bypassing security permissions on files. It works for most API calls, although in some cases (most notably CreateFile) you must provide a special flag in order to make use of backup privilege. As well as deleting files or removing directories that you don't have permission to, you can change attributes, change permissions, and even assign ownership to somebody else, which is not otherwise permitted.
assume that file not open with incompatible to delete share flags and no section on file.
for delete file enough 2 things - we have FILE_DELETE_CHILD on parent folder. and file is not read only. in this case call ZwDeleteFile (but not DeleteFile or RemoveDirectory - both this api will fail if file have empty DACL) is enough. in case file have read-only attribute - ZwDeleteFile fail with code STATUS_CANNOT_DELETE. in this case we need first remove read only. for this need open file with FILE_WRITE_ATTRIBUTES access. we can do this if we have SE_RESTORE_PRIVILEGE and set FILE_OPEN_FOR_BACKUP_INTENT option in call to ZwOpenFile. so code for delete file can be next:
NTSTATUS DeleteEx(POBJECT_ATTRIBUTES poa)
{
NTSTATUS status = ZwDeleteFile(poa);
if (status == STATUS_CANNOT_DELETE)
{
BOOLEAN b;
RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, TRUE, FALSE, &b);
HANDLE hFile;
IO_STATUS_BLOCK iosb;
if (0 <= (status = NtOpenFile(&hFile, FILE_WRITE_ATTRIBUTES, poa, &iosb, FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_BACKUP_INTENT)))
{
static FILE_BASIC_INFORMATION fbi = {{},{},{},{}, FILE_ATTRIBUTE_NORMAL};
status = ZwSetInformationFile(hFile, &iosb, &fbi, sizeof(fbi), FileBasicInformation);
NtClose(hFile);
if (0 <= status)
{
status = ZwDeleteFile(poa);
}
}
}
return status;
}
I'm a Domain Admin and I want to take ownership of some shared folders on some server of my domain programmatically in API(for example C++). I did some reading work and found that a Domain Admin is in the member machine's Local Admins group by default, and the Local Admins users can take ownership anyway. I just wrtie some code in this way but still encountered ERROR_ACCESS_DENIED when getting the owner sid using GetNamedSecurityInfo? Where's the problem?
Something interesting is: When I changed the GetNamedSecurityInfo's secound argument from SE_FILE_OBJECT to SE_LMSHARE, it would succeed(also set one). But I didn't see the owner changed in the "Security" tab of folder's properties. I know a "share" permission is different with "security" one. a "share" permission even don't have a owner. So what owner did I get when calling GetNamedSecurityInfo by the SE_LMSHARE argument?
Here's the function i use for Taking ownership for the folder "strFileName", on server "strServerName", the owner changed to is just the Domain Admin account known as "strDomainName" "strUserName" "strPassword", orginal owner is reserved in "pOriginSID".
I got error code 5 in the GetNamedSecurityInfo call (also the Set one). I also write a impersonation method "logOnByUserPassword" which seems not to work, i paste it below.
HANDLE ADPermissionSearch::getAccessTokenByCredential(CString strDomainName, CString strUserName, CString strPassword)
{
CString strUPNUserName = strUserName + _T("#") + strDomainName;
HANDLE hToken;
BOOL bResult;
//bResult = LogonUser(strUserName, strDomainName, strPassword, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT,
// &hToken);
if (strDomainName != _T(""))
{
bResult = LogonUser(strUPNUserName, _T(""), strPassword, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT,
&hToken);
}
else
{
bResult = LogonUser(strUserName, _T("."), strPassword, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT,
&hToken);
}
if (bResult == FALSE)
{
MyMessageBox_Error(_T("getAccessTokenByCredential Error."), _T("Error"));
return FALSE;
}
else
{
return hToken;
}
}
int ADPermissionSearch::takeOwnership(CString strServerName, CString strFileName, CString strDomainName, CString strUserName, CString strPassword, __out PSID &pOriginSID)
{
CString strUNCFileName = _T("\\\\") + strServerName + _T("\\") + strFileName;
_bstr_t bstrUNCFileName = _bstr_t(strUNCFileName);
PSID pSIDAdmin = NULL;
SID_IDENTIFIER_AUTHORITY SIDAuthNT = SECURITY_NT_AUTHORITY;
HANDLE hToken = NULL;
DWORD dwRes;
// Create a SID for the BUILTIN\Administrators group.
if (!AllocateAndInitializeSid(&SIDAuthNT, 2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&pSIDAdmin))
{
if (pSIDAdmin)
FreeSid(pSIDAdmin);
if (hToken)
CloseHandle(hToken);
MyMessageBox_Error(_T("takeOwnership"));
return 0;
}
// If the preceding call failed because access was denied,
// enable the SE_TAKE_OWNERSHIP_NAME privilege, create a SID for
// the Administrators group, take ownership of the object, and
// disable the privilege. Then try again to set the object's DACL.
// Open a handle to the access token for the calling process.
/*
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES,
&hToken))
{
if (pSIDAdmin)
FreeSid(pSIDAdmin);
if (hToken)
CloseHandle(hToken);
MyMessageBox_Error(_T("takeOwnership"));
return 0;
}
*/
if ((hToken = getAccessTokenByCredential(strDomainName, strUserName, strPassword)) == NULL)
{
if (pSIDAdmin)
FreeSid(pSIDAdmin);
if (hToken)
CloseHandle(hToken);
MyMessageBox_Error(_T("takeOwnership"));
return 0;
}
// Enable the SE_TAKE_OWNERSHIP_NAME privilege.
if (!setPrivilege(hToken, SE_TAKE_OWNERSHIP_NAME, TRUE))
{
if (pSIDAdmin)
FreeSid(pSIDAdmin);
if (hToken)
CloseHandle(hToken);
MyMessageBox_Error(_T("takeOwnership"));
return 0;
}
// Get the original owner in the object's security descriptor.
dwRes = GetNamedSecurityInfo(
bstrUNCFileName, // name of the object
SE_FILE_OBJECT, // type of object
OWNER_SECURITY_INFORMATION, // change only the object's owner
&pOriginSID, // SID of Administrator group
NULL,
NULL,
NULL,
NULL);
if (dwRes != ERROR_SUCCESS)
{
if (pSIDAdmin)
FreeSid(pSIDAdmin);
if (hToken)
CloseHandle(hToken);
MyMessageBox_Error(_T("takeOwnership"));
return 0;
}
// Set the owner in the object's security descriptor.
dwRes = SetNamedSecurityInfo(
bstrUNCFileName, // name of the object
SE_FILE_OBJECT, // type of object
OWNER_SECURITY_INFORMATION, // change only the object's owner
pSIDAdmin, // SID of Administrator group
NULL,
NULL,
NULL);
if (dwRes != ERROR_SUCCESS)
{
if (pSIDAdmin)
FreeSid(pSIDAdmin);
if (hToken)
CloseHandle(hToken);
MyMessageBox_Error(_T("takeOwnership"));
return 0;
}
// Disable the SE_TAKE_OWNERSHIP_NAME privilege.
if (!setPrivilege(hToken, SE_TAKE_OWNERSHIP_NAME, FALSE))
{
if (pSIDAdmin)
FreeSid(pSIDAdmin);
if (hToken)
CloseHandle(hToken);
MyMessageBox_Error(_T("takeOwnership"));
return 0;
}
return 1;
}
BOOL ADDirectorySearch::logOnByUserPassword(CString strDomainName, CString strUserName, CString strPassword)
{
CString strUPNUserName = strUserName + _T("#") + strDomainName;
HANDLE hToken;
BOOL bResult;
//bResult = LogonUser(strUserName, strDomainName, strPassword, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT,
// &hToken);
if (strDomainName != _T(""))
{
bResult = LogonUser(strUPNUserName, _T(""), strPassword, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT,
&hToken);
}
else
{
bResult = LogonUser(strUserName, _T("."), strPassword, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_DEFAULT,
&hToken);
}
if (bResult == FALSE)
{
MyMessageBox_Error(_T("logOnByUserPassword Error."), _T("Error"));
return FALSE;
}
else
{
bResult = ImpersonateLoggedOnUser(hToken);
if (bResult == FALSE)
{
MyMessageBox_Error(_T("logOnByUserPassword Error."), _T("Error"));
return FALSE;
}
else
{
return TRUE;
}
}
}
Local admins are subject to the usual Windows security checks with one exception: they can always take ownership of a secured object regardless of the permissions. This ensures that admins are always able to regain control.
However, you are not trying to take ownership, you are trying to read the current owner and you don't necessarily have permission to do that.
It's not clear from your code why you are trying to read the owner. You don't seem to do anything with it. Maybe remove the call to GetNamedSecurityInfo altogether.
Update
The intention is to write a program that checks the DACLs on every share. So it needs to save the current owner, take ownership, read the DACLs and restore the owner. But the current owner cannot be read until ownership has been taken.
I think this behaviour is by design. The original intention was that admins were able to take ownership, but not hide the fact that they had from the owner of an object, though there are ways around this. For example, for files you can read the complete security descriptor (including the owner) by enabling the backup privilege, calling BackupRead and parsing the output (a sequence of WIN32_STREAM_ID structures each followed by data). I don't know if there's a simpler way.
Information about shares is stored in the registry under:
SYSTEM\CurrentControlSet\Services\LanmanServer\Shares
The security info seems to be stored in the Security subkey, in a value named after the share. This binary value seems to be a security descriptor so you can read the owner with GetSecurityDescriptorOwner. You can also read all the other security info from this security descriptor, so you shouldn't need to change the owner at all.
I noticed that GoogleToolbarNotifier.exe cannot be killed from Process Explorer. It returns "Access Denied". It runs as the user, it runs "Normal" priority, and it runs from Program Files.
How did they do it?
I think there might be a way to modify the ACL, or mark the process as 'critical', but I cannot seem to locate anything.
Update:
I found the answer with a good bit of digging. #Alex K. was correct in that PROCESS_TERMINATE permission was removed for the process, but I wanted to supply the answer in code:
static const bool ProtectProcess()
{
HANDLE hProcess = GetCurrentProcess();
EXPLICIT_ACCESS denyAccess = {0};
DWORD dwAccessPermissions = GENERIC_WRITE|PROCESS_ALL_ACCESS|WRITE_DAC|DELETE|WRITE_OWNER|READ_CONTROL;
BuildExplicitAccessWithName( &denyAccess, _T("CURRENT_USER"), dwAccessPermissions, DENY_ACCESS, NO_INHERITANCE );
PACL pTempDacl = NULL;
DWORD dwErr = 0;
dwErr = SetEntriesInAcl( 1, &denyAccess, NULL, &pTempDacl );
// check dwErr...
dwErr = SetSecurityInfo( hProcess, SE_KERNEL_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, pTempDacl, NULL );
// check dwErr...
LocalFree( pTempDacl );
CloseHandle( hProcess );
return dwErr == ERROR_SUCCESS;
}
The code given in the question is misleading. It constructs a DACL with no allow entries and one deny entry; that might make sense if you were applying the DACL to a file with inheritance enabled, but in this case the deny entry is redundant. In the Windows access control model, if a DACL exists but contains no matching ACE, access is implicitly denied.
Here's my version, which applies an empty DACL, denying all access. (Note that it returns an error code rather than a boolean.)
DWORD ProtectProcess(void)
{
HANDLE hProcess = GetCurrentProcess();
PACL pEmptyDacl;
DWORD dwErr;
// using malloc guarantees proper alignment
pEmptyDacl = (PACL)malloc(sizeof(ACL));
if (!InitializeAcl(pEmptyDacl, sizeof(ACL), ACL_REVISION))
{
dwErr = GetLastError();
}
else
{
dwErr = SetSecurityInfo(hProcess, SE_KERNEL_OBJECT,
DACL_SECURITY_INFORMATION, NULL, NULL, pEmptyDacl, NULL);
}
free(pEmptyDacl);
return dwErr;
}
When running my copy of that has Deny set on the Terminate permission (Process Explorer shows this).
Presumably they call SetKernelObjectSecurity to change/remove the ACLs when their process loads.
I have tried to do it with the help of writing windows services ..and then making some changes
here is the link to write a simple windows service
http://code.msdn.microsoft.com/windowsdesktop/CppWindowsService-cacf4948
and we can update Servicabase.cpp file with the following two statements..
fCanStop=FALSE;
fCanShutdown=FALSE;
I have a VC++ console app and I need to check to see if another process is running. I don't have the window title, all I have is the executable name. How do I get the process handle / PID for it? Can I enumerate the processes running with this .exe ?
Use the CreateToolhelp32Snapshot Function
hSnapShot = FCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
Followed by Process32First and Process32Next.
You will get a PROCESSENTRY32 struct as follows with an szExeFile member.
PROCESSENTRY32W processInfo;
processInfo.szExeFile
Make sure to first acquire the privilege SeDebugPrivilege before enumerating, that way you will get all processes across all sessions and users.
To acquire the privilege so you get all sessions:
acquirePrivilegeByName(SE_DEBUG_NAME);// SeDebugPrivilege
Where acquirePrivilegeByName is defined as:
BOOL acquirePrivilegeByName(
const TCHAR *szPrivilegeName)
{
HANDLE htoken;
TOKEN_PRIVILEGES tkp;
DWORD dwerr;
//---------------- adjust process token privileges to grant privilege
if (szPrivilegeName == NULL)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (!LookupPrivilegeValue(NULL, szPrivilegeName, &(tkp.Privileges[0].Luid)))
return FALSE;
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &htoken))
return FALSE;
if (!AdjustTokenPrivileges(htoken, FALSE, &tkp, 0, NULL, NULL) ||
GetLastError() != ERROR_SUCCESS) // may equal ERROR_NOT_ALL_ASSIGNED
{
dwerr = GetLastError();
CloseHandle(htoken);
SetLastError(dwerr);
return FALSE;
}
CloseHandle(htoken);
SetLastError(ERROR_SUCCESS);
return TRUE;
} //acquirePrivilegeByName()
If you need the full process image name you can use QueryFullProcessImageName, but the szExeFile member may be enough for your needs.
You can use EnumProcesses to enumerate the processes on a system.
You'll need to use OpenProcess to get a process handle, then QueryFullProcessImageName to get the processes executable.