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.
1104 lines
29 KiB
1104 lines
29 KiB
//+----------------------------------------------------------------------------
|
|
//
|
|
// Copyright (C) 2000, Microsoft Corporation
|
|
//
|
|
// File: DfsAdsiApi.cxx
|
|
//
|
|
// Contents: Contains APIs to communicate with the DS
|
|
//
|
|
// Classes: none.
|
|
//
|
|
// History: March. 13 2001, Author: Rohanp
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
#include "DfsAdsiAPi.hxx"
|
|
#include "DfsError.hxx"
|
|
#include "dfsgeneric.hxx"
|
|
#include "dfsinit.hxx"
|
|
#include "lm.h"
|
|
#include "lmdfs.h"
|
|
|
|
#include "strsafe.h"
|
|
#include "dsgetdc.h"
|
|
//
|
|
// dfsdev: comment this properly.
|
|
//
|
|
LPWSTR RootDseString=L"LDAP://RootDSE";
|
|
|
|
//
|
|
// The prefix for AD path string. This is used to generate a path of
|
|
// the form "LDAP://<dcname>/CN=,...DC=,..."
|
|
//
|
|
LPWSTR LdapPrefixString=L"LDAP://";
|
|
|
|
DFSSTATUS
|
|
DfsCreateDN(
|
|
OUT LPWSTR PathString,
|
|
IN size_t CharacterCount,
|
|
LPWSTR DCName,
|
|
LPWSTR PathPrefix,
|
|
LPWSTR *CNNames );
|
|
|
|
DFSSTATUS
|
|
DfsGenerateRootCN(
|
|
LPWSTR RootName,
|
|
LPWSTR *pRootCNName);
|
|
|
|
VOID
|
|
DfsDeleteRootCN(
|
|
LPWSTR PathString);
|
|
|
|
DFSSTATUS
|
|
DfsGenerateADPathString(
|
|
IN LPWSTR DCName,
|
|
IN LPWSTR ObjectName,
|
|
IN LPWSTR PathPrefix,
|
|
OUT LPOLESTR *pPathString);
|
|
|
|
VOID
|
|
DfsDeleteADPathString(
|
|
LPWSTR PathString);
|
|
|
|
DFSSTATUS
|
|
DfsGetADObjectWithDsGetDCName(
|
|
LPWSTR DCName,
|
|
REFIID Id,
|
|
LPWSTR ObjectName,
|
|
ULONG GetDCFlags,
|
|
PVOID *ppObject );
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: DfsCreateDN
|
|
//
|
|
// Arguments:
|
|
// OUT LPWSTR PathString -> updated pathstring on return
|
|
// IN size_t CharacterCount -> number of characters in PathString
|
|
// LPWSTR DCName -> DcNAME to use
|
|
// LPWSTR PathPrefix -> path prefix if any
|
|
// LPWSTR *CNNames -> Array of CNNames.
|
|
|
|
// Returns: Status
|
|
// ERROR_SUCCESS on success
|
|
// ERROR status code otherwise
|
|
//
|
|
//
|
|
// Description: dfscreatedn take a pathstring and fills it with the
|
|
// information necessary for the path to be used as a
|
|
// Distinguished Name.
|
|
// It starts the string with LDAP://, follows that with
|
|
// a DCName if supplied, and then adds the array of
|
|
// CNNames passed in one after the other , each one
|
|
// followed by a , the final outcome is something like:
|
|
// LDAP://ntdev-dc-01/CN=Dfs-Configuration, CN=System, Dc=Ntdev, etc
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
DFSSTATUS
|
|
DfsCreateDN(
|
|
OUT LPWSTR PathString,
|
|
IN size_t CharacterCount,
|
|
LPWSTR DCName,
|
|
LPWSTR PathPrefix,
|
|
LPWSTR *CNNames )
|
|
{
|
|
LPWSTR *InArray = CNNames;
|
|
LPWSTR CNName;
|
|
HRESULT HResult = S_OK;
|
|
size_t CurrentCharacterCount = CharacterCount;
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
//
|
|
// If we are not adding the usual "LDAP://" string,
|
|
// Start with a NULL so string cat will work right.
|
|
//
|
|
|
|
if (CurrentCharacterCount <=0 )
|
|
{
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (PathPrefix != NULL)
|
|
{
|
|
HResult = StringCchCopy( PathString,
|
|
CurrentCharacterCount,
|
|
PathPrefix );
|
|
} else {
|
|
|
|
*PathString = UNICODE_NULL;
|
|
}
|
|
|
|
if (SUCCEEDED(HResult)) {
|
|
//
|
|
// if the dc name is specified, we want to go to a specific dc
|
|
// add that in.
|
|
//
|
|
if (!IsEmptyString(DCName))
|
|
{
|
|
HResult = StringCchCat( PathString,
|
|
CurrentCharacterCount,
|
|
DCName );
|
|
|
|
if (SUCCEEDED(HResult))
|
|
{
|
|
HResult = StringCchCat( PathString,
|
|
CurrentCharacterCount,
|
|
L"/" );
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// Now treat the CNNames as an array of LPWSTR and add each one of
|
|
// the lpwstr to our path.
|
|
//
|
|
|
|
if (SUCCEEDED(HResult)) {
|
|
if (CNNames != NULL)
|
|
{
|
|
while (((CNName = *InArray++) != NULL) &&
|
|
(SUCCEEDED(HResult)))
|
|
{
|
|
HResult = StringCchCat( PathString,
|
|
CurrentCharacterCount,
|
|
CNName );
|
|
|
|
if ((*InArray != NULL) &&
|
|
(SUCCEEDED(HResult)))
|
|
{
|
|
HResult = StringCchCat( PathString,
|
|
CurrentCharacterCount,
|
|
L"," );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!SUCCEEDED(HResult))
|
|
{
|
|
Status = HRESULT_CODE(HResult);
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
DFSSTATUS
|
|
DfsGenerateDfsAdNameContext(
|
|
PUNICODE_STRING pString )
|
|
|
|
{
|
|
IADs *pRootDseObject;
|
|
HRESULT HResult;
|
|
VARIANT VarDSRoot;
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
BSTR NamingContext;
|
|
|
|
NamingContext = SysAllocString(L"defaultNamingContext");
|
|
if (NamingContext == NULL)
|
|
{
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
HResult = ADsGetObject( RootDseString,
|
|
IID_IADs,
|
|
(void **)&pRootDseObject );
|
|
|
|
if (SUCCEEDED(HResult))
|
|
{
|
|
VariantInit( &VarDSRoot );
|
|
// Get the Directory Object on the root DSE, to get to the server configuration
|
|
HResult = pRootDseObject->Get(NamingContext, &VarDSRoot);
|
|
|
|
if (SUCCEEDED(HResult))
|
|
{
|
|
Status = DfsCreateUnicodeStringFromString( pString,
|
|
(LPWSTR)V_BSTR(&VarDSRoot) );
|
|
}
|
|
|
|
VariantClear(&VarDSRoot);
|
|
|
|
pRootDseObject->Release();
|
|
}
|
|
|
|
if (!SUCCEEDED(HResult))
|
|
{
|
|
Status = DfsGetErrorFromHr(HResult);
|
|
}
|
|
|
|
SysFreeString(NamingContext);
|
|
return Status;
|
|
}
|
|
|
|
|
|
DFSSTATUS
|
|
DfsGenerateDfsAdNameContextForDomain(
|
|
PUNICODE_STRING pString,
|
|
LPWSTR DCName )
|
|
|
|
{
|
|
IADs *pRootDseObject;
|
|
HRESULT HResult;
|
|
VARIANT VarDSRoot;
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
BSTR NamingContext;
|
|
LPWSTR CNNames[2];
|
|
LPWSTR PathString = NULL;
|
|
CNNames[0] = L"RootDSE";
|
|
CNNames[1] = NULL;
|
|
|
|
size_t CharacterCount = 1;
|
|
|
|
CharacterCount += wcslen(LdapPrefixString);
|
|
if (DCName)
|
|
{
|
|
CharacterCount += wcslen(DCName) + 1;
|
|
CharacterCount++;
|
|
}
|
|
CharacterCount += wcslen(CNNames[0]) + 1;
|
|
CharacterCount++;
|
|
|
|
PathString = new WCHAR[CharacterCount];
|
|
if (PathString == NULL)
|
|
{
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
Status = DfsCreateDN( PathString,
|
|
CharacterCount,
|
|
DCName,
|
|
LdapPrefixString,
|
|
CNNames);
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
NamingContext = SysAllocString(L"defaultNamingContext");
|
|
if (NamingContext == NULL)
|
|
{
|
|
Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
|
|
HResult = ADsGetObject( PathString,
|
|
IID_IADs,
|
|
(void **)&pRootDseObject );
|
|
|
|
if (SUCCEEDED(HResult))
|
|
{
|
|
VariantInit( &VarDSRoot );
|
|
// Get the Directory Object on the root DSE, to get to the server configuration
|
|
HResult = pRootDseObject->Get(NamingContext, &VarDSRoot);
|
|
|
|
if (SUCCEEDED(HResult))
|
|
{
|
|
Status = DfsCreateUnicodeStringFromString( pString,
|
|
(LPWSTR)V_BSTR(&VarDSRoot) );
|
|
}
|
|
|
|
VariantClear(&VarDSRoot);
|
|
|
|
pRootDseObject->Release();
|
|
}
|
|
|
|
if (!SUCCEEDED(HResult))
|
|
{
|
|
Status = DfsGetErrorFromHr(HResult);
|
|
}
|
|
|
|
SysFreeString(NamingContext);
|
|
}
|
|
delete [] PathString;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
#define MAX_CN_ARRAY 5
|
|
|
|
DFSSTATUS
|
|
DfsGenerateADPathString(
|
|
IN LPWSTR DCName,
|
|
IN LPWSTR ObjectName,
|
|
IN LPWSTR PathPrefix,
|
|
OUT LPWSTR *pPathString)
|
|
{
|
|
LPWSTR CNNames[MAX_CN_ARRAY];
|
|
ULONG Index;
|
|
LPWSTR ADNameContext;
|
|
size_t CharacterCount = 0;
|
|
size_t UseCount;
|
|
HRESULT HResult = S_OK;
|
|
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
|
|
do {
|
|
CharacterCount = 1; // For null termination;
|
|
|
|
if (PathPrefix != NULL)
|
|
{
|
|
Status = DfsStringCchLength( PathPrefix,
|
|
MAXUSHORT,
|
|
&UseCount);
|
|
if (Status != ERROR_SUCCESS)
|
|
break;
|
|
|
|
CharacterCount += UseCount + 1; // path prefix + seperator
|
|
}
|
|
|
|
//
|
|
// ADNameContext will be of the form CN=NtDev, CN=Micorosft etc etc.
|
|
//
|
|
ADNameContext = DfsGetDfsAdNameContextString();
|
|
if (ADNameContext == NULL)
|
|
{
|
|
Status = ERROR_NOT_READY;
|
|
break;
|
|
}
|
|
|
|
if (DCName != NULL)
|
|
{
|
|
Status = DfsStringCchLength( DCName,
|
|
MAXUSHORT,
|
|
&UseCount);
|
|
if (Status != ERROR_SUCCESS)
|
|
break;
|
|
|
|
CharacterCount += UseCount + 1; //dcname + seperator;
|
|
}
|
|
|
|
if (ObjectName != NULL)
|
|
{
|
|
Status = DfsStringCchLength( ObjectName,
|
|
MAXUSHORT,
|
|
&UseCount);
|
|
if (Status != ERROR_SUCCESS)
|
|
break;
|
|
|
|
CharacterCount += UseCount + 1; //ObjectName + seperator
|
|
}
|
|
|
|
Status = DfsStringCchLength( DFS_AD_CONFIG_DATA,
|
|
MAXUSHORT,
|
|
&UseCount);
|
|
if (Status != ERROR_SUCCESS)
|
|
break;
|
|
|
|
CharacterCount += UseCount + 1; // config container + seperator;
|
|
|
|
Status = DfsStringCchLength( ADNameContext,
|
|
MAXUSHORT,
|
|
&UseCount);
|
|
|
|
if (Status != ERROR_SUCCESS)
|
|
break;
|
|
|
|
CharacterCount += UseCount + 1; //context + seperator;
|
|
|
|
//
|
|
// Note: Be very wary here. We have a fixed CNNames array, and
|
|
// we are using that knowledge here. Adding any more CNNames
|
|
// needs to bump up the array count.
|
|
//
|
|
Index = 0;
|
|
if (ObjectName != NULL)
|
|
{
|
|
CNNames[Index++] = ObjectName;
|
|
}
|
|
CNNames[Index++] = DFS_AD_CONFIG_DATA;
|
|
CNNames[Index++] = ADNameContext;
|
|
CNNames[Index] = NULL;
|
|
|
|
|
|
*pPathString = new WCHAR[CharacterCount];
|
|
if (*pPathString == NULL)
|
|
{
|
|
Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
break;
|
|
}
|
|
|
|
Status = DfsCreateDN( *pPathString,
|
|
CharacterCount,
|
|
DCName,
|
|
PathPrefix,
|
|
CNNames);
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
delete [] *pPathString;
|
|
*pPathString = NULL;
|
|
break;
|
|
}
|
|
|
|
} while (FALSE);
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
DfsDeleteADPathString(
|
|
LPWSTR PathString)
|
|
{
|
|
delete [] PathString;
|
|
return NOTHING;
|
|
}
|
|
|
|
|
|
DFSSTATUS
|
|
DfsGetADObjectWithDsGetDCName(
|
|
LPWSTR DCName,
|
|
REFIID Id,
|
|
LPWSTR ObjectName,
|
|
ULONG GetDCFlags,
|
|
PVOID *ppObject )
|
|
//
|
|
// This function calls ADSI to open the object. Alas, we're not thrilled with
|
|
// how ADSI does DC-stickyness so we'll work around that by getting the DC
|
|
// name and passing it in to ADSI ourselves so that they don't have to bother.
|
|
//
|
|
{
|
|
HRESULT HResult;
|
|
LPWSTR PathString;
|
|
DFSSTATUS Status;
|
|
LPWSTR DCNameToUse = DCName; // may be DCName or pointer into dcInfo
|
|
DOMAIN_CONTROLLER_INFO *dcInfo = NULL; // be sure to free if not null
|
|
|
|
if (DCName == NULL) {
|
|
|
|
Status = DsGetDcName( NULL, // computer name
|
|
NULL, // domain name
|
|
NULL, // domain guid
|
|
NULL, // site name
|
|
GetDCFlags,
|
|
&dcInfo
|
|
);
|
|
|
|
if ((Status == ERROR_SUCCESS) && (dcInfo->DomainControllerName != NULL)) {
|
|
|
|
DCNameToUse = dcInfo->DomainControllerName;
|
|
|
|
// don't include the double slashes at the front of the DC name
|
|
|
|
while (*DCNameToUse == L'\\') {
|
|
DCNameToUse++;
|
|
}
|
|
}
|
|
}
|
|
|
|
Status = DfsGenerateADPathString( DCNameToUse,
|
|
ObjectName,
|
|
LdapPrefixString,
|
|
&PathString );
|
|
|
|
if (dcInfo != NULL) {
|
|
(VOID) NetApiBufferFree( dcInfo ); // ignore status returned
|
|
}
|
|
|
|
if (Status != ERROR_SUCCESS)
|
|
{
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// open dfs configuration container for enumeration.
|
|
//
|
|
|
|
HResult = ADsOpenObject(PathString,
|
|
NULL,
|
|
NULL,
|
|
ADS_SECURE_AUTHENTICATION | ADS_FAST_BIND | ADS_SERVER_BIND,
|
|
Id,
|
|
ppObject );
|
|
|
|
Status = DfsGetErrorFromHr(HResult);
|
|
|
|
DfsDeleteADPathString( PathString );
|
|
|
|
return Status;
|
|
}
|
|
|
|
DFSSTATUS
|
|
DfsGetADObject(
|
|
LPWSTR DCName,
|
|
REFIID Id,
|
|
LPWSTR ObjectName,
|
|
PVOID *ppObject )
|
|
{
|
|
DFSSTATUS Status;
|
|
|
|
Status = DfsGetADObjectWithDsGetDCName( DCName,
|
|
Id,
|
|
ObjectName,
|
|
DS_DIRECTORY_SERVICE_REQUIRED,
|
|
ppObject );
|
|
|
|
if (Status != ERROR_SUCCESS && DCName == NULL) {
|
|
|
|
Status = DfsGetADObjectWithDsGetDCName( DCName,
|
|
Id,
|
|
ObjectName,
|
|
DS_FORCE_REDISCOVERY | DS_DIRECTORY_SERVICE_REQUIRED,
|
|
ppObject );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
//
|
|
// Given the root share name, get the root object.
|
|
//
|
|
|
|
DFSSTATUS
|
|
DfsGetDfsRootADObject(
|
|
LPWSTR DCName,
|
|
LPWSTR RootName,
|
|
IADs **ppRootObject )
|
|
{
|
|
LPWSTR RootCNName = NULL;
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
|
|
Status = DfsGenerateRootCN( RootName, &RootCNName );
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
Status = DfsGetADObject( DCName,
|
|
IID_IADs,
|
|
RootCNName,
|
|
(PVOID *)ppRootObject );
|
|
|
|
DfsDeleteRootCN( RootCNName );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
DFSSTATUS
|
|
PackRootName(
|
|
LPWSTR Name,
|
|
PDFS_INFO_200 pDfsInfo200,
|
|
PULONG pBufferSize,
|
|
PULONG pTotalSize )
|
|
{
|
|
size_t CharacterCount, ByteCount;
|
|
ULONG NeedSize;
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
HRESULT HResult;
|
|
LPWSTR UseName;
|
|
|
|
UseName = &Name[3]; // skip over the leading whacks.
|
|
|
|
HResult = StringCchLength( UseName,
|
|
MAXUSHORT,
|
|
&CharacterCount );
|
|
if (SUCCEEDED(HResult))
|
|
{
|
|
CharacterCount += 1; // For null termination;
|
|
|
|
ByteCount = CharacterCount * sizeof(WCHAR);
|
|
|
|
|
|
NeedSize = sizeof(DFS_INFO_200) + ByteCount;
|
|
*pTotalSize += NeedSize;
|
|
if (*pBufferSize >= NeedSize)
|
|
{
|
|
LPWSTR pStringBuffer;
|
|
|
|
pStringBuffer = (LPWSTR)((ULONG_PTR)(pDfsInfo200) + *pBufferSize - ByteCount);
|
|
|
|
HResult = StringCchCopy( pStringBuffer,
|
|
CharacterCount,
|
|
UseName );
|
|
if (SUCCEEDED(HResult))
|
|
{
|
|
pDfsInfo200->FtDfsName = (LPWSTR)pStringBuffer;
|
|
*pBufferSize -= NeedSize;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Status = ERROR_BUFFER_OVERFLOW;
|
|
*pBufferSize = 0;
|
|
}
|
|
}
|
|
|
|
if (!SUCCEEDED(HResult))
|
|
{
|
|
Status = HRESULT_CODE(HResult);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
DFSSTATUS
|
|
DfsGetDfsConfigurationObject(
|
|
LPWSTR DCName,
|
|
IADsContainer **ppDfsConfiguration )
|
|
{
|
|
DFSSTATUS Status;
|
|
|
|
Status = DfsGetADObject( DCName,
|
|
IID_IADsContainer,
|
|
NULL,
|
|
(PVOID *)ppDfsConfiguration );
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
DFSSTATUS
|
|
DfsGetBinaryFromVariant(VARIANT *ovData, BYTE ** ppBuf,
|
|
unsigned long * pcBufLen)
|
|
{
|
|
DFSSTATUS Status = ERROR_INVALID_PARAMETER;
|
|
void * pArrayData = NULL;
|
|
|
|
//Binary data is stored in the variant as an array of unsigned char
|
|
if(ovData->vt == (VT_ARRAY|VT_UI1))
|
|
{
|
|
//Retrieve size of array
|
|
*pcBufLen = ovData->parray->rgsabound[0].cElements;
|
|
|
|
*ppBuf = new BYTE[*pcBufLen]; //Allocate a buffer to store the data
|
|
if(*ppBuf != NULL)
|
|
{
|
|
//Obtain safe pointer to the array
|
|
SafeArrayAccessData(ovData->parray,&pArrayData);
|
|
|
|
//Copy the bitmap into our buffer
|
|
memcpy(*ppBuf, pArrayData, *pcBufLen);
|
|
|
|
//Unlock the variant data
|
|
SafeArrayUnaccessData(ovData->parray);
|
|
|
|
Status = ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
DFSSTATUS
|
|
DfsCheckRootADObjectExistence(
|
|
LPWSTR DCName,
|
|
PUNICODE_STRING pRootName,
|
|
GUID *pGuid )
|
|
{
|
|
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
IADs *pRootObject = NULL;
|
|
BYTE *pBuffer = NULL;
|
|
ULONG Length = 0;
|
|
UNICODE_STRING RootName;
|
|
|
|
Status = DfsCreateUnicodeString( &RootName,
|
|
pRootName );
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
Status = DfsGetDfsRootADObject( DCName,
|
|
RootName.Buffer,
|
|
&pRootObject );
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
|
|
//
|
|
//
|
|
// Now check for ability to read an attribute...
|
|
|
|
VARIANT Variant;
|
|
HRESULT HResult;
|
|
|
|
VariantInit(&Variant);
|
|
|
|
LPWSTR pszAttrs[] = { L"pKTGuid" };
|
|
DWORD dwNumber = sizeof( pszAttrs ) /sizeof(LPWSTR);
|
|
HResult = ADsBuildVarArrayStr( pszAttrs, dwNumber, &Variant );
|
|
if (HResult == S_OK)
|
|
{
|
|
HResult = pRootObject->GetInfoEx(Variant, 0);
|
|
|
|
if (HResult != S_OK)
|
|
{
|
|
Status = DfsGetErrorFromHr(HResult);
|
|
}
|
|
}
|
|
|
|
VariantClear(&Variant);
|
|
VariantInit( &Variant );
|
|
|
|
if ((Status == ERROR_SUCCESS) && pGuid)
|
|
{
|
|
//
|
|
// Make a BSTR out of WCHAR *
|
|
//
|
|
BSTR PktGuidBstr = SysAllocString(L"pKTGuid");
|
|
if (PktGuidBstr == NULL)
|
|
{
|
|
Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
HResult = pRootObject->Get( PktGuidBstr, &Variant );
|
|
if (HResult == S_OK)
|
|
{
|
|
Status = DfsGetBinaryFromVariant( &Variant, &pBuffer, &Length );
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
if (Length > sizeof(GUID))
|
|
{
|
|
Length = sizeof(GUID);
|
|
}
|
|
|
|
RtlCopyMemory( pGuid, pBuffer, Length);
|
|
|
|
delete [] pBuffer;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (HResult != S_OK)
|
|
{
|
|
Status = DfsGetErrorFromHr(HResult);
|
|
}
|
|
}
|
|
|
|
if (PktGuidBstr != NULL) // paranoia
|
|
{
|
|
SysFreeString( PktGuidBstr );
|
|
}
|
|
}
|
|
}
|
|
|
|
pRootObject->Release();
|
|
}
|
|
DfsFreeUnicodeString( &RootName );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
DFSSTATUS
|
|
DfsDeleteDfsRootObject(
|
|
LPWSTR DCName,
|
|
LPWSTR RootName )
|
|
{
|
|
BSTR ObjectName, ObjectClass;
|
|
DFSSTATUS Status;
|
|
HRESULT HResult;
|
|
IADsContainer *pDfsConfiguration;
|
|
IADs *pRootObject;
|
|
|
|
Status = DfsGetDfsRootADObject( DCName,
|
|
RootName,
|
|
&pRootObject );
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
Status = DfsGetDfsConfigurationObject( DCName,
|
|
&pDfsConfiguration );
|
|
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
|
|
HResult = pRootObject->get_Name(&ObjectName);
|
|
if (SUCCEEDED(HResult))
|
|
{
|
|
HResult = pRootObject->get_Class(&ObjectClass);
|
|
|
|
if (SUCCEEDED(HResult))
|
|
{
|
|
HResult = pDfsConfiguration->Delete( ObjectClass,
|
|
ObjectName );
|
|
|
|
SysFreeString(ObjectClass);
|
|
}
|
|
SysFreeString(ObjectName);
|
|
}
|
|
pDfsConfiguration->Release();
|
|
|
|
Status = DfsGetErrorFromHr(HResult);
|
|
}
|
|
|
|
pRootObject->Release();
|
|
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
DFSSTATUS
|
|
DfsEnumerateDfsADRoots(
|
|
LPWSTR DCName,
|
|
PULONG_PTR pBuffer,
|
|
PULONG pBufferSize,
|
|
PULONG pEntriesRead,
|
|
DWORD MaxEntriesToRead,
|
|
LPDWORD pResumeHandle,
|
|
PULONG pSizeRequired )
|
|
{
|
|
HRESULT HResult;
|
|
IADsContainer *pDfsConfiguration;
|
|
IEnumVARIANT *pEnum;
|
|
|
|
ULONG TotalSize = 0;
|
|
ULONG TotalEntries = 0;
|
|
PDFS_INFO_200 pDfsInfo200;
|
|
ULONG BufferSize = *pBufferSize;
|
|
DFSSTATUS Status;
|
|
LONG ResumeCount;
|
|
LONG NumRoots;
|
|
|
|
//
|
|
// point the dfsinfo200 structure to the start of buffer passed in
|
|
// we will use this as an array of info200 buffers.
|
|
//
|
|
pDfsInfo200 = (PDFS_INFO_200)*pBuffer;
|
|
|
|
|
|
Status = DfsGetDfsConfigurationObject( DCName,
|
|
&pDfsConfiguration );
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
HResult = ADsBuildEnumerator( pDfsConfiguration,
|
|
&pEnum );
|
|
|
|
if (SUCCEEDED(HResult))
|
|
{
|
|
VARIANT Variant;
|
|
ULONG Fetched;
|
|
BSTR BString;
|
|
IADs *pRootObject;
|
|
IDispatch *pDisp;
|
|
|
|
ResumeCount = 0;
|
|
NumRoots = 0;
|
|
|
|
// See if we need to resume from a previous enumeration.
|
|
if (pResumeHandle && *pResumeHandle > 0)
|
|
{
|
|
ResumeCount = *pResumeHandle;
|
|
}
|
|
|
|
VariantInit(&Variant);
|
|
while ((HResult = ADsEnumerateNext(pEnum,
|
|
1,
|
|
&Variant,
|
|
&Fetched)) == S_OK)
|
|
{
|
|
if (TotalEntries >= MaxEntriesToRead)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Skip ResumeCount number of entries
|
|
NumRoots++;
|
|
if (NumRoots <= ResumeCount)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
pDisp = V_DISPATCH(&Variant);
|
|
pDisp->QueryInterface(IID_IADs, (void **)&pRootObject);
|
|
pDisp->Release();
|
|
|
|
HResult = pRootObject->get_Name(&BString);
|
|
if (HResult == S_OK)
|
|
{
|
|
Status = PackRootName( BString, pDfsInfo200, &BufferSize, &TotalSize );
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
TotalEntries++;
|
|
pDfsInfo200++;
|
|
}
|
|
SysFreeString(BString);
|
|
}
|
|
pRootObject->Release();
|
|
|
|
// DfsDev: investigate. this causes an av.
|
|
// VariantClear(&Variant);
|
|
|
|
}
|
|
|
|
if (HResult == S_FALSE)
|
|
{
|
|
HResult = S_OK;
|
|
}
|
|
|
|
ADsFreeEnumerator( pEnum );
|
|
}
|
|
|
|
pDfsConfiguration->Release();
|
|
|
|
if (!SUCCEEDED(HResult))
|
|
{
|
|
Status = DfsGetErrorFromHr(HResult);
|
|
}
|
|
}
|
|
|
|
if ((Status == ERROR_SUCCESS) || (Status == ERROR_BUFFER_OVERFLOW))
|
|
{
|
|
*pSizeRequired = TotalSize;
|
|
if (TotalSize > *pBufferSize)
|
|
{
|
|
Status = ERROR_BUFFER_OVERFLOW;
|
|
}
|
|
else if (TotalEntries == 0)
|
|
{
|
|
Status = ERROR_NO_MORE_ITEMS;
|
|
}
|
|
}
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
*pEntriesRead = TotalEntries;
|
|
|
|
if (pResumeHandle)
|
|
{
|
|
*pResumeHandle = NumRoots;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
DFSSTATUS
|
|
DfsGenerateRootCN(
|
|
LPWSTR RootName,
|
|
LPWSTR *pRootCNName)
|
|
{
|
|
size_t CharacterCount, UseCount;
|
|
|
|
LPWSTR UsePrefix = L"CN=";
|
|
HRESULT HResult;
|
|
DFSSTATUS Status = ERROR_SUCCESS;
|
|
LPWSTR RootCNName;
|
|
|
|
CharacterCount = 1; // for null termination;
|
|
|
|
HResult = StringCchLength( UsePrefix,
|
|
MAXUSHORT,
|
|
&UseCount);
|
|
if (SUCCEEDED(HResult))
|
|
{
|
|
CharacterCount += UseCount;
|
|
HResult = StringCchLength( RootName,
|
|
MAXUSHORT,
|
|
&UseCount);
|
|
if (SUCCEEDED(HResult))
|
|
{
|
|
CharacterCount += UseCount;
|
|
}
|
|
}
|
|
|
|
|
|
if (SUCCEEDED(HResult))
|
|
{
|
|
RootCNName = new WCHAR[CharacterCount];
|
|
|
|
if (RootCNName == NULL)
|
|
{
|
|
Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
HResult = StringCchCopy( RootCNName,
|
|
CharacterCount,
|
|
UsePrefix );
|
|
|
|
if (SUCCEEDED(HResult))
|
|
{
|
|
HResult = StringCchCat( RootCNName,
|
|
CharacterCount,
|
|
RootName );
|
|
}
|
|
|
|
if (!SUCCEEDED(HResult))
|
|
{
|
|
delete [] RootCNName;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Note: here are 2 possibilities. We either have a HResult that is
|
|
// not S_OK or we have a Status that is already set.
|
|
// If the HResult is not S_OK, convert that to a status and return
|
|
// Otherwise, use the status as set by the above code.
|
|
// IF YOU DO HRESULT_CODE() at this point on a S_OK Hresult, we will
|
|
// lost the status above.
|
|
//
|
|
if (!SUCCEEDED(HResult))
|
|
{
|
|
Status = HRESULT_CODE(HResult);
|
|
}
|
|
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
*pRootCNName = RootCNName;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
DfsDeleteRootCN(
|
|
LPWSTR RootCNName)
|
|
{
|
|
delete [] RootCNName;
|
|
return NOTHING;
|
|
}
|
|
|
|
//
|
|
// Given a DFS rootname, this spews out a heap-allocated, NULL terminated
|
|
// string containing the LDAP name context. This string, for example
|
|
// ends up in the FTDfsObjectDN value name in the registry.
|
|
// eg: CN=Rootname,CN=Dfs-Configuration,CN=System,DC=supwdomain,DC=nttest, ...
|
|
//
|
|
DFSSTATUS
|
|
DfsGenerateDNPathString(
|
|
LPWSTR RootObjName,
|
|
OUT LPWSTR *pPathString)
|
|
{
|
|
DFSSTATUS Status;
|
|
LPWSTR RootCNName;
|
|
|
|
//
|
|
// Generate the CN=Rootname part.
|
|
//
|
|
Status = DfsGenerateRootCN( RootObjName, &RootCNName );
|
|
if (Status == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Now add in the rest of the CN and DC configuration strings.
|
|
//
|
|
Status = DfsGenerateADPathString( NULL,
|
|
RootCNName,
|
|
NULL,
|
|
pPathString );
|
|
DfsDeleteRootCN( RootCNName );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
DfsDeleteDNPathString(
|
|
LPWSTR PathString)
|
|
{
|
|
DfsDeleteADPathString( PathString );
|
|
return NOTHING;
|
|
|
|
}
|
|
|
|
|
|
|