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.
1862 lines
40 KiB
1862 lines
40 KiB
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name :
|
|
admacl.cxx
|
|
|
|
Abstract:
|
|
This module defines Admin API Access Check API
|
|
|
|
Author:
|
|
|
|
Philippe Choquier 02-Dec-1996
|
|
--*/
|
|
#include "precomp.hxx"
|
|
|
|
#include <imd.h>
|
|
#include <iadm.h>
|
|
#include <mb.hxx>
|
|
|
|
#include "admacl.hxx"
|
|
#include "coiadm.hxx"
|
|
|
|
#ifndef ARRAYSIZE
|
|
#define ARRAYSIZE(_a) (sizeof((_a))/sizeof(*(_a)))
|
|
#endif
|
|
|
|
//
|
|
// Globals
|
|
//
|
|
CInitAdmacl g_cinitadmacl;
|
|
CAdminAclCache g_AclCache;
|
|
|
|
CInitAdmacl::CInitAdmacl()
|
|
{
|
|
DBG_REQUIRE(SUCCEEDED(g_AclCache.Init()));
|
|
}
|
|
|
|
CInitAdmacl::~CInitAdmacl()
|
|
{
|
|
DBG_ASSERT(g_AclCache.IsEmpty()==S_OK);
|
|
}
|
|
|
|
//
|
|
// Generic mapping for Application access check
|
|
|
|
GENERIC_MAPPING g_FileGenericMapping =
|
|
{
|
|
FILE_READ_DATA,
|
|
FILE_WRITE_DATA,
|
|
FILE_EXECUTE,
|
|
FILE_ALL_ACCESS
|
|
};
|
|
|
|
BOOL
|
|
AdminAclNotifyClose(
|
|
LPVOID pvAdmin,
|
|
METADATA_HANDLE hAdminHandle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Notify admin acl access check module of close request
|
|
|
|
Arguments:
|
|
|
|
pvAdmin - admin context
|
|
hAdminHandle - handle to metadata
|
|
|
|
Returns:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
g_AclCache.Remove(pvAdmin, hAdminHandle);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
AdminAclDisableAclCache()
|
|
{
|
|
g_AclCache.Disable();
|
|
}
|
|
|
|
void
|
|
AdminAclEnableAclCache()
|
|
{
|
|
g_AclCache.Enable();
|
|
}
|
|
|
|
BOOL
|
|
AdminAclFlushCache(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Flush cache
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Returns:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
g_AclCache.Flush();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
AdminAclNotifySetOrDeleteProp(
|
|
METADATA_HANDLE ,
|
|
DWORD dwId
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Notify admin acl access check module of update to metabase
|
|
|
|
Arguments:
|
|
|
|
hAdminHandle - handle to metadata
|
|
dwId - property ID set or deleted
|
|
|
|
Returns:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
// flush cache for all ACLs
|
|
if ( dwId == MD_ADMIN_ACL )
|
|
{
|
|
g_AclCache.Flush();
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static HRESULT
|
|
_GetThreadToken(
|
|
HANDLE *phThreadToken)
|
|
/*++
|
|
Routine Description:
|
|
Returns handle to the thread impersonation token.
|
|
The caller must close the handle.
|
|
|
|
Arguments:
|
|
phThreadToken - Out, a handle to the thread token
|
|
|
|
Returns:
|
|
S_OK on success, E_* on failure
|
|
--*/
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
HANDLE hThread;
|
|
HANDLE hToken=NULL;
|
|
IServerSecurity* pServerSecurity=NULL;
|
|
|
|
// Check args
|
|
if (phThreadToken)
|
|
{
|
|
// Initialize to NULL
|
|
*phThreadToken=NULL;
|
|
}
|
|
else
|
|
{
|
|
hr=E_INVALIDARG;
|
|
goto exit;
|
|
}
|
|
|
|
// Get the pseudo handle to the current thread
|
|
hThread=GetCurrentThread();
|
|
|
|
//
|
|
// test if already impersonated ( inprocess call w/o marshalling )
|
|
// If not call DCOM to retrieve security context & impersonate, then
|
|
// extract access token.
|
|
//
|
|
if (!OpenThreadToken(hThread, TOKEN_EXECUTE|TOKEN_QUERY, TRUE, &hToken))
|
|
{
|
|
// this thread is not impersonating -> process token
|
|
|
|
|
|
// Get the DCOM server security object
|
|
hr=CoGetCallContext(IID_IServerSecurity, (VOID**)&pServerSecurity);
|
|
if (FAILED(hr))
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
// Impersonate the caller
|
|
hr=pServerSecurity->ImpersonateClient();
|
|
if (FAILED(hr))
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
// Try again to get the token
|
|
if (!OpenThreadToken(hThread, TOKEN_EXECUTE|TOKEN_QUERY, TRUE, &hToken))
|
|
{
|
|
hr=HRESULT_FROM_WIN32(GetLastError());
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
// Return the token
|
|
*phThreadToken=hToken;
|
|
hToken=NULL;
|
|
|
|
exit:
|
|
// Cleanup
|
|
if (pServerSecurity)
|
|
{
|
|
if (pServerSecurity->IsImpersonating())
|
|
{
|
|
// better here, since
|
|
// COM otherwise is reclaiming the
|
|
// thread token too late.
|
|
pServerSecurity->RevertToSelf();
|
|
}
|
|
pServerSecurity->Release();
|
|
}
|
|
if (hToken)
|
|
{
|
|
CloseHandle(hToken);
|
|
}
|
|
|
|
// Done
|
|
return (hr);
|
|
}
|
|
|
|
BOOL
|
|
AdminAclAccessCheck(
|
|
IMDCOM* pMDCom,
|
|
LPVOID pvAdmin,
|
|
METADATA_HANDLE hAdminHandle,
|
|
LPCWSTR pszRelPath,
|
|
DWORD dwId, // check for MD_ADMIN_ACL, must have special right to write them
|
|
// can be 0 for non ID based access ( enum, create )
|
|
// or -1 for GetAll
|
|
DWORD dwAccess, // METADATA_PERMISSION_*
|
|
COpenHandle* pohHandle,
|
|
LPBOOL pfEnableSecureAccess
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Perform access check based on path, ID and access type
|
|
|
|
Arguments:
|
|
|
|
hAdminHandle - Open handle
|
|
pszRelPath - path to object ( relative to open path )
|
|
dwId - property ID
|
|
dwAccess - access type, as defined by metabase header
|
|
pfEnableSecureAccess - update with TRUE if read access to secure properties granted
|
|
|
|
Returns:
|
|
|
|
TRUE on success, FALSE on failure
|
|
|
|
--*/
|
|
{
|
|
CAdminAcl* pAdminAclCurrent = NULL;
|
|
BOOL bReturn = TRUE;
|
|
HANDLE hAccTok = NULL;
|
|
LPBYTE pAcl = NULL;
|
|
DWORD dwRef;
|
|
BOOL fIsAnyAcl;
|
|
BOOL bAddToCache = FALSE;
|
|
DWORD dwAcc;
|
|
DWORD dwGrantedAccess;
|
|
BYTE PrivSet[400];
|
|
DWORD cbPrivilegeSet = sizeof(PrivSet);
|
|
BOOL fAccessGranted;
|
|
|
|
if ( pfEnableSecureAccess )
|
|
{
|
|
*pfEnableSecureAccess = TRUE;
|
|
}
|
|
|
|
if ( pszRelPath == NULL )
|
|
{
|
|
pszRelPath = L"";
|
|
}
|
|
|
|
// Get the token
|
|
_GetThreadToken(&hAccTok);
|
|
// can be non null only if obtained from thread
|
|
if (hAccTok==NULL)
|
|
{
|
|
//
|
|
// For now, assume failure to get IServerSecurity means we are
|
|
// in the SYSTEM context, so grant access.
|
|
//
|
|
|
|
bReturn = TRUE;
|
|
goto exit;
|
|
}
|
|
|
|
// find match : look for exact path match
|
|
// keep at most N entry, reset at top of list when accessed
|
|
// Investigate: if in <NSE> : cut-off point just before <nse>
|
|
g_AclCache.Find(pvAdmin, hAdminHandle, pszRelPath, &pAdminAclCurrent);
|
|
|
|
//
|
|
// BUGBUG This checking only checks path and handle, not DCOM instance
|
|
// So there could be incorrect matches.
|
|
//
|
|
|
|
if( pAdminAclCurrent == NULL )
|
|
{
|
|
pAdminAclCurrent = new CAdminAcl;
|
|
if ( pAdminAclCurrent==NULL )
|
|
{
|
|
//
|
|
// failed to create new cache entry
|
|
//
|
|
|
|
bReturn = FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
// read ACL
|
|
|
|
if ( !pohHandle->GetAcl( pMDCom, pszRelPath, &pAcl, &dwRef ) )
|
|
{
|
|
pAcl = NULL;
|
|
dwRef = NULL;
|
|
}
|
|
|
|
//
|
|
// BUGBUG should normalize the path so /x and x don't generate
|
|
// 2 entries
|
|
//
|
|
|
|
//
|
|
// If path is too long,
|
|
// Go ahead and check the ACL, but don't put in cache
|
|
//
|
|
|
|
bReturn = pAdminAclCurrent->Init( pMDCom,
|
|
pvAdmin,
|
|
hAdminHandle,
|
|
pszRelPath,
|
|
pAcl,
|
|
dwRef,
|
|
&bAddToCache );
|
|
//
|
|
// Currently no possible failures
|
|
//
|
|
DBG_ASSERT(bReturn);
|
|
|
|
if ( !bReturn )
|
|
{
|
|
|
|
|
|
goto exit;
|
|
}
|
|
|
|
if (bAddToCache)
|
|
{
|
|
g_AclCache.Add(pAdminAclCurrent);
|
|
}
|
|
|
|
}
|
|
|
|
DBG_ASSERT(pAdminAclCurrent);
|
|
|
|
//
|
|
// Access check
|
|
//
|
|
pAcl = pAdminAclCurrent->GetAcl();
|
|
|
|
if (pAcl==NULL)
|
|
{
|
|
//
|
|
// No ACL : access check succeed
|
|
//
|
|
|
|
bReturn = TRUE;
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Protected properties require EXECUTE access rights instead of WRITE
|
|
//
|
|
|
|
if ( dwAccess & METADATA_PERMISSION_WRITE )
|
|
{
|
|
if ( dwId == MD_ADMIN_ACL ||
|
|
dwId == MD_VPROP_ADMIN_ACL_RAW_BINARY ||
|
|
dwId == MD_APPPOOL_ORPHAN_ACTION_EXE ||
|
|
dwId == MD_APPPOOL_ORPHAN_ACTION_PARAMS ||
|
|
dwId == MD_APPPOOL_AUTO_SHUTDOWN_EXE ||
|
|
dwId == MD_APPPOOL_AUTO_SHUTDOWN_PARAMS ||
|
|
dwId == MD_APPPOOL_IDENTITY_TYPE ||
|
|
dwId == MD_APP_APPPOOL_ID ||
|
|
dwId == MD_APP_ISOLATED ||
|
|
dwId == MD_VR_PATH ||
|
|
dwId == MD_ACCESS_PERM ||
|
|
dwId == MD_VR_USERNAME ||
|
|
dwId == MD_VR_PASSWORD ||
|
|
dwId == MD_ANONYMOUS_USER_NAME ||
|
|
dwId == MD_ANONYMOUS_PWD ||
|
|
dwId == MD_LOGSQL_USER_NAME ||
|
|
dwId == MD_LOGSQL_PASSWORD ||
|
|
dwId == MD_WAM_USER_NAME ||
|
|
dwId == MD_WAM_PWD ||
|
|
dwId == MD_AD_CONNECTIONS_USERNAME ||
|
|
dwId == MD_AD_CONNECTIONS_PASSWORD ||
|
|
dwId == MD_MAX_BANDWIDTH ||
|
|
dwId == MD_MAX_BANDWIDTH_BLOCKED ||
|
|
dwId == MD_ISM_ACCESS_CHECK ||
|
|
dwId == MD_FILTER_LOAD_ORDER ||
|
|
dwId == MD_FILTER_ENABLED ||
|
|
dwId == MD_FILTER_IMAGE_PATH ||
|
|
dwId == MD_SECURE_BINDINGS ||
|
|
dwId == MD_SERVER_BINDINGS ||
|
|
dwId == MD_ASP_ENABLECLIENTDEBUG ||
|
|
dwId == MD_ASP_ENABLESERVERDEBUG ||
|
|
dwId == MD_ASP_ENABLEPARENTPATHS ||
|
|
dwId == MD_ASP_ERRORSTONTLOG ||
|
|
dwId == MD_ASP_KEEPSESSIONIDSECURE ||
|
|
dwId == MD_ASP_LOGERRORREQUESTS ||
|
|
dwId == MD_ASP_DISKTEMPLATECACHEDIRECTORY ||
|
|
dwId == 36948 || // RouteUserName
|
|
dwId == 36949 || // RoutePassword
|
|
dwId == 36958 || // SmtpDsPassword
|
|
dwId == 41191 || // Pop3DsPassword
|
|
dwId == 45461 || // FeedAccountName
|
|
dwId == 45462 || // FeedPassword
|
|
dwId == 49384 ) // ImapDsPassword
|
|
{
|
|
dwAcc = MD_ACR_RESTRICTED_WRITE;
|
|
}
|
|
else
|
|
{
|
|
dwAcc = MD_ACR_WRITE;
|
|
}
|
|
}
|
|
else // ! only METADATA_PERMISSION_WRITE
|
|
{
|
|
if ( dwId == AAC_ENUM_KEYS )
|
|
{
|
|
dwAcc = MD_ACR_ENUM_KEYS;
|
|
}
|
|
else
|
|
{
|
|
// assume read access
|
|
dwAcc = MD_ACR_READ;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If copy or delete key, check if ACL exists in subtree
|
|
// if yes required MD_ACR_RESTRICTED_WRITE
|
|
//
|
|
|
|
if ( dwAcc == MD_ACR_WRITE &&
|
|
(dwId == AAC_COPYKEY || dwId == AAC_DELETEKEY) )
|
|
{
|
|
if ( pohHandle->CheckSubAcls( pMDCom, pszRelPath, &fIsAnyAcl ) &&
|
|
fIsAnyAcl )
|
|
{
|
|
dwAcc = MD_ACR_RESTRICTED_WRITE;
|
|
}
|
|
}
|
|
|
|
CheckAgain:
|
|
if ( !AccessCheck( pAcl,
|
|
hAccTok,
|
|
dwAcc,
|
|
&g_FileGenericMapping,
|
|
(PRIVILEGE_SET *) PrivSet,
|
|
&cbPrivilegeSet,
|
|
&dwGrantedAccess,
|
|
&fAccessGranted ) ||
|
|
!fAccessGranted )
|
|
{
|
|
if ( dwAcc != MD_ACR_WRITE_DAC && (dwId == MD_ADMIN_ACL) )
|
|
{
|
|
dwAcc = MD_ACR_WRITE_DAC;
|
|
goto CheckAgain;
|
|
}
|
|
|
|
//
|
|
// If read access denied, retry with restricted read right
|
|
// only if not called from GetAll()
|
|
//
|
|
|
|
if ( dwAcc == MD_ACR_READ &&
|
|
pfEnableSecureAccess )
|
|
{
|
|
dwAcc = MD_ACR_UNSECURE_PROPS_READ;
|
|
*pfEnableSecureAccess = FALSE;
|
|
goto CheckAgain;
|
|
}
|
|
|
|
SetLastError( ERROR_ACCESS_DENIED );
|
|
bReturn = FALSE;
|
|
}
|
|
|
|
exit:
|
|
// Cleanup
|
|
if (pAdminAclCurrent)
|
|
{
|
|
pAdminAclCurrent->Release();
|
|
}
|
|
if (hAccTok)
|
|
{
|
|
CloseHandle( hAccTok );
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
CAdminAcl::~CAdminAcl(
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Destructor for Admin Acl cache entry
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Returns:
|
|
|
|
Nothing
|
|
|
|
--*/
|
|
{
|
|
if ( m_pMDCom )
|
|
{
|
|
if ( m_dwAclRef )
|
|
{
|
|
m_pMDCom->ComMDReleaseReferenceData( m_dwAclRef );
|
|
}
|
|
m_pMDCom->Release();
|
|
}
|
|
|
|
m_dwSignature = ADMINACL_FREED_SIGN;
|
|
}
|
|
|
|
DWORD
|
|
CAdminAcl::AddRef()
|
|
/*++
|
|
Routine Description:
|
|
Interlocked increments the reference count.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
The new reference count.
|
|
--*/
|
|
{
|
|
return ((DWORD)InterlockedIncrement((LONG*)&m_cRef));
|
|
}
|
|
|
|
DWORD
|
|
CAdminAcl::Release()
|
|
/*++
|
|
Routine Description:
|
|
Interlocked decrements the reference count.
|
|
When the reference count reaches 0 deletes the object.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
The new reference count.
|
|
--*/
|
|
{
|
|
DWORD cRef;
|
|
|
|
cRef=(DWORD)InterlockedDecrement((LONG*)&m_cRef);
|
|
|
|
if (cRef==0)
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
return (cRef);
|
|
}
|
|
|
|
BOOL
|
|
CAdminAcl::Init(
|
|
IMDCOM* pMDCom,
|
|
LPVOID pvAdmin,
|
|
METADATA_HANDLE hAdminHandle,
|
|
LPCWSTR pszPath,
|
|
LPBYTE pAcl,
|
|
DWORD dwAclRef,
|
|
PBOOL pbIsPathCorrect
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize an Admin Acl cache entry
|
|
|
|
Arguments:
|
|
|
|
hAdminHandle - metadata handle
|
|
pszPath - path to object ( absolute )
|
|
pAcl - ptr to ACL for this path ( may be NULL )
|
|
dwAclRef - access by reference ID
|
|
|
|
Returns:
|
|
|
|
Nothing
|
|
|
|
--*/
|
|
{
|
|
m_hAdminHandle = hAdminHandle;
|
|
m_pvAdmin = pvAdmin;
|
|
*pbIsPathCorrect = TRUE;
|
|
|
|
if (pszPath != NULL)
|
|
{
|
|
if ( wcslen( pszPath ) < (sizeof(m_wchPath) / sizeof(WCHAR)) )
|
|
{
|
|
wcscpy( m_wchPath, pszPath );
|
|
}
|
|
else
|
|
{
|
|
m_wchPath[0] = (WCHAR)'\0';
|
|
*pbIsPathCorrect = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_wchPath[0] = (WCHAR)'\0';
|
|
}
|
|
m_pAcl = pAcl;
|
|
m_dwAclRef = dwAclRef;
|
|
m_pMDCom = pMDCom;
|
|
pMDCom->AddRef();
|
|
|
|
m_dwSignature = ADMINACL_INIT_SIGN;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
HRESULT
|
|
COpenHandle::Init(
|
|
METADATA_HANDLE hAdminHandle,
|
|
LPCWSTR pszRelPath,
|
|
LPCWSTR pszParentPath
|
|
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize an open context cache entry
|
|
|
|
Arguments:
|
|
|
|
pvAdmin - admin context
|
|
hAdminHandle - metadata handle
|
|
pszRelPath - path to object ( absolute )
|
|
|
|
|
|
Returns:
|
|
|
|
Nothing
|
|
|
|
--*/
|
|
{
|
|
|
|
HRESULT hresReturn = ERROR_SUCCESS;
|
|
LPWSTR pszRelPathIndex = (LPWSTR)pszRelPath;
|
|
|
|
m_hAdminHandle = hAdminHandle;
|
|
m_lRefCount = 1;
|
|
|
|
if (pszRelPath == NULL)
|
|
{
|
|
pszRelPathIndex = L"";
|
|
}
|
|
|
|
DBG_ASSERT(pszParentPath != NULL);
|
|
DBG_ASSERT((*pszParentPath == (WCHAR)'\0') ||
|
|
ISPATHDELIMW(*pszParentPath));
|
|
|
|
//
|
|
// Strip front slash now, add it in later
|
|
//
|
|
|
|
if (ISPATHDELIMW(*pszRelPathIndex))
|
|
{
|
|
pszRelPathIndex++;
|
|
}
|
|
|
|
DWORD dwRelPathLen = (DWORD)wcslen(pszRelPathIndex);
|
|
DWORD dwParentPathLen = (DWORD)wcslen(pszParentPath);
|
|
|
|
DBG_ASSERT((dwParentPathLen == 0) ||
|
|
(!ISPATHDELIMW(pszParentPath[dwParentPathLen -1])));
|
|
|
|
//
|
|
// Get rid of trailing slash for good
|
|
//
|
|
|
|
if ((dwRelPathLen > 0) && (ISPATHDELIMW(pszRelPathIndex[dwRelPathLen -1])))
|
|
{
|
|
dwRelPathLen--;
|
|
}
|
|
|
|
//
|
|
// Include space for mid slash if Relpath exists
|
|
// Include space for termination
|
|
//
|
|
|
|
DWORD dwTotalSize =
|
|
(dwRelPathLen + dwParentPathLen + 1 + ((dwRelPathLen > 0) ? 1 : 0)) * sizeof(WCHAR);
|
|
|
|
m_pszPath = (LPWSTR)LocalAlloc(LMEM_FIXED, dwTotalSize);
|
|
|
|
if (m_pszPath == NULL)
|
|
{
|
|
hresReturn = RETURNCODETOHRESULT(GetLastError());
|
|
}
|
|
else
|
|
{
|
|
|
|
//
|
|
// OK to always copy the first part
|
|
//
|
|
|
|
memcpy(m_pszPath,
|
|
pszParentPath,
|
|
dwParentPathLen * sizeof(WCHAR));
|
|
|
|
//
|
|
// Don't need slash if there is no RelPath
|
|
//
|
|
|
|
if (dwRelPathLen > 0)
|
|
{
|
|
|
|
m_pszPath[dwParentPathLen] = (WCHAR)'/';
|
|
|
|
memcpy(m_pszPath + dwParentPathLen + 1,
|
|
pszRelPathIndex,
|
|
dwRelPathLen * sizeof(WCHAR));
|
|
|
|
}
|
|
|
|
m_pszPath[(dwTotalSize / sizeof(WCHAR)) - 1] = (WCHAR)'\0';
|
|
|
|
//
|
|
// Now convert \ to / for string compares
|
|
//
|
|
|
|
LPWSTR pszPathIndex = m_pszPath;
|
|
|
|
while ((pszPathIndex = wcschr(pszPathIndex, (WCHAR)'\\')) != NULL)
|
|
{
|
|
*pszPathIndex = (WCHAR)'/';
|
|
}
|
|
|
|
}
|
|
|
|
|
|
return hresReturn;
|
|
}
|
|
|
|
// Whistler 53924
|
|
/*++
|
|
|
|
function backstrchr
|
|
returns the last occurrence of a charcter or NULL if not found
|
|
--*/
|
|
|
|
WCHAR * backstrchr(WCHAR * pString,WCHAR ThisChar)
|
|
{
|
|
WCHAR *pCurrentPos = NULL;
|
|
|
|
while(*pString)
|
|
{
|
|
if (*pString == ThisChar)
|
|
{
|
|
pCurrentPos = pString;
|
|
}
|
|
pString++;
|
|
};
|
|
return pCurrentPos;
|
|
}
|
|
|
|
|
|
BOOL
|
|
COpenHandle::GetAcl(
|
|
IMDCOM* pMDCom,
|
|
LPCWSTR pszRelPath,
|
|
LPBYTE* pAcl,
|
|
LPDWORD pdwRef
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Retrieve Acl
|
|
|
|
Arguments:
|
|
|
|
pszPath - path to object
|
|
ppAcl - updated with ptr to ACL if success
|
|
pdwRef - updated with ref to ACL if success
|
|
|
|
Returns:
|
|
|
|
TRUE if success, otherwise FALSE
|
|
|
|
--*/
|
|
{
|
|
METADATA_RECORD mdRecord = { 0 };
|
|
HRESULT hRes;
|
|
DWORD dwRequiredLen;
|
|
BOOL bReturn = TRUE;
|
|
LPWSTR pszFullPath;
|
|
LPWSTR pszRelPathIndex = (LPWSTR)pszRelPath;
|
|
|
|
|
|
|
|
if (pszRelPathIndex == NULL)
|
|
{
|
|
pszRelPathIndex = L"";
|
|
}
|
|
|
|
DBG_ASSERT(m_pszPath != NULL);
|
|
DBG_ASSERT((*m_pszPath == (WCHAR)'\0') ||
|
|
ISPATHDELIMW(*m_pszPath));
|
|
|
|
//
|
|
// Strip front slash now, add it in later
|
|
//
|
|
|
|
if (ISPATHDELIMW(*pszRelPathIndex))
|
|
{
|
|
pszRelPathIndex++;
|
|
}
|
|
|
|
|
|
DWORD dwPathLen = (DWORD)wcslen(m_pszPath);
|
|
DWORD dwRelPathLen = (DWORD)wcslen(pszRelPathIndex);
|
|
|
|
DBG_ASSERT((dwPathLen == 0) ||
|
|
(!ISPATHDELIMW(m_pszPath[dwPathLen -1])));
|
|
|
|
//
|
|
// Get rid of trailing slash for good
|
|
//
|
|
|
|
if ((dwRelPathLen > 0) && (ISPATHDELIMW(pszRelPathIndex[dwRelPathLen -1])))
|
|
{
|
|
dwRelPathLen--;
|
|
}
|
|
|
|
//
|
|
// Include space for mid slash and termination
|
|
//
|
|
|
|
DWORD dwTotalSize = (dwPathLen + dwRelPathLen + 1 + ((dwRelPathLen > 0) ? 1 : 0)) * sizeof(WCHAR);
|
|
|
|
pszFullPath = (LPWSTR)LocalAlloc(LMEM_FIXED, dwTotalSize);
|
|
|
|
if (pszFullPath == NULL)
|
|
{
|
|
bReturn = FALSE;
|
|
}
|
|
else
|
|
{
|
|
memcpy(pszFullPath,
|
|
m_pszPath,
|
|
dwPathLen * sizeof(WCHAR));
|
|
|
|
//
|
|
// Don't need slash if there is no RelPath
|
|
//
|
|
|
|
if (dwRelPathLen > 0)
|
|
{
|
|
pszFullPath[dwPathLen] = (WCHAR)'/';
|
|
|
|
memcpy(pszFullPath + dwPathLen + 1,
|
|
pszRelPathIndex,
|
|
dwRelPathLen * sizeof(WCHAR));
|
|
|
|
}
|
|
|
|
pszFullPath[(dwTotalSize - sizeof(WCHAR)) / sizeof(WCHAR)] = (WCHAR)'\0';
|
|
|
|
//
|
|
// Now convert \ to / for string compares
|
|
// m_pszPath was already converted, so start at relpath
|
|
//
|
|
|
|
LPWSTR pszPathIndex = pszFullPath + (dwPathLen);
|
|
|
|
while ((pszPathIndex = wcschr(pszPathIndex, (WCHAR)'\\')) != NULL)
|
|
{
|
|
*pszPathIndex = (WCHAR)'/';
|
|
}
|
|
|
|
//
|
|
// Use /schema ACL if path = /schema/...
|
|
//
|
|
|
|
if (_wcsnicmp(pszFullPath,
|
|
IIS_MD_ADSI_SCHEMA_PATH_W L"/",
|
|
((sizeof(IIS_MD_ADSI_SCHEMA_PATH_W L"/") / sizeof(WCHAR)) - 1)) == 0)
|
|
{
|
|
pszFullPath[(sizeof(IIS_MD_ADSI_SCHEMA_PATH_W) / sizeof(WCHAR)) -1] = (WCHAR)'\0';
|
|
}
|
|
|
|
mdRecord.dwMDIdentifier = MD_ADMIN_ACL;
|
|
mdRecord.dwMDAttributes = METADATA_INHERIT | METADATA_PARTIAL_PATH | METADATA_REFERENCE;
|
|
mdRecord.dwMDUserType = IIS_MD_UT_SERVER;
|
|
mdRecord.dwMDDataType = BINARY_METADATA;
|
|
mdRecord.dwMDDataLen = 0;
|
|
mdRecord.pbMDData = NULL;
|
|
mdRecord.dwMDDataTag = NULL;
|
|
|
|
hRes = pMDCom->ComMDGetMetaDataW( METADATA_MASTER_ROOT_HANDLE,
|
|
pszFullPath,
|
|
&mdRecord,
|
|
&dwRequiredLen );
|
|
|
|
// Whistler 53924
|
|
if(HRESULTTOWIN32(hRes) == ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
WCHAR * pLastSlash = NULL;
|
|
while ((pLastSlash = backstrchr(pszFullPath,L'/')) != NULL)
|
|
{
|
|
*pLastSlash = L'\0';
|
|
pLastSlash = NULL;
|
|
|
|
mdRecord.dwMDDataLen = 0;
|
|
mdRecord.pbMDData = NULL;
|
|
mdRecord.dwMDDataTag = NULL;
|
|
|
|
hRes = pMDCom->ComMDGetMetaDataW( METADATA_MASTER_ROOT_HANDLE,
|
|
pszFullPath,
|
|
&mdRecord,
|
|
&dwRequiredLen );
|
|
if (SUCCEEDED(hRes)) break;
|
|
}
|
|
}
|
|
|
|
if ( FAILED( hRes ) || !mdRecord.dwMDDataTag )
|
|
{
|
|
bReturn = FALSE;
|
|
}
|
|
|
|
LocalFree( pszFullPath );
|
|
}
|
|
|
|
if ( bReturn )
|
|
{
|
|
*pAcl = mdRecord.pbMDData;
|
|
*pdwRef = mdRecord.dwMDDataTag;
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
|
|
BOOL
|
|
COpenHandle::CheckSubAcls(
|
|
IMDCOM* pMDCom,
|
|
LPCWSTR pszRelPath,
|
|
LPBOOL pfIsAnyAcl
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check if Acls exist in subtree
|
|
|
|
Arguments:
|
|
|
|
pszRelPath - path to object
|
|
pfIsAnyAcl - updated with TRUE if sub-acls exists, otherwise FALSE
|
|
|
|
Returns:
|
|
|
|
TRUE if success, otherwise FALSE
|
|
|
|
--*/
|
|
{
|
|
HRESULT hRes;
|
|
DWORD dwRequiredLen;
|
|
BOOL bReturn = TRUE;
|
|
LPWSTR pszFullPath;
|
|
|
|
*pfIsAnyAcl = FALSE;
|
|
|
|
LPWSTR pszRelPathIndex = (LPWSTR)pszRelPath;
|
|
|
|
if (pszRelPathIndex == NULL)
|
|
{
|
|
pszRelPathIndex = L"";
|
|
}
|
|
|
|
DBG_ASSERT(m_pszPath != NULL);
|
|
DBG_ASSERT((*m_pszPath == (WCHAR)'\0') ||
|
|
ISPATHDELIMW(*m_pszPath));
|
|
|
|
//
|
|
// Strip front slash now, add it in later
|
|
//
|
|
|
|
if (ISPATHDELIMW(*pszRelPathIndex))
|
|
{
|
|
pszRelPathIndex++;
|
|
}
|
|
|
|
|
|
DWORD dwPathLen = (DWORD)wcslen(m_pszPath);
|
|
DWORD dwRelPathLen = (DWORD)wcslen(pszRelPathIndex);
|
|
|
|
DBG_ASSERT((dwPathLen == 0) ||
|
|
(!ISPATHDELIMW(m_pszPath[dwPathLen -1])));
|
|
|
|
//
|
|
// Get rid of trailing slash for good
|
|
//
|
|
|
|
if ((dwRelPathLen > 0) && (ISPATHDELIMW(pszRelPathIndex[dwRelPathLen -1])))
|
|
{
|
|
dwRelPathLen--;
|
|
}
|
|
|
|
//
|
|
// Include space for mid slash and termination
|
|
//
|
|
|
|
DWORD dwTotalSize = (dwPathLen + dwRelPathLen + 1 + ((dwRelPathLen > 0) ? 1 : 0)) * sizeof(WCHAR);
|
|
|
|
pszFullPath = (LPWSTR)LocalAlloc(LMEM_FIXED, dwTotalSize);
|
|
|
|
if (pszFullPath == NULL)
|
|
{
|
|
bReturn = FALSE;
|
|
}
|
|
else
|
|
{
|
|
memcpy(pszFullPath,
|
|
m_pszPath,
|
|
dwPathLen * sizeof(WCHAR));
|
|
|
|
//
|
|
// Don't need slash if there is no RelPath
|
|
//
|
|
|
|
if (dwRelPathLen > 0)
|
|
{
|
|
|
|
pszFullPath[dwPathLen] = (WCHAR)'/';
|
|
|
|
memcpy(pszFullPath + dwPathLen + 1,
|
|
pszRelPathIndex,
|
|
dwRelPathLen * sizeof(WCHAR));
|
|
}
|
|
|
|
pszFullPath[(dwTotalSize - sizeof(WCHAR)) / sizeof(WCHAR)] = (WCHAR)'\0';
|
|
|
|
hRes = pMDCom->ComMDGetMetaDataPathsW(METADATA_MASTER_ROOT_HANDLE,
|
|
pszFullPath,
|
|
MD_ADMIN_ACL,
|
|
BINARY_METADATA,
|
|
0,
|
|
NULL,
|
|
&dwRequiredLen );
|
|
|
|
LocalFree( pszFullPath );
|
|
|
|
if ( FAILED( hRes ) )
|
|
{
|
|
if ( hRes == RETURNCODETOHRESULT( ERROR_INSUFFICIENT_BUFFER ) )
|
|
{
|
|
bReturn = TRUE;
|
|
*pfIsAnyAcl = TRUE;
|
|
}
|
|
else
|
|
{
|
|
bReturn = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
VOID
|
|
COpenHandle::Release(PVOID pvAdmin)
|
|
{
|
|
if (InterlockedDecrement(&m_lRefCount) == 0)
|
|
{
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
AdminAclNotifyClose(pvAdmin, m_hAdminHandle);
|
|
|
|
((CADMCOMW *)pvAdmin)->DeleteNode(m_hAdminHandle);
|
|
}
|
|
}
|
|
|
|
CAdminAclCache::CAdminAclCache()
|
|
/*++
|
|
Routine Description:
|
|
C++ constructor. Initializes all members to 0.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
n/a
|
|
--*/
|
|
{
|
|
m_fEnabled=1;
|
|
m_cAdminAclCache=0;
|
|
memset(m_rgpAdminAclCache, 0, sizeof(m_rgpAdminAclCache));
|
|
}
|
|
|
|
CAdminAclCache::~CAdminAclCache()
|
|
/*++
|
|
Routine Description:
|
|
C++ destructor. Deletes everthing in the cache.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
n/a
|
|
--*/
|
|
{
|
|
Flush();
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CAdminAclCache::Init()
|
|
/*++
|
|
Routine Description:
|
|
Initializes the cache. Right now there is nothing to do.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
S_OK on success. E_* failure.
|
|
--*/
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
|
|
//exit:
|
|
// Done
|
|
return (hr);
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CAdminAclCache::IsEnabled()
|
|
/*++
|
|
Routine Description:
|
|
Checks whether the cache is enabled.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
S_OK enabled, S_FALSE disabled
|
|
--*/
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
|
|
// Check
|
|
if (m_fEnabled<=0)
|
|
{
|
|
// Return S_FALSE
|
|
hr=S_FALSE;
|
|
}
|
|
|
|
//exit:
|
|
// Done
|
|
return (hr);
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CAdminAclCache::IsEmpty()
|
|
/*++
|
|
Routine Description:
|
|
Checks whether the cache is empty.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
S_OK empty, S_FALSE not empty
|
|
--*/
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
|
|
// Check
|
|
if (m_cAdminAclCache!=0)
|
|
{
|
|
// Return S_FALSE
|
|
hr=S_FALSE;
|
|
}
|
|
|
|
//exit:
|
|
// Done
|
|
return (hr);
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CAdminAclCache::Disable()
|
|
/*++
|
|
Routine Description:
|
|
Disables the cache.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
S_OK
|
|
--*/
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
|
|
InterlockedDecrement(&m_fEnabled);
|
|
|
|
//exit:
|
|
// Done
|
|
return (hr);
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CAdminAclCache::Enable()
|
|
/*++
|
|
Routine Description:
|
|
Enables the cache.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
S_OK
|
|
--*/
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
|
|
InterlockedIncrement(&m_fEnabled);
|
|
|
|
//exit:
|
|
// Done
|
|
return (hr);
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CAdminAclCache::Flush()
|
|
/*++
|
|
Routine Description:
|
|
Removes all times from the cache.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
S_OK
|
|
--*/
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
CAdminAcl *rgpToDelete[ARRAYSIZE(m_rgpAdminAclCache)];
|
|
DWORD cToDelete;
|
|
DWORD i;
|
|
|
|
// Lock exlcusive
|
|
m_Lock.WriteLock();
|
|
|
|
// Copy to local vars
|
|
cToDelete=m_cAdminAclCache;
|
|
memmove(rgpToDelete,
|
|
m_rgpAdminAclCache,
|
|
cToDelete*sizeof(*rgpToDelete));
|
|
|
|
// Wipe out
|
|
InterlockedExchange((LONG*)&m_cAdminAclCache, 0);
|
|
memset(m_rgpAdminAclCache, 0, sizeof(m_rgpAdminAclCache));
|
|
|
|
// Unlock
|
|
m_Lock.WriteUnlock();
|
|
|
|
// Loop over the cached elements
|
|
for (i=0; i<cToDelete; i++)
|
|
{
|
|
// Release
|
|
rgpToDelete[i]->Release();
|
|
}
|
|
|
|
//exit:
|
|
return (hr);
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CAdminAclCache::Remove(
|
|
LPVOID pvAdmin,
|
|
METADATA_HANDLE hAdminHandle)
|
|
/*++
|
|
Routine Description:
|
|
Removes all items matching the object and the handle.
|
|
|
|
Arguments:
|
|
pvAdmin - the admin context
|
|
hAdminHandle - the metadata handle
|
|
|
|
Returns:
|
|
S_OK
|
|
--*/
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
CAdminAcl *rgpToDelete[ARRAYSIZE(m_rgpAdminAclCache)];
|
|
DWORD cToDelete;
|
|
DWORD dwRead;
|
|
DWORD dwWrite;
|
|
DWORD i;
|
|
|
|
// Try to lock shared
|
|
if (m_Lock.TryReadLock())
|
|
{
|
|
// Loop over the cached elements
|
|
for (i=0; i<m_cAdminAclCache; i++)
|
|
{
|
|
// Match?
|
|
if ((m_rgpAdminAclCache[i]->GetAdminContext()==pvAdmin)&&
|
|
(m_rgpAdminAclCache[i]->GetAdminHandle()==hAdminHandle))
|
|
{
|
|
// Found one
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Not found?
|
|
if (i==m_cAdminAclCache)
|
|
{
|
|
// Unlock
|
|
m_Lock.ReadUnlock();
|
|
|
|
// Done
|
|
goto exit;
|
|
}
|
|
|
|
// Lock exlcusive
|
|
m_Lock.ConvertSharedToExclusive();
|
|
}
|
|
else
|
|
{
|
|
// Lock exlcusive
|
|
m_Lock.WriteLock();
|
|
}
|
|
|
|
// In both case we have a write lock now
|
|
|
|
// Loop over the cached elements
|
|
for (cToDelete=dwWrite=dwRead=0; dwRead<m_cAdminAclCache; dwRead++)
|
|
{
|
|
// Match?
|
|
if ((m_rgpAdminAclCache[dwRead]->GetAdminContext()==pvAdmin)&&
|
|
(m_rgpAdminAclCache[dwRead]->GetAdminHandle()==hAdminHandle))
|
|
{
|
|
// Copy to the local array
|
|
rgpToDelete[cToDelete++]=m_rgpAdminAclCache[dwRead];
|
|
}
|
|
else
|
|
{
|
|
// If we removed some
|
|
if (dwWrite!=dwRead)
|
|
{
|
|
// Move to the empty place
|
|
m_rgpAdminAclCache[dwWrite]=m_rgpAdminAclCache[dwRead];
|
|
}
|
|
|
|
// Advance
|
|
dwWrite++;
|
|
}
|
|
}
|
|
|
|
DBG_ASSERT((dwWrite+cToDelete)==m_cAdminAclCache);
|
|
|
|
// Set the new size
|
|
InterlockedExchange((LONG*)&m_cAdminAclCache, dwWrite);
|
|
|
|
// Unlock
|
|
m_Lock.WriteUnlock();
|
|
|
|
// Loop over the elements to be deleted
|
|
for (i=0; i<cToDelete; i++)
|
|
{
|
|
// Release
|
|
rgpToDelete[i]->Release();
|
|
}
|
|
|
|
exit:
|
|
return (hr);
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CAdminAclCache::Find(
|
|
LPVOID pvAdmin,
|
|
METADATA_HANDLE hAdminHandle,
|
|
LPCWSTR pwszRelPath,
|
|
CAdminAcl **ppAdminAcl)
|
|
/*++
|
|
Routine Description:
|
|
Finds ACL matching the object, the handle and the path.
|
|
|
|
Arguments:
|
|
pvAdmin - the admin context
|
|
hAdminHandle - the metadata handle
|
|
pwszRelPath - the path (NULL is treated as empty string)
|
|
ppAdminAcl - out the acl if found. the caller must release it.
|
|
|
|
Returns:
|
|
S_OK found, S_FALSE not found. E_* on failure
|
|
--*/
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
CAdminAcl *pAdminAcl=NULL;
|
|
DWORD i;
|
|
BOOL fWriteLocked=FALSE;
|
|
|
|
// Check args
|
|
if (ppAdminAcl==NULL)
|
|
{
|
|
hr=E_INVALIDARG;
|
|
goto exit;
|
|
}
|
|
else
|
|
{
|
|
// Init
|
|
*ppAdminAcl=NULL;
|
|
}
|
|
|
|
// Don't search if the cache is disabled
|
|
hr=IsEnabled();
|
|
if (hr!=S_OK)
|
|
{
|
|
// Not found
|
|
goto exit;
|
|
}
|
|
|
|
if (pwszRelPath==NULL)
|
|
{
|
|
// Set to empty
|
|
pwszRelPath=L"";
|
|
}
|
|
|
|
// Try to lock shared
|
|
if (!m_Lock.TryReadLock())
|
|
{
|
|
// Report potentially wrong "Not found" instead of waiting for the writes to finish.
|
|
hr=S_FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
// Try to find
|
|
hr=_Find(pvAdmin, hAdminHandle, pwszRelPath, &pAdminAcl, &i);
|
|
|
|
// If found
|
|
if (hr==S_OK)
|
|
{
|
|
DBG_ASSERT(pAdminAcl&&(i<m_cAdminAclCache));
|
|
|
|
// Try to lock exclusive
|
|
if (m_Lock.TryConvertSharedToExclusive())
|
|
{
|
|
// Move to be 1st element
|
|
_MoveFirst(i);
|
|
|
|
// Remember to write unlock
|
|
fWriteLocked=TRUE;
|
|
}
|
|
}
|
|
|
|
// Write locked?
|
|
if (fWriteLocked)
|
|
{
|
|
// Unlock
|
|
m_Lock.WriteUnlock();
|
|
}
|
|
else
|
|
{
|
|
// Unlock
|
|
m_Lock.ReadUnlock();
|
|
}
|
|
|
|
// Return
|
|
*ppAdminAcl=pAdminAcl;
|
|
pAdminAcl=NULL;
|
|
|
|
exit:
|
|
// Cleanup
|
|
if (pAdminAcl)
|
|
{
|
|
pAdminAcl->Release();
|
|
}
|
|
|
|
// Done
|
|
return (hr);
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CAdminAclCache::Add(
|
|
CAdminAcl *pAdminAcl)
|
|
/*++
|
|
Routine Description:
|
|
Adds ACL to the cache.
|
|
|
|
Arguments:
|
|
pAdminAcl - the acl.
|
|
|
|
Returns:
|
|
S_OK the element was added. S_FALSE the element was not added. E_* failure.
|
|
--*/
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
DWORD i;
|
|
CAdminAcl *pTemp=NULL;
|
|
LPVOID pvAdmin;
|
|
METADATA_HANDLE hAdminHandle;
|
|
LPCWSTR pwszRelPath;
|
|
CAdminAcl *pAdminAclToRelease = NULL;
|
|
|
|
// Check args
|
|
if (pAdminAcl==NULL)
|
|
{
|
|
hr=E_INVALIDARG;
|
|
goto exit;
|
|
}
|
|
|
|
// Don't add anything if the cache is disabled
|
|
hr=IsEnabled();
|
|
if (hr!=S_OK)
|
|
{
|
|
// Not found
|
|
goto exit;
|
|
}
|
|
|
|
// Get in locals
|
|
pvAdmin=pAdminAcl->GetAdminContext();
|
|
hAdminHandle=pAdminAcl->GetAdminHandle();
|
|
pwszRelPath=pAdminAcl->GetPath()?pAdminAcl->GetPath():L"";
|
|
|
|
// Try to lock exclusive
|
|
if (!m_Lock.TryWriteLock())
|
|
{
|
|
// Couldn't add
|
|
hr=S_FALSE;
|
|
goto exit;
|
|
}
|
|
|
|
// TryWriteLock() above succeeded, so here we have a write lock
|
|
|
|
// Try to find
|
|
hr=_Find(pvAdmin, hAdminHandle, pwszRelPath, &pTemp, &i);
|
|
|
|
// If found
|
|
if (hr==S_OK)
|
|
{
|
|
DBG_ASSERT(i<m_cAdminAclCache);
|
|
|
|
// Move to be 1st element
|
|
_MoveFirst(i);
|
|
}
|
|
else
|
|
{
|
|
// Add
|
|
hr=_InsertFirst(pAdminAcl, &pAdminAclToRelease);
|
|
}
|
|
|
|
// Unlock
|
|
m_Lock.WriteUnlock();
|
|
|
|
exit:
|
|
// Cleanup
|
|
if (pTemp)
|
|
{
|
|
pTemp->Release();
|
|
}
|
|
if ( pAdminAclToRelease )
|
|
{
|
|
pAdminAclToRelease->Release();
|
|
}
|
|
|
|
// Done
|
|
return (hr);
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CAdminAclCache::_Find(
|
|
LPVOID pvAdmin,
|
|
METADATA_HANDLE hAdminHandle,
|
|
LPCWSTR pwszRelPath,
|
|
CAdminAcl **ppAdminAcl,
|
|
DWORD *pdwIndex)
|
|
/*++
|
|
Routine Description:
|
|
Finds ACL matching the object, the handle and the path.
|
|
The caller must acquire any lock.
|
|
|
|
Arguments:
|
|
pvAdmin - the admin context
|
|
hAdminHandle - the metadata handle
|
|
pwszRelPath - the path (NULL is treated as empty string)
|
|
ppAdminAcl - out the acl if found. the caller must release it.
|
|
pdwIndex - out the index of the acl if found.
|
|
|
|
Returns:
|
|
S_OK found, S_FALSE not found. E_* on failure
|
|
--*/
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
CAdminAcl *pAdminAcl=NULL;
|
|
DWORD i;
|
|
LPCWSTR pwszAclPath;
|
|
|
|
// Check args
|
|
if (ppAdminAcl==NULL)
|
|
{
|
|
hr=E_INVALIDARG;
|
|
}
|
|
else
|
|
{
|
|
// Init
|
|
*ppAdminAcl=NULL;
|
|
}
|
|
if (pdwIndex==NULL)
|
|
{
|
|
hr=E_INVALIDARG;
|
|
}
|
|
else
|
|
{
|
|
// Init
|
|
*pdwIndex=0;
|
|
}
|
|
// Invalid args?
|
|
if (FAILED(hr))
|
|
{
|
|
// Bail
|
|
goto exit;
|
|
}
|
|
|
|
if (pwszRelPath==NULL)
|
|
{
|
|
// Set to empty
|
|
pwszRelPath=L"";
|
|
}
|
|
|
|
// Loop and search
|
|
for (i=0; i<m_cAdminAclCache; i++)
|
|
{
|
|
DBG_ASSERT(m_rgpAdminAclCache[i]);
|
|
|
|
pwszAclPath=m_rgpAdminAclCache[i]->GetPath()?
|
|
m_rgpAdminAclCache[i]->GetPath():L"";
|
|
|
|
if ((m_rgpAdminAclCache[i]->GetAdminContext()==pvAdmin)&&
|
|
(m_rgpAdminAclCache[i]->GetAdminHandle()==hAdminHandle)&&
|
|
(_wcsicmp(pwszAclPath, pwszRelPath)==0))
|
|
{
|
|
pAdminAcl=m_rgpAdminAclCache[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Found one?
|
|
if (pAdminAcl)
|
|
{
|
|
// Addref
|
|
pAdminAcl->AddRef();
|
|
// Return
|
|
*ppAdminAcl=pAdminAcl;
|
|
*pdwIndex=i;
|
|
// Don't free
|
|
pAdminAcl=NULL;
|
|
}
|
|
else
|
|
{
|
|
// Not found
|
|
hr=S_FALSE;
|
|
}
|
|
|
|
exit:
|
|
// In all cases here pAdminAcl should be NULL:
|
|
// 1. If we failed, because of invalid arguments it is initialized to NULL
|
|
// 2. If we couldn't find it in the cache it still as initialized to NULL
|
|
// 3. If we found in the cache we moved it to *ppAdminAcl and set it back to NULL
|
|
DBG_ASSERT( pAdminAcl == NULL );
|
|
|
|
// Done
|
|
return (hr);
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CAdminAclCache::_MoveFirst(
|
|
DWORD i)
|
|
/*++
|
|
Routine Description:
|
|
Moves the i-th element to be 1st (at possition 0).
|
|
The caller must acquire write lock.
|
|
|
|
Arguments:
|
|
i - in the index of the acl to move 1st.
|
|
|
|
Returns:
|
|
S_OK success. E_* on failure.
|
|
--*/
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
CAdminAcl *pAdminAcl=NULL;
|
|
|
|
DBG_ASSERT(i<m_cAdminAclCache);
|
|
|
|
// Check args
|
|
if (i>=m_cAdminAclCache)
|
|
{
|
|
// Bail
|
|
hr=E_INVALIDARG;
|
|
goto exit;
|
|
}
|
|
|
|
// If already 1st
|
|
if (i==0)
|
|
{
|
|
// Nothing to do
|
|
goto exit;
|
|
}
|
|
|
|
// Save
|
|
pAdminAcl=m_rgpAdminAclCache[i];
|
|
DBG_ASSERT(pAdminAcl);
|
|
|
|
// Move the 1st i elements 1 position right
|
|
memmove(m_rgpAdminAclCache+1,
|
|
m_rgpAdminAclCache,
|
|
i*sizeof(*m_rgpAdminAclCache));
|
|
|
|
// Move to 1st place
|
|
m_rgpAdminAclCache[0]=pAdminAcl;
|
|
|
|
exit:
|
|
// Done
|
|
return (hr);
|
|
}
|
|
|
|
STDMETHODIMP
|
|
CAdminAclCache::_InsertFirst(
|
|
CAdminAcl *pAdminAcl,
|
|
CAdminAcl **ppAdminAclToRelease)
|
|
/*++
|
|
Routine Description:
|
|
Adds ACL as the 1st element of the cache.
|
|
The caller must acquire write lock.
|
|
|
|
Arguments:
|
|
pAdminAcl - the acl.
|
|
ppAdminAclToRelease - since the function is called under a write lock
|
|
it should not call directly release on the element that
|
|
is to be deleted from the cache. Instead it will return
|
|
the element to caller to release after unlocking.
|
|
|
|
Returns:
|
|
S_OK the element was added. E_* failure.
|
|
--*/
|
|
{
|
|
// Locals
|
|
HRESULT hr=S_OK;
|
|
|
|
if ( ppAdminAclToRelease == NULL )
|
|
{
|
|
hr=E_INVALIDARG;
|
|
goto exit;
|
|
}
|
|
else
|
|
{
|
|
// Initialize to NULL
|
|
*ppAdminAclToRelease = NULL;
|
|
}
|
|
|
|
// Check args
|
|
if (pAdminAcl==NULL)
|
|
{
|
|
hr=E_INVALIDARG;
|
|
goto exit;
|
|
}
|
|
|
|
// If the cache is full
|
|
if (m_cAdminAclCache==ARRAYSIZE(m_rgpAdminAclCache))
|
|
{
|
|
// Delete one element
|
|
InterlockedDecrement((LONG*)&m_cAdminAclCache);
|
|
|
|
DBG_ASSERT(m_rgpAdminAclCache[m_cAdminAclCache]);
|
|
|
|
// Return the last element to the caller to release
|
|
*ppAdminAclToRelease = m_rgpAdminAclCache[m_cAdminAclCache];
|
|
m_rgpAdminAclCache[m_cAdminAclCache]=NULL;
|
|
}
|
|
|
|
// Anything in the cache?
|
|
if (m_cAdminAclCache)
|
|
{
|
|
// Move all elements 1 position right
|
|
memmove(m_rgpAdminAclCache+1,
|
|
m_rgpAdminAclCache,
|
|
m_cAdminAclCache*sizeof(*m_rgpAdminAclCache));
|
|
|
|
}
|
|
|
|
// Put on 1st place
|
|
m_rgpAdminAclCache[0]=pAdminAcl;
|
|
pAdminAcl->AddRef();
|
|
|
|
// Added 1 element
|
|
InterlockedIncrement((LONG*)&m_cAdminAclCache);
|
|
|
|
exit:
|
|
|
|
// Done
|
|
return (hr);
|
|
}
|
|
|