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.
 
 
 
 
 
 

2727 lines
78 KiB

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
dsfixup.c
Abstract:
Implementation of a variety of fixup routines for the Lsa/Ds interaction.
Author:
Mac McLain (MacM) Jan 17, 1997
Environment:
User Mode
Revision History:
--*/
#include <lsapch2.h>
#include <dbp.h>
#include <lmcons.h>
#include <lmaccess.h>
#include <alloca.h>
//
// List entry that maintains information on notifications
//
typedef struct _LSAP_DSFU_NOTIFICATION_NODE {
LIST_ENTRY List ;
LUID AuthenticationId;
PSID UserSid;
PDSNAME ObjectPath;
ULONG Class;
SECURITY_DB_DELTA_TYPE DeltaType;
ULONG OldTrustDirection;
ULONG OldTrustType;
BOOLEAN ReplicatedInChange;
BOOLEAN ChangeOriginatedInLSA;
} LSAP_DSFU_NOTIFICATION_NODE, *PLSAP_DSFU_NOTIFICATION_NODE;
LIST_ENTRY LsapFixupList ;
SAFE_CRITICAL_SECTION LsapFixupLock ;
BOOLEAN LsapFixupThreadActive ;
//
// Packages that need to be called when trust changes. Right now, it's only Kerberos. If
// that changes, this will have to be changed into a list and processed.
//
pfLsaTrustChangeNotificationCallback LsapKerberosTrustNotificationFunction = NULL;
//
// Local prototypes
//
#define LSAP_DS_FULL_FIXUP TRUE
#define LSAP_DS_NOTIFY_FIXUP FALSE
NTSTATUS
LsapDsFixupTrustedDomainObject(
IN PDSNAME TrustObject,
IN BOOLEAN Startup,
IN ULONG SamCount,
IN PSAMPR_RID_ENUMERATION SamAccountList
);
NTSTATUS
LsapDsTrustRenameObject(
IN PDSNAME TrustObject,
IN PUNICODE_STRING NewDns,
OUT PDSNAME *NewObjectName
);
DWORD
WINAPI LsapDsFixupCallback(
LPVOID ParameterBlock
);
VOID
LsapFreeNotificationNode(
IN PLSAP_DSFU_NOTIFICATION_NODE NotificationNode
);
NTSTATUS
LsapDsFixupTrustByInfo(
IN PDSNAME ObjectPath,
IN PLSAPR_TRUSTED_DOMAIN_INFORMATION_EX2 TrustInfo2,
IN ULONG PosixOffset,
IN SECURITY_DB_DELTA_TYPE DeltaType,
IN PSID UserSid,
IN LUID AuthenticationId,
IN BOOLEAN ReplicatedInChange,
IN BOOLEAN ChangeOriginatedInLSA
);
NTSTATUS
LsapDsTrustFixInterdomainTrustAccount(
IN PDSNAME ObjectPath,
IN SECURITY_DB_DELTA_TYPE DeltaType,
IN ULONG Options,
IN PSID UserSid,
IN LUID AuthenticationId
);
BOOL
LsapDsQueueFixupRequest(
PLSAP_DSFU_NOTIFICATION_NODE Node
);
NTSTATUS
LsapDsFixupTrustForXrefChange(
IN PDSNAME ObjectPath,
IN BOOLEAN TransactionActive
);
NTSTATUS
LsapDsFixupTrustedDomainOnRestartCallback(IN PVOID Parameter)
{
return(LsapDsFixupTrustedDomainObjectOnRestart());
}
NTSTATUS
LsapDsFixupTrustedDomainObjectOnRestart(
VOID
)
/*++
Routine Description:
This routine will go through and ensure that all of the trusted domain objects
are up to date. This includes:
Ensuring that the parent x-ref pointer is set
There is not new authentication information on the object
That the domain name has not changed
That a domain x-ref object doesn't exist for a downlevel domain.
If one does, the domain will be updated to an uplevel domain.
Arguments:
VOID
Return Values:
STATUS_SUCCESS -- Success
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
PDSNAME *DsNames = NULL;
ULONG Items=0, i;
BOOLEAN CloseTransaction = FALSE;
SAM_ENUMERATE_HANDLE SamEnum = 0;
PSAMPR_ENUMERATION_BUFFER RidEnum = NULL;
ULONG SamCount = 0;
DOMAIN_SERVER_ROLE ServerRole = DomainServerRolePrimary;
BOOLEAN RollbackTransaction = FALSE;
BOOLEAN FixupFailed = FALSE;
//
// Begin a DS transaction.
//
Status = LsapDsInitAllocAsNeededEx( LSAP_DB_DS_OP_TRANSACTION,
TrustedDomainObject,
&CloseTransaction );
if ( !NT_SUCCESS( Status ) ) {
return( Status );
}
Status = LsapDsGetListOfSystemContainerItems( CLASS_TRUSTED_DOMAIN,
&Items,
&DsNames );
if ( Status == STATUS_OBJECT_NAME_NOT_FOUND ) {
Items = 0;
Status = STATUS_SUCCESS;
}
ASSERT(SampExistsDsTransaction());
ASSERT(THVerifyCount(1));
if ( NT_SUCCESS( Status ) ) {
LsapSaveDsThreadState();
Status = LsapOpenSam();
if ( !NT_SUCCESS( Status ) ) {
LsapDsDebugOut(( DEB_ERROR,
"LsapDsFixupTrustedDomainObjectOnRestart: Sam not opened\n"));
} else {
//
// Query the server role, PDC/BDC
//
Status = SamIQueryServerRole(
LsapAccountDomainHandle,
&ServerRole
);
if ((NT_SUCCESS(Status)) && (DomainServerRolePrimary==ServerRole))
{
//
// Enumerate all of the SAM Interdomain trust accounts
//
Status = SamrEnumerateUsersInDomain( LsapAccountDomainHandle,
&SamEnum,
USER_INTERDOMAIN_TRUST_ACCOUNT,
&RidEnum,
0xFFFFFFFF,
&SamCount );
if ( !NT_SUCCESS( Status ) ) {
LsapDsDebugOut(( DEB_FIXUP,
"SamEnumerateUsersInDomain failed with 0x%lx\n",
Status ));
} else {
LsapDsDebugOut(( DEB_FIXUP,
"SamEnumerateUsersInDomain returned %lu accounts\n",
SamCount ));
}
}
}
LsapRestoreDsThreadState();
}
ASSERT(SampExistsDsTransaction());
ASSERT(THVerifyCount(1));
//
// Perform fixup only on PDC
//
if (( NT_SUCCESS( Status ) ) && (DomainServerRolePrimary==ServerRole)) {
for ( i = 0; i < Items; i++ ) {
ASSERT(SampExistsDsTransaction());
ASSERT(THVerifyCount(1));
Status = LsapDsFixupTrustedDomainObject( DsNames[ i ], LSAP_DS_FULL_FIXUP,
SamCount, (NULL!=RidEnum)?RidEnum->Buffer:NULL );
if (!NT_SUCCESS(Status))
{
FixupFailed = TRUE;
}
}
} else if ( Status == STATUS_OBJECT_NAME_NOT_FOUND ) {
Status = STATUS_SUCCESS;
}
if ( RidEnum ) {
SamFreeMemory( RidEnum );
}
ASSERT(SampExistsDsTransaction());
ASSERT(THVerifyCount(1));
if (!NT_SUCCESS(Status))
{
RollbackTransaction = TRUE;
}
//
// Close the transacation
//
LsapDsDeleteAllocAsNeededEx2( LSAP_DB_DS_OP_TRANSACTION,
TrustedDomainObject,
CloseTransaction,
RollbackTransaction
);
ASSERT(!SampExistsDsTransaction());
ASSERT(THVerifyCount(0));
//
// If failed then queue for a second try
//
if ((!NT_SUCCESS(Status)) || (FixupFailed))
{
LsaIRegisterNotification(
LsapDsFixupTrustedDomainOnRestartCallback,
NULL,
NOTIFIER_TYPE_INTERVAL,
0, // no class
NOTIFIER_FLAG_ONE_SHOT,
600, // wait for another 10 mins
NULL // no handle
);
}
return( STATUS_SUCCESS );
}
NTSTATUS
LsapDsFixupTrustForXrefChange(
IN PDSNAME ObjectPath,
IN BOOLEAN TransactionActive
)
/*++
This routine does the appropriate changes to the TDO to make it uplevel, when the
cross ref replicates in
Parameters
ObjectPath -- The path to the Xref ( ie the DSNAME of the Xref )
TransactionActive -- Indicates that a transaction is active and the trusted domain lock
is held. Therefore these 2 operations need not be done
Return Values
STATUS_SUCCESS
Other Error codes
--*/
{
ATTRBLOCK Read, Results;
PDSNAME NcName = NULL;
PSID TrustedDomainSid=NULL;
UNICODE_STRING DnsName;
UNICODE_STRING FlatName;
UNICODE_STRING TruncatedName;
BOOLEAN NcNameFound = FALSE;
BOOLEAN DnsNameFound = FALSE;
BOOLEAN FlatNameFound = FALSE;
LSAPR_HANDLE TrustedDomain=0;
NTSTATUS Status = STATUS_SUCCESS;
DSNAME *TrustedDomainDsName = NULL;
DSNAME *NewObjectName = NULL;
ULONG j;
ULONG TrustType=0,TrustDirection=0,TrustAttributes=0;
ULONG ForestTrustLength = 0;
PBYTE ForestTrustInfo = NULL;
BOOLEAN CloseTransaction=FALSE;
BOOLEAN ActiveThreadState = FALSE;
BOOLEAN TrustChanged = FALSE;
BOOLEAN FoundCorrespondingTDO = FALSE;
RtlZeroMemory(&DnsName, sizeof(UNICODE_STRING));
RtlZeroMemory(&FlatName, sizeof(UNICODE_STRING));
RtlZeroMemory(&TruncatedName, sizeof(UNICODE_STRING));
if (!TransactionActive)
{
//
// Begin a Transaction
//
Status = LsapDsInitAllocAsNeededEx(
0,
TrustedDomainObject,
&CloseTransaction
);
if (!NT_SUCCESS(Status))
goto Error;
ActiveThreadState = TRUE;
}
//
// Read the fixup information we need. This includes:
// Attributes
// Trust partner
// Crossref info
// Type
// Initial incoming auth info
// Initial outgoing auth info
//
Read.attrCount = LsapDsTrustedDomainFixupXRefCount;
Read.pAttr = LsapDsTrustedDomainFixupXRefAttributes;
Status = LsapDsReadByDsName( ObjectPath,
0,
&Read,
&Results );
if (!NT_SUCCESS(Status))
{
goto Error;
}
ASSERT(SampExistsDsTransaction());
ASSERT(THVerifyCount(1));
for ( j = 0; j < Results.attrCount; j++ ) {
switch ( Results.pAttr[ j ].attrTyp ) {
case ATT_NC_NAME:
NcName = (DSNAME *) LSAP_DS_GET_DS_ATTRIBUTE_AS_DSNAME(&Results.pAttr[ j ] );
if (NcName->SidLen>0)
{
TrustedDomainSid = LsapAllocateLsaHeap(NcName->SidLen);
if ( NULL==TrustedDomainSid)
{
Status = STATUS_NO_MEMORY;
goto Error;
}
RtlCopyMemory(TrustedDomainSid,&NcName->Sid,NcName->SidLen) ;
}
NcNameFound = TRUE;
break;
case ATT_DNS_ROOT:
DnsName.Length = ( USHORT) LSAP_DS_GET_DS_ATTRIBUTE_LENGTH( &Results.pAttr[ j ] );
DnsName.MaximumLength = DnsName.Length;
//
// Allocate the buffer off of the process heap, so that we can use it even after the thread state
// has been killed.
//
DnsName.Buffer = LsapAllocateLsaHeap(DnsName.MaximumLength);
if (NULL==DnsName.Buffer)
{
Status = STATUS_NO_MEMORY;
goto Error;
}
RtlCopyMemory(
DnsName.Buffer,
LSAP_DS_GET_DS_ATTRIBUTE_AS_PWSTR( &Results.pAttr[ j ] ),
DnsName.Length
);
DnsNameFound = TRUE;
break;
case ATT_NETBIOS_NAME:
FlatName.Length = ( USHORT) LSAP_DS_GET_DS_ATTRIBUTE_LENGTH( &Results.pAttr[ j ] );
FlatName.MaximumLength = FlatName.Length;
//
// Allocate the buffer off of the process heap, so that we can use it even after the thread state
// has been killed.
//
FlatName.Buffer = LsapAllocateLsaHeap(FlatName.MaximumLength);
if (NULL==FlatName.Buffer)
{
Status = STATUS_NO_MEMORY;
goto Error;
}
RtlCopyMemory(
FlatName.Buffer,
LSAP_DS_GET_DS_ATTRIBUTE_AS_PWSTR( &Results.pAttr[ j ] ),
FlatName.Length
);
FlatNameFound = TRUE;
break;
}
}
//
// Patch up the TDO ( if required ) after finding it by the corresponding SID
//
if ((NcNameFound) && (NcName->SidLen>0))
{
//
// Case of an instantiated NC
//
PDSNAME CategoryName = LsaDsStateInfo.SystemContainerItems.TrustedDomainObject;
ATTRVAL TDOAttVals[] = {
{ CategoryName->structLen, (PUCHAR)CategoryName },
{ RtlLengthSid(TrustedDomainSid), (PUCHAR)TrustedDomainSid }
};
ATTR TDOAttrs[] = {
{ ATT_OBJECT_CATEGORY, {1, &TDOAttVals[0] } },
{ ATT_SECURITY_IDENTIFIER, {1, &TDOAttVals[1] } }
};
ASSERT(NULL!=TrustedDomainSid);
Status = LsapDsSearchUnique(
0,
LsaDsStateInfo.DsSystemContainer,
TDOAttrs,
sizeof( TDOAttrs ) / sizeof( ATTR ),
&TrustedDomainDsName
);
if ((STATUS_OBJECT_NAME_NOT_FOUND==Status)
&& (FlatNameFound))
{
ATTRVAL TDOFlatNameAttVals[] = {
{ CategoryName->structLen, (PUCHAR)CategoryName },
{ FlatName.Length, (PUCHAR)FlatName.Buffer}
};
ATTR TDOFlatNameAttrs[] = {
{ ATT_OBJECT_CATEGORY, {1, &TDOFlatNameAttVals[0] } },
{ ATT_TRUST_PARTNER, {1, &TDOFlatNameAttVals[1] } }
};
//
// We could not find the TDO by SID. Maybe this is a case of an inbount only
// trust . Try to find by the flat name
//
Status = LsapDsSearchUnique(
0,
LsaDsStateInfo.DsSystemContainer,
TDOFlatNameAttrs,
sizeof( TDOFlatNameAttrs ) / sizeof( ATTR ),
&TrustedDomainDsName
);
}
//
// Bail if we could not find the corresponding TDO
//
if (!NT_SUCCESS(Status))
{
//
// Failure to find the TDO is not an error. It just means that
// a direct trust to that domain does not exist. Therefore
// reset the error code before bailing
//
if (STATUS_OBJECT_NAME_NOT_FOUND==Status)
{
Status = STATUS_SUCCESS;
}
goto Error;
}
FoundCorrespondingTDO = TRUE;
//
// Read and Modify the trust type attribue
//
//
// Read the fixup information we need. This includes:
// Attributes
// Trust partner
// Crossref info
// Type
// Initial incoming auth info
// Initial outgoing auth info
//
Read.attrCount = LsapDsTrustedDomainFixupAttributeCount;
Read.pAttr = LsapDsTrustedDomainFixupAttributes;
Status = LsapDsReadByDsName( TrustedDomainDsName,
0,
&Read,
&Results );
if (!NT_SUCCESS(Status))
{
//
// Failure to find a matching TDO is not an error. It simply means that we
// do not have a direct trust to the domain described by the cross ref. Reset
// error codes to success and bail
//
if (STATUS_OBJECT_NAME_NOT_FOUND == Status)
{
Status = STATUS_SUCCESS;
}
goto Error;
}
for ( j = 0; j < Results.attrCount; j++ ) {
switch ( Results.pAttr[ j ].attrTyp ) {
case ATT_TRUST_TYPE:
TrustType = LSAP_DS_GET_DS_ATTRIBUTE_AS_ULONG( &Results.pAttr[ j ] );
break;
case ATT_TRUST_DIRECTION:
TrustDirection = LSAP_DS_GET_DS_ATTRIBUTE_AS_ULONG( &Results.pAttr[ j ] );
break;
case ATT_TRUST_ATTRIBUTES:
TrustAttributes = LSAP_DS_GET_DS_ATTRIBUTE_AS_ULONG( &Results.pAttr[ j ] );
break;
case ATT_MS_DS_TRUST_FOREST_TRUST_INFO:
ForestTrustLength = ( ULONG )LSAP_DS_GET_DS_ATTRIBUTE_LENGTH( &Results.pAttr[ j ] );
ForestTrustInfo = LSAP_DS_GET_DS_ATTRIBUTE_AS_PBYTE( &Results.pAttr[ j ] );
break;
}
}
if ((TrustType & TRUST_TYPE_DOWNLEVEL ) && (DnsNameFound))
{
//
// If the trust type is marked as downlevel, we need to change this to an uplevel trust
//
//
// Setup the Attrblock structure for the DS
//
ATTRVAL TDOWriteAttVals[] = {
{ sizeof(ULONG), (PUCHAR)&TrustType},
{ ObjectPath->structLen, (PUCHAR)ObjectPath},
{ DnsName.Length, (PUCHAR)DnsName.Buffer}
};
ATTR TDOWriteAttrs[] = {
{ ATT_TRUST_TYPE, {1, &TDOWriteAttVals[0] } },
{ ATT_DOMAIN_CROSS_REF, {1, &TDOWriteAttVals[1] } },
{ ATT_TRUST_PARTNER, {1, &TDOWriteAttVals[2] } }
};
ATTRBLOCK TDOWriteAttrBlock = {sizeof(TDOWriteAttrs)/sizeof(TDOWriteAttrs[0]),TDOWriteAttrs};
//
// Change trust type to uplevel
//
TrustType &= ~((ULONG) TRUST_TYPE_DOWNLEVEL);
TrustType |=TRUST_TYPE_UPLEVEL;
//
// Set the attributes on the TDO
//
Status = LsapDsWriteByDsName(
TrustedDomainDsName,
LSAPDS_REPLACE_ATTRIBUTE,
&TDOWriteAttrBlock
);
if (!NT_SUCCESS(Status))
goto Error;
//
// O.K now rename the object ( sets the DNS domain name )
//
Status = LsapDsTruncateNameToFitCN(
&DnsName,
&TruncatedName
);
if (!NT_SUCCESS(Status))
goto Error;
Status = LsapDsTrustRenameObject(
TrustedDomainDsName,
&TruncatedName,
&NewObjectName
);
if (!NT_SUCCESS(Status))
goto Error;
TrustChanged = TRUE;
}
}
Error:
//
// Update the LSA in memory list regarding the Trust change
// Note this update is done after the commit, except in the case
// where the caller has the transaction open which occurs during an
// upgrade from NT4 where notifications to the in memory trust list
// are not processed anyway.
//
if ((NT_SUCCESS(Status)) && (TrustChanged))
{
LSAPR_TRUSTED_DOMAIN_INFORMATION_EX2 NewTrustInfo;
RtlZeroMemory(&NewTrustInfo, sizeof(NewTrustInfo));
ASSERT(DnsNameFound);
if (DnsNameFound)
{
RtlCopyMemory(&NewTrustInfo.Name,&DnsName, sizeof(UNICODE_STRING));
}
if (FlatNameFound)
{
RtlCopyMemory(&NewTrustInfo.FlatName,&FlatName,sizeof(UNICODE_STRING));
}
ASSERT(NcName->SidLen >0);
NewTrustInfo.Sid = TrustedDomainSid;
NewTrustInfo.TrustType = TrustType;
NewTrustInfo.TrustDirection = TrustDirection;
NewTrustInfo.TrustAttributes = TrustAttributes;
NewTrustInfo.ForestTrustLength = ForestTrustLength;
NewTrustInfo.ForestTrustInfo = ForestTrustInfo;
LsapDbFixupTrustedDomainListEntry(
NewTrustInfo.Sid,
&NewTrustInfo.Name,
&NewTrustInfo.FlatName,
&NewTrustInfo,
NULL
);
}
//
// Commit / Rollback the transaction if necessary
//
if (ActiveThreadState)
{
BOOLEAN RollbackTransaction = (NT_SUCCESS(Status))?FALSE:TRUE;
LsapDsDeleteAllocAsNeededEx2(
0,
TrustedDomainObject,
CloseTransaction,
RollbackTransaction // rollback transaction
);
ASSERT(!SampExistsDsTransaction());
ASSERT(THVerifyCount(0));
}
if ((!NT_SUCCESS(Status)) && FoundCorrespondingTDO)
{
//
// If we could not update the Corresponding CrossRef then event log
//
//
// Event log the error
//
if (DnsNameFound)
{
SpmpReportEventU(
EVENTLOG_ERROR_TYPE,
LSA_TRUST_UPGRADE_ERROR,
0,
sizeof( ULONG ),
&Status,
1,
&DnsName
);
}
else if (FlatNameFound)
{
SpmpReportEventU(
EVENTLOG_ERROR_TYPE,
LSA_TRUST_UPGRADE_ERROR,
0,
sizeof( ULONG ),
&Status,
1,
&FlatName
);
}
//
// We do not event log the failure if no name is found, but then
// it is an extremely wierd case indeed.
//
}
if (TrustedDomainDsName)
LsapFreeLsaHeap(TrustedDomainDsName);
if (NewObjectName)
LsapFreeLsaHeap(NewObjectName);
if (DnsName.Buffer)
LsapFreeLsaHeap(DnsName.Buffer);
if (FlatName.Buffer)
LsapFreeLsaHeap(FlatName.Buffer);
if (TrustedDomainSid)
LsapFreeLsaHeap(TrustedDomainSid);
if (TruncatedName.Buffer)
LsapFreeLsaHeap(TruncatedName.Buffer);
return(Status);
}
NTSTATUS
LsapDsFixupTrustedDomainObject(
IN PDSNAME TrustObject,
IN BOOLEAN Startup,
IN ULONG SamCount,
IN PSAMPR_RID_ENUMERATION SamAccountList
)
/*++
Routine Description:
This routine will fixup an individual trusted domain object
Arguments:
TrustObject -- Trusted domain object to fix up
Startup -- If TRUE, this is startup fixup, so do the full set. Otherwise, it's notification
fixup, so a limited set is done.
Return Values:
STATUS_SUCCESS -- Success
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
PDSNAME NewTrust = NULL;
ULONG Items, i, j;
ATTRBLOCK Read, Results, XRefResults, Write;
ATTR WriteAttrs[ 4 ];
ATTRVAL WriteAttrVal[ sizeof( WriteAttrs ) / sizeof( ATTR ) ];
ULONG Attributes, Type, XRefAttr, Direction = 0;
PUNICODE_STRING InitialIncoming = NULL, InitialOutgoing = NULL, Partner = NULL, Flat = NULL;
UNICODE_STRING Initial, UnicodePartner, UnicodeIncoming, UnicodeOutgoing, XRefDns, FlatName = { 0 };
BOOLEAN CloseTransaction, WriteXRef, WritePartner, WriteAttribute, WriteType;
BOOLEAN RenameRequired = FALSE, RemoveIncoming = FALSE, RemoveOutgoing = FALSE;
BOOLEAN RemoveObject = FALSE;
PSAMPR_RID_ENUMERATION CurrentAccount = NULL;
TRUSTED_DOMAIN_INFORMATION_EX UpdateInfoEx;
ULONG Size = 0;
//
// A DS transaction should already exist at this point.
//
ASSERT(SampExistsDsTransaction());
ASSERT(THVerifyCount(1));
RtlZeroMemory( &Write, sizeof( ATTRBLOCK ) );
WriteXRef = FALSE;
WritePartner = FALSE;
WriteAttribute = FALSE;
WriteType = FALSE;
LsapDsDebugOut(( DEB_FIXUP,
"Processing %ws\n",
LsapDsNameFromDsName( TrustObject ) ));
//
// Read the fixup information we need. This includes:
// Attributes
// Trust partner
// Crossref info
// Type
// Initial incoming auth info
// Initial outgoing auth info
//
Read.attrCount = LsapDsTrustedDomainFixupAttributeCount;
Read.pAttr = LsapDsTrustedDomainFixupAttributes;
Status = LsapDsReadByDsName( TrustObject,
0,
&Read,
&Results );
if ( NT_SUCCESS( Status ) ) {
for ( j = 0; j < Results.attrCount; j++ ) {
switch ( Results.pAttr[ j ].attrTyp ) {
case ATT_TRUST_PARTNER:
UnicodePartner.Length = ( USHORT) LSAP_DS_GET_DS_ATTRIBUTE_LENGTH( &Results.pAttr[ j ] );
UnicodePartner.MaximumLength = UnicodePartner.Length;
UnicodePartner.Buffer = LSAP_DS_GET_DS_ATTRIBUTE_AS_PWSTR( &Results.pAttr[ j ] );
Partner = &UnicodePartner;
break;
case ATT_FLAT_NAME:
FlatName.Length = ( USHORT) LSAP_DS_GET_DS_ATTRIBUTE_LENGTH( &Results.pAttr[ j ] );
FlatName.MaximumLength = FlatName.Length;
FlatName.Buffer = LSAP_DS_GET_DS_ATTRIBUTE_AS_PWSTR( &Results.pAttr[ j ] );
Flat = &FlatName;
break;
case ATT_TRUST_ATTRIBUTES:
Attributes = LSAP_DS_GET_DS_ATTRIBUTE_AS_ULONG( &Results.pAttr[ j ] );
break;
case ATT_TRUST_DIRECTION:
Direction = LSAP_DS_GET_DS_ATTRIBUTE_AS_ULONG( &Results.pAttr[ j ] );
break;
case ATT_TRUST_TYPE:
Type = LSAP_DS_GET_DS_ATTRIBUTE_AS_ULONG( &Results.pAttr[ j ] );
break;
case ATT_INITIAL_AUTH_INCOMING:
UnicodeIncoming.Length = ( USHORT) LSAP_DS_GET_DS_ATTRIBUTE_LENGTH( &Results.pAttr[ j ] );
UnicodeIncoming.MaximumLength = UnicodeIncoming.Length;
UnicodeIncoming.Buffer = LSAP_DS_GET_DS_ATTRIBUTE_AS_PWSTR( &Results.pAttr[ j ] );
InitialIncoming = &UnicodeIncoming;
break;
case ATT_INITIAL_AUTH_OUTGOING:
UnicodeOutgoing.Length = ( USHORT) LSAP_DS_GET_DS_ATTRIBUTE_LENGTH( &Results.pAttr[ j ] );
UnicodeOutgoing.MaximumLength = UnicodeOutgoing.Length;
UnicodeOutgoing.Buffer = LSAP_DS_GET_DS_ATTRIBUTE_AS_PWSTR( &Results.pAttr[ j ] );
InitialOutgoing = &UnicodeOutgoing;
break;
default:
//
// If other attributes that we do not necessarily want came back, then do nothing.
//
break;
}
}
}
//
// See if we have the proper interdomain trust account set
//
if ( NT_SUCCESS( Status ) && Startup && FLAG_ON( Direction, TRUST_DIRECTION_INBOUND ) ) {
//
// Find the interdomain trust account that matches
//
for ( Items = 0; Items < SamCount; Items++ ) {
if ( FlatName.Length + sizeof( WCHAR ) == SamAccountList[ Items ].Name.Length &&
RtlPrefixUnicodeString( &FlatName,
( PUNICODE_STRING )&SamAccountList[ Items ].Name,
TRUE ) ) {
CurrentAccount = &SamAccountList[ Items ];
break;
}
}
//
// We have no account, so we had better create one
//
if ( CurrentAccount == NULL ) {
Status = LsapDsCreateInterdomainTrustAccountByDsName( TrustObject,
&FlatName );
if ( !NT_SUCCESS( Status ) ) {
SpmpReportEventU(
EVENTLOG_WARNING_TYPE,
LSAEVENT_ITA_FOR_TRUST_NOT_CREATED,
0,
sizeof( ULONG ),
&Status,
1,
Partner ? Partner : &FlatName
);
}
} else {
if ( RemoveObject ) {
LsapDsDebugOut(( DEB_FIXUP,
"InterdomainTrustAccount %wZ being removed\n",
&CurrentAccount->Name ));
}
}
}
#if DBG
if ( !NT_SUCCESS( Status ) ) {
LsapDsDebugOut(( DEB_FIXUP, "Fixup of %ws failed with 0x%lx\n",
LsapDsNameFromDsName( TrustObject ), Status ));
}
#endif
//
// The DS transaction should remain open at this point.
//
ASSERT(SampExistsDsTransaction());
ASSERT(THVerifyCount(1));
return( Status );
}
NTSTATUS
LsapDsTrustRenameObject(
IN PDSNAME TrustObject,
IN PUNICODE_STRING NewDns,
OUT PDSNAME *NewObjectName
)
/*++
Routine Description:
This routine will rename an existing trusted domain object
Arguments:
Return Values:
STATUS_SUCCESS -- Success
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
PDSNAME NewObject = NULL;
ULONG Len = 0;
//
// Build a new object name
//
if ( NewObjectName != NULL ) {
Len = LsapDsLengthAppendRdnLength( LsaDsStateInfo.DsSystemContainer,
NewDns->Length + sizeof( WCHAR ) );
*NewObjectName = LsapAllocateLsaHeap( Len );
if ( *NewObjectName == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
Status = LsapDsMapDsReturnToStatus( AppendRDN( LsaDsStateInfo.DsSystemContainer,
*NewObjectName,
Len,
NewDns->Buffer,
NewDns->Length / sizeof( WCHAR ),
ATT_COMMON_NAME ) );
}
}
if ( NT_SUCCESS( Status ) ) {
//
// Do the rename
//
Status = LsapDsRenameObject( TrustObject,
NULL,
ATT_COMMON_NAME,
NewDns );
if ( !NT_SUCCESS( Status ) ) {
LsapDsDebugOut(( DEB_FIXUP,
"Rename of %ws to %wZ failed with 0x%lx\n",
LsapDsNameFromDsName( TrustObject ),
NewDns,
Status ));
if ( NewObjectName != NULL ) {
LsapFreeLsaHeap( *NewObjectName );
*NewObjectName = NULL;
}
}
}
return( Status );
}
NTSTATUS
LsaIDsNotifiedObjectChange(
IN ULONG Class,
IN PVOID ObjectPath,
IN SECURITY_DB_DELTA_TYPE DeltaType,
IN PSID UserSid,
IN LUID AuthenticationId,
IN BOOLEAN ReplicatedInChange,
IN BOOLEAN ChangeOriginatedInLSA
)
/*++
Routine Description:
This routine is called by the Ds when an object that the Lsa cares about is modified.
This call is made synchronously to the Ds commit thread, and so must spend as little
time doing stuff as possible. Used only as a dispatch mechanism.
Arguments:
Class -- Class Id of the object being modified
ObjectPath -- Full Ds path to the object that has been modified
DeltaType -- Type of the modification
UserSid -- The SID of the user who made this change, if known
AuthenticationId -- ?? authentication ID of the user who made this change
ReplicatedInChange -- TRUE if this is a replicated-in change
ChangeOriginatedInLSA -- TRUE if the change originated in LSA
Return Values:
STATUS_SUCCESS -- Success
STATUS_INSUFFICIENT_RESOURCES -- A memory allocation failed
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
PLSAP_DSFU_NOTIFICATION_NODE NotificationNode;
PDSNAME Object = ( PDSNAME )ObjectPath;
BOOLEAN TrustedDomainChangeQueued;
//
// Queue the item to be processed
//
LsapDsDebugOut(( DEB_DSNOTIFY,
"LsaIDsNotifiedObjectChange called for 0x%lx / %lu / %ws\n",
Class,
DeltaType,
LsapDsNameFromDsName( Object ) ));
//
// If this is notification of the NTDS-DSA object, the only action we care about
// is a rename. All others we eat. This notification actually gets passed on to
// Netlogon, and that's all it cares about
//
if ( Class == CLASS_NTDS_DSA && DeltaType != SecurityDbRename ) {
LsapDsDebugOut(( DEB_DSNOTIFY,
"Notification for class NTDS_DSA ( %ws )was not a rename. Eating change\n",
LsapDsNameFromDsName( Object ) ));
return( STATUS_SUCCESS );
}
//
// If class is TRUSTED_DOMAIN or CROSS_REF then this means the trusted domain change
// is not queued yet. So when we negate the statement above, the trusted domain change
// is queued if class is not TRUSTED_DOMAIN and is not CROSS_REF.
//
TrustedDomainChangeQueued = ( Class != CLASS_TRUSTED_DOMAIN ) &&
( Class != CLASS_CROSS_REF );
NotificationNode = LsapAllocateLsaHeap( sizeof( LSAP_DSFU_NOTIFICATION_NODE ) );
if ( NotificationNode == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
RtlZeroMemory( NotificationNode, sizeof( LSAP_DSFU_NOTIFICATION_NODE ) );
if ( RtlValidSid( UserSid ) ) {
LSAPDS_ALLOC_AND_COPY_SID_ON_SUCCESS( Status, NotificationNode->UserSid, UserSid );
}
if ( NT_SUCCESS( Status ) ) {
Status = LsapDsCopyDsNameLsa( &NotificationNode->ObjectPath,
Object );
if (NT_SUCCESS(Status))
{
NotificationNode->Class = Class;
NotificationNode->DeltaType = DeltaType;
NotificationNode->AuthenticationId = AuthenticationId;
NotificationNode->ReplicatedInChange = ReplicatedInChange;
NotificationNode->ChangeOriginatedInLSA = ChangeOriginatedInLSA;
//
// If this is an originating change,
// get the TrustDirection that existed before the change.
//
if ( !ReplicatedInChange ) {
PLSADS_PER_THREAD_INFO CurrentThreadInfo;
CurrentThreadInfo = TlsGetValue( LsapDsThreadState );
// ASSERT( CurrentThreadInfo != NULL )
if ( CurrentThreadInfo != NULL ) {
NotificationNode->OldTrustDirection = CurrentThreadInfo->OldTrustDirection;
NotificationNode->OldTrustType = CurrentThreadInfo->OldTrustType;
}
}
//
// Queue the request to another thread
//
if ( LsapDsQueueFixupRequest( NotificationNode ) ) {
TrustedDomainChangeQueued = TRUE;
NotificationNode = NULL;
} else {
Status = STATUS_INSUFFICIENT_RESOURCES ;
}
}
}
if ( !NT_SUCCESS( Status ) ) {
LsapFreeNotificationNode( NotificationNode );
}
}
//
// Only notify netlogon on non-replicated-in changes
// Replicated-in changes are handled by the fixup routine
// (LsapDsFixupCallback)
//
// We need to notify Netlogon as soon as the changes are in the
// database, so DsEnumerateDomainTrusts returns up-to-date
// information. Delaying notification until the callback routine
// means that there would be a window during which netlogon cache
// would not contain the correct information
//
// In the future, we'd like to have a single trusted domain cache
// and this entire logic should go away.
//
if ( !ReplicatedInChange &&
Class == LsapDsClassIds[LsapDsClassTrustedDomain] ) {
NTSTATUS Status2;
Status2 = LsapNotifyNetlogonOfTrustChange(
NULL,
DeltaType
);
if ( !NT_SUCCESS( Status2 ) ) {
LsapDsDebugOut(( DEB_DSNOTIFY,
"LsapNotifyNetlogonOfTrustChange failed with 0x%lx\n",
Status2 ));
}
LsapDbSetStatusFromSecondary( Status, Status2 );
}
//
// If there is an error, we need to invalidate the cache.
//
if( !TrustedDomainChangeQueued ) {
if( NT_SUCCESS( LsapDbAcquireWriteLockTrustedDomainList() ) ) {
LsapDbMakeCacheInvalid( TrustedDomainObject );
LsapDbReleaseLockTrustedDomainList();
}
}
return( Status );
}
NTSTATUS
LsaIKerberosRegisterTrustNotification(
IN pfLsaTrustChangeNotificationCallback Callback,
IN LSAP_REGISTER Register
)
/*++
Routine Description:
This routine is provided so that in process logon packages, such as Kerberos, can
get notification of trust changes. Currently, only one such package is supported.
Arguments:
Callback -- Address of callback function to make
Register -- Whether to register or unregister the notification
Return Values:
STATUS_SUCCESS -- Success
STATUS_INVALID_PARAMETER -- A request was made to register a NULL callback
STATUS_UNCSUCCESSFUL -- The operation couldn't be compeleted. Either a callback is
already registered or no call back is registered.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
switch ( Register ) {
case LsaRegister:
if ( LsapKerberosTrustNotificationFunction == NULL ) {
if ( Callback == NULL ) {
Status = STATUS_INVALID_PARAMETER;
} else {
LsapKerberosTrustNotificationFunction = Callback;
}
} else {
Status = STATUS_UNSUCCESSFUL;
}
break;
case LsaUnregister:
if ( LsapKerberosTrustNotificationFunction == NULL ) {
Status = STATUS_UNSUCCESSFUL;
} else {
LsapKerberosTrustNotificationFunction = NULL;
}
break;
default:
Status = STATUS_INVALID_PARAMETER;
break;
}
return( Status );
}
VOID
LsapFreeNotificationNode(
IN PLSAP_DSFU_NOTIFICATION_NODE NotificationNode
)
/*++
Routine Description:
This routine frees a notification node
Arguments:
NotificationNode -- Node to be freed
Return Values:
VOID
--*/
{
if ( NotificationNode ) {
LsapFreeLsaHeap( NotificationNode->UserSid );
LsapFreeLsaHeap( NotificationNode->ObjectPath );
LsapFreeLsaHeap( NotificationNode );
}
}
NTSTATUS
LsapDsFixupChangeNotificationForReplicator(
LSAP_DB_OBJECT_TYPE_ID ObjectType,
PSID Sid,
PUNICODE_STRING FlatName,
PDSNAME ObjectPath,
SECURITY_DB_DELTA_TYPE DeltaType,
BOOLEAN ReplicatedInChange
)
/*++
Routine Description:
This routine provides change notifications to netlogon on trust/global secret changes. This handles replicated in changes in a multimaster system and also
secret object changes ( needed for NT4 ) when outbound trust changes.
Parameters:
Sid -- The SID of the trusted domain object
FlatName -- The flat name/netbios name of the domain
ObjectPath -- The DSNAME of the object. identifies the object in the DS
DeltaType -- Type of change, add/modify/delete
Return Values
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
LARGE_INTEGER One = {1,0};
PBYTE Buffer;
UNICODE_STRING SecretName;
BOOLEAN SerialNumberChanged = FALSE;
//
// Lock the LSA database.
//
// We need at least the following locks:
// PolicyLock: protect policy cache
// RegistryLock: protects LsapDbState.PolicyModificationInfo (and registry transaction)
// Registry lock is acquired later, if we decide that the modification info
// is going to change
//
LsapDbAcquireLockEx( PolicyObject, LSAP_DB_READ_ONLY_TRANSACTION );
//
// Handle TDO notification.
//
if (TrustedDomainObject==ObjectType) {
//
// Give one notification for the trusted domain object
//
LsapDbLockAcquire( &LsapDbState.RegistryLock );
LsapDbState.PolicyModificationInfo.ModifiedId.QuadPart+=One.QuadPart;
SerialNumberChanged = TRUE;
LsapNetNotifyDelta(
SecurityDbLsa,
LsapDbState.PolicyModificationInfo.ModifiedId,
DeltaType,
SecurityDbObjectLsaTDomain,
0,
Sid,
NULL,
TRUE, // Replicate immediately
NULL
);
//
// Give a second notification for the secret object, corresponding to the trusted
// domain object. NT4 stores the auth info in a global secret, while NT5 stored it
// in the TDO itself. The TDO is exposed as global secret also for NT4.
//
SafeAllocaAllocate( Buffer, FlatName->Length + sizeof( LSAP_DS_TRUSTED_DOMAIN_SECRET_PREFIX ));
if ( Buffer == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
RtlZeroMemory(Buffer, FlatName->Length +
sizeof( LSAP_DS_TRUSTED_DOMAIN_SECRET_PREFIX ) );
RtlCopyMemory( Buffer, LSAP_DS_TRUSTED_DOMAIN_SECRET_PREFIX,
sizeof( LSAP_DS_TRUSTED_DOMAIN_SECRET_PREFIX ) );
RtlCopyMemory( Buffer +
sizeof( LSAP_DS_TRUSTED_DOMAIN_SECRET_PREFIX ) -
sizeof(WCHAR),
FlatName->Buffer,
FlatName->Length);
RtlInitUnicodeString( &SecretName, (PWSTR)Buffer );
LsapDbState.PolicyModificationInfo.ModifiedId.QuadPart+=One.QuadPart;
SerialNumberChanged = TRUE;
LsapNetNotifyDelta(
SecurityDbLsa,
LsapDbState.PolicyModificationInfo.ModifiedId,
DeltaType,
SecurityDbObjectLsaSecret,
0,
NULL,
&SecretName,
TRUE, // Replicate immediately
NULL
);
SafeAllocaFree( Buffer );
Buffer = NULL;
}
else if ((SecretObject==ObjectType) )
{
WCHAR RdnStart[ MAX_RDN_SIZE + 1 ];
ULONG Len;
ATTRTYP AttrType;
BOOLEAN SkipNotification = FALSE;
Status = LsapDsMapDsReturnToStatus( GetRDNInfoExternal(
ObjectPath,
RdnStart,
&Len,
&AttrType ) );
if ( NT_SUCCESS( Status ) ) {
ULONG UnmangledLen;
//
// The RDN is mangled on a delete, however the first 75 chars
// of the RDN is prserved ( and we use upto 64 Chars ) so we
// are O.K wrt to character loss ( fortunately ).
// So just adjust the size accordingly.
//
if ((SecurityDbDelete==DeltaType) &&
(IsMangledRDNExternal(RdnStart,Len,&UnmangledLen)))
{
Len = UnmangledLen;
}
//
// Allocate a buffer to hold the name
//
SafeAllocaAllocate( Buffer, Len * sizeof( WCHAR ) + sizeof( LSA_GLOBAL_SECRET_PREFIX ));
if ( Buffer == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
//
// If the LSA created the global secret, we appended a postfix... Remove
// that here.
//
RdnStart[ Len ] = UNICODE_NULL;
if ( Len > LSAP_DS_SECRET_POSTFIX_LEN &&
_wcsicmp( &RdnStart[Len-LSAP_DS_SECRET_POSTFIX_LEN],
LSAP_DS_SECRET_POSTFIX ) == 0 ) {
Len -= LSAP_DS_SECRET_POSTFIX_LEN;
RdnStart[ Len ] = UNICODE_NULL;
}
RtlCopyMemory( Buffer,
LSA_GLOBAL_SECRET_PREFIX,
sizeof( LSA_GLOBAL_SECRET_PREFIX ) );
RtlCopyMemory( Buffer + sizeof( LSA_GLOBAL_SECRET_PREFIX ) - sizeof(WCHAR),
RdnStart,
( Len + 1 ) * sizeof( WCHAR ) );
RtlInitUnicodeString( &SecretName, (PWSTR)Buffer );
//
// If this is a secret object deletion,
// Skip the notification if the secret was simply morphed into a TDO.
//
if ( DeltaType == SecurityDbDelete ) {
BOOLEAN DsTrustedDomainSecret;
(VOID) LsapDsIsSecretDsTrustedDomain(
&SecretName,
NULL, // No object info since not openning handle
0, // No options since not openning handle
0, // No access since not openning handle
NULL, // Don't return a handle to the object
&DsTrustedDomainSecret );
if ( DsTrustedDomainSecret ) {
SkipNotification = TRUE;
}
}
//
// Do the actual notification.
//
if ( !SkipNotification ) {
LsapDbLockAcquire( &LsapDbState.RegistryLock );
LsapDbState.PolicyModificationInfo.ModifiedId.QuadPart+=One.QuadPart;
SerialNumberChanged = TRUE;
LsapNetNotifyDelta(
SecurityDbLsa,
LsapDbState.PolicyModificationInfo.ModifiedId,
DeltaType,
SecurityDbObjectLsaSecret,
0,
NULL,
&SecretName,
TRUE, // Replicate immediately
NULL
);
}
SafeAllocaFree( Buffer );
Buffer = NULL;
}
}
//
// If the serial number changed,
// write it to the registry.
//
// Don't do this by going through the policy object since we want to
// avoid any side effects when writing this non-replicated attribute.
//
if ( SerialNumberChanged ) {
//
// Invalidate the cache for the Policy Modification Information
//
LsapDbMakeInvalidInformationPolicy( PolicyModificationInformation );
//
// Open the transaction
//
Status = LsapRegOpenTransaction();
if ( NT_SUCCESS(Status) ) {
//
// Write the attribute
Status = LsapDbWriteAttributeObject(
LsapDbHandle,
&LsapDbNames[ PolMod ],
(PVOID) &LsapDbState.PolicyModificationInfo,
(ULONG) sizeof (POLICY_MODIFICATION_INFO)
);
if ( NT_SUCCESS(Status) ) {
Status = LsapRegApplyTransaction();
} else {
Status = LsapRegAbortTransaction();
}
}
}
Cleanup:
LsapDbReleaseLockEx( PolicyObject, LSAP_DB_READ_ONLY_TRANSACTION );
if ( SerialNumberChanged ) {
LsapDbLockRelease( &LsapDbState.RegistryLock );
}
return (Status);
}
DWORD
WINAPI LsapDsFixupCallback(
LPVOID ParameterBlock
)
/*++
Routine Description:
This is the worker thread for the fixup code. Whenever notification of an object change
comes from the Ds, it ends up routing through here, which will dispatch it as appropriate.
Arguments:
ParameterBlock -- NotificationNode of information sufficient to determine what
operation should be taken
Passed Class must be one of the followings;
CLASS_TRUSTED_DOMAIN
CLASS_SECRET
CLASS_CROSS_REF
CLASS_USER
or ERROR_INVALID_PARAMETER is returned.
Return Values:
ERROR_SUCCESS -- Success
ERROR_NOT_ENOUGH_MEMORY -- A memory allocation failed
ERROR_INVALID_PARAMETER -- An unexpected parameter was encountered
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
PLSAP_DSFU_NOTIFICATION_NODE NotificationNode = ( PLSAP_DSFU_NOTIFICATION_NODE )ParameterBlock;
LSAPR_TRUSTED_DOMAIN_FULL_INFORMATION2 TrustInfo2;
BOOLEAN CloseTransaction = FALSE, RollbackTransaction = FALSE, ActiveThreadState = FALSE;
LSAP_DB_OBJECT_TYPE_ID ObjType = NullObject;
BOOLEAN TrustedDomainCacheIsUpToDate;
LsapDsDebugOut(( DEB_DSNOTIFY | DEB_FTRACE,
"LsapDsFixupCallback called for 0x%lx / %lu / %ws\n",
NotificationNode->Class,
NotificationNode->DeltaType,
LsapDsNameFromDsName( NotificationNode->ObjectPath ) ));
switch ( NotificationNode->Class ) {
case CLASS_TRUSTED_DOMAIN:
ObjType = TrustedDomainObject;
break;
case CLASS_SECRET:
ObjType = SecretObject;
break;
}
RtlZeroMemory(&TrustInfo2, sizeof(TrustInfo2));
//
// If class is TRUSTED_DOMAIN or CROSS_REF then this means the trusted domain change
// is not reflected to the TrustedDomainCache so it is not upto date. Therefore when we negate
// the statement above, the trusted domain cache is upto date if class is not TRUSTED_DOMAIN
// and is not CROSS_REF.
//
TrustedDomainCacheIsUpToDate = ( NotificationNode->Class != CLASS_TRUSTED_DOMAIN ) &&
( NotificationNode->Class != CLASS_CROSS_REF );
//
// Initialize the DS allocator and create a DS thread state at this point.
//
if ( ObjType != NullObject ) {
Status = LsapDsInitAllocAsNeededEx( 0,
ObjType,
&CloseTransaction );
if ( !NT_SUCCESS( Status ) ) {
goto FixupCallbackCleanup;
}
ActiveThreadState = TRUE;
}
//
// Handle a TDO changing.
//
if ( NotificationNode->Class == LsapDsClassIds[ LsapDsClassTrustedDomain ] ) {
//
// Get a description of the TDO as it appears in the DS.
//
Status = LsapDsGetTrustedDomainInfoEx( NotificationNode->ObjectPath,
NotificationNode->DeltaType == SecurityDbDelete ?
LSAPDS_READ_DELETED : 0,
TrustedDomainFullInformation2Internal,
(PLSAPR_TRUSTED_DOMAIN_INFO)&TrustInfo2,
NULL );
if ( !NT_SUCCESS(Status) ) {
//
// Before a deletion notification to a trusted domain, a change notification is sent. We don't
// want/expect this notification. So, skip it! After all we are not interested in a change in a
// deleted trusted domain object.
//
if( NotificationNode->DeltaType == SecurityDbChange &&
Status == STATUS_OBJECT_NAME_NOT_FOUND &&
DsIsMangledDn(
LsapDsNameFromDsName( NotificationNode->ObjectPath ),
DS_MANGLE_OBJECT_RDN_FOR_DELETION ) ) {
TrustedDomainCacheIsUpToDate = TRUE;
}
goto FixupCallbackCleanup;
}
//
// Our DS Transaction State should not be altered by LsapDsGetTrustedDomainInfoEx
//
ASSERT(SampExistsDsTransaction());
ASSERT(THVerifyCount(1));
//
// If this is a replicated in change,
// get the previous trust direction from the cache entry on this machine.
//
if ( NotificationNode->ReplicatedInChange ) {
//
// Default the old direction to the new direction.
//
NotificationNode->OldTrustDirection = TrustInfo2.Information.TrustDirection;
NotificationNode->OldTrustType = TrustInfo2.Information.TrustType;
//
// Grab the GUID of the real TDO for this named trust.
//
Status = LsapDbAcquireReadLockTrustedDomainList();
if ( NT_SUCCESS(Status)) {
PLSAP_DB_TRUSTED_DOMAIN_LIST_ENTRY TrustEntry;
//
// Lookup the information in the trusted domain list
//
Status = LsapDbLookupNameTrustedDomainListEx(
&TrustInfo2.Information.Name,
&TrustEntry );
if ( NT_SUCCESS(Status)) {
ASSERT(NULL!=TrustEntry);
NotificationNode->OldTrustDirection = TrustEntry->TrustInfoEx.TrustDirection;
NotificationNode->OldTrustType = TrustEntry->TrustInfoEx.TrustType;
}
LsapDbReleaseLockTrustedDomainList();
}
}
//
// Any failure to update the cache is handled by LsapDsFixupTrustByInfo
//
TrustedDomainCacheIsUpToDate = TRUE;
//
// Fix the TDL to match the actual object.
//
Status = LsapDsFixupTrustByInfo( NotificationNode->ObjectPath,
&TrustInfo2.Information,
TrustInfo2.PosixOffset.Offset,
NotificationNode->DeltaType,
NotificationNode->UserSid,
NotificationNode->AuthenticationId,
NotificationNode->ReplicatedInChange,
NotificationNode->ChangeOriginatedInLSA
);
//
// Only notify netlogon on a replicated-in change
// Non-replicated-in changes are handled by the commit callback routine
// (LsaIDsNotifiedObjectChange)
//
if ( NotificationNode->ReplicatedInChange ) {
NTSTATUS Status2;
Status2 = LsapNotifyNetlogonOfTrustChange(
NULL,
NotificationNode->DeltaType
);
if ( !NT_SUCCESS( Status2 ) ) {
LsapDsDebugOut(( DEB_DSNOTIFY,
"LsapNotifyNetlogonOfTrustChange failed with 0x%lx\n",
Status2 ));
}
LsapDbSetStatusFromSecondary( Status, Status2 );
}
} else if ( NotificationNode->Class == LsapDsClassIds[ LsapDsClassSecret ] ) {
//
// Currently, there is nothing to do...
//
} else if ( NotificationNode->Class == LsapDsClassIds[ LsapDsClassXRef ] ){
//
// New Cross ref has replicated in, look at corresponding TDO and see if it needs
// to be renamed
//
Status = LsapDsFixupTrustForXrefChange(
NotificationNode->ObjectPath,
FALSE // will begin and end its own transaction
);
//
// Since a cross-ref has changed, repopulate the cross-forest trust cache
// with local forest trust information
//
Status = LsapDbAcquireWriteLockTrustedDomainList();
if ( NT_SUCCESS( Status )) {
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();
}
TrustedDomainCacheIsUpToDate = TRUE;
//
// Notify netlogon and kerberos of the possibility of the trust tree having changed
//
if ( LsapKerberosTrustNotificationFunction ) {
LsaIRegisterNotification( ( SEC_THREAD_START )LsapKerberosTrustNotificationFunction,
( PVOID ) NotificationNode->DeltaType,
NOTIFIER_TYPE_IMMEDIATE,
0,
NOTIFIER_FLAG_ONE_SHOT,
0,
0 );
}
Status = I_NetNotifyDsChange( NlOrgChanged );
if ( !NT_SUCCESS( Status ) ) {
LsapDsDebugOut(( DEB_ERROR,
"I_NetNotifyDsChange( NlOrgChange ) failed with 0x%lx\n" ));
}
} else if ( NotificationNode->Class == CLASS_USER ) {
//
// Nothing really to do out here. We do not take any change notifications to user
// objects.
//
} else {
Status = STATUS_INVALID_PARAMETER;
}
FixupCallbackCleanup:
RollbackTransaction = (NT_SUCCESS(Status))?FALSE:TRUE;
//
// Destruction of the thread state will delete the memory alloced by the SearchNonUnique call
//
if (ActiveThreadState)
{
LsapDsDeleteAllocAsNeededEx2( 0,
ObjType,
CloseTransaction,
RollbackTransaction
);
}
//
// Assert that we have cleaned up the DS properly
//
ASSERT(!SampExistsDsTransaction());
ASSERT(THVerifyCount(0));
if (NT_SUCCESS(Status))
{
//
// We need to provide netlogon notifications on trust/secret changes
// for replication to NT4
//
if ((TrustedDomainObject==ObjType) &&
(NULL!=TrustInfo2.Information.Sid) &&
(TrustInfo2.Information.FlatName.Length>0))
{
BOOLEAN NotifyNetlogon = TRUE;
SECURITY_DB_DELTA_TYPE DeltaTypeToUse = NotificationNode->DeltaType;
//
// If this object need not be replicated to NT 4,
// be careful about giving spurious notifications.
//
if ( !LsapReplicateTdoNt4( TrustInfo2.Information.TrustDirection,
TrustInfo2.Information.TrustType ) ) {
//
// If the object is just being created or deleted,
// then NT 4 isn't interested in this trust.
//
if ( DeltaTypeToUse == SecurityDbNew ||
DeltaTypeToUse == SecurityDbDelete ) {
NotifyNetlogon = FALSE;
//
// If the object didn't used to be replicated to NT 4,
// then this change simply isn't interesting to NT 4 replication.
//
} else if ( !LsapReplicateTdoNt4( NotificationNode->OldTrustDirection,
NotificationNode->OldTrustType ) ) {
NotifyNetlogon = FALSE;
//
// If this object used to replicated to NT 4,
// then this is really an object deletion as far as NT 4 replication
// is concerned.
//
} else {
DeltaTypeToUse = SecurityDbDelete;
}
}
//
// Now notify netlogon
//
if ( NotifyNetlogon ) {
//
// If we knew that the Outbound password property has been changed,
// then and only then should we notify netlogon that the underlying global
// secret has changed.
// However, the change is probably not worth making, because the cost
// of not making it is a few extra bytes on the wire.
//
LsapDsFixupChangeNotificationForReplicator(
TrustedDomainObject,
TrustInfo2.Information.Sid,
(PUNICODE_STRING) &TrustInfo2.Information.FlatName,
NotificationNode->ObjectPath,
DeltaTypeToUse,
NotificationNode->ReplicatedInChange
);
}
}
else if (SecretObject == ObjType)
{
LsapDsFixupChangeNotificationForReplicator(
SecretObject,
NULL,
NULL,
NotificationNode->ObjectPath,
NotificationNode->DeltaType,
NotificationNode->ReplicatedInChange
);
}
}
//
// Free the allocations...
//
_fgu__LSAPR_TRUSTED_DOMAIN_INFO( (PLSAPR_TRUSTED_DOMAIN_INFO)&TrustInfo2,
TrustedDomainFullInformation2Internal );
LsapFreeNotificationNode( NotificationNode );
if( !TrustedDomainCacheIsUpToDate ) {
if( NT_SUCCESS( LsapDbAcquireWriteLockTrustedDomainList() ) ) {
LsapDbMakeCacheInvalid( TrustedDomainObject );
LsapDbReleaseLockTrustedDomainList();
}
}
return( RtlNtStatusToDosError( Status ) );
}
NTSTATUS
LsapDsFixupTrustByInfo(
IN PDSNAME ObjectPath,
IN PLSAPR_TRUSTED_DOMAIN_INFORMATION_EX2 TrustInfo2,
IN ULONG PosixOffset,
IN SECURITY_DB_DELTA_TYPE DeltaType,
IN PSID UserSid,
IN LUID AuthenticationId,
IN BOOLEAN ReplicatedInChange,
IN BOOLEAN ChangeOriginatedInLSA
)
/*++
Routine Description:
This function does the fixup of trusted domain objects following notification from the
Ds. For created and deleted objects, it involves updating the trust list and notifying
netlogon and kerberos of changes. For modifications, verification of correctness is done.
Upon an object creaton the corresponding SAM account is created. Upon an object deletion
the corresponding SAM account is delted.
Arguments:
ObjectPath -- The Ds name of the object that changed
TrustInfo2 -- The information that is currently available about the trust
PosixOffset -- Posix offset of the domain.
DeltaType -- Type of change that happened
UserSid -- The user that was responsible for making this change
AuthenticationId -- AuthenticationId of the user making the change
ReplicatedInChange -- Indicates that the change replicated in instead of being an originating
change
ChangeOriginatedInLSA -- Indicates that the change has originated in LSA as opposed to DS/LDAP
Return Values:
ERROR_SUCCESS -- Success
ERROR_NOT_ENOUGH_MEMORY -- A memory allocation failed
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
LSAPR_TRUST_INFORMATION TrustInformation;
BOOLEAN AcquiredListWriteLock = FALSE;
BOOLEAN TrustLockAcquired = FALSE;
LsapDsDebugOut(( DEB_FTRACE,
"LsapDsFixupTrustByInfo( %ws, %x, %x, ...)\n",
ObjectPath ? LsapDsNameFromDsName( ObjectPath ) : L"<null>",
TrustInfo2,
DeltaType ));
//
// If this is a replicated in change,
// make sure the TDO has a valid Posix Offset in it.
//
if ( ReplicatedInChange &&
(DeltaType == SecurityDbNew || DeltaType == SecurityDbChange ) ) {
DOMAIN_SERVER_ROLE ServerRole;
//
// Only change the Posix Offset on the PDC.
//
Status = SamIQueryServerRole(
LsapAccountDomainHandle,
&ServerRole
);
if ( NT_SUCCESS(Status) && ServerRole == DomainServerRolePrimary ) {
BOOLEAN PosixOffsetChanged = FALSE;
if ( LsapDbDcInRootDomain()) {
//
// On a root domain PDC, acquire the trust lock before
// the rest to ensure no deadlocks if forest trust data
// needs to be written back to the DS
//
LsapDbAcquireLockEx( TrustedDomainObject, LSAP_DB_LOCK );
TrustLockAcquired = TRUE;
}
//
// If we should have a Posix Offset,
// ensure we have one.
//
if ( LsapNeedPosixOffset( TrustInfo2->TrustDirection,
TrustInfo2->TrustType ) ) {
if ( PosixOffset == 0 ) {
//
// Need to grab the TDL write lock while allocating a Posix Offset
//
Status = LsapDbAcquireWriteLockTrustedDomainList();
if ( NT_SUCCESS(Status)) {
AcquiredListWriteLock = TRUE;
//
// Allocate the next available Posix Offset.
//
Status = LsapDbAllocatePosixOffsetTrustedDomainList(
&PosixOffset );
if ( NT_SUCCESS(Status)) {
PosixOffsetChanged = TRUE;
}
}
}
//
// If we shouldn't have a Posix Offset,
// ensure we don't have one.
//
} else {
if ( PosixOffset != 0 ) {
PosixOffset = 0;
PosixOffsetChanged = TRUE;
}
}
//
// If we're forcing the Posix Offset to change,
// do it now.
//
if ( PosixOffsetChanged ) {
ATTRVAL TDOWriteAttVals[] = {
{ sizeof(ULONG), (PUCHAR)&PosixOffset},
};
ATTR TDOWriteAttrs[] = {
{ ATT_TRUST_POSIX_OFFSET, {1, &TDOWriteAttVals[0] } },
};
ATTRBLOCK TDOWriteAttrBlock = {sizeof(TDOWriteAttrs)/sizeof(TDOWriteAttrs[0]),TDOWriteAttrs};
//
// Set the Posix Offset on the TDO
//
Status = LsapDsWriteByDsName(
ObjectPath,
LSAPDS_REPLACE_ATTRIBUTE,
&TDOWriteAttrBlock
);
if (!NT_SUCCESS(Status)) {
Status = STATUS_SUCCESS; // This isn't fatal
}
}
}
}
switch ( DeltaType ) {
case SecurityDbNew:
if (ReplicatedInChange)
{
//
// On a replicated in change notification, insert the trusted domain
// object into the trusted domain list. This need not be done for the
// case of an originating change as this is done within the LSA call to
// create the trusted domain object.
//
if ( NT_SUCCESS( LsapDbAcquireWriteLockTrustedDomainList())) {
if( LsapDbIsValidTrustedDomainList() ) {
Status = LsapDbInsertTrustedDomainList(
TrustInfo2,
PosixOffset
);
if ( !NT_SUCCESS( Status ) ) {
LsapDsDebugOut(( DEB_ERROR,
"LsapDbInsertTrustedDomainList for %wZ failed with 0x%lx\n",
&TrustInfo2->FlatName,
Status ));
}
}
LsapDbReleaseLockTrustedDomainList();
}
}
break;
case SecurityDbChange:
//
// On a replicated in change update the trusted domain List
//
if (ReplicatedInChange)
{
Status = LsapDbFixupTrustedDomainListEntry(
TrustInfo2->Sid,
&TrustInfo2->Name,
&TrustInfo2->FlatName,
TrustInfo2,
&PosixOffset );
if ( !NT_SUCCESS( Status ) ) {
LsapDsDebugOut(( DEB_ERROR,
"LsapDbFixupTrustedDomainList for %wZ failed with 0x%lx\n",
&TrustInfo2->FlatName,
Status ));
}
}
break;
case SecurityDbDelete:
if ( ReplicatedInChange || !ChangeOriginatedInLSA) {
PLSAP_DB_TRUSTED_DOMAIN_LIST_ENTRY TrustEntry;
GUID RealTdoGuid;
//
// Check to see that the object path is not a mangled name.
// If it is drop the notification on the floor.
//
// If a set of duplicate trusts exist,
// we must delete only the good one from the cache
//
//
// Grab the GUID of the real TDO for this named trust.
//
Status = LsapDbAcquireReadLockTrustedDomainList();
if (!NT_SUCCESS(Status)) {
break;
}
//
// Lookup the information in the trusted domain list
//
Status = LsapDbLookupNameTrustedDomainListEx(
&TrustInfo2->Name,
&TrustEntry
);
if (!NT_SUCCESS(Status)) {
LsapDbReleaseLockTrustedDomainList();
break;
}
ASSERT(NULL!=TrustEntry);
RealTdoGuid = TrustEntry->ObjectGuidInDs;
LsapDbReleaseLockTrustedDomainList();
//
// If this TDO isn't the TDO that we've selected to represent this trust,
// then it is the mangled TDO.
//
// Totally ignore changes to this mangled TDO.
//
if ((!LsapNullUuid(&RealTdoGuid))
&& (0!=memcmp(&RealTdoGuid, &ObjectPath->Guid, sizeof(GUID))))
{
//
// Error out. This will cause us to not update the trusted domain list and not
// provide netlogon notifications
//
Status = STATUS_OBJECT_NAME_COLLISION;
break;
}
TrustInformation.Sid = TrustInfo2->Sid;
RtlCopyMemory( &TrustInformation.Name, &TrustInfo2->FlatName, sizeof( UNICODE_STRING ) );
if ( NT_SUCCESS( LsapDbAcquireWriteLockTrustedDomainList() ) ) {
Status = LsapDbDeleteTrustedDomainList( &TrustInformation );
if ( !NT_SUCCESS( Status ) ) {
ASSERTMSG( "We checked that it was in the list. Who deleted it before we do?",
Status != STATUS_NO_SUCH_DOMAIN );
LsapDsDebugOut(( DEB_ERROR,
"LsapDbDeleteTrustedDomainList for %wZ failed with 0x%lx\n",
&TrustInfo2->FlatName,
Status ));
}
LsapDbReleaseLockTrustedDomainList();
}
}
//
// if a TDO is deleted by LSA, the audit will be generated in
// the main thread (LsarDeleteObject). However, if a TDO
// is deleted by LDAP, DS does not call LsarDeleteObject
// to effect the change in LSA, it simply deletes object
// and sends a notification. We generate the audit here
// if the change did not originate in LSA (indicated by
// the value of ChangeOriginatedInLSA).
//
if ((UserSid && LsapAdtAuditingEnabledHint(AuditCategoryPolicyChange, EVENTLOG_AUDIT_SUCCESS)) &&
(!ReplicatedInChange) &&
(!ChangeOriginatedInLSA)) {
Status = LsapAdtTrustedDomainRem(
EVENTLOG_AUDIT_SUCCESS,
(PUNICODE_STRING) &TrustInfo2->Name,
(PSID) TrustInfo2->Sid,
UserSid,
&AuthenticationId
);
}
break;
default:
//
// Unsupported delta type
//
LsapDsDebugOut(( DEB_ERROR,
"LsapDsFixupTrustByInfo received an unsupported delta type of %lu\n",
DeltaType ));
}
//
// If necessary, release the Trusted Domain List Write Lock.
//
if (AcquiredListWriteLock) {
LsapDbReleaseLockTrustedDomainList();
AcquiredListWriteLock = FALSE;
}
if ( TrustLockAcquired ) {
LsapDbReleaseLockEx( TrustedDomainObject, LSAP_DB_LOCK );
}
return( Status );
}
NTSTATUS
LsapDsInitFixupQueue(
VOID
)
{
InitializeListHead( &LsapFixupList );
return SafeInitializeCriticalSection( &LsapFixupLock, ( DWORD )LSAP_FIXUP_LOCK_ENUM );
}
DWORD
LsapDsFixupQueueWorker(
PVOID Ignored
)
{
PLSAP_DSFU_NOTIFICATION_NODE Node ;
PLIST_ENTRY List ;
SafeEnterCriticalSection( &LsapFixupLock );
if ( LsapFixupThreadActive )
{
SafeLeaveCriticalSection( &LsapFixupLock );
return 0 ;
}
LsapFixupThreadActive = TRUE ;
while ( !IsListEmpty( &LsapFixupList ) )
{
List = RemoveHeadList( &LsapFixupList );
SafeLeaveCriticalSection( &LsapFixupLock );
Node = CONTAINING_RECORD( List, LSAP_DSFU_NOTIFICATION_NODE, List );
LsapDsFixupCallback( Node );
SafeEnterCriticalSection( &LsapFixupLock );
}
LsapFixupThreadActive = FALSE ;
SafeLeaveCriticalSection( &LsapFixupLock );
return 0 ;
}
BOOL
LsapDsQueueFixupRequest(
PLSAP_DSFU_NOTIFICATION_NODE Node
)
{
BOOL Ret = TRUE ;
SafeEnterCriticalSection( &LsapFixupLock );
if ( LsapFixupThreadActive == FALSE )
{
Ret = QueueUserWorkItem( LsapDsFixupQueueWorker, NULL, 0 );
}
if ( Ret )
{
InsertTailList( &LsapFixupList, &Node->List );
}
SafeLeaveCriticalSection( &LsapFixupLock );
return Ret ;
}
NTSTATUS
LsapNotifyNetlogonOfTrustWithParent(
VOID
)
/*++
Routine Description:
Notifies Netlogon of trust relationship with the parent domain.
Arguments:
None
Returns:
STATUS_SUCCESS if happy
STATUS_ error code otherwise
--*/
{
NTSTATUS Status;
PLSAPR_FOREST_TRUST_INFO ForestTrustInfo = NULL;
ASSERT( SamIIsRebootAfterPromotion());
//
// Locate the trust link to the parent
//
Status = LsaIQueryForestTrustInfo(
LsapPolicyHandle,
&ForestTrustInfo
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_ERROR,
"LsapNotifyNetlogonOfTrustWithParent got error 0x%lx back from LsaIQueryForestTrustInfo\n",
Status ));
goto Cleanup;
}
ASSERT( ForestTrustInfo );
if ( ForestTrustInfo->ParentDomainReference == NULL ) {
//
// We're the root domain of the forest. Nothing to do.
//
Status = STATUS_SUCCESS;
goto Cleanup;
}
//
// Notify netlogon of the trust relationship changing.
// Nothing changed, but this will force the necessary replication.
//
Status = LsapDsFixupChangeNotificationForReplicator(
TrustedDomainObject,
ForestTrustInfo->ParentDomainReference->DomainSid,
&ForestTrustInfo->ParentDomainReference->FlatName,
NULL,
SecurityDbChange,
FALSE
);
if ( !NT_SUCCESS( Status )) {
LsapDsDebugOut(( DEB_ERROR,
"LsapNotifyNetlogonOfTrustWithParent got error 0x%lx back from LsapDsFixupChangeNotificationForReplicator\n",
Status ));
goto Cleanup;
}
Cleanup:
LsaIFreeForestTrustInfo( ForestTrustInfo );
return Status;
}