/*--------------------------------------------------------------------------- File: SetDetector.cpp Comments: implementation of COM object to detect whether a set of users and groups forms a closed set. This is used by the GUI, and the engine to determine whether ReACLing must be done for intra-forest, mixed-mode source domain, incremental migrations. (c) Copyright 1999, Mission Critical Software, Inc., All Rights Reserved Proprietary and confidential to Mission Critical Software, Inc. REVISION LOG ENTRY Revision By: Christy Boles Revised on 07/01/99 --------------------------------------------------------------------------- */// SetDetector.cpp : Implementation of CMcsClosedSetApp and DLL registration. #include "stdafx.h" #include "ClSet.h" #include "Detector.h" #include "Common.hpp" #include "UString.hpp" #include "TNode.hpp" #include "ResStr.h" #include #include #include #import "\bin\NetEnum.tlb" no_namespace, named_guids class TItemNode : public TNode { _bstr_t m_name; long m_iter; long m_status; long m_nLinks; TItemNode ** m_Links; public: TItemNode(WCHAR const * name) { m_name = name; m_Links = NULL; m_status = 0; m_iter = 0; m_nLinks = 0; } void SetIter(long val) { m_iter = val; } void SetStatus(long val) { m_status = val; } long GetStatus() { return m_status; } long GetIter() { return m_iter; } WCHAR const * GetName() { return (WCHAR const *)m_name; } }; class TLinkTree : public TNodeListSortable { static int CompNode(const TNode * n1, const TNode * n2) { TItemNode * t1 = (TItemNode *)n1; TItemNode * t2 = (TItemNode *)n2; return UStrICmp(t1->GetName(),t2->GetName()); } static int CompValue(const TNode * n, const void * v) { TItemNode * p = (TItemNode *)n; WCHAR const * name = (WCHAR const *) v; return UStrICmp(p->GetName(),name); } public: TLinkTree() { TypeSetTree(); CompareSet(&TLinkTree::CompNode); } ~TLinkTree() { ToSorted(); DeleteAllListItems(TItemNode); } TItemNode * Lookup(WCHAR const * name) { return (TItemNode *)Find(&TLinkTree::CompValue,name); } }; ///////////////////////////////////////////////////////////////////////////// // HRESULT AddToList( TLinkTree * pUsers, TLinkTree * pGroups, WCHAR const * domain, WCHAR const * pName ) { HRESULT hr = S_OK; IADs * pADs = NULL; WCHAR path[MAX_PATH]; BSTR bstrClass = NULL; TItemNode * pNode = NULL; swprintf(path,L"LDAP://%s/%s",domain,pName); hr = ADsGetObject(path,IID_IADs,(void**)&pADs); if ( SUCCEEDED(hr) ) { // get the object class hr = pADs->get_Class(&bstrClass); if ( SUCCEEDED(hr) ) { if (!UStrICmp((WCHAR*)bstrClass,L"user") ) { // add it to the users list pNode = new TItemNode(path); pUsers->Insert(pNode); } else if ( !UStrICmp((WCHAR*)bstrClass,L"group") ) { // add it to the groups list pNode = new TItemNode(path); pGroups->Insert(pNode); } else { // we only support users and groups! MCSASSERT(FALSE); hr = E_NOTIMPL; } SysFreeString(bstrClass); } pADs->Release(); } return hr; } STDMETHODIMP CSetDetector::IsClosedSet( BSTR domain, /*[in]*/ LONG nItems, /*[in]*/ BSTR pNames[], /*[in,size_is(nItems)]*/ BOOL * bIsClosed, /*[out]*/ BSTR * pReason /*[out]*/ ) { HRESULT hr = S_OK; long i; TLinkTree users; TLinkTree groups; BOOL bClosed = TRUE; WCHAR path[MAX_PATH]; // initialize output variables (*bIsClosed) = FALSE; (*pReason) = NULL; // sort through the list of items and add them to the trees for ( i = 0 ; i < nItems ; i++ ) { // for each item, get an IADs pointer to it and add it to the tree hr = AddToList(&users,&users,domain,pNames[i]); } // now iterate through the user's tree checking the group memberships of each user TNodeTreeEnum e; TItemNode * pUser; IADsUser * pADs = NULL; IADsContainer * pCont = NULL; INetObjEnumeratorPtr pEnum; VARIANT member; VARIANT memberOf; IEnumVARIANT * pEnumerator = NULL; WCHAR reason[1000] = L""; VariantInit(&member); VariantInit(&memberOf); hr = pEnum.CreateInstance(CLSID_NetObjEnumerator); if ( SUCCEEDED(hr) ) { for ( i = 0 , pUser = (TItemNode*)e.OpenFirst(&users) ; pUser && bClosed ; pUser = (TItemNode*)e.Next() , i++) { // get the group memberships for the user swprintf(path,L"LDAP://%s/%s",domain,pNames[i]); SAFEARRAYBOUND bound[1] = { { 3, 0 } }; SAFEARRAY * pArray = SafeArrayCreate(VT_BSTR,1,bound); hr = pEnum->SetQuery(path,domain,L"(objectClass=*)",ADS_SCOPE_BASE,TRUE); if ( SUCCEEDED(hr) ) { long ndx[1]; ndx[0] = 0; SafeArrayPutElement(pArray,ndx,SysAllocString(L"member")); ndx[0] = 1; SafeArrayPutElement(pArray,ndx,SysAllocString(L"memberOf")); ndx[0] = 2; SafeArrayPutElement(pArray,ndx,SysAllocString(L"distinguishedName")); hr = pEnum->SetColumns((long)pArray); } if ( SUCCEEDED(hr) ) { hr = pEnum->Execute(&pEnumerator); } if ( SUCCEEDED(hr) ) { unsigned long count = 0; while ( bClosed && hr != S_FALSE ) { hr = pEnumerator->Next(1,&member,&count); // break if there was an error, or Next returned S_FALSE if ( hr != S_OK ) break; // see if this is an array if ( member.vt == ( VT_ARRAY | VT_VARIANT ) ) { pArray = member.parray; VARIANT * pData; WCHAR path2[MAX_PATH]; SafeArrayAccessData(pArray,(void**)&pData); // pData[0] has the members list if ( pData[0].vt == ( VT_ARRAY | VT_VARIANT) ) { // enumerate the members, checking to see if each one is in the list SAFEARRAY * pMembers = pData[0].parray; VARIANT * pMemVar = NULL; long count; SafeArrayGetUBound(pMembers,1,&count); SafeArrayAccessData(pMembers,(void**)&pMemVar); for ( long i = 0 ; i <= count ; i++ ) { swprintf(path2,L"LDAP://%ls/%ls",domain,pMemVar[i].bstrVal); // check each member to see if it is in the tree if ( ! users.Lookup(path2) ) { WCHAR * args[2]; args[0] = path; args[1] = pMemVar[i].bstrVal; FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY, GET_STRING(IDS_CLSET_MEMBER_NOT_INCLUDED),0,0,reason,DIM(reason),(char **)args); bClosed = FALSE; break; } } SafeArrayUnaccessData(pMembers); } else { // there may be just a single member if ( pData[0].vt == VT_BSTR && UStrLen(pData[0].bstrVal)) { swprintf(path2,L"LDAP://%ls/%ls",domain,pData[0].bstrVal); // check each member to see if it is in the tree if ( ! users.Lookup(path2) ) { WCHAR * args[2]; args[0] = path; args[1] = pData[0].bstrVal; FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY, GET_STRING(IDS_CLSET_MEMBER_NOT_INCLUDED),0,0,reason,DIM(reason),(char **)args); bClosed = FALSE; break; } } } // pData[1] has the memberOf list if ( pData[1].vt == ( VT_ARRAY | VT_VARIANT) ) { // enumerate the member-ofs, checking to see if each one is in the list // enumerate the members, checking to see if each one is in the list SAFEARRAY * pMembers = pData[1].parray; VARIANT * pMemVar = NULL; long count; hr = SafeArrayGetUBound(pMembers,1,&count); hr = SafeArrayAccessData(pMembers,(void**)&pMemVar); for ( long i = 0 ; i <= count ; i++ ) { swprintf(path2,L"LDAP://%ls/%ls",domain,pMemVar[i].bstrVal); // check each member to see if it is in the tree if ( ! users.Lookup(path2) ) { WCHAR * args[2]; args[0] = path; args[1] = pMemVar[i].bstrVal; FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY, GET_STRING(IDS_CLSET_GROUP_NOT_INCLUDED),0,0,reason,DIM(reason),(char **)args); bClosed = FALSE; break; } } SafeArrayUnaccessData(pMembers); } else { // there may be just a single entry if ( pData[1].vt == VT_BSTR && UStrLen(pData[1].bstrVal)) { swprintf(path2,L"LDAP://%ls/%ls",domain,pData[1].bstrVal); // check each member to see if it is in the tree if ( ! users.Lookup(path2) ) { WCHAR * args[2]; args[0] = path; args[1] = pData[1].bstrVal; FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY, GET_STRING(IDS_CLSET_GROUP_NOT_INCLUDED),0,0,reason,DIM(reason),(char **)args); bClosed = FALSE; break; } } } SafeArrayUnaccessData(pArray); } } pEnumerator->Release(); VariantInit(&member); } } e.Close(); } (*bIsClosed) = bClosed; if ( ! bClosed ) { (*pReason) = SysAllocString(reason); } return hr; }