Temporarily impersonate and enable privileges?
- by Luke
We maintain a DLL that does a lot of system-related things; traversing the file system, registry, etc. The callers of this DLL may or may not be using impersonation. In order to better support all possible scenarios I'm trying to modify it to be smarter. I'll use the example of deleting a file. Currently we just call DeleteFile(), and if that fails that's the end of that. I've come up with the following:
BOOL TryReallyHardToDeleteFile(LPCTSTR lpFileName)
{
// 1. caller without privilege
BOOL bSuccess = DeleteFile(lpFileName);
DWORD dwError = GetLastError();
if(!bSuccess && dwError == ERROR_ACCESS_DENIED)
{
// failed with access denied; try with privilege
DWORD dwOldRestorePrivilege = 0;
BOOL bHasRestorePrivilege = SetPrivilege(SE_RESTORE_NAME, SE_PRIVILEGE_ENABLED, &dwOldRestorePrivilege);
if(bHasRestorePrivilege)
{
// 2. caller with privilege
bSuccess = DeleteFile(lpFileName);
dwError = GetLastError();
SetPrivilege(SE_RESTORE_NAME, dwOldRestorePrivilege, NULL);
}
if(!bSuccess && dwError == ERROR_ACCESS_DENIED)
{
// failed with access denied; if caller is impersonating then try as process
HANDLE hToken = NULL;
if(OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_IMPERSONATE, TRUE, &hToken))
{
if(RevertToSelf())
{
// 3. process without privilege
bSuccess = DeleteFile(lpFileName);
dwError = GetLastError();
if(!bSuccess && dwError == ERROR_ACCESS_DENIED)
{
// failed with access denied; try with privilege
bHasRestorePrivilege = SetPrivilege(SE_RESTORE_NAME, SE_PRIVILEGE_ENABLED, &dwOldRestorePrivilege);
if(bHasRestorePrivilege)
{
// 4. process with privilege
bSuccess = DeleteFile(lpFileName);
dwError = GetLastError();
SetPrivilege(SE_RESTORE_NAME, dwOldRestorePrivilege, NULL);
}
}
SetThreadToken(NULL, hToken);
}
CloseHandle(hToken);
hToken = NULL;
}
}
}
if(!bSuccess)
{
SetLastError(dwError);
}
return bSuccess;
}
So first it tries as the caller. If that fails with access denied, it temporarily enables privileges in the caller's token and tries again. If that fails with access denied and the caller is impersonating, it temporarily unimpersonates and tries again. If that fails with access denied, it temporarily enables privileges in the process token and tries again. I think this should handle pretty much any situation, but I was wondering if there was a better way to achieve this? There are a lot of operations that we would potentially want to use this method (i.e. pretty much any operation that accesses securable objects).