Reputation: 11421
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).
Upvotes: 2
Views: 1264
Reputation: 3290
the only way you can do this is to run as a service, and impersonate the user from within the service. the service must be granted the impersonate privileve, which all services accounts are granted by default. when you impersonate the caller, you will probably have to impersonate for delegation so you can go off machine easily.
Upvotes: 0
Reputation: 5454
Backup and restore privilege together will provide full access to all files, full stop. These are available to LocalSystem. You must open files with FILE_FLAG_BACKUP_SEMANTICS to use this. Some Win32 APIs were not designed to be used with this and will not pass the flag on to the kernel, although in some of those cases you can use CreateFile to open the directory instead. (To the kernel, directories are just another kind of file).
If you really need to be able to access everything, I would say enable these privileges and do the scanning ops that should succeed regardless of the caller's security unimpersonated.
One outstanding issue is that the files could be locked or opened but not sharing access. There's no way to get around this from user mode (without killing the process that owns the resource, probably overkill). This is why the mainstream scanners I know of implement this function with a kernel mode filesystem filter driver.
Also, think about auditing: do you want audit entries to show up for LocalSystem or the user associated with the calling process?
Upvotes: 1
Reputation: 3290
this all seems really funky for a DLL! this sounds like a job for a service rather than a DLL, or, if you're going to allow a user account to remove data put there by a privileged account, then why not simply set the ACL on the object that allows the delete operation?
With that said, what are you actually trying to do? A user account should not generally be able to remove data put there by an admininstrtive account!
Upvotes: 0