/*++ Copyright (c) 1996 Microsoft Corporation Module Name : admacl.cxx Abstract: This module defines Admin API Access Check API Author: Philippe Choquier 02-Dec-1996 --*/ #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include #include #include #include #include #include #ifdef __cplusplus } // extern "C" #endif // __cplusplus #include "iiscnfgp.h" #include #include #include #include #include #include #include #include #include #include #include // // Globals // CRITICAL_SECTION CAdminAcl::m_csList; LIST_ENTRY CAdminAcl::m_ListHead; LONG CAdminAcl::m_lInList; LONG g_lMaxInList = 30; DWORD g_dwInit = 0; CRITICAL_SECTION g_csInitCritSec; CInitAdmacl g_cinitadmacl; LONG g_lCacheAcls = 1; extern COpenHandle g_ohMasterRootHandle; CInitAdmacl::CInitAdmacl() { INITIALIZE_CRITICAL_SECTION( &g_csInitCritSec ); INITIALIZE_CRITICAL_SECTION( &CAdminAcl::m_csList ); InitializeListHead( &CAdminAcl::m_ListHead ); CAdminAcl::m_lInList = 0; DBG_REQUIRE(SUCCEEDED(g_ohMasterRootHandle.Init(METADATA_MASTER_ROOT_HANDLE, L"", L"", FALSE))); } CInitAdmacl::~CInitAdmacl() { LIST_ENTRY* pEntry; LIST_ENTRY* pNext; CAdminAcl* pAA = NULL; COpenHandle* pOC; DBG_ASSERT(IsListEmpty( &CAdminAcl::m_ListHead )); DeleteCriticalSection( &CAdminAcl::m_csList ); DeleteCriticalSection( &g_csInitCritSec ); } // // 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 --*/ { LIST_ENTRY* pEntry; LIST_ENTRY* pNext; CAdminAcl* pAA = NULL; // flush cache for all entries using this handle EnterCriticalSection( &CAdminAcl::m_csList ); for ( pEntry = CAdminAcl::m_ListHead.Flink ; pEntry != &CAdminAcl::m_ListHead ; pEntry = pNext ) { pAA = CONTAINING_RECORD( pEntry, CAdminAcl, CAdminAcl::m_ListEntry ); pNext = pEntry->Flink; if (( pAA->GetAdminContext() == pvAdmin) && ( pAA->GetAdminHandle() == hAdminHandle )) { RemoveEntryList( &pAA->m_ListEntry ); delete pAA; InterlockedDecrement( &CAdminAcl::m_lInList ); } } LeaveCriticalSection( &CAdminAcl::m_csList ); return TRUE; } void AdminAclDisableAclCache() { EnterCriticalSection( &CAdminAcl::m_csList ); g_lCacheAcls--; LeaveCriticalSection( &CAdminAcl::m_csList ); } void AdminAclEnableAclCache() { EnterCriticalSection( &CAdminAcl::m_csList ); g_lCacheAcls++; LeaveCriticalSection( &CAdminAcl::m_csList ); } BOOL AdminAclFlushCache( ) /*++ Routine Description: Flush cache Arguments: None Returns: TRUE on success, FALSE on failure --*/ { LIST_ENTRY* pEntry; LIST_ENTRY* pNext; CAdminAcl* pAA = NULL; // flush cache for all entries EnterCriticalSection( &CAdminAcl::m_csList ); for ( pEntry = CAdminAcl::m_ListHead.Flink ; pEntry != &CAdminAcl::m_ListHead ; pEntry = pNext ) { pAA = CONTAINING_RECORD( pEntry, CAdminAcl, CAdminAcl::m_ListEntry ); pNext = pEntry->Flink; RemoveEntryList( &pAA->m_ListEntry ); delete pAA; InterlockedDecrement( &CAdminAcl::m_lInList ); } LeaveCriticalSection( &CAdminAcl::m_csList ); return TRUE; } BOOL AdminAclNotifySetOrDeleteProp( METADATA_HANDLE hAdminHandle, 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 --*/ { LIST_ENTRY* pEntry; LIST_ENTRY* pNext; CAdminAcl* pAA = NULL; BOOL fSt = TRUE; UINT cE; // flush cache for all ACLs if ( dwId == MD_ADMIN_ACL ) { EnterCriticalSection( &CAdminAcl::m_csList ); for ( pEntry = CAdminAcl::m_ListHead.Flink ; pEntry != &CAdminAcl::m_ListHead ; pEntry = pNext ) { pAA = CONTAINING_RECORD( pEntry, CAdminAcl, CAdminAcl::m_ListEntry ); pNext = pEntry->Flink; RemoveEntryList( &pAA->m_ListEntry ); delete pAA; InterlockedDecrement( &CAdminAcl::m_lInList ); } LeaveCriticalSection( &CAdminAcl::m_csList ); } return fSt; } 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 --*/ { LIST_ENTRY* pEntry; CAdminAcl* pAdminAclCurrent = NULL; BOOL bReturn = TRUE; HANDLE hAccTok = NULL; LPBYTE pAcl; DWORD dwRef; BOOL fIsAnyAcl; BOOL bAddToCache = TRUE; if ( pfEnableSecureAccess ) { *pfEnableSecureAccess = TRUE; } if ( pszRelPath == NULL ) { pszRelPath = L""; } // find match : look for exact path match // keep at most N entry, reset at top of list when accessed // Investigate: if in : cut-off point just before EnterCriticalSection( &CAdminAcl::m_csList ); // // BUGBUG This checking only checks path and handle, not DCOM instance // So there could be incorrect matches. // for ( pEntry = CAdminAcl::m_ListHead.Flink; pEntry != &CAdminAcl::m_ListHead ; pEntry = pEntry->Flink ) { pAdminAclCurrent = CONTAINING_RECORD( pEntry, CAdminAcl, CAdminAcl::m_ListEntry ); if ((pAdminAclCurrent->GetAdminContext() == pvAdmin) && (pAdminAclCurrent->GetAdminHandle() == hAdminHandle) && (_wcsicmp(pAdminAclCurrent->GetPath(), pszRelPath) == 0)) { RemoveEntryList( &pAdminAclCurrent->m_ListEntry ); InterlockedDecrement( &CAdminAcl::m_lInList ); break; } else { pAdminAclCurrent = NULL; } } if( pAdminAclCurrent == NULL ) { // // Check # of entries in cache, if above limit remove LRU // if ( CAdminAcl::m_lInList >= g_lMaxInList-1 ) { pEntry = CAdminAcl::m_ListHead.Blink; pAdminAclCurrent = CONTAINING_RECORD( pEntry, CAdminAcl, CAdminAcl::m_ListEntry ); RemoveEntryList( &pAdminAclCurrent->m_ListEntry ); delete pAdminAclCurrent; InterlockedDecrement( &CAdminAcl::m_lInList ); } if ( (pAdminAclCurrent = new CAdminAcl) ) { // 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 // if ( !pAdminAclCurrent->Init( pMDCom, pvAdmin, hAdminHandle, pszRelPath, pAcl, dwRef, &bAddToCache ) ) { // // Currently no possible failures // DBG_ASSERT(FALSE); } } else { // // failed to create new cache entry // bReturn = FALSE; } } if ( pAdminAclCurrent ) { // // Access check // if ( pAcl = pAdminAclCurrent->GetAcl() ) { IServerSecurity* pSec; // // test if already impersonated ( inprocess call w/o marshalling ) // If not call DCOM to retrieve security context & impersonate, then // extract access token. // if ( OpenThreadToken( GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &hAccTok ) ) { pSec = NULL; } else // this thread is not impersonating -> process token { if ( SUCCEEDED( CoGetCallContext( IID_IServerSecurity, (void**)&pSec ) ) ) { pSec->ImpersonateClient(); if ( !OpenThreadToken( GetCurrentThread(), TOKEN_EXECUTE|TOKEN_QUERY, TRUE, &hAccTok ) ) { hAccTok = NULL; } } else { pSec = NULL; } } if ( hAccTok ) // can be non null only if obtained from thread { DWORD dwAcc; // // Protected properties require EXECUTE access rights instead of WRITE // if ( dwAccess & METADATA_PERMISSION_WRITE ) { if ( dwId == MD_ADMIN_ACL || dwId == MD_APP_ISOLATED || dwId == MD_VR_PATH || dwId == MD_ACCESS_PERM || dwId == MD_ANONYMOUS_USER_NAME || dwId == MD_ANONYMOUS_PWD || dwId == MD_MAX_BANDWIDTH || dwId == MD_MAX_BANDWIDTH_BLOCKED || dwId == MD_ISM_ACCESS_CHECK || dwId == MD_FILTER_LOAD_ORDER || dwId == MD_FILTER_STATE || dwId == MD_FILTER_ENABLED || dwId == MD_FILTER_DESCRIPTION || dwId == MD_FILTER_FLAGS || dwId == MD_FILTER_IMAGE_PATH || dwId == MD_SECURE_BINDINGS || dwId == MD_SERVER_BINDINGS ) { 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; } } DWORD dwGrantedAccess; BYTE PrivSet[400]; DWORD cbPrivilegeSet = sizeof(PrivSet); BOOL fAccessGranted; 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; } CloseHandle( hAccTok ); } else { // // For now, assume failure to get IServerSecurity means we are // in the SYSTEM context, so grant access. // bReturn = TRUE; } if ( pSec ) // Good COM cleanp { pSec->RevertToSelf(); // better here, since // COM otherwise is reclaiming the // thread token too late. pSec->Release(); } } else { // // No ACL : access check succeed // bReturn = TRUE; } if (bAddToCache && IsAclCachingEnabled) { // // Set as last used in LRU list // // // Investigate how to put a breakpoint here // InsertHeadList( &CAdminAcl::m_ListHead, &pAdminAclCurrent->m_ListEntry ); InterlockedIncrement( &CAdminAcl::m_lInList ); } else { delete pAdminAclCurrent; } } LeaveCriticalSection( &CAdminAcl::m_csList ); if ( !bReturn ) { SetLastError( ERROR_ACCESS_DENIED ); } 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; } 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, BOOL fIsNse ) /*++ Routine Description: Initialize an open context cache entry Arguments: pvAdmin - admin context hAdminHandle - metadata handle pszRelPath - path to object ( absolute ) fIsNse - TRUE if inside NSE Returns: Nothing --*/ { HRESULT hresReturn = ERROR_SUCCESS; LPWSTR pszRelPathIndex = (LPWSTR)pszRelPath; m_hAdminHandle = hAdminHandle; m_fIsNse = fIsNse; 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 = wcslen(pszRelPathIndex); DWORD dwParentPathLen = 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; HRESULT hRes; DWORD dwRequiredLen; BOOL bReturn = TRUE; METADATA_HANDLE hAdminHandle; 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 = wcslen(m_pszPath); DWORD dwRelPathLen = 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'; } if ( m_fIsNse ) { pszPathIndex = wcschr( pszFullPath, (WCHAR)'<' ); DBG_ASSERT(pszPathIndex != NULL); *pszPathIndex = (WCHAR)'\0'; hRes = pMDCom->ComMDOpenMetaObjectW( METADATA_MASTER_ROOT_HANDLE, pszFullPath, METADATA_PERMISSION_READ, 5000, &hAdminHandle ); if ( SUCCEEDED( hRes ) ) { 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( hAdminHandle, L"", &mdRecord, &dwRequiredLen ); if ( FAILED( hRes ) || !mdRecord.dwMDDataTag ) { bReturn = FALSE; } pMDCom->ComMDCloseMetaObject( hAdminHandle ); } } else { 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'/')) { *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 --*/ { METADATA_RECORD mdRecord; HRESULT hRes; DWORD dwRequiredLen; BOOL bReturn = TRUE; METADATA_HANDLE hAdminHandle; LPWSTR pszFullPath; DWORD dwPathLen; *pfIsAnyAcl = FALSE; if ( !m_fIsNse ) { 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 = wcslen(m_pszPath); DWORD dwRelPathLen = 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); } }