/*++ Copyright (c) 1996 Microsoft Corporation Module Name : comobj.cxx Abstract: This module defines DCOM Admin APIs. Author: Sophia Chung (sophiac) 23-Nov-1996 --*/ #include "precomp.hxx" #include #include "coiadm.hxx" #include "admacl.hxx" // // Globals // DECLARE_DEBUG_PRINTS_OBJECT(); ULONG g_dwRefCount = 0; COpenHandle g_ohMasterRootHandle; HANDLE_TABLE g_MasterRoot = { NULL, 0, METADATA_MASTER_ROOT_HANDLE, &g_ohMasterRootHandle }; DWORD CADMCOMW::sm_dwProcessIdThis = 0; DWORD CADMCOMW::sm_dwProcessIdRpcSs = 0; // // Private prototypes // // // Used by RestoreHelper // #define RESTORE_HISTORY 0x1 #define RESTORE_BACKUP 0x2 BOOL MakeParentPath( LPWSTR pszPath ); //------------------------------ CADMCOMW::CADMCOMW(): m_ImpIConnectionPointContainer(), m_ImpExpHelp(), m_pMdObject(NULL), m_pMdObject3(NULL), m_dwRefCount(1), m_dwHandleValue(1), m_pEventSink(NULL), m_pConnPoint(NULL), m_piuFTM(NULL), m_bTerminated(FALSE), m_bIsTerminateRoutineComplete(FALSE), m_dwProcessIdCaller(0), m_hProcessCaller(NULL), m_hWaitCaller(NULL), m_dwThreadIdDisconnect(0) { HRESULT hRes; memset((PVOID)m_hashtab, 0, sizeof(m_hashtab) ); InitializeListHead( &m_ObjectListEntry ); hRes = CoCreateInstance(CLSID_MDCOM, NULL, CLSCTX_INPROC_SERVER, IID_IMDCOM2, (void**) &m_pMdObject); if (FAILED(hRes)) { DBGPRINTF(( DBG_CONTEXT, "[CADMCOMW::CADMCOMW] CoCreateInstance(MDCOM) failed, error %lx\n", hRes )); } else { hRes = m_pMdObject->ComMDInitialize(); if (FAILED(hRes)) { DBGPRINTF(( DBG_CONTEXT, "[CADMCOMW::CADMCOMW] ComMDInitialize(MDCOM) failed, error %lx\n", hRes )); m_pMdObject->Release(); m_pMdObject = NULL; } } if (SUCCEEDED(hRes)) { // pickup IMDCOM3 if available // ignore the return value by design // IIS5.1 metadata.dll will fail this call. IIS6.0+ metadata will succeed this call HRESULT hrTemp = m_pMdObject->QueryInterface(IID_IMDCOM3, (void**)&m_pMdObject3); DBG_ASSERT(SUCCEEDED(hrTemp) || NULL == m_pMdObject3); if ( FAILED( hrTemp ) ) { m_pMdObject3 = NULL; } m_pEventSink = new CImpIMDCOMSINKW((IMSAdminBaseW*)this); if( m_pEventSink == NULL ) { DBGPRINTF(( DBG_CONTEXT, "[CADMCOMW::CADMCOMW] CImpIMDCOMSINKW failed, error %lx\n", ERROR_NOT_ENOUGH_MEMORY )); hRes = RETURNCODETOHRESULT(ERROR_NOT_ENOUGH_MEMORY); } else { m_pEventSink->AddRef(); // ImpExpHelp m_ImpExpHelp.Init(this); m_ImpIConnectionPointContainer.Init(this); // Rig this COPaper COM object to be connectable. Assign the connection // point array. This object's connection points are determined at // compile time--it currently has only one connection point: // the CONNPOINT_PAPERSINK connection point. Create a connection // point object for this and assign it into the array. This array could // easily grow to support additional connection points in the future. // First try initializing the connection point object. Pass 'this' as the // pHostObj pointer used by the connection point to pass its AddRef and // Release calls back to the host connectable object. // The initialization will create // its initial dynamic connection array. hRes = m_ConnectionPoint.Init(this, IID_IMSAdminBaseSink_W); if (SUCCEEDED(hRes)) { // // Admin's sink // IConnectionPointContainer* pConnPointContainer = NULL; // First query the object for its Connection Point Container. This // essentially asks the object in the server if it is connectable. hRes = m_pMdObject->QueryInterface( IID_IConnectionPointContainer, (PVOID *)&pConnPointContainer); if SUCCEEDED(hRes) { // Find the requested Connection Point. This AddRef's the // returned pointer. hRes = pConnPointContainer->FindConnectionPoint(IID_IMDCOMSINK_W, &m_pConnPoint); if (SUCCEEDED(hRes)) { hRes = m_pConnPoint->Advise((IUnknown *)m_pEventSink, &m_dwCookie); } pConnPointContainer->Release(); pConnPointContainer = NULL; if (SUCCEEDED(hRes)) { hRes = CoCreateFreeThreadedMarshaler((IUnknown *)this, &m_piuFTM); } } } else { m_ConnectionPoint.Terminate(); } } } if ( SUCCEEDED( hRes ) ) { // Initialize watching the caller process hRes = InitializeCallerWatch(); if ( FAILED( hRes ) ) { DBGPRINTF(( DBG_CONTEXT, "InitializeCallerWatch() failed in CADMCOMW::~CADMCOMW hr=0x%08x.\n", hRes )); } } SetStatus(hRes); // // Insert our object into the global list only if it is valid. // if( SUCCEEDED(hRes) ) { AddObjectToList(); } else { StopNotifications( TRUE ); } } CADMCOMW::~CADMCOMW() { Terminate(); } HRESULT CADMCOMW::StopNotifications( BOOL fRemoveAllPending) { HRESULT hr = S_OK; if ( m_pEventSink != NULL ) { m_pEventSink->DetachAdminObject(); } if ( ( m_dwCookie != 0 ) && ( m_pConnPoint != NULL ) ) { m_pConnPoint->Unadvise(m_dwCookie); m_dwCookie = 0; } m_ConnectionPoint.Disable(); if ( fRemoveAllPending ) { RemoveAllPendingNotifications( TRUE ); } return hr; } HRESULT CADMCOMW::RemoveAllPendingNotifications( BOOL fWaitForCurrent) { HRESULT hr = S_OK; NOTIFY_CONTEXT::RemoveWorkFor( this, fWaitForCurrent ); return hr; } VOID CADMCOMW::Terminate() { HANDLE_TABLE *node; HANDLE_TABLE *nextnode; DWORD i; BOOL bTerminated; // // Terminate must only be called from two locations. And they // should synchronize correctly. // // 1. From ~CADMCOMW. Obviously this should only be called once. // // 2. From ForceTerminate. That routine should only be called in // shutdown. With a reference held on this object. So the final // release should call the dtor and this routine should noop. // bTerminated = (BOOL)InterlockedCompareExchange( (LONG*)&m_bTerminated, TRUE, FALSE ); if( !bTerminated ) { ShutdownCallerWatch(); // // Tell ADMWPROX.DLL to release this object's associated security // context. // ReleaseObjectSecurityContextW( ( IUnknown* )this ); // // Do final release of the connection point objects. // If this isn't the final release, then the client has an outstanding // unbalanced reference to a connection point and a memory leak may // likely result because the host COPaper object is now going away yet // a connection point for this host object will not end up deleting // itself (and its connections array). // m_ConnectionPoint.Terminate(); if (SUCCEEDED(GetStatus())) { m_LockHandleResource.WriteLock(); // // Close all opened handles // for( i = 0; i < HASHSIZE; i++ ) { for( node = nextnode = m_hashtab[i]; node != NULL; node = nextnode ) { if ( node->hAdminHandle != INVALID_ADMINHANDLE_VALUE ) { AdminAclNotifyClose( (LPVOID)this, node->hAdminHandle ); // // call metadata com api // m_pMdObject->ComMDCloseMetaObject( node->hActualHandle ); } nextnode = node->next; delete node->pohHandle; LocalFree(node); } m_hashtab[i] = NULL; } // // Issue TaylorW 3/20/2001 // QFE tree contains this call: // // AdminAclNotifyClose( (LPVOID)this, METADATA_MASTER_ROOT_HANDLE ); // // I have no idea when this may have entered their tree or been lost // from ours. I don't see any record in source depot of it being // removed or added. Need to investigate why it would be needed. // m_LockHandleResource.WriteUnlock(); } if ( m_pEventSink != NULL ) { m_pEventSink->Release(); m_pEventSink = NULL; } if ( m_pConnPoint != NULL ) { m_pConnPoint->Release(); m_pConnPoint = NULL; } if ( m_pMdObject != NULL ) { m_pMdObject->ComMDTerminate(TRUE); m_pMdObject->Release(); m_pMdObject = NULL; if ( m_pMdObject3 != NULL ) { m_pMdObject3->Release(); m_pMdObject3 = NULL; } } if ( m_piuFTM != NULL ) { m_piuFTM->Release(); m_piuFTM = NULL; } m_bIsTerminateRoutineComplete = TRUE; } while ( m_bIsTerminateRoutineComplete != TRUE ) { Sleep( 100 ); } } VOID CADMCOMW::ForceTerminate() { const INT MAX_WAIT_TRIES = 5; INT WaitTries; DBG_ASSERT( !m_bIsTerminateRoutineComplete ); DBG_ASSERT( !m_bTerminated ); // // Wait on the reference count of this object. But bound // the wait so a leaked in process object does not prevent // us from shutting down the service. // // Wait on a ref count of 1, because the caller better be // holding our last reference. This assumes all external // references are killed through CoDisconnect() and all // internal references are released because of dependent // services shutting down. // // Issue TaylorW 3/20/2001 // // In iis 5.1 the web service will shutdown filters after // it has already reported that it is done shutting down. // This is bad, but changing the shutdown logic of the // web service is not worth doing at this time. Hopefully // the shutdown timeout will be sufficient to allow this // operation to complete. // // Windows Bugs 318006 // // CoDisconnect the object and the connection point DisconnectOrphaned(); for( WaitTries = 0; m_dwRefCount > 1 && WaitTries < MAX_WAIT_TRIES; WaitTries++ ) { SleepEx( WaitTries*200, TRUE ); } // // If we timed out. Something is wrong. Most likely someone in // process has leaked this object. These asserts are actually // overactive unless ref tracing is enabled on this object. // // // Issue TaylorW 4/9/2001 // // It looks like front page leaks a base object from in process. // So these assertions need to be turned off. // #define DEBUG_BASE_OBJ_LEAK 0x80000000L IF_DEBUG( BASE_OBJ_LEAK ) { DBG_ASSERT( m_dwRefCount == 1 ); DBG_ASSERT( WaitTries < MAX_WAIT_TRIES ); } // // Go ahead and try to clean up. // Terminate(); } HRESULT CADMCOMW::QueryInterface( REFIID riid, void **ppObject) { HRESULT hr; // If caller watch is not initialized if ( !IsCallerWatchInitialized() ) { // Initialize the caller watch hr = InitializeCallerWatch(); if ( FAILED( hr ) ) { return hr; } } if (riid==IID_IUnknown || riid==IID_IMSAdminBase_W) { *ppObject = (IMSAdminBase *) this; AddRef(); } else if (IID_IMSAdminBase2_W == riid) { *ppObject = (IMSAdminBase2 *) this; AddRef(); } else if (IID_IMSAdminBase3_W == riid && NULL != m_pMdObject3) { *ppObject = (IMSAdminBase3 *) this; AddRef(); } else if (IID_IMSImpExpHelp_W == riid) { *ppObject = &m_ImpExpHelp; AddRef(); } else if (IID_IConnectionPointContainer == riid) { METADATA_HANDLE hActualHandle; hr = LookupAndAccessCheck( METADATA_MASTER_ROOT_HANDLE, &hActualHandle, NULL, 0, METADATA_PERMISSION_READ); if (FAILED(hr)) { return hr; } *ppObject = &m_ImpIConnectionPointContainer; AddRef(); } else if (IID_IMarshal == riid) { return m_piuFTM->QueryInterface(riid, ppObject); } else { return E_NOINTERFACE; } return S_OK; } ULONG CADMCOMW::AddRef( ) { DWORD dwRefCount; dwRefCount = InterlockedIncrement((long *)&m_dwRefCount); if( sm_pDbgRefTraceLog ) { WriteRefTraceLog( sm_pDbgRefTraceLog, dwRefCount, this ); } return dwRefCount; } ULONG CADMCOMW::Release( ) { DWORD dwRefCount; dwRefCount = InterlockedDecrement((long *)&m_dwRefCount); if( sm_pDbgRefTraceLog ) { WriteRefTraceLog( sm_pDbgRefTraceLog, -(LONG)dwRefCount, this ); } if( dwRefCount == 1 ) { // // We keep a list of objects around so that we can clean up and // shutdown successfully. The list holds a reference to this object // when we hit a reference of 1, we know it is time to remove // ourselves from the list. If we are in shutdown we may already // have been removed from the list. But normally, this call to // RemoveObjectFromList removes our last reference and thus sends // us back through Release and ultimately to our destructor. // RemoveObjectFromList(); } else if( dwRefCount == 0 ) { delete this; } return dwRefCount; } HRESULT CADMCOMW::AddKey( IN METADATA_HANDLE hMDHandle, IN LPCWSTR pszMDPath) /*++ Routine Description: Add meta object and adds it to the list of child objects for the object specified by Path. Arguments: hMDHandle - open handle pszMDPath - path of the object to be added Return Value: Status. --*/ { HRESULT hresReturn = S_OK; hresReturn = AddKeyHelper(hMDHandle, pszMDPath); return hresReturn; } HRESULT CADMCOMW::DeleteKey( IN METADATA_HANDLE hMDHandle, IN LPCWSTR pszMDPath) /*++ Routine Description: Deletes a meta object and all of its data. Arguments: hMDHandle - open handle pszMDPath - path of object to be deleted, relative to the path of Handle. Must not be NULL. Return Value: Status. --*/ { HRESULT hresReturn = S_OK; METADATA_HANDLE hActualHandle; if (pszMDPath == NULL) { hresReturn = E_INVALIDARG; } else { // // lookup and access check // hresReturn = LookupAndAccessCheck(hMDHandle, &hActualHandle, pszMDPath, AAC_DELETEKEY, METADATA_PERMISSION_WRITE); if (SUCCEEDED(hresReturn)) { // // call metadata com api // hresReturn = m_pMdObject->ComMDDeleteMetaObjectW( hActualHandle, pszMDPath ); } } return hresReturn; } HRESULT STDMETHODCALLTYPE CADMCOMW::DeleteChildKeys( METADATA_HANDLE hMDHandle, LPCWSTR pszMDPath) /*++ Routine Description: Deletes all child meta objects of the specified object, with all of their data. Arguments: hMDHandle - open handle pszMDPath - path of the parent of the objects to be deleted, relative to the path of Handle. Return Value: Status. --*/ { HRESULT hresReturn = S_OK; METADATA_HANDLE hActualHandle; // // lookup and access check // hresReturn = LookupAndAccessCheck(hMDHandle, &hActualHandle, pszMDPath, AAC_DELETEKEY, METADATA_PERMISSION_WRITE); if (SUCCEEDED(hresReturn)) { // // call metadata com api // hresReturn = m_pMdObject->ComMDDeleteChildMetaObjectsW( hActualHandle, pszMDPath ); } return hresReturn; } HRESULT STDMETHODCALLTYPE CADMCOMW::EnumKeys( METADATA_HANDLE hMDHandle, LPCWSTR pszMDPath, LPWSTR pszMDName, DWORD dwMDEnumObjectIndex) /*++ Routine Description: Enumerate objects in path. Arguments: hMDHandle - open handle pszMDPath - path of parent object, relative to the path of Handle eg. "Root Object/Child/GrandChild" pszMDName - buffer where the Name of the object is returned dwEnumObjectIndex - index of the value to be retrieved Return Value: Status. --*/ { HRESULT hresReturn = S_OK; METADATA_HANDLE hActualHandle; if (pszMDName == NULL) { hresReturn = E_INVALIDARG; } else { // // lookup and access check // hresReturn = LookupAndAccessCheck(hMDHandle, &hActualHandle, pszMDPath, AAC_ENUM_KEYS, METADATA_PERMISSION_READ); if (SUCCEEDED(hresReturn)) { // // call metadata com api // hresReturn = m_pMdObject->ComMDEnumMetaObjectsW( hActualHandle, pszMDPath, pszMDName, dwMDEnumObjectIndex ); } } return hresReturn; } HRESULT STDMETHODCALLTYPE CADMCOMW::CopyKey( METADATA_HANDLE hMDSourceHandle, LPCWSTR pszMDSourcePath, METADATA_HANDLE hMDDestHandle, LPCWSTR pszMDDestPath, BOOL bMDOverwriteFlag, BOOL bMDCopyFlag) /*++ Routine Description: Copy or move source meta object and its data and descendants to Dest. Arguments: hMDSourceHandle - open handle pszMDSourcePath - path of the object to be copied hMDDestHandle - handle of the new location for the object pszMDDestPath - path of the new location for the object, relative to the path of hMDDestHandle bMDOverwriteFlag - determine the behavior if a meta object with the same name as source is already a child of pszMDDestPath. bMDCopyFlag - determine whether Source is deleted from its original location Return Value: Status --*/ { HRESULT hresReturn = S_OK; METADATA_HANDLE hSActualHandle; METADATA_HANDLE hDActualHandle; // // lookup and access check source // if (bMDCopyFlag) { hresReturn = LookupAndAccessCheck(hMDSourceHandle, &hSActualHandle, pszMDSourcePath, 0, METADATA_PERMISSION_READ); } else { // // Deleting source path, so need delete permission // hresReturn = LookupAndAccessCheck(hMDSourceHandle, &hSActualHandle, pszMDSourcePath, AAC_DELETEKEY, METADATA_PERMISSION_WRITE); } if (SUCCEEDED(hresReturn)) { // // lookup and access check dest // hresReturn = LookupAndAccessCheck(hMDDestHandle, &hDActualHandle, pszMDDestPath, AAC_COPYKEY, METADATA_PERMISSION_WRITE); if (SUCCEEDED(hresReturn)) { // // call metadata com api // hresReturn = m_pMdObject->ComMDCopyMetaObjectW( hSActualHandle, pszMDSourcePath, hDActualHandle, pszMDDestPath, bMDOverwriteFlag, bMDCopyFlag ); } } return hresReturn; } HRESULT STDMETHODCALLTYPE CADMCOMW::RenameKey( METADATA_HANDLE hMDHandle, LPCWSTR pszMDPath, LPCWSTR pszMDNewName) { HRESULT hr = S_OK; BOOL fRet; DWORD dwError; METADATA_HANDLE hActualHandle; COpenHandle *pohParent = NULL; DWORD cchMDPath; DWORD i; STRAU strNewPath; // Check args if ( ( hMDHandle == METADATA_MASTER_ROOT_HANDLE ) || ( pszMDPath == NULL ) || ( pszMDNewName == NULL ) || ( *pszMDNewName == L'\0' ) || ( wcschr( pszMDNewName, MD_PATH_DELIMETERW ) != NULL ) || ( wcschr( pszMDNewName, MD_ALT_PATH_DELIMETERW ) != NULL ) ) { hr = E_INVALIDARG; goto exit; } // Get the len of the source path cchMDPath = (DWORD)wcslen( pszMDPath ); // Check if ( ( cchMDPath == 0 ) || ( cchMDPath >= METADATA_MAX_NAME_LEN ) ) { hr = E_INVALIDARG; goto exit; } // Map Admin Handle to Actual Handle dwError = Lookup( hMDHandle, &hActualHandle, &pohParent ); if ( dwError != ERROR_SUCCESS ) { hr = HRESULT_FROM_WIN32( dwError ); goto exit; } // Check access fRet = AdminAclAccessCheck( m_pMdObject, this, hMDHandle, pszMDPath, MD_ADMIN_ACL, METADATA_PERMISSION_WRITE, pohParent ); if ( !fRet ) { dwError = GetLastError(); hr = HRESULT_FROM_WIN32( dwError ); goto exit; } // Now we need to do the harder part: check whether the caller // has access to the new path. To do so we need to construct the // target path by replacing the last name in the source path with the new name. // If the source path ends with delimiter if ( ( pszMDPath[cchMDPath-1] == MD_PATH_DELIMETERW ) || ( pszMDPath[cchMDPath-1] == MD_ALT_PATH_DELIMETERW ) ) { // Ignore it cchMDPath--; // The resulting path should not be empty or ending with delimiter if ( ( cchMDPath == 0 ) || ( pszMDPath[cchMDPath-1] == MD_PATH_DELIMETERW ) || ( pszMDPath[cchMDPath-1] == MD_ALT_PATH_DELIMETERW ) ) { hr = E_INVALIDARG; goto exit; } } // Find the last delimiter for ( i = cchMDPath; i != 0; i-- ) { // Delimiter? if ( ( pszMDPath[i-1] == MD_PATH_DELIMETERW ) || ( pszMDPath[i-1] == MD_ALT_PATH_DELIMETERW ) ) { // The part we are interested in is upto excluding the delimiter // It is valid for cchMDPath to become 0. cchMDPath = i-1; break; } } // If there was no delimiter if ( i == 0 ) { // This source was just a name, so bellow we shouldn't copy anything cchMDPath = 0; } // Copy the source path w/o the last name fRet = strNewPath.Copy( (const LPWSTR)pszMDPath, cchMDPath ); if ( !fRet ) { hr = E_OUTOFMEMORY; goto exit; } fRet = strNewPath.Append( L"/", 1 ); if ( !fRet ) { hr = E_OUTOFMEMORY; goto exit; } // Append the new name fRet = strNewPath.Append( (const LPWSTR)pszMDNewName ); if ( !fRet ) { hr = E_OUTOFMEMORY; goto exit; } // Check access on the target path fRet = AdminAclAccessCheck( m_pMdObject, this, hMDHandle, strNewPath.QueryStrW(), MD_ADMIN_ACL, METADATA_PERMISSION_WRITE, pohParent ); if ( !fRet ) { dwError = GetLastError(); hr = HRESULT_FROM_WIN32( dwError ); goto exit; } // call metadata com api hr = m_pMdObject->ComMDRenameMetaObjectW( hActualHandle, pszMDPath, pszMDNewName ); if ( FAILED( hr ) ) { goto exit; } exit: if ( pohParent != NULL ) { pohParent->Release(this); pohParent = NULL; } return hr; } HRESULT STDMETHODCALLTYPE CADMCOMW::SetData( METADATA_HANDLE hMDHandle, LPCWSTR pszMDPath, PMETADATA_RECORD pmdrMDData) /*++ Routine Description: Set a data object. Arguments: hMDHandle - open handle pszMDPath - path of the meta object with which this data is associated pmdrMDData - data to set Return Value: Status. --*/ { HRESULT hresReturn = S_OK; METADATA_HANDLE hActualHandle; // // lookup and access check // hresReturn = LookupAndAccessCheck(hMDHandle, &hActualHandle, pszMDPath, pmdrMDData->dwMDIdentifier, METADATA_PERMISSION_WRITE ); if (SUCCEEDED(hresReturn)) { if ( !AdminAclNotifySetOrDeleteProp( hMDHandle, pmdrMDData->dwMDIdentifier ) ) { DBGPRINTF(( DBG_CONTEXT, "[CADMCOMW::SetData] AdminAclNotifySetOrDel failed, error %lx\n", GetLastError() )); hresReturn = RETURNCODETOHRESULT( GetLastError() ); } else { // // call metadata com api // hresReturn = m_pMdObject->ComMDSetMetaDataW( hActualHandle, pszMDPath, pmdrMDData ); } } return hresReturn; } HRESULT STDMETHODCALLTYPE CADMCOMW::GetData( METADATA_HANDLE hMDHandle, LPCWSTR pszMDPath, PMETADATA_RECORD pmdrMDData, DWORD *pdwMDRequiredDataLen) /*++ Routine Description: Get one metadata value Arguments: hMDHandle - open handle pszMDPath - path of the meta object with which this data is associated pmdrMDData - data structure pdwMDRequiredDataLen - updated with required length Return Value: Status. --*/ { HRESULT hresReturn = S_OK; METADATA_HANDLE hActualHandle; BOOL fEnableSecureAccess; BOOL fRequestedInheritedStatus; DWORD dwRetCode; if ((pmdrMDData == NULL) || ((pmdrMDData->dwMDDataLen != 0) && (pmdrMDData->pbMDData == NULL)) || !CheckGetAttributes(pmdrMDData->dwMDAttributes) || (pmdrMDData->dwMDDataType >= INVALID_END_METADATA)) { hresReturn = E_INVALIDARG; } else { // // lookup and access check // hresReturn = LookupAndAccessCheck(hMDHandle, &hActualHandle, pszMDPath, pmdrMDData->dwMDIdentifier, METADATA_PERMISSION_READ, &fEnableSecureAccess ); if (SUCCEEDED(hresReturn)) { fRequestedInheritedStatus = pmdrMDData->dwMDAttributes & METADATA_ISINHERITED; pmdrMDData->dwMDAttributes |= METADATA_ISINHERITED; // // call metadata com api // hresReturn = m_pMdObject->ComMDGetMetaDataW( hActualHandle, pszMDPath, pmdrMDData, pdwMDRequiredDataLen ); // // if metadata is secure, check if can access this property from // where it is defined, i.e using the ACL visible at the definition // point in tree. // if ( SUCCEEDED( hresReturn ) && (pmdrMDData->dwMDAttributes & METADATA_SECURE) && (dwRetCode = IsReadAccessGranted( hMDHandle, (LPWSTR)pszMDPath, pmdrMDData )) != ERROR_SUCCESS ) { hresReturn = RETURNCODETOHRESULT( dwRetCode ); } if ( !fRequestedInheritedStatus ) { pmdrMDData->dwMDAttributes &= ~METADATA_ISINHERITED; } // // if metadata secure, check access allowed to secure properties // if ( SUCCEEDED( hresReturn ) && (pmdrMDData->dwMDAttributes & METADATA_SECURE) && !fEnableSecureAccess) { *pdwMDRequiredDataLen = 0; pmdrMDData->dwMDDataLen = 0; hresReturn = RETURNCODETOHRESULT( ERROR_ACCESS_DENIED ); } } } return hresReturn; } HRESULT STDMETHODCALLTYPE CADMCOMW::DeleteData( METADATA_HANDLE hMDHandle, LPCWSTR pszMDPath, DWORD dwMDIdentifier, DWORD dwMDDataType) /*++ Routine Description: Deletes a data object. Arguments: hMDHandle - open handle pszMDPath - path of the meta object with which this data is associated dwMDIdentifier - identifier of the data to remove dwMDDataType - optional type of the data to remove Return Value: Status. --*/ { HRESULT hresReturn = S_OK; METADATA_HANDLE hActualHandle; if (dwMDDataType >= INVALID_END_METADATA) { hresReturn = E_INVALIDARG; } else { // // lookup and access check // hresReturn = LookupAndAccessCheck(hMDHandle, &hActualHandle, pszMDPath, dwMDIdentifier, METADATA_PERMISSION_WRITE); if (SUCCEEDED(hresReturn)) { if ( !AdminAclNotifySetOrDeleteProp( hMDHandle, dwMDIdentifier ) ) { DBGPRINTF(( DBG_CONTEXT, "[CADMCOMW::DeleteData] AdminAclNotifySetOrDel failed, error %lx\n", GetLastError() )); hresReturn = RETURNCODETOHRESULT( GetLastError() ); } else { // // call metadata com api // hresReturn = m_pMdObject->ComMDDeleteMetaDataW( hActualHandle, pszMDPath, dwMDIdentifier, dwMDDataType ); } } } return hresReturn; } HRESULT STDMETHODCALLTYPE CADMCOMW::EnumData( METADATA_HANDLE hMDHandle, LPCWSTR pszMDPath, PMETADATA_RECORD pmdrMDData, DWORD dwMDEnumDataIndex, DWORD *pdwMDRequiredDataLen) /*++ Routine Description: Enumerate properties of object. Arguments: hMDHandle - open handle pszMDPath - path of the meta object with which this data is associated pmdrMDData - data structure pdwMDRequiredDataLen - updated with required length Return Value: Status. --*/ { HRESULT hresReturn = S_OK; METADATA_HANDLE hActualHandle; BOOL fSecure; BOOL fRequestedInheritedStatus; DWORD dwRetCode; if ((pmdrMDData == NULL) || ((pmdrMDData->dwMDDataLen != 0) && (pmdrMDData->pbMDData == NULL)) || !CheckGetAttributes(pmdrMDData->dwMDAttributes) || (pmdrMDData->dwMDDataType >= INVALID_END_METADATA)) { hresReturn = E_INVALIDARG; } else { // // lookup and access check // hresReturn = LookupAndAccessCheck(hMDHandle, &hActualHandle, pszMDPath, 0, METADATA_PERMISSION_READ, &fSecure); if (SUCCEEDED(hresReturn)) { fRequestedInheritedStatus = pmdrMDData->dwMDAttributes & METADATA_ISINHERITED; pmdrMDData->dwMDAttributes |= METADATA_ISINHERITED; // // call metadata com api // hresReturn = m_pMdObject->ComMDEnumMetaDataW( hActualHandle, pszMDPath, pmdrMDData, dwMDEnumDataIndex, pdwMDRequiredDataLen ); // // if metadata is secure, check if can access this property from // where it is defined, i.e using the ACL visible at the definition // point in tree. // if ( SUCCEEDED( hresReturn ) && (pmdrMDData->dwMDAttributes & METADATA_SECURE) && (dwRetCode = IsReadAccessGranted( hMDHandle, (LPWSTR)pszMDPath, pmdrMDData )) != ERROR_SUCCESS ) { hresReturn = RETURNCODETOHRESULT( dwRetCode ); if ( !pmdrMDData->dwMDDataTag ) { memset( pmdrMDData->pbMDData, 0x0, pmdrMDData->dwMDDataLen ); } } if ( !fRequestedInheritedStatus ) { pmdrMDData->dwMDAttributes &= ~METADATA_ISINHERITED; } if ( !fSecure && SUCCEEDED(hresReturn) ) { if ( pmdrMDData->dwMDAttributes & METADATA_SECURE ) { hresReturn = RETURNCODETOHRESULT( ERROR_ACCESS_DENIED ); if ( !pmdrMDData->dwMDDataTag ) { memset( pmdrMDData->pbMDData, 0x0, pmdrMDData->dwMDDataLen ); } } } } } return hresReturn; } HRESULT STDMETHODCALLTYPE CADMCOMW::GetAllData( METADATA_HANDLE hMDHandle, LPCWSTR pszMDPath, DWORD dwMDAttributes, DWORD dwMDUserType, DWORD dwMDDataType, DWORD *pdwMDNumDataEntries, DWORD *pdwMDDataSetNumber, DWORD dwMDBufferSize, unsigned char *pbMDBuffer, DWORD *pdwMDRequiredBufferSize) /*++ Routine Description: Gets all data associated with a Meta Object Arguments: hMDHandle - open handle pszMDPath - path of the meta object with which this data is associated dwMDAttributes - flags for the data dwMDUserType - user Type for the data dwMDDataType - type of the data pdwMDNumDataEntries - number of entries copied to Buffer pdwMDDataSetNumber - number associated with this data set dwMDBufferSize - size in bytes of buffer pbMDBuffer - buffer to store the data pdwMDRequiredBufferSize - updated with required length of buffer Return Value: Status. --*/ { HRESULT hresReturn = S_OK; METADATA_HANDLE hActualHandle; BOOL fSecure; BOOL fRequestedInheritedStatus; if ((pdwMDNumDataEntries == NULL) || ((dwMDBufferSize != 0) && (pbMDBuffer == NULL)) || !CheckGetAttributes(dwMDAttributes) || (dwMDDataType >= INVALID_END_METADATA)) { hresReturn = E_INVALIDARG; } else { // // lookup and access check // hresReturn = LookupAndAccessCheck(hMDHandle, &hActualHandle, pszMDPath, AAC_GETALL, METADATA_PERMISSION_READ, &fSecure ); if (SUCCEEDED(hresReturn)) { fRequestedInheritedStatus = dwMDAttributes & METADATA_ISINHERITED; dwMDAttributes |= METADATA_ISINHERITED; // // call metadata com api // hresReturn = m_pMdObject->ComMDGetAllMetaDataW( hActualHandle, pszMDPath, dwMDAttributes, dwMDUserType, dwMDDataType, pdwMDNumDataEntries, pdwMDDataSetNumber, dwMDBufferSize, pbMDBuffer, pdwMDRequiredBufferSize ); if ( SUCCEEDED(hresReturn) ) { PMETADATA_GETALL_RECORD pMDRecord; DWORD iP; // // Scan for secure properties // For such properties, check if user has access to it using following rules: // - must have right to access secure properties in ACE // - must have access to property using ACL visible where property is defined // if no access to property then remove it from list of returned properties pMDRecord = (PMETADATA_GETALL_RECORD)pbMDBuffer; for ( iP = 0 ; iP < *pdwMDNumDataEntries ; ) { if ( pMDRecord->dwMDAttributes & METADATA_SECURE ) { if ( !fSecure || IsReadAccessGranted( hMDHandle, (LPWSTR)pszMDPath, (PMETADATA_RECORD)pMDRecord ) != ERROR_SUCCESS ) { // // remove this property from METADATA_RECORD list, // zero out content // memset( pbMDBuffer + pMDRecord->dwMDDataOffset, 0x0, pMDRecord->dwMDDataLen ); --*pdwMDNumDataEntries; memmove( pMDRecord, pMDRecord + 1, sizeof(METADATA_GETALL_RECORD) * (*pdwMDNumDataEntries-iP) ); continue; } } if ( !fRequestedInheritedStatus ) { pMDRecord->dwMDAttributes &= ~METADATA_ISINHERITED; } ++iP; ++pMDRecord; } } } } return hresReturn; } HRESULT STDMETHODCALLTYPE CADMCOMW::DeleteAllData( METADATA_HANDLE hMDHandle, LPCWSTR pszMDPath, DWORD dwMDUserType, DWORD dwMDDataType) { HRESULT hresReturn = S_OK; METADATA_HANDLE hActualHandle; if (dwMDDataType >= INVALID_END_METADATA) { hresReturn = E_INVALIDARG; } else { // // lookup and access check // hresReturn = LookupAndAccessCheck(hMDHandle, &hActualHandle, pszMDPath, 0, METADATA_PERMISSION_WRITE); if (SUCCEEDED(hresReturn)) { // // call metadata com api // hresReturn = m_pMdObject->ComMDDeleteAllMetaDataW( hActualHandle, pszMDPath, dwMDUserType, dwMDDataType ); } } return hresReturn; } HRESULT STDMETHODCALLTYPE CADMCOMW::CopyData( METADATA_HANDLE hMDSourceHandle, LPCWSTR pszMDSourcePath, METADATA_HANDLE hMDDestHandle, LPCWSTR pszMDDestPath, DWORD dwMDAttributes, DWORD dwMDUserType, DWORD dwMDDataType, BOOL bMDCopyFlag) /*++ Routine Description: Copies or moves data associated with the source object to the destination object. Arguments: hMDSourceHandle - open handle pszMDSourcePath - path of the meta object with which then source data is associated hMDDestHandle - handle returned by MDOpenKey with write permission pszMDDestPath - path of the meta object for data to be copied to dwMDAttributes - flags for the data dwMDUserType - user Type for the data dwMDDataType - optional type of the data to copy bMDCopyFlag - if true, data will be copied; if false, data will be moved. Return Value: Status. --*/ { HRESULT hresReturn = S_OK; METADATA_HANDLE hSActualHandle; METADATA_HANDLE hDActualHandle; if (((!bMDCopyFlag) && (dwMDAttributes & METADATA_INHERIT)) || ((dwMDAttributes & METADATA_PARTIAL_PATH) && !(dwMDAttributes & METADATA_INHERIT))) { hresReturn = E_INVALIDARG; } else { // // lookup and access check source // if (bMDCopyFlag) { hresReturn = LookupAndAccessCheck(hMDSourceHandle, &hSActualHandle, pszMDSourcePath, 0, METADATA_PERMISSION_READ); } else { // // Deleting source data, so need delete permission // hresReturn = LookupAndAccessCheck(hMDSourceHandle, &hSActualHandle, pszMDSourcePath, 0, METADATA_PERMISSION_WRITE); } if (SUCCEEDED(hresReturn)) { // // lookup and access check dest // hresReturn = LookupAndAccessCheck(hMDDestHandle, &hDActualHandle, pszMDDestPath, 0, METADATA_PERMISSION_WRITE); if (SUCCEEDED(hresReturn)) { // // call metadata com api // hresReturn = m_pMdObject->ComMDCopyMetaDataW(hSActualHandle, pszMDSourcePath, hDActualHandle, pszMDDestPath, dwMDAttributes, dwMDUserType, dwMDDataType, bMDCopyFlag ); } } } return hresReturn; } HRESULT STDMETHODCALLTYPE CADMCOMW::GetDataPaths( METADATA_HANDLE hMDHandle, LPCWSTR pszMDPath, DWORD dwMDIdentifier, DWORD dwMDDataType, DWORD dwMDBufferSize, LPWSTR pszMDBuffer, DWORD *pdwMDRequiredBufferSize) { HRESULT hresReturn = S_OK; METADATA_HANDLE hActualHandle; BOOL fSecure; if (((pszMDBuffer == NULL) && (dwMDBufferSize != 0)) || (dwMDDataType >= INVALID_END_METADATA) || (pdwMDRequiredBufferSize == NULL)) { hresReturn = E_INVALIDARG; } else { // // lookup and access check // hresReturn = LookupAndAccessCheck(hMDHandle, &hActualHandle, pszMDPath, 0, METADATA_PERMISSION_READ, &fSecure); if (SUCCEEDED(hresReturn)) { // // call metadata com api // hresReturn = m_pMdObject->ComMDGetMetaDataPathsW( hActualHandle, pszMDPath, dwMDIdentifier, dwMDDataType, dwMDBufferSize, pszMDBuffer, pdwMDRequiredBufferSize ); } } return hresReturn; } HRESULT STDMETHODCALLTYPE CADMCOMW::OpenKey( METADATA_HANDLE hMDHandle, LPCWSTR pszMDPath, DWORD dwMDAccessRequested, DWORD dwMDTimeOut, PMETADATA_HANDLE phMDNewHandle) /*++ Routine Description: Opens a meta object for read and/or write access. Arguments: hMDHandle - open handle pszMDPath - path of the object to be opened dwMDAccessRequested - permissions requested dwMDTimeOut - time to block waiting for open to succeed, in miliseconds. phMDNewHandle - handle to be passed to other MD routines Return Value: Status. --*/ { HRESULT hresReturn = S_OK; // If caller watch is not initialized if ( !IsCallerWatchInitialized() ) { // Initialize the caller watch hresReturn = InitializeCallerWatch(); if ( FAILED( hresReturn ) ) { return hresReturn; } } hresReturn = OpenKeyHelper(hMDHandle, pszMDPath, dwMDAccessRequested, dwMDTimeOut, phMDNewHandle); return hresReturn; } HRESULT STDMETHODCALLTYPE CADMCOMW::CloseKey( METADATA_HANDLE hMDHandle) /*++ Routine Description: Closes a handle to a meta object. Arguments: hMDHandle - open handle Return Value: Status. --*/ { HRESULT hresReturn = S_OK; METADATA_HANDLE hActualHandle; DWORD dwTemp; COpenHandle *pohHandle; if ((hMDHandle == METADATA_MASTER_ROOT_HANDLE)) { hresReturn = E_HANDLE; } else { // // Map Admin Handle to Actual Handle // if( (dwTemp = Lookup( hMDHandle, &hActualHandle, &pohHandle )) != ERROR_SUCCESS ) { hresReturn = RETURNCODETOHRESULT(dwTemp); } else { // // call metadata com api // hresReturn = m_pMdObject->ComMDCloseMetaObject( hActualHandle ); pohHandle->Release(this); // // Remove node from handle table // if (SUCCEEDED(hresReturn)) { pohHandle->Release(this); } } } return hresReturn; } HRESULT STDMETHODCALLTYPE CADMCOMW::ChangePermissions( METADATA_HANDLE hMDHandle, DWORD dwMDTimeOut, DWORD dwMDAccessRequested) /*++ Routine Description: Changes permissions on an open meta object handle. Arguments: hMDHandle - handle to be modified dwMDTimeOut - time to block waiting for open to succeed, in miliseconds. dwMDAccessRequested - requested permissions Return Value: Status. --*/ { HRESULT hr = S_OK; METADATA_HANDLE hActualHandle; if ( hMDHandle == METADATA_MASTER_ROOT_HANDLE ) { hr = E_HANDLE; goto exit; } if ( ( ( dwMDAccessRequested & ( METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE ) ) == 0 ) || ( ( dwMDAccessRequested & ~( METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE ) ) != 0 ) ) { hr = E_INVALIDARG; goto exit; } // // Map Admin Handle to Actual Handle // and check access // hr = LookupAndAccessCheck( hMDHandle, &hActualHandle, L"", 0, dwMDAccessRequested ); if( FAILED( hr ) ) { goto exit; } // // call metadata com api // hr = m_pMdObject->ComMDChangePermissions( hActualHandle, dwMDTimeOut, dwMDAccessRequested ); exit: return hr; } HRESULT STDMETHODCALLTYPE CADMCOMW::SaveData( ) /*++ Routine Description: Saves all data changed since the last load or save to permanent storage. Arguments: None. Return Value: Status. --*/ { HRESULT hr = S_OK; METADATA_HANDLE mdhRoot = METADATA_MASTER_ROOT_HANDLE; METADATA_HANDLE hActualHandle; // // lookup and access check // hr = LookupAndAccessCheck( METADATA_MASTER_ROOT_HANDLE, &hActualHandle, L"", 0, METADATA_PERMISSION_READ); if ( FAILED( hr ) ) { goto exit; } // // First try to lock the tree // hr = m_pMdObject->ComMDOpenMetaObjectW( METADATA_MASTER_ROOT_HANDLE, NULL, METADATA_PERMISSION_READ, DEFAULT_SAVE_TIMEOUT, &mdhRoot ); if ( FAILED( hr ) ) { goto exit; } // // call metadata com api // hr = m_pMdObject->ComMDSaveData(mdhRoot); m_pMdObject->ComMDCloseMetaObject(mdhRoot); exit: return hr; } HRESULT STDMETHODCALLTYPE CADMCOMW::GetHandleInfo( METADATA_HANDLE hMDHandle, PMETADATA_HANDLE_INFO pmdhiInfo) /*++ Routine Description: Gets the information associated with a handle. Arguments: hMDHandle - handle to get information about pmdhiInfo - structure filled in with the information Return Value: Status. --*/ { HRESULT hresReturn = S_OK; METADATA_HANDLE hActualHandle; DWORD dwRetCode = ERROR_SUCCESS; if (pmdhiInfo == NULL) { hresReturn = E_INVALIDARG; } else { // // Map Admin Handle to Actual Handle // if( (dwRetCode = Lookup( hMDHandle, &hActualHandle )) != ERROR_SUCCESS ) { hresReturn = RETURNCODETOHRESULT(dwRetCode); } else { // // call metadata com api // hresReturn = m_pMdObject->ComMDGetHandleInfo( hActualHandle, pmdhiInfo ); } } return hresReturn; } HRESULT STDMETHODCALLTYPE CADMCOMW::GetSystemChangeNumber( DWORD *pdwSystemChangeNumber) /*++ Routine Description: Gets the System Change Number. Arguments: pdwSystemChangeNumber - system change number Return Value: Status. --*/ { HRESULT hr = S_OK; METADATA_HANDLE hActualHandle; // Check args if ( pdwSystemChangeNumber == NULL ) { hr = E_INVALIDARG; goto exit; } // // lookup and access check // hr = LookupAndAccessCheck( METADATA_MASTER_ROOT_HANDLE, &hActualHandle, L"", 0, METADATA_PERMISSION_READ ); if ( FAILED( hr ) ) { goto exit; } // // call metadata com api // hr = m_pMdObject->ComMDGetSystemChangeNumber( pdwSystemChangeNumber ); exit: return hr; } HRESULT STDMETHODCALLTYPE CADMCOMW::GetDataSetNumber( METADATA_HANDLE hMDHandle, LPCWSTR pszMDPath, DWORD *pdwMDDataSetNumber) /*++ Routine Description: Gets all the data set number associated with a Meta Object. Arguments: hMDHandle - open handle pszMDPath - path of the meta object with which this data is associated pdwMDDataSetNumber - number associated with this data set Return Value: Status. --*/ { HRESULT hresReturn = S_OK; METADATA_HANDLE hActualHandle; if (pdwMDDataSetNumber == NULL) { hresReturn = E_INVALIDARG; } else { // // lookup and access check // hresReturn = LookupAndAccessCheck(hMDHandle, &hActualHandle, pszMDPath, 0, METADATA_PERMISSION_READ); if (SUCCEEDED(hresReturn)) { // // call metadata com api // hresReturn = m_pMdObject->ComMDGetDataSetNumberW( hActualHandle, pszMDPath, pdwMDDataSetNumber ); } } return hresReturn; } HRESULT STDMETHODCALLTYPE CADMCOMW::SetLastChangeTime( METADATA_HANDLE hMDHandle, LPCWSTR pszMDPath, PFILETIME pftMDLastChangeTime, BOOL bLocalTime) /*++ Routine Description: Set the last change time associated with a Meta Object. Arguments: hMDHandle - open handle pszMDPath - path of the affected meta object pftMDLastChangeTime - new change time for the meta object Return Value: Status. --*/ { HRESULT hr = S_OK; BOOL fRet; DWORD dwError; METADATA_HANDLE hActualHandle; FILETIME ftTime; FILETIME *pftTime = NULL; // Check arhs if ( ( pftMDLastChangeTime == NULL ) || ( hMDHandle == METADATA_MASTER_ROOT_HANDLE ) ) { hr = E_INVALIDARG; goto exit; } // lookup and access check hr = LookupAndAccessCheck( hMDHandle, &hActualHandle, pszMDPath, 0, METADATA_PERMISSION_WRITE ); if ( FAILED( hr ) ) { goto exit; } if ( bLocalTime ) { fRet = LocalFileTimeToFileTime( pftMDLastChangeTime, &ftTime ); if ( !fRet ) { dwError = GetLastError(); hr = HRESULT_FROM_WIN32( dwError ); goto exit; } pftTime = &ftTime; } else { pftTime = pftMDLastChangeTime; } // call metadata com api hr = m_pMdObject->ComMDSetLastChangeTimeW( hActualHandle, pszMDPath, pftTime ); if ( FAILED( hr ) ) { goto exit; } exit: return hr; } HRESULT STDMETHODCALLTYPE CADMCOMW::GetLastChangeTime( METADATA_HANDLE hMDHandle, LPCWSTR pszMDPath, PFILETIME pftMDLastChangeTime, BOOL bLocalTime) /*++ Routine Description: Set the last change time associated with a Meta Object. Arguments: Handle - open handle pszMDPath - path of the affected meta object pftMDLastChangeTime - place to return the change time for the meta object Return Value: Status. --*/ { HRESULT hresReturn = S_OK; METADATA_HANDLE hActualHandle; FILETIME ftTime; if (pftMDLastChangeTime == NULL) { hresReturn = E_INVALIDARG; } else { // // lookup and access check // hresReturn = LookupAndAccessCheck(hMDHandle, &hActualHandle, pszMDPath, 0, METADATA_PERMISSION_READ); if (SUCCEEDED(hresReturn)) { // // call metadata com api // hresReturn = m_pMdObject->ComMDGetLastChangeTimeW( hActualHandle, pszMDPath, &ftTime ); if (bLocalTime) { if (!FileTimeToLocalFileTime(&ftTime, pftMDLastChangeTime)) { hresReturn = E_UNEXPECTED; } } else { *pftMDLastChangeTime = ftTime; } } } return hresReturn; } HRESULT CADMCOMW::BackupHelper( LPCWSTR pszMDBackupLocation, DWORD dwMDVersion, DWORD dwMDFlags, LPCWSTR pszPasswd) { HRESULT hresReturn = S_OK; HRESULT hresWarning = S_OK; METADATA_HANDLE mdhRoot = METADATA_MASTER_ROOT_HANDLE; if ( !AdminAclAccessCheck( m_pMdObject, (LPVOID)this, METADATA_MASTER_ROOT_HANDLE, L"", MD_ADMIN_ACL, METADATA_PERMISSION_WRITE, &g_ohMasterRootHandle ) ) { DBGPRINTF(( DBG_CONTEXT, "[CADMCOMW::Backup] AdminAclAccessCheck failed, error %lx\n", GetLastError() )); hresReturn = RETURNCODETOHRESULT( GetLastError() ); } else { if ((dwMDFlags & MD_BACKUP_SAVE_FIRST) != 0) { // // First lock the tree // hresReturn = m_pMdObject->ComMDOpenMetaObjectW( METADATA_MASTER_ROOT_HANDLE, NULL, METADATA_PERMISSION_READ, DEFAULT_SAVE_TIMEOUT, &mdhRoot); } if (FAILED(hresReturn)) { if ((dwMDFlags & MD_BACKUP_FORCE_BACKUP) != 0) { hresWarning = MD_WARNING_SAVE_FAILED; hresReturn = ERROR_SUCCESS; dwMDFlags &= ~(MD_BACKUP_FORCE_BACKUP | MD_BACKUP_SAVE_FIRST); } } if (SUCCEEDED(hresReturn)) { // // call metadata com api // if( !pszPasswd ) { hresReturn = m_pMdObject->ComMDBackupW(mdhRoot, pszMDBackupLocation, dwMDVersion, dwMDFlags); } else { hresReturn = m_pMdObject->ComMDBackupWithPasswdW(mdhRoot, pszMDBackupLocation, dwMDVersion, dwMDFlags, pszPasswd); } if ((dwMDFlags & MD_BACKUP_SAVE_FIRST) != 0) { m_pMdObject->ComMDCloseMetaObject(mdhRoot); } } if (hresReturn == ERROR_SUCCESS) { hresReturn = hresWarning; } } return hresReturn; } #define MD_DEFAULT_BACKUP_LOCATION_W L"MDBackUp" HRESULT CADMCOMW::RestoreHelper( LPCWSTR pszMDBackupLocation, DWORD dwMDVersion, DWORD dwMDMinorVersion, LPCWSTR pszPasswd, DWORD dwMDFlags, DWORD dwRestoreType) // RESTORE_HISTORY or RESTORE_BACKUP { HRESULT hr = S_OK; DWORD dwError; BOOL fRet; BUFFER bufDependentServices; DWORD cServices = 0; WCHAR pszEnumLocation[MD_BACKUP_MAX_LEN] = {0}; DWORD dwEnumVersion; DWORD dwEnumMinorVersion; FILETIME ftEnumTime; DWORD i; DWORD dwEnableHistory = FALSE; DWORD dwEnableEWR = FALSE; BOOL fEnableAclCache = FALSE; // This should be called only internally, so passing wrong restore type is a bug DBG_ASSERT( dwRestoreType == RESTORE_HISTORY || dwRestoreType == RESTORE_BACKUP ); // Check args if ( ( dwRestoreType != RESTORE_HISTORY && dwRestoreType != RESTORE_BACKUP ) || ( dwRestoreType == RESTORE_BACKUP && pszMDBackupLocation == NULL ) || ( pszMDBackupLocation && wcslen(pszMDBackupLocation) >= MD_BACKUP_MAX_LEN ) ) { hr = E_INVALIDARG; goto exit; } // Check access fRet = AdminAclAccessCheck( m_pMdObject, (LPVOID)this, METADATA_MASTER_ROOT_HANDLE, L"", MD_ADMIN_ACL, METADATA_PERMISSION_WRITE, &g_ohMasterRootHandle ); if ( !fRet ) { dwError = GetLastError(); DBGPRINTF(( DBG_CONTEXT, ( ( dwRestoreType == RESTORE_HISTORY ) ? "[CADMCOMW::RestoreHistory] AdminAclAccessCheck failed, error %lx\n" : "[CADMCOMW::Restore] AdminAclAccessCheck failed, error %lx\n" ), dwError )); hr = HRESULT_FROM_WIN32( dwError ); goto exit; } // Find the requested backup/history for ( i = 0; SUCCEEDED( hr ); i++ ) { if ( dwRestoreType == RESTORE_HISTORY ) { if( pszMDBackupLocation != NULL ) { wcscpy( pszEnumLocation, pszMDBackupLocation ); } else { *pszEnumLocation = L'\0'; } hr = m_pMdObject->ComMDEnumHistoryW( pszEnumLocation, &dwEnumVersion, &dwEnumMinorVersion, &ftEnumTime, i); if ( FAILED( hr ) ) { break; } if( dwMDFlags & MD_HISTORY_LATEST ) { break; } else { if ( ( dwEnumVersion == dwMDVersion ) && ( dwEnumMinorVersion == dwMDMinorVersion ) ) { break; } } } else { if( pszMDBackupLocation != NULL ) { wcscpy( pszEnumLocation, pszMDBackupLocation ); } else { wcscpy( pszEnumLocation, MD_DEFAULT_BACKUP_LOCATION_W ); } hr = m_pMdObject->ComMDEnumBackupsW( pszEnumLocation, &dwEnumVersion, &ftEnumTime, i); if ( FAILED( hr ) ) { break; } if ( ( dwEnumVersion == dwMDVersion ) || ( dwMDVersion == MD_BACKUP_HIGHEST_VERSION ) ) { break; } } } // If we failed to find the requested backup/history if ( FAILED( hr ) ) { // If asked for an version that doesn't exist // adjust the error code if ( hr == HRESULT_FROM_WIN32( ERROR_NO_MORE_ITEMS ) ) { if( dwRestoreType == RESTORE_HISTORY ) { if( dwMDFlags & MD_HISTORY_LATEST ) { hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ); } else { hr = MD_ERROR_INVALID_VERSION; } } else { hr = E_INVALIDARG; } } goto exit; } // // Looks like a valid metabase // // Disable History (and EWR) to prevent the dependent services creating // history and deleting currently valid history files during their stoppping. // Keep the current state of EnableHistory and EnableEWR, so we can restore // them if restore fails. hr = DisableHistory(&dwEnableHistory, &dwEnableEWR); if ( FAILED( hr ) ) { goto exit; } // Stop all dependent services, keeping their list so later we can start them again hr = EnumAndStopDependentServices( &cServices, &bufDependentServices ); if ( FAILED( hr ) ) { goto exit; } // Disable the Acl cache, since we are going to re-load the whole metabase AdminAclDisableAclCache(); // Remember to re-enable it fEnableAclCache = TRUE; // Discard everything from the Acl caches AdminAclFlushCache(); // Finally call the CMDCOM method if( dwRestoreType == RESTORE_HISTORY ) { hr = m_pMdObject->ComMDRestoreHistoryW( pszMDBackupLocation, dwMDVersion, dwMDMinorVersion, dwMDFlags ); } else { if( !pszPasswd ) { hr = m_pMdObject->ComMDRestoreW( pszMDBackupLocation, dwMDVersion, dwMDFlags ); } else { hr = m_pMdObject->ComMDRestoreWithPasswdW( pszMDBackupLocation, dwMDVersion, dwMDFlags, pszPasswd ); } } if ( FAILED( hr ) ) { goto exit; } // // Issue TaylorW 4/10/2001 // // After the restore, notify clients, as data has changed // and all handles have become invalid // // Windows Bug 82423 // // If restore succeed the values of the EnableHistory and EnableEWR are // set as from the backup/history file, so we don't restore them. dwEnableHistory = FALSE; dwEnableEWR = FALSE; exit: // If something went wrong after we got the EnableHistoy and EnableEWR and // at least one of them was not FALSE set them back if ( dwEnableHistory || dwEnableEWR ) { SetHistoryAndEWR( dwEnableHistory, dwEnableEWR ); } // If we disable the acl cache if ( fEnableAclCache ) { // Re-enable it AdminAclEnableAclCache(); } // If we stopped any services if ( cServices ) { // Enable them back StartDependentServices( cServices, (ENUM_SERVICE_STATUS*)bufDependentServices.QueryPtr() ); } return hr; } HRESULT CADMCOMW::EnumAndStopDependentServices( DWORD *pcServices, BUFFER *pbufDependentServices) { // Locals HRESULT hr = S_OK; DWORD dwError; BOOL fRet; SC_HANDLE schSCM = NULL; SC_HANDLE schIISADMIN = NULL; SC_HANDLE schDependent = NULL; SERVICE_STATUS ssDependent; DWORD dwBytesNeeded; ENUM_SERVICE_STATUS *pessDependentServices = NULL; DWORD cServices = 0; DWORD i; // Check args DBG_ASSERT( ( pcServices != NULL ) && ( pbufDependentServices != NULL ) ); if ( ( pcServices == NULL ) || ( pbufDependentServices == NULL ) ) { hr = E_INVALIDARG; goto exit; } // Init *pcServices = 0; // Open SCM schSCM = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS ); if ( schSCM == NULL ) { dwError = GetLastError(); hr = HRESULT_FROM_WIN32( dwError ); goto exit; } // Open IISADMIN schIISADMIN = OpenService( schSCM, "IISADMIN", STANDARD_RIGHTS_REQUIRED | SERVICE_ENUMERATE_DEPENDENTS ); if ( schIISADMIN == NULL ) { dwError = GetLastError(); hr = HRESULT_FROM_WIN32( dwError ); goto exit; } pessDependentServices = (ENUM_SERVICE_STATUS*)pbufDependentServices->QueryPtr(); DBG_ASSERT( pessDependentServices ); fRet = EnumDependentServices( schIISADMIN, SERVICE_ACTIVE, pessDependentServices, pbufDependentServices->QuerySize(), &dwBytesNeeded, &cServices ); // The buffer is too small? if ( !fRet && ( GetLastError() == ERROR_MORE_DATA ) ) { // Resize the buffer fRet = pbufDependentServices->Resize( dwBytesNeeded ); if ( !fRet ) { hr = E_OUTOFMEMORY; goto exit; } // Retry the call pessDependentServices = (ENUM_SERVICE_STATUS*)pbufDependentServices->QueryPtr(); DBG_ASSERT( pessDependentServices ); fRet = EnumDependentServices( schIISADMIN, SERVICE_ACTIVE, pessDependentServices, dwBytesNeeded, &dwBytesNeeded, &cServices ); } if ( !fRet ) { dwError = GetLastError(); hr = HRESULT_FROM_WIN32( dwError ); goto exit; } // // Open handles and send service control stop command // for ( i = 0; i < cServices; i++ ) { if ( ( pessDependentServices[i].ServiceStatus.dwCurrentState == SERVICE_STOP_PENDING ) || ( pessDependentServices[i].ServiceStatus.dwCurrentState == SERVICE_STOPPED ) ) { continue; } schDependent = OpenService( schSCM, pessDependentServices[i].lpServiceName, SERVICE_ALL_ACCESS ); if ( schDependent == NULL ) { continue; } //Stop Service ControlService( schDependent, SERVICE_CONTROL_STOP, &ssDependent ); WaitForServiceStatus( schDependent, SERVICE_STOPPED ); CloseServiceHandle( schDependent ); schDependent = NULL; } // Return *pcServices = cServices; exit: if ( schSCM != NULL ) { CloseServiceHandle( schSCM ); schSCM = NULL; } if ( schIISADMIN != NULL ) { CloseServiceHandle( schIISADMIN ); schIISADMIN = NULL; } if ( schDependent != NULL ) { CloseServiceHandle( schDependent ); schDependent = NULL; } return hr; } HRESULT CADMCOMW::StartDependentServices( DWORD cServices, ENUM_SERVICE_STATUS *pessDependentServices) { // Locals HRESULT hr = S_OK; DWORD dwError; DWORD i = 0; SC_HANDLE schSCM = NULL; SC_HANDLE schDependent = NULL; // Check agrs if ( cServices == 0 ) { // Nop goto exit; } DBG_ASSERT( pessDependentServices != NULL ); if ( pessDependentServices == NULL ) { hr = E_INVALIDARG; goto exit; } // Open SCM schSCM = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS ); if ( schSCM == NULL ) { dwError = GetLastError(); hr = HRESULT_FROM_WIN32( dwError ); goto exit; } // // Open handles and start services // Use reverse order, since EnumServices orders // list by dependencies // for ( i = 0; i < cServices; i++ ) { if ( ( pessDependentServices[cServices-1-i].ServiceStatus.dwCurrentState == SERVICE_STOP_PENDING ) || ( pessDependentServices[cServices-1-i].ServiceStatus.dwCurrentState == SERVICE_STOPPED ) ) { continue; } schDependent = OpenService( schSCM, pessDependentServices[cServices-1-i].lpServiceName, SERVICE_ALL_ACCESS ); if ( schDependent == NULL ) { continue; } //Start Service StartService( schDependent, 0, NULL ); WaitForServiceStatus( schDependent, SERVICE_RUNNING ); CloseServiceHandle( schDependent ); schDependent = NULL; } exit: if ( schSCM != NULL ) { CloseServiceHandle( schSCM ); schSCM = NULL; } if ( schDependent != NULL ) { CloseServiceHandle( schDependent ); schDependent = NULL; } return hr; } HRESULT CADMCOMW::SetHistoryAndEWR( DWORD dwEnableHistory, DWORD dwEnableEWR) { // Locals HRESULT hr = S_OK; METADATA_HANDLE mdhRoot = METADATA_MASTER_ROOT_HANDLE; METADATA_RECORD mdrHistory = { MD_ROOT_ENABLE_HISTORY, METADATA_NO_ATTRIBUTES, IIS_MD_UT_SERVER, DWORD_METADATA, sizeof(DWORD), (BYTE*)&dwEnableHistory, 0 }; METADATA_RECORD mdrEWR = { MD_ROOT_ENABLE_EDIT_WHILE_RUNNING, METADATA_NO_ATTRIBUTES, IIS_MD_UT_SERVER, DWORD_METADATA, sizeof(DWORD), (BYTE*)&dwEnableEWR, 0 }; // Open the the root for writting hr = m_pMdObject->ComMDOpenMetaObjectW( METADATA_MASTER_ROOT_HANDLE, L"/LM", METADATA_PERMISSION_WRITE, DEFAULT_SAVE_TIMEOUT, &mdhRoot ); if ( FAILED( hr ) ) { goto exit; } // Set EWR 1st hr = m_pMdObject->ComMDSetMetaDataW( mdhRoot, L"", &mdrEWR ); if ( FAILED( hr ) ) { goto exit; } // Set History after EWR hr = m_pMdObject->ComMDSetMetaDataW( mdhRoot, L"", &mdrHistory ); if ( FAILED( hr ) ) { goto exit; } // Close the write handle to allow saving the metabase DBG_ASSERT( mdhRoot != METADATA_MASTER_ROOT_HANDLE ); m_pMdObject->ComMDCloseMetaObject( mdhRoot ); mdhRoot = METADATA_MASTER_ROOT_HANDLE; // Save the metabase hr = m_pMdObject->ComMDSaveData(); if ( FAILED( hr ) ) { goto exit; } exit: if ( mdhRoot != METADATA_MASTER_ROOT_HANDLE ) { m_pMdObject->ComMDCloseMetaObject( mdhRoot ); mdhRoot = METADATA_MASTER_ROOT_HANDLE; } return hr; } HRESULT CADMCOMW::DisableHistory( DWORD *pdwEnableHistoryOld, DWORD *pdwEnableEWROld) { // Locals HRESULT hr = S_OK; DWORD dwEnableHistory = FALSE; DWORD dwEnableEWR = FALSE; DWORD dwT; METADATA_HANDLE mdhRoot = METADATA_MASTER_ROOT_HANDLE; METADATA_RECORD mdrHistory = { MD_ROOT_ENABLE_HISTORY, METADATA_NO_ATTRIBUTES, IIS_MD_UT_SERVER, DWORD_METADATA, sizeof(DWORD), (BYTE*)&dwEnableHistory, 0 }; METADATA_RECORD mdrEWR = { MD_ROOT_ENABLE_EDIT_WHILE_RUNNING, METADATA_NO_ATTRIBUTES, IIS_MD_UT_SERVER, DWORD_METADATA, sizeof(DWORD), (BYTE*)&dwEnableEWR, 0 }; // Check agrs if ( ( pdwEnableHistoryOld == NULL ) || ( pdwEnableEWROld == NULL ) ) { hr = E_INVALIDARG; goto exit; } // Intialize *pdwEnableHistoryOld = FALSE; *pdwEnableEWROld = FALSE; hr = m_pMdObject->ComMDOpenMetaObjectW( METADATA_MASTER_ROOT_HANDLE, L"/LM", METADATA_PERMISSION_READ, DEFAULT_SAVE_TIMEOUT, &mdhRoot ); if ( hr == HRESULT_FROM_WIN32( ERROR_PATH_NOT_FOUND ) ) { // If /LM is not there don't fail. // Treat as if EWR and History are not enabled (*pdwEnableHistoryOld and *pdwEnableEWROld are initialized to FALSE) // This can happen only during sysprep. hr = S_OK; goto exit; } if ( FAILED( hr ) ) { goto exit; } // Get EWR hr = m_pMdObject->ComMDGetMetaDataW( mdhRoot, L"", &mdrEWR, &dwT ); if ( hr == MD_ERROR_DATA_NOT_FOUND ) { // If the property is not there don't fail. // Treat as if EWR is not enabled (dwEnableEWR is initialized to FALSE) hr = S_OK; } if ( FAILED( hr ) ) { goto exit; } // Get History hr = m_pMdObject->ComMDGetMetaDataW( mdhRoot, L"", &mdrHistory, &dwT ); if ( hr == MD_ERROR_DATA_NOT_FOUND ) { // If the property is not there don't fail. // Treat as if History is not enabled (dwEnableHistory is initialized to FALSE) hr = S_OK; } if ( FAILED( hr ) ) { goto exit; } // If both are already disabled if ( !dwEnableHistory && !dwEnableEWR ) { // No need to change anything in the metabase. // No need to change the out parameters, // because they are already initialized to FALSE // Just exit. goto exit; } // Close the read handle to allow writing in SetHistoryAndEWR DBG_ASSERT( mdhRoot != METADATA_MASTER_ROOT_HANDLE ); m_pMdObject->ComMDCloseMetaObject( mdhRoot ); mdhRoot = METADATA_MASTER_ROOT_HANDLE; // Disable history and ewr // They have to be changed in pairs, because EWR turns history on hr = SetHistoryAndEWR( FALSE, FALSE ); if ( FAILED( hr ) ) { goto exit; } // Return *pdwEnableHistoryOld = dwEnableHistory; *pdwEnableEWROld = dwEnableEWR; exit: if ( mdhRoot != METADATA_MASTER_ROOT_HANDLE ) { m_pMdObject->ComMDCloseMetaObject( mdhRoot ); mdhRoot = METADATA_MASTER_ROOT_HANDLE; } return hr; } HRESULT STDMETHODCALLTYPE CADMCOMW::Backup( LPCWSTR pszMDBackupLocation, DWORD dwMDVersion, DWORD dwMDFlags) { return BackupHelper( pszMDBackupLocation, dwMDVersion, dwMDFlags ); } HRESULT STDMETHODCALLTYPE CADMCOMW::BackupWithPasswd( LPCWSTR pszMDBackupLocation, DWORD dwMDVersion, DWORD dwMDFlags, LPCWSTR pszPasswd) { return BackupHelper( pszMDBackupLocation, dwMDVersion, dwMDFlags, pszPasswd ); } HRESULT STDMETHODCALLTYPE CADMCOMW::Restore( LPCWSTR pszMDBackupLocation, DWORD dwMDVersion, DWORD dwMDFlags) { return RestoreHelper( pszMDBackupLocation, dwMDVersion, 0, NULL, dwMDFlags, RESTORE_BACKUP ); } HRESULT STDMETHODCALLTYPE CADMCOMW::RestoreWithPasswd( LPCWSTR pszMDBackupLocation, DWORD dwMDVersion, DWORD dwMDFlags, LPCWSTR pszPasswd) { return RestoreHelper( pszMDBackupLocation, dwMDVersion, 0, pszPasswd, dwMDFlags, RESTORE_BACKUP ); } HRESULT STDMETHODCALLTYPE CADMCOMW::EnumBackups( LPWSTR pszMDBackupLocation, DWORD *pdwMDVersion, PFILETIME pftMDBackupTime, DWORD dwMDEnumIndex) { HRESULT hresReturn = S_OK; if ( !AdminAclAccessCheck( m_pMdObject, (LPVOID)this, METADATA_MASTER_ROOT_HANDLE, L"", MD_ADMIN_ACL, METADATA_PERMISSION_WRITE, &g_ohMasterRootHandle ) ) { DBGPRINTF(( DBG_CONTEXT, "[CADMCOMW::EnumBackups AdminAclAccessCheck failed, error %lx\n", GetLastError() )); hresReturn = RETURNCODETOHRESULT( GetLastError() ); } else { // // call metadata com api // hresReturn = m_pMdObject->ComMDEnumBackupsW(pszMDBackupLocation, pdwMDVersion, pftMDBackupTime, dwMDEnumIndex); } return hresReturn; } HRESULT STDMETHODCALLTYPE CADMCOMW::DeleteBackup( LPCWSTR pszMDBackupLocation, DWORD dwMDVersion) { HRESULT hresReturn = S_OK; if ( !AdminAclAccessCheck( m_pMdObject, (LPVOID)this, METADATA_MASTER_ROOT_HANDLE, L"", MD_ADMIN_ACL, METADATA_PERMISSION_WRITE, &g_ohMasterRootHandle ) ) { DBGPRINTF(( DBG_CONTEXT, "[CADMCOMW::DeleteBackup] AdminAclAccessCheck failed, error %lx\n", GetLastError() )); hresReturn = RETURNCODETOHRESULT( GetLastError() ); } else { // // call metadata com api // hresReturn = m_pMdObject->ComMDDeleteBackupW(pszMDBackupLocation, dwMDVersion); } return hresReturn; } HRESULT STDMETHODCALLTYPE CADMCOMW::Export( LPCWSTR i_wszPasswd, LPCWSTR i_wszFileName, LPCWSTR i_wszSourcePath, DWORD i_dwMDFlags) { HRESULT hr = S_OK; DWORD dwError; METADATA_HANDLE mdh = METADATA_MASTER_ROOT_HANDLE; METADATA_HANDLE mdhActual = METADATA_MASTER_ROOT_HANDLE; COpenHandle* pohActual = NULL; STRAU strFileName; // // parameter validation // if ( ( i_wszFileName == NULL ) || ( *i_wszFileName == L'\0' ) || ( i_wszSourcePath == NULL ) ) { hr = E_INVALIDARG; goto exit; } hr = CoImpersonateClient(); if ( FAILED( hr ) ) { goto exit; } // IVANPASH 598894 (SCR) // Restrict the access to Export only to administrators if ( !AdminAclAccessCheck( m_pMdObject, (LPVOID)this, METADATA_MASTER_ROOT_HANDLE, L"", MD_ADMIN_ACL, METADATA_PERMISSION_WRITE, &g_ohMasterRootHandle ) ) { dwError = GetLastError(); DBGPRINTF(( DBG_CONTEXT, "[CADMCOMW::Export] AdminAclAccessCheck failed, error %lx\n", dwError )); hr = RETURNCODETOHRESULT( dwError ); goto exit; } hr = OpenKeyHelper( METADATA_MASTER_ROOT_HANDLE, i_wszSourcePath, METADATA_PERMISSION_READ, DEFAULT_SAVE_TIMEOUT, &mdh); if ( FAILED( hr ) ) { goto exit; } // // pohActual refCount = 2 after Lookup. // dwError = Lookup(mdh, &mdhActual, &pohActual); if ( dwError != ERROR_SUCCESS ) { hr = RETURNCODETOHRESULT( dwError ); DBG_ASSERT( pohActual == NULL ); // // Yes, an open key does not get closed, but Lookup really should // not fail if mdh is a valid key. // Also CloseKey would do exactly the same Lookup call and will fail too. // goto exit; } // // Move refCount down to 1. // pohActual->Release(this); if( !AdminAclAccessCheck( m_pMdObject, (LPVOID)this, mdh, L"", MD_ADMIN_ACL, METADATA_PERMISSION_WRITE, pohActual ) ) { dwError = GetLastError(); DBGPRINTF(( DBG_CONTEXT, "[CADMCOMW::Export] AdminAclAccessCheck failed, error %lx\n", dwError )); hr = RETURNCODETOHRESULT( dwError ); goto exit; } // IVANPASH 598894 (SCR) // Prepend the file name with \\?\ (or \\?\UNC\) to prevent canonicalization hr = MakePathCanonicalizationProof( i_wszFileName, FALSE, &strFileName ); if ( FAILED( hr ) ) { goto exit; } // Don't use i_wszFileName any more i_wszFileName = NULL; // call metadata com api hr = m_pMdObject->ComMDExportW( mdhActual, i_wszPasswd, strFileName.QueryStrW(), i_wszSourcePath, i_dwMDFlags); if ( FAILED( hr ) ) { goto exit; } exit: // At this moment mdh can actually contain a valid CMDCOMW handle. // It is not explicitely closed, because the code bellow actually does // the same as CloseKey(mdh). if ( pohActual ) { // close key if ( mdhActual != METADATA_MASTER_ROOT_HANDLE ) { // call metadata com api m_pMdObject->ComMDCloseMetaObject( mdhActual ); } // Remove node from handle table pohActual->Release(this); pohActual=NULL; } return hr; } HRESULT STDMETHODCALLTYPE CADMCOMW::Import( LPCWSTR i_wszPasswd, LPCWSTR i_wszFileName, LPCWSTR i_wszSourcePath, LPCWSTR i_wszDestPath, DWORD i_dwMDFlags) /*++ Synopsis: Arguments: [i_wszPasswd] - [i_wszFileName] - [i_wszSourcePath] - Absolute metabase path [i_wszDestPath] - Absolute metabase path [i_dwMDFlags] - Return Value: --*/ { HRESULT hr = S_OK; DWORD dwError; METADATA_HANDLE mdh = 0; METADATA_HANDLE mdhActual = 0; COpenHandle* pohActual = NULL; LPWSTR wszDeepest = NULL; LONG cchDeepest = 0; LPWSTR wszEnd = NULL; LONG idx = 0; WCHAR wszKeyType[METADATA_MAX_STRING_LEN] = {0}; DWORD dwRequiredSize = 0; METADATA_RECORD mr = { MD_KEY_TYPE, METADATA_NO_ATTRIBUTES, IIS_MD_UT_SERVER, STRING_METADATA, METADATA_MAX_STRING_LEN*sizeof(WCHAR), (LPBYTE)wszKeyType, 0 }; STRAU strFileName; // // parameter validation // if ( ( i_wszFileName == NULL ) || ( *i_wszFileName == L'\0' ) || ( i_wszSourcePath == NULL ) || ( i_wszDestPath == NULL ) ) { hr = E_INVALIDARG; goto exit; } if (i_wszPasswd == NULL) { i_wszPasswd = L""; } hr = CoImpersonateClient(); if ( FAILED( hr ) ) { goto exit; } // IVANPASH 598894 (SCR) // Restrict the access to Import only to administrators if ( !AdminAclAccessCheck( m_pMdObject, (LPVOID)this, METADATA_MASTER_ROOT_HANDLE, L"", MD_ADMIN_ACL, METADATA_PERMISSION_WRITE, &g_ohMasterRootHandle ) ) { dwError = GetLastError(); DBGPRINTF(( DBG_CONTEXT, "[CADMCOMW::Import] AdminAclAccessCheck failed, error %lx\n", dwError )); hr = RETURNCODETOHRESULT( dwError ); goto exit; } // // Copy i_wszDestPath to wszDeepest // Remove trailing slashes // cchDeepest = (LONG)wcslen(i_wszDestPath); wszDeepest = new WCHAR[1+cchDeepest]; if(!wszDeepest) { hr = RETURNCODETOHRESULT(ERROR_NOT_ENOUGH_MEMORY); goto exit; } memcpy(wszDeepest, i_wszDestPath, sizeof(WCHAR)*(cchDeepest+1)); while( cchDeepest > 0 && IS_MD_PATH_DELIM(wszDeepest[cchDeepest-1]) ) { cchDeepest--; } // // Open the deepest level key possible // wszEnd = wszDeepest + cchDeepest; for(idx = cchDeepest; idx >= 0; idx--) { if(idx == 0 || idx == cchDeepest || IS_MD_PATH_DELIM(*wszEnd)) { *wszEnd = L'\0'; hr = OpenKeyHelper( METADATA_MASTER_ROOT_HANDLE, wszDeepest, METADATA_PERMISSION_WRITE | METADATA_PERMISSION_READ, DEFAULT_SAVE_TIMEOUT, &mdh); if( FAILED(hr) && hr != RETURNCODETOHRESULT(ERROR_PATH_NOT_FOUND) ) { goto exit; } else if(SUCCEEDED(hr)) { break; } } wszEnd--; } if(FAILED(hr)) { goto exit; } // // If we are here, we now have an Open metabase handle // dwError = Lookup(mdh, &mdhActual, &pohActual); hr = RETURNCODETOHRESULT(dwError); if(FAILED(hr)) { // // Yes, an open key does not get closed, but Lookup really should // not fail if mdh is a valid key. // Also CloseKey would do exactly the same Lookup call and will fail too. // goto exit; } pohActual->Release(this); // Decrements refcount from 2 to 1. if( !AdminAclAccessCheck( m_pMdObject, (LPVOID)this, mdh, L"", MD_ADMIN_ACL, METADATA_PERMISSION_WRITE | METADATA_PERMISSION_READ, pohActual ) ) { DBGPRINTF(( DBG_CONTEXT, "[CADMCOMW::Import] AdminAclAccessCheck failed, error %lx\n", GetLastError() )); hr = RETURNCODETOHRESULT( GetLastError() ); goto exit; } // // Get the keytype // If the node does not exist, or node exists but keytype doesn't, we // will not set wszKeytype and hence ComMDImport will not attempt to match // the source and dest keytype // hr = m_pMdObject->ComMDGetMetaDataW( mdhActual, i_wszDestPath+idx, &mr, &dwRequiredSize); if(hr == RETURNCODETOHRESULT(ERROR_PATH_NOT_FOUND)) { hr = S_OK; } else if(hr == MD_ERROR_DATA_NOT_FOUND) { hr = S_OK; } if(FAILED(hr)) { DBGPRINTF((DBG_CONTEXT, "Error trying to retrieve keytype for %ws\n", i_wszDestPath+idx)); goto exit; } // IVANPASH 598894 (SCR) // Prepend the file name with \\?\ (or \\?\UNC\) to prevent canonicalization hr = MakePathCanonicalizationProof( i_wszFileName, TRUE, &strFileName ); if ( FAILED( hr ) ) { goto exit; } // Don't use i_wszFileName any more i_wszFileName = NULL; // // Call Import // hr = m_pMdObject->ComMDImportW( mdhActual, i_wszDestPath+idx, wszKeyType, i_wszPasswd, strFileName.QueryStrW(), i_wszSourcePath, i_dwMDFlags); if(FAILED(hr)) { goto exit; } exit: if(pohActual != NULL) { // // Close Key // m_pMdObject->ComMDCloseMetaObject( mdhActual ); pohActual->Release(this); pohActual = NULL; } if ( wszDeepest ) { delete [] wszDeepest; wszDeepest = NULL; } return hr; } HRESULT STDMETHODCALLTYPE CADMCOMW::RestoreHistory( LPCWSTR pszMDHistoryLocation, DWORD dwMDMajorVersion, DWORD dwMDMinorVersion, DWORD dwMDFlags) { HRESULT hresReturn = S_OK; if( (dwMDFlags & ~MD_HISTORY_LATEST) != 0 && dwMDFlags != 0 ) { return HRESULT_FROM_WIN32(ERROR_INVALID_FLAGS); } if( (dwMDFlags & MD_HISTORY_LATEST) && (dwMDMajorVersion != 0 || dwMDMinorVersion != 0) ) { return E_INVALIDARG; } // // parameter validation done in here. // hresReturn = RestoreHelper(pszMDHistoryLocation, dwMDMajorVersion, dwMDMinorVersion, NULL, dwMDFlags, RESTORE_HISTORY); return hresReturn; } HRESULT STDMETHODCALLTYPE CADMCOMW::EnumHistory( LPWSTR io_wszMDHistoryLocation, DWORD *o_pdwMDMajorVersion, DWORD *o_pdwMDMinorVersion, PFILETIME o_pftMDHistoryTime, DWORD i_dwMDEnumIndex) { HRESULT hresReturn = S_OK; if (io_wszMDHistoryLocation == NULL || o_pdwMDMajorVersion == NULL || o_pdwMDMinorVersion == NULL || o_pftMDHistoryTime == NULL) { return E_INVALIDARG; } if ( !AdminAclAccessCheck( m_pMdObject, (LPVOID)this, METADATA_MASTER_ROOT_HANDLE, L"", MD_ADMIN_ACL, METADATA_PERMISSION_WRITE, &g_ohMasterRootHandle ) ) { DBGPRINTF(( DBG_CONTEXT, "[CADMCOMW::EnumHistory AdminAclAccessCheck failed, error %lx\n", GetLastError() )); hresReturn = RETURNCODETOHRESULT( GetLastError() ); } else { // // call metadata com api // hresReturn = m_pMdObject->ComMDEnumHistoryW(io_wszMDHistoryLocation, o_pdwMDMajorVersion, o_pdwMDMinorVersion, o_pftMDHistoryTime, i_dwMDEnumIndex); } return hresReturn; } HRESULT STDMETHODCALLTYPE CADMCOMW::GetChildPaths( METADATA_HANDLE hMDHandle, LPCWSTR pszMDPath, DWORD cchMDBufferSize, WCHAR *pszBuffer, DWORD *pcchMDRequiredBufferSize) /*++ Routine Description: Retrieves all child keys of a given path from a given handle Arguments: hMDHandle - open handle pszMDPath - path of the meta object with which this data is associated cchMDBufferSize - sizeof buffer passed in, in wchars pszBuffer - buffer, allocated by caller, that result is placed into pcchMDRequiredBufferSize - required size, filled in only if buffer is insufficient Return Value: Status. s_ok on success. --*/ { HRESULT hr = S_OK; METADATA_HANDLE hActualHandle; BOOL fSecure; // // lookup and access check // hr = LookupAndAccessCheck(hMDHandle, &hActualHandle, pszMDPath, 0, METADATA_PERMISSION_READ, &fSecure); if (FAILED(hr)) { goto done; } DBG_ASSERT( NULL != m_pMdObject3 ); hr = m_pMdObject3->ComMDGetChildPathsW(hActualHandle, pszMDPath, cchMDBufferSize, pszBuffer, pcchMDRequiredBufferSize); if (FAILED(hr)) { goto done; } hr = S_OK; done: return hr; } HRESULT CADMCOMW::AddKeyHelper( IN METADATA_HANDLE hMDHandle, IN LPCWSTR pszMDPath) /*++ Routine Description: Add meta object and adds it to the list of child objects for the object specified by Path. Arguments: hMDHandle - open handle pszMDPath - path of the object to be added Return Value: Status. --*/ { HRESULT hresReturn = S_OK; METADATA_HANDLE hActualHandle; if ((pszMDPath == NULL) || (*pszMDPath == (WCHAR)'\0')) { hresReturn = E_INVALIDARG; } else { // // lookup and access check // hresReturn = LookupAndAccessCheck(hMDHandle, &hActualHandle, pszMDPath, 0, METADATA_PERMISSION_WRITE); if (SUCCEEDED(hresReturn)) { // // call metadata com api // hresReturn = m_pMdObject->ComMDAddMetaObjectW( hActualHandle, pszMDPath ); } } return hresReturn; } HRESULT CADMCOMW::OpenKeyHelper( METADATA_HANDLE hMDHandle, LPCWSTR pszMDPath, DWORD dwMDAccessRequested, DWORD dwMDTimeOut, PMETADATA_HANDLE phMDNewHandle) /*++ Routine Description: Opens a meta object for read and/or write access. - This is used by Export. Arguments: hMDHandle - open handle pszMDPath - path of the object to be opened dwMDAccessRequested - permissions requested dwMDTimeOut - time to block waiting for open to succeed, in miliseconds. phMDNewHandle - handle to be passed to other MD routines Return Value: Status. --*/ { HRESULT hr = S_OK; DWORD dwError; BOOL fRet; METADATA_HANDLE hNewHandle = METADATA_MASTER_ROOT_HANDLE; METADATA_HANDLE hActualHandle = METADATA_MASTER_ROOT_HANDLE; COpenHandle *pohParent = NULL; // Check args if ( ( phMDNewHandle == NULL ) || ( ( dwMDAccessRequested & ( METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE ) ) == 0 ) || ( ( dwMDAccessRequested & ~( METADATA_PERMISSION_READ | METADATA_PERMISSION_WRITE ) ) != 0 ) || ( ( pszMDPath != NULL ) && ( wcsstr( pszMDPath, L"" ) != NULL ) ) ) { // // used to be magic name for the Name Space Extension access // It was removed for IIS6 but to prevent legacy applications // to write obsolete and unusable data to metabase deny access to path // containing // hr = E_INVALIDARG; goto exit; } // // Map Admin Handle to Actual Handle // // // This Addrefs pohParent, which makes sure it doesn't do away // pohParent is needed by AddNode // dwError = Lookup( hMDHandle, &hActualHandle, &pohParent ); if( dwError != ERROR_SUCCESS ) { hr = HRESULT_FROM_WIN32( dwError ); goto exit; } // Check access fRet = AdminAclAccessCheck( m_pMdObject, this, hMDHandle, pszMDPath, 0, dwMDAccessRequested, pohParent ); if ( !fRet ) { dwError = GetLastError(); if ( dwError != ERROR_ACCESS_DENIED ) { hr = HRESULT_FROM_WIN32( dwError ); goto exit; } // If failed with access denied for writing or writing+reading if ( ( dwMDAccessRequested & METADATA_PERMISSION_WRITE ) != 0 ) { // Retry checking access for writing MD_ADMIN_ACL // AdminAclAccessCheck will try 1st to check MD_ACR_RESTRICTED_WRITE and than for MD_ACR_WRITE_DAC fRet = AdminAclAccessCheck( m_pMdObject, this, hMDHandle, pszMDPath, MD_ADMIN_ACL, dwMDAccessRequested, pohParent ); } else { // If failed with access denied for reading (and the write bit is not set) if ( dwMDAccessRequested == METADATA_PERMISSION_READ ) { // Retry checking access for enum only // AdminAclAccessCheck internally already check for both MD_ACR_UNSECURE_PROPS_READ and MD_ACR_READ fRet = AdminAclAccessCheck( m_pMdObject, this, hMDHandle, pszMDPath, AAC_ENUM_KEYS, dwMDAccessRequested, pohParent ); } } } if ( !fRet ) { dwError = GetLastError(); hr = HRESULT_FROM_WIN32( dwError ); goto exit; } // // call metadata com api // hr = m_pMdObject->ComMDOpenMetaObjectW( hActualHandle, pszMDPath, dwMDAccessRequested, dwMDTimeOut, &hNewHandle ); if( FAILED( hr ) ) { goto exit; } // Add the opened handle to table hr = AddNode( hNewHandle, pohParent, phMDNewHandle, pszMDPath ); if( FAILED( hr ) ) { goto exit; } // Don't close the metadata handle hNewHandle = METADATA_MASTER_ROOT_HANDLE; exit: if ( hNewHandle != METADATA_MASTER_ROOT_HANDLE ) { m_pMdObject->ComMDCloseMetaObject( hNewHandle ); hNewHandle = METADATA_MASTER_ROOT_HANDLE; } if ( pohParent != NULL ) { pohParent->Release(this); pohParent = NULL; } return hr; } DWORD CADMCOMW::Lookup( IN METADATA_HANDLE hHandle, OUT METADATA_HANDLE *phActualHandle, OUT COpenHandle **ppohHandle) { HANDLE_TABLE *phtNode; DWORD dwReturn = ERROR_INVALID_HANDLE; if( hHandle == METADATA_MASTER_ROOT_HANDLE ) { *phActualHandle = g_MasterRoot.hActualHandle; if (ppohHandle != NULL) { *ppohHandle = g_MasterRoot.pohHandle; (*ppohHandle)->AddRef(); } dwReturn = ERROR_SUCCESS; } else { m_LockHandleResource.ReadLock(); for( phtNode = m_hashtab[(DWORD)hHandle % HASHSIZE]; phtNode != NULL; phtNode = phtNode->next ) { if( phtNode->hAdminHandle == hHandle ) { *phActualHandle = phtNode->hActualHandle; if (ppohHandle != NULL) { *ppohHandle = phtNode->pohHandle; (*ppohHandle)->AddRef(); } dwReturn = ERROR_SUCCESS; break; } } m_LockHandleResource.ReadUnlock(); } return dwReturn; } VOID CADMCOMW::DisableAllHandles( ) { HANDLE_TABLE *phtNode; DWORD i; // // At this point, all metadata handles should be closed because a retore // just happened. So don't need to close these handles. // // // Can't just delete them, becuase of syncronization problems // with CloseKey and Lookup. Set the hande to an invalid value // So Lookup won't use them. // m_LockHandleResource.WriteLock(); for( i = 0; i < HASHSIZE; i++ ) { for( phtNode = m_hashtab[i]; phtNode != NULL; phtNode = phtNode->next ) { phtNode->hAdminHandle = INVALID_ADMINHANDLE_VALUE; } } m_LockHandleResource.WriteUnlock(); } HRESULT CADMCOMW::LookupAndAccessCheck( IN METADATA_HANDLE hHandle, OUT METADATA_HANDLE *phActualHandle, IN LPCWSTR pszPath, IN DWORD dwId, // check for MD_ADMIN_ACL, must have special right to write them IN DWORD dwAccess, // METADATA_PERMISSION_* OUT LPBOOL pfEnableSecureAccess) { DWORD dwReturn = ERROR_SUCCESS; COpenHandle *pohParent; // // Map Admin Handle to Actual Handle // // // This Addrefs pohParent, which makes sure it doesn't go away // until AdminAclAccessCheck is done // dwReturn = Lookup( hHandle, phActualHandle, &pohParent); if (dwReturn == ERROR_SUCCESS) { if (!AdminAclAccessCheck(m_pMdObject, (LPVOID)this, hHandle, pszPath, dwId, dwAccess, pohParent, pfEnableSecureAccess)) { dwReturn = GetLastError(); } pohParent->Release(this); } return RETURNCODETOHRESULT(dwReturn); } DWORD CADMCOMW::LookupActualHandle( IN METADATA_HANDLE hHandle) { HANDLE_TABLE *phtNode; DWORD i; DWORD dwReturn = ERROR_INVALID_HANDLE; m_LockHandleResource.ReadLock(); for( i = 0; (i < HASHSIZE) && (dwReturn != ERROR_SUCCESS); i++ ) { for( phtNode = m_hashtab[i]; (phtNode != NULL) && (dwReturn != ERROR_SUCCESS); phtNode = phtNode->next ) { if( phtNode->hActualHandle == hHandle ) { dwReturn = ERROR_SUCCESS; } } } m_LockHandleResource.ReadUnlock(); return dwReturn; } HRESULT CADMCOMW::AddNode( METADATA_HANDLE hActualHandle, COpenHandle *pohParentHandle, PMETADATA_HANDLE phAdminHandle, LPCWSTR pszPath) { HRESULT hresReturn = S_OK; HANDLE_TABLE *phtNode = (HANDLE_TABLE *)LocalAlloc(LMEM_FIXED, sizeof(*phtNode)); DWORD hashVal; COpenHandle *pohHandle = new COpenHandle; if ((phtNode == NULL) || (pohHandle == NULL)) { hresReturn = E_OUTOFMEMORY; if( phtNode ) { LocalFree(phtNode); } if( pohHandle ) { delete pohHandle; } } else { m_LockHandleResource.WriteLock(); hresReturn = pohHandle->Init( m_dwHandleValue, pszPath, pohParentHandle->GetPath() ); if (FAILED(hresReturn)) { LocalFree(phtNode); delete pohHandle; } else { phtNode->pohHandle = pohHandle; phtNode->hAdminHandle = m_dwHandleValue; *phAdminHandle = m_dwHandleValue++; phtNode->hActualHandle = hActualHandle; hashVal = (phtNode->hAdminHandle) % HASHSIZE; phtNode->next = m_hashtab[hashVal]; m_hashtab[hashVal] = phtNode; } m_LockHandleResource.WriteUnlock(); } return hresReturn; } DWORD CADMCOMW::DeleteNode( METADATA_HANDLE hHandle) { HANDLE_TABLE *phtNode; HANDLE_TABLE *phtDelNode; DWORD HashValue = (DWORD)hHandle % HASHSIZE; if( hHandle == METADATA_MASTER_ROOT_HANDLE ) { return ERROR_SUCCESS; } m_LockHandleResource.WriteLock(); phtNode = m_hashtab[HashValue]; // // check single node linked list // if( phtNode->hAdminHandle == hHandle ) { m_hashtab[HashValue] = phtNode->next; delete phtNode->pohHandle; LocalFree(phtNode); } else { for( ; phtNode != NULL; phtNode = phtNode->next ) { phtDelNode = phtNode->next; if( phtDelNode != NULL ) { if( phtDelNode->hAdminHandle == hHandle ) { phtNode->next = phtDelNode->next; delete phtDelNode->pohHandle; LocalFree(phtDelNode); break; } } } } m_LockHandleResource.WriteUnlock(); return ERROR_SUCCESS; } //--------------- /*M+M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M+++M Method: CADMCOMW::NotifySinks Summary: Internal utility method of this COM object used to fire event notification calls to all listening connection sinks in the client. Args: PAPER_EVENT PaperEvent Type of notification event. SHORT nX X cordinate. Value is 0 unless event needs it. SHORT nY Y cordinate. Value is 0 unless event needs it. SHORT nInkWidth Ink Width. Value is 0 unless event needs it. SHORT crInkColor COLORREF RGB color value. Value is 0 unless event needs it. Modifies: ... Returns: HRESULT Standard OLE result code. NOERROR for success. M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M---M-M*/ // Initialize class static members NOTIFY_CONTEXT * NOTIFY_CONTEXT::s_pCurrentlyWorkingOn = NULL; CReaderWriterLock3 NOTIFY_CONTEXT::s_LockCurrentlyWorkingOn; LIST_ENTRY NOTIFY_CONTEXT::s_listEntry; CRITICAL_SECTION NOTIFY_CONTEXT::s_critSec; BOOL NOTIFY_CONTEXT::s_fInitializedCritSec = FALSE; HANDLE NOTIFY_CONTEXT::s_hShutdown = NULL; HANDLE NOTIFY_CONTEXT::s_hDataAvailable = NULL; HANDLE NOTIFY_CONTEXT::s_hThread = NULL; DWORD NOTIFY_CONTEXT::s_dwThreadId = 0; HRESULT CADMCOMW::NotifySinks( METADATA_HANDLE hMDHandle, DWORD dwMDNumElements, MD_CHANGE_OBJECT_W pcoChangeList[], BOOL bIsMainNotification) { HRESULT hr = S_OK; // if the object is terminated, or calling sinks is disabled or IISADMIN is shutting // down ignore the notification and return S_OK to the caller. if ( m_bTerminated || sm_fShutdownInProgress ) { goto done; } // // if the meta handle is for this object, return S_OK to // the caller (admin's sink). // if( bIsMainNotification && ( LookupActualHandle( hMDHandle ) == ERROR_SUCCESS ) ) { goto done; } // Any listeners registered for notifications? hr = m_ConnectionPoint.ListenersPresent(); if ( hr != S_OK ) { // We are going to ingore this notification, but return S_OK to the caller. hr = S_OK; goto done; } // Enqueue the notification, which will AddRef this. hr = NOTIFY_CONTEXT::CreateNewContext( this, hMDHandle, dwMDNumElements, pcoChangeList, bIsMainNotification ); if (FAILED(hr)) { goto done; } hr = S_OK; done: return hr; } NOTIFY_CONTEXT::NOTIFY_CONTEXT() : _dwSignature(NOTIFY_CONTEXT_SIGNATURE), _pCADMCOMW(NULL), _dwMDNumElements(0), _pcoChangeList(NULL), _bIsMainNotification(FALSE) { InitializeListHead(&_listEntry); } NOTIFY_CONTEXT::~NOTIFY_CONTEXT() { InitializeListHead(&_listEntry); _dwSignature = NOTIFY_CONTEXT_SIGNATURE_FREE; s_LockCurrentlyWorkingOn.ReadLock(); if ( this == s_pCurrentlyWorkingOn ) { s_LockCurrentlyWorkingOn.ConvertSharedToExclusive(); if ( this == s_pCurrentlyWorkingOn ) { s_pCurrentlyWorkingOn = NULL; } s_LockCurrentlyWorkingOn.WriteUnlock(); } else { s_LockCurrentlyWorkingOn.ReadUnlock(); } if (_pCADMCOMW) { _pCADMCOMW->Release(); _pCADMCOMW = NULL; } if (_pcoChangeList) { for (DWORD i = 0; i < _dwMDNumElements; i++) { delete [] _pcoChangeList[i].pszMDPath; _pcoChangeList[i].pszMDPath = NULL; _pcoChangeList[i].dwMDChangeType = 0; _pcoChangeList[i].dwMDNumDataIDs = 0; delete [] _pcoChangeList[i].pdwMDDataIDs; _pcoChangeList[i].pdwMDDataIDs = NULL; } } _dwMDNumElements = 0; delete [] _pcoChangeList; _pcoChangeList = NULL; _bIsMainNotification = FALSE; } //static HRESULT NOTIFY_CONTEXT::CreateNewContext( CADMCOMW *pCADMCOMW, METADATA_HANDLE , DWORD dwMDNumElements, MD_CHANGE_OBJECT_W *pcoChangeList, BOOL bIsMainNotification) { HRESULT hr = S_OK; BOOL fRet = FALSE; NOTIFY_CONTEXT *pContext = NULL; DBG_ASSERT( pCADMCOMW != NULL ); if ( pCADMCOMW == NULL ) { hr = E_INVALIDARG; goto done; } pContext = new NOTIFY_CONTEXT; if (NULL == pContext) { hr = E_OUTOFMEMORY; goto done; } pContext->_pCADMCOMW = pCADMCOMW; pContext->_pCADMCOMW->AddRef(); pContext->_dwMDNumElements = dwMDNumElements; pContext->_bIsMainNotification = bIsMainNotification; pContext->_pcoChangeList = new MD_CHANGE_OBJECT_W[dwMDNumElements]; if (NULL == pContext->_pcoChangeList) { hr = E_OUTOFMEMORY; goto done; } ZeroMemory(pContext->_pcoChangeList, dwMDNumElements * sizeof(MD_CHANGE_OBJECT_W)); for (DWORD i = 0; i < dwMDNumElements; i++) { DWORD dwLength = (DWORD)wcslen(pcoChangeList[i].pszMDPath); pContext->_pcoChangeList[i].pszMDPath = new WCHAR[dwLength + 1]; if (NULL == pContext->_pcoChangeList[i].pszMDPath) { hr = E_OUTOFMEMORY; goto done; } wcscpy(pContext->_pcoChangeList[i].pszMDPath, pcoChangeList[i].pszMDPath); pContext->_pcoChangeList[i].dwMDChangeType = pcoChangeList[i].dwMDChangeType; pContext->_pcoChangeList[i].dwMDNumDataIDs = pcoChangeList[i].dwMDNumDataIDs; pContext->_pcoChangeList[i].pdwMDDataIDs = new DWORD[pContext->_pcoChangeList[i].dwMDNumDataIDs]; if (NULL == pContext->_pcoChangeList[i].pdwMDDataIDs) { hr = E_OUTOFMEMORY; goto done; } memcpy(pContext->_pcoChangeList[i].pdwMDDataIDs, pcoChangeList[i].pdwMDDataIDs, sizeof(DWORD) * pContext->_pcoChangeList[i].dwMDNumDataIDs); } EnterCriticalSection(&s_critSec); InsertTailList(&s_listEntry, &pContext->_listEntry); LeaveCriticalSection(&s_critSec); fRet = SetEvent(s_hDataAvailable); if (FALSE == fRet) { hr = HRESULT_FROM_WIN32(GetLastError()); goto done; } hr = S_OK; done: if (FAILED(hr)) { delete pContext; } return hr; } //static HRESULT NOTIFY_CONTEXT::Initialize() { HRESULT hr = S_OK; BOOL fRet = FALSE; s_hShutdown = NULL; s_hDataAvailable = NULL; s_fInitializedCritSec = FALSE; InitializeListHead(&s_listEntry); fRet = InitializeCriticalSectionAndSpinCount(&s_critSec, 0x80000001); if (FALSE == fRet) { hr = HRESULT_FROM_WIN32(GetLastError()); goto done; } s_fInitializedCritSec = TRUE; s_hShutdown = CreateEvent(NULL, // security descrpitor TRUE, // manual reset FALSE, // initial state NULL); // name if (NULL == s_hShutdown) { hr = HRESULT_FROM_WIN32(GetLastError()); goto done; } s_hDataAvailable = CreateEvent(NULL, // security descriptor FALSE, // auto reset FALSE, // initial state NULL); // name if (NULL == s_hDataAvailable) { hr = HRESULT_FROM_WIN32(GetLastError()); goto done; } s_hThread = CreateThread(NULL, 0, NotifyThreadProc, NULL, 0, &s_dwThreadId); if (NULL == s_hThread) { hr = HRESULT_FROM_WIN32(GetLastError()); goto done; } hr = S_OK; done: return hr; } //static VOID NOTIFY_CONTEXT::RemoveWorkFor( CADMCOMW *pCADMCOMW, BOOL fWaitForCurrent) { LIST_ENTRY ListToDelete; LIST_ENTRY *ple; NOTIFY_CONTEXT *pContext; LIST_ENTRY *pleNext; CADMCOMW *pCurrentCADMCOMW; if ( !s_fInitializedCritSec ) { return; } InitializeListHead( &ListToDelete ); EnterCriticalSection(&s_critSec); // eat all remaining data ple = s_listEntry.Flink; while ( ple != &s_listEntry ) { pleNext = ple->Flink; pContext = NOTIFY_CONTEXTFromListEntry( ple ); if ( pContext->_pCADMCOMW == pCADMCOMW ) { RemoveEntryList( ple ); InitializeListHead( ple ); InsertTailList( &ListToDelete, ple ); } pContext = NULL; ple = pleNext; } LeaveCriticalSection(&s_critSec); // Delete all collected notification contexts ple = ListToDelete.Flink; while ( ple != &ListToDelete ) { pleNext = ple->Flink; RemoveEntryList( ple ); InitializeListHead( ple ); pContext = NOTIFY_CONTEXTFromListEntry( ple ); delete pContext; pContext = NULL; ple = pleNext; } if ( fWaitForCurrent && ( s_dwThreadId != GetCurrentThreadId() ) ) { do { s_LockCurrentlyWorkingOn.ReadLock(); if ( s_pCurrentlyWorkingOn != NULL ) { pCurrentCADMCOMW = s_pCurrentlyWorkingOn->_pCADMCOMW; } else { pCurrentCADMCOMW = NULL; } s_LockCurrentlyWorkingOn.ReadUnlock(); // waiting for the NOTIFY_CONTEXT currently being processed to be // released so we can return and guarantee that no more work is queued. if ( pCADMCOMW == pCurrentCADMCOMW ) { Sleep(100); } } while ( pCADMCOMW == pCurrentCADMCOMW ); } } //static VOID NOTIFY_CONTEXT::RemoveAllWork( VOID ) { LIST_ENTRY * ple; NOTIFY_CONTEXT * pContext; LIST_ENTRY * pleNext; LIST_ENTRY ListToDelete; BOOL fShutdownNotifications = FALSE; if ( !s_fInitializedCritSec ) { return; } InitializeListHead( &ListToDelete ); EnterCriticalSection( &s_critSec ); // Delete all data notifications ple = s_listEntry.Flink; while ( ple != &s_listEntry ) { pleNext = ple->Flink; pContext = NOTIFY_CONTEXTFromListEntry( ple ); if ( pContext->_bIsMainNotification ) { RemoveEntryList( ple ); InitializeListHead( ple ); InsertTailList( &ListToDelete, ple ); } else { fShutdownNotifications = TRUE; } pContext = NULL; ple = pleNext; } LeaveCriticalSection( &s_critSec ); // Delete all collected notification contexts ple = ListToDelete.Flink; while ( ple != &ListToDelete ) { pleNext = ple->Flink; RemoveEntryList( ple ); InitializeListHead( ple ); pContext = NOTIFY_CONTEXTFromListEntry( ple ); delete pContext; pContext = NULL; ple = pleNext; } if ( fShutdownNotifications ) { Sleep( 1000 ); } EnterCriticalSection( &s_critSec ); ple = s_listEntry.Flink; InitializeListHead( &s_listEntry ); LeaveCriticalSection( &s_critSec ); // eat all remaining data while ( ple != &s_listEntry ) { pleNext = ple->Flink; InitializeListHead( ple ); pContext = NOTIFY_CONTEXTFromListEntry(ple); delete pContext; pContext = NULL; ple = pleNext; } if ( s_dwThreadId != GetCurrentThreadId() ) { while( s_pCurrentlyWorkingOn ) { // waiting for the NOTIFY_CONTEXT currently being processed to be // released so we can return and guarantee that no more work is queued. Sleep(100); } } } //static VOID NOTIFY_CONTEXT::Terminate() { DWORD dwRet; DBG_ASSERT(IsListEmpty(&s_listEntry)); if (s_hShutdown) { SetEvent(s_hShutdown); } if (s_hThread) { // need thread to terminate before shutting down more dwRet = WaitForSingleObject(s_hThread, INFINITE); DBG_ASSERT(WAIT_OBJECT_0 == dwRet); CloseHandle(s_hThread); s_hThread = NULL; } if (s_hDataAvailable) { CloseHandle(s_hDataAvailable); s_hDataAvailable = NULL; } if (s_hShutdown) { CloseHandle(s_hShutdown); s_hShutdown = NULL; } if (s_fInitializedCritSec) { DeleteCriticalSection(&s_critSec); s_fInitializedCritSec = FALSE; } } //static HRESULT NOTIFY_CONTEXT::GetNextContext( NOTIFY_CONTEXT ** ppContext) { HRESULT hr = S_OK; DWORD dwRet; NOTIFY_CONTEXT * pContext = NULL; PLIST_ENTRY ple; HANDLE arrHandles[2]; DBG_ASSERT(ppContext != NULL); *ppContext = NULL; EnterCriticalSection(&s_critSec); if (!IsListEmpty(&s_listEntry)) { ple = RemoveHeadList(&s_listEntry); InitializeListHead( ple ); pContext = NOTIFY_CONTEXTFromListEntry(ple); } LeaveCriticalSection(&s_critSec); arrHandles[0] = s_hDataAvailable; arrHandles[1] = s_hShutdown; while ( pContext == NULL ) { dwRet = WaitForMultipleObjects( 2, arrHandles, FALSE, INFINITE); if (dwRet == WAIT_OBJECT_0) { // data was signalled as available EnterCriticalSection(&s_critSec); if (!IsListEmpty(&s_listEntry)) { ple = RemoveHeadList(&s_listEntry); InitializeListHead( ple ); pContext = NOTIFY_CONTEXTFromListEntry(ple); } LeaveCriticalSection(&s_critSec); } else if (dwRet == WAIT_OBJECT_0 + 1) { hr = HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS); goto done; } else { DBG_ASSERT( ( dwRet == WAIT_OBJECT_0 ) || ( dwRet == WAIT_OBJECT_0+1 ) ); hr = E_UNEXPECTED; goto done; } } DBG_ASSERT( pContext ); s_LockCurrentlyWorkingOn.WriteLock(); s_pCurrentlyWorkingOn = pContext; s_LockCurrentlyWorkingOn.WriteUnlock(); *ppContext = pContext; pContext = NULL; hr = S_OK; done: DBG_ASSERT( pContext == NULL ); return hr; } //static DWORD WINAPI NOTIFY_CONTEXT::NotifyThreadProc( LPVOID ) { HRESULT hr = S_OK; CADMCOMW *pCADMCOMW; NOTIFY_CONTEXT *pContext = NULL; hr = CoInitializeEx( NULL, COINIT_MULTITHREADED ); if (FAILED(hr)) { // bleh. Nothing to be done then. return (DWORD)-1; } for( ; ; ) { hr = NOTIFY_CONTEXT::GetNextContext(&pContext); if (FAILED(hr)) { goto done; } DBG_ASSERT(NULL != pContext); DBG_ASSERT(NULL != pContext->_pCADMCOMW); pCADMCOMW = pContext->_pCADMCOMW; // Keep the object alive during the call to NotifySinksAsync pCADMCOMW->AddRef(); pCADMCOMW->NotifySinksAsync( pContext->_dwMDNumElements, pContext->_pcoChangeList, pContext->_bIsMainNotification ); delete pContext; // Release after deleting the context pCADMCOMW->Release(); pCADMCOMW = NULL; } done: CoUninitialize(); return 0; } HRESULT CADMCOMW::NotifySinksAsync( DWORD dwMDNumElements, MD_CHANGE_OBJECT_W pcoChangeList[], BOOL bIsMainNotification) { HRESULT hr = S_OK; CONNECTDATA *pConnData = NULL; ULONG cConnData = 0; ULONG i; if ( m_bTerminated ) { goto exit; } hr = m_ConnectionPoint.ListenersPresent(); if ( hr != S_OK ) { goto exit; } hr = m_ConnectionPoint.InternalEnumSinks( &pConnData, &cConnData ); if ( FAILED( hr ) || ( cConnData == 0 ) ) { goto exit; } // Loop thru the connection point's connections // and dispatch the event notification to that sink. for ( i = 0; iRelease(); pConnData[i].pUnk = NULL; pConnData[i].dwCookie = 0; } exit: if ( pConnData != NULL ) { for ( i = 0; iRelease(); pConnData[i].pUnk = NULL; } } delete [] pConnData; pConnData = NULL; } return hr; } HRESULT CADMCOMW::NotifySinkHelper( IUnknown *pUnk, DWORD dwMDNumElements, MD_CHANGE_OBJECT_W pcoChangeList[], BOOL bIsMainNotification) { // Locals HRESULT hr = S_OK; ICallFactory *pCF = NULL; IMSAdminBaseSinkW *pIADMCOMSINKW_Synchro = NULL; AsyncIMSAdminBaseSinkW *pIADMCOMSINKW_Async = NULL; // Check args DBG_ASSERT( pUnk != NULL ); if ( pUnk == NULL ) { hr = E_INVALIDARG; goto exit; } // If we are talking to a proxy if ( m_dwProcessIdCaller != sm_dwProcessIdThis ) { // // asynchronous callback // // Get the call factory hr = pUnk->QueryInterface( IID_ICallFactory, (VOID**)&pCF ); if ( FAILED( hr ) ) { DBGPRINTF(( DBG_CONTEXT, "Failled in to get ICallFactory !!!\n" )); goto exit; } // Create a asynchronous call hr = pCF->CreateCall( IID_AsyncIMSAdminBaseSink_W, NULL, IID_AsyncIMSAdminBaseSink_W, (IUnknown**)&pIADMCOMSINKW_Async ); if ( FAILED( hr ) ) { DBGPRINTF(( DBG_CONTEXT, "Failled in CreateCall to ICallFactory !!!\n" )); goto exit; } // Set the impersonation level to identify to prevent // elevation to LocalSystem in the client process. hr = SetSinkCallbackSecurityBlanket( pIADMCOMSINKW_Async ); if ( FAILED( hr ) ) { DBGPRINTF(( DBG_CONTEXT, "SetSinkCallbackSecurityBlanket failled for the async sink !!!\n" )); goto exit; } if (bIsMainNotification) { hr = pIADMCOMSINKW_Async->Begin_SinkNotify( dwMDNumElements, pcoChangeList ); } else { hr = pIADMCOMSINKW_Async->Begin_ShutdownNotify(); } } else { // The client is inproc -> synchronous notifications // // synchronous callback // hr = pUnk->QueryInterface( IID_IMSAdminBaseSink_W, (VOID**)&pIADMCOMSINKW_Synchro ); if ( FAILED( hr ) ) { DBGPRINTF(( DBG_CONTEXT, "Failled in QueryInterface for IID_IMSAdminBaseSink_W\n" )); goto exit; } if (bIsMainNotification) { hr = pIADMCOMSINKW_Synchro->SinkNotify( dwMDNumElements, pcoChangeList ); } else { hr = pIADMCOMSINKW_Synchro->ShutdownNotify(); } } exit: if ( pIADMCOMSINKW_Synchro != NULL ) { pIADMCOMSINKW_Synchro->Release(); pIADMCOMSINKW_Synchro = NULL; } if ( pIADMCOMSINKW_Async != NULL ) { pIADMCOMSINKW_Async->Release(); pIADMCOMSINKW_Async = NULL; } if ( pCF != NULL ) { pCF->Release(); pCF = NULL; } return hr; } // // Stubs for routine that clients shouldn't be calling anyway. // HRESULT CADMCOMW::KeyExchangePhase1() { return E_FAIL; } HRESULT CADMCOMW::KeyExchangePhase2() { return E_FAIL; } HRESULT STDMETHODCALLTYPE CADMCOMW::GetServerGuid( void) { return E_FAIL; } HRESULT STDMETHODCALLTYPE CADMCOMW::UnmarshalInterface( IMSAdminBaseW * *piadmbwInterface) { AddRef(); // Always return interfaces addref'ed *piadmbwInterface = (IMSAdminBaseW *)this; return (S_OK); } BOOL CADMCOMW::CheckGetAttributes( DWORD dwAttributes) { DWORD dwReturn = TRUE; if ((dwAttributes & METADATA_REFERENCE) || ((dwAttributes & METADATA_PARTIAL_PATH) && !(dwAttributes & METADATA_INHERIT))) { dwReturn = FALSE; } return dwReturn; } VOID WaitForServiceStatus( SC_HANDLE schDependent, DWORD dwDesiredServiceState) { DWORD dwSleepTotal = 0; SERVICE_STATUS ssDependent; while (dwSleepTotal < MAX_SLEEP) { if (QueryServiceStatus(schDependent, &ssDependent)) { if (ssDependent.dwCurrentState == dwDesiredServiceState) { break; } else { // // Still pending... // Sleep(SLEEP_INTERVAL); dwSleepTotal += SLEEP_INTERVAL; } } else { break; } } } DWORD CADMCOMW::IsReadAccessGranted( METADATA_HANDLE hHandle, LPWSTR pszPath, METADATA_RECORD* pmdRecord) /*++ Routine Description: Check if read access to property granted based on ACL visible at point in metabase where property is stored ( as opposed as check made by AdminAclAccessCheck which uses the ACL visible at path specified during data access ) Arguments: hHandle - DCOM metabase handle pszPath - path relative to hHandle pmdRecord - metadata info to access property Returns: ERROR_SUCCESS if access granted, otherwise error code --*/ { DWORD dwStatus = ERROR_SUCCESS; LPWSTR pszPropPath; // // If property is not inherited then we already checked the correct ACL // if ( !(pmdRecord->dwMDAttributes & METADATA_ISINHERITED) ) { return dwStatus; } // determine from where we got it // do AccessCheck if ( (dwStatus = FindClosestProp( hHandle, pszPath, &pszPropPath, pmdRecord->dwMDIdentifier, pmdRecord->dwMDDataType, pmdRecord->dwMDUserType, METADATA_SECURE, TRUE )) == ERROR_SUCCESS ) { if ( pszPropPath ) // i.e such a property exist { dwStatus = AdminAclAccessCheck( m_pMdObject, (LPVOID)this, METADATA_MASTER_ROOT_HANDLE, pszPropPath, pmdRecord->dwMDIdentifier, METADATA_PERMISSION_READ, &g_ohMasterRootHandle ) ? ERROR_SUCCESS : GetLastError(); LocalFree( pszPropPath ); } else { dwStatus = MD_ERROR_DATA_NOT_FOUND; // // Should not happen unless handle is master root : // if we are here then we succeeded accessing data and as we have a read handle // nobody should be able to delete it // if master root handle we don't have such protection, so property could // have been deleted. // DBG_ASSERT ( METADATA_MASTER_ROOT_HANDLE == hHandle ); } } return dwStatus; } DWORD CADMCOMW::FindClosestProp( METADATA_HANDLE hHandle, LPWSTR pszRelPath, LPWSTR* ppszPropPath, DWORD dwPropId, DWORD dwDataType, DWORD dwUserType, DWORD dwAttr, BOOL fSkipCurrentNode) /*++ Routine Description: Find the closest path where the specified property exist ( in the direction of the root ) in metabase Arguments: hHandle - DCOM metabase handle pszRelPath - path relative to hHandle ppszPropPath - updated with path to property or NULL if property not found dwPropId - property ID dwDataType - property data type dwUserType - property user type dwAttr - property attribute fSkipCurrentNode - TRUE to skip current node while scanning for property Returns: TRUE if success ( including property not found ), otherwise FALSE --*/ { DWORD dwReturn; LPWSTR pszParentPath; METADATA_HANDLE hActualHandle; COpenHandle *pohParent; HRESULT hRes; METADATA_RECORD mdRecord; DWORD dwRequiredLen; LPWSTR pszPath; BOOL fFound; DWORD dwRelPathLen; DWORD dwParentPathLen; DWORD dwTotalSize; dwReturn = Lookup( hHandle, &hActualHandle, &pohParent); if ( dwReturn != ERROR_SUCCESS ) { return dwReturn; } pszParentPath = pohParent->GetPath(); if (pszRelPath == NULL) { pszRelPath = L""; } DBG_ASSERT(pszParentPath != NULL); DBG_ASSERT((*pszParentPath == (WCHAR)'\0') || ISPATHDELIMW(*pszParentPath)); // // Strip front slash now, add it in later // if (ISPATHDELIMW(*pszRelPath)) { pszRelPath++; } dwRelPathLen = (DWORD)wcslen(pszRelPath); dwParentPathLen = (DWORD)wcslen(pszParentPath); DBG_ASSERT((dwParentPathLen == 0) || (!ISPATHDELIMW(pszParentPath[dwParentPathLen -1]))); // // Get rid of trailing slash for good // if ((dwRelPathLen > 0) && (ISPATHDELIMW(pszRelPath[dwRelPathLen -1]))) { dwRelPathLen--; } // // Include space for mid slash if Relpath exists // Include space for termination // dwTotalSize = (dwRelPathLen + dwParentPathLen + 1 + ((dwRelPathLen > 0) ? 1 : 0)) * sizeof(WCHAR); *ppszPropPath = pszPath = (LPWSTR)LocalAlloc(LMEM_FIXED, dwTotalSize); if (pszPath == NULL) { dwReturn = GetLastError(); } else { // // OK to always copy the first part // memcpy(pszPath, pszParentPath, dwParentPathLen * sizeof(WCHAR)); // // Don't need slash if there is no RelPath // if (dwRelPathLen > 0) { pszPath[dwParentPathLen] = (WCHAR)'/'; memcpy(pszPath + dwParentPathLen + 1, pszRelPath, dwRelPathLen * sizeof(WCHAR)); } pszPath[(dwTotalSize / sizeof(WCHAR)) - 1] = (WCHAR)'\0'; // // Now convert \ to / for string compares // LPWSTR pszPathIndex = pszPath; while ((pszPathIndex = wcschr(pszPathIndex, (WCHAR)'\\')) != NULL) { *pszPathIndex = (WCHAR)'/'; } // scan for property pszPathIndex = pszPath + wcslen(pszPath); for ( ; ; ) { if ( !fSkipCurrentNode ) { // check prop exist mdRecord.dwMDIdentifier = dwPropId; mdRecord.dwMDAttributes = dwAttr; mdRecord.dwMDUserType = dwUserType; mdRecord.dwMDDataType = dwDataType; mdRecord.dwMDDataLen = 0; mdRecord.pbMDData = NULL; mdRecord.dwMDDataTag = NULL; hRes = m_pMdObject->ComMDGetMetaDataW( METADATA_MASTER_ROOT_HANDLE, pszPath, &mdRecord, &dwRequiredLen ); if ( hRes == RETURNCODETOHRESULT(ERROR_INSUFFICIENT_BUFFER) ) { break; } } // scan backward for delimiter fFound = FALSE; if ( pszPathIndex > pszPath ) { do { if ( *--pszPathIndex == L'/' ) { *pszPathIndex = L'\0'; fFound = TRUE; break; } } while ( pszPathIndex > pszPath ); } if ( !fFound ) { // Property not found, return OK status with NULL string *ppszPropPath = NULL; LocalFree( pszPath ); break; } fSkipCurrentNode = FALSE; } } pohParent->Release( this ); return dwReturn; } BOOL MakeParentPath( LPWSTR pszPath) /*++ Routine Description: Make the path points to parent Arguments: pszPath - path to adjust Returns: TRUE if success, otherwise FALSE ( no parent ) --*/ { LPWSTR pszPathIndex = pszPath + wcslen( pszPath ); BOOL fFound = FALSE; while ( pszPathIndex > pszPath ) { if ( *--pszPathIndex == L'/' ) { *pszPathIndex = L'\0'; fFound = TRUE; break; } } return fFound; } HRESULT CADMCOMW::InitializeCallerWatch(VOID) /*++ Routine Description: Sets up watching the caller process. Arguments: none Returns: HRESULT. --*/ { // Locals HRESULT hr = S_OK; RPC_STATUS RpcStatus = RPC_S_OK; HANDLE hProcessCaller = NULL; HANDLE hWaitCaller = NULL; HANDLE hToken = NULL; IServerSecurity *pServerSecurity = NULL; unsigned int fClientLocal = 0; DWORD dwProcessId = 0; BOOL fRet; // If already initialized if ( IsCallerWatchInitialized() ) { // Just exit goto exit; } // Get the COM context for the call hr = CoGetCallContext( IID_IServerSecurity, (VOID**)&pServerSecurity ); if ( FAILED( hr ) ) { // Succeess if there is no call context (inproc call). if ( hr == RPC_E_CALL_COMPLETE ) { // This is not a failure hr = S_OK; // The caller is our process m_dwProcessIdCaller = sm_dwProcessIdThis; } if ( FAILED( hr ) ) { DBGPRINTF(( DBG_CONTEXT, "CoGetCallContext() failed in InitializeCallerWatch hr=0x%08x.\n", hr )); } // Bail out goto exit; } // Check whether the client is on the same machine RpcStatus = I_RpcBindingIsClientLocal( NULL, &fClientLocal ); // Not a RPC call? if ( ( RpcStatus == RPC_S_NO_CALL_ACTIVE ) || ( RpcStatus == RPC_S_INVALID_BINDING ) ) { // The caller is our process m_dwProcessIdCaller = sm_dwProcessIdThis; goto exit; } // Remote client? if ( ( RpcStatus == RPC_S_OK ) && ( fClientLocal == 0 ) ) { // There is no way to watch the caller m_dwProcessIdCaller = (DWORD)-1; goto exit; } // Failed? if ( ( RpcStatus != RPC_S_OK ) && ( RpcStatus != RPC_S_CANNOT_SUPPORT ) ) { hr = HRESULT_FROM_WIN32( RpcStatus ); DBGPRINTF(( DBG_CONTEXT, "I_RpcBindingIsClientLocal() failed in InitializeCallerWatch hr=0x%08x.\n", hr )); goto exit; } // Try to get the caller pid RpcStatus = I_RpcBindingInqLocalClientPID( NULL, &dwProcessId ); // Not a RPC call? if ( ( RpcStatus == RPC_S_NO_CALL_ACTIVE ) || ( RpcStatus == RPC_S_INVALID_BINDING ) ) { // The caller is our process m_dwProcessIdCaller = sm_dwProcessIdThis; goto exit; } // Failed? if ( RpcStatus != RPC_S_OK ) { hr = HRESULT_FROM_WIN32( RpcStatus ); DBGPRINTF(( DBG_CONTEXT, "I_RpcBindingInqLocalClientPID() failed in InitializeCallerWatch hr=0x%08x.\n", hr )); goto exit; } // If the caller is RpcSs if ( dwProcessId == sm_dwProcessIdRpcSs ) { // wait for the next call to come goto exit; } // If the caller is in our process if ( dwProcessId == sm_dwProcessIdThis ) { // Save the caller pid m_dwProcessIdCaller = sm_dwProcessIdThis; // Not need to watch outself goto exit; } // Try to get the thread impersonation token fRet = OpenThreadToken( GetCurrentThread(), TOKEN_IMPERSONATE | TOKEN_QUERY, TRUE, &hToken ); DBG_ASSERT( !fRet || hToken ); // Check whether the thread was impersonated if ( fRet && ( hToken != NULL ) ) { // Revet to LocalSystem fRet = RevertToSelf(); if ( !fRet ) { hr = HRESULT_FROM_WIN32( GetLastError() ); DBGPRINTF(( DBG_CONTEXT, "RevertToSelf() failed in InitializeCallerWatch hr=0x%08x.\n", hr )); // Since RevertToSelf failed do not try to impersonate CloseHandle( hToken ); hToken = NULL; goto exit; } } // Open the process hProcessCaller = OpenProcess( SYNCHRONIZE, FALSE, dwProcessId ); if ( hProcessCaller == NULL ) { hr = HRESULT_FROM_WIN32( GetLastError() ); DBGPRINTF(( DBG_CONTEXT, "OpenProcess() failed in InitializeCallerWatch hr=0x%08x.\n", hr )); goto exit; } // Register wait for the process to end fRet = RegisterWaitForSingleObject( &hWaitCaller, hProcessCaller, CallerWatchWaitOrTimerCallback, this, INFINITE, WT_EXECUTEONLYONCE); if ( !fRet ) { hr = HRESULT_FROM_WIN32( GetLastError() ); DBGPRINTF(( DBG_CONTEXT, "RegisterWaitForSingleObject() failed in InitializeCallerWatch hr=0x%08x.\n", hr )); goto exit; } DBG_ASSERT( hWaitCaller != NULL ); // Save the handles in the object if ( NULL == InterlockedCompareExchangePointer( &m_hProcessCaller, hProcessCaller, NULL ) ) { m_hWaitCaller = hWaitCaller; m_dwProcessIdCaller = dwProcessId; hProcessCaller = NULL; hWaitCaller = NULL; } DBG_ASSERT( m_dwProcessIdCaller == dwProcessId ); exit: // Cleanup if ( pServerSecurity ) { pServerSecurity->Release(); pServerSecurity = NULL; } if ( hWaitCaller != NULL) { UnregisterWaitEx( hWaitCaller, INVALID_HANDLE_VALUE ); hWaitCaller = NULL; } if ( hProcessCaller != NULL ) { CloseHandle( hProcessCaller ); hProcessCaller = NULL; } if ( hToken ) { fRet = ImpersonateLoggedOnUser( hToken ); if ( !fRet ) { hr = HRESULT_FROM_WIN32( GetLastError() ); DBGPRINTF(( DBG_CONTEXT, "ImpersonateLoggedOnUser() failed in InitializeCallerWatch hr=0x%08x.\n", hr )); } CloseHandle( hToken ); hToken = NULL; } DBG_ASSERT( SUCCEEDED( hr ) ); // Done return hr; } HRESULT CADMCOMW::ShutdownCallerWatch(VOID) /*++ Routine Description: Shuts down watching the caller process. Arguments: none Returns: HRESULT. --*/ { // Locals HRESULT hr = S_OK; BOOL fRet; HANDLE hWaitCaller = NULL; HANDLE hProcessCaller = NULL; DWORD dwOldThreadId; DWORD dwThreadId = GetCurrentThreadId(); // Set the thread id dwOldThreadId = InterlockedCompareExchange( (LONG*)&m_dwThreadIdDisconnect, (LONG)dwThreadId, 0 ); // Get the wait object hWaitCaller = InterlockedExchangePointer( &m_hWaitCaller, NULL ); // If not initialized/already closed if ( hWaitCaller == NULL ) { // Nothing goto exit; } // Get the process hProcessCaller = InterlockedExchangePointer( &m_hProcessCaller, NULL ); // If we are in the same thread as the callback function // we cannot wait for it to finish if ( dwOldThreadId == dwThreadId ) { // Delete the registered wait for the caller process // NULL passed as completion event causes // UnregisterWaitEx to return w/o blocking fRet = UnregisterWaitEx( hWaitCaller, NULL ); // The call fails with ERROR_IO_PENDING if the callback function // is still running (which is expected, because it called us indirectly), // but any other error is failure if ( !fRet && ( GetLastError() == ERROR_IO_PENDING ) ) { // This is not a failure fRet = TRUE; SetLastError( ERROR_SUCCESS ); } } else { // Delete the registered wait for the caller process // INVALID_HANDLE_VALUE passed as completion event causes // UnregisterWaitEx not to return until the callback function returns fRet = UnregisterWaitEx( hWaitCaller, INVALID_HANDLE_VALUE ); } hWaitCaller = NULL; if ( !fRet ) { hr = HRESULT_FROM_WIN32( GetLastError() ); DBGPRINTF(( DBG_CONTEXT, "UnregisterWaitEx() failed in ShutdownCallerWatch hr=0x%08x.\n", hr )); goto exit; } exit: if ( hProcessCaller ) { // Close the process handle CloseHandle( hProcessCaller ); hProcessCaller = NULL; } if ( dwOldThreadId == 0 ) { // Restore the thread id dwOldThreadId = InterlockedCompareExchange( (LONG*)&m_dwThreadIdDisconnect, 0, (LONG)dwThreadId ); DBG_ASSERT( dwOldThreadId == dwThreadId ); } DBG_ASSERT( SUCCEEDED( hr ) ); // Done return hr; } HRESULT CADMCOMW::DisconnectOrphaned(VOID) /*++ Routine Description: Calls CoDisconnectObject on the object and the connection point. Arguments: none Returns: HRESULT. --*/ { // Locals HRESULT hr = S_OK; DWORD dwOldThreadId = (DWORD)-1; DWORD dwThreadId = GetCurrentThreadId(); DWORD cRef = 0; // If already terminated if ( m_bTerminated ) { // Do nothing goto exit; } // Set the thread id dwOldThreadId = InterlockedCompareExchange( (LONG*)&m_dwThreadIdDisconnect, (LONG)dwThreadId, 0 ); if ( dwOldThreadId != 0 ) { goto exit; } // AddRef, to prevent the object being destroyed by one of the CoDisconnect calls AddRef(); // Disconnect the object CoDisconnectObject( static_cast( this ), 0 ); // Disconnect the connection point CoDisconnectObject( static_cast( &m_ConnectionPoint ), 0 ); // Stop getting and firing notifications. // This is a forcible termination so remove all pending notifications StopNotifications( TRUE ); // Release (which may delete the object) cRef = Release(); exit: if ( ( cRef > 1 ) && ( dwOldThreadId == 0 ) ) { // Set the thread id dwOldThreadId = InterlockedCompareExchange( (LONG*)&m_dwThreadIdDisconnect, 0, (LONG)dwThreadId ); DBG_ASSERT( dwOldThreadId == dwThreadId ); } DBG_ASSERT( SUCCEEDED( hr ) ); // Done return hr; } VOID CALLBACK CADMCOMW::CallerWatchWaitOrTimerCallback( PVOID lpParameter, // thread data BOOLEAN TimerOrWaitFired) // reason /*++ Routine Description: Passed as a callback to RegisterWaitForSingleObject. Called when the caller process terminates. CoDisconnects the object to force COM to release it before the 10 min timeout. Arguments: lpParameter - the callback context passed to RegisterWaitForSingleObject. Must be the CADMCOMW object used by the process waiting on. TimerOrWaitFired - The reason to call the callback. Since we are waiting on the handle for INFINITE time must be always FALSE. Returns: VOID. --*/ { // Locals HRESULT hr = S_OK; CADMCOMW *pThis; BOOL fUninitCom = FALSE; DBG_ASSERT( lpParameter != NULL ); DBG_ASSERT( TimerOrWaitFired == FALSE ); // Check if ( ( lpParameter == NULL ) || ( TimerOrWaitFired != FALSE ) ) { hr = E_INVALIDARG; DBGPRINTF(( DBG_CONTEXT, "Invadil args passed to CallerWatchWaitOrTimerCallback.\n", hr )); goto exit; } // Initialize COM hr = CoInitializeEx( NULL, COINIT_MULTITHREADED ); // If succeeded if ( SUCCEEDED( hr ) ) { // Remember to uninit fUninitCom = TRUE; } // If already initialized w/ different model if ( hr == RPC_E_CHANGED_MODE ) { // Ok it is thread is apartment threaded, but this means that // COM is already initialized and there is no need to uninitialize it. hr = S_OK; } DBG_ASSERT( SUCCEEDED( hr ) ); // If really failed if ( FAILED( hr ) ) { DBGPRINTF(( DBG_CONTEXT, "CoInitializeEx() failed in CallerWatchWaitOrTimerCallback hr=0x%08x.\n", hr )); // Ouch! Nothing we can do. The object will have to wait the timeout. goto exit; } // Get the object pThis = (CADMCOMW*)lpParameter; // Disconnect it hr = pThis->DisconnectOrphaned(); DBG_ASSERT( SUCCEEDED( hr ) ); if ( FAILED( hr ) ) { DBGPRINTF(( DBG_CONTEXT, "DisconnectOrphaned() failed in CallerWatchWaitOrTimerCallback hr=0x%08x.\n", hr )); goto exit; } exit: DBG_ASSERT( SUCCEEDED( hr ) ); // Cleanup if ( fUninitCom ) { // Uninitialize COM CoUninitialize(); } } HRESULT CADMCOMW::GetPids(VOID) /*++ Routine Description: Gets the pid of this process (inetinfo.exe) and saves it to sm_dwProcessIdThis. Gets the pid of the svchost process running RpcSs and saves it to sm_dwProcessIdRpcSs. Arguments: None Returns: HRESULT. --*/ { // Locals HRESULT hr = S_OK; BOOL fRet; SC_HANDLE schSCM = NULL; SC_HANDLE schRpcSs = NULL; SERVICE_STATUS_PROCESS ServiceStatusProcessRcpSs = {0}; DWORD cbRequired = 0; // Save the current processid sm_dwProcessIdThis = GetCurrentProcessId(); DBG_ASSERT( sm_dwProcessIdThis != 0 ); // If already initialized don't play w/ SCM again if ( sm_dwProcessIdRpcSs != 0 ) { goto exit; } // Open SCM schSCM = OpenSCManager( NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE ); if (schSCM == NULL) { hr = HRESULT_FROM_WIN32( GetLastError() ); DBGPRINTF(( DBG_CONTEXT, "OpenSCManager() failed in GetPids hr=0x%08x.\n", hr )); goto exit; } // Open RpcSs schRpcSs = OpenServiceA( schSCM, "RpcSs", SERVICE_QUERY_STATUS); if ( schRpcSs == NULL ) { hr = HRESULT_FROM_WIN32( GetLastError() ); DBGPRINTF(( DBG_CONTEXT, "OpenService() failed in GetPids hr=0x%08x.\n", hr )); goto exit; } // Query the status of RpcSs to get the pid fRet = QueryServiceStatusEx( schRpcSs, SC_STATUS_PROCESS_INFO, (BYTE*)&ServiceStatusProcessRcpSs, sizeof(ServiceStatusProcessRcpSs), &cbRequired ); if ( !fRet ) { DBG_ASSERT( GetLastError() != ERROR_INSUFFICIENT_BUFFER ); hr = HRESULT_FROM_WIN32( GetLastError() ); DBGPRINTF(( DBG_CONTEXT, "QueryServiceStatusEx() failed in GetPids hr=0x%08x.\n", hr )); goto exit; } // Save the pid sm_dwProcessIdRpcSs = ServiceStatusProcessRcpSs.dwProcessId; DBG_ASSERT( sm_dwProcessIdRpcSs != 0 ); exit: // Cleanup if ( schRpcSs != NULL ) { CloseServiceHandle( schRpcSs ); schRpcSs = NULL; } if ( schSCM != NULL ) { CloseServiceHandle( schSCM ); schSCM = NULL; } DBG_ASSERT( SUCCEEDED( hr ) ); return hr; } HRESULT MakePathCanonicalizationProof( LPCWSTR pwszName, BOOL fResolve, STRAU *pstrPath) /*++ Routine Description: The function tries make a file name proof to all canonicalization problems. If fResolve is FALSE: This functions adds a prefix to the string, which is "\\?\UNC\" for a UNC path, and "\\?\" for other paths. This prefix tells Windows not to parse the path. If fResolve is TRUE: This function constructs a file name as above, opens the file and gets the real name from the handle. Stolen from iisutil.dll, because coadmin links only with iisrtl Arguments: IN pszName - The path to be converted IN fResolve - Whether the caller can live with the name just prefixed or needs the real name. OUT pstrPath - Output path created Return Values: HRESULT --*/ { HRESULT hr = S_OK; DWORD dwError; BOOL fRet; NTSTATUS Status = STATUS_SUCCESS; IO_STATUS_BLOCK IoStatusBlock; HANDLE hFile = NULL; BUFFER Buff; FILE_NAME_INFORMATION *pFileNameInfo = NULL; DWORD dwSize; DWORD dwReqSize; WCHAR wszPrefix[4]; WCHAR wchT; // Check args if ( ( pwszName == NULL ) || ( pstrPath == NULL ) ) { hr = E_INVALIDARG; goto exit; } pstrPath->Reset(); if ( pwszName[ 0 ] == L'\\' && pwszName[ 1 ] == L'\\' ) { // If the path is already canonicalized, just return if ( ( pwszName[ 2 ] == '?' || pwszName[ 2 ] == '.' ) && pwszName[ 3 ] == '\\' ) { // Prepend "\\?\" // If the path was in DOS form ("\\.\"), // we need to change it to Win32 from ("\\?\") fRet = pstrPath->Append( L"\\\\?\\" ); if ( !fRet ) { hr = E_OUTOFMEMORY; goto exit; } // Copy the path into the string fRet = pstrPath->Append( (const LPWSTR)(pwszName+4) ); if ( !fRet ) { hr = E_OUTOFMEMORY; goto exit; } // Just return the copy goto exit; } if ( fResolve ) { wszPrefix[0] = L'\\'; wszPrefix[1] = L'\0'; } // Skip the "\\" pwszName += 2; // Prepend "\\?\UNC\" fRet = pstrPath->Append( L"\\\\?\\UNC\\" ); if ( !fRet ) { hr = E_OUTOFMEMORY; goto exit; } } else { if ( fResolve ) { wchT = pwszName[0]; if ( ( wchT >= L'A' ) && ( wchT <= L'Z' ) ) { wchT = wchT - L'A' + L'a'; } if ( ( wchT < L'a' ) || ( wchT > L'z' ) ) { hr = E_INVALIDARG; goto exit; } if ( pwszName[1] != L':' ) { hr = E_INVALIDARG; goto exit; } if ( pwszName[2] != L'\\' ) { hr = E_INVALIDARG; goto exit; } wszPrefix[0] = pwszName[0]; wszPrefix[1] = pwszName[1]; wszPrefix[2] = L'\0'; } // Prepend "\\?\" fRet = pstrPath->Append( L"\\\\?\\" ); if ( !fRet ) { hr = E_OUTOFMEMORY; goto exit; } } // Add the path fRet = pstrPath->Append( (const LPWSTR)pwszName ); if ( !fRet ) { hr = E_OUTOFMEMORY; goto exit; } // Don't use the original filename anymore pwszName = NULL; // If the caller can work with the anti-canonicalized file name if ( !fResolve ) { // We are done, return goto exit; } // Well have to do it the hard way hFile = CreateFileW( pstrPath->QueryStrW(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if ( ( hFile == NULL ) || ( hFile == INVALID_HANDLE_VALUE ) ) { dwError = GetLastError(); hr = HRESULT_FROM_WIN32( dwError ); goto exit; } // Assume the size of the name should approximate the same dwSize = sizeof( FILE_NAME_INFORMATION ) + pstrPath->QueryCBW()+ sizeof( WCHAR ); dwSize = ( dwSize + 0x0000000Ful ) & ~0x0000000Ful; // Set the size fRet = Buff.Resize( dwSize ); if ( !fRet ) { hr = E_OUTOFMEMORY; goto exit; } pFileNameInfo = (FILE_NAME_INFORMATION*)Buff.QueryPtr(); DBG_ASSERT( pFileNameInfo ); // Get the name from the handle Status = NtQueryInformationFile( hFile, &IoStatusBlock, pFileNameInfo, dwSize, FileNameInformation ); // If the buffer is not big enough if ( ( Status == STATUS_BUFFER_OVERFLOW ) || ( Status == STATUS_BUFFER_TOO_SMALL ) ) { // Calculate the new size dwReqSize = sizeof(FILE_NAME_INFORMATION) + pFileNameInfo->FileNameLength + sizeof( WCHAR ); DBG_ASSERT( dwReqSize > dwSize ); dwSize = ( dwReqSize + 0x0000000Ful ) & ~0x0000000Ful; fRet = Buff.Resize( dwSize ); if ( !fRet ) { hr = E_OUTOFMEMORY; goto exit; } pFileNameInfo = (FILE_NAME_INFORMATION*)Buff.QueryPtr(); DBG_ASSERT( pFileNameInfo ); // Retry getting the name from the handle Status = NtQueryInformationFile( hFile, &IoStatusBlock, pFileNameInfo, dwSize, FileNameInformation ); } if ( !NT_SUCCESS( Status ) ) { dwError = RtlNtStatusToDosError( Status ); DBG_ASSERT( dwError != ERROR_SUCCESS ); if ( dwError != ERROR_MR_MID_NOT_FOUND ) { hr = HRESULT_FROM_WIN32( dwError ); } else { hr = HRESULT_FROM_NT( Status ); DBG_ASSERT ( FAILED( hr ) ); } goto exit; } pstrPath->Reset(); // Set the prefix fRet = pstrPath->Copy( wszPrefix ); if ( !fRet ) { hr = E_OUTOFMEMORY; goto exit; } // Append the name fRet = pstrPath->Append( pFileNameInfo->FileName, pFileNameInfo->FileNameLength/sizeof( WCHAR ) ); if ( !fRet ) { hr = E_OUTOFMEMORY; goto exit; } exit: if ( ( hFile != NULL ) && ( hFile != INVALID_HANDLE_VALUE ) ) { CloseHandle(hFile); hFile = NULL; } if ( FAILED( hr ) ) { pstrPath->Reset(); } return hr; }