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