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.
 
 
 
 
 
 

2173 lines
58 KiB

/*++
Copyright (c) 1987-2001 Microsoft Corporation
Module Name:
ftnfoctx.c
Abstract:
Utility routines to manipulate the forest trust context
Author:
27-Jul-00 (cliffv)
Environment:
User mode only.
Contains NT-specific code.
Requires ANSI C extensions: slash-slash comments, long external names.
Revision History:
--*/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <netdebug.h>
#include <ntlsa.h>
#include <ftnfoctx.h>
#include <align.h> // ROUND_UP_POINTER
#include <rpcutil.h> // MIDL_user_free
#include <stdlib.h> // qsort
VOID
NetpInitFtinfoContext(
OUT PNL_FTINFO_CONTEXT FtinfoContext
)
/*++
Routine Description:
Routine to initialize the Ftinfo context structure.
Arguments:
FtinfoContext - Context to initialize
Return Value:
None
--*/
{
RtlZeroMemory( FtinfoContext, sizeof(*FtinfoContext) );
InitializeListHead( &FtinfoContext->FtinfoList );
}
VOID
NetpMarshalFtinfoEntry (
IN PLSA_FOREST_TRUST_RECORD InFtinfoRecord,
OUT PLSA_FOREST_TRUST_RECORD OutFtinfoRecord,
IN OUT LPBYTE *WherePtr
)
/*++
Routine Description:
Routine to marshalls a single Ftinfo entry
Arguments:
InFtinfoRecord - Template to copy into InFtinfoRecord
OutFtinfoRecord - Entry to fill in
On input, points to a zeroed buffer.
WherePtr - On input, specifies where to marshal to.
On output, points to the first byte past the marshalled data.
Return Value:
TRUE - Success
FALSE - if no memory can be allocated
--*/
{
LPBYTE Where = *WherePtr;
ULONG Size;
ULONG SidSize;
ULONG NameSize;
NetpAssert( Where == ROUND_UP_POINTER( Where, ALIGN_WORST ));
//
// Copy the fixed size data
//
OutFtinfoRecord->ForestTrustType = InFtinfoRecord->ForestTrustType;
OutFtinfoRecord->Flags = InFtinfoRecord->Flags;
OutFtinfoRecord->Time = InFtinfoRecord->Time;
//
// Fill in a domain entry
//
switch( InFtinfoRecord->ForestTrustType ) {
case ForestTrustDomainInfo:
//
// Copy the DWORD aligned data
//
if ( InFtinfoRecord->ForestTrustData.DomainInfo.Sid != NULL ) {
SidSize = RtlLengthSid( InFtinfoRecord->ForestTrustData.DomainInfo.Sid );
OutFtinfoRecord->ForestTrustData.DomainInfo.Sid = (PISID) Where;
RtlCopyMemory( Where, InFtinfoRecord->ForestTrustData.DomainInfo.Sid, SidSize );
Where += SidSize;
}
//
// Copy the WCHAR aligned data
//
NameSize = InFtinfoRecord->ForestTrustData.DomainInfo.DnsName.Length;
if ( NameSize != 0 ) {
OutFtinfoRecord->ForestTrustData.DomainInfo.DnsName.Buffer = (LPWSTR) Where;
OutFtinfoRecord->ForestTrustData.DomainInfo.DnsName.MaximumLength = (USHORT) (NameSize+sizeof(WCHAR));
OutFtinfoRecord->ForestTrustData.DomainInfo.DnsName.Length = (USHORT)NameSize;
RtlCopyMemory( Where, InFtinfoRecord->ForestTrustData.DomainInfo.DnsName.Buffer, NameSize );
Where += NameSize;
*((LPWSTR)Where) = L'\0';
Where += sizeof(WCHAR);
}
NameSize = InFtinfoRecord->ForestTrustData.DomainInfo.NetbiosName.Length;
if ( NameSize != 0 ) {
OutFtinfoRecord->ForestTrustData.DomainInfo.NetbiosName.Buffer = (LPWSTR) Where;
OutFtinfoRecord->ForestTrustData.DomainInfo.NetbiosName.MaximumLength = (USHORT) (NameSize+sizeof(WCHAR));
OutFtinfoRecord->ForestTrustData.DomainInfo.NetbiosName.Length = (USHORT)NameSize;
RtlCopyMemory( Where, InFtinfoRecord->ForestTrustData.DomainInfo.NetbiosName.Buffer, NameSize );
Where += NameSize;
*((LPWSTR)Where) = L'\0';
Where += sizeof(WCHAR);
}
break;
//
// Fill in a TLN entry
//
case ForestTrustTopLevelName:
case ForestTrustTopLevelNameEx:
//
// Copy the WCHAR aligned data
//
NameSize = InFtinfoRecord->ForestTrustData.TopLevelName.Length;
if ( NameSize != 0 ) {
OutFtinfoRecord->ForestTrustData.TopLevelName.Buffer = (LPWSTR) Where;
OutFtinfoRecord->ForestTrustData.TopLevelName.MaximumLength = (USHORT) (NameSize+sizeof(WCHAR));
OutFtinfoRecord->ForestTrustData.TopLevelName.Length = (USHORT)NameSize;
RtlCopyMemory( Where, InFtinfoRecord->ForestTrustData.TopLevelName.Buffer, NameSize );
Where += NameSize;
*((LPWSTR)Where) = L'\0';
Where += sizeof(WCHAR);
}
break;
default:
NetpAssert( FALSE );
}
Where = ROUND_UP_POINTER( Where, ALIGN_WORST );
*WherePtr = Where;
}
VOID
NetpCompareHelper (
IN PUNICODE_STRING Name,
IN OUT PULONG Index,
OUT PUNICODE_STRING CurrentLabel
)
/*++
Routine Description:
This routine is a helper routine for finding the next rightmost label in a string.
Arguments:
Name - The input dns name. The dns name should not have a trailing .
Index - On input, should contain the value returned by the previous call to this routine.
On input for the first call, should be set to Name->Length/sizeof(WCHAR).
On output, zero is returned to indicate that this is the last of the name. The
caller should not call again. Any other value output is a context for the next
call to this routine.
CurrentLabel - Returns a descriptor describing the substring which is the next label.
Return Value:
None.
--*/
{
ULONG PreviousIndex = *Index;
ULONG CurrentIndex = *Index;
ULONG LabelIndex;
NetpAssert( CurrentIndex != 0 );
//
// Find the beginning of the next label
//
while ( CurrentIndex > 0 ) {
CurrentIndex--;
if ( Name->Buffer[CurrentIndex] == L'.' ) {
break;
}
}
if ( CurrentIndex == 0 ) {
LabelIndex = CurrentIndex;
} else {
LabelIndex = CurrentIndex + 1;
}
//
// Return it to the caller
//
CurrentLabel->Buffer = &Name->Buffer[LabelIndex];
CurrentLabel->Length = (USHORT)((PreviousIndex - LabelIndex) * sizeof(WCHAR));
CurrentLabel->MaximumLength = CurrentLabel->Length;
*Index = CurrentIndex;
}
int
NetpCompareDnsNameWithSortOrder(
IN PUNICODE_STRING Name1,
IN PUNICODE_STRING Name2
)
/*++
Routine Description:
Routine to compare two DNS names. The DNS names must not have a trailing "."
Labels are compare right to left to present a pleasent viewing order.
Arguments:
Name1 - First name to compare.
Name2 - Second name to compare.
Return Value:
Signed value that gives the results of the comparison:
Zero - String1 equals String2
< Zero - String1 less than String2
> Zero - String1 greater than String2
--*/
{
ULONG Index1 = Name1->Length/sizeof(WCHAR);
ULONG Index2 = Name2->Length/sizeof(WCHAR);
UNICODE_STRING Label1;
UNICODE_STRING Label2;
LONG Result;
//
// Loop comparing labels
//
while ( Index1 != 0 && Index2 != 0 ) {
//
// Get the next label from each string
//
NetpCompareHelper ( Name1, &Index1, &Label1 );
NetpCompareHelper ( Name2, &Index2, &Label2 );
//
// If the labels are different,
// return that result to the caller.
//
Result = RtlCompareUnicodeString( &Label1, &Label2, TRUE );
if ( Result != 0 ) {
return (int)Result;
}
}
//
// ASSERT: one label is a (proper) substring of the other
//
// If the first name is longer, indicate it is greater than the second
//
return Index1-Index2;
}
int __cdecl NetpCompareFtinfoEntryDns(
const void *String1,
const void *String2
)
/*++
Routine Description:
qsort comparison routine for Dns string in Ftinfo entries
Arguments:
String1: First string to compare
String2: Second string to compare
Return Value:
Signed value that gives the results of the comparison:
Zero - String1 equals String2
< Zero - String1 less than String2
> Zero - String1 greater than String2
--*/
{
PLSA_FOREST_TRUST_RECORD Entry1 = *((PLSA_FOREST_TRUST_RECORD *)String1);
PLSA_FOREST_TRUST_RECORD Entry2 = *((PLSA_FOREST_TRUST_RECORD *)String2);
PUNICODE_STRING Name1;
PUNICODE_STRING Name2;
int Result;
//
// Get the name from the entry
//
switch ( Entry1->ForestTrustType ) {
case ForestTrustTopLevelName:
case ForestTrustTopLevelNameEx:
Name1 = &Entry1->ForestTrustData.TopLevelName;
break;
case ForestTrustDomainInfo:
Name1 = &Entry1->ForestTrustData.DomainInfo.DnsName;
break;
default:
//
// If Entry2 can be recognized,
// then entry 2 is less than this one.
//
switch ( Entry2->ForestTrustType ) {
case ForestTrustTopLevelName:
case ForestTrustTopLevelNameEx:
case ForestTrustDomainInfo:
return 1; // This name is greater than the other
}
//
// Otherwise simply leave them in the same order
//
if ((Entry1 - Entry2) < 0 ) {
return -1;
} else if ((Entry1 - Entry2) > 0 ) {
return 1;
} else {
return 0;
}
}
switch ( Entry2->ForestTrustType ) {
case ForestTrustTopLevelName:
case ForestTrustTopLevelNameEx:
Name2 = &Entry2->ForestTrustData.TopLevelName;
break;
case ForestTrustDomainInfo:
Name2 = &Entry2->ForestTrustData.DomainInfo.DnsName;
break;
default:
//
// Since Entry1 is a recognized type,
// this Entry2 is greater.
//
return -1; // This name is greater than the other
}
//
// If the labels are different,
// return the difference to the caller.
//
Result = NetpCompareDnsNameWithSortOrder( Name1, Name2 );
if ( Result != 0 ) {
return Result;
}
//
// If the labels are the same,
// indicate TLNs are before domain info records.
//
return Entry1->ForestTrustType - Entry2->ForestTrustType;
}
int
NetpCompareSid(
PSID Sid1,
PSID Sid2
)
/*++
Routine description:
SID comparison routine that actually indicates if one sid is greater than another
Arguments:
Sid1 - First Sid
Sid2 - Second Sid
Returns:
Signed value that gives the results of the comparison:
Zero - String1 equals String2
< Zero - String1 less than String2
> Zero - String1 greater than String2
--*/
{
DWORD Size1;
DWORD Size2;
LPBYTE Byte1;
LPBYTE Byte2;
ULONG i;
NetpAssert( Sid1 && RtlValidSid( Sid1 ));
NetpAssert( Sid2 && RtlValidSid( Sid2 ));
//
// The NULL SID is smaller
//
if ( Sid1 == NULL ) {
if ( Sid2 != NULL ) {
return -1;
} else {
return 0;
}
}
if ( Sid2 == NULL ) {
if ( Sid1 != NULL ) {
return 1;
} else {
return 0;
}
}
//
// The longer sid is greater
//
Size1 = RtlLengthSid( Sid1 );
Size2 = RtlLengthSid( Sid2 );
if ( Size1 != Size2 ) {
return Size1 - Size2;
}
//
// Otherwise compare the bytes
//
Byte1 = (LPBYTE)Sid1;
Byte2 = (LPBYTE)Sid2;
for ( i=0; i<Size1; i++ ) {
if ( Byte1[i] != Byte2[i] ) {
return Byte1[i] - Byte2[i];
}
}
return 0;
}
int __cdecl NetpCompareFtinfoEntrySid(
const void *String1,
const void *String2
)
/*++
Routine Description:
qsort comparison routine for Sid string in Ftinfo entries
Arguments:
String1: First string to compare
String2: Second string to compare
Return Value:
Signed value that gives the results of the comparison:
Zero - String1 equals String2
< Zero - String1 less than String2
> Zero - String1 greater than String2
--*/
{
PLSA_FOREST_TRUST_RECORD Entry1 = *((PLSA_FOREST_TRUST_RECORD *)String1);
PLSA_FOREST_TRUST_RECORD Entry2 = *((PLSA_FOREST_TRUST_RECORD *)String2);
PSID Sid1;
PSID Sid2;
int Result;
//
// Get the Sid from the entry
//
switch ( Entry1->ForestTrustType ) {
case ForestTrustDomainInfo:
Sid1 = Entry1->ForestTrustData.DomainInfo.Sid;
break;
default:
//
// If Entry2 can be recognized,
// then entry 2 is less than this one.
//
switch ( Entry2->ForestTrustType ) {
case ForestTrustDomainInfo:
return 1; // This name is greater than the other
}
//
// Otherwise simply leave them in the same order
//
if ((Entry1 - Entry2) < 0 ) {
return -1;
} else if ((Entry1 - Entry2) > 0 ) {
return 1;
} else {
return 0;
}
}
switch ( Entry2->ForestTrustType ) {
case ForestTrustDomainInfo:
Sid2 = Entry2->ForestTrustData.DomainInfo.Sid;
break;
default:
//
// Since Entry1 is a recognized type,
// this Entry2 is greater.
//
return -1; // This name is greater than the other
}
//
// Simply return the different of the sids.
//
return NetpCompareSid( Sid1, Sid2 );
}
int __cdecl NetpCompareFtinfoEntryNetbios(
const void *String1,
const void *String2
)
/*++
Routine Description:
qsort comparison routine for Netbios name in Ftinfo entries
Arguments:
String1: First string to compare
String2: Second string to compare
Return Value:
Signed value that gives the results of the comparison:
Zero - String1 equals String2
< Zero - String1 less than String2
> Zero - String1 greater than String2
--*/
{
PLSA_FOREST_TRUST_RECORD Entry1 = *((PLSA_FOREST_TRUST_RECORD *)String1);
PLSA_FOREST_TRUST_RECORD Entry2 = *((PLSA_FOREST_TRUST_RECORD *)String2);
PUNICODE_STRING Name1;
PUNICODE_STRING Name2;
int Result;
//
// Get the Sid from the entry
//
switch ( Entry1->ForestTrustType ) {
case ForestTrustDomainInfo:
Name1 = &Entry1->ForestTrustData.DomainInfo.NetbiosName;
if ( Name1->Length != 0 && Name1->Buffer != NULL ) {
break;
}
default:
//
// If Entry2 can be recognized,
// then entry 2 is less than this one.
//
switch ( Entry2->ForestTrustType ) {
case ForestTrustDomainInfo:
return 1; // This name is greater than the other
}
//
// Otherwise simply leave them in the same order
//
if ((Entry1 - Entry2) < 0 ) {
return -1;
} else if ((Entry1 - Entry2) > 0 ) {
return 1;
} else {
return 0;
}
}
switch ( Entry2->ForestTrustType ) {
case ForestTrustDomainInfo:
Name2 = &Entry2->ForestTrustData.DomainInfo.NetbiosName;
if ( Name2->Length != 0 && Name2->Buffer != NULL ) {
break;
}
default:
//
// Since Entry1 is a recognized type,
// this Entry2 is greater.
//
return -1; // This name is greater than the other
}
//
// Simply return the difference of the names
//
return RtlCompareUnicodeString( Name1, Name2, TRUE );
}
PLSA_FOREST_TRUST_INFORMATION
NetpCopyFtinfoContext(
IN PNL_FTINFO_CONTEXT FtinfoContext
)
/*++
Routine Description:
Routine to allocate an FTinfo array from an FTinfo context.
Arguments:
FtinfoContext - Context to use
The caller must have previously called NetpInitFtinfoContext
Return Value:
FTinfo array. The caller should free this array using MIDL_user_free.
If NULL, not enough memory was available.
--*/
{
PNL_FTINFO_ENTRY FtinfoEntry;
PLIST_ENTRY ListEntry;
PLSA_FOREST_TRUST_INFORMATION LocalForestTrustInfo;
LPBYTE Where;
ULONG Size;
ULONG i;
PLSA_FOREST_TRUST_RECORD Entries;
//
// Allocate a structure to return to the caller.
//
Size = ROUND_UP_COUNT( sizeof( *LocalForestTrustInfo ), ALIGN_WORST) +
ROUND_UP_COUNT( FtinfoContext->FtinfoCount * sizeof(LSA_FOREST_TRUST_RECORD), ALIGN_WORST) +
ROUND_UP_COUNT( FtinfoContext->FtinfoCount * sizeof(PLSA_FOREST_TRUST_RECORD), ALIGN_WORST) +
FtinfoContext->FtinfoSize;
LocalForestTrustInfo = MIDL_user_allocate( Size );
if ( LocalForestTrustInfo == NULL ) {
return NULL;
}
RtlZeroMemory( LocalForestTrustInfo, Size );
Where = (LPBYTE)(LocalForestTrustInfo+1);
Where = ROUND_UP_POINTER( Where, ALIGN_WORST );
//
// Fill it in
//
LocalForestTrustInfo->RecordCount = FtinfoContext->FtinfoCount;
//
// Grab a huge chunk of ALIGN_WORST
// (We fill it in during the loop below.)
//
Entries = (PLSA_FOREST_TRUST_RECORD) Where;
Where = (LPBYTE)(&Entries[FtinfoContext->FtinfoCount]);
Where = ROUND_UP_POINTER( Where, ALIGN_WORST );
//
// Grab a huge chunk of dword aligned
// (We fill it in during the loop below.)
//
LocalForestTrustInfo->Entries = (PLSA_FOREST_TRUST_RECORD *) Where;
Where = (LPBYTE)(&LocalForestTrustInfo->Entries[FtinfoContext->FtinfoCount]);
Where = ROUND_UP_POINTER( Where, ALIGN_WORST );
//
// Fill in the individual entries
//
i = 0;
for ( ListEntry = FtinfoContext->FtinfoList.Flink ;
ListEntry != &FtinfoContext->FtinfoList ;
ListEntry = ListEntry->Flink) {
FtinfoEntry = CONTAINING_RECORD( ListEntry, NL_FTINFO_ENTRY, Next );
LocalForestTrustInfo->Entries[i] = &Entries[i];
NetpMarshalFtinfoEntry (
&FtinfoEntry->Record,
&Entries[i],
&Where );
i++;
}
NetpAssert( i == FtinfoContext->FtinfoCount );
NetpAssert( Where == ((LPBYTE)LocalForestTrustInfo) + Size );
//
// Sort them into alphabetical order
//
qsort( LocalForestTrustInfo->Entries,
LocalForestTrustInfo->RecordCount,
sizeof(PLSA_FOREST_TRUST_RECORD),
NetpCompareFtinfoEntryDns );
//
// Return the allocated buffer to the caller.
//
return LocalForestTrustInfo;
}
VOID
NetpCleanFtinfoContext(
IN PNL_FTINFO_CONTEXT FtinfoContext
)
/*++
Routine Description:
Routine to cleanup the Ftinfo context structure.
Arguments:
FtinfoContext - Context to clean
The caller must have previously called NetpInitFtinfoContext
Return Value:
None
--*/
{
PLIST_ENTRY ListEntry;
PNL_FTINFO_ENTRY FtinfoEntry;
//
// Loop freeing the entries
//
while ( !IsListEmpty( &FtinfoContext->FtinfoList ) ) {
//
// Delink an entry
//
ListEntry = RemoveHeadList( &FtinfoContext->FtinfoList );
FtinfoEntry = CONTAINING_RECORD( ListEntry, NL_FTINFO_ENTRY, Next );
FtinfoContext->FtinfoCount -= 1;
FtinfoContext->FtinfoSize -= FtinfoEntry->Size;
RtlFreeHeap( RtlProcessHeap(), 0, FtinfoEntry );
}
NetpAssert( FtinfoContext->FtinfoCount == 0 );
NetpAssert( FtinfoContext->FtinfoSize == 0 );
}
PLSA_FOREST_TRUST_RECORD
NetpAllocFtinfoEntry2 (
IN PNL_FTINFO_CONTEXT FtinfoContext,
IN PLSA_FOREST_TRUST_RECORD InFtinfoRecord
)
/*++
Routine Description:
Same as NetpAllocFtinfoEntry except takes a template of an FTinfo entry on input.
Arguments:
FtinfoContext - Context to link the entry onto.
InFtinfoRecord - Template to copy into InFtinfoRecord
Return Value:
Returns the address of the allocated forest trust record.
The caller should not and cannot deallocate this buffer. It has a header and is
linked into the FtinfoContext.
Returns NULL if no memory can be allocated.
--*/
{
PNL_FTINFO_ENTRY FtinfoEntry;
ULONG Size = ROUND_UP_COUNT(sizeof(NL_FTINFO_ENTRY), ALIGN_WORST);
ULONG DataSize = 0;
LPBYTE Where;
//
// Compute the size of the entry.
//
switch( InFtinfoRecord->ForestTrustType ) {
case ForestTrustDomainInfo:
if ( InFtinfoRecord->ForestTrustData.DomainInfo.Sid != NULL ) {
DataSize += RtlLengthSid( InFtinfoRecord->ForestTrustData.DomainInfo.Sid );
}
if ( InFtinfoRecord->ForestTrustData.DomainInfo.DnsName.Length != 0 ) {
DataSize += InFtinfoRecord->ForestTrustData.DomainInfo.DnsName.Length + sizeof(WCHAR);
}
if ( InFtinfoRecord->ForestTrustData.DomainInfo.NetbiosName.Length != 0 ) {
DataSize += InFtinfoRecord->ForestTrustData.DomainInfo.NetbiosName.Length + sizeof(WCHAR);
}
break;
case ForestTrustTopLevelName:
case ForestTrustTopLevelNameEx:
if ( InFtinfoRecord->ForestTrustData.TopLevelName.Length != 0 ) {
DataSize += InFtinfoRecord->ForestTrustData.TopLevelName.Length + sizeof(WCHAR);
}
break;
default:
NetpAssert( FALSE );
return NULL;
}
DataSize = ROUND_UP_COUNT(DataSize, ALIGN_WORST);
//
// Allocate an entry
//
Size += DataSize;
FtinfoEntry = RtlAllocateHeap( RtlProcessHeap(), 0, Size );
if ( FtinfoEntry == NULL ) {
return NULL;
}
RtlZeroMemory( FtinfoEntry, Size );
Where = (LPBYTE)(FtinfoEntry+1);
//
// Fill it in.
//
FtinfoEntry->Size = DataSize;
NetpMarshalFtinfoEntry ( InFtinfoRecord,
&FtinfoEntry->Record,
&Where );
NetpAssert( Where == ((LPBYTE)FtinfoEntry) + Size )
//
// Link it onto the list
//
InsertHeadList( &FtinfoContext->FtinfoList, &FtinfoEntry->Next );
FtinfoContext->FtinfoSize += FtinfoEntry->Size;
FtinfoContext->FtinfoCount += 1;
return &FtinfoEntry->Record;
}
BOOLEAN
NetpAllocFtinfoEntry (
IN PNL_FTINFO_CONTEXT FtinfoContext,
IN LSA_FOREST_TRUST_RECORD_TYPE ForestTrustType,
IN PUNICODE_STRING Name,
IN PSID Sid,
IN PUNICODE_STRING NetbiosName
)
/*++
Routine Description:
Routine to allocate a single Ftinfo entry and link it onto the context.
Arguments:
FtinfoContext - Context to link the entry onto.
ForestTypeType - Specifies the type of record to allocate. This must be
ForestTrustTopLevelName or ForestTrustDomainInfo.
Name - Specifies the name for the record.
Sid - Specifies the SID for the record. (Ignored for ForestTrustTopLevelName.)
NetbiosName - Specifies the netbios name for the record. (Ignored for ForestTrustTopLevelName.)
Return Value:
TRUE - Success
FALSE - if no memory can be allocated
--*/
{
LSA_FOREST_TRUST_RECORD FtinfoRecord = {0};
//
// Initialize the template Ftinfo entry
//
FtinfoRecord.ForestTrustType = ForestTrustType;
switch( ForestTrustType ) {
case ForestTrustDomainInfo:
FtinfoRecord.ForestTrustData.DomainInfo.Sid = Sid;
FtinfoRecord.ForestTrustData.DomainInfo.DnsName = *Name;
FtinfoRecord.ForestTrustData.DomainInfo.NetbiosName = *NetbiosName;
break;
case ForestTrustTopLevelName:
case ForestTrustTopLevelNameEx:
FtinfoRecord.ForestTrustData.TopLevelName = *Name;
break;
default:
NetpAssert( FALSE );
return FALSE;
}
//
// Call the routine that takes a template and does the rest of the job
//
return (NetpAllocFtinfoEntry2( FtinfoContext, &FtinfoRecord ) != NULL);
}
BOOLEAN
NetpIsSubordinate(
IN const UNICODE_STRING * Subordinate,
IN const UNICODE_STRING * Superior,
IN BOOLEAN EqualReturnsTrue
)
/*++
Routine Description:
Determines if Subordinate string is indeed subordinate to Superior
For example, "NY.acme.com" is subordinate to "acme.com", but
"NY.acme.com" is NOT subordinate to "me.com" or "NY.acme.com"
Arguments:
Subordinate name to test for subordinate status
Superior name to test for superior status
EqualReturnsTrue - TRUE if equal names should return TRUE also
Returns:
TRUE is Subordinate is subordinate to Superior
FALSE otherwise
--*/
{
USHORT SubIndex, SupIndex;
UNICODE_STRING Temp;
ASSERT( Subordinate && Subordinate->Buffer );
ASSERT( Superior && Superior->Buffer );
//
// If equal names are to be considered subordinate,
// compare the names for equality.
//
if ( EqualReturnsTrue &&
RtlEqualUnicodeString( Subordinate, Superior, TRUE )) {
return TRUE;
}
//
// A subordinate name must be longer than the superior name
//
if ( Subordinate->Length <= Superior->Length ) {
return FALSE;
}
//
// Subordinate name must be separated from the superior part by a period
//
if ( Subordinate->Buffer[( Subordinate->Length - Superior->Length ) / sizeof( WCHAR ) - 1] != L'.' ) {
return FALSE;
}
//
// Ensure the trailing part of the two names are the same.
//
Temp = *Subordinate;
Temp.Buffer += ( Subordinate->Length - Superior->Length ) / sizeof( WCHAR );
Temp.Length = Superior->Length;
Temp.MaximumLength = Temp.Length;
if ( !RtlEqualUnicodeString( &Temp, Superior, TRUE )) {
return FALSE;
}
return TRUE;
}
BOOLEAN
NetpAddTlnFtinfoEntry (
IN PNL_FTINFO_CONTEXT FtinfoContext,
IN PUNICODE_STRING Name
)
/*++
Routine Description:
Routine to add a TLN Ftinfo entry to the list.
If there is already a TLN that is equal to or superior to this one, this TLN is
ignored. (e.g., a TLN of a.acme.com is ignored of acme.com already exists in the list.)
If there is already a TLN that is inferior to this one, the inferior TLN is
removed and this one is added. (e.g., a TLN of acme.com causes an existing TLN of
a.acme.com to be replaced by the new entry.)
Arguments:
FtinfoContext - Context to link the entry onto.
Name - Specifies the name for the record.
Return Value:
TRUE - Success
FALSE - if no memory can be allocated
--*/
{
PNL_FTINFO_ENTRY FtinfoEntry;
PLIST_ENTRY ListEntry;
//
// Loop through the list of existing entries
//
for ( ListEntry = FtinfoContext->FtinfoList.Flink ;
ListEntry != &FtinfoContext->FtinfoList ;
) {
FtinfoEntry = CONTAINING_RECORD( ListEntry, NL_FTINFO_ENTRY, Next );
ListEntry = ListEntry->Flink;
//
// Ignore entries that aren't TLNs.
//
if ( FtinfoEntry->Record.ForestTrustType != ForestTrustTopLevelName ) {
continue;
}
//
// If the new name is subordinate (or equal to) to one already in the list,
// ignore the new name.
//
if ( NetpIsSubordinate( Name,
&FtinfoEntry->Record.ForestTrustData.TopLevelName,
TRUE ) ) {
return TRUE;
}
//
// If the existing name is subordinate to the new name,
// remove the existing name.
//
if ( NetpIsSubordinate( &FtinfoEntry->Record.ForestTrustData.TopLevelName,
Name,
FALSE ) ) {
RemoveEntryList( &FtinfoEntry->Next );
FtinfoContext->FtinfoCount -= 1;
FtinfoContext->FtinfoSize -= FtinfoEntry->Size;
RtlFreeHeap( RtlProcessHeap(), 0, FtinfoEntry );
// continue looping since there may be more names to remove
}
}
//
// Add the new entry to the list
//
return NetpAllocFtinfoEntry( FtinfoContext,
ForestTrustTopLevelName,
Name,
NULL, // No sid
NULL ); // No Netbios name
}
VOID
NetpMergeFtinfoHelper(
IN PLSA_FOREST_TRUST_INFORMATION NewForestTrustInfo,
IN PLSA_FOREST_TRUST_INFORMATION OldForestTrustInfo,
IN OUT PULONG NewIndex,
IN OUT PULONG OldIndex,
OUT PLSA_FOREST_TRUST_RECORD *NewEntry,
OUT PLSA_FOREST_TRUST_RECORD *OldEntry,
OUT PULONG OldFlags,
IN int (__cdecl *Routine) (const void *, const void *)
)
/*++
Routine Description:
This routine walks a pair of FTinfo arrays in sorted order and returns the next
entry. If both entries are the same in the sort order, this routine returns an entry
from both arrays
Arguments:
NewForestTrustInfo - Pointer to the first array
OldForestTrustInfo - Pointer to the second array
NewIndex - Current index into the first sorted array
OldIndex - Current index into the second sorted array
Before calling this routine the first time, the caller should set these parameters to zero.
Both indices zero triggers this routine to qsort the arrays.
The caller should *not* call this routine if both NewIndex and OldIndex are greater
than the corresponding record count.
NewEntry - Returns a pointer to an entry to be processed from the first sorted array.
OldEntry - Returns a pointer to an entry to be processed from the second sorted array.
Returns NULL if no entry is to be processed from the corresponding array.
OldFlags - Returns the Flags field that corresponds to OldEntry.
If there are duplicates of OldEntry, those duplicates are silently ignored by
this routine. This field returns the logical OR of the Flags field of those entries.
Routine - Comparison routine to passed to qsort to sort the FTinfo arrays.
Return Value:
None.
--*/
{
int RetVal;
//
// Sort the arrays
//
if ( *NewIndex == 0 && *OldIndex == 0 ) {
qsort( NewForestTrustInfo->Entries,
NewForestTrustInfo->RecordCount,
sizeof(PLSA_FOREST_TRUST_RECORD),
Routine );
qsort( OldForestTrustInfo->Entries,
OldForestTrustInfo->RecordCount,
sizeof(PLSA_FOREST_TRUST_RECORD),
Routine );
}
//
// Compare the first entry at the front of each list to determine which list
// to consume an entry from.
//
*NewEntry = NULL;
*OldEntry = NULL;
*OldFlags = 0;
if ( *NewIndex < NewForestTrustInfo->RecordCount ) {
//
// If neither list is empty,
// compare the entries to determine which is next.
//
if ( *OldIndex < OldForestTrustInfo->RecordCount ) {
RetVal = (*Routine)(
&NewForestTrustInfo->Entries[*NewIndex],
&OldForestTrustInfo->Entries[*OldIndex] );
//
// If the new entry is less than or equal to the old entry,
// consume the new entry.
//
if ( RetVal <= 0 ) {
*NewEntry = NewForestTrustInfo->Entries[*NewIndex];
(*NewIndex) ++;
}
//
// If the old entry is less than or equal to the new entry,
// consume the old entry.
//
if ( RetVal >= 0 ) {
*OldEntry = OldForestTrustInfo->Entries[*OldIndex];
(*OldIndex) ++;
}
//
// If the old list is empty and the new list isn't,
// consume an entry from the new list.
//
} else {
*NewEntry = NewForestTrustInfo->Entries[*NewIndex];
(*NewIndex) ++;
}
} else {
//
// If the new list is empty and the old list isn't,
// consume an entry from the old list.
//
if ( *OldIndex < OldForestTrustInfo->RecordCount ) {
*OldEntry = OldForestTrustInfo->Entries[*OldIndex];
(*OldIndex) ++;
}
}
//
// If we're returning an "OldEntry",
// weed out all duplicates of that OldEntry.
//
if ( *OldEntry != NULL ) {
*OldFlags |= (*OldEntry)->Flags;
while ( *OldIndex < OldForestTrustInfo->RecordCount ) {
//
// Stop as soon as we hit an entry that isn't a duplicate.
//
RetVal = (*Routine)(
OldEntry,
&OldForestTrustInfo->Entries[*OldIndex] );
if ( RetVal != 0 ) {
break;
}
*OldFlags |= (*OldEntry)->Flags;
}
}
}
NTSTATUS
NetpMergeFtinfo(
IN PUNICODE_STRING TrustedDomainName,
IN PLSA_FOREST_TRUST_INFORMATION InNewForestTrustInfo,
IN PLSA_FOREST_TRUST_INFORMATION InOldForestTrustInfo OPTIONAL,
OUT PLSA_FOREST_TRUST_INFORMATION *MergedForestTrustInfo
)
/*++
Routine Description:
This function merges the changes from a new FTinfo into an old FTinfo and
produces the resultant FTinfo.
The merged FTinfo records are a combinition of the new and old records.
Here's where the merged records come from:
* The TLN exclusion records are copied from the TDO intact.
* The TLN record from the trusted domain that maps to the dns domain name of the
TDO is copied enabled. This reflects the LSA requirement that such a TLN not
be disabled. For instance, if the TDO is for a.acme.com and there is a TLN for
a.acme.com that TLN will be enabled. Also, if the TDO is for a.acme.com and
there is a TLN for acme.com, that TLN will be enabled.
* All other TLN records from the trusted domain are copied disabled with the
following exceptions. If there is an enabled TLN on the TDO, all TLNs from the
trusted domain that equal (or are subordinate to) the TDO TLN are marked as
disabled. This follows the philosophy that new TLNs are imported as enabled.
For instance, if the TDO had an enabled TLN for a.acme.com that TLN will still
be enabled after the automatic update. If the TDO had an enabled TLN for
acme.com and the trusted forest now has a TLN for a.acme.com, the resultant
FTinfo will have an enabled TLN for a.acme.com.
* The domain records from the trusted domain are copied enabled with the
following exceptions. If there is a disabled domain record on the TDO whose
dns domain name, or domain sid exactly matches the domain record, then the domain
remains disabled. If there is a domain record on the TDO whose netbios name is
disabled and whose netbios name exactly matches the netbios name on a domain
record, then the netbios name is disabled.
* Finally, orphaned exclusion records (those that are not subordinate to any TLN)
are removed (Bug #707630).
Arguments:
TrustedDomainName - Trusted domain that is to be updated.
NewForestTrustInfo - Specified the new array of FTinfo records as returned from the
TrustedDomainName.
The Flags field and Time field of the TLN entries are ignored.
OldForestTrustInfo - Specified the array of FTinfo records as returned from the
TDO. This field may be NULL if there is no existing records.
MergedForestTrustInfo - Returns the resulant FTinfo records.
The caller should free this buffer using MIDL_user_free.
Return Value:
STATUS_SUCCESS: Success.
STATUS_INVALID_PARAMETER: One of the following happened:
* There was no New TLN that TrustedDomainName is subordinate to.
--*/
{
NTSTATUS Status;
LSA_FOREST_TRUST_INFORMATION OldForestTrustInfo;
LSA_FOREST_TRUST_INFORMATION NewForestTrustInfo;
LSA_FOREST_TRUST_INFORMATION NetbiosForestTrustInfo;
NL_FTINFO_CONTEXT FtinfoContext;
ULONG NewIndex;
ULONG OldIndex;
ULONG OldFlags;
PLSA_FOREST_TRUST_RECORD NewEntry;
PLSA_FOREST_TRUST_RECORD PreviousNewEntry;
PLSA_FOREST_TRUST_RECORD OldEntry;
BOOLEAN DomainTlnFound = FALSE;
PLSA_FOREST_TRUST_RECORD OldTlnPrefix;
ULONG Index;
//
// Initialization
//
*MergedForestTrustInfo = NULL;
NetpInitFtinfoContext( &FtinfoContext );
RtlZeroMemory( &OldForestTrustInfo, sizeof(OldForestTrustInfo) );
RtlZeroMemory( &NewForestTrustInfo, sizeof(NewForestTrustInfo) );
RtlZeroMemory( &NetbiosForestTrustInfo, sizeof(NetbiosForestTrustInfo) );
//
// Make a copy of the data that'll be qsorted so that we don't modify the caller's buffer.
//
if ( InOldForestTrustInfo != NULL ) {
OldForestTrustInfo.RecordCount = InOldForestTrustInfo->RecordCount;
OldForestTrustInfo.Entries = RtlAllocateHeap( RtlProcessHeap(), 0, OldForestTrustInfo.RecordCount * sizeof(PLSA_FOREST_TRUST_RECORD) );
if ( OldForestTrustInfo.Entries == NULL ) {
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
RtlCopyMemory( OldForestTrustInfo.Entries,
InOldForestTrustInfo->Entries,
OldForestTrustInfo.RecordCount * sizeof(PLSA_FOREST_TRUST_RECORD) );
}
NewForestTrustInfo.RecordCount = InNewForestTrustInfo->RecordCount;
NewForestTrustInfo.Entries = RtlAllocateHeap( RtlProcessHeap(), 0, NewForestTrustInfo.RecordCount * sizeof(PLSA_FOREST_TRUST_RECORD) );
if ( NewForestTrustInfo.Entries == NULL ) {
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
RtlCopyMemory( NewForestTrustInfo.Entries,
InNewForestTrustInfo->Entries,
NewForestTrustInfo.RecordCount * sizeof(PLSA_FOREST_TRUST_RECORD) );
//
// Allocate a temporary Ftinfo array containing all of the domain entries.
// Allocate it a worst case size.
//
NetbiosForestTrustInfo.Entries = RtlAllocateHeap(
RtlProcessHeap(),
0,
(OldForestTrustInfo.RecordCount+NewForestTrustInfo.RecordCount) * sizeof(PLSA_FOREST_TRUST_RECORD) );
if ( NetbiosForestTrustInfo.Entries == NULL ) {
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
//
// Loop through each list in DNS canonical order processing the least entry.
//
// This loop handles TLN and TLNEX entries only
//
NewIndex = 0;
OldIndex = 0;
OldTlnPrefix = NULL;
PreviousNewEntry = NULL;
while ( NewIndex < NewForestTrustInfo.RecordCount ||
OldIndex < OldForestTrustInfo.RecordCount ) {
//
// Grab the next entry from each of the sorted arrays
//
NetpMergeFtinfoHelper( &NewForestTrustInfo,
&OldForestTrustInfo,
&NewIndex,
&OldIndex,
&NewEntry,
&OldEntry,
&OldFlags,
NetpCompareFtinfoEntryDns );
//
// Process the old entry
//
if ( OldEntry != NULL ) {
//
// Remember to most recent TLN record from the old array.
//
if ( OldEntry->ForestTrustType == ForestTrustTopLevelName ) {
OldTlnPrefix = OldEntry;
//
// TLN exclusion records are taken from the old entries
//
} else if ( OldEntry->ForestTrustType == ForestTrustTopLevelNameEx ) {
if ( NetpAllocFtinfoEntry2( &FtinfoContext, OldEntry ) == NULL ) {
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
}
}
//
// Process the new entry
//
if ( NewEntry != NULL ) {
//
// Handle TLN entries
//
if ( NewEntry->ForestTrustType == ForestTrustTopLevelName ) {
BOOLEAN SetTlnNewFlag;
LSA_FOREST_TRUST_RECORD NewEntryCopy;
//
// Make a copy of the new entry.
//
// We modify the entry to get the time and flags right. We don't want
// to modify the callers buffer.
//
NewEntryCopy = *NewEntry;
//
// Ignore duplicate new entries
//
// If the name of this new entry is subordinate to the previous new entry,
// then this TLN can be quietly dropped.
//
// This is the case where the trusted domain sent us a TLN for both
// acme.com and a.acme.com. The second entry is a duplicate.
//
if ( PreviousNewEntry != NULL &&
PreviousNewEntry->ForestTrustType == ForestTrustTopLevelName ) {
if ( NetpIsSubordinate( &NewEntry->ForestTrustData.TopLevelName,
&PreviousNewEntry->ForestTrustData.TopLevelName,
TRUE ) ) {
continue;
}
}
//
// By default any TLN from the new list should be marked as new.
//
SetTlnNewFlag = TRUE;
//
// Set the flags and timestamp on the new entry.
//
// If we're processing an entry from both lists,
// grab the flags and timestamp from the old entry.
//
if ( OldEntry != NULL ) {
NewEntryCopy.Flags = OldFlags;
NewEntryCopy.Time = OldEntry->Time;
// This entry isn't 'new'.
SetTlnNewFlag = FALSE;
//
// Otherwise indicate that we have no information
//
} else {
NewEntryCopy.Flags = 0;
NewEntryCopy.Time.QuadPart = 0;
}
//
// If this new entry is subordinate to the most recent old TLN record,
// use the flag bits from that most recent old TLN record.
//
if ( OldTlnPrefix != NULL &&
NetpIsSubordinate( &NewEntryCopy.ForestTrustData.TopLevelName,
&OldTlnPrefix->ForestTrustData.TopLevelName,
FALSE ) ) {
//
// If the old TLN was disabled by the admin,
// so should the new entry.
//
if ( OldTlnPrefix->Flags & LSA_TLN_DISABLED_ADMIN ) {
NewEntryCopy.Flags |= LSA_TLN_DISABLED_ADMIN;
SetTlnNewFlag = FALSE;
//
// If the old TLN was enabled,
// so should the new entry.
//
} else if ( (OldTlnPrefix->Flags & LSA_FTRECORD_DISABLED_REASONS) == 0 ) {
SetTlnNewFlag = FALSE;
}
}
//
// If the name of the forest is subordinate of or equal to the TLN name,
// enable the entry.
//
if ( NetpIsSubordinate( TrustedDomainName,
&NewEntryCopy.ForestTrustData.TopLevelName,
TRUE )) {
SetTlnNewFlag = FALSE;
DomainTlnFound = TRUE;
}
//
// If this is a new TLN,
// mark it as such.
//
if ( SetTlnNewFlag ) {
NewEntryCopy.Flags |= LSA_TLN_DISABLED_NEW;
}
//
// Merge the new entry into the list
//
if ( NetpAllocFtinfoEntry2( &FtinfoContext, &NewEntryCopy ) == NULL ) {
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
//
// Remember this previous entry for the next iteration.
//
PreviousNewEntry = NewEntry;
}
}
}
//
// Loop through each list in SID canonical order processing the least entry.
//
// This loop handles DOMAIN entries only
//
// This is in a separate loop since we want to process domain entries in SID order
// to ensure the correct disabled bits are merged from the old list even though the
// DNS domain name changes.
//
NewIndex = 0;
OldIndex = 0;
PreviousNewEntry = NULL;
while ( NewIndex < NewForestTrustInfo.RecordCount ||
OldIndex < OldForestTrustInfo.RecordCount ) {
//
// Grab the next entry from each of the sorted arrays
//
NetpMergeFtinfoHelper( &NewForestTrustInfo,
&OldForestTrustInfo,
&NewIndex,
&OldIndex,
&NewEntry,
&OldEntry,
&OldFlags,
NetpCompareFtinfoEntrySid );
//
// Ignore the netbios bits for now (We'll get them on the next pass through the data.)
//
OldFlags &= ~(LSA_NB_DISABLED_ADMIN|LSA_NB_DISABLED_CONFLICT);
//
// Process the old entry
//
if ( OldEntry != NULL ) {
//
// Don't let the lack of a new entry allow an admin disabled entry to be deleted.
//
if ( OldEntry->ForestTrustType == ForestTrustDomainInfo &&
(OldFlags & LSA_SID_DISABLED_ADMIN) != 0 &&
NewEntry == NULL ) {
//
// Make a copy of the entry to ensure we don't modify the caller's buffer
//
LSA_FOREST_TRUST_RECORD OldEntryCopy;
OldEntryCopy = *OldEntry;
OldEntryCopy.Flags = OldFlags;
//
// Allocate entry.
//
// Remember the address of the entry for the netbios pass.
//
NetbiosForestTrustInfo.Entries[NetbiosForestTrustInfo.RecordCount] =
NetpAllocFtinfoEntry2( &FtinfoContext, &OldEntryCopy );
if ( NetbiosForestTrustInfo.Entries[NetbiosForestTrustInfo.RecordCount] == NULL ) {
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
NetbiosForestTrustInfo.RecordCount++;
}
}
//
// Process the new entry
//
if ( NewEntry != NULL ) {
//
// Handle domain entries
//
if ( NewEntry->ForestTrustType == ForestTrustDomainInfo ) {
LSA_FOREST_TRUST_RECORD NewEntryCopy;
//
// Make a copy of the new entry.
//
// We modify the entry to get the time and flags right. We don't want
// to modify the callers buffer.
//
NewEntryCopy = *NewEntry;
//
// Ignore duplicate new entries
//
// If the name of this new entry is subordinate to the previous new entry,
// then this entry can be quietly dropped.
//
// We arbitrarily drop the second entry even though the other fields of the
// triple might be different.
//
if ( PreviousNewEntry != NULL &&
PreviousNewEntry->ForestTrustType == ForestTrustDomainInfo ) {
if ( RtlEqualSid( NewEntryCopy.ForestTrustData.DomainInfo.Sid,
PreviousNewEntry->ForestTrustData.DomainInfo.Sid ) ) {
continue;
}
}
//
// Set the flags and timestamp on the new entry.
//
// If we're processing an entry from both lists,
// grab the flags and timestamp from the old entry.
//
if ( OldEntry != NULL ) {
NewEntryCopy.Flags = OldFlags;
NewEntryCopy.Time = OldEntry->Time;
//
// Otherwise indicate that we have no information
//
} else {
NewEntryCopy.Flags = 0;
NewEntryCopy.Time.QuadPart = 0;
}
//
// Merge the new entry into the list
//
// Remember the address of the entry for the netbios pass.
//
NetbiosForestTrustInfo.Entries[NetbiosForestTrustInfo.RecordCount] =
NetpAllocFtinfoEntry2( &FtinfoContext, &NewEntryCopy );
if ( NetbiosForestTrustInfo.Entries[NetbiosForestTrustInfo.RecordCount] == NULL ) {
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
NetbiosForestTrustInfo.RecordCount++;
//
// Ensure there is a TLN for this domain entry
//
if ( !NetpAddTlnFtinfoEntry ( &FtinfoContext,
&NewEntryCopy.ForestTrustData.DomainInfo.DnsName ) ) {
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
//
// Remember this previous entry for the next iteration.
//
PreviousNewEntry = NewEntry;
}
}
}
//
// Loop through each list in Netbios canonical order processing the least entry.
//
// This loop handle the Netbios name in the domain entries.
//
// This is in a separate loop since we want to process domain entries in Netbios order
// to ensure the correct disabled bits are merged from the old list even though the
// DNS domain name or domain sid changes.
//
// This iteration is fundamentally different than the previous two. This iteration
// uses NetbiosForestTrustInfo as the 'new' array. It is a psuedo ftinfo array that
// is built as the list of all the domain entries that have been copied into FtinfoContext.
// So, this iteration simply has to find that pre-existing entry and set the flags
// appropriately.
//
NewIndex = 0;
OldIndex = 0;
PreviousNewEntry = NULL;
while ( NewIndex < NetbiosForestTrustInfo.RecordCount ||
OldIndex < OldForestTrustInfo.RecordCount ) {
//
// Grab the next entry from each of the sorted arrays
//
NetpMergeFtinfoHelper( &NetbiosForestTrustInfo,
&OldForestTrustInfo,
&NewIndex,
&OldIndex,
&NewEntry,
&OldEntry,
&OldFlags,
NetpCompareFtinfoEntryNetbios );
//
// Ignore everything except the netbios bits.
//
// Everything else was processed on the previous iteration.
//
OldFlags &= (LSA_NB_DISABLED_ADMIN|LSA_NB_DISABLED_CONFLICT);
//
// This loop preserves the netbios disabled bits.
// If there is no old entry, there's nothing to preserve.
//
if ( OldEntry == NULL ) {
continue;
}
//
// If there is no new entry,
// ensure the *admin* disabled bit it preserved anyway.
//
if ( NewEntry == NULL ) {
//
// Don't let the lack of a new entry allow an admin disabled entry to be deleted.
//
// Note that the newly added entry might have a duplicate DNS name or SID.
//
if ( OldEntry->ForestTrustType == ForestTrustDomainInfo &&
(OldFlags & LSA_NB_DISABLED_ADMIN) != 0 ) {
//
// Make a copy of the entry to ensure we don't modify the caller's buffer
//
LSA_FOREST_TRUST_RECORD OldEntryCopy;
OldEntryCopy = *OldEntry;
OldEntryCopy.Flags = OldFlags;
if ( !NetpAllocFtinfoEntry2( &FtinfoContext, &OldEntryCopy ) ) {
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
}
//
// Copy any netbios disabled bits to the existing new entry.
//
} else {
//
// The NetbiosForestTrustInfo array only has domain entries.
// And the entries are equal so both must be domain entries.
//
NetpAssert( NewEntry->ForestTrustType == ForestTrustDomainInfo );
NetpAssert( OldEntry->ForestTrustType == ForestTrustDomainInfo );
NewEntry->Flags |= OldFlags;
}
}
//
// Ensure there is a TLN that DomainName is subordinate to
//
if ( !DomainTlnFound ) {
Status = STATUS_INVALID_PARAMETER;
goto Cleanup;
}
//
// Return the collected entries to the caller.
//
*MergedForestTrustInfo = NetpCopyFtinfoContext( &FtinfoContext );
if ( *MergedForestTrustInfo == NULL ) {
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
//
// Remove orphaned TLN exclusion records from the merged information
//
for ( Index = 0; Index < (*MergedForestTrustInfo)->RecordCount; Index++ ) {
PLSA_FOREST_TRUST_RECORD This = (*MergedForestTrustInfo)->Entries[Index];
UNICODE_STRING * ExclusionName;
ULONG Index2;
BOOL Subordinate = FALSE;
if ( This->ForestTrustType != ForestTrustTopLevelNameEx ) {
//
// Only interested in orphaned exclusions
//
continue;
}
ExclusionName = &This->ForestTrustData.TopLevelName;
for ( Index2 = 0; Index2 < (*MergedForestTrustInfo)->RecordCount; Index2++ ) {
PLSA_FOREST_TRUST_RECORD Other = (*MergedForestTrustInfo)->Entries[Index2];
UNICODE_STRING * TopLevelName;
if ( Other->ForestTrustType != ForestTrustTopLevelName ) {
//
// Only interested in top level names (exclusion must be subordinate to it)
//
continue;
}
TopLevelName = &Other->ForestTrustData.TopLevelName;
//
// First perform a subordinate check where equality is not enough
//
if ( NetpIsSubordinate( ExclusionName, TopLevelName, FALSE )) {
Subordinate = TRUE;
//
// Now check for equality
//
} else if ( RtlEqualUnicodeString( ExclusionName, TopLevelName, TRUE )) {
//
// A top level name is the same as an exclusion name.
// Throw away the exclusion record, but ensure that
// the top level name record is marked "disabled".
//
if (( Other->Flags & LSA_FTRECORD_DISABLED_REASONS ) == 0 ) {
Other->Flags |= LSA_TLN_DISABLED_NEW;
}
break;
}
if ( Subordinate ) {
break;
}
}
if ( !Subordinate ) {
//
// This is an orphaned exclusion record. Remove it.
//
(*MergedForestTrustInfo)->RecordCount -= 1;
(*MergedForestTrustInfo)->Entries[Index] = (*MergedForestTrustInfo)->Entries[(*MergedForestTrustInfo)->RecordCount];
Index -= 1;
}
}
Status = STATUS_SUCCESS;
Cleanup:
//
// Clean FtInfoContext
//
NetpCleanFtinfoContext( &FtinfoContext );
if ( OldForestTrustInfo.Entries != NULL ) {
RtlFreeHeap( RtlProcessHeap(), 0, OldForestTrustInfo.Entries );
}
if ( NewForestTrustInfo.Entries != NULL ) {
RtlFreeHeap( RtlProcessHeap(), 0, NewForestTrustInfo.Entries );
}
if ( NetbiosForestTrustInfo.Entries != NULL ) { RtlFreeHeap( RtlProcessHeap(), 0, NetbiosForestTrustInfo.Entries ); } return Status; }