WindowsXP-SP1/admin/admt/setdtct/detector.cpp
2020-09-30 16:53:49 +02:00

334 lines
12 KiB
C++

/*---------------------------------------------------------------------------
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 <comdef.h>
#include <iads.h>
#include <adshlp.h>
#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;
}