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.
 
 
 
 
 
 

643 lines
20 KiB

//+----------------------------------------------------------------------------
//
// Copyright (C) 2001, Microsoft Corporation
//
// File: DfsXForest.cxx
//
// Contents: the Dfs xforest info class
//
// Classes: DfsXForest, DfsDomainNameTable
//
// History: Nov, 15 2002, Author: udayh
//
//-----------------------------------------------------------------------------
#include "dfsxforest.hxx"
#include "lm.h"
#include "dfsxforest.tmh"
//
// This file implements DfsXForest class. This class enumerates all
// local domains and cross forest domains, and builds a table of uniqye
// domain names.
//
// Ideally this should be an API such as DsEnumerateDomainTrusts, but
// lacking that we are putting this support here.
//
// Since this is happening so close to RTM, there is minimal code change
// in the original path, and all functionality has been implemented in
// this new class. This is called from DfsDomainInformation, where
// instead of enumerating domains by calling DsEnumerateDomainTrusts we
// enumerate domains by building the DfsXForest instance.
//
// DfsDomainInformation gets the DfsXForest instance, reads all the domains
// from the DfsXForest and destroys the DfsXForest.
//
//
// In future, we may either want to get an API for this. If that does
// not happen, it may be worthwhile changing the DfsDomainInformation
// code to use the DfsXForest instance all the time instead of
// building an array of domain names enumerated using this class.
// That would be a more performance optimal solution.
//
//
DFSSTATUS
DfsXForest::Initialize( DFSSTATUS *pXforestInitStatus )
{
DFSSTATUS Status;
Status = _DomainTable.Initialize();
if (Status == ERROR_SUCCESS)
{
Status = DfsAddLocalDomainsToDomainTable();
}
if (Status == ERROR_SUCCESS)
{
DFSSTATUS DfsCrossForestDomainStatus;
DfsCrossForestDomainStatus = InitializeForestRootName();
DFS_TRACE_ERROR_NORM(DfsCrossForestDomainStatus, REFERRAL_SERVER,
"DfsXForest::InitializeForestRootName, Status 0x%x\n",
DfsCrossForestDomainStatus);
//
// If we cannot get cross forest domain information,
// we continue with just the local information.
//
if (DfsCrossForestDomainStatus == ERROR_SUCCESS)
{
DfsCrossForestDomainStatus = DfsAddCrossForestDomainsToDomainTable();
DFS_TRACE_ERROR_NORM(DfsCrossForestDomainStatus, REFERRAL_SERVER,
"DfsXForest::DfsAddCrossForestDomainsToDomainTable, Status 0x%x\n",
DfsCrossForestDomainStatus);
}
*pXforestInitStatus = DfsCrossForestDomainStatus;
}
return Status;
}
//
// InitializeForestRootName: Get the Root Forest name and store it
// in our private member _ForestRootName.
//
//
DFSSTATUS
DfsXForest::InitializeForestRootName()
{
LSA_OBJECT_ATTRIBUTES ObjectAttributes;
NTSTATUS NtStatus = 0;
DFSSTATUS Status = ERROR_SUCCESS;
LSA_HANDLE hPolicy;
//attempt to open the policy.
RtlInitUnicodeString(&_ForestRootName, NULL);
ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));//object attributes are reserved, so initalize to zeroes.
NtStatus = LsaOpenPolicy( NULL,
&ObjectAttributes,
POLICY_VIEW_LOCAL_INFORMATION,
&hPolicy); //recieves the policy handle
DFS_TRACE_ERROR_HIGH( NtStatus, REFERRAL_SERVER, "LsaOpenPolicy, NtStatus 0x%x\n", NtStatus );
if (NT_SUCCESS(NtStatus))
{
//ask for audit event policy information
PPOLICY_DNS_DOMAIN_INFO info;
NtStatus = LsaQueryInformationPolicy(hPolicy,
PolicyDnsDomainInformation,
(PVOID *)&info);
DFS_TRACE_ERROR_HIGH( NtStatus, REFERRAL_SERVER, "LsaQueryInformationPolicy, NtStatus 0x%x\n", NtStatus );
if (NT_SUCCESS(NtStatus))
{
//
// convert LSA_UNICODE_STRING to normal UNICODE_STRING.
// not that it matters now, but if things should change,
// we will be in trouble.
// Save the forest name and we are done.
//
UNICODE_STRING TempForestName;
TempForestName.Buffer = info->DnsForestName.Buffer;
TempForestName.Length = info->DnsForestName.Length;
TempForestName.MaximumLength = info->DnsForestName.MaximumLength;
Status = DfsCreateUnicodeString( &_ForestRootName,
&TempForestName);
//free policy info structure
LsaFreeMemory((PVOID) info);
}
//Freeing the policy object handle
LsaClose(hPolicy);
}
if (!NT_SUCCESS(NtStatus))
{
Status = RtlNtStatusToDosError(NtStatus);
}
return Status;
}
//
// DfsAddLocalDomainsToDomainTable: enumerate all the local domain
// trusts and add this to the domain table.
//
DFSSTATUS
DfsXForest::DfsAddLocalDomainsToDomainTable()
{
DFSSTATUS Status;
ULONG DsDomainCount = 0;
PDS_DOMAIN_TRUSTS pDsDomainTrusts = NULL;
ULONG Index;
Status = DsEnumerateDomainTrusts( NULL,
DS_DOMAIN_VALID_FLAGS,
&pDsDomainTrusts,
&DsDomainCount );
DFS_TRACE_ERROR_NORM( Status, REFERRAL_SERVER, "DsEnumerateDomainTrusts, Status 0x%x\n", Status );
if (Status == ERROR_SUCCESS)
{
for (Index = 0; (Status == ERROR_SUCCESS) && (Index < DsDomainCount); Index++)
{
if (pDsDomainTrusts[Index].TrustType == TRUST_TYPE_DOWNLEVEL)
continue;
if (Status == ERROR_SUCCESS)
{
if (IsEmptyString(pDsDomainTrusts[Index].NetbiosDomainName) == FALSE)
{
Status = AddDomainToDomainTable(pDsDomainTrusts[Index].NetbiosDomainName, NULL, TRUE);
}
}
if (Status == ERROR_SUCCESS)
{
if (IsEmptyString(pDsDomainTrusts[Index].DnsDomainName) == FALSE)
{
Status = AddDomainToDomainTable(pDsDomainTrusts[Index].DnsDomainName, NULL, FALSE);
}
}
}
NetApiBufferFree(pDsDomainTrusts);
}
return Status;
}
//
// DfsAddForestDomainsToDomainTable: enumerate all the domains in a forest
// and add that to our table.
//
//
DFSSTATUS
DfsXForest::DfsAddForestDomainsToDomainTable( LSA_HANDLE hPolicy,
LPWSTR RootNameString)
{
DFSSTATUS Status;
NTSTATUS NtStatus;
ULONG j;
LSA_UNICODE_STRING LsaRootName;
UNICODE_STRING RootName;
PLSA_FOREST_TRUST_INFORMATION pForestTrustInfo = NULL;
RtlInitUnicodeString(&RootName, RootNameString);
LsaRootName.Length = RootName.Length;
LsaRootName.MaximumLength = RootName.MaximumLength;
LsaRootName.Buffer = RootName.Buffer;
NtStatus = LsaQueryForestTrustInformation( hPolicy,
&LsaRootName,
&pForestTrustInfo);
Status = RtlNtStatusToDosError(NtStatus);
if (Status == ERROR_SUCCESS)
{
for (j = 0; (Status == ERROR_SUCCESS) && (j < pForestTrustInfo->RecordCount); j++ )
{
PLSA_FOREST_TRUST_DOMAIN_INFO pDomainInfo;
if (pForestTrustInfo->Entries[j]->ForestTrustType == ForestTrustDomainInfo)
{
pDomainInfo = &pForestTrustInfo->Entries[j]->ForestTrustData.DomainInfo;
//
// If only the DnsDomainName is empty, add both the dns and
// netbios domain names.
// The reason is: we cannot resolve netbios domain names
// to DCs. We bind to the DNS domain name to resolve netbios
// domains, so we add both netbios and dns names in
// the add domain to table for netbios names.
//
if (IsEmptyUnicodeString((PUNICODE_STRING)&pDomainInfo->DnsName) == FALSE)
{
Status = AddDomainToDomainTable((PUNICODE_STRING)&pDomainInfo->DnsName, NULL, FALSE);
if ((Status == ERROR_SUCCESS) && (IsEmptyUnicodeString((PUNICODE_STRING)&pDomainInfo->NetbiosName) == FALSE))
{
Status = AddDomainToDomainTable((PUNICODE_STRING)&pDomainInfo->NetbiosName,
(PUNICODE_STRING)&pDomainInfo->DnsName,
TRUE);
}
}
}
}
LsaFreeMemory(pForestTrustInfo);
}
return Status;
}
//
// Magic trust flags: KahrenT indicates this is what we need.
//
#define DOMAIN_TRUST_FLAGS DS_DOMAIN_DIRECT_INBOUND
//
// DfsAddCrossForestDomainsToDomainTable: Enumerate each forest that we have
// a trust to, and add that to our table.
//
//
DFSSTATUS
DfsXForest::DfsAddCrossForestDomainsToDomainTable()
{
DFSSTATUS Status;
NTSTATUS NtStatus;
ULONG DsDomainCount = 0;
PDS_DOMAIN_TRUSTS pDsDomainTrusts = NULL;
PDOMAIN_CONTROLLER_INFO pDomainControllerInfo = NULL;
LSA_OBJECT_ATTRIBUTES ObjectAttributes;
LSA_HANDLE hPolicy;
ULONG Index;
//object attributes are reserved, so initalize to zeroes.
ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));
Status = DsGetDcName( NULL, //computer name
_ForestRootName.Buffer,
NULL, // domain guid
NULL, // site name
DS_DIRECTORY_SERVICE_REQUIRED | DS_FORCE_REDISCOVERY,
&pDomainControllerInfo );
if (Status == ERROR_SUCCESS)
{
Status = DsEnumerateDomainTrusts( pDomainControllerInfo->DomainControllerName,
DOMAIN_TRUST_FLAGS,
&pDsDomainTrusts,
&DsDomainCount );
if (Status == ERROR_SUCCESS)
{
LSA_UNICODE_STRING LsaDCName;
RtlInitUnicodeString(&LsaDCName,
pDomainControllerInfo->DomainControllerName);
NtStatus = LsaOpenPolicy(&LsaDCName,
&ObjectAttributes,
POLICY_VIEW_LOCAL_INFORMATION,
&hPolicy);
Status = RtlNtStatusToDosError(NtStatus);
if (Status == ERROR_SUCCESS)
{
for (Index = 0; Index < DsDomainCount; Index++)
{
PDS_DOMAIN_TRUSTS pDomainTrust;
DFSSTATUS ForestTrustStatus;
pDomainTrust = &pDsDomainTrusts[Index];
//
// If the domain is not in our forest, and it is
// not MIT kerberos and it has transitive trust,
// we have found a match. Add that forest and its
// domains to our list.
//
if (!(pDomainTrust->Flags & DS_DOMAIN_IN_FOREST) &&
(pDomainTrust->TrustType != TRUST_TYPE_MIT) &&
(pDomainTrust->TrustAttributes & TRUST_ATTRIBUTE_FOREST_TRANSITIVE))
{
//
// Ignore status return.
//
// If any one of the domain enumeration fails,
// we dont want to fail the entire domain
// table creation.
//
ForestTrustStatus = DfsAddForestDomainsToDomainTable( hPolicy,
pDomainTrust->DnsDomainName);
}
}
LsaClose(hPolicy);
}
NetApiBufferFree(pDsDomainTrusts);
}
NetApiBufferFree(pDomainControllerInfo);
}
return Status;
}
//
//AddDomainToDomainTable: given a domain name add it to our table
//
DFSSTATUS
DfsXForest::AddDomainToDomainTable(LPWSTR DomainName,
LPWSTR BindDomainName,
BOOLEAN Netbios)
{
UNICODE_STRING DomainNameUnicode;
UNICODE_STRING BindDomainNameUnicode;
DFSSTATUS Status;
RtlInitUnicodeString(&DomainNameUnicode, DomainName);
if (BindDomainName != NULL)
{
RtlInitUnicodeString(&BindDomainNameUnicode, BindDomainName);
}
Status = AddDomainToDomainTable( &DomainNameUnicode,
BindDomainName ? &BindDomainNameUnicode : NULL,
Netbios);
return Status;
}
//
//AddDomainToDomainTable: given a domain name add it to our table
//
DFSSTATUS
DfsXForest::AddDomainToDomainTable(PUNICODE_STRING DomainName,
PUNICODE_STRING BindDomainName,
BOOLEAN Netbios)
{
DFSSTATUS Status;
Status = _DomainTable.AddDomain(DomainName, BindDomainName, Netbios);
//
// Buffer overflow indicates that we went over the hash table limit.
// However, we keep adding so that we have a count of how many we
// missed.
//
if (Status == ERROR_BUFFER_OVERFLOW)
{
Status = ERROR_SUCCESS;
}
return Status;
}
//
// AddDomain: Insert the domain name in our hash table.
//
DFSSTATUS
DfsDomainNameTable::AddDomain(PUNICODE_STRING DomainName,
PUNICODE_STRING BindDomainName,
BOOLEAN Netbios)
{
PDFS_DOMAIN_NAME_DATA pDomainData = NULL;
ULONG DomainDataLength;
DFSSTATUS Status = ERROR_SUCCESS;
USHORT DomainNameLength = DomainName->Length;
USHORT MaxDomainNameLength = DomainNameLength + sizeof(WCHAR);
USHORT BindDomainNameLength = 0;
USHORT MaxBindDomainNameLength = 0;
NTSTATUS NtStatus;
ULONG CurrentSize = 0;
if (_DomainReferralSize > MAX_REFERRAL_SIZE)
{
_DomainsSkipped++;
return ERROR_BUFFER_OVERFLOW;
}
CurrentSize = sizeof(DFS_REFERRAL_V3) + sizeof(UNICODE_PATH_SEP)
+ DomainName->Length + sizeof(UNICODE_NULL);
if (BindDomainName != NULL)
{
BindDomainNameLength = BindDomainName->Length;
MaxBindDomainNameLength = BindDomainNameLength + sizeof(WCHAR);
}
DomainDataLength = sizeof(DFS_DOMAIN_NAME_DATA) + MaxDomainNameLength + MaxBindDomainNameLength;
pDomainData = (PDFS_DOMAIN_NAME_DATA) DfsAllocateForDomainTable(DomainDataLength);
if (pDomainData != NULL)
{
RtlZeroMemory(pDomainData, DomainDataLength);
pDomainData->Header.RefCount = 1;
pDomainData->Header.pvKey = (PVOID)&pDomainData->DomainInfo.DomainName;
pDomainData->Header.pData = (PVOID)pDomainData;
pDomainData->DomainInfo.Netbios = Netbios;
RtlInitUnicodeString(&pDomainData->DomainInfo.DomainName, NULL);
pDomainData->DomainInfo.DomainName.Buffer = (LPWSTR)(pDomainData + 1);
pDomainData->DomainInfo.DomainName.MaximumLength = MaxDomainNameLength;
pDomainData->DomainInfo.DomainName.Length = DomainNameLength;
RtlCopyMemory(pDomainData->DomainInfo.DomainName.Buffer,
DomainName->Buffer,
DomainNameLength);
pDomainData->DomainInfo.DomainName.Buffer[DomainNameLength/sizeof(WCHAR)] = 0;
if (BindDomainName)
{
pDomainData->DomainInfo.BindDomainName.Buffer = (LPWSTR)(((ULONG_PTR)(pDomainData + 1)) + MaxDomainNameLength);
pDomainData->DomainInfo.BindDomainName.MaximumLength = MaxBindDomainNameLength;
pDomainData->DomainInfo.BindDomainName.Length = BindDomainNameLength;
RtlCopyMemory(pDomainData->DomainInfo.BindDomainName.Buffer,
BindDomainName->Buffer,
BindDomainNameLength);
pDomainData->DomainInfo.BindDomainName.Buffer[BindDomainNameLength/sizeof(WCHAR)] = 0;
pDomainData->DomainInfo.UseBindDomain = TRUE;
}
}
else
{
Status = ERROR_NOT_ENOUGH_MEMORY;
}
if (Status == ERROR_SUCCESS)
{
NtStatus = SHashInsertKey(_pDomainNameTable,
pDomainData,
&pDomainData->DomainInfo.DomainName,
SHASH_FAIL_IFFOUND);
if (NtStatus != STATUS_SUCCESS)
{
DfsDeallocateForDomainTable( pDomainData );
//
// if we have collision, we already know this name and
// we can ignore this error.
//
if (NtStatus != STATUS_OBJECT_NAME_COLLISION)
{
Status = RtlNtStatusToDosError( NtStatus );
}
}
else
{
//
// we successfully added to the table, bump up our
// DomainReferralSize.
//
InterlockedDecrement(&pDomainData->Header.RefCount);
_DomainReferralSize += CurrentSize;
}
}
return Status;
}
//
// InvalidateDomainTable: Run through the hash and throw out all our
// entries.
//
//
VOID
DfsDomainNameTable::InvalidateDomainTable(VOID)
{
SHASH_ITERATOR Iter;
PDFS_DOMAIN_NAME_DATA pExistingData = NULL;
NTSTATUS NtStatus;
pExistingData = (PDFS_DOMAIN_NAME_DATA) SHashStartEnumerate(&Iter, _pDomainNameTable);
while (pExistingData != NULL)
{
//
// Remove this item. There's nothing we can do if we hit errors
// except to keep going.
//
NtStatus = SHashRemoveKey(_pDomainNameTable,
&pExistingData->DomainInfo.DomainName,
NULL );
//
// ignore status here, since we want to keep going.
//
pExistingData = (PDFS_DOMAIN_NAME_DATA) SHashNextEnumerate(&Iter, _pDomainNameTable);
}
SHashFinishEnumerate(&Iter, _pDomainNameTable);
}
//
// GetCount: Run through our hash and get a count of the number of
// items. I wish we could ask the hash table for the count.
//
ULONG
DfsDomainNameTable::GetCount(VOID)
{
SHASH_ITERATOR Iter;
PDFS_DOMAIN_NAME_DATA pExistingData = NULL;
ULONG nEntries = 0;
pExistingData = (PDFS_DOMAIN_NAME_DATA) SHashStartEnumerate(&Iter, _pDomainNameTable);
while (pExistingData != NULL)
{
nEntries++;
pExistingData = (PDFS_DOMAIN_NAME_DATA) SHashNextEnumerate(&Iter, _pDomainNameTable);
}
SHashFinishEnumerate(&Iter, _pDomainNameTable);
return nEntries;
}
//
// FinishDomainNameEnumerate
//
VOID
DfsDomainNameTable::FinishDomainNameEnumerate( SHASH_ITERATOR *pIter )
{
SHashFinishEnumerate( pIter, _pDomainNameTable);
}
// Allocate and deallocate the cache data entry
PVOID
DfsAllocateForDomainTable(ULONG Size )
{
PVOID RetValue = NULL;
if (Size)
{
RetValue = (PVOID) new BYTE[Size];
if (RetValue != NULL)
{
RtlZeroMemory( RetValue, Size );
}
}
return RetValue;
}
VOID
DfsDeallocateForDomainTable(PVOID pPointer )
{
if (pPointer)
{
delete [] (PBYTE)pPointer;
}
}