/*++ Copyright (C) 1998-2001 Microsoft Corporation Module Name: OLDSEC.CPP Abstract: contains various routines and classes used providing backward security support. History: a-davj 02-sept-99 Created. --*/ #include "precomp.h" #include #include #include #include #include #include "secure.h" #include #include #include #include "oldsec.h" //*************************************************************************** // // CreateTheInstance // // Takes a class object, and a CCombined entry and creates an instance. // // PARAMETERS: /// // ppObj used to pass back the created instance // pClass Class object used for spawning // pCombinedEntry Has the combined values for all the aces which mach // the user or group. // RETURN VALUES: // // S_OK if all is well, else an error code // //*************************************************************************** HRESULT CreateTheInstance(IWbemClassObject ** ppObj, IWbemClassObject * pClass, CCombinedAce * pCombinedEntry) { if(ppObj == NULL || pCombinedEntry == NULL || pClass == NULL) return WBEM_E_INVALID_PARAMETER; // spawn the instance SCODE sc = pClass->SpawnInstance(0, ppObj); if(FAILED(sc)) return sc; CReleaseMe rm(*ppObj); // we addref at end if all is well // populate the instance bool bEnabled=false, bEditSecurity=false, bExecMethods=false; DWORD dwMask = pCombinedEntry->m_dwDeny; if(dwMask == 0) dwMask = pCombinedEntry->m_dwAllow; if( (dwMask & WBEM_ENABLE) && (dwMask & WBEM_REMOTE_ACCESS) && (dwMask & WBEM_WRITE_PROVIDER) && pCombinedEntry->m_dwAllow) bEnabled = true; if(dwMask & READ_CONTROL) bEditSecurity = true; if(dwMask & WBEM_METHOD_EXECUTE) bExecMethods = true; DWORD dwPermission = 0; // start at read if(dwMask & WBEM_PARTIAL_WRITE_REP) dwPermission = 1; if(dwMask & WBEM_FULL_WRITE_REP) dwPermission = 2; VARIANT var; var.vt = VT_I4; var.lVal = dwPermission; sc = (*ppObj)->Put(L"Permissions", 0, &var, 0); var.vt = VT_BOOL; var.boolVal = (bEnabled) ? VARIANT_TRUE : VARIANT_FALSE; sc = (*ppObj)->Put(L"Enabled", 0, &var, 0); var.boolVal = (bEditSecurity) ? VARIANT_TRUE : VARIANT_FALSE; sc = (*ppObj)->Put(L"EditSecurity", 0, &var, 0); var.boolVal = (bExecMethods) ? VARIANT_TRUE : VARIANT_FALSE; sc = (*ppObj)->Put(L"ExecuteMethods", 0, &var, 0); // Get the account and domain info LPWSTR pwszAccount = NULL; LPWSTR pwszDomain = NULL; sc = pCombinedEntry->GetNames(pwszAccount, pwszDomain); if(FAILED(sc)) return sc; CDeleteMe dm1(pwszAccount); CDeleteMe dm2(pwszDomain); var.vt = VT_BSTR; if(pwszAccount && wcslen(pwszAccount) > 0) // SEC:REVIEWED 2002-03-22 : OK, <->GetNames() call assures valid strings> { BSTR bstr = SysAllocString(pwszAccount); // SEC:REVIEWED 2002-03-22 : OK if(bstr == NULL) return WBEM_E_OUT_OF_MEMORY; var.bstrVal = bstr; sc = (*ppObj)->Put(L"Name", 0, &var, 0); SysFreeString(bstr); } if(pwszDomain && wcslen(pwszDomain)) // SEC:REVIEWED 2002-03-22 : OK { BSTR bstr = SysAllocString(pwszDomain); // SEC:REVIEWED 2002-03-22 : OK if(bstr == NULL) return WBEM_E_OUT_OF_MEMORY; var.bstrVal = bstr; sc = (*ppObj)->Put(L"Authority", 0, &var, 0); SysFreeString(bstr); } (*ppObj)->AddRef(); // make up for the releaseme return S_OK; } //*************************************************************************** // // GetAceStylePath // // Takes a parsed object path and converts it into "authority|name" // format. // // PARAMETERS: // // pOutput parsed path object // pToBeDeleted Set to newly allocated string. Call must free if // this routine return S_OK // // RETURN VALUES: // // S_OK if all is well, else an error code // //*************************************************************************** HRESULT GetAceStylePath(ParsedObjectPath* pOutput, LPWSTR *pToBeDeleted) { LPWSTR pRet; int iLen; int iAuthLen = 0; int iNameLen = 0; KeyRef* pAuth; KeyRef* pName; if(pOutput == NULL || pToBeDeleted == NULL) return WBEM_E_INVALID_PARAMETER; if(wbem_wcsicmp(pOutput->m_pClass, L"__ntlmuser") && wbem_wcsicmp(pOutput->m_pClass, L"__ntlmgroup")) return WBEM_E_INVALID_OBJECT_PATH; if(pOutput->m_bSingletonObj || pOutput->m_dwNumKeys < 1 || pOutput->m_dwNumKeys > 2) return WBEM_E_INVALID_OBJECT_PATH; // The authority key is optional if(pOutput->m_dwNumKeys == 1) { pAuth = NULL; pName = pOutput->m_paKeys[0]; } else { // Determine which order the keys are in pAuth = pOutput->m_paKeys[0]; pName = pOutput->m_paKeys[1]; if(wbem_wcsicmp(pAuth->m_pName, L"Authority")) { pAuth = pOutput->m_paKeys[1]; pName = pOutput->m_paKeys[0]; } } // do some more checking if((pAuth && wbem_wcsicmp(pAuth->m_pName, L"Authority")) || wbem_wcsicmp(pName->m_pName, L"Name")) return WBEM_E_INVALID_OBJECT_PATH; if(pAuth && pAuth->m_vValue.vt == VT_BSTR && pAuth->m_vValue.bstrVal != 0) iAuthLen = wcslen(pAuth->m_vValue.bstrVal); // SEC:REVIEWED 2002-03-22 : OK, prior logic assures NULL else iAuthLen = 1; // assume a "." if(pName->m_vValue.vt == VT_BSTR && pName->m_vValue.bstrVal != 0) iNameLen = wcslen(pName->m_vValue.bstrVal); // SEC:REVIEWED 2002-03-22 : OK, prior logic assures NULL if(iNameLen == 0 || iAuthLen == 0) return WBEM_E_INVALID_OBJECT_PATH; // allocate some memory iLen = 2 + iNameLen + iAuthLen; pRet = new WCHAR[iLen]; if(pRet == NULL) return WBEM_E_OUT_OF_MEMORY; if(pAuth && pAuth->m_vValue.vt == VT_BSTR && pAuth->m_vValue.bstrVal != 0) StringCchCopyW(pRet, iLen, pAuth->m_vValue.bstrVal); else StringCchCopyW(pRet, iLen, L"."); StringCchCatW(pRet, iLen, L"|"); StringCchCatW(pRet, iLen, pName->m_vValue.bstrVal); *pToBeDeleted = pRet; return S_OK; } //*************************************************************************** // // CCombinedAce::CCombinedAce // // Constructor. Since there might be several ACEs for a single user or // group in the ACL, this structure is used to combine all the aces for a // sid into one. // // PARAMETERS: // // pwszName User/group name. // //*************************************************************************** CCombinedAce::CCombinedAce(WCHAR *pwszName) { m_dwAllow = 0; m_dwDeny = 0; m_BadAce = false; m_wcFullName = NULL; if(pwszName) { DUP_STRING_NEW(m_wcFullName, pwszName); } } //*************************************************************************** // // CCombinedAce::AddToMasks // // Since this class is used to combine possibly many ACEs into a single // entry, this is called each time an ACE needs to be "OR"ed in. // // PARAMETERS: // // pAce Pointer to the ace to be combined. // //*************************************************************************** void CCombinedAce::AddToMasks(CBaseAce * pAce) { if(pAce == NULL) return; // Only aces with the container inherit bit can // be translated back. if(pAce->GetFlags() != CONTAINER_INHERIT_ACE) m_BadAce = true; if(pAce->GetType() == ACCESS_ALLOWED_ACE_TYPE) m_dwAllow |= pAce->GetAccessMask(); else m_dwDeny |= pAce->GetAccessMask(); return; } //*************************************************************************** // // CCombinedAce::IsValidOldEntry // // Checks a combined ace and determines if it could be converted into // an old style object. // // PARAMETERS: // // bIsGroup Set to true if the entry is for a group. // // RETURN VALUES: // // TRUE if the entry would make a valid instance // //*************************************************************************** bool CCombinedAce::IsValidOldEntry(bool bIsGroup) { // If we have detected incompatible flags, all is done! if(m_BadAce) return false; if(bIsGroup && m_dwDeny) return false; // group denies were not supported if(m_dwDeny && m_dwAllow) return false; // can not mix allows and denies in old system if(m_dwDeny) { return true; } DWORD dwOldAllow = WBEM_ENABLE | WBEM_REMOTE_ACCESS | WBEM_WRITE_PROVIDER; DWORD dwMask = m_dwDeny; if(dwMask == 0) dwMask = m_dwAllow; // all these must be set if it is an allow. DWORD dwTemp = dwMask; dwTemp &= dwOldAllow; if(m_dwAllow != 0 && dwTemp != dwOldAllow) return false; // cant have full repository write without partial if((dwMask & WBEM_FULL_WRITE_REP) != 0 && (dwMask & WBEM_PARTIAL_WRITE_REP) == 0) return false; return true; } //*************************************************************************** // // CCombinedAce::GetNames // // Retrieves the account name and authority // // PARAMETERS: // // pwszAccount Set to newly allocated string, Caller must free if success // pwszDomain Set to newly allocated string, Caller must free if success // // RETURN VALUES: // // S_OK if all is well, else an error code // //*************************************************************************** HRESULT CCombinedAce::GetNames(LPWSTR & pwszAccount, LPWSTR &pwszDomain) { pwszAccount = 0; pwszDomain = 0; // find the position of the '|' if(m_wcFullName == 0) return WBEM_E_FAILED; WCHAR * pwcSeparator; try { for(pwcSeparator = m_wcFullName; *pwcSeparator && *pwcSeparator != L'|'; pwcSeparator++); // SEC:REVIEWED 2002-03-22 : OK } catch(...) { return WBEM_E_FAILED; } DWORD dwLenDomain; DWORD dwLenUser; bool bUseDotDomain = false; if(*pwcSeparator == 0) { return WBEM_E_FAILED; } if(pwcSeparator == m_wcFullName) { bUseDotDomain = true; dwLenDomain = 2; } else { dwLenDomain = pwcSeparator - m_wcFullName + 1; } dwLenUser = wcslen(pwcSeparator); // SEC:REVIEWED 2002-03-22 : OK; prior logic assures NULL if(dwLenUser == 0) return WBEM_E_INVALID_OBJECT_PATH; // Allocate space for the two strings pwszAccount = new WCHAR[dwLenUser]; if(pwszAccount == NULL) return WBEM_E_OUT_OF_MEMORY; StringCchCopyW(pwszAccount, dwLenUser, pwcSeparator+1); pwszDomain = new WCHAR[dwLenDomain]; if(pwszDomain == NULL) { delete pwszAccount; pwszAccount = 0; return WBEM_E_OUT_OF_MEMORY; } if(bUseDotDomain) StringCchCopyW(pwszDomain, dwLenDomain, L"."); else { StringCchCopyW(pwszDomain, dwLenDomain, m_wcFullName); } return S_OK; } //*************************************************************************** // // RootSD::RootSD // // Constructor. This class contains a pointer to the root namespace and the // flex array of aces. // //*************************************************************************** RootSD::RootSD() { m_bOK = false; m_pFlex = NULL; m_pRoot = CWbemNamespace::CreateInstance(); if(m_pRoot == NULL) return; HRESULT hRes = m_pRoot->Initialize(L"root", L"Administrator", 0 , FULL_RIGHTS, true, false, NULL, 0xFFFFFFFF, FALSE, NULL); if (FAILED(hRes)) { m_pRoot->Release(); m_pRoot = NULL; return; } else if (m_pRoot->GetStatus() != 0) { m_pRoot->Release(); m_pRoot = NULL; return; } m_pRoot->AddRef(); // Get the security descriptor m_pFlex = NULL; SCODE sc = m_pRoot->GetAceList(&m_pFlex); if(!FAILED(sc)) m_bOK = true; } //*************************************************************************** // // RootSD::~RootSD // // Destructor. // //*************************************************************************** RootSD::~RootSD() { if(m_pRoot) m_pRoot->Release(); if(m_pFlex) delete m_pFlex; } //*************************************************************************** // // RootSD::StoreAceList() // // Stores the Ace list back into the db. // // RETURN VALUES: // // S_OK if all is well, else an error code // //*************************************************************************** HRESULT RootSD::StoreAceList() { if(m_bOK && m_pFlex && m_pRoot) m_pRoot->PutAceList(m_pFlex); return S_OK; } //*************************************************************************** // // RootSD::RemoveMatchingEntries // // Goes through the ACE list and removes all entries that match the name // // PARAMETERS: // // pwszAccountName Name of the account to be deleted // // RETURN VALUES: // // S_OK if all is well, else an error code // //*************************************************************************** HRESULT RootSD::RemoveMatchingEntries(LPWSTR pwszAccountName) { if(!m_bOK || m_pFlex == NULL) return WBEM_E_FAILED; for(int iPos = m_pFlex->Size()-1; iPos >= 0; iPos--) { CBaseAce * pListAce = (CBaseAce *)m_pFlex->GetAt(iPos); WCHAR * pwszAceListUserName; if(pListAce == NULL) continue; HRESULT hr = pListAce->GetFullUserName2(&pwszAceListUserName); if(FAILED(hr)) return hr; CDeleteMe dm1(pwszAceListUserName); if(wbem_wcsicmp(pwszAceListUserName, pwszAccountName) == 0) { delete pListAce; m_pFlex->RemoveAt(iPos); } } return S_OK; } //*************************************************************************** // // OldSecList::OldSecList // // Constructor. This class contains the list of combined entries for the // aces in the root namespace. Note that the list is of just the users, // or just the groups. // //*************************************************************************** OldSecList::OldSecList(bool bGroups) { // Attach up to the root namespace RootSD rsd; if(!rsd.IsOK()) return; // empty list // Get the Security namespace CFlexAceArray * pRootNsAceList = rsd.GetAceList(); if(pRootNsAceList == NULL) return; // For each ACE in the root namespace list for(int iAce = 0; iAce < pRootNsAceList->Size(); iAce++) { // Search the entries in the combined list to see if there is already one for // this ace CBaseAce * pAce = (CBaseAce *)pRootNsAceList->GetAt(iAce); WCHAR * pwszAceListUserName; if(pAce == NULL) continue; CNtAce * pNtAce = (CNtAce *)pAce; CNtSid sid; pNtAce->GetSid(sid); DWORD dwUsage; LPWSTR pAccount = NULL; LPWSTR pDomain = NULL; if(sid.GetInfo(&pAccount, &pDomain, &dwUsage)) continue; delete pAccount; delete pDomain; if(dwUsage == SidTypeUser && bGroups) continue; if(dwUsage != SidTypeUser && !bGroups) continue; HRESULT hr = pAce->GetFullUserName2(&pwszAceListUserName); if(FAILED(hr)) continue; CDeleteMe dm1(pwszAceListUserName); bool bExisting = false; for(int iMergedEntry = 0; iMergedEntry < m_MergedAceList.Size(); iMergedEntry++) { CCombinedAce * pCombinedEntry = (CCombinedAce *)m_MergedAceList.GetAt(iMergedEntry); if(pCombinedEntry) { if(wbem_wcsicmp(pwszAceListUserName, pCombinedEntry->m_wcFullName) == 0 ) { bExisting = true; pCombinedEntry->AddToMasks(pAce); break; } } } // If necessary add a new entry if(!bExisting) { CCombinedAce * pCombinedEntry = new CCombinedAce(pwszAceListUserName); if(pCombinedEntry == NULL) return; pCombinedEntry->AddToMasks(pAce); if (CFlexArray::no_error != m_MergedAceList.Add((void *)pCombinedEntry)) { //throw CX_Exception(); } } } } //*************************************************************************** // // OldSecList::~OldSecList() // // Destructor. // //*************************************************************************** OldSecList::~OldSecList() { // Delete the stuff used in the entry list. for(int iCnt = m_MergedAceList.Size() - 1; iCnt >= 0; iCnt--) { CCombinedAce * pCombinedEntry = (CCombinedAce *)m_MergedAceList.GetAt(iCnt); delete pCombinedEntry; } } //*************************************************************************** // // OldSecList::GetValidCombined // // Returns a combined entry at the specified index, but only if it is valid. // // PARAMETERS: // // iIndex Index into the array, 0 is the first // bGroup true if a group entry is desired. // // RETURN VALUES: // // If all is well, a pointer to the combined entry. NULL indicates failure. // //*************************************************************************** CCombinedAce * OldSecList::GetValidCombined(int iIndex, bool bGroup) { if(iIndex < 0 || iIndex >= m_MergedAceList.Size()) return NULL; // Get the entry CCombinedAce * pCombinedEntry = (CCombinedAce *)m_MergedAceList.GetAt(iIndex); if(pCombinedEntry == NULL) return NULL; // verify that the entry can be translated back into an old security setting if(pCombinedEntry->IsValidOldEntry(bGroup)) return pCombinedEntry; else { DEBUGTRACE((LOG_WBEMCORE, "Invalid ace entry combination encountered, name = %S, " "allow=0x%x, deny=0x%x, flag validity=&d", pCombinedEntry->m_wcFullName, pCombinedEntry->m_dwAllow, pCombinedEntry->m_dwDeny, pCombinedEntry->m_BadAce)); return NULL; } } //*************************************************************************** // // OldSecList::GetValidCombined // // Returns a combined entry which matches the name, but only if it is valid. // // PARAMETERS: // // pName Name to be found. It is of the format "authority|name" // bGroup true if a group entry is desired. // // RETURN VALUES: // // If all is well, a pointer to the combined entry. NULL indicates failure. // //*************************************************************************** CCombinedAce * OldSecList::GetValidCombined(LPWSTR pName, bool bGroup) { if(pName == NULL) return NULL; // Go through the list and look for the matching entry int iCnt; CCombinedAce * pCombinedEntry; for(iCnt = 0; iCnt < m_MergedAceList.Size(); iCnt++) { pCombinedEntry = (CCombinedAce *)m_MergedAceList.GetAt(iCnt); if(pCombinedEntry && !wbem_wcsicmp(pCombinedEntry->m_wcFullName, pName)) break; } if(iCnt == m_MergedAceList.Size()) return NULL; // verify that the entry can be translated back into an old security setting if(pCombinedEntry->IsValidOldEntry(bGroup)) return pCombinedEntry; else { DEBUGTRACE((LOG_WBEMCORE, "Invalid ace entry combination encountered, name = %S, " "allow=0x%x, deny=0x%x, flag validity=&d", pCombinedEntry->m_wcFullName, pCombinedEntry->m_dwAllow, pCombinedEntry->m_dwDeny, pCombinedEntry->m_BadAce)); return NULL; } } //*************************************************************************** // // CWbemNamespace::EnumerateSecurityClassInstances // // Equivalent the a CreateInstanceEnumAsync call. // // PARAMETERS: // // wszClassName class name // pSink Where to indicate the value // pContext pointer to the context object. // lFlags flags. // // RETURN VALUES: // // S_OK if all is well, else an error code // //*************************************************************************** HRESULT CWbemNamespace::EnumerateSecurityClassInstances(LPWSTR wszClassName, IWbemObjectSink* pSink, IWbemContext* pContext, long lFlags) { SCODE sc = WBEM_E_FAILED; IWbemClassObject FAR* pObj = NULL; // Do sanity check of arguments. if(pSink == NULL || wszClassName == NULL ) // Should not happen return WBEM_E_INVALID_PARAMETER; // Get the class object IWbemClassObject * pClass = NULL; sc = GetObject(wszClassName, 0, pContext, &pClass, NULL); if(sc != S_OK) { return sc; } CReleaseMe rm(pClass); bool bGroup = false; if(wbem_wcsicmp(L"__ntlmgroup", wszClassName) == 0) bGroup = true; OldSecList osl(bGroup); sc = S_OK; for(int i = 0; i < osl.Size(); i++) { IWbemClassObject * pObj = NULL; CCombinedAce * pCombinedEntry = osl.GetValidCombined(i, bGroup); if(pCombinedEntry) { sc = CreateTheInstance(&pObj, pClass, pCombinedEntry); if(sc == S_OK) { if (SUCCEEDED(sc = DecorateObject(pObj))) { pSink->Indicate(1,&pObj); } pObj->Release(); } } } // Set status, all done return sc; } //*************************************************************************** // // CWbemNamespace::PutSecurityClassInstances // // Equivalent to a PutInstanceAsync call. // // PARAMETERS: // // wszClassName class name // pObj Object to be "put" // pSink where to SetStatus // pContext pointer to the context object // lFlags flags // // RETURN VALUES: // // S_OK if all is well, else an error code // //*************************************************************************** HRESULT CWbemNamespace::PutSecurityClassInstances(LPWSTR wszClassName, IWbemClassObject * pObj, IWbemObjectSink* pSink, IWbemContext* pContext, long lFlags) { // Check the args if(wszClassName == NULL || pObj == NULL || pSink == NULL) { pSink->SetStatus(0,WBEM_E_INVALID_PARAMETER,NULL,NULL); return S_OK; } RootSD rsd; if(!rsd.IsOK()) { pSink->SetStatus(0,WBEM_E_FAILED,NULL,NULL); return S_OK; } // Get the Security namespace CFlexAceArray * pRootNsAceList = rsd.GetAceList(); bool bGroup = false; if(wbem_wcsicmp(L"__ntlmgroup", wszClassName) == 0) bGroup = true; // Convert to new sid CBaseAce * pAce = ConvertOldObjectToAce(pObj, bGroup); if(pAce == NULL) { pSink->SetStatus(0,WBEM_E_INVALID_OBJECT,NULL,NULL); return S_OK; } // Delete all entries with the same name WCHAR * pwszAccountName; HRESULT hr = pAce->GetFullUserName2(&pwszAccountName); if(FAILED(hr)) { pSink->SetStatus(0,hr,NULL,NULL); return S_OK; } CDeleteMe dm1(pwszAccountName); rsd.RemoveMatchingEntries(pwszAccountName); // Add the new entries pRootNsAceList->Add(pAce); // Put Sid back hr = rsd.StoreAceList(); pSink->SetStatus(0,hr,NULL,NULL); return S_OK; } //*************************************************************************** // // CWbemNamespace::DeleteSecurityClassInstances // // Equivalent to a DeleteInstanceAsync routine // // PARAMETERS: // // pParsedPath parsed object path // pSink where to SetStatus // pContext pointer to the context object // lFlags flags // // RETURN VALUES: // // S_OK if all is well, else an error code // //*************************************************************************** HRESULT CWbemNamespace::DeleteSecurityClassInstances(ParsedObjectPath* pParsedPath, IWbemObjectSink* pSink, IWbemContext* pContext, long lFlags) { // Check the args if(pParsedPath == NULL || pSink == NULL || pSink == NULL) return WBEM_E_INVALID_PARAMETER; // Parse the path and contruct the domain|user string LPWSTR pAcePath = NULL; HRESULT hr = GetAceStylePath(pParsedPath, &pAcePath); if(FAILED(hr)) { pSink->SetStatus(0,hr,NULL,NULL); return S_OK; } CDeleteMe dm(pAcePath); // Delete the entries RootSD rsd; if(!rsd.IsOK()) hr = WBEM_E_FAILED; else { CFlexAceArray * pRootNsAceList = rsd.GetAceList(); int iOriginalSize = pRootNsAceList->Size(); // Delete all entries with the same name rsd.RemoveMatchingEntries(pAcePath); int iNewSize = pRootNsAceList->Size(); if(iNewSize < iOriginalSize) hr = rsd.StoreAceList(); else hr = WBEM_E_NOT_FOUND; } pSink->SetStatus(0,hr,NULL,NULL); return S_OK; } //*************************************************************************** // // CWbemNamespace::GetSecurityClassInstances // // Equivalent to a GetObjectAsync call. // // PARAMETERS: // // pParsedPath parsed object path // pSink where to SetStatus // pContext pointer to the context object // lFlags flags // // RETURN VALUES: // // S_OK if all is well, else an error code // //*************************************************************************** HRESULT CWbemNamespace::GetSecurityClassInstances(ParsedObjectPath* pParsedPath, CBasicObjectSink* pSink, IWbemContext* pContext,long lFlags) { // Check the args if(pParsedPath == NULL|| pSink == NULL) return WBEM_E_INVALID_PARAMETER; // Parse the path and contruct the domain|user string LPWSTR pAcePath = NULL; HRESULT hr = GetAceStylePath(pParsedPath, &pAcePath); if(FAILED(hr)) { pSink->SetStatus(0,hr,NULL,NULL); return S_OK; } CDeleteMe dm(pAcePath); IWbemClassObject * pClass = NULL; SCODE sc = GetObject(pParsedPath->m_pClass, 0, pContext, &pClass, NULL); if(sc != S_OK) { pSink->SetStatus(0,sc,NULL, NULL); return S_OK; } CReleaseMe rm(pClass); bool bGroup = false; if(wbem_wcsicmp(L"__ntlmgroup", pParsedPath->m_pClass) == 0) bGroup = true; OldSecList osl(bGroup); IWbemClassObject * pObj = NULL; CCombinedAce * pCombinedEntry = osl.GetValidCombined(pAcePath, bGroup); if(pCombinedEntry == NULL) { pSink->SetStatus(0, WBEM_E_INVALID_OBJECT_PATH, NULL, NULL); return S_OK; } sc = CreateTheInstance(&pObj, pClass, pCombinedEntry); if(sc == S_OK) // not all entries make for valid old entries, so failure is common { if (SUCCEEDED(sc = DecorateObject(pObj))) { pSink->Indicate(1,&pObj); } pObj->Release(); } // Set status, all done pSink->SetStatus(0,sc,NULL, NULL); return S_OK; }