Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

864 lines
18 KiB

//---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1995
//
// File: cggi.cxx
//
// Contents: This file contains the Group Object's
// IADsGroup and IADsGroupOperation methods
//
// History: 11-1-95 krishnag Created.
//
//----------------------------------------------------------------------------
#include "ldap.hxx"
#pragma hdrstop
#include <winldap.h>
#include "..\ldapc\ldpcache.hxx"
#include "..\ldapc\ldaputil.hxx"
#include "..\ldapc\parse.hxx"
HRESULT
BuildLDAPPathFromADsPath2(
LPWSTR szADsPathName,
LPWSTR *pszLDAPServer,
LPWSTR *pszLDAPDn,
DWORD * pdwPort
);
HRESULT
LdapGetSyntaxOfAttributeOnServer(
LPTSTR pszServerPath,
LPTSTR pszAttrName,
DWORD *pdwSyntaxId,
CCredentials& Credentials,
DWORD dwPort,
BOOL fFromServer = FALSE
);
HRESULT
ReadServerSupportsIsADControl(
LPWSTR pszLDAPServer,
BOOL * pfDomScopeSupported,
CCredentials& Credentials,
DWORD dwPort
);
BOOL
VerifyIfMember(
BSTR bstrMember,
VARIANT * VariantArray,
ULONG cElementFetched
);
HRESULT
ValidateProvider(
POBJECTINFO pObjectInfo
);
BOOL
MapLdapClassToADsClass(
LPTSTR *aLdapClasses,
int nCount,
LPTSTR pszADsClass
);
struct _classmapping
{
LPTSTR pszLdapClassName;
LPTSTR pszADsClassName;
} aClassMap[] =
{
{ TEXT("user"), USER_CLASS_NAME}, // NTDS
{ TEXT("group"), GROUP_CLASS_NAME},
{ TEXT("localGroup"), GROUP_CLASS_NAME},
{ TEXT("printQueue"), PRINTER_CLASS_NAME},
{ TEXT("country"), TEXT("Country") },
{ TEXT("locality"), TEXT("Locality") },
{ TEXT("organization"), TEXT("Organization")},
{ TEXT("organizationalUnit"), TEXT("Organizational Unit") },
{ TEXT("domain"), DOMAIN_CLASS_NAME},
{ TEXT("person"), USER_CLASS_NAME },
{ TEXT("organizationalPerson"), USER_CLASS_NAME },
{ TEXT("residentialPerson"), USER_CLASS_NAME },
{ TEXT("groupOfNames"), GROUP_CLASS_NAME },
{ TEXT("groupOfUniqueNames"), GROUP_CLASS_NAME }
};
// Class CLDAPGroup
STDMETHODIMP CLDAPGroup::get_Description(THIS_ BSTR FAR* retval)
{
GET_PROPERTY_BSTR((IADsGroup *)this, description);
}
STDMETHODIMP CLDAPGroup::put_Description(THIS_ BSTR bstrdescription)
{
PUT_PROPERTY_BSTR((IADsGroup *)this, description);
}
STDMETHODIMP
CLDAPGroup::Members(
THIS_ IADsMembers FAR* FAR* ppMembers
)
{
VARIANT v;
HRESULT hr = S_OK;
BSTR bstrParent = NULL;
BSTR bstrName = NULL;
BSTR bstrADsPath = NULL;
IADsObjOptPrivate *pPrivOpt = NULL;
BOOL fRangeRetrieval = FALSE;
VariantInit(&v);
hr = get_VARIANT_Property((IADs *) ((IADsGroup *) this),
TEXT("member"),
&v );
if ( hr == E_ADS_PROPERTY_NOT_FOUND )
{
SAFEARRAY *aList = NULL;
SAFEARRAYBOUND aBound;
hr = S_OK;
aBound.lLbound = 0;
aBound.cElements = 0;
aList = SafeArrayCreate( VT_VARIANT, 1, &aBound );
if ( aList == NULL )
{
hr = E_OUTOFMEMORY;
BAIL_ON_FAILURE(hr);
}
V_VT(&v) = VT_ARRAY | VT_VARIANT;
V_ARRAY(&v) = aList;
}
BAIL_ON_FAILURE(hr);
hr = get_Parent( &bstrParent );
BAIL_ON_FAILURE(hr);
hr = get_Name( &bstrName );
BAIL_ON_FAILURE(hr);
hr = _pADs->get_ADsPath( &bstrADsPath);
BAIL_ON_FAILURE(hr);
//
// We need to see if range retrieval was used.
// That info is needed in the enumerator.
//
hr = _pADs->QueryInterface(
IID_IADsObjOptPrivate,
(void **)&pPrivOpt
);
BAIL_ON_FAILURE(hr);
//
// Not a problem if this fails.
//
hr = pPrivOpt->GetOption (
LDAP_MEMBER_HAS_RANGE,
(void *) &fRangeRetrieval
);
hr = CLDAPGroupCollection::CreateGroupCollection(
bstrParent,
bstrADsPath,
bstrName,
&v,
_Credentials,
_pADs,
IID_IADsMembers,
fRangeRetrieval,
(void **)ppMembers
);
BAIL_ON_FAILURE(hr);
error:
if ( bstrParent )
ADsFreeString( bstrParent );
if ( bstrName )
ADsFreeString( bstrName );
if (bstrADsPath) {
ADsFreeString( bstrADsPath);
}
if (pPrivOpt) {
pPrivOpt->Release();
}
VariantClear(&v);
RRETURN(hr);
}
STDMETHODIMP
CLDAPGroup::IsMember(
THIS_ BSTR bstrMember,
VARIANT_BOOL FAR *bMember
)
{
HRESULT hr = S_OK;
if (_dwServerType == SERVER_TYPE_UNKNOWN) {
hr = UpdateServerType();
//
// The only reason the above call shoudl fail is
// if we could not read the ADsPath of the cgenobj.
//
BAIL_ON_FAILURE(hr);
}
if (_dwServerType == SERVER_TYPE_AD) {
hr = IsMemberOnAD(
bstrMember,
bMember
);
}
else {
hr = IsMemberOnOther(
bstrMember,
bMember
);
}
error:
RRETURN(hr);
}
//
// Checks membership if the server is AD. This is because
// we know that AD supports the LDAPCompare operation. There
// is just one round trip on the wire this time.
//
HRESULT
CLDAPGroup::IsMemberOnAD(
THIS_ BSTR bstrMember,
VARIANT_BOOL FAR *bMember
)
{
HRESULT hr = S_OK;
PADSLDP pLdp = NULL;
BSTR bstrParentADsPath = NULL;
IADsObjOptPrivate *pADsPrivateObjectOptions = NULL;
LPWSTR pszGroupServer = NULL, pszGroupDn = NULL;
LPWSTR pszMemberServer = NULL, pszMemberDn = NULL;
DWORD dwGroupPort = 0, dwMemberPort = 0;
if (!bstrMember || !*bstrMember || !bMember) {
BAIL_ON_FAILURE(hr = E_ADS_BAD_PARAMETER);
}
//
// Default to this value.
//
*bMember = VARIANT_FALSE;
//
// We need the ADsPath of the parent group object.
// Since the input parameter is an ADsPath, we need to
// make sure that the serverName if any matches before
// going onto doing the LDAPCompare operation on the
// DN to verify if the DN is part of member.
//
hr = _pADs->get_ADsPath(&bstrParentADsPath);
BAIL_ON_FAILURE(hr);
//
// Split the path into components we are interesteed in.
//
hr = BuildLDAPPathFromADsPath2(
bstrParentADsPath,
&pszGroupServer,
&pszGroupDn,
&dwGroupPort
);
BAIL_ON_FAILURE(hr);
hr = BuildLDAPPathFromADsPath2(
bstrMember,
&pszMemberServer,
&pszMemberDn,
&dwMemberPort
);
BAIL_ON_FAILURE(hr);
if ((pszMemberServer && !pszGroupServer)
|| (pszGroupServer && !pszMemberServer)
|| (dwMemberPort != dwGroupPort)
|| ( (pszMemberServer && pszGroupServer)
#ifdef WIN95
&& (_wcsicmp(pszMemberServer, pszGroupServer))
#else
&& (CompareStringW(
LOCALE_SYSTEM_DEFAULT,
NORM_IGNORECASE,
pszMemberServer,
-1,
pszGroupServer,
-1
) != CSTR_EQUAL)
#endif
)
) {
//
// Mismatched paths (e.g., bound to group with a serverless
// path, user is passing in a server path)
//
*bMember = VARIANT_FALSE;
hr = E_ADS_BAD_PARAMETER;
goto error;
}
//
// At this point we have a match on the server names and port.
//
hr = _pADs->QueryInterface(
IID_IADsObjOptPrivate,
(void **)&pADsPrivateObjectOptions
);
BAIL_ON_FAILURE(hr);
hr = pADsPrivateObjectOptions->GetOption (
LDP_CACHE_ENTRY,
&pLdp
);
BAIL_ON_FAILURE(hr);
//
// We can now do a LDAPCompare to see if the object is a member.
//
hr = LdapCompareExt(
pLdp,
pszGroupDn,
L"member",
pszMemberDn,
NULL, // Data
NULL, // ClientControls
NULL // ServerControls
);
if (hr == HRESULT_FROM_WIN32(ERROR_DS_COMPARE_FALSE)) {
hr = S_OK;
*bMember = VARIANT_FALSE;
}
else if (hr == HRESULT_FROM_WIN32(ERROR_DS_COMPARE_TRUE)) {
hr = S_OK;
*bMember = VARIANT_TRUE;
} else if (hr == HRESULT_FROM_WIN32(ERROR_DS_NO_ATTRIBUTE_OR_VALUE)) {
//
// This is also valid as the member attribute might be empty.
//
hr = S_OK;
*bMember = VARIANT_FALSE;
}
BAIL_ON_FAILURE(hr);
error:
//
// Cleanup all strings that could have been alloced.
//
if (bstrParentADsPath) {
ADsFreeString(bstrParentADsPath);
}
if (pszGroupServer) {
FreeADsStr(pszGroupServer);
}
if (pszGroupDn) {
FreeADsStr(pszGroupDn);
}
if (pszMemberServer) {
FreeADsStr(pszMemberServer);
}
if (pszMemberDn) {
FreeADsStr(pszMemberDn);
}
//
// Miscellaneous cleanup.
//
if (pADsPrivateObjectOptions) {
pADsPrivateObjectOptions->Release();
}
RRETURN(hr);
}
//
// This routine is used if the server is not AD - preserves
// older behaviour. It creates an Enumerator, goes through that
// comparing the paths to see if there is a match. This is
// pretty network intensive.
//
HRESULT
CLDAPGroup::IsMemberOnOther(
THIS_ BSTR bstrMember,
VARIANT_BOOL FAR* bMember
)
{
IADsMembers FAR * pMembers = NULL;
IUnknown FAR * pUnknown = NULL;
IEnumVARIANT FAR * pEnumVar = NULL;
DWORD i = 0;
HRESULT hr = S_OK;
VARIANT_BOOL fMember = FALSE;
VARIANT VariantArray[10];
BOOL fContinue = TRUE;
ULONG cElementFetched = 0;
hr = Members(
&pMembers
);
BAIL_ON_FAILURE(hr);
hr = pMembers->get__NewEnum(
&pUnknown
);
BAIL_ON_FAILURE(hr);
hr = pUnknown->QueryInterface(
IID_IEnumVARIANT,
(void **)&pEnumVar
);
BAIL_ON_FAILURE(hr);
while (fContinue) {
IADs *pObject ;
hr = pEnumVar->Next(
10,
VariantArray,
&cElementFetched
);
if (hr == S_FALSE) {
fContinue = FALSE;
//
// Reset hr to S_OK, we want to return success
//
hr = S_OK;
}
fMember = (VARIANT_BOOL)VerifyIfMember(
bstrMember,
VariantArray,
cElementFetched
);
if (fMember) {
fContinue = FALSE;
}
for (i = 0; i < cElementFetched; i++ ) {
IDispatch *pDispatch = NULL;
pDispatch = VariantArray[i].pdispVal;
pDispatch->Release();
}
memset(VariantArray, 0, sizeof(VARIANT)*10);
}
error:
*bMember = fMember? VARIANT_TRUE : VARIANT_FALSE;
if (pEnumVar) {
pEnumVar->Release();
}
if (pUnknown) {
pUnknown->Release();
}
if (pMembers) {
pMembers->Release();
}
RRETURN(hr);
}
BOOL
VerifyIfMember(
BSTR bstrMember,
VARIANT * VariantArray,
ULONG cElementFetched
)
{
DWORD i = 0;
HRESULT hr = S_OK;
IADs FAR * pObject = NULL;
IDispatch FAR * pDispatch = NULL;
for (i = 0; i < cElementFetched; i++ ) {
IDispatch *pDispatch = NULL;
BSTR bstrName = NULL;
pDispatch = VariantArray[i].pdispVal;
hr = pDispatch->QueryInterface(
IID_IADs,
(VOID **) &pObject
);
BAIL_ON_FAILURE(hr);
hr = pObject->get_ADsPath(&bstrName);
BAIL_ON_FAILURE(hr);
#ifdef WIN95
if (!_wcsicmp(bstrName, bstrMember)) {
#else
if (CompareStringW(
LOCALE_SYSTEM_DEFAULT,
NORM_IGNORECASE,
bstrName,
-1,
bstrMember,
-1
) == CSTR_EQUAL
) {
#endif
SysFreeString(bstrName);
bstrName = NULL;
pObject->Release();
return(TRUE);
}
SysFreeString(bstrName);
bstrName = NULL;
pObject->Release();
}
error:
return(FALSE);
}
STDMETHODIMP
CLDAPGroup::Add(THIS_ BSTR bstrNewItem)
{
RRETURN( ModifyGroup(bstrNewItem, TRUE ));
}
STDMETHODIMP
CLDAPGroup::Remove(THIS_ BSTR bstrItemToBeRemoved)
{
RRETURN( ModifyGroup(bstrItemToBeRemoved, FALSE ));
}
HRESULT
CLDAPGroup::ModifyGroup( THIS_ BSTR bstrItem, BOOL fAdd )
{
HRESULT hr = S_OK;
DWORD dwStatus = 0L;
TCHAR *pszLDAPServer = NULL;
TCHAR *pszItemLDAPServer = NULL;
TCHAR *pszLDAPDn = NULL;
TCHAR *pszItemLDAPDn = NULL;
BSTR bstrADsPath = NULL;
DWORD dwSyntaxId;
ADS_LDP * ld = NULL;
LDAPModW *aMod[2];
LDAPModW ldapmod;
WCHAR *aStrings[2];
OBJECTINFO ObjectInfo;
POBJECTINFO pObjectInfo = &ObjectInfo;
DWORD dwPort = 0;
if (!bstrItem || !*bstrItem) {
RRETURN(E_FAIL);
}
memset(pObjectInfo, 0, sizeof(OBJECTINFO));
pObjectInfo->ObjectType = TOKEN_LDAPOBJECT;
hr = ADsObject(bstrItem, pObjectInfo);
BAIL_ON_FAILURE(hr);
hr = ValidateProvider(pObjectInfo);
BAIL_ON_FAILURE(hr);
hr = BuildLDAPPathFromADsPath2(
bstrItem,
&pszItemLDAPServer,
&pszItemLDAPDn,
&dwPort
);
BAIL_ON_FAILURE(hr);
hr = get_ADsPath( &bstrADsPath );
BAIL_ON_FAILURE(hr);
hr = BuildLDAPPathFromADsPath2(
bstrADsPath,
&pszLDAPServer,
&pszLDAPDn,
&dwPort
);
BAIL_ON_FAILURE(hr);
hr = LdapGetSyntaxOfAttributeOnServer(
pszLDAPServer,
TEXT("member"),
&dwSyntaxId,
_Credentials,
pObjectInfo->PortNumber
);
BAIL_ON_FAILURE(hr);
hr = LdapOpenObject(
pszLDAPServer,
pszLDAPDn,
&ld,
_Credentials,
dwPort
);
BAIL_ON_FAILURE(hr);
aMod[0] = &ldapmod;
aMod[1] = NULL;
aStrings[0] = pszItemLDAPDn;
aStrings[1] = NULL;
ldapmod.mod_type = L"member";
ldapmod.mod_values = aStrings;
ldapmod.mod_op = fAdd? LDAP_MOD_ADD : LDAP_MOD_DELETE;
dwStatus = LdapModifyS(
ld,
pszLDAPDn,
aMod
);
if (dwStatus) {
hr = HRESULT_FROM_WIN32(dwStatus);
BAIL_ON_FAILURE(hr);
}
error:
FreeObjectInfo( &ObjectInfo );
if (pszItemLDAPServer)
FreeADsStr( pszItemLDAPServer );
if (pszItemLDAPDn) {
FreeADsStr(pszItemLDAPDn);
}
if (pszLDAPDn) {
FreeADsStr(pszLDAPDn);
}
if (pszLDAPServer)
FreeADsStr( pszLDAPServer );
if (bstrADsPath)
ADsFreeString( bstrADsPath );
if (ld) {
LdapCloseObject(ld);
}
RRETURN(hr);
}
HRESULT
CLDAPGroup::UpdateServerType()
{
HRESULT hr = S_OK;
BSTR bstrADsPath = NULL;
LPWSTR pszGroupServer = NULL;
LPWSTR pszGroupDn = NULL;
BOOL fServerIsAD = FALSE;
DWORD dwGroupPort = 0;
//
// Read the servertype only if we have not already done so.
//
if (_dwServerType == SERVER_TYPE_UNKNOWN) {
hr = _pADs->get_ADsPath( &bstrADsPath);
BAIL_ON_FAILURE(hr);
hr = BuildLDAPPathFromADsPath2(
bstrADsPath,
&pszGroupServer,
&pszGroupDn,
&dwGroupPort
);
BAIL_ON_FAILURE(hr);
hr = ReadServerSupportsIsADControl(
pszGroupServer,
&fServerIsAD,
_Credentials,
dwGroupPort
);
//
// Treat failure to mean server is not AD
//
if (FAILED(hr)) {
fServerIsAD = FALSE;
hr = S_OK;
}
if (fServerIsAD) {
_dwServerType = SERVER_TYPE_AD;
}
else {
_dwServerType = SERVER_TYPE_NOT_AD;
}
}
error:
if (bstrADsPath) {
ADsFreeString(bstrADsPath);
}
if (pszGroupServer) {
FreeADsStr(pszGroupServer);
}
if (pszGroupDn) {
FreeADsStr(pszGroupDn);
}
RRETURN(hr);
}
HRESULT
ValidateProvider(
POBJECTINFO pObjectInfo
)
{
//
// The provider name is case-sensitive. This is a restriction that OLE
// has put on us.
//
if (_tcscmp(pObjectInfo->ProviderName, L"LDAP") == 0) {
RRETURN(S_OK);
}
RRETURN(E_FAIL);
}
BOOL
MapLdapClassToADsClass(
LPTSTR *aLdapClasses,
int nCount,
LPTSTR pszADsClass
)
{
*pszADsClass = 0;
if ( nCount == 0 )
return FALSE;
if ( _tcsicmp( aLdapClasses[nCount-1], TEXT("Top")) == 0 )
{
for ( int j = 0; j < nCount; j++ )
{
LPTSTR pszLdapClass = aLdapClasses[j];
for ( int i = 0; i < ARRAY_SIZE(aClassMap); i++ )
{
if ( _tcsicmp( pszLdapClass, aClassMap[i].pszLdapClassName ) == 0 )
{
_tcscpy( pszADsClass, aClassMap[i].pszADsClassName );
return TRUE;
}
}
}
_tcscpy( pszADsClass, aLdapClasses[0] );
return FALSE;
}
else
{
for ( int j = nCount-1; j >= 0; j-- )
{
LPTSTR pszLdapClass = aLdapClasses[j];
for ( int i = 0; i < ARRAY_SIZE(aClassMap); i++ )
{
if ( _tcsicmp( pszLdapClass, aClassMap[i].pszLdapClassName ) == 0 )
{
_tcscpy( pszADsClass, aClassMap[i].pszADsClassName );
return TRUE;
}
}
}
_tcscpy( pszADsClass, aLdapClasses[nCount-1] );
return FALSE;
}
}