You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
194 lines
6.4 KiB
194 lines
6.4 KiB
//+----------------------------------------------------------------------------
|
|
//
|
|
// Job Scheduler
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 2002.
|
|
//
|
|
// File: FolderSecurity.cpp
|
|
//
|
|
// Contents: Class to read folder security and perform access checks against it
|
|
//
|
|
// History: 5-April-02 HHance created
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include <Windows.h>
|
|
#include <FolderSecurity.h>
|
|
|
|
// disable cilly warning about bools (we'll put it back, later...)
|
|
#pragma warning( push )
|
|
#pragma warning( disable: 4800)
|
|
|
|
// returns S_OK if the folder's DACL allows the requested access
|
|
// E_ACCESSDENIED if not
|
|
// E_NOTFOUND if file/folder cannot be found
|
|
// other error on other error
|
|
|
|
// HANDLE clientToken // handle to client access token
|
|
// DWORD desiredAccess // requested access rights
|
|
//
|
|
// NOTE: Do not use the GENERIC_XXX rights, they must already have been mapped
|
|
//
|
|
// Suggested rights:
|
|
// FILE_READ_DATA
|
|
// FILE_WRITE_DATA
|
|
// FILE_EXECUTE
|
|
// FILE_DELETE_CHILD
|
|
//
|
|
HRESULT FolderAccessCheck(const WCHAR* pFolderName, HANDLE clientToken, DWORD desiredAccess)
|
|
{
|
|
if ((desiredAccess == 0) ||
|
|
(pFolderName == NULL))
|
|
return false;
|
|
|
|
|
|
HRESULT hr = E_ACCESSDENIED;
|
|
|
|
DWORD dSize = 0;
|
|
|
|
// call once to see how big a buffer we need
|
|
if (!GetFileSecurityW(pFolderName, DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION, NULL, 0, &dSize))
|
|
{
|
|
DWORD dwErr = GetLastError();
|
|
if (dwErr != ERROR_INSUFFICIENT_BUFFER)
|
|
return HRESULT_FROM_WIN32(dwErr);
|
|
}
|
|
|
|
PSECURITY_DESCRIPTOR pSD = NULL;
|
|
|
|
if ((dSize > 0) && (pSD = new BYTE[dSize + 1]))
|
|
{
|
|
// get it for real (hopefully)
|
|
if (GetFileSecurityW(pFolderName, DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION, pSD, dSize, &dSize))
|
|
{
|
|
// all the args access check could ever want
|
|
GENERIC_MAPPING gm;
|
|
gm.GenericRead = FILE_GENERIC_READ;
|
|
gm.GenericWrite = FILE_GENERIC_WRITE;
|
|
gm.GenericExecute = FILE_GENERIC_EXECUTE;
|
|
gm.GenericAll = FILE_ALL_ACCESS;
|
|
|
|
PRIVILEGE_SET ps;
|
|
DWORD psLength = sizeof(PRIVILEGE_SET);
|
|
|
|
// guilty until proven innocent
|
|
BOOL accessStatus = FALSE;
|
|
DWORD grantedAccess = 0;
|
|
|
|
if (AccessCheck(pSD, clientToken, desiredAccess, &gm, &ps, &psLength, &grantedAccess, &accessStatus))
|
|
if (accessStatus && ((grantedAccess & desiredAccess) == desiredAccess))
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
delete[] pSD;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// helper function - uses current thread/process token
|
|
// to call AccessCheck
|
|
HRESULT FolderAccessCheckOnThreadToken(const WCHAR* pFolderName, DWORD desiredAccess)
|
|
{
|
|
HANDLE hToken = INVALID_HANDLE_VALUE;
|
|
|
|
// Use the thread's own token if he's got one.
|
|
HRESULT hr = E_ACCESSDENIED;
|
|
|
|
if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken))
|
|
hr = FolderAccessCheck(pFolderName, hToken, desiredAccess);
|
|
else
|
|
// that didn't work, let's see if we can get the process token
|
|
{
|
|
if (ImpersonateSelf(SecurityImpersonation))
|
|
{
|
|
if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken))
|
|
hr = FolderAccessCheck(pFolderName, hToken, desiredAccess);
|
|
RevertToSelf();
|
|
}
|
|
}
|
|
|
|
if (hToken != INVALID_HANDLE_VALUE)
|
|
CloseHandle(hToken);
|
|
|
|
return hr;
|
|
}
|
|
|
|
// helper function - makes use of RPC Impersonation capabilities
|
|
// intended to be called from the task scheduler service process
|
|
// if bHandleImpersonation is true, this function calls RPCImpersonateClient and RPCRevertToSelf
|
|
HRESULT RPCFolderAccessCheck(const WCHAR* pFolderName, DWORD desiredAccess, bool bHandleImpersonation)
|
|
{
|
|
HRESULT hr = E_ACCESSDENIED;
|
|
|
|
bool bRet = true;
|
|
|
|
if (bHandleImpersonation)
|
|
bRet = (RPC_S_OK == RpcImpersonateClient(NULL));
|
|
|
|
if (bRet)
|
|
{
|
|
HANDLE hToken = INVALID_HANDLE_VALUE;
|
|
|
|
bRet = OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken);
|
|
|
|
if (bHandleImpersonation)
|
|
RpcRevertToSelf();
|
|
|
|
if (bRet)
|
|
hr = FolderAccessCheck(pFolderName, hToken, desiredAccess);
|
|
|
|
if (hToken != INVALID_HANDLE_VALUE)
|
|
CloseHandle(hToken);
|
|
}
|
|
|
|
return hr;
|
|
};
|
|
|
|
// helper function - makes use of COM impersonation capabilities
|
|
// ** Will Fail if COM hasn't been initialized **
|
|
// ** Or we can't imperonate the client **
|
|
HRESULT CoFolderAccessCheck(const WCHAR* pFolderName, DWORD desiredAccess)
|
|
{
|
|
bool bAlreadyImpersonated = false;
|
|
IServerSecurity* iSecurity = NULL;
|
|
HRESULT hr = E_ACCESSDENIED;
|
|
|
|
if (SUCCEEDED(hr = CoGetCallContext(IID_IServerSecurity, (void**)&iSecurity)))
|
|
{
|
|
// We were impersonating when we got here?
|
|
// if not - try to impersonate the client now.
|
|
bool bWeImpersonating = false;
|
|
if (bAlreadyImpersonated = iSecurity->IsImpersonating())
|
|
bWeImpersonating = true;
|
|
else
|
|
bWeImpersonating = SUCCEEDED(iSecurity->ImpersonateClient());
|
|
|
|
// if we've got a thread token, let the helper's helper help out
|
|
if (bWeImpersonating)
|
|
{
|
|
HANDLE hToken = INVALID_HANDLE_VALUE;
|
|
if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken))
|
|
{
|
|
if (!bAlreadyImpersonated)
|
|
iSecurity->RevertToSelf();
|
|
hr = FolderAccessCheck(pFolderName, hToken, desiredAccess);
|
|
CloseHandle(hToken);
|
|
}
|
|
else if (!bAlreadyImpersonated)
|
|
iSecurity->RevertToSelf();
|
|
}
|
|
|
|
iSecurity->Release();
|
|
}
|
|
else if ((hr == RPC_E_CALL_COMPLETE) || (hr == CO_E_NOTINITIALIZED))
|
|
hr = FolderAccessCheckOnThreadToken(pFolderName, desiredAccess);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
#pragma warning(pop)
|