Leaked source code of windows server 2003
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

//+----------------------------------------------------------------------------
//
// 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;
}