//+------------------------------------------------------------------------ // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1996 - 1997 // // File: cclsacc.cxx // // Contents: Class factory and IUnknown methods for CClassAccess // // Author: DebiM // //------------------------------------------------------------------------- #include "cstore.hxx" HRESULT __stdcall CClassAccess::QueryInterface(REFIID riid, void * * ppvObject) { IUnknown *pUnkTemp = NULL; SCODE sc = S_OK; if( IsEqualIID( IID_IUnknown, riid ) ) { pUnkTemp = (IUnknown *)(IClassAccess *)this; } else if( IsEqualIID( IID_IClassAccess, riid ) ) { pUnkTemp = (IUnknown *)(IClassAccess *)this; } else { sc = (E_NOINTERFACE); } if((pUnkTemp != NULL) && (SUCCEEDED(sc))) { *ppvObject = (void * )pUnkTemp; pUnkTemp->AddRef(); } return(sc); } ULONG __stdcall CClassAccess::AddRef() { InterlockedIncrement(( long * )&m_uRefs ); return m_uRefs; } ULONG __stdcall CClassAccess::Release() { unsigned long uTmp = InterlockedDecrement((long *)&m_uRefs); unsigned long cRef = m_uRefs; // 0 is the only valid value to check if (uTmp == 0) { delete this; } return(cRef); } // // Constructor // unsigned long gulcClassFactory = 0; CClassAccessCF::CClassAccessCF() { m_uRefs = 1; InterlockedIncrement((long *) &gulcClassFactory ); } // // Destructor // CClassAccessCF::~CClassAccessCF() { InterlockedDecrement((long *) &gulcClassFactory ); } HRESULT __stdcall CClassAccessCF::QueryInterface(REFIID riid, void * * ppvObject) { IUnknown *pUnkTemp = NULL; SCODE sc = S_OK; if( IsEqualIID( IID_IUnknown, riid ) ) { pUnkTemp = (IUnknown *)(ITypeLib *)this; } else if( IsEqualIID( IID_IClassFactory, riid ) ) { pUnkTemp = (IUnknown *)(IClassFactory *)this; } else { sc = (E_NOINTERFACE); } if((pUnkTemp != NULL) && (SUCCEEDED(sc))) { *ppvObject = (void * )pUnkTemp; pUnkTemp->AddRef(); } return(sc); } ULONG __stdcall CClassAccessCF::AddRef() { InterlockedIncrement(( long * )&m_uRefs ); return m_uRefs; } ULONG __stdcall CClassAccessCF::Release() { unsigned long uTmp = InterlockedDecrement((long *)&m_uRefs); unsigned long cRef = m_uRefs; // 0 is the only valid value to check if (uTmp == 0) { delete this; } return(cRef); } // // IClassFactory Overide // HRESULT __stdcall CClassAccessCF::CreateInstance(IUnknown * pUnkOuter, REFIID riid, void * * ppvObject) { CClassAccess * pIUnk = NULL; SCODE sc = S_OK; if( pUnkOuter == NULL ) { if( (pIUnk = new CClassAccess()) != NULL) { sc = pIUnk->QueryInterface( riid , ppvObject ); if(FAILED(sc)) { sc = E_UNEXPECTED; } pIUnk->Release(); } else sc = E_OUTOFMEMORY; } else { return E_INVALIDARG; } return (sc); } HRESULT __stdcall CClassAccessCF::LockServer(BOOL fLock) { if(fLock) { InterlockedIncrement((long *) &gulcClassFactory ); } else { InterlockedDecrement((long *) &gulcClassFactory ); } return(S_OK); } CClassAccess::CClassAccess() { m_uRefs = 1; m_cCalls = 0; pStoreList = NULL; cStores = 0; m_pszClassStorePath = NULL; m_pRsopUserToken = NULL; } CClassAccess::~CClassAccess() { DWORD i; delete [] m_pszClassStorePath; for (i = 0; i < cStores; i++) { if (pStoreList[i]->pszClassStorePath) { CsMemFree (pStoreList[i]->pszClassStorePath); pStoreList[i]->pszClassStorePath = NULL; } CsMemFree(pStoreList[i]); pStoreList[i] = NULL; } CsMemFree(pStoreList); cStores = NULL; } //---------------------------------------------------------------------- // // void PrintClassSpec( uCLSSPEC * pclsspec // Class Spec (GUID/Ext/MIME) ) { STRINGGUID szClsid; if (pclsspec->tyspec == TYSPEC_CLSID) { StringFromGUID (pclsspec->tagged_union.clsid, szClsid); CSDBGPrint((DM_WARNING, IDS_CSTORE_CLASSSPEC, TYSPEC_CLSID, szClsid)); } if (pclsspec->tyspec == TYSPEC_PROGID) { CSDBGPrint((DM_WARNING, IDS_CSTORE_CLASSSPEC, TYSPEC_PROGID, pclsspec->tagged_union.pProgId)); } if (pclsspec->tyspec == TYSPEC_FILEEXT) { CSDBGPrint((DM_WARNING, IDS_CSTORE_CLASSSPEC, TYSPEC_FILEEXT, pclsspec->tagged_union.pFileExt)); } } //---------------------------------------------------------------------- HRESULT STDMETHODCALLTYPE CClassAccess::GetAppInfo( uCLSSPEC * pclsspec, // Class Spec (GUID/Ext/MIME) QUERYCONTEXT * pQryContext, // Query Attributes PACKAGEDISPINFO * pPackageInfo ) // // This is the most common method to access the Class Store. // It queries the class store for implementations for a specific // Class Id, or File Ext, or ProgID or MIME type. // // If a matching implementation is available for the object type, // client architecture, locale and class context pointer to the // binary is returned. { // // Assume that this method is called in the security context // of the user process. Hence there is no need to impersonate. // // // Get the list of Class Stores for this user // HRESULT hr = S_OK; ULONG i = 0, j = 0, k= 0; IClassAccess * pICA = NULL; BOOL fCache = FALSE; PSID pUserSid = NULL; BOOL fFound = FALSE; QUERYCONTEXT QueryContext; if ((!pPackageInfo)) return E_INVALIDARG; memset(pPackageInfo, 0, sizeof(PACKAGEDISPINFO)); if ( pQryContext ) { QueryContext = *pQryContext; } else { // gets the default information. QueryContext.dwContext = CLSCTX_ALL; GetDefaultPlatform( &QueryContext.Platform ); QueryContext.Locale = LANG_SYSTEM_DEFAULT; QueryContext.dwVersionHi = (DWORD) -1; QueryContext.dwVersionLo = (DWORD) -1; } if (gDebug) PrintClassSpec(pclsspec); if (!pStoreList) hr = GetUserClassStores( m_pszClassStorePath, &pStoreList, &cStores, &fCache, &pUserSid); ERROR_ON_FAILURE(hr); for (i=0; i < cStores; i++) { if (!(pICA = GetNextValidClassStore(pStoreList, cStores, pUserSid, NULL, pclsspec, fCache, &i, &hr))) { ASSERT(FAILED(hr)); return hr; } // // Call method on this store // pICA->AddRef(); hr = pICA->GetAppInfo( pclsspec, &QueryContext, pPackageInfo); // Release it after use. pICA->Release(); if ( CS_E_OBJECT_NOTFOUND != hr ) { ERROR_ON_FAILURE(hr); } // // We are iterating through the class stores from highest precedence to lowest -- // thus, the first container to return success will be our choice. // if (SUCCEEDED(hr)) { fFound = TRUE; break; } hr = S_OK; } if ( ! fFound ) { hr = CS_E_PACKAGE_NOTFOUND; } Error_Cleanup: if (pUserSid) CsMemFree (pUserSid); if ( pICA ) { pICA->Release(); } if (fFound) { return S_OK; } return hr; } #define MAX_GUID_CCH 38 // // IsClassStoreForPolicy // BOOL IsClassStoreForPolicy(CLASSCONTAINER* pClassStore, LPWSTR wszPolicyId) { LPWSTR pszPolicyGuid; // Path looks like: // LDAP://CN=,CN=,CN=<{policy-guid}>,... // // Look for ',' first // pszPolicyGuid = wcschr(pClassStore->pszClassStorePath, L','); if (!pszPolicyGuid) { return FALSE; } // // Look for the second ',' // pszPolicyGuid = wcschr(pszPolicyGuid + 1, L','); if (!pszPolicyGuid) { return FALSE; } // // Now get to '{' at start of guid -- it is 4 chars // past the ',' which we are currently at. Use wcschr // to make sure we don't go past the end of the string // and that our assumptions about the structure of the // path are correct // if (wcschr(pszPolicyGuid, L'{') == (pszPolicyGuid + 4)) { pszPolicyGuid += 4; // // Now that we have the '{', we are at the start of the guid // and can compare with the requested policy id // if (_wcsnicmp(pszPolicyGuid, wszPolicyId, MAX_GUID_CCH) == 0) { return TRUE; } } return FALSE; } // // GetNextValidClassStore // // IClassAccess *GetNextValidClassStore(CLASSCONTAINER** pStoreList, DWORD cStores, PSID pUserSid, PRSOPTOKEN pRsopToken, uCLSSPEC* pClassSpec, BOOL fCache, DWORD * pcount, HRESULT* phr) { IClassAccess *pretICA = NULL; BOOL bSpecificPolicy; LPWSTR wszPolicyGuid; wszPolicyGuid = NULL; *phr = S_OK; bSpecificPolicy = pClassSpec ? TYSPEC_PACKAGENAME == pClassSpec->tyspec : FALSE; if (bSpecificPolicy) { GuidToString(pClassSpec->tagged_union.ByName.PolicyId, &wszPolicyGuid); if (!wszPolicyGuid) { *phr = E_OUTOFMEMORY; } } if (SUCCEEDED(*phr)) { for (pStoreList += (*pcount); (*pcount) < cStores; (*pcount)++, pStoreList++) { if (bSpecificPolicy && !IsClassStoreForPolicy(*pStoreList, wszPolicyGuid)) { continue; } { CSDBGPrint((DM_WARNING, IDS_CSTORE_BIND, (*pcount), (*pStoreList)->pszClassStorePath)); // // Bind to this Class Store // if (wcsncmp((*pStoreList)->pszClassStorePath, L"LDAP:", 5) == 0) { // // If the Storename starts with ADCS or LDAP // it is NTDS based implementation. Call directly. // IClassAccess *pCA = NULL; LPOLESTR szClassStore = (*pStoreList)->pszClassStorePath; *phr = pCF->CreateConnectedInstance( NULL, szClassStore, pUserSid, pRsopToken, fCache, (void **)&pCA); if ( ! SUCCEEDED(*phr) ) { break; } pretICA = pCA; } else { *phr = CS_E_INVALID_PATH; } break; } } } if (wszPolicyGuid) { CsMemFree(wszPolicyGuid); } if (!pretICA) { ASSERT(FAILED(*phr)); } CSDBGPrint((DM_WARNING, IDS_CSTORE_BIND_STATUS, *phr)); return pretICA; } HRESULT STDMETHODCALLTYPE CClassAccess::EnumPackages( LPOLESTR pszPackageName, GUID *pCategory, ULONGLONG *pLastUsn, DWORD dwQuerySpec, IEnumPackage **ppIEnumPackage) { // // Get the list of Class Stores for this user // HRESULT hr = S_OK; ULONG i; IEnumPackage **Enum; ULONG cEnum = 0; CMergedEnumPackage *EnumMerged = NULL; IClassAccess *pICA = NULL; ULONGLONG LastUsn, CopyLastUsn, *pCopyLastUsn; BOOL fCache = FALSE; PSID pUserSid = NULL; switch ( dwQuerySpec ) { case APPQUERY_ALL: case APPQUERY_ADMINISTRATIVE: case APPQUERY_POLICY: case APPQUERY_USERDISPLAY: case APPQUERY_RSOP_LOGGING: case APPQUERY_RSOP_ARP: break; default: return E_INVALIDARG; } LastUsn = 0; if (pLastUsn) { pCopyLastUsn = &CopyLastUsn; *pCopyLastUsn = *pLastUsn; } else pCopyLastUsn = NULL; // // Get the list of Class Stores for this user // if (!pStoreList) hr = GetUserClassStores( m_pszClassStorePath, &pStoreList, &cStores, &fCache, &pUserSid); *ppIEnumPackage = NULL; if ((hr == S_OK) && (cStores == 0)) { hr = CS_E_NO_CLASSSTORE; } if (SUCCEEDED(hr)) { // // Dynamically allocate a vector of interfaces (class stores). // Previously, this was a fixed size array, which, besides // only allowing for a small number of class stores, caused an // access violation because the rest of the code assumed that the // size of the fixed array could be arbitrarily large. // Enum = new IEnumPackage*[cStores]; if (!Enum) { hr = E_OUTOFMEMORY; } } if (!SUCCEEDED(hr)) { // // Free the Sid // if (pUserSid) CsMemFree (pUserSid); return hr; } for (i=0; i < cStores; i++) { if (!(pICA = GetNextValidClassStore(pStoreList, cStores, pUserSid, m_pRsopUserToken, NULL, fCache, &i, &hr))) { ASSERT(FAILED(hr)); return hr; } // // Call method on this store // hr = pICA->EnumPackages (pszPackageName, pCategory, pCopyLastUsn, dwQuerySpec, &(Enum[cEnum])); if (FAILED(hr)) { break; } if (pCopyLastUsn) { if (LastUsn < *pCopyLastUsn) LastUsn = *pCopyLastUsn; *pCopyLastUsn = *pLastUsn; } if (SUCCEEDED(hr)) cEnum++; } if (SUCCEEDED(hr)) { EnumMerged = new CMergedEnumPackage; hr = EnumMerged->Initialize(Enum, cEnum); if (FAILED(hr)) { for (i = 0; i < cEnum; i++) Enum[i]->Release(); delete EnumMerged; } else { hr = EnumMerged->QueryInterface(IID_IEnumPackage, (void **)ppIEnumPackage); if (FAILED(hr)) delete EnumMerged; } if (pLastUsn) { if (LastUsn > *pLastUsn) *pLastUsn = LastUsn; } } if (pUserSid) CsMemFree (pUserSid); // // Free the dynamically allocated vector of interfaces // delete [] Enum; if ( pICA ) { pICA->Release(); } return hr; } HRESULT __stdcall CClassAccess::SetClassStorePath( LPOLESTR pszClassStorePath, void* pRsopUserToken) { DWORD cchPath; // // We only allow this to be set once -- if it's already // set we return an error. // if (m_pszClassStorePath) { return E_INVALIDARG; } if ( ! pszClassStorePath ) { return S_OK; } cchPath = lstrlen(pszClassStorePath) + 1; m_pszClassStorePath = new WCHAR[cchPath]; if (!m_pszClassStorePath) { return E_OUTOFMEMORY; } (void) StringCchCopy(m_pszClassStorePath, cchPath, pszClassStorePath); m_pRsopUserToken = (PRSOPTOKEN) pRsopUserToken; return S_OK; }