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.
 
 
 
 
 
 

11396 lines
318 KiB

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
dbftrust.cxx
Abstract:
LSA Forest Trust Manager - Routines for managing forest trust information
These routines manipulate information associated with the forest trust
attribute of trusted domain objects
--*/
extern "C" {
#include <lsapch2.h>
#include "dbp.h"
#include <ntdsapip.h>
}
#include <smbgtpt.h>
#include <wmistr.h>
#include <evntrace.h>
#include <lsawmi.h>
#include "dbftrust.h"
#include <dnsapi.h>
#include <ftnfoctx.h>
#include <malloc.h>
#include <alloca.h>
//
// Blob version # (in case we want to overhaul the format entirely at a later time
//
#define LSAP_FOREST_TRUST_BLOB_VERSION_1 (( ULONG ) 1)
#define LSAP_FOREST_TRUST_BLOB_VERSION LSAP_FOREST_TRUST_BLOB_VERSION_1
//
// To prevent forest trust blobs of large size, number of records must be
// smaller than MAX_RECORDS_IN_FOREST_TRUST_INFO
//
#define MAX_RECORDS_IN_FOREST_TRUST_INFO 4000
//
// Forest trust cache debug-only statistics
//
#if DBG
DWORD FTCache::sm_TdoEntries = 0;
DWORD FTCache::sm_TlnEntries = 0;
DWORD FTCache::sm_DomainInfoEntries = 0;
DWORD FTCache::sm_BinaryEntries = 0;
DWORD FTCache::sm_TlnKeys = 0;
DWORD FTCache::sm_SidKeys = 0;
DWORD FTCache::sm_DnsNameKeys = 0;
DWORD FTCache::sm_NetbiosNameKeys = 0;
#endif
//
// DS notification handles
//
DWORD XrefNotificationHandle = NULL;
DWORD UpnSuffixNotificationHandle = NULL;
///////////////////////////////////////////////////////////////////////////////
//
// Helper routines
//
///////////////////////////////////////////////////////////////////////////////
template<class T>
BOOLEAN
XOR( const T& t1, const T& t2 )
{
return ( !t1 != !t2 );
}
USHORT
DnsNameComponents(
IN const UNICODE_STRING * const Name
)
/*++
Routine Description:
Counts the number of components of a DNS name
Arguments:
Name something of the form "NY.acme.com"
Returns:
Number of components (at least 1)
--*/
{
USHORT Result = 1;
USHORT Index;
ASSERT( Name );
ASSERT( Name->Length > 0 );
ASSERT( Name->Buffer != NULL );
for ( Index = 0 ; Index <= Name->Length ; Index += sizeof( WCHAR )) {
if ( Name->Buffer[Index / sizeof( WCHAR )] == L'.' ) {
Result += 1;
}
}
return Result;
}
void
NextDnsComponent(
IN UNICODE_STRING * Name
)
/*++
Routine Description:
Changes the given name to point to the next component of a DNS name.
The Buffer component of the name must NOT be dynamically allocated,
since this code will change the Buffer pointer.
The name must have at least two components, else an assert will fire.
Arguments:
Name name to modify
Returns:
Modifies the Name parameter passed in; asserts if input invalid
--*/
{
USHORT Index;
BOOLEAN Found = FALSE;
ASSERT( Name );
ASSERT( Name->Length > 0 );
ASSERT( Name->Buffer != NULL );
for ( Index = 0 ; Index < Name->Length ; Index += sizeof( WCHAR )) {
if ( Name->Buffer[Index / sizeof( WCHAR )] == L'.' ) {
Found = TRUE;
Index += sizeof( WCHAR );
break;
}
}
//
// Somebody else should take care of passing valid data to this routine
//
ASSERT( !Found || Index < Name->Length );
Name->Buffer += Index / sizeof( WCHAR );
Name->Length -= Index;
Name->MaximumLength -= Index;
ASSERT( Found || Name->Length == 0 );
return;
}
BOOLEAN
IsSubordinate(
IN const UNICODE_STRING * Subordinate,
IN const UNICODE_STRING * Superior
)
/*++
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
Returns:
TRUE is Subordinate is subordinate to Superior
FALSE otherwise
--*/
{
USHORT SubIndex, SupIndex;
UNICODE_STRING Temp;
ASSERT( Subordinate && Subordinate->Buffer );
ASSERT( Superior && Superior->Buffer );
ASSERT( LsapValidateLsaUnicodeString( Subordinate ));
ASSERT( LsapValidateLsaUnicodeString( Superior ));
//
// 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;
}
//
// The last part of two names must match exactly (but not case sensitively)
//
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;
}
///////////////////////////////////////////////////////////////////////////////
//
// Memory management code
//
///////////////////////////////////////////////////////////////////////////////
#define FtcAllocate( size ) MIDL_user_allocate( size )
#define FtcFree( buffer ) MIDL_user_free( buffer )
PVOID
FtcReallocate(
IN OPTIONAL void * OldPointer,
IN size_t OldByteCount,
IN size_t NewByteCount
)
{
void * Result = MIDL_user_allocate( NewByteCount );
if ( Result == NULL ) {
return NULL;
}
if ( OldPointer != NULL ) {
RtlCopyMemory( Result, OldPointer, OldByteCount );
MIDL_user_free( OldPointer );
}
return Result;
}
BOOLEAN
FtcCopySid(
IN PSID * Destination,
IN PSID Source
)
/*++
Routine Description:
Allocates and fills in a copy of a given SID
Arguments:
Destination address of destination SID
Source source SID
Returns:
FALSE if operation failed (out of memory)
TRUE otherwise
--*/
{
ULONG SidLength;
ASSERT( Destination );
if ( Source == NULL ) {
*Destination = NULL;
return TRUE;
}
SidLength = RtlLengthSid( Source );
*Destination = FtcAllocate( SidLength );
if ( *Destination == NULL ) {
return FALSE;
}
RtlCopySid(
SidLength,
*Destination,
Source
);
return TRUE;
}
BOOLEAN
FtcCopyUnicodeString(
IN UNICODE_STRING * Destination,
IN UNICODE_STRING * Source,
IN PWSTR Buffer = NULL
)
/*++
Routine Description:
Copies the contents of one Unicode string to another
Arguments:
Destination destination string
Source source string
Buffer (optional) address of the buffer for destination data
Returns:
FALSE if operation failed (out of memory)
TRUE otherwise
--*/
{
ASSERT( Destination );
ASSERT( Source && LsapValidateLsaUnicodeString( Source ));
if ( Buffer != NULL && Source->Length > 0 ) {
Destination->Buffer = Buffer;
} else if ( Source->Length > 0 ) {
Destination->Buffer = ( PWSTR )FtcAllocate( Source->Length + sizeof( WCHAR ));
if ( Destination->Buffer == NULL ) {
return FALSE;
}
} else {
Destination->Buffer = NULL;
}
Destination->MaximumLength = Source->Length + sizeof( WCHAR );
RtlCopyUnicodeString(
Destination,
Source
);
return TRUE;
}
void
LsapFreeForestTrustInfo(
IN LSA_FOREST_TRUST_INFORMATION * ForestTrustInfo
)
/*++
Routine Description:
Deallocates memory taken up by the ForestTrustInfo structure
Arguments:
ForestTrustInfo structure to free
Returns:
Nothing
--*/
{
ULONG i;
if ( ForestTrustInfo == NULL ) {
return;
}
i = ForestTrustInfo->RecordCount;
while ( i > 0 ) {
LSA_FOREST_TRUST_RECORD * Record = ForestTrustInfo->Entries[--i];
if ( Record == NULL ) {
continue;
}
switch ( Record->ForestTrustType ) {
case ForestTrustTopLevelName:
case ForestTrustTopLevelNameEx:
FtcFree( Record->ForestTrustData.TopLevelName.Buffer );
break;
case ForestTrustDomainInfo:
FtcFree( Record->ForestTrustData.DomainInfo.Sid );
FtcFree( Record->ForestTrustData.DomainInfo.DnsName.Buffer );
FtcFree( Record->ForestTrustData.DomainInfo.NetbiosName.Buffer );
break;
default:
FtcFree( Record->ForestTrustData.Data.Buffer );
break;
}
FtcFree( Record );
}
FtcFree( ForestTrustInfo->Entries );
ForestTrustInfo->RecordCount = 0;
ForestTrustInfo->Entries = NULL;
return;
}
void
LsapFreeCollisionInfo(
IN OUT PLSA_FOREST_TRUST_COLLISION_INFORMATION * CollisionInfo
)
/*++
Routine Description:
Deallocates memory taken up by the CollisionInfo structure
Arguments:
CollisionInfo structure to free
Returns:
Nothing
--*/
{
ASSERT( CollisionInfo );
if ( *CollisionInfo == NULL ) {
return;
}
ULONG i = (*CollisionInfo)->RecordCount;
while ( i > 0 ) {
LSA_FOREST_TRUST_COLLISION_RECORD * Record = (*CollisionInfo)->Entries[--i];
if ( Record == NULL ) {
continue;
}
switch ( Record->Type ) {
case CollisionTdo:
FtcFree( Record->Name.Buffer );
break;
default:
ASSERT( FALSE ); // NYI
}
FtcFree( Record );
}
FtcFree( (*CollisionInfo)->Entries );
FtcFree( *CollisionInfo );
*CollisionInfo = NULL;
return;
}
PVOID
NTAPI
FtcAllocateRoutine(
struct _RTL_AVL_TABLE * Table,
CLONG ByteSize
)
{
UNREFERENCED_PARAMETER( Table );
return RtlAllocateHeap( RtlProcessHeap(), 0, ByteSize );
}
void
NTAPI
FtcFreeRoutine(
struct _RTL_AVL_TABLE * Table,
PVOID Buffer
)
{
UNREFERENCED_PARAMETER( Table );
RtlFreeHeap( RtlProcessHeap(), 0, Buffer );
}
///////////////////////////////////////////////////////////////////////////////
//
// Comparison and matching routines
//
///////////////////////////////////////////////////////////////////////////////
template<class T>
inline
RTL_GENERIC_COMPARE_RESULTS
FtcGenericCompare(
IN const T& a,
IN const T& b
)
/*++
Routine Description:
Compares two values and returns one of
GenericEqual
GenericGreaterThan
GenericLessThan
Arguments:
a, b numbers to compare
--*/
{
if ( a < b ) {
return GenericLessThan;
} else if ( a > b ) {
return GenericGreaterThan;
} else {
return GenericEqual;
}
}
inline
RTL_GENERIC_COMPARE_RESULTS
FtcGenericCompareMemory(
IN BYTE * Buffer1,
IN BYTE * Buffer2,
IN ULONG Length
)
/*++
Routine Description:
Compares two buffers and returns one of
GenericEqual
GenericGreaterThan
GenericLessThan
Arguments:
Buffer1, Buffer2 buffers to compare
Length length of buffers
--*/
{
RTL_GENERIC_COMPARE_RESULTS Result = GenericEqual;
for ( ULONG i = 0 ; i < Length ; i++ ) {
const BYTE& b1 = Buffer1[i];
const BYTE& b2 = Buffer2[i];
if ( b1 == b2 ) {
continue;
} else if ( b1 < b2 ) {
Result = GenericLessThan;
} else {
Result = GenericGreaterThan;
}
break;
}
return Result;
}
RTL_GENERIC_COMPARE_RESULTS
NTAPI
SidCompareRoutine(
struct _RTL_AVL_TABLE * Table,
PVOID FirstStruct,
PVOID SecondStruct
)
/*++
Routine description:
SID comparison callback for AVL tree management
Arguments:
Table table for which this comparison takes place (ignored)
FirstStruct, SecondStruct points to SID*'s to compare
Returns:
equal/greater-than/less-than
NOTE:
1. the code relies on the fact that SID* is the first entry inside the
SID_KEY structure. this way, a pointer to a SID* can be passed
to AVL management routines (Lookup/Insert/Delete)
2. this is the same code as RtlEqualSid, except it returns
RTL_GENERIC_COMPARE_RESULTS
--*/
{
SID *Sid1, *Sid2;
RTL_GENERIC_COMPARE_RESULTS Result;
PSID_IDENTIFIER_AUTHORITY Authority1, Authority2;
DWORD DomainSubAuthCount1, DomainSubAuthCount2;
ULONG i;
UNREFERENCED_PARAMETER( Table );
ASSERT( FirstStruct );
ASSERT( SecondStruct );
Sid1 = *( PISID * )FirstStruct;
Sid2 = *( PISID * )SecondStruct;
ASSERT( Sid1 && RtlValidSid( Sid1 ));
ASSERT( Sid2 && RtlValidSid( Sid2 ));
if ( GenericEqual != ( Result = FtcGenericCompare<UCHAR>(
Sid1->Revision,
Sid2->Revision ))) {
return Result;
} else if ( GenericEqual != ( Result = FtcGenericCompare<UCHAR>(
*RtlSubAuthorityCountSid( Sid1 ),
*RtlSubAuthorityCountSid( Sid2 )))) {
return Result;
} else {
return FtcGenericCompareMemory(
( BYTE * )Sid1,
( BYTE * )Sid2,
RtlLengthSid( Sid1 )
);
}
}
RTL_GENERIC_COMPARE_RESULTS
NTAPI
UnicodeStringCompareRoutine(
struct _RTL_AVL_TABLE * Table,
PVOID FirstStruct,
PVOID SecondStruct
)
/*++
Routine Description:
Unicode string comparison routine for AVL tables
Arguments:
Table Table used in comparison (ignored)
FirstStruct Pointer to first unicode string
SecondStruct Pointer to second unicode string
Returns:
equal/greater-than/less-than
--*/
{
INT Result;
UNICODE_STRING *String1, *String2;
UNREFERENCED_PARAMETER( Table );
ASSERT( FirstStruct );
ASSERT( SecondStruct );
String1 = ( UNICODE_STRING * )FirstStruct;
String2 = ( UNICODE_STRING * )SecondStruct;
Result = RtlCompareUnicodeString(
String1,
String2,
TRUE
);
if ( Result < 0 ) {
return GenericLessThan;
} else if ( Result > 0 ) {
return GenericGreaterThan;
} else {
return GenericEqual;
}
}
///////////////////////////////////////////////////////////////////////////////
//
// Global forest trust cache object
//
///////////////////////////////////////////////////////////////////////////////
FTCache * g_FTCache = NULL;
///////////////////////////////////////////////////////////////////////////////
//
// Validation code
//
///////////////////////////////////////////////////////////////////////////////
BOOLEAN
LsapValidateForestTrustInfo(
IN LSA_FOREST_TRUST_INFORMATION * ForestTrustInfo
)
/*++
Routine Description:
Runs a validation algorithm on the ForestTrustInfo structure passed in.
Strips trailing periods from names where appropriate.
Arguments:
ForestTrustInfo data to validate
Returns:
TRUE if ForestTrustInfo checks out, FALSE otherwise
--*/
{
ULONG i;
if ( ForestTrustInfo == NULL ) {
return TRUE;
}
if ( ForestTrustInfo->RecordCount > 0 &&
ForestTrustInfo->Entries == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "LsapValidateForestTrustInfo: ForestTrustInfo->Entries is NULL (%s:%d)\n", __FILE__, __LINE__ ));
return FALSE;
}
for ( i = 0 ; i < ForestTrustInfo->RecordCount ; i++ ) {
LSA_FOREST_TRUST_RECORD * Record;
Record = ForestTrustInfo->Entries[i];
if ( Record == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "LsapValidateForestTrustInfo: Record %d is invalid (%s:%d)\n", i, __FILE__, __LINE__ ));
return FALSE;
}
switch ( Record->ForestTrustType ) {
case ForestTrustTopLevelName:
case ForestTrustTopLevelNameEx: {
BOOLEAN Valid;
if ( !LsapValidateLsaUnicodeString( &Record->ForestTrustData.TopLevelName )) {
LsapDsDebugOut(( DEB_FTINFO, "LsapValidateForestTrustInfo: Record %d is invalid (%s:%d)\n", i, __FILE__, __LINE__ ));
return FALSE;
}
//
// DNS names have additional tests applied to them
//
LsapValidateDnsName(( UNICODE_STRING * )&Record->ForestTrustData.TopLevelName, &Valid );
if ( !Valid ) {
LsapDsDebugOut(( DEB_FTINFO, "LsapValidateForestTrustInfo: Record %d is invalid (%s:%d)\n", i, __FILE__, __LINE__ ));
return FALSE;
}
//
// DNS names must have their trailing period stripped. Now is a good time
//
LsapRemoveTrailingDot(( UNICODE_STRING * )&Record->ForestTrustData.TopLevelName, FALSE );
//
// Exclusion entries must have more than one DNS name component
// (e.g. "acme.com" is valid but "acme" is not)
//
if ( Record->ForestTrustType == ForestTrustTopLevelNameEx &&
DnsNameComponents( &Record->ForestTrustData.TopLevelName ) < 2 ) {
LsapDsDebugOut(( DEB_FTINFO, "LsapValidateForestTrustInfo: Record %d is invalid (%s:%d)\n", i, __FILE__, __LINE__ ));
return FALSE;
}
break;
}
case ForestTrustDomainInfo: {
//
// ISSUE-2000/07/28-markpu
// are NULL SIDs allowed?
//
BYTE DomainSid[SECURITY_MAX_SID_SIZE];
DWORD cbSid = sizeof( DomainSid );
BOOLEAN Valid;
if ( !RtlValidSid(( SID * )Record->ForestTrustData.DomainInfo.Sid ) ||
!LsapValidateLsaUnicodeString( &Record->ForestTrustData.DomainInfo.DnsName ) ||
!LsapValidateLsaUnicodeString( &Record->ForestTrustData.DomainInfo.NetbiosName )) {
LsapDsDebugOut(( DEB_FTINFO, "LsapValidateForestTrustInfo: Record %d is invalid (%s:%d)\n", i, __FILE__, __LINE__ ));
return FALSE;
}
//
// SIDs passed in should be valid domain SIDs
//
if ( FALSE == GetWindowsAccountDomainSid(
( SID * )Record->ForestTrustData.DomainInfo.Sid,
( PSID )DomainSid,
&cbSid )) {
ASSERT( GetLastError() != ERROR_INVALID_SID ); // RtlValidSid check already performed
LsapDsDebugOut(( DEB_FTINFO, "LsapValidateForestTrustInfo: Record %d is invalid, Error 0x%lx (%s:%d)\n", i, GetLastError(), __FILE__, __LINE__ ));
return FALSE;
} else {
//
// For domain SIDs, GetWindowsAccountDomainSid returns a SID equal to the one passed in.
// If the two SIDs are not equal, the SID passed in is not a true domain SID
//
if ( !RtlEqualSid(
( PSID )DomainSid,
( PSID )Record->ForestTrustData.DomainInfo.Sid )) {
LsapDsDebugOut(( DEB_FTINFO, "LsapValidateForestTrustInfo: Record %d is invalid (%s:%d)\n", i, __FILE__, __LINE__ ));
return FALSE;
}
}
//
// DNS and NetBIOS names have additional tests applied to them
//
if ( Record->ForestTrustData.DomainInfo.NetbiosName.Length > 0 ) {
LsapValidateNetbiosName(( UNICODE_STRING * )&Record->ForestTrustData.DomainInfo.NetbiosName, &Valid );
if ( !Valid ) {
LsapDsDebugOut(( DEB_FTINFO, "LsapValidateForestTrustInfo: Record %d is invalid (%s:%d)\n", i, __FILE__, __LINE__ ));
return FALSE;
}
}
LsapValidateDnsName(( UNICODE_STRING * )&Record->ForestTrustData.DomainInfo.DnsName, &Valid );
if ( !Valid ) {
LsapDsDebugOut(( DEB_FTINFO, "LsapValidateForestTrustInfo: Record %d is invalid (%s:%d)\n", i, __FILE__, __LINE__ ));
return FALSE;
}
//
// DNS names must have their trailing period stripped. Now is a good time
//
LsapRemoveTrailingDot(( UNICODE_STRING * )&Record->ForestTrustData.DomainInfo.DnsName, FALSE );
break;
}
default:
//
// ISSUE-2000/07/21-markpu
// add some binary blob checking
//
break;
}
}
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
//
// C Wrappers around the FTCache class
//
///////////////////////////////////////////////////////////////////////////////
NTSTATUS
LsapForestTrustCacheInitialize()
{
NTSTATUS Status;
LsapDsDebugOut(( DEB_FTINFO, "Forest trust cache being initialized\n" ));
ASSERT( g_FTCache == NULL );
g_FTCache = new FTCache;
if ( g_FTCache == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in LsapForestTrustCacheInitialize (%s:%d)\n", __FILE__, __LINE__ ));
return STATUS_INSUFFICIENT_RESOURCES;
}
Status = g_FTCache->Initialize();
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsapForestTrustCacheInitialize: FTCache::Initialize returned 0x%x\n", Status ));
delete g_FTCache;
g_FTCache = NULL;
}
return Status;
}
inline void
LsapForestTrustCacheSetLocalValid()
{
LsapDsDebugOut(( DEB_FTINFO, "Forest trust cache set \"local valid\"\n" ));
ASSERT( LsapDbIsLockedTrustedDomainList());
g_FTCache->SetLocalValid();
}
inline void
LsapForestTrustCacheSetExternalValid()
{
LsapDsDebugOut(( DEB_FTINFO, "Forest trust cache set \"external valid\"\n" ));
ASSERT( LsapDbIsLockedTrustedDomainList());
g_FTCache->SetExternalValid();
}
inline void
LsapForestTrustCacheSetInvalid()
{
LsapDsDebugOut(( DEB_FTINFO, "Forest trust cache set \"invalid\"\n" ));
ASSERT( LsapDbIsLockedTrustedDomainList());
g_FTCache->SetInvalid();
}
inline BOOLEAN
LsapForestTrustCacheIsLocalValid()
{
ASSERT( LsapDbIsLockedTrustedDomainList());
return g_FTCache->IsLocalValid();
}
inline BOOLEAN
LsapForestTrustCacheIsExternalValid()
{
ASSERT( LsapDbIsLockedTrustedDomainList());
return g_FTCache->IsExternalValid();
}
NTSTATUS
LsapForestTrustCacheInsert(
IN UNICODE_STRING * TrustedDomainName,
IN OPTIONAL PSID TrustedDomainSid,
IN LSA_FOREST_TRUST_INFORMATION * ForestTrustInfo,
IN BOOLEAN LocalForestEntry
)
/*++
Routine Description:
Inserts forest trust information for particular trusted domain
name into the cache.
This routine gets called as a result of
* the system preloading the cache or
* replication traffic
As a result, the data coming in can not be assumed to be collision-free.
Collisions are resolved, and on the root domain PDC, the resulting forest
trust data is written back out to the DS.
Arguments:
TrustedDomainName name of the TDO
TrustedDomainSid SID of the TDO (can be NULL)
ForestTrustInfo forest trust information
LocalForestEntry does this data correspond to the local forest?
Returns:
STATUS_SUCCESS success
STATUS_INTERNAL_ERROR
STATUS_INSUFFICIENT_RESOURCES out of memory
STATUS_INVALID_PARAMETER data passed in is inconsistent
--*/
{
NTSTATUS Status;
FTCache::TDO_ENTRY TdoEntryOld = {0};
FTCache::TDO_ENTRY * TdoEntryNew = NULL;
FTCache::CONFLICT_PAIR * ConflictPairs = NULL;
ULONG ConflictPairsTotal = 0;
BOOLEAN ObjectReferenced = FALSE;
DOMAIN_SERVER_ROLE ServerRole = DomainServerRoleBackup;
ASSERT( LsapDbIsLockedTrustedDomainList());
//
// Forest trust is not enabled until .NET forest mode is entered
//
// Local forest entries are allowed in always (the cache always needs to
// contain information about the local forest)
//
if ( !LsapDbNoMoreWin2KForest() && !LocalForestEntry ) {
return STATUS_SUCCESS;
}
LsarpReturnCheckSetup();
LsapEnterFunc( "LsapForestTrustCacheInsert" );
LsapDsDebugOut(( DEB_FTINFO, "LsapForestTrustCacheInsert called\n" ));
if ( !LsapValidateForestTrustInfo( ForestTrustInfo )) {
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in LsapForestTrustCacheInsert (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
goto Error;
}
Status = g_FTCache->Insert(
TrustedDomainName,
TrustedDomainSid,
ForestTrustInfo,
LocalForestEntry,
&TdoEntryOld,
&TdoEntryNew,
&ConflictPairs,
&ConflictPairsTotal
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsapForestTrustCacheInsert: FTCache::Insert returned 0x%x\n", Status ));
goto Error;
}
if ( ConflictPairs ) {
g_FTCache->ReconcileConflictPairs(
NULL, // let the winner be determined algorithmically
ConflictPairs,
ConflictPairsTotal
);
}
//
// If we are in the root domain, there is a chance
// that this DC is a PDC and thus reconciled changes
// need to be written out
//
if ( ConflictPairs && // conflicts were detected
LsapDbDcInRootDomain() && // this is a root domain DC
LsapSamOpened ) { // SAM is started (a must to call SamI APIs)
//
// Query the server role, PDC/BDC
//
ASSERT( LsapAccountDomainHandle );
Status = SamIQueryServerRole(
LsapAccountDomainHandle,
&ServerRole
);
if ( !NT_SUCCESS(Status)) {
LsapDsDebugOut(( DEB_FTINFO, "LsapForestTrustCacheInsert: SamIQueryServerRole returned 0x%x\n", Status ));
goto Error;
}
if ( ServerRole == DomainServerRolePrimary ) {
ULONG TdoNamesCount = 0;
PUNICODE_STRING * TdoNames = NULL;
ULONG i, j;
for ( i = 0 ; i < ConflictPairsTotal ; i++ ) {
PUNICODE_STRING String1 = NULL;
PUNICODE_STRING String2 = NULL;
//
// Get trusted domain names corresponding to conflicting entries
// Local forest entries are excepted -- they do not correspond
// to regular TDOs, always win in conflict reconciliation
// and do not get written out to disk during such reconciliation
//
switch ( ConflictPairs[i].EntryType1 ) {
case ForestTrustTopLevelName:
String1 = ConflictPairs[i].TlnEntry1->TdoEntry->LocalForestEntry ?
NULL :
&ConflictPairs[i].TlnEntry1->TdoEntry->TrustedDomainName;
break;
case ForestTrustDomainInfo:
String1 = ConflictPairs[i].DomainInfoEntry1->TdoEntry->LocalForestEntry ?
NULL :
&ConflictPairs[i].DomainInfoEntry1->TdoEntry->TrustedDomainName;
break;
}
switch ( ConflictPairs[i].EntryType2 ) {
case ForestTrustTopLevelName:
String2 = ConflictPairs[i].TlnEntry2->TdoEntry->LocalForestEntry ?
NULL :
&ConflictPairs[i].TlnEntry2->TdoEntry->TrustedDomainName;
break;
case ForestTrustDomainInfo:
String2 = ConflictPairs[i].DomainInfoEntry2->TdoEntry->LocalForestEntry ?
NULL :
&ConflictPairs[i].DomainInfoEntry2->TdoEntry->TrustedDomainName;
break;
}
//
// No point continuing if neither name can be found
//
if ( !String1 && !String2 ) {
continue;
}
//
// Append the names to the array, uniquely
//
for ( j = 0 ; String1 && j < TdoNamesCount ; j++ ) {
if ( RtlEqualUnicodeString(
String1,
TdoNames[j],
TRUE )) {
break;
}
}
if ( String1 && j == TdoNamesCount ) {
PUNICODE_STRING * TdoNamesT;
TdoNamesT = ( TdoNames ?
( PUNICODE_STRING * )LocalReAlloc(
TdoNames,
( TdoNamesCount + 1 ) * sizeof( PUNICODE_STRING ),
0 ) :
( PUNICODE_STRING * )LocalAlloc(
0,
sizeof( PUNICODE_STRING )));
if ( TdoNamesT == NULL ) {
if ( TdoNames ) {
LocalFree( TdoNames );
}
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in LsapForestTrustCacheInsert (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
TdoNames = TdoNamesT;
TdoNames[TdoNamesCount] = String1;
TdoNamesCount += 1;
}
for ( j = 0 ; String2 && j < TdoNamesCount ; j++ ) {
if ( RtlEqualUnicodeString(
String2,
TdoNames[j],
TRUE )) {
break;
}
}
if ( String2 && j == TdoNamesCount ) {
PUNICODE_STRING * TdoNamesT;
TdoNamesT = ( TdoNames ?
( PUNICODE_STRING * )LocalReAlloc(
TdoNames,
( TdoNamesCount + 1 ) * sizeof( PUNICODE_STRING ),
0 ) :
( PUNICODE_STRING * )LocalAlloc(
0,
sizeof( PUNICODE_STRING )));
if ( TdoNamesT == NULL ) {
if ( TdoNames ) {
LocalFree( TdoNames );
}
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in LsapForestTrustCacheInsert (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
TdoNames = TdoNamesT;
TdoNames[TdoNamesCount] = String2;
TdoNamesCount += 1;
}
}
ASSERT( TdoNamesCount > 0 );
ASSERT( TdoNames != NULL );
Status = LsapDbReferenceObject(
LsapPolicyHandle,
0,
PolicyObject,
TrustedDomainObject,
LSAP_DB_DS_OP_TRANSACTION // no lock acquired (must be done outside the routine)
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsapForestTrustCacheInsert: LsapDbReferenceObject returned 0x%x\n", Status ));
LocalFree( TdoNames );
goto Error;
}
ObjectReferenced = TRUE;
//
// Write every affected TDO out to disk
//
for ( i = 0 ; i < TdoNamesCount ; i++ ) {
LSAP_DB_OBJECT_INFORMATION ObjInfo;
LSAP_DB_ATTRIBUTE Attributes[LSAP_DB_ATTRS_INFO_CLASS_DOMAIN];
LSAP_DB_ATTRIBUTE * NextAttribute;
ULONG AttributeCount = 0;
LSAPR_HANDLE TrustedDomainHandle = NULL;
ULONG BlobLength = 0;
BYTE * BlobData = NULL;
FTCache::TDO_ENTRY * TdoEntry;
LSAP_DB_TRUSTED_DOMAIN_LIST_ENTRY * TrustInfoForName;
//
// Get the right name
//
Status = LsapDbLookupNameTrustedDomainListEx(
( LSAPR_UNICODE_STRING * )TdoNames[i],
&TrustInfoForName
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsapForestTrustCacheInsert: LsapDbLookupNameTrustedDomainListEx returned 0x%lx for %wZ (%s:%d)\n", Status, ( UNICODE_STRING * )TrustedDomainName, __FILE__, __LINE__ ));
LocalFree( TdoNames );
goto Error;
}
//
// Build a temporary handle
//
RtlZeroMemory( &ObjInfo, sizeof( ObjInfo ));
ObjInfo.ObjectTypeId = TrustedDomainObject;
ObjInfo.ContainerTypeId = NullObject;
ObjInfo.Sid = NULL;
ObjInfo.DesiredObjectAccess = TRUSTED_SET_AUTH;
InitializeObjectAttributes(
&ObjInfo.ObjectAttributes,
( UNICODE_STRING * )&TrustInfoForName->TrustInfoEx.Name,
0L,
LsapPolicyHandle,
NULL
);
//
// Get a handle to the TDO
//
Status = LsapDbOpenObject(
&ObjInfo,
TRUSTED_SET_AUTH,
0,
&TrustedDomainHandle
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsapForestTrustCacheInsert: LsapDbOpenObject returned 0x%x\n", Status ));
LocalFree( TdoNames );
goto Error;
}
//
// Locate the cache entry corresponding to this TDO
//
TdoEntry = ( FTCache::TDO_ENTRY * )RtlLookupElementGenericTableAvl(
&g_FTCache->m_TdoTable,
TdoNames[i]
);
if ( TdoEntry == NULL ) {
//
// What do you mean "not there"???
//
Status = STATUS_NOT_FOUND;
LsapDsDebugOut(( DEB_FTINFO, "LsapForestTrustCacheInsert: RtlLookupElementGenericTableAvl returned 0x%x\n", Status ));
ASSERT( FALSE );
LocalFree( TdoNames );
LsapDbCloseHandle( TrustedDomainHandle );
goto Error;
}
Status = g_FTCache->MarshalBlob(
TdoEntry,
&BlobLength,
&BlobData
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsapForestTrustCacheInsert: FTCache::MarshalBlob returned 0x%x\n", Status ));
LocalFree( TdoNames );
LsapDbCloseHandle( TrustedDomainHandle );
goto Error;
}
NextAttribute = Attributes;
LsapDbInitializeAttributeDs(
NextAttribute,
TrDmForT,
BlobData,
BlobLength,
FALSE
);
AttributeCount++;
ASSERT( AttributeCount <= LSAP_DB_ATTRS_INFO_CLASS_DOMAIN );
//
// Write the attributes to the DS.
//
Status = LsapDbWriteAttributesObject(
TrustedDomainHandle,
Attributes,
AttributeCount
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsapForestTrustCacheInsert: LsapDbWriteAttributesObject returned 0x%x\n", Status ));
LocalFree( TdoNames );
LsapDbCloseHandle( TrustedDomainHandle );
FtcFree( BlobData );
goto Error;
}
LsapDbCloseHandle( TrustedDomainHandle );
FtcFree( BlobData );
}
LocalFree( TdoNames );
}
}
Cleanup:
if ( ObjectReferenced ) {
Status = LsapDbDereferenceObject(
&LsapPolicyHandle,
PolicyObject,
TrustedDomainObject,
LSAP_DB_DS_OP_TRANSACTION |
LSAP_DB_OMIT_REPLICATOR_NOTIFICATION,
(SECURITY_DB_DELTA_TYPE) 0,
Status
);
}
//
// If operation completed successfully on a PDC,
// audit conflict resolution information here
//
if ( NT_SUCCESS( Status ) &&
ConflictPairs &&
ServerRole == DomainServerRolePrimary ) {
g_FTCache->AuditCollisions(
ConflictPairs,
ConflictPairsTotal
);
}
if ( TdoEntryOld.RecordCount > 0 ) {
g_FTCache->PurgeTdoEntry( &TdoEntryOld );
}
FtcFree( ConflictPairs );
LsapDsDebugOut(( DEB_FTINFO, "LsapForestTrustCacheInsert returning, Status: 0x%x\n", Status ));
LsapExitFunc( "LsapForestTrustCacheInsert", Status );
LsarpReturnPrologue();
return Status;
Error:
ASSERT( !NT_SUCCESS( Status ));
//
// Rollback the changes made to the cache
//
if ( TdoEntryNew ) {
g_FTCache->RollbackChanges( TdoEntryNew, &TdoEntryOld );
TdoEntryNew = NULL;
}
goto Cleanup;
}
inline NTSTATUS
LsapForestTrustCacheRemove(
IN UNICODE_STRING * TrustedDomainName
)
{
ASSERT( LsapDbIsLockedTrustedDomainList());
return g_FTCache->Remove( TrustedDomainName );
}
inline NTSTATUS
LsapForestTrustCacheRetrieve(
IN UNICODE_STRING * TrustedDomainName,
OUT LSA_FOREST_TRUST_INFORMATION * * ForestTrustInfo
)
{
ASSERT( LsapDbIsLockedTrustedDomainList());
return g_FTCache->Retrieve(
TrustedDomainName,
ForestTrustInfo
);
}
NTSTATUS
LsapAddToCollisionInfo(
IN OUT PLSA_FOREST_TRUST_COLLISION_INFORMATION * CollisionInfo,
IN ULONG Index,
IN LSA_FOREST_TRUST_COLLISION_RECORD_TYPE Type,
IN ULONG Flags,
IN OPTIONAL UNICODE_STRING * Name
)
/*++
Routine Description:
Apends the given index and trusted domain name to the
provided collision information structure.
Arguments:
CollisionInfo structure to append to
Index new entry's index
Type type of collision
Flags new flags set as result of collision
Name name (only for certain types of collisions)
Returns:
STATUS_SUCCESS operation completed successfully
this routine takes ownership of ForestTrustRecord
STATUS_INSUFFICIENT_RESOURCES
--*/
{
NTSTATUS Status;
LSA_FOREST_TRUST_COLLISION_RECORD * CollisionRecord;
PLSA_FOREST_TRUST_COLLISION_RECORD * EntriesT;
BOOLEAN CollisionInfoAllocated = FALSE;
ASSERT( CollisionInfo != NULL );
//
// First, see if collision information for this index already exists,
// and if so, update the existing entry
//
if ( *CollisionInfo != NULL ) {
for ( ULONG i = 0 ; i < (*CollisionInfo)->RecordCount ; i += 1 ) {
PLSA_FOREST_TRUST_COLLISION_RECORD Record = (*CollisionInfo)->Entries[i];
if ( Record->Index == Index &&
Record->Type == Type &&
(( Name == NULL && Record->Name.Length == 0 ) ||
( Name != NULL && RtlEqualUnicodeString(
Name,
&Record->Name,
TRUE )))) {
//
// This seems like an additional conflict for the same
// entry. Just add the new flags into the mix and get out.
//
Record->Flags |= Flags;
return STATUS_SUCCESS;
}
}
}
CollisionRecord = ( LSA_FOREST_TRUST_COLLISION_RECORD * )
FtcAllocate( sizeof( LSA_FOREST_TRUST_COLLISION_RECORD ));
if ( CollisionRecord == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in LsapAddToCollisionInfo (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
CollisionRecord->Index = Index;
CollisionRecord->Type = Type;
CollisionRecord->Flags = Flags;
if ( Name != NULL ) {
if ( FALSE == FtcCopyUnicodeString(
( UNICODE_STRING * )&CollisionRecord->Name,
Name )) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in LsapAddToCollisionInfo (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
}
if ( *CollisionInfo == NULL ) {
*CollisionInfo = ( LSA_FOREST_TRUST_COLLISION_INFORMATION * )
FtcAllocate( sizeof( LSA_FOREST_TRUST_COLLISION_INFORMATION ));
if ( *CollisionInfo == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in LsapAddToCollisionInfo (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
RtlZeroMemory( *CollisionInfo, sizeof( LSA_FOREST_TRUST_COLLISION_INFORMATION ));
CollisionInfoAllocated = TRUE;
}
EntriesT = ( LSA_FOREST_TRUST_COLLISION_RECORD * * )FtcReallocate(
(*CollisionInfo)->Entries,
(*CollisionInfo)->RecordCount * sizeof( LSA_FOREST_TRUST_COLLISION_RECORD * ),
((*CollisionInfo)->RecordCount + 1) * sizeof( LSA_FOREST_TRUST_COLLISION_RECORD * )
);
if ( EntriesT == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in LsapAddToCollisionInfo (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
(*CollisionInfo)->Entries = EntriesT;
(*CollisionInfo)->Entries[(*CollisionInfo)->RecordCount] = CollisionRecord;
(*CollisionInfo)->RecordCount += 1;
Status = STATUS_SUCCESS;
Cleanup:
return Status;
Error:
ASSERT( !NT_SUCCESS( Status ));
if ( CollisionRecord ) {
FtcFree( CollisionRecord->Name.Buffer );
FtcFree( CollisionRecord );
}
if ( CollisionInfoAllocated ) {
FtcFree( *CollisionInfo );
*CollisionInfo = NULL;
}
goto Cleanup;
}
///////////////////////////////////////////////////////////////////////////////
//
// FTCache public interface
//
///////////////////////////////////////////////////////////////////////////////
FTCache::FTCache()
/*++
Routine Description:
FTCache constructor
Arguments:
None
Returns:
Nothing
NOTE: FTCache::Initialize() must be called before the object is usable
--*/
{
m_Initialized = FALSE;
m_LocalValid = FALSE;
m_ExternalValid = FALSE;
}
FTCache::~FTCache()
/*++
Routine Description:
FTCache destructor
Removes all cache contents as part of the destruction process
Arguments:
None
Returns:
Nothing
--*/
{
if ( m_Initialized ) {
Purge();
}
}
NTSTATUS
FTCache::Initialize()
/*++
Routine Description:
Initializes the forest trust cache.
The initialized cache is not populated and its Valid
bit is set to FALSE
Arguments:
None
Returns:
STATUS_SUCCESS cache initialized successfully
--*/
{
ASSERT( !m_Initialized );
RtlInitializeGenericTableAvl(
&m_TdoTable,
UnicodeStringCompareRoutine,
FtcAllocateRoutine,
FtcFreeRoutine,
NULL
);
RtlInitializeGenericTableAvl(
&m_TopLevelNameTable,
UnicodeStringCompareRoutine,
FtcAllocateRoutine,
FtcFreeRoutine,
NULL
);
RtlInitializeGenericTableAvl(
&m_DomainSidTable,
SidCompareRoutine,
FtcAllocateRoutine,
FtcFreeRoutine,
NULL
);
RtlInitializeGenericTableAvl(
&m_DnsNameTable,
UnicodeStringCompareRoutine,
FtcAllocateRoutine,
FtcFreeRoutine,
NULL
);
RtlInitializeGenericTableAvl(
&m_NetbiosNameTable,
UnicodeStringCompareRoutine,
FtcAllocateRoutine,
FtcFreeRoutine,
NULL
);
m_Initialized = TRUE;
return STATUS_SUCCESS;
}
void
FTCache::SetInvalid()
/*++
Routine Description:
Sets the cache to the "invalid" state and purges its contents
Arguments:
None
Returns:
Nothing
--*/
{
//
// Unregister DS notifications
//
if ( XrefNotificationHandle != NULL ||
UpnSuffixNotificationHandle != NULL ) {
NTSTATUS Status = 0;
BOOLEAN CloseTransaction = FALSE;
//
// Although we are not going to have a transaction, we need
// a valid thread state. Therefore we need to call
// LsapDsInitAllocAsNeededEx
//
Status = LsapDsInitAllocAsNeededEx(
LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_NO_LOCK |
LSAP_DB_DS_OP_TRANSACTION,
TrustedDomainObject,
&CloseTransaction
);
if ( NT_SUCCESS( Status )) {
if ( XrefNotificationHandle != NULL ) {
ULONG DirResult;
DirResult = DirNotifyUnRegister(
XrefNotificationHandle,
NULL // DirNotifyUnRegister does not use its second parameter anyway
);
ASSERT( 0 == DirResult );
//
// Bug 488913:
// DirNotifyUnRegister doesn't actually start a transaction so,
// LsapDsContinueTransaction shouldn't be called.
//
//
// Continue the transaction
//
// LsapDsContinueTransaction();
//
}
if ( UpnSuffixNotificationHandle != NULL ) {
ULONG DirResult;
DirResult = DirNotifyUnRegister(
UpnSuffixNotificationHandle,
NULL // DirNotifyUnRegister does not use its second parameter anyway
);
ASSERT( 0 == DirResult );
}
LsapDsDeleteAllocAsNeededEx(
LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_NO_LOCK |
LSAP_DB_DS_OP_TRANSACTION,
TrustedDomainObject,
CloseTransaction
);
}
//
// Clear the notification handle even if there was an error
// Got no choice here, really.
//
XrefNotificationHandle = NULL;
UpnSuffixNotificationHandle = NULL;
}
//
// Mark the cache as invalid
//
m_LocalValid = FALSE;
m_ExternalValid = FALSE;
//
// And since it's invalid, blow away its contents -- they need to be rebuilt anyway
//
Purge();
}
NTSTATUS
FTCache::Insert(
IN UNICODE_STRING * TrustedDomainName,
IN OPTIONAL PSID TrustedDomainSid,
IN LSA_FOREST_TRUST_INFORMATION * ForestTrustInfo,
IN BOOLEAN LocalForestEntry,
OUT TDO_ENTRY * TdoEntryOldOut,
OUT TDO_ENTRY * * TdoEntryNewOut,
OUT CONFLICT_PAIR * * ConflictPairs,
OUT ULONG * ConflictPairsTotal
)
/*++
Routine Description:
Inserts entries corresponding to the given TDO into the cache
Arguments:
TrustedDomainName Name of the TDO that the enries correspond to
TrustedDomainSid Sid of the TDO
ForestTrustInfo Entries to insert
LocalForestEntry Does this entry correspond to the local forest?
TdoEntryOldOut Used to return old TDO entry
TdoEntryNewOut Used to return new TDO entry
ConflictPairs Used to return conflict information
ConflictPairsTotal
Returns:
STATUS_SUCCESS Entry added successfully
STATUS_INSUFFICIENT_RESOURCES Ran out of memory; entry not added
STATUS_INVALID_PARAMETER Parameters were somehow incorrect
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
TDO_ENTRY * TdoEntryNew = NULL;
LIST_ENTRY * ListEntry;
LARGE_INTEGER CurrentTime;
ULONG Current;
ASSERT( m_Initialized );
if ( TrustedDomainName == NULL ||
ForestTrustInfo == NULL ||
TdoEntryNewOut == NULL ||
TdoEntryOldOut == NULL ||
ConflictPairs == NULL ||
ConflictPairsTotal == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in FTCache::Insert (%s:%d)\n", __FILE__, __LINE__ ));
ASSERT( FALSE ); // should not happen in released code
return STATUS_INVALID_PARAMETER;
}
LsapDsDebugOut((
DEB_FTINFO,
"FTCache::Insert called, TDO: %wZ, Entries: %d, Local: %s\n",
TrustedDomainName,
ForestTrustInfo->RecordCount,
LocalForestEntry ? "Yes" : "No"
));
RtlZeroMemory( TdoEntryOldOut, sizeof( TDO_ENTRY ));
*ConflictPairs = NULL;
*ConflictPairsTotal = 0;
#if DBG
//
// Debug only: display diagnostic information about what's being inserted
//
if ( LsaDsInfoLevel & DEB_FTINFO ) {
LsapDsDebugOut((
DEB_FTINFO,
"\n++++++++++++++++++ FTInfo Record List +++++++++++++++++++\n\n"
));
for ( Current = 0 ; Current < ForestTrustInfo->RecordCount ; Current++ ) {
LSA_FOREST_TRUST_RECORD * Record = ForestTrustInfo->Entries[Current];
BOOLEAN Duplicate = FALSE;
switch ( Record->ForestTrustType ) {
case ForestTrustTopLevelName:
case ForestTrustTopLevelNameEx: {
LsapDsDebugOut((
DEB_FTINFO,
"%d. TopLevelName%s: %wZ (%s: %d)\n",
Current,
Record->ForestTrustType == ForestTrustTopLevelNameEx ? "Ex" : "",
&Record->ForestTrustData.TopLevelName,
Record->Flags ? "Disabled" : "Enabled",
Record->Flags
));
break;
}
case ForestTrustDomainInfo: {
LsapDsDebugOut((
DEB_FTINFO,
"%d. DomainInfo: DNS %wZ, NB %wZ (%s: %d)\n",
Current,
&Record->ForestTrustData.DomainInfo.DnsName,
&Record->ForestTrustData.DomainInfo.NetbiosName,
Record->Flags ? "Disabled" : "Enabled",
Record->Flags
));
break;
}
default:
LsapDsDebugOut((
DEB_FTINFO,
"%d. Unrecognized type: %d\n",
Current,
Record->ForestTrustType
));
break;
}
}
LsapDsDebugOut((
DEB_FTINFO,
"\n---------------------------------------------------------\n\n"
));
}
#endif
//
// Figure out if the cache contains information for this TDO already.
// If so, save off the existing info in case the changes need to
// be reverted later.
//
TdoEntryNew = ( TDO_ENTRY * )RtlLookupElementGenericTableAvl(
&m_TdoTable,
TrustedDomainName
);
if ( TdoEntryNew == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Cache does not contain current information for %wZ\n", TrustedDomainName ));
CLONG EntrySize = sizeof( TDO_ENTRY ) + TrustedDomainName->Length + sizeof( WCHAR );
TDO_ENTRY * TdoEntry;
SafeAllocaAllocate( TdoEntry, EntrySize );
if ( TdoEntry == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::Insert (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
TdoEntry->RecordCount = 0;
TdoEntry->TrustedDomainSid = NULL; // filled in later
//
// Initialize a cache entry for this TDO
//
FtcCopyUnicodeString(
&TdoEntry->TrustedDomainName,
TrustedDomainName,
TdoEntry->TrustedDomainNameBuffer
);
TdoEntryNew = ( TDO_ENTRY * )RtlInsertElementGenericTableAvl(
&m_TdoTable,
TdoEntry,
EntrySize,
NULL
);
SafeAllocaFree( TdoEntry );
TdoEntry = NULL;
if ( TdoEntryNew == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::Insert (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
TdoEntryNew->TrustedDomainName.Buffer = TdoEntryNew->TrustedDomainNameBuffer;
ASSERT( ++sm_TdoEntries > 0 );
} else {
ASSERT( RtlEqualUnicodeString(
&TdoEntryNew->TrustedDomainName,
TrustedDomainName,
TRUE ));
LsapDsDebugOut(( DEB_FTINFO, "FTCache::Insert: Cache already contains information for %wZ\n", TrustedDomainName ));
if ( TdoEntryNew->LocalForestEntry != LocalForestEntry ) {
//
// Replacing a local forest entry with a non-local forest entry
// (or vice versa) is clearly bogus
//
LsapDsDebugOut(( DEB_FTINFO, "FTCache::Insert: LocalForestEntry flags mismatched (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
ASSERT( FALSE ); // not sure if this is even possible
TdoEntryNew = NULL; // ensure no rollback at cleanup time
goto Error;
}
CopyTdoEntry( TdoEntryOldOut, TdoEntryNew );
}
ASSERT( TdoEntryNew != NULL );
//
// All InitializeListHead calls must take place AFTER
// the entry has been inserted into the table
// since they use self-referencing pointers
//
TdoEntryNew->RecordCount = 0;
TdoEntryNew->LocalForestEntry = LocalForestEntry;
InitializeListHead( &TdoEntryNew->TlnList );
InitializeListHead( &TdoEntryNew->DomainInfoList );
InitializeListHead( &TdoEntryNew->BinaryList );
//
// Store the SID with the entry
//
if ( TrustedDomainSid ) {
ULONG SidLength = RtlLengthSid( TrustedDomainSid );
TdoEntryNew->TrustedDomainSid = ( PSID )FtcAllocate( SidLength );
if ( TdoEntryNew->TrustedDomainSid == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::Insert (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
RtlCopySid(
SidLength,
TdoEntryNew->TrustedDomainSid,
TrustedDomainSid
);
} else {
TdoEntryNew->TrustedDomainSid = NULL;
}
//
// All "new" entries will be given the same timestamp
//
GetSystemTimeAsFileTime(( LPFILETIME )&CurrentTime );
//
// Populate the forest trust cache with the information from ForestTrustInfo
// This is done without regard to collisions. A collision check follows.
//
for ( Current = 0 ; Current < ForestTrustInfo->RecordCount ; Current++ ) {
LSA_FOREST_TRUST_RECORD * Record = ForestTrustInfo->Entries[Current];
BOOLEAN Duplicate = FALSE;
switch ( Record->ForestTrustType ) {
case ForestTrustTopLevelName: {
UNICODE_STRING TopLevelName = Record->ForestTrustData.TopLevelName;
TLN_ENTRY * SubordinateEntry = NULL;
TLN_KEY * * TlnKeys = NULL;
TLN_ENTRY * * TlnEntries = NULL;
USHORT DnsComponents;
USHORT Component;
//
// If these asserts fire, you forgot to validate the
// data before passing it in. Use LsapValidateForestTrustInfo().
//
ASSERT( TopLevelName.Length > 0 );
ASSERT( TopLevelName.Buffer != NULL );
DnsComponents = DnsNameComponents( &TopLevelName );
SafeAllocaAllocate( TlnKeys, sizeof( TLN_KEY * ) * DnsComponents );
if ( TlnKeys == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::Insert (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto TlnError;
}
RtlZeroMemory( TlnKeys, sizeof( TLN_KEY * ) * DnsComponents );
SafeAllocaAllocate( TlnEntries, sizeof( TLN_ENTRY * ) * DnsComponents );
if ( TlnEntries == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::Insert (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto TlnError;
}
RtlZeroMemory( TlnEntries, sizeof( TLN_ENTRY * ) * DnsComponents );
for ( Component = 0 ; Component < DnsComponents ; Component++ ) {
//
// Move to the next component (except on first iteration through the loop)
//
if ( Component > 0 ) {
NextDnsComponent( &TopLevelName );
}
//
// Locate a match for this TLN in the cache
//
TlnKeys[Component] = ( TLN_KEY * )RtlLookupElementGenericTableAvl(
&m_TopLevelNameTable,
&TopLevelName
);
if ( TlnKeys[Component] == NULL ) {
//
// Nothing matching this top level name was found;
// Initialize and insert a new entry into the tree
//
CLONG KeySize = sizeof( TLN_KEY ) + TopLevelName.Length + sizeof( WCHAR );
TLN_KEY * TlnKeyNew;
SafeAllocaAllocate( TlnKeyNew, KeySize );
if ( TlnKeyNew == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::Insert (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto TlnError;
}
TlnKeyNew->Count = 0;
FtcCopyUnicodeString(
&TlnKeyNew->TopLevelName,
&TopLevelName,
TlnKeyNew->TopLevelNameBuffer
);
TlnKeys[Component] = ( TLN_KEY * )RtlInsertElementGenericTableAvl(
&m_TopLevelNameTable,
TlnKeyNew,
KeySize,
NULL
);
SafeAllocaFree( TlnKeyNew );
TlnKeyNew = NULL;
if ( TlnKeys[Component] == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::Insert (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto TlnError;
}
TlnKeys[Component]->TopLevelName.Buffer = TlnKeys[Component]->TopLevelNameBuffer;
ASSERT( ++sm_TlnKeys > 0 );
//
// All InitializeListHead calls must take place AFTER
// the entry has been inserted into the table
// since they use self-referential pointers
//
InitializeListHead( &TlnKeys[Component]->List );
} else {
//
// This name is known inside the cache.
// This could be duplicate, invalid parameter or a conflict.
// For now, check for duplicates and invalid
// parameter conditions only.
//
for ( ListEntry = TlnKeys[Component]->List.Flink;
ListEntry != &TlnKeys[Component]->List;
ListEntry = ListEntry->Flink ) {
TLN_ENTRY * TlnEntry;
TlnEntry = TLN_ENTRY::EntryFromAvlEntry( ListEntry );
if ( TlnEntry->TdoEntry != TdoEntryNew ) {
//
// Only interested in the entries for the same TDO here
//
continue;
}
if ( TlnEntry->Excluded ) {
//
// Having both a top level name and an exclusion
// record for the same name is clearly invalid.
//
// Also, a top level name can not be subordinate
// to an exclusion record for the same TLN.
//
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in FTCache::Insert (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
goto TlnError;
} else if ( TlnEntry->SubordinateEntry != NULL &&
SubordinateEntry == NULL ) {
//
// If we're looking at a subordinate entry
// (e.g. acme.com subordinate to NY.acme.com)
// and we're trying to insert a top level entry
// of acme.com, then the existece of the other
// top level entry is invalid (the namespace would
// be claimed by acme.com and NY.acme.com is redundant)
//
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in FTCache::Insert (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
goto TlnError;
} else if ( TlnEntry->SubordinateEntry == NULL &&
SubordinateEntry != NULL ) {
//
// If we're looking at a top-level entry (e.g. acme.com)
// and we're trying to insert a superior entry of another
// top level name that matches it (say "acme.com" for
// a TLN of "NY.acme.com") then we're doing something
// wrong.
//
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in FTCache::Insert (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
goto TlnError;
} else if ( TlnEntry->SubordinateEntry != NULL &&
SubordinateEntry != NULL ) {
//
// Both entries are superior entries. This will happen
// if both "WA.acme.com" and "NY.acme.com" are claimed
// by this TDO, so both "acme.com" entries must exist
// in the cache and it's not a problem.
//
// Do nothing
//
} else {
ASSERT( TlnEntry->SubordinateEntry == NULL );
ASSERT( SubordinateEntry == NULL );
//
// Found two identical top-level entries for the same
// TDO. Second entry is a duplicate and can be dropped.
//
// Note that the disabled status of the two can be
// different. Too bad. The later entry is ignored.
// Alternatively, this can be considered as invalid parameter.
//
LsapDsDebugOut(( DEB_FTINFO, "Record %d is a duplicate of entry %p (%s:%d)\n", Current, TlnEntry, __FILE__, __LINE__ ));
Duplicate = TRUE;
break;
}
}
if ( Duplicate ) {
break;
}
}
ASSERT( TlnKeys[Component] != NULL );
TlnEntries[Component] = ( TLN_ENTRY * )FtcAllocate( sizeof( TLN_ENTRY ));
if ( TlnEntries[Component] == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::Insert (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto TlnError;
}
ASSERT( ++sm_TlnEntries > 0 );
//
// Associate this item with the TDO cache entry
//
InsertHeadList( &TdoEntryNew->TlnList, &TlnEntries[Component]->TdoListEntry );
//
// ... and also with the AVL tree entry
//
TlnKeys[Component]->Count += 1;
InsertHeadList( &TlnKeys[Component]->List, &TlnEntries[Component]->AvlListEntry );
//
// Initialize the rest of TLN_ENTRY fields
//
if ( Record->Time.LowPart == 0 && Record->Time.HighPart == 0 ) {
TlnEntries[Component]->Time = CurrentTime;
} else {
TlnEntries[Component]->Time = Record->Time;
}
TlnEntries[Component]->Excluded = FALSE;
TlnEntries[Component]->Index = Current;
TlnEntries[Component]->TdoEntry = TdoEntryNew;
TlnEntries[Component]->SubordinateEntry = SubordinateEntry;
TlnEntries[Component]->TlnKey = TlnKeys[Component];
TlnEntries[Component]->SetFlags( Record->Flags );
//
// Prepare for next iteration
//
SubordinateEntry = TlnEntries[Component];
}
SafeAllocaFree( TlnKeys );
SafeAllocaFree( TlnEntries );
break;
TlnError:
//
// Blow away all the entries and keys inserted in this round
//
for ( Component = 0 ; Component < DnsComponents ; Component++ ) {
if ( TlnEntries &&
TlnEntries[Component] != NULL ) {
ASSERT( sm_TlnEntries-- > 0 );
RemoveEntryList( &TlnEntries[Component]->TdoListEntry );
RemoveEntryList( &TlnEntries[Component]->AvlListEntry );
TlnEntries[Component]->TlnKey->Count -= 1;
FtcFree( TlnEntries[Component] );
}
if ( TlnKeys &&
TlnKeys[Component] != NULL &&
TlnKeys[Component]->Count == 0 ) {
BOOLEAN Found;
Found = RtlDeleteElementGenericTableAvl(
&m_TopLevelNameTable,
TlnKeys[Component]
);
ASSERT( sm_TlnKeys-- > 0 );
ASSERT( Found );
}
}
SafeAllocaFree( TlnKeys );
SafeAllocaFree( TlnEntries );
goto Error;
}
case ForestTrustTopLevelNameEx: {
UNICODE_STRING TopLevelName = Record->ForestTrustData.TopLevelName;
TLN_ENTRY * TlnEntry;
TLN_KEY * TlnKey;
//
// If these asserts fire, you forgot to validate the
// data before passing it in. Use LsapValidateForestTrustInfo().
//
ASSERT( TopLevelName.Length > 0 );
ASSERT( TopLevelName.Buffer != NULL );
if ( 1 >= DnsNameComponents( &TopLevelName )) {
//
// An exclusion record claims part of the namespace, and
// so must contain at least 2 DNS name components.
// e.g. 'acme.com' is valid but 'com' is not
//
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in FTCache::Insert (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
goto Error;
}
TlnKey = ( TLN_KEY * )RtlLookupElementGenericTableAvl(
&m_TopLevelNameTable,
&TopLevelName
);
if ( TlnKey == NULL ) {
//
// Nothing matching this top level name was found;
// Initialize and insert a new entry into the tree
//
CLONG KeySize = sizeof( TLN_KEY ) + TopLevelName.Length + sizeof( WCHAR );
TLN_KEY * TlnKeyNew;
SafeAllocaAllocate( TlnKeyNew, KeySize );
if ( TlnKeyNew == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::Insert (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
TlnKeyNew->Count = 0;
FtcCopyUnicodeString(
&TlnKeyNew->TopLevelName,
&TopLevelName,
TlnKeyNew->TopLevelNameBuffer
);
TlnKey = ( TLN_KEY * )RtlInsertElementGenericTableAvl(
&m_TopLevelNameTable,
TlnKeyNew,
KeySize,
NULL
);
SafeAllocaFree( TlnKeyNew );
TlnKeyNew = NULL;
if ( TlnKey == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::Insert (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
TlnKey->TopLevelName.Buffer = TlnKey->TopLevelNameBuffer;
ASSERT( ++sm_TlnKeys > 0 );
//
// All InitializeListHead calls must take place AFTER
// the entry has been inserted into the table
// since they use self-referential pointers
//
InitializeListHead( &TlnKey->List );
} else {
//
// This name is known inside the cache.
// This could be duplicate, invalid parameter or a conflict.
// For now, check for duplicates and invalid
// parameter conditions only.
//
for ( ListEntry = TlnKey->List.Flink;
ListEntry != &TlnKey->List;
ListEntry = ListEntry->Flink ) {
TLN_ENTRY * TlnEntry;
TlnEntry = TLN_ENTRY::EntryFromAvlEntry( ListEntry );
if ( TlnEntry->TdoEntry != TdoEntryNew ) {
//
// Only interested in the entries for the same TDO here
//
continue;
}
if ( TlnEntry->Excluded != TRUE ) {
//
// Having both a top level and an exclusion
// record for the same name is clearly broken.
//
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in FTCache::Insert (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
goto Error;
}
//
// If the other entry is for the same TDO as this one,
// this is bogus input. Ignore this record.
//
LsapDsDebugOut(( DEB_FTINFO, "FTCache::Insert: Record %d is a duplicate of entry %p (%s:%d)\n", Current, TlnEntry, __FILE__, __LINE__ ));
Duplicate = TRUE;
break;
}
if ( Duplicate ) {
break;
}
}
ASSERT( TlnKey != NULL );
TlnEntry = ( TLN_ENTRY * )FtcAllocate( sizeof( TLN_ENTRY ));
if ( TlnEntry == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::Insert (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
ASSERT( ++sm_TlnEntries > 0 );
//
// Associate this item with the TDO cache entry
//
InsertHeadList( &TdoEntryNew->TlnList, &TlnEntry->TdoListEntry );
//
// ... and also with the AVL tree entry
//
TlnKey->Count += 1;
InsertHeadList( &TlnKey->List, &TlnEntry->AvlListEntry );
//
// Initialize the rest of TLN_ENTRY fields
//
TlnEntry->Excluded = TRUE;
TlnEntry->Index = Current;
TlnEntry->TdoEntry = TdoEntryNew;
TlnEntry->SuperiorEntry = NULL;
TlnEntry->TlnKey = TlnKey;
TlnEntry->SetFlags( Record->Flags ); // value ignored for exclusion entries
if ( Record->Time.LowPart == 0 && Record->Time.HighPart == 0 ) {
TlnEntry->Time = CurrentTime;
} else {
TlnEntry->Time = Record->Time;
}
break;
}
case ForestTrustDomainInfo: {
SID * Sid = ( SID * )Record->ForestTrustData.DomainInfo.Sid;
UNICODE_STRING * DnsName = &Record->ForestTrustData.DomainInfo.DnsName;
UNICODE_STRING * NetbiosName = &Record->ForestTrustData.DomainInfo.NetbiosName;
DOMAIN_INFO_ENTRY * DomainInfoEntry;
DOMAIN_SID_KEY * SidKey;
DNS_NAME_KEY * DnsKey;
NETBIOS_NAME_KEY * NetbiosKey;
BOOLEAN NetbiosPresent = ( NetbiosName->Length > 0 && NetbiosName->Buffer != NULL );
ULONG SidLength;
BOOLEAN SidKeyAllocated = FALSE;
BOOLEAN DnsKeyAllocated = FALSE;
BOOLEAN NetbiosKeyAllocated = FALSE;
//
// If these asserts fire, you forgot to validate the
// data before passing it in. Use LsapValidateForestTrustInfo().
//
ASSERT( Sid );
ASSERT( DnsName->Length > 0 );
ASSERT( DnsName->Buffer != NULL );
if ( !RtlValidSid( Sid )) {
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in FTCache::Insert (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
goto Error;
}
SidLength = RtlLengthSid( Sid );
ASSERT( SidLength > 0 );
SidKey = ( DOMAIN_SID_KEY * )RtlLookupElementGenericTableAvl(
&m_DomainSidTable,
&Sid
);
DnsKey = ( DNS_NAME_KEY * )RtlLookupElementGenericTableAvl(
&m_DnsNameTable,
DnsName
);
if ( NetbiosPresent ) {
NetbiosKey = (NETBIOS_NAME_KEY * )RtlLookupElementGenericTableAvl(
&m_NetbiosNameTable,
NetbiosName
);
} else {
NetbiosKey = NULL;
}
if ( SidKey == NULL ) {
//
// Nothing matching this domain SID was found;
// Initialize and insert a new entry into the tree
//
CLONG KeySize = sizeof( DOMAIN_SID_KEY ) + SidLength;
DOMAIN_SID_KEY * SidKeyNew;
SafeAllocaAllocate( SidKeyNew, KeySize );
if ( SidKeyNew == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::Insert (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto DomainInfoError;
}
SidKeyNew->Count = 0;
SidKeyNew->DomainSid = ( SID * )SidKeyNew->SidBuffer;
RtlCopySid( SidLength, SidKeyNew->DomainSid, Sid );
SidKey = ( DOMAIN_SID_KEY * )RtlInsertElementGenericTableAvl(
&m_DomainSidTable,
SidKeyNew,
KeySize,
NULL
);
SafeAllocaFree( SidKeyNew );
SidKeyNew = NULL;
if ( SidKey == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::Insert (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto DomainInfoError;
}
SidKey->DomainSid = ( SID * )SidKey->SidBuffer;
SidKeyAllocated = TRUE;
ASSERT( ++sm_SidKeys > 0 );
//
// All InitializeListHead calls must take place AFTER
// the entry has been inserted into the table
// since they use self-referential pointers
//
InitializeListHead( &SidKey->List );
} else {
//
// This SID is known inside the cache.
// Check for duplicates here, for conflicts later.
//
for ( ListEntry = SidKey->List.Flink;
ListEntry != &SidKey->List;
ListEntry = ListEntry->Flink ) {
DOMAIN_INFO_ENTRY * DomainInfoEntry;
DomainInfoEntry = DOMAIN_INFO_ENTRY::EntryFromSidEntry( ListEntry );
if ( DomainInfoEntry->TdoEntry != TdoEntryNew ) {
//
// Only interested in the entries for the same TDO here
//
continue;
}
LsapDsDebugOut(( DEB_FTINFO, "Record %d is a duplicate of entry %p (%s:%d)\n", Current, DomainInfoEntry, __FILE__, __LINE__ ));
Duplicate = TRUE;
break;
}
if ( Duplicate ) {
goto DuplicateEntry;
}
}
ASSERT( SidKey != NULL );
if ( DnsKey == NULL ) {
//
// Nothing matches this DNS name
// Initialize and insert a new entry into the tree
//
CLONG KeySize = sizeof( DNS_NAME_KEY ) + DnsName->Length + sizeof( WCHAR );
DNS_NAME_KEY * DnsKeyNew;
SafeAllocaAllocate( DnsKeyNew, KeySize );
if ( DnsKeyNew == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::Insert (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto DomainInfoError;
}
DnsKeyNew->Count = 0;
FtcCopyUnicodeString(
&DnsKeyNew->DnsName,
DnsName,
DnsKeyNew->DnsNameBuffer
);
DnsKey = ( DNS_NAME_KEY * )RtlInsertElementGenericTableAvl(
&m_DnsNameTable,
DnsKeyNew,
KeySize,
NULL
);
SafeAllocaFree( DnsKeyNew );
DnsKeyNew = NULL;
if ( DnsKey == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::Insert (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto DomainInfoError;
}
DnsKey->DnsName.Buffer = DnsKey->DnsNameBuffer;
DnsKeyAllocated = TRUE;
ASSERT( ++sm_DnsNameKeys > 0 );
//
// All InitializeListHead calls must take place AFTER
// the entry has been inserted into the table
// since they use self-referential pointers
//
InitializeListHead( &DnsKey->List );
} else {
//
// This name is known inside the cache.
// Check for duplicates here, for conflicts later.
//
for ( ListEntry = DnsKey->List.Flink;
ListEntry != &DnsKey->List;
ListEntry = ListEntry->Flink ) {
DOMAIN_INFO_ENTRY * DomainInfoEntry;
DomainInfoEntry = DOMAIN_INFO_ENTRY::EntryFromDnsEntry( ListEntry );
if ( DomainInfoEntry->TdoEntry != TdoEntryNew ) {
//
// Only interested in the entries for the same TDO here
//
continue;
}
LsapDsDebugOut(( DEB_FTINFO, "Record %d is a duplicate of entry %p (%s:%d)\n", Current, DomainInfoEntry, __FILE__, __LINE__ ));
Duplicate = TRUE;
break;
}
if ( Duplicate ) {
goto DuplicateEntry;
}
}
ASSERT( DnsKey != NULL );
if ( NetbiosPresent && NetbiosKey == NULL ) {
//
// Nothing matches this NetbiosName
// Initialize and insert a new entry into the tree
//
CLONG KeySize = sizeof( NETBIOS_NAME_KEY ) + NetbiosName->Length + sizeof( WCHAR );
NETBIOS_NAME_KEY * NetbiosKeyNew;
SafeAllocaAllocate( NetbiosKeyNew, KeySize );
if ( NetbiosKeyNew == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::Insert (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto DomainInfoError;
}
NetbiosKeyNew->Count = 0;
FtcCopyUnicodeString(
&NetbiosKeyNew->NetbiosName,
NetbiosName,
NetbiosKeyNew->NetbiosNameBuffer
);
NetbiosKey = ( NETBIOS_NAME_KEY * )RtlInsertElementGenericTableAvl(
&m_NetbiosNameTable,
NetbiosKeyNew,
KeySize,
NULL
);
SafeAllocaFree( NetbiosKeyNew );
NetbiosKeyNew = NULL;
if ( NetbiosKey == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::Insert (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto DomainInfoError;
}
NetbiosKey->NetbiosName.Buffer = NetbiosKey->NetbiosNameBuffer;
NetbiosKeyAllocated = TRUE;
ASSERT( ++sm_NetbiosNameKeys > 0 );
//
// All InitializeListHead calls must take place AFTER
// the entry has been inserted into the table
// since they use self-referential pointers
//
InitializeListHead( &NetbiosKey->List );
} else if ( NetbiosKey != NULL ) {
//
// This name is known inside the cache.
// Check for duplicates here, for conflicts later.
//
for ( ListEntry = NetbiosKey->List.Flink;
ListEntry != &NetbiosKey->List;
ListEntry = ListEntry->Flink ) {
DOMAIN_INFO_ENTRY * DomainInfoEntry;
DomainInfoEntry = DOMAIN_INFO_ENTRY::EntryFromNetbiosEntry( ListEntry );
if ( DomainInfoEntry->TdoEntry != TdoEntryNew ) {
//
// Only interested in the entries for the same TDO here
//
continue;
}
LsapDsDebugOut(( DEB_FTINFO, "Record %d is a duplicate of entry %p (%s:%d)\n", Current, DomainInfoEntry, __FILE__, __LINE__ ));
Duplicate = TRUE;
break;
}
if ( Duplicate ) {
goto DuplicateEntry;
}
}
ASSERT( !NetbiosPresent || NetbiosKey != NULL );
DomainInfoEntry = ( DOMAIN_INFO_ENTRY * )FtcAllocate( sizeof( DOMAIN_INFO_ENTRY ));
if ( DomainInfoEntry == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::Insert (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto DomainInfoError;
}
DomainInfoEntry->Sid = ( SID * )FtcAllocate( SidLength );
if ( DomainInfoEntry->Sid == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::Insert (%s:%d)\n", __FILE__, __LINE__ ));
FtcFree( DomainInfoEntry );
Status = STATUS_INSUFFICIENT_RESOURCES;
goto DomainInfoError;
}
ASSERT( ++sm_DomainInfoEntries > 0 );
//
// Associate this item with the TDO cache entry
//
InsertHeadList( &TdoEntryNew->DomainInfoList, &DomainInfoEntry->TdoListEntry );
//
// And also with the AVL list entries
//
SidKey->Count += 1;
InsertHeadList( &SidKey->List, &DomainInfoEntry->SidAvlListEntry );
DnsKey->Count += 1;
InsertHeadList( &DnsKey->List, &DomainInfoEntry->DnsAvlListEntry );
if ( NetbiosPresent ) {
NetbiosKey->Count += 1;
InsertHeadList( &NetbiosKey->List, &DomainInfoEntry->NetbiosAvlListEntry );
} else {
InitializeListHead( &DomainInfoEntry->NetbiosAvlListEntry );
}
//
// Initialize the rest of DomainInfoEntry fields
//
if ( Record->Time.LowPart == 0 && Record->Time.HighPart == 0 ) {
DomainInfoEntry->Time = CurrentTime;
} else {
DomainInfoEntry->Time = Record->Time;
}
DomainInfoEntry->Index = Current;
RtlCopySid( SidLength, DomainInfoEntry->Sid, Sid );
DomainInfoEntry->TdoEntry = TdoEntryNew;
DomainInfoEntry->SubordinateTo = NULL;
DomainInfoEntry->SidKey = SidKey;
DomainInfoEntry->DnsKey = DnsKey;
DomainInfoEntry->NetbiosKey = NetbiosKey;
DomainInfoEntry->SetFlags( Record->Flags );
break;
DuplicateEntry:
//
// Duplicates aren't errors!
//
ASSERT( NT_SUCCESS( Status ));
DomainInfoError:
if ( SidKeyAllocated ) {
BOOLEAN Found;
Found = RtlDeleteElementGenericTableAvl(
&m_DomainSidTable,
&Sid
);
ASSERT( sm_SidKeys-- > 0 );
}
if ( DnsKeyAllocated ) {
BOOLEAN Found;
Found = RtlDeleteElementGenericTableAvl(
&m_DnsNameTable,
&DnsName
);
ASSERT( sm_DnsNameKeys-- > 0 );
ASSERT( Found );
}
if ( NetbiosKeyAllocated ) {
BOOLEAN Found;
Found = RtlDeleteElementGenericTableAvl(
&m_NetbiosNameTable,
&NetbiosName
);
ASSERT( sm_NetbiosNameKeys-- > 0 );
ASSERT( Found );
}
if ( !NT_SUCCESS( Status )) {
goto Error;
} else {
break;
}
}
default: {
BINARY_ENTRY * BinaryEntry;
BinaryEntry = ( BINARY_ENTRY * )FtcAllocate( sizeof( BINARY_ENTRY ));
if ( BinaryEntry == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::Insert (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
BinaryEntry->Data.Length = Record->ForestTrustData.Data.Length;
if ( BinaryEntry->Data.Length > 0 ) {
BinaryEntry->Data.Buffer = ( BYTE * )FtcAllocate( BinaryEntry->Data.Length );
if ( BinaryEntry->Data.Buffer == NULL ) {
FtcFree( BinaryEntry );
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::Insert (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
RtlCopyMemory(
BinaryEntry->Data.Buffer,
Record->ForestTrustData.Data.Buffer,
BinaryEntry->Data.Length
);
} else {
BinaryEntry->Data.Buffer = NULL;
}
ASSERT( ++sm_BinaryEntries > 0 );
//
// Associate this item with the TDO cache entry
//
InsertHeadList( &TdoEntryNew->BinaryList, &BinaryEntry->TdoListEntry );
//
// Initialize the rest of BinaryEntry fields
//
BinaryEntry->Type = Record->ForestTrustType;
BinaryEntry->SetFlags( Record->Flags );
break;
}
}
if ( !Duplicate ) {
TdoEntryNew->RecordCount += 1;
}
}
ASSERT( TdoEntryNew->RecordCount <= ForestTrustInfo->RecordCount );
//
// Before testing for conflicts, validate the records for internal
// consistency. Lack of consistency is an invalid parameter, not a conflict.
//
//
// First validate top-level and excluded entries
//
for ( ListEntry = TdoEntryNew->TlnList.Flink;
ListEntry != &TdoEntryNew->TlnList;
ListEntry = ListEntry->Flink ) {
TLN_ENTRY * TlnEntry;
TlnEntry = TLN_ENTRY::EntryFromTdoEntry( ListEntry );
if ( TlnEntry->Excluded ) {
ASSERT( TlnEntry->SuperiorEntry == NULL );
//
// An excluded entry must be subordinate to a top level name
// for the same TDO
//
UNICODE_STRING ExcludedName = TlnEntry->TlnKey->TopLevelName;
//
// The name was validated prior to inserting it into the tree
//
ASSERT( 1 < DnsNameComponents( &ExcludedName ));
for ( NextDnsComponent( &ExcludedName );
ExcludedName.Length > 0;
NextDnsComponent( &ExcludedName )) {
TLN_KEY * TlnKeySup = ( TLN_KEY * )RtlLookupElementGenericTableAvl(
&m_TopLevelNameTable,
&ExcludedName
);
if ( TlnKeySup == NULL ) {
continue;
}
for ( LIST_ENTRY * ListEntrySup = TlnKeySup->List.Flink;
ListEntrySup != &TlnKeySup->List;
ListEntrySup = ListEntrySup->Flink ) {
TLN_ENTRY * TlnEntrySup;
TlnEntrySup = TLN_ENTRY::EntryFromAvlEntry( ListEntrySup );
if ( TlnEntrySup->TdoEntry != TdoEntryNew ) {
//
// Only interested in the entries for the same TDO here
//
continue;
}
if ( TlnEntrySup->Excluded ) {
//
// Another entry for the same TDO excludes a superior namespace.
// UI wants to retain both (in case sales.redmond.microsoft.com is
// already excluded and now the admin wants to exclude
// redmond.microsoft.com also)
//
continue;
}
if ( TlnEntrySup->SubordinateEntry != NULL ) {
//
// Skip over superior entries -- they do not participate
// in consistency checking because they just point to
// real top level entries
//
continue;
}
//
// Found our superior entry! Remember it.
//
TlnEntry->SuperiorEntry = TlnEntrySup;
break;
}
if ( TlnEntry->SuperiorEntry != NULL ) {
break;
}
}
if ( TlnEntry->SuperiorEntry == NULL ) {
//
// This excluded top-level name entry does not have
// a superior TLN, thus it is invalid.
//
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in FTCache::Insert (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
goto Error;
}
}
}
//
// Now validate domain info entries.
// Each domain info entry must be subordinate to a top level name.
//
// An exception is made for domain info entries that have the
// "administratively disabled" bits set and are not subordinate
// to any top level name. Such entries are treated as "store and ignore"
// to prevent rogue trusted domains from making the trusting side
// forget about administratively disabled entries.
//
for ( ListEntry = TdoEntryNew->DomainInfoList.Flink;
ListEntry != &TdoEntryNew->DomainInfoList;
ListEntry = ListEntry->Flink ) {
DOMAIN_INFO_ENTRY * DomainInfoEntry;
DomainInfoEntry = DOMAIN_INFO_ENTRY::EntryFromTdoEntry( ListEntry );
//
// DomainInfoEntry->SubordinateTo is the field we seek to fill in
//
ASSERT( DomainInfoEntry->SubordinateTo == NULL );
for ( UNICODE_STRING SuperiorName = DomainInfoEntry->DnsKey->DnsName;
SuperiorName.Length > 0;
NextDnsComponent( &SuperiorName )) {
TLN_KEY * TlnKeySup = ( TLN_KEY * )RtlLookupElementGenericTableAvl(
&m_TopLevelNameTable,
&SuperiorName
);
if ( TlnKeySup == NULL ) {
continue;
}
for ( LIST_ENTRY * ListEntrySup = TlnKeySup->List.Flink;
ListEntrySup != &TlnKeySup->List;
ListEntrySup = ListEntrySup->Flink ) {
TLN_ENTRY * TlnEntrySup;
TlnEntrySup = TLN_ENTRY::EntryFromAvlEntry( ListEntrySup );
if ( TlnEntrySup->TdoEntry != TdoEntryNew ) {
//
// Only interested in the entries for the same TDO here
//
continue;
}
if ( TlnEntrySup->Excluded ) {
//
// Looking for non-excluded TLNs only
//
continue;
}
if ( TlnEntrySup->SubordinateEntry == NULL ) {
//
// Found our match: a non-excluded TLN that is not a
// subordinate of another TLN
//
DomainInfoEntry->SubordinateTo = TlnEntrySup;
break;
}
}
if ( DomainInfoEntry->SubordinateTo != NULL ) {
//
// Found the entry we're subordinate to, stop the search here
//
break;
}
}
if ( DomainInfoEntry->SubordinateTo == NULL &&
!DomainInfoEntry->IsSidAdminDisabled()) {
//
// This name is not admin-disabled and
// does not have a superior TLN.
// Thus it is invalid.
//
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in FTCache::Insert (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
goto Error;
}
}
//
// At this point, the newly inserted trusted domain entry is internally
// consistent. Code that follows fully assumes this fact.
//
//
// Rules for an enabled top level name record:
//
// * Must not be equal to an enabled TLN for another TDO
// * Must not be subordinate to an enabled TLN for another TDO
// unless the other TDO has a corresponding exclusion record
// * Must not be superior to an enabled TLN for another TDO
// unless this TDO has a corresponding exclusion record
//
for ( ListEntry = TdoEntryNew->TlnList.Flink;
ListEntry != &TdoEntryNew->TlnList;
ListEntry = ListEntry->Flink ) {
TLN_ENTRY * ThisEntry;
ThisEntry = TLN_ENTRY::EntryFromTdoEntry( ListEntry );
if ( ThisEntry->Excluded ) {
//
// Nothing to check at the moment (validity was verified above)
//
continue;
}
if ( !ThisEntry->Enabled()) {
//
// Records that are not enabled are not considered for collision detection
//
continue;
}
for ( LIST_ENTRY * ListEntrySup = ThisEntry->TlnKey->List.Flink;
ListEntrySup != &ThisEntry->TlnKey->List;
ListEntrySup = ListEntrySup->Flink ) {
TLN_ENTRY * OtherEntry;
TLN_ENTRY * ConflictEntry = NULL;
OtherEntry = TLN_ENTRY::EntryFromAvlEntry( ListEntrySup );
if ( OtherEntry->TdoEntry == TdoEntryNew ||
OtherEntry->TdoEntry == TdoEntryOldOut ) {
//
// Only interested in entries corresponding to other TDOs
//
continue;
}
if ( OtherEntry->Excluded ) {
//
// Not interested in excluded entries
//
continue;
}
if ( !OtherEntry->Enabled()) {
//
// Not interested in disabled entries
//
continue;
}
//
// Several types of conflicts are possible:
//
if ( ThisEntry->SubordinateEntry == NULL &&
OtherEntry->SubordinateEntry == NULL ) {
//
// - this forest claims "acme.com"
// - the other forest also claims "acme.com"
//
ConflictEntry = OtherEntry;
LsapDsDebugOut(( DEB_FTINFO, "Entry %p is in conflict with entry %p (%s:%d)\n", ThisEntry, OtherEntry, __FILE__, __LINE__ ));
} else if ( ThisEntry->SubordinateEntry == NULL &&
OtherEntry->SubordinateEntry != NULL ) {
//
// - this forest claims "acme.com"
// - the other forest claims "foo.ny.acme.com"
// - this forest does not disown
// either "ny.acme.com" or "foo.ny.acme.com"
//
TLN_ENTRY * SubEntry;
for ( SubEntry = OtherEntry->SubordinateEntry;
SubEntry != NULL;
SubEntry = SubEntry->SubordinateEntry ) {
if ( ThisEntry->TdoEntry->Excludes( &SubEntry->TlnKey->TopLevelName )) {
break;
}
}
//
// SubEntry == NULL means the above loop has terminated
// without having found a matching exclusion record.
// Hence, this entry is in conflict
//
if ( SubEntry == NULL ) {
ConflictEntry = OtherEntry;
LsapDsDebugOut(( DEB_FTINFO, "Entry %p is in conflict with entry %p (%s:%d)\n", ThisEntry, OtherEntry, __FILE__, __LINE__ ));
}
} else if ( ThisEntry->SubordinateEntry != NULL &&
OtherEntry->SubordinateEntry == NULL ) {
//
// - this forest claims "foo.ny.acme.com"
// - the other forest claims "acme.com"
// - the other forest does not disown
// either "ny.acme.com" or "foo.ny.acme.com"
//
TLN_ENTRY * SubEntry;
for ( SubEntry = ThisEntry->SubordinateEntry;
SubEntry != NULL;
SubEntry = SubEntry->SubordinateEntry ) {
if ( OtherEntry->TdoEntry->Excludes( &SubEntry->TlnKey->TopLevelName )) {
break;
}
}
//
// SubEntry == NULL means the above loop has terminated
// without having found a matching exclusion record.
// Hence, this entry is in conflict
//
if ( SubEntry == NULL ) {
ConflictEntry = OtherEntry;
LsapDsDebugOut(( DEB_FTINFO, "Entry %p is in conflict with entry %p (%s:%d)\n", ThisEntry, OtherEntry, __FILE__, __LINE__ ));
}
}
if ( ConflictEntry != NULL &&
( ThisEntry->SubordinateEntry != NULL ||
OtherEntry->SubordinateEntry != NULL ) &&
( ThisEntry->TdoEntry->LocalForestEntry ||
OtherEntry->TdoEntry->LocalForestEntry )) {
//
// Non-equality conflicts where one of the sides is
// the local forest are allowed to stay, otherwise a
// forest named "bar.com" would not be able to establish
// trust with another forest named "foo.bar.com"
//
LsapDsDebugOut(( DEB_FTINFO, "Conflict between entry %p and entry %p has been cleared (%s:%d)\n", ThisEntry, OtherEntry, __FILE__, __LINE__ ));
ConflictEntry = NULL;
}
if ( ConflictEntry != NULL ) {
TLN_ENTRY * ConflictThis = ThisEntry;
TLN_ENTRY * ConflictOther = OtherEntry;
while ( ConflictThis->SubordinateEntry != NULL ) {
ConflictThis = ConflictThis->SubordinateEntry;
}
while ( ConflictOther->SubordinateEntry != NULL ) {
ConflictOther = ConflictOther->SubordinateEntry;
}
Status = AddConflictPair(
ConflictPairs,
ConflictPairsTotal,
ForestTrustTopLevelName,
ConflictThis,
LSA_TLN_DISABLED_CONFLICT,
ForestTrustTopLevelName,
ConflictOther,
LSA_TLN_DISABLED_CONFLICT
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "FTCache::Insert: AddConflictPair returned 0x%x\n", Status ));
goto Error;
}
LsapDsDebugOut(( DEB_FTINFO, "Entry %p (index %d) is in conflict with entry %p (%s:%d)\n", ConflictThis, ConflictThis->Index, ConflictOther, __FILE__, __LINE__ ));
}
}
}
//
// Rules for an enabled domain information record:
//
// * No part of the DNS name must be equal to that of an enabled
// top level name corresponding to a different TDO unless the other
// TDO explicitly disowns the DNS name of this record
// * No part must be equal to that of an enabled domain info entry
// corresponding to a different TDO
//
for ( ListEntry = TdoEntryNew->DomainInfoList.Flink;
ListEntry != &TdoEntryNew->DomainInfoList;
ListEntry = ListEntry->Flink ) {
DOMAIN_INFO_ENTRY * ThisEntry;
ThisEntry = DOMAIN_INFO_ENTRY::EntryFromTdoEntry( ListEntry );
if ( ThisEntry->SubordinateTo == NULL ) {
//
// Skip over store-and-ignore entries
//
ASSERT( ThisEntry->IsSidAdminDisabled());
continue;
}
if ( !ThisEntry->SubordinateTo->Enabled()) {
//
// Only consider enabled entries
//
continue;
}
if ( ThisEntry->TdoEntry->Excludes( &ThisEntry->DnsKey->DnsName )) {
//
// Entries excluded by this TDO are not considered either
//
continue;
}
//
// Check for SID conflicts
//
if ( ThisEntry->SidEnabled()) {
for ( LIST_ENTRY * ListEntrySid = ThisEntry->SidKey->List.Flink;
ListEntrySid != &ThisEntry->SidKey->List;
ListEntrySid = ListEntrySid->Flink ) {
DOMAIN_INFO_ENTRY * OtherEntry;
OtherEntry = DOMAIN_INFO_ENTRY::EntryFromSidEntry( ListEntrySid );
if ( OtherEntry->SubordinateTo == NULL ) {
//
// Skip over store-and-ignore entries
//
ASSERT( OtherEntry->IsSidAdminDisabled());
continue;
}
if ( OtherEntry->TdoEntry == TdoEntryNew ||
OtherEntry->TdoEntry == TdoEntryOldOut ) {
//
// Only interested in entries corresponding to other TDOs
//
continue;
}
if ( !OtherEntry->SidEnabled() ||
!OtherEntry->SubordinateTo->Enabled()) {
//
// Only interested in enabled entries
//
continue;
}
//
// Conflict! SID claimed by two forests!
//
Status = AddConflictPair(
ConflictPairs,
ConflictPairsTotal,
ForestTrustDomainInfo,
ThisEntry,
LSA_SID_DISABLED_CONFLICT,
ForestTrustDomainInfo,
OtherEntry,
LSA_SID_DISABLED_CONFLICT
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "FTCache::Insert: AddConflictPair returned 0x%x\n", Status ));
goto Error;
}
LsapDsDebugOut(( DEB_FTINFO, "FTCache::Insert: Entry %p (index %d) is in conflict with entry %p (%s:%d)\n", ThisEntry, ThisEntry->Index, OtherEntry, __FILE__, __LINE__ ));
}
//
// Cross-check DNS names with Netbios names
//
NETBIOS_NAME_KEY * NetbiosKey = ( NETBIOS_NAME_KEY * )RtlLookupElementGenericTableAvl(
&m_NetbiosNameTable,
&ThisEntry->DnsKey->DnsName
);
if ( NetbiosKey != NULL ) {
for ( LIST_ENTRY * ListEntryNB = NetbiosKey->List.Flink;
ListEntryNB != &NetbiosKey->List;
ListEntryNB = ListEntryNB->Flink ) {
DOMAIN_INFO_ENTRY * OtherEntry;
OtherEntry = DOMAIN_INFO_ENTRY::EntryFromNetbiosEntry( ListEntryNB );
if ( OtherEntry->SubordinateTo == NULL ) {
//
// Skip over store-and-ignore entries
//
ASSERT( OtherEntry->IsSidAdminDisabled());
continue;
}
if ( OtherEntry->TdoEntry == TdoEntryNew ||
OtherEntry->TdoEntry == TdoEntryOldOut ) {
//
// Only interested in entries corresponding to other TDOs
//
continue;
}
if ( !OtherEntry->NetbiosEnabled() ||
!OtherEntry->SubordinateTo->Enabled()) {
//
// Only interested in enabled entries
//
continue;
}
//
// Conflict! DNS name claimed by a Netbios name in another forest!
//
Status = AddConflictPair(
ConflictPairs,
ConflictPairsTotal,
ForestTrustDomainInfo,
ThisEntry,
LSA_SID_DISABLED_CONFLICT,
ForestTrustDomainInfo,
OtherEntry,
LSA_NB_DISABLED_CONFLICT
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "FTCache::Insert: AddConflictPair returned 0x%x\n", Status ));
goto Error;
}
LsapDsDebugOut(( DEB_FTINFO, "FTCache::Insert: Entry %p (index %d) is in conflict with entry %p (%s:%d)\n", ThisEntry, ThisEntry->Index, OtherEntry, __FILE__, __LINE__ ));
}
}
}
//
// Check for NETBIOS conflicts
//
if ( ThisEntry->NetbiosKey &&
ThisEntry->NetbiosEnabled()) {
for ( LIST_ENTRY * ListEntryNB = ThisEntry->NetbiosKey->List.Flink;
ListEntryNB != &ThisEntry->NetbiosKey->List;
ListEntryNB = ListEntryNB->Flink ) {
DOMAIN_INFO_ENTRY * OtherEntry;
OtherEntry = DOMAIN_INFO_ENTRY::EntryFromNetbiosEntry( ListEntryNB );
if ( OtherEntry->SubordinateTo == NULL ) {
//
// Skip over store-and-ignore entries
//
ASSERT( OtherEntry->IsSidAdminDisabled());
continue;
}
if ( OtherEntry->TdoEntry == TdoEntryNew ||
OtherEntry->TdoEntry == TdoEntryOldOut ) {
//
// Only interested in entries corresponding to other TDOs
//
continue;
}
if ( !OtherEntry->NetbiosEnabled() ||
OtherEntry->NetbiosKey == NULL ||
!OtherEntry->SubordinateTo->Enabled()) {
//
// Only interested in enabled entries
//
continue;
}
//
// Conflict! Netbios name claimed by two forests!
//
Status = AddConflictPair(
ConflictPairs,
ConflictPairsTotal,
ForestTrustDomainInfo,
ThisEntry,
LSA_NB_DISABLED_CONFLICT,
ForestTrustDomainInfo,
OtherEntry,
LSA_NB_DISABLED_CONFLICT
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "FTCache::Insert: AddConflictPair returned 0x%x\n", Status ));
goto Error;
}
LsapDsDebugOut(( DEB_FTINFO, "FTCache::Insert: Entry %p (index %d) is in conflict with entry %p (%s:%d)\n", ThisEntry, ThisEntry->Index, OtherEntry, __FILE__, __LINE__ ));
}
//
// Cross-check Netbios names with DNS names
//
DNS_NAME_KEY * DnsKey = ( DNS_NAME_KEY * )RtlLookupElementGenericTableAvl(
&m_DnsNameTable,
&ThisEntry->NetbiosKey->NetbiosName
);
if ( DnsKey != NULL ) {
for ( LIST_ENTRY * ListEntryDns = DnsKey->List.Flink;
ListEntryDns != &DnsKey->List;
ListEntryDns = ListEntryDns->Flink ) {
DOMAIN_INFO_ENTRY * OtherEntry;
OtherEntry = DOMAIN_INFO_ENTRY::EntryFromDnsEntry( ListEntryDns );
if ( OtherEntry->SubordinateTo == NULL ) {
//
// Skip over store-and-ignore entries
//
ASSERT( OtherEntry->IsSidAdminDisabled());
continue;
}
if ( OtherEntry->TdoEntry == TdoEntryNew ||
OtherEntry->TdoEntry == TdoEntryOldOut ) {
//
// Only interested in entries corresponding to other TDOs
//
continue;
}
if ( !OtherEntry->SidEnabled() ||
!OtherEntry->SubordinateTo->Enabled()) {
//
// Only interested in enabled entries
//
continue;
}
//
// Conflict! Netbios name claimed by a DNS name in another forest!
//
Status = AddConflictPair(
ConflictPairs,
ConflictPairsTotal,
ForestTrustDomainInfo,
ThisEntry,
LSA_NB_DISABLED_CONFLICT,
ForestTrustDomainInfo,
OtherEntry,
LSA_SID_DISABLED_CONFLICT
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "FTCache::Insert: AddConflictPair returned 0x%x\n", Status ));
goto Error;
}
LsapDsDebugOut(( DEB_FTINFO, "FTCache::Insert: Entry %p (index %d) is in conflict with entry %p (%s:%d)\n", ThisEntry, ThisEntry->Index, OtherEntry, __FILE__, __LINE__ ));
}
}
}
}
//
// At this time, ConflictPairs array contains a
// complete list of pairs of entries that conflicted
//
Status = STATUS_SUCCESS;
Cleanup:
*TdoEntryNewOut = TdoEntryNew;
LsapDsDebugOut(( DEB_FTINFO, "FTCache::Insert returning, Status: 0x%x\n", Status ));
return Status;
Error:
ASSERT( !NT_SUCCESS( Status ));
if ( TdoEntryNew ) {
RollbackChanges( TdoEntryNew, TdoEntryOldOut );
TdoEntryNew = NULL;
}
//
// Make sure no "old entry" nonsense is returned
// to the caller in case of error
//
ASSERT( TdoEntryOldOut->RecordCount == 0 );
if ( *ConflictPairs != NULL ) {
FtcFree( *ConflictPairs );
*ConflictPairs = NULL;
*ConflictPairsTotal = 0;
}
goto Cleanup;
}
NTSTATUS
FTCache::Remove(
IN UNICODE_STRING * TrustedDomainName
)
/*++
Routine Description:
Removes all entires corresponding to the given trusted domain information
name from the forest trust cache
Arguments:
TrustedDomainName name of the TDO to be wiped from the cache
Returns:
STATUS_SUCCESS entries corresponding to the given TDO have been
successfully removed
STATUS_NOT_FOUND there was no entry by this name in the cache
STATUS_INVALID_PARAMETER name of the TDO was invalid
--*/
{
NTSTATUS Status;
TDO_ENTRY * TdoEntry = NULL;
ASSERT( m_Initialized );
if ( TrustedDomainName == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in FTCache::Remove\n" ));
return STATUS_INVALID_PARAMETER;
}
LsapDsDebugOut(( DEB_FTINFO, "FTCache::Remove called, TDO: %wZ\n", TrustedDomainName ));
//
// Locate the cache entry corresponding to this TDO
//
TdoEntry = ( TDO_ENTRY * )RtlLookupElementGenericTableAvl(
&m_TdoTable,
TrustedDomainName
);
if ( TdoEntry == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "FTCache::Remove could not locate an entry for %wZ\n", TrustedDomainName ));
Status = STATUS_NOT_FOUND;
goto Error;
} else if ( TdoEntry->LocalForestEntry ) {
//
// The information about the local forest can not be removed,
// it can only be replaced.
//
// The entire cache must be invalidated in order to get rid of it.
//
LsapDsDebugOut(( DEB_FTINFO, "FTCache::Remove not removing local entry %wZ\n", TrustedDomainName ));
} else {
RemoveTdoEntry( TdoEntry );
LsapDsDebugOut(( DEB_FTINFO, "FTCache::Remove successfully removed entry %wZ\n", TrustedDomainName ));
}
Status = STATUS_SUCCESS;
Cleanup:
LsapDsDebugOut(( DEB_FTINFO, "FTCache::Remove returning, Status: 0x%x\n", Status ));
return Status;
Error:
ASSERT( !NT_SUCCESS( Status ));
goto Cleanup;
}
NTSTATUS
FTCache::Retrieve(
IN UNICODE_STRING * TrustedDomainName,
OUT LSA_FOREST_TRUST_INFORMATION * * ForestTrustInfo
)
/*++
Routine Description:
Retrieves forest trust information corresponding to a given TDO name
from the forest trust cache
Arguments:
TrustedDomainName name of the TDO to be queried
ForestTrustInfo used to return forest trust information data
for that TDO
Returns:
STATUS_SUCCESS entries corresponding to the given TDO have been
successfully returned
STATUS_NOT_FOUND this TDO is not in the cache
STATUS_INSUFFICIENT_RESOURCES out of memory
STATUS_INVALID_PARAMETER name of the TDO was invalid
STATUS_INTERNAL_ERROR the cache is in an inconsistent state
--*/
{
NTSTATUS Status;
LIST_ENTRY * ListEntry;
TDO_ENTRY * TdoEntry = NULL;
if ( TrustedDomainName == NULL ||
ForestTrustInfo == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in FTCache::Retrieve (%s:%d)\n", __FILE__, __LINE__ ));
return STATUS_INVALID_PARAMETER;
}
LsapDsDebugOut(( DEB_FTINFO, "FTCache::Retrieve called, TDO: %wZ\n", TrustedDomainName ));
*ForestTrustInfo = NULL;
//
// If the cache is not valid, do not even bother to proceed
//
if ( !IsExternalValid()) {
LsapDsDebugOut(( DEB_FTINFO, "FTCache::Retrieve called on an invalid cache\n" ));
Status = STATUS_INTERNAL_ERROR;
goto Error;
}
//
// Locate the cache entry corresponding to this TDO
//
TdoEntry = ( TDO_ENTRY * )RtlLookupElementGenericTableAvl(
&m_TdoTable,
TrustedDomainName
);
if ( TdoEntry == NULL ) {
Status = STATUS_NOT_FOUND;
goto Error;
} else if ( TdoEntry->LocalForestEntry ) {
//
// The caller should ensure that local forest info is not retrieved
//
LsapDsDebugOut(( DEB_FTINFO, "FTCache::Retrieve called asking for local trust information\n" ));
Status = STATUS_NOT_FOUND;
ASSERT( FALSE );
goto Error;
}
*ForestTrustInfo = ( LSA_FOREST_TRUST_INFORMATION * )FtcAllocate(
sizeof( LSA_FOREST_TRUST_INFORMATION )
);
if ( *ForestTrustInfo == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::Retrieve (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
(*ForestTrustInfo)->RecordCount = 0;
(*ForestTrustInfo)->Entries = ( LSA_FOREST_TRUST_RECORD * * )FtcAllocate(
TdoEntry->RecordCount * sizeof( LSA_FOREST_TRUST_RECORD * )
);
if ( (*ForestTrustInfo)->Entries == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::Retrieve (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
//
// Read top level name entries for this TDO
//
for ( ListEntry = TdoEntry->TlnList.Flink;
ListEntry != &TdoEntry->TlnList;
ListEntry = ListEntry->Flink ) {
TLN_ENTRY * TlnEntry;
LSA_FOREST_TRUST_RECORD * Record;
TlnEntry = TLN_ENTRY::EntryFromTdoEntry( ListEntry );
if ( TlnEntry->Excluded ||
TlnEntry->SubordinateEntry == NULL ) {
Record = RecordFromTopLevelNameEntry( TlnEntry );
if ( Record == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::Retrieve (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
ASSERT( (*ForestTrustInfo)->RecordCount < TdoEntry->RecordCount );
(*ForestTrustInfo)->Entries[(*ForestTrustInfo)->RecordCount] = Record;
(*ForestTrustInfo)->RecordCount += 1;
}
}
//
// Read domain info entries for this TDO
//
for ( ListEntry = TdoEntry->DomainInfoList.Flink;
ListEntry != &TdoEntry->DomainInfoList;
ListEntry = ListEntry->Flink ) {
DOMAIN_INFO_ENTRY * DomainInfoEntry;
LSA_FOREST_TRUST_RECORD * Record;
DomainInfoEntry = DOMAIN_INFO_ENTRY::EntryFromTdoEntry( ListEntry );
Record = RecordFromDomainInfoEntry( DomainInfoEntry );
if ( Record == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::Retrieve (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
ASSERT( (*ForestTrustInfo)->RecordCount < TdoEntry->RecordCount );
(*ForestTrustInfo)->Entries[(*ForestTrustInfo)->RecordCount] = Record;
(*ForestTrustInfo)->RecordCount += 1;
}
//
// Read binary entries for this TDO
//
for ( ListEntry = TdoEntry->BinaryList.Flink;
ListEntry != &TdoEntry->BinaryList;
ListEntry = ListEntry->Flink ) {
BINARY_ENTRY * BinaryEntry;
LSA_FOREST_TRUST_RECORD * Record;
BinaryEntry = BINARY_ENTRY::EntryFromTdoEntry( ListEntry );
Record = RecordFromBinaryEntry( BinaryEntry );
if ( Record == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::Retrieve (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
ASSERT( (*ForestTrustInfo)->RecordCount < TdoEntry->RecordCount );
(*ForestTrustInfo)->Entries[(*ForestTrustInfo)->RecordCount] = Record;
(*ForestTrustInfo)->RecordCount += 1;
}
ASSERT( (*ForestTrustInfo)->RecordCount == TdoEntry->RecordCount );
Status = STATUS_SUCCESS;
Cleanup:
LsapDsDebugOut(( DEB_FTINFO, "FTCache::Retrieve returning, Status: 0x%x\n", Status ));
return Status;
Error:
ASSERT( !NT_SUCCESS( Status ));
LsapFreeForestTrustInfo( *ForestTrustInfo );
FtcFree( *ForestTrustInfo );
*ForestTrustInfo = NULL;
goto Cleanup;
}
NTSTATUS
FTCache::Match(
IN LSA_ROUTING_MATCH_TYPE Type,
IN PVOID Data,
IN BOOLEAN SearchLocal,
OUT OPTIONAL UNICODE_STRING * TrustedDomainName,
OUT OPTIONAL PSID * TrustedDomainSid
)
/*++
Routine Description:
Finds match for given data in the cache
Arguments:
Type Type of Data parameter
Data Data to match
SearchLocal Search the local information only
TrustedDomainName Used to return the match, if one was found.
Caller must use LsaIFree_LSAPR_UNICODE_STRING_BUFFER
TrustedDomainSid Used to return the match, if one was found.
Caller must use MIDL_user_free
Returns:
STATUS_SUCCESS Match was found
STATUS_NO_MATCH Match was not found
STATUS_INVALID_PARAMETER Check the inputs
STATUS_INTERNAL_ERROR Cache is internally inconsistent
STATUS_INSUFFICIENT_RESOURCES Out of memory
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
SID * Sid = NULL;
UNICODE_STRING * String = NULL;
BOOLEAN IsLocal = !SearchLocal; // Equivalent to "no match by default"
LsapDsDebugOut(( DEB_FTINFO, "FTCache::Match called\n" ));
if ( Data == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in FTCache::Match (%s:%d)\n", __FILE__, __LINE__ ));
return STATUS_INVALID_PARAMETER;
}
if ( TrustedDomainName ) {
RtlZeroMemory( TrustedDomainName, sizeof( UNICODE_STRING ));
}
if ( TrustedDomainSid ) {
*TrustedDomainSid = NULL;
}
switch ( Type ) {
case RoutingMatchDomainSid:
Sid = ( SID * )Data;
if ( !RtlValidSid( Sid )) {
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in FTCache::Match (%s:%d)\n", __FILE__, __LINE__ ));
return STATUS_INVALID_PARAMETER;
}
break;
case RoutingMatchDomainName:
case RoutingMatchUpn:
case RoutingMatchSpn:
case RoutingMatchNamespace:
String = ( UNICODE_STRING * )Data;
if ( !LsapValidateLsaUnicodeString( String )) {
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in FTCache::Match (%s:%d)\n", __FILE__, __LINE__ ));
return STATUS_INVALID_PARAMETER;
}
//
// ISSUE-2000/07/24-markpu
// is it appopriate to modify UPNs and SPNs prior to matching?
//
LsapRemoveTrailingDot( String, FALSE );
break;
default:
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in FTCache::Match (%s:%d)\n", __FILE__, __LINE__ ));
return STATUS_INVALID_PARAMETER;
}
LsapDbAcquireReadLockTrustedDomainList();
if ( SearchLocal && !IsLocalValid()) {
//
// Searching a local cache and local information is invalid -- rebuild
//
LsapDbConvertReadLockTrustedDomainListToExclusive();
Status = LsapForestTrustInsertLocalInfo();
LsapDbConvertWriteLockTrustedDomainListToShared();
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "FTCache::Match: attempt to rebuild the cache failed with 0x%x\n", Status ));
goto Error;
} else {
LsapDsDebugOut(( DEB_FTINFO, "FTCache::Match: cache rebuilt successfully\n" ));
}
}
if ( !SearchLocal && !IsExternalValid()) {
//
// Searching for non-local information
// Rebuilding code on a GC outside of a root domain differs
// from what is done on every DC in the root domain
//
if ( SamIAmIGC() && !LsapDbDcInRootDomain()) {
LsapDbConvertReadLockTrustedDomainListToExclusive();
LsapForestTrustCacheSetInvalid();
//
// DCs in the root domain must not allow other forests to claim SIDs
// and namespaces that conflict with their own forest
// For that, the information about the current forest is inserted
// into the forest trust cache as just another (though special) entry
//
Status = LsapForestTrustInsertLocalInfo();
if ( NT_SUCCESS( Status )) {
Status = LsapRebuildFtCacheGC();
}
LsapDbConvertWriteLockTrustedDomainListToShared();
} else {
LsapDbConvertReadLockTrustedDomainListToExclusive();
Status = LsapDbBuildTrustedDomainCache();
LsapDbConvertWriteLockTrustedDomainListToShared();
}
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "FTCache::Match: attempt to rebuild the cache failed with 0x%x\n", Status ));
goto Error;
} else {
LsapDsDebugOut(( DEB_FTINFO, "FTCache::Match: cache rebuilt successfully\n" ));
}
}
//
// The information we're looking for better be valid
//
ASSERT(( SearchLocal && IsLocalValid()) || ( !SearchLocal && IsExternalValid()));
//
// Guard against stack overflows and such
//
__try {
switch ( Type ) {
case RoutingMatchDomainSid:
Status = MatchSid( Sid, &IsLocal, TrustedDomainName, TrustedDomainSid );
break;
case RoutingMatchDomainName:
Status = MatchDnsName( String, &IsLocal, TrustedDomainName, TrustedDomainSid );
if ( Status == STATUS_NO_MATCH ) {
Status = MatchNetbiosName( String, &IsLocal, TrustedDomainName, TrustedDomainSid );
}
break;
case RoutingMatchUpn:
Status = MatchUpn( String, &IsLocal, TrustedDomainName, TrustedDomainSid );
break;
case RoutingMatchSpn:
Status = MatchSpn( String, &IsLocal, TrustedDomainName, TrustedDomainSid );
break;
case RoutingMatchNamespace:
Status = MatchNamespace( String, &IsLocal, TrustedDomainName, TrustedDomainSid );
break;
default:
ASSERT( FALSE );
}
} __except ( EXCEPTION_EXECUTE_HANDLER ) {
Status = GetExceptionCode();
goto Error;
}
//
// If we were looking for a local match and found a non-local match instead,
// that's the same as if no match was found.
//
if ( NT_SUCCESS( Status ) &&
(( IsLocal == FALSE ) != ( SearchLocal == FALSE ))) {
Status = STATUS_NO_MATCH;
if ( TrustedDomainName != NULL ) {
LsaIFree_LSAPR_UNICODE_STRING_BUFFER(
( PLSAPR_UNICODE_STRING ) TrustedDomainName
);
RtlZeroMemory( TrustedDomainName, sizeof( UNICODE_STRING ));
}
if ( TrustedDomainSid != NULL ) {
MIDL_user_free( *TrustedDomainSid );
*TrustedDomainSid = NULL;
}
goto Error;
}
Cleanup:
LsapDbReleaseLockTrustedDomainList();
LsapDsDebugOut(( DEB_FTINFO, "FTCache::Match returning, Status: 0x%lx\n", Status ));
return Status;
Error:
ASSERT( !NT_SUCCESS( Status ));
goto Cleanup;
}
///////////////////////////////////////////////////////////////////////////////
//
// FTCache private methods
//
///////////////////////////////////////////////////////////////////////////////
void
FTCache::Purge()
/*++
Routine Description:
Removes the contents of the cache
Arguments:
None
Returns:
Nothing
--*/
{
TDO_ENTRY * TdoEntry;
LsapDsDebugOut(( DEB_FTINFO, "FTCache::Purge called\n" ));
for ( TdoEntry = ( TDO_ENTRY * )RtlEnumerateGenericTableAvl( &m_TdoTable, TRUE );
TdoEntry != NULL;
TdoEntry = ( TDO_ENTRY * )RtlEnumerateGenericTableAvl( &m_TdoTable, TRUE )) {
RemoveTdoEntry( TdoEntry );
}
LsapDsDebugOut(( DEB_FTINFO, "FTCache::Purge returning\n" ));
}
void
FTCache::RollbackChanges(
IN TDO_ENTRY * TdoEntryNew,
IN TDO_ENTRY * TdoEntryOld
)
/*++
Routine Description:
Arguments:
TdoEntryNew result of inserting new data into the cache
TdoEntryOld old contents of the cache
(Record count of 0 indicates no previous contents)
Returns:
Nothing
--*/
{
ASSERT( TdoEntryNew );
ASSERT( TdoEntryOld );
if ( TdoEntryOld->RecordCount > 0 ) {
//
// Reinstate old contents of the entry
// if the old entry is non-empty
//
PurgeTdoEntry( TdoEntryNew );
CopyTdoEntry( TdoEntryNew, TdoEntryOld );
} else {
//
// Altogether remove the node from the tree
// if there is nothing to reinstate
//
RemoveTdoEntry( TdoEntryNew );
}
//
// We've taken over the old entry.
// Leave it in an invalid state.
//
RtlZeroMemory( TdoEntryOld, sizeof( TDO_ENTRY ));
}
void
FTCache::PurgeTdoEntry(
IN TDO_ENTRY * TdoEntry
)
/*++
Routine Description:
Cleans up all entries associated with a given TDO entry
but leaves the other fields alone
Arguments:
TdoEntry entry to purge
Returns:
Nothing
--*/
{
//
// Remove TDO entries from the top level name table
//
while ( !IsListEmpty( &TdoEntry->TlnList )) {
TLN_ENTRY * TlnEntry;
TlnEntry = TLN_ENTRY::EntryFromTdoEntry( TdoEntry->TlnList.Flink );
RemoveEntryList( &TlnEntry->TdoListEntry );
RemoveEntryList( &TlnEntry->AvlListEntry );
ASSERT( TlnEntry->TlnKey );
TlnEntry->TlnKey->Count -= 1;
//
// If this is the last entry for this top level name in the tree,
// remove the associated key from the list
//
if ( IsListEmpty( &TlnEntry->TlnKey->List )) {
BOOLEAN Found;
ASSERT( TlnEntry->TlnKey->Count == 0 );
Found = RtlDeleteElementGenericTableAvl(
&m_TopLevelNameTable,
&TlnEntry->TlnKey->TopLevelName
);
ASSERT( sm_TlnKeys-- > 0 );
ASSERT( Found ); // better be there!!!
}
FtcFree( TlnEntry );
ASSERT( sm_TlnEntries-- > 0 );
}
//
// Remove TDO entries from the DomainInfo-related tables
//
while ( !IsListEmpty( &TdoEntry->DomainInfoList )) {
DOMAIN_INFO_ENTRY * DomainInfoEntry;
DomainInfoEntry = DOMAIN_INFO_ENTRY::EntryFromTdoEntry( TdoEntry->DomainInfoList.Flink );
RemoveEntryList( &DomainInfoEntry->TdoListEntry );
RemoveEntryList( &DomainInfoEntry->SidAvlListEntry );
RemoveEntryList( &DomainInfoEntry->DnsAvlListEntry );
RemoveEntryList( &DomainInfoEntry->NetbiosAvlListEntry );
ASSERT( DomainInfoEntry->SidKey );
DomainInfoEntry->SidKey->Count -= 1;
//
// If this is the last entry for the domain SID in the tree,
// remove the associated key from the list
//
if ( IsListEmpty( &DomainInfoEntry->SidKey->List )) {
BOOLEAN Found;
ASSERT( DomainInfoEntry->SidKey->Count == 0 );
Found = RtlDeleteElementGenericTableAvl(
&m_DomainSidTable,
&DomainInfoEntry->SidKey->DomainSid
);
ASSERT( sm_SidKeys-- > 0 );
ASSERT( Found ); // better be there!
}
ASSERT( DomainInfoEntry->DnsKey );
DomainInfoEntry->DnsKey->Count -= 1;
//
// If this is the last entry for the DNS name in the tree,
// remove the associated key from the list
//
if ( IsListEmpty( &DomainInfoEntry->DnsKey->List )) {
BOOLEAN Found;
ASSERT( DomainInfoEntry->DnsKey->Count == 0 );
Found = RtlDeleteElementGenericTableAvl(
&m_DnsNameTable,
&DomainInfoEntry->DnsKey->DnsName
);
ASSERT( sm_DnsNameKeys-- > 0 );
ASSERT( Found ); // better be there!
}
if ( DomainInfoEntry->NetbiosKey != NULL ) {
DomainInfoEntry->NetbiosKey->Count -= 1;
//
// If this is the last entry for the DNS name in the tree,
// remove the associated key from the list
//
if ( IsListEmpty( &DomainInfoEntry->NetbiosKey->List )) {
BOOLEAN Found;
ASSERT( DomainInfoEntry->NetbiosKey->Count == 0 );
Found = RtlDeleteElementGenericTableAvl(
&m_NetbiosNameTable,
&DomainInfoEntry->NetbiosKey->NetbiosName
);
ASSERT( sm_NetbiosNameKeys-- > 0 );
ASSERT( Found ); // better be there!
}
}
FtcFree( DomainInfoEntry->Sid );
FtcFree( DomainInfoEntry );
ASSERT( sm_DomainInfoEntries-- > 0 );
}
//
// Remove binary entries for this TDO
//
while ( !IsListEmpty( &TdoEntry->BinaryList )) {
BINARY_ENTRY * BinaryEntry;
BinaryEntry = BINARY_ENTRY::EntryFromTdoEntry( TdoEntry->BinaryList.Flink );
RemoveEntryList( &BinaryEntry->TdoListEntry );
FtcFree( BinaryEntry->Data.Buffer );
FtcFree( BinaryEntry );
ASSERT( sm_BinaryEntries-- > 0 );
}
//
// This entry no longer contains anything of interest
//
TdoEntry->RecordCount = 0;
FtcFree( TdoEntry->TrustedDomainSid );
TdoEntry->TrustedDomainSid = NULL;
ASSERT( IsListEmpty( &TdoEntry->TlnList ));
ASSERT( IsListEmpty( &TdoEntry->DomainInfoList ));
ASSERT( IsListEmpty( &TdoEntry->BinaryList ));
}
void
FTCache::RemoveTdoEntry(
IN TDO_ENTRY * TdoEntry
)
/*++
Routine Description:
Removes the corresponding TdoEntry and all its associated data from the cache
Arguments:
TdoEntry Entry to remove
Returns:
Nothing
--*/
{
BOOLEAN Found;
ASSERT( TdoEntry );
LsapDsDebugOut(( DEB_FTINFO, "FTCache::RemoveTdoEntry called, TdoEntry is %p\n", TdoEntry ));
PurgeTdoEntry( TdoEntry );
//
// Remove the cache entry from the list and free it
//
Found = RtlDeleteElementGenericTableAvl(
&m_TdoTable,
&TdoEntry->TrustedDomainName
);
ASSERT( sm_TdoEntries-- > 0 );
ASSERT( Found );
LsapDsDebugOut(( DEB_FTINFO, "FTCache::RemoveTdoEntry returning\n" ));
}
void
FTCache::CopyTdoEntry(
IN TDO_ENTRY * Destination,
IN TDO_ENTRY * Source
)
/*++
Routine Description:
Creates a temporary copy of a TDO entry, the copy is used in rollback code.
No new memory is allocated, the data is merely stored away for a while.
Arguments:
Destination Entry to fill in
Source Entry to get the data from
Returns:
Nothing
--*/
{
LIST_ENTRY * ListEntry;
*Destination = *Source;
//
// Make the list entries point to the backup copy of the TDO entry
// (we're about to reuse the contents of *Source)
//
if ( IsListEmpty( &Source->TlnList )) {
InitializeListHead( &Destination->TlnList );
} else {
Destination->TlnList.Flink->Blink = &Destination->TlnList;
Destination->TlnList.Blink->Flink = &Destination->TlnList;
}
if ( IsListEmpty( &Source->DomainInfoList )) {
InitializeListHead( &Destination->DomainInfoList );
} else {
Destination->DomainInfoList.Flink->Blink = &Destination->DomainInfoList;
Destination->DomainInfoList.Blink->Flink = &Destination->DomainInfoList;
}
if ( IsListEmpty( &Source->BinaryList )) {
InitializeListHead( &Destination->BinaryList );
} else {
Destination->BinaryList.Flink->Blink = &Destination->BinaryList;
Destination->BinaryList.Blink->Flink = &Destination->BinaryList;
}
//
// Point all the "TdoEntry" member variables of all components to
// Destination so there is no confusion when testing for duplicates
//
for ( ListEntry = Destination->TlnList.Flink;
ListEntry != &Destination->TlnList;
ListEntry = ListEntry->Flink ) {
TLN_ENTRY * TlnEntry;
TlnEntry = TLN_ENTRY::EntryFromTdoEntry( ListEntry );
TlnEntry->TdoEntry = Destination;
}
for ( ListEntry = Destination->DomainInfoList.Flink;
ListEntry != &Destination->DomainInfoList;
ListEntry = ListEntry->Flink ) {
DOMAIN_INFO_ENTRY * DomainInfoEntry;
DomainInfoEntry = DOMAIN_INFO_ENTRY::EntryFromTdoEntry( ListEntry );
DomainInfoEntry->TdoEntry = Destination;
}
}
LSA_FOREST_TRUST_RECORD *
FTCache::RecordFromTopLevelNameEntry(
IN TLN_ENTRY * Entry
)
/*++
Routine Description:
Creates a forest trust record structure from a top level name entry
Arguments:
Entry top level name entry to convert
Returns:
Allocated top level name record or NULL if out of memory
--*/
{
LSA_FOREST_TRUST_RECORD * Record;
ASSERT( Entry->Excluded ||
Entry->SubordinateEntry == NULL );
Record = ( LSA_FOREST_TRUST_RECORD * )FtcAllocate(
sizeof( LSA_FOREST_TRUST_RECORD )
);
if ( Record == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::RecordFromTopLevelEntry (%s:%d)\n", __FILE__, __LINE__ ));
goto Error;
}
Record->Time = Entry->Time;
Record->Flags = Entry->Flags();
Record->ForestTrustType = Entry->Excluded ?
ForestTrustTopLevelNameEx :
ForestTrustTopLevelName;
if ( FALSE == FtcCopyUnicodeString(
&Record->ForestTrustData.TopLevelName,
&Entry->TlnKey->TopLevelName )) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::RecordFromTopLevelEntry (%s:%d)\n", __FILE__, __LINE__ ));
goto Error;
}
Cleanup:
return Record;
Error:
FtcFree( Record );
Record = NULL;
goto Cleanup;
}
LSA_FOREST_TRUST_RECORD *
FTCache::RecordFromDomainInfoEntry(
IN DOMAIN_INFO_ENTRY * Entry
)
/*++
Routine Description:
Creates a forest trust record structure from a domain info entry
Arguments:
Entry domain info entry to convert
Returns:
Allocated domain info record or NULL if out of memory
--*/
{
LSA_FOREST_TRUST_RECORD * Record;
ULONG SidLength;
Record = ( LSA_FOREST_TRUST_RECORD * )FtcAllocate(
sizeof( LSA_FOREST_TRUST_RECORD )
);
if ( Record == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::RecordFromDomainInfoEntry (%s:%d)\n", __FILE__, __LINE__ ));
goto Error;
}
ASSERT( RtlValidSid( Entry->Sid ));
SidLength = RtlLengthSid( Entry->Sid );
ASSERT( SidLength > 0 );
Record->Time = Entry->Time;
Record->Flags = Entry->Flags();
Record->ForestTrustType = ForestTrustDomainInfo;
Record->ForestTrustData.DomainInfo.Sid = ( SID * )FtcAllocate( SidLength );
if ( Record->ForestTrustData.DomainInfo.Sid == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::RecordFromDomainInfoEntry (%s:%d)\n", __FILE__, __LINE__ ));
goto Error;
}
RtlCopySid( SidLength, ( SID * )Record->ForestTrustData.DomainInfo.Sid, Entry->Sid );
if ( FALSE == FtcCopyUnicodeString(
( UNICODE_STRING * )&Record->ForestTrustData.DomainInfo.DnsName,
&Entry->DnsKey->DnsName )) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::RecordFromDomainInfoEntry (%s:%d)\n", __FILE__, __LINE__ ));
FtcFree( Record->ForestTrustData.DomainInfo.Sid );
goto Error;
}
if ( Entry->NetbiosKey != NULL ) {
if ( FALSE == FtcCopyUnicodeString(
( UNICODE_STRING * )&Record->ForestTrustData.DomainInfo.NetbiosName,
&Entry->NetbiosKey->NetbiosName )) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::RecordFromDomainInfoEntry (%s:%d)\n", __FILE__, __LINE__ ));
FtcFree( Record->ForestTrustData.DomainInfo.DnsName.Buffer );
FtcFree( Record->ForestTrustData.DomainInfo.Sid );
goto Error;
}
} else {
Record->ForestTrustData.DomainInfo.NetbiosName.Buffer = NULL;
Record->ForestTrustData.DomainInfo.NetbiosName.Length = 0;
Record->ForestTrustData.DomainInfo.NetbiosName.MaximumLength = 0;
}
Cleanup:
return Record;
Error:
FtcFree( Record );
Record = NULL;
goto Cleanup;
}
LSA_FOREST_TRUST_RECORD *
FTCache::RecordFromBinaryEntry(
IN BINARY_ENTRY * Entry
)
/*++
Routine Description:
Creates a forest trust record structure from a binary entry
Arguments:
Entry binary entry to convert
Returns:
Allocated binary record or NULL if out of memory
--*/
{
LSA_FOREST_TRUST_RECORD * Record;
Record = ( LSA_FOREST_TRUST_RECORD * )FtcAllocate(
sizeof( LSA_FOREST_TRUST_RECORD )
);
if ( Record == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::RecordFromBinaryEntry (%s:%d)\n", __FILE__, __LINE__ ));
goto Error;
}
Record->Time = Entry->Time;
Record->Flags = Entry->Flags();
Record->ForestTrustType = Entry->Type;
Record->ForestTrustData.Data.Length = Entry->Data.Length;
if ( Entry->Data.Length > 0 ) {
Record->ForestTrustData.Data.Buffer = ( BYTE * )FtcAllocate( Entry->Data.Length );
if ( Record->ForestTrustData.Data.Buffer == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::RecordFromBinaryEntry (%s:%d)\n", __FILE__, __LINE__ ));
goto Error;
}
RtlCopyMemory(
Record->ForestTrustData.Data.Buffer,
Entry->Data.Buffer,
Entry->Data.Length
);
} else {
Record->ForestTrustData.Data.Buffer = NULL;
}
Cleanup:
return Record;
Error:
FtcFree( Record );
Record = NULL;
goto Cleanup;
}
NTSTATUS
FTCache::MatchSid(
IN SID * Sid,
OUT BOOLEAN * IsLocal,
OUT OPTIONAL UNICODE_STRING * TrustedDomainName,
OUT OPTIONAL PSID * TrustedDomainSid
)
/*++
Routine Description:
Locates a match for the given SID in the cache
Arguments:
Sid SID to match
IsLocal Indicates whether the result is a local match
TrustedDomainName Used to return the name of the trusted domain
TrustedDomainSid Used to return the SID of the trusted domain
Returns:
STATUS_SUCCESS Match was found
STATUS_NO_MATCH Match was not found
STATUS_INSUFFICIENT_RESOURCES Out of memory
STATUS_INVALID_PARAMETER Sid passed in not valid
STATUS_INVALID_SID Sid passed in not a valid account SID
STATUS_BUFFER_OVERFLOW Sid too long
--*/
{
NTSTATUS Status;
DOMAIN_SID_KEY * SidKey;
UNICODE_STRING * MatchName = NULL;
PSID MatchSid = NULL;
DWORD SidBuffer[256];
DWORD cbSid = sizeof( SidBuffer );
PSID DomainSid = ( PSID )SidBuffer;
//
// Caller must validate parameters
//
ASSERT( Sid );
ASSERT( RtlValidSid( Sid ));
if ( FALSE == GetWindowsAccountDomainSid(
Sid,
SidBuffer,
&cbSid )) {
DWORD ErrorCode = GetLastError();
switch( ErrorCode ) {
case ERROR_INVALID_SID:
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in FTCache::MatchSid (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
break;
case ERROR_INVALID_PARAMETER:
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in FTCache::MatchSid (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
break;
case ERROR_NON_ACCOUNT_SID:
Status = STATUS_INVALID_SID;
break;
case ERROR_INSUFFICIENT_BUFFER:
Status = STATUS_BUFFER_OVERFLOW;
break;
default:
ASSERT( FALSE ); // map the error code
Status = ErrorCode;
break;
}
goto Error;
}
SidKey = ( DOMAIN_SID_KEY * )RtlLookupElementGenericTableAvl(
&m_DomainSidTable,
&DomainSid
);
if ( SidKey == NULL ) {
Status = STATUS_NO_MATCH;
goto Error;
}
for ( LIST_ENTRY * ListEntry = SidKey->List.Flink;
ListEntry != &SidKey->List;
ListEntry = ListEntry->Flink ) {
DOMAIN_INFO_ENTRY * DomainInfoEntry;
DomainInfoEntry = DOMAIN_INFO_ENTRY::EntryFromSidEntry( ListEntry );
if ( DomainInfoEntry->SubordinateTo == NULL ) {
//
// Skip over store-and-ignore entries
//
ASSERT( DomainInfoEntry->IsSidAdminDisabled());
continue;
}
//
// Conditions for an entry to be considered a match:
//
// - Entry must be enabled
// - corresponding top-level entry must be enabled
// - this entry must not be disowned by its TDO
//
if ( DomainInfoEntry->SidEnabled() &&
DomainInfoEntry->SubordinateTo->Enabled() &&
!DomainInfoEntry->TdoEntry->Excludes( &DomainInfoEntry->DnsKey->DnsName )) {
MatchName = &DomainInfoEntry->TdoEntry->TrustedDomainName;
MatchSid = DomainInfoEntry->TdoEntry->TrustedDomainSid;
*IsLocal = DomainInfoEntry->TdoEntry->LocalForestEntry;
break;
}
}
if ( MatchName == NULL || MatchSid == NULL ) {
Status = STATUS_NO_MATCH;
goto Error;
} else {
if ( TrustedDomainName &&
!FtcCopyUnicodeString( TrustedDomainName, MatchName )) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::MatchSid (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
if ( TrustedDomainSid &&
!FtcCopySid( TrustedDomainSid, MatchSid )) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::MatchSid (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
}
Status = STATUS_SUCCESS;
Cleanup:
return Status;
Error:
ASSERT( !NT_SUCCESS( Status ));
if ( TrustedDomainName ) {
FtcFree( TrustedDomainName->Buffer );
RtlZeroMemory( TrustedDomainName, sizeof( UNICODE_STRING ));
}
if ( TrustedDomainSid ) {
FtcFree( *TrustedDomainSid );
*TrustedDomainSid = NULL;
}
goto Cleanup;
}
NTSTATUS
FTCache::MatchDnsName(
IN UNICODE_STRING * Name,
OUT BOOLEAN * IsLocal,
OUT OPTIONAL UNICODE_STRING * TrustedDomainName,
OUT OPTIONAL PSID * TrustedDomainSid
)
/*++
Routine Description:
Locates a match for the given DNS in the cache
Arguments:
Name DNS name to match
IsLocal Indicates whether the result is a local match
TrustedDomainName Used to return the name of the trusted domain
TrustedDomainSid Used to return the SID of the trusted domain
Returns:
STATUS_SUCCESS Match was found
STATUS_NO_MATCH Match was not found
STATUS_INSUFFICIENT_RESOURCES Out of memory
--*/
{
NTSTATUS Status;
DNS_NAME_KEY * DnsKey;
UNICODE_STRING * MatchName = NULL;
PSID MatchSid = NULL;
//
// Caller must validate parameters
//
ASSERT( Name );
ASSERT( LsapValidateLsaUnicodeString( Name ));
DnsKey = ( DNS_NAME_KEY * )RtlLookupElementGenericTableAvl(
&m_DnsNameTable,
Name
);
if ( DnsKey == NULL ) {
Status = STATUS_NO_MATCH;
goto Error;
}
for ( LIST_ENTRY * ListEntry = DnsKey->List.Flink;
ListEntry != &DnsKey->List;
ListEntry = ListEntry->Flink ) {
DOMAIN_INFO_ENTRY * DomainInfoEntry;
DomainInfoEntry = DOMAIN_INFO_ENTRY::EntryFromDnsEntry( ListEntry );
if ( DomainInfoEntry->SubordinateTo == NULL ) {
//
// Skip over store-and-ignore entries
//
ASSERT( DomainInfoEntry->IsSidAdminDisabled());
continue;
}
//
// Conditions for an entry to be considered a match:
//
// - Entry must be enabled
// - corresponding top-level entry must be enabled
// - this entry must not be disowned by its TDO
//
if ( DomainInfoEntry->SidEnabled() &&
DomainInfoEntry->SubordinateTo->Enabled() &&
!DomainInfoEntry->TdoEntry->Excludes( &DomainInfoEntry->DnsKey->DnsName )) {
MatchName = &DomainInfoEntry->TdoEntry->TrustedDomainName;
MatchSid = DomainInfoEntry->TdoEntry->TrustedDomainSid;
*IsLocal = DomainInfoEntry->TdoEntry->LocalForestEntry;
break;
}
}
if ( MatchName == NULL || MatchSid == NULL ) {
Status = STATUS_NO_MATCH;
goto Error;
} else {
if ( TrustedDomainName &&
!FtcCopyUnicodeString( TrustedDomainName, MatchName )) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::MatchDnsName (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
if ( TrustedDomainSid &&
!FtcCopySid( TrustedDomainSid, MatchSid )) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::MatchDnsName (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
}
Status = STATUS_SUCCESS;
Cleanup:
return Status;
Error:
ASSERT( !NT_SUCCESS( Status ));
if ( TrustedDomainName ) {
FtcFree( TrustedDomainName->Buffer );
RtlZeroMemory( TrustedDomainName, sizeof( UNICODE_STRING ));
}
if ( TrustedDomainSid ) {
FtcFree( *TrustedDomainSid );
*TrustedDomainSid = NULL;
}
goto Cleanup;
}
NTSTATUS
FTCache::MatchNetbiosName(
IN UNICODE_STRING * Name,
OUT BOOLEAN * IsLocal,
OUT OPTIONAL UNICODE_STRING * TrustedDomainName,
OUT OPTIONAL PSID * TrustedDomainSid
)
/*++
Routine Description:
Locates a match for the given NetBios name in the cache
Arguments:
Name NetBios name to match
IsLocal Indicates whether the result is a local match
TrustedDomainName Used to return the name of the trusted domain
TrustedDomainSid Used to return the SID of the trusted domain
Returns:
STATUS_SUCCESS Match was found
STATUS_NO_MATCH Match was not found
STATUS_INSUFFICIENT_RESOURCES Out of memory
--*/
{
NTSTATUS Status;
NETBIOS_NAME_KEY * NetbiosKey;
UNICODE_STRING * MatchName = NULL;
PSID MatchSid = NULL;
//
// Caller must validate parameters
//
ASSERT( Name );
ASSERT( LsapValidateLsaUnicodeString( Name ));
NetbiosKey = ( NETBIOS_NAME_KEY * )RtlLookupElementGenericTableAvl(
&m_NetbiosNameTable,
Name
);
if ( NetbiosKey == NULL ) {
Status = STATUS_NO_MATCH;
goto Error;
}
for ( LIST_ENTRY * ListEntry = NetbiosKey->List.Flink;
ListEntry != &NetbiosKey->List;
ListEntry = ListEntry->Flink ) {
DOMAIN_INFO_ENTRY * DomainInfoEntry;
DomainInfoEntry = DOMAIN_INFO_ENTRY::EntryFromNetbiosEntry( ListEntry );
if ( DomainInfoEntry->SubordinateTo == NULL ) {
//
// Skip over store-and-ignore entries
//
ASSERT( DomainInfoEntry->IsSidAdminDisabled());
continue;
}
//
// Conditions for a netbios entry to be considered a match:
//
// - Entry must be enabled
// - Netbios portion must be enabled
// - corresponding top-level entry must be enabled
// - this entry must not be disowned by its TDO
//
if ( DomainInfoEntry->NetbiosEnabled() &&
DomainInfoEntry->SubordinateTo->Enabled() &&
!DomainInfoEntry->TdoEntry->Excludes( &DomainInfoEntry->DnsKey->DnsName )) {
MatchName = &DomainInfoEntry->TdoEntry->TrustedDomainName;
MatchSid = DomainInfoEntry->TdoEntry->TrustedDomainSid;
*IsLocal = DomainInfoEntry->TdoEntry->LocalForestEntry;
break;
}
}
if ( MatchName == NULL || MatchSid == NULL ) {
Status = STATUS_NO_MATCH;
goto Error;
} else {
if ( TrustedDomainName &&
!FtcCopyUnicodeString( TrustedDomainName, MatchName )) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::MatchNetbiosName (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
if ( TrustedDomainSid &&
!FtcCopySid( TrustedDomainSid, MatchSid )) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::MatchSid (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
}
Status = STATUS_SUCCESS;
Cleanup:
return Status;
Error:
ASSERT( !NT_SUCCESS( Status ));
if ( TrustedDomainName ) {
FtcFree( TrustedDomainName->Buffer );
RtlZeroMemory( TrustedDomainName, sizeof( UNICODE_STRING ));
}
if ( TrustedDomainSid ) {
FtcFree( *TrustedDomainSid );
*TrustedDomainSid = NULL;
}
goto Cleanup;
}
NTSTATUS
FTCache::MatchUpn(
IN UNICODE_STRING * Name,
OUT BOOLEAN * IsLocal,
OUT OPTIONAL UNICODE_STRING * TrustedDomainName,
OUT OPTIONAL PSID * TrustedDomainSid
)
/*++
Routine Description:
Locates a match for the given UPN in the cache.
UPNs are parsed to select the suffix to the right of the rightmost "@" symbol.
Suffix is compared with the FTInfo records of type TLN.
A substring comparision is performed.
o The suffix must be equal to, or subordinate to, the value in the TLN record.
o The suffix must be compared against all external TLN records and the
longest substring match wins.
o If the suffix is equal to, or subordinate to, a TlnExclusion record, no
match is possible for that particular forest TDO.
Arguments:
Name UPN to match
IsLocal Indicates whether the result is a local match
TrustedDomainName Used to return the name of the trusted domain
TrustedDomainSid Used to return the SID of the trusted domain
Returns:
STATUS_SUCCESS Match was found
STATUS_NO_MATCH Match was not found
STATUS_INSUFFICIENT_RESOURCES Out of memory
STATUS_INVALID_PARAMETER The UPN is malformed
--*/
{
NTSTATUS Status;
TLN_KEY * TlnKey = NULL;
UNICODE_STRING Suffix = { 0 };
TLN_ENTRY * MatchingTln;
SHORT Index;
//
// Caller must validate parameters
//
ASSERT( Name );
ASSERT( LsapValidateLsaUnicodeString( Name ));
//
// Extract the suffix from the UPN
//
for ( Index = Name->Length / 2 - 1;
Index >= 0;
Index -= 1 ) {
if ( Name->Buffer[Index] == L'@' ) {
Index += 1;
Suffix.Length = Name->Length - Index * sizeof( WCHAR );
Suffix.MaximumLength = Suffix.Length;
Suffix.Buffer = &Name->Buffer[Index];
break;
}
}
//
// Reject '@' at beginning or end of UPN, or UPNs with missing '@'
//
if ( Index == 0 || Index == 1 || Index == Name->Length / 2 ) {
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in FTCache::MatchUpn (%s:%d)\n", __FILE__, __LINE__ ));
return STATUS_INVALID_PARAMETER;
}
Status = MatchNamespace( &Suffix, IsLocal, TrustedDomainName, TrustedDomainSid );
if ( !NT_SUCCESS( Status )) {
goto Error;
}
Cleanup:
return Status;
Error:
ASSERT( !NT_SUCCESS( Status ));
goto Cleanup;
}
NTSTATUS
FTCache::MatchSpn(
IN UNICODE_STRING * Name,
OUT BOOLEAN * IsLocal,
OUT OPTIONAL UNICODE_STRING * TrustedDomainName,
OUT OPTIONAL PSID * TrustedDomainSid
)
/*++
Routine Description:
Locates a match for the given SPN in the cache
SPNs are the most complicated name form to parse and match. The matching
function parses the name starting from the right and proceeds in the
following order until it gets the first match, or exhausts all possibilities.
>Domain component is found: Either the Kerberos client or the KDC will
probably be able to route the request. If not, the matching function is
called.
The suffix to the right of the "@" is compared against FTInfo records of
type TLN, using a substring comparison.
o The suffix must be equal to, or subordinate to, the value in the TLN record.
o The suffix must be compared against all external TLN records and the
longest substring match wins.
o If the suffix is equal to, or subordinate to, a TlnExclusion record, no
match is possible for that particular forest TDO.
The next component to the left, ServiceName or InstanceName, is subjected
to the same substring comparison.
Both tests must generate a match, and both results must point to the same
forest TDO. Otherwise, no match is returned to the caller.
>ServiceName component is found first: Uses same substring comparison
described above.
The entire ServiceName value is compared against FTInfo records of type TLN.
>InstanceName component is found first: Uses same substring comparison
described above.
The entire InstanceName value is compared against FTInfo records of type TLN.
Arguments:
Name SPN to match
IsLocal Indicates whether the result is a local match
TrustedDomainName Used to return the name of the trusted domain
TrustedDomainSid Used to return the SID of the trusted domain
Returns:
STATUS_SUCCESS Match was found
STATUS_NO_MATCH Match was not found
STATUS_INSUFFICIENT_RESOURCES Out of memory
--*/
{
NTSTATUS Status;
UNICODE_STRING * MatchName = NULL;
PSID MatchSid = NULL;
WCHAR InstanceBuffer[ DNS_MAX_NAME_LENGTH + 1 ];
WCHAR DomainBuffer[ DNS_MAX_NAME_LENGTH + 1 ];
WCHAR RealmBuffer[ DNS_MAX_NAME_LENGTH + 1 ];
DWORD InstanceLength = DNS_MAX_NAME_LENGTH + 1;
DWORD DomainLength = DNS_MAX_NAME_LENGTH + 1;
DWORD RealmLength = DNS_MAX_NAME_LENGTH + 1;
UNICODE_STRING Instance = {0};
UNICODE_STRING Domain = {0};
UNICODE_STRING Realm = {0};
//
// Caller must validate parameters
//
ASSERT( Name );
ASSERT( LsapValidateLsaUnicodeString( Name ));
//
// Dissect the SPN into its constituent components
//
Status = DsCrackSpn3W(
Name->Buffer,
Name->Length / sizeof( WCHAR ),
NULL, // service class (don't care)
NULL, // service class (don't care)
&InstanceLength,
InstanceBuffer,
NULL, // instance port (don't care)
&DomainLength,
DomainBuffer,
&RealmLength,
RealmBuffer
);
if ( Status == ERROR_INVALID_PARAMETER ) {
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in FTCache::MatchSpn (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
} else if ( Status == ERROR_BUFFER_OVERFLOW ) {
Status = STATUS_BUFFER_OVERFLOW;
} else if ( Status != ERROR_SUCCESS ) {
//
// This assert will fire if DsCrackSpn3W returns
// an unrecognized ERROR_ code (need to map to STATUS_ code)
//
ASSERT( FALSE );
}
if ( Status != STATUS_SUCCESS ) {
goto Error;
}
//
// Instance name and service name lengths include terminators
//
ASSERT( InstanceLength > 0 );
ASSERT( DomainLength > 0 );
ASSERT( RealmLength > 0 );
InstanceLength--;
DomainLength--;
RealmLength--;
Instance.Length = ( USHORT )InstanceLength * sizeof( WCHAR );
Instance.MaximumLength = Instance.Length + sizeof( WCHAR );
Instance.Buffer = InstanceBuffer;
Domain.Length = ( USHORT )DomainLength * sizeof( WCHAR );
Domain.MaximumLength = Domain.Length + sizeof( WCHAR );
Domain.Buffer = DomainBuffer;
Realm.Length = ( USHORT )RealmLength * sizeof( WCHAR );
Realm.MaximumLength = Realm.Length + sizeof( WCHAR );
Realm.Buffer = RealmBuffer;
//
// Case 1: Domain name (realm) is present
//
if ( Realm.Length > 0 ) {
TLN_ENTRY * MatchRealm; // match for realm name component
TLN_ENTRY * MatchOther; // match for the other (domain or instance) component
MatchRealm = LongestSubstringMatchTln( IsLocal, &Realm );
if ( MatchRealm == NULL ) {
Status = STATUS_NO_MATCH;
goto Error;
} else if ( Domain.Length > 0 ) {
MatchOther = LongestSubstringMatchTln( IsLocal, &Domain );
} else if ( Instance.Length > 0 ) {
MatchOther = LongestSubstringMatchTln( IsLocal, &Instance );
} else {
MatchOther = NULL;
}
if ( MatchOther == NULL ) {
Status = STATUS_NO_MATCH;
goto Error;
} else if ( MatchRealm->TdoEntry != MatchOther->TdoEntry ) {
Status = STATUS_NO_MATCH;
goto Error;
} else {
MatchName = &MatchRealm->TdoEntry->TrustedDomainName;
MatchSid = MatchRealm->TdoEntry->TrustedDomainSid;
}
if ( TrustedDomainName &&
!FtcCopyUnicodeString( TrustedDomainName, MatchName )) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::MatchNamespace (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
if ( TrustedDomainSid &&
!FtcCopySid( TrustedDomainSid, MatchSid )) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::MatchNamespace (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
} else if ( Domain.Length > 0 ) {
Status = MatchNamespace( &Domain, IsLocal, TrustedDomainName, TrustedDomainSid );
} else if ( Instance.Length > 0 ) {
Status = MatchNamespace( &Instance, IsLocal, TrustedDomainName, TrustedDomainSid );
} else {
Status = STATUS_NO_MATCH;
}
if ( !NT_SUCCESS( Status )) {
goto Error;
}
Cleanup:
return Status;
Error:
if ( TrustedDomainName ) {
FtcFree( TrustedDomainName->Buffer );
RtlZeroMemory( TrustedDomainName, sizeof( UNICODE_STRING ));
}
if ( TrustedDomainSid ) {
FtcFree( *TrustedDomainSid );
*TrustedDomainSid = NULL;
}
ASSERT( !NT_SUCCESS( Status ));
goto Cleanup;
}
NTSTATUS
FTCache::MatchNamespace(
IN UNICODE_STRING * Name,
OUT BOOLEAN * IsLocal,
OUT OPTIONAL UNICODE_STRING * TrustedDomainName,
OUT OPTIONAL PSID * TrustedDomainSid
)
/*++
Routine Description:
Locates a match for the given namespace in the cache.
Namespace is compared with the FTInfo records of type TLN.
A substring comparision is performed.
o The namespace must be equal to, or subordinate to, the value in the
TLN record.
o The namespace must be compared against all TLN records and the
longest substring match wins.
o If the namespace is equal to, or subordinate to, a TlnExclusion record, no
match is possible for that particular forest TDO.
Arguments:
Name Namespace to match
IsLocal Indicates whether the result is a local match
TrustedDomainName Used to return the name of the trusted domain name
TrustedDomainSid Used to return the SID of the trusted domain
Returns:
STATUS_SUCCESS Match was found
STATUS_NO_MATCH Match was not found
STATUS_INSUFFICIENT_RESOURCES Out of memory
--*/
{
NTSTATUS Status;
TLN_KEY * TlnKey = NULL;
TLN_ENTRY * MatchingTln;
//
// Caller must validate parameters
//
ASSERT( Name );
ASSERT( LsapValidateLsaUnicodeString( Name ));
MatchingTln = LongestSubstringMatchTln( IsLocal, Name );
if ( MatchingTln == NULL ) {
Status = STATUS_NO_MATCH;
goto Error;
}
if ( TrustedDomainName &&
!FtcCopyUnicodeString( TrustedDomainName, &MatchingTln->TdoEntry->TrustedDomainName )) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::MatchNamespace (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
if ( TrustedDomainSid &&
!FtcCopySid( TrustedDomainSid, MatchingTln->TdoEntry->TrustedDomainSid )) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::MatchNamespace (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
Status = STATUS_SUCCESS;
Cleanup:
return Status;
Error:
ASSERT( !NT_SUCCESS( Status ));
if ( TrustedDomainName ) {
FtcFree( TrustedDomainName->Buffer );
RtlZeroMemory( TrustedDomainName, sizeof( UNICODE_STRING ));
}
if ( TrustedDomainSid ) {
FtcFree( *TrustedDomainSid );
*TrustedDomainSid = NULL;
}
goto Cleanup;
}
FTCache::TLN_ENTRY *
FTCache::LongestSubstringMatchTln(
OUT BOOLEAN * IsLocal,
IN UNICODE_STRING * String
)
/*++
Routine Description:
Finds a longest substring match to a given string
among all the enabled TLN records.
Arguments:
IsLocal Indicates whether the result is a local match
String string to match. must not be NULL.
Returns:
NULL if no match,
a pointer to a TLN entry otherwise
--*/
{
TLN_ENTRY * Match = NULL;
ASSERT( String );
for ( UNICODE_STRING Temp = *String;
Temp.Length > 0;
NextDnsComponent( &Temp )) {
TLN_KEY * TlnKey = ( TLN_KEY * )RtlLookupElementGenericTableAvl(
&m_TopLevelNameTable,
&Temp
);
if ( TlnKey == NULL ) {
continue;
}
for ( LIST_ENTRY * ListEntry = TlnKey->List.Flink;
ListEntry != &TlnKey->List;
ListEntry = ListEntry->Flink ) {
TLN_ENTRY * TlnEntry;
TlnEntry = TLN_ENTRY::EntryFromAvlEntry( ListEntry );
if ( TlnEntry->Excluded ) {
//
// Skip over excluded entries
//
continue;
}
//
// Conditions for a top level entry to be considered a match:
//
// - Entry must be enabled
// - Entry must not be a pseudo entry
// - the string must not be disowned by the entry's TDO
//
if ( TlnEntry->Enabled() &&
TlnEntry->SubordinateEntry == NULL &&
!TlnEntry->TdoEntry->Excludes( String )) {
Match = TlnEntry;
*IsLocal = TlnEntry->TdoEntry->LocalForestEntry;
break;
}
}
if ( Match != NULL ) {
break;
}
}
return Match;
}
NTSTATUS
FTCache::AddConflictPair(
IN OUT FTCache::CONFLICT_PAIR * * ConflictPairs,
IN OUT ULONG * ConflictPairsTotal,
IN LSA_FOREST_TRUST_RECORD_TYPE Type1,
IN void * Conflict1,
IN ULONG Flag1,
IN LSA_FOREST_TRUST_RECORD_TYPE Type2,
IN void * Conflict2,
IN ULONG Flag2
)
/*++
Routine Description:
Adds a given pair of records to an array of such pairs
Arguments:
ConflictPairs an array of conflict pairs to add to
ConflictPairsCount number of entries in ConflictPairs
Type1 type of the first side of conflict
Conflict1 first side of conflict
Flag1 flags of the first side of conflict
Type2 type of the second side of conflict
Conflict2 second side of conflict
Flag2 flags of the second side of conflict
Returns:
STATUS_SUCCESS if happy
STATUS_INSUFFICIENT_RESOURCES if out of memory
--*/
{
NTSTATUS Status;
ASSERT( ConflictPairs );
ASSERT( ConflictPairsTotal );
ASSERT( Conflict1 );
ASSERT( Conflict2 );
if ( *ConflictPairs == NULL ) {
*ConflictPairs = ( CONFLICT_PAIR * )FtcAllocate( sizeof( CONFLICT_PAIR ));
if ( ConflictPairs == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::AddConflictPair (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
} else {
CONFLICT_PAIR * ConflictPairsT;
ConflictPairsT = ( CONFLICT_PAIR * )FtcReallocate(
*ConflictPairs,
sizeof( CONFLICT_PAIR ) * ( *ConflictPairsTotal ),
sizeof( CONFLICT_PAIR ) * ( *ConflictPairsTotal + 1 ));
if ( ConflictPairsT == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::AddConflictPair (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
*ConflictPairs = ConflictPairsT;
}
ASSERT( *ConflictPairs );
(*ConflictPairs)[*ConflictPairsTotal].EntryType1 = Type1;
(*ConflictPairs)[*ConflictPairsTotal].Entry1 = Conflict1;
(*ConflictPairs)[*ConflictPairsTotal].Flag1 = Flag1;
(*ConflictPairs)[*ConflictPairsTotal].EntryType2 = Type2;
(*ConflictPairs)[*ConflictPairsTotal].Entry2 = Conflict2;
(*ConflictPairs)[*ConflictPairsTotal].Flag2 = Flag2;
*ConflictPairsTotal += 1;
Status = STATUS_SUCCESS;
Cleanup:
return Status;
Error:
ASSERT( !NT_SUCCESS( Status ));
goto Cleanup;
}
void
FTCache::ReconcileConflictPairs(
IN OPTIONAL const TDO_ENTRY * TdoEntry,
IN CONFLICT_PAIR * ConflictPairs,
IN ULONG ConflictPairsTotal
)
/*++
Routine Description:
Iterates through an array of conflict pairs and marks
entries corresponding to a given TDO entry as disabled
Arguments:
TdoEntry entry to be reconciled:
every conflict pair matching this entry
will be marked as disabled due to conflict
if NULL, the side of the conflict belonging
to a TDO with an alphabetically smaller name wins
unless it is the local forest entry, which always wins
ConflictPairs array of conflict pair records
ConflictPairsTotal
Returns:
Nothing
--*/
{
ASSERT( ConflictPairs );
for ( ULONG i = 0 ; i < ConflictPairsTotal ; i++ ) {
CONFLICT_PAIR& Pair = ConflictPairs[i];
if ( TdoEntry == NULL ) {
//
// Extract TDO entries corresponding to each
// side of the conflict so we can compare the
// TDO names and decide who wins
//
RTL_GENERIC_COMPARE_RESULTS Result;
TDO_ENTRY * TdoEntry1 = Pair.TdoEntry1();
TDO_ENTRY * TdoEntry2 = Pair.TdoEntry2();
if ( TdoEntry1->LocalForestEntry ) {
//
// A local forest entry can not lose
//
Pair.DisableEntry2();
} else if ( TdoEntry2->LocalForestEntry ) {
Pair.DisableEntry1();
} else {
//
// Compare TDO names of the conflicting entries
// Alphabetically smaller name wins
// This algorithm is deterministic
//
Result = UnicodeStringCompareRoutine(
NULL,
&TdoEntry1->TrustedDomainName,
&TdoEntry2->TrustedDomainName
);
if ( Result == GenericGreaterThan ) {
Pair.DisableEntry1();
} else if ( Result == GenericLessThan ) {
Pair.DisableEntry2();
} else {
ASSERT( FALSE ); // two TDO names can not be equal!!!
}
}
} else {
if ( TdoEntry == Pair.TdoEntry1()) {
Pair.DisableEntry1();
}
if ( TdoEntry == Pair.TdoEntry2()) {
Pair.DisableEntry2();
}
}
}
}
NTSTATUS
FTCache::GenerateConflictInfo(
IN CONFLICT_PAIR * ConflictPairs,
IN ULONG ConflictPairsTotal,
IN TDO_ENTRY * TdoEntry,
OUT PLSA_FOREST_TRUST_COLLISION_INFORMATION * CollisionInfo
)
/*++
Routine Description:
Generates conflict information in the format expected by the
caller from the conflict pairs array
Arguments:
ConflictPairs array of entries representing conflict information
ConflictPairsTotal count of entries in the "ConflitPairs" array
TdoEntry TDO entry in conflict
CollisionInfo used to return generated collision data
Returns:
STATUS_SUCCESS happy
STATUS_INSUFFICIENT_RESOURCES ran out of memory generated collision data
STATUS_INVALID_PARAMETER will also assert if invalid data encountered
--*/
{
NTSTATUS Status;
ASSERT( ConflictPairs );
ASSERT( TdoEntry );
ASSERT( CollisionInfo && *CollisionInfo == NULL );
for ( ULONG i = 0 ; i < ConflictPairsTotal ; i++ ) {
CONFLICT_PAIR& Pair = ConflictPairs[i];
ULONG Index;
ULONG Flags;
UNICODE_STRING * String;
switch ( Pair.EntryType1 ) {
case ForestTrustTopLevelName:
case ForestTrustTopLevelNameEx: {
ASSERT( Pair.TlnEntry1 );
ASSERT( Pair.TlnEntry1->TdoEntry == TdoEntry );
Index = Pair.TlnEntry1->Index;
Flags = Pair.TlnEntry1->Flags() | Pair.Flag1;
break;
}
case ForestTrustDomainInfo: {
ASSERT( Pair.DomainInfoEntry1 );
ASSERT( Pair.DomainInfoEntry1->TdoEntry == TdoEntry );
Index = Pair.DomainInfoEntry1->Index;
Flags = Pair.DomainInfoEntry1->Flags() | Pair.Flag1;
break;
}
default:
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in FTCache::GenerateConflictInfo (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
ASSERT( FALSE );
goto Error;
}
switch ( Pair.EntryType2 ) {
case ForestTrustTopLevelName:
case ForestTrustTopLevelNameEx: {
ASSERT( Pair.TlnEntry2 );
ASSERT( Pair.TlnEntry2->TdoEntry != TdoEntry );
String = &Pair.TlnEntry2->TdoEntry->TrustedDomainName;
break;
}
case ForestTrustDomainInfo: {
ASSERT( Pair.DomainInfoEntry2 );
ASSERT( Pair.DomainInfoEntry2->TdoEntry != TdoEntry );
String = &Pair.DomainInfoEntry2->TdoEntry->TrustedDomainName;
break;
}
default:
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in FTCache::GenerateConflictInfo (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
ASSERT( FALSE );
goto Error;
}
Status = LsapAddToCollisionInfo(
CollisionInfo,
Index,
CollisionTdo,
Flags,
String
);
if ( !NT_SUCCESS( Status )) {
goto Error;
}
}
Status = STATUS_SUCCESS;
Cleanup:
return Status;
Error:
ASSERT( !NT_SUCCESS( Status ));
//
// Cleanup generated conflict info
//
LsapFreeCollisionInfo( CollisionInfo );
ASSERT( *CollisionInfo == NULL );
goto Cleanup;
}
BOOLEAN
FTCache::TDO_ENTRY::Excludes(
IN const UNICODE_STRING * Name
)
/*++
Routine Description:
Determines if the given unicode name is disowned by this TDO_ENTRY
Arguments:
Name name to check
Returns:
TRUE if name is identical to one of the namespaces disowned by this tdo
or if it is subordinate to one of the disowned namespaces
FALSE otherwise
--*/
{
BOOLEAN Result = FALSE;
for ( LIST_ENTRY * ListEntry = TlnList.Flink;
ListEntry != &TlnList;
ListEntry = ListEntry->Flink ) {
TLN_ENTRY * TlnEntry;
TlnEntry = TLN_ENTRY::EntryFromTdoEntry( ListEntry );
if ( !TlnEntry->Excluded ) {
continue;
}
//
// Only those exclusion entries that have a superior TLN are considered,
// the rest are "stored and ignored"
//
if ( TlnEntry->SuperiorEntry == NULL ) {
continue;
}
if ( RtlEqualUnicodeString(
&TlnEntry->TlnKey->TopLevelName,
Name,
TRUE )) {
Result = TRUE;
break;
} else if ( IsSubordinate( Name, &TlnEntry->TlnKey->TopLevelName )) {
Result = TRUE;
break;
}
}
return Result;
}
void
FTCache::AuditChanges(
IN OPTIONAL const TDO_ENTRY * TdoEntryOld,
IN OPTIONAL const TDO_ENTRY * TdoEntryNew
)
/*++
Routine Description:
Computes differences between two TDO_ENTRY structures
and calls the corresponding auditing routines
Arguments:
TdoEntryOld old data (can be NULL in case of addition)
TdoEntryNew new data (can be NULL in case of deletion)
Returns:
Nothing (auditing failures are ignored)
--*/
{
//
// Log a new or modified TLN
//
#define LogNewTLN( TopLevelName, ForestName ) \
{ \
Status = SpmpReportEventU( \
EVENTLOG_WARNING_TYPE, \
LSA_FOREST_CLAIMED_NEW_TLN, \
0, \
0, \
NULL, \
2, \
TopLevelName, \
ForestName \
); \
\
if ( !NT_SUCCESS( Status )) { \
\
goto Cleanup; \
} \
}
//
// Criteria for logging the new or modified
// TLN
//
#define MustLogNewTLN( TlnEntry ) \
(( !TlnEntry->Excluded ) && \
( TlnEntry->Flags() & LSA_TLN_DISABLED_NEW ))
NTSTATUS Status;
LUID OperationId;
BOOLEAN AuditingEnabled;
if ( TdoEntryOld == NULL &&
TdoEntryNew == NULL ) {
//
// Give us at least something!
//
return;
}
//
// No auditing is performed on local forest entries
//
if ( TdoEntryOld && TdoEntryOld->LocalForestEntry ) {
return;
}
if ( TdoEntryNew && TdoEntryNew->LocalForestEntry ) {
return;
}
AuditingEnabled = LsapAdtAuditingEnabledHint(
AuditCategoryPolicyChange,
EVENTLOG_AUDIT_SUCCESS
);
if ( !AuditingEnabled ) {
LsapDsDebugOut(( DEB_FTINFO, "FTCache::AuditChanges: Auditing of x-forest trust changes skipped\n" ));
}
Status = NtAllocateLocallyUniqueId( &OperationId );
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "FTCache::AuditChanges: Can not allocate a LUID for auditing (0x%lx)\n", Status ));
return;
}
if ( TdoEntryOld == NULL || TdoEntryNew == NULL ) {
LIST_ENTRY * ListEntry;
//
// This is an add or remove operation
// Figure out which one it is and apply to every entry
//
typedef NTSTATUS
(*PFNAUDITFN)(
IN PUNICODE_STRING ForestName,
IN PSID ForestSid,
IN PLUID OperationId,
IN LSA_FOREST_TRUST_RECORD_TYPE EntryType,
IN ULONG Flags,
IN PUNICODE_STRING TopLevelName,
IN PUNICODE_STRING DnsName,
IN PUNICODE_STRING NetbiosName,
IN PSID Sid
);
const BOOLEAN Adding = ( TdoEntryNew ? TRUE : FALSE );
const TDO_ENTRY * Entry = ( Adding ? TdoEntryNew : TdoEntryOld );
PFNAUDITFN AuditChangeFn = ( Adding ?
LsapAdtTrustedForestInfoEntryAdd :
LsapAdtTrustedForestInfoEntryRem );
//
// New data is being added to the cache
//
for ( ListEntry = Entry->TlnList.Flink;
ListEntry != &Entry->TlnList;
ListEntry = ListEntry->Flink ) {
TLN_ENTRY * TlnEntry;
TlnEntry = TLN_ENTRY::EntryFromTdoEntry( ListEntry );
//
// Skip over "pseudo" entries
//
if ( !( TlnEntry->Excluded ||
TlnEntry->SubordinateEntry == NULL )) {
continue;
}
//
// Bug 494666: log disabled TLN entries (since they are new)
// regardless of auditing being enabled or not
//
if ( Adding &&
MustLogNewTLN( TlnEntry )) {
LogNewTLN(
&( TlnEntry->TlnKey->TopLevelName ),
&( Entry->TrustedDomainName )
);
}
//
// Auditing is not enabled, continue
//
if( !AuditingEnabled ) {
continue;
}
Status = AuditChangeFn(
( PUNICODE_STRING )&Entry->TrustedDomainName,
Entry->TrustedDomainSid,
&OperationId,
TlnEntry->Excluded ?
ForestTrustTopLevelNameEx :
ForestTrustTopLevelName,
TlnEntry->Flags(),
&TlnEntry->TlnKey->TopLevelName,
NULL,
NULL,
NULL
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "FTCache::AuditChanges: Auditing failure 0x%lx on entry %p\n (%s:%d)", Status, TlnEntry, __FILE__, __LINE__ ));
goto Cleanup;
}
}
//
// if Auditing is not enabled
// there is nothing left to do
// so goto cleanup
//
if ( !AuditingEnabled ) {
goto Cleanup;
}
for ( ListEntry = Entry->DomainInfoList.Flink;
ListEntry != &Entry->DomainInfoList;
ListEntry = ListEntry->Flink ) {
DOMAIN_INFO_ENTRY * DomainInfoEntry;
DomainInfoEntry = DOMAIN_INFO_ENTRY::EntryFromTdoEntry( ListEntry );
Status = AuditChangeFn(
( PUNICODE_STRING )&Entry->TrustedDomainName,
Entry->TrustedDomainSid,
&OperationId,
ForestTrustDomainInfo,
DomainInfoEntry->Flags(),
NULL,
&DomainInfoEntry->DnsKey->DnsName,
DomainInfoEntry->NetbiosKey ?
&DomainInfoEntry->NetbiosKey->NetbiosName :
NULL,
DomainInfoEntry->SidKey->DomainSid
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "FTCache::AuditChanges: Auditing failure 0x%lx on entry %p (%s:%d)\n", Status, DomainInfoEntry, __FILE__, __LINE__ ));
goto Cleanup;
}
}
for ( ListEntry = Entry->BinaryList.Flink;
ListEntry != &Entry->BinaryList;
ListEntry = ListEntry->Flink ) {
BINARY_ENTRY * BinaryEntry;
BinaryEntry = BINARY_ENTRY::EntryFromTdoEntry( ListEntry );
Status = AuditChangeFn(
( PUNICODE_STRING )&Entry->TrustedDomainName,
Entry->TrustedDomainSid,
&OperationId,
BinaryEntry->Type,
BinaryEntry->Flags(),
NULL,
NULL,
NULL,
NULL
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "FTCache::AuditChanges: Auditing failure 0x%lx on entry %p (%s:%d)\n", Status, BinaryEntry, __FILE__, __LINE__ ));
goto Cleanup;
}
}
} else {
//
// Data is being modified
//
LIST_ENTRY * ListEntry;
const TDO_ENTRY * TdoEntry = TdoEntryNew; // have to pick one for auditing
ASSERT( RtlEqualUnicodeString(
&TdoEntryOld->TrustedDomainName,
&TdoEntryNew->TrustedDomainName,
TRUE ));
//
// Go through new TLN entries, see if they are present in the old list
// If not, they're additions
// If yes, check for modifications
//
for ( ListEntry = TdoEntryNew->TlnList.Flink;
ListEntry != &TdoEntryNew->TlnList;
ListEntry = ListEntry->Flink ) {
TLN_ENTRY * TlnEntryNew;
BOOLEAN Found = FALSE;
BOOLEAN Modified = FALSE;
TlnEntryNew = TLN_ENTRY::EntryFromTdoEntry( ListEntry );
//
// Skip over "pseudo" entries
//
if ( !( TlnEntryNew->Excluded ||
TlnEntryNew->SubordinateEntry == NULL )) {
continue;
}
for ( LIST_ENTRY * ListEntryAvl = TlnEntryNew->TlnKey->List.Flink;
ListEntryAvl != &TlnEntryNew->TlnKey->List;
ListEntryAvl = ListEntryAvl->Flink ) {
TLN_ENTRY * TlnEntryOld;
TlnEntryOld = TLN_ENTRY::EntryFromAvlEntry( ListEntryAvl );
//
// Specifically looking for old entries
//
if ( TlnEntryOld->TdoEntry != TdoEntryOld ) {
continue;
}
//
// Skip over "pseudo" entries
//
if ( !( TlnEntryOld->Excluded ||
TlnEntryOld->SubordinateEntry == NULL )) {
continue;
}
if ( TlnEntryNew->Excluded != TlnEntryOld->Excluded ) {
continue;
}
Found = TRUE;
//
// The only thing that can change on a top level name entry
// is its flags
//
if ( TlnEntryOld->Flags() != TlnEntryNew->Flags()) {
//
// Bug 494666: log disabled TLN entries
// regardless of auditing being enabled or not
//
if ( MustLogNewTLN( TlnEntryOld )) {
LogNewTLN(
&( TlnEntryNew->TlnKey->TopLevelName ),
&( TdoEntry->TrustedDomainName )
);
}
//
// if auditing is enabled
//
if ( AuditingEnabled ) {
Status = LsapAdtTrustedForestInfoEntryMod(
( PUNICODE_STRING )&TdoEntry->TrustedDomainName,
TdoEntry->TrustedDomainSid,
&OperationId,
TlnEntryOld->Excluded ?
ForestTrustTopLevelNameEx :
ForestTrustTopLevelName,
TlnEntryOld->Flags(),
&TlnEntryOld->TlnKey->TopLevelName,
NULL,
NULL,
NULL,
TlnEntryNew->Flags(),
&TlnEntryNew->TlnKey->TopLevelName,
NULL,
NULL,
NULL
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "FTCache::AuditChanges: Auditing failure 0x%lx on entries %p (old) and %p (new) (%s:%d)\n", Status, TlnEntryOld, TlnEntryNew, __FILE__, __LINE__ ));
}
}
Modified = TRUE;
}
//
// The Index field of the entry is reused here to aid performance.
// Index contains the ordinal number the entry had in the original
// array of FTInfo entries. This is necessary in order to report
// conflict information during insertion, and the data is NOT
// used anywhere afterwards.
// We set this field to '-1' here. Later, when looking for
// old entries that are being removed, the algorithm is simply to
// pick those whose index field is not -1 and report them as removals.
//
ASSERT( TlnEntryOld->Index != 0xFFFFFFFF );
TlnEntryOld->Index = 0xFFFFFFFF;
break;
}
//
// If an entry wasn't a modification, it's an addition
//
if ( !Found && !Modified ) {
//
// Bug 494666: log disabled TLN entries (since they are new)
// regardless of auditing being enabled or not
//
if ( MustLogNewTLN( TlnEntryNew )) {
LogNewTLN(
&( TlnEntryNew->TlnKey->TopLevelName ),
&( TdoEntry->TrustedDomainName )
);
}
//
// if auditing is not enabled
// continue with the next TLN entry
//
if ( !AuditingEnabled ) {
continue;
}
Status = LsapAdtTrustedForestInfoEntryAdd(
( PUNICODE_STRING )&TdoEntry->TrustedDomainName,
TdoEntry->TrustedDomainSid,
&OperationId,
TlnEntryNew->Excluded ?
ForestTrustTopLevelNameEx :
ForestTrustTopLevelName,
TlnEntryNew->Flags(),
&TlnEntryNew->TlnKey->TopLevelName,
NULL,
NULL,
NULL
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "FTCache::AuditChanges: Auditing failure 0x%lx on entry %p (%s:%d)\n", Status, TlnEntryNew, __FILE__, __LINE__ ));
}
}
}
//
// if Auditing is not enabled
// there is nothing left to do
// so goto cleanup
//
if ( !AuditingEnabled ) {
goto Cleanup;
}
//
// Go through old entries, see if they have been marked as
// found. If not, audit them as removals.
//
for ( ListEntry = TdoEntryOld->TlnList.Flink;
ListEntry != &TdoEntryOld->TlnList;
ListEntry = ListEntry->Flink ) {
TLN_ENTRY * TlnEntryOld;
TlnEntryOld = TLN_ENTRY::EntryFromTdoEntry( ListEntry );
//
// Skip over "pseudo" entries
//
if ( !( TlnEntryOld->Excluded ||
TlnEntryOld->SubordinateEntry == NULL )) {
continue;
}
//
// Entries marked with index of 0xFFFFFFFF have been
// found when looking for modifications. Skip over those here.
//
if ( TlnEntryOld->Index == 0xFFFFFFFF ) {
TlnEntryOld->Index = 0; // no need to keep the special value around
continue;
}
Status = LsapAdtTrustedForestInfoEntryRem(
( PUNICODE_STRING )&TdoEntry->TrustedDomainName,
TdoEntry->TrustedDomainSid,
&OperationId,
TlnEntryOld->Excluded ?
ForestTrustTopLevelNameEx :
ForestTrustTopLevelName,
TlnEntryOld->Flags(),
&TlnEntryOld->TlnKey->TopLevelName,
NULL,
NULL,
NULL
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "FTCache::AuditChanges: Auditing failure 0x%x on entry %p (%s:%d)\n", Status, TlnEntryOld, __FILE__, __LINE__ ));
}
}
//
// Go through new domain info entries, see if they are present in the old list
// If not, they're additions
// If yes, check for modifications
//
for ( ListEntry = TdoEntryNew->DomainInfoList.Flink;
ListEntry != &TdoEntryNew->DomainInfoList;
ListEntry = ListEntry->Flink ) {
DOMAIN_INFO_ENTRY * DomainInfoEntryNew;
BOOLEAN Found = FALSE;
BOOLEAN Modified = FALSE;
DomainInfoEntryNew = DOMAIN_INFO_ENTRY::EntryFromTdoEntry( ListEntry );
//
// The invariant of any entry is its SID
// The assumption is that for a given SID, the DNS name and Netbios
// name may change, as well as flags
//
for ( LIST_ENTRY * ListEntrySid = DomainInfoEntryNew->SidKey->List.Flink;
ListEntrySid != &DomainInfoEntryNew->SidKey->List;
ListEntrySid = ListEntrySid->Flink ) {
DOMAIN_INFO_ENTRY * DomainInfoEntryOld;
DomainInfoEntryOld = DOMAIN_INFO_ENTRY::EntryFromSidEntry( ListEntrySid );
//
// Specifically looking for old entries
//
if ( DomainInfoEntryOld->TdoEntry != TdoEntryOld ) {
continue;
}
Found = TRUE;
if ( DomainInfoEntryOld->Flags() != DomainInfoEntryNew->Flags()) {
Modified = TRUE;
} else if ( !RtlEqualUnicodeString(
&DomainInfoEntryOld->DnsKey->DnsName,
&DomainInfoEntryNew->DnsKey->DnsName,
TRUE )) {
Modified = TRUE;
} else if ( XOR<NETBIOS_NAME_KEY*>(
DomainInfoEntryOld->NetbiosKey,
DomainInfoEntryNew->NetbiosKey )) {
Modified = TRUE;
} else if ( DomainInfoEntryOld->NetbiosKey &&
DomainInfoEntryNew->NetbiosKey &&
!RtlEqualUnicodeString(
&DomainInfoEntryOld->NetbiosKey->NetbiosName,
&DomainInfoEntryNew->NetbiosKey->NetbiosName,
TRUE )) {
Modified = TRUE;
}
if ( Modified ) {
Status = LsapAdtTrustedForestInfoEntryMod(
( PUNICODE_STRING )&TdoEntry->TrustedDomainName,
TdoEntry->TrustedDomainSid,
&OperationId,
ForestTrustDomainInfo,
DomainInfoEntryOld->Flags(),
NULL,
&DomainInfoEntryOld->DnsKey->DnsName,
DomainInfoEntryOld->NetbiosKey ?
&DomainInfoEntryOld->NetbiosKey->NetbiosName :
NULL,
DomainInfoEntryOld->Sid,
DomainInfoEntryNew->Flags(),
NULL,
&DomainInfoEntryNew->DnsKey->DnsName,
DomainInfoEntryNew->NetbiosKey ?
&DomainInfoEntryNew->NetbiosKey->NetbiosName :
NULL,
DomainInfoEntryNew->Sid
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "FTCache::AuditChanges: Auditing failure 0x%lx on entries %p (old) and %p (new) (%s:%d)\n", Status, DomainInfoEntryOld, DomainInfoEntryNew, __FILE__, __LINE__ ));
}
}
//
// The Index field of the entry is reused here to aid performance.
// Index contains the ordinal number the entry had in the original
// array of FTInfo entries. This is necessary in order to report
// conflict information during insertion, and the data is NOT
// used anywhere afterwards.
// We set this field to '-1' here. Later, when looking for
// old entries that are being removed, the algorithm is simply to
// pick those whose index field is not -1 and report them as removals.
//
ASSERT( DomainInfoEntryOld->Index != 0xFFFFFFFF );
DomainInfoEntryOld->Index = 0xFFFFFFFF;
break;
}
//
// If an entry wasn't a modification, it's an addition
//
if ( !Found && !Modified ) {
Status = LsapAdtTrustedForestInfoEntryAdd(
( PUNICODE_STRING )&TdoEntry->TrustedDomainName,
TdoEntry->TrustedDomainSid,
&OperationId,
ForestTrustDomainInfo,
DomainInfoEntryNew->Flags(),
NULL,
&DomainInfoEntryNew->DnsKey->DnsName,
DomainInfoEntryNew->NetbiosKey ?
&DomainInfoEntryNew->NetbiosKey->NetbiosName :
NULL,
DomainInfoEntryNew->Sid
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "FTCache::AuditChanges: Auditing failure 0x%lx on entry %p (%s:%d)\n", Status, DomainInfoEntryNew, __FILE__, __LINE__ ));
}
}
}
//
// Go through old entries, see if they have been marked as
// modified. If not, audit them as removals.
//
for ( ListEntry = TdoEntryOld->DomainInfoList.Flink;
ListEntry != &TdoEntryOld->DomainInfoList;
ListEntry = ListEntry->Flink ) {
DOMAIN_INFO_ENTRY * DomainInfoEntryOld;
DomainInfoEntryOld = DOMAIN_INFO_ENTRY::EntryFromTdoEntry( ListEntry );
//
// Entries marked with index of 0xFFFFFFFF have been
// found when looking for modifications. Skip over those here.
//
if ( DomainInfoEntryOld->Index == 0xFFFFFFFF ) {
DomainInfoEntryOld->Index = 0; // no need to keep the special value around
continue;
}
Status = LsapAdtTrustedForestInfoEntryRem(
( PUNICODE_STRING )&TdoEntry->TrustedDomainName,
TdoEntry->TrustedDomainSid,
&OperationId,
ForestTrustDomainInfo,
DomainInfoEntryOld->Flags(),
NULL,
&DomainInfoEntryOld->DnsKey->DnsName,
DomainInfoEntryOld->NetbiosKey ?
&DomainInfoEntryOld->NetbiosKey->NetbiosName :
NULL,
DomainInfoEntryOld->Sid
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "FTCache::AuditChanges: Auditing failure 0x%lx on entry %p (%s:%d)\n", Status, TdoEntry, __FILE__, __LINE__ ));
}
}
//
// Binary entries are audited as replacements
//
for ( ListEntry = TdoEntryOld->BinaryList.Flink;
ListEntry != &TdoEntryOld->BinaryList;
ListEntry = ListEntry->Flink ) {
BINARY_ENTRY * BinaryEntry;
BinaryEntry = BINARY_ENTRY::EntryFromTdoEntry( ListEntry );
Status = LsapAdtTrustedForestInfoEntryRem(
( PUNICODE_STRING )&TdoEntryOld->TrustedDomainName,
TdoEntryOld->TrustedDomainSid,
&OperationId,
BinaryEntry->Type,
BinaryEntry->Flags(),
NULL,
NULL,
NULL,
NULL
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "FTCache::AuditChanges: Auditing failure 0x%lx on entry %p (%s:%d)\n", Status, TdoEntryOld, __FILE__, __LINE__ ));
goto Cleanup;
}
}
for ( ListEntry = TdoEntryNew->BinaryList.Flink;
ListEntry != &TdoEntryNew->BinaryList;
ListEntry = ListEntry->Flink ) {
BINARY_ENTRY * BinaryEntry;
BinaryEntry = BINARY_ENTRY::EntryFromTdoEntry( ListEntry );
Status = LsapAdtTrustedForestInfoEntryAdd(
( PUNICODE_STRING )&TdoEntryNew->TrustedDomainName,
TdoEntryNew->TrustedDomainSid,
&OperationId,
BinaryEntry->Type,
BinaryEntry->Flags(),
NULL,
NULL,
NULL,
NULL
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "FTCache::AuditChanges: Auditing failure 0x%lx on entry %p (%s:%d)\n", Status, TdoEntryNew, __FILE__, __LINE__ ));
goto Cleanup;
}
}
}
Cleanup:
return;
}
void
FTCache::AuditCollisions(
IN CONFLICT_PAIR * ConflictPairs,
IN ULONG ConflictPairsTotal
)
/*++
Routine description:
Generates audits for collisions
Arguments:
ConflictPairs array of conflict pairs
ConflictPairsTotal number of elements in ConflictPairs
Returns:
Nothing
--*/
{
for ( ULONG i = 0 ; i < ConflictPairsTotal ; i++ ) {
NTSTATUS Status;
CONFLICT_PAIR& Pair = ConflictPairs[i];
TDO_ENTRY * TdoEntry1 = Pair.TdoEntry1();
TDO_ENTRY * TdoEntry2 = Pair.TdoEntry2();
LSA_FOREST_TRUST_COLLISION_RECORD_TYPE CollisionTargetType;
PUNICODE_STRING CollisionTargetName;
PUNICODE_STRING ForestRootDomainName;
PUNICODE_STRING TopLevelName;
PUNICODE_STRING DnsName;
PUNICODE_STRING NetbiosName;
PSID Sid;
ULONG NewFlags;
ASSERT( TdoEntry1 != NULL );
ASSERT( TdoEntry2 != NULL );
if ( TdoEntry1->LocalForestEntry ) {
CollisionTargetType = CollisionXref;
CollisionTargetName = &TdoEntry1->TrustedDomainName;
ForestRootDomainName = &TdoEntry2->TrustedDomainName;
TopLevelName = ( Pair.EntryType2 == ForestTrustTopLevelName ) ?
&Pair.TlnEntry2->TlnKey->TopLevelName : NULL;
DnsName = ( Pair.EntryType2 == ForestTrustDomainInfo ) ?
&Pair.DomainInfoEntry2->DnsKey->DnsName : NULL;
NetbiosName = ( Pair.EntryType2 == ForestTrustDomainInfo &&
Pair.DomainInfoEntry2->NetbiosKey ) ?
&Pair.DomainInfoEntry2->NetbiosKey->NetbiosName : NULL;
Sid = ( Pair.EntryType2 == ForestTrustDomainInfo ) ?
Pair.DomainInfoEntry2->SidKey->DomainSid : NULL;
NewFlags = Pair.Flag2;
} else if ( TdoEntry2->LocalForestEntry ) {
CollisionTargetType = CollisionXref;
CollisionTargetName = &TdoEntry2->TrustedDomainName;
ForestRootDomainName = &TdoEntry1->TrustedDomainName;
TopLevelName = ( Pair.EntryType1 == ForestTrustTopLevelName ) ?
&Pair.TlnEntry1->TlnKey->TopLevelName : NULL;
DnsName = ( Pair.EntryType1 == ForestTrustDomainInfo ) ?
&Pair.DomainInfoEntry1->DnsKey->DnsName : NULL;
NetbiosName = ( Pair.EntryType1 == ForestTrustDomainInfo &&
Pair.DomainInfoEntry1->NetbiosKey ) ?
&Pair.DomainInfoEntry1->NetbiosKey->NetbiosName : NULL;
Sid = ( Pair.EntryType1 == ForestTrustDomainInfo ) ?
Pair.DomainInfoEntry1->SidKey->DomainSid : NULL;
NewFlags = Pair.Flag1;
} else {
CollisionTargetType = CollisionTdo;
CollisionTargetName = &TdoEntry2->TrustedDomainName;
ForestRootDomainName = &TdoEntry1->TrustedDomainName;
TopLevelName = ( Pair.EntryType2 == ForestTrustTopLevelName ) ?
&Pair.TlnEntry2->TlnKey->TopLevelName : NULL;
DnsName = ( Pair.EntryType2 == ForestTrustDomainInfo ) ?
&Pair.DomainInfoEntry2->DnsKey->DnsName : NULL;
NetbiosName = ( Pair.EntryType2 == ForestTrustDomainInfo &&
Pair.DomainInfoEntry2->NetbiosKey ) ?
&Pair.DomainInfoEntry2->NetbiosKey->NetbiosName : NULL;
Sid = ( Pair.EntryType2 == ForestTrustDomainInfo ) ?
Pair.DomainInfoEntry2->SidKey->DomainSid : NULL;
NewFlags = Pair.Flag2;
}
Status = LsapAdtTrustedForestNamespaceCollision(
CollisionTargetType,
CollisionTargetName,
ForestRootDomainName,
TopLevelName,
DnsName,
NetbiosName,
Sid,
NewFlags
);
}
return;
}
///////////////////////////////////////////////////////////////////////////////
//
// Marshaling routines
//
///////////////////////////////////////////////////////////////////////////////
ULONG
LsapMarshalSid(
IN BYTE * Blob,
IN SID * Sid
)
/*++
Routine Description:
Writes the internal representation of a SID into the buffer
If buffer is NULL, returns the number of bytes needed
NOTE: The buffer, if not NULL, is assumed to be big enough!!!
Arguments:
Blob address of the target buffer
Sid SID to marshal
Returns:
if Blob != NULL, number of bytes written into the buffer
if Blob == NULL, number of bytes needed to represent the SID
--*/
{
ULONG Index = 0;
UCHAR i;
ASSERT( RtlValidSid( Sid ));
if ( Blob != NULL ) {
//
// Leave space for the length
//
Index += sizeof( ULONG );
Blob[Index] = Sid->Revision;
Index += sizeof( UCHAR );
Blob[Index] = Sid->SubAuthorityCount;
Index += sizeof( UCHAR );
ASSERT( sizeof( SID_IDENTIFIER_AUTHORITY ) == 6 );
RtlCopyMemory( &Blob[Index], Sid->IdentifierAuthority.Value, sizeof( SID_IDENTIFIER_AUTHORITY ));
Index += sizeof( SID_IDENTIFIER_AUTHORITY );
for ( i = 0 ; i < Sid->SubAuthorityCount ; i++ ) {
SmbPutUlong( &Blob[Index], Sid->SubAuthority[i] );
Index += sizeof( ULONG );
}
//
// Now that we know the length, prepend it at the beginning of the blob
//
SmbPutUlong( &Blob[0], Index - sizeof( ULONG ));
} else {
Index = sizeof( ULONG ) + // length of data that follows
sizeof( UCHAR ) + // revision
sizeof( UCHAR ) + // subauthority count
sizeof( SID_IDENTIFIER_AUTHORITY ) + // identifier authority
Sid->SubAuthorityCount * sizeof( ULONG ); // subauthority data
}
return Index;
}
ULONG
LsapUnmarshalSid(
IN BYTE * Blob,
OUT PISID * Sid
)
/*++
Routine Description:
Decodes the internal representation of a SID
Caller must use FtcFree to free the generated SID
Arguments:
Blob address of the buffer containing internal representation
of the SID
Sid used ot return the generated SID
Returns:
Number of bytes read from the blob
If memory could not be allocated or there was an error reading
data from the blob, raises an exception. The caller must be
prepared to handle this exception and use GetExceptionCode() to
obtain the reason for failure.
--*/
{
NTSTATUS Status;
ULONG Index = 0;
ULONG Length;
ASSERT( Blob );
ASSERT( Sid );
*Sid = NULL;
__try {
UCHAR Revision, SubAuthorityCount;
UCHAR i;
//
// Ignore the length field
//
Length = SmbGetUlong( &Blob[Index] );
Index += sizeof( ULONG );
Revision = ( UCHAR )Blob[Index];
Index += sizeof( BYTE );
SubAuthorityCount = ( UCHAR )Blob[Index];
Index += sizeof( BYTE );
*Sid = ( SID * )FtcAllocate(
sizeof( SID ) + SubAuthorityCount * sizeof( ULONG )
);
if ( *Sid == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::MatchSpn (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
(*Sid)->Revision = Revision;
(*Sid)->SubAuthorityCount = SubAuthorityCount;
ASSERT( sizeof( SID_IDENTIFIER_AUTHORITY ) == 6 );
RtlCopyMemory( (*Sid)->IdentifierAuthority.Value, &Blob[Index], sizeof( SID_IDENTIFIER_AUTHORITY ));
Index += sizeof( SID_IDENTIFIER_AUTHORITY );
for ( i = 0 ; i < SubAuthorityCount ; i++ ) {
(*Sid)->SubAuthority[i] = SmbGetUlong( &Blob[Index] );
Index += sizeof( ULONG );
}
} __except ( EXCEPTION_EXECUTE_HANDLER ) {
ASSERT( FALSE );
Status = GetExceptionCode();
goto Error;
}
//
// Verify the correctness of the length field
//
ASSERT( Length == Index - sizeof( ULONG ));
return Index;
Error:
FtcFree( *Sid );
*Sid = NULL;
RaiseException( Status, 0, 0, NULL );
return 0; // so the compiler doesn't complain
}
ULONG
LsapMarshalString(
IN BYTE * Blob,
IN UNICODE_STRING * String
)
/*++
Routine Description:
Writes an internal representation of a Unicode string into the buffer
If buffer is NULL, returns the number of bytes needed
NOTE: The buffer, if not NULL, is assumed to be big enough!!!
Arguments:
Blob address of the target buffer
String address of a UNICODE_STRING structure to marshal
Returns:
if Blob != NULL, number of bytes written into the buffer
if Blob == NULL, number of bytes needed to represent the string
If conversion to internal representation fails, will raise an
exception. Caller should be prepared to handle this exception and
use GetExceptionCode() to obtain the reason for failure.
--*/
{
ULONG Index = 0;
ULONG Length;
ASSERT( String );
//
// Leave space for the length
//
Index += sizeof( ULONG );
Length = WideCharToMultiByte(
CP_UTF8,
0,
String->Buffer,
String->Length / sizeof( WCHAR ),
Blob ? ( LPSTR )&Blob[Index] : NULL,
Blob ? MAXLONG : 0,
NULL,
NULL
);
if ( Length == 0 && String->Length > 0 ) {
RaiseException( GetLastError(), 0, 0, NULL );
}
//
// Now that we know the length, prepend it at the beginning of the blob
//
if ( Blob != NULL ) {
SmbPutUlong( &Blob[0], Length );
}
Index += Length;
return Index;
}
ULONG
LsapUnmarshalString(
IN BYTE * Blob,
OUT UNICODE_STRING * String
)
/*++
Routine Description:
Decodes the internal representation of a Unicode string
Caller must use FtcFree to free the generated string
Arguments:
Blob address of the buffer containing internal representation
of the Unicode string
String used to return the generated string
Returns:
Number of bytes read from the blob
If memory could not be allocated or there was an error during
conversion, raises an exception. The caller must be
prepared to handle this exception and use GetExceptionCode() to
obtain the reason for failure.
--*/
{
NTSTATUS Status;
ULONG Index = 0;
ASSERT( Blob );
ASSERT( String );
String->Buffer = NULL;
__try {
ULONG Length;
//
// Obtain the length of the UTF-8 encoded unicode string
//
Length = SmbGetUlong( &Blob[Index] );
Index += sizeof( ULONG );
//
// See how big the buffer should be for the decoded string
//
if ( Length > 0 ) {
String->Length = ( USHORT )MultiByteToWideChar(
CP_UTF8,
0,
( LPSTR )&Blob[Index],
Length,
NULL,
0
) * sizeof( WCHAR );
if ( String->Length == 0 ) {
Status = GetLastError();
LsapDsDebugOut(( DEB_FTINFO, "LsapUnmarshalString: MultiByteToWideChar returned 0x%x\n", Status ));
goto Error;
}
} else {
String->Length = 0;
}
//
// Prepare to have the string NULL-terminated
//
String->MaximumLength = String->Length + sizeof( WCHAR );
String->Buffer = ( PWSTR )FtcAllocate( String->MaximumLength );
if ( String->Buffer == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in LsapUnmarshalString (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
//
// Now perform the actual conversion. Note that we don't care
// what the return value is anymore, as long as it's non-zero
//
if ( Length > 0 ) {
if (0 == MultiByteToWideChar(
CP_UTF8,
0,
( LPSTR )&Blob[Index],
Length,
String->Buffer,
String->Length / sizeof( WCHAR ))) {
Status = GetLastError();
ASSERT( FALSE ); // certainly not expecting this to fail (it just succeeded, above)
goto Error;
}
Index += Length;
}
//
// NULL-terminate the string for future convenience
//
(String->Buffer)[String->Length / sizeof( WCHAR )] = L'\0';
} __except ( EXCEPTION_EXECUTE_HANDLER ) {
ASSERT( FALSE );
Status = GetExceptionCode();
goto Error;
}
return Index;
Error:
FtcFree( String->Buffer );
String->Buffer = NULL;
RaiseException( Status, 0, 0, NULL );
return 0; // so the compiler doesn't complain
}
ULONG
LsapUnmarshalData(
IN BYTE * Blob,
OUT LSA_FOREST_TRUST_BINARY_DATA * Data
)
/*++
Routine Description:
Decodes the internal representation of a binary data blob
Caller must use FtcFree to free the generated structure
Arguments:
Blob address of the buffer containing internal representation
of the binary blob
Data used to return the generated blob
Returns:
Number of bytes read from the blob
If memory could not be allocated, raises an exception.
The caller must be prepared to handle this exception and use
GetExceptionCode() to obtain the reason for failure.
--*/
{
NTSTATUS Status;
ULONG Index = 0;
ASSERT( Blob );
ASSERT( Data );
Data->Buffer = NULL;
__try {
//
// Obtain the length
//
Data->Length = SmbGetUlong( &Blob[Index] );
Index += sizeof( ULONG );
if ( Data->Length > 0 ) {
Data->Buffer = ( BYTE * )FtcAllocate( Data->Length );
if ( Data->Buffer == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in LsapUnmarshalData (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
RtlCopyMemory( Data->Buffer, &Blob[Index], Data->Length );
Index += Data->Length;
}
} __except ( EXCEPTION_EXECUTE_HANDLER ) {
Status = GetExceptionCode();
ASSERT( FALSE );
goto Error;
}
return Index;
Error:
FtcFree( Data->Buffer );
Data->Buffer = NULL;
RaiseException( Status, 0, 0, NULL );
return 0; // so the compiler doesn't complain
}
NTSTATUS
FTCache::MarshalBlob(
IN TDO_ENTRY * TdoEntry,
OUT ULONG * MarshaledSize,
OUT PBYTE * MarshaledBlob
)
/*++
Routine Description:
Marshals the given ForestTrustInfo structure into a string of bytes
Arguments:
TdoEntry Entry in the cache containing data to marshal
MarshaledSize Used to return the number of bytes in MarshaledBlob
MarshaledBlob Used to return the marshaled data
Returns:
STATUS_SUCCESS
STATUS_INVALID_PARAMETER
STATUS_INSUFFICIENT_RESOURCES
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG Length = 0;
BYTE * Blob = NULL;
ULONG Index;
ULONG Records = 0;
LIST_ENTRY * ListEntry;
if ( TdoEntry == NULL ||
MarshaledSize == NULL ||
MarshaledBlob == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in FTCache::MarshalBlob (%s:%d)\n", __FILE__, __LINE__ ));
return STATUS_INVALID_PARAMETER;
}
//
// First figure out how big the blob is going to be
//
//
// Version number is at the beginning of every blob
//
Length = sizeof( ULONG );
//
// Number of entries follows the version # of the blob
//
Length += sizeof( ULONG );
//
// Every record has certain common elements. Count them here.
//
Length += TdoEntry->RecordCount * (
//
// Length of the record
//
sizeof( ULONG ) +
//
// "Flags"
//
sizeof( ULONG ) +
//
// Timestamp
//
2 * sizeof( ULONG ) +
//
// Record type (no more tha 256 different values allowed)
//
sizeof( BYTE )
);
//
// Iterate over top level name entries
//
for ( ListEntry = TdoEntry->TlnList.Flink;
ListEntry != &TdoEntry->TlnList;
ListEntry = ListEntry->Flink ) {
TLN_ENTRY * TlnEntry;
TlnEntry = TLN_ENTRY::EntryFromTdoEntry( ListEntry );
//
// Ignore artifically inserted "pseudo" entries
//
if ( !( TlnEntry->Excluded ||
TlnEntry->SubordinateEntry == NULL )) {
continue;
}
ASSERT( Records < TdoEntry->RecordCount );
Records += 1;
__try {
Length += LsapMarshalString( NULL, &TlnEntry->TlnKey->TopLevelName );
} __except ( EXCEPTION_EXECUTE_HANDLER ) {
Status = GetExceptionCode();
break;
}
}
if ( !NT_SUCCESS( Status )) {
goto Error;
}
//
// Iterate over domain info entries
//
for ( ListEntry = TdoEntry->DomainInfoList.Flink;
ListEntry != &TdoEntry->DomainInfoList;
ListEntry = ListEntry->Flink ) {
DOMAIN_INFO_ENTRY * DomainInfoEntry;
DomainInfoEntry = DOMAIN_INFO_ENTRY::EntryFromTdoEntry( ListEntry );
ASSERT( Records < TdoEntry->RecordCount );
Records += 1;
__try {
Length += LsapMarshalSid( NULL, DomainInfoEntry->Sid );
Length += LsapMarshalString( NULL, &DomainInfoEntry->DnsKey->DnsName );
if ( DomainInfoEntry->NetbiosKey ) {
Length += LsapMarshalString( NULL, &DomainInfoEntry->NetbiosKey->NetbiosName );
} else {
UNICODE_STRING EmptyString = { 0, 0, NULL };
Length += LsapMarshalString( NULL, &EmptyString );
}
} __except ( EXCEPTION_EXECUTE_HANDLER ) {
Status = GetExceptionCode();
break;
}
}
if ( !NT_SUCCESS( Status )) {
goto Error;
}
//
// Iterate over binary entries
//
for ( ListEntry = TdoEntry->BinaryList.Flink;
ListEntry != &TdoEntry->BinaryList;
ListEntry = ListEntry->Flink ) {
BINARY_ENTRY * BinaryEntry;
BinaryEntry = BINARY_ENTRY::EntryFromTdoEntry( ListEntry );
ASSERT( Records < TdoEntry->RecordCount );
Records += 1;
//
// Space for the length
//
Length += sizeof( ULONG );
//
// Space for the data
//
Length += BinaryEntry->Data.Length;
}
ASSERT( NT_SUCCESS( Status ));
ASSERT( Records == TdoEntry->RecordCount );
//
// Now, allocate space and populate it
//
ASSERT( Length > 0 );
Blob = ( BYTE * )FtcAllocate( Length );
if ( Blob == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in FTCache::MarshalBlob (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
//
// Start with the version # of the blob
//
Index = 0;
SmbPutUlong( &Blob[Index], LSAP_FOREST_TRUST_BLOB_VERSION );
Index += sizeof( ULONG );
ASSERT( Index <= Length );
//
// Number of entries follows the version # of the blob
//
SmbPutUlong( &Blob[Index], TdoEntry->RecordCount );
Index += sizeof( ULONG );
ASSERT( Index <= Length );
//
// Iterate over top level name entries
//
for ( ListEntry = TdoEntry->TlnList.Flink;
ListEntry != &TdoEntry->TlnList;
ListEntry = ListEntry->Flink ) {
TLN_ENTRY * TlnEntry;
ULONG StartingIndex;
TlnEntry = TLN_ENTRY::EntryFromTdoEntry( ListEntry );
//
// Ignore artifically inserted "pseudo" entries
//
if ( !( TlnEntry->Excluded ||
TlnEntry->SubordinateEntry == NULL )) {
continue;
}
//
// Reserve space for the length
//
StartingIndex = Index;
Index += sizeof( ULONG );
//
// "Flags"
//
SmbPutUlong( &Blob[Index], TlnEntry->Flags());
Index += sizeof( ULONG );
ASSERT( Index <= Length );
//
// Timestamp
//
SmbPutUlong( &Blob[Index], TlnEntry->Time.HighPart );
Index += sizeof( ULONG );
ASSERT( Index <= Length );
SmbPutUlong( &Blob[Index], TlnEntry->Time.LowPart );
Index += sizeof( ULONG );
ASSERT( Index <= Length );
//
// Record type
//
Blob[Index] = ( BYTE )( TlnEntry->Excluded ?
ForestTrustTopLevelNameEx :
ForestTrustTopLevelName );
Index += sizeof( BYTE );
ASSERT( Index <= Length );
__try {
Index += LsapMarshalString( &Blob[Index], &TlnEntry->TlnKey->TopLevelName );
ASSERT( Index <= Length );
} __except ( EXCEPTION_EXECUTE_HANDLER ) {
Status = GetExceptionCode();
break;
}
//
// Remember the length
//
SmbPutUlong( &Blob[StartingIndex], Index - ( StartingIndex + sizeof( ULONG )));
}
if ( !NT_SUCCESS( Status )) {
goto Error;
}
//
// Iterate over domain info entries
//
for ( ListEntry = TdoEntry->DomainInfoList.Flink;
ListEntry != &TdoEntry->DomainInfoList;
ListEntry = ListEntry->Flink ) {
DOMAIN_INFO_ENTRY * DomainInfoEntry;
ULONG StartingIndex;
DomainInfoEntry = DOMAIN_INFO_ENTRY::EntryFromTdoEntry( ListEntry );
//
// Reserve space for the length
//
StartingIndex = Index;
Index += sizeof( ULONG );
//
// "Flags"
//
SmbPutUlong( &Blob[Index], DomainInfoEntry->Flags());
Index += sizeof( ULONG );
ASSERT( Index <= Length );
//
// Timestamp
//
SmbPutUlong( &Blob[Index], DomainInfoEntry->Time.HighPart );
Index += sizeof( ULONG );
ASSERT( Index <= Length );
SmbPutUlong( &Blob[Index], DomainInfoEntry->Time.LowPart );
Index += sizeof( ULONG );
ASSERT( Index <= Length );
//
// Record type
//
Blob[Index] = ( BYTE )( ForestTrustDomainInfo );
Index += sizeof( BYTE );
ASSERT( Index <= Length );
__try {
Index += LsapMarshalSid( &Blob[Index], DomainInfoEntry->Sid );
ASSERT( Index <= Length );
Index += LsapMarshalString( &Blob[Index], &DomainInfoEntry->DnsKey->DnsName );
ASSERT( Index <= Length );
if ( DomainInfoEntry->NetbiosKey ) {
Index += LsapMarshalString( &Blob[Index], &DomainInfoEntry->NetbiosKey->NetbiosName );
ASSERT( Index <= Length );
} else {
UNICODE_STRING EmptyString = { 0, 0, NULL };
Index += LsapMarshalString( &Blob[Index], &EmptyString );
ASSERT( Index <= Length );
}
} __except ( EXCEPTION_EXECUTE_HANDLER ) {
Status = GetExceptionCode();
break;
}
//
// Remember the length
//
SmbPutUlong( &Blob[StartingIndex], Index - ( StartingIndex + sizeof( ULONG )));
}
if ( !NT_SUCCESS( Status )) {
goto Error;
}
//
// Iterate over binary entries
//
for ( ListEntry = TdoEntry->BinaryList.Flink;
ListEntry != &TdoEntry->BinaryList;
ListEntry = ListEntry->Flink ) {
BINARY_ENTRY * BinaryEntry;
ULONG StartingIndex;
BinaryEntry = BINARY_ENTRY::EntryFromTdoEntry( ListEntry );
//
// Reserve space for the length
//
StartingIndex = Index;
Index += sizeof( ULONG );
//
// "Flags"
//
SmbPutUlong( &Blob[Index], BinaryEntry->Flags());
Index += sizeof( ULONG );
ASSERT( Index <= Length );
//
// Timestamp
//
SmbPutUlong( &Blob[Index], BinaryEntry->Time.HighPart );
Index += sizeof( ULONG );
ASSERT( Index <= Length );
SmbPutUlong( &Blob[Index], BinaryEntry->Time.LowPart );
Index += sizeof( ULONG );
ASSERT( Index <= Length );
//
// Record type
//
Blob[Index] = ( BYTE )( BinaryEntry->Type );
Index += sizeof( BYTE );
ASSERT( Index <= Length );
//
// Blob length
//
SmbPutUlong( &Blob[Index], BinaryEntry->Data.Length );
Index += sizeof( ULONG );
ASSERT( Index <= Length );
//
// Blob data
//
RtlCopyMemory( &Blob[Index], BinaryEntry->Data.Buffer, BinaryEntry->Data.Length );
Index += BinaryEntry->Data.Length;
ASSERT( Index <= Length );
//
// Remember the record length
//
SmbPutUlong( &Blob[StartingIndex], Index - ( StartingIndex + sizeof( ULONG )));
}
ASSERT( Status == STATUS_SUCCESS );
Cleanup:
*MarshaledSize = Length;
*MarshaledBlob = Blob;
return Status;
Error:
ASSERT( !NT_SUCCESS( Status ));
FtcFree( Blob );
Blob = NULL;
Length = 0;
goto Cleanup;
}
NTSTATUS
LsapForestTrustUnmarshalBlob(
IN ULONG Length,
IN BYTE * Blob,
IN LSA_FOREST_TRUST_RECORD_TYPE HighestRecordType,
OUT LSA_FOREST_TRUST_INFORMATION * ForestTrustInfo
)
/*++
Routine Description:
Takes a marshalled blob and makes sense of it by extracting a coherent
forest trust information structure
Arguments:
Length Length of the buffer pointed to by Blob
Blob Marshaled data
HighestRecordType Highest record type the client understands
ForestTrustInfo Used to return the unmarshaled information
Returns:
STATUS_SUCCESS
STATUS_INVALID_PARAMETER
STATUS_INSUFFICIENT_RESOURCES
STATUS_UNKNOWN_REVISION The forest trust blob's version is unrecognized
--*/
{
NTSTATUS Status;
ULONG Index = 0;
ULONG i = 0;
//
// ISSUE-2000/07/21-markpu
// highest record type is as yet unused
//
UNREFERENCED_PARAMETER( HighestRecordType );
if ( Blob == NULL ||
ForestTrustInfo == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in LsapForestTrustUnmarshalBlob (%s:%d)\n", __FILE__, __LINE__ ));
return STATUS_INVALID_PARAMETER;
}
ForestTrustInfo->RecordCount = 0;
ForestTrustInfo->Entries = NULL;
__try {
ULONG Version;
//
// Retrieve and check the version # of the blob
//
Version = SmbGetUlong( &Blob[Index] );
Index += sizeof( ULONG );
if ( Index > Length ) {
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in LsapForestTrustUnmarshalBlob (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
ASSERT( FALSE );
goto Error;
}
if ( Version != LSAP_FOREST_TRUST_BLOB_VERSION ) {
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in LsapForestTrustUnmarshalBlob (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_UNKNOWN_REVISION;
goto Error;
}
//
// Retrieve the number of entries and allocate space for the array
//
ForestTrustInfo->RecordCount = SmbGetUlong( &Blob[Index] );
Index += sizeof( ULONG );
ASSERT( Index <= Length );
if ( Index > Length ) {
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in LsapForestTrustUnmarshalBlob (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
ASSERT( FALSE );
goto Error;
}
if ( ForestTrustInfo->RecordCount > 0 ) {
ForestTrustInfo->Entries = ( LSA_FOREST_TRUST_RECORD * * )FtcAllocate(
ForestTrustInfo->RecordCount *
sizeof( LSA_FOREST_TRUST_RECORD * )
);
if ( ForestTrustInfo->Entries == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in LsapForestTrustUnmarshalBlob (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
}
for ( i = 0 ; i < ForestTrustInfo->RecordCount ; i++ ) {
LSA_FOREST_TRUST_RECORD * Record;
ULONG RecordLength;
ULONG StartingIndex;
Record = ForestTrustInfo->Entries[i] = ( LSA_FOREST_TRUST_RECORD * )FtcAllocate(
sizeof( LSA_FOREST_TRUST_RECORD )
);
if ( Record == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in LsapForestTrustUnmarshalBlob (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
StartingIndex = Index;
RecordLength = SmbGetUlong( &Blob[Index] );
Index += sizeof( ULONG );
if ( Index > Length ) {
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in LsapForestTrustUnmarshalBlob (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
ASSERT( FALSE );
goto Error;
}
Record->Flags = SmbGetUlong( &Blob[Index] );
Index += sizeof( ULONG );
if ( Index > Length ) {
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in LsapForestTrustUnmarshalBlob (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
ASSERT( FALSE );
goto Error;
}
Record->Time.HighPart = SmbGetUlong( &Blob[Index] );
Index += sizeof( ULONG );
if ( Index > Length ) {
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in LsapForestTrustUnmarshalBlob (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
ASSERT( FALSE );
goto Error;
}
Record->Time.LowPart = SmbGetUlong( &Blob[Index] );
Index += sizeof( ULONG );
if ( Index > Length ) {
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in LsapForestTrustUnmarshalBlob (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
ASSERT( FALSE );
goto Error;
}
Record->ForestTrustType = ( LSA_FOREST_TRUST_RECORD_TYPE )Blob[Index];
Index += sizeof( BYTE );
if ( Index > Length ) {
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in LsapForestTrustUnmarshalBlob (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
ASSERT( FALSE );
goto Error;
}
switch ( Record->ForestTrustType ) {
case ForestTrustTopLevelName:
case ForestTrustTopLevelNameEx:
Index += LsapUnmarshalString( &Blob[Index], &Record->ForestTrustData.TopLevelName );
if ( Index > Length ) {
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in LsapForestTrustUnmarshalBlob (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
ASSERT( FALSE );
goto Error;
}
break;
case ForestTrustDomainInfo:
Index += LsapUnmarshalSid( &Blob[Index], ( PISID * )&Record->ForestTrustData.DomainInfo.Sid );
if ( Index > Length ) {
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in LsapForestTrustUnmarshalBlob (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
ASSERT( FALSE );
goto Error;
}
Index += LsapUnmarshalString( &Blob[Index], &Record->ForestTrustData.DomainInfo.DnsName );
if ( Index > Length ) {
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in LsapForestTrustUnmarshalBlob (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
ASSERT( FALSE );
goto Error;
}
Index += LsapUnmarshalString( &Blob[Index], &Record->ForestTrustData.DomainInfo.NetbiosName );
if ( Index > Length ) {
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in LsapForestTrustUnmarshalBlob (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
ASSERT( FALSE );
goto Error;
}
break;
default:
Index += LsapUnmarshalData(
&Blob[Index],
&Record->ForestTrustData.Data
);
if ( Index > Length ) {
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in LsapForestTrustUnmarshalBlob (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
ASSERT( FALSE );
goto Error;
}
break;
}
//
// Cross-check the reported record length
//
ASSERT( RecordLength == Index - ( StartingIndex + sizeof( ULONG )));
}
} __except ( EXCEPTION_EXECUTE_HANDLER ) {
Status = GetExceptionCode();
if ( Status == STATUS_ACCESS_VIOLATION ) {
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in LsapForestTrustUnmarshalBlob (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
ASSERT( FALSE ); // this indicates a bug
}
goto Error;
}
Status = STATUS_SUCCESS;
Cleanup:
return Status;
Error:
ASSERT( !NT_SUCCESS( Status ));
if ( ForestTrustInfo != NULL ) {
ForestTrustInfo->RecordCount = i;
LsapFreeForestTrustInfo( ForestTrustInfo );
}
goto Cleanup;
}
///////////////////////////////////////////////////////////////////////////////
//
// Routines for manipulating local forest information
//
///////////////////////////////////////////////////////////////////////////////
NTSTATUS
LsapAddTreeTrustInfo(
IN OUT PNL_FTINFO_CONTEXT Context,
IN PLSAPR_TREE_TRUST_INFO Tti
)
/*++
Routine Description:
Adds information about the subtree described by 'Tti' to the given context
Calls itself recursively for child trees of 'Tti'
Arguments:
Context context to add to
Tti structure describing a domain subtree
Returns:
STATUS_SUCCESS
STATUS_INSUFFICIENT_RESOURCES
--*/
{
NTSTATUS Status;
ULONG i;
ASSERT( Context );
ASSERT( Tti );
//
// Add a TLN entry.
// Subordinate TLN entries are expunged as necessary
//
if ( FALSE == NetpAddTlnFtinfoEntry(
Context,
&Tti->DnsDomainName )) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in LsapAddTreeTrustInfo (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
//
// Add a domain entry
//
if ( Tti->DomainSid == NULL ) {
//
// "Young" X-Ref objects might exist without a SID (because a new
// child domain X-Ref is created without a SID, a SID is added later.
// Domain info entries without SIDs are not considered valid, so for
// the time being, do not insert the domain info entry for this X-Ref.
// Later, the X-Ref will have the SID added to it, at which point
// replication will take care of inserting the proper information into
// the tree.
//
LsapDsDebugOut(( DEB_FTINFO, "LsapAddTreeTrustInfo: skipping domain info entry for %wZ\n", &Tti->DnsDomainName, __FILE__, __LINE__ ));
} else if ( FALSE == NetpAllocFtinfoEntry(
Context,
ForestTrustDomainInfo,
&Tti->DnsDomainName,
Tti->DomainSid,
&Tti->FlatName )) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in LsapAddTreeTrustInfo (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
//
// Call ourselves recursively for every child domain
//
for ( i = 0 ; i < Tti->Children ; i++ ) {
Status = LsapAddTreeTrustInfo(
Context,
&Tti->ChildDomains[i]
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsapAddTreeTrustInfo: LslapAddTreeTrustInfo returned 0x%x\n", Status ));
goto Error;
}
}
Status = STATUS_SUCCESS;
Cleanup:
return Status;
Error:
ASSERT( !NT_SUCCESS( Status ));
goto Cleanup;
}
NTSTATUS
LsapGetForestTrustInformation(
IN BOOLEAN RootOnly,
OUT PLSA_FOREST_TRUST_INFORMATION *ForestTrustInfo
)
/*++
Routine Description:
Worker routine to get the ForestTrustInformation array for the local domain.
Arguments:
RootOnly - if TRUE, operation is only allowed in the root domain of the forest
ForestTrustInfo - Returns a pointer to a structure containing a count and an
array of FTInfo records describing the namespaces claimed by the
domain specified by TrustedDomainName. The Accepted field and Time
field of all returned records will be zero. The buffer should be freed
by calling MIDL_user_free.
Return Value:
STATUS_SUCCESS
STATUS_INSUFFICIENT_RESOURCES out of memory
STATUS_INVALID_DOMAIN_STATE must be called on a DC in a root domain
--*/
{
NTSTATUS Status;
ULONG i;
PLSAP_UPN_SUFFIXES UpnSuffixes = NULL;
PLSAPR_FOREST_TRUST_INFO Fti = NULL;
NL_FTINFO_CONTEXT Context;
//
// Initialization
//
*ForestTrustInfo = NULL;
NetpInitFtinfoContext( &Context );
//
// First get a list of all the domains in the forest.
//
Status = LsaIQueryForestTrustInfo(
LsapPolicyHandle,
&Fti
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsapGetForestTrustInformation: LsaIQueryForestTrustInfo returned 0x%x\n", Status ));
goto Error;
}
if ( Fti->ParentDomainReference != NULL && RootOnly ) {
//
// Operation only allowed on the root domain of the forest
// If parent domain reference exists, we are not the root domain
//
Status = STATUS_INVALID_DOMAIN_STATE;
goto Error;
}
//
// Add TLN entries and domain info entries from the forest
//
Status = LsapAddTreeTrustInfo(
&Context,
&Fti->RootTrust
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsapGetForestTrustInformation: LsapAddTreeTrustInfo returned 0x%x\n", Status ));
goto Error;
}
//
// Get the list of UPN and SPN suffixes
//
Status = LsaIQueryUpnSuffixes( &UpnSuffixes );
if ( !NT_SUCCESS(Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsapGetForestTrustInformation: LsaIQueryUpnSuffixes returned 0x%x\n", Status ));
goto Error;
}
//
// Add each UPN/SPN suffix as a TLN
//
for ( i = 0 ; i < UpnSuffixes->SuffixCount ; i++ ) {
UNICODE_STRING * UpnSuffix = &UpnSuffixes->Suffixes[i];
BOOLEAN Valid;
//
// All sorts of strings can claim to be UPN suffixes.
// Only allow those that pass our validation checking.
//
LsapValidateDnsName( UpnSuffix, &Valid );
if ( !Valid ) {
continue;
}
if ( !NetpAddTlnFtinfoEntry(
&Context,
UpnSuffix )) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in LsapGetForestTrustInformation (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
}
//
// Return the collected entries to the caller.
//
*ForestTrustInfo = NetpCopyFtinfoContext( &Context );
if ( *ForestTrustInfo == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in LsapGetForestTrustInformation (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
} else if ( !LsapValidateForestTrustInfo( *ForestTrustInfo )) {
LsapDsDebugOut(( DEB_FTINFO, "LsapGetForestTrustInformation: Generated forest trust information internally inconsistent\n" ));
Status = STATUS_INTERNAL_DB_ERROR;
goto Error;
}
Cleanup:
NetpCleanFtinfoContext( &Context );
LsaIFree_LSAP_UPN_SUFFIXES( UpnSuffixes );
return Status;
Error:
ASSERT( !NT_SUCCESS( Status ));
goto Cleanup;
}
NTSTATUS
LsaIGetForestTrustInformation(
OUT PLSA_FOREST_TRUST_INFORMATION *ForestTrustInfo
)
/*++
Routine Description:
Worker routine to get the ForestTrustInformation array for the local domain.
Arguments:
ForestTrustInfo - Returns a pointer to a structure containing a count and an
array of FTInfo records describing the namespaces claimed by the
domain specified by TrustedDomainName. The Accepted field and Time
field of all returned records will be zero. The buffer should be freed
by calling MIDL_user_free.
Return Value:
STATUS_SUCCESS
STATUS_INSUFFICIENT_RESOURCES out of memory
STATUS_INVALID_DOMAIN_STATE must be called on a DC in a root domain
--*/
{
return LsapGetForestTrustInformation( TRUE, ForestTrustInfo );
}
NTSTATUS
LsaIUpdateForestTrustInformation(
IN LSAPR_HANDLE PolicyHandle,
IN UNICODE_STRING * TrustedDomainName,
IN PLSA_FOREST_TRUST_INFORMATION NewForestTrustInfo
)
/*++
Routine Description:
This function write the specified NewForestTrustInfo onto the named TDO.
The NewForestTrustInfo is merged with the exsiting information using the following algorithm:
The FTinfo records written are described in the NetpMergeFTinfo routine.
Arguments:
PolicyHandle - open policy handle
TrustedDomainName - Trusted domain that is to be updated. This domain must have the
TRUST_ATTRIBUTE_FOREST_TRANSITIVE bit set.
NewForestTrustInfo - Specified the new array of FTinfo records as returned from the
trusted domain.
Return Value:
STATUS_SUCCESS: Success.
STATUS_INVALID_PARAMETER NewForestTrustInfo passed in is incorrect
--*/
{
NTSTATUS Status;
PLSA_FOREST_TRUST_INFORMATION OldForestTrustInfo = NULL;
PLSA_FOREST_TRUST_INFORMATION MergedForestTrustInfo = NULL;
PLSA_FOREST_TRUST_COLLISION_INFORMATION CollisionInfo = NULL;
if ( !LsapValidateForestTrustInfo( NewForestTrustInfo )) {
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in LsaIUpdateForestTrustInformation (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
goto Error;
}
Status = LsarQueryForestTrustInformation(
PolicyHandle,
TrustedDomainName,
ForestTrustRecordTypeLast,
&OldForestTrustInfo
);
if ( Status == STATUS_NOT_FOUND ) {
//
// If there's no FTinfo on the TDO,
// that's OK.
//
OldForestTrustInfo = NULL;
Status = STATUS_SUCCESS;
} else if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsaIUpdateForestTrustInformation: %wZ: LsarQueryForestTrustInformation returned 0x%lx\n", TrustedDomainName, Status));
goto Error;
}
//
// Compute the merged FTINFO
//
Status = NetpMergeFtinfo(
TrustedDomainName,
NewForestTrustInfo,
OldForestTrustInfo,
&MergedForestTrustInfo
);
if ( !NT_SUCCESS(Status) ) {
LsapDsDebugOut(( DEB_FTINFO, "LsaIUpdateForestTrustInformation: %wZ: NetpMergeTrustInformation returned 0x%lx\n", TrustedDomainName, Status));
goto Error;
}
ASSERT( LsapValidateForestTrustInfo( MergedForestTrustInfo ));
//
// Write the merge FTINFO back to the LSA
//
Status = LsarSetForestTrustInformation(
PolicyHandle,
TrustedDomainName,
ForestTrustRecordTypeLast,
MergedForestTrustInfo,
FALSE, // This is not a read-only call - write updated data out
&CollisionInfo
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsaIUpdateForestTrustInformation: %wZ: LsarSetForestTrustInformation returned 0x%lx\n", TrustedDomainName, Status));
goto Error;
}
Cleanup:
LsaIFree_LSA_FOREST_TRUST_INFORMATION( &OldForestTrustInfo );
LsaIFree_LSA_FOREST_TRUST_COLLISION_INFORMATION( &CollisionInfo );
MIDL_user_free( MergedForestTrustInfo );
return Status;
Error:
ASSERT( !NT_SUCCESS( Status ));
goto Cleanup;
}
NTSTATUS NTAPI
LsaIForestTrustFindMatch(
IN LSA_ROUTING_MATCH_TYPE Type,
IN PVOID Data,
OUT PLSA_UNICODE_STRING Match
)
/*++
Routine Description:
Finds match for given data in the cache
Arguments:
Type Type of Data parameter
Data Data to match
Match Used to return the match, if found.
Caller must use LsaIFree_LSAPR_UNICODE_STRING_BUFFER
Returns:
STATUS_SUCCESS Match was found
STATUS_NO_MATCH Match was not found
STATUS_INVALID_DOMAIN_STATE Machine must be a GC or a DC in the root domain
STATUS_INVALID_PARAMETER Check the inputs
STATUS_INTERNAL_ERROR Cache is internally inconsistent
STATUS_INSUFFICIENT_RESOURCES Out of memory
--*/
{
if ( !LsapDbDcInRootDomain()) {
//
// Bummer. This domain controller is not part of the root DC.
// We can only perform matching if this DC is also a GC
//
if ( !SamIAmIGC()) {
return STATUS_INVALID_DOMAIN_STATE;
}
}
return LsapForestTrustFindMatch( Type, Data, FALSE, Match, NULL );
}
NTSTATUS
LsapForestTrustInsertLocalInfo(
)
/*++
Routine Description:
Inserts information about the local forest into the cache
Arguments:
None
Returns:
NTSTATUS - Standard Nt Result Code
--*/
{
NTSTATUS Status;
PLSA_FOREST_TRUST_INFORMATION LocalForestTrustInfo = NULL;
PPOLICY_DNS_DOMAIN_INFO PolicyDnsDomainInfo = NULL;
//
// Insert the data for the local forest into the cache
//
Status = LsapGetForestTrustInformation(
FALSE,
&LocalForestTrustInfo
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsapForestTrustInsertLocalInfo: LsapGetForestTrustInformation returned 0x%x\n", Status ));
goto Error;
}
//
// Need the local domain SID and DNS domain name
//
Status = LsapDbQueryInformationPolicy(
LsapPolicyHandle,
PolicyDnsDomainInformation,
( PLSAPR_POLICY_INFORMATION *)&PolicyDnsDomainInfo
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsapForestTrustInsertLocalInfo: LsapDbQueryInformationPolicy returned 0x%x\n", Status ));
goto Error;
}
Status = LsapForestTrustCacheInsert(
&PolicyDnsDomainInfo->DnsDomainName,
PolicyDnsDomainInfo->Sid,
LocalForestTrustInfo,
TRUE
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsapForestTrustInsertLocalInfo: LsapForestTrustCacheInsert returned 0x%x\n", Status ));
goto Error;
}
LsapForestTrustCacheSetLocalValid();
Cleanup:
LsaIFree_LSAPR_POLICY_INFORMATION(
PolicyDnsDomainInformation,
( PLSAPR_POLICY_INFORMATION )PolicyDnsDomainInfo
);
MIDL_user_free( LocalForestTrustInfo );
return Status;
Error:
ASSERT( !NT_SUCCESS( Status ));
goto Cleanup;
}
///////////////////////////////////////////////////////////////////////////////
//
// Exported set/query/match routines
//
///////////////////////////////////////////////////////////////////////////////
BOOLEAN
LsapHavingForestTrustMakesSense(
IN ULONG TrustDirection,
IN ULONG TrustType,
IN ULONG TrustAttributes
)
/*++
Routine Description:
Determines whether the TDO is of the kind that may contain
forest trust information
Arguments:
TrustDirection
TrustType
TrustAttributes
Returns:
TRUE/FALSE
--*/
{
UNREFERENCED_PARAMETER( TrustDirection );
UNREFERENCED_PARAMETER( TrustType );
if ( !LsapDbNoMoreWin2KForest()) {
//
// If we are not in .NET forest mode, behave as if the forest transitive
// bit did not exist
//
TrustAttributes &= ~TRUST_ATTRIBUTE_FOREST_TRANSITIVE;
}
return ( 0 != ( TrustAttributes & TRUST_ATTRIBUTE_FOREST_TRANSITIVE ));
}
NTSTATUS
LsarQueryForestTrustInformation(
IN LSAPR_HANDLE PolicyHandle,
IN LSA_UNICODE_STRING * TrustedDomainName,
IN LSA_FOREST_TRUST_RECORD_TYPE HighestRecordType,
OUT PLSA_FOREST_TRUST_INFORMATION * ForestTrustInfo
)
/*++
Routine Description
The LsarQueryForestTrustInformation API returns forest trust information
for the given trusted domain object.
Arguments:
PolicyHandle - An open handle to a Policy object
TrustedDomainName - Name of the trusted domain object
HighestRecordType - Highest enum value recognized by the client
ForestTrustInfo - Used to return forest trust information
Returns:
NTSTATUS - Standard Nt Result Code
STATUS_SUCCESS
STATUS_INVALID_PARAMETER Parameters were somehow invalid
Most likely, the TRUST_ATTRIBUTE_FOREST_TRANSITIVE
trust attribute bit is not set on the TDO
STATUS_NOT_FOUND Forest trust information does not exist for this TDO
STATUS_NO_SUCH_DOMAIN The specified TDO does not exist
STATUS_INSUFFICIENT_RESOURCES Ran out of memory
STATUS_INVALID_DOMAIN_STATE Operation is only legal on domain controllers in root domain
--*/
{
NTSTATUS Status;
LSAP_DB_OBJECT_INFORMATION ObjInfo;
LSAPR_HANDLE TrustedDomainHandle = NULL;
BOOLEAN CacheLocked = FALSE;
BOOLEAN ObjectReferenced = FALSE;
BOOLEAN TrustedDomainListLocked = FALSE;
LSAP_DB_TRUSTED_DOMAIN_LIST_ENTRY * TrustInfoForName;
LsarpReturnCheckSetup();
LsapEnterFunc( "LsarQueryForestTrustInformation" );
LsapTraceEvent( EVENT_TRACE_TYPE_START, LsaTraceEvent_QueryForestTrustInformation );
//
// Validate the input buffer
//
if ( ForestTrustInfo == NULL ||
!LsapValidateLsaUnicodeString( TrustedDomainName )) {
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in LsarQueryForestTrustInformation (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
goto Error;
}
*ForestTrustInfo = NULL;
//
// Under current design, only domains in the root domain of the forest
// can have forest trust information
//
if ( !LsapDbDcInRootDomain()) {
LsapDsDebugOut(( DEB_FTINFO, "LsarQueryForestTrustInformation: must be a DC in the root domain\n" ));
Status = STATUS_INVALID_DOMAIN_STATE;
goto Error;
}
//
// Cross-forest trust will not work until all domain controllerss
// in the forest have been upgraded to Whistler
//
if ( !LsapDbNoMoreWin2KForest()) {
LsapDsDebugOut(( DEB_FTINFO, "LsarQueryForestTrustInformation: must have an all-Whistler forest\n" ));
Status = STATUS_INVALID_DOMAIN_STATE;
goto Error;
}
Status = LsapDbReferenceObject(
PolicyHandle,
0,
PolicyObject,
TrustedDomainObject,
LSAP_DB_LOCK |
LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_DS_OP_TRANSACTION
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsarQueryForestTrustInformation: LsapDbReferenceObject returned 0x%x\n", Status ));
goto Error;
}
ObjectReferenced = TRUE;
//
// Get the right name. Entries in the forest trust cache
// are located by full name name, NOT flat name
//
Status = LsapDbAcquireReadLockTrustedDomainList();
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsarQueryForestTrustInformation: LsapDbAcquireReadLockTrustedDomainList returned 0x%x\n", Status ));
goto Error;
}
TrustedDomainListLocked = TRUE;
//
// Get the right name
//
Status = LsapDbLookupNameTrustedDomainListEx(
( LSAPR_UNICODE_STRING * )TrustedDomainName,
&TrustInfoForName
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsarQueryForestTrustInformation: No trust entry found for %wZ: 0x%lx\n", ( UNICODE_STRING * )TrustedDomainName, Status ));
goto Error;
}
if ( !LsapHavingForestTrustMakesSense(
TrustInfoForName->TrustInfoEx.TrustDirection,
TrustInfoForName->TrustInfoEx.TrustType,
TrustInfoForName->TrustInfoEx.TrustAttributes )) {
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in LsarQueryForestTrustInformation (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
goto Error;
}
//
// Build a temporary handle
//
// ISSUE-2000/07/27-markpu
// do we need to open the object?
// we don't use the handle for anything
// except verifying access (TRUSTED_QUERY_AUTH)
//
//
// For purposes of forest trust, entries are identified by full name only
//
RtlZeroMemory( &ObjInfo, sizeof( ObjInfo ));
ObjInfo.ObjectTypeId = TrustedDomainObject;
ObjInfo.ContainerTypeId = NullObject;
ObjInfo.Sid = NULL;
ObjInfo.DesiredObjectAccess = TRUSTED_QUERY_AUTH;
InitializeObjectAttributes(
&ObjInfo.ObjectAttributes,
( UNICODE_STRING * )&TrustInfoForName->TrustInfoEx.Name,
0L,
PolicyHandle,
NULL
);
//
// Get a handle to the TDO
//
Status = LsapDbOpenObject(
&ObjInfo,
TRUSTED_QUERY_AUTH,
0,
&TrustedDomainHandle
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsarQueryForestTrustInformation: LsapDbOpenObject returned 0x%x\n", Status ));
goto Error;
}
Status = LsapForestTrustCacheRetrieve(
( UNICODE_STRING * )&TrustInfoForName->TrustInfoEx.Name,
ForestTrustInfo
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsarQueryForestTrustInformation: LsapForestTrustCacheRetrieve returned 0x%x\n", Status ));
goto Error;
}
ASSERT( LsapValidateForestTrustInfo( *ForestTrustInfo ));
Cleanup:
if ( TrustedDomainHandle != NULL ) {
LsapDbCloseObject(
&TrustedDomainHandle,
0,
Status
);
}
if ( TrustedDomainListLocked ) {
LsapDbReleaseLockTrustedDomainList();
}
//
// Dereference the object
//
if ( ObjectReferenced ) {
Status = LsapDbDereferenceObject(
&PolicyHandle,
PolicyObject,
TrustedDomainObject,
LSAP_DB_LOCK |
LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_DS_OP_TRANSACTION,
(SECURITY_DB_DELTA_TYPE) 0,
Status
);
}
LsapTraceEvent( EVENT_TRACE_TYPE_END, LsaTraceEvent_QueryForestTrustInformation );
LsapExitFunc( "LsarQueryForestTrustInformation", Status );
LsarpReturnPrologue();
return Status;
Error:
ASSERT( !NT_SUCCESS( Status ));
goto Cleanup;
}
NTSTATUS
LsarSetForestTrustInformation(
IN LSAPR_HANDLE PolicyHandle,
IN LSA_UNICODE_STRING * TrustedDomainName,
IN LSA_FOREST_TRUST_RECORD_TYPE HighestRecordType,
IN LSA_FOREST_TRUST_INFORMATION * ForestTrustInfo,
IN BOOLEAN CheckOnly,
OUT PLSA_FOREST_TRUST_COLLISION_INFORMATION * CollisionInfo
)
/*++
Routine Description
The LsarSetForestTrustInformation API sets forest trust information
on the given trusted domain object.
In case if it fails the operation due to a collision, it will return
the list of entries that conflicted.
Arguments:
PolicyHandle - An open handle to a Policy object
TrustedDomainName - Name of the trusted domain object
HighestRecordType - Highest enum value recognized by the client
ForestTrustInfo - Contains forest trust information to set
If RecordCount is 0, current forest trust information
will be deleted
CheckOnly - if TRUE, perform collision detection only without actually
committing anything to permanent storage
CollisionInfo - In case of collision error, used to return collision info
Returns:
STATUS_SUCCESS
STATUS_INVALID_PARAMETER Parameters were somehow invalid
STATUS_INSUFFICIENT_RESOURCES Ran out of memory
STATUS_INVALID_DOMAIN_STATE Operation is only legal on domain controllers in root domain
STATUS_INVALID_DOMAIN_ROLE Operation is only legal on the primary domain controller
STATUS_INVALID_SERVER_STATE The server is shutting down and can not
process the request
STATUS_PARAMETER_QUOTA_EXCEEDED Too many FTInfo entries
--*/
{
NTSTATUS Status;
LSAP_DB_OBJECT_INFORMATION ObjInfo;
LSAPR_HANDLE TrustedDomainHandle = NULL;
BOOLEAN ObjectReferenced = FALSE;
BOOLEAN TrustedDomainListLocked = FALSE;
LSAP_DB_TRUSTED_DOMAIN_LIST_ENTRY * TrustInfoForName = NULL;
LSAP_DB_ATTRIBUTE Attributes[LSAP_DB_ATTRS_INFO_CLASS_DOMAIN];
LSAP_DB_ATTRIBUTE * NextAttribute;
ULONG AttributeCount = 0;
ULONG BlobLength = 0;
BYTE * BlobData = NULL;
FTCache::TDO_ENTRY TdoEntryOld = {0};
FTCache::TDO_ENTRY * TdoEntryNew = NULL;
FTCache::CONFLICT_PAIR * ConflictPairs = NULL;
ULONG ConflictPairsTotal = 0;
DOMAIN_SERVER_ROLE ServerRole;
LsarpReturnCheckSetup();
LsapEnterFunc( "LsarSetForestTrustInformation" );
LsapTraceEvent( EVENT_TRACE_TYPE_START, LsaTraceEvent_SetForestTrustInformation );
//
// Top level pointers defer to [ref] and can not be NULL
//
ASSERT( ForestTrustInfo != NULL );
ASSERT( CollisionInfo != NULL );
//
// Prevent forest trust blobs over MAX_RECORDS_IN_FOREST_TRUST_INFO
// records
//
if ( ForestTrustInfo->RecordCount > MAX_RECORDS_IN_FOREST_TRUST_INFO ) {
LsapDsDebugOut(( DEB_FTINFO, "Too many entries in LsarSetForestTrustInformation (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_PARAMETER_QUOTA_EXCEEDED;
goto Error;
}
//
// Validate the input data
//
if ( !LsapValidateLsaUnicodeString( TrustedDomainName ) ||
!LsapValidateForestTrustInfo( ForestTrustInfo )) {
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in LsarSetForestTrustInformation (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
goto Error;
}
*CollisionInfo = NULL;
//
// Under current design, only domains in the root domain of the forest
// can have forest trust information set on them.
//
if ( !LsapDbDcInRootDomain()) {
LsapDsDebugOut(( DEB_FTINFO, "LsarSetForestTrustInformation: must be a DC in the root domain\n" ));
Status = STATUS_INVALID_DOMAIN_STATE;
goto Error;
}
//
// Furthermore, forest trust information can only be set on the primary
// domain controller
//
ASSERT( LsapAccountDomainHandle );
Status = SamIQueryServerRole(
LsapAccountDomainHandle,
&ServerRole
);
if ( !NT_SUCCESS(Status)) {
LsapDsDebugOut(( DEB_FTINFO, "LsarSetForestTrustInformation: SamIQueryServerRole returned 0x%x\n", Status ));
goto Error;
} else if ( ServerRole != DomainServerRolePrimary ) {
LsapDsDebugOut(( DEB_FTINFO, "LsarSetForestTrustInformation: must be a PDC in the root domain\n" ));
Status = STATUS_INVALID_DOMAIN_ROLE;
goto Error;
}
//
// Cross-forest trust will not work until all domain controllers
// in the forest have been upgraded to Whistler
//
if ( !LsapDbNoMoreWin2KForest()) {
LsapDsDebugOut(( DEB_FTINFO, "LsarSetForestTrustInformation: must have an all-Whistler forest\n" ));
Status = STATUS_INVALID_DOMAIN_STATE;
goto Error;
}
//
// The client should not understand more than we do:
// the RPC interface should've stopped them
//
ASSERT( HighestRecordType <= ForestTrustRecordTypeLast );
Status = LsapDbReferenceObject(
PolicyHandle,
0,
PolicyObject,
TrustedDomainObject,
LSAP_DB_LOCK
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsarSetForestTrustInformation: LsapDbReferenceObject returned 0x%x\n", Status ));
goto Error;
}
ObjectReferenced = TRUE;
//
// Lock the Trusted Domain List for the duration of the operation,
// since we'll be making changes to it here
//
Status = LsapDbAcquireWriteLockTrustedDomainList();
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsarSetForestTrustInformation: LsapDbAcquireWriteLockTrustedDomainList returned 0x%x\n", Status ));
goto Error;
}
TrustedDomainListLocked = TRUE;
//
// Get the right name
//
Status = LsapDbLookupNameTrustedDomainListEx(
( LSAPR_UNICODE_STRING * )TrustedDomainName,
&TrustInfoForName
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsarSetForestTrustInformation: No trust entry found for %wZ: 0x%lx\n", ( UNICODE_STRING * )TrustedDomainName, Status ));
goto Error;
}
//
// Build a temporary handle
//
RtlZeroMemory( &ObjInfo, sizeof( ObjInfo ));
ObjInfo.ObjectTypeId = TrustedDomainObject;
ObjInfo.ContainerTypeId = NullObject;
ObjInfo.Sid = NULL;
ObjInfo.DesiredObjectAccess = TRUSTED_SET_AUTH;
InitializeObjectAttributes(
&ObjInfo.ObjectAttributes,
( UNICODE_STRING * )&TrustInfoForName->TrustInfoEx.Name,
0L,
PolicyHandle,
NULL
);
//
// Get a handle to the TDO
//
Status = LsapDbOpenObject(
&ObjInfo,
TRUSTED_SET_AUTH,
0,
&TrustedDomainHandle
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsarSetForestTrustInformation: LsapDbOpenObject returned 0x%x\n", Status ));
goto Error;
}
if ( ForestTrustInfo->RecordCount > 0 ) {
if ( !LsapHavingForestTrustMakesSense(
TrustInfoForName->TrustInfoEx.TrustDirection,
TrustInfoForName->TrustInfoEx.TrustType,
TrustInfoForName->TrustInfoEx.TrustAttributes )) {
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in LsarSetForestTrustInformation (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
goto Error;
}
//
// Insert the entry into the cache.
// This is the first part of the insert operation:
// -- conflicts are not resolved
// -- old copy of the data is preserved in case
// the operation needs to be rolled back later
//
Status = g_FTCache->Insert(
( UNICODE_STRING * )&TrustInfoForName->TrustInfoEx.Name,
( PSID )TrustInfoForName->TrustInfoEx.Sid,
ForestTrustInfo,
FALSE,
&TdoEntryOld,
&TdoEntryNew,
&ConflictPairs,
&ConflictPairsTotal
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsarSetForestTrustInformation: FTCache::Insert returned 0x%x\n", Status ));
goto Error;
}
ASSERT( TdoEntryNew );
//
// The client wants to know what the deal is with conflicts
//
if ( ConflictPairs != NULL ) {
Status = FTCache::GenerateConflictInfo(
ConflictPairs,
ConflictPairsTotal,
TdoEntryNew,
CollisionInfo
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsarSetForestTrustInformation: FTCache::GenerateConflictInfo returned 0x%x\n", Status ));
goto Error;
}
}
//
// Don't waste cycles marking entries as disabled or
// marshalling the blob if we are not going to write it
//
if ( !CheckOnly ) {
if ( ConflictPairs != NULL ) {
FTCache::ReconcileConflictPairs(
TdoEntryNew,
ConflictPairs,
ConflictPairsTotal
);
}
//
// Marshal the data in preparation for writing it to the DS
//
Status = FTCache::MarshalBlob(
TdoEntryNew,
&BlobLength,
&BlobData
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsarSetForestTrustInformation: FTCache::MarshalBlob returned 0x%x\n", Status ));
goto Error;
}
}
}
//
// if only a collision check was requested, bail here
//
if ( CheckOnly ) {
//
// Rollback the changes made to the forest trust cache
//
if ( TdoEntryNew ) {
g_FTCache->RollbackChanges( TdoEntryNew, &TdoEntryOld );
TdoEntryNew = NULL;
}
goto Cleanup;
}
NextAttribute = Attributes;
LsapDbInitializeAttributeDs(
NextAttribute,
TrDmForT,
BlobData,
BlobLength,
FALSE
);
AttributeCount++;
ASSERT( AttributeCount <= LSAP_DB_ATTRS_INFO_CLASS_DOMAIN );
//
// Write the attributes to the DS.
//
Status = LsapDbWriteAttributesObject(
TrustedDomainHandle,
Attributes,
AttributeCount
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsarSetForestTrustInformation: LsapDbWriteAttributesObject returned 0x%x\n", Status ));
goto Error;
}
#if DBG
//
// In debug builds only, display the array of conflict pairs
//
if ( *CollisionInfo != 0 ) {
LsapDsDebugOut((
DEB_FTINFO,
"\n********** Collisions were detected **********\n\n"
));
for ( ULONG Current = 0 ; Current < ( *CollisionInfo )->RecordCount ; Current++ ) {
LsapDsDebugOut((
DEB_FTINFO,
"%d. Index: %d, Type: %s, Flags: %d, Name: %wZ\n",
Current,
( *CollisionInfo )->Entries[Current]->Index,
( *CollisionInfo )->Entries[Current]->Type == 0 ? "TDO" : "XRef",
( *CollisionInfo )->Entries[Current]->Flags,
&( *CollisionInfo )->Entries[Current]->Name
));
}
}
#endif
Status = STATUS_SUCCESS;
Cleanup:
//
// DS write was successful - can now complete the cache transaction
// While at it, if this wasn't a probing (CheckOnly) call, audit the changes
//
if ( NT_SUCCESS( Status ) && !CheckOnly) {
if ( TdoEntryNew != NULL ) {
ASSERT( ForestTrustInfo->RecordCount > 0 );
g_FTCache->AuditChanges(
TdoEntryOld.RecordCount > 0 ?
&TdoEntryOld : NULL,
TdoEntryNew
);
//
// Do not audit the collisions -- the collisions here only affect
// the information passed in by the caller, and the caller is being
// informed of them.
//
} else if ( ForestTrustInfo->RecordCount == 0 ) {
FTCache::TDO_ENTRY * TdoEntry;
//
// ForestTrustInfo->RecordCount == 0 indicates a request for removal
//
TdoEntry = ( FTCache::TDO_ENTRY * )RtlLookupElementGenericTableAvl(
&g_FTCache->m_TdoTable,
( UNICODE_STRING * )&TrustInfoForName->TrustInfoEx.Name
);
if ( TdoEntry != NULL ) {
g_FTCache->AuditChanges( TdoEntry, NULL );
}
Status = g_FTCache->Remove(( UNICODE_STRING * )&TrustInfoForName->TrustInfoEx.Name );
if ( Status == STATUS_NOT_FOUND ) {
//
// Not sure if this can even happen, but if it's not in the
// cache, that's what I wanted in the first place
//
Status = STATUS_SUCCESS;
}
//
// Only a bug in the code would cause a deletion to fail
//
ASSERT( NT_SUCCESS( Status ));
}
}
if ( TrustedDomainHandle != NULL ) {
LsapDbCloseObject(
&TrustedDomainHandle,
0,
Status
);
}
FtcFree( BlobData );
FtcFree( ConflictPairs );
if ( TdoEntryOld.RecordCount > 0 ) {
g_FTCache->PurgeTdoEntry( &TdoEntryOld );
}
if ( TrustedDomainListLocked ) {
LsapDbReleaseLockTrustedDomainList();
}
if ( ObjectReferenced ) {
Status = LsapDbDereferenceObject(
&PolicyHandle,
PolicyObject,
TrustedDomainObject,
LSAP_DB_LOCK |
LSAP_DB_OMIT_REPLICATOR_NOTIFICATION,
(SECURITY_DB_DELTA_TYPE) 0,
Status
);
}
LsapTraceEvent( EVENT_TRACE_TYPE_END, LsaTraceEvent_SetForestTrustInformation );
LsapExitFunc( "LsarSetForestTrustInformation", Status );
LsarpReturnPrologue();
return Status;
Error:
ASSERT( !NT_SUCCESS( Status ));
LsapFreeCollisionInfo( CollisionInfo );
ASSERT( *CollisionInfo == NULL );
//
// Rollback the changes made to the forest trust cache
//
if ( TdoEntryNew ) {
g_FTCache->RollbackChanges( TdoEntryNew, &TdoEntryOld );
TdoEntryNew = NULL;
}
goto Cleanup;
}
NTSTATUS
LsapForestTrustFindMatch(
IN LSA_ROUTING_MATCH_TYPE Type,
IN PVOID Data,
IN BOOLEAN SearchLocal,
OUT OPTIONAL PLSA_UNICODE_STRING MatchName,
OUT OPTIONAL PSID * MatchSid
)
/*++
Routine Description:
Finds match for given data in the cache
Arguments:
Type Type of Data parameter
Data Data to match
SearchLocal Search local information only,
when FALSE, local information will be skipped
MatchName Used to return the match, if found.
Caller must use LsaIFree_LSAPR_UNICODE_STRING_BUFFER
MatchSid Used to return the SID of the matching domain, if found
Caller must use MIDL_user_free
Returns:
STATUS_SUCCESS Match was found
STATUS_NO_MATCH Match was not found
STATUS_INVALID_PARAMETER Check the inputs
STATUS_INTERNAL_ERROR Cache is internally inconsistent
STATUS_INSUFFICIENT_RESOURCES Out of memory
--*/
{
if ( g_FTCache == NULL ) {
return STATUS_INTERNAL_ERROR;
}
return g_FTCache->Match( Type, Data, SearchLocal, ( UNICODE_STRING * )MatchName, MatchSid );
}
///////////////////////////////////////////////////////////////////////////////
//
// Code required when running on a GC outside of the root domain
//
///////////////////////////////////////////////////////////////////////////////
NTSTATUS
LsapForestTrustCacheInsertEntInf(
IN ENTINF * EntInf
)
/*++
Routine Description:
Checks the trusted domain object corresponding to the given EntInf structure
and. if appropriate, inserts the forest trust data into the forest trust cache
Arguments:
EntInf result of a DS search
Returns:
STATUS_SUCCESS entry added OK
STATUS_INTERNAL_ERROR should never see this, really
STATUS_INSUFFICIENT_RESOURCES out of memory
STATUS_NOT_FOUND not all required attributes present
--*/
{
NTSTATUS Status;
UNICODE_STRING TrustedDomainName = {0};
PSID TrustedDomainSid = NULL;
ULONG TrustAttributes = 0, TrustDirection = 0, TrustType = 0;
ULONG ForestTrustLength = 0;
PBYTE ForestTrustData = NULL;
LSA_FOREST_TRUST_INFORMATION ForestTrustInfo = {0};
const USHORT TotalRequiredAttributes = 0x1 | 0x2 | 0x4 | 0x8;
USHORT FoundRequiredAttributes = 0;
BOOL IsDeleted = FALSE;
BOOL ForestTrustDataPresent = FALSE;
ASSERT( EntInf );
ASSERT( LsapDbIsLockedTrustedDomainList());
if ( g_FTCache == NULL ) {
ASSERT( FALSE );
return STATUS_INTERNAL_ERROR;
}
for ( ULONG i = 0 ; i < EntInf->AttrBlock.attrCount ; i++ ) {
switch ( EntInf->AttrBlock.pAttr[i].attrTyp ) {
case ATT_TRUST_PARTNER:
FoundRequiredAttributes |= 0x1;
TrustedDomainName.Buffer = LSAP_DS_GET_DS_ATTRIBUTE_AS_PWSTR( &EntInf->AttrBlock.pAttr[i] );
TrustedDomainName.Length = ( USHORT) LSAP_DS_GET_DS_ATTRIBUTE_LENGTH( &EntInf->AttrBlock.pAttr[i] );
TrustedDomainName.MaximumLength = TrustedDomainName.Length;
break;
case ATT_SECURITY_IDENTIFIER: // optional
TrustedDomainSid = ( PSID )LSAP_DS_GET_DS_ATTRIBUTE_AS_PWSTR( &EntInf->AttrBlock.pAttr[i] );
break;
case ATT_TRUST_ATTRIBUTES:
FoundRequiredAttributes |= 0x2;
TrustAttributes = LSAP_DS_GET_DS_ATTRIBUTE_AS_ULONG( &EntInf->AttrBlock.pAttr[i] );
break;
case ATT_TRUST_DIRECTION:
FoundRequiredAttributes |= 0x4;
TrustDirection = LSAP_DS_GET_DS_ATTRIBUTE_AS_ULONG( &EntInf->AttrBlock.pAttr[i] );
break;
case ATT_TRUST_TYPE:
FoundRequiredAttributes |= 0x8;
TrustType = LSAP_DS_GET_DS_ATTRIBUTE_AS_ULONG( &EntInf->AttrBlock.pAttr[i] );
break;
case ATT_MS_DS_TRUST_FOREST_TRUST_INFO: // optional
ForestTrustLength = ( ULONG )LSAP_DS_GET_DS_ATTRIBUTE_LENGTH( &EntInf->AttrBlock.pAttr[i] );
ForestTrustData = LSAP_DS_GET_DS_ATTRIBUTE_AS_PBYTE( &EntInf->AttrBlock.pAttr[i] );
ForestTrustDataPresent = ( ForestTrustLength > 0 && ForestTrustData != NULL );
break;
case ATT_IS_DELETED: // optional
IsDeleted = LSAP_DS_GET_DS_ATTRIBUTE_AS_ULONG( &EntInf->AttrBlock.pAttr[i] );
break;
default:
LsapDsDebugOut(( DEB_FTINFO, "Invalid parameter in LsapForestTrustUnmarshalBlob (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INVALID_PARAMETER;
ASSERT( FALSE );
goto Error;
}
}
if ( FoundRequiredAttributes != TotalRequiredAttributes ) {
LsapDsDebugOut(( DEB_FTINFO, "LsapForestTrustCacheInsertEntInf: not all required parameters found 0x%lx (%s:%d)\n", FoundRequiredAttributes, __FILE__, __LINE__ ));
Status = STATUS_NOT_FOUND;
goto Error;
}
if ( IsDeleted || !ForestTrustDataPresent ) {
LsapDsDebugOut(( DEB_FTINFO, "LsapForestTrustCacheInsertEntInf: removing an entry for %wZ\n", &TrustedDomainName ));
Status = LsapForestTrustCacheRemove( &TrustedDomainName );
if ( Status == STATUS_NOT_FOUND ) {
Status = STATUS_SUCCESS;
}
ASSERT( Status == STATUS_SUCCESS );
} else if ( ForestTrustDataPresent &&
LsapHavingForestTrustMakesSense(
TrustDirection,
TrustType,
TrustAttributes )) {
LsapDsDebugOut(( DEB_FTINFO, "LsapForestTrustCacheInsertEntInf: adding an entry for %wZ\n", &TrustedDomainName ));
Status = LsapForestTrustUnmarshalBlob(
ForestTrustLength,
ForestTrustData,
ForestTrustRecordTypeLast,
&ForestTrustInfo
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsapForestTrustCacheInsertEntInf: LsapForestTrustUnmarshalBlob returned 0x%x\n", Status ));
goto Error;
}
Status = LsapForestTrustCacheInsert(
&TrustedDomainName,
TrustedDomainSid,
&ForestTrustInfo,
FALSE
);
LsapFreeForestTrustInfo( &ForestTrustInfo );
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsapForestTrustCacheInsertEntInf: LsapForestTrustCacheInsert returned 0x%x\n", Status ));
goto Error;
}
LsapDsDebugOut(( DEB_FTINFO, "LsapForestTrustCacheInsertEntInf: the entry for %wZ successfully added\n", &TrustedDomainName ));
}
Status = STATUS_SUCCESS;
Cleanup:
return Status;
Error:
ASSERT( !NT_SUCCESS( Status ));
goto Cleanup;
}
BOOL
LsapNotifyPrepareToImpersonate(
ULONG Client,
ULONG Server,
VOID **ImpersonateData
)
/*++
Routine Description:
Called before DS notification fires.
Arguments:
Client, Server, ImpersonateData -- forwarded to DirPrepareForImpersonate
Returns:
TRUE if everything went fine,
FALSE if ran out of memory allocating resources
--*/
{
return DirPrepareForImpersonate(
Client,
Server,
ImpersonateData
);
}
VOID
LsapForestTrustTransmitDataXrefChange(
ULONG hClient,
ULONG hServer,
ENTINF *EntInf
)
/*++
Routine Description:
Notification callback for changes to trusted domain objects
Arguments:
hClient ignored
hServer ignored
EntInf <fill in>
Returns:
Nothing
--*/
{
NTSTATUS Status;
UNREFERENCED_PARAMETER( hClient );
UNREFERENCED_PARAMETER( hServer );
LsapDbAcquireWriteLockTrustedDomainList();
if ( g_FTCache == NULL ) {
ASSERT( FALSE ); // how is this possible???
LsapDbReleaseLockTrustedDomainList();
return;
}
Status = LsapForestTrustCacheInsertEntInf( EntInf );
if ( Status == STATUS_NOT_FOUND ) {
Status = STATUS_SUCCESS;
} else if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsapForestTrustOnGcTransmitData: LsapForestTrustCacheInsertEntInf returned 0x%x\n", Status ));
LsapForestTrustCacheSetInvalid();
}
LsapDbReleaseLockTrustedDomainList();
return;
}
VOID
LsapForestTrustTransmitDataUpnSuffixChange(
ULONG hClient,
ULONG hServer,
ENTINF *EntInf
)
/*++
Routine Description:
Notification callback for changes to trusted domain objects
Arguments:
hClient ignored
hServer ignored
EntInf <fill in>
Returns:
Nothing
--*/
{
NTSTATUS Status;
UNREFERENCED_PARAMETER( hClient );
UNREFERENCED_PARAMETER( hServer );
UNREFERENCED_PARAMETER( EntInf );
LsapDbAcquireWriteLockTrustedDomainList();
if ( g_FTCache == NULL ) {
ASSERT( FALSE ); // how is this possible???
LsapDbReleaseLockTrustedDomainList();
return;
} else if ( !g_FTCache->IsLocalValid()) {
LsapDbReleaseLockTrustedDomainList();
return;
}
Status = LsapForestTrustInsertLocalInfo();
if ( !NT_SUCCESS( Status )) {
//
// Had trouble inserting forest trust info into the cache!!!
// Mark the trusted domain list as invalid so it can get rebuilt.
//
LsapDbPurgeTrustedDomainCache();
}
LsapDbReleaseLockTrustedDomainList();
return;
}
VOID
LsapNotifyStopImpersonating(
ULONG Client,
ULONG Server,
VOID *ImpersonateData
)
/*++
Routine Description:
Called after DS notification fires.
Arguments:
Client, Server, ImpersonateData - forwarded to DirStopImpersonating
Returns:
Nothing
--*/
{
DirStopImpersonating(
Client,
Server,
ImpersonateData
);
}
NTSTATUS
LsapRebuildFtCacheGC()
/*++
Routine Description:
Rebuilds the forest trust cache on a global catalog server
which is outside the root domain of the forest
Arguments:
None
Returns:
STATUS_SUCCESS OK
STATUS_INTERNAL_ERROR no forest trust cache object
STATUS_INVALID_DOMAIN_STATE must be a GC outside of the root domain
STATUS_INSUFFICIENT_RESOURCES out of memory
--*/
{
NTSTATUS Status;
ULONG DirResult;
ULONG NameLen, RootLen, SystemLen;
PDSNAME RootName = NULL, SystemName = NULL;
FILTER Filter = {0};
ENTINFSEL Selection = {0};
SEARCHARG SearchArg = {0};
NOTIFYARG NotifyArg = {0};
NOTIFYRES * NotifyRes = NULL;
SEARCHRES * SearchRes = NULL;
BOOLEAN CloseTransaction = FALSE;
//
// List of attributes we care about for forest trust.
// If you change this list, change the switch statement
// inside LsapForestTrustCacheInsertEntInf.
//
ATTR AttrArray[] = {
{ ATT_TRUST_PARTNER, { 0, NULL }},
{ ATT_SECURITY_IDENTIFIER, { 0, NULL }},
{ ATT_TRUST_ATTRIBUTES, { 0, NULL }},
{ ATT_TRUST_DIRECTION, { 0, NULL }},
{ ATT_TRUST_TYPE, { 0, NULL }},
{ ATT_MS_DS_TRUST_FOREST_TRUST_INFO, { 0, NULL }},
{ ATT_IS_DELETED, { 0, NULL }}};
LsapDsDebugOut(( DEB_FTINFO, "Rebuilding forest trust cache on the GC (%s:%d)\n", __FILE__, __LINE__ ));
LsapDbAcquireWriteLockTrustedDomainList();
if ( g_FTCache == NULL ) {
ASSERT( FALSE ); // how is this possible???
Status = STATUS_INTERNAL_ERROR;
LsapDbReleaseLockTrustedDomainList();
LsapDsDebugOut(( DEB_FTINFO, "LsapRebuildFtCacheGC: Error 0x%lx (%s:%d)\n", Status, __FILE__, __LINE__ ));
return Status;
}
if ( LsapDbDcInRootDomain() || !SamIAmIGC()) {
LsapDbReleaseLockTrustedDomainList();
LsapDsDebugOut(( DEB_FTINFO, "LsapRebuildFtCacheGC: Must be called on a GC outside of root domain\n" ));
Status = STATUS_INVALID_DOMAIN_STATE;
return Status;
}
if ( g_FTCache->IsExternalValid()) {
LsapDbReleaseLockTrustedDomainList();
LsapDsDebugOut(( DEB_FTINFO, "LsapRebuildFtCacheGC: No need to rebuild, returning OK (%s:%d)\n", __FILE__, __LINE__ ));
return STATUS_SUCCESS;
}
//
// See if we already have a transaction going and open one if necessary
//
Status = LsapDsInitAllocAsNeededEx(
LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_DS_OP_TRANSACTION,
NullObject,
&CloseTransaction
);
if ( !NT_SUCCESS( Status )) {
LsapDbReleaseLockTrustedDomainList();
LsapDsDebugOut(( DEB_FTINFO, "LsapRebuildFtCacheGC: LsapDsOpenTransaction returned 0x%x\n", Status ));
return Status;
}
//
// Find the name of the root NC
//
RootLen = 0;
Status = GetConfigurationName(
DSCONFIGNAME_ROOT_DOMAIN,
&RootLen,
NULL
);
ASSERT( Status == STATUS_BUFFER_TOO_SMALL );
RootName = ( PDSNAME )LsapAllocateLsaHeap( RootLen );
if ( RootName == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in LsapRebuildFtCacheGC (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
Status = GetConfigurationName(
DSCONFIGNAME_ROOT_DOMAIN,
&RootLen,
RootName
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsapRebuildFtCacheGC: GetConfigurationName returned 0x%x\n", Status ));
goto Error;
}
//
// Construct the SYSTEM container name
//
NameLen = 10 + // wcslen( L"CN=System," )
wcslen( RootName->StringName );
SystemLen = DSNameSizeFromLen( NameLen );
SystemName = ( PDSNAME )LsapAllocateLsaHeap( SystemLen );
if ( SystemName == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in LsapRebuildFtCacheGC (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
SystemName->structLen = SystemLen;
SystemName->NameLen = NameLen;
swprintf(
SystemName->StringName,
L"CN=System,%ws",
RootName->StringName
);
//
// Register for notifications on the system container
// Sadly, filters can not be applied to notifications
//
Filter.choice = FILTER_CHOICE_ITEM;
Filter.FilterTypes.Item.choice = FI_CHOICE_TRUE;
Selection.attSel = EN_ATTSET_LIST;
Selection.AttrTypBlock.attrCount = sizeof( AttrArray ) / sizeof( AttrArray[0] );
Selection.AttrTypBlock.pAttr = AttrArray;
Selection.infoTypes = EN_INFOTYPES_TYPES_VALS;
SearchArg.pObject = SystemName;
SearchArg.choice = SE_CHOICE_IMMED_CHLDRN;
SearchArg.bOneNC = TRUE;
SearchArg.pFilter = &Filter;
SearchArg.searchAliases = FALSE;
SearchArg.pSelection = &Selection;
SearchArg.pSelectionRange = NULL;
SearchArg.fPutResultsInSortedTable = FALSE;
InitCommarg( &SearchArg.CommArg );
SearchArg.pResObj = NULL;
NotifyArg.pfPrepareForImpersonate = LsapNotifyPrepareToImpersonate;
NotifyArg.pfTransmitData = LsapForestTrustTransmitDataXrefChange;
NotifyArg.pfStopImpersonating = LsapNotifyStopImpersonating;
NotifyArg.hClient = 0;
DirResult = DirNotifyRegister(
&SearchArg,
&NotifyArg,
&NotifyRes
);
if ( NotifyRes ) {
Status = LsapDsMapDsReturnToStatusEx( &( NotifyRes->CommRes ));
} else {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in LsapRebuildFtCacheGC (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
}
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsapRebuildFtCacheGC: DirNotifyRegister returned 0x%x\n", Status ));
goto Error;
}
//
// Continue the transaction
//
LsapDsContinueTransaction();
//
// Remember the notification handle
//
ASSERT( XrefNotificationHandle == NULL );
XrefNotificationHandle = NotifyRes->hServer;
//
// One schema per forest means that the name of the TDO object category
// will be the same as in the root NC, so it's safe to use LsaDsStateInfo.
//
ASSERT( NULL != LsaDsStateInfo.SystemContainerItems.TrustedDomainObject );
//
// Unlike notifications, searches support filters. So use them.
//
RtlZeroMemory( &Filter, sizeof( Filter ));
Filter.choice = FILTER_CHOICE_ITEM;
Filter.FilterTypes.Item.choice = FI_CHOICE_EQUALITY;
Filter.FilterTypes.Item.FilTypes.ava.type = ATT_OBJECT_CATEGORY;
Filter.FilterTypes.Item.FilTypes.ava.Value.valLen = LsaDsStateInfo.SystemContainerItems.TrustedDomainObject->structLen;
Filter.FilterTypes.Item.FilTypes.ava.Value.pVal = ( BYTE * ) LsaDsStateInfo.SystemContainerItems.TrustedDomainObject;
RtlZeroMemory( &Selection, sizeof( Selection ));
Selection.attSel = EN_ATTSET_LIST;
Selection.AttrTypBlock.attrCount = sizeof( AttrArray ) / sizeof( AttrArray[0] );
Selection.AttrTypBlock.pAttr = AttrArray;
Selection.infoTypes = EN_INFOTYPES_TYPES_VALS;
RtlZeroMemory( &SearchArg, sizeof( SearchArg ));
SearchArg.pObject = SystemName;
SearchArg.choice = SE_CHOICE_IMMED_CHLDRN;
SearchArg.bOneNC = TRUE;
SearchArg.pFilter = &Filter;
SearchArg.searchAliases = FALSE;
SearchArg.pSelection = &Selection;
SearchArg.pSelectionRange = NULL;
SearchArg.fPutResultsInSortedTable = FALSE;
InitCommarg( &SearchArg.CommArg );
SearchArg.pResObj = NULL;
//
// Now that we're registered for notifications,
// read out the trusted domain objects
//
DirResult = DirSearch(
&SearchArg,
&SearchRes
);
if ( SearchRes ) {
Status = LsapDsMapDsReturnToStatusEx( &( SearchRes->CommRes ));
} else {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in LsapRebuildFtCacheGC (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
}
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsapRebuildFtCacheGC: DirSearch returned 0x%x\n", Status ));
goto Error;
}
//
// Voila! All that's left is to take those search results
// and populate our cache with them
//
if ( SearchRes->count > 0 ) {
for ( ENTINFLIST * Current = &( SearchRes->FirstEntInf );
Current != NULL;
Current = Current->pNextEntInf ) {
Status = LsapForestTrustCacheInsertEntInf( &Current->Entinf );
if ( Status == STATUS_NOT_FOUND ) {
Status = STATUS_SUCCESS;
} else if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsapRebuildFtCacheGC: LsapForestTrustCacheInsertEntInf returned 0x%x\n", Status ));
goto Error;
}
}
}
//
// Cache has been successfully rebuilt
//
LsapForestTrustCacheSetExternalValid();
Status = STATUS_SUCCESS;
LsapDsDebugOut(( DEB_FTINFO, "LsapRebuildFtCacheGC: Cache rebuilt OK (%s:%d)\n", __FILE__, __LINE__ ));
Cleanup:
LsapDsDeleteAllocAsNeededEx(
LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_DS_OP_TRANSACTION,
NullObject,
CloseTransaction
);
LsapDbReleaseLockTrustedDomainList();
LsapFreeLsaHeap( RootName );
LsapFreeLsaHeap( SystemName );
return Status;
Error:
ASSERT( !NT_SUCCESS( Status ));
//
// Undo everything we've accomplished,
// unregister notifications, etc.
//
LsapForestTrustCacheSetInvalid();
goto Cleanup;
}
NTSTATUS
LsapRegisterForUpnListNotifications()
/*++
Routine Description:
Registers for notifications on the Partitions container to learn of changes
to the UpnSuffixes attribute.
Arguments:
None
Returns:
STATUS_SUCCESS OK
STATUS_INSUFFICIENT_RESOURCES out of memory
--*/
{
NTSTATUS Status;
ULONG DirResult;
ULONG PartitionsLen;
PDSNAME PartitionsName = NULL;
FILTER Filter = {0};
ENTINFSEL Selection = {0};
SEARCHARG SearchArg = {0};
NOTIFYARG NotifyArg = {0};
NOTIFYRES * NotifyRes = NULL;
ATTR AttrArray[] = {{ ATT_UPN_SUFFIXES, { 0, NULL }}};
BOOLEAN CloseTransaction;
LsapDsDebugOut(( DEB_FTINFO, "Registering for notificaitons on the UPN list\n" ));
//
// See if we already have a transaction going and open one if necessary
//
Status = LsapDsInitAllocAsNeededEx(
LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_DS_OP_TRANSACTION,
NullObject,
&CloseTransaction
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsapRegisterForUpnListNotifications: LsapDsOpenTransaction returned 0x%x\n", Status ));
return Status;
}
//
// Find the name of the root NC
//
PartitionsLen = 0;
Status = GetConfigurationName(
DSCONFIGNAME_PARTITIONS,
&PartitionsLen,
NULL
);
ASSERT( Status == STATUS_BUFFER_TOO_SMALL );
PartitionsName = ( PDSNAME )LsapAllocateLsaHeap( PartitionsLen );
if ( PartitionsName == NULL ) {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in LsapRegisterForUpnListNotifications (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
Status = GetConfigurationName(
DSCONFIGNAME_PARTITIONS,
&PartitionsLen,
PartitionsName
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsapRegisterForUpnListNotifications: GetConfigurationName returned 0x%x\n", Status ));
goto Error;
}
//
// Register for notifications on the partitions container
// Sadly, filters can not be applied to notifications
//
Filter.choice = FILTER_CHOICE_ITEM;
Filter.FilterTypes.Item.choice = FI_CHOICE_TRUE;
Selection.attSel = EN_ATTSET_LIST;
Selection.AttrTypBlock.attrCount = sizeof( AttrArray ) / sizeof( AttrArray[0] );
Selection.AttrTypBlock.pAttr = AttrArray;
Selection.infoTypes = EN_INFOTYPES_TYPES_ONLY;
SearchArg.pObject = PartitionsName;
SearchArg.choice = SE_CHOICE_BASE_ONLY;
SearchArg.bOneNC = TRUE;
SearchArg.pFilter = &Filter;
SearchArg.searchAliases = FALSE;
SearchArg.pSelection = &Selection;
SearchArg.pSelectionRange = NULL;
SearchArg.fPutResultsInSortedTable = FALSE;
InitCommarg( &SearchArg.CommArg );
SearchArg.pResObj = NULL;
NotifyArg.pfPrepareForImpersonate = LsapNotifyPrepareToImpersonate;
NotifyArg.pfTransmitData = LsapForestTrustTransmitDataUpnSuffixChange;
NotifyArg.pfStopImpersonating = LsapNotifyStopImpersonating;
NotifyArg.hClient = 0;
DirResult = DirNotifyRegister(
&SearchArg,
&NotifyArg,
&NotifyRes
);
if ( NotifyRes ) {
Status = LsapDsMapDsReturnToStatusEx( &( NotifyRes->CommRes ));
} else {
LsapDsDebugOut(( DEB_FTINFO, "Out of memory in LsapRegisterForUpnListNotifications (%s:%d)\n", __FILE__, __LINE__ ));
Status = STATUS_INSUFFICIENT_RESOURCES;
}
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_FTINFO, "LsapRegisterForUpnListNotifications: DirNotifyRegister returned 0x%x\n", Status ));
goto Error;
} // // Remember the notification handle // ASSERT( UpnSuffixNotificationHandle == NULL ); UpnSuffixNotificationHandle = NotifyRes->hServer; Status = STATUS_SUCCESS; LsapDsDebugOut(( DEB_FTINFO, "LsapRegisterForUpnListNotifications: UPN notifications registered OK\n" )); Cleanup: LsapDsDeleteAllocAsNeededEx( LSAP_DB_READ_ONLY_TRANSACTION | LSAP_DB_DS_OP_TRANSACTION, NullObject, CloseTransaction ); LsapFreeLsaHeap( PartitionsName ); return Status; Error: ASSERT( !NT_SUCCESS( Status )); ASSERT( UpnSuffixNotificationHandle == NULL ); goto Cleanup; } void LsaINotifyGCStatusChange( IN BOOLEAN PromotingToGC ) /*++ Routine Description: Gets called whenever the GC state of the machine changes. Currently the only use of this information is to figure out that this is a DC outside of the root domain and no longer a GC and throw away the forest trust cache information Arguments: PromotingToGC - flag to decide whether promoting to GC or demoting from GC Returns: Nothing --*/ { if ( !LsapDbDcInRootDomain() && !PromotingToGC ) { LsapDbAcquireReadLockTrustedDomainList(); if ( LsapForestTrustCacheIsExternalValid()) { // // This machine is no longer a GC -- throw away the cache // LsapDbConvertReadLockTrustedDomainListToExclusive(); LsapForestTrustCacheSetInvalid(); } LsapDbReleaseLockTrustedDomainList(); } }