334 lines
12 KiB
C++
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;
|
||
|
}
|